|
| 1 | +/** Content Graph adapter + generator integration tests (#1157). */ |
| 2 | +import { assert, assertEquals } from '@std/assert'; |
| 3 | +import { |
| 4 | + extractMarkdownReferences, |
| 5 | + extractRoadmapTimeline, |
| 6 | + scanPublicRoutes, |
| 7 | +} from './content-graph-adapters.ts'; |
| 8 | +import { generateContentGraphJson } from '../generate-content-graph.ts'; |
| 9 | +import type { ContentGraph } from './content-graph.ts'; |
| 10 | + |
| 11 | +Deno.test('extractMarkdownReferences: classifies internal links, skips external and fragments', () => { |
| 12 | + const content = [ |
| 13 | + 'See [the guide](/guide/getting-started) and [localized](/zh/guide/api#x).', |
| 14 | + 'Relative [peer](./other.md) and [translation](other.zh.md) plus [anchor](#top).', |
| 15 | + 'External [site](https://example.com/docs) and [mail](mailto:a@b.c) are skipped.', |
| 16 | + '[ref]: /architecture/islands', |
| 17 | + ].join('\n'); |
| 18 | + const references = extractMarkdownReferences( |
| 19 | + content, |
| 20 | + (target) => |
| 21 | + target.startsWith('/') |
| 22 | + ? { kind: 'route', target } |
| 23 | + : { kind: 'entry', target: `article:guide/${target.replace(/\.mdx?$/, '')}` }, |
| 24 | + ); |
| 25 | + assertEquals(references, [ |
| 26 | + { kind: 'route', target: '/guide/getting-started', line: 1 }, |
| 27 | + { kind: 'route', target: '/zh/guide/api', line: 1 }, |
| 28 | + { kind: 'entry', target: 'article:guide/./other', line: 2 }, |
| 29 | + { kind: 'entry', target: 'article:guide/other.zh', line: 2 }, |
| 30 | + { kind: 'route', target: '/architecture/islands', line: 4 }, |
| 31 | + ]); |
| 32 | +}); |
| 33 | + |
| 34 | +Deno.test('extractRoadmapTimeline: reads the bilingual timeline through the TS AST', () => { |
| 35 | + const source = ` |
| 36 | +const entries: Record<'en' | 'zh', TimelineEntry[]> = { |
| 37 | + en: [ |
| 38 | + { version: 'v0.44.0-beta.1', theme: 'qualification', copy: 'English copy.', state: 'next', stamp: 'CURRENT', status: 'prerelease' }, |
| 39 | + ], |
| 40 | + zh: [ |
| 41 | + { version: 'v0.44.0-beta.1', theme: '限定', copy: '中文文案。', state: 'next', stamp: 'CURRENT', status: 'prerelease' }, |
| 42 | + ], |
| 43 | +}; |
| 44 | +`; |
| 45 | + const timeline = extractRoadmapTimeline(source); |
| 46 | + assertEquals(Object.keys(timeline).sort(), ['en', 'zh']); |
| 47 | + assertEquals(timeline.en.length, 1); |
| 48 | + assertEquals(timeline.en[0].version, 'v0.44.0-beta.1'); |
| 49 | + assertEquals(timeline.en[0].stamp, 'CURRENT'); |
| 50 | + assertEquals(timeline.zh[0].copy, '中文文案。'); |
| 51 | + assertEquals(timeline.en[0].line > 0, true); |
| 52 | +}); |
| 53 | + |
| 54 | +Deno.test('scanPublicRoutes: discovers the real www route universe', async () => { |
| 55 | + const routes = await scanPublicRoutes(); |
| 56 | + for (const expected of ['/guide/getting-started', '/roadmap', '/apilist', '/changelog']) { |
| 57 | + assert(routes.includes(expected), `missing route ${expected}`); |
| 58 | + } |
| 59 | + assert(routes.every((route) => !route.includes(':')), 'dynamic segments must be excluded'); |
| 60 | +}); |
| 61 | + |
| 62 | +Deno.test('generateContentGraphJson: real repo sources produce a valid deterministic graph', async () => { |
| 63 | + const first = await generateContentGraphJson(); |
| 64 | + const second = await generateContentGraphJson(); |
| 65 | + assertEquals(first, second); |
| 66 | + |
| 67 | + const graph = JSON.parse(first) as ContentGraph; |
| 68 | + const ids = new Set(graph.entries.map((entry) => entry.id)); |
| 69 | + // Every adapter contributes: markdown, public API data, compiler metadata, |
| 70 | + // roadmap and release truth. |
| 71 | + assert(ids.has('article:guide/getting-started:en'), 'markdown adapter missing'); |
| 72 | + assert(ids.has('api:@openelement/element'), 'api adapter missing'); |
| 73 | + assert(ids.has('element:open-dialog'), 'compiler metadata adapter missing'); |
| 74 | + assert(ids.has('roadmap:en:0'), 'roadmap adapter missing'); |
| 75 | + assert(ids.has('release:state'), 'release adapter missing'); |
| 76 | + |
| 77 | + // Locale pairs are symmetric and translated. |
| 78 | + const en = graph.entries.find((entry) => entry.id === 'article:guide/getting-started:en'); |
| 79 | + const zh = graph.entries.find((entry) => entry.id === 'article:guide/getting-started:zh'); |
| 80 | + assert(en && zh, 'guide locale pair missing'); |
| 81 | + assertEquals(en.alternates, [{ locale: 'zh', id: 'article:guide/getting-started:zh' }]); |
| 82 | + assertEquals(zh.alternates, [{ locale: 'en', id: 'article:guide/getting-started:en' }]); |
| 83 | +}); |
0 commit comments