Luigit
repositories / pi-ext

pi-ext

bugabingas pi extensions

owned by admin

extensions/strata/__e2e__/review.spec.ts

Raw
import { expect, test } from "@playwright/test";
import { startReviewServer } from "../server.js";
import type { AskRequest, Feedback, Review, ReviewServer } from "../types.js";

function makeReview(id = "snapshot-e2e"): Review {
	return {
		snapshot: {
			id,
			repoRoot: "/safe/project",
			source: { kind: "working" },
			base: "base-e2e",
			head: "head-e2e",
			hunks: [
				{
					id: "hunk-parser",
					path: "src/parser.ts",
					header: "@@ -8,2 +8,2 @@ parse",
					lines: [
						{ kind: "delete", text: "return unsafe(value);", oldLine: 8 },
						{ kind: "add", text: "return parse(value);", newLine: 8 },
						{ kind: "context", text: "}", oldLine: 9, newLine: 9 },
					],
				},
				{
					id: "hunk-view",
					path: "src/view.ts",
					oldPath: "src/old-view.ts",
					header: "@@ -20 +20 @@ render",
					lines: [
						{ kind: "delete", text: "title.textContent = input;", oldLine: 20 },
						{
							kind: "add",
							text: "title.textContent = '<img src=x onerror=alert(1)>';",
							newLine: 20,
						},
					],
				},
			],
			skipped: [{ path: "art/logo.bin", reason: "binary" }],
		},
		plan: {
			summary: "Parse safely, then render text.",
			cohorts: [
				{
					title: "Runtime",
					layers: [
						{
							id: "parser",
							title: "Parse input",
							summary: "Validate input before use.",
							hunks: [
								{
									id: "hunk-parser",
									summary: "Replaces unsafe coercion with parsing.",
								},
							],
							flow: ["Raw value", "Parser", "Typed value"],
						},
						{
							id: "view",
							title: "Render output",
							summary: "Render the parsed value.",
							hunks: [
								{
									id: "hunk-view",
									summary: "Keeps model-like markup as inert text.",
								},
							],
						},
					],
				},
			],
		},
		draft: { reviewed: [], findings: [], notes: "" },
	};
}

let server: ReviewServer | undefined;

test.afterEach(() => {
	server?.close();
	server = undefined;
});

test("reviews every pane, persists drafts, and keeps content inert", async ({
	page,
}, testInfo) => {
	const pageErrors: string[] = [];
	page.on("pageerror", (error) => pageErrors.push(error.message));
	const saved = [];
	server = await startReviewServer({
		review: makeReview(),
		isCurrent: async () => true,
		refresh: async () => makeReview("snapshot-refreshed"),
		ask: async (request) =>
			`<strong>Plain answer to ${request.question}</strong>`,
		save: (draft) => saved.push(structuredClone(draft)),
		submit: async () => {},
	});
	await page.goto(server.url);

	await expect(page.locator("#source-label")).toContainText("snapshot-e2e");
	await expect(page.locator("#view-summary")).toHaveText(
		"Parse safely, then render text.",
	);
	await expect(page.locator(".hunk")).toHaveCount(2);
	await expect(page.locator("#omitted-list")).toContainText(
		"art/logo.bin: binary",
	);
	await expect(page.locator("img")).toHaveCount(0);
	await expect(
		page.getByText("<img src=x onerror=alert(1)>", { exact: false }),
	).toBeVisible();

	await page.getByRole("button", { name: "Parse input" }).click();
	await expect(page.locator("#diff-heading")).toHaveText("Parse input");
	await expect(page.locator("#view-summary")).toHaveText(
		"Validate input before use.",
	);
	await expect(page.locator(".hunk")).toHaveCount(1);
	await expect(page.locator("#explanation")).toHaveText(
		"Replaces unsafe coercion with parsing.",
	);
	await expect(page.locator(".flow-step")).toHaveText([
		"Raw value",
		"Parser",
		"Typed value",
	]);
	await expect(
		page.getByRole("heading", { name: "Dependency order" }),
	).toBeVisible();
	await expect(page.locator(".flow-description")).toContainText(
		"Suggested reading sequence, not runtime execution.",
	);

	await page.keyboard.press("]");
	await page.keyboard.press("l");
	await expect(page.locator("#diff-heading")).toHaveText("Render output");
	await page.keyboard.press("[");
	await page.keyboard.press("l");
	await expect(page.locator("#diff-heading")).toHaveText("Parse input");

	await page.getByRole("button", { name: "Comment on new line 8" }).click();
	await page.locator("#comment-severity").selectOption("major");
	await page.locator("#comment-text").fill("Check parser failure behavior.");
	await page.getByRole("button", { name: "Add comment" }).click();
	await expect(page.locator(".finding")).toContainText(
		"Check parser failure behavior.",
	);

	await page
		.getByRole("button", { name: "Remove comment on new line 8" })
		.click();
	await expect(page.locator(".finding")).toHaveCount(0);
	await page.getByRole("button", { name: "Comment on old line 8" }).click();
	await page.locator("#comment-text").fill("Check parser failure behavior.");
	await page.getByRole("button", { name: "Add comment", exact: true }).click();
	await expect(page.locator(".finding")).toContainText("old:8");
	await page.getByRole("button", { name: "Mark reviewed" }).click();
	await expect(page.locator("#progress-label")).toHaveText("1 / 2 reviewed");
	await page.locator("#notes").fill("Run the malformed-input test.");
	await expect(page.locator("#status")).toHaveText("Draft saved");

	await page.getByRole("tab", { name: "Ask" }).click();
	await page.locator("#question").fill("What changed?");
	await page.getByRole("button", { name: "Send", exact: true }).click();
	await expect(page.locator("#chat")).toContainText(
		"<strong>Plain answer to What changed?</strong>",
	);
	await expect(page.locator("#chat .chat-question")).toHaveText(
		"What changed?",
	);

	await page.reload();
	await page.getByRole("tab", { name: "Feedback" }).click();
	await expect(page.locator("#notes")).toHaveValue(
		"Run the malformed-input test.",
	);
	await expect(page.locator(".finding")).toContainText(
		"Check parser failure behavior.",
	);
	await expect(page.locator("#progress-label")).toHaveText("1 / 2 reviewed");
	await expect.poll(() => saved.length).toBeGreaterThan(0);
	await page.screenshot({
		path: testInfo.outputPath("strata-workspace.png"),
		fullPage: true,
	});
	expect(pageErrors).toEqual([]);
});

test("confirms refresh, reports errors and stale feedback, then submits", async ({
	page,
}, testInfo) => {
	let current = true;
	const submitted: Feedback[] = [];
	server = await startReviewServer({
		review: makeReview(),
		isCurrent: async () => current,
		refresh: async () => makeReview("snapshot-refreshed"),
		ask: async (request) => {
			if (request.question === "fail") throw new Error("model unavailable");
			return "ok";
		},
		save: () => {},
		submit: async (feedback) => {
			submitted.push(feedback);
		},
	});
	await page.goto(server.url);
	await page.getByRole("button", { name: "Parse input" }).click();
	await page.getByRole("tab", { name: "Ask" }).click();
	await page.locator("#question").fill("fail");
	await page.getByRole("button", { name: "Send", exact: true }).click();
	await expect(page.locator("#status")).toHaveText("model unavailable");
	await expect(page.locator("#question")).toHaveValue("fail");
	current = false;

	await page.getByRole("tab", { name: "Feedback" }).click();
	await page.locator("#notes").fill("Do not lose this draft.");
	await expect(page.locator("#status")).toHaveText("Draft saved");
	page.once("dialog", (dialog) => dialog.accept());
	await page.getByRole("button", { name: "Send feedback to Pi" }).click();
	await expect(page.locator("#status")).toContainText("snapshot is stale");
	await expect(page.locator("#notes")).toHaveValue("Do not lose this draft.");
	current = true;
	page.once("dialog", (dialog) => dialog.accept());
	await page.getByRole("button", { name: "Send feedback to Pi" }).click();
	await expect(page.locator("#status")).toHaveText("Feedback sent to Pi");
	await expect(page.locator("#notes")).toHaveValue("");
	expect(submitted).toHaveLength(1);
	expect(submitted[0].snapshotId).toBe("snapshot-e2e");

	await page.locator("#notes").fill("Discard on explicit refresh.");
	await expect(page.locator("#status")).toHaveText("Draft saved");
	page.once("dialog", (dialog) => dialog.accept());
	await page.getByRole("button", { name: "Refresh" }).click();
	await expect(page.locator("#source-label")).toContainText("snapshot-ref");
	await expect(page.locator("#notes")).toHaveValue("");

	await page.setViewportSize({ width: 390, height: 844 });
	await expect(page.locator(".layer-nav")).toBeHidden();
	await expect(page.locator(".detail-region")).toBeHidden();
	await page.getByRole("button", { name: "Show review layers" }).click();
	await expect(page.locator("#omitted-list")).toContainText(
		"art/logo.bin: binary",
	);
	await page.getByRole("button", { name: "Hide review layers" }).click();
	await page.getByRole("button", { name: "Show review drawer" }).click();
	await page.getByRole("tab", { name: "Ask" }).click();
	await expect(page.locator("#question")).toBeInViewport();
	expect(
		await page.evaluate(
			() => document.documentElement.scrollWidth <= window.innerWidth,
		),
	).toBe(true);
	await page.screenshot({
		path: testInfo.outputPath("strata-phone.png"),
		fullPage: true,
	});
});

test("collapses both side panels independently and provides keyboard tabs", async ({
	page,
}) => {
	server = await startReviewServer({
		review: makeReview(),
		isCurrent: async () => true,
		refresh: async () => makeReview("snapshot-refreshed"),
		ask: async () => "ok",
		save: () => {},
		submit: async () => {},
	});
	await page.goto(server.url);
	const initialWidth = await page
		.locator(".diff-region")
		.evaluate((element) => element.getBoundingClientRect().width);

	await page.getByRole("button", { name: "Hide review layers" }).click();
	await expect(page.locator(".layer-nav")).toBeHidden();
	const withoutLayers = await page
		.locator(".diff-region")
		.evaluate((element) => element.getBoundingClientRect().width);
	expect(withoutLayers).toBeGreaterThan(initialWidth);

	await page.getByRole("button", { name: "Hide review drawer" }).click();
	await expect(page.locator(".detail-region")).toBeHidden();
	const diffOnly = await page
		.locator(".diff-region")
		.evaluate((element) => element.getBoundingClientRect().width);
	expect(diffOnly).toBeGreaterThan(withoutLayers);

	await page.getByRole("button", { name: "Show review drawer" }).click();
	const detailsTab = page.getByRole("tab", { name: "Details" });
	await expect(detailsTab).toHaveAttribute("aria-selected", "true");
	await detailsTab.press("ArrowRight");
	await expect(page.getByRole("tab", { name: "Ask" })).toHaveAttribute(
		"aria-selected",
		"true",
	);
	await expect(page.locator("#details-panel")).toBeHidden();
	await expect(page.locator("#ask-panel")).toBeVisible();
	for (const colorScheme of ["light", "dark"] as const) {
		await page.emulateMedia({ colorScheme });
		await page.getByRole("tab", { name: "Ask" }).hover();
		const foreground = await page
			.locator("body")
			.evaluate((element) => getComputedStyle(element).color);
		await expect(page.getByRole("tab", { name: "Ask" })).toHaveCSS(
			"color",
			foreground,
		);
	}
	await page.getByRole("tab", { name: "Ask" }).press("End");
	await expect(page.getByRole("tab", { name: "Feedback" })).toHaveAttribute(
		"aria-selected",
		"true",
	);
	await page.getByRole("button", { name: "Show review layers" }).click();
	await expect(page.locator(".layer-nav")).toBeVisible();
});

test("keeps model-generated layer IDs distinct from review-wide draft keys", async ({
	page,
}) => {
	const review = makeReview();
	review.plan.cohorts[0].layers[0].id = "__all_changes__";
	review.plan.cohorts[0].layers[1].id = "__proto__";
	server = await startReviewServer({
		review,
		isCurrent: async () => true,
		refresh: async () => review,
		ask: async () => "Answer",
		save: () => {},
		submit: async () => {},
	});
	await page.goto(server.url);
	await page.getByRole("tab", { name: "Ask" }).click();
	for (const name of ["All changes", "Parse input", "Render output"]) {
		await page.getByRole("button", { name }).click();
		await page.locator("#question").fill(`Draft for ${name}`);
	}
	await page.reload();
	await page.getByRole("tab", { name: "Ask" }).click();
	for (const name of ["All changes", "Parse input", "Render output"]) {
		await page.getByRole("button", { name }).click();
		await expect(page.locator("#question")).toHaveValue(`Draft for ${name}`);
	}
});

test("keeps scoped threads, hunk focus, drafts, and async answers distinct", async ({
	page,
}) => {
	const requests: AskRequest[] = [];
	let releaseDelayed: (() => void) | undefined;
	server = await startReviewServer({
		review: makeReview(),
		isCurrent: async () => true,
		refresh: async () => makeReview("snapshot-refreshed"),
		ask: async (request) => {
			requests.push(structuredClone(request));
			if (request.question === "fail") throw new Error("model unavailable");
			if (request.question === "Delayed once") {
				await new Promise<void>((resolve) => {
					releaseDelayed = resolve;
				});
			}
			if (request.question === "Long answer") {
				return Array.from(
					{ length: 160 },
					(_, index) => `Answer line ${index}`,
				).join("\n");
			}
			return `Answer for ${request.question}`;
		},
		save: () => {},
		submit: async () => {},
	});
	await page.goto(server.url);
	await page.getByRole("tab", { name: "Ask" }).click();
	await expect(page.locator("#ask-scope")).toHaveText("All changes");
	await page.locator("#question").fill("All changes draft");
	await page.getByRole("button", { name: "Parse input" }).click();
	await page.locator("#question").fill("Parser draft");
	await page.getByRole("button", { name: "All changes" }).click();
	await page.locator("#hunk-hunk-parser").scrollIntoViewIfNeeded();
	await page.locator("#hunk-hunk-parser").click();
	await expect(page.locator("#ask-focus")).toContainText("src/parser.ts");
	await expect(page.locator("#question")).toHaveValue("All changes draft");
	await page.locator("#question").press("End");
	await page.locator("#question").press("Enter");
	await expect(page.locator("#question")).toHaveValue("All changes draft\n");
	expect(requests).toHaveLength(0);
	await page.locator("#question").press("Shift+Enter");
	await expect.poll(() => requests.length).toBe(1);
	expect(requests[0]).toMatchObject({
		layerId: null,
		hunkId: "hunk-parser",
		question: "All changes draft",
		history: [],
	});
	await expect(page.locator("#chat")).toContainText(
		"Answer for All changes draft",
	);
	await expect(page.locator("#question")).toBeFocused();

	await page.getByRole("button", { name: "Parse input" }).click();
	await expect(page.locator("#question")).toHaveValue("Parser draft");
	await page.locator("#question").fill("Parser question");
	await page.getByRole("button", { name: "Send", exact: true }).click();
	await expect.poll(() => requests.length).toBe(2);
	expect(requests[1]).toMatchObject({
		layerId: "parser",
		hunkId: "hunk-parser",
		history: [],
	});

	await page.getByRole("button", { name: "All changes" }).click();
	await page.locator("#hunk-hunk-view").click();
	await expect(page.locator("#ask-scope")).toHaveText("All changes");
	await expect(page.locator("#ask-focus")).toContainText("src/view.ts");
	await page.locator("#question").fill("Focused view question");
	await page.locator("#question").press("Shift+Enter");
	await expect.poll(() => requests.length).toBe(3);
	expect(requests[2]).toMatchObject({
		layerId: null,
		hunkId: "hunk-view",
		history: [
			{
				question: "All changes draft",
				hunkId: "hunk-parser",
			},
		],
	});

	await page.getByRole("button", { name: "Parse input" }).click();
	await page.locator("#question").fill("Delayed once");
	await page.locator("#question").press("Shift+Enter");
	await expect.poll(() => requests.length).toBe(4);
	await page.evaluate(() => {
		(document.querySelector("#ask-form") as HTMLFormElement).requestSubmit();
	});
	await page.getByRole("button", { name: "Render output" }).click();
	await expect(page.locator("#ask-scope")).toHaveText("Render output");
	expect(
		requests.filter((request) => request.question === "Delayed once"),
	).toHaveLength(1);
	releaseDelayed?.();
	await expect(page.locator("#status")).toHaveText("Answer received");
	await page.getByRole("button", { name: "Parse input" }).click();
	await expect(page.locator("#chat")).toContainText("Answer for Delayed once");

	await page.locator("#question").fill("fail");
	await page.locator("#question").press("Shift+Enter");
	await expect(page.locator("#status")).toHaveText("model unavailable");
	await expect(page.locator("#question")).toHaveValue("fail");
	await expect(page.locator(".chat-error")).toHaveText("model unavailable");

	await page.locator("#question").fill("Long answer");
	await page.locator("#question").press("Shift+Enter");
	await expect(page.locator("#chat")).toContainText("Answer line 159");
	expect(
		await page
			.locator("#chat")
			.evaluate((element) => element.scrollHeight > element.clientHeight),
	).toBe(true);
	await page.locator("#chat").evaluate((element) => {
		element.scrollTop = 0;
	});
	await page.locator("#hunk-hunk-parser").click();
	expect(
		await page.locator("#chat").evaluate((element) => element.scrollTop),
	).toBe(0);
	await page.getByRole("button", { name: "Render output" }).click();
	await page.locator("#question").fill("Unsent view draft");
	await page.reload();
	await page.getByRole("tab", { name: "Ask" }).click();
	await page.getByRole("button", { name: "Render output" }).click();
	await expect(page.locator("#question")).toHaveValue("Unsent view draft");
	await page.getByRole("button", { name: "Parse input" }).click();
	await expect(page.locator("#chat")).toContainText("Answer for Delayed once");
	await expect(page.locator("#chat")).toContainText("Answer line 159");

	await page.setViewportSize({ width: 390, height: 844 });
	await page.getByRole("button", { name: "Show review drawer" }).click();
	await page.getByRole("tab", { name: "Ask" }).click();
	await expect(page.locator(".ask-composer")).toBeInViewport();
});

test("renders model layer summaries and hunk references in model order", async ({
	page,
}) => {
	const review = makeReview();
	review.plan.cohorts[0].layers = [
		{
			id: "ordered",
			title: "Ordered review",
			summary: "Read rendering before parsing.",
			hunks: [
				{ id: "hunk-view", summary: "Rendering follows parsed input." },
				{ id: "hunk-parser", summary: "Parsing establishes the input." },
			],
		},
	];
	server = await startReviewServer({
		review,
		isCurrent: async () => true,
		refresh: async () => makeReview("snapshot-refreshed"),
		ask: async () => "ok",
		save: () => {},
		submit: async () => {},
	});
	await page.goto(server.url);
	await page.getByRole("button", { name: "Ordered review" }).click();
	await expect(page.locator("#view-summary")).toHaveText(
		"Read rendering before parsing.",
	);
	await expect(page.locator(".file-name")).toHaveText([
		"src/old-view.ts → src/view.ts",
		"src/parser.ts",
	]);
});

test("rejects stale tabs without overwriting their notes", async ({ page }) => {
	const saved: Review["draft"][] = [];
	server = await startReviewServer({
		review: makeReview(),
		isCurrent: async () => true,
		refresh: async () => makeReview("snapshot-refreshed"),
		ask: async () => "ok",
		save: (draft) => saved.push(structuredClone(draft)),
		submit: async () => {},
	});
	const secondTab = await page.context().newPage();
	await page.goto(server.url);
	await secondTab.goto(server.url);
	page.once("dialog", (dialog) => dialog.accept());
	await page.getByRole("button", { name: "Refresh" }).click();
	await expect(page.locator("#source-label")).toContainText("snapshot-ref");

	await secondTab.getByRole("tab", { name: "Feedback" }).click();
	await secondTab.locator("#notes").fill("Keep this stale-tab note.");
	await expect(secondTab.locator("#status")).toContainText(
		"Review changed in another tab",
	);
	await expect(secondTab.locator("#notes")).toHaveValue(
		"Keep this stale-tab note.",
	);
	const draftKeys = await secondTab.evaluate(() => Object.keys(sessionStorage));
	expect(draftKeys.some((key) => key.endsWith(":snapshot-e2e"))).toBe(true);
	expect(draftKeys.some((key) => key.endsWith(":snapshot-refreshed"))).toBe(
		false,
	);
	await secondTab.waitForTimeout(400);
	expect(saved).toHaveLength(0);
	await secondTab.close();
});

test("keeps navigation available while asking and recovers initial busy pages", async ({
	page,
}) => {
	let answer!: (value: string) => void;
	let started!: () => void;
	const didStart = new Promise<void>((resolve) => {
		started = resolve;
	});
	server = await startReviewServer({
		review: makeReview(),
		isCurrent: async () => true,
		refresh: async () => makeReview("snapshot-refreshed"),
		ask: async () =>
			new Promise<string>((resolve) => {
				answer = resolve;
				started();
			}),
		save: () => {},
		submit: async () => {},
	});
	await page.goto(server.url);
	await page.getByRole("button", { name: "Parse input" }).click();
	await page.getByRole("tab", { name: "Ask" }).click();
	await page.locator("#question").fill("Wait");
	await page.getByRole("button", { name: "Send", exact: true }).click();
	await didStart;

	const waitingTab = await page.context().newPage();
	await waitingTab.goto(server.url);
	await expect(
		waitingTab.getByRole("button", { name: "Refresh" }),
	).toBeDisabled();
	await waitingTab.getByRole("tab", { name: "Ask" }).click();
	await expect(
		waitingTab.getByRole("button", { name: "Send", exact: true }),
	).toBeDisabled();
	await expect(
		waitingTab.getByRole("button", { name: "Mark reviewed" }).first(),
	).toBeDisabled();
	await expect(waitingTab.getByRole("button", { name: "Close" })).toBeEnabled();
	await waitingTab.getByRole("button", { name: "Parse input" }).click();
	await expect(waitingTab.locator("#diff-heading")).toHaveText("Parse input");

	answer("done");
	await expect(page.locator("#chat")).toContainText("done");
	await expect(
		waitingTab.getByRole("button", { name: "Refresh" }),
	).toBeEnabled();
	await waitingTab.close();
});

test("retains a debounced draft across immediate reload and cancels in-flight asks", async ({
	page,
}) => {
	const saved: Review["draft"][] = [];
	let askAborted = false;
	let askStarted!: () => void;
	const didStart = new Promise<void>((resolve) => {
		askStarted = resolve;
	});
	server = await startReviewServer({
		review: makeReview(),
		isCurrent: async () => true,
		refresh: async () => makeReview("snapshot-refreshed"),
		ask: async (_request, signal) =>
			new Promise<string>((_resolve, reject) => {
				askStarted();
				signal.addEventListener("abort", () => {
					askAborted = true;
					reject(new Error("aborted"));
				});
			}),
		save: (draft) => saved.push(structuredClone(draft)),
		submit: async () => {},
	});
	await page.goto(server.url);
	await page.getByRole("tab", { name: "Feedback" }).click();
	await page.locator("#notes").pressSequentially("Debounced note");
	await page.waitForTimeout(100);
	expect(saved).toHaveLength(0);
	await page.reload();
	await expect(page.locator("#notes")).toHaveValue("Debounced note");
	await expect.poll(() => saved.length).toBe(1);

	await page.getByRole("button", { name: "Parse input" }).click();
	await page.getByRole("tab", { name: "Ask" }).click();
	await page.locator("#question").fill("Cancel me");
	await page.getByRole("button", { name: "Send", exact: true }).click();
	await didStart;
	await page.reload();
	await expect.poll(() => askAborted).toBe(true);
	await expect(page.locator("#status")).toHaveText("Review ready");
});

test("keeps the dark context rail visible while scrolling a long diff", async ({
	page,
}, testInfo) => {
	const review = makeReview();
	review.snapshot.hunks[0].lines.push(
		...Array.from({ length: 100 }, (_, i) => ({
			kind: "context" as const,
			text: `const item${i} = ${i};`,
			oldLine: i + 10,
			newLine: i + 10,
		})),
	);
	server = await startReviewServer({
		review,
		isCurrent: async () => true,
		refresh: async () => review,
		ask: async () => "answer",
		save: () => {},
		submit: async () => {},
	});
	await page.emulateMedia({ colorScheme: "dark" });
	await page.goto(server.url);
	await expect(page.locator("#status")).toHaveText("Review ready");
	await page
		.locator('.hunk[data-hunk-id="hunk-view"]')
		.scrollIntoViewIfNeeded();
	await expect(page.locator("#current-path")).toHaveText("src/view.ts");
	await expect(page.locator("#explanation")).toBeInViewport();
	expect(
		await page.locator(".diff-region").evaluate((el) => el.scrollTop),
	).toBeGreaterThan(100);
	expect(await page.evaluate(() => window.scrollY)).toBe(0);
	await page.screenshot({
		path: testInfo.outputPath("strata-dark-scroll.png"),
	});
});