1 |
|
2 |
|
3 |
|
4 |
|
5 | const assert = require('assert').strict;
|
6 | const path = require('path');
|
7 | const sinon = require('sinon');
|
8 | const lib = require('./lib');
|
9 | const webpack = require('webpack');
|
10 | const fsExtra = require('fs-extra');
|
11 |
|
12 |
|
13 | const devServerFn = require('../lib/dev-server');
|
14 | const logger = require('../lib/logger');
|
15 | const wpDevConf = require('../webpack-config/webpack.base.conf');
|
16 | const febsModule = require('../index');
|
17 |
|
18 | describe('FEBS Development Tests', function () {
|
19 | let compile;
|
20 | let fs;
|
21 |
|
22 | logger.setLogLevel('warn');
|
23 |
|
24 | beforeEach(function () {
|
25 | process.env.FEBS_TEST = true;
|
26 |
|
27 |
|
28 | fs = lib.createFS();
|
29 |
|
30 |
|
31 | compile = lib.createCompileFn(fs, 'development');
|
32 | });
|
33 |
|
34 | describe('ECMAScript', async function () {
|
35 | it('builds ES bundle', async function () {
|
36 | const compiled = await compile(lib.createConf({
|
37 | entry: {
|
38 | app: lib.absPath('fixtures/src/main-es2015.js'),
|
39 | },
|
40 | }));
|
41 |
|
42 | assert.equal(compiled.code.app[0].filename, 'app.bundle.js');
|
43 | assert(compiled.code.app[0].content.includes('add: function add()'));
|
44 | });
|
45 |
|
46 | it('transpiles ES from @rei namespace only', async function () {
|
47 |
|
48 | const srcReiNamespace = path.join(__dirname, 'test-modules/@rei/test');
|
49 | const destReiNamespace = path.join(__dirname, '../node_modules/@rei/test');
|
50 | const srcNonReiNamespace = path.join(__dirname, 'test-modules/some-module');
|
51 | const destNonReiNamespace = path.join(__dirname, '../node_modules/some-module');
|
52 |
|
53 | fsExtra.copySync(srcReiNamespace, destReiNamespace);
|
54 | fsExtra.copySync(srcNonReiNamespace, destNonReiNamespace);
|
55 |
|
56 | const compiled = await compile(lib.createConf({
|
57 | entry: {
|
58 | app: lib.absPath('fixtures/src/main-es2015-rei-namespace.js'),
|
59 | },
|
60 | }));
|
61 |
|
62 |
|
63 | assert(compiled.code.app[0].content.includes('add3: function add3'));
|
64 |
|
65 |
|
66 | assert(!compiled.code.app[0].content.includes('add4: function add4'));
|
67 |
|
68 |
|
69 | fsExtra.removeSync(destReiNamespace);
|
70 | fsExtra.removeSync(destNonReiNamespace);
|
71 | });
|
72 |
|
73 | it('builds multiple ES bundles', async function () {
|
74 | const compiled = await compile(lib.createConf({
|
75 | entry: {
|
76 | app1: lib.absPath('fixtures/src/main-es2015.js'),
|
77 | app2: lib.absPath('fixtures/src/main-es2015.js'),
|
78 | },
|
79 | }));
|
80 |
|
81 | assert(lib.compiledContains(compiled, {
|
82 | entryName: /app1/,
|
83 | content: /unction add/,
|
84 | fileName: /\.js$/,
|
85 | }));
|
86 |
|
87 | assert(lib.compiledContains(compiled, {
|
88 | entryName: /app2/,
|
89 | content: /unction add/,
|
90 | fileName: /\.js$/,
|
91 | }));
|
92 | });
|
93 |
|
94 | it('detects ES syntax errors', async function () {
|
95 | await compile(lib.createConf({
|
96 | entry: {
|
97 | app: lib.absPath('fixtures/src/main-es2015-syntax-errors.js'),
|
98 | },
|
99 | })).then((o) => {
|
100 | assert.ok(o.stats.compilation.errors[0].message.includes('Unexpected token'));
|
101 | });
|
102 | });
|
103 |
|
104 | it('polyfills based on supported browsers (IE11)', async function () {
|
105 | const compiled = await compile(lib.createConf({
|
106 | entry: {
|
107 | app1: lib.absPath('fixtures/src/main-es-polyfill.js'),
|
108 | },
|
109 | }));
|
110 |
|
111 |
|
112 | assert(lib.compiledContains(compiled, {
|
113 | entryName: /app1/,
|
114 | content: /es6.object.assign/,
|
115 | fileName: /\.js$/,
|
116 | }));
|
117 |
|
118 |
|
119 | assert(lib.compiledContains(compiled, {
|
120 | entryName: /app1/,
|
121 | content: /es6.promise/,
|
122 | fileName: /\.js$/,
|
123 | }));
|
124 | })
|
125 | });
|
126 |
|
127 |
|
128 | describe('Vue', function () {
|
129 | it('compiles Vue tags', async function () {
|
130 | const compiled = await compile(lib.createConf({
|
131 | entry: {
|
132 | app: lib.absPath('fixtures/src/vue/main-vue.js'),
|
133 | },
|
134 | }));
|
135 |
|
136 | assert(lib.compiledWithNoErrors(compiled), compiled.stats.compilation.errors);
|
137 |
|
138 | assert(lib.compiledContains(compiled, {
|
139 | entryName: /^app$/,
|
140 | content: /Vue says/,
|
141 | fileName: /\.js$/,
|
142 | }));
|
143 | });
|
144 |
|
145 | it('extracted external vue css styles and put into app css file', async function () {
|
146 | const compiled = await compile(lib.createConf({
|
147 | entry: {
|
148 | app: lib.absPath('fixtures/src/vue/main-vue.js'),
|
149 | },
|
150 | }));
|
151 |
|
152 | assert(lib.compiledWithNoErrors(compiled), compiled.stats.compilation.errors);
|
153 |
|
154 | assert(lib.compiledContains(compiled, {
|
155 | entryName: /^app$/,
|
156 | content: /papayawhip/,
|
157 | fileName: /\.css$/,
|
158 | }));
|
159 | });
|
160 |
|
161 | it('extracted inline vue css styles and put into app css file', async function () {
|
162 | const compiled = await compile(lib.createConf({
|
163 | entry: {
|
164 | app: lib.absPath('fixtures/src/vue/main-vue.js'),
|
165 | },
|
166 | }));
|
167 |
|
168 | assert(lib.compiledContains(compiled, {
|
169 | entryName: /^app$/,
|
170 | content: /red/,
|
171 | fileName: /\.css$/,
|
172 | }));
|
173 |
|
174 | assert(lib.compiledWithNoErrors(compiled), compiled.stats.compilation.errors);
|
175 | });
|
176 |
|
177 | it('transpiles es2015+ Vue tags', async function () {
|
178 | const compiled = await compile(lib.createConf({
|
179 | entry: {
|
180 | app: lib.absPath('fixtures/src/vue/main-vue.js'),
|
181 | },
|
182 | }));
|
183 |
|
184 | assert(lib.compiledWithNoErrors(compiled), compiled.stats.compilation.errors);
|
185 |
|
186 | assert(lib.compiledContains(compiled, {
|
187 | entryName: /^app$/,
|
188 | content: /function helloWorld/,
|
189 | fileName: /\.js$/,
|
190 | }));
|
191 | });
|
192 |
|
193 | it('detects Vue JavaScript syntax errors', async function () {
|
194 | await compile(lib.createConf({
|
195 | entry: {
|
196 | app: lib.absPath('fixtures/src/vue/main-vue-syntax-error.js'),
|
197 | },
|
198 | })).then((o) => {
|
199 | assert.ok(o.stats.compilation.errors[0].message.includes('SyntaxError'));
|
200 | });
|
201 | });
|
202 | });
|
203 |
|
204 | describe('Sourcemaps', async function () {
|
205 | it('generates inline ES sourcemaps', async function () {
|
206 | const compiled = await compile(lib.createConf({
|
207 | entry: {
|
208 | app: lib.absPath('fixtures/src/main-es2015.js'),
|
209 | },
|
210 | }));
|
211 |
|
212 | assert(compiled.code.app[0].content.includes('sourceURL'));
|
213 | });
|
214 | });
|
215 |
|
216 | describe('Manifest', async function () {
|
217 | it('generates a manifest json file for versioned asset mappings', async function () {
|
218 | const getJsonFromFS = lib.getJsonFromFile(fs);
|
219 |
|
220 | const compiled = await compile(lib.createConf({
|
221 | entry: {
|
222 | app: lib.absPath('fixtures/src/main-es2015.js'),
|
223 | },
|
224 | }));
|
225 |
|
226 | assert(lib.compiledWithNoErrors(compiled), compiled.stats.compilation.errors);
|
227 |
|
228 | const manifestFile = path.resolve(compiled.options.output.path, 'febs-manifest.json');
|
229 | assert(fs.statSync(manifestFile).isFile());
|
230 |
|
231 | const manifestJson = getJsonFromFS(manifestFile);
|
232 | assert.equal(manifestJson['app.js'], 'app.bundle.js');
|
233 | });
|
234 | });
|
235 |
|
236 | describe('Logger', function () {
|
237 | it('should contain setLogLevel function', function () {
|
238 | assert(logger.setLogLevel);
|
239 | });
|
240 |
|
241 | it('allow changing log levels', function () {
|
242 | logger.setLogLevel('warn');
|
243 | assert.equal(logger.transports.console.level, 'warn');
|
244 | });
|
245 | });
|
246 |
|
247 | describe('addVueSSRToWebpackConfig', function () {
|
248 | it('should add VueSSRServerPlugin to webpack config', function () {
|
249 | const febs = febsModule({
|
250 | fs,
|
251 | });
|
252 |
|
253 | const wpConfig = febs.addVueSSRToWebpackConfig(true, wpDevConf);
|
254 |
|
255 | assert(wpConfig.plugins.some(plugin => plugin.constructor.name === 'VueSSRServerPlugin'));
|
256 | assert.equal(wpConfig.output.libraryTarget, 'commonjs2');
|
257 | });
|
258 |
|
259 | it('should not add VueSSRServerPlugin', function () {
|
260 | const febs = febsModule({
|
261 | fs,
|
262 | });
|
263 |
|
264 | const wpConfig = febs.addVueSSRToWebpackConfig(false, wpDevConf);
|
265 |
|
266 | assert(wpConfig.plugins.every(plugin => plugin.constructor.name !== 'VueSSRServerPlugin'));
|
267 | });
|
268 | });
|
269 |
|
270 | describe('getWebpackConfig', function () {
|
271 | it('should not return multiple plugin entries after merging confs', function () {
|
272 | const febs = febsModule({
|
273 | fs,
|
274 | });
|
275 | const expectedLength = wpDevConf.module.rules.length;
|
276 | const wpConfig = febs.getWebpackConfig(false)(wpDevConf);
|
277 | assert.equal(expectedLength, wpConfig.module.rules.length);
|
278 | });
|
279 |
|
280 | it('should not contain ManifestPlugin if SSR build', function () {
|
281 | const febs = febsModule({
|
282 | fs,
|
283 | });
|
284 |
|
285 | const wpConfig = febs.getWebpackConfigFn(true)(wpDevConf);
|
286 | assert(wpConfig.plugins.every(plugin => plugin.constructor.name !== 'ManifestPlugin'));
|
287 | });
|
288 |
|
289 | it('should contain ManifestPlugin if not SSR build', function () {
|
290 | const febs = febsModule({
|
291 | fs,
|
292 | });
|
293 |
|
294 | const wpConfig = febs.getWebpackConfigFn(false)(wpDevConf);
|
295 | assert(wpConfig.plugins.some(plugin => plugin.constructor.name === 'ManifestPlugin'));
|
296 | });
|
297 | });
|
298 |
|
299 | describe('Webpack config', async function () {
|
300 | it('Output path can be modified', async function () {
|
301 | const compiled = await compile(lib.createConf({
|
302 | entry: {
|
303 | app: lib.absPath('fixtures/src/main-es2015.js'),
|
304 | },
|
305 | output: {
|
306 | path: lib.absPath('build/modified-output-path'),
|
307 | },
|
308 | }));
|
309 |
|
310 | assert(compiled.options.output.path.includes('build/modified-output-path'));
|
311 | });
|
312 | });
|
313 |
|
314 | describe('febs-config', function () {
|
315 | it('should allow dist path to be changed', function () {
|
316 | const desiredOutputPath = path.resolve('./cool_output_path');
|
317 |
|
318 | const febs = febsModule({
|
319 | fs,
|
320 | }, {
|
321 | output: {
|
322 | path: desiredOutputPath,
|
323 | },
|
324 | });
|
325 |
|
326 | const webpackConfig = febs.getWebpackConfig(false)(wpDevConf);
|
327 |
|
328 | assert.equal(webpackConfig.output.path, path.resolve(desiredOutputPath, '@rei', 'febs'));
|
329 | });
|
330 |
|
331 | it('should allow entry points to be changed', function () {
|
332 | const desiredEntryPath = 'src/js/entryX.js';
|
333 |
|
334 | const webpackConfig = febsModule({
|
335 | fs,
|
336 | }, {
|
337 | entry: {
|
338 | app: [
|
339 | desiredEntryPath,
|
340 | ],
|
341 | },
|
342 | }).getWebpackConfig(false)(wpDevConf);
|
343 |
|
344 | assert(webpackConfig.entry.app[0].endsWith(desiredEntryPath));
|
345 | });
|
346 |
|
347 | describe('febsConfigMerge', function () {
|
348 | it('should override output path from febs-config', function () {
|
349 | const febs = febsModule({
|
350 | fs,
|
351 | });
|
352 |
|
353 | const febsConfig = {
|
354 | output: {
|
355 | path: 'a',
|
356 | },
|
357 | };
|
358 |
|
359 | const wpConfig = {
|
360 | output: {
|
361 | path: 'b',
|
362 | },
|
363 | };
|
364 |
|
365 | const expected = path.resolve(process.cwd(), febsConfig.output.path, '@rei/febs');
|
366 | assert.deepEqual(febs.febsConfigMerge(febsConfig, wpConfig).output.path, expected);
|
367 | });
|
368 |
|
369 | it('should use wpConf output if none in febs-config', function () {
|
370 | const febs = febsModule({
|
371 | fs,
|
372 | });
|
373 |
|
374 | const febsConfig = {
|
375 | entry: {},
|
376 | };
|
377 |
|
378 | const wpConfig = {
|
379 | output: {
|
380 | path: 'b',
|
381 | },
|
382 | };
|
383 |
|
384 | assert.deepEqual(febs.febsConfigMerge(febsConfig, wpConfig).output.path, 'b');
|
385 | });
|
386 |
|
387 | it('should update wpConfig entry with fully qualified paths', function () {
|
388 | const febs = febsModule({
|
389 | fs,
|
390 | });
|
391 |
|
392 | const febsConfig = {
|
393 | entry: {
|
394 | details: [
|
395 | 'relative/path/to/entry0.js',
|
396 | 'relative/path/to/entry1.js',
|
397 | ],
|
398 | },
|
399 | };
|
400 |
|
401 | const wpConfig = {
|
402 | entry: {
|
403 | app: [
|
404 | 'some/path/to/entry.js',
|
405 | ],
|
406 | },
|
407 | output: {
|
408 | path: 'b',
|
409 | },
|
410 | };
|
411 |
|
412 | const expected0 = path.resolve(process.cwd(), febsConfig.entry.details[0]);
|
413 | const expected1 = path.resolve(process.cwd(), febsConfig.entry.details[1]);
|
414 | assert.deepEqual(febs.febsConfigMerge(febsConfig, wpConfig).entry.details[0], expected0);
|
415 | assert.deepEqual(febs.febsConfigMerge(febsConfig, wpConfig).entry.details[1], expected1);
|
416 | });
|
417 |
|
418 | it('original webpack config should not be modified', function () {
|
419 | const febs = febsModule({
|
420 | fs,
|
421 | });
|
422 |
|
423 | const febsConfig = {
|
424 | entry: {
|
425 | details: [
|
426 | 'relative/path/to/entry0.js',
|
427 | 'relative/path/to/entry1.js',
|
428 | ],
|
429 | },
|
430 | };
|
431 |
|
432 | const wpConfig = {
|
433 | entry: {
|
434 | app: [
|
435 | 'some/path/to/entry.js',
|
436 | ],
|
437 | },
|
438 | output: {
|
439 | path: 'b',
|
440 | },
|
441 | };
|
442 |
|
443 | febs.febsConfigMerge(febsConfig, wpConfig);
|
444 |
|
445 | assert.equal(wpConfig.output.path, 'b');
|
446 | assert.deepEqual(wpConfig.entry.app, ['some/path/to/entry.js']);
|
447 | });
|
448 | });
|
449 | });
|
450 |
|
451 | describe('Exit codes', function () {
|
452 | it('should not return exit code 1 in dev mode so that watching persists)', async function () {
|
453 | await compile(lib.createConf({
|
454 | entry: {
|
455 | app1: lib.absPath('fixtures/src/main-es2015-syntax-errors.js'),
|
456 | },
|
457 | })).then((o) => {
|
458 | assert.equal(o.exitCode, 0);
|
459 | });
|
460 | });
|
461 | });
|
462 |
|
463 | describe('Dev Server', function () {
|
464 |
|
465 | let FakeWDS;
|
466 |
|
467 | beforeEach(() => FakeWDS = function (compiler) {
|
468 | this.listen = () => {
|
469 | };
|
470 | this.compiler = compiler;
|
471 | });
|
472 |
|
473 | it('should create new server', function () {
|
474 | const devServer = devServerFn(FakeWDS, () => {
|
475 | });
|
476 | assert(devServer instanceof FakeWDS);
|
477 | });
|
478 |
|
479 | it('should pass in compiler and webpack conf', function () {
|
480 | const febs = febsModule({
|
481 | fs,
|
482 | });
|
483 |
|
484 |
|
485 | const devServer = febs.startDevServerFn(FakeWDS)();
|
486 | assert(devServer instanceof FakeWDS);
|
487 |
|
488 |
|
489 | assert(devServer.compiler instanceof webpack.Compiler);
|
490 | });
|
491 | });
|
492 | });
|