Skip to content

Commit

Permalink
feat: allow custom host for --inspect and --inspect-brk (#5509)
Browse files Browse the repository at this point in the history
  • Loading branch information
sheremet-va committed Apr 8, 2024
1 parent 672a1ed commit 6157282
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 15 deletions.
4 changes: 2 additions & 2 deletions docs/guide/cli.md
Expand Up @@ -96,8 +96,8 @@ Run only [benchmark](https://vitest.dev/guide/features.html#benchmarking-experim
| `--shard <shard>` | Execute tests in a specified shard |
| `--sequence` | Define in what order to run tests. Use [cac's dot notation] to specify options (for example, use `--sequence.shuffle` to run tests in random order or `--sequence.shuffle --sequence.seed SEED_ID` to run a specific order) |
| `--no-color` | Removes colors from the console output |
| `--inspect` | Enables Node.js inspector |
| `--inspect-brk` | Enables Node.js inspector with break |
| `--inspect [[host:]port]` | Enable Node.js inspector (default: 127.0.0.1:9229) |
| `--inspect-brk [[host:]port]` | Enables Node.js inspector and break before the test starts |
| `--bail <number>` | Stop test execution when given number of tests have failed |
| `--retry <times>` | Retry the test specific number of times if it fails |
| `--exclude <glob>` | Additional file globs to be excluded from test |
Expand Down
1 change: 1 addition & 0 deletions packages/vitest/src/constants.ts
@@ -1,6 +1,7 @@
// if changed, update also jsdocs and docs
export const defaultPort = 51204
export const defaultBrowserPort = 63315
export const defaultInspectPort = 9229

export const EXIT_CODE_RESTART = 43

Expand Down
25 changes: 22 additions & 3 deletions packages/vitest/src/node/cli/cli-config.ts
Expand Up @@ -308,7 +308,9 @@ export const cliOptionsConfig: VitestCLIOptions = {
if (typeof browser === 'boolean')
return { enabled: browser }
if (browser === 'true' || browser === 'false')
return { enabled: browser !== 'false' }
return { enabled: browser === 'true' }
if (browser === 'yes' || browser === 'no')
return { enabled: browser === 'yes' }
if (typeof browser === 'string')
return { enabled: true, name: browser }
return browser
Expand Down Expand Up @@ -465,11 +467,28 @@ export const cliOptionsConfig: VitestCLIOptions = {
},
},
inspect: {
description: 'Enable Node.js inspector',
description: 'Enable Node.js inspector (default: 127.0.0.1:9229)',
argument: '[[host:]port]',
transform(portOrEnabled) {
if (portOrEnabled === 0 || portOrEnabled === 'true' || portOrEnabled === 'yes')
return true
if (portOrEnabled === 'false' || portOrEnabled === 'no')
return false
return portOrEnabled
},
},
inspectBrk: {
description: 'Enable Node.js inspector with break',
description: 'Enable Node.js inspector and break before the test starts',
argument: '[[host:]port]',
transform(portOrEnabled) {
if (portOrEnabled === 0 || portOrEnabled === 'true' || portOrEnabled === 'yes')
return true
if (portOrEnabled === 'false' || portOrEnabled === 'no')
return false
return portOrEnabled
},
},
inspector: null,
testTimeout: {
description: 'Default timeout of a test in milliseconds (default: 5000)',
argument: '<timeout>',
Expand Down
27 changes: 24 additions & 3 deletions packages/vitest/src/node/config.ts
Expand Up @@ -3,7 +3,7 @@ import { normalize, relative, resolve } from 'pathe'
import c from 'picocolors'
import type { ResolvedConfig as ResolvedViteConfig } from 'vite'
import type { ApiConfig, ResolvedConfig, UserConfig, VitestRunMode } from '../types'
import { defaultBrowserPort, defaultPort, extraInlineDeps } from '../constants'
import { defaultBrowserPort, defaultInspectPort, defaultPort, extraInlineDeps } from '../constants'
import { benchmarkConfigDefaults, configDefaults } from '../defaults'
import { isCI, stdProvider, toArray } from '../utils'
import type { BuiltinPool } from '../types/pool-options'
Expand All @@ -20,6 +20,21 @@ function resolvePath(path: string, root: string) {
)
}

function parseInspector(inspect: string | undefined | boolean | number) {
if (typeof inspect === 'boolean' || inspect === undefined)
return {}
if (typeof inspect === 'number')
return { port: inspect }

if (inspect.match(/https?:\//))
throw new Error(`Inspector host cannot be a URL. Use "host:port" instead of "${inspect}"`)

const [host, port] = inspect.split(':')
if (!port)
return { host }
return { host, port: Number(port) || defaultInspectPort }
}

export function resolveApiServerConfig<Options extends ApiConfig & UserConfig>(
options: Options,
): ApiConfig | undefined {
Expand Down Expand Up @@ -88,8 +103,14 @@ export function resolveConfig(
mode,
} as any as ResolvedConfig

resolved.inspect = Boolean(resolved.inspect)
resolved.inspectBrk = Boolean(resolved.inspectBrk)
const inspector = resolved.inspect || resolved.inspectBrk

resolved.inspector = {
...resolved.inspector,
...parseInspector(inspector),
enabled: !!inspector,
waitForDebugger: options.inspector?.waitForDebugger ?? !!resolved.inspectBrk,
}

if (viteConfig.base !== '/')
resolved.base = viteConfig.base
Expand Down
1 change: 1 addition & 0 deletions packages/vitest/src/node/workspace.ts
Expand Up @@ -410,6 +410,7 @@ export class WorkspaceProject {
},
inspect: this.ctx.config.inspect,
inspectBrk: this.ctx.config.inspectBrk,
inspector: this.ctx.config.inspector,
alias: [],
includeTaskLocation: this.config.includeTaskLocation ?? this.ctx.config.includeTaskLocation,
env: {
Expand Down
9 changes: 6 additions & 3 deletions packages/vitest/src/runtime/inspector.ts
Expand Up @@ -12,18 +12,21 @@ let session: InstanceType<typeof inspector.Session>
*/
export function setupInspect(ctx: ContextRPC) {
const config = ctx.config
const isEnabled = config.inspect || config.inspectBrk
const isEnabled = config.inspector.enabled

if (isEnabled) {
inspector = __require('node:inspector')
// Inspector may be open already if "isolate: false" is used
const isOpen = inspector.url() !== undefined

if (!isOpen) {
inspector.open()
inspector.open(
config.inspector.port,
config.inspector.host,
config.inspector.waitForDebugger,
)

if (config.inspectBrk) {
inspector.waitForDebugger()
const firstTestFile = ctx.files[0]

// Stop at first test file
Expand Down
26 changes: 24 additions & 2 deletions packages/vitest/src/types/config.ts
Expand Up @@ -675,15 +675,37 @@ export interface InlineConfig {
*
* Requires `poolOptions.threads.singleThread: true` OR `poolOptions.forks.singleFork: true`.
*/
inspect?: boolean
inspect?: boolean | string

/**
* Debug tests by opening `node:inspector` in worker / child process and wait for debugger to connect.
* Provides similar experience as `--inspect-brk` Node CLI argument.
*
* Requires `poolOptions.threads.singleThread: true` OR `poolOptions.forks.singleFork: true`.
*/
inspectBrk?: boolean
inspectBrk?: boolean | string

/**
* Inspector options. If `--inspect` or `--inspect-brk` is enabled, these options will be passed to the inspector.
*/
inspector?: {
/**
* Enable inspector
*/
enabled?: boolean
/**
* Port to run inspector on
*/
port?: number
/**
* Host to run inspector on
*/
host?: string
/**
* Wait for debugger to connect before running tests
*/
waitForDebugger?: boolean
}

/**
* Modify default Chai config. Vitest uses Chai for `expect` and `assert` matches.
Expand Down
51 changes: 49 additions & 2 deletions test/config/test/resolution.test.ts
@@ -1,7 +1,7 @@
import type { UserConfig } from 'vitest'
import type { UserConfig as ViteUserConfig } from 'vite'
import { describe, expect, it } from 'vitest'
import { createVitest } from 'vitest/node'
import { describe, expect, it, onTestFinished, vi } from 'vitest'
import { createVitest, parseCLI } from 'vitest/node'
import { extraInlineDeps } from 'vitest/config'

async function vitest(cliOptions: UserConfig, configValue: UserConfig = {}, viteConfig: ViteUserConfig = {}) {
Expand Down Expand Up @@ -263,3 +263,50 @@ describe('correctly defines api flag', () => {
})
})
})

describe.each([
'--inspect',
'--inspect-brk',
])('correctly parses %s flags', (inspectFlagName) => {
it.each([
['', { enabled: true }],
['true', { enabled: true }],
['yes', { enabled: true }],
['false', { enabled: false }],
['no', { enabled: false }],

['1002', { enabled: true, port: 1002 }],
['www.remote.com:1002', { enabled: true, port: 1002, host: 'www.remote.com' }],
['www.remote.com', { enabled: true, host: 'www.remote.com' }],
])(`parses "vitest ${inspectFlagName} %s" value`, async (cliValue, inspect) => {
const rawConfig = parseCLI(
`vitest --no-file-parallelism ${inspectFlagName} ${cliValue}`,
)
const c = await config(rawConfig.options)
expect(c.inspector).toEqual({
...inspect,
waitForDebugger: inspectFlagName === '--inspect-brk' && inspect.enabled,
})
})
it('cannot use a URL', async () => {
const url = 'https://www.remote.com:1002'
const rawConfig = parseCLI([
'vitest',
'--no-file-parallelism',
inspectFlagName,
url,
])
const error = vi.fn()
const originalError = console.error
console.error = error
onTestFinished(() => {
console.error = originalError
})
await expect(async () => {
await config(rawConfig.options)
}).rejects.toThrowError()
expect(error.mock.lastCall[0]).toEqual(
expect.stringContaining(`Inspector host cannot be a URL. Use "host:port" instead of "${url}"`),
)
})
})

0 comments on commit 6157282

Please sign in to comment.