Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix location for test.each #10413

Merged
merged 12 commits into from Sep 14, 2020
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -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))

Expand Down
45 changes: 32 additions & 13 deletions e2e/__tests__/locationInResults.test.ts
Expand Up @@ -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', () => {
Expand All @@ -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,
Expand All @@ -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,
});
});
8 changes: 8 additions & 0 deletions e2e/location-in-results/__tests__/test.js
Expand Up @@ -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();
Expand All @@ -33,4 +37,8 @@ describe('nested', () => {
fit('fit nested', () => {
expect(true).toBeTruthy();
});

it.each([true, true])('it each nested', () => {
expect(true).toBeTruthy();
});
});
12 changes: 10 additions & 2 deletions packages/jest-circus/src/utils.ts
Expand Up @@ -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';
Expand All @@ -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,
Expand Down Expand Up @@ -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' &&
Expand Down
56 changes: 25 additions & 31 deletions packages/jest-jasmine2/src/index.ts
Expand Up @@ -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,
Expand All @@ -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<T extends Global.ItBase>(original: T): T {
const wrapped = (
testName: Global.TestName,
fn: Global.TestFn,
timeout?: number,
) => {
SimenB marked this conversation as resolved.
Show resolved Hide resolved
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);
Expand Down