code-editing.spec.ts 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  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(':nth-match(textarea, 1)', '```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(':nth-match(textarea, 1)')).toBe('```clojure\n```')
  27. await page.waitForTimeout(500)
  28. await page.press(':nth-match(textarea, 1)', 'Escape')
  29. await page.waitForSelector('.CodeMirror pre', { state: 'visible' })
  30. // NOTE: must wait here, await loading of CodeMirror editor
  31. await page.waitForTimeout(500)
  32. await page.click('.CodeMirror pre')
  33. await page.waitForTimeout(500)
  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(500) // editor unloading
  39. await page.press('.block-editor textarea', 'Escape')
  40. await page.waitForTimeout(500) // 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(500)
  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. await page.waitForTimeout(500)
  49. })
  50. test('convert from block content to code', async ({ page }) => {
  51. await createRandomPage(page)
  52. await page.type('.block-editor textarea', '```')
  53. await page.press('.block-editor textarea', 'Shift+Enter')
  54. await page.waitForTimeout(100) // wait for hotkey handler
  55. await page.press('.block-editor textarea', 'Escape')
  56. await page.waitForSelector('.CodeMirror pre', { state: 'visible' })
  57. await page.waitForTimeout(500)
  58. await page.click('.CodeMirror pre')
  59. await page.waitForTimeout(500)
  60. expect(await page.locator('.CodeMirror-gutter-wrapper .CodeMirror-linenumber >> nth=-1').innerText()).toBe('1')
  61. await page.press('.CodeMirror textarea', 'Escape')
  62. await page.waitForTimeout(500)
  63. expect(await page.inputValue('.block-editor textarea')).toBe('```\n```')
  64. // reset block, code block with 1 line
  65. await page.fill('.block-editor textarea', '```\n\n```')
  66. await page.waitForTimeout(500) // wait for fill
  67. await escapeToCodeEditor(page)
  68. expect(await page.locator('.CodeMirror-gutter-wrapper .CodeMirror-linenumber >> nth=-1').innerText()).toBe('1')
  69. await escapeToBlockEditor(page)
  70. expect(await page.inputValue('.block-editor textarea')).toBe('```\n\n```')
  71. // reset block, code block with 2 line
  72. await page.fill('.block-editor textarea', '```\n\n\n```')
  73. await page.waitForTimeout(500)
  74. await escapeToCodeEditor(page)
  75. expect(await page.locator('.CodeMirror-gutter-wrapper .CodeMirror-linenumber >> nth=-1').innerText()).toBe('2')
  76. await escapeToBlockEditor(page)
  77. expect(await page.inputValue('.block-editor textarea')).toBe('```\n\n\n```')
  78. await page.fill('.block-editor textarea', '```\n indented\nsecond line\n\n```')
  79. await page.waitForTimeout(500)
  80. await escapeToCodeEditor(page)
  81. await escapeToBlockEditor(page)
  82. expect(await page.inputValue('.block-editor textarea')).toBe('```\n indented\nsecond line\n\n```')
  83. await page.fill('.block-editor textarea', '```\n indented\n indented\n```')
  84. await page.waitForTimeout(500)
  85. await escapeToCodeEditor(page)
  86. await escapeToBlockEditor(page)
  87. expect(await page.inputValue('.block-editor textarea')).toBe('```\n indented\n indented\n```')
  88. })
  89. test('code block mixed input source', async ({ page }) => {
  90. await createRandomPage(page)
  91. await page.fill('.block-editor textarea', '```\n ABC\n```')
  92. await page.waitForTimeout(500) // wait for fill
  93. await escapeToCodeEditor(page)
  94. await page.type('.CodeMirror textarea', ' DEF\nGHI')
  95. await page.waitForTimeout(500)
  96. await page.press('.CodeMirror textarea', 'Escape')
  97. await page.waitForTimeout(500)
  98. // NOTE: auto-indent is on
  99. expect(await page.inputValue('.block-editor textarea')).toBe('```\n ABC DEF\n GHI\n```')
  100. })
  101. test('code block with text around', async ({ page }) => {
  102. await createRandomPage(page)
  103. await page.fill('.block-editor textarea', 'Heading\n```\n```\nFooter')
  104. await page.waitForTimeout(500)
  105. await escapeToCodeEditor(page)
  106. await page.type('.CodeMirror textarea', 'first\n second')
  107. await page.waitForTimeout(500)
  108. await page.press('.CodeMirror textarea', 'Escape')
  109. await page.waitForTimeout(500)
  110. expect(await page.inputValue('.block-editor textarea')).toBe('Heading\n```\nfirst\n second\n```\nFooter')
  111. })
  112. test('multiple code block', async ({ page }) => {
  113. await createRandomPage(page)
  114. // NOTE: the two code blocks are of the same content
  115. await page.fill('.block-editor textarea', '中文 Heading\n```clojure\n```\nMiddle 🚀\n```clojure\n```\nFooter')
  116. await page.waitForTimeout(500)
  117. await page.press('.block-editor textarea', 'Escape')
  118. await page.waitForSelector('.CodeMirror pre', { state: 'visible' })
  119. // first
  120. await page.waitForTimeout(500)
  121. await page.click('.CodeMirror pre >> nth=0')
  122. await page.waitForTimeout(500)
  123. await page.type('.CodeMirror textarea >> nth=0', ':key-test\n', { strict: true })
  124. await page.waitForTimeout(500)
  125. await page.press('.CodeMirror textarea >> nth=0', 'Escape')
  126. await page.waitForTimeout(500)
  127. expect(await page.inputValue('.block-editor textarea'))
  128. .toBe('中文 Heading\n```clojure\n:key-test\n\n```\nMiddle 🚀\n```clojure\n```\nFooter')
  129. // second
  130. await page.press('.block-editor textarea', 'Escape')
  131. await page.waitForSelector('.CodeMirror pre', { state: 'visible' })
  132. await page.waitForTimeout(500)
  133. await page.click('.CodeMirror pre >> nth=1')
  134. await page.waitForTimeout(500)
  135. await page.type('.CodeMirror textarea >> nth=1', '\n :key-test 日本語\n', { strict: true })
  136. await page.waitForTimeout(500)
  137. await page.press('.CodeMirror textarea >> nth=1', 'Escape')
  138. await page.waitForTimeout(500)
  139. expect(await page.inputValue('.block-editor textarea'))
  140. .toBe('中文 Heading\n```clojure\n:key-test\n\n```\nMiddle 🚀\n```clojure\n\n :key-test 日本語\n\n```\nFooter')
  141. })
  142. test('click outside to exit', async ({ page }) => {
  143. await createRandomPage(page)
  144. await page.fill('.block-editor textarea', 'Header ``Click``\n```\n ABC\n```')
  145. await page.waitForTimeout(500) // wait for fill
  146. await escapeToCodeEditor(page)
  147. await page.type('.CodeMirror textarea', ' DEF\nGHI')
  148. await page.waitForTimeout(500)
  149. await page.click('text=Click')
  150. await page.waitForTimeout(500)
  151. // NOTE: auto-indent is on
  152. expect(await page.inputValue('.block-editor textarea')).toBe('Header ``Click``\n```\n ABC DEF\n GHI\n```')
  153. })
  154. test('click lanuage label to exit #3463', async ({ page }) => {
  155. await createRandomPage(page)
  156. await page.press('.block-editor textarea', 'Enter')
  157. await page.waitForTimeout(200)
  158. await page.fill('.block-editor textarea', '```cpp\n```')
  159. await page.waitForTimeout(500)
  160. await escapeToCodeEditor(page)
  161. await page.type('.CodeMirror textarea', '#include<iostream>')
  162. await page.waitForTimeout(500)
  163. await page.click('text=cpp') // the language label
  164. await page.waitForTimeout(500)
  165. expect(await page.inputValue('.block-editor textarea')).toBe('```cpp\n#include<iostream>\n```')
  166. })
  167. test('multi properties with code', async ({ page }) => {
  168. await createRandomPage(page)
  169. await page.fill('.block-editor textarea',
  170. 'type:: code\n' +
  171. '类型:: 代码\n' +
  172. '```go\n' +
  173. 'if err != nil {\n' +
  174. '\treturn err\n' +
  175. '}\n' +
  176. '```'
  177. )
  178. await page.waitForTimeout(500)
  179. await escapeToCodeEditor(page)
  180. // first character of code
  181. await page.click('.CodeMirror pre', { position: { x: 1, y: 5 } })
  182. await page.waitForTimeout(500)
  183. await page.type('.CodeMirror textarea', '// Returns nil\n')
  184. await page.waitForTimeout(500)
  185. await page.press('.CodeMirror textarea', 'Escape')
  186. await page.waitForTimeout(500)
  187. expect(await page.inputValue('.block-editor textarea')).toBe(
  188. 'type:: code\n' +
  189. '类型:: 代码\n' +
  190. '```go\n' +
  191. '// Returns nil\n' +
  192. 'if err != nil {\n' +
  193. '\treturn err\n' +
  194. '}\n' +
  195. '```'
  196. )
  197. })