Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
- This test has a WPT meta file that expects 2 subtest issues.
- This WPT test may be referenced by the following Test IDs:
- /input-events/input-events-delete-selection.html - WPT Dashboard Interop Dashboard
<!DOCTYPE html>
<meta charset="utf-8" />
<title>
Input Event tests for deletion with non-collapsed selection
</title>
<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>
<div id="rich" contenteditable></div>
<script>
let inputEventsLog = [];
const rich = document.getElementById("rich");
const isMacOS = navigator.platform.indexOf("Mac") === 0;
const MODIFIER_KEY = isMacOS ? "\uE00A" : "\uE009"; // Alt on Mac, Ctrl on others
function log(event) {
inputEventsLog.push({
type: event.type,
inputType: event.inputType,
data: event.data,
});
}
function resetRich() {
inputEventsLog = [];
rich.innerHTML = "";
}
function selectText(startNode, startOffset, endNode, endOffset) {
const selection = window.getSelection();
const range = document.createRange();
range.setStart(startNode, startOffset);
range.setEnd(endNode, endOffset);
selection.removeAllRanges();
selection.addRange(range);
}
rich.addEventListener("beforeinput", log);
rich.addEventListener("input", log);
// Test Ctrl+Backspace with non-collapsed selection
promise_test(async function () {
this.add_cleanup(resetRich);
rich.innerHTML = "hello world";
rich.focus();
const textNode = rich.firstChild;
// Select "or" in "world"
selectText(textNode, 6, textNode, 8);
// Simulate Ctrl+Backspace (Alt+Backspace on Mac)
await new test_driver.Actions()
.keyDown(MODIFIER_KEY)
.keyDown("\uE003") // Backspace
.keyUp("\uE003")
.keyUp(MODIFIER_KEY)
.send();
assert_equals(inputEventsLog.length, 2, "Should have 2 events");
const [beforeInputEvent, inputEvent] = inputEventsLog;
assert_equals(beforeInputEvent.type, "beforeinput");
assert_equals(inputEvent.type, "input");
// When deleting a selection, inputType should be deleteContentBackward
assert_equals(
beforeInputEvent.inputType,
"deleteContentBackward",
"Ctrl+Backspace with selection should report deleteContentBackward"
);
assert_equals(
inputEvent.inputType,
"deleteContentBackward",
"Ctrl+Backspace with selection should report deleteContentBackward"
);
}, "Ctrl+Backspace with non-collapsed selection should report deleteContentBackward");
// Test Ctrl+Backspace with caret (collapsed selection)
promise_test(async function () {
this.add_cleanup(resetRich);
rich.innerHTML = "hello world";
rich.focus();
const textNode = rich.firstChild;
// Place caret after "world"
selectText(textNode, 11, textNode, 11);
// Simulate Ctrl+Backspace (Alt+Backspace on Mac)
await new test_driver.Actions()
.keyDown(MODIFIER_KEY)
.keyDown("\uE003") // Backspace
.keyUp("\uE003")
.keyUp(MODIFIER_KEY)
.send();
assert_equals(inputEventsLog.length, 2, "Should have 2 events");
const [beforeInputEvent, inputEvent] = inputEventsLog;
assert_equals(beforeInputEvent.type, "beforeinput");
assert_equals(inputEvent.type, "input");
// With caret, should be deleteWordBackward
assert_equals(
beforeInputEvent.inputType,
"deleteWordBackward",
"Ctrl+Backspace with caret should report deleteWordBackward"
);
assert_equals(
inputEvent.inputType,
"deleteWordBackward",
"Ctrl+Backspace with caret should report deleteWordBackward"
);
}, "Ctrl+Backspace with caret should report deleteWordBackward");
// Test Ctrl+Delete with non-collapsed selection
promise_test(async function () {
this.add_cleanup(resetRich);
rich.innerHTML = "hello world";
rich.focus();
const textNode = rich.firstChild;
// Select "ell" in "hello"
selectText(textNode, 1, textNode, 4);
// Simulate Ctrl+Delete (Alt+Delete on Mac)
await new test_driver.Actions()
.keyDown(MODIFIER_KEY)
.keyDown("\uE017") // Delete
.keyUp("\uE017")
.keyUp(MODIFIER_KEY)
.send();
assert_equals(inputEventsLog.length, 2, "Should have 2 events");
const [beforeInputEvent, inputEvent] = inputEventsLog;
assert_equals(beforeInputEvent.type, "beforeinput");
assert_equals(inputEvent.type, "input");
// When deleting a selection, inputType should be deleteContentForward
assert_equals(
beforeInputEvent.inputType,
"deleteContentForward",
"Ctrl+Delete with selection should report deleteContentForward"
);
assert_equals(
inputEvent.inputType,
"deleteContentForward",
"Ctrl+Delete with selection should report deleteContentForward"
);
}, "Ctrl+Delete with non-collapsed selection should report deleteContentForward");
// Test Ctrl+Delete with caret (collapsed selection)
promise_test(async function () {
this.add_cleanup(resetRich);
rich.innerHTML = "hello world";
rich.focus();
const textNode = rich.firstChild;
// Place caret before "world"
selectText(textNode, 6, textNode, 6);
// Simulate Ctrl+Delete (Alt+Delete on Mac)
await new test_driver.Actions()
.keyDown(MODIFIER_KEY)
.keyDown("\uE017") // Delete
.keyUp("\uE017")
.keyUp(MODIFIER_KEY)
.send();
assert_equals(inputEventsLog.length, 2, "Should have 2 events");
const [beforeInputEvent, inputEvent] = inputEventsLog;
assert_equals(beforeInputEvent.type, "beforeinput");
assert_equals(inputEvent.type, "input");
// With caret, should be deleteWordForward
assert_equals(
beforeInputEvent.inputType,
"deleteWordForward",
"Ctrl+Delete with caret should report deleteWordForward"
);
assert_equals(
inputEvent.inputType,
"deleteWordForward",
"Ctrl+Delete with caret should report deleteWordForward"
);
}, "Ctrl+Delete with caret should report deleteWordForward");
// Test regular Backspace with non-collapsed selection (should still be deleteContentBackward)
promise_test(async function () {
this.add_cleanup(resetRich);
rich.innerHTML = "hello world";
rich.focus();
const textNode = rich.firstChild;
// Select "lo wo"
selectText(textNode, 3, textNode, 8);
// Simulate Backspace (without Ctrl)
await test_driver.send_keys(rich, "\uE003"); // Backspace
assert_equals(inputEventsLog.length, 2, "Should have 2 events");
const [beforeInputEvent, inputEvent] = inputEventsLog;
assert_equals(beforeInputEvent.type, "beforeinput");
assert_equals(inputEvent.type, "input");
assert_equals(
beforeInputEvent.inputType,
"deleteContentBackward",
"Regular Backspace with selection should report deleteContentBackward"
);
assert_equals(
inputEvent.inputType,
"deleteContentBackward",
"Regular Backspace with selection should report deleteContentBackward"
);
}, "Regular Backspace with non-collapsed selection should report deleteContentBackward");
// Test regular Delete with non-collapsed selection (should be deleteContentForward)
promise_test(async function () {
this.add_cleanup(resetRich);
rich.innerHTML = "hello world";
rich.focus();
const textNode = rich.firstChild;
// Select "lo wo"
selectText(textNode, 3, textNode, 8);
// Simulate Delete (without Ctrl)
await test_driver.send_keys(rich, "\uE017"); // Delete
assert_equals(inputEventsLog.length, 2, "Should have 2 events");
const [beforeInputEvent, inputEvent] = inputEventsLog;
assert_equals(beforeInputEvent.type, "beforeinput");
assert_equals(inputEvent.type, "input");
assert_equals(
beforeInputEvent.inputType,
"deleteContentForward",
"Regular Delete with selection should report deleteContentForward"
);
assert_equals(
inputEvent.inputType,
"deleteContentForward",
"Regular Delete with selection should report deleteContentForward"
);
}, "Regular Delete with non-collapsed selection should report deleteContentForward");
</script>