packager: DependencyGraph-test: go through fs.watch mock rather than manual API

Reviewed By: davidaurelio

Differential Revision: D4628593

fbshipit-source-id: 43ccdb038bd387e87096f2a7020c98d915fa5bba
This commit is contained in:
Jean Lauliac 2017-03-01 07:50:39 -08:00 committed by Facebook Github Bot
parent 556c432260
commit 9aa1215e21
3 changed files with 111 additions and 66 deletions

View File

@ -8,6 +8,7 @@
*/ */
'use strict'; 'use strict';
const {EventEmitter} = require('events');
const {dirname} = require.requireActual('path'); const {dirname} = require.requireActual('path');
const fs = jest.genMockFromModule('fs'); const fs = jest.genMockFromModule('fs');
const path = require('path'); const path = require('path');
@ -96,7 +97,12 @@ fs.readFileSync.mockImplementation(function(filepath, encoding) {
function makeStatResult(node) { function makeStatResult(node) {
const isSymlink = node != null && node.SYMLINK != null; const isSymlink = node != null && node.SYMLINK != null;
return { return {
isBlockDevice: () => false,
isCharacterDevice: () => false,
isDirectory: () => node != null && typeof node === 'object' && !isSymlink, isDirectory: () => node != null && typeof node === 'object' && !isSymlink,
isFIFO: () => false,
isFile: () => node != null && typeof node === 'string',
isSocket: () => false,
isSymbolicLink: () => isSymlink, isSymbolicLink: () => isSymlink,
mtime, mtime,
}; };
@ -242,6 +248,34 @@ fs.createWriteStream.mock.returned = [];
fs.__setMockFilesystem = object => (filesystem = object); fs.__setMockFilesystem = object => (filesystem = object);
const watcherListByPath = new Map();
fs.watch.mockImplementation((filename, options, listener) => {
if (options.recursive) {
throw new Error('recursive watch not implemented');
}
let watcherList = watcherListByPath.get(filename);
if (watcherList == null) {
watcherList = [];
watcherListByPath.set(filename, watcherList);
}
const fsWatcher = new EventEmitter();
fsWatcher.on('change', listener);
fsWatcher.close = () => {
watcherList.splice(watcherList.indexOf(fsWatcher), 1);
fsWatcher.close = () => { throw new Error('FSWatcher is already closed'); };
};
watcherList.push(fsWatcher);
});
fs.__triggerWatchEvent = (eventType, filename) => {
const directWatchers = watcherListByPath.get(filename) || [];
directWatchers.forEach(wtc => wtc.emit('change', eventType));
const dirPath = path.dirname(filename);
const dirWatchers = watcherListByPath.get(dirPath) || [];
dirWatchers.forEach(wtc => wtc.emit('change', eventType, path.relative(dirPath, filename)));
};
function getToNode(filepath) { function getToNode(filepath) {
// Ignore the drive for Windows paths. // Ignore the drive for Windows paths.
if (filepath.match(/^[a-zA-Z]:\\/)) { if (filepath.match(/^[a-zA-Z]:\\/)) {

View File

@ -130,7 +130,7 @@ describe('DependencyGraph', function() {
}, },
getTransformCacheKey: () => 'abcdef', getTransformCacheKey: () => 'abcdef',
reporter: require('../../lib/reporting').nullReporter, reporter: require('../../lib/reporting').nullReporter,
watch: false, watch: true,
}; };
}); });
@ -3111,70 +3111,74 @@ describe('DependencyGraph', function() {
filesystem.root['index.js'] = filesystem.root['index.js'] filesystem.root['index.js'] = filesystem.root['index.js']
.replace('require("dontWork")', '') .replace('require("dontWork")', '')
.replace('require("wontWork")', ''); .replace('require("wontWork")', '');
dgraph.processFileChange('change', root + '/index.js', mockStat); triggerWatchEvent('change', root + '/index.js');
return getOrderedDependenciesAsJSON(dgraph, '/root/index.js') return new Promise(resolve => {
.then(deps => { dgraph.once('change', () => {
expect(deps).toEqual([ return resolve(getOrderedDependenciesAsJSON(dgraph, '/root/index.js')
{ .then(deps => {
id: 'index', expect(deps).toEqual([
path: '/root/index.js', {
dependencies: [ id: 'index',
'shouldWork', path: '/root/index.js',
'ember', dependencies: [
'internalVendoredPackage', 'shouldWork',
'anotherIndex', 'ember',
], 'internalVendoredPackage',
isAsset: false, 'anotherIndex',
isJSON: false, ],
isPolyfill: false, isAsset: false,
resolution: undefined, isJSON: false,
}, isPolyfill: false,
{ resolution: undefined,
id: 'shouldWork', },
path: '/root/node_modules/react-haste/main.js', {
dependencies: ['submodule'], id: 'shouldWork',
isAsset: false, path: '/root/node_modules/react-haste/main.js',
isJSON: false, dependencies: ['submodule'],
isPolyfill: false, isAsset: false,
resolution: undefined, isJSON: false,
}, isPolyfill: false,
{ resolution: undefined,
id: 'submodule/main.js', },
path: '/root/node_modules/react-haste/node_modules/submodule/main.js', {
dependencies: [], id: 'submodule/main.js',
isAsset: false, path: '/root/node_modules/react-haste/node_modules/submodule/main.js',
isJSON: false, dependencies: [],
isPolyfill: false, isAsset: false,
resolution: undefined, isJSON: false,
}, isPolyfill: false,
{ resolution: undefined,
id: 'ember/main.js', },
path: '/root/node_modules/ember/main.js', {
dependencies: [], id: 'ember/main.js',
isAsset: false, path: '/root/node_modules/ember/main.js',
isJSON: false, dependencies: [],
isPolyfill: false, isAsset: false,
resolution: undefined, isJSON: false,
}, isPolyfill: false,
{ resolution: undefined,
id: 'internalVendoredPackage', },
path: '/root/vendored_modules/a-vendored-package/main.js', {
dependencies: [], id: 'internalVendoredPackage',
isAsset: false, path: '/root/vendored_modules/a-vendored-package/main.js',
isJSON: false, dependencies: [],
isPolyfill: false, isAsset: false,
resolution: undefined, isJSON: false,
}, isPolyfill: false,
{ resolution: undefined,
id: 'anotherIndex', },
path: '/anotherRoot/index.js', {
dependencies: [], id: 'anotherIndex',
isAsset: false, path: '/anotherRoot/index.js',
isJSON: false, dependencies: [],
isPolyfill: false, isAsset: false,
resolution: undefined, isJSON: false,
}, isPolyfill: false,
]); resolution: undefined,
},
]);
}));
});
}); });
}); });
}); });
@ -5455,4 +5459,8 @@ describe('DependencyGraph', function() {
function setMockFileSystem(object) { function setMockFileSystem(object) {
return require('graceful-fs').__setMockFilesystem(object); return require('graceful-fs').__setMockFilesystem(object);
} }
function triggerWatchEvent(eventType, filename) {
return require('graceful-fs').__triggerWatchEvent(eventType, filename);
}
}); });

View File

@ -36,6 +36,7 @@ const {
createActionStartEntry, createActionStartEntry,
log, log,
} = require('../Logger'); } = require('../Logger');
const {EventEmitter} = require('events');
import type {Options as TransformOptions} from '../JSTransformer/worker/worker'; import type {Options as TransformOptions} from '../JSTransformer/worker/worker';
import type GlobalTransformCache from '../lib/GlobalTransformCache'; import type GlobalTransformCache from '../lib/GlobalTransformCache';
@ -72,7 +73,7 @@ type Options = {
watch: boolean, watch: boolean,
}; };
class DependencyGraph { class DependencyGraph extends EventEmitter {
_opts: Options; _opts: Options;
_haste: JestHasteMap; _haste: JestHasteMap;
_hasteFS: HasteFS; _hasteFS: HasteFS;
@ -84,6 +85,7 @@ class DependencyGraph {
_loading: Promise<void>; _loading: Promise<void>;
constructor(opts: Options) { constructor(opts: Options) {
super();
this._opts = {...opts}; this._opts = {...opts};
this._helpers = new DependencyGraphHelpers(this._opts); this._helpers = new DependencyGraphHelpers(this._opts);
this.load(); this.load();
@ -154,6 +156,7 @@ class DependencyGraph {
eventsQueue.forEach(({type, filePath, stat}) => eventsQueue.forEach(({type, filePath, stat}) =>
this.processFileChange(type, filePath, stat) this.processFileChange(type, filePath, stat)
); );
this.emit('change');
}); });
const buildingHasteMapLogEntry = const buildingHasteMapLogEntry =