Skip to content

Commit 0493db2

Browse files
committed
test(webview): add Codecov ratcheting & Playwright CT coverage pipeline
1 parent e10e9d3 commit 0493db2

8 files changed

Lines changed: 191 additions & 10 deletions

File tree

.github/workflows/code-qa.yml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,25 +135,32 @@ jobs:
135135
# lanes for behavioral confidence, but duplicating coverage uploads
136136
# there mostly adds Codecov overhead without changing pass/fail
137137
# behavior.
138-
# Coverage is uploaded in three separate steps so each LCOV gets the
138+
# Coverage is uploaded in separate steps so each LCOV gets the
139139
# correct flag set. Codecov double-counts overlapping lines when a
140140
# single upload carries multiple flags whose paths overlap, so the
141-
# two core lanes (which both cover packages/core/src/**) must be
142-
# uploaded individually with their own lane flag.
141+
# core lanes and webview lane must be uploaded individually with
142+
# their own flag.
143143
# See https://docs.codecov.com/docs/flags
144144
- name: Upload non-core coverage to Codecov
145145
if: matrix.upload-coverage
146146
uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4
147147
with:
148148
files: >-
149149
src/coverage/lcov.info,
150-
webview-ui/coverage/lcov.info,
151150
packages/cloud/coverage/lcov.info,
152151
packages/telemetry/coverage/lcov.info,
153152
apps/cli/coverage/lcov.info
154153
disable_search: true
155154
flags: ${{ matrix.codecov-flag }}
156155
token: ${{ secrets.CODECOV_TOKEN }}
156+
- name: Upload webview JSDOM coverage to Codecov
157+
if: matrix.upload-coverage
158+
uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4
159+
with:
160+
files: webview-ui/coverage/lcov.info
161+
disable_search: true
162+
flags: webview-ui
163+
token: ${{ secrets.CODECOV_TOKEN }}
157164
- name: Upload core unit coverage to Codecov
158165
if: matrix.upload-coverage
159166
uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4

.github/workflows/visual-regression.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ jobs:
3232
install-args: "--frozen-lockfile"
3333
- name: Run webview visual tests
3434
run: pnpm --filter @roo-code/vscode-webview test:visual
35+
- name: Upload visual test coverage to Codecov
36+
if: always()
37+
uses: codecov/codecov-action@b9fd7d16f6d7d1b5d2bec1a2887e65ceed900238 # v4
38+
with:
39+
files: webview-ui/coverage-ct/lcov.info
40+
disable_search: true
41+
flags: webview-ui-ct
42+
token: ${{ secrets.CODECOV_TOKEN }}
3543
- name: Upload visual test artifacts
3644
if: failure()
3745
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4

codecov.yml

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,31 @@ coverage:
66
default:
77
target: auto # never regress below current baseline
88
threshold: 1%
9+
webview:
10+
target: auto # webview project ratchet: never drop below current baseline
11+
threshold: 0.5%
12+
flags:
13+
- webview-ui
914
patch:
1015
default:
1116
target: 80% # new lines must be 80% covered
1217
threshold: 0%
18+
webview-patch:
19+
target: 70% # new lines in webview must be 70% covered
20+
threshold: 0%
21+
flags:
22+
- webview-ui
1323

1424
flag_management:
1525
individual_flags:
26+
- name: webview-ui
27+
paths:
28+
- webview-ui/src/
29+
carryforward: true
30+
- name: webview-ui-ct
31+
paths:
32+
- webview-ui/src/
33+
carryforward: true
1634
- name: core-unit
1735
paths:
1836
- packages/core/src/
@@ -22,6 +40,18 @@ flag_management:
2240
- packages/core/src/
2341
carryforward: true
2442

43+
component_management:
44+
individual_components:
45+
- component_id: webview_components
46+
name: "Webview UI Components"
47+
paths:
48+
- webview-ui/src/components/
49+
- component_id: webview_state
50+
name: "Webview State & Context"
51+
paths:
52+
- webview-ui/src/context/
53+
- webview-ui/src/state/
54+
2555
comment:
26-
layout: "diff, flags"
56+
layout: "diff, flags, components"
2757
behavior: default

knip.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
{
22
"$schema": "https://unpkg.com/knip@5/schema.json",
33
"ignore": ["**/__tests__/**", "apps/vscode-e2e/**", "scripts/**", "apps/cli/scripts/**"],
4-
"ignoreDependencies": ["lint-staged", "ovsx"],
4+
"ignoreDependencies": ["lint-staged"],
55
"ignoreExportsUsedInFile": true,
6+
"playwright": false,
7+
"playwright-ct": false,
68
"workspaces": {
79
"src": {
810
"entry": ["extension.ts", "extension/api.ts", "workers/countTokens.ts"],
@@ -30,7 +32,7 @@
3032
"source-map",
3133
"tailwindcss",
3234
"tailwindcss-animate",
33-
"vscode"
35+
"monocart-coverage-reports"
3436
]
3537
},
3638
"apps/cli": {

pnpm-lock.yaml

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

webview-ui/AGENTS.md

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,45 @@
22

33
This file provides guidance to agents working in `webview-ui/`.
44

5+
## Testing Strategy Overview
6+
7+
We use a complementary two-layer strategy for testing webview UI code:
8+
9+
1. **Vitest + JSDOM (`*.test.tsx`)**: Unit, hook, state-machine, and interaction tests.
10+
2. **Playwright Component Testing (`*.visual.tsx`)**: Visual snapshot, VS Code theme variable, layout, and shadow DOM tests.
11+
12+
---
13+
14+
### When to write a JSDOM Test (`*.test.tsx`) vs. a Playwright Visual Test (`*.visual.tsx`)
15+
16+
| Testing Goal | Recommended Harness |
17+
| :-------------------------------------------------------------------- | :-------------------------------------------------------------------- |
18+
| Component state transitions, reducer actions, custom hook behavior | **Vitest + JSDOM** (`*.test.tsx`) |
19+
| User interactions (button clicks, form validation, text typing) | **Vitest + JSDOM** (`*.test.tsx`) using `@testing-library/user-event` |
20+
| Conditional DOM rendering or prop wiring | **Vitest + JSDOM** (`*.test.tsx`) |
21+
| Visual layout, flexbox/grid alignment, or padding/margin verification | **Playwright CT** (`*.visual.tsx`) |
22+
| VS Code dark/light theme CSS tokens (`--vscode-*`) | **Playwright CT** (`*.visual.tsx`) |
23+
| Web component shadow DOM style encapsulation & upgrades | **Playwright CT** (`*.visual.tsx`) |
24+
25+
---
26+
27+
## Unit & State Tests (Vitest + JSDOM)
28+
529
- Prefer local `webview-ui` tests for React/webview behavior. If a change is about component rendering, local state, hooks, form dirty-state, validation, or prop wiring inside the webview, add or update Vitest coverage under `webview-ui/src/**/__tests__` instead of reaching for `apps/vscode-e2e`.
630
- 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`.
731
- 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.
832
- 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.
933

10-
## Visual Tests
34+
### Coverage & Codecov Quality Gates
35+
36+
Codecov tracks `webview-ui` coverage under the `webview-ui` flag.
37+
38+
- **Ratcheting (`target: auto`)**: Overall webview coverage will never drop below the current baseline as new tests are added.
39+
- **Patch Gate (`target: 70%`)**: New or modified lines in PRs touching `webview-ui/src/` must meet minimum test coverage, ensuring state changes and new UI logic stay tested over time.
40+
41+
---
42+
43+
## Visual Tests (Playwright CT)
1144

1245
- 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.
1346
- Keep behavioral assertions in Vitest. A `*.visual.tsx` test should establish a deterministic state and make a focused screenshot assertion.

webview-ui/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@
108108
"@vitest/ui": "4.1.9",
109109
"babel-plugin-react-compiler": "1.0.0",
110110
"jsdom": "26.1.0",
111+
"monocart-coverage-reports": "^2.12.12",
111112
"vite": "8.1.0",
112113
"vitest": "4.1.9"
113114
}

webview-ui/playwright-ct.config.ts

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,35 @@ export default defineConfig({
1414
snapshotPathTemplate: "{testDir}/{testFileDir}/__screenshots__/{arg}{ext}",
1515
fullyParallel: true,
1616
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"]],
17+
? [
18+
["html", { open: "never", outputFolder: path.resolve(dirname, "playwright-report") }],
19+
["github"],
20+
["list"],
21+
[
22+
"monocart-coverage-reports",
23+
{
24+
name: "Webview Playwright CT Coverage",
25+
outputDir: path.resolve(dirname, "coverage-ct"),
26+
reports: ["lcov", "html"],
27+
entryFilter: (entry: { url: string }) => entry.url.includes("/src/"),
28+
sourceFilter: (sourcePath: string) => sourcePath.includes("src/"),
29+
},
30+
],
31+
]
32+
: [
33+
["html", { open: "never", outputFolder: "/tmp/webview-ui-playwright-report" }],
34+
["list"],
35+
[
36+
"monocart-coverage-reports",
37+
{
38+
name: "Webview Playwright CT Coverage",
39+
outputDir: path.resolve(dirname, "coverage-ct"),
40+
reports: ["lcov", "html"],
41+
entryFilter: (entry: { url: string }) => entry.url.includes("/src/"),
42+
sourceFilter: (sourcePath: string) => sourcePath.includes("src/"),
43+
},
44+
],
45+
],
1946
use: {
2047
ctTemplateDir: "./playwright",
2148
ctViteConfig: {

0 commit comments

Comments
 (0)