diff --git a/api-health-acc.json b/api-health-acc.json new file mode 100644 index 0000000..11e262e --- /dev/null +++ b/api-health-acc.json @@ -0,0 +1,310 @@ +{ + "id": "api-health-acc", + "name": "API 건강성 감사: 회계", + "version": "1.0.0", + "auth": { + "role": "admin" + }, + "menuNavigation": { + "level1": "회계관리", + "level2": "거래처관리" + }, + "screenshotPolicy": { + "captureOnFail": true, + "captureOnPass": false + }, + "steps": [ + { + "id": 1, + "name": "[회계관리 > 거래처관리] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 2, + "name": "[회계관리 > 거래처관리] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 3, + "name": "[회계관리 > 거래처관리] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 4, + "name": "[회계관리 > 어음관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "어음관리", + "timeout": 10000 + }, + { + "id": 5, + "name": "[회계관리 > 어음관리] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 6, + "name": "[회계관리 > 어음관리] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 7, + "name": "[회계관리 > 어음관리] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 8, + "name": "[회계관리 > 입금관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "입금관리", + "timeout": 10000 + }, + { + "id": 9, + "name": "[회계관리 > 입금관리] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 10, + "name": "[회계관리 > 입금관리] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 11, + "name": "[회계관리 > 입금관리] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 12, + "name": "[회계관리 > 출금관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "출금관리", + "timeout": 10000 + }, + { + "id": 13, + "name": "[회계관리 > 출금관리] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 14, + "name": "[회계관리 > 출금관리] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 15, + "name": "[회계관리 > 출금관리] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 16, + "name": "[회계관리 > 매출관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "매출관리", + "timeout": 10000 + }, + { + "id": 17, + "name": "[회계관리 > 매출관리] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 18, + "name": "[회계관리 > 매출관리] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 19, + "name": "[회계관리 > 매출관리] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 20, + "name": "[회계관리 > 매입관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "매입관리", + "timeout": 10000 + }, + { + "id": 21, + "name": "[회계관리 > 매입관리] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 22, + "name": "[회계관리 > 매입관리] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 23, + "name": "[회계관리 > 매입관리] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 24, + "name": "[회계관리 > 악성채권관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "악성채권관리", + "timeout": 10000 + }, + { + "id": 25, + "name": "[회계관리 > 악성채권관리] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 26, + "name": "[회계관리 > 악성채권관리] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 27, + "name": "[회계관리 > 악성채권관리] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 28, + "name": "[회계관리 > 예상지출관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "예상지출관리", + "timeout": 10000 + }, + { + "id": 29, + "name": "[회계관리 > 예상지출관리] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 30, + "name": "[회계관리 > 예상지출관리] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 31, + "name": "[회계관리 > 예상지출관리] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 32, + "name": "[회계관리 > 카드내역관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "카드내역관리", + "timeout": 10000 + }, + { + "id": 33, + "name": "[회계관리 > 카드내역관리] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 34, + "name": "[회계관리 > 카드내역관리] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 35, + "name": "[회계관리 > 카드내역관리] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 36, + "name": "[회계관리 > 결제관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "결제관리", + "timeout": 10000 + }, + { + "id": 37, + "name": "[회계관리 > 결제관리] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 38, + "name": "[회계관리 > 결제관리] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 39, + "name": "[회계관리 > 결제관리] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + } + ] +} \ No newline at end of file diff --git a/api-health-prod-misc.json b/api-health-prod-misc.json new file mode 100644 index 0000000..027d1c3 --- /dev/null +++ b/api-health-prod-misc.json @@ -0,0 +1,280 @@ +{ + "id": "api-health-prod-misc", + "name": "API 건강성 감사: 생산/기타", + "version": "1.0.0", + "auth": { + "role": "admin" + }, + "menuNavigation": { + "level1": "생산관리", + "level2": "작업지시 관리" + }, + "screenshotPolicy": { + "captureOnFail": true, + "captureOnPass": false + }, + "steps": [ + { + "id": 1, + "name": "[생산관리 > 작업지시 관리] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 2, + "name": "[생산관리 > 작업지시 관리] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 3, + "name": "[생산관리 > 작업지시 관리] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 4, + "name": "[생산관리 > 작업실적] 메뉴 이동", + "action": "menu_navigate", + "level1": "생산관리", + "level2": "작업실적", + "timeout": 10000 + }, + { + "id": 5, + "name": "[생산관리 > 작업실적] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 6, + "name": "[생산관리 > 작업실적] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 7, + "name": "[생산관리 > 작업실적] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 8, + "name": "[생산관리 > 품목관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "생산관리", + "level2": "품목관리", + "timeout": 10000 + }, + { + "id": 9, + "name": "[생산관리 > 품목관리] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 10, + "name": "[생산관리 > 품목관리] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 11, + "name": "[생산관리 > 품목관리] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 12, + "name": "[생산관리 > 작업자 화면] 메뉴 이동", + "action": "menu_navigate", + "level1": "생산관리", + "level2": "작업자 화면", + "timeout": 10000 + }, + { + "id": 13, + "name": "[생산관리 > 작업자 화면] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 14, + "name": "[생산관리 > 작업자 화면] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 15, + "name": "[생산관리 > 작업자 화면] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 16, + "name": "[품질관리 > 제품검사관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "품질관리", + "level2": "제품검사관리", + "timeout": 10000 + }, + { + "id": 17, + "name": "[품질관리 > 제품검사관리] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 18, + "name": "[품질관리 > 제품검사관리] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 19, + "name": "[품질관리 > 제품검사관리] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 20, + "name": "[자재관리 > 입고관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "자재관리", + "level2": "입고관리", + "timeout": 10000 + }, + { + "id": 21, + "name": "[자재관리 > 입고관리] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 22, + "name": "[자재관리 > 입고관리] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 23, + "name": "[자재관리 > 입고관리] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 24, + "name": "[자재관리 > 재고현황] 메뉴 이동", + "action": "menu_navigate", + "level1": "자재관리", + "level2": "재고현황", + "timeout": 10000 + }, + { + "id": 25, + "name": "[자재관리 > 재고현황] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 26, + "name": "[자재관리 > 재고현황] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 27, + "name": "[자재관리 > 재고현황] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 28, + "name": "[게시판 > 자유게시판] 메뉴 이동", + "action": "menu_navigate", + "level1": "게시판", + "level2": "자유게시판", + "timeout": 10000 + }, + { + "id": 29, + "name": "[게시판 > 자유게시판] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 30, + "name": "[게시판 > 자유게시판] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 31, + "name": "[게시판 > 자유게시판] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 32, + "name": "[게시판 > 공지사항] 메뉴 이동", + "action": "menu_navigate", + "level1": "게시판", + "level2": "공지사항", + "timeout": 10000 + }, + { + "id": 33, + "name": "[게시판 > 공지사항] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 34, + "name": "[게시판 > 공지사항] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 35, + "name": "[게시판 > 공지사항] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + } + ] +} \ No newline at end of file diff --git a/api-health-sales-hr.json b/api-health-sales-hr.json new file mode 100644 index 0000000..8ce87a0 --- /dev/null +++ b/api-health-sales-hr.json @@ -0,0 +1,280 @@ +{ + "id": "api-health-sales-hr", + "name": "API 건강성 감사: 판매/인사", + "version": "1.0.0", + "auth": { + "role": "admin" + }, + "menuNavigation": { + "level1": "판매관리", + "level2": "거래처관리" + }, + "screenshotPolicy": { + "captureOnFail": true, + "captureOnPass": false + }, + "steps": [ + { + "id": 1, + "name": "[판매관리 > 거래처관리] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 2, + "name": "[판매관리 > 거래처관리] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 3, + "name": "[판매관리 > 거래처관리] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 4, + "name": "[판매관리 > 수주관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "판매관리", + "level2": "수주관리", + "timeout": 10000 + }, + { + "id": 5, + "name": "[판매관리 > 수주관리] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 6, + "name": "[판매관리 > 수주관리] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 7, + "name": "[판매관리 > 수주관리] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 8, + "name": "[판매관리 > 견적관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "판매관리", + "level2": "견적관리", + "timeout": 10000 + }, + { + "id": 9, + "name": "[판매관리 > 견적관리] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 10, + "name": "[판매관리 > 견적관리] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 11, + "name": "[판매관리 > 견적관리] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 12, + "name": "[판매관리 > 단가관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "판매관리", + "level2": "단가관리", + "timeout": 10000 + }, + { + "id": 13, + "name": "[판매관리 > 단가관리] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 14, + "name": "[판매관리 > 단가관리] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 15, + "name": "[판매관리 > 단가관리] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 16, + "name": "[인사관리 > 사원관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "인사관리", + "level2": "사원관리", + "timeout": 10000 + }, + { + "id": 17, + "name": "[인사관리 > 사원관리] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 18, + "name": "[인사관리 > 사원관리] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 19, + "name": "[인사관리 > 사원관리] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 20, + "name": "[인사관리 > 급여관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "인사관리", + "level2": "급여관리", + "timeout": 10000 + }, + { + "id": 21, + "name": "[인사관리 > 급여관리] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 22, + "name": "[인사관리 > 급여관리] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 23, + "name": "[인사관리 > 급여관리] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 24, + "name": "[인사관리 > 근태현황] 메뉴 이동", + "action": "menu_navigate", + "level1": "인사관리", + "level2": "근태현황", + "timeout": 10000 + }, + { + "id": 25, + "name": "[인사관리 > 근태현황] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 26, + "name": "[인사관리 > 근태현황] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 27, + "name": "[인사관리 > 근태현황] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 28, + "name": "[인사관리 > 휴가관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "인사관리", + "level2": "휴가관리", + "timeout": 10000 + }, + { + "id": 29, + "name": "[인사관리 > 휴가관리] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 30, + "name": "[인사관리 > 휴가관리] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 31, + "name": "[인사관리 > 휴가관리] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + }, + { + "id": 32, + "name": "[인사관리 > 카드관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "인사관리", + "level2": "카드관리", + "timeout": 10000 + }, + { + "id": 33, + "name": "[인사관리 > 카드관리] API 인터셉터 설치", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'INSTALL_INTERCEPTOR'};window.__API_HEALTH__=[];window.__API_HEALTH_START__=Date.now();if(!window.__HEALTH_FETCH_PATCHED__){ const origFetch=window.fetch; window.fetch=async function(...args){ const url=typeof args[0]==='string'?args[0]:(args[0]?.url||''); const method=(args[1]?.method||'GET').toUpperCase(); const t0=Date.now(); try{ const resp=await origFetch.apply(this,args); const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:resp.status,duration:dur,ok:resp.ok,ts:Date.now()}); } return resp; }catch(e){ const dur=Date.now()-t0; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method,status:0,duration:dur,ok:false,error:e.message,ts:Date.now()}); } throw e; } }; window.__HEALTH_FETCH_PATCHED__=true;}if(!window.__HEALTH_XHR_PATCHED__){ const origOpen=XMLHttpRequest.prototype.open; const origSend=XMLHttpRequest.prototype.send; XMLHttpRequest.prototype.open=function(method,url,...rest){ this.__h_method=method;this.__h_url=url;this.__h_t0=Date.now(); return origOpen.apply(this,[method,url,...rest]); }; XMLHttpRequest.prototype.send=function(...args){ this.addEventListener('loadend',function(){ const url=this.__h_url||''; if(url.includes('/api/')||url.includes('/v1/')){ window.__API_HEALTH__.push({url:url.substring(0,120),method:(this.__h_method||'GET').toUpperCase(),status:this.status,duration:Date.now()-(this.__h_t0||Date.now()),ok:this.status>=200&&this.status<400,ts:Date.now()}); } }); return origSend.apply(this,args); }; window.__HEALTH_XHR_PATCHED__=true;}R.interceptorInstalled=true;R.ok=true;return JSON.stringify(R);})()", + "timeout": 5000, + "phase": "INSTALL" + }, + { + "id": 34, + "name": "[인사관리 > 카드관리] API 호출 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 35, + "name": "[인사관리 > 카드관리] API 건강성 감사", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'API_AUDIT'};const logs=window.__API_HEALTH__||[];R.totalCalls=logs.length;if(logs.length===0){R.warn='API 호출 없음 (인터셉터 미동작 또는 API 없는 페이지)';R.grade='WARN';R.ok=true;return JSON.stringify(R);}const errors4xx=logs.filter(l=>l.status>=400&&l.status<500);const errors5xx=logs.filter(l=>l.status>=500);const networkErrors=logs.filter(l=>l.status===0);const slowCalls=logs.filter(l=>l.duration>2000);const successCalls=logs.filter(l=>l.ok);R.errors4xx=errors4xx.length;R.errors5xx=errors5xx.length;R.networkErrors=networkErrors.length;R.slowCalls=slowCalls.length;R.successCount=successCalls.length;const durations=logs.map(l=>l.duration).filter(d=>d>0);R.avgResponseTime=durations.length>0?Math.round(durations.reduce((a,b)=>a+b,0)/durations.length):0;R.maxResponseTime=durations.length>0?Math.max(...durations):0;R.minResponseTime=durations.length>0?Math.min(...durations):0;const errorRate=logs.length>0?((errors4xx.length+errors5xx.length+networkErrors.length)/logs.length*100):0;R.errorRate=Math.round(errorRate*10)/10;R.failedUrls=[...errors5xx,...errors4xx,...networkErrors].slice(0,5).map(l=>({url:l.url,status:l.status,method:l.method,duration:l.duration}));R.slowUrls=slowCalls.slice(0,3).map(l=>({url:l.url,duration:l.duration,method:l.method}));if(errors5xx.length>0||errorRate>10){R.grade='FAIL';}else if(errors4xx.length>0||slowCalls.length>3||R.avgResponseTime>1000){R.grade='WARN';}else{R.grade='PASS';}R.summary=R.totalCalls+'개 API 호출 | '+R.successCount+'성공 | '+(errors4xx.length+errors5xx.length)+'에러 | '+slowCalls.length+'느림 | 평균 '+R.avgResponseTime+'ms | 등급: '+R.grade;window.__API_HEALTH__=[];R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "API_AUDIT" + } + ] +} \ No newline at end of file diff --git a/cross-module-data-consistency.json b/cross-module-data-consistency.json new file mode 100644 index 0000000..50d8a17 --- /dev/null +++ b/cross-module-data-consistency.json @@ -0,0 +1,122 @@ +{ + "id": "cross-module-data-consistency", + "name": "모듈 간 데이터 일관성 검증 (판매↔회계, 판매↔생산)", + "version": "1.0.0", + "auth": { + "role": "admin" + }, + "menuNavigation": { + "level1": "판매관리", + "level2": "거래처관리" + }, + "screenshotPolicy": { + "captureOnFail": true, + "captureOnPass": false + }, + "steps": [ + { + "id": 1, + "name": "[판매 > 거래처관리] 페이지 로드 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 2, + "name": "[판매 > 거래처관리] 테이블 로드 대기", + "action": "wait_for_table", + "timeout": 5000 + }, + { + "id": 3, + "name": "[판매 > 거래처관리] 거래처명 캡처", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CAPTURE_VENDOR'};await w(1500);const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);R.rowCount=rows.length;if(rows.length===0){R.warn='판매>거래처관리 테이블에 데이터 없음';R.ok=true;return JSON.stringify(R);}const cells=rows[0].querySelectorAll('td');let vendorName='';for(let i=1;i=2&&t.length<=30&&!/^[\\d,.]+$/.test(t)&&!/^\\d{4}[-/]/.test(t)&&!cells[i].querySelector('input[type=\"checkbox\"]')){ vendorName=t;break; }}R.vendorName=vendorName;if(!vendorName){R.warn='거래처명 추출 실패';R.ok=true;return JSON.stringify(R);}if(!window.__CROSS_DATA__)window.__CROSS_DATA__={};window.__CROSS_DATA__.vendorName=vendorName;R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "CAPTURE_VENDOR" + }, + { + "id": 4, + "name": "[회계 > 거래처관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "거래처관리", + "timeout": 10000 + }, + { + "id": 5, + "name": "[회계 > 거래처관리] 페이지 로드 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 6, + "name": "[회계 > 거래처관리] 테이블 로드 대기", + "action": "wait_for_table", + "timeout": 5000 + }, + { + "id": 7, + "name": "[회계 > 거래처관리] 거래처 존재 확인", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'VERIFY_VENDOR_ACC'};await w(2000);const vendorName=window.__CROSS_DATA__?.vendorName;if(!vendorName){R.warn='캡처된 거래처명 없음 (이전 단계 실패)';R.ok=true;return JSON.stringify(R);}R.searchTarget=vendorName;const searchInput=document.querySelector('input[placeholder*=\"검색\"]')||document.querySelector('input[type=\"search\"]')||document.querySelector('input[role=\"searchbox\"]');if(searchInput){ searchInput.focus();await w(200); const nativeSetter=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set; if(nativeSetter)nativeSetter.call(searchInput,vendorName);else searchInput.value=vendorName; searchInput.dispatchEvent(new Event('input',{bubbles:true})); searchInput.dispatchEvent(new Event('change',{bubbles:true})); searchInput.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',code:'Enter',keyCode:13,bubbles:true})); await w(2500); R.searchUsed=true;}else{R.searchUsed=false;}const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);R.rowCount=rows.length;const found=rows.some(r=>r.innerText?.includes(vendorName));R.vendorFound=found;if(!found&&!searchInput){ R.vendorFoundInPage=document.body.innerText.includes(vendorName);}if(!found){R.warn='⚠️ 회계>거래처관리에서 ['+vendorName+'] 미발견 - 모듈 간 데이터 불일치 가능';R.ok=true;}else{R.info='✅ 판매/회계 거래처 데이터 일치 확인: '+vendorName;R.ok=true;}if(searchInput){ const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set; if(ns)ns.call(searchInput,'');else searchInput.value=''; searchInput.dispatchEvent(new Event('input',{bubbles:true})); searchInput.dispatchEvent(new Event('change',{bubbles:true})); searchInput.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',code:'Enter',keyCode:13,bubbles:true})); await w(1500);}return JSON.stringify(R);})()", + "timeout": 15000, + "phase": "VERIFY_VENDOR_ACC" + }, + { + "id": 8, + "name": "[판매 > 단가관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "판매관리", + "level2": "단가관리", + "timeout": 10000 + }, + { + "id": 9, + "name": "[판매 > 단가관리] 페이지 로드 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 10, + "name": "[판매 > 단가관리] 테이블 로드 대기", + "action": "wait_for_table", + "timeout": 5000 + }, + { + "id": 11, + "name": "[판매 > 단가관리] 품목명 캡처", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CAPTURE_ITEM'};await w(1500);const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);R.rowCount=rows.length;if(rows.length===0){R.warn='판매>단가관리 테이블에 데이터 없음';R.ok=true;return JSON.stringify(R);}const cells=rows[0].querySelectorAll('td');let itemName='';for(let i=1;i=2&&t.length<=30&&!/^[\\d,.]+$/.test(t)&&!/^\\d{4}[-/]/.test(t)&&!cells[i].querySelector('input[type=\"checkbox\"]')){ itemName=t;break; }}R.itemName=itemName;if(!itemName){R.warn='품목명 추출 실패';R.ok=true;return JSON.stringify(R);}if(!window.__CROSS_DATA__)window.__CROSS_DATA__={};window.__CROSS_DATA__.itemName=itemName;R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "CAPTURE_ITEM" + }, + { + "id": 12, + "name": "[생산 > 품목관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "생산관리", + "level2": "품목관리", + "timeout": 10000 + }, + { + "id": 13, + "name": "[생산 > 품목관리] 페이지 로드 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 14, + "name": "[생산 > 품목관리] 테이블 로드 대기", + "action": "wait_for_table", + "timeout": 5000 + }, + { + "id": 15, + "name": "[생산 > 품목관리] 품목 존재 확인", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'VERIFY_ITEM_PROD'};await w(2000);const itemName=window.__CROSS_DATA__?.itemName;if(!itemName){R.warn='캡처된 품목명 없음 (이전 단계 실패)';R.ok=true;return JSON.stringify(R);}R.searchTarget=itemName;const searchInput=document.querySelector('input[placeholder*=\"검색\"]')||document.querySelector('input[type=\"search\"]')||document.querySelector('input[role=\"searchbox\"]');if(searchInput){ searchInput.focus();await w(200); const nativeSetter=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set; if(nativeSetter)nativeSetter.call(searchInput,itemName);else searchInput.value=itemName; searchInput.dispatchEvent(new Event('input',{bubbles:true})); searchInput.dispatchEvent(new Event('change',{bubbles:true})); searchInput.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',code:'Enter',keyCode:13,bubbles:true})); await w(2500); R.searchUsed=true;}else{R.searchUsed=false;}const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);R.rowCount=rows.length;const found=rows.some(r=>r.innerText?.includes(itemName));R.itemFound=found;if(!found){R.warn='⚠️ 생산>품목관리에서 ['+itemName+'] 미발견 - 모듈 간 데이터 불일치 가능';R.ok=true;}else{R.info='✅ 판매/생산 품목 데이터 일치 확인: '+itemName;R.ok=true;}if(searchInput){ const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set; if(ns)ns.call(searchInput,'');else searchInput.value=''; searchInput.dispatchEvent(new Event('input',{bubbles:true})); searchInput.dispatchEvent(new Event('change',{bubbles:true})); searchInput.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',code:'Enter',keyCode:13,bubbles:true})); await w(1500);}return JSON.stringify(R);})()", + "timeout": 15000, + "phase": "VERIFY_ITEM_PROD" + } + ] +} \ No newline at end of file diff --git a/detail-roundtrip-acc.json b/detail-roundtrip-acc.json new file mode 100644 index 0000000..09b9ce2 --- /dev/null +++ b/detail-roundtrip-acc.json @@ -0,0 +1,190 @@ +{ + "id": "detail-roundtrip-acc", + "name": "상세 조회 왕복 검증: 회계", + "version": "1.0.0", + "auth": { + "role": "admin" + }, + "menuNavigation": { + "level1": "회계관리", + "level2": "거래처관리" + }, + "screenshotPolicy": { + "captureOnFail": true, + "captureOnPass": false + }, + "steps": [ + { + "id": 1, + "name": "[회계관리 > 거래처관리] 페이지 로드 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 2, + "name": "[회계관리 > 거래처관리] 테이블 로드 대기", + "action": "wait_for_table", + "timeout": 5000 + }, + { + "id": 3, + "name": "[회계관리 > 거래처관리] 테이블 상태 캡처", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CAPTURE'};const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);R.rowCount=rows.length;if(rows.length===0){R.warn='테이블에 데이터 없음 - 상세 조회 불가';R.ok=true;return JSON.stringify(R);}const firstRow=rows[0];const cells=Array.from(firstRow.querySelectorAll('td'));const cellTexts=cells.map(c=>c.innerText?.trim().substring(0,50)).filter(t=>t.length>0);R.firstRowTexts=cellTexts;const style=window.getComputedStyle(firstRow);R.hasCursor=style.cursor==='pointer';R.listUrl=window.location.href;window.__CAPTURED__={rowCount:rows.length,firstRowTexts:cellTexts,listUrl:window.location.href};R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "CAPTURE" + }, + { + "id": 4, + "name": "[회계관리 > 거래처관리] 첫 행 클릭 → 상세 이동", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CLICK_ROW'};const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);if(rows.length===0){R.error='행 없음';R.ok=false;return JSON.stringify(R);}const firstRow=rows[0];const targetCell=firstRow.querySelector('td:nth-child(2)')||firstRow.querySelector('td');if(!targetCell){R.error='클릭할 셀 없음';R.ok=false;return JSON.stringify(R);}R.clickedText=targetCell.innerText?.trim().substring(0,30);R.urlBefore=window.location.href;targetCell.click();await w(500);if(window.location.href===R.urlBefore){ const link=firstRow.querySelector('a[href]'); if(link){link.click();await w(500);}}let waited=0;while(window.location.href===R.urlBefore&&waited<5000){await w(300);waited+=300;}R.urlAfter=window.location.href;R.urlChanged=R.urlAfter!==R.urlBefore;if(!R.urlChanged){R.warn='행 클릭 후 URL 변경 없음 - 모달 또는 인라인 상세일 수 있음';R.ok=true;return JSON.stringify(R);}R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "CLICK_ROW" + }, + { + "id": 5, + "name": "[회계관리 > 거래처관리] 상세 페이지 데이터 검증", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'VERIFY_DETAIL'};await w(1500);R.currentUrl=window.location.href;const idPattern=new RegExp('[/][0-9a-f]{8,}|[/][0-9]+[?]|[/][0-9]+$');R.hasIdInUrl=idPattern.test(R.currentUrl);R.hasViewMode=R.currentUrl.includes('mode=view')||R.currentUrl.includes('mode=edit');const captured=window.__CAPTURED__;R.hasCapturedData=!!captured;if(captured&&captured.firstRowTexts){ const pageText=document.body.innerText; let matchCount=0; const checks=[]; for(const t of captured.firstRowTexts){ if(t.length>=2){ const found=pageText.includes(t); if(found)matchCount++; checks.push({text:t.substring(0,20),found}); } } R.dataChecks=checks.slice(0,5); R.matchCount=matchCount; R.totalChecked=checks.length; R.dataMatch=matchCount>0;}const tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button,button[data-state]');R.tabCount=tabs.length;R.tabLabels=Array.from(tabs).slice(0,5).map(t=>t.innerText?.trim().substring(0,20));const inputs=document.querySelectorAll('input:not([type=\"hidden\"]),textarea,select');R.inputCount=inputs.length;R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "VERIFY_DETAIL" + }, + { + "id": 6, + "name": "[회계관리 > 거래처관리] 목록으로 복귀", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'GO_BACK'};R.detailUrl=window.location.href;const captured=window.__CAPTURED__;const listUrl=captured?.listUrl||'';const listBtn=Array.from(document.querySelectorAll('button,a')).find(b=>{ const txt=b.innerText?.trim()||''; return /목록|리스트|뒤로|Back|List/i.test(txt)&&b.offsetParent!==null;});if(listBtn){ R.method='목록 버튼 클릭'; listBtn.click(); await w(2000);}else{ R.method='history.back()'; window.history.back(); await w(2000);}R.returnedUrl=window.location.href;R.urlMatches=listUrl?R.returnedUrl===listUrl:!R.returnedUrl.includes('mode=view');if(!R.urlMatches&&listUrl){ R.info='목록 URL과 다름: expected='+listUrl.substring(listUrl.length-40)+' actual='+R.returnedUrl.substring(R.returnedUrl.length-40);}R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "GO_BACK" + }, + { + "id": 7, + "name": "[회계관리 > 거래처관리] 목록 무결성 확인", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(1500);const R={phase:'LIST_INTACT'};const captured=window.__CAPTURED__;if(!captured){R.warn='캡처 데이터 없음';R.ok=true;return JSON.stringify(R);}const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);R.currentRowCount=rows.length;R.originalRowCount=captured.rowCount;R.rowCountMatch=R.currentRowCount===R.originalRowCount;if(rows.length>0){ const cells=Array.from(rows[0].querySelectorAll('td')); const cellTexts=cells.map(c=>c.innerText?.trim().substring(0,50)).filter(t=>t.length>0); R.currentFirstRow=cellTexts.slice(0,3); R.originalFirstRow=(captured.firstRowTexts||[]).slice(0,3); let textMatch=0; for(const t of R.originalFirstRow){ if(cellTexts.some(c=>c.includes(t)||t.includes(c)))textMatch++; } R.firstRowMatch=textMatch>0;}else{ R.firstRowMatch=false; R.warn='목록 복귀 후 행 없음';}R.intact=R.rowCountMatch&&R.firstRowMatch;if(!R.intact&&!R.rowCountMatch)R.info='행 수 변경: '+R.originalRowCount+'→'+R.currentRowCount;R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "LIST_INTACT" + }, + { + "id": 8, + "name": "[회계관리 > 어음관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "어음관리", + "timeout": 10000 + }, + { + "id": 9, + "name": "[회계관리 > 어음관리] 페이지 로드 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 10, + "name": "[회계관리 > 어음관리] 테이블 로드 대기", + "action": "wait_for_table", + "timeout": 5000 + }, + { + "id": 11, + "name": "[회계관리 > 어음관리] 테이블 상태 캡처", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CAPTURE'};const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);R.rowCount=rows.length;if(rows.length===0){R.warn='테이블에 데이터 없음 - 상세 조회 불가';R.ok=true;return JSON.stringify(R);}const firstRow=rows[0];const cells=Array.from(firstRow.querySelectorAll('td'));const cellTexts=cells.map(c=>c.innerText?.trim().substring(0,50)).filter(t=>t.length>0);R.firstRowTexts=cellTexts;const style=window.getComputedStyle(firstRow);R.hasCursor=style.cursor==='pointer';R.listUrl=window.location.href;window.__CAPTURED__={rowCount:rows.length,firstRowTexts:cellTexts,listUrl:window.location.href};R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "CAPTURE" + }, + { + "id": 12, + "name": "[회계관리 > 어음관리] 첫 행 클릭 → 상세 이동", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CLICK_ROW'};const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);if(rows.length===0){R.error='행 없음';R.ok=false;return JSON.stringify(R);}const firstRow=rows[0];const targetCell=firstRow.querySelector('td:nth-child(2)')||firstRow.querySelector('td');if(!targetCell){R.error='클릭할 셀 없음';R.ok=false;return JSON.stringify(R);}R.clickedText=targetCell.innerText?.trim().substring(0,30);R.urlBefore=window.location.href;targetCell.click();await w(500);if(window.location.href===R.urlBefore){ const link=firstRow.querySelector('a[href]'); if(link){link.click();await w(500);}}let waited=0;while(window.location.href===R.urlBefore&&waited<5000){await w(300);waited+=300;}R.urlAfter=window.location.href;R.urlChanged=R.urlAfter!==R.urlBefore;if(!R.urlChanged){R.warn='행 클릭 후 URL 변경 없음 - 모달 또는 인라인 상세일 수 있음';R.ok=true;return JSON.stringify(R);}R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "CLICK_ROW" + }, + { + "id": 13, + "name": "[회계관리 > 어음관리] 상세 페이지 데이터 검증", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'VERIFY_DETAIL'};await w(1500);R.currentUrl=window.location.href;const idPattern=new RegExp('[/][0-9a-f]{8,}|[/][0-9]+[?]|[/][0-9]+$');R.hasIdInUrl=idPattern.test(R.currentUrl);R.hasViewMode=R.currentUrl.includes('mode=view')||R.currentUrl.includes('mode=edit');const captured=window.__CAPTURED__;R.hasCapturedData=!!captured;if(captured&&captured.firstRowTexts){ const pageText=document.body.innerText; let matchCount=0; const checks=[]; for(const t of captured.firstRowTexts){ if(t.length>=2){ const found=pageText.includes(t); if(found)matchCount++; checks.push({text:t.substring(0,20),found}); } } R.dataChecks=checks.slice(0,5); R.matchCount=matchCount; R.totalChecked=checks.length; R.dataMatch=matchCount>0;}const tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button,button[data-state]');R.tabCount=tabs.length;R.tabLabels=Array.from(tabs).slice(0,5).map(t=>t.innerText?.trim().substring(0,20));const inputs=document.querySelectorAll('input:not([type=\"hidden\"]),textarea,select');R.inputCount=inputs.length;R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "VERIFY_DETAIL" + }, + { + "id": 14, + "name": "[회계관리 > 어음관리] 목록으로 복귀", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'GO_BACK'};R.detailUrl=window.location.href;const captured=window.__CAPTURED__;const listUrl=captured?.listUrl||'';const listBtn=Array.from(document.querySelectorAll('button,a')).find(b=>{ const txt=b.innerText?.trim()||''; return /목록|리스트|뒤로|Back|List/i.test(txt)&&b.offsetParent!==null;});if(listBtn){ R.method='목록 버튼 클릭'; listBtn.click(); await w(2000);}else{ R.method='history.back()'; window.history.back(); await w(2000);}R.returnedUrl=window.location.href;R.urlMatches=listUrl?R.returnedUrl===listUrl:!R.returnedUrl.includes('mode=view');if(!R.urlMatches&&listUrl){ R.info='목록 URL과 다름: expected='+listUrl.substring(listUrl.length-40)+' actual='+R.returnedUrl.substring(R.returnedUrl.length-40);}R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "GO_BACK" + }, + { + "id": 15, + "name": "[회계관리 > 어음관리] 목록 무결성 확인", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(1500);const R={phase:'LIST_INTACT'};const captured=window.__CAPTURED__;if(!captured){R.warn='캡처 데이터 없음';R.ok=true;return JSON.stringify(R);}const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);R.currentRowCount=rows.length;R.originalRowCount=captured.rowCount;R.rowCountMatch=R.currentRowCount===R.originalRowCount;if(rows.length>0){ const cells=Array.from(rows[0].querySelectorAll('td')); const cellTexts=cells.map(c=>c.innerText?.trim().substring(0,50)).filter(t=>t.length>0); R.currentFirstRow=cellTexts.slice(0,3); R.originalFirstRow=(captured.firstRowTexts||[]).slice(0,3); let textMatch=0; for(const t of R.originalFirstRow){ if(cellTexts.some(c=>c.includes(t)||t.includes(c)))textMatch++; } R.firstRowMatch=textMatch>0;}else{ R.firstRowMatch=false; R.warn='목록 복귀 후 행 없음';}R.intact=R.rowCountMatch&&R.firstRowMatch;if(!R.intact&&!R.rowCountMatch)R.info='행 수 변경: '+R.originalRowCount+'→'+R.currentRowCount;R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "LIST_INTACT" + }, + { + "id": 16, + "name": "[회계관리 > 입금관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "입금관리", + "timeout": 10000 + }, + { + "id": 17, + "name": "[회계관리 > 입금관리] 페이지 로드 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 18, + "name": "[회계관리 > 입금관리] 테이블 로드 대기", + "action": "wait_for_table", + "timeout": 5000 + }, + { + "id": 19, + "name": "[회계관리 > 입금관리] 테이블 상태 캡처", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CAPTURE'};const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);R.rowCount=rows.length;if(rows.length===0){R.warn='테이블에 데이터 없음 - 상세 조회 불가';R.ok=true;return JSON.stringify(R);}const firstRow=rows[0];const cells=Array.from(firstRow.querySelectorAll('td'));const cellTexts=cells.map(c=>c.innerText?.trim().substring(0,50)).filter(t=>t.length>0);R.firstRowTexts=cellTexts;const style=window.getComputedStyle(firstRow);R.hasCursor=style.cursor==='pointer';R.listUrl=window.location.href;window.__CAPTURED__={rowCount:rows.length,firstRowTexts:cellTexts,listUrl:window.location.href};R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "CAPTURE" + }, + { + "id": 20, + "name": "[회계관리 > 입금관리] 첫 행 클릭 → 상세 이동", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CLICK_ROW'};const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);if(rows.length===0){R.error='행 없음';R.ok=false;return JSON.stringify(R);}const firstRow=rows[0];const targetCell=firstRow.querySelector('td:nth-child(2)')||firstRow.querySelector('td');if(!targetCell){R.error='클릭할 셀 없음';R.ok=false;return JSON.stringify(R);}R.clickedText=targetCell.innerText?.trim().substring(0,30);R.urlBefore=window.location.href;targetCell.click();await w(500);if(window.location.href===R.urlBefore){ const link=firstRow.querySelector('a[href]'); if(link){link.click();await w(500);}}let waited=0;while(window.location.href===R.urlBefore&&waited<5000){await w(300);waited+=300;}R.urlAfter=window.location.href;R.urlChanged=R.urlAfter!==R.urlBefore;if(!R.urlChanged){R.warn='행 클릭 후 URL 변경 없음 - 모달 또는 인라인 상세일 수 있음';R.ok=true;return JSON.stringify(R);}R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "CLICK_ROW" + }, + { + "id": 21, + "name": "[회계관리 > 입금관리] 상세 페이지 데이터 검증", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'VERIFY_DETAIL'};await w(1500);R.currentUrl=window.location.href;const idPattern=new RegExp('[/][0-9a-f]{8,}|[/][0-9]+[?]|[/][0-9]+$');R.hasIdInUrl=idPattern.test(R.currentUrl);R.hasViewMode=R.currentUrl.includes('mode=view')||R.currentUrl.includes('mode=edit');const captured=window.__CAPTURED__;R.hasCapturedData=!!captured;if(captured&&captured.firstRowTexts){ const pageText=document.body.innerText; let matchCount=0; const checks=[]; for(const t of captured.firstRowTexts){ if(t.length>=2){ const found=pageText.includes(t); if(found)matchCount++; checks.push({text:t.substring(0,20),found}); } } R.dataChecks=checks.slice(0,5); R.matchCount=matchCount; R.totalChecked=checks.length; R.dataMatch=matchCount>0;}const tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button,button[data-state]');R.tabCount=tabs.length;R.tabLabels=Array.from(tabs).slice(0,5).map(t=>t.innerText?.trim().substring(0,20));const inputs=document.querySelectorAll('input:not([type=\"hidden\"]),textarea,select');R.inputCount=inputs.length;R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "VERIFY_DETAIL" + }, + { + "id": 22, + "name": "[회계관리 > 입금관리] 목록으로 복귀", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'GO_BACK'};R.detailUrl=window.location.href;const captured=window.__CAPTURED__;const listUrl=captured?.listUrl||'';const listBtn=Array.from(document.querySelectorAll('button,a')).find(b=>{ const txt=b.innerText?.trim()||''; return /목록|리스트|뒤로|Back|List/i.test(txt)&&b.offsetParent!==null;});if(listBtn){ R.method='목록 버튼 클릭'; listBtn.click(); await w(2000);}else{ R.method='history.back()'; window.history.back(); await w(2000);}R.returnedUrl=window.location.href;R.urlMatches=listUrl?R.returnedUrl===listUrl:!R.returnedUrl.includes('mode=view');if(!R.urlMatches&&listUrl){ R.info='목록 URL과 다름: expected='+listUrl.substring(listUrl.length-40)+' actual='+R.returnedUrl.substring(R.returnedUrl.length-40);}R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "GO_BACK" + }, + { + "id": 23, + "name": "[회계관리 > 입금관리] 목록 무결성 확인", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(1500);const R={phase:'LIST_INTACT'};const captured=window.__CAPTURED__;if(!captured){R.warn='캡처 데이터 없음';R.ok=true;return JSON.stringify(R);}const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);R.currentRowCount=rows.length;R.originalRowCount=captured.rowCount;R.rowCountMatch=R.currentRowCount===R.originalRowCount;if(rows.length>0){ const cells=Array.from(rows[0].querySelectorAll('td')); const cellTexts=cells.map(c=>c.innerText?.trim().substring(0,50)).filter(t=>t.length>0); R.currentFirstRow=cellTexts.slice(0,3); R.originalFirstRow=(captured.firstRowTexts||[]).slice(0,3); let textMatch=0; for(const t of R.originalFirstRow){ if(cellTexts.some(c=>c.includes(t)||t.includes(c)))textMatch++; } R.firstRowMatch=textMatch>0;}else{ R.firstRowMatch=false; R.warn='목록 복귀 후 행 없음';}R.intact=R.rowCountMatch&&R.firstRowMatch;if(!R.intact&&!R.rowCountMatch)R.info='행 수 변경: '+R.originalRowCount+'→'+R.currentRowCount;R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "LIST_INTACT" + } + ] +} \ No newline at end of file diff --git a/detail-roundtrip-hr-board.json b/detail-roundtrip-hr-board.json new file mode 100644 index 0000000..3aaea10 --- /dev/null +++ b/detail-roundtrip-hr-board.json @@ -0,0 +1,130 @@ +{ + "id": "detail-roundtrip-hr-board", + "name": "상세 조회 왕복 검증: 인사/게시판", + "version": "1.0.0", + "auth": { + "role": "admin" + }, + "menuNavigation": { + "level1": "인사관리", + "level2": "사원관리" + }, + "screenshotPolicy": { + "captureOnFail": true, + "captureOnPass": false + }, + "steps": [ + { + "id": 1, + "name": "[인사관리 > 사원관리] 페이지 로드 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 2, + "name": "[인사관리 > 사원관리] 테이블 로드 대기", + "action": "wait_for_table", + "timeout": 5000 + }, + { + "id": 3, + "name": "[인사관리 > 사원관리] 테이블 상태 캡처", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CAPTURE'};const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);R.rowCount=rows.length;if(rows.length===0){R.warn='테이블에 데이터 없음 - 상세 조회 불가';R.ok=true;return JSON.stringify(R);}const firstRow=rows[0];const cells=Array.from(firstRow.querySelectorAll('td'));const cellTexts=cells.map(c=>c.innerText?.trim().substring(0,50)).filter(t=>t.length>0);R.firstRowTexts=cellTexts;const style=window.getComputedStyle(firstRow);R.hasCursor=style.cursor==='pointer';R.listUrl=window.location.href;window.__CAPTURED__={rowCount:rows.length,firstRowTexts:cellTexts,listUrl:window.location.href};R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "CAPTURE" + }, + { + "id": 4, + "name": "[인사관리 > 사원관리] 첫 행 클릭 → 상세 이동", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CLICK_ROW'};const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);if(rows.length===0){R.error='행 없음';R.ok=false;return JSON.stringify(R);}const firstRow=rows[0];const targetCell=firstRow.querySelector('td:nth-child(2)')||firstRow.querySelector('td');if(!targetCell){R.error='클릭할 셀 없음';R.ok=false;return JSON.stringify(R);}R.clickedText=targetCell.innerText?.trim().substring(0,30);R.urlBefore=window.location.href;targetCell.click();await w(500);if(window.location.href===R.urlBefore){ const link=firstRow.querySelector('a[href]'); if(link){link.click();await w(500);}}let waited=0;while(window.location.href===R.urlBefore&&waited<5000){await w(300);waited+=300;}R.urlAfter=window.location.href;R.urlChanged=R.urlAfter!==R.urlBefore;if(!R.urlChanged){R.warn='행 클릭 후 URL 변경 없음 - 모달 또는 인라인 상세일 수 있음';R.ok=true;return JSON.stringify(R);}R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "CLICK_ROW" + }, + { + "id": 5, + "name": "[인사관리 > 사원관리] 상세 페이지 데이터 검증", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'VERIFY_DETAIL'};await w(1500);R.currentUrl=window.location.href;const idPattern=new RegExp('[/][0-9a-f]{8,}|[/][0-9]+[?]|[/][0-9]+$');R.hasIdInUrl=idPattern.test(R.currentUrl);R.hasViewMode=R.currentUrl.includes('mode=view')||R.currentUrl.includes('mode=edit');const captured=window.__CAPTURED__;R.hasCapturedData=!!captured;if(captured&&captured.firstRowTexts){ const pageText=document.body.innerText; let matchCount=0; const checks=[]; for(const t of captured.firstRowTexts){ if(t.length>=2){ const found=pageText.includes(t); if(found)matchCount++; checks.push({text:t.substring(0,20),found}); } } R.dataChecks=checks.slice(0,5); R.matchCount=matchCount; R.totalChecked=checks.length; R.dataMatch=matchCount>0;}const tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button,button[data-state]');R.tabCount=tabs.length;R.tabLabels=Array.from(tabs).slice(0,5).map(t=>t.innerText?.trim().substring(0,20));const inputs=document.querySelectorAll('input:not([type=\"hidden\"]),textarea,select');R.inputCount=inputs.length;R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "VERIFY_DETAIL" + }, + { + "id": 6, + "name": "[인사관리 > 사원관리] 목록으로 복귀", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'GO_BACK'};R.detailUrl=window.location.href;const captured=window.__CAPTURED__;const listUrl=captured?.listUrl||'';const listBtn=Array.from(document.querySelectorAll('button,a')).find(b=>{ const txt=b.innerText?.trim()||''; return /목록|리스트|뒤로|Back|List/i.test(txt)&&b.offsetParent!==null;});if(listBtn){ R.method='목록 버튼 클릭'; listBtn.click(); await w(2000);}else{ R.method='history.back()'; window.history.back(); await w(2000);}R.returnedUrl=window.location.href;R.urlMatches=listUrl?R.returnedUrl===listUrl:!R.returnedUrl.includes('mode=view');if(!R.urlMatches&&listUrl){ R.info='목록 URL과 다름: expected='+listUrl.substring(listUrl.length-40)+' actual='+R.returnedUrl.substring(R.returnedUrl.length-40);}R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "GO_BACK" + }, + { + "id": 7, + "name": "[인사관리 > 사원관리] 목록 무결성 확인", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(1500);const R={phase:'LIST_INTACT'};const captured=window.__CAPTURED__;if(!captured){R.warn='캡처 데이터 없음';R.ok=true;return JSON.stringify(R);}const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);R.currentRowCount=rows.length;R.originalRowCount=captured.rowCount;R.rowCountMatch=R.currentRowCount===R.originalRowCount;if(rows.length>0){ const cells=Array.from(rows[0].querySelectorAll('td')); const cellTexts=cells.map(c=>c.innerText?.trim().substring(0,50)).filter(t=>t.length>0); R.currentFirstRow=cellTexts.slice(0,3); R.originalFirstRow=(captured.firstRowTexts||[]).slice(0,3); let textMatch=0; for(const t of R.originalFirstRow){ if(cellTexts.some(c=>c.includes(t)||t.includes(c)))textMatch++; } R.firstRowMatch=textMatch>0;}else{ R.firstRowMatch=false; R.warn='목록 복귀 후 행 없음';}R.intact=R.rowCountMatch&&R.firstRowMatch;if(!R.intact&&!R.rowCountMatch)R.info='행 수 변경: '+R.originalRowCount+'→'+R.currentRowCount;R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "LIST_INTACT" + }, + { + "id": 8, + "name": "[게시판 > 자유게시판] 메뉴 이동", + "action": "menu_navigate", + "level1": "게시판", + "level2": "자유게시판", + "timeout": 10000 + }, + { + "id": 9, + "name": "[게시판 > 자유게시판] 페이지 로드 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 10, + "name": "[게시판 > 자유게시판] 테이블 로드 대기", + "action": "wait_for_table", + "timeout": 5000 + }, + { + "id": 11, + "name": "[게시판 > 자유게시판] 테이블 상태 캡처", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CAPTURE'};const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);R.rowCount=rows.length;if(rows.length===0){R.warn='테이블에 데이터 없음 - 상세 조회 불가';R.ok=true;return JSON.stringify(R);}const firstRow=rows[0];const cells=Array.from(firstRow.querySelectorAll('td'));const cellTexts=cells.map(c=>c.innerText?.trim().substring(0,50)).filter(t=>t.length>0);R.firstRowTexts=cellTexts;const style=window.getComputedStyle(firstRow);R.hasCursor=style.cursor==='pointer';R.listUrl=window.location.href;window.__CAPTURED__={rowCount:rows.length,firstRowTexts:cellTexts,listUrl:window.location.href};R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "CAPTURE" + }, + { + "id": 12, + "name": "[게시판 > 자유게시판] 첫 행 클릭 → 상세 이동", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CLICK_ROW'};const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);if(rows.length===0){R.error='행 없음';R.ok=false;return JSON.stringify(R);}const firstRow=rows[0];const targetCell=firstRow.querySelector('td:nth-child(2)')||firstRow.querySelector('td');if(!targetCell){R.error='클릭할 셀 없음';R.ok=false;return JSON.stringify(R);}R.clickedText=targetCell.innerText?.trim().substring(0,30);R.urlBefore=window.location.href;targetCell.click();await w(500);if(window.location.href===R.urlBefore){ const link=firstRow.querySelector('a[href]'); if(link){link.click();await w(500);}}let waited=0;while(window.location.href===R.urlBefore&&waited<5000){await w(300);waited+=300;}R.urlAfter=window.location.href;R.urlChanged=R.urlAfter!==R.urlBefore;if(!R.urlChanged){R.warn='행 클릭 후 URL 변경 없음 - 모달 또는 인라인 상세일 수 있음';R.ok=true;return JSON.stringify(R);}R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "CLICK_ROW" + }, + { + "id": 13, + "name": "[게시판 > 자유게시판] 상세 페이지 데이터 검증", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'VERIFY_DETAIL'};await w(1500);R.currentUrl=window.location.href;const idPattern=new RegExp('[/][0-9a-f]{8,}|[/][0-9]+[?]|[/][0-9]+$');R.hasIdInUrl=idPattern.test(R.currentUrl);R.hasViewMode=R.currentUrl.includes('mode=view')||R.currentUrl.includes('mode=edit');const captured=window.__CAPTURED__;R.hasCapturedData=!!captured;if(captured&&captured.firstRowTexts){ const pageText=document.body.innerText; let matchCount=0; const checks=[]; for(const t of captured.firstRowTexts){ if(t.length>=2){ const found=pageText.includes(t); if(found)matchCount++; checks.push({text:t.substring(0,20),found}); } } R.dataChecks=checks.slice(0,5); R.matchCount=matchCount; R.totalChecked=checks.length; R.dataMatch=matchCount>0;}const tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button,button[data-state]');R.tabCount=tabs.length;R.tabLabels=Array.from(tabs).slice(0,5).map(t=>t.innerText?.trim().substring(0,20));const inputs=document.querySelectorAll('input:not([type=\"hidden\"]),textarea,select');R.inputCount=inputs.length;R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "VERIFY_DETAIL" + }, + { + "id": 14, + "name": "[게시판 > 자유게시판] 목록으로 복귀", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'GO_BACK'};R.detailUrl=window.location.href;const captured=window.__CAPTURED__;const listUrl=captured?.listUrl||'';const listBtn=Array.from(document.querySelectorAll('button,a')).find(b=>{ const txt=b.innerText?.trim()||''; return /목록|리스트|뒤로|Back|List/i.test(txt)&&b.offsetParent!==null;});if(listBtn){ R.method='목록 버튼 클릭'; listBtn.click(); await w(2000);}else{ R.method='history.back()'; window.history.back(); await w(2000);}R.returnedUrl=window.location.href;R.urlMatches=listUrl?R.returnedUrl===listUrl:!R.returnedUrl.includes('mode=view');if(!R.urlMatches&&listUrl){ R.info='목록 URL과 다름: expected='+listUrl.substring(listUrl.length-40)+' actual='+R.returnedUrl.substring(R.returnedUrl.length-40);}R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "GO_BACK" + }, + { + "id": 15, + "name": "[게시판 > 자유게시판] 목록 무결성 확인", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(1500);const R={phase:'LIST_INTACT'};const captured=window.__CAPTURED__;if(!captured){R.warn='캡처 데이터 없음';R.ok=true;return JSON.stringify(R);}const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);R.currentRowCount=rows.length;R.originalRowCount=captured.rowCount;R.rowCountMatch=R.currentRowCount===R.originalRowCount;if(rows.length>0){ const cells=Array.from(rows[0].querySelectorAll('td')); const cellTexts=cells.map(c=>c.innerText?.trim().substring(0,50)).filter(t=>t.length>0); R.currentFirstRow=cellTexts.slice(0,3); R.originalFirstRow=(captured.firstRowTexts||[]).slice(0,3); let textMatch=0; for(const t of R.originalFirstRow){ if(cellTexts.some(c=>c.includes(t)||t.includes(c)))textMatch++; } R.firstRowMatch=textMatch>0;}else{ R.firstRowMatch=false; R.warn='목록 복귀 후 행 없음';}R.intact=R.rowCountMatch&&R.firstRowMatch;if(!R.intact&&!R.rowCountMatch)R.info='행 수 변경: '+R.originalRowCount+'→'+R.currentRowCount;R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "LIST_INTACT" + } + ] +} \ No newline at end of file diff --git a/detail-roundtrip-sales.json b/detail-roundtrip-sales.json new file mode 100644 index 0000000..02aae07 --- /dev/null +++ b/detail-roundtrip-sales.json @@ -0,0 +1,190 @@ +{ + "id": "detail-roundtrip-sales", + "name": "상세 조회 왕복 검증: 판매", + "version": "1.0.0", + "auth": { + "role": "admin" + }, + "menuNavigation": { + "level1": "판매관리", + "level2": "거래처관리" + }, + "screenshotPolicy": { + "captureOnFail": true, + "captureOnPass": false + }, + "steps": [ + { + "id": 1, + "name": "[판매관리 > 거래처관리] 페이지 로드 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 2, + "name": "[판매관리 > 거래처관리] 테이블 로드 대기", + "action": "wait_for_table", + "timeout": 5000 + }, + { + "id": 3, + "name": "[판매관리 > 거래처관리] 테이블 상태 캡처", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CAPTURE'};const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);R.rowCount=rows.length;if(rows.length===0){R.warn='테이블에 데이터 없음 - 상세 조회 불가';R.ok=true;return JSON.stringify(R);}const firstRow=rows[0];const cells=Array.from(firstRow.querySelectorAll('td'));const cellTexts=cells.map(c=>c.innerText?.trim().substring(0,50)).filter(t=>t.length>0);R.firstRowTexts=cellTexts;const style=window.getComputedStyle(firstRow);R.hasCursor=style.cursor==='pointer';R.listUrl=window.location.href;window.__CAPTURED__={rowCount:rows.length,firstRowTexts:cellTexts,listUrl:window.location.href};R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "CAPTURE" + }, + { + "id": 4, + "name": "[판매관리 > 거래처관리] 첫 행 클릭 → 상세 이동", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CLICK_ROW'};const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);if(rows.length===0){R.error='행 없음';R.ok=false;return JSON.stringify(R);}const firstRow=rows[0];const targetCell=firstRow.querySelector('td:nth-child(2)')||firstRow.querySelector('td');if(!targetCell){R.error='클릭할 셀 없음';R.ok=false;return JSON.stringify(R);}R.clickedText=targetCell.innerText?.trim().substring(0,30);R.urlBefore=window.location.href;targetCell.click();await w(500);if(window.location.href===R.urlBefore){ const link=firstRow.querySelector('a[href]'); if(link){link.click();await w(500);}}let waited=0;while(window.location.href===R.urlBefore&&waited<5000){await w(300);waited+=300;}R.urlAfter=window.location.href;R.urlChanged=R.urlAfter!==R.urlBefore;if(!R.urlChanged){R.warn='행 클릭 후 URL 변경 없음 - 모달 또는 인라인 상세일 수 있음';R.ok=true;return JSON.stringify(R);}R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "CLICK_ROW" + }, + { + "id": 5, + "name": "[판매관리 > 거래처관리] 상세 페이지 데이터 검증", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'VERIFY_DETAIL'};await w(1500);R.currentUrl=window.location.href;const idPattern=new RegExp('[/][0-9a-f]{8,}|[/][0-9]+[?]|[/][0-9]+$');R.hasIdInUrl=idPattern.test(R.currentUrl);R.hasViewMode=R.currentUrl.includes('mode=view')||R.currentUrl.includes('mode=edit');const captured=window.__CAPTURED__;R.hasCapturedData=!!captured;if(captured&&captured.firstRowTexts){ const pageText=document.body.innerText; let matchCount=0; const checks=[]; for(const t of captured.firstRowTexts){ if(t.length>=2){ const found=pageText.includes(t); if(found)matchCount++; checks.push({text:t.substring(0,20),found}); } } R.dataChecks=checks.slice(0,5); R.matchCount=matchCount; R.totalChecked=checks.length; R.dataMatch=matchCount>0;}const tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button,button[data-state]');R.tabCount=tabs.length;R.tabLabels=Array.from(tabs).slice(0,5).map(t=>t.innerText?.trim().substring(0,20));const inputs=document.querySelectorAll('input:not([type=\"hidden\"]),textarea,select');R.inputCount=inputs.length;R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "VERIFY_DETAIL" + }, + { + "id": 6, + "name": "[판매관리 > 거래처관리] 목록으로 복귀", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'GO_BACK'};R.detailUrl=window.location.href;const captured=window.__CAPTURED__;const listUrl=captured?.listUrl||'';const listBtn=Array.from(document.querySelectorAll('button,a')).find(b=>{ const txt=b.innerText?.trim()||''; return /목록|리스트|뒤로|Back|List/i.test(txt)&&b.offsetParent!==null;});if(listBtn){ R.method='목록 버튼 클릭'; listBtn.click(); await w(2000);}else{ R.method='history.back()'; window.history.back(); await w(2000);}R.returnedUrl=window.location.href;R.urlMatches=listUrl?R.returnedUrl===listUrl:!R.returnedUrl.includes('mode=view');if(!R.urlMatches&&listUrl){ R.info='목록 URL과 다름: expected='+listUrl.substring(listUrl.length-40)+' actual='+R.returnedUrl.substring(R.returnedUrl.length-40);}R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "GO_BACK" + }, + { + "id": 7, + "name": "[판매관리 > 거래처관리] 목록 무결성 확인", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(1500);const R={phase:'LIST_INTACT'};const captured=window.__CAPTURED__;if(!captured){R.warn='캡처 데이터 없음';R.ok=true;return JSON.stringify(R);}const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);R.currentRowCount=rows.length;R.originalRowCount=captured.rowCount;R.rowCountMatch=R.currentRowCount===R.originalRowCount;if(rows.length>0){ const cells=Array.from(rows[0].querySelectorAll('td')); const cellTexts=cells.map(c=>c.innerText?.trim().substring(0,50)).filter(t=>t.length>0); R.currentFirstRow=cellTexts.slice(0,3); R.originalFirstRow=(captured.firstRowTexts||[]).slice(0,3); let textMatch=0; for(const t of R.originalFirstRow){ if(cellTexts.some(c=>c.includes(t)||t.includes(c)))textMatch++; } R.firstRowMatch=textMatch>0;}else{ R.firstRowMatch=false; R.warn='목록 복귀 후 행 없음';}R.intact=R.rowCountMatch&&R.firstRowMatch;if(!R.intact&&!R.rowCountMatch)R.info='행 수 변경: '+R.originalRowCount+'→'+R.currentRowCount;R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "LIST_INTACT" + }, + { + "id": 8, + "name": "[판매관리 > 수주관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "판매관리", + "level2": "수주관리", + "timeout": 10000 + }, + { + "id": 9, + "name": "[판매관리 > 수주관리] 페이지 로드 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 10, + "name": "[판매관리 > 수주관리] 테이블 로드 대기", + "action": "wait_for_table", + "timeout": 5000 + }, + { + "id": 11, + "name": "[판매관리 > 수주관리] 테이블 상태 캡처", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CAPTURE'};const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);R.rowCount=rows.length;if(rows.length===0){R.warn='테이블에 데이터 없음 - 상세 조회 불가';R.ok=true;return JSON.stringify(R);}const firstRow=rows[0];const cells=Array.from(firstRow.querySelectorAll('td'));const cellTexts=cells.map(c=>c.innerText?.trim().substring(0,50)).filter(t=>t.length>0);R.firstRowTexts=cellTexts;const style=window.getComputedStyle(firstRow);R.hasCursor=style.cursor==='pointer';R.listUrl=window.location.href;window.__CAPTURED__={rowCount:rows.length,firstRowTexts:cellTexts,listUrl:window.location.href};R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "CAPTURE" + }, + { + "id": 12, + "name": "[판매관리 > 수주관리] 첫 행 클릭 → 상세 이동", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CLICK_ROW'};const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);if(rows.length===0){R.error='행 없음';R.ok=false;return JSON.stringify(R);}const firstRow=rows[0];const targetCell=firstRow.querySelector('td:nth-child(2)')||firstRow.querySelector('td');if(!targetCell){R.error='클릭할 셀 없음';R.ok=false;return JSON.stringify(R);}R.clickedText=targetCell.innerText?.trim().substring(0,30);R.urlBefore=window.location.href;targetCell.click();await w(500);if(window.location.href===R.urlBefore){ const link=firstRow.querySelector('a[href]'); if(link){link.click();await w(500);}}let waited=0;while(window.location.href===R.urlBefore&&waited<5000){await w(300);waited+=300;}R.urlAfter=window.location.href;R.urlChanged=R.urlAfter!==R.urlBefore;if(!R.urlChanged){R.warn='행 클릭 후 URL 변경 없음 - 모달 또는 인라인 상세일 수 있음';R.ok=true;return JSON.stringify(R);}R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "CLICK_ROW" + }, + { + "id": 13, + "name": "[판매관리 > 수주관리] 상세 페이지 데이터 검증", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'VERIFY_DETAIL'};await w(1500);R.currentUrl=window.location.href;const idPattern=new RegExp('[/][0-9a-f]{8,}|[/][0-9]+[?]|[/][0-9]+$');R.hasIdInUrl=idPattern.test(R.currentUrl);R.hasViewMode=R.currentUrl.includes('mode=view')||R.currentUrl.includes('mode=edit');const captured=window.__CAPTURED__;R.hasCapturedData=!!captured;if(captured&&captured.firstRowTexts){ const pageText=document.body.innerText; let matchCount=0; const checks=[]; for(const t of captured.firstRowTexts){ if(t.length>=2){ const found=pageText.includes(t); if(found)matchCount++; checks.push({text:t.substring(0,20),found}); } } R.dataChecks=checks.slice(0,5); R.matchCount=matchCount; R.totalChecked=checks.length; R.dataMatch=matchCount>0;}const tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button,button[data-state]');R.tabCount=tabs.length;R.tabLabels=Array.from(tabs).slice(0,5).map(t=>t.innerText?.trim().substring(0,20));const inputs=document.querySelectorAll('input:not([type=\"hidden\"]),textarea,select');R.inputCount=inputs.length;R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "VERIFY_DETAIL" + }, + { + "id": 14, + "name": "[판매관리 > 수주관리] 목록으로 복귀", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'GO_BACK'};R.detailUrl=window.location.href;const captured=window.__CAPTURED__;const listUrl=captured?.listUrl||'';const listBtn=Array.from(document.querySelectorAll('button,a')).find(b=>{ const txt=b.innerText?.trim()||''; return /목록|리스트|뒤로|Back|List/i.test(txt)&&b.offsetParent!==null;});if(listBtn){ R.method='목록 버튼 클릭'; listBtn.click(); await w(2000);}else{ R.method='history.back()'; window.history.back(); await w(2000);}R.returnedUrl=window.location.href;R.urlMatches=listUrl?R.returnedUrl===listUrl:!R.returnedUrl.includes('mode=view');if(!R.urlMatches&&listUrl){ R.info='목록 URL과 다름: expected='+listUrl.substring(listUrl.length-40)+' actual='+R.returnedUrl.substring(R.returnedUrl.length-40);}R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "GO_BACK" + }, + { + "id": 15, + "name": "[판매관리 > 수주관리] 목록 무결성 확인", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(1500);const R={phase:'LIST_INTACT'};const captured=window.__CAPTURED__;if(!captured){R.warn='캡처 데이터 없음';R.ok=true;return JSON.stringify(R);}const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);R.currentRowCount=rows.length;R.originalRowCount=captured.rowCount;R.rowCountMatch=R.currentRowCount===R.originalRowCount;if(rows.length>0){ const cells=Array.from(rows[0].querySelectorAll('td')); const cellTexts=cells.map(c=>c.innerText?.trim().substring(0,50)).filter(t=>t.length>0); R.currentFirstRow=cellTexts.slice(0,3); R.originalFirstRow=(captured.firstRowTexts||[]).slice(0,3); let textMatch=0; for(const t of R.originalFirstRow){ if(cellTexts.some(c=>c.includes(t)||t.includes(c)))textMatch++; } R.firstRowMatch=textMatch>0;}else{ R.firstRowMatch=false; R.warn='목록 복귀 후 행 없음';}R.intact=R.rowCountMatch&&R.firstRowMatch;if(!R.intact&&!R.rowCountMatch)R.info='행 수 변경: '+R.originalRowCount+'→'+R.currentRowCount;R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "LIST_INTACT" + }, + { + "id": 16, + "name": "[판매관리 > 견적관리] 메뉴 이동", + "action": "menu_navigate", + "level1": "판매관리", + "level2": "견적관리", + "timeout": 10000 + }, + { + "id": 17, + "name": "[판매관리 > 견적관리] 페이지 로드 대기", + "action": "wait", + "timeout": 3000 + }, + { + "id": 18, + "name": "[판매관리 > 견적관리] 테이블 로드 대기", + "action": "wait_for_table", + "timeout": 5000 + }, + { + "id": 19, + "name": "[판매관리 > 견적관리] 테이블 상태 캡처", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CAPTURE'};const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);R.rowCount=rows.length;if(rows.length===0){R.warn='테이블에 데이터 없음 - 상세 조회 불가';R.ok=true;return JSON.stringify(R);}const firstRow=rows[0];const cells=Array.from(firstRow.querySelectorAll('td'));const cellTexts=cells.map(c=>c.innerText?.trim().substring(0,50)).filter(t=>t.length>0);R.firstRowTexts=cellTexts;const style=window.getComputedStyle(firstRow);R.hasCursor=style.cursor==='pointer';R.listUrl=window.location.href;window.__CAPTURED__={rowCount:rows.length,firstRowTexts:cellTexts,listUrl:window.location.href};R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "CAPTURE" + }, + { + "id": 20, + "name": "[판매관리 > 견적관리] 첫 행 클릭 → 상세 이동", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'CLICK_ROW'};const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);if(rows.length===0){R.error='행 없음';R.ok=false;return JSON.stringify(R);}const firstRow=rows[0];const targetCell=firstRow.querySelector('td:nth-child(2)')||firstRow.querySelector('td');if(!targetCell){R.error='클릭할 셀 없음';R.ok=false;return JSON.stringify(R);}R.clickedText=targetCell.innerText?.trim().substring(0,30);R.urlBefore=window.location.href;targetCell.click();await w(500);if(window.location.href===R.urlBefore){ const link=firstRow.querySelector('a[href]'); if(link){link.click();await w(500);}}let waited=0;while(window.location.href===R.urlBefore&&waited<5000){await w(300);waited+=300;}R.urlAfter=window.location.href;R.urlChanged=R.urlAfter!==R.urlBefore;if(!R.urlChanged){R.warn='행 클릭 후 URL 변경 없음 - 모달 또는 인라인 상세일 수 있음';R.ok=true;return JSON.stringify(R);}R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "CLICK_ROW" + }, + { + "id": 21, + "name": "[판매관리 > 견적관리] 상세 페이지 데이터 검증", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'VERIFY_DETAIL'};await w(1500);R.currentUrl=window.location.href;const idPattern=new RegExp('[/][0-9a-f]{8,}|[/][0-9]+[?]|[/][0-9]+$');R.hasIdInUrl=idPattern.test(R.currentUrl);R.hasViewMode=R.currentUrl.includes('mode=view')||R.currentUrl.includes('mode=edit');const captured=window.__CAPTURED__;R.hasCapturedData=!!captured;if(captured&&captured.firstRowTexts){ const pageText=document.body.innerText; let matchCount=0; const checks=[]; for(const t of captured.firstRowTexts){ if(t.length>=2){ const found=pageText.includes(t); if(found)matchCount++; checks.push({text:t.substring(0,20),found}); } } R.dataChecks=checks.slice(0,5); R.matchCount=matchCount; R.totalChecked=checks.length; R.dataMatch=matchCount>0;}const tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button,button[data-state]');R.tabCount=tabs.length;R.tabLabels=Array.from(tabs).slice(0,5).map(t=>t.innerText?.trim().substring(0,20));const inputs=document.querySelectorAll('input:not([type=\"hidden\"]),textarea,select');R.inputCount=inputs.length;R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "VERIFY_DETAIL" + }, + { + "id": 22, + "name": "[판매관리 > 견적관리] 목록으로 복귀", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));const R={phase:'GO_BACK'};R.detailUrl=window.location.href;const captured=window.__CAPTURED__;const listUrl=captured?.listUrl||'';const listBtn=Array.from(document.querySelectorAll('button,a')).find(b=>{ const txt=b.innerText?.trim()||''; return /목록|리스트|뒤로|Back|List/i.test(txt)&&b.offsetParent!==null;});if(listBtn){ R.method='목록 버튼 클릭'; listBtn.click(); await w(2000);}else{ R.method='history.back()'; window.history.back(); await w(2000);}R.returnedUrl=window.location.href;R.urlMatches=listUrl?R.returnedUrl===listUrl:!R.returnedUrl.includes('mode=view');if(!R.urlMatches&&listUrl){ R.info='목록 URL과 다름: expected='+listUrl.substring(listUrl.length-40)+' actual='+R.returnedUrl.substring(R.returnedUrl.length-40);}R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "GO_BACK" + }, + { + "id": 23, + "name": "[판매관리 > 견적관리] 목록 무결성 확인", + "action": "evaluate", + "script": "(async()=>{const w=ms=>new Promise(r=>setTimeout(r,ms));await w(1500);const R={phase:'LIST_INTACT'};const captured=window.__CAPTURED__;if(!captured){R.warn='캡처 데이터 없음';R.ok=true;return JSON.stringify(R);}const rows=Array.from(document.querySelectorAll('table tbody tr')).filter(r=>r.offsetParent!==null);R.currentRowCount=rows.length;R.originalRowCount=captured.rowCount;R.rowCountMatch=R.currentRowCount===R.originalRowCount;if(rows.length>0){ const cells=Array.from(rows[0].querySelectorAll('td')); const cellTexts=cells.map(c=>c.innerText?.trim().substring(0,50)).filter(t=>t.length>0); R.currentFirstRow=cellTexts.slice(0,3); R.originalFirstRow=(captured.firstRowTexts||[]).slice(0,3); let textMatch=0; for(const t of R.originalFirstRow){ if(cellTexts.some(c=>c.includes(t)||t.includes(c)))textMatch++; } R.firstRowMatch=textMatch>0;}else{ R.firstRowMatch=false; R.warn='목록 복귀 후 행 없음';}R.intact=R.rowCountMatch&&R.firstRowMatch;if(!R.intact&&!R.rowCountMatch)R.info='행 수 변경: '+R.originalRowCount+'→'+R.currentRowCount;R.ok=true;return JSON.stringify(R);})()", + "timeout": 10000, + "phase": "LIST_INTACT" + } + ] +} \ No newline at end of file diff --git a/search-function-audit-acc1.json b/search-function-audit-acc1.json new file mode 100644 index 0000000..40b3d50 --- /dev/null +++ b/search-function-audit-acc1.json @@ -0,0 +1,156 @@ +{ + "enabled": true, + "id": "search-function-audit-acc1", + "name": "검색 기능 감사: 회계관리 (1/6)", + "description": "회계관리 모듈 전반의 검색/필터 기능 동작 검증", + "baseUrl": "https://dev.codebridge-x.com", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": [ + "error", + "fail", + "timeout" + ] + }, + "menuNavigation": { + "level1": "회계관리", + "level2": "거래처관리", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "[검색감사] 거래처관리 페이지 대기", + "action": "wait", + "duration": 2000 + }, + { + "id": 2, + "name": "[검색감사] 거래처관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 3, + "name": "▶ 회계관리 > 입금관리 이동", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "입금관리" + }, + { + "id": 4, + "name": "[검색감사] 입금관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 5, + "name": "▶ 회계관리 > 출금관리 이동", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "출금관리" + }, + { + "id": 6, + "name": "[검색감사] 출금관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 7, + "name": "▶ 회계관리 > 어음관리 이동", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "어음관리" + }, + { + "id": 8, + "name": "[검색감사] 어음관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 9, + "name": "▶ 회계관리 > 악성채권추심관리 이동", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "악성채권추심관리" + }, + { + "id": 10, + "name": "[검색감사] 악성채권추심관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 11, + "name": "▶ 회계관리 > 입출금계좌조회 이동", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "입출금계좌조회" + }, + { + "id": 12, + "name": "[검색감사] 입출금계좌조회", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 13, + "name": "▶ 회계관리 > 카드내역조회 이동", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "카드내역조회" + }, + { + "id": 14, + "name": "[검색감사] 카드내역조회", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 15, + "name": "▶ 회계관리 > 매입관리 이동", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "매입관리" + }, + { + "id": 16, + "name": "[검색감사] 매입관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 17, + "name": "▶ 회계관리 > 매출관리 이동", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "매출관리" + }, + { + "id": 18, + "name": "[검색감사] 매출관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 19, + "name": "▶ 회계관리 > 미수금현황 이동", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "미수금현황" + }, + { + "id": 20, + "name": "[검색감사] 미수금현황", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + } + ] +} \ No newline at end of file diff --git a/search-function-audit-acc2-hr.json b/search-function-audit-acc2-hr.json new file mode 100644 index 0000000..1489394 --- /dev/null +++ b/search-function-audit-acc2-hr.json @@ -0,0 +1,156 @@ +{ + "enabled": true, + "id": "search-function-audit-acc2-hr", + "name": "검색 기능 감사: 회계관리2+인사관리 (2/6)", + "description": "회계관리 나머지 + 인사관리 모듈의 검색/필터 기능 동작 검증", + "baseUrl": "https://dev.codebridge-x.com", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": [ + "error", + "fail", + "timeout" + ] + }, + "menuNavigation": { + "level1": "회계관리", + "level2": "지출예상내역서", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "[검색감사] 지출예상내역서 페이지 대기", + "action": "wait", + "duration": 2000 + }, + { + "id": 2, + "name": "[검색감사] 지출예상내역서", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 3, + "name": "▶ 회계관리 > 결제내역 이동", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "결제내역" + }, + { + "id": 4, + "name": "[검색감사] 결제내역", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 5, + "name": "▶ 회계관리 > 거래처원장 이동", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "거래처원장" + }, + { + "id": 6, + "name": "[검색감사] 거래처원장", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 7, + "name": "▶ 인사관리 > 사원관리 이동", + "action": "menu_navigate", + "level1": "인사관리", + "level2": "사원관리" + }, + { + "id": 8, + "name": "[검색감사] 사원관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 9, + "name": "▶ 인사관리 > 부서관리 이동", + "action": "menu_navigate", + "level1": "인사관리", + "level2": "부서관리" + }, + { + "id": 10, + "name": "[검색감사] 부서관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 11, + "name": "▶ 인사관리 > 급여관리 이동", + "action": "menu_navigate", + "level1": "인사관리", + "level2": "급여관리" + }, + { + "id": 12, + "name": "[검색감사] 급여관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 13, + "name": "▶ 인사관리 > 근태관리 이동", + "action": "menu_navigate", + "level1": "인사관리", + "level2": "근태관리" + }, + { + "id": 14, + "name": "[검색감사] 근태관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 15, + "name": "▶ 인사관리 > 근태현황 이동", + "action": "menu_navigate", + "level1": "인사관리", + "level2": "근태현황" + }, + { + "id": 16, + "name": "[검색감사] 근태현황", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 17, + "name": "▶ 인사관리 > 카드관리 이동", + "action": "menu_navigate", + "level1": "인사관리", + "level2": "카드관리" + }, + { + "id": 18, + "name": "[검색감사] 카드관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 19, + "name": "▶ 인사관리 > 휴가관리 이동", + "action": "menu_navigate", + "level1": "인사관리", + "level2": "휴가관리" + }, + { + "id": 20, + "name": "[검색감사] 휴가관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + } + ] +} \ No newline at end of file diff --git a/search-function-audit-boards.json b/search-function-audit-boards.json new file mode 100644 index 0000000..c0be7fd --- /dev/null +++ b/search-function-audit-boards.json @@ -0,0 +1,156 @@ +{ + "enabled": true, + "id": "search-function-audit-boards", + "name": "검색 기능 감사: 게시판/고객센터/설정1 (5/6)", + "description": "게시판, 고객센터, 설정 모듈 전반의 검색/필터 기능 동작 검증", + "baseUrl": "https://dev.codebridge-x.com", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": [ + "error", + "fail", + "timeout" + ] + }, + "menuNavigation": { + "level1": "게시판", + "level2": "자유게시판", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "[검색감사] 자유게시판 페이지 대기", + "action": "wait", + "duration": 2000 + }, + { + "id": 2, + "name": "[검색감사] 자유게시판", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 3, + "name": "▶ 게시판 > 게시판 관리 이동", + "action": "menu_navigate", + "level1": "게시판", + "level2": "게시판 관리" + }, + { + "id": 4, + "name": "[검색감사] 게시판 관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 5, + "name": "▶ 고객센터 > 공지사항 이동", + "action": "menu_navigate", + "level1": "고객센터", + "level2": "공지사항" + }, + { + "id": 6, + "name": "[검색감사] 공지사항", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 7, + "name": "▶ 고객센터 > FAQ 이동", + "action": "menu_navigate", + "level1": "고객센터", + "level2": "FAQ" + }, + { + "id": 8, + "name": "[검색감사] FAQ", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 9, + "name": "▶ 고객센터 > 이벤트 게시판 이동", + "action": "menu_navigate", + "level1": "고객센터", + "level2": "이벤트 게시판" + }, + { + "id": 10, + "name": "[검색감사] 이벤트 게시판", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 11, + "name": "▶ 설정 > 회사정보 이동", + "action": "menu_navigate", + "level1": "설정", + "level2": "회사정보" + }, + { + "id": 12, + "name": "[검색감사] 회사정보", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 13, + "name": "▶ 설정 > 계정정보 이동", + "action": "menu_navigate", + "level1": "설정", + "level2": "계정정보" + }, + { + "id": 14, + "name": "[검색감사] 계정정보", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 15, + "name": "▶ 설정 > 근태설정 이동", + "action": "menu_navigate", + "level1": "설정", + "level2": "근태설정" + }, + { + "id": 16, + "name": "[검색감사] 근태설정", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 17, + "name": "▶ 설정 > 계좌관리 이동", + "action": "menu_navigate", + "level1": "설정", + "level2": "계좌관리" + }, + { + "id": 18, + "name": "[검색감사] 계좌관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 19, + "name": "▶ 설정 > 알림설정 이동", + "action": "menu_navigate", + "level1": "설정", + "level2": "알림설정" + }, + { + "id": 20, + "name": "[검색감사] 알림설정", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + } + ] +} \ No newline at end of file diff --git a/search-function-audit-production.json b/search-function-audit-production.json new file mode 100644 index 0000000..bd38f5b --- /dev/null +++ b/search-function-audit-production.json @@ -0,0 +1,156 @@ +{ + "enabled": true, + "id": "search-function-audit-production", + "name": "검색 기능 감사: 생산/품목/품질/자재 (3/6)", + "description": "생산관리, 품목관리, 품질관리, 자재관리 모듈의 검색/필터 기능 동작 검증", + "baseUrl": "https://dev.codebridge-x.com", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": [ + "error", + "fail", + "timeout" + ] + }, + "menuNavigation": { + "level1": "생산관리", + "level2": "품목관리", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "[검색감사] 품목관리 페이지 대기", + "action": "wait", + "duration": 2000 + }, + { + "id": 2, + "name": "[검색감사] 품목관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 3, + "name": "▶ 생산관리 > 생산 현황판 이동", + "action": "menu_navigate", + "level1": "생산관리", + "level2": "생산 현황판" + }, + { + "id": 4, + "name": "[검색감사] 생산 현황판", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 5, + "name": "▶ 생산관리 > 작업자 화면 이동", + "action": "menu_navigate", + "level1": "생산관리", + "level2": "작업자 화면" + }, + { + "id": 6, + "name": "[검색감사] 작업자 화면", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 7, + "name": "▶ 생산관리 > 작업지시 관리 이동", + "action": "menu_navigate", + "level1": "생산관리", + "level2": "작업지시 관리" + }, + { + "id": 8, + "name": "[검색감사] 작업지시 관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 9, + "name": "▶ 생산관리 > 작업실적 이동", + "action": "menu_navigate", + "level1": "생산관리", + "level2": "작업실적" + }, + { + "id": 10, + "name": "[검색감사] 작업실적", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 11, + "name": "▶ 품목관리 > 품목기준관리 이동", + "action": "menu_navigate", + "level1": "품목관리", + "level2": "품목기준관리" + }, + { + "id": 12, + "name": "[검색감사] 품목기준관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 13, + "name": "▶ 품질관리 > 품질인정심사 시스템 이동", + "action": "menu_navigate", + "level1": "품질관리", + "level2": "품질인정심사 시스템" + }, + { + "id": 14, + "name": "[검색감사] 품질인정심사 시스템", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 15, + "name": "▶ 품질관리 > 제품검사관리 이동", + "action": "menu_navigate", + "level1": "품질관리", + "level2": "제품검사관리" + }, + { + "id": 16, + "name": "[검색감사] 제품검사관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 17, + "name": "▶ 자재관리 > 재고현황 이동", + "action": "menu_navigate", + "level1": "자재관리", + "level2": "재고현황" + }, + { + "id": 18, + "name": "[검색감사] 재고현황", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 19, + "name": "▶ 자재관리 > 입고관리 이동", + "action": "menu_navigate", + "level1": "자재관리", + "level2": "입고관리" + }, + { + "id": 20, + "name": "[검색감사] 입고관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + } + ] +} \ No newline at end of file diff --git a/search-function-audit-sales-approval.json b/search-function-audit-sales-approval.json new file mode 100644 index 0000000..88a2c51 --- /dev/null +++ b/search-function-audit-sales-approval.json @@ -0,0 +1,130 @@ +{ + "enabled": true, + "id": "search-function-audit-sales-approval", + "name": "검색 기능 감사: 판매/출고/결재 (4/6)", + "description": "판매관리, 출고관리, 결재관리 모듈의 검색/필터 기능 동작 검증", + "baseUrl": "https://dev.codebridge-x.com", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": [ + "error", + "fail", + "timeout" + ] + }, + "menuNavigation": { + "level1": "판매관리", + "level2": "거래처관리", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "[검색감사] 거래처관리 페이지 대기", + "action": "wait", + "duration": 2000 + }, + { + "id": 2, + "name": "[검색감사] 거래처관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 3, + "name": "▶ 판매관리 > 수주관리 이동", + "action": "menu_navigate", + "level1": "판매관리", + "level2": "수주관리" + }, + { + "id": 4, + "name": "[검색감사] 수주관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 5, + "name": "▶ 판매관리 > 단가관리 이동", + "action": "menu_navigate", + "level1": "판매관리", + "level2": "단가관리" + }, + { + "id": 6, + "name": "[검색감사] 단가관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 7, + "name": "▶ 판매관리 > 견적관리 이동", + "action": "menu_navigate", + "level1": "판매관리", + "level2": "견적관리" + }, + { + "id": 8, + "name": "[검색감사] 견적관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 9, + "name": "▶ 출고관리 > 출고관리 이동", + "action": "menu_navigate", + "level1": "출고관리", + "level2": "출고관리" + }, + { + "id": 10, + "name": "[검색감사] 출고관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 11, + "name": "▶ 결재관리 > 결재함 이동", + "action": "menu_navigate", + "level1": "결재관리", + "level2": "결재함" + }, + { + "id": 12, + "name": "[검색감사] 결재함", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 13, + "name": "▶ 결재관리 > 기안함 이동", + "action": "menu_navigate", + "level1": "결재관리", + "level2": "기안함" + }, + { + "id": 14, + "name": "[검색감사] 기안함", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 15, + "name": "▶ 결재관리 > 참조함 이동", + "action": "menu_navigate", + "level1": "결재관리", + "level2": "참조함" + }, + { + "id": 16, + "name": "[검색감사] 참조함", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + } + ] +} \ No newline at end of file diff --git a/search-function-audit-settings.json b/search-function-audit-settings.json new file mode 100644 index 0000000..c17ebf7 --- /dev/null +++ b/search-function-audit-settings.json @@ -0,0 +1,117 @@ +{ + "enabled": true, + "id": "search-function-audit-settings", + "name": "검색 기능 감사: 설정2 (6/6)", + "description": "설정 모듈 나머지의 검색/필터 기능 동작 검증", + "baseUrl": "https://dev.codebridge-x.com", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": [ + "error", + "fail", + "timeout" + ] + }, + "menuNavigation": { + "level1": "설정", + "level2": "권한관리", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "[검색감사] 권한관리 페이지 대기", + "action": "wait", + "duration": 2000 + }, + { + "id": 2, + "name": "[검색감사] 권한관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 3, + "name": "▶ 설정 > 팝업관리 이동", + "action": "menu_navigate", + "level1": "설정", + "level2": "팝업관리" + }, + { + "id": 4, + "name": "[검색감사] 팝업관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 5, + "name": "▶ 설정 > 직책관리 이동", + "action": "menu_navigate", + "level1": "설정", + "level2": "직책관리" + }, + { + "id": 6, + "name": "[검색감사] 직책관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 7, + "name": "▶ 설정 > 직급관리 이동", + "action": "menu_navigate", + "level1": "설정", + "level2": "직급관리" + }, + { + "id": 8, + "name": "[검색감사] 직급관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 9, + "name": "▶ 설정 > 구독관리 이동", + "action": "menu_navigate", + "level1": "설정", + "level2": "구독관리" + }, + { + "id": 10, + "name": "[검색감사] 구독관리", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 11, + "name": "▶ 설정 > 휴가정책 이동", + "action": "menu_navigate", + "level1": "설정", + "level2": "휴가정책" + }, + { + "id": 12, + "name": "[검색감사] 휴가정책", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + }, + { + "id": 13, + "name": "▶ 설정 > 근무일정 이동", + "action": "menu_navigate", + "level1": "설정", + "level2": "근무일정" + }, + { + "id": 14, + "name": "[검색감사] 근무일정", + "action": "evaluate", + "script": "(async()=>{const R={p:location.pathname};const ss=['input[type=\"search\"]','input[placeholder*=\"검색\"]','input[placeholder*=\"Search\"]','input[role=\"searchbox\"]','[class*=\"search\"] input'];let si=null;for(const s of ss){si=document.querySelector(s);if(si)break;}if(!si) si=Array.from(document.querySelectorAll('input[type=\"text\"],input:not([type])')).find(i=>/검색|search|조회/i.test(i.placeholder||''));R.hasSearch=!!si;R.searchPlaceholder=si?(si.placeholder||'').substring(0,40):'';R.filters=document.querySelectorAll('button[role=\"combobox\"],select').length;R.tabs=document.querySelectorAll('[role=\"tab\"],[role=\"tablist\"] button').length;const rc=()=>document.querySelectorAll('table tbody tr,[role=\"row\"]').length;R.rows=rc();R.hasTable=R.rows>0||!!document.querySelector('table,[role=\"grid\"]');if(si&&R.rows>0){const ns=Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value')?.set;if(ns)ns.call(si,'zzz_no_match_e2e');else si.value='zzz_no_match_e2e';si.dispatchEvent(new Event('input',{bubbles:true}));si.dispatchEvent(new Event('change',{bubbles:true}));si.dispatchEvent(new KeyboardEvent('keydown',{key:'Enter',keyCode:13,bubbles:true}));const sb=document.querySelector('button[class*=\"search\"],button[aria-label*=\"검색\"]');if(sb)sb.click();await new Promise(w=>setTimeout(w,1500));R.afterRows=rc();R.searchWorked=R.afterRowssetTimeout(w,800));}return JSON.stringify(R);})()" + } + ] +} \ No newline at end of file