From f42cf4ab7d136ac51c2a6bfe585c63a09d961cb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Wed, 4 Mar 2026 11:42:23 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20deprecated=20window.=5F=5FAPI=5FLOGS=5F?= =?UTF-8?q?=5F=20=E2=86=92=20window.=5F=5FE2E=5F=5F.getApiLogs()=20?= =?UTF-8?q?=ED=8C=A8=ED=84=B4=20=EC=88=98=EC=A0=95=20(17=EA=B0=9C=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- approval-box.json | 6 +++--- edge-rapid-click-acc-sales.json | 2 +- full-crud-acc-bills.json | 10 +++++----- full-crud-acc-deposit.json | 8 ++++---- full-crud-acc-sales.json | 8 ++++---- full-crud-board.json | 10 +++++----- hr-salary-long-term-care.json | 2 +- production-work-order.json | 8 ++++---- quality-inspection.json | 2 +- quality-performance-report.json | 2 +- reload-persist-acc-deposit.json | 2 +- sales-management.json | 2 +- sales-order-bulk-delete.json | 4 ++-- sales-order.json | 8 ++++---- sales-quotation.json | 8 ++++---- system-dashboard.json | 2 +- vendor-management.json | 2 +- 17 files changed, 43 insertions(+), 43 deletions(-) diff --git a/approval-box.json b/approval-box.json index cf688a1..9bd6389 100644 --- a/approval-box.json +++ b/approval-box.json @@ -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" } diff --git a/edge-rapid-click-acc-sales.json b/edge-rapid-click-acc-sales.json index ce939df..8d01809 100644 --- a/edge-rapid-click-acc-sales.json +++ b/edge-rapid-click-acc-sales.json @@ -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" }, diff --git a/full-crud-acc-bills.json b/full-crud-acc-bills.json index dc16390..45466dd 100644 --- a/full-crud-acc-bills.json +++ b/full-crud-acc-bills.json @@ -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" } diff --git a/full-crud-acc-deposit.json b/full-crud-acc-deposit.json index 4cab265..98f6ef7 100644 --- a/full-crud-acc-deposit.json +++ b/full-crud-acc-deposit.json @@ -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" } diff --git a/full-crud-acc-sales.json b/full-crud-acc-sales.json index c4d27d3..eb30011 100644 --- a/full-crud-acc-sales.json +++ b/full-crud-acc-sales.json @@ -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" } diff --git a/full-crud-board.json b/full-crud-board.json index c7f8408..a62bb33 100644 --- a/full-crud-board.json +++ b/full-crud-board.json @@ -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" } diff --git a/hr-salary-long-term-care.json b/hr-salary-long-term-care.json index 01e852f..58d768d 100644 --- a/hr-salary-long-term-care.json +++ b/hr-salary-long-term-care.json @@ -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": [ diff --git a/production-work-order.json b/production-work-order.json index 211dc4d..d9c5a02 100644 --- a/production-work-order.json +++ b/production-work-order.json @@ -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" } diff --git a/quality-inspection.json b/quality-inspection.json index 32bf7db..c8d327a 100644 --- a/quality-inspection.json +++ b/quality-inspection.json @@ -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": [ diff --git a/quality-performance-report.json b/quality-performance-report.json index 07d9686..ad930ff 100644 --- a/quality-performance-report.json +++ b/quality-performance-report.json @@ -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": [ diff --git a/reload-persist-acc-deposit.json b/reload-persist-acc-deposit.json index a3b01ff..dab9e1a 100644 --- a/reload-persist-acc-deposit.json +++ b/reload-persist-acc-deposit.json @@ -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;ib.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;ib.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" }, diff --git a/sales-management.json b/sales-management.json index 13ef7af..1189b33 100644 --- a/sales-management.json +++ b/sales-management.json @@ -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" }, diff --git a/sales-order-bulk-delete.json b/sales-order-bulk-delete.json index 37d0ff4..f0af298 100644 --- a/sales-order-bulk-delete.json +++ b/sales-order-bulk-delete.json @@ -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": [ diff --git a/sales-order.json b/sales-order.json index 77fa5a2..9cc8ff3 100644 --- a/sales-order.json +++ b/sales-order.json @@ -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": [ diff --git a/sales-quotation.json b/sales-quotation.json index df52052..4ac0678 100644 --- a/sales-quotation.json +++ b/sales-quotation.json @@ -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": [ diff --git a/system-dashboard.json b/system-dashboard.json index 7d55100..98bdca7 100644 --- a/system-dashboard.json +++ b/system-dashboard.json @@ -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, diff --git a/vendor-management.json b/vendor-management.json index 699244a..ff5d089 100644 --- a/vendor-management.json +++ b/vendor-management.json @@ -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" }