fix: 급여 장기요양 셀렉터 수정(label→span) + 달력 토스트 스텝 제거

- hr-salary-long-term-care: steps 8,9,13,14 셀렉터를 label→span으로 변경, container를 [class*=field]→[class*=flex]로 변경 (실제 DOM 구조 반영)
- settings-calendar-crud: toast verify steps 3개 제거 (Server Actions는 토스트 미사용), 19→16 스텝
This commit is contained in:
김보곤
2026-03-05 21:51:41 +09:00
parent b95f7fc132
commit 4b5bf7a873
2 changed files with 16 additions and 39 deletions

View File

@@ -1,7 +1,7 @@
{ {
"id": "hr-salary-long-term-care", "id": "hr-salary-long-term-care",
"name": "급여 장기요양보험 필드 검증 테스트", "name": "급여 장기요양보험 필드 검증 테스트",
"version": "1.1.0", "version": "1.2.0",
"enabled": true, "enabled": true,
"screenshotPolicy": { "screenshotPolicy": {
"captureOnFail": true, "captureOnFail": true,
@@ -68,14 +68,14 @@
"name": "[READ] 장기요양보험 필드 존재 확인 (상세)", "name": "[READ] 장기요양보험 필드 존재 확인 (상세)",
"phase": "READ", "phase": "READ",
"action": "evaluate", "action": "evaluate",
"script": "(()=>{const R={phase:'DETAIL_LTC_CHECK'};const modal=document.querySelector('[role=\"dialog\"],[aria-modal=\"true\"],[class*=\"Dialog\"]');const scope=modal||document;const bodyText=scope.innerText||'';R.hasLongTermCare=bodyText.includes('장기요양')||bodyText.includes('장기요양보험');const labels=Array.from(scope.querySelectorAll('label,dt,[class*=\"label\"],[class*=\"Label\"]'));const ltcLabel=labels.find(l=>l.innerText?.includes('장기요양'));R.labelFound=!!ltcLabel;if(ltcLabel){const container=ltcLabel.closest('[class*=\"field\"],[class*=\"Field\"],[class*=\"form-item\"],[class*=\"row\"]');if(container){const valueEl=container.querySelector('input,span,dd,[class*=\"value\"],[class*=\"Value\"]');R.value=valueEl?.value||valueEl?.innerText?.trim()||'N/A';}}R.ok=R.hasLongTermCare||R.labelFound;R.info=R.ok?'pass: 장기요양보험 필드 발견':'fail: 장기요양보험 필드 미발견';return JSON.stringify(R);})()" "script": "(()=>{const R={phase:'DETAIL_LTC_CHECK'};const modal=document.querySelector('[role=\"dialog\"],[aria-modal=\"true\"],[class*=\"Dialog\"]');const scope=modal||document;const bodyText=scope.innerText||'';R.hasLongTermCare=bodyText.includes('장기요양')||bodyText.includes('장기요양보험');const allEls=Array.from(scope.querySelectorAll('span,label,dt'));const ltcEl=allEls.find(l=>/^장기요양(보험)?$/.test(l.innerText?.trim()));R.labelFound=!!ltcEl;if(ltcEl){const container=ltcEl.closest('[class*=\"flex\"]')||ltcEl.parentElement;if(container){const valSpans=Array.from(container.querySelectorAll('span,div')).filter(el=>el!==ltcEl&&/[0-9]/.test(el.innerText||''));if(valSpans.length>0)R.value=valSpans[valSpans.length-1].innerText?.trim()||'N/A';}}R.ok=R.hasLongTermCare||R.labelFound;R.info=R.ok?'pass: 장기요양보험 필드 발견'+(R.value?' ('+R.value+')':''):'fail: 장기요양보험 필드 미발견';return JSON.stringify(R);})()"
}, },
{ {
"id": 9, "id": 9,
"name": "[READ] 건강보험/장기요양 값 비교 (자동계산 검증)", "name": "[READ] 건강보험/장기요양 값 비교 (자동계산 검증)",
"phase": "READ", "phase": "READ",
"action": "evaluate", "action": "evaluate",
"script": "(()=>{const R={phase:'CALC_VERIFY'};const modal=document.querySelector('[role=\"dialog\"],[aria-modal=\"true\"],[class*=\"Dialog\"]');const scope=modal||document;const labels=Array.from(scope.querySelectorAll('label,dt,[class*=\"label\"],[class*=\"Label\"]'));const getVal=keyword=>{const lbl=labels.find(l=>l.innerText?.includes(keyword));if(!lbl)return null;const container=lbl.closest('[class*=\"field\"],[class*=\"Field\"],[class*=\"form-item\"],[class*=\"row\"]');if(!container)return null;const valEl=container.querySelector('input,span,dd,[class*=\"value\"]');const raw=valEl?.value||valEl?.innerText?.trim()||'';return parseInt(raw.replace(/[^0-9]/g,''))||null;};R.healthInsurance=getVal('건강보험');R.longTermCare=getVal('장기요양');if(R.healthInsurance&&R.longTermCare){const expected=Math.round(R.healthInsurance*0.1281);R.expectedLTC=expected;R.tolerance=Math.abs(R.longTermCare-expected);R.isCorrect=R.tolerance<=10;R.info=R.isCorrect?'pass: 장기요양='+R.longTermCare+' (건강보험 '+R.healthInsurance+'×12.81%='+expected+')':'warn: 장기요양='+R.longTermCare+' vs 예상='+expected+' (차이='+R.tolerance+')';}else{R.info='warn: 건강보험('+R.healthInsurance+') 또는 장기요양('+R.longTermCare+') 값 미확인';}R.ok=true;return JSON.stringify(R);})()" "script": "(()=>{const R={phase:'CALC_VERIFY'};const modal=document.querySelector('[role=\"dialog\"],[aria-modal=\"true\"],[class*=\"Dialog\"]');const scope=modal||document;const allEls=Array.from(scope.querySelectorAll('span,label,dt'));const getVal=keyword=>{const lbl=allEls.find(l=>l.innerText?.trim()===keyword);if(!lbl)return null;const container=lbl.closest('[class*=\"flex\"]')||lbl.parentElement;if(!container)return null;const valEls=Array.from(container.querySelectorAll('span,div')).filter(el=>el!==lbl&&/[0-9]/.test(el.innerText||''));if(valEls.length===0)return null;const raw=valEls[valEls.length-1].innerText?.trim()||'';return parseInt(raw.replace(/[^0-9]/g,''))||null;};R.healthInsurance=getVal('건강보험');R.longTermCare=getVal('장기요양보험')||getVal('장기요양');if(R.healthInsurance&&R.longTermCare){const expected=Math.round(R.healthInsurance*0.1281);R.expectedLTC=expected;R.tolerance=Math.abs(R.longTermCare-expected);R.isCorrect=R.tolerance<=10;R.info=R.isCorrect?'pass: 장기요양='+R.longTermCare+' (건강보험 '+R.healthInsurance+'×12.81%='+expected+')':'warn: 장기요양='+R.longTermCare+' vs 예상='+expected+' (차이='+R.tolerance+')';}else{R.info='warn: 건강보험('+R.healthInsurance+') 또는 장기요양('+R.longTermCare+') 값 미확인';}R.ok=true;return JSON.stringify(R);})()"
}, },
{ {
"id": 10, "id": 10,
@@ -104,14 +104,14 @@
"name": "[CREATE] 등록 폼에서 장기요양보험 필드 확인", "name": "[CREATE] 등록 폼에서 장기요양보험 필드 확인",
"phase": "CREATE", "phase": "CREATE",
"action": "evaluate", "action": "evaluate",
"script": "(()=>{const R={phase:'CREATE_LTC_CHECK'};const modal=document.querySelector('[role=\"dialog\"],[aria-modal=\"true\"],[class*=\"Dialog\"]');const scope=modal||document;const bodyText=scope.innerText||'';R.hasLongTermCare=bodyText.includes('장기요양')||bodyText.includes('장기요양보험');const labels=Array.from(scope.querySelectorAll('label'));const ltcLabel=labels.find(l=>l.innerText?.includes('장기요양'));R.labelFound=!!ltcLabel;if(ltcLabel){const container=ltcLabel.closest('[class*=\"field\"],[class*=\"Field\"],[class*=\"form-item\"],[class*=\"row\"]');if(container){const input=container.querySelector('input');R.hasInput=!!input;R.inputReadOnly=input?.readOnly||false;R.inputValue=input?.value||'';}}R.ok=R.hasLongTermCare||R.labelFound;R.info=R.ok?'pass: 등록 폼에 장기요양보험 필드 존재'+(R.inputReadOnly?' (자동계산, readOnly)':' (입력 가능)'):'fail: 등록 폼에 장기요양보험 필드 미발견';return JSON.stringify(R);})()" "script": "(()=>{const R={phase:'CREATE_LTC_CHECK'};const modal=document.querySelector('[role=\"dialog\"],[aria-modal=\"true\"],[class*=\"Dialog\"]');const scope=modal||document;const bodyText=scope.innerText||'';R.hasLongTermCare=bodyText.includes('장기요양')||bodyText.includes('장기요양보험');const allEls=Array.from(scope.querySelectorAll('span,label,dt'));const ltcEl=allEls.find(l=>/^장기요양(보험)?$/.test(l.innerText?.trim()));R.labelFound=!!ltcEl;if(ltcEl){const container=ltcEl.closest('[class*=\"flex\"]')||ltcEl.parentElement;if(container){const input=container.querySelector('input');R.hasInput=!!input;R.inputReadOnly=input?.readOnly||false;R.inputValue=input?.value||'';}}R.ok=R.hasLongTermCare||R.labelFound;R.info=R.ok?'pass: 등록 폼에 장기요양보험 필드 존재'+(R.inputReadOnly?' (자동계산, readOnly)':' (입력 가능)'):'fail: 등록 폼에 장기요양보험 필드 미발견';return JSON.stringify(R);})()"
}, },
{ {
"id": 14, "id": 14,
"name": "[CREATE] 건강보험 입력 → 장기요양 자동계산 검증", "name": "[CREATE] 건강보험 입력 → 장기요양 자동계산 검증",
"phase": "CREATE", "phase": "CREATE",
"action": "evaluate", "action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const sv=(el,v)=>{const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(el,v);else el.value=v;el.dispatchEvent(new Event('input',{bubbles:true}));el.dispatchEvent(new Event('change',{bubbles:true}));};const R={phase:'AUTO_CALC'};const modal=document.querySelector('[role=\"dialog\"],[aria-modal=\"true\"],[class*=\"Dialog\"]');const scope=modal||document;const labels=Array.from(scope.querySelectorAll('label'));const healthLabel=labels.find(l=>l.innerText?.includes('건강보험')&&!l.innerText?.includes('장기'));if(!healthLabel){R.info='건강보험 입력 필드 미발견';R.ok=true;return JSON.stringify(R);}const healthContainer=healthLabel.closest('[class*=\"field\"],[class*=\"Field\"],[class*=\"form-item\"],[class*=\"row\"]');const healthInput=healthContainer?.querySelector('input');if(!healthInput||healthInput.readOnly||healthInput.disabled){R.info='건강보험 입력 불가 (readOnly)';R.ok=true;return JSON.stringify(R);}sv(healthInput,'100000');await w(800);const ltcLabel=labels.find(l=>l.innerText?.includes('장기요양'));if(ltcLabel){const ltcContainer=ltcLabel.closest('[class*=\"field\"],[class*=\"Field\"],[class*=\"form-item\"],[class*=\"row\"]');const ltcEl=ltcContainer?.querySelector('input,span,[class*=\"value\"]');const ltcVal=ltcEl?.value||ltcEl?.innerText?.trim()||'';const numVal=parseInt(ltcVal.replace(/[^0-9]/g,''))||0;R.healthInput=100000;R.longTermCareOutput=numVal;R.expected=Math.round(100000*0.1281);R.isAutoCalculated=Math.abs(numVal-R.expected)<=10;R.info=R.isAutoCalculated?'pass: 건강보험 100,000 → 장기요양 '+numVal+' (예상 '+R.expected+')':'warn: 장기요양='+numVal+' vs 예상='+R.expected;}else{R.info='장기요양 필드 미발견';}R.ok=true;return JSON.stringify(R);})()", "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const sv=(el,v)=>{const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(el,v);else el.value=v;el.dispatchEvent(new Event('input',{bubbles:true}));el.dispatchEvent(new Event('change',{bubbles:true}));};const R={phase:'AUTO_CALC'};const modal=document.querySelector('[role=\"dialog\"],[aria-modal=\"true\"],[class*=\"Dialog\"]');const scope=modal||document;const allEls=Array.from(scope.querySelectorAll('span,label,dt'));const healthEl=allEls.find(l=>l.innerText?.trim()==='건강보험');if(!healthEl){R.info='건강보험 입력 필드 미발견';R.ok=true;return JSON.stringify(R);}const healthContainer=healthEl.closest('[class*=\"flex\"]')||healthEl.parentElement;const healthInput=healthContainer?.querySelector('input');if(!healthInput||healthInput.readOnly||healthInput.disabled){R.info='건강보험 입력 불가 (readOnly)';R.ok=true;return JSON.stringify(R);}sv(healthInput,'100000');await w(800);const ltcEl=allEls.find(l=>/^장기요양(보험)?$/.test(l.innerText?.trim()));if(ltcEl){const ltcContainer=ltcEl.closest('[class*=\"flex\"]')||ltcEl.parentElement;const ltcInput=ltcContainer?.querySelector('input');const ltcValEl=ltcInput||Array.from(ltcContainer?.querySelectorAll('span,div')||[]).find(el=>el!==ltcEl&&/[0-9]/.test(el.innerText||''));const ltcVal=ltcInput?.value||ltcValEl?.innerText?.trim()||'';const numVal=parseInt(ltcVal.replace(/[^0-9]/g,''))||0;R.healthInput=100000;R.longTermCareOutput=numVal;R.expected=Math.round(100000*0.1281);R.isAutoCalculated=Math.abs(numVal-R.expected)<=10;R.info=R.isAutoCalculated?'pass: 건강보험 100,000 → 장기요양 '+numVal+' (예상 '+R.expected+')':'warn: 장기요양='+numVal+' vs 예상='+R.expected;}else{R.info='장기요양 필드 미발견';}R.ok=true;return JSON.stringify(R);})()",
"timeout": 10000 "timeout": 10000
}, },
{ {

View File

@@ -1,13 +1,13 @@
{ {
"id": "settings-calendar-crud", "id": "settings-calendar-crud",
"name": "달력 일정 CRUD 테스트", "name": "달력 일정 CRUD 테스트",
"version": "3.2.0", "version": "3.3.0",
"enabled": true, "enabled": true,
"screenshotPolicy": { "screenshotPolicy": {
"captureOnFail": true, "captureOnFail": true,
"captureOnPass": false "captureOnPass": false
}, },
"description": "설정 > 달력관리 메뉴의 달력 일정 등록/조회/수정/삭제 전체 CRUD (Server Actions POST 방식)", "description": "설정 > 달력관리 메뉴의 달력 일정 등록/조회/수정/삭제 전체 CRUD (Server Actions POST 방식, 토스트 미사용)",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": { "menuNavigation": {
"level1": "설정", "level1": "설정",
@@ -66,27 +66,20 @@
}, },
{ {
"id": 7, "id": 7,
"name": "[CREATE] 등록 토스트 확인",
"phase": "CREATE",
"action": "verify_toast",
"verify": { "contains": "등록|완료|성공" }
},
{
"id": 8,
"name": "[CREATE] API POST 검증 (Server Action)", "name": "[CREATE] API POST 검증 (Server Action)",
"phase": "CREATE", "phase": "CREATE",
"action": "evaluate", "action": "evaluate",
"script": "(()=>{try{const apiData=window.__E2E__?window.__E2E__.getApiLogs():{logs:[]};const logs=apiData.logs||[];const posts=logs.filter(l=>l.method==='POST'&&(l.url.includes('calendar')||l.url.includes('schedule'))&&l.status>=200&&l.status<300);if(posts.length>0){return JSON.stringify({ok:true,info:'pass: ServerAction POST ×'+posts.length+' status='+posts[posts.length-1].status});}const allPosts=logs.filter(l=>l.method==='POST'&&l.ok);if(allPosts.length>0){return JSON.stringify({ok:true,info:'pass: POST calls='+allPosts.length+' (ServerAction pattern)'});}return JSON.stringify({ok:true,info:'warn: no POST captured (apiLogs='+logs.length+')'});}catch(e){return JSON.stringify({ok:true,info:'warn: API check error: '+e.message});}})()" "script": "(()=>{try{const apiData=window.__E2E__?window.__E2E__.getApiLogs():{logs:[]};const logs=apiData.logs||[];const posts=logs.filter(l=>l.method==='POST'&&(l.url.includes('calendar')||l.url.includes('schedule'))&&l.status>=200&&l.status<300);if(posts.length>0){return JSON.stringify({ok:true,info:'pass: ServerAction POST ×'+posts.length+' status='+posts[posts.length-1].status});}const allPosts=logs.filter(l=>l.method==='POST'&&l.ok);if(allPosts.length>0){return JSON.stringify({ok:true,info:'pass: POST calls='+allPosts.length+' (ServerAction pattern)'});}return JSON.stringify({ok:true,info:'warn: no POST captured (apiLogs='+logs.length+')'});}catch(e){return JSON.stringify({ok:true,info:'warn: API check error: '+e.message});}})()"
}, },
{ {
"id": 9, "id": 8,
"name": "[CREATE] 모달 닫힘 확인 + 목록 새로고침", "name": "[CREATE] 모달 닫힘 확인 + 목록 새로고침",
"phase": "CREATE", "phase": "CREATE",
"action": "evaluate", "action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const isVis=el=>!!el&&el.getBoundingClientRect().width>0;const R={phase:'MODAL_CHECK'};await w(1000);const modal=document.querySelector('[role=\"dialog\"]');if(isVis(modal)){const closeBtn=Array.from(modal.querySelectorAll('button')).find(b=>/닫기|취소|Close/.test(b.innerText?.trim()));if(closeBtn){closeBtn.click();await w(500);}R.wasClosed=true;}R.ok=true;return JSON.stringify(R);})()" "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const isVis=el=>!!el&&el.getBoundingClientRect().width>0;const R={phase:'MODAL_CHECK'};await w(1000);const modal=document.querySelector('[role=\"dialog\"]');if(isVis(modal)){const closeBtn=Array.from(modal.querySelectorAll('button')).find(b=>/닫기|취소|Close/.test(b.innerText?.trim()));if(closeBtn){closeBtn.click();await w(500);}R.wasClosed=true;}R.ok=true;return JSON.stringify(R);})()"
}, },
{ {
"id": 10, "id": 9,
"name": "[CREATE] 목록에서 등록 결과 확인", "name": "[CREATE] 목록에서 등록 결과 확인",
"phase": "CREATE", "phase": "CREATE",
"action": "evaluate", "action": "evaluate",
@@ -94,7 +87,7 @@
"timeout": 10000 "timeout": 10000
}, },
{ {
"id": 11, "id": 10,
"name": "[UPDATE] E2E 일정 행 클릭 → 다이얼로그 열기 → 수정 → 저장 (통합)", "name": "[UPDATE] E2E 일정 행 클릭 → 다이얼로그 열기 → 수정 → 저장 (통합)",
"phase": "UPDATE", "phase": "UPDATE",
"action": "evaluate", "action": "evaluate",
@@ -102,29 +95,21 @@
"timeout": 25000 "timeout": 25000
}, },
{ {
"id": 12, "id": 11,
"name": "[UPDATE] 수정 토스트 확인 (Server Action: 토스트 없을 수 있음)",
"phase": "UPDATE",
"action": "verify_toast",
"verify": { "contains": "수정|완료|성공" },
"critical": false
},
{
"id": 13,
"name": "[UPDATE] API 수정 검증 (Server Action)", "name": "[UPDATE] API 수정 검증 (Server Action)",
"phase": "UPDATE", "phase": "UPDATE",
"action": "evaluate", "action": "evaluate",
"script": "(()=>{try{const apiData=window.__E2E__?window.__E2E__.getApiLogs():{logs:[]};const logs=apiData.logs||[];const posts=logs.filter(l=>l.method==='POST'&&(l.url.includes('calendar')||l.url.includes('schedule'))&&l.ok);return JSON.stringify({ok:true,info:'pass: ServerAction calls='+posts.length+' total='+logs.length});}catch(e){return JSON.stringify({ok:true,info:'warn: '+e.message});}})()" "script": "(()=>{try{const apiData=window.__E2E__?window.__E2E__.getApiLogs():{logs:[]};const logs=apiData.logs||[];const posts=logs.filter(l=>l.method==='POST'&&(l.url.includes('calendar')||l.url.includes('schedule'))&&l.ok);return JSON.stringify({ok:true,info:'pass: ServerAction calls='+posts.length+' total='+logs.length});}catch(e){return JSON.stringify({ok:true,info:'warn: '+e.message});}})()"
}, },
{ {
"id": 14, "id": 12,
"name": "[UPDATE] 모달 닫힘 확인", "name": "[UPDATE] 모달 닫힘 확인",
"phase": "UPDATE", "phase": "UPDATE",
"action": "evaluate", "action": "evaluate",
"script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const isVis=el=>!!el&&el.getBoundingClientRect().width>0;await w(500);const modal=document.querySelector('[role=\"dialog\"],[data-state=\"open\"][class*=\"Sheet\"]');if(isVis(modal)){const closeBtn=Array.from(modal.querySelectorAll('button')).find(b=>/닫기|취소|Close/.test(b.innerText?.trim()));if(closeBtn){closeBtn.click();await w(500);}}return JSON.stringify({ok:true,phase:'MODAL_CLOSE'});})()" "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const isVis=el=>!!el&&el.getBoundingClientRect().width>0;await w(500);const modal=document.querySelector('[role=\"dialog\"],[data-state=\"open\"][class*=\"Sheet\"]');if(isVis(modal)){const closeBtn=Array.from(modal.querySelectorAll('button')).find(b=>/닫기|취소|Close/.test(b.innerText?.trim()));if(closeBtn){closeBtn.click();await w(500);}}return JSON.stringify({ok:true,phase:'MODAL_CLOSE'});})()"
}, },
{ {
"id": 15, "id": 13,
"name": "[DELETE] E2E 수정일정 행 클릭 → 삭제 (통합)", "name": "[DELETE] E2E 수정일정 행 클릭 → 삭제 (통합)",
"phase": "DELETE", "phase": "DELETE",
"action": "evaluate", "action": "evaluate",
@@ -133,22 +118,14 @@
"critical": false "critical": false
}, },
{ {
"id": 16, "id": 14,
"name": "[DELETE] 삭제 토스트 확인 (Server Action: 토스트 없을 수 있음)",
"phase": "DELETE",
"action": "verify_toast",
"verify": { "contains": "삭제|완료|성공" },
"critical": false
},
{
"id": 17,
"name": "[DELETE] API 삭제 검증 (Server Action)", "name": "[DELETE] API 삭제 검증 (Server Action)",
"phase": "DELETE", "phase": "DELETE",
"action": "evaluate", "action": "evaluate",
"script": "(()=>{try{const apiData=window.__E2E__?window.__E2E__.getApiLogs():{logs:[]};const logs=apiData.logs||[];const posts=logs.filter(l=>l.method==='POST'&&(l.url.includes('calendar')||l.url.includes('schedule'))&&l.ok);return JSON.stringify({ok:true,info:'pass: ServerAction calls='+posts.length+' total='+logs.length});}catch(e){return JSON.stringify({ok:true,info:'warn: '+e.message});}})()" "script": "(()=>{try{const apiData=window.__E2E__?window.__E2E__.getApiLogs():{logs:[]};const logs=apiData.logs||[];const posts=logs.filter(l=>l.method==='POST'&&(l.url.includes('calendar')||l.url.includes('schedule'))&&l.ok);return JSON.stringify({ok:true,info:'pass: ServerAction calls='+posts.length+' total='+logs.length});}catch(e){return JSON.stringify({ok:true,info:'warn: '+e.message});}})()"
}, },
{ {
"id": 18, "id": 15,
"name": "[DELETE] 목록에서 삭제 확인", "name": "[DELETE] 목록에서 삭제 확인",
"phase": "DELETE", "phase": "DELETE",
"action": "evaluate", "action": "evaluate",
@@ -156,7 +133,7 @@
"timeout": 10000 "timeout": 10000
}, },
{ {
"id": 19, "id": 16,
"name": "[SUMMARY] API 호출 통계", "name": "[SUMMARY] API 호출 통계",
"action": "evaluate", "action": "evaluate",
"script": "(()=>{try{const apiData=window.__E2E__?window.__E2E__.getApiLogs():{logs:[]};const logs=apiData.logs||[];const cal=logs.filter(l=>l.url.includes('calendar')||l.url.includes('schedule'));return JSON.stringify({ok:true,info:'API total='+logs.length+' calendar='+cal.length+' POST='+cal.filter(l=>l.method==='POST').length+' success='+cal.filter(l=>l.ok).length});}catch(e){return JSON.stringify({ok:true,info:'API summary error: '+e.message});}})()" "script": "(()=>{try{const apiData=window.__E2E__?window.__E2E__.getApiLogs():{logs:[]};const logs=apiData.logs||[];const cal=logs.filter(l=>l.url.includes('calendar')||l.url.includes('schedule'));return JSON.stringify({ok:true,info:'API total='+logs.length+' calendar='+cal.length+' POST='+cal.filter(l=>l.method==='POST').length+' success='+cal.filter(l=>l.ok).length});}catch(e){return JSON.stringify({ok:true,info:'API summary error: '+e.message});}})()"