mirror of https://github.com/status-im/metro.git
Add tests for worker code
Summary: This adds unit tests for the new buck worker code, including a test for source map generation. Reviewed By: cpojer Differential Revision: D4193657 fbshipit-source-id: 06f7bfb5efa4f411178543a728ac7e42511caa3c
This commit is contained in:
parent
7181a2d436
commit
1c16124e1c
|
@ -93,10 +93,9 @@ export type PackageData = {|
|
||||||
'react-native'?: Object | string,
|
'react-native'?: Object | string,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
export type TransformFnResult = {|
|
export type TransformFnResult = {
|
||||||
ast: Object,
|
ast: Object,
|
||||||
map?: Object,
|
};
|
||||||
|};
|
|
||||||
|
|
||||||
export type TransformFn = (
|
export type TransformFn = (
|
||||||
data: {|
|
data: {|
|
||||||
|
|
|
@ -0,0 +1,104 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2016-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 optimizeModule = require('../optimize-module');
|
||||||
|
const transformModule = require('../transform-module');
|
||||||
|
const transform = require('../../../../../transformer.js');
|
||||||
|
const {SourceMapConsumer} = require('source-map');
|
||||||
|
|
||||||
|
const {objectContaining} = jasmine;
|
||||||
|
|
||||||
|
describe('optimizing JS modules', () => {
|
||||||
|
const filename = 'arbitrary/file.js';
|
||||||
|
const optimizationOptions = {
|
||||||
|
dev: false,
|
||||||
|
platform: 'android',
|
||||||
|
};
|
||||||
|
const originalCode =
|
||||||
|
`if (Platform.OS !== 'android') {
|
||||||
|
require('arbitrary-dev');
|
||||||
|
} else {
|
||||||
|
__DEV__ ? require('arbitrary-android-dev') : require('arbitrary-android-prod');
|
||||||
|
}`;
|
||||||
|
|
||||||
|
let transformResult;
|
||||||
|
beforeAll(done => {
|
||||||
|
transformModule(originalCode, {filename, transform}, (error, result) => {
|
||||||
|
if (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
transformResult = JSON.stringify(result);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('copies everything from the transformed file, except for transform results', done => {
|
||||||
|
optimizeModule(transformResult, optimizationOptions, (error, result) => {
|
||||||
|
const expected = JSON.parse(transformResult);
|
||||||
|
delete expected.transformed;
|
||||||
|
expect(result).toEqual(objectContaining(expected));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('code optimization', () => {
|
||||||
|
let dependencyMapName, injectedVars, optimized, requireName;
|
||||||
|
beforeAll(done => {
|
||||||
|
optimizeModule(transformResult, optimizationOptions, (error, result) => {
|
||||||
|
optimized = result.transformed.default;
|
||||||
|
injectedVars = optimized.code.match(/function\(([^)]*)/)[1].split(',');
|
||||||
|
[requireName,,,, dependencyMapName] = injectedVars;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('optimizes code', () => {
|
||||||
|
expect(optimized.code)
|
||||||
|
.toEqual(`__d(function(${injectedVars}){${requireName}(${dependencyMapName}[0])});`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('extracts dependencies', () => {
|
||||||
|
expect(optimized.dependencies).toEqual(['arbitrary-android-prod']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates source maps', () => {
|
||||||
|
const consumer = new SourceMapConsumer(optimized.map);
|
||||||
|
const column = optimized.code.lastIndexOf(requireName + '(');
|
||||||
|
const loc = findLast(originalCode, 'require');
|
||||||
|
|
||||||
|
expect(consumer.originalPositionFor({line: 1, column}))
|
||||||
|
.toEqual(objectContaining(loc));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not extract dependencies for polyfills', done => {
|
||||||
|
optimizeModule(
|
||||||
|
transformResult,
|
||||||
|
{...optimizationOptions, isPolyfill: true},
|
||||||
|
(error, result) => {
|
||||||
|
expect(result.transformed.default.dependencies).toEqual([]);
|
||||||
|
done();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function findLast(code, needle) {
|
||||||
|
const lines = code.split(/(?:(?!.)\s)+/);
|
||||||
|
let line = lines.length;
|
||||||
|
while (line--) {
|
||||||
|
const column = lines[line].lastIndexOf(needle);
|
||||||
|
if (column !== -1) {
|
||||||
|
return {line: line + 1, column};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,232 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2016-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 transformModule = require('../transform-module');
|
||||||
|
|
||||||
|
const t = require('babel-types');
|
||||||
|
const {SourceMapConsumer} = require('source-map');
|
||||||
|
const {fn} = require('../../test-helpers');
|
||||||
|
const {parse} = require('babylon');
|
||||||
|
const generate = require('babel-generator').default;
|
||||||
|
const {traverse} = require('babel-core');
|
||||||
|
|
||||||
|
const {any, objectContaining} = jasmine;
|
||||||
|
|
||||||
|
describe('transforming JS modules:', () => {
|
||||||
|
const filename = 'arbitrary';
|
||||||
|
|
||||||
|
let transform;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
transform = fn();
|
||||||
|
transform.stub.yields(null, transformResult());
|
||||||
|
});
|
||||||
|
|
||||||
|
const {bodyAst, sourceCode, transformedCode} = createTestData();
|
||||||
|
|
||||||
|
const options = variants => ({
|
||||||
|
filename,
|
||||||
|
transform,
|
||||||
|
variants,
|
||||||
|
});
|
||||||
|
|
||||||
|
const transformResult = (body = bodyAst) => ({
|
||||||
|
ast: t.file(t.program(body)),
|
||||||
|
});
|
||||||
|
|
||||||
|
it('passes through file name and code', done => {
|
||||||
|
transformModule(sourceCode, options(), (error, result) => {
|
||||||
|
expect(result).toEqual(objectContaining({
|
||||||
|
code: sourceCode,
|
||||||
|
file: filename,
|
||||||
|
}));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('exposes a haste ID if present', done => {
|
||||||
|
const hasteID = 'TheModule';
|
||||||
|
const codeWithHasteID = `/** @providesModule ${hasteID} */`;
|
||||||
|
transformModule(codeWithHasteID, options(), (error, result) => {
|
||||||
|
expect(result).toEqual(objectContaining({hasteID}));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets `isPolyfill` to `false` by default', done => {
|
||||||
|
transformModule(sourceCode, options(), (error, result) => {
|
||||||
|
expect(result).toEqual(objectContaining({isPolyfill: false}));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sets `isPolyfill` to `true` if the input is a polyfill', done => {
|
||||||
|
transformModule(sourceCode, {...options(), polyfill: true}, (error, result) => {
|
||||||
|
expect(result).toEqual(objectContaining({isPolyfill: true}));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls the passed-in transform function with code, file name, and options for all passed in variants', done => {
|
||||||
|
const variants = {dev: {dev: true}, prod: {dev: false}};
|
||||||
|
|
||||||
|
transformModule(sourceCode, options(variants), () => {
|
||||||
|
expect(transform)
|
||||||
|
.toBeCalledWith({filename, sourceCode, options: variants.dev}, any(Function));
|
||||||
|
expect(transform)
|
||||||
|
.toBeCalledWith({filename, sourceCode, options: variants.prod}, any(Function));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('calls back with any error yielded by the transform function', done => {
|
||||||
|
const error = new Error();
|
||||||
|
transform.stub.yields(error);
|
||||||
|
|
||||||
|
transformModule(sourceCode, options(), e => {
|
||||||
|
expect(e).toBe(error);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('wraps the code produced by the transform function into a module factory', done => {
|
||||||
|
transformModule(sourceCode, options(), (error, result) => {
|
||||||
|
expect(error).toEqual(null);
|
||||||
|
|
||||||
|
const {code, dependencyMapName} = result.transformed.default;
|
||||||
|
expect(code.replace(/\s+/g, ''))
|
||||||
|
.toEqual(
|
||||||
|
`__d(function(require,module,global,exports,${
|
||||||
|
dependencyMapName}){${transformedCode}});`
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('wraps the code produced by the transform function into an immediately invoked function expression for polyfills', done => {
|
||||||
|
transformModule(sourceCode, {...options(), polyfill: true}, (error, result) => {
|
||||||
|
expect(error).toEqual(null);
|
||||||
|
|
||||||
|
const {code} = result.transformed.default;
|
||||||
|
expect(code.replace(/\s+/g, ''))
|
||||||
|
.toEqual(`(function(global){${transformedCode}})(this);`);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates source maps', done => {
|
||||||
|
transformModule(sourceCode, options(), (error, result) => {
|
||||||
|
const {code, map} = result.transformed.default;
|
||||||
|
const column = code.indexOf('code');
|
||||||
|
const consumer = new SourceMapConsumer(map);
|
||||||
|
expect(consumer.originalPositionFor({line: 1, column}))
|
||||||
|
.toEqual(objectContaining({line: 1, column: sourceCode.indexOf('code')}));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('extracts dependencies (require calls)', done => {
|
||||||
|
const dep1 = 'foo', dep2 = 'bar';
|
||||||
|
const code = `require('${dep1}'),require('${dep2}')`;
|
||||||
|
const {body} = parse(code).program;
|
||||||
|
transform.stub.yields(null, transformResult(body));
|
||||||
|
|
||||||
|
transformModule(code, options(), (error, result) => {
|
||||||
|
expect(result.transformed.default)
|
||||||
|
.toEqual(objectContaining({dependencies: [dep1, dep2]}));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('transforms for all variants', done => {
|
||||||
|
const variants = {dev: {dev: true}, prod: {dev: false}};
|
||||||
|
transform.stub
|
||||||
|
.withArgs(filename, sourceCode, variants.dev)
|
||||||
|
.yields(null, transformResult(bodyAst))
|
||||||
|
.withArgs(filename, sourceCode, variants.prod)
|
||||||
|
.yields(null, transformResult([]));
|
||||||
|
|
||||||
|
transformModule(sourceCode, options(variants), (error, result) => {
|
||||||
|
const {dev, prod} = result.transformed;
|
||||||
|
expect(dev.code.replace(/\s+/g, ''))
|
||||||
|
.toEqual(
|
||||||
|
`__d(function(require,module,global,exports,${
|
||||||
|
dev.dependencyMapName}){arbitrary(code);});`
|
||||||
|
);
|
||||||
|
expect(prod.code.replace(/\s+/g, ''))
|
||||||
|
.toEqual(
|
||||||
|
`__d(function(require,module,global,exports,${
|
||||||
|
prod.dependencyMapName}){arbitrary(code);});`
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prefixes JSON files with `module.exports = `', done => {
|
||||||
|
const json = '{"foo":"bar"}';
|
||||||
|
|
||||||
|
transformModule(json, {...options(), filename: 'some.json'}, (error, result) => {
|
||||||
|
const {code} = result.transformed.default;
|
||||||
|
expect(code.replace(/\s+/g, ''))
|
||||||
|
.toEqual(
|
||||||
|
'__d(function(require,module,global,exports){' +
|
||||||
|
`module.exports=${json}});`
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not create source maps for JSON files', done => {
|
||||||
|
transformModule('{}', {...options(), filename: 'some.json'}, (error, result) => {
|
||||||
|
expect(result.transformed.default)
|
||||||
|
.toEqual(objectContaining({map: null}));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds package data for `package.json` files', done => {
|
||||||
|
const pkg = {
|
||||||
|
name: 'package-name',
|
||||||
|
main: 'package/main',
|
||||||
|
browser: {browser: 'defs'},
|
||||||
|
'react-native': {'react-native': 'defs'},
|
||||||
|
};
|
||||||
|
|
||||||
|
transformModule(
|
||||||
|
JSON.stringify(pkg),
|
||||||
|
{...options(), filename: 'arbitrary/package.json'},
|
||||||
|
(error, result) => {
|
||||||
|
expect(result.package).toEqual(pkg);
|
||||||
|
done();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function createTestData() {
|
||||||
|
// creates test data with an transformed AST, so that we can test source
|
||||||
|
// map generation.
|
||||||
|
const sourceCode = 'some(arbitrary(code));';
|
||||||
|
const fileAst = parse(sourceCode);
|
||||||
|
traverse(fileAst, {
|
||||||
|
CallExpression(path) {
|
||||||
|
if (path.node.callee.name === 'some') {
|
||||||
|
path.replaceWith(path.node.arguments[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
bodyAst: fileAst.program.body,
|
||||||
|
sourceCode,
|
||||||
|
transformedCode: generate(fileAst).code,
|
||||||
|
};
|
||||||
|
}
|
|
@ -21,9 +21,9 @@ const sourceMap = require('source-map');
|
||||||
import type {Callback, TransformedFile, TransformResult} from '../types.flow';
|
import type {Callback, TransformedFile, TransformResult} from '../types.flow';
|
||||||
|
|
||||||
export type OptimizationOptions = {|
|
export type OptimizationOptions = {|
|
||||||
dev?: boolean,
|
dev: boolean,
|
||||||
isPolyfill?: boolean,
|
isPolyfill?: boolean,
|
||||||
platform?: string,
|
platform: string,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
function optimizeModule(
|
function optimizeModule(
|
||||||
|
|
|
@ -88,7 +88,7 @@ function transformJSON(json, options, callback) {
|
||||||
const code =
|
const code =
|
||||||
`__d(function(${moduleFactoryParameters.join(', ')}) { module.exports = \n${
|
`__d(function(${moduleFactoryParameters.join(', ')}) { module.exports = \n${
|
||||||
json
|
json
|
||||||
}\n})`;
|
}\n});`;
|
||||||
|
|
||||||
const moduleData = {
|
const moduleData = {
|
||||||
code,
|
code,
|
||||||
|
|
Loading…
Reference in New Issue