UNPKG

18.3 kBJavaScriptView Raw
1'use strict';
2
3require('dotenv').config();
4
5const _ = require('underscore');
6const path = require('path');
7const fs = require('fs-extra');
8const request = require('request');
9const zlib = require('zlib');
10const NodeunitAsync = require('nodeunit-async');
11
12const AssetProcessor = require('../lib/assetProcessor');
13
14// create our TestHelper
15const th = new NodeunitAsync();
16
17if (_.any(['S3_BUCKET', 'S3_ACCESS_KEY', 'S3_SECRET', 'GITHUB_OAUTH_TOKEN'], k => { return _.isUndefined(process.env[k]); })) {
18 throw new Error("Missing required configuration. Do you have a .env file?");
19}
20
21const credentials = {
22 s3: {
23 bucket: process.env.S3_BUCKET,
24 key: process.env.S3_ACCESS_KEY,
25 secret: process.env.S3_SECRET
26 },
27 git: {
28 token: process.env.GITHUB_OAUTH_TOKEN
29 }
30};
31
32// Common assetProcessor used for majority of tests
33const assetProcessor = _assetProcessorForTestConfig('test-config.json');
34
35exports.testGetJavaScriptFiles = function(test) {
36
37 test.expect(1);
38
39 th.runTest(test, {
40 getJavaScriptFiles: [function(next) {
41 assetProcessor.getJavaScriptFiles(next);
42 }],
43 assertResult: ['getJavaScriptFiles', function(next, results) {
44 const files = results && results.getJavaScriptFiles;
45 const expected = [
46 'js/js_directory_1/one.js',
47 'js/js_directory_2/two.js',
48 'js/js_directory_1/three.js',
49 'mixed/four.js',
50 'mixed/mixed_directory_1/five.js',
51 'mixed/mixed_directory_2/six.js',
52 'js/js_excluded/shouldBeIncluded.js',
53 'js/js_directory_1/anywhere1.js',
54 'js/js_directory_2/anywhere2.js',
55 'mixed/anywhere5.js',
56 'mixed/mixed_directory_1/anywhere3.js',
57 'mixed/mixed_directory_2/anywhere4.js'
58 ];
59
60 test.deepEqual(expected, files);
61 next();
62 }]
63 });
64};
65
66exports.testGetCssFiles = function(test) {
67
68 test.expect(1);
69
70 th.runTest(test, {
71 getCssFiles: [function(next) {
72 assetProcessor.getCssFiles(next);
73 }],
74 assertResult: ['getCssFiles', function(next, results) {
75 const files = results && results.getCssFiles;
76 const expected = [
77 'css/css_directory_1/one.css',
78 'css/css_directory_2/two.css',
79 'css/css_directory_1/three.css',
80 'mixed/mixed_directory_1/four.css',
81 'css/css_directory_2/five.css',
82 'css/css_priority/priority2_butfirst.css',
83 'css/css_priority/prioirty1_butsecond.css',
84 'css/css_priority/priority3_butthird.css',
85 'css/css_directory_1/anywhere1.css',
86 'css/css_directory_1/anywhere2.css',
87 'css/css_directory_2/anywhere3.css',
88 'mixed/mixed_directory_2/anywhere4.css'
89 ];
90 test.deepEqual(expected, files);
91 next();
92 }]
93 });
94};
95
96exports.testGetImageFiles = function(test) {
97
98 test.expect(1);
99
100 th.runTest(test, {
101 getImageFiles: [function(next) {
102 assetProcessor.getImageFiles(next);
103 }],
104 assertResult: ['getImageFiles', function(next, results) {
105 const files = results && results.getImageFiles;
106 const expected = [
107 'img/img_directory_1/grey_wash_wall.png',
108 'img/img_directory_1/mooning.png',
109 'img/img_directory_2/purty_wood.png',
110 'img/slash_it.png'
111 ];
112 test.deepEqual(expected, files);
113 next();
114 }]
115 });
116};
117
118exports.testGetExtraFiles = function(test) {
119
120 test.expect(1);
121
122 th.runTest(test, {
123 getExtraFiles: [function(next) {
124 assetProcessor.getExtraFiles(next);
125 }],
126 assertResult: ['getExtraFiles', function(next, results) {
127 const files = results && results.getExtraFiles;
128 const expected = [
129 'extra/fonts/FontAwesome.otf',
130 'extra/fonts/fontawesome-webfont.eot',
131 'extra/fonts/fontawesome-webfont.svg',
132 'extra/fonts/fontawesome-webfont.ttf',
133 'extra/fonts/fontawesome-webfont.woff',
134 'extra/swf/copy_csv_xls.swf'
135 ];
136 test.deepEqual(expected, files);
137 next();
138 }]
139 });
140};
141
142exports.testGetFilesNormalizedFullPaths = function(test) {
143
144 test.expect(1);
145
146 th.runTest(test, {
147 getCssFiles: [function(next) {
148 assetProcessor.getCssFiles(true, next);
149 }],
150 assertResult: ['getCssFiles', function(next, results) {
151 const files = results && results.getCssFiles;
152 test.ok(files[0] && files[0].indexOf(path.join(__dirname, 'test-files', 'css')) === 0);
153 next();
154 }]
155 });
156};
157
158exports.testCompileLessFiles = function(test) {
159
160 const filesToCreate = [
161 path.join(__dirname, 'test-files', 'css', 'css_directory_1', 'three.css'),
162 path.join(__dirname, 'test-files', 'mixed', 'mixed_directory_2', 'anywhere4.css')
163 ];
164
165
166 test.expect(2);
167
168 filesToCreate.forEach(function(file) {
169 fs.copySync(file, file+'.deleted');
170 fs.unlinkSync(file);
171 });
172
173 th.runTest(test, {
174 compileLessFiles: [function(next) {
175 assetProcessor.compileLessFiles(next);
176 }],
177 assertResult: ['compileLessFiles', function(next, results) {
178 const files = results && results.compileLessFiles || [];
179 test.ok(files.some(function(file) {
180 return path.basename(file) === 'three.css';
181 }));
182 test.ok(files.some(function(file) {
183 return path.basename(file) === 'anywhere4.css';
184 }));
185
186 //TODO: modify nodeunit async to have a per-test teardown; otherwise failure here could screw thing up
187 filesToCreate.forEach(function(file) {
188 const deletedPath = file+'.deleted';
189 if (fs.existsSync(deletedPath)) {
190 fs.copySync(deletedPath ,file);
191 fs.unlinkSync(deletedPath);
192 }
193 });
194
195 next();
196 }]
197 });
198
199};
200
201exports.testUploadJavaScriptToCdn = function(test) {
202
203 test.expect(8);
204
205 th.runTest(test, {
206 uploadJavaScriptToCdn: [function(next) {
207 assetProcessor.uploadJavaScriptToCdn(next);
208 }],
209 download: ['uploadJavaScriptToCdn', function(next, results) {
210 _downloadAndUncompress(results.uploadJavaScriptToCdn, next);
211 }],
212 assertResult: ['download', function(next, results) {
213
214 const js = results.download && results.download || '';
215
216 test.ok(results.uploadJavaScriptToCdn);
217 test.ok(js);
218 test.equal(-1, js.indexOf('longVariableName'));
219 test.ok(js.indexOf('\n') === -1 || js.indexOf('\n') > 100); // newline added at end of file for source mapping, but none before that
220 test.ok(js.indexOf('1') < js.indexOf('2'));
221 test.ok(js.indexOf('2') < js.indexOf('3'));
222 test.ok(js.indexOf('3') < js.indexOf('4'));
223 test.ok(js.indexOf('5') < js.indexOf('6'));
224
225 next();
226 }]
227 });
228
229};
230
231exports.testUploadJavaScriptToCdn_withErrors = function(test) {
232
233 const otherAssetProcessor = _assetProcessorForTestConfig('test-config-withErrors.json');
234
235 test.expect(2);
236
237 th.runTest(test, {
238 uploadJavaScriptToCdn: [function(next) {
239 otherAssetProcessor.uploadJavaScriptToCdn(function(err) {
240 next(null, err);
241 });
242 }],
243 assertResult: ['uploadJavaScriptToCdn', function(next, results) {
244
245 const err = results.uploadJavaScriptToCdn;
246
247 test.ok(err);
248 test.ok(err.message.match(/unexpected token/i));
249
250 next();
251 }]
252 });
253
254};
255
256
257exports.testUploadCssToCdn = function(test) {
258
259 test.expect(13);
260
261 th.runTest(test, {
262 uploadCssToCdn: [function(next) {
263 assetProcessor.uploadCssToCdn(next);
264 }],
265 download: ['uploadCssToCdn', function(next, results) {
266 _downloadAndUncompress(results.uploadCssToCdn, next);
267 }],
268 assertResult: ['download', function(next, results) {
269
270 const css = results.download && results.download || '';
271 const bucket = credentials.s3 && credentials.s3.bucket || 'my-aws-s3-bucket';
272
273 test.ok(results.uploadCssToCdn);
274
275 test.ok(css.indexOf('.one') < css.indexOf('.two'));
276 test.ok(css.indexOf('.three') < css.indexOf('.four'));
277 test.ok(css.indexOf('.four') < css.indexOf('.five'));
278 test.ok(css.indexOf('.five') < css.indexOf('.p2'));
279 test.ok(css.indexOf('.p2') < css.indexOf('.p1'));
280 test.ok(css.indexOf('.p1') < css.indexOf('.p3'));
281
282 // Tests the URL Rebase
283 test.notEqual(-1, css.indexOf('url(/img/img_directory_1/grey_wash_wall.png)'));
284 test.notEqual(-1, css.indexOf('url(/img/img_directory_1/mooning.png)'));
285 test.notEqual(-1, css.indexOf('url(/extra/fonts/fontawesome-webfont.woff)'));
286 test.notEqual(-1, css.indexOf('url(//fonts.googleapis.com/css?family=Roboto:400,300,500,500italic,700,900,400italic,700italic)'));
287 test.notEqual(-1, css.indexOf('url(https://themes.googleusercontent.com/static/fonts/opensans/v8/MTP_ySUJH_bn48VBG8sNSnhCUOGz7vYGh680lGh-uXM.woff)'));
288 test.ok(css.indexOf('url(data:image/png;base64') > 0);
289
290 next();
291 }]
292 });
293};
294
295exports.testUploadImagesToCdn = function(test) {
296
297 test.expect(2);
298
299 th.runTest(test, {
300 uploadImagesToCdn: [function(next) {
301 assetProcessor.uploadImagesToCdn(next);
302 }],
303 download: ['uploadImagesToCdn', function(next, results) {
304 const sampleImageUrl = results.uploadImagesToCdn+'/img_directory_2/purty_wood.png';
305 request(sampleImageUrl, function(err, response, body) {
306 next(err, !err && response.statusCode === 200 && (''+body).length);
307 });
308 }],
309 assertResult: ['download', function(next, results) {
310
311 test.ok(results.uploadImagesToCdn);
312 test.ok(results.download > 10000);
313
314 next();
315 }]
316 });
317
318};
319
320exports.testUploadExtrasToCdn = function(test) {
321
322 test.expect(3);
323
324 th.runTest(test, {
325 uploadExtrasToCdn: [function(next) {
326 assetProcessor.uploadExtrasToCdn(next);
327 }],
328 download: ['uploadExtrasToCdn', function(next, results) {
329 const sampleImageUrl = results.uploadExtrasToCdn+'/fonts/FontAwesome.otf';
330 request(sampleImageUrl, function(err, response, body) {
331 next(err, !err && response.statusCode === 200 && (''+body).length);
332 });
333 }],
334 assertResult: ['download', function(next, results) {
335
336 test.ok(results.uploadExtrasToCdn);
337 test.ok(results.uploadExtrasToCdn && results.uploadExtrasToCdn.toLowerCase().indexOf('s3') >= 0);
338 test.ok(results.download > 10000);
339
340 next();
341 }]
342 });
343
344};
345
346function _downloadAndUncompress(url, callback) {
347 const stream = request(url).pipe(zlib.createGunzip());
348 let uncompressed = '';
349
350 stream.on('data', function(data) {
351 uncompressed += data;
352 });
353
354 stream.on('error', function(err) {
355 callback(err);
356 });
357
358 stream.on('end', function() {
359 callback(null, uncompressed);
360 });
361}
362
363exports.testEnsureAssets = function(test) {
364
365 test.expect(8);
366
367 th.runTest(test, {
368 ensureAssets: [function(next) {
369 assetProcessor.ensureAssets(next);
370 }],
371 assertResult: ['ensureAssets', function(next, results) {
372 const ensureResult = results.ensureAssets || {};
373
374 test.notEqual('undefined', typeof ensureResult.jsUrl);
375 test.notEqual('undefined', typeof ensureResult.jsChanged);
376 test.notEqual('undefined', typeof ensureResult.cssUrl);
377 test.notEqual('undefined', typeof ensureResult.cssChanged);
378 test.notEqual('undefined', typeof ensureResult.imagesUrl);
379 test.notEqual('undefined', typeof ensureResult.imagesChanged);
380 test.notEqual('undefined', typeof ensureResult.extrasUrl);
381 test.notEqual('undefined', typeof ensureResult.extrasChanged);
382
383 next();
384 }]
385 });
386
387};
388
389exports.testTargetFiles = function(test) {
390
391 // we will use a different configuration than other tests
392 const otherAssetProcessor = _assetProcessorForTestConfig('test-config-2.json');
393
394 test.expect(4);
395
396 th.runTest(test, {
397 getJavaScriptFiles: [function(next) {
398 otherAssetProcessor.getJavaScriptFiles(next);
399 }],
400 getCssFiles: [function(next) {
401 otherAssetProcessor.getCssFiles(next);
402 }],
403 getImageFiles: [function(next) {
404 otherAssetProcessor.getImageFiles(next);
405 }],
406 getExtraFiles: [function(next) {
407 otherAssetProcessor.getExtraFiles(next);
408 }],
409 assertResults: ['getJavaScriptFiles', 'getCssFiles', 'getImageFiles', 'getExtraFiles', function(next, results) {
410
411 test.deepEqual(['test/test-files/js/js_directory_1/one.js', 'test/test-files/js/js_directory_2/two.js', 'test/test-files/js/js_directory_1/three.js'], results.getJavaScriptFiles);
412 test.deepEqual(['test/test-files/css/css_directory_1/one.css', 'test/test-files/css/css_directory_2/two.css', 'test/test-files/css/css_directory_1/three.css', 'test/test-files/mixed/mixed_directory_1/four.css'], results.getCssFiles);
413 test.deepEqual(['test/test-files/img/slash_it.png'], results.getImageFiles);
414 test.deepEqual(['test/test-files/extra/swf/copy_csv_xls.swf', 'test/test-files/extra/fonts/FontAwesome.otf'], results.getExtraFiles);
415
416 next();
417 }]
418 })
419
420};
421
422exports.testUploadExtrasToCdnCloudfront = function(test) {
423
424 // we will use a different configuration than other tests
425 const otherAssetProcessor = _assetProcessorForTestConfig('test-config-3.json');
426
427 test.expect(2);
428
429 th.runTest(test, {
430 uploadExtrasToCdn: [function(next) {
431 otherAssetProcessor.uploadExtrasToCdn(next);
432 }],
433 assertResult: ['uploadExtrasToCdn', function(next, results) {
434 test.ok(results.uploadExtrasToCdn && results.uploadExtrasToCdn.indexOf('cloudfront') >= 0);
435 test.ok(results.uploadExtrasToCdn && results.uploadExtrasToCdn.indexOf('dummyCfMapping') >= 0);
436 next();
437 }]
438 });
439
440};
441
442exports.testUseCloudFrontWithoutS3 = function(test) {
443
444 // we will use a different configuration than other tests
445 const configPath = path.resolve(__dirname, 'test-files', 'test-config-5.json');
446 const config = fs.readJsonFileSync(configPath);
447 config.root = path.normalize(path.resolve(path.dirname(configPath), config.root || '.'));
448 const otherAssetProcessor = new AssetProcessor(config);
449
450 test.expect(1);
451
452 th.runTest(test, {
453 processAssets: [function(next) {
454 otherAssetProcessor.processAssets(next);
455 }],
456 assertResult: ['processAssets', function(next, results) {
457
458 const cssFilePath = path.resolve(__dirname, 'test-files', 'css', results.processAssets.cssUrl.split('/').pop());
459 const cssFile = fs.readFileSync(cssFilePath).toString();
460
461 // Test url rebase
462 test.ok(cssFile.indexOf('/img') >= 0);
463 next();
464 }]
465 });
466
467};
468
469
470// NOTE: The files this is trying to pull from github do not exist anymore
471// exports.testImportLatestStylesheets = function(test) {
472//
473// // we will use a different configuration than other tests
474// const otherAssetProcessor = _assetProcessorForTestConfig('test-config-4.json');
475// const importDir = path.resolve(__dirname, 'test-files', 'import');
476//
477// const expectedFile1 = path.resolve(importDir, 'audioblocks-style-custom.css');
478// const expectedFile2 = path.resolve(importDir, 'global-style-custom.css');
479//
480// if (!credentials.git) {
481// console.warn('With no git credentials this test will fail');
482// }
483//
484// // clean up any previous tests
485// if (fs.existsSync(importDir)) {
486// fs.removeSync(importDir);
487// }
488// // simulate existing files
489// fs.mkdirpSync(importDir);
490// fs.writeFileSync(expectedFile1, 'existing file1');
491// fs.writeFileSync(expectedFile2, 'existing file2');
492//
493// test.expect(4);
494//
495// th.runTest(test, {
496// uploadExtrasToCdn: [function(next) {
497// otherAssetProcessor.importLatestStylesheets(next);
498// }],
499// assertResult: ['uploadExtrasToCdn', function(next) {
500// const css1 = fs.existsSync(expectedFile1) && fs.readFileSync(expectedFile1, 'utf8');
501// const css2 = fs.existsSync(expectedFile2) && fs.readFileSync(expectedFile2, 'utf8');
502//
503// test.ok(css1 && css1.length > 1000);
504// test.ok(css1 && css1.indexOf('mapped/path/to/images/home/home-notes-dark-blue.png') > 0);
505// test.ok(css2 && css2.length > 200);
506// test.ok(css2 && css2.indexOf('another/mapping/for/fonts/fontawesome-webfont.eot') > 0);
507//
508// next();
509// }]
510// });
511//
512// };
513
514/**
515 * Helper function that creates an AssetProcessor for the given config file
516 * @param configFile name of test config file to use
517 * @returns {AssetProcessor}
518 * @private
519 */
520function _assetProcessorForTestConfig(configFile) {
521 // we will use a different configuration than other tests
522 const configPath = path.resolve(__dirname, 'test-files', configFile);
523 const config = fs.readJsonFileSync(configPath);
524
525 // mix in secret credentials to hard coded configs
526 config.s3 = _.extend(config.s3 || {}, credentials.s3);
527 config.git = _.extend(config.git || {}, credentials.git);
528
529 config.root = path.normalize(path.resolve(path.dirname(configPath), config.root || '.'));
530
531 return new AssetProcessor(config);
532}