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"
|
||||
}
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user