Skip to content

Commit

Permalink
Add support for custom dependency extractors
Browse files Browse the repository at this point in the history
  • Loading branch information
rubennorte committed Nov 1, 2018
1 parent b5310c1 commit 933b451
Show file tree
Hide file tree
Showing 21 changed files with 149 additions and 17 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
- `[babel-preset-jest]` [**BREAKING**] Export a function instead of an object for Babel 7 compatibility ([#7203](https://github.com/facebook/jest/pull/7203))
- `[expect]` Check constructor equality in .toStrictEqual() ([#7005](https://github.com/facebook/jest/pull/7005))
- `[jest-util]` Add `jest.getTimerCount()` to get the count of scheduled fake timers ([#7285](https://github.com/facebook/jest/pull/7285))
- `[jest-config]` Add `dependencyExtractor` option to use a custom module to extract dependencies from files ([#7313](https://github.com/facebook/jest/pull/7313))

### Fixes

Expand Down
8 changes: 8 additions & 0 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,14 @@ Jest will fail if:
- The `./src/api/very-important-module.js` file has less than 100% coverage.
- Every remaining file combined has less than 50% coverage (`global`).

### `dependencyExtractor` [string]

Default: `undefined`

This option allows the use of a custom dependency extractor. It must be a node module that exports a function expecting a string as the first argument for the code to analyze and Jest's dependency extractor as the second argument (in case you only want to extend it).

The function should return a `Set` with the dependencies found in the code.

### `errorOnDeprecated` [boolean]

Default: `false`
Expand Down
1 change: 1 addition & 0 deletions e2e/__tests__/__snapshots__/show_config.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ exports[`--showConfig outputs config info and exits 1`] = `
\\"/node_modules/\\"
],
\\"cwd\\": \\"<<REPLACED_ROOT_DIR>>\\",
\\"dependencyExtractor\\": null,
\\"detectLeaks\\": false,
\\"detectOpenHandles\\": false,
\\"errorOnDeprecated\\": false,
Expand Down
40 changes: 40 additions & 0 deletions e2e/__tests__/find_related_files.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,46 @@ describe('--findRelatedTests flag', () => {
expect(stderr).toMatch(summaryMsg);
});

test('runs tests related to filename with a custom dependency extractor', () => {
writeFiles(DIR, {
'.watchmanconfig': '',
'__tests__/test.test.js': `
const dynamicImport = path => Promise.resolve(require(path));
test('a', () => dynamicImport('../a').then(a => {
expect(a.foo).toBe(5);
}));
`,
'a.js': 'module.exports = {foo: 5};',
'dependencyExtractor.js': `
const DYNAMIC_IMPORT_RE = /(?:^|[^.]\\s*)(\\bdynamicImport\\s*?\\(\\s*?)([\`'"])([^\`'"]+)(\\2\\s*?\\))/g;
module.exports = function dependencyExtractor(code) {
const dependencies = new Set();
const addDependency = (match, pre, quot, dep, post) => {
dependencies.add(dep);
return match;
};
code.replace(DYNAMIC_IMPORT_RE, addDependency);
return dependencies;
};
`,
'package.json': JSON.stringify({
jest: {
dependencyExtractor: '<rootDir>/dependencyExtractor.js',
testEnvironment: 'node',
},
}),
});

const {stdout} = runJest(DIR, ['a.js']);
expect(stdout).toMatch('');

const {stderr} = runJest(DIR, ['--findRelatedTests', 'a.js']);
expect(stderr).toMatch('PASS __tests__/test.test.js');

const summaryMsg = 'Ran all test suites related to files matching /a.js/i.';
expect(stderr).toMatch(summaryMsg);
});

test('generates coverage report for filename', () => {
writeFiles(DIR, {
'.watchmanconfig': '',
Expand Down
1 change: 1 addition & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ module.exports = {
'/packages/jest-cli/src/__tests__/__fixtures__/',
'/packages/jest-cli/src/lib/__tests__/fixtures/',
'/packages/jest-haste-map/src/__tests__/haste_impl.js',
'/packages/jest-haste-map/src/__tests__/dependencyExtractor.js',
'/packages/jest-resolve-dependencies/src/__tests__/__fixtures__/',
'/packages/jest-runtime/src/__tests__/defaultResolver.js',
'/packages/jest-runtime/src/__tests__/module_dir/',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ module.exports = {
// An object that configures minimum threshold enforcement for coverage results
// coverageThreshold: null,
// A path to a custom dependency extractor
// dependencyExtractor: null,
// Make calling deprecated APIs throw helpful error messages
// errorOnDeprecated: false,
Expand Down
1 change: 1 addition & 0 deletions packages/jest-config/src/Defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export default ({
coverageReporters: ['json', 'text', 'lcov', 'clover'],
coverageThreshold: null,
cwd: process.cwd(),
dependencyExtractor: null,
detectLeaks: false,
detectOpenHandles: false,
errorOnDeprecated: false,
Expand Down
1 change: 1 addition & 0 deletions packages/jest-config/src/Descriptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export default ({
'A list of reporter names that Jest uses when writing coverage reports',
coverageThreshold:
'An object that configures minimum threshold enforcement for coverage results',
dependencyExtractor: 'A path to a custom dependency extractor',
errorOnDeprecated:
'Make calling deprecated APIs throw helpful error messages',
forceCoverageMatch:
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 @@ -40,6 +40,7 @@ export default ({
statements: 100,
},
},
dependencyExtractor: '<rootDir>/dependencyExtractor.js',
displayName: 'project-name',
errorOnDeprecated: false,
expand: false,
Expand Down
1 change: 1 addition & 0 deletions packages/jest-config/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ const groupOptions = (
clearMocks: options.clearMocks,
coveragePathIgnorePatterns: options.coveragePathIgnorePatterns,
cwd: options.cwd,
dependencyExtractor: options.dependencyExtractor,
detectLeaks: options.detectLeaks,
detectOpenHandles: options.detectOpenHandles,
displayName: options.displayName,
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 @@ -483,6 +483,7 @@ export default function normalize(options: InitialOptions, argv: Argv) {
replaceRootDirInPath(options.rootDir, options[key]),
);
break;
case 'dependencyExtractor':
case 'globalSetup':
case 'globalTeardown':
case 'moduleLoader':
Expand Down
31 changes: 31 additions & 0 deletions packages/jest-haste-map/src/__tests__/dependencyExtractor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/**
* Copyright (c) 2014-present, Facebook, Inc. 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 blockCommentRe = /\/\*[^]*?\*\//g;
const lineCommentRe = /\/\/.*/g;
const LOAD_MODULE_RE = /(?:^|[^.]\s*)(\bloadModule\s*?\(\s*?)([`'"])([^`'"]+)(\2\s*?\))/g;

module.exports = function dependencyExtractor(
code,
defaultDependencyExtractor,
) {
const dependencies = defaultDependencyExtractor(code);

const addDependency = (match, pre, quot, dep, post) => {
dependencies.add(dep);
return match;
};

code
.replace(blockCommentRe, '')
.replace(lineCommentRe, '')
.replace(LOAD_MODULE_RE, addDependency);

return dependencies;
};
8 changes: 8 additions & 0 deletions packages/jest-haste-map/src/__tests__/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -885,8 +885,11 @@ describe('HasteMap', () => {

it('distributes work across workers', () => {
const jestWorker = require('jest-worker');
const path = require('path');
const dependencyExtractor = path.join(__dirname, 'dependencyExtractor.js');
return new HasteMap(
Object.assign({}, defaultConfig, {
dependencyExtractor,
hasteImplModulePath: undefined,
maxWorkers: 4,
}),
Expand All @@ -902,6 +905,7 @@ describe('HasteMap', () => {
{
computeDependencies: true,
computeSha1: false,
dependencyExtractor,
filePath: '/project/fruits/Banana.js',
hasteImplModulePath: undefined,
rootDir: '/project',
Expand All @@ -911,6 +915,7 @@ describe('HasteMap', () => {
{
computeDependencies: true,
computeSha1: false,
dependencyExtractor,
filePath: '/project/fruits/Pear.js',
hasteImplModulePath: undefined,
rootDir: '/project',
Expand All @@ -920,6 +925,7 @@ describe('HasteMap', () => {
{
computeDependencies: true,
computeSha1: false,
dependencyExtractor,
filePath: '/project/fruits/Strawberry.js',
hasteImplModulePath: undefined,
rootDir: '/project',
Expand All @@ -929,6 +935,7 @@ describe('HasteMap', () => {
{
computeDependencies: true,
computeSha1: false,
dependencyExtractor,
filePath: '/project/fruits/__mocks__/Pear.js',
hasteImplModulePath: undefined,
rootDir: '/project',
Expand All @@ -938,6 +945,7 @@ describe('HasteMap', () => {
{
computeDependencies: true,
computeSha1: false,
dependencyExtractor,
filePath: '/project/vegetables/Melon.js',
hasteImplModulePath: undefined,
rootDir: '/project',
Expand Down
16 changes: 15 additions & 1 deletion packages/jest-haste-map/src/__tests__/worker.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ describe('worker', () => {
'/project/fruits/Pear.js': `
const Banana = require("Banana");
const Strawberry = require('Strawberry');
const Lime = loadModule('Lime');
`,
'/project/fruits/Strawberry.js': `
// Strawberry!
Expand Down Expand Up @@ -95,6 +96,19 @@ describe('worker', () => {
});
});

it('accepts a custom dependency extractor', async () => {
expect(
await worker({
computeDependencies: true,
dependencyExtractor: path.join(__dirname, 'dependencyExtractor.js'),
filePath: '/project/fruits/Pear.js',
rootDir,
}),
).toEqual({
dependencies: ['Banana', 'Strawberry', 'Lime'],
});
});

it('delegates to hasteImplModulePath for getting the id', async () => {
expect(
await worker({
Expand Down Expand Up @@ -179,7 +193,7 @@ describe('worker', () => {
filePath: '/project/fruits/Pear.js',
rootDir,
}),
).toEqual({sha1: '0cb0930919e068f146da84d9a0ad0182e4bdb673'});
).toEqual({sha1: 'c7a7a68a1c8aaf452669dd2ca52ac4a434d25552'});

await expect(
getSha1({computeSha1: true, filePath: '/i/dont/exist.js', rootDir}),
Expand Down
5 changes: 5 additions & 0 deletions packages/jest-haste-map/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ type Options = {
computeDependencies?: boolean,
computeSha1?: boolean,
console?: Console,
dependencyExtractor?: string,
extensions: Array<string>,
forceNodeFilesystemAPI?: boolean,
hasteImplModulePath?: string,
Expand All @@ -75,6 +76,7 @@ type InternalOptions = {
cacheDirectory: string,
computeDependencies: boolean,
computeSha1: boolean,
dependencyExtractor?: string,
extensions: Array<string>,
forceNodeFilesystemAPI: boolean,
hasteImplModulePath?: string,
Expand Down Expand Up @@ -233,6 +235,7 @@ class HasteMap extends EventEmitter {
? true
: options.computeDependencies,
computeSha1: options.computeSha1 || false,
dependencyExtractor: options.dependencyExtractor,
extensions: options.extensions,
forceNodeFilesystemAPI: !!options.forceNodeFilesystemAPI,
hasteImplModulePath: options.hasteImplModulePath,
Expand Down Expand Up @@ -504,6 +507,7 @@ class HasteMap extends EventEmitter {
.getSha1({
computeDependencies: this._options.computeDependencies,
computeSha1,
dependencyExtractor: this._options.dependencyExtractor,
filePath,
hasteImplModulePath: this._options.hasteImplModulePath,
rootDir,
Expand Down Expand Up @@ -567,6 +571,7 @@ class HasteMap extends EventEmitter {
.worker({
computeDependencies: this._options.computeDependencies,
computeSha1,
dependencyExtractor: this._options.dependencyExtractor,
filePath,
hasteImplModulePath: this._options.hasteImplModulePath,
rootDir,
Expand Down
Loading

0 comments on commit 933b451

Please sign in to comment.