Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Errors

/* Any copyright is dedicated to the Public Domain.
"use strict";
// Test that markup-containers in the markup-view do flash when their
// corresponding DOM nodes mutate
// Have to use the same timer functions used by the inspector.
// eslint-disable-next-line mozilla/no-redeclare-with-import-autofix
const { clearTimeout } = ChromeUtils.importESModule(
"resource://gre/modules/Timer.sys.mjs"
);
const TEST_URL = URL_ROOT + "doc_markup_flashing.html";
// The test data contains a list of mutations to test.
// Each item is an object:
// - desc: a description of the test step, for better logging
// - mutate: a generator function that should make changes to the content DOM
// - attribute: if set, the test will expect the corresponding attribute to
// flash instead of the whole node
// - flashedNode: [optional] the css selector of the node that is expected to
// flash in the markup-view as a result of the mutation.
// If missing, the rootNode (".list") will be expected to flash
const TEST_DATA = [
{
desc: "Adding a new node should flash the new node",
async mutate() {
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
const newLi = content.document.createElement("LI");
newLi.textContent = "new list item";
content.document.querySelector(".list").appendChild(newLi);
});
},
flashedNode: ".list li:nth-child(3)",
},
{
desc: "Removing a node should flash its parent",
async mutate() {
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
const root = content.document.querySelector(".list");
root.removeChild(root.lastElementChild);
});
},
},
{
desc: "Re-appending an existing node should only flash this node",
async mutate() {
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
const root = content.document.querySelector(".list");
root.appendChild(root.firstElementChild);
});
},
flashedNode: ".list .item:last-child",
},
{
desc: "Adding an attribute should flash the attribute",
attribute: "test-name",
async mutate() {
await setContentPageElementAttribute(
".list",
"test-name",
"value-" + Date.now()
);
},
},
{
desc:
"Adding an attribute with css reserved characters should flash the " +
"attribute",
attribute: "one:two",
async mutate() {
await setContentPageElementAttribute(
".list",
"one:two",
"value-" + Date.now()
);
},
},
{
desc: "Editing an attribute should flash the attribute",
attribute: "class",
async mutate() {
await setContentPageElementAttribute(
".list",
"class",
"list value-" + Date.now()
);
},
},
{
desc: "Multiple changes to an attribute should flash the attribute",
attribute: "class",
async mutate() {
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
const root = content.document.querySelector(".list");
root.removeAttribute("class");
root.setAttribute("class", "list value-" + Date.now());
root.setAttribute("class", "list value-" + Date.now());
root.removeAttribute("class");
root.setAttribute("class", "list value-" + Date.now());
root.setAttribute("class", "list value-" + Date.now());
});
},
},
{
desc: "Removing an attribute should flash the node",
async mutate() {
await SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
const root = content.document.querySelector(".list");
root.removeAttribute("class");
});
},
},
];
add_task(async function () {
await pushPref("privacy.reduceTimerPrecision", false);
const { inspector } = await openInspectorForURL(TEST_URL);
// Make sure mutated nodes flash for a very long time so we can more easily
// assert they do
inspector.markup.CONTAINER_FLASHING_DURATION = 1000 * 60 * 60;
info("Getting the <ul.list> root node to test mutations on");
const rootNodeFront = await getNodeFront(".list", inspector);
info("Selecting the last element of the root node before starting");
await selectNode(".list .item:nth-child(2)", inspector);
for (const { mutate, flashedNode, desc, attribute } of TEST_DATA) {
info("Starting test: " + desc);
info("Mutating the DOM and listening for markupmutation event");
const onMutation = inspector.once("markupmutation");
await mutate();
const mutations = await onMutation;
info("Wait for the breadcrumbs widget to update if it needs to");
if (inspector.breadcrumbs._hasInterestingMutations(mutations)) {
await inspector.once("breadcrumbs-updated");
}
info("Asserting that the correct markup-container is flashing");
let flashingNodeFront = rootNodeFront;
if (flashedNode) {
flashingNodeFront = await getNodeFront(flashedNode, inspector);
}
if (attribute) {
await assertAttributeFlashing(flashingNodeFront, attribute, inspector);
} else {
await assertNodeFlashing(flashingNodeFront, inspector);
}
}
});
function assertNodeFlashing(nodeFront, inspector) {
const container = getContainerForNodeFront(nodeFront, inspector);
ok(container, "Markup container for node found");
ok(
container.tagState.classList.contains("theme-bg-contrast"),
"Markup container for node is flashing"
);
// Clear the mutation flashing timeout now that we checked the node was
// flashing.
clearTimeout(container._flashMutationTimer);
container._flashMutationTimer = null;
container.tagState.classList.remove("theme-bg-contrast");
}
function assertAttributeFlashing(nodeFront, attribute, inspector) {
const container = getContainerForNodeFront(nodeFront, inspector);
ok(container, "Markup container for node found");
ok(
container.editor.attrElements.get(attribute),
"Attribute exists on editor"
);
const attributeElement = container.editor.getAttributeElement(attribute);
ok(
attributeElement.classList.contains("theme-bg-contrast"),
"Element for " + attribute + " attribute is flashing"
);
attributeElement.classList.remove("theme-bg-contrast");
}