diff --git a/test/issue-update.test.js b/test/issue-update.test.js index 9de80bd..0dc179d 100644 --- a/test/issue-update.test.js +++ b/test/issue-update.test.js @@ -327,3 +327,15 @@ test('總覽網頁與其他欄位可以一次送出', async (t) => { assert.match(patches[0].body.body, /圖解版總覽/); assert.equal(patches[0].body.due_date, '2026-09-24T00:00:00Z'); }); + +test('網址夾帶上一次寫回的提醒文字時擋下,免得那句話重複兩次', async (t) => { + const stub = await withStub(t); + + const { json } = await run( + ['--overview-url', 'https://example.com/a(此連結預設為私有,組織外無法開啟)'], + stub, + ); + + assert.equal(json.error.code, 'BAD_OVERVIEW_URL'); + assert.equal(stub.requests.length, 0); +}); diff --git a/test/overview-artifact.test.js b/test/overview-artifact.test.js index 8f24b5d..846888f 100644 --- a/test/overview-artifact.test.js +++ b/test/overview-artifact.test.js @@ -8,6 +8,8 @@ import { readPrompt, readTemplateFile } from './helpers/prompt-doc.js'; const template = readTemplateFile('overview-artifact.html'); const planPrompt = readPrompt('sdlc-plan'); const analyzePrompt = readPrompt('sdlc-analyze'); +/** 只看產生總覽那一步,避免拿整份正本的任何一處來充數 */ +const planStep = planPrompt.slice(planPrompt.indexOf('### 6.'), planPrompt.indexOf('### 7.')); /** 模板要填的欄位 */ const PLACEHOLDERS = ['標題', '來源議題', '總覽', '目標', '流程圖', '工作包全景', '頁尾']; @@ -41,10 +43,19 @@ test('深色模式不靠手動切換也能用', () => { }); test('mermaid 圖有被實際渲染,不是把原始碼丟給讀者看', () => { - assert.match(template, /mermaid/); + // 只比對「有出現 mermaid 字樣」會被 CDN 網址矇混過去,要驗到真的有初始化 + const script = template.match(//)?.[0] ?? ''; + assert.match(script, /mermaid\.initialize\(/); assert.match(template, /class="mermaid"/); }); +test('模板裡唯一的腳本就是畫圖那一段,沒有夾帶其他邏輯', () => { + // AGENTS.md 說模板不含邏輯,這份是唯一的例外,例外要維持在最小範圍 + const scripts = template.match(//g) ?? []; + assert.equal(scripts.length, 1); + assert.equal(/\bfetch\(|localStorage|document\.cookie|XMLHttpRequest/.test(scripts[0]), false); +}); + test('工作包全景是整段佔位,規劃階段才填得了空字串', () => { // 規劃階段沒有工作包,整段要能消失,所以佔位不可以被包在寫死的 section 裡 const line = template.split('\n').find((l) => l.includes('{{工作包全景}}')); @@ -54,22 +65,22 @@ test('工作包全景是整段佔位,規劃階段才填得了空字串', () => // ── sdlc-plan 的步驟 ─────────────────────────────────────────────── test('規劃正本交代了產生總覽與寫回網址', () => { - assert.match(planPrompt, /templates\/overview-artifact\.html/); - assert.match(planPrompt, /--overview-url/); - assert.match(planPrompt, /issue-update/); + assert.match(planStep, /templates\/overview-artifact\.html/); + assert.match(planStep, /--overview-url/); + assert.match(planStep, /issue-update/); }); test('規劃正本說明流程圖填進模板時不帶圍欄', () => { - assert.match(planPrompt, /不\*\*含\*\*圍欄|\*\*不含\*\*圍欄/); + assert.match(planStep, /不\*\*含\*\*圍欄|\*\*不含\*\*圍欄/); }); test('規劃階段沒有工作包,正本要說清楚全景填空字串', () => { - assert.match(planPrompt, /\{\{工作包全景\}\}/); - assert.match(planPrompt, /填空字串/); + assert.match(planStep, /\{\{工作包全景\}\}/); + assert.match(planStep, /填空字串/); }); test('正本說明這份網頁是給非技術的人看的', () => { - assert.match(planPrompt, /非技術/); + assert.match(planStep, /非技術/); }); // ── sdlc-analyze 的步驟 ──────────────────────────────────────────── @@ -106,10 +117,10 @@ test('兩份正本都指名同一支腳本寫回網址', () => { }); test('規劃正本說明連結會自動附上私有提醒', () => { - assert.match(planPrompt, /私有/); - assert.match(planPrompt, /組織外/); + assert.match(planStep, /私有/); + assert.match(planStep, /組織外/); }); test('規劃正本強調議題原本的白話總覽不被取代', () => { - assert.match(planPrompt, /網頁是補充,不是取代/); + assert.match(planStep, /網頁是補充,不是取代/); });