Skip to content

Commit

Permalink
fix: load transformers before installing require hooks (jestjs#7752)
Browse files Browse the repository at this point in the history
  • Loading branch information
SimenB authored and captain-yossarian committed Jul 18, 2019
1 parent c37a185 commit bfaa70b
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
- `[jest-config]` Do not use a uuid as `name` since that breaks caching ([#7746](https://github.com/facebook/jest/pull/7746))
- `[jest-config]` Make sure `normalize` can consume `Defaults` without warnings ([#7742](https://github.com/facebook/jest/pull/7742))
- `[jest-config]` Allow `moduleFileExtensions` without 'js' for custom runners ([#7751](https://github.com/facebook/jest/pull/7751))
- `[jest-cli]` Load transformers before installing require hooks ([#7752](https://github.com/facebook/jest/pull/7752)

### Chore & Maintenance

Expand Down
12 changes: 12 additions & 0 deletions e2e/__tests__/globalSetup.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,22 @@ import {cleanup} from '../Utils';
const DIR = path.join(os.tmpdir(), 'jest-global-setup');
const project1DIR = path.join(os.tmpdir(), 'jest-global-setup-project-1');
const project2DIR = path.join(os.tmpdir(), 'jest-global-setup-project-2');
const customTransformDIR = path.join(
os.tmpdir(),
'jest-global-setup-custom-transform',
);

beforeEach(() => {
cleanup(DIR);
cleanup(project1DIR);
cleanup(project2DIR);
cleanup(customTransformDIR);
});
afterAll(() => {
cleanup(DIR);
cleanup(project1DIR);
cleanup(project2DIR);
cleanup(customTransformDIR);
});

test('globalSetup is triggered once before all test suites', () => {
Expand Down Expand Up @@ -155,3 +161,9 @@ test('globalSetup throws with named export', () => {
`TypeError: globalSetup file must export a function at ${setupPath}`,
);
});

test('should not transpile the transformer', () => {
const {status} = runJest('global-setup-custom-transform', [`--no-cache`]);

expect(status).toBe(0);
});
25 changes: 25 additions & 0 deletions e2e/global-setup-custom-transform/__tests__/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* 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.
*/
'use strict';

const fs = require('fs');
const os = require('os');
const path = require('path');
const greeting = require('../');

const DIR = path.join(os.tmpdir(), 'jest-global-setup-custom-transform');

test('should exist setup file', () => {
const files = fs.readdirSync(DIR);
expect(files).toHaveLength(1);
const setup = fs.readFileSync(path.join(DIR, files[0]), 'utf8');
expect(setup).toBe('setup');
});

test('should transform imported file', () => {
expect(greeting).toEqual('hello, world!');
});
3 changes: 3 additions & 0 deletions e2e/global-setup-custom-transform/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.

module.exports = 'hello!';
9 changes: 9 additions & 0 deletions e2e/global-setup-custom-transform/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"jest": {
"testEnvironment": "node",
"globalSetup": "<rootDir>/setup.js",
"transform": {
"^.+\\.js$": "<rootDir>/transformer.js"
}
}
}
22 changes: 22 additions & 0 deletions e2e/global-setup-custom-transform/setup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* 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.
*/
const crypto = require('crypto');
const fs = require('fs');
const {createDirectory} = require('jest-util');
const os = require('os');
const path = require('path');

const DIR = path.join(os.tmpdir(), 'jest-global-setup-custom-transform');

module.exports = function() {
return new Promise(resolve => {
createDirectory(DIR);
const fileId = crypto.randomBytes(20).toString('hex');
fs.writeFileSync(path.join(DIR, fileId), 'setup');
resolve();
});
};
15 changes: 15 additions & 0 deletions e2e/global-setup-custom-transform/transformer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.

'use strict';

const fileToTransform = require.resolve('./index.js');

module.exports = {
process(src, filename) {
if (filename === fileToTransform) {
return src.replace('hello', 'hello, world');
}

return src;
},
};
4 changes: 4 additions & 0 deletions packages/jest-cli/src/runGlobalHook.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ export default ({

const transformer = new Runtime.ScriptTransformer(projectConfig);

// Load the transformer to avoid a cycle where we need to load a
// transformer in order to transform it in the require hooks
transformer.preloadTransformer(modulePath);

const revertHook = addHook(
(code, filename) =>
transformer.transformSource(filename, code, false).code || code,
Expand Down
6 changes: 6 additions & 0 deletions packages/jest-runtime/src/ScriptTransformer.js
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,12 @@ export default class ScriptTransformer {
}
}

// We don't want to expose transformers to the outside - this function is just
// to warm up `this._transformCache`
preloadTransformer(filepath: Path): void {
this._getTransformer(filepath);
}

transformSource(filepath: Path, content: string, instrument: boolean) {
const filename = this._getRealPath(filepath);
const transform = this._getTransformer(filename);
Expand Down
27 changes: 22 additions & 5 deletions packages/jest-runtime/src/__tests__/script_transformer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ describe('ScriptTransformer', () => {
expect(vm.Script.mock.calls[0][0]).toMatchSnapshot();

// no-cache case
expect(fs.readFileSync.mock.calls.length).toBe(1);
expect(fs.readFileSync).toHaveBeenCalledTimes(1);
expect(fs.readFileSync).toBeCalledWith('/fruits/banana.js', 'utf8');

// in-memory cache
Expand Down Expand Up @@ -258,7 +258,7 @@ describe('ScriptTransformer', () => {
expect(vm.Script.mock.calls[0][0]).toContain(fsSourceCode);

// Native files should never be transformed.
expect(shouldInstrument.mock.calls.length).toBe(0);
expect(shouldInstrument).toHaveBeenCalledTimes(0);
});

it(
Expand Down Expand Up @@ -512,7 +512,7 @@ describe('ScriptTransformer', () => {
scriptTransformer = new ScriptTransformer(transformConfig);
scriptTransformer.transform('/fruits/banana.js', {});

expect(fs.readFileSync.mock.calls.length).toBe(2);
expect(fs.readFileSync).toHaveBeenCalledTimes(2);
expect(fs.readFileSync).toBeCalledWith('/fruits/banana.js', 'utf8');
expect(fs.readFileSync).toBeCalledWith(cachePath, 'utf8');
expect(writeFileAtomic.sync).not.toBeCalled();
Expand All @@ -525,7 +525,7 @@ describe('ScriptTransformer', () => {
scriptTransformer = new ScriptTransformer(transformConfig);
scriptTransformer.transform('/fruits/banana.js', {});

expect(fs.readFileSync.mock.calls.length).toBe(1);
expect(fs.readFileSync).toHaveBeenCalledTimes(1);
expect(fs.readFileSync).toBeCalledWith('/fruits/banana.js', 'utf8');
expect(fs.readFileSync).not.toBeCalledWith(cachePath, 'utf8');
expect(writeFileAtomic.sync).toBeCalled();
Expand All @@ -546,7 +546,24 @@ describe('ScriptTransformer', () => {

anotherScriptTransformer.transform('/fruits/banana.js', {});

expect(fs.readFileSync.mock.calls.length).toBe(2);
expect(fs.readFileSync).toHaveBeenCalledTimes(2);
expect(fs.readFileSync).toBeCalledWith('/fruits/banana.js', 'utf8');
});

it('preload transformer when using `preloadTransformer`', () => {
const scriptTransformer = new ScriptTransformer({
...config,
transform: [['^.+\\.js$', 'test_preprocessor']],
});

expect(Array.from(scriptTransformer._transformCache.entries())).toEqual([]);

expect(
scriptTransformer.preloadTransformer('/fruits/banana.js'),
).toBeUndefined();

expect(Array.from(scriptTransformer._transformCache.entries())).toEqual([
['test_preprocessor', expect.any(Object)],
]);
});
});

0 comments on commit bfaa70b

Please sign in to comment.