diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a386a795440..821fecb28ca5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ - `[jest-environment-node]` Implement `compileFunction` ([#9140](https://github.com/facebook/jest/pull/9140)) - `[@jest/fake-timers]` Add Lolex as implementation of fake timers ([#8897](https://github.com/facebook/jest/pull/8897)) - `[jest-get-type]` Add `BigInt` support. ([#8382](https://github.com/facebook/jest/pull/8382)) +- `[jest-haste-map]` Enable crawling for symlinked test files ([#9350](https://github.com/facebook/jest/issues/9350)) - `[jest-matcher-utils]` Add `BigInt` support to `ensureNumbers` `ensureActualIsNumber`, `ensureExpectedIsNumber` ([#8382](https://github.com/facebook/jest/pull/8382)) - `[jest-reporters]` Export utils for path formatting ([#9162](https://github.com/facebook/jest/pull/9162)) - `[jest-runner]` Warn if a worker had to be force exited ([#8206](https://github.com/facebook/jest/pull/8206)) diff --git a/e2e/Utils.ts b/e2e/Utils.ts index a98e2e6bca39..f294fbb09138 100644 --- a/e2e/Utils.ts +++ b/e2e/Utils.ts @@ -90,6 +90,25 @@ export const writeFiles = ( }); }; +export const writeSymlinks = ( + directory: string, + symlinks: {[existingFile: string]: string}, +) => { + createDirectory(directory); + Object.keys(symlinks).forEach(fileOrPath => { + const symLinkPath = symlinks[fileOrPath]; + const dirname = path.dirname(symLinkPath); + + if (dirname !== '/') { + createDirectory(path.join(directory, dirname)); + } + fs.symlinkSync( + path.resolve(directory, ...fileOrPath.split('/')), + path.resolve(directory, ...symLinkPath.split('/')), + ); + }); +}; + const NUMBER_OF_TESTS_TO_FORCE_USING_WORKERS = 25; /** * Forces Jest to use workers by generating many test files to run. diff --git a/e2e/__tests__/crawlSymlinks.test.ts b/e2e/__tests__/crawlSymlinks.test.ts new file mode 100644 index 000000000000..6a6477863468 --- /dev/null +++ b/e2e/__tests__/crawlSymlinks.test.ts @@ -0,0 +1,43 @@ +/** + * 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. + */ + +import {tmpdir} from 'os'; +import * as path from 'path'; +import * as fs from 'fs'; +import {writeFiles, writeSymlinks} from '../Utils'; +import runJest from '../runJest'; +const DIR = path.resolve(tmpdir(), 'crawl-symlinks-test'); + +test('Node crawler picks up symlinked files', () => { + if (fs.existsSync(DIR)) { + fs.rmdirSync(DIR, {recursive: true}); + } + + writeFiles(DIR, { + 'package.json': ` + { + "jest": { + "testMatch": ["/test-files/test.js"] + } + } + `, + 'symlinked-files/test.js': ` + test('1+1', () => { + expect(1).toBe(1); + }); + `, + }); + + writeSymlinks(DIR, { + 'symlinked-files/test.js': 'test-files/test.js', + }); + + const {stdout, stderr, exitCode} = runJest(DIR, ['--no-watchman']); + expect(stderr).toContain('Test Suites: 1 passed, 1 total'); + expect(stdout).toEqual(''); + expect(exitCode).toEqual(0); +}); diff --git a/packages/jest-haste-map/src/crawlers/__tests__/node.test.js b/packages/jest-haste-map/src/crawlers/__tests__/node.test.js index 69d3c3d6ca73..97e7094bdebf 100644 --- a/packages/jest-haste-map/src/crawlers/__tests__/node.test.js +++ b/packages/jest-haste-map/src/crawlers/__tests__/node.test.js @@ -118,8 +118,13 @@ describe('node crawler', () => { expect(childProcess.spawn).lastCalledWith('find', [ '/project/fruits', '/project/vegtables', + '(', '-type', 'f', + '-o', + '-type', + 'l', + ')', '(', '-iname', '*.js', diff --git a/packages/jest-haste-map/src/crawlers/node.ts b/packages/jest-haste-map/src/crawlers/node.ts index 6e6b6b5d4c9d..13c13a0511c2 100644 --- a/packages/jest-haste-map/src/crawlers/node.ts +++ b/packages/jest-haste-map/src/crawlers/node.ts @@ -48,13 +48,32 @@ function find( fs.lstat(file, (err, stat) => { activeCalls--; - if (!err && stat && !stat.isSymbolicLink()) { - if (stat.isDirectory()) { - search(file); + if (!err && stat) { + if (stat.isSymbolicLink()) { + activeCalls++; + fs.stat(file, (err, stat) => { + activeCalls--; + if (!err && stat) { + if (stat.isFile()) { + const ext = path.extname(file).substr(1); + if (extensions.indexOf(ext) !== -1) { + result.push([file, stat.mtime.getTime(), stat.size]); + } + } + } + + if (activeCalls === 0) { + callback(result); + } + }); } else { - const ext = path.extname(file).substr(1); - if (extensions.indexOf(ext) !== -1) { - result.push([file, stat.mtime.getTime(), stat.size]); + if (stat.isDirectory()) { + search(file); + } else { + const ext = path.extname(file).substr(1); + if (extensions.indexOf(ext) !== -1) { + result.push([file, stat.mtime.getTime(), stat.size]); + } } } } @@ -84,7 +103,8 @@ function findNative( callback: Callback, ): void { const args = Array.from(roots); - args.push('-type', 'f'); + args.push('(', '-type', 'f', '-o', '-type', 'l', ')'); + if (extensions.length) { args.push('('); } @@ -121,7 +141,8 @@ function findNative( } else { lines.forEach(path => { fs.stat(path, (err, stat) => { - if (!err && stat) { + // Filter out symlinks that describe directories + if (!err && stat && !stat.isDirectory()) { result.push([path, stat.mtime.getTime(), stat.size]); } if (--count === 0) {