test-article-auto-format.mjs 3.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. import assert from 'node:assert/strict'
  2. import { readFile } from 'node:fs/promises'
  3. import ts from 'typescript'
  4. const sourceUrl = new URL(
  5. '../src/views/Editor/EnglishSpeaking/composables/articleAutoFormatRules.ts',
  6. import.meta.url,
  7. )
  8. const compiled = ts.transpileModule(await readFile(sourceUrl, 'utf8'), {
  9. compilerOptions: { module: ts.ModuleKind.ESNext, target: ts.ScriptTarget.ES2022 },
  10. }).outputText
  11. const {
  12. countWords,
  13. normalizeParagraphs,
  14. hasOverlongParagraph,
  15. shouldPromptOnPaste,
  16. MAX_PARAGRAPH_WORDS,
  17. PASTE_PROMPT_MIN_WORDS,
  18. } = await import(`data:text/javascript,${encodeURIComponent(compiled)}`)
  19. /** 生成 n 个单词的文本 */
  20. const words = n => Array.from({ length: n }, (_, i) => `w${i}`).join(' ')
  21. // ---------------- countWords ----------------
  22. assert.equal(countWords(''), 0)
  23. assert.equal(countWords(' \n\n '), 0)
  24. assert.equal(countWords('one two three'), 3)
  25. assert.equal(countWords(' leading and trailing '), 3)
  26. assert.equal(countWords(words(300)), 300)
  27. // ---------------- normalizeParagraphs ----------------
  28. // 与后端 split_paragraphs 同规则:单 \n 与空行皆为段界
  29. assert.equal(normalizeParagraphs('A one.\n\nB two.'), 'A one.\n\nB two.')
  30. assert.equal(normalizeParagraphs('A one.\nB two.'), 'A one.\n\nB two.')
  31. assert.equal(normalizeParagraphs('A one.\n\n\n\n \nB two.'), 'A one.\n\nB two.')
  32. assert.equal(normalizeParagraphs(' A one here. '), 'A one here.')
  33. assert.equal(normalizeParagraphs(''), '')
  34. assert.equal(normalizeParagraphs(' \n\n '), '')
  35. // 幂等:归一化结果再归一化不变(会回写 textarea,必须稳定)
  36. assert.equal(normalizeParagraphs(normalizeParagraphs('A.\nB.\n\n\nC.')), 'A.\n\nB.\n\nC.')
  37. // ---------------- hasOverlongParagraph(299 / 300 / 301 边界)----------------
  38. assert.equal(MAX_PARAGRAPH_WORDS, 300)
  39. assert.equal(hasOverlongParagraph(''), false)
  40. assert.equal(hasOverlongParagraph(words(299)), false)
  41. assert.equal(hasOverlongParagraph(words(300)), false)
  42. assert.equal(hasOverlongParagraph(words(301)), true)
  43. // 多段:只要有一段超标就 true
  44. assert.equal(hasOverlongParagraph(`${words(10)}\n\n${words(301)}`), true)
  45. assert.equal(hasOverlongParagraph(`${words(200)}\n\n${words(200)}`), false)
  46. // 单 \n 也是段界,故 200+200 不算一段 400
  47. assert.equal(hasOverlongParagraph(`${words(200)}\n${words(200)}`), false)
  48. // ---------------- shouldPromptOnPaste ----------------
  49. assert.equal(PASTE_PROMPT_MIN_WORDS, 50)
  50. // spec §3.2 step 1 只有两条:原内容 trim 后为空,且新内容 > 50 词。
  51. assert.equal(shouldPromptOnPaste('', words(60)), true) // 空框粘贴长文 → 弹
  52. assert.equal(shouldPromptOnPaste(' ', words(51)), true) // 纯空白算空框;51 词过阈值
  53. assert.equal(shouldPromptOnPaste('\n\n \n', words(60)), true) // 只剩换行的框同样算空
  54. assert.equal(shouldPromptOnPaste('', words(50)), false) // 恰好 50 词不弹(严格 >)
  55. assert.equal(shouldPromptOnPaste('', words(40)), false) // 短文不弹
  56. assert.equal(shouldPromptOnPaste(words(300), words(400)), false) // 原文非空 → 不打断编辑
  57. assert.equal(shouldPromptOnPaste('x', words(400)), false) // 哪怕只有一个字符也不算空框
  58. // 粘贴内容自带空行照样提示:带 \n\n ≠ 分得适合朗读(三段各 400 词就是反例)
  59. assert.equal(shouldPromptOnPaste('', `${words(30)}\n\n${words(30)}`), true)
  60. assert.equal(shouldPromptOnPaste('', `${words(400)}\n\n${words(400)}`), true)
  61. // enspeak 的 sentenceCount>=2 条件已弃用:无标点长文照样提示
  62. assert.equal(shouldPromptOnPaste('', words(60).replace(/\./g, '')), true)
  63. console.log('article auto-format rules OK')