translation-pairing.spec.ts 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. /** Regression tests for the bilingual corpus scope and structural signature. */
  2. import { describe, expect, it } from 'vitest'
  3. import {
  4. isTranslationScopeFile,
  5. parseTranslationMarkdown,
  6. parseTranslationPairingManifest,
  7. translationStructureDiff,
  8. translationStructureSignature,
  9. } from './translation-pairing.ts'
  10. function signature(markdown: string) {
  11. return translationStructureSignature(parseTranslationMarkdown(markdown), 'counterpart.zh.md')
  12. }
  13. describe('translation pairing manifest', () => {
  14. it('accepts an exclusions-only manifest', () => {
  15. expect(parseTranslationPairingManifest(JSON.stringify({
  16. excluded: ['docs/generated/'],
  17. }))).toEqual({
  18. excluded: ['docs/generated/'],
  19. })
  20. })
  21. it.each([
  22. ['required', ['packages/README.md']],
  23. ['requiredClasses', ['readme']],
  24. ['requiredSince', '2026-07-14'],
  25. ] as const)('rejects obsolete policy field %s instead of accepting an inert requirement', (field, value) => {
  26. expect(() => parseTranslationPairingManifest(JSON.stringify({
  27. excluded: [],
  28. [field]: value,
  29. }))).toThrow(`unsupported field(s): ${field}; every in-scope document is required`)
  30. })
  31. it('rejects a missing or non-string exclusion list', () => {
  32. expect(() => parseTranslationPairingManifest('{}')).toThrow('excluded must be an array of strings')
  33. expect(() => parseTranslationPairingManifest(JSON.stringify({
  34. excluded: [42],
  35. }))).toThrow('excluded must be an array of strings')
  36. })
  37. })
  38. describe('translation scope discovery', () => {
  39. it.each([
  40. 'README.md',
  41. 'apps/cli/README.md',
  42. 'future/subtree/readme.md',
  43. 'packages/example/README.zh.md',
  44. 'native/example/README.i18n.yaml',
  45. '.agents/notes/proposed/feature.md',
  46. 'docs/guide.md',
  47. 'python/guide.md',
  48. ])('includes %s', (file) => {
  49. expect(isTranslationScopeFile(file)).toBe(true)
  50. })
  51. it.each([
  52. 'packages/example/guide.md',
  53. 'examples/tutorial.md',
  54. 'website/reference.md',
  55. 'packages/example/README.txt',
  56. 'vendor/example/README.md',
  57. 'packages/example/node_modules/dependency/README.md',
  58. 'packages/example/lib/README.md',
  59. 'coverage/report/README.md',
  60. 'python/sdk-runtime/src/deepseek_harness_runtime/runtime/dsh-jsonrpc-agent-macos-arm64/README.md',
  61. 'python/sdk-runtime/src/deepseek_harness_runtime/runtime/node/README.md',
  62. ])('excludes non-source or non-README path %s', (file) => {
  63. expect(isTranslationScopeFile(file)).toBe(false)
  64. })
  65. })
  66. describe('translation structural signature', () => {
  67. it('accepts matching list kinds, starts, and item counts', () => {
  68. const source = signature('3. One\n4. Two\n\n- A\n- B\n')
  69. const counterpart = signature('3. 一\n4. 二\n\n- 甲\n- 乙\n')
  70. expect(translationStructureDiff(source, counterpart)).toEqual([])
  71. })
  72. it('rejects an altered ordered-list start', () => {
  73. const source = signature('3. One\n4. Two\n\n- A\n- B\n')
  74. const counterpart = signature('1. 一\n2. 二\n\n- 甲\n- 乙\n')
  75. expect(translationStructureDiff(source, counterpart)).toEqual([
  76. 'list (kind, start, item count) #1 diverges between the pair: "ordered:start=3:items=2" vs "ordered:start=1:items=2"',
  77. ])
  78. })
  79. it('rejects a missing list item', () => {
  80. const source = signature('- A\n- B\n')
  81. const counterpart = signature('- 甲\n')
  82. expect(translationStructureDiff(source, counterpart)).toEqual([
  83. 'list (kind, start, item count) #1 diverges between the pair: "bullet:items=2" vs "bullet:items=1"',
  84. ])
  85. })
  86. it('rejects altered table row or column counts', () => {
  87. const source = signature('| A | B |\n|---|---|\n| 1 | 2 |\n| 3 | 4 |\n')
  88. const counterpart = signature('| 甲 | 乙 |\n|---|---|\n| 一 | 二 |\n')
  89. expect(translationStructureDiff(source, counterpart)).toEqual([
  90. 'table (row x column count) #1 diverges between the pair: "3x2" vs "2x2"',
  91. ])
  92. })
  93. })