- detail-roundtrip: 목록→행클릭→상세페이지→데이터검증→목록복귀→무결성확인 (3그룹, 8페이지) - cross-module: 판매↔회계 거래처, 판매↔생산 품목 교차 데이터 일관성 검증 - api-health: 28개 주요 페이지 API 상태코드/응답시간/에러율 전수 감사 (3그룹) - 전체 7/7 PASS 확인
190 lines
19 KiB
JSON
190 lines
19 KiB
JSON
{
|
|
"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"
|
|
}
|
|
]
|
|
} |