content/xul/templates/tests/chrome/templates_shared.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /**
michael@0 2 * This script is used for testing XUL templates. Call test_template within
michael@0 3 * a load event handler.
michael@0 4 *
michael@0 5 * A test should have a root node with the datasources attribute with the
michael@0 6 * id 'root', and a few global variables defined in the test's XUL file:
michael@0 7 *
michael@0 8 * testid: the testid, used when outputting test results
michael@0 9 * expectedOutput: e4x data containing the expected output. It can optionally
michael@0 10 * be enclosed in an <output> element as most tests generate
michael@0 11 * more than one node of output.
michael@0 12 * isTreeBuilder: true for dont-build-content trees, false otherwise
michael@0 13 * queryType: 'rdf', 'xml', etc.
michael@0 14 * needsOpen: true for menu tests where the root menu must be opened before
michael@0 15 * comparing results
michael@0 16 * notWorkingYet: true if this test isn't working yet, outputs todo results
michael@0 17 * notWorkingYetDynamic: true if the dynamic changes portion of the test
michael@0 18 * isn't working yet, outputs todo results
michael@0 19 * changes: an array of functions to perform in sequence to test dynamic changes
michael@0 20 * to the datasource.
michael@0 21 *
michael@0 22 * If the <output> element has an unordered attribute set to true, the
michael@0 23 * children within it must all appear to match, but may appear in any order.
michael@0 24 * If the unordered attribute is not set, the children must appear in the same
michael@0 25 * order.
michael@0 26 *
michael@0 27 * If the 'changes' array is used, it should be an array of functions. Each
michael@0 28 * function will be called in order and a comparison of the output will be
michael@0 29 * performed. This allows changes to be made to the datasource to ensure that
michael@0 30 * the generated template output has been updated. Within the expected output
michael@0 31 * XML, the step attribute may be set to a number on an element to indicate
michael@0 32 * that an element only applies before or after a particular change. If step
michael@0 33 * is set to a positive number, that element will only exist after that step in
michael@0 34 * the list of changes made. If step is set to a negative number, that element
michael@0 35 * will only exist until that step. Steps are numbered starting at 1. For
michael@0 36 * example:
michael@0 37 * <label value="Cat"/>
michael@0 38 * <label step="2" value="Dog"/>
michael@0 39 * <label step="-5" value="Mouse"/>
michael@0 40 * The first element will always exist. The second element will only appear
michael@0 41 * after the second change is made. The third element will only appear until
michael@0 42 * the fifth change and it will no longer be present at later steps.
michael@0 43 *
michael@0 44 * If the anyid attribute is set to true on an element in the expected output,
michael@0 45 * then the value of the id attribute on that element is not compared for a
michael@0 46 * match. This is used, for example, for xml datasources, where the ids set on
michael@0 47 * the generated output are pseudo-random.
michael@0 48 */
michael@0 49
michael@0 50 const ZOO_NS = "http://www.some-fictitious-zoo.com/";
michael@0 51 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
michael@0 52 const debug = false;
michael@0 53
michael@0 54 var expectedConsoleMessages = [];
michael@0 55 var expectLoggedMessages = null;
michael@0 56
michael@0 57 try {
michael@0 58 const RDF = Components.classes["@mozilla.org/rdf/rdf-service;1"].
michael@0 59 getService(Components.interfaces.nsIRDFService);
michael@0 60 const ContainerUtils = Components.classes["@mozilla.org/rdf/container-utils;1"].
michael@0 61 getService(Components.interfaces.nsIRDFContainerUtils);
michael@0 62 } catch(ex) { }
michael@0 63
michael@0 64 var xmlDoc;
michael@0 65
michael@0 66 function test_template()
michael@0 67 {
michael@0 68 var root = document.getElementById("root");
michael@0 69
michael@0 70 var ds;
michael@0 71 if (queryType == "rdf" && RDF) {
michael@0 72 var ioService = Components.classes["@mozilla.org/network/io-service;1"].
michael@0 73 getService(Components.interfaces.nsIIOService);
michael@0 74
michael@0 75 var src = window.location.href.replace(/test_tmpl.*xul/, "animals.rdf");
michael@0 76 ds = RDF.GetDataSourceBlocking(src);
michael@0 77
michael@0 78 if (expectLoggedMessages) {
michael@0 79 Components.classes["@mozilla.org/consoleservice;1"].
michael@0 80 getService(Components.interfaces.nsIConsoleService).reset();
michael@0 81 }
michael@0 82
michael@0 83 if (root.getAttribute("datasources") == "rdf:null")
michael@0 84 root.setAttribute("datasources", "animals.rdf");
michael@0 85 }
michael@0 86 else if (queryType == "xml") {
michael@0 87 var src = window.location.href.replace(/test_tmpl.*xul/, "animals.xml");
michael@0 88 xmlDoc = new XMLHttpRequest();
michael@0 89 xmlDoc.open("get", src, false);
michael@0 90 xmlDoc.send(null);
michael@0 91 }
michael@0 92
michael@0 93 // open menus if necessary
michael@0 94 if (needsOpen)
michael@0 95 root.open = true;
michael@0 96
michael@0 97 if (expectLoggedMessages)
michael@0 98 expectLoggedMessages();
michael@0 99
michael@0 100 checkResults(root, 0);
michael@0 101
michael@0 102 if (changes.length) {
michael@0 103 var usedds = ds;
michael@0 104 // within these chrome tests, RDF datasources won't be modifiable unless
michael@0 105 // an in-memory datasource is used instead. Call copyRDFDataSource to
michael@0 106 // copy the datasource.
michael@0 107 if (queryType == "rdf")
michael@0 108 usedds = copyRDFDataSource(root, ds);
michael@0 109 if (needsOpen)
michael@0 110 root.open = true;
michael@0 111 setTimeout(iterateChanged, 0, root, usedds);
michael@0 112 }
michael@0 113 else {
michael@0 114 if (needsOpen)
michael@0 115 root.open = false;
michael@0 116 if (expectedConsoleMessages.length)
michael@0 117 compareConsoleMessages();
michael@0 118 SimpleTest.finish();
michael@0 119 }
michael@0 120 }
michael@0 121
michael@0 122 function iterateChanged(root, ds)
michael@0 123 {
michael@0 124 Components.classes["@mozilla.org/consoleservice;1"].
michael@0 125 getService(Components.interfaces.nsIConsoleService).reset();
michael@0 126
michael@0 127 for (var c = 0; c < changes.length; c++) {
michael@0 128 changes[c](ds, root);
michael@0 129 checkResults(root, c + 1);
michael@0 130 }
michael@0 131
michael@0 132 if (needsOpen)
michael@0 133 root.open = false;
michael@0 134 if (expectedConsoleMessages.length)
michael@0 135 compareConsoleMessages();
michael@0 136 SimpleTest.finish();
michael@0 137 }
michael@0 138
michael@0 139 function checkResults(root, step)
michael@0 140 {
michael@0 141 var output = expectedOutput.cloneNode(true);
michael@0 142 setForCurrentStep(output, step);
michael@0 143
michael@0 144 var error;
michael@0 145 var actualoutput = root;
michael@0 146 if (isTreeBuilder) {
michael@0 147 // convert the tree's view data into the equivalent DOM structure
michael@0 148 // for easier comparison
michael@0 149 actualoutput = treeViewToDOM(root);
michael@0 150 var treechildrenElements = [e for (e of output.children) if (e.localName === "treechildren")];
michael@0 151 error = compareOutput(actualoutput, treechildrenElements[0], false);
michael@0 152 }
michael@0 153 else {
michael@0 154 error = compareOutput(actualoutput, output, true);
michael@0 155 }
michael@0 156
michael@0 157 var adjtestid = testid;
michael@0 158 if (step > 0)
michael@0 159 adjtestid += " dynamic step " + step;
michael@0 160
michael@0 161 var stilltodo = ((step == 0 && notWorkingYet) || (step > 0 && notWorkingYetDynamic));
michael@0 162 if (stilltodo)
michael@0 163 todo(false, adjtestid);
michael@0 164 else
michael@0 165 ok(!error, adjtestid);
michael@0 166
michael@0 167 if ((!stilltodo && error) || debug) {
michael@0 168 // for debugging, serialize the XML output
michael@0 169 var serializedXML = "";
michael@0 170 var rootNodes = actualoutput.childNodes;
michael@0 171 for (var n = 0; n < rootNodes.length; n++) {
michael@0 172 var node = rootNodes[n];
michael@0 173 if (node.localName != "template")
michael@0 174 serializedXML += ((new XMLSerializer()).serializeToString(node));
michael@0 175 }
michael@0 176
michael@0 177 // remove the XUL namespace declarations to make the output more readable
michael@0 178 const nsrepl = new RegExp("xmlns=\"" + XUL_NS + "\" ", "g");
michael@0 179 serializedXML = serializedXML.replace(nsrepl, "");
michael@0 180 if (debug)
michael@0 181 dump("-------- " + adjtestid + " " + error + ":\n" + serializedXML + "\n");
michael@0 182 if (!stilltodo && error)
michael@0 183 is(serializedXML, "Same", "Error is: " + error);
michael@0 184 }
michael@0 185 }
michael@0 186
michael@0 187 /**
michael@0 188 * Adjust the expected output to acccount for any step attributes.
michael@0 189 */
michael@0 190 function setForCurrentStep(content, currentStep)
michael@0 191 {
michael@0 192 var todelete = [];
michael@0 193 for (var child of content.childNodes) {
michael@0 194 if (child.nodeType === Node.ELEMENT_NODE) {
michael@0 195 var stepstr = child.getAttribute("step") || "";
michael@0 196 var stepsarr = stepstr.split(",");
michael@0 197 for (var s = 0; s < stepsarr.length; s++) {
michael@0 198 var step = parseInt(stepsarr[s]);
michael@0 199 if ((step > 0 && step > currentStep) ||
michael@0 200 (step < 0 && -step <= currentStep)) {
michael@0 201 todelete.push(child);
michael@0 202 }
michael@0 203 }
michael@0 204 } else if (child.nodeType === Node.TEXT_NODE) {
michael@0 205 // Drop empty text nodes.
michael@0 206 if (child.nodeValue.trim() === "")
michael@0 207 todelete.push(child);
michael@0 208 }
michael@0 209 }
michael@0 210
michael@0 211 for (var e of todelete)
michael@0 212 content.removeChild(e);
michael@0 213
michael@0 214 for (var child of content.children) {
michael@0 215 child.removeAttribute("step");
michael@0 216 setForCurrentStep(child, currentStep);
michael@0 217 }
michael@0 218 }
michael@0 219
michael@0 220 /**
michael@0 221 * Compares the 'actual' DOM output with the 'expected' output. This function
michael@0 222 * is called recursively, with isroot true if actual refers to the root of the
michael@0 223 * template. Returns a null string if they are equal and an error string if
michael@0 224 * they are not equal. This function is called recursively as it iterates
michael@0 225 * through each node in the DOM tree.
michael@0 226 */
michael@0 227 function compareOutput(actual, expected, isroot)
michael@0 228 {
michael@0 229 if (isroot && expected.localName != "data")
michael@0 230 return "expected must be a <data> element";
michael@0 231
michael@0 232 var t;
michael@0 233
michael@0 234 // compare text nodes
michael@0 235 if (expected.nodeType == Node.TEXT_NODE) {
michael@0 236 if (actual.nodeValue !== expected.nodeValue.trim())
michael@0 237 return "Text " + actual.nodeValue + " doesn't match " + expected.nodeValue;
michael@0 238 return "";
michael@0 239 }
michael@0 240
michael@0 241 if (!isroot) {
michael@0 242 var anyid = false;
michael@0 243 // make sure that the tags match
michael@0 244 if (actual.localName != expected.localName)
michael@0 245 return "Tag name " + expected.localName + " not found";
michael@0 246
michael@0 247 // loop through the attributes in the expected node and compare their
michael@0 248 // values with the corresponding attribute on the actual node
michael@0 249
michael@0 250 var expectedAttrs = expected.attributes;
michael@0 251 for (var a = 0; a < expectedAttrs.length; a++) {
michael@0 252 var attr = expectedAttrs[a];
michael@0 253 var expectval = attr.value;
michael@0 254 // skip checking the id when anyid="true", however make sure to
michael@0 255 // ensure that the id is actually present.
michael@0 256 if (attr.name == "anyid" && expectval == "true") {
michael@0 257 anyid = true;
michael@0 258 if (!actual.hasAttribute("id"))
michael@0 259 return "expected id attribute";
michael@0 260 }
michael@0 261 else if (actual.getAttribute(attr.name) != expectval) {
michael@0 262 return "attribute " + attr.name + " is '" +
michael@0 263 actual.getAttribute(attr.name) + "' instead of '" + expectval + "'";
michael@0 264 }
michael@0 265 }
michael@0 266
michael@0 267 // now loop through the actual attributes and make sure that there aren't
michael@0 268 // any extra attributes that weren't expected
michael@0 269 var length = actual.attributes.length;
michael@0 270 for (t = 0; t < length; t++) {
michael@0 271 var aattr = actual.attributes[t];
michael@0 272 var expectval = expected.getAttribute(aattr.name);
michael@0 273 // ignore some attributes that don't matter
michael@0 274 if (expectval != actual.getAttribute(aattr.name) &&
michael@0 275 aattr.name != "staticHint" && aattr.name != "xmlns" &&
michael@0 276 (aattr.name != "id" || !anyid))
michael@0 277 return "extra attribute " + aattr.name;
michael@0 278 }
michael@0 279 }
michael@0 280
michael@0 281 // ensure that the node has the right number of children. Subtract one for
michael@0 282 // the root node to account for the <template> node.
michael@0 283 length = actual.childNodes.length - (isroot ? 1 : 0);
michael@0 284 if (length != expected.childNodes.length)
michael@0 285 return "incorrect child node count of " + actual.localName + " " + length +
michael@0 286 " expected " + expected.childNodes.length;
michael@0 287
michael@0 288 // if <data unordered="true"> is used, then the child nodes may be in any order
michael@0 289 var unordered = (expected.localName == "data" && expected.getAttribute("unordered") == "true");
michael@0 290
michael@0 291 // next, loop over the children and call compareOutput recursively on each one
michael@0 292 var adj = 0;
michael@0 293 for (t = 0; t < actual.childNodes.length; t++) {
michael@0 294 var actualnode = actual.childNodes[t];
michael@0 295 // skip the <template> element, and add one to the indices when looking
michael@0 296 // at the later nodes to account for it
michael@0 297 if (isroot && actualnode.localName == "template") {
michael@0 298 adj++;
michael@0 299 }
michael@0 300 else {
michael@0 301 var output = "unexpected";
michael@0 302 if (unordered) {
michael@0 303 var expectedChildren = expected.childNodes;
michael@0 304 for (var e = 0; e < expectedChildren.length; e++) {
michael@0 305 output = compareOutput(actualnode, expectedChildren[e], false);
michael@0 306 if (!output)
michael@0 307 break;
michael@0 308 }
michael@0 309 }
michael@0 310 else {
michael@0 311 output = compareOutput(actualnode, expected.childNodes[t - adj], false);
michael@0 312 }
michael@0 313
michael@0 314 // an error was returned, so return early
michael@0 315 if (output)
michael@0 316 return output;
michael@0 317 }
michael@0 318 }
michael@0 319
michael@0 320 return "";
michael@0 321 }
michael@0 322
michael@0 323 /*
michael@0 324 * copy the datasource into an in-memory datasource so that it can be modified
michael@0 325 */
michael@0 326 function copyRDFDataSource(root, sourceds)
michael@0 327 {
michael@0 328 var dsourcesArr = [];
michael@0 329 var composite = root.database;
michael@0 330 var dsources = composite.GetDataSources();
michael@0 331 while (dsources.hasMoreElements()) {
michael@0 332 sourceds = dsources.getNext().QueryInterface(Components.interfaces.nsIRDFDataSource);
michael@0 333 dsourcesArr.push(sourceds);
michael@0 334 }
michael@0 335
michael@0 336 for (var d = 0; d < dsourcesArr.length; d++)
michael@0 337 composite.RemoveDataSource(dsourcesArr[d]);
michael@0 338
michael@0 339 var newds = Components.classes["@mozilla.org/rdf/datasource;1?name=in-memory-datasource"].
michael@0 340 createInstance(Components.interfaces.nsIRDFDataSource);
michael@0 341
michael@0 342 var sourcelist = sourceds.GetAllResources();
michael@0 343 while (sourcelist.hasMoreElements()) {
michael@0 344 var source = sourcelist.getNext();
michael@0 345 var props = sourceds.ArcLabelsOut(source);
michael@0 346 while (props.hasMoreElements()) {
michael@0 347 var prop = props.getNext();
michael@0 348 if (prop instanceof Components.interfaces.nsIRDFResource) {
michael@0 349 var targets = sourceds.GetTargets(source, prop, true);
michael@0 350 while (targets.hasMoreElements())
michael@0 351 newds.Assert(source, prop, targets.getNext(), true);
michael@0 352 }
michael@0 353 }
michael@0 354 }
michael@0 355
michael@0 356 composite.AddDataSource(newds);
michael@0 357 root.builder.rebuild();
michael@0 358
michael@0 359 return newds;
michael@0 360 }
michael@0 361
michael@0 362 /**
michael@0 363 * Converts a tree view (nsITreeView) into the equivalent DOM tree.
michael@0 364 * Returns the treechildren
michael@0 365 */
michael@0 366 function treeViewToDOM(tree)
michael@0 367 {
michael@0 368 var treechildren = document.createElement("treechildren");
michael@0 369
michael@0 370 if (tree.view)
michael@0 371 treeViewToDOMInner(tree.columns, treechildren, tree.view, tree.builder, 0, 0);
michael@0 372
michael@0 373 return treechildren;
michael@0 374 }
michael@0 375
michael@0 376 function treeViewToDOMInner(columns, treechildren, view, builder, start, level)
michael@0 377 {
michael@0 378 var end = view.rowCount;
michael@0 379
michael@0 380 for (var i = start; i < end; i++) {
michael@0 381 if (view.getLevel(i) < level)
michael@0 382 return i - 1;
michael@0 383
michael@0 384 var id = builder ? builder.getResourceAtIndex(i).Value : "id" + i;
michael@0 385 var item = document.createElement("treeitem");
michael@0 386 item.setAttribute("id", id);
michael@0 387 treechildren.appendChild(item);
michael@0 388
michael@0 389 var row = document.createElement("treerow");
michael@0 390 item.appendChild(row);
michael@0 391
michael@0 392 for (var c = 0; c < columns.length; c++) {
michael@0 393 var cell = document.createElement("treecell");
michael@0 394 var label = view.getCellText(i, columns[c]);
michael@0 395 if (label)
michael@0 396 cell.setAttribute("label", label);
michael@0 397 row.appendChild(cell);
michael@0 398 }
michael@0 399
michael@0 400 if (view.isContainer(i)) {
michael@0 401 item.setAttribute("container", "true");
michael@0 402 item.setAttribute("empty", view.isContainerEmpty(i) ? "true" : "false");
michael@0 403
michael@0 404 if (!view.isContainerEmpty(i) && view.isContainerOpen(i)) {
michael@0 405 item.setAttribute("open", "true");
michael@0 406
michael@0 407 var innertreechildren = document.createElement("treechildren");
michael@0 408 item.appendChild(innertreechildren);
michael@0 409
michael@0 410 i = treeViewToDOMInner(columns, innertreechildren, view, builder, i + 1, level + 1);
michael@0 411 }
michael@0 412 }
michael@0 413 }
michael@0 414
michael@0 415 return i;
michael@0 416 }
michael@0 417
michael@0 418 function expectConsoleMessage(ref, id, isNew, isActive, extra)
michael@0 419 {
michael@0 420 var message = "In template with id root" +
michael@0 421 (ref ? " using ref " + ref : "") + "\n " +
michael@0 422 (isNew ? "New " : "Removed ") + (isActive ? "active" : "inactive") +
michael@0 423 " result for query " + extra + ": " + id;
michael@0 424 expectedConsoleMessages.push(message);
michael@0 425 }
michael@0 426
michael@0 427 function compareConsoleMessages()
michael@0 428 {
michael@0 429 var consoleService = Components.classes["@mozilla.org/consoleservice;1"].
michael@0 430 getService(Components.interfaces.nsIConsoleService);
michael@0 431 var messages = consoleService.getMessageArray() || [];
michael@0 432 messages = messages.map(function (m) m.message);
michael@0 433 // Copy to avoid modifying expectedConsoleMessages
michael@0 434 var expect = expectedConsoleMessages.concat();
michael@0 435 for (var m = 0; m < messages.length; m++) {
michael@0 436 if (messages[m] == expect[0]) {
michael@0 437 ok(true, "found message " + expect.shift());
michael@0 438 }
michael@0 439 }
michael@0 440 if (expect.length != 0) {
michael@0 441 ok(false, "failed to find expected console messages: " + expect);
michael@0 442 }
michael@0 443 }
michael@0 444
michael@0 445 function copyToProfile(filename)
michael@0 446 {
michael@0 447 if (Cc === undefined) {
michael@0 448 var Cc = Components.classes;
michael@0 449 var Ci = Components.interfaces;
michael@0 450 }
michael@0 451
michael@0 452 var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
michael@0 453 .getService(Ci.mozIJSSubScriptLoader);
michael@0 454 loader.loadSubScript("chrome://mochikit/content/chrome-harness.js");
michael@0 455
michael@0 456 var file = Cc["@mozilla.org/file/directory_service;1"]
michael@0 457 .getService(Ci.nsIProperties)
michael@0 458 .get("ProfD", Ci.nsIFile);
michael@0 459 file.append(filename);
michael@0 460
michael@0 461 var parentURI = getResolvedURI(getRootDirectory(window.location.href));
michael@0 462 if (parentURI.JARFile) {
michael@0 463 parentURI = extractJarToTmp(parentURI);
michael@0 464 } else {
michael@0 465 var fileHandler = Cc["@mozilla.org/network/protocol;1?name=file"].
michael@0 466 getService(Ci.nsIFileProtocolHandler);
michael@0 467 parentURI = fileHandler.getFileFromURLSpec(parentURI.spec);
michael@0 468 }
michael@0 469
michael@0 470 parentURI = parentURI.QueryInterface(Ci.nsILocalFile);
michael@0 471 parentURI.append(filename);
michael@0 472 try {
michael@0 473 var retVal = parentURI.copyToFollowingLinks(file.parent, filename);
michael@0 474 } catch (ex) {
michael@0 475 //ignore this error as the file could exist already
michael@0 476 }
michael@0 477 }

mercurial