fix: deprecated window.__API_LOGS__ → window.__E2E__.getApiLogs() 패턴 수정 (17개 파일)

- approval-box, edge-rapid-click-acc-sales, full-crud-* (4개)
- hr-salary-long-term-care, production-work-order
- quality-inspection, quality-performance-report
- reload-persist-acc-deposit, sales-management
- sales-order-bulk-delete, sales-order, sales-quotation
- system-dashboard, vendor-management
- 전체 209/209 ALL PASS 검증 완료

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
김보곤
2026-03-04 11:42:23 +09:00
parent ed9e6270cc
commit f42cf4ab7d
17 changed files with 43 additions and 43 deletions

View File

@@ -152,14 +152,14 @@
"name": "필수 검증: PDF 다운로드 실행",
"description": "PDF 다운로드 버튼 클릭 및 API 응답 확인",
"action": "evaluate",
"script": "(async () => { const pdfBtn = document.querySelector(\"button:has-text('PDF'), [aria-label*='PDF']\") || Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('PDF')); if (!pdfBtn) return 'WARN: PDF button not found'; pdfBtn.click(); await new Promise(r => setTimeout(r, 3000)); const logs = window.__API_LOGS__ || []; const pdfCall = logs.find(l => l.url?.includes('/pdf')); if (pdfCall && pdfCall.ok) return 'PASS: PDF download API success (status ' + pdfCall.status + ')'; if (pdfCall) return 'FAIL: PDF API error (status ' + pdfCall.status + ')'; return 'WARN: PDF API call not captured'; })()"
"script": "(async () => { const pdfBtn = document.querySelector(\"button:has-text('PDF'), [aria-label*='PDF']\") || Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('PDF')); if (!pdfBtn) return 'WARN: PDF button not found'; pdfBtn.click(); await new Promise(r => setTimeout(r, 3000)); const logs = (window.__E2E__?window.__E2E__.getApiLogs().logs:[]); const pdfCall = logs.find(l => l.url?.includes('/pdf')); if (pdfCall && pdfCall.ok) return 'PASS: PDF download API success (status ' + pdfCall.status + ')'; if (pdfCall) return 'FAIL: PDF API error (status ' + pdfCall.status + ')'; return 'WARN: PDF API call not captured'; })()"
},
{
"id": 14,
"name": "PDF 파일 유효성 + content-type 검증",
"description": "다운로드된 PDF API 응답의 status 200 및 content-type 확인",
"action": "evaluate",
"script": "(()=>{const logs=window.__API_LOGS__||[];const pdfCall=logs.find(l=>l.url?.includes('/pdf'));if(!pdfCall)return JSON.stringify({ok:true,info:'WARN: PDF API call not in logs - skip validation'});const R={phase:'PDF_VALIDATE',status:pdfCall.status,ok:pdfCall.ok,duration:pdfCall.duration};if(pdfCall.ok&&pdfCall.status===200){R.info='PASS: PDF API 200 OK, duration='+pdfCall.duration+'ms';}else{R.info='FAIL: PDF API status='+pdfCall.status;}return JSON.stringify(R);})()",
"script": "(()=>{const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const pdfCall=logs.find(l=>l.url?.includes('/pdf'));if(!pdfCall)return JSON.stringify({ok:true,info:'WARN: PDF API call not in logs - skip validation'});const R={phase:'PDF_VALIDATE',status:pdfCall.status,ok:pdfCall.ok,duration:pdfCall.duration};if(pdfCall.ok&&pdfCall.status===200){R.info='PASS: PDF API 200 OK, duration='+pdfCall.duration+'ms';}else{R.info='FAIL: PDF API status='+pdfCall.status;}return JSON.stringify(R);})()",
"timeout": 5000,
"phase": "VERIFY"
},
@@ -315,7 +315,7 @@
"name": "[VERIFY] API 호출 요약",
"description": "전체 테스트 동안의 API 호출 통계 수집",
"action": "evaluate",
"script": "(()=>{const logs=window.__API_LOGS__||[];const errors=window.__API_ERRORS__||[];const R={phase:'API_SUMMARY',total:logs.length,success:logs.filter(l=>l.ok).length,failed:errors.length,avgResponseTime:logs.length>0?Math.round(logs.reduce((s,l)=>s+(l.duration||0),0)/logs.length):0,slowCalls:logs.filter(l=>l.duration>2000).length};const approvalCalls=logs.filter(l=>l.url?.includes('/approval'));R.approvalCalls=approvalCalls.length;R.approvalMethods=approvalCalls.map(l=>l.method+' '+l.status).join(', ').substring(0,200);R.ok=true;R.info='API total='+R.total+' success='+R.success+' failed='+R.failed+' avg='+R.avgResponseTime+'ms slow='+R.slowCalls;return JSON.stringify(R);})()",
"script": "(()=>{const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const errors=(window.__E2E__?window.__E2E__.getApiLogs().errors:[]);const R={phase:'API_SUMMARY',total:logs.length,success:logs.filter(l=>l.ok).length,failed:errors.length,avgResponseTime:logs.length>0?Math.round(logs.reduce((s,l)=>s+(l.duration||0),0)/logs.length):0,slowCalls:logs.filter(l=>l.duration>2000).length};const approvalCalls=logs.filter(l=>l.url?.includes('/approval'));R.approvalCalls=approvalCalls.length;R.approvalMethods=approvalCalls.map(l=>l.method+' '+l.status).join(', ').substring(0,200);R.ok=true;R.info='API total='+R.total+' success='+R.success+' failed='+R.failed+' avg='+R.avgResponseTime+'ms slow='+R.slowCalls;return JSON.stringify(R);})()",
"timeout": 5000,
"phase": "VERIFY"
}

View File

@@ -58,7 +58,7 @@
"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);})()",
"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.__E2E__?window.__E2E__.getApiLogs().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.__E2E__?window.__E2E__.getApiLogs().logs:[])).length;R.apiCallsDuringRapid=afterApiCount-beforeApiCount;const postCalls=((window.__E2E__?window.__E2E__.getApiLogs().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"
},

View File

@@ -24,7 +24,7 @@
"id": 2,
"name": "[회계관리 > 어음관리] ts 초기화 + sessionStorage 연동",
"action": "evaluate",
"script": "(()=>{try{sessionStorage.removeItem('__E2E_TS__');}catch(e){}delete window.__E2E_TS__;window.__API_LOGS__=window.__API_LOGS__||[];return JSON.stringify({ok:true,cleared:true});})()",
"script": "(()=>{try{sessionStorage.removeItem('__E2E_TS__');}catch(e){}delete window.__E2E_TS__;return JSON.stringify({ok:true,cleared:true});})()",
"timeout": 3000
},
{
@@ -83,7 +83,7 @@
"id": 10,
"name": "[회계관리 > 어음관리] [CREATE] API POST 검증",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_POST_CHECK'};const logs=window.__API_LOGS__||[];const postLogs=logs.filter(l=>l.method==='POST'&&l.status>=200&&l.status<300);R.postCount=postLogs.length;R.found=postLogs.length>0;if(postLogs.length>0){const last=postLogs[postLogs.length-1];R.lastPost={url:last.url?.substring(0,80),status:last.status,duration:last.duration};}R.ok=true;R.info=R.found?'POST API '+R.lastPost.status+' ('+R.lastPost.duration+'ms)':'POST API 미감지 (모니터 타이밍 이슈 가능)';return JSON.stringify(R);})()",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_POST_CHECK'};const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const postLogs=logs.filter(l=>l.method==='POST'&&l.status>=200&&l.status<300);R.postCount=postLogs.length;R.found=postLogs.length>0;if(postLogs.length>0){const last=postLogs[postLogs.length-1];R.lastPost={url:last.url?.substring(0,80),status:last.status,duration:last.duration};}R.ok=true;R.info=R.found?'POST API '+R.lastPost.status+' ('+R.lastPost.duration+'ms)':'POST API 미감지 (모니터 타이밍 이슈 가능)';return JSON.stringify(R);})()",
"timeout": 10000,
"phase": "VERIFY"
},
@@ -165,7 +165,7 @@
"id": 21,
"name": "[회계관리 > 어음관리] [UPDATE] API PUT 검증",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_PUT_CHECK'};const logs=window.__API_LOGS__||[];const putLogs=logs.filter(l=>(l.method==='PUT'||l.method==='PATCH')&&l.status>=200&&l.status<300);R.putCount=putLogs.length;R.found=putLogs.length>0;if(putLogs.length>0){const last=putLogs[putLogs.length-1];R.lastPut={url:last.url?.substring(0,80),status:last.status,duration:last.duration};}R.ok=true;R.info=R.found?'PUT/PATCH API '+R.lastPut.status+' ('+R.lastPut.duration+'ms)':'PUT/PATCH API 미감지';return JSON.stringify(R);})()",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_PUT_CHECK'};const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const putLogs=logs.filter(l=>(l.method==='PUT'||l.method==='PATCH')&&l.status>=200&&l.status<300);R.putCount=putLogs.length;R.found=putLogs.length>0;if(putLogs.length>0){const last=putLogs[putLogs.length-1];R.lastPut={url:last.url?.substring(0,80),status:last.status,duration:last.duration};}R.ok=true;R.info=R.found?'PUT/PATCH API '+R.lastPut.status+' ('+R.lastPut.duration+'ms)':'PUT/PATCH API 미감지';return JSON.stringify(R);})()",
"timeout": 10000,
"phase": "VERIFY"
},
@@ -210,7 +210,7 @@
"id": 27,
"name": "[회계관리 > 어음관리] [DELETE] API DELETE 검증",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_DELETE_CHECK'};const logs=window.__API_LOGS__||[];const delLogs=logs.filter(l=>l.method==='DELETE'&&l.status>=200&&l.status<300);R.delCount=delLogs.length;R.found=delLogs.length>0;if(delLogs.length>0){const last=delLogs[delLogs.length-1];R.lastDelete={url:last.url?.substring(0,80),status:last.status,duration:last.duration};}R.ok=true;R.info=R.found?'DELETE API '+R.lastDelete.status+' ('+R.lastDelete.duration+'ms)':'DELETE API 미감지';return JSON.stringify(R);})()",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_DELETE_CHECK'};const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const delLogs=logs.filter(l=>l.method==='DELETE'&&l.status>=200&&l.status<300);R.delCount=delLogs.length;R.found=delLogs.length>0;if(delLogs.length>0){const last=delLogs[delLogs.length-1];R.lastDelete={url:last.url?.substring(0,80),status:last.status,duration:last.duration};}R.ok=true;R.info=R.found?'DELETE API '+R.lastDelete.status+' ('+R.lastDelete.duration+'ms)':'DELETE API 미감지';return JSON.stringify(R);})()",
"timeout": 10000,
"phase": "VERIFY"
},
@@ -246,7 +246,7 @@
"id": 32,
"name": "[회계관리 > 어음관리] [SUMMARY] API 호출 통계",
"action": "evaluate",
"script": "(async()=>{const R={phase:'API_SUMMARY'};const logs=window.__API_LOGS__||[];R.totalCalls=logs.length;R.successCalls=logs.filter(l=>l.status>=200&&l.status<300).length;R.failedCalls=logs.filter(l=>l.status>=400).length;R.avgResponseTime=logs.length>0?Math.round(logs.reduce((s,l)=>s+(l.duration||0),0)/logs.length):0;R.slowCalls=logs.filter(l=>(l.duration||0)>2000).length;R.methods={GET:logs.filter(l=>l.method==='GET').length,POST:logs.filter(l=>l.method==='POST').length,PUT:logs.filter(l=>l.method==='PUT').length,PATCH:logs.filter(l=>l.method==='PATCH').length,DELETE:logs.filter(l=>l.method==='DELETE').length};R.ok=true;R.info='API: '+R.totalCalls+'건 (성공 '+R.successCalls+', 실패 '+R.failedCalls+', 평균 '+R.avgResponseTime+'ms, 느린호출 '+R.slowCalls+'건)';return JSON.stringify(R);})()",
"script": "(async()=>{const R={phase:'API_SUMMARY'};const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);R.totalCalls=logs.length;R.successCalls=logs.filter(l=>l.status>=200&&l.status<300).length;R.failedCalls=logs.filter(l=>l.status>=400).length;R.avgResponseTime=logs.length>0?Math.round(logs.reduce((s,l)=>s+(l.duration||0),0)/logs.length):0;R.slowCalls=logs.filter(l=>(l.duration||0)>2000).length;R.methods={GET:logs.filter(l=>l.method==='GET').length,POST:logs.filter(l=>l.method==='POST').length,PUT:logs.filter(l=>l.method==='PUT').length,PATCH:logs.filter(l=>l.method==='PATCH').length,DELETE:logs.filter(l=>l.method==='DELETE').length};R.ok=true;R.info='API: '+R.totalCalls+'건 (성공 '+R.successCalls+', 실패 '+R.failedCalls+', 평균 '+R.avgResponseTime+'ms, 느린호출 '+R.slowCalls+'건)';return JSON.stringify(R);})()",
"timeout": 10000,
"phase": "VERIFY"
}

View File

@@ -100,7 +100,7 @@
"id": 12,
"name": "[회계관리 > 입금관리] [CREATE] API POST 검증",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_POST_CHECK'};const logs=window.__API_LOGS__||[];const postLogs=logs.filter(l=>l.method==='POST'&&l.status>=200&&l.status<300);R.postCount=postLogs.length;R.found=postLogs.length>0;if(postLogs.length>0){const last=postLogs[postLogs.length-1];R.url=last.url?.substring(0,80);R.status=last.status;R.duration=last.duration;}R.ok=true;R.info=R.found?'✅ POST '+R.status+' ('+R.duration+'ms)':'⚠️ POST 미감지 (API 모니터 범위 밖일 수 있음)';return JSON.stringify(R);})()",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_POST_CHECK'};const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const postLogs=logs.filter(l=>l.method==='POST'&&l.status>=200&&l.status<300);R.postCount=postLogs.length;R.found=postLogs.length>0;if(postLogs.length>0){const last=postLogs[postLogs.length-1];R.url=last.url?.substring(0,80);R.status=last.status;R.duration=last.duration;}R.ok=true;R.info=R.found?'✅ POST '+R.status+' ('+R.duration+'ms)':'⚠️ POST 미감지 (API 모니터 범위 밖일 수 있음)';return JSON.stringify(R);})()",
"timeout": 5000,
"phase": "VERIFY"
},
@@ -198,7 +198,7 @@
"id": 25,
"name": "[회계관리 > 입금관리] [UPDATE] API PUT/PATCH 검증",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_PUT_CHECK'};const logs=window.__API_LOGS__||[];const putLogs=logs.filter(l=>(l.method==='PUT'||l.method==='PATCH')&&l.status>=200&&l.status<300);R.putCount=putLogs.length;R.found=putLogs.length>0;if(putLogs.length>0){const last=putLogs[putLogs.length-1];R.url=last.url?.substring(0,80);R.status=last.status;R.duration=last.duration;}R.ok=true;R.info=R.found?'✅ PUT/PATCH '+R.status+' ('+R.duration+'ms)':'⚠️ PUT/PATCH 미감지';return JSON.stringify(R);})()",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_PUT_CHECK'};const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const putLogs=logs.filter(l=>(l.method==='PUT'||l.method==='PATCH')&&l.status>=200&&l.status<300);R.putCount=putLogs.length;R.found=putLogs.length>0;if(putLogs.length>0){const last=putLogs[putLogs.length-1];R.url=last.url?.substring(0,80);R.status=last.status;R.duration=last.duration;}R.ok=true;R.info=R.found?'✅ PUT/PATCH '+R.status+' ('+R.duration+'ms)':'⚠️ PUT/PATCH 미감지';return JSON.stringify(R);})()",
"timeout": 5000,
"phase": "VERIFY"
},
@@ -243,7 +243,7 @@
"id": 31,
"name": "[회계관리 > 입금관리] [DELETE] API DELETE 검증",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_DELETE_CHECK'};const logs=window.__API_LOGS__||[];const delLogs=logs.filter(l=>l.method==='DELETE'&&l.status>=200&&l.status<300);R.deleteCount=delLogs.length;R.found=delLogs.length>0;if(delLogs.length>0){const last=delLogs[delLogs.length-1];R.url=last.url?.substring(0,80);R.status=last.status;R.duration=last.duration;}R.ok=true;R.info=R.found?'✅ DELETE '+R.status+' ('+R.duration+'ms)':'⚠️ DELETE 미감지';return JSON.stringify(R);})()",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_DELETE_CHECK'};const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const delLogs=logs.filter(l=>l.method==='DELETE'&&l.status>=200&&l.status<300);R.deleteCount=delLogs.length;R.found=delLogs.length>0;if(delLogs.length>0){const last=delLogs[delLogs.length-1];R.url=last.url?.substring(0,80);R.status=last.status;R.duration=last.duration;}R.ok=true;R.info=R.found?'✅ DELETE '+R.status+' ('+R.duration+'ms)':'⚠️ DELETE 미감지';return JSON.stringify(R);})()",
"timeout": 5000,
"phase": "VERIFY"
},
@@ -273,7 +273,7 @@
"id": 35,
"name": "[회계관리 > 입금관리] [VERIFY] API 호출 전체 요약",
"action": "evaluate",
"script": "(()=>{const logs=window.__API_LOGS__||[];const R={phase:'API_SUMMARY',total:logs.length,success:logs.filter(l=>l.ok||l.status<400).length,failed:logs.filter(l=>!l.ok&&l.status>=400).length,avgResponseTime:logs.length>0?Math.round(logs.reduce((s,l)=>s+(l.duration||0),0)/logs.length):0,slowCalls:logs.filter(l=>l.duration>2000).length,methods:{}};logs.forEach(l=>{R.methods[l.method]=(R.methods[l.method]||0)+1;});R.ok=true;R.info='API: '+R.total+'건 (성공:'+R.success+' 실패:'+R.failed+' 평균:'+R.avgResponseTime+'ms)';return JSON.stringify(R);})()",
"script": "(()=>{const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const R={phase:'API_SUMMARY',total:logs.length,success:logs.filter(l=>l.ok||l.status<400).length,failed:logs.filter(l=>!l.ok&&l.status>=400).length,avgResponseTime:logs.length>0?Math.round(logs.reduce((s,l)=>s+(l.duration||0),0)/logs.length):0,slowCalls:logs.filter(l=>l.duration>2000).length,methods:{}};logs.forEach(l=>{R.methods[l.method]=(R.methods[l.method]||0)+1;});R.ok=true;R.info='API: '+R.total+'건 (성공:'+R.success+' 실패:'+R.failed+' 평균:'+R.avgResponseTime+'ms)';return JSON.stringify(R);})()",
"timeout": 5000,
"phase": "VERIFY"
}

View File

@@ -92,7 +92,7 @@
"id": 11,
"name": "[회계관리 > 매출관리] [CREATE] API POST 검증",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_POST_CHECK'};const logs=window.__API_LOGS__||[];const postLogs=logs.filter(l=>l.method==='POST'&&l.status>=200&&l.status<300);R.postCount=postLogs.length;R.found=postLogs.length>0;if(postLogs.length>0){const last=postLogs[postLogs.length-1];R.url=last.url?.substring(0,80);R.status=last.status;R.duration=last.duration;}R.ok=true;R.info=R.found?'✅ POST '+R.status+' ('+R.duration+'ms)':'⚠️ POST 호출 미감지 (API 모니터 범위 밖일 수 있음)';return JSON.stringify(R);})()",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_POST_CHECK'};const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const postLogs=logs.filter(l=>l.method==='POST'&&l.status>=200&&l.status<300);R.postCount=postLogs.length;R.found=postLogs.length>0;if(postLogs.length>0){const last=postLogs[postLogs.length-1];R.url=last.url?.substring(0,80);R.status=last.status;R.duration=last.duration;}R.ok=true;R.info=R.found?'✅ POST '+R.status+' ('+R.duration+'ms)':'⚠️ POST 호출 미감지 (API 모니터 범위 밖일 수 있음)';return JSON.stringify(R);})()",
"timeout": 5000,
"phase": "VERIFY"
},
@@ -190,7 +190,7 @@
"id": 24,
"name": "[회계관리 > 매출관리] [UPDATE] API PUT/PATCH 검증",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_PUT_CHECK'};const logs=window.__API_LOGS__||[];const putLogs=logs.filter(l=>(l.method==='PUT'||l.method==='PATCH')&&l.status>=200&&l.status<300);R.putCount=putLogs.length;R.found=putLogs.length>0;if(putLogs.length>0){const last=putLogs[putLogs.length-1];R.url=last.url?.substring(0,80);R.status=last.status;R.duration=last.duration;}R.ok=true;R.info=R.found?'✅ PUT/PATCH '+R.status+' ('+R.duration+'ms)':'⚠️ PUT/PATCH 미감지';return JSON.stringify(R);})()",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_PUT_CHECK'};const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const putLogs=logs.filter(l=>(l.method==='PUT'||l.method==='PATCH')&&l.status>=200&&l.status<300);R.putCount=putLogs.length;R.found=putLogs.length>0;if(putLogs.length>0){const last=putLogs[putLogs.length-1];R.url=last.url?.substring(0,80);R.status=last.status;R.duration=last.duration;}R.ok=true;R.info=R.found?'✅ PUT/PATCH '+R.status+' ('+R.duration+'ms)':'⚠️ PUT/PATCH 미감지';return JSON.stringify(R);})()",
"timeout": 5000,
"phase": "VERIFY"
},
@@ -235,7 +235,7 @@
"id": 30,
"name": "[회계관리 > 매출관리] [DELETE] API DELETE 검증",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_DELETE_CHECK'};const logs=window.__API_LOGS__||[];const delLogs=logs.filter(l=>l.method==='DELETE'&&l.status>=200&&l.status<300);R.deleteCount=delLogs.length;R.found=delLogs.length>0;if(delLogs.length>0){const last=delLogs[delLogs.length-1];R.url=last.url?.substring(0,80);R.status=last.status;R.duration=last.duration;}R.ok=true;R.info=R.found?'✅ DELETE '+R.status+' ('+R.duration+'ms)':'⚠️ DELETE 미감지';return JSON.stringify(R);})()",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_DELETE_CHECK'};const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const delLogs=logs.filter(l=>l.method==='DELETE'&&l.status>=200&&l.status<300);R.deleteCount=delLogs.length;R.found=delLogs.length>0;if(delLogs.length>0){const last=delLogs[delLogs.length-1];R.url=last.url?.substring(0,80);R.status=last.status;R.duration=last.duration;}R.ok=true;R.info=R.found?'✅ DELETE '+R.status+' ('+R.duration+'ms)':'⚠️ DELETE 미감지';return JSON.stringify(R);})()",
"timeout": 5000,
"phase": "VERIFY"
},
@@ -259,7 +259,7 @@
"id": 33,
"name": "[회계관리 > 매출관리] [VERIFY] API 호출 전체 요약",
"action": "evaluate",
"script": "(()=>{const logs=window.__API_LOGS__||[];const R={phase:'API_SUMMARY',total:logs.length,success:logs.filter(l=>l.ok||l.status<400).length,failed:logs.filter(l=>!l.ok&&l.status>=400).length,avgResponseTime:logs.length>0?Math.round(logs.reduce((s,l)=>s+(l.duration||0),0)/logs.length):0,slowCalls:logs.filter(l=>l.duration>2000).length,methods:{}};logs.forEach(l=>{R.methods[l.method]=(R.methods[l.method]||0)+1;});R.ok=true;R.info='API: '+R.total+'건 (성공:'+R.success+' 실패:'+R.failed+' 평균:'+R.avgResponseTime+'ms)';return JSON.stringify(R);})()",
"script": "(()=>{const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const R={phase:'API_SUMMARY',total:logs.length,success:logs.filter(l=>l.ok||l.status<400).length,failed:logs.filter(l=>!l.ok&&l.status>=400).length,avgResponseTime:logs.length>0?Math.round(logs.reduce((s,l)=>s+(l.duration||0),0)/logs.length):0,slowCalls:logs.filter(l=>l.duration>2000).length,methods:{}};logs.forEach(l=>{R.methods[l.method]=(R.methods[l.method]||0)+1;});R.ok=true;R.info='API: '+R.total+'건 (성공:'+R.success+' 실패:'+R.failed+' 평균:'+R.avgResponseTime+'ms)';return JSON.stringify(R);})()",
"timeout": 5000,
"phase": "VERIFY"
}

View File

@@ -24,7 +24,7 @@
"id": 2,
"name": "[게시판 > 자유게시판] ts 초기화 + sessionStorage 연동",
"action": "evaluate",
"script": "(()=>{try{sessionStorage.removeItem('__E2E_TS__');}catch(e){}delete window.__E2E_TS__;window.__API_LOGS__=window.__API_LOGS__||[];return JSON.stringify({ok:true,cleared:true});})()",
"script": "(()=>{try{sessionStorage.removeItem('__E2E_TS__');}catch(e){}delete window.__E2E_TS__;return JSON.stringify({ok:true,cleared:true});})()",
"timeout": 3000
},
{
@@ -45,7 +45,7 @@
"id": 5,
"name": "[게시판 > 자유게시판] [CREATE] API POST 검증",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_POST_CHECK'};const logs=window.__API_LOGS__||[];const postLogs=logs.filter(l=>l.method==='POST'&&l.status>=200&&l.status<300);R.postCount=postLogs.length;R.found=postLogs.length>0;if(postLogs.length>0){const last=postLogs[postLogs.length-1];R.lastPost={url:last.url?.substring(0,80),status:last.status,duration:last.duration};}R.ok=true;R.info=R.found?'POST API '+R.lastPost.status+' ('+R.lastPost.duration+'ms)':'POST API 미감지 (모니터 타이밍 이슈 가능)';return JSON.stringify(R);})()",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_POST_CHECK'};const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const postLogs=logs.filter(l=>l.method==='POST'&&l.status>=200&&l.status<300);R.postCount=postLogs.length;R.found=postLogs.length>0;if(postLogs.length>0){const last=postLogs[postLogs.length-1];R.lastPost={url:last.url?.substring(0,80),status:last.status,duration:last.duration};}R.ok=true;R.info=R.found?'POST API '+R.lastPost.status+' ('+R.lastPost.duration+'ms)':'POST API 미감지 (모니터 타이밍 이슈 가능)';return JSON.stringify(R);})()",
"timeout": 10000,
"phase": "VERIFY"
},
@@ -119,7 +119,7 @@
"id": 15,
"name": "[게시판 > 자유게시판] [UPDATE] API PUT 검증",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_PUT_CHECK'};const logs=window.__API_LOGS__||[];const putLogs=logs.filter(l=>(l.method==='PUT'||l.method==='PATCH')&&l.status>=200&&l.status<300);R.putCount=putLogs.length;R.found=putLogs.length>0;if(putLogs.length>0){const last=putLogs[putLogs.length-1];R.lastPut={url:last.url?.substring(0,80),status:last.status,duration:last.duration};}R.ok=true;R.info=R.found?'PUT/PATCH API '+R.lastPut.status+' ('+R.lastPut.duration+'ms)':'PUT/PATCH API 미감지';return JSON.stringify(R);})()",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_PUT_CHECK'};const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const putLogs=logs.filter(l=>(l.method==='PUT'||l.method==='PATCH')&&l.status>=200&&l.status<300);R.putCount=putLogs.length;R.found=putLogs.length>0;if(putLogs.length>0){const last=putLogs[putLogs.length-1];R.lastPut={url:last.url?.substring(0,80),status:last.status,duration:last.duration};}R.ok=true;R.info=R.found?'PUT/PATCH API '+R.lastPut.status+' ('+R.lastPut.duration+'ms)':'PUT/PATCH API 미감지';return JSON.stringify(R);})()",
"timeout": 10000,
"phase": "VERIFY"
},
@@ -194,7 +194,7 @@
"id": 25,
"name": "[게시판 > 자유게시판] [DELETE] API DELETE 검증",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_DELETE_CHECK'};const logs=window.__API_LOGS__||[];const delLogs=logs.filter(l=>l.method==='DELETE'&&l.status>=200&&l.status<300);R.delCount=delLogs.length;R.found=delLogs.length>0;if(delLogs.length>0){const last=delLogs[delLogs.length-1];R.lastDelete={url:last.url?.substring(0,80),status:last.status,duration:last.duration};}R.ok=true;R.info=R.found?'DELETE API '+R.lastDelete.status+' ('+R.lastDelete.duration+'ms)':'DELETE API 미감지';return JSON.stringify(R);})()",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_DELETE_CHECK'};const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const delLogs=logs.filter(l=>l.method==='DELETE'&&l.status>=200&&l.status<300);R.delCount=delLogs.length;R.found=delLogs.length>0;if(delLogs.length>0){const last=delLogs[delLogs.length-1];R.lastDelete={url:last.url?.substring(0,80),status:last.status,duration:last.duration};}R.ok=true;R.info=R.found?'DELETE API '+R.lastDelete.status+' ('+R.lastDelete.duration+'ms)':'DELETE API 미감지';return JSON.stringify(R);})()",
"timeout": 10000,
"phase": "VERIFY"
},
@@ -248,7 +248,7 @@
"id": 33,
"name": "[게시판 > 자유게시판] [SUMMARY] API 호출 통계",
"action": "evaluate",
"script": "(async()=>{const R={phase:'API_SUMMARY'};const logs=window.__API_LOGS__||[];R.totalCalls=logs.length;R.successCalls=logs.filter(l=>l.status>=200&&l.status<300).length;R.failedCalls=logs.filter(l=>l.status>=400).length;R.avgResponseTime=logs.length>0?Math.round(logs.reduce((s,l)=>s+(l.duration||0),0)/logs.length):0;R.slowCalls=logs.filter(l=>(l.duration||0)>2000).length;R.methods={GET:logs.filter(l=>l.method==='GET').length,POST:logs.filter(l=>l.method==='POST').length,PUT:logs.filter(l=>l.method==='PUT').length,PATCH:logs.filter(l=>l.method==='PATCH').length,DELETE:logs.filter(l=>l.method==='DELETE').length};R.ok=true;R.info='API: '+R.totalCalls+'건 (성공 '+R.successCalls+', 실패 '+R.failedCalls+', 평균 '+R.avgResponseTime+'ms, 느린호출 '+R.slowCalls+'건)';return JSON.stringify(R);})()",
"script": "(async()=>{const R={phase:'API_SUMMARY'};const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);R.totalCalls=logs.length;R.successCalls=logs.filter(l=>l.status>=200&&l.status<300).length;R.failedCalls=logs.filter(l=>l.status>=400).length;R.avgResponseTime=logs.length>0?Math.round(logs.reduce((s,l)=>s+(l.duration||0),0)/logs.length):0;R.slowCalls=logs.filter(l=>(l.duration||0)>2000).length;R.methods={GET:logs.filter(l=>l.method==='GET').length,POST:logs.filter(l=>l.method==='POST').length,PUT:logs.filter(l=>l.method==='PUT').length,PATCH:logs.filter(l=>l.method==='PATCH').length,DELETE:logs.filter(l=>l.method==='DELETE').length};R.ok=true;R.info='API: '+R.totalCalls+'건 (성공 '+R.successCalls+', 실패 '+R.failedCalls+', 평균 '+R.avgResponseTime+'ms, 느린호출 '+R.slowCalls+'건)';return JSON.stringify(R);})()",
"timeout": 10000,
"phase": "VERIFY"
}

View File

@@ -125,7 +125,7 @@
"id": 16,
"name": "[SUMMARY] API 호출 통계",
"action": "evaluate",
"script": "(()=>{const logs=window.__API_LOGS__||[];const salaryApi=logs.filter(l=>l.url.includes('salary')||l.url.includes('payroll'));return 'pass: API summary - total='+logs.length+' salary_api='+salaryApi.length+' success='+logs.filter(l=>l.status>=200&&l.status<300).length+' failed='+logs.filter(l=>l.status>=400).length;})()"
"script": "(()=>{const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const salaryApi=logs.filter(l=>l.url.includes('salary')||l.url.includes('payroll'));return 'pass: API summary - total='+logs.length+' salary_api='+salaryApi.length+' success='+logs.filter(l=>l.status>=200&&l.status<300).length+' failed='+logs.filter(l=>l.status>=400).length;})()"
}
],
"expectedAPIs": [

View File

@@ -141,7 +141,7 @@
"id": 14,
"name": "[생산관리 > 작업지시 관리] [CREATE] API POST 검증",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(1000);const R={phase:'API_POST_CHECK'};const logs=window.__API_LOGS__||[];const posts=logs.filter(l=>l.method==='POST'&&l.status>=200&&l.status<300);R.postCount=posts.length;R.lastPost=posts.length>0?{url:posts[posts.length-1].url?.substring(0,80),status:posts[posts.length-1].status,duration:posts[posts.length-1].duration}:null;R.ok=true;R.info=posts.length>0?'POST API '+posts[posts.length-1].status+' OK':'warn: POST API 미감지 (타이밍 이슈 가능)';return JSON.stringify(R);})()",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(1000);const R={phase:'API_POST_CHECK'};const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const posts=logs.filter(l=>l.method==='POST'&&l.status>=200&&l.status<300);R.postCount=posts.length;R.lastPost=posts.length>0?{url:posts[posts.length-1].url?.substring(0,80),status:posts[posts.length-1].status,duration:posts[posts.length-1].duration}:null;R.ok=true;R.info=posts.length>0?'POST API '+posts[posts.length-1].status+' OK':'warn: POST API 미감지 (타이밍 이슈 가능)';return JSON.stringify(R);})()",
"timeout": 10000,
"phase": "VERIFY"
},
@@ -225,7 +225,7 @@
"id": 25,
"name": "[생산관리 > 작업지시 관리] [UPDATE] API PUT 검증",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_PUT_CHECK'};const logs=window.__API_LOGS__||[];const puts=logs.filter(l=>(l.method==='PUT'||l.method==='PATCH')&&l.status>=200&&l.status<300);R.putCount=puts.length;R.lastPut=puts.length>0?{url:puts[puts.length-1].url?.substring(0,80),status:puts[puts.length-1].status,duration:puts[puts.length-1].duration}:null;R.ok=true;R.info=puts.length>0?'PUT/PATCH API '+puts[puts.length-1].status+' OK':'warn: PUT/PATCH API 미감지';return JSON.stringify(R);})()",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_PUT_CHECK'};const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const puts=logs.filter(l=>(l.method==='PUT'||l.method==='PATCH')&&l.status>=200&&l.status<300);R.putCount=puts.length;R.lastPut=puts.length>0?{url:puts[puts.length-1].url?.substring(0,80),status:puts[puts.length-1].status,duration:puts[puts.length-1].duration}:null;R.ok=true;R.info=puts.length>0?'PUT/PATCH API '+puts[puts.length-1].status+' OK':'warn: PUT/PATCH API 미감지';return JSON.stringify(R);})()",
"timeout": 10000,
"phase": "VERIFY"
},
@@ -249,7 +249,7 @@
"id": 28,
"name": "[생산관리 > 작업지시 관리] [DELETE] API DELETE 검증",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_DELETE_CHECK'};const logs=window.__API_LOGS__||[];const dels=logs.filter(l=>l.method==='DELETE'&&l.status>=200&&l.status<300);R.deleteCount=dels.length;R.lastDelete=dels.length>0?{url:dels[dels.length-1].url?.substring(0,80),status:dels[dels.length-1].status}:null;R.ok=true;R.info=dels.length>0?'DELETE API '+dels[dels.length-1].status+' OK':'warn: DELETE API 미감지';return JSON.stringify(R);})()",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(500);const R={phase:'API_DELETE_CHECK'};const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const dels=logs.filter(l=>l.method==='DELETE'&&l.status>=200&&l.status<300);R.deleteCount=dels.length;R.lastDelete=dels.length>0?{url:dels[dels.length-1].url?.substring(0,80),status:dels[dels.length-1].status}:null;R.ok=true;R.info=dels.length>0?'DELETE API '+dels[dels.length-1].status+' OK':'warn: DELETE API 미감지';return JSON.stringify(R);})()",
"timeout": 10000,
"phase": "VERIFY"
},
@@ -265,7 +265,7 @@
"id": 30,
"name": "[생산관리 > 작업지시 관리] [FINAL] API 요약 + 콘솔 에러 확인",
"action": "evaluate",
"script": "(()=>{const R={phase:'FINAL_SUMMARY'};const logs=window.__API_LOGS__||[];R.apiSummary={total:logs.length,success:logs.filter(l=>l.ok||l.status<400).length,failed:logs.filter(l=>!l.ok&&l.status>=400).length,avgResponseTime:logs.length>0?Math.round(logs.reduce((s,l)=>s+(l.duration||0),0)/logs.length):0,slowCalls:logs.filter(l=>l.duration>2000).length,methods:{GET:logs.filter(l=>l.method==='GET').length,POST:logs.filter(l=>l.method==='POST').length,PUT:logs.filter(l=>l.method==='PUT'||l.method==='PATCH').length,DELETE:logs.filter(l=>l.method==='DELETE').length}};const errs=window.__CONSOLE_ERRORS__||[];R.consoleErrors=errs.length;R.errorSamples=errs.slice(0,3);R.ok=true;return JSON.stringify(R);})()",
"script": "(()=>{const R={phase:'FINAL_SUMMARY'};const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);R.apiSummary={total:logs.length,success:logs.filter(l=>l.ok||l.status<400).length,failed:logs.filter(l=>!l.ok&&l.status>=400).length,avgResponseTime:logs.length>0?Math.round(logs.reduce((s,l)=>s+(l.duration||0),0)/logs.length):0,slowCalls:logs.filter(l=>l.duration>2000).length,methods:{GET:logs.filter(l=>l.method==='GET').length,POST:logs.filter(l=>l.method==='POST').length,PUT:logs.filter(l=>l.method==='PUT'||l.method==='PATCH').length,DELETE:logs.filter(l=>l.method==='DELETE').length}};const errs=window.__CONSOLE_ERRORS__||[];R.consoleErrors=errs.length;R.errorSamples=errs.slice(0,3);R.ok=true;return JSON.stringify(R);})()",
"timeout": 10000,
"phase": "VERIFY"
}

View File

@@ -125,7 +125,7 @@
"id": 17,
"name": "[SUMMARY] API 호출 통계",
"action": "evaluate",
"script": "(() => { const logs = window.__E2E__ ? window.__E2E__.getApiLogs().logs : (window.__API_LOGS__ || []); const inspApi = logs.filter(l => l.url?.includes('inspection')); const failedApis = logs.filter(l => l.status >= 400); return 'pass: API total=' + logs.length + ' inspection=' + inspApi.length + ' failed=' + failedApis.length; })()"
"script": "(() => { const logs = window.__E2E__ ? window.__E2E__.getApiLogs().logs : ((window.__E2E__?window.__E2E__.getApiLogs().logs:[])); const inspApi = logs.filter(l => l.url?.includes('inspection')); const failedApis = logs.filter(l => l.status >= 400); return 'pass: API total=' + logs.length + ' inspection=' + inspApi.length + ' failed=' + failedApis.length; })()"
}
],
"expectedAPIs": [

View File

@@ -90,7 +90,7 @@
"id": 12,
"name": "[SUMMARY] API 호출 통계",
"action": "evaluate",
"script": "(() => { const logs = window.__E2E__ ? window.__E2E__.getApiLogs().logs : (window.__API_LOGS__ || []); const perfApi = logs.filter(l => l.url?.includes('performance')); const failedApis = logs.filter(l => l.status >= 400); return 'pass: API total=' + logs.length + ' performance=' + perfApi.length + ' failed=' + failedApis.length; })()"
"script": "(() => { const logs = window.__E2E__ ? window.__E2E__.getApiLogs().logs : ((window.__E2E__?window.__E2E__.getApiLogs().logs:[])); const perfApi = logs.filter(l => l.url?.includes('performance')); const failedApis = logs.filter(l => l.status >= 400); return 'pass: API total=' + logs.length + ' performance=' + perfApi.length + ' failed=' + failedApis.length; })()"
}
],
"knownIssues": [

View File

@@ -30,7 +30,7 @@
"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);sessionStorage.setItem('__E2E_TS__',ts);}catch(e){}const R={phase:'CREATE',ts};const btn=Array.from(document.querySelectorAll('button')).find(b=>/입금.*등록|입금등록|등록/.test(b.innerText?.trim()));if(!btn){R.error='등록 버튼 없음';return JSON.stringify(R);}btn.click();await w(2500);R.url=location.pathname+location.search;const formArea=document.querySelector('main')||document.querySelector('[class*=\"content\"]')||document.body;const combos=Array.from(formArea.querySelectorAll('button[role=\"combobox\"]')).filter(b=>b.offsetParent!==null&&!b.closest('nav,[class*=sidebar],[class*=Sidebar]'));R.comboCount=combos.length;for(let i=0;i<combos.length;i++){document.body.click();await w(200);combos[i].scrollIntoView({block:'center'});combos[i].click();await w(700);const lb=document.querySelector('[role=\"listbox\"]');if(lb){const opt=lb.querySelector('[role=\"option\"]');if(opt){opt.click();await w(500);}}else{document.dispatchEvent(new KeyboardEvent('keydown',{key:'Escape',bubbles:true}));await w(200);}}await w(500);const dateButtons=Array.from(formArea.querySelectorAll('button')).filter(b=>b.innerText?.trim()==='날짜 선택'&&b.offsetParent!==null);R.dateButtonCount=dateButtons.length;for(const db of dateButtons){db.scrollIntoView({block:'center'});await w(200);db.click();await w(600);if(!document.querySelector('table[class*=\"rdp\"],.rdp-month,[role=\"grid\"]')){db.click();await w(800);}const today=document.querySelector('[aria-selected=\"true\"]')||document.querySelector('button[name=\"day\"].bg-primary')||document.querySelector('.rdp-day_today button')||Array.from(document.querySelectorAll('button[name=\"day\"],td[role=\"gridcell\"] button,.rdp-day button')).find(b=>b.getAttribute('aria-selected')==='true'||b.classList.contains('bg-primary')||b.tabIndex===0)||document.querySelector('button[name=\"day\"]')||document.querySelector('td[role=\"gridcell\"] button');if(today){today.click();await w(400);}else{document.dispatchEvent(new KeyboardEvent('keydown',{key:'Escape',bubbles:true}));await w(300);}}await w(300);const nameInput=document.querySelector('input[placeholder*=\"입금자명\"]')||document.querySelector('input[placeholder*=\"입금자\"]');if(nameInput){sv(nameInput,'E2E_TEST_입금자_'+ts);await w(200);}R.nameFound=!!nameInput;const amtInput=document.querySelector('input[placeholder*=\"입금금액\"]')||document.querySelector('input[inputmode=\"numeric\"]')||document.querySelector('input[type=\"number\"]');if(amtInput){sv(amtInput,'50000');await w(200);}const noteInput=document.querySelector('input[placeholder*=\"적요\"]')||document.querySelector('textarea[placeholder*=\"적요\"]');if(noteInput){sv(noteInput,'E2E_TEST_입금_'+ts);await w(200);}R.noteFound=!!noteInput;if(nameInput&&!nameInput.value?.includes('E2E_TEST_')){sv(nameInput,'E2E_TEST_입금자_'+ts);await w(200);}if(noteInput&&!noteInput.value?.includes('E2E_TEST_')){sv(noteInput,'E2E_TEST_입금_'+ts);await w(200);}const submitBtn=Array.from(document.querySelectorAll('button')).find(b=>/^등록$|^저장$/.test(b.innerText?.trim())&&b.offsetParent!==null);if(!submitBtn){R.error='등록 버튼 없음';return JSON.stringify(R);}submitBtn.click();await w(3000);R.urlAfter=location.pathname+location.search;R.navigatedBack=!location.search.includes('mode=new');if(R.navigatedBack){R.ok=true;}else{const _t=document.querySelector('[class*=\"toast\"],[class*=\"Toastify\"],[role=\"alert\"]');const _al=window.__API_LOGS__||[];const _ps=_al.some(l=>l.method==='POST'&&l.ok);R.hasToast=!!_t;R.hasPostSuccess=_ps;if(_ps||_t){R.ok=true;R.warn='등록 성공(API/토스트 확인) but 리다이렉트 미동작';}else{R.ok=false;R.error='등록 실패 (API POST 없음, url='+R.urlAfter+')';}}return JSON.stringify(R);})()",
"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);sessionStorage.setItem('__E2E_TS__',ts);}catch(e){}const R={phase:'CREATE',ts};const btn=Array.from(document.querySelectorAll('button')).find(b=>/입금.*등록|입금등록|등록/.test(b.innerText?.trim()));if(!btn){R.error='등록 버튼 없음';return JSON.stringify(R);}btn.click();await w(2500);R.url=location.pathname+location.search;const formArea=document.querySelector('main')||document.querySelector('[class*=\"content\"]')||document.body;const combos=Array.from(formArea.querySelectorAll('button[role=\"combobox\"]')).filter(b=>b.offsetParent!==null&&!b.closest('nav,[class*=sidebar],[class*=Sidebar]'));R.comboCount=combos.length;for(let i=0;i<combos.length;i++){document.body.click();await w(200);combos[i].scrollIntoView({block:'center'});combos[i].click();await w(700);const lb=document.querySelector('[role=\"listbox\"]');if(lb){const opt=lb.querySelector('[role=\"option\"]');if(opt){opt.click();await w(500);}}else{document.dispatchEvent(new KeyboardEvent('keydown',{key:'Escape',bubbles:true}));await w(200);}}await w(500);const dateButtons=Array.from(formArea.querySelectorAll('button')).filter(b=>b.innerText?.trim()==='날짜 선택'&&b.offsetParent!==null);R.dateButtonCount=dateButtons.length;for(const db of dateButtons){db.scrollIntoView({block:'center'});await w(200);db.click();await w(600);if(!document.querySelector('table[class*=\"rdp\"],.rdp-month,[role=\"grid\"]')){db.click();await w(800);}const today=document.querySelector('[aria-selected=\"true\"]')||document.querySelector('button[name=\"day\"].bg-primary')||document.querySelector('.rdp-day_today button')||Array.from(document.querySelectorAll('button[name=\"day\"],td[role=\"gridcell\"] button,.rdp-day button')).find(b=>b.getAttribute('aria-selected')==='true'||b.classList.contains('bg-primary')||b.tabIndex===0)||document.querySelector('button[name=\"day\"]')||document.querySelector('td[role=\"gridcell\"] button');if(today){today.click();await w(400);}else{document.dispatchEvent(new KeyboardEvent('keydown',{key:'Escape',bubbles:true}));await w(300);}}await w(300);const nameInput=document.querySelector('input[placeholder*=\"입금자명\"]')||document.querySelector('input[placeholder*=\"입금자\"]');if(nameInput){sv(nameInput,'E2E_TEST_입금자_'+ts);await w(200);}R.nameFound=!!nameInput;const amtInput=document.querySelector('input[placeholder*=\"입금금액\"]')||document.querySelector('input[inputmode=\"numeric\"]')||document.querySelector('input[type=\"number\"]');if(amtInput){sv(amtInput,'50000');await w(200);}const noteInput=document.querySelector('input[placeholder*=\"적요\"]')||document.querySelector('textarea[placeholder*=\"적요\"]');if(noteInput){sv(noteInput,'E2E_TEST_입금_'+ts);await w(200);}R.noteFound=!!noteInput;if(nameInput&&!nameInput.value?.includes('E2E_TEST_')){sv(nameInput,'E2E_TEST_입금자_'+ts);await w(200);}if(noteInput&&!noteInput.value?.includes('E2E_TEST_')){sv(noteInput,'E2E_TEST_입금_'+ts);await w(200);}const submitBtn=Array.from(document.querySelectorAll('button')).find(b=>/^등록$|^저장$/.test(b.innerText?.trim())&&b.offsetParent!==null);if(!submitBtn){R.error='등록 버튼 없음';return JSON.stringify(R);}submitBtn.click();await w(3000);R.urlAfter=location.pathname+location.search;R.navigatedBack=!location.search.includes('mode=new');if(R.navigatedBack){R.ok=true;}else{const _t=document.querySelector('[class*=\"toast\"],[class*=\"Toastify\"],[role=\"alert\"]');const _al=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const _ps=_al.some(l=>l.method==='POST'&&l.ok);R.hasToast=!!_t;R.hasPostSuccess=_ps;if(_ps||_t){R.ok=true;R.warn='등록 성공(API/토스트 확인) but 리다이렉트 미동작';}else{R.ok=false;R.error='등록 실패 (API POST 없음, url='+R.urlAfter+')';}}return JSON.stringify(R);})()",
"timeout": 30000,
"phase": "CREATE"
},

View File

@@ -124,7 +124,7 @@
"id": 16,
"name": "[회계관리 > 매출관리] [UPDATE] 수정 내용 검증 (공급가액 1,000,000 재계산 확인)",
"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:'VERIFY_UPDATE'};const pageText=document.body.innerText;R.toast=toastInfo();const toastOk=R.toast.text&&(/수정|완료|저장|성공/.test(R.toast.text));R.hasSupply1M=pageText.includes('1,000,000')||pageText.includes('1000000');R.toastOk=toastOk;R.url=location.pathname+location.search;R.onListPage=!location.search.includes('mode=edit')&&!location.search.includes('mode=view')&&!/\\/[0-9]+$/.test(location.pathname);const logs=(window.__API_LOGS__||[]);R.apiPutOk=logs.some(l=>(l.method==='PUT'||l.method==='PATCH')&&l.ok);if(!toastOk&&!R.hasSupply1M&&R.onListPage){R.info='save redirected to list (toast expired), verifying via row click';const rows=document.querySelectorAll('table tbody tr');let target=Array.from(rows).find(r=>r.innerText?.includes('E2E_TEST_'));if(!target&&rows.length>0)target=rows[0];if(target){target.click();await w(2500);const dt=document.body.innerText;R.hasSupply1M=dt.includes('1,000,000')||dt.includes('1000000');R.reEnteredDetail=true;}}R.ok=toastOk||R.hasSupply1M||R.apiPutOk||(R.onListPage&&!location.search.includes('mode=edit'));return JSON.stringify(R);})()",
"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:'VERIFY_UPDATE'};const pageText=document.body.innerText;R.toast=toastInfo();const toastOk=R.toast.text&&(/수정|완료|저장|성공/.test(R.toast.text));R.hasSupply1M=pageText.includes('1,000,000')||pageText.includes('1000000');R.toastOk=toastOk;R.url=location.pathname+location.search;R.onListPage=!location.search.includes('mode=edit')&&!location.search.includes('mode=view')&&!/\\/[0-9]+$/.test(location.pathname);const logs=((window.__E2E__?window.__E2E__.getApiLogs().logs:[]));R.apiPutOk=logs.some(l=>(l.method==='PUT'||l.method==='PATCH')&&l.ok);if(!toastOk&&!R.hasSupply1M&&R.onListPage){R.info='save redirected to list (toast expired), verifying via row click';const rows=document.querySelectorAll('table tbody tr');let target=Array.from(rows).find(r=>r.innerText?.includes('E2E_TEST_'));if(!target&&rows.length>0)target=rows[0];if(target){target.click();await w(2500);const dt=document.body.innerText;R.hasSupply1M=dt.includes('1,000,000')||dt.includes('1000000');R.reEnteredDetail=true;}}R.ok=toastOk||R.hasSupply1M||R.apiPutOk||(R.onListPage&&!location.search.includes('mode=edit'));return JSON.stringify(R);})()",
"timeout": 15000,
"phase": "UPDATE"
},

View File

@@ -156,7 +156,7 @@
"name": "[BULK-DELETE] API DELETE /orders/bulk 검증",
"phase": "DELETE",
"action": "evaluate",
"script": "(()=>{const logs=window.__API_LOGS__||[];const bulkDels=logs.filter(l=>l.method==='DELETE'&&(l.url.includes('bulk')||l.url.includes('orders'))&&l.status>=200&&l.status<300);const singleDels=logs.filter(l=>l.method==='DELETE'&&l.status>=200&&l.status<300);return bulkDels.length>0?'pass: BULK DELETE '+bulkDels[bulkDels.length-1].status+' url='+bulkDels[bulkDels.length-1].url.split('/').slice(-2).join('/'):singleDels.length>0?'pass: DELETE found ('+singleDels.length+' calls)':'warn: no DELETE API calls found';})()"
"script": "(()=>{const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const bulkDels=logs.filter(l=>l.method==='DELETE'&&(l.url.includes('bulk')||l.url.includes('orders'))&&l.status>=200&&l.status<300);const singleDels=logs.filter(l=>l.method==='DELETE'&&l.status>=200&&l.status<300);return bulkDels.length>0?'pass: BULK DELETE '+bulkDels[bulkDels.length-1].status+' url='+bulkDels[bulkDels.length-1].url.split('/').slice(-2).join('/'):singleDels.length>0?'pass: DELETE found ('+singleDels.length+' calls)':'warn: no DELETE API calls found';})()"
},
{
"id": 19,
@@ -170,7 +170,7 @@
"id": 20,
"name": "[SUMMARY] API 호출 통계",
"action": "evaluate",
"script": "(()=>{const logs=window.__API_LOGS__||[];const summary={total:logs.length,GET:logs.filter(l=>l.method==='GET').length,POST:logs.filter(l=>l.method==='POST').length,PUT:logs.filter(l=>l.method==='PUT').length,DELETE:logs.filter(l=>l.method==='DELETE').length,success:logs.filter(l=>l.status>=200&&l.status<300).length,failed:logs.filter(l=>l.status>=400).length,avgResponseTime:logs.length>0?Math.round(logs.reduce((s,l)=>s+(l.duration||0),0)/logs.length):0};return 'pass: API summary - total='+summary.total+' POST='+summary.POST+' DELETE='+summary.DELETE+' success='+summary.success+' failed='+summary.failed+' avg='+summary.avgResponseTime+'ms';})()"
"script": "(()=>{const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const summary={total:logs.length,GET:logs.filter(l=>l.method==='GET').length,POST:logs.filter(l=>l.method==='POST').length,PUT:logs.filter(l=>l.method==='PUT').length,DELETE:logs.filter(l=>l.method==='DELETE').length,success:logs.filter(l=>l.status>=200&&l.status<300).length,failed:logs.filter(l=>l.status>=400).length,avgResponseTime:logs.length>0?Math.round(logs.reduce((s,l)=>s+(l.duration||0),0)/logs.length):0};return 'pass: API summary - total='+summary.total+' POST='+summary.POST+' DELETE='+summary.DELETE+' success='+summary.success+' failed='+summary.failed+' avg='+summary.avgResponseTime+'ms';})()"
}
],
"expectedAPIs": [

View File

@@ -184,7 +184,7 @@
"name": "[CREATE] API POST 검증",
"phase": "CREATE",
"action": "evaluate",
"script": "(()=>{const logs=window.__API_LOGS__||[];const posts=logs.filter(l=>l.method==='POST'&&l.status>=200&&l.status<300);return posts.length>0?'pass: POST '+posts[posts.length-1].status+' ('+posts[posts.length-1].url.split('/').pop()+')':'warn: no successful POST found';})()"
"script": "(()=>{const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const posts=logs.filter(l=>l.method==='POST'&&l.status>=200&&l.status<300);return posts.length>0?'pass: POST '+posts[posts.length-1].status+' ('+posts[posts.length-1].url.split('/').pop()+')':'warn: no successful POST found';})()"
},
{
"id": 19,
@@ -269,7 +269,7 @@
"name": "[UPDATE] API PUT 검증",
"phase": "UPDATE",
"action": "evaluate",
"script": "(()=>{const logs=window.__API_LOGS__||[];const puts=logs.filter(l=>(l.method==='PUT'||l.method==='PATCH')&&l.status>=200&&l.status<300);return puts.length>0?'pass: '+puts[puts.length-1].method+' '+puts[puts.length-1].status+' ('+puts[puts.length-1].url.split('/').pop()+')':'warn: no successful PUT/PATCH found';})()"
"script": "(()=>{const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const puts=logs.filter(l=>(l.method==='PUT'||l.method==='PATCH')&&l.status>=200&&l.status<300);return puts.length>0?'pass: '+puts[puts.length-1].method+' '+puts[puts.length-1].status+' ('+puts[puts.length-1].url.split('/').pop()+')':'warn: no successful PUT/PATCH found';})()"
},
{
"id": 30,
@@ -285,14 +285,14 @@
"name": "[DELETE] API DELETE 검증 + 목록 복귀",
"phase": "DELETE",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'DELETE_VERIFY'};const logs=window.__API_LOGS__||[];const dels=logs.filter(l=>l.method==='DELETE'&&l.status>=200&&l.status<300);R.deleteApiFound=dels.length>0;if(dels.length>0)R.deleteStatus=dels[dels.length-1].status;await w(2000);const onForm=location.search.includes('mode=view')||location.search.includes('mode=edit')||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(1000);const rows=Array.from(document.querySelectorAll('table tbody tr'));const ts=window.__E2E_TS__||sessionStorage.getItem('__E2E_TS__')||'IMPOSSIBLE';const stillExists=rows.some(r=>r.innerText?.includes(ts)&&r.innerText?.includes('E2E_TEST_'));R.stillExists=stillExists;R.ok=!stillExists;R.info=stillExists?'warn: E2E data still in list':'pass: E2E data removed from list';return JSON.stringify(R);})()",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'DELETE_VERIFY'};const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const dels=logs.filter(l=>l.method==='DELETE'&&l.status>=200&&l.status<300);R.deleteApiFound=dels.length>0;if(dels.length>0)R.deleteStatus=dels[dels.length-1].status;await w(2000);const onForm=location.search.includes('mode=view')||location.search.includes('mode=edit')||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(1000);const rows=Array.from(document.querySelectorAll('table tbody tr'));const ts=window.__E2E_TS__||sessionStorage.getItem('__E2E_TS__')||'IMPOSSIBLE';const stillExists=rows.some(r=>r.innerText?.includes(ts)&&r.innerText?.includes('E2E_TEST_'));R.stillExists=stillExists;R.ok=!stillExists;R.info=stillExists?'warn: E2E data still in list':'pass: E2E data removed from list';return JSON.stringify(R);})()",
"timeout": 20000
},
{
"id": 32,
"name": "[SUMMARY] API 호출 통계",
"action": "evaluate",
"script": "(()=>{const logs=window.__API_LOGS__||[];const summary={total:logs.length,GET:logs.filter(l=>l.method==='GET').length,POST:logs.filter(l=>l.method==='POST').length,PUT:logs.filter(l=>l.method==='PUT'||l.method==='PATCH').length,DELETE:logs.filter(l=>l.method==='DELETE').length,success:logs.filter(l=>l.status>=200&&l.status<300).length,failed:logs.filter(l=>l.status>=400).length,avgResponseTime:logs.length>0?Math.round(logs.reduce((s,l)=>s+(l.duration||0),0)/logs.length):0,slowCalls:logs.filter(l=>l.duration>2000).length};return 'pass: API summary - total='+summary.total+' GET='+summary.GET+' POST='+summary.POST+' PUT='+summary.PUT+' DELETE='+summary.DELETE+' success='+summary.success+' failed='+summary.failed+' avg='+summary.avgResponseTime+'ms slow='+summary.slowCalls;})()"
"script": "(()=>{const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const summary={total:logs.length,GET:logs.filter(l=>l.method==='GET').length,POST:logs.filter(l=>l.method==='POST').length,PUT:logs.filter(l=>l.method==='PUT'||l.method==='PATCH').length,DELETE:logs.filter(l=>l.method==='DELETE').length,success:logs.filter(l=>l.status>=200&&l.status<300).length,failed:logs.filter(l=>l.status>=400).length,avgResponseTime:logs.length>0?Math.round(logs.reduce((s,l)=>s+(l.duration||0),0)/logs.length):0,slowCalls:logs.filter(l=>l.duration>2000).length};return 'pass: API summary - total='+summary.total+' GET='+summary.GET+' POST='+summary.POST+' PUT='+summary.PUT+' DELETE='+summary.DELETE+' success='+summary.success+' failed='+summary.failed+' avg='+summary.avgResponseTime+'ms slow='+summary.slowCalls;})()"
}
],
"expectedAPIs": [

View File

@@ -174,7 +174,7 @@
"name": "[CREATE] API POST 검증",
"phase": "CREATE",
"action": "evaluate",
"script": "(()=>{const logs=window.__API_LOGS__||[];const posts=logs.filter(l=>l.method==='POST'&&l.status>=200&&l.status<300);return posts.length>0?'pass: POST '+posts[posts.length-1].status+' ('+posts[posts.length-1].url.split('/').pop()+')':'warn: no successful POST found';})()"
"script": "(()=>{const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const posts=logs.filter(l=>l.method==='POST'&&l.status>=200&&l.status<300);return posts.length>0?'pass: POST '+posts[posts.length-1].status+' ('+posts[posts.length-1].url.split('/').pop()+')':'warn: no successful POST found';})()"
},
{
"id": 18,
@@ -266,7 +266,7 @@
"name": "[UPDATE] API PUT 검증",
"phase": "UPDATE",
"action": "evaluate",
"script": "(()=>{const logs=window.__API_LOGS__||[];const puts=logs.filter(l=>(l.method==='PUT'||l.method==='PATCH')&&l.status>=200&&l.status<300);return puts.length>0?'pass: '+puts[puts.length-1].method+' '+puts[puts.length-1].status+' ('+puts[puts.length-1].url.split('/').pop()+')':'warn: no successful PUT/PATCH found';})()"
"script": "(()=>{const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const puts=logs.filter(l=>(l.method==='PUT'||l.method==='PATCH')&&l.status>=200&&l.status<300);return puts.length>0?'pass: '+puts[puts.length-1].method+' '+puts[puts.length-1].status+' ('+puts[puts.length-1].url.split('/').pop()+')':'warn: no successful PUT/PATCH found';})()"
},
{
"id": 30,
@@ -282,14 +282,14 @@
"name": "[DELETE] API DELETE 검증 + 목록 복귀",
"phase": "DELETE",
"action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'DELETE_VERIFY'};const logs=window.__API_LOGS__||[];const dels=logs.filter(l=>l.method==='DELETE'&&l.status>=200&&l.status<300);R.deleteApiFound=dels.length>0;if(dels.length>0)R.deleteStatus=dels[dels.length-1].status;await w(2000);const onForm=location.search.includes('mode=view')||location.search.includes('mode=edit')||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(1000);const rows=Array.from(document.querySelectorAll('table tbody tr'));const ts=window.__E2E_TS__||sessionStorage.getItem('__E2E_TS__')||'IMPOSSIBLE';const stillExists=rows.some(r=>r.innerText?.includes(ts)&&r.innerText?.includes('E2E_TEST_'));R.stillExists=stillExists;R.ok=!stillExists;R.info=stillExists?'warn: E2E data still in list':'pass: E2E data removed from list';return JSON.stringify(R);})()",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'DELETE_VERIFY'};const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const dels=logs.filter(l=>l.method==='DELETE'&&l.status>=200&&l.status<300);R.deleteApiFound=dels.length>0;if(dels.length>0)R.deleteStatus=dels[dels.length-1].status;await w(2000);const onForm=location.search.includes('mode=view')||location.search.includes('mode=edit')||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(1000);const rows=Array.from(document.querySelectorAll('table tbody tr'));const ts=window.__E2E_TS__||sessionStorage.getItem('__E2E_TS__')||'IMPOSSIBLE';const stillExists=rows.some(r=>r.innerText?.includes(ts)&&r.innerText?.includes('E2E_TEST_'));R.stillExists=stillExists;R.ok=!stillExists;R.info=stillExists?'warn: E2E data still in list':'pass: E2E data removed from list';return JSON.stringify(R);})()",
"timeout": 20000
},
{
"id": 32,
"name": "[SUMMARY] API 호출 통계",
"action": "evaluate",
"script": "(()=>{const logs=window.__API_LOGS__||[];const summary={total:logs.length,GET:logs.filter(l=>l.method==='GET').length,POST:logs.filter(l=>l.method==='POST').length,PUT:logs.filter(l=>l.method==='PUT'||l.method==='PATCH').length,DELETE:logs.filter(l=>l.method==='DELETE').length,success:logs.filter(l=>l.status>=200&&l.status<300).length,failed:logs.filter(l=>l.status>=400).length,avgResponseTime:logs.length>0?Math.round(logs.reduce((s,l)=>s+(l.duration||0),0)/logs.length):0,slowCalls:logs.filter(l=>l.duration>2000).length};return 'pass: API summary - total='+summary.total+' GET='+summary.GET+' POST='+summary.POST+' PUT='+summary.PUT+' DELETE='+summary.DELETE+' success='+summary.success+' failed='+summary.failed+' avg='+summary.avgResponseTime+'ms slow='+summary.slowCalls;})()"
"script": "(()=>{const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);const summary={total:logs.length,GET:logs.filter(l=>l.method==='GET').length,POST:logs.filter(l=>l.method==='POST').length,PUT:logs.filter(l=>l.method==='PUT'||l.method==='PATCH').length,DELETE:logs.filter(l=>l.method==='DELETE').length,success:logs.filter(l=>l.status>=200&&l.status<300).length,failed:logs.filter(l=>l.status>=400).length,avgResponseTime:logs.length>0?Math.round(logs.reduce((s,l)=>s+(l.duration||0),0)/logs.length):0,slowCalls:logs.filter(l=>l.duration>2000).length};return 'pass: API summary - total='+summary.total+' GET='+summary.GET+' POST='+summary.POST+' PUT='+summary.PUT+' DELETE='+summary.DELETE+' success='+summary.success+' failed='+summary.failed+' avg='+summary.avgResponseTime+'ms slow='+summary.slowCalls;})()"
}
],
"expectedAPIs": [

View File

@@ -121,7 +121,7 @@
"id": 17,
"name": "대시보드 API 호출 확인",
"action": "evaluate",
"script": "(() => { const logs = window.__API_LOGS__ || []; const dashAPIs = logs.filter(l => l.url?.includes('dashboard') || l.url?.includes('summary') || l.url?.includes('statistics')); return 'Dashboard API calls: ' + dashAPIs.length + ', Total API calls: ' + logs.length; })()"
"script": "(() => { const logs = (window.__E2E__?window.__E2E__.getApiLogs().logs:[]); const dashAPIs = logs.filter(l => l.url?.includes('dashboard') || l.url?.includes('summary') || l.url?.includes('statistics')); return 'Dashboard API calls: ' + dashAPIs.length + ', Total API calls: ' + logs.length; })()"
},
{
"id": 18,

View File

@@ -313,7 +313,7 @@
"id": 36,
"name": "[회계관리 > 거래처관리] [FINAL] API 요약 + 콘솔 에러 확인",
"action": "evaluate",
"script": "(()=>{const R={phase:'FINAL_SUMMARY'};const logs=window.__API_LOGS__||[];R.apiSummary={total:logs.length,success:logs.filter(l=>l.ok||l.status<400).length,failed:logs.filter(l=>!l.ok&&l.status>=400).length,avgResponseTime:logs.length>0?Math.round(logs.reduce((s,l)=>s+(l.duration||0),0)/logs.length):0,slowCalls:logs.filter(l=>l.duration>2000).length,methods:{GET:logs.filter(l=>l.method==='GET').length,POST:logs.filter(l=>l.method==='POST').length,PUT:logs.filter(l=>l.method==='PUT'||l.method==='PATCH').length,DELETE:logs.filter(l=>l.method==='DELETE').length}};const errs=window.__CONSOLE_ERRORS__||[];R.consoleErrors=errs.length;R.errorSamples=errs.slice(0,5);R.ok=true;R.info='API calls: '+logs.length+', errors: '+errs.length;return JSON.stringify(R);})()",
"script": "(()=>{const R={phase:'FINAL_SUMMARY'};const logs=(window.__E2E__?window.__E2E__.getApiLogs().logs:[]);R.apiSummary={total:logs.length,success:logs.filter(l=>l.ok||l.status<400).length,failed:logs.filter(l=>!l.ok&&l.status>=400).length,avgResponseTime:logs.length>0?Math.round(logs.reduce((s,l)=>s+(l.duration||0),0)/logs.length):0,slowCalls:logs.filter(l=>l.duration>2000).length,methods:{GET:logs.filter(l=>l.method==='GET').length,POST:logs.filter(l=>l.method==='POST').length,PUT:logs.filter(l=>l.method==='PUT'||l.method==='PATCH').length,DELETE:logs.filter(l=>l.method==='DELETE').length}};const errs=window.__CONSOLE_ERRORS__||[];R.consoleErrors=errs.length;R.errorSamples=errs.slice(0,5);R.ok=true;R.info='API calls: '+logs.length+', errors: '+errs.length;return JSON.stringify(R);})()",
"timeout": 10000,
"phase": "VERIFY"
}