feat: 매출관리 집중 정밀 테스트 시나리오 8종 추가 (126스텝)

This commit is contained in:
김보곤
2026-02-13 20:02:18 +09:00
parent 026a3ce7b0
commit 9a4a9ed5db
8 changed files with 1069 additions and 0 deletions

185
multi-item-acc-sales.json Normal file
View File

@@ -0,0 +1,185 @@
{
"id": "multi-item-acc-sales",
"name": "다중 품목 등록 + 자동계산 + 품목삭제 재계산: 매출관리",
"version": "1.0.0",
"auth": {
"role": "admin"
},
"menuNavigation": {
"level1": "회계관리",
"level2": "매출관리"
},
"screenshotPolicy": {
"captureOnFail": true,
"captureOnPass": false
},
"steps": [
{
"id": 1,
"name": "[회계관리 > 매출관리] 페이지 로드 대기",
"action": "wait",
"timeout": 3000
},
{
"id": 2,
"name": "[회계관리 > 매출관리] 테이블 로드 대기",
"action": "wait_for_table",
"timeout": 8000
},
{
"id": 3,
"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);}btn.click();await w(2500);R.url=location.pathname+location.search;R.ok=true;return JSON.stringify(R);})()",
"timeout": 15000,
"phase": "CREATE"
},
{
"id": 4,
"name": "[회계관리 > 매출관리] [CREATE] 등록 폼 로드 대기",
"action": "wait",
"timeout": 2000
},
{
"id": 5,
"name": "[회계관리 > 매출관리] [CREATE] 기본정보 입력 (거래처+매출유형)",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));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;const R={phase:'BASIC_INFO',ts};const combos=Array.from(document.querySelectorAll('button[role=\"combobox\"]')).filter(b=>b.offsetParent!==null);R.comboCount=combos.length;for(let i=0;i<Math.min(combos.length,2);i++){const cb=combos[i];cb.click();await w(600);const lb=document.querySelector('[role=\"listbox\"]');if(lb){const opts=lb.querySelectorAll('[role=\"option\"]');if(i===1){const target=Array.from(opts).find(o=>o.innerText?.includes('제품매출'))||opts[0];if(target)target.click();}else{const opt=opts[0];if(opt)opt.click();}await w(400);}else{document.dispatchEvent(new KeyboardEvent('keydown',{key:'Escape',bubbles:true}));await w(200);}await w(300);}R.ok=true;return JSON.stringify(R);})()",
"timeout": 20000,
"phase": "CREATE"
},
{
"id": 6,
"name": "[회계관리 > 매출관리] [ITEM-A] 품목A 입력: 수량=3, 단가=10,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__||'';const R={phase:'ITEM_A'};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 itemInput=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('item')||lbl.includes('품목');});if(itemInput){sv(itemInput,'E2E_TEST_품목A_'+ts);R.itemFilled=true;await w(200);}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){sv(qtyInput,'3');R.qtyFilled=true;await w(200);}const priceInput=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('price')||nm.includes('unitPrice')||lbl.includes('단가');});if(priceInput){sv(priceInput,'10000');R.priceFilled=true;await w(300);}await w(500);const pageText=document.body.innerText;R.hasSupply30000=pageText.includes('30,000')||pageText.includes('30000');R.ok=true;return JSON.stringify(R);})()",
"timeout": 15000,
"phase": "CREATE"
},
{
"id": 7,
"name": "[회계관리 > 매출관리] [ITEM-A] 공급가액 30,000 확인",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'VERIFY_ITEM_A'};await w(500);const pageText=document.body.innerText;const inputs=Array.from(document.querySelectorAll('input')).filter(i=>i.offsetParent!==null);R.hasSupply30000=pageText.includes('30,000')||pageText.includes('30000')||inputs.some(i=>i.value==='30000'||i.value==='30,000');R.ok=true;R.info=R.hasSupply30000?'✅ 공급가액 30,000 확인':'⚠️ 공급가액 30,000 미감지';return JSON.stringify(R);})()",
"timeout": 10000,
"phase": "VERIFY"
},
{
"id": 8,
"name": "[회계관리 > 매출관리] [ITEM-B] 품목 추가 버튼(+) 클릭",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'ADD_ITEM_B'};const addBtn=Array.from(document.querySelectorAll('button')).find(b=>{const t=b.innerText?.trim()||'';return(t==='+'||t.includes('추가')||t.includes('품목 추가'))&&b.offsetParent!==null&&!b.disabled;});if(!addBtn){R.error='품목 추가 버튼 없음';R.ok=false;return JSON.stringify(R);}addBtn.click();await w(1000);R.ok=true;return JSON.stringify(R);})()",
"timeout": 10000,
"phase": "CREATE"
},
{
"id": 9,
"name": "[회계관리 > 매출관리] [ITEM-B] 품목B 입력: 수량=5, 단가=20,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__||'';const R={phase:'ITEM_B'};const allInputGroups=document.querySelectorAll('[class*=\"item-row\"],[class*=\"ItemRow\"],table tbody tr,div[class*=\"row\"]');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 itemInputs=inputs.filter(i=>{const ph=i.placeholder||'';const nm=i.name||'';return ph.includes('품목')||nm.includes('item');});if(itemInputs.length>=2){sv(itemInputs[1],'E2E_TEST_품목B_'+ts);R.itemFilled=true;await w(200);}else if(itemInputs.length===1){sv(itemInputs[0],'E2E_TEST_품목B_'+ts);R.itemFilled=true;await w(200);}const qtyInputs=inputs.filter(i=>{const ph=i.placeholder||'';const nm=i.name||'';return ph.includes('수량')||nm.includes('quantity')||nm.includes('qty');});const targetQty=qtyInputs.length>=2?qtyInputs[1]:qtyInputs.find(i=>!i.value||i.value==='0'||i.value==='');if(targetQty){sv(targetQty,'5');R.qtyFilled=true;await w(200);}const priceInputs=inputs.filter(i=>{const ph=i.placeholder||'';const nm=i.name||'';return ph.includes('단가')||nm.includes('price')||nm.includes('unitPrice');});const targetPrice=priceInputs.length>=2?priceInputs[1]:priceInputs.find(i=>!i.value||i.value==='0'||i.value==='');if(targetPrice){sv(targetPrice,'20000');R.priceFilled=true;await w(300);}await w(500);R.ok=true;return JSON.stringify(R);})()",
"timeout": 15000,
"phase": "CREATE"
},
{
"id": 10,
"name": "[회계관리 > 매출관리] [ITEM-B] 공급가액 100,000 확인",
"action": "evaluate",
"script": "(async()=>{const R={phase:'VERIFY_ITEM_B'};const pageText=document.body.innerText;const inputs=Array.from(document.querySelectorAll('input')).filter(i=>i.offsetParent!==null);R.hasSupply100000=pageText.includes('100,000')||pageText.includes('100000')||inputs.some(i=>i.value==='100000'||i.value==='100,000');R.ok=true;R.info=R.hasSupply100000?'✅ 공급가액 100,000 확인':'⚠️ 공급가액 100,000 미감지';return JSON.stringify(R);})()",
"timeout": 10000,
"phase": "VERIFY"
},
{
"id": 11,
"name": "[회계관리 > 매출관리] [ITEM-C] 품목 추가 버튼(+) 클릭",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'ADD_ITEM_C'};const addBtn=Array.from(document.querySelectorAll('button')).find(b=>{const t=b.innerText?.trim()||'';return(t==='+'||t.includes('추가')||t.includes('품목 추가'))&&b.offsetParent!==null&&!b.disabled;});if(addBtn){addBtn.click();await w(1000);R.ok=true;}else{R.error='품목 추가 버튼 없음';R.ok=false;}return JSON.stringify(R);})()",
"timeout": 10000,
"phase": "CREATE"
},
{
"id": 12,
"name": "[회계관리 > 매출관리] [ITEM-C] 품목C 입력: 수량=1, 단가=50,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__||'';const R={phase:'ITEM_C'};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 itemInputs=inputs.filter(i=>{const ph=i.placeholder||'';const nm=i.name||'';return ph.includes('품목')||nm.includes('item');});const emptyItem=itemInputs.find(i=>!i.value)||itemInputs[itemInputs.length-1];if(emptyItem){sv(emptyItem,'E2E_TEST_품목C_'+ts);R.itemFilled=true;await w(200);}const qtyInputs=inputs.filter(i=>{const ph=i.placeholder||'';const nm=i.name||'';return ph.includes('수량')||nm.includes('quantity')||nm.includes('qty');});const emptyQty=qtyInputs.find(i=>!i.value||i.value==='0'||i.value==='')||qtyInputs[qtyInputs.length-1];if(emptyQty){sv(emptyQty,'1');R.qtyFilled=true;await w(200);}const priceInputs=inputs.filter(i=>{const ph=i.placeholder||'';const nm=i.name||'';return ph.includes('단가')||nm.includes('price')||nm.includes('unitPrice');});const emptyPrice=priceInputs.find(i=>!i.value||i.value==='0'||i.value==='')||priceInputs[priceInputs.length-1];if(emptyPrice){sv(emptyPrice,'50000');R.priceFilled=true;await w(300);}await w(500);R.ok=true;return JSON.stringify(R);})()",
"timeout": 15000,
"phase": "CREATE"
},
{
"id": 13,
"name": "[회계관리 > 매출관리] [TOTAL-3] 3품목 합계 검증: 공급=180,000 부가세=18,000 합계=198,000",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'TOTAL_3_ITEMS'};await w(500);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.hasSupply180000=allVals.includes('180,000')||allVals.includes('180000');R.hasVat18000=allVals.includes('18,000')||allVals.includes('18000');R.hasTotal198000=allVals.includes('198,000')||allVals.includes('198000');R.ok=true;R.info=[R.hasSupply180000?'✅ 공급 180,000':'⚠️ 공급 180,000 미감지',R.hasVat18000?'✅ 부가세 18,000':'⚠️ 부가세 18,000 미감지',R.hasTotal198000?'✅ 합계 198,000':'⚠️ 합계 198,000 미감지'].join(' | ');return JSON.stringify(R);})()",
"timeout": 10000,
"phase": "VERIFY"
},
{
"id": 14,
"name": "[회계관리 > 매출관리] [DELETE-B] 품목B 삭제",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'DELETE_ITEM_B'};const delBtns=Array.from(document.querySelectorAll('button')).filter(b=>{const t=b.innerText?.trim()||'';const svg=b.querySelector('svg');return(t==='삭제'||t===''||t==='-'||t==='X'||t==='x'||svg)&&b.offsetParent!==null&&b.closest('table,div[class*=\"item\"],div[class*=\"Item\"],div[class*=\"row\"]');});R.delBtnCount=delBtns.length;if(delBtns.length>=2){delBtns[1].click();R.clickedIdx=1;await w(1000);}else if(delBtns.length===1){delBtns[0].click();R.clickedIdx=0;await w(1000);}else{const rows=document.querySelectorAll('[class*=\"item-row\"],[class*=\"ItemRow\"]');R.itemRowCount=rows.length;R.info='품목 삭제 버튼 미발견';R.ok=true;return JSON.stringify(R);}const cfm=Array.from(document.querySelectorAll('[role=\"alertdialog\"] button,[role=\"dialog\"] button,button')).find(b=>/확인|삭제|예/.test(b.innerText?.trim())&&b.offsetParent!==null);if(cfm){cfm.click();await w(1000);}R.ok=true;return JSON.stringify(R);})()",
"timeout": 15000,
"phase": "CREATE"
},
{
"id": 15,
"name": "[회계관리 > 매출관리] [DELETE-B] 품목삭제 후 대기",
"action": "wait",
"timeout": 1000
},
{
"id": 16,
"name": "[회계관리 > 매출관리] [TOTAL-2] 재계산 검증: 공급=80,000 부가세=8,000 합계=88,000",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'TOTAL_2_ITEMS'};await w(500);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.hasSupply80000=allVals.includes('80,000')||allVals.includes('80000');R.hasVat8000=allVals.includes('8,000')||allVals.includes('8000');R.hasTotal88000=allVals.includes('88,000')||allVals.includes('88000');R.ok=true;R.info=[R.hasSupply80000?'✅ 공급 80,000':'⚠️ 공급 80,000 미감지',R.hasVat8000?'✅ 부가세 8,000':'⚠️ 부가세 8,000 미감지',R.hasTotal88000?'✅ 합계 88,000':'⚠️ 합계 88,000 미감지'].join(' | ');return JSON.stringify(R);})()",
"timeout": 10000,
"phase": "VERIFY"
},
{
"id": 17,
"name": "[회계관리 > 매출관리] [SUBMIT] 등록 클릭",
"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": 18,
"name": "[회계관리 > 매출관리] [SUBMIT] 등록 후 대기 + 목록 복귀",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(2000);const onForm=location.search.includes('mode=new')||location.search.includes('mode=edit');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": 15000,
"phase": "CREATE"
},
{
"id": 19,
"name": "[회계관리 > 매출관리] [SUBMIT] 목록 안정화 대기",
"action": "wait",
"timeout": 2000
},
{
"id": 20,
"name": "[회계관리 > 매출관리] [VERIFY] 목록에서 합계 확인",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'VERIFY_LIST'};await w(500);const rows=document.querySelectorAll('table tbody tr');R.rowCount=rows.length;const found=Array.from(rows).find(r=>r.innerText?.includes('E2E_TEST_'));R.found=!!found;if(found){R.rowText=found.innerText?.substring(0,120);R.has88000=found.innerText?.includes('88,000')||found.innerText?.includes('88000');R.has80000=found.innerText?.includes('80,000')||found.innerText?.includes('80000');}R.ok=R.found;return JSON.stringify(R);})()",
"timeout": 10000,
"phase": "VERIFY"
},
{
"id": 21,
"name": "[회계관리 > 매출관리] [CLEANUP] 테스트 데이터 삭제",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const ts=window.__E2E_TS__||'E2E_TEST_';const R={phase:'CLEANUP'};const rows=Array.from(document.querySelectorAll('table tbody tr'));const row=rows.find(r=>r.innerText?.includes('E2E_TEST_'));if(!row){R.info='E2E_TEST_ 행 없음 - 삭제 스킵';R.ok=true;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=>/확인|삭제|예/.test(b.innerText?.trim())&&b!==delBtn&&b.offsetParent!==null);if(cfm){cfm.click();await w(3000);}R.ok=true;return JSON.stringify(R);})()",
"timeout": 30000,
"phase": "DELETE",
"critical": true
},
{
"id": 22,
"name": "[회계관리 > 매출관리] [CLEANUP] 삭제 확인",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(2000);const onForm=location.search.includes('mode=view')||location.search.includes('mode=edit');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=document.querySelectorAll('table tbody tr');const found=Array.from(rows).find(r=>r.innerText?.includes(window.__E2E_TS__||'IMPOSSIBLE'));return JSON.stringify({phase:'VERIFY_CLEANUP',stillExists:!!found,ok:!found});})()",
"timeout": 15000,
"phase": "VERIFY"
}
]
}