forked from vitest-dev/vitest
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: show browser console in the terminal (vitest-dev#3048)
- Loading branch information
1 parent
a60bba0
commit d082eaa
Showing
21 changed files
with
356 additions
and
90 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import { rpc } from './rpc' | ||
import { importId } from './utils' | ||
|
||
const { Date, console } = globalThis | ||
|
||
export const setupConsoleLogSpy = async () => { | ||
const { stringify, format, utilInspect } = await importId('vitest/utils') as typeof import('vitest/utils') | ||
const { log, info, error, dir, dirxml, trace, time, timeEnd, timeLog, warn, debug, count, countReset } = console | ||
const formatInput = (input: unknown) => { | ||
if (input instanceof Node) | ||
return stringify(input) | ||
return format(input) | ||
} | ||
const processLog = (args: unknown[]) => args.map(formatInput).join(' ') | ||
const sendLog = (type: 'stdout' | 'stderr', content: string) => { | ||
if (content.startsWith('[vite]')) | ||
return | ||
const unknownTestId = '__vitest__unknown_test__' | ||
// @ts-expect-error untyped global | ||
const taskId = globalThis.__vitest_worker__?.current?.id ?? unknownTestId | ||
rpc().sendLog({ | ||
content, | ||
time: Date.now(), | ||
taskId, | ||
type, | ||
size: content.length, | ||
}) | ||
} | ||
const stdout = (base: (...args: unknown[]) => void) => (...args: unknown[]) => { | ||
sendLog('stdout', processLog(args)) | ||
return base(...args) | ||
} | ||
const stderr = (base: (...args: unknown[]) => void) => (...args: unknown[]) => { | ||
sendLog('stderr', processLog(args)) | ||
return base(...args) | ||
} | ||
console.log = stdout(log) | ||
console.debug = stdout(debug) | ||
console.info = stdout(info) | ||
|
||
console.error = stderr(error) | ||
console.warn = stderr(warn) | ||
|
||
console.dir = (item, options) => { | ||
sendLog('stdout', utilInspect(item, options)) | ||
return dir(item, options) | ||
} | ||
|
||
console.dirxml = (...args) => { | ||
sendLog('stdout', processLog(args)) | ||
return dirxml(...args) | ||
} | ||
|
||
console.trace = (...args: unknown[]) => { | ||
const content = processLog(args) | ||
const error = new Error('Trace') | ||
const stack = (error.stack || '').split('\n').slice(2).join('\n') | ||
sendLog('stdout', `${content}\n${stack}`) | ||
return trace(...args) | ||
} | ||
|
||
const timeLabels: Record<string, number> = {} | ||
|
||
console.time = (label = 'default') => { | ||
const now = performance.now() | ||
time(label) | ||
timeLabels[label] = now | ||
} | ||
|
||
console.timeLog = (label = 'default') => { | ||
timeLog(label) | ||
if (!(label in timeLabels)) | ||
sendLog('stderr', `Timer "${label}" does not exist`) | ||
else | ||
sendLog('stdout', `${label}: ${timeLabels[label]} ms`) | ||
} | ||
|
||
console.timeEnd = (label = 'default') => { | ||
const end = performance.now() | ||
timeEnd(label) | ||
const start = timeLabels[label] | ||
if (!(label in timeLabels)) { | ||
sendLog('stderr', `Timer "${label}" does not exist`) | ||
} | ||
else if (start) { | ||
const duration = end - start | ||
sendLog('stdout', `${label}: ${duration} ms`) | ||
} | ||
} | ||
|
||
const countLabels: Record<string, number> = {} | ||
|
||
console.count = (label = 'default') => { | ||
const counter = (countLabels[label] ?? 0) + 1 | ||
countLabels[label] = counter | ||
sendLog('stdout', `${label}: ${counter}`) | ||
return count(label) | ||
} | ||
|
||
console.countReset = (label = 'default') => { | ||
countLabels[label] = 0 | ||
return countReset(label) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import type { | ||
getSafeTimers, | ||
} from '@vitest/utils' | ||
import type { VitestClient } from '@vitest/ws-client' | ||
|
||
const { get } = Reflect | ||
const safeRandom = Math.random | ||
|
||
function withSafeTimers(getTimers: typeof getSafeTimers, fn: () => void) { | ||
const { setTimeout, clearTimeout, nextTick, setImmediate, clearImmediate } = getTimers() | ||
|
||
const currentSetTimeout = globalThis.setTimeout | ||
const currentClearTimeout = globalThis.clearTimeout | ||
const currentRandom = globalThis.Math.random | ||
const currentNextTick = globalThis.process.nextTick | ||
const currentSetImmediate = globalThis.setImmediate | ||
const currentClearImmediate = globalThis.clearImmediate | ||
|
||
try { | ||
globalThis.setTimeout = setTimeout | ||
globalThis.clearTimeout = clearTimeout | ||
globalThis.Math.random = safeRandom | ||
globalThis.process.nextTick = nextTick | ||
globalThis.setImmediate = setImmediate | ||
globalThis.clearImmediate = clearImmediate | ||
|
||
const result = fn() | ||
return result | ||
} | ||
finally { | ||
globalThis.setTimeout = currentSetTimeout | ||
globalThis.clearTimeout = currentClearTimeout | ||
globalThis.Math.random = currentRandom | ||
globalThis.setImmediate = currentSetImmediate | ||
globalThis.clearImmediate = currentClearImmediate | ||
nextTick(() => { | ||
globalThis.process.nextTick = currentNextTick | ||
}) | ||
} | ||
} | ||
|
||
const promises = new Set<Promise<unknown>>() | ||
|
||
export const rpcDone = async () => { | ||
if (!promises.size) | ||
return | ||
const awaitable = Array.from(promises) | ||
return Promise.all(awaitable) | ||
} | ||
|
||
export const createSafeRpc = (client: VitestClient, getTimers: () => any): VitestClient['rpc'] => { | ||
return new Proxy(client.rpc, { | ||
get(target, p, handler) { | ||
const sendCall = get(target, p, handler) | ||
const safeSendCall = (...args: any[]) => withSafeTimers(getTimers, async () => { | ||
const result = sendCall(...args) | ||
promises.add(result) | ||
try { | ||
return await result | ||
} | ||
finally { | ||
promises.delete(result) | ||
} | ||
}) | ||
safeSendCall.asEvent = sendCall.asEvent | ||
return safeSendCall | ||
}, | ||
}) | ||
} | ||
|
||
export const rpc = (): VitestClient['rpc'] => { | ||
// @ts-expect-error not typed global | ||
return globalThis.__vitest_worker__.safeRpc | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,26 +1,24 @@ | ||
import type { VitestClient } from '@vitest/ws-client' | ||
import { rpc } from './rpc' | ||
import type { SnapshotEnvironment } from '#types' | ||
|
||
export class BrowserSnapshotEnvironment implements SnapshotEnvironment { | ||
constructor(private client: VitestClient) {} | ||
|
||
readSnapshotFile(filepath: string): Promise<string | null> { | ||
return this.client.rpc.readFile(filepath) | ||
return rpc().readFile(filepath) | ||
} | ||
|
||
saveSnapshotFile(filepath: string, snapshot: string): Promise<void> { | ||
return this.client.rpc.writeFile(filepath, snapshot) | ||
return rpc().writeFile(filepath, snapshot) | ||
} | ||
|
||
resolvePath(filepath: string): Promise<string> { | ||
return this.client.rpc.resolveSnapshotPath(filepath) | ||
return rpc().resolveSnapshotPath(filepath) | ||
} | ||
|
||
removeSnapshotFile(filepath: string): Promise<void> { | ||
return this.client.rpc.removeFile(filepath) | ||
return rpc().removeFile(filepath) | ||
} | ||
|
||
async prepareDirectory(filepath: string): Promise<void> { | ||
await this.client.rpc.createDirectory(filepath) | ||
await rpc().createDirectory(filepath) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export const importId = (id: string) => { | ||
const name = `/@id/${id}` | ||
return import(name) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.