Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/workflows/visual-regression.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Webview Visual Regression

on:
workflow_dispatch:
pull_request:
types: [opened, reopened, ready_for_review, synchronize]
paths:
- "webview-ui/**"
- "src/shared/**"
- "package.json"
- "pnpm-lock.yaml"
- ".github/workflows/visual-regression.yml"
merge_group:
types: [checks_requested]

permissions:
contents: read

jobs:
webview-visual:
runs-on: ubuntu-latest
timeout-minutes: 10
container:
image: mcr.microsoft.com/playwright:v1.60.0-noble@sha256:9bd26ad900bb5e0f4dee75839e957a89ae89c2b7ab1e76050e559790e946b948
options: --ipc=host
steps:
- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- name: Setup Node.js and pnpm
uses: ./.github/actions/setup-node-pnpm
with:
install-args: "--frozen-lockfile"
- name: Run webview visual tests
run: pnpm --filter @roo-code/vscode-webview test:visual
- name: Upload visual test artifacts
if: failure()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: webview-visual-regression
path: |
webview-ui/playwright-report
webview-ui/test-results
if-no-files-found: ignore
118 changes: 118 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions webview-ui/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

# testing
/coverage
/playwright-report
/test-results
/playwright/.cache

# production
/build
Expand Down
11 changes: 11 additions & 0 deletions webview-ui/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,14 @@ This file provides guidance to agents working in `webview-ui/`.
- 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`.
- 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.
- 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.

## Visual Tests

- 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.
- Keep behavioral assertions in Vitest. A `*.visual.tsx` test should establish a deterministic state and make a focused screenshot assertion.
- Run visual comparisons with `pnpm test:visual:docker` from `webview-ui/`.
- Update intentional baselines with `pnpm test:visual:docker:update` and commit the resulting `__screenshots__` files with the UI change.
- Use the Docker commands when creating or reviewing baselines; host-rendered screenshots are not the source of truth.
- 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.
- 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.
- The current baseline naming assumes a single Chromium project. Include `{projectName}` in `snapshotPathTemplate` before adding another browser project.
12 changes: 12 additions & 0 deletions webview-ui/docker-compose.visual.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
services:
visual:
image: mcr.microsoft.com/playwright:v1.60.0-noble@sha256:9bd26ad900bb5e0f4dee75839e957a89ae89c2b7ab1e76050e559790e946b948
ipc: host
user: "${UID:-1000}:${GID:-1000}"
working_dir: /work
environment:
COREPACK_HOME: /tmp/corepack
HOME: /tmp/playwright
volumes:
- ..:/work
command: sh -lc "corepack pnpm --filter @roo-code/vscode-webview test:visual"
6 changes: 6 additions & 0 deletions webview-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
"pretest": "turbo run bundle --cwd ..",
"test": "vitest run",
"test:coverage": "vitest run --coverage",
"test:visual": "playwright test -c playwright-ct.config.ts",
"test:visual:update": "playwright test -c playwright-ct.config.ts --update-snapshots",
"test:visual:docker": "node playwright/run-docker.mjs",
"test:visual:docker:update": "node playwright/run-docker.mjs --update",
"format": "prettier --write src",
"dev": "vite",
"build": "tsc -b && vite build",
Expand Down Expand Up @@ -85,6 +89,8 @@
"zod": "^3.25.61"
},
"devDependencies": {
"@playwright/experimental-ct-react": "1.60.0",
"@playwright/test": "1.60.0",
"@roo-code/config-eslint": "workspace:^",
"@roo-code/config-typescript": "workspace:^",
"@testing-library/jest-dom": "6.6.3",
Expand Down
65 changes: 65 additions & 0 deletions webview-ui/playwright-ct.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import path from "path"
import { fileURLToPath } from "url"

import { defineConfig } from "@playwright/experimental-ct-react"
import react from "@vitejs/plugin-react"
import tailwindcss from "@tailwindcss/vite"

const dirname = path.dirname(fileURLToPath(import.meta.url))

export default defineConfig({
testDir: "./src",
testMatch: "**/*.visual.tsx",
outputDir: process.env.CI ? path.resolve(dirname, "test-results") : "/tmp/webview-ui-playwright-test-results",
snapshotPathTemplate: "{testDir}/{testFileDir}/__screenshots__/{arg}{ext}",
fullyParallel: true,
reporter: process.env.CI
? [["html", { open: "never", outputFolder: path.resolve(dirname, "playwright-report") }], ["github"], ["list"]]
: [["html", { open: "never", outputFolder: "/tmp/webview-ui-playwright-report" }], ["list"]],
use: {
ctTemplateDir: "./playwright",
ctViteConfig: {
plugins: [
react({
babel: {
plugins: [["babel-plugin-react-compiler", { target: "18" }]],
},
}),
tailwindcss(),
],
resolve: {
alias: {
"@": path.resolve(dirname, "./src"),
"@src": path.resolve(dirname, "./src"),
"@roo": path.resolve(dirname, "../src/shared"),
vscode: path.resolve(dirname, "./src/__mocks__/vscode.ts"),
},
},
define: {
"process.platform": JSON.stringify(process.platform),
"process.env.NODE_ENV": JSON.stringify(process.env.NODE_ENV ?? "test"),
"process.env.PKG_NAME": JSON.stringify("zoo-code"),
"process.env.PKG_VERSION": JSON.stringify("0.0.0-test"),
"process.env.PKG_OUTPUT_CHANNEL": JSON.stringify("Zoo-Code"),
"process.env.PKG_RELEASE_CHANNEL": JSON.stringify("stable"),
},
optimizeDeps: {
exclude: ["@vscode/codicons"],
},
},
viewport: { width: 520, height: 360 },
deviceScaleFactor: 1,
colorScheme: "dark",
},
expect: {
toHaveScreenshot: {
animations: "disabled",
},
},
projects: [
{
name: "chromium",
use: { browserName: "chromium" },
},
],
})
12 changes: 12 additions & 0 deletions webview-ui/playwright/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>webview-ui visual tests</title>
</head>
<body class="vscode-dark" data-vscode-theme-id="Default Dark Modern">
<div id="root"></div>
<script type="module" src="./index.tsx"></script>
</body>
</html>
3 changes: 3 additions & 0 deletions webview-ui/playwright/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import "./vscode-theme-dark.css"
import "@vscode/codicons/dist/codicon.css"
import "../src/index.css"
Loading
Loading