evOo-local/packages/testkit/src/playwright-pr-screenshot-comment.test.ts
cynarAI 6c7eed7580 Initial clean evOo-local from f00577b
Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline
(Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/*
packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
2026-09-20 17:15:16 +02:00

316 lines
10 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
buildPlaywrightPrScreenshotComment,
PLAYWRIGHT_PR_SCREENSHOT_COMMENT_MARKER,
type PlaywrightPrCommentScreenshot,
screenshotMatchesSpecStem,
selectPlaywrightPrFeatureFrames,
specStemsFromChangedPaths,
} from "./playwright-pr-screenshot-comment.js";
describe("specStemsFromChangedPaths", () => {
it("extracts Playwright spec stems from changed paths", () => {
expect(
specStemsFromChangedPaths([
"apps/web/e2e/tool-activity-shell.spec.ts",
"apps/web/src/components/ToolActivity.tsx",
"apps/web/e2e/tool-activity-disclosure.spec.ts",
"README.md",
]),
).toEqual(["tool-activity-disclosure", "tool-activity-shell"]);
});
});
describe("screenshotMatchesSpecStem", () => {
it("matches Playwright output directories that start with the spec stem", () => {
expect(
screenshotMatchesSpecStem(
"tool-activity-shell-produc-bb211-n-in-every-disclosure-state-chromium/shell.png",
"tool-activity-shell",
),
).toBe(true);
expect(
screenshotMatchesSpecStem(
"tool-activity-disclosure-t-f2d47-s-collapsed-until-disclosed-chromium/complete.png",
"tool-activity-disclosure",
),
).toBe(true);
expect(
screenshotMatchesSpecStem(
"onboarding-conversation-fo-bf02c-rves-a-completed-connection-chromium/01.png",
"tool-activity-shell",
),
).toBe(false);
});
});
describe("selectPlaywrightPrFeatureFrames", () => {
it("prefers NEW checkpoints over everything else", () => {
const selection = selectPlaywrightPrFeatureFrames({
changedPaths: [],
screenshots: [
screenshot({
comparison: "changed",
fileName: "images/001-onboarding.png",
source: "onboarding-x/01.png",
title: "onboarding drift",
}),
screenshot({
comparison: "new",
fileName: "images/002-shell-live.png",
source: "tool-activity-shell-x/shell-live.png",
title: "shell live",
}),
],
});
expect(selection).toEqual({
frames: [
expect.objectContaining({
kind: "new",
title: "shell live",
}),
],
omittedCount: 0,
});
});
it("includes CHANGED checkpoints only when their spec is in the PR diff", () => {
const selection = selectPlaywrightPrFeatureFrames({
changedPaths: ["apps/web/e2e/tool-activity-disclosure.spec.ts"],
screenshots: [
screenshot({
comparison: "changed",
fileName: "images/001-complete.png",
source: "tool-activity-disclosure-t-f2d47-chromium/complete.png",
title: "complete collapsed",
}),
screenshot({
comparison: "changed",
fileName: "images/002-onboarding.png",
source: "onboarding-conversation-fo-bf02c-chromium/01.png",
title: "onboarding",
}),
screenshot({
comparison: "unchanged",
fileName: "images/003-other.png",
source: "tool-activity-disclosure-t-f2d47-chromium/other.png",
title: "other",
}),
],
});
expect(selection.frames).toEqual([
expect.objectContaining({
kind: "changed",
title: "complete collapsed",
}),
]);
expect(selection.omittedCount).toBe(0);
});
it("returns an empty selection for suite-vs-main drift only", () => {
const selection = selectPlaywrightPrFeatureFrames({
changedPaths: ["apps/web/src/components/ToolActivity.tsx"],
screenshots: [
screenshot({
comparison: "changed",
fileName: "images/001-onboarding.png",
source: "onboarding-x/01.png",
title: "onboarding",
}),
screenshot({
comparison: "changed",
fileName: "images/002-settings.png",
source: "settings-x/01.png",
title: "settings",
}),
],
});
expect(selection).toEqual({ frames: [], omittedCount: 0 });
});
it("caps the named list and reports how many were omitted", () => {
const screenshots = Array.from({ length: 10 }, (_, index) =>
screenshot({
comparison: "new",
fileName: `images/${String(index + 1).padStart(3, "0")}-frame.png`,
source: `tool-activity-shell-x/frame-${index}.png`,
title: `frame ${index + 1}`,
}),
);
const selection = selectPlaywrightPrFeatureFrames({
changedPaths: ["apps/web/e2e/tool-activity-shell.spec.ts"],
limit: 8,
screenshots,
});
expect(selection.frames).toHaveLength(8);
expect(selection.frames.map((frame) => frame.title)).toEqual([
"frame 1",
"frame 2",
"frame 3",
"frame 4",
"frame 5",
"frame 6",
"frame 7",
"frame 8",
]);
expect(selection.omittedCount).toBe(2);
});
});
describe("buildPlaywrightPrScreenshotComment", () => {
const urls = {
dashboardUrl: "https://example.com/playwright/index.html",
galleryUrl: "https://example.com/playwright/prs/452/index.html",
runUrl: "https://github.com/example/rakazo/actions/runs/1",
screenshotsUrl: "https://example.com/playwright/runs/1-1/screenshots/index.html",
sha: "abcdef1234567890",
};
it("names NEW frames with direct PNG links and keeps gallery links secondary", () => {
const body = buildPlaywrightPrScreenshotComment({
...urls,
changedPaths: ["apps/web/e2e/tool-activity-shell.spec.ts"],
screenshots: [
screenshot({
comparison: "new",
fileName: "images/010-shell-live-collapsed-desktop-1440x900.png",
source: "tool-activity-shell-x/shell-live-collapsed-desktop-1440x900.png",
title: "shell live collapsed desktop 1440x900",
}),
],
});
expect(body).toContain(PLAYWRIGHT_PR_SCREENSHOT_COMMENT_MARKER);
expect(body).toContain("### Playwright screenshots");
expect(body).toContain("Feature frames for this PR:");
expect(body).toContain(
"- [shell live collapsed desktop 1440x900](https://example.com/playwright/runs/1-1/screenshots/images/010-shell-live-collapsed-desktop-1440x900.png) (new)",
);
expect(body).toContain(
"[Open screenshot gallery](https://example.com/playwright/prs/452/index.html) · [Dashboard](https://example.com/playwright/index.html) · [CI run](https://github.com/example/rakazo/actions/runs/1)",
);
expect(body).toContain("Updated for commit `abcdef1`.");
expect(body).not.toContain("![");
expect(body).not.toContain("—");
});
it("includes spec-touched CHANGED frames after NEW frames", () => {
const body = buildPlaywrightPrScreenshotComment({
...urls,
changedPaths: ["apps/web/e2e/tool-activity-disclosure.spec.ts"],
screenshots: [
screenshot({
comparison: "changed",
fileName: "images/020-complete-collapsed-desktop-1440x900.png",
source:
"tool-activity-disclosure-t-f2d47-chromium/complete-collapsed-desktop-1440x900.png",
title: "complete collapsed desktop 1440x900",
}),
screenshot({
comparison: "new",
fileName: "images/010-shell-live.png",
source: "tool-activity-shell-x/shell-live.png",
title: "shell live",
}),
screenshot({
comparison: "changed",
fileName: "images/030-onboarding.png",
source: "onboarding-x/01.png",
title: "onboarding",
}),
],
});
expect(body).toContain(
"- [shell live](https://example.com/playwright/runs/1-1/screenshots/images/010-shell-live.png) (new)",
);
expect(body).toContain(
"- [complete collapsed desktop 1440x900](https://example.com/playwright/runs/1-1/screenshots/images/020-complete-collapsed-desktop-1440x900.png) (changed)",
);
expect(body).not.toContain("onboarding");
});
it("says so in one line when only suite drift remains", () => {
const body = buildPlaywrightPrScreenshotComment({
...urls,
changedPaths: ["apps/web/src/components/ToolActivity.tsx"],
screenshots: [
screenshot({
comparison: "changed",
fileName: "images/001-onboarding.png",
source: "onboarding-x/01.png",
title: "onboarding",
}),
],
});
expect(body).toContain("No new feature frames; gallery is suite-vs-main drift.");
expect(body).not.toContain("Feature frames for this PR:");
expect(body).not.toContain("onboarding");
expect(body).toContain("[Open screenshot gallery]");
});
it("caps named frames and points to the gallery for the rest", () => {
const screenshots = Array.from({ length: 10 }, (_, index) =>
screenshot({
comparison: "new",
fileName: `images/${String(index + 1).padStart(3, "0")}-frame.png`,
source: `tool-activity-shell-x/frame-${index}.png`,
title: `frame ${index + 1}`,
}),
);
const body = buildPlaywrightPrScreenshotComment({
...urls,
changedPaths: ["apps/web/e2e/tool-activity-shell.spec.ts"],
screenshots,
});
expect(body).toContain(
"- [frame 8](https://example.com/playwright/runs/1-1/screenshots/images/008-frame.png) (new)",
);
expect(body).not.toContain("frame 9");
expect(body).toContain("- +2 more in the gallery");
});
it("neutralizes Markdown and HTML syntax in contributor-controlled titles", () => {
const body = buildPlaywrightPrScreenshotComment({
...urls,
changedPaths: [],
screenshots: [
screenshot({
comparison: "new",
fileName: "images/010-crafted.png",
source: "tool-activity-shell-x/crafted.png",
title: "evil]\n**bold** `code` _em_ #heading [link] <details>&",
}),
],
});
expect(body).toContain(
"- [evil\\] \\*\\*bold\\*\\* \\`code\\` \\_em\\_ \\#heading \\[link\\] &lt;details&gt;&amp;](https://example.com/playwright/runs/1-1/screenshots/images/010-crafted.png) (new)",
);
expect(body).not.toContain("\n**bold**");
expect(body).not.toContain("`code`");
expect(body).not.toContain("<details>");
});
});
function screenshot(
overrides: Partial<PlaywrightPrCommentScreenshot> = {},
): PlaywrightPrCommentScreenshot {
return {
captureType: "checkpoint",
comparison: "new",
fileName: "images/001-checkpoint.png",
source: "golden-chromium/checkpoint.png",
title: "checkpoint",
...overrides,
};
}