185 lines
6.7 KiB
JavaScript
185 lines
6.7 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
import { chromium } from 'playwright';
|
|
|
|
import { authenticateFrontdoor, createAuthenticatedContext } from './live-frontdoor-auth.mjs';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
const webRoot = path.resolve(__dirname, '..');
|
|
const outputDirectory = path.join(webRoot, 'output', 'playwright');
|
|
|
|
const BASE_URL = process.env.STELLAOPS_FRONTDOOR_BASE_URL?.trim() || 'https://stella-ops.local';
|
|
const STATE_PATH = path.join(outputDirectory, 'live-frontdoor-auth-state.json');
|
|
const REPORT_PATH = path.join(outputDirectory, 'live-frontdoor-auth-report.json');
|
|
const RESULT_PATH = path.join(outputDirectory, 'live-hotfix-action-check.json');
|
|
const EXPECTED_SCOPE = {
|
|
tenant: 'demo-prod',
|
|
regions: 'us-east',
|
|
environments: 'stage',
|
|
timeWindow: '7d',
|
|
};
|
|
const HOTFIX_LIST_URL = new URL('/releases/hotfixes', BASE_URL);
|
|
const HOTFIX_CREATE_URL = new URL('/releases/hotfixes/new', BASE_URL);
|
|
|
|
for (const [key, value] of Object.entries(EXPECTED_SCOPE)) {
|
|
HOTFIX_LIST_URL.searchParams.set(key, value);
|
|
HOTFIX_CREATE_URL.searchParams.set(key, value);
|
|
}
|
|
|
|
function collectScopeIssues(url, expectedScope, label) {
|
|
const issues = [];
|
|
const parsed = new URL(url);
|
|
|
|
for (const [key, expectedValue] of Object.entries(expectedScope)) {
|
|
const actualValue = parsed.searchParams.get(key);
|
|
if (actualValue !== expectedValue) {
|
|
issues.push(`${label} expected ${key}=${expectedValue} but got ${actualValue ?? '(missing)'}`);
|
|
}
|
|
}
|
|
|
|
return issues;
|
|
}
|
|
|
|
function shouldIgnoreRequestFailure(request) {
|
|
const url = request.url();
|
|
if (/\.(?:css|js|map|png|jpg|jpeg|svg|woff2?)(?:$|\?)/i.test(url)) {
|
|
return true;
|
|
}
|
|
|
|
const error = request.failure()?.errorText ?? '';
|
|
return error.includes('net::ERR_ABORTED');
|
|
}
|
|
|
|
async function main() {
|
|
mkdirSync(outputDirectory, { recursive: true });
|
|
|
|
await authenticateFrontdoor({
|
|
baseUrl: BASE_URL,
|
|
statePath: STATE_PATH,
|
|
reportPath: REPORT_PATH,
|
|
headless: true,
|
|
});
|
|
|
|
const authReport = JSON.parse(readFileSync(REPORT_PATH, 'utf8'));
|
|
const browser = await chromium.launch({ headless: true, args: ['--disable-dev-shm-usage'] });
|
|
|
|
try {
|
|
const context = await createAuthenticatedContext(browser, authReport, {
|
|
statePath: STATE_PATH,
|
|
contextOptions: {
|
|
acceptDownloads: false,
|
|
},
|
|
});
|
|
const page = await context.newPage();
|
|
const runtimeIssues = [];
|
|
const failedActions = [];
|
|
|
|
page.on('console', (message) => {
|
|
if (message.type() === 'error') {
|
|
runtimeIssues.push(`console:${message.text()}`);
|
|
}
|
|
});
|
|
|
|
page.on('pageerror', (error) => {
|
|
runtimeIssues.push(`pageerror:${error.message}`);
|
|
});
|
|
|
|
page.on('response', (response) => {
|
|
const url = response.url();
|
|
if (/\.(?:css|js|map|png|jpg|jpeg|svg|woff2?)(?:$|\?)/i.test(url)) {
|
|
return;
|
|
}
|
|
|
|
if (response.status() >= 400) {
|
|
runtimeIssues.push(`response:${response.status()}:${response.request().method()}:${url}`);
|
|
}
|
|
});
|
|
|
|
page.on('requestfailed', (request) => {
|
|
if (shouldIgnoreRequestFailure(request)) {
|
|
return;
|
|
}
|
|
|
|
runtimeIssues.push(`requestfailed:${request.method()}:${request.url()}:${request.failure()?.errorText ?? 'unknown'}`);
|
|
});
|
|
|
|
const result = {
|
|
checkedAtUtc: new Date().toISOString(),
|
|
hotfixListUrl: '',
|
|
hotfixListHeading: '',
|
|
hotfixReviewUrl: '',
|
|
hotfixReviewHeading: '',
|
|
hotfixCreateUrl: '',
|
|
hotfixCreateHeading: '',
|
|
failedActionCount: 0,
|
|
failedActions,
|
|
runtimeIssueCount: 0,
|
|
runtimeIssues,
|
|
scopeIssues: [],
|
|
};
|
|
|
|
await page.goto(HOTFIX_LIST_URL.toString(), { waitUntil: 'networkidle', timeout: 30_000 });
|
|
result.hotfixListUrl = page.url();
|
|
result.hotfixListHeading = (await page.locator('h1').first().textContent().catch(() => '')) ?? '';
|
|
|
|
await Promise.all([
|
|
page.waitForURL(/\/releases\/hotfixes\/platform-bundle-1-3-1-hotfix1/, { timeout: 10_000 }),
|
|
page.getByRole('link', { name: 'Review' }).click(),
|
|
]);
|
|
await page.waitForLoadState('networkidle');
|
|
result.hotfixReviewUrl = page.url();
|
|
result.hotfixReviewHeading = (await page.locator('h1').first().textContent().catch(() => '')) ?? '';
|
|
result.scopeIssues.push(...collectScopeIssues(result.hotfixReviewUrl, EXPECTED_SCOPE, 'hotfixReviewUrl'));
|
|
|
|
if (!new URL(result.hotfixReviewUrl).pathname.endsWith('/releases/hotfixes/platform-bundle-1-3-1-hotfix1')) {
|
|
failedActions.push(`Review expected /releases/hotfixes/platform-bundle-1-3-1-hotfix1 but landed on ${result.hotfixReviewUrl}`);
|
|
}
|
|
|
|
if (!result.hotfixReviewHeading.includes('platform-bundle-1-3-1-hotfix1')) {
|
|
failedActions.push(`Review expected hotfix detail heading but found "${result.hotfixReviewHeading}"`);
|
|
}
|
|
|
|
await page.goto(HOTFIX_CREATE_URL.toString(), { waitUntil: 'networkidle', timeout: 30_000 });
|
|
result.hotfixCreateUrl = page.url();
|
|
result.hotfixCreateHeading = (await page.locator('h1').first().textContent().catch(() => '')) ?? '';
|
|
result.scopeIssues.push(...collectScopeIssues(result.hotfixCreateUrl, EXPECTED_SCOPE, 'hotfixCreateUrl'));
|
|
|
|
const createUrl = new URL(result.hotfixCreateUrl);
|
|
if (createUrl.pathname !== '/releases/versions/new') {
|
|
failedActions.push(`Create Hotfix expected /releases/versions/new but landed on ${result.hotfixCreateUrl}`);
|
|
}
|
|
|
|
if (createUrl.searchParams.get('type') !== 'hotfix' || createUrl.searchParams.get('hotfixLane') !== 'true') {
|
|
failedActions.push(`Create Hotfix expected type=hotfix and hotfixLane=true but landed on ${result.hotfixCreateUrl}`);
|
|
}
|
|
|
|
if (result.hotfixCreateHeading !== 'Create Release Version') {
|
|
failedActions.push(`Create Hotfix expected "Create Release Version" heading but found "${result.hotfixCreateHeading}"`);
|
|
}
|
|
|
|
result.failedActionCount = failedActions.length;
|
|
result.runtimeIssueCount = runtimeIssues.length + result.scopeIssues.length;
|
|
result.runtimeIssues.push(...result.scopeIssues);
|
|
|
|
writeFileSync(RESULT_PATH, `${JSON.stringify(result, null, 2)}\n`, 'utf8');
|
|
await context.close();
|
|
process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
|
|
if (result.failedActionCount > 0 || result.runtimeIssueCount > 0) {
|
|
throw new Error(`hotfix action check failed: failedActionCount=${result.failedActionCount} runtimeIssueCount=${result.runtimeIssueCount}`);
|
|
}
|
|
} finally {
|
|
await browser.close();
|
|
}
|
|
}
|
|
|
|
main().catch((error) => {
|
|
process.stderr.write(`[live-hotfix-action-check] ${error instanceof Error ? error.message : String(error)}\n`);
|
|
process.exit(1);
|
|
});
|