Skip to content

Commit

Permalink
Add fsChange hook for plugins (#5708)
Browse files Browse the repository at this point in the history
* Add fsChange hook for plugins

* Rename fsChange to fileChange
  • Loading branch information
rogeliog authored and cpojer committed Mar 3, 2018
1 parent 27a1dc6 commit 5fabc34
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 0 deletions.
60 changes: 60 additions & 0 deletions packages/jest-cli/src/__tests__/watch.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,31 @@ const watchPluginPath = `${__dirname}/__fixtures__/watch_plugin`;
const watchPlugin2Path = `${__dirname}/__fixtures__/watch_plugin2`;
let results;

jest.mock(
'../search_source',
() =>
class {
constructor(context) {
this._context = context;
}

findMatchingTests(pattern) {
const paths = [
'./path/to/file1-test.js',
'./path/to/file2-test.js',
].filter(path => path.match(pattern));

return {
tests: paths.map(path => ({
context: this._context,
duration: null,
path,
})),
};
}
},
);

jest.doMock('chalk', () => new chalk.constructor({enabled: false}));
jest.doMock(
'../run_jest',
Expand Down Expand Up @@ -274,6 +299,41 @@ describe('Watch mode flows', () => {
expect(apply).toHaveBeenCalled();
});

it('allows WatchPlugins to hook into file system changes', async () => {
const fileChange = jest.fn();
const pluginPath = `${__dirname}/__fixtures__/plugin_path_fs_change`;
jest.doMock(
pluginPath,
() =>
class WatchPlugin {
apply(jestHooks) {
jestHooks.fileChange(fileChange);
}
},
{virtual: true},
);

watch(
Object.assign({}, globalConfig, {
rootDir: __dirname,
watchPlugins: [pluginPath],
}),
contexts,
pipe,
hasteMapInstances,
stdin,
);

expect(fileChange).toHaveBeenCalledWith({
projects: [
{
config: contexts[0].config,
testPaths: ['./path/to/file1-test.js', './path/to/file2-test.js'],
},
],
});
});

it('triggers enter on a WatchPlugin when its key is pressed', async () => {
const run = jest.fn(() => Promise.resolve());
const pluginPath = `${__dirname}/__fixtures__/plugin_path`;
Expand Down
19 changes: 19 additions & 0 deletions packages/jest-cli/src/jest_hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,52 @@
*/

import type {AggregatedResult} from 'types/TestResult';
import type {ProjectConfig, Path} from 'types/Config';

type JestHookExposedFS = {
projects: Array<{config: ProjectConfig, testPaths: Array<Path>}>,
};

type FsChange = (fs: JestHookExposedFS) => void;
type ShouldRunTestSuite = (testPath: string) => Promise<boolean>;
type TestRunComplete = (results: AggregatedResult) => void;

export type JestHookSubscriber = {
fileChange: (fn: FsChange) => void,
shouldRunTestSuite: (fn: ShouldRunTestSuite) => void,
testRunComplete: (fn: TestRunComplete) => void,
};

export type JestHookEmitter = {
fileChange: (fs: JestHookExposedFS) => void,
shouldRunTestSuite: (testPath: string) => Promise<boolean>,
testRunComplete: (results: AggregatedResult) => void,
};

class JestHooks {
_listeners: {
fileChange: Array<FsChange>,
shouldRunTestSuite: Array<ShouldRunTestSuite>,
testRunComplete: Array<TestRunComplete>,
};

constructor() {
this._listeners = {
fileChange: [],
shouldRunTestSuite: [],
testRunComplete: [],
};
}

isUsed(hook: string) {
return this._listeners[hook] && this._listeners[hook].length;
}

getSubscriber(): JestHookSubscriber {
return {
fileChange: fn => {
this._listeners.fileChange.push(fn);
},
shouldRunTestSuite: fn => {
this._listeners.shouldRunTestSuite.push(fn);
},
Expand All @@ -48,6 +65,8 @@ class JestHooks {

getEmitter(): JestHookEmitter {
return {
fileChange: fs =>
this._listeners.fileChange.forEach(listener => listener(fs)),
shouldRunTestSuite: async testPath =>
Promise.all(
this._listeners.shouldRunTestSuite.map(listener =>
Expand Down
13 changes: 13 additions & 0 deletions packages/jest-cli/src/watch.js
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,18 @@ export default function watch(
let shouldDisplayWatchUsage = true;
let isWatchUsageDisplayed = false;

const emitFsChange = () => {
if (hooks.isUsed('fileChange')) {
const projects = searchSources.map(({context, searchSource}) => ({
config: context.config,
testPaths: searchSource.findMatchingTests('').tests.map(t => t.path),
}));
hooks.getEmitter().fileChange({projects});
}
};

emitFsChange();

hasteMapInstances.forEach((hasteMapInstance, index) => {
hasteMapInstance.on('change', ({eventsQueue, hasteFS, moduleMap}) => {
const validPaths = eventsQueue.filter(({filePath}) => {
Expand All @@ -176,6 +188,7 @@ export default function watch(
context,
searchSource: new SearchSource(context),
};
emitFsChange();
startRun(globalConfig);
}
});
Expand Down

0 comments on commit 5fabc34

Please sign in to comment.