123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347 |
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- /* eslint no-unused-vars: [2, {"vars": "local", "args": "none"}] */
- /* import-globals-from ../../framework/test/shared-head.js */
- "use strict";
- // shared-head.js handles imports, constants, and utility functions
- Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js", this);
- const {DOMHelpers} = Cu.import("resource://devtools/client/shared/DOMHelpers.jsm", {});
- const {Hosts} = require("devtools/client/framework/toolbox-hosts");
- const TEST_URI_ROOT = "http://example.com/browser/devtools/client/shared/test/";
- const OPTIONS_VIEW_URL = TEST_URI_ROOT + "doc_options-view.xul";
- function catchFail(func) {
- return function () {
- try {
- return func.apply(null, arguments);
- } catch (ex) {
- ok(false, ex);
- console.error(ex);
- finish();
- throw ex;
- }
- };
- }
- /**
- * Polls a given function waiting for the given value.
- *
- * @param object options
- * Options object with the following properties:
- * - validator
- * A validator function that should return the expected value. This is
- * called every few milliseconds to check if the result is the expected
- * one. When the returned result is the expected one, then the |success|
- * function is called and polling stops. If |validator| never returns
- * the expected value, then polling timeouts after several tries and
- * a failure is recorded - the given |failure| function is invoked.
- * - success
- * A function called when the validator function returns the expected
- * value.
- * - failure
- * A function called if the validator function timeouts - fails to return
- * the expected value in the given time.
- * - name
- * Name of test. This is used to generate the success and failure
- * messages.
- * - timeout
- * Timeout for validator function, in milliseconds. Default is 5000 ms.
- * - value
- * The expected value. If this option is omitted then the |validator|
- * function must return a trueish value.
- * Each of the provided callback functions will receive two arguments:
- * the |options| object and the last value returned by |validator|.
- */
- function waitForValue(options) {
- let start = Date.now();
- let timeout = options.timeout || 5000;
- let lastValue;
- function wait(validatorFn, successFn, failureFn) {
- if ((Date.now() - start) > timeout) {
- // Log the failure.
- ok(false, "Timed out while waiting for: " + options.name);
- let expected = "value" in options ?
- "'" + options.value + "'" :
- "a trueish value";
- info("timeout info :: got '" + lastValue + "', expected " + expected);
- failureFn(options, lastValue);
- return;
- }
- lastValue = validatorFn(options, lastValue);
- let successful = "value" in options ?
- lastValue == options.value :
- lastValue;
- if (successful) {
- ok(true, options.name);
- successFn(options, lastValue);
- } else {
- setTimeout(() => {
- wait(validatorFn, successFn, failureFn);
- }, 100);
- }
- }
- wait(options.validator, options.success, options.failure);
- }
- function oneTimeObserve(name, callback) {
- return new Promise((resolve) => {
- let func = function () {
- Services.obs.removeObserver(func, name);
- if (callback) {
- callback();
- }
- resolve();
- };
- Services.obs.addObserver(func, name, false);
- });
- }
- let createHost =
- Task.async(function* (type = "bottom", src = "data:text/html;charset=utf-8,") {
- let host = new Hosts[type](gBrowser.selectedTab);
- let iframe = yield host.create();
- yield new Promise(resolve => {
- let domHelper = new DOMHelpers(iframe.contentWindow);
- iframe.setAttribute("src", src);
- domHelper.onceDOMReady(resolve);
- });
- return [host, iframe.contentWindow, iframe.contentDocument];
- });
- /**
- * Check the correctness of the data recorded in Telemetry after
- * loadTelemetryAndRecordLogs was called.
- */
- function checkTelemetryResults(Telemetry) {
- let result = Telemetry.prototype.telemetryInfo;
- for (let histId in result) {
- let value = result[histId];
- if (histId.endsWith("OPENED_COUNT")) {
- ok(value.length > 1, histId + " has more than one entry");
- let okay = value.every(function (element) {
- return element === true;
- });
- ok(okay, "All " + histId + " entries are === true");
- } else if (histId.endsWith("TIME_ACTIVE_SECONDS")) {
- ok(value.length > 1, histId + " has more than one entry");
- let okay = value.every(function (element) {
- return element > 0;
- });
- ok(okay, "All " + histId + " entries have time > 0");
- }
- }
- }
- /**
- * Open and close the toolbox in the current browser tab, several times, waiting
- * some amount of time in between.
- * @param {Number} nbOfTimes
- * @param {Number} usageTime in milliseconds
- * @param {String} toolId
- */
- function* openAndCloseToolbox(nbOfTimes, usageTime, toolId) {
- for (let i = 0; i < nbOfTimes; i++) {
- info("Opening toolbox " + (i + 1));
- let target = TargetFactory.forTab(gBrowser.selectedTab);
- yield gDevTools.showToolbox(target, toolId);
- // We use a timeout to check the toolbox's active time
- yield new Promise(resolve => setTimeout(resolve, usageTime));
- info("Closing toolbox " + (i + 1));
- yield gDevTools.closeToolbox(target);
- }
- }
- /**
- * Synthesize a profile for testing.
- */
- function synthesizeProfileForTest(samples) {
- const RecordingUtils = require("devtools/shared/performance/recording-utils");
- samples.unshift({
- time: 0,
- frames: []
- });
- let uniqueStacks = new RecordingUtils.UniqueStacks();
- return RecordingUtils.deflateThread({
- samples: samples,
- markers: []
- }, uniqueStacks);
- }
- /**
- * Waits until a predicate returns true.
- *
- * @param function predicate
- * Invoked once in a while until it returns true.
- * @param number interval [optional]
- * How often the predicate is invoked, in milliseconds.
- */
- function waitUntil(predicate, interval = 10) {
- if (predicate()) {
- return Promise.resolve(true);
- }
- return new Promise(resolve => {
- setTimeout(function () {
- waitUntil(predicate).then(() => resolve(true));
- }, interval);
- });
- }
- /**
- * Show the presets list sidebar in the cssfilter widget popup
- * @param {CSSFilterWidget} widget
- * @return {Promise}
- */
- function showFilterPopupPresets(widget) {
- let onRender = widget.once("render");
- widget._togglePresets();
- return onRender;
- }
- /**
- * Show presets list and create a sample preset with the name and value provided
- * @param {CSSFilterWidget} widget
- * @param {string} name
- * @param {string} value
- * @return {Promise}
- */
- let showFilterPopupPresetsAndCreatePreset =
- Task.async(function* (widget, name, value) {
- yield showFilterPopupPresets(widget);
- let onRender = widget.once("render");
- widget.setCssValue(value);
- yield onRender;
- let footer = widget.el.querySelector(".presets-list .footer");
- footer.querySelector("input").value = name;
- onRender = widget.once("render");
- widget._savePreset({
- preventDefault: () => {}
- });
- yield onRender;
- });
- /**
- * Utility function for testing CSS code samples that have been
- * syntax-highlighted.
- *
- * The CSS syntax highlighter emits a collection of DOM nodes that have
- * CSS classes applied to them. This function checks that those nodes
- * are what we expect.
- *
- * @param {array} expectedNodes
- * A representation of the nodes we expect to see.
- * Each node is an object containing two properties:
- * - type: a string which can be one of:
- * - text, comment, property-name, property-value
- * - text: the textContent of the node
- *
- * For example, given a string like this:
- * "<comment> The part we want </comment>\n this: is-the-part-we-want;"
- *
- * we would represent the expected output like this:
- * [{type: "comment", text: "<comment> The part we want </comment>"},
- * {type: "text", text: "\n"},
- * {type: "property-name", text: "this"},
- * {type: "text", text: ":"},
- * {type: "text", text: " "},
- * {type: "property-value", text: "is-the-part-we-want"},
- * {type: "text", text: ";"}];
- *
- * @param {Node} parent
- * The DOM node whose children are the output of the syntax highlighter.
- */
- function checkCssSyntaxHighlighterOutput(expectedNodes, parent) {
- /**
- * The classes applied to the output nodes by the syntax highlighter.
- * These must be same as the definitions in MdnDocsWidget.js.
- */
- const PROPERTY_NAME_COLOR = "theme-fg-color5";
- const PROPERTY_VALUE_COLOR = "theme-fg-color1";
- const COMMENT_COLOR = "theme-comment";
- /**
- * Check the type and content of a single node.
- */
- function checkNode(expected, actual) {
- ok(actual.textContent == expected.text,
- "Check that node has the expected textContent");
- info("Expected text content: [" + expected.text + "]");
- info("Actual text content: [" + actual.textContent + "]");
- info("Check that node has the expected type");
- if (expected.type == "text") {
- ok(actual.nodeType == 3, "Check that node is a text node");
- } else {
- ok(actual.tagName.toUpperCase() == "SPAN", "Check that node is a SPAN");
- }
- info("Check that node has the expected className");
- let expectedClassName = null;
- let actualClassName = null;
- switch (expected.type) {
- case "property-name":
- expectedClassName = PROPERTY_NAME_COLOR;
- break;
- case "property-value":
- expectedClassName = PROPERTY_VALUE_COLOR;
- break;
- case "comment":
- expectedClassName = COMMENT_COLOR;
- break;
- default:
- ok(!actual.classList, "No className expected");
- return;
- }
- ok(actual.classList.length == 1, "One className expected");
- actualClassName = actual.classList[0];
- ok(expectedClassName == actualClassName, "Check className value");
- info("Expected className: " + expectedClassName);
- info("Actual className: " + actualClassName);
- }
- info("Logging the actual nodes we have:");
- for (let j = 0; j < parent.childNodes.length; j++) {
- let n = parent.childNodes[j];
- info(j + " / " +
- "nodeType: " + n.nodeType + " / " +
- "textContent: " + n.textContent);
- }
- ok(parent.childNodes.length == parent.childNodes.length,
- "Check we have the expected number of nodes");
- info("Expected node count " + expectedNodes.length);
- info("Actual node count " + expectedNodes.length);
- for (let i = 0; i < expectedNodes.length; i++) {
- info("Check node " + i);
- checkNode(expectedNodes[i], parent.childNodes[i]);
- }
- }
|