Skip to content

Commit 865b38d

Browse files
committed
Handlebars engine, plus unit tests and proposed pattern tree for
file-based unit testing
1 parent fe6206b commit 865b38d

File tree

5 files changed

+273
-0
lines changed

5 files changed

+273
-0
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* handlebars pattern engine for patternlab-node - v0.15.1 - 2015
3+
*
4+
* Geoffrey Pursell, Brian Muenzenmeyer, and the web community.
5+
* Licensed under the MIT license.
6+
*
7+
* Many thanks to Brad Frost and Dave Olsen for inspiration, encouragement, and advice.
8+
*
9+
*/
10+
11+
(function () {
12+
"use strict";
13+
14+
var Handlebars = require('handlebars');
15+
16+
var engine_handlebars = {
17+
engine: Handlebars,
18+
engineName: 'handlebars',
19+
engineFileExtension: '.hbs',
20+
21+
// regexes, stored here so they're only compiled once
22+
// GTP warning: unchanged copypasta from mustache engine
23+
findPartialsRE: /{{>\s*((?:\d+-[\w-]+\/)+(\d+-[\w-]+(\.\w+)?)|[A-Za-z0-9-]+)(\:[\w-]+)?(\(\s*\w+\s*:\s*(?:'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*")\))?\s*}}/g,
24+
findPartialsWithStyleModifiersRE: /{{>([ ])?([\w\-\.\/~]+)(?!\()(\:[A-Za-z0-9-_|]+)+(?:(| )\(.*)?([ ])?}}/g,
25+
findPartialsWithPatternParametersRE: /{{>([ ])?([\w\-\.\/~]+)(?:\:[A-Za-z0-9-_|]+)?(?:(| )\(.*)+([ ])?}}/g,
26+
findListItemsRE: /({{#( )?)(list(I|i)tems.)(one|two|three|four|five|six|seven|eight|nine|ten|eleven|twelve|thirteen|fourteen|fifteen|sixteen|seventeen|eighteen|nineteen|twenty)( )?}}/g,
27+
getPartialKeyRE: /{{>([ ])?([\w\-\.\/~]+)(:[A-z-_|]+)?(?:\:[A-Za-z0-9-_]+)?(?:(| )\(.*)?([ ])?}}/g,
28+
29+
// render it
30+
renderPattern: function renderPattern(template, data, partials) {
31+
if (partials) {
32+
Handlebars.registerPartial(partials);
33+
}
34+
var compiled = Handlebars.compile(template);
35+
return compiled(data);
36+
},
37+
38+
// find and return any {{> template-name }} within pattern
39+
findPartials: function findPartials(pattern) {
40+
var matches = pattern.template.match(this.findPartialsRE);
41+
return matches;
42+
},
43+
findPartialsWithStyleModifiers: function(pattern) {
44+
var matches = pattern.template.match(this.findPartialsWithStyleModifiersRE);
45+
return matches;
46+
},
47+
// returns any patterns that match {{> value(foo:"bar") }} or {{>
48+
// value:mod(foo:"bar") }} within the pattern
49+
findPartialsWithPatternParameters: function(pattern) {
50+
var matches = pattern.template.match(this.findPartialsWithPatternParametersRE);
51+
return matches;
52+
},
53+
findListItems: function(pattern) {
54+
var matches = pattern.template.match(this.findListItemsRE);
55+
return matches;
56+
},
57+
// given a pattern, and a partial string, tease out the "pattern key" and
58+
// return it.
59+
getPartialKey: function(pattern, partialString) {
60+
var partialKey = partialString.replace(this.getPartialKeyRE, '$2');
61+
return partialKey;
62+
}
63+
};
64+
65+
module.exports = engine_handlebars;
66+
})();

test/engine_handlebars_tests.js

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
(function () {
2+
"use strict";
3+
4+
var path = require('path');
5+
var pa = require('../builder/pattern_assembler');
6+
var object_factory = require('../builder/object_factory');
7+
var testPatternsPath = path.resolve(__dirname, 'files', '_handlebars-test-patterns');
8+
9+
// fake pattern lab constructor:
10+
// sets up a fake patternlab object, which is needed by the pattern processing
11+
// apparatus.
12+
function fakePatternLab() {
13+
var fpl = {
14+
partials: {},
15+
patterns: [],
16+
footer: '',
17+
header: '',
18+
listitems: {},
19+
listItemArray: [],
20+
data: {
21+
link: {}
22+
},
23+
config: require('../config.json'),
24+
package: {}
25+
};
26+
27+
// patch the pattern source so the pattern assembler can correctly determine
28+
// the "subdir"
29+
fpl.config.patterns.source = './test/files/_handlebars-test-patterns';
30+
31+
return fpl;
32+
}
33+
34+
35+
exports['engine_handlebars'] = {
36+
'hello world handlebars pattern renders': function (test) {
37+
test.expect(1);
38+
39+
var patternPath = path.resolve(
40+
testPatternsPath,
41+
'00-atoms',
42+
'00-global',
43+
'00-helloworld.hbs'
44+
);
45+
46+
// do all the normal processing of the pattern
47+
var patternlab = new fakePatternLab();
48+
var assembler = new pa();
49+
var helloWorldPattern = assembler.process_pattern_iterative(patternPath, patternlab);
50+
assembler.process_pattern_recursive(patternPath, patternlab);
51+
52+
test.equals(helloWorldPattern.render(), 'Hello world!\n');
53+
test.done();
54+
},
55+
'hello worlds handlebars pattern can see the atoms-helloworld partial and renders it twice': function (test) {
56+
test.expect(1);
57+
58+
// pattern paths
59+
var pattern1Path = path.resolve(
60+
testPatternsPath,
61+
'00-atoms',
62+
'00-global',
63+
'00-helloworld.hbs'
64+
);
65+
var pattern2Path = path.resolve(
66+
testPatternsPath,
67+
'00-atoms',
68+
'00-global',
69+
'01-helloworlds.hbs'
70+
);
71+
72+
// set up environment
73+
var patternlab = new fakePatternLab(); // environment
74+
var assembler = new pa();
75+
76+
// do all the normal processing of the pattern
77+
assembler.process_pattern_iterative(pattern1Path, patternlab);
78+
var helloWorldsPattern = assembler.process_pattern_iterative(pattern2Path, patternlab);
79+
assembler.process_pattern_recursive(pattern1Path, patternlab);
80+
assembler.process_pattern_recursive(pattern2Path, patternlab);
81+
82+
// test
83+
test.equals(helloWorldsPattern.render(), 'Hello world!\n and Hello world!\n\n');
84+
test.done();
85+
},
86+
// GTP warning: unchanged copypasta from mustache engine
87+
'find_pattern_partials finds partials': function(test){
88+
// NOTES from GTP:
89+
// it's nice to have so much test coverage, but it retrospect, I'm not
90+
// happy with the structure I wound up with in this test; it's too
91+
// difficult to add test cases and test failure reporting is not very
92+
// granular.
93+
94+
test.expect(16);
95+
96+
// setup current pattern from what we would have during execution
97+
// docs on partial syntax are here:
98+
// http://patternlab.io/docs/pattern-including.html
99+
var currentPattern = object_factory.oPattern.create(
100+
'/home/fakeuser/pl/source/_patterns/01-molecules/00-testing/00-test-mol.hbs', // abspath
101+
'01-molecules\\00-testing', // subdir
102+
'00-test-mol.hbs', // filename,
103+
null, // data
104+
{
105+
template: "{{> molecules-comment-header}}asdfasdf" +
106+
"{{> molecules-comment-header}}" +
107+
"{{> \n molecules-comment-header\n}}" +
108+
"{{> }}" +
109+
"{{> molecules-weird-spacing }}" +
110+
"{{> molecules-ba_d-cha*rs }}" +
111+
"{{> molecules-single-comment(description: 'A life isn\\'t like a garden. Perfect moments can be had, but not preserved, except in memory.') }}" +
112+
'{{> molecules-single-comment(description: "A life is like a \\"garden\\". Perfect moments can be had, but not preserved, except in memory.") }}' +
113+
"{{> molecules-single-comment:foo }}" +
114+
// verbose partial syntax, introduced in v0.12.0, with file extension
115+
"{{> 01-molecules/06-components/03-comment-header.hbs }}" +
116+
"{{> 01-molecules/06-components/02-single-comment.hbs(description: 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.') }}" +
117+
"{{> molecules-single-comment:foo }}" +
118+
"{{>atoms-error(message: 'That\\'s no moon...')}}" +
119+
'{{>atoms-error(message: \'That\\\'s no moon...\')}}' +
120+
"{{> 00-atoms/00-global/ }}" +
121+
// verbose partial syntax, introduced in v0.12.0, no file extension
122+
"{{> 00-atoms/00-global/06-test }}" +
123+
"{{> molecules-single-comment:foo_1 }}" +
124+
"{{> molecules-single-comment:foo-1 }}"
125+
}
126+
);
127+
128+
var results = currentPattern.findPartials();
129+
console.log(results);
130+
test.equals(results.length, 15);
131+
test.equals(results[0], "{{> molecules-comment-header}}");
132+
test.equals(results[1], "{{> molecules-comment-header}}");
133+
test.equals(results[2], "{{> \n molecules-comment-header\n}}");
134+
test.equals(results[3], "{{> molecules-weird-spacing }}");
135+
test.equals(results[4], "{{> molecules-single-comment(description: 'A life isn\\'t like a garden. Perfect moments can be had, but not preserved, except in memory.') }}");
136+
test.equals(results[5], '{{> molecules-single-comment(description: "A life is like a \\"garden\\". Perfect moments can be had, but not preserved, except in memory.") }}');
137+
test.equals(results[6], "{{> molecules-single-comment:foo }}");
138+
test.equals(results[7], "{{> 01-molecules/06-components/03-comment-header.hbs }}");
139+
test.equals(results[8], "{{> 01-molecules/06-components/02-single-comment.hbs(description: 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.') }}");
140+
test.equals(results[9], "{{> molecules-single-comment:foo }}");
141+
test.equals(results[10], "{{>atoms-error(message: 'That\\'s no moon...')}}");
142+
test.equals(results[11], "{{>atoms-error(message: 'That\\'s no moon...')}}");
143+
test.equals(results[12], "{{> 00-atoms/00-global/06-test }}");
144+
test.equals(results[13], '{{> molecules-single-comment:foo_1 }}');
145+
test.equals(results[14], '{{> molecules-single-comment:foo-1 }}');
146+
test.done();
147+
},
148+
// GTP warning: unchanged copypasta from mustache engine
149+
'find_pattern_partials finds verbose partials': function(test){
150+
test.expect(3);
151+
152+
//setup current pattern from what we would have during execution
153+
var currentPattern = new object_factory.oPattern(
154+
'/home/fakeuser/pl/source/_patterns/01-molecules/00-testing/00-test-mol.hbs', // abspath
155+
'01-molecules\\00-testing', // subdir
156+
'00-test-mol.hbs', // filename,
157+
null // data
158+
);
159+
currentPattern.template = "<h1>{{> 01-molecules/06-components/03-comment-header.hbs }}</h1><div>{{> 01-molecules/06-components/02-single-comment.hbs(description: 'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.') }}</div>";
160+
161+
var results = currentPattern.findPartials();
162+
test.equals(results.length, 2);
163+
test.equals(results[0], '{{> 01-molecules/06-components/03-comment-header.hbs }}');
164+
test.equals(results[1], '{{> 01-molecules/06-components/02-single-comment.hbs(description: \'A life is like a garden. Perfect moments can be had, but not preserved, except in memory.\') }}');
165+
test.done();
166+
},
167+
// GTP warning: unchanged copypasta from mustache engine
168+
'find_pattern_partials_with_parameters finds parameters with verbose partials': function(test){
169+
test.expect(2);
170+
171+
//setup current pattern from what we would have during execution
172+
var currentPattern = new object_factory.oPattern(
173+
'/home/fakeuser/pl/source/_patterns/01-molecules/00-testing/00-test-mol.hbs', // abspath
174+
'01-molecules\\00-testing', // subdir
175+
'00-test-mol.hbs', // filename,
176+
null // data
177+
);
178+
currentPattern.template = "<h1>{{> 01-molecules/06-components/molecules-comment-header}}</h1><div>{{> 01-molecules/06-components/molecules-single-comment(bar:'baz') }}</div>";
179+
180+
var results = currentPattern.findPartialsWithPatternParameters();
181+
test.equals(results.length, 1);
182+
test.equals(results[0], "{{> 01-molecules/06-components/molecules-single-comment(bar:'baz') }}");
183+
184+
test.done();
185+
},
186+
// GTP warning: unchanged copypasta from mustache engine
187+
'find_pattern_partials_with_parameters finds no style modifiers when only partials present': function(test){
188+
test.expect(1);
189+
190+
//setup current pattern from what we would have during execution
191+
var currentPattern = new object_factory.oPattern(
192+
'/home/fakeuser/pl/source/_patterns/01-molecules/00-testing/00-test-mol.hbs', // abspath
193+
'01-molecules\\00-testing', // subdir
194+
'00-test-mol.hbs', // filename,
195+
null // data
196+
);
197+
currentPattern.template = "<h1>{{> molecules-comment-header}}</h1><div>{{> molecules-single-comment }}</div>";
198+
199+
var results = currentPattern.findPartialsWithPatternParameters();
200+
test.equals(results, null);
201+
202+
test.done();
203+
}
204+
};
205+
})();

test/files/_handlebars-test-patterns/00-atoms/00-global/00-foo.hbs

Whitespace-only changes.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Hello world!
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{{> atoms-helloworld}} and {{> atoms-helloworld}}

0 commit comments

Comments
 (0)