Skip to content

Commit 17a475c

Browse files
bjohansebasclaude
andcommitted
test(e2e): keyboard pagination, paginate=false, and the cross-copy runtime filter
Arrow keys page through problems with real keyboard events (the frame holds focus after the pager click), paginate=false shows the full problem list with no counter, and a runtime filter configured by a later bundled copy is honored by the window listeners the first copy attached — proven non-vacuously by first catching an error through copy A, then watching copy B's rejecting filter keep the overlay away. The three jsdom equivalents are removed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent e1b9eca commit 17a475c

2 files changed

Lines changed: 79 additions & 48 deletions

File tree

test/e2e/overlay.test.js

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,11 +313,58 @@ describe("error overlay (browser)", () => {
313313
document.body.textContent.includes("2 / 2"),
314314
);
315315

316+
// Real keyboard navigation — the frame has focus after the click.
317+
await page.keyboard.press("ArrowLeft");
318+
await frame.waitForFunction(() =>
319+
document.body.textContent.includes("1 / 2"),
320+
);
321+
await page.keyboard.press("ArrowRight");
322+
await frame.waitForFunction(() =>
323+
document.body.textContent.includes("2 / 2"),
324+
);
325+
316326
expect(await frame.evaluate(() => document.body.textContent)).toContain(
317327
"2 / 2",
318328
);
319329
});
320330

331+
it("shows the full problem list when paginate=false", async () => {
332+
hotApp = await createHotApp({
333+
query: '?overlay={"paginate":false}',
334+
code: `
335+
try {
336+
require("./a");
337+
} catch (err) {
338+
// expected
339+
}
340+
try {
341+
require("./b");
342+
} catch (err) {
343+
// expected
344+
}
345+
`,
346+
files: {
347+
"a.js": "broken a {{{",
348+
"b.js": "broken b {{{",
349+
},
350+
});
351+
({ page, browser } = await runBrowser());
352+
353+
await page.goto(hotApp.url);
354+
355+
const frame = await waitForOverlay(page);
356+
await frame.waitForFunction(
357+
() =>
358+
document.body.textContent.includes("broken a {{{") &&
359+
document.body.textContent.includes("broken b {{{"),
360+
);
361+
362+
// Both problems at once, no pager.
363+
expect(await frame.evaluate(() => document.body.textContent)).not.toContain(
364+
"1 / 2",
365+
);
366+
});
367+
321368
it("accumulates runtime errors and pages between them", async () => {
322369
hotApp = await createHotApp({
323370
code: `
@@ -453,7 +500,12 @@ describe("overlay shared state across bundled copies (browser)", () => {
453500
* @returns {string} app source
454501
*/
455502
const exposeOverlay = (globalName) =>
456-
`globalThis.${globalName} = require(${JSON.stringify(OVERLAY_ENTRY)});`;
503+
`globalThis.${globalName} = require(${JSON.stringify(OVERLAY_ENTRY)});
504+
globalThis.boom = (message) => {
505+
setTimeout(() => {
506+
throw new Error(message);
507+
}, 0);
508+
};`;
457509

458510
const start = async () => {
459511
hotApp = await createHotApp({
@@ -605,6 +657,32 @@ describe("overlay shared state across bundled copies (browser)", () => {
605657
expect(await page.$(`#${OVERLAY_ID}`)).not.toBeNull();
606658
});
607659

660+
it("honors the runtime filter configured by a later copy", async () => {
661+
await start();
662+
await page.goto(hotApp.url);
663+
664+
// The first copy attaches the window listeners; a runtime error lands in
665+
// the overlay, proving they are live.
666+
await page.evaluate(() => {
667+
globalThis.overlayA.default({ catchRuntimeError: true });
668+
globalThis.boom("caught-by-A");
669+
});
670+
await page.waitForSelector(`#${OVERLAY_ID}`, { timeout: 30000 });
671+
await page.evaluate(() => globalThis.overlayA.clear());
672+
673+
// A later copy swaps in a rejecting filter — the listeners the first
674+
// copy attached must honor it.
675+
await page.evaluate(() => {
676+
globalThis.overlayB.default({ catchRuntimeError: () => false });
677+
globalThis.boom("filtered-out");
678+
});
679+
await new Promise((resolve) => {
680+
setTimeout(resolve, 500);
681+
});
682+
683+
expect(await page.$(`#${OVERLAY_ID}`)).toBeNull();
684+
});
685+
608686
it("fills state fields missing from an older copy's shape", async () => {
609687
await start();
610688
// An older package version created a leaner shared state before the

test/overlay.test.js

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -170,30 +170,6 @@ describe("overlay", () => {
170170
expect(getCard().textContent).not.toContain("boom-before");
171171
expect(getCard().textContent).not.toContain("1 / 2");
172172
});
173-
174-
it("honors the runtime filter configured by a later copy", () => {
175-
// The window listeners were attached by this copy…
176-
configureOverlay({ catchRuntimeError: true });
177-
178-
// …but the filter comes from a second bundled copy of the module.
179-
jest.resetModules();
180-
181-
const laterCopy = require("../client-src/overlay");
182-
183-
laterCopy.default({ catchRuntimeError: () => false });
184-
185-
globalThis.dispatchEvent(
186-
new ErrorEvent("error", {
187-
error: new Error("filtered-out"),
188-
message: "filtered-out",
189-
}),
190-
);
191-
192-
expect(getOverlay()).toBeNull();
193-
194-
// Restore the plain-boolean configuration for the remaining tests.
195-
configureOverlay({ catchRuntimeError: true });
196-
});
197173
});
198174

199175
describe("open in editor", () => {
@@ -274,20 +250,6 @@ describe("overlay", () => {
274250
expect(getCard().textContent).toContain("first boom");
275251
});
276252

277-
it("navigates with the arrow keys", () => {
278-
showProblems("errors", ["first boom", "second boom"]);
279-
280-
getOverlay().contentDocument.dispatchEvent(
281-
new KeyboardEvent("keydown", { key: "ArrowRight" }),
282-
);
283-
expect(getCard().textContent).toContain("second boom");
284-
285-
getOverlay().contentDocument.dispatchEvent(
286-
new KeyboardEvent("keydown", { key: "ArrowLeft" }),
287-
);
288-
expect(getCard().textContent).toContain("first boom");
289-
});
290-
291253
it("resets to the first page on a new problem set", () => {
292254
showProblems("errors", ["first boom", "second boom"]);
293255

@@ -313,15 +275,6 @@ describe("overlay", () => {
313275
expect(getCard().textContent).toContain("second boom");
314276
expect(getCard().textContent).toContain("2 / 2");
315277
});
316-
317-
it("shows the full list when disabled", () => {
318-
configureOverlay({ paginate: false });
319-
showProblems("errors", ["first boom", "second boom"]);
320-
321-
expect(getCard().textContent).toContain("first boom");
322-
expect(getCard().textContent).toContain("second boom");
323-
expect(getCard().textContent).not.toContain("1 / 2");
324-
});
325278
});
326279

327280
describe("configureOverlay", () => {

0 commit comments

Comments
 (0)