Skip to content

Commit

Permalink
fix: Preserve order of jest calls when hoisting (#10536)
Browse files Browse the repository at this point in the history
  • Loading branch information
tgriesser committed Oct 29, 2020
1 parent fe2032c commit bce0b8a
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

### Fixes

- `[babel-plugin-jest-hoist]` Preserve order of hoisted mock nodes within containing block ([#10536](https://github.com/facebook/jest/pull/10536))
- `[jest-config]` Fix bug introduced in watch mode by PR[#10678](https://github.com/facebook/jest/pull/10678/files#r511037803) ([#10692](https://github.com/facebook/jest/pull/10692))
- `[expect]` Stop modifying the sample in `expect.objectContaining()` ([#10711](https://github.com/facebook/jest/pull/10711))
- `[jest-circus, jest-jasmine2]` fix: don't assume `stack` is always a string ([#10697](https://github.com/facebook/jest/pull/10697))
Expand Down
8 changes: 8 additions & 0 deletions e2e/babel-plugin-jest-hoist/__test_modules__/g.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

export default () => 'unmocked';
2 changes: 1 addition & 1 deletion e2e/babel-plugin-jest-hoist/__tests__/integration.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ myObject.mock('apple', 27);

// Variable names prefixed with `mock` (ignore case) should not throw as out-of-scope
const MockMethods = () => {};
jest.mock('../__test_modules__/f', () => MockMethods);
jest.mock('../__test_modules__/g', () => MockMethods);

describe('babel-plugin-jest-hoist', () => {
it('does not throw during transform', () => {
Expand Down
3 changes: 2 additions & 1 deletion packages/babel-plugin-jest-hoist/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
},
"devDependencies": {
"@types/babel__template": "^7.0.2",
"@types/node": "*"
"@types/node": "*",
"babel-plugin-tester": "9.2.0"
},
"publishConfig": {
"access": "public"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`babel-plugin-jest-hoist top level mocking: top level mocking 1`] = `
require('x');
jest.enableAutomock();
jest.disableAutomock();
↓ ↓ ↓ ↓ ↓ ↓
_getJestObj().enableAutomock();
_getJestObj().disableAutomock();
function _getJestObj() {
const { jest } = require("@jest/globals");
_getJestObj = () => jest;
return jest;
}
require("x");
`;

exports[`babel-plugin-jest-hoist within a block with no siblings: within a block with no siblings 1`] = `
beforeEach(() => {
jest.mock('someNode')
})
↓ ↓ ↓ ↓ ↓ ↓
function _getJestObj() {
const { jest } = require("@jest/globals");
_getJestObj = () => jest;
return jest;
}
beforeEach(() => {
_getJestObj().mock("someNode");
});
`;

exports[`babel-plugin-jest-hoist within a block: within a block 1`] = `
beforeEach(() => {
require('x')
jest.mock('someNode')
})
↓ ↓ ↓ ↓ ↓ ↓
function _getJestObj() {
const { jest } = require("@jest/globals");
_getJestObj = () => jest;
return jest;
}
beforeEach(() => {
_getJestObj().mock("someNode");
require("x");
});
`;
43 changes: 43 additions & 0 deletions packages/babel-plugin-jest-hoist/src/__tests__/hoistPlugin.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import pluginTester from 'babel-plugin-tester';
import babelPluginJestHoist from '..';

pluginTester({
plugin: babelPluginJestHoist,
pluginName: 'babel-plugin-jest-hoist',
tests: {
'top level mocking': {
code: `
require('x');
jest.enableAutomock();
jest.disableAutomock();
`,
snapshot: true,
},
'within a block': {
code: `
beforeEach(() => {
require('x')
jest.mock('someNode')
})
`,
snapshot: true,
},
'within a block with no siblings': {
code: `
beforeEach(() => {
jest.mock('someNode')
})
`,
snapshot: true,
},
},
});
34 changes: 28 additions & 6 deletions packages/babel-plugin-jest-hoist/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@

import type {NodePath} from '@babel/traverse';
import {
BlockStatement,
CallExpression,
Expression,
Identifier,
Node,
Program,
callExpression,
emptyStatement,
isIdentifier,
} from '@babel/types';
import {statement} from '@babel/template';
Expand Down Expand Up @@ -113,7 +116,7 @@ FUNCTIONS.mock = args => {

const ids: Set<NodePath<Identifier>> = new Set();
const parentScope = moduleFactory.parentPath.scope;
// @ts-expect-error: ReferencedIdentifier is not known on visitors
// @ts-expect-error: ReferencedIdentifier and blacklist are not known on visitors
moduleFactory.traverse(IDVisitor, {ids});
for (const id of ids) {
const {name} = id.node;
Expand Down Expand Up @@ -281,14 +284,33 @@ export default (): PluginObj<{
},
// in `post` to make sure we come after an import transform and can unshift above the `require`s
post({path: program}: {path: NodePath<Program>}) {
const self = this;
visitBlock(program);
program.traverse({
CallExpression: callExpr => {
BlockStatement: visitBlock,
});

function visitBlock(block: NodePath<BlockStatement> | NodePath<Program>) {
// use a temporary empty statement instead of the real first statement, which may itself be hoisted
const [firstNonHoistedStatementOfBlock] = block.unshiftContainer(
'body',
emptyStatement(),
);
block.traverse({
CallExpression: visitCallExpr,
// do not traverse into nested blocks, or we'll hoist calls in there out to this block
// @ts-expect-error blacklist is not known
blacklist: ['BlockStatement'],
});
firstNonHoistedStatementOfBlock.remove();

function visitCallExpr(callExpr: NodePath<CallExpression>) {
const {
node: {callee},
} = callExpr;
if (
isIdentifier(callee) &&
callee.name === this.jestObjGetterIdentifier?.name
callee.name === self.jestObjGetterIdentifier?.name
) {
const mockStmt = callExpr.getStatementParent();

Expand All @@ -297,12 +319,12 @@ export default (): PluginObj<{
const mockStmtParent = mockStmt.parentPath;
if (mockStmtParent.isBlock()) {
mockStmt.remove();
mockStmtParent.unshiftContainer('body', [mockStmtNode]);
firstNonHoistedStatementOfBlock.insertBefore(mockStmtNode);
}
}
}
},
});
}
}
},
});
/* eslint-enable */
43 changes: 41 additions & 2 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3251,6 +3251,16 @@ __metadata:
languageName: node
linkType: hard

"@types/babel-plugin-tester@npm:^9.0.0":
version: 9.0.0
resolution: "@types/babel-plugin-tester@npm:9.0.0"
dependencies:
"@types/babel__core": "*"
"@types/prettier": "*"
checksum: d6b465ef6ce980927dd3b10ae0a39fd8384688ba2d15bf2a30ac4ae567572abb2768992e7c2389f873c87c8d9ba5eafe88feb399e1e9e93f3cbfc5fe27da7077
languageName: node
linkType: hard

"@types/babel-types@npm:*":
version: 7.0.9
resolution: "@types/babel-types@npm:7.0.9"
Expand All @@ -3265,7 +3275,7 @@ __metadata:
languageName: node
linkType: hard

"@types/babel__core@npm:^7.0.0, @types/babel__core@npm:^7.0.4, @types/babel__core@npm:^7.1.0, @types/babel__core@npm:^7.1.7":
"@types/babel__core@npm:*, @types/babel__core@npm:^7.0.0, @types/babel__core@npm:^7.0.4, @types/babel__core@npm:^7.1.0, @types/babel__core@npm:^7.1.7":
version: 7.1.7
resolution: "@types/babel__core@npm:7.1.7"
dependencies:
Expand Down Expand Up @@ -3638,6 +3648,13 @@ __metadata:
languageName: node
linkType: hard

"@types/prettier@npm:*":
version: 2.1.1
resolution: "@types/prettier@npm:2.1.1"
checksum: 3671bedc845a0e61bb8eb698746e1f6d1201ac784f95c536cd653c1406a51c0e9c338ecbbc73f1b5fd5fe0b0af98edf7e85428810357d959355ab46b3a63ebe6
languageName: node
linkType: hard

"@types/prettier@npm:^2.0.0":
version: 2.1.5
resolution: "@types/prettier@npm:2.1.5"
Expand Down Expand Up @@ -4813,6 +4830,7 @@ __metadata:
"@types/babel__template": ^7.0.2
"@types/babel__traverse": ^7.0.6
"@types/node": "*"
babel-plugin-tester: 9.2.0
languageName: unknown
linkType: soft

Expand All @@ -4830,6 +4848,20 @@ __metadata:
languageName: node
linkType: hard

"babel-plugin-tester@npm:9.2.0":
version: 9.2.0
resolution: "babel-plugin-tester@npm:9.2.0"
dependencies:
"@types/babel-plugin-tester": ^9.0.0
lodash.mergewith: ^4.6.2
prettier: ^2.0.1
strip-indent: ^3.0.0
peerDependencies:
"@babel/core": ^7.9.0
checksum: ce247d30010fc4e7f28733187a6b347f292226078a72c58c399d2f30f5ec2a054cdfe81831d3455292a58ab3db3df35cae1db8a44234a35466090b90075a619c
languageName: node
linkType: hard

"babel-plugin-transform-typescript-metadata@npm:*":
version: 0.3.1
resolution: "babel-plugin-transform-typescript-metadata@npm:0.3.1"
Expand Down Expand Up @@ -12841,6 +12873,13 @@ fsevents@^1.2.7:
languageName: node
linkType: hard

"lodash.mergewith@npm:^4.6.2":
version: 4.6.2
resolution: "lodash.mergewith@npm:4.6.2"
checksum: 3561b63cebc629721ab4c016627fc54929ee33cdef1854b4a15ade71dd8eb5f2fc602830efe5395aed41c607d65e2cce356667116aa7156b82468594b42ab95f
languageName: node
linkType: hard

"lodash.padstart@npm:^4.6.1":
version: 4.6.1
resolution: "lodash.padstart@npm:4.6.1"
Expand Down Expand Up @@ -15865,7 +15904,7 @@ fsevents@^1.2.7:
languageName: node
linkType: hard

"prettier@npm:^2.1.1":
"prettier@npm:^2.0.1, prettier@npm:^2.1.1":
version: 2.1.2
resolution: "prettier@npm:2.1.2"
bin:
Expand Down

0 comments on commit bce0b8a

Please sign in to comment.