Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* Any copyright is dedicated to the Public Domain. |
michael@0 | 2 | http://creativecommons.org/publicdomain/zero/1.0/ */ |
michael@0 | 3 | |
michael@0 | 4 | // Tests that the DOM Template engine works properly |
michael@0 | 5 | |
michael@0 | 6 | /* |
michael@0 | 7 | * These tests run both in Mozilla/Mochitest and plain browsers (as does |
michael@0 | 8 | * domtemplate) |
michael@0 | 9 | * We should endevour to keep the source in sync. |
michael@0 | 10 | */ |
michael@0 | 11 | |
michael@0 | 12 | var promise = Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js", {}).Promise; |
michael@0 | 13 | var template = Cu.import("resource://gre/modules/devtools/Templater.jsm", {}).template; |
michael@0 | 14 | |
michael@0 | 15 | const TEST_URI = "http://example.com/browser/browser/devtools/shared/test/browser_templater_basic.html"; |
michael@0 | 16 | |
michael@0 | 17 | function test() { |
michael@0 | 18 | addTab(TEST_URI, function() { |
michael@0 | 19 | info("Starting DOM Templater Tests"); |
michael@0 | 20 | runTest(0); |
michael@0 | 21 | }); |
michael@0 | 22 | } |
michael@0 | 23 | |
michael@0 | 24 | function runTest(index) { |
michael@0 | 25 | var options = tests[index] = tests[index](); |
michael@0 | 26 | var holder = content.document.createElement('div'); |
michael@0 | 27 | holder.id = options.name; |
michael@0 | 28 | var body = content.document.body; |
michael@0 | 29 | body.appendChild(holder); |
michael@0 | 30 | holder.innerHTML = options.template; |
michael@0 | 31 | |
michael@0 | 32 | info('Running ' + options.name); |
michael@0 | 33 | template(holder, options.data, options.options); |
michael@0 | 34 | |
michael@0 | 35 | if (typeof options.result == 'string') { |
michael@0 | 36 | is(holder.innerHTML, options.result, options.name); |
michael@0 | 37 | } |
michael@0 | 38 | else { |
michael@0 | 39 | ok(holder.innerHTML.match(options.result) != null, |
michael@0 | 40 | options.name + ' result=\'' + holder.innerHTML + '\''); |
michael@0 | 41 | } |
michael@0 | 42 | |
michael@0 | 43 | if (options.also) { |
michael@0 | 44 | options.also(options); |
michael@0 | 45 | } |
michael@0 | 46 | |
michael@0 | 47 | function runNextTest() { |
michael@0 | 48 | index++; |
michael@0 | 49 | if (index < tests.length) { |
michael@0 | 50 | runTest(index); |
michael@0 | 51 | } |
michael@0 | 52 | else { |
michael@0 | 53 | finished(); |
michael@0 | 54 | } |
michael@0 | 55 | } |
michael@0 | 56 | |
michael@0 | 57 | if (options.later) { |
michael@0 | 58 | var ais = is.bind(this); |
michael@0 | 59 | |
michael@0 | 60 | function createTester(holder, options) { |
michael@0 | 61 | return function() { |
michael@0 | 62 | ais(holder.innerHTML, options.later, options.name + ' later'); |
michael@0 | 63 | runNextTest(); |
michael@0 | 64 | }.bind(this); |
michael@0 | 65 | } |
michael@0 | 66 | |
michael@0 | 67 | executeSoon(createTester(holder, options)); |
michael@0 | 68 | } |
michael@0 | 69 | else { |
michael@0 | 70 | runNextTest(); |
michael@0 | 71 | } |
michael@0 | 72 | } |
michael@0 | 73 | |
michael@0 | 74 | function finished() { |
michael@0 | 75 | gBrowser.removeCurrentTab(); |
michael@0 | 76 | info("Finishing DOM Templater Tests"); |
michael@0 | 77 | tests = null; |
michael@0 | 78 | finish(); |
michael@0 | 79 | } |
michael@0 | 80 | |
michael@0 | 81 | /** |
michael@0 | 82 | * Why have an array of functions that return data rather than just an array |
michael@0 | 83 | * of the data itself? Some of these tests contain calls to delayReply() which |
michael@0 | 84 | * sets up async processing using executeSoon(). Since the execution of these |
michael@0 | 85 | * tests is asynchronous, the delayed reply will probably arrive before the |
michael@0 | 86 | * test is executed, making the test be synchronous. So we wrap the data in a |
michael@0 | 87 | * function so we only set it up just before we use it. |
michael@0 | 88 | */ |
michael@0 | 89 | var tests = [ |
michael@0 | 90 | function() { return { |
michael@0 | 91 | name: 'simpleNesting', |
michael@0 | 92 | template: '<div id="ex1">${nested.value}</div>', |
michael@0 | 93 | data: { nested:{ value:'pass 1' } }, |
michael@0 | 94 | result: '<div id="ex1">pass 1</div>' |
michael@0 | 95 | };}, |
michael@0 | 96 | |
michael@0 | 97 | function() { return { |
michael@0 | 98 | name: 'returnDom', |
michael@0 | 99 | template: '<div id="ex2">${__element.ownerDocument.createTextNode(\'pass 2\')}</div>', |
michael@0 | 100 | options: { allowEval: true }, |
michael@0 | 101 | data: {}, |
michael@0 | 102 | result: '<div id="ex2">pass 2</div>' |
michael@0 | 103 | };}, |
michael@0 | 104 | |
michael@0 | 105 | function() { return { |
michael@0 | 106 | name: 'srcChange', |
michael@0 | 107 | template: '<img _src="${fred}" id="ex3">', |
michael@0 | 108 | data: { fred:'green.png' }, |
michael@0 | 109 | result: /<img( id="ex3")? src="green.png"( id="ex3")?>/ |
michael@0 | 110 | };}, |
michael@0 | 111 | |
michael@0 | 112 | function() { return { |
michael@0 | 113 | name: 'ifTrue', |
michael@0 | 114 | template: '<p if="${name !== \'jim\'}">hello ${name}</p>', |
michael@0 | 115 | options: { allowEval: true }, |
michael@0 | 116 | data: { name: 'fred' }, |
michael@0 | 117 | result: '<p>hello fred</p>' |
michael@0 | 118 | };}, |
michael@0 | 119 | |
michael@0 | 120 | function() { return { |
michael@0 | 121 | name: 'ifFalse', |
michael@0 | 122 | template: '<p if="${name !== \'jim\'}">hello ${name}</p>', |
michael@0 | 123 | options: { allowEval: true }, |
michael@0 | 124 | data: { name: 'jim' }, |
michael@0 | 125 | result: '' |
michael@0 | 126 | };}, |
michael@0 | 127 | |
michael@0 | 128 | function() { return { |
michael@0 | 129 | name: 'simpleLoop', |
michael@0 | 130 | template: '<p foreach="index in ${[ 1, 2, 3 ]}">${index}</p>', |
michael@0 | 131 | options: { allowEval: true }, |
michael@0 | 132 | data: {}, |
michael@0 | 133 | result: '<p>1</p><p>2</p><p>3</p>' |
michael@0 | 134 | };}, |
michael@0 | 135 | |
michael@0 | 136 | function() { return { |
michael@0 | 137 | name: 'loopElement', |
michael@0 | 138 | template: '<loop foreach="i in ${array}">${i}</loop>', |
michael@0 | 139 | data: { array: [ 1, 2, 3 ] }, |
michael@0 | 140 | result: '123' |
michael@0 | 141 | };}, |
michael@0 | 142 | |
michael@0 | 143 | // Bug 692028: DOMTemplate memory leak with asynchronous arrays |
michael@0 | 144 | // Bug 692031: DOMTemplate async loops do not drop the loop element |
michael@0 | 145 | function() { return { |
michael@0 | 146 | name: 'asyncLoopElement', |
michael@0 | 147 | template: '<loop foreach="i in ${array}">${i}</loop>', |
michael@0 | 148 | data: { array: delayReply([1, 2, 3]) }, |
michael@0 | 149 | result: '<span></span>', |
michael@0 | 150 | later: '123' |
michael@0 | 151 | };}, |
michael@0 | 152 | |
michael@0 | 153 | function() { return { |
michael@0 | 154 | name: 'saveElement', |
michael@0 | 155 | template: '<p save="${element}">${name}</p>', |
michael@0 | 156 | data: { name: 'pass 8' }, |
michael@0 | 157 | result: '<p>pass 8</p>', |
michael@0 | 158 | also: function(options) { |
michael@0 | 159 | ok(options.data.element.innerHTML, 'pass 9', 'saveElement saved'); |
michael@0 | 160 | delete options.data.element; |
michael@0 | 161 | } |
michael@0 | 162 | };}, |
michael@0 | 163 | |
michael@0 | 164 | function() { return { |
michael@0 | 165 | name: 'useElement', |
michael@0 | 166 | template: '<p id="pass9">${adjust(__element)}</p>', |
michael@0 | 167 | options: { allowEval: true }, |
michael@0 | 168 | data: { |
michael@0 | 169 | adjust: function(element) { |
michael@0 | 170 | is('pass9', element.id, 'useElement adjust'); |
michael@0 | 171 | return 'pass 9b' |
michael@0 | 172 | } |
michael@0 | 173 | }, |
michael@0 | 174 | result: '<p id="pass9">pass 9b</p>' |
michael@0 | 175 | };}, |
michael@0 | 176 | |
michael@0 | 177 | function() { return { |
michael@0 | 178 | name: 'asyncInline', |
michael@0 | 179 | template: '${delayed}', |
michael@0 | 180 | data: { delayed: delayReply('inline') }, |
michael@0 | 181 | result: '<span></span>', |
michael@0 | 182 | later: 'inline' |
michael@0 | 183 | };}, |
michael@0 | 184 | |
michael@0 | 185 | // Bug 692028: DOMTemplate memory leak with asynchronous arrays |
michael@0 | 186 | function() { return { |
michael@0 | 187 | name: 'asyncArray', |
michael@0 | 188 | template: '<p foreach="i in ${delayed}">${i}</p>', |
michael@0 | 189 | data: { delayed: delayReply([1, 2, 3]) }, |
michael@0 | 190 | result: '<span></span>', |
michael@0 | 191 | later: '<p>1</p><p>2</p><p>3</p>' |
michael@0 | 192 | };}, |
michael@0 | 193 | |
michael@0 | 194 | function() { return { |
michael@0 | 195 | name: 'asyncMember', |
michael@0 | 196 | template: '<p foreach="i in ${delayed}">${i}</p>', |
michael@0 | 197 | data: { delayed: [delayReply(4), delayReply(5), delayReply(6)] }, |
michael@0 | 198 | result: '<span></span><span></span><span></span>', |
michael@0 | 199 | later: '<p>4</p><p>5</p><p>6</p>' |
michael@0 | 200 | };}, |
michael@0 | 201 | |
michael@0 | 202 | // Bug 692028: DOMTemplate memory leak with asynchronous arrays |
michael@0 | 203 | function() { return { |
michael@0 | 204 | name: 'asyncBoth', |
michael@0 | 205 | template: '<p foreach="i in ${delayed}">${i}</p>', |
michael@0 | 206 | data: { |
michael@0 | 207 | delayed: delayReply([ |
michael@0 | 208 | delayReply(4), |
michael@0 | 209 | delayReply(5), |
michael@0 | 210 | delayReply(6) |
michael@0 | 211 | ]) |
michael@0 | 212 | }, |
michael@0 | 213 | result: '<span></span>', |
michael@0 | 214 | later: '<p>4</p><p>5</p><p>6</p>' |
michael@0 | 215 | };}, |
michael@0 | 216 | |
michael@0 | 217 | // Bug 701762: DOMTemplate fails when ${foo()} returns undefined |
michael@0 | 218 | function() { return { |
michael@0 | 219 | name: 'functionReturningUndefiend', |
michael@0 | 220 | template: '<p>${foo()}</p>', |
michael@0 | 221 | options: { allowEval: true }, |
michael@0 | 222 | data: { |
michael@0 | 223 | foo: function() {} |
michael@0 | 224 | }, |
michael@0 | 225 | result: '<p>undefined</p>' |
michael@0 | 226 | };}, |
michael@0 | 227 | |
michael@0 | 228 | // Bug 702642: DOMTemplate is relatively slow when evaluating JS ${} |
michael@0 | 229 | function() { return { |
michael@0 | 230 | name: 'propertySimple', |
michael@0 | 231 | template: '<p>${a.b.c}</p>', |
michael@0 | 232 | data: { a: { b: { c: 'hello' } } }, |
michael@0 | 233 | result: '<p>hello</p>' |
michael@0 | 234 | };}, |
michael@0 | 235 | |
michael@0 | 236 | function() { return { |
michael@0 | 237 | name: 'propertyPass', |
michael@0 | 238 | template: '<p>${Math.max(1, 2)}</p>', |
michael@0 | 239 | options: { allowEval: true }, |
michael@0 | 240 | result: '<p>2</p>' |
michael@0 | 241 | };}, |
michael@0 | 242 | |
michael@0 | 243 | function() { return { |
michael@0 | 244 | name: 'propertyFail', |
michael@0 | 245 | template: '<p>${Math.max(1, 2)}</p>', |
michael@0 | 246 | result: '<p>${Math.max(1, 2)}</p>' |
michael@0 | 247 | };}, |
michael@0 | 248 | |
michael@0 | 249 | // Bug 723431: DOMTemplate should allow customisation of display of |
michael@0 | 250 | // null/undefined values |
michael@0 | 251 | function() { return { |
michael@0 | 252 | name: 'propertyUndefAttrFull', |
michael@0 | 253 | template: '<p>${nullvar}|${undefinedvar1}|${undefinedvar2}</p>', |
michael@0 | 254 | data: { nullvar: null, undefinedvar1: undefined }, |
michael@0 | 255 | result: '<p>null|undefined|undefined</p>' |
michael@0 | 256 | };}, |
michael@0 | 257 | |
michael@0 | 258 | function() { return { |
michael@0 | 259 | name: 'propertyUndefAttrBlank', |
michael@0 | 260 | template: '<p>${nullvar}|${undefinedvar1}|${undefinedvar2}</p>', |
michael@0 | 261 | data: { nullvar: null, undefinedvar1: undefined }, |
michael@0 | 262 | options: { blankNullUndefined: true }, |
michael@0 | 263 | result: '<p>||</p>' |
michael@0 | 264 | };}, |
michael@0 | 265 | |
michael@0 | 266 | function() { return { |
michael@0 | 267 | name: 'propertyUndefAttrFull', |
michael@0 | 268 | template: '<div><p value="${nullvar}"></p><p value="${undefinedvar1}"></p><p value="${undefinedvar2}"></p></div>', |
michael@0 | 269 | data: { nullvar: null, undefinedvar1: undefined }, |
michael@0 | 270 | result: '<div><p value="null"></p><p value="undefined"></p><p value="undefined"></p></div>' |
michael@0 | 271 | };}, |
michael@0 | 272 | |
michael@0 | 273 | function() { return { |
michael@0 | 274 | name: 'propertyUndefAttrBlank', |
michael@0 | 275 | template: '<div><p value="${nullvar}"></p><p value="${undefinedvar1}"></p><p value="${undefinedvar2}"></p></div>', |
michael@0 | 276 | data: { nullvar: null, undefinedvar1: undefined }, |
michael@0 | 277 | options: { blankNullUndefined: true }, |
michael@0 | 278 | result: '<div><p value=""></p><p value=""></p><p value=""></p></div>' |
michael@0 | 279 | };} |
michael@0 | 280 | ]; |
michael@0 | 281 | |
michael@0 | 282 | function delayReply(data) { |
michael@0 | 283 | var d = promise.defer(); |
michael@0 | 284 | executeSoon(function() { |
michael@0 | 285 | d.resolve(data); |
michael@0 | 286 | }); |
michael@0 | 287 | return d.promise; |
michael@0 | 288 | } |