basic.spec.ts 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. import { expect } from '@playwright/test'
  2. import fs from 'fs/promises'
  3. import path from 'path'
  4. import { test } from './fixtures'
  5. import { randomString, createRandomPage } from './utils'
  6. test('toggle sidebar', async ({ page }) => {
  7. let sidebar = page.locator('#left-sidebar')
  8. // Left sidebar is toggled by `is-open` class
  9. if (/is-open/.test(await sidebar.getAttribute('class'))) {
  10. await page.click('#left-menu.button')
  11. await expect(sidebar).not.toHaveClass(/is-open/)
  12. } else {
  13. await page.click('#left-menu.button')
  14. await page.waitForTimeout(10)
  15. await expect(sidebar).toHaveClass(/is-open/)
  16. await page.click('#left-menu.button')
  17. await page.waitForTimeout(10)
  18. await expect(sidebar).not.toHaveClass(/is-open/)
  19. }
  20. await page.click('#left-menu.button')
  21. await page.waitForTimeout(10)
  22. await expect(sidebar).toHaveClass(/is-open/)
  23. await page.waitForSelector('#left-sidebar .left-sidebar-inner', { state: 'visible' })
  24. await page.waitForSelector('#left-sidebar a:has-text("New page")', { state: 'visible' })
  25. })
  26. test('search', async ({ page }) => {
  27. await page.click('#search-button')
  28. await page.waitForSelector('[placeholder="Search or create page"]')
  29. await page.fill('[placeholder="Search or create page"]', 'welcome')
  30. await page.waitForTimeout(500)
  31. const results = await page.$$('#ui__ac-inner .block')
  32. expect(results.length).toBeGreaterThanOrEqual(1)
  33. })
  34. test('create page and blocks, save to disk', async ({ page, block, graphDir }) => {
  35. const pageTitle = await createRandomPage(page)
  36. // do editing
  37. await block.mustFill('this is my first bullet')
  38. await block.enterNext()
  39. await block.waitForBlocks(2)
  40. await block.mustFill('this is my second bullet')
  41. await block.clickNext()
  42. await block.mustFill('this is my third bullet')
  43. await block.indent()
  44. await block.enterNext()
  45. await page.keyboard.type('continue editing test')
  46. await page.keyboard.press('Shift+Enter')
  47. await page.keyboard.type('continue')
  48. await block.enterNext()
  49. expect(await block.unindent()).toBe(true)
  50. expect(await block.unindent()).toBe(false)
  51. await block.mustFill('test ok')
  52. await page.keyboard.press('Escape')
  53. await block.waitForBlocks(5)
  54. // active edit, and create next block
  55. await block.clickNext()
  56. await page.fill('textarea >> nth=0', 'test')
  57. for (let i = 0; i < 5; i++) {
  58. await page.keyboard.press('Backspace', { delay: 100 })
  59. }
  60. await page.keyboard.press('Escape')
  61. await block.waitForBlocks(5)
  62. await page.waitForTimeout(2000) // wait for saving to disk
  63. const contentOnDisk = await fs.readFile(
  64. path.join(graphDir, `pages/${pageTitle}.md`),
  65. 'utf8'
  66. )
  67. expect(contentOnDisk.trim()).toEqual(`
  68. - this is my first bullet
  69. - this is my second bullet
  70. - this is my third bullet
  71. - continue editing test
  72. continue
  73. - test ok`.trim())
  74. })
  75. test('delete and backspace', async ({ page, block }) => {
  76. await createRandomPage(page)
  77. await block.mustFill('test')
  78. // backspace
  79. await page.keyboard.press('Backspace')
  80. await page.keyboard.press('Backspace')
  81. expect(await page.inputValue('textarea >> nth=0')).toBe('te')
  82. // refill
  83. await block.enterNext()
  84. await block.mustType('test')
  85. await page.keyboard.press('ArrowLeft', { delay: 50 })
  86. await page.keyboard.press('ArrowLeft', { delay: 50 })
  87. // delete
  88. await page.keyboard.press('Delete', { delay: 50 })
  89. expect(await page.inputValue('textarea >> nth=0')).toBe('tet')
  90. await page.keyboard.press('Delete', { delay: 50 })
  91. expect(await page.inputValue('textarea >> nth=0')).toBe('te')
  92. await page.keyboard.press('Delete', { delay: 50 })
  93. expect(await page.inputValue('textarea >> nth=0')).toBe('te')
  94. // TODO: test delete & backspace across blocks
  95. })
  96. test('selection', async ({ page, block }) => {
  97. await createRandomPage(page)
  98. // add 5 blocks
  99. await block.mustFill('line 1')
  100. await block.enterNext()
  101. await block.mustFill('line 2')
  102. await block.enterNext()
  103. expect(await block.indent()).toBe(true)
  104. await block.mustFill('line 3')
  105. await block.enterNext()
  106. await block.mustFill('line 4')
  107. expect(await block.indent()).toBe(true)
  108. await block.enterNext()
  109. await block.mustFill('line 5')
  110. // shift+up select 3 blocks
  111. await page.keyboard.down('Shift')
  112. await page.keyboard.press('ArrowUp')
  113. await page.keyboard.press('ArrowUp')
  114. await page.keyboard.press('ArrowUp')
  115. await page.keyboard.up('Shift')
  116. await block.waitForSelectedBlocks(3)
  117. await page.keyboard.press('Backspace')
  118. await block.waitForBlocks(2)
  119. })
  120. test('template', async ({ page, block }) => {
  121. const randomTemplate = randomString(6)
  122. await createRandomPage(page)
  123. await block.mustFill('template test\ntemplate:: ' + randomTemplate)
  124. await page.keyboard.press('Enter')
  125. await block.clickNext()
  126. expect(await block.indent()).toBe(true)
  127. await block.mustFill('line1')
  128. await block.enterNext()
  129. await block.mustFill('line2')
  130. await block.enterNext()
  131. expect(await block.indent()).toBe(true)
  132. await block.mustFill('line3')
  133. await block.enterNext()
  134. expect(await block.unindent()).toBe(true)
  135. expect(await block.unindent()).toBe(true)
  136. expect(await block.unindent()).toBe(false) // already at the first level
  137. await block.waitForBlocks(5)
  138. // NOTE: use delay to type slower, to trigger auto-completion UI.
  139. await block.mustType('/template')
  140. await page.click('[title="Insert a created template here"]')
  141. // type to search template name
  142. await page.keyboard.type(randomTemplate.substring(0, 3), { delay: 100 })
  143. const popupMenuItem = page.locator('.absolute >> text=' + randomTemplate)
  144. await popupMenuItem.waitFor({ timeout: 2000 }) // wait for template search
  145. await popupMenuItem.click()
  146. await block.waitForBlocks(8)
  147. })
  148. test('auto completion square brackets', async ({ page, block }) => {
  149. await createRandomPage(page)
  150. // In this test, `type` is unsed instead of `fill`, to allow for auto-completion.
  151. // [[]]
  152. await block.mustType('This is a [', { toBe: 'This is a []'})
  153. await block.mustType('[', { toBe: 'This is a [[]]'})
  154. // wait for search popup
  155. await page.waitForSelector('text="Search for a page"')
  156. // re-enter edit mode
  157. await page.press('textarea >> nth=0', 'Escape')
  158. await page.click('.ls-block >> nth=-1')
  159. await page.waitForSelector('textarea >> nth=0', { state: 'visible' })
  160. // #3253
  161. await page.press('textarea >> nth=0', 'ArrowLeft')
  162. await page.press('textarea >> nth=0', 'ArrowLeft')
  163. await page.press('textarea >> nth=0', 'Enter')
  164. await page.waitForSelector('text="Search for a page"', { state: 'visible' })
  165. // type more `]`s
  166. await page.type('textarea >> nth=0', ']')
  167. expect(await page.inputValue('textarea >> nth=0')).toBe('This is a [[]]')
  168. await page.type('textarea >> nth=0', ']')
  169. expect(await page.inputValue('textarea >> nth=0')).toBe('This is a [[]]')
  170. await page.type('textarea >> nth=0', ']')
  171. expect(await page.inputValue('textarea >> nth=0')).toBe('This is a [[]]]')
  172. })
  173. test('auto completion and auto pair', async ({ page, block }) => {
  174. await createRandomPage(page)
  175. await block.mustFill('Auto-completion test')
  176. await block.enterNext()
  177. // {{
  178. await block.mustType('type {{', { toBe: 'type {{}}'})
  179. // ((
  180. await block.clickNext()
  181. await block.mustType('type (', { toBe: 'type ()'})
  182. await block.mustType('(', { toBe: 'type (())'})
  183. // [[ #3251
  184. await block.clickNext()
  185. await block.mustType('type [', { toBe: 'type []'})
  186. await block.mustType('[', { toBe: 'type [[]]'})
  187. await block.escapeEditing() // escape any popup from `[[]]`
  188. // ``
  189. await block.clickNext()
  190. await block.mustType('type `', { toBe: 'type ``'})
  191. await block.mustType('code here', { toBe: 'type `code here`'})
  192. })
  193. test('invalid page props #3944', async ({ page, block }) => {
  194. await createRandomPage(page)
  195. await block.mustFill('public:: true\nsize:: 65535')
  196. await page.press('textarea >> nth=0', 'Enter')
  197. // Force rendering property block
  198. await block.clickNext()
  199. })