Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Warnings

<!DOCTYPE HTML>
<!-- Any copyright is dedicated to the Public Domain.
<html>
<head>
<meta charset="utf-8">
<title><!-- Shadow Parts issue with xul/xbl domparser --></title>
<link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
<script>
const { BrowserTestUtils } = ChromeUtils.importESModule(
);
const DEFAULT_SECTION_NAMES = ["one", "two", "three"];
function makeButton({ name, deckId }) {
let button = document.createElement("button", { is: "named-deck-button" });
button.setAttribute("name", name);
button.deckId = deckId;
button.textContent = name.toUpperCase();
return button;
}
function makeSection({ name }) {
let view = document.createElement("section");
view.setAttribute("name", name);
view.textContent = name + name;
return view;
}
function addSection({ name, deck, buttons }) {
let button = makeButton({ name, deckId: deck.id });
buttons.appendChild(button);
let view = makeSection({ name });
deck.appendChild(view);
return { button, view };
}
async function runTests({ deck, buttons }) {
const selectedSlot = deck.shadowRoot.querySelector('slot[name="selected"]');
const getButtonByName = name => buttons.querySelector(`[name="${name}"]`);
function checkState(name, count, empty = false) {
// Check that the right view is selected.
is(deck.selectedViewName, name, "The right view is selected");
// Verify there's one element in the slot.
let slottedEls = selectedSlot.assignedElements();
if (empty) {
is(slottedEls.length, 0, "The deck is empty");
} else {
is(slottedEls.length, 1, "There's one visible view");
is(
slottedEls[0].getAttribute("name"),
name,
"The correct view is in the slot"
);
}
// Check that the hidden properties are set.
let sections = deck.querySelectorAll("section");
is(sections.length, count, "There are the right number of sections");
for (let section of sections) {
let sectionName = section.getAttribute("name");
if (sectionName == name) {
is(section.slot, "selected", `${sectionName} is visible`);
} else {
is(section.slot, "", `${sectionName} is hidden`);
}
}
// Check the right button is selected.
is(buttons.children.length, count, "There are the right number of buttons");
for (let button of buttons.children) {
let buttonName = button.getAttribute("name");
let selected = buttonName == name;
is(
button.hasAttribute("selected"),
selected,
`${buttonName} is ${selected ? "selected" : "not selected"}`
);
}
}
// Check that the first view is selected by default.
checkState("one", 3);
// Switch to the third view.
info("Switch to section three");
getButtonByName("three").click();
checkState("three", 3);
// Add a new section, nothing changes.
info("Add section last");
let last = addSection({ name: "last", deck, buttons });
checkState("three", 4);
// We can switch to the new section.
last.button.click();
info("Switch to section last");
checkState("last", 4);
info("Switch view with selectedViewName");
let shown = BrowserTestUtils.waitForEvent(deck, "view-changed");
deck.selectedViewName = "two";
await shown;
checkState("two", 4);
info("Switch back to the last view to test removing selected view");
shown = BrowserTestUtils.waitForEvent(deck, "view-changed");
deck.setAttribute("selected-view", "last");
await shown;
checkState("last", 4);
// Removing the selected section leaves the selected slot empty.
info("Remove section last");
last.button.remove();
last.view.remove();
info("Should not have any selected views");
checkState("last", 3, true);
// Setting a missing view will give a "view-changed" event.
info("Set view to a missing name");
let hidden = BrowserTestUtils.waitForEvent(deck, "view-changed");
deck.selectedViewName = "missing";
await hidden;
checkState("missing", 3, true);
// Adding the view won't trigger "view-changed", but the view will slotted.
info("Add the missing view, it should be shown");
shown = BrowserTestUtils.waitForEvent(selectedSlot, "slotchange");
let viewChangedEvent = false;
let viewChangedFn = () => {
viewChangedEvent = true;
};
deck.addEventListener("view-changed", viewChangedFn);
addSection({ name: "missing", deck, buttons });
await shown;
deck.removeEventListener("view-changed", viewChangedFn);
ok(!viewChangedEvent, "The view-changed event didn't fire");
checkState("missing", 4);
}
async function setup({ beAsync, first, deckId }) {
// Make the deck and buttons.
const deck = document.createElement("named-deck");
deck.id = deckId;
for (let name of DEFAULT_SECTION_NAMES) {
deck.appendChild(makeSection({ name }));
}
const buttons = document.createElement("button-group");
for (let name of DEFAULT_SECTION_NAMES) {
buttons.appendChild(makeButton({ name, deckId }));
}
let ordered;
if (first == "deck") {
ordered = [deck, buttons];
} else if (first == "buttons") {
ordered = [buttons, deck];
} else {
throw new Error("Invalid order");
}
// Insert them in the specified order, possibly async.
document.body.appendChild(ordered.shift());
if (beAsync) {
await new Promise(resolve => requestAnimationFrame(resolve));
}
document.body.appendChild(ordered.shift());
return { deck, buttons };
}
add_task(async function testNamedDeckAndButtons() {
// Check adding the deck first.
dump("Running deck first tests synchronously");
await runTests(await setup({ beAsync: false, first: "deck", deckId: "deck-sync" }));
dump("Running deck first tests asynchronously");
await runTests(await setup({ beAsync: true, first: "deck", deckId: "deck-async" }));
// Check adding the buttons first.
dump("Running buttons first tests synchronously");
await runTests(await setup({ beAsync: false, first: "buttons", deckId: "buttons-sync" }));
dump("Running buttons first tests asynchronously");
await runTests(await setup({ beAsync: true, first: "buttons", deckId: "buttons-async" }));
});
add_task(async function testFocusAndClickMixing() {
const waitForAnimationFrame = () =>
new Promise(r => requestAnimationFrame(r));
const sendTab = (e = {}) => {
synthesizeKey("VK_TAB", e);
return waitForAnimationFrame();
};
const firstButton = document.createElement("button");
document.body.append(firstButton);
const { deck, buttons: buttonGroup } = await setup({
beAsync: false,
first: "buttons",
deckId: "focus-click-mixing",
});
const buttons = buttonGroup.children;
firstButton.focus();
const secondButton = document.createElement("button");
document.body.append(secondButton);
await sendTab();
is(document.activeElement, buttons[0], "first deck button is focused");
is(deck.selectedViewName, "one", "first view is shown");
await sendTab();
is(document.activeElement, secondButton, "focus moves out of group");
await sendTab({ shiftKey: true });
is(document.activeElement, buttons[0], "focus moves back to first button");
// Click on another tab button, this should make it the focusable button.
synthesizeMouseAtCenter(buttons[1], {});
await waitForAnimationFrame();
is(deck.selectedViewName, "two", "second view is shown");
if (document.activeElement != buttons[1]) {
// On Mac the button isn't focused on click, but it is on Windows/Linux.
await sendTab();
}
is(document.activeElement, buttons[1], "second deck button is focusable");
await sendTab();
is(document.activeElement, secondButton, "focus moved to second plain button");
await sendTab({ shiftKey: true });
is(document.activeElement, buttons[1], "second deck button is focusable");
await sendTab({ shiftKey: true });
is(
document.activeElement,
firstButton,
"next shift-tab moves out of button group"
);
});
</script>
</head>
<body>
</body>
</html>