diff --git a/test/sdlc-feat-assets.test.js b/test/sdlc-feat-assets.test.js index 5ee051a..13e381d 100644 --- a/test/sdlc-feat-assets.test.js +++ b/test/sdlc-feat-assets.test.js @@ -11,7 +11,8 @@ import { assertNeutralPrompt, readPrompt } from './helpers/prompt-doc.js'; const prompt = readPrompt('sdlc-feat'); /** 各段的內容分開切,避免把別段的字樣誤認成這一段的規則 */ const phase1 = prompt.slice(prompt.indexOf('## 第一段'), prompt.indexOf('## 第二段')); -const phase2 = prompt.slice(prompt.indexOf('## 第二段'), prompt.indexOf('## 邊界')); +const phase2 = prompt.slice(prompt.indexOf('## 第二段'), prompt.indexOf('## 第三段')); +const phase3 = prompt.slice(prompt.indexOf('## 第三段'), prompt.indexOf('## 邊界')); test('正本平台中立,description 前綴正確', () => { assertNeutralPrompt(prompt, 'sdlc-feat'); @@ -180,3 +181,77 @@ test('邊界把第二段不做的事也列出來', () => { assert.match(boundary, /不提交、不開 PR、不停錶/); assert.match(boundary, /不改與待辦無關的程式碼/); }); + +// ── 第三段:提交與開立 PR ───────────────────────────────────────── + +test('第三段指名兩支腳本,順序為先提交再開 PR', () => { + const order = ['commit-split.js', 'pr-create.js']; + const positions = order.map((name) => phase3.indexOf(name)); + assert.equal(positions.every((p) => p >= 0), true, '兩支腳本都要被指名'); + assert.deepEqual([...positions].sort((a, b) => a - b), positions); +}); + +test('要等待辦全部勾完才進第三段', () => { + assert.match(phase3, /全部待辦都勾完之後才進這一段/); +}); + +test('--type 是程式碼那一批的類型,其餘由腳本自己認', () => { + assert.match(phase3, /測試、文件與設定檔\s*\n?由腳本自己認出來|由腳本自己認出來/); + assert.match(phase3, /不必也不能指定/); +}); + +test('--scope 什麼時候要給寫清楚了', () => { + assert.match(phase3, /只在某一批有多個檔案時才需要/); + assert.match(phase3, /單檔那批的 scope 就是檔名/); +}); + +test('commit 描述要用繁體中文,並交代夾雜英文的處理', () => { + assert.match(phase3, /描述用繁體中文/); + assert.match(phase3, /保留原文/); +}); + +test('跨兩個功能時要分兩次跑,且指名用哪個旗標做得到', () => { + assert.match(phase3, /分兩次跑/); + assert.match(phase3, /--files/, '光說「分兩次跑」而不說怎麼分,等於沒說'); + assert.match(phase3, /失去了分批的意義/); +}); + +test('PR 描述的八個段落都列出來,且標明順序不能換', () => { + for (const section of [ + '摘要', '需求議題', '工作包議題', '變更內容', + '設計重點', '解決的問題', '影響的功能', '測試結果', + ]) { + assert.match(phase3, new RegExp(`\\*\\*${section}\\*\\*`), `缺少段落說明:${section}`); + } + assert.match(phase3, /順序不能換/); +}); + +test('測試結果要放實際輸出,並交代沒有自動化測試時怎麼辦', () => { + assert.match(phase3, /放實際跑過的輸出/); + assert.match(phase3, /已測試通過/, '要指名這句被禁止的寫法'); + assert.match(phase3, /手動\s*\n?驗證步驟|手動驗證步驟/); + assert.match(phase3, /補真的內容/); +}); + +test('標題由腳本設為分支名,不另外指定', () => { + assert.match(phase3, /標題由腳本設為分支名/); + assert.match(phase3, /不必也不能另外指定/); +}); + +test('先開 PR 再停錶,且 PR 沒開成就不停錶', () => { + assert.match(phase3, /先開 PR 再停錶/); + assert.match(phase3, /沒開成就不停錶/); + assert.match(phase3, /工時要記在真的有做事的那段時間上/); +}); + +test('兩支腳本都要求先試跑', () => { + const dryRuns = phase3.match(/--dry-run/g) ?? []; + assert.ok(dryRuns.length >= 2, `兩支寫入型腳本各要先試跑,只找到 ${dryRuns.length} 處`); +}); + +test('邊界把第三段不做的事也列出來', () => { + const boundary = prompt.slice(prompt.indexOf('## 邊界')); + assert.match(boundary, /第三段不改任何一行程式碼/); + assert.match(boundary, /不把「已測試通過」這種空話/); + assert.match(boundary, /不代替使用者決定 commit 的類型與描述/); +});