test(browser): tighten relay test watchdog timeouts

This commit is contained in:
Peter Steinberger
2026-02-21 23:03:29 +00:00
parent 1534248169
commit b1c50cc5c0

View File

@@ -9,6 +9,10 @@ import {
} from "./extension-relay.js"; } from "./extension-relay.js";
import { getFreePort } from "./test-port.js"; import { getFreePort } from "./test-port.js";
const RELAY_MESSAGE_TIMEOUT_MS = 2_000;
const RELAY_LIST_MATCH_TIMEOUT_MS = 1_500;
const RELAY_TEST_TIMEOUT_MS = 10_000;
function waitForOpen(ws: WebSocket) { function waitForOpen(ws: WebSocket) {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
ws.once("open", () => resolve()); ws.once("open", () => resolve());
@@ -81,7 +85,7 @@ function createMessageQueue(ws: WebSocket) {
reject(err instanceof Error ? err : new Error(String(err))); reject(err instanceof Error ? err : new Error(String(err)));
}); });
const next = (timeoutMs = 5000) => const next = (timeoutMs = RELAY_MESSAGE_TIMEOUT_MS) =>
new Promise<string>((resolve, reject) => { new Promise<string>((resolve, reject) => {
const existing = queue.shift(); const existing = queue.shift();
if (existing !== undefined) { if (existing !== undefined) {
@@ -103,7 +107,7 @@ function createMessageQueue(ws: WebSocket) {
async function waitForListMatch<T>( async function waitForListMatch<T>(
fetchList: () => Promise<T>, fetchList: () => Promise<T>,
predicate: (value: T) => boolean, predicate: (value: T) => boolean,
timeoutMs = 2000, timeoutMs = RELAY_LIST_MATCH_TIMEOUT_MS,
intervalMs = 50, intervalMs = 50,
): Promise<T> { ): Promise<T> {
let latest: T | undefined; let latest: T | undefined;
@@ -217,123 +221,129 @@ describe("chrome extension relay server", () => {
ext.close(); ext.close();
}); });
it("tracks attached page targets and exposes them via CDP + /json/list", async () => { it(
const port = await getFreePort(); "tracks attached page targets and exposes them via CDP + /json/list",
cdpUrl = `http://127.0.0.1:${port}`; async () => {
await ensureChromeExtensionRelayServer({ cdpUrl }); const port = await getFreePort();
cdpUrl = `http://127.0.0.1:${port}`;
await ensureChromeExtensionRelayServer({ cdpUrl });
const ext = new WebSocket(`ws://127.0.0.1:${port}/extension`, { const ext = new WebSocket(`ws://127.0.0.1:${port}/extension`, {
headers: relayAuthHeaders(`ws://127.0.0.1:${port}/extension`), headers: relayAuthHeaders(`ws://127.0.0.1:${port}/extension`),
}); });
await waitForOpen(ext); await waitForOpen(ext);
// Simulate a tab attach coming from the extension. // Simulate a tab attach coming from the extension.
ext.send( ext.send(
JSON.stringify({ JSON.stringify({
method: "forwardCDPEvent", method: "forwardCDPEvent",
params: {
method: "Target.attachedToTarget",
params: { params: {
sessionId: "cb-tab-1", method: "Target.attachedToTarget",
targetInfo: { params: {
targetId: "t1", sessionId: "cb-tab-1",
type: "page", targetInfo: {
title: "Example", targetId: "t1",
url: "https://example.com", type: "page",
}, title: "Example",
waitingForDebugger: false, url: "https://example.com",
}, },
}, waitingForDebugger: false,
}),
);
const list = (await fetch(`${cdpUrl}/json/list`, {
headers: relayAuthHeaders(cdpUrl),
}).then((r) => r.json())) as Array<{
id?: string;
url?: string;
title?: string;
}>;
expect(list.some((t) => t.id === "t1" && t.url === "https://example.com")).toBe(true);
// Simulate navigation updating tab metadata.
ext.send(
JSON.stringify({
method: "forwardCDPEvent",
params: {
method: "Target.targetInfoChanged",
params: {
targetInfo: {
targetId: "t1",
type: "page",
title: "DER STANDARD",
url: "https://www.derstandard.at/",
}, },
}, },
}, }),
}), );
);
const list2 = await waitForListMatch( const list = (await fetch(`${cdpUrl}/json/list`, {
async () => headers: relayAuthHeaders(cdpUrl),
(await fetch(`${cdpUrl}/json/list`, { }).then((r) => r.json())) as Array<{
headers: relayAuthHeaders(cdpUrl), id?: string;
}).then((r) => r.json())) as Array<{ url?: string;
id?: string; title?: string;
url?: string; }>;
title?: string; expect(list.some((t) => t.id === "t1" && t.url === "https://example.com")).toBe(true);
}>,
(list) => // Simulate navigation updating tab metadata.
list.some( ext.send(
JSON.stringify({
method: "forwardCDPEvent",
params: {
method: "Target.targetInfoChanged",
params: {
targetInfo: {
targetId: "t1",
type: "page",
title: "DER STANDARD",
url: "https://www.derstandard.at/",
},
},
},
}),
);
const list2 = await waitForListMatch(
async () =>
(await fetch(`${cdpUrl}/json/list`, {
headers: relayAuthHeaders(cdpUrl),
}).then((r) => r.json())) as Array<{
id?: string;
url?: string;
title?: string;
}>,
(list) =>
list.some(
(t) =>
t.id === "t1" &&
t.url === "https://www.derstandard.at/" &&
t.title === "DER STANDARD",
),
);
expect(
list2.some(
(t) => (t) =>
t.id === "t1" && t.url === "https://www.derstandard.at/" && t.title === "DER STANDARD", t.id === "t1" && t.url === "https://www.derstandard.at/" && t.title === "DER STANDARD",
), ),
); ).toBe(true);
expect(
list2.some(
(t) =>
t.id === "t1" && t.url === "https://www.derstandard.at/" && t.title === "DER STANDARD",
),
).toBe(true);
const cdp = new WebSocket(`ws://127.0.0.1:${port}/cdp`, { const cdp = new WebSocket(`ws://127.0.0.1:${port}/cdp`, {
headers: relayAuthHeaders(`ws://127.0.0.1:${port}/cdp`), headers: relayAuthHeaders(`ws://127.0.0.1:${port}/cdp`),
}); });
await waitForOpen(cdp); await waitForOpen(cdp);
const q = createMessageQueue(cdp); const q = createMessageQueue(cdp);
cdp.send(JSON.stringify({ id: 1, method: "Target.getTargets" })); cdp.send(JSON.stringify({ id: 1, method: "Target.getTargets" }));
const res1 = JSON.parse(await q.next()) as { id: number; result?: unknown }; const res1 = JSON.parse(await q.next()) as { id: number; result?: unknown };
expect(res1.id).toBe(1); expect(res1.id).toBe(1);
expect(JSON.stringify(res1.result ?? {})).toContain("t1"); expect(JSON.stringify(res1.result ?? {})).toContain("t1");
cdp.send( cdp.send(
JSON.stringify({ JSON.stringify({
id: 2, id: 2,
method: "Target.attachToTarget", method: "Target.attachToTarget",
params: { targetId: "t1" }, params: { targetId: "t1" },
}), }),
); );
const received: Array<{ const received: Array<{
id?: number; id?: number;
method?: string; method?: string;
result?: unknown; result?: unknown;
params?: unknown; params?: unknown;
}> = []; }> = [];
received.push(JSON.parse(await q.next()) as never); received.push(JSON.parse(await q.next()) as never);
received.push(JSON.parse(await q.next()) as never); received.push(JSON.parse(await q.next()) as never);
const res2 = received.find((m) => m.id === 2); const res2 = received.find((m) => m.id === 2);
expect(res2?.id).toBe(2); expect(res2?.id).toBe(2);
expect(JSON.stringify(res2?.result ?? {})).toContain("cb-tab-1"); expect(JSON.stringify(res2?.result ?? {})).toContain("cb-tab-1");
const evt = received.find((m) => m.method === "Target.attachedToTarget"); const evt = received.find((m) => m.method === "Target.attachedToTarget");
expect(evt?.method).toBe("Target.attachedToTarget"); expect(evt?.method).toBe("Target.attachedToTarget");
expect(JSON.stringify(evt?.params ?? {})).toContain("t1"); expect(JSON.stringify(evt?.params ?? {})).toContain("t1");
cdp.close(); cdp.close();
ext.close(); ext.close();
}, 15_000); },
RELAY_TEST_TIMEOUT_MS,
);
it("rebroadcasts attach when a session id is reused for a new target", async () => { it("rebroadcasts attach when a session id is reused for a new target", async () => {
const port = await getFreePort(); const port = await getFreePort();