react-native-macos
Version:
A framework for building native macOS apps using React
451 lines (404 loc) • 12.4 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.
*/
'use strict';
jest.disableAutomock();
jest
.setMock('worker-farm', () => () => undefined)
.setMock('../../worker-farm', () => () => undefined)
.setMock('uglify-js')
.mock('image-size')
.mock('fs')
.mock('os')
.mock('assert')
.mock('progress')
.mock('../../node-haste')
.mock('../../JSTransformer')
.mock('../../lib/declareOpts')
.mock('../../Resolver')
.mock('../Bundle')
.mock('../HMRBundle')
.mock('../../Logger')
.mock('../../lib/declareOpts');
var Bundler = require('../');
var Resolver = require('../../Resolver');
var defaults = require('../../../defaults');
var sizeOf = require('image-size');
var fs = require('fs');
const os = require('os');
const {any, objectContaining} = expect;
var commonOptions = {
allowBundleUpdates: false,
assetExts: defaults.assetExts,
cacheVersion: 'smth',
extraNodeModules: {},
platforms: defaults.platforms,
resetCache: false,
sourceExts: defaults.sourceExts,
watch: false,
};
describe('Bundler', function() {
function createModule({
path,
id,
dependencies,
isAsset,
isJSON,
isPolyfill,
resolution,
}) {
return {
path,
resolution,
getDependencies: () => Promise.resolve(dependencies),
getName: () => Promise.resolve(id),
isJSON: () => isJSON,
isAsset: () => isAsset,
isPolyfill: () => isPolyfill,
read: () => ({
code: 'arbitrary',
source: 'arbitrary',
}),
};
}
var getDependencies;
var getModuleSystemDependencies;
var bundler;
var assetServer;
var modules;
var projectRoots;
beforeEach(function() {
os.cpus.mockReturnValue({length: 1});
getDependencies = jest.fn();
getModuleSystemDependencies = jest.fn();
projectRoots = ['/root'];
Resolver.mockImplementation(function() {
return {
getDependencies,
getModuleSystemDependencies,
};
});
Resolver.load = jest.fn().mockImplementation(opts => Promise.resolve(new Resolver(opts)));
fs.statSync.mockImplementation(function() {
return {
isDirectory: () => true,
};
});
fs.readFile.mockImplementation(function(file, callback) {
callback(null, '{"json":true}');
});
assetServer = {
getAssetData: jest.fn(),
};
bundler = new Bundler({
...commonOptions,
projectRoots,
assetServer,
});
modules = [
createModule({id: 'foo', path: '/root/foo.js', dependencies: []}),
createModule({id: 'bar', path: '/root/bar.js', dependencies: []}),
createModule({
id: 'new_image.png',
path: '/root/img/new_image.png',
isAsset: true,
resolution: 2,
dependencies: [],
}),
createModule({
id: 'package/file.json',
path: '/root/file.json',
isJSON: true,
dependencies: [],
}),
];
getDependencies.mockImplementation((main, options, transformOptions) =>
Promise.resolve({
mainModuleId: 'foo',
dependencies: modules,
options: transformOptions,
getModuleId: () => 123,
getResolvedDependencyPairs: () => [],
})
);
getModuleSystemDependencies.mockImplementation(function() {
return [];
});
sizeOf.mockImplementation(function(path, cb) {
cb(null, {width: 50, height: 100});
});
});
it('gets the list of dependencies from the resolver', function() {
const entryFile = '/root/foo.js';
return bundler.getDependencies({entryFile, recursive: true}).then(() =>
// jest calledWith does not support jasmine.any
expect(getDependencies.mock.calls[0].slice(0, -2)).toEqual([
'/root/foo.js',
{dev: true, platform: undefined, recursive: true},
{
preloadedModules: undefined,
ramGroups: undefined,
transformer: {
dev: true,
minify: false,
platform: undefined,
transform: {
dev: true,
generateSourceMaps: false,
hot: false,
inlineRequires: false,
platform: undefined,
projectRoot: projectRoots[0],
},
},
},
])
);
});
it('allows overriding the platforms array', () => {
expect(bundler._opts.platforms).toEqual(['ios', 'android', 'windows', 'web', 'macos']);
const b = new Bundler({
...commonOptions,
projectRoots,
assetServer,
platforms: ['android', 'vr'],
});
expect(b._opts.platforms).toEqual(['android', 'vr']);
});
describe('.bundle', () => {
const mockAsset = {
scales: [1, 2, 3],
files: [
'/root/img/img.png',
'/root/img/img@2x.png',
'/root/img/img@3x.png',
],
hash: 'i am a hash',
name: 'img',
type: 'png',
};
beforeEach(() => {
assetServer.getAssetData
.mockImplementation(() => Promise.resolve(mockAsset));
});
it('creates a bundle', function() {
return bundler.bundle({
entryFile: '/root/foo.js',
runBeforeMainModule: [],
runModule: true,
sourceMapUrl: 'source_map_url',
}).then(bundle => {
const ithAddedModule = i => bundle.addModule.mock.calls[i][2].path;
expect(ithAddedModule(0)).toEqual('/root/foo.js');
expect(ithAddedModule(1)).toEqual('/root/bar.js');
expect(ithAddedModule(2)).toEqual('/root/img/new_image.png');
expect(ithAddedModule(3)).toEqual('/root/file.json');
expect(bundle.finalize.mock.calls[0]).toEqual([{
runModule: true,
runBeforeMainModule: [],
allowUpdates: false,
}]);
expect(bundle.addAsset.mock.calls[0]).toEqual([{
__packager_asset: true,
fileSystemLocation: '/root/img',
httpServerLocation: '/assets/img',
width: 50,
height: 100,
scales: [1, 2, 3],
files: [
'/root/img/img.png',
'/root/img/img@2x.png',
'/root/img/img@3x.png',
],
hash: 'i am a hash',
name: 'img',
type: 'png',
}]);
// TODO(amasad) This fails with 0 != 5 in OSS
//expect(ProgressBar.prototype.tick.mock.calls.length).toEqual(modules.length);
});
});
it('loads and runs asset plugins', function() {
jest.mock('mockPlugin1', () => {
return asset => {
asset.extraReverseHash = asset.hash.split('').reverse().join('');
return asset;
};
}, {virtual: true});
jest.mock('asyncMockPlugin2', () => {
return asset => {
expect(asset.extraReverseHash).toBeDefined();
return new Promise(resolve => {
asset.extraPixelCount = asset.width * asset.height;
resolve(asset);
});
};
}, {virtual: true});
return bundler.bundle({
entryFile: '/root/foo.js',
runBeforeMainModule: [],
runModule: true,
sourceMapUrl: 'source_map_url',
assetPlugins: ['mockPlugin1', 'asyncMockPlugin2'],
}).then(bundle => {
expect(bundle.addAsset.mock.calls[0]).toEqual([{
__packager_asset: true,
fileSystemLocation: '/root/img',
httpServerLocation: '/assets/img',
width: 50,
height: 100,
scales: [1, 2, 3],
files: [
'/root/img/img.png',
'/root/img/img@2x.png',
'/root/img/img@3x.png',
],
hash: 'i am a hash',
name: 'img',
type: 'png',
extraReverseHash: 'hsah a ma i',
extraPixelCount: 5000,
}]);
});
});
it('calls the module post-processing function', () => {
const postProcessModules = jest.fn().mockImplementation((ms, e) => ms);
const b = new Bundler({
...commonOptions,
postProcessModules,
projectRoots,
assetServer,
});
const dev = false;
const minify = true;
const platform = 'arbitrary';
const entryFile = '/root/foo.js';
return b.bundle({
dev,
entryFile,
minify,
platform,
runBeforeMainModule: [],
runModule: true,
sourceMapUrl: 'source_map_url',
}).then(() => {
expect(postProcessModules)
.toBeCalledWith(
modules.map(x => objectContaining({
name: any(String),
id: any(Number),
code: any(String),
sourceCode: any(String),
sourcePath: x.path,
meta: any(Object),
polyfill: !!x.isPolyfill(),
})),
entryFile,
{dev, minify, platform},
);
});
});
it('respects the order of modules returned by the post-processing function', () => {
const postProcessModules = jest.fn().mockImplementation((ms, e) => ms.reverse());
const b = new Bundler({
...commonOptions,
postProcessModules,
projectRoots,
assetServer,
});
const entryFile = '/root/foo.js';
return b.bundle({
entryFile,
runBeforeMainModule: [],
runModule: true,
sourceMapUrl: 'source_map_url',
}).then(bundle => {
const ithAddedModule = i => bundle.addModule.mock.calls[i][2].path;
[
'/root/file.json',
'/root/img/new_image.png',
'/root/bar.js',
'/root/foo.js',
].forEach((path, ix) => expect(ithAddedModule(ix)).toEqual(path));
});
});
});
describe('.getOrderedDependencyPaths', () => {
beforeEach(() => {
assetServer.getAssetData.mockImplementation(function(relPath) {
if (relPath === 'img/new_image.png') {
return Promise.resolve({
scales: [1, 2, 3],
files: [
'/root/img/new_image.png',
'/root/img/new_image@2x.png',
'/root/img/new_image@3x.png',
],
hash: 'i am a hash',
name: 'img',
type: 'png',
});
} else if (relPath === 'img/new_image2.png') {
return Promise.resolve({
scales: [1, 2, 3],
files: [
'/root/img/new_image2.png',
'/root/img/new_image2@2x.png',
'/root/img/new_image2@3x.png',
],
hash: 'i am a hash',
name: 'img',
type: 'png',
});
}
throw new Error('unknown image ' + relPath);
});
});
it('should get the concrete list of all dependency files', () => {
modules.push(
createModule({
id: 'new_image2.png',
path: '/root/img/new_image2.png',
isAsset: true,
resolution: 2,
dependencies: [],
}),
);
return bundler.getOrderedDependencyPaths('/root/foo.js', true)
.then(paths => expect(paths).toEqual([
'/root/foo.js',
'/root/bar.js',
'/root/img/new_image.png',
'/root/img/new_image@2x.png',
'/root/img/new_image@3x.png',
'/root/file.json',
'/root/img/new_image2.png',
'/root/img/new_image2@2x.png',
'/root/img/new_image2@3x.png',
]));
});
describe('number of workers', () => {
beforeEach(() => {
delete process.env.REACT_NATIVE_MAX_WORKERS;
});
afterEach(() => {
delete process.env.REACT_NATIVE_MAX_WORKERS;
});
it('return correct number of workers', () => {
os.cpus.mockReturnValue({length: 1});
expect(Bundler.getMaxWorkerCount()).toBe(1);
os.cpus.mockReturnValue({length: 8});
expect(Bundler.getMaxWorkerCount()).toBe(6);
os.cpus.mockReturnValue({length: 24});
expect(Bundler.getMaxWorkerCount()).toBe(14);
process.env.REACT_NATIVE_MAX_WORKERS = 5;
expect(Bundler.getMaxWorkerCount()).toBe(5);
});
});
});
});