source: trip-planner-front/node_modules/less/test/less-test.js@ 84d0fbb

Last change on this file since 84d0fbb was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 20.3 KB
Line 
1/* jshint latedef: nofunc */
2
3var logger = require('../lib/less/logger').default;
4
5var isVerbose = process.env.npm_config_loglevel !== 'concise';
6logger.addListener({
7 info(msg) {
8 if (isVerbose) {
9 process.stdout.write(msg + '\n');
10 }
11 },
12 warn(msg) {
13 process.stdout.write(msg + '\n');
14 },
15 erro(msg) {
16 process.stdout.write(msg + '\n');
17 }
18});
19
20
21module.exports = function() {
22 var path = require('path'),
23 fs = require('fs'),
24 clone = require('copy-anything').copy;
25
26 var less = require('../');
27
28 var stylize = require('../lib/less-node/lessc-helper').stylize;
29
30 var globals = Object.keys(global);
31
32 var oneTestOnly = process.argv[2],
33 isFinished = false;
34
35 var testFolder = path.dirname(require.resolve('@less/test-data'));
36 var lessFolder = path.join(testFolder, 'less');
37
38 // Define String.prototype.endsWith if it doesn't exist (in older versions of node)
39 // This is required by the testSourceMap function below
40 if (typeof String.prototype.endsWith !== 'function') {
41 String.prototype.endsWith = function (str) {
42 return this.slice(-str.length) === str;
43 }
44 }
45
46 var queueList = [],
47 queueRunning = false;
48 function queue(func) {
49 if (queueRunning) {
50 // console.log("adding to queue");
51 queueList.push(func);
52 } else {
53 // console.log("first in queue - starting");
54 queueRunning = true;
55 func();
56 }
57 }
58 function release() {
59 if (queueList.length) {
60 // console.log("running next in queue");
61 var func = queueList.shift();
62 setTimeout(func, 0);
63 } else {
64 // console.log("stopping queue");
65 queueRunning = false;
66 }
67 }
68
69 var totalTests = 0,
70 failedTests = 0,
71 passedTests = 0,
72 finishTimer = setInterval(endTest, 500);
73
74 less.functions.functionRegistry.addMultiple({
75 add: function (a, b) {
76 return new(less.tree.Dimension)(a.value + b.value);
77 },
78 increment: function (a) {
79 return new(less.tree.Dimension)(a.value + 1);
80 },
81 _color: function (str) {
82 if (str.value === 'evil red') { return new(less.tree.Color)('600'); }
83 }
84 });
85
86 function testSourcemap(name, err, compiledLess, doReplacements, sourcemap, baseFolder) {
87 if (err) {
88 fail('ERROR: ' + (err && err.message));
89 return;
90 }
91 // Check the sourceMappingURL at the bottom of the file
92 var expectedSourceMapURL = name + '.css.map',
93 sourceMappingPrefix = '/*# sourceMappingURL=',
94 sourceMappingSuffix = ' */',
95 expectedCSSAppendage = sourceMappingPrefix + expectedSourceMapURL + sourceMappingSuffix;
96 if (!compiledLess.endsWith(expectedCSSAppendage)) {
97 // To display a better error message, we need to figure out what the actual sourceMappingURL value was, if it was even present
98 var indexOfSourceMappingPrefix = compiledLess.indexOf(sourceMappingPrefix);
99 if (indexOfSourceMappingPrefix === -1) {
100 fail('ERROR: sourceMappingURL was not found in ' + baseFolder + '/' + name + '.css.');
101 return;
102 }
103
104 var startOfSourceMappingValue = indexOfSourceMappingPrefix + sourceMappingPrefix.length,
105 indexOfNextSpace = compiledLess.indexOf(' ', startOfSourceMappingValue),
106 actualSourceMapURL = compiledLess.substring(startOfSourceMappingValue, indexOfNextSpace === -1 ? compiledLess.length : indexOfNextSpace);
107 fail('ERROR: sourceMappingURL should be "' + expectedSourceMapURL + '" but is "' + actualSourceMapURL + '".');
108 }
109
110 fs.readFile(path.join('test/', name) + '.json', 'utf8', function (e, expectedSourcemap) {
111 process.stdout.write('- ' + path.join(baseFolder, name) + ': ');
112 if (sourcemap === expectedSourcemap) {
113 ok('OK');
114 } else if (err) {
115 fail('ERROR: ' + (err && err.message));
116 if (isVerbose) {
117 process.stdout.write('\n');
118 process.stdout.write(err.stack + '\n');
119 }
120 } else {
121 difference('FAIL', expectedSourcemap, sourcemap);
122 }
123 });
124 }
125
126 function testSourcemapWithoutUrlAnnotation(name, err, compiledLess, doReplacements, sourcemap, baseFolder) {
127 if (err) {
128 fail('ERROR: ' + (err && err.message));
129 return;
130 }
131 // This matches with strings that end($) with source mapping url annotation.
132 var sourceMapRegExp = /\/\*# sourceMappingURL=.+\.css\.map \*\/$/;
133 if (sourceMapRegExp.test(compiledLess)) {
134 fail('ERROR: sourceMappingURL found in ' + baseFolder + '/' + name + '.css.');
135 return;
136 }
137
138 // Even if annotation is not necessary, the map file should be there.
139 fs.readFile(path.join('test/', name) + '.json', 'utf8', function (e, expectedSourcemap) {
140 process.stdout.write('- ' + path.join(baseFolder, name) + ': ');
141 if (sourcemap === expectedSourcemap) {
142 ok('OK');
143 } else if (err) {
144 fail('ERROR: ' + (err && err.message));
145 if (isVerbose) {
146 process.stdout.write('\n');
147 process.stdout.write(err.stack + '\n');
148 }
149 } else {
150 difference('FAIL', expectedSourcemap, sourcemap);
151 }
152 });
153 }
154
155 function testEmptySourcemap(name, err, compiledLess, doReplacements, sourcemap, baseFolder) {
156 process.stdout.write('- ' + path.join(baseFolder, name) + ': ');
157 if (err) {
158 fail('ERROR: ' + (err && err.message));
159 } else {
160 var expectedSourcemap = undefined;
161 if ( compiledLess !== '' ) {
162 difference('\nCompiledLess must be empty', '', compiledLess);
163
164 } else if (sourcemap !== expectedSourcemap) {
165 fail('Sourcemap must be undefined');
166 } else {
167 ok('OK');
168 }
169 }
170 }
171
172 function testImports(name, err, compiledLess, doReplacements, sourcemap, baseFolder, imports) {
173 if (err) {
174 fail('ERROR: ' + (err && err.message));
175 return;
176 }
177
178 function stringify(str) {
179 return JSON.stringify(imports, null, ' ')
180 }
181
182 /** Imports are not sorted */
183 const importsString = stringify(imports.sort())
184
185 fs.readFile(path.join(lessFolder, name) + '.json', 'utf8', function (e, expectedImports) {
186 if (e) {
187 fail('ERROR: ' + (e && e.message));
188 return;
189 }
190 process.stdout.write('- ' + path.join(baseFolder, name) + ': ');
191 expectedImports = stringify(JSON.parse(expectedImports).sort());
192 expectedImports = globalReplacements(expectedImports, baseFolder);
193
194 if (expectedImports === importsString) {
195 ok('OK');
196 } else if (err) {
197 fail('ERROR: ' + (err && err.message));
198 if (isVerbose) {
199 process.stdout.write('\n');
200 process.stdout.write(err.stack + '\n');
201 }
202 } else {
203 difference('FAIL', expectedImports, importsString);
204 }
205 });
206 }
207
208 function testErrors(name, err, compiledLess, doReplacements, sourcemap, baseFolder) {
209 fs.readFile(path.join(baseFolder, name) + '.txt', 'utf8', function (e, expectedErr) {
210 process.stdout.write('- ' + path.join(baseFolder, name) + ': ');
211 expectedErr = doReplacements(expectedErr, baseFolder, err && err.filename);
212 if (!err) {
213 if (compiledLess) {
214 fail('No Error', 'red');
215 } else {
216 fail('No Error, No Output');
217 }
218 } else {
219 var errMessage = err.toString();
220 if (errMessage === expectedErr) {
221 ok('OK');
222 } else {
223 difference('FAIL', expectedErr, errMessage);
224 }
225 }
226 });
227 }
228
229 // https://github.com/less/less.js/issues/3112
230 function testJSImport() {
231 process.stdout.write('- Testing root function registry');
232 less.functions.functionRegistry.add('ext', function() {
233 return new less.tree.Anonymous('file');
234 });
235 var expected = '@charset "utf-8";\n';
236 toCSS({}, path.join(lessFolder, 'root-registry', 'root.less'), function(error, output) {
237 if (error) {
238 return fail('ERROR: ' + error);
239 }
240 if (output.css === expected) {
241 return ok('OK');
242 }
243 difference('FAIL', expected, output.css);
244 });
245 }
246
247 function globalReplacements(input, directory, filename) {
248 var path = require('path');
249 var p = filename ? path.join(path.dirname(filename), '/') : directory,
250 pathimport = path.join(directory + 'import/'),
251 pathesc = p.replace(/[.:/\\]/g, function(a) { return '\\' + (a == '\\' ? '\/' : a); }),
252 pathimportesc = pathimport.replace(/[.:/\\]/g, function(a) { return '\\' + (a == '\\' ? '\/' : a); });
253
254 return input.replace(/\{path\}/g, p)
255 .replace(/\{node\}/g, '')
256 .replace(/\{\/node\}/g, '')
257 .replace(/\{pathhref\}/g, '')
258 .replace(/\{404status\}/g, '')
259 .replace(/\{nodepath\}/g, path.join(process.cwd(), 'node_modules', '/'))
260 .replace(/\{pathrel\}/g, path.join(path.relative(lessFolder, p), '/'))
261 .replace(/\{pathesc\}/g, pathesc)
262 .replace(/\{pathimport\}/g, pathimport)
263 .replace(/\{pathimportesc\}/g, pathimportesc)
264 .replace(/\r\n/g, '\n');
265 }
266
267 function checkGlobalLeaks() {
268 return Object.keys(global).filter(function(v) {
269 return globals.indexOf(v) < 0;
270 });
271 }
272
273 function testSyncronous(options, filenameNoExtension) {
274 if (oneTestOnly && ('Test Sync ' + filenameNoExtension) !== oneTestOnly) {
275 return;
276 }
277 totalTests++;
278 queue(function() {
279 var isSync = true;
280 toCSS(options, path.join(lessFolder, filenameNoExtension + '.less'), function (err, result) {
281 process.stdout.write('- Test Sync ' + filenameNoExtension + ': ');
282
283 if (isSync) {
284 ok('OK');
285 } else {
286 fail('Not Sync');
287 }
288 release();
289 });
290 isSync = false;
291 });
292 }
293
294 function runTestSet(options, foldername, verifyFunction, nameModifier, doReplacements, getFilename) {
295 options = options ? clone(options) : {};
296 runTestSetInternal(lessFolder, options, foldername, verifyFunction, nameModifier, doReplacements, getFilename);
297 }
298
299 function runTestSetNormalOnly(options, foldername, verifyFunction, nameModifier, doReplacements, getFilename) {
300 runTestSetInternal(lessFolder, options, foldername, verifyFunction, nameModifier, doReplacements, getFilename);
301 }
302
303 function runTestSetInternal(baseFolder, opts, foldername, verifyFunction, nameModifier, doReplacements, getFilename) {
304 foldername = foldername || '';
305
306 var originalOptions = opts || {};
307
308 if (!doReplacements) {
309 doReplacements = globalReplacements;
310 }
311
312 function getBasename(file) {
313 return foldername + path.basename(file, '.less');
314 }
315
316 fs.readdirSync(path.join(baseFolder, foldername)).forEach(function (file) {
317 if (!/\.less$/.test(file)) { return; }
318
319 var options = clone(originalOptions);
320
321 var name = getBasename(file);
322
323 if (oneTestOnly && name !== oneTestOnly) {
324 return;
325 }
326
327 totalTests++;
328
329 if (options.sourceMap && !options.sourceMap.sourceMapFileInline) {
330 options.sourceMap = {
331 sourceMapOutputFilename: name + '.css',
332 sourceMapBasepath: baseFolder,
333 sourceMapRootpath: 'testweb/',
334 disableSourcemapAnnotation: options.sourceMap.disableSourcemapAnnotation
335 };
336 // This options is normally set by the bin/lessc script. Setting it causes the sourceMappingURL comment to be appended to the CSS
337 // output. The value is designed to allow the sourceMapBasepath option to be tested, as it should be removed by less before
338 // setting the sourceMappingURL value, leaving just the sourceMapOutputFilename and .map extension.
339 options.sourceMap.sourceMapFilename = options.sourceMap.sourceMapBasepath + '/' + options.sourceMap.sourceMapOutputFilename + '.map';
340 }
341
342 options.getVars = function(file) {
343 try {
344 return JSON.parse(fs.readFileSync(getFilename(getBasename(file), 'vars', baseFolder), 'utf8'));
345 }
346 catch (e) {
347 return {};
348 }
349 };
350
351 var doubleCallCheck = false;
352 queue(function() {
353 toCSS(options, path.join(baseFolder, foldername + file), function (err, result) {
354
355 if (doubleCallCheck) {
356 totalTests++;
357 fail('less is calling back twice');
358 process.stdout.write(doubleCallCheck + '\n');
359 process.stdout.write((new Error()).stack + '\n');
360 return;
361 }
362 doubleCallCheck = (new Error()).stack;
363
364 /**
365 * @todo - refactor so the result object is sent to the verify function
366 */
367 if (verifyFunction) {
368 var verificationResult = verifyFunction(
369 name, err, result && result.css, doReplacements, result && result.map, baseFolder, result && result.imports
370 );
371 release();
372 return verificationResult;
373 }
374
375 if (err) {
376 fail('ERROR: ' + (err && err.message));
377 if (isVerbose) {
378 process.stdout.write('\n');
379 if (err.stack) {
380 process.stdout.write(err.stack + '\n');
381 } else {
382 // this sometimes happen - show the whole error object
383 console.log(err);
384 }
385 }
386 release();
387 return;
388 }
389 var css_name = name;
390 if (nameModifier) { css_name = nameModifier(name); }
391
392 fs.readFile(path.join(testFolder, 'css', css_name) + '.css', 'utf8', function (e, css) {
393 process.stdout.write('- ' + path.join(baseFolder, css_name) + ': ');
394
395 css = css && doReplacements(css, path.join(baseFolder, foldername));
396 if (result.css === css) { ok('OK'); }
397 else {
398 difference('FAIL', css, result.css);
399 }
400 release();
401 });
402 });
403 });
404 });
405 }
406
407 function diff(left, right) {
408 require('diff').diffLines(left, right).forEach(function(item) {
409 if (item.added || item.removed) {
410 var text = item.value && item.value.replace('\n', String.fromCharCode(182) + '\n').replace('\ufeff', '[[BOM]]');
411 process.stdout.write(stylize(text, item.added ? 'green' : 'red'));
412 } else {
413 process.stdout.write(item.value && item.value.replace('\ufeff', '[[BOM]]'));
414 }
415 });
416 process.stdout.write('\n');
417 }
418
419 function fail(msg) {
420 process.stdout.write(stylize(msg, 'red') + '\n');
421 failedTests++;
422 endTest();
423 }
424
425 function difference(msg, left, right) {
426 process.stdout.write(stylize(msg, 'yellow') + '\n');
427 failedTests++;
428
429 diff(left || '', right || '');
430 endTest();
431 }
432
433 function ok(msg) {
434 process.stdout.write(stylize(msg, 'green') + '\n');
435 passedTests++;
436 endTest();
437 }
438
439 function finished() {
440 isFinished = true;
441 endTest();
442 }
443
444 function endTest() {
445 if (isFinished && ((failedTests + passedTests) >= totalTests)) {
446 clearInterval(finishTimer);
447 var leaked = checkGlobalLeaks();
448 process.stdout.write('\n');
449 if (failedTests > 0) {
450 process.stdout.write(failedTests + stylize(' Failed', 'red') + ', ' + passedTests + ' passed\n');
451 } else {
452 process.stdout.write(stylize('All Passed ', 'green') + passedTests + ' run\n');
453 }
454 if (leaked.length > 0) {
455 process.stdout.write('\n');
456 process.stdout.write(stylize('Global leak detected: ', 'red') + leaked.join(', ') + '\n');
457 }
458
459 if (leaked.length || failedTests) {
460 process.on('exit', function() { process.reallyExit(1); });
461 }
462 }
463 }
464
465 function contains(fullArray, obj) {
466 for (var i = 0; i < fullArray.length; i++) {
467 if (fullArray[i] === obj) {
468 return true;
469 }
470 }
471 return false;
472 }
473
474 /**
475 *
476 * @param {Object} options
477 * @param {string} filePath
478 * @param {Function} callback
479 */
480 function toCSS(options, filePath, callback) {
481 options = options || {};
482 var str = fs.readFileSync(filePath, 'utf8'), addPath = path.dirname(filePath);
483 if (typeof options.paths !== 'string') {
484 options.paths = options.paths || [];
485 if (!contains(options.paths, addPath)) {
486 options.paths.push(addPath);
487 }
488 } else {
489 options.paths = [options.paths]
490 }
491 options.paths = options.paths.map(searchPath => {
492 return path.resolve(lessFolder, searchPath)
493 })
494 options.filename = path.resolve(process.cwd(), filePath);
495 options.optimization = options.optimization || 0;
496
497 if (options.globalVars) {
498 options.globalVars = options.getVars(filePath);
499 } else if (options.modifyVars) {
500 options.modifyVars = options.getVars(filePath);
501 }
502 if (options.plugin) {
503 var Plugin = require(path.resolve(process.cwd(), options.plugin));
504 options.plugins = [Plugin];
505 }
506 less.render(str, options, callback);
507 }
508
509 function testNoOptions() {
510 if (oneTestOnly && 'Integration' !== oneTestOnly) {
511 return;
512 }
513 totalTests++;
514 try {
515 process.stdout.write('- Integration - creating parser without options: ');
516 less.render('');
517 } catch (e) {
518 fail(stylize('FAIL\n', 'red'));
519 return;
520 }
521 ok(stylize('OK\n', 'green'));
522 }
523
524 function testImportRedirect(nockScope) {
525 return (name, err, css, doReplacements, sourcemap, baseFolder) => {
526 process.stdout.write('- ' + path.join(baseFolder, name) + ': ');
527 if (err) {
528 fail('FAIL: ' + (err && err.message));
529 return;
530 }
531 const expected = 'h1 {\n color: red;\n}\n';
532 if (css !== expected) {
533 difference('FAIL', expected, css);
534 return;
535 }
536 nockScope.done();
537 ok('OK');
538 };
539 }
540
541 return {
542 runTestSet: runTestSet,
543 runTestSetNormalOnly: runTestSetNormalOnly,
544 testSyncronous: testSyncronous,
545 testErrors: testErrors,
546 testSourcemap: testSourcemap,
547 testSourcemapWithoutUrlAnnotation: testSourcemapWithoutUrlAnnotation,
548 testImports: testImports,
549 testImportRedirect: testImportRedirect,
550 testEmptySourcemap: testEmptySourcemap,
551 testNoOptions: testNoOptions,
552 testJSImport: testJSImport,
553 finished: finished
554 };
555};
Note: See TracBrowser for help on using the repository browser.