|
|
@@ -0,0 +1,181 @@
|
|
|
+import { expect } from '@playwright/test'
|
|
|
+import { test } from './fixtures'
|
|
|
+import {
|
|
|
+ createRandomPage, randomInt, randomInsert, randomEditDelete, randomEditMoveUpDown, IsMac, randomString,
|
|
|
+} from './utils'
|
|
|
+
|
|
|
+/**
|
|
|
+ * Randomized test for single page editing. Block-wise.
|
|
|
+ *
|
|
|
+ * For now, only check total number of blocks.
|
|
|
+ */
|
|
|
+
|
|
|
+interface RandomTestStep {
|
|
|
+ /// target block
|
|
|
+ target: number;
|
|
|
+ /// action
|
|
|
+ op: string;
|
|
|
+ text: string;
|
|
|
+ /// expected total block number
|
|
|
+ expectedBlocks: number;
|
|
|
+}
|
|
|
+
|
|
|
+// TODO: add better frequency support
|
|
|
+const availableOps = [
|
|
|
+ "insertByEnter",
|
|
|
+ "insertAtLast",
|
|
|
+ // "backspace", // FIXME: cannot backspace to delete block if has children, and prev is a parent, so skip
|
|
|
+ // "delete", // FIXME: cannot delete to delete block if next is outdented
|
|
|
+ "edit",
|
|
|
+ "moveUp",
|
|
|
+ "moveDown",
|
|
|
+ "indent",
|
|
|
+ "unindent",
|
|
|
+ "indent",
|
|
|
+ "unindent",
|
|
|
+ "indent",
|
|
|
+ "indent",
|
|
|
+ // TODO: selection
|
|
|
+]
|
|
|
+
|
|
|
+
|
|
|
+const generateRandomTest = (size: number): RandomTestStep[] => {
|
|
|
+ let blockCount = 1; // default block
|
|
|
+ let steps: RandomTestStep[] = []
|
|
|
+ for (let i = 0; i < size; i++) {
|
|
|
+ let op = availableOps[Math.floor(Math.random() * availableOps.length)];
|
|
|
+ // freq adjust
|
|
|
+ if (Math.random() > 0.9) {
|
|
|
+ op = "insertByEnter"
|
|
|
+ }
|
|
|
+ let loc = Math.floor(Math.random() * blockCount)
|
|
|
+ let text = randomString(randomInt(2, 3))
|
|
|
+
|
|
|
+ if (op === "insertByEnter" || op === "insertAtLast") {
|
|
|
+ blockCount++
|
|
|
+ } else if (op === "backspace") {
|
|
|
+ if (blockCount == 1) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ blockCount--
|
|
|
+ text = null
|
|
|
+ } else if (op === "delete") {
|
|
|
+ if (blockCount == 1) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ // cannot delete last block
|
|
|
+ if (loc === blockCount - 1) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ blockCount--
|
|
|
+ text = null
|
|
|
+ } else if (op === "moveUp" || op === "moveDown") {
|
|
|
+ // no op
|
|
|
+ text = null
|
|
|
+ } else if (op === "indent" || op === "unindent") {
|
|
|
+ // no op
|
|
|
+ text = null
|
|
|
+ } else if (op === "edit") {
|
|
|
+ // no ap
|
|
|
+ } else {
|
|
|
+ throw new Error("unexpected op");
|
|
|
+ }
|
|
|
+ if (blockCount < 1) {
|
|
|
+ blockCount = 1
|
|
|
+ }
|
|
|
+
|
|
|
+ let step: RandomTestStep = {
|
|
|
+ target: loc,
|
|
|
+ op,
|
|
|
+ text,
|
|
|
+ expectedBlocks: blockCount,
|
|
|
+ }
|
|
|
+ steps.push(step)
|
|
|
+ }
|
|
|
+
|
|
|
+ return steps
|
|
|
+}
|
|
|
+
|
|
|
+test('Random editor operations', async ({ page, block }) => {
|
|
|
+ const steps = generateRandomTest(20)
|
|
|
+
|
|
|
+ await createRandomPage(page)
|
|
|
+ await block.mustType("randomized test!")
|
|
|
+
|
|
|
+ for (let i = 0; i < steps.length; i++) {
|
|
|
+ let step = steps[i]
|
|
|
+ const { target, op, expectedBlocks, text } = step;
|
|
|
+
|
|
|
+ console.log(step)
|
|
|
+
|
|
|
+ if (op === "insertByEnter") {
|
|
|
+ await block.activeEditing(target)
|
|
|
+ let charCount = (await page.inputValue('textarea >> nth=0')).length
|
|
|
+ // FIXME: CHECK expect(await block.selectionStart()).toBe(charCount)
|
|
|
+
|
|
|
+ await page.keyboard.press('Enter', { delay: 50 })
|
|
|
+ // FIXME: CHECK await block.waitForBlocks(expectedBlocks)
|
|
|
+ // FIXME: use await block.mustType(text)
|
|
|
+ await block.mustFill(text)
|
|
|
+ } else if (op === "insertAtLast") {
|
|
|
+ await block.clickNext()
|
|
|
+ await block.mustType(text)
|
|
|
+ } else if (op === "backspace") {
|
|
|
+ await block.activeEditing(target)
|
|
|
+ const charCount = (await page.inputValue('textarea >> nth=0')).length
|
|
|
+ for (let i = 0; i < charCount + 1; i++) {
|
|
|
+ await page.keyboard.press('Backspace', { delay: 50 })
|
|
|
+ }
|
|
|
+ } else if (op === "delete") {
|
|
|
+ // move text-cursor to begining
|
|
|
+ // then press delete
|
|
|
+ // then move text-cursor to the end
|
|
|
+ await block.activeEditing(target)
|
|
|
+ let charCount = (await page.inputValue('textarea >> nth=0')).length
|
|
|
+ for (let i = 0; i < charCount; i++) {
|
|
|
+ await page.keyboard.press('ArrowLeft', { delay: 50 })
|
|
|
+ }
|
|
|
+ expect.soft(await block.selectionStart()).toBe(0)
|
|
|
+ for (let i = 0; i < charCount + 1; i++) {
|
|
|
+ await page.keyboard.press('Delete', { delay: 50 })
|
|
|
+ }
|
|
|
+ await block.waitForBlocks(expectedBlocks)
|
|
|
+ charCount = (await page.inputValue('textarea >> nth=0')).length
|
|
|
+ for (let i = 0; i < charCount; i++) {
|
|
|
+ await page.keyboard.press('ArrowRight', { delay: 50 })
|
|
|
+ }
|
|
|
+ } else if (op === "edit") {
|
|
|
+ await block.activeEditing(target)
|
|
|
+ await block.mustFill('') // clear old text
|
|
|
+ await block.mustType(text)
|
|
|
+ } else if (op === "moveUp") {
|
|
|
+ await block.activeEditing(target)
|
|
|
+ if (IsMac) {
|
|
|
+ await page.keyboard.press('Meta+Shift+ArrowUp')
|
|
|
+ } else {
|
|
|
+ await page.keyboard.press('Alt+Shift+ArrowUp')
|
|
|
+ }
|
|
|
+
|
|
|
+ } else if (op === "moveDown") {
|
|
|
+ await block.activeEditing(target)
|
|
|
+ if (IsMac) {
|
|
|
+ await page.keyboard.press('Meta+Shift+ArrowDown')
|
|
|
+ } else {
|
|
|
+ await page.keyboard.press('Alt+Shift+ArrowDown')
|
|
|
+ }
|
|
|
+ } else if (op === "indent") {
|
|
|
+ await block.activeEditing(target)
|
|
|
+ await page.keyboard.press('Tab', { delay: 50 })
|
|
|
+ } else if (op === "unindent") {
|
|
|
+ await block.activeEditing(target)
|
|
|
+ await page.keyboard.press('Shift+Tab', { delay: 50 })
|
|
|
+ } else {
|
|
|
+ throw new Error("unexpected op");
|
|
|
+ }
|
|
|
+
|
|
|
+ // FIXME: CHECK await block.waitForBlocks(expectedBlocks)
|
|
|
+ await page.waitForTimeout(50)
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+})
|