197 lines
5.2 KiB
JavaScript
197 lines
5.2 KiB
JavaScript
import fs from 'node:fs/promises';
|
|
import path from 'node:path';
|
|
import { chromium, devices } from 'playwright';
|
|
|
|
const baseUrl = 'https://127.0.0.1:4400';
|
|
const outputDir = path.resolve(process.cwd(), '..', '..', '..', 'output', 'playwright', 'qa-visual-review');
|
|
|
|
const mockConfig = {
|
|
authority: {
|
|
issuer: 'https://authority.local',
|
|
clientId: 'stella-ops-ui',
|
|
authorizeEndpoint: 'https://authority.local/connect/authorize',
|
|
tokenEndpoint: 'https://authority.local/connect/token',
|
|
logoutEndpoint: 'https://authority.local/connect/logout',
|
|
redirectUri: 'https://127.0.0.1:4400/auth/callback',
|
|
postLogoutRedirectUri: 'https://127.0.0.1:4400/',
|
|
scope: 'openid profile email ui.read',
|
|
audience: 'https://scanner.local',
|
|
dpopAlgorithms: ['ES256'],
|
|
refreshLeewaySeconds: 60,
|
|
},
|
|
apiBaseUrls: {
|
|
authority: 'https://authority.local',
|
|
scanner: 'https://scanner.local',
|
|
policy: 'https://policy.local',
|
|
concelier: 'https://concelier.local',
|
|
attestor: 'https://attestor.local',
|
|
gateway: 'https://gateway.local',
|
|
},
|
|
quickstartMode: true,
|
|
setup: 'complete',
|
|
};
|
|
|
|
const oidcConfig = {
|
|
issuer: mockConfig.authority.issuer,
|
|
authorization_endpoint: mockConfig.authority.authorizeEndpoint,
|
|
token_endpoint: mockConfig.authority.tokenEndpoint,
|
|
jwks_uri: 'https://authority.local/.well-known/jwks.json',
|
|
response_types_supported: ['code'],
|
|
subject_types_supported: ['public'],
|
|
id_token_signing_alg_values_supported: ['RS256'],
|
|
};
|
|
|
|
const shellSession = {
|
|
subjectId: 'qa-visual-user',
|
|
tenant: 'tenant-default',
|
|
scopes: [
|
|
'ui.read',
|
|
'admin',
|
|
'ui.admin',
|
|
'orch:read',
|
|
'orch:operate',
|
|
'orch:quota',
|
|
'findings:read',
|
|
'vuln:view',
|
|
'vuln:investigate',
|
|
'vuln:operate',
|
|
'vuln:audit',
|
|
'authority:tenants.read',
|
|
'advisory:read',
|
|
'vex:read',
|
|
'exceptions:read',
|
|
'exceptions:approve',
|
|
'aoc:verify',
|
|
'policy:read',
|
|
'policy:author',
|
|
'policy:review',
|
|
'policy:approve',
|
|
'policy:simulate',
|
|
'policy:audit',
|
|
'health:read',
|
|
'notify:viewer',
|
|
'release:read',
|
|
'release:write',
|
|
'release:publish',
|
|
'sbom:read',
|
|
'signer:read',
|
|
'analytics.read',
|
|
'scheduler:read',
|
|
'scheduler:operate',
|
|
],
|
|
};
|
|
|
|
const routesToCapture = [
|
|
'/setup',
|
|
'/setup/wizard',
|
|
'/mission-control/board',
|
|
'/security',
|
|
'/security/findings',
|
|
'/releases',
|
|
'/ops',
|
|
];
|
|
|
|
function sanitizeRoute(route) {
|
|
return route.replace(/^\//, '').replace(/[/?#=&]+/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '') || 'home';
|
|
}
|
|
|
|
async function applyMocks(page) {
|
|
await page.route('**/*', (route) => {
|
|
const url = route.request().url();
|
|
|
|
if (url.includes('/config.json')) {
|
|
return route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify(mockConfig),
|
|
});
|
|
}
|
|
|
|
if (url.includes('/platform/envsettings.json')) {
|
|
return route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify(mockConfig),
|
|
});
|
|
}
|
|
|
|
if (url.includes('/.well-known/openid-configuration')) {
|
|
return route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify(oidcConfig),
|
|
});
|
|
}
|
|
|
|
if (url.includes('/.well-known/jwks.json')) {
|
|
return route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({ keys: [] }),
|
|
});
|
|
}
|
|
|
|
const apiLike =
|
|
url.includes('authority.local') ||
|
|
url.includes('scanner.local') ||
|
|
url.includes('policy.local') ||
|
|
url.includes('concelier.local') ||
|
|
url.includes('attestor.local') ||
|
|
url.includes('gateway.local') ||
|
|
url.includes('/platform/') ||
|
|
url.includes('/authority/') ||
|
|
url.includes('/scanner/') ||
|
|
url.includes('/policy/') ||
|
|
url.includes('/concelier/') ||
|
|
url.includes('/attestor/') ||
|
|
url.includes('/api/');
|
|
|
|
if (apiLike) {
|
|
return route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({ items: [], data: [], total: 0 }),
|
|
});
|
|
}
|
|
|
|
return route.continue();
|
|
});
|
|
|
|
await page.addInitScript((session) => {
|
|
window.__stellaopsTestSession = session;
|
|
}, shellSession);
|
|
}
|
|
|
|
async function captureContext(browser, name, contextOptions) {
|
|
const context = await browser.newContext({ ignoreHTTPSErrors: true, ...contextOptions });
|
|
const page = await context.newPage();
|
|
|
|
await applyMocks(page);
|
|
|
|
for (const route of routesToCapture) {
|
|
const fullUrl = `${baseUrl}${route}`;
|
|
await page.goto(fullUrl, { waitUntil: 'domcontentloaded' });
|
|
await page.waitForTimeout(2500);
|
|
|
|
const file = path.join(outputDir, `${name}-${sanitizeRoute(route)}.png`);
|
|
await page.screenshot({ path: file, fullPage: true });
|
|
console.log(`[${name}] captured ${route} -> ${file}`);
|
|
}
|
|
|
|
await context.close();
|
|
}
|
|
|
|
(async () => {
|
|
await fs.mkdir(outputDir, { recursive: true });
|
|
const browser = await chromium.launch({ headless: true });
|
|
|
|
try {
|
|
await captureContext(browser, 'desktop', { viewport: { width: 1440, height: 1024 } });
|
|
await captureContext(browser, 'mobile', devices['iPhone 13']);
|
|
} finally {
|
|
await browser.close();
|
|
}
|
|
|
|
console.log(`Screenshots saved in ${outputDir}`);
|
|
})();
|