Files
Pier_v1/scripts/review-design-b.cjs
T
ypshimandClaude Haiku 5.5 da7b13a568 feat(ui): 기둥·말뚝 통합 입력 · 현장타설 화면 · 헤더 한 줄 정리
- 기둥·말뚝 입력을 한 카드로 통합(공통 입력, 구간별 예외 보존, 횡철근 표)
- 현장타설 실배근 · 삽도 · 수량을 화면에 연결하고 프로젝트 저장 · 복원
- 상단 헤더를 한 줄로 축약(상태 축약, 파일 메뉴, 말줄임)
- 디자인 검토 반영(가져오기 범위, ③ 모델 높이 · 접기, 검토 항목 이동)
- 화면 검증 스크립트(verify-unified-input · verify-design-fixes 등) 보관

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
2026-11-06 13:15:01 +09:00

53 lines
6.3 KiB
JavaScript

// Independent design evidence: only the newly created Chrome tab is modified.
const fs = require('node:fs');
const path = require('node:path');
const [input, out] = process.argv.slice(2);
const report = { views: [], console: [], exceptions: [], started: new Date().toISOString() };
const sleep = ms => new Promise(r => setTimeout(r, ms));
let ws;
async function main() {
const target = await (await fetch('http://127.0.0.1:9444/json/new?http://127.0.0.1:5173/', { method: 'PUT' })).json();
report.targetId = target.id;
ws = new WebSocket(target.webSocketDebuggerUrl);
await new Promise((r,j) => {ws.onopen=r; ws.onerror=j;});
let next=0; const pending=new Map();
ws.onmessage=({data})=> {const m=JSON.parse(data); if(m.id){const p=pending.get(m.id);if(p){clearTimeout(p.t);pending.delete(m.id);m.error?p.j(m.error):p.r(m.result);}} else if(m.method==='Runtime.consoleAPICalled')report.console.push({type:m.params.type,args:m.params.args.map(x=>x.value??x.description)});else if(m.method==='Runtime.exceptionThrown')report.exceptions.push(m.params.exceptionDetails);};
const send=(method,params={})=>new Promise((r,j)=>{const id=++next;const t=setTimeout(()=>j(new Error(method+' timeout')),20000);pending.set(id,{r,j,t});ws.send(JSON.stringify({id,method,params}));});
const ev=async expression=>{const r=await send('Runtime.evaluate',{expression,returnByValue:true,awaitPromise:true});if(r.exceptionDetails)throw new Error(r.exceptionDetails.text);return r.result.value;};
const until=async e=>{for(let n=0;n<100;n++){if(await ev(e))return;await sleep(150);}throw new Error('Missing state '+e);};
const click=async e=>ev(`(()=>{const n=${e};if(!n||n.disabled)throw new Error('Missing/disabled click');n.click();return true;})()`);
await send('Runtime.enable'); await send('Page.enable'); await send('DOM.enable');
await send('Emulation.setDeviceMetricsOverride',{width:1600,height:1000,deviceScaleFactor:1,mobile:false});
await until(`typeof window.loadBytesToApp==='function'`);
report.injectionPreflight=await ev(`(()=>{document.title='[Human] Design evidence B';let s=document.createElement('script');s.textContent='window.__designBMutation=true';document.head.append(s);return window.__designBMutation===true;})()`);
const measure=async name=>{
if(process.env.OVERLAY_URL) report.overlayInjection=await ev(`new Promise(resolve=>{document.title='[Human] Design evidence B';scrollTo(0,0);const s=document.createElement('script');s.src=${JSON.stringify(process.env.OVERLAY_URL)};s.onload=()=>setTimeout(()=>resolve({loaded:true}),2600);s.onerror=()=>resolve({loaded:false});document.head.append(s);})`);
await sleep(350);
const facts=await ev(`(()=>{const visible=e=>{const r=e.getBoundingClientRect();return r.width>0&&r.height>0&&getComputedStyle(e).visibility!=='hidden'};const rect=e=>{const r=e.getBoundingClientRect();return {x:r.x,y:r.y,width:r.width,height:r.height,right:r.right,bottom:r.bottom}};const controls=[...document.querySelectorAll('button,input,select,textarea')].filter(visible);const fields=controls.filter(e=>/INPUT|SELECT|TEXTAREA/.test(e.tagName));const named=e=>!!(e.labels?.length||e.getAttribute('aria-label')||e.getAttribute('aria-labelledby')||e.title);return {viewport:{w:innerWidth,h:innerHeight},doc:{w:document.documentElement.scrollWidth,h:document.documentElement.scrollHeight},text:document.body.innerText,controls:controls.map(e=>({tag:e.tagName,id:e.id,text:(e.innerText||e.getAttribute('aria-label')||e.value||'').slice(0,90),...rect(e),font:getComputedStyle(e).fontSize,disabled:e.disabled})),unnamedFields:fields.filter(e=>!named(e)).map(e=>({id:e.id,type:e.type,...rect(e)})),overflow:[...document.querySelectorAll('body *')].filter(visible).filter(e=>e.scrollWidth>e.clientWidth+2&&e.clientWidth>0).map(e=>({tag:e.tagName,id:e.id,cls:e.className,client:e.clientWidth,scroll:e.scrollWidth,overflowX:getComputedStyle(e).overflowX,...rect(e)})).slice(0,40),active:{id:document.activeElement.id,tag:document.activeElement.tagName},dialogs:[...document.querySelectorAll('dialog[open]')].map(rect)};})()`);
facts.name=name;report.views.push(facts);
const shot=await send('Page.captureScreenshot',{format:'png'});fs.writeFileSync(path.join(out,name+'.png'),Buffer.from(shot.data,'base64'));
};
await measure('01-landing-desktop');
await click(`document.querySelector('[data-card="excel"] button')`);
await ev(`document.getElementById('project-name').value='디자인 독립 검증 B'`);
const doc=await send('DOM.getDocument');const file=await send('DOM.querySelector',{nodeId:doc.root.nodeId,selector:'#file'});
await send('DOM.setFileInputFiles',{nodeId:file.nodeId,files:[input]});
await until(`!document.getElementById('dlg-apply').disabled`);
await measure('02-import-desktop');
await click(`document.getElementById('dlg-apply')`);
await click(`document.getElementById('nav-plan')`);
await click(`[...document.querySelectorAll('#plan-pier-items button')].find(b=>b.textContent.includes('교대')&&b.textContent.includes('A1'))`);
await measure('03-A1-plan-desktop');
await click(`document.getElementById('nav-rebar')`);
await click(`[...document.querySelectorAll('#rebar-pier-items button')].find(b=>b.textContent.includes('교대')&&b.textContent.includes('A1'))`);
await until(`document.querySelectorAll('#rebar-tabs button').length===6`);
await click(`[...document.querySelectorAll('#rebar-tabs button')].find(b=>b.textContent.startsWith('벽체'))`);
await measure('04-A1-rebar-desktop');
await send('Emulation.setDeviceMetricsOverride',{width:1280,height:800,deviceScaleFactor:1,mobile:false});
await measure('05-A1-rebar-laptop');
await send('Input.dispatchKeyEvent',{type:'keyDown',key:'Tab',code:'Tab',windowsVirtualKeyCode:9});await send('Input.dispatchKeyEvent',{type:'keyUp',key:'Tab',code:'Tab',windowsVirtualKeyCode:9});
report.keyboardFocus=await ev(`(()=>{const e=document.activeElement;const s=getComputedStyle(e);return {tag:e.tagName,id:e.id,text:e.innerText?.slice(0,80),outline:s.outline,focusVisible:e.matches(':focus-visible')}})()`);
report.result='COMPLETE';
}
fs.mkdirSync(out,{recursive:true});main().catch(e=>{report.result='PARTIAL';report.failure=e.stack;process.exitCode=1;}).finally(()=>{report.finished=new Date().toISOString();fs.writeFileSync(path.join(out,'browser.json'),JSON.stringify(report,null,2));console.log(JSON.stringify({result:report.result,views:report.views.length,failure:report.failure}));ws?.close();});