Files
sam-scenarios/edge-rapid-click-acc-sales.json

90 lines
9.1 KiB
JSON
Raw Normal View History

{
"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": 5000
},
{
"id": 2,
"name": "[회계관리 > 매출관리] 테이블 로드 대기",
"action": "wait_for_table",
"timeout": 20000
},
{
"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"
}
]
}