react-native-desktop
Version:
A framework for building native desktop apps using React
516 lines (467 loc) • 16.5 kB
JavaScript
/**
* Copyright (c) 2015-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.
*/
;
jest.unmock('../');
jest.mock('path');
const Promise = require('promise');
const Resolver = require('../');
const path = require('path');
let DependencyGraph = jest.fn();
jest.setMock('node-haste', DependencyGraph);
let Module;
let Polyfill;
describe('Resolver', function() {
beforeEach(function() {
DependencyGraph.mockClear();
Module = jest.fn(function() {
this.getName = jest.fn();
this.getDependencies = jest.fn();
this.isPolyfill = jest.fn().mockReturnValue(false);
this.isJSON = jest.fn().mockReturnValue(false);
});
Polyfill = jest.fn(function() {
var polyfill = new Module();
polyfill.isPolyfill.mockReturnValue(true);
return polyfill;
});
DependencyGraph.replacePatterns = require.requireActual('node-haste/lib/lib/replacePatterns');
DependencyGraph.prototype.createPolyfill = jest.fn();
DependencyGraph.prototype.getDependencies = jest.fn();
// For the polyfillDeps
path.join = jest.fn((a, b) => b);
DependencyGraph.prototype.load = jest.fn(() => Promise.resolve());
});
class ResolutionResponseMock {
constructor({dependencies, mainModuleId}) {
this.dependencies = dependencies;
this.mainModuleId = mainModuleId;
this.getModuleId = createGetModuleId();
}
prependDependency(dependency) {
this.dependencies.unshift(dependency);
}
finalize() {
return Promise.resolve(this);
}
getResolvedDependencyPairs() {
return [];
}
}
function createModule(id, dependencies) {
var module = new Module({});
module.path = id;
module.getName.mockImpl(() => Promise.resolve(id));
module.getDependencies.mockImpl(() => Promise.resolve(dependencies));
return module;
}
function createJsonModule(id) {
const module = createModule(id, []);
module.isJSON.mockReturnValue(true);
return module;
}
function createPolyfill(id, dependencies) {
var polyfill = new Polyfill({});
polyfill.getName = jest.fn(() => Promise.resolve(id));
polyfill.getDependencies =
jest.fn(() => Promise.resolve(dependencies));
return polyfill;
}
describe('getDependencies', function() {
it('forwards transform options to the dependency graph', function() {
const transformOptions = {arbitrary: 'options'};
const platform = 'ios';
const entry = '/root/index.js';
DependencyGraph.prototype.getDependencies.mockImplementation(
() => Promise.reject());
new Resolver({projectRoot: '/root'})
.getDependencies(entry, {platform}, transformOptions);
expect(DependencyGraph.prototype.getDependencies).toBeCalledWith({
entryPath: entry,
platform: platform,
transformOptions: transformOptions,
recursive: true,
});
});
pit('should get dependencies with polyfills', function() {
var module = createModule('index');
var deps = [module];
var depResolver = new Resolver({
projectRoot: '/root',
});
DependencyGraph.prototype.getDependencies.mockImpl(function() {
return Promise.resolve(new ResolutionResponseMock({
dependencies: deps,
mainModuleId: 'index',
}));
});
return depResolver
.getDependencies(
'/root/index.js',
{ dev: false },
undefined,
undefined,
createGetModuleId()
).then(function(result) {
expect(result.mainModuleId).toEqual('index');
expect(result.dependencies[result.dependencies.length - 1]).toBe(module);
expect(DependencyGraph.prototype.createPolyfill.mock.calls.map((call) => call[0])).toEqual([
{ id: 'polyfills/polyfills.js',
file: 'polyfills/polyfills.js',
dependencies: []
},
{ id: 'polyfills/console.js',
file: 'polyfills/console.js',
dependencies: [
'polyfills/polyfills.js'
],
},
{ id: 'polyfills/error-guard.js',
file: 'polyfills/error-guard.js',
dependencies: [
'polyfills/polyfills.js',
'polyfills/console.js'
],
},
{ id: 'polyfills/Number.es6.js',
file: 'polyfills/Number.es6.js',
dependencies: [
'polyfills/polyfills.js',
'polyfills/console.js',
'polyfills/error-guard.js'
],
},
{ id: 'polyfills/String.prototype.es6.js',
file: 'polyfills/String.prototype.es6.js',
dependencies: [
'polyfills/polyfills.js',
'polyfills/console.js',
'polyfills/error-guard.js',
'polyfills/Number.es6.js',
],
},
{ id: 'polyfills/Array.prototype.es6.js',
file: 'polyfills/Array.prototype.es6.js',
dependencies: [
'polyfills/polyfills.js',
'polyfills/console.js',
'polyfills/error-guard.js',
'polyfills/Number.es6.js',
'polyfills/String.prototype.es6.js',
],
},
{ id: 'polyfills/Array.es6.js',
file: 'polyfills/Array.es6.js',
dependencies: [
'polyfills/polyfills.js',
'polyfills/console.js',
'polyfills/error-guard.js',
'polyfills/Number.es6.js',
'polyfills/String.prototype.es6.js',
'polyfills/Array.prototype.es6.js',
],
},
{ id: 'polyfills/Object.es7.js',
file: 'polyfills/Object.es7.js',
dependencies: [
'polyfills/polyfills.js',
'polyfills/console.js',
'polyfills/error-guard.js',
'polyfills/Number.es6.js',
'polyfills/String.prototype.es6.js',
'polyfills/Array.prototype.es6.js',
'polyfills/Array.es6.js',
],
},
{ id: 'polyfills/babelHelpers.js',
file: 'polyfills/babelHelpers.js',
dependencies: [
'polyfills/polyfills.js',
'polyfills/console.js',
'polyfills/error-guard.js',
'polyfills/Number.es6.js',
'polyfills/String.prototype.es6.js',
'polyfills/Array.prototype.es6.js',
'polyfills/Array.es6.js',
'polyfills/Object.es7.js',
],
},
]);
});
});
pit('should get dependencies with polyfills', function() {
var module = createModule('index');
var deps = [module];
var depResolver = new Resolver({
projectRoot: '/root',
});
DependencyGraph.prototype.getDependencies.mockImpl(function() {
return Promise.resolve(new ResolutionResponseMock({
dependencies: deps,
mainModuleId: 'index',
}));
});
const polyfill = {};
DependencyGraph.prototype.createPolyfill.mockReturnValueOnce(polyfill);
return depResolver
.getDependencies(
'/root/index.js',
{ dev: true },
undefined,
undefined,
createGetModuleId()
).then(function(result) {
expect(result.mainModuleId).toEqual('index');
expect(DependencyGraph.mock.instances[0].getDependencies)
.toBeCalledWith({entryPath: '/root/index.js', recursive: true});
expect(result.dependencies[0]).toBe(polyfill);
expect(result.dependencies[result.dependencies.length - 1])
.toBe(module);
});
});
pit('should pass in more polyfills', function() {
var module = createModule('index');
var deps = [module];
var depResolver = new Resolver({
projectRoot: '/root',
polyfillModuleNames: ['some module'],
});
DependencyGraph.prototype.getDependencies.mockImpl(function() {
return Promise.resolve(new ResolutionResponseMock({
dependencies: deps,
mainModuleId: 'index',
}));
});
return depResolver
.getDependencies(
'/root/index.js',
{ dev: false },
undefined,
undefined,
createGetModuleId()
).then((result) => {
expect(result.mainModuleId).toEqual('index');
expect(DependencyGraph.prototype.createPolyfill.mock.calls[result.dependencies.length - 2]).toEqual([
{ file: 'some module',
id: 'some module',
dependencies: [
'polyfills/polyfills.js',
'polyfills/console.js',
'polyfills/error-guard.js',
'polyfills/Number.es6.js',
'polyfills/String.prototype.es6.js',
'polyfills/Array.prototype.es6.js',
'polyfills/Array.es6.js',
'polyfills/Object.es7.js',
'polyfills/babelHelpers.js',
]
},
]);
});
});
});
describe('wrapModule', function() {
let depResolver;
beforeEach(() => {
depResolver = new Resolver({
depResolver,
projectRoot: '/root',
});
});
pit('should resolve modules', function() {
/*eslint-disable */
var code = [
// require
'require("x")',
'require("y")',
'require( \'z\' )',
'require( "a")',
'require("b" )',
].join('\n');
/*eslint-disable */
function *findDependencyOffsets() {
const re = /(['"']).*?\1/g;
let match;
while ((match = re.exec(code))) {
yield match.index;
}
}
const dependencyOffsets = Array.from(findDependencyOffsets());
const module = createModule('test module', ['x', 'y']);
const resolutionResponse = new ResolutionResponseMock({
dependencies: [module],
mainModuleId: 'test module',
});
resolutionResponse.getResolvedDependencyPairs = (module) => {
return [
['x', createModule('changed')],
['y', createModule('Y')],
];
}
const moduleIds = new Map(
resolutionResponse
.getResolvedDependencyPairs()
.map(([importId, module]) =>
[importId, resolutionResponse.getModuleId(module)])
);
return depResolver.wrapModule({
resolutionResponse,
module: module,
name: 'test module',
code,
meta: {dependencyOffsets},
dev: false,
}).then(({code: processedCode}) => {
expect(processedCode).toEqual([
`__d(${resolutionResponse.getModuleId(module)} /* test module */, function(global, require, module, exports) {` +
// require
`require(${moduleIds.get('x')} /* x */)`,
`require(${moduleIds.get('y')} /* y */)`,
'require( \'z\' )',
'require( "a")',
'require("b" )',
'});',
].join('\n'));
});
});
pit('should add module transport names as third argument to `__d`', () => {
const module = createModule('test module');
const code = 'arbitrary(code)'
const resolutionResponse = new ResolutionResponseMock({
dependencies: [module],
mainModuleId: 'test module',
});
return depResolver.wrapModule({
resolutionResponse,
code,
module,
name: 'test module',
dev: true,
}).then(({code: processedCode}) =>
expect(processedCode).toEqual([
`__d(${resolutionResponse.getModuleId(module)} /* test module */, function(global, require, module, exports) {` +
code,
'}, "test module");'
].join('\n'))
);
});
pit('should pass through passed-in source maps', () => {
const module = createModule('test module');
const resolutionResponse = new ResolutionResponseMock({
dependencies: [module],
mainModuleId: 'test module',
});
const inputMap = {version: 3, mappings: 'ARBITRARY'};
return depResolver.wrapModule({
resolutionResponse,
module,
name: 'test module',
code: 'arbitrary(code)',
map: inputMap,
}).then(({map}) => expect(map).toBe(inputMap));
});
pit('should resolve polyfills', function () {
const depResolver = new Resolver({
projectRoot: '/root',
});
const polyfill = createPolyfill('test polyfill', []);
const code = [
'global.fetch = () => 1;',
].join('');
return depResolver.wrapModule({
module: polyfill,
code
}).then(({code: processedCode}) => {
expect(processedCode).toEqual([
'(function(global) {',
'global.fetch = () => 1;',
"\n})(typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : this);",
].join(''));
});
});
describe('JSON files:', () => {
const code = JSON.stringify({arbitrary: "data"});
const id = 'arbitrary.json';
let depResolver, module, resolutionResponse;
beforeEach(() => {
depResolver = new Resolver({projectRoot: '/root'});
module = createJsonModule(id);
resolutionResponse = new ResolutionResponseMock({
dependencies: [module],
mainModuleId: id,
});
});
pit('should prefix JSON files with `module.exports=`', () => {
return depResolver
.wrapModule({resolutionResponse, module, name: id, code, dev: false})
.then(({code: processedCode}) =>
expect(processedCode).toEqual([
`__d(${resolutionResponse.getModuleId(module)} /* ${id} */, function(global, require, module, exports) {`,
`module.exports = ${code}\n});`,
].join('')));
});
});
describe('minification:', () => {
const code ='arbitrary(code)';
const id = 'arbitrary.js';
let depResolver, minifyCode, module, resolutionResponse, sourceMap;
beforeEach(() => {
minifyCode = jest.fn((filename, code, map) =>
Promise.resolve({code, map}));
depResolver = new Resolver({
projectRoot: '/root',
minifyCode,
});
module = createModule(id);
module.path = '/arbitrary/path.js';
resolutionResponse = new ResolutionResponseMock({
dependencies: [module],
mainModuleId: id,
});
sourceMap = {version: 3, sources: ['input'], mappings: 'whatever'};
});
pit('should invoke the minifier with the wrapped code', () => {
const wrappedCode = `__d(${resolutionResponse.getModuleId(module)} /* ${id} */, function(global, require, module, exports) {${code}\n});`
return depResolver
.wrapModule({
resolutionResponse,
module,
name: id,
code,
map: sourceMap,
minify: true,
dev: false,
}).then(() => {
expect(minifyCode).toBeCalledWith(module.path, wrappedCode, sourceMap);
});
});
pit('should use minified code', () => {
const minifiedCode = 'minified(code)';
const minifiedMap = {version: 3, file: ['minified']};
minifyCode.mockReturnValue(Promise.resolve({code: minifiedCode, map: minifiedMap}));
return depResolver
.wrapModule({resolutionResponse, module, name: id, code, minify: true})
.then(({code, map}) => {
expect(code).toEqual(minifiedCode);
expect(map).toEqual(minifiedMap);
});
});
});
});
function createGetModuleId() {
let nextId = 1;
const knownIds = new Map();
function createId(path) {
const id = nextId;
nextId += 1;
knownIds.set(path, id);
return id;
}
return ({path}) => knownIds.get(path) || createId(path);
}
});