style(web): format chart-links E2E

Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
This commit is contained in:
Cursor Agent 2026-10-07 12:57:48 +00:00
parent 51b456409d
commit 22000ad2ad
No known key found for this signature in database

View File

@ -3,90 +3,91 @@ import { captureScreenshot } from "./helpers";
const fixture = "/e2e/fixtures/chart-links.html"; const fixture = "/e2e/fixtures/chart-links.html";
test.describe.serial("chart links", () => { test.describe
test("cannot execute script when clicked or expanded", async ({ page }, testInfo) => { .serial("chart links", () => {
test.setTimeout(120_000); test("cannot execute script when clicked or expanded", async ({ page }, testInfo) => {
// ChartCanvas lazy-loads Observable Plot; pay the import cost once before the matrix below. test.setTimeout(120_000);
await page.goto(`${fixture}?${new URLSearchParams({ href: "#details", inspect: "0" })}`); // ChartCanvas lazy-loads Observable Plot; pay the import cost once before the matrix below.
await expect(page.locator("svg circle").first()).toBeVisible({ timeout: 90_000 }); await page.goto(`${fixture}?${new URLSearchParams({ href: "#details", inspect: "0" })}`);
for (const inspect of ["0", "1"]) { await expect(page.locator("svg circle").first()).toBeVisible({ timeout: 90_000 });
for (const href of [ for (const inspect of ["0", "1"]) {
'javascript:void(document.body.dataset.plotExecuted="yes")', for (const href of [
' \u0000\u001fJaVa\tScRi\nPt:void(document.body.dataset.plotExecuted="yes")', 'javascript:void(document.body.dataset.plotExecuted="yes")',
'javascript:%76oid(document.body.dataset.plotExecuted="yes")', ' \u0000\u001fJaVa\tScRi\nPt:void(document.body.dataset.plotExecuted="yes")',
"data:text/html,<script>document.body.dataset.plotExecuted='yes'</script>", 'javascript:%76oid(document.body.dataset.plotExecuted="yes")',
"file:///example.txt", "data:text/html,<script>document.body.dataset.plotExecuted='yes'</script>",
]) { "file:///example.txt",
await page.goto(`${fixture}?${new URLSearchParams({ href, inspect })}`); ]) {
const circle = page.locator("svg circle").first(); await page.goto(`${fixture}?${new URLSearchParams({ href, inspect })}`);
// Every navigation renders Plot again, which takes seconds on a loaded CI runner. const circle = page.locator("svg circle").first();
await expect(circle).toBeVisible({ timeout: 30_000 }); // Every navigation renders Plot again, which takes seconds on a loaded CI runner.
const assertInert = async () => { await expect(circle).toBeVisible({ timeout: 30_000 });
const links = page.locator("svg a"); const assertInert = async () => {
expect(await links.count()).toBeGreaterThan(0); const links = page.locator("svg a");
expect( expect(await links.count()).toBeGreaterThan(0);
await links.evaluateAll((anchors) => expect(
anchors.every( await links.evaluateAll((anchors) =>
(anchor) => anchors.every(
!anchor.hasAttribute("href") && (anchor) =>
!anchor.hasAttributeNS("http://www.w3.org/1999/xlink", "href"), !anchor.hasAttribute("href") &&
!anchor.hasAttributeNS("http://www.w3.org/1999/xlink", "href"),
),
), ),
), ).toBe(true);
).toBe(true); await expect(page.locator("body")).not.toHaveAttribute("data-plot-executed");
await expect(page.locator("body")).not.toHaveAttribute("data-plot-executed"); };
}; await assertInert();
await assertInert(); await circle.click();
await circle.click(); // Hover inspection still works, without reintroducing unsafe links.
// Hover inspection still works, without reintroducing unsafe links. if (inspect === "1") await expect(page.locator('[aria-label="tip"]')).toBeVisible();
if (inspect === "1") await expect(page.locator('[aria-label="tip"]')).toBeVisible(); await assertInert();
await assertInert(); await page.getByRole("button", { name: "Expand", exact: true }).click();
await page.getByRole("button", { name: "Expand", exact: true }).click(); const dialog = page.getByRole("dialog");
const dialog = page.getByRole("dialog"); await expect(dialog.locator("svg circle")).toBeVisible({ timeout: 30_000 });
await expect(dialog.locator("svg circle")).toBeVisible({ timeout: 30_000 }); await dialog.locator("svg circle").click();
await dialog.locator("svg circle").click(); await assertInert();
await assertInert(); await expect(page).toHaveURL(`${fixture}?${new URLSearchParams({ href, inspect })}`);
await expect(page).toHaveURL(`${fixture}?${new URLSearchParams({ href, inspect })}`); await page.getByRole("button", { name: "Close chart" }).click();
await page.getByRole("button", { name: "Close chart" }).click(); }
} }
} await captureScreenshot(page, testInfo, "chart-with-inert-link");
await captureScreenshot(page, testInfo, "chart-with-inert-link"); });
});
test("retain supported URLs and browser encoding semantics", async ({ page }) => { test("retain supported URLs and browser encoding semantics", async ({ page }) => {
test.setTimeout(120_000); test.setTimeout(120_000);
await page.route("https://example.test/**", (route) => await page.route("https://example.test/**", (route) =>
route.fulfill({ body: "Chart destination" }), route.fulfill({ body: "Chart destination" }),
); );
await page.route("**/e2e/fixtures/java*", (route) => await page.route("**/e2e/fixtures/java*", (route) =>
route.fulfill({ body: "Chart destination" }), route.fulfill({ body: "Chart destination" }),
); );
await page.goto(`${fixture}?${new URLSearchParams({ href: "#details" })}`); await page.goto(`${fixture}?${new URLSearchParams({ href: "#details" })}`);
await expect(page.locator("svg circle").first()).toBeVisible({ timeout: 30_000 });
for (const href of [
"https://example.test/chart?a=1&b=2",
"mailto:chart@example.test",
"tel:+15555550123",
"#details",
"java&#x73;cript:void(0)",
"javascript%3Avoid(0)",
"java%73cript:void(0)",
]) {
await page.goto(`${fixture}?${new URLSearchParams({ href })}`);
await expect(page.locator("svg circle").first()).toBeVisible({ timeout: 30_000 }); await expect(page.locator("svg circle").first()).toBeVisible({ timeout: 30_000 });
const link = page.locator("svg a").first(); for (const href of [
await expect(link).toBeAttached(); "https://example.test/chart?a=1&b=2",
expect( "mailto:chart@example.test",
await link.evaluate((anchor) => "tel:+15555550123",
anchor.getAttributeNS("http://www.w3.org/1999/xlink", "href"), "#details",
), "java&#x73;cript:void(0)",
).toBe(href); "javascript%3Avoid(0)",
const destination = new URL(href, page.url()); "java%73cript:void(0)",
expect(destination.protocol).not.toBe("javascript:"); ]) {
if (destination.protocol === "http:" || destination.protocol === "https:") { await page.goto(`${fixture}?${new URLSearchParams({ href })}`);
await link.locator("circle").click(); await expect(page.locator("svg circle").first()).toBeVisible({ timeout: 30_000 });
await expect(page).toHaveURL(destination.href); const link = page.locator("svg a").first();
await expect(page.locator("body")).not.toHaveAttribute("data-plot-executed"); await expect(link).toBeAttached();
expect(
await link.evaluate((anchor) =>
anchor.getAttributeNS("http://www.w3.org/1999/xlink", "href"),
),
).toBe(href);
const destination = new URL(href, page.url());
expect(destination.protocol).not.toBe("javascript:");
if (destination.protocol === "http:" || destination.protocol === "https:") {
await link.locator("circle").click();
await expect(page).toHaveURL(destination.href);
await expect(page.locator("body")).not.toHaveAttribute("data-plot-executed");
}
} }
} });
}); });
});