diff --git a/e2e/__tests__/hoist-jest.test.ts b/e2e/__tests__/hoist-jest.test.ts index 213dbc44ec..454e0028ed 100644 --- a/e2e/__tests__/hoist-jest.test.ts +++ b/e2e/__tests__/hoist-jest.test.ts @@ -1,22 +1,46 @@ -import * as path from 'path' +import path from 'path' + +import execa from 'execa' import { json as runWithJson } from '../run-jest' import { runNpmInstall } from '../utils' -const DIR = path.resolve(__dirname, '..', 'hoist-jest') +const { createBundle } = require('../../scripts/lib/bundle') -beforeEach(() => { - runNpmInstall(DIR) -}) +const executeTest = (dirName: string): void => { + test(`successfully runs the tests with isolatedModules: false`, () => { + const { json } = runWithJson(dirName) + + expect(json.success).toBe(true) + }) + + test(`successfully runs the tests with isolatedModules true`, () => { + const { json } = runWithJson(dirName, ['-c=jest-isolated.config.js']) -test('successfully runs the tests inside `hoist-jest/` with isolatedModules: false', () => { - const { json } = runWithJson(DIR) + expect(json.success).toBe(true) + }) +} - expect(json.success).toBe(true) +describe('non-ts-factory', () => { + const DIR = path.resolve(__dirname, '..', 'hoist-jest', 'non-ts-factory') + + beforeAll(() => { + runNpmInstall(DIR) + const bundle = createBundle() + execa.sync('npm', ['install', '--no-package-lock', '--no-shrinkwrap', '--no-save', bundle], { + cwd: DIR, + }) + }) + + executeTest(DIR) }) -test('successfully runs the tests inside `hoist-jest/` with isolatedModules true', () => { - const { json } = runWithJson(DIR, ['-c=jest-isolated.config.js']) +describe('ts-factory', () => { + const DIR = path.resolve(__dirname, '..', 'hoist-jest', 'ts-factory') + + beforeAll(() => { + runNpmInstall(DIR) + }) - expect(json.success).toBe(true) + executeTest(DIR) }) diff --git a/e2e/hoist-jest/__tests__/import-jest.test.js b/e2e/hoist-jest/__tests__/import-jest.test.js index cc3eb62df9..25ba38794f 100644 --- a/e2e/hoist-jest/__tests__/import-jest.test.js +++ b/e2e/hoist-jest/__tests__/import-jest.test.js @@ -13,11 +13,10 @@ aliasedJest.unmock('../__test_modules__/b') JestGlobals.jest.unmock('../__test_modules__/c') // These will not be hoisted above imports -// TODO: This is a bug to fix -// { -// const jest = {unmock: () => {}}; -// jest.unmock('../__test_modules__/d'); -// } +{ + const jest = { unmock: () => {} } + jest.unmock('../__test_modules__/d') +} // tests @@ -36,8 +35,7 @@ test('namespace import', () => { expect(c()).toBe('unmocked') }) -// TODO: Enable this once the TODO above is fixed -test.skip('fake jest, shadowed import', () => { +test('fake jest, shadowed import', () => { expect(d._isMockFunction).toBe(true) expect(d()).toBe(undefined) }) diff --git a/e2e/hoist-jest/__tests__/integration.test.js b/e2e/hoist-jest/__tests__/integration.test.js index 56e1e8fe2b..8853872609 100644 --- a/e2e/hoist-jest/__tests__/integration.test.js +++ b/e2e/hoist-jest/__tests__/integration.test.js @@ -1,8 +1,7 @@ import React from 'react' import Mocked from '../__test_modules__/mocked' import Unmocked from '../__test_modules__/unmocked' -// TODO: Enable when the 2nd TODO is fixed -// import a from '../__test_modules__/a' +import a from '../__test_modules__/a' import b from '../__test_modules__/b' import c from '../__test_modules__/c' import d from '../__test_modules__/d' @@ -48,18 +47,17 @@ jest.mock('virtual-module', () => 'kiwi', { virtual: true }) jest.mock('has-flow-types', () => () => 3, { virtual: true, }) +jest.mock('../__test_modules__/a') // These will not be hoisted jest.unmock('../__test_modules__/a').dontMock('../__test_modules__/b') -// eslint-disable-next-line no-useless-concat -jest.unmock('../__test_modules__/' + 'a') jest.dontMock('../__test_modules__/mocked') -// TODO: This is a bug to fix -// { -// const jest = {unmock: () => {}}; -// // Would error (used before initialization) if hoisted to the top of the scope -// jest.unmock('../__test_modules__/a'); -// } + +{ + const jest = { unmock: () => {} } + // Would error (used before initialization) if hoisted to the top of the scope + jest.unmock('../__test_modules__/a') +} // This must not throw an error const myObject = { mock: () => {} } @@ -122,9 +120,8 @@ describe('hoisting', () => { expect(Mocked._isMockFunction).toBe(true) expect(new Mocked().isMocked).toEqual(undefined) - // TODO: Enable this once the TODO above is fixed - // expect(a._isMockFunction).toBe(true); - // expect(a()).toEqual(undefined); + expect(a._isMockFunction).toBe(true) + expect(a()).toEqual(undefined) expect(b._isMockFunction).toBe(true) expect(b()).toEqual(undefined) diff --git a/e2e/hoist-jest/jest-isolated.config.js b/e2e/hoist-jest/non-ts-factory/jest-isolated.config.js similarity index 54% rename from e2e/hoist-jest/jest-isolated.config.js rename to e2e/hoist-jest/non-ts-factory/jest-isolated.config.js index e6bd41227a..ed9a5d2220 100644 --- a/e2e/hoist-jest/jest-isolated.config.js +++ b/e2e/hoist-jest/non-ts-factory/jest-isolated.config.js @@ -8,7 +8,11 @@ module.exports = { }, }, }, + roots: ['', '/../__tests__'], + moduleNameMapper: { + react$: '/node_modules/react', + }, transform: { - '^.+.[tj]sx?$': '/../../dist/index.js', + '^.+.[tj]sx?$': 'ts-jest', }, } diff --git a/e2e/hoist-jest/non-ts-factory/package-lock.json b/e2e/hoist-jest/non-ts-factory/package-lock.json new file mode 100644 index 0000000000..c0ea07b44a --- /dev/null +++ b/e2e/hoist-jest/non-ts-factory/package-lock.json @@ -0,0 +1,95 @@ +{ + "name": "non-ts-factory", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "react": "^17.0.2", + "typescript": "~3.8.3" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/typescript": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + } + }, + "dependencies": { + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "react": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", + "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "typescript": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==" + } + } +} diff --git a/e2e/hoist-jest/non-ts-factory/package.json b/e2e/hoist-jest/non-ts-factory/package.json new file mode 100644 index 0000000000..e12c0fd87c --- /dev/null +++ b/e2e/hoist-jest/non-ts-factory/package.json @@ -0,0 +1,24 @@ +{ + "dependencies": { + "react": "^17.0.2", + "typescript": "~3.8.3" + }, + "jest": { + "automock": true, + "globals": { + "ts-jest": { + "diagnostics": false, + "tsconfig": { + "allowJs": true + } + } + }, + "roots": ["", "/../__tests__"], + "moduleNameMapper": { + "react$": "/node_modules/react" + }, + "transform": { + "^.+\\.[tj]sx?$": "ts-jest" + } + } +} diff --git a/e2e/hoist-jest/ts-factory/jest-isolated.config.js b/e2e/hoist-jest/ts-factory/jest-isolated.config.js new file mode 100644 index 0000000000..c117bd2d8a --- /dev/null +++ b/e2e/hoist-jest/ts-factory/jest-isolated.config.js @@ -0,0 +1,18 @@ +module.exports = { + automock: true, + globals: { + 'ts-jest': { + isolatedModules: true, + tsconfig: { + allowJs: true, + }, + }, + }, + roots: ['', '/../__tests__'], + moduleNameMapper: { + react$: '/node_modules/react', + }, + transform: { + '^.+.[tj]sx?$': '/../../../dist/index.js', + }, +} diff --git a/e2e/hoist-jest/package-lock.json b/e2e/hoist-jest/ts-factory/package-lock.json similarity index 99% rename from e2e/hoist-jest/package-lock.json rename to e2e/hoist-jest/ts-factory/package-lock.json index b48c0923dc..988598fba8 100644 --- a/e2e/hoist-jest/package-lock.json +++ b/e2e/hoist-jest/ts-factory/package-lock.json @@ -1,5 +1,5 @@ { - "name": "hoist-jest", + "name": "ts-factory", "lockfileVersion": 2, "requires": true, "packages": { diff --git a/e2e/hoist-jest/package.json b/e2e/hoist-jest/ts-factory/package.json similarity index 60% rename from e2e/hoist-jest/package.json rename to e2e/hoist-jest/ts-factory/package.json index abd9506a2e..9ca97b7a7c 100644 --- a/e2e/hoist-jest/package.json +++ b/e2e/hoist-jest/ts-factory/package.json @@ -12,12 +12,12 @@ } } }, + "roots": ["", "/../__tests__"], "moduleNameMapper": { - "@jest/globals": "/../../node_modules/@jest/globals", - "@types/react": "/../../node_modules/@types/react" + "react$": "/node_modules/react" }, "transform": { - "^.+\\.[tj]sx?$": "/../../dist/index.js" + "^.+\\.[tj]sx?$": "/../../../dist/index.js" } } } diff --git a/e2e/utils.ts b/e2e/utils.ts index 04b9d60aa2..12b79c84bc 100644 --- a/e2e/utils.ts +++ b/e2e/utils.ts @@ -40,7 +40,7 @@ export const runNpmInstall = (cwd: Config.Path, env?: Record): R fs.writeFileSync(lockfilePath, '') } - return run(exists ? 'npm ci' : 'npm i', cwd, env) + return run(exists ? 'npm ci --no-optional' : 'npm i --no-optional', cwd, env) } const replaceTime = (str: string): string => diff --git a/src/config/__snapshots__/config-set.spec.ts.snap b/src/config/__snapshots__/config-set.spec.ts.snap index 89b24268ad..a1ed3e9c6c 100644 --- a/src/config/__snapshots__/config-set.spec.ts.snap +++ b/src/config/__snapshots__/config-set.spec.ts.snap @@ -60,7 +60,7 @@ Object { Object { "factory": [Function], "name": "hoist-jest", - "version": 1, + "version": 2, }, ], } @@ -74,7 +74,7 @@ Object { Object { "factory": [Function], "name": "hoist-jest", - "version": 1, + "version": 2, }, Object { "factory": [Function], @@ -95,7 +95,7 @@ Object { Object { "factory": [Function], "name": "hoist-jest", - "version": 1, + "version": 2, }, ], } @@ -113,7 +113,7 @@ Object { Object { "factory": [Function], "name": "hoist-jest", - "version": 1, + "version": 2, }, ], } @@ -127,7 +127,7 @@ Object { Object { "factory": [Function], "name": "hoist-jest", - "version": 1, + "version": 2, }, Object { "factory": [Function], diff --git a/src/transformers/__snapshots__/hoist-jest.spec.ts.snap b/src/transformers/__snapshots__/hoist-jest.spec.ts.snap index 4f4d8f3f50..c8b2ba50ae 100644 --- a/src/transformers/__snapshots__/hoist-jest.spec.ts.snap +++ b/src/transformers/__snapshots__/hoist-jest.spec.ts.snap @@ -1,66 +1,64 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`hoisting should hoist correctly jest methods 1`] = ` +exports[`hoist-jest with module CommonJS should hoist correctly when not using @jest/globals 1`] = ` "\\"use strict\\"; Object.defineProperty(exports, \\"__esModule\\", { value: true }); -var globals_1 = require(\\"@jest/globals\\"); // These will all be hoisted above imports jest.unmock('react'); jest.deepUnmock('../__test_modules__/Unmocked'); jest.unmock('../__test_modules__/c').unmock('../__test_modules__/d'); -jest.mock('../__test_modules__/f', function () { +jest.mock('../__test_modules__/f', () => { if (!global.CALLS) { global.CALLS = 0; } global.CALLS++; return { _isMock: true, - fn: function () { + fn: () => { // The \`jest.mock\` transform will allow require, built-ins and globals. - var path = require('path'); - var array = new Array(3); + const path = require('path'); + const array = new Array(3); array[0] = path.sep; - return jest.fn(function () { return array; }); + return jest.fn(() => array); }, }; }); -jest.mock(\\"../__test_modules__/jestBackticks\\"); -jest.mock('virtual-module', function () { return 'kiwi'; }, { virtual: true }); +jest.mock(\`../__test_modules__/jestBackticks\`); +jest.mock('virtual-module', () => 'kiwi', { virtual: true }); // This has types that should be ignored by the out-of-scope variables check. -jest.mock('has-flow-types', function () { return function (props) { return 3; }; }, { +jest.mock('has-flow-types', () => (props) => 3, { virtual: true, }); -jest.unmock('../__test_modules__/' + 'a'); -jest.mock('../__test_modules__/f', function () { return MockMethods; }); -var Unmocked_1 = require(\\"../__test_modules__/Unmocked\\"); -var Mocked_1 = require(\\"../__test_modules__/Mocked\\"); -var a_1 = require(\\"../__test_modules__/a\\"); -var b_1 = require(\\"../__test_modules__/b\\"); -var c_1 = require(\\"../__test_modules__/c\\"); -var d_1 = require(\\"../__test_modules__/d\\"); -var jestBackticks_1 = require(\\"../__test_modules__/jestBackticks\\"); +jest.mock('../__test_modules__/f', () => MockMethods); +const Unmocked_1 = require(\\"../__test_modules__/Unmocked\\"); +const Mocked_1 = require(\\"../__test_modules__/Mocked\\"); +const a_1 = require(\\"../__test_modules__/a\\"); +const b_1 = require(\\"../__test_modules__/b\\"); +const c_1 = require(\\"../__test_modules__/c\\"); +const d_1 = require(\\"../__test_modules__/d\\"); +const jestBackticks_1 = require(\\"../__test_modules__/jestBackticks\\"); // The virtual mock call below will be hoisted above this \`require\` call. -var virtualModule = require('virtual-module'); -var e; +const virtualModule = require('virtual-module'); +let e; (function () { // hoisted to the top of the function scope jest.unmock('../__test_modules__/e'); - var _getJestObj = 42; + const _getJestObj = 42; e = require('../__test_modules__/e').default; })(); // These will not be hoisted jest.unmock('../__test_modules__/a').dontMock('../__test_modules__/b'); jest.dontMock('../__test_modules__/Mocked'); { + const jest = { unmock: () => { } }; // Would error (used before initialization) if hoisted to the top of the scope jest.unmock('../__test_modules__/a'); - var jest = { unmock: function () { } }; } // This must not throw an error -var myObject = { mock: function () { } }; +const myObject = { mock: () => { } }; myObject.mock('apple', 27); // Variable names prefixed with \`mock\` (ignore case) should not throw as out-of-scope -var MockMethods = function () { }; +const MockMethods = () => { }; console.log(Unmocked_1.default); console.log(Mocked_1.default); console.log(a_1.default); @@ -70,33 +68,220 @@ console.log(d_1.default); console.log(e); console.log(virtualModule); console.log(jestBackticks_1.default); -console.log(globals_1.it); " `; -exports[`hoisting should hoist correctly jest methods 2`] = ` +exports[`hoist-jest with module CommonJS should hoist correctly when using with @jest/globals 1`] = ` "\\"use strict\\"; Object.defineProperty(exports, \\"__esModule\\", { value: true }); -var globals_1 = require(\\"@jest/globals\\"); -var globals_2 = require(\\"@jest/globals\\"); -var JestGlobals = require(\\"@jest/globals\\"); -// These will be hoisted above imports -globals_1.jest.unmock('../__test_modules__/a'); -globals_2.jest.unmock('../__test_modules__/b'); -JestGlobals.jest.unmock('../__test_modules__/c'); -var a_1 = require(\\"../__test_modules__/a\\"); -var b_1 = require(\\"../__test_modules__/b\\"); -var c_1 = require(\\"../__test_modules__/c\\"); -var d_1 = require(\\"../__test_modules__/d\\"); -// These will not be hoisted above imports +const JestGlobals = require(\\"@jest/globals\\"); +const globals_1 = require(\\"@jest/globals\\"); +const globals_2 = require(\\"@jest/globals\\"); +// These will all be hoisted above imports +globals_2.jest.unmock('react'); +globals_1.jest.deepUnmock('../__test_modules__/Unmocked'); +JestGlobals.jest.unmock('../__test_modules__/c').unmock('../__test_modules__/d'); +JestGlobals.jest.mock('../__test_modules__/f', () => { + if (!global.CALLS) { + global.CALLS = 0; + } + global.CALLS++; + return { + _isMock: true, + fn: () => { + // The \`jest.mock\` transform will allow require, built-ins and globals. + const path = require('path'); + const array = new Array(3); + array[0] = path.sep; + return globals_2.jest.fn(() => array); + }, + }; +}); +globals_1.jest.jest.mock(\`../__test_modules__/jestBackticks\`); +globals_2.jest.mock('virtual-module', () => 'kiwi', { virtual: true }); +// This has types that should be ignored by the out-of-scope variables check. +globals_2.jest.mock('has-flow-types', () => (props) => 3, { + virtual: true, +}); +JestGlobals.jest.mock('../__test_modules__/f', () => MockMethods); +const Unmocked_1 = require(\\"../__test_modules__/Unmocked\\"); +const Mocked_1 = require(\\"../__test_modules__/Mocked\\"); +const a_1 = require(\\"../__test_modules__/a\\"); +const b_1 = require(\\"../__test_modules__/b\\"); +const c_1 = require(\\"../__test_modules__/c\\"); +const d_1 = require(\\"../__test_modules__/d\\"); +const jestBackticks_1 = require(\\"../__test_modules__/jestBackticks\\"); +// The virtual mock call below will be hoisted above this \`require\` call. +const virtualModule = require('virtual-module'); +let e; +(function () { + // hoisted to the top of the function scope + globals_2.jest.unmock('../__test_modules__/e'); + const _getJestObj = 42; + e = require('../__test_modules__/e').default; +})(); +// These will not be hoisted +globals_2.jest.unmock('../__test_modules__/a').dontMock('../__test_modules__/b'); +globals_1.jest.dontMock('../__test_modules__/Mocked'); { - jest_1.unmock('../__test_modules__/d'); - var jest_1 = { unmock: function () { } }; + const jest = { unmock: () => { } }; + // Would error (used before initialization) if hoisted to the top of the scope + jest.unmock('../__test_modules__/a'); } +// This must not throw an error +const myObject = { mock: () => { } }; +myObject.mock('apple', 27); +// Variable names prefixed with \`mock\` (ignore case) should not throw as out-of-scope +const MockMethods = () => { }; +console.log(Unmocked_1.default); +console.log(Mocked_1.default); console.log(a_1.default); console.log(b_1.default); console.log(c_1.default); console.log(d_1.default); -console.log(globals_1.it); +console.log(e); +console.log(virtualModule); +console.log(jestBackticks_1.default); +" +`; + +exports[`hoist-jest with module ESM should hoist correctly when not using @jest/globals 1`] = ` +"// These will all be hoisted above imports +jest.unmock('react'); +jest.deepUnmock('../__test_modules__/Unmocked'); +jest.unmock('../__test_modules__/c').unmock('../__test_modules__/d'); +jest.mock('../__test_modules__/f', () => { + if (!global.CALLS) { + global.CALLS = 0; + } + global.CALLS++; + return { + _isMock: true, + fn: () => { + // The \`jest.mock\` transform will allow require, built-ins and globals. + const path = require('path'); + const array = new Array(3); + array[0] = path.sep; + return jest.fn(() => array); + }, + }; +}); +jest.mock(\`../__test_modules__/jestBackticks\`); +jest.mock('virtual-module', () => 'kiwi', { virtual: true }); +// This has types that should be ignored by the out-of-scope variables check. +jest.mock('has-flow-types', () => (props) => 3, { + virtual: true, +}); +jest.mock('../__test_modules__/f', () => MockMethods); +import Unmocked from '../__test_modules__/Unmocked'; +import Mocked from '../__test_modules__/Mocked'; +import a from '../__test_modules__/a'; +import b from '../__test_modules__/b'; +import c from '../__test_modules__/c'; +import d from '../__test_modules__/d'; +import jestBackticks from '../__test_modules__/jestBackticks'; +// The virtual mock call below will be hoisted above this \`require\` call. +const virtualModule = require('virtual-module'); +let e; +(function () { + // hoisted to the top of the function scope + jest.unmock('../__test_modules__/e'); + const _getJestObj = 42; + e = require('../__test_modules__/e').default; +})(); +// These will not be hoisted +jest.unmock('../__test_modules__/a').dontMock('../__test_modules__/b'); +jest.dontMock('../__test_modules__/Mocked'); +{ + const jest = { unmock: () => { } }; + // Would error (used before initialization) if hoisted to the top of the scope + jest.unmock('../__test_modules__/a'); +} +// This must not throw an error +const myObject = { mock: () => { } }; +myObject.mock('apple', 27); +// Variable names prefixed with \`mock\` (ignore case) should not throw as out-of-scope +const MockMethods = () => { }; +console.log(Unmocked); +console.log(Mocked); +console.log(a); +console.log(b); +console.log(c); +console.log(d); +console.log(e); +console.log(virtualModule); +console.log(jestBackticks); +" +`; + +exports[`hoist-jest with module ESM should hoist correctly when using with @jest/globals 1`] = ` +"import * as JestGlobals from '@jest/globals'; +import { jest as aliasedJest } from '@jest/globals'; +import { jest } from '@jest/globals'; +// These will all be hoisted above imports +jest.unmock('react'); +aliasedJest.deepUnmock('../__test_modules__/Unmocked'); +JestGlobals.jest.unmock('../__test_modules__/c').unmock('../__test_modules__/d'); +JestGlobals.jest.mock('../__test_modules__/f', () => { + if (!global.CALLS) { + global.CALLS = 0; + } + global.CALLS++; + return { + _isMock: true, + fn: () => { + // The \`jest.mock\` transform will allow require, built-ins and globals. + const path = require('path'); + const array = new Array(3); + array[0] = path.sep; + return jest.fn(() => array); + }, + }; +}); +aliasedJest.jest.mock(\`../__test_modules__/jestBackticks\`); +jest.mock('virtual-module', () => 'kiwi', { virtual: true }); +// This has types that should be ignored by the out-of-scope variables check. +jest.mock('has-flow-types', () => (props) => 3, { + virtual: true, +}); +JestGlobals.jest.mock('../__test_modules__/f', () => MockMethods); +import Unmocked from '../__test_modules__/Unmocked'; +import Mocked from '../__test_modules__/Mocked'; +import a from '../__test_modules__/a'; +import b from '../__test_modules__/b'; +import c from '../__test_modules__/c'; +import d from '../__test_modules__/d'; +import jestBackticks from '../__test_modules__/jestBackticks'; +// The virtual mock call below will be hoisted above this \`require\` call. +const virtualModule = require('virtual-module'); +let e; +(function () { + // hoisted to the top of the function scope + jest.unmock('../__test_modules__/e'); + const _getJestObj = 42; + e = require('../__test_modules__/e').default; +})(); +// These will not be hoisted +jest.unmock('../__test_modules__/a').dontMock('../__test_modules__/b'); +aliasedJest.dontMock('../__test_modules__/Mocked'); +{ + const jest = { unmock: () => { } }; + // Would error (used before initialization) if hoisted to the top of the scope + jest.unmock('../__test_modules__/a'); +} +// This must not throw an error +const myObject = { mock: () => { } }; +myObject.mock('apple', 27); +// Variable names prefixed with \`mock\` (ignore case) should not throw as out-of-scope +const MockMethods = () => { }; +console.log(Unmocked); +console.log(Mocked); +console.log(a); +console.log(b); +console.log(c); +console.log(d); +console.log(e); +console.log(virtualModule); +console.log(jestBackticks); " `; diff --git a/src/transformers/hoist-jest.spec.ts b/src/transformers/hoist-jest.spec.ts index 9dc6930aec..0db2f11715 100644 --- a/src/transformers/hoist-jest.spec.ts +++ b/src/transformers/hoist-jest.spec.ts @@ -3,130 +3,198 @@ import ts from 'typescript' import { createConfigSet } from '../__helpers__/fakers' import { TsCompiler } from '../compiler/ts-compiler' -import * as hoist from './hoist-jest' - -const CODE_WITH_HOISTING_NO_JEST_GLOBALS = ` -import React from 'react' -import Unmocked from '../__test_modules__/Unmocked' -import Mocked from '../__test_modules__/Mocked' -import {it} from '@jest/globals' -import a from '../__test_modules__/a' -import b from '../__test_modules__/b' -import c from '../__test_modules__/c' -import d from '../__test_modules__/d' -import f from '../__test_modules__/f' -import jestBackticks from '../__test_modules__/jestBackticks' - -// The virtual mock call below will be hoisted above this \`require\` call. -const virtualModule = require('virtual-module') - -// These will all be hoisted above imports -jest.unmock('react') -jest.deepUnmock('../__test_modules__/Unmocked') -jest.unmock('../__test_modules__/c').unmock('../__test_modules__/d') - -let e; -(function () { - const _getJestObj = 42; - e = require('../__test_modules__/e').default - // hoisted to the top of the function scope - jest.unmock('../__test_modules__/e') -})() - -jest.mock('../__test_modules__/f', () => { - if (!global.CALLS) { - global.CALLS = 0 - } - global.CALLS++ - - return { - _isMock: true, - fn: () => { - // The \`jest.mock\` transform will allow require, built-ins and globals. - const path = require('path') - const array = new Array(3) - array[0] = path.sep - return jest.fn(() => array) - }, - }; -}) -jest.mock(\`../__test_modules__/jestBackticks\`) -jest.mock('virtual-module', () => 'kiwi', {virtual: true}) -// This has types that should be ignored by the out-of-scope variables check. -jest.mock('has-flow-types', () => (props: {children: mixed}) => 3, { - virtual: true, -}) +import { factory as hoistJest, name, version } from './hoist-jest' -// These will not be hoisted -jest.unmock('../__test_modules__/a').dontMock('../__test_modules__/b') -jest.unmock('../__test_modules__/' + 'a') -jest.dontMock('../__test_modules__/Mocked') -{ - const jest = {unmock: () => {}}; - // Would error (used before initialization) if hoisted to the top of the scope - jest.unmock('../__test_modules__/a') -} - -// This must not throw an error -const myObject = {mock: () => {}} -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) - -console.log(Unmocked) -console.log(Mocked) -console.log(a) -console.log(b) -console.log(c) -console.log(d) -console.log(e) -console.log(virtualModule) -console.log(jestBackticks) -console.log(it) -` -const CODE_WITH_HOISTING_HAS_JEST_GLOBALS = ` +const BASE_IMPORT_CODE = ` + import React from 'react' + import Unmocked from '../__test_modules__/Unmocked' + import Mocked from '../__test_modules__/Mocked' import a from '../__test_modules__/a' import b from '../__test_modules__/b' - - import {it, jest} from '@jest/globals' - import {jest as aliasedJest} from '@jest/globals' - import * as JestGlobals from '@jest/globals' - import c from '../__test_modules__/c' import d from '../__test_modules__/d' + import f from '../__test_modules__/f' + import jestBackticks from '../__test_modules__/jestBackticks' +` - // These will be hoisted above imports +const CODE_WITH_HOISTING_NO_JEST_GLOBALS = + BASE_IMPORT_CODE + + `\n` + + ` + // The virtual mock call below will be hoisted above this \`require\` call. + const virtualModule = require('virtual-module') + + // These will all be hoisted above imports + jest.unmock('react') + jest.deepUnmock('../__test_modules__/Unmocked') + jest.unmock('../__test_modules__/c').unmock('../__test_modules__/d') + + let e; + (function () { + const _getJestObj = 42; + e = require('../__test_modules__/e').default + // hoisted to the top of the function scope + jest.unmock('../__test_modules__/e') + })() + + jest.mock('../__test_modules__/f', () => { + if (!global.CALLS) { + global.CALLS = 0 + } + global.CALLS++ + + return { + _isMock: true, + fn: () => { + // The \`jest.mock\` transform will allow require, built-ins and globals. + const path = require('path') + const array = new Array(3) + array[0] = path.sep + return jest.fn(() => array) + }, + }; + }) + jest.mock(\`../__test_modules__/jestBackticks\`) + jest.mock('virtual-module', () => 'kiwi', {virtual: true}) + // This has types that should be ignored by the out-of-scope variables check. + jest.mock('has-flow-types', () => (props: {children: mixed}) => 3, { + virtual: true, + }) + + // These will not be hoisted + jest.unmock('../__test_modules__/a').dontMock('../__test_modules__/b') + jest.dontMock('../__test_modules__/Mocked') + { + const jest = {unmock: () => {}}; + // Would error (used before initialization) if hoisted to the top of the scope + jest.unmock('../__test_modules__/a') + } - jest.unmock('../__test_modules__/a') - aliasedJest.unmock('../__test_modules__/b') - JestGlobals.jest.unmock('../__test_modules__/c') + // This must not throw an error + const myObject = {mock: () => {}} + myObject.mock('apple', 27) - // These will not be hoisted above imports + // Variable names prefixed with \`mock\` (ignore case) should not throw as out-of-scope + const MockMethods = () => {} + jest.mock('../__test_modules__/f', () => MockMethods) + console.log(Unmocked) + console.log(Mocked) + console.log(a) + console.log(b) + console.log(c) + console.log(d) + console.log(e) + console.log(virtualModule) + console.log(jestBackticks) +` +const CODE_WITH_HOISTING_HAS_JEST_GLOBALS = + BASE_IMPORT_CODE + + `\n` + + ` + import {jest} from '@jest/globals' + import {jest as aliasedJest} from '@jest/globals' + import * as JestGlobals from '@jest/globals' + + // The virtual mock call below will be hoisted above this \`require\` call. + const virtualModule = require('virtual-module') + + // These will all be hoisted above imports + jest.unmock('react') + aliasedJest.deepUnmock('../__test_modules__/Unmocked') + JestGlobals.jest.unmock('../__test_modules__/c').unmock('../__test_modules__/d') + + let e; + (function () { + const _getJestObj = 42; + e = require('../__test_modules__/e').default + // hoisted to the top of the function scope + jest.unmock('../__test_modules__/e') + })() + + JestGlobals.jest.mock('../__test_modules__/f', () => { + if (!global.CALLS) { + global.CALLS = 0 + } + global.CALLS++ + + return { + _isMock: true, + fn: () => { + // The \`jest.mock\` transform will allow require, built-ins and globals. + const path = require('path') + const array = new Array(3) + array[0] = path.sep + return jest.fn(() => array) + }, + }; + }) + aliasedJest.jest.mock(\`../__test_modules__/jestBackticks\`) + jest.mock('virtual-module', () => 'kiwi', {virtual: true}) + // This has types that should be ignored by the out-of-scope variables check. + jest.mock('has-flow-types', () => (props: {children: mixed}) => 3, { + virtual: true, + }) + + // These will not be hoisted + jest.unmock('../__test_modules__/a').dontMock('../__test_modules__/b') + aliasedJest.dontMock('../__test_modules__/Mocked') { const jest = {unmock: () => {}}; - jest.unmock('../__test_modules__/d'); + // Would error (used before initialization) if hoisted to the top of the scope + jest.unmock('../__test_modules__/a') } + // This must not throw an error + const myObject = {mock: () => {}} + myObject.mock('apple', 27) + + // Variable names prefixed with \`mock\` (ignore case) should not throw as out-of-scope + const MockMethods = () => {} + JestGlobals.jest.mock('../__test_modules__/f', () => MockMethods) + + console.log(Unmocked) + console.log(Mocked) console.log(a) console.log(b) console.log(c) console.log(d) - console.log(it) + console.log(e) + console.log(virtualModule) + console.log(jestBackticks) ` -const createFactory = () => hoist.factory(new TsCompiler(createConfigSet(), new Map())) -const transpile = (source: string) => ts.transpileModule(source, { transformers: { before: [createFactory()] } }) +const createFactory = () => hoistJest(new TsCompiler(createConfigSet(), new Map())) +const transpile = (source: string, moduleKind: number) => + ts.transpileModule(source, { + transformers: { before: [createFactory()] }, + compilerOptions: { + module: moduleKind, + target: ts.ScriptTarget.ES2015, + }, + }) -describe('hoisting', () => { - test.each([CODE_WITH_HOISTING_NO_JEST_GLOBALS, CODE_WITH_HOISTING_HAS_JEST_GLOBALS])( - 'should hoist correctly jest methods', - (data) => { - const out = transpile(data) +const executeTest = (moduleKind: number): void => { + test('should hoist correctly when not using @jest/globals', () => { + expect(transpile(CODE_WITH_HOISTING_NO_JEST_GLOBALS, moduleKind).outputText).toMatchSnapshot() + }) - expect(out.outputText).toMatchSnapshot() - }, - ) + test('should hoist correctly when using with @jest/globals', () => { + expect(transpile(CODE_WITH_HOISTING_HAS_JEST_GLOBALS, moduleKind).outputText).toMatchSnapshot() + }) +} + +describe('hoist-jest', () => { + test('should have correct transformer name and version', () => { + expect(name).toBe('hoist-jest') + expect(version).toBe(2) + }) + + describe('with module CommonJS', () => { + executeTest(ts.ModuleKind.CommonJS) + }) + + describe('with module ESM', () => { + executeTest(ts.ModuleKind.ESNext) + }) }) diff --git a/src/transformers/hoist-jest.ts b/src/transformers/hoist-jest.ts index af48dcbedb..faf6a0d737 100644 --- a/src/transformers/hoist-jest.ts +++ b/src/transformers/hoist-jest.ts @@ -1,15 +1,6 @@ +import type { Jest } from '@jest/environment' import { LogContexts, LogLevels } from 'bs-logger' -import type { - Block, - ExpressionStatement, - ImportDeclaration, - Node, - SourceFile, - Statement, - TransformationContext, - Transformer, - Visitor, -} from 'typescript' +import type _ts from 'typescript' import type { TsCompilerInstance } from '../types' @@ -17,45 +8,44 @@ import type { TsCompilerInstance } from '../types' * Remember to increase the version whenever transformer's content is changed. This is to inform Jest to not reuse * the previous cache which contains old transformer's content */ -export const version = 1 +export const version = 2 // Used for constructing cache key export const name = 'hoist-jest' -/** - * What methods of `jest` we should hoist - */ -const HOIST_METHODS = ['mock', 'unmock', 'enableAutomock', 'disableAutomock', 'deepUnmock'] +type HoistedMethod = keyof Pick + +const HOIST_METHODS: HoistedMethod[] = ['mock', 'unmock', 'enableAutomock', 'disableAutomock', 'deepUnmock'] const JEST_GLOBALS_MODULE_NAME = '@jest/globals' const JEST_GLOBAL_NAME = 'jest' -const ROOT_LEVEL_AST = 1 -/** - * The factory of hoisting transformer factory - * - * @internal - */ -export function factory({ configSet }: TsCompilerInstance): (ctx: TransformationContext) => Transformer { - const logger = configSet.logger.child({ namespace: 'ts-hoisting' }) - /** - * Our compiler (typescript, or a module with typescript-like interface) - * To access Program or TypeChecker, do: cs.tsCompiler.program or cs.tsCompiler.program.getTypeChecker() - */ +export function factory({ configSet }: TsCompilerInstance) { + const logger = configSet.logger.child({ namespace: name }) const ts = configSet.compilerModule - const importNames: string[] = [] + const tsFactory = ts.factory ? ts.factory : ts + const importNamesOfJestObj: string[] = [] + + const isJestGlobalImport = (node: _ts.Node): node is _ts.ImportDeclaration => { + return ( + ts.isImportDeclaration(node) && + ts.isStringLiteral(node.moduleSpecifier) && + node.moduleSpecifier.text === JEST_GLOBALS_MODULE_NAME + ) + } - function shouldHoistExpression(node: Node): boolean { + const shouldHoistExpression = (node: _ts.Node): node is _ts.ExpressionStatement => { if ( ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression) && - HOIST_METHODS.includes(node.expression.name.text) + HOIST_METHODS.includes(node.expression.name.text as HoistedMethod) ) { - if (importNames.length) { + if (importNamesOfJestObj.length) { // @jest/globals is in used return ( - (ts.isIdentifier(node.expression.expression) && importNames.includes(node.expression.expression.text)) || + (ts.isIdentifier(node.expression.expression) && + importNamesOfJestObj.includes(node.expression.expression.text)) || (ts.isPropertyAccessExpression(node.expression.expression) && ts.isIdentifier(node.expression.expression.expression) && - importNames.includes(node.expression.expression.expression.text)) || + importNamesOfJestObj.includes(node.expression.expression.expression.text)) || shouldHoistExpression(node.expression.expression) ) } else { @@ -73,132 +63,118 @@ export function factory({ configSet }: TsCompilerInstance): (ctx: Transformation /** * Checks whether given node is a statement that we need to hoist */ - function shouldHoistNode(node: Node): node is ExpressionStatement { + const isHoistableStatement = (node: _ts.Node): node is _ts.ExpressionStatement => { return ts.isExpressionStatement(node) && shouldHoistExpression(node.expression) } - function isJestGlobalImport(node: Node): node is ImportDeclaration { - return ( - ts.isImportDeclaration(node) && - ts.isStringLiteral(node.moduleSpecifier) && - node.moduleSpecifier.text === JEST_GLOBALS_MODULE_NAME + const canHoistInBlockScope = (node: _ts.Block): node is _ts.Block => + !!node.statements.find( + (stmt) => + ts.isVariableStatement(stmt) && + stmt.declarationList.declarations.find((decl) => decl.name.getText() !== JEST_GLOBAL_NAME) && + node.statements.find((stmt) => isHoistableStatement(stmt)), ) - } /** - * Create a source file visitor which will visit all nodes in a source file + * Sort statements according to priority + * - Import Jest object from `@jest/globals` + * - Hoistable methods + * - Non-hoistable methods */ - function createVisitor(ctx: TransformationContext, _: SourceFile) { - /** - * Current block level - */ - let level = 0 - /** - * List of nodes which needs to be hoisted, indexed by their owning level - */ - const hoisted: Statement[][] = [] - /** - * Called when we enter a block to increase the level - */ - const enter = () => { - level++ - // reuse arrays - if (hoisted[level]) { - hoisted[level].splice(0, hoisted[level].length) - } + const sortStatements = (statements: _ts.Statement[]): _ts.Statement[] => { + if (statements.length <= 1) { + return statements } - /** - * Called when we leave a block to decrease the level - */ - const exit = () => level-- - /** - * Adds a node to the list of nodes to be hoisted in the current level - * - * @param node The node to hoist - */ - const hoist = (node: Statement) => { - if (hoisted[level]) { - hoisted[level].push(node) + const pivot = statements[0] + const leftPart: _ts.Statement[] = [] + const rightPart: _ts.Statement[] = [] + for (let i = 1; i < statements.length; i++) { + const currentStatement = statements[i] + if (isJestGlobalImport(currentStatement)) { + leftPart.push(currentStatement) } else { - hoisted[level] = [node] + isHoistableStatement(currentStatement) && !isHoistableStatement(pivot) && !isJestGlobalImport(pivot) + ? leftPart.push(currentStatement) + : rightPart.push(currentStatement) } } - /** - * Our main visitor, which will be called recursively for each node in the source file's AST - */ - const visitor: Visitor = (node) => { - // enter this level - enter() - - // visit each child - let resultNode = ts.visitEachChild(node, visitor, ctx) - /** - * Gather all possible import names, from different types of import syntax including: - * - named import, e.g. `import { jest } from '@jest/globals'` - * - aliased named import, e.g. `import {jest as aliasedJest} from '@jest/globals'` - * - namespace import, e.g `import * as JestGlobals from '@jest/globals'` - */ - if ( - isJestGlobalImport(resultNode) && - resultNode.importClause?.namedBindings && - (ts.isNamespaceImport(resultNode.importClause.namedBindings) || - ts.isNamedImports(resultNode.importClause.namedBindings)) - ) { - const { namedBindings } = resultNode.importClause - const jestImportName = ts.isNamespaceImport(namedBindings) - ? namedBindings.name.text - : namedBindings.elements.find( - (element) => element.name.text === JEST_GLOBAL_NAME || element.propertyName?.text === JEST_GLOBAL_NAME, - )?.name.text - if (jestImportName) { - importNames.push(jestImportName) - } - } - // check if we have something to hoist in this level - if (hoisted[level] && hoisted[level].length) { - // re-order children so that hoisted ones appear first - // this is actually the main job of this transformer - const hoistedStmts = hoisted[level] - const otherStmts = (resultNode as Block).statements.filter( - (s) => !hoistedStmts.includes(s) && !isJestGlobalImport(s), + + return sortStatements(leftPart).concat(pivot, sortStatements(rightPart)) + } + + const createVisitor = (ctx: _ts.TransformationContext, _: _ts.SourceFile) => { + const visitor: _ts.Visitor = (node) => { + const resultNode = ts.visitEachChild(node, visitor, ctx) + // Since we use `visitEachChild`, we go upwards tree so all children node elements are checked first + if (ts.isBlock(resultNode) && canHoistInBlockScope(resultNode)) { + const newNodeArrayStatements = tsFactory.createNodeArray( + sortStatements(resultNode.statements as unknown as _ts.Statement[]), ) - const newNode = ts.getMutableClone(resultNode) as Block - const newStatements = [...hoistedStmts, ...otherStmts] - if (level === ROOT_LEVEL_AST) { - const jestGlobalsImportStmts = (resultNode as Block).statements.filter((s) => isJestGlobalImport(s)) - // jest methods should not be hoisted higher than import `@jest/globals` - resultNode = { - ...newNode, - statements: ts.createNodeArray([...jestGlobalsImportStmts, ...newStatements]), - } as Statement - } else { - resultNode = { - ...newNode, - statements: ts.createNodeArray(newStatements), - } as Statement - } - } - // exit the level - exit() + return tsFactory.updateBlock(resultNode, newNodeArrayStatements) + } else { + if (ts.isSourceFile(resultNode)) { + resultNode.statements.forEach((stmt) => { + /** + * Gather all possible import names, from different types of import syntax including: + * - named import, e.g. `import { jest } from '@jest/globals'` + * - aliased named import, e.g. `import {jest as aliasedJest} from '@jest/globals'` + * - namespace import, e.g `import * as JestGlobals from '@jest/globals'` + */ + if ( + isJestGlobalImport(stmt) && + stmt.importClause?.namedBindings && + (ts.isNamespaceImport(stmt.importClause.namedBindings) || + ts.isNamedImports(stmt.importClause.namedBindings)) + ) { + const { namedBindings } = stmt.importClause + const jestImportName = ts.isNamespaceImport(namedBindings) + ? namedBindings.name.text + : namedBindings.elements.find( + (element) => + element.name.text === JEST_GLOBAL_NAME || element.propertyName?.text === JEST_GLOBAL_NAME, + )?.name.text + if (jestImportName) { + importNamesOfJestObj.push(jestImportName) + } + } + }) + const newNodeArrayStatements = tsFactory.createNodeArray( + sortStatements(resultNode.statements as unknown as _ts.Statement[]), + ) + importNamesOfJestObj.length = 0 - if (shouldHoistNode(resultNode)) { - // hoist into current level - hoist(resultNode as Statement) - } + return ts.factory + ? ts.factory.updateSourceFile( + resultNode, + newNodeArrayStatements, + resultNode.isDeclarationFile, + resultNode.referencedFiles, + resultNode.typeReferenceDirectives, + resultNode.hasNoDefaultLib, + resultNode.libReferenceDirectives, + ) + : ts.updateSourceFileNode( + resultNode, + newNodeArrayStatements, + resultNode.isDeclarationFile, + resultNode.referencedFiles, + resultNode.typeReferenceDirectives, + resultNode.hasNoDefaultLib, + resultNode.libReferenceDirectives, + ) + } - // finally returns the currently visited node - return resultNode + return resultNode + } } return visitor } // returns the transformer factory - return (ctx: TransformationContext): Transformer => - logger.wrap( - { [LogContexts.logLevel]: LogLevels.debug, call: null }, - 'visitSourceFileNode(): hoisting', - (sf: SourceFile) => ts.visitNode(sf, createVisitor(ctx, sf)), + return (ctx: _ts.TransformationContext): _ts.Transformer<_ts.SourceFile> => + logger.wrap({ [LogContexts.logLevel]: LogLevels.debug, call: null }, 'visitSourceFileNode(): hoist jest', (sf) => + ts.visitNode(sf, createVisitor(ctx, sf)), ) } diff --git a/src/transformers/path-mapping.spec.ts b/src/transformers/path-mapping.spec.ts index 35a0b516ed..37442418f8 100644 --- a/src/transformers/path-mapping.spec.ts +++ b/src/transformers/path-mapping.spec.ts @@ -1,15 +1,13 @@ import path from 'path' -import { testing } from 'bs-logger' import ts from 'typescript' import { createConfigSet } from '../__helpers__/fakers' import { TsCompiler } from '../compiler/ts-compiler' import { normalizeSlashes } from '../utils/normalize-slashes' -import * as pathMapping from './path-mapping' +import { factory as pathMapping } from './path-mapping' -const logger = testing.createLoggerMock() const TS_JS_CODE_WITH_PATH_ALIAS = ` import { parse } from '@utils/json' import hoo from '@utils/json' @@ -51,9 +49,8 @@ describe('path-mapping', () => { tsJestConfig: { tsconfig, }, - logger, }) - const createFactory = () => pathMapping.factory(new TsCompiler(configSet, new Map())) + const createFactory = () => pathMapping(new TsCompiler(configSet, new Map())) const transpile = (source: string) => ts.transpileModule(source, { transformers: { before: [createFactory()] } }) jest.spyOn(ts, 'resolveModuleName').mockReturnValue({ resolvedModule: { @@ -106,9 +103,8 @@ describe('path-mapping', () => { }, }, }, - logger, }) - const createFactory = () => pathMapping.factory(new TsCompiler(configSet, new Map())) + const createFactory = () => pathMapping(new TsCompiler(configSet, new Map())) const transpile = (source: string) => ts.transpileModule(source, { transformers: { before: [createFactory()] } }) const resolvedFileNameStub = path.join('..', `utils/json.${extension}`) jest.spyOn(ts, 'resolveModuleName').mockReturnValue({ diff --git a/src/transformers/path-mapping.ts b/src/transformers/path-mapping.ts index 57238c6601..0fb3c2d9d6 100644 --- a/src/transformers/path-mapping.ts +++ b/src/transformers/path-mapping.ts @@ -27,7 +27,7 @@ const isBaseDir = (base: string, dir: string) => !relative(base, dir)?.startsWit export function factory({ configSet, }: TsCompilerInstance): (ctx: _ts.TransformationContext) => _ts.Transformer<_ts.SourceFile> { - const logger = configSet.logger.child({ namespace: 'ts-path-mapping' }) + const logger = configSet.logger.child({ namespace: name }) const ts = configSet.compilerModule const tsFactory = ts.factory ? ts.factory : ts const compilerOptions = configSet.parsedTsConfig.options diff --git a/tsconfig.spec.json b/tsconfig.spec.json index 4346a2737c..ef0856eb48 100644 --- a/tsconfig.spec.json +++ b/tsconfig.spec.json @@ -5,5 +5,5 @@ "target": "ES2015", }, "includes": ["src/**/*.spec.ts"], - "exclude": ["e2e"] + "exclude": ["e2e", "examples"] }