ความคืบหน้างาน ไฟล์ต้นฉบับ

tracker/e2e/task-brief-check.mjs

// Read-only browser checks for task briefs.
import {pathToFileURL} from 'node:url';
const [chrome,base,out,modulePath='playwright']=process.argv.slice(2);
if(!chrome||!base||!out)throw Error('Usage: node task-brief-check.mjs <chrome> <base-url> <output-dir> [playwright-module]');
const {chromium}=await import(modulePath.startsWith('/')?pathToFileURL(modulePath).href:modulePath);
const browser=await chromium.launch({executablePath:chrome,headless:true,args:['--no-sandbox']});
let passed=0;const check=(v,m)=>{if(!v)throw Error(m);passed++;console.log('PASS '+m)};
try{
 const p=await browser.newPage({viewport:{width:1440,height:1000}});await p.route('https://fonts.googleapis.com/**',r=>r.abort());await p.route('https://fonts.gstatic.com/**',r=>r.abort());
 let total=0;
 for(const id of ['T-203','T-303','T-304','T-501','T-502','T-503','T-504','T-505','T-506','T-601','T-602','T-701','T-702']){
  const response=await p.goto(base+'/tasks/'+id,{waitUntil:'domcontentloaded'});if(response.status()!==200||!await p.locator('#h-task-brief').count()||!await p.locator('.task-deliverables li').count()||!await p.locator('.task-question-list>li').count())throw Error('Missing brief '+id);total+=await p.locator('.task-question-list>li').count();
 }
 check(total===39,'all 13 pending tasks expose their 39 stakeholder questions and deliverables');
 await p.goto(base+'/tasks/T-303',{waitUntil:'domcontentloaded'});check(await p.locator('.task-question-list>li').count()===5,'T-303 shows five specific hosting questions');
 check(await p.locator('.task-question-state').allTextContents().then(s=>s.every(x=>x==='รอข้อมูล')),'unresolved hosting questions remain explicitly open');
 check(await p.locator('.task-question-list').textContent().then(t=>t.includes('ผู้ที่ควรตอบ')&&t.includes('ข้อมูลนี้ใช้เพื่อ')&&t.includes('Infrastructure')&&t.includes('Project owner')),'questions identify intended respondents and decision impact');
 check(await p.locator('.task-dependency').allTextContents().then(ids=>ids.includes('T-301')&&ids.includes('T-302')),'dependency tasks are visible as navigable references');
 check(await p.locator('.task-proposed-owner').textContent().then(t=>t.includes('รอยืนยันผู้รับผิดชอบจริง')),'proposed ownership is distinguished from an actual assignment');
 await p.locator('#h-task-brief').scrollIntoViewIfNeeded();await p.screenshot({path:out+'/task-303-brief.png'});
 await p.setViewportSize({width:390,height:844});await p.click('[data-theme-choice=dark]');await p.locator('#h-task-questions').scrollIntoViewIfNeeded();check(await p.evaluate(()=>document.documentElement.scrollWidth<=innerWidth+2),'task questions fit a mobile viewport in dark mode');await p.screenshot({path:out+'/task-303-mobile.png'});
 await p.goto(base+'/tasks/T-302',{waitUntil:'domcontentloaded'});check(await p.locator('.task-summary').textContent().then(t=>t.includes('SQL Server')&&!t.includes('แนะนำ PostgreSQL')),'database summary reflects the confirmed SQL Server decision');
 await p.goto(base+'/tasks/T-102',{waitUntil:'domcontentloaded'});check(await p.locator('#h-task-brief').count()===1,'completed historical tasks show explanatory context');

 console.log(passed+' checks passed');
}finally{await browser.close()}