diff options
Diffstat (limited to 'testing/web-platform/mozilla/tests/editor/delete-space-after-double-click-selection.html')
-rw-r--r-- | testing/web-platform/mozilla/tests/editor/delete-space-after-double-click-selection.html | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/testing/web-platform/mozilla/tests/editor/delete-space-after-double-click-selection.html b/testing/web-platform/mozilla/tests/editor/delete-space-after-double-click-selection.html new file mode 100644 index 0000000000..6c065cbc12 --- /dev/null +++ b/testing/web-platform/mozilla/tests/editor/delete-space-after-double-click-selection.html @@ -0,0 +1,278 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <meta name="timeout" content="long"> + <title>Test for Bug 1783641</title> + <script src=/resources/testharness.js></script> + <script src=/resources/testharnessreport.js></script> + <script src="/resources/testdriver.js"></script> + <script src="/resources/testdriver-actions.js"></script> + <style> + .testStyle { + font-family: 'Courier New', Courier, monospace; + font-size: 12px; + padding: 0px; + width: 200px; + } + </style> +</head> +<body> + <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1783641">Mozilla Bug 1783641</a><br /> + <span class="testStyle" id="placeholder"></span> + <input class="testStyle" type="text" /> + <div class="testStyle" contenteditable></div> + <textarea class="testStyle"></textarea> + <script> + + promise_test(async t => { + await new Promise(resolve => { window.onload = resolve; }); + + await SpecialPowers.pushPrefEnv({ + set: [ + ["editor.word_select.delete_space_after_doubleclick_selection", true], + ["layout.word_select.eat_space_to_next_word", false] + ] + }); + }, "Test setup"); + const placeHolder = document.getElementById("placeholder"); + const deleteKey = "\uE017"; + + function waitForRender() { + return new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve))); + }; + + for (const selector of ["input", "div[contenteditable]", "textarea"]) { + const editableElement = document.querySelector(selector); + editableElement.focus(); + + /** + * Helper functions to set or get the value and the current selection of `editableElement`, + * regardless of its actual type. + */ + const setValue = (aValue) => { + editableElement.tagName.toLowerCase() == "div" + ? editableElement.innerHTML = aValue + : editableElement.value = aValue; + } + const getValue = () => { + return editableElement.tagName.toLowerCase() == "div" + ? editableElement.innerHTML + : editableElement.value; + }; + const getSelection = () => { + return editableElement.tagName.toLowerCase() == "div" + ? document.getSelection().toString() + : editableElement.value.substring( + editableElement.selectionStart, + editableElement.selectionEnd + ); + }; + + /** + * Places a double click in `editableElement` at exactly the end of `aPlaceHolderText` and press delete. + * `aPlaceholderText` therefore should contain the same text as the value of the `editableElement` + * up to the point where the doubleclick should happen. + * + * If `aSelectionValue` is defined, the selection created by the double click is compared to `aSelectionValue`. + */ + const doubleClickAndDelete = async (aPlaceHolderText, aSelectionValue = undefined) => { + placeHolder.innerHTML = aPlaceHolderText; + editableElement.focus(); + await waitForRender(); + const absInputPos = editableElement.getBoundingClientRect(); + selectionOffset = { + x: placeHolder.getBoundingClientRect().width, + y: Math.floor(placeHolder.getBoundingClientRect().height / 2) + }; + await (new test_driver.Actions() + // for some reason this still doesn't work: + // .pointerMove(Math.floor(selectionOffset.x), Math.floor(selectionOffset.y), { origin: editableElement }) + // but this does: + .pointerMove( + Math.floor(absInputPos.x + selectionOffset.x), + Math.floor(absInputPos.y + selectionOffset.y), + { origin: "viewport" } + ) + .pointerDown() + .pointerUp() + .pointerDown() + .pointerUp()) + .send() + await waitForRender(); + if (aSelectionValue !== undefined) { + assert_equals(getSelection(), aSelectionValue, "Wrong selection value!"); + } + return test_driver.send_keys(editableElement, deleteKey); + }; + if (editableElement.tagName.toLowerCase() == "div") { + promise_test(async t => { + setValue("<p>abc def<span></span></p>"); + await doubleClickAndDelete("abc de", "def"); + await waitForRender(); + assert_equals( + getValue(), + "<p>abc</p>", + "The <span> at the end of the string must be removed, as well as the whitespace in between words."); + }, `${editableElement.tagName}: An empty span at the end of the selection should be considered end of selection!`); + } + promise_test(async t => { + setValue("one two"); + await doubleClickAndDelete("on", "one"); + await waitForRender(); + assert_equals( + getValue(), + "two", + "The whitespace between words must be removed when a word at the beginning is selected and deleted!" + ); + }, `${editableElement.tagName}: Remove word at the beginning of string should remove the whitespace in between.`); + + promise_test(async t => { + setValue("one two"); + await doubleClickAndDelete("one tw", "two"); + await waitForRender(); + assert_equals( + getValue(), + "one", + "The whitespace between words must be removed when a word is selected at the end of the string and deleted!" + ); + }, `${editableElement.tagName}: Remove word at the end of a string should remove the whitespace in between.`); + + promise_test(async t => { + setValue("one two three"); + await doubleClickAndDelete("one tw", "two"); + await waitForRender(); + assert_equals( + getValue(), + "one three", + "One whitespace between words must be removed when a word is selected and deleted!" + ); + await waitForRender(); + if (editableElement.tagName.toLowerCase() == "div") { + document.getSelection().setBaseAndExtent( + editableElement.firstChild, + 0, + editableElement.firstChild, + 3 + ); + } + else { + editableElement.setSelectionRange(0, 3); + } + await test_driver.send_keys(editableElement, deleteKey); + // div[contenteditable] returns ' three' here. + assert_equals( + getValue().replace(/ /g, " "), + " three", + "The whitespace must not be removed when selecting a word without doubleclicking it!" + ); + + }, `${editableElement.tagName}: Remove word in the middle of a string should remove one whitespace ` + + "only if selection is created by double click."); + + promise_test(async t => { + setValue("one two three"); + await doubleClickAndDelete("one tw", "two"); + await waitForRender(); + assert_equals( + getValue(), + "one three", + "One whitespace between words must be removed when a word is selected and deleted!" + ); + }, `${editableElement.tagName}: Only one whitespace character should be removed when there are multiple.`); + + promise_test(async t => { + setValue("one two"); + await doubleClickAndDelete("one tw", "two"); + await waitForRender(); + assert_equals( + getValue(), + "one ", + "One whitespace character between words must be removed when a word is selected and deleted!" + ); + }, `${editableElement.tagName}: Only one whitespace character should be removed when ` + + "there are multiple whitespaces and the deleted range is the end of the string."); + + promise_test(async t => { + setValue("one two, three"); + await doubleClickAndDelete("one tw", "two"); + await waitForRender(); + assert_equals( + getValue(), + "one, three", + "The whitespace in front of the selected word must be removed when punctuation follows selection!" + ); + }, `${editableElement.tagName}: Removing a word before punctuation should remove the whitespace.`); + + promise_test(async t => { + setValue("one, two"); + await doubleClickAndDelete("one, tw", "two"); + await waitForRender(); + assert_equals( + getValue(), + "one,", + "The whitespace in front of the selected word must be removed!" + ); + }, `${editableElement.tagName}: Remove a word after punctuation should remove the whitespace.`); + + promise_test(async t => { + setValue("one\u00A0two, three"); // adds a + await doubleClickAndDelete("one tw", "two"); + await waitForRender(); + assert_equals( + getValue(), + "one, three", + "The whitespace between words must be removed when a word is selected and deleted!" + ); + }, `${editableElement.tagName}: Removing a word between a and punctuation should remove the nbsp character.`); + + if (editableElement.tagName.toLowerCase() == "div") { + promise_test(async t => { + setValue("one two<br>"); + await doubleClickAndDelete("one tw", "two"); + await waitForRender(); + assert_equals( + getValue(), + "one<br>", + "The line break must be preserved!" + ); + }, `${editableElement.tagName}: Removing a word in front of a line break should preserve the line break.`); + } + if (editableElement.tagName.toLowerCase() == "textarea") { + promise_test(async t => { + setValue("one two\n"); + await doubleClickAndDelete("one tw", "two"); + await waitForRender(); + assert_equals( + getValue(), + "one\n", + "The line break must be preserved!" + ); + }, `${editableElement.tagName}: RRemoving a word in front of a line break should preserve the line break.`); + } + promise_test(async t => { + setValue("one two"); + await doubleClickAndDelete("on", "one"); + await waitForRender(); + assert_equals( + getValue(), + "two", + "The whitespace between words must be removed when a word at the beginning is selected and deleted!" + ); + document.execCommand("undo", false, null); + assert_equals( + getValue(), + "one two", + "Undo action must restore the original state!" + ); + document.execCommand("redo", false, null); + assert_equals( + getValue(), + "two", + "Redo action must remove the word and whitespace again!" + ); + }, `${editableElement.tagName}: Undo and Redo actions should take the removed whitespace into account.`); + } + </script> +</body> +</html> |