Skip to content

Commit 7392dc7

Browse files
authored
feat: support debugging tests in the browser mode (#662)
1 parent a428472 commit 7392dc7

38 files changed

+901
-456
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ jobs:
4343

4444
- name: test-e2e-legacy
4545
run: pnpm test-e2e --retry 2
46+
env:
47+
TEST_LEGACY: true
4648

4749
- uses: actions/upload-artifact@v4
4850
if: always()

CLAUDE.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
This is the **Vitest extension for Visual Studio Code** - a VSCode extension that provides test running, debugging, and coverage capabilities for Vitest tests. The extension integrates with VSCode's native TestController API to provide a unified testing experience.
8+
9+
## Architecture
10+
11+
The project uses a monorepo structure with multiple packages that work together:
12+
13+
- **packages/extension** - Main VSCode extension entry point and core logic
14+
- **packages/shared** - Shared utilities and RPC communication between extension and workers
15+
- **packages/worker** - New worker implementation for running Vitest processes
16+
- **packages/worker-legacy** - Legacy worker implementation for older Vitest versions
17+
- **samples/** - Sample projects for testing and demonstration
18+
19+
### Key Components
20+
21+
- **Extension Host** (packages/extension): Manages VSCode integration, test discovery, debugging, coverage
22+
- **Worker Processes** (packages/worker*): Execute Vitest in isolated processes, handle test running and reporting
23+
- **RPC Communication** (packages/shared): Bidirectional communication between extension and workers using birpc
24+
- **API Abstraction**: Supports both child_process and terminal shell types for running Vitest
25+
26+
## Development Commands
27+
28+
### Building
29+
```bash
30+
pnpm build # Build for production (minified)
31+
pnpm dev # Build in development mode with watch and sourcemap
32+
pnpm vscode:prepublish # Prepare for publishing (runs build)
33+
```
34+
35+
### Testing
36+
```bash
37+
pnpm test # Run unit tests (Mocha-based VSCode tests)
38+
pnpm test:watch # Run unit tests in watch mode
39+
pnpm test-e2e # Run end-to-end tests (Vitest-based)
40+
```
41+
42+
### Code Quality
43+
```bash
44+
pnpm typecheck # TypeScript type checking
45+
pnpm lint # Run ESLint
46+
pnpm lint:fix # Run ESLint with auto-fix
47+
```
48+
49+
### Packaging
50+
```bash
51+
pnpm package # Create .vsix package for distribution
52+
```
53+
54+
## Package Manager
55+
56+
Uses **pnpm** with workspaces. The project requires pnpm@10.11.1 as specified in package.json.
57+
58+
## Build System
59+
60+
- **tsup** - Main build tool for bundling TypeScript
61+
- Multiple entry points: extension, workers, setup files
62+
- Supports both CJS and ESM output formats
63+
- External dependencies like 'vscode' and 'vitest' are excluded from bundles
64+
65+
## Testing Infrastructure
66+
67+
- **Unit Tests**: Mocha-based tests in `test/unit/` using `@vscode/test-cli`
68+
- **E2E Tests**: Vitest-based tests in `test/e2e/`
69+
- **VSCode Test Runner**: Uses `.vscode-test.mjs` configuration
70+
- **Samples**: Multiple sample projects for testing different scenarios
71+
72+
## Worker Architecture
73+
74+
The extension uses a multi-process architecture:
75+
- Extension runs in VSCode extension host
76+
- Worker processes execute Vitest in isolation
77+
- Communication via RPC (birpc)
78+
- Supports both legacy and modern Vitest versions
79+
- Can spawn workers via child_process or terminal
80+
81+
## Key Configuration Files
82+
83+
- `tsup.config.ts` - Build configuration with multiple entry points
84+
- `pnpm-workspace.yaml` - Workspace and catalog definitions
85+
- `.vscode-test.mjs` - VSCode test runner configuration
86+
- `tsconfig.base.json` - Base TypeScript configuration
87+
88+
## Development Notes
89+
90+
- Extension activates on workspaces containing Vitest config files
91+
- Supports both standalone configs and Vitest workspace configurations
92+
- Uses static AST analysis for test discovery (experimentalStaticAstCollect)
93+
- Integrates with VSCode's native testing UI and debugging capabilities
94+
- Supports coverage collection and display

package.json

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -315,21 +315,25 @@
315315
},
316316
"pnpm": {
317317
"overrides": {
318-
"@vitest/browser": "^4.0.0-beta.8",
319-
"@vitest/coverage": "^4.0.0-beta.8",
320-
"@vitest/runner": "^4.0.0-beta.8",
321-
"vitest": "^4.0.0-beta.8",
318+
"@vitest/browser": "^4.0.0-beta.12",
319+
"@vitest/coverage": "^4.0.0-beta.12",
320+
"@vitest/runner": "^4.0.0-beta.12",
321+
"@vitest/utils": "^4.0.0-beta.12",
322+
"vitest": "^4.0.0-beta.12",
322323
"vitest-vscode-extension>@vitest/browser": "^3.2.0",
323324
"vitest-vscode-extension>@vitest/coverage": "^3.2.0",
324325
"vitest-vscode-extension>@vitest/runner": "^3.2.0",
326+
"vitest-vscode-extension>@vitest/utils": "^3.2.0",
325327
"vitest-vscode-extension>vitest": "^3.2.0",
326328
"vitest-vscode-shared>@vitest/browser": "^3.2.0",
327329
"vitest-vscode-shared>@vitest/coverage": "^3.2.0",
328330
"vitest-vscode-shared>@vitest/runner": "^3.2.0",
331+
"vitest-vscode-shared>@vitest/utils": "^3.2.0",
329332
"vitest-vscode-shared>vitest": "^3.2.0",
330333
"vitest-vscode-worker-legacy>@vitest/browser": "^3.2.0",
331334
"vitest-vscode-worker-legacy>@vitest/coverage": "^3.2.0",
332335
"vitest-vscode-worker-legacy>@vitest/runner": "^3.2.0",
336+
"vitest-vscode-worker-legacy>@vitest/utils": "^3.2.0",
333337
"vitest-vscode-worker-legacy>vitest": "^3.2.0"
334338
}
335339
},

packages/extension/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"version": "0.0.0",
44
"private": true,
55
"dependencies": {
6+
"@vitest/browser": "workspace:*",
67
"vitest": "$vitest",
78
"vitest-vscode-shared": "workspace:*"
89
}

packages/extension/src/api.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,11 @@ export class VitestFolderAPI {
183183
}
184184

185185
waitForCoverageReport() {
186-
return this.meta.rpc.waitForCoverageReport()
186+
if (this.process.closed)
187+
return
188+
return this.meta.rpc.waitForCoverageReport().catch(() => {
189+
// ignore if failed -- can only fail if rpc is closed
190+
})
187191
}
188192

189193
async invalidateIstanbulTestModules(modules: string[] | null) {

packages/extension/src/api/child_process.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export async function createVitestProcess(pkg: VitestPackage) {
9090
vitest.on('exit', onExit)
9191
vitest.on('error', onError)
9292

93-
waitForWsConnection(wss, pkg, false, 'child_process', false)
93+
waitForWsConnection(wss, pkg, 'child_process', false)
9494
.then((resolved) => {
9595
resolved.handlers.onStdout = (callback: (data: string) => void) => {
9696
stdoutCallbacks.add(callback)

packages/extension/src/api/terminal.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ export async function createVitestTerminalProcess(pkg: VitestPackage): Promise<R
112112
wss.once('connection', () => {
113113
clearTimeout(timeout)
114114
})
115-
waitForWsConnection(wss, pkg, false, 'terminal', !!shellIntegration).then(resolve, reject)
115+
waitForWsConnection(wss, pkg, 'terminal', !!shellIntegration).then(resolve, reject)
116116
})
117117

118118
log.info('[API]', `${formatPkg(pkg)} terminal process ${processId} created`)

packages/extension/src/api/ws.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import type { WorkerEvent, WorkerRunnerOptions } from 'vitest-vscode-shared'
1+
import type { WorkerEvent, WorkerRunnerDebugOptions, WorkerRunnerOptions } from 'vitest-vscode-shared'
22
import type { WebSocket, WebSocketServer } from 'ws'
33
import type { ResolvedMeta } from '../api'
44
import type { VitestPackage } from './pkg'
55
import { pathToFileURL } from 'node:url'
66
import { gte } from 'semver'
77
import { getConfig } from '../config'
8-
import { finalCoverageFileName, setupFilePath } from '../constants'
8+
import { browserSetupFilePath, finalCoverageFileName, setupFilePath } from '../constants'
99
import { log } from '../log'
1010
import { createVitestRpc } from './rpc'
1111

@@ -16,7 +16,6 @@ export type WsConnectionMetadata = Omit<ResolvedMeta, 'process'> & {
1616
export function waitForWsConnection(
1717
wss: WebSocketServer,
1818
pkg: VitestPackage,
19-
debug: boolean,
2019
shellType: 'terminal' | 'child_process',
2120
hasShellIntegration: boolean,
2221
) {
@@ -25,7 +24,7 @@ export function waitForWsConnection(
2524
onWsConnection(
2625
ws,
2726
pkg,
28-
debug,
27+
false,
2928
shellType,
3029
hasShellIntegration,
3130
meta => resolve(meta),
@@ -52,7 +51,7 @@ export function waitForWsConnection(
5251
export function onWsConnection(
5352
ws: WebSocket,
5453
pkg: VitestPackage,
55-
debug: boolean,
54+
debug: WorkerRunnerDebugOptions | boolean,
5655
shellType: 'terminal' | 'child_process',
5756
hasShellIntegration: boolean,
5857
onStart: (meta: WsConnectionMetadata) => unknown,
@@ -132,7 +131,10 @@ export function onWsConnection(
132131
pnpLoader: pnpLoader && gte(process.version, '18.19.0')
133132
? pathToFileURL(pnpLoader).toString()
134133
: undefined,
135-
setupFilePath,
134+
setupFilePaths: {
135+
watcher: setupFilePath,
136+
browserDebug: browserSetupFilePath,
137+
},
136138
finalCoverageFileName,
137139
},
138140
debug,

packages/extension/src/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export const minimumNodeVersion = '18.0.0'
77
export const distDir = __dirname
88
export const workerPath = resolve(__dirname, 'worker.js')
99
export const setupFilePath = resolve(__dirname, 'setupFile.mjs')
10+
export const browserSetupFilePath = resolve(__dirname, 'browserSetupFile.mjs')
1011

1112
export const configGlob = '**/*{vite,vitest}*.config*.{ts,js,mjs,cjs,cts,mts}'
1213
export const workspaceGlob = '**/*vitest.{workspace,projects}*.{ts,js,mjs,cjs,cts,mts,json}'

0 commit comments

Comments
 (0)