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 = `${error.name}: ${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 = `${error.name}: ${ 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('

') if (errors?.length) { result.htmlError = errors } return t }) }