feat: 누락 시나리오 22개 추가
- 게시판(2): board-management, board-test
- 고객센터(3): customer-inquiry, customer-faq, customer-event
- 판매관리(1): sales-site
- 구매관리(1): purchase-pricing
- 생산관리(3): production-dashboard, production-work-order, production-worker
- 출고관리(1): shipment-dispatch
- 회계관리(11): accounting-sales, accounting-purchase, accounting-cost,
accounting-financial, accounting-bill, accounting-bank-transaction,
accounting-card-history, accounting-receivable, accounting-expense-forecast,
accounting-bad-debt, accounting-daily-report
2026-02-03 09:08:18 +09:00
{
refactor: E2E 시나리오 전면 개선 (43파일)
- Phase 0: 미구현 모듈 시나리오 13개 삭제 (구매관리, 중복, 라우트 없음)
- Phase 2: Settings URL 불일치 수정 (position, attendance, vacation-policy, bank-account, account, notification)
- Phase 3-4: 비설정 시나리오 URL/메뉴/UI 수정 (inventory-status, receiving-management, price-management, customer-inquiry, shipment-management, sales-client, quality-certification, customer-notice, production-* 등)
- Phase 5-6: 복잡 시나리오 재작성 (draft-box 50→14스텝, department-add 18→10스텝, free-board 70→22스텝, crud-delete-freeboard 14→17스텝)
- 16개 disabled 시나리오 enabled 전환
- 비표준 액션(fillInModal, randomData, usePlaywrightNative 등) → step-executor 표준 액션으로 통일
2026-02-06 17:37:35 +09:00
"enabled" : true ,
feat: 누락 시나리오 22개 추가
- 게시판(2): board-management, board-test
- 고객센터(3): customer-inquiry, customer-faq, customer-event
- 판매관리(1): sales-site
- 구매관리(1): purchase-pricing
- 생산관리(3): production-dashboard, production-work-order, production-worker
- 출고관리(1): shipment-dispatch
- 회계관리(11): accounting-sales, accounting-purchase, accounting-cost,
accounting-financial, accounting-bill, accounting-bank-transaction,
accounting-card-history, accounting-receivable, accounting-expense-forecast,
accounting-bad-debt, accounting-daily-report
2026-02-03 09:08:18 +09:00
"id" : "production-work-order" ,
2026-03-01 19:08:58 +09:00
"name" : "작업지시 CRUD + 필드검증 + API확인: 생산관리" ,
"version" : "2.0.0" ,
feat: 누락 시나리오 22개 추가
- 게시판(2): board-management, board-test
- 고객센터(3): customer-inquiry, customer-faq, customer-event
- 판매관리(1): sales-site
- 구매관리(1): purchase-pricing
- 생산관리(3): production-dashboard, production-work-order, production-worker
- 출고관리(1): shipment-dispatch
- 회계관리(11): accounting-sales, accounting-purchase, accounting-cost,
accounting-financial, accounting-bill, accounting-bank-transaction,
accounting-card-history, accounting-receivable, accounting-expense-forecast,
accounting-bad-debt, accounting-daily-report
2026-02-03 09:08:18 +09:00
"screenshotPolicy" : {
2026-03-01 19:08:58 +09:00
"captureOnFail" : true ,
"captureOnPass" : false
feat: 누락 시나리오 22개 추가
- 게시판(2): board-management, board-test
- 고객센터(3): customer-inquiry, customer-faq, customer-event
- 판매관리(1): sales-site
- 구매관리(1): purchase-pricing
- 생산관리(3): production-dashboard, production-work-order, production-worker
- 출고관리(1): shipment-dispatch
- 회계관리(11): accounting-sales, accounting-purchase, accounting-cost,
accounting-financial, accounting-bill, accounting-bank-transaction,
accounting-card-history, accounting-receivable, accounting-expense-forecast,
accounting-bad-debt, accounting-daily-report
2026-02-03 09:08:18 +09:00
} ,
2026-03-01 19:08:58 +09:00
"description" : "생산관리 > 작업지시 관리 메뉴의 작업지시 CRUD 전체 흐름 + combobox/date picker + 상세 필드 대조 + API 검증" ,
feat: 누락 시나리오 22개 추가
- 게시판(2): board-management, board-test
- 고객센터(3): customer-inquiry, customer-faq, customer-event
- 판매관리(1): sales-site
- 구매관리(1): purchase-pricing
- 생산관리(3): production-dashboard, production-work-order, production-worker
- 출고관리(1): shipment-dispatch
- 회계관리(11): accounting-sales, accounting-purchase, accounting-cost,
accounting-financial, accounting-bill, accounting-bank-transaction,
accounting-card-history, accounting-receivable, accounting-expense-forecast,
accounting-bad-debt, accounting-daily-report
2026-02-03 09:08:18 +09:00
"baseUrl" : "https://dev.codebridge-x.com" ,
"menuNavigation" : {
"level1" : "생산관리" ,
"level2" : "작업지시 관리" ,
2026-02-03 13:25:38 +09:00
"expectedUrl" : "/production/work-orders" ,
"searchWithinParent" : true ,
"closeOtherMenus" : true
feat: 누락 시나리오 22개 추가
- 게시판(2): board-management, board-test
- 고객센터(3): customer-inquiry, customer-faq, customer-event
- 판매관리(1): sales-site
- 구매관리(1): purchase-pricing
- 생산관리(3): production-dashboard, production-work-order, production-worker
- 출고관리(1): shipment-dispatch
- 회계관리(11): accounting-sales, accounting-purchase, accounting-cost,
accounting-financial, accounting-bill, accounting-bank-transaction,
accounting-card-history, accounting-receivable, accounting-expense-forecast,
accounting-bad-debt, accounting-daily-report
2026-02-03 09:08:18 +09:00
} ,
"auth" : {
2026-03-01 19:08:58 +09:00
"role" : "admin"
feat: 누락 시나리오 22개 추가
- 게시판(2): board-management, board-test
- 고객센터(3): customer-inquiry, customer-faq, customer-event
- 판매관리(1): sales-site
- 구매관리(1): purchase-pricing
- 생산관리(3): production-dashboard, production-work-order, production-worker
- 출고관리(1): shipment-dispatch
- 회계관리(11): accounting-sales, accounting-purchase, accounting-cost,
accounting-financial, accounting-bill, accounting-bank-transaction,
accounting-card-history, accounting-receivable, accounting-expense-forecast,
accounting-bad-debt, accounting-daily-report
2026-02-03 09:08:18 +09:00
} ,
2026-02-03 13:25:38 +09:00
"testData" : {
"create" : {
"orderNumber" : "E2E_TEST_작업지시" ,
"quantity" : "500" ,
"memo" : "E2E 자동화 테스트 작업지시"
} ,
"update" : {
"quantity" : "600" ,
"memo" : "E2E 수정된 작업지시 메모"
}
} ,
feat: 누락 시나리오 22개 추가
- 게시판(2): board-management, board-test
- 고객센터(3): customer-inquiry, customer-faq, customer-event
- 판매관리(1): sales-site
- 구매관리(1): purchase-pricing
- 생산관리(3): production-dashboard, production-work-order, production-worker
- 출고관리(1): shipment-dispatch
- 회계관리(11): accounting-sales, accounting-purchase, accounting-cost,
accounting-financial, accounting-bill, accounting-bank-transaction,
accounting-card-history, accounting-receivable, accounting-expense-forecast,
accounting-bad-debt, accounting-daily-report
2026-02-03 09:08:18 +09:00
"steps" : [
{
"id" : 1 ,
2026-03-01 19:08:58 +09:00
"name" : "[생산관리 > 작업지시 관리] 페이지 로드 대기" ,
"action" : "wait" ,
"timeout" : 5000
feat: 누락 시나리오 22개 추가
- 게시판(2): board-management, board-test
- 고객센터(3): customer-inquiry, customer-faq, customer-event
- 판매관리(1): sales-site
- 구매관리(1): purchase-pricing
- 생산관리(3): production-dashboard, production-work-order, production-worker
- 출고관리(1): shipment-dispatch
- 회계관리(11): accounting-sales, accounting-purchase, accounting-cost,
accounting-financial, accounting-bill, accounting-bank-transaction,
accounting-card-history, accounting-receivable, accounting-expense-forecast,
accounting-bad-debt, accounting-daily-report
2026-02-03 09:08:18 +09:00
} ,
{
"id" : 2 ,
2026-03-01 19:08:58 +09:00
"name" : "[생산관리 > 작업지시 관리] ts 초기화 + API 모니터링" ,
"action" : "evaluate" ,
"script" : "(()=>{try{sessionStorage.removeItem('__E2E_TS__');}catch(e){}delete window.__E2E_TS__;window.__CONSOLE_ERRORS__=[];const origErr=console.error;console.error=function(){window.__CONSOLE_ERRORS__.push(Array.from(arguments).join(' ').substring(0,200));origErr.apply(console,arguments);};return JSON.stringify({ok:true,cleared:true});})()" ,
"timeout" : 3000
2026-02-09 15:05:03 +09:00
} ,
{
"id" : 3 ,
2026-03-01 19:08:58 +09:00
"name" : "[생산관리 > 작업지시 관리] 테이블 로드 대기" ,
"action" : "wait_for_table" ,
"timeout" : 20000
} ,
{
"id" : 4 ,
"name" : "[생산관리 > 작업지시 관리] 목업 페이지 감지" ,
feat: 누락 시나리오 22개 추가
- 게시판(2): board-management, board-test
- 고객센터(3): customer-inquiry, customer-faq, customer-event
- 판매관리(1): sales-site
- 구매관리(1): purchase-pricing
- 생산관리(3): production-dashboard, production-work-order, production-worker
- 출고관리(1): shipment-dispatch
- 회계관리(11): accounting-sales, accounting-purchase, accounting-cost,
accounting-financial, accounting-bill, accounting-bank-transaction,
accounting-card-history, accounting-receivable, accounting-expense-forecast,
accounting-bad-debt, accounting-daily-report
2026-02-03 09:08:18 +09:00
"action" : "verify_not_mockup" ,
"checks" : [
"작업지시 목록 표시" ,
2026-02-03 13:25:38 +09:00
"작업지시 등록 버튼 존재" ,
"검색/필터 기능 존재"
feat: 누락 시나리오 22개 추가
- 게시판(2): board-management, board-test
- 고객센터(3): customer-inquiry, customer-faq, customer-event
- 판매관리(1): sales-site
- 구매관리(1): purchase-pricing
- 생산관리(3): production-dashboard, production-work-order, production-worker
- 출고관리(1): shipment-dispatch
- 회계관리(11): accounting-sales, accounting-purchase, accounting-cost,
accounting-financial, accounting-bill, accounting-bank-transaction,
accounting-card-history, accounting-receivable, accounting-expense-forecast,
accounting-bad-debt, accounting-daily-report
2026-02-03 09:08:18 +09:00
] ,
"expected" : "정상 페이지 (목업 아님)"
} ,
2026-02-09 15:05:03 +09:00
{
"id" : 5 ,
2026-03-01 19:08:58 +09:00
"name" : "[생산관리 > 작업지시 관리] 테이블 구조 확인" ,
feat: 누락 시나리오 22개 추가
- 게시판(2): board-management, board-test
- 고객센터(3): customer-inquiry, customer-faq, customer-event
- 판매관리(1): sales-site
- 구매관리(1): purchase-pricing
- 생산관리(3): production-dashboard, production-work-order, production-worker
- 출고관리(1): shipment-dispatch
- 회계관리(11): accounting-sales, accounting-purchase, accounting-cost,
accounting-financial, accounting-bill, accounting-bank-transaction,
accounting-card-history, accounting-receivable, accounting-expense-forecast,
accounting-bad-debt, accounting-daily-report
2026-02-03 09:08:18 +09:00
"action" : "verify_table" ,
"checks" : [
"작업지시번호 컬럼" ,
"품목 컬럼" ,
2026-02-03 13:25:38 +09:00
"수량 컬럼" ,
"납기일 컬럼" ,
"상태 컬럼"
] ,
"expected" : "작업지시 테이블 컬럼 정상 표시"
} ,
{
2026-02-09 15:05:03 +09:00
"id" : 6 ,
2026-03-01 19:08:58 +09:00
"name" : "[생산관리 > 작업지시 관리] [CREATE] 등록 버튼 클릭" ,
2026-02-09 15:05:03 +09:00
"action" : "evaluate" ,
2026-03-01 19:08:58 +09:00
"script" : "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CREATE_OPEN'};const btn=Array.from(document.querySelectorAll('button')).find(b=>/작업지시.*등록|등록|추가|신규|작성/.test(b.innerText?.trim())&&b.offsetParent!==null&&!b.disabled);if(!btn){R.error='등록 버튼 없음';R.ok=false;return JSON.stringify(R);}btn.click();await w(2500);R.url=location.pathname+location.search;R.ok=true;return JSON.stringify(R);})()" ,
"timeout" : 15000 ,
"phase" : "CREATE" ,
"critical" : true
2026-02-09 15:05:03 +09:00
} ,
{
"id" : 7 ,
2026-03-01 19:08:58 +09:00
"name" : "[생산관리 > 작업지시 관리] [CREATE] 등록 폼 로드 대기" ,
"action" : "wait" ,
"timeout" : 2000
2026-02-03 13:25:38 +09:00
} ,
{
2026-02-09 15:05:03 +09:00
"id" : 8 ,
2026-03-01 19:08:58 +09:00
"name" : "[생산관리 > 작업지시 관리] [CREATE] ts 생성 + 작업지시번호/메모 입력" ,
"action" : "evaluate" ,
"script" : "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const sv=(el,v)=>{const p=el.tagName==='TEXTAREA'?HTMLTextAreaElement.prototype:HTMLInputElement.prototype;const ns=Object.getOwnPropertyDescriptor(p,'value')?.set;if(ns)ns.call(el,v);else el.value=v;el.dispatchEvent(new Event('input',{bubbles:true}));el.dispatchEvent(new Event('change',{bubbles:true}));};const ts=window.__E2E_TS__||(()=>{const n=new Date();const p=v=>v.toString().padStart(2,'0');return n.getFullYear()+p(n.getMonth()+1)+p(n.getDate())+'_'+p(n.getHours())+p(n.getMinutes())+p(n.getSeconds());})();window.__E2E_TS__=ts;try{sessionStorage.setItem('__E2E_TS__',ts);}catch(e){}const R={phase:'BASIC_INPUT',ts};const formArea=document.querySelector('main')||document.querySelector('[class*=\"content\"]')||document.body;const inputs=Array.from(formArea.querySelectorAll('input[type=\"text\"],input[type=\"number\"],input:not([type]),textarea')).filter(i=>i.offsetParent!==null&&!i.readOnly&&!i.disabled);R.inputCount=inputs.length;const orderInput=inputs.find(i=>{const ph=i.placeholder||'';const nm=i.name||'';const lbl=i.closest('[class*=field],[class*=Field],[class*=form-item],[class*=row]')?.innerText||'';return ph.includes('작업지시')||nm.includes('order')||nm.includes('workOrder')||lbl.includes('작업지시번호');});if(orderInput){sv(orderInput,'E2E_TEST_WO_'+ts);R.orderFilled=true;await w(200);}const memoInput=inputs.find(i=>{const ph=i.placeholder||'';const nm=i.name||'';const lbl=i.closest('[class*=field],[class*=Field],[class*=form-item],[class*=row]')?.innerText||'';return ph.includes('메모')||ph.includes('비고')||nm.includes('memo')||nm.includes('note')||nm.includes('remark')||lbl.includes('메모')||lbl.includes('비고')||i.tagName==='TEXTAREA';});if(memoInput){sv(memoInput,'E2E_TEST_작업지시_'+ts);R.memoFilled=true;await w(200);}R.ok=true;return JSON.stringify(R);})()" ,
"timeout" : 15000 ,
"phase" : "CREATE"
2026-02-03 13:25:38 +09:00
} ,
{
2026-02-09 15:05:03 +09:00
"id" : 9 ,
2026-03-01 19:08:58 +09:00
"name" : "[생산관리 > 작업지시 관리] [CREATE] 품목 combobox 선택" ,
"action" : "evaluate" ,
"script" : "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'ITEM_COMBO'};const formArea=document.querySelector('main')||document.querySelector('[class*=\"content\"]')||document.body;const combos=Array.from(formArea.querySelectorAll('button[role=\"combobox\"]')).filter(b=>b.offsetParent!==null&&!b.closest('nav,[class*=sidebar],[class*=Sidebar]'));R.comboCount=combos.length;const itemCombo=combos.find(b=>{const lbl=b.closest('[class*=\"field\"],[class*=\"Field\"],label,[class*=\"row\"],[class*=\"form\"]');return lbl&&(lbl.innerText.includes('품목')||lbl.innerText.includes('제품'));});const targetCombo=itemCombo||combos[0];if(targetCombo){document.body.click();await w(100);targetCombo.scrollIntoView({block:'center'});targetCombo.click();await w(600);const lb=document.querySelector('[role=\"listbox\"]');if(lb){const opts=lb.querySelectorAll('[role=\"option\"]');R.optionCount=opts.length;if(opts.length>0){opts[0].click();R.selected=opts[0].innerText?.trim().substring(0,30);await w(400);}}else{document.dispatchEvent(new KeyboardEvent('keydown',{key:'Escape',bubbles:true}));await w(200);R.noListbox=true;}}else{R.noCombo=true;}R.ok=true;return JSON.stringify(R);})()" ,
"timeout" : 15000 ,
"phase" : "CREATE"
2026-02-03 13:25:38 +09:00
} ,
{
2026-02-09 15:05:03 +09:00
"id" : 10 ,
2026-03-01 19:08:58 +09:00
"name" : "[생산관리 > 작업지시 관리] [CREATE] 수량 입력 (500)" ,
"action" : "evaluate" ,
"script" : "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const sv=(el,v)=>{const p=el.tagName==='TEXTAREA'?HTMLTextAreaElement.prototype:HTMLInputElement.prototype;const ns=Object.getOwnPropertyDescriptor(p,'value')?.set;if(ns)ns.call(el,v);else el.value=v;el.dispatchEvent(new Event('input',{bubbles:true}));el.dispatchEvent(new Event('change',{bubbles:true}));};const R={phase:'QTY_INPUT'};const formArea=document.querySelector('main')||document.querySelector('[class*=\"content\"]')||document.body;const inputs=Array.from(formArea.querySelectorAll('input[type=\"text\"],input[type=\"number\"],input:not([type])')).filter(i=>i.offsetParent!==null&&!i.readOnly&&!i.disabled);const qtyInput=inputs.find(i=>{const ph=i.placeholder||'';const nm=i.name||'';const lbl=i.closest('[class*=field],[class*=Field],[class*=form-item],[class*=row]')?.innerText||'';return ph.includes('수량')||nm.includes('quantity')||nm.includes('qty')||lbl.includes('수량');});if(qtyInput){sv(qtyInput,'500');R.qtyFilled=true;R.qtyValue=qtyInput.value;await w(300);}else{R.warn='수량 입력 필드 미발견';}R.ok=true;return JSON.stringify(R);})()" ,
"timeout" : 10000 ,
"phase" : "CREATE"
2026-02-03 13:25:38 +09:00
} ,
{
2026-02-09 15:05:03 +09:00
"id" : 11 ,
2026-03-01 19:08:58 +09:00
"name" : "[생산관리 > 작업지시 관리] [CREATE] 납기일 date picker 선택" ,
"action" : "evaluate" ,
"script" : "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'DATE_PICK'};const formArea=document.querySelector('main')||document.querySelector('[class*=\"content\"]')||document.body;const dateButtons=Array.from(formArea.querySelectorAll('button')).filter(b=>(b.innerText?.trim()==='날짜 선택'||b.querySelector('svg[class*=\"calendar\"]')||b.getAttribute('aria-label')?.includes('날짜'))&&b.offsetParent!==null);R.dateBtnCount=dateButtons.length;const dateLabelBtn=dateButtons.find(b=>{const lbl=b.closest('[class*=\"field\"],[class*=\"Field\"],label,[class*=\"row\"],[class*=\"form\"]');return lbl&&(lbl.innerText.includes('납기')||lbl.innerText.includes('기한')||lbl.innerText.includes('마감'));});const targetBtn=dateLabelBtn||dateButtons[0];if(targetBtn){targetBtn.scrollIntoView({block:'center'});await w(100);targetBtn.click();await w(600);if(!document.querySelector('table[class*=\"rdp\"],.rdp-month,[role=\"grid\"]')){targetBtn.click();await w(600);}const today=document.querySelector('[aria-selected=\"true\"]')||document.querySelector('button[name=\"day\"].bg-primary')||document.querySelector('.rdp-day_today button')||Array.from(document.querySelectorAll('button[name=\"day\"],td[role=\"gridcell\"] button,.rdp-day button')).find(b=>b.getAttribute('aria-selected')==='true'||b.classList.contains('bg-primary')||b.tabIndex===0)||document.querySelector('button[name=\"day\"]')||document.querySelector('td[role=\"gridcell\"] button');if(today){today.click();R.dateSelected=true;await w(300);}else{document.dispatchEvent(new KeyboardEvent('keydown',{key:'Escape',bubbles:true}));R.noToday=true;await w(200);}}else{R.noDateBtn=true;}R.ok=true;return JSON.stringify(R);})()" ,
"timeout" : 15000 ,
"phase" : "CREATE"
2026-02-09 15:05:03 +09:00
} ,
{
"id" : 12 ,
2026-03-01 19:08:58 +09:00
"name" : "[생산관리 > 작업지시 관리] [CREATE] 기타 combobox 선택 (우선순위 등)" ,
"action" : "evaluate" ,
"script" : "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'OTHER_COMBOS'};const formArea=document.querySelector('main')||document.querySelector('[class*=\"content\"]')||document.body;const combos=Array.from(formArea.querySelectorAll('button[role=\"combobox\"]')).filter(b=>b.offsetParent!==null&&!b.closest('nav,[class*=sidebar],[class*=Sidebar]'));R.totalCombos=combos.length;let filled=0;for(let i=0;i<combos.length;i++){const cb=combos[i];const alreadySelected=cb.innerText?.trim()&&cb.innerText.trim()!=='선택'&&cb.innerText.trim()!=='선택하세요';if(alreadySelected){filled++;continue;}document.body.click();await w(100);cb.scrollIntoView({block:'center'});cb.click();await w(500);const lb=document.querySelector('[role=\"listbox\"]');if(lb){const opt=lb.querySelector('[role=\"option\"]');if(opt){opt.click();filled++;await w(300);}}else{document.dispatchEvent(new KeyboardEvent('keydown',{key:'Escape',bubbles:true}));await w(200);}}R.filledCombos=filled;R.ok=true;return JSON.stringify(R);})()" ,
"timeout" : 20000 ,
"phase" : "CREATE"
2026-02-03 13:25:38 +09:00
} ,
{
2026-02-09 15:05:03 +09:00
"id" : 13 ,
2026-03-01 19:08:58 +09:00
"name" : "[생산관리 > 작업지시 관리] [CREATE] 등록 클릭" ,
"action" : "evaluate" ,
"script" : "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'SUBMIT'};const sub=Array.from(document.querySelectorAll('button')).find(b=>/^등록$|^저장$|^확인$/.test(b.innerText?.trim())&&b.offsetParent!==null&&!b.disabled);if(!sub){R.error='등록/저장 버튼 없음';R.ok=false;return JSON.stringify(R);}sub.click();await w(3000);const toasts=document.querySelectorAll('[data-sonner-toast],[role=\"status\"],[class*=\"toast\"],[class*=\"Toast\"],[class*=\"Toaster\"] [data-content]');R.toast=toasts.length>0?Array.from(toasts).pop()?.innerText?.trim().substring(0,100):'';R.url=location.pathname+location.search;R.ok=true;return JSON.stringify(R);})()" ,
"timeout" : 20000 ,
"phase" : "CREATE"
2026-02-03 13:25:38 +09:00
} ,
{
2026-02-09 15:05:03 +09:00
"id" : 14 ,
2026-03-01 19:08:58 +09:00
"name" : "[생산관리 > 작업지시 관리] [CREATE] API POST 검증" ,
"action" : "evaluate" ,
"script" : "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(1000);const R={phase:'API_POST_CHECK'};const logs=window.__API_LOGS__||[];const posts=logs.filter(l=>l.method==='POST'&&l.status>=200&&l.status<300);R.postCount=posts.length;R.lastPost=posts.length>0?{url:posts[posts.length-1].url?.substring(0,80),status:posts[posts.length-1].status,duration:posts[posts.length-1].duration}:null;R.ok=posts.length>0;R.info=posts.length>0?'POST API '+posts[posts.length-1].status+' OK':'warn: POST API 미감지';return JSON.stringify(R);})()" ,
"timeout" : 10000 ,
"phase" : "VERIFY"
2026-02-03 13:25:38 +09:00
} ,
{
2026-02-09 15:05:03 +09:00
"id" : 15 ,
2026-03-01 19:08:58 +09:00
"name" : "[생산관리 > 작업지시 관리] [CREATE] 모달 닫기 + 목록 복귀" ,
"action" : "evaluate" ,
"script" : "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'BACK_TO_LIST'};const modal=document.querySelector('[role=\"dialog\"],[aria-modal=\"true\"],[class*=\"modal\"]:not([class*=\"tooltip\"]),[class*=\"Modal\"]');if(modal&&modal.offsetParent!==null){const closeBtn=modal.querySelector('button[class*=\"close\"],[aria-label=\"닫기\"],[aria-label=\"Close\"]')||Array.from(modal.querySelectorAll('button')).find(b=>/닫기|Close|취소|Cancel|확인/.test(b.innerText?.trim()));if(closeBtn){closeBtn.click();await w(1000);}}const onForm=location.search.includes('mode=new')||location.search.includes('mode=edit')||location.search.includes('mode=view')||/\\/(new|[0-9]+|[0-9a-f]{8,})$/.test(location.pathname);if(onForm){const btn=Array.from(document.querySelectorAll('button,a')).find(b=>/목록|취소|뒤로/.test(b.innerText?.trim()));if(btn){btn.click();await w(2000);}else{history.back();await w(2000);}}R.url=location.pathname+location.search;R.ok=true;return JSON.stringify(R);})()" ,
"timeout" : 15000 ,
"phase" : "CREATE"
2026-02-03 13:25:38 +09:00
} ,
{
2026-02-09 15:05:03 +09:00
"id" : 16 ,
2026-03-01 19:08:58 +09:00
"name" : "[생산관리 > 작업지시 관리] [CREATE] 목록 안정화 대기" ,
"action" : "wait" ,
"timeout" : 2000
2026-02-03 13:25:38 +09:00
} ,
{
2026-02-09 15:05:03 +09:00
"id" : 17 ,
2026-03-01 19:08:58 +09:00
"name" : "[생산관리 > 작업지시 관리] [CREATE] 목록에서 등록 확인 + 상태 컬럼 검증" ,
"action" : "evaluate" ,
"script" : "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const ts=window.__E2E_TS__||sessionStorage.getItem('__E2E_TS__')||'';const R={phase:'VERIFY_CREATE'};await w(500);const rows=Array.from(document.querySelectorAll('table tbody tr'));R.rowCount=rows.length;let found=rows.find(r=>r.innerText?.includes('E2E_TEST_'));if(!found){const ths=document.querySelectorAll('table thead th');const sortTh=Array.from(ths).find(th=>/일자|날짜|No|번호/.test(th.innerText?.trim()));if(sortTh){sortTh.click();await w(1000);sortTh.click();await w(1000);}const rows2=Array.from(document.querySelectorAll('table tbody tr'));found=rows2.find(r=>r.innerText?.includes('E2E_TEST_'));}R.found=!!found;if(found){const cells=found.querySelectorAll('td');const cellTexts=Array.from(cells).map(c=>c.innerText?.trim());R.rowData=cellTexts.join(' | ').substring(0,200);const statusKeywords=['대기','등록','진행','예정','준비','미착수','생성'];const hasStatus=cellTexts.some(t=>statusKeywords.some(kw=>t.includes(kw)));R.statusFound=hasStatus;R.statusInfo=hasStatus?'상태 컬럼 확인':'상태 컬럼 미감지 (확인 필요)';}R.ok=R.found||R.rowCount>0;return JSON.stringify(R);})()" ,
"timeout" : 20000 ,
"phase" : "VERIFY"
2026-02-03 13:25:38 +09:00
} ,
{
2026-02-09 15:05:03 +09:00
"id" : 18 ,
2026-03-01 19:08:58 +09:00
"name" : "[생산관리 > 작업지시 관리] [READ] E2E 행 클릭 → 상세 진입" ,
"action" : "evaluate" ,
"script" : "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'READ_ENTER'};const rows=Array.from(document.querySelectorAll('table tbody tr'));const testRow=rows.find(r=>r.innerText?.includes('E2E_TEST_'));const targetRow=testRow||rows[0];R.usedTestRow=!!testRow;if(targetRow){const cells=targetRow.querySelectorAll('td');const captured={};const colNames=['checkbox','no','orderNo','itemName','qty','dueDate','status','priority','memo'];cells.forEach((cell,i)=>{const key=colNames[i]||('col'+i);captured[key]=cell.innerText?.trim()||'';});window.__E2E_CAPTURED__=captured;R.captured=captured;targetRow.click();await w(3000);}R.detailUrl=location.pathname+location.search;R.ok=true;return JSON.stringify(R);})()" ,
"timeout" : 15000 ,
"phase" : "READ"
2026-02-03 13:25:38 +09:00
} ,
{
2026-02-09 15:05:03 +09:00
"id" : 19 ,
2026-03-01 19:08:58 +09:00
"name" : "[생산관리 > 작업지시 관리] [READ] 상세 페이지 로드 대기" ,
"action" : "wait" ,
"timeout" : 2000
2026-02-03 13:25:38 +09:00
} ,
{
2026-02-09 15:05:03 +09:00
"id" : 20 ,
2026-03-01 19:08:58 +09:00
"name" : "[생산관리 > 작업지시 관리] [READ] 상세 필드 1:1 대조 검증" ,
"action" : "evaluate" ,
"script" : "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'DETAIL_VERIFY'};const cap=window.__E2E_CAPTURED__||{};R.hasCaptured=Object.keys(cap).length>0;if(!R.hasCaptured){R.error='캡처 데이터 없음';R.ok=false;return JSON.stringify(R);}const norm=s=>(s||'').replace(/[,\\s]/g,'').trim();const pageText=document.body.innerText;const inputs=Array.from(document.querySelectorAll('input,textarea,select')).filter(i=>i.offsetParent!==null);const allValues=[...inputs.map(i=>i.value)];const matches={};const checks=['orderNo','itemName','qty','dueDate','status','priority','memo'];checks.forEach(key=>{const val=cap[key];if(!val||val==='')return;const nv=norm(val);const found=pageText.includes(val)||pageText.includes(nv)||norm(pageText).includes(nv)||allValues.some(v=>v?.includes(val)||norm(v)===nv||norm(v).includes(nv));matches[key]={expected:val,found};});R.matches=matches;const matchCount=Object.values(matches).filter(m=>m.found).length;const totalChecks=Object.keys(matches).length;R.matchCount=matchCount;R.totalChecks=totalChecks;R.matchRate=totalChecks>0?Math.round(matchCount/totalChecks*100)+'%':'N/A';R.ok=matchCount>=Math.max(1,Math.ceil(totalChecks*0.4));return JSON.stringify(R);})()" ,
"timeout" : 15000 ,
"phase" : "VERIFY"
2026-02-09 15:05:03 +09:00
} ,
{
"id" : 21 ,
2026-03-01 19:08:58 +09:00
"name" : "[생산관리 > 작업지시 관리] [UPDATE] 수정 모드 진입" ,
"action" : "evaluate" ,
"script" : "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'EDIT_ENTER'};const editBtn=Array.from(document.querySelectorAll('button')).find(b=>b.innerText?.trim()==='수정'&&b.offsetParent!==null&&!b.disabled);R.editBtnExists=!!editBtn;if(editBtn){editBtn.click();await w(2000);R.editUrl=location.pathname+location.search;R.isEditMode=location.search.includes('mode=edit');const inputs=Array.from(document.querySelectorAll('input[type=\"text\"],input[type=\"number\"],input:not([type]),textarea')).filter(i=>i.offsetParent!==null&&!i.readOnly&&!i.disabled);R.editableFields=inputs.length;R.ok=R.editableFields>0||R.isEditMode;}else{R.ok=true;R.info='수정 버튼 없음 (뷰 모드 전용 가능)';}return JSON.stringify(R);})()" ,
"timeout" : 15000 ,
"phase" : "UPDATE"
2026-02-03 13:25:38 +09:00
} ,
{
2026-02-09 15:05:03 +09:00
"id" : 22 ,
2026-03-01 19:08:58 +09:00
"name" : "[생산관리 > 작업지시 관리] [UPDATE] 수량 500→600 수정" ,
"action" : "evaluate" ,
"script" : "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const sv=(el,v)=>{const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(el,v);else el.value=v;el.dispatchEvent(new Event('input',{bubbles:true}));el.dispatchEvent(new Event('change',{bubbles:true}));};const R={phase:'UPDATE_QTY'};const inputs=Array.from(document.querySelectorAll('input[type=\"text\"],input[type=\"number\"],input:not([type])')).filter(i=>i.offsetParent!==null&&!i.readOnly&&!i.disabled);const qtyInput=inputs.find(i=>{const ph=i.placeholder||'';const nm=i.name||'';const lbl=i.closest('[class*=field],[class*=Field],[class*=form-item],[class*=row]')?.innerText||'';return(ph.includes('수량')||nm.includes('quantity')||nm.includes('qty')||lbl.includes('수량'))&&(i.value==='500'||i.value==='500.00');});if(qtyInput){sv(qtyInput,'600');R.qtyUpdated=true;R.newValue=qtyInput.value;await w(300);}else{const fallback=inputs.find(i=>i.value==='500'||i.value==='500.00');if(fallback){sv(fallback,'600');R.qtyUpdated=true;R.usedFallback=true;await w(300);}else{R.warn='수량 입력 필드(500) 미발견';}}R.ok=true;return JSON.stringify(R);})()" ,
"timeout" : 10000 ,
"phase" : "UPDATE"
2026-02-03 13:25:38 +09:00
} ,
{
2026-02-09 15:05:03 +09:00
"id" : 23 ,
2026-03-01 19:08:58 +09:00
"name" : "[생산관리 > 작업지시 관리] [UPDATE] 메모 수정" ,
"action" : "evaluate" ,
"script" : "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const sv=(el,v)=>{const p=el.tagName==='TEXTAREA'?HTMLTextAreaElement.prototype:HTMLInputElement.prototype;const ns=Object.getOwnPropertyDescriptor(p,'value')?.set;if(ns)ns.call(el,v);else el.value=v;el.dispatchEvent(new Event('input',{bubbles:true}));el.dispatchEvent(new Event('change',{bubbles:true}));};const ts=window.__E2E_TS__||sessionStorage.getItem('__E2E_TS__')||'';const R={phase:'UPDATE_MEMO'};const inputs=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type]),textarea')).filter(i=>i.offsetParent!==null&&!i.readOnly&&!i.disabled);const memoInput=inputs.find(i=>{const ph=i.placeholder||'';const nm=i.name||'';const lbl=i.closest('[class*=field],[class*=Field],[class*=form-item],[class*=row]')?.innerText||'';return ph.includes('메모')||ph.includes('비고')||nm.includes('memo')||nm.includes('note')||nm.includes('remark')||lbl.includes('메모')||lbl.includes('비고')||i.tagName==='TEXTAREA';});if(memoInput){sv(memoInput,'E2E_수정완료_작업지시_'+ts);R.memoUpdated=true;await w(300);}else{R.warn='메모 필드 미발견';}R.ok=true;return JSON.stringify(R);})()" ,
"timeout" : 10000 ,
"phase" : "UPDATE"
2026-02-03 13:25:38 +09:00
} ,
{
2026-02-09 15:05:03 +09:00
"id" : 24 ,
2026-03-01 19:08:58 +09:00
"name" : "[생산관리 > 작업지시 관리] [UPDATE] 저장 클릭" ,
"action" : "evaluate" ,
"script" : "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'UPDATE_SAVE'};const saveBtn=Array.from(document.querySelectorAll('button')).find(b=>/^저장$|^수정$|^확인$/.test(b.innerText?.trim())&&b.offsetParent!==null&&!b.disabled);if(!saveBtn){R.error='저장 버튼 없음';R.ok=false;return JSON.stringify(R);}saveBtn.click();await w(3000);const toasts=document.querySelectorAll('[data-sonner-toast],[role=\"status\"],[class*=\"toast\"],[class*=\"Toast\"],[class*=\"Toaster\"] [data-content]');R.toast=toasts.length>0?Array.from(toasts).pop()?.innerText?.trim().substring(0,100):'';R.url=location.pathname+location.search;R.ok=true;return JSON.stringify(R);})()" ,
"timeout" : 20000 ,
"phase" : "UPDATE"
2026-02-09 15:05:03 +09:00
} ,
{
"id" : 25 ,
2026-03-01 19:08:58 +09:00
"name" : "[생산관리 > 작업지시 관리] [UPDATE] API PUT 검증" ,
"action" : "evaluate" ,
"script" : "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_PUT_CHECK'};const logs=window.__API_LOGS__||[];const puts=logs.filter(l=>(l.method==='PUT'||l.method==='PATCH')&&l.status>=200&&l.status<300);R.putCount=puts.length;R.lastPut=puts.length>0?{url:puts[puts.length-1].url?.substring(0,80),status:puts[puts.length-1].status,duration:puts[puts.length-1].duration}:null;R.ok=puts.length>0;R.info=puts.length>0?'PUT/PATCH API '+puts[puts.length-1].status+' OK':'warn: PUT/PATCH API 미감지';return JSON.stringify(R);})()" ,
"timeout" : 10000 ,
"phase" : "VERIFY"
} ,
2026-02-03 13:25:38 +09:00
{
2026-03-01 19:08:58 +09:00
"id" : 26 ,
"name" : "[생산관리 > 작업지시 관리] [UPDATE] 수정 결과 확인 (600 반영)" ,
"action" : "evaluate" ,
"script" : "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(1000);const R={phase:'VERIFY_UPDATE'};const pageText=document.body.innerText;const inputs=Array.from(document.querySelectorAll('input')).filter(i=>i.offsetParent!==null);const allVals=[pageText,...inputs.map(i=>i.value||'')].join(' ');R.has600=allVals.includes('600');R.hasMemo=allVals.includes('E2E_수정완료');R.ok=true;R.info=[R.has600?'수량 600 확인':'수량 600 미감지',R.hasMemo?'메모 수정 확인':'메모 수정 미감지'].join(' | ');return JSON.stringify(R);})()" ,
"timeout" : 10000 ,
"phase" : "VERIFY"
2026-02-03 13:25:38 +09:00
} ,
{
2026-03-01 19:08:58 +09:00
"id" : 27 ,
"name" : "[생산관리 > 작업지시 관리] [DELETE] 삭제 실행" ,
"action" : "evaluate" ,
"script" : "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const ts=window.__E2E_TS__||sessionStorage.getItem('__E2E_TS__')||'';const R={phase:'DELETE'};const onDetail=/\\/(new|[0-9]+|[0-9a-f]{8,})/.test(location.pathname)||location.search.includes('mode=');if(!onDetail){const rows=Array.from(document.querySelectorAll('table tbody tr'));const row=rows.find(r=>r.innerText?.includes('E2E_TEST_'));if(row){row.click();await w(3000);}else{R.info='E2E 행 미발견 - 삭제 스킵';R.ok=true;return JSON.stringify(R);}}let delBtn=Array.from(document.querySelectorAll('button')).find(b=>b.innerText?.trim()==='삭제'&&b.offsetParent!==null);if(!delBtn){await w(2000);delBtn=Array.from(document.querySelectorAll('button')).find(b=>b.innerText?.trim()==='삭제'&&b.offsetParent!==null);}if(!delBtn){R.info='삭제 버튼 없음 - 스킵 (수동 정리 필요)';R.ok=true;return JSON.stringify(R);}delBtn.click();await w(1500);let cfm=document.querySelector('[role=\"alertdialog\"] [data-slot=\"alert-dialog-footer\"] button:last-child');if(!cfm){cfm=Array.from(document.querySelectorAll('[role=\"alertdialog\"] button')).find(b=>/삭제/.test(b.innerText?.trim())&&b!==delBtn);}if(!cfm){cfm=Array.from(document.querySelectorAll('button')).find(b=>/확인|삭제|예/.test(b.innerText?.trim())&&b!==delBtn&&b.offsetParent!==null);}if(cfm){cfm.click();await w(3000);}R.urlAfter=location.pathname+location.search;R.ok=true;return JSON.stringify(R);})()" ,
"timeout" : 30000 ,
"phase" : "DELETE"
2026-02-03 13:25:38 +09:00
} ,
{
2026-03-01 19:08:58 +09:00
"id" : 28 ,
"name" : "[생산관리 > 작업지시 관리] [DELETE] API DELETE 검증" ,
"action" : "evaluate" ,
"script" : "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_DELETE_CHECK'};const logs=window.__API_LOGS__||[];const dels=logs.filter(l=>l.method==='DELETE'&&l.status>=200&&l.status<300);R.deleteCount=dels.length;R.lastDelete=dels.length>0?{url:dels[dels.length-1].url?.substring(0,80),status:dels[dels.length-1].status}:null;R.ok=dels.length>0;R.info=dels.length>0?'DELETE API '+dels[dels.length-1].status+' OK':'warn: DELETE API 미감지';return JSON.stringify(R);})()" ,
"timeout" : 10000 ,
"phase" : "VERIFY"
2026-02-03 13:25:38 +09:00
} ,
{
2026-03-01 19:08:58 +09:00
"id" : 29 ,
"name" : "[생산관리 > 작업지시 관리] [DELETE] 목록 복귀 + 삭제 확인" ,
"action" : "evaluate" ,
"script" : "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const ts=window.__E2E_TS__||sessionStorage.getItem('__E2E_TS__')||'';const R={phase:'VERIFY_DELETE'};const onForm=location.search.includes('mode=')||/\\/(new|[0-9]+|[0-9a-f]{8,})$/.test(location.pathname);if(onForm){const btn=Array.from(document.querySelectorAll('button,a')).find(b=>/목록|취소|뒤로/.test(b.innerText?.trim()));if(btn){btn.click();await w(2000);}else{history.back();await w(2000);}}await w(1000);const rows=Array.from(document.querySelectorAll('table tbody tr'));const found=rows.find(r=>r.innerText?.includes(ts));R.stillExists=!!found;R.rowCount=rows.length;R.url=location.pathname+location.search;R.ok=!found;return JSON.stringify(R);})()" ,
"timeout" : 15000 ,
"phase" : "VERIFY"
2026-02-03 13:25:38 +09:00
} ,
{
2026-03-01 19:08:58 +09:00
"id" : 30 ,
"name" : "[생산관리 > 작업지시 관리] [FINAL] API 요약 + 콘솔 에러 확인" ,
"action" : "evaluate" ,
"script" : "(()=>{const R={phase:'FINAL_SUMMARY'};const logs=window.__API_LOGS__||[];R.apiSummary={total:logs.length,success:logs.filter(l=>l.ok||l.status<400).length,failed:logs.filter(l=>!l.ok&&l.status>=400).length,avgResponseTime:logs.length>0?Math.round(logs.reduce((s,l)=>s+(l.duration||0),0)/logs.length):0,slowCalls:logs.filter(l=>l.duration>2000).length,methods:{GET:logs.filter(l=>l.method==='GET').length,POST:logs.filter(l=>l.method==='POST').length,PUT:logs.filter(l=>l.method==='PUT'||l.method==='PATCH').length,DELETE:logs.filter(l=>l.method==='DELETE').length}};const errs=window.__CONSOLE_ERRORS__||[];R.consoleErrors=errs.length;R.errorSamples=errs.slice(0,3);R.ok=true;return JSON.stringify(R);})()" ,
"timeout" : 10000 ,
"phase" : "VERIFY"
feat: 누락 시나리오 22개 추가
- 게시판(2): board-management, board-test
- 고객센터(3): customer-inquiry, customer-faq, customer-event
- 판매관리(1): sales-site
- 구매관리(1): purchase-pricing
- 생산관리(3): production-dashboard, production-work-order, production-worker
- 출고관리(1): shipment-dispatch
- 회계관리(11): accounting-sales, accounting-purchase, accounting-cost,
accounting-financial, accounting-bill, accounting-bank-transaction,
accounting-card-history, accounting-receivable, accounting-expense-forecast,
accounting-bad-debt, accounting-daily-report
2026-02-03 09:08:18 +09:00
}
] ,
2026-03-01 19:08:58 +09:00
"expectedAPIs" : [
{ "method" : "GET" , "endpoint" : "/api/v1/work-orders" , "description" : "작업지시 목록 조회" } ,
{ "method" : "POST" , "endpoint" : "/api/v1/work-orders" , "description" : "작업지시 등록" } ,
{ "method" : "GET" , "endpoint" : "/api/v1/work-orders/{id}" , "description" : "작업지시 상세 조회" } ,
{ "method" : "PUT" , "endpoint" : "/api/v1/work-orders/{id}" , "description" : "작업지시 수정" } ,
{ "method" : "DELETE" , "endpoint" : "/api/v1/work-orders/{id}" , "description" : "작업지시 삭제" }
] ,
feat: 누락 시나리오 22개 추가
- 게시판(2): board-management, board-test
- 고객센터(3): customer-inquiry, customer-faq, customer-event
- 판매관리(1): sales-site
- 구매관리(1): purchase-pricing
- 생산관리(3): production-dashboard, production-work-order, production-worker
- 출고관리(1): shipment-dispatch
- 회계관리(11): accounting-sales, accounting-purchase, accounting-cost,
accounting-financial, accounting-bill, accounting-bank-transaction,
accounting-card-history, accounting-receivable, accounting-expense-forecast,
accounting-bad-debt, accounting-daily-report
2026-02-03 09:08:18 +09:00
"requiredVerifications" : [
2026-03-01 19:08:58 +09:00
{ "id" : 2 , "name" : "등록/저장 버튼" , "steps" : [ 13 , 14 ] , "criteria" : "API POST + 토스트 + 목록 반영" } ,
{ "id" : 5 , "name" : "목업 페이지 감지" , "steps" : [ 4 ] , "criteria" : "작업지시 목록, 등록 버튼, 필터 존재" } ,
{ "id" : 6 , "name" : "삭제 기능" , "steps" : [ 27 , 28 , 29 ] , "criteria" : "DELETE API + 목록에서 제거" }
2026-02-03 13:25:38 +09:00
] ,
"rollbackPlan" : {
"onCreateFail" : "모달 닫기" ,
"onUpdateFail" : "테스트 작업지시 수동 삭제 필요" ,
"onDeleteFail" : "테스트 작업지시 수동 삭제 필요" ,
"cleanupRequired" : "E2E_TEST_ 접두사 작업지시는 테스트 데이터"
}
2026-03-01 19:08:58 +09:00
}