add tests

Summary: Tests for things introduced over the last 10 weeks

Reviewed By: cpojer

Differential Revision: D4442404

fbshipit-source-id: cf7a14f237360fde083b7e2ffcd9d70aafd0ced6
This commit is contained in:
David Aurelio 2017-01-23 03:47:26 -08:00 committed by Facebook Github Bot
parent 212d31e322
commit 2ab7988cbe
5 changed files with 268 additions and 5 deletions

View File

@ -96,7 +96,6 @@ function* concat<T>(...iterables: Array<Iterable<T>>): Iterable<T> {
function prelude(optimize) {
return virtualModule(
`var __DEV__= ${String(!optimize)
}, __BUNDLE_START_TIME__ = Date.now();`
`var __DEV__=${String(!optimize)},__BUNDLE_START_TIME__=Date.now();`
);
}

View File

@ -0,0 +1,119 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
jest.disableAutomock();
const ModuleGraph = require('../ModuleGraph');
const defaults = require('../../../../defaults');
const FILE_TYPE = 'module';
describe('build setup', () => {
const buildSetup = ModuleGraph.createBuildSetup(graph);
const noOptions = {};
const noEntryPoints = [];
it('adds a prelude containing start time and `__DEV__` to the build', done => {
buildSetup(noEntryPoints, noOptions, (error, result) => {
expect(error).toEqual(null);
const [prelude] = result.modules;
expect(prelude).toEqual({
dependencies: [],
file: {
code: 'var __DEV__=true,__BUNDLE_START_TIME__=Date.now();',
path: '',
type: 'script',
},
});
done();
});
});
it('sets `__DEV__` to false in the prelude if optimization is enabled', done => {
buildSetup(noEntryPoints, {optimize: true}, (error, result) => {
const [prelude] = result.modules;
expect(prelude.file.code)
.toEqual('var __DEV__=false,__BUNDLE_START_TIME__=Date.now();');
done();
});
});
it('places the module system implementation directly after the prelude', done => {
buildSetup(noEntryPoints, noOptions, (error, result) => {
const [, moduleSystem] = result.modules;
expect(moduleSystem).toEqual({
dependencies: [],
file: {
code: '',
path: defaults.moduleSystem,
type: FILE_TYPE,
},
});
done();
});
});
it('places polyfills after the module system', done => {
buildSetup(noEntryPoints, noOptions, (error, result) => {
const polyfills =
Array.from(result.modules).slice(2, 2 + defaults.polyfills.length);
expect(polyfills).toEqual(defaults.polyfills.map(moduleFromPath));
done();
});
});
it('places all modules from `defaults.runBeforeMainModule` after the polyfills', done => {
buildSetup(noEntryPoints, noOptions, (error, result) => {
const additionalModules =
Array.from(result.modules).slice(-defaults.runBeforeMainModule.length);
expect(additionalModules)
.toEqual(defaults.runBeforeMainModule.map(moduleFromPath));
done();
});
});
it('places all entry points at the end', done => {
const entryPoints = ['a', 'b', 'c'];
buildSetup(entryPoints, noOptions, (error, result) => {
expect(Array.from(result.modules).slice(-3))
.toEqual(entryPoints.map(moduleFromPath));
done();
});
});
it('concatenates `runBeforeMainModule` and entry points as `entryModules`', done => {
const entryPoints = ['a', 'b', 'c'];
buildSetup(entryPoints, noOptions, (error, result) => {
expect(Array.from(result.entryModules)).toEqual(
defaults.runBeforeMainModule.concat(entryPoints).map(moduleFromPath));
done();
});
});
});
function moduleFromPath(path) {
return {
dependencies: [],
file: {
code: '',
path,
type: FILE_TYPE,
},
};
}
function graph(entryPoints, platform, options, callback) {
const modules = Array.from(entryPoints, moduleFromPath);
callback(null, {
entryModules: modules,
modules,
});
}

View File

@ -8,6 +8,7 @@
*/
'use strict';
const generate = require('babel-generator').default;
const stub = require('sinon/lib/sinon/stub');
exports.fn = () => {
@ -16,3 +17,7 @@ exports.fn = () => {
f.stub = s;
return f;
};
const generateOptions = {concise: true};
exports.codeFromAst = ast => generate(ast, generateOptions).code;
exports.comparableCode = code => code.trim().replace(/\s\s+/g, ' ');

View File

@ -0,0 +1,133 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
'use strict';
jest.disableAutomock();
const collectDependencies = require('../collect-dependencies');
const astFromCode = require('babylon').parse;
const {codeFromAst, comparableCode} = require('../../test-helpers');
const {any} = expect;
describe('dependency collection from ASTs:', () => {
it('collects dependency identifiers from the code', () => {
const ast = astFromCode(`
const a = require('b/lib/a');
exports.do = () => require("do");
if (!something) {
require("setup/something");
}
`);
expect(collectDependencies(ast).dependencies)
.toEqual(['b/lib/a', 'do', 'setup/something']);
});
it('supports template literals as arguments', () => {
const ast = astFromCode('require(`left-pad`)');
expect(collectDependencies(ast).dependencies)
.toEqual(['left-pad']);
});
it('ignores template literals with interpolations', () => {
const ast = astFromCode('require(`left${"-"}pad`)');
expect(collectDependencies(ast).dependencies)
.toEqual([]);
});
it('ignores tagged template literals', () => {
const ast = astFromCode('require(tag`left-pad`)');
expect(collectDependencies(ast).dependencies)
.toEqual([]);
});
it('exposes a string as `dependencyMapName`', () => {
const ast = astFromCode('require("arbitrary")');
expect(collectDependencies(ast).dependencyMapName)
.toEqual(any(String));
});
it('exposes a string as `dependencyMapName` even without collecting dependencies', () => {
const ast = astFromCode('');
expect(collectDependencies(ast).dependencyMapName)
.toEqual(any(String));
});
it('replaces all required module ID strings with array lookups and keeps the ID as second argument', () => {
const ast = astFromCode(`
const a = require('b/lib/a');
const b = require(123);
exports.do = () => require("do");
if (!something) {
require("setup/something");
}
`);
const {dependencyMapName} = collectDependencies(ast);
expect(codeFromAst(ast)).toEqual(comparableCode(`
const a = require(${dependencyMapName}[0], 'b/lib/a');
const b = require(123);
exports.do = () => require(${dependencyMapName}[1], "do");
if (!something) {
require(${dependencyMapName}[2], "setup/something");
}
`));
});
});
describe('Dependency collection from optimized ASTs:', () => {
const dependencyMapName = 'arbitrary';
const {forOptimization} = collectDependencies;
let ast, names;
beforeEach(() => {
ast = astFromCode(`
const a = require(${dependencyMapName}[0], 'b/lib/a');
const b = require(123);
exports.do = () => require(${dependencyMapName}[1], "do");
if (!something) {
require(${dependencyMapName}[2], "setup/something");
}
`);
names = ['b/lib/a', 'do', 'setup/something'];
});
it('passes the `dependencyMapName` through', () => {
const result = forOptimization(ast, names, dependencyMapName);
expect(result.dependencyMapName).toEqual(dependencyMapName);
});
it('returns the list of passed in dependencies', () => {
const result = forOptimization(ast, names, dependencyMapName);
expect(result.dependencies).toEqual(names);
});
it('only returns dependencies that are in the code', () => {
ast = astFromCode(`require(${dependencyMapName}[1], 'do')`);
const result = forOptimization(ast, names, dependencyMapName);
expect(result.dependencies).toEqual(['do']);
});
it('replaces all call signatures inserted by a prior call to `collectDependencies`', () => {
forOptimization(ast, names, dependencyMapName);
expect(codeFromAst(ast)).toEqual(comparableCode(`
const a = require(${dependencyMapName}[0]);
const b = require(123);
exports.do = () => require(${dependencyMapName}[1]);
if (!something) {
require(${dependencyMapName}[2]);
}
`));
});
});

View File

@ -28,12 +28,14 @@ class Replacement {
isRequireCall(callee, firstArg) {
return (
callee.type === 'Identifier' && callee.name === 'require' &&
firstArg && firstArg.type === 'StringLiteral'
firstArg && isLiteralString(firstArg)
);
}
getIndex(stringLiteral) {
const name = stringLiteral.value;
getIndex(stringLiteralOrTemplateLiteral) {
const name = stringLiteralOrTemplateLiteral.quasis
? stringLiteralOrTemplateLiteral.quasis[0].value.cooked
: stringLiteralOrTemplateLiteral.value;
let index = this.nameToIndex.get(name);
if (index !== undefined) {
return index;
@ -132,6 +134,11 @@ function collectDependencies(ast, replacement, dependencyMapIdentifier) {
};
}
function isLiteralString(node) {
return node.type === 'StringLiteral' ||
node.type === 'TemplateLiteral' && node.quasis.length === 1;
}
exports = module.exports =
(ast: AST) => collectDependencies(ast, new Replacement());
exports.forOptimization =