このコミットが含まれているのは:
@@ -0,0 +1,112 @@
|
||||
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import path from 'node:path'
|
||||
import { afterAll, afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
import { generateHelpAssets } from './help-assets'
|
||||
|
||||
import type { ArticleMeta, HelpChange, HelpSearchEntry, HelpTitleIndex } from '@/lib/help'
|
||||
|
||||
vi.hoisted (() => {
|
||||
vi.stubGlobal ('Uint8Array', Object.getPrototypeOf (new TextEncoder ().encode ('')).constructor)
|
||||
})
|
||||
afterAll (() => vi.unstubAllGlobals ())
|
||||
|
||||
|
||||
const readAsset = <T,> (modules: Map<string, string>, name: string): T => {
|
||||
const source = modules.get (`virtual:help/${ name }`)!
|
||||
const argument = source.slice ('export default JSON.parse('.length, -1)
|
||||
return JSON.parse (JSON.parse (argument))
|
||||
}
|
||||
|
||||
|
||||
describe ('Build-time Help assets', () => {
|
||||
let root: string
|
||||
|
||||
beforeEach (async () => {
|
||||
root = await mkdtemp (path.join (tmpdir (), 'btrc-help-assets-'))
|
||||
})
|
||||
|
||||
afterEach (async () => {
|
||||
await rm (root, { recursive: true, force: true })
|
||||
})
|
||||
|
||||
const writeArticle = async (
|
||||
identifier: string,
|
||||
editions: Record<string, string>,
|
||||
current = '2026-10-02.10',
|
||||
) => {
|
||||
const articleRoot = path.join (root, identifier)
|
||||
const editionRoot = path.join (articleRoot, 'editions', 'ja')
|
||||
await mkdir (editionRoot, { recursive: true })
|
||||
await writeFile (path.join (articleRoot, 'article.yml'), [
|
||||
`identifier: ${ identifier }`, 'canonical_language: ja', 'title: stale YAML title',
|
||||
'languages:', ' ja:', ` current_edition: ${ current }`].join ('\n'))
|
||||
await Promise.all (Object.entries (editions).map (([edition, source]) =>
|
||||
writeFile (path.join (editionRoot, `${ edition }.mdx`), source)))
|
||||
}
|
||||
|
||||
it ('generates current search and ordered published history from each edition H1', async () => {
|
||||
await writeArticle ('guide', {
|
||||
'2026-10-01': '# 旧ホーム\n\n過去本文',
|
||||
'2026-10-02': '# ホーム\n\n最初の同日版',
|
||||
'2026-10-02.2': '# ホーム\n\n第二版',
|
||||
'2026-10-02.3': '# ホーム\n\n第三版',
|
||||
'2026-10-02.10': '\uFEFF# 最新ホーム\r\n\r\n現在本文\r\n# 別見出し',
|
||||
'2026-10-02.11': '# 未公開\n\n検索に含めない' })
|
||||
const { modules } = await generateHelpAssets (root)
|
||||
const articles = readAsset<Record<string, ArticleMeta>> (modules, 'articles')
|
||||
expect (articles.guide.languages.ja).toEqual ({
|
||||
currentEdition: '2026-10-02.10', publishedEditions: [
|
||||
'2026-10-01', '2026-10-02', '2026-10-02.2', '2026-10-02.3', '2026-10-02.10'] })
|
||||
expect (readAsset<Record<string, HelpSearchEntry[]>> (modules, 'search').ja).toEqual ([{
|
||||
identifier: 'guide', edition: '2026-10-02.10', title: '最新ホーム',
|
||||
body: '\r\n現在本文\r\n# 別見出し' }])
|
||||
const changes = readAsset<Record<string, HelpChange[]>> (modules, 'changes').ja
|
||||
expect (changes.map (row => [row.edition, row.title, row.pred, row.change, row.date]))
|
||||
.toEqual ([
|
||||
['2026-10-02.10', '最新ホーム', '2026-10-02.3', 'update', '2026-10-02'],
|
||||
['2026-10-02.3', 'ホーム', '2026-10-02.2', 'update', '2026-10-02'],
|
||||
['2026-10-02.2', 'ホーム', '2026-10-02', 'update', '2026-10-02'],
|
||||
['2026-10-02', 'ホーム', '2026-10-01', 'update', '2026-10-02'],
|
||||
['2026-10-01', '旧ホーム', null, 'new', '2026-10-01']])
|
||||
const titles = readAsset<Record<string, HelpTitleIndex>> (modules, 'titles').ja
|
||||
expect (titles).toEqual ({ '旧ホーム': ['guide'], 'ホーム': ['guide'],
|
||||
'最新ホーム': ['guide'] })
|
||||
expect (modules.get ('virtual:help/loaders')).not.toContain ('2026-10-02.11')
|
||||
expect (modules.get ('virtual:help/loaders')).toContain ('2026-10-02.10.mdx?raw')
|
||||
expect (modules.get ('virtual:help/search')).not.toContain ('import(')
|
||||
})
|
||||
|
||||
it ('retains ambiguous title identifiers and normalises width and case', async () => {
|
||||
await writeArticle ('alpha', { '2026-10-02': '# HOME\n本文' }, '2026-10-02')
|
||||
await writeArticle ('beta', { '2026-10-02': '# Home\n本文' }, '2026-10-02')
|
||||
const { modules } = await generateHelpAssets (root)
|
||||
expect (readAsset<Record<string, HelpTitleIndex>> (modules, 'titles').ja.home)
|
||||
.toEqual (['alpha', 'beta'])
|
||||
expect (readAsset<Record<string, HelpChange[]>> (modules, 'changes').ja
|
||||
.map (row => row.identifier)).toEqual (['alpha', 'beta'])
|
||||
})
|
||||
|
||||
it ('rejects missing current files during generation', async () => {
|
||||
await writeArticle ('guide', { '2026-10-02': '# ホーム' })
|
||||
await expect (generateHelpAssets (root)).rejects.toThrow ('Missing current Help edition')
|
||||
})
|
||||
|
||||
it.each (['本文\n# 遅い見出し', '# \n本文']) (
|
||||
'rejects missing leading H1 at build time: %s', async source => {
|
||||
await writeArticle ('guide', { '2026-10-02.10': source })
|
||||
await expect (generateHelpAssets (root)).rejects.toThrow ('Missing leading Help H1')
|
||||
})
|
||||
|
||||
it ('rejects absent canonical metadata instead of generating incomplete assets', async () => {
|
||||
await writeArticle ('guide', { '2026-10-02.10': '# ホーム' })
|
||||
await writeFile (path.join (root, 'guide', 'article.yml'),
|
||||
'identifier: guide\nlanguages:\n ja:\n current_edition: 2026-10-02.10')
|
||||
await expect (generateHelpAssets (root)).rejects.toThrow ('Invalid Help metadata')
|
||||
})
|
||||
|
||||
it ('rejects an empty Help checkout', async () => {
|
||||
await expect (generateHelpAssets (root)).rejects.toThrow ('No Help articles found')
|
||||
})
|
||||
})
|
||||
新しいイシューから参照
ユーザーをブロックする