From 2ab7988cbec6610c7dc54e12f775945215dc8c05 Mon Sep 17 00:00:00 2001 From: David Aurelio Date: Mon, 23 Jan 2017 03:47:26 -0800 Subject: [PATCH] add tests Summary: Tests for things introduced over the last 10 weeks Reviewed By: cpojer Differential Revision: D4442404 fbshipit-source-id: cf7a14f237360fde083b7e2ffcd9d70aafd0ced6 --- .../src/ModuleGraph/ModuleGraph.js | 3 +- .../ModuleGraph/__tests__/ModuleGraph-test.js | 119 ++++++++++++++++ .../src/ModuleGraph/test-helpers.js | 5 + .../__tests__/collect-dependencies-test.js | 133 ++++++++++++++++++ .../worker/collect-dependencies.js | 13 +- 5 files changed, 268 insertions(+), 5 deletions(-) create mode 100644 packager/react-packager/src/ModuleGraph/__tests__/ModuleGraph-test.js create mode 100644 packager/react-packager/src/ModuleGraph/worker/__tests__/collect-dependencies-test.js diff --git a/packager/react-packager/src/ModuleGraph/ModuleGraph.js b/packager/react-packager/src/ModuleGraph/ModuleGraph.js index bd559940c..7c98b772d 100644 --- a/packager/react-packager/src/ModuleGraph/ModuleGraph.js +++ b/packager/react-packager/src/ModuleGraph/ModuleGraph.js @@ -96,7 +96,6 @@ function* concat(...iterables: Array>): Iterable { function prelude(optimize) { return virtualModule( - `var __DEV__= ${String(!optimize) - }, __BUNDLE_START_TIME__ = Date.now();` + `var __DEV__=${String(!optimize)},__BUNDLE_START_TIME__=Date.now();` ); } diff --git a/packager/react-packager/src/ModuleGraph/__tests__/ModuleGraph-test.js b/packager/react-packager/src/ModuleGraph/__tests__/ModuleGraph-test.js new file mode 100644 index 000000000..9b0f8e854 --- /dev/null +++ b/packager/react-packager/src/ModuleGraph/__tests__/ModuleGraph-test.js @@ -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, + }); +} diff --git a/packager/react-packager/src/ModuleGraph/test-helpers.js b/packager/react-packager/src/ModuleGraph/test-helpers.js index bfeed6788..35deb46ae 100644 --- a/packager/react-packager/src/ModuleGraph/test-helpers.js +++ b/packager/react-packager/src/ModuleGraph/test-helpers.js @@ -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, ' '); diff --git a/packager/react-packager/src/ModuleGraph/worker/__tests__/collect-dependencies-test.js b/packager/react-packager/src/ModuleGraph/worker/__tests__/collect-dependencies-test.js new file mode 100644 index 000000000..72d9a6f3b --- /dev/null +++ b/packager/react-packager/src/ModuleGraph/worker/__tests__/collect-dependencies-test.js @@ -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]); + } + `)); + }); +}); diff --git a/packager/react-packager/src/ModuleGraph/worker/collect-dependencies.js b/packager/react-packager/src/ModuleGraph/worker/collect-dependencies.js index 37e447259..b17c8c9d3 100644 --- a/packager/react-packager/src/ModuleGraph/worker/collect-dependencies.js +++ b/packager/react-packager/src/ModuleGraph/worker/collect-dependencies.js @@ -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 =