metro: traverseDependencies-integration-test: switch to the memory FS

Reviewed By: rafeca

Differential Revision: D7257733

fbshipit-source-id: a82583bfed0921a69e4518bf32bcdb4515f50c94
This commit is contained in:
Jean Lauliac 2018-04-11 18:00:02 -07:00 committed by Facebook Github Bot
parent 13e0844dcf
commit 55e7e50232
1 changed files with 145 additions and 137 deletions

View File

@ -12,16 +12,16 @@
jest.useRealTimers(); jest.useRealTimers();
jest jest
.mock('fs')
.mock('graceful-fs')
.mock('metro-core')
.mock('../../lib/TransformCaching') .mock('../../lib/TransformCaching')
// It's noticeably faster to prevent running watchman from FileWatcher. // It's noticeably faster to prevent running watchman from FileWatcher.
.mock('child_process', () => ({})) .mock('child_process', () => ({}))
.mock('os', () => ({ .mock('os', () => ({
...require.requireActual('os'),
platform: () => 'test', platform: () => 'test',
})); tmpdir: () => (process.platform === 'win32' ? 'C:\\tmp' : '/tmp'),
hostname: () => 'testhost',
endianness: () => 'LE',
}))
.mock('graceful-fs', () => require('fs'));
// Super-simple mock for extracting dependencies // Super-simple mock for extracting dependencies
const extractDependencies = function(sourceCode: string) { const extractDependencies = function(sourceCode: string) {
@ -36,8 +36,6 @@ const extractDependencies = function(sourceCode: string) {
return deps; return deps;
}; };
jest.mock('graceful-fs', () => require('fs'));
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
beforeEach(() => { beforeEach(() => {
@ -94,6 +92,7 @@ describe('traverseDependencies', function() {
beforeEach(function() { beforeEach(function() {
jest.resetModules(); jest.resetModules();
jest.mock('fs', () => new (require('metro-memory-fs'))());
Module = require('../../node-haste/Module'); Module = require('../../node-haste/Module');
traverseDependencies = require('../traverseDependencies'); traverseDependencies = require('../traverseDependencies');
@ -2336,6 +2335,8 @@ describe('traverseDependencies', function() {
// reload path module // reload path module
jest.resetModules(); jest.resetModules();
jest.mock('path', () => require.requireActual('path').win32); jest.mock('path', () => require.requireActual('path').win32);
jest.mock('fs', () => new (require('metro-memory-fs'))('win32'));
require('os').tmpdir = () => 'c:\\tmp';
DependencyGraph = require('../../node-haste/DependencyGraph'); DependencyGraph = require('../../node-haste/DependencyGraph');
processDgraph = processDgraphFor.bind(null, DependencyGraph); processDgraph = processDgraphFor.bind(null, DependencyGraph);
}); });
@ -2984,10 +2985,16 @@ describe('traverseDependencies', function() {
expect(error.originModulePath).toBe('/root/index.js'); expect(error.originModulePath).toBe('/root/index.js');
expect(error.targetModuleName).toBe('dontWork'); expect(error.targetModuleName).toBe('dontWork');
} }
filesystem.root['index.js'] = filesystem.root['index.js'] return triggerAndProcessWatchEvent(dgraph, () => {
const fs = require('fs');
const code = fs.readFileSync(root + '/index.js', 'utf8');
fs.writeFileSync(
root + '/index.js',
code
.replace('require("dontWork")', '') .replace('require("dontWork")', '')
.replace('require("wontWork")', ''); .replace('require("wontWork")', ''),
return triggerAndProcessWatchEvent(dgraph, 'change', root + '/index.js') );
})
.then(() => getOrderedDependenciesAsJSON(dgraph, '/root/index.js')) .then(() => getOrderedDependenciesAsJSON(dgraph, '/root/index.js'))
.then(deps => { .then(deps => {
expect(deps).toEqual([ expect(deps).toEqual([
@ -3416,6 +3423,8 @@ describe('traverseDependencies', function() {
// reload path module // reload path module
jest.resetModules(); jest.resetModules();
jest.mock('path', () => require.requireActual('path').win32); jest.mock('path', () => require.requireActual('path').win32);
jest.mock('fs', () => new (require('metro-memory-fs'))('win32'));
require('os').tmpdir = () => 'c:\\tmp';
DependencyGraph = require('../../node-haste/DependencyGraph'); DependencyGraph = require('../../node-haste/DependencyGraph');
processDgraph = processDgraphFor.bind(null, DependencyGraph); processDgraph = processDgraphFor.bind(null, DependencyGraph);
({ ({
@ -3900,10 +3909,16 @@ describe('traverseDependencies', function() {
expect(error.originModulePath).toBe('C:\\root\\index.js'); expect(error.originModulePath).toBe('C:\\root\\index.js');
expect(error.targetModuleName).toBe('dontWork'); expect(error.targetModuleName).toBe('dontWork');
} }
filesystem.root['index.js'] = filesystem.root['index.js'] await triggerAndProcessWatchEvent(dgraph, () => {
const fs = require('fs');
fs.writeFileSync(
entryPath,
fs
.readFileSync(entryPath, 'utf8')
.replace('require("dontWork")', '') .replace('require("dontWork")', '')
.replace('require("wontWork")', ''); .replace('require("wontWork")', ''),
await triggerAndProcessWatchEvent(dgraph, 'change', entryPath); );
});
const deps = await getOrderedDependenciesAsJSON(dgraph, entryPath); const deps = await getOrderedDependenciesAsJSON(dgraph, entryPath);
expect(deps).toEqual([ expect(deps).toEqual([
{ {
@ -4275,6 +4290,7 @@ describe('traverseDependencies', function() {
describe('file watch updating', function() { describe('file watch updating', function() {
let DependencyGraph; let DependencyGraph;
let processDgraph; let processDgraph;
let fs;
beforeEach(function() { beforeEach(function() {
Object.defineProperty(process, 'platform', { Object.defineProperty(process, 'platform', {
@ -4285,11 +4301,12 @@ describe('traverseDependencies', function() {
DependencyGraph = require('../../node-haste/DependencyGraph'); DependencyGraph = require('../../node-haste/DependencyGraph');
processDgraph = processDgraphFor.bind(null, DependencyGraph); processDgraph = processDgraphFor.bind(null, DependencyGraph);
fs = require('fs');
}); });
it('updates module dependencies', async () => { it('updates module dependencies', async () => {
var root = '/root'; var root = '/root';
var filesystem = setMockFileSystem({ setMockFileSystem({
root: { root: {
'index.js': [ 'index.js': [
'/**', '/**',
@ -4318,11 +4335,12 @@ describe('traverseDependencies', function() {
const entryPath = '/root/index.js'; const entryPath = '/root/index.js';
await processDgraph(opts, async dgraph => { await processDgraph(opts, async dgraph => {
await getOrderedDependenciesAsJSON(dgraph, entryPath); await getOrderedDependenciesAsJSON(dgraph, entryPath);
filesystem.root['index.js'] = filesystem.root['index.js'].replace( await triggerAndProcessWatchEvent(dgraph, () => {
'require("foo")', fs.writeFileSync(
'', entryPath,
fs.readFileSync(entryPath, 'utf8').replace('require("foo")', ''),
); );
await triggerAndProcessWatchEvent(dgraph, 'change', entryPath); });
const deps = await getOrderedDependenciesAsJSON(dgraph, entryPath); const deps = await getOrderedDependenciesAsJSON(dgraph, entryPath);
expect(deps).toEqual([ expect(deps).toEqual([
{ {
@ -4345,7 +4363,7 @@ describe('traverseDependencies', function() {
it('updates module dependencies on file change', async () => { it('updates module dependencies on file change', async () => {
var root = '/root'; var root = '/root';
var filesystem = setMockFileSystem({ setMockFileSystem({
root: { root: {
'index.js': [ 'index.js': [
'/**', '/**',
@ -4374,11 +4392,12 @@ describe('traverseDependencies', function() {
const entryPath = '/root/index.js'; const entryPath = '/root/index.js';
await processDgraph(opts, async dgraph => { await processDgraph(opts, async dgraph => {
await getOrderedDependenciesAsJSON(dgraph, entryPath); await getOrderedDependenciesAsJSON(dgraph, entryPath);
filesystem.root['index.js'] = filesystem.root['index.js'].replace( await triggerAndProcessWatchEvent(dgraph, () => {
'require("foo")', fs.writeFileSync(
'', entryPath,
fs.readFileSync(entryPath, 'utf8').replace('require("foo")', ''),
); );
await triggerAndProcessWatchEvent(dgraph, 'change', root + '/index.js'); });
const deps = await getOrderedDependenciesAsJSON(dgraph, entryPath); const deps = await getOrderedDependenciesAsJSON(dgraph, entryPath);
expect(deps).toEqual([ expect(deps).toEqual([
{ {
@ -4401,7 +4420,7 @@ describe('traverseDependencies', function() {
it('updates module dependencies on file delete', async () => { it('updates module dependencies on file delete', async () => {
var root = '/root'; var root = '/root';
var filesystem = setMockFileSystem({ setMockFileSystem({
root: { root: {
'index.js': [ 'index.js': [
'/**', '/**',
@ -4430,8 +4449,9 @@ describe('traverseDependencies', function() {
const entryPath = '/root/index.js'; const entryPath = '/root/index.js';
await processDgraph(opts, async dgraph => { await processDgraph(opts, async dgraph => {
await getOrderedDependenciesAsJSON(dgraph, entryPath); await getOrderedDependenciesAsJSON(dgraph, entryPath);
delete filesystem.root['foo.js']; await triggerAndProcessWatchEvent(dgraph, () => {
await triggerAndProcessWatchEvent(dgraph, 'change', root + '/foo.js'); fs.unlinkSync(root + '/foo.js');
});
try { try {
await getOrderedDependenciesAsJSON(dgraph, '/root/index.js'); await getOrderedDependenciesAsJSON(dgraph, '/root/index.js');
throw new Error('should be unreachable'); throw new Error('should be unreachable');
@ -4448,7 +4468,7 @@ describe('traverseDependencies', function() {
it('updates module dependencies on file add', async () => { it('updates module dependencies on file add', async () => {
expect.assertions(1); expect.assertions(1);
var root = '/root'; var root = '/root';
var filesystem = setMockFileSystem({ setMockFileSystem({
root: { root: {
'index.js': [ 'index.js': [
'/**', '/**',
@ -4477,19 +4497,15 @@ describe('traverseDependencies', function() {
const entryPath = '/root/index.js'; const entryPath = '/root/index.js';
await processDgraph(opts, async dgraph => { await processDgraph(opts, async dgraph => {
await getOrderedDependenciesAsJSON(dgraph, entryPath); await getOrderedDependenciesAsJSON(dgraph, entryPath);
filesystem.root['bar.js'] = [ await triggerAndProcessWatchEvent(dgraph, () => {
'/**', fs.writeFileSync(
' * @providesModule bar', root + '/bar.js',
' */', ['/**', ' * @providesModule bar', ' */', 'require("foo")'].join(
'require("foo")', '\n',
].join('\n'); ),
await triggerAndProcessWatchEvent(dgraph, 'change', root + '/bar.js');
filesystem.root.aPackage['main.js'] = 'require("bar")';
await triggerAndProcessWatchEvent(
dgraph,
'change',
root + '/aPackage/main.js',
); );
fs.writeFileSync(root + '/aPackage/main.js', 'require("bar")');
});
const deps = await getOrderedDependenciesAsJSON(dgraph, entryPath); const deps = await getOrderedDependenciesAsJSON(dgraph, entryPath);
expect(deps).toEqual([ expect(deps).toEqual([
{ {
@ -4528,7 +4544,7 @@ describe('traverseDependencies', function() {
it('updates module dependencies on relative asset add', async () => { it('updates module dependencies on relative asset add', async () => {
var root = '/root'; var root = '/root';
var filesystem = setMockFileSystem({ setMockFileSystem({
root: { root: {
'index.js': [ 'index.js': [
'/**', '/**',
@ -4555,8 +4571,9 @@ describe('traverseDependencies', function() {
expect(error.originModulePath).toBe('/root/index.js'); expect(error.originModulePath).toBe('/root/index.js');
expect(error.targetModuleName).toBe('./foo.png'); expect(error.targetModuleName).toBe('./foo.png');
} }
filesystem.root['foo.png'] = ''; await triggerAndProcessWatchEvent(dgraph, () => {
await triggerAndProcessWatchEvent(dgraph, 'change', root + '/foo.png'); fs.writeFileSync(root + '/foo.png', '');
});
const deps = await getOrderedDependenciesAsJSON(dgraph, entryPath); const deps = await getOrderedDependenciesAsJSON(dgraph, entryPath);
expect(deps).toEqual([ expect(deps).toEqual([
{ {
@ -4582,7 +4599,7 @@ describe('traverseDependencies', function() {
it('changes to browser field', async () => { it('changes to browser field', async () => {
expect.assertions(1); expect.assertions(1);
var root = '/root'; var root = '/root';
var filesystem = setMockFileSystem({ setMockFileSystem({
root: { root: {
'index.js': [ 'index.js': [
'/**', '/**',
@ -4605,16 +4622,16 @@ describe('traverseDependencies', function() {
const entryPath = '/root/index.js'; const entryPath = '/root/index.js';
await processDgraph(opts, async dgraph => { await processDgraph(opts, async dgraph => {
await getOrderedDependenciesAsJSON(dgraph, entryPath); await getOrderedDependenciesAsJSON(dgraph, entryPath);
filesystem.root.aPackage['package.json'] = JSON.stringify({ await triggerAndProcessWatchEvent(dgraph, () => {
fs.writeFileSync(
root + '/aPackage/package.json',
JSON.stringify({
name: 'aPackage', name: 'aPackage',
main: 'main.js', main: 'main.js',
browser: 'browser.js', browser: 'browser.js',
}); }),
await triggerAndProcessWatchEvent(
dgraph,
'change',
root + '/aPackage/package.json',
); );
});
const deps = await getOrderedDependenciesAsJSON(dgraph, entryPath); const deps = await getOrderedDependenciesAsJSON(dgraph, entryPath);
expect(deps).toEqual([ expect(deps).toEqual([
{ {
@ -4639,7 +4656,7 @@ describe('traverseDependencies', function() {
it('removes old package from cache', async () => { it('removes old package from cache', async () => {
var root = '/root'; var root = '/root';
var filesystem = setMockFileSystem({ setMockFileSystem({
root: { root: {
'index.js': [ 'index.js': [
'/**', '/**',
@ -4662,20 +4679,23 @@ describe('traverseDependencies', function() {
const entryPath = '/root/index.js'; const entryPath = '/root/index.js';
await processDgraph(opts, async dgraph => { await processDgraph(opts, async dgraph => {
await getOrderedDependenciesAsJSON(dgraph, entryPath); await getOrderedDependenciesAsJSON(dgraph, entryPath);
filesystem.root['index.js'] = [ await triggerAndProcessWatchEvent(dgraph, () => {
fs.writeFileSync(
root + '/index.js',
[
'/**', '/**',
' * @providesModule index', ' * @providesModule index',
' */', ' */',
'require("bPackage")', 'require("bPackage")',
].join('\n'); ].join('\n'),
filesystem.root.aPackage['package.json'] = JSON.stringify({ );
fs.writeFileSync(
root + '/aPackage/package.json',
JSON.stringify({
name: 'bPackage', name: 'bPackage',
main: 'main.js', main: 'main.js',
}); }),
await new Promise(resolve => { );
dgraph.once('change', () => resolve());
triggerWatchEvent('change', root + '/index.js');
triggerWatchEvent('change', root + '/aPackage/package.json');
}); });
const deps = await getOrderedDependenciesAsJSON(dgraph, entryPath); const deps = await getOrderedDependenciesAsJSON(dgraph, entryPath);
expect(deps).toEqual([ expect(deps).toEqual([
@ -4701,7 +4721,7 @@ describe('traverseDependencies', function() {
it('should update node package changes', async () => { it('should update node package changes', async () => {
expect.assertions(2); expect.assertions(2);
var root = '/root'; var root = '/root';
var filesystem = setMockFileSystem({ setMockFileSystem({
root: { root: {
'index.js': [ 'index.js': [
'/**', '/**',
@ -4761,12 +4781,9 @@ describe('traverseDependencies', function() {
}, },
]); ]);
filesystem.root.node_modules.foo['main.js'] = 'lol'; await triggerAndProcessWatchEvent(dgraph, () => {
await triggerAndProcessWatchEvent( fs.writeFileSync(root + '/node_modules/foo/main.js', 'lol');
dgraph, });
'change',
root + '/node_modules/foo/main.js',
);
const deps2 = await getOrderedDependenciesAsJSON(dgraph, entryPath); const deps2 = await getOrderedDependenciesAsJSON(dgraph, entryPath);
expect(deps2).toEqual([ expect(deps2).toEqual([
{ {
@ -4793,7 +4810,7 @@ describe('traverseDependencies', function() {
it('should update node package main changes', async () => { it('should update node package main changes', async () => {
expect.assertions(1); expect.assertions(1);
var root = '/root'; var root = '/root';
var filesystem = setMockFileSystem({ setMockFileSystem({
root: { root: {
'index.js': [ 'index.js': [
'/**', '/**',
@ -4818,16 +4835,16 @@ describe('traverseDependencies', function() {
const entryPath = '/root/index.js'; const entryPath = '/root/index.js';
await processDgraph(opts, async dgraph => { await processDgraph(opts, async dgraph => {
await getOrderedDependenciesAsJSON(dgraph, entryPath); await getOrderedDependenciesAsJSON(dgraph, entryPath);
filesystem.root.node_modules.foo['package.json'] = JSON.stringify({ await triggerAndProcessWatchEvent(dgraph, () => {
fs.writeFileSync(
root + '/node_modules/foo/package.json',
JSON.stringify({
name: 'foo', name: 'foo',
main: 'main.js', main: 'main.js',
browser: 'browser.js', browser: 'browser.js',
}); }),
await triggerAndProcessWatchEvent(
dgraph,
'change',
root + '/node_modules/foo/package.json',
); );
});
const deps = await getOrderedDependenciesAsJSON(dgraph, entryPath); const deps = await getOrderedDependenciesAsJSON(dgraph, entryPath);
expect(deps).toEqual([ expect(deps).toEqual([
{ {
@ -4851,40 +4868,10 @@ describe('traverseDependencies', function() {
}); });
}); });
it('should not error when the watcher reports a known file as added', async () => {
expect.assertions(1);
var root = '/root';
setMockFileSystem({
root: {
'index.js': [
'/**',
' * @providesModule index',
' */',
'var b = require("b");',
].join('\n'),
'b.js': [
'/**',
' * @providesModule b',
' */',
'module.exports = function() {};',
].join('\n'),
},
});
const opts = {...defaults, projectRoots: [root]};
const entryPath = '/root/index.js';
await processDgraph(opts, async dgraph => {
await getOrderedDependenciesAsJSON(dgraph, entryPath);
await triggerAndProcessWatchEvent(dgraph, 'change', root + '/index.js');
const deps = await getOrderedDependenciesAsJSON(dgraph, entryPath);
expect(deps).toBeDefined();
});
});
it('should recover from multiple modules with the same name', async () => { it('should recover from multiple modules with the same name', async () => {
const root = '/root'; const root = '/root';
console.warn = jest.fn(); console.warn = jest.fn();
const filesystem = setMockFileSystem({ setMockFileSystem({
root: { root: {
'index.js': [ 'index.js': [
'/**', '/**',
@ -4902,12 +4889,14 @@ describe('traverseDependencies', function() {
const entryPath = '/root/index.js'; const entryPath = '/root/index.js';
await processDgraph(opts, async dgraph => { await processDgraph(opts, async dgraph => {
await getOrderedDependenciesAsJSON(dgraph, entryPath); await getOrderedDependenciesAsJSON(dgraph, entryPath);
filesystem.root['b.js'] = ['/**', ' * @providesModule a', ' */'].join( await triggerAndProcessWatchEvent(dgraph, () => {
'\n', fs.writeFileSync(
root + '/b.js',
['/**', ' * @providesModule a', ' */'].join('\n'),
); );
await triggerAndProcessWatchEvent(dgraph, 'change', root + '/b.js'); });
try { try {
await getOrderedDependenciesAsJSON(dgraph, root + '/index.js'); await getOrderedDependenciesAsJSON(dgraph, entryPath);
throw new Error('expected `getOrderedDependenciesAsJSON` to fail'); throw new Error('expected `getOrderedDependenciesAsJSON` to fail');
} catch (error) { } catch (error) {
const {AmbiguousModuleResolutionError} = require('metro-core'); const {AmbiguousModuleResolutionError} = require('metro-core');
@ -4915,11 +4904,13 @@ describe('traverseDependencies', function() {
throw error; throw error;
} }
expect(console.warn).toBeCalled(); expect(console.warn).toBeCalled();
filesystem.root['b.js'] = ['/**', ' * @providesModule b', ' */'].join(
'\n',
);
await triggerAndProcessWatchEvent(dgraph, 'change', root + '/b.js');
} }
await triggerAndProcessWatchEvent(dgraph, () => {
fs.writeFileSync(
root + '/b.js',
['/**', ' * @providesModule b', ' */'].join('\n'),
);
});
const deps = await getOrderedDependenciesAsJSON(dgraph, entryPath); const deps = await getOrderedDependenciesAsJSON(dgraph, entryPath);
expect(deps).toMatchSnapshot(); expect(deps).toMatchSnapshot();
}); });
@ -5340,20 +5331,37 @@ describe('traverseDependencies', function() {
} }
function setMockFileSystem(object) { function setMockFileSystem(object) {
return require('fs').__setMockFilesystem(object); const fs = require('fs');
const root = process.platform === 'win32' ? 'c:\\' : '/';
mockDir(fs, root, {...object, tmp: {}});
} }
function triggerAndProcessWatchEvent(dgraphPromise, eventType, filename) { function mockDir(fs, dirPath, desc) {
for (const entName in desc) {
const ent = desc[entName];
const entPath = require('path').join(dirPath, entName);
if (typeof ent === 'string') {
fs.writeFileSync(entPath, ent);
continue;
}
if (typeof ent !== 'object') {
throw new Error(require('util').format('invalid entity:', ent));
}
fs.mkdirSync(entPath);
mockDir(fs, entPath, ent);
}
}
function triggerAndProcessWatchEvent(dgraphPromise, fsOperation) {
return Promise.resolve(dgraphPromise).then( return Promise.resolve(dgraphPromise).then(
dgraph => dgraph =>
new Promise(resolve => { new Promise(resolve => {
dgraph.once('change', () => resolve()); // FIXME: Timeout is needed to wait for thing to settle down a bit.
triggerWatchEvent(eventType, filename); // This adds flakiness to this test, and normally should not be
// needed.
dgraph.once('change', () => setTimeout(resolve, 100));
fsOperation();
}), }),
); );
} }
function triggerWatchEvent(eventType, filename) {
return require('fs').__triggerWatchEvent(eventType, filename);
}
}); });