From ce9e83c01e40ec977f9061c92fa82d82be8633a4 Mon Sep 17 00:00:00 2001 From: Dan Muller Date: Thu, 29 Oct 2020 15:43:00 +0000 Subject: [PATCH 01/13] feat(jest-haste-map): Enable crawling for symlink test files --- CHANGELOG.md | 1 + docs/Configuration.md | 2 + e2e/__tests__/crawlSymlinks.test.ts | 114 ++++++++++++++++++ packages/jest-config/src/ValidConfig.ts | 1 + .../src/__tests__/index.test.js | 13 ++ packages/jest-haste-map/src/crawlers/node.ts | 21 +++- packages/jest-haste-map/src/index.ts | 38 ++++++ packages/jest-haste-map/src/types.ts | 1 + packages/jest-runtime/src/index.ts | 1 + packages/jest-types/src/Config.ts | 2 + 10 files changed, 189 insertions(+), 5 deletions(-) create mode 100644 e2e/__tests__/crawlSymlinks.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index c652a32acd75..31aa3fa9695b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Features - `[jest-core]` Add `findRelatedTests` and `nonFlagArgs` in allowed config options for `updateConfigAndRun` in watch plugins ([#10659](https://github.com/facebook/jest/pull/10659)) +- `[jest-haste-map]` Add `enableSymlinks` configuration option to follow symlinks for test files ([#9350](https://github.com/facebook/jest/issues/9350)) ### Fixes diff --git a/docs/Configuration.md b/docs/Configuration.md index c34bebedfcdf..b6e304d67d8b 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -476,6 +476,8 @@ type HasteConfig = { computeSha1?: boolean; // The platform to use as the default, e.g. 'ios'. defaultPlatform?: string | null; + // Whether to follow symlinks when crawling for files. + enableSymlinks?: boolean; // Path to a custom implementation of Haste. hasteImplModulePath?: string; // All platforms to target, e.g ['ios', 'android']. diff --git a/e2e/__tests__/crawlSymlinks.test.ts b/e2e/__tests__/crawlSymlinks.test.ts new file mode 100644 index 000000000000..7a0dfefdc01e --- /dev/null +++ b/e2e/__tests__/crawlSymlinks.test.ts @@ -0,0 +1,114 @@ +/** + * 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 runJest from '../runJest'; +import {cleanup, writeFiles, writeSymlinks} from '../Utils'; + +const DIR = path.resolve(tmpdir(), 'crawl-symlinks-test'); + +beforeEach(() => { + cleanup(DIR); +}); + +afterEach(() => { + cleanup(DIR); +}); + +function init( + extraFiles: { + [filename: string]: string; + } = {}, +) { + writeFiles(DIR, { + 'package.json': JSON.stringify({ + jest: { + testMatch: ['/test-files/test.js'], + }, + }), + 'symlinked-files/test.js': ` + test('1+1', () => { + expect(1).toBe(1); + }); + `, + ...extraFiles, + }); + + writeSymlinks(DIR, { + 'symlinked-files/test.js': 'test-files/test.js', + }); +} + +test('Node crawler picks up symlinked files when option is set as flag', () => { + // Symlinks are only enabled on windows with developer mode. + // https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/ + if (process.platform === 'win32') { + return; + } + + init(); + const {stdout, stderr, exitCode} = runJest(DIR, [ + '--haste={"enableSymlinks": true}', + ]); + + expect(stdout).toEqual(''); + expect(stderr).toContain('Test Suites: 1 passed, 1 total'); + expect(exitCode).toEqual(0); +}); + +test('Node crawler does not pick up symlinked files by default', () => { + init(); + const {stdout, stderr, exitCode} = runJest(DIR, []); + expect(stdout).toContain('No tests found, exiting with code 1'); + expect(stderr).toEqual(''); + expect(exitCode).toEqual(1); +}); + +test('Warns if --enableSymlinks passed but not set in .watchmanconfig', () => { + // Symlinks are only enabled on windows with developer mode. + // https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/ + if (process.platform === 'win32') { + return; + } + + init({'.watchmanconfig': ''}); + + const {stdout, stderr, exitCode} = runJest(DIR, [ + '--haste={"enableSymlinks": true}', + ]); + + expect(stdout).toEqual( + `jest-haste-map: --enableSymlinks was passed but symlink ` + + `crawling is not enabled in .watchmanconfig. + To enable symlink crawling in .watchmanconfig set \"watch_symlinks\": true.`, + ); + + expect(stderr).toContain('Test Suites: 1 passed, 1 total'); + expect(exitCode).toEqual(0); +}); + +test('Warns if watch_symlinks true in .watchmanconfig but not passed', () => { + // Symlinks are only enabled on windows with developer mode. + // https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/ + if (process.platform === 'win32') { + return; + } + + init({'.watchmanconfig': JSON.stringify({watch_symlinks: true})}); + + const {stdout, stderr, exitCode} = runJest(DIR, []); + + expect(stdout).toContain( + 'jest-haste-map: watch_symlinks is enabled in .watchmanconfig but ' + + '--enableSymlinks was not passed to Jest as a flag.', + ); + expect(stdout).toContain('No tests found, exiting with code 1'); + expect(stderr).toEqual(''); + + expect(exitCode).toEqual(1); +}); diff --git a/packages/jest-config/src/ValidConfig.ts b/packages/jest-config/src/ValidConfig.ts index 712f1fb967b5..7d0ba0e6c075 100644 --- a/packages/jest-config/src/ValidConfig.ts +++ b/packages/jest-config/src/ValidConfig.ts @@ -54,6 +54,7 @@ const initialOptions: Config.InitialOptions = { haste: { computeSha1: true, defaultPlatform: 'ios', + enableSymlinks: false, hasteImplModulePath: '/haste_impl.js', platforms: ['ios', 'android'], throwOnModuleCollision: false, diff --git a/packages/jest-haste-map/src/__tests__/index.test.js b/packages/jest-haste-map/src/__tests__/index.test.js index 6dba04a9a81e..2e151abeb5c5 100644 --- a/packages/jest-haste-map/src/__tests__/index.test.js +++ b/packages/jest-haste-map/src/__tests__/index.test.js @@ -88,6 +88,19 @@ let mockChangedFiles; let mockFs; jest.mock('graceful-fs', () => ({ + existsSync: jest.fn(path => { + // A file change can be triggered by writing into the + // mockChangedFiles object. + if (mockChangedFiles && path in mockChangedFiles) { + return true; + } + + if (mockFs[path]) { + return true; + } + + return false; + }), readFileSync: jest.fn((path, options) => { // A file change can be triggered by writing into the // mockChangedFiles object. diff --git a/packages/jest-haste-map/src/crawlers/node.ts b/packages/jest-haste-map/src/crawlers/node.ts index ef04a2762b42..488cc6739378 100644 --- a/packages/jest-haste-map/src/crawlers/node.ts +++ b/packages/jest-haste-map/src/crawlers/node.ts @@ -61,6 +61,7 @@ function find( extensions: Array, ignore: IgnoreMatcher, callback: Callback, + enableSymlinks?: boolean, ): void { const result: Result = []; let activeCalls = 0; @@ -98,7 +99,9 @@ function find( activeCalls++; - fs.lstat(file, (err, stat) => { + const stat = enableSymlinks ? fs.stat : fs.lstat; + + stat(file, (err, stat) => { activeCalls--; // This logic is unnecessary for node > v10.10, but leaving it in @@ -138,9 +141,15 @@ function findNative( extensions: Array, ignore: IgnoreMatcher, callback: Callback, + enableSymlinks: boolean, ): void { const args = Array.from(roots); - args.push('-type', 'f'); + if (enableSymlinks) { + args.push('(', '-type', 'f', '-o', '-type', 'l', ')'); + } else { + args.push('-type', 'f'); + } + if (extensions.length) { args.push('('); } @@ -177,7 +186,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) { @@ -201,6 +211,7 @@ export = async function nodeCrawl( forceNodeFilesystemAPI, ignore, rootDir, + enableSymlinks, roots, } = options; @@ -231,9 +242,9 @@ export = async function nodeCrawl( }; if (useNativeFind) { - findNative(roots, extensions, ignore, callback); + findNative(roots, extensions, ignore, callback, enableSymlinks); } else { - find(roots, extensions, ignore, callback); + find(roots, extensions, ignore, callback, enableSymlinks); } }); }; diff --git a/packages/jest-haste-map/src/index.ts b/packages/jest-haste-map/src/index.ts index c7d82bf36c0a..36e9bb24308f 100644 --- a/packages/jest-haste-map/src/index.ts +++ b/packages/jest-haste-map/src/index.ts @@ -13,6 +13,7 @@ import {EventEmitter} from 'events'; import {tmpdir} from 'os'; import * as path from 'path'; import type {Stats} from 'graceful-fs'; +import {existsSync, readFileSync} from 'graceful-fs'; import {NodeWatcher, Watcher as SaneWatcher} from 'sane'; import type {Config} from '@jest/types'; import {escapePathForRegex} from 'jest-regex-util'; @@ -59,6 +60,7 @@ type Options = { computeSha1?: boolean; console?: Console; dependencyExtractor?: string | null; + enableSymlinks?: boolean; extensions: Array; forceNodeFilesystemAPI?: boolean; hasteImplModulePath?: string; @@ -82,6 +84,7 @@ type InternalOptions = { computeDependencies: boolean; computeSha1: boolean; dependencyExtractor: string | null; + enableSymlinks: boolean; extensions: Array; forceNodeFilesystemAPI: boolean; hasteImplModulePath?: string; @@ -234,6 +237,7 @@ class HasteMap extends EventEmitter { : options.computeDependencies, computeSha1: options.computeSha1 || false, dependencyExtractor: options.dependencyExtractor || null, + enableSymlinks: options.enableSymlinks || false, extensions: options.extensions, forceNodeFilesystemAPI: !!options.forceNodeFilesystemAPI, hasteImplModulePath: options.hasteImplModulePath, @@ -738,6 +742,7 @@ class HasteMap extends EventEmitter { const crawlerOptions: CrawlerOptions = { computeSha1: options.computeSha1, data: hasteMap, + enableSymlinks: options.enableSymlinks, extensions: options.extensions, forceNodeFilesystemAPI: options.forceNodeFilesystemAPI, ignore, @@ -745,6 +750,39 @@ class HasteMap extends EventEmitter { roots: options.roots, }; + const watchmanConfigPath = path.join(options.rootDir, '.watchmanconfig'); + if (existsSync(watchmanConfigPath)) { + try { + const configStr = readFileSync(watchmanConfigPath, 'utf8') + .toString() + .trim(); + const watchmanConfig = configStr === '' ? {} : JSON.parse(configStr); + const watchSymlinks = Boolean(watchmanConfig['watch_symlinks']); + + // Automatically enable symlink crawling in node crawler if watchman is + // set up to crawl symlinks. + if (watchSymlinks && !crawlerOptions.enableSymlinks) { + this._console.warn( + `jest-haste-map: watch_symlinks is enabled in .watchmanconfig ` + + `but --enableSymlinks was not passed to Jest as a flag. ` + + ` This will result in different behavior when watchman ` + + `is enabled or disabled`, + ); + } else if (crawlerOptions.enableSymlinks) { + this._console.warn( + `jest-haste-map: --enableSymlinks was passed but symlink ` + + `crawling is not enabled in .watchmanconfig.\n` + + ` To enable symlink crawling in .watchmanconfig set ` + + `"watch_symlinks": true.`, + ); + } + } catch (error) { + this._console.warn( + `jest-haste-map: Failed to parse .watchmanconfig.\n Original Error: ${error}`, + ); + } + } + const retry = (error: Error) => { if (crawl === watchmanCrawl) { this._console.warn( diff --git a/packages/jest-haste-map/src/types.ts b/packages/jest-haste-map/src/types.ts index 14a08bac856e..810115513fcc 100644 --- a/packages/jest-haste-map/src/types.ts +++ b/packages/jest-haste-map/src/types.ts @@ -30,6 +30,7 @@ export type WorkerMetadata = { export type CrawlerOptions = { computeSha1: boolean; + enableSymlinks: boolean; data: InternalHasteMap; extensions: Array; forceNodeFilesystemAPI: boolean; diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index f9c0cb9ac9c4..ff3cdf640312 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -321,6 +321,7 @@ class Runtime { computeSha1: config.haste.computeSha1, console: options && options.console, dependencyExtractor: config.dependencyExtractor, + enableSymlinks: config.haste.enableSymlinks, extensions: [Snapshot.EXTENSION].concat(config.moduleFileExtensions), hasteImplModulePath: config.haste.hasteImplModulePath, ignorePattern, diff --git a/packages/jest-types/src/Config.ts b/packages/jest-types/src/Config.ts index cf48b24c66d6..fb9e1cbfc4c8 100644 --- a/packages/jest-types/src/Config.ts +++ b/packages/jest-types/src/Config.ts @@ -22,6 +22,8 @@ export type HasteConfig = { computeSha1?: boolean; /** The platform to use as the default, e.g. 'ios'. */ defaultPlatform?: string | null; + /** Whether to follow symlinks when crawling for files. */ + enableSymlinks?: boolean; /** Path to a custom implementation of Haste. */ hasteImplModulePath?: string; /** All platforms to target, e.g ['ios', 'android']. */ From c7d7f6a490a1558910920f1b0e2e8f44fb183324 Mon Sep 17 00:00:00 2001 From: mrmeku Date: Thu, 29 Oct 2020 12:09:02 -0600 Subject: [PATCH 02/13] Update docs/Configuration.md Co-authored-by: Simen Bekkhus --- docs/Configuration.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index b6e304d67d8b..f5234bc0b541 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -472,17 +472,17 @@ This will be used to configure the behavior of `jest-haste-map`, Jest's internal ```ts type HasteConfig = { - // Whether to hash files using SHA-1. + /** Whether to hash files using SHA-1. */ computeSha1?: boolean; - // The platform to use as the default, e.g. 'ios'. + /** The platform to use as the default, e.g. 'ios'. */ defaultPlatform?: string | null; - // Whether to follow symlinks when crawling for files. + /** Whether to follow symlinks when crawling for files. */ enableSymlinks?: boolean; - // Path to a custom implementation of Haste. + /** Path to a custom implementation of Haste. */ hasteImplModulePath?: string; - // All platforms to target, e.g ['ios', 'android']. + /** All platforms to target, e.g ['ios', 'android']. */ platforms?: Array; - // Whether to throw on error on module collision. + /** Whether to throw on error on module collision. */ throwOnModuleCollision?: boolean; }; ``` From c87128c1fd6ee6cbf0e7477a8b9b4a007f9a4452 Mon Sep 17 00:00:00 2001 From: mrmeku Date: Fri, 30 Oct 2020 10:03:46 -0600 Subject: [PATCH 03/13] Update packages/jest-haste-map/src/index.ts Co-authored-by: Simen Bekkhus --- packages/jest-haste-map/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-haste-map/src/index.ts b/packages/jest-haste-map/src/index.ts index 36e9bb24308f..bb7b1276cd46 100644 --- a/packages/jest-haste-map/src/index.ts +++ b/packages/jest-haste-map/src/index.ts @@ -765,7 +765,7 @@ class HasteMap extends EventEmitter { this._console.warn( `jest-haste-map: watch_symlinks is enabled in .watchmanconfig ` + `but --enableSymlinks was not passed to Jest as a flag. ` + - ` This will result in different behavior when watchman ` + + `This will result in different behavior when watchman ` + `is enabled or disabled`, ); } else if (crawlerOptions.enableSymlinks) { From 766b679b33800303ddd5e7452997e350a5ec03e8 Mon Sep 17 00:00:00 2001 From: Dan Muller Date: Fri, 30 Oct 2020 16:20:16 +0000 Subject: [PATCH 04/13] Add watchman variant of tests --- e2e/Utils.ts | 16 +++ e2e/__tests__/crawlSymlinks.test.ts | 140 +++++++++++++++------------ packages/jest-haste-map/src/index.ts | 2 +- 3 files changed, 96 insertions(+), 62 deletions(-) diff --git a/e2e/Utils.ts b/e2e/Utils.ts index c8d20585e834..96a4729946d1 100644 --- a/e2e/Utils.ts +++ b/e2e/Utils.ts @@ -290,3 +290,19 @@ export const testIfHg = (...args: Parameters) => { test.skip(...args); } }; + +// Certain environments (like CITGM and GH Actions) do not come with watchman installed +let watchmanIsInstalled: boolean | null = null; + +export const testIfWatchman = (...args: Parameters) => { + if (watchmanIsInstalled === null) { + watchmanIsInstalled = which.sync('watchman', {nothrow: true}) !== null; + } + + if (watchmanIsInstalled) { + test(...args); + } else { + console.warn('watchman is not installed - skipping some tests'); + test.skip(...args); + } +}; diff --git a/e2e/__tests__/crawlSymlinks.test.ts b/e2e/__tests__/crawlSymlinks.test.ts index 7a0dfefdc01e..0802972a0d9b 100644 --- a/e2e/__tests__/crawlSymlinks.test.ts +++ b/e2e/__tests__/crawlSymlinks.test.ts @@ -8,7 +8,7 @@ import {tmpdir} from 'os'; import * as path from 'path'; import runJest from '../runJest'; -import {cleanup, writeFiles, writeSymlinks} from '../Utils'; +import {cleanup, testIfWatchman, writeFiles, writeSymlinks} from '../Utils'; const DIR = path.resolve(tmpdir(), 'crawl-symlinks-test'); @@ -44,71 +44,89 @@ function init( }); } -test('Node crawler picks up symlinked files when option is set as flag', () => { - // Symlinks are only enabled on windows with developer mode. - // https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/ - if (process.platform === 'win32') { - return; - } - - init(); - const {stdout, stderr, exitCode} = runJest(DIR, [ - '--haste={"enableSymlinks": true}', - ]); - - expect(stdout).toEqual(''); - expect(stderr).toContain('Test Suites: 1 passed, 1 total'); - expect(exitCode).toEqual(0); -}); - -test('Node crawler does not pick up symlinked files by default', () => { - init(); - const {stdout, stderr, exitCode} = runJest(DIR, []); - expect(stdout).toContain('No tests found, exiting with code 1'); - expect(stderr).toEqual(''); - expect(exitCode).toEqual(1); -}); - -test('Warns if --enableSymlinks passed but not set in .watchmanconfig', () => { - // Symlinks are only enabled on windows with developer mode. - // https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/ - if (process.platform === 'win32') { - return; - } - - init({'.watchmanconfig': ''}); - - const {stdout, stderr, exitCode} = runJest(DIR, [ - '--haste={"enableSymlinks": true}', - ]); - - expect(stdout).toEqual( - `jest-haste-map: --enableSymlinks was passed but symlink ` + - `crawling is not enabled in .watchmanconfig. +[ + {runner: test, watchmanFlag: '--no-watchman'}, + {runner: testIfWatchman, watchmanFlag: '--watchman'}, +].forEach(({watchmanFlag, runner}) => { + describe(`crawling symlinks with ${watchmanFlag}`, () => { + runner( + 'Node crawler picks up symlinked files when option is set as flag', + () => { + // Symlinks are only enabled on windows with developer mode. + // https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/ + if (process.platform === 'win32') { + return; + } + + init(); + const {stdout, stderr, exitCode} = runJest(DIR, [ + '--haste={"enableSymlinks": true}', + watchmanFlag, + ]); + + expect(stdout).toEqual(''); + expect(stderr).toContain('Test Suites: 1 passed, 1 total'); + expect(exitCode).toEqual(0); + }, + ); + + runner('Node crawler does not pick up symlinked files by default', () => { + init(); + const {stdout, stderr, exitCode} = runJest(DIR, [watchmanFlag]); + expect(stdout).toContain('No tests found, exiting with code 1'); + expect(stderr).toEqual(''); + expect(exitCode).toEqual(1); + }); + + runner( + 'Warns if --enableSymlinks passed but not set in .watchmanconfig', + () => { + // Symlinks are only enabled on windows with developer mode. + // https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/ + if (process.platform === 'win32') { + return; + } + + init({'.watchmanconfig': ''}); + + const {stdout, stderr, exitCode} = runJest(DIR, [ + '--haste={"enableSymlinks": true}', + watchmanFlag, + ]); + + expect(stdout).toEqual( + `jest-haste-map: --enableSymlinks was passed but symlink ` + + `crawling is not enabled in .watchmanconfig. To enable symlink crawling in .watchmanconfig set \"watch_symlinks\": true.`, - ); + ); - expect(stderr).toContain('Test Suites: 1 passed, 1 total'); - expect(exitCode).toEqual(0); -}); + expect(stderr).toContain('Test Suites: 1 passed, 1 total'); + expect(exitCode).toEqual(0); + }, + ); -test('Warns if watch_symlinks true in .watchmanconfig but not passed', () => { - // Symlinks are only enabled on windows with developer mode. - // https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/ - if (process.platform === 'win32') { - return; - } + runner( + 'Warns if watch_symlinks true in .watchmanconfig but not passed', + () => { + // Symlinks are only enabled on windows with developer mode. + // https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/ + if (process.platform === 'win32') { + return; + } - init({'.watchmanconfig': JSON.stringify({watch_symlinks: true})}); + init({'.watchmanconfig': JSON.stringify({watch_symlinks: true})}); - const {stdout, stderr, exitCode} = runJest(DIR, []); + const {stdout, stderr, exitCode} = runJest(DIR, [watchmanFlag]); - expect(stdout).toContain( - 'jest-haste-map: watch_symlinks is enabled in .watchmanconfig but ' + - '--enableSymlinks was not passed to Jest as a flag.', - ); - expect(stdout).toContain('No tests found, exiting with code 1'); - expect(stderr).toEqual(''); + expect(stdout).toContain( + 'jest-haste-map: watch_symlinks is enabled in .watchmanconfig but ' + + '--enableSymlinks was not passed to Jest as a flag.', + ); + expect(stdout).toContain('No tests found, exiting with code 1'); + expect(stderr).toEqual(''); - expect(exitCode).toEqual(1); + expect(exitCode).toEqual(1); + }, + ); + }); }); diff --git a/packages/jest-haste-map/src/index.ts b/packages/jest-haste-map/src/index.ts index bb7b1276cd46..36e9bb24308f 100644 --- a/packages/jest-haste-map/src/index.ts +++ b/packages/jest-haste-map/src/index.ts @@ -765,7 +765,7 @@ class HasteMap extends EventEmitter { this._console.warn( `jest-haste-map: watch_symlinks is enabled in .watchmanconfig ` + `but --enableSymlinks was not passed to Jest as a flag. ` + - `This will result in different behavior when watchman ` + + ` This will result in different behavior when watchman ` + `is enabled or disabled`, ); } else if (crawlerOptions.enableSymlinks) { From cc360f956a54e14dfeccccbac09c0925fd37af5e Mon Sep 17 00:00:00 2001 From: Daniel Muller Date: Fri, 30 Oct 2020 20:59:53 -0600 Subject: [PATCH 05/13] Throw if watchman is used with haste.enableSymlinks --- docs/Configuration.md | 6 +- e2e/__tests__/crawlSymlinks.test.ts | 126 +++++++++------------------ packages/jest-haste-map/src/index.ts | 42 +++------ packages/jest-types/src/Config.ts | 6 +- 4 files changed, 64 insertions(+), 116 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index f5234bc0b541..af05443ecbec 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -476,7 +476,11 @@ type HasteConfig = { computeSha1?: boolean; /** The platform to use as the default, e.g. 'ios'. */ defaultPlatform?: string | null; - /** Whether to follow symlinks when crawling for files. */ + /** + * Whether to follow symlinks when crawling for files. + * This options cannot be used in projects which use watchman. + * Projects with a .watchmanconfig will error if this option is set to true. + */ enableSymlinks?: boolean; /** Path to a custom implementation of Haste. */ hasteImplModulePath?: string; diff --git a/e2e/__tests__/crawlSymlinks.test.ts b/e2e/__tests__/crawlSymlinks.test.ts index 0802972a0d9b..dab330d750ec 100644 --- a/e2e/__tests__/crawlSymlinks.test.ts +++ b/e2e/__tests__/crawlSymlinks.test.ts @@ -8,7 +8,7 @@ import {tmpdir} from 'os'; import * as path from 'path'; import runJest from '../runJest'; -import {cleanup, testIfWatchman, writeFiles, writeSymlinks} from '../Utils'; +import {cleanup, writeFiles, writeSymlinks} from '../Utils'; const DIR = path.resolve(tmpdir(), 'crawl-symlinks-test'); @@ -44,89 +44,47 @@ function init( }); } -[ - {runner: test, watchmanFlag: '--no-watchman'}, - {runner: testIfWatchman, watchmanFlag: '--watchman'}, -].forEach(({watchmanFlag, runner}) => { - describe(`crawling symlinks with ${watchmanFlag}`, () => { - runner( - 'Node crawler picks up symlinked files when option is set as flag', - () => { - // Symlinks are only enabled on windows with developer mode. - // https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/ - if (process.platform === 'win32') { - return; - } - - init(); - const {stdout, stderr, exitCode} = runJest(DIR, [ - '--haste={"enableSymlinks": true}', - watchmanFlag, - ]); - - expect(stdout).toEqual(''); - expect(stderr).toContain('Test Suites: 1 passed, 1 total'); - expect(exitCode).toEqual(0); - }, - ); - - runner('Node crawler does not pick up symlinked files by default', () => { - init(); - const {stdout, stderr, exitCode} = runJest(DIR, [watchmanFlag]); - expect(stdout).toContain('No tests found, exiting with code 1'); - expect(stderr).toEqual(''); - expect(exitCode).toEqual(1); - }); - - runner( - 'Warns if --enableSymlinks passed but not set in .watchmanconfig', - () => { - // Symlinks are only enabled on windows with developer mode. - // https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/ - if (process.platform === 'win32') { - return; - } - - init({'.watchmanconfig': ''}); - - const {stdout, stderr, exitCode} = runJest(DIR, [ - '--haste={"enableSymlinks": true}', - watchmanFlag, - ]); - - expect(stdout).toEqual( - `jest-haste-map: --enableSymlinks was passed but symlink ` + - `crawling is not enabled in .watchmanconfig. - To enable symlink crawling in .watchmanconfig set \"watch_symlinks\": true.`, - ); - - expect(stderr).toContain('Test Suites: 1 passed, 1 total'); - expect(exitCode).toEqual(0); - }, - ); - - runner( - 'Warns if watch_symlinks true in .watchmanconfig but not passed', - () => { - // Symlinks are only enabled on windows with developer mode. - // https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/ - if (process.platform === 'win32') { - return; - } - - init({'.watchmanconfig': JSON.stringify({watch_symlinks: true})}); - - const {stdout, stderr, exitCode} = runJest(DIR, [watchmanFlag]); +const noWatchman = '--no-watchman'; +test('Node crawler picks up symlinked files when option is set as flag', () => { + // Symlinks are only enabled on windows with developer mode. + // https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/ + if (process.platform === 'win32') { + return; + } + + init(); + const {stdout, stderr, exitCode} = runJest(DIR, [ + '--haste={"enableSymlinks": true}', + noWatchman, + ]); + + expect(stdout).toEqual(''); + expect(stderr).toContain('Test Suites: 1 passed, 1 total'); + expect(exitCode).toEqual(0); +}); - expect(stdout).toContain( - 'jest-haste-map: watch_symlinks is enabled in .watchmanconfig but ' + - '--enableSymlinks was not passed to Jest as a flag.', - ); - expect(stdout).toContain('No tests found, exiting with code 1'); - expect(stderr).toEqual(''); +test('Node crawler does not pick up symlinked files by default', () => { + init(); + const {stdout, stderr, exitCode} = runJest(DIR, [noWatchman]); + expect(stdout).toContain('No tests found, exiting with code 1'); + expect(stderr).toEqual(''); + expect(exitCode).toEqual(1); +}); - expect(exitCode).toEqual(1); - }, - ); - }); +test('Should throw if .watchmanconfig used with haste.enableSymlinks', () => { + init({'.watchmanconfig': JSON.stringify({})}); + + const {stdout, stderr, exitCode} = runJest(DIR, [ + '--haste={"enableSymlinks": true}', + ]); + + expect(stderr).toEqual(''); + expect(stdout).toContain( + 'jest-haste-map: haste.enableSymlinks config option was set, but ' + + 'is incompatible with watchman.\n' + + ' Either set haste.enableSymlinks to false or remove ' + + '.watchmanconfig to disable watchman. \n' + + ' Exiting with code 1.', + ); + expect(exitCode).toEqual(1); }); diff --git a/packages/jest-haste-map/src/index.ts b/packages/jest-haste-map/src/index.ts index 36e9bb24308f..fba2d9dca9bc 100644 --- a/packages/jest-haste-map/src/index.ts +++ b/packages/jest-haste-map/src/index.ts @@ -13,7 +13,7 @@ import {EventEmitter} from 'events'; import {tmpdir} from 'os'; import * as path from 'path'; import type {Stats} from 'graceful-fs'; -import {existsSync, readFileSync} from 'graceful-fs'; +import {existsSync} from 'graceful-fs'; import {NodeWatcher, Watcher as SaneWatcher} from 'sane'; import type {Config} from '@jest/types'; import {escapePathForRegex} from 'jest-regex-util'; @@ -750,36 +750,18 @@ class HasteMap extends EventEmitter { roots: options.roots, }; - const watchmanConfigPath = path.join(options.rootDir, '.watchmanconfig'); - if (existsSync(watchmanConfigPath)) { - try { - const configStr = readFileSync(watchmanConfigPath, 'utf8') - .toString() - .trim(); - const watchmanConfig = configStr === '' ? {} : JSON.parse(configStr); - const watchSymlinks = Boolean(watchmanConfig['watch_symlinks']); - - // Automatically enable symlink crawling in node crawler if watchman is - // set up to crawl symlinks. - if (watchSymlinks && !crawlerOptions.enableSymlinks) { - this._console.warn( - `jest-haste-map: watch_symlinks is enabled in .watchmanconfig ` + - `but --enableSymlinks was not passed to Jest as a flag. ` + - ` This will result in different behavior when watchman ` + - `is enabled or disabled`, - ); - } else if (crawlerOptions.enableSymlinks) { - this._console.warn( - `jest-haste-map: --enableSymlinks was passed but symlink ` + - `crawling is not enabled in .watchmanconfig.\n` + - ` To enable symlink crawling in .watchmanconfig set ` + - `"watch_symlinks": true.`, - ); - } - } catch (error) { - this._console.warn( - `jest-haste-map: Failed to parse .watchmanconfig.\n Original Error: ${error}`, + if (options.enableSymlinks) { + const watchmanConfigPath = path.join(options.rootDir, '.watchmanconfig'); + + if (existsSync(watchmanConfigPath)) { + this._console.error( + 'jest-haste-map: haste.enableSymlinks config option was set, but ' + + 'is incompatible with watchman.\n' + + ' Either set haste.enableSymlinks to false or remove ' + + '.watchmanconfig to disable watchman. \n' + + ' Exiting with code 1.', ); + process.exit(1); } } diff --git a/packages/jest-types/src/Config.ts b/packages/jest-types/src/Config.ts index fb9e1cbfc4c8..206b5868ae70 100644 --- a/packages/jest-types/src/Config.ts +++ b/packages/jest-types/src/Config.ts @@ -22,7 +22,11 @@ export type HasteConfig = { computeSha1?: boolean; /** The platform to use as the default, e.g. 'ios'. */ defaultPlatform?: string | null; - /** Whether to follow symlinks when crawling for files. */ + /** + * Whether to follow symlinks when crawling for files. + * This options cannot be used in projects which use watchman. + * Projects with a .watchmanconfig will error if this option is set to true. + */ enableSymlinks?: boolean; /** Path to a custom implementation of Haste. */ hasteImplModulePath?: string; From abc5aaa05573901cff17080ed6e68a956a46d5d5 Mon Sep 17 00:00:00 2001 From: Daniel Muller Date: Sun, 1 Nov 2020 17:01:29 -0700 Subject: [PATCH 06/13] Move validation logic to jest-config --- docs/Configuration.md | 2 +- e2e/Utils.ts | 16 ---------------- e2e/__tests__/crawlSymlinks.test.ts | 21 +-------------------- packages/jest-config/src/normalize.ts | 8 ++++++++ packages/jest-haste-map/src/index.ts | 16 ---------------- 5 files changed, 10 insertions(+), 53 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index af05443ecbec..8a0b7cb1df02 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -476,7 +476,7 @@ type HasteConfig = { computeSha1?: boolean; /** The platform to use as the default, e.g. 'ios'. */ defaultPlatform?: string | null; - /** + /** * Whether to follow symlinks when crawling for files. * This options cannot be used in projects which use watchman. * Projects with a .watchmanconfig will error if this option is set to true. diff --git a/e2e/Utils.ts b/e2e/Utils.ts index 96a4729946d1..c8d20585e834 100644 --- a/e2e/Utils.ts +++ b/e2e/Utils.ts @@ -290,19 +290,3 @@ export const testIfHg = (...args: Parameters) => { test.skip(...args); } }; - -// Certain environments (like CITGM and GH Actions) do not come with watchman installed -let watchmanIsInstalled: boolean | null = null; - -export const testIfWatchman = (...args: Parameters) => { - if (watchmanIsInstalled === null) { - watchmanIsInstalled = which.sync('watchman', {nothrow: true}) !== null; - } - - if (watchmanIsInstalled) { - test(...args); - } else { - console.warn('watchman is not installed - skipping some tests'); - test.skip(...args); - } -}; diff --git a/e2e/__tests__/crawlSymlinks.test.ts b/e2e/__tests__/crawlSymlinks.test.ts index dab330d750ec..32d2f78068e9 100644 --- a/e2e/__tests__/crawlSymlinks.test.ts +++ b/e2e/__tests__/crawlSymlinks.test.ts @@ -6,9 +6,8 @@ */ import {tmpdir} from 'os'; import * as path from 'path'; - -import runJest from '../runJest'; import {cleanup, writeFiles, writeSymlinks} from '../Utils'; +import runJest from '../runJest'; const DIR = path.resolve(tmpdir(), 'crawl-symlinks-test'); @@ -70,21 +69,3 @@ test('Node crawler does not pick up symlinked files by default', () => { expect(stderr).toEqual(''); expect(exitCode).toEqual(1); }); - -test('Should throw if .watchmanconfig used with haste.enableSymlinks', () => { - init({'.watchmanconfig': JSON.stringify({})}); - - const {stdout, stderr, exitCode} = runJest(DIR, [ - '--haste={"enableSymlinks": true}', - ]); - - expect(stderr).toEqual(''); - expect(stdout).toContain( - 'jest-haste-map: haste.enableSymlinks config option was set, but ' + - 'is incompatible with watchman.\n' + - ' Either set haste.enableSymlinks to false or remove ' + - '.watchmanconfig to disable watchman. \n' + - ' Exiting with code 1.', - ); - expect(exitCode).toEqual(1); -}); diff --git a/packages/jest-config/src/normalize.ts b/packages/jest-config/src/normalize.ts index 82da0cc8a397..8e91123cb089 100644 --- a/packages/jest-config/src/normalize.ts +++ b/packages/jest-config/src/normalize.ts @@ -952,6 +952,14 @@ export default function normalize( return newOptions; }, newOptions); + if (options.watchman && options.haste?.enableSymlinks) { + throw new ValidationError( + 'Validation Error', + 'haste.enableSymlinks is incompatible with watchman', + 'Either set haste.enableSymlinks to false or do not use watchman', + ); + } + newOptions.roots.forEach((root, i) => { verifyDirectoryExists(root, `roots[${i}]`); }); diff --git a/packages/jest-haste-map/src/index.ts b/packages/jest-haste-map/src/index.ts index fba2d9dca9bc..cc350d66a2bf 100644 --- a/packages/jest-haste-map/src/index.ts +++ b/packages/jest-haste-map/src/index.ts @@ -13,7 +13,6 @@ import {EventEmitter} from 'events'; import {tmpdir} from 'os'; import * as path from 'path'; import type {Stats} from 'graceful-fs'; -import {existsSync} from 'graceful-fs'; import {NodeWatcher, Watcher as SaneWatcher} from 'sane'; import type {Config} from '@jest/types'; import {escapePathForRegex} from 'jest-regex-util'; @@ -750,21 +749,6 @@ class HasteMap extends EventEmitter { roots: options.roots, }; - if (options.enableSymlinks) { - const watchmanConfigPath = path.join(options.rootDir, '.watchmanconfig'); - - if (existsSync(watchmanConfigPath)) { - this._console.error( - 'jest-haste-map: haste.enableSymlinks config option was set, but ' + - 'is incompatible with watchman.\n' + - ' Either set haste.enableSymlinks to false or remove ' + - '.watchmanconfig to disable watchman. \n' + - ' Exiting with code 1.', - ); - process.exit(1); - } - } - const retry = (error: Error) => { if (crawl === watchmanCrawl) { this._console.warn( From fb2e5268d89db0b0b68245e8c115fe62428bd0f9 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 2 Nov 2020 08:31:05 +0100 Subject: [PATCH 07/13] error in haste map as well and add tests verifying it --- e2e/__tests__/crawlSymlinks.test.ts | 25 +++++++++++++ .../src/__tests__/normalize.test.js | 23 ++++++++++++ packages/jest-config/src/normalize.ts | 4 +++ .../src/__tests__/index.test.js | 36 +++++++++++++++++++ packages/jest-haste-map/src/index.ts | 8 +++++ 5 files changed, 96 insertions(+) diff --git a/e2e/__tests__/crawlSymlinks.test.ts b/e2e/__tests__/crawlSymlinks.test.ts index 32d2f78068e9..8f59213ff0ae 100644 --- a/e2e/__tests__/crawlSymlinks.test.ts +++ b/e2e/__tests__/crawlSymlinks.test.ts @@ -6,6 +6,7 @@ */ import {tmpdir} from 'os'; import * as path from 'path'; +import {wrap} from 'jest-snapshot-serializer-raw'; import {cleanup, writeFiles, writeSymlinks} from '../Utils'; import runJest from '../runJest'; @@ -69,3 +70,27 @@ test('Node crawler does not pick up symlinked files by default', () => { expect(stderr).toEqual(''); expect(exitCode).toEqual(1); }); + +test('Should throw if watchman used with haste.enableSymlinks', () => { + init({'.watchmanconfig': JSON.stringify({})}); + + // it should throw both if watchman is explicitly provided and not + const run1 = runJest(DIR, ['--haste={"enableSymlinks": true}']); + const run2 = runJest(DIR, ['--haste={"enableSymlinks": true}', '--watchman']); + + expect(run1.exitCode).toEqual(run2.exitCode); + expect(run1.stderr).toEqual(run2.stderr); + expect(run1.stdout).toEqual(run2.stdout); + + const {exitCode, stderr, stdout} = run1; + + expect(stdout).toEqual(''); + expect(wrap(stderr)).toMatchInlineSnapshot(` + Validation Error: + + haste.enableSymlinks is incompatible with watchman + + Either set haste.enableSymlinks to false or do not use watchman + `); + expect(exitCode).toEqual(1); +}); diff --git a/packages/jest-config/src/__tests__/normalize.test.js b/packages/jest-config/src/__tests__/normalize.test.js index ae3b198b2fed..6cde82c538e7 100644 --- a/packages/jest-config/src/__tests__/normalize.test.js +++ b/packages/jest-config/src/__tests__/normalize.test.js @@ -1714,3 +1714,26 @@ describe('testTimeout', () => { ).toThrowErrorMatchingSnapshot(); }); }); + +describe('haste.enableSymlinks', () => { + it('should throw if watchman is not disabled', () => { + expect(() => + normalize({haste: {enableSymlinks: true}, rootDir: '/root/'}, {}), + ).toThrow('haste.enableSymlinks is incompatible with watchman'); + + expect(() => + normalize( + {haste: {enableSymlinks: true}, rootDir: '/root/', watchman: true}, + {}, + ), + ).toThrow('haste.enableSymlinks is incompatible with watchman'); + + const {options} = normalize( + {haste: {enableSymlinks: true}, rootDir: '/root/', watchman: false}, + {}, + ); + + expect(options.haste.enableSymlinks).toBe(true); + expect(options.watchman).toBe(false); + }); +}); diff --git a/packages/jest-config/src/normalize.ts b/packages/jest-config/src/normalize.ts index 8e91123cb089..f9f8d6fe45c7 100644 --- a/packages/jest-config/src/normalize.ts +++ b/packages/jest-config/src/normalize.ts @@ -577,6 +577,10 @@ export default function normalize( }); } + if (options.watchman == null) { + options.watchman = DEFAULT_CONFIG.watchman; + } + const optionKeys = Object.keys(options) as Array; optionKeys.reduce((newOptions, key: keyof Config.InitialOptions) => { diff --git a/packages/jest-haste-map/src/__tests__/index.test.js b/packages/jest-haste-map/src/__tests__/index.test.js index 2e151abeb5c5..b571f3486618 100644 --- a/packages/jest-haste-map/src/__tests__/index.test.js +++ b/packages/jest-haste-map/src/__tests__/index.test.js @@ -517,6 +517,42 @@ describe('HasteMap', () => { }); }); + it('throws if both symlinks and watchman is enabled', () => { + expect( + () => new HasteMap({...defaultConfig, enableSymlinks: true}), + ).toThrow( + 'Set either `enableSymlinks` to false or `useWatchman` to false.', + ); + expect( + () => + new HasteMap({ + ...defaultConfig, + enableSymlinks: true, + useWatchman: true, + }), + ).toThrow( + 'Set either `enableSymlinks` to false or `useWatchman` to false.', + ); + + expect( + () => + new HasteMap({ + ...defaultConfig, + enableSymlinks: false, + useWatchman: true, + }), + ).not.toThrow(); + + expect( + () => + new HasteMap({ + ...defaultConfig, + enableSymlinks: true, + useWatchman: false, + }), + ).not.toThrow(); + }); + describe('builds a haste map on a fresh cache with SHA-1s', () => { [false, true].forEach(useWatchman => { it('uses watchman: ' + useWatchman, async () => { diff --git a/packages/jest-haste-map/src/index.ts b/packages/jest-haste-map/src/index.ts index cc350d66a2bf..d2c28a6b4102 100644 --- a/packages/jest-haste-map/src/index.ts +++ b/packages/jest-haste-map/src/index.ts @@ -278,6 +278,14 @@ class HasteMap extends EventEmitter { this._options.ignorePattern = new RegExp(VCS_DIRECTORIES); } + if (this._options.enableSymlinks && this._options.useWatchman) { + throw new Error( + 'jest-haste-map: enableSymlinks config option was set, but ' + + 'is incompatible with watchman.\n' + + 'Set either `enableSymlinks` to false or `useWatchman` to false.', + ); + } + const rootDirHash = createHash('md5').update(options.rootDir).digest('hex'); let hasteImplHash = ''; let dependencyExtractorHash = ''; From a531e780cbad319366e5c627441a9614bcb03dce Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 2 Nov 2020 09:00:02 +0100 Subject: [PATCH 08/13] remove optional --- packages/jest-haste-map/src/crawlers/node.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jest-haste-map/src/crawlers/node.ts b/packages/jest-haste-map/src/crawlers/node.ts index 488cc6739378..d336f8c886e6 100644 --- a/packages/jest-haste-map/src/crawlers/node.ts +++ b/packages/jest-haste-map/src/crawlers/node.ts @@ -61,7 +61,7 @@ function find( extensions: Array, ignore: IgnoreMatcher, callback: Callback, - enableSymlinks?: boolean, + enableSymlinks: boolean, ): void { const result: Result = []; let activeCalls = 0; From c497fcb2061dad59dfb554f99c22209f33db6888 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 2 Nov 2020 09:28:53 +0100 Subject: [PATCH 09/13] correct watchman comment --- docs/Configuration.md | 2 +- packages/jest-types/src/Config.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Configuration.md b/docs/Configuration.md index 8a0b7cb1df02..1a3138aefdc3 100644 --- a/docs/Configuration.md +++ b/docs/Configuration.md @@ -479,7 +479,7 @@ type HasteConfig = { /** * Whether to follow symlinks when crawling for files. * This options cannot be used in projects which use watchman. - * Projects with a .watchmanconfig will error if this option is set to true. + * Projects with `watchman` set to true will error if this option is set to true. */ enableSymlinks?: boolean; /** Path to a custom implementation of Haste. */ diff --git a/packages/jest-types/src/Config.ts b/packages/jest-types/src/Config.ts index 206b5868ae70..9b8da96249eb 100644 --- a/packages/jest-types/src/Config.ts +++ b/packages/jest-types/src/Config.ts @@ -25,7 +25,7 @@ export type HasteConfig = { /** * Whether to follow symlinks when crawling for files. * This options cannot be used in projects which use watchman. - * Projects with a .watchmanconfig will error if this option is set to true. + * Projects with `watchman` set to true will error if this option is set to true. */ enableSymlinks?: boolean; /** Path to a custom implementation of Haste. */ From f1c70498769495abd32f83390538d976fc99aaca Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 2 Nov 2020 09:39:14 +0100 Subject: [PATCH 10/13] cleanup test --- e2e/__tests__/crawlSymlinks.test.ts | 34 ++++++++++------------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/e2e/__tests__/crawlSymlinks.test.ts b/e2e/__tests__/crawlSymlinks.test.ts index 8f59213ff0ae..f09fe7f39001 100644 --- a/e2e/__tests__/crawlSymlinks.test.ts +++ b/e2e/__tests__/crawlSymlinks.test.ts @@ -14,17 +14,7 @@ const DIR = path.resolve(tmpdir(), 'crawl-symlinks-test'); beforeEach(() => { cleanup(DIR); -}); - -afterEach(() => { - cleanup(DIR); -}); -function init( - extraFiles: { - [filename: string]: string; - } = {}, -) { writeFiles(DIR, { 'package.json': JSON.stringify({ jest: { @@ -32,19 +22,21 @@ function init( }, }), 'symlinked-files/test.js': ` - test('1+1', () => { - expect(1).toBe(1); - }); - `, - ...extraFiles, + test('1+1', () => { + expect(1).toBe(1); + }); + `, }); writeSymlinks(DIR, { 'symlinked-files/test.js': 'test-files/test.js', }); -} +}); + +afterEach(() => { + cleanup(DIR); +}); -const noWatchman = '--no-watchman'; test('Node crawler picks up symlinked files when option is set as flag', () => { // Symlinks are only enabled on windows with developer mode. // https://blogs.windows.com/windowsdeveloper/2016/12/02/symlinks-windows-10/ @@ -52,10 +44,9 @@ test('Node crawler picks up symlinked files when option is set as flag', () => { return; } - init(); const {stdout, stderr, exitCode} = runJest(DIR, [ '--haste={"enableSymlinks": true}', - noWatchman, + '--no-watchman', ]); expect(stdout).toEqual(''); @@ -64,16 +55,13 @@ test('Node crawler picks up symlinked files when option is set as flag', () => { }); test('Node crawler does not pick up symlinked files by default', () => { - init(); - const {stdout, stderr, exitCode} = runJest(DIR, [noWatchman]); + const {stdout, stderr, exitCode} = runJest(DIR, ['--no-watchman']); expect(stdout).toContain('No tests found, exiting with code 1'); expect(stderr).toEqual(''); expect(exitCode).toEqual(1); }); test('Should throw if watchman used with haste.enableSymlinks', () => { - init({'.watchmanconfig': JSON.stringify({})}); - // it should throw both if watchman is explicitly provided and not const run1 = runJest(DIR, ['--haste={"enableSymlinks": true}']); const run2 = runJest(DIR, ['--haste={"enableSymlinks": true}', '--watchman']); From 30a07e16b53c81b35a9fb4f24993d8accf982199 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Mon, 2 Nov 2020 10:26:04 +0100 Subject: [PATCH 11/13] pass callback as last arg --- packages/jest-haste-map/src/crawlers/node.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/jest-haste-map/src/crawlers/node.ts b/packages/jest-haste-map/src/crawlers/node.ts index d336f8c886e6..075498c4702a 100644 --- a/packages/jest-haste-map/src/crawlers/node.ts +++ b/packages/jest-haste-map/src/crawlers/node.ts @@ -60,8 +60,8 @@ function find( roots: Array, extensions: Array, ignore: IgnoreMatcher, - callback: Callback, enableSymlinks: boolean, + callback: Callback, ): void { const result: Result = []; let activeCalls = 0; @@ -140,8 +140,8 @@ function findNative( roots: Array, extensions: Array, ignore: IgnoreMatcher, - callback: Callback, enableSymlinks: boolean, + callback: Callback, ): void { const args = Array.from(roots); if (enableSymlinks) { @@ -242,9 +242,9 @@ export = async function nodeCrawl( }; if (useNativeFind) { - findNative(roots, extensions, ignore, callback, enableSymlinks); + findNative(roots, extensions, ignore, enableSymlinks, callback); } else { - find(roots, extensions, ignore, callback, enableSymlinks); + find(roots, extensions, ignore, enableSymlinks, callback); } }); }; From dcda5ba7f01fbf8fc50bb758dacad057b1e18243 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 1 Apr 2021 11:02:32 +0200 Subject: [PATCH 12/13] move changelog entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cbc281c8230e..2220d15ea6b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - `[jest-environment-node]` Add AbortController to globals ([#11182](https://github.com/facebook/jest/pull/11182)) - `[@jest/fake-timers]` Update to `@sinonjs/fake-timers` to v7 ([#11198](https://github.com/facebook/jest/pull/11198)) - `[jest-haste-map]` Handle injected scm clocks ([#10966](https://github.com/facebook/jest/pull/10966)) +- `[jest-haste-map]` Add `enableSymlinks` configuration option to follow symlinks for test files ([#9351](https://github.com/facebook/jest/pull/9351)) - `[jest-repl, jest-runner]` [**BREAKING**] Run transforms over environment ([#8751](https://github.com/facebook/jest/pull/8751)) - `[jest-runner]` [**BREAKING**] set exit code to 1 if test logs after teardown ([#10728](https://github.com/facebook/jest/pull/10728)) - `[jest-runner]` [**BREAKING**] Run transforms over `runnner` ([#8823](https://github.com/facebook/jest/pull/8823)) @@ -120,7 +121,6 @@ ### Features - `[jest-core]` Add `findRelatedTests` and `nonFlagArgs` in allowed config options for `updateConfigAndRun` in watch plugins ([#10659](https://github.com/facebook/jest/pull/10659)) -- `[jest-haste-map]` Add `enableSymlinks` configuration option to follow symlinks for test files ([#9350](https://github.com/facebook/jest/issues/9350)) ### Fixes From 88cfea0062f7d6ea26171a5ad26259ce1c3fd3d2 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 1 Apr 2021 12:12:59 +0200 Subject: [PATCH 13/13] normalize is async now --- packages/jest-config/src/__tests__/normalize.test.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/jest-config/src/__tests__/normalize.test.ts b/packages/jest-config/src/__tests__/normalize.test.ts index f3530f502f70..1000d1e10c14 100644 --- a/packages/jest-config/src/__tests__/normalize.test.ts +++ b/packages/jest-config/src/__tests__/normalize.test.ts @@ -1844,19 +1844,19 @@ describe('extensionsToTreatAsEsm', () => { }); describe('haste.enableSymlinks', () => { - it('should throw if watchman is not disabled', () => { - expect(() => + it('should throw if watchman is not disabled', async () => { + await expect( normalize({haste: {enableSymlinks: true}, rootDir: '/root/'}, {}), - ).toThrow('haste.enableSymlinks is incompatible with watchman'); + ).rejects.toThrow('haste.enableSymlinks is incompatible with watchman'); - expect(() => + await expect( normalize( {haste: {enableSymlinks: true}, rootDir: '/root/', watchman: true}, {}, ), - ).toThrow('haste.enableSymlinks is incompatible with watchman'); + ).rejects.toThrow('haste.enableSymlinks is incompatible with watchman'); - const {options} = normalize( + const {options} = await normalize( {haste: {enableSymlinks: true}, rootDir: '/root/', watchman: false}, {}, );