random.spec.ts 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. import { expect } from '@playwright/test'
  2. import { test } from './fixtures'
  3. import {
  4. createRandomPage, randomInt, randomInsert, randomEditDelete, randomEditMoveUpDown, IsMac, randomString,
  5. } from './utils'
  6. /**
  7. * Randomized test for single page editing. Block-wise.
  8. *
  9. * For now, only check total number of blocks.
  10. */
  11. interface RandomTestStep {
  12. /// target block
  13. target: number;
  14. /// action
  15. op: string;
  16. text: string;
  17. /// expected total block number
  18. expectedBlocks: number;
  19. }
  20. // TODO: add better frequency support
  21. const availableOps = [
  22. "insertByEnter",
  23. "insertAtLast",
  24. // "backspace", // FIXME: cannot backspace to delete block if has children, and prev is a parent, so skip
  25. // "delete", // FIXME: cannot delete to delete block if next is outdented
  26. "edit",
  27. "moveUp",
  28. "moveDown",
  29. "indent",
  30. "unindent",
  31. "indent",
  32. "unindent",
  33. "indent",
  34. "indent",
  35. // TODO: selection
  36. ]
  37. const generateRandomTest = (size: number): RandomTestStep[] => {
  38. let blockCount = 1; // default block
  39. let steps: RandomTestStep[] = []
  40. for (let i = 0; i < size; i++) {
  41. let op = availableOps[Math.floor(Math.random() * availableOps.length)];
  42. // freq adjust
  43. if (Math.random() > 0.9) {
  44. op = "insertByEnter"
  45. }
  46. let loc = Math.floor(Math.random() * blockCount)
  47. let text = randomString(randomInt(2, 3))
  48. if (op === "insertByEnter" || op === "insertAtLast") {
  49. blockCount++
  50. } else if (op === "backspace") {
  51. if (blockCount == 1) {
  52. continue
  53. }
  54. blockCount--
  55. text = null
  56. } else if (op === "delete") {
  57. if (blockCount == 1) {
  58. continue
  59. }
  60. // cannot delete last block
  61. if (loc === blockCount - 1) {
  62. continue
  63. }
  64. blockCount--
  65. text = null
  66. } else if (op === "moveUp" || op === "moveDown") {
  67. // no op
  68. text = null
  69. } else if (op === "indent" || op === "unindent") {
  70. // no op
  71. text = null
  72. } else if (op === "edit") {
  73. // no ap
  74. } else {
  75. throw new Error("unexpected op");
  76. }
  77. if (blockCount < 1) {
  78. blockCount = 1
  79. }
  80. let step: RandomTestStep = {
  81. target: loc,
  82. op,
  83. text,
  84. expectedBlocks: blockCount,
  85. }
  86. steps.push(step)
  87. }
  88. return steps
  89. }
  90. test('Random editor operations', async ({ page, block }) => {
  91. const steps = generateRandomTest(20)
  92. await createRandomPage(page)
  93. await block.mustType("randomized test!")
  94. for (let i = 0; i < steps.length; i++) {
  95. let step = steps[i]
  96. const { target, op, expectedBlocks, text } = step;
  97. console.log(step)
  98. if (op === "insertByEnter") {
  99. await block.activeEditing(target)
  100. let charCount = (await page.inputValue('textarea >> nth=0')).length
  101. // FIXME: CHECK expect(await block.selectionStart()).toBe(charCount)
  102. await page.keyboard.press('Enter', { delay: 50 })
  103. // FIXME: CHECK await block.waitForBlocks(expectedBlocks)
  104. // FIXME: use await block.mustType(text)
  105. await block.mustFill(text)
  106. } else if (op === "insertAtLast") {
  107. await block.clickNext()
  108. await block.mustType(text)
  109. } else if (op === "backspace") {
  110. await block.activeEditing(target)
  111. const charCount = (await page.inputValue('textarea >> nth=0')).length
  112. for (let i = 0; i < charCount + 1; i++) {
  113. await page.keyboard.press('Backspace', { delay: 50 })
  114. }
  115. } else if (op === "delete") {
  116. // move text-cursor to begining
  117. // then press delete
  118. // then move text-cursor to the end
  119. await block.activeEditing(target)
  120. let charCount = (await page.inputValue('textarea >> nth=0')).length
  121. for (let i = 0; i < charCount; i++) {
  122. await page.keyboard.press('ArrowLeft', { delay: 50 })
  123. }
  124. expect.soft(await block.selectionStart()).toBe(0)
  125. for (let i = 0; i < charCount + 1; i++) {
  126. await page.keyboard.press('Delete', { delay: 50 })
  127. }
  128. await block.waitForBlocks(expectedBlocks)
  129. charCount = (await page.inputValue('textarea >> nth=0')).length
  130. for (let i = 0; i < charCount; i++) {
  131. await page.keyboard.press('ArrowRight', { delay: 50 })
  132. }
  133. } else if (op === "edit") {
  134. await block.activeEditing(target)
  135. await block.mustFill('') // clear old text
  136. await block.mustType(text)
  137. } else if (op === "moveUp") {
  138. await block.activeEditing(target)
  139. if (IsMac) {
  140. await page.keyboard.press('Meta+Shift+ArrowUp')
  141. } else {
  142. await page.keyboard.press('Alt+Shift+ArrowUp')
  143. }
  144. } else if (op === "moveDown") {
  145. await block.activeEditing(target)
  146. if (IsMac) {
  147. await page.keyboard.press('Meta+Shift+ArrowDown')
  148. } else {
  149. await page.keyboard.press('Alt+Shift+ArrowDown')
  150. }
  151. } else if (op === "indent") {
  152. await block.activeEditing(target)
  153. await page.keyboard.press('Tab', { delay: 50 })
  154. } else if (op === "unindent") {
  155. await block.activeEditing(target)
  156. await page.keyboard.press('Shift+Tab', { delay: 50 })
  157. } else {
  158. throw new Error("unexpected op");
  159. }
  160. // FIXME: CHECK await block.waitForBlocks(expectedBlocks)
  161. await page.waitForTimeout(50)
  162. }
  163. })