From 235b9502f9d7a4e6922b1d542c6aac468b0148a2 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Fri, 10 Jun 2022 10:10:55 +0300 Subject: [PATCH] fix: can mock files with space in path (#1457) * fix: can mock files with space in path * test: testing files with spaces --- examples/mocks/src/has space in path.ts | 1 + examples/mocks/test/spaced.spec.ts | 7 +++ packages/vitest/src/utils/source-map.ts | 58 ++++++++++++++++++++----- 3 files changed, 54 insertions(+), 12 deletions(-) create mode 100644 examples/mocks/src/has space in path.ts create mode 100644 examples/mocks/test/spaced.spec.ts diff --git a/examples/mocks/src/has space in path.ts b/examples/mocks/src/has space in path.ts new file mode 100644 index 000000000000..9abd53e21612 --- /dev/null +++ b/examples/mocks/src/has space in path.ts @@ -0,0 +1 @@ +export const thisIsOk = false diff --git a/examples/mocks/test/spaced.spec.ts b/examples/mocks/test/spaced.spec.ts new file mode 100644 index 000000000000..7a03ff4ab9db --- /dev/null +++ b/examples/mocks/test/spaced.spec.ts @@ -0,0 +1,7 @@ +import { thisIsOk } from '../src/has space in path' + +vi.mock('../src/has space in path', () => ({ thisIsOk: true })) + +test('modules with spaces in name is mocked', () => { + expect(thisIsOk).toBe(true) +}) diff --git a/packages/vitest/src/utils/source-map.ts b/packages/vitest/src/utils/source-map.ts index 197be8649694..0e86b46c8b00 100644 --- a/packages/vitest/src/utils/source-map.ts +++ b/packages/vitest/src/utils/source-map.ts @@ -21,9 +21,6 @@ export function getOriginalPos(map: RawSourceMap | null | undefined, { line, col }) } -const stackFnCallRE = /at (.*) \((.+):(\d+):(\d+)\)$/ -const stackBarePathRE = /at ?(.*) (.+):(\d+):(\d+)$/ - export async function interpretSourcePos(stackFrames: ParsedStack[], ctx: Vitest): Promise { for (const frame of stackFrames) { if ('sourcePos' in frame) @@ -47,6 +44,18 @@ const stackIgnorePatterns = [ '/node_modules/tinyspy/', ] +function extractLocation(urlLike: string) { + // Fail-fast but return locations like "(native)" + if (!urlLike.includes(':')) + return [urlLike] + + const regExp = /(.+?)(?::(\d+))?(?::(\d+))?$/ + const parts = regExp.exec(urlLike.replace(/[()]/g, '')) + if (!parts) + return [urlLike] + return [parts[1], parts[2] || undefined, parts[3] || undefined] +} + export function parseStacktrace(e: ErrorWithDiff, full = false): ParsedStack[] { if (e.stacks) return e.stacks @@ -54,24 +63,49 @@ export function parseStacktrace(e: ErrorWithDiff, full = false): ParsedStack[] { const stackStr = e.stack || e.stackStr || '' const stackFrames = stackStr .split('\n') + // Based on https://github.com/stacktracejs/error-stack-parser + // Credit to stacktracejs .map((raw): ParsedStack | null => { - const line = raw.trim() - const match = line.match(stackFnCallRE) || line.match(stackBarePathRE) - if (!match) + let line = raw.trim() + + if (line.includes('(eval ')) + line = line.replace(/eval code/g, 'eval').replace(/(\(eval at [^()]*)|(,.*$)/g, '') + + let sanitizedLine = line + .replace(/^\s+/, '') + .replace(/\(eval code/g, '(') + .replace(/^.*?\s+/, '') + + // capture and preseve the parenthesized location "(/foo/my bar.js:12:87)" in + // case it has spaces in it, as the string is split on \s+ later on + const location = sanitizedLine.match(/ (\(.+\)$)/) + + // remove the parenthesized location from the line, if it was matched + sanitizedLine = location ? sanitizedLine.replace(location[0], '') : sanitizedLine + + // if a location was matched, pass it to extractLocation() otherwise pass all sanitizedLine + // because this line doesn't have function name + const [url, lineNumber, columnNumber] = extractLocation(location ? location[1] : sanitizedLine) + let method = (location && sanitizedLine) || '' + let file = url && ['eval', ''].includes(url) ? undefined : url + + if (!file || !lineNumber || !columnNumber) return null - let file = slash(match[2]) + if (method.startsWith('async ')) + method = method.slice(6) + if (file.startsWith('file://')) file = file.slice(7) - if (!full && stackIgnorePatterns.some(p => file.includes(p))) + if (!full && stackIgnorePatterns.some(p => file && file.includes(p))) return null return { - method: match[1], - file: match[2], - line: parseInt(match[3]), - column: parseInt(match[4]), + method, + file: slash(file), + line: parseInt(lineNumber), + column: parseInt(columnNumber), } }) .filter(notNullish)