280 lines
36 KiB
JSON
280 lines
36 KiB
JSON
{
|
|
"id": "api-health-prod-misc",
|
|
"name": "API 건강성 감사: 생산/기타",
|
|
"version": "2.0.0",
|
|
"auth": {
|
|
"role": "admin"
|
|
},
|
|
"menuNavigation": {
|
|
"level1": "생산관리",
|
|
"level2": "작업지시 관리"
|
|
},
|
|
"screenshotPolicy": {
|
|
"captureOnFail": true,
|
|
"captureOnPass": false
|
|
},
|
|
"steps": [
|
|
{
|
|
"id": 1,
|
|
"name": "[생산관리 > 작업지시 관리] 마커 기록",
|
|
"action": "evaluate",
|
|
"script": "(async()=>{const apiLogs=window.__E2E__?window.__E2E__.getApiLogs():{logs:[],summary:{}};window.__AH_MARK__={ ts:Date.now(), apiLogCount:apiLogs.logs.length, perfCount:performance.getEntriesByType('resource').length};return JSON.stringify({phase:'MARK_START',ok:true,info:'apiLogs:'+apiLogs.logs.length+' perf:'+window.__AH_MARK__.perfCount});})()",
|
|
"timeout": 5000,
|
|
"phase": "MARK"
|
|
},
|
|
{
|
|
"id": 2,
|
|
"name": "[생산관리 > 작업지시 관리] API 호출 대기",
|
|
"action": "wait",
|
|
"timeout": 3000
|
|
},
|
|
{
|
|
"id": 3,
|
|
"name": "[생산관리 > 작업지시 관리] API 건강성 감사",
|
|
"action": "evaluate",
|
|
"script": "(async()=>{const R={phase:'API_AUDIT'};const mark=window.__AH_MARK__||{ts:0,apiLogCount:0,perfCount:0};const apiData=window.__E2E__?window.__E2E__.getApiLogs():{logs:[],errors:[],summary:{}};const newApiLogs=apiData.logs.slice(mark.apiLogCount);R.monitorCalls=newApiLogs.length;const allRes=performance.getEntriesByType('resource');const newRes=allRes.slice(mark.perfCount);const apiRes=newRes.filter(e=>e.name.includes('/api/')||e.name.includes('/v1/')||e.name.includes('/graphql'));R.perfApiCalls=apiRes.length;const jsRes=newRes.filter(e=>e.initiatorType==='script'||e.name.endsWith('.js'));const cssRes=newRes.filter(e=>e.initiatorType==='link'||e.name.endsWith('.css'));const imgRes=newRes.filter(e=>e.initiatorType==='img'||e.name.match(new RegExp('\\\\.(png|jpg|svg|gif|webp)')));R.resourceBreakdown={total:newRes.length,api:apiRes.length,js:jsRes.length,css:cssRes.length,img:imgRes.length};R.totalCalls=Math.max(newApiLogs.length,apiRes.length);R.totalResources=newRes.length;if(R.totalCalls===0){ if(newRes.length>0){ const avgDur=Math.round(newRes.reduce((s,e)=>s+e.duration,0)/newRes.length); R.grade='PASS';R.ok=true; R.summary=newRes.length+'개 리소스 로드(API 0) | avg '+avgDur+'ms | PASS'; R.avgResponseTime=avgDur; }else{ R.grade='PASS';R.ok=true; R.summary='리소스/API 호출 없음 (SPA 캐시) | PASS'; } return JSON.stringify(R);}if(newApiLogs.length>0){ const errors4xx=newApiLogs.filter(l=>l.status>=400&&l.status<500); const errors5xx=newApiLogs.filter(l=>l.status>=500); const netErr=newApiLogs.filter(l=>!l.ok&&l.status===0); const slow=newApiLogs.filter(l=>l.duration>2000); const ok=newApiLogs.filter(l=>l.ok); R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length; R.slowCalls=slow.length;R.successCount=ok.length; const durs=newApiLogs.map(l=>l.duration).filter(d=>d>0); R.avgResponseTime=durs.length>0?Math.round(durs.reduce((a,b)=>a+b,0)/durs.length):0; R.maxResponseTime=durs.length>0?Math.max(...durs):0; const errRate=((errors4xx.length+errors5xx.length+netErr.length)/newApiLogs.length*100); R.errorRate=Math.round(errRate*10)/10; R.failedUrls=[...errors5xx,...errors4xx].slice(0,3).map(l=>l.url.split('?')[0].substring(0,80)); if(errors5xx.length>0||errRate>10){R.grade='FAIL';} else if(errors4xx.length>0||slow.length>3||R.avgResponseTime>1000){R.grade='WARN';} else{R.grade='PASS';} R.summary=newApiLogs.length+'개 API | '+ok.length+'OK '+(errors4xx.length+errors5xx.length)+'err '+slow.length+'slow | avg '+R.avgResponseTime+'ms | '+R.grade;}else{ const durs=apiRes.map(e=>Math.round(e.duration)); R.avgResponseTime=durs.length>0?Math.round(durs.reduce((a,b)=>a+b,0)/durs.length):0; R.slowCalls=apiRes.filter(e=>e.duration>2000).length; if(R.slowCalls>3||R.avgResponseTime>1000){R.grade='WARN';} else{R.grade='PASS';} R.summary=apiRes.length+'개 API(perf) | avg '+R.avgResponseTime+'ms | '+R.grade;}R.ok=true;return JSON.stringify(R);})()",
|
|
"timeout": 10000,
|
|
"phase": "API_AUDIT"
|
|
},
|
|
{
|
|
"id": 4,
|
|
"name": "[생산관리 > 작업실적] 마커 기록",
|
|
"action": "evaluate",
|
|
"script": "(async()=>{const apiLogs=window.__E2E__?window.__E2E__.getApiLogs():{logs:[],summary:{}};window.__AH_MARK__={ ts:Date.now(), apiLogCount:apiLogs.logs.length, perfCount:performance.getEntriesByType('resource').length};return JSON.stringify({phase:'MARK_START',ok:true,info:'apiLogs:'+apiLogs.logs.length+' perf:'+window.__AH_MARK__.perfCount});})()",
|
|
"timeout": 3000,
|
|
"phase": "MARK"
|
|
},
|
|
{
|
|
"id": 5,
|
|
"name": "[생산관리 > 작업실적] 메뉴 이동",
|
|
"action": "menu_navigate",
|
|
"level1": "생산관리",
|
|
"level2": "작업실적",
|
|
"timeout": 10000
|
|
},
|
|
{
|
|
"id": 6,
|
|
"name": "[생산관리 > 작업실적] API 호출 대기",
|
|
"action": "wait",
|
|
"timeout": 3000
|
|
},
|
|
{
|
|
"id": 7,
|
|
"name": "[생산관리 > 작업실적] API 건강성 감사",
|
|
"action": "evaluate",
|
|
"script": "(async()=>{const R={phase:'API_AUDIT'};const mark=window.__AH_MARK__||{ts:0,apiLogCount:0,perfCount:0};const apiData=window.__E2E__?window.__E2E__.getApiLogs():{logs:[],errors:[],summary:{}};const newApiLogs=apiData.logs.slice(mark.apiLogCount);R.monitorCalls=newApiLogs.length;const allRes=performance.getEntriesByType('resource');const newRes=allRes.slice(mark.perfCount);const apiRes=newRes.filter(e=>e.name.includes('/api/')||e.name.includes('/v1/')||e.name.includes('/graphql'));R.perfApiCalls=apiRes.length;const jsRes=newRes.filter(e=>e.initiatorType==='script'||e.name.endsWith('.js'));const cssRes=newRes.filter(e=>e.initiatorType==='link'||e.name.endsWith('.css'));const imgRes=newRes.filter(e=>e.initiatorType==='img'||e.name.match(new RegExp('\\\\.(png|jpg|svg|gif|webp)')));R.resourceBreakdown={total:newRes.length,api:apiRes.length,js:jsRes.length,css:cssRes.length,img:imgRes.length};R.totalCalls=Math.max(newApiLogs.length,apiRes.length);R.totalResources=newRes.length;if(R.totalCalls===0){ if(newRes.length>0){ const avgDur=Math.round(newRes.reduce((s,e)=>s+e.duration,0)/newRes.length); R.grade='PASS';R.ok=true; R.summary=newRes.length+'개 리소스 로드(API 0) | avg '+avgDur+'ms | PASS'; R.avgResponseTime=avgDur; }else{ R.grade='PASS';R.ok=true; R.summary='리소스/API 호출 없음 (SPA 캐시) | PASS'; } return JSON.stringify(R);}if(newApiLogs.length>0){ const errors4xx=newApiLogs.filter(l=>l.status>=400&&l.status<500); const errors5xx=newApiLogs.filter(l=>l.status>=500); const netErr=newApiLogs.filter(l=>!l.ok&&l.status===0); const slow=newApiLogs.filter(l=>l.duration>2000); const ok=newApiLogs.filter(l=>l.ok); R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length; R.slowCalls=slow.length;R.successCount=ok.length; const durs=newApiLogs.map(l=>l.duration).filter(d=>d>0); R.avgResponseTime=durs.length>0?Math.round(durs.reduce((a,b)=>a+b,0)/durs.length):0; R.maxResponseTime=durs.length>0?Math.max(...durs):0; const errRate=((errors4xx.length+errors5xx.length+netErr.length)/newApiLogs.length*100); R.errorRate=Math.round(errRate*10)/10; R.failedUrls=[...errors5xx,...errors4xx].slice(0,3).map(l=>l.url.split('?')[0].substring(0,80)); if(errors5xx.length>0||errRate>10){R.grade='FAIL';} else if(errors4xx.length>0||slow.length>3||R.avgResponseTime>1000){R.grade='WARN';} else{R.grade='PASS';} R.summary=newApiLogs.length+'개 API | '+ok.length+'OK '+(errors4xx.length+errors5xx.length)+'err '+slow.length+'slow | avg '+R.avgResponseTime+'ms | '+R.grade;}else{ const durs=apiRes.map(e=>Math.round(e.duration)); R.avgResponseTime=durs.length>0?Math.round(durs.reduce((a,b)=>a+b,0)/durs.length):0; R.slowCalls=apiRes.filter(e=>e.duration>2000).length; if(R.slowCalls>3||R.avgResponseTime>1000){R.grade='WARN';} else{R.grade='PASS';} R.summary=apiRes.length+'개 API(perf) | avg '+R.avgResponseTime+'ms | '+R.grade;}R.ok=true;return JSON.stringify(R);})()",
|
|
"timeout": 10000,
|
|
"phase": "API_AUDIT"
|
|
},
|
|
{
|
|
"id": 8,
|
|
"name": "[생산관리 > 품목관리] 마커 기록",
|
|
"action": "evaluate",
|
|
"script": "(async()=>{const apiLogs=window.__E2E__?window.__E2E__.getApiLogs():{logs:[],summary:{}};window.__AH_MARK__={ ts:Date.now(), apiLogCount:apiLogs.logs.length, perfCount:performance.getEntriesByType('resource').length};return JSON.stringify({phase:'MARK_START',ok:true,info:'apiLogs:'+apiLogs.logs.length+' perf:'+window.__AH_MARK__.perfCount});})()",
|
|
"timeout": 3000,
|
|
"phase": "MARK"
|
|
},
|
|
{
|
|
"id": 9,
|
|
"name": "[생산관리 > 품목관리] 메뉴 이동",
|
|
"action": "menu_navigate",
|
|
"level1": "생산관리",
|
|
"level2": "품목관리",
|
|
"timeout": 10000
|
|
},
|
|
{
|
|
"id": 10,
|
|
"name": "[생산관리 > 품목관리] API 호출 대기",
|
|
"action": "wait",
|
|
"timeout": 3000
|
|
},
|
|
{
|
|
"id": 11,
|
|
"name": "[생산관리 > 품목관리] API 건강성 감사",
|
|
"action": "evaluate",
|
|
"script": "(async()=>{const R={phase:'API_AUDIT'};const mark=window.__AH_MARK__||{ts:0,apiLogCount:0,perfCount:0};const apiData=window.__E2E__?window.__E2E__.getApiLogs():{logs:[],errors:[],summary:{}};const newApiLogs=apiData.logs.slice(mark.apiLogCount);R.monitorCalls=newApiLogs.length;const allRes=performance.getEntriesByType('resource');const newRes=allRes.slice(mark.perfCount);const apiRes=newRes.filter(e=>e.name.includes('/api/')||e.name.includes('/v1/')||e.name.includes('/graphql'));R.perfApiCalls=apiRes.length;const jsRes=newRes.filter(e=>e.initiatorType==='script'||e.name.endsWith('.js'));const cssRes=newRes.filter(e=>e.initiatorType==='link'||e.name.endsWith('.css'));const imgRes=newRes.filter(e=>e.initiatorType==='img'||e.name.match(new RegExp('\\\\.(png|jpg|svg|gif|webp)')));R.resourceBreakdown={total:newRes.length,api:apiRes.length,js:jsRes.length,css:cssRes.length,img:imgRes.length};R.totalCalls=Math.max(newApiLogs.length,apiRes.length);R.totalResources=newRes.length;if(R.totalCalls===0){ if(newRes.length>0){ const avgDur=Math.round(newRes.reduce((s,e)=>s+e.duration,0)/newRes.length); R.grade='PASS';R.ok=true; R.summary=newRes.length+'개 리소스 로드(API 0) | avg '+avgDur+'ms | PASS'; R.avgResponseTime=avgDur; }else{ R.grade='PASS';R.ok=true; R.summary='리소스/API 호출 없음 (SPA 캐시) | PASS'; } return JSON.stringify(R);}if(newApiLogs.length>0){ const errors4xx=newApiLogs.filter(l=>l.status>=400&&l.status<500); const errors5xx=newApiLogs.filter(l=>l.status>=500); const netErr=newApiLogs.filter(l=>!l.ok&&l.status===0); const slow=newApiLogs.filter(l=>l.duration>2000); const ok=newApiLogs.filter(l=>l.ok); R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length; R.slowCalls=slow.length;R.successCount=ok.length; const durs=newApiLogs.map(l=>l.duration).filter(d=>d>0); R.avgResponseTime=durs.length>0?Math.round(durs.reduce((a,b)=>a+b,0)/durs.length):0; R.maxResponseTime=durs.length>0?Math.max(...durs):0; const errRate=((errors4xx.length+errors5xx.length+netErr.length)/newApiLogs.length*100); R.errorRate=Math.round(errRate*10)/10; R.failedUrls=[...errors5xx,...errors4xx].slice(0,3).map(l=>l.url.split('?')[0].substring(0,80)); if(errors5xx.length>0||errRate>10){R.grade='FAIL';} else if(errors4xx.length>0||slow.length>3||R.avgResponseTime>1000){R.grade='WARN';} else{R.grade='PASS';} R.summary=newApiLogs.length+'개 API | '+ok.length+'OK '+(errors4xx.length+errors5xx.length)+'err '+slow.length+'slow | avg '+R.avgResponseTime+'ms | '+R.grade;}else{ const durs=apiRes.map(e=>Math.round(e.duration)); R.avgResponseTime=durs.length>0?Math.round(durs.reduce((a,b)=>a+b,0)/durs.length):0; R.slowCalls=apiRes.filter(e=>e.duration>2000).length; if(R.slowCalls>3||R.avgResponseTime>1000){R.grade='WARN';} else{R.grade='PASS';} R.summary=apiRes.length+'개 API(perf) | avg '+R.avgResponseTime+'ms | '+R.grade;}R.ok=true;return JSON.stringify(R);})()",
|
|
"timeout": 10000,
|
|
"phase": "API_AUDIT"
|
|
},
|
|
{
|
|
"id": 12,
|
|
"name": "[생산관리 > 작업자 화면] 마커 기록",
|
|
"action": "evaluate",
|
|
"script": "(async()=>{const apiLogs=window.__E2E__?window.__E2E__.getApiLogs():{logs:[],summary:{}};window.__AH_MARK__={ ts:Date.now(), apiLogCount:apiLogs.logs.length, perfCount:performance.getEntriesByType('resource').length};return JSON.stringify({phase:'MARK_START',ok:true,info:'apiLogs:'+apiLogs.logs.length+' perf:'+window.__AH_MARK__.perfCount});})()",
|
|
"timeout": 3000,
|
|
"phase": "MARK"
|
|
},
|
|
{
|
|
"id": 13,
|
|
"name": "[생산관리 > 작업자 화면] 메뉴 이동",
|
|
"action": "menu_navigate",
|
|
"level1": "생산관리",
|
|
"level2": "작업자 화면",
|
|
"timeout": 10000
|
|
},
|
|
{
|
|
"id": 14,
|
|
"name": "[생산관리 > 작업자 화면] API 호출 대기",
|
|
"action": "wait",
|
|
"timeout": 3000
|
|
},
|
|
{
|
|
"id": 15,
|
|
"name": "[생산관리 > 작업자 화면] API 건강성 감사",
|
|
"action": "evaluate",
|
|
"script": "(async()=>{const R={phase:'API_AUDIT'};const mark=window.__AH_MARK__||{ts:0,apiLogCount:0,perfCount:0};const apiData=window.__E2E__?window.__E2E__.getApiLogs():{logs:[],errors:[],summary:{}};const newApiLogs=apiData.logs.slice(mark.apiLogCount);R.monitorCalls=newApiLogs.length;const allRes=performance.getEntriesByType('resource');const newRes=allRes.slice(mark.perfCount);const apiRes=newRes.filter(e=>e.name.includes('/api/')||e.name.includes('/v1/')||e.name.includes('/graphql'));R.perfApiCalls=apiRes.length;const jsRes=newRes.filter(e=>e.initiatorType==='script'||e.name.endsWith('.js'));const cssRes=newRes.filter(e=>e.initiatorType==='link'||e.name.endsWith('.css'));const imgRes=newRes.filter(e=>e.initiatorType==='img'||e.name.match(new RegExp('\\\\.(png|jpg|svg|gif|webp)')));R.resourceBreakdown={total:newRes.length,api:apiRes.length,js:jsRes.length,css:cssRes.length,img:imgRes.length};R.totalCalls=Math.max(newApiLogs.length,apiRes.length);R.totalResources=newRes.length;if(R.totalCalls===0){ if(newRes.length>0){ const avgDur=Math.round(newRes.reduce((s,e)=>s+e.duration,0)/newRes.length); R.grade='PASS';R.ok=true; R.summary=newRes.length+'개 리소스 로드(API 0) | avg '+avgDur+'ms | PASS'; R.avgResponseTime=avgDur; }else{ R.grade='PASS';R.ok=true; R.summary='리소스/API 호출 없음 (SPA 캐시) | PASS'; } return JSON.stringify(R);}if(newApiLogs.length>0){ const errors4xx=newApiLogs.filter(l=>l.status>=400&&l.status<500); const errors5xx=newApiLogs.filter(l=>l.status>=500); const netErr=newApiLogs.filter(l=>!l.ok&&l.status===0); const slow=newApiLogs.filter(l=>l.duration>2000); const ok=newApiLogs.filter(l=>l.ok); R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length; R.slowCalls=slow.length;R.successCount=ok.length; const durs=newApiLogs.map(l=>l.duration).filter(d=>d>0); R.avgResponseTime=durs.length>0?Math.round(durs.reduce((a,b)=>a+b,0)/durs.length):0; R.maxResponseTime=durs.length>0?Math.max(...durs):0; const errRate=((errors4xx.length+errors5xx.length+netErr.length)/newApiLogs.length*100); R.errorRate=Math.round(errRate*10)/10; R.failedUrls=[...errors5xx,...errors4xx].slice(0,3).map(l=>l.url.split('?')[0].substring(0,80)); if(errors5xx.length>0||errRate>10){R.grade='FAIL';} else if(errors4xx.length>0||slow.length>3||R.avgResponseTime>1000){R.grade='WARN';} else{R.grade='PASS';} R.summary=newApiLogs.length+'개 API | '+ok.length+'OK '+(errors4xx.length+errors5xx.length)+'err '+slow.length+'slow | avg '+R.avgResponseTime+'ms | '+R.grade;}else{ const durs=apiRes.map(e=>Math.round(e.duration)); R.avgResponseTime=durs.length>0?Math.round(durs.reduce((a,b)=>a+b,0)/durs.length):0; R.slowCalls=apiRes.filter(e=>e.duration>2000).length; if(R.slowCalls>3||R.avgResponseTime>1000){R.grade='WARN';} else{R.grade='PASS';} R.summary=apiRes.length+'개 API(perf) | avg '+R.avgResponseTime+'ms | '+R.grade;}R.ok=true;return JSON.stringify(R);})()",
|
|
"timeout": 10000,
|
|
"phase": "API_AUDIT"
|
|
},
|
|
{
|
|
"id": 16,
|
|
"name": "[품질관리 > 제품검사관리] 마커 기록",
|
|
"action": "evaluate",
|
|
"script": "(async()=>{const apiLogs=window.__E2E__?window.__E2E__.getApiLogs():{logs:[],summary:{}};window.__AH_MARK__={ ts:Date.now(), apiLogCount:apiLogs.logs.length, perfCount:performance.getEntriesByType('resource').length};return JSON.stringify({phase:'MARK_START',ok:true,info:'apiLogs:'+apiLogs.logs.length+' perf:'+window.__AH_MARK__.perfCount});})()",
|
|
"timeout": 3000,
|
|
"phase": "MARK"
|
|
},
|
|
{
|
|
"id": 17,
|
|
"name": "[품질관리 > 제품검사관리] 메뉴 이동",
|
|
"action": "menu_navigate",
|
|
"level1": "품질관리",
|
|
"level2": "제품검사관리",
|
|
"timeout": 10000
|
|
},
|
|
{
|
|
"id": 18,
|
|
"name": "[품질관리 > 제품검사관리] API 호출 대기",
|
|
"action": "wait",
|
|
"timeout": 3000
|
|
},
|
|
{
|
|
"id": 19,
|
|
"name": "[품질관리 > 제품검사관리] API 건강성 감사",
|
|
"action": "evaluate",
|
|
"script": "(async()=>{const R={phase:'API_AUDIT'};const mark=window.__AH_MARK__||{ts:0,apiLogCount:0,perfCount:0};const apiData=window.__E2E__?window.__E2E__.getApiLogs():{logs:[],errors:[],summary:{}};const newApiLogs=apiData.logs.slice(mark.apiLogCount);R.monitorCalls=newApiLogs.length;const allRes=performance.getEntriesByType('resource');const newRes=allRes.slice(mark.perfCount);const apiRes=newRes.filter(e=>e.name.includes('/api/')||e.name.includes('/v1/')||e.name.includes('/graphql'));R.perfApiCalls=apiRes.length;const jsRes=newRes.filter(e=>e.initiatorType==='script'||e.name.endsWith('.js'));const cssRes=newRes.filter(e=>e.initiatorType==='link'||e.name.endsWith('.css'));const imgRes=newRes.filter(e=>e.initiatorType==='img'||e.name.match(new RegExp('\\\\.(png|jpg|svg|gif|webp)')));R.resourceBreakdown={total:newRes.length,api:apiRes.length,js:jsRes.length,css:cssRes.length,img:imgRes.length};R.totalCalls=Math.max(newApiLogs.length,apiRes.length);R.totalResources=newRes.length;if(R.totalCalls===0){ if(newRes.length>0){ const avgDur=Math.round(newRes.reduce((s,e)=>s+e.duration,0)/newRes.length); R.grade='PASS';R.ok=true; R.summary=newRes.length+'개 리소스 로드(API 0) | avg '+avgDur+'ms | PASS'; R.avgResponseTime=avgDur; }else{ R.grade='PASS';R.ok=true; R.summary='리소스/API 호출 없음 (SPA 캐시) | PASS'; } return JSON.stringify(R);}if(newApiLogs.length>0){ const errors4xx=newApiLogs.filter(l=>l.status>=400&&l.status<500); const errors5xx=newApiLogs.filter(l=>l.status>=500); const netErr=newApiLogs.filter(l=>!l.ok&&l.status===0); const slow=newApiLogs.filter(l=>l.duration>2000); const ok=newApiLogs.filter(l=>l.ok); R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length; R.slowCalls=slow.length;R.successCount=ok.length; const durs=newApiLogs.map(l=>l.duration).filter(d=>d>0); R.avgResponseTime=durs.length>0?Math.round(durs.reduce((a,b)=>a+b,0)/durs.length):0; R.maxResponseTime=durs.length>0?Math.max(...durs):0; const errRate=((errors4xx.length+errors5xx.length+netErr.length)/newApiLogs.length*100); R.errorRate=Math.round(errRate*10)/10; R.failedUrls=[...errors5xx,...errors4xx].slice(0,3).map(l=>l.url.split('?')[0].substring(0,80)); if(errors5xx.length>0||errRate>10){R.grade='FAIL';} else if(errors4xx.length>0||slow.length>3||R.avgResponseTime>1000){R.grade='WARN';} else{R.grade='PASS';} R.summary=newApiLogs.length+'개 API | '+ok.length+'OK '+(errors4xx.length+errors5xx.length)+'err '+slow.length+'slow | avg '+R.avgResponseTime+'ms | '+R.grade;}else{ const durs=apiRes.map(e=>Math.round(e.duration)); R.avgResponseTime=durs.length>0?Math.round(durs.reduce((a,b)=>a+b,0)/durs.length):0; R.slowCalls=apiRes.filter(e=>e.duration>2000).length; if(R.slowCalls>3||R.avgResponseTime>1000){R.grade='WARN';} else{R.grade='PASS';} R.summary=apiRes.length+'개 API(perf) | avg '+R.avgResponseTime+'ms | '+R.grade;}R.ok=true;return JSON.stringify(R);})()",
|
|
"timeout": 10000,
|
|
"phase": "API_AUDIT"
|
|
},
|
|
{
|
|
"id": 20,
|
|
"name": "[자재관리 > 입고관리] 마커 기록",
|
|
"action": "evaluate",
|
|
"script": "(async()=>{const apiLogs=window.__E2E__?window.__E2E__.getApiLogs():{logs:[],summary:{}};window.__AH_MARK__={ ts:Date.now(), apiLogCount:apiLogs.logs.length, perfCount:performance.getEntriesByType('resource').length};return JSON.stringify({phase:'MARK_START',ok:true,info:'apiLogs:'+apiLogs.logs.length+' perf:'+window.__AH_MARK__.perfCount});})()",
|
|
"timeout": 3000,
|
|
"phase": "MARK"
|
|
},
|
|
{
|
|
"id": 21,
|
|
"name": "[자재관리 > 입고관리] 메뉴 이동",
|
|
"action": "menu_navigate",
|
|
"level1": "자재관리",
|
|
"level2": "입고관리",
|
|
"timeout": 10000
|
|
},
|
|
{
|
|
"id": 22,
|
|
"name": "[자재관리 > 입고관리] API 호출 대기",
|
|
"action": "wait",
|
|
"timeout": 3000
|
|
},
|
|
{
|
|
"id": 23,
|
|
"name": "[자재관리 > 입고관리] API 건강성 감사",
|
|
"action": "evaluate",
|
|
"script": "(async()=>{const R={phase:'API_AUDIT'};const mark=window.__AH_MARK__||{ts:0,apiLogCount:0,perfCount:0};const apiData=window.__E2E__?window.__E2E__.getApiLogs():{logs:[],errors:[],summary:{}};const newApiLogs=apiData.logs.slice(mark.apiLogCount);R.monitorCalls=newApiLogs.length;const allRes=performance.getEntriesByType('resource');const newRes=allRes.slice(mark.perfCount);const apiRes=newRes.filter(e=>e.name.includes('/api/')||e.name.includes('/v1/')||e.name.includes('/graphql'));R.perfApiCalls=apiRes.length;const jsRes=newRes.filter(e=>e.initiatorType==='script'||e.name.endsWith('.js'));const cssRes=newRes.filter(e=>e.initiatorType==='link'||e.name.endsWith('.css'));const imgRes=newRes.filter(e=>e.initiatorType==='img'||e.name.match(new RegExp('\\\\.(png|jpg|svg|gif|webp)')));R.resourceBreakdown={total:newRes.length,api:apiRes.length,js:jsRes.length,css:cssRes.length,img:imgRes.length};R.totalCalls=Math.max(newApiLogs.length,apiRes.length);R.totalResources=newRes.length;if(R.totalCalls===0){ if(newRes.length>0){ const avgDur=Math.round(newRes.reduce((s,e)=>s+e.duration,0)/newRes.length); R.grade='PASS';R.ok=true; R.summary=newRes.length+'개 리소스 로드(API 0) | avg '+avgDur+'ms | PASS'; R.avgResponseTime=avgDur; }else{ R.grade='PASS';R.ok=true; R.summary='리소스/API 호출 없음 (SPA 캐시) | PASS'; } return JSON.stringify(R);}if(newApiLogs.length>0){ const errors4xx=newApiLogs.filter(l=>l.status>=400&&l.status<500); const errors5xx=newApiLogs.filter(l=>l.status>=500); const netErr=newApiLogs.filter(l=>!l.ok&&l.status===0); const slow=newApiLogs.filter(l=>l.duration>2000); const ok=newApiLogs.filter(l=>l.ok); R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length; R.slowCalls=slow.length;R.successCount=ok.length; const durs=newApiLogs.map(l=>l.duration).filter(d=>d>0); R.avgResponseTime=durs.length>0?Math.round(durs.reduce((a,b)=>a+b,0)/durs.length):0; R.maxResponseTime=durs.length>0?Math.max(...durs):0; const errRate=((errors4xx.length+errors5xx.length+netErr.length)/newApiLogs.length*100); R.errorRate=Math.round(errRate*10)/10; R.failedUrls=[...errors5xx,...errors4xx].slice(0,3).map(l=>l.url.split('?')[0].substring(0,80)); if(errors5xx.length>0||errRate>10){R.grade='FAIL';} else if(errors4xx.length>0||slow.length>3||R.avgResponseTime>1000){R.grade='WARN';} else{R.grade='PASS';} R.summary=newApiLogs.length+'개 API | '+ok.length+'OK '+(errors4xx.length+errors5xx.length)+'err '+slow.length+'slow | avg '+R.avgResponseTime+'ms | '+R.grade;}else{ const durs=apiRes.map(e=>Math.round(e.duration)); R.avgResponseTime=durs.length>0?Math.round(durs.reduce((a,b)=>a+b,0)/durs.length):0; R.slowCalls=apiRes.filter(e=>e.duration>2000).length; if(R.slowCalls>3||R.avgResponseTime>1000){R.grade='WARN';} else{R.grade='PASS';} R.summary=apiRes.length+'개 API(perf) | avg '+R.avgResponseTime+'ms | '+R.grade;}R.ok=true;return JSON.stringify(R);})()",
|
|
"timeout": 10000,
|
|
"phase": "API_AUDIT"
|
|
},
|
|
{
|
|
"id": 24,
|
|
"name": "[자재관리 > 재고현황] 마커 기록",
|
|
"action": "evaluate",
|
|
"script": "(async()=>{const apiLogs=window.__E2E__?window.__E2E__.getApiLogs():{logs:[],summary:{}};window.__AH_MARK__={ ts:Date.now(), apiLogCount:apiLogs.logs.length, perfCount:performance.getEntriesByType('resource').length};return JSON.stringify({phase:'MARK_START',ok:true,info:'apiLogs:'+apiLogs.logs.length+' perf:'+window.__AH_MARK__.perfCount});})()",
|
|
"timeout": 3000,
|
|
"phase": "MARK"
|
|
},
|
|
{
|
|
"id": 25,
|
|
"name": "[자재관리 > 재고현황] 메뉴 이동",
|
|
"action": "menu_navigate",
|
|
"level1": "자재관리",
|
|
"level2": "재고현황",
|
|
"timeout": 10000
|
|
},
|
|
{
|
|
"id": 26,
|
|
"name": "[자재관리 > 재고현황] API 호출 대기",
|
|
"action": "wait",
|
|
"timeout": 3000
|
|
},
|
|
{
|
|
"id": 27,
|
|
"name": "[자재관리 > 재고현황] API 건강성 감사",
|
|
"action": "evaluate",
|
|
"script": "(async()=>{const R={phase:'API_AUDIT'};const mark=window.__AH_MARK__||{ts:0,apiLogCount:0,perfCount:0};const apiData=window.__E2E__?window.__E2E__.getApiLogs():{logs:[],errors:[],summary:{}};const newApiLogs=apiData.logs.slice(mark.apiLogCount);R.monitorCalls=newApiLogs.length;const allRes=performance.getEntriesByType('resource');const newRes=allRes.slice(mark.perfCount);const apiRes=newRes.filter(e=>e.name.includes('/api/')||e.name.includes('/v1/')||e.name.includes('/graphql'));R.perfApiCalls=apiRes.length;const jsRes=newRes.filter(e=>e.initiatorType==='script'||e.name.endsWith('.js'));const cssRes=newRes.filter(e=>e.initiatorType==='link'||e.name.endsWith('.css'));const imgRes=newRes.filter(e=>e.initiatorType==='img'||e.name.match(new RegExp('\\\\.(png|jpg|svg|gif|webp)')));R.resourceBreakdown={total:newRes.length,api:apiRes.length,js:jsRes.length,css:cssRes.length,img:imgRes.length};R.totalCalls=Math.max(newApiLogs.length,apiRes.length);R.totalResources=newRes.length;if(R.totalCalls===0){ if(newRes.length>0){ const avgDur=Math.round(newRes.reduce((s,e)=>s+e.duration,0)/newRes.length); R.grade='PASS';R.ok=true; R.summary=newRes.length+'개 리소스 로드(API 0) | avg '+avgDur+'ms | PASS'; R.avgResponseTime=avgDur; }else{ R.grade='PASS';R.ok=true; R.summary='리소스/API 호출 없음 (SPA 캐시) | PASS'; } return JSON.stringify(R);}if(newApiLogs.length>0){ const errors4xx=newApiLogs.filter(l=>l.status>=400&&l.status<500); const errors5xx=newApiLogs.filter(l=>l.status>=500); const netErr=newApiLogs.filter(l=>!l.ok&&l.status===0); const slow=newApiLogs.filter(l=>l.duration>2000); const ok=newApiLogs.filter(l=>l.ok); R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length; R.slowCalls=slow.length;R.successCount=ok.length; const durs=newApiLogs.map(l=>l.duration).filter(d=>d>0); R.avgResponseTime=durs.length>0?Math.round(durs.reduce((a,b)=>a+b,0)/durs.length):0; R.maxResponseTime=durs.length>0?Math.max(...durs):0; const errRate=((errors4xx.length+errors5xx.length+netErr.length)/newApiLogs.length*100); R.errorRate=Math.round(errRate*10)/10; R.failedUrls=[...errors5xx,...errors4xx].slice(0,3).map(l=>l.url.split('?')[0].substring(0,80)); if(errors5xx.length>0||errRate>10){R.grade='FAIL';} else if(errors4xx.length>0||slow.length>3||R.avgResponseTime>1000){R.grade='WARN';} else{R.grade='PASS';} R.summary=newApiLogs.length+'개 API | '+ok.length+'OK '+(errors4xx.length+errors5xx.length)+'err '+slow.length+'slow | avg '+R.avgResponseTime+'ms | '+R.grade;}else{ const durs=apiRes.map(e=>Math.round(e.duration)); R.avgResponseTime=durs.length>0?Math.round(durs.reduce((a,b)=>a+b,0)/durs.length):0; R.slowCalls=apiRes.filter(e=>e.duration>2000).length; if(R.slowCalls>3||R.avgResponseTime>1000){R.grade='WARN';} else{R.grade='PASS';} R.summary=apiRes.length+'개 API(perf) | avg '+R.avgResponseTime+'ms | '+R.grade;}R.ok=true;return JSON.stringify(R);})()",
|
|
"timeout": 10000,
|
|
"phase": "API_AUDIT"
|
|
},
|
|
{
|
|
"id": 28,
|
|
"name": "[게시판 > 자유게시판] 마커 기록",
|
|
"action": "evaluate",
|
|
"script": "(async()=>{const apiLogs=window.__E2E__?window.__E2E__.getApiLogs():{logs:[],summary:{}};window.__AH_MARK__={ ts:Date.now(), apiLogCount:apiLogs.logs.length, perfCount:performance.getEntriesByType('resource').length};return JSON.stringify({phase:'MARK_START',ok:true,info:'apiLogs:'+apiLogs.logs.length+' perf:'+window.__AH_MARK__.perfCount});})()",
|
|
"timeout": 3000,
|
|
"phase": "MARK"
|
|
},
|
|
{
|
|
"id": 29,
|
|
"name": "[게시판 > 자유게시판] 메뉴 이동",
|
|
"action": "menu_navigate",
|
|
"level1": "게시판",
|
|
"level2": "자유게시판",
|
|
"timeout": 10000
|
|
},
|
|
{
|
|
"id": 30,
|
|
"name": "[게시판 > 자유게시판] API 호출 대기",
|
|
"action": "wait",
|
|
"timeout": 3000
|
|
},
|
|
{
|
|
"id": 31,
|
|
"name": "[게시판 > 자유게시판] API 건강성 감사",
|
|
"action": "evaluate",
|
|
"script": "(async()=>{const R={phase:'API_AUDIT'};const mark=window.__AH_MARK__||{ts:0,apiLogCount:0,perfCount:0};const apiData=window.__E2E__?window.__E2E__.getApiLogs():{logs:[],errors:[],summary:{}};const newApiLogs=apiData.logs.slice(mark.apiLogCount);R.monitorCalls=newApiLogs.length;const allRes=performance.getEntriesByType('resource');const newRes=allRes.slice(mark.perfCount);const apiRes=newRes.filter(e=>e.name.includes('/api/')||e.name.includes('/v1/')||e.name.includes('/graphql'));R.perfApiCalls=apiRes.length;const jsRes=newRes.filter(e=>e.initiatorType==='script'||e.name.endsWith('.js'));const cssRes=newRes.filter(e=>e.initiatorType==='link'||e.name.endsWith('.css'));const imgRes=newRes.filter(e=>e.initiatorType==='img'||e.name.match(new RegExp('\\\\.(png|jpg|svg|gif|webp)')));R.resourceBreakdown={total:newRes.length,api:apiRes.length,js:jsRes.length,css:cssRes.length,img:imgRes.length};R.totalCalls=Math.max(newApiLogs.length,apiRes.length);R.totalResources=newRes.length;if(R.totalCalls===0){ if(newRes.length>0){ const avgDur=Math.round(newRes.reduce((s,e)=>s+e.duration,0)/newRes.length); R.grade='PASS';R.ok=true; R.summary=newRes.length+'개 리소스 로드(API 0) | avg '+avgDur+'ms | PASS'; R.avgResponseTime=avgDur; }else{ R.grade='PASS';R.ok=true; R.summary='리소스/API 호출 없음 (SPA 캐시) | PASS'; } return JSON.stringify(R);}if(newApiLogs.length>0){ const errors4xx=newApiLogs.filter(l=>l.status>=400&&l.status<500); const errors5xx=newApiLogs.filter(l=>l.status>=500); const netErr=newApiLogs.filter(l=>!l.ok&&l.status===0); const slow=newApiLogs.filter(l=>l.duration>2000); const ok=newApiLogs.filter(l=>l.ok); R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length; R.slowCalls=slow.length;R.successCount=ok.length; const durs=newApiLogs.map(l=>l.duration).filter(d=>d>0); R.avgResponseTime=durs.length>0?Math.round(durs.reduce((a,b)=>a+b,0)/durs.length):0; R.maxResponseTime=durs.length>0?Math.max(...durs):0; const errRate=((errors4xx.length+errors5xx.length+netErr.length)/newApiLogs.length*100); R.errorRate=Math.round(errRate*10)/10; R.failedUrls=[...errors5xx,...errors4xx].slice(0,3).map(l=>l.url.split('?')[0].substring(0,80)); if(errors5xx.length>0||errRate>10){R.grade='FAIL';} else if(errors4xx.length>0||slow.length>3||R.avgResponseTime>1000){R.grade='WARN';} else{R.grade='PASS';} R.summary=newApiLogs.length+'개 API | '+ok.length+'OK '+(errors4xx.length+errors5xx.length)+'err '+slow.length+'slow | avg '+R.avgResponseTime+'ms | '+R.grade;}else{ const durs=apiRes.map(e=>Math.round(e.duration)); R.avgResponseTime=durs.length>0?Math.round(durs.reduce((a,b)=>a+b,0)/durs.length):0; R.slowCalls=apiRes.filter(e=>e.duration>2000).length; if(R.slowCalls>3||R.avgResponseTime>1000){R.grade='WARN';} else{R.grade='PASS';} R.summary=apiRes.length+'개 API(perf) | avg '+R.avgResponseTime+'ms | '+R.grade;}R.ok=true;return JSON.stringify(R);})()",
|
|
"timeout": 10000,
|
|
"phase": "API_AUDIT"
|
|
},
|
|
{
|
|
"id": 32,
|
|
"name": "[게시판 > 공지사항] 마커 기록",
|
|
"action": "evaluate",
|
|
"script": "(async()=>{const apiLogs=window.__E2E__?window.__E2E__.getApiLogs():{logs:[],summary:{}};window.__AH_MARK__={ ts:Date.now(), apiLogCount:apiLogs.logs.length, perfCount:performance.getEntriesByType('resource').length};return JSON.stringify({phase:'MARK_START',ok:true,info:'apiLogs:'+apiLogs.logs.length+' perf:'+window.__AH_MARK__.perfCount});})()",
|
|
"timeout": 3000,
|
|
"phase": "MARK"
|
|
},
|
|
{
|
|
"id": 33,
|
|
"name": "[게시판 > 공지사항] 메뉴 이동",
|
|
"action": "menu_navigate",
|
|
"level1": "게시판",
|
|
"level2": "공지사항",
|
|
"timeout": 10000
|
|
},
|
|
{
|
|
"id": 34,
|
|
"name": "[게시판 > 공지사항] API 호출 대기",
|
|
"action": "wait",
|
|
"timeout": 3000
|
|
},
|
|
{
|
|
"id": 35,
|
|
"name": "[게시판 > 공지사항] API 건강성 감사",
|
|
"action": "evaluate",
|
|
"script": "(async()=>{const R={phase:'API_AUDIT'};const mark=window.__AH_MARK__||{ts:0,apiLogCount:0,perfCount:0};const apiData=window.__E2E__?window.__E2E__.getApiLogs():{logs:[],errors:[],summary:{}};const newApiLogs=apiData.logs.slice(mark.apiLogCount);R.monitorCalls=newApiLogs.length;const allRes=performance.getEntriesByType('resource');const newRes=allRes.slice(mark.perfCount);const apiRes=newRes.filter(e=>e.name.includes('/api/')||e.name.includes('/v1/')||e.name.includes('/graphql'));R.perfApiCalls=apiRes.length;const jsRes=newRes.filter(e=>e.initiatorType==='script'||e.name.endsWith('.js'));const cssRes=newRes.filter(e=>e.initiatorType==='link'||e.name.endsWith('.css'));const imgRes=newRes.filter(e=>e.initiatorType==='img'||e.name.match(new RegExp('\\\\.(png|jpg|svg|gif|webp)')));R.resourceBreakdown={total:newRes.length,api:apiRes.length,js:jsRes.length,css:cssRes.length,img:imgRes.length};R.totalCalls=Math.max(newApiLogs.length,apiRes.length);R.totalResources=newRes.length;if(R.totalCalls===0){ if(newRes.length>0){ const avgDur=Math.round(newRes.reduce((s,e)=>s+e.duration,0)/newRes.length); R.grade='PASS';R.ok=true; R.summary=newRes.length+'개 리소스 로드(API 0) | avg '+avgDur+'ms | PASS'; R.avgResponseTime=avgDur; }else{ R.grade='PASS';R.ok=true; R.summary='리소스/API 호출 없음 (SPA 캐시) | PASS'; } return JSON.stringify(R);}if(newApiLogs.length>0){ const errors4xx=newApiLogs.filter(l=>l.status>=400&&l.status<500); const errors5xx=newApiLogs.filter(l=>l.status>=500); const netErr=newApiLogs.filter(l=>!l.ok&&l.status===0); const slow=newApiLogs.filter(l=>l.duration>2000); const ok=newApiLogs.filter(l=>l.ok); R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length; R.slowCalls=slow.length;R.successCount=ok.length; const durs=newApiLogs.map(l=>l.duration).filter(d=>d>0); R.avgResponseTime=durs.length>0?Math.round(durs.reduce((a,b)=>a+b,0)/durs.length):0; R.maxResponseTime=durs.length>0?Math.max(...durs):0; const errRate=((errors4xx.length+errors5xx.length+netErr.length)/newApiLogs.length*100); R.errorRate=Math.round(errRate*10)/10; R.failedUrls=[...errors5xx,...errors4xx].slice(0,3).map(l=>l.url.split('?')[0].substring(0,80)); if(errors5xx.length>0||errRate>10){R.grade='FAIL';} else if(errors4xx.length>0||slow.length>3||R.avgResponseTime>1000){R.grade='WARN';} else{R.grade='PASS';} R.summary=newApiLogs.length+'개 API | '+ok.length+'OK '+(errors4xx.length+errors5xx.length)+'err '+slow.length+'slow | avg '+R.avgResponseTime+'ms | '+R.grade;}else{ const durs=apiRes.map(e=>Math.round(e.duration)); R.avgResponseTime=durs.length>0?Math.round(durs.reduce((a,b)=>a+b,0)/durs.length):0; R.slowCalls=apiRes.filter(e=>e.duration>2000).length; if(R.slowCalls>3||R.avgResponseTime>1000){R.grade='WARN';} else{R.grade='PASS';} R.summary=apiRes.length+'개 API(perf) | avg '+R.avgResponseTime+'ms | '+R.grade;}R.ok=true;return JSON.stringify(R);})()",
|
|
"timeout": 10000,
|
|
"phase": "API_AUDIT"
|
|
}
|
|
]
|
|
} |