Skip to content

Commit

Permalink
jest.requireActual (jestjs#4260)
Browse files Browse the repository at this point in the history
  • Loading branch information
aaronabramov committed Aug 13, 2017
1 parent 43b6cb9 commit 722dc91
Show file tree
Hide file tree
Showing 5 changed files with 222 additions and 96 deletions.
60 changes: 60 additions & 0 deletions integration_tests/__tests__/jest_require_actual.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @flow
*/

'use strict';

const path = require('path');
const os = require('os');
const skipOnWindows = require('../../scripts/skip_on_windows');
const {cleanup, writeFiles} = require('../utils');
const runJest = require('../runJest');

const DIR = path.resolve(os.tmpdir(), 'jest_require_actual_test');

skipOnWindows.suite();

beforeEach(() => cleanup(DIR));
afterAll(() => cleanup(DIR));

test('understands dependencies using require.requireActual', () => {
writeFiles(DIR, {
'.watchmanconfig': '',
'__tests__/a.test.js': `
const a = require.requireActual('../a');
test('a', () => {});
`,
'__tests__/b.test.js': `test('b', () => {});`,
'a.js': `module.exports = {}`,
'package.json': JSON.stringify({jest: {}}),
});

let stdout;
let stderr;
({stdout, stderr} = runJest(DIR, ['--findRelatedTests', 'a.js']));

expect(stdout).not.toMatch('No tests found');
expect(stderr).toMatch('PASS __tests__/a.test.js');
expect(stderr).not.toMatch('PASS __tests__/b.test.js');

// change to jest.requireActual
writeFiles(DIR, {
'__tests__/a.test.js': `
const a = jest.requireActual('../a');
test('a', () => {});
`,
});

({stderr, stdout} = runJest(DIR, ['--findRelatedTests', 'a.js']));
expect(stdout).not.toMatch('No tests found');
expect(stderr).toMatch('PASS __tests__/a.test.js');
expect(stderr).not.toMatch('PASS __tests__/b.test.js');
});
162 changes: 85 additions & 77 deletions packages/jest-haste-map/src/lib/__tests__/extract_requires.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,100 +6,108 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails oncall+jsinfra
* @flow
*/
'use strict';

const extractRequires = require('../extract_requires');

describe('extractRequires', () => {
it('extracts both requires and imports from code', () => {
const code = `
it('extracts both requires and imports from code', () => {
const code = `
import module1 from 'module1';
const module2 = require('module2');
`;

expect(extractRequires(code)).toEqual(['module1', 'module2']);
});
expect(extractRequires(code)).toEqual(['module1', 'module2']);
});

it('extracts requires in order', () => {
const code = `
it('extracts requires in order', () => {
const code = `
const module1 = require('module1');
const module2 = require('module2');
const module3 = require('module3');
`;

expect(extractRequires(code)).toEqual(['module1', 'module2', 'module3']);
});

it('strips out comments from code', () => {
const code = `// comment const module2 = require('module2');`;

expect(extractRequires(code)).toEqual([]);
});

it('ignores requires in comments', () => {
const code = [
'// const module1 = require("module1");',
'/**',
' * const module2 = require("module2");',
' */',
].join('\n');

expect(extractRequires(code)).toEqual([]);
});

it('ignores requires in comments with Windows line endings', () => {
const code = [
'// const module1 = require("module1");',
'/**',
' * const module2 = require("module2");',
' */',
].join('\r\n');

expect(extractRequires(code)).toEqual([]);
});

it('ignores requires in comments with unicode line endings', () => {
const code = [
'// const module1 = require("module1");\u2028',
'// const module1 = require("module2");\u2029',
'/*\u2028',
'const module2 = require("module3");\u2029',
' */',
].join('');

expect(extractRequires(code)).toEqual([]);
});

it('does not contain duplicates', () => {
const code = `
expect(extractRequires(code)).toEqual(['module1', 'module2', 'module3']);
});

it('strips out comments from code', () => {
const code = `// comment const module2 = require('module2');`;

expect(extractRequires(code)).toEqual([]);
});

it('ignores requires in comments', () => {
const code = [
'// const module1 = require("module1");',
'/**',
' * const module2 = require("module2");',
' */',
].join('\n');

expect(extractRequires(code)).toEqual([]);
});

it('ignores requires in comments with Windows line endings', () => {
const code = [
'// const module1 = require("module1");',
'/**',
' * const module2 = require("module2");',
' */',
].join('\r\n');

expect(extractRequires(code)).toEqual([]);
});

it('ignores requires in comments with unicode line endings', () => {
const code = [
'// const module1 = require("module1");\u2028',
'// const module1 = require("module2");\u2029',
'/*\u2028',
'const module2 = require("module3");\u2029',
' */',
].join('');

expect(extractRequires(code)).toEqual([]);
});

it('does not contain duplicates', () => {
const code = `
const module1 = require('module1');
const module1Dup = require('module1');
`;

expect(extractRequires(code)).toEqual(['module1']);
});

it('ignores type imports', () => {
const code = [
"import type foo from 'bar';",
'import type {',
' bar,',
' baz,',
"} from 'wham'",
].join('\r\n');

expect(extractRequires(code)).toEqual([]);
});

it('ignores type exports', () => {
const code = [
'export type Foo = number;',
'export default {}',
"export * from 'module1'",
].join('\r\n');

expect(extractRequires(code)).toEqual(['module1']);
});
expect(extractRequires(code)).toEqual(['module1']);
});

it('ignores type imports', () => {
const code = [
"import type foo from 'bar';",
'import type {',
' bar,',
' baz,',
"} from 'wham'",
].join('\r\n');

expect(extractRequires(code)).toEqual([]);
});

it('ignores type exports', () => {
const code = [
'export type Foo = number;',
'export default {}',
"export * from 'module1'",
].join('\r\n');

expect(extractRequires(code)).toEqual(['module1']);
});

it('understands require.requireActual', () => {
const code = `require.requireActual('pizza');`;
expect(extractRequires(code)).toEqual(['pizza']);
});

it('understands jest.requireActual', () => {
const code = `jest.requireActual('whiskey');`;
expect(extractRequires(code)).toEqual(['whiskey']);
});
2 changes: 1 addition & 1 deletion packages/jest-haste-map/src/lib/extract_requires.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const lineCommentRe = /\/\/.*/g;
const replacePatterns = {
EXPORT_RE: /(\bexport\s+(?!type )(?:[^'"]+\s+from\s+)??)(['"])([^'"]+)(\2)/g,
IMPORT_RE: /(\bimport\s+(?!type )(?:[^'"]+\s+from\s+)??)(['"])([^'"]+)(\2)/g,
REQUIRE_EXTENSIONS_PATTERN: /(?:^|[^.]\s*)(\b(?:require\s*?\.\s*?(?:requireActual|requireMock)|jest\s*?\.\s*?genMockFromModule)\s*?\(\s*?)([`'"])([^`'"]+)(\2\s*?\))/g,
REQUIRE_EXTENSIONS_PATTERN: /(?:^|[^.]\s*)(\b(?:require\s*?\.\s*?(?:requireActual|requireMock)|jest\s*?\.\s*?(?:requireActual|genMockFromModule))\s*?\(\s*?)([`'"])([^`'"]+)(\2\s*?\))/g,
REQUIRE_RE: /(?:^|[^.]\s*)(\brequire\s*?\(\s*?)([`'"])([^`'"]+)(\2\s*?\))/g,
};

Expand Down
46 changes: 28 additions & 18 deletions packages/jest-runtime/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {Argv} from 'types/Argv';
import type {Glob, Path, ProjectConfig} from 'types/Config';
import type {Environment} from 'types/Environment';
import type {Context} from 'types/Context';
import type {Jest, LocalModuleRequire} from 'types/Jest';
import type {ModuleMap} from 'jest-haste-map';
import type {MockFunctionMetadata, ModuleMocker} from 'types/Mock';

Expand Down Expand Up @@ -524,7 +525,11 @@ class Runtime {
dirname, // __dirname
filename, // __filename
this._environment.global, // global object
this._createJestObjectFor(filename), // jest object
this._createJestObjectFor(
filename,
// $FlowFixMe
(localModule.require: LocalModuleRequire),
), // jest object
);

this._isCurrentlyExecutingManualMock = origCurrExecutingManualMock;
Expand Down Expand Up @@ -637,7 +642,10 @@ class Runtime {
return (this._shouldMockModuleCache[moduleID] = true);
}

_createRequireImplementation(from: Path, options: ?InternalModuleOptions) {
_createRequireImplementation(
from: Path,
options: ?InternalModuleOptions,
): LocalModuleRequire {
const moduleRequire =
options && options.isInternalModule
? (moduleName: string) => this.requireInternalModule(from, moduleName)
Expand All @@ -650,14 +658,14 @@ class Runtime {
return moduleRequire;
}

_createJestObjectFor(from: Path) {
_createJestObjectFor(from: Path, localRequire: LocalModuleRequire): Jest {
const disableAutomock = () => {
this._shouldAutoMock = false;
return runtime;
return jestObject;
};
const enableAutomock = () => {
this._shouldAutoMock = true;
return runtime;
return jestObject;
};
const unmock = (moduleName: string) => {
const moduleID = this._resolver.getModuleID(
Expand All @@ -666,7 +674,7 @@ class Runtime {
moduleName,
);
this._explicitShouldMock[moduleID] = false;
return runtime;
return jestObject;
};
const deepUnmock = (moduleName: string) => {
const moduleID = this._resolver.getModuleID(
Expand All @@ -676,12 +684,12 @@ class Runtime {
);
this._explicitShouldMock[moduleID] = false;
this._transitiveShouldMock[moduleID] = false;
return runtime;
return jestObject;
};
const mock = (
moduleName: string,
mockFactory: Object,
options: {virtual: boolean},
mockFactory?: Object,
options?: {virtual: boolean},
) => {
if (mockFactory !== undefined) {
return setMockFactory(moduleName, mockFactory, options);
Expand All @@ -693,31 +701,31 @@ class Runtime {
moduleName,
);
this._explicitShouldMock[moduleID] = true;
return runtime;
return jestObject;
};
const setMockFactory = (moduleName, mockFactory, options) => {
this.setMock(from, moduleName, mockFactory, options);
return runtime;
return jestObject;
};
const clearAllMocks = () => {
this.clearAllMocks();
return runtime;
return jestObject;
};
const resetAllMocks = () => {
this.resetAllMocks();
return runtime;
return jestObject;
};
const useFakeTimers = () => {
this._environment.fakeTimers.useFakeTimers();
return runtime;
return jestObject;
};
const useRealTimers = () => {
this._environment.fakeTimers.useRealTimers();
return runtime;
return jestObject;
};
const resetModules = () => {
this.resetModules();
return runtime;
return jestObject;
};
const fn = this._moduleMocker.fn.bind(this._moduleMocker);
const spyOn = this._moduleMocker.spyOn.bind(this._moduleMocker);
Expand All @@ -728,9 +736,10 @@ class Runtime {
: (this._environment.global[
Symbol.for('TEST_TIMEOUT_SYMBOL')
] = timeout);
return jestObject;
};

const runtime = {
const jestObject = {
addMatchers: (matchers: Object) =>
this._environment.global.jasmine.addMatchers(matchers),

Expand All @@ -751,6 +760,7 @@ class Runtime {
isMockFunction: this._moduleMocker.isMockFunction,

mock,
requireActual: localRequire.requireActual,
resetAllMocks,
resetModuleRegistry: resetModules,
resetModules,
Expand All @@ -773,7 +783,7 @@ class Runtime {
useFakeTimers,
useRealTimers,
};
return runtime;
return jestObject;
}
}

Expand Down
Loading

0 comments on commit 722dc91

Please sign in to comment.