diff --git a/docs/guide/debugging.md b/docs/guide/debugging.md index e827d85ac525..a723018198b4 100644 --- a/docs/guide/debugging.md +++ b/docs/guide/debugging.md @@ -38,6 +38,66 @@ You can also add a dedicated launch configuration to debug a test file in VS Cod Then in the debug tab, ensure 'Debug Current Test File' is selected. You can then open the test file you want to debug and press F5 to start debugging. +### Browser mode + +To debug [Vitest Browser Mode](/guide/browser/index.md), pass `--inspect` in CLI or define it in your Vitest configuration: + +::: code-group +```bash [CLI] +vitest --inspect --browser +``` +```ts [vitest.config.js] +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + inspect: true, + browser: { + name: 'chromium', + provider: 'playwright', + }, + }, +}) +``` +::: + +By default Vitest will use port `9229` as debugging port. You can overwrite it with by passing value in `inspect`: + +```bash +vitest --inspect=127.0.0.1:3000 --browser +``` + +Use following [VSCode Compound configuration](https://code.visualstudio.com/docs/editor/debugging#_compound-launch-configurations) for launching Vitest and attaching debugger in the browser: + +```json +{ + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Run Vitest Browser", + "program": "${workspaceRoot}/node_modules/vitest/vitest.mjs", + "console": "integratedTerminal", + "args": ["--inspect", "--browser"] + }, + { + "type": "chrome", + "request": "attach", + "name": "Attach to Vitest Browser", + "port": 9229 + } + ], + "compounds": [ + { + "name": "Debug Vitest Browser", + "configurations": ["Attach to Vitest Browser", "Run Vitest Browser"], + "stopAll": true + } + ] +} +``` + ## IntelliJ IDEA Create a 'Node.js' run configuration. Use the following settings to run all tests in debug mode: diff --git a/packages/browser/src/node/providers/playwright.ts b/packages/browser/src/node/providers/playwright.ts index cef27c34f629..d037649b8230 100644 --- a/packages/browser/src/node/providers/playwright.ts +++ b/packages/browser/src/node/providers/playwright.ts @@ -71,6 +71,18 @@ export class PlaywrightBrowserProvider implements BrowserProvider { headless: options.headless, } satisfies LaunchOptions + if (this.ctx.config.inspector.enabled) { + // NodeJS equivalent defaults: https://nodejs.org/en/learn/getting-started/debugging#enable-inspector + const port = this.ctx.config.inspector.port || 9229 + const host = this.ctx.config.inspector.host || '127.0.0.1' + + launchOptions.args ||= [] + launchOptions.args.push(`--remote-debugging-port=${port}`) + launchOptions.args.push(`--remote-debugging-address=${host}`) + + this.ctx.logger.log(`Debugger listening on ws://${host}:${port}`) + } + // start Vitest UI maximized only on supported browsers if (this.ctx.config.browser.ui && this.browserName === 'chromium') { if (!launchOptions.args) { diff --git a/packages/vitest/src/node/config/resolveConfig.ts b/packages/vitest/src/node/config/resolveConfig.ts index 443c2d8a6d5e..604af0171210 100644 --- a/packages/vitest/src/node/config/resolveConfig.ts +++ b/packages/vitest/src/node/config/resolveConfig.ts @@ -213,8 +213,9 @@ export function resolveConfig( && resolved.poolOptions?.threads?.singleThread const isSingleFork = resolved.pool === 'forks' && resolved.poolOptions?.forks?.singleFork + const isBrowser = resolved.browser.enabled - if (resolved.fileParallelism && !isSingleThread && !isSingleFork) { + if (resolved.fileParallelism && !isSingleThread && !isSingleFork && !isBrowser) { const inspectOption = `--inspect${resolved.inspectBrk ? '-brk' : ''}` throw new Error( `You cannot use ${inspectOption} without "--no-file-parallelism", "poolOptions.threads.singleThread" or "poolOptions.forks.singleFork"`, @@ -222,17 +223,27 @@ export function resolveConfig( } } - // In browser-mode v8-coverage works only with playwright + chromium - if (resolved.browser.enabled && resolved.coverage.enabled && resolved.coverage.provider === 'v8') { - if (!(resolved.browser.provider === 'playwright' && resolved.browser.name === 'chromium')) { - const browserConfig = { browser: { provider: resolved.browser.provider, name: resolved.browser.name } } + // Browser-mode "Playwright + Chromium" only features: + if (resolved.browser.enabled && !(resolved.browser.provider === 'playwright' && resolved.browser.name === 'chromium')) { + const browserConfig = { browser: { provider: resolved.browser.provider, name: resolved.browser.name } } + if (resolved.coverage.enabled && resolved.coverage.provider === 'v8') { throw new Error( `@vitest/coverage-v8 does not work with\n${JSON.stringify(browserConfig, null, 2)}\n` + `\nUse either:\n${JSON.stringify({ browser: { provider: 'playwright', name: 'chromium' } }, null, 2)}` + `\n\n...or change your coverage provider to:\n${JSON.stringify({ coverage: { provider: 'istanbul' } }, null, 2)}\n`, ) } + + if (resolved.inspect || resolved.inspectBrk) { + const inspectOption = `--inspect${resolved.inspectBrk ? '-brk' : ''}` + + throw new Error( + `${inspectOption} does not work with\n${JSON.stringify(browserConfig, null, 2)}\n` + + `\nUse either:\n${JSON.stringify({ browser: { provider: 'playwright', name: 'chromium' } }, null, 2)}` + + `\n\n...or disable ${inspectOption}\n`, + ) + } } resolved.coverage.reporter = resolveCoverageReporters(resolved.coverage.reporter) diff --git a/test/config/test/failures.test.ts b/test/config/test/failures.test.ts index b633229a8eff..bfc4517fca83 100644 --- a/test/config/test/failures.test.ts +++ b/test/config/test/failures.test.ts @@ -5,6 +5,10 @@ import { version } from 'vitest/package.json' import { normalize, resolve } from 'pathe' import * as testUtils from '../../test-utils' +const providers = ['playwright', 'webdriverio', 'preview'] as const +const names = ['edge', 'chromium', 'webkit', 'chrome', 'firefox', 'safari'] as const +const browsers = providers.map(provider => names.map(name => ({ name, provider }))).flat() + function runVitest(config: NonNullable & { shard?: any }) { return testUtils.runVitest({ root: './fixtures/test', ...config }, []) } @@ -63,20 +67,17 @@ test('inspect-brk cannot be used with multi processing', async () => { expect(stderr).toMatch('Error: You cannot use --inspect without "--no-file-parallelism", "poolOptions.threads.singleThread" or "poolOptions.forks.singleFork"') }) -test('v8 coverage provider throws when not playwright + chromium', async () => { - const providers = ['playwright', 'webdriverio', 'preview'] - const names = ['edge', 'chromium', 'webkit', 'chrome', 'firefox', 'safari'] +test('inspect and --inspect-brk cannot be used when not playwright + chromium', async () => { + for (const option of ['inspect', 'inspectBrk']) { + const cli = `--inspect${option === 'inspectBrk' ? '-brk' : ''}` - for (const provider of providers) { - for (const name of names) { + for (const { provider, name } of browsers) { if (provider === 'playwright' && name === 'chromium') { continue } const { stderr } = await runVitest({ - coverage: { - enabled: true, - }, + [option]: true, browser: { enabled: true, provider, @@ -85,6 +86,47 @@ test('v8 coverage provider throws when not playwright + chromium', async () => { }) expect(stderr).toMatch( +`Error: ${cli} does not work with +{ + "browser": { + "provider": "${provider}", + "name": "${name}" + } +} + +Use either: +{ + "browser": { + "provider": "playwright", + "name": "chromium" + } +} + +...or disable ${cli} +`, + ) + } + } +}) + +test('v8 coverage provider throws when not playwright + chromium', async () => { + for (const { provider, name } of browsers) { + if (provider === 'playwright' && name === 'chromium') { + continue + } + + const { stderr } = await runVitest({ + coverage: { + enabled: true, + }, + browser: { + enabled: true, + provider, + name, + }, + }) + + expect(stderr).toMatch( `Error: @vitest/coverage-v8 does not work with { "browser": { @@ -108,8 +150,7 @@ Use either: } } `, - ) - } + ) } })