test: cover persistent browser navigation

This commit is contained in:
Codex
2026-07-30 22:13:05 +08:00
parent 25502a52bb
commit 4fbc902325
+58 -4
View File
@@ -19,6 +19,7 @@ const FORBIDDEN_PORT = 8788;
const EXTERNAL_ORIGIN = parseExternalE2eOrigin(process.env.E2E_ORIGIN);
const SCREENSHOT_DIR = process.env.E2E_SCREENSHOT_DIR?.trim();
const FLEET_FIXTURE = process.env.E2E_FLEET_FIXTURE !== '0';
const requestPathnames = [];
const CHROME_CANDIDATES = [
process.env.CHROME_BIN,
@@ -77,11 +78,20 @@ const FLEET_FIXTURE_ITEMS = [
async function builtAssetHandler(request, response) {
const pathname = new URL(request.url ?? '/', 'http://e2e.local').pathname;
if (pathname === '/favicon.ico' || pathname === '/api/v1/events') {
requestPathnames.push(pathname);
if (pathname === '/favicon.ico') {
response.statusCode = 204;
response.end();
return;
}
if (pathname === '/api/v1/events') {
response.writeHead(200, {
'content-type': 'text/event-stream',
'cache-control': 'no-cache',
});
response.flushHeaders();
return;
}
let body;
if (pathname === '/api/v1/auth/status')
body = { configured: false, protectionEnabled: false, authenticated: false };
@@ -884,7 +894,51 @@ try {
);
await assertWidePageGutters(cdp, 'Nodes');
await keyboardNavigate(cdp, '设置', '/settings/instances', '实例');
await eventually(
cdp,
`document.querySelector('.connection-status [data-state="open"]') !== null`,
'Event stream did not reach the open state',
);
await evaluate(cdp, `window.__navigationLifetime = crypto.randomUUID()`);
const navigationLifetime = await evaluate(cdp, `window.__navigationLifetime`);
const requestCountsBeforeSettings = EXTERNAL_ORIGIN
? undefined
: {
events: requestPathnames.filter((pathname) => pathname === '/api/v1/events').length,
fleet: requestPathnames.filter(
(pathname) =>
pathname === '/api/v1/instances' ||
/^\/api\/v1\/instances\/[^/]+\/resources$/u.test(pathname),
).length,
};
await keyboardNavigate(cdp, '设置', '/settings/system', '密码保护');
assert.equal(
await evaluate(cdp, `window.__navigationLifetime`),
navigationLifetime,
'Settings navigation reloaded the document',
);
assert.equal(
await evaluate(cdp, `document.activeElement?.id`),
'main-content',
'Settings navigation must focus the main content',
);
if (requestCountsBeforeSettings) {
await delay(100);
assert.equal(
requestPathnames.filter((pathname) => pathname === '/api/v1/events').length,
requestCountsBeforeSettings.events,
'Settings navigation recreated the event subscription',
);
assert.equal(
requestPathnames.filter(
(pathname) =>
pathname === '/api/v1/instances' ||
/^\/api\/v1\/instances\/[^/]+\/resources$/u.test(pathname),
).length,
requestCountsBeforeSettings.fleet,
'Settings navigation requested Fleet data',
);
}
await assertWidePageGutters(cdp, 'Settings');
await keyboardNavigate(cdp, '自动化', '/automation', '自动化');
await assertWidePageGutters(cdp, 'Automation');
@@ -980,7 +1034,7 @@ try {
`document.querySelector('[role="tab"][aria-selected="true"]')?.textContent.trim() === '操作审计'`,
'/audit alias did not select operation audit',
);
await keyboardNavigate(cdp, '设置', '/settings/instances', '实例');
await keyboardNavigate(cdp, '设置', '/settings/system', '密码保护');
assert.deepEqual(failures, [], `Browser failures detected:\n${failures.join('\n')}`);
console.log(`PASS real Chrome E2E (${chromeBinary})`);
@@ -990,7 +1044,7 @@ try {
: `PASS isolated built-asset server on ${origin} (legacy port 8788 untouched)`,
);
console.log(
'PASS three-item navigation, Automation drawer and aliases, batch selection, and 390/768/1024/1440/1920 responsive layouts',
'PASS persistent navigation, live SSE status, Automation workflows, and 390/768/1024/1440/1920 responsive layouts',
);
} catch (error) {
console.error(error instanceof Error ? error.stack : error);