gecko/browser/devtools/styleinspector/test/head.js

320 lines
9.1 KiB
JavaScript

/* vim:set ts=2 sw=2 sts=2 et: */
/* 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/. */
const TEST_BASE_HTTP = "http://example.com/browser/browser/devtools/styleinspector/test/";
const TEST_BASE_HTTPS = "https://example.com/browser/browser/devtools/styleinspector/test/";
//Services.prefs.setBoolPref("devtools.dump.emit", true);
Services.prefs.setBoolPref("devtools.debugger.log", true);
let tempScope = {};
Cu.import("resource:///modules/devtools/gDevTools.jsm", tempScope);
let ConsoleUtils = tempScope.ConsoleUtils;
let gDevTools = tempScope.gDevTools;
Cu.import("resource://gre/modules/devtools/Loader.jsm", tempScope);
let devtools = tempScope.devtools;
let TargetFactory = devtools.TargetFactory;
let {CssHtmlTree} = devtools.require("devtools/styleinspector/computed-view");
let {CssRuleView, _ElementStyle} = devtools.require("devtools/styleinspector/rule-view");
let {CssLogic, CssSelector} = devtools.require("devtools/styleinspector/css-logic");
let promise = devtools.require("sdk/core/promise");
gDevTools.testing = true;
SimpleTest.registerCleanupFunction(() => {
gDevTools.testing = false;
});
SimpleTest.registerCleanupFunction(() => {
Services.prefs.clearUserPref("devtools.debugger.log");
Services.prefs.clearUserPref("devtools.dump.emit");
});
let {
editableField,
getInplaceEditorForSpan: inplaceEditor
} = devtools.require("devtools/shared/inplace-editor");
Components.utils.import("resource://gre/modules/devtools/Console.jsm", tempScope);
let console = tempScope.console;
let browser, hudId, hud, hudBox, filterBox, outputNode, cs;
function addTab(aURL)
{
gBrowser.selectedTab = gBrowser.addTab();
content.location = aURL;
browser = gBrowser.getBrowserForTab(gBrowser.selectedTab);
}
function openInspector(callback)
{
let target = TargetFactory.forTab(gBrowser.selectedTab);
gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
callback(toolbox.getCurrentPanel());
});
}
function getActiveInspector()
{
let target = TargetFactory.forTab(gBrowser.selectedTab);
return gDevTools.getToolbox(target).getPanel("inspector");
}
function openView(name, callback)
{
openInspector(inspector => {
function onReady() {
inspector.sidebar.select(name);
let { view } = inspector.sidebar.getWindowForTab(name)[name];
callback(inspector, view);
}
if (inspector.sidebar.getTab(name)) {
onReady();
} else {
inspector.sidebar.once(name + "-ready", onReady);
}
});
}
function openRuleView(callback)
{
openView("ruleview", callback);
}
function openComputedView(callback)
{
openView("computedview", callback);
}
/**
* Simple DOM node accesor function that takes either a node or a string css
* selector as argument and returns the corresponding node
* @param {String|DOMNode} nodeOrSelector
* @return {DOMNode}
*/
function getNode(nodeOrSelector)
{
let node = nodeOrSelector;
if (typeof nodeOrSelector === "string") {
node = content.document.querySelector(nodeOrSelector);
ok(node, "A node was found for selector " + nodeOrSelector);
}
return node;
}
/**
* Set the inspector's current selection to a node or to the first match of the
* given css selector
* @param {String|DOMNode} nodeOrSelector
* @param {InspectorPanel} inspector The instance of InspectorPanel currently loaded in the toolbox
* @param {String} reason Defaults to "test" which instructs the inspector not to highlight the node upon selection
* @return a promise that resolves when the inspector is updated with the new
* node
*/
function selectNode(nodeOrSelector, inspector, reason="test")
{
info("Selecting the node " + nodeOrSelector);
let node = getNode(nodeOrSelector);
let updated = inspector.once("inspector-updated");
inspector.selection.setNode(node, reason);
return updated;
}
function addStyle(aDocument, aString)
{
let node = aDocument.createElement('style');
node.setAttribute("type", "text/css");
node.textContent = aString;
aDocument.getElementsByTagName("head")[0].appendChild(node);
return node;
}
function finishTest()
{
finish();
}
function tearDown()
{
try {
let target = TargetFactory.forTab(gBrowser.selectedTab);
gDevTools.closeToolbox(target);
}
catch (ex) {
dump(ex);
}
while (gBrowser.tabs.length > 1) {
gBrowser.removeCurrentTab();
}
browser = hudId = hud = filterBox = outputNode = cs = null;
}
function getComputedView() {
let inspector = getActiveInspector();
return inspector.sidebar.getWindowForTab("computedview").computedview.view;
}
function ruleView()
{
let inspector = getActiveInspector();
return inspector.sidebar.getWindowForTab("ruleview").ruleview.view;
}
function waitForEditorFocus(aParent, aCallback)
{
aParent.addEventListener("focus", function onFocus(evt) {
if (inplaceEditor(evt.target) && evt.target.tagName == "input") {
aParent.removeEventListener("focus", onFocus, true);
let editor = inplaceEditor(evt.target);
executeSoon(function() {
aCallback(editor);
});
}
}, true);
}
function waitForEditorBlur(aEditor, aCallback)
{
let input = aEditor.input;
input.addEventListener("blur", function onBlur() {
input.removeEventListener("blur", onBlur, false);
executeSoon(function() {
aCallback();
});
}, false);
}
function fireCopyEvent(element) {
let evt = element.ownerDocument.createEvent("Event");
evt.initEvent("copy", true, true);
element.dispatchEvent(evt);
}
function contextMenuClick(element) {
var evt = element.ownerDocument.createEvent('MouseEvents');
var button = 2; // right click
evt.initMouseEvent('contextmenu', true, true,
element.ownerDocument.defaultView, 1, 0, 0, 0, 0, false,
false, false, false, button, null);
element.dispatchEvent(evt);
}
function expectRuleChange(rule) {
return rule._applyingModifications;
}
function promiseDone(promise) {
promise.then(null, err => {
ok(false, "Promise failed: " + err);
if (err.stack) {
dump(err.stack);
}
SimpleTest.finish();
});
}
function getComputedPropertyValue(aName)
{
let computedview = getComputedView();
let props = computedview.styleDocument.querySelectorAll(".property-view");
for (let prop of props) {
let name = prop.querySelector(".property-name");
if (name.textContent === aName) {
let value = prop.querySelector(".property-value");
return value.textContent;
}
}
}
/**
* Polls a given function waiting for it to become true.
*
* @param object aOptions
* Options object with the following properties:
* - 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, succesFn
* is called and polling stops. If validatorFn never returns true, then
* polling timeouts after several tries and a failure is recorded.
* - successFn
* A function called when the validator function returns true.
* - failureFn
* A function called if the validator function timeouts - fails to return
* true 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.
*/
function waitForSuccess(aOptions)
{
let start = Date.now();
let timeout = aOptions.timeout || 5000;
function wait(validatorFn, successFn, failureFn)
{
if ((Date.now() - start) > timeout) {
// Log the failure.
ok(false, "Timed out while waiting for: " + aOptions.name);
failureFn(aOptions);
return;
}
if (validatorFn(aOptions)) {
ok(true, aOptions.name);
successFn();
}
else {
setTimeout(function() wait(validatorFn, successFn, failureFn), 100);
}
}
wait(aOptions.validatorFn, aOptions.successFn, aOptions.failureFn);
}
registerCleanupFunction(tearDown);
waitForExplicitFinish();
/**
* @return a promise that resolves when the tooltip is shown
*/
function assertTooltipShownOn(tooltip, element) {
return Task.spawn(function*() {
let isTarget = yield isHoverTooltipTarget(tooltip, element);
ok(isTarget, "The element is a tooltip target");
});
}
/**
* Given a tooltip object instance (see Tooltip.js), checks if it is set to
* toggle and hover and if so, checks if the given target is a valid hover target.
* This won't actually show the tooltip (the less we interact with XUL panels
* during test runs, the better).
* @return a promise that resolves when the answer is known. Also, this will
* delegate to a function in the rule-view which will insert content into the
* tooltip
*/
function isHoverTooltipTarget(tooltip, target) {
if (!tooltip._basedNode || !tooltip.panel) {
return promise.reject(new Error("The tooltip passed isn't set to toggle on hover or is not a tooltip"));
}
// The tooltip delegates to a user defined cb that inserts content in the tooltip
// when calling isValidHoverTarget
return tooltip.isValidHoverTarget(target);
}