Skip to content

Commit

Permalink
Allow environment to transform test scripts (#7454)
Browse files Browse the repository at this point in the history
  • Loading branch information
corevo authored and SimenB committed Dec 24, 2018
1 parent 6488fb7 commit a6eff76
Show file tree
Hide file tree
Showing 11 changed files with 77 additions and 12 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- `[jest-haste-map]` [**BREAKING**] Remove name from hash in `HasteMap.getCacheFilePath` ([#7218](https://github.com/facebook/jest/pull/7218))
- `[babel-preset-jest]` [**BREAKING**] Export a function instead of an object for Babel 7 compatibility ([#7203](https://github.com/facebook/jest/pull/7203))
- `[jest-haste-map]` [**BREAKING**] Expose relative paths when getting the file iterator ([#7321](https://github.com/facebook/jest/pull/7321))
- `[jest-runtime]` Add `extraGlobals` to config to load extra global variables into the execution vm ([#7454](https://github.com/facebook/jest/pull/7454))
- `[jest-util]` Export `specialChars` containing Unicode characters and ANSI escapes for console output ([#7532](https://github.com/facebook/jest/pull/7532))
- `[jest-config]` Handle typescript (`ts` and `tsx`) by default ([#7533](https://github.com/facebook/jest/pull/7533))
- `[jest-validate]` Add support for comments in `package.json` using a `"//"` key ([#7295](https://github.com/facebook/jest/pull/7295))
Expand Down
2 changes: 2 additions & 0 deletions TestUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const DEFAULT_GLOBAL_CONFIG: GlobalConfig = {
enabledTestsMap: null,
errorOnDeprecated: false,
expand: false,
extraGlobals: [],
filter: null,
findRelatedTests: false,
forceExit: false,
Expand Down Expand Up @@ -77,6 +78,7 @@ const DEFAULT_PROJECT_CONFIG: ProjectConfig = {
detectOpenHandles: false,
displayName: undefined,
errorOnDeprecated: false,
extraGlobals: [],
filter: null,
forceCoverageMatch: [],
globals: {},
Expand Down
17 changes: 17 additions & 0 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,23 @@ Default: `false`

Make calling deprecated APIs throw helpful error messages. Useful for easing the upgrade process.

### `extraGlobals` [array<string>]

Default: `undefined`

Test files run inside a [vm](https://nodejs.org/api/vm.html), which slows calls to global context properties (e.g. `Math`). With this option you can specify extra properties to be defined inside the vm for faster lookups.

For example, if your tests call `Math` often, you can pass it by setting `extraGlobals`.

```json
{
...
"jest": {
"extraGlobals": ["Math"]
}
}
```

### `forceCoverageMatch` [array<string>]

Default: `['']`
Expand Down
1 change: 1 addition & 0 deletions packages/jest-config/src/ValidConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export default ({
displayName: 'project-name',
errorOnDeprecated: false,
expand: false,
extraGlobals: [],
filter: '<rootDir>/filter.js',
forceCoverageMatch: ['**/*.t.js'],
forceExit: false,
Expand Down
2 changes: 2 additions & 0 deletions packages/jest-config/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ const groupOptions = (
enabledTestsMap: options.enabledTestsMap,
errorOnDeprecated: options.errorOnDeprecated,
expand: options.expand,
extraGlobals: options.extraGlobals,
filter: options.filter,
findRelatedTests: options.findRelatedTests,
forceExit: options.forceExit,
Expand Down Expand Up @@ -165,6 +166,7 @@ const groupOptions = (
detectOpenHandles: options.detectOpenHandles,
displayName: options.displayName,
errorOnDeprecated: options.errorOnDeprecated,
extraGlobals: options.extraGlobals,
filter: options.filter,
forceCoverageMatch: options.forceCoverageMatch,
globals: options.globals,
Expand Down
1 change: 1 addition & 0 deletions packages/jest-config/src/normalize.js
Original file line number Diff line number Diff line change
Expand Up @@ -660,6 +660,7 @@ export default function normalize(options: InitialOptions, argv: Argv) {
case 'displayName':
case 'errorOnDeprecated':
case 'expand':
case 'extraGlobals':
case 'globals':
case 'findRelatedTests':
case 'forceCoverageMatch':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,11 @@ module.exports = () => {
}});"
`;

exports[`ScriptTransformer transforms a file properly 3`] = `
"({\\"Object.<anonymous>\\":function(module,exports,require,__dirname,__filename,global,jest,Math){module.exports = \\"banana\\";
}});"
`;

exports[`ScriptTransformer uses multiple preprocessors 1`] = `
"({\\"Object.<anonymous>\\":function(module,exports,require,__dirname,__filename,global,jest){
const TRANSFORMED = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,12 @@ describe('ScriptTransformer', () => {
// If we disable coverage, we get a different result.
scriptTransformer.transform('/fruits/kiwi.js', {collectCoverage: false});
expect(vm.Script.mock.calls[1][0]).toEqual(snapshot);

scriptTransformer.transform('/fruits/banana.js', {
// to make sure jest isn't declared twice
extraGlobals: ['Math', 'jest'],
}).script;
expect(vm.Script.mock.calls[3][0]).toMatchSnapshot();
});

it('does not transform Node core modules', () => {
Expand Down
18 changes: 14 additions & 4 deletions packages/jest-runtime/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -645,13 +645,14 @@ class Runtime {
Object.defineProperty(localModule, 'require', {
value: this._createRequireImplementation(localModule, options),
});

const extraGlobals = this._config.extraGlobals || [];
const transformedFile = this._scriptTransformer.transform(
filename,
{
collectCoverage: this._coverageOptions.collectCoverage,
collectCoverageFrom: this._coverageOptions.collectCoverageFrom,
collectCoverageOnlyFrom: this._coverageOptions.collectCoverageOnlyFrom,
extraGlobals,
isInternalModule,
},
this._cacheFS[filename],
Expand Down Expand Up @@ -686,8 +687,7 @@ class Runtime {
}

const wrapper = runScript[ScriptTransformer.EVAL_RESULT_VARIABLE];
wrapper.call(
localModule.exports, // module context
const moduleArguments = new Set([
localModule, // module object
localModule.exports, // module exports
localModule.require, // require implementation
Expand All @@ -699,7 +699,17 @@ class Runtime {
// $FlowFixMe
(localModule.require: LocalModuleRequire),
), // jest object
);
...extraGlobals.map(globalVariable => {
if (this._environment.global[globalVariable]) {
return this._environment.global[globalVariable];
}

throw new Error(
`You have requested '${globalVariable}' as a global variable, but it was not present. Please check your config or your global environment.`,
);
}),
]);
wrapper.call(localModule.exports, ...Array.from(moduleArguments));

this._isCurrentlyExecutingManualMock = origCurrExecutingManualMock;
this._currentlyExecutingModulePath = lastExecutingModulePath;
Expand Down
33 changes: 25 additions & 8 deletions packages/jest-runtime/src/script_transformer.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export type Options = {|
collectCoverage: boolean,
collectCoverageFrom: Array<Glob>,
collectCoverageOnlyFrom: ?{[key: string]: boolean, __proto__: null},
extraGlobals?: Array<string>,
isCoreModule?: boolean,
isInternalModule?: boolean,
|};
Expand Down Expand Up @@ -313,18 +314,20 @@ export default class ScriptTransformer {
(this._shouldTransform(filename) || instrument);

try {
const extraGlobals = (options && options.extraGlobals) || [];

if (willTransform) {
const transformedSource = this.transformSource(
filename,
content,
instrument,
);

wrappedCode = wrap(transformedSource.code);
wrappedCode = wrap(transformedSource.code, ...extraGlobals);
sourceMapPath = transformedSource.sourceMapPath;
mapCoverage = transformedSource.mapCoverage;
} else {
wrappedCode = wrap(content);
wrappedCode = wrap(content, ...extraGlobals);
}

return {
Expand Down Expand Up @@ -525,11 +528,25 @@ const calcIgnorePatternRegexp = (config: ProjectConfig): ?RegExp => {
return new RegExp(config.transformIgnorePatterns.join('|'));
};

const wrap = content =>
'({"' +
ScriptTransformer.EVAL_RESULT_VARIABLE +
'":function(module,exports,require,__dirname,__filename,global,jest){' +
content +
'\n}});';
const wrap = (content, ...extras) => {
const globals = new Set([
'module',
'exports',
'require',
'__dirname',
'__filename',
'global',
'jest',
...extras,
]);

return (
'({"' +
ScriptTransformer.EVAL_RESULT_VARIABLE +
`":function(${Array.from(globals).join(',')}){` +
content +
'\n}});'
);
};

ScriptTransformer.EVAL_RESULT_VARIABLE = 'Object.<anonymous>';
3 changes: 3 additions & 0 deletions types/Config.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export type InitialOptions = {
detectOpenHandles?: boolean,
displayName?: string,
expand?: boolean,
extraGlobals?: Array<string>,
filter?: Path,
findRelatedTests?: boolean,
forceCoverageMatch?: Array<Glob>,
Expand Down Expand Up @@ -204,6 +205,7 @@ export type GlobalConfig = {|
detectOpenHandles: boolean,
enabledTestsMap: ?{[key: string]: {[key: string]: boolean}},
expand: boolean,
extraGlobals: Array<string>,
filter: ?Path,
findRelatedTests: boolean,
forceExit: boolean,
Expand Down Expand Up @@ -257,6 +259,7 @@ export type ProjectConfig = {|
detectOpenHandles: boolean,
displayName: ?string,
errorOnDeprecated: boolean,
extraGlobals: Array<string>,
filter: ?Path,
forceCoverageMatch: Array<Glob>,
globals: ConfigGlobals,
Expand Down

0 comments on commit a6eff76

Please sign in to comment.