Source code

Revision control

Copy as Markdown

Other Tools

/* Any copyright is dedicated to the Public Domain.
/* eslint no-unused-vars: [2, {"vars": "local"}] */
"use strict";
// Import the inspector's head.js first (which itself imports shared-head.js).
Services.scriptloader.loadSubScript(
this
);
var {
CssRuleView,
} = require("resource://devtools/client/inspector/rules/rules.js");
var {
getInplaceEditorForSpan: inplaceEditor,
} = require("resource://devtools/client/shared/inplace-editor.js");
const {
getCssVariableColor,
} = require("resource://devtools/client/shared/theme.js");
const TEST_URL_ROOT =
const TEST_URL_ROOT_SSL =
const ROOT_TEST_DIR = getRootDirectory(gTestPath);
const STYLE_INSPECTOR_L10N = new LocalizationHelper(
"devtools/shared/locales/styleinspector.properties"
);
// Clean-up all prefs that might have been changed during a test run
// (safer here because if the test fails, then the pref is never reverted)
registerCleanupFunction(() => {
Services.prefs.clearUserPref("devtools.defaultColorUnit");
});
/**
* The functions found below are here to ease test development and maintenance.
* Most of these functions are stateless and will require some form of context
* (the instance of the current toolbox, or inspector panel for instance).
*
* Most of these functions are async too and return promises.
*
* All tests should follow the following pattern:
*
* add_task(async function() {
* await addTab(TEST_URI);
* let {toolbox, inspector} = await openInspector();
* await inspector.sidebar.select(viewId);
* let view = inspector.getPanel(viewId).view;
* await selectNode("#test", inspector);
* await someAsyncTestFunction(view);
* });
*
* add_task is the way to define the testcase in the test file. It accepts
* a single argument: a function returning a promise (usually async function).
*
* There is no need to clean tabs up at the end of a test as this is done
* automatically.
*
* It is advised not to store any references on the global scope. There
* shouldn't be a need to anyway. Thanks to async functions, test steps, even
* though asynchronous, can be described in a nice flat way, and
* if/for/while/... control flow can be used as in sync code, making it
* possible to write the outline of the test case all in add_task, and delegate
* actual processing and assertions to other functions.
*/
/* *********************************************
* UTILS
* *********************************************
* General test utilities.
* Add new tabs, open the toolbox and switch to the various panels, select
* nodes, get node references, ...
*/
/**
* Polls a given function waiting for it to return true.
*
* @param {Function} validatorFn
* A validator function that returns a boolean.
* This is called every few milliseconds to check if the result is true.
* When it is true, the promise resolves.
* @param {String} name
* Optional name of the test. This is used to generate
* the success and failure messages.
* @return a promise that resolves when the function returned true or rejects
* if the timeout is reached
*/
function waitForSuccess(validatorFn, name = "untitled") {
return new Promise(resolve => {
function wait(validator) {
if (validator()) {
ok(true, "Validator function " + name + " returned true");
resolve();
} else {
setTimeout(() => wait(validator), 200);
}
}
wait(validatorFn);
});
}
/**
* Get the dataURL for the font family tooltip.
*
* @param {Window} win
* @param {String} font
* The font family value.
* @param {object} nodeFront
* The NodeActor that will used to retrieve the dataURL for the
* font family tooltip contents.
*/
var getFontFamilyDataURL = async function (win, font, nodeFront) {
const fillStyle = getCssVariableColor("--theme-body-color", win);
const { data } = await nodeFront.getFontFamilyDataURL(font, fillStyle);
const dataURL = await data.string();
return dataURL;
};
/* *********************************************
* RULE-VIEW
* *********************************************
* Rule-view related test utility functions
* This object contains functions to get rules, get properties, ...
*/
/**
* Simulate a color change in a given color picker tooltip, and optionally wait
* for a given element in the page to have its style changed as a result
*
* @param {RuleView} ruleView
* The related rule view instance
* @param {SwatchColorPickerTooltip} colorPicker
* @param {Array} newRgba
* The new color to be set [r, g, b, a]
* @param {Object} expectedChange
* Optional object that needs the following props:
* - {DOMNode} element The element in the page that will have its
* style changed.
* - {String} name The style name that will be changed
* - {String} value The expected style value
* The style will be checked like so: getComputedStyle(element)[name] === value
*/
var simulateColorPickerChange = async function (
ruleView,
colorPicker,
newRgba,
expectedChange
) {
const onRuleViewChanged = ruleView.once("ruleview-changed");
info("Getting the spectrum colorpicker object");
const spectrum = await colorPicker.spectrum;
info("Setting the new color");
spectrum.rgb = newRgba;
info("Applying the change");
spectrum.updateUI();
spectrum.onChange();
info("Waiting for rule-view to update");
await onRuleViewChanged;
if (expectedChange) {
info("Waiting for the style to be applied on the page");
await waitForSuccess(() => {
const { element, name, value } = expectedChange;
return content.getComputedStyle(element)[name] === value;
}, "Color picker change applied on the page");
}
};
/* *********************************************
* COMPUTED-VIEW
* *********************************************
* Computed-view related utility functions.
* Allows to get properties, links, expand properties, ...
*/
/**
* Get references to the name and value span nodes corresponding to a given
* property name in the computed-view
*
* @param {CssComputedView} view
* The instance of the computed view panel
* @param {String} name
* The name of the property to retrieve
* @return an object {nameSpan, valueSpan}
*/
function getComputedViewProperty(view, name) {
let prop;
for (const property of view.styleDocument.querySelectorAll(
".computed-property-view"
)) {
const nameSpan = property.querySelector(".computed-property-name");
const valueSpan = property.querySelector(".computed-property-value");
if (nameSpan.firstChild.textContent === name) {
prop = { nameSpan, valueSpan };
break;
}
}
return prop;
}
/**
* Get the text value of the property corresponding to a given name in the
* computed-view
*
* @param {CssComputedView} view
* The instance of the computed view panel
* @param {String} name
* The name of the property to retrieve
* @return {String} The property value
*/
function getComputedViewPropertyValue(view, name, propertyName) {
return getComputedViewProperty(view, name, propertyName).valueSpan
.textContent;
}