Files
sam-scenarios/full-crud-acc-sales.json
김보곤 f42cf4ab7d fix: deprecated window.__API_LOGS__ → window.__E2E__.getApiLogs() 패턴 수정 (17개 파일)
- approval-box, edge-rapid-click-acc-sales, full-crud-* (4개)
- hr-salary-long-term-care, production-work-order
- quality-inspection, quality-performance-report
- reload-persist-acc-deposit, sales-management
- sales-order-bulk-delete, sales-order, sales-quotation
- system-dashboard, vendor-management
- 전체 209/209 ALL PASS 검증 완료

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 11:42:23 +09:00

267 lines
28 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"id": "full-crud-acc-sales",
"name": "Full CRUD 테스트: 매출관리 (계산검증+상세대조+API검증)",
"version": "2.0.0",
"auth": {
"role": "admin"
},
"menuNavigation": {
"level1": "회계관리",
"level2": "매출관리"
},
"screenshotPolicy": {
"captureOnFail": true,
"captureOnPass": false
},
"steps": [
{
"id": 1,
"name": "[회계관리 > 매출관리] 페이지 로드 대기",
"action": "wait",
"timeout": 5000
},
{
"id": 2,
"name": "[회계관리 > 매출관리] 테이블 로드 대기",
"action": "wait_for_table",
"timeout": 20000
},
{
"id": 3,
"name": "[회계관리 > 매출관리] [SETUP] 타임스탬프 초기화 + sessionStorage 저장",
"action": "evaluate",
"script": "(()=>{const ts=window.__E2E_TS__||sessionStorage.getItem('__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){}return JSON.stringify({ok:true,ts});})()",
"timeout": 5000,
"phase": "SETUP"
},
{
"id": 4,
"name": "[회계관리 > 매출관리] [CREATE] 매출 등록 버튼 클릭",
"action": "evaluate",
"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);}R.btnText=btn.innerText?.trim();btn.click();await w(2500);R.url=location.pathname+location.search;R.ok=true;return JSON.stringify(R);})()",
"timeout": 15000,
"phase": "CREATE"
},
{
"id": 5,
"name": "[회계관리 > 매출관리] [CREATE] 등록 폼 로드 대기",
"action": "wait",
"timeout": 2000
},
{
"id": 6,
"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:'VENDOR_SELECT',ts};const combos=Array.from(document.querySelectorAll('button[role=\"combobox\"]')).filter(b=>b.offsetParent!==null);R.comboCount=combos.length;for(let i=0;i<combos.length;i++){const cb=combos[i];const lbl=cb.closest('[class*=field],[class*=Field],[class*=form-item]')?.querySelector('label')?.innerText||'';if(lbl.includes('거래처')||i===0){document.body.click();await w(100);cb.scrollIntoView({block:'center'});cb.click();await w(600);const lb=document.querySelector('[role=\"listbox\"]');if(lb){const opt=lb.querySelector('[role=\"option\"]');if(opt){opt.click();await w(400);R.vendorSelected=true;R.vendorName=opt.innerText?.trim().substring(0,30);}}else{document.dispatchEvent(new KeyboardEvent('keydown',{key:'Escape',bubbles:true}));await w(200);}break;}}R.ok=true;return JSON.stringify(R);})()",
"timeout": 15000,
"phase": "CREATE"
},
{
"id": 7,
"name": "[회계관리 > 매출관리] [CREATE] 매출유형 콤보박스 선택 (제품매출)",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'TYPE_SELECT'};const combos=Array.from(document.querySelectorAll('button[role=\"combobox\"]')).filter(b=>b.offsetParent!==null);for(let i=0;i<combos.length;i++){const cb=combos[i];const lbl=cb.closest('[class*=field],[class*=Field],[class*=form-item]')?.querySelector('label')?.innerText||'';if(lbl.includes('매출유형')||lbl.includes('유형')||i===1){document.body.click();await w(100);cb.scrollIntoView({block:'center'});cb.click();await w(600);const lb=document.querySelector('[role=\"listbox\"]');if(lb){const opts=lb.querySelectorAll('[role=\"option\"]');const target=Array.from(opts).find(o=>o.innerText?.includes('제품매출'))||opts[0];if(target){target.click();await w(400);R.typeSelected=true;R.typeName=target.innerText?.trim().substring(0,30);}}else{document.dispatchEvent(new KeyboardEvent('keydown',{key:'Escape',bubbles:true}));await w(200);}break;}}R.ok=true;return JSON.stringify(R);})()",
"timeout": 15000,
"phase": "CREATE"
},
{
"id": 8,
"name": "[회계관리 > 매출관리] [CREATE] 품목명·수량(5)·단가(100,000)·적요 입력",
"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:'FILL_FIELDS',ts};const inputs=Array.from(document.querySelectorAll('input[type=\"text\"],input[type=\"number\"],input:not([type])')).filter(i=>i.offsetParent!==null&&!i.readOnly&&!i.disabled);for(const inp of inputs){const ph=inp.placeholder||'';const nm=inp.name||'';const lbl=inp.closest('[class*=field],[class*=Field],[class*=form-item]')?.querySelector('label')?.innerText||'';if(ph.includes('품목')||nm.includes('item')||lbl.includes('품목')){sv(inp,'E2E_TEST_품목_'+ts);R.itemFilled=true;await w(200);break;}}for(const inp of inputs){const ph=inp.placeholder||'';const nm=inp.name||'';const lbl=inp.closest('[class*=field],[class*=Field],[class*=form-item]')?.querySelector('label')?.innerText||'';if(ph.includes('수량')||nm.includes('quantity')||nm.includes('qty')||lbl.includes('수량')){sv(inp,'5');R.qtyFilled=true;await w(200);break;}}for(const inp of inputs){const ph=inp.placeholder||'';const nm=inp.name||'';const lbl=inp.closest('[class*=field],[class*=Field],[class*=form-item]')?.querySelector('label')?.innerText||'';if(ph.includes('단가')||nm.includes('price')||nm.includes('unitPrice')||lbl.includes('단가')){sv(inp,'100000');R.priceFilled=true;await w(200);break;}}const noteInput=inputs.find(i=>{const ph=i.placeholder||'';const nm=i.name||'';const lbl=i.closest('[class*=field],[class*=Field],[class*=form-item]')?.querySelector('label')?.innerText||'';return ph.includes('적요')||nm.includes('note')||nm.includes('remark')||lbl.includes('적요');});if(noteInput){sv(noteInput,'E2E_TEST_매출_'+ts);R.noteFilled=true;await w(200);}await w(800);R.ok=true;return JSON.stringify(R);})()",
"timeout": 20000,
"phase": "CREATE"
},
{
"id": 9,
"name": "[회계관리 > 매출관리] [CREATE] 자동계산 검증: 수량(5)×단가(100,000)=공급가액(500,000), 부가세(50,000), 합계(550,000)",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const toNum=s=>Number((s||'').replace(/[^0-9.-]/g,''));const R={phase:'CALC_VERIFY_CREATE'};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.hasSupply500000=allVals.includes('500,000')||allVals.includes('500000');R.hasVat50000=allVals.includes('50,000')||allVals.includes('50000');R.hasTotal550000=allVals.includes('550,000')||allVals.includes('550000');const supplyInputs=inputs.filter(i=>{const ph=i.placeholder||'';const nm=i.name||'';const lbl=i.closest('[class*=field],[class*=Field],[class*=form-item]')?.querySelector('label')?.innerText||'';return ph.includes('공급')||nm.includes('supply')||lbl.includes('공급');});if(supplyInputs.length>0){const val=toNum(supplyInputs[0].value);R.supplyValue=val;R.supplyMatch=val===500000;}const vatInputs=inputs.filter(i=>{const ph=i.placeholder||'';const nm=i.name||'';const lbl=i.closest('[class*=field],[class*=Field],[class*=form-item]')?.querySelector('label')?.innerText||'';return ph.includes('부가세')||nm.includes('vat')||nm.includes('tax')||lbl.includes('부가세');});if(vatInputs.length>0){const val=toNum(vatInputs[0].value);R.vatValue=val;R.vatMatch=val===50000;}R.ok=true;R.info=[R.hasSupply500000?'✅ 공급 500,000':'⚠️ 공급 500,000 미감지',R.hasVat50000?'✅ 부가세 50,000':'⚠️ 부가세 50,000 미감지',R.hasTotal550000?'✅ 합계 550,000':'⚠️ 합계 550,000 미감지'].join(' | ');return JSON.stringify(R);})()",
"timeout": 10000,
"phase": "VERIFY"
},
{
"id": 10,
"name": "[회계관리 > 매출관리] [CREATE] 등록 클릭",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const toastInfo=()=>{const t=document.querySelectorAll('[data-sonner-toast],[role=\"status\"],[class*=\"toast\"],[class*=\"Toast\"],[class*=\"Toaster\"] [data-content]');return{count:t.length,text:t.length>0?Array.from(t).pop()?.innerText?.trim().substring(0,100):''};};const R={phase:'SUBMIT'};const sub=Array.from(document.querySelectorAll('button')).find(b=>b.innerText?.trim()==='등록'&&b.offsetParent!==null&&!b.disabled);if(!sub){R.error='등록 버튼 없음';R.ok=false;return JSON.stringify(R);}sub.click();await w(3000);R.toast=toastInfo();R.ok=true;return JSON.stringify(R);})()",
"timeout": 20000,
"phase": "CREATE"
},
{
"id": 11,
"name": "[회계관리 > 매출관리] [CREATE] API POST 검증",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_POST_CHECK'};const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const postLogs=logs.filter(l=>l.method==='POST'&&l.status>=200&&l.status<300);R.postCount=postLogs.length;R.found=postLogs.length>0;if(postLogs.length>0){const last=postLogs[postLogs.length-1];R.url=last.url?.substring(0,80);R.status=last.status;R.duration=last.duration;}R.ok=true;R.info=R.found?'✅ POST '+R.status+' ('+R.duration+'ms)':'⚠️ POST 호출 미감지 (API 모니터 범위 밖일 수 있음)';return JSON.stringify(R);})()",
"timeout": 5000,
"phase": "VERIFY"
},
{
"id": 12,
"name": "[회계관리 > 매출관리] [CREATE] 생성 후 대기",
"action": "wait",
"timeout": 3000
},
{
"id": 13,
"name": "[회계관리 > 매출관리] [CREATE] 목록 복귀",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const onForm=location.search.includes('mode=new')||location.search.includes('mode=edit')||location.search.includes('mode=view')||new RegExp('/(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);}}return JSON.stringify({url:location.pathname+location.search});})()",
"timeout": 10000,
"phase": "CREATE"
},
{
"id": 14,
"name": "[회계관리 > 매출관리] [CREATE] 목록 안정화 대기",
"action": "wait",
"timeout": 2000
},
{
"id": 15,
"name": "[회계관리 > 매출관리] [VERIFY] 생성 데이터 확인",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const ts=window.__E2E_TS__||sessionStorage.getItem('__E2E_TS__')||'E2E_TEST_';const R={phase:'VERIFY_CREATE'};await w(500);const rows=document.querySelectorAll('table tbody tr');R.rowCount=rows.length;let found=Array.from(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(1500);sortTh.click();await w(1500);}const rows2=document.querySelectorAll('table tbody tr');found=Array.from(rows2).find(r=>r.innerText?.includes('E2E_TEST_'));R.rowCount=rows2.length;if(found)R.info='found after sort';}R.found=!!found;R.ok=R.found;if(found){R.foundText=found.innerText?.substring(0,100);const cells=found.querySelectorAll('td');window.__E2E_LIST_ROW__={};const colNames=['checkbox','no','salesNo','vendorName','salesDate','salesType','supplyAmount','vat','totalAmount'];cells.forEach((cell,i)=>{const key=colNames[i]||('col'+i);window.__E2E_LIST_ROW__[key]=cell.innerText?.trim();});}return JSON.stringify(R);})()",
"timeout": 15000,
"phase": "VERIFY"
},
{
"id": 16,
"name": "[회계관리 > 매출관리] [READ] 상세 페이지 진입",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const ts=window.__E2E_TS__||sessionStorage.getItem('__E2E_TS__')||'E2E_TEST_';const R={phase:'READ'};let row;for(let i=0;i<3;i++){const rows=Array.from(document.querySelectorAll('table tbody tr'));row=rows.find(r=>r.innerText?.includes('E2E_TEST_'));if(row)break;await w(1000);}if(!row){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(1500);sortTh.click();await w(1500);}const rows2=Array.from(document.querySelectorAll('table tbody tr'));row=rows2.find(r=>r.innerText?.includes('E2E_TEST_'));}if(!row){R.error='E2E_TEST_ 행 없음 (정렬 후에도 미발견)';R.ok=false;return JSON.stringify(R);}row.click();await w(2500);R.detailUrl=location.pathname+location.search;R.ok=true;return JSON.stringify(R);})()",
"timeout": 15000,
"phase": "READ"
},
{
"id": 17,
"name": "[회계관리 > 매출관리] [READ] 상세 페이지 대기",
"action": "wait",
"timeout": 2000
},
{
"id": 18,
"name": "[회계관리 > 매출관리] [READ] 목록↔상세 필드 1:1 대조 (detail roundtrip)",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'DETAIL_ROUNDTRIP'};const listRow=window.__E2E_LIST_ROW__||{};R.hasListData=Object.keys(listRow).length>0;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=['salesNo','vendorName','salesDate','salesType','supplyAmount','vat','totalAmount'];checks.forEach(key=>{const val=listRow[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));R.info='상세 대조: '+R.matchCount+'/'+R.totalChecks+' ('+R.matchRate+')';return JSON.stringify(R);})()",
"timeout": 15000,
"phase": "VERIFY"
},
{
"id": 19,
"name": "[회계관리 > 매출관리] [READ] 상세 필드값 검증 (E2E데이터+수량5+단가100000+공급500000)",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const ts=window.__E2E_TS__||sessionStorage.getItem('__E2E_TS__')||'E2E_TEST_';const R={phase:'READ_VERIFY'};R.url=location.pathname+location.search;const pageText=document.body.innerText;const inputs=Array.from(document.querySelectorAll('input,textarea')).filter(i=>i.offsetParent!==null);R.fieldCount=inputs.length;R.hasE2EData=pageText.includes('E2E_TEST_')||inputs.some(i=>i.value?.includes('E2E_TEST_'));const hasQty=pageText.includes('5')||inputs.some(i=>i.value==='5');const hasPrice=pageText.includes('100,000')||pageText.includes('100000')||inputs.some(i=>i.value?.includes('100000'));const hasSupply=pageText.includes('500,000')||pageText.includes('500000');const hasVat=pageText.includes('50,000')||pageText.includes('50000');const hasTotal=pageText.includes('550,000')||pageText.includes('550000');R.fieldChecks={qty:hasQty,price:hasPrice,supply:hasSupply,vat:hasVat,total:hasTotal};R.ok=R.hasE2EData;R.info='E2E='+R.hasE2EData+' qty='+hasQty+' price='+hasPrice+' supply='+hasSupply+' vat='+hasVat+' total='+hasTotal;return JSON.stringify(R);})()",
"timeout": 15000,
"phase": "READ"
},
{
"id": 20,
"name": "[회계관리 > 매출관리] [UPDATE] 수정 모드 진입",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'UPDATE_ENTER'};const editBtn=Array.from(document.querySelectorAll('button')).find(b=>b.innerText?.trim()==='수정'&&b.offsetParent!==null);if(!editBtn){R.error='수정 버튼 없음';R.ok=false;return JSON.stringify(R);}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])')).filter(i=>i.offsetParent!==null&&!i.readOnly&&!i.disabled);R.editableFields=inputs.length;R.ok=R.editableFields>0||R.isEditMode;return JSON.stringify(R);})()",
"timeout": 15000,
"phase": "UPDATE"
},
{
"id": 21,
"name": "[회계관리 > 매출관리] [UPDATE] 수량 5→10 변경 + 적요 수정",
"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_FIELDS'};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]')?.querySelector('label')?.innerText||'';return ph.includes('수량')||nm.includes('quantity')||nm.includes('qty')||lbl.includes('수량');});if(qtyInput){R.oldQty=qtyInput.value;sv(qtyInput,'10');R.newQty='10';await w(300);}const noteInput=inputs.find(i=>{const ph=i.placeholder||'';const nm=i.name||'';const lbl=i.closest('[class*=field],[class*=Field],[class*=form-item]')?.querySelector('label')?.innerText||'';return ph.includes('적요')||nm.includes('note')||nm.includes('remark')||lbl.includes('적요');});if(noteInput){sv(noteInput,'E2E_수정됨_'+ts);R.noteModified=true;await w(200);}await w(800);R.ok=true;return JSON.stringify(R);})()",
"timeout": 15000,
"phase": "UPDATE"
},
{
"id": 22,
"name": "[회계관리 > 매출관리] [UPDATE] 재계산 검증: 10×100,000=공급(1,000,000), 부가세(100,000), 합계(1,100,000)",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const toNum=s=>Number((s||'').replace(/[^0-9.-]/g,''));const R={phase:'CALC_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.hasSupply1M=allVals.includes('1,000,000')||allVals.includes('1000000');R.hasVat100K=allVals.includes('100,000')||allVals.includes('100000');R.hasTotal1100K=allVals.includes('1,100,000')||allVals.includes('1100000');const supplyInputs=inputs.filter(i=>{const ph=i.placeholder||'';const nm=i.name||'';const lbl=i.closest('[class*=field],[class*=Field],[class*=form-item]')?.querySelector('label')?.innerText||'';return ph.includes('공급')||nm.includes('supply')||lbl.includes('공급');});if(supplyInputs.length>0){const val=toNum(supplyInputs[0].value);R.supplyValue=val;R.supplyMatch=val===1000000;}R.ok=true;R.info=[R.hasSupply1M?'✅ 공급 1,000,000':'⚠️ 공급 1,000,000 미감지',R.hasVat100K?'✅ 부가세 100,000':'⚠️ 부가세 100,000 미감지 (단가와 동일값 주의)',R.hasTotal1100K?'✅ 합계 1,100,000':'⚠️ 합계 1,100,000 미감지'].join(' | ');return JSON.stringify(R);})()",
"timeout": 10000,
"phase": "VERIFY"
},
{
"id": 23,
"name": "[회계관리 > 매출관리] [UPDATE] 저장 클릭",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const toastInfo=()=>{const t=document.querySelectorAll('[data-sonner-toast],[role=\"status\"],[class*=\"toast\"],[class*=\"Toast\"],[class*=\"Toaster\"] [data-content]');return{count:t.length,text:t.length>0?Array.from(t).pop()?.innerText?.trim().substring(0,100):''};};const R={phase:'UPDATE_SAVE'};const editBtn=document.querySelector('button');const saveBtn=Array.from(document.querySelectorAll('button')).find(b=>/저장|수정|확인/.test(b.innerText?.trim())&&b.offsetParent!==null&&!b.disabled);if(saveBtn){saveBtn.click();await w(3000);}R.toast=toastInfo();R.ok=true;return JSON.stringify(R);})()",
"timeout": 20000,
"phase": "UPDATE"
},
{
"id": 24,
"name": "[회계관리 > 매출관리] [UPDATE] API PUT/PATCH 검증",
"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.__E2E__?window.__E2E__.getApiLogs().logs:[]);const putLogs=logs.filter(l=>(l.method==='PUT'||l.method==='PATCH')&&l.status>=200&&l.status<300);R.putCount=putLogs.length;R.found=putLogs.length>0;if(putLogs.length>0){const last=putLogs[putLogs.length-1];R.url=last.url?.substring(0,80);R.status=last.status;R.duration=last.duration;}R.ok=true;R.info=R.found?'✅ PUT/PATCH '+R.status+' ('+R.duration+'ms)':'⚠️ PUT/PATCH 미감지';return JSON.stringify(R);})()",
"timeout": 5000,
"phase": "VERIFY"
},
{
"id": 25,
"name": "[회계관리 > 매출관리] [UPDATE] 저장 후 대기",
"action": "wait",
"timeout": 3000
},
{
"id": 26,
"name": "[회계관리 > 매출관리] [UPDATE] 수정 내용 검증 (공급가액 1,000,000 재계산)",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'VERIFY_UPDATE'};R.url=location.pathname+location.search;const notInEdit=!location.search.includes('mode=edit');const pageText=document.body.innerText;const inputs=Array.from(document.querySelectorAll('input,textarea')).filter(i=>i.offsetParent!==null);const hasModified=pageText.includes('수정됨')||inputs.some(i=>i.value?.includes('수정됨'));const hasNewSupply=pageText.includes('1,000,000')||pageText.includes('1000000');const toasts=document.querySelectorAll('[data-sonner-toast],[role=\"status\"],[class*=\"toast\"],[class*=\"Toast\"]');const toastOk=Array.from(toasts).some(t=>/수정|완료|저장|성공/.test(t.innerText));R.hasModified=hasModified;R.hasNewSupply=hasNewSupply;R.toastOk=toastOk;R.notInEdit=notInEdit;R.ok=notInEdit||hasModified||toastOk;if(R.ok&&!hasModified&&!toastOk)R.info='edit mode exited (save successful)';return JSON.stringify(R);})()",
"timeout": 15000,
"phase": "UPDATE"
},
{
"id": 27,
"name": "[회계관리 > 매출관리] [UPDATE] 목록 복귀",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const onForm=location.search.includes('mode=new')||location.search.includes('mode=edit')||location.search.includes('mode=view')||new RegExp('/(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);}}return JSON.stringify({url:location.pathname+location.search});})()",
"timeout": 10000,
"phase": "UPDATE"
},
{
"id": 28,
"name": "[회계관리 > 매출관리] [UPDATE] 목록 안정화 대기",
"action": "wait",
"timeout": 2000
},
{
"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__')||'E2E_TEST_';const toastInfo=()=>{const t=document.querySelectorAll('[data-sonner-toast],[role=\"status\"],[class*=\"toast\"],[class*=\"Toast\"],[class*=\"Toaster\"] [data-content]');return{count:t.length,text:t.length>0?Array.from(t).pop()?.innerText?.trim().substring(0,100):''};};const R={phase:'DELETE'};if(!location.search.includes('mode=view')&&!location.search.includes('mode=edit')){const rows=Array.from(document.querySelectorAll('table tbody tr'));const row=rows.find(r=>r.innerText?.includes('E2E_TEST_'));if(!row){R.error='E2E_TEST_ 행 없음';R.ok=false;return JSON.stringify(R);}row.click();await w(2500);}const delBtn=Array.from(document.querySelectorAll('button')).find(b=>b.innerText?.trim()==='삭제'&&b.offsetParent!==null);if(!delBtn){R.error='삭제 버튼 없음';R.ok=false;return JSON.stringify(R);}delBtn.click();await w(1000);const cfm=Array.from(document.querySelectorAll('[role=\"alertdialog\"] button,[role=\"dialog\"] button,button')).find(b=>/확인|삭제|예|Yes/.test(b.innerText?.trim())&&b!==delBtn&&b.offsetParent!==null);if(cfm){cfm.click();await w(3000);}R.toast=toastInfo();R.ok=true;return JSON.stringify(R);})()",
"timeout": 30000,
"phase": "DELETE",
"critical": true
},
{
"id": 30,
"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.__E2E__?window.__E2E__.getApiLogs().logs:[]);const delLogs=logs.filter(l=>l.method==='DELETE'&&l.status>=200&&l.status<300);R.deleteCount=delLogs.length;R.found=delLogs.length>0;if(delLogs.length>0){const last=delLogs[delLogs.length-1];R.url=last.url?.substring(0,80);R.status=last.status;R.duration=last.duration;}R.ok=true;R.info=R.found?'✅ DELETE '+R.status+' ('+R.duration+'ms)':'⚠️ DELETE 미감지';return JSON.stringify(R);})()",
"timeout": 5000,
"phase": "VERIFY"
},
{
"id": 31,
"name": "[회계관리 > 매출관리] [DELETE] 목록 복귀 + 대기",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const onForm=location.search.includes('mode=new')||location.search.includes('mode=edit')||location.search.includes('mode=view')||new RegExp('/(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(2000);return JSON.stringify({url:location.pathname+location.search});})()",
"timeout": 10000,
"phase": "DELETE"
},
{
"id": 32,
"name": "[회계관리 > 매출관리] [VERIFY] 삭제 확인 (목록에서 E2E_TEST_ 미존재)",
"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'};await w(1000);const rows=document.querySelectorAll('table tbody tr');R.rowCount=rows.length;const found=Array.from(rows).find(r=>r.innerText?.includes(ts));R.stillExists=!!found;R.ok=!found;R.ts=ts;R.info=R.ok?'✅ 삭제 완료 - 목록에 테스트 데이터 없음':'❌ 삭제 후에도 데이터 존재';return JSON.stringify(R);})()",
"timeout": 15000,
"phase": "VERIFY"
},
{
"id": 33,
"name": "[회계관리 > 매출관리] [VERIFY] API 호출 전체 요약",
"action": "evaluate",
"script": "(()=>{const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const R={phase:'API_SUMMARY',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:{}};logs.forEach(l=>{R.methods[l.method]=(R.methods[l.method]||0)+1;});R.ok=true;R.info='API: '+R.total+'건 (성공:'+R.success+' 실패:'+R.failed+' 평균:'+R.avgResponseTime+'ms)';return JSON.stringify(R);})()",
"timeout": 5000,
"phase": "VERIFY"
}
]
}