1 /* -*- js-indent-level: 2; tab-width: 2; indent-tabs-mode: nil -*- */
2 /* vim:set ts=2 sw=2 sts=2 et: */
4 // Generally gTestPath should be set by the harness.
8 * SimpleTest framework object.
12 var parentRunner = null;
14 // Using a try/catch rather than SpecialPowers.Cu.isRemoteProxy() because
15 // it doesn't cover the case where an iframe is xorigin but fission is
17 let isSameOrigin = function (w) {
21 if (e instanceof DOMException) {
27 let isXOrigin = !isSameOrigin(window);
29 // Note: duplicated in browser-test.js . See also bug 1820150.
30 function isErrorOrException(err) {
31 // It'd be nice if we had either `Error.isError(err)` or `Error.isInstance(err)`
32 // but we don't, so do it ourselves:
36 if (err instanceof SpecialPowers.Ci.nsIException) {
40 let glob = SpecialPowers.Cu.getGlobalForObject(err);
41 return err instanceof glob.Error;
43 // getGlobalForObject can be upset if it doesn't get passed an object.
44 // Just do a standard instanceof check using this global and cross fingers:
46 return err instanceof Error;
49 // In normal test runs, the window that has a TestRunner in its parent is
50 // the primary window. In single test runs, if there is no parent and there
51 // is no opener then it is the primary window.
54 !(opener || (window.arguments && window.arguments[0].SimpleTest));
56 var isPrimaryTestWindow =
57 (isXOrigin && parent != window && parent == top) ||
58 (!isXOrigin && (!!parent.TestRunner || isSingleTestRun));
61 "TEST-UNEXPECTED-FAIL, Exception caught: " +
68 window.location.href +
75 this.harnessWindow = harnessWindow;
76 let url = new URL(document.URL);
77 this.testFile = url.pathname;
78 this.showTestReport = url.searchParams.get("showTestReport") == "true";
79 this.expected = url.searchParams.get("expected");
81 callHarnessMethod(applyOn, command, ...params) {
82 // Message handled by xOriginTestRunnerHandler in TestRunner.js
83 this.harnessWindow.postMessage(
85 harnessType: "SimpleTest",
94 let url = new URL(document.URL);
96 currentTestURL: url.searchParams.get("currentTestURL"),
97 testRoot: url.searchParams.get("testRoot"),
100 addFailedTest(test) {
101 this.callHarnessMethod("runner", "addFailedTest", test);
103 expectAssertions(min, max) {
104 this.callHarnessMethod("runner", "expectAssertions", min, max);
106 expectChildProcessCrash() {
107 this.callHarnessMethod("runner", "expectChildProcessCrash");
109 requestLongerTimeout(factor) {
110 this.callHarnessMethod("runner", "requestLongerTimeout", factor);
112 _lastAssertionCount: 0,
113 testFinished(tests) {
114 var newAssertionCount = SpecialPowers.assertionCount();
115 var numAsserts = newAssertionCount - this._lastAssertionCount;
116 this._lastAssertionCount = newAssertionCount;
117 this.callHarnessMethod("runner", "addAssertionCount", numAsserts);
118 this.callHarnessMethod("runner", "testFinished", tests);
122 xOriginRunner.callHarnessMethod("logger", "structuredLogger.info", msg);
125 xOriginRunner.callHarnessMethod(
127 "structuredLogger.warning",
132 xOriginRunner.callHarnessMethod("logger", "structuredLogger.error", msg);
134 activateBuffering() {
135 xOriginRunner.callHarnessMethod(
137 "structuredLogger.activateBuffering"
140 deactivateBuffering() {
141 xOriginRunner.callHarnessMethod(
143 "structuredLogger.deactivateBuffering"
146 testStatus(url, subtest, status, expected, diagnostic, stack) {
147 xOriginRunner.callHarnessMethod(
149 "structuredLogger.testStatus",
161 // Finds the TestRunner for this test run and the SpecialPowers object (in
162 // case it is not defined) from a parent/opener window.
164 // Finding the SpecialPowers object is needed when we have ChromePowers in
165 // harness.xhtml and we need SpecialPowers in the iframe, and also for tests
166 // like test_focus.xhtml where we open a window which opens another window which
167 // includes SimpleTest.js.
169 function ancestor(w) {
175 SpecialPowers.wrap(Window).isInstance(w.arguments[0]) &&
179 var w = ancestor(window);
180 while (w && !parentRunner) {
181 isXOrigin = !isSameOrigin(w);
187 xOriginRunner.init(w);
188 parentRunner = xOriginRunner;
192 parentRunner = w.TestRunner;
193 if (!parentRunner && w.wrappedJSObject) {
194 parentRunner = w.wrappedJSObject.TestRunner;
201 SimpleTest.harnessParameters = parentRunner.getParameterInfo();
205 /* Helper functions pulled out of various MochiKit modules */
206 if (typeof repr == "undefined") {
207 this.repr = function repr(o) {
208 if (typeof o == "undefined") {
210 } else if (o === null) {
214 if (typeof o.__repr__ == "function") {
216 } else if (typeof o.repr == "function" && o.repr != repr) {
222 typeof o.NAME == "string" &&
223 (o.toString == Function.prototype.toString ||
224 o.toString == Object.prototype.toString)
232 ostring = 1 / o > 0 ? "+0" : "-0";
233 } else if (typeof o === "string") {
234 ostring = JSON.stringify(o);
235 } else if (Array.isArray(o)) {
236 ostring = "[" + o.map(val => repr(val)).join(", ") + "]";
241 return "[" + typeof o + "]";
243 if (typeof o == "function") {
244 o = ostring.replace(/^\s+/, "");
245 var idx = o.indexOf("{");
247 o = o.substr(0, idx) + "{...}";
254 /* This returns a function that applies the previously given parameters.
255 * This is used by SimpleTest.showReport
257 if (typeof partial == "undefined") {
258 this.partial = function (func) {
260 for (let i = 1; i < arguments.length; i++) {
261 args.push(arguments[i]);
264 if (arguments.length) {
265 for (let i = 1; i < arguments.length; i++) {
266 args.push(arguments[i]);
274 if (typeof getElement == "undefined") {
275 this.getElement = function (id) {
276 return typeof id == "string" ? document.getElementById(id) : id;
278 this.$ = this.getElement;
281 SimpleTest._newCallStack = function (path) {
282 var rval = function callStackHandler() {
283 var callStack = callStackHandler.callStack;
284 for (var i = 0; i < callStack.length; i++) {
285 if (callStack[i].apply(this, arguments) === false) {
299 if (typeof addLoadEvent == "undefined") {
300 this.addLoadEvent = function (func) {
301 var existing = window.onload;
302 var regfunc = existing;
305 typeof existing == "function" &&
306 typeof existing.callStack == "object" &&
307 existing.callStack !== null
310 regfunc = SimpleTest._newCallStack("onload");
311 if (typeof existing == "function") {
312 regfunc.callStack.push(existing);
314 window.onload = regfunc;
316 regfunc.callStack.push(func);
320 function createEl(type, attrs, html) {
321 //use createElementNS so the xul/xhtml tests have no issues
323 if (!document.body) {
324 el = document.createElementNS("http://www.w3.org/1999/xhtml", type);
326 el = document.createElement(type);
328 if (attrs !== null && attrs !== undefined) {
329 for (var k in attrs) {
330 el.setAttribute(k, attrs[k]);
333 if (html !== null && html !== undefined) {
334 el.appendChild(document.createTextNode(html));
339 /* lots of tests use this as a helper to get css properties */
340 if (typeof computedStyle == "undefined") {
341 this.computedStyle = function (elem, cssProperty) {
342 elem = getElement(elem);
343 if (elem.currentStyle) {
344 return elem.currentStyle[cssProperty];
346 if (typeof document.defaultView == "undefined" || document === null) {
349 var style = document.defaultView.getComputedStyle(elem);
350 if (typeof style == "undefined" || style === null) {
354 var selectorCase = cssProperty.replace(/([A-Z])/g, "-$1").toLowerCase();
356 return style.getPropertyValue(selectorCase);
360 SimpleTest._tests = [];
361 SimpleTest._stopOnLoad = true;
362 SimpleTest._cleanupFunctions = [];
363 SimpleTest._taskCleanupFunctions = [];
364 SimpleTest._currentTask = null;
365 SimpleTest._timeoutFunctions = [];
366 SimpleTest._inChaosMode = false;
367 // When using failure pattern file to filter unexpected issues,
368 // SimpleTest.expected would be an array of [pattern, expected count],
369 // and SimpleTest.num_failed would be an array of actual counts which
370 // has the same length as SimpleTest.expected.
371 SimpleTest.expected = "pass";
372 SimpleTest.num_failed = 0;
374 SpecialPowers.setAsDefaultAssertHandler();
376 function usesFailurePatterns() {
377 return Array.isArray(SimpleTest.expected);
381 * Checks whether there is any failure pattern matches the given error
382 * message, and if found, bumps the counter of the failure pattern.
384 * @return {boolean} Whether a matched failure pattern is found.
386 function recordIfMatchesFailurePattern(name, diag) {
387 let index = SimpleTest.expected.findIndex(([pat, count]) => {
390 (typeof name == "string" && name.includes(pat)) ||
391 (typeof diag == "string" && diag.includes(pat))
395 SimpleTest.num_failed[index]++;
401 SimpleTest.setExpected = function () {
405 if (!Array.isArray(parentRunner.expected)) {
406 SimpleTest.expected = parentRunner.expected;
408 // Assertions are checked by the runner.
409 SimpleTest.expected = parentRunner.expected.filter(
410 ([pat]) => pat != "ASSERTION"
412 SimpleTest.num_failed = new Array(SimpleTest.expected.length);
413 SimpleTest.num_failed.fill(0);
416 SimpleTest.setExpected();
419 * Something like assert.
421 SimpleTest.ok = function (condition, name) {
422 if (arguments.length > 2) {
423 const diag = "Too many arguments passed to `ok(condition, name)`";
424 SimpleTest.record(false, name, diag);
426 SimpleTest.record(condition, name);
430 SimpleTest.record = function (condition, name, diag, stack, expected) {
431 var test = { result: !!condition, name, diag };
434 if (SimpleTest.expected == "fail") {
436 SimpleTest.num_failed++;
442 message: "TEST-PASS",
447 message: "TEST-KNOWN-FAIL",
449 } else if (!test.result && usesFailurePatterns()) {
450 if (recordIfMatchesFailurePattern(name, diag)) {
452 // Add a mark for unexpected failures suppressed by failure pattern.
453 name = "[suppressed] " + name;
458 message: "TEST-KNOWN-FAIL",
463 message: "TEST-UNEXPECTED-FAIL",
465 } else if (expected == "fail") {
469 message: "TEST-UNEXPECTED-PASS",
474 message: "TEST-KNOWN-FAIL",
480 message: "TEST-PASS",
485 message: "TEST-UNEXPECTED-FAIL",
492 stack = new Error().stack
493 .replace(/^(.*@)http:\/\/mochi.test:8888\/tests\//gm, " $1")
496 stack = stack.join("\n");
498 SimpleTest._logResult(test, successInfo, failureInfo, stack);
499 SimpleTest._tests.push(test);
503 * Roughly equivalent to ok(Object.is(a, b), name)
505 SimpleTest.is = function (a, b, name) {
506 // Be lazy and use Object.is til we want to test a browser without it.
507 var pass = Object.is(a, b);
508 var diag = pass ? "" : "got " + repr(a) + ", expected " + repr(b);
509 SimpleTest.record(pass, name, diag);
512 SimpleTest.isfuzzy = function (a, b, epsilon, name) {
513 var pass = a >= b - epsilon && a <= b + epsilon;
522 SimpleTest.record(pass, name, diag);
525 SimpleTest.isnot = function (a, b, name) {
526 var pass = !Object.is(a, b);
527 var diag = pass ? "" : "didn't expect " + repr(a) + ", but got it";
528 SimpleTest.record(pass, name, diag);
532 * Check that the function call throws an exception.
534 SimpleTest.doesThrow = function (fn, name) {
535 var gotException = false;
541 ok(gotException, name);
544 // --------------- Test.Builder/Test.More todo() -----------------
546 SimpleTest.todo = function (condition, name, diag) {
547 var test = { result: !!condition, name, diag, todo: true };
550 usesFailurePatterns() &&
551 recordIfMatchesFailurePattern(name, diag)
553 // Flipping the result to false so we don't get unexpected result. There
554 // is no perfect way here. A known failure can trigger unexpected pass,
555 // in which case, tagging it as KNOWN-FAIL probably makes more sense than
558 // Add a mark for unexpected failures suppressed by failure pattern.
559 name = "[suppressed] " + name;
564 message: "TEST-UNEXPECTED-PASS",
569 message: "TEST-KNOWN-FAIL",
571 SimpleTest._logResult(test, successInfo, failureInfo);
572 SimpleTest._tests.push(test);
576 * Returns the absolute URL to a test data file from where tests
577 * are served. i.e. the file doesn't necessarely exists where tests
580 * (For android, mochitest are executed on the device, while
581 * all mochitest html (and others) files are served from the test runner
584 SimpleTest.getTestFileURL = function (path) {
585 var location = window.location;
586 // Remove mochitest html file name from the path
587 var remotePath = location.pathname.replace(/\/[^\/]+?$/, "");
588 var url = location.origin + remotePath + "/" + path;
592 SimpleTest._getCurrentTestURL = function () {
594 (SimpleTest.harnessParameters &&
595 SimpleTest.harnessParameters.currentTestURL) ||
596 (parentRunner && parentRunner.currentTestURL) ||
597 (typeof gTestPath == "string" && gTestPath) ||
602 SimpleTest._forceLogMessageOutput = false;
605 * Force all test messages to be displayed. Only applies for the current test.
607 SimpleTest.requestCompleteLog = function () {
608 if (!parentRunner || SimpleTest._forceLogMessageOutput) {
612 parentRunner.structuredLogger.deactivateBuffering();
613 SimpleTest._forceLogMessageOutput = true;
615 SimpleTest.registerCleanupFunction(function () {
616 parentRunner.structuredLogger.activateBuffering();
617 SimpleTest._forceLogMessageOutput = false;
621 SimpleTest._logResult = function (test, passInfo, failInfo, stack) {
622 var url = SimpleTest._getCurrentTestURL();
623 var result = test.result ? passInfo : failInfo;
624 var diagnostic = test.diag || null;
625 // BUGFIX : coercing test.name to a string, because some a11y tests pass an xpconnect object
626 var subtest = test.name ? String(test.name) : null;
627 var isError = !test.result == !test.todo;
630 if (!result.status || !result.expected) {
632 parentRunner.structuredLogger.info(diagnostic);
638 parentRunner.addFailedTest(url);
641 parentRunner.structuredLogger.testStatus(
649 } else if (typeof dump === "function") {
650 var diagMessage = test.name + (test.diag ? " - " + test.diag : "");
651 var debugMsg = [result.message, url, diagMessage].join(" | ");
652 dump(debugMsg + "\n");
654 // Non-Mozilla browser? Just do nothing.
658 SimpleTest.info = function (name, message) {
659 var log = message ? name + " | " + message : name;
661 parentRunner.structuredLogger.info(log);
668 * Copies of is and isnot with the call to ok replaced by a call to todo.
671 SimpleTest.todo_is = function (a, b, name) {
672 var pass = Object.is(a, b);
674 ? repr(a) + " should equal " + repr(b)
675 : "got " + repr(a) + ", expected " + repr(b);
676 SimpleTest.todo(pass, name, diag);
679 SimpleTest.todo_isnot = function (a, b, name) {
680 var pass = !Object.is(a, b);
682 ? repr(a) + " should not equal " + repr(b)
683 : "didn't expect " + repr(a) + ", but got it";
684 SimpleTest.todo(pass, name, diag);
688 * Makes a test report, returns it as a DIV element.
690 SimpleTest.report = function () {
695 var tallyAndCreateDiv = function (test) {
697 var diag = test.diag ? " - " + test.diag : "";
698 if (test.todo && !test.result) {
701 msg = "todo | " + test.name + diag;
702 } else if (test.result && !test.todo) {
705 msg = "passed | " + test.name + diag;
709 msg = "failed | " + test.name + diag;
711 div = createEl("div", { class: cls }, msg);
715 for (var d = 0; d < SimpleTest._tests.length; d++) {
716 results.push(tallyAndCreateDiv(SimpleTest._tests[d]));
720 // eslint-disable-next-line no-nested-ternary
721 failed != 0 ? "some_fail" : passed == 0 ? "todo_only" : "all_pass";
723 var div1 = createEl("div", { class: "tests_report" });
724 var div2 = createEl("div", { class: "tests_summary " + summary_class });
725 var div3 = createEl("div", { class: "tests_passed" }, "Passed: " + passed);
726 var div4 = createEl("div", { class: "tests_failed" }, "Failed: " + failed);
727 var div5 = createEl("div", { class: "tests_todo" }, "Todo: " + todo);
728 div2.appendChild(div3);
729 div2.appendChild(div4);
730 div2.appendChild(div5);
731 div1.appendChild(div2);
732 for (var t = 0; t < results.length; t++) {
734 div1.appendChild(results[t]);
740 * Toggle element visibility
742 SimpleTest.toggle = function (el) {
743 if (computedStyle(el, "display") == "block") {
744 el.style.display = "none";
746 el.style.display = "block";
751 * Toggle visibility for divs with a specific class.
753 SimpleTest.toggleByClass = function (cls, evt) {
754 var children = document.getElementsByTagName("div");
756 for (var i = 0; i < children.length; i++) {
757 var child = children[i];
758 var clsName = child.className;
762 var classNames = clsName.split(" ");
763 for (var j = 0; j < classNames.length; j++) {
764 if (classNames[j] == cls) {
765 elements.push(child);
770 for (var t = 0; t < elements.length; t++) {
771 //TODO: again, for-in loop over elems seems to break this
772 SimpleTest.toggle(elements[t]);
775 evt.preventDefault();
780 * Shows the report in the browser
782 SimpleTest.showReport = function () {
783 var togglePassed = createEl("a", { href: "#" }, "Toggle passed checks");
784 var toggleFailed = createEl("a", { href: "#" }, "Toggle failed checks");
785 var toggleTodo = createEl("a", { href: "#" }, "Toggle todo checks");
786 togglePassed.onclick = partial(SimpleTest.toggleByClass, "test_ok");
787 toggleFailed.onclick = partial(SimpleTest.toggleByClass, "test_not_ok");
788 toggleTodo.onclick = partial(SimpleTest.toggleByClass, "test_todo");
789 var body = document.body; // Handles HTML documents
792 body = document.getElementsByTagNameNS(
793 "http://www.w3.org/1999/xhtml",
797 var firstChild = body.childNodes[0];
800 addNode = function (el) {
801 body.insertBefore(el, firstChild);
804 addNode = function (el) {
805 body.appendChild(el);
808 addNode(togglePassed);
809 addNode(createEl("span", null, " "));
810 addNode(toggleFailed);
811 addNode(createEl("span", null, " "));
813 addNode(SimpleTest.report());
814 // Add a separator from the test content.
815 addNode(createEl("hr"));
819 * Tells SimpleTest to don't finish the test when the document is loaded,
820 * useful for asynchronous tests.
822 * When SimpleTest.waitForExplicitFinish is called,
823 * explicit SimpleTest.finish() is required.
825 SimpleTest.waitForExplicitFinish = function () {
826 SimpleTest._stopOnLoad = false;
830 * Multiply the timeout the parent runner uses for this test by the
833 * For example, in a test that may take a long time to complete, using
834 * "SimpleTest.requestLongerTimeout(5)" will give it 5 times as long to
837 * @param {Number} factor
838 * The multiplication factor to use on the timeout for this test.
840 SimpleTest.requestLongerTimeout = function (factor) {
842 parentRunner.requestLongerTimeout(factor);
845 "[SimpleTest.requestLongerTimeout()] ignoring request, maybe you meant to call the global `requestLongerTimeout` instead?\n"
851 * Note that the given range of assertions is to be expected. When
852 * this function is not called, 0 assertions are expected. When only
853 * one argument is given, that number of assertions are expected.
855 * A test where we expect to have assertions (which should largely be a
856 * transitional mechanism to get assertion counts down from our current
857 * situation) can call the SimpleTest.expectAssertions() function, with
858 * either one or two arguments: one argument gives an exact number
859 * expected, and two arguments give a range. For example, a test might do
860 * one of the following:
864 * // Currently triggers two assertions (bug NNNNNN).
865 * SimpleTest.expectAssertions(2);
867 * // Currently triggers one assertion on Mac (bug NNNNNN).
868 * if (navigator.platform.indexOf("Mac") == 0) {
869 * SimpleTest.expectAssertions(1);
872 * // Currently triggers two assertions on all platforms (bug NNNNNN),
873 * // but intermittently triggers two additional assertions (bug NNNNNN)
875 * if (navigator.platform.indexOf("Win") == 0) {
876 * SimpleTest.expectAssertions(2, 4);
878 * SimpleTest.expectAssertions(2);
881 * // Intermittently triggers up to three assertions (bug NNNNNN).
882 * SimpleTest.expectAssertions(0, 3);
884 SimpleTest.expectAssertions = function (min, max) {
886 parentRunner.expectAssertions(min, max);
890 SimpleTest._flakyTimeoutIsOK = false;
891 SimpleTest._originalSetTimeout = window.setTimeout;
892 window.setTimeout = function SimpleTest_setTimeoutShim() {
893 // Don't break tests that are loaded without a parent runner.
895 // Right now, we only enable these checks for mochitest-plain.
896 switch (SimpleTest.harnessParameters.testRoot) {
903 !SimpleTest._alreadyFinished &&
904 arguments.length > 1 &&
907 if (SimpleTest._flakyTimeoutIsOK) {
910 "The author of the test has indicated that flaky timeouts are expected. Reason: " +
911 SimpleTest._flakyTimeoutReason
916 "Test attempted to use a flaky timeout value " + arguments[1]
922 return SimpleTest._originalSetTimeout.apply(window, arguments);
926 * Request the framework to allow usage of setTimeout(func, timeout)
927 * where ``timeout > 0``. This is required to note that the author of
928 * the test is aware of the inherent flakiness in the test caused by
929 * that, and asserts that there is no way around using the magic timeout
930 * value number for some reason.
932 * Use of this function is **STRONGLY** discouraged. Think twice before
933 * using it. Such magic timeout values could result in intermittent
934 * failures in your test, and are almost never necessary!
936 * @param {String} reason
937 * A string representation of the reason why the test needs timeouts.
940 SimpleTest.requestFlakyTimeout = function (reason) {
941 SimpleTest.is(typeof reason, "string", "A valid string reason is expected");
942 SimpleTest.isnot(reason, "", "Reason cannot be empty");
943 SimpleTest._flakyTimeoutIsOK = true;
944 SimpleTest._flakyTimeoutReason = reason;
948 * If the page is not yet loaded, waits for the load event. If the page is
949 * not yet focused, focuses and waits for the window to be focused.
950 * If the current page is 'about:blank', then the page is assumed to not
951 * yet be loaded. Pass true for expectBlankPage to not make this assumption
952 * if you expect a blank page to be present.
954 * The target object should be specified if it is different than 'window'. The
955 * actual focused window may be a descendant window of aObject.
957 * @param {Window|browser|BrowsingContext} [aObject]
958 * Optional object to be focused, and may be any of:
959 * window - a window object to focus
960 * browser - a <browser>/<iframe> element. The top-level window
961 * within the frame will be focused.
962 * browsing context - a browsing context containing a window to focus
963 * If not specified, defaults to the global 'window'.
964 * @param {boolean} [expectBlankPage=false]
965 * True if targetWindow.location is 'about:blank'.
966 * @param {boolean} [aBlurSubframe=false]
967 * If true, and a subframe within the window to focus is focused, blur
968 * it so that the specified window or browsing context will receive
971 * @returns The browsing context that was focused.
973 SimpleTest.promiseFocus = async function (
975 aExpectBlankPage = false,
976 aBlurSubframe = false
986 async function waitForEvent(aTarget, aEventName) {
987 return new Promise(resolve => {
988 aTarget.addEventListener(aEventName, resolve, {
995 if (SpecialPowers.wrap(Window).isInstance(aObject)) {
996 windowToFocus = aObject;
998 let isBlank = windowToFocus.location.href == "about:blank";
1000 aExpectBlankPage != isBlank ||
1001 windowToFocus.document.readyState != "complete"
1003 info("must wait for load");
1004 await waitForEvent(windowToFocus, "load");
1007 if (SpecialPowers.wrap(Element).isInstance(aObject)) {
1008 // assume this is a browser/iframe element
1009 browsingContext = aObject.browsingContext;
1011 browsingContext = aObject;
1015 browsingContext == aObject ? aObject.top.embedderElement : aObject;
1016 windowToFocus = browser.ownerGlobal;
1019 if (!windowToFocus.document.hasFocus()) {
1020 info("must wait for focus");
1021 let focusPromise = waitForEvent(windowToFocus.document, "focus");
1022 SpecialPowers.focus(windowToFocus);
1027 if (windowToFocus.document.activeElement != browser) {
1031 info("must wait for focus in content");
1033 // Make sure that the child process thinks it is focused as well.
1034 await SpecialPowers.ensureFocus(browsingContext, aBlurSubframe);
1036 if (aBlurSubframe) {
1037 SpecialPowers.clearFocus(windowToFocus);
1040 browsingContext = windowToFocus.browsingContext;
1043 // Some tests rely on this delay, likely expecting layout or paint to occur.
1044 await new Promise(resolve => {
1045 SimpleTest.executeSoon(resolve);
1048 return browsingContext;
1052 * Version of promiseFocus that uses a callback. For compatibility,
1053 * the callback is passed one argument, the window that was focused.
1054 * If the focused window is not in the same process, null is supplied.
1056 SimpleTest.waitForFocus = function (callback, aObject, expectBlankPage) {
1057 SimpleTest.promiseFocus(aObject, expectBlankPage).then(focusedBC => {
1058 callback(focusedBC?.window);
1061 /* eslint-enable mozilla/use-services */
1063 SimpleTest.stripLinebreaksAndWhitespaceAfterTags = function (aString) {
1064 return aString.replace(/(>\s*(\r\n|\n|\r)*\s*)/gm, ">");
1068 * `navigator.platform` should include this, when the platform is Windows.
1070 const kPlatformWindows = "Win";
1073 * See `SimpleTest.waitForClipboard`.
1075 const kTextHtmlPrefixClipboardDataWindows =
1076 "<html><body>\n<!--StartFragment-->";
1079 * See `SimpleTest.waitForClipboard`.
1081 const kTextHtmlSuffixClipboardDataWindows =
1082 "<!--EndFragment-->\n</body>\n</html>";
1085 * Polls the clipboard waiting for the expected value. A known value different than
1086 * the expected value is put on the clipboard first (and also polled for) so we
1087 * can be sure the value we get isn't just the expected value because it was already
1088 * on the clipboard. This only uses the global clipboard and only for text/plain
1091 * @param {String|Function} aExpectedStringOrValidatorFn
1092 * The string value that is expected to be on the clipboard, or a
1093 * validator function getting expected clipboard data and returning a bool.
1094 * If you specify string value, line breakers in clipboard are treated
1095 * as LineFeed. Therefore, you cannot include CarriageReturn to the
1097 * If you specify string value and expect "text/html" data, this wraps
1098 * the expected value with `kTextHtmlPrefixClipboardDataWindows` and
1099 * `kTextHtmlSuffixClipboardDataWindows` only when it runs on Windows
1100 * because they are appended only by nsDataObj.cpp for Windows.
1101 * https://searchfox.org/mozilla-central/rev/8f7b017a31326515cb467e69eef1f6c965b4f00e/widget/windows/nsDataObj.cpp#1798-1805,1839-1840,1842
1102 * Therefore, you can specify selected (copied) HTML data simply on any
1104 * @param {Function} aSetupFn
1105 * A function responsible for setting the clipboard to the expected value,
1106 * called after the known value setting succeeds.
1107 * @param {Function} aSuccessFn
1108 * A function called when the expected value is found on the clipboard.
1109 * @param {Function} aFailureFn
1110 * A function called if the expected value isn't found on the clipboard
1111 * within 5s. It can also be called if the known value can't be found.
1112 * @param {String} [aFlavor="text/plain"]
1113 * The flavor to look for.
1114 * @param {Number} [aTimeout=5000]
1115 * The timeout (in milliseconds) to wait for a clipboard change.
1116 * @param {boolean} [aExpectFailure=false]
1117 * If true, fail if the clipboard contents are modified within the timeout
1118 * interval defined by aTimeout. When aExpectFailure is true, the argument
1119 * aExpectedStringOrValidatorFn must be null, as it won't be used.
1120 * @param {boolean} [aDontInitializeClipboardIfExpectFailure=false]
1121 * If aExpectFailure and this is set to true, this does NOT initialize
1122 * clipboard with random data before running aSetupFn.
1124 SimpleTest.waitForClipboard = function (
1125 aExpectedStringOrValidatorFn,
1132 aDontInitializeClipboardIfExpectFailure
1134 let promise = SimpleTest.promiseClipboardChange(
1135 aExpectedStringOrValidatorFn,
1140 aDontInitializeClipboardIfExpectFailure
1142 promise.then(aSuccessFn).catch(aFailureFn);
1146 * Promise-oriented version of waitForClipboard.
1148 SimpleTest.promiseClipboardChange = async function (
1149 aExpectedStringOrValidatorFn,
1154 aDontInitializeClipboardIfExpectFailure
1156 let requestedFlavor = aFlavor || "text/plain";
1158 // The known value we put on the clipboard before running aSetupFn
1159 let initialVal = "waitForClipboard-known-value-" + Math.random();
1160 let preExpectedVal = initialVal;
1162 let inputValidatorFn;
1163 if (aExpectFailure) {
1164 // If we expect failure, the aExpectedStringOrValidatorFn should be null
1165 if (aExpectedStringOrValidatorFn !== null) {
1168 "When expecting failure, aExpectedStringOrValidatorFn must be null"
1172 inputValidatorFn = function (aData) {
1173 return aData != initialVal;
1175 // Build a default validator function for common string input.
1176 } else if (typeof aExpectedStringOrValidatorFn == "string") {
1177 if (aExpectedStringOrValidatorFn.includes("\r")) {
1179 "Use function instead of string to compare raw line breakers in clipboard"
1182 if (requestedFlavor === "text/html" && navigator.platform.includes("Win")) {
1183 inputValidatorFn = function (aData) {
1185 aData.replace(/\r\n?/g, "\n") ===
1186 kTextHtmlPrefixClipboardDataWindows +
1187 aExpectedStringOrValidatorFn +
1188 kTextHtmlSuffixClipboardDataWindows
1192 inputValidatorFn = function (aData) {
1193 return aData.replace(/\r\n?/g, "\n") === aExpectedStringOrValidatorFn;
1197 inputValidatorFn = aExpectedStringOrValidatorFn;
1200 let maxPolls = aTimeout ? aTimeout / 100 : 50;
1202 async function putAndVerify(operationFn, validatorFn, flavor, expectFailure) {
1203 await operationFn();
1206 for (let i = 0; i < maxPolls; i++) {
1207 data = SpecialPowers.getClipboardData(flavor);
1208 if (validatorFn(data)) {
1209 // Don't show the success message when waiting for preExpectedVal
1210 if (preExpectedVal) {
1211 preExpectedVal = null;
1215 "Clipboard has the given value: '" + data + "'"
1222 // Wait 100ms and check again.
1223 await new Promise(resolve => {
1224 SimpleTest._originalSetTimeout.apply(window, [resolve, 100]);
1228 let errorMsg = `Timed out while polling clipboard for ${
1229 preExpectedVal ? "initialized" : "requested"
1230 } data, got: ${data}`;
1231 SimpleTest.ok(expectFailure, errorMsg);
1232 if (!expectFailure) {
1233 throw new Error(errorMsg);
1238 if (!aExpectFailure || !aDontInitializeClipboardIfExpectFailure) {
1239 // First we wait for a known value different from the expected one.
1240 SimpleTest.info(`Initializing clipboard with "${preExpectedVal}"...`);
1243 SpecialPowers.clipboardCopyString(preExpectedVal);
1246 return aData == preExpectedVal;
1253 "Succeeded initializing clipboard, start requested things..."
1256 preExpectedVal = null;
1259 return putAndVerify(
1268 * Wait for a condition for a while (actually up to 3s here).
1270 * @param {Function} aCond
1271 * A function returns the result of the condition
1272 * @param {Function} aCallback
1273 * A function called after the condition is passed or timeout.
1274 * @param {String} aErrorMsg
1275 * The message displayed when the condition failed to pass
1278 SimpleTest.waitForCondition = function (aCond, aCallback, aErrorMsg) {
1279 this.promiseWaitForCondition(aCond, aErrorMsg).then(() => aCallback());
1281 SimpleTest.promiseWaitForCondition = async function (aCond, aErrorMsg) {
1282 for (let tries = 0; tries < 30; ++tries) {
1283 // Wait 100ms between checks.
1284 await new Promise(resolve => {
1285 SimpleTest._originalSetTimeout.apply(window, [resolve, 100]);
1288 let conditionPassed;
1290 conditionPassed = await aCond();
1292 ok(false, `${e}\n${e.stack}`);
1293 conditionPassed = false;
1295 if (conditionPassed) {
1299 ok(false, aErrorMsg);
1303 * Executes a function shortly after the call, but lets the caller continue
1304 * working (or finish).
1306 * @param {Function} aFunc
1307 * Function to execute soon.
1309 SimpleTest.executeSoon = function (aFunc) {
1310 if ("SpecialPowers" in window) {
1311 return SpecialPowers.executeSoon(aFunc, window);
1313 setTimeout(aFunc, 0);
1314 return null; // Avoid warning.
1318 * Register a cleanup/teardown function (which may be async) to run after all
1319 * tasks have finished, before running the next test. If async (or the function
1320 * returns a promise), the framework will wait for the promise/async function
1323 * @param {Function} aFunc
1324 * The cleanup/teardown function to run.
1326 SimpleTest.registerCleanupFunction = function (aFunc) {
1327 SimpleTest._cleanupFunctions.push(aFunc);
1331 * Register a cleanup/teardown function (which may be async) to run after the
1332 * current task has finished, before running the next task. If async (or the
1333 * function returns a promise), the framework will wait for the promise/async
1334 * function to resolve.
1336 * @param {Function} aFunc
1337 * The cleanup/teardown function to run.
1339 SimpleTest.registerCurrentTaskCleanupFunction = function (aFunc) {
1340 if (!SimpleTest._currentTask) {
1343 SimpleTest._currentTask._cleanupFunctions ||= [];
1344 SimpleTest._currentTask._cleanupFunctions.push(aFunc);
1348 * Register a cleanup/teardown function (which may be async) to run after each
1349 * task has finished, before running the next task. If async (or the
1350 * function returns a promise), the framework will wait for the promise/async
1351 * function to resolve.
1353 * @param {Function} aFunc
1354 * The cleanup/teardown function to run.
1356 SimpleTest.registerTaskCleanupFunction = function (aFunc) {
1357 SimpleTest._taskCleanupFunctions.push(aFunc);
1360 SimpleTest.registerTimeoutFunction = function (aFunc) {
1361 SimpleTest._timeoutFunctions.push(aFunc);
1364 SimpleTest.testInChaosMode = function () {
1365 if (SimpleTest._inChaosMode) {
1366 // It's already enabled for this test, don't enter twice
1369 SpecialPowers.DOMWindowUtils.enterChaosMode();
1370 SimpleTest._inChaosMode = true;
1371 // increase timeout here as chaosmode is very slow (i.e. 10x)
1372 // doing 20x as this overwrites anything the tests set
1373 SimpleTest.requestLongerTimeout(20);
1376 SimpleTest.timeout = async function () {
1377 for (const func of SimpleTest._timeoutFunctions) {
1380 SimpleTest._timeoutFunctions = [];
1383 SimpleTest.finishWithFailure = function (msg) {
1384 SimpleTest.ok(false, msg);
1385 SimpleTest.finish();
1389 * Finishes the tests. This is automatically called, except when
1390 * SimpleTest.waitForExplicitFinish() has been invoked.
1392 SimpleTest.finish = function () {
1393 if (SimpleTest._alreadyFinished) {
1395 "TEST-UNEXPECTED-FAIL | SimpleTest | this test already called finish!";
1397 parentRunner.structuredLogger.error(err);
1403 if (SimpleTest.expected == "fail" && SimpleTest.num_failed <= 0) {
1404 let msg = "We expected at least one failure";
1407 name: "fail-if condition in manifest",
1413 message: "TEST-KNOWN-FAIL",
1418 message: "TEST-UNEXPECTED-PASS",
1420 SimpleTest._logResult(test, successInfo, failureInfo);
1421 SimpleTest._tests.push(test);
1422 } else if (usesFailurePatterns()) {
1423 SimpleTest.expected.forEach(([pat, expected_count], i) => {
1424 let count = SimpleTest.num_failed[i];
1426 if (expected_count === null && count == 0) {
1427 diag = "expected some failures but got none";
1428 } else if (expected_count !== null && expected_count != count) {
1429 diag = `expected ${expected_count} failures but got ${count}`;
1434 ? `failure pattern \`${pat}\` in this test`
1435 : "failures in this test";
1436 let test = { result: false, name, diag };
1440 message: "TEST-PASS",
1445 message: "TEST-UNEXPECTED-FAIL",
1447 SimpleTest._logResult(test, successInfo, failureInfo);
1448 SimpleTest._tests.push(test);
1452 SimpleTest._timeoutFunctions = [];
1454 SimpleTest.testsLength = SimpleTest._tests.length;
1456 SimpleTest._alreadyFinished = true;
1458 if (SimpleTest._inChaosMode) {
1459 SpecialPowers.DOMWindowUtils.leaveChaosMode();
1460 SimpleTest._inChaosMode = false;
1463 var afterCleanup = async function () {
1464 SpecialPowers.removeFiles();
1466 if (SpecialPowers.DOMWindowUtils.isTestControllingRefreshes) {
1467 SimpleTest.ok(false, "test left refresh driver under test control");
1468 SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
1470 if (SimpleTest._expectingUncaughtException) {
1473 "expectUncaughtException was called but no uncaught exception was detected!"
1476 if (!SimpleTest._tests.length) {
1479 "[SimpleTest.finish()] No checks actually run. " +
1480 "(You need to call ok(), is(), or similar " +
1481 "functions at least once. Make sure you use " +
1482 "SimpleTest.waitForExplicitFinish() if you need " +
1487 let workers = await SpecialPowers.registeredServiceWorkers();
1489 if (SimpleTest._expectingRegisteredServiceWorker) {
1490 if (workers.length === 0) {
1493 "This test is expected to leave a service worker registered"
1496 } else if (workers.length) {
1497 let FULL_PROFILE_WORKERS_TO_IGNORE = [];
1498 if (parentRunner.conditionedProfile) {
1499 // Full profile has service workers in the profile, without clearing the
1500 // profile service workers will be leftover. We perform a startsWith
1501 // check below because some origins (s.0cf.io) use a cache-busting query
1503 FULL_PROFILE_WORKERS_TO_IGNORE = [
1504 "https://www.youtube.com/sw.js",
1505 "https://s.0cf.io/sw.js",
1510 "This test left a service worker registered without cleaning it up"
1514 for (let worker of workers) {
1516 FULL_PROFILE_WORKERS_TO_IGNORE.some(ignoreBase =>
1517 worker.scriptSpec.startsWith(ignoreBase)
1524 `Left over worker: ${worker.scriptSpec} (scope: ${worker.scope})`
1527 promise = SpecialPowers.removeAllServiceWorkerData();
1530 // If we want to wait for removeAllServiceWorkerData to finish, above,
1531 // there's a small chance that spinning the event loop could cause
1532 // SpecialPowers and SimpleTest to go away (e.g. if the test did
1533 // document.open). promise being non-null should be rare (a test would
1534 // have had to already fail by leaving a service worker around), so
1535 // limit the chances of the async wait happening to that case.
1538 /* We're running in an iframe, and the parent has a TestRunner */
1539 parentRunner.testFinished(SimpleTest._tests);
1542 if (!parentRunner || parentRunner.showTestReport) {
1543 SpecialPowers.flushPermissions(function () {
1544 SpecialPowers.flushPrefEnv(function () {
1545 SimpleTest.showReport();
1552 promise.then(finish);
1558 var executeCleanupFunction = function () {
1559 var func = SimpleTest._cleanupFunctions.pop();
1570 SimpleTest.ok(false, "Cleanup function threw exception: " + ex);
1573 if (ret && ret.constructor.name == "Promise") {
1574 ret.then(executeCleanupFunction, ex =>
1575 SimpleTest.ok(false, "Cleanup promise rejected: " + ex)
1578 executeCleanupFunction();
1582 executeCleanupFunction();
1584 SpecialPowers.notifyObservers(null, "test-complete");
1588 * Monitor console output from now until endMonitorConsole is called.
1590 * Expect to receive all console messages described by the elements of
1591 * ``msgs``, an array, in the order listed in ``msgs``; each element is an
1592 * object which may have any number of the following properties:
1594 * message, errorMessage, sourceName, sourceLine, category: string or regexp
1595 * lineNumber, columnNumber: number
1596 * isScriptError, isWarning: boolean
1598 * Strings, numbers, and booleans must compare equal to the named
1599 * property of the Nth console message. Regexps must match. Any
1600 * fields present in the message but not in the pattern object are ignored.
1602 * In addition to the above properties, elements in ``msgs`` may have a ``forbid``
1603 * boolean property. When ``forbid`` is true, a failure is logged each time a
1604 * matching message is received.
1606 * If ``forbidUnexpectedMsgs`` is true, then the messages received in the console
1607 * must exactly match the non-forbidden messages in ``msgs``; for each received
1608 * message not described by the next element in ``msgs``, a failure is logged. If
1609 * false, then other non-forbidden messages are ignored, but all expected
1610 * messages must still be received.
1612 * After endMonitorConsole is called, ``continuation`` will be called
1613 * asynchronously. (Normally, you will want to pass ``SimpleTest.finish`` here.)
1615 * It is incorrect to use this function in a test which has not called
1616 * SimpleTest.waitForExplicitFinish.
1618 SimpleTest.monitorConsole = function (
1621 forbidUnexpectedMsgs
1623 if (SimpleTest._stopOnLoad) {
1624 ok(false, "Console monitoring requires use of waitForExplicitFinish.");
1627 function msgMatches(msg, pat) {
1628 for (var k in pat) {
1632 if (pat[k] instanceof RegExp && typeof msg[k] === "string") {
1633 if (!pat[k].test(msg[k])) {
1636 } else if (msg[k] !== pat[k]) {
1643 var forbiddenMsgs = [];
1645 while (i < msgs.length) {
1647 if ("forbid" in pat) {
1648 var forbid = pat.forbid;
1651 forbiddenMsgs.push(pat);
1660 var assertionLabel = JSON.stringify(msgs);
1661 function listener(msg) {
1662 if (msg.message === "SENTINEL" && !msg.isScriptError) {
1666 "monitorConsole | number of messages " + assertionLabel
1668 SimpleTest.executeSoon(continuation);
1671 for (let pat of forbiddenMsgs) {
1672 if (msgMatches(msg, pat)) {
1675 "monitorConsole | observed forbidden message " + JSON.stringify(msg)
1680 if (counter >= msgs.length) {
1681 var str = "monitorConsole | extra message | " + JSON.stringify(msg);
1682 if (forbidUnexpectedMsgs) {
1689 var matches = msgMatches(msg, msgs[counter]);
1690 if (forbidUnexpectedMsgs) {
1693 "monitorConsole | [" + counter + "] must match " + JSON.stringify(msg)
1697 "monitorConsole | [" +
1700 (matches ? "matched " : "did not match ") +
1708 SpecialPowers.registerConsoleListener(listener);
1712 * Stop monitoring console output.
1714 SimpleTest.endMonitorConsole = function () {
1715 SpecialPowers.postConsoleSentinel();
1719 * Run ``testfn`` synchronously, and monitor its console output.
1721 * ``msgs`` is handled as described above for monitorConsole.
1723 * After ``testfn`` returns, console monitoring will stop, and ``continuation``
1724 * will be called asynchronously.
1727 SimpleTest.expectConsoleMessages = function (testfn, msgs, continuation) {
1728 SimpleTest.monitorConsole(continuation, msgs);
1730 SimpleTest.executeSoon(SimpleTest.endMonitorConsole);
1734 * Wrapper around ``expectConsoleMessages`` for the case where the test has
1735 * only one ``testfn`` to run.
1737 SimpleTest.runTestExpectingConsoleMessages = function (testfn, msgs) {
1738 SimpleTest.waitForExplicitFinish();
1739 SimpleTest.expectConsoleMessages(testfn, msgs, SimpleTest.finish);
1743 * Indicates to the test framework that the current test expects one or
1744 * more crashes (from plugins or IPC documents), and that the minidumps from
1745 * those crashes should be removed.
1747 SimpleTest.expectChildProcessCrash = function () {
1749 parentRunner.expectChildProcessCrash();
1754 * Indicates to the test framework that the next uncaught exception during
1755 * the test is expected, and should not cause a test failure.
1757 SimpleTest.expectUncaughtException = function (aExpecting) {
1758 SimpleTest._expectingUncaughtException =
1759 aExpecting === void 0 || !!aExpecting;
1763 * Returns whether the test has indicated that it expects an uncaught exception
1766 SimpleTest.isExpectingUncaughtException = function () {
1767 return SimpleTest._expectingUncaughtException;
1771 * Indicates to the test framework that all of the uncaught exceptions
1772 * during the test are known problems that should be fixed in the future,
1773 * but which should not cause the test to fail currently.
1775 SimpleTest.ignoreAllUncaughtExceptions = function (aIgnoring) {
1776 SimpleTest._ignoringAllUncaughtExceptions =
1777 aIgnoring === void 0 || !!aIgnoring;
1781 * Returns whether the test has indicated that all uncaught exceptions should be
1784 SimpleTest.isIgnoringAllUncaughtExceptions = function () {
1785 return SimpleTest._ignoringAllUncaughtExceptions;
1789 * Indicates to the test framework that this test is expected to leave a
1790 * service worker registered when it finishes.
1792 SimpleTest.expectRegisteredServiceWorker = function () {
1793 SimpleTest._expectingRegisteredServiceWorker = true;
1797 * Resets any state this SimpleTest object has. This is important for
1798 * browser chrome mochitests, which reuse the same SimpleTest object
1801 SimpleTest.reset = function () {
1802 SimpleTest._ignoringAllUncaughtExceptions = false;
1803 SimpleTest._expectingUncaughtException = false;
1804 SimpleTest._expectingRegisteredServiceWorker = false;
1805 SimpleTest._bufferedMessages = [];
1808 if (isPrimaryTestWindow) {
1809 addLoadEvent(function () {
1810 if (SimpleTest._stopOnLoad) {
1811 SimpleTest.finish();
1816 // --------------- Test.Builder/Test.More isDeeply() -----------------
1818 SimpleTest.DNE = { dne: "Does not exist" };
1819 SimpleTest.LF = "\r\n";
1821 SimpleTest._deepCheck = function (e1, e2, stack, seen) {
1823 if (Object.is(e1, e2)) {
1824 // Handles identical primitives and references.
1827 typeof e1 != "object" ||
1828 typeof e2 != "object" ||
1832 // If either argument is a primitive or function, don't consider the arguments the same.
1834 } else if (e1 == SimpleTest.DNE || e2 == SimpleTest.DNE) {
1836 } else if (SimpleTest.isa(e1, "Array") && SimpleTest.isa(e2, "Array")) {
1837 ok = SimpleTest._eqArray(e1, e2, stack, seen);
1839 ok = SimpleTest._eqAssoc(e1, e2, stack, seen);
1844 SimpleTest._eqArray = function (a1, a2, stack, seen) {
1845 // Return if they're the same object.
1850 // JavaScript objects have no unique identifiers, so we have to store
1851 // references to them all in an array, and then compare the references
1852 // directly. It's slow, but probably won't be much of an issue in
1853 // practice. Start by making a local copy of the array to as to avoid
1854 // confusing a reference seen more than once (such as [a, a]) for a
1855 // circular reference.
1856 for (var j = 0; j < seen.length; j++) {
1857 if (seen[j][0] == a1) {
1858 return seen[j][1] == a2;
1862 // If we get here, we haven't seen a1 before, so store it with reference
1864 seen.push([a1, a2]);
1867 // Only examines enumerable attributes. Only works for numeric arrays!
1868 // Associative arrays return 0. So call _eqAssoc() for them, instead.
1869 var max = Math.max(a1.length, a2.length);
1871 return SimpleTest._eqAssoc(a1, a2, stack, seen);
1873 for (var i = 0; i < max; i++) {
1874 var e1 = i < a1.length ? a1[i] : SimpleTest.DNE;
1875 var e2 = i < a2.length ? a2[i] : SimpleTest.DNE;
1876 stack.push({ type: "Array", idx: i, vals: [e1, e2] });
1877 ok = SimpleTest._deepCheck(e1, e2, stack, seen);
1887 SimpleTest._eqAssoc = function (o1, o2, stack, seen) {
1888 // Return if they're the same object.
1893 // JavaScript objects have no unique identifiers, so we have to store
1894 // references to them all in an array, and then compare the references
1895 // directly. It's slow, but probably won't be much of an issue in
1896 // practice. Start by making a local copy of the array to as to avoid
1897 // confusing a reference seen more than once (such as [a, a]) for a
1898 // circular reference.
1899 seen = seen.slice(0);
1900 for (let j = 0; j < seen.length; j++) {
1901 if (seen[j][0] == o1) {
1902 return seen[j][1] == o2;
1906 // If we get here, we haven't seen o1 before, so store it with reference
1908 seen.push([o1, o2]);
1910 // They should be of the same class.
1913 // Only examines enumerable attributes.
1915 // eslint-disable-next-line no-unused-vars
1920 // eslint-disable-next-line no-unused-vars
1924 var bigger = o1Size > o2Size ? o1 : o2;
1925 for (let i in bigger) {
1926 var e1 = i in o1 ? o1[i] : SimpleTest.DNE;
1927 var e2 = i in o2 ? o2[i] : SimpleTest.DNE;
1928 stack.push({ type: "Object", idx: i, vals: [e1, e2] });
1929 ok = SimpleTest._deepCheck(e1, e2, stack, seen);
1939 SimpleTest._formatStack = function (stack) {
1940 var variable = "$Foo";
1941 for (let i = 0; i < stack.length; i++) {
1942 var entry = stack[i];
1943 var type = entry.type;
1944 var idx = entry.idx;
1946 if (type == "Array") {
1947 // Numeric array index.
1948 variable += "[" + idx + "]";
1950 // Associative array index.
1951 idx = idx.replace("'", "\\'");
1952 variable += "['" + idx + "']";
1957 var vals = stack[stack.length - 1].vals.slice(0, 2);
1959 variable.replace("$Foo", "got"),
1960 variable.replace("$Foo", "expected"),
1963 var out = "Structures begin differing at:" + SimpleTest.LF;
1964 for (let i = 0; i < vals.length; i++) {
1966 if (val === SimpleTest.DNE) {
1967 val = "Does not exist";
1971 out += vars[i] + " = " + val + SimpleTest.LF;
1977 SimpleTest.isDeeply = function (it, as, name) {
1978 var stack = [{ vals: [it, as] }];
1980 if (SimpleTest._deepCheck(it, as, stack, seen)) {
1981 SimpleTest.record(true, name);
1983 SimpleTest.record(false, name, SimpleTest._formatStack(stack));
1987 SimpleTest.typeOf = function (object) {
1988 var c = Object.prototype.toString.apply(object);
1989 var name = c.substring(8, c.length - 1);
1990 if (name != "Object") {
1993 // It may be a non-core class. Try to extract the class name from
1994 // the constructor function. This may not work in all implementations.
1995 if (/function ([^(\s]+)/.test(Function.toString.call(object.constructor))) {
2002 SimpleTest.isa = function (object, clas) {
2003 return SimpleTest.typeOf(object) == clas;
2007 var ok = SimpleTest.ok;
2008 var record = SimpleTest.record;
2009 var is = SimpleTest.is;
2010 var isfuzzy = SimpleTest.isfuzzy;
2011 var isnot = SimpleTest.isnot;
2012 var todo = SimpleTest.todo;
2013 var todo_is = SimpleTest.todo_is;
2014 var todo_isnot = SimpleTest.todo_isnot;
2015 var isDeeply = SimpleTest.isDeeply;
2016 var info = SimpleTest.info;
2018 var gOldOnError = window.onerror;
2019 window.onerror = function simpletestOnerror(
2027 // XXX Chrome mochitests sometimes trigger this window.onerror handler,
2028 // but there are a number of uncaught JS exceptions from those tests.
2029 // For now, for tests that self identify as having unintentional uncaught
2030 // exceptions, just dump it so that the error is visible but doesn't cause
2031 // a test failure. See bug 652494.
2032 var isExpected = !!SimpleTest._expectingUncaughtException;
2033 var message = (isExpected ? "expected " : "") + "uncaught exception";
2034 var error = errorMsg + " at ";
2036 error += originalException.stack;
2038 // At least use the url+line+column we were given
2039 error += url + ":" + lineNumber + ":" + columnNumber;
2041 if (!SimpleTest._ignoringAllUncaughtExceptions) {
2042 // Don't log if SimpleTest.finish() is already called, it would cause failures
2043 if (!SimpleTest._alreadyFinished) {
2044 SimpleTest.record(isExpected, message, error);
2046 SimpleTest._expectingUncaughtException = false;
2048 SimpleTest.todo(false, message + ": " + error);
2050 // There is no Components.stack.caller to log. (See bug 511888.)
2052 // Call previous handler.
2055 // Ignore return value: always run default handler.
2056 gOldOnError(errorMsg, url, lineNumber);
2059 SimpleTest.info("Exception thrown by gOldOnError(): " + e);
2062 SimpleTest.info("JavaScript error stack:\n" + e.stack);
2067 if (!SimpleTest._stopOnLoad && !isExpected && !SimpleTest._alreadyFinished) {
2068 // Need to finish() manually here, yet let the test actually end first.
2069 SimpleTest.executeSoon(SimpleTest.finish);
2073 // Lifted from dom/media/test/manifest.js
2074 // Make sure to not touch navigator in here, since we want to push prefs that
2075 // will affect the APIs it exposes, but the set of exposed APIs is determined
2076 // when Navigator.prototype is created. So if we touch navigator before pushing
2077 // the prefs, the APIs it exposes will not take those prefs into account. We
2078 // work around this by using a navigator object from a different global for our
2079 // UA string testing.
2080 var gAndroidSdk = null;
2081 function getAndroidSdk() {
2082 if (gAndroidSdk === null) {
2083 var iframe = document.documentElement.appendChild(
2084 document.createElement("iframe")
2086 iframe.style.display = "none";
2087 var nav = iframe.contentWindow.navigator;
2089 !nav.userAgent.includes("Mobile") &&
2090 !nav.userAgent.includes("Tablet")
2094 // See nsSystemInfo.cpp, the getProperty('version') returns different value
2095 // on each platforms, so we need to distinguish the android platform.
2096 var versionString = nav.userAgent.includes("Android")
2099 gAndroidSdk = SpecialPowers.Services.sysinfo.getProperty(versionString);
2101 document.documentElement.removeChild(iframe);
2106 // add_task(generatorFunction):
2107 // Call `add_task(generatorFunction)` for each separate
2108 // asynchronous task in a mochitest. Tasks are run consecutively.
2109 // Before the first task, `SimpleTest.waitForExplicitFinish()`
2110 // will be called automatically, and after the last task,
2111 // `SimpleTest.finish()` will be called.
2112 var add_task = (function () {
2113 // The list of tasks to run.
2115 var run_only_this_task = null;
2117 function isGenerator(value) {
2119 value && typeof value === "object" && typeof value.next === "function"
2123 // The "add_task" function
2124 return function (generatorFunction, options = { isSetup: false }) {
2125 if (task_list.length === 0) {
2126 // This is the first time add_task has been called.
2127 // First, confirm that SimpleTest is available.
2129 throw new Error("SimpleTest not available.");
2131 // Don't stop tests until asynchronous tasks are finished.
2132 SimpleTest.waitForExplicitFinish();
2133 // Because the client is using add_task for this set of tests,
2134 // we need to spawn a "master task" that calls each task in succesion.
2135 // Use setTimeout to ensure the master task runs after the client
2137 setTimeout(function nextTick() {
2138 // If we are in a HTML document, we should wait for the document
2139 // to be fully loaded.
2140 // These checks ensure that we are in an HTML document without
2141 // throwing TypeError; also I am told that readyState in XUL documents
2142 // are totally bogus so we don't try to do this there.
2144 typeof window !== "undefined" &&
2145 typeof HTMLDocument !== "undefined" &&
2146 window.document instanceof HTMLDocument &&
2147 window.document.readyState !== "complete"
2149 setTimeout(nextTick);
2154 // Allow for a task to be skipped; we need only use the structured logger
2155 // for this, whilst deactivating log buffering to ensure that messages
2156 // are always printed to stdout.
2157 function skipTask(name) {
2158 let logger = parentRunner && parentRunner.structuredLogger;
2160 info("add_task | Skipping test " + name);
2163 logger.deactivateBuffering();
2164 logger.testStatus(SimpleTest._getCurrentTestURL(), name, "SKIP");
2165 logger.warning("add_task | Skipping test " + name);
2166 logger.activateBuffering();
2169 // We stop the entire test file at the first exception because this
2170 // may mean that the state of subsequent tests may be corrupt.
2172 for (var task of task_list) {
2173 SimpleTest._currentTask = task;
2174 var name = task.name || "";
2177 (run_only_this_task && task != run_only_this_task)
2182 const taskInfo = action =>
2185 task.isSetup ? "add_setup" : "add_task"
2186 } | ${action} ${name}`
2188 taskInfo("Entering");
2189 let result = await task();
2190 if (isGenerator(result)) {
2191 ok(false, "Task returned a generator");
2193 if (task._cleanupFunctions) {
2194 for (const fn of task._cleanupFunctions) {
2198 for (const fn of SimpleTest._taskCleanupFunctions) {
2201 taskInfo("Leaving");
2202 delete SimpleTest._currentTask;
2208 typeof ex == "string" ||
2209 (typeof ex == "object" && isErrorOrException(ex))
2211 serializedEx = `${ex}`;
2213 serializedEx = JSON.stringify(ex);
2219 "Should not throw any errors",
2225 "(The exception cannot be converted to string.)",
2226 "Should not throw any errors",
2231 // All tasks are finished.
2232 SimpleTest.finish();
2236 generatorFunction.skip = () => (generatorFunction.__skipMe = true);
2237 generatorFunction.only = () => (run_only_this_task = generatorFunction);
2238 // Add the task to the list of tasks to run after
2239 // the main thread is finished.
2240 if (options.isSetup) {
2241 generatorFunction.isSetup = true;
2242 let lastSetupIndex = task_list.findLastIndex(t => t.isSetup) + 1;
2243 task_list.splice(lastSetupIndex, 0, generatorFunction);
2245 task_list.push(generatorFunction);
2247 return generatorFunction;
2251 // Like add_task, but setup tasks are executed first.
2252 function add_setup(generatorFunction) {
2253 return add_task(generatorFunction, { isSetup: true });
2256 // Request complete log when using failure patterns so that failure info
2257 // from infra can be useful.
2258 if (usesFailurePatterns()) {
2259 SimpleTest.requestCompleteLog();
2262 addEventListener("message", async event => {
2263 if (event.data == "SimpleTest:timeout") {
2264 await SimpleTest.timeout();
2265 SimpleTest.finish();