UNPKG

24.7 kBJavaScriptView Raw
1/* jshint quotmark: false */
2'use strict';
3
4var Q = require('q'),
5 SINON = require('sinon'),
6 requireMocked = require('require-mocked'),
7 assert = require('chai').assert,
8 mockFs = require('q-io/fs-mock'),
9 BEM = require('..'),
10 U = BEM.require('./util'),
11 PATH = BEM.require('./path'),
12 TECH = BEM.require('./tech'),
13 Level = BEM.require('./level').Level,
14 createTech = TECH.createTech,
15 getTechClass = TECH.getTechClass;
16// Turn off deprecation warnings
17U.deprecate.silence = true;
18
19/**
20 * Mocha BDD interface.
21 *
22 * @name describe @function
23 * @name it @function
24 * @name before @function
25 * @name after @function
26 * @name beforeEach @function
27 * @name afterEach @function
28 */
29
30describe('tech', function() {
31
32 describe('getTechClass()', function() {
33
34 var testTech = require.resolve(PATH.resolve(__dirname, 'data/techs/test-tech.js'));
35 var testTechV2 = require.resolve(PATH.resolve(__dirname, 'data/techs/test-tech-v2.js'));
36
37 /**
38 * Creates level that always resolves to specified path
39 */
40 function mockLevel(techPath) {
41 return {
42 resolveTech: function() {
43 return techPath;
44 }
45 };
46 }
47
48 it('for path', function() {
49
50 // tech class
51 var T = getTechClass(testTech),
52
53 // tech object
54 o = new T('tech', 'tech');
55
56 assert.isTrue(o.test);
57
58 });
59
60 it('for module object', function() {
61
62 // tech class
63 var T = getTechClass({ test: true }),
64
65 // tech object
66 o = new T('tech', 'tech');
67
68 assert.isTrue(o.test);
69
70 });
71
72 it('for module with baseTechPath property', function() {
73
74 // tech class
75 var T = getTechClass({
76 baseTechPath: testTech,
77 test2: true
78 }),
79
80 // tech object
81 o = new T('tech', 'tech');
82
83 assert.isTrue(o.test);
84 assert.isTrue(o.test2);
85
86 });
87
88 it('for module with baseTechName property', function() {
89 // tech class
90 var T = getTechClass({
91 baseTechName: 'base',
92 test2: true
93 }, mockLevel(testTech)),
94
95 // tech object
96 o = new T('tech', 'tech');
97
98 assert.isTrue(o.test);
99 assert.isTrue(o.test2);
100
101 });
102
103 it('for module with baseTech property', function() {
104
105 // tech class
106 var T = getTechClass({
107 baseTech: getTechClass({ test: true }),
108 test2: true
109 }),
110
111 // tech object
112 o = new T('tech', 'tech');
113
114 assert.isTrue(o.test);
115 assert.isTrue(o.test2);
116
117 });
118
119 it('for module with techMixin property', function() {
120
121 var T = getTechClass({
122 baseTech: getTechClass({ test: true }),
123 techMixin: {
124 test2: true
125 }
126 }),
127 o = new T('tech', 'tech');
128
129 assert.isTrue(o.test);
130 assert.isTrue(o.test2);
131
132 });
133
134 it('for module with function-style code', function() {
135 var T = getTechClass(require.resolve('./data/techs/function-tech.js'));
136 T = new T('function-tech', 'function-tech');
137 assert.equal(T.getBuildResults(), BEM.require.resolve('./techs/js.js'));
138 });
139
140 it('throws an error when baseTechName is unresolvable', function() {
141 assert.throws(function() {
142 var level = new Level('', '');
143 getTechClass({
144 baseTechName: 'nonexistent'
145 }, level);
146 });
147 });
148
149 describe('when API_VER is specified', function() {
150 describe('without base tech', function() {
151 it('loads TechV2 when API_VER=2', function() {
152 var T = getTechClass({
153 API_VER: 2
154 });
155
156 assert.instanceOf(new T(), TECH.TechV2);
157 });
158
159 it('loads TechV1 when API_VER=1', function() {
160 var T = getTechClass({
161 API_VER: 1
162 });
163
164 assert.instanceOf(new T(), TECH.Tech);
165 });
166
167 });
168
169 describe('with base tech', function() {
170 it('disallows to inherit V2 tech from V1', function() {
171 assert.throws(function() {
172 getTechClass({
173 baseTechName: 'base',
174 API_VER: 2
175 }, mockLevel(testTech));
176 });
177 });
178
179 it('disallows to inherit V1 tech from V2', function() {
180 assert.throws(function() {
181 getTechClass({
182 baseTechName: 'base',
183 API_VER: 1
184 }, mockLevel(testTechV2));
185 });
186 });
187
188 it('allows to inherit V1 tech from V1', function() {
189 assert.doesNotThrow(function() {
190 getTechClass({
191 baseTechName: 'base',
192 API_VER: 1
193 }, mockLevel(testTech));
194 });
195 });
196
197 it('allows to inherit V2 tech from V2', function() {
198 assert.doesNotThrow(function() {
199 getTechClass({
200 baseTechName: 'base',
201 API_VER: 2
202 }, mockLevel(testTechV2));
203 });
204 });
205
206 it('loads base tech from lib/techs/ by default when API_VER is 1', function() {
207 var level = new Level('', '');
208 var T = getTechClass({
209 baseTechName: 'js',
210 API_VER: 1
211 }, level);
212 assert.instanceOf(new T(), TECH.Tech);
213 });
214
215 it('loads base tech from lib/techs/v2 by default when API_VER is 2', function() {
216 var level = new Level('', '');
217 var T = getTechClass({
218 baseTechName: 'js',
219 API_VER: 2
220 }, level);
221 assert.instanceOf(new T(), TECH.TechV2);
222 });
223 });
224 });
225
226 describe('when API_VER is not specified', function() {
227 describe('without base tech', function() {
228 it('loads V1 tech', function() {
229 var T = getTechClass({});
230 assert.instanceOf(new T(), TECH.Tech);
231 });
232 });
233
234 describe('with base tech', function() {
235 it('allows to inherit from V1 tech', function() {
236 assert.doesNotThrow(function() {
237 getTechClass({
238 baseTechName: 'base'
239 }, mockLevel(testTech));
240 });
241 });
242
243
244 it('allows to inherit from V2 tech', function() {
245 assert.doesNotThrow(function() {
246 getTechClass({
247 baseTechName: 'base'
248 }, mockLevel(testTechV2));
249 });
250 });
251
252 it('loads base tech from lib/techs by default', function() {
253 var level = new Level('', '');
254 var T = getTechClass({
255 baseTechName: 'js',
256 }, level);
257 assert.instanceOf(new T(), TECH.Tech);
258 });
259 });
260 });
261 });
262
263 describe('getBuildResult', function() {
264 it('calls getBuildResultChunk with source suffix', function() {
265 var TechClass = getTechClass({
266 API_VER: 2
267 });
268
269 var tech = new TechClass();
270 tech.getBuildResultChunk = SINON.spy();
271 tech.getBuildResult([
272 {absPath: '/source.source_suffix', suffix: 'source_suffix'}
273 ], 'dest_suffix', '/out', {});
274
275 SINON.assert.calledWith(tech.getBuildResultChunk,
276 SINON.match.any,
277 SINON.match.any,
278 "source_suffix");
279 });
280 });
281
282 describe('v2', function() {
283
284 function createTechObj(decl) {
285 decl = decl || {};
286 decl.API_VER = 2;
287 var TechClass = getTechClass(decl);
288 return new TechClass();
289 }
290
291 describe('getCreateResults()', function() {
292
293 var tech;
294 beforeEach(function() {
295 tech = createTechObj({
296 API_VER: 2,
297 getCreateSuffixes: function() {
298 return ['js', 'css'];
299 },
300 getCreateResult: function (path, suffix, vars) {
301 return Q.resolve(suffix + ' content');
302 }
303 });
304 });
305
306 it('should return one value for each suffix', function() {
307 var result = tech.getCreateResults('/tmp', {});
308
309 return assert.isFulfilled(Q.all([
310 assert.eventually.property(result, 'css'),
311 assert.eventually.property(result, 'js'),
312 ]));
313 });
314
315 it('should return result of getCreateResult for each key', function() {
316 var result= tech.getCreateResults('/tmp', {});
317 return assert.isFulfilled(Q.all([
318 assert.eventually.propertyVal(result, 'css', 'css content'),
319 assert.eventually.propertyVal(result, 'js', 'js content')
320 ]));
321 });
322
323 });
324
325 describe('getBuildResult()', function () {
326 var tech;
327 beforeEach(function() {
328 tech = createTechObj({
329 API_VER: 2,
330
331 getBuildResultChunk: function(relPath, path, suffix) {
332 return 'relPath: ' + relPath + ' ' +
333 'path: ' + path + ' ' +
334 'suffix: ' + suffix;
335 }
336 });
337 });
338
339 it('should return chunk for each file', function() {
340 var result = tech.getBuildResult([
341 {absPath: '/test/1.js', suffix: 'js'},
342 {absPath: '/test/2.css', suffix: 'css'}
343 ], 'out.js', '/test/result/out.js');
344
345
346 return assert.eventually.deepEqual(result, [
347 'relPath: ../1.js path: /test/1.js suffix: js',
348 'relPath: ../2.css path: /test/2.css suffix: css'
349 ]);
350 });
351 });
352
353 function createMockedTech(fs) {
354 var path = (process.env.COVER? '../lib-cov/' : '../lib/') + 'tech/index.js';
355
356 var MOCKTECH = requireMocked(require.resolve(path), {
357 mocks: {
358 'q-io/fs': mockFs(fs)
359 }
360 });
361
362
363 var TechClass = MOCKTECH.getTechClass({API_VER: 2});
364 var tech = new TechClass('techName', '/some/path');
365 tech.setContext({opts:{}});
366 return tech;
367 }
368
369 describe('validate()', function() {
370
371 it('should return false when meta file does not exists', function() {
372 var tech = createMockedTech({
373 'dest': ''
374 });
375
376 return assert.eventually.isFalse(tech.validate('dest', [
377 {absPath: 'source', lastUpdated: 1374796800000}
378 ], {}));
379 });
380
381 it('should return false when amount of source files in cache different from current', function() {
382 var tech = createMockedTech({
383 '.bem': {
384 'cache': {
385 'dest~techName.meta.js': JSON.stringify({
386 buildFiles: [
387 {absPath: 'source1', lastUpdated: 1374796800000},
388 {absPath: 'source2', lastUpdated: 1374796800000}
389 ]
390 })
391 }
392 },
393 'dest': ''
394 });
395
396 return assert.eventually.isFalse(tech.validate('dest', [
397 {absPath: 'source1', lastUpdated: 1374796800000}
398 ], {}));
399 });
400
401 it('should return false when source file changed names', function() {
402 var tech = createMockedTech({
403 '.bem': {
404 'cache': {
405 'dest~techName.meta.js': JSON.stringify({
406 buildFiles: [
407 {absPath: 'oldSource', lastUpdated: 1374710400000}
408 ]
409 })
410 }
411 },
412 'dest': ''
413 });
414
415 return assert.eventually.isFalse(tech.validate('dest', [
416 {absPath: 'newSource', lastUpdated: 1374710400000}
417 ], {}));
418 });
419
420 it('should return false when source file has been updated', function() {
421 var tech = createMockedTech({
422 '.bem': {
423 'cache': {
424 'dest~techName.meta.js': JSON.stringify({
425 buildFiles: [
426 {absPath: 'source', lastUpdated: 1374710400000}
427 ]
428 })
429 }
430 },
431 'dest': ''
432 });
433
434 return assert.eventually.isFalse(tech.validate('dest', [
435 {absPath: 'source', lastUpdated: 1374796800000}
436 ], {}));
437 });
438
439 it('should return false when destination file does not exists', function() {
440 var tech = createMockedTech({
441 '.bem': {
442 'cache': {
443 'dest~techName.meta.js': JSON.stringify({
444 buildFiles: [
445 {absPath: 'source', lastUpdated: 1374710400000}
446 ]
447 })
448 }
449 }
450 });
451
452 return assert.eventually.isFalse(tech.validate('dest', [
453 {absPath: 'source', lastUpdated: 1374710400000}
454 ], {}));
455
456 });
457
458 it('should return true when all previous conditions met', function() {
459 var tech = createMockedTech({
460 '.bem': {
461 'cache': {
462 'dest~techName.meta.js': JSON.stringify({
463 buildFiles: [
464 {absPath: 'source', lastUpdated: 1374710400000}
465 ]
466 })
467 }
468 },
469 'dest': ''
470 });
471
472
473 return assert.eventually.isTrue(tech.validate('dest', [
474 {absPath: 'source', lastUpdated: 1374710400000}
475 ], {}));
476 });
477
478 it('should return false when opts.force is set', function() {
479 var tech = createMockedTech({
480 '.bem': {
481 'cache': {
482 'dest~techName.meta.js': JSON.stringify({
483 buildFiles: [
484 {absPath: 'source', lastUpdated: 1374710400000}
485 ]
486 })
487 }
488 },
489 'dest': ''
490 });
491
492 return assert.eventually.isFalse(tech.validate('dest', [
493 {absPath: 'source', lastUpdated: 1374710400000}
494 ], {force: true}));
495 });
496 });
497
498
499
500 describe('getSuffixes()', function () {
501
502 it('should return each source suffix from build suffixes map', function () {
503 var tech = createTechObj({
504 getBuildSuffixesMap: function () {
505 return {
506 'js': ['js', 'coffee'],
507 'css': ['styl', 'scss']
508 };
509 }
510 });
511
512 //TODO: Update chai and repalce with sameMembers
513 assert.deepEqual(tech.getSuffixes(), ['js', 'coffee', 'styl', 'scss']);
514 });
515
516 it('should have no duplicates', function (){
517 var tech = createTechObj({
518 getBuildSuffixesMap: function () {
519 return {
520 'js': ['js', 'css'],
521 'css': ['js', 'css']
522 };
523 }
524 });
525 assert.deepEqual(tech.getSuffixes(), ['js', 'css']);
526 });
527 });
528
529 describe.skip('matchSuffix()', function () {
530 });
531
532 describe('getPath()', function () {
533
534 it('should return prefix and suffix concatenated', function() {
535 var tech = createTechObj({});
536 assert.equal(tech.getPath('/test/example', 'js'), '/test/example.js');
537 });
538
539 it('should use techName when suffix is not passed', function () {
540 var tech = createTechObj({
541 getTechName: function() {
542 return 'test';
543 }
544 });
545
546 assert.equal(tech.getPath('/test/example'), '/test/example.test');
547 });
548 });
549
550 describe('getPaths()', function () {
551 it('should return path for single suffix and prefix', function() {
552 var tech = createTechObj();
553 assert.deepEqual(tech.getPaths('/test/example', 'js'), ['/test/example.js']);
554 });
555
556 it('should return all possible pathes for arrays of suffixes and prefixes', function () {
557 var paths = createTechObj().getPaths(['/test/example1', '/test/example2'],
558 ['js', 'css']);
559
560 //TODO: replace with sameMembers
561 assert.include(paths, '/test/example1.js');
562 assert.include(paths, '/test/example2.js');
563 assert.include(paths, '/test/example1.css');
564 assert.include(paths, '/test/example2.css');
565
566 });
567
568 it('should use getSuffixes() if suffixes has not been passed', function () {
569 var paths = createTechObj({
570 getSuffixes: function () {
571 return ['html', 'less'];
572 }
573 })
574 .getPaths(['/test/example1', '/test/example2']);
575
576 assert.include(paths, '/test/example1.html');
577 assert.include(paths, '/test/example2.html');
578 assert.include(paths, '/test/example1.less');
579 assert.include(paths, '/test/example2.less');
580 });
581 });
582
583 describe('getTechName()', function () {
584 var tech;
585 beforeEach(function () {
586 tech = createTechObj();
587 });
588
589 it('should return techName if its set', function () {
590 tech.techName = 'SomeTech';
591
592 assert.equal(tech.getTechName(), 'SomeTech');
593 });
594
595 it('should return file name if techName is not set', function () {
596 tech.techPath = '/test/someFile.js';
597
598 assert.equal(tech.getTechName(), 'someFile');
599 });
600 });
601
602 });
603
604});
605
606function testBaseTech(techPath, techAlias) {
607
608 var bemLib = process.env.COVER? 'bem/lib-cov/' : 'bem/lib/',
609 techName = PATH.basename(techPath),
610 absTechPath = require.resolve(PATH.resolve(__dirname, techPath)),
611 relTechPath = techPath + '.js',
612 re = process.env.COVER? /^\.\.\/lib-cov\// : /^\.\.\/lib\//;
613
614 // NOTE: techPath will be always in unix format
615 if(re.test(techPath)) {
616 relTechPath = relTechPath.replace(re, bemLib);
617
618 // default tech identified by '' relative path
619 if(techName === 'tech') relTechPath = '';
620 }
621
622 techAlias = techAlias || techName;
623
624 describe("Tech.createTech('" + techPath + "')", function() {
625
626 var tech = createTech(require.resolve(techPath),
627 techAlias === techName ? null : techAlias);
628
629 // meta data
630 describe(".getTechName()", function() {
631
632 it("equals to '" + techAlias + "'", function() {
633 assert.equal(tech.getTechName(), techAlias);
634 });
635
636 });
637
638 describe(".getSuffixes()", function() {
639
640 var suffixes = tech.getSuffixes();
641
642 it("returns array", function() {
643 assert.instanceOf(suffixes, Array);
644 });
645
646 it("returns all suffixes", function() {
647 assert.deepEqual(suffixes, [techAlias]);
648 });
649
650 });
651
652 describe(".matchSuffix()", function() {
653
654 tech.getSuffixes().forEach(function(suffix) {
655 it("matches '" + suffix + "' and '." + suffix + "'", function() {
656 assert.isTrue(tech.matchSuffix(suffix));
657 assert.isTrue(tech.matchSuffix('.' + suffix));
658 });
659 });
660
661 });
662
663 describe(".getTechPath()", function() {
664
665 it("equals to " + absTechPath, function() {
666 assert.equal(tech.getTechPath(), absTechPath);
667 });
668
669 });
670
671 describe(".getTechRelativePath(" + __dirname + ")", function() {
672
673 var p = PATH.unixToOs(relTechPath);
674 it("equals to " + p, function() {
675 assert.equal(tech.getTechRelativePath(__dirname), p);
676 });
677
678 });
679
680 // create
681 describe(".create()", function() {});
682
683 describe(".getCreateResult()", function() {});
684
685 describe(".getCreateResults()", function() {
686
687 var res = tech.getCreateResults('test', { BlockName: 'b-test' });
688
689 it("contains results for all suffixes", function(done) {
690
691 Q.done(res, function(res) {
692 tech.getSuffixes().forEach(function(suffix) {
693 assert.property(res, suffix);
694 });
695 done();
696 }, done);
697
698 });
699
700 });
701
702 describe(".storeCreateResult()", function() {});
703
704 describe(".storeCreateResults()", function() {});
705
706 describe(".readContent()", function() {});
707
708 describe(".readAllContent()", function() {
709
710 var res = tech.readAllContent('test');
711
712 it("contains results for all suffixes", function(done) {
713
714 Q.done(res, function(res) {
715 tech.getSuffixes().forEach(function(suffix) {
716 assert.property(res, suffix);
717 });
718 done();
719 }, done);
720
721 });
722
723 });
724
725 // build
726 describe(".build()", function() {});
727
728 describe(".getBuildResult()", function() {});
729
730 describe(".getBuildResults()", function() {});
731
732 describe(".storeBuildResult()", function() {});
733
734 describe(".storeBuildResults()", function() {});
735
736 });
737
738}
739
740describe('tech modules', function() {
741
742 var lib = process.env.COVER? '../lib-cov/' : '../lib/';
743
744 testBaseTech(lib + 'techs/js');
745 testBaseTech(lib + 'techs/css');
746 testBaseTech(lib + 'tech/index', 'def');
747 testBaseTech('./data/techs/test.js');
748
749});