← Files DataARCHIVED FILE
tests/data-report-source-preview-browser.smoke.mjs
15.2 KB · Sep 30, 2026 · 23:19 UTC
import assert from "node:assert/strict";
import { cpSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join, resolve } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { chromium } from "playwright-core";
import { resolveChromiumExecutable, runDataAppFixtureBuild } from "./browser-helpers.mjs";
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const template = join(root, "templates/data-app/base");
const project = mkdtempSync(join(tmpdir(), "data-report-source-preview-"));
const href = "https://example.com/reviewed-source";
const title = "Synthetic experiment readout";
const longHref = "https://example.com/long-source";
const wrappedHref = "https://example.com/wrapped-source";
const summary = "The experiment measured repeat use. Its authors recommended another test; they did not record a rollout decision.";
const providerCases = [
["Google Docs", "https://docs.google.com/document/d/synthetic/edit", "google-docs"],
["Google Sheets", "https://docs.google.com/spreadsheets/d/synthetic/edit", "google-sheets"],
["Google Slides", "https://docs.google.com/presentation/d/synthetic/edit", "google-slides"],
["Google Drive", "https://drive.google.com/file/d/synthetic/view", "google-drive"],
["Slack", "https://example.slack.com/archives/synthetic", "slack"],
["Notion", "https://www.notion.so/synthetic", "notion"],
["GitHub", "https://github.com/example/synthetic", "github"],
["Statsig", "https://console.statsig.com/synthetic", "database"],
];
const previews = { [href]: { title, summary, source: "Experiment review", date: "August 2026", approvedForReport: true },
[longHref]: { title: "Long source", summary: "Additional reviewed context. ".repeat(28), approvedForReport: true },
[wrappedHref]: { title: "Wrapped source", summary: "A short reviewed summary.", approvedForReport: true },
"https://example.com/restricted": { title: "Restricted title", summary: "RESTRICTED_PREVIEW_SENTINEL" } };
for (const [label, url] of providerCases) previews[url] = { title: `${label} fixture`, summary: "Synthetic source context.", approvedForReport: true };
const markdown = `The [reviewed experiment](${href}) informs the next test. [Ordinary source](https://example.com/ordinary). [Restricted source](https://example.com/restricted).`;
const authored = `import React from "react";
import { RichNarrative } from "../../data-app-public.jsx";
export function ReportContent() { return <article className="report-content"><h1>Source preview contract</h1>
<RichNarrative id="preview:body" value={${JSON.stringify(markdown)}} sourcePreviews={${JSON.stringify(previews)}} />
<RichNarrative id="preview:providers" value={${JSON.stringify(providerCases.map(([label,url])=>`[${label} fixture](${url})`).join(" · "))}} sourcePreviews={${JSON.stringify(previews)}} />
<div style={{height:500}} /><RichNarrative id="preview:long" value={${JSON.stringify(`[Long reviewed source](${longHref})`)}} sourcePreviews={${JSON.stringify(previews)}} /><div style={{height:500}} />
<div style={{width:240,marginInline:"auto"}}><RichNarrative id="preview:wrapped" value={${JSON.stringify(`Read the [complete reviewed source explaining the comparison and its limitations](${wrappedHref}).`)}} sourcePreviews={${JSON.stringify(previews)}} /></div><div style={{height:500}} />
<button type="button">Outside target</button></article>; }`;
let browser;
const errors = [];
try {
cpSync(template, project, { recursive: true, filter: (path) => !path.includes("/node_modules") && !path.includes("/dist") });
writeFileSync(join(project, "src/content/report/ReportContent.jsx"), authored);
writeFileSync(join(project, "src/data.json"), JSON.stringify({ id: "source-preview-contract", surface: "report",
title: "Source preview contract", status: "fixture", generatedAt: "2026-08-19T12:00:00Z", filters: [], queries: {} }));
const build = runDataAppFixtureBuild(project, { pluginRoot: root });
assert.equal(build.status, 0, `${build.stdout}\n${build.stderr}`);
browser = await chromium.launch({ executablePath: resolveChromiumExecutable(), headless: true });
const page = await browser.newPage({ viewport: { width: 1000, height: 720 } });
page.on("pageerror", (error) => errors.push(error.message));
const sourceRequests = [];
page.on("request", (request) => { if (request.url().startsWith("https://")) sourceRequests.push(request.url()); });
const interceptSourceLinks = () => {
window.__sourceLinks = [];
document.addEventListener("click", (event) => {
const link = event.target.closest('a[href^="https://example.com/"]');
if (link && !event.defaultPrevented) { event.preventDefault(); window.__sourceLinks.push(link.href); }
});
};
await page.addInitScript(interceptSourceLinks);
const url = pathToFileURL(join(project, "dist/index.html")).href;
await page.goto(url, { waitUntil: "load" });
const link = page.getByRole("link", { name: "reviewed experiment", exact: true });
const card = page.locator('.source-preview-card[role="tooltip"]');
assert.equal(await link.getAttribute("href"), href);
assert.equal(await page.getByRole("link", { name: "Ordinary source", exact: true }).getAttribute("aria-haspopup"), null);
assert.equal(await page.getByRole("link", { name: "Restricted source", exact: true }).getAttribute("aria-haspopup"), null);
await page.clock.install();
await page.clock.pauseAt(new Date(Date.now() + 1000));
await link.hover();
await page.clock.runFor(249);
assert.equal(await card.count(), 0, "Passing over a link must not open its tooltip immediately");
await page.mouse.move(5, 500);
await page.clock.runFor(300);
assert.equal(await card.count(), 0, "Leaving the link cancels a pending tooltip");
await link.hover();
await page.keyboard.press("Escape");
await page.clock.runFor(300);
assert.equal(await card.count(), 0, "Escape cancels a pending hover even before the tooltip opens");
await page.mouse.move(5, 500);
await link.hover();
await page.clock.runFor(250);
await card.waitFor();
await page.clock.resume();
assert.match(await card.innerText(), /did not record a rollout decision/u);
assert.deepEqual(await card.evaluate((el) => {
const meta = el.querySelector(".source-preview-meta");
const title = el.querySelector(".source-preview-title");
const summary = el.querySelector(".source-preview-summary");
return { padding: getComputedStyle(el).padding, iconGap: getComputedStyle(meta).gap,
metaToTitle: Math.round(title.getBoundingClientRect().top - meta.getBoundingClientRect().bottom),
titleToSummary: Math.round(summary.getBoundingClientRect().top - title.getBoundingClientRect().bottom) };
}), { padding: "16px", iconGap: "6px", metaToTitle: 8, titleToSummary: 4 },
"The source card keeps its metadata spacing and groups the title with its description");
assert.equal(await card.locator('[data-source-provider="web"]').count(), 1);
const centered = await card.boundingBox();
const centeredAnchor = await link.boundingBox();
const expectedCenterLeft = Math.max(12, Math.min(centeredAnchor.x + centeredAnchor.width / 2 - centered.width / 2, 1000 - centered.width - 12));
assert.ok(Math.abs(centered.x - expectedCenterLeft) < 1, "Source tooltip centers on its link unless constrained by the viewport");
await card.hover();
assert.equal(await card.isVisible(), true);
assert.equal(await card.locator("a,button,[tabindex]").count(), 0, "The tooltip has no duplicate navigation control");
assert.equal(await link.getAttribute("aria-describedby"), await card.getAttribute("id"));
const accessibility = await page.context().newCDPSession(page);
const { root: domRoot } = await accessibility.send("DOM.getDocument");
const { nodeId } = await accessibility.send("DOM.querySelector", { nodeId: domRoot.nodeId, selector: `a[href="${href}"]` });
const { nodes } = await accessibility.send("Accessibility.getPartialAXTree", { nodeId, fetchRelatives: false });
assert.ok(nodes.some((node) => node.description?.value?.includes(summary)), "The accessible description must include the source summary");
await accessibility.detach();
assert.deepEqual(await page.evaluate(() => window.__sourceLinks), []);
assert.deepEqual(sourceRequests, [], "Previews must not fetch linked content");
await page.mouse.click(5, 500);
assert.equal(await card.count(), 0);
for (const [label, , provider] of providerCases) {
await page.getByRole("link", { name: `${label} fixture`, exact: true }).focus();
const icon = card.locator(`[data-source-provider="${provider}"]`);
assert.equal(await icon.count(), 1, label);
assert.ok((await card.innerText()).includes(label), `${label} has a readable fallback source name`);
assert.equal(await icon.getAttribute("aria-hidden"), "true");
const bounds = await icon.boundingBox();
assert.equal(bounds.width, 16);
assert.equal(bounds.height, 16);
for (const img of await icon.locator("img").all())
assert.ok(await img.evaluate(node => node.complete && node.naturalWidth > 0 && node.src.startsWith("data:")), `${label} icon is bundled`);
await page.keyboard.press("Escape");
}
assert.deepEqual(sourceRequests, [], "Provider icons must not request remote assets or source contents");
for (const label of ["GitHub", "Notion"]) {
const colors = [];
for (const colorScheme of ["light", "dark"]) {
await page.emulateMedia({ colorScheme });
await page.waitForFunction((scheme) => document.documentElement.dataset.colorScheme === scheme, colorScheme);
await page.getByRole("button", { name: "Outside target", exact: true }).focus();
await page.getByRole("link", { name: `${label} fixture`, exact: true }).focus();
const mask = card.locator(".source-preview-provider-mask");
const paint = await mask.evaluate(node => { const style = getComputedStyle(node); return {
mask: style.maskImage, background: style.backgroundColor, color: style.color,
width: node.getBoundingClientRect().width, height: node.getBoundingClientRect().height,
}; });
assert.match(paint.mask, /^url\(["']?data:image\/svg\+xml/u, `${label} mask is bundled`);
assert.equal(paint.background, paint.color, `${label} follows the foreground color`);
assert.equal(paint.width, 16);
assert.equal(paint.height, 16);
colors.push(paint.color);
await page.keyboard.press("Escape");
}
assert.notEqual(colors[0], colors[1], `${label} adapts to light and dark appearance`);
}
await page.emulateMedia({ colorScheme: "light" });
await link.focus();
assert.equal(await card.count(), 1, "Keyboard focus opens without the hover delay");
await page.keyboard.press("Tab");
await card.waitFor({ state: "detached" });
await link.focus();
await page.keyboard.press("Escape");
assert.equal(await card.count(), 0);
assert.equal(await link.evaluate((node) => node === document.activeElement), true);
await link.click();
assert.deepEqual(await page.evaluate(() => window.__sourceLinks), [href], "Mouse click remains direct source navigation");
await link.press("Enter");
assert.deepEqual(await page.evaluate(() => window.__sourceLinks), [href, href], "Enter follows the original link");
await page.mouse.move(5, 500);
await link.hover();
await page.emulateMedia({ media: "print" });
assert.equal(await card.isVisible(), false);
assert.equal(await link.getAttribute("href"), href);
await page.emulateMedia({ media: "screen" });
await page.mouse.click(5, 500);
const wrapped = page.getByRole("link", { name: "complete reviewed source explaining the comparison and its limitations", exact: true });
await wrapped.evaluate((node) => node.scrollIntoView({ block: "center" }));
const fragments = await wrapped.evaluate((node) => [...node.getClientRects()].map(({left,right,top,bottom,width,height}) => ({left,right,top,bottom,width,height})));
assert.ok(fragments.length >= 2, "The fixture must exercise a multiline inline link");
for (const fragment of [fragments[0], fragments.at(-1)]) {
await page.mouse.move(fragment.left + fragment.width / 2, fragment.top + fragment.height / 2);
await card.waitFor();
await page.waitForFunction(({fragment}) => {
const rect = document.querySelector('.source-preview-card')?.getBoundingClientRect();
return rect && Math.abs(rect.left + rect.width / 2 - (fragment.left + fragment.width / 2)) < 1
&& rect.bottom <= fragment.top;
}, {fragment});
}
await page.keyboard.press("Escape");
await page.getByRole("button", { name: "Edit text and layout", exact: true }).click();
assert.equal(await card.count(), 0);
assert.equal(await page.locator('[data-editable-id="preview:body"] [contenteditable="true"]').count(), 1);
const mobile = await browser.newPage({ viewport: { width: 390, height: 600 }, hasTouch: true, isMobile: true });
mobile.on("pageerror", (error) => errors.push(error.message));
await mobile.addInitScript(interceptSourceLinks);
await mobile.goto(url, { waitUntil: "load" });
await mobile.getByRole("link", { name: "reviewed experiment", exact: true }).tap();
const mobileCard = mobile.locator('.source-preview-card[role="tooltip"]');
await mobileCard.waitFor();
const currentViewUrl = new URL(url);
currentViewUrl.searchParams.set("view", "1");
assert.equal(mobile.url(), currentViewUrl.href, "First touch opens the preview without navigating");
const bounds = await mobileCard.boundingBox();
assert.ok(bounds.x >= 11 && bounds.x + bounds.width <= 391 && bounds.y >= 11 && bounds.y + bounds.height <= 601, JSON.stringify(bounds));
assert.deepEqual(await mobile.evaluate(() => window.__sourceLinks), []);
assert.equal(await mobileCard.locator("a,button,[tabindex]").count(), 0);
await mobile.getByRole("link", { name: "reviewed experiment", exact: true }).tap();
assert.deepEqual(await mobile.evaluate(() => window.__sourceLinks), [href], "Second touch follows the original link");
assert.equal(await mobile.locator('.dashboard-topbar[data-mode="view"]').count(), 1,
"Two source taps must not trigger double-click-to-edit");
assert.equal(await mobile.locator('[contenteditable="true"]').count(), 0);
assert.equal(await mobileCard.count(), 0);
await mobile.getByRole("link", { name: "reviewed experiment", exact: true }).tap();
await mobileCard.waitFor();
await mobile.touchscreen.tap(5, 580);
assert.equal(await mobileCard.count(), 0);
await mobile.setViewportSize({ width: 390, height: 320 });
const longLink = mobile.getByRole("link", { name: "Long reviewed source", exact: true });
await longLink.evaluate((node) => node.scrollIntoView({ block: "center" }));
await longLink.tap();
await mobileCard.waitFor();
const longBounds = await mobileCard.boundingBox();
const anchorBounds = await longLink.boundingBox();
assert.ok(longBounds.y + longBounds.height <= anchorBounds.y || longBounds.y >= anchorBounds.y + anchorBounds.height,
`Tooltip must leave its only navigation link reachable: ${JSON.stringify({longBounds,anchorBounds})}`);
assert.ok(await mobileCard.evaluate((node) => node.scrollHeight > node.clientHeight), "Long context scrolls within the available side");
await longLink.tap();
assert.deepEqual(await mobile.evaluate(() => window.__sourceLinks), [href, longHref]);
assert.deepEqual(errors, []);
console.log("Source-preview browser smoke passed.");
} finally {
await browser?.close();
rmSync(project, { recursive: true, force: true });
}
SHA-256: 72d6741efb67ba08ba50a8653d5eacffd9aee9092d6a2b811b393d3ce6e7b37c