1 | const run = jest.fn();
|
2 | const close = jest.fn();
|
3 | const watch = jest.fn().mockReturnValue({ close });
|
4 | const compiler = jest.fn().mockReturnValue({ run, watch });
|
5 |
|
6 | jest.setMock('webpack', compiler);
|
7 | jest.mock('../watchModeIPC');
|
8 | jest.mock('webpack/lib/Stats');
|
9 |
|
10 | let webpackWorker;
|
11 | let promiseMock;
|
12 | let webpackMock;
|
13 | let webpackStatsMock;
|
14 | let notifyIPCWatchCompileDone;
|
15 |
|
16 | describe('webpackWorker', () => {
|
17 | beforeEach(() => {
|
18 | promiseMock = require('bluebird');
|
19 | webpackMock = require('webpack');
|
20 | webpackStatsMock = require('webpack/lib/Stats');
|
21 | webpackWorker = require('../webpackWorker.js');
|
22 | notifyIPCWatchCompileDone = require('../watchModeIPC').notifyIPCWatchCompileDone;
|
23 | jest.doMock('testConfig', () => ({ webpack: 'config' }), { virtual: true });
|
24 | jest.resetModules();
|
25 | jest.clearAllMocks();
|
26 | process.removeAllListeners();
|
27 | });
|
28 |
|
29 | describe('arguments', () => {
|
30 | describe('options', () => {
|
31 |
|
32 | let _argv;
|
33 | beforeEach(() => {
|
34 | _argv = process.argv;
|
35 | });
|
36 | afterEach(() => {
|
37 | process.argv = _argv;
|
38 | });
|
39 |
|
40 | const optionsTest = (options) => {
|
41 | const finishStats = {
|
42 | compilation: {},
|
43 | startTime: 1500034498,
|
44 | endTime: 1500054500,
|
45 | };
|
46 | const doneCallback = jest.fn();
|
47 | jest.spyOn(console, 'log').mockImplementation(() => {});
|
48 |
|
49 |
|
50 | webpackWorker('testConfig', options , 0, 1, doneCallback);
|
51 | const thenCb = promiseMock.then.mock.calls[0][0];
|
52 | thenCb({ webpack: 'config', name: 'testApp' });
|
53 | const finishedCallback = run.mock.calls[0][0];
|
54 | finishedCallback(null, finishStats);
|
55 |
|
56 | expect(doneCallback).toHaveBeenCalled();
|
57 | if (options.json) {
|
58 | expect(console.log).not.toHaveBeenCalled();
|
59 | } else {
|
60 | expect(console.log).toHaveBeenCalledTimes(2);
|
61 | expect(console.log).toHaveBeenLastCalledWith(
|
62 | '%s Finished building %s within %s seconds',
|
63 | '[WEBPACK]',
|
64 | 'testApp',
|
65 | '20.002'
|
66 | );
|
67 | }
|
68 |
|
69 | if (options.argv) {
|
70 | expect(process.argv).toEqual(options.argv);
|
71 | }
|
72 | };
|
73 |
|
74 |
|
75 | it('should report json only when json: true', () => {
|
76 | optionsTest({ json: true });
|
77 | });
|
78 |
|
79 | it('should report to console as well only when json: false', () => {
|
80 | optionsTest({ json: false });
|
81 | });
|
82 |
|
83 | it('should set process arguments to the passed ones', () => {
|
84 | optionsTest({ json:true, argv: ['--watch', '--bail']});
|
85 | });
|
86 |
|
87 | it('should call done with stats when there is stats object', () => {
|
88 | const doneCallback = jest.fn();
|
89 | const statsObj = {
|
90 | compilation: {
|
91 | errors: [
|
92 | { message: 'module not found' },
|
93 | { message: 'module not found 2' },
|
94 | ]
|
95 | },
|
96 | toJson: jest.fn().mockReturnThis()
|
97 | };
|
98 | webpackWorker('testConfig', {}, 0, 1, doneCallback);
|
99 | const thenCb = promiseMock.then.mock.calls[0][0];
|
100 | thenCb({ webpack: 'config', name: 'testApp' });
|
101 | let finishedCallback = run.mock.calls[0][0];
|
102 |
|
103 | expect(process.listenerCount('SIGINT')).toBe(1);
|
104 | finishedCallback(null, statsObj);
|
105 |
|
106 | expect(process.listenerCount('SIGINT')).toBe(0);
|
107 | expect(doneCallback).toHaveBeenCalled();
|
108 | expect(doneCallback.mock.calls[0][0]).toMatchSnapshot();
|
109 |
|
110 | });
|
111 |
|
112 | it('should log in watch mode instead of calling done callback', () => {
|
113 | jest.useFakeTimers();
|
114 | const _temp = global.Date;
|
115 | global.Date = jest.fn(() => (
|
116 | {
|
117 | toTimeString: jest.fn().mockReturnValue({
|
118 | split: jest.fn().mockReturnValue(["12:05:54"])
|
119 | })
|
120 | }
|
121 | ));
|
122 | jest.spyOn(console, 'log');
|
123 | const doneCallback = jest.fn();
|
124 | const statsObj = {
|
125 | compilation: {
|
126 | errors: [
|
127 | { message: 'module not found' },
|
128 | { message: 'module not found 2' },
|
129 | ]
|
130 | },
|
131 | toJson: jest.fn().mockReturnThis()
|
132 | };
|
133 | webpackWorker('testConfig', { watch: true }, 0, 1, doneCallback);
|
134 | const thenCb = promiseMock.then.mock.calls[0][0];
|
135 | thenCb({ webpack: 'config', name: 'testApp' });
|
136 | let finishedCallback = watch.mock.calls[0][1];
|
137 |
|
138 | expect(process.listenerCount('SIGINT')).toBe(1);
|
139 | finishedCallback(null, statsObj);
|
140 |
|
141 | expect(process.listenerCount('SIGINT')).toBe(1);
|
142 | expect(doneCallback).not.toHaveBeenCalled();
|
143 | expect(console.log.mock.calls).toMatchSnapshot();
|
144 | expect(notifyIPCWatchCompileDone).toHaveBeenCalledWith(0);
|
145 | process.removeAllListeners('SIGINT');
|
146 | jest.useRealTimers();
|
147 | global.Date = _temp;
|
148 | });
|
149 |
|
150 | });
|
151 |
|
152 | describe('multi config options', () => {
|
153 | const multiConfigTest = options => {
|
154 | const errorMessage = '[WEBPACK] There is a difference between the amount of the provided configs. Maybe you where expecting command line arguments to be passed to your webpack.config.js. If so, you\'ll need to separate them with a -- from the parallel-webpack options.'
|
155 | jest.doMock('multiTestConfig', () => ( [{ fail: true}, { webpack: 'config'}]), { virtual: true });
|
156 | jest.doMock('testConfig', () => ({ webpack: 'config' }), { virtual: true });
|
157 | jest.spyOn(console, 'error').mockImplementation(() => {});
|
158 |
|
159 | webpackWorker(
|
160 | options.multi ? 'multiTestConfig' : 'testConfig',
|
161 | { json: true },
|
162 | options.configIndex,
|
163 | options.expectedConfigs,
|
164 | jest.fn()
|
165 | );
|
166 | const allConfigs = promiseMock.resolve.mock.calls[0][0];
|
167 | const thenCb = promiseMock.then.mock.calls[0][0];
|
168 | if (options.multi && options.expectedConfigs < 3) {
|
169 | const targetConfig = allConfigs[options.configIndex];
|
170 | thenCb(allConfigs)
|
171 | expect(compiler.mock.calls[0][0]).toEqual(targetConfig);
|
172 | } else {
|
173 | expect(() => thenCb(allConfigs)).toThrow(errorMessage)
|
174 | expect(console.error).toHaveBeenCalled();
|
175 | }
|
176 | }
|
177 |
|
178 | it('should select the correct indexed one if configs are array', () => {
|
179 | multiConfigTest({ multi: true, configIndex: 1, expectedConfigs: 2 });
|
180 | });
|
181 |
|
182 | it('should fail if expectedConfigLength > 1 in case of single config', () => {
|
183 | multiConfigTest({ multi: false, configIndex: 1, expectedConfigs: 2 });
|
184 | });
|
185 |
|
186 | it('should fail if expectedConfigLength dont match with config.length', () => {
|
187 | multiConfigTest({ multi: true, configIndex: 1, expectedConfigs: 3 });
|
188 | });
|
189 |
|
190 | it('should be able to handle config function return promise of array of config object', () => {
|
191 | const originalConfigs = [{ webpack: 'config' }, { webpack: 'config2' }];
|
192 | jest.doMock('promiseReturnConfigArray', () => Promise.resolve(originalConfigs), { virtual: true });
|
193 | const configIndex = 1
|
194 | const expectedConfigLength = 2
|
195 | webpackWorker(
|
196 | 'promiseReturnConfigArray',
|
197 | { json: true },
|
198 | configIndex,
|
199 | expectedConfigLength,
|
200 | jest.fn()
|
201 | )
|
202 | const thenCb = promiseMock.then.mock.calls[0][0];
|
203 | const targetConfig = originalConfigs[configIndex];
|
204 | thenCb(originalConfigs);
|
205 | expect(compiler.mock.calls[0][0]).toEqual(targetConfig);
|
206 | })
|
207 | })
|
208 | });
|
209 |
|
210 | describe('module functions', () => {
|
211 | describe('getAppName', () => {
|
212 | const getAppNameTest = (options, appName) => {
|
213 | const doneCallback = jest.fn();
|
214 | jest.spyOn(console, 'log');
|
215 | webpackWorker('testConfig', { json: false }, 0, 1, doneCallback);
|
216 |
|
217 | const thenCb = promiseMock.then.mock.calls[0][0];
|
218 | thenCb(Object.assign({ webpack: 'config' }, options));
|
219 |
|
220 | let finishedCallback = run.mock.calls[0][0];
|
221 | finishedCallback(null, { compilation: {}});
|
222 |
|
223 | expect(console.log.mock.calls[1][2]).toBe(appName);
|
224 | };
|
225 |
|
226 | it('should get the name from config.name', () => {
|
227 | getAppNameTest({ name: 'testApp' }, 'testApp');
|
228 | });
|
229 |
|
230 | it('should get the name from output.filename', () => {
|
231 | getAppNameTest(
|
232 | {
|
233 | output: {
|
234 | filename: 'test.app.js'
|
235 | }
|
236 | },
|
237 | 'test.app.js'
|
238 | );
|
239 | });
|
240 |
|
241 | it('should replace "[name]" pattern if there is one entry point', () => {
|
242 | getAppNameTest(
|
243 | {
|
244 | output: {
|
245 | filename: 'bundle.[name].js',
|
246 | },
|
247 | entry: {
|
248 | testApp: 'filepath',
|
249 | }
|
250 | },
|
251 | 'bundle.testApp.js'
|
252 | );
|
253 | });
|
254 |
|
255 | it('should replace all matches of "[name]" pattern if there is one entry point', () => {
|
256 | getAppNameTest(
|
257 | {
|
258 | output: {
|
259 | filename: 'bundle/[name]/[name].js',
|
260 | },
|
261 | entry: {
|
262 | testApp: 'filepath',
|
263 | }
|
264 | },
|
265 | 'bundle/testApp/testApp.js'
|
266 | );
|
267 | });
|
268 | });
|
269 |
|
270 | describe('getOutputOptions', () => {
|
271 | it('should get stats options if they set', () => {
|
272 | const statsObj = {
|
273 | compilation: {
|
274 | },
|
275 | toString: jest.fn().mockReturnValue('size: 321.kb'),
|
276 | toJson: jest.fn().mockReturnValue('size: 321.kb')
|
277 | };
|
278 | jest.spyOn(console, 'log');
|
279 | const doneCallback = jest.fn();
|
280 | webpackWorker('testConfig', {
|
281 | stats: true,
|
282 | modulesSort: 'name',
|
283 | chunksSort: 'size',
|
284 | assetsSort: 'name',
|
285 | exclude: ['file'],
|
286 | colors: true
|
287 | }, 0, 1, doneCallback);
|
288 |
|
289 | expect(promiseMock.resolve.mock.calls[0][0]).toEqual({ webpack: 'config' });
|
290 | const thenCb = promiseMock.then.mock.calls[0][0];
|
291 | thenCb({ webpack: 'config', name: 'testApp' });
|
292 |
|
293 |
|
294 | let finishedCallback = run.mock.calls[0][0];
|
295 | finishedCallback(null, statsObj);
|
296 | expect(statsObj.toString.mock.calls).toMatchSnapshot();
|
297 | });
|
298 |
|
299 | it('should translate stats string to object', () => {
|
300 | jest.spyOn(console, 'log');
|
301 | let presetToOptions = jest.spyOn(webpackStatsMock, 'presetToOptions');
|
302 |
|
303 | const doneCallback = jest.fn();
|
304 |
|
305 | webpackWorker('testConfig', {
|
306 | stats: true,
|
307 | modulesSort: 'name',
|
308 | chunksSort: 'size',
|
309 | assetsSort: 'name',
|
310 | exclude: ['file'],
|
311 | colors: true
|
312 | }, 0, 1, doneCallback);
|
313 |
|
314 | expect(promiseMock.resolve.mock.calls[0][0]).toEqual({ webpack: 'config' });
|
315 | const thenCb = promiseMock.then.mock.calls[0][0];
|
316 | thenCb({ webpack: 'config', name: 'testApp', 'stats': 'verbose' });
|
317 |
|
318 | expect(presetToOptions).toHaveBeenCalled();
|
319 | })
|
320 | });
|
321 | });
|
322 |
|
323 | describe('creator function', () => {
|
324 | describe('shutdownCallback', () => {
|
325 |
|
326 | let _exit;
|
327 | beforeEach(() => {
|
328 | _exit = process.exit;
|
329 | });
|
330 | afterEach(() => {
|
331 | process.exit = _exit;
|
332 | process.removeAllListeners('SIGINT');
|
333 | });
|
334 |
|
335 | const shutdownCallbackTest = options => {
|
336 | const doneCallback = jest.fn();
|
337 | const statsObj = {
|
338 | compilation: {
|
339 | errors: [
|
340 | { message: 'module not found' },
|
341 | { message: 'module not found 2' },
|
342 | ]
|
343 | },
|
344 | toJson: jest.fn().mockReturnThis()
|
345 | };
|
346 | process.exit = jest.fn();
|
347 |
|
348 | webpackWorker('testConfig', options , 0, 1, doneCallback);
|
349 | const thenCb = promiseMock.then.mock.calls[0][0];
|
350 | thenCb({ webpack: 'config', name: 'testApp' });
|
351 | const shutdownCallback = process.listeners('SIGINT')[0];
|
352 | shutdownCallback();
|
353 |
|
354 |
|
355 | if (options.watch) {
|
356 | expect(close.mock.calls[0][0]).toBe(doneCallback);
|
357 | expect(doneCallback.mock.calls[0][0]).toMatchSnapshot();
|
358 | } else {
|
359 | expect(close).not.toHaveBeenCalled();
|
360 | expect(doneCallback.mock.calls[0][0]).toMatchSnapshot();
|
361 | }
|
362 | };
|
363 |
|
364 | it('should watcher.close and done in watch mode on SIGINT', () => {
|
365 | shutdownCallbackTest({ watch: true });
|
366 | });
|
367 |
|
368 | it('should call done callback', () => {
|
369 | shutdownCallbackTest({ watch: false });
|
370 | });
|
371 | });
|
372 |
|
373 | describe('finishedCallback', () => {
|
374 | const finishCbTest = options => {
|
375 | const statsObj = {
|
376 | compilation: {
|
377 | },
|
378 | toString: jest.fn().mockReturnValue('size: 321.kb'),
|
379 | toJson: jest.fn().mockReturnValue('size: 321.kb')
|
380 | };
|
381 | const doneCallback = jest.fn();
|
382 | jest.spyOn(console, 'log');
|
383 | jest.spyOn(console, 'error');
|
384 |
|
385 | webpackWorker('testConfig', options.worker, 0, 1, doneCallback);
|
386 | expect(promiseMock.resolve.mock.calls[0][0]).toEqual({ webpack: 'config' });
|
387 |
|
388 | const thenCb = promiseMock.then.mock.calls[0][0];
|
389 | thenCb({ webpack: 'config', name: 'testApp' });
|
390 | expect(process.listenerCount('SIGINT')).toBe(1);
|
391 |
|
392 | let finishedCallback = run.mock.calls[0][0];
|
393 |
|
394 |
|
395 | if (options.isError) {
|
396 | finishedCallback({ error: 'Exception' }, { compilation: {}});
|
397 | expect(console.error).toHaveBeenCalledTimes(2);
|
398 | expect(doneCallback.mock.calls[0]).toEqual([{error: 'Exception'}]);
|
399 | } else if (options.isStats) {
|
400 | finishedCallback(null, statsObj);
|
401 | expect(doneCallback.mock.calls[0]).toMatchSnapshot();
|
402 | expect(console.log.mock.calls[1]).toEqual(['size: 321.kb']);
|
403 | } else {
|
404 | finishedCallback(null, statsObj);
|
405 | expect(doneCallback.mock.calls[0]).toEqual([null, '']);
|
406 | }
|
407 | expect(process.listenerCount('SIGINT')).toBe(0);
|
408 |
|
409 | };
|
410 |
|
411 | it('should spit out error, remove SIGINT and close when an exception happen', () => {
|
412 | finishCbTest({
|
413 | worker: { json: true },
|
414 | isError: true
|
415 | });
|
416 | });
|
417 |
|
418 | it('should remove SIGINT and call done with stats', () => {
|
419 | finishCbTest({
|
420 | worker: { stats: true },
|
421 | isError: false,
|
422 | isStats: true
|
423 | });
|
424 | });
|
425 |
|
426 | it('should set the promise and call done on success', () => {
|
427 | finishCbTest({
|
428 | worker: { json: true },
|
429 | isError: false,
|
430 | isStats: false
|
431 | });
|
432 | });
|
433 | });
|
434 | });
|
435 | });
|