diff --git a/test/delivery-types-assets.test.js b/test/delivery-types-assets.test.js new file mode 100644 index 0000000..0788483 --- /dev/null +++ b/test/delivery-types-assets.test.js @@ -0,0 +1,55 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { readReference, readTemplate } from './helpers/prompt-doc.js'; + +const rules = readReference('delivery-types'); +const template = readTemplate('requirement-issue'); +const TYPES = [ + '需求描述概要', + 'WBS(工作分解結構)', + '流程圖', + '甘特圖', + 'PERT 圖', + '關鍵路徑圖', + 'API 契約文件', +]; + +function sectionOf(text, heading) { + const lines = text.split('\n'); + const start = lines.findIndex((line) => line.replace(/^### (?:\d+\. )?/, '') === heading); + assert.notEqual(start, -1, `規則正本缺少「${heading}」`); + const rest = lines.slice(start + 1); + const end = rest.findIndex((line) => line.startsWith('### ')); + return (end === -1 ? rest : rest.slice(0, end)).join('\n'); +} + +test('需求模板以文件取代流程圖,且保留固定段落順序', () => { + const headings = [...template.matchAll(/^## (.+)$/gm)].map((match) => match[1]); + assert.deepEqual(headings, [ + '總覽', '背景', '目標', '非目標', '領域名詞表', '文件', '驗收標準', '影響範圍', '未決事項', + ]); + assert.match(template, /\{\{文件\}\}/); + assert.equal(template.includes('## 流程圖'), false); +}); + +test('規則正本逐一交代七種文件的必要內容、產出位置與 ELI5 變體', () => { + assert.match(rules, /^## 七種交付類型$/m); + for (const type of TYPES) { + const section = sectionOf(rules, type); + assert.match(section, /必要內容/); + assert.match(section, /產出位置/); + assert.match(section, /ELI5 變體/); + } +}); + +test('API 契約規則禁止寫入目標專案,且摘要可由來源重產', () => { + const section = sectionOf(rules, 'API 契約文件'); + assert.match(section, /禁止把 API 契約文件寫入目標專案 repo/); + assert.match(rules, /摘要可由同一份來源重新產生/); +}); + +test('交付文件共通規則保留逐一確認與預覽能力分流', () => { + assert.match(rules, /逐一確認/); + assert.match(rules, /可開啟、可分享的預覽/); + assert.match(rules, /沒有預覽能力時依使用者確認的方式交付/); +}); diff --git a/test/issue-extract.test.js b/test/issue-extract.test.js index 2aa0d37..d1d9dec 100644 --- a/test/issue-extract.test.js +++ b/test/issue-extract.test.js @@ -40,7 +40,7 @@ const FULL_BODY = `## 總覽 | 需求議題 | 描述一項需求的 Gitea issue | | 工作包 | 從需求拆出的可獨立完成的單位 | -## 流程圖 +## 文件 \`\`\`mermaid flowchart TD @@ -108,7 +108,7 @@ test('抽出契約上的每一個欄位', async (t) => { assert.equal(code, 0); assert.deepEqual(Object.keys(json.data).sort(), [ 'index', 'labels', 'title', 'url', - '影響範圍', '未決事項', '未處理留言數', '流程圖', + '影響範圍', '未決事項', '未處理留言數', '文件', '目標', '總覽', '背景', '名詞表', '非目標', '驗收標準', ].sort()); }); @@ -159,14 +159,14 @@ test('名詞表解析成 term 與 def,表頭與分隔列不算一筆', async ( ]); }); -test('流程圖原樣帶出,連圍欄一起', async (t) => { +test('文件原樣帶出,連圍欄一起', async (t) => { const stub = await withStub(t); const { json } = await run([], stub); - assert.match(json.data.流程圖, /^```mermaid/); - assert.match(json.data.流程圖, /flowchart TD/); - assert.match(json.data.流程圖, /```$/); + assert.match(json.data.文件, /^```mermaid/); + assert.match(json.data.文件, /flowchart TD/); + assert.match(json.data.文件, /```$/); }); // ── 模板變體 ─────────────────────────────────────────────────────── @@ -192,7 +192,7 @@ test('缺段落回傳空值而不是報錯', async (t) => { assert.equal(code, 0); assert.equal(json.data.總覽, '只有總覽的議題。'); assert.equal(json.data.背景, ''); - assert.equal(json.data.流程圖, ''); + assert.equal(json.data.文件, ''); assert.deepEqual(json.data.目標, []); assert.deepEqual(json.data.名詞表, []); assert.deepEqual(json.data.驗收標準, []); @@ -354,13 +354,13 @@ test('--dry-run 印出將發出的請求,且不碰 Gitea', async (t) => { // ── 圍欄與表格的邊界(皆為 code review 抓出的實際缺陷,這裡封住回頭路)──── test('~~~ 圍欄裡的井字號不是標題', async (t) => { - const body = '## 流程圖\n\n~~~\n## 這不是標題\n~~~\n\n## 目標\n\n- 真的目標\n'; + const body = '## 文件\n\n~~~\n## 這不是標題\n~~~\n\n## 目標\n\n- 真的目標\n'; const stub = await withStub(t, {}, { body }); const { json } = await run([], stub); assert.deepEqual(json.data.目標, ['真的目標']); - assert.match(json.data.流程圖, /這不是標題/, '圍欄內容原樣留在流程圖段落裡'); + assert.match(json.data.文件, /這不是標題/, '圍欄內容原樣留在文件段落裡'); }); test('圍欄裡的減號不是清單項', async (t) => { @@ -382,14 +382,14 @@ test('圍欄要同種標記才算關閉,混用時不會提早收尾', async (t }); test('圍欄沒關就到結尾時,其後內容算在圍欄內(與 markdown 渲染一致)', async (t) => { - const body = '## 流程圖\n\n```mermaid\nflowchart TD\n\n## 驗收標準\n\n- 被圍欄吃掉\n'; + const body = '## 文件\n\n```mermaid\nflowchart TD\n\n## 驗收標準\n\n- 被圍欄吃掉\n'; const stub = await withStub(t, {}, { body }); const { code, json } = await run([], stub); assert.equal(code, 0, '不該炸掉,只是內容歸屬不同'); assert.deepEqual(json.data.驗收標準, []); - assert.match(json.data.流程圖, /驗收標準/); + assert.match(json.data.文件, /驗收標準/); }); test('名詞表的欄位內容可以有逸脫的直線', async (t) => {