From dd3d8e75d71965c8cf17efa76456ed6c94739a6e Mon Sep 17 00:00:00 2001 From: Elliott de Launay Date: Sun, 7 Jun 2026 23:50:43 +0000 Subject: [PATCH] test(webview): add visual regression harness --- .github/workflows/visual-regression.yml | 43 +++++++ pnpm-lock.yaml | 118 ++++++++++++++++++ webview-ui/.gitignore | 3 + webview-ui/AGENTS.md | 11 ++ webview-ui/docker-compose.visual.yml | 12 ++ webview-ui/package.json | 6 + webview-ui/playwright-ct.config.ts | 65 ++++++++++ webview-ui/playwright/index.html | 12 ++ webview-ui/playwright/index.tsx | 3 + webview-ui/playwright/run-docker.mjs | 20 +++ webview-ui/playwright/vscode-theme-dark.css | 89 +++++++++++++ .../__tests__/ProgressIndicator.visual.tsx | 20 +++ .../progress-indicator-dark.png | Bin 0 -> 494 bytes webview-ui/tsconfig.json | 3 +- 14 files changed, 403 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/visual-regression.yml create mode 100644 webview-ui/docker-compose.visual.yml create mode 100644 webview-ui/playwright-ct.config.ts create mode 100644 webview-ui/playwright/index.html create mode 100644 webview-ui/playwright/index.tsx create mode 100644 webview-ui/playwright/run-docker.mjs create mode 100644 webview-ui/playwright/vscode-theme-dark.css create mode 100644 webview-ui/src/components/chat/__tests__/ProgressIndicator.visual.tsx create mode 100644 webview-ui/src/components/chat/__tests__/__screenshots__/progress-indicator-dark.png diff --git a/.github/workflows/visual-regression.yml b/.github/workflows/visual-regression.yml new file mode 100644 index 0000000000..c487eb72dd --- /dev/null +++ b/.github/workflows/visual-regression.yml @@ -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 diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index df4220524e..bef615b746 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -965,6 +965,12 @@ importers: specifier: 3.25.76 version: 3.25.76 devDependencies: + '@playwright/experimental-ct-react': + specifier: 1.60.0 + version: 1.60.0(@types/node@20.19.41)(esbuild@0.28.0)(jiti@2.4.2)(tsx@4.19.4)(vite@8.0.14(@types/node@20.19.41)(esbuild@0.28.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.9.0))(yaml@2.9.0) + '@playwright/test': + specifier: 1.60.0 + version: 1.60.0 '@roo-code/config-eslint': specifier: workspace:^ version: link:../packages/config-eslint @@ -2297,6 +2303,20 @@ packages: cpu: [x64] os: [win32] + '@playwright/experimental-ct-core@1.60.0': + resolution: {integrity: sha512-cMYDe0KpfdfFTZR2ev6frlgIwksSnXCu1o5s/DxpiVPTJeKp8TSXo9dzKuRhUMDAxAQESF1uM4uPQ56PZ/a5QA==} + engines: {node: '>=18'} + + '@playwright/experimental-ct-react@1.60.0': + resolution: {integrity: sha512-PT9RbpLO1nRtIcG/odJKM0m0UjQjLKYOcjs52UpASb8fEWovInHGi10Tw6JXl/3aMgSayX2hUzhYd8+yRFxj4w==} + engines: {node: '>=18'} + hasBin: true + + '@playwright/test@1.60.0': + resolution: {integrity: sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==} + engines: {node: '>=18'} + hasBin: true + '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} @@ -2902,6 +2922,9 @@ packages: cpu: [x64] os: [win32] + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + '@rolldown/pluginutils@1.0.0-rc.3': resolution: {integrity: sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==} @@ -3772,6 +3795,12 @@ packages: resolution: {integrity: sha512-Fw28YZpRnA3cAHHDlkt7xQHiJ0fcL+NRcIqsocZQUSmbzeIKRpwttJjik5ZGanXP+vlA4SbTg+AbA3bP363l+w==} engines: {node: '>= 20'} + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: 8.0.14 + '@vitejs/plugin-react@5.2.0': resolution: {integrity: sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -5496,6 +5525,11 @@ packages: resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} engines: {node: '>=6 <7 || >=8'} + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -7457,6 +7491,16 @@ packages: pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + playwright-core@1.60.0: + resolution: {integrity: sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.60.0: + resolution: {integrity: sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==} + engines: {node: '>=18'} + hasBin: true + points-on-curve@0.2.0: resolution: {integrity: sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==} @@ -7681,6 +7725,10 @@ packages: peerDependencies: react: ^19.2.0 + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + react-refresh@0.18.0: resolution: {integrity: sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==} engines: {node: '>=0.10.0'} @@ -11054,6 +11102,49 @@ snapshots: '@oxc-resolver/binding-win32-x64-msvc@11.2.0': optional: true + '@playwright/experimental-ct-core@1.60.0(@types/node@20.19.41)(esbuild@0.28.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.9.0)': + dependencies: + playwright: 1.60.0 + playwright-core: 1.60.0 + vite: 8.0.14(@types/node@20.19.41)(esbuild@0.28.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.9.0) + transitivePeerDependencies: + - '@types/node' + - '@vitejs/devtools' + - esbuild + - jiti + - less + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml + + '@playwright/experimental-ct-react@1.60.0(@types/node@20.19.41)(esbuild@0.28.0)(jiti@2.4.2)(tsx@4.19.4)(vite@8.0.14(@types/node@20.19.41)(esbuild@0.28.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.9.0))(yaml@2.9.0)': + dependencies: + '@playwright/experimental-ct-core': 1.60.0(@types/node@20.19.41)(esbuild@0.28.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.9.0) + '@vitejs/plugin-react': 4.7.0(vite@8.0.14(@types/node@20.19.41)(esbuild@0.28.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.9.0)) + transitivePeerDependencies: + - '@types/node' + - '@vitejs/devtools' + - esbuild + - jiti + - less + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - vite + - yaml + + '@playwright/test@1.60.0': + dependencies: + playwright: 1.60.0 + '@polka/url@1.0.0-next.29': {} '@qdrant/js-client-rest@1.14.0(typescript@5.8.3)': @@ -11628,6 +11719,8 @@ snapshots: '@rolldown/binding-win32-x64-msvc@1.0.2': optional: true + '@rolldown/pluginutils@1.0.0-beta.27': {} + '@rolldown/pluginutils@1.0.0-rc.3': {} '@rolldown/pluginutils@1.0.1': {} @@ -12600,6 +12693,18 @@ snapshots: '@vercel/oidc@3.1.0': {} + '@vitejs/plugin-react@4.7.0(vite@8.0.14(@types/node@20.19.41)(esbuild@0.28.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.9.0))': + dependencies: + '@babel/core': 7.29.0 + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 8.0.14(@types/node@20.19.41)(esbuild@0.28.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.9.0) + transitivePeerDependencies: + - supports-color + '@vitejs/plugin-react@5.2.0(vite@8.0.14(@types/node@20.19.41)(esbuild@0.28.0)(jiti@2.4.2)(tsx@4.19.4)(yaml@2.9.0))': dependencies: '@babel/core': 7.29.0 @@ -14660,6 +14765,9 @@ snapshots: jsonfile: 4.0.0 universalify: 0.1.2 + fsevents@2.3.2: + optional: true + fsevents@2.3.3: optional: true @@ -16963,6 +17071,14 @@ snapshots: mlly: 1.7.4 pathe: 2.0.3 + playwright-core@1.60.0: {} + + playwright@1.60.0: + dependencies: + playwright-core: 1.60.0 + optionalDependencies: + fsevents: 2.3.2 + points-on-curve@0.2.0: {} points-on-path@0.2.1: @@ -17195,6 +17311,8 @@ snapshots: react: 19.2.3 scheduler: 0.27.0 + react-refresh@0.17.0: {} + react-refresh@0.18.0: {} react-remark@2.1.0(react@18.3.1): diff --git a/webview-ui/.gitignore b/webview-ui/.gitignore index 1f81cba3f5..6ea14c0fd4 100644 --- a/webview-ui/.gitignore +++ b/webview-ui/.gitignore @@ -7,6 +7,9 @@ # testing /coverage +/playwright-report +/test-results +/playwright/.cache # production /build diff --git a/webview-ui/AGENTS.md b/webview-ui/AGENTS.md index adab48e9e0..4f778efc07 100644 --- a/webview-ui/AGENTS.md +++ b/webview-ui/AGENTS.md @@ -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. diff --git a/webview-ui/docker-compose.visual.yml b/webview-ui/docker-compose.visual.yml new file mode 100644 index 0000000000..477d359ad8 --- /dev/null +++ b/webview-ui/docker-compose.visual.yml @@ -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" diff --git a/webview-ui/package.json b/webview-ui/package.json index a30725b6db..912fd6f890 100644 --- a/webview-ui/package.json +++ b/webview-ui/package.json @@ -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", @@ -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", diff --git a/webview-ui/playwright-ct.config.ts b/webview-ui/playwright-ct.config.ts new file mode 100644 index 0000000000..5c7ebde101 --- /dev/null +++ b/webview-ui/playwright-ct.config.ts @@ -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" }, + }, + ], +}) diff --git a/webview-ui/playwright/index.html b/webview-ui/playwright/index.html new file mode 100644 index 0000000000..144ced8a48 --- /dev/null +++ b/webview-ui/playwright/index.html @@ -0,0 +1,12 @@ + + + + + + webview-ui visual tests + + +
+ + + diff --git a/webview-ui/playwright/index.tsx b/webview-ui/playwright/index.tsx new file mode 100644 index 0000000000..de2867bd70 --- /dev/null +++ b/webview-ui/playwright/index.tsx @@ -0,0 +1,3 @@ +import "./vscode-theme-dark.css" +import "@vscode/codicons/dist/codicon.css" +import "../src/index.css" diff --git a/webview-ui/playwright/run-docker.mjs b/webview-ui/playwright/run-docker.mjs new file mode 100644 index 0000000000..e223199808 --- /dev/null +++ b/webview-ui/playwright/run-docker.mjs @@ -0,0 +1,20 @@ +import { spawnSync } from "node:child_process" + +const updateSnapshots = process.argv.includes("--update") +const composeArgs = ["-f", "docker-compose.visual.yml", "run", "--rm", "visual"] + +if (updateSnapshots) { + composeArgs.push("sh", "-lc", "corepack pnpm --filter @roo-code/vscode-webview test:visual:update") +} + +const hasComposePlugin = spawnSync("docker", ["compose", "version"], { stdio: "ignore" }).status === 0 +const command = hasComposePlugin ? "docker" : "docker-compose" +const args = hasComposePlugin ? ["compose", ...composeArgs] : composeArgs +const result = spawnSync(command, args, { stdio: "inherit" }) + +if (result.error) { + console.error(`Unable to run ${command}: ${result.error.message}`) + process.exit(1) +} + +process.exit(result.status ?? 1) diff --git a/webview-ui/playwright/vscode-theme-dark.css b/webview-ui/playwright/vscode-theme-dark.css new file mode 100644 index 0000000000..30d49f968b --- /dev/null +++ b/webview-ui/playwright/vscode-theme-dark.css @@ -0,0 +1,89 @@ +:root { + color-scheme: dark; + --vscode-font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; + --vscode-font-size: 13px; + --vscode-font-weight: normal; + --vscode-editor-font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; + --vscode-editor-font-size: 12px; + --vscode-editor-line-height: 18px; + --vscode-foreground: #cccccc; + --vscode-disabledForeground: #808080; + --vscode-descriptionForeground: #9d9d9d; + --vscode-errorForeground: #f48771; + --vscode-focusBorder: #007fd4; + --vscode-editor-foreground: #d4d4d4; + --vscode-editor-background: #1e1e1e; + --vscode-editorGroup-border: #2b2b2b; + --vscode-editorWarning-foreground: #cca700; + --vscode-editorWarning-background: #352a05; + --vscode-button-foreground: #ffffff; + --vscode-button-background: #0e639c; + --vscode-button-hoverBackground: #1177bb; + --vscode-button-secondaryForeground: #cccccc; + --vscode-button-secondaryBackground: #3a3d41; + --vscode-dropdown-foreground: #f0f0f0; + --vscode-dropdown-background: #3c3c3c; + --vscode-dropdown-border: #3c3c3c; + --vscode-input-foreground: #cccccc; + --vscode-input-background: #3c3c3c; + --vscode-input-border: #3c3c3c; + --vscode-badge-foreground: #ffffff; + --vscode-badge-background: #4d4d4d; + --vscode-notifications-foreground: #cccccc; + --vscode-notifications-background: #252526; + --vscode-notifications-border: #303031; + --vscode-list-hoverForeground: #cccccc; + --vscode-list-hoverBackground: #2a2d2e; + --vscode-list-focusBackground: #04395e; + --vscode-list-activeSelectionBackground: #04395e; + --vscode-list-activeSelectionForeground: #ffffff; + --vscode-toolbar-hoverBackground: #2a2d2e; + --vscode-toolbar-hoverOutline: #00000000; + --vscode-panel-border: #2b2b2b; + --vscode-progressBar-background: #0e70c0; + --vscode-sideBar-foreground: #cccccc; + --vscode-sideBar-background: #252526; + --vscode-sideBar-border: #2b2b2b; + --vscode-sideBarSectionHeader-foreground: #cccccc; + --vscode-sideBarSectionHeader-background: #00000000; + --vscode-sideBarSectionHeader-border: #2b2b2b; + --vscode-titleBar-activeForeground: #cccccc; + --vscode-titleBar-inactiveForeground: #999999; + --vscode-charts-blue: #3794ff; + --vscode-charts-green: #89d185; + --vscode-charts-orange: #d18616; + --vscode-charts-red: #f14c4c; + --vscode-charts-yellow: #cca700; + --vscode-inputValidation-infoForeground: #d4d4d4; + --vscode-inputValidation-infoBackground: #063b49; + --vscode-inputValidation-infoBorder: #007acc; + --vscode-inputValidation-warningForeground: #d4d4d4; + --vscode-inputValidation-warningBackground: #352a05; + --vscode-inputValidation-warningBorder: #b89500; + --vscode-widget-border: #303031; + --vscode-widget-shadow: #0000005c; + --vscode-textLink-foreground: #3794ff; + --vscode-textCodeBlock-background: #2d2d2d; + --vscode-menu-foreground: #cccccc; + --vscode-menu-background: #252526; + --vscode-editorHoverWidget-foreground: #cccccc; + --vscode-editorHoverWidget-background: #252526; + --vscode-editorHoverWidget-border: #454545; + --vscode-banner-background: #04395e; + --vscode-banner-foreground: #ffffff; + --vscode-scrollbarSlider-background: #79797966; + --vscode-scrollbarSlider-hoverBackground: #646464b3; + --vscode-scrollbarSlider-activeBackground: #bfbfbf66; +} + +body { + margin: 0; + background: var(--vscode-editor-background); + color: var(--vscode-editor-foreground); + font-family: var(--vscode-font-family); + font-size: var(--vscode-font-size); +} + +#root { + padding: 16px; +} diff --git a/webview-ui/src/components/chat/__tests__/ProgressIndicator.visual.tsx b/webview-ui/src/components/chat/__tests__/ProgressIndicator.visual.tsx new file mode 100644 index 0000000000..3a54ba9a55 --- /dev/null +++ b/webview-ui/src/components/chat/__tests__/ProgressIndicator.visual.tsx @@ -0,0 +1,20 @@ +import React from "react" +import { expect, test } from "@playwright/experimental-ct-react" + +import { ProgressIndicator } from "../ProgressIndicator" + +test("renders a toolkit progress indicator in the VS Code dark theme", async ({ mount }) => { + const component = await mount() + const progressRing = component.locator("vscode-progress-ring") + + await progressRing.evaluate(async (element) => { + await customElements.whenDefined("vscode-progress-ring") + await new Promise((resolve) => requestAnimationFrame(() => resolve())) + + if (!element.shadowRoot) { + throw new Error("VSCodeProgressRing did not create its shadow root") + } + }) + + await expect(component).toHaveScreenshot("progress-indicator-dark.png") +}) diff --git a/webview-ui/src/components/chat/__tests__/__screenshots__/progress-indicator-dark.png b/webview-ui/src/components/chat/__tests__/__screenshots__/progress-indicator-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..407c74d9bea02c203e489a53415d997d321e180e GIT binary patch literal 494 zcmVX zD-lyGMN4!18}HoCD2Xl<`(T)J?)}cW=jWrErlCJ@Oe?(}Qy$=QKe&RP#Ck$Z1yKWN z-KzAA=<{2=dk{54$j0Q`1DcK2T%srn{7Rz>`K4`ScM65HEplUfITz!1xcF^KDwhY! zo;7%iWZtZJD2X|Te!TnOh@s1_z%gc}dU}&O3e!Hry@HtGyx67rg!Mvhafdq;TkuSN zI1)H(s>iI@KJFom^-VH$!XCcDmF{8FrQxrz^mvL|Knk4TY?P@Us0EwVfrxQGY6~%D z5r9mgCu>-lNc#X%B`~Rva&Cgf1&lg^fQg>8LY1-hMCP&xFkB2DSY`LJN$joIa8({# zvI^5bG0DA?^egXDi&N{Z0*W&}Adqy3=+!;Yt<8 literal 0 HcmV?d00001 diff --git a/webview-ui/tsconfig.json b/webview-ui/tsconfig.json index c075b99612..ccd0a2f548 100644 --- a/webview-ui/tsconfig.json +++ b/webview-ui/tsconfig.json @@ -18,12 +18,11 @@ "isolatedModules": true, "noEmit": true, "jsx": "react-jsx", - "baseUrl": ".", "paths": { "@/*": ["./src/*"], "@src/*": ["./src/*"], "@roo/*": ["../src/shared/*"] } }, - "include": ["src", "../src/shared", "vitest.setup.ts"] + "include": ["src", "playwright", "playwright-ct.config.ts", "../src/shared", "vitest.setup.ts"] }