OLD | NEW |
| (Empty) |
1 // Copyright 2006 Google Inc. | |
2 // | |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | |
4 // you may not use this file except in compliance with the License. | |
5 // You may obtain a copy of the License at | |
6 // | |
7 // http://www.apache.org/licenses/LICENSE-2.0 | |
8 // | |
9 // Unless required by applicable law or agreed to in writing, software | |
10 // distributed under the License is distributed on an "AS IS" BASIS, | |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or | |
12 // implied. See the License for the specific language governing | |
13 // permissions and limitations under the License. | |
14 /** | |
15 * @author Steffen Meschkat (mesch@google.com) | |
16 * @fileoverview Unittest and examples for jstemplates. | |
17 */ | |
18 | |
19 function jstWrap(data, template) { | |
20 return jstProcess(new JsEvalContext(data), template); | |
21 } | |
22 | |
23 function testJstSelect() { | |
24 // Template cardinality from jsselect. | |
25 var t = document.getElementById('t1'); | |
26 var d = { | |
27 items: [ 'A', 'B', 'C', '' ] | |
28 } | |
29 jstWrap(d, t); | |
30 | |
31 var h = t.innerHTML; | |
32 var clone = domCloneNode(t); | |
33 assertTrue(/>A<\/div>/.test(h)); | |
34 assertTrue(/>B<\/div>/.test(h)); | |
35 assertTrue(/>C<\/div>/.test(h)); | |
36 assertTrue(/><\/div>/.test(h)); | |
37 | |
38 // Reprocessing with identical data. | |
39 jstWrap(d, t); | |
40 assertAttributesMatch(t, clone); | |
41 | |
42 // Reprocessing with changed data. | |
43 d.items[1] = 'BB'; | |
44 jstWrap(d, t); | |
45 | |
46 h = t.innerHTML; | |
47 assertTrue(/>A<\/div>/.test(h)); | |
48 assertFalse(/>B<\/div>/.test(h)); | |
49 assertTrue(/>BB<\/div>/.test(h)); | |
50 assertTrue(/>C<\/div>/.test(h)); | |
51 | |
52 // Reprocessing with dropped data. | |
53 d.items.pop(); | |
54 d.items.pop(); | |
55 jstWrap(d, t); | |
56 h = t.innerHTML; | |
57 assertTrue(/>A<\/div>/.test(h)); | |
58 assertTrue(/>BB<\/div>/.test(h)); | |
59 assertFalse(/>C<\/div>/.test(h)); | |
60 assertFalse(/><\/div>/.test(h)); | |
61 | |
62 // Reprocessing with dropped data, once more. | |
63 d.items.pop(); | |
64 jstWrap(d, t); | |
65 h = t.innerHTML; | |
66 assertTrue(/>A<\/div>/.test(h)); | |
67 assertFalse(/>BB<\/div>/.test(h)); | |
68 assertFalse(/>C<\/div>/.test(h)); | |
69 | |
70 // Reprocessing with empty data -- the last template instance is | |
71 // preserved, and only hidden. | |
72 d.items.pop(); | |
73 jstWrap(d, t); | |
74 | |
75 assertTrue(/>A<\/div>/.test(h)); | |
76 assertFalse(/>BB<\/div>/.test(h)); | |
77 assertFalse(/>C<\/div>/.test(h)); | |
78 | |
79 // Reprocessing with added data. | |
80 d.items.push('D'); | |
81 jstWrap(d, t); | |
82 h = t.innerHTML; | |
83 assertFalse(/>A<\/div>/.test(h)); | |
84 assertTrue(/>D<\/div>/.test(h)); | |
85 } | |
86 | |
87 function testJstDisplay() { | |
88 var t = document.getElementById('t2'); | |
89 var d = { | |
90 display: true | |
91 } | |
92 jstWrap(d, t); | |
93 | |
94 var h = t.innerHTML; | |
95 assertFalse(/display:\s*none/.test(h)); | |
96 | |
97 d.display = false; | |
98 jstWrap(d, t); | |
99 | |
100 h = t.innerHTML; | |
101 assertTrue(/display:\s*none/.test(h)); | |
102 | |
103 // Check that 'this' within js expressions is the template node | |
104 t = document.getElementById('t2a'); | |
105 d = { | |
106 showId: 'x' | |
107 }; | |
108 jstWrap(d, t); | |
109 | |
110 h = t.innerHTML; | |
111 assertFalse(/display:\s*none/.test(h)); | |
112 | |
113 d.showId = 'y'; | |
114 jstWrap(d, t); | |
115 | |
116 h = t.innerHTML; | |
117 assertTrue(/display:\s*none/.test(h)); | |
118 } | |
119 | |
120 function stringContains(str, sub) { | |
121 return str.indexOf(sub) != -1; | |
122 } | |
123 | |
124 function testJseval() { | |
125 var data = {}; | |
126 | |
127 var counter = 0; | |
128 var ctx = new JsEvalContext(data); | |
129 ctx.setVariable("callback1", function() { | |
130 ++counter; | |
131 }); | |
132 ctx.setVariable("callback2", function() { | |
133 counter *= 2; | |
134 }); | |
135 | |
136 jstProcess(ctx, document.getElementById('testJseval1')); | |
137 assertEquals("testJseval1", 1, counter); | |
138 | |
139 jstProcess(ctx, document.getElementById('testJseval2')); | |
140 assertEquals("testJseval2", 4, counter); | |
141 } | |
142 | |
143 function testJstValues() { | |
144 var t = document.getElementById('t3'); | |
145 var d = {}; | |
146 jstWrap(d, t); | |
147 var h = t.innerHTML; | |
148 assertTrue(stringContains(h, 'http://maps.google.com/')); | |
149 var t3a = document.getElementById('t3a'); | |
150 assertEquals('http://maps.google.com/', t3a.foo.bar.baz); | |
151 assertEquals('http://maps.google.com/', t3a.bar); | |
152 assertEquals('red', t3a.style.backgroundColor); | |
153 } | |
154 | |
155 function testJstTransclude() { | |
156 var t = document.getElementById('t4'); | |
157 var p = document.getElementById('parent'); | |
158 var d = {}; | |
159 jstWrap(d, t); | |
160 var h = p.innerHTML; | |
161 assertTrue(h, stringContains(h, 'http://maps.google.com/')); | |
162 } | |
163 | |
164 function assertAttributesMatch(first, second) { | |
165 assertEquals('assertAttributesMatch: number of child nodes', | |
166 jsLength(first.childNodes), jsLength(second.childNodes)); | |
167 var b = second.firstChild; | |
168 for (var a = first.firstChild; a; a = a.nextSibling) { | |
169 var att = a.attributes; | |
170 if (att) { | |
171 assertTrue(b.attributes != null); | |
172 assertEquals('assertAttributesMatch: number of attribute nodes', | |
173 att.length, b.attributes.length); | |
174 for (var i = 0; i < jsLength(att); i++) { | |
175 var a = att[i]; | |
176 assertEquals('assertAttributesMatch: value of attribute ' + a.name, | |
177 a.value, b.getAttribute(a.name)); | |
178 } | |
179 } else { | |
180 assertNull(b.attributes); | |
181 } | |
182 b = b.nextSibling; | |
183 } | |
184 } | |
185 | |
186 function testJsskip() { | |
187 var div = domCreateElement(document, "DIV"); | |
188 div.innerHTML = [ | |
189 '<div jseval="outercallback()" jsskip="1">', | |
190 '<div jseval="innercallback()">', | |
191 '</div>', | |
192 '</div>' | |
193 ].join(''); | |
194 | |
195 var data = {}; | |
196 var ctx = new JsEvalContext(data); | |
197 var outerCalled = false; | |
198 ctx.setVariable("outercallback", function() { | |
199 outerCalled = true; | |
200 }); | |
201 var innerCalled = false; | |
202 ctx.setVariable("innercallback", function() { | |
203 innerCalled = true; | |
204 }); | |
205 jstProcess(ctx, div); | |
206 | |
207 assertTrue(outerCalled); | |
208 assertFalse(innerCalled); | |
209 } | |
210 | |
211 function testScalarContext() { | |
212 var t = document.getElementById('testScalarContext'); | |
213 | |
214 jstWrap(true, t); | |
215 assertTrue(/>true</.test(t.innerHTML)); | |
216 | |
217 jstWrap(false, t); | |
218 assertTrue(/>false</.test(t.innerHTML)); | |
219 | |
220 jstWrap(0, t); | |
221 assertTrue(/>0</.test(t.innerHTML)); | |
222 | |
223 jstWrap("foo", t); | |
224 assertTrue(/>foo</.test(t.innerHTML)); | |
225 | |
226 jstWrap(undefined, t); | |
227 assertTrue(/>undefined</.test(t.innerHTML)); | |
228 | |
229 jstWrap(null, t); | |
230 assertTrue(/>null</.test(t.innerHTML)); | |
231 } | |
232 | |
233 function testJstLoadTemplate() { | |
234 var wrapperId = 'testJstLoadTemplateWrapper'; | |
235 var id = 'testJstLoadTemplate'; | |
236 jstLoadTemplate_(document, '<div id="' + id + '">content</div>', wrapperId); | |
237 var wrapperElem = document.getElementById(wrapperId); | |
238 assertTrue('Expected wrapper element to be in document', | |
239 !!wrapperElem); | |
240 var newTemplate = document.getElementById(id); | |
241 assertTrue('Expected newly loaded template to be in document', | |
242 !!newTemplate); | |
243 assertTrue('Expected wrapper to be grandparent of template', | |
244 newTemplate.parentNode.parentNode == wrapperElem); | |
245 | |
246 // Make sure the next template loaded with the same wrapper id re-uses the | |
247 // wrapper element. | |
248 var id2 = 'testJstLoadTemplate2'; | |
249 jstLoadTemplate_(document, '<div id="' + id2 + '">content</div>', wrapperId); | |
250 var newTemplate2 = document.getElementById(id2); | |
251 assertTrue('Expected newly loaded template to be in document', | |
252 !!newTemplate2); | |
253 assertTrue('Expected wrapper to be grandparent of template', | |
254 newTemplate2.parentNode.parentNode == wrapperElem); | |
255 } | |
256 | |
257 function testJstGetTemplateFromDom() { | |
258 var element; | |
259 // Get by id a template in the document | |
260 // Success | |
261 element = jstGetTemplate('t1'); | |
262 assertTrue("Asserted jstGetTemplate('t1') to return a dom element", | |
263 !!element); | |
264 // Failure | |
265 element = jstGetTemplate('asdf'); | |
266 assertFalse("Asserted jstGetTemplate('asdf') to return null", | |
267 !!element); | |
268 } | |
269 | |
270 function testJstGetTemplateFromFunction() { | |
271 var element; | |
272 // Fetch a jstemplate by id from within a html string, passed via a function. | |
273 function returnHtmlWithId(id) { | |
274 var html = | |
275 '<div>' + | |
276 '<div id="' + id + '">Here is the template</div>' + | |
277 '</div>'; | |
278 return html; | |
279 } | |
280 // Success | |
281 element = jstGetTemplate('template', | |
282 partial(returnHtmlWithId, 'template')); | |
283 assertTrue("Expected jstGetTemplate('template') to return a dom element", | |
284 !!element); | |
285 | |
286 // Failure | |
287 element = jstGetTemplate('asdf', | |
288 partial(returnHtmlWithId, 'zxcv')); | |
289 assertFalse("Expected jstGetTemplate('zxcv') to return null", | |
290 !!element); | |
291 } | |
292 | |
293 function testPrepareNode() { | |
294 var id, node; | |
295 // Reset the cache so we're testing from a known state. | |
296 JstProcessor.jstCache_ = {}; | |
297 JstProcessor.jstCache_[0] = {}; | |
298 | |
299 // Skip pre-processed nodes. Preprocessed nodes are those with a | |
300 // PROP_jstcache property. | |
301 var t = document.getElementById('t1'); | |
302 var caches = []; | |
303 caches.push(JstProcessor.prepareNode_(t)); | |
304 caches.push(JstProcessor.prepareNode_(t)); | |
305 assertEquals('The same cache should be returned on each call to prepareNode', | |
306 caches[0], caches[1]); | |
307 | |
308 // Preprocessing a node with a jst attribute should return a valid struct | |
309 id = 'testPrepareNodeWithAttributes'; | |
310 jstLoadTemplate_(document, '<div id="' + id + '" jsskip="1"></div>'); | |
311 node = document.getElementById(id); | |
312 var cache = JstProcessor.prepareNode_(node); | |
313 try { | |
314 var jsskip = cache['jsskip']({}, {}); | |
315 } catch (e) { | |
316 fail('Exception when evaluating jsskip from cache'); | |
317 } | |
318 assertEquals(1, jsskip); | |
319 } | |
320 | |
321 | |
322 function testPrepareNodeWithNoAttributes() { | |
323 // Preprocessing a node with no jst attributes should return null | |
324 var id = 'testPrepareNodeNoAttributes'; | |
325 jstLoadTemplate_(document, '<div id="' + id + '"></div>'); | |
326 var node = document.getElementById(id); | |
327 assertEquals('prepareNode with no jst attributes should return default', | |
328 JstProcessor.jstcache_[0], JstProcessor.prepareNode_(node)); | |
329 } | |
330 | |
331 | |
332 function testJsVars() { | |
333 var template = document.createElement('div'); | |
334 document.body.appendChild(template); | |
335 template.innerHTML = '<div jsvars="foo:\'foo\';bar:true;$baz:1"></div>'; | |
336 | |
337 var context = new JsEvalContext; | |
338 jstProcess(context, template); | |
339 | |
340 assertEquals('foo', context.getVariable('foo')); | |
341 assertEquals(1, context.getVariable('$baz')); | |
342 assertTrue(context.getVariable('bar')); | |
343 assertUndefined(context.getVariable('foobar')); | |
344 } | |
345 | |
346 | |
347 function testCacheReuse() { | |
348 var template = document.createElement('div'); | |
349 document.body.appendChild(template); | |
350 template.innerHTML = | |
351 '<div jsvars="foo:\'foo\';bar:true;$baz:1"></div>' + | |
352 '<span jsvars="foo:\'foo\';bar:true;$baz:1"></span>'; | |
353 JstProcessor.prepareTemplate_(template); | |
354 assertEquals(template.firstChild.getAttribute(ATT_jstcache), | |
355 template.lastChild.getAttribute(ATT_jstcache)); | |
356 } | |
OLD | NEW |