From c8351767f9aecf7852182417ad077bb23a64391d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Fri, 9 Nov 2018 12:09:22 +0000 Subject: [PATCH 1/6] Add failing e2e test --- e2e/__tests__/watch_mode_no_access.test.js | 77 ++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 e2e/__tests__/watch_mode_no_access.test.js diff --git a/e2e/__tests__/watch_mode_no_access.test.js b/e2e/__tests__/watch_mode_no_access.test.js new file mode 100644 index 000000000000..6d6a3038fe34 --- /dev/null +++ b/e2e/__tests__/watch_mode_no_access.test.js @@ -0,0 +1,77 @@ +/** + * 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. + * + * @flow + */ + +'use strict'; + +import fs from 'fs'; +import os from 'os'; +import path from 'path'; +import {cleanup, writeFiles} from '../Utils'; +import {runContinuous} from '../runJest'; + +const DIR = path.resolve(os.tmpdir(), 'watch_mode_no_access'); + +const sleep = time => new Promise(resolve => setTimeout(resolve, time)); + +beforeEach(() => cleanup(DIR)); +afterAll(() => cleanup(DIR)); + +const setupFiles = () => { + writeFiles(DIR, { + '__tests__/foo.spec.js': ` + const foo = require('../foo'); + test('foo', () => { expect(typeof foo).toBe('number'); }); + `, + 'foo.js': ` + module.exports = 0; + `, + 'package.json': JSON.stringify({ + jest: {}, + }), + }); +}; + +let testRun; + +afterEach(async () => { + if (testRun) { + await testRun.end(); + } +}); + +test('does not re-run tests when only access time is modified', async () => { + setupFiles(); + + testRun = runContinuous(DIR, ['--watchAll', '--no-watchman']); + + const testCompletedRE = /Ran all test suites./g; + const numberOfTestRuns = (stderr: string): number => { + const matches = stderr.match(testCompletedRE); + return matches ? matches.length : 0; + }; + + // First run + await testRun.waitUntil(({stderr}) => numberOfTestRuns(stderr) === 1); + + // Should re-run the test + const modulePath = path.join(DIR, 'foo.js'); + const stat = fs.lstatSync(modulePath); + fs.utimesSync(modulePath, stat.atime, stat.mtime); + await testRun.waitUntil(({stderr}) => numberOfTestRuns(stderr) === 2); + + // Should NOT re-run the test + const fakeATime = 1541723621; + fs.utimesSync(modulePath, fakeATime, stat.mtime); + await sleep(3000); + expect(numberOfTestRuns(testRun.getCurrentOutput().stderr)).toBe(2); + + // Should re-run the test + fs.writeFileSync(modulePath, 'module.exports = 1;', {encoding: 'utf-8'}); + await testRun.waitUntil(({stderr}) => numberOfTestRuns(stderr) === 3); +}); From d2812bcc71e5a40682cfbd7f4b4228815bb53d5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Fri, 9 Nov 2018 12:09:59 +0000 Subject: [PATCH 2/6] Stop re-running tests when mtime has not changed --- CHANGELOG.md | 1 + packages/jest-haste-map/src/index.ts | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0e0db1897e2c..ad6205cfa706 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ - `[jest-circus]` [**BREAKING**] Fail tests if a test takes a done callback and have return values ([#9129](https://github.com/facebook/jest/pull/9129)) - `[jest-circus]` [**BREAKING**] Throw a proper error if a test / hook is defined asynchronously ([#8096](https://github.com/facebook/jest/pull/8096)) - `[jest-config, jest-resolve]` [**BREAKING**] Remove support for `browser` field ([#9943](https://github.com/facebook/jest/pull/9943)) +- `[jest-haste-map]` Stop reporting files as changed when they are only accessed ([#7347](https://github.com/facebook/jest/pull/7347)) - `[jest-resolve]` Show relative path from root dir for `module not found` errors ([#9963](https://github.com/facebook/jest/pull/9963)) ### Chore & Maintenance diff --git a/packages/jest-haste-map/src/index.ts b/packages/jest-haste-map/src/index.ts index 129858c1f40d..37ff65131ec5 100644 --- a/packages/jest-haste-map/src/index.ts +++ b/packages/jest-haste-map/src/index.ts @@ -846,6 +846,19 @@ class HasteMap extends EventEmitter { return; } + const relativeFilePath = fastPath.relative(rootDir, filePath); + const fileMetadata = hasteMap.files.get(relativeFilePath); + + // The file has been accessed, not modified + if ( + fileMetadata && + type === 'change' && + stat && + fileMetadata[H.MTIME] === stat.mtime.getTime() + ) { + return; + } + changeQueue = changeQueue .then(() => { // If we get duplicate events for the same file, ignore them. @@ -879,9 +892,6 @@ class HasteMap extends EventEmitter { return null; }; - const relativeFilePath = fastPath.relative(rootDir, filePath); - const fileMetadata = hasteMap.files.get(relativeFilePath); - // If it's not an addition, delete the file and all its metadata if (fileMetadata != null) { const moduleName = fileMetadata[H.ID]; From 54f441865b6f8b114098da52ac70c5fabf40e3e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Sun, 3 May 2020 21:47:27 +0200 Subject: [PATCH 3/6] fix test --- ...ccess.test.js => watchModeNoAccess.test.ts} | 18 ++++++++++-------- packages/jest-haste-map/src/index.ts | 4 +--- 2 files changed, 11 insertions(+), 11 deletions(-) rename e2e/__tests__/{watch_mode_no_access.test.js => watchModeNoAccess.test.ts} (82%) diff --git a/e2e/__tests__/watch_mode_no_access.test.js b/e2e/__tests__/watchModeNoAccess.test.ts similarity index 82% rename from e2e/__tests__/watch_mode_no_access.test.js rename to e2e/__tests__/watchModeNoAccess.test.ts index 6d6a3038fe34..a198080a2780 100644 --- a/e2e/__tests__/watch_mode_no_access.test.js +++ b/e2e/__tests__/watchModeNoAccess.test.ts @@ -9,22 +9,23 @@ 'use strict'; -import fs from 'fs'; -import os from 'os'; -import path from 'path'; +import * as os from 'os'; +import * as path from 'path'; +import * as fs from 'graceful-fs'; import {cleanup, writeFiles} from '../Utils'; import {runContinuous} from '../runJest'; const DIR = path.resolve(os.tmpdir(), 'watch_mode_no_access'); -const sleep = time => new Promise(resolve => setTimeout(resolve, time)); +const sleep = (time: number) => + new Promise(resolve => setTimeout(resolve, time)); beforeEach(() => cleanup(DIR)); afterAll(() => cleanup(DIR)); const setupFiles = () => { writeFiles(DIR, { - '__tests__/foo.spec.js': ` + '__tests__/foo.test.js': ` const foo = require('../foo'); test('foo', () => { expect(typeof foo).toBe('number'); }); `, @@ -37,7 +38,7 @@ const setupFiles = () => { }); }; -let testRun; +let testRun: ReturnType; afterEach(async () => { if (testRun) { @@ -62,12 +63,13 @@ test('does not re-run tests when only access time is modified', async () => { // Should re-run the test const modulePath = path.join(DIR, 'foo.js'); const stat = fs.lstatSync(modulePath); - fs.utimesSync(modulePath, stat.atime, stat.mtime); + fs.utimesSync(modulePath, stat.atime.getTime(), stat.mtime.getTime()); + await testRun.waitUntil(({stderr}) => numberOfTestRuns(stderr) === 2); // Should NOT re-run the test const fakeATime = 1541723621; - fs.utimesSync(modulePath, fakeATime, stat.mtime); + fs.utimesSync(modulePath, fakeATime, stat.mtime.getTime()); await sleep(3000); expect(numberOfTestRuns(testRun.getCurrentOutput().stderr)).toBe(2); diff --git a/packages/jest-haste-map/src/index.ts b/packages/jest-haste-map/src/index.ts index 37ff65131ec5..b4f11edd99dd 100644 --- a/packages/jest-haste-map/src/index.ts +++ b/packages/jest-haste-map/src/index.ts @@ -851,10 +851,8 @@ class HasteMap extends EventEmitter { // The file has been accessed, not modified if ( - fileMetadata && type === 'change' && - stat && - fileMetadata[H.MTIME] === stat.mtime.getTime() + fileMetadata?.[H.MTIME] === stat?.mtime.getTime() ) { return; } From 24c4b5461b0e0adcd915e989c0d46b23b188280d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Sun, 3 May 2020 22:35:25 +0200 Subject: [PATCH 4/6] fix copyright --- e2e/__tests__/watchModeNoAccess.test.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/e2e/__tests__/watchModeNoAccess.test.ts b/e2e/__tests__/watchModeNoAccess.test.ts index a198080a2780..d7e4ef6e5a15 100644 --- a/e2e/__tests__/watchModeNoAccess.test.ts +++ b/e2e/__tests__/watchModeNoAccess.test.ts @@ -1,14 +1,11 @@ /** - * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * 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. * - * @flow */ -'use strict'; - import * as os from 'os'; import * as path from 'path'; import * as fs from 'graceful-fs'; From 2858ff4fe861e0fc1dae64ec10db6898311e0704 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Sun, 3 May 2020 22:40:48 +0200 Subject: [PATCH 5/6] adjust file accessed check --- packages/jest-haste-map/src/index.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/jest-haste-map/src/index.ts b/packages/jest-haste-map/src/index.ts index b4f11edd99dd..130fdea44ebe 100644 --- a/packages/jest-haste-map/src/index.ts +++ b/packages/jest-haste-map/src/index.ts @@ -852,7 +852,9 @@ class HasteMap extends EventEmitter { // The file has been accessed, not modified if ( type === 'change' && - fileMetadata?.[H.MTIME] === stat?.mtime.getTime() + fileMetadata && + stat && + fileMetadata[H.MTIME] === stat.mtime.getTime() ) { return; } From f4b9b468a674c70780ac4e448df1127159b4d8d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Sun, 3 May 2020 22:55:54 +0200 Subject: [PATCH 6/6] bring back reading fileMetadata from async callback --- packages/jest-haste-map/src/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/jest-haste-map/src/index.ts b/packages/jest-haste-map/src/index.ts index 130fdea44ebe..ef1e420400df 100644 --- a/packages/jest-haste-map/src/index.ts +++ b/packages/jest-haste-map/src/index.ts @@ -892,6 +892,8 @@ class HasteMap extends EventEmitter { return null; }; + const fileMetadata = hasteMap.files.get(relativeFilePath); + // If it's not an addition, delete the file and all its metadata if (fileMetadata != null) { const moduleName = fileMetadata[H.ID];