summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/editing/run/undo-redo.html
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/editing/run/undo-redo.html
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/editing/run/undo-redo.html')
-rw-r--r--testing/web-platform/tests/editing/run/undo-redo.html226
1 files changed, 226 insertions, 0 deletions
diff --git a/testing/web-platform/tests/editing/run/undo-redo.html b/testing/web-platform/tests/editing/run/undo-redo.html
new file mode 100644
index 0000000000..69bee00c86
--- /dev/null
+++ b/testing/web-platform/tests/editing/run/undo-redo.html
@@ -0,0 +1,226 @@
+<!DOCTYPE html>
+<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="../include/editor-test-utils.js"></script>
+<iframe srcdoc=""></iframe>
+<script>
+"use strict";
+const iframe = document.querySelector("iframe");
+
+promise_test(async () => {
+ await new Promise(resolve => {
+ addEventListener("load", resolve, {once: true});
+ });
+}, "Waiting for load...");
+
+/**
+ * This test does NOT test whether the edit result is valid or invalid.
+ * This test just tests whether "undo" and "redo" restores previous state
+ * and additional "undo" and "redo" does not run unexpectedly.
+ *
+ * description: Set string to explain what's testing.
+ * editorInnerHTML: Set initial innerHTML value of editor.
+ * init: Set a function object if you need to test complicated cases, e.g.,
+ * testing with empty text node.
+ * run: Set a function object which run something modifying the editor (or
+ * does nothing).
+ * expectedUndoResult: Set an expected innerHTML result as string or array
+ * of the string. If this is not specified, it's compared
+ * with editorInnerHTML value.
+ * cleanUp: Set a function object if you need to clean something up after the
+ * test.
+ */
+
+const tests = [
+ {
+ description: "insertParagraph at start of a paragraph",
+ editorInnerHTML: "<p>[]abcdef</p>",
+ run: (win, doc, editingHost) => {
+ doc.execCommand("insertParagraph");
+ },
+ },
+ {
+ description: "insertParagraph at middle of a paragraph",
+ editorInnerHTML: "<p>abc[]def</p>",
+ run: (win, doc, editingHost) => {
+ doc.execCommand("insertParagraph");
+ },
+ },
+ {
+ description: "insertParagraph at end of a paragraph",
+ editorInnerHTML: "<p>abcdef[]</p>",
+ run: (win, doc, editingHost) => {
+ doc.execCommand("insertParagraph");
+ },
+ },
+ {
+ description: "insertParagraph at start of a listitem",
+ editorInnerHTML: "<ul><li>[]abcdef</li></ul>",
+ run: (win, doc, editingHost) => {
+ doc.execCommand("insertParagraph");
+ },
+ },
+ {
+ description: "insertParagraph at middle of a listitem",
+ editorInnerHTML: "<ul><li>abc[]def</li></ul>",
+ run: (win, doc, editingHost) => {
+ doc.execCommand("insertParagraph");
+ },
+ },
+ {
+ description: "insertParagraph at end of a listitem",
+ editorInnerHTML: "<ul><li>abcdef[]</li></ul>",
+ run: (win, doc, editingHost) => {
+ doc.execCommand("insertParagraph");
+ },
+ },
+ {
+ description: "insertLineBreak at start of a paragraph",
+ editorInnerHTML: "<p>[]abcdef</p>",
+ run: (win, doc, editingHost) => {
+ doc.execCommand("insertLineBreak");
+ },
+ },
+ {
+ description: "insertLineBreak at middle of a paragraph",
+ editorInnerHTML: "<p>abc[]def</p>",
+ run: (win, doc, editingHost) => {
+ doc.execCommand("insertLineBreak");
+ },
+ },
+ {
+ description: "insertLineBreak at end of a paragraph",
+ editorInnerHTML: "<p>abcdef[]</p>",
+ run: (win, doc, editingHost) => {
+ doc.execCommand("insertLineBreak");
+ },
+ },
+ {
+ description: "insertLineBreak at start of a listitem",
+ editorInnerHTML: "<ul><li>[]abcdef</li></ul>",
+ run: (win, doc, editingHost) => {
+ doc.execCommand("insertLineBreak");
+ },
+ },
+ {
+ description: "insertLineBreak at middle of a listitem",
+ editorInnerHTML: "<ul><li>abc[]def</li></ul>",
+ run: (win, doc, editingHost) => {
+ doc.execCommand("insertLineBreak");
+ },
+ },
+ {
+ description: "insertLineBreak at end of a listitem",
+ editorInnerHTML: "<ul><li>abcdef[]</li></ul>",
+ run: (win, doc, editingHost) => {
+ doc.execCommand("insertLineBreak");
+ },
+ },
+ {
+ description: "delete at start of second paragraph",
+ editorInnerHTML: "<p>abc</p><p>[]def</p>",
+ run: (win, doc, editingHost) => {
+ doc.execCommand("delete");
+ }
+ },
+ {
+ description: "forwarddelete at end of first paragraph",
+ editorInnerHTML: "<p>abc[]</p><p>def</p>",
+ run: (win, doc, editingHost) => {
+ doc.execCommand("forwarddelete");
+ }
+ },
+ {
+ description: "delete at start of second paragraph starting with an emoji",
+ editorInnerHTML: "<p>abc\uD83D\uDC49</p><p>[]\uD83D\uDC48def</p>",
+ run: (win, doc, editingHost) => {
+ doc.execCommand("delete");
+ }
+ },
+ {
+ description: "forwarddelete at end of first paragraph ending with an emoji",
+ editorInnerHTML: "<p>abc\uD83D\uDC49[]</p><p>\uD83D\uDC48def</p>",
+ run: (win, doc, editingHost) => {
+ doc.execCommand("forwarddelete");
+ }
+ },
+];
+
+for (const curTest of tests) {
+ promise_test(async t => {
+ await new Promise(resolve => {
+ iframe.addEventListener("load", resolve, {once: true});
+ iframe.srcdoc = "<html><body><div contenteditable></div></body></html>";
+ });
+ const contentDocument = iframe.contentDocument;
+ const contentWindow = iframe.contentWindow;
+ contentWindow.focus();
+ const editingHost = contentDocument.querySelector("div[contenteditable]");
+ const utils = new EditorTestUtils(editingHost, window);
+ utils.setupEditingHost(curTest.editorInnerHTML);
+ contentDocument.documentElement.scrollHeight; // flush pending things
+ if (typeof curTest.init == "function") {
+ await curTest.init(contentWindow, contentDocument, editingHost);
+ }
+ const initialValue = editingHost.innerHTML;
+ await curTest.run(contentWindow, contentDocument, editingHost);
+ const newValue = editingHost.innerHTML;
+ test(t2 => {
+ const ret = contentDocument.execCommand("undo");
+ if (curTest.expectedUndoResult !== undefined) {
+ if (typeof curTest.expectedUndoResult == "string") {
+ assert_equals(
+ editingHost.innerHTML,
+ curTest.expectedUndoResult,
+ `${t2.name}: should restore the innerHTML value`
+ );
+ } else {
+ assert_in_array(
+ editingHost.innerHTML,
+ curTest.expectedUndoResult,
+ `${t2.name}: should restore one of the innerHTML values`
+ );
+ }
+ } else {
+ assert_equals(
+ editingHost.innerHTML,
+ initialValue,
+ `${t2.name}: should restore the initial innerHTML value`
+ );
+ }
+ assert_true(ret, `${t2.name}: execCommand("undo") should return true`);
+ }, `${t.name} - first undo`);
+ test(t3 => {
+ const ret = contentDocument.execCommand("redo");
+ assert_equals(
+ editingHost.innerHTML,
+ newValue,
+ `${t3.name}: should restore the modified innerHTML value`
+ );
+ assert_true(ret, `${t3.name}: execCommand("redo") should return true`);
+ }, `${curTest.description} - first redo`);
+ test(t4 => {
+ const ret = contentDocument.execCommand("redo");
+ assert_equals(
+ editingHost.innerHTML,
+ newValue,
+ `${t4.name}: should not modify the modified innerHTML value`
+ );
+ assert_false(ret, `${t4.name}: execCommand("redo") should return false`);
+ }, `${curTest.description} - second redo`);
+ if (typeof curTest.cleanUp == "function") {
+ await curTest.cleanUp(contentWindow, contentDocument, editingHost);
+ }
+ iframe.srcdoc = "";
+ contentDocument.documentElement.scrollHeight; // flush pending things
+ await new Promise(resolve =>
+ requestAnimationFrame(
+ () => requestAnimationFrame(resolve)
+ )
+ );
+ }, curTest.description);
+}
+</script>