Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement jest.requireActual #4260

Merged
merged 1 commit into from
Aug 13, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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