feat: 매출관리 집중 정밀 테스트 시나리오 8종 추가 (126스텝)
This commit is contained in:
120
batch-update-account-sales.json
Normal file
120
batch-update-account-sales.json
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
{
|
||||||
|
"id": "batch-update-account-sales",
|
||||||
|
"name": "계정과목 일괄변경 버그 회귀 테스트 (BUG-SALES-20260115-001): 매출관리",
|
||||||
|
"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": "[회계관리 > 매출관리] [CAPTURE] 첫 행 현재 매출유형 캡처",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CAPTURE_BEFORE'};await w(500);const rows=document.querySelectorAll('table tbody tr');R.rowCount=rows.length;if(rows.length===0){R.error='테이블에 데이터 없음';R.ok=false;return JSON.stringify(R);}const firstRow=rows[0];const cells=firstRow.querySelectorAll('td');R.cellCount=cells.length;const typeColIdx=[5,4,3].find(idx=>cells[idx]&&cells[idx].innerText?.trim()!=='');const typeValue=typeColIdx!==undefined?cells[typeColIdx]?.innerText?.trim():'';R.beforeType=typeValue;window.__E2E_BEFORE_TYPE__=typeValue;R.firstRowText=firstRow.innerText?.substring(0,100);R.ok=true;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 15000,
|
||||||
|
"phase": "CAPTURE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"name": "[회계관리 > 매출관리] [SELECT] 첫 행 체크박스 선택",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'SELECT_ROW'};const rows=document.querySelectorAll('table tbody tr');if(rows.length===0){R.error='행 없음';R.ok=false;return JSON.stringify(R);}const firstRow=rows[0];const checkbox=firstRow.querySelector('input[type=\"checkbox\"],button[role=\"checkbox\"]');if(checkbox){checkbox.click();await w(500);R.checked=true;}else{firstRow.querySelector('td')?.click();await w(500);R.clickedFirstCell=true;}R.ok=true;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 10000,
|
||||||
|
"phase": "SELECT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"name": "[회계관리 > 매출관리] [SELECT] 계정과목 드롭다운에서 다른 값 선택",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CHANGE_ACCOUNT'};const beforeType=window.__E2E_BEFORE_TYPE__||'';const combos=Array.from(document.querySelectorAll('button[role=\"combobox\"],select')).filter(b=>b.offsetParent!==null);const labels=Array.from(document.querySelectorAll('label')).filter(l=>/계정과목|매출유형/.test(l.innerText));R.comboCount=combos.length;R.labelCount=labels.length;let targetCombo=null;for(const cb of combos){const lbl=cb.closest('[class*=field],[class*=Field],[class*=form-item]')?.querySelector('label')?.innerText||'';if(lbl.includes('계정과목')||lbl.includes('매출유형')){targetCombo=cb;break;}}if(!targetCombo&&combos.length>0){targetCombo=combos[0];}if(!targetCombo){const selectEls=document.querySelectorAll('select');for(const sel of selectEls){const lbl=sel.closest('[class*=field],[class*=Field],[class*=form-item]')?.querySelector('label')?.innerText||'';if(lbl.includes('계정과목')){targetCombo=sel;break;}}if(!targetCombo&&selectEls.length>0)targetCombo=selectEls[0];}if(!targetCombo){R.error='계정과목 콤보박스 없음';R.ok=false;return JSON.stringify(R);}if(targetCombo.tagName==='SELECT'){const opts=targetCombo.options;for(let i=0;i<opts.length;i++){if(opts[i].text!==beforeType&&opts[i].value){targetCombo.selectedIndex=i;targetCombo.dispatchEvent(new Event('change',{bubbles:true}));R.selectedValue=opts[i].text;break;}}R.ok=true;}else{targetCombo.click();await w(600);const lb=document.querySelector('[role=\"listbox\"]');if(lb){const opts=Array.from(lb.querySelectorAll('[role=\"option\"]'));const diff=opts.find(o=>o.innerText?.trim()!==beforeType&&o.innerText?.trim()!==''&&o.innerText?.trim()!=='미설정');if(diff){diff.click();R.selectedValue=diff.innerText?.trim();await w(400);}else if(opts[0]){opts[0].click();R.selectedValue=opts[0].innerText?.trim();await w(400);}}else{document.dispatchEvent(new KeyboardEvent('keydown',{key:'Escape',bubbles:true}));await w(200);}R.ok=true;}window.__E2E_NEW_TYPE__=R.selectedValue||'';return JSON.stringify(R);})()",
|
||||||
|
"timeout": 15000,
|
||||||
|
"phase": "SELECT"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"name": "[회계관리 > 매출관리] [SAVE] 저장 버튼 클릭",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'SAVE'};const saveBtn=Array.from(document.querySelectorAll('button')).find(b=>b.innerText?.trim()==='저장'&&b.offsetParent!==null&&!b.disabled);if(!saveBtn){R.error='저장 버튼 없음';R.ok=false;return JSON.stringify(R);}saveBtn.click();await w(1500);R.ok=true;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 10000,
|
||||||
|
"phase": "SAVE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"name": "[회계관리 > 매출관리] [SAVE] 확인 다이얼로그 → 확인 클릭",
|
||||||
|
"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:'CONFIRM'};const dlg=document.querySelector('[role=\"alertdialog\"],[role=\"dialog\"]');if(dlg&&dlg.offsetParent!==null){R.dialogText=dlg.innerText?.substring(0,100);const cfmBtn=Array.from(dlg.querySelectorAll('button')).find(b=>/확인|저장|예|Yes/.test(b.innerText?.trim()));if(cfmBtn){cfmBtn.click();await w(3000);}R.dialogConfirmed=true;}else{R.dialogFound=false;await w(2000);}R.toast=toastInfo();R.ok=true;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 15000,
|
||||||
|
"phase": "SAVE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"name": "[회계관리 > 매출관리] [VERIFY-1] 토스트 메시지 확인",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'VERIFY_TOAST'};await w(1000);const toasts=document.querySelectorAll('[data-sonner-toast],[role=\"status\"],[class*=\"toast\"],[class*=\"Toast\"],[class*=\"Toaster\"] [data-content]');R.toastCount=toasts.length;R.toastTexts=Array.from(toasts).map(t=>t.innerText?.trim().substring(0,80)).filter(Boolean);const successToast=R.toastTexts.some(t=>/성공|완료|저장|변경/.test(t));R.successToast=successToast;R.ok=true;R.info=successToast?'✅ 성공 토스트 확인':'⚠️ 성공 토스트 미감지';return JSON.stringify(R);})()",
|
||||||
|
"timeout": 10000,
|
||||||
|
"phase": "VERIFY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"name": "[회계관리 > 매출관리] [VERIFY-2] ★핵심★ 첫 행 매출유형 실제 변경 확인",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'VERIFY_DATA_CHANGED'};await w(1000);const beforeType=window.__E2E_BEFORE_TYPE__||'';const newType=window.__E2E_NEW_TYPE__||'';const rows=document.querySelectorAll('table tbody tr');if(rows.length===0){R.error='테이블 행 없음';R.ok=false;return JSON.stringify(R);}const firstRow=rows[0];const cells=firstRow.querySelectorAll('td');const currentTypes=Array.from(cells).map(c=>c.innerText?.trim()).filter(Boolean);R.beforeType=beforeType;R.expectedNewType=newType;R.currentCellValues=currentTypes.slice(0,8);const changed=newType&¤tTypes.some(v=>v.includes(newType));const unchanged=beforeType&¤tTypes.some(v=>v===beforeType);R.dataActuallyChanged=changed;R.dataStillOld=unchanged&&!changed;R.ok=true;if(R.dataStillOld&&beforeType!==newType){R.bugDetected=true;R.info='🐛 BUG-SALES-20260115-001 재현: 토스트 성공 but 데이터 미변경 (beforeType='+beforeType+', expected='+newType+')';}else if(R.dataActuallyChanged){R.info='✅ 데이터 실제 변경 확인 ('+beforeType+' → '+newType+')';}else{R.info='⚠️ 변경 확인 불가 (beforeType='+beforeType+', newType='+newType+')';}return JSON.stringify(R);})()",
|
||||||
|
"timeout": 15000,
|
||||||
|
"phase": "VERIFY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"name": "[회계관리 > 매출관리] [RELOAD] 새로고침",
|
||||||
|
"action": "reload",
|
||||||
|
"timeout": 10000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"name": "[회계관리 > 매출관리] [RELOAD] 새로고침 후 대기",
|
||||||
|
"action": "wait",
|
||||||
|
"timeout": 5000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"name": "[회계관리 > 매출관리] [RELOAD] 테이블 재로드 대기",
|
||||||
|
"action": "wait_for_table",
|
||||||
|
"timeout": 8000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"name": "[회계관리 > 매출관리] [VERIFY-3] ★핵심★ 새로고침 후에도 변경값 유지 확인",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'VERIFY_PERSIST'};await w(1000);const beforeType=window.__E2E_BEFORE_TYPE__||'';const newType=window.__E2E_NEW_TYPE__||'';const rows=document.querySelectorAll('table tbody tr');if(rows.length===0){R.error='테이블 행 없음 after reload';R.ok=false;return JSON.stringify(R);}const firstRow=rows[0];const cells=firstRow.querySelectorAll('td');const currentTypes=Array.from(cells).map(c=>c.innerText?.trim()).filter(Boolean);R.beforeType=beforeType;R.expectedNewType=newType;R.currentCellValues=currentTypes.slice(0,8);const persisted=newType&¤tTypes.some(v=>v.includes(newType));const reverted=beforeType&¤tTypes.some(v=>v===beforeType)&&!persisted;R.persistedAfterReload=persisted;R.revertedAfterReload=reverted;R.ok=true;if(reverted&&beforeType!==newType){R.bugDetected=true;R.info='🐛 BUG-SALES-20260115-001 재현: 새로고침 후 원래값 복귀 ('+beforeType+')';}else if(persisted){R.info='✅ 새로고침 후에도 변경값 유지 ('+newType+')';}else{R.info='⚠️ 새로고침 후 상태 확인 불가';}return JSON.stringify(R);})()",
|
||||||
|
"timeout": 15000,
|
||||||
|
"phase": "VERIFY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 14,
|
||||||
|
"name": "[회계관리 > 매출관리] [RESTORE] 원래 값 복원 (선택적)",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'RESTORE'};const beforeType=window.__E2E_BEFORE_TYPE__||'';if(!beforeType){R.info='원래 값 없음 - 복원 스킵';R.ok=true;return JSON.stringify(R);}const rows=document.querySelectorAll('table tbody tr');if(rows.length===0){R.ok=true;return JSON.stringify(R);}const firstRow=rows[0];const checkbox=firstRow.querySelector('input[type=\"checkbox\"],button[role=\"checkbox\"]');if(checkbox)checkbox.click();await w(500);const combos=Array.from(document.querySelectorAll('button[role=\"combobox\"],select')).filter(b=>b.offsetParent!==null);if(combos.length>0){const cb=combos[0];if(cb.tagName==='SELECT'){for(let i=0;i<cb.options.length;i++){if(cb.options[i].text===beforeType){cb.selectedIndex=i;cb.dispatchEvent(new Event('change',{bubbles:true}));break;}}}else{cb.click();await w(600);const lb=document.querySelector('[role=\"listbox\"]');if(lb){const opt=Array.from(lb.querySelectorAll('[role=\"option\"]')).find(o=>o.innerText?.trim()===beforeType);if(opt){opt.click();await w(400);}else{document.dispatchEvent(new KeyboardEvent('keydown',{key:'Escape',bubbles:true}));await w(200);}}}}const saveBtn=Array.from(document.querySelectorAll('button')).find(b=>b.innerText?.trim()==='저장'&&b.offsetParent!==null);if(saveBtn){saveBtn.click();await w(1500);const cfm=Array.from(document.querySelectorAll('[role=\"alertdialog\"] button,[role=\"dialog\"] button')).find(b=>/확인/.test(b.innerText?.trim()));if(cfm){cfm.click();await w(2000);}}R.info='원래 값 복원 시도 완료';R.ok=true;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 20000,
|
||||||
|
"phase": "RESTORE"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
106
detail-verify-acc-sales.json
Normal file
106
detail-verify-acc-sales.json
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
{
|
||||||
|
"id": "detail-verify-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": "[회계관리 > 매출관리] [CAPTURE] 첫 행 모든 셀 값 캡처",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CAPTURE'};await w(500);const rows=document.querySelectorAll('table tbody tr');R.rowCount=rows.length;if(rows.length===0){R.error='테이블에 데이터 없음';R.ok=false;return JSON.stringify(R);}const firstRow=rows[0];const cells=firstRow.querySelectorAll('td');R.cellCount=cells.length;const captured={};const colNames=['checkbox','no','salesNo','vendorName','salesDate','salesType','supplyAmount','vat','totalAmount','taxInvoice','transStatement'];cells.forEach((cell,i)=>{const key=colNames[i]||('col'+i);const text=cell.innerText?.trim()||'';const input=cell.querySelector('input[type=\"checkbox\"]');if(input){captured[key]=input.checked?'checked':'unchecked';}else{captured[key]=text;}});window.__E2E_CAPTURED__=captured;R.captured=captured;R.ok=true;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 15000,
|
||||||
|
"phase": "CAPTURE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"name": "[회계관리 > 매출관리] [READ] 첫 행 클릭 → 상세 진입",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'READ'};const rows=document.querySelectorAll('table tbody tr');if(rows.length===0){R.error='테이블 행 없음';R.ok=false;return JSON.stringify(R);}rows[0].click();await w(2500);R.detailUrl=location.pathname+location.search;R.ok=true;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 15000,
|
||||||
|
"phase": "READ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"name": "[회계관리 > 매출관리] [READ] 상세 페이지 로드 대기",
|
||||||
|
"action": "wait",
|
||||||
|
"timeout": 2000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"name": "[회계관리 > 매출관리] [VERIFY] 상세 페이지 필드 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 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=cap[key];if(!val||val==='')return;const cleanVal=val.replace(/,/g,'').trim();const found=pageText.includes(val)||pageText.includes(cleanVal)||allValues.some(v=>v?.includes(val)||v?.includes(cleanVal));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,totalChecks-2);return JSON.stringify(R);})()",
|
||||||
|
"timeout": 15000,
|
||||||
|
"phase": "VERIFY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"name": "[회계관리 > 매출관리] [VERIFY] 세금계산서/거래명세서 Switch 상태 확인",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'SWITCH_VERIFY'};const pageText=document.body.innerText;R.hasTaxInvoice=pageText.includes('세금계산서');R.hasTransStatement=pageText.includes('거래명세서');const switches=document.querySelectorAll('button[role=\"switch\"],input[type=\"checkbox\"][role=\"switch\"],[class*=\"switch\"],[class*=\"Switch\"]');R.switchCount=switches.length;R.switchStates=Array.from(switches).map((sw,i)=>{const lbl=sw.closest('[class*=field],[class*=Field],[class*=form-item]')?.querySelector('label')?.innerText||('switch_'+i);const checked=sw.getAttribute('aria-checked')==='true'||sw.checked||sw.getAttribute('data-state')==='checked';return{label:lbl.trim().substring(0,20),checked};});R.ok=true;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 10000,
|
||||||
|
"phase": "VERIFY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"name": "[회계관리 > 매출관리] [VERIFY] 수정 모드 진입 가능 확인",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'EDIT_ACCESS'};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": "VERIFY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"name": "[회계관리 > 매출관리] [CANCEL] 취소 클릭",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CANCEL'};const cancelBtn=Array.from(document.querySelectorAll('button,a')).find(b=>/취소|목록|뒤로/.test(b.innerText?.trim())&&b.offsetParent!==null);if(cancelBtn){cancelBtn.click();await w(2000);}else{history.back();await w(2000);}R.url=location.pathname+location.search;R.ok=true;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 10000,
|
||||||
|
"phase": "CANCEL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"name": "[회계관리 > 매출관리] [CANCEL] 목록 복귀 대기",
|
||||||
|
"action": "wait",
|
||||||
|
"timeout": 2000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"name": "[회계관리 > 매출관리] [VERIFY] 목록 복귀 후 테이블 확인",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'BACK_VERIFY'};await w(500);R.url=location.pathname+location.search;R.isListPage=location.pathname.includes('/accounting/sales')&&!location.search.includes('mode=');const rows=document.querySelectorAll('table tbody tr');R.rowCount=rows.length;R.ok=R.rowCount>0;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 10000,
|
||||||
|
"phase": "VERIFY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"name": "[회계관리 > 매출관리] [VERIFY] 취소 후 데이터 무변경 확인",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'NO_CHANGE_VERIFY'};const cap=window.__E2E_CAPTURED__||{};if(!cap.salesNo){R.ok=true;R.info='캡처 데이터 없어 스킵';return JSON.stringify(R);}await w(500);const rows=document.querySelectorAll('table tbody tr');if(rows.length===0){R.ok=true;R.info='빈 테이블';return JSON.stringify(R);}const firstRow=rows[0];const cells=firstRow.querySelectorAll('td');const salesNoCell=cells[2]?.innerText?.trim()||'';R.expectedSalesNo=cap.salesNo;R.actualSalesNo=salesNoCell;R.noChange=salesNoCell===cap.salesNo||salesNoCell.includes(cap.salesNo);R.ok=true;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 10000,
|
||||||
|
"phase": "VERIFY"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
122
edge-boundary-acc-sales.json
Normal file
122
edge-boundary-acc-sales.json
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
{
|
||||||
|
"id": "edge-boundary-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": "[회계관리 > 매출관리] [EDGE] 등록 폼 열기",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'OPEN_FORM'};const priorities=['매출 등록','매출등록','등록','추가','신규'];let btn=null;for(const kw of priorities){btn=Array.from(document.querySelectorAll('button')).find(b=>{const t=b.innerText?.trim()||'';return t.includes(kw)&&b.offsetParent!==null&&!b.disabled;});if(btn)break;}if(!btn){R.err='등록 버튼 없음';R.ok=true;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": "OPEN_FORM"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"name": "[회계관리 > 매출관리] [EDGE] 폼 렌더링 대기",
|
||||||
|
"action": "wait",
|
||||||
|
"timeout": 2000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"name": "[회계관리 > 매출관리] [EDGE] 수량=0 입력 → 자동계산 반응 확인",
|
||||||
|
"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:'ZERO_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=>(i.placeholder||'').includes('수량')||(i.name||'').includes('quantity')||(i.name||'').includes('qty'));if(qtyInput){sv(qtyInput,'0');await w(500);R.qtySet=true;R.qtyValue=qtyInput.value;const supplyInputs=inputs.filter(i=>(i.placeholder||'').includes('공급')||(i.name||'').includes('supply')||(i.name||'').includes('amount'));R.supplyValue=supplyInputs.length>0?supplyInputs[0].value:'N/A';const hasError=document.querySelector('[class*=\"error\"],[class*=\"Error\"],[role=\"alert\"],.text-red-500,.text-destructive');R.errorShown=!!hasError;}else{R.qtyInputNotFound=true;const numInputs=inputs.filter(i=>i.type==='number'||(i.placeholder||'').match(/[0-9]/));if(numInputs.length>0){sv(numInputs[0],'0');await w(500);R.fallbackSet=true;}}R.ok=true;R.info=R.qtySet?'수량=0 입력 완료, 공급가액='+R.supplyValue:'수량 필드 미발견';return JSON.stringify(R);})()",
|
||||||
|
"timeout": 10000,
|
||||||
|
"phase": "BOUNDARY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"name": "[회계관리 > 매출관리] [EDGE] 수량=-1 입력 → 거부/에러 확인",
|
||||||
|
"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:'NEGATIVE_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=>(i.placeholder||'').includes('수량')||(i.name||'').includes('quantity')||(i.name||'').includes('qty'));if(qtyInput){sv(qtyInput,'-1');await w(500);R.qtySet=true;R.qtyValue=qtyInput.value;R.qtyAccepted=qtyInput.value==='-1';const hasError=document.querySelector('[class*=\"error\"],[class*=\"Error\"],[role=\"alert\"],.text-red-500,.text-destructive');R.errorShown=!!hasError;if(hasError){R.errorText=hasError.innerText?.trim().substring(0,80);}R.ariaInvalid=qtyInput.getAttribute('aria-invalid')==='true';}else{R.qtyInputNotFound=true;const numInputs=inputs.filter(i=>i.type==='number');if(numInputs.length>0){sv(numInputs[0],'-1');await w(500);R.fallbackSet=true;R.fallbackAccepted=numInputs[0].value==='-1';}}R.ok=true;R.info=R.errorShown?'✅ 음수 입력 시 에러 표시':'⚠️ 음수 입력 에러 미표시';return JSON.stringify(R);})()",
|
||||||
|
"timeout": 10000,
|
||||||
|
"phase": "BOUNDARY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"name": "[회계관리 > 매출관리] [EDGE] 단가=99999.99 소수점 입력 → 처리 확인",
|
||||||
|
"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:'DECIMAL_PRICE'};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 priceInput=inputs.find(i=>(i.placeholder||'').includes('단가')||(i.name||'').includes('price')||(i.name||'').includes('unitPrice'));if(priceInput){sv(priceInput,'99999.99');await w(500);R.priceSet=true;R.displayValue=priceInput.value;R.decimalKept=priceInput.value.includes('.');R.rounded=!priceInput.value.includes('.')&&priceInput.value!=='';const hasError=document.querySelector('[class*=\"error\"],[class*=\"Error\"],[role=\"alert\"],.text-red-500,.text-destructive');R.errorShown=!!hasError;}else{R.priceInputNotFound=true;const numInputs=inputs.filter(i=>i.type==='number'||(i.placeholder||'').match(/단가|금액|price/i));if(numInputs.length>1){sv(numInputs[1],'99999.99');await w(500);R.fallbackSet=true;R.fallbackValue=numInputs[1].value;}}R.ok=true;R.info=R.decimalKept?'소수점 유지됨: '+R.displayValue:(R.rounded?'소수점 반올림/제거됨: '+R.displayValue:'단가 필드 미발견');return JSON.stringify(R);})()",
|
||||||
|
"timeout": 10000,
|
||||||
|
"phase": "BOUNDARY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"name": "[회계관리 > 매출관리] [EDGE] 품목명 255자 초과 입력 → 잘림/에러 확인",
|
||||||
|
"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:'MAX_LENGTH'};const longStr='E2E_TEST_LONG_'+'A'.repeat(260);const inputs=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type]),textarea')).filter(i=>i.offsetParent!==null&&!i.readOnly&&!i.disabled);const itemInput=inputs.find(i=>(i.placeholder||'').includes('품목')||(i.name||'').includes('item')||(i.name||'').includes('product'));if(itemInput){sv(itemInput,longStr);await w(500);R.inputSet=true;R.inputLength=itemInput.value.length;R.maxLengthAttr=itemInput.maxLength||'none';R.truncated=itemInput.value.length<longStr.length;R.exactLength=itemInput.value.length;const hasError=document.querySelector('[class*=\"error\"],[class*=\"Error\"],[role=\"alert\"],.text-red-500,.text-destructive');R.errorShown=!!hasError;}else{R.itemInputNotFound=true;if(inputs.length>0){sv(inputs[0],longStr);await w(500);R.fallbackSet=true;R.fallbackLength=inputs[0].value.length;R.fallbackTruncated=inputs[0].value.length<longStr.length;}}R.ok=true;R.info=R.truncated?'✅ 255자 초과 입력 잘림 ('+R.exactLength+'자)':(R.errorShown?'✅ 255자 초과 시 에러 표시':'⚠️ 255자 초과 입력이 그대로 수용됨 ('+R.inputLength+'자)');return JSON.stringify(R);})()",
|
||||||
|
"timeout": 10000,
|
||||||
|
"phase": "BOUNDARY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"name": "[회계관리 > 매출관리] [EDGE] 특수문자/XSS 입력 → 방어 확인",
|
||||||
|
"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:'XSS_CHECK'};const xssPayload='<script>alert(1)</script><img onerror=alert(1) src=x>';const inputs=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type]),textarea')).filter(i=>i.offsetParent!==null&&!i.readOnly&&!i.disabled);const itemInput=inputs.find(i=>(i.placeholder||'').includes('품목')||(i.name||'').includes('item')||(i.name||'').includes('product'));const targetInput=itemInput||inputs[0];if(targetInput){sv(targetInput,xssPayload);await w(500);R.inputSet=true;R.storedValue=targetInput.value;R.xssAccepted=targetInput.value.includes('<script>');R.sanitized=!targetInput.value.includes('<script>');const hasError=document.querySelector('[class*=\"error\"],[class*=\"Error\"],[role=\"alert\"],.text-red-500,.text-destructive');R.errorShown=!!hasError;const scriptTags=document.querySelectorAll('script:not([src])');R.injectedScripts=Array.from(scriptTags).filter(s=>s.textContent?.includes('alert(1)')).length;}else{R.inputNotFound=true;}R.ok=true;R.info=R.sanitized?'✅ XSS 입력 새니타이징됨':(R.errorShown?'✅ XSS 입력 시 에러 표시':'⚠️ XSS 페이로드가 그대로 수용됨 - 서버 측 방어 확인 필요');return JSON.stringify(R);})()",
|
||||||
|
"timeout": 10000,
|
||||||
|
"phase": "BOUNDARY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"name": "[회계관리 > 매출관리] [EDGE] 빈 폼 저장 시도 → 유효성 검사 확인",
|
||||||
|
"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:'EMPTY_SUBMIT'};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);for(const inp of inputs){sv(inp,'');await w(100);}await w(500);const beforeErrors=document.querySelectorAll('[class*=\"error\"],[class*=\"Error\"],[class*=\"destructive\"],[role=\"alert\"]').length;const submitBtn=Array.from(document.querySelectorAll('button')).find(b=>{const t=b.innerText?.trim()||'';return(/등록|저장|확인|제출/.test(t))&&b.offsetParent!==null&&!b.disabled;});if(!submitBtn){R.err='저장/등록 버튼 없음';R.ok=true;return JSON.stringify(R);}R.submitBtnText=submitBtn.innerText?.trim();submitBtn.click();await w(2000);const toasts=document.querySelectorAll('[data-sonner-toast],[role=\"status\"],[class*=\"toast\"],[class*=\"Toast\"],[class*=\"Toaster\"] [data-content]');R.toastCount=toasts.length;if(toasts.length>0){R.toastTexts=Array.from(toasts).map(t=>t.innerText?.trim().substring(0,80)).filter(Boolean);}const errors=document.querySelectorAll('[class*=\"error\"],[class*=\"Error\"],[class*=\"destructive\"],[role=\"alert\"],[class*=\"invalid\"]');R.errorCount=errors.length;R.newErrors=errors.length-beforeErrors;if(errors.length>0){R.errorTexts=Array.from(errors).slice(0,5).map(e=>e.innerText?.trim().substring(0,60)).filter(Boolean);}const invalidFields=document.querySelectorAll('[aria-invalid=\"true\"]');R.ariaInvalidCount=invalidFields.length;const redBorders=Array.from(document.querySelectorAll('input,textarea,select,[role=\"combobox\"]')).filter(el=>{const cs=getComputedStyle(el);return cs.borderColor?.includes('rgb(239')||cs.borderColor?.includes('rgb(220')||cs.borderColor?.includes('rgb(248');});R.redBorderCount=redBorders.length;const dialogs=document.querySelectorAll('[role=\"alertdialog\"],[role=\"dialog\"]');const validationDialog=Array.from(dialogs).find(d=>d.offsetParent!==null);R.hasValidationDialog=!!validationDialog;if(validationDialog){R.dialogText=validationDialog.innerText?.trim().substring(0,100);}R.totalValidationSignals=R.toastCount+R.newErrors+R.ariaInvalidCount+R.redBorderCount+(R.hasValidationDialog?1:0);R.validationTriggered=R.totalValidationSignals>0;R.ok=true;R.info=R.validationTriggered?'✅ 빈 폼 제출 시 유효성 검사 정상 동작 (시그널 '+R.totalValidationSignals+'개)':'⚠️ 빈 폼 제출 시 유효성 검사 미감지';return JSON.stringify(R);})()",
|
||||||
|
"timeout": 15000,
|
||||||
|
"phase": "BOUNDARY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"name": "[회계관리 > 매출관리] [EDGE] 빈 폼 제출 후 대기",
|
||||||
|
"action": "wait",
|
||||||
|
"timeout": 2000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"name": "[회계관리 > 매출관리] [EDGE] 유효성 검사 다이얼로그 닫기",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CLOSE_VALIDATION'};const dlg=document.querySelector('[role=\"alertdialog\"],[role=\"dialog\"]');if(dlg&&dlg.offsetParent!==null){const closeBtn=dlg.querySelector('button[class*=\"close\"]')||Array.from(dlg.querySelectorAll('button')).find(b=>/닫기|확인|취소|Close/.test(b.innerText?.trim()));if(closeBtn){closeBtn.click();await w(500);R.dialogClosed=true;}else{document.dispatchEvent(new KeyboardEvent('keydown',{key:'Escape',bubbles:true}));await w(500);R.escapeSent=true;}}else{R.noDialog=true;}R.ok=true;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 5000,
|
||||||
|
"phase": "BOUNDARY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"name": "[회계관리 > 매출관리] [EDGE] 경계값 종합 평가",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const R={phase:'BOUNDARY_SUMMARY'};R.tests=['수량=0 자동계산','수량=-1 음수 거부','단가=99999.99 소수점','품목명 255자 초과','XSS 특수문자 방어','빈 폼 유효성 검사'];R.info='경계값 테스트 6개 항목 실행 완료';R.ok=true;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 5000,
|
||||||
|
"phase": "SUMMARY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 14,
|
||||||
|
"name": "[회계관리 > 매출관리] [CLOSE] 폼/모달 닫기 → 목록 복귀",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CLOSE_FORM'};const dlg=document.querySelector('[role=\"alertdialog\"],[role=\"dialog\"]');if(dlg&&dlg.offsetParent!==null){const closeBtn=dlg.querySelector('button[class*=\"close\"]')||Array.from(dlg.querySelectorAll('button')).find(b=>/닫기|확인|취소|Close/.test(b.innerText?.trim()));if(closeBtn){closeBtn.click();await w(500);}else{document.dispatchEvent(new KeyboardEvent('keydown',{key:'Escape',bubbles:true}));await w(500);}}if(location.search.includes('mode=new')||location.search.includes('mode=edit')){const backBtn=Array.from(document.querySelectorAll('button,a')).find(b=>/목록|취소|뒤로/.test(b.innerText?.trim()));if(backBtn){backBtn.click();await w(2000);}else{history.back();await w(2000);}}const modal=document.querySelector('[role=\"dialog\"],[aria-modal=\"true\"],[class*=\"modal\"]:not([class*=\"tooltip\"])');if(modal&&modal.offsetParent!==null){const xBtn=modal.querySelector('button[class*=\"close\"],[aria-label=\"닫기\"],[aria-label=\"Close\"]')||Array.from(modal.querySelectorAll('button')).find(b=>/닫기|취소|Close/.test(b.innerText?.trim()));if(xBtn){xBtn.click();await w(500);}else{document.dispatchEvent(new KeyboardEvent('keydown',{key:'Escape',bubbles:true}));await w(500);}}R.url=location.pathname+location.search;R.ok=true;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 10000,
|
||||||
|
"phase": "CLOSE_FORM"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
90
edge-rapid-click-acc-sales.json
Normal file
90
edge-rapid-click-acc-sales.json
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
{
|
||||||
|
"id": "edge-rapid-click-acc-sales",
|
||||||
|
"name": "엣지 케이스: UI 내구성 연타 테스트 (회계 > 매출관리)",
|
||||||
|
"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": "[회계관리 > 매출관리] [RAPID] 헤더 체크박스 10회 연타 → 최종 상태 일관성",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'RAPID_HEADER_CHECKBOX'};const headerCb=document.querySelector('table thead input[type=\"checkbox\"],table thead button[role=\"checkbox\"]');if(!headerCb){R.err='헤더 체크박스 없음';R.ok=true;return JSON.stringify(R);}let clickCount=0;for(let i=0;i<10;i++){headerCb.click();clickCount++;await w(50);}R.clickCount=clickCount;await w(1000);const isChecked=headerCb.checked||headerCb.getAttribute('aria-checked')==='true'||headerCb.getAttribute('data-state')==='checked';R.finalState=isChecked?'checked':'unchecked';R.expectedState='unchecked';R.consistent=R.finalState===R.expectedState;const bodyCheckboxes=document.querySelectorAll('table tbody input[type=\"checkbox\"],table tbody button[role=\"checkbox\"]');const checkedCount=Array.from(bodyCheckboxes).filter(cb=>cb.checked||cb.getAttribute('aria-checked')==='true'||cb.getAttribute('data-state')==='checked').length;R.bodyTotal=bodyCheckboxes.length;R.bodyChecked=checkedCount;R.allConsistent=(isChecked&&checkedCount===bodyCheckboxes.length)||(!isChecked&&checkedCount===0);R.ok=true;R.info=R.allConsistent?'✅ 10회 연타 후 체크박스 일관성 유지 ('+R.finalState+', body: '+checkedCount+'/'+bodyCheckboxes.length+')':'⚠️ 체크박스 상태 불일치 (header='+R.finalState+', body='+checkedCount+'/'+bodyCheckboxes.length+')';return JSON.stringify(R);})()",
|
||||||
|
"timeout": 15000,
|
||||||
|
"phase": "RAPID_CLICK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"name": "[회계관리 > 매출관리] [RAPID] 체크박스 연타 후 안정화 대기",
|
||||||
|
"action": "wait",
|
||||||
|
"timeout": 1000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"name": "[회계관리 > 매출관리] [RAPID] 등록 폼 열기",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'OPEN_FORM'};const priorities=['매출 등록','매출등록','등록','추가','신규'];let btn=null;for(const kw of priorities){btn=Array.from(document.querySelectorAll('button')).find(b=>{const t=b.innerText?.trim()||'';return t.includes(kw)&&b.offsetParent!==null&&!b.disabled;});if(btn)break;}if(!btn){R.err='등록 버튼 없음';R.ok=true;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": "OPEN_FORM"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"name": "[회계관리 > 매출관리] [RAPID] 폼 렌더링 대기",
|
||||||
|
"action": "wait",
|
||||||
|
"timeout": 2000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"name": "[회계관리 > 매출관리] [RAPID] 등록 버튼 5회 연타 → 중복 제출 방지 확인",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'RAPID_SUBMIT'};const btn=Array.from(document.querySelectorAll('button')).find(b=>{const t=b.innerText?.trim()||'';return(/등록|저장|확인|제출/.test(t))&&b.offsetParent!==null&&!b.disabled;});if(!btn){R.err='저장/등록 버튼 없음';R.ok=true;return JSON.stringify(R);}R.btnText=btn.innerText?.trim();const beforeApiCount=(window.__API_LOGS__||[]).length;let clickCount=0;for(let i=0;i<5;i++){btn.click();clickCount++;await w(50);}R.clickCount=clickCount;await w(3000);const afterApiCount=(window.__API_LOGS__||[]).length;R.apiCallsDuringRapid=afterApiCount-beforeApiCount;const postCalls=(window.__API_LOGS__||[]).slice(beforeApiCount).filter(l=>l.method==='POST');R.postCallCount=postCalls.length;R.duplicateProtection=R.postCallCount<=1;const toasts=document.querySelectorAll('[data-sonner-toast],[role=\"status\"],[class*=\"toast\"],[class*=\"Toast\"]');R.toastCount=toasts.length;if(toasts.length>0){R.toastTexts=Array.from(toasts).map(t=>t.innerText?.trim().substring(0,80)).filter(Boolean);}const dialogs=document.querySelectorAll('[role=\"dialog\"],[role=\"alertdialog\"],[aria-modal=\"true\"]');const visibleDialogs=Array.from(dialogs).filter(d=>d.offsetParent!==null);R.dialogCount=visibleDialogs.length;const hasError=document.querySelector('[class*=\"error\"],[class*=\"Error\"],[role=\"alert\"]');R.hasError=!!hasError;R.ok=true;R.info=R.duplicateProtection?'✅ 5회 연타 시 중복 제출 방지 (POST '+R.postCallCount+'회)':'⚠️ 5회 연타 시 중복 POST 발생 ('+R.postCallCount+'회)';return JSON.stringify(R);})()",
|
||||||
|
"timeout": 20000,
|
||||||
|
"phase": "RAPID_CLICK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"name": "[회계관리 > 매출관리] [RAPID] 연타 후 상태 확인 + 다이얼로그 닫기",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'RAPID_RESULT'};await w(1000);const dlg=document.querySelector('[role=\"alertdialog\"],[role=\"dialog\"]');if(dlg&&dlg.offsetParent!==null){R.dialogFound=true;const closeBtn=dlg.querySelector('button[class*=\"close\"]')||Array.from(dlg.querySelectorAll('button')).find(b=>/닫기|확인|취소|Close/.test(b.innerText?.trim()));if(closeBtn){closeBtn.click();await w(500);R.dialogClosed=true;}else{document.dispatchEvent(new KeyboardEvent('keydown',{key:'Escape',bubbles:true}));await w(500);}}R.url=location.pathname+location.search;R.pageStable=!document.querySelector('.loading,.spinner,[class*=\"skeleton\"]');R.ok=true;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 10000,
|
||||||
|
"phase": "RAPID_CLICK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"name": "[회계관리 > 매출관리] [RAPID] 품목 추가 버튼 10회 연타 → 적절한 행 수 확인",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'RAPID_ADD_ITEM'};const addBtn=Array.from(document.querySelectorAll('button')).find(b=>{const t=b.innerText?.trim()||'';return(/품목.*추가|행.*추가|\\+.*추가|추가/.test(t)||t==='+')&&b.offsetParent!==null&&!b.disabled;});if(!addBtn){R.err='품목 추가 버튼 없음 (폼 미진입 가능)';R.ok=true;return JSON.stringify(R);}R.addBtnText=addBtn.innerText?.trim();const beforeRows=document.querySelectorAll('table tbody tr, [class*=\"item-row\"],[class*=\"ItemRow\"],[class*=\"grid-row\"]').length;let clickCount=0;for(let i=0;i<10;i++){addBtn.click();clickCount++;await w(80);}R.clickCount=clickCount;await w(2000);const afterRows=document.querySelectorAll('table tbody tr, [class*=\"item-row\"],[class*=\"ItemRow\"],[class*=\"grid-row\"]').length;R.beforeRows=beforeRows;R.afterRows=afterRows;R.addedRows=afterRows-beforeRows;R.reasonableRowCount=R.addedRows<=10&&R.addedRows>=1;R.ok=true;R.info=R.reasonableRowCount?'✅ 10회 연타 후 품목 행 '+R.addedRows+'개 추가 (합리적)':'⚠️ 10회 연타 후 품목 행 '+R.addedRows+'개 추가 (비정상)';return JSON.stringify(R);})()",
|
||||||
|
"timeout": 20000,
|
||||||
|
"phase": "RAPID_CLICK"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"name": "[회계관리 > 매출관리] [CLOSE] 폼/모달 닫기 → 목록 복귀",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CLOSE_FORM'};const dlg=document.querySelector('[role=\"alertdialog\"],[role=\"dialog\"]');if(dlg&&dlg.offsetParent!==null){const closeBtn=dlg.querySelector('button[class*=\"close\"]')||Array.from(dlg.querySelectorAll('button')).find(b=>/닫기|확인|취소|Close/.test(b.innerText?.trim()));if(closeBtn){closeBtn.click();await w(500);}else{document.dispatchEvent(new KeyboardEvent('keydown',{key:'Escape',bubbles:true}));await w(500);}}if(location.search.includes('mode=new')||location.search.includes('mode=edit')){const backBtn=Array.from(document.querySelectorAll('button,a')).find(b=>/목록|취소|뒤로/.test(b.innerText?.trim()));if(backBtn){backBtn.click();await w(2000);}else{history.back();await w(2000);}}const modal=document.querySelector('[role=\"dialog\"],[aria-modal=\"true\"],[class*=\"modal\"]:not([class*=\"tooltip\"])');if(modal&&modal.offsetParent!==null){const xBtn=modal.querySelector('button[class*=\"close\"],[aria-label=\"닫기\"],[aria-label=\"Close\"]')||Array.from(modal.querySelectorAll('button')).find(b=>/닫기|취소|Close/.test(b.innerText?.trim()));if(xBtn){xBtn.click();await w(500);}else{document.dispatchEvent(new KeyboardEvent('keydown',{key:'Escape',bubbles:true}));await w(500);}}R.url=location.pathname+location.search;R.ok=true;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 10000,
|
||||||
|
"phase": "CLOSE_FORM"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
163
full-crud-acc-sales.json
Normal file
163
full-crud-acc-sales.json
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
{
|
||||||
|
"id": "full-crud-acc-sales",
|
||||||
|
"name": "Full CRUD 테스트: 매출관리",
|
||||||
|
"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);}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": 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 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;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:'CREATE',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){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;}}else{document.dispatchEvent(new KeyboardEvent('keydown',{key:'Escape',bubbles:true}));await w(200);}break;}}await w(300);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){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;}}else{document.dispatchEvent(new KeyboardEvent('keydown',{key:'Escape',bubbles:true}));await w(200);}break;}}await w(300);const inputs=Array.from(document.querySelectorAll('input[type=\"text\"],input[type=\"number\"],input:not([type])'));const visInputs=inputs.filter(i=>i.offsetParent!==null&&!i.readOnly&&!i.disabled);for(const inp of visInputs){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 visInputs){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 visInputs){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=visInputs.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(500);const sub=Array.from(document.querySelectorAll('button')).find(b=>b.innerText?.trim()==='등록'&&b.offsetParent!==null&&!b.disabled);if(!sub){R.error='등록 버튼 없음';return JSON.stringify(R);}sub.click();await w(3000);R.toast=toastInfo();R.ok=true;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 30000,
|
||||||
|
"phase": "CREATE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"name": "[회계관리 > 매출관리] [CREATE] 생성 후 대기",
|
||||||
|
"action": "wait",
|
||||||
|
"timeout": 3000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"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": 8,
|
||||||
|
"name": "[회계관리 > 매출관리] [CREATE] 목록 안정화 대기",
|
||||||
|
"action": "wait",
|
||||||
|
"timeout": 2000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"name": "[회계관리 > 매출관리] [VERIFY] 생성 데이터 확인",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const ts=window.__E2E_TS__||'E2E_TEST_';const R={phase:'VERIFY_CREATE'};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;R.ok=R.found;if(found)R.foundText=found.innerText?.substring(0,100);return JSON.stringify(R);})()",
|
||||||
|
"timeout": 15000,
|
||||||
|
"phase": "VERIFY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"name": "[회계관리 > 매출관리] [READ] 상세 페이지 진입",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const ts=window.__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){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": 11,
|
||||||
|
"name": "[회계관리 > 매출관리] [READ] 상세 페이지 대기",
|
||||||
|
"action": "wait",
|
||||||
|
"timeout": 2000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"name": "[회계관리 > 매출관리] [READ] 상세 데이터 검증 (품목/수량/단가/공급가액)",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const ts=window.__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');R.fieldChecks={qty:hasQty,price:hasPrice,supply:hasSupply,vat:hasVat};R.ok=R.hasE2EData;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 15000,
|
||||||
|
"phase": "READ"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"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__||'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:'UPDATE'};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;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(500);const saveBtn=Array.from(document.querySelectorAll('button')).find(b=>/저장|수정|확인/.test(b.innerText?.trim())&&b!==editBtn&&b.offsetParent!==null&&!b.disabled);if(saveBtn){saveBtn.click();await w(3000);}R.toast=toastInfo();R.ok=true;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 30000,
|
||||||
|
"phase": "UPDATE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 14,
|
||||||
|
"name": "[회계관리 > 매출관리] [UPDATE] 저장 후 대기",
|
||||||
|
"action": "wait",
|
||||||
|
"timeout": 3000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 15,
|
||||||
|
"name": "[회계관리 > 매출관리] [UPDATE] 수정 내용 검증 (공급가액 1,000,000 재계산)",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'VERIFY_UPDATE'};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.ok=hasModified||toastOk;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 15000,
|
||||||
|
"phase": "UPDATE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 16,
|
||||||
|
"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": 17,
|
||||||
|
"name": "[회계관리 > 매출관리] [UPDATE] 목록 안정화 대기",
|
||||||
|
"action": "wait",
|
||||||
|
"timeout": 2000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 18,
|
||||||
|
"name": "[회계관리 > 매출관리] [DELETE] 데이터 삭제",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const ts=window.__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": 19,
|
||||||
|
"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": 20,
|
||||||
|
"name": "[회계관리 > 매출관리] [VERIFY] 삭제 확인",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const ts=window.__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;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 15000,
|
||||||
|
"phase": "VERIFY"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
185
multi-item-acc-sales.json
Normal file
185
multi-item-acc-sales.json
Normal 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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
131
reload-persist-acc-sales.json
Normal file
131
reload-persist-acc-sales.json
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
{
|
||||||
|
"id": "reload-persist-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 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{localStorage.setItem('__E2E_TS__',ts);}catch(e){}const R={phase:'CREATE',ts};const btn=Array.from(document.querySelectorAll('button')).find(b=>/매출.*등록|등록/.test(b.innerText?.trim())&&b.offsetParent!==null&&!b.disabled);if(!btn){R.error='등록 버튼 없음';return JSON.stringify(R);}btn.click();await w(2500);const combos=Array.from(document.querySelectorAll('button[role=\"combobox\"]')).filter(b=>b.offsetParent!==null);for(let i=0;i<Math.min(combos.length,2);i++){combos[i].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);}}else{document.dispatchEvent(new KeyboardEvent('keydown',{key:'Escape',bubbles:true}));await w(200);}await w(300);}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||'';if(ph.includes('품목')||nm.includes('item')){sv(inp,'E2E_TEST_리로드_'+ts);await w(200);break;}}for(const inp of inputs){const ph=inp.placeholder||'';const nm=inp.name||'';if(ph.includes('수량')||nm.includes('quantity')||nm.includes('qty')){sv(inp,'2');await w(200);break;}}for(const inp of inputs){const ph=inp.placeholder||'';const nm=inp.name||'';if(ph.includes('단가')||nm.includes('price')||nm.includes('unitPrice')){sv(inp,'50000');await w(200);break;}}const noteInp=inputs.find(i=>(i.placeholder||'').includes('적요')||(i.name||'').includes('note'));if(noteInp){sv(noteInp,'E2E_TEST_매출리로드_'+ts);await w(200);}await w(500);const sub=Array.from(document.querySelectorAll('button')).find(b=>b.innerText?.trim()==='등록'&&b.offsetParent!==null&&!b.disabled);if(sub){sub.click();await w(3000);}R.ok=true;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 30000,
|
||||||
|
"phase": "CREATE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"name": "[회계관리 > 매출관리] [CREATE] 생성 후 대기",
|
||||||
|
"action": "wait",
|
||||||
|
"timeout": 3000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"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');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": 6,
|
||||||
|
"name": "[회계관리 > 매출관리] [CREATE] 목록 안정화",
|
||||||
|
"action": "wait",
|
||||||
|
"timeout": 2000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"name": "[회계관리 > 매출관리] [VERIFY] 새로고침 전 데이터 확인",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const ts=window.__E2E_TS__||(()=>{try{return localStorage.getItem('__E2E_TS__')}catch(e){return null}})()||'E2E_TEST_';const R={phase:'VERIFY_BEFORE'};await w(500);const rows=document.querySelectorAll('table tbody tr');R.rowCount=rows.length;R.beforeRowCount=rows.length;window.__E2E_BEFORE_ROWS__=rows.length;const found=Array.from(rows).find(r=>r.innerText?.includes(ts))||Array.from(rows).find(r=>r.innerText?.includes('E2E_TEST_'));R.found=!!found;R.ok=R.found;R.ts=ts;if(found)R.foundText=found.innerText?.substring(0,80);if(!found)R.error='E2E_TEST_ 데이터 없음';return JSON.stringify(R);})()",
|
||||||
|
"timeout": 10000,
|
||||||
|
"phase": "VERIFY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"name": "[회계관리 > 매출관리] [RELOAD] 페이지 새로고침",
|
||||||
|
"action": "reload",
|
||||||
|
"timeout": 10000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"name": "[회계관리 > 매출관리] [RELOAD] 새로고침 후 대기",
|
||||||
|
"action": "wait",
|
||||||
|
"timeout": 5000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"name": "[회계관리 > 매출관리] [RELOAD] SPA 안정화 대기",
|
||||||
|
"action": "wait",
|
||||||
|
"timeout": 5000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"name": "[회계관리 > 매출관리] [VERIFY] 새로고침 후 데이터 유지 확인",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const ts=window.__E2E_TS__||(()=>{try{return localStorage.getItem('__E2E_TS__')}catch(e){return null}})()||'E2E_TEST_';const R={phase:'VERIFY_AFTER'};await w(500);const rows=document.querySelectorAll('table tbody tr');R.rowCount=rows.length;R.afterRowCount=rows.length;const found=Array.from(rows).find(r=>r.innerText?.includes(ts))||Array.from(rows).find(r=>r.innerText?.includes('E2E_TEST_'));R.found=!!found;R.ok=R.found;R.ts=ts;if(found)R.foundText=found.innerText?.substring(0,80);if(!found)R.error='새로고침 후 E2E_TEST_ 데이터 사라짐';return JSON.stringify(R);})()",
|
||||||
|
"timeout": 10000,
|
||||||
|
"phase": "VERIFY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"name": "[회계관리 > 매출관리] [VERIFY] 행 수 비교",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const R={phase:'ROW_COUNT_COMPARE'};const beforeRows=window.__E2E_BEFORE_ROWS__||0;const currentRows=document.querySelectorAll('table tbody tr').length;R.beforeRows=beforeRows;R.afterRows=currentRows;R.rowCountMatch=beforeRows===currentRows;R.ok=true;R.info=R.rowCountMatch?'✅ 행 수 일치 ('+beforeRows+' → '+currentRows+')':'⚠️ 행 수 불일치 ('+beforeRows+' → '+currentRows+')';return JSON.stringify(R);})()",
|
||||||
|
"timeout": 10000,
|
||||||
|
"phase": "VERIFY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"name": "[회계관리 > 매출관리] [DELETE] 테스트 데이터 삭제",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const ts=window.__E2E_TS__||(()=>{try{return localStorage.getItem('__E2E_TS__')}catch(e){return null}})()||'E2E_TEST_';const R={phase:'DELETE'};const rows=Array.from(document.querySelectorAll('table tbody tr'));const row=rows.find(r=>r.innerText?.includes(ts))||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=>/확인|삭제|예/.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": 14,
|
||||||
|
"name": "[회계관리 > 매출관리] [DELETE] 삭제 후 대기",
|
||||||
|
"action": "wait",
|
||||||
|
"timeout": 3000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 15,
|
||||||
|
"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);}}return JSON.stringify({url:location.pathname+location.search});})()",
|
||||||
|
"timeout": 10000,
|
||||||
|
"phase": "DELETE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 16,
|
||||||
|
"name": "[회계관리 > 매출관리] [VERIFY] 삭제 확인",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const ts=window.__E2E_TS__||(()=>{try{return localStorage.getItem('__E2E_TS__')}catch(e){return null}})();const R={phase:'VERIFY_DELETE'};await w(2000);const rows=document.querySelectorAll('table tbody tr');R.rowCount=rows.length;R.ts=ts||'(no ts)';if(!ts){R.ok=true;R.skipped='no ts';return JSON.stringify(R);}const found=Array.from(rows).find(r=>r.innerText?.includes(ts));R.stillExists=!!found;R.ok=!found;if(found)R.error='삭제된 데이터(ts='+ts+')가 여전히 존재';return JSON.stringify(R);})()",
|
||||||
|
"timeout": 10000,
|
||||||
|
"phase": "VERIFY"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
152
search-filter-acc-sales.json
Normal file
152
search-filter-acc-sales.json
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
{
|
||||||
|
"id": "search-filter-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": "[회계관리 > 매출관리] [BASELINE] 초기 행 수 + UI 요소 캡처",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'BASELINE'};await w(500);const rows=document.querySelectorAll('table tbody tr');R.initialRowCount=rows.length;window.__E2E_INITIAL_ROWS__=rows.length;const dateInputs=document.querySelectorAll('input[type=\"date\"],button[class*=\"date\"],button[class*=\"Date\"],[class*=\"datepicker\"],[class*=\"DatePicker\"]');R.dateInputCount=dateInputs.length;const searchInputs=document.querySelectorAll('input[type=\"search\"],input[placeholder*=\"검색\"],input[placeholder*=\"search\"]');R.searchInputCount=searchInputs.length;const combos=Array.from(document.querySelectorAll('button[role=\"combobox\"],select')).filter(b=>b.offsetParent!==null);R.comboCount=combos.length;const filterBtns=Array.from(document.querySelectorAll('button')).filter(b=>/검색|조회|필터/.test(b.innerText?.trim()));R.filterBtnCount=filterBtns.length;const pagination=document.querySelectorAll('[class*=\"pagination\"],[class*=\"Pagination\"],nav[aria-label*=\"page\"],button[aria-label*=\"page\"]');R.hasPagination=pagination.length>0;R.ok=R.initialRowCount>0;R.info='초기 행 수: '+R.initialRowCount+', 콤보: '+R.comboCount+', 페이지네이션: '+R.hasPagination;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 10000,
|
||||||
|
"phase": "BASELINE"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"name": "[회계관리 > 매출관리] [DATE-FILTER] 날짜 범위 필터 설정",
|
||||||
|
"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:'DATE_FILTER'};const dateInputs=Array.from(document.querySelectorAll('input[type=\"date\"]')).filter(i=>i.offsetParent!==null);R.dateInputCount=dateInputs.length;if(dateInputs.length>=1){const today=new Date();const monthAgo=new Date(today);monthAgo.setMonth(monthAgo.getMonth()-1);const fmt=d=>`${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}`;sv(dateInputs[0],fmt(monthAgo));await w(300);if(dateInputs.length>=2){sv(dateInputs[1],fmt(today));await w(300);}R.dateSet=true;}else{const dateBtns=Array.from(document.querySelectorAll('button')).filter(b=>b.innerText?.includes('날짜')||b.className?.includes('date')||b.className?.includes('Date'));if(dateBtns.length>0){dateBtns[0].click();await w(600);const todayBtn=document.querySelector('[aria-selected=\"true\"],button[name=\"day\"].bg-primary');if(todayBtn){todayBtn.click();await w(300);}R.dateSet=true;}}const searchBtn=Array.from(document.querySelectorAll('button')).find(b=>/검색|조회/.test(b.innerText?.trim())&&b.offsetParent!==null);if(searchBtn){searchBtn.click();await w(2000);}else{await w(1000);}const rows=document.querySelectorAll('table tbody tr');R.filteredRowCount=rows.length;R.ok=true;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 15000,
|
||||||
|
"phase": "FILTER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"name": "[회계관리 > 매출관리] [DATE-FILTER] 결과 확인 후 초기화",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'DATE_RESET'};const rows=document.querySelectorAll('table tbody tr');R.afterFilterRows=rows.length;const resetBtn=Array.from(document.querySelectorAll('button')).find(b=>/초기화|리셋|Reset|전체/.test(b.innerText?.trim())&&b.offsetParent!==null);if(resetBtn){resetBtn.click();await w(2000);R.resetClicked=true;}else{const dateInputs=Array.from(document.querySelectorAll('input[type=\"date\"]')).filter(i=>i.offsetParent!==null);dateInputs.forEach(inp=>{inp.value='';inp.dispatchEvent(new Event('change',{bubbles:true}));});await w(500);const searchBtn=Array.from(document.querySelectorAll('button')).find(b=>/검색|조회/.test(b.innerText?.trim()));if(searchBtn){searchBtn.click();await w(2000);}}const rowsAfterReset=document.querySelectorAll('table tbody tr');R.afterResetRows=rowsAfterReset.length;R.ok=true;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 15000,
|
||||||
|
"phase": "FILTER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"name": "[회계관리 > 매출관리] [VENDOR-FILTER] 거래처 필터 선택",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'VENDOR_FILTER'};const combos=Array.from(document.querySelectorAll('button[role=\"combobox\"]')).filter(b=>b.offsetParent!==null);const vendorCombo=combos.find(cb=>{const lbl=cb.closest('[class*=field],[class*=Field],[class*=form-item]')?.querySelector('label')?.innerText||'';return lbl.includes('거래처');});const target=vendorCombo||combos[0];if(!target){R.info='거래처 필터 콤보 없음';R.ok=true;return JSON.stringify(R);}target.click();await w(600);const lb=document.querySelector('[role=\"listbox\"]');if(lb){const opts=lb.querySelectorAll('[role=\"option\"]');if(opts.length>0){const vendorOpt=opts[0];R.selectedVendor=vendorOpt.innerText?.trim();window.__E2E_VENDOR__=R.selectedVendor;vendorOpt.click();await w(400);}}else{document.dispatchEvent(new KeyboardEvent('keydown',{key:'Escape',bubbles:true}));await w(200);}const searchBtn=Array.from(document.querySelectorAll('button')).find(b=>/검색|조회/.test(b.innerText?.trim())&&b.offsetParent!==null);if(searchBtn){searchBtn.click();await w(2000);}else{await w(1000);}const rows=document.querySelectorAll('table tbody tr');R.filteredRowCount=rows.length;R.ok=true;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 15000,
|
||||||
|
"phase": "FILTER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"name": "[회계관리 > 매출관리] [VENDOR-FILTER] 모든 행이 해당 거래처인지 확인",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'VENDOR_VERIFY'};const vendor=window.__E2E_VENDOR__||'';R.expectedVendor=vendor;const rows=document.querySelectorAll('table tbody tr');R.rowCount=rows.length;if(!vendor||rows.length===0){R.ok=true;R.info='거래처 필터 미적용 또는 결과 없음';return JSON.stringify(R);}let matchCount=0;Array.from(rows).forEach(row=>{if(row.innerText?.includes(vendor))matchCount++;});R.matchCount=matchCount;R.allMatch=matchCount===rows.length;R.ok=true;R.info=R.allMatch?'✅ 모든 행('+rows.length+')이 거래처 '+vendor+' 일치':'⚠️ '+matchCount+'/'+rows.length+' 행만 일치';return JSON.stringify(R);})()",
|
||||||
|
"timeout": 10000,
|
||||||
|
"phase": "VERIFY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"name": "[회계관리 > 매출관리] [VENDOR-FILTER] 초기화",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'VENDOR_RESET'};const resetBtn=Array.from(document.querySelectorAll('button')).find(b=>/초기화|리셋|Reset|전체/.test(b.innerText?.trim())&&b.offsetParent!==null);if(resetBtn){resetBtn.click();await w(2000);R.resetClicked=true;}else{location.reload();await w(3000);R.reloaded=true;}R.ok=true;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 15000,
|
||||||
|
"phase": "FILTER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"name": "[회계관리 > 매출관리] [VENDOR-FILTER] 초기화 대기",
|
||||||
|
"action": "wait",
|
||||||
|
"timeout": 2000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"name": "[회계관리 > 매출관리] [TYPE-FILTER] 매출유형 필터 선택",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'TYPE_FILTER'};const combos=Array.from(document.querySelectorAll('button[role=\"combobox\"]')).filter(b=>b.offsetParent!==null);const typeCombo=combos.find(cb=>{const lbl=cb.closest('[class*=field],[class*=Field],[class*=form-item]')?.querySelector('label')?.innerText||'';return lbl.includes('매출유형')||lbl.includes('유형');});const target=typeCombo||(combos.length>1?combos[1]:null);if(!target){R.info='매출유형 필터 콤보 없음';R.ok=true;return JSON.stringify(R);}target.click();await w(600);const lb=document.querySelector('[role=\"listbox\"]');if(lb){const opts=lb.querySelectorAll('[role=\"option\"]');const typeOpt=Array.from(opts).find(o=>o.innerText?.includes('제품매출'))||opts[0];if(typeOpt){R.selectedType=typeOpt.innerText?.trim();window.__E2E_TYPE__=R.selectedType;typeOpt.click();await w(400);}}else{document.dispatchEvent(new KeyboardEvent('keydown',{key:'Escape',bubbles:true}));await w(200);}const searchBtn=Array.from(document.querySelectorAll('button')).find(b=>/검색|조회/.test(b.innerText?.trim())&&b.offsetParent!==null);if(searchBtn){searchBtn.click();await w(2000);}else{await w(1000);}const rows=document.querySelectorAll('table tbody tr');R.filteredRowCount=rows.length;R.ok=true;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 15000,
|
||||||
|
"phase": "FILTER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"name": "[회계관리 > 매출관리] [TYPE-FILTER] 모든 행이 해당 유형인지 확인",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const R={phase:'TYPE_VERIFY'};const sType=window.__E2E_TYPE__||'';R.expectedType=sType;const rows=document.querySelectorAll('table tbody tr');R.rowCount=rows.length;if(!sType||rows.length===0){R.ok=true;R.info='매출유형 필터 미적용 또는 결과 없음';return JSON.stringify(R);}let matchCount=0;Array.from(rows).forEach(row=>{if(row.innerText?.includes(sType))matchCount++;});R.matchCount=matchCount;R.allMatch=matchCount===rows.length;R.ok=true;R.info=R.allMatch?'✅ 모든 행('+rows.length+')이 유형 '+sType+' 일치':'⚠️ '+matchCount+'/'+rows.length+' 행만 일치';return JSON.stringify(R);})()",
|
||||||
|
"timeout": 10000,
|
||||||
|
"phase": "VERIFY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"name": "[회계관리 > 매출관리] [RESET] 전체 필터 초기화",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'FULL_RESET'};const resetBtn=Array.from(document.querySelectorAll('button')).find(b=>/초기화|리셋|Reset|전체/.test(b.innerText?.trim())&&b.offsetParent!==null);if(resetBtn){resetBtn.click();await w(2000);R.resetClicked=true;}else{location.reload();await w(3000);R.reloaded=true;}R.ok=true;return JSON.stringify(R);})()",
|
||||||
|
"timeout": 15000,
|
||||||
|
"phase": "FILTER"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"name": "[회계관리 > 매출관리] [RESET] 초기화 후 대기",
|
||||||
|
"action": "wait",
|
||||||
|
"timeout": 3000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 14,
|
||||||
|
"name": "[회계관리 > 매출관리] [RESET] 원래 행 수 복귀 확인",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const R={phase:'RESET_VERIFY'};const initialRows=window.__E2E_INITIAL_ROWS__||0;const currentRows=document.querySelectorAll('table tbody tr').length;R.initialRows=initialRows;R.currentRows=currentRows;R.restored=currentRows>=initialRows||Math.abs(currentRows-initialRows)<=2;R.ok=true;R.info=R.restored?'✅ 행 수 복귀 ('+initialRows+' → '+currentRows+')':'⚠️ 행 수 불일치 ('+initialRows+' → '+currentRows+')';return JSON.stringify(R);})()",
|
||||||
|
"timeout": 10000,
|
||||||
|
"phase": "VERIFY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 15,
|
||||||
|
"name": "[회계관리 > 매출관리] [PAGINATION] 페이지네이션 존재 확인",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'PAGINATION_CHECK'};const pagBtns=Array.from(document.querySelectorAll('button,a')).filter(b=>{const t=b.innerText?.trim();const ariaLabel=b.getAttribute('aria-label')||'';return(/^[0-9]+$/.test(t)||/다음|next|이전|prev|>|<|>>|<</.test(t)||/page/i.test(ariaLabel))&&b.offsetParent!==null;});R.paginationBtnCount=pagBtns.length;R.hasPagination=pagBtns.length>0;if(!R.hasPagination){R.info='페이지네이션 없음 (데이터 적음 가능)';R.ok=true;return JSON.stringify(R);}const firstRowText=document.querySelector('table tbody tr')?.innerText?.substring(0,60)||'';window.__E2E_FIRST_PAGE_TEXT__=firstRowText;const page2Btn=pagBtns.find(b=>b.innerText?.trim()==='2')||pagBtns.find(b=>/다음|next|>/.test(b.innerText?.trim()));if(page2Btn){page2Btn.click();await w(2000);R.navigatedToPage2=true;const newFirstRowText=document.querySelector('table tbody tr')?.innerText?.substring(0,60)||'';R.dataChanged=newFirstRowText!==firstRowText;R.ok=true;R.info=R.dataChanged?'✅ 2페이지 이동 → 데이터 변경됨':'⚠️ 2페이지 이동 → 데이터 동일';}else{R.ok=true;R.info='2페이지 버튼 없음';}return JSON.stringify(R);})()",
|
||||||
|
"timeout": 15000,
|
||||||
|
"phase": "PAGINATION"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 16,
|
||||||
|
"name": "[회계관리 > 매출관리] [PAGINATION] 대기",
|
||||||
|
"action": "wait",
|
||||||
|
"timeout": 1000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 17,
|
||||||
|
"name": "[회계관리 > 매출관리] [PAGINATION] 1페이지 복귀",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'PAGE1_RETURN'};const page1Btn=Array.from(document.querySelectorAll('button,a')).find(b=>{const t=b.innerText?.trim();const ariaLabel=b.getAttribute('aria-label')||'';return(t==='1'||/이전|prev|</.test(t))&&b.offsetParent!==null&&!/<</.test(t);});if(page1Btn){page1Btn.click();await w(2000);R.navigatedToPage1=true;}const firstRowText=document.querySelector('table tbody tr')?.innerText?.substring(0,60)||'';const originalText=window.__E2E_FIRST_PAGE_TEXT__||'';R.dataRestored=firstRowText===originalText;R.ok=true;R.info=R.dataRestored?'✅ 1페이지 복귀 → 원래 데이터':'⚠️ 1페이지 복귀 → 데이터 불일치';return JSON.stringify(R);})()",
|
||||||
|
"timeout": 15000,
|
||||||
|
"phase": "PAGINATION"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 18,
|
||||||
|
"name": "[회계관리 > 매출관리] [SUMMARY] 필터/검색 테스트 완료",
|
||||||
|
"action": "evaluate",
|
||||||
|
"script": "(async()=>{const R={phase:'SUMMARY'};R.initialRows=window.__E2E_INITIAL_ROWS__||0;R.currentRows=document.querySelectorAll('table tbody tr').length;R.url=location.pathname+location.search;R.ok=true;R.info='검색/필터/페이지네이션 테스트 완료';return JSON.stringify(R);})()",
|
||||||
|
"timeout": 5000,
|
||||||
|
"phase": "VERIFY"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user