random.spec.ts 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. import { expect } from '@playwright/test'
  2. import { test } from './fixtures'
  3. import {
  4. createRandomPage, randomInt, 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. // TODO: Fix test that intermittently started failing after https://github.com/logseq/logseq/pull/6945
  91. test.skip('Random editor operations', async ({ page, block }) => {
  92. const steps = generateRandomTest(20)
  93. await createRandomPage(page)
  94. await block.mustType("randomized test!")
  95. for (let i = 0; i < steps.length; i++) {
  96. let step = steps[i]
  97. const { target, op, expectedBlocks, text } = step;
  98. console.log(step)
  99. if (op === "insertByEnter") {
  100. await block.activeEditing(target)
  101. let charCount = (await page.inputValue('textarea >> nth=0')).length
  102. // FIXME: CHECK expect(await block.selectionStart()).toBe(charCount)
  103. await page.keyboard.press('Enter', { delay: 50 })
  104. // FIXME: CHECK await block.waitForBlocks(expectedBlocks)
  105. // FIXME: use await block.mustType(text)
  106. await block.mustFill(text)
  107. } else if (op === "insertAtLast") {
  108. await block.clickNext()
  109. await block.mustType(text)
  110. } else if (op === "backspace") {
  111. await block.activeEditing(target)
  112. const charCount = (await page.inputValue('textarea >> nth=0')).length
  113. for (let i = 0; i < charCount + 1; i++) {
  114. await page.keyboard.press('Backspace', { delay: 50 })
  115. }
  116. } else if (op === "delete") {
  117. // move text-cursor to begining
  118. // then press delete
  119. // then move text-cursor to the end
  120. await block.activeEditing(target)
  121. let charCount = (await page.inputValue('textarea >> nth=0')).length
  122. for (let i = 0; i < charCount; i++) {
  123. await page.keyboard.press('ArrowLeft', { delay: 50 })
  124. }
  125. expect.soft(await block.selectionStart()).toBe(0)
  126. for (let i = 0; i < charCount + 1; i++) {
  127. await page.keyboard.press('Delete', { delay: 50 })
  128. }
  129. await block.waitForBlocks(expectedBlocks)
  130. charCount = (await page.inputValue('textarea >> nth=0')).length
  131. for (let i = 0; i < charCount; i++) {
  132. await page.keyboard.press('ArrowRight', { delay: 50 })
  133. }
  134. } else if (op === "edit") {
  135. await block.activeEditing(target)
  136. await block.mustFill('') // clear old text
  137. await block.mustType(text)
  138. } else if (op === "moveUp") {
  139. await block.activeEditing(target)
  140. if (IsMac) {
  141. await page.keyboard.press('Meta+Shift+ArrowUp')
  142. } else {
  143. await page.keyboard.press('Alt+Shift+ArrowUp')
  144. }
  145. } else if (op === "moveDown") {
  146. await block.activeEditing(target)
  147. if (IsMac) {
  148. await page.keyboard.press('Meta+Shift+ArrowDown')
  149. } else {
  150. await page.keyboard.press('Alt+Shift+ArrowDown')
  151. }
  152. } else if (op === "indent") {
  153. await block.activeEditing(target)
  154. await page.keyboard.press('Tab', { delay: 50 })
  155. } else if (op === "unindent") {
  156. await block.activeEditing(target)
  157. await page.keyboard.press('Shift+Tab', { delay: 50 })
  158. } else {
  159. throw new Error("unexpected op");
  160. }
  161. // FIXME: CHECK await block.waitForBlocks(expectedBlocks)
  162. await page.waitForTimeout(50)
  163. }
  164. })