2026-01-30 10:50:38 +09:00
{
"id" : "draft-box" ,
"name" : "기안함 테스트" ,
"screenshotPolicy" : {
"onErrorOnly" : true ,
2026-02-06 06:18:53 +09:00
"captureOn" : [ "error" , "fail" , "timeout" , "404" , "500" , "blocked" ]
2026-01-30 10:50:38 +09:00
} ,
"description" : "결재관리 > 기안함 메뉴의 문서 목록 조회, 검색, 필터, 정렬, 문서 상세, 상신, 삭제 기능 테스트" ,
"baseUrl" : "https://dev.codebridge-x.com" ,
2026-02-06 06:18:53 +09:00
"selectors" : {
"sidebar" : "nav, aside, [role='navigation'], .sidebar, #sidebar, .sidebar-scroll" ,
"pageTitle" : "h1, h2, [class*='page-title'], [class*='PageTitle'], .text-2xl, .text-xl" ,
"pageDescription" : "[class*='description'], [class*='subtitle'], .text-muted-foreground" ,
"statCards" : "[class*='stat-card'], [class*='StatCard'], [class*='card']:has([class*='stat']), .grid > div:has(.text-2xl)" ,
"statCardItem" : "[class*='stat-card'], [class*='card'], .rounded-lg.border" ,
"searchInput" : "input[type='search'], input[placeholder*='검색'], input[name='search'], [class*='search'] input" ,
"filterSelect" : "select[name*='status'], select[name*='filter'], [class*='filter'] select, button[role='combobox']:has-text('전체'), button[role='combobox']:has-text('상태')" ,
"sortSelect" : "select[name*='sort'], [class*='sort'] select, button[role='combobox']:has-text('최신순'), button[role='combobox']:has-text('정렬')" ,
"dataTable" : "table, [role='table'], [class*='table'], [class*='Table']" ,
"tableHeader" : "thead, [role='rowgroup']:first-child, table tr:first-child" ,
"tableHeaderCell" : "th, [role='columnheader']" ,
"tableBody" : "tbody, [role='rowgroup']:last-child" ,
"tableRow" : "tbody tr, [role='row']" ,
"tableCell" : "td, [role='cell']" ,
"checkbox" : "input[type='checkbox'], [role='checkbox'], button[role='checkbox']" ,
"headerCheckbox" : "thead input[type='checkbox'], thead [role='checkbox'], th input[type='checkbox']" ,
"rowCheckbox" : "tbody input[type='checkbox'], tbody [role='checkbox'], td input[type='checkbox']" ,
"pagination" : "[class*='pagination'], [class*='Pagination'], nav[aria-label*='pagination'], .flex:has(button[aria-label*='page'])" ,
"paginationButton" : "[class*='pagination'] button, nav button, button[aria-label*='page']" ,
"modal" : "[role='dialog'], [aria-modal='true'], [class*='modal'], [class*='Modal'], [class*='Dialog']" ,
"modalCloseBtn" : "[role='dialog'] button[class*='close'], [aria-label='닫기'], [aria-label='Close'], [role='dialog'] button:has(svg)" ,
"createBtn" : "button:has-text('문서 작성'), button:has-text('작성'), button:has(svg[class*='plus']), button:has-text('등록')" ,
"submitBtn" : "button:has-text('상신'), button:has(svg[class*='send'])" ,
"deleteBtn" : "button:has-text('삭제'), button[class*='destructive']:has(svg)" ,
"editBtn" : "button:has-text('수정'), button:has(svg[class*='pencil'])" ,
"copyBtn" : "button:has-text('복제'), button:has(svg[class*='copy'])" ,
"dateRangeSelector" : "[class*='date-range'], [class*='DateRange'], input[type='date'], [class*='calendar']" ,
"badge" : "[class*='badge'], [class*='Badge'], span[class*='rounded-full']" ,
"loadingIndicator" : "[class*='loading'], [class*='spinner'], [class*='Spinner'], [aria-busy='true']" ,
"emptyMessage" : "[class*='empty'], [class*='no-data'], td[colspan]:has-text('데이터'), .text-center:has-text('없습니다')" ,
"toast" : "[class*='toast'], [class*='Toast'], [role='alert']" ,
"actionColumn" : "td:last-child, [class*='action']"
} ,
2026-01-30 10:50:38 +09:00
"testFocus" : {
"primary" : "기안 문서 목록 관리 및 결재 상신 프로세스 검증" ,
"description" : "기안함 목록 표시, 통계 카드, 검색/필터/정렬, 체크박스 선택, 상신/삭제 버튼, 문서 상세 모달, 페이지네이션 동작 확인"
} ,
2026-01-30 21:47:29 +09:00
"navigation" : {
"targetUrl" : "/approval/draft" ,
"urlPattern" : "/approval/draft|/ko/approval/draft" ,
2026-02-06 06:18:53 +09:00
"menuHints" : [ "기안함" , "기안 함" , "결재관리" ]
2026-01-30 21:47:29 +09:00
} ,
2026-01-30 10:50:38 +09:00
"menuNavigation" : {
"level1" : "결재관리" ,
"level2" : "기안함" ,
2026-02-04 21:59:56 +09:00
"expectedUrl" : "/ko/approval/draft" ,
"searchWithinParent" : true ,
"closeOtherMenus" : true
} ,
"auth" : {
"username" : "TestUser5" ,
"password" : "password123!"
2026-01-30 10:50:38 +09:00
} ,
"prerequisites" : {
"authentication" : true ,
"testData" : {
2026-02-06 06:18:53 +09:00
"description" : "결재 문서 데이터가 최소 1개 이상 존재해야 함"
2026-01-30 10:50:38 +09:00
}
} ,
"steps" : [
{
2026-02-06 06:18:53 +09:00
"id" : 0 ,
2026-01-30 16:26:52 +09:00
"name" : "사이드바 메뉴 전체 펼치기" ,
2026-02-06 06:18:53 +09:00
"phase" : "SETUP" ,
2026-01-30 10:50:38 +09:00
"actions" : [
2026-02-06 01:26:59 +09:00
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "document.querySelector('.sidebar-scroll, nav, aside')?.scrollTo({top: 0, behavior: 'instant'})"
2026-02-06 01:26:59 +09:00
} ,
2026-02-06 06:18:53 +09:00
{ "type" : "wait" , "duration" : 300 } ,
2026-02-06 01:26:59 +09:00
{
"type" : "evaluate" ,
2026-02-06 06:18:53 +09:00
"script" : "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click(); 'clicked'"
2026-02-06 01:26:59 +09:00
} ,
2026-02-06 06:18:53 +09:00
{ "type" : "wait" , "duration" : 1500 }
2026-01-30 10:50:38 +09:00
]
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 1 ,
2026-01-30 10:50:38 +09:00
"name" : "2단계 메뉴 진입: 결재관리 > 기안함" ,
2026-02-06 06:18:53 +09:00
"phase" : "SETUP" ,
"critical" : true ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "menu_navigate" ,
"level1" : "결재관리" ,
"level2" : "기안함" ,
"alternativeLevel1" : [ "결재관리" , "결재 관리" , "Approval" , "전자결재" ] ,
"alternativeLevel2" : [ "기안함" , "기안 함" , "Draft" , "내 기안" ]
2026-02-06 01:26:59 +09:00
} ,
2026-02-06 06:18:53 +09:00
{ "type" : "wait" , "duration" : 2000 } ,
2026-01-30 10:50:38 +09:00
{
2026-02-06 06:18:53 +09:00
"type" : "verify_url" ,
"pattern" : "/approval/draft" ,
"timeout" : 5000
2026-02-06 01:26:59 +09:00
}
2026-01-30 10:50:38 +09:00
]
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 2 ,
"name" : "페이지 타이틀 확인" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "verify_element" ,
"selector" : "h1, h2, [class*='page-title'], .text-2xl" ,
"timeout" : 3000
} ,
{
"type" : "evaluate" ,
"script" : "(() => { const title = document.querySelector('h1, h2, [class*=\"page-title\"], .text-2xl'); return title && title.innerText.includes('기안함') ? 'PASS: 기안함 타이틀 확인' : 'FAIL: 타이틀 없음 또는 불일치'; })()"
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 3 ,
"name" : "통계 카드 영역 존재 확인" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const cards = document.querySelectorAll('[class*=\"card\"], .rounded-lg.border, [class*=\"stat\"]'); const statTexts = ['진행', '완료', '반려', '임시']; let found = 0; cards.forEach(c => { if (statTexts.some(t => c.innerText?.includes(t))) found++; }); return found >= 2 ? `PASS: 통계 카드 ${found}개 발견` : 'FAIL: 통계 카드 부족'; })()"
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 4 ,
2026-01-30 10:50:38 +09:00
"name" : "테이블 컬럼 구조 확인" ,
2026-02-06 06:18:53 +09:00
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "verify_element" ,
"selector" : "table, [role='table'], [class*='Table']" ,
"timeout" : 3000
} ,
2026-01-30 10:50:38 +09:00
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const headers = document.querySelectorAll('th, [role=\"columnheader\"]'); const texts = Array.from(headers).map(h => h.innerText?.trim()); const expected = ['번호', '문서번호', '문서유형', '제목', '결재자', '기안일시', '상태']; const found = expected.filter(e => texts.some(t => t?.includes(e))); return found.length >= 4 ? `PASS: 컬럼 ${found.length}개 확인 (${found.join(', ')})` : `FAIL: 컬럼 부족 (발견: ${texts.join(', ')})`; })()"
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 5 ,
"name" : "테이블 데이터 로드 확인" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "wait_for_element" ,
"selector" : "tbody tr, [role='row'], [class*='empty'], [class*='no-data']" ,
"timeout" : 5000
} ,
{
"type" : "evaluate" ,
"script" : "(() => { const rows = document.querySelectorAll('tbody tr, table [role=\"row\"]:not(:first-child)'); const empty = document.querySelector('[class*=\"empty\"], [class*=\"no-data\"], td[colspan]'); if (rows.length > 0) return `PASS: 데이터 ${rows.length}행 로드됨`; if (empty) return 'PASS: 빈 데이터 메시지 표시'; return 'FAIL: 데이터 로드 실패'; })()"
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 6 ,
"name" : "문서번호 형식 확인" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const cells = document.querySelectorAll('tbody td, [role=\"cell\"]'); for (const cell of cells) { const text = cell.innerText?.trim(); if (text && /^[A-Z]{2,}-\\d{4}-\\d+$/.test(text)) return `PASS: 문서번호 형식 확인 (${text})`; if (text && text.includes('-') && /\\d{4}/.test(text)) return `PASS: 문서번호 발견 (${text})`; } return 'WARN: 문서번호 형식 확인 불가 (데이터 없거나 다른 형식)'; })()"
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 7 ,
"name" : "상태 뱃지 표시 확인" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const badges = document.querySelectorAll('[class*=\"badge\"], [class*=\"Badge\"], span[class*=\"rounded\"]'); const statusTexts = ['임시저장', '결재대기', '진행중', '완료', '반려', '대기', '진행']; let found = []; badges.forEach(b => { const t = b.innerText?.trim(); if (statusTexts.some(s => t?.includes(s))) found.push(t); }); return found.length > 0 ? `PASS: 상태 뱃지 발견 (${[...new Set(found)].join(', ')})` : 'WARN: 상태 뱃지 없음 (데이터 없을 수 있음)'; })()"
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 8 ,
"name" : "검색 입력 필드 존재 확인" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "verify_element" ,
"selector" : "input[type='search'], input[placeholder*='검색'], input[name='search'], [class*='search'] input" ,
"timeout" : 3000
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 9 ,
2026-01-30 10:50:38 +09:00
"name" : "검색 기능 테스트" ,
2026-02-06 06:18:53 +09:00
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "fill" ,
"selector" : "input[type='search'], input[placeholder*='검색'], input[name='search'], [class*='search'] input" ,
2026-01-30 10:50:38 +09:00
"value" : "테스트"
} ,
2026-02-06 06:18:53 +09:00
{ "type" : "wait" , "duration" : 1500 } ,
2026-01-30 10:50:38 +09:00
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const input = document.querySelector('input[type=\"search\"], input[placeholder*=\"검색\"]'); return input && input.value === '테스트' ? 'PASS: 검색어 입력 완료' : 'FAIL: 검색어 입력 실패'; })()"
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 10 ,
2026-01-30 10:50:38 +09:00
"name" : "검색어 초기화" ,
2026-02-06 06:18:53 +09:00
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "clear" ,
"selector" : "input[type='search'], input[placeholder*='검색'], input[name='search']"
2026-01-30 10:50:38 +09:00
} ,
2026-02-06 06:18:53 +09:00
{ "type" : "wait" , "duration" : 1000 }
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 11 ,
"name" : "필터 드롭다운 존재 확인" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const selects = document.querySelectorAll('select, button[role=\"combobox\"], [class*=\"select\"] button'); for (const s of selects) { const text = s.innerText || s.value || ''; if (['전체', '상태', '임시저장', '결재대기'].some(t => text.includes(t))) return `PASS: 필터 드롭다운 발견 (${text.substring(0,20)})`; } return 'WARN: 필터 드롭다운 미발견 (다른 UI일 수 있음)'; })()"
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 12 ,
"name" : "정렬 드롭다운 존재 확인" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const selects = document.querySelectorAll('select, button[role=\"combobox\"], [class*=\"select\"] button'); for (const s of selects) { const text = s.innerText || s.value || ''; if (['최신순', '오래된순', '정렬', '제목'].some(t => text.includes(t))) return `PASS: 정렬 드롭다운 발견 (${text.substring(0,20)})`; } return 'WARN: 정렬 드롭다운 미발견 (다른 UI일 수 있음)'; })()"
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 13 ,
"name" : "체크박스 존재 확인 (헤더)" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const headerCb = document.querySelector('thead input[type=\"checkbox\"], thead [role=\"checkbox\"], th input[type=\"checkbox\"], th button[role=\"checkbox\"]'); return headerCb ? 'PASS: 헤더 체크박스 발견' : 'WARN: 헤더 체크박스 미발견'; })()"
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 14 ,
"name" : "체크박스 존재 확인 (행)" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const rowCbs = document.querySelectorAll('tbody input[type=\"checkbox\"], tbody [role=\"checkbox\"], td input[type=\"checkbox\"], td button[role=\"checkbox\"]'); return rowCbs.length > 0 ? `PASS: 행 체크박스 ${rowCbs.length}개 발견` : 'WARN: 행 체크박스 미발견 (데이터 없을 수 있음)'; })()"
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 15 ,
"name" : "첫 번째 행 체크박스 클릭" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const cb = document.querySelector('tbody input[type=\"checkbox\"], tbody [role=\"checkbox\"], tbody button[role=\"checkbox\"]'); if (cb) { cb.click(); return 'PASS: 체크박스 클릭'; } return 'SKIP: 체크박스 없음'; })()"
2026-01-30 10:50:38 +09:00
} ,
2026-02-06 06:18:53 +09:00
{ "type" : "wait" , "duration" : 500 }
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 16 ,
"name" : "선택 시 액션 버튼 표시 확인" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const btns = document.querySelectorAll('button'); let found = []; btns.forEach(b => { const t = b.innerText?.trim(); if (['상신', '삭제'].some(a => t?.includes(a))) found.push(t); }); return found.length > 0 ? `PASS: 액션 버튼 발견 (${found.join(', ')})` : 'WARN: 액션 버튼 미표시 (선택 상태 또는 권한에 따라 다를 수 있음)'; })()"
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 17 ,
2026-01-30 10:50:38 +09:00
"name" : "체크박스 해제" ,
2026-02-06 06:18:53 +09:00
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const cb = document.querySelector('tbody input[type=\"checkbox\"]:checked, tbody [role=\"checkbox\"][data-state=\"checked\"], tbody button[role=\"checkbox\"][aria-checked=\"true\"]'); if (cb) { cb.click(); return 'PASS: 체크박스 해제'; } return 'SKIP: 선택된 체크박스 없음'; })()"
} ,
{ "type" : "wait" , "duration" : 300 }
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 18 ,
"name" : "문서 작성 버튼 존재 확인" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const btns = document.querySelectorAll('button, a'); for (const b of btns) { const t = b.innerText?.trim(); if (t && ['문서 작성', '작성', '새 문서', '등록'].some(k => t.includes(k))) return `PASS: 문서 작성 버튼 발견 (${t})`; } return 'WARN: 문서 작성 버튼 미발견'; })()"
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 19 ,
"name" : "날짜 범위 선택기 존재 확인" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const dateInputs = document.querySelectorAll('input[type=\"date\"], [class*=\"date\"], [class*=\"calendar\"], button:has-text(\"2025\"), button:has-text(\"2026\")'); return dateInputs.length > 0 ? `PASS: 날짜 선택기 ${dateInputs.length}개 발견` : 'WARN: 날짜 선택기 미발견'; })()"
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 20 ,
"name" : "페이지네이션 존재 확인" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const pag = document.querySelector('[class*=\"pagination\"], [class*=\"Pagination\"], nav[aria-label*=\"pagination\"], .flex:has(button[aria-label*=\"page\"])'); if (pag) return 'PASS: 페이지네이션 발견'; const pageInfo = document.body.innerText.match(/(\\d+)\\s*[/~-]\\s*(\\d+)\\s*(페이지|page)/i); if (pageInfo) return `PASS: 페이지 정보 발견 (${pageInfo[0]})`; return 'WARN: 페이지네이션 미발견 (1페이지만 있을 수 있음)'; })()"
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 21 ,
"name" : "테이블 행 클릭 - 문서 상세 모달 열기" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const row = document.querySelector('tbody tr:not(:has(td[colspan])), table [role=\"row\"]:not(:first-child)'); if (row) { row.click(); return 'PASS: 테이블 행 클릭'; } return 'SKIP: 클릭할 행 없음'; })()"
2026-01-30 10:50:38 +09:00
} ,
2026-02-06 06:18:53 +09:00
{ "type" : "wait" , "duration" : 1500 }
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 22 ,
"name" : "모달 열림 확인" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const modal = document.querySelector('[role=\"dialog\"], [aria-modal=\"true\"], [class*=\"modal\"][class*=\"open\"], [class*=\"Modal\"], [class*=\"Dialog\"], [data-state=\"open\"]'); if (modal && modal.offsetParent !== null) return 'PASS: 모달 열림 확인'; return 'WARN: 모달 미열림 (임시저장 문서는 수정 페이지로 이동할 수 있음)'; })()"
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 23 ,
"name" : "모달 내용 확인" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const modal = document.querySelector('[role=\"dialog\"], [aria-modal=\"true\"], [class*=\"Modal\"], [class*=\"Dialog\"]'); if (!modal) return 'SKIP: 모달 없음'; const text = modal.innerText || ''; const checks = ['문서번호', '기안', '결재', '제목'].filter(k => text.includes(k)); return checks.length >= 2 ? `PASS: 모달 내용 확인 (${checks.join(', ')})` : 'WARN: 모달 내용 부족'; })()"
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 24 ,
"name" : "모달 내 버튼 확인 (수정/복제)" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const modal = document.querySelector('[role=\"dialog\"], [aria-modal=\"true\"], [class*=\"Modal\"]'); if (!modal) return 'SKIP: 모달 없음'; const btns = modal.querySelectorAll('button'); const found = []; btns.forEach(b => { const t = b.innerText?.trim(); if (['수정', '복제', '상신', '닫기'].some(k => t?.includes(k))) found.push(t); }); return found.length > 0 ? `PASS: 모달 버튼 발견 (${found.join(', ')})` : 'WARN: 모달 버튼 미발견'; })()"
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 25 ,
2026-01-30 10:50:38 +09:00
"name" : "모달 닫기" ,
2026-02-06 06:18:53 +09:00
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "close_modal"
2026-01-30 10:50:38 +09:00
} ,
2026-02-06 06:18:53 +09:00
{ "type" : "wait" , "duration" : 500 }
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 26 ,
"name" : "모달 닫힘 확인" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const modal = document.querySelector('[role=\"dialog\"][data-state=\"open\"], [aria-modal=\"true\"]:not([hidden])'); if (!modal || modal.offsetParent === null) return 'PASS: 모달 닫힘 확인'; return 'WARN: 모달이 아직 열려있음'; })()"
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 27 ,
"name" : "전체 선택 체크박스 클릭" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const headerCb = document.querySelector('thead input[type=\"checkbox\"], thead [role=\"checkbox\"], th input[type=\"checkbox\"], th button[role=\"checkbox\"]'); if (headerCb) { headerCb.click(); return 'PASS: 전체 선택 체크박스 클릭'; } return 'SKIP: 전체 선택 체크박스 없음'; })()"
2026-02-06 01:26:59 +09:00
} ,
2026-02-06 06:18:53 +09:00
{ "type" : "wait" , "duration" : 500 }
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 28 ,
"name" : "전체 선택 결과 확인" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const checkedCbs = document.querySelectorAll('tbody input[type=\"checkbox\"]:checked, tbody [role=\"checkbox\"][data-state=\"checked\"], tbody button[role=\"checkbox\"][aria-checked=\"true\"]'); return checkedCbs.length > 0 ? `PASS: ${checkedCbs.length}개 행 선택됨` : 'WARN: 선택된 행 없음'; })()"
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 29 ,
"name" : "전체 선택 해제" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const headerCb = document.querySelector('thead input[type=\"checkbox\"]:checked, thead [role=\"checkbox\"][data-state=\"checked\"], thead button[role=\"checkbox\"][aria-checked=\"true\"]'); if (headerCb) { headerCb.click(); return 'PASS: 전체 선택 해제'; } return 'SKIP: 체크된 전체 선택 없음'; })()"
2026-01-30 10:50:38 +09:00
} ,
2026-02-06 06:18:53 +09:00
{ "type" : "wait" , "duration" : 300 }
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 30 ,
2026-01-30 10:50:38 +09:00
"name" : "테이블 hover 효과 확인" ,
2026-02-06 06:18:53 +09:00
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
"type" : "hover" ,
2026-02-06 06:18:53 +09:00
"selector" : "tbody tr:first-child, table [role='row']:nth-child(2)"
} ,
2026-01-30 10:50:38 +09:00
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const row = document.querySelector('tbody tr:first-child'); if (row) { const style = getComputedStyle(row); return 'PASS: hover 동작 확인 (시각적 효과는 스크린샷으로 확인)'; } return 'SKIP: hover 대상 없음'; })()"
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 31 ,
"name" : "빈 검색 결과 테스트" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "fill" ,
"selector" : "input[type='search'], input[placeholder*='검색'], input[name='search']" ,
"value" : "ZZZZNOTEXIST99999"
2026-01-30 10:50:38 +09:00
} ,
2026-02-06 06:18:53 +09:00
{ "type" : "wait" , "duration" : 1500 } ,
2026-01-30 10:50:38 +09:00
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const empty = document.querySelector('[class*=\"empty\"], [class*=\"no-data\"], td[colspan]'); const rows = document.querySelectorAll('tbody tr:not(:has(td[colspan]))'); if (empty || rows.length === 0) return 'PASS: 빈 결과 표시 확인'; return 'WARN: 빈 결과 메시지 미표시'; })()"
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 32 ,
"name" : "검색어 최종 초기화" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "clear" ,
"selector" : "input[type='search'], input[placeholder*='검색'], input[name='search']"
2026-01-30 10:50:38 +09:00
} ,
2026-02-06 06:18:53 +09:00
{ "type" : "wait" , "duration" : 1000 }
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 33 ,
2026-01-30 10:50:38 +09:00
"name" : "콘솔 에러 확인" ,
2026-02-06 06:18:53 +09:00
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { return 'PASS: 콘솔 에러 확인 (step-executor API 로그에서 확인)'; })()"
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 34 ,
"name" : "API 호출 요약 확인" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "evaluate" ,
"script" : "(() => { const logs = window.__API_LOGS__ || []; const approvalLogs = logs.filter(l => l.url?.includes('approval')); return approvalLogs.length > 0 ? `PASS: API 호출 ${approvalLogs.length}건 (approval 관련)` : 'WARN: approval API 호출 미감지'; })()"
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-06 06:18:53 +09:00
"id" : 35 ,
"name" : "최종 URL 확인" ,
"phase" : "READ" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
2026-02-06 06:18:53 +09:00
"type" : "verify_url" ,
"pattern" : "/approval/draft" ,
"timeout" : 3000
2026-01-30 10:50:38 +09:00
}
2026-02-06 06:18:53 +09:00
]
2026-01-30 10:50:38 +09:00
}
] ,
"cleanup" : {
"description" : "테스트 후 정리 작업 (없음)" ,
"actions" : [ ]
} ,
"notes" : [
"20개씩 페이지네이션 (itemsPerPage: 20)" ,
"검색 필드: 문서번호, 제목, 기안자 검색 가능" ,
"필터: 전체, 임시저장, 결재대기, 진행중, 완료, 반려" ,
"정렬: 최신순, 오래된순, 제목 오름차순, 제목 내림차순" ,
"체크박스: 개별 선택, 전체 선택 가능" ,
"상신/삭제: 선택된 항목이 있을 때만 버튼 표시" ,
"임시저장 문서: 선택 시 작업 컬럼에 수정/삭제 버튼 표시" ,
"문서 클릭 동작: 임시저장 → 수정 페이지, 그 외 → 상세 모달" ,
2026-02-06 06:18:53 +09:00
"IntegratedListTemplateV2 템플릿 사용으로 반응형 지원"
2026-01-30 10:50:38 +09:00
]
2026-02-06 06:18:53 +09:00
}