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,
|
||||
|};
|
||||
|
||||
export type TransformFnResult = {|
|
||||
export type TransformFnResult = {
|
||||
ast: Object,
|
||||
map?: Object,
|
||||
|};
|
||||
};
|
||||
|
||||
export type TransformFn = (
|
||||
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';
|
||||
|
||||
export type OptimizationOptions = {|
|
||||
dev?: boolean,
|
||||
dev: boolean,
|
||||
isPolyfill?: boolean,
|
||||
platform?: string,
|
||||
platform: string,
|
||||
|};
|
||||
|
||||
function optimizeModule(
|
||||
|
|
|
@ -88,7 +88,7 @@ function transformJSON(json, options, callback) {
|
|||
const code =
|
||||
`__d(function(${moduleFactoryParameters.join(', ')}) { module.exports = \n${
|
||||
json
|
||||
}\n})`;
|
||||
}\n});`;
|
||||
|
||||
const moduleData = {
|
||||
code,
|
||||
|
|
Loading…
Reference in New Issue