Skip to content

Commit dd3d8e7

Browse files
committed
test(webview): add visual regression harness
1 parent a390c95 commit dd3d8e7

14 files changed

Lines changed: 403 additions & 2 deletions

File tree

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: Webview Visual Regression
2+
3+
on:
4+
workflow_dispatch:
5+
pull_request:
6+
types: [opened, reopened, ready_for_review, synchronize]
7+
paths:
8+
- "webview-ui/**"
9+
- "src/shared/**"
10+
- "package.json"
11+
- "pnpm-lock.yaml"
12+
- ".github/workflows/visual-regression.yml"
13+
merge_group:
14+
types: [checks_requested]
15+
16+
permissions:
17+
contents: read
18+
19+
jobs:
20+
webview-visual:
21+
runs-on: ubuntu-latest
22+
timeout-minutes: 10
23+
container:
24+
image: mcr.microsoft.com/playwright:v1.60.0-noble@sha256:9bd26ad900bb5e0f4dee75839e957a89ae89c2b7ab1e76050e559790e946b948
25+
options: --ipc=host
26+
steps:
27+
- name: Checkout code
28+
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
29+
- name: Setup Node.js and pnpm
30+
uses: ./.github/actions/setup-node-pnpm
31+
with:
32+
install-args: "--frozen-lockfile"
33+
- name: Run webview visual tests
34+
run: pnpm --filter @roo-code/vscode-webview test:visual
35+
- name: Upload visual test artifacts
36+
if: failure()
37+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
38+
with:
39+
name: webview-visual-regression
40+
path: |
41+
webview-ui/playwright-report
42+
webview-ui/test-results
43+
if-no-files-found: ignore

pnpm-lock.yaml

Lines changed: 118 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

webview-ui/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77

88
# testing
99
/coverage
10+
/playwright-report
11+
/test-results
12+
/playwright/.cache
1013

1114
# production
1215
/build

webview-ui/AGENTS.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,14 @@ This file provides guidance to agents working in `webview-ui/`.
66
- Use `apps/vscode-e2e` only when the behavior depends on the real VS Code extension environment: extension-host to webview messaging, VS Code workspace APIs, task execution flows, or other end-to-end behavior that needs `@vscode/test-electron`.
77
- When a regression can be proven with a component or webview integration test, keep it in `webview-ui`. Do not promote it to e2e just because the UI is hosted inside VS Code.
88
- For `SettingsView`, preserve the cached-state pattern from the repo root guidance: inputs should operate on local `cachedState` until the user saves, and tests should distinguish automatic initialization from real user edits.
9+
10+
## Visual Tests
11+
12+
- Add Playwright screenshot tests selectively for components where layout, styling, VS Code theme variables, or real web-component rendering are part of the behavior under test.
13+
- Keep behavioral assertions in Vitest. A `*.visual.tsx` test should establish a deterministic state and make a focused screenshot assertion.
14+
- Run visual comparisons with `pnpm test:visual:docker` from `webview-ui/`.
15+
- Update intentional baselines with `pnpm test:visual:docker:update` and commit the resulting `__screenshots__` files with the UI change.
16+
- Use the Docker commands when creating or reviewing baselines; host-rendered screenshots are not the source of truth.
17+
- If Docker is unavailable, `pnpm test:visual` can help diagnose test code, but do not create or update committed baselines from the host rendering environment.
18+
- Keep visual tests limited to components supported by the current Playwright harness. Add shared extension state, translation, React Query, or other provider support before snapshotting components that require it.
19+
- The current baseline naming assumes a single Chromium project. Include `{projectName}` in `snapshotPathTemplate` before adding another browser project.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
services:
2+
visual:
3+
image: mcr.microsoft.com/playwright:v1.60.0-noble@sha256:9bd26ad900bb5e0f4dee75839e957a89ae89c2b7ab1e76050e559790e946b948
4+
ipc: host
5+
user: "${UID:-1000}:${GID:-1000}"
6+
working_dir: /work
7+
environment:
8+
COREPACK_HOME: /tmp/corepack
9+
HOME: /tmp/playwright
10+
volumes:
11+
- ..:/work
12+
command: sh -lc "corepack pnpm --filter @roo-code/vscode-webview test:visual"

webview-ui/package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
"pretest": "turbo run bundle --cwd ..",
99
"test": "vitest run",
1010
"test:coverage": "vitest run --coverage",
11+
"test:visual": "playwright test -c playwright-ct.config.ts",
12+
"test:visual:update": "playwright test -c playwright-ct.config.ts --update-snapshots",
13+
"test:visual:docker": "node playwright/run-docker.mjs",
14+
"test:visual:docker:update": "node playwright/run-docker.mjs --update",
1115
"format": "prettier --write src",
1216
"dev": "vite",
1317
"build": "tsc -b && vite build",
@@ -85,6 +89,8 @@
8589
"zod": "^3.25.61"
8690
},
8791
"devDependencies": {
92+
"@playwright/experimental-ct-react": "1.60.0",
93+
"@playwright/test": "1.60.0",
8894
"@roo-code/config-eslint": "workspace:^",
8995
"@roo-code/config-typescript": "workspace:^",
9096
"@testing-library/jest-dom": "6.6.3",

webview-ui/playwright-ct.config.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import path from "path"
2+
import { fileURLToPath } from "url"
3+
4+
import { defineConfig } from "@playwright/experimental-ct-react"
5+
import react from "@vitejs/plugin-react"
6+
import tailwindcss from "@tailwindcss/vite"
7+
8+
const dirname = path.dirname(fileURLToPath(import.meta.url))
9+
10+
export default defineConfig({
11+
testDir: "./src",
12+
testMatch: "**/*.visual.tsx",
13+
outputDir: process.env.CI ? path.resolve(dirname, "test-results") : "/tmp/webview-ui-playwright-test-results",
14+
snapshotPathTemplate: "{testDir}/{testFileDir}/__screenshots__/{arg}{ext}",
15+
fullyParallel: true,
16+
reporter: process.env.CI
17+
? [["html", { open: "never", outputFolder: path.resolve(dirname, "playwright-report") }], ["github"], ["list"]]
18+
: [["html", { open: "never", outputFolder: "/tmp/webview-ui-playwright-report" }], ["list"]],
19+
use: {
20+
ctTemplateDir: "./playwright",
21+
ctViteConfig: {
22+
plugins: [
23+
react({
24+
babel: {
25+
plugins: [["babel-plugin-react-compiler", { target: "18" }]],
26+
},
27+
}),
28+
tailwindcss(),
29+
],
30+
resolve: {
31+
alias: {
32+
"@": path.resolve(dirname, "./src"),
33+
"@src": path.resolve(dirname, "./src"),
34+
"@roo": path.resolve(dirname, "../src/shared"),
35+
vscode: path.resolve(dirname, "./src/__mocks__/vscode.ts"),
36+
},
37+
},
38+
define: {
39+
"process.platform": JSON.stringify(process.platform),
40+
"process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV ?? "test"),
41+
"process.env.PKG_NAME": JSON.stringify("zoo-code"),
42+
"process.env.PKG_VERSION": JSON.stringify("0.0.0-test"),
43+
"process.env.PKG_OUTPUT_CHANNEL": JSON.stringify("Zoo-Code"),
44+
"process.env.PKG_RELEASE_CHANNEL": JSON.stringify("stable"),
45+
},
46+
optimizeDeps: {
47+
exclude: ["@vscode/codicons"],
48+
},
49+
},
50+
viewport: { width: 520, height: 360 },
51+
deviceScaleFactor: 1,
52+
colorScheme: "dark",
53+
},
54+
expect: {
55+
toHaveScreenshot: {
56+
animations: "disabled",
57+
},
58+
},
59+
projects: [
60+
{
61+
name: "chromium",
62+
use: { browserName: "chromium" },
63+
},
64+
],
65+
})

webview-ui/playwright/index.html

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>webview-ui visual tests</title>
7+
</head>
8+
<body class="vscode-dark" data-vscode-theme-id="Default Dark Modern">
9+
<div id="root"></div>
10+
<script type="module" src="./index.tsx"></script>
11+
</body>
12+
</html>

webview-ui/playwright/index.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import "./vscode-theme-dark.css"
2+
import "@vscode/codicons/dist/codicon.css"
3+
import "../src/index.css"

0 commit comments

Comments
 (0)