Skip to content

Commit aac072e

Browse files
committed
fix(webview): address code review feedback for Playwright CT harness
1 parent c4c2c6f commit aac072e

7 files changed

Lines changed: 74 additions & 18 deletions

File tree

.github/workflows/visual-regression.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ on:
1313
merge_group:
1414
types: [checks_requested]
1515

16+
concurrency:
17+
group: ${{ github.workflow }}-${{ github.ref }}
18+
cancel-in-progress: true
19+
1620
permissions:
1721
contents: read
1822

webview-ui/docker-compose.visual.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ services:
55
user: "${UID:-1000}:${GID:-1000}"
66
working_dir: /work
77
environment:
8-
COREPACK_HOME: /tmp/corepack
8+
COREPACK_HOME: /work/node_modules/.cache/corepack
99
HOME: /tmp/playwright
1010
volumes:
1111
- ..:/work

webview-ui/playwright-ct.config.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import path from "path"
22
import { fileURLToPath } from "url"
33

4-
import { defineConfig, type ReporterDescription } from "@playwright/experimental-ct-react"
4+
import { defineConfig } from "@playwright/experimental-ct-react"
5+
import type { ReporterDescription } from "@playwright/test"
56
import react from "@vitejs/plugin-react"
67
import tailwindcss from "@tailwindcss/vite"
78

@@ -15,18 +16,21 @@ const monocartReporter: ReporterDescription = [
1516
coverage: {
1617
outputDir: path.resolve(dirname, "coverage-ct"),
1718
reports: ["lcovonly", "v8"],
18-
// Entry URLs are the served asset chunks (`/assets/*.js`); filter kept
19-
// permissive so V8 coverage can then source-map back to originals.
20-
entryFilter: (entry: { url: string }) => entry.url.includes("/assets/"),
21-
sourceFilter: (sourcePath: string) => sourcePath.includes("src/"),
19+
// Entry URLs from Vite CT dev server or built bundle.
20+
entryFilter: (entry: { url: string }) => entry.url.includes("/src/") || entry.url.includes("/assets/"),
21+
sourceFilter: (sourcePath: string) =>
22+
sourcePath.includes("src/") &&
23+
!sourcePath.includes(".visual.") &&
24+
!sourcePath.includes(".spec.") &&
25+
!sourcePath.includes(".test."),
2226
},
2327
},
2428
]
2529

2630
export default defineConfig({
2731
testDir: "./src",
2832
testMatch: "**/*.visual.tsx",
29-
outputDir: process.env.CI ? path.resolve(dirname, "test-results") : "/tmp/webview-ui-playwright-test-results",
33+
outputDir: path.resolve(dirname, "test-results"),
3034
snapshotPathTemplate: "{testDir}/{testFileDir}/__screenshots__/{arg}{ext}",
3135
fullyParallel: true,
3236
reporter: process.env.CI
@@ -36,7 +40,11 @@ export default defineConfig({
3640
["list"],
3741
monocartReporter,
3842
]
39-
: [["html", { open: "never", outputFolder: "/tmp/webview-ui-playwright-report" }], ["list"], monocartReporter],
43+
: [
44+
["html", { open: "never", outputFolder: path.resolve(dirname, "playwright-report") }],
45+
["list"],
46+
monocartReporter,
47+
],
4048
use: {
4149
ctTemplateDir: "./playwright",
4250
ctViteConfig: {
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
declare module "monocart-reporter" {
2+
export function addCoverageReport(coverageData: any[], testInfo: any): Promise<void>
3+
}

webview-ui/playwright/run-docker.mjs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { spawnSync } from "node:child_process"
1+
import { spawn, spawnSync } from "node:child_process"
22
import path from "node:path"
33
import { fileURLToPath } from "node:url"
44

@@ -27,11 +27,27 @@ const spawnEnv = {
2727
const hasComposePlugin = spawnSync("docker", ["compose", "version"], { stdio: "ignore" }).status === 0
2828
const command = hasComposePlugin ? "docker" : "docker-compose"
2929
const args = hasComposePlugin ? ["compose", ...composeArgs] : composeArgs
30-
const result = spawnSync(command, args, { stdio: "inherit", env: spawnEnv })
3130

32-
if (result.error) {
33-
console.error(`Unable to run ${command}: ${result.error.message}`)
34-
process.exit(1)
31+
const child = spawn(command, args, { stdio: "inherit", env: spawnEnv })
32+
33+
const forwardSignal = (signal) => {
34+
if (child.pid && !child.killed) {
35+
child.kill(signal)
36+
}
3537
}
3638

37-
process.exit(result.status ?? 1)
39+
process.on("SIGINT", () => forwardSignal("SIGINT"))
40+
process.on("SIGTERM", () => forwardSignal("SIGTERM"))
41+
42+
child.on("error", (err) => {
43+
console.error(`Unable to run ${command}: ${err.message}`)
44+
process.exit(1)
45+
})
46+
47+
child.on("close", (code, signal) => {
48+
if (signal) {
49+
process.kill(process.pid, signal)
50+
} else {
51+
process.exit(code ?? 1)
52+
}
53+
})

webview-ui/src/components/welcome/__tests__/RooHero.visual.tsx

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,38 @@ import React from "react"
33
import { expect, test } from "../../../../playwright/coverage-fixture"
44
import RooHero from "../RooHero"
55

6-
test("renders the welcome hero in the VS Code dark theme", async ({ mount }) => {
7-
const component = await mount(<RooHero />)
8-
6+
async function waitForAssetsAndRender(component: any) {
97
await component.evaluate(async () => {
108
await document.fonts.ready
9+
const images = Array.from(document.querySelectorAll("img"))
10+
await Promise.all(
11+
images.map((img) => {
12+
if (img.complete) return Promise.resolve()
13+
return new Promise((resolve) => {
14+
img.onload = resolve
15+
img.onerror = resolve
16+
})
17+
}),
18+
)
1119
await new Promise<void>((resolve) => requestAnimationFrame(() => resolve()))
1220
})
21+
}
22+
23+
test("renders the welcome hero in the VS Code dark theme", async ({ mount }) => {
24+
const component = await mount(<RooHero />)
25+
26+
await waitForAssetsAndRender(component)
1327

1428
await expect(component).toHaveScreenshot("zoo-hero-dark.png")
1529
})
30+
31+
test("renders the welcome hero hovered state", async ({ mount }) => {
32+
const component = await mount(<RooHero />)
33+
34+
await waitForAssetsAndRender(component)
35+
await component.hover()
36+
37+
await expect(component).toHaveScreenshot("zoo-hero-dark-hover.png", {
38+
animations: "disabled",
39+
})
40+
})

webview-ui/vitest.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export default defineConfig({
1515
reporters,
1616
silent,
1717
environment: "jsdom",
18-
include: ["src/**/*.spec.ts", "src/**/*.spec.tsx"],
18+
include: ["src/**/*.spec.{ts,tsx}", "src/**/*.test.{ts,tsx}"],
1919
onConsoleLog,
2020
maxWorkers: isCI ? 1 : undefined,
2121
testTimeout: isCI ? 15000 : 5000,

0 commit comments

Comments
 (0)