code-editing.spec.ts 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. import { expect } from '@playwright/test'
  2. import { test } from './fixtures'
  3. import { createRandomPage, escapeToCodeEditor, escapeToBlockEditor } from './utils'
  4. /**
  5. * NOTE: CodeMirror is a complex library that requires a lot of setup to work.
  6. * This test suite is designed to test the basic functionality of the editor.
  7. * It is not intended to test the full functionality of CodeMirror.
  8. * For more information, see: https://codemirror.net/doc/manual.html
  9. */
  10. test('switch code editing mode', async ({ page }) => {
  11. await createRandomPage(page)
  12. // NOTE: ` will trigger auto-pairing in Logseq
  13. // NOTE: ( will trigger auto-pairing in CodeMirror
  14. // NOTE: waitForTimeout is needed to ensure that the hotkey handler is finished (shift+enter)
  15. // NOTE: waitForTimeout is needed to ensure that the CodeMirror editor is fully loaded and unloaded
  16. // NOTE: multiple textarea elements are existed in the editor, be careful to select the right one
  17. // code block with 0 line
  18. await page.type('textarea >> nth=0', '```clojure\n')
  19. // line number: 1
  20. await page.waitForSelector('.CodeMirror pre', { state: 'visible' })
  21. expect(await page.locator('.CodeMirror-gutter-wrapper .CodeMirror-linenumber').innerText()).toBe('1')
  22. // lang label: clojure
  23. expect(await page.innerText('.block-body .extensions__code-lang')).toBe('clojure')
  24. await page.press('.CodeMirror textarea', 'Escape')
  25. await page.waitForSelector('.CodeMirror pre', { state: 'hidden' })
  26. expect(await page.inputValue('textarea >> nth=0')).toBe('```clojure\n```')
  27. await page.waitForTimeout(200)
  28. await page.press('textarea >> nth=0', 'Escape')
  29. await page.waitForSelector('.CodeMirror pre', { state: 'visible' })
  30. // NOTE: must wait here, await loading of CodeMirror editor
  31. await page.waitForTimeout(200)
  32. await page.click('.CodeMirror pre')
  33. await page.waitForTimeout(200)
  34. await page.type('.CodeMirror textarea', '(+ 1 1')
  35. await page.press('.CodeMirror textarea', 'Escape')
  36. await page.waitForSelector('.CodeMirror pre', { state: 'hidden' })
  37. expect(await page.inputValue('.block-editor textarea')).toBe('```clojure\n(+ 1 1)\n```')
  38. await page.waitForTimeout(200) // editor unloading
  39. await page.press('.block-editor textarea', 'Escape')
  40. await page.waitForTimeout(200) // editor loading
  41. // click position is estimated to be at the begining of the first line
  42. await page.click('.CodeMirror pre', { position: { x: 1, y: 5 } })
  43. await page.waitForTimeout(200)
  44. await page.type('.CodeMirror textarea', ';; comment\n\n \n')
  45. await page.press('.CodeMirror textarea', 'Escape')
  46. await page.waitForSelector('.CodeMirror pre', { state: 'hidden' })
  47. expect(await page.inputValue('.block-editor textarea')).toBe('```clojure\n;; comment\n\n \n(+ 1 1)\n```')
  48. })
  49. test('convert from block content to code', async ({ page }) => {
  50. await createRandomPage(page)
  51. await page.type('.block-editor textarea', '```')
  52. await page.press('.block-editor textarea', 'Shift+Enter')
  53. await page.waitForTimeout(200) // wait for hotkey handler
  54. await page.press('.block-editor textarea', 'Escape')
  55. await page.waitForSelector('.CodeMirror pre', { state: 'visible' })
  56. await page.waitForTimeout(500)
  57. await page.click('.CodeMirror pre')
  58. await page.waitForTimeout(500)
  59. expect(await page.locator('.CodeMirror-gutter-wrapper .CodeMirror-linenumber >> nth=-1').innerText()).toBe('1')
  60. await page.press('.CodeMirror textarea', 'Escape')
  61. await page.waitForTimeout(500)
  62. expect(await page.inputValue('.block-editor textarea')).toBe('```\n```')
  63. // reset block, code block with 1 line
  64. await page.fill('.block-editor textarea', '```\n\n```')
  65. await page.waitForTimeout(200) // wait for fill
  66. await escapeToCodeEditor(page)
  67. expect(await page.locator('.CodeMirror-gutter-wrapper .CodeMirror-linenumber >> nth=-1').innerText()).toBe('1')
  68. await escapeToBlockEditor(page)
  69. expect(await page.inputValue('.block-editor textarea')).toBe('```\n\n```')
  70. // reset block, code block with 2 line
  71. await page.fill('.block-editor textarea', '```\n\n\n```')
  72. await page.waitForTimeout(200)
  73. await escapeToCodeEditor(page)
  74. expect(await page.locator('.CodeMirror-gutter-wrapper .CodeMirror-linenumber >> nth=-1').innerText()).toBe('2')
  75. await escapeToBlockEditor(page)
  76. expect(await page.inputValue('.block-editor textarea')).toBe('```\n\n\n```')
  77. await page.fill('.block-editor textarea', '```\n indented\nsecond line\n\n```')
  78. await page.waitForTimeout(200)
  79. await escapeToCodeEditor(page)
  80. await escapeToBlockEditor(page)
  81. expect(await page.inputValue('.block-editor textarea')).toBe('```\n indented\nsecond line\n\n```')
  82. await page.fill('.block-editor textarea', '```\n indented\n indented\n```')
  83. await page.waitForTimeout(200)
  84. await escapeToCodeEditor(page)
  85. await escapeToBlockEditor(page)
  86. expect(await page.inputValue('.block-editor textarea')).toBe('```\n indented\n indented\n```')
  87. })
  88. test('code block mixed input source', async ({ page }) => {
  89. await createRandomPage(page)
  90. await page.fill('.block-editor textarea', '```\n ABC\n```')
  91. await page.waitForTimeout(500) // wait for fill
  92. await escapeToCodeEditor(page)
  93. await page.type('.CodeMirror textarea', ' DEF\nGHI')
  94. await page.waitForTimeout(500)
  95. await page.press('.CodeMirror textarea', 'Escape')
  96. await page.waitForTimeout(500)
  97. // NOTE: auto-indent is on
  98. expect(await page.inputValue('.block-editor textarea')).toBe('```\n ABC DEF\n GHI\n```')
  99. })
  100. test('code block with text around', async ({ page }) => {
  101. await createRandomPage(page)
  102. await page.fill('.block-editor textarea', 'Heading\n```\n```\nFooter')
  103. await page.waitForTimeout(200)
  104. await escapeToCodeEditor(page)
  105. await page.type('.CodeMirror textarea', 'first\n second')
  106. await page.waitForTimeout(500)
  107. await page.press('.CodeMirror textarea', 'Escape')
  108. await page.waitForTimeout(500)
  109. expect(await page.inputValue('.block-editor textarea')).toBe('Heading\n```\nfirst\n second\n```\nFooter')
  110. })
  111. test('multiple code block', async ({ page }) => {
  112. await createRandomPage(page)
  113. // NOTE: the two code blocks are of the same content
  114. await page.fill('.block-editor textarea', '中文 Heading\n```clojure\n```\nMiddle 🚀\n```clojure\n```\nFooter')
  115. await page.waitForTimeout(200)
  116. await page.press('.block-editor textarea', 'Escape')
  117. await page.waitForSelector('.CodeMirror pre', { state: 'visible' })
  118. // first
  119. await page.waitForTimeout(500)
  120. await page.click('.CodeMirror pre >> nth=0')
  121. await page.waitForTimeout(500)
  122. await page.type('.CodeMirror textarea >> nth=0', ':key-test\n', { strict: true })
  123. await page.waitForTimeout(500)
  124. await page.press('.CodeMirror textarea >> nth=0', 'Escape')
  125. await page.waitForTimeout(500)
  126. expect(await page.inputValue('.block-editor textarea'))
  127. .toBe('中文 Heading\n```clojure\n:key-test\n\n```\nMiddle 🚀\n```clojure\n```\nFooter')
  128. // second
  129. await page.press('.block-editor textarea', 'Escape')
  130. await page.waitForSelector('.CodeMirror pre', { state: 'visible' })
  131. await page.waitForTimeout(500)
  132. await page.click('.CodeMirror >> nth=1 >> pre')
  133. await page.waitForTimeout(500)
  134. await page.type('.CodeMirror textarea >> nth=1', '\n :key-test 日本語\n', { strict: true })
  135. await page.waitForTimeout(500)
  136. await page.press('.CodeMirror textarea >> nth=1', 'Escape')
  137. await page.waitForTimeout(500)
  138. expect(await page.inputValue('.block-editor textarea'))
  139. .toBe('中文 Heading\n```clojure\n:key-test\n\n```\nMiddle 🚀\n```clojure\n\n :key-test 日本語\n\n```\nFooter')
  140. })
  141. test('click outside to exit', async ({ page }) => {
  142. await createRandomPage(page)
  143. await page.fill('.block-editor textarea', 'Header ``Click``\n```\n ABC\n```')
  144. await page.waitForTimeout(200) // wait for fill
  145. await escapeToCodeEditor(page)
  146. await page.type('.CodeMirror textarea', ' DEF\nGHI')
  147. await page.waitForTimeout(500)
  148. await page.click('text=Click')
  149. await page.waitForTimeout(500)
  150. // NOTE: auto-indent is on
  151. expect(await page.inputValue('.block-editor textarea')).toBe('Header ``Click``\n```\n ABC DEF\n GHI\n```')
  152. })
  153. test('click language label to exit #3463', async ({ page, block }) => {
  154. await createRandomPage(page)
  155. await page.fill('.block-editor textarea', '```cpp\n```')
  156. await page.waitForTimeout(200)
  157. await escapeToCodeEditor(page)
  158. await page.type('.CodeMirror textarea', '#include<iostream>')
  159. await page.waitForTimeout(500)
  160. await page.click('text=cpp') // the language label
  161. await page.waitForTimeout(500)
  162. expect(await page.inputValue('.block-editor textarea')).toBe('```cpp\n#include<iostream>\n```')
  163. })
  164. test('multi properties with code', async ({ page }) => {
  165. await createRandomPage(page)
  166. await page.fill('.block-editor textarea',
  167. 'type:: code\n' +
  168. '类型:: 代码\n' +
  169. '```go\n' +
  170. 'if err != nil {\n' +
  171. '\treturn err\n' +
  172. '}\n' +
  173. '```'
  174. )
  175. await page.waitForTimeout(200)
  176. await escapeToCodeEditor(page)
  177. // first character of code
  178. await page.click('.CodeMirror pre', { position: { x: 1, y: 5 } })
  179. await page.waitForTimeout(500)
  180. await page.type('.CodeMirror textarea', '// Returns nil\n')
  181. await page.waitForTimeout(500)
  182. await page.press('.CodeMirror textarea', 'Escape')
  183. await page.waitForTimeout(500)
  184. expect(await page.inputValue('.block-editor textarea')).toBe(
  185. 'type:: code\n' +
  186. '类型:: 代码\n' +
  187. '```go\n' +
  188. '// Returns nil\n' +
  189. 'if err != nil {\n' +
  190. '\treturn err\n' +
  191. '}\n' +
  192. '```'
  193. )
  194. })