diff --git a/src/test/system/attachment_gallery_test.js b/src/test/system/attachment_gallery_test.js index 2938046c3..47e885fe4 100644 --- a/src/test/system/attachment_gallery_test.js +++ b/src/test/system/attachment_gallery_test.js @@ -2,6 +2,7 @@ import { assert, clickToolbarButton, createImageAttachment, + expectDocument, insertAttachments, pressKey, test, @@ -9,52 +10,52 @@ import { typeCharacters, } from "test/test_helper" import { OBJECT_REPLACEMENT_CHARACTER } from "trix/constants" +import { nextFrame } from "../test_helpers/timing_helpers" const ORC = OBJECT_REPLACEMENT_CHARACTER testGroup("Attachment galleries", { template: "editor_empty" }, () => { - test("inserting more than one image attachment creates a gallery block", function (expectDocument) { + test("inserting more than one image attachment creates a gallery block", () => { insertAttachments(createImageAttachments(2)) assert.blockAttributes([ 0, 2 ], [ "attachmentGallery" ]) expectDocument(`${ORC}${ORC}\n`) }) - test("gallery formatting is removed from blocks containing less than two image attachments", function (expectDocument) { + test("gallery formatting is removed from blocks containing less than two image attachments", async () => { insertAttachments(createImageAttachments(2)) assert.blockAttributes([ 0, 2 ], [ "attachmentGallery" ]) getEditor().setSelectedRange([ 1, 2 ]) - pressKey("backspace", () => { - requestAnimationFrame(() => { - assert.blockAttributes([ 0, 2 ], []) - expectDocument(`${ORC}\n`) - }) - }) + + await pressKey("backspace") + await nextFrame() + + assert.blockAttributes([ 0, 2 ], []) + expectDocument(`${ORC}\n`) }) - test("typing in an attachment gallery block splits it", function (expectDocument) { + test("typing in an attachment gallery block splits it", async () => { insertAttachments(createImageAttachments(4)) getEditor().setSelectedRange(2) - typeCharacters("a", () => { - requestAnimationFrame(() => { - assert.blockAttributes([ 0, 2 ], [ "attachmentGallery" ]) - assert.blockAttributes([ 3, 4 ], []) - assert.blockAttributes([ 5, 7 ], [ "attachmentGallery" ]) - expectDocument(`${ORC}${ORC}\na\n${ORC}${ORC}\n`) - }) - }) + + await typeCharacters("a") + await nextFrame() + + assert.blockAttributes([ 0, 2 ], [ "attachmentGallery" ]) + assert.blockAttributes([ 3, 4 ], []) + assert.blockAttributes([ 5, 7 ], [ "attachmentGallery" ]) + expectDocument(`${ORC}${ORC}\na\n${ORC}${ORC}\n`) }) - test("inserting a gallery in a formatted block", (expectDocument) => { - clickToolbarButton({ attribute: "quote" }, () => { - typeCharacters("abc", () => { - insertAttachments(createImageAttachments(2)) - requestAnimationFrame(() => { - assert.blockAttributes([ 0, 3 ], [ "quote" ]) - assert.blockAttributes([ 4, 6 ], [ "attachmentGallery" ]) - expectDocument(`abc\n${ORC}${ORC}\n`) - }) - }) - }) + test("inserting a gallery in a formatted block", async () => { + await clickToolbarButton({ attribute: "quote" }) + await typeCharacters("abc") + + insertAttachments(createImageAttachments(2)) + await nextFrame() + + assert.blockAttributes([ 0, 3 ], [ "quote" ]) + assert.blockAttributes([ 4, 6 ], [ "attachmentGallery" ]) + expectDocument(`abc\n${ORC}${ORC}\n`) }) }) diff --git a/src/test/system/attachment_test.js b/src/test/system/attachment_test.js index e95735a09..486daf11a 100644 --- a/src/test/system/attachment_test.js +++ b/src/test/system/attachment_test.js @@ -2,13 +2,12 @@ import * as config from "trix/config" import { OBJECT_REPLACEMENT_CHARACTER } from "trix/constants" import { - after, assert, clickElement, clickToolbarButton, createFile, - defer, dragToCoordinates, + expectDocument, moveCursor, pressKey, test, @@ -16,129 +15,129 @@ import { triggerEvent, typeCharacters, } from "test/test_helper" +import { delay, nextFrame } from "../test_helpers/timing_helpers" testGroup("Attachments", { template: "editor_with_image" }, () => { - test("moving an image by drag and drop", (expectDocument) => { - typeCharacters("!", () => { - moveCursor({ direction: "right", times: 1 }, (coordinates) => { - const img = document.activeElement.querySelector("img") - triggerEvent(img, "mousedown") - defer(() => { - dragToCoordinates(coordinates, () => { - expectDocument(`!a${OBJECT_REPLACEMENT_CHARACTER}b\n`) - }) - }) - }) - }) + test("moving an image by drag and drop", async () => { + await typeCharacters("!") + + const coordinates = await moveCursor({ direction: "right", times: 1 }) + const img = document.activeElement.querySelector("img") + triggerEvent(img, "mousedown") + + await nextFrame() + await dragToCoordinates(coordinates) + + expectDocument(`!a${OBJECT_REPLACEMENT_CHARACTER}b\n`) }) - test("removing an image", (expectDocument) => { - after(20, () => { - clickElement(getFigure(), () => { - const closeButton = getFigure().querySelector("[data-trix-action=remove]") - clickElement(closeButton, () => { - expectDocument("ab\n") - }) - }) - }) + test("removing an image", async () => { + await delay(20) + await clickElement(getFigure()) + + const closeButton = getFigure().querySelector("[data-trix-action=remove]") + await clickElement(closeButton) + + expectDocument("ab\n") }) - test("editing an image caption", (expectDocument) => { - after(20, () => { - clickElement(findElement("figure"), () => { - clickElement(findElement("figcaption"), () => { - defer(() => { - const textarea = findElement("textarea") - assert.ok(textarea) - textarea.focus() - textarea.value = "my" - triggerEvent(textarea, "input") - defer(() => { - textarea.value = "" - defer(() => { - textarea.value = "my caption" - triggerEvent(textarea, "input") - pressKey("return", () => { - assert.notOk(findElement("textarea")) - assert.textAttributes([ 2, 3 ], { caption: "my caption" }) - assert.locationRange({ index: 0, offset: 3 }) - expectDocument(`ab${OBJECT_REPLACEMENT_CHARACTER}\n`) - }) - }) - }) - }) - }) - }) - }) + test("editing an image caption", async () => { + await delay(20) + + await clickElement(findElement("figure")) + await clickElement(findElement("figcaption")) + + await nextFrame() + + const textarea = findElement("textarea") + assert.ok(textarea) + + textarea.focus() + textarea.value = "my" + triggerEvent(textarea, "input") + + await nextFrame() + textarea.value = "" + + await nextFrame() + textarea.value = "my caption" + triggerEvent(textarea, "input") + + await pressKey("return") + assert.notOk(findElement("textarea")) + assert.textAttributes([ 2, 3 ], { caption: "my caption" }) + assert.locationRange({ index: 0, offset: 3 }) + expectDocument(`ab${OBJECT_REPLACEMENT_CHARACTER}\n`) }) - test("editing an attachment caption with no filename", (done) => - after(20, () => { - let captionElement = findElement("figcaption") - assert.ok(captionElement.clientHeight > 0) - assert.equal(captionElement.getAttribute("data-trix-placeholder"), config.lang.captionPlaceholder) - - clickElement(findElement("figure"), () => { - captionElement = findElement("figcaption") - assert.ok(captionElement.clientHeight > 0) - assert.equal(captionElement.getAttribute("data-trix-placeholder"), config.lang.captionPlaceholder) - done() - }) - })) - - test("updating an attachment's href attribute while editing its caption", (expectDocument) => { + test("editing an attachment caption with no filename", async () => { + await delay(20) + + let captionElement = findElement("figcaption") + assert.ok(captionElement.clientHeight > 0) + assert.equal(captionElement.getAttribute("data-trix-placeholder"), config.lang.captionPlaceholder) + + await clickElement(findElement("figure")) + + captionElement = findElement("figcaption") + assert.ok(captionElement.clientHeight > 0) + assert.equal(captionElement.getAttribute("data-trix-placeholder"), config.lang.captionPlaceholder) + }) + + test("updating an attachment's href attribute while editing its caption", async () => { const attachment = getEditorController().attachmentManager.getAttachments()[0] - after(20, () => { - clickElement(findElement("figure"), () => { - clickElement(findElement("figcaption"), () => { - defer(() => { - let textarea = findElement("textarea") - assert.ok(textarea) - textarea.focus() - textarea.value = "my caption" - triggerEvent(textarea, "input") - attachment.setAttributes({ href: "https://example.com" }) - defer(() => { - textarea = findElement("textarea") - assert.ok(document.activeElement === textarea) - assert.equal(textarea.value, "my caption") - pressKey("return", () => { - assert.notOk(findElement("textarea")) - assert.textAttributes([ 2, 3 ], { caption: "my caption" }) - assert.locationRange({ index: 0, offset: 3 }) - expectDocument(`ab${OBJECT_REPLACEMENT_CHARACTER}\n`) - }) - }) - }) - }) - }) - }) + await delay(20) + + await clickElement(findElement("figure")) + await clickElement(findElement("figcaption")) + + await nextFrame() + + let textarea = findElement("textarea") + assert.ok(textarea) + textarea.focus() + textarea.value = "my caption" + triggerEvent(textarea, "input") + attachment.setAttributes({ href: "https://example.com" }) + + await nextFrame() + + textarea = findElement("textarea") + assert.ok(document.activeElement === textarea) + assert.equal(textarea.value, "my caption") + + await pressKey("return") + + assert.notOk(findElement("textarea")) + assert.textAttributes([ 2, 3 ], { caption: "my caption" }) + assert.locationRange({ index: 0, offset: 3 }) + + expectDocument(`ab${OBJECT_REPLACEMENT_CHARACTER}\n`) }) testGroup("File insertion", { template: "editor_empty" }, () => { - test("inserting a file in a formatted block", (expectDocument) => { - clickToolbarButton({ attribute: "bullet" }, () => { - clickToolbarButton({ attribute: "bold" }, () => { - getComposition().insertFile(createFile()) - assert.blockAttributes([ 0, 1 ], [ "bulletList", "bullet" ]) - assert.textAttributes([ 0, 1 ], {}) - expectDocument(`${OBJECT_REPLACEMENT_CHARACTER}\n`) - }) - }) + test("inserting a file in a formatted block", async () => { + await clickToolbarButton({ attribute: "bullet" }) + await clickToolbarButton({ attribute: "bold" }) + + getComposition().insertFile(createFile()) + + assert.blockAttributes([ 0, 1 ], [ "bulletList", "bullet" ]) + assert.textAttributes([ 0, 1 ], {}) + expectDocument(`${OBJECT_REPLACEMENT_CHARACTER}\n`) }) - test("inserting a files in a formatted block", (expectDocument) => { - clickToolbarButton({ attribute: "quote" }, () => { - clickToolbarButton({ attribute: "italic" }, () => { - getComposition().insertFiles([ createFile(), createFile() ]) - - assert.blockAttributes([ 0, 2 ], [ "quote" ]) - assert.textAttributes([ 0, 1 ], {}) - assert.textAttributes([ 1, 2 ], {}) - expectDocument(`${OBJECT_REPLACEMENT_CHARACTER}${OBJECT_REPLACEMENT_CHARACTER}\n`) - }) - }) + test("inserting a files in a formatted block", async () => { + await clickToolbarButton({ attribute: "quote" }) + await clickToolbarButton({ attribute: "italic" }) + + getComposition().insertFiles([ createFile(), createFile() ]) + + assert.blockAttributes([ 0, 2 ], [ "quote" ]) + assert.textAttributes([ 0, 1 ], {}) + assert.textAttributes([ 1, 2 ], {}) + expectDocument(`${OBJECT_REPLACEMENT_CHARACTER}${OBJECT_REPLACEMENT_CHARACTER}\n`) }) }) }) diff --git a/src/test/system/basic_input_test.js b/src/test/system/basic_input_test.js index 23bb0b4cd..580975d2e 100644 --- a/src/test/system/basic_input_test.js +++ b/src/test/system/basic_input_test.js @@ -1,9 +1,9 @@ import * as config from "trix/config" import { assert, - defer, dragToCoordinates, expandSelection, + expectDocument, insertNode, moveCursor, pressKey, @@ -15,91 +15,125 @@ import { typeCharacters, } from "test/test_helper" +import { nextFrame } from "../test_helpers/timing_helpers" + testGroup("Basic input", { template: "editor_empty" }, () => { - test("typing", (expectDocument) => typeCharacters("abc", () => expectDocument("abc\n"))) - - test("backspacing", (expectDocument) => - typeCharacters("abc\b", () => { - expectDocument("ab\n") - })) - - test("pressing delete", (expectDocument) => - typeCharacters("ab", () => moveCursor("left", () => pressKey("delete", () => expectDocument("a\n"))))) - - test("pressing return", (expectDocument) => - typeCharacters("ab", () => pressKey("return", () => typeCharacters("c", () => expectDocument("ab\nc\n"))))) - - test("pressing escape in Safari", (expectDocument) => - typeCharacters("a", () => { - if (triggerEvent(document.activeElement, "keydown", { charCode: 0, keyCode: 27, which: 27, key: "Escape", code: "Escape" })) { - triggerEvent(document.activeElement, "keypress", { charCode: 27, keyCode: 27, which: 27, key: "Escape", code: "Escape" }) - } - defer(() => expectDocument("a\n")) - })) - - test("pressing escape in Firefox", (expectDocument) => - typeCharacters("a", () => { - if (triggerEvent(document.activeElement, "keydown", { charCode: 0, keyCode: 27, which: 27, key: "Escape", code: "Escape" })) { - triggerEvent(document.activeElement, "keypress", { charCode: 0, keyCode: 27, which: 0, key: "Escape", code: "Escape" }) - } - defer(() => expectDocument("a\n")) - })) - - test("pressing escape in Chrome", (expectDocument) => - typeCharacters("a", () => { - triggerEvent(document.activeElement, "keydown", { - charCode: 0, - keyCode: 27, - which: 27, - key: "Escape", - code: "Escape", - }) - defer(() => expectDocument("a\n")) - })) - - test("cursor left", (expectDocument) => - typeCharacters("ac", () => moveCursor("left", () => typeCharacters("b", () => expectDocument("abc\n"))))) - - test("replace entire document", (expectDocument) => - typeCharacters("abc", () => selectAll(() => typeCharacters("d", () => expectDocument("d\n"))))) - - test("remove entire document", (expectDocument) => - typeCharacters("abc", () => selectAll(() => typeCharacters("\b", () => expectDocument("\n"))))) - - test("drag text", (expectDocument) => - typeCharacters("abc", () => - moveCursor({ direction: "left", times: 2 }, (coordinates) => - moveCursor("right", () => - expandSelection("right", () => dragToCoordinates(coordinates, () => expectDocument("acb\n"))) - ) - ) - )) - - testIf(config.input.getLevel() === 0, "inserting newline after cursor (control + o)", (expectDocument) => { - typeCharacters("ab", () => { - moveCursor("left", () => { - triggerEvent(document.activeElement, "keydown", { charCode: 0, keyCode: 79, which: 79, ctrlKey: true }) - defer(() => { - assert.locationRange({ index: 0, offset: 1 }) - expectDocument("a\nb\n") - }) - }) + test("typing", async () => { + await typeCharacters("abc") + expectDocument("abc\n") + }) + + test("backspacing", async () => { + await typeCharacters("abc\b") + expectDocument("ab\n") + }) + + test("pressing delete", async () => { + await typeCharacters("ab") + await moveCursor("left") + await pressKey("delete") + expectDocument("a\n") + }) + + test("pressing return", async () => { + await typeCharacters("ab") + await pressKey("return") + await typeCharacters("c") + + expectDocument("ab\nc\n") + }) + + test("pressing escape in Safari", async () => { + await typeCharacters("a") + + if (triggerEvent(document.activeElement, "keydown", { charCode: 0, keyCode: 27, which: 27, key: "Escape", code: "Escape" })) { + triggerEvent(document.activeElement, "keypress", { charCode: 27, keyCode: 27, which: 27, key: "Escape", code: "Escape" }) + } + + await nextFrame() + expectDocument("a\n") + }) + + test("pressing escape in Firefox", async () => { + await typeCharacters("a") + if (triggerEvent(document.activeElement, "keydown", { charCode: 0, keyCode: 27, which: 27, key: "Escape", code: "Escape" })) { + triggerEvent(document.activeElement, "keypress", { charCode: 0, keyCode: 27, which: 0, key: "Escape", code: "Escape" }) + } + await nextFrame() + expectDocument("a\n") + }) + + test("pressing escape in Chrome", async () => { + await typeCharacters("a") + triggerEvent(document.activeElement, "keydown", { + charCode: 0, + keyCode: 27, + which: 27, + key: "Escape", + code: "Escape", }) + await nextFrame() + expectDocument("a\n") + }) + + test("cursor left", async () => { + await typeCharacters("ac") + await moveCursor("left") + await typeCharacters("b") + + expectDocument("abc\n") + }) + + test("replace entire document", async () => { + await typeCharacters("abc") + await selectAll() + await typeCharacters("d") + + expectDocument("d\n") + }) + + test("remove entire document", async () => { + await typeCharacters("abc") + await selectAll() + await typeCharacters("\b") + + expectDocument("\n") + }) + + test("drag text", async () => { + await typeCharacters("abc") + const coordinates = await moveCursor({ direction: "left", times: 2 }) + await nextFrame() + + await moveCursor("right") + await expandSelection("right") + await dragToCoordinates(coordinates) + + await expectDocument("acb\n") + }) + + testIf(config.input.getLevel() === 0, "inserting newline after cursor (control + o)", async () => { + await typeCharacters("ab") + await moveCursor("left") + + triggerEvent(document.activeElement, "keydown", { charCode: 0, keyCode: 79, which: 79, ctrlKey: true }) + await nextFrame() + + assert.locationRange({ index: 0, offset: 1 }) + expectDocument("a\nb\n") }) - testIf(config.input.getLevel() === 0, "inserting ó with control + alt + o (AltGr)", (expectDocument) => { - typeCharacters("ab", () => { - moveCursor("left", () => { - if (triggerEvent(document.activeElement, "keydown", { charCode: 0, keyCode: 79, which: 79, altKey: true, ctrlKey: true })) { - triggerEvent(document.activeElement, "keypress", { charCode: 243, keyCode: 243, which: 243, altKey: true, ctrlKey: true }) - insertNode(document.createTextNode("ó")) - } - - defer(() => { - assert.locationRange({ index: 0, offset: 2 }) - expectDocument("aób\n") - }) - }) - }) + testIf(config.input.getLevel() === 0, "inserting ó with control + alt + o (AltGr)", async () => { + await typeCharacters("ab") + await moveCursor("left") + + if (triggerEvent(document.activeElement, "keydown", { charCode: 0, keyCode: 79, which: 79, altKey: true, ctrlKey: true })) { + triggerEvent(document.activeElement, "keypress", { charCode: 243, keyCode: 243, which: 243, altKey: true, ctrlKey: true }) + insertNode(document.createTextNode("ó")) + } + + await nextFrame() + assert.locationRange({ index: 0, offset: 2 }) + expectDocument("aób\n") }) }) diff --git a/src/test/system/block_formatting_test.js b/src/test/system/block_formatting_test.js index 8c45038c2..4ad953e5a 100644 --- a/src/test/system/block_formatting_test.js +++ b/src/test/system/block_formatting_test.js @@ -5,8 +5,8 @@ import Document from "trix/models/document" import { assert, clickToolbarButton, - defer, expandSelection, + expectDocument, isToolbarButtonActive, isToolbarButtonDisabled, moveCursor, @@ -17,70 +17,59 @@ import { testGroup, typeCharacters, } from "test/test_helper" +import { nextFrame } from "../test_helpers/timing_helpers" testGroup("Block formatting", { template: "editor_empty" }, () => { - test("applying block attributes", (done) => { - typeCharacters("abc", () => { - clickToolbarButton({ attribute: "quote" }, () => { - assert.blockAttributes([ 0, 4 ], [ "quote" ]) - assert.ok(isToolbarButtonActive({ attribute: "quote" })) - clickToolbarButton({ attribute: "code" }, () => { - assert.blockAttributes([ 0, 4 ], [ "quote", "code" ]) - assert.ok(isToolbarButtonActive({ attribute: "code" })) - clickToolbarButton({ attribute: "code" }, () => { - assert.blockAttributes([ 0, 4 ], [ "quote" ]) - assert.notOk(isToolbarButtonActive({ attribute: "code" })) - assert.ok(isToolbarButtonActive({ attribute: "quote" })) - done() - }) - }) - }) - }) - }) - - test("applying block attributes to text after newline", (done) => { - typeCharacters("a\nbc", () => { - clickToolbarButton({ attribute: "quote" }, () => { - assert.blockAttributes([ 0, 2 ], []) - assert.blockAttributes([ 2, 4 ], [ "quote" ]) - done() - }) - }) - }) - - test("applying block attributes to text between newlines", (done) => { - typeCharacters("ab\ndef\nghi\nj\n", () => { - moveCursor({ direction: "left", times: 2 }, () => { - expandSelection({ direction: "left", times: 5 }, () => { - clickToolbarButton({ attribute: "quote" }, () => { - assert.blockAttributes([ 0, 3 ], []) - assert.blockAttributes([ 3, 11 ], [ "quote" ]) - assert.blockAttributes([ 11, 13 ], []) - done() - }) - }) - }) - }) - }) - - test("applying bullets to text with newlines", (done) => { - typeCharacters("abc\ndef\nghi\njkl\nmno\n", () => { - moveCursor({ direction: "left", times: 2 }, () => { - expandSelection({ direction: "left", times: 15 }, () => { - clickToolbarButton({ attribute: "bullet" }, () => { - assert.blockAttributes([ 0, 4 ], [ "bulletList", "bullet" ]) - assert.blockAttributes([ 4, 8 ], [ "bulletList", "bullet" ]) - assert.blockAttributes([ 8, 12 ], [ "bulletList", "bullet" ]) - assert.blockAttributes([ 12, 16 ], [ "bulletList", "bullet" ]) - assert.blockAttributes([ 16, 20 ], [ "bulletList", "bullet" ]) - done() - }) - }) - }) - }) - }) - - test("applying block attributes to adjacent unformatted blocks consolidates them", (done) => { + test("applying block attributes", async () => { + await typeCharacters("abc") + await clickToolbarButton({ attribute: "quote" }) + + assert.blockAttributes([ 0, 4 ], [ "quote" ]) + assert.ok(isToolbarButtonActive({ attribute: "quote" })) + + await clickToolbarButton({ attribute: "code" }) + assert.blockAttributes([ 0, 4 ], [ "quote", "code" ]) + assert.ok(isToolbarButtonActive({ attribute: "code" })) + + await clickToolbarButton({ attribute: "code" }) + assert.blockAttributes([ 0, 4 ], [ "quote" ]) + assert.notOk(isToolbarButtonActive({ attribute: "code" })) + assert.ok(isToolbarButtonActive({ attribute: "quote" })) + }) + + test("applying block attributes to text after newline", async () => { + await typeCharacters("a\nbc") + await clickToolbarButton({ attribute: "quote" }) + + assert.blockAttributes([ 0, 2 ], []) + assert.blockAttributes([ 2, 4 ], [ "quote" ]) + }) + + test("applying block attributes to text between newlines", async () => { + await typeCharacters("ab\ndef\nghi\nj\n") + await moveCursor({ direction: "left", times: 2 }) + await expandSelection({ direction: "left", times: 5 }) + await clickToolbarButton({ attribute: "quote" }) + + assert.blockAttributes([ 0, 3 ], []) + assert.blockAttributes([ 3, 11 ], [ "quote" ]) + assert.blockAttributes([ 11, 13 ], []) + }) + + test("applying bullets to text with newlines", async () => { + await typeCharacters("abc\ndef\nghi\njkl\nmno\n") + await moveCursor({ direction: "left", times: 2 }) + await expandSelection({ direction: "left", times: 15 }) + await clickToolbarButton({ attribute: "bullet" }) + + assert.blockAttributes([ 0, 4 ], [ "bulletList", "bullet" ]) + assert.blockAttributes([ 4, 8 ], [ "bulletList", "bullet" ]) + assert.blockAttributes([ 8, 12 ], [ "bulletList", "bullet" ]) + assert.blockAttributes([ 12, 16 ], [ "bulletList", "bullet" ]) + assert.blockAttributes([ 16, 20 ], [ "bulletList", "bullet" ]) + }) + + test("applying block attributes to adjacent unformatted blocks consolidates them", async () => { const document = new Document([ new Block(Text.textForStringWithAttributes("1"), [ "bulletList", "bullet" ]), new Block(Text.textForStringWithAttributes("a"), []), @@ -95,110 +84,95 @@ testGroup("Block formatting", { template: "editor_empty" }, () => { { index: 0, offset: 0 }, { index: 5, offset: 1 }, ]) - defer(() => { - clickToolbarButton({ attribute: "quote" }, () => { - assert.blockAttributes([ 0, 2 ], [ "bulletList", "bullet", "quote" ]) - assert.blockAttributes([ 2, 8 ], [ "quote" ]) - assert.blockAttributes([ 8, 10 ], [ "bulletList", "bullet", "quote" ]) - assert.blockAttributes([ 10, 12 ], [ "bulletList", "bullet", "quote" ]) - done() - }) - }) - }) - - test("breaking out of the end of a block", (done) => { - typeCharacters("abc", () => { - clickToolbarButton({ attribute: "quote" }, () => { - typeCharacters("\n\n", () => { - const document = getDocument() - assert.equal(document.getBlockCount(), 2) - - let block = document.getBlockAtIndex(0) - assert.deepEqual(block.getAttributes(), [ "quote" ]) - assert.equal(block.toString(), "abc\n") - - block = document.getBlockAtIndex(1) - assert.deepEqual(block.getAttributes(), []) - assert.equal(block.toString(), "\n") - - assert.locationRange({ index: 1, offset: 0 }) - done() - }) - }) - }) - }) - - test("breaking out of the middle of a block before character", (done) => { + + await nextFrame() + await clickToolbarButton({ attribute: "quote" }) + + assert.blockAttributes([ 0, 2 ], [ "bulletList", "bullet", "quote" ]) + assert.blockAttributes([ 2, 8 ], [ "quote" ]) + assert.blockAttributes([ 8, 10 ], [ "bulletList", "bullet", "quote" ]) + assert.blockAttributes([ 10, 12 ], [ "bulletList", "bullet", "quote" ]) + }) + + test("breaking out of the end of a block", async () => { + await typeCharacters("abc") + await clickToolbarButton({ attribute: "quote" }) + await typeCharacters("\n\n") + + const document = getDocument() + assert.equal(document.getBlockCount(), 2) + + let block = document.getBlockAtIndex(0) + assert.deepEqual(block.getAttributes(), [ "quote" ]) + assert.equal(block.toString(), "abc\n") + + block = document.getBlockAtIndex(1) + assert.deepEqual(block.getAttributes(), []) + assert.equal(block.toString(), "\n") + + assert.locationRange({ index: 1, offset: 0 }) + }) + + test("breaking out of the middle of a block before character", async () => { // * = cursor // // ab // *c // - typeCharacters("abc", () => { - clickToolbarButton({ attribute: "quote" }, () => { - moveCursor("left", () => { - typeCharacters("\n\n", () => { - const document = getDocument() - assert.equal(document.getBlockCount(), 3) - - let block = document.getBlockAtIndex(0) - assert.deepEqual(block.getAttributes(), [ "quote" ]) - assert.equal(block.toString(), "ab\n") - - block = document.getBlockAtIndex(1) - assert.deepEqual(block.getAttributes(), []) - assert.equal(block.toString(), "\n") - - block = document.getBlockAtIndex(2) - assert.deepEqual(block.getAttributes(), [ "quote" ]) - assert.equal(block.toString(), "c\n") - - assert.locationRange({ index: 2, offset: 0 }) - done() - }) - }) - }) - }) - }) - - test("breaking out of the middle of a block before newline", (done) => { + await typeCharacters("abc") + await clickToolbarButton({ attribute: "quote" }) + await moveCursor("left") + await typeCharacters("\n\n") + + const document = getDocument() + assert.equal(document.getBlockCount(), 3) + + let block = document.getBlockAtIndex(0) + assert.deepEqual(block.getAttributes(), [ "quote" ]) + assert.equal(block.toString(), "ab\n") + + block = document.getBlockAtIndex(1) + assert.deepEqual(block.getAttributes(), []) + assert.equal(block.toString(), "\n") + + block = document.getBlockAtIndex(2) + assert.deepEqual(block.getAttributes(), [ "quote" ]) + assert.equal(block.toString(), "c\n") + + assert.locationRange({ index: 2, offset: 0 }) + }) + + test("breaking out of the middle of a block before newline", async () => { // * = cursor // // ab // * // c // - typeCharacters("abc", () => { - clickToolbarButton({ attribute: "quote" }, () => { - moveCursor("left", () => { - typeCharacters("\n", () => { - moveCursor("left", () => { - typeCharacters("\n\n", () => { - const document = getDocument() - assert.equal(document.getBlockCount(), 3) - - let block = document.getBlockAtIndex(0) - assert.deepEqual(block.getAttributes(), [ "quote" ]) - assert.equal(block.toString(), "ab\n") - - block = document.getBlockAtIndex(1) - assert.deepEqual(block.getAttributes(), []) - assert.equal(block.toString(), "\n") - - block = document.getBlockAtIndex(2) - assert.deepEqual(block.getAttributes(), [ "quote" ]) - assert.equal(block.toString(), "c\n") - - done() - }) - }) - }) - }) - }) - }) - }) - - test("breaking out of a formatted block with adjacent non-formatted blocks", (expectDocument) => { + await typeCharacters("abc") + await clickToolbarButton({ attribute: "quote" }) + await moveCursor("left") + await typeCharacters("\n") + await moveCursor("left") + await typeCharacters("\n\n") + + const document = getDocument() + assert.equal(document.getBlockCount(), 3) + + let block = document.getBlockAtIndex(0) + assert.deepEqual(block.getAttributes(), [ "quote" ]) + assert.equal(block.toString(), "ab\n") + + block = document.getBlockAtIndex(1) + assert.deepEqual(block.getAttributes(), []) + assert.equal(block.toString(), "\n") + + block = document.getBlockAtIndex(2) + assert.deepEqual(block.getAttributes(), [ "quote" ]) + assert.equal(block.toString(), "c\n") + }) + + test("breaking out of a formatted block with adjacent non-formatted blocks", async () => { // * = cursor // // a @@ -213,260 +187,204 @@ testGroup("Block formatting", { template: "editor_empty" }, () => { replaceDocument(document) getEditor().setSelectedRange(3) - typeCharacters("\n\n", () => { - document = getDocument() - assert.equal(document.getBlockCount(), 4) - assert.blockAttributes([ 0, 1 ], []) - assert.blockAttributes([ 2, 3 ], [ "quote" ]) - assert.blockAttributes([ 4, 5 ], []) - assert.blockAttributes([ 5, 6 ], []) - expectDocument("a\nb\n\nc\n") - }) + await typeCharacters("\n\n") + + document = getDocument() + assert.equal(document.getBlockCount(), 4) + assert.blockAttributes([ 0, 1 ], []) + assert.blockAttributes([ 2, 3 ], [ "quote" ]) + assert.blockAttributes([ 4, 5 ], []) + assert.blockAttributes([ 5, 6 ], []) + expectDocument("a\nb\n\nc\n") }) - test("breaking out a block after newline at offset 0", (done) => // * = cursor + test("breaking out a block after newline at offset 0", async () => { + // * = cursor // // // *a // - typeCharacters("a", () => { - clickToolbarButton({ attribute: "quote" }, () => { - moveCursor("left", () => { - typeCharacters("\n\n", () => { - const document = getDocument() - assert.equal(document.getBlockCount(), 2) - - let block = document.getBlockAtIndex(0) - assert.deepEqual(block.getAttributes(), []) - assert.equal(block.toString(), "\n") - - block = document.getBlockAtIndex(1) - assert.deepEqual(block.getAttributes(), [ "quote" ]) - assert.equal(block.toString(), "a\n") - assert.locationRange({ index: 1, offset: 0 }) - - done() - }) - }) - }) - })) - - test("deleting the only non-block-break character in a block", (done) => { - typeCharacters("ab", () => { - clickToolbarButton({ attribute: "quote" }, () => { - typeCharacters("\b\b", () => { - assert.blockAttributes([ 0, 1 ], [ "quote" ]) - done() - }) - }) - }) - }) - - test("backspacing a quote", (done) => { - clickToolbarButton({ attribute: "quote" }, () => { - assert.blockAttributes([ 0, 1 ], [ "quote" ]) - pressKey("backspace", () => { - assert.blockAttributes([ 0, 1 ], []) - done() - }) - }) - }) - - test("backspacing a nested quote", (done) => { - clickToolbarButton({ attribute: "quote" }, () => { - clickToolbarButton({ action: "increaseNestingLevel" }, () => { - assert.blockAttributes([ 0, 1 ], [ "quote", "quote" ]) - pressKey("backspace", () => { - assert.blockAttributes([ 0, 1 ], [ "quote" ]) - pressKey("backspace", () => { - assert.blockAttributes([ 0, 1 ], []) - done() - }) - }) - }) - }) - }) - - test("backspacing a list item", (done) => { - clickToolbarButton({ attribute: "bullet" }, () => { - assert.blockAttributes([ 0, 1 ], [ "bulletList", "bullet" ]) - pressKey("backspace", () => { - assert.blockAttributes([ 0, 0 ], []) - done() - }) - }) - }) - - test("backspacing a nested list item", (expectDocument) => { - clickToolbarButton({ attribute: "bullet" }, () => { - typeCharacters("a\n", () => { - clickToolbarButton({ action: "increaseNestingLevel" }, () => { - assert.blockAttributes([ 2, 3 ], [ "bulletList", "bullet", "bulletList", "bullet" ]) - pressKey("backspace", () => { - assert.blockAttributes([ 2, 3 ], [ "bulletList", "bullet" ]) - expectDocument("a\n\n") - }) - }) - }) - }) - }) - - test("backspacing a list item inside a quote", (done) => { - clickToolbarButton({ attribute: "quote" }, () => { - clickToolbarButton({ attribute: "bullet" }, () => { - assert.blockAttributes([ 0, 1 ], [ "quote", "bulletList", "bullet" ]) - pressKey("backspace", () => { - assert.blockAttributes([ 0, 1 ], [ "quote" ]) - pressKey("backspace", () => { - assert.blockAttributes([ 0, 1 ], []) - done() - }) - }) - }) - }) - }) - - test("backspacing selected nested list items", (expectDocument) => { - clickToolbarButton({ attribute: "bullet" }, () => { - typeCharacters("a\n", () => { - clickToolbarButton({ action: "increaseNestingLevel" }, () => { - typeCharacters("b", () => { - getSelectionManager().setLocationRange([ - { index: 0, offset: 0 }, - { index: 1, offset: 1 }, - ]) - pressKey("backspace", () => { - assert.blockAttributes([ 0, 1 ], [ "bulletList", "bullet" ]) - expectDocument("\n") - }) - }) - }) - }) - }) - }) - - test("backspace selection spanning formatted blocks", (expectDocument) => { - clickToolbarButton({ attribute: "quote" }, () => { - typeCharacters("ab\n\n", () => { - clickToolbarButton({ attribute: "code" }, () => { - typeCharacters("cd", () => { - getSelectionManager().setLocationRange([ - { index: 0, offset: 1 }, - { index: 1, offset: 1 }, - ]) - getComposition().deleteInDirection("backward") - assert.blockAttributes([ 0, 2 ], [ "quote" ]) - expectDocument("ad\n") - }) - }) - }) - }) - }) - - test("backspace selection spanning and entire formatted block and a formatted block", (expectDocument) => { - clickToolbarButton({ attribute: "quote" }, () => { - typeCharacters("ab\n\n", () => { - clickToolbarButton({ attribute: "code" }, () => { - typeCharacters("cd", () => { - getSelectionManager().setLocationRange([ - { index: 0, offset: 0 }, - { index: 1, offset: 1 }, - ]) - getComposition().deleteInDirection("backward") - assert.blockAttributes([ 0, 2 ], [ "code" ]) - expectDocument("d\n") - }) - }) - }) - }) - }) - - test("increasing list level", (done) => { + await typeCharacters("a") + await clickToolbarButton({ attribute: "quote" }) + await moveCursor("left") + await typeCharacters("\n\n") + + const document = getDocument() + assert.equal(document.getBlockCount(), 2) + + let block = document.getBlockAtIndex(0) + assert.deepEqual(block.getAttributes(), []) + assert.equal(block.toString(), "\n") + + block = document.getBlockAtIndex(1) + assert.deepEqual(block.getAttributes(), [ "quote" ]) + assert.equal(block.toString(), "a\n") + assert.locationRange({ index: 1, offset: 0 }) + }) + + test("deleting the only non-block-break character in a block", async () => { + await typeCharacters("ab") + await clickToolbarButton({ attribute: "quote" }) + await typeCharacters("\b\b") + assert.blockAttributes([ 0, 1 ], [ "quote" ]) + }) + + test("backspacing a quote", async () => { + await nextFrame() + await clickToolbarButton({ attribute: "quote" }) + assert.blockAttributes([ 0, 1 ], [ "quote" ]) + await pressKey("backspace") + assert.blockAttributes([ 0, 1 ], []) + }) + + test("backspacing a nested quote", async () => { + await clickToolbarButton({ attribute: "quote" }) + await clickToolbarButton({ action: "increaseNestingLevel" }) + assert.blockAttributes([ 0, 1 ], [ "quote", "quote" ]) + await pressKey("backspace") + assert.blockAttributes([ 0, 1 ], [ "quote" ]) + await pressKey("backspace") + assert.blockAttributes([ 0, 1 ], []) + }) + + test("backspacing a list item", async () => { + await clickToolbarButton({ attribute: "bullet" }) + assert.blockAttributes([ 0, 1 ], [ "bulletList", "bullet" ]) + await pressKey("backspace") + assert.blockAttributes([ 0, 0 ], []) + }) + + test("backspacing a nested list item", async () => { + await clickToolbarButton({ attribute: "bullet" }) + await typeCharacters("a\n") + await clickToolbarButton({ action: "increaseNestingLevel" }) + assert.blockAttributes([ 2, 3 ], [ "bulletList", "bullet", "bulletList", "bullet" ]) + await pressKey("backspace") + assert.blockAttributes([ 2, 3 ], [ "bulletList", "bullet" ]) + expectDocument("a\n\n") + }) + + test("backspacing a list item inside a quote", async () => { + await clickToolbarButton({ attribute: "quote" }) + await clickToolbarButton({ attribute: "bullet" }) + assert.blockAttributes([ 0, 1 ], [ "quote", "bulletList", "bullet" ]) + + await pressKey("backspace") + assert.blockAttributes([ 0, 1 ], [ "quote" ]) + await pressKey("backspace") + assert.blockAttributes([ 0, 1 ], []) + }) + + test("backspacing selected nested list items", async () => { + await clickToolbarButton({ attribute: "bullet" }) + await typeCharacters("a\n") + await clickToolbarButton({ action: "increaseNestingLevel" }) + await typeCharacters("b") + getSelectionManager().setLocationRange([ + { index: 0, offset: 0 }, + { index: 1, offset: 1 }, + ]) + await pressKey("backspace") + assert.blockAttributes([ 0, 1 ], [ "bulletList", "bullet" ]) + expectDocument("\n") + }) + + test("backspace selection spanning formatted blocks", async () => { + await clickToolbarButton({ attribute: "quote" }) + await typeCharacters("ab\n\n") + await clickToolbarButton({ attribute: "code" }) + await typeCharacters("cd") + getSelectionManager().setLocationRange([ + { index: 0, offset: 1 }, + { index: 1, offset: 1 }, + ]) + getComposition().deleteInDirection("backward") + assert.blockAttributes([ 0, 2 ], [ "quote" ]) + expectDocument("ad\n") + }) + + test("backspace selection spanning and entire formatted block and a formatted block", async () => { + await clickToolbarButton({ attribute: "quote" }) + await typeCharacters("ab\n\n") + await clickToolbarButton({ attribute: "code" }) + await typeCharacters("cd") + getSelectionManager().setLocationRange([ + { index: 0, offset: 0 }, + { index: 1, offset: 1 }, + ]) + getComposition().deleteInDirection("backward") + assert.blockAttributes([ 0, 2 ], [ "code" ]) + expectDocument("d\n") + }) + + test("increasing list level", async () => { assert.ok(isToolbarButtonDisabled({ action: "increaseNestingLevel" })) assert.ok(isToolbarButtonDisabled({ action: "decreaseNestingLevel" })) - clickToolbarButton({ attribute: "bullet" }, () => { - assert.ok(isToolbarButtonDisabled({ action: "increaseNestingLevel" })) - assert.notOk(isToolbarButtonDisabled({ action: "decreaseNestingLevel" })) - typeCharacters("a\n", () => { - assert.notOk(isToolbarButtonDisabled({ action: "increaseNestingLevel" })) - assert.notOk(isToolbarButtonDisabled({ action: "decreaseNestingLevel" })) - clickToolbarButton({ action: "increaseNestingLevel" }, () => { - typeCharacters("b", () => { - assert.ok(isToolbarButtonDisabled({ action: "increaseNestingLevel" })) - assert.notOk(isToolbarButtonDisabled({ action: "decreaseNestingLevel" })) - assert.blockAttributes([ 0, 2 ], [ "bulletList", "bullet" ]) - assert.blockAttributes([ 2, 4 ], [ "bulletList", "bullet", "bulletList", "bullet" ]) - done() - }) - }) - }) - }) - }) - - test("changing list type", (done) => - clickToolbarButton({ attribute: "bullet" }, () => { - assert.blockAttributes([ 0, 1 ], [ "bulletList", "bullet" ]) - clickToolbarButton({ attribute: "number" }, () => { - assert.blockAttributes([ 0, 1 ], [ "numberList", "number" ]) - done() - }) - })) - - test("adding bullet to heading block", (done) => { - clickToolbarButton({ attribute: "heading1" }, () => { - clickToolbarButton({ attribute: "bullet" }, () => { - assert.ok(isToolbarButtonActive({ attribute: "heading1" })) - assert.blockAttributes([ 1, 2 ], []) - done() - }) - }) - }) - - test("removing bullet from heading block", (done) => { - clickToolbarButton({ attribute: "bullet" }, () => { - clickToolbarButton({ attribute: "heading1" }, () => { - assert.ok(isToolbarButtonDisabled({ attribute: "bullet" })) - done() - }) - }) - }) - - test("breaking out of heading in list", (expectDocument) => { - clickToolbarButton({ attribute: "bullet" }, () => { - clickToolbarButton({ attribute: "heading1" }, () => { - assert.ok(isToolbarButtonActive({ attribute: "heading1" })) - typeCharacters("abc", () => { - typeCharacters("\n", () => { - assert.ok(isToolbarButtonActive({ attribute: "bullet" })) - const document = getDocument() - assert.equal(document.getBlockCount(), 2) - assert.blockAttributes([ 0, 4 ], [ "bulletList", "bullet", "heading1" ]) - assert.blockAttributes([ 4, 5 ], [ "bulletList", "bullet" ]) - expectDocument("abc\n\n") - }) - }) - }) - }) - }) - - test("breaking out of middle of heading block", (expectDocument) => { - clickToolbarButton({ attribute: "heading1" }, () => { - typeCharacters("abc", () => { - assert.ok(isToolbarButtonActive({ attribute: "heading1" })) - moveCursor({ direction: "left", times: 1 }, () => { - typeCharacters("\n", () => { - const document = getDocument() - assert.equal(document.getBlockCount(), 2) - assert.blockAttributes([ 0, 3 ], [ "heading1" ]) - assert.blockAttributes([ 3, 4 ], [ "heading1" ]) - expectDocument("ab\nc\n") - }) - }) - }) - }) - }) - - test("breaking out of middle of heading block with preceding blocks", (expectDocument) => { + await clickToolbarButton({ attribute: "bullet" }) + assert.ok(isToolbarButtonDisabled({ action: "increaseNestingLevel" })) + assert.notOk(isToolbarButtonDisabled({ action: "decreaseNestingLevel" })) + await typeCharacters("a\n") + assert.notOk(isToolbarButtonDisabled({ action: "increaseNestingLevel" })) + assert.notOk(isToolbarButtonDisabled({ action: "decreaseNestingLevel" })) + await clickToolbarButton({ action: "increaseNestingLevel" }) + await typeCharacters("b") + assert.ok(isToolbarButtonDisabled({ action: "increaseNestingLevel" })) + assert.notOk(isToolbarButtonDisabled({ action: "decreaseNestingLevel" })) + assert.blockAttributes([ 0, 2 ], [ "bulletList", "bullet" ]) + assert.blockAttributes([ 2, 4 ], [ "bulletList", "bullet", "bulletList", "bullet" ]) + }) + + test("changing list type", async () => { + await clickToolbarButton({ attribute: "bullet" }) + assert.blockAttributes([ 0, 1 ], [ "bulletList", "bullet" ]) + await clickToolbarButton({ attribute: "number" }) + assert.blockAttributes([ 0, 1 ], [ "numberList", "number" ]) + }) + + test("adding bullet to heading block", async () => { + await clickToolbarButton({ attribute: "heading1" }) + await clickToolbarButton({ attribute: "bullet" }) + + assert.ok(isToolbarButtonActive({ attribute: "heading1" })) + assert.blockAttributes([ 1, 2 ], []) + }) + + test("removing bullet from heading block", async () => { + await clickToolbarButton({ attribute: "bullet" }) + await clickToolbarButton({ attribute: "heading1" }) + assert.ok(isToolbarButtonDisabled({ attribute: "bullet" })) + }) + + test("breaking out of heading in list", async () => { + await clickToolbarButton({ attribute: "bullet" }) + await clickToolbarButton({ attribute: "heading1" }) + assert.ok(isToolbarButtonActive({ attribute: "heading1" })) + await typeCharacters("abc") + await typeCharacters("\n") + + assert.ok(isToolbarButtonActive({ attribute: "bullet" })) + const document = getDocument() + assert.equal(document.getBlockCount(), 2) + assert.blockAttributes([ 0, 4 ], [ "bulletList", "bullet", "heading1" ]) + assert.blockAttributes([ 4, 5 ], [ "bulletList", "bullet" ]) + expectDocument("abc\n\n") + }) + + test("breaking out of middle of heading block", async () => { + await clickToolbarButton({ attribute: "heading1" }) + await typeCharacters("abc") + assert.ok(isToolbarButtonActive({ attribute: "heading1" })) + await moveCursor({ direction: "left", times: 1 }) + await typeCharacters("\n") + + const document = getDocument() + assert.equal(document.getBlockCount(), 2) + assert.blockAttributes([ 0, 3 ], [ "heading1" ]) + assert.blockAttributes([ 3, 4 ], [ "heading1" ]) + expectDocument("ab\nc\n") + }) + + test("breaking out of middle of heading block with preceding blocks", async () => { let document = new Document([ new Block(Text.textForStringWithAttributes("a"), [ "heading1" ]), new Block(Text.textForStringWithAttributes("b"), []), @@ -477,18 +395,17 @@ testGroup("Block formatting", { template: "editor_empty" }, () => { getEditor().setSelectedRange(5) assert.ok(isToolbarButtonActive({ attribute: "heading1" })) - typeCharacters("\n", () => { - document = getDocument() - assert.equal(document.getBlockCount(), 4) - assert.blockAttributes([ 0, 1 ], [ "heading1" ]) - assert.blockAttributes([ 2, 3 ], []) - assert.blockAttributes([ 4, 5 ], [ "heading1" ]) - assert.blockAttributes([ 6, 7 ], [ "heading1" ]) - expectDocument("a\nb\nc\nd\n") - }) + await typeCharacters("\n") + document = getDocument() + assert.equal(document.getBlockCount(), 4) + assert.blockAttributes([ 0, 1 ], [ "heading1" ]) + assert.blockAttributes([ 2, 3 ], []) + assert.blockAttributes([ 4, 5 ], [ "heading1" ]) + assert.blockAttributes([ 6, 7 ], [ "heading1" ]) + expectDocument("a\nb\nc\nd\n") }) - test("breaking out of end of heading block with preceding blocks", (expectDocument) => { + test("breaking out of end of heading block with preceding blocks", async () => { let document = new Document([ new Block(Text.textForStringWithAttributes("a"), [ "heading1" ]), new Block(Text.textForStringWithAttributes("b"), []), @@ -499,18 +416,18 @@ testGroup("Block formatting", { template: "editor_empty" }, () => { getEditor().setSelectedRange(6) assert.ok(isToolbarButtonActive({ attribute: "heading1" })) - typeCharacters("\n", () => { - document = getDocument() - assert.equal(document.getBlockCount(), 4) - assert.blockAttributes([ 0, 1 ], [ "heading1" ]) - assert.blockAttributes([ 2, 3 ], []) - assert.blockAttributes([ 4, 6 ], [ "heading1" ]) - assert.blockAttributes([ 7, 8 ], []) - expectDocument("a\nb\ncd\n\n") - }) + await typeCharacters("\n") + document = getDocument() + assert.equal(document.getBlockCount(), 4) + assert.blockAttributes([ 0, 1 ], [ "heading1" ]) + assert.blockAttributes([ 2, 3 ], []) + assert.blockAttributes([ 4, 6 ], [ "heading1" ]) + assert.blockAttributes([ 7, 8 ], []) + expectDocument("a\nb\ncd\n\n") + }) - test("inserting newline before heading", (done) => { + test("inserting newline before heading", async () => { let document = new Document([ new Block(Text.textForStringWithAttributes("\n"), []), new Block(Text.textForStringWithAttributes("abc"), [ "heading1" ]), @@ -519,23 +436,20 @@ testGroup("Block formatting", { template: "editor_empty" }, () => { replaceDocument(document) getEditor().setSelectedRange(0) - typeCharacters("\n", () => { - document = getDocument() - assert.equal(document.getBlockCount(), 2) - - let block = document.getBlockAtIndex(0) - assert.deepEqual(block.getAttributes(), []) - assert.equal(block.toString(), "\n\n\n") + await typeCharacters("\n") + document = getDocument() + assert.equal(document.getBlockCount(), 2) - block = document.getBlockAtIndex(1) - assert.deepEqual(block.getAttributes(), [ "heading1" ]) - assert.equal(block.toString(), "abc\n") + let block = document.getBlockAtIndex(0) + assert.deepEqual(block.getAttributes(), []) + assert.equal(block.toString(), "\n\n\n") - done() - }) + block = document.getBlockAtIndex(1) + assert.deepEqual(block.getAttributes(), [ "heading1" ]) + assert.equal(block.toString(), "abc\n") }) - test("inserting multiple newlines before heading", (done) => { + test("inserting multiple newlines before heading", async () => { let document = new Document([ new Block(Text.textForStringWithAttributes("\n"), []), new Block(Text.textForStringWithAttributes("abc"), [ "heading1" ]), @@ -544,22 +458,20 @@ testGroup("Block formatting", { template: "editor_empty" }, () => { replaceDocument(document) getEditor().setSelectedRange(0) - typeCharacters("\n\n", () => { - document = getDocument() - assert.equal(document.getBlockCount(), 2) + await typeCharacters("\n\n") + document = getDocument() + assert.equal(document.getBlockCount(), 2) - let block = document.getBlockAtIndex(0) - assert.deepEqual(block.getAttributes(), []) - assert.equal(block.toString(), "\n\n\n\n") + let block = document.getBlockAtIndex(0) + assert.deepEqual(block.getAttributes(), []) + assert.equal(block.toString(), "\n\n\n\n") - block = document.getBlockAtIndex(1) - assert.deepEqual(block.getAttributes(), [ "heading1" ]) - assert.equal(block.toString(), "abc\n") - done() - }) + block = document.getBlockAtIndex(1) + assert.deepEqual(block.getAttributes(), [ "heading1" ]) + assert.equal(block.toString(), "abc\n") }) - test("inserting multiple newlines before formatted block", (expectDocument) => { + test("inserting multiple newlines before formatted block", async () => { let document = new Document([ new Block(Text.textForStringWithAttributes("\n"), []), new Block(Text.textForStringWithAttributes("abc"), [ "quote" ]), @@ -568,18 +480,17 @@ testGroup("Block formatting", { template: "editor_empty" }, () => { replaceDocument(document) getEditor().setSelectedRange(1) - typeCharacters("\n\n", () => { - document = getDocument() - assert.equal(document.getBlockCount(), 2) - assert.blockAttributes([ 0, 1 ], []) - assert.blockAttributes([ 2, 3 ], []) - assert.blockAttributes([ 4, 6 ], [ "quote" ]) - assert.locationRange({ index: 0, offset: 3 }) - expectDocument("\n\n\n\nabc\n") - }) + await typeCharacters("\n\n") + document = getDocument() + assert.equal(document.getBlockCount(), 2) + assert.blockAttributes([ 0, 1 ], []) + assert.blockAttributes([ 2, 3 ], []) + assert.blockAttributes([ 4, 6 ], [ "quote" ]) + assert.locationRange({ index: 0, offset: 3 }) + expectDocument("\n\n\n\nabc\n") }) - test("inserting newline after heading with text in following block", (expectDocument) => { + test("inserting newline after heading with text in following block", async () => { let document = new Document([ new Block(Text.textForStringWithAttributes("ab"), [ "heading1" ]), new Block(Text.textForStringWithAttributes("cd"), []), @@ -588,17 +499,16 @@ testGroup("Block formatting", { template: "editor_empty" }, () => { replaceDocument(document) getEditor().setSelectedRange(2) - typeCharacters("\n", () => { - document = getDocument() - assert.equal(document.getBlockCount(), 3) - assert.blockAttributes([ 0, 2 ], [ "heading1" ]) - assert.blockAttributes([ 3, 4 ], []) - assert.blockAttributes([ 5, 6 ], []) - expectDocument("ab\n\ncd\n") - }) + await typeCharacters("\n") + document = getDocument() + assert.equal(document.getBlockCount(), 3) + assert.blockAttributes([ 0, 2 ], [ "heading1" ]) + assert.blockAttributes([ 3, 4 ], []) + assert.blockAttributes([ 5, 6 ], []) + expectDocument("ab\n\ncd\n") }) - test("backspacing a newline in an empty block with adjacent formatted blocks", (expectDocument) => { + test("backspacing a newline in an empty block with adjacent formatted blocks", async () => { let document = new Document([ new Block(Text.textForStringWithAttributes("abc"), [ "heading1" ]), new Block(), @@ -608,16 +518,15 @@ testGroup("Block formatting", { template: "editor_empty" }, () => { replaceDocument(document) getEditor().setSelectedRange(4) - pressKey("backspace", () => { - document = getDocument() - assert.equal(document.getBlockCount(), 2) - assert.blockAttributes([ 0, 1 ], [ "heading1" ]) - assert.blockAttributes([ 2, 3 ], [ "heading1" ]) - expectDocument("abc\nd\n") - }) + await pressKey("backspace") + document = getDocument() + assert.equal(document.getBlockCount(), 2) + assert.blockAttributes([ 0, 1 ], [ "heading1" ]) + assert.blockAttributes([ 2, 3 ], [ "heading1" ]) + expectDocument("abc\nd\n") }) - test("backspacing a newline at beginning of non-formatted block", (expectDocument) => { + test("backspacing a newline at beginning of non-formatted block", async () => { let document = new Document([ new Block(Text.textForStringWithAttributes("ab"), [ "heading1" ]), new Block(Text.textForStringWithAttributes("\ncd"), []), @@ -626,29 +535,25 @@ testGroup("Block formatting", { template: "editor_empty" }, () => { replaceDocument(document) getEditor().setSelectedRange(3) - pressKey("backspace", () => { - document = getDocument() - assert.equal(document.getBlockCount(), 2) - assert.blockAttributes([ 0, 2 ], [ "heading1" ]) - assert.blockAttributes([ 3, 5 ], []) - expectDocument("ab\ncd\n") - }) - }) - - test("inserting newline after single character header", (expectDocument) => { - clickToolbarButton({ attribute: "heading1" }, () => { - typeCharacters("a", () => { - typeCharacters("\n", () => { - const document = getDocument() - assert.equal(document.getBlockCount(), 2) - assert.blockAttributes([ 0, 1 ], [ "heading1" ]) - expectDocument("a\n\n") - }) - }) - }) - }) - - test("terminal attributes are only added once", (expectDocument) => { + await pressKey("backspace") + document = getDocument() + assert.equal(document.getBlockCount(), 2) + assert.blockAttributes([ 0, 2 ], [ "heading1" ]) + assert.blockAttributes([ 3, 5 ], []) + expectDocument("ab\ncd\n") + }) + + test("inserting newline after single character header", async () => { + await clickToolbarButton({ attribute: "heading1" }) + await typeCharacters("a") + await typeCharacters("\n") + const document = getDocument() + assert.equal(document.getBlockCount(), 2) + assert.blockAttributes([ 0, 1 ], [ "heading1" ]) + expectDocument("a\n\n") + }) + + test("terminal attributes are only added once", async () => { replaceDocument( new Document([ new Block(Text.textForStringWithAttributes("a"), []), @@ -657,18 +562,16 @@ testGroup("Block formatting", { template: "editor_empty" }, () => { ]) ) - selectAll(() => { - clickToolbarButton({ attribute: "heading1" }, () => { - assert.equal(getDocument().getBlockCount(), 3) - assert.blockAttributes([ 0, 1 ], [ "heading1" ]) - assert.blockAttributes([ 2, 3 ], [ "heading1" ]) - assert.blockAttributes([ 4, 5 ], [ "heading1" ]) - expectDocument("a\nb\nc\n") - }) - }) + await selectAll() + await clickToolbarButton({ attribute: "heading1" }) + assert.equal(getDocument().getBlockCount(), 3) + assert.blockAttributes([ 0, 1 ], [ "heading1" ]) + assert.blockAttributes([ 2, 3 ], [ "heading1" ]) + assert.blockAttributes([ 4, 5 ], [ "heading1" ]) + expectDocument("a\nb\nc\n") }) - test("terminal attributes replace existing terminal attributes", (expectDocument) => { + test("terminal attributes replace existing terminal attributes", async () => { replaceDocument( new Document([ new Block(Text.textForStringWithAttributes("a"), []), @@ -677,110 +580,86 @@ testGroup("Block formatting", { template: "editor_empty" }, () => { ]) ) - selectAll(() => { - clickToolbarButton({ attribute: "code" }, () => { - assert.equal(getDocument().getBlockCount(), 3) - assert.blockAttributes([ 0, 1 ], [ "code" ]) - assert.blockAttributes([ 2, 3 ], [ "code" ]) - assert.blockAttributes([ 4, 5 ], [ "code" ]) - expectDocument("a\nb\nc\n") - }) - }) - }) - - test("code blocks preserve newlines", (expectDocument) => { - typeCharacters("a\nb", () => { - selectAll(() => { - clickToolbarButton({ attribute: "code" }, () => { - assert.equal(getDocument().getBlockCount(), 1) - assert.blockAttributes([ 0, 3 ], [ "code" ]) - expectDocument("a\nb\n") - }) - }) - }) - }) - - test("code blocks are not indentable", (done) => { - clickToolbarButton({ attribute: "code" }, () => { - assert.notOk(isToolbarButtonActive({ action: "increaseNestingLevel" })) - done() - }) - }) - - test("code blocks are terminal", (done) => { - clickToolbarButton({ attribute: "code" }, () => { - assert.ok(isToolbarButtonDisabled({ attribute: "quote" })) - assert.ok(isToolbarButtonDisabled({ attribute: "heading1" })) - assert.ok(isToolbarButtonDisabled({ attribute: "bullet" })) - assert.ok(isToolbarButtonDisabled({ attribute: "number" })) - assert.notOk(isToolbarButtonDisabled({ attribute: "code" })) - assert.notOk(isToolbarButtonDisabled({ attribute: "bold" })) - assert.notOk(isToolbarButtonDisabled({ attribute: "italic" })) - done() - }) - }) - - test("unindenting a code block inside a bullet", (expectDocument) => { - clickToolbarButton({ attribute: "bullet" }, () => { - clickToolbarButton({ attribute: "code" }, () => { - typeCharacters("a", () => { - clickToolbarButton({ action: "decreaseNestingLevel" }, () => { - const document = getDocument() - assert.equal(document.getBlockCount(), 1) - assert.blockAttributes([ 0, 1 ], [ "code" ]) - expectDocument("a\n") - }) - }) - }) - }) - }) - - test("indenting a heading inside a bullet", (expectDocument) => { - clickToolbarButton({ attribute: "bullet" }, () => { - typeCharacters("a", () => { - typeCharacters("\n", () => { - clickToolbarButton({ attribute: "heading1" }, () => { - typeCharacters("b", () => { - clickToolbarButton({ action: "increaseNestingLevel" }, () => { - const document = getDocument() - assert.equal(document.getBlockCount(), 2) - assert.blockAttributes([ 0, 1 ], [ "bulletList", "bullet" ]) - assert.blockAttributes([ 2, 3 ], [ "bulletList", "bullet", "bulletList", "bullet", "heading1" ]) - expectDocument("a\nb\n") - }) - }) - }) - }) - }) - }) - }) - - test("indenting a quote inside a bullet", (expectDocument) => { - clickToolbarButton({ attribute: "bullet" }, () => { - clickToolbarButton({ attribute: "quote" }, () => { - clickToolbarButton({ action: "increaseNestingLevel" }, () => { - const document = getDocument() - assert.equal(document.getBlockCount(), 1) - assert.blockAttributes([ 0, 1 ], [ "bulletList", "bullet", "quote", "quote" ]) - expectDocument("\n") - }) - }) - }) - }) - - test("list indentation constraints consider the list type", (expectDocument) => { - clickToolbarButton({ attribute: "bullet" }, () => { - typeCharacters("a\n\n", () => { - clickToolbarButton({ attribute: "number" }, () => { - clickToolbarButton({ action: "increaseNestingLevel" }, () => { - const document = getDocument() - assert.equal(document.getBlockCount(), 2) - assert.blockAttributes([ 0, 1 ], [ "bulletList", "bullet" ]) - assert.blockAttributes([ 2, 3 ], [ "numberList", "number" ]) - expectDocument("a\n\n") - }) - }) - }) - }) + await selectAll() + await clickToolbarButton({ attribute: "code" }) + assert.equal(getDocument().getBlockCount(), 3) + assert.blockAttributes([ 0, 1 ], [ "code" ]) + assert.blockAttributes([ 2, 3 ], [ "code" ]) + assert.blockAttributes([ 4, 5 ], [ "code" ]) + expectDocument("a\nb\nc\n") + }) + + test("code blocks preserve newlines", async () => { + await typeCharacters("a\nb") + await selectAll() + clickToolbarButton({ attribute: "code" }) + assert.equal(getDocument().getBlockCount(), 1) + assert.blockAttributes([ 0, 3 ], [ "code" ]) + expectDocument("a\nb\n") + }) + + test("code blocks are not indentable", async () => { + await clickToolbarButton({ attribute: "code" }) + assert.notOk(isToolbarButtonActive({ action: "increaseNestingLevel" })) + }) + + test("code blocks are terminal", async () => { + await clickToolbarButton({ attribute: "code" }) + assert.ok(isToolbarButtonDisabled({ attribute: "quote" })) + assert.ok(isToolbarButtonDisabled({ attribute: "heading1" })) + assert.ok(isToolbarButtonDisabled({ attribute: "bullet" })) + assert.ok(isToolbarButtonDisabled({ attribute: "number" })) + assert.notOk(isToolbarButtonDisabled({ attribute: "code" })) + assert.notOk(isToolbarButtonDisabled({ attribute: "bold" })) + assert.notOk(isToolbarButtonDisabled({ attribute: "italic" })) + }) + + test("unindenting a code block inside a bullet", async () => { + await clickToolbarButton({ attribute: "bullet" }) + await clickToolbarButton({ attribute: "code" }) + await typeCharacters("a") + await clickToolbarButton({ action: "decreaseNestingLevel" }) + const document = getDocument() + assert.equal(document.getBlockCount(), 1) + assert.blockAttributes([ 0, 1 ], [ "code" ]) + expectDocument("a\n") + }) + + test("indenting a heading inside a bullet", async () => { + await clickToolbarButton({ attribute: "bullet" }) + await typeCharacters("a") + await typeCharacters("\n") + await clickToolbarButton({ attribute: "heading1" }) + await typeCharacters("b") + await clickToolbarButton({ action: "increaseNestingLevel" }) + + const document = getDocument() + assert.equal(document.getBlockCount(), 2) + assert.blockAttributes([ 0, 1 ], [ "bulletList", "bullet" ]) + assert.blockAttributes([ 2, 3 ], [ "bulletList", "bullet", "bulletList", "bullet", "heading1" ]) + expectDocument("a\nb\n") + }) + + test("indenting a quote inside a bullet", async () => { + await clickToolbarButton({ attribute: "bullet" }) + await clickToolbarButton({ attribute: "quote" }) + await clickToolbarButton({ action: "increaseNestingLevel" }) + const document = getDocument() + assert.equal(document.getBlockCount(), 1) + assert.blockAttributes([ 0, 1 ], [ "bulletList", "bullet", "quote", "quote" ]) + expectDocument("\n") + }) + + test("list indentation constraints consider the list type", async () => { + await clickToolbarButton({ attribute: "bullet" }) + await typeCharacters("a\n\n") + await clickToolbarButton({ attribute: "number" }) + await clickToolbarButton({ action: "increaseNestingLevel" }) + + const document = getDocument() + assert.equal(document.getBlockCount(), 2) + assert.blockAttributes([ 0, 1 ], [ "bulletList", "bullet" ]) + assert.blockAttributes([ 2, 3 ], [ "numberList", "number" ]) + expectDocument("a\n\n") }) }) diff --git a/src/test/system/caching_test.js b/src/test/system/caching_test.js index acdeccebd..92da9a25d 100644 --- a/src/test/system/caching_test.js +++ b/src/test/system/caching_test.js @@ -1,29 +1,24 @@ import { assert, clickToolbarButton, moveCursor, test, testGroup, typeCharacters } from "test/test_helper" testGroup("View caching", { template: "editor_empty" }, () => { - test("reparsing and rendering identical texts", (done) => { - typeCharacters("a\nb\na", () => { - moveCursor({ direction: "left", times: 2 }, () => { - clickToolbarButton({ attribute: "quote" }, () => { - const html = getEditorElement().innerHTML - getEditorController().reparse() - getEditorController().render() - assert.equal(getEditorElement().innerHTML, html) - done() - }) - }) - }) + test("reparsing and rendering identical texts", async () => { + await typeCharacters("a\nb\na") + await moveCursor({ direction: "left", times: 2 }) + await clickToolbarButton({ attribute: "quote" }) + + const html = getEditorElement().innerHTML + getEditorController().reparse() + getEditorController().render() + assert.equal(getEditorElement().innerHTML, html) }) - test("reparsing and rendering identical blocks", (done) => { - clickToolbarButton({ attribute: "bullet" }, () => { - typeCharacters("a\na", () => { - const html = getEditorElement().innerHTML - getEditorController().reparse() - getEditorController().render() - assert.equal(getEditorElement().innerHTML, html) - done() - }) - }) + test("reparsing and rendering identical blocks", async () => { + await clickToolbarButton({ attribute: "bullet" }) + await typeCharacters("a\na") + + const html = getEditorElement().innerHTML + getEditorController().reparse() + getEditorController().render() + assert.equal(getEditorElement().innerHTML, html) }) }) diff --git a/src/test/system/canceled_input_test.js b/src/test/system/canceled_input_test.js index 5598def06..c0c9f7913 100644 --- a/src/test/system/canceled_input_test.js +++ b/src/test/system/canceled_input_test.js @@ -1,4 +1,4 @@ -import { pressKey, test, testGroup, typeCharacters } from "test/test_helper" +import { expectDocument, pressKey, test, testGroup, typeCharacters } from "test/test_helper" const testOptions = { template: "editor_empty", @@ -37,27 +37,24 @@ const cancel = (event) => { } testGroup("Canceled input", testOptions, () => { - test("ignoring canceled input events in capturing phase", (expectDocument) => { - typeCharacters("a", () => { - cancelingInCapturingPhase = true - pressKey("backspace", () => { - pressKey("return", () => { - cancelingInCapturingPhase = false - typeCharacters("b", () => expectDocument("ab\n")) - }) - }) - }) + test("ignoring canceled input events in capturing phase", async () => { + await typeCharacters("a") + cancelingInCapturingPhase = true + await pressKey("backspace") + await pressKey("return") + cancelingInCapturingPhase = false + await typeCharacters("b") + + expectDocument("ab\n") }) - test("ignoring canceled input events at target", (expectDocument) => { - typeCharacters("a", () => { - cancelingAtTarget = true - pressKey("backspace", () => { - pressKey("return", () => { - cancelingAtTarget = false - typeCharacters("b", () => expectDocument("ab\n")) - }) - }) - }) + test("ignoring canceled input events at target", async () => { + await typeCharacters("a") + cancelingAtTarget = true + await pressKey("backspace") + await pressKey("return") + cancelingAtTarget = false + await typeCharacters("b") + expectDocument("ab\n") }) }) diff --git a/src/test/system/composition_input_test.js b/src/test/system/composition_input_test.js index f1f4682cc..74be60e87 100644 --- a/src/test/system/composition_input_test.js +++ b/src/test/system/composition_input_test.js @@ -3,8 +3,8 @@ import * as config from "trix/config" import { assert, clickToolbarButton, - defer, endComposition, + expectDocument, insertNode, pressKey, selectNode, @@ -17,99 +17,104 @@ import { typeCharacters, updateComposition, } from "test/test_helper" +import { nextFrame } from "../test_helpers/timing_helpers" testGroup("Composition input", { template: "editor_empty" }, () => { - test("composing", (expectDocument) => - startComposition("a", () => updateComposition("ab", () => endComposition("abc", () => expectDocument("abc\n"))))) - - test("typing and composing", (expectDocument) => - typeCharacters("a", () => - startComposition("b", () => - updateComposition("bc", () => endComposition("bcd", () => typeCharacters("e", () => expectDocument("abcde\n")))) - ) - )) - - test("composition input is serialized", (expectDocument) => { - startComposition("´", () => { - endComposition("é", () => { - assert.equal(getEditorElement().value, "
é
") - expectDocument("é\n") - }) - }) + test("composing", async () => { + await startComposition("a") + await updateComposition("ab") + await endComposition("abc") + + expectDocument("abc\n") }) - test("pressing after a canceled composition", (expectDocument) => { - typeCharacters("ab", () => { - triggerEvent(document.activeElement, "compositionend", { data: "ab" }) - pressKey("return", () => expectDocument("ab\n\n")) - }) + test("typing and composing", async () => { + await typeCharacters("a") + await startComposition("b") + await updateComposition("bc") + await endComposition("bcd") + await typeCharacters("e") + + expectDocument("abcde\n") }) - test("composing formatted text", (expectDocument) => { - typeCharacters("abc", () => { - clickToolbarButton({ attribute: "bold" }, () => { - startComposition("d", () => { - updateComposition("de", () => { - endComposition("def", () => { - assert.textAttributes([ 0, 3 ], {}) - assert.textAttributes([ 3, 6 ], { bold: true }) - expectDocument("abcdef\n") - }) - }) - }) - }) - }) + test("composition input is serialized", async () => { + await startComposition("´") + await endComposition("é") + + assert.equal(getEditorElement().value, "
é
") + expectDocument("é\n") }) - test("composing away from formatted text", (expectDocument) => { - clickToolbarButton({ attribute: "bold" }, () => { - typeCharacters("abc", () => { - clickToolbarButton({ attribute: "bold" }, () => { - startComposition("d", () => { - updateComposition("de", () => { - endComposition("def", () => { - assert.textAttributes([ 0, 3 ], { bold: true }) - assert.textAttributes([ 3, 6 ], {}) - expectDocument("abcdef\n") - }) - }) - }) - }) - }) - }) + test("pressing after a canceled composition", async () => { + await typeCharacters("ab") + triggerEvent(document.activeElement, "compositionend", { data: "ab" }) + await pressKey("return") + + expectDocument("ab\n\n") }) - test("composing another language using a QWERTY keyboard", (expectDocument) => { + test("composing formatted text", async () => { + await typeCharacters("abc") + await clickToolbarButton({ attribute: "bold" }) + await startComposition("d") + await updateComposition("de") + await endComposition("def") + + assert.textAttributes([ 0, 3 ], {}) + assert.textAttributes([ 3, 6 ], { bold: true }) + expectDocument("abcdef\n") + }) + + test("composing away from formatted text", async () => { + await clickToolbarButton({ attribute: "bold" }) + await typeCharacters("abc") + await clickToolbarButton({ attribute: "bold" }) + await startComposition("d") + await updateComposition("de") + await endComposition("def") + + assert.textAttributes([ 0, 3 ], { bold: true }) + assert.textAttributes([ 3, 6 ], {}) + expectDocument("abcdef\n") + }) + + test("composing another language using a QWERTY keyboard", async () => { const element = getEditorElement() const keyCodes = { x: 120, i: 105 } triggerEvent(element, "keypress", { charCode: keyCodes.x, keyCode: keyCodes.x, which: keyCodes.x }) - startComposition("x", () => { - triggerEvent(element, "keypress", { charCode: keyCodes.i, keyCode: keyCodes.i, which: keyCodes.i }) - updateComposition("xi", () => endComposition("喜", () => expectDocument("喜\n"))) - }) + await startComposition("x") + triggerEvent(element, "keypress", { charCode: keyCodes.i, keyCode: keyCodes.i, which: keyCodes.i }) + await updateComposition("xi") + await endComposition("喜") + + expectDocument("喜\n") }) // Simulates the sequence of events when pressing backspace through a word on Android - testIf(config.input.getLevel() === 0, "backspacing through a composition", (expectDocument) => { + testIf(config.input.getLevel() === 0, "backspacing through a composition", async () => { const element = getEditorElement() element.editor.insertString("a cat") triggerEvent(element, "keydown", { charCode: 0, keyCode: 229, which: 229 }) triggerEvent(element, "compositionupdate", { data: "ca" }) triggerEvent(element, "input") - removeCharacters(-1, () => { - triggerEvent(element, "keydown", { charCode: 0, keyCode: 229, which: 229 }) - triggerEvent(element, "compositionupdate", { data: "c" }) - triggerEvent(element, "input") - triggerEvent(element, "compositionend", { data: "c" }) - removeCharacters(-1, () => pressKey("backspace", () => expectDocument("a \n"))) - }) + await removeCharacters(-1) + + triggerEvent(element, "keydown", { charCode: 0, keyCode: 229, which: 229 }) + triggerEvent(element, "compositionupdate", { data: "c" }) + triggerEvent(element, "input") + triggerEvent(element, "compositionend", { data: "c" }) + await removeCharacters(-1) + await pressKey("backspace") + + expectDocument("a \n") }) // Simulates the sequence of events when pressing backspace at the end of a // word and updating it on Android (running older versions of System WebView) - testIf(config.input.getLevel() === 0, "updating a composition", (expectDocument) => { + testIf(config.input.getLevel() === 0, "updating a composition", async () => { const element = getEditorElement() element.editor.insertString("cat") @@ -117,17 +122,19 @@ testGroup("Composition input", { template: "editor_empty" }, () => { triggerEvent(element, "compositionstart", { data: "cat" }) triggerEvent(element, "compositionupdate", { data: "cat" }) triggerEvent(element, "input") - removeCharacters(-1, () => { - triggerEvent(element, "keydown", { charCode: 0, keyCode: 229, which: 229 }) - triggerEvent(element, "compositionupdate", { data: "car" }) - triggerEvent(element, "input") - triggerEvent(element, "compositionend", { data: "car" }) - insertNode(document.createTextNode("r"), () => expectDocument("car\n")) - }) + await removeCharacters(-1) + + triggerEvent(element, "keydown", { charCode: 0, keyCode: 229, which: 229 }) + triggerEvent(element, "compositionupdate", { data: "car" }) + triggerEvent(element, "input") + triggerEvent(element, "compositionend", { data: "car" }) + await insertNode(document.createTextNode("r")) + + expectDocument("car\n") }) // Simulates the sequence of events when typing on Android and then tapping elsewhere - testIf(config.input.getLevel() === 0, "leaving a composition", (expectDocument) => { + testIf(config.input.getLevel() === 0, "leaving a composition", async () => { const element = getEditorElement() triggerEvent(element, "keydown", { charCode: 0, keyCode: 229, which: 229 }) @@ -138,44 +145,50 @@ testGroup("Composition input", { template: "editor_empty" }, () => { const node = document.createTextNode("c") insertNode(node) selectNode(node) - defer(() => { - triggerEvent(element, "keydown", { charCode: 0, keyCode: 229, which: 229 }) - triggerInputEvent(element, "beforeinput", { inputType: "insertCompositionText", data: "ca" }) - triggerEvent(element, "compositionupdate", { data: "ca" }) - triggerEvent(element, "input") - node.data = "ca" - defer(() => { - triggerEvent(element, "compositionend", { data: "" }) - defer(() => expectDocument("ca\n")) - }) - }) + + await nextFrame() + + triggerEvent(element, "keydown", { charCode: 0, keyCode: 229, which: 229 }) + triggerInputEvent(element, "beforeinput", { inputType: "insertCompositionText", data: "ca" }) + triggerEvent(element, "compositionupdate", { data: "ca" }) + triggerEvent(element, "input") + node.data = "ca" + + await nextFrame() + triggerEvent(element, "compositionend", { data: "" }) + + await nextFrame() + expectDocument("ca\n") }) - testIf(config.browser.composesExistingText, "composition events from cursor movement are ignored", (expectDocument) => { + testIf(config.browser.composesExistingText, "composition events from cursor movement are ignored", async () => { const element = getEditorElement() element.editor.insertString("ab ") element.editor.setSelectedRange(0) triggerEvent(element, "compositionstart", { data: "" }) triggerEvent(element, "compositionupdate", { data: "ab" }) - defer(() => { - element.editor.setSelectedRange(1) - triggerEvent(element, "compositionupdate", { data: "ab" }) - defer(() => { - element.editor.setSelectedRange(2) - triggerEvent(element, "compositionupdate", { data: "ab" }) - defer(() => { - element.editor.setSelectedRange(3) - triggerEvent(element, "compositionend", { data: "ab" }) - defer(() => expectDocument("ab \n")) - }) - }) - }) + + await nextFrame() + element.editor.setSelectedRange(1) + triggerEvent(element, "compositionupdate", { data: "ab" }) + + await nextFrame() + + element.editor.setSelectedRange(2) + triggerEvent(element, "compositionupdate", { data: "ab" }) + + await nextFrame() + element.editor.setSelectedRange(3) + triggerEvent(element, "compositionend", { data: "ab" }) + + await nextFrame() + expectDocument("ab \n") }) // Simulates compositions in Firefox where the final composition data is // dispatched as both compositionupdate and compositionend. - testIf(config.input.getLevel() === 0, "composition ending with same data as last update", (expectDocument) => { + testIf(config.input.getLevel() === 0, "composition ending with same data as last update", async () => { const element = getEditorElement() triggerEvent(element, "keydown", { charCode: 0, keyCode: 229, which: 229 }) @@ -184,32 +197,34 @@ testGroup("Composition input", { template: "editor_empty" }, () => { const node = document.createTextNode("´") insertNode(node) selectNode(node) - defer(() => { - triggerEvent(element, "keydown", { charCode: 0, keyCode: 229, which: 229 }) - triggerEvent(element, "compositionupdate", { data: "é" }) - triggerEvent(element, "input") - node.data = "é" - defer(() => { - triggerEvent(element, "keydown", { charCode: 0, keyCode: 229, which: 229 }) - triggerEvent(element, "compositionupdate", { data: "éé" }) - triggerEvent(element, "input") - node.data = "éé" - defer(() => { - triggerEvent(element, "compositionend", { data: "éé" }) - defer(() => { - assert.locationRange({ index: 0, offset: 2 }) - expectDocument("éé\n") - }) - }) - }) - }) + + await nextFrame() + + triggerEvent(element, "keydown", { charCode: 0, keyCode: 229, which: 229 }) + triggerEvent(element, "compositionupdate", { data: "é" }) + triggerEvent(element, "input") + node.data = "é" + + await nextFrame() + + triggerEvent(element, "keydown", { charCode: 0, keyCode: 229, which: 229 }) + triggerEvent(element, "compositionupdate", { data: "éé" }) + triggerEvent(element, "input") + node.data = "éé" + + await nextFrame() + triggerEvent(element, "compositionend", { data: "éé" }) + + await nextFrame() + assert.locationRange({ index: 0, offset: 2 }) + expectDocument("éé\n") }) }) -const removeCharacters = function (direction, callback) { +const removeCharacters = async (direction) => { const selection = rangy.getSelection() const range = selection.getRangeAt(0) range.moveStart("character", direction) range.deleteContents() - defer(callback) + await nextFrame() } diff --git a/src/test/system/cursor_movement_test.js b/src/test/system/cursor_movement_test.js index 860271866..eef0abe6a 100644 --- a/src/test/system/cursor_movement_test.js +++ b/src/test/system/cursor_movement_test.js @@ -10,73 +10,69 @@ import { } from "test/test_helper" testGroup("Cursor movement", { template: "editor_empty" }, () => { - test("move cursor around attachment", (done) => { + test("move cursor around attachment", async () => { insertFile(createFile()) assert.locationRange({ index: 0, offset: 1 }) - moveCursor("left", () => { - assert.locationRange({ index: 0, offset: 0 }, { index: 0, offset: 1 }) - moveCursor("left", () => { - assert.locationRange({ index: 0, offset: 0 }) - moveCursor("right", () => { - assert.locationRange({ index: 0, offset: 0 }, { index: 0, offset: 1 }) - moveCursor("right", () => { - assert.locationRange({ index: 0, offset: 1 }) - done() - }) - }) - }) - }) + + await moveCursor("left") + assert.locationRange({ index: 0, offset: 0 }, { index: 0, offset: 1 }) + + await moveCursor("left") + assert.locationRange({ index: 0, offset: 0 }) + + await moveCursor("right") + assert.locationRange({ index: 0, offset: 0 }, { index: 0, offset: 1 }) + + await moveCursor("right") + assert.locationRange({ index: 0, offset: 1 }) }) - test("move cursor around attachment and text", (done) => { + test("move cursor around attachment and text", async () => { insertString("a") insertFile(createFile()) insertString("b") assert.locationRange({ index: 0, offset: 3 }) - moveCursor("left", () => { - assert.locationRange({ index: 0, offset: 2 }) - moveCursor("left", () => { - assert.locationRange({ index: 0, offset: 1 }, { index: 0, offset: 2 }) - moveCursor("left", () => { - assert.locationRange({ index: 0, offset: 1 }) - moveCursor("left", () => { - assert.locationRange({ index: 0, offset: 0 }) - done() - }) - }) - }) - }) + + await moveCursor("left") + assert.locationRange({ index: 0, offset: 2 }) + + await moveCursor("left") + assert.locationRange({ index: 0, offset: 1 }, { index: 0, offset: 2 }) + + await moveCursor("left") + assert.locationRange({ index: 0, offset: 1 }) + + await moveCursor("left") + assert.locationRange({ index: 0, offset: 0 }) }) - test("expand selection over attachment", (done) => { + test("expand selection over attachment", async () => { insertFile(createFile()) assert.locationRange({ index: 0, offset: 1 }) - expandSelection("left", () => { - assert.locationRange({ index: 0, offset: 0 }, { index: 0, offset: 1 }) - moveCursor("left", () => { - assert.locationRange({ index: 0, offset: 0 }) - expandSelection("right", () => { - assert.locationRange({ index: 0, offset: 0 }, { index: 0, offset: 1 }) - done() - }) - }) - }) + + await expandSelection("left") + assert.locationRange({ index: 0, offset: 0 }, { index: 0, offset: 1 }) + + await moveCursor("left") + assert.locationRange({ index: 0, offset: 0 }) + + await expandSelection("right") + assert.locationRange({ index: 0, offset: 0 }, { index: 0, offset: 1 }) }) - test("expand selection over attachment and text", (done) => { + test("expand selection over attachment and text", async () => { insertString("a") insertFile(createFile()) insertString("b") assert.locationRange({ index: 0, offset: 3 }) - expandSelection("left", () => { - assert.locationRange({ index: 0, offset: 2 }, { index: 0, offset: 3 }) - expandSelection("left", () => { - assert.locationRange({ index: 0, offset: 1 }, { index: 0, offset: 3 }) - expandSelection("left", () => { - assert.locationRange({ index: 0, offset: 0 }, { index: 0, offset: 3 }) - done() - }) - }) - }) + + await expandSelection("left") + assert.locationRange({ index: 0, offset: 2 }, { index: 0, offset: 3 }) + + await expandSelection("left") + assert.locationRange({ index: 0, offset: 1 }, { index: 0, offset: 3 }) + + await expandSelection("left") + assert.locationRange({ index: 0, offset: 0 }, { index: 0, offset: 3 }) }) }) diff --git a/src/test/system/custom_element_test.js b/src/test/system/custom_element_test.js index 490d91e6d..2e77e3fc4 100644 --- a/src/test/system/custom_element_test.js +++ b/src/test/system/custom_element_test.js @@ -2,12 +2,11 @@ import { rangesAreEqual } from "trix/core/helpers" import { TEST_IMAGE_URL, - after, assert, clickElement, clickToolbarButton, createFile, - defer, + expectDocument, insertImageAttachment, moveCursor, pasteContent, @@ -18,9 +17,10 @@ import { typeCharacters, typeInToolbarDialog, } from "test/test_helper" +import { delay, nextFrame } from "../test_helpers/timing_helpers" testGroup("Custom element API", { template: "editor_empty" }, () => { - test("element triggers trix-initialize on first connect", (done) => { + test("element triggers trix-initialize on first connect", async () => { const container = document.getElementById("trix-container") container.innerHTML = "" @@ -29,16 +29,15 @@ testGroup("Custom element API", { template: "editor_empty" }, () => { element.addEventListener("trix-initialize", () => initializeEventCount++) container.appendChild(element) - requestAnimationFrame(() => { - container.removeChild(element) - requestAnimationFrame(() => { - container.appendChild(element) - after(60, () => { - assert.equal(initializeEventCount, 1) - done() - }) - }) - }) + await nextFrame() + + container.removeChild(element) + await nextFrame() + + container.appendChild(element) + await delay(60) + + assert.equal(initializeEventCount, 1) }) test("files are accepted by default", () => { @@ -116,138 +115,132 @@ testGroup("Custom element API", { template: "editor_empty" }, () => { assert.equal(eventCount, 1) }) - test("element triggers trix-change events when the document changes", (done) => { + test("element triggers trix-change events when the document changes", async () => { const element = getEditorElement() let eventCount = 0 element.addEventListener("trix-change", (event) => eventCount++) - typeCharacters("a", () => { - assert.equal(eventCount, 1) - moveCursor("left", () => { - assert.equal(eventCount, 1) - typeCharacters("bcd", () => { - assert.equal(eventCount, 4) - clickToolbarButton({ action: "undo" }, () => { - assert.equal(eventCount, 5) - done() - }) - }) - }) - }) + await typeCharacters("a") + assert.equal(eventCount, 1) + + await moveCursor("left") + assert.equal(eventCount, 1) + + await typeCharacters("bcd") + assert.equal(eventCount, 4) + + await clickToolbarButton({ action: "undo" }) + assert.equal(eventCount, 5) }) - test("element triggers trix-change event after toggling attributes", (done) => { + test("element triggers trix-change event after toggling attributes", async () => { const element = getEditorElement() const { editor } = element - const afterChangeEvent = function (edit, callback) { - let handler - element.addEventListener( - "trix-change", - handler = function (event) { - element.removeEventListener("trix-change", handler) - callback(event) - } - ) - edit() + const afterChangeEvent = (edit) => { + return new Promise((resolve) => { + let handler + element.addEventListener( + "trix-change", + handler = function (event) { + element.removeEventListener("trix-change", handler) + resolve(event) + } + ) + edit() + }) } - typeCharacters("hello", () => { - let edit = () => editor.activateAttribute("quote") - afterChangeEvent(edit, () => { - assert.ok(editor.attributeIsActive("quote")) - - edit = () => editor.deactivateAttribute("quote") - afterChangeEvent(edit, () => { - assert.notOk(editor.attributeIsActive("quote")) - - editor.setSelectedRange([ 0, 5 ]) - edit = () => editor.activateAttribute("bold") - afterChangeEvent(edit, () => { - assert.ok(editor.attributeIsActive("bold")) - - edit = () => editor.deactivateAttribute("bold") - afterChangeEvent(edit, () => { - assert.notOk(editor.attributeIsActive("bold")) - done() - }) - }) - }) - }) - }) + await typeCharacters("hello") + + let edit = () => editor.activateAttribute("quote") + await afterChangeEvent(edit) + assert.ok(editor.attributeIsActive("quote")) + + edit = () => editor.deactivateAttribute("quote") + await afterChangeEvent(edit) + assert.notOk(editor.attributeIsActive("quote")) + + editor.setSelectedRange([ 0, 5 ]) + + edit = () => editor.activateAttribute("bold") + await afterChangeEvent(edit) + assert.ok(editor.attributeIsActive("bold")) + + edit = () => editor.deactivateAttribute("bold") + await afterChangeEvent(edit) + assert.notOk(editor.attributeIsActive("bold")) }) - test("disabled attributes aren't considered active", (done) => { + test("disabled attributes aren't considered active", async () => { const { editor } = getEditorElement() editor.activateAttribute("heading1") assert.notOk(editor.attributeIsActive("code")) assert.notOk(editor.attributeIsActive("quote")) - done() }) - test("element triggers trix-selection-change events when the location range changes", (done) => { + test("element triggers trix-selection-change events when the location range changes", async () => { const element = getEditorElement() let eventCount = 0 + element.addEventListener("trix-selection-change", (event) => eventCount++) + await nextFrame() - typeCharacters("a", () => { - assert.equal(eventCount, 1) - moveCursor("left", () => { - assert.equal(eventCount, 2) - done() - }) - }) + await typeCharacters("a") + assert.equal(eventCount, 1) + + await moveCursor("left") + assert.equal(eventCount, 2) }) - test("only triggers trix-selection-change events on the active element", (done) => { + test("only triggers trix-selection-change events on the active element", () => { const elementA = getEditorElement() const elementB = document.createElement("trix-editor") elementA.parentNode.insertBefore(elementB, elementA.nextSibling) - elementB.addEventListener("trix-initialize", () => { - elementA.editor.insertString("a") - elementB.editor.insertString("b") - rangy.getSelection().removeAllRanges() - - let eventCountA = 0 - let eventCountB = 0 - elementA.addEventListener("trix-selection-change", (event) => eventCountA++) - elementB.addEventListener("trix-selection-change", (event) => eventCountB++) - - elementA.editor.setSelectedRange(0) - assert.equal(eventCountA, 1) - assert.equal(eventCountB, 0) - - elementB.editor.setSelectedRange(0) - assert.equal(eventCountA, 1) - assert.equal(eventCountB, 1) - - elementA.editor.setSelectedRange(1) - assert.equal(eventCountA, 2) - assert.equal(eventCountB, 1) - done() + return new Promise((resolve) => { + elementB.addEventListener("trix-initialize", () => { + elementA.editor.insertString("a") + elementB.editor.insertString("b") + rangy.getSelection().removeAllRanges() + + let eventCountA = 0 + let eventCountB = 0 + elementA.addEventListener("trix-selection-change", (event) => eventCountA++) + elementB.addEventListener("trix-selection-change", (event) => eventCountB++) + + elementA.editor.setSelectedRange(0) + assert.equal(eventCountA, 1) + assert.equal(eventCountB, 0) + + elementB.editor.setSelectedRange(0) + assert.equal(eventCountA, 1) + assert.equal(eventCountB, 1) + + elementA.editor.setSelectedRange(1) + assert.equal(eventCountA, 2) + assert.equal(eventCountB, 1) + resolve() + }) }) }) - test("element triggers toolbar dialog events", (done) => { + test("element triggers toolbar dialog events", async () => { const element = getEditorElement() const events = [] element.addEventListener("trix-toolbar-dialog-show", (event) => events.push(event.type)) - element.addEventListener("trix-toolbar-dialog-hide", (event) => events.push(event.type)) + await nextFrame() - clickToolbarButton({ action: "link" }, () => { - typeInToolbarDialog("http://example.com", { attribute: "href" }, () => { - defer(() => { - assert.deepEqual(events, [ "trix-toolbar-dialog-show", "trix-toolbar-dialog-hide" ]) - done() - }) - }) - }) + await clickToolbarButton({ action: "link" }) + await typeInToolbarDialog("http://example.com", { attribute: "href" }) + await nextFrame() + + assert.deepEqual(events, [ "trix-toolbar-dialog-show", "trix-toolbar-dialog-hide" ]) }) - test("element triggers before-paste event with paste data", (expectDocument) => { + test("element triggers before-paste event with paste data", async () => { const element = getEditorElement() let eventCount = 0 let paste = null @@ -257,17 +250,17 @@ testGroup("Custom element API", { template: "editor_empty" }, () => { paste = event.paste }) - typeCharacters("", () => { - pasteContent("text/html", "hello", () => { - assert.equal(eventCount, 1) - assert.equal(paste.type, "text/html") - assert.equal(paste.html, "hello") - expectDocument("hello\n") - }) - }) + await typeCharacters("") + await pasteContent("text/html", "hello") + + assert.equal(eventCount, 1) + assert.equal(paste.type, "text/html") + assert.equal(paste.html, "hello") + + expectDocument("hello\n") }) - test("element triggers before-paste event with mutable paste data", (expectDocument) => { + test("element triggers before-paste event with mutable paste data", async () => { const element = getEditorElement() let eventCount = 0 let paste = null @@ -278,16 +271,15 @@ testGroup("Custom element API", { template: "editor_empty" }, () => { paste.html = "greetings" }) - typeCharacters("", () => { - pasteContent("text/html", "hello", () => { - assert.equal(eventCount, 1) - assert.equal(paste.type, "text/html") - expectDocument("greetings\n") - }) - }) + await typeCharacters("") + await pasteContent("text/html", "hello") + + assert.equal(eventCount, 1) + assert.equal(paste.type, "text/html") + expectDocument("greetings\n") }) - test("element triggers paste event with position range", (done) => { + test("element triggers paste event with position range", async () => { const element = getEditorElement() let eventCount = 0 let paste = null @@ -297,17 +289,15 @@ testGroup("Custom element API", { template: "editor_empty" }, () => { paste = event.paste }) - typeCharacters("", () => { - pasteContent("text/html", "hello", () => { - assert.equal(eventCount, 1) - assert.equal(paste.type, "text/html") - assert.ok(rangesAreEqual([ 0, 5 ], paste.range)) - done() - }) - }) + await typeCharacters("") + await pasteContent("text/html", "hello") + + assert.equal(eventCount, 1) + assert.equal(paste.type, "text/html") + assert.ok(rangesAreEqual([ 0, 5 ], paste.range)) }) - test("element triggers attribute change events", (done) => { + test("element triggers attribute change events", async () => { const element = getEditorElement() let eventCount = 0 let attributes = null @@ -317,17 +307,16 @@ testGroup("Custom element API", { template: "editor_empty" }, () => { attributes = event.attributes }) - typeCharacters("", () => { - assert.equal(eventCount, 0) - clickToolbarButton({ attribute: "bold" }, () => { - assert.equal(eventCount, 1) - assert.deepEqual({ bold: true }, attributes) - done() - }) - }) + await typeCharacters("") + assert.equal(eventCount, 0) + + await clickToolbarButton({ attribute: "bold" }) + + assert.equal(eventCount, 1) + assert.deepEqual({ bold: true }, attributes) }) - test("element triggers action change events", (done) => { + test("element triggers action change events", async () => { const element = getEditorElement() let eventCount = 0 let actions = null @@ -337,18 +326,17 @@ testGroup("Custom element API", { template: "editor_empty" }, () => { actions = event.actions }) - typeCharacters("", () => { - assert.equal(eventCount, 0) - clickToolbarButton({ attribute: "bullet" }, () => { - assert.equal(eventCount, 1) - assert.equal(actions.decreaseNestingLevel, true) - assert.equal(actions.increaseNestingLevel, false) - done() - }) - }) + await typeCharacters("") + assert.equal(eventCount, 0) + + await clickToolbarButton({ attribute: "bullet" }) + + assert.equal(eventCount, 1) + assert.equal(actions.decreaseNestingLevel, true) + assert.equal(actions.increaseNestingLevel, false) }) - test("element triggers custom focus and blur events", (done) => { + test("element triggers custom focus and blur events", async () => { const element = getEditorElement() let focusEventCount = 0 @@ -357,35 +345,34 @@ testGroup("Custom element API", { template: "editor_empty" }, () => { element.addEventListener("trix-blur", () => blurEventCount++) triggerEvent(element, "blur") - defer(() => { - assert.equal(blurEventCount, 1) - assert.equal(focusEventCount, 0) + await delay(10) - triggerEvent(element, "focus") - defer(() => { - assert.equal(blurEventCount, 1) - assert.equal(focusEventCount, 1) + assert.equal(blurEventCount, 1) + assert.equal(focusEventCount, 0) - insertImageAttachment() - after(20, () => { - clickElement(element.querySelector("figure"), () => { - const textarea = element.querySelector("textarea") - textarea.focus() - defer(() => { - assert.equal(document.activeElement, textarea) - assert.equal(blurEventCount, 1) - assert.equal(focusEventCount, 1) - done() - }) - }) - }) - }) - }) + triggerEvent(element, "focus") + await delay(10) + + assert.equal(blurEventCount, 1) + assert.equal(focusEventCount, 1) + + insertImageAttachment() + await delay(20) + + await clickElement(element.querySelector("figure")) + + const textarea = element.querySelector("textarea") + textarea.focus() + await nextFrame() + + assert.equal(document.activeElement, textarea) + assert.equal(blurEventCount, 1) + assert.equal(focusEventCount, 1) }) // Selenium doesn't seem to focus windows properly in some browsers (FF 47 on OS X) // so skip this test when unfocused pending a better solution. - testIf(document.hasFocus(), "element triggers custom focus event when autofocusing", (done) => { + testIf(document.hasFocus(), "element triggers custom focus event when autofocusing", () => { const element = document.createElement("trix-editor") element.setAttribute("autofocus", "") @@ -396,39 +383,39 @@ testGroup("Custom element API", { template: "editor_empty" }, () => { container.innerHTML = "" container.appendChild(element) - element.addEventListener("trix-initialize", () => { - assert.equal(focusEventCount, 1) - done() + return new Promise((resolve) => { + element.addEventListener("trix-initialize", () => { + assert.equal(focusEventCount, 1) + resolve() + }) }) }) - test("element serializes HTML after attribute changes", (done) => { + test("element serializes HTML after attribute changes", async () => { const element = getEditorElement() let serializedHTML = element.value - typeCharacters("a", () => { - assert.notEqual(serializedHTML, element.value) - serializedHTML = element.value + await typeCharacters("a") + assert.notEqual(serializedHTML, element.value) + serializedHTML = element.value - clickToolbarButton({ attribute: "quote" }, () => { - assert.notEqual(serializedHTML, element.value) - serializedHTML = element.value + await clickToolbarButton({ attribute: "quote" }) + assert.notEqual(serializedHTML, element.value) + serializedHTML = element.value - clickToolbarButton({ attribute: "quote" }, () => { - assert.notEqual(serializedHTML, element.value) - done() - }) - }) - }) + await clickToolbarButton({ attribute: "quote" }) + assert.notEqual(serializedHTML, element.value) }) - test("element serializes HTML after attachment attribute changes", (done) => { + test("element serializes HTML after attachment attribute changes", async () => { const element = getEditorElement() const attributes = { url: "test_helpers/fixtures/logo.png", contentType: "image/png" } - element.addEventListener("trix-attachment-add", function (event) { - const { attachment } = event - requestAnimationFrame(() => { + const promise = new Promise((resolve) => { + element.addEventListener("trix-attachment-add", async (event) => { + const { attachment } = event + await nextFrame() + let serializedHTML = element.value attachment.setAttributes(attributes) assert.notEqual(serializedHTML, element.value) @@ -441,94 +428,90 @@ testGroup("Custom element API", { template: "editor_empty" }, () => { ) attachment.remove() - requestAnimationFrame(() => done()) + await nextFrame() + resolve() }) }) - requestAnimationFrame(() => insertImageAttachment()) + + await nextFrame() + insertImageAttachment() + + return promise }) - test("editor resets to its original value on form reset", (expectDocument) => { + test("editor resets to its original value on form reset", async () => { const element = getEditorElement() const { form } = element.inputElement - typeCharacters("hello", () => { - form.reset() - expectDocument("\n") - }) + await typeCharacters("hello") + form.reset() + expectDocument("\n") }) - test("editor resets to last-set value on form reset", (expectDocument) => { + test("editor resets to last-set value on form reset", async () => { const element = getEditorElement() const { form } = element.inputElement element.value = "hi" - typeCharacters("hello", () => { - form.reset() - expectDocument("hi\n") - }) + await typeCharacters("hello") + form.reset() + expectDocument("hi\n") }) - test("editor respects preventDefault on form reset", (expectDocument) => { + test("editor respects preventDefault on form reset", async () => { const element = getEditorElement() const { form } = element.inputElement const preventDefault = (event) => event.preventDefault() - typeCharacters("hello", () => { - form.addEventListener("reset", preventDefault, false) - form.reset() - form.removeEventListener("reset", preventDefault, false) - expectDocument("hello\n") - }) + await typeCharacters("hello") + + form.addEventListener("reset", preventDefault, false) + form.reset() + form.removeEventListener("reset", preventDefault, false) + expectDocument("hello\n") }) }) testGroup("