Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Errors

/* Any copyright is dedicated to the Public Domain.
"use strict";
// Test that the breadcrumbs widget refreshes correctly when there are markup
// mutations (and that it doesn't refresh when those mutations don't change its
// output).
const TEST_URI = URL_ROOT + "doc_inspector_breadcrumbs.html";
// Each item in the TEST_DATA array is a test case that should contain the
// following properties:
// - desc {String} A description of this test case (will be logged).
// - setup {Function*} A generator function (can yield promises) that sets up
// the test case. Useful for selecting a node before starting the test.
// - run {Function*} A generator function (can yield promises) that runs the
// actual test case, i.e, mutates the content DOM to cause the breadcrumbs
// to refresh, or not.
// - shouldRefresh {Boolean} Once the `run` function has completed, and the test
// has detected that the page has changed, this boolean instructs the test to
// verify if the breadcrumbs has refreshed or not.
// - output {Array} A list of strings for the text that should be found in each
// button after the test has run.
const TEST_DATA = [
{
desc: "Adding a child at the end of the chain shouldn't change anything",
async setup(inspector) {
await selectNode("#i1111", inspector);
},
async run({ walker, selection }) {
await walker.setInnerHTML(selection.nodeFront, "<b>test</b>");
},
shouldRefresh: false,
output: ["html", "body", "article#i1", "div#i11", "div#i111", "div#i1111"],
},
{
desc: "Updating an ID to an displayed element should refresh",
setup() {},
async run({ walker }) {
const node = await walker.querySelector(walker.rootNode, "#i1");
await node.modifyAttributes([
{
attributeName: "id",
newValue: "i1-changed",
},
]);
},
shouldRefresh: true,
output: [
"html",
"body",
"article#i1-changed",
"div#i11",
"div#i111",
"div#i1111",
],
},
{
desc: "Updating an class to a displayed element should refresh",
setup() {},
async run({ walker }) {
const node = await walker.querySelector(walker.rootNode, "body");
await node.modifyAttributes([
{
attributeName: "class",
newValue: "test-class",
},
]);
},
shouldRefresh: true,
output: [
"html",
"body.test-class",
"article#i1-changed",
"div#i11",
"div#i111",
"div#i1111",
],
},
{
desc:
"Updating a non id/class attribute to a displayed element should not " +
"refresh",
setup() {},
async run({ walker }) {
const node = await walker.querySelector(walker.rootNode, "#i11");
await node.modifyAttributes([
{
attributeName: "name",
newValue: "value",
},
]);
},
shouldRefresh: false,
output: [
"html",
"body.test-class",
"article#i1-changed",
"div#i11",
"div#i111",
"div#i1111",
],
},
{
desc: "Moving a child in an element that's not displayed should not refresh",
setup() {},
async run({ walker }) {
// Re-append #i1211 as a last child of #i2.
const parent = await walker.querySelector(walker.rootNode, "#i2");
const child = await walker.querySelector(walker.rootNode, "#i211");
await walker.insertBefore(child, parent);
},
shouldRefresh: false,
output: [
"html",
"body.test-class",
"article#i1-changed",
"div#i11",
"div#i111",
"div#i1111",
],
},
{
desc: "Moving an undisplayed child in a displayed element should not refresh",
setup() {},
async run({ walker }) {
// Re-append #i2 in body (move it to the end).
const parent = await walker.querySelector(walker.rootNode, "body");
const child = await walker.querySelector(walker.rootNode, "#i2");
await walker.insertBefore(child, parent);
},
shouldRefresh: false,
output: [
"html",
"body.test-class",
"article#i1-changed",
"div#i11",
"div#i111",
"div#i1111",
],
},
{
desc:
"Updating attributes on an element that's not displayed should not " +
"refresh",
setup() {},
async run({ walker }) {
const node = await walker.querySelector(walker.rootNode, "#i2");
await node.modifyAttributes([
{
attributeName: "id",
newValue: "i2-changed",
},
{
attributeName: "class",
newValue: "test-class",
},
]);
},
shouldRefresh: false,
output: [
"html",
"body.test-class",
"article#i1-changed",
"div#i11",
"div#i111",
"div#i1111",
],
},
{
desc: "Removing the currently selected node should refresh",
async setup(inspector) {
await selectNode("#i2-changed", inspector);
},
async run({ walker, selection }) {
await walker.removeNode(selection.nodeFront);
},
shouldRefresh: true,
output: ["html", "body.test-class"],
},
{
desc: "Changing the class of the currently selected node should refresh",
setup() {},
async run({ selection }) {
await selection.nodeFront.modifyAttributes([
{
attributeName: "class",
newValue: "test-class-changed",
},
]);
},
shouldRefresh: true,
output: ["html", "body.test-class-changed"],
},
{
desc: "Changing the id of the currently selected node should refresh",
setup() {},
async run({ selection }) {
await selection.nodeFront.modifyAttributes([
{
attributeName: "id",
newValue: "new-id",
},
]);
},
shouldRefresh: true,
output: ["html", "body#new-id.test-class-changed"],
},
];
add_task(async function () {
const { inspector } = await openInspectorForURL(TEST_URI);
const breadcrumbs = inspector.panelDoc.getElementById(
"inspector-breadcrumbs"
);
const container = breadcrumbs.querySelector(".html-arrowscrollbox-inner");
const win = container.ownerDocument.defaultView;
for (const { desc, setup, run, shouldRefresh, output } of TEST_DATA) {
info("Running test case: " + desc);
info(
"Listen to markupmutation events from the inspector to know when a " +
"test case has completed"
);
const onContentMutation = inspector.once("markupmutation");
info("Running setup");
await setup(inspector);
info("Listen to mutations on the breadcrumbs container");
let hasBreadcrumbsMutated = false;
const observer = new win.MutationObserver(mutations => {
// Only consider childList changes or tooltiptext/checked attributes
// changes. The rest may be mutations caused by the overflowing arrowbox.
for (const { type, attributeName } of mutations) {
const isChildList = type === "childList";
const isAttributes =
type === "attributes" &&
(attributeName === "checked" || attributeName === "tooltiptext");
if (isChildList || isAttributes) {
hasBreadcrumbsMutated = true;
break;
}
}
});
observer.observe(container, {
attributes: true,
childList: true,
subtree: true,
});
info("Running the test case");
await run(inspector);
info("Wait until the page has mutated");
await onContentMutation;
if (shouldRefresh) {
info("The breadcrumbs is expected to refresh, so wait for it");
await inspector.once("inspector-updated");
} else {
ok(
!inspector._updateProgress,
"The breadcrumbs widget is not currently updating"
);
}
is(shouldRefresh, hasBreadcrumbsMutated, "Has the breadcrumbs refreshed?");
observer.disconnect();
info("Check the output of the breadcrumbs widget");
is(container.childNodes.length, output.length, "Correct number of buttons");
for (let i = 0; i < container.childNodes.length; i++) {
is(
output[i],
container.childNodes[i].textContent,
"Text content for button " + i + " is correct"
);
}
}
});