Source code
Revision control
Copy as Markdown
Other Tools
<!DOCTYPE HTML>
<html>
<head>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/wai-aria/scripts/aria-utils.js"></script>
</head>
<body>
<custom-element id="customElement"></custom-element>
<custom-button id="customButton"></custom-button>
<!-- Targets -->
<div id="target" popover>Popover content</div>
<dialog id="dialog">Dialog content</dialog>
<script>
class CustomElement extends HTMLElement {
constructor() {
super();
this.internals_ = this.attachInternals();
}
}
customElements.define('custom-element', CustomElement);
class CustomButton extends HTMLElement {
static formAssociated = true;
constructor() {
super();
this.internals_ = this.attachInternals();
this.internals_.type = "button";
}
get disabled() {
return this.hasAttribute('disabled');
}
set disabled(value) {
if (value) {
this.setAttribute('disabled', '');
} else {
this.removeAttribute('disabled');
}
}
}
customElements.define('custom-button', CustomButton);
function resetState() {
document.getElementById("target").hidePopover();
document.getElementById("dialog").close();
document.getElementById('customButton').outerHTML = '<custom-button id="customButton">';
document.getElementById('customElement').outerHTML = '<custom-element id="customElement">';
}
// Test popover commands
test(t => {
const customElement = document.getElementById("customElement");
const target = document.getElementById("target");
customElement.setAttribute('commandfor', 'target');
customElement.setAttribute('command', 'toggle-popover');
t.add_cleanup(() => resetState());
// Custom element without type=button should not work with commandfor
assert_false(target.matches(':popover-open'), "Popover should start closed");
customElement.click();
assert_false(target.matches(':popover-open'), "Custom element without type=button should not trigger popover");
}, "Custom element without type=button does not support commandfor attribute.");
test(t => {
const customButton = document.getElementById("customButton");
const target = document.getElementById("target");
customButton.setAttribute('commandfor', 'target');
customButton.setAttribute('command', 'toggle-popover');
t.add_cleanup(() => resetState());
// Custom button with type=button should work
assert_false(target.matches(':popover-open'), "Popover should start closed");
customButton.click();
assert_true(target.matches(':popover-open'), "Custom button with type=button should open popover");
// Second click should close
customButton.click();
assert_false(target.matches(':popover-open'), "Second click should close popover");
}, "Custom button with type=button and command='toggle-popover' toggles popover.");
test(t => {
const customButton = document.getElementById("customButton");
const target = document.getElementById("target");
customButton.setAttribute('commandfor', 'target');
customButton.setAttribute('command', 'show-popover');
t.add_cleanup(() => resetState());
assert_false(target.matches(':popover-open'), "Popover should start closed");
customButton.click();
assert_true(target.matches(':popover-open'), "show-popover should open popover");
// Second click should not close (show action only opens)
customButton.click();
assert_true(target.matches(':popover-open'), "show-popover should not close popover on second click");
}, "Custom button with command='show-popover' only opens popover.");
test(t => {
const customButton = document.getElementById("customButton");
const target = document.getElementById("target");
customButton.setAttribute('commandfor', 'target');
customButton.setAttribute('command', 'hide-popover');
t.add_cleanup(() => resetState());
// Start with popover open
target.showPopover();
assert_true(target.matches(':popover-open'), "Popover should start open");
customButton.click();
assert_false(target.matches(':popover-open'), "hide-popover should close popover");
// Second click should not open (hide action only closes)
customButton.click();
assert_false(target.matches(':popover-open'), "hide-popover should not open popover on second click");
}, "Custom button with command='hide-popover' only closes popover.");
test(t => {
const customButton = document.getElementById("customButton");
const target = document.getElementById("target");
customButton.setAttribute('commandfor', 'target');
customButton.setAttribute('command', 'toggle-popover');
customButton.disabled = true;
t.add_cleanup(() => resetState());
// Disabled custom button should not work
assert_true(customButton.disabled, "Custom button should be disabled");
assert_false(target.matches(':popover-open'), "Popover should start closed");
customButton.click();
assert_false(target.matches(':popover-open'), "Disabled custom button should not open popover");
}, "Disabled custom button does not support commandfor.");
// Test dialog commands
test(t => {
const customButton = document.getElementById("customButton");
const dialog = document.getElementById("dialog");
customButton.setAttribute('commandfor', 'dialog');
customButton.setAttribute('command', 'show-modal');
t.add_cleanup(() => resetState());
assert_false(dialog.open, "Dialog should start closed");
customButton.click();
assert_true(dialog.open, "show-modal should open dialog");
}, "Custom button with command='show-modal' opens dialog.");
test(t => {
const customButton = document.getElementById("customButton");
const dialog = document.getElementById("dialog");
customButton.setAttribute('commandfor', 'dialog');
customButton.setAttribute('command', 'close');
t.add_cleanup(() => resetState());
// Start with dialog open
dialog.showModal();
assert_true(dialog.open, "Dialog should start open");
customButton.click();
assert_false(dialog.open, "close command should close dialog");
}, "Custom button with command='close' closes dialog.");
test(t => {
const customButton = document.getElementById("customButton");
const dialog = document.getElementById("dialog");
customButton.setAttribute('commandfor', 'dialog');
customButton.setAttribute('command', 'request-close');
t.add_cleanup(() => resetState());
// Test with dialog open - should close it
dialog.showModal();
assert_true(dialog.open, "Dialog should start open");
customButton.click();
assert_false(dialog.open, "request-close command should close open dialog");
// Test with dialog already closed - should do nothing
assert_false(dialog.open, "Dialog should be closed");
customButton.click();
assert_false(dialog.open, "request-close command should not open closed dialog");
}, "Custom button with command='request-close' closes open dialog and does nothing when dialog is already closed.");
// Test custom commands
test(t => {
const customButton = document.getElementById("customButton");
const target = document.getElementById("target");
let commandEventFired = false;
let receivedCommand = '';
customButton.setAttribute('commandfor', 'target');
customButton.setAttribute('command', '--my-custom-action');
const commandListener = (e) => {
commandEventFired = true;
receivedCommand = e.command;
};
target.addEventListener('command', commandListener, { signal: t.get_signal() });
t.add_cleanup(() => resetState());
customButton.click();
assert_true(commandEventFired, "Custom command should fire command event");
assert_equals(receivedCommand, '--my-custom-action', "Command event should contain custom command");
}, "Custom button with custom command fires command event.");
// Test invalid commands
test(t => {
const customButton = document.getElementById("customButton");
const target = document.getElementById("target");
let commandEventFired = false;
customButton.setAttribute('commandfor', 'target');
customButton.setAttribute('command', 'invalid-action');
const commandListener = (e) => {
commandEventFired = true;
};
target.addEventListener('command', commandListener, { signal: t.get_signal() });
t.add_cleanup(() => resetState());
customButton.click();
assert_false(commandEventFired, "Invalid command should not fire command event");
}, "Custom button with invalid command does not fire command event.");
</script>
</body>
</html>