Harden live Playwright action sweeps for cold-loaded surfaces

This commit is contained in:
master
2026-03-11 09:44:42 +02:00
parent ae09af4e65
commit 7a1c090f2e
3 changed files with 89 additions and 32 deletions

View File

@@ -15,6 +15,7 @@ const authStatePath = path.join(outputDir, 'live-mission-control-action-sweep.st
const authReportPath = path.join(outputDir, 'live-mission-control-action-sweep.auth.json');
const scopeQuery = 'tenant=demo-prod&regions=us-east&environments=stage&timeWindow=7d';
const STEP_TIMEOUT_MS = 30_000;
const ELEMENT_WAIT_MS = 8_000;
function isStaticAsset(url) {
return /\.(?:css|js|map|png|jpg|jpeg|svg|woff2?)(?:$|\?)/i.test(url);
@@ -86,7 +87,7 @@ function attachRuntimeObservers(page, runtime) {
async function settle(page) {
await page.waitForLoadState('domcontentloaded', { timeout: 15_000 }).catch(() => {});
await page.waitForTimeout(1_000);
await page.waitForTimeout(1_500);
}
async function headingText(page) {
@@ -136,29 +137,35 @@ async function navigate(page, route) {
await settle(page);
}
async function resolveLink(page, options) {
if (options.hrefIncludes) {
const candidates = page.locator(`a[href*="${options.hrefIncludes}"]`);
const count = await candidates.count();
for (let index = 0; index < count; index += 1) {
const candidate = candidates.nth(index);
const text = ((await candidate.innerText().catch(() => '')) || '').trim();
if (!options.name || text === options.name || text.includes(options.name)) {
return candidate;
async function resolveLink(page, options, timeoutMs = ELEMENT_WAIT_MS) {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
if (options.hrefIncludes) {
const candidates = page.locator(`a[href*="${options.hrefIncludes}"]`);
const count = await candidates.count();
for (let index = 0; index < count; index += 1) {
const candidate = candidates.nth(index);
const text = ((await candidate.innerText().catch(() => '')) || '').trim();
if (!options.name || text === options.name || text.includes(options.name)) {
return candidate;
}
}
}
}
if (options.name) {
const roleLocator = page.getByRole('link', { name: options.name }).first();
if ((await roleLocator.count()) > 0) {
return roleLocator;
if (options.name) {
const roleLocator = page.getByRole('link', { name: options.name }).first();
if ((await roleLocator.count()) > 0) {
return roleLocator;
}
const textLocator = page.locator('a', { hasText: options.name }).first();
if ((await textLocator.count()) > 0) {
return textLocator;
}
}
const textLocator = page.locator('a', { hasText: options.name }).first();
if ((await textLocator.count()) > 0) {
return textLocator;
}
await page.waitForTimeout(250);
}
return null;
@@ -279,21 +286,24 @@ async function main() {
{
action: 'link:Stage detail',
name: 'Detail',
hrefIncludes: '/setup/topology/environments/stage/posture',
hrefIncludes:
'/setup/topology/environments/stage/posture?tenant=demo-prod&regions=us-east&environments=stage&timeWindow=7d&region=us-east&environment=stage',
expectedPath: '/setup/topology/environments/stage/posture',
expectQuery: { environment: 'stage', region: 'us-east' },
},
{
action: 'link:Stage findings',
name: 'Findings',
hrefIncludes: 'environment=stage',
hrefIncludes:
'/security/findings?tenant=demo-prod&regions=us-east&environments=stage&timeWindow=7d&region=us-east&environment=stage',
expectedPath: '/security/findings',
expectQuery: { environment: 'stage', region: 'us-east' },
},
{
action: 'link:Risk table open stage',
name: 'Open',
hrefIncludes: '/setup/topology/environments/stage/posture',
hrefIncludes:
'/setup/topology/environments/stage/posture?tenant=demo-prod&regions=us-east&environments=stage&timeWindow=7d&region=us-east&environment=stage',
expectedPath: '/setup/topology/environments/stage/posture',
expectQuery: { environment: 'stage', region: 'us-east' },
},
@@ -310,6 +320,7 @@ async function main() {
{
action: 'link:Watchlist alert',
name: 'Identity watchlist alert requires signer review',
hrefIncludes: 'alertId=alert-001&returnTo=%2Fmission-control%2Falerts',
expectedPath: '/setup/trust-signing/watchlist/alerts',
expectQuery: {
alertId: 'alert-001',