111 lines
2.5 KiB
TypeScript

import type Convert from 'ansi-to-html'
import type { RunnerTask, TestError } from 'vitest'
import { parseStacktrace } from '@vitest/utils/source-map'
import Filter from 'ansi-to-html'
import { escapeHtml } from '~/utils/escape'
declare module '@vitest/runner' {
interface TaskResult {
htmlError?: string
}
}
export function isTestFile(name: string, fileName?: string) {
return fileName && name.endsWith(fileName)
}
export async function openInEditor(name: string, line: number, column: number) {
const url = encodeURI(`${name}:${line}:${column}`)
await fetch(`/__open-in-editor?file=${url}`)
}
export function createAnsiToHtmlFilter(dark: boolean) {
return new Filter({
fg: dark ? '#FFF' : '#000',
bg: dark ? '#000' : '#FFF',
})
}
function isPrimitive(value: unknown) {
return (
value === null || (typeof value !== 'function' && typeof value !== 'object')
)
}
export function parseError(e: unknown) {
let error = e as TestError
if (isPrimitive(e)) {
error = {
message: String(error).split(/\n/g)[0],
stack: String(error),
name: '',
stacks: [],
}
}
if (!e) {
const err = new Error('unknown error')
error = {
message: err.message,
stack: err.stack,
name: '',
stacks: [],
}
}
error.stacks = parseStacktrace(error.stack || '', {
ignoreStackEntries: [],
})
return error
}
function createHtmlError(filter: Convert, error: TestError) {
let htmlError = ''
if (error.message?.includes('\x1B')) {
htmlError = `<b>${error.name}</b>: ${filter.toHtml(
escapeHtml(error.message),
)}`
}
const startStrWithX1B = error.stack?.includes('\x1B')
if (startStrWithX1B) {
if (htmlError.length > 0) {
htmlError += filter.toHtml(
escapeHtml((error.stack) as string),
)
}
else {
htmlError = `<b>${error.name}</b>: ${
error.message
}${filter.toHtml(
escapeHtml((error.stack) as string),
)}`
}
}
if (htmlError.length > 0) {
return htmlError
}
return null
}
export function mapLeveledTaskStacks(dark: boolean, tasks: RunnerTask[]) {
const filter = createAnsiToHtmlFilter(dark)
return tasks.map((t) => {
const result = t.result
if (!result || result.htmlError) {
return t
}
const errors = result.errors
?.map(error => createHtmlError(filter, error))
.filter(error => error != null)
.join('<br><br>')
if (errors?.length) {
result.htmlError = errors
}
return t
})
}