browser/devtools/shared/test/browser_templater_basic.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/browser/devtools/shared/test/browser_templater_basic.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,288 @@
     1.4 +/* Any copyright is dedicated to the Public Domain.
     1.5 +   http://creativecommons.org/publicdomain/zero/1.0/ */
     1.6 +
     1.7 +// Tests that the DOM Template engine works properly
     1.8 +
     1.9 +/*
    1.10 + * These tests run both in Mozilla/Mochitest and plain browsers (as does
    1.11 + * domtemplate)
    1.12 + * We should endevour to keep the source in sync.
    1.13 + */
    1.14 +
    1.15 +var promise = Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js", {}).Promise;
    1.16 +var template = Cu.import("resource://gre/modules/devtools/Templater.jsm", {}).template;
    1.17 +
    1.18 +const TEST_URI = "http://example.com/browser/browser/devtools/shared/test/browser_templater_basic.html";
    1.19 +
    1.20 +function test() {
    1.21 +  addTab(TEST_URI, function() {
    1.22 +    info("Starting DOM Templater Tests");
    1.23 +    runTest(0);
    1.24 +  });
    1.25 +}
    1.26 +
    1.27 +function runTest(index) {
    1.28 +  var options = tests[index] = tests[index]();
    1.29 +  var holder = content.document.createElement('div');
    1.30 +  holder.id = options.name;
    1.31 +  var body = content.document.body;
    1.32 +  body.appendChild(holder);
    1.33 +  holder.innerHTML = options.template;
    1.34 +
    1.35 +  info('Running ' + options.name);
    1.36 +  template(holder, options.data, options.options);
    1.37 +
    1.38 +  if (typeof options.result == 'string') {
    1.39 +    is(holder.innerHTML, options.result, options.name);
    1.40 +  }
    1.41 +  else {
    1.42 +    ok(holder.innerHTML.match(options.result) != null,
    1.43 +       options.name + ' result=\'' + holder.innerHTML + '\'');
    1.44 +  }
    1.45 +
    1.46 +  if (options.also) {
    1.47 +    options.also(options);
    1.48 +  }
    1.49 +
    1.50 +  function runNextTest() {
    1.51 +    index++;
    1.52 +    if (index < tests.length) {
    1.53 +      runTest(index);
    1.54 +    }
    1.55 +    else {
    1.56 +      finished();
    1.57 +    }
    1.58 +  }
    1.59 +
    1.60 +  if (options.later) {
    1.61 +    var ais = is.bind(this);
    1.62 +
    1.63 +    function createTester(holder, options) {
    1.64 +      return function() {
    1.65 +        ais(holder.innerHTML, options.later, options.name + ' later');
    1.66 +        runNextTest();
    1.67 +      }.bind(this);
    1.68 +    }
    1.69 +
    1.70 +    executeSoon(createTester(holder, options));
    1.71 +  }
    1.72 +  else {
    1.73 +    runNextTest();
    1.74 +  }
    1.75 +}
    1.76 +
    1.77 +function finished() {
    1.78 +  gBrowser.removeCurrentTab();
    1.79 +  info("Finishing DOM Templater Tests");
    1.80 +  tests = null;
    1.81 +  finish();
    1.82 +}
    1.83 +
    1.84 +/**
    1.85 + * Why have an array of functions that return data rather than just an array
    1.86 + * of the data itself? Some of these tests contain calls to delayReply() which
    1.87 + * sets up async processing using executeSoon(). Since the execution of these
    1.88 + * tests is asynchronous, the delayed reply will probably arrive before the
    1.89 + * test is executed, making the test be synchronous. So we wrap the data in a
    1.90 + * function so we only set it up just before we use it.
    1.91 + */
    1.92 +var tests = [
    1.93 +  function() { return {
    1.94 +    name: 'simpleNesting',
    1.95 +    template: '<div id="ex1">${nested.value}</div>',
    1.96 +    data: { nested:{ value:'pass 1' } },
    1.97 +    result: '<div id="ex1">pass 1</div>'
    1.98 +  };},
    1.99 +
   1.100 +  function() { return {
   1.101 +    name: 'returnDom',
   1.102 +    template: '<div id="ex2">${__element.ownerDocument.createTextNode(\'pass 2\')}</div>',
   1.103 +    options: { allowEval: true },
   1.104 +    data: {},
   1.105 +    result: '<div id="ex2">pass 2</div>'
   1.106 +  };},
   1.107 +
   1.108 +  function() { return {
   1.109 +    name: 'srcChange',
   1.110 +    template: '<img _src="${fred}" id="ex3">',
   1.111 +    data: { fred:'green.png' },
   1.112 +    result: /<img( id="ex3")? src="green.png"( id="ex3")?>/
   1.113 +  };},
   1.114 +
   1.115 +  function() { return {
   1.116 +    name: 'ifTrue',
   1.117 +    template: '<p if="${name !== \'jim\'}">hello ${name}</p>',
   1.118 +    options: { allowEval: true },
   1.119 +    data: { name: 'fred' },
   1.120 +    result: '<p>hello fred</p>'
   1.121 +  };},
   1.122 +
   1.123 +  function() { return {
   1.124 +    name: 'ifFalse',
   1.125 +    template: '<p if="${name !== \'jim\'}">hello ${name}</p>',
   1.126 +    options: { allowEval: true },
   1.127 +    data: { name: 'jim' },
   1.128 +    result: ''
   1.129 +  };},
   1.130 +
   1.131 +  function() { return {
   1.132 +    name: 'simpleLoop',
   1.133 +    template: '<p foreach="index in ${[ 1, 2, 3 ]}">${index}</p>',
   1.134 +    options: { allowEval: true },
   1.135 +    data: {},
   1.136 +    result: '<p>1</p><p>2</p><p>3</p>'
   1.137 +  };},
   1.138 +
   1.139 +  function() { return {
   1.140 +    name: 'loopElement',
   1.141 +    template: '<loop foreach="i in ${array}">${i}</loop>',
   1.142 +    data: { array: [ 1, 2, 3 ] },
   1.143 +    result: '123'
   1.144 +  };},
   1.145 +
   1.146 +  // Bug 692028: DOMTemplate memory leak with asynchronous arrays
   1.147 +  // Bug 692031: DOMTemplate async loops do not drop the loop element
   1.148 +  function() { return {
   1.149 +    name: 'asyncLoopElement',
   1.150 +    template: '<loop foreach="i in ${array}">${i}</loop>',
   1.151 +    data: { array: delayReply([1, 2, 3]) },
   1.152 +    result: '<span></span>',
   1.153 +    later: '123'
   1.154 +  };},
   1.155 +
   1.156 +  function() { return {
   1.157 +    name: 'saveElement',
   1.158 +    template: '<p save="${element}">${name}</p>',
   1.159 +    data: { name: 'pass 8' },
   1.160 +    result: '<p>pass 8</p>',
   1.161 +    also: function(options) {
   1.162 +      ok(options.data.element.innerHTML, 'pass 9', 'saveElement saved');
   1.163 +      delete options.data.element;
   1.164 +    }
   1.165 +  };},
   1.166 +
   1.167 +  function() { return {
   1.168 +    name: 'useElement',
   1.169 +    template: '<p id="pass9">${adjust(__element)}</p>',
   1.170 +    options: { allowEval: true },
   1.171 +    data: {
   1.172 +      adjust: function(element) {
   1.173 +        is('pass9', element.id, 'useElement adjust');
   1.174 +        return 'pass 9b'
   1.175 +      }
   1.176 +    },
   1.177 +    result: '<p id="pass9">pass 9b</p>'
   1.178 +  };},
   1.179 +
   1.180 +  function() { return {
   1.181 +    name: 'asyncInline',
   1.182 +    template: '${delayed}',
   1.183 +    data: { delayed: delayReply('inline') },
   1.184 +    result: '<span></span>',
   1.185 +    later: 'inline'
   1.186 +  };},
   1.187 +
   1.188 +  // Bug 692028: DOMTemplate memory leak with asynchronous arrays
   1.189 +  function() { return {
   1.190 +    name: 'asyncArray',
   1.191 +    template: '<p foreach="i in ${delayed}">${i}</p>',
   1.192 +    data: { delayed: delayReply([1, 2, 3]) },
   1.193 +    result: '<span></span>',
   1.194 +    later: '<p>1</p><p>2</p><p>3</p>'
   1.195 +  };},
   1.196 +
   1.197 +  function() { return {
   1.198 +    name: 'asyncMember',
   1.199 +    template: '<p foreach="i in ${delayed}">${i}</p>',
   1.200 +    data: { delayed: [delayReply(4), delayReply(5), delayReply(6)] },
   1.201 +    result: '<span></span><span></span><span></span>',
   1.202 +    later: '<p>4</p><p>5</p><p>6</p>'
   1.203 +  };},
   1.204 +
   1.205 +  // Bug 692028: DOMTemplate memory leak with asynchronous arrays
   1.206 +  function() { return {
   1.207 +    name: 'asyncBoth',
   1.208 +    template: '<p foreach="i in ${delayed}">${i}</p>',
   1.209 +    data: {
   1.210 +      delayed: delayReply([
   1.211 +        delayReply(4),
   1.212 +        delayReply(5),
   1.213 +        delayReply(6)
   1.214 +      ])
   1.215 +    },
   1.216 +    result: '<span></span>',
   1.217 +    later: '<p>4</p><p>5</p><p>6</p>'
   1.218 +  };},
   1.219 +
   1.220 +  // Bug 701762: DOMTemplate fails when ${foo()} returns undefined
   1.221 +  function() { return {
   1.222 +    name: 'functionReturningUndefiend',
   1.223 +    template: '<p>${foo()}</p>',
   1.224 +    options: { allowEval: true },
   1.225 +    data: {
   1.226 +      foo: function() {}
   1.227 +    },
   1.228 +    result: '<p>undefined</p>'
   1.229 +  };},
   1.230 +
   1.231 +  // Bug 702642: DOMTemplate is relatively slow when evaluating JS ${}
   1.232 +  function() { return {
   1.233 +    name: 'propertySimple',
   1.234 +    template: '<p>${a.b.c}</p>',
   1.235 +    data: { a: { b: { c: 'hello' } } },
   1.236 +    result: '<p>hello</p>'
   1.237 +  };},
   1.238 +
   1.239 +  function() { return {
   1.240 +    name: 'propertyPass',
   1.241 +    template: '<p>${Math.max(1, 2)}</p>',
   1.242 +    options: { allowEval: true },
   1.243 +    result: '<p>2</p>'
   1.244 +  };},
   1.245 +
   1.246 +  function() { return {
   1.247 +    name: 'propertyFail',
   1.248 +    template: '<p>${Math.max(1, 2)}</p>',
   1.249 +    result: '<p>${Math.max(1, 2)}</p>'
   1.250 +  };},
   1.251 +
   1.252 +  // Bug 723431: DOMTemplate should allow customisation of display of
   1.253 +  // null/undefined values
   1.254 +  function() { return {
   1.255 +    name: 'propertyUndefAttrFull',
   1.256 +    template: '<p>${nullvar}|${undefinedvar1}|${undefinedvar2}</p>',
   1.257 +    data: { nullvar: null, undefinedvar1: undefined },
   1.258 +    result: '<p>null|undefined|undefined</p>'
   1.259 +  };},
   1.260 +
   1.261 +  function() { return {
   1.262 +    name: 'propertyUndefAttrBlank',
   1.263 +    template: '<p>${nullvar}|${undefinedvar1}|${undefinedvar2}</p>',
   1.264 +    data: { nullvar: null, undefinedvar1: undefined },
   1.265 +    options: { blankNullUndefined: true },
   1.266 +    result: '<p>||</p>'
   1.267 +  };},
   1.268 +
   1.269 +  function() { return {
   1.270 +    name: 'propertyUndefAttrFull',
   1.271 +    template: '<div><p value="${nullvar}"></p><p value="${undefinedvar1}"></p><p value="${undefinedvar2}"></p></div>',
   1.272 +    data: { nullvar: null, undefinedvar1: undefined },
   1.273 +    result: '<div><p value="null"></p><p value="undefined"></p><p value="undefined"></p></div>'
   1.274 +  };},
   1.275 +
   1.276 +  function() { return {
   1.277 +    name: 'propertyUndefAttrBlank',
   1.278 +    template: '<div><p value="${nullvar}"></p><p value="${undefinedvar1}"></p><p value="${undefinedvar2}"></p></div>',
   1.279 +    data: { nullvar: null, undefinedvar1: undefined },
   1.280 +    options: { blankNullUndefined: true },
   1.281 +    result: '<div><p value=""></p><p value=""></p><p value=""></p></div>'
   1.282 +  };}
   1.283 +];
   1.284 +
   1.285 +function delayReply(data) {
   1.286 +  var d = promise.defer();
   1.287 +  executeSoon(function() {
   1.288 +    d.resolve(data);
   1.289 +  });
   1.290 +  return d.promise;
   1.291 +}

mercurial