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

Clear resolved modules cache before runJest in watch mode #8650

Merged
merged 15 commits into from Jul 14, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion e2e/__tests__/__snapshots__/moduleNameMapper.test.ts.snap
Expand Up @@ -30,6 +30,6 @@ FAIL __tests__/index.js
12 | module.exports = () => 'test';
13 |

at createNoMappedModuleFoundError (../../packages/jest-resolve/build/index.js:472:17)
at createNoMappedModuleFoundError (../../packages/jest-resolve/build/index.js:501:17)
at Object.require (index.js:10:1)
`;
Expand Up @@ -33,6 +33,6 @@ FAIL __tests__/test.js
| ^
4 |

at Resolver.resolveModule (../../packages/jest-resolve/build/index.js:230:17)
at Resolver.resolveModule (../../packages/jest-resolve/build/index.js:259:17)
at Object.require (index.js:3:18)
`;
1 change: 1 addition & 0 deletions packages/jest-core/package.json
Expand Up @@ -19,6 +19,7 @@
"jest-haste-map": "^24.8.0",
"jest-message-util": "^24.8.0",
"jest-regex-util": "^24.3.0",
"jest-resolve": "^24.8.0",
"jest-resolve-dependencies": "^24.8.0",
"jest-runner": "^24.8.0",
"jest-runtime": "^24.8.0",
Expand Down
187 changes: 187 additions & 0 deletions packages/jest-core/src/__tests__/watch-file-changes.test.ts
@@ -0,0 +1,187 @@
/**
* 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.
*
*/

'use strict';

import path from 'path';
import fs from 'fs';
import os from 'os';
import {JestHook} from 'jest-watcher';
import Runtime from 'jest-runtime';
import {normalize} from 'jest-config';
import HasteMap from 'jest-haste-map';
import rimraf from 'rimraf';
import {AggregatedResult} from '@jest/test-result';

describe('Watch mode flows with changed files', () => {
jest.resetModules();

let watch: any;
let pipe: NodeJS.ReadStream;
let stdin: MockStdin;
const testDirectory = path.resolve(os.tmpdir(), 'jest-tmp');
const fileTargetPath = path.resolve(testDirectory, 'lost-file.js');
const fileTargetPath2 = path.resolve(
testDirectory,
'watch-test-fake.test.js',
);
const cacheDirectory = path.resolve(os.tmpdir(), `tmp${Math.random()}`);
let hasteMapInstance: HasteMap;

beforeEach(() => {
watch = require('../watch').default;
pipe = {write: jest.fn()} as any;
stdin = new MockStdin();
rimraf.sync(cacheDirectory);
rimraf.sync(testDirectory);
fs.mkdirSync(testDirectory);
fs.mkdirSync(cacheDirectory);
});

afterEach(() => {
jest.resetModules();
if (hasteMapInstance) {
hasteMapInstance.end();
}
rimraf.sync(cacheDirectory);
rimraf.sync(testDirectory);
});

it('should correct require new files without legacy cache', async () => {
fs.writeFileSync(
SimenB marked this conversation as resolved.
Show resolved Hide resolved
fileTargetPath2,
`
require('./lost-file.js');
describe('Fake test', () => {
it('Hey', () => {

});
});
`,
);

const config = normalize(
Connormiha marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason not to use createGlobalConfig and createProjectConfig from TestUtils.ts in root here? Instead of normalize

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makeGlobalConfig from TestUtils.ts doesn't use normalizer inside. Without it this test will not work correct.

{
cache: false,
cacheDirectory,
coverageReporters: [],
maxConcurrency: 1,
maxWorkers: 1,
moduleDirectories: ['node_modules'],
onlyChanged: false,
reporters: [],
rootDir: testDirectory,
silent: true,
testRegex: ['watch-test-fake\\.test\\.js$'],
watch: false,
watchman: false,
},
{} as any,
).options;

hasteMapInstance = await Runtime.createHasteMap(config, {
maxWorkers: 1,
resetCache: true,
watch: true,
watchman: false,
});

const realContext = await hasteMapInstance.build().then(hasteMap => ({
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 awaits instead of the then?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need both variables hasteMapInstance and realContext.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

const hasteMap = await hasteMapInstance.build();

const realContext = {
  config,
  hasteFS: hasteMap.hasteFS,
  moduleMap: hasteMap.moduleMap,
  resolver: Runtime.createResolver(config, hasteMap.moduleMap),
};

?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hasteMapInstance should be passed in watch. Build doens't return instance.

config,
hasteFS: hasteMap.hasteFS,
moduleMap: hasteMap.moduleMap,
resolver: Runtime.createResolver(config, hasteMap.moduleMap),
}));

const hook = new JestHook();
const firstErrorPromise = new Promise(resolve => {
hook.getSubscriber().onTestRunComplete(resolve);
});
await watch(
{
...config,
watchPlugins: [],
},
[realContext],
pipe,
[hasteMapInstance],
stdin,
hook,
);

await firstErrorPromise;

const successPromise = new Promise<AggregatedResult>(resolve => {
hook.getSubscriber().onTestRunComplete(resolve);
});

// Create lost file
fs.writeFileSync(
fileTargetPath,
`
describe('Fake group', () => {
it('Fake 1', () => {});
it('Fake 2', () => {});
it('Fake 3', () => {});
});
`,
);

const resultSuccessReport = await successPromise;

expect(resultSuccessReport).toMatchObject({
numFailedTestSuites: 0,
numFailedTests: 0,
numPassedTests: 4,
numRuntimeErrorTestSuites: 0,
success: true,
wasInterrupted: false,
});
expect(resultSuccessReport.testResults[0]).toMatchObject({
failureMessage: null,
});

const errorPromise = new Promise<AggregatedResult>(resolve => {
hook.getSubscriber().onTestRunComplete(resolve);
});

// Remove again to ensure about no legacy cache
fs.unlinkSync(fileTargetPath);

const resultErrorReport = await errorPromise;

// After remove file we have to fail tests
expect(resultErrorReport).toMatchObject({
numFailedTestSuites: 1,
numPassedTests: 0,
numRuntimeErrorTestSuites: 1,
success: false,
wasInterrupted: false,
});
});
});

class MockStdin {
private _callbacks: Array<any>;

constructor() {
this._callbacks = [];
}

resume() {}

setEncoding() {}

on(_: any, callback: any) {
this._callbacks.push(callback);
}

emit(key: string) {
this._callbacks.forEach(cb => cb(key));
}
}
4 changes: 4 additions & 0 deletions packages/jest-core/src/watch.ts
Expand Up @@ -15,6 +15,7 @@ import {formatExecError} from 'jest-message-util';
import {isInteractive, preRunMessage, specialChars} from 'jest-util';
import {ValidationError} from 'jest-validate';
import {Context} from 'jest-runtime';
import Resolver from 'jest-resolve';
import {Config} from '@jest/types';
import {
AllowedConfigOptions,
Expand Down Expand Up @@ -275,6 +276,9 @@ export default function watch(
isRunning = true;
const configs = contexts.map(context => context.config);
const changedFilesPromise = getChangedFilesPromise(globalConfig, configs);
// Clear cache for required modules
Resolver.clearDefaultResolverCache();

return runJest({
changedFilesPromise,
contexts,
Expand Down
4 changes: 4 additions & 0 deletions packages/jest-resolve/src/defaultResolver.ts
Expand Up @@ -44,6 +44,10 @@ export default function defaultResolver(
});
}

export const clearDefaultResolverCache = () => {
checkedPaths.clear();
};

const REGEX_RELATIVE_IMPORT = /^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[\\\/])/;

function resolveSync(
Expand Down
6 changes: 5 additions & 1 deletion packages/jest-resolve/src/index.ts
Expand Up @@ -12,7 +12,7 @@ import {sync as realpath} from 'realpath-native';
import chalk from 'chalk';
import nodeModulesPaths from './nodeModulesPaths';
import isBuiltinModule from './isBuiltinModule';
import defaultResolver from './defaultResolver';
import defaultResolver, {clearDefaultResolverCache} from './defaultResolver';
import {ResolverConfig} from './types';

type FindNodeModuleConfig = {
Expand Down Expand Up @@ -79,6 +79,10 @@ class Resolver {
this._modulePathCache = new Map();
}

static clearDefaultResolverCache() {
clearDefaultResolverCache();
}

static findNodeModule(
path: Config.Path,
options: FindNodeModuleConfig,
Expand Down