summaryrefslogtreecommitdiffstats
path: root/devtools/client/shared/test/browser_templater_basic.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/shared/test/browser_templater_basic.js')
-rw-r--r--devtools/client/shared/test/browser_templater_basic.js286
1 files changed, 286 insertions, 0 deletions
diff --git a/devtools/client/shared/test/browser_templater_basic.js b/devtools/client/shared/test/browser_templater_basic.js
new file mode 100644
index 000000000..256900cf5
--- /dev/null
+++ b/devtools/client/shared/test/browser_templater_basic.js
@@ -0,0 +1,286 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests that the DOM Template engine works properly
+
+/*
+ * These tests run both in Mozilla/Mochitest and plain browsers (as does
+ * domtemplate)
+ * We should endevour to keep the source in sync.
+ */
+
+const {template} = require("devtools/shared/gcli/templater");
+
+const TEST_URI = TEST_URI_ROOT + "browser_templater_basic.html";
+
+var test = Task.async(function* () {
+ yield addTab("about:blank");
+ let [host,, doc] = yield createHost("bottom", TEST_URI);
+
+ info("Starting DOM Templater Tests");
+ runTest(0, host, doc);
+});
+
+function runTest(index, host, doc) {
+ let options = tests[index] = tests[index]();
+ let holder = doc.createElement("div");
+ holder.id = options.name;
+ let body = doc.body;
+ body.appendChild(holder);
+ holder.innerHTML = options.template;
+
+ info("Running " + options.name);
+ template(holder, options.data, options.options);
+
+ if (typeof options.result == "string") {
+ is(holder.innerHTML, options.result, options.name);
+ } else {
+ ok(holder.innerHTML.match(options.result) != null,
+ options.name + " result='" + holder.innerHTML + "'");
+ }
+
+ if (options.also) {
+ options.also(options);
+ }
+
+ function runNextTest() {
+ index++;
+ if (index < tests.length) {
+ runTest(index, host, doc);
+ } else {
+ finished(host);
+ }
+ }
+
+ if (options.later) {
+ let ais = is.bind(this);
+
+ function createTester(testHolder, testOptions) {
+ return () => {
+ ais(testHolder.innerHTML, testOptions.later, testOptions.name + " later");
+ runNextTest();
+ };
+ }
+
+ executeSoon(createTester(holder, options));
+ } else {
+ runNextTest();
+ }
+}
+
+function finished(host) {
+ host.destroy();
+ gBrowser.removeCurrentTab();
+ info("Finishing DOM Templater Tests");
+ tests = null;
+ finish();
+}
+
+/**
+ * Why have an array of functions that return data rather than just an array
+ * of the data itself? Some of these tests contain calls to delayReply() which
+ * sets up async processing using executeSoon(). Since the execution of these
+ * tests is asynchronous, the delayed reply will probably arrive before the
+ * test is executed, making the test be synchronous. So we wrap the data in a
+ * function so we only set it up just before we use it.
+ */
+var tests = [
+ () => ({
+ name: "simpleNesting",
+ template: '<div id="ex1">${nested.value}</div>',
+ data: { nested: { value: "pass 1" } },
+ result: '<div id="ex1">pass 1</div>'
+ }),
+
+ () => ({
+ name: "returnDom",
+ template: '<div id="ex2">${__element.ownerDocument.createTextNode(\'pass 2\')}</div>',
+ options: { allowEval: true },
+ data: {},
+ result: '<div id="ex2">pass 2</div>'
+ }),
+
+ () => ({
+ name: "srcChange",
+ template: '<img _src="${fred}" id="ex3">',
+ data: { fred: "green.png" },
+ result: /<img( id="ex3")? src="green.png"( id="ex3")?>/
+ }),
+
+ () => ({
+ name: "ifTrue",
+ template: '<p if="${name !== \'jim\'}">hello ${name}</p>',
+ options: { allowEval: true },
+ data: { name: "fred" },
+ result: "<p>hello fred</p>"
+ }),
+
+ () => ({
+ name: "ifFalse",
+ template: '<p if="${name !== \'jim\'}">hello ${name}</p>',
+ options: { allowEval: true },
+ data: { name: "jim" },
+ result: ""
+ }),
+
+ () => ({
+ name: "simpleLoop",
+ template: '<p foreach="index in ${[ 1, 2, 3 ]}">${index}</p>',
+ options: { allowEval: true },
+ data: {},
+ result: "<p>1</p><p>2</p><p>3</p>"
+ }),
+
+ () => ({
+ name: "loopElement",
+ template: '<loop foreach="i in ${array}">${i}</loop>',
+ data: { array: [ 1, 2, 3 ] },
+ result: "123"
+ }),
+
+ // Bug 692028: DOMTemplate memory leak with asynchronous arrays
+ // Bug 692031: DOMTemplate async loops do not drop the loop element
+ () => ({
+ name: "asyncLoopElement",
+ template: '<loop foreach="i in ${array}">${i}</loop>',
+ data: { array: delayReply([1, 2, 3]) },
+ result: "<span></span>",
+ later: "123"
+ }),
+
+ () => ({
+ name: "saveElement",
+ template: '<p save="${element}">${name}</p>',
+ data: { name: "pass 8" },
+ result: "<p>pass 8</p>",
+ also: function (options) {
+ ok(options.data.element.innerHTML, "pass 9", "saveElement saved");
+ delete options.data.element;
+ }
+ }),
+
+ () => ({
+ name: "useElement",
+ template: '<p id="pass9">${adjust(__element)}</p>',
+ options: { allowEval: true },
+ data: {
+ adjust: function (element) {
+ is("pass9", element.id, "useElement adjust");
+ return "pass 9b";
+ }
+ },
+ result: '<p id="pass9">pass 9b</p>'
+ }),
+
+ () => ({
+ name: "asyncInline",
+ template: "${delayed}",
+ data: { delayed: delayReply("inline") },
+ result: "<span></span>",
+ later: "inline"
+ }),
+
+ // Bug 692028: DOMTemplate memory leak with asynchronous arrays
+ () => ({
+ name: "asyncArray",
+ template: '<p foreach="i in ${delayed}">${i}</p>',
+ data: { delayed: delayReply([1, 2, 3]) },
+ result: "<span></span>",
+ later: "<p>1</p><p>2</p><p>3</p>"
+ }),
+
+ () => ({
+ name: "asyncMember",
+ template: '<p foreach="i in ${delayed}">${i}</p>',
+ data: { delayed: [delayReply(4), delayReply(5), delayReply(6)] },
+ result: "<span></span><span></span><span></span>",
+ later: "<p>4</p><p>5</p><p>6</p>"
+ }),
+
+ // Bug 692028: DOMTemplate memory leak with asynchronous arrays
+ () => ({
+ name: "asyncBoth",
+ template: '<p foreach="i in ${delayed}">${i}</p>',
+ data: {
+ delayed: delayReply([
+ delayReply(4),
+ delayReply(5),
+ delayReply(6)
+ ])
+ },
+ result: "<span></span>",
+ later: "<p>4</p><p>5</p><p>6</p>"
+ }),
+
+ // Bug 701762: DOMTemplate fails when ${foo()} returns undefined
+ () => ({
+ name: "functionReturningUndefiend",
+ template: "<p>${foo()}</p>",
+ options: { allowEval: true },
+ data: {
+ foo: function () {}
+ },
+ result: "<p>undefined</p>"
+ }),
+
+ // Bug 702642: DOMTemplate is relatively slow when evaluating JS ${}
+ () => ({
+ name: "propertySimple",
+ template: "<p>${a.b.c}</p>",
+ data: { a: { b: { c: "hello" } } },
+ result: "<p>hello</p>"
+ }),
+
+ () => ({
+ name: "propertyPass",
+ template: "<p>${Math.max(1, 2)}</p>",
+ options: { allowEval: true },
+ result: "<p>2</p>"
+ }),
+
+ () => ({
+ name: "propertyFail",
+ template: "<p>${Math.max(1, 2)}</p>",
+ result: "<p>${Math.max(1, 2)}</p>"
+ }),
+
+ // Bug 723431: DOMTemplate should allow customisation of display of
+ // null/undefined values
+ () => ({
+ name: "propertyUndefAttrFull",
+ template: "<p>${nullvar}|${undefinedvar1}|${undefinedvar2}</p>",
+ data: { nullvar: null, undefinedvar1: undefined },
+ result: "<p>null|undefined|undefined</p>"
+ }),
+
+ () => ({
+ name: "propertyUndefAttrBlank",
+ template: "<p>${nullvar}|${undefinedvar1}|${undefinedvar2}</p>",
+ data: { nullvar: null, undefinedvar1: undefined },
+ options: { blankNullUndefined: true },
+ result: "<p>||</p>"
+ }),
+
+ /* eslint-disable max-len */
+ () => ({
+ name: "propertyUndefAttrFull",
+ template: '<div><p value="${nullvar}"></p><p value="${undefinedvar1}"></p><p value="${undefinedvar2}"></p></div>',
+ data: { nullvar: null, undefinedvar1: undefined },
+ result: '<div><p value="null"></p><p value="undefined"></p><p value="undefined"></p></div>'
+ }),
+
+ () => ({
+ name: "propertyUndefAttrBlank",
+ template: '<div><p value="${nullvar}"></p><p value="${undefinedvar1}"></p><p value="${undefinedvar2}"></p></div>',
+ data: { nullvar: null, undefinedvar1: undefined },
+ options: { blankNullUndefined: true },
+ result: '<div><p value=""></p><p value=""></p><p value=""></p></div>'
+ })
+ /* eslint-enable max-len */
+];
+
+function delayReply(data) {
+ return new Promise(resolve => resolve(data));
+}