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, "