From ac73de8989ca06675858b4c8b4f3e6f864a071f0 Mon Sep 17 00:00:00 2001 From: Cameron Little Date: Mon, 14 Sep 2020 21:41:27 +0200 Subject: [PATCH] Fix location for `test.each` (#10413) --- CHANGELOG.md | 1 + e2e/__tests__/locationInResults.test.ts | 45 ++++++++++++------ e2e/location-in-results/__tests__/test.js | 8 ++++ packages/jest-circus/src/utils.ts | 12 ++++- packages/jest-jasmine2/src/index.ts | 56 ++++++++++------------- 5 files changed, 76 insertions(+), 46 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbbbe9f4a20f..97a12828567f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ### Fixes +- `[jest-circus, jest-jasmine2]` Find correct location for `test.each` tests ([#10413](https://github.com/facebook/jest/pull/10413)) - `[jest-console]` Add `Console` constructor to `console` object ([#10502](https://github.com/facebook/jest/pull/10502)) - `[jest-globals]` Fix lifecycle hook function types ([#10480](https://github.com/facebook/jest/pull/10480)) diff --git a/e2e/__tests__/locationInResults.test.ts b/e2e/__tests__/locationInResults.test.ts index e444ed7b6830..25808059c29c 100644 --- a/e2e/__tests__/locationInResults.test.ts +++ b/e2e/__tests__/locationInResults.test.ts @@ -13,13 +13,11 @@ it('defaults to null for location', () => { const assertions = result.testResults[0].assertionResults; expect(result.success).toBe(true); - expect(result.numTotalTests).toBe(6); - expect(assertions[0].location).toBeNull(); - expect(assertions[1].location).toBeNull(); - expect(assertions[2].location).toBeNull(); - expect(assertions[3].location).toBeNull(); - expect(assertions[4].location).toBeNull(); - expect(assertions[5].location).toBeNull(); + expect(result.numTotalTests).toBe(10); + expect(assertions).toHaveLength(10); + for (const assertion of assertions) { + expect(assertion.location).toBeNull(); + } }); it('adds correct location info when provided with flag', () => { @@ -29,7 +27,8 @@ it('adds correct location info when provided with flag', () => { const assertions = result.testResults[0].assertionResults; expect(result.success).toBe(true); - expect(result.numTotalTests).toBe(6); + expect(result.numTotalTests).toBe(10); + expect(assertions[0].location).toEqual({ column: 1, line: 12, @@ -45,20 +44,40 @@ it('adds correct location info when provided with flag', () => { line: 20, }); - // Technically the column should be 3, but callsites is not correct. - // jest-circus uses stack-utils + asyncErrors which resolves this. expect(assertions[3].location).toEqual({ - column: isJestCircusRun() ? 3 : 2, - line: 25, + column: isJestCircusRun() ? 1 : 22, + line: 24, }); expect(assertions[4].location).toEqual({ + column: isJestCircusRun() ? 1 : 22, + line: 24, + }); + + // Technically the column should be 3, but callsites is not correct. + // jest-circus uses stack-utils + asyncErrors which resolves this. + expect(assertions[5].location).toEqual({ column: isJestCircusRun() ? 3 : 2, line: 29, }); - expect(assertions[5].location).toEqual({ + expect(assertions[6].location).toEqual({ column: isJestCircusRun() ? 3 : 2, line: 33, }); + + expect(assertions[7].location).toEqual({ + column: isJestCircusRun() ? 3 : 2, + line: 37, + }); + + expect(assertions[8].location).toEqual({ + column: isJestCircusRun() ? 3 : 24, + line: 41, + }); + + expect(assertions[9].location).toEqual({ + column: isJestCircusRun() ? 3 : 24, + line: 41, + }); }); diff --git a/e2e/location-in-results/__tests__/test.js b/e2e/location-in-results/__tests__/test.js index dfc62f3a91fd..fa9c66a0a287 100644 --- a/e2e/location-in-results/__tests__/test.js +++ b/e2e/location-in-results/__tests__/test.js @@ -21,6 +21,10 @@ fit('fit no ancestors', () => { expect(true).toBeTruthy(); }); +it.each([true, true])('it each no ancestors', () => { + expect(true).toBeTruthy(); +}); + describe('nested', () => { it('it nested', () => { expect(true).toBeTruthy(); @@ -33,4 +37,8 @@ describe('nested', () => { fit('fit nested', () => { expect(true).toBeTruthy(); }); + + it.each([true, true])('it each nested', () => { + expect(true).toBeTruthy(); + }); }); diff --git a/packages/jest-circus/src/utils.ts b/packages/jest-circus/src/utils.ts index 58fab0d82286..b1e9f8aeb95d 100644 --- a/packages/jest-circus/src/utils.ts +++ b/packages/jest-circus/src/utils.ts @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. */ +import * as path from 'path'; import type {Circus} from '@jest/types'; import {convertDescriptorToString, formatTime} from 'jest-util'; import isGeneratorFn from 'is-generator-fn'; @@ -17,6 +18,8 @@ import {ROOT_DESCRIBE_BLOCK_NAME, getState} from './state'; const stackUtils = new StackUtils({cwd: 'A path that does not exist'}); +const jestEachBuildDir = path.dirname(require.resolve('jest-each')); + export const makeDescribe = ( name: Circus.BlockName, parent?: Circus.DescribeBlock, @@ -299,8 +302,13 @@ export const makeSingleTestResult = ( let location = null; if (includeTestLocationInResult) { - const stackLine = test.asyncError.stack.split('\n')[1]; - const parsedLine = stackUtils.parseLine(stackLine); + const stackLines = test.asyncError.stack.split('\n'); + const stackLine = stackLines[1]; + let parsedLine = stackUtils.parseLine(stackLine); + if (parsedLine?.file?.startsWith(jestEachBuildDir)) { + const stackLine = stackLines[4]; + parsedLine = stackUtils.parseLine(stackLine); + } if ( parsedLine && typeof parsedLine.column === 'number' && diff --git a/packages/jest-jasmine2/src/index.ts b/packages/jest-jasmine2/src/index.ts index b70e7a314cd6..36567220b120 100644 --- a/packages/jest-jasmine2/src/index.ts +++ b/packages/jest-jasmine2/src/index.ts @@ -22,6 +22,8 @@ import type {Jasmine as JestJasmine} from './types'; const JASMINE = require.resolve('./jasmine/jasmineLight'); +const jestEachBuildDir = path.dirname(require.resolve('jest-each')); + async function jasmine2( globalConfig: Config.GlobalConfig, config: Config.ProjectConfig, @@ -47,38 +49,30 @@ async function jasmine2( // TODO: Remove config option if V8 exposes some way of getting location of caller // in a future version if (config.testLocationInResults === true) { - const originalIt = environment.global.it; - environment.global.it = ((...args) => { - const stack = getCallsite(1, runtime.getSourceMaps()); - const it = originalIt(...args); - - // @ts-expect-error - it.result.__callsite = stack; - - return it; - }) as Global.Global['it']; - - const originalXit = environment.global.xit; - environment.global.xit = ((...args) => { - const stack = getCallsite(1, runtime.getSourceMaps()); - const xit = originalXit(...args); - - // @ts-expect-error - xit.result.__callsite = stack; - - return xit; - }) as Global.Global['xit']; - - const originalFit = environment.global.fit; - environment.global.fit = ((...args) => { - const stack = getCallsite(1, runtime.getSourceMaps()); - const fit = originalFit(...args); - - // @ts-expect-error - fit.result.__callsite = stack; + function wrapIt(original: T): T { + const wrapped = ( + testName: Global.TestName, + fn: Global.TestFn, + timeout?: number, + ) => { + const sourcemaps = runtime.getSourceMaps(); + let stack = getCallsite(1, sourcemaps); + const it = original(testName, fn, timeout); + + if (stack.getFileName()?.startsWith(jestEachBuildDir)) { + stack = getCallsite(4, sourcemaps); + } + // @ts-expect-error + it.result.__callsite = stack; + + return it; + }; + return (wrapped as any) as T; + } - return fit; - }) as Global.Global['fit']; + environment.global.it = wrapIt(environment.global.it); + environment.global.xit = wrapIt(environment.global.xit); + environment.global.fit = wrapIt(environment.global.fit); } jasmineAsyncInstall(globalConfig, environment.global);