vitest/test/browser/specs/inspect.test.ts

114 lines
3.2 KiB
TypeScript

import type { InspectorNotification } from 'node:inspector'
import { expect, test, vi } from 'vitest'
import WebSocket from 'ws'
import { runVitestCli } from '../../test-utils'
type Message = Partial<InspectorNotification<any>>
const REMOTE_DEBUG_URL = '127.0.0.1:9123'
test.each(['', 'with workspace'])('--inspect-brk stops at test file %s', async (isWorkspace) => {
const options = ['--root', 'fixtures/inspect', '--no-file-parallelism', '--inspect-brk', REMOTE_DEBUG_URL]
if (isWorkspace) {
options.push('--config')
options.push('vitest.config.with-workspace.ts')
}
const { vitest, waitForClose } = await runVitestCli(...options)
await vitest.waitForStdout(`Debugger listening on ws://${REMOTE_DEBUG_URL}`)
const url = await vi.waitFor(() => fetch(`http://${REMOTE_DEBUG_URL}/json/list`)
.then(response => response.json())
.then(json => json[0].webSocketDebuggerUrl), { timeout: 30_000 })
const { receive, send } = await createChannel(url)
const paused = receive('Debugger.paused')
send({ method: 'Debugger.enable' })
send({ method: 'Runtime.enable' })
await receive('Runtime.executionContextCreated')
send({ method: 'Runtime.runIfWaitingForDebugger' })
const { params } = await paused
const scriptId = params.callFrames[0].functionLocation.scriptId
// Verify that debugger paused on test file
const { result } = await send({ method: 'Debugger.getScriptSource', params: { scriptId } })
expect(result.scriptSource).toContain('test("sum", () => {')
expect(result.scriptSource).toContain('expect(1 + 1).toBe(2)')
send({ method: 'Debugger.resume' })
await vitest.waitForStdout('Test Files 1 passed (1)')
await waitForClose()
}, 60_000)
async function createChannel(url: string) {
const ws = new WebSocket(url)
let id = 1
let listeners = []
ws.onmessage = (message) => {
const response = JSON.parse(message.data.toString())
listeners.forEach(listener => listener(response))
}
async function receive(methodOrId?: string | { id: number }): Promise<Message> {
const { promise, resolve, reject } = withResolvers()
listeners.push(listener)
ws.onerror = reject
function listener(message) {
const filter = typeof methodOrId === 'string' ? { method: methodOrId } : { id: methodOrId.id }
const methodMatch = message.method && message.method === filter.method
const idMatch = message.id && message.id === filter.id
if (methodMatch || idMatch) {
resolve(message)
listeners = listeners.filter(l => l !== listener)
ws.onerror = undefined
}
else if (!filter.id && !filter.method) {
resolve(message)
}
}
return promise
}
async function send(message: Message): Promise<any> {
const currentId = id++
const json = JSON.stringify({ ...message, id: currentId })
const receiver = receive({ id: currentId })
ws.send(json)
return receiver
}
await new Promise((resolve, reject) => {
ws.onerror = reject
ws.on('open', resolve)
})
return { receive, send }
}
function withResolvers() {
let reject: (error: unknown) => void
let resolve: (response: Message) => void
const promise: Promise<Message> = new Promise((...args) => {
[resolve, reject] = args
})
return { promise, resolve, reject }
}