Files
NativeScript/packages/webpack/templates/webpack.config.spec.ts
2020-10-17 16:15:20 -07:00

307 lines
11 KiB
TypeScript

import * as proxyquire from 'proxyquire';
import * as nsWebpackIndex from '../index';
import { join } from 'path';
// With noCallThru enabled, `proxyquire` will not fall back to requiring the real module to populate properties that are not mocked.
// This allows us to mock packages that are not available in node_modules.
// In case you want to enable fallback for a specific object, just add `'@noCallThru': false`.
proxyquire.noCallThru();
class EmptyClass {}
let angularCompilerOptions: any;
class AngularCompilerStub {
constructor(options) {
angularCompilerOptions = options;
}
}
let terserOptions: any;
class TerserJsStub {
constructor(options) {
terserOptions = options;
}
}
const nativeScriptDevWebpack = {
GenerateBundleStarterPlugin: EmptyClass,
GenerateNativeScriptEntryPointsPlugin: EmptyClass,
WatchStateLoggerPlugin: EmptyClass,
PlatformFSPlugin: EmptyClass,
getAppPath: () => 'app',
getEntryModule: () => 'EntryModule',
hasRootLevelScopedModules: () => false,
hasRootLevelScopedAngular: () => false,
processTsPathsForScopedModules: () => false,
processTsPathsForScopedAngular: () => false,
getResolver: () => null,
getConvertedExternals: nsWebpackIndex.getConvertedExternals,
getSourceMapFilename: nsWebpackIndex.getSourceMapFilename,
processAppComponents: nsWebpackIndex.processAppComponents,
getUserDefinedEntries: nsWebpackIndex.getUserDefinedEntries,
};
const emptyObject = {};
const FakeHmrTransformerFlag = 'hmr';
const FakeNativeClassTransformerFlag = 'NativeClass';
const FakeLazyTransformerFlag = 'lazy';
const webpackConfigAngular = proxyquire('./webpack.angular', {
'@nativescript/webpack': nativeScriptDevWebpack,
'@nativescript/webpack/nativescript-target': emptyObject,
'@nativescript/webpack/transformers/ns-support-hmr-ng': {
nsSupportHmrNg: () => {
return FakeHmrTransformerFlag;
},
},
'@nativescript/webpack/transformers/ns-transform-native-classes-ng': {
nsTransformNativeClassesNg: () => {
return FakeNativeClassTransformerFlag;
},
},
'@nativescript/webpack/helpers/angular-config-parser': {
parseWorkspaceConfig: (platform, envConfigs, rootPath = '') => {
return {
fileReplacements: {},
copyReplacements: [],
};
},
hasConfigurations: (envConfigs) => {
return false;
},
},
'@nativescript/webpack/utils/ast-utils': {
getMainModulePath: () => {
return 'fakePath';
},
},
'@nativescript/webpack/utils/tsconfig-utils': {
getNoEmitOnErrorFromTSConfig: () => {
return false;
},
getCompilerOptionsFromTSConfig: () => {
return false;
},
},
'@nativescript/webpack/plugins/NativeScriptAngularCompilerPlugin': {
getAngularCompilerPlugin: () => {
return AngularCompilerStub;
},
},
'@ngtools/webpack': {
AngularCompilerPlugin: AngularCompilerStub,
},
'terser-webpack-plugin': TerserJsStub,
});
const webpackConfigTypeScript = proxyquire('./webpack.typescript', {
'@nativescript/webpack': nativeScriptDevWebpack,
'@nativescript/webpack/nativescript-target': emptyObject,
'@nativescript/webpack/transformers/ns-transform-native-classes': emptyObject,
'@nativescript/webpack/utils/tsconfig-utils': {
getNoEmitOnErrorFromTSConfig: () => {
return false;
},
getCompilerOptionsFromTSConfig: () => {
return false;
},
},
'terser-webpack-plugin': TerserJsStub,
});
const webpackConfigJavaScript = proxyquire('./webpack.javascript', {
'@nativescript/webpack': nativeScriptDevWebpack,
'@nativescript/webpack/nativescript-target': emptyObject,
'terser-webpack-plugin': TerserJsStub,
});
const webpackConfigVue = proxyquire('./webpack.vue', {
'@nativescript/webpack': nativeScriptDevWebpack,
'@nativescript/webpack/nativescript-target': emptyObject,
'@nativescript/webpack/transformers/ns-transform-native-classes': emptyObject,
'vue-loader/lib/plugin': EmptyClass,
'nativescript-vue-template-compiler': emptyObject,
'terser-webpack-plugin': TerserJsStub,
});
describe('webpack.config.js', () => {
const getInput = (options: { platform: string; hmr?: boolean; externals?: string[]; sourceMap?: boolean; hiddenSourceMap?: boolean | string }) => {
const input: any = Object.assign({}, options);
input[options.platform] = true;
return input;
};
[
{ type: 'javascript', webpackConfig: webpackConfigJavaScript },
{ type: 'typescript', webpackConfig: webpackConfigTypeScript },
{ type: 'angular', webpackConfig: webpackConfigAngular },
{ type: 'vue', webpackConfig: webpackConfigVue },
].forEach((element) => {
const { type, webpackConfig } = element;
['android', 'ios'].forEach((platform) => {
describe(`verify externals for webpack.${type}.js (${platform})`, () => {
afterEach(() => {
nativeScriptDevWebpack.getConvertedExternals = nsWebpackIndex.getConvertedExternals;
});
it('returns empty array when externals are not passed', () => {
const input = getInput({ platform });
const config = webpackConfig(input);
expect(config.externals).toEqual([]);
});
it('calls getConvertedExternals to convert externals', () => {
let isCalled = false;
nativeScriptDevWebpack.getConvertedExternals = () => {
isCalled = true;
return [];
};
const input = getInput({ platform, externals: ['nativescript-vue'] });
webpackConfig(input);
expect(isCalled).toBe(true, 'Webpack.config.js must use the getConvertedExternals method');
});
// if (platform === "ios") {
// it('has inspector_modules entry when tns-core-modules are not externals', () => {
// const input = getInput({ platform, externals: ['nativescript-vue'] });
// const config = webpackConfig(input);
// expect(config.entry["tns_modules/@nativescript/core/inspector_modules"]).toEqual("inspector_modules");
// });
// it('does not have inspector_modules entry when @nativescript/core are externals', () => {
// const input = getInput({ platform, externals: ['@nativescript'] });
// const config = webpackConfig(input);
// expect(config.entry["tns_modules/@nativescript/core/inspector_modules"]).toBeUndefined();
// });
// }
[
{
input: ['nativescript-vue'],
expectedOutput: [/^nativescript-vue((\/.*)|$)/],
},
{
input: ['nativescript-vue', 'nativescript-angular'],
expectedOutput: [/^nativescript-vue((\/.*)|$)/, /^nativescript-angular((\/.*)|$)/],
},
].forEach((testCase) => {
const input = getInput({ platform, externals: testCase.input });
it(`are correct regular expressions, for input ${testCase.input}`, () => {
const config = webpackConfig(input);
expect(config.externals).toEqual(testCase.expectedOutput);
});
});
});
if (type === 'angular') {
describe(`angular transformers for webpack.${type}.js (${platform})`, () => {
beforeEach(() => {
angularCompilerOptions = null;
});
it('should contain only NativeClass transformer by default', () => {
const input = getInput({ platform });
webpackConfig(input);
expect(angularCompilerOptions).toBeDefined();
expect(angularCompilerOptions.platformTransformers).toBeDefined();
expect(angularCompilerOptions.platformTransformers.length).toEqual(1);
expect(angularCompilerOptions.platformTransformers[0]).toEqual(FakeNativeClassTransformerFlag);
});
it('should contain the HMR transformer when the HMR flag is passed', () => {
const input = getInput({ platform, hmr: true });
webpackConfig(input);
expect(angularCompilerOptions).toBeDefined();
expect(angularCompilerOptions.platformTransformers).toBeDefined();
expect(angularCompilerOptions.platformTransformers.length).toEqual(2);
expect(angularCompilerOptions.platformTransformers[1]).toEqual(FakeHmrTransformerFlag);
});
});
}
describe(`source map for webpack.${type}.js (${platform})`, () => {
beforeEach(() => {
terserOptions = null;
});
it('should not set source maps without the flag', () => {
const input = getInput({ platform, sourceMap: false });
const config = webpackConfig(input);
expect(config.devtool).toEqual('none');
expect(terserOptions.sourceMap).toBeFalsy();
expect(terserOptions.terserOptions.output.semicolons).toBeTruthy();
expect(config.output.sourceMapFilename).toEqual('[file].map');
});
it('should set inline-source-map devtool', () => {
const input = getInput({ platform, sourceMap: true });
const config = webpackConfig(input);
expect(config.devtool).toEqual('inline-source-map');
expect(terserOptions.sourceMap).toBeTruthy();
expect(terserOptions.terserOptions.output.semicolons).toBeFalsy();
expect(config.output.sourceMapFilename).toEqual('[file].map');
});
});
describe(`hidden source map for webpack.${type}.js (${platform})`, () => {
beforeEach(() => {
terserOptions = null;
});
it('should not set source maps without the flag', () => {
const input = getInput({ platform, hiddenSourceMap: false });
const config = webpackConfig(input);
expect(config.devtool).toEqual('none');
expect(terserOptions.sourceMap).toBeFalsy();
expect(terserOptions.terserOptions.output.semicolons).toBeTruthy();
expect(config.output.sourceMapFilename).toEqual('[file].map');
});
it('should set hidden-source-map devtool and the default sourceMap folder', () => {
const input = getInput({ platform, hiddenSourceMap: true });
const config = webpackConfig(input);
expect(config.devtool).toEqual('hidden-source-map');
expect(terserOptions.sourceMap).toBeTruthy();
expect(terserOptions.terserOptions.output.semicolons).toBeFalsy();
expect(config.output.sourceMapFilename).toEqual(join('..', 'sourceMap', '[file].map'));
});
it('should override the sourceMap property and the default sourceMap folder', () => {
const input = getInput({ platform, sourceMap: true, hiddenSourceMap: true });
const config = webpackConfig(input);
expect(config.devtool).toEqual('hidden-source-map');
expect(terserOptions.sourceMap).toBeTruthy();
expect(terserOptions.terserOptions.output.semicolons).toBeFalsy();
expect(config.output.sourceMapFilename).toEqual(join('..', 'sourceMap', '[file].map'));
});
it('should set hidden-source-map devtool and override the sourceMapFilename', () => {
const newSourceMapFolder = 'myCoolSourceMapFolder';
const input = getInput({ platform, sourceMap: true, hiddenSourceMap: newSourceMapFolder });
const config = webpackConfig(input);
expect(config.devtool).toEqual('hidden-source-map');
expect(terserOptions.sourceMap).toBeTruthy();
expect(terserOptions.terserOptions.output.semicolons).toBeFalsy();
expect(config.output.sourceMapFilename).toEqual(join('..', newSourceMapFolder, '[file].map'));
});
});
});
});
});