diff --git a/_backup_before_enhance/accounting-bad-debt.json b/_backup_before_enhance/accounting-bad-debt.json new file mode 100644 index 0000000..9a14f4c --- /dev/null +++ b/_backup_before_enhance/accounting-bad-debt.json @@ -0,0 +1,259 @@ +{ + "id": "accounting-bad-debt", + "name": "악성채권추심관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "회계관리 > 악성채권추심관리 메뉴의 악성채권 CRUD 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "회계관리", + "level2": "악성채권추심관리", + "expectedUrl": "/accounting/bad-debt-collection", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "testData": { + "create": { + "vendorName": "E2E_TEST_채권거래처_{timestamp}", + "debtAmount": "1000000", + "status": "추심중" + } + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 회계관리 > 악성채권추심관리", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "악성채권추심관리", + "expected": { + "url_contains": "/accounting", + "visible": ["악성채권", "추심"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "악성채권 목록 표시", + "채권 등록 버튼 존재", + "상태 필터 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "악성채권 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "거래처 컬럼", + "채권금액 컬럼", + "발생일 컬럼", + "상태 컬럼", + "담당자 컬럼" + ], + "expected": "악성채권 테이블 표시" + }, + { + "id": 4, + "name": "통계 카드 확인", + "action": "verify_elements", + "checks": [ + "총 채권금액 표시", + "추심중 건수", + "회수완료 건수" + ], + "expected": "통계 정보 표시" + }, + { + "id": 5, + "phase": "CREATE", + "name": "[CREATE] 채권 등록 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('등록'), button:has-text('추가'), button:has-text('신규')", + "expected": { + "modal_open": true + } + }, + { + "id": 6, + "phase": "CREATE", + "name": "[CREATE] 거래처 선택", + "action": "click_if_exists", + "target": "select[name*='vendor'], input[placeholder*='거래처']", + "expected": "거래처 선택 가능" + }, + { + "id": 7, + "phase": "CREATE", + "name": "[CREATE] 채권금액 입력", + "action": "click_if_exists", + "target": "input[name*='amount'], input[placeholder*='금액']" + }, + { + "id": 8, + "phase": "CREATE", + "name": "[CREATE] 필수 검증 #2: 채권 저장", + "action": "click_if_exists", + "target": "button:has-text('저장'), button:has-text('등록'), button:has-text('확인')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "POST /api/v1/accounting/bad-debts", + "toast": "등록|저장|완료|성공" + }, + "expected": "채권 등록 완료" + }, + { + "id": 9, + "phase": "READ", + "name": "[READ] 등록된 채권 검색", + "action": "click_if_exists", + "target": "input[type='search'], input[placeholder*='검색']" + }, + { + "id": 10, + "phase": "READ", + "name": "[READ] 등록된 채권 확인", + "action": "verify_detail", + "checks": [ + "E2E_TEST_채권거래처 목록에 표시" + ], + "expected": "등록된 채권 확인" + }, + { + "id": 11, + "phase": "READ", + "name": "[READ] 채권 상세 조회", + "action": "click_if_exists", + "target": "table tbody tr:has-text('E2E_TEST_채권거래처')", + "expected": { + "detail_view": true + } + }, + { + "id": 12, + "name": "상세 정보 확인", + "action": "verify_elements", + "checks": [ + "거래처 정보 표시", + "채권금액 표시", + "추심 이력 표시" + ], + "expected": "상세 정보 표시" + }, + { + "id": 13, + "phase": "UPDATE", + "name": "[UPDATE] 상태 변경", + "action": "click_if_exists", + "target": "button:has-text('상태변경'), select[name*='status']", + "expected": "상태 변경 가능" + }, + { + "id": 14, + "phase": "UPDATE", + "name": "[UPDATE] 추심 메모 추가", + "action": "click_if_exists", + "target": "textarea[name*='memo'], textarea[placeholder*='메모']" + }, + { + "id": 15, + "phase": "UPDATE", + "name": "[UPDATE] 변경 저장", + "action": "click_if_exists", + "target": "button:has-text('저장'), button:has-text('확인')", + "verify": { + "api_call": "PUT /api/v1/accounting/bad-debts", + "toast": "수정|저장|완료|성공" + }, + "expected": "채권 정보 수정 완료" + }, + { + "id": 16, + "phase": "DELETE", + "name": "[DELETE] 채권 삭제", + "action": "click_if_exists", + "target": "button:has-text('삭제'), button:has-text('제거')", + "expected": { + "confirm_dialog": true + } + }, + { + "id": 17, + "phase": "DELETE", + "name": "[DELETE] 삭제 확인", + "action": "click_if_exists", + "target": "[role='alertdialog'] button:has-text('확인'), [role='dialog'] button:has-text('삭제')", + "verify": { + "api_call": "DELETE /api/v1/accounting/bad-debts", + "toast": "삭제|제거|완료|성공" + }, + "expected": "채권 삭제 완료" + }, + { + "id": 18, + "phase": "DELETE", + "name": "[DELETE] 삭제 확인", + "action": "verify_detail", + "checks": [ + "E2E_TEST_채권거래처 목록에서 제거" + ], + "expected": "채권 삭제 반영" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/accounting/bad-debts", + "description": "악성채권 목록 조회" + }, + { + "method": "POST", + "endpoint": "/api/v1/accounting/bad-debts", + "description": "악성채권 등록" + }, + { + "method": "GET", + "endpoint": "/api/v1/accounting/bad-debts/:id", + "description": "악성채권 상세 조회" + }, + { + "method": "PUT", + "endpoint": "/api/v1/accounting/bad-debts/:id", + "description": "악성채권 수정" + }, + { + "method": "DELETE", + "endpoint": "/api/v1/accounting/bad-debts/:id", + "description": "악성채권 삭제" + } + ], + "requiredVerifications": [ + { + "id": 2, + "name": "저장 버튼", + "steps": [8, 15], + "criteria": "API 호출 + 성공 토스트 + 데이터 반영" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "악성채권 목록, 등록 버튼, 상태 필터 존재" + } + ], + "rollbackPlan": { + "onCreateFail": "등록 모달 닫고 재시도", + "onUpdateFail": "페이지 새로고침 후 재시도", + "onDeleteFail": "수동 삭제 필요", + "cleanupRequired": "E2E_TEST_채권거래처* 패턴 데이터 삭제" + } +} diff --git a/_backup_before_enhance/accounting-bank-transaction.json b/_backup_before_enhance/accounting-bank-transaction.json new file mode 100644 index 0000000..fe565a7 --- /dev/null +++ b/_backup_before_enhance/accounting-bank-transaction.json @@ -0,0 +1,195 @@ +{ + "id": "accounting-bank-transaction", + "name": "입출금계좌조회 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "회계관리 > 입출금계좌조회 메뉴의 계좌 거래내역 조회/필터/엑셀 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "회계관리", + "level2": "입출금계좌조회", + "expectedUrl": "/accounting/bank-transactions", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 회계관리 > 입출금계좌조회", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "입출금계좌조회", + "expected": { + "url_contains": "/accounting", + "visible": ["입출금", "계좌"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "거래내역 목록 표시", + "계좌 선택 가능", + "기간 필터 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "거래내역 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "거래일 컬럼", + "입금 컬럼", + "출금 컬럼", + "잔액 컬럼", + "적요 컬럼" + ], + "expected": "거래내역 테이블 표시" + }, + { + "id": 4, + "name": "계좌 선택 드롭다운 확인", + "action": "verify_elements", + "checks": [ + "계좌 선택 가능" + ], + "expected": "계좌 선택 가능" + }, + { + "id": 5, + "phase": "FILTER", + "name": "[FILTER] 기간 필터 - 시작일", + "action": "click_if_exists", + "target": "input[type='date']:first-of-type, [class*='datepicker']:first-of-type", + "expected": "날짜 선택 열림" + }, + { + "id": 6, + "phase": "FILTER", + "name": "[FILTER] 기간 필터 적용", + "action": "click_if_exists", + "target": "button:has-text('조회'), button:has-text('검색'), button:has-text('적용')", + "expected": "필터 적용됨" + }, + { + "id": 7, + "phase": "FILTER", + "name": "[FILTER] 필터 결과 확인", + "action": "verify_detail", + "checks": [ + "필터된 데이터 표시 또는 결과 없음" + ], + "expected": "필터 동작 확인" + }, + { + "id": 8, + "phase": "READ", + "name": "[READ] 거래 상세 보기", + "action": "click_if_exists", + "target": "table tbody tr:first-child", + "expected": { + "detail_view": true + } + }, + { + "id": 9, + "name": "상세 정보 확인", + "action": "verify_detail", + "checks": [ + "거래일시 표시", + "금액 표시", + "적요 표시" + ], + "expected": "거래 상세 정보 표시" + }, + { + "id": 10, + "name": "목록으로 돌아가기", + "action": "click_if_exists", + "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", + "expected": "목록 페이지로 복귀" + }, + { + "id": 11, + "name": "입금 합계 확인", + "action": "verify_elements", + "checks": [ + "입금 합계 표시" + ], + "expected": "입금 합계 표시" + }, + { + "id": 12, + "name": "출금 합계 확인", + "action": "verify_elements", + "checks": [ + "출금 합계 표시" + ], + "expected": "출금 합계 표시" + }, + { + "id": 13, + "name": "엑셀 다운로드 버튼 확인", + "action": "verify_elements", + "checks": [ + "엑셀 다운로드 버튼 존재" + ], + "expected": "엑셀 다운로드 기능 표시" + }, + { + "id": 14, + "name": "인쇄 버튼 확인", + "action": "verify_elements", + "checks": [ + "인쇄 버튼 존재" + ], + "expected": "인쇄 기능 표시" + }, + { + "id": 15, + "name": "페이지네이션 확인", + "action": "verify_elements", + "checks": [ + "페이지 번호 표시" + ], + "expected": "페이지네이션 표시" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/accounting/bank-transactions", + "description": "입출금 내역 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/accounting/bank-accounts", + "description": "계좌 목록 조회" + } + ], + "requiredVerifications": [ + { + "id": 3, + "name": "검색/필터", + "steps": [5, 6, 7], + "criteria": "기간 필터 동작" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "거래내역 목록, 계좌 선택, 기간 필터 존재" + } + ], + "rollbackPlan": { + "note": "조회 전용 페이지로 데이터 변경 없음" + } +} diff --git a/_backup_before_enhance/accounting-bill.json b/_backup_before_enhance/accounting-bill.json new file mode 100644 index 0000000..6ffc9a6 --- /dev/null +++ b/_backup_before_enhance/accounting-bill.json @@ -0,0 +1,293 @@ +{ + "id": "accounting-bill", + "name": "어음관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "회계관리 > 어음관리 메뉴의 어음 조회/등록/수정/삭제 전체 CRUD 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "회계관리", + "level2": "어음관리", + "expectedUrl": "/accounting/bills", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "testData": { + "create": { + "billNumber": "E2E_TEST_어음", + "amount": "1000000", + "dueDate": "2026-03-15", + "issuer": "테스트발행처", + "memo": "E2E 자동화 테스트 어음" + }, + "update": { + "memo": "E2E 수정된 어음 메모" + } + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 회계관리 > 어음관리", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "어음관리", + "expected": { + "url_contains": "/accounting/bills", + "visible": ["어음관리", "어음"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "어음 목록 표시", + "어음 등록 버튼 존재", + "검색/필터 기능 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "어음 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "어음번호 컬럼", + "금액 컬럼", + "만기일 컬럼", + "발행처 컬럼", + "상태 컬럼" + ], + "expected": "어음 테이블 컬럼 정상 표시" + }, + { + "id": 4, + "name": "검색 기능 테스트", + "action": "click_if_exists", + "target": "input[placeholder*='검색']", + "value": "테스트", + "expected": { + "data_filtered": true + } + }, + { + "id": 5, + "phase": "CREATE", + "name": "[CREATE] 어음 등록 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('등록'), button:has-text('어음 등록'), button:has-text('추가')", + "expected": { + "modal": true, + "modalTitle": "어음 등록" + } + }, + { + "id": 6, + "phase": "CREATE", + "name": "[CREATE] 어음 정보 입력", + "action": "fill_form", + "fields": [ + {"name": "어음번호", "type": "text", "value": "E2E_TEST_어음_{timestamp}"}, + {"name": "금액", "type": "number", "value": "1000000"}, + {"name": "만기일", "type": "date", "value": "2026-03-15"}, + {"name": "발행처", "type": "text", "value": "테스트발행처"}, + {"name": "메모", "type": "text", "value": "E2E 자동화 테스트 어음"} + ], + "note": "타임스탬프로 고유성 보장" + }, + { + "id": 7, + "phase": "CREATE", + "name": "[CREATE] 필수 검증 #2: 등록 저장", + "action": "click_if_exists", + "target": "button:has-text('저장'), button:has-text('등록')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "POST /api/v1/bills", + "toast": "등록|완료|성공" + }, + "expected": "어음 등록 완료" + }, + { + "id": "7-modal-close", + "phase": "CREATE", + "name": "[CREATE] 모달 닫기 확인", + "action": "close_modal_if_open", + "expected": "모달 닫힘" + }, + { + "id": 8, + "phase": "CREATE", + "name": "[CREATE] 등록 결과 확인", + "action": "verify_detail", + "search": "E2E_TEST_어음", + "expected": { + "row_exists": true, + "contains": ["E2E", "1,000,000"] + } + }, + { + "id": 9, + "phase": "READ", + "name": "[READ] 어음 상세 페이지 진입", + "action": "click_if_exists", + "target": "table tbody tr:has-text('E2E')", + "expected": { + "url_contains": "/accounting/bills/", + "visible": ["어음 상세", "수정", "삭제"] + } + }, + { + "id": 10, + "phase": "READ", + "name": "[READ] 상세 정보 확인", + "action": "verify_detail", + "checks": [ + "어음번호: E2E_TEST_어음", + "금액: 1,000,000", + "만기일: 2026-03-15", + "발행처: 테스트발행처" + ], + "expected": "입력한 데이터와 일치" + }, + { + "id": 11, + "phase": "UPDATE", + "name": "[UPDATE] 수정 모드 진입", + "action": "click_if_exists", + "target": "button:has-text('수정')", + "expected": { + "url_contains": "mode=edit", + "fields_editable": true + } + }, + { + "id": 12, + "phase": "UPDATE", + "name": "[UPDATE] 메모 수정", + "action": "click_if_exists", + "target": "textarea[name*='memo'], input[placeholder*='메모']", + "value": "E2E 수정된 어음 메모_{timestamp}", + "clear": true + }, + { + "id": 13, + "phase": "UPDATE", + "name": "[UPDATE] 필수 검증 #2: 수정 저장", + "action": "click_if_exists", + "target": "button:has-text('저장')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "PUT /api/v1/bills/", + "toast": "수정|완료|성공" + }, + "expected": "수정 완료" + }, + { + "id": 14, + "phase": "UPDATE", + "name": "[UPDATE] 수정 결과 확인", + "action": "verify_detail", + "checks": [ + "메모: E2E 수정된 어음" + ], + "expected": "수정된 데이터 반영" + }, + { + "id": 15, + "phase": "DELETE", + "name": "[DELETE] 삭제 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('삭제')", + "expected": { + "confirm_dialog": true, + "dialog_message": "삭제|정말" + } + }, + { + "id": 16, + "phase": "DELETE", + "name": "[DELETE] 필수 검증 #6: 삭제 확인", + "action": "click_if_exists", + "target": "button:has-text('확인'), button:has-text('삭제')", + "verify": { + "api_call": "DELETE /api/v1/bills/", + "toast": "삭제|완료|성공", + "redirect": "/accounting/bills" + }, + "expected": "삭제 완료 및 목록 복귀" + }, + { + "id": 17, + "phase": "DELETE", + "name": "[DELETE] 삭제 결과 확인", + "action": "verify_detail", + "search": "E2E 수정된 어음", + "expected": { + "row_exists": false, + "message": "테스트 어음이 목록에서 제거됨" + } + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/bills", + "description": "어음 목록 조회" + }, + { + "method": "POST", + "endpoint": "/api/v1/bills", + "description": "어음 등록" + }, + { + "method": "GET", + "endpoint": "/api/v1/bills/{id}", + "description": "어음 상세 조회" + }, + { + "method": "PUT", + "endpoint": "/api/v1/bills/{id}", + "description": "어음 수정" + }, + { + "method": "DELETE", + "endpoint": "/api/v1/bills/{id}", + "description": "어음 삭제" + } + ], + "requiredVerifications": [ + { + "id": 2, + "name": "등록/저장 버튼", + "steps": [7, 13], + "criteria": "API 호출 + 성공 토스트 + 데이터 반영" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "어음 목록, 등록 버튼, 필터 존재" + }, + { + "id": 6, + "name": "삭제 기능", + "steps": [15, 16, 17], + "criteria": "DELETE API + 목록에서 제거" + } + ], + "rollbackPlan": { + "onCreateFail": "모달 닫기", + "onUpdateFail": "테스트 어음 수동 삭제 필요", + "onDeleteFail": "테스트 어음 수동 삭제 필요", + "cleanupRequired": "E2E_TEST_ 접두사 어음은 테스트 데이터" + } +} diff --git a/_backup_before_enhance/accounting-card-history.json b/_backup_before_enhance/accounting-card-history.json new file mode 100644 index 0000000..b23b371 --- /dev/null +++ b/_backup_before_enhance/accounting-card-history.json @@ -0,0 +1,196 @@ +{ + "id": "accounting-card-history", + "name": "카드내역조회 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "회계관리 > 카드내역조회 메뉴의 카드 사용내역 조회/필터/엑셀 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "회계관리", + "level2": "카드내역조회", + "expectedUrl": "/accounting/card-transactions", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 회계관리 > 카드내역조회", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "카드내역조회", + "expected": { + "url_contains": "/accounting", + "visible": ["카드", "내역"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "카드 사용내역 목록 표시", + "카드 선택 가능", + "기간 필터 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "카드내역 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "사용일 컬럼", + "카드명 컬럼", + "가맹점 컬럼", + "금액 컬럼", + "승인번호 컬럼" + ], + "expected": "카드내역 테이블 표시" + }, + { + "id": 4, + "name": "카드 선택 드롭다운 확인", + "action": "verify_elements", + "checks": [ + "카드 선택 가능" + ], + "expected": "카드 선택 가능" + }, + { + "id": 5, + "phase": "FILTER", + "name": "[FILTER] 기간 필터 - 시작일", + "action": "click_if_exists", + "target": "input[type='date']:first-of-type, [class*='datepicker']:first-of-type", + "expected": "날짜 선택 열림" + }, + { + "id": 6, + "phase": "FILTER", + "name": "[FILTER] 기간 필터 적용", + "action": "click_if_exists", + "target": "button:has-text('조회'), button:has-text('검색'), button:has-text('적용')", + "expected": "필터 적용됨" + }, + { + "id": 7, + "phase": "FILTER", + "name": "[FILTER] 필터 결과 확인", + "action": "verify_detail", + "checks": [ + "필터된 데이터 표시 또는 결과 없음" + ], + "expected": "필터 동작 확인" + }, + { + "id": 8, + "phase": "READ", + "name": "[READ] 카드 사용내역 상세 보기", + "action": "click_if_exists", + "target": "table tbody tr:first-child", + "expected": { + "detail_view": true + } + }, + { + "id": 9, + "name": "상세 정보 확인", + "action": "verify_detail", + "checks": [ + "사용일시 표시", + "가맹점 정보 표시", + "금액 표시", + "승인번호 표시" + ], + "expected": "카드 사용 상세 정보 표시" + }, + { + "id": 10, + "name": "목록으로 돌아가기", + "action": "click_if_exists", + "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", + "expected": "목록 페이지로 복귀" + }, + { + "id": 11, + "name": "사용금액 합계 확인", + "action": "verify_elements", + "checks": [ + "합계 금액 표시" + ], + "expected": "사용금액 합계 표시" + }, + { + "id": 12, + "name": "카드별 사용 현황 확인", + "action": "verify_elements", + "checks": [ + "카드별 사용 현황 또는 통계" + ], + "expected": "카드별 현황 표시" + }, + { + "id": 13, + "name": "엑셀 다운로드 버튼 확인", + "action": "verify_elements", + "checks": [ + "엑셀 다운로드 버튼 존재" + ], + "expected": "엑셀 다운로드 기능 표시" + }, + { + "id": 14, + "name": "인쇄 버튼 확인", + "action": "verify_elements", + "checks": [ + "인쇄 버튼 존재" + ], + "expected": "인쇄 기능 표시" + }, + { + "id": 15, + "name": "페이지네이션 확인", + "action": "verify_elements", + "checks": [ + "페이지 번호 표시" + ], + "expected": "페이지네이션 표시" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/accounting/card-transactions", + "description": "카드 사용내역 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/accounting/cards", + "description": "카드 목록 조회" + } + ], + "requiredVerifications": [ + { + "id": 3, + "name": "검색/필터", + "steps": [5, 6, 7], + "criteria": "기간 필터 동작" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "카드내역 목록, 카드 선택, 기간 필터 존재" + } + ], + "rollbackPlan": { + "note": "조회 전용 페이지로 데이터 변경 없음" + } +} diff --git a/_backup_before_enhance/accounting-client.json b/_backup_before_enhance/accounting-client.json new file mode 100644 index 0000000..cea6d00 --- /dev/null +++ b/_backup_before_enhance/accounting-client.json @@ -0,0 +1,283 @@ +{ + "id": "accounting-client", + "name": "회계거래처관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] + }, + "description": "회계관리 > 거래처관리 메뉴의 거래처 CRUD 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "회계관리", + "level2": "거래처관리", + "expectedUrl": "/accounting/vendors", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "testData": { + "create": { + "vendorName": "E2E_TEST_회계거래처_{timestamp}", + "vendorType": "매출", + "creditGrade": "A" + } + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 회계관리 > 거래처관리", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "거래처관리", + "expected": { + "url_contains": "/accounting/vendors", + "visible": [ + "거래처관리", + "거래처" + ] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "거래처 목록 표시", + "거래처 등록 버튼 존재", + "검색 기능 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "통계 카드 확인", + "action": "verify_elements", + "checks": [ + "전체 거래처 카드", + "매출 거래처 카드", + "매입 거래처 카드" + ], + "expected": "통계 카드 표시" + }, + { + "id": 4, + "name": "거래처 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "구분 컬럼", + "거래처명 컬럼", + "신용등급 컬럼", + "거래등급 컬럼", + "사업자등록번호 컬럼" + ], + "expected": "거래처 테이블 표시" + }, + { + "id": 5, + "phase": "CREATE", + "name": "[CREATE] 거래처 등록 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('등록'), button:has-text('추가'), button:has-text('신규')", + "expected": { + "modal_open": true + } + }, + { + "id": 6, + "phase": "CREATE", + "name": "[CREATE] 거래처명 입력", + "action": "click_if_exists", + "target": "input[name*='name'], input[placeholder*='거래처명']", + "value": "E2E_TEST_회계거래처_{timestamp}", + "clear": true + }, + { + "id": 7, + "phase": "CREATE", + "name": "[CREATE] 거래처 구분 선택", + "action": "click_if_exists", + "target": "select[name*='type'], button:has-text('매출')", + "expected": "거래처 구분 선택" + }, + { + "id": 8, + "phase": "CREATE", + "name": "[CREATE] 필수 검증 #2: 거래처 저장", + "action": "click_if_exists", + "target": "button:has-text('저장'), button:has-text('등록'), button:has-text('확인')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "POST /api/v1/accounting/vendors", + "toast": "등록|저장|완료|성공" + }, + "expected": "거래처 등록 완료" + }, + { + "id": 9, + "phase": "READ", + "name": "[READ] 등록된 거래처 검색", + "action": "click_if_exists", + "target": "input[type='search'], input[placeholder*='검색']", + "value": "E2E_TEST_회계거래처", + "submit": true + }, + { + "id": 10, + "phase": "READ", + "name": "[READ] 등록된 거래처 목록 확인", + "action": "verify_detail", + "checks": [ + "E2E_TEST_회계거래처 목록에 표시" + ], + "expected": "등록된 거래처 확인" + }, + { + "id": 11, + "phase": "READ", + "name": "[READ] 거래처 상세 조회", + "action": "click_if_exists", + "target": "table tbody tr:has-text('E2E_TEST_회계거래처')", + "expected": { + "detail_view": true, + "url_change": true + } + }, + { + "id": 12, + "name": "상세 페이지 정보 확인", + "action": "verify_elements", + "checks": [ + "거래처명 표시", + "사업자등록번호 표시", + "연락처 정보" + ], + "expected": "상세 정보 표시" + }, + { + "id": 13, + "phase": "UPDATE", + "name": "[UPDATE] 수정 모드 진입", + "action": "click_if_exists", + "target": "button:has-text('수정'), button:has-text('편집')", + "expected": { + "edit_mode": true + } + }, + { + "id": 14, + "phase": "UPDATE", + "name": "[UPDATE] 거래처 정보 수정", + "action": "click_if_exists", + "target": "input[name*='name'], input[placeholder*='거래처명']", + "value": "E2E_TEST_회계거래처_수정", + "clear": true + }, + { + "id": 15, + "phase": "UPDATE", + "name": "[UPDATE] 거래처 저장", + "action": "click_if_exists", + "target": "button:has-text('저장'), button:has-text('확인')", + "verify": { + "api_call": "PUT /api/v1/accounting/vendors", + "toast": "수정|저장|완료|성공" + }, + "expected": "거래처 수정 완료" + }, + { + "id": 16, + "phase": "DELETE", + "name": "[DELETE] 거래처 삭제", + "action": "click_if_exists", + "target": "button:has-text('삭제'), button:has-text('제거')", + "expected": { + "confirm_dialog": true + } + }, + { + "id": 17, + "phase": "DELETE", + "name": "[DELETE] 삭제 확인", + "action": "click_if_exists", + "target": "[role='alertdialog'] button:has-text('확인'), [role='dialog'] button:has-text('삭제')", + "verify": { + "api_call": "DELETE /api/v1/accounting/vendors", + "toast": "삭제|제거|완료|성공" + }, + "expected": "거래처 삭제 완료" + }, + { + "id": 18, + "phase": "DELETE", + "name": "[DELETE] 삭제 확인", + "action": "verify_detail", + "checks": [ + "E2E_TEST_회계거래처 목록에서 제거" + ], + "expected": "거래처 삭제 반영" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/accounting/vendors", + "description": "거래처 목록 조회" + }, + { + "method": "POST", + "endpoint": "/api/v1/accounting/vendors", + "description": "거래처 등록" + }, + { + "method": "GET", + "endpoint": "/api/v1/accounting/vendors/:id", + "description": "거래처 상세 조회" + }, + { + "method": "PUT", + "endpoint": "/api/v1/accounting/vendors/:id", + "description": "거래처 수정" + }, + { + "method": "DELETE", + "endpoint": "/api/v1/accounting/vendors/:id", + "description": "거래처 삭제" + } + ], + "requiredVerifications": [ + { + "id": 2, + "name": "저장 버튼", + "steps": [ + 8, + 15 + ], + "criteria": "API 호출 + 성공 토스트 + 데이터 반영" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [ + 2 + ], + "criteria": "거래처 목록, 등록 버튼, 검색 기능 존재" + } + ], + "rollbackPlan": { + "onCreateFail": "등록 모달 닫고 재시도", + "onUpdateFail": "페이지 새로고침 후 재시도", + "onDeleteFail": "수동 삭제 필요", + "cleanupRequired": "E2E_TEST_회계거래처* 패턴 데이터 삭제" + } +} \ No newline at end of file diff --git a/_backup_before_enhance/accounting-deposit.json b/_backup_before_enhance/accounting-deposit.json new file mode 100644 index 0000000..b8e6ccf --- /dev/null +++ b/_backup_before_enhance/accounting-deposit.json @@ -0,0 +1,302 @@ +{ + "id": "accounting-deposit", + "name": "입금관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "회계관리 > 입금관리 메뉴의 입금 조회/등록/수정/삭제 전체 CRUD 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "회계관리", + "level2": "입금관리", + "expectedUrl": "/accounting/deposits", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "testData": { + "create": { + "vendorName": "E2E_TEST_입금거래처", + "amount": "100000", + "depositMethod": "계좌이체", + "memo": "E2E 자동화 테스트 입금" + }, + "update": { + "amount": "150000", + "memo": "E2E 수정된 입금 메모" + } + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 회계관리 > 입금관리", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "입금관리", + "expected": { + "url_contains": "/accounting/deposits", + "visible": ["입금관리", "입금"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "입금 목록 표시", + "입금 등록 버튼 존재", + "검색/필터 기능 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "입금 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "입금일 컬럼", + "거래처 컬럼", + "금액 컬럼", + "입금방법 컬럼", + "상태 컬럼" + ], + "expected": "입금 테이블 컬럼 정상 표시" + }, + { + "id": 4, + "name": "검색 기능 테스트", + "action": "click_if_exists", + "target": "input[placeholder*='검색']", + "value": "테스트", + "expected": { + "data_filtered": true + } + }, + { + "id": 5, + "phase": "CREATE", + "name": "[CREATE] 입금 등록 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('등록'), button:has-text('입금 등록'), button:has-text('추가')", + "expected": { + "modal": true, + "modalTitle": "입금 등록" + } + }, + { + "id": 6, + "phase": "CREATE", + "name": "[CREATE] 입금 정보 입력", + "action": "fill_form", + "fields": [ + {"name": "거래처", "type": "select", "value": "E2E_TEST_입금거래처"}, + {"name": "입금일", "type": "date", "value": "2026-02-03"}, + {"name": "금액", "type": "number", "value": "100000"}, + {"name": "입금방법", "type": "select", "value": "계좌이체"}, + {"name": "메모", "type": "text", "value": "E2E 자동화 테스트 입금_{timestamp}"} + ], + "note": "타임스탬프로 고유성 보장" + }, + { + "id": 7, + "phase": "CREATE", + "name": "[CREATE] 필수 검증 #2: 등록 저장", + "action": "click_if_exists", + "target": "button:has-text('저장'), button:has-text('등록')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "POST /api/v1/deposits", + "toast": "등록|완료|성공" + }, + "expected": "입금 등록 완료" + }, + { + "id": "7-modal-close", + "phase": "CREATE", + "name": "[CREATE] 모달 닫기 확인", + "action": "close_modal_if_open", + "expected": "모달 닫힘" + }, + { + "id": 8, + "phase": "CREATE", + "name": "[CREATE] 등록 결과 확인", + "action": "verify_detail", + "search": "E2E 자동화 테스트 입금", + "expected": { + "row_exists": true, + "contains": ["E2E", "100,000"] + } + }, + { + "id": 9, + "phase": "READ", + "name": "[READ] 입금 상세 페이지 진입", + "action": "click_if_exists", + "target": "table tbody tr:has-text('E2E')", + "expected": { + "url_contains": "/accounting/deposits/", + "visible": ["입금 상세", "수정", "삭제"] + } + }, + { + "id": 10, + "phase": "READ", + "name": "[READ] 상세 정보 확인", + "action": "verify_detail", + "checks": [ + "금액: 100,000", + "입금방법: 계좌이체", + "메모: E2E 자동화 테스트" + ], + "expected": "입력한 데이터와 일치" + }, + { + "id": 11, + "phase": "UPDATE", + "name": "[UPDATE] 수정 모드 진입", + "action": "click_if_exists", + "target": "button:has-text('수정')", + "expected": { + "url_contains": "mode=edit", + "fields_editable": true + } + }, + { + "id": 12, + "phase": "UPDATE", + "name": "[UPDATE] 금액 수정", + "action": "click_if_exists", + "target": "input[name*='amount'], input[placeholder*='금액']", + "value": "150000", + "clear": true + }, + { + "id": 13, + "phase": "UPDATE", + "name": "[UPDATE] 메모 수정", + "action": "click_if_exists", + "target": "textarea[name*='memo'], input[placeholder*='메모']", + "value": "E2E 수정된 입금 메모_{timestamp}", + "clear": true + }, + { + "id": 14, + "phase": "UPDATE", + "name": "[UPDATE] 필수 검증 #2: 수정 저장", + "action": "click_if_exists", + "target": "button:has-text('저장')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "PUT /api/v1/deposits/", + "toast": "수정|완료|성공" + }, + "expected": "수정 완료" + }, + { + "id": 15, + "phase": "UPDATE", + "name": "[UPDATE] 수정 결과 확인", + "action": "verify_detail", + "checks": [ + "금액: 150,000", + "메모: E2E 수정된 입금" + ], + "expected": "수정된 데이터 반영" + }, + { + "id": 16, + "phase": "DELETE", + "name": "[DELETE] 삭제 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('삭제')", + "expected": { + "confirm_dialog": true, + "dialog_message": "삭제|정말" + } + }, + { + "id": 17, + "phase": "DELETE", + "name": "[DELETE] 필수 검증 #6: 삭제 확인", + "action": "click_if_exists", + "target": "button:has-text('확인'), button:has-text('삭제')", + "verify": { + "api_call": "DELETE /api/v1/deposits/", + "toast": "삭제|완료|성공", + "redirect": "/accounting/deposits" + }, + "expected": "삭제 완료 및 목록 복귀" + }, + { + "id": 18, + "phase": "DELETE", + "name": "[DELETE] 삭제 결과 확인", + "action": "verify_detail", + "search": "E2E 수정된 입금", + "expected": { + "row_exists": false, + "message": "테스트 데이터가 목록에서 제거됨" + } + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/deposits", + "description": "입금 목록 조회" + }, + { + "method": "POST", + "endpoint": "/api/v1/deposits", + "description": "입금 등록" + }, + { + "method": "GET", + "endpoint": "/api/v1/deposits/{id}", + "description": "입금 상세 조회" + }, + { + "method": "PUT", + "endpoint": "/api/v1/deposits/{id}", + "description": "입금 수정" + }, + { + "method": "DELETE", + "endpoint": "/api/v1/deposits/{id}", + "description": "입금 삭제" + } + ], + "requiredVerifications": [ + { + "id": 2, + "name": "등록/저장 버튼", + "steps": [7, 14], + "criteria": "API 호출 + 성공 토스트 + 데이터 반영" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "입금 목록, 등록 버튼, 필터 존재" + }, + { + "id": 6, + "name": "삭제 기능", + "steps": [16, 17, 18], + "criteria": "DELETE API + 목록에서 제거" + } + ], + "rollbackPlan": { + "onCreateFail": "모달 닫기 → 다음 테스트 영향 없음", + "onUpdateFail": "테스트 데이터 수동 삭제 필요", + "onDeleteFail": "테스트 데이터 수동 삭제 필요", + "cleanupRequired": "E2E_TEST_ 접두사 데이터는 테스트 데이터" + } +} diff --git a/_backup_before_enhance/accounting-expense-forecast.json b/_backup_before_enhance/accounting-expense-forecast.json new file mode 100644 index 0000000..ae963b9 --- /dev/null +++ b/_backup_before_enhance/accounting-expense-forecast.json @@ -0,0 +1,193 @@ +{ + "id": "accounting-expense-forecast", + "name": "지출예상내역서 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "회계관리 > 지출예상내역서 메뉴의 지출 예상 조회/필터/인쇄 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "회계관리", + "level2": "지출예상내역서", + "expectedUrl": "/accounting/expected-expenses", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 회계관리 > 지출예상내역서", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "지출예상내역서", + "expected": { + "url_contains": "/accounting", + "visible": ["지출", "예상"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "지출 예상 내역 표시", + "기간 선택 가능", + "합계 금액 표시" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "지출예상 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "지출 항목 컬럼", + "예상일 컬럼", + "금액 컬럼", + "비고 컬럼" + ], + "expected": "지출예상 테이블 표시" + }, + { + "id": 4, + "name": "기간 선택 확인", + "action": "verify_elements", + "checks": [ + "기간 선택 가능 (월/주/일)" + ], + "expected": "기간 선택 가능" + }, + { + "id": 5, + "phase": "FILTER", + "name": "[FILTER] 월 선택", + "action": "click_if_exists", + "target": "input[type='month'], select[name*='month'], [class*='month-picker']", + "expected": "월 선택 열림" + }, + { + "id": 6, + "phase": "FILTER", + "name": "[FILTER] 조회 적용", + "action": "click_if_exists", + "target": "button:has-text('조회'), button:has-text('검색'), button:has-text('적용')", + "expected": "필터 적용됨" + }, + { + "id": 7, + "phase": "FILTER", + "name": "[FILTER] 필터 결과 확인", + "action": "verify_detail", + "checks": [ + "선택한 기간의 지출 예상 표시" + ], + "expected": "필터 동작 확인" + }, + { + "id": 8, + "name": "지출 카테고리별 확인", + "action": "verify_elements", + "checks": [ + "카테고리별 지출 예상 표시" + ], + "expected": "카테고리별 지출 표시" + }, + { + "id": 9, + "name": "합계 금액 확인", + "action": "verify_detail", + "checks": [ + "총 지출 예상 금액 표시" + ], + "expected": "합계 금액 표시" + }, + { + "id": 10, + "name": "일별 지출 예상 확인", + "action": "verify_elements", + "checks": [ + "일별 지출 예상 캘린더 또는 리스트" + ], + "expected": "일별 지출 표시" + }, + { + "id": 11, + "name": "주요 지출 항목 확인", + "action": "verify_elements", + "checks": [ + "주요 지출 항목 하이라이트" + ], + "expected": "주요 항목 표시" + }, + { + "id": 12, + "name": "인쇄 버튼 확인", + "action": "verify_elements", + "checks": [ + "인쇄 버튼 존재" + ], + "expected": "인쇄 기능 표시" + }, + { + "id": 13, + "name": "엑셀 다운로드 버튼 확인", + "action": "verify_elements", + "checks": [ + "엑셀 다운로드 버튼 존재" + ], + "expected": "엑셀 다운로드 기능 표시" + }, + { + "id": 14, + "name": "PDF 내보내기 확인", + "action": "verify_elements", + "checks": [ + "PDF 내보내기 버튼 존재" + ], + "expected": "PDF 내보내기 기능 표시" + }, + { + "id": 15, + "name": "이전/다음 기간 네비게이션", + "action": "verify_elements", + "checks": [ + "이전/다음 기간 이동 가능" + ], + "expected": "기간 네비게이션 표시" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/accounting/expected-expenses", + "description": "지출예상 목록 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/accounting/expected-expenses/summary", + "description": "지출예상 요약 조회" + } + ], + "requiredVerifications": [ + { + "id": 3, + "name": "검색/필터", + "steps": [5, 6, 7], + "criteria": "기간 필터 동작" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "지출예상 목록, 기간 선택, 합계 표시 존재" + } + ], + "rollbackPlan": { + "note": "조회 전용 페이지로 데이터 변경 없음" + } +} diff --git a/_backup_before_enhance/accounting-payment.json b/_backup_before_enhance/accounting-payment.json new file mode 100644 index 0000000..39b5b1c --- /dev/null +++ b/_backup_before_enhance/accounting-payment.json @@ -0,0 +1,211 @@ +{ + "id": "accounting-payment", + "name": "결제내역 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "회계관리 > 결제내역 메뉴의 결제 내역 조회/필터/검색/다운로드 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "회계관리", + "level2": "결제내역", + "expectedUrl": "/payment-history", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 회계관리 > 결제내역", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "결제내역", + "expected": { + "url_contains": "/payment", + "visible": ["결제내역", "결제"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "결제 내역 목록 표시", + "기간 필터 존재", + "검색 기능 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "결제내역 페이지 구조 확인", + "action": "verify_elements", + "checks": [ + "시작일 선택", + "종료일 선택", + "결제방법 필터", + "조회 버튼" + ], + "expected": "결제내역 조회 폼 정상 표시" + }, + { + "id": 4, + "phase": "FILTER", + "name": "[FILTER] 기간 필터 - 시작일", + "action": "click_if_exists", + "target": "input[type='date']:first-of-type, input[name*='start']", + "value": "2025-01-01", + "expected": "시작일 입력" + }, + { + "id": 5, + "phase": "FILTER", + "name": "[FILTER] 기간 필터 - 종료일", + "action": "click_if_exists", + "target": "input[type='date']:last-of-type, input[name*='end']", + "value": "2025-12-31", + "expected": "종료일 입력" + }, + { + "id": 6, + "phase": "FILTER", + "name": "[FILTER] 조회 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('조회'), button:has-text('검색')", + "expected": { + "data_loaded": true, + "api_call": "GET /api/v1/payments" + } + }, + { + "id": 7, + "phase": "READ", + "name": "[READ] 결제 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "결제일 컬럼 표시", + "결제금액 컬럼 표시", + "결제방법 컬럼 표시", + "상태 컬럼 표시" + ], + "expected": "결제 테이블 정상 표시" + }, + { + "id": 8, + "phase": "READ", + "name": "[READ] 결제 데이터 표시 확인", + "action": "verify_detail", + "checks": [ + "테이블에 데이터 행 존재 또는 '데이터 없음' 메시지", + "금액 포맷 정상" + ], + "expected": "결제 데이터 정상 표시" + }, + { + "id": 9, + "phase": "FILTER", + "name": "[FILTER] 결제방법 필터 테스트", + "action": "click_if_exists", + "target": "select[name*='method'], button:has-text('결제방법'), [class*='filter']:has-text('방법')", + "expected": "결제방법 필터 옵션 표시" + }, + { + "id": 10, + "phase": "FILTER", + "name": "[FILTER] 상태 필터 테스트", + "action": "verify_elements", + "checks": [ + "완료/취소/대기 등 상태 필터 가능" + ], + "expected": "상태 필터 기능 확인" + }, + { + "id": 11, + "phase": "READ", + "name": "[READ] 결제 상세 조회", + "action": "click_if_exists", + "target": "table tbody tr:first-child, [class*='list'] [class*='item']:first-child", + "expected": { + "detail_view": true + } + }, + { + "id": 12, + "name": "결제 상세 정보 확인", + "action": "verify_detail", + "checks": [ + "결제일시 상세", + "결제금액 상세", + "결제방법 상세", + "거래번호/승인번호" + ], + "expected": "결제 상세 정보 표시" + }, + { + "id": 13, + "name": "목록으로 돌아가기", + "action": "click_if_exists", + "target": "button:has-text('목록'), button:has-text('뒤로'), [class*='back']", + "expected": "목록 페이지로 복귀" + }, + { + "id": 14, + "name": "필수 검증 #1: 엑셀 다운로드", + "action": "click_if_exists", + "target": "button:has-text('엑셀'), button:has-text('Excel'), button:has-text('다운로드')", + "verify": { + "api_call": "GET /api/v1/payments/export", + "file_download": true + }, + "expected": "엑셀 파일 다운로드" + }, + { + "id": 15, + "name": "합계 금액 표시 확인", + "action": "verify_elements", + "checks": [ + "총 결제금액 합계 표시" + ], + "expected": "합계 영역 표시" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/payments", + "description": "결제내역 목록 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/payments/:id", + "description": "결제 상세 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/payments/export", + "description": "결제내역 엑셀 다운로드" + } + ], + "requiredVerifications": [ + { + "id": 1, + "name": "엑셀 다운로드", + "steps": [14], + "criteria": "API 호출 + 파일 다운로드" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "결제 내역 목록, 기간 필터, 검색 기능 존재" + } + ], + "rollbackPlan": { + "note": "조회 전용 페이지로 데이터 변경 없음" + } +} diff --git a/_backup_before_enhance/accounting-purchase.json b/_backup_before_enhance/accounting-purchase.json new file mode 100644 index 0000000..c6ec3b7 --- /dev/null +++ b/_backup_before_enhance/accounting-purchase.json @@ -0,0 +1,196 @@ +{ + "id": "accounting-purchase", + "name": "매입관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "회계관리 > 매입관리 메뉴의 매입 조회/필터/엑셀 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "회계관리", + "level2": "매입관리", + "expectedUrl": "/accounting/purchase-accounting", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 회계관리 > 매입관리", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "매입관리", + "expected": { + "url_contains": "/accounting", + "visible": ["매입관리", "매입"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "매입 목록 표시", + "기간 필터 존재", + "합계 금액 표시" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "매입 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "거래일 컬럼", + "거래처 컬럼", + "품목 컬럼", + "금액 컬럼", + "부가세 컬럼" + ], + "expected": "매입 테이블 표시" + }, + { + "id": 4, + "name": "매입 통계 카드 확인", + "action": "verify_elements", + "checks": [ + "총 매입액 표시", + "당월 매입 표시", + "전월 대비 표시" + ], + "expected": "통계 카드 표시" + }, + { + "id": 5, + "phase": "FILTER", + "name": "[FILTER] 기간 필터 - 시작일", + "action": "click_if_exists", + "target": "input[type='date']:first-of-type, [class*='datepicker']:first-of-type", + "expected": "날짜 선택 열림" + }, + { + "id": 6, + "phase": "FILTER", + "name": "[FILTER] 기간 필터 적용", + "action": "click_if_exists", + "target": "button:has-text('조회'), button:has-text('검색'), button:has-text('적용')", + "expected": "필터 적용됨" + }, + { + "id": 7, + "phase": "FILTER", + "name": "[FILTER] 필터 결과 확인", + "action": "verify_detail", + "checks": [ + "필터된 매입 데이터 표시 또는 결과 없음" + ], + "expected": "필터 동작 확인" + }, + { + "id": 8, + "phase": "FILTER", + "name": "[FILTER] 거래처별 필터", + "action": "click_if_exists", + "target": "select[name*='vendor'], button:has-text('거래처')", + "expected": "거래처 필터 가능" + }, + { + "id": 9, + "phase": "READ", + "name": "[READ] 매입 상세 보기", + "action": "click_if_exists", + "target": "table tbody tr:first-child", + "expected": { + "detail_view": true + } + }, + { + "id": 10, + "name": "상세 정보 확인", + "action": "verify_detail", + "checks": [ + "거래 상세 정보 표시", + "품목 정보 표시", + "금액 상세 표시" + ], + "expected": "매입 상세 정보 표시" + }, + { + "id": 11, + "name": "목록으로 돌아가기", + "action": "click_if_exists", + "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", + "expected": "목록 페이지로 복귀" + }, + { + "id": 12, + "name": "매입 합계 확인", + "action": "verify_elements", + "checks": [ + "매입 합계 금액 표시" + ], + "expected": "매입 합계 표시" + }, + { + "id": 13, + "name": "엑셀 다운로드 버튼 확인", + "action": "verify_elements", + "checks": [ + "엑셀 다운로드 버튼 존재" + ], + "expected": "엑셀 다운로드 기능 표시" + }, + { + "id": 14, + "name": "인쇄 버튼 확인", + "action": "verify_elements", + "checks": [ + "인쇄 버튼 존재" + ], + "expected": "인쇄 기능 표시" + }, + { + "id": 15, + "name": "페이지네이션 확인", + "action": "verify_elements", + "checks": [ + "페이지 번호 표시" + ], + "expected": "페이지네이션 표시" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/accounting/purchases", + "description": "매입 목록 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/accounting/purchases/summary", + "description": "매입 요약 조회" + } + ], + "requiredVerifications": [ + { + "id": 3, + "name": "검색/필터", + "steps": [5, 6, 7, 8], + "criteria": "기간 필터 + 거래처 필터 동작" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "매입 목록, 기간 필터, 합계 표시 존재" + } + ], + "rollbackPlan": { + "note": "조회 전용 페이지로 데이터 변경 없음" + } +} diff --git a/_backup_before_enhance/accounting-receivable.json b/_backup_before_enhance/accounting-receivable.json new file mode 100644 index 0000000..d362643 --- /dev/null +++ b/_backup_before_enhance/accounting-receivable.json @@ -0,0 +1,241 @@ +{ + "id": "accounting-receivable", + "name": "미수금현황 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "회계관리 > 미수금현황 메뉴의 미수금 조회/필터/검색/엑셀 다운로드 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "회계관리", + "level2": "미수금현황", + "expectedUrl": "/accounting/receivables-status", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "testData": { + "filterStartDate": "2026-01-01", + "filterEndDate": "2026-02-28", + "searchKeyword": "테스트" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 회계관리 > 미수금현황", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "미수금현황", + "expected": { + "url_contains": "/accounting/receivables", + "visible": ["미수금현황", "미수금"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "미수금 목록 표시", + "검색/필터 기능 존재", + "날짜 선택 가능" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "미수금 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "거래처 컬럼", + "미수금액 컬럼", + "매출일 컬럼", + "입금예정일 컬럼", + "연체일수 컬럼" + ], + "expected": "미수금 테이블 컬럼 정상 표시" + }, + { + "id": 4, + "name": "통계 카드 확인", + "action": "verify_elements", + "checks": [ + "총 미수금액 표시", + "정상 미수금 표시", + "연체 미수금 표시" + ], + "expected": "미수금 통계 카드 표시" + }, + { + "id": 5, + "phase": "FILTER", + "name": "[FILTER] 기간 필터 적용", + "actions": [ + { "type": "click_if_exists", "target": "input[type='date']:first-of-type, input[placeholder*='시작'], input[name*='start']" }, + { "type": "click_if_exists", "target": "input[type='date']:last-of-type, input[placeholder*='종료'], input[name*='end']" }, + { "type": "wait", "duration": 500 } + ], + "expected": { + "filter_applied": true + } + }, + { + "id": 6, + "phase": "FILTER", + "name": "[FILTER] 필터 결과 확인", + "action": "verify_detail", + "expected": { + "data_filtered": true, + "table_updated": true + } + }, + { + "id": 7, + "phase": "SEARCH", + "name": "[SEARCH] 거래처 검색", + "action": "click_if_exists", + "target": "input[placeholder*='검색'], input[placeholder*='거래처']", + "value": "테스트", + "expected": { + "data_filtered": true + } + }, + { + "id": 8, + "phase": "SEARCH", + "name": "[SEARCH] 검색 결과 확인", + "action": "verify_detail", + "search": "테스트", + "expected": { + "row_exists": true, + "filtered_by_keyword": true + } + }, + { + "id": 9, + "phase": "READ", + "name": "[READ] 미수금 상세 클릭", + "action": "click_if_exists", + "target": "table tbody tr:first-child", + "expected": { + "detail_modal_or_page": true, + "visible": ["거래처", "미수금액", "상세"] + } + }, + { + "id": 10, + "phase": "READ", + "name": "[READ] 상세 정보 확인", + "action": "verify_detail", + "checks": [ + "거래처명 표시", + "미수금액 표시", + "매출 내역 표시" + ], + "expected": "미수금 상세 정보 정상 표시" + }, + { + "id": 11, + "phase": "READ", + "name": "[READ] 목록으로 복귀", + "action": "click_if_exists", + "target": "button:has-text('목록'), button:has-text('목록으로'), button:has-text('닫기'), button:has-text('뒤로')", + "expected": { + "url_contains": "/accounting/receivables" + } + }, + { + "id": 12, + "phase": "EXPORT", + "name": "[EXPORT] 엑셀 다운로드 버튼 확인", + "action": "verify_elements", + "checks": [ + "엑셀 다운로드 버튼 존재" + ], + "expected": "다운로드 기능 존재" + }, + { + "id": 13, + "phase": "EXPORT", + "name": "[EXPORT] 필수 검증 #1: 엑셀 다운로드", + "action": "click_if_exists", + "target": "button:has-text('엑셀'), button:has-text('다운로드'), button:has-text('내보내기')", + "verify": { + "file_download": true, + "file_type": "xlsx", + "api_call": "GET /api/v1/receivables/export" + }, + "expected": "엑셀 파일 다운로드" + }, + { + "id": 14, + "phase": "SORT", + "name": "[SORT] 컬럼 정렬 테스트", + "action": "click_if_exists", + "target": "th:has-text('미수금액'), th:has-text('미수금'), th:has-text('금액')", + "expected": { + "sort_applied": true, + "data_reordered": true + } + }, + { + "id": 15, + "name": "연체 현황 탭 확인", + "action": "click_if_exists", + "target": "button:has-text('연체'), [role='tab']:has-text('연체')", + "expected": { + "tab_active": true, + "filtered_data": true + } + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/receivables", + "description": "미수금 목록 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/receivables/summary", + "description": "미수금 통계 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/receivables/{id}", + "description": "미수금 상세 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/receivables/export", + "description": "미수금 엑셀 다운로드" + } + ], + "requiredVerifications": [ + { + "id": 1, + "name": "파일 다운로드", + "steps": [13], + "criteria": "엑셀 파일 다운로드 동작" + }, + { + "id": 3, + "name": "검색/필터", + "steps": [5, 6, 7, 8], + "criteria": "기간 필터 및 검색 기능" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "미수금 목록, 필터, 다운로드 버튼 존재" + } + ], + "rollbackPlan": { + "note": "미수금현황은 조회 전용 페이지로 CRUD 없음 (데이터 생성/수정/삭제 없음)" + } +} diff --git a/_backup_before_enhance/accounting-sales.json b/_backup_before_enhance/accounting-sales.json new file mode 100644 index 0000000..a8116a2 --- /dev/null +++ b/_backup_before_enhance/accounting-sales.json @@ -0,0 +1,196 @@ +{ + "id": "accounting-sales", + "name": "매출관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "회계관리 > 매출관리 메뉴의 매출 조회/필터/엑셀 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "회계관리", + "level2": "매출관리", + "expectedUrl": "/accounting/sales-accounting", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 회계관리 > 매출관리", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "매출관리", + "expected": { + "url_contains": "/accounting", + "visible": ["매출관리", "매출"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "매출 목록 표시", + "기간 필터 존재", + "합계 금액 표시" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "매출 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "거래일 컬럼", + "거래처 컬럼", + "품목 컬럼", + "금액 컬럼", + "부가세 컬럼" + ], + "expected": "매출 테이블 표시" + }, + { + "id": 4, + "name": "매출 통계 카드 확인", + "action": "verify_elements", + "checks": [ + "총 매출액 표시", + "당월 매출 표시", + "전월 대비 표시" + ], + "expected": "통계 카드 표시" + }, + { + "id": 5, + "phase": "FILTER", + "name": "[FILTER] 기간 필터 - 시작일", + "action": "click_if_exists", + "target": "input[type='date']:first-of-type, [class*='datepicker']:first-of-type", + "expected": "날짜 선택 열림" + }, + { + "id": 6, + "phase": "FILTER", + "name": "[FILTER] 기간 필터 적용", + "action": "click_if_exists", + "target": "button:has-text('조회'), button:has-text('검색'), button:has-text('적용')", + "expected": "필터 적용됨" + }, + { + "id": 7, + "phase": "FILTER", + "name": "[FILTER] 필터 결과 확인", + "action": "verify_detail", + "checks": [ + "필터된 매출 데이터 표시 또는 결과 없음" + ], + "expected": "필터 동작 확인" + }, + { + "id": 8, + "phase": "FILTER", + "name": "[FILTER] 거래처별 필터", + "action": "click_if_exists", + "target": "select[name*='vendor'], button:has-text('거래처')", + "expected": "거래처 필터 가능" + }, + { + "id": 9, + "phase": "READ", + "name": "[READ] 매출 상세 보기", + "action": "click_if_exists", + "target": "table tbody tr:first-child", + "expected": { + "detail_view": true + } + }, + { + "id": 10, + "name": "상세 정보 확인", + "action": "verify_detail", + "checks": [ + "거래 상세 정보 표시", + "품목 정보 표시", + "금액 상세 표시" + ], + "expected": "매출 상세 정보 표시" + }, + { + "id": 11, + "name": "목록으로 돌아가기", + "action": "click_if_exists", + "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", + "expected": "목록 페이지로 복귀" + }, + { + "id": 12, + "name": "매출 합계 확인", + "action": "verify_elements", + "checks": [ + "매출 합계 금액 표시" + ], + "expected": "매출 합계 표시" + }, + { + "id": 13, + "name": "엑셀 다운로드 버튼 확인", + "action": "verify_elements", + "checks": [ + "엑셀 다운로드 버튼 존재" + ], + "expected": "엑셀 다운로드 기능 표시" + }, + { + "id": 14, + "name": "인쇄 버튼 확인", + "action": "verify_elements", + "checks": [ + "인쇄 버튼 존재" + ], + "expected": "인쇄 기능 표시" + }, + { + "id": 15, + "name": "페이지네이션 확인", + "action": "verify_elements", + "checks": [ + "페이지 번호 표시" + ], + "expected": "페이지네이션 표시" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/accounting/sales", + "description": "매출 목록 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/accounting/sales/summary", + "description": "매출 요약 조회" + } + ], + "requiredVerifications": [ + { + "id": 3, + "name": "검색/필터", + "steps": [5, 6, 7, 8], + "criteria": "기간 필터 + 거래처 필터 동작" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "매출 목록, 기간 필터, 합계 표시 존재" + } + ], + "rollbackPlan": { + "note": "조회 전용 페이지로 데이터 변경 없음" + } +} diff --git a/_backup_before_enhance/accounting-withdrawal.json b/_backup_before_enhance/accounting-withdrawal.json new file mode 100644 index 0000000..f3988c6 --- /dev/null +++ b/_backup_before_enhance/accounting-withdrawal.json @@ -0,0 +1,302 @@ +{ + "id": "accounting-withdrawal", + "name": "출금관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "회계관리 > 출금관리 메뉴의 출금 조회/등록/수정/삭제 전체 CRUD 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "회계관리", + "level2": "출금관리", + "expectedUrl": "/accounting/withdrawals", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "testData": { + "create": { + "vendorName": "E2E_TEST_출금거래처", + "amount": "50000", + "withdrawalMethod": "계좌이체", + "memo": "E2E 자동화 테스트 출금" + }, + "update": { + "amount": "75000", + "memo": "E2E 수정된 출금 메모" + } + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 회계관리 > 출금관리", + "action": "menu_navigate", + "level1": "회계관리", + "level2": "출금관리", + "expected": { + "url_contains": "/accounting/withdrawals", + "visible": ["출금관리", "출금"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "출금 목록 표시", + "출금 등록 버튼 존재", + "검색/필터 기능 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "출금 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "출금일 컬럼", + "거래처 컬럼", + "금액 컬럼", + "출금방법 컬럼", + "상태 컬럼" + ], + "expected": "출금 테이블 컬럼 정상 표시" + }, + { + "id": 4, + "name": "검색 기능 테스트", + "action": "click_if_exists", + "target": "input[placeholder*='검색']", + "value": "테스트", + "expected": { + "data_filtered": true + } + }, + { + "id": 5, + "phase": "CREATE", + "name": "[CREATE] 출금 등록 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('등록'), button:has-text('출금 등록'), button:has-text('추가')", + "expected": { + "modal": true, + "modalTitle": "출금 등록" + } + }, + { + "id": 6, + "phase": "CREATE", + "name": "[CREATE] 출금 정보 입력", + "action": "fill_form", + "fields": [ + {"name": "거래처", "type": "select", "value": "E2E_TEST_출금거래처"}, + {"name": "출금일", "type": "date", "value": "2026-02-03"}, + {"name": "금액", "type": "number", "value": "50000"}, + {"name": "출금방법", "type": "select", "value": "계좌이체"}, + {"name": "메모", "type": "text", "value": "E2E 자동화 테스트 출금_{timestamp}"} + ], + "note": "타임스탬프로 고유성 보장" + }, + { + "id": 7, + "phase": "CREATE", + "name": "[CREATE] 필수 검증 #2: 등록 저장", + "action": "click_if_exists", + "target": "button:has-text('저장'), button:has-text('등록')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "POST /api/v1/withdrawals", + "toast": "등록|완료|성공" + }, + "expected": "출금 등록 완료" + }, + { + "id": "7-modal-close", + "phase": "CREATE", + "name": "[CREATE] 모달 닫기 확인", + "action": "close_modal_if_open", + "expected": "모달 닫힘" + }, + { + "id": 8, + "phase": "CREATE", + "name": "[CREATE] 등록 결과 확인", + "action": "verify_detail", + "search": "E2E 자동화 테스트 출금", + "expected": { + "row_exists": true, + "contains": ["E2E", "50,000"] + } + }, + { + "id": 9, + "phase": "READ", + "name": "[READ] 출금 상세 페이지 진입", + "action": "click_if_exists", + "target": "table tbody tr:has-text('E2E')", + "expected": { + "url_contains": "/accounting/withdrawals/", + "visible": ["출금 상세", "수정", "삭제"] + } + }, + { + "id": 10, + "phase": "READ", + "name": "[READ] 상세 정보 확인", + "action": "verify_detail", + "checks": [ + "금액: 50,000", + "출금방법: 계좌이체", + "메모: E2E 자동화 테스트" + ], + "expected": "입력한 데이터와 일치" + }, + { + "id": 11, + "phase": "UPDATE", + "name": "[UPDATE] 수정 모드 진입", + "action": "click_if_exists", + "target": "button:has-text('수정')", + "expected": { + "url_contains": "mode=edit", + "fields_editable": true + } + }, + { + "id": 12, + "phase": "UPDATE", + "name": "[UPDATE] 금액 수정", + "action": "click_if_exists", + "target": "input[name*='amount'], input[placeholder*='금액']", + "value": "75000", + "clear": true + }, + { + "id": 13, + "phase": "UPDATE", + "name": "[UPDATE] 메모 수정", + "action": "click_if_exists", + "target": "textarea[name*='memo'], input[placeholder*='메모']", + "value": "E2E 수정된 출금 메모_{timestamp}", + "clear": true + }, + { + "id": 14, + "phase": "UPDATE", + "name": "[UPDATE] 필수 검증 #2: 수정 저장", + "action": "click_if_exists", + "target": "button:has-text('저장')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "PUT /api/v1/withdrawals/", + "toast": "수정|완료|성공" + }, + "expected": "수정 완료" + }, + { + "id": 15, + "phase": "UPDATE", + "name": "[UPDATE] 수정 결과 확인", + "action": "verify_detail", + "checks": [ + "금액: 75,000", + "메모: E2E 수정된 출금" + ], + "expected": "수정된 데이터 반영" + }, + { + "id": 16, + "phase": "DELETE", + "name": "[DELETE] 삭제 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('삭제')", + "expected": { + "confirm_dialog": true, + "dialog_message": "삭제|정말" + } + }, + { + "id": 17, + "phase": "DELETE", + "name": "[DELETE] 필수 검증 #6: 삭제 확인", + "action": "click_if_exists", + "target": "button:has-text('확인'), button:has-text('삭제')", + "verify": { + "api_call": "DELETE /api/v1/withdrawals/", + "toast": "삭제|완료|성공", + "redirect": "/accounting/withdrawals" + }, + "expected": "삭제 완료 및 목록 복귀" + }, + { + "id": 18, + "phase": "DELETE", + "name": "[DELETE] 삭제 결과 확인", + "action": "verify_detail", + "search": "E2E 수정된 출금", + "expected": { + "row_exists": false, + "message": "테스트 데이터가 목록에서 제거됨" + } + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/withdrawals", + "description": "출금 목록 조회" + }, + { + "method": "POST", + "endpoint": "/api/v1/withdrawals", + "description": "출금 등록" + }, + { + "method": "GET", + "endpoint": "/api/v1/withdrawals/{id}", + "description": "출금 상세 조회" + }, + { + "method": "PUT", + "endpoint": "/api/v1/withdrawals/{id}", + "description": "출금 수정" + }, + { + "method": "DELETE", + "endpoint": "/api/v1/withdrawals/{id}", + "description": "출금 삭제" + } + ], + "requiredVerifications": [ + { + "id": 2, + "name": "등록/저장 버튼", + "steps": [7, 14], + "criteria": "API 호출 + 성공 토스트 + 데이터 반영" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "출금 목록, 등록 버튼, 필터 존재" + }, + { + "id": 6, + "name": "삭제 기능", + "steps": [16, 17, 18], + "criteria": "DELETE API + 목록에서 제거" + } + ], + "rollbackPlan": { + "onCreateFail": "모달 닫기 → 다음 테스트 영향 없음", + "onUpdateFail": "테스트 데이터 수동 삭제 필요", + "onDeleteFail": "테스트 데이터 수동 삭제 필요", + "cleanupRequired": "E2E_TEST_ 접두사 데이터는 테스트 데이터" + } +} diff --git a/_backup_before_enhance/approval-box.json b/_backup_before_enhance/approval-box.json new file mode 100644 index 0000000..0d58d12 --- /dev/null +++ b/_backup_before_enhance/approval-box.json @@ -0,0 +1,679 @@ +{ + "id": "approval-box", + "name": "결재함 E2E 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] + }, + "description": "결재함 페이지의 전체 기능을 검증합니다 (탭 전환, 검색, 필터, 승인/반려, 모달)", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "결재관리", + "level2": "결재함", + "expectedUrl": "/ko/approval/inbox", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "navigation": { + "targetUrl": "/approval/inbox", + "urlPattern": "/approval/inbox|/ko/approval/inbox", + "menuHints": [ + "결재함", + "결재 함", + "결재관리" + ] + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", + "level1": "결재관리", + "level2": "결재함", + "alternativeLevel2Names": [ + "결재함", + "결재 함", + "승인함", + "Approval Box", + "inbox" + ], + "fallbackUrls": [ + "/ko/approval/inbox", + "/ko/approval/box", + "/ko/approvals/inbox", + "/ko/approval-management/inbox", + "/approval/inbox" + ], + "scrollConfig": { + "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", + "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + } + }, + "steps": [ + { + "id": 0, + "name": "사이드바 메뉴 전체 펼치기", + "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", + "actions": [ + { + "type": "scroll", + "target": "sidebar", + "direction": "top", + "description": "사이드바 최상단으로 스크롤" + }, + { + "type": "wait", + "duration": 300 + }, + { + "type": "evaluate", + "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" + }, + { + "type": "wait", + "duration": 2000 + } + ], + "verification": [ + "사이드바가 화면에 보이는지 확인", + "모든 메뉴가 펼쳐졌는지 확인" + ] + }, + { + "id": 1, + "name": "1차 메뉴 찾기: 결재관리 (스크롤 포함)", + "description": "사이드바를 스크롤하며 '결재관리' 메뉴를 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "결재관리", + "alternativeTexts": [ + "결재관리", + "결재 관리", + "Approval", + "전자결재" + ], + "scrollContainer": "sidebar", + "maxAttempts": 10, + "description": "스크롤하며 결재관리 메뉴 찾기" + }, + { + "type": "wait", + "duration": 300 + }, + { + "type": "click_if_exists", + "target": "결재관리", + "description": "결재관리 메뉴 클릭" + }, + { + "type": "wait", + "duration": 500, + "description": "서브메뉴 펼쳐지기 대기" + }, + { + "type": "screenshot", + "name": "approval_menu_expanded" + } + ], + "verification": [ + "결재관리 메뉴가 클릭되었는지 확인", + "서브메뉴가 펼쳐졌는지 확인", + "하위 메뉴 항목들이 보이는지 확인" + ], + "fallback": { + "if": "메뉴를 찾을 수 없음", + "then": "사이드바 전체를 스크롤하며 재탐색" + } + }, + { + "id": 2, + "name": "2차 메뉴 찾기: 결재함 (스크롤 포함)", + "description": "서브메뉴에서 '결재함'을 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "결재함", + "alternativeTexts": [ + "결재함", + "결재 함", + "Inbox", + "승인함" + ], + "scrollContainer": "submenu", + "maxAttempts": 5, + "description": "서브메뉴에서 결재함 찾기" + }, + { + "type": "wait", + "duration": 200 + }, + { + "type": "click_if_exists", + "target": "결재함", + "description": "결재함 메뉴 클릭" + }, + { + "type": "wait", + "target": "페이지 로드 완료", + "timeout": 10000 + }, + { + "type": "screenshot", + "name": "approval_box_page" + } + ], + "verification": [ + "결재함 메뉴 클릭 성공", + "페이지 이동 또는 컨텐츠 로드" + ] + }, + { + "id": 3, + "name": "404 에러 감지 및 대체 경로 시도", + "description": "페이지 로드 후 404 에러 여부 확인, 404시 대체 경로 탐색", + "actions": [ + { + "type": "wait", + "duration": 1000 + }, + { + "type": "checkFor404", + "indicators": [ + "페이지를 찾을 수 없습니다", + "404", + "Not Found", + "존재하지 않거나" + ] + }, + { + "type": "screenshot", + "name": "page_load_result" + } + ], + "verification": [ + "현재 페이지가 404인지 확인" + ], + "onError404": { + "description": "404 에러 발생 시 대체 URL 시도", + "actions": [ + { + "type": "log", + "message": "404 감지 - 대체 경로 탐색 시작" + }, + { + "type": "tryAlternativeUrls", + "urls": [ + "/ko/approval/inbox", + "/ko/approvals/inbox", + "/ko/approval-box" + ], + "stopOnSuccess": true + }, + { + "type": "ifStillFailed", + "action": "navigateViaMenuClick", + "description": "URL 직접 접근 실패 시 메뉴 클릭으로 재시도" + } + ] + } + }, + { + "id": 4, + "name": "페이지 정상 로드 확인", + "description": "결재함 페이지가 정상적으로 로드되었는지 확인", + "actions": [ + { + "type": "verify", + "target": "pageTitle", + "contains": [ + "결재함", + "결재", + "Approval" + ] + }, + { + "type": "verify", + "target": "pageContent", + "notContains": [ + "404", + "찾을 수 없습니다", + "Not Found" + ] + } + ], + "verification": [ + "페이지 제목 '결재함' 또는 관련 텍스트 표시", + "404 에러 메시지 미표시", + "콘텐츠가 정상 렌더링됨" + ], + "successCriteria": { + "urlPattern": "/approval", + "requiredElements": [ + "결재", + "문서", + "승인" + ] + } + }, + { + "id": 5, + "name": "통계 카드 확인", + "action": "verify_element", + "target": "[class*='card'], [class*='stat']", + "verification": [ + "전체결재 건수 기록", + "미결재 건수 기록", + "결재완료 건수 기록", + "결재반려 건수 기록" + ] + }, + { + "id": 6, + "name": "탭 구조 확인", + "action": "verify_element", + "target": "[role='tab'], button[role='tab']", + "verification": [ + "'전체결재' 탭 존재 확인", + "'미결재' 탭 존재 확인", + "'결재완료' 탭 존재 확인", + "'결재반려' 탭 존재 확인" + ] + }, + { + "id": 7, + "name": "테이블 데이터 확인", + "action": "verify_table", + "target": "table", + "verification": [ + "테이블 헤더 컬럼 확인", + "데이터 행 존재 여부 확인", + "페이지네이션 표시 확인" + ] + }, + { + "id": 8, + "name": "⚠️ 필수 검증: 결재 문서 상세 보기", + "description": "테이블에서 결재 문서 클릭하여 상세 모달/페이지 확인", + "actions": [ + { + "type": "click_if_exists", + "target": "미결재 탭", + "description": "미결재 탭으로 이동" + }, + { + "type": "wait", + "duration": 500 + }, + { + "type": "click_if_exists", + "target": "첫 번째 결재 문서 행", + "description": "결재 문서 클릭" + }, + { + "type": "wait", + "target": "상세 모달 또는 페이지" + } + ], + "expect": { + "detailView": true, + "fields": [ + "문서 제목", + "기안자", + "기안일", + "결재 상태" + ], + "buttons": [ + "승인", + "반려", + "PDF", + "인쇄" + ] + }, + "note": "결재 문서가 없으면 데이터 생성 또는 SKIP" + }, + { + "id": "8-pdf-1", + "name": "⚠️ 필수 검증: PDF 다운로드 전 모달 스크린샷", + "description": "PDF 생성 전 모달 상태를 스크린샷으로 캡처하여 CSS 문제 감지용 기준 이미지 확보", + "prerequisite": "step-8의 문서 상세 모달이 열려있는 상태에서 실행", + "actions": [ + { + "type": "screenshot", + "name": "pdf-preview-before-download-approval-box", + "fullPage": false, + "selector": "[role='dialog'], .modal, [data-state='open']", + "savePath": "tests/e2e/results/hotfix/screenshots/", + "description": "PDF 생성 대상 모달 전체 캡처" + } + ], + "verify": { + "screenshotCaptured": true, + "purpose": "PDF CSS 문제 감지를 위한 기준 이미지" + } + }, + { + "id": "8-pdf-2", + "name": "⚠️ 필수 검증: PDF 다운로드 실행 및 파일 보관", + "description": "PDF 다운로드 후 파일을 지정 폴더에 보관하여 수동 검증 가능하게 함", + "actions": [ + { + "type": "verify", + "target": "PDF 버튼 존재", + "selector": "button:has-text('PDF'), [aria-label*='PDF']", + "description": "PDF 다운로드 버튼 존재 확인" + }, + { + "type": "expectResponse", + "id": "pdf-download-response-approval-box", + "urlPattern": "/api/v1/approvals/*/pdf", + "description": "PDF 다운로드 API 응답 대기 설정" + }, + { + "type": "click_if_exists", + "target": "PDF 버튼", + "selector": "button:has-text('PDF')", + "description": "PDF 다운로드 버튼 클릭" + }, + { + "type": "wait", + "duration": 3000, + "description": "PDF 생성 및 다운로드 대기" + }, + { + "type": "assertResponse", + "id": "pdf-download-response-approval-box", + "checks": { + "status": 200, + "contentType": "application/pdf" + } + }, + { + "type": "saveDownloadedFile", + "targetPath": "tests/e2e/results/hotfix/pdf-samples/", + "fileNamePattern": "approval-box-{timestamp}.pdf", + "description": "다운로드된 PDF 파일을 지정 폴더에 보관" + } + ], + "verify": { + "apiSuccess": true, + "fileDownloaded": true, + "fileSaved": "tests/e2e/results/hotfix/pdf-samples/" + } + }, + { + "id": "8-pdf-3", + "name": "⚠️ PDF 파일 유효성 검증", + "description": "다운로드된 PDF 파일의 기본 유효성 검사", + "actions": [ + { + "type": "verifyDownloadedFile", + "checks": { + "fileExists": true, + "fileSize": "> 1024", + "pdfSignature": "%PDF-", + "description": "PDF 파일 헤더 검증" + } + } + ], + "verify": { + "pdfValid": true, + "minFileSize": "1KB 이상" + } + }, + { + "id": "8-pdf-4", + "name": "📋 PDF 스타일 수동 확인 체크리스트", + "type": "manualVerification", + "description": "개발자가 다운로드된 PDF를 열어 시각적으로 확인해야 하는 항목", + "manualChecklist": [ + { + "id": "css-1", + "item": "테이블 경계선이 올바르게 표시되는가?", + "category": "테이블 스타일" + }, + { + "id": "css-2", + "item": "한글 폰트가 깨지지 않고 정상 표시되는가?", + "category": "폰트" + }, + { + "id": "css-3", + "item": "숫자/금액 정렬이 올바른가? (우측 정렬)", + "category": "정렬" + }, + { + "id": "css-4", + "item": "여백(margin/padding)이 적절한가?", + "category": "레이아웃" + }, + { + "id": "css-5", + "item": "헤더/푸터가 각 페이지에 올바르게 표시되는가?", + "category": "페이지 구조" + }, + { + "id": "css-6", + "item": "로고/이미지가 정상 표시되는가?", + "category": "이미지" + }, + { + "id": "css-7", + "item": "페이지 나눔(page break)이 적절한 위치에서 발생하는가?", + "category": "페이지 나눔" + }, + { + "id": "css-8", + "item": "배경색/강조색이 올바르게 적용되었는가?", + "category": "색상" + }, + { + "id": "css-9", + "item": "텍스트가 잘리거나 겹치지 않는가?", + "category": "오버플로우" + }, + { + "id": "css-10", + "item": "결재선 정보가 정상적으로 표시되는가?", + "category": "결재선" + } + ], + "outputFiles": { + "screenshot": "tests/e2e/results/hotfix/screenshots/pdf-preview-before-download-approval-box-*.png", + "pdfFile": "tests/e2e/results/hotfix/pdf-samples/approval-box-*.pdf" + }, + "reportFlag": { + "requiresManualReview": true, + "message": "⚠️ PDF 스타일 수동 확인 필요 - 위 체크리스트 항목을 PDF 파일에서 직접 확인하세요" + } + }, + { + "id": 9, + "name": "⚠️ 필수 검증 #4: 결재 승인 실제 수행", + "description": "미결재 문서에 대해 실제 승인 처리 수행", + "actions": [ + { + "type": "verify", + "target": "승인 버튼 존재" + }, + { + "type": "click_if_exists", + "target": "승인 버튼", + "description": "결재 승인 클릭" + }, + { + "type": "wait", + "target": "확인 다이얼로그" + }, + { + "type": "click_if_exists", + "target": "확인", + "description": "승인 확인" + } + ], + "expect": { + "urlMaintained": true, + "noErrorPage": true, + "apiCall": "POST /api/v1/approvals/{id}/approve", + "toast": "승인되었습니다", + "statusChange": "미결재 → 결재완료" + }, + "note": "⚠️ 버튼 존재만 확인하면 불완전! 실제 승인까지 검증 필수!" + }, + { + "id": "9-1", + "name": "결재 승인 결과 확인", + "description": "승인 후 결재완료 탭에서 해당 문서 확인", + "actions": [ + { + "type": "click_if_exists", + "target": "결재완료 탭" + }, + { + "type": "wait", + "duration": 500 + } + ], + "verify": { + "documentMoved": "승인한 문서가 결재완료 탭에 표시", + "statusUpdated": "결재 상태가 '완료'로 변경" + } + }, + { + "id": 10, + "name": "⚠️ 필수 검증 #4: 결재 반려 실제 수행", + "description": "미결재 문서에 대해 실제 반려 처리 수행", + "actions": [ + { + "type": "click_if_exists", + "target": "미결재 탭", + "description": "미결재 탭으로 이동" + }, + { + "type": "wait", + "duration": 500 + }, + { + "type": "click_if_exists", + "target": "결재 문서 행", + "description": "결재 문서 선택" + }, + { + "type": "wait", + "target": "상세 보기" + }, + { + "type": "click_if_exists", + "target": "반려 버튼", + "description": "결재 반려 클릭" + }, + { + "type": "wait", + "target": "반려 사유 입력 모달" + }, + { + "type": "click_if_exists", + "target": "반려 사유" + }, + { + "type": "click_if_exists", + "target": "확인", + "description": "반려 확인" + } + ], + "expect": { + "urlMaintained": true, + "noErrorPage": true, + "apiCall": "POST /api/v1/approvals/{id}/reject", + "toast": "반려되었습니다", + "statusChange": "미결재 → 결재반려" + }, + "note": "⚠️ 반려 버튼 존재만 확인하면 불완전! 실제 반려까지 검증 필수!" + }, + { + "id": "10-1", + "name": "결재 반려 결과 확인", + "description": "반려 후 결재반려 탭에서 해당 문서 확인", + "actions": [ + { + "type": "click_if_exists", + "target": "결재반려 탭" + }, + { + "type": "wait", + "duration": 500 + } + ], + "verify": { + "documentMoved": "반려한 문서가 결재반려 탭에 표시", + "statusUpdated": "결재 상태가 '반려'로 변경", + "rejectReason": "반려 사유가 표시" + } + }, + { + "id": 11, + "name": "검색 기능 테스트", + "description": "검색 필터로 결재 문서 검색", + "actions": [ + { + "type": "click_if_exists", + "target": "전체결재 탭" + }, + { + "type": "click_if_exists", + "target": "검색 입력창" + }, + { + "type": "click_if_exists", + "target": "검색 버튼" + } + ], + "verify": { + "searchApplied": true, + "filteredResults": "검색어에 맞는 결과 표시" + } + } + ], + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 4, + "name": "결재 승인/반려 완료", + "trigger": "결재 문서 상세의 승인/반려 버튼", + "verification": "실제 승인/반려 동작 + API 호출 + 결과 확인", + "failCondition": "버튼 존재만 확인, 클릭하지 않음", + "steps": [ + "9", + "10" + ] + } + ] + }, + "expectedAPIs": [ + "GET /api/v1/approvals/inbox - 결재함 목록 조회", + "GET /api/v1/approvals/inbox/summary - 결재함 통계", + "GET /api/v1/approvals/{id} - 결재 문서 상세 조회", + "POST /api/v1/approvals/{id}/approve - 결재 승인", + "POST /api/v1/approvals/{id}/reject - 결재 반려" + ], + "notes": [ + "⚠️ 404 방지: 반드시 메뉴 클릭으로 페이지 진입 (직접 URL 접근 금지)", + "⚠️ 스크롤 필수: 사이드바가 길 경우 메뉴가 화면 밖에 있을 수 있음", + "⚠️ 대체 경로: 메뉴명이 변경되었을 수 있으므로 다양한 이름으로 탐색", + "메뉴 계층: 결재관리 > 결재함", + "탭 전환 시 URL 변경 없이 데이터만 필터링됨" + ] +} \ No newline at end of file diff --git a/_backup_before_enhance/attendance-checkin.json b/_backup_before_enhance/attendance-checkin.json new file mode 100644 index 0000000..bfd58bc --- /dev/null +++ b/_backup_before_enhance/attendance-checkin.json @@ -0,0 +1,461 @@ +{ + "id": "attendance-checkin", + "name": "근태현황 출퇴근 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "위치 정보 권한 허용 후 출근/퇴근 기록을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/hr/attendance", + + "navigation": { + "targetUrl": "/hr/attendance", + "urlPattern": "/hr/attendance|/ko/hr/attendance", + "menuHints": ["근태현황", "근태 현황", "출퇴근", "인사관리"] + }, + "menuNavigation": { + "level1": "인사관리", + "level2": "근태현황", + "expectedUrl": "/hr/attendance", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", + "level1": "인사관리", + "level2": "근태현황", + "alternativeLevel1Names": ["인사관리", "인사 관리", "HR", "Human Resource", "HR관리"], + "alternativeLevel2Names": ["근태현황", "근태 현황", "출퇴근", "Attendance", "출퇴근현황", "근태관리"], + "fallbackUrls": [ + "/hr/attendance", + "/ko/hr/attendance", + "/ko/hr/attendance-status", + "/ko/hr/checkin", + "/ko/human-resource/attendance" + ], + "scrollConfig": { + "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", + "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + } + }, + + "timeout": 120000, + "tags": ["hr", "attendance", "geolocation", "checkin", "checkout"], + + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + + "browserConfig": { + "permissions": { + "geolocation": { + "grant": true, + "description": "위치 정보 접근 권한 허용 - 출퇴근 기록에 필수", + "mockLocation": { + "enabled": true, + "latitude": 37.557358, + "longitude": 126.864414, + "accuracy": 100 + } + } + }, + "contextOptions": { + "geolocation": { + "latitude": 37.557358, + "longitude": 126.864414 + }, + "permissions": ["geolocation"] + } + }, + + "preTestSetup": { + "description": "테스트 시작 전 Playwright 브라우저 컨텍스트에서 위치 권한 설정", + "playwright": { + "grantPermissions": ["geolocation"], + "setGeolocation": { + "latitude": 37.557358, + "longitude": 126.864414, + "accuracy": 100 + }, + "code": [ + "// Playwright MCP 사용 시 브라우저 시작 직후 실행", + "// mcp__playwright__playwright_evaluate로 위치 권한 자동 허용", + "await context.grantPermissions(['geolocation']);", + "await context.setGeolocation({ latitude: 37.557358, longitude: 126.864414 });" + ] + } + }, + + "steps": [ + { + "id": "step-0", + "name": "🔐 Geolocation API 모킹 (권한 팝업 방지)", + "description": "페이지 로드 직후 Geolocation API를 모킹하여 브라우저 권한 팝업이 나타나지 않도록 함", + "executeBeforeNavigation": false, + "executeImmediately": true, + "actions": [ + { + "type": "evaluate", + "script": "(() => { const mockPosition = { coords: { latitude: 37.557358, longitude: 126.864414, accuracy: 100, altitude: null, altitudeAccuracy: null, heading: null, speed: null }, timestamp: Date.now() }; const mockGeolocation = { getCurrentPosition: (success, error, options) => { console.log('[E2E] Geolocation.getCurrentPosition - 모킹된 위치 반환'); setTimeout(() => success(mockPosition), 50); }, watchPosition: (success, error, options) => { console.log('[E2E] Geolocation.watchPosition - 모킹된 위치 반환'); setTimeout(() => success(mockPosition), 50); return 1; }, clearWatch: (id) => {} }; Object.defineProperty(navigator, 'geolocation', { value: mockGeolocation, writable: false, configurable: true }); console.log('[E2E] Geolocation API 모킹 완료 - 서울 영등포구 좌표'); return { success: true, coords: mockPosition.coords }; })()", + "description": "Geolocation API 모킹 (서울 영등포구 좌표: 37.557358, 126.864414)" + }, + { "type": "wait", "duration": 300, "description": "모킹 적용 대기" } + ], + "note": "Geolocation API를 모킹하면 브라우저가 위치 권한을 요청하지 않음" + }, + { + "id": "step-0-1", + "name": "🗺️ 브라우저 위치 권한 팝업 클릭 (좌측 상단)", + "description": "Chrome 브라우저 좌측 상단에 나타나는 '사이트에 있는 동안 허용' 팝업 클릭", + "actions": [ + { "type": "wait", "duration": 1500, "description": "위치 권한 팝업 표시 대기" }, + { + "type": "evaluate", + "script": "(async function() { const permissionSelectors = [ '[class*=\"permission\"][class*=\"allow\"]', '[class*=\"infobar\"] button', '[aria-label*=\"허용\"]', '[aria-label*=\"Allow\"]', 'button:has-text(\"사이트에 있는 동안 허용\")', 'button:has-text(\"허용\")', 'button:has-text(\"Allow\")', '[data-testid*=\"permission\"]', '.permission-prompt button', '[class*=\"PermissionPrompt\"] button' ]; for (const sel of permissionSelectors) { try { const btn = document.querySelector(sel); if (btn && btn.offsetParent !== null) { btn.click(); console.log('[E2E] 위치 권한 팝업 클릭 성공:', sel); await new Promise(r => setTimeout(r, 500)); return { clicked: true, selector: sel }; } } catch(e) {} } const allButtons = Array.from(document.querySelectorAll('button, [role=\"button\"]')); const allowBtn = allButtons.find(b => { const text = b.innerText || b.textContent || ''; return text.includes('사이트에 있는 동안 허용') || text.includes('허용') || text.includes('Allow'); }); if (allowBtn && allowBtn.offsetParent !== null) { allowBtn.click(); console.log('[E2E] 위치 권한 팝업 텍스트 검색으로 클릭'); return { clicked: true, method: 'textSearch' }; } console.log('[E2E] 위치 권한 팝업 없음 (이미 허용되었거나 모킹으로 우회됨)'); return { clicked: false, reason: 'no_popup_found' }; })()", + "description": "좌측 상단 권한 팝업 찾아서 클릭" + }, + { "type": "wait", "duration": 500, "description": "권한 설정 적용 대기" } + ], + "errorHandling": { + "onTimeout": "continue", + "onNotFound": "continue", + "reason": "팝업이 없으면 이미 허용된 상태로 간주" + } + }, + { + "id": "step-0-2", + "name": "📂 사이드바 메뉴 전체 펼치기", + "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴 펼침", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 300 }, + { + "type": "evaluate", + "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" + }, + { "type": "wait", "duration": 2000 }, + { "type": "screenshot", "name": "after_permission_grant_and_menu_expanded" } + ], + "verification": [ + "사이드바 메뉴가 펼쳐졌는지 확인" + ] + }, + { + "id": 2, + "name": "1차 메뉴 찾기: 인사관리 (스크롤 포함)", + "description": "사이드바를 스크롤하며 '인사관리' 메뉴를 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "인사관리", + "alternativeTexts": ["인사관리", "인사 관리", "HR", "Human Resource"], + "scrollContainer": "sidebar", + "maxAttempts": 10, + "description": "스크롤하며 인사관리 메뉴 찾기" + }, + { "type": "wait", "duration": 300 }, + { "type": "click_if_exists", "target": "인사관리", "description": "인사관리 메뉴 클릭" }, + { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, + { "type": "screenshot", "name": "hr_menu_expanded" } + ], + "verification": [ + "인사관리 메뉴가 클릭되었는지 확인", + "서브메뉴가 펼쳐졌는지 확인", + "하위 메뉴 항목들이 보이는지 확인" + ], + "fallback": { + "if": "메뉴를 찾을 수 없음", + "then": "사이드바 전체를 스크롤하며 재탐색" + } + }, + { + "id": 3, + "name": "2차 메뉴 찾기: 근태현황 (스크롤 포함)", + "description": "서브메뉴에서 '근태현황'을 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "근태현황", + "alternativeTexts": ["근태현황", "근태 현황", "출퇴근", "Attendance"], + "scrollContainer": "submenu", + "maxAttempts": 5, + "description": "서브메뉴에서 근태현황 찾기" + }, + { "type": "wait", "duration": 200 }, + { "type": "click_if_exists", "target": "근태현황", "description": "근태현황 메뉴 클릭" }, + { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 }, + { "type": "screenshot", "name": "attendance_page" } + ], + "verification": [ + "근태현황 메뉴 클릭 성공", + "페이지 이동 또는 컨텐츠 로드" + ] + }, + { + "id": 4, + "name": "404 에러 감지 및 대체 경로 시도", + "description": "페이지 로드 후 404 에러 여부 확인, 404시 대체 경로 탐색", + "actions": [ + { "type": "wait", "duration": 1000 }, + { "type": "checkFor404", "indicators": [ + "페이지를 찾을 수 없습니다", + "404", + "Not Found", + "존재하지 않거나" + ]}, + { "type": "screenshot", "name": "page_load_result" } + ], + "verification": [ + "현재 페이지가 404인지 확인" + ], + "onError404": { + "description": "404 에러 발생 시 대체 URL 시도", + "actions": [ + { "type": "log", "message": "404 감지 - 대체 경로 탐색 시작" }, + { + "type": "tryAlternativeUrls", + "urls": [ + "/ko/hr/attendance", + "/ko/hr/attendance-status", + "/ko/hr/checkin" + ], + "stopOnSuccess": true + }, + { + "type": "ifStillFailed", + "action": "navigateViaMenuClick", + "description": "URL 직접 접근 실패 시 메뉴 클릭으로 재시도" + } + ] + } + }, + { + "id": 5, + "name": "페이지 정상 로드 확인", + "description": "근태현황 페이지가 정상적으로 로드되었는지 확인", + "actions": [ + { "type": "verify", "target": "pageTitle", "contains": ["근태현황", "출퇴근", "Attendance"] }, + { "type": "verify", "target": "pageContent", "notContains": ["404", "찾을 수 없습니다", "Not Found"] } + ], + "verification": [ + "페이지 제목 '근태현황' 또는 관련 텍스트 표시", + "404 에러 메시지 미표시", + "콘텐츠가 정상 렌더링됨" + ], + "successCriteria": { + "urlPattern": "/hr/attendance", + "requiredElements": ["출퇴근", "출근", "퇴근", "현재 시간"] + } + }, + { + "id": "step-5", + "name": "브라우저 위치 권한 설정", + "description": "Playwright context에서 위치 정보 권한을 허용하고 가상 위치 설정", + "playwright": { + "code": "await context.grantPermissions(['geolocation']);", + "setGeolocation": { + "latitude": 37.557358, + "longitude": 126.864414 + } + }, + "expect": { + "permissionGranted": "geolocation" + } + }, + { + "id": "step-6", + "name": "위치 정보 로딩 대기", + "description": "Google Map 로딩 및 현재 위치 표시 대기", + "waitFor": { + "type": "element", + "selector": "region[name='지도']", + "timeout": 10000 + }, + "expect": { + "mapLoaded": true, + "locationMarkerVisible": true + } + }, + { + "id": "step-7", + "name": "사용자 정보 확인", + "description": "출퇴근 패널에서 로그인한 사용자 정보 확인", + "verify": { + "userInfo": { + "name": "홍킬동", + "department": "부서명 · 개발중인 메뉴" + }, + "currentTime": { + "format": "HH:mm:ss", + "updating": true + } + } + }, + { + "id": "step-8", + "name": "출근 상태 확인", + "description": "현재 출퇴근 상태 확인 (출근 전/출근 후)", + "capture": { + "variable": "attendanceStatus", + "checkElements": [ + { "selector": "button:has-text('출근하기')", "status": "not_checked_in" }, + { "selector": "text=출근 완료", "status": "checked_in" }, + { "selector": "button:has-text('퇴근하기')", "status": "ready_to_checkout" } + ] + } + }, + { + "id": "step-9", + "name": "출근하기 (미출근 상태인 경우)", + "description": "출근하기 버튼이 활성화된 경우 클릭하여 출근 기록", + "condition": { + "if": "{attendanceStatus} == 'not_checked_in'" + }, + "actions": [ + { "type": "click_if_exists", "target": "출근하기" } + ], + "waitFor": { + "type": "text", + "content": "출근 완료", + "timeout": 5000 + }, + "expect": { + "toast": ["출근", "완료", "성공"], + "visible": ["출근 완료", "출근 시간"] + } + }, + { + "id": "step-10", + "name": "출근 완료 상태 확인", + "description": "출근 완료 후 상태 및 출근 시간 표시 확인", + "verify": { + "visible": ["출근 완료"], + "checkInTime": { + "format": "HH:mm:ss", + "displayed": true + }, + "buttonState": { + "출근하기": "hidden_or_disabled", + "퇴근하기": "enabled_or_visible" + } + } + }, + { + "id": "step-11", + "name": "퇴근하기 버튼 상태 확인", + "description": "출근 완료 후 퇴근하기 버튼 활성화 여부 확인", + "verify": { + "button": { + "target": "퇴근하기", + "state": "visible", + "note": "일부 시스템에서는 최소 근무 시간 후에만 활성화될 수 있음" + } + } + }, + { + "id": "step-12", + "name": "퇴근하기 (선택적)", + "description": "퇴근하기 버튼이 활성화된 경우 클릭하여 퇴근 기록", + "optional": true, + "condition": { + "if": "button[name='퇴근하기']:enabled" + }, + "actions": [ + { "type": "click_if_exists", "target": "퇴근하기" } + ], + "waitFor": { + "type": "text", + "content": ["퇴근 완료", "퇴근 시간"], + "timeout": 5000 + }, + "expect": { + "toast": ["퇴근", "완료", "성공"], + "visible": ["퇴근 완료", "퇴근 시간"] + } + }, + { + "id": "step-13", + "name": "최종 상태 확인", + "description": "출퇴근 기록 후 최종 상태 확인", + "verify": { + "url": "/hr/attendance", + "mapDisplayed": true, + "attendanceRecorded": true + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/hr/attendance", + "message": "근태현황 페이지에 머물러야 함" + }, + { + "type": "permission", + "name": "geolocation", + "state": "granted", + "message": "위치 정보 권한이 허용되어야 함" + }, + { + "type": "elementExists", + "selector": "region[name='지도']", + "message": "Google Map이 표시되어야 함" + }, + { + "type": "elementExists", + "selector": "text=현재 시간", + "message": "현재 시간이 표시되어야 함" + } + ], + + "cleanup": { + "enabled": false, + "description": "출퇴근 기록은 삭제하지 않음 (업무 데이터)", + "note": "테스트 후 수동으로 관리자가 삭제 필요시 처리" + }, + + "notes": { + "testScope": "위치 권한 허용 -> 근태현황 페이지 이동 -> 출근/퇴근 기록 테스트", + "antiPattern404": "직접 URL 접근 금지 - 반드시 메뉴 클릭으로 페이지 진입", + "scrollRequired": "사이드바 스크롤을 통해 메뉴 항목 탐색 필수", + "correctUrl": "/hr/attendance (기존 /ko/hr/attendance에서 수정됨)" + }, + + "playwrightMcpInstructions": { + "description": "Playwright MCP를 사용한 위치 권한 설정 방법", + "beforeNavigation": [ + "1. 브라우저 navigate 전에 위치 권한 설정이 필요함", + "2. Playwright MCP는 브라우저 컨텍스트 레벨에서 권한을 설정할 수 없으므로 UI 팝업 처리 필요" + ], + "uiPermissionHandling": { + "description": "위치 권한 팝업이 나타나면 '항상 허용' 버튼 클릭", + "selectors": [ + "button:has-text('항상 허용')", + "button:has-text('허용')", + "button:has-text('Allow')" + ], + "workflow": [ + "1. 페이지 로드 후 1-2초 대기", + "2. 위치 권한 팝업 존재 여부 확인", + "3. 팝업이 있으면 '항상 허용' 버튼 클릭", + "4. 팝업이 없으면 이미 권한이 허용된 상태로 간주하고 진행" + ] + }, + "mockGeolocation": { + "description": "테스트용 가상 위치 설정", + "latitude": 37.557358, + "longitude": 126.864414, + "note": "서울 영등포구 좌표 (테스트 회사 위치 가정)" + } + } +} diff --git a/_backup_before_enhance/board-management.json b/_backup_before_enhance/board-management.json new file mode 100644 index 0000000..13f937c --- /dev/null +++ b/_backup_before_enhance/board-management.json @@ -0,0 +1,81 @@ +{ + "id": "board-management", + "name": "게시판 관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "게시판 > 게시판 관리 목록/검색/상세 기능 검증", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "게시판", + "level2": "게시판 관리", + "expectedUrl": "/board/board-management", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 게시판 > 게시판 관리", + "action": "menu_navigate", + "level1": "게시판", + "level2": "게시판 관리", + "expected": { "url_contains": "/board" } + }, + { + "id": 2, + "name": "목업 감지", + "action": "verify_not_mockup" + }, + { + "id": 3, + "name": "게시판 관리 페이지 확인", + "action": "verify_detail", + "checks": ["visible_text:게시판"] + }, + { + "id": 4, + "name": "테이블 확인", + "action": "verify_table" + }, + { + "id": 5, + "name": "검색 기능", + "action": "search", + "value": "테스트" + }, + { + "id": 6, + "name": "검색 후 확인", + "action": "verify_detail", + "checks": ["visible_text:게시판"] + }, + { + "id": 7, + "name": "첫 번째 행 클릭", + "action": "click_first_row" + }, + { + "id": 8, + "name": "상세 확인", + "action": "verify_detail", + "checks": ["visible_text:게시판"] + }, + { + "id": 9, + "name": "모달 닫기", + "action": "close_modal_if_open" + }, + { + "id": 10, + "name": "목록 복귀", + "action": "click_if_exists", + "target": "button:has-text('목록'), a:has-text('목록')" + } + ] +} diff --git a/_backup_before_enhance/company-info.json b/_backup_before_enhance/company-info.json new file mode 100644 index 0000000..7d3dc90 --- /dev/null +++ b/_backup_before_enhance/company-info.json @@ -0,0 +1,435 @@ +{ + "id": "company-info", + "name": "설정 - 회사정보", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "회사 정보 관리 기능 테스트 - 회사 정보 조회, 수정, 회사 추가 기능", + "baseUrl": "https://dev.codebridge-x.com", + + "navigation": { + "targetUrl": "/company-info", + "urlPattern": "/company-info|/ko/company-info|/settings/company-info", + "menuHints": ["회사정보", "회사 정보", "설정"] + }, + "menuNavigation": { + "level1": "설정", + "level2": "회사정보", + "expectedUrl": "/company-info", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", + "level1": "설정", + "level2": "회사정보", + "alternativeLevel1Names": ["설정", "Settings", "환경설정", "시스템설정", "관리"], + "alternativeLevel2Names": ["회사정보", "회사 정보", "Company Info", "회사관리", "기업정보"], + "fallbackUrls": [ + "/company-info", + "/ko/company-info", + "/settings/company-info", + "/ko/settings/company-info" + ], + "scrollConfig": { + "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", + "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + } + }, + + "expectedAPIs": [ + { + "method": "GET", + "path": "/api/v1/company-info", + "description": "회사 정보 조회" + }, + { + "method": "PUT", + "path": "/api/v1/company-info/:id", + "description": "회사 정보 수정" + }, + { + "method": "POST", + "path": "/api/v1/company-info", + "description": "회사 추가" + } + ], + + "steps": [ + { + "id": 0, + "name": "사이드바 메뉴 전체 펼치기", + "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", + "actions": [ + { "type": "scroll", "target": "sidebar", "direction": "top", "description": "사이드바 최상단으로 스크롤" }, + { "type": "wait", "duration": 300 }, + { "type": "evaluate", "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" }, + { "type": "wait", "duration": 2000 } + ], + "verification": [ + "사이드바가 화면에 보이는지 확인", + "모든 메뉴가 펼쳐졌는지 확인" + ] + }, + { + "id": 1, + "name": "1차 메뉴 찾기: 설정 (스크롤 포함)", + "description": "사이드바를 스크롤하며 '설정' 메뉴를 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "설정", + "alternativeTexts": ["설정", "Settings", "환경설정", "시스템설정"], + "scrollContainer": "sidebar", + "maxAttempts": 10, + "description": "스크롤하며 설정 메뉴 찾기" + }, + { "type": "wait", "duration": 300 }, + { "type": "click_if_exists", "target": "설정", "description": "설정 메뉴 클릭" }, + { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, + { "type": "screenshot", "name": "settings_menu_expanded" } + ], + "verification": [ + "설정 메뉴가 클릭되었는지 확인", + "서브메뉴가 펼쳐졌는지 확인", + "하위 메뉴 항목들이 보이는지 확인" + ], + "fallback": { + "if": "메뉴를 찾을 수 없음", + "then": "사이드바 전체를 스크롤하며 재탐색" + } + }, + { + "id": 2, + "name": "2차 메뉴 찾기: 회사정보 (스크롤 포함)", + "description": "서브메뉴에서 '회사정보'를 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "회사정보", + "alternativeTexts": ["회사정보", "회사 정보", "Company Info", "회사관리"], + "scrollContainer": "submenu", + "maxAttempts": 5, + "description": "서브메뉴에서 회사정보 찾기" + }, + { "type": "wait", "duration": 200 }, + { "type": "click_if_exists", "target": "회사정보", "description": "회사정보 메뉴 클릭" }, + { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 }, + { "type": "screenshot", "name": "company_info_page" } + ], + "verification": [ + "회사정보 메뉴 클릭 성공", + "페이지 이동 또는 컨텐츠 로드" + ] + }, + { + "id": 3, + "name": "404 에러 감지 및 대체 경로 시도", + "description": "페이지 로드 후 404 에러 여부 확인, 404시 대체 경로 탐색", + "actions": [ + { "type": "wait", "duration": 1000 }, + { "type": "checkFor404", "indicators": [ + "페이지를 찾을 수 없습니다", + "404", + "Not Found", + "존재하지 않거나" + ]}, + { "type": "screenshot", "name": "page_load_result" } + ], + "verification": [ + "현재 페이지가 404인지 확인" + ], + "onError404": { + "description": "404 에러 발생 시 대체 URL 시도", + "actions": [ + { "type": "log", "message": "404 감지 - 대체 경로 탐색 시작" }, + { + "type": "tryAlternativeUrls", + "urls": [ + "/company-info", + "/ko/company-info" + ], + "stopOnSuccess": true + }, + { + "type": "ifStillFailed", + "action": "navigateViaMenuClick", + "description": "URL 직접 접근 실패 시 메뉴 클릭으로 재시도" + } + ] + } + }, + { + "id": 4, + "name": "페이지 정상 로드 확인", + "description": "회사정보 페이지가 정상적으로 로드되었는지 확인", + "actions": [ + { "type": "verify", "target": "pageTitle", "contains": ["회사정보", "회사 정보", "Company"] }, + { "type": "verify", "target": "pageContent", "notContains": ["404", "찾을 수 없습니다", "Not Found"] } + ], + "verification": [ + "페이지 제목 '회사정보' 또는 관련 텍스트 표시", + "404 에러 메시지 미표시", + "콘텐츠가 정상 렌더링됨" + ], + "successCriteria": { + "urlPattern": "/company-info", + "requiredElements": ["회사", "회사명", "대표자명"] + } + }, + { + "step": 5, + "name": "페이지 제목 확인", + "action": "verify", + "target": "heading", + "expected": "회사정보", + "validation": "페이지 제목이 '회사정보'로 표시됨" + }, + { + "step": 6, + "name": "회사 추가 버튼 존재 확인", + "action": "verify", + "target": "button[text='회사 추가']", + "expected": "button exists", + "validation": "회사 추가 버튼이 표시됨" + }, + { + "step": 7, + "name": "수정 버튼 존재 확인", + "action": "verify", + "target": "button[text='수정']", + "expected": "button exists", + "validation": "수정 버튼이 표시됨" + }, + { + "step": 8, + "name": "회사명 필드 확인", + "action": "verify", + "target": "textbox[label='회사명'][disabled]", + "expected": "프론트_테스트회사", + "validation": "회사명이 표시되고 비활성화 상태" + }, + { + "step": 9, + "name": "대표자명 필드 확인", + "action": "verify", + "target": "textbox[label='대표자명'][disabled]", + "expected": "프론트", + "validation": "대표자명이 표시되고 비활성화 상태" + }, + { + "step": 10, + "name": "업태 필드 확인", + "action": "verify", + "target": "textbox[label='업태'][disabled]", + "expected": "업태명", + "validation": "업태가 표시되고 비활성화 상태" + }, + { + "step": 11, + "name": "업종 필드 확인", + "action": "verify", + "target": "textbox[label='업종'][disabled]", + "expected": "업종명", + "validation": "업종이 표시되고 비활성화 상태" + }, + { + "step": 12, + "name": "주소 필드 확인", + "action": "verify", + "target": "textbox[label='주소명'][disabled]", + "expected": "주소 표시", + "validation": "주소가 표시되고 비활성화 상태" + }, + { + "step": 13, + "name": "이메일 필드 확인", + "action": "verify", + "target": "textbox[label='이메일 (아이디)'][disabled]", + "expected": "이메일 표시", + "validation": "이메일이 표시되고 비활성화 상태" + }, + { + "step": 14, + "name": "사업자등록번호 필드 확인", + "action": "verify", + "target": "textbox[label='사업자등록번호'][disabled]", + "expected": "사업자등록번호 표시", + "validation": "사업자등록번호가 표시되고 비활성화 상태" + }, + { + "step": 15, + "name": "수정 버튼 클릭", + "action": "click_if_exists", + "target": "button[text='수정']", + "expected": "edit mode enabled", + "validation": "수정 모드로 전환됨" + }, + { + "step": 16, + "name": "수정 모드 - 필드 활성화 확인", + "action": "verify", + "target": "textbox:not([disabled])", + "expected": "fields enabled", + "validation": "텍스트 필드들이 활성화됨" + }, + { + "step": 17, + "name": "취소 버튼 클릭", + "action": "click_if_exists", + "target": "button[text='취소']", + "expected": "edit mode disabled", + "validation": "조회 모드로 복귀" + }, + { + "step": 18, + "name": "회사 추가 버튼 클릭", + "action": "click_if_exists", + "target": "button[text='회사 추가']", + "expected": "dialog opened", + "validation": "회사 추가 다이얼로그가 열림" + }, + { + "step": 19, + "name": "회사 추가 다이얼로그 확인", + "action": "verify", + "target": "dialog", + "expected": "회사 추가 다이얼로그 표시", + "validation": "다이얼로그 제목, 입력 필드, 버튼 확인" + }, + { + "step": 20, + "name": "다이얼로그 닫기", + "action": "click_if_exists", + "target": "dialog button[text='취소']", + "expected": "dialog closed", + "validation": "다이얼로그가 닫힘" + }, + { + "step": 21, + "name": "수정 모드에서 데이터 변경 테스트", + "description": "실제 데이터를 수정하고 저장 기능 검증", + "actions": [ + { "type": "click_if_exists", "target": "수정", "description": "수정 모드 진입" } + ], + "expect": { + "fieldsEnabled": true + } + }, + { + "step": 22, + "name": "업태 필드 수정", + "description": "업태 필드 값 변경", + "actions": [ + { "type": "clear", "target": "업태" }, + { "type": "fill", "target": "업태", "value": "테스트업태_수정" } + ] + }, + { + "step": 23, + "name": "저장 버튼 클릭", + "description": "수정된 회사 정보 저장", + "actions": [ + { "type": "click_if_exists", "target": "저장" } + ], + "waitFor": { + "type": "apiResponse", + "method": "PUT", + "timeout": 5000 + }, + "expect": { + "toast": ["수정", "완료", "성공", "저장"] + } + }, + { + "step": 24, + "name": "⚠️ 필수 검증: 수정 데이터 반영 확인", + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", + "description": "수정된 업태 값이 반영되었는지 확인", + "verify": { + "fieldValue": { + "target": "업태", + "expected": "테스트업태_수정" + } + } + }, + { + "step": 25, + "name": "회사 추가 다이얼로그 열기", + "description": "회사 추가 버튼 클릭하여 다이얼로그 열기", + "actions": [ + { "type": "click_if_exists", "target": "회사 추가" } + ], + "expect": { + "dialog": true, + "visible": ["회사명", "대표자명", "사업자등록번호", "등록", "취소"] + } + }, + { + "step": 26, + "name": "새 회사 정보 입력", + "description": "회사 추가 다이얼로그에서 필수 정보 입력", + "actions": [ + { "type": "fill", "target": "회사명", "value": "테스트회사_{timestamp}" }, + { "type": "fill", "target": "대표자명", "value": "테스트대표" }, + { "type": "fill", "target": "사업자등록번호", "value": "123-45-67890" } + ] + }, + { + "step": 27, + "name": "회사 등록", + "description": "등록 버튼 클릭하여 새 회사 등록", + "actions": [ + { "type": "click_if_exists", "target": "등록" } + ], + "waitFor": { + "type": "apiResponse", + "method": "POST", + "timeout": 5000 + }, + "expect": { + "toast": ["등록", "완료", "성공"], + "dialogClosed": true + } + }, + { + "step": 28, + "name": "⚠️ 필수 검증: 회사 등록 반영 확인", + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 등록 확인 필수!", + "description": "등록된 회사가 목록에 표시되는지 확인", + "verify": { + "visible": "테스트회사" + } + }, + { + "step": 29, + "name": "원복: 업태 필드 원래 값으로 복구", + "description": "테스트 후 원래 값으로 복구", + "actions": [ + { "type": "click_if_exists", "target": "수정" }, + { "type": "clear", "target": "업태" }, + { "type": "fill", "target": "업태", "value": "업태명" }, + { "type": "click_if_exists", "target": "저장" } + ], + "expect": { + "toast": ["수정", "완료", "성공", "저장"] + } + } + ], + + "notes": [ + "직접 URL 접근 금지: 반드시 메뉴 클릭으로 페이지 진입 (404 방지)", + "스크롤 필수: 사이드바가 길 경우 메뉴가 화면 밖에 있을 수 있음", + "대체 경로: 메뉴명이 변경되었을 수 있으므로 다양한 이름으로 탐색", + "메뉴 계층: 설정 > 회사정보" + ] +} diff --git a/_backup_before_enhance/customer-event.json b/_backup_before_enhance/customer-event.json new file mode 100644 index 0000000..5eeae78 --- /dev/null +++ b/_backup_before_enhance/customer-event.json @@ -0,0 +1,164 @@ +{ + "id": "customer-event", + "name": "이벤트 게시판 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "고객센터 > 이벤트 게시판 메뉴의 이벤트 목록 조회/상세보기 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "고객센터", + "level2": "이벤트 게시판", + "expectedUrl": "/customer-center/events", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 고객센터 > 이벤트 게시판", + "action": "menu_navigate", + "level1": "고객센터", + "level2": "이벤트 게시판", + "expected": { + "url_contains": "/customer-center", + "visible": ["이벤트"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "이벤트 목록 표시", + "이벤트 카드 또는 리스트" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "이벤트 페이지 구조 확인", + "action": "verify_elements", + "checks": [ + "이벤트 목록 (카드 또는 테이블)", + "이벤트 상태 표시 (진행중/종료)", + "검색 기능" + ], + "expected": "이벤트 페이지 구조 정상" + }, + { + "id": 4, + "phase": "READ", + "name": "[READ] 이벤트 목록 데이터 확인", + "action": "verify_detail", + "checks": [ + "이벤트 항목 존재 또는 '등록된 이벤트 없음' 메시지" + ], + "expected": "이벤트 데이터 표시" + }, + { + "id": 5, + "phase": "FILTER", + "name": "[FILTER] 상태별 필터", + "action": "verify_elements", + "checks": [ + "전체/진행중/종료 필터 가능" + ], + "expected": "상태 필터 표시" + }, + { + "id": 6, + "phase": "READ", + "name": "[READ] 이벤트 상세 보기", + "action": "click_if_exists", + "target": "[class*='event']:first-child, table tbody tr:first-child, [class*='card']:first-child", + "expected": { + "detail_view": true + } + }, + { + "id": 7, + "name": "이벤트 상세 정보 확인", + "action": "verify_detail", + "checks": [ + "이벤트 제목", + "이벤트 기간", + "이벤트 내용", + "이벤트 이미지 (있을 경우)" + ], + "expected": "이벤트 상세 정보 표시" + }, + { + "id": 8, + "name": "이벤트 참여 버튼 확인", + "action": "verify_elements", + "checks": [ + "참여하기 버튼 존재 여부 (진행중 이벤트)" + ], + "expected": "참여 버튼 확인" + }, + { + "id": 9, + "name": "공유 기능 확인", + "action": "verify_elements", + "checks": [ + "공유 버튼 또는 링크 복사 기능" + ], + "expected": "공유 기능 표시" + }, + { + "id": 10, + "name": "목록으로 돌아가기", + "action": "click_if_exists", + "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", + "expected": "목록 페이지로 복귀" + }, + { + "id": 11, + "name": "페이지네이션 확인", + "action": "verify_elements", + "checks": [ + "페이지 번호 또는 더보기 버튼" + ], + "expected": "페이지네이션 표시" + }, + { + "id": 12, + "name": "이벤트 기간 정보 확인", + "action": "verify_detail", + "checks": [ + "시작일/종료일 표시", + "D-Day 또는 남은 기간 표시" + ], + "expected": "기간 정보 표시" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/customer/events", + "description": "이벤트 목록 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/customer/events/:id", + "description": "이벤트 상세 조회" + } + ], + "requiredVerifications": [ + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "이벤트 목록, 이벤트 카드/리스트 존재" + } + ], + "rollbackPlan": { + "note": "조회 전용 페이지로 데이터 변경 없음" + } +} diff --git a/_backup_before_enhance/customer-faq.json b/_backup_before_enhance/customer-faq.json new file mode 100644 index 0000000..fc1387f --- /dev/null +++ b/_backup_before_enhance/customer-faq.json @@ -0,0 +1,185 @@ +{ + "id": "customer-faq", + "name": "FAQ 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] + }, + "description": "고객센터 > FAQ 메뉴의 자주 묻는 질문 조회/검색/카테고리 필터 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "고객센터", + "level2": "FAQ", + "expectedUrl": "/customer-center/faq", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 고객센터 > FAQ", + "action": "menu_navigate", + "level1": "고객센터", + "level2": "FAQ", + "expected": { + "url_contains": "/customer-center/faq", + "visible": [ + "FAQ", + "자주 묻는 질문" + ] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "FAQ 목록 표시", + "카테고리 또는 검색 기능 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "FAQ 페이지 구조 확인", + "action": "verify_elements", + "checks": [ + "FAQ 항목 목록 (아코디언 또는 리스트)", + "카테고리 탭/필터", + "검색 기능" + ], + "expected": "FAQ 페이지 구조 정상" + }, + { + "id": 4, + "phase": "READ", + "name": "[READ] FAQ 목록 데이터 확인", + "action": "verify_detail", + "checks": [ + "FAQ 항목 존재 또는 '등록된 FAQ 없음' 메시지" + ], + "expected": "FAQ 데이터 표시" + }, + { + "id": 5, + "phase": "READ", + "name": "[READ] FAQ 항목 펼치기", + "action": "click_if_exists", + "target": "[class*='accordion']:first-child, [class*='faq']:first-child, table tbody tr:first-child", + "expected": { + "accordion_open": true + } + }, + { + "id": 6, + "name": "FAQ 답변 내용 확인", + "action": "verify_detail", + "checks": [ + "답변 내용 표시" + ], + "expected": "FAQ 답변 표시" + }, + { + "id": 7, + "phase": "FILTER", + "name": "[FILTER] 카테고리 필터", + "action": "verify_element", + "target": "[class*='category'], [class*='tab']", + "expected": "카테고리 선택 가능" + }, + { + "id": 8, + "phase": "FILTER", + "name": "[FILTER] 카테고리 선택 후 결과", + "action": "verify_detail", + "checks": [ + "해당 카테고리 FAQ만 표시" + ], + "expected": "카테고리 필터 동작" + }, + { + "id": 9, + "phase": "SEARCH", + "name": "[SEARCH] FAQ 검색", + "action": "click_if_exists", + "target": "input[type='search'], input[placeholder*='검색']", + "value": "테스트", + "submit": true + }, + { + "id": 10, + "phase": "SEARCH", + "name": "[SEARCH] 검색 결과 확인", + "action": "verify_detail", + "checks": [ + "검색 결과 표시 또는 결과 없음 메시지" + ], + "expected": "검색 기능 동작" + }, + { + "id": 11, + "name": "FAQ 접기/펼치기 토글", + "action": "verify_elements", + "checks": [ + "접기/펼치기 아이콘 또는 버튼" + ], + "expected": "토글 기능 표시" + }, + { + "id": 12, + "name": "전체 보기/접기", + "action": "verify_elements", + "checks": [ + "전체 펼치기/접기 버튼 존재 여부" + ], + "expected": "전체 토글 기능 확인" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/customer/faq", + "description": "FAQ 목록 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/customer/faq/categories", + "description": "FAQ 카테고리 조회" + } + ], + "requiredVerifications": [ + { + "id": 3, + "name": "검색/필터", + "steps": [ + 7, + 8, + 9, + 10 + ], + "criteria": "카테고리 필터 + 검색 기능 동작" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [ + 2 + ], + "criteria": "FAQ 목록, 카테고리/검색 기능 존재" + } + ], + "rollbackPlan": { + "note": "조회 전용 페이지로 데이터 변경 없음" + } +} diff --git a/_backup_before_enhance/customer-notice.json b/_backup_before_enhance/customer-notice.json new file mode 100644 index 0000000..c94c686 --- /dev/null +++ b/_backup_before_enhance/customer-notice.json @@ -0,0 +1,201 @@ +{ + "id": "customer-notice", + "name": "공지사항 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "고객센터 > 공지사항 메뉴의 공지사항 조회/검색/상세보기 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "고객센터", + "level2": "공지사항", + "expectedUrl": "/customer-center/notices", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 고객센터 > 공지사항", + "action": "menu_navigate", + "level1": "고객센터", + "level2": "공지사항", + "expected": { + "url_contains": "/customer-center/notices", + "visible": ["공지사항"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "공지사항 목록 표시", + "검색 기능 존재", + "목록 테이블 또는 카드" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "공지사항 목록 구조 확인", + "action": "verify_table", + "checks": [ + "번호 컬럼", + "제목 컬럼", + "작성자 컬럼", + "작성일 컬럼", + "조회수 컬럼" + ], + "expected": "공지사항 목록 표시" + }, + { + "id": 4, + "phase": "READ", + "name": "[READ] 공지사항 목록 데이터 확인", + "action": "verify_detail", + "checks": [ + "공지사항 데이터 행 존재 또는 '등록된 공지 없음' 메시지" + ], + "expected": "공지사항 데이터 표시" + }, + { + "id": 5, + "phase": "SEARCH", + "name": "[SEARCH] 공지사항 검색", + "action": "fill", + "target": "input[type='search'], input[placeholder*='검색']", + "value": "테스트", + "submit": true + }, + { + "id": 6, + "phase": "SEARCH", + "name": "[SEARCH] 검색 결과 확인", + "action": "verify_detail", + "checks": [ + "검색 결과 표시 또는 결과 없음 메시지" + ], + "expected": "검색 기능 동작" + }, + { + "id": 7, + "phase": "SEARCH", + "name": "[SEARCH] 검색 초기화", + "action": "click_if_exists", + "target": "button:has-text('초기화'), button:has-text('전체'), button[class*='clear']", + "expected": "검색 초기화" + }, + { + "id": 8, + "phase": "READ", + "name": "[READ] 공지사항 상세 보기", + "action": "click_if_exists", + "target": "table tbody tr:first-child, [class*='list'] [class*='item']:first-child", + "expected": { + "detail_view": true, + "url_change": true + } + }, + { + "id": 9, + "name": "상세 페이지 구조 확인", + "action": "verify_elements", + "checks": [ + "제목 표시", + "작성자 표시", + "작성일 표시", + "내용 표시" + ], + "expected": "상세 페이지 정상 표시" + }, + { + "id": 10, + "name": "첨부파일 확인", + "action": "verify_elements", + "checks": [ + "첨부파일 영역 존재 여부" + ], + "expected": "첨부파일 영역 확인" + }, + { + "id": 11, + "name": "이전/다음 글 네비게이션", + "action": "verify_elements", + "checks": [ + "이전 글 링크 또는 버튼", + "다음 글 링크 또는 버튼" + ], + "expected": "글 네비게이션 표시" + }, + { + "id": 12, + "name": "목록으로 돌아가기", + "action": "click_if_exists", + "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", + "expected": "목록 페이지로 복귀" + }, + { + "id": 13, + "name": "페이지네이션 확인", + "action": "verify_elements", + "checks": [ + "페이지 번호 표시", + "이전/다음 페이지 버튼" + ], + "expected": "페이지네이션 표시" + }, + { + "id": 14, + "name": "정렬 기능 확인", + "action": "verify_elements", + "checks": [ + "최신순/조회순 정렬 가능" + ], + "expected": "정렬 기능 표시" + }, + { + "id": 15, + "name": "중요 공지 표시 확인", + "action": "verify_elements", + "checks": [ + "중요/고정 공지 상단 표시 여부" + ], + "expected": "중요 공지 표시 확인" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/customer-center/noticess", + "description": "공지사항 목록 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/customer-center/noticess/:id", + "description": "공지사항 상세 조회" + } + ], + "requiredVerifications": [ + { + "id": 3, + "name": "검색/필터", + "steps": [5, 6, 7], + "criteria": "검색 기능 동작" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "공지사항 목록, 검색 기능, 목록 표시 존재" + } + ], + "rollbackPlan": { + "note": "조회 전용 페이지로 데이터 변경 없음" + } +} diff --git a/_backup_before_enhance/department-add.json b/_backup_before_enhance/department-add.json new file mode 100644 index 0000000..fd1dfc2 --- /dev/null +++ b/_backup_before_enhance/department-add.json @@ -0,0 +1,150 @@ +{ + "enabled": true, + "id": "department-add", + "name": "부서관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "인사관리 > 부서관리 메뉴의 부서 트리/목록 조회 및 UI 검증 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "인사관리", + "level2": "부서관리", + "expectedUrl": "/hr/department-management", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 인사관리 > 부서관리", + "action": "menu_navigate", + "level1": "인사관리", + "level2": "부서관리", + "expected": { + "url_contains": "/hr/department", + "visible": ["부서관리"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "부서 목록 또는 트리 표시", + "부서 추가 버튼 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "부서 트리/목록 구조 확인", + "action": "verify_elements", + "checks": [ + "부서 목록 또는 트리 구조 표시", + "추가 버튼 존재", + "부서 정보 표시" + ], + "expected": "부서관리 페이지 정상 표시" + }, + { + "id": 4, + "phase": "READ", + "name": "[READ] 부서 목록 데이터 확인", + "action": "verify_detail", + "checks": [ + "부서 목록 데이터 표시됨" + ], + "expected": "부서 목록 정상" + }, + { + "id": 5, + "phase": "READ", + "name": "[READ] 첫 번째 부서 노드 클릭", + "action": "click_if_exists", + "target": "table tbody tr:first-child, [class*='tree'] > *:first-child, li:first-child" + }, + { + "id": 6, + "phase": "READ", + "name": "[READ] 부서 상세 정보 확인", + "action": "verify_detail", + "checks": [ + "부서 상세 정보 표시" + ], + "expected": "부서 상세 정보 확인" + }, + { + "id": 7, + "name": "부서 추가 버튼 확인", + "action": "click_if_exists", + "target": "button:has-text('추가'), button:has-text('등록'), button:has-text('부서 추가')" + }, + { + "id": 8, + "name": "추가 폼/모달 확인", + "action": "verify_elements", + "checks": [ + "부서명 입력 필드 존재", + "저장/등록 버튼 존재" + ], + "expected": "부서 추가 폼 표시" + }, + { + "id": 9, + "name": "추가 모달 닫기", + "action": "close_modal_if_open", + "expected": "모달 닫힘" + }, + { + "id": 10, + "name": "부서 트리 구조 확인", + "action": "verify_elements", + "checks": [ + "부서 계층 구조 표시" + ], + "expected": "트리 구조 확인" + }, + { + "id": 11, + "name": "삭제 버튼 존재 확인", + "action": "verify_elements", + "checks": [ + "삭제 버튼 존재 여부" + ], + "expected": "삭제 기능 확인" + }, + { + "id": 12, + "name": "부서관리 페이지 최종 확인", + "action": "verify_detail", + "checks": [ + "부서관리 페이지 정상 동작" + ], + "expected": "페이지 정상 확인" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/departments", + "description": "부서 목록 조회" + } + ], + "requiredVerifications": [ + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "부서 목록, 추가 버튼 존재" + } + ], + "rollbackPlan": { + "note": "READ-only 패턴으로 안정성 우선, CRUD 테스트 제거" + } +} diff --git a/_backup_before_enhance/deposit-management.json b/_backup_before_enhance/deposit-management.json new file mode 100644 index 0000000..1fcd55f --- /dev/null +++ b/_backup_before_enhance/deposit-management.json @@ -0,0 +1,468 @@ +{ + "id": "deposit-management", + "name": "입금관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "입금관리 목록 조회, 계정과목명 일괄변경, 상세 수정 기능 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/ko/accounting/deposits", + "navigation": { + "targetUrl": "/accounting/deposits", + "urlPattern": "/accounting/deposits|/ko/accounting/deposits", + "menuHints": ["입금관리", "입금 관리", "회계관리"] + }, + "menuNavigation": { + "level1": "회계관리", + "level2": "입금관리", + "expectedUrl": "/ko/accounting/deposits", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", + "level1": "회계관리", + "level2": "입금관리", + "alternativeLevel1Names": ["회계관리", "회계 관리", "Accounting"], + "alternativeLevel2Names": ["입금관리", "입금 관리", "Deposits"], + "scrollConfig": { + "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", + "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + } + }, + "timeout": 60000, + "tags": ["accounting", "deposit", "crud"], + + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 메뉴 전체 펼치기", + "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", + "actions": [ + { "type": "scroll", "target": "sidebar", "direction": "top", "description": "사이드바 최상단으로 스크롤" }, + { "type": "wait", "duration": 300 }, + { "type": "evaluate", "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" }, + { "type": "wait", "duration": 2000 } + ] + }, + { + "id": "step-1", + "name": "2단계 메뉴 진입: 회계관리 > 입금관리", + "description": "사이드바를 스크롤하며 회계관리 > 입금관리 메뉴를 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "회계관리", + "alternativeTexts": ["회계관리", "회계 관리", "Accounting"], + "scrollContainer": "sidebar", + "maxAttempts": 10, + "description": "스크롤하며 회계관리 메뉴 찾기" + }, + { "type": "click_if_exists", "target": "회계관리", "description": "회계관리 메뉴 클릭" }, + { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, + { + "type": "scrollAndFind", + "target": "입금관리", + "alternativeTexts": ["입금관리", "입금 관리", "Deposits"], + "scrollContainer": "submenu", + "maxAttempts": 5, + "description": "서브메뉴에서 입금관리 찾기" + }, + { "type": "click_if_exists", "target": "입금관리", "description": "입금관리 메뉴 클릭" }, + { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 } + ], + "expect": { + "url": "/accounting/deposits", + "visible": ["입금관리", "총 입금"] + }, + "verification": [ + "회계관리 메뉴가 펼쳐졌는지 확인", + "입금관리 서브메뉴 클릭 성공", + "404 에러 없이 페이지 로드 완료" + ] + }, + { + "id": "step-2", + "name": "목록 페이지 구조 확인", + "description": "테이블 및 필터 요소 확인", + "expect": { + "visible": ["입금일", "입금계좌", "입금자명", "입금금액", "거래처", "적요", "입금유형"], + "elements": { + "statisticsCards": ["총 입금", "당월 입금", "거래처 미설정", "입금유형 미설정"], + "filters": ["계정과목명", "저장", "새로고침"], + "pagination": true + } + } + }, + { + "id": "step-3", + "name": "계정과목명 드롭다운 옵션 확인", + "description": "계정과목명 일괄변경 드롭다운 옵션 검증", + "actions": [ + { "type": "click_if_exists", "target": "계정과목명 드롭다운", "description": "드롭다운 열기" } + ], + "expect": { + "options": ["미설정", "매출대금", "선수금", "가수금", "임대수익", "이자수익", "보증금 반환", "차입금", "자본금", "부가세 환급", "기타"] + } + }, + { + "id": "step-4", + "name": "체크박스 선택 후 계정과목명 일괄변경", + "description": "테이블 행 선택 후 계정과목명 일괄변경 저장", + "actions": [ + { "type": "click_if_exists", "target": "첫 번째 행 체크박스", "description": "행 선택" }, + { "type": "click_if_exists", "target": "계정과목명 드롭다운", "description": "드롭다운 열기" }, + { "type": "click_if_exists", "target": "매출대금", "description": "매출대금 선택" }, + { "type": "click_if_exists", "target": "저장", "description": "저장 버튼 클릭" } + ], + "expect": { + "dialog": "확인 다이얼로그 표시", + "toast": "변경 완료 메시지" + } + }, + { + "id": "step-4-1", + "name": "⚠️ 필수 검증: 계정과목명 변경 데이터 반영 확인", + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", + "description": "저장 후 테이블에서 변경된 입금유형 값 확인", + "expect": { + "tableCell": { + "row": 1, + "column": "입금유형", + "value": "매출대금" + } + }, + "knownBugReference": "BUG-SALES-20260115-001 (매출관리 동일 버그 확인 필요)" + }, + { + "id": "step-5", + "name": "입금 상세 페이지 이동", + "description": "테이블 행 클릭하여 상세 페이지로 이동", + "actions": [ + { "type": "click_if_exists", "target": "테이블 첫 번째 행", "description": "행 클릭 (체크박스 제외 영역)" } + ], + "expect": { + "url": "/accounting/deposits/{id}", + "visible": ["입금 상세", "기본 정보", "목록", "삭제", "수정"] + } + }, + { + "id": "step-6", + "name": "상세 페이지 읽기 모드 필드 확인", + "description": "수정 전 필드들이 비활성화 상태인지 확인", + "expect": { + "fields": [ + { "name": "입금일", "disabled": true }, + { "name": "입금계좌", "disabled": true }, + { "name": "입금자명", "disabled": true }, + { "name": "입금금액", "disabled": true }, + { "name": "적요", "disabled": true }, + { "name": "거래처", "disabled": true }, + { "name": "입금 유형", "disabled": true } + ] + } + }, + { + "id": "step-7", + "name": "수정 모드 전환", + "description": "수정 버튼 클릭하여 편집 모드로 전환", + "click": "수정", + "expect": { + "url": "/accounting/deposits/{id}?mode=edit", + "visible": ["입금 수정", "취소", "저장"], + "notVisible": ["목록", "삭제", "수정"] + } + }, + { + "id": "step-8", + "name": "수정 모드 필드 활성화 검증", + "description": "수정 가능한 필드와 불가능한 필드 확인", + "expect": { + "fields": [ + { "name": "입금일", "disabled": true, "note": "은행데이터 - 수정 불가" }, + { "name": "입금계좌", "disabled": true, "note": "은행데이터 - 수정 불가" }, + { "name": "입금자명", "disabled": true, "note": "은행데이터 - 수정 불가" }, + { "name": "입금금액", "disabled": true, "note": "은행데이터 - 수정 불가" }, + { "name": "적요", "disabled": false, "editable": true }, + { "name": "거래처", "disabled": false, "type": "combobox", "editable": true }, + { "name": "입금 유형", "disabled": false, "type": "combobox", "editable": true } + ] + } + }, + { + "id": "step-9", + "name": "거래처 드롭다운 옵션 확인", + "description": "거래처 선택 드롭다운 옵션 검증", + "actions": [ + { "type": "click_if_exists", "target": "거래처 드롭다운", "description": "드롭다운 열기" } + ], + "expect": { + "options": ["거래처테스트", "아크더레드", "코브라브릿지", "가우스전자", "아크아크"] + } + }, + { + "id": "step-10", + "name": "입금 유형 드롭다운 옵션 확인", + "description": "입금 유형 선택 드롭다운 옵션 검증", + "actions": [ + { "type": "click_if_exists", "target": "입금 유형 드롭다운", "description": "드롭다운 열기" } + ], + "expect": { + "options": ["미설정", "매출대금", "선수금", "가수금", "임대수익", "이자수익", "보증금 반환", "차입금", "자본금", "부가세 환급", "기타"] + } + }, + { + "id": "step-11", + "name": "수정 데이터 입력", + "description": "수정 가능한 필드에 테스트 데이터 입력", + "form": { + "fields": [ + { "name": "적요", "type": "text", "value": "테스트 적요 수정" } + ] + }, + "actions": [ + { "type": "click_if_exists", "target": "거래처 드롭다운", "description": "거래처 드롭다운 열기" }, + { "type": "click_if_exists", "target": "거래처테스트", "description": "거래처 선택" }, + { "type": "click_if_exists", "target": "입금 유형 드롭다운", "description": "입금 유형 드롭다운 열기" }, + { "type": "click_if_exists", "target": "매출대금", "description": "매출대금 선택" } + ] + }, + { + "id": "step-12", + "name": "저장 및 결과 확인", + "description": "저장 버튼 클릭 후 데이터 반영 확인", + "click": "저장", + "expect": { + "toast": "저장 완료 메시지", + "url": "/accounting/deposits/{id}", + "mode": "view" + } + }, + { + "id": "step-12-1", + "name": "⚠️ 필수 검증: 수정 데이터 반영 확인", + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", + "description": "저장 후 상세 페이지에서 변경된 값 확인", + "expect": { + "fields": [ + { "name": "적요", "value": "테스트 적요 수정" }, + { "name": "거래처", "value": "거래처테스트" }, + { "name": "입금 유형", "value": "매출대금" } + ] + } + }, + { + "id": "step-13", + "name": "취소 버튼 동작 확인", + "description": "수정 모드에서 취소 버튼 동작 검증", + "actions": [ + { "type": "click_if_exists", "target": "수정", "description": "수정 모드 진입" }, + { "type": "click_if_exists", "target": "취소", "description": "취소 버튼 클릭" } + ], + "expect": { + "url": "/accounting/deposits/{id}", + "mode": "view", + "visible": ["입금 상세", "목록", "삭제", "수정"] + } + }, + { + "id": "step-14", + "name": "목록 버튼 동작 확인", + "description": "목록 버튼 클릭하여 목록 페이지로 이동", + "click": "목록", + "expect": { + "url": "/accounting/deposits", + "visible": ["입금관리", "총 입금"] + } + }, + { + "id": "step-15", + "name": "필터 드롭다운 검증", + "description": "목록 페이지 필터 드롭다운 옵션 확인", + "note": "3개의 필터 드롭다운 존재 (거래처, 입금유형, 정렬)", + "expect": { + "filters": [ + { "name": "거래처 필터", "default": "전체" }, + { "name": "입금유형 필터", "default": "전체" }, + { "name": "정렬", "default": "최신순", "options": ["최신순", "등록순", "금액 높은순", "금액 낮은순"] } + ] + } + }, + { + "id": "step-16", + "name": "날짜 필터 검증", + "description": "날짜 필터 버튼 동작 확인", + "actions": [ + { "type": "click_if_exists", "target": "당해년도", "description": "당해년도 버튼 클릭" } + ], + "expect": { + "dateRange": { + "start": "2026-01-01", + "end": "2026-12-31" + } + } + }, + { + "id": "step-17", + "name": "페이지네이션 동작 확인", + "description": "페이지네이션 버튼 동작 검증", + "expect": { + "pagination": { + "totalItems": 60, + "itemsPerPage": 20, + "currentPage": 1, + "totalPages": 3 + } + }, + "actions": [ + { "type": "click_if_exists", "target": "다음", "description": "다음 페이지로 이동" } + ], + "expectAfterAction": { + "currentPage": 2, + "displayText": "전체 60개 중 21-40개 표시" + } + } + ], + + "deleteTest": { + "note": "삭제 테스트 - 이전에 스킵되었으나 CRUD 완전성을 위해 추가", + "steps": [ + { + "id": "step-delete-1", + "name": "삭제 버튼 클릭", + "description": "상세 페이지에서 삭제 버튼 클릭", + "actions": [ + { "type": "click_if_exists", "target": "삭제" } + ], + "expect": { + "confirmDialog": true, + "dialogText": ["삭제", "하시겠습니까"] + } + }, + { + "id": "step-delete-2", + "name": "삭제 확인", + "description": "삭제 확인 다이얼로그에서 확인 클릭", + "actions": [ + { "type": "click_if_exists", "target": "확인", "description": "삭제 확인" } + ], + "waitFor": { + "type": "navigation", + "url": "/accounting/deposits", + "timeout": 5000 + }, + "expect": { + "toast": ["삭제", "완료", "성공"], + "url": "/accounting/deposits" + } + }, + { + "id": "step-delete-3", + "name": "⚠️ 필수 검증: 삭제 데이터 반영 확인", + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 삭제 확인 필수!", + "description": "목록에서 삭제된 입금 내역이 없어졌는지 확인", + "verify": { + "tableNotContains": "테스트 적요 수정" + } + } + ] + }, + + "knownBugs": [ + { + "id": "BUG-SALES-20260115-001", + "description": "계정과목명 일괄변경 시 토스트 성공 표시되나 실제 데이터 미변경 (매출관리)", + "relatedSteps": ["step-4-1"], + "note": "입금관리에서도 동일한 버그가 존재할 수 있으므로 step-4-1에서 검증 필수" + } + ], + + "testData": { + "sampleDeposit": { + "date": "2025-12-28", + "account": "운영계좌", + "depositor": "CJ대한통운", + "amount": "8,209,677", + "vendor": "CJ대한통운", + "description": "CJ대한통운 입금", + "depositType": "미설정" + }, + "modifiedData": { + "description": "테스트 적요 수정", + "vendor": "거래처테스트", + "depositType": "매출대금" + } + }, + + "pageStructure": { + "listPage": { + "url": "/accounting/deposits", + "title": "입금관리", + "statistics": ["총 입금", "당월 입금", "거래처 미설정", "입금유형 미설정"], + "tableColumns": ["checkbox", "입금일", "입금계좌", "입금자명", "입금금액", "거래처", "적요", "입금유형", "action"], + "batchUpdate": { + "label": "계정과목명", + "saveButton": "저장" + }, + "filters": ["거래처", "입금유형", "정렬"], + "dateFilters": ["당해년도", "전전월", "전월", "당월", "어제", "오늘"] + }, + "detailPage": { + "url": "/accounting/deposits/{id}", + "title": "입금 상세", + "buttons": ["목록", "삭제", "수정"], + "fields": { + "readOnly": ["입금일", "입금계좌", "입금자명", "입금금액"], + "editable": ["적요", "거래처", "입금 유형"] + } + }, + "editPage": { + "url": "/accounting/deposits/{id}?mode=edit", + "title": "입금 수정", + "buttons": ["취소", "저장"] + } + }, + + "dropdownOptions": { + "accountSubject": { + "label": "계정과목명", + "options": ["미설정", "매출대금", "선수금", "가수금", "임대수익", "이자수익", "보증금 반환", "차입금", "자본금", "부가세 환급", "기타"] + }, + "depositType": { + "label": "입금 유형", + "options": ["미설정", "매출대금", "선수금", "가수금", "임대수익", "이자수익", "보증금 반환", "차입금", "자본금", "부가세 환급", "기타"] + }, + "vendor": { + "label": "거래처", + "options": ["거래처테스트", "아크더레드", "코브라브릿지", "가우스전자", "아크아크"] + }, + "sortOrder": { + "label": "정렬", + "options": ["최신순", "등록순", "금액 높은순", "금액 낮은순"] + } + }, + + "assertions": [ + { + "type": "url", + "expected": "/accounting/deposits", + "message": "목록 페이지 URL 확인" + }, + { + "type": "text", + "target": "body", + "expected": "입금관리", + "message": "페이지 타이틀 확인" + } + ] +} diff --git a/_backup_before_enhance/draft-box.json b/_backup_before_enhance/draft-box.json new file mode 100644 index 0000000..f71c4ac --- /dev/null +++ b/_backup_before_enhance/draft-box.json @@ -0,0 +1,188 @@ +{ + "enabled": true, + "id": "draft-box", + "name": "기안함 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "결재관리 > 기안함 메뉴의 문서 목록 조회, 검색, 필터 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "결재관리", + "level2": "기안함", + "expectedUrl": "/approval/draft", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 결재관리 > 기안함", + "action": "menu_navigate", + "level1": "결재관리", + "level2": "기안함", + "expected": { + "url_contains": "/approval/draft", + "visible": ["기안함"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "기안함 목록 표시", + "검색/필터 기능 존재", + "문서 작성 버튼 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "통계 카드 확인", + "action": "verify_element", + "checks": [ + "진행 카드 표시", + "완료 카드 표시", + "반려 카드 표시", + "임시 저장 카드 표시" + ], + "expected": "통계 카드 4개 표시" + }, + { + "id": 4, + "name": "기안함 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "문서번호 컬럼", + "문서유형 컬럼", + "제목 컬럼", + "결재자 컬럼", + "기안일시 컬럼", + "상태 컬럼" + ], + "expected": "기안함 테이블 컬럼 정상 표시" + }, + { + "id": 5, + "name": "데이터 로드 확인", + "action": "verify_detail", + "checks": [ + "기안 문서 데이터 행 존재 또는 '데이터가 없습니다' 메시지" + ], + "expected": "기안 문서 데이터 표시" + }, + { + "id": 6, + "phase": "SEARCH", + "name": "[SEARCH] 검색 기능 테스트", + "action": "fill", + "target": "input[type='search'], input[placeholder*='검색']", + "value": "테스트", + "submit": true + }, + { + "id": 7, + "phase": "SEARCH", + "name": "[SEARCH] 검색 결과 확인", + "action": "verify_detail", + "checks": [ + "검색 결과 표시 또는 결과 없음 메시지" + ], + "expected": "검색 기능 동작" + }, + { + "id": 8, + "phase": "SEARCH", + "name": "[SEARCH] 검색 초기화", + "action": "click_if_exists", + "target": "button:has-text('초기화'), button:has-text('전체'), button[class*='clear']", + "expected": "검색 초기화" + }, + { + "id": 9, + "name": "필터 기능 테스트", + "action": "click_if_exists", + "target": "select, [role='combobox'], button:has-text('임시저장')", + "expected": "필터 옵션 표시" + }, + { + "id": 10, + "phase": "READ", + "name": "[READ] 문서 상세 보기", + "action": "click_if_exists", + "target": "table tbody tr:first-child td:nth-child(2), table tbody tr:first-child", + "expected": { + "detail_view": true + } + }, + { + "id": 11, + "name": "상세 페이지/모달 확인", + "action": "verify_element", + "checks": [ + "문서 상세 정보 표시", + "문서번호 또는 제목 표시" + ], + "expected": "상세 정보 표시" + }, + { + "id": 12, + "name": "모달/상세 닫기", + "action": "close_modal_if_open", + "expected": "모달 닫힘" + }, + { + "id": 13, + "name": "페이지네이션 확인", + "action": "verify_element", + "checks": [ + "페이지 번호 또는 이전/다음 버튼" + ], + "expected": "페이지네이션 표시" + }, + { + "id": 14, + "name": "문서 작성 버튼 확인", + "action": "verify_element", + "checks": [ + "문서 작성 또는 신규 작성 버튼 존재" + ], + "expected": "문서 작성 버튼 확인" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/approvals/drafts", + "description": "기안함 목록 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/approvals/drafts/summary", + "description": "기안함 통계 카드" + } + ], + "requiredVerifications": [ + { + "id": 3, + "name": "검색/필터", + "steps": [6, 7, 8], + "criteria": "검색 기능 동작" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "기안함 목록, 검색 기능, 문서 작성 버튼 존재" + } + ], + "rollbackPlan": { + "note": "조회 전용 테스트로 데이터 변경 없음" + } +} diff --git a/_backup_before_enhance/employee-register.json b/_backup_before_enhance/employee-register.json new file mode 100644 index 0000000..419f123 --- /dev/null +++ b/_backup_before_enhance/employee-register.json @@ -0,0 +1,372 @@ +{ + "id": "employee-register", + "name": "직원 등록 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "신규 직원 정보를 입력하고 등록하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/ko/hr/employee-management", + "navigation": { + "targetUrl": "/hr/employee-management", + "urlPattern": "/hr/employee-management|/ko/hr/employee-management", + "menuHints": ["사원관리", "사원 관리", "인사관리"] + }, + "menuNavigation": { + "level1": "인사관리", + "level2": "사원관리", + "expectedUrl": "/ko/hr/employee-management", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "level1": { + "text": "인사관리", + "scrollContainer": ".sidebar-scroll, [class*='sidebar'], nav", + "maxScrollAttempts": 5, + "scrollStep": 200 + }, + "level2": { + "text": "사원관리", + "waitAfterLevel1": 500 + }, + "fallbackUrl": "/ko/hr/employee-management", + "timeout": 10000 + }, + "timeout": 60000, + "tags": ["hr", "employee", "crud"], + + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 메뉴 전체 펼치기", + "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll, [class*=\"sidebar\"], nav')?.scrollTo({top: 0, behavior: 'instant'})", + "description": "사이드바 스크롤 최상단으로 이동" + }, + { "type": "wait", "duration": 300 }, + { + "type": "evaluate", + "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()", + "description": "모두 펼치기 버튼 클릭" + }, + { + "type": "wait", + "duration": 2000, + "description": "메뉴 펼침 완료 대기" + } + ] + }, + { + "id": "step-1", + "name": "인사관리 메뉴 진입", + "description": "인사관리 > 직원관리 메뉴로 이동 (scrollAndFind 패턴)", + "menuNavigation": { + "pattern": "scrollAndFind", + "level1": { + "text": "인사관리", + "scrollContainer": ".sidebar-scroll, [class*='sidebar'], nav", + "scrollStep": 200, + "maxAttempts": 5 + }, + "level2": { + "text": "직원관리", + "waitAfterLevel1Click": 500 + } + }, + "expect": { + "url": "/hr/employee-management", + "visible": ["사원관리", "사원 등록"] + } + }, + { + "id": "step-2", + "name": "사원 등록 페이지 이동", + "click": "사원 등록", + "expect": { + "url": "/hr/employee-management?mode=new", + "visible": ["사원 등록", "사원 정보"] + } + }, + { + "id": "step-3", + "name": "사원 정보 입력", + "description": "기본 사원 정보 입력", + "form": { + "fields": [ + { "name": "이름 *", "type": "text", "value": "홍길동" }, + { "name": "주민등록번호", "type": "text", "value": "900101-1234567" }, + { "name": "휴대폰", "type": "text", "value": "010-1234-5678" }, + { "name": "이메일 *", "type": "text", "value": "test.employee@codebridge-x.com" }, + { "name": "연봉", "type": "number", "value": "50000000" } + ] + } + }, + { + "id": "step-4", + "name": "급여계좌 정보 입력", + "form": { + "fields": [ + { "name": "은행명", "type": "text", "value": "신한은행" }, + { "name": "계좌번호", "type": "text", "value": "110-123-456789" }, + { "name": "예금주", "type": "text", "value": "홍길동" } + ] + } + }, + { + "id": "step-5", + "name": "사원 상세 정보 입력", + "form": { + "fields": [ + { "name": "사원코드", "type": "text", "value": "EMP2026001" }, + { "name": "남성", "type": "radio", "value": "true" }, + { "name": "상세주소를 입력해주세요", "type": "text", "value": "123번지 4층" } + ] + } + }, + { + "id": "step-6", + "name": "인사 정보 입력", + "form": { + "fields": [ + { "name": "입사일", "type": "date", "value": "2026-01-14" } + ] + }, + "actions": [ + { "type": "click_if_exists", "target": "고용형태 선택", "description": "고용형태 드롭다운 열기" }, + { "type": "click_if_exists", "target": "정규직", "description": "정규직 선택" }, + { "type": "click_if_exists", "target": "직급 선택", "description": "직급 드롭다운 열기" }, + { "type": "click_if_exists", "target": "사원", "description": "사원 직급 선택" } + ] + }, + { + "id": "step-7", + "name": "사용자 정보 입력", + "form": { + "fields": [ + { "name": "아이디 *", "type": "text", "value": "testuser2026" }, + { "name": "비밀번호 *", "type": "text", "value": "password123!" }, + { "name": "비밀번호 확인 *", "type": "text", "value": "password123!" } + ] + } + }, + { + "id": "step-8", + "name": "등록 완료", + "click": "등록", + "waitFor": "사원관리", + "expect": { + "url": "/hr/employee-management", + "text": ["홍길동"] + } + }, + { + "id": "step-8-1", + "name": "검색 기간 설정 - 유효 기간", + "description": "등록된 사원의 입사일(2026-01-14)이 포함되는 기간으로 검색", + "actions": [ + { + "type": "click_if_exists", + "target": "input[placeholder*='시작'], input[name*='startDate'], input[type='date']:first-of-type" + }, + { + "type": "click_if_exists", + "target": "input[placeholder*='종료'], input[name*='endDate'], input[type='date']:last-of-type" + }, + { + "type": "click_if_exists", + "target": "button:has-text('검색'), .search-btn, [type='submit']" + } + ], + "expect": { + "tableContains": "홍길동", + "rowCount": ">= 1" + }, + "onFail": { + "record": true, + "message": "검색 기간 2026-01-01 ~ 2026-01-31 내 입사일(2026-01-14) 사원이 검색되지 않음", + "severity": "HIGH", + "bugType": "검색 기간 필터링 오류" + } + }, + { + "id": "step-8-2", + "name": "검색 기간 설정 - 범위 외 기간", + "description": "등록된 사원의 입사일이 포함되지 않는 기간으로 검색하여 검색되지 않음을 확인", + "actions": [ + { + "type": "click_if_exists", + "target": "input[placeholder*='시작'], input[name*='startDate'], input[type='date']:first-of-type" + }, + { + "type": "click_if_exists", + "target": "input[placeholder*='종료'], input[name*='endDate'], input[type='date']:last-of-type" + }, + { + "type": "click_if_exists", + "target": "button:has-text('검색'), .search-btn, [type='submit']" + } + ], + "expect": { + "tableNotContains": "홍길동", + "alternativeExpect": { + "emptyResult": true, + "message": "검색 결과 없음" + } + }, + "verify": { + "searchPeriodValidation": { + "inputPeriod": "2025-01-01 ~ 2025-12-31", + "employeeJoinDate": "2026-01-14", + "expectedResult": "NOT_FOUND", + "actualResultCheck": true + } + }, + "onFail": { + "record": true, + "message": "검색 기간 2025-01-01 ~ 2025-12-31 범위 외 입사일(2026-01-14) 사원이 검색됨 - 기간 필터 미작동", + "severity": "HIGH", + "bugType": "검색 기간 필터링 미작동" + } + }, + { + "id": "step-8-3", + "name": "검색 기간 초기화 및 전체 조회", + "description": "검색 조건 초기화하여 등록된 사원이 다시 표시되는지 확인", + "actions": [ + { + "type": "click_if_exists", + "target": "초기화", + "fallbackSelectors": ["button:has-text('초기화')", ".reset-btn", "button:has-text('Reset')"] + }, + { + "type": "click_if_exists", + "target": "검색", + "fallbackSelectors": ["button:has-text('검색')", ".search-btn"] + } + ], + "expect": { + "tableContains": "홍길동" + }, + "onFail": { + "record": true, + "message": "검색 초기화 후 전체 조회에서 등록된 사원이 표시되지 않음", + "severity": "MEDIUM", + "bugType": "검색 초기화 오류" + } + }, + { + "id": "step-9", + "name": "등록된 직원 상세 페이지 이동", + "description": "등록된 직원을 클릭하여 상세 페이지로 이동", + "actions": [ + { + "type": "click_if_exists", + "target": "table tbody tr:has-text('홍길동')", + "description": "해당 행 클릭하여 상세 페이지 이동" + } + ], + "expect": { + "url": "/hr/employee-management/{id}", + "visible": ["사원 상세", "수정", "삭제", "목록"] + } + }, + { + "id": "step-10", + "name": "직원 수정 모드 전환", + "description": "수정 버튼 클릭하여 편집 모드로 전환", + "click": "수정", + "expect": { + "url": "/hr/employee-management/{id}?mode=edit", + "visible": ["사원 수정", "취소", "저장"] + } + }, + { + "id": "step-11", + "name": "직원 정보 수정", + "description": "휴대폰 번호 변경", + "form": { + "fields": [ + { "name": "휴대폰", "type": "text", "value": "010-9999-8888", "clear": true } + ] + } + }, + { + "id": "step-12", + "name": "수정 저장", + "description": "수정된 직원 정보 저장", + "click": "저장", + "waitFor": "사원 상세", + "expect": { + "toast": ["수정", "완료", "성공", "저장"], + "url": "/hr/employee-management/{id}" + } + }, + { + "id": "step-12-1", + "name": "⚠️ 필수 검증: 수정 데이터 반영 확인", + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", + "description": "상세 페이지에서 수정된 휴대폰 번호 확인", + "verify": { + "fieldValue": { + "target": "휴대폰", + "expected": "010-9999-8888" + } + } + }, + { + "id": "step-13", + "name": "직원 삭제", + "description": "삭제 버튼 클릭하여 직원 삭제", + "click": "삭제", + "expect": { + "confirmDialog": true, + "dialogText": ["삭제", "하시겠습니까"] + } + }, + { + "id": "step-14", + "name": "삭제 확인", + "description": "삭제 확인 다이얼로그에서 확인 클릭", + "click": "확인", + "waitFor": "사원관리", + "expect": { + "toast": ["삭제", "완료", "성공"], + "url": "/hr/employee-management" + } + }, + { + "id": "step-15", + "name": "⚠️ 필수 검증: 삭제 데이터 반영 확인", + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 삭제 확인 필수!", + "description": "목록에서 삭제된 직원이 없어졌는지 확인", + "verify": { + "tableNotContains": "홍길동" + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/hr/employee-management", + "message": "등록 후 직원 목록 페이지로 이동해야 함" + }, + { + "type": "text", + "target": "body", + "expected": "홍길동", + "message": "등록된 직원이 목록에 표시되어야 함" + } + ] +} diff --git a/_backup_before_enhance/free-board.json b/_backup_before_enhance/free-board.json new file mode 100644 index 0000000..ba53d52 --- /dev/null +++ b/_backup_before_enhance/free-board.json @@ -0,0 +1,82 @@ +{ + "enabled": true, + "id": "free-board", + "name": "자유게시판 E2E 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "자유게시판 목록/검색/상세 기능 검증", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "게시판", + "level2": "자유게시판", + "expectedUrl": "/boards/free", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 게시판 > 자유게시판", + "action": "menu_navigate", + "level1": "게시판", + "level2": "자유게시판", + "expected": { "url_contains": "/boards/free" } + }, + { + "id": 2, + "name": "목업 감지", + "action": "verify_not_mockup" + }, + { + "id": 3, + "name": "자유게시판 페이지 확인", + "action": "verify_detail", + "checks": ["visible_text:게시판"] + }, + { + "id": 4, + "name": "테이블 확인", + "action": "verify_table" + }, + { + "id": 5, + "name": "검색 기능", + "action": "search", + "value": "테스트" + }, + { + "id": 6, + "name": "검색 후 페이지 확인", + "action": "verify_detail", + "checks": ["visible_text:게시판"] + }, + { + "id": 7, + "name": "첫 번째 행 클릭", + "action": "click_first_row" + }, + { + "id": 8, + "name": "상세 페이지 확인", + "action": "verify_detail", + "checks": ["visible_text:게시"] + }, + { + "id": 9, + "name": "모달 닫기", + "action": "close_modal_if_open" + }, + { + "id": 10, + "name": "목록 복귀", + "action": "click_if_exists", + "target": "button:has-text('목록'), a:has-text('목록')" + } + ] +} diff --git a/_backup_before_enhance/hr-attendance-admin.json b/_backup_before_enhance/hr-attendance-admin.json new file mode 100644 index 0000000..1d42ef8 --- /dev/null +++ b/_backup_before_enhance/hr-attendance-admin.json @@ -0,0 +1,83 @@ +{ + "id": "hr-attendance-admin", + "name": "근태관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "인사관리 > 근태관리 목록/상세 기능 검증", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "인사관리", + "level2": "근태관리", + "expectedUrl": "/hr/attendance-management", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 인사관리 > 근태관리", + "action": "menu_navigate", + "level1": "인사관리", + "level2": "근태관리", + "expected": { "url_contains": "/hr/attendance" } + }, + { + "id": 2, + "name": "목업 감지", + "action": "verify_not_mockup" + }, + { + "id": 3, + "name": "근태관리 페이지 확인", + "action": "verify_detail", + "checks": ["visible_text:근태"] + }, + { + "id": 4, + "name": "UI 요소 확인", + "action": "verify_detail", + "checks": ["visible_text:관리"] + }, + { + "id": 5, + "name": "필터/검색 시도", + "action": "click_if_exists", + "target": "input[type='search'], input[type='date'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input, [class*='filter'] input" + }, + { + "id": 6, + "name": "대기", + "action": "wait", + "duration": 1000 + }, + { + "id": 7, + "name": "행 클릭 시도", + "action": "click_if_exists", + "target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']" + }, + { + "id": 8, + "name": "상세 확인", + "action": "verify_detail", + "checks": ["visible_text:근태"] + }, + { + "id": 9, + "name": "모달 닫기", + "action": "close_modal_if_open" + }, + { + "id": 10, + "name": "최종 확인", + "action": "verify_detail", + "checks": ["visible_text:근태"] + } + ] +} diff --git a/_backup_before_enhance/hr-attendance-status.json b/_backup_before_enhance/hr-attendance-status.json new file mode 100644 index 0000000..13c99a0 --- /dev/null +++ b/_backup_before_enhance/hr-attendance-status.json @@ -0,0 +1,211 @@ +{ + "id": "hr-attendance-status", + "name": "근태현황 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "인사관리 > 근태현황 메뉴의 출퇴근 현황 조회/필터/출퇴근 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "인사관리", + "level2": "근태현황", + "expectedUrl": "/hr/attendance", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 인사관리 > 근태현황", + "action": "menu_navigate", + "level1": "인사관리", + "level2": "근태현황", + "expected": { + "url_contains": "/hr/attendance", + "visible": ["근태현황", "출근", "퇴근"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "출퇴근 현황 표시", + "날짜 선택 가능", + "출근/퇴근 버튼 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "근태 현황 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "날짜 컬럼", + "출근시간 컬럼", + "퇴근시간 컬럼", + "근무시간 컬럼", + "상태 컬럼" + ], + "expected": "근태 현황 테이블 표시" + }, + { + "id": 4, + "name": "오늘 근태 상태 확인", + "action": "verify_elements", + "checks": [ + "오늘 날짜 표시", + "현재 근무 상태 표시" + ], + "expected": "오늘 근태 상태 표시" + }, + { + "id": 5, + "name": "출근 버튼 확인", + "action": "verify_elements", + "checks": [ + "출근 버튼 존재" + ], + "expected": "출근 버튼 표시" + }, + { + "id": 6, + "name": "퇴근 버튼 확인", + "action": "verify_elements", + "checks": [ + "퇴근 버튼 존재" + ], + "expected": "퇴근 버튼 표시" + }, + { + "id": 7, + "phase": "FILTER", + "name": "[FILTER] 기간 필터 - 월 선택", + "action": "click_if_exists", + "target": "input[type='month'], select[name*='month'], [class*='month-picker']", + "expected": "월 선택 열림" + }, + { + "id": 8, + "phase": "FILTER", + "name": "[FILTER] 조회 적용", + "action": "click_if_exists", + "target": "button:has-text('조회'), button:has-text('검색'), button:has-text('적용')", + "expected": "필터 적용됨" + }, + { + "id": 9, + "phase": "FILTER", + "name": "[FILTER] 필터 결과 확인", + "action": "verify_detail", + "checks": [ + "선택한 월의 근태 데이터 표시" + ], + "expected": "필터 동작 확인" + }, + { + "id": 10, + "name": "근무 시간 통계 확인", + "action": "verify_elements", + "checks": [ + "총 근무시간 표시", + "정상 출근 일수 표시" + ], + "expected": "근무 통계 표시" + }, + { + "id": 11, + "name": "지각/조퇴/결근 통계 확인", + "action": "verify_elements", + "checks": [ + "지각 횟수 표시", + "조퇴 횟수 표시", + "결근 횟수 표시" + ], + "expected": "근태 이상 통계 표시" + }, + { + "id": 12, + "phase": "READ", + "name": "[READ] 특정 일자 상세 보기", + "action": "click_if_exists", + "target": "table tbody tr:first-child", + "expected": { + "detail_view": true + } + }, + { + "id": 13, + "name": "상세 근태 정보 확인", + "action": "verify_detail", + "checks": [ + "출근 시간 표시", + "퇴근 시간 표시", + "근무 시간 표시" + ], + "expected": "상세 근태 정보 표시" + }, + { + "id": 14, + "name": "엑셀 다운로드 버튼 확인", + "action": "verify_elements", + "checks": [ + "엑셀 다운로드 버튼 존재" + ], + "expected": "엑셀 다운로드 기능 표시" + }, + { + "id": 15, + "name": "인쇄 버튼 확인", + "action": "verify_elements", + "checks": [ + "인쇄 버튼 존재" + ], + "expected": "인쇄 기능 표시" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/hr/attendance", + "description": "근태 현황 조회" + }, + { + "method": "POST", + "endpoint": "/api/v1/hr/attendance/check-in", + "description": "출근 체크" + }, + { + "method": "POST", + "endpoint": "/api/v1/hr/attendance/check-out", + "description": "퇴근 체크" + }, + { + "method": "GET", + "endpoint": "/api/v1/hr/attendance/summary", + "description": "근태 통계 조회" + } + ], + "requiredVerifications": [ + { + "id": 3, + "name": "검색/필터", + "steps": [7, 8, 9], + "criteria": "기간 필터 동작" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "근태 현황, 날짜 선택, 출퇴근 버튼 존재" + } + ], + "rollbackPlan": { + "note": "조회 전용 페이지로 데이터 변경 없음 (출퇴근 체크는 별도 테스트)" + } +} diff --git a/_backup_before_enhance/hr-card.json b/_backup_before_enhance/hr-card.json new file mode 100644 index 0000000..a2a8c97 --- /dev/null +++ b/_backup_before_enhance/hr-card.json @@ -0,0 +1,81 @@ +{ + "id": "hr-card", + "name": "카드관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "인사관리 > 카드관리 목록/검색/상세 기능 검증", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "인사관리", + "level2": "카드관리", + "expectedUrl": "/hr/card-management", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 인사관리 > 카드관리", + "action": "menu_navigate", + "level1": "인사관리", + "level2": "카드관리", + "expected": { "url_contains": "/hr/card" } + }, + { + "id": 2, + "name": "목업 감지", + "action": "verify_not_mockup" + }, + { + "id": 3, + "name": "카드관리 페이지 확인", + "action": "verify_detail", + "checks": ["visible_text:카드"] + }, + { + "id": 4, + "name": "테이블 확인", + "action": "verify_table" + }, + { + "id": 5, + "name": "검색 기능", + "action": "search", + "value": "테스트" + }, + { + "id": 6, + "name": "검색 후 확인", + "action": "verify_detail", + "checks": ["visible_text:카드"] + }, + { + "id": 7, + "name": "첫 번째 행 클릭", + "action": "click_first_row" + }, + { + "id": 8, + "name": "상세 확인", + "action": "verify_detail", + "checks": ["visible_text:카드"] + }, + { + "id": 9, + "name": "모달 닫기", + "action": "close_modal_if_open" + }, + { + "id": 10, + "name": "목록 복귀", + "action": "click_if_exists", + "target": "button:has-text('목록'), a:has-text('목록')" + } + ] +} diff --git a/_backup_before_enhance/hr-department.json b/_backup_before_enhance/hr-department.json new file mode 100644 index 0000000..aa13811 --- /dev/null +++ b/_backup_before_enhance/hr-department.json @@ -0,0 +1,83 @@ +{ + "id": "hr-department", + "name": "부서관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "인사관리 > 부서관리 목록/상세 기능 검증", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "인사관리", + "level2": "부서관리", + "expectedUrl": "/hr/department-management", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 인사관리 > 부서관리", + "action": "menu_navigate", + "level1": "인사관리", + "level2": "부서관리", + "expected": { "url_contains": "/hr/department" } + }, + { + "id": 2, + "name": "목업 감지", + "action": "verify_not_mockup" + }, + { + "id": 3, + "name": "부서관리 페이지 확인", + "action": "verify_detail", + "checks": ["visible_text:부서"] + }, + { + "id": 4, + "name": "UI 요소 확인", + "action": "verify_detail", + "checks": ["visible_text:관리"] + }, + { + "id": 5, + "name": "검색 입력 시도", + "action": "click_if_exists", + "target": "input[type='search'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input" + }, + { + "id": 6, + "name": "대기", + "action": "wait", + "duration": 1000 + }, + { + "id": 7, + "name": "행 클릭 시도", + "action": "click_if_exists", + "target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']" + }, + { + "id": 8, + "name": "상세 확인", + "action": "verify_detail", + "checks": ["visible_text:부서"] + }, + { + "id": 9, + "name": "모달 닫기", + "action": "close_modal_if_open" + }, + { + "id": 10, + "name": "최종 확인", + "action": "verify_detail", + "checks": ["visible_text:부서"] + } + ] +} diff --git a/_backup_before_enhance/hr-employee.json b/_backup_before_enhance/hr-employee.json new file mode 100644 index 0000000..4e0a946 --- /dev/null +++ b/_backup_before_enhance/hr-employee.json @@ -0,0 +1,81 @@ +{ + "id": "hr-employee", + "name": "사원관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "인사관리 > 사원관리 목록/검색/상세 기능 검증", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "인사관리", + "level2": "사원관리", + "expectedUrl": "/hr/employee-management", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 인사관리 > 사원관리", + "action": "menu_navigate", + "level1": "인사관리", + "level2": "사원관리", + "expected": { "url_contains": "/hr/employee" } + }, + { + "id": 2, + "name": "목업 감지", + "action": "verify_not_mockup" + }, + { + "id": 3, + "name": "사원관리 페이지 확인", + "action": "verify_detail", + "checks": ["visible_text:사원"] + }, + { + "id": 4, + "name": "테이블 확인", + "action": "verify_table" + }, + { + "id": 5, + "name": "검색 기능", + "action": "search", + "value": "테스트" + }, + { + "id": 6, + "name": "검색 후 확인", + "action": "verify_detail", + "checks": ["visible_text:사원"] + }, + { + "id": 7, + "name": "첫 번째 행 클릭", + "action": "click_first_row" + }, + { + "id": 8, + "name": "상세 확인", + "action": "verify_detail", + "checks": ["visible_text:사원"] + }, + { + "id": 9, + "name": "모달 닫기", + "action": "close_modal_if_open" + }, + { + "id": 10, + "name": "목록 복귀", + "action": "click_if_exists", + "target": "button:has-text('목록'), a:has-text('목록')" + } + ] +} diff --git a/_backup_before_enhance/hr-salary.json b/_backup_before_enhance/hr-salary.json new file mode 100644 index 0000000..4c8141b --- /dev/null +++ b/_backup_before_enhance/hr-salary.json @@ -0,0 +1,81 @@ +{ + "id": "hr-salary", + "name": "급여관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "인사관리 > 급여관리 목록/검색/상세 기능 검증", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "인사관리", + "level2": "급여관리", + "expectedUrl": "/hr/salary-management", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 인사관리 > 급여관리", + "action": "menu_navigate", + "level1": "인사관리", + "level2": "급여관리", + "expected": { "url_contains": "/hr/salary" } + }, + { + "id": 2, + "name": "목업 감지", + "action": "verify_not_mockup" + }, + { + "id": 3, + "name": "급여관리 페이지 확인", + "action": "verify_detail", + "checks": ["visible_text:급여"] + }, + { + "id": 4, + "name": "테이블 확인", + "action": "verify_table" + }, + { + "id": 5, + "name": "검색 기능", + "action": "search", + "value": "테스트" + }, + { + "id": 6, + "name": "검색 후 확인", + "action": "verify_detail", + "checks": ["visible_text:급여"] + }, + { + "id": 7, + "name": "첫 번째 행 클릭", + "action": "click_first_row" + }, + { + "id": 8, + "name": "상세 확인", + "action": "verify_detail", + "checks": ["visible_text:급여"] + }, + { + "id": 9, + "name": "모달 닫기", + "action": "close_modal_if_open" + }, + { + "id": 10, + "name": "목록 복귀", + "action": "click_if_exists", + "target": "button:has-text('목록'), a:has-text('목록')" + } + ] +} diff --git a/_backup_before_enhance/hr-vacation.json b/_backup_before_enhance/hr-vacation.json new file mode 100644 index 0000000..c7df2ac --- /dev/null +++ b/_backup_before_enhance/hr-vacation.json @@ -0,0 +1,352 @@ +{ + "id": "hr-vacation", + "name": "휴가관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] + }, + "description": "인사관리 > 휴가관리 메뉴의 휴가 신청/조회/수정/취소 전체 CRUD 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "인사관리", + "level2": "휴가관리", + "expectedUrl": "/hr/vacation-management", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "testData": { + "create": { + "vacationType": "연차", + "startDate": "2026-02-10", + "endDate": "2026-02-10", + "reason": "E2E 자동화 테스트 휴가 신청" + }, + "update": { + "reason": "E2E 수정된 휴가 사유" + } + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 인사관리 > 휴가관리", + "action": "menu_navigate", + "level1": "인사관리", + "level2": "휴가관리", + "expected": { + "url_contains": "/hr/vacation", + "visible": [ + "휴가관리", + "휴가" + ] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "휴가 목록 표시", + "휴가 신청 버튼 존재", + "연차 현황 표시" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "휴가 현황 카드 확인", + "action": "verify_elements", + "checks": [ + "잔여 연차 표시", + "사용 연차 표시", + "총 연차 표시" + ], + "expected": "휴가 현황 카드 정상 표시" + }, + { + "id": 4, + "name": "휴가 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "휴가 유형 컬럼", + "시작일 컬럼", + "종료일 컬럼", + "상태 컬럼", + "신청일 컬럼" + ], + "expected": "휴가 테이블 컬럼 정상 표시" + }, + { + "id": 5, + "phase": "CREATE", + "name": "[CREATE] 휴가 신청 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('신청'), button:has-text('휴가 신청'), button:has-text('추가')", + "expected": { + "modal": true, + "modalTitle": "휴가 신청" + } + }, + { + "id": 6, + "phase": "CREATE", + "name": "[CREATE] 휴가 정보 입력", + "action": "click_if_exists", + "fields": [ + { + "name": "휴가 유형", + "type": "select", + "value": "연차" + }, + { + "name": "시작일", + "type": "date", + "value": "2026-02-10" + }, + { + "name": "종료일", + "type": "date", + "value": "2026-02-10" + }, + { + "name": "사유", + "type": "textarea", + "value": "E2E 자동화 테스트 휴가 신청_{timestamp}" + } + ], + "note": "타임스탬프로 고유성 보장", + "target": "form, [role='dialog'], .modal" + }, + { + "id": 7, + "phase": "CREATE", + "name": "[CREATE] 필수 검증 #2: 신청 저장", + "action": "click_if_exists", + "target": "button:has-text('신청'), button:has-text('저장'), button:has-text('확인')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "POST /api/v1/vacations", + "toast": "신청|등록|완료|성공" + }, + "expected": "휴가 신청 완료" + }, + { + "id": "7-modal-close", + "phase": "CREATE", + "name": "[CREATE] 모달 닫기 확인", + "action": "close_modal_if_open", + "expected": "모달 닫힘" + }, + { + "id": 8, + "phase": "CREATE", + "name": "[CREATE] 신청 결과 확인", + "action": "verify_detail", + "search": "E2E 자동화 테스트 휴가", + "expected": { + "row_exists": true, + "contains": [ + "연차", + "대기", + "2026-02-10" + ] + } + }, + { + "id": 9, + "phase": "READ", + "name": "[READ] 휴가 상세 페이지 진입", + "action": "click_if_exists", + "target": "table tbody tr:has-text('E2E')", + "expected": { + "url_contains": "/hr/vacation", + "visible": [ + "휴가 상세", + "수정", + "취소" + ] + } + }, + { + "id": 10, + "phase": "READ", + "name": "[READ] 상세 정보 확인", + "action": "verify_detail", + "checks": [ + "휴가 유형: 연차", + "시작일: 2026-02-10", + "종료일: 2026-02-10", + "상태: 대기", + "사유: E2E 자동화 테스트" + ], + "expected": "입력한 데이터와 일치" + }, + { + "id": 11, + "phase": "UPDATE", + "name": "[UPDATE] 수정 모드 진입", + "action": "click_if_exists", + "target": "button:has-text('수정')", + "expected": { + "modal_or_edit_mode": true, + "fields_editable": true + } + }, + { + "id": 12, + "phase": "UPDATE", + "name": "[UPDATE] 사유 수정", + "action": "click_if_exists", + "target": "textarea[name*='reason'], input[placeholder*='사유']", + "value": "E2E 수정된 휴가 사유_{timestamp}", + "clear": true + }, + { + "id": 13, + "phase": "UPDATE", + "name": "[UPDATE] 필수 검증 #2: 수정 저장", + "action": "click_if_exists", + "target": "button:has-text('저장'), button:has-text('수정')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "PUT /api/v1/vacations/", + "toast": "수정|완료|성공" + }, + "expected": "수정 완료" + }, + { + "id": 14, + "phase": "UPDATE", + "name": "[UPDATE] 수정 결과 확인", + "action": "verify_detail", + "checks": [ + "사유: E2E 수정된 휴가" + ], + "expected": "수정된 데이터 반영" + }, + { + "id": 15, + "phase": "DELETE", + "name": "[DELETE] 취소 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('취소'), button:has-text('신청 취소')", + "expected": { + "confirm_dialog": true, + "dialog_message": "취소|정말" + } + }, + { + "id": 16, + "phase": "DELETE", + "name": "[DELETE] 필수 검증 #6: 취소 확인", + "action": "click_if_exists", + "target": "button:has-text('확인'), button:has-text('예')", + "verify": { + "api_call": "DELETE /api/v1/vacations/", + "toast": "취소|삭제|완료|성공", + "redirect": "/hr/vacation" + }, + "expected": "휴가 취소 완료 및 목록 복귀" + }, + { + "id": 17, + "phase": "DELETE", + "name": "[DELETE] 취소 결과 확인", + "action": "verify_detail", + "search": "E2E 수정된 휴가", + "expected": { + "row_exists": false, + "message": "테스트 휴가 신청이 목록에서 제거됨" + } + }, + { + "id": 18, + "phase": "VERIFY", + "name": "[VERIFY] 연차 잔여일 확인", + "action": "verify_elements", + "checks": [ + "잔여 연차가 원래 값으로 복원" + ], + "expected": "휴가 취소 후 연차 복원 확인" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/vacations", + "description": "휴가 목록 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/vacations/summary", + "description": "휴가 현황 조회" + }, + { + "method": "POST", + "endpoint": "/api/v1/vacations", + "description": "휴가 신청" + }, + { + "method": "GET", + "endpoint": "/api/v1/vacations/{id}", + "description": "휴가 상세 조회" + }, + { + "method": "PUT", + "endpoint": "/api/v1/vacations/{id}", + "description": "휴가 수정" + }, + { + "method": "DELETE", + "endpoint": "/api/v1/vacations/{id}", + "description": "휴가 취소" + } + ], + "requiredVerifications": [ + { + "id": 2, + "name": "등록/저장 버튼", + "steps": [ + 7, + 13 + ], + "criteria": "API 호출 + 성공 토스트 + 데이터 반영" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [ + 2 + ], + "criteria": "휴가 목록, 신청 버튼, 현황 카드 존재" + }, + { + "id": 6, + "name": "삭제 기능", + "steps": [ + 15, + 16, + 17 + ], + "criteria": "DELETE API + 목록에서 제거 + 연차 복원" + } + ], + "rollbackPlan": { + "onCreateFail": "모달 닫기 → 다음 테스트 영향 없음", + "onUpdateFail": "테스트 휴가 수동 취소 필요", + "onDeleteFail": "테스트 휴가 수동 취소 필요", + "cleanupRequired": "E2E_ 접두사 휴가 신청은 테스트 데이터" + } +} \ No newline at end of file diff --git a/_backup_before_enhance/inventory-status.json b/_backup_before_enhance/inventory-status.json new file mode 100644 index 0000000..3ad0a6a --- /dev/null +++ b/_backup_before_enhance/inventory-status.json @@ -0,0 +1,250 @@ +{ + "id": "inventory-status", + "name": "재고현황 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "자재관리 > 재고현황 페이지의 재고 조회 및 엑셀 다운로드 기능을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/material/stock-status", + "navigation": { + "targetUrl": "/material/stock-status", + "urlPattern": "/material/stock-status|/ko/material/stock-status", + "menuHints": ["재고현황", "재고 현황", "자재관리"] + }, + "menuNavigation": { + "level1": "자재관리", + "level2": "재고현황", + "expectedUrl": "/material/stock-status", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebar": { + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 5, + "waitAfterScroll": 300 + }, + "level1": { + "text": "자재관리", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "재고현황", + "waitAfterClick": 300 + }, + "fallbackUrl": "/material/stock-status", + "expectedUrl": "/material/stock-status" + }, + "timeout": 90000, + "tags": ["material", "inventory", "read-only"], + + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 메뉴 전체 펼치기", + "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 300 }, + { + "type": "evaluate", + "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" + }, + { "type": "wait", "duration": 2000 } + ] + }, + { + "id": "step-1", + "name": "자재관리 메뉴 진입", + "description": "자재관리 > 재고현황 메뉴로 이동", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "자재관리", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click_if_exists", "target": "자재관리" }, + { "type": "wait", "duration": 500 }, + { "type": "click_if_exists", "target": "재고현황" } + ], + "expect": { + "url": "/material/stock-status", + "visible": ["재고 목록", "엑셀 다운로드"] + }, + "fallback": { + "type": "navigate", + "url": "/material/stock-status" + } + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "통계 카드와 테이블 구조 확인", + "verify": { + "visible": ["전체 품목", "정상 재고", "재고 부족", "재고 없음"], + "tableColumns": ["번호", "품목코드", "품목명", "품목유형", "단위", "재고량", "안전재고", "LOT", "상태", "위치"] + } + }, + { + "id": "step-3", + "name": "필수 검증 #3: 품목유형 탭 필터 - 원자재", + "description": "원자재 탭 클릭하여 필터링 확인", + "actions": [ + { "type": "click_if_exists", "target": "원자재", "role": "tab" }, + { "type": "wait", "duration": 500 } + ], + "expect": { + "tabActive": "원자재", + "dataFiltered": true + } + }, + { + "id": "step-4", + "name": "필수 검증 #3: 품목유형 탭 필터 - 부자재", + "description": "부자재 탭 클릭하여 필터링 확인", + "actions": [ + { "type": "click_if_exists", "target": "부자재", "role": "tab" }, + { "type": "wait", "duration": 500 } + ], + "expect": { + "tabActive": "부자재", + "dataFiltered": true + } + }, + { + "id": "step-5", + "name": "필수 검증 #3: 품목유형 탭 필터 - 소모품", + "description": "소모품 탭 클릭하여 필터링 확인", + "actions": [ + { "type": "click_if_exists", "target": "소모품", "role": "tab" }, + { "type": "wait", "duration": 500 } + ], + "expect": { + "tabActive": "소모품", + "dataFiltered": true + } + }, + { + "id": "step-6", + "name": "전체 탭으로 복귀", + "description": "전체 탭 클릭하여 모든 재고 표시", + "actions": [ + { "type": "click_if_exists", "target": "전체", "role": "tab" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "tabActive": "전체", + "allDataShown": true + } + }, + { + "id": "step-7", + "name": "필수 검증 #1: 엑셀 다운로드", + "description": "엑셀 다운로드 버튼 동작 확인", + "actions": [ + { "type": "click_if_exists", "target": "엑셀 다운로드" }, + { "type": "wait", "duration": 1000 } + ], + "expect": { + "downloadTriggered": true, + "noErrorPage": true + }, + "verify": { + "apiCall": "GET /api/material/stock-status/export" + } + }, + { + "id": "step-8", + "name": "재고 상세 열기", + "description": "재고 항목 클릭하여 상세 보기", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('tbody tr')?.click()" + } + ], + "expect": { + "pageOrModal": "재고 상세", + "visible": ["품목코드", "품목명", "재고량", "LOT"] + } + }, + { + "id": "step-9", + "name": "상세 닫기", + "description": "ESC 키로 상세 닫기 또는 뒤로가기", + "actions": [ + { "type": "press", "key": "Escape" }, + { "type": "wait", "duration": 300 } + ] + }, + { + "id": "step-10", + "name": "페이지네이션 확인", + "description": "페이지네이션 동작 확인", + "actions": [ + { "type": "click_if_exists", "target": "다음" }, + { "type": "wait", "duration": 500 } + ], + "expect": { + "pageChanged": true + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/material/stock-status", + "message": "재고현황 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "button:has-text('엑셀 다운로드')", + "message": "엑셀 다운로드 버튼이 표시되어야 함" + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 1, + "name": "파일 다운로드", + "trigger": "엑셀 다운로드 버튼", + "verification": "Network API + 실제 다운로드 확인", + "failCondition": "Console LOG만으로 PASS 금지" + }, + { + "id": 3, + "name": "검색/필터", + "trigger": "품목유형 탭 필터", + "verification": "데이터 변화 확인", + "failCondition": "필터 적용 후 데이터 무변화" + } + ] + }, + + "notes": { + "testScope": "재고현황 조회 및 필터링, 엑셀 다운로드 테스트", + "pageType": "조회 전용 (입고관리에서 재고 증가, 출하관리에서 재고 감소)", + "statsCards": ["전체 품목", "정상 재고", "재고 부족", "재고 없음"], + "typeTabs": ["전체", "원자재", "부자재", "소모품"], + "tableColumns": ["번호", "품목코드", "품목명", "품목유형", "단위", "재고량", "안전재고", "LOT", "상태", "위치"], + "prerequisites": "로그인된 사용자" + } +} diff --git a/_backup_before_enhance/item-management.json b/_backup_before_enhance/item-management.json new file mode 100644 index 0000000..95ad6d1 --- /dev/null +++ b/_backup_before_enhance/item-management.json @@ -0,0 +1,150 @@ +{ + "id": "item-management", + "name": "품목관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "생산관리 > 품목관리 메뉴의 품목 목록 조회 및 UI 검증 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "생산관리", + "level2": "품목관리", + "expectedUrl": "/production/screen-production", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 생산관리 > 품목관리", + "action": "menu_navigate", + "level1": "생산관리", + "level2": "품목관리", + "expected": { + "url_contains": "/production", + "visible": ["품목관리", "품목"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "품목 목록 또는 통계 카드 표시", + "품목 등록 버튼 존재", + "검색 기능 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "품목 UI 구조 확인", + "action": "verify_elements", + "checks": [ + "통계 카드 영역", + "품목 목록 테이블", + "검색 입력 필드", + "탭/필터 버튼" + ], + "expected": "품목관리 UI 정상 표시" + }, + { + "id": 4, + "name": "테이블 구조 확인", + "action": "verify_table", + "checks": [ + "품목코드 컬럼", + "품목명 컬럼", + "규격 컬럼" + ], + "expected": "품목 테이블 표시" + }, + { + "id": 5, + "phase": "READ", + "name": "[READ] 품목 목록 데이터 확인", + "action": "verify_detail", + "checks": [ + "품목 목록 데이터 표시됨" + ], + "expected": "품목 목록 정상" + }, + { + "id": 6, + "phase": "READ", + "name": "[READ] 첫 번째 행 클릭", + "action": "click_if_exists", + "target": "table tbody tr:first-child, button:has-text('상세')" + }, + { + "id": 7, + "phase": "READ", + "name": "[READ] 품목 상세 정보 확인", + "action": "verify_detail", + "checks": [ + "품목 상세 정보 표시" + ], + "expected": "품목 상세 정보 확인" + }, + { + "id": 8, + "name": "상세 모달/페이지 닫기", + "action": "click_if_exists", + "target": "button:has-text('닫기'), button:has-text('Close'), button:has-text('목록'), [class*='close']" + }, + { + "id": 9, + "name": "모달 닫기 확인", + "action": "close_modal_if_open", + "expected": "모달 닫힘" + }, + { + "id": 10, + "name": "탭/필터 기능 확인", + "action": "click_if_exists", + "target": "button:has-text('전체'), [class*='tab']:has-text('전체')" + }, + { + "id": 11, + "name": "등록 버튼 존재 확인", + "action": "verify_elements", + "checks": [ + "품목 등록 버튼 존재" + ], + "expected": "등록 버튼 표시" + }, + { + "id": 12, + "name": "품목관리 페이지 최종 확인", + "action": "verify_elements", + "checks": [ + "품목 목록 구조 정상", + "통계 카드 또는 요약 정보 표시" + ], + "expected": "품목관리 페이지 정상" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/items", + "description": "품목 목록 조회" + } + ], + "requiredVerifications": [ + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "품목 목록, 등록 버튼, 검색 기능 존재" + } + ], + "rollbackPlan": { + "note": "READ-only 패턴으로 안정성 우선, 비표준 포맷 제거" + } +} diff --git a/_backup_before_enhance/item-master.json b/_backup_before_enhance/item-master.json new file mode 100644 index 0000000..30d98ac --- /dev/null +++ b/_backup_before_enhance/item-master.json @@ -0,0 +1,83 @@ +{ + "id": "item-master", + "name": "품목기준관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "품목관리 > 품목기준관리 목록/상세 기능 검증", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "품목관리", + "level2": "품목기준관리", + "expectedUrl": "/master-data/item-master-data-management", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 품목관리 > 품목기준관리", + "action": "menu_navigate", + "level1": "품목관리", + "level2": "품목기준관리", + "expected": { "url_contains": "/master-data" } + }, + { + "id": 2, + "name": "목업 감지", + "action": "verify_not_mockup" + }, + { + "id": 3, + "name": "품목기준관리 페이지 확인", + "action": "verify_detail", + "checks": ["visible_text:품목"] + }, + { + "id": 4, + "name": "UI 요소 확인", + "action": "verify_detail", + "checks": ["visible_text:관리"] + }, + { + "id": 5, + "name": "검색 입력 시도", + "action": "click_if_exists", + "target": "input[type='search'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input" + }, + { + "id": 6, + "name": "대기", + "action": "wait", + "duration": 1000 + }, + { + "id": 7, + "name": "행 클릭 시도", + "action": "click_if_exists", + "target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']" + }, + { + "id": 8, + "name": "상세 확인", + "action": "verify_detail", + "checks": ["visible_text:품목"] + }, + { + "id": 9, + "name": "모달 닫기", + "action": "close_modal_if_open" + }, + { + "id": 10, + "name": "최종 확인", + "action": "verify_detail", + "checks": ["visible_text:품목"] + } + ] +} diff --git a/_backup_before_enhance/login.json b/_backup_before_enhance/login.json new file mode 100644 index 0000000..c17d35e --- /dev/null +++ b/_backup_before_enhance/login.json @@ -0,0 +1,321 @@ +{ + "id": "login-test", + "name": "로그인 테스트 (끝판왕)", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] + }, + "description": "로그인 페이지 UI 검증, 로그인 실패/성공, 대시보드 진입, 로그아웃까지 전체 인증 플로우 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "timeout": 30000, + "tags": [ + "auth", + "login", + "critical" + ], + "auth": { + "username": "TestUser5", + "password": "password123!", + "wrongPassword": "wrongpassword" + }, + "selectors": { + "usernameInput": "#userId", + "passwordInput": "#password", + "rememberMeCheckbox": "input[type='checkbox']", + "forgotPasswordButton": "button:has-text('비밀번호를 잊으셨나요?')", + "loginButton": "button[type='submit']", + "passwordToggle": "button:has(.lucide-eye)", + "userProfileButton": "button:has-text('홍킬동')", + "logoutButton": "button:has-text('로그아웃')" + }, + "steps": [ + { + "id": 1, + "name": "로그인 페이지 접속", + "action": "navigate", + "target": "/ko/login", + "expected": { + "url": "/ko/login", + "visible": [ + "로그인", + "환영합니다", + "SAM MES", + "아이디", + "비밀번호" + ] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "아이디 입력 필드 존재 및 입력 가능", + "비밀번호 입력 필드 존재 및 입력 가능", + "로그인 버튼 존재 및 클릭 가능" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "UI 요소 검증 - 입력 필드", + "action": "verify_elements", + "checks": [ + "아이디 입력 필드 placeholder: '아이디를 입력하세요'", + "비밀번호 입력 필드 placeholder: '비밀번호를 입력하세요'", + "비밀번호 표시/숨김 토글 버튼 존재" + ], + "expected": "모든 입력 필드 정상" + }, + { + "id": 4, + "name": "UI 요소 검증 - 옵션", + "action": "verify_elements", + "checks": [ + "로그인 상태 유지 체크박스 존재", + "비밀번호 찾기 링크 존재", + "로그인 버튼 존재" + ], + "expected": "모든 옵션 요소 정상" + }, + { + "id": 5, + "name": "비밀번호 표시/숨김 토글 테스트", + "action": "click_if_exists", + "target": "passwordToggle", + "expected": "비밀번호 필드 type이 'text'로 변경 (표시 모드)" + }, + { + "id": 6, + "name": "비밀번호 숨김 복원", + "action": "click_if_exists", + "target": "passwordToggle", + "expected": "비밀번호 필드 type이 'password'로 복원 (숨김 모드)" + }, + { + "id": 7, + "name": "로그인 실패 테스트 - 빈 필드", + "action": "click_if_exists", + "target": "loginButton", + "expected": "유효성 검사 에러 또는 로그인 실패 메시지" + }, + { + "id": 8, + "name": "아이디 입력", + "action": "click_if_exists", + "target": "usernameInput", + "value": "TestUser5", + "expected": "아이디 필드에 값 입력됨" + }, + { + "id": 9, + "name": "로그인 실패 테스트 - 잘못된 비밀번호", + "action": "click_if_exists", + "target": "passwordInput", + "value": "wrongpassword", + "expected": "비밀번호 필드에 값 입력됨" + }, + { + "id": 10, + "name": "잘못된 비밀번호로 로그인 시도", + "action": "click_if_exists", + "target": "loginButton", + "expected": "로그인 실패 에러 메시지 표시", + "verify": { + "type": "error_message", + "contains": [ + "실패", + "오류", + "일치하지 않", + "incorrect", + "failed" + ] + } + }, + { + "id": 11, + "name": "비밀번호 필드 초기화", + "action": "click_if_exists", + "target": "passwordInput", + "expected": "비밀번호 필드 비워짐" + }, + { + "id": 12, + "name": "올바른 비밀번호 입력", + "action": "click_if_exists", + "target": "passwordInput", + "value": "password123!", + "expected": "올바른 비밀번호 입력됨" + }, + { + "id": 13, + "name": "필수 검증 #2: 로그인 버튼 클릭", + "action": "click_if_exists", + "target": "loginButton", + "verify": { + "url_should_change": true, + "no_error_page": true, + "api_call": "POST /api/v1/auth/login" + }, + "expected": "로그인 성공 및 대시보드로 이동" + }, + { + "id": 14, + "name": "대시보드 페이지 확인", + "action": "wait_for_navigation", + "expected": { + "url_contains": "/dashboard", + "visible": [ + "대시보드", + "홍킬동" + ] + } + }, + { + "id": 15, + "name": "사용자 정보 표시 확인", + "action": "verify_elements", + "checks": [ + "사용자명 '홍킬동' 표시", + "메뉴 영역 표시", + "SAM 로고 표시" + ], + "expected": "사용자 정보 및 메인 레이아웃 정상" + }, + { + "id": 16, + "name": "세션 유지 확인 - 페이지 새로고침", + "action": "reload", + "expected": "새로고침 후에도 로그인 상태 유지" + }, + { + "id": 17, + "name": "새로고침 후 대시보드 유지 확인", + "action": "verify_url", + "expected": { + "url_contains": "/dashboard", + "visible": [ + "대시보드", + "홍킬동" + ] + } + }, + { + "id": 18, + "name": "사용자 프로필 메뉴 열기", + "action": "click_if_exists", + "target": "userProfileButton", + "expected": "사용자 메뉴 드롭다운 열림" + }, + { + "id": 19, + "name": "로그아웃 버튼 클릭", + "action": "click_if_exists", + "target": "logoutButton", + "expected": "로그아웃 처리 및 로그인 페이지로 이동" + }, + { + "id": 20, + "name": "로그아웃 후 로그인 페이지 확인", + "action": "verify_element", + "expected": { + "url_contains": "/login", + "visible": [ + "로그인", + "아이디", + "비밀번호" + ] + } + }, + { + "id": 21, + "name": "로그아웃 후 보호된 페이지 접근 시도", + "action": "navigate", + "target": "/ko/dashboard", + "expected": "로그인 페이지로 리다이렉트" + }, + { + "id": 22, + "name": "재로그인 테스트", + "actions": [ + { + "type": "click_if_exists", + "target": "usernameInput", + "value": "TestUser5" + }, + { + "type": "click_if_exists", + "target": "passwordInput", + "value": "password123!" + }, + { + "type": "click_if_exists", + "target": "loginButton" + } + ], + "expected": "재로그인 성공" + }, + { + "id": 23, + "name": "최종 확인 - 대시보드 진입", + "action": "verify_url", + "expected": { + "url_contains": "/dashboard", + "visible": [ + "대시보드", + "홍킬동" + ] + } + } + ], + "requiredVerifications": [ + { + "id": 2, + "name": "등록/저장 버튼 (로그인)", + "steps": [ + 13 + ], + "criteria": "로그인 버튼 클릭 → API 호출 → 대시보드 이동" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [ + 2 + ], + "criteria": "입력 필드 동작, 버튼 클릭 가능" + } + ], + "expectedAPIs": [ + { + "method": "POST", + "endpoint": "/api/v1/auth/login", + "description": "로그인 인증" + }, + { + "method": "GET", + "endpoint": "/api/v1/auth/me", + "description": "현재 사용자 정보 조회" + }, + { + "method": "POST", + "endpoint": "/api/v1/auth/logout", + "description": "로그아웃" + } + ], + "testData": { + "validUser": { + "username": "TestUser5", + "password": "password123!", + "displayName": "홍킬동" + }, + "invalidPassword": "wrongpassword" + } +} \ No newline at end of file diff --git a/_backup_before_enhance/material-receiving.json b/_backup_before_enhance/material-receiving.json new file mode 100644 index 0000000..6d84fa5 --- /dev/null +++ b/_backup_before_enhance/material-receiving.json @@ -0,0 +1,301 @@ +{ + "id": "material-receiving", + "name": "입고관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "자재관리 > 입고관리 메뉴의 입고 조회/등록/수정/삭제 전체 CRUD 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "자재관리", + "level2": "입고관리", + "expectedUrl": "/material/receiving-management", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "testData": { + "create": { + "itemName": "E2E_TEST_입고품목", + "quantity": "100", + "vendor": "테스트거래처", + "memo": "E2E 자동화 테스트 입고" + }, + "update": { + "quantity": "150", + "memo": "E2E 수정된 입고 메모" + } + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 자재관리 > 입고관리", + "action": "menu_navigate", + "level1": "자재관리", + "level2": "입고관리", + "expected": { + "url_contains": "/material/receiving", + "visible": ["입고관리", "입고"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "입고 목록 표시", + "입고 등록 버튼 존재", + "검색/필터 기능 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "입고 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "입고일 컬럼", + "품목 컬럼", + "수량 컬럼", + "거래처 컬럼", + "상태 컬럼" + ], + "expected": "입고 테이블 컬럼 정상 표시" + }, + { + "id": 4, + "name": "검색 기능 테스트", + "action": "click_if_exists", + "target": "input[placeholder*='검색']", + "value": "테스트", + "expected": { + "data_filtered": true + } + }, + { + "id": 5, + "phase": "CREATE", + "name": "[CREATE] 입고 등록 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('등록'), button:has-text('입고 등록'), button:has-text('추가')", + "expected": { + "modal": true, + "modalTitle": "입고 등록" + } + }, + { + "id": 6, + "phase": "CREATE", + "name": "[CREATE] 입고 정보 입력", + "action": "fill_form", + "fields": [ + {"name": "입고일", "type": "date", "value": "2026-02-03"}, + {"name": "품목", "type": "select", "value": "E2E_TEST_입고품목"}, + {"name": "수량", "type": "number", "value": "100"}, + {"name": "거래처", "type": "select", "value": "테스트거래처"}, + {"name": "메모", "type": "text", "value": "E2E 자동화 테스트 입고_{timestamp}"} + ], + "note": "타임스탬프로 고유성 보장" + }, + { + "id": 7, + "phase": "CREATE", + "name": "[CREATE] 필수 검증 #2: 등록 저장", + "action": "click_if_exists", + "target": "button:has-text('저장'), button:has-text('등록')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "POST /api/v1/receivings", + "toast": "등록|완료|성공" + }, + "expected": "입고 등록 완료" + }, + { + "id": "7-modal-close", + "phase": "CREATE", + "name": "[CREATE] 모달 닫기 확인", + "action": "close_modal_if_open", + "expected": "모달 닫힘" + }, + { + "id": 8, + "phase": "CREATE", + "name": "[CREATE] 등록 결과 확인", + "action": "verify_detail", + "search": "E2E 자동화 테스트 입고", + "expected": { + "row_exists": true, + "contains": ["E2E", "100"] + } + }, + { + "id": 9, + "phase": "READ", + "name": "[READ] 입고 상세 페이지 진입", + "action": "click_if_exists", + "target": "table tbody tr:has-text('E2E')", + "expected": { + "url_contains": "/material/receiving", + "visible": ["입고 상세", "수정", "삭제"] + } + }, + { + "id": 10, + "phase": "READ", + "name": "[READ] 상세 정보 확인", + "action": "verify_detail", + "checks": [ + "수량: 100", + "메모: E2E 자동화 테스트" + ], + "expected": "입력한 데이터와 일치" + }, + { + "id": 11, + "phase": "UPDATE", + "name": "[UPDATE] 수정 모드 진입", + "action": "click_if_exists", + "target": "button:has-text('수정')", + "expected": { + "url_contains": "mode=edit", + "fields_editable": true + } + }, + { + "id": 12, + "phase": "UPDATE", + "name": "[UPDATE] 수량 수정", + "action": "click_if_exists", + "target": "input[name*='quantity'], input[placeholder*='수량']", + "value": "150", + "clear": true + }, + { + "id": 13, + "phase": "UPDATE", + "name": "[UPDATE] 메모 수정", + "action": "click_if_exists", + "target": "textarea[name*='memo'], input[placeholder*='메모']", + "value": "E2E 수정된 입고 메모_{timestamp}", + "clear": true + }, + { + "id": 14, + "phase": "UPDATE", + "name": "[UPDATE] 필수 검증 #2: 수정 저장", + "action": "click_if_exists", + "target": "button:has-text('저장')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "PUT /api/v1/receivings/", + "toast": "수정|완료|성공" + }, + "expected": "수정 완료" + }, + { + "id": 15, + "phase": "UPDATE", + "name": "[UPDATE] 수정 결과 확인", + "action": "verify_detail", + "checks": [ + "수량: 150", + "메모: E2E 수정된 입고" + ], + "expected": "수정된 데이터 반영" + }, + { + "id": 16, + "phase": "DELETE", + "name": "[DELETE] 삭제 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('삭제')", + "expected": { + "confirm_dialog": true, + "dialog_message": "삭제|정말" + } + }, + { + "id": 17, + "phase": "DELETE", + "name": "[DELETE] 필수 검증 #6: 삭제 확인", + "action": "click_if_exists", + "target": "button:has-text('확인'), button:has-text('삭제')", + "verify": { + "api_call": "DELETE /api/v1/receivings/", + "toast": "삭제|완료|성공", + "redirect": "/material/receiving" + }, + "expected": "삭제 완료 및 목록 복귀" + }, + { + "id": 18, + "phase": "DELETE", + "name": "[DELETE] 삭제 결과 확인", + "action": "verify_detail", + "search": "E2E 수정된 입고", + "expected": { + "row_exists": false, + "message": "테스트 입고가 목록에서 제거됨" + } + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/receivings", + "description": "입고 목록 조회" + }, + { + "method": "POST", + "endpoint": "/api/v1/receivings", + "description": "입고 등록" + }, + { + "method": "GET", + "endpoint": "/api/v1/receivings/{id}", + "description": "입고 상세 조회" + }, + { + "method": "PUT", + "endpoint": "/api/v1/receivings/{id}", + "description": "입고 수정" + }, + { + "method": "DELETE", + "endpoint": "/api/v1/receivings/{id}", + "description": "입고 삭제" + } + ], + "requiredVerifications": [ + { + "id": 2, + "name": "등록/저장 버튼", + "steps": [7, 14], + "criteria": "API 호출 + 성공 토스트 + 데이터 반영" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "입고 목록, 등록 버튼, 필터 존재" + }, + { + "id": 6, + "name": "삭제 기능", + "steps": [16, 17, 18], + "criteria": "DELETE API + 목록에서 제거" + } + ], + "rollbackPlan": { + "onCreateFail": "모달 닫기", + "onUpdateFail": "테스트 입고 수동 삭제 필요", + "onDeleteFail": "테스트 입고 수동 삭제 필요", + "cleanupRequired": "E2E_TEST_ 접두사 입고는 테스트 데이터" + } +} diff --git a/_backup_before_enhance/material-stock.json b/_backup_before_enhance/material-stock.json new file mode 100644 index 0000000..1253d71 --- /dev/null +++ b/_backup_before_enhance/material-stock.json @@ -0,0 +1,212 @@ +{ + "id": "material-stock", + "name": "재고현황 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "자재관리 > 재고현황 메뉴의 재고 현황 조회/검색/필터/다운로드 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "자재관리", + "level2": "재고현황", + "expectedUrl": "/material/stock-status", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 자재관리 > 재고현황", + "action": "menu_navigate", + "level1": "자재관리", + "level2": "재고현황", + "expected": { + "url_contains": "/material/stock", + "visible": ["재고현황", "재고"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "재고 목록 표시", + "검색 기능 존재", + "필터 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "재고현황 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "품목코드 컬럼", + "품목명 컬럼", + "현재고 컬럼", + "안전재고 컬럼", + "위치/창고 컬럼" + ], + "expected": "재고 테이블 표시" + }, + { + "id": 4, + "phase": "READ", + "name": "[READ] 재고 데이터 확인", + "action": "verify_detail", + "checks": [ + "재고 데이터 행 존재 또는 '데이터 없음' 메시지" + ], + "expected": "재고 데이터 표시" + }, + { + "id": 5, + "phase": "SEARCH", + "name": "[SEARCH] 품목 검색", + "action": "fill", + "target": "input[type='search'], input[placeholder*='검색']", + "value": "테스트", + "submit": true + }, + { + "id": 6, + "phase": "SEARCH", + "name": "[SEARCH] 검색 결과 확인", + "action": "verify_detail", + "checks": [ + "검색 결과 표시 또는 결과 없음 메시지" + ], + "expected": "검색 기능 동작" + }, + { + "id": 7, + "phase": "SEARCH", + "name": "[SEARCH] 검색 초기화", + "action": "click_if_exists", + "target": "button:has-text('초기화'), button:has-text('리셋'), button[class*='clear']", + "expected": "검색 초기화" + }, + { + "id": 8, + "phase": "FILTER", + "name": "[FILTER] 창고/위치 필터", + "action": "click_if_exists", + "target": "select[name*='warehouse'], select[name*='location'], button:has-text('창고')", + "expected": "창고 필터 옵션 표시" + }, + { + "id": 9, + "phase": "FILTER", + "name": "[FILTER] 재고 상태 필터", + "action": "verify_elements", + "checks": [ + "재고 부족/적정/과다 필터 가능" + ], + "expected": "재고 상태 필터 표시" + }, + { + "id": 10, + "name": "안전재고 이하 품목 확인", + "action": "verify_elements", + "checks": [ + "안전재고 이하 품목 강조 표시", + "부족 재고 경고 표시" + ], + "expected": "안전재고 알림 표시" + }, + { + "id": 11, + "name": "재고 이동 이력 링크", + "action": "verify_elements", + "checks": [ + "재고 이동 이력 조회 버튼 또는 링크" + ], + "expected": "이력 조회 기능 표시" + }, + { + "id": 12, + "name": "재고 현황 요약", + "action": "verify_elements", + "checks": [ + "총 품목 수", + "재고 금액 합계", + "부족 품목 수" + ], + "expected": "요약 정보 표시" + }, + { + "id": 13, + "name": "필수 검증 #1: 엑셀 다운로드", + "action": "click_if_exists", + "target": "button:has-text('엑셀'), button:has-text('Excel'), button:has-text('다운로드')", + "verify": { + "api_call": "GET /api/v1/material/stock/export", + "file_download": true + }, + "expected": "엑셀 파일 다운로드" + }, + { + "id": 14, + "name": "인쇄 기능 확인", + "action": "verify_elements", + "checks": [ + "인쇄 버튼 존재" + ], + "expected": "인쇄 기능 표시" + }, + { + "id": 15, + "name": "재고 조정 버튼 확인", + "action": "verify_elements", + "checks": [ + "재고 조정 버튼 존재 여부" + ], + "expected": "재고 조정 기능 확인" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/material/stock", + "description": "재고현황 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/material/stock/export", + "description": "재고현황 엑셀 다운로드" + }, + { + "method": "GET", + "endpoint": "/api/v1/material/warehouses", + "description": "창고 목록 조회" + } + ], + "requiredVerifications": [ + { + "id": 1, + "name": "엑셀 다운로드", + "steps": [13], + "criteria": "API 호출 + 파일 다운로드" + }, + { + "id": 3, + "name": "검색/필터", + "steps": [5, 6, 7], + "criteria": "검색 + 필터 기능 동작" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "재고 목록, 검색 기능, 필터 존재" + } + ], + "rollbackPlan": { + "note": "조회 전용 페이지로 데이터 변경 없음" + } +} diff --git a/_backup_before_enhance/pdf-download-test.json b/_backup_before_enhance/pdf-download-test.json new file mode 100644 index 0000000..0cc3283 --- /dev/null +++ b/_backup_before_enhance/pdf-download-test.json @@ -0,0 +1,429 @@ +{ + "id": "pdf-download-test", + "name": "PDF 다운로드 전체 검사", + "description": "PDF 다운로드 버튼이 있는 모든 페이지를 순회하며 PDF 다운로드 기능과 품질을 검증하는 E2E 테스트", + "version": "1.0.0", + "createdAt": "2026-01-29", + "screenshotPolicy": { + "onErrorOnly": false, + "captureOn": ["before-download", "after-download", "error", "fail"] + }, + "baseUrl": "https://dev.codebridge-x.com", + "timeout": 180000, + "tags": ["pdf", "download", "quality", "export"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + } + }, + + "pdfTargets": [ + { + "id": "draft-box", + "name": "기안함 PDF", + "menuNavigation": { + "level1": "결재관리", + "level2": "기안함" + }, + "accessMethod": "modal", + "triggerAction": "clickFirstRow", + "pdfButtonSelector": "button:has-text('PDF')", + "pdfApiPattern": "/api/v1/approvals/*/pdf", + "expectedContentType": "application/pdf" + }, + { + "id": "approval-box", + "name": "결재함 PDF", + "menuNavigation": { + "level1": "결재관리", + "level2": "결재함" + }, + "accessMethod": "modal", + "triggerAction": "clickFirstRow", + "pdfButtonSelector": "button:has-text('PDF')", + "pdfApiPattern": "/api/v1/approvals/*/pdf", + "expectedContentType": "application/pdf" + }, + { + "id": "reference-box", + "name": "참조함 PDF", + "menuNavigation": { + "level1": "결재관리", + "level2": "참조함" + }, + "accessMethod": "modal", + "triggerAction": "clickFirstRow", + "pdfButtonSelector": "button:has-text('PDF')", + "pdfApiPattern": "/api/v1/approvals/*/pdf", + "expectedContentType": "application/pdf" + }, + { + "id": "vendor-ledger", + "name": "거래처원장 PDF", + "menuNavigation": { + "level1": "자재관리", + "level2": "거래처원장" + }, + "accessMethod": "detailPage", + "triggerAction": "clickFirstRow", + "pdfButtonSelector": "button:has-text('PDF 다운로드')", + "pdfApiPattern": "/api/v1/vendor-ledger/*/export-pdf", + "expectedContentType": "application/pdf" + } + ], + + "steps": [ + { + "id": "step-0", + "name": "PDF 샘플 폴더 준비", + "description": "PDF 파일 저장 폴더 확인", + "setup": { + "createFolders": [ + "react/tests/e2e/results/hotfix/pdf-samples/", + "react/tests/e2e/results/hotfix/screenshots/" + ] + } + }, + + { + "id": "pdf-test-1", + "name": "기안함 PDF 다운로드 테스트", + "target": "draft-box", + "steps": [ + { + "id": "1-1", + "action": "navigate", + "menu": { "level1": "결재관리", "level2": "기안함" }, + "expect": { "url": "/approval/drafts" } + }, + { + "id": "1-2", + "action": "waitForTable", + "timeout": 5000, + "expect": { "rowCount": ">= 1" } + }, + { + "id": "1-3", + "action": "clickFirstRow", + "description": "첫 번째 문서 클릭하여 상세 모달 열기" + }, + { + "id": "1-4", + "action": "waitForModal", + "timeout": 3000, + "expect": { "modalVisible": true } + }, + { + "id": "1-5", + "action": "screenshot", + "name": "draft-box_pdf-preview_{timestamp}", + "selector": "[role='dialog'], .modal", + "savePath": "react/tests/e2e/results/hotfix/screenshots/", + "description": "PDF 생성 전 모달 스크린샷" + }, + { + "id": "1-6", + "action": "verifyButtonExists", + "selector": "button:has-text('PDF')", + "expect": { "visible": true } + }, + { + "id": "1-7", + "action": "setupDownloadListener", + "description": "다운로드 이벤트 리스너 설정" + }, + { + "id": "1-8", + "action": "click_if_exists", + "selector": "button:has-text('PDF')", + "description": "PDF 버튼 클릭" + }, + { + "id": "1-9", + "action": "wait", + "duration": 3000, + "description": "PDF 생성 및 다운로드 대기" + }, + { + "id": "1-10", + "action": "verifyDownload", + "checks": { + "fileDownloaded": true, + "fileExtension": ".pdf", + "minFileSize": 1024, + "pdfSignature": "%PDF-" + } + }, + { + "id": "1-11", + "action": "saveDownloadedFile", + "targetPath": "react/tests/e2e/results/hotfix/pdf-samples/", + "fileName": "draft-box_{timestamp}.pdf" + }, + { + "id": "1-12", + "action": "closeModal", + "method": "pressEscape" + } + ], + "onSuccess": { + "log": "draft-box PDF 다운로드 성공", + "recordFile": true + }, + "onFail": { + "screenshot": true, + "continueToNext": true + } + }, + + { + "id": "pdf-test-2", + "name": "결재함 PDF 다운로드 테스트", + "target": "approval-box", + "steps": [ + { + "id": "2-1", + "action": "navigate", + "menu": { "level1": "결재관리", "level2": "결재함" }, + "expect": { "url": "/approval/approval-box" } + }, + { + "id": "2-2", + "action": "waitForTable", + "timeout": 5000, + "expect": { "rowCount": ">= 1" } + }, + { + "id": "2-3", + "action": "clickFirstRow", + "description": "첫 번째 문서 클릭하여 상세 모달 열기" + }, + { + "id": "2-4", + "action": "waitForModal", + "timeout": 3000 + }, + { + "id": "2-5", + "action": "screenshot", + "name": "approval-box_pdf-preview_{timestamp}", + "selector": "[role='dialog'], .modal", + "savePath": "react/tests/e2e/results/hotfix/screenshots/" + }, + { + "id": "2-6", + "action": "click_if_exists", + "selector": "button:has-text('PDF')" + }, + { + "id": "2-7", + "action": "wait", + "duration": 3000 + }, + { + "id": "2-8", + "action": "verifyDownload", + "checks": { + "fileDownloaded": true, + "fileExtension": ".pdf", + "minFileSize": 1024 + } + }, + { + "id": "2-9", + "action": "saveDownloadedFile", + "targetPath": "react/tests/e2e/results/hotfix/pdf-samples/", + "fileName": "approval-box_{timestamp}.pdf" + }, + { + "id": "2-10", + "action": "closeModal" + } + ] + }, + + { + "id": "pdf-test-3", + "name": "참조함 PDF 다운로드 테스트", + "target": "reference-box", + "steps": [ + { + "id": "3-1", + "action": "navigate", + "menu": { "level1": "결재관리", "level2": "참조함" }, + "expect": { "url": "/approval/reference-box" } + }, + { + "id": "3-2", + "action": "waitForTable", + "timeout": 5000, + "expect": { "rowCount": ">= 1" } + }, + { + "id": "3-3", + "action": "clickFirstRow" + }, + { + "id": "3-4", + "action": "waitForModal", + "timeout": 3000 + }, + { + "id": "3-5", + "action": "screenshot", + "name": "reference-box_pdf-preview_{timestamp}", + "selector": "[role='dialog'], .modal", + "savePath": "react/tests/e2e/results/hotfix/screenshots/" + }, + { + "id": "3-6", + "action": "click_if_exists", + "selector": "button:has-text('PDF')" + }, + { + "id": "3-7", + "action": "wait", + "duration": 3000 + }, + { + "id": "3-8", + "action": "verifyDownload", + "checks": { + "fileDownloaded": true, + "fileExtension": ".pdf", + "minFileSize": 1024 + } + }, + { + "id": "3-9", + "action": "saveDownloadedFile", + "targetPath": "react/tests/e2e/results/hotfix/pdf-samples/", + "fileName": "reference-box_{timestamp}.pdf" + }, + { + "id": "3-10", + "action": "closeModal" + } + ] + }, + + { + "id": "pdf-test-4", + "name": "거래처원장 PDF 다운로드 테스트", + "target": "vendor-ledger", + "steps": [ + { + "id": "4-1", + "action": "navigate", + "menu": { "level1": "자재관리", "level2": "거래처원장" }, + "expect": { "url": "/accounting/vendor-ledger" } + }, + { + "id": "4-2", + "action": "waitForTable", + "timeout": 5000, + "expect": { "rowCount": ">= 1" } + }, + { + "id": "4-3", + "action": "clickFirstRow", + "description": "거래처 클릭하여 상세 페이지 이동" + }, + { + "id": "4-4", + "action": "waitForNavigation", + "timeout": 5000, + "expect": { "urlContains": "/accounting/vendor-ledger/" } + }, + { + "id": "4-5", + "action": "screenshot", + "name": "vendor-ledger_pdf-preview_{timestamp}", + "fullPage": true, + "savePath": "react/tests/e2e/results/hotfix/screenshots/" + }, + { + "id": "4-6", + "action": "verifyButtonExists", + "selector": "button:has-text('PDF 다운로드')", + "expect": { "visible": true, "count": ">= 1" } + }, + { + "id": "4-7", + "action": "click_if_exists", + "selector": "button:has-text('PDF 다운로드')", + "index": 0, + "description": "첫 번째 PDF 다운로드 버튼 클릭" + }, + { + "id": "4-8", + "action": "wait", + "duration": 3000 + }, + { + "id": "4-9", + "action": "verifyDownload", + "checks": { + "fileDownloaded": true, + "fileExtension": ".pdf", + "minFileSize": 1024 + } + }, + { + "id": "4-10", + "action": "saveDownloadedFile", + "targetPath": "react/tests/e2e/results/hotfix/pdf-samples/", + "fileName": "vendor-ledger_{timestamp}.pdf" + }, + { + "id": "4-11", + "action": "navigateBack", + "description": "목록으로 돌아가기" + } + ] + } + ], + + "pdfQualityChecklist": { + "description": "다운로드된 PDF 파일 수동 검증 체크리스트", + "items": [ + { "id": "q1", "category": "파일", "item": "PDF 파일이 정상적으로 열리는가?" }, + { "id": "q2", "category": "폰트", "item": "한글 폰트가 깨지지 않고 정상 표시되는가?" }, + { "id": "q3", "category": "테이블", "item": "테이블 경계선이 올바르게 표시되는가?" }, + { "id": "q4", "category": "정렬", "item": "숫자/금액이 우측 정렬되어 있는가?" }, + { "id": "q5", "category": "레이아웃", "item": "여백(margin/padding)이 적절한가?" }, + { "id": "q6", "category": "페이지", "item": "헤더/푸터가 각 페이지에 올바르게 표시되는가?" }, + { "id": "q7", "category": "이미지", "item": "로고/이미지가 정상 표시되는가?" }, + { "id": "q8", "category": "페이지나눔", "item": "페이지 나눔이 적절한 위치에서 발생하는가?" }, + { "id": "q9", "category": "색상", "item": "배경색/강조색이 올바르게 적용되었는가?" }, + { "id": "q10", "category": "오버플로우", "item": "텍스트가 잘리거나 겹치지 않는가?" } + ] + }, + + "outputPaths": { + "pdfSamples": "react/tests/e2e/results/hotfix/pdf-samples/", + "screenshots": "react/tests/e2e/results/hotfix/screenshots/", + "report": "react/tests/e2e/results/hotfix/" + }, + + "reportTemplate": { + "successFile": "OK-pdf-download-test_{timestamp}.md", + "failFile": "Fail-pdf-download-test_{timestamp}.md", + "sections": [ + "테스트 요약", + "개별 PDF 테스트 결과", + "다운로드된 PDF 파일 목록", + "품질 체크리스트 (수동 확인 필요)" + ] + }, + + "executionNotes": [ + "이 시나리오는 'PDF 다운로드 전체 검사해줘' 명령으로 실행", + "각 PDF 버튼 클릭 후 3초 대기 (PDF 생성 시간)", + "다운로드된 PDF는 pdf-samples 폴더에 타임스탬프와 함께 저장", + "테스트 완료 후 PDF 파일을 열어 품질 체크리스트 수동 확인 필요" + ] +} diff --git a/_backup_before_enhance/production-dashboard.json b/_backup_before_enhance/production-dashboard.json new file mode 100644 index 0000000..767d10a --- /dev/null +++ b/_backup_before_enhance/production-dashboard.json @@ -0,0 +1,71 @@ +{ + "id": "production-dashboard", + "name": "생산 현황판 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "생산관리 > 생산 현황판 대시보드 기능 검증", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "생산관리", + "level2": "생산 현황판", + "expectedUrl": "/production/dashboard", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 생산관리 > 생산 현황판", + "action": "menu_navigate", + "level1": "생산관리", + "level2": "생산 현황판", + "expected": { "url_contains": "/production" } + }, + { + "id": 2, + "name": "목업 감지", + "action": "verify_not_mockup" + }, + { + "id": 3, + "name": "생산 현황판 페이지 확인", + "action": "verify_detail", + "checks": ["visible_text:생산"] + }, + { + "id": 4, + "name": "현황 데이터 확인", + "action": "verify_detail", + "checks": ["visible_text:현황"] + }, + { + "id": 5, + "name": "필터 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('필터'), select, [class*='filter'] button, [class*='date-picker']" + }, + { + "id": 6, + "name": "대기", + "action": "wait", + "duration": 1000 + }, + { + "id": 7, + "name": "모달 닫기", + "action": "close_modal_if_open" + }, + { + "id": 8, + "name": "생산 현황판 최종 확인", + "action": "verify_detail", + "checks": ["visible_text:생산"] + } + ] +} diff --git a/_backup_before_enhance/production-item.json b/_backup_before_enhance/production-item.json new file mode 100644 index 0000000..e89c08b --- /dev/null +++ b/_backup_before_enhance/production-item.json @@ -0,0 +1,83 @@ +{ + "id": "production-item", + "name": "생산품목관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "품목관리 > 품목기준관리 목록/검색/상세 기능 검증", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "품목관리", + "level2": "품목기준관리", + "expectedUrl": "/master-data/item-master-data-management", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 품목관리 > 품목기준관리", + "action": "menu_navigate", + "level1": "품목관리", + "level2": "품목기준관리", + "expected": { "url_contains": "/master-data" } + }, + { + "id": 2, + "name": "목업 감지", + "action": "verify_not_mockup" + }, + { + "id": 3, + "name": "품목기준관리 페이지 확인", + "action": "verify_detail", + "checks": ["visible_text:품목"] + }, + { + "id": 4, + "name": "테이블 또는 목록 확인", + "action": "verify_detail", + "checks": ["visible_text:관리"] + }, + { + "id": 5, + "name": "검색 입력 시도", + "action": "click_if_exists", + "target": "input[type='search'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input" + }, + { + "id": 6, + "name": "대기", + "action": "wait", + "duration": 1000 + }, + { + "id": 7, + "name": "행 클릭 시도", + "action": "click_if_exists", + "target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']" + }, + { + "id": 8, + "name": "상세 확인", + "action": "verify_detail", + "checks": ["visible_text:품목"] + }, + { + "id": 9, + "name": "모달 닫기", + "action": "close_modal_if_open" + }, + { + "id": 10, + "name": "최종 확인", + "action": "verify_detail", + "checks": ["visible_text:품목"] + } + ] +} diff --git a/_backup_before_enhance/production-work-order.json b/_backup_before_enhance/production-work-order.json new file mode 100644 index 0000000..bc9e21a --- /dev/null +++ b/_backup_before_enhance/production-work-order.json @@ -0,0 +1,350 @@ +{ + "enabled": true, + "id": "production-work-order", + "name": "작업지시 관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] + }, + "description": "생산관리 > 작업지시 관리 메뉴의 작업지시 조회/등록/수정/삭제 전체 CRUD 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "생산관리", + "level2": "작업지시 관리", + "expectedUrl": "/production/work-orders", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "testData": { + "create": { + "orderNumber": "E2E_TEST_작업지시", + "itemName": "테스트품목", + "quantity": "500", + "dueDate": "2026-02-10", + "memo": "E2E 자동화 테스트 작업지시" + }, + "update": { + "quantity": "600", + "memo": "E2E 수정된 작업지시 메모" + } + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 생산관리 > 작업지시 관리", + "action": "menu_navigate", + "level1": "생산관리", + "level2": "작업지시 관리", + "expected": { + "url_contains": "/production/work-orders", + "visible": [ + "작업지시" + ] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "작업지시 목록 표시", + "작업지시 등록 버튼 존재", + "검색/필터 기능 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "작업지시 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "작업지시번호 컬럼", + "품목 컬럼", + "수량 컬럼", + "납기일 컬럼", + "상태 컬럼" + ], + "expected": "작업지시 테이블 컬럼 정상 표시" + }, + { + "id": 4, + "name": "검색 기능 테스트", + "action": "click_if_exists", + "target": "input[placeholder*='검색']", + "value": "테스트", + "expected": { + "data_filtered": true + } + }, + { + "id": 5, + "phase": "CREATE", + "name": "[CREATE] 작업지시 등록 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('등록'), button:has-text('작업지시 등록'), button:has-text('추가')", + "expected": { + "modal": true, + "modalTitle": "작업지시 등록" + } + }, + { + "id": 6, + "phase": "CREATE", + "name": "[CREATE] 작업지시 정보 입력", + "action": "click_if_exists", + "fields": [ + { + "name": "작업지시번호", + "type": "text", + "value": "E2E_TEST_작업지시_{timestamp}" + }, + { + "name": "품목", + "type": "select", + "value": "테스트품목" + }, + { + "name": "수량", + "type": "number", + "value": "500" + }, + { + "name": "납기일", + "type": "date", + "value": "2026-02-10" + }, + { + "name": "메모", + "type": "text", + "value": "E2E 자동화 테스트 작업지시" + } + ], + "note": "타임스탬프로 고유성 보장", + "target": "form, [role='dialog'], .modal" + }, + { + "id": 7, + "phase": "CREATE", + "name": "[CREATE] 필수 검증 #2: 등록 저장", + "action": "click_if_exists", + "target": "button:has-text('저장'), button:has-text('등록')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "POST /api/v1/work-orders", + "toast": "등록|완료|성공" + }, + "expected": "작업지시 등록 완료" + }, + { + "id": "7-modal-close", + "phase": "CREATE", + "name": "[CREATE] 모달 닫기 확인", + "action": "close_modal_if_open", + "expected": "모달 닫힘" + }, + { + "id": 8, + "phase": "CREATE", + "name": "[CREATE] 등록 결과 확인", + "action": "verify_detail", + "search": "E2E_TEST_작업지시", + "expected": { + "row_exists": true, + "contains": [ + "E2E", + "500" + ] + } + }, + { + "id": 9, + "phase": "READ", + "name": "[READ] 작업지시 상세 페이지 진입", + "action": "click_if_exists", + "target": "table tbody tr:has-text('E2E')", + "expected": { + "url_contains": "/production/work-orders/", + "visible": [ + "작업지시 상세", + "수정", + "삭제" + ] + } + }, + { + "id": 10, + "phase": "READ", + "name": "[READ] 상세 정보 확인", + "action": "verify_detail", + "checks": [ + "작업지시번호: E2E_TEST_작업지시", + "수량: 500", + "납기일: 2026-02-10" + ], + "expected": "입력한 데이터와 일치" + }, + { + "id": 11, + "phase": "UPDATE", + "name": "[UPDATE] 수정 모드 진입", + "action": "click_if_exists", + "target": "button:has-text('수정')", + "expected": { + "url_contains": "mode=edit", + "fields_editable": true + } + }, + { + "id": 12, + "phase": "UPDATE", + "name": "[UPDATE] 수량 수정", + "action": "click_if_exists", + "target": "input[name*='quantity'], input[placeholder*='수량']", + "value": "600", + "clear": true + }, + { + "id": 13, + "phase": "UPDATE", + "name": "[UPDATE] 메모 수정", + "action": "click_if_exists", + "target": "textarea[name*='memo'], input[placeholder*='메모']", + "value": "E2E 수정된 작업지시 메모_{timestamp}", + "clear": true + }, + { + "id": 14, + "phase": "UPDATE", + "name": "[UPDATE] 필수 검증 #2: 수정 저장", + "action": "click_if_exists", + "target": "button:has-text('저장')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "PUT /api/v1/work-orders/", + "toast": "수정|완료|성공" + }, + "expected": "수정 완료" + }, + { + "id": 15, + "phase": "UPDATE", + "name": "[UPDATE] 수정 결과 확인", + "action": "verify_detail", + "checks": [ + "수량: 600", + "메모: E2E 수정된 작업지시" + ], + "expected": "수정된 데이터 반영" + }, + { + "id": 16, + "phase": "DELETE", + "name": "[DELETE] 삭제 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('삭제')", + "expected": { + "confirm_dialog": true, + "dialog_message": "삭제|정말" + } + }, + { + "id": 17, + "phase": "DELETE", + "name": "[DELETE] 필수 검증 #6: 삭제 확인", + "action": "click_if_exists", + "target": "button:has-text('확인'), button:has-text('삭제')", + "verify": { + "api_call": "DELETE /api/v1/work-orders/", + "toast": "삭제|완료|성공", + "redirect": "/production/work-orders" + }, + "expected": "삭제 완료 및 목록 복귀" + }, + { + "id": 18, + "phase": "DELETE", + "name": "[DELETE] 삭제 결과 확인", + "action": "verify_detail", + "search": "E2E 수정된 작업지시", + "expected": { + "row_exists": false, + "message": "테스트 작업지시가 목록에서 제거됨" + } + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/work-orders", + "description": "작업지시 목록 조회" + }, + { + "method": "POST", + "endpoint": "/api/v1/work-orders", + "description": "작업지시 등록" + }, + { + "method": "GET", + "endpoint": "/api/v1/work-orders/{id}", + "description": "작업지시 상세 조회" + }, + { + "method": "PUT", + "endpoint": "/api/v1/work-orders/{id}", + "description": "작업지시 수정" + }, + { + "method": "DELETE", + "endpoint": "/api/v1/work-orders/{id}", + "description": "작업지시 삭제" + } + ], + "requiredVerifications": [ + { + "id": 2, + "name": "등록/저장 버튼", + "steps": [ + 7, + 14 + ], + "criteria": "API 호출 + 성공 토스트 + 데이터 반영" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [ + 2 + ], + "criteria": "작업지시 목록, 등록 버튼, 필터 존재" + }, + { + "id": 6, + "name": "삭제 기능", + "steps": [ + 16, + 17, + 18 + ], + "criteria": "DELETE API + 목록에서 제거" + } + ], + "rollbackPlan": { + "onCreateFail": "모달 닫기", + "onUpdateFail": "테스트 작업지시 수동 삭제 필요", + "onDeleteFail": "테스트 작업지시 수동 삭제 필요", + "cleanupRequired": "E2E_TEST_ 접두사 작업지시는 테스트 데이터" + } +} \ No newline at end of file diff --git a/_backup_before_enhance/production-work-result.json b/_backup_before_enhance/production-work-result.json new file mode 100644 index 0000000..e967e80 --- /dev/null +++ b/_backup_before_enhance/production-work-result.json @@ -0,0 +1,257 @@ +{ + "enabled": true, + "id": "production-work-result", + "name": "작업실적 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "생산관리 > 작업실적 메뉴의 작업 실적 CRUD 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "생산관리", + "level2": "작업실적", + "expectedUrl": "/production/work-results", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "testData": { + "create": { + "description": "E2E_TEST_실적_{timestamp}", + "quantity": "100", + "defectQty": "5" + } + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 생산관리 > 작업실적", + "action": "menu_navigate", + "level1": "생산관리", + "level2": "작업실적", + "expected": { + "url_contains": "/production/work", + "visible": ["작업실적"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "작업실적 목록 표시", + "실적 등록 버튼 존재", + "기간 필터 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "작업실적 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "작업일 컬럼", + "품목 컬럼", + "지시 수량 컬럼", + "실적 수량 컬럼", + "작업자 컬럼" + ], + "expected": "작업실적 테이블 표시" + }, + { + "id": 4, + "phase": "FILTER", + "name": "[FILTER] 기간 필터 - 시작일", + "action": "click_if_exists", + "target": "input[type='date']:first-of-type, input[name*='start']", + "value": "2025-01-01" + }, + { + "id": 5, + "phase": "FILTER", + "name": "[FILTER] 기간 필터 - 종료일", + "action": "click_if_exists", + "target": "input[type='date']:last-of-type, input[name*='end']", + "value": "2025-12-31" + }, + { + "id": 6, + "phase": "FILTER", + "name": "[FILTER] 조회 실행", + "action": "click_if_exists", + "target": "button:has-text('조회'), button:has-text('검색')", + "expected": { + "api_call": "GET /api/v1/production/work-results" + } + }, + { + "id": 7, + "phase": "CREATE", + "name": "[CREATE] 실적 등록 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('등록'), button:has-text('추가'), button:has-text('신규')", + "expected": { + "modal_open": true + } + }, + { + "id": 8, + "phase": "CREATE", + "name": "[CREATE] 작업 지시 선택", + "action": "click_if_exists", + "target": "select[name*='order'], button:has-text('작업지시'), input[placeholder*='작업지시']", + "expected": "작업 지시 선택 가능" + }, + { + "id": 9, + "phase": "CREATE", + "name": "[CREATE] 생산 수량 입력", + "action": "click_if_exists", + "target": "input[name*='quantity'], input[name*='qty'], input[placeholder*='수량']", + "value": "100", + "clear": true + }, + { + "id": 10, + "phase": "CREATE", + "name": "[CREATE] 불량 수량 입력", + "action": "click_if_exists", + "target": "input[name*='defect'], input[placeholder*='불량']", + "value": "5", + "clear": true + }, + { + "id": 11, + "phase": "CREATE", + "name": "[CREATE] 필수 검증 #2: 실적 저장", + "action": "click_if_exists", + "target": "button:has-text('저장'), button:has-text('등록'), button:has-text('확인')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "POST /api/v1/production/work-results", + "toast": "등록|저장|완료|성공" + }, + "expected": "실적 등록 완료" + }, + { + "id": 12, + "phase": "READ", + "name": "[READ] 등록된 실적 확인", + "action": "verify_detail", + "checks": [ + "등록한 실적 목록에 표시" + ], + "expected": "등록된 실적 확인" + }, + { + "id": 13, + "phase": "READ", + "name": "[READ] 실적 상세 조회", + "action": "click_if_exists", + "target": "table tbody tr:first-child", + "expected": { + "detail_view": true + } + }, + { + "id": 14, + "name": "실적 상세 정보 확인", + "action": "verify_detail", + "checks": [ + "작업일시", + "품목 정보", + "생산 수량", + "불량 수량", + "작업자 정보" + ], + "expected": "실적 상세 정보 표시" + }, + { + "id": 15, + "phase": "UPDATE", + "name": "[UPDATE] 실적 수정", + "action": "click_if_exists", + "target": "button:has-text('수정'), button:has-text('편집')", + "expected": { + "edit_mode": true + } + }, + { + "id": 16, + "phase": "UPDATE", + "name": "[UPDATE] 수량 수정", + "action": "click_if_exists", + "target": "input[name*='quantity'], input[name*='qty']", + "value": "95", + "clear": true + }, + { + "id": 17, + "phase": "UPDATE", + "name": "[UPDATE] 수정 저장", + "action": "click_if_exists", + "target": "button:has-text('저장'), button:has-text('확인')", + "verify": { + "api_call": "PUT /api/v1/production/work-results", + "toast": "수정|저장|완료|성공" + }, + "expected": "실적 수정 완료" + }, + { + "id": 18, + "name": "엑셀 다운로드 확인", + "action": "click_if_exists", + "target": "button:has-text('엑셀'), button:has-text('Excel'), button:has-text('다운로드')", + "verify": { + "file_download": true + }, + "expected": "엑셀 파일 다운로드" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/production/work-results", + "description": "작업실적 목록 조회" + }, + { + "method": "POST", + "endpoint": "/api/v1/production/work-results", + "description": "작업실적 등록" + }, + { + "method": "PUT", + "endpoint": "/api/v1/production/work-results/:id", + "description": "작업실적 수정" + }, + { + "method": "GET", + "endpoint": "/api/v1/production/work-results/export", + "description": "작업실적 엑셀 다운로드" + } + ], + "requiredVerifications": [ + { + "id": 2, + "name": "저장 버튼", + "steps": [11, 17], + "criteria": "API 호출 + 성공 토스트 + 데이터 반영" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "작업실적 목록, 등록 버튼, 기간 필터 존재" + } + ], + "rollbackPlan": { + "onCreateFail": "등록 모달 닫고 재시도", + "onUpdateFail": "페이지 새로고침 후 재시도", + "note": "작업실적은 작업지시와 연관되어 있어 주의 필요" + } +} diff --git a/_backup_before_enhance/production-worker.json b/_backup_before_enhance/production-worker.json new file mode 100644 index 0000000..4469dd7 --- /dev/null +++ b/_backup_before_enhance/production-worker.json @@ -0,0 +1,83 @@ +{ + "id": "production-worker", + "name": "작업자 화면 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "생산관리 > 작업자 화면 목록/상세 기능 검증", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "생산관리", + "level2": "작업자 화면", + "expectedUrl": "/production/worker-screen", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 생산관리 > 작업자 화면", + "action": "menu_navigate", + "level1": "생산관리", + "level2": "작업자 화면", + "expected": { "url_contains": "/production" } + }, + { + "id": 2, + "name": "목업 감지", + "action": "verify_not_mockup" + }, + { + "id": 3, + "name": "작업자 화면 페이지 확인", + "action": "verify_detail", + "checks": ["visible_text:작업"] + }, + { + "id": 4, + "name": "UI 요소 확인", + "action": "verify_detail", + "checks": ["visible_text:관리"] + }, + { + "id": 5, + "name": "검색 입력 시도", + "action": "click_if_exists", + "target": "input[type='search'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input" + }, + { + "id": 6, + "name": "대기", + "action": "wait", + "duration": 1000 + }, + { + "id": 7, + "name": "행 클릭 시도", + "action": "click_if_exists", + "target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']" + }, + { + "id": 8, + "name": "상세 확인", + "action": "verify_detail", + "checks": ["visible_text:작업"] + }, + { + "id": 9, + "name": "모달 닫기", + "action": "close_modal_if_open" + }, + { + "id": 10, + "name": "최종 확인", + "action": "verify_detail", + "checks": ["visible_text:작업"] + } + ] +} diff --git a/_backup_before_enhance/quality-certification.json b/_backup_before_enhance/quality-certification.json new file mode 100644 index 0000000..3181366 --- /dev/null +++ b/_backup_before_enhance/quality-certification.json @@ -0,0 +1,83 @@ +{ + "id": "quality-certification", + "name": "품질인정심사 시스템 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "품질관리 > 품질인정심사 시스템 목록/상세 기능 검증", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "품질관리", + "level2": "품질인정심사 시스템", + "expectedUrl": "/quality/qms", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 품질관리 > 품질인정심사 시스템", + "action": "menu_navigate", + "level1": "품질관리", + "level2": "품질인정심사 시스템", + "expected": { "url_contains": "/quality" } + }, + { + "id": 2, + "name": "목업 감지", + "action": "verify_not_mockup" + }, + { + "id": 3, + "name": "품질인정심사 페이지 확인", + "action": "verify_detail", + "checks": ["visible_text:품질"] + }, + { + "id": 4, + "name": "UI 요소 확인", + "action": "verify_detail", + "checks": ["visible_text:인정"] + }, + { + "id": 5, + "name": "검색 입력 시도", + "action": "click_if_exists", + "target": "input[type='search'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input" + }, + { + "id": 6, + "name": "대기", + "action": "wait", + "duration": 1000 + }, + { + "id": 7, + "name": "행 클릭 시도", + "action": "click_if_exists", + "target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']" + }, + { + "id": 8, + "name": "상세 확인", + "action": "verify_detail", + "checks": ["visible_text:품질"] + }, + { + "id": 9, + "name": "모달 닫기", + "action": "close_modal_if_open" + }, + { + "id": 10, + "name": "최종 확인", + "action": "verify_detail", + "checks": ["visible_text:품질"] + } + ] +} diff --git a/_backup_before_enhance/quality-inspection.json b/_backup_before_enhance/quality-inspection.json new file mode 100644 index 0000000..ec8d42b --- /dev/null +++ b/_backup_before_enhance/quality-inspection.json @@ -0,0 +1,359 @@ +{ + "enabled": true, + "id": "quality-inspection", + "name": "제품검사관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] + }, + "description": "품질관리 > 제품검사관리 메뉴의 제품검사 조회/등록/수정/삭제 전체 CRUD 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "품질관리", + "level2": "제품검사관리", + "expectedUrl": "/quality/inspections", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "testData": { + "create": { + "siteName": "E2E_TEST_현장", + "orderCompany": "E2E_TEST_수주처", + "location": "테스트구역A", + "inspector": "홍길동", + "memo": "E2E 자동화 테스트 제품검사" + }, + "update": { + "location": "테스트구역B", + "memo": "E2E 수정된 제품검사 메모" + } + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 품질관리 > 제품검사관리", + "action": "menu_navigate", + "level1": "품질관리", + "level2": "제품검사관리", + "expected": { + "url_contains": "/quality/inspections", + "visible": [ + "제품검사", + "검사" + ] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "제품검사 목록 표시", + "제품검사 등록 버튼 존재", + "검색/필터 기능 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "제품검사 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "품질관리서 번호 컬럼", + "현장명 컬럼", + "수주처 컬럼", + "검사기간 컬럼", + "상태 컬럼", + "검사자 컬럼" + ], + "expected": "제품검사 테이블 컬럼 정상 표시" + }, + { + "id": 4, + "name": "검색 기능 테스트", + "action": "click_if_exists", + "target": "input[placeholder*='검색']", + "value": "테스트", + "expected": { + "data_filtered": true + } + }, + { + "id": 5, + "phase": "CREATE", + "name": "[CREATE] 제품검사 등록 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('등록'), button:has-text('제품검사 등록'), button:has-text('추가')", + "expected": { + "modal_or_page": true, + "title": "제품검사 등록" + } + }, + { + "id": 6, + "phase": "CREATE", + "name": "[CREATE] 제품검사 정보 입력", + "action": "fill_form", + "fields": [ + { + "name": "현장명", + "type": "text", + "value": "E2E_TEST_현장_{timestamp}" + }, + { + "name": "수주처", + "type": "select", + "value": "E2E_TEST_수주처" + }, + { + "name": "개소", + "type": "text", + "value": "테스트구역A" + }, + { + "name": "검사자", + "type": "select", + "value": "홍길동" + }, + { + "name": "메모", + "type": "text", + "value": "E2E 자동화 테스트 제품검사_{timestamp}" + } + ], + "note": "타임스탬프로 고유성 보장" + }, + { + "id": 7, + "phase": "CREATE", + "name": "[CREATE] 필수 검증 #2: 등록 저장", + "action": "click_if_exists", + "target": "button:has-text('저장'), button:has-text('등록')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "POST /api/v1/quality/inspections", + "toast": "등록|완료|성공" + }, + "expected": "제품검사 등록 완료" + }, + { + "id": "7-modal-close", + "phase": "CREATE", + "name": "[CREATE] 모달 닫기 확인", + "action": "close_modal_if_open", + "expected": "모달 닫힘" + }, + { + "id": 8, + "phase": "CREATE", + "name": "[CREATE] 등록 결과 확인", + "action": "verify_detail", + "search": "E2E_TEST_현장", + "expected": { + "row_exists": true, + "contains": [ + "E2E_TEST", + "접수" + ] + } + }, + { + "id": 9, + "phase": "READ", + "name": "[READ] 제품검사 상세 페이지 진입", + "action": "click_if_exists", + "target": "table tbody tr:has-text('E2E_TEST')", + "expected": { + "url_contains": "/quality/inspections/", + "visible": [ + "품질관리서", + "수정", + "삭제" + ] + } + }, + { + "id": 10, + "phase": "READ", + "name": "[READ] 상세 정보 확인", + "action": "verify_detail", + "checks": [ + "현장명: E2E_TEST_현장", + "개소: 테스트구역A", + "검사자: 홍길동" + ], + "expected": "입력한 데이터와 일치" + }, + { + "id": 11, + "phase": "UPDATE", + "name": "[UPDATE] 수정 모드 진입", + "action": "click_if_exists", + "target": "button:has-text('수정')", + "expected": { + "url_contains": "mode=edit", + "fields_editable": true + } + }, + { + "id": 12, + "phase": "UPDATE", + "name": "[UPDATE] 개소 수정", + "action": "click_if_exists", + "target": "input[name*='location'], input[placeholder*='개소']", + "value": "테스트구역B", + "clear": true + }, + { + "id": 13, + "phase": "UPDATE", + "name": "[UPDATE] 메모 수정", + "action": "click_if_exists", + "target": "textarea[name*='memo'], input[placeholder*='메모']", + "value": "E2E 수정된 제품검사 메모_{timestamp}", + "clear": true + }, + { + "id": 14, + "phase": "UPDATE", + "name": "[UPDATE] 필수 검증 #2: 수정 저장", + "action": "click_if_exists", + "target": "button:has-text('저장')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "PUT /api/v1/quality/inspections/", + "toast": "수정|완료|성공" + }, + "expected": "수정 완료" + }, + { + "id": 15, + "phase": "UPDATE", + "name": "[UPDATE] 수정 결과 확인", + "action": "verify_detail", + "checks": [ + "개소: 테스트구역B", + "메모: E2E 수정된" + ], + "expected": "수정된 데이터 반영" + }, + { + "id": 16, + "phase": "DELETE", + "name": "[DELETE] 삭제 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('삭제')", + "expected": { + "confirm_dialog": true, + "dialog_message": "삭제|정말" + } + }, + { + "id": 17, + "phase": "DELETE", + "name": "[DELETE] 필수 검증 #6: 삭제 확인", + "action": "click_if_exists", + "target": "button:has-text('확인'), button:has-text('삭제')", + "verify": { + "api_call": "DELETE /api/v1/quality/inspections/", + "toast": "삭제|완료|성공", + "redirect": "/quality/inspections" + }, + "expected": "삭제 완료 및 목록 복귀" + }, + { + "id": 18, + "phase": "DELETE", + "name": "[DELETE] 삭제 결과 확인", + "action": "verify_detail", + "search": "E2E 수정된 제품검사", + "expected": { + "row_exists": false, + "message": "테스트 제품검사가 목록에서 제거됨" + } + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/quality/inspections", + "description": "제품검사 목록 조회" + }, + { + "method": "POST", + "endpoint": "/api/v1/quality/inspections", + "description": "제품검사 등록" + }, + { + "method": "GET", + "endpoint": "/api/v1/quality/inspections/{id}", + "description": "제품검사 상세 조회" + }, + { + "method": "PUT", + "endpoint": "/api/v1/quality/inspections/{id}", + "description": "제품검사 수정" + }, + { + "method": "DELETE", + "endpoint": "/api/v1/quality/inspections/{id}", + "description": "제품검사 삭제" + } + ], + "requiredVerifications": [ + { + "id": 2, + "name": "등록/저장 버튼", + "steps": [ + 7, + 14 + ], + "criteria": "API 호출 + 성공 토스트 + 데이터 반영" + }, + { + "id": 3, + "name": "검색/필터", + "steps": [ + 4 + ], + "criteria": "검색 기능 동작" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [ + 2 + ], + "criteria": "제품검사 목록, 등록 버튼, 필터 존재" + }, + { + "id": 6, + "name": "삭제 기능", + "steps": [ + 16, + 17, + 18 + ], + "criteria": "DELETE API + 목록에서 제거" + } + ], + "rollbackPlan": { + "onCreateFail": "모달 닫기", + "onUpdateFail": "테스트 제품검사 수동 삭제 필요", + "onDeleteFail": "테스트 제품검사 수동 삭제 필요", + "cleanupRequired": "E2E_TEST_ 접두사 제품검사는 테스트 데이터" + } +} \ No newline at end of file diff --git a/_backup_before_enhance/receiving-management.json b/_backup_before_enhance/receiving-management.json new file mode 100644 index 0000000..3c69f16 --- /dev/null +++ b/_backup_before_enhance/receiving-management.json @@ -0,0 +1,197 @@ +{ + "enabled": true, + "id": "receiving-management", + "name": "입고관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "자재관리 > 입고관리 페이지의 입고 조회 및 상태별 필터링 기능을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/material/receiving-management", + "navigation": { + "targetUrl": "/material/receiving-management", + "urlPattern": "/material/receiving-management|/ko/material/receiving-management", + "menuHints": ["입고관리", "입고 관리", "자재관리"] + }, + "menuNavigation": { + "level1": "자재관리", + "level2": "입고관리", + "expectedUrl": "/material/receiving-management", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebar": { + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 5, + "waitAfterScroll": 300 + }, + "level1": { + "text": "자재관리", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "입고관리", + "waitAfterClick": 300 + }, + "fallbackUrl": "/material/receiving-management", + "expectedUrl": "/material/receiving-management" + }, + "timeout": 90000, + "tags": ["material", "receiving", "read-only"], + + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 메뉴 전체 펼치기", + "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 300 }, + { + "type": "evaluate", + "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" + }, + { "type": "wait", "duration": 2000 } + ] + }, + { + "id": "step-1", + "name": "자재관리 메뉴 진입", + "description": "자재관리 > 입고관리 메뉴로 이동", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "자재관리", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click_if_exists", "target": "자재관리" }, + { "type": "wait", "duration": 500 }, + { "type": "click_if_exists", "target": "입고관리" } + ], + "expect": { + "url": "/material/receiving-management", + "visible": ["입고 목록"] + }, + "fallback": { + "type": "navigate", + "url": "/material/receiving-management" + } + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "통계 카드와 테이블 구조 확인", + "verify": { + "visible": ["입고대기", "배송중", "검사대기", "금일입고"], + "tableColumns": ["번호", "발주번호", "품목코드", "품목명", "공급업체", "발주수량", "입고수량", "LOT번호", "상태"] + } + }, + { + "id": "step-3", + "name": "필수 검증 #3: 상태 탭 필터 - 입고대기", + "description": "입고대기 탭 클릭하여 필터링 확인", + "actions": [ + { "type": "click_if_exists", "target": "입고대기", "role": "tab" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "tabActive": "입고대기", + "dataFiltered": true + } + }, + { + "id": "step-4", + "name": "필수 검증 #3: 상태 탭 필터 - 입고완료", + "description": "입고완료 탭 클릭하여 필터링 확인", + "actions": [ + { "type": "click_if_exists", "target": "입고완료", "role": "tab" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "tabActive": "입고완료", + "dataFiltered": true + } + }, + { + "id": "step-5", + "name": "전체 탭으로 복귀", + "description": "전체 탭 클릭하여 모든 입고 표시", + "actions": [ + { "type": "click_if_exists", "target": "전체", "role": "tab" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "tabActive": "전체", + "allDataShown": true + } + }, + { + "id": "step-6", + "name": "빈 상태 확인", + "description": "데이터가 없을 때 빈 상태 메시지 확인", + "verify": { + "emptyStateVisible": "검색 결과가 없습니다" + } + }, + { + "id": "step-7", + "name": "통계 카드 값 확인", + "description": "입고대기/배송중/검사대기/금일입고 카운트 표시 확인", + "verify": { + "statsCards": ["입고대기", "배송중", "검사대기", "금일입고"], + "countsDisplayed": true + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/material/receiving-management", + "message": "입고관리 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "text=입고 목록", + "message": "입고 목록 제목이 표시되어야 함" + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 3, + "name": "검색/필터", + "trigger": "상태 탭 필터", + "verification": "데이터 변화 확인", + "failCondition": "필터 적용 후 데이터 무변화" + } + ] + }, + + "notes": { + "testScope": "입고 목록 조회 및 상태별 필터링 테스트", + "pageType": "조회 전용 (발주 기반 입고 처리)", + "statsCards": ["입고대기", "배송중", "검사대기", "금일입고"], + "statusTabs": ["전체", "입고대기", "입고완료"], + "tableColumns": ["번호", "발주번호", "품목코드", "품목명", "공급업체", "발주수량", "입고수량", "LOT번호", "상태"], + "workflow": "발주 → 배송중 → 검사대기 → 입고완료", + "prerequisites": "로그인된 사용자, 발주 데이터 존재 시 입고 가능" + } +} diff --git a/_backup_before_enhance/reference-box.json b/_backup_before_enhance/reference-box.json new file mode 100644 index 0000000..8caa532 --- /dev/null +++ b/_backup_before_enhance/reference-box.json @@ -0,0 +1,839 @@ +{ + "id": "reference-box", + "name": "참조함 E2E 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] + }, + "description": "참조함 페이지의 모든 기능 검증 (탭 전환, 검색, 필터, 정렬, 열람/미열람 처리, 문서 상세)", + "baseUrl": "https://dev.codebridge-x.com", + "navigation": { + "targetUrl": "/approval/reference", + "urlPattern": "/approval/reference|/ko/approval/reference", + "menuHints": [ + "참조함", + "참조 함", + "결재관리" + ] + }, + "menuNavigation": { + "level1": "결재관리", + "level2": "참조함", + "expectedUrl": "/ko/approval/reference", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", + "level1": "결재관리", + "level2": "참조함", + "alternativeLevel1Names": [ + "결재관리", + "결재 관리", + "Approval", + "전자결재" + ], + "alternativeLevel2Names": [ + "참조함", + "참조 함", + "Reference", + "참조문서", + "CC문서" + ], + "scrollConfig": { + "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", + "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + } + }, + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/approvals/reference", + "description": "참조함 목록 조회", + "queryParams": [ + "page", + "per_page", + "search", + "is_read", + "approval_type", + "sort_by", + "sort_dir" + ] + }, + { + "method": "POST", + "endpoint": "/api/v1/approvals/{id}/read", + "description": "열람 처리" + }, + { + "method": "POST", + "endpoint": "/api/v1/approvals/{id}/unread", + "description": "미열람 처리" + } + ], + "steps": [ + { + "id": 0, + "name": "사이드바 메뉴 전체 펼치기", + "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { + "type": "wait", + "duration": 300 + }, + { + "type": "evaluate", + "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" + }, + { + "type": "wait", + "duration": 2000 + } + ] + }, + { + "id": 1, + "name": "2단계 메뉴 진입: 결재관리 > 참조함", + "description": "사이드바를 스크롤하며 결재관리 > 참조함 메뉴를 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "결재관리", + "alternativeTexts": [ + "결재관리", + "결재 관리", + "Approval", + "전자결재" + ], + "scrollContainer": "sidebar", + "maxAttempts": 10, + "description": "스크롤하며 결재관리 메뉴 찾기" + }, + { + "type": "click_if_exists", + "target": "결재관리", + "description": "결재관리 메뉴 클릭" + }, + { + "type": "wait", + "duration": 500, + "description": "서브메뉴 펼쳐지기 대기" + }, + { + "type": "scrollAndFind", + "target": "참조함", + "alternativeTexts": [ + "참조함", + "참조 함", + "Reference", + "참조문서" + ], + "scrollContainer": "submenu", + "maxAttempts": 5, + "description": "서브메뉴에서 참조함 찾기" + }, + { + "type": "click_if_exists", + "target": "참조함", + "description": "참조함 메뉴 클릭" + }, + { + "type": "wait", + "target": "페이지 로드 완료", + "timeout": 10000 + } + ], + "verification": [ + "페이지 URL이 /approval/reference인지 확인", + "페이지 제목 '참조함' 표시 확인", + "설명 텍스트 '참조로 지정된 문서를 확인합니다.' 표시 확인", + "통계 카드 3개 표시 (전체, 열람, 미열람)", + "날짜 범위 선택기 표시 확인", + "검색창 표시 확인 (placeholder: '제목, 기안자, 부서 검색...')", + "탭 버튼 3개 표시 (전체, 열람, 미열람)", + "필터 드롭다운 표시 (전체, 지출결의서, 품의서, 지출예상내역서)", + "정렬 드롭다운 표시 (최신순, 오래된순, 기안일 오름차순, 기안일 내림차순)", + "테이블 헤더 확인 (번호, 문서번호, 문서유형, 제목, 기안자, 기안일시, 상태)" + ] + }, + { + "id": 2, + "name": "데이터 로딩 대기", + "action": "wait", + "duration": 3000, + "verification": [ + "테이블에 데이터 행이 표시되는지 확인", + "통계 카드에 숫자가 표시되는지 확인 (N건)", + "'검색 결과가 없습니다' 메시지가 없는지 확인", + "각 문서의 열람 상태 배지 표시 확인 (열람/미열람)" + ] + }, + { + "id": 3, + "name": "통계 카드 데이터 확인", + "action": "verify_element", + "target": "[class*='card'], [class*='stat']", + "verification": [ + "전체 건수 = 열람 건수 + 미열람 건수", + "각 카드의 아이콘 표시 확인 (Files, Eye, EyeOff)", + "스크린샷 촬영하여 초기 상태 저장" + ] + }, + { + "id": 4, + "name": "탭 전환 - 열람 탭", + "action": "click_if_exists", + "target": "button:has-text('열람')", + "verification": [ + "탭 활성화 상태 변경 확인", + "테이블에 '열람' 상태 문서만 표시", + "모든 행의 상태 배지가 '열람'인지 확인", + "표시된 문서 개수가 통계 카드의 '열람' 건수와 일치" + ] + }, + { + "id": 5, + "name": "탭 전환 - 미열람 탭", + "action": "click_if_exists", + "target": "button:has-text('미열람')", + "verification": [ + "탭 활성화 상태 변경 확인", + "테이블에 '미열람' 상태 문서만 표시", + "모든 행의 상태 배지가 '미열람'인지 확인", + "표시된 문서 개수가 통계 카드의 '미열람' 건수와 일치" + ] + }, + { + "id": 6, + "name": "탭 전환 - 전체 탭으로 복귀", + "action": "click_if_exists", + "target": "button:has-text('전체')", + "verification": [ + "탭 활성화 상태 변경 확인", + "테이블에 모든 문서 표시 (열람 + 미열람)", + "표시된 문서 개수가 통계 카드의 '전체' 건수와 일치" + ] + }, + { + "id": 7, + "name": "⚠️ 필수 검증: 검색 기능 - 기안자 검색", + "actions": [ + { + "type": "capture", + "variable": "beforeSearchCount", + "selector": "table tbody tr", + "extract": "count", + "description": "검색 전 문서 수 저장" + }, + { + "type": "click_if_exists", + "target": "input[type='search'], input[placeholder*='검색']", + "description": "검색창 존재 확인" + }, + { + "type": "wait", + "duration": 1000, + "description": "검색 결과 로딩 대기" + }, + { + "type": "capture", + "variable": "afterSearchCount", + "selector": "table tbody tr", + "extract": "count", + "description": "검색 후 문서 수 저장" + } + ], + "verify": { + "searchApplied": true, + "tableContains": "김철수", + "dataChanged": "beforeSearchCount may differ from afterSearchCount" + }, + "verification": [ + "검색창에 입력한 텍스트 표시 확인", + "Enter 키 입력 또는 자동 검색 실행", + "검색어를 포함한 문서만 필터링되어 표시", + "검색 결과 건수 확인" + ] + }, + { + "id": "7-1", + "name": "검색 결과 데이터 검증", + "description": "검색 결과의 모든 행이 검색어를 포함하는지 확인", + "verify": { + "allRowsContain": "김철수", + "columnToCheck": "기안자" + } + }, + { + "id": 8, + "name": "검색 초기화", + "actions": [ + { + "type": "click_if_exists", + "target": "input[type='search'], input[placeholder*='검색']", + "description": "검색창 존재 확인" + }, + { + "type": "wait", + "duration": 500 + }, + { + "type": "capture", + "variable": "afterClearCount", + "selector": "table tbody tr", + "extract": "count" + } + ], + "verify": { + "dataRestored": "afterClearCount should equal beforeSearchCount" + }, + "verification": [ + "전체 문서 목록 복원 확인", + "원래 문서 개수로 복원" + ] + }, + { + "id": 9, + "name": "필터 기능 - 문서유형 선택", + "action": "select", + "target": "select, [role='combobox']", + "value": "품의서", + "verification": [ + "필터 드롭다운 값이 '품의서'로 변경", + "테이블에 '품의서' 유형 문서만 표시", + "모든 행의 문서유형 배지가 '품의서'인지 확인" + ] + }, + { + "id": 10, + "name": "필터 초기화", + "action": "select", + "target": "select, [role='combobox']", + "value": "전체", + "verification": [ + "전체 문서 목록 복원 확인" + ] + }, + { + "id": 11, + "name": "정렬 기능 - 오래된순", + "action": "click_if_exists", + "target": "select, [role='combobox']", + "verification": [ + "정렬 드롭다운 값이 '오래된순'으로 변경", + "테이블 데이터가 오래된 날짜부터 표시", + "기안일시 컬럼 확인하여 오름차순 정렬 검증" + ] + }, + { + "id": 12, + "name": "정렬 초기화", + "action": "click_if_exists", + "target": "select, [role='combobox']", + "verification": [ + "테이블 데이터가 최신 날짜부터 표시" + ] + }, + { + "id": 13, + "name": "체크박스 - 단일 선택", + "action": "click_if_exists", + "target": "table tbody tr:first-child input[type='checkbox']", + "verification": [ + "체크박스 선택 상태 확인", + "'열람' 버튼 표시 확인", + "'미열람' 버튼 표시 확인", + "조건부 버튼이 헤더 영역에 표시됨" + ] + }, + { + "id": 14, + "name": "체크박스 - 선택 해제", + "action": "click_if_exists", + "target": "table tbody tr:first-child input[type='checkbox']", + "verification": [ + "체크박스 선택 해제 확인", + "'열람' 및 '미열람' 버튼 사라짐 확인" + ] + }, + { + "id": 15, + "name": "체크박스 - 다중 선택", + "action": "click_if_exists", + "target": "table tbody tr input[type='checkbox']", + "verification": [ + "2개 문서 선택 확인", + "'열람' 및 '미열람' 버튼 표시 확인" + ] + }, + { + "id": 16, + "name": "문서 상세 모달 - 열기", + "action": "click_if_exists", + "target": "table tbody tr:first-child td:nth-child(2)", + "verification": [ + "문서 상세 모달 열림 확인", + "⚠️ 필수 검증 #5: 목업 페이지 감지", + "모달 제목 확인 (문서유형 + ' 상세')", + "문서번호 표시 확인", + "작성일자 표시 확인", + "기안자 정보 표시 확인", + "결재선 정보 표시 확인", + "PDF/인쇄 버튼 존재 확인" + ] + }, + { + "id": "16-pdf-1", + "name": "⚠️ 필수 검증: PDF 다운로드 전 모달 스크린샷", + "description": "PDF 생성 전 모달 상태를 스크린샷으로 캡처하여 CSS 문제 감지용 기준 이미지 확보", + "prerequisite": "step-16의 문서 상세 모달이 열려있는 상태에서 실행", + "actions": [ + { + "type": "screenshot", + "name": "pdf-preview-before-download-reference-box", + "fullPage": false, + "selector": "[role='dialog'], .modal, [data-state='open']", + "savePath": "tests/e2e/results/hotfix/screenshots/", + "description": "PDF 생성 대상 모달 전체 캡처" + } + ], + "verify": { + "screenshotCaptured": true, + "purpose": "PDF CSS 문제 감지를 위한 기준 이미지" + } + }, + { + "id": "16-pdf-2", + "name": "⚠️ 필수 검증: PDF 다운로드 실행 및 파일 보관", + "description": "PDF 다운로드 후 파일을 지정 폴더에 보관하여 수동 검증 가능하게 함", + "actions": [ + { + "type": "verify", + "target": "PDF 버튼 존재", + "selector": "button:has-text('PDF'), [aria-label*='PDF']", + "description": "PDF 다운로드 버튼 존재 확인" + }, + { + "type": "expectResponse", + "id": "pdf-download-response-reference-box", + "urlPattern": "/api/v1/approvals/*/pdf", + "description": "PDF 다운로드 API 응답 대기 설정" + }, + { + "type": "click_if_exists", + "target": "PDF 버튼", + "selector": "button:has-text('PDF')", + "description": "PDF 다운로드 버튼 클릭" + }, + { + "type": "wait", + "duration": 3000, + "description": "PDF 생성 및 다운로드 대기" + }, + { + "type": "assertResponse", + "id": "pdf-download-response-reference-box", + "checks": { + "status": 200, + "contentType": "application/pdf" + } + }, + { + "type": "saveDownloadedFile", + "targetPath": "tests/e2e/results/hotfix/pdf-samples/", + "fileNamePattern": "reference-box-{timestamp}.pdf", + "description": "다운로드된 PDF 파일을 지정 폴더에 보관" + } + ], + "verify": { + "apiSuccess": true, + "fileDownloaded": true, + "fileSaved": "tests/e2e/results/hotfix/pdf-samples/" + } + }, + { + "id": "16-pdf-3", + "name": "⚠️ PDF 파일 유효성 검증", + "description": "다운로드된 PDF 파일의 기본 유효성 검사", + "actions": [ + { + "type": "verifyDownloadedFile", + "checks": { + "fileExists": true, + "fileSize": "> 1024", + "pdfSignature": "%PDF-", + "description": "PDF 파일 헤더 검증" + } + } + ], + "verify": { + "pdfValid": true, + "minFileSize": "1KB 이상" + } + }, + { + "id": "16-pdf-4", + "name": "📋 PDF 스타일 수동 확인 체크리스트", + "type": "manualVerification", + "description": "개발자가 다운로드된 PDF를 열어 시각적으로 확인해야 하는 항목", + "manualChecklist": [ + { + "id": "css-1", + "item": "테이블 경계선이 올바르게 표시되는가?", + "category": "테이블 스타일" + }, + { + "id": "css-2", + "item": "한글 폰트가 깨지지 않고 정상 표시되는가?", + "category": "폰트" + }, + { + "id": "css-3", + "item": "숫자/금액 정렬이 올바른가? (우측 정렬)", + "category": "정렬" + }, + { + "id": "css-4", + "item": "여백(margin/padding)이 적절한가?", + "category": "레이아웃" + }, + { + "id": "css-5", + "item": "헤더/푸터가 각 페이지에 올바르게 표시되는가?", + "category": "페이지 구조" + }, + { + "id": "css-6", + "item": "로고/이미지가 정상 표시되는가?", + "category": "이미지" + }, + { + "id": "css-7", + "item": "페이지 나눔(page break)이 적절한 위치에서 발생하는가?", + "category": "페이지 나눔" + }, + { + "id": "css-8", + "item": "배경색/강조색이 올바르게 적용되었는가?", + "category": "색상" + }, + { + "id": "css-9", + "item": "텍스트가 잘리거나 겹치지 않는가?", + "category": "오버플로우" + }, + { + "id": "css-10", + "item": "결재선 정보가 정상적으로 표시되는가?", + "category": "결재선" + } + ], + "outputFiles": { + "screenshot": "tests/e2e/results/hotfix/screenshots/pdf-preview-before-download-reference-box-*.png", + "pdfFile": "tests/e2e/results/hotfix/pdf-samples/reference-box-*.pdf" + }, + "reportFlag": { + "requiresManualReview": true, + "message": "⚠️ PDF 스타일 수동 확인 필요 - 위 체크리스트 항목을 PDF 파일에서 직접 확인하세요" + } + }, + { + "id": 17, + "name": "문서 상세 모달 - 닫기", + "action": "close_modal", + "verification": [ + "모달 닫힘 확인", + "원래 참조함 페이지로 복귀", + "페이지 URL 유지 (/approval/reference)" + ] + }, + { + "id": 18, + "name": "미열람 탭으로 이동", + "action": "click_if_exists", + "target": "button:has-text('미열람')", + "verification": [ + "미열람 문서만 표시 확인", + "모든 문서의 상태가 '미열람'" + ] + }, + { + "id": 19, + "name": "열람 처리 - 문서 선택", + "action": "click_if_exists", + "target": "table tbody tr:first-child input[type='checkbox']", + "verification": [ + "체크박스 선택 확인", + "'열람' 버튼 표시 확인" + ] + }, + { + "id": 20, + "name": "열람 처리 - 확인 다이얼로그", + "action": "click_if_exists", + "target": "button:has-text('열람')", + "verification": [ + "확인 다이얼로그 표시", + "다이얼로그 제목: '열람 처리'", + "다이얼로그 메시지: '정말 1건을 열람 처리하시겠습니까?'", + "취소 버튼 표시", + "확인 버튼 표시" + ] + }, + { + "id": 21, + "name": "열람 처리 - URL 안정성 검증 (⚠️ 필수 검증 #2)", + "action": "save_url", + "verification": [ + "⚠️ URL 변경 여부 확인 (변경되면 안됨)", + "⚠️ 에러 페이지 텍스트 검색 ('페이지를 찾을 수 없습니다', '404', 'Not Found' 등)", + "⚠️ 원래 페이지 요소 존재 확인 (테이블, 탭 등)", + "성공 토스트 메시지 표시: '열람 처리 완료'", + "토스트 설명: '열람 처리가 완료되었습니다.'", + "다이얼로그 자동 닫힘", + "체크박스 선택 해제", + "미열람 탭의 문서 개수 1개 감소" + ], + "criticalCheck": { + "type": "URL_STABILITY", + "beforeURL": "/approval/reference", + "afterURL": "/approval/reference", + "errorPageTexts": [ + "페이지를 찾을 수 없습니다", + "404", + "Not Found", + "서버 에러", + "500" + ], + "successToast": "열람 처리 완료" + } + }, + { + "id": 22, + "name": "열람 처리 후 데이터 검증", + "action": "verify_detail", + "verification": [ + "미열람 탭에서 처리된 문서 제거 확인", + "통계 카드의 '미열람' 건수 1개 감소 (실시간 업데이트)", + "통계 카드의 '열람' 건수 1개 증가 (실시간 업데이트)" + ] + }, + { + "id": 23, + "name": "열람 탭으로 이동하여 검증", + "action": "click_if_exists", + "target": "button:has-text('열람')", + "verification": [ + "열람 탭에 방금 처리한 문서 표시 확인", + "해당 문서의 상태 배지가 '열람'으로 변경" + ] + }, + { + "id": 24, + "name": "미열람 처리 - 문서 선택", + "action": "click_if_exists", + "target": "table tbody tr:first-child input[type='checkbox']", + "verification": [ + "체크박스 선택 확인", + "'미열람' 버튼 표시 확인" + ] + }, + { + "id": 25, + "name": "미열람 처리 - 확인 다이얼로그", + "action": "click_if_exists", + "target": "button:has-text('미열람')", + "verification": [ + "확인 다이얼로그 표시", + "다이얼로그 제목: '미열람 처리'", + "다이얼로그 메시지: '정말 1건을 미열람 처리하시겠습니까?'", + "취소 버튼 표시", + "확인 버튼 표시" + ] + }, + { + "id": 26, + "name": "미열람 처리 - URL 안정성 검증 (⚠️ 필수 검증 #2)", + "action": "save_url", + "verification": [ + "⚠️ URL 변경 여부 확인 (변경되면 안됨)", + "⚠️ 에러 페이지 텍스트 검색", + "성공 토스트 메시지 표시: '미열람 처리 완료'", + "토스트 설명: '미열람 처리가 완료되었습니다.'", + "다이얼로그 자동 닫힘", + "체크박스 선택 해제", + "열람 탭의 문서 개수 1개 감소" + ], + "criticalCheck": { + "type": "URL_STABILITY", + "beforeURL": "/approval/reference", + "afterURL": "/approval/reference", + "errorPageTexts": [ + "페이지를 찾을 수 없습니다", + "404", + "Not Found", + "서버 에러", + "500" + ], + "successToast": "미열람 처리 완료" + } + }, + { + "id": 27, + "name": "미열람 처리 후 데이터 검증", + "action": "verify_detail", + "verification": [ + "열람 탭에서 처리된 문서 제거 확인", + "통계 카드의 '열람' 건수 1개 감소 (실시간 업데이트)", + "통계 카드의 '미열람' 건수 1개 증가 (실시간 업데이트)" + ] + }, + { + "id": 28, + "name": "일괄 열람 처리 - 다중 선택", + "action": "click_if_exists", + "target": "button:has-text('미열람')", + "verification": [ + "2개 문서 선택 확인", + "'열람' 버튼 표시 확인" + ] + }, + { + "id": 29, + "name": "일괄 열람 처리 - 실행", + "action": "click_if_exists", + "target": "button:has-text('열람')", + "verification": [ + "다이얼로그 메시지: '정말 2건을 열람 처리하시겠습니까?'", + "확인 버튼 클릭", + "⚠️ URL 안정성 확인", + "성공 토스트 메시지 표시", + "미열람 탭에서 2개 문서 제거", + "통계 카드 업데이트 (미열람 -2, 열람 +2)" + ] + }, + { + "id": 30, + "name": "날짜 범위 선택기 테스트", + "action": "click_if_exists", + "target": "button:has-text('당월')", + "verification": [ + "시작일과 종료일이 당월로 변경", + "데이터 재로드 확인 (로딩 인디케이터 또는 데이터 변화)" + ] + }, + { + "id": 31, + "name": "페이지네이션 테스트", + "action": "click_if_exists", + "target": "button:has-text('2')", + "verification": [ + "페이지네이션 컨트롤 표시 확인", + "2페이지 버튼 클릭", + "페이지 번호 변경 확인", + "새로운 데이터 로드 확인" + ], + "conditional": "문서 개수가 20개 이상인 경우에만 실행" + }, + { + "id": 32, + "name": "Console 로그 확인", + "action": "verify_element", + "target": "body", + "verification": [ + "ERROR 로그 없는지 확인", + "WARNING 로그 확인 (접근성 경고 등)", + "Network 요청 확인 (GET /api/v1/approvals/reference)" + ] + }, + { + "id": 33, + "name": "최종 통계 확인", + "action": "click_if_exists", + "target": "button:has-text('전체')", + "verification": [ + "전체 문서 목록 표시", + "통계 카드 수치 정확성 확인", + "전체 = 열람 + 미열람 수식 성립", + "스크린샷 촬영하여 최종 상태 저장" + ] + } + ], + "mandatoryVerifications": [ + { + "id": "VERIFICATION_2", + "name": "등록/저장 동작 검증 (URL 안정성)", + "appliesTo": [ + "Step 21", + "Step 26", + "Step 29" + ], + "requirements": [ + "URL 변경 여부 확인 (처리 전 URL과 처리 후 URL 비교)", + "에러 페이지 텍스트 검색 ('페이지를 찾을 수 없습니다', '404', 'Not Found', '서버 에러', '500')", + "원래 페이지 요소 존재 확인 (테이블, 탭, 통계 카드 등)", + "성공 토스트 메시지 표시 확인", + "다이얼로그 자동 닫힘 확인" + ] + }, + { + "id": "VERIFICATION_5", + "name": "목업/미완성 페이지 감지", + "appliesTo": [ + "Step 1", + "Step 16" + ], + "requirements": [ + "입력 필드 존재 여부 확인 (검색창 등)", + "동작하는 버튼 확인 (최소 2개 버튼 클릭 테스트)", + "API 호출 확인 (Console LOG만 아닌 실제 Network Request)", + "데이터 변경 가능 여부 확인 (열람/미열람 처리 등)", + "목업 판정 기준: 2개 이상 항목 해당 시 목업으로 판정" + ] + } + ], + "bugReportTemplate": { + "priority": "Critical/High/Medium/Low", + "component": "ReferenceBox", + "affectedArea": "react", + "relatedFiles": [ + "C:\\Users\\codeb\\react\\src\\components\\approval\\ReferenceBox\\index.tsx", + "C:\\Users\\codeb\\react\\src\\components\\approval\\ReferenceBox\\actions.ts", + "C:\\Users\\codeb\\react\\src\\components\\approval\\ReferenceBox\\types.ts" + ], + "changeApprovalPolicy": "✅ 즉시 가능 / ⚠️ 컨펌 필요 / 🔴 금지" + }, + "testData": { + "searchKeyword": "김철수", + "dateRange": { + "startDate": "2025-01-01", + "endDate": "2026-01-31" + } + }, + "notes": [ + "참조함은 열람/미열람 상태 관리가 핵심 기능입니다.", + "결재함과 달리 승인/반려 기능은 없고, 열람/미열람 처리만 가능합니다.", + "통계 카드의 실시간 업데이트 확인이 중요합니다.", + "URL 안정성 검증(필수 검증 #2)을 모든 처리 동작에서 수행해야 합니다.", + "문서 상세 모달은 읽기 전용(mode='reference')으로 표시됩니다." + ] +} diff --git a/_backup_before_enhance/sales-client.json b/_backup_before_enhance/sales-client.json new file mode 100644 index 0000000..b5784a5 --- /dev/null +++ b/_backup_before_enhance/sales-client.json @@ -0,0 +1,274 @@ +{ + "enabled": true, + "id": "sales-client", + "name": "판매거래처관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] + }, + "description": "판매관리 > 거래처관리 메뉴의 판매 거래처 CRUD 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "판매관리", + "level2": "거래처관리", + "expectedUrl": "/sales/client-management-sales-admin", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "testData": { + "create": { + "clientName": "E2E_TEST_판매처_{timestamp}", + "businessNumber": "987-65-43210", + "representative": "테스트 대표" + } + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 판매관리 > 거래처관리", + "action": "menu_navigate", + "level1": "판매관리", + "level2": "거래처관리", + "expected": { + "url_contains": "/sales/client", + "visible": [ + "거래처관리", + "거래처" + ] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "거래처 목록 표시", + "거래처 등록 버튼 존재", + "검색 기능 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "거래처 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "거래처명 컬럼", + "사업자번호 컬럼", + "대표자 컬럼", + "연락처 컬럼" + ], + "expected": "거래처 테이블 표시" + }, + { + "id": 4, + "phase": "CREATE", + "name": "[CREATE] 거래처 등록 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('등록'), button:has-text('추가'), button:has-text('신규')", + "expected": { + "modal_open": true + } + }, + { + "id": 5, + "phase": "CREATE", + "name": "[CREATE] 거래처명 입력", + "action": "fill", + "target": "input[name*='name'], input[placeholder*='거래처명']", + "value": "E2E_TEST_판매처_{timestamp}", + "clear": true + }, + { + "id": 6, + "phase": "CREATE", + "name": "[CREATE] 사업자번호 입력", + "action": "click_if_exists", + "target": "input[name*='business'], input[placeholder*='사업자']", + "value": "987-65-43210", + "clear": true + }, + { + "id": 7, + "phase": "CREATE", + "name": "[CREATE] 대표자명 입력", + "action": "fill", + "target": "input[name*='representative'], input[placeholder*='대표']", + "value": "테스트 대표", + "clear": true + }, + { + "id": 8, + "phase": "CREATE", + "name": "[CREATE] 필수 검증 #2: 거래처 저장", + "action": "click_if_exists", + "target": "button:has-text('저장'), button:has-text('등록'), button:has-text('확인')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "POST /api/v1/sales/clients", + "toast": "등록|저장|완료|성공" + }, + "expected": "거래처 등록 완료" + }, + { + "id": 9, + "phase": "READ", + "name": "[READ] 등록된 거래처 검색", + "action": "click_if_exists", + "target": "input[type='search'], input[placeholder*='검색']", + "value": "E2E_TEST_판매처", + "submit": true + }, + { + "id": 10, + "phase": "READ", + "name": "[READ] 등록된 거래처 확인", + "action": "verify_detail", + "checks": [ + "E2E_TEST_판매처 목록에 표시" + ], + "expected": "등록된 거래처 확인" + }, + { + "id": 11, + "phase": "READ", + "name": "[READ] 거래처 상세 조회", + "action": "click_if_exists", + "target": "table tbody tr:has-text('E2E_TEST')", + "expected": { + "detail_view": true + } + }, + { + "id": 12, + "phase": "UPDATE", + "name": "[UPDATE] 거래처 수정 모드 진입", + "action": "click_if_exists", + "target": "button:has-text('수정'), button:has-text('편집')", + "expected": { + "edit_mode": true + } + }, + { + "id": 13, + "phase": "UPDATE", + "name": "[UPDATE] 대표자명 수정", + "action": "fill", + "target": "input[name*='representative'], input[placeholder*='대표']", + "value": "수정된 대표", + "clear": true + }, + { + "id": 14, + "phase": "UPDATE", + "name": "[UPDATE] 거래처 저장", + "action": "click_if_exists", + "target": "button:has-text('저장'), button:has-text('확인')", + "verify": { + "api_call": "PUT /api/v1/sales/clients", + "toast": "수정|저장|완료|성공" + }, + "expected": "거래처 수정 완료" + }, + { + "id": 15, + "phase": "DELETE", + "name": "[DELETE] 거래처 삭제", + "action": "click_if_exists", + "target": "button:has-text('삭제'), button:has-text('제거')", + "expected": { + "confirm_dialog": true + } + }, + { + "id": 16, + "phase": "DELETE", + "name": "[DELETE] 삭제 확인", + "action": "click_if_exists", + "target": "[role='alertdialog'] button:has-text('확인'), [role='dialog'] button:has-text('삭제')", + "verify": { + "api_call": "DELETE /api/v1/sales/clients", + "toast": "삭제|제거|완료|성공" + }, + "expected": "거래처 삭제 완료" + }, + { + "id": 17, + "phase": "DELETE", + "name": "[DELETE] 삭제 확인", + "action": "verify_detail", + "checks": [ + "E2E_TEST_판매처 목록에서 제거" + ], + "expected": "거래처 삭제 반영" + }, + { + "id": 18, + "name": "엑셀 다운로드 확인", + "action": "verify_elements", + "checks": [ + "엑셀 다운로드 버튼 존재" + ], + "expected": "엑셀 다운로드 기능 표시" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/sales/clients", + "description": "거래처 목록 조회" + }, + { + "method": "POST", + "endpoint": "/api/v1/sales/clients", + "description": "거래처 등록" + }, + { + "method": "PUT", + "endpoint": "/api/v1/sales/clients/:id", + "description": "거래처 수정" + }, + { + "method": "DELETE", + "endpoint": "/api/v1/sales/clients/:id", + "description": "거래처 삭제" + } + ], + "requiredVerifications": [ + { + "id": 2, + "name": "저장 버튼", + "steps": [ + 8, + 14 + ], + "criteria": "API 호출 + 성공 토스트 + 데이터 반영" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [ + 2 + ], + "criteria": "거래처 목록, 등록 버튼, 검색 기능 존재" + } + ], + "rollbackPlan": { + "onCreateFail": "등록 모달 닫고 재시도", + "onUpdateFail": "페이지 새로고침 후 재시도", + "onDeleteFail": "수동 삭제 필요", + "cleanupRequired": "E2E_TEST_판매처_* 패턴 데이터 삭제" + } +} \ No newline at end of file diff --git a/_backup_before_enhance/sales-management.json b/_backup_before_enhance/sales-management.json new file mode 100644 index 0000000..0095315 --- /dev/null +++ b/_backup_before_enhance/sales-management.json @@ -0,0 +1,726 @@ +{ + "enabled": true, + "id": "sales-management", + "name": "매출관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] + }, + "description": "회계관리 > 매출관리 메뉴의 매출등록, 계정과목 저장, 품목 동적 추가, 자동계산 로직 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "회계관리", + "level2": "매출관리", + "expectedUrl": "/ko/accounting/sales", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "navigation": { + "targetUrl": "/accounting/sales", + "urlPattern": "/accounting/sales|/ko/accounting/sales", + "menuHints": [ + "매출관리", + "매출", + "회계관리" + ] + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebarSelector": ".sidebar-scroll, [class*='sidebar'], nav[class*='menu']", + "scrollConfig": { + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + }, + "level1": { + "text": "회계관리", + "selectors": [ + "//span[contains(text(),'회계관리')]", + "//div[contains(@class,'menu')]//span[text()='회계관리']", + "[data-menu='accounting']" + ] + }, + "level2": { + "text": "매출관리", + "selectors": [ + "//a[contains(text(),'매출관리')]", + "//span[contains(text(),'매출관리')]", + "[href*='/accounting/sales']" + ] + }, + "fallbackUrl": "/ko/accounting/sales" + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 0, + "name": "사이드바 메뉴 전체 펼치기", + "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { + "type": "wait", + "duration": 300 + }, + { + "type": "evaluate", + "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" + }, + { + "type": "wait", + "duration": 2000 + } + ], + "expected": "사이드바 전체 메뉴가 펼쳐짐" + }, + { + "id": 1, + "name": "로그인", + "action": "click_if_exists", + "target": "form, [role=\"dialog\"], .modal", + "expected": "로그인 성공 후 메인 페이지 이동" + }, + { + "id": 2, + "name": "2단계 메뉴 진입: 회계관리 > 매출관리", + "description": "스크롤하며 회계관리 메뉴를 찾아 클릭 후 매출관리 진입", + "navigationPattern": "scrollAndFind", + "actions": [ + { + "type": "scrollAndFind", + "description": "사이드바 스크롤하며 회계관리 메뉴 찾기", + "scrollContainer": ".sidebar-scroll, [class*='sidebar']", + "targetText": "회계관리", + "scrollStep": 200, + "maxAttempts": 10 + }, + { + "type": "click_if_exists", + "target": "회계관리", + "selectors": [ + "//span[contains(text(),'회계관리')]", + "//div[contains(@class,'menu')]//span[text()='회계관리']" + ] + }, + { + "type": "wait", + "duration": 500 + }, + { + "type": "scrollAndFind", + "description": "확장된 서브메뉴에서 매출관리 찾기", + "scrollContainer": ".sidebar-scroll, [class*='sidebar']", + "targetText": "매출관리", + "scrollStep": 100, + "maxAttempts": 5 + }, + { + "type": "click_if_exists", + "target": "매출관리", + "selectors": [ + "//a[contains(text(),'매출관리')]", + "//span[contains(text(),'매출관리')]", + "[href*='/accounting/sales']" + ] + }, + { + "type": "wait", + "target": "페이지 로드 완료" + } + ], + "fallback": { + "type": "navigate", + "url": "/ko/accounting/sales" + }, + "expected": { + "url": "/ko/accounting/sales", + "pageTitle": "매출관리", + "elements": [ + "매출 등록 버튼", + "테이블" + ] + } + }, + { + "id": 3, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "입력 필드 존재 (검색창, 날짜 선택)", + "동작하는 버튼 존재 (매출 등록, 엑셀 다운로드)", + "테이블 데이터 표시", + "API 호출 확인" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 4, + "name": "목록 페이지 - 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "체크박스 컬럼", + "No. 컬럼", + "매출번호 컬럼", + "거래처명 컬럼", + "매출일 컬럼", + "매출유형 컬럼", + "공급가액 컬럼", + "부가세 컬럼", + "합계금액 컬럼", + "계산서 컬럼", + "명세서 컬럼" + ], + "expected": "테이블 컬럼 구조 정상 표시" + }, + { + "id": 5, + "name": "계정과목명 드롭박스 확인", + "action": "verify_elements", + "checks": [ + "계정과목명 라벨 존재", + "드롭박스(Select) 존재", + "저장 버튼 존재" + ], + "expected": "계정과목 선택 UI 정상 표시" + }, + { + "id": 6, + "name": "계정과목명 드롭박스 옵션 확인", + "action": "click_if_exists", + "target": "accountSubject", + "checks": [ + "미설정 옵션", + "제품매출 옵션", + "상품매출 옵션", + "기타매출 옵션" + ], + "expected": "계정과목 옵션 목록 표시" + }, + { + "id": 7, + "name": "체크박스 선택 (계정과목 저장용)", + "action": "click_if_exists", + "target": "first_row", + "expected": "첫 번째 행 체크박스 선택됨" + }, + { + "id": 8, + "name": "계정과목 변경 - 제품매출 선택", + "action": "click_if_exists", + "target": "accountSubject", + "value": "product", + "expected": "계정과목이 '제품매출'로 변경됨" + }, + { + "id": 9, + "name": "필수 검증 #2: 계정과목 저장 버튼 클릭", + "action": "click_if_exists", + "target": "저장", + "checks": [ + "확인 다이얼로그 표시", + "선택된 항목 수 표시", + "계정과목명 표시" + ], + "expected": "저장 확인 다이얼로그 표시" + }, + { + "id": 10, + "name": "저장 확인 다이얼로그 - 확인 클릭", + "action": "click_if_exists", + "checks": [ + "API 호출 확인 (PUT /api/v1/sales/batch-update-account)", + "성공 토스트 메시지", + "URL 유지 확인" + ], + "expected": "계정과목 저장 성공" + }, + { + "id": "10-1", + "name": "⚠️ 필수 검증: 계정과목명 변경 데이터 반영 확인", + "action": "verify_data_update", + "target": "first_row", + "checks": [ + "선택한 행의 매출유형 컬럼 값 확인", + "변경 전 값과 비교 (예: 기타 매출 → 제품 매출)", + "페이지 새로고침 후에도 변경된 값 유지 확인", + "API 응답과 UI 표시값 일치 확인" + ], + "expected": "선택한 행의 매출유형이 '제품매출'로 실제 변경되어야 함", + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!" + }, + { + "id": 11, + "name": "매출 등록 버튼 클릭", + "action": "click_if_exists", + "target": "매출 등록", + "expected": "매출 등록 페이지로 이동 (/ko/accounting/sales?mode=new)" + }, + { + "id": 12, + "name": "매출 등록 페이지 - URL 확인", + "action": "verify_url", + "target": "/ko/accounting/sales?mode=new", + "expected": "매출 등록 페이지 URL 정상" + }, + { + "id": 13, + "name": "매출 등록 페이지 - 기본정보 섹션 확인", + "action": "verify_elements", + "checks": [ + "매출번호 필드 (자동생성, readonly)", + "매출일 필드 (DatePicker)", + "거래처명 드롭박스", + "매출유형 드롭박스" + ], + "expected": "기본정보 섹션 정상 표시" + }, + { + "id": 14, + "name": "매출번호 자동생성 확인", + "action": "verify_field", + "target": "salesNo", + "checks": [ + "값이 자동 생성됨 (예: S-2026-0001)", + "readonly 상태" + ], + "expected": "매출번호 자동생성 확인" + }, + { + "id": 15, + "name": "거래처명 드롭박스 클릭", + "action": "click_if_exists", + "target": "vendorId", + "expected": "거래처 목록 표시" + }, + { + "id": 16, + "name": "거래처명 선택", + "action": "click_if_exists", + "target": "vendorId", + "value": "first_available", + "expected": "거래처가 선택됨" + }, + { + "id": 17, + "name": "매출유형 드롭박스 확인", + "action": "click_if_exists", + "target": "salesType", + "checks": [ + "외상매출 옵션", + "제품매출 옵션", + "상품매출 옵션", + "부품매출 옵션", + "공사매출 옵션", + "임대매출 옵션", + "기타매출 옵션" + ], + "expected": "매출유형 옵션 목록 표시" + }, + { + "id": 18, + "name": "매출유형 선택 - 제품매출", + "action": "click_if_exists", + "target": "salesType", + "value": "product", + "expected": "매출유형이 '제품매출'로 선택됨" + }, + { + "id": 19, + "name": "품목정보 섹션 확인", + "action": "verify_elements", + "checks": [ + "품목 정보 제목", + "품목 추가 버튼 (+)", + "품목 테이블 헤더 (품목명, 수량, 단가, 공급가액, 부가세, 적요)", + "기본 품목 행 1개 존재" + ], + "expected": "품목정보 섹션 정상 표시" + }, + { + "id": 20, + "name": "품목 동적 추가 - 추가 버튼 클릭", + "action": "click_if_exists", + "target": "품목 추가", + "expected": "새로운 품목 행 추가됨" + }, + { + "id": 21, + "name": "품목 행 개수 확인 (2개)", + "action": "verify_row_count", + "target": "items_table", + "expected": "품목 행이 2개로 증가" + }, + { + "id": 22, + "name": "품목 동적 삭제 - 두 번째 행 삭제", + "action": "click_if_exists", + "target": "remove_item_row_2", + "expected": "두 번째 품목 행 삭제됨" + }, + { + "id": 23, + "name": "품목 행 개수 확인 (1개)", + "action": "verify_row_count", + "target": "items_table", + "expected": "품목 행이 1개로 감소" + }, + { + "id": 24, + "name": "품목명 입력", + "action": "click_if_exists", + "target": "items[0].itemName", + "value": "테스트 품목", + "expected": "품목명 입력됨" + }, + { + "id": 25, + "name": "수량 입력", + "action": "click_if_exists", + "target": "items[0].quantity", + "value": "10", + "expected": "수량 입력됨" + }, + { + "id": 26, + "name": "단가 입력", + "action": "click_if_exists", + "target": "items[0].unitPrice", + "value": "50000", + "expected": "단가 입력됨" + }, + { + "id": 27, + "name": "자동계산 검증 - 공급가액", + "action": "verify_calculated_value", + "target": "items[0].supplyAmount", + "formula": "quantity * unitPrice", + "expectedValue": "500000", + "checks": [ + "수량(10) × 단가(50000) = 공급가액(500,000)", + "자동으로 계산되어 표시됨" + ], + "expected": "공급가액이 500,000원으로 자동 계산됨" + }, + { + "id": 28, + "name": "자동계산 검증 - 부가세", + "action": "verify_calculated_value", + "target": "items[0].vat", + "formula": "supplyAmount * 0.1", + "expectedValue": "50000", + "checks": [ + "공급가액(500,000) × 10% = 부가세(50,000)", + "자동으로 계산되어 표시됨" + ], + "expected": "부가세가 50,000원으로 자동 계산됨" + }, + { + "id": 29, + "name": "적요 입력 (선택사항)", + "action": "click_if_exists", + "target": "items[0].note", + "value": "테스트 적요", + "expected": "적요 입력됨" + }, + { + "id": 30, + "name": "세금계산서 발행 Switch 확인", + "action": "verify_elements", + "target": "taxInvoice_section", + "checks": [ + "세금계산서 발행 라벨", + "Switch 버튼 존재", + "기본값 OFF 상태" + ], + "expected": "세금계산서 발행 Switch 정상 표시" + }, + { + "id": 31, + "name": "세금계산서 발행 Switch ON", + "action": "click_if_exists", + "target": "taxInvoiceSwitch", + "value": "on", + "expected": "세금계산서 발행 Switch가 ON으로 변경됨" + }, + { + "id": 32, + "name": "세금계산서 발행 Switch OFF", + "action": "click_if_exists", + "target": "taxInvoiceSwitch", + "value": "off", + "expected": "세금계산서 발행 Switch가 OFF로 변경됨" + }, + { + "id": 33, + "name": "거래명세서 발행 Switch 확인", + "action": "verify_elements", + "target": "transactionStatement_section", + "checks": [ + "거래명세서 발행 라벨", + "Switch 버튼 존재", + "기본값 OFF 상태" + ], + "expected": "거래명세서 발행 Switch 정상 표시" + }, + { + "id": 34, + "name": "거래명세서 발행 Switch ON", + "action": "click_if_exists", + "target": "transactionStatementSwitch", + "value": "on", + "expected": "거래명세서 발행 Switch가 ON으로 변경됨" + }, + { + "id": 35, + "name": "거래명세서 발행 Switch OFF", + "action": "click_if_exists", + "target": "transactionStatementSwitch", + "value": "off", + "expected": "거래명세서 발행 Switch가 OFF로 변경됨" + }, + { + "id": 36, + "name": "합계 금액 확인", + "action": "verify_totals", + "checks": [ + "총 공급가액: 500,000원", + "총 부가세: 50,000원", + "총 합계금액: 550,000원" + ], + "expected": "합계 금액 정상 표시" + }, + { + "id": 37, + "name": "취소 버튼 동작 테스트", + "action": "click_if_exists", + "target": "취소", + "expected": "취소 확인 다이얼로그 또는 목록 페이지로 이동" + }, + { + "id": 38, + "name": "취소 확인 - 목록 페이지 복귀", + "action": "verify_url", + "target": "/ko/accounting/sales", + "expected": "매출 목록 페이지로 복귀" + }, + { + "id": 39, + "name": "다시 매출 등록 페이지 진입", + "action": "click_if_exists", + "target": "매출 등록", + "expected": "매출 등록 페이지로 이동" + }, + { + "id": 40, + "name": "등록 테스트용 데이터 입력 - 거래처 선택", + "action": "click_if_exists", + "target": "vendorId", + "value": "first_available", + "expected": "거래처 선택됨" + }, + { + "id": 41, + "name": "등록 테스트용 데이터 입력 - 매출유형", + "action": "click_if_exists", + "target": "salesType", + "value": "product", + "expected": "매출유형 선택됨" + }, + { + "id": 42, + "name": "등록 테스트용 데이터 입력 - 품목명", + "action": "click_if_exists", + "target": "items[0].itemName", + "value": "E2E 테스트 품목", + "expected": "품목명 입력됨" + }, + { + "id": 43, + "name": "등록 테스트용 데이터 입력 - 수량", + "action": "click_if_exists", + "target": "items[0].quantity", + "value": "5", + "expected": "수량 입력됨" + }, + { + "id": 44, + "name": "등록 테스트용 데이터 입력 - 단가", + "action": "click_if_exists", + "target": "items[0].unitPrice", + "value": "100000", + "expected": "단가 입력됨" + }, + { + "id": 45, + "name": "필수 검증 #2: 등록 버튼 클릭", + "action": "click_if_exists", + "target": "등록", + "checks": [ + "버튼 클릭 전 URL 저장", + "API 호출 확인 (POST /api/v1/sales)", + "에러 페이지 감지", + "성공 토스트 메시지 확인" + ], + "expected": "매출 등록 완료" + }, + { + "id": 46, + "name": "등록 성공 확인 - 토스트 메시지", + "action": "verify_toast", + "target": "매출이 등록되었습니다", + "expected": "성공 토스트 메시지 표시" + }, + { + "id": 47, + "name": "등록 성공 확인 - 목록 페이지 이동", + "action": "verify_url", + "target": "/ko/accounting/sales", + "expected": "매출 목록 페이지로 이동" + }, + { + "id": 48, + "name": "등록된 매출 목록 확인", + "action": "verify_table_data", + "checks": [ + "신규 등록된 매출이 목록에 표시됨", + "품목명: E2E 테스트 품목", + "공급가액: 500,000원" + ], + "expected": "등록된 매출이 목록에 표시됨" + }, + { + "id": 49, + "name": "거래처 미선택 시 유효성 검증 테스트", + "action": "navigate", + "target": "/ko/accounting/sales?mode=new", + "expected": "매출 등록 페이지 이동" + }, + { + "id": 50, + "name": "거래처 미선택 상태에서 등록 시도", + "action": "click_if_exists", + "target": "등록", + "expected": "유효성 검증 실패 - 경고 메시지" + }, + { + "id": 51, + "name": "유효성 검증 메시지 확인", + "action": "verify_toast", + "target": "거래처를 선택해주세요", + "type": "warning", + "expected": "거래처 선택 요청 경고 메시지 표시" + } + ], + "requiredVerifications": [ + { + "id": 1, + "name": "파일 다운로드 (엑셀)", + "steps": [], + "criteria": "이 시나리오에서는 테스트하지 않음 (별도 시나리오)" + }, + { + "id": 2, + "name": "등록/저장 버튼", + "steps": [ + 9, + 10, + 45, + 46, + 47 + ], + "criteria": "계정과목 저장 + 매출 등록 시 API 호출 + 성공 토스트 + URL 유지/이동 확인" + }, + { + "id": 3, + "name": "검색/필터", + "steps": [], + "criteria": "이 시나리오에서는 테스트하지 않음 (등록 중심 테스트)" + }, + { + "id": 4, + "name": "모달 등록 완료", + "steps": [ + 9, + 10 + ], + "criteria": "계정과목 저장 확인 다이얼로그 → 확인 클릭 → 저장 완료" + }, + { + "id": 6, + "name": "⚠️ 계정과목명 변경 데이터 반영 (필수)", + "steps": [ + "10-1" + ], + "criteria": "저장 후 실제 테이블 데이터가 변경되었는지 확인. 토스트만 확인하면 불충분!", + "priority": "critical", + "knownBug": { + "bugId": "BUG-SALES-20260115-001", + "status": "OPEN", + "description": "성공 토스트 표시되나 실제 데이터 미변경" + } + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [ + 3 + ], + "criteria": "입력 필드, 동작 버튼, API 호출 확인" + } + ], + "testData": { + "vendor": "첫 번째 사용 가능한 거래처", + "salesType": "product", + "item": { + "itemName": "E2E 테스트 품목", + "quantity": 5, + "unitPrice": 100000 + }, + "calculatedValues": { + "supplyAmount": 500000, + "vat": 50000, + "totalAmount": 550000 + } + }, + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/sales", + "description": "매출 목록 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/clients", + "description": "거래처 목록 조회 (드롭박스용)" + }, + { + "method": "POST", + "endpoint": "/api/v1/sales", + "description": "매출 등록" + }, + { + "method": "PUT", + "endpoint": "/api/v1/sales/batch-update-account", + "description": "계정과목 일괄 변경" + } + ], + "skipTests": [ + { + "feature": "삭제 기능", + "reason": "사용자 요청에 따라 삭제 테스트 제외" + } + ] +} \ No newline at end of file diff --git a/_backup_before_enhance/sales-order.json b/_backup_before_enhance/sales-order.json new file mode 100644 index 0000000..e2d0a7c --- /dev/null +++ b/_backup_before_enhance/sales-order.json @@ -0,0 +1,372 @@ +{ + "enabled": true, + "id": "sales-order", + "name": "수주관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] + }, + "description": "판매관리 > 수주관리 메뉴의 수주 조회/등록/수정/삭제 전체 CRUD 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "판매관리", + "level2": "수주관리", + "expectedUrl": "/sales/order-management-sales", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "testData": { + "create": { + "clientName": "E2E_TEST_거래처", + "itemName": "테스트품목", + "quantity": "200", + "unitPrice": "15000", + "deliveryDate": "2026-02-15", + "memo": "E2E 자동화 테스트 수주" + }, + "update": { + "quantity": "250", + "memo": "E2E 수정된 수주 메모" + } + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 판매관리 > 수주관리", + "action": "menu_navigate", + "level1": "판매관리", + "level2": "수주관리", + "expected": { + "url_contains": "/sales/order", + "visible": [ + "수주관리", + "수주" + ] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "수주 목록 표시", + "수주 등록 버튼 존재", + "검색/필터 기능 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "수주 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "수주번호 컬럼", + "거래처 컬럼", + "품목 컬럼", + "수량 컬럼", + "금액 컬럼", + "상태 컬럼" + ], + "expected": "수주 테이블 컬럼 정상 표시" + }, + { + "id": 4, + "name": "검색 기능 테스트", + "action": "click_if_exists", + "target": "input[placeholder*='검색']", + "value": "테스트", + "expected": { + "data_filtered": true + } + }, + { + "id": 5, + "phase": "CREATE", + "name": "[CREATE] 수주 등록 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('등록'), button:has-text('수주 등록'), button:has-text('추가')", + "expected": { + "modal_or_page": true, + "title": "수주 등록" + } + }, + { + "id": 6, + "phase": "CREATE", + "name": "[CREATE] 수주 정보 입력", + "action": "fill_form", + "fields": [ + { + "name": "거래처", + "type": "select", + "value": "E2E_TEST_거래처" + }, + { + "name": "수주일", + "type": "date", + "value": "2026-02-03" + }, + { + "name": "품목", + "type": "select", + "value": "테스트품목" + }, + { + "name": "수량", + "type": "number", + "value": "200" + }, + { + "name": "단가", + "type": "number", + "value": "15000" + }, + { + "name": "납기일", + "type": "date", + "value": "2026-02-15" + }, + { + "name": "메모", + "type": "text", + "value": "E2E 자동화 테스트 수주_{timestamp}" + } + ], + "note": "타임스탬프로 고유성 보장" + }, + { + "id": 7, + "phase": "CREATE", + "name": "[CREATE] 필수 검증 #2: 등록 저장", + "action": "click_if_exists", + "target": "button:has-text('저장'), button:has-text('등록')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "POST /api/v1/sales-orders", + "toast": "등록|완료|성공" + }, + "expected": "수주 등록 완료" + }, + { + "id": "7-modal-close", + "phase": "CREATE", + "name": "[CREATE] 모달 닫기 확인", + "action": "close_modal_if_open", + "expected": "모달 닫힘" + }, + { + "id": 8, + "phase": "CREATE", + "name": "[CREATE] 등록 결과 확인", + "action": "verify_detail", + "search": "E2E 자동화 테스트 수주", + "expected": { + "row_exists": true, + "contains": [ + "E2E", + "200", + "3,000,000" + ] + } + }, + { + "id": 9, + "phase": "READ", + "name": "[READ] 수주 상세 페이지 진입", + "action": "click_if_exists", + "target": "table tbody tr:has-text('E2E')", + "expected": { + "url_contains": "/sales/order", + "visible": [ + "수주 상세", + "수정", + "삭제" + ] + } + }, + { + "id": 10, + "phase": "READ", + "name": "[READ] 상세 정보 확인", + "action": "verify_detail", + "checks": [ + "거래처: E2E_TEST_거래처", + "수량: 200", + "금액: 3,000,000", + "납기일: 2026-02-15" + ], + "expected": "입력한 데이터와 일치" + }, + { + "id": 11, + "phase": "UPDATE", + "name": "[UPDATE] 수정 모드 진입", + "action": "click_if_exists", + "target": "button:has-text('수정')", + "expected": { + "url_contains": "mode=edit", + "fields_editable": true + } + }, + { + "id": 12, + "phase": "UPDATE", + "name": "[UPDATE] 수량 수정", + "action": "click_if_exists", + "target": "input[name*='quantity'], input[placeholder*='수량']", + "value": "250", + "clear": true + }, + { + "id": 13, + "phase": "UPDATE", + "name": "[UPDATE] 메모 수정", + "action": "click_if_exists", + "target": "textarea[name*='memo'], input[placeholder*='메모']", + "value": "E2E 수정된 수주 메모_{timestamp}", + "clear": true + }, + { + "id": 14, + "phase": "UPDATE", + "name": "[UPDATE] 필수 검증 #2: 수정 저장", + "action": "click_if_exists", + "target": "button:has-text('저장')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "PUT /api/v1/sales-orders/", + "toast": "수정|완료|성공" + }, + "expected": "수정 완료" + }, + { + "id": 15, + "phase": "UPDATE", + "name": "[UPDATE] 수정 결과 확인", + "action": "verify_detail", + "checks": [ + "수량: 250", + "금액: 3,750,000" + ], + "expected": "수정된 데이터 반영" + }, + { + "id": 16, + "phase": "DELETE", + "name": "[DELETE] 삭제 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('삭제')", + "expected": { + "confirm_dialog": true, + "dialog_message": "삭제|정말" + } + }, + { + "id": 17, + "phase": "DELETE", + "name": "[DELETE] 필수 검증 #6: 삭제 확인", + "action": "click_if_exists", + "target": "button:has-text('확인'), button:has-text('삭제')", + "verify": { + "api_call": "DELETE /api/v1/sales-orders/", + "toast": "삭제|완료|성공", + "redirect": "/sales/order" + }, + "expected": "삭제 완료 및 목록 복귀" + }, + { + "id": 18, + "phase": "DELETE", + "name": "[DELETE] 삭제 결과 확인", + "action": "verify_detail", + "search": "E2E 수정된 수주", + "expected": { + "row_exists": false, + "message": "테스트 수주가 목록에서 제거됨" + } + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/sales-orders", + "description": "수주 목록 조회" + }, + { + "method": "POST", + "endpoint": "/api/v1/sales-orders", + "description": "수주 등록" + }, + { + "method": "GET", + "endpoint": "/api/v1/sales-orders/{id}", + "description": "수주 상세 조회" + }, + { + "method": "PUT", + "endpoint": "/api/v1/sales-orders/{id}", + "description": "수주 수정" + }, + { + "method": "DELETE", + "endpoint": "/api/v1/sales-orders/{id}", + "description": "수주 삭제" + } + ], + "requiredVerifications": [ + { + "id": 2, + "name": "등록/저장 버튼", + "steps": [ + 7, + 14 + ], + "criteria": "API 호출 + 성공 토스트 + 데이터 반영" + }, + { + "id": 3, + "name": "검색/필터", + "steps": [ + 4 + ], + "criteria": "검색 기능 동작" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [ + 2 + ], + "criteria": "수주 목록, 등록 버튼, 필터 존재" + }, + { + "id": 6, + "name": "삭제 기능", + "steps": [ + 16, + 17, + 18 + ], + "criteria": "DELETE API + 목록에서 제거" + } + ], + "rollbackPlan": { + "onCreateFail": "모달 닫기", + "onUpdateFail": "테스트 수주 수동 삭제 필요", + "onDeleteFail": "테스트 수주 수동 삭제 필요", + "cleanupRequired": "E2E_TEST_ 접두사 수주는 테스트 데이터" + } +} \ No newline at end of file diff --git a/_backup_before_enhance/sales-pricing.json b/_backup_before_enhance/sales-pricing.json new file mode 100644 index 0000000..eb06d9b --- /dev/null +++ b/_backup_before_enhance/sales-pricing.json @@ -0,0 +1,154 @@ +{ + "enabled": true, + "id": "sales-pricing", + "name": "단가관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "판매관리 > 단가관리 메뉴의 단가 목록 조회 및 UI 검증 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "판매관리", + "level2": "단가관리", + "expectedUrl": "/sales/pricing-management", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 판매관리 > 단가관리", + "action": "menu_navigate", + "level1": "판매관리", + "level2": "단가관리", + "expected": { + "url_contains": "/sales/pricing", + "visible": ["단가관리", "단가"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "단가 목록 표시", + "단가 등록 버튼 존재", + "검색 기능 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "단가 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "품목 컬럼", + "거래처 컬럼", + "단가 컬럼", + "적용일 컬럼" + ], + "expected": "단가 테이블 표시" + }, + { + "id": 4, + "name": "단가 UI 요소 확인", + "action": "verify_elements", + "checks": [ + "등록 버튼 존재", + "검색 입력 필드", + "단가 목록" + ], + "expected": "단가 UI 정상 표시" + }, + { + "id": 5, + "phase": "READ", + "name": "[READ] 단가 목록 확인", + "action": "verify_detail", + "checks": [ + "단가 목록 데이터 표시됨" + ], + "expected": "단가 목록 정상" + }, + { + "id": 6, + "phase": "READ", + "name": "[READ] 첫 번째 단가 클릭", + "action": "click_if_exists", + "target": "table tbody tr:first-child" + }, + { + "id": 7, + "phase": "READ", + "name": "[READ] 단가 상세 정보 확인", + "action": "verify_detail", + "checks": [ + "단가 상세 정보 표시" + ], + "expected": "단가 상세 정보 확인" + }, + { + "id": 8, + "name": "상세 모달/페이지 닫기", + "action": "click_if_exists", + "target": "button:has-text('닫기'), button:has-text('목록'), button:has-text('Close')" + }, + { + "id": 9, + "name": "모달 닫기 확인", + "action": "close_modal_if_open", + "expected": "모달 닫힘" + }, + { + "id": 10, + "name": "목록 복귀 확인", + "action": "verify_table", + "checks": [ + "단가 목록 표시" + ], + "expected": "목록 복귀 확인" + }, + { + "id": 11, + "name": "엑셀 다운로드 버튼 확인", + "action": "verify_elements", + "checks": [ + "엑셀 다운로드 버튼 존재 여부" + ], + "expected": "엑셀 다운로드 기능 확인" + }, + { + "id": 12, + "name": "단가관리 페이지 최종 확인", + "action": "verify_elements", + "checks": [ + "단가 목록 구조 정상", + "등록 버튼 존재" + ], + "expected": "단가관리 페이지 정상" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/sales/pricing", + "description": "단가 목록 조회" + } + ], + "requiredVerifications": [ + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "단가 목록, 등록 버튼, 검색 기능 존재" + } + ], + "rollbackPlan": { + "note": "READ-only 패턴으로 안정성 우선" + } +} diff --git a/_backup_before_enhance/sales-quotation.json b/_backup_before_enhance/sales-quotation.json new file mode 100644 index 0000000..2b39076 --- /dev/null +++ b/_backup_before_enhance/sales-quotation.json @@ -0,0 +1,364 @@ +{ + "enabled": true, + "id": "sales-quotation", + "name": "견적관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] + }, + "description": "판매관리 > 견적관리 메뉴의 견적서 조회/등록/수정/삭제 전체 CRUD 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "판매관리", + "level2": "견적관리", + "expectedUrl": "/sales/quote-management", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "testData": { + "create": { + "clientName": "E2E_TEST_거래처", + "itemName": "테스트품목", + "quantity": "100", + "unitPrice": "10000", + "memo": "E2E 자동화 테스트 견적" + }, + "update": { + "quantity": "150", + "memo": "E2E 수정된 견적 메모" + } + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 판매관리 > 견적관리", + "action": "menu_navigate", + "level1": "판매관리", + "level2": "견적관리", + "expected": { + "url_contains": "/sales/quote", + "visible": [ + "견적관리", + "견적" + ] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "견적 목록 표시", + "견적 등록 버튼 존재", + "검색/필터 기능 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "견적 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "견적번호 컬럼", + "거래처 컬럼", + "품목 컬럼", + "금액 컬럼", + "상태 컬럼", + "작성일 컬럼" + ], + "expected": "견적 테이블 컬럼 정상 표시" + }, + { + "id": 4, + "name": "검색 기능 테스트", + "action": "click_if_exists", + "target": "input[placeholder*='검색']", + "value": "테스트", + "expected": { + "data_filtered": true + } + }, + { + "id": 5, + "phase": "CREATE", + "name": "[CREATE] 견적 등록 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('등록'), button:has-text('견적 등록'), button:has-text('추가')", + "expected": { + "modal_or_page": true, + "title": "견적 등록" + } + }, + { + "id": 6, + "phase": "CREATE", + "name": "[CREATE] 견적 정보 입력", + "action": "fill_form", + "fields": [ + { + "name": "거래처", + "type": "select", + "value": "E2E_TEST_거래처" + }, + { + "name": "견적일", + "type": "date", + "value": "2026-02-03" + }, + { + "name": "품목", + "type": "select", + "value": "테스트품목" + }, + { + "name": "수량", + "type": "number", + "value": "100" + }, + { + "name": "단가", + "type": "number", + "value": "10000" + }, + { + "name": "메모", + "type": "text", + "value": "E2E 자동화 테스트 견적_{timestamp}" + } + ], + "note": "타임스탬프로 고유성 보장" + }, + { + "id": 7, + "phase": "CREATE", + "name": "[CREATE] 필수 검증 #2: 등록 저장", + "action": "click_if_exists", + "target": "button:has-text('저장'), button:has-text('등록')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "POST /api/v1/quotations", + "toast": "등록|완료|성공" + }, + "expected": "견적 등록 완료" + }, + { + "id": "7-modal-close", + "phase": "CREATE", + "name": "[CREATE] 모달 닫기 확인", + "action": "close_modal_if_open", + "expected": "모달 닫힘" + }, + { + "id": 8, + "phase": "CREATE", + "name": "[CREATE] 등록 결과 확인", + "action": "verify_detail", + "search": "E2E 자동화 테스트 견적", + "expected": { + "row_exists": true, + "contains": [ + "E2E", + "1,000,000" + ] + } + }, + { + "id": 9, + "phase": "READ", + "name": "[READ] 견적 상세 페이지 진입", + "action": "click_if_exists", + "target": "table tbody tr:has-text('E2E')", + "expected": { + "url_contains": "/sales/quote", + "visible": [ + "견적 상세", + "수정", + "삭제" + ] + } + }, + { + "id": 10, + "phase": "READ", + "name": "[READ] 상세 정보 확인", + "action": "verify_detail", + "checks": [ + "거래처: E2E_TEST_거래처", + "수량: 100", + "금액: 1,000,000" + ], + "expected": "입력한 데이터와 일치" + }, + { + "id": 11, + "phase": "UPDATE", + "name": "[UPDATE] 수정 모드 진입", + "action": "click_if_exists", + "target": "button:has-text('수정')", + "expected": { + "url_contains": "mode=edit", + "fields_editable": true + } + }, + { + "id": 12, + "phase": "UPDATE", + "name": "[UPDATE] 수량 수정", + "action": "click_if_exists", + "target": "input[name*='quantity'], input[placeholder*='수량']", + "value": "150", + "clear": true + }, + { + "id": 13, + "phase": "UPDATE", + "name": "[UPDATE] 메모 수정", + "action": "click_if_exists", + "target": "textarea[name*='memo'], input[placeholder*='메모']", + "value": "E2E 수정된 견적 메모_{timestamp}", + "clear": true + }, + { + "id": 14, + "phase": "UPDATE", + "name": "[UPDATE] 필수 검증 #2: 수정 저장", + "action": "click_if_exists", + "target": "button:has-text('저장')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "PUT /api/v1/quotations/", + "toast": "수정|완료|성공" + }, + "expected": "수정 완료" + }, + { + "id": 15, + "phase": "UPDATE", + "name": "[UPDATE] 수정 결과 확인", + "action": "verify_detail", + "checks": [ + "수량: 150", + "금액: 1,500,000" + ], + "expected": "수정된 데이터 반영" + }, + { + "id": 16, + "phase": "DELETE", + "name": "[DELETE] 삭제 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('삭제')", + "expected": { + "confirm_dialog": true, + "dialog_message": "삭제|정말" + } + }, + { + "id": 17, + "phase": "DELETE", + "name": "[DELETE] 필수 검증 #6: 삭제 확인", + "action": "click_if_exists", + "target": "button:has-text('확인'), button:has-text('삭제')", + "verify": { + "api_call": "DELETE /api/v1/quotations/", + "toast": "삭제|완료|성공", + "redirect": "/sales/quote" + }, + "expected": "삭제 완료 및 목록 복귀" + }, + { + "id": 18, + "phase": "DELETE", + "name": "[DELETE] 삭제 결과 확인", + "action": "verify_detail", + "search": "E2E 수정된 견적", + "expected": { + "row_exists": false, + "message": "테스트 견적이 목록에서 제거됨" + } + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/quotations", + "description": "견적 목록 조회" + }, + { + "method": "POST", + "endpoint": "/api/v1/quotations", + "description": "견적 등록" + }, + { + "method": "GET", + "endpoint": "/api/v1/quotations/{id}", + "description": "견적 상세 조회" + }, + { + "method": "PUT", + "endpoint": "/api/v1/quotations/{id}", + "description": "견적 수정" + }, + { + "method": "DELETE", + "endpoint": "/api/v1/quotations/{id}", + "description": "견적 삭제" + } + ], + "requiredVerifications": [ + { + "id": 2, + "name": "등록/저장 버튼", + "steps": [ + 7, + 14 + ], + "criteria": "API 호출 + 성공 토스트 + 데이터 반영" + }, + { + "id": 3, + "name": "검색/필터", + "steps": [ + 4 + ], + "criteria": "검색 기능 동작" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [ + 2 + ], + "criteria": "견적 목록, 등록 버튼, 필터 존재" + }, + { + "id": 6, + "name": "삭제 기능", + "steps": [ + 16, + 17, + 18 + ], + "criteria": "DELETE API + 목록에서 제거" + } + ], + "rollbackPlan": { + "onCreateFail": "모달 닫기", + "onUpdateFail": "테스트 견적 수동 삭제 필요", + "onDeleteFail": "테스트 견적 수동 삭제 필요", + "cleanupRequired": "E2E_TEST_ 접두사 견적은 테스트 데이터" + } +} \ No newline at end of file diff --git a/_backup_before_enhance/settings-account.json b/_backup_before_enhance/settings-account.json new file mode 100644 index 0000000..3c0a8aa --- /dev/null +++ b/_backup_before_enhance/settings-account.json @@ -0,0 +1,157 @@ +{ + "id": "settings-account", + "name": "계정정보 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "설정 > 계정정보 메뉴의 계정 정보 조회 및 UI 검증 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "설정", + "level2": "계정정보", + "expectedUrl": "/settings/account-info", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 설정 > 계정정보", + "action": "menu_navigate", + "level1": "설정", + "level2": "계정정보", + "expected": { + "url_contains": "/settings/account", + "visible": ["계정정보", "프로필"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "계정 정보 폼 표시", + "프로필 이미지 영역 존재", + "수정 버튼 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "계정 정보 폼 구조 확인", + "action": "verify_elements", + "checks": [ + "사용자명 표시", + "이메일 표시", + "연락처 표시", + "프로필 이미지 영역" + ], + "expected": "계정 정보 폼 정상 표시" + }, + { + "id": 4, + "phase": "READ", + "name": "[READ] 현재 계정 정보 확인", + "action": "verify_detail", + "checks": [ + "사용자명 표시됨", + "이메일 표시됨", + "연락처 표시됨" + ], + "expected": "현재 계정 정보 정상 표시" + }, + { + "id": 5, + "phase": "UPDATE", + "name": "[UPDATE] 프로필 수정 모드 진입", + "action": "click_if_exists", + "target": "button:has-text('수정'), button:has-text('편집')", + "expected": { + "edit_mode": true, + "fields_editable": true + } + }, + { + "id": 6, + "phase": "UPDATE", + "name": "[UPDATE] 표시 이름 필드 확인", + "action": "click_if_exists", + "target": "input[name*='displayName'], input[name*='name'], input[placeholder*='이름']" + }, + { + "id": 7, + "phase": "UPDATE", + "name": "[UPDATE] 연락처 필드 확인", + "action": "click_if_exists", + "target": "input[name*='phone'], input[type='tel']" + }, + { + "id": 8, + "phase": "UPDATE", + "name": "[UPDATE] 필수 검증 #2: 프로필 저장", + "action": "click_if_exists", + "target": "button:has-text('저장'), button:has-text('확인')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "toast": "저장|수정|완료|성공" + }, + "expected": "프로필 저장 완료" + }, + { + "id": 9, + "phase": "UPDATE", + "name": "[UPDATE] 저장 결과 확인", + "action": "verify_detail", + "checks": [ + "프로필 정보 표시됨" + ], + "expected": "프로필 정보 표시" + }, + { + "id": 10, + "name": "비밀번호 변경 버튼 확인", + "action": "verify_elements", + "checks": [ + "비밀번호 변경 버튼 존재" + ], + "expected": "비밀번호 변경 버튼 표시" + }, + { + "id": 11, + "name": "비밀번호 변경 모달 열기", + "action": "click_if_exists", + "target": "button:has-text('비밀번호 변경'), button:has-text('비밀번호')" + }, + { + "id": 12, + "name": "비밀번호 변경 모달 닫기", + "action": "close_modal_if_open", + "expected": "모달 닫힘" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/users/profile", + "description": "프로필 정보 조회" + } + ], + "requiredVerifications": [ + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "계정 정보 폼, 수정 버튼 존재" + } + ], + "rollbackPlan": { + "onUpdateFail": "페이지 새로고침으로 원래 값 복원", + "note": "READ-only 패턴으로 안정성 우선" + } +} diff --git a/_backup_before_enhance/settings-attendance.json b/_backup_before_enhance/settings-attendance.json new file mode 100644 index 0000000..88d253b --- /dev/null +++ b/_backup_before_enhance/settings-attendance.json @@ -0,0 +1,161 @@ +{ + "id": "settings-attendance", + "name": "근태설정 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "설정 > 근태설정 메뉴의 근태 정책 조회 및 UI 검증 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "설정", + "level2": "근태설정", + "expectedUrl": "/settings/attendance-settings", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 설정 > 근태설정", + "action": "menu_navigate", + "level1": "설정", + "level2": "근태설정", + "expected": { + "url_contains": "/settings/attendance", + "visible": ["근태설정", "근태"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "근태 설정 폼 표시", + "출퇴근 시간 설정 가능", + "저장 버튼 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "근태 설정 폼 구조 확인", + "action": "verify_elements", + "checks": [ + "지각 기준 시간 설정", + "조퇴 기준 시간 설정", + "자동 퇴근 처리 설정", + "출퇴근 인정 범위" + ], + "expected": "근태 설정 폼 정상 표시" + }, + { + "id": 4, + "phase": "READ", + "name": "[READ] 현재 근태 설정 확인", + "action": "verify_detail", + "checks": [ + "지각 기준 표시", + "조퇴 기준 표시", + "자동 퇴근 설정 표시" + ], + "expected": "현재 근태 설정 정상 표시" + }, + { + "id": 5, + "phase": "UPDATE", + "name": "[UPDATE] 지각 기준 필드 확인", + "action": "click_if_exists", + "target": "input[name*='late'], input[placeholder*='지각']" + }, + { + "id": 6, + "phase": "UPDATE", + "name": "[UPDATE] 조퇴 기준 필드 확인", + "action": "click_if_exists", + "target": "input[name*='early'], input[placeholder*='조퇴']" + }, + { + "id": 7, + "phase": "UPDATE", + "name": "[UPDATE] 자동 퇴근 시간 필드 확인", + "action": "click_if_exists", + "target": "input[name*='autoCheckout'], input[type='time']" + }, + { + "id": 8, + "phase": "UPDATE", + "name": "[UPDATE] 필수 검증 #2: 근태 설정 저장", + "action": "click_if_exists", + "target": "button:has-text('저장'), button:has-text('적용')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "toast": "저장|적용|완료|성공" + }, + "expected": "근태 설정 저장 완료" + }, + { + "id": 9, + "phase": "UPDATE", + "name": "[UPDATE] 저장 결과 확인", + "action": "verify_detail", + "checks": [ + "근태 설정 정보 표시됨" + ], + "expected": "설정 정보 표시" + }, + { + "id": 10, + "name": "위치 기반 출퇴근 설정 확인", + "action": "verify_elements", + "checks": [ + "GPS 출퇴근 사용 여부", + "출퇴근 가능 위치 설정" + ], + "expected": "위치 기반 설정 표시" + }, + { + "id": 11, + "name": "근태 이상 알림 설정 확인", + "action": "verify_elements", + "checks": [ + "지각 알림 설정", + "무단결근 알림 설정" + ], + "expected": "알림 설정 표시" + }, + { + "id": 12, + "name": "부서별 근태 설정 확인", + "action": "verify_elements", + "checks": [ + "부서별 설정 가능" + ], + "expected": "부서별 설정 표시" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/settings/attendance", + "description": "근태 설정 조회" + } + ], + "requiredVerifications": [ + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "근태 설정 폼, 저장 버튼 존재" + } + ], + "rollbackPlan": { + "onUpdateFail": "페이지 새로고침으로 원래 값 복원", + "note": "READ-only 패턴으로 안정성 우선" + } +} diff --git a/_backup_before_enhance/settings-bank-account.json b/_backup_before_enhance/settings-bank-account.json new file mode 100644 index 0000000..8fd3892 --- /dev/null +++ b/_backup_before_enhance/settings-bank-account.json @@ -0,0 +1,153 @@ +{ + "id": "settings-bank-account", + "name": "계좌관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "설정 > 계좌관리 메뉴의 계좌 목록 조회 및 UI 검증 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "설정", + "level2": "계좌관리", + "expectedUrl": "/settings/accounts", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 설정 > 계좌관리", + "action": "menu_navigate", + "level1": "설정", + "level2": "계좌관리", + "expected": { + "url_contains": "/settings/accounts", + "visible": ["계좌관리", "계좌"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "계좌 목록 표시", + "계좌 등록 버튼 존재", + "검색/필터 기능 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "계좌 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "은행명 컬럼", + "계좌번호 컬럼", + "예금주 컬럼", + "계좌유형 컬럼" + ], + "expected": "계좌 테이블 컬럼 정상 표시" + }, + { + "id": 4, + "name": "검색 기능 확인", + "action": "click_if_exists", + "target": "input[placeholder*='검색']", + "expected": { + "search_available": true + } + }, + { + "id": 5, + "name": "계좌 등록 버튼 확인", + "action": "click_if_exists", + "target": "button:has-text('등록'), button:has-text('계좌 등록'), button:has-text('추가')", + "expected": { + "modal_or_page": true + } + }, + { + "id": 6, + "name": "등록 폼/모달 확인", + "action": "verify_elements", + "checks": [ + "은행명 입력 필드", + "계좌번호 입력 필드", + "예금주 입력 필드" + ], + "expected": "계좌 등록 폼 표시" + }, + { + "id": 7, + "name": "등록 모달 닫기", + "action": "close_modal_if_open", + "expected": "모달 닫힘" + }, + { + "id": 8, + "phase": "READ", + "name": "[READ] 계좌 목록 데이터 확인", + "action": "verify_detail", + "checks": [ + "계좌 목록 데이터 표시됨" + ], + "expected": "계좌 목록 정상 표시" + }, + { + "id": 9, + "phase": "READ", + "name": "[READ] 첫 번째 행 클릭", + "action": "click_if_exists", + "target": "table tbody tr:first-child" + }, + { + "id": 10, + "phase": "READ", + "name": "[READ] 계좌 상세 정보 확인", + "action": "verify_detail", + "checks": [ + "계좌 상세 정보 표시" + ], + "expected": "계좌 상세 정보 확인" + }, + { + "id": 11, + "name": "상세 모달 닫기", + "action": "close_modal_if_open", + "expected": "모달 닫힘" + }, + { + "id": 12, + "name": "계좌 목록 최종 확인", + "action": "verify_elements", + "checks": [ + "계좌 목록 구조 정상", + "등록 버튼 존재" + ], + "expected": "계좌관리 페이지 정상" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/bank-accounts", + "description": "계좌 목록 조회" + } + ], + "requiredVerifications": [ + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "계좌 목록, 등록 버튼, 필터 존재" + } + ], + "rollbackPlan": { + "note": "READ-only 패턴으로 안정성 우선" + } +} diff --git a/_backup_before_enhance/settings-company.json b/_backup_before_enhance/settings-company.json new file mode 100644 index 0000000..b774e58 --- /dev/null +++ b/_backup_before_enhance/settings-company.json @@ -0,0 +1,190 @@ +{ + "id": "settings-company", + "name": "회사정보 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "설정 > 회사정보 메뉴의 회사 정보 조회/수정 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "설정", + "level2": "회사정보", + "expectedUrl": "/company-info", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "testData": { + "update": { + "companyPhone": "02-1234-5678", + "companyFax": "02-1234-5679" + } + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 설정 > 회사정보", + "action": "menu_navigate", + "level1": "설정", + "level2": "회사정보", + "expected": { + "url_contains": "/company", + "visible": ["회사정보", "회사"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "회사 정보 폼 표시", + "사업자등록번호 표시", + "수정 버튼 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "회사 정보 폼 구조 확인", + "action": "verify_elements", + "checks": [ + "회사명 표시", + "사업자등록번호 표시", + "대표자명 표시", + "주소 표시", + "연락처 표시" + ], + "expected": "회사 정보 폼 정상 표시" + }, + { + "id": 4, + "phase": "READ", + "name": "[READ] 현재 회사 정보 확인", + "action": "verify_detail", + "checks": [ + "회사명 표시됨", + "사업자등록번호 표시됨", + "대표자명 표시됨" + ], + "expected": "현재 회사 정보 정상 표시" + }, + { + "id": 5, + "phase": "UPDATE", + "name": "[UPDATE] 회사 정보 수정 모드 진입", + "action": "click_if_exists", + "target": "button:has-text('수정'), button:has-text('편집')", + "expected": { + "edit_mode": true, + "fields_editable": true + } + }, + { + "id": 6, + "phase": "UPDATE", + "name": "[UPDATE] 회사 전화번호 수정", + "action": "click_if_exists", + "target": "input[name*='phone'], input[placeholder*='전화']" + }, + { + "id": 7, + "phase": "UPDATE", + "name": "[UPDATE] 팩스번호 수정", + "action": "click_if_exists", + "target": "input[name*='fax'], input[placeholder*='팩스']" + }, + { + "id": 8, + "phase": "UPDATE", + "name": "[UPDATE] 필수 검증 #2: 회사 정보 저장", + "action": "click_if_exists", + "target": "button:has-text('저장'), button:has-text('확인')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "PUT /api/v1/company", + "toast": "저장|수정|완료|성공" + }, + "expected": "회사 정보 저장 완료" + }, + { + "id": 9, + "phase": "UPDATE", + "name": "[UPDATE] 저장 결과 확인", + "action": "verify_detail", + "checks": [ + "전화번호: 02-1234-5678", + "팩스: 02-1234-5679" + ], + "expected": "수정된 정보 반영" + }, + { + "id": 10, + "name": "로고 이미지 영역 확인", + "action": "verify_elements", + "checks": [ + "회사 로고 표시", + "로고 변경 버튼" + ], + "expected": "로고 영역 표시" + }, + { + "id": 11, + "name": "사업자등록증 영역 확인", + "action": "verify_elements", + "checks": [ + "사업자등록증 첨부", + "파일 업로드 버튼" + ], + "expected": "첨부 영역 표시" + }, + { + "id": 12, + "name": "법인등록번호 확인", + "action": "verify_elements", + "checks": [ + "법인등록번호 표시" + ], + "expected": "법인등록번호 표시" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/company", + "description": "회사 정보 조회" + }, + { + "method": "PUT", + "endpoint": "/api/v1/company", + "description": "회사 정보 수정" + }, + { + "method": "POST", + "endpoint": "/api/v1/company/logo", + "description": "회사 로고 업로드" + } + ], + "requiredVerifications": [ + { + "id": 2, + "name": "저장 버튼", + "steps": [8], + "criteria": "API 호출 + 성공 토스트 + 정보 반영" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "회사 정보 폼, 수정 버튼 존재" + } + ], + "rollbackPlan": { + "onUpdateFail": "페이지 새로고침으로 원래 값 복원", + "note": "회사 정보 수정은 관리자 권한이 필요할 수 있음" + } +} diff --git a/_backup_before_enhance/settings-notification.json b/_backup_before_enhance/settings-notification.json new file mode 100644 index 0000000..30b2658 --- /dev/null +++ b/_backup_before_enhance/settings-notification.json @@ -0,0 +1,196 @@ +{ + "id": "settings-notification", + "name": "알림설정 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "설정 > 알림설정 메뉴의 알림 설정 조회/수정/저장 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "설정", + "level2": "알림설정", + "expectedUrl": "/settings/notification-settings", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "testData": { + "update": { + "emailNotification": true, + "pushNotification": true, + "approvalNotification": true, + "attendanceNotification": false + } + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 설정 > 알림설정", + "action": "menu_navigate", + "level1": "설정", + "level2": "알림설정", + "expected": { + "url_contains": "/settings/notification-settings", + "visible": ["알림설정", "알림"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "알림 설정 폼 표시", + "알림 ON/OFF 토글 존재", + "저장 버튼 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "알림 설정 폼 구조 확인", + "action": "verify_elements", + "checks": [ + "이메일 알림 토글", + "푸시 알림 토글", + "결재 알림 설정", + "근태 알림 설정" + ], + "expected": "알림 설정 폼 정상 표시" + }, + { + "id": 4, + "phase": "READ", + "name": "[READ] 현재 알림 설정 확인", + "action": "verify_detail", + "checks": [ + "이메일 알림 상태 표시", + "푸시 알림 상태 표시", + "결재 알림 상태 표시" + ], + "expected": "현재 알림 설정 정상 표시" + }, + { + "id": 5, + "phase": "UPDATE", + "name": "[UPDATE] 이메일 알림 토글", + "action": "click_if_exists", + "target": "input[name*='email'], label:has-text('이메일') input[type='checkbox']", + "expected": { + "toggle_changed": true + } + }, + { + "id": 6, + "phase": "UPDATE", + "name": "[UPDATE] 푸시 알림 토글", + "action": "click_if_exists", + "target": "input[name*='push'], label:has-text('푸시') input[type='checkbox']", + "expected": { + "toggle_changed": true + } + }, + { + "id": 7, + "phase": "UPDATE", + "name": "[UPDATE] 결재 알림 설정", + "action": "click_if_exists", + "target": "input[name*='approval'], label:has-text('결재') input[type='checkbox']", + "expected": { + "toggle_changed": true + } + }, + { + "id": 8, + "phase": "UPDATE", + "name": "[UPDATE] 필수 검증 #2: 알림 설정 저장", + "action": "click_if_exists", + "target": "button:has-text('저장'), button:has-text('적용')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "PUT /api/v1/settings/notifications", + "toast": "저장|적용|완료|성공" + }, + "expected": "알림 설정 저장 완료" + }, + { + "id": 9, + "phase": "UPDATE", + "name": "[UPDATE] 저장 결과 확인", + "action": "verify_detail", + "checks": [ + "변경된 알림 설정 반영" + ], + "expected": "수정된 설정 반영" + }, + { + "id": 10, + "name": "알림 유형별 설정 확인", + "action": "verify_elements", + "checks": [ + "시스템 알림 설정", + "업무 알림 설정", + "일정 알림 설정" + ], + "expected": "알림 유형별 설정 표시" + }, + { + "id": 11, + "name": "알림 수신 시간 설정 확인", + "action": "verify_elements", + "checks": [ + "수신 시간대 설정", + "방해금지 시간 설정" + ], + "expected": "시간 설정 표시" + }, + { + "id": 12, + "name": "알림 테스트 전송 확인", + "action": "verify_elements", + "checks": [ + "테스트 알림 전송 버튼" + ], + "expected": "테스트 버튼 표시" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/settings/notifications", + "description": "알림 설정 조회" + }, + { + "method": "PUT", + "endpoint": "/api/v1/settings/notifications", + "description": "알림 설정 수정" + }, + { + "method": "POST", + "endpoint": "/api/v1/settings/notifications/test", + "description": "테스트 알림 전송" + } + ], + "requiredVerifications": [ + { + "id": 2, + "name": "저장 버튼", + "steps": [8], + "criteria": "API 호출 + 성공 토스트 + 설정 반영" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "알림 설정 폼, 저장 버튼 존재" + } + ], + "rollbackPlan": { + "onUpdateFail": "페이지 새로고침으로 원래 값 복원", + "note": "설정 페이지는 수정 후 원복 테스트 권장" + } +} diff --git a/_backup_before_enhance/settings-permission.json b/_backup_before_enhance/settings-permission.json new file mode 100644 index 0000000..fe5eb66 --- /dev/null +++ b/_backup_before_enhance/settings-permission.json @@ -0,0 +1,158 @@ +{ + "id": "settings-permission", + "name": "권한관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "설정 > 권한관리 메뉴의 권한 그룹 목록 조회 및 UI 검증 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "설정", + "level2": "권한관리", + "expectedUrl": "/settings/permissions", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 설정 > 권한관리", + "action": "menu_navigate", + "level1": "설정", + "level2": "권한관리", + "expected": { + "url_contains": "/settings/permissions", + "visible": ["권한관리", "권한"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "권한 그룹 목록 표시", + "권한 추가 버튼 존재", + "권한 설정 영역 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "권한 그룹 목록 확인", + "action": "verify_elements", + "checks": [ + "권한 그룹 목록 테이블 또는 리스트", + "기본 역할 (관리자, 사용자 등) 표시", + "권한 추가 버튼" + ], + "expected": "권한 목록 정상 표시" + }, + { + "id": 4, + "phase": "READ", + "name": "[READ] 첫 번째 권한 그룹 클릭", + "action": "click_if_exists", + "target": "table tbody tr:first-child, li:first-child, [class*='list'] > *:first-child", + "expected": { + "visible": ["메뉴 권한", "기능 권한"] + } + }, + { + "id": 5, + "phase": "READ", + "name": "[READ] 권한 체크박스 구조 확인", + "action": "verify_elements", + "checks": [ + "메뉴별 읽기/쓰기/삭제 권한", + "전체 선택/해제 기능" + ], + "expected": "권한 체크박스 매트릭스 표시" + }, + { + "id": 6, + "phase": "READ", + "name": "[READ] 권한 상세 정보 확인", + "action": "verify_detail", + "checks": [ + "권한 그룹 정보 표시됨", + "체크박스 또는 권한 목록 표시" + ], + "expected": "권한 상세 정보 표시" + }, + { + "id": 7, + "name": "권한 추가 버튼 확인", + "action": "click_if_exists", + "target": "button:has-text('추가'), button:has-text('권한 추가'), button:has-text('역할 추가')", + "expected": { + "modal_or_form": true + } + }, + { + "id": 8, + "name": "추가 모달 확인", + "action": "verify_elements", + "checks": [ + "역할명 입력 필드", + "설명 입력 필드" + ], + "expected": "권한 추가 폼 표시" + }, + { + "id": 9, + "name": "추가 모달 닫기", + "action": "close_modal_if_open", + "expected": "모달 닫힘" + }, + { + "id": 10, + "name": "저장 버튼 존재 확인", + "action": "verify_elements", + "checks": [ + "저장 또는 적용 버튼 존재" + ], + "expected": "저장 버튼 표시" + }, + { + "id": 11, + "name": "삭제 버튼 존재 확인", + "action": "verify_elements", + "checks": [ + "삭제 버튼 존재 여부" + ], + "expected": "삭제 기능 확인" + }, + { + "id": 12, + "name": "권한관리 페이지 최종 확인", + "action": "verify_detail", + "checks": [ + "권한관리 페이지 정상 동작" + ], + "expected": "페이지 정상 확인" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/roles", + "description": "역할 목록 조회" + } + ], + "requiredVerifications": [ + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "권한 목록, 추가 버튼, 권한 체크박스 존재" + } + ], + "rollbackPlan": { + "note": "READ-only 패턴으로 안정성 우선" + } +} diff --git a/_backup_before_enhance/settings-popup.json b/_backup_before_enhance/settings-popup.json new file mode 100644 index 0000000..77ce822 --- /dev/null +++ b/_backup_before_enhance/settings-popup.json @@ -0,0 +1,153 @@ +{ + "id": "settings-popup", + "name": "팝업관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "설정 > 팝업관리 메뉴의 팝업 목록 조회 및 UI 검증 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "설정", + "level2": "팝업관리", + "expectedUrl": "/settings/popup-management", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 설정 > 팝업관리", + "action": "menu_navigate", + "level1": "설정", + "level2": "팝업관리", + "expected": { + "url_contains": "/settings/popup", + "visible": ["팝업관리", "팝업"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "팝업 목록 표시", + "팝업 등록 버튼 존재", + "상태 필터 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "팝업 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "팝업 제목 컬럼", + "상태 컬럼", + "기간 컬럼", + "노출 위치 컬럼" + ], + "expected": "팝업 테이블 표시" + }, + { + "id": 4, + "name": "기존 팝업 확인", + "action": "verify_elements", + "checks": [ + "등록된 팝업 존재 또는 등록된 팝업 없음" + ], + "expected": "팝업 목록 상태 확인" + }, + { + "id": 5, + "name": "팝업 등록 버튼 확인", + "action": "click_if_exists", + "target": "button:has-text('등록'), button:has-text('추가'), button:has-text('신규')", + "expected": { + "modal_open": true + } + }, + { + "id": 6, + "name": "등록 폼 요소 확인", + "action": "verify_elements", + "checks": [ + "제목 입력 필드", + "내용 입력 필드", + "기간 설정 필드" + ], + "expected": "팝업 등록 폼 표시" + }, + { + "id": 7, + "name": "등록 모달 닫기", + "action": "close_modal_if_open", + "expected": "모달 닫힘" + }, + { + "id": 8, + "phase": "READ", + "name": "[READ] 팝업 목록 데이터 확인", + "action": "verify_detail", + "checks": [ + "팝업 목록 데이터 표시됨" + ], + "expected": "팝업 목록 정상 표시" + }, + { + "id": 9, + "phase": "READ", + "name": "[READ] 첫 번째 행 클릭", + "action": "click_if_exists", + "target": "table tbody tr:first-child" + }, + { + "id": 10, + "phase": "READ", + "name": "[READ] 팝업 상세 정보 확인", + "action": "verify_detail", + "checks": [ + "팝업 상세 정보 표시" + ], + "expected": "팝업 상세 정보 확인" + }, + { + "id": 11, + "name": "상세 모달 닫기", + "action": "close_modal_if_open", + "expected": "모달 닫힘" + }, + { + "id": 12, + "name": "팝업관리 페이지 최종 확인", + "action": "verify_elements", + "checks": [ + "팝업 목록 구조 정상", + "등록 버튼 존재" + ], + "expected": "팝업관리 페이지 정상" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/settings/popups", + "description": "팝업 목록 조회" + } + ], + "requiredVerifications": [ + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "팝업 목록, 등록 버튼, 상태 필터 존재" + } + ], + "rollbackPlan": { + "note": "READ-only 패턴으로 안정성 우선" + } +} diff --git a/_backup_before_enhance/settings-position.json b/_backup_before_enhance/settings-position.json new file mode 100644 index 0000000..66b7f41 --- /dev/null +++ b/_backup_before_enhance/settings-position.json @@ -0,0 +1,71 @@ +{ + "id": "settings-position", + "name": "직책관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "설정 > 직책관리 목록/검색/상세 기능 검증", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "설정", + "level2": "직책관리", + "expectedUrl": "/settings/titles", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 설정 > 직책관리", + "action": "menu_navigate", + "level1": "설정", + "level2": "직책관리", + "expected": { "url_contains": "/settings" } + }, + { + "id": 2, + "name": "목업 감지", + "action": "verify_not_mockup" + }, + { + "id": 3, + "name": "직책관리 페이지 확인", + "action": "verify_detail", + "checks": ["visible_text:직책"] + }, + { + "id": 4, + "name": "설정 페이지 확인", + "action": "verify_detail", + "checks": ["visible_text:설정"] + }, + { + "id": 5, + "name": "추가 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('추가'), button:has-text('등록'), button:has-text('신규')" + }, + { + "id": 6, + "name": "대기", + "action": "wait", + "duration": 1000 + }, + { + "id": 7, + "name": "모달 닫기", + "action": "close_modal_if_open" + }, + { + "id": 8, + "name": "직책관리 최종 확인", + "action": "verify_detail", + "checks": ["visible_text:직책"] + } + ] +} diff --git a/_backup_before_enhance/settings-rank.json b/_backup_before_enhance/settings-rank.json new file mode 100644 index 0000000..f9156d7 --- /dev/null +++ b/_backup_before_enhance/settings-rank.json @@ -0,0 +1,71 @@ +{ + "id": "settings-rank", + "name": "직급관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "설정 > 직급관리 목록/검색/상세 기능 검증", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "설정", + "level2": "직급관리", + "expectedUrl": "/settings/ranks", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 설정 > 직급관리", + "action": "menu_navigate", + "level1": "설정", + "level2": "직급관리", + "expected": { "url_contains": "/settings" } + }, + { + "id": 2, + "name": "목업 감지", + "action": "verify_not_mockup" + }, + { + "id": 3, + "name": "직급관리 페이지 확인", + "action": "verify_detail", + "checks": ["visible_text:직급"] + }, + { + "id": 4, + "name": "설정 페이지 확인", + "action": "verify_detail", + "checks": ["visible_text:설정"] + }, + { + "id": 5, + "name": "추가 버튼 클릭", + "action": "click_if_exists", + "target": "button:has-text('추가'), button:has-text('등록'), button:has-text('신규')" + }, + { + "id": 6, + "name": "대기", + "action": "wait", + "duration": 1000 + }, + { + "id": 7, + "name": "모달 닫기", + "action": "close_modal_if_open" + }, + { + "id": 8, + "name": "직급관리 최종 확인", + "action": "verify_detail", + "checks": ["visible_text:직급"] + } + ] +} diff --git a/_backup_before_enhance/settings-subscription.json b/_backup_before_enhance/settings-subscription.json new file mode 100644 index 0000000..9210e1b --- /dev/null +++ b/_backup_before_enhance/settings-subscription.json @@ -0,0 +1,134 @@ +{ + "id": "settings-subscription", + "name": "구독관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "설정 > 구독관리 메뉴의 구독 정보 조회/플랜 비교 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "설정", + "level2": "구독관리", + "expectedUrl": "/subscription", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "selectors": { + "planCard": ".card, [class*='plan'], [class*='Plan'], [class*='subscription']", + "priceText": "[class*='price'], [class*='Price'], [class*='amount'], [class*='cost']", + "dateInfo": "[class*='date'], [class*='period'], time", + "paymentInfo": "[class*='payment'], [class*='billing'], [class*='card']", + "planTable": "table, [class*='plan-compare'], [class*='pricing-table']", + "actionButton": "button, [role='button']", + "usageCard": "[class*='usage'], [class*='quota'], [class*='limit']", + "historyTable": "table tbody tr, [class*='history'] li, [class*='payment-list']" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 설정 > 구독관리", + "action": "menu_navigate", + "level1": "설정", + "level2": "구독관리", + "expected": { + "url_contains": "/subscription", + "visible": ["구독관리", "구독"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "구독 정보 표시", + "플랜 정보 표시", + "결제 정보 표시" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "현재 플랜 카드 존재 확인", + "action": "verify_element", + "target": "planCard", + "verification": { "exists": true } + }, + { + "id": 4, + "name": "플랜/가격 정보 텍스트 확인", + "action": "evaluate", + "script": "(() => { const text = document.body.innerText; return text.includes('플랜') || text.includes('요금') || text.includes('Plan') || text.includes('구독'); })()" + }, + { + "id": 5, + "name": "구독 기간/날짜 정보 확인", + "action": "evaluate", + "script": "(() => { const text = document.body.innerText; return text.includes('기간') || text.includes('시작') || text.includes('종료') || /\\d{4}[-./]\\d{2}[-./]\\d{2}/.test(text); })()" + }, + { + "id": 6, + "name": "결제 관련 정보 표시 확인", + "action": "evaluate", + "script": "(() => { const text = document.body.innerText; return text.includes('결제') || text.includes('카드') || text.includes('금액') || text.includes('원'); })()" + }, + { + "id": 7, + "name": "플랜 비교/변경 UI 확인", + "action": "verify_element", + "target": "table, [class*='plan'], [class*='compare'], button:has-text('플랜'), button:has-text('변경'), button:has-text('업그레이드')" + }, + { + "id": 8, + "name": "사용량 현황 영역 확인", + "action": "evaluate", + "script": "(() => { const text = document.body.innerText; return text.includes('사용') || text.includes('용량') || text.includes('사용자') || text.includes('%'); })()" + }, + { + "id": 9, + "name": "결제 내역 영역 확인", + "action": "verify_element", + "target": "table tbody tr, [class*='history'], [class*='payment-list'], [class*='billing-history']" + }, + { + "id": 10, + "name": "다운로드/영수증 버튼 확인", + "action": "verify_element", + "target": "button:has-text('다운로드'), button:has-text('영수증'), button:has-text('Download'), a:has-text('영수증'), [class*='download']" + }, + { + "id": 11, + "name": "결제 수단 관련 UI 확인", + "action": "evaluate", + "script": "(() => { const text = document.body.innerText; return text.includes('결제 수단') || text.includes('카드') || text.includes('계좌') || document.querySelector('[class*=\"payment-method\"], [class*=\"card-info\"]'); })()" + }, + { + "id": 12, + "name": "구독 관리 버튼 확인 (취소/해지 포함)", + "action": "verify_element", + "target": "button:has-text('취소'), button:has-text('해지'), button:has-text('관리'), button:has-text('변경'), [class*='cancel'], [class*='manage']" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/subscription", + "description": "구독 정보 조회" + } + ], + "requiredVerifications": [ + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "구독 정보, 플랜 정보, 결제 정보 존재" + } + ], + "rollbackPlan": { + "note": "조회 전용 페이지로 데이터 변경 없음 (결제/플랜 변경은 별도 테스트)" + } +} diff --git a/_backup_before_enhance/settings-vacation-policy.json b/_backup_before_enhance/settings-vacation-policy.json new file mode 100644 index 0000000..ef33a43 --- /dev/null +++ b/_backup_before_enhance/settings-vacation-policy.json @@ -0,0 +1,197 @@ +{ + "id": "settings-vacation-policy", + "name": "휴가정책 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "설정 > 휴가정책 메뉴의 휴가 정책 조회/수정/저장 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "설정", + "level2": "휴가정책", + "expectedUrl": "/settings/leave-policy", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "testData": { + "update": { + "annualLeave": "15", + "halfDayEnabled": true, + "carryOverDays": "5", + "memo": "E2E 테스트 휴가정책" + }, + "restore": { + "annualLeave": "15", + "halfDayEnabled": true, + "carryOverDays": "5" + } + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 설정 > 휴가정책", + "action": "menu_navigate", + "level1": "설정", + "level2": "휴가정책", + "expected": { + "url_contains": "/settings/leave-policy", + "visible": ["휴가정책", "휴가"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "휴가 정책 설정 폼 표시", + "연차 부여 기준 설정 가능", + "저장 버튼 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "휴가 정책 폼 구조 확인", + "action": "verify_elements", + "checks": [ + "연차 부여 기준 입력", + "반차 사용 여부 설정", + "휴가 이월 일수 설정", + "휴가 유형별 설정" + ], + "expected": "휴가 정책 폼 정상 표시" + }, + { + "id": 4, + "phase": "READ", + "name": "[READ] 현재 정책 값 확인", + "action": "verify_detail", + "checks": [ + "연차 부여 기준 표시", + "반차 사용 설정 표시", + "이월 일수 표시" + ], + "expected": "현재 정책 값 정상 표시" + }, + { + "id": 5, + "phase": "UPDATE", + "name": "[UPDATE] 연차 부여 기준 수정", + "action": "click_if_exists", + "target": "input[name*='annual'], input[placeholder*='연차']" + }, + { + "id": 6, + "phase": "UPDATE", + "name": "[UPDATE] 반차 사용 설정", + "action": "click_if_exists", + "target": "input[type='checkbox'][name*='half'], label:has-text('반차')", + "expected": { + "checkbox_toggled": true + } + }, + { + "id": 7, + "phase": "UPDATE", + "name": "[UPDATE] 이월 일수 수정", + "action": "click_if_exists", + "target": "input[name*='carryOver'], input[placeholder*='이월']" + }, + { + "id": 8, + "phase": "UPDATE", + "name": "[UPDATE] 필수 검증 #2: 정책 저장", + "action": "click_if_exists", + "target": "button:has-text('저장'), button:has-text('적용')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "PUT /api/v1/settings/vacation-policy", + "toast": "저장|적용|완료|성공" + }, + "expected": "휴가 정책 저장 완료" + }, + { + "id": 9, + "phase": "UPDATE", + "name": "[UPDATE] 저장 결과 확인", + "action": "verify_detail", + "checks": [ + "연차: 15", + "이월 일수: 5" + ], + "expected": "수정된 정책 반영" + }, + { + "id": 10, + "name": "휴가 유형 관리 확인", + "action": "verify_elements", + "checks": [ + "연차 유형 표시", + "병가 유형 표시", + "경조사 유형 표시" + ], + "expected": "휴가 유형 목록 표시" + }, + { + "id": 11, + "phase": "CREATE", + "name": "[CREATE] 휴가 유형 추가 버튼 확인", + "action": "verify_elements", + "checks": [ + "휴가 유형 추가 버튼 존재" + ], + "expected": "추가 버튼 표시" + }, + { + "id": 12, + "name": "정책 적용 대상 확인", + "action": "verify_elements", + "checks": [ + "부서별 적용 설정", + "직급별 적용 설정" + ], + "expected": "적용 대상 설정 표시" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/settings/vacation-policy", + "description": "휴가 정책 조회" + }, + { + "method": "PUT", + "endpoint": "/api/v1/settings/vacation-policy", + "description": "휴가 정책 수정" + }, + { + "method": "GET", + "endpoint": "/api/v1/vacation-types", + "description": "휴가 유형 목록 조회" + } + ], + "requiredVerifications": [ + { + "id": 2, + "name": "저장 버튼", + "steps": [8], + "criteria": "API 호출 + 성공 토스트 + 설정 반영" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "휴가 정책 폼, 저장 버튼 존재" + } + ], + "rollbackPlan": { + "onUpdateFail": "페이지 새로고침으로 원래 값 복원", + "note": "설정 페이지는 수정 후 원복 테스트 권장" + } +} diff --git a/_backup_before_enhance/settings-work-schedule.json b/_backup_before_enhance/settings-work-schedule.json new file mode 100644 index 0000000..c0d36bc --- /dev/null +++ b/_backup_before_enhance/settings-work-schedule.json @@ -0,0 +1,188 @@ +{ + "id": "settings-work-schedule", + "name": "근무일정 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "설정 > 근무일정 메뉴의 근무일정 조회/수정/저장 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "설정", + "level2": "근무일정", + "expectedUrl": "/settings/work-schedule", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "testData": { + "update": { + "workDays": "월,화,수,목,금", + "startTime": "09:00", + "endTime": "18:00", + "breakTime": "60" + } + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 설정 > 근무일정", + "action": "menu_navigate", + "level1": "설정", + "level2": "근무일정", + "expected": { + "url_contains": "/settings/work-schedule", + "visible": ["근무일정", "근무"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "근무일정 설정 폼 표시", + "근무일 선택 가능", + "저장 버튼 존재" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "근무일정 폼 구조 확인", + "action": "verify_elements", + "checks": [ + "근무일 선택 (월~일)", + "출근 시간 입력", + "퇴근 시간 입력", + "휴게 시간 설정" + ], + "expected": "근무일정 폼 정상 표시" + }, + { + "id": 4, + "phase": "READ", + "name": "[READ] 현재 근무일정 확인", + "action": "verify_detail", + "checks": [ + "근무일 표시", + "출근 시간 표시", + "퇴근 시간 표시" + ], + "expected": "현재 근무일정 정상 표시" + }, + { + "id": 5, + "phase": "UPDATE", + "name": "[UPDATE] 출근 시간 수정", + "action": "click_if_exists", + "target": "input[name*='start'], input[type='time']:first-of-type" + }, + { + "id": 6, + "phase": "UPDATE", + "name": "[UPDATE] 퇴근 시간 수정", + "action": "click_if_exists", + "target": "input[name*='end'], input[type='time']:last-of-type" + }, + { + "id": 7, + "phase": "UPDATE", + "name": "[UPDATE] 휴게 시간 설정", + "action": "click_if_exists", + "target": "input[name*='break'], input[placeholder*='휴게']" + }, + { + "id": 8, + "phase": "UPDATE", + "name": "[UPDATE] 필수 검증 #2: 근무일정 저장", + "action": "click_if_exists", + "target": "button:has-text('저장'), button:has-text('적용')", + "verify": { + "url_maintained": true, + "no_error_page": true, + "api_call": "PUT /api/v1/settings/work-schedule", + "toast": "저장|적용|완료|성공" + }, + "expected": "근무일정 저장 완료" + }, + { + "id": 9, + "phase": "UPDATE", + "name": "[UPDATE] 저장 결과 확인", + "action": "verify_detail", + "checks": [ + "출근: 09:00", + "퇴근: 18:00", + "휴게: 60분" + ], + "expected": "수정된 일정 반영" + }, + { + "id": 10, + "name": "휴무일 설정 확인", + "action": "verify_elements", + "checks": [ + "토요일 휴무 설정", + "일요일 휴무 설정", + "공휴일 자동 적용" + ], + "expected": "휴무일 설정 표시" + }, + { + "id": 11, + "name": "주간 근무시간 계산 확인", + "action": "verify_elements", + "checks": [ + "주간 근무시간 합계 표시" + ], + "expected": "근무시간 자동 계산" + }, + { + "id": 12, + "name": "부서별 근무일정 확인", + "action": "verify_elements", + "checks": [ + "부서별 일정 설정 가능" + ], + "expected": "부서별 일정 표시" + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/settings/work-schedule", + "description": "근무일정 조회" + }, + { + "method": "PUT", + "endpoint": "/api/v1/settings/work-schedule", + "description": "근무일정 수정" + }, + { + "method": "GET", + "endpoint": "/api/v1/holidays", + "description": "공휴일 목록 조회" + } + ], + "requiredVerifications": [ + { + "id": 2, + "name": "저장 버튼", + "steps": [8], + "criteria": "API 호출 + 성공 토스트 + 설정 반영" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "근무일정 폼, 저장 버튼 존재" + } + ], + "rollbackPlan": { + "onUpdateFail": "페이지 새로고침으로 원래 값 복원", + "note": "설정 페이지는 수정 후 원복 테스트 권장" + } +} diff --git a/_backup_before_enhance/shipment-management.json b/_backup_before_enhance/shipment-management.json new file mode 100644 index 0000000..6772a88 --- /dev/null +++ b/_backup_before_enhance/shipment-management.json @@ -0,0 +1,83 @@ +{ + "id": "shipment-management", + "name": "출고관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "출고관리 > 출고관리 목록/상세 기능 검증", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "출고관리", + "level2": "출고관리", + "expectedUrl": "/outbound/shipments", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 1, + "name": "메뉴 진입: 출고관리 > 출고관리", + "action": "menu_navigate", + "level1": "출고관리", + "level2": "출고관리", + "expected": { "url_contains": "/outbound" } + }, + { + "id": 2, + "name": "목업 감지", + "action": "verify_not_mockup" + }, + { + "id": 3, + "name": "출고관리 페이지 확인", + "action": "verify_detail", + "checks": ["visible_text:출고"] + }, + { + "id": 4, + "name": "UI 요소 확인", + "action": "verify_detail", + "checks": ["visible_text:관리"] + }, + { + "id": 5, + "name": "검색 입력 시도", + "action": "click_if_exists", + "target": "input[type='search'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input" + }, + { + "id": 6, + "name": "대기", + "action": "wait", + "duration": 1000 + }, + { + "id": 7, + "name": "행 클릭 시도", + "action": "click_if_exists", + "target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']" + }, + { + "id": 8, + "name": "상세 확인", + "action": "verify_detail", + "checks": ["visible_text:출고"] + }, + { + "id": 9, + "name": "모달 닫기", + "action": "close_modal_if_open" + }, + { + "id": 10, + "name": "최종 확인", + "action": "verify_detail", + "checks": ["visible_text:출고"] + } + ] +} diff --git a/_backup_before_enhance/vendor-ledger.json b/_backup_before_enhance/vendor-ledger.json new file mode 100644 index 0000000..137f70f --- /dev/null +++ b/_backup_before_enhance/vendor-ledger.json @@ -0,0 +1,725 @@ +{ + "enabled": true, + "id": "vendor-ledger", + "name": "거래처원장 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] + }, + "description": "회계관리 > 거래처원장 메뉴의 기간 설정, 검색, 테이블, 다운로드, 상세 페이지 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "navigation": { + "targetUrl": "/accounting/vendor-ledger", + "urlPattern": "/accounting/vendor-ledger|/ko/accounting/vendor-ledger", + "menuHints": [ + "거래처원장", + "거래처 원장", + "회계관리" + ] + }, + "menuNavigation": { + "level1": "회계관리", + "level2": "거래처원장", + "expectedUrl": "/ko/accounting/vendor-ledger", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "level1": { + "text": "회계관리", + "alternativeNames": [ + "회계", + "Accounting" + ], + "scrollConfig": { + "direction": "down", + "maxScrollAttempts": 5, + "scrollAmount": 200 + } + }, + "level2": { + "text": "거래처원장", + "alternativeNames": [ + "거래처 원장", + "Vendor Ledger" + ], + "scrollConfig": { + "direction": "down", + "maxScrollAttempts": 3, + "scrollAmount": 150 + } + }, + "expectedUrl": "/ko/accounting/vendor-ledger", + "fallbackUrl": "/ko/accounting/vendor-ledger" + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 0, + "name": "사이드바 메뉴 전체 펼치기", + "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { + "type": "wait", + "duration": 300 + }, + { + "type": "evaluate", + "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" + }, + { + "type": "wait", + "duration": 2000 + } + ], + "expected": "사이드바 전체 메뉴가 펼쳐짐" + }, + { + "id": 1, + "name": "로그인 상태 확인", + "action": "verify_page", + "expected": "이미 로그인된 상태" + }, + { + "id": 2, + "name": "2단계 메뉴 진입: 회계관리 > 거래처원장", + "description": "회계관리 > 거래처원장 메뉴로 이동하여 페이지 로드 확인 (scrollAndFind 패턴 사용)", + "actions": [ + { + "type": "scrollAndFind", + "level": 1, + "target": "회계관리", + "alternativeSelectors": [ + "text=회계관리", + "[data-menu='accounting']", + "a:has-text('회계관리')", + "span:has-text('회계관리')" + ], + "scrollConfig": { + "container": ".sidebar-scroll, [class*='sidebar'], nav", + "direction": "down", + "maxAttempts": 5, + "scrollAmount": 200 + } + }, + { + "type": "click_if_exists", + "target": "회계관리" + }, + { + "type": "wait", + "duration": 500 + }, + { + "type": "scrollAndFind", + "level": 2, + "target": "거래처원장", + "alternativeSelectors": [ + "text=거래처원장", + "[data-menu='vendor-ledger']", + "a:has-text('거래처원장')", + "span:has-text('거래처원장')" + ], + "scrollConfig": { + "container": ".sidebar-scroll, [class*='sidebar'], nav", + "direction": "down", + "maxAttempts": 3, + "scrollAmount": 150 + } + }, + { + "type": "click_if_exists", + "target": "거래처원장" + }, + { + "type": "wait", + "target": "페이지 로드 완료" + } + ], + "expected": { + "url": "/ko/accounting/vendor-ledger", + "pageTitle": "거래처원장", + "elements": [ + "통계 카드", + "테이블" + ] + } + }, + { + "id": 3, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "입력 필드 존재 (검색창, 날짜 선택)", + "동작하는 버튼 존재 (엑셀 다운로드)", + "테이블 데이터 표시", + "API 호출 확인" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 4, + "name": "통계 카드 확인", + "action": "verify_elements", + "checks": [ + "전기 이월 카드 표시", + "매출 카드 표시", + "수금 카드 표시", + "잔액 카드 표시" + ], + "expected": "4개 통계 카드 모두 표시, 금액 형식 확인" + }, + { + "id": 5, + "name": "테이블 구조 확인", + "action": "verify_table", + "checks": [ + "체크박스 컬럼", + "No. 컬럼", + "거래처명 컬럼", + "이월잔액 컬럼", + "매출 컬럼", + "수금 컬럼", + "잔액 컬럼", + "결제일 컬럼" + ], + "expected": "8개 컬럼 존재, 합계 행 표시" + }, + { + "id": 6, + "name": "기간 설정 - 시작일 변경", + "action": "click_if_exists", + "target": "startDate", + "value": "2025-01-01", + "expected": "시작일 변경 후 데이터 재조회" + }, + { + "id": 7, + "name": "기간 설정 - 종료일 변경", + "action": "click_if_exists", + "target": "endDate", + "value": "2025-12-31", + "expected": "종료일 변경 후 데이터 재조회" + }, + { + "id": 8, + "name": "기간 설정 - 데이터 변화 확인", + "action": "verify_detail", + "checks": [ + "테이블 데이터 갱신", + "통계 카드 값 갱신", + "합계 행 값 갱신" + ], + "expected": "기간에 맞는 데이터로 변경됨" + }, + { + "id": 9, + "name": "⚠️ 필수 검증: 검색 기능 테스트", + "actions": [ + { + "type": "capture", + "variable": "beforeSearchCount", + "selector": "table tbody tr", + "extract": "count", + "description": "검색 전 행 수 저장" + }, + { + "type": "click_if_exists", + "target": "input[type='search'], input[placeholder*='검색']", + "description": "검색 입력창 존재 확인" + }, + { + "type": "wait", + "duration": 1000, + "description": "검색 결과 로딩 대기" + }, + { + "type": "capture", + "variable": "afterSearchCount", + "selector": "table tbody tr", + "extract": "count", + "description": "검색 후 행 수 저장" + } + ], + "verify": { + "searchApplied": true, + "tableContains": "{testData.searchKeyword}", + "dataChanged": "beforeSearchCount may differ from afterSearchCount" + }, + "expected": "검색어에 맞는 거래처만 필터링" + }, + { + "id": "9-1", + "name": "검색 결과 데이터 검증", + "description": "검색 결과의 모든 행이 검색어를 포함하는지 확인", + "verify": { + "allRowsContain": "{testData.searchKeyword}", + "columnToCheck": "거래처명" + } + }, + { + "id": 10, + "name": "검색 결과 확인", + "action": "verify_search_result", + "checks": [ + "테이블 행 수 변화", + "검색어 포함 거래처명 표시" + ], + "expected": "검색 결과 정상 표시" + }, + { + "id": 11, + "name": "검색 초기화", + "actions": [ + { + "type": "click_if_exists", + "target": "input[type='search'], input[placeholder*='검색']", + "description": "검색 입력창 존재 확인" + }, + { + "type": "wait", + "duration": 500 + }, + { + "type": "capture", + "variable": "afterClearCount", + "selector": "table tbody tr", + "extract": "count" + } + ], + "verify": { + "dataRestored": "afterClearCount should equal beforeSearchCount" + }, + "expected": "전체 데이터 다시 표시" + }, + { + "id": 12, + "name": "체크박스 선택", + "action": "click_if_exists", + "target": "first_row", + "expected": "첫 번째 행 체크박스 선택됨" + }, + { + "id": 13, + "name": "전체 선택 체크박스", + "action": "click_if_exists", + "target": "select_all", + "expected": "모든 행 체크박스 선택됨" + }, + { + "id": 14, + "name": "전체 선택 해제", + "action": "click_if_exists", + "target": "select_all", + "expected": "모든 행 체크박스 해제됨" + }, + { + "id": 15, + "name": "필수 검증 #1: 엑셀 다운로드", + "action": "click_download", + "target": "엑셀 다운로드", + "checks": [ + "버튼 클릭", + "Network API 호출 확인 (/api/v1/vendor-ledger/export)", + "Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "파일 다운로드 이벤트 발생", + "성공 토스트 메시지 (엑셀 파일이 다운로드되었습니다)" + ], + "expected": "엑셀 파일 다운로드 완료" + }, + { + "id": 16, + "name": "테이블 행 클릭 - 상세 페이지 이동", + "action": "click_if_exists", + "target": "table tbody tr:first-child", + "expected": "거래처원장 상세 페이지로 이동" + }, + { + "id": 17, + "name": "상세 페이지 - URL 파라미터 확인", + "action": "verify_url", + "checks": [ + "URL에 거래처 ID 포함", + "start_date 파라미터 포함", + "end_date 파라미터 포함" + ], + "expected": "URL 파라미터 정상 전달" + }, + { + "id": 18, + "name": "상세 페이지 - 헤더 확인", + "action": "verify_elements", + "checks": [ + "거래처원장 상세 (거래명세서별) 타이틀", + "목록 버튼 존재" + ], + "expected": "상세 페이지 헤더 정상 표시" + }, + { + "id": 19, + "name": "상세 페이지 - 거래처 정보 카드 확인", + "action": "verify_vendor_info", + "checks": [ + "회사명 표시", + "사업자등록번호 표시", + "대표자 표시", + "전화번호 표시", + "모바일 표시", + "팩스 표시", + "이메일 표시", + "주소 표시", + "기간 표시" + ], + "expected": "거래처 정보 모두 표시" + }, + { + "id": 20, + "name": "상세 페이지 - 요약 통계 확인", + "action": "verify_summary", + "checks": [ + "이월잔액 표시", + "매출 표시 (녹색)", + "수금 표시 (파란색)", + "잔액 표시" + ], + "expected": "4개 요약 통계 정상 표시" + }, + { + "id": 21, + "name": "상세 페이지 - 판매/수금 내역 테이블 확인", + "action": "verify_transaction_table", + "checks": [ + "일자 컬럼", + "적요 컬럼", + "판매 컬럼", + "수금 컬럼", + "잔액 컬럼", + "작업 컬럼" + ], + "expected": "판매/수금 내역 테이블 정상 표시" + }, + { + "id": 22, + "name": "상세 페이지 - 기간 변경", + "action": "click_if_exists", + "startDate": "2025-06-01", + "endDate": "2025-06-30", + "expected": "기간 변경 후 거래 내역 재조회", + "target": "input[type='date'], [class*='date-picker']" + }, + { + "id": 23, + "name": "상세 페이지 - 거래 내역 데이터 변화 확인", + "action": "verify_transactions_update", + "checks": [ + "테이블 데이터 갱신", + "요약 통계 값 갱신" + ], + "expected": "변경된 기간의 데이터 표시" + }, + { + "id": 24, + "name": "⚠️ 필수 검증: PDF 다운로드 전 페이지 스크린샷", + "description": "PDF 생성 전 페이지 상태를 스크린샷으로 캡처하여 CSS 문제 감지용 기준 이미지 확보", + "actions": [ + { + "type": "screenshot", + "name": "pdf-preview-before-download", + "fullPage": true, + "savePath": "tests/e2e/results/hotfix/screenshots/", + "description": "PDF 생성 대상 페이지 전체 캡처" + }, + { + "type": "screenshot", + "name": "pdf-content-area", + "selector": ".vendor-ledger-detail, .pdf-content, main", + "savePath": "tests/e2e/results/hotfix/screenshots/", + "description": "PDF 콘텐츠 영역만 캡처" + } + ], + "verify": { + "screenshotCaptured": true, + "purpose": "PDF CSS 문제 감지를 위한 기준 이미지" + } + }, + { + "id": "24-1", + "name": "⚠️ 필수 검증: PDF 다운로드 실행 및 파일 보관", + "description": "PDF 다운로드 후 파일을 지정 폴더에 보관하여 수동 검증 가능하게 함", + "actions": [ + { + "type": "expectResponse", + "id": "pdf-download-response", + "urlPattern": "/api/v1/vendor-ledger/*/export-pdf", + "description": "PDF 다운로드 API 응답 대기 설정" + }, + { + "type": "click_if_exists", + "target": "PDF 다운로드", + "description": "PDF 다운로드 버튼 클릭" + }, + { + "type": "wait", + "duration": 3000, + "description": "PDF 생성 및 다운로드 대기" + }, + { + "type": "assertResponse", + "id": "pdf-download-response", + "checks": { + "status": 200, + "contentType": "application/pdf" + } + }, + { + "type": "saveDownloadedFile", + "targetPath": "tests/e2e/results/hotfix/pdf-samples/", + "fileNamePattern": "vendor-ledger-{vendorId}-{timestamp}.pdf", + "description": "다운로드된 PDF 파일을 지정 폴더에 보관" + } + ], + "verify": { + "apiSuccess": true, + "fileDownloaded": true, + "fileSaved": "tests/e2e/results/hotfix/pdf-samples/" + } + }, + { + "id": "24-2", + "name": "⚠️ PDF 파일 유효성 검증", + "description": "다운로드된 PDF 파일의 기본 유효성 검사", + "actions": [ + { + "type": "verifyDownloadedFile", + "checks": { + "fileExists": true, + "fileSize": "> 1024", + "pdfSignature": "%PDF-", + "description": "PDF 파일 헤더 검증" + } + } + ], + "verify": { + "pdfValid": true, + "minFileSize": "1KB 이상" + } + }, + { + "id": "24-3", + "name": "📋 PDF 스타일 수동 확인 체크리스트", + "type": "manualVerification", + "description": "개발자가 다운로드된 PDF를 열어 시각적으로 확인해야 하는 항목", + "manualChecklist": [ + { + "id": "css-1", + "item": "테이블 경계선이 올바르게 표시되는가?", + "category": "테이블 스타일" + }, + { + "id": "css-2", + "item": "한글 폰트가 깨지지 않고 정상 표시되는가?", + "category": "폰트" + }, + { + "id": "css-3", + "item": "숫자/금액 정렬이 올바른가? (우측 정렬)", + "category": "정렬" + }, + { + "id": "css-4", + "item": "여백(margin/padding)이 적절한가?", + "category": "레이아웃" + }, + { + "id": "css-5", + "item": "헤더/푸터가 각 페이지에 올바르게 표시되는가?", + "category": "페이지 구조" + }, + { + "id": "css-6", + "item": "로고/이미지가 정상 표시되는가?", + "category": "이미지" + }, + { + "id": "css-7", + "item": "페이지 나눔(page break)이 적절한 위치에서 발생하는가?", + "category": "페이지 나눔" + }, + { + "id": "css-8", + "item": "배경색/강조색이 올바르게 적용되었는가?", + "category": "색상" + }, + { + "id": "css-9", + "item": "텍스트가 잘리거나 겹치지 않는가?", + "category": "오버플로우" + }, + { + "id": "css-10", + "item": "인쇄 시 레이아웃이 유지되는가?", + "category": "인쇄 호환성" + } + ], + "outputFiles": { + "screenshot": "tests/e2e/results/hotfix/screenshots/pdf-preview-before-download-*.png", + "pdfFile": "tests/e2e/results/hotfix/pdf-samples/vendor-ledger-*.pdf" + }, + "reportFlag": { + "requiresManualReview": true, + "message": "⚠️ PDF 스타일 수동 확인 필요 - 위 체크리스트 항목을 PDF 파일에서 직접 확인하세요" + } + }, + { + "id": 25, + "name": "상세 페이지 - 작업 버튼 확인 (어음 항목)", + "action": "verify_action_buttons", + "checks": [ + "어음 관련 항목에 수정 버튼(Pencil 아이콘) 존재", + "일반 항목에는 작업 버튼 없음" + ], + "expected": "hasAction=true인 항목에만 버튼 표시" + }, + { + "id": 26, + "name": "상세 페이지 - 목록 버튼 클릭", + "action": "click_if_exists", + "target": "목록", + "expected": "거래처원장 목록 페이지로 복귀" + }, + { + "id": 27, + "name": "목록 페이지 복귀 확인", + "action": "verify_url", + "target": "/ko/accounting/vendor-ledger", + "expected": "목록 페이지 정상 표시" + }, + { + "id": 28, + "name": "페이지네이션 동작 확인", + "action": "verify_pagination", + "checks": [ + "현재 페이지 표시", + "전체 페이지 수 표시", + "페이지 이동 버튼 동작" + ], + "expected": "페이지네이션 정상 동작" + } + ], + "requiredVerifications": [ + { + "id": 1, + "name": "파일 다운로드 (엑셀/PDF)", + "steps": [ + 15, + 24, + "24-1", + "24-2", + "24-3" + ], + "criteria": "Network API 호출 + 실제 파일 다운로드 + 성공 토스트 + PDF 스타일 검증" + }, + { + "id": "PDF-STYLE", + "name": "PDF 스타일/CSS 검증", + "steps": [ + 24, + "24-1", + "24-2", + "24-3" + ], + "criteria": "스크린샷 캡처 + PDF 파일 보관 + 수동 체크리스트 확인", + "manualReviewRequired": true, + "outputPaths": { + "screenshots": "tests/e2e/results/hotfix/screenshots/", + "pdfFiles": "tests/e2e/results/hotfix/pdf-samples/" + } + }, + { + "id": 2, + "name": "등록/저장 버튼", + "steps": [], + "criteria": "해당 없음 (조회 전용 페이지)" + }, + { + "id": 3, + "name": "검색/필터", + "steps": [ + 6, + 7, + 8, + 9, + 10, + 11 + ], + "criteria": "기간 설정 및 검색 시 데이터 변화 확인" + }, + { + "id": 4, + "name": "모달 등록 완료", + "steps": [], + "criteria": "해당 없음 (조회 전용 페이지)" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [ + 3 + ], + "criteria": "입력 필드, 동작 버튼, API 호출 확인" + } + ], + "testData": { + "searchKeyword": "테스트", + "dateRange": { + "start": "2025-01-01", + "end": "2025-12-31" + }, + "detailDateRange": { + "start": "2025-06-01", + "end": "2025-06-30" + } + }, + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/vendor-ledger", + "description": "거래처원장 목록 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/vendor-ledger/summary", + "description": "거래처원장 요약 통계 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/vendor-ledger/{id}", + "description": "거래처원장 상세 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/vendor-ledger/export", + "description": "거래처원장 엑셀 다운로드" + }, + { + "method": "GET", + "endpoint": "/api/v1/vendor-ledger/{id}/export-pdf", + "description": "거래처원장 상세 PDF 다운로드" + } + ] +} diff --git a/_backup_before_enhance/vendor-management.json b/_backup_before_enhance/vendor-management.json new file mode 100644 index 0000000..28b1192 --- /dev/null +++ b/_backup_before_enhance/vendor-management.json @@ -0,0 +1,576 @@ +{ + "id": "vendor-management", + "name": "거래처관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "회계관리 > 거래처관리 메뉴의 목록 조회, 필터, 검색, 상세 페이지 진입, 수정 및 저장 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "navigation": { + "targetUrl": "/accounting/vendors", + "urlPattern": "/accounting/vendors", + "menuHints": ["거래처관리", "거래처 관리", "회계관리"] + }, + "menuNavigation": { + "level1": "회계관리", + "level2": "거래처관리", + "expectedUrl": "/accounting/vendors", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", + "level1": "회계관리", + "level2": "거래처관리", + "alternativeLevel1Names": ["회계관리", "회계 관리", "Accounting"], + "alternativeLevel2Names": ["거래처관리", "거래처 관리", "Vendors"], + "scrollConfig": { + "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", + "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + } + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "notes": { + "skip": ["등록 버튼 (추후 구현 예정)", "삭제 기능 (보류)"], + "focus": ["테이블 행 클릭 → 상세 페이지", "수정 모드 진입", "수정 후 저장"], + "uiNotes": [ + "필터 드롭다운: Radix UI Select (button[role='combobox'])", + "체크박스: Radix UI Checkbox (button[role='checkbox'])", + "저장: 확인 다이얼로그 없이 직접 저장 후 목록으로 리다이렉트" + ] + }, + "steps": [ + { + "id": "step-0", + "name": "사이드바 메뉴 전체 펼치기", + "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 300 }, + { + "type": "evaluate", + "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" + }, + { "type": "wait", "duration": 2000 } + ] + }, + { + "id": "step-1", + "name": "2단계 메뉴 진입: 회계관리 > 거래처관리", + "description": "사이드바를 스크롤하며 회계관리 > 거래처관리 메뉴를 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "회계관리", + "alternativeTexts": ["회계관리", "회계 관리", "Accounting"], + "scrollContainer": "sidebar", + "maxAttempts": 10, + "description": "스크롤하며 회계관리 메뉴 찾기" + }, + { "type": "click_if_exists", "target": "회계관리", "description": "회계관리 메뉴 클릭" }, + { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, + { + "type": "scrollAndFind", + "target": "거래처관리", + "alternativeTexts": ["거래처관리", "거래처 관리", "Vendors"], + "scrollContainer": "submenu", + "maxAttempts": 5, + "description": "서브메뉴에서 거래처관리 찾기" + }, + { "type": "click_if_exists", "target": "거래처관리", "description": "거래처관리 메뉴 클릭" }, + { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 } + ], + "expect": { + "url": "/accounting/vendors", + "pageTitle": "거래처관리", + "elements": ["통계 카드", "테이블", "검색창"] + }, + "verification": [ + "회계관리 메뉴가 펼쳐졌는지 확인", + "거래처관리 서브메뉴 클릭 성공", + "404 에러 없이 페이지 로드 완료" + ] + }, + { + "id": 3, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "검색 입력 필드 존재 (placeholder: 거래처명, 거래처코드, 사업자번호 검색...)", + "필터 드롭다운 존재 (구분, 신용등급, 거래등급, 악성채권 - Radix combobox)", + "테이블 데이터 표시", + "API 호출 확인" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 4, + "name": "통계 카드 확인", + "action": "verify_elements", + "checks": [ + "전체 거래처 카드 표시", + "매출 거래처 카드 표시", + "매입 거래처 카드 표시" + ], + "expected": "3개 통계 카드 모두 표시" + }, + { + "id": 5, + "name": "테이블 구조 확인", + "action": "verify_table", + "checks": [ + "체크박스 컬럼 (Radix button[role='checkbox'])", + "번호 컬럼", + "구분 컬럼 (매출/매입/매입매출)", + "거래처명 컬럼", + "매입 결제일 컬럼", + "매출 결제일 컬럼", + "신용등급 컬럼", + "거래등급 컬럼", + "미수금 컬럼", + "악성채권 컬럼" + ], + "expected": "10개 컬럼 존재" + }, + { + "id": 6, + "name": "⚠️ 필수 검증: 검색 기능", + "description": "검색어 입력 후 테이블 데이터가 필터링되는지 확인", + "actions": [ + { + "type": "evaluate", + "script": "(() => { const c = document.querySelectorAll('table tbody tr').length; window.__e2e_beforeSearch = c; return 'beforeSearch=' + c; })()", + "description": "검색 전 행 수 저장" + }, + { "type": "fill", "target": "input[placeholder*='검색']", "value": "가우스", "description": "검색어 '가우스' 입력" }, + { "type": "wait", "duration": 1000, "description": "검색 결과 대기" }, + { + "type": "evaluate", + "script": "(() => { const c = document.querySelectorAll('table tbody tr').length; return 'afterSearch=' + c + ', filtered=' + (c < (window.__e2e_beforeSearch||999)); })()", + "description": "검색 후 행 수 확인" + } + ], + "verify": { + "searchApplied": true, + "tableContains": "가우스", + "dataFiltered": "검색어에 맞는 거래처만 필터링되어야 함" + } + }, + { + "id": 7, + "name": "검색 결과 데이터 검증", + "description": "검색 결과의 각 행에 검색어가 포함되어 있는지 확인", + "actions": [ + { + "type": "verify_text", + "target": "table tbody", + "text": "가우스", + "description": "테이블에 가우스 텍스트 존재 확인" + } + ], + "verify": { + "allRowsContain": "가우스", + "verifyMethod": "테이블의 모든 행이 검색어 '가우스'를 포함하는지 확인" + } + }, + { + "id": 8, + "name": "검색 초기화 및 복원 확인", + "description": "검색어 삭제 후 전체 목록 복원 확인", + "actions": [ + { + "type": "evaluate", + "script": "(() => { const inp = document.querySelector('input[placeholder*=\"검색\"]'); if(inp){ const nset = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value').set; nset.call(inp,''); inp.dispatchEvent(new Event('input',{bubbles:true})); inp.dispatchEvent(new Event('change',{bubbles:true})); return 'cleared'; } return 'not found'; })()", + "description": "검색어 삭제" + }, + { "type": "wait", "duration": 1000, "description": "목록 복원 대기" }, + { + "type": "evaluate", + "script": "(() => { const c = document.querySelectorAll('table tbody tr').length; return 'restored rows=' + c + ', restored=' + (c >= (window.__e2e_beforeSearch||1)); })()", + "description": "복원 후 행 수 확인" + } + ], + "verify": { + "dataRestored": true, + "rowCountRestored": "검색 전과 유사한 행 수로 복원" + } + }, + { + "id": 9, + "name": "구분 필터 테스트 (매출)", + "description": "첫 번째 Radix combobox를 클릭하여 '매출' 옵션 선택", + "actions": [ + { + "type": "evaluate", + "script": "(async () => { const cbs = document.querySelectorAll('button[role=\"combobox\"]'); if(!cbs[0]) return 'combobox not found'; cbs[0].click(); await new Promise(r=>setTimeout(r,500)); const opt = Array.from(document.querySelectorAll('[role=\"option\"]')).find(o=>o.innerText?.trim()==='매출'); if(opt){ opt.click(); await new Promise(r=>setTimeout(r,1000)); return 'selected 매출, rows=' + document.querySelectorAll('table tbody tr').length; } return 'option 매출 not found'; })()", + "description": "구분 필터에서 매출 선택" + } + ], + "expected": "매출 거래처만 필터링" + }, + { + "id": 10, + "name": "구분 필터 초기화", + "description": "구분 필터를 '전체'로 되돌리기", + "actions": [ + { + "type": "evaluate", + "script": "(async () => { const cbs = document.querySelectorAll('button[role=\"combobox\"]'); if(!cbs[0]) return 'combobox not found'; cbs[0].click(); await new Promise(r=>setTimeout(r,500)); const opt = Array.from(document.querySelectorAll('[role=\"option\"]')).find(o=>o.innerText?.trim()==='전체'); if(opt){ opt.click(); await new Promise(r=>setTimeout(r,1000)); return 'selected 전체, rows=' + document.querySelectorAll('table tbody tr').length; } return 'option 전체 not found'; })()", + "description": "구분 필터를 전체로 초기화" + } + ], + "expected": "전체 데이터 다시 표시" + }, + { + "id": 11, + "name": "테이블 행 클릭 - 상세 페이지 이동", + "description": "목록 페이지에서 첫 번째 행을 클릭하여 상세 페이지로 이동", + "actions": [ + { + "type": "evaluate", + "script": "(() => { const url = window.location.href; if(!url.includes('/accounting/vendors') || url.includes('mode=')) return 'NOT on list page: ' + url; return 'on list page'; })()", + "description": "목록 페이지 확인" + }, + { + "type": "evaluate", + "script": "(async () => { const rows = document.querySelectorAll('table tbody tr'); if(rows.length===0) return 'no rows'; rows[0].click(); await new Promise(r=>setTimeout(r,2000)); return 'clicked row, url=' + window.location.href; })()", + "description": "첫 번째 행 클릭" + } + ], + "expected": "거래처 상세 페이지로 이동" + }, + { + "id": 12, + "name": "상세 페이지 - URL 확인", + "action": "verify_url", + "target": "/accounting/vendors/\\d+", + "checks": [ + "URL에 거래처 ID 포함 (/accounting/vendors/{id})" + ], + "expected": "URL 정상 전달" + }, + { + "id": 13, + "name": "상세 페이지 - 헤더 확인", + "action": "verify_elements", + "checks": [ + "거래처 상세 타이틀", + "목록 버튼 존재", + "삭제 버튼 존재", + "수정 버튼 존재" + ], + "expected": "상세 페이지 헤더 정상 표시" + }, + { + "id": 14, + "name": "상세 페이지 - 기본 정보 카드 확인", + "action": "verify_detail_info", + "checks": [ + "사업자등록번호 필드", + "거래처코드 필드", + "거래처명 필드", + "대표자명 필드", + "업태 필드", + "업종 필드" + ], + "expected": "기본 정보 모두 표시 (읽기 전용)" + }, + { + "id": 15, + "name": "상세 페이지 - 연락처 정보 확인", + "action": "verify_detail_info", + "checks": [ + "주소 필드", + "전화번호 필드", + "모바일 필드", + "팩스 필드", + "이메일 필드" + ], + "expected": "연락처 정보 모두 표시" + }, + { + "id": 16, + "name": "상세 페이지 - 담당자 정보 확인", + "action": "verify_detail_info", + "checks": [ + "담당자명 필드", + "담당자 전화 필드", + "시스템 관리자 필드" + ], + "expected": "담당자 정보 모두 표시" + }, + { + "id": 17, + "name": "상세 페이지 - 회사 정보 확인", + "action": "verify_detail_info", + "checks": [ + "회사 로고 영역", + "매입 결제일 필드", + "매출 결제일 필드" + ], + "expected": "회사 정보 모두 표시" + }, + { + "id": 18, + "name": "상세 페이지 - 신용/거래 정보 확인", + "action": "verify_detail_info", + "checks": [ + "신용등급 필드", + "거래등급 필드", + "세금계산서 이메일 필드", + "입금계좌 은행 필드", + "계좌 필드", + "예금주 필드" + ], + "expected": "신용/거래 정보 모두 표시" + }, + { + "id": 19, + "name": "상세 페이지 - 추가 정보 확인", + "action": "verify_detail_info", + "checks": [ + "미수금 필드", + "악성채권 금액 필드", + "악성채권 상태 필드" + ], + "expected": "추가 정보 모두 표시" + }, + { + "id": 20, + "name": "상세 페이지 - 메모 섹션 확인", + "action": "verify_elements", + "checks": [ + "메모 카드 존재", + "메모 리스트 또는 빈 메시지" + ], + "expected": "메모 섹션 표시" + }, + { + "id": 21, + "name": "핵심 테스트: 수정 버튼 클릭", + "action": "click_if_exists", + "target": "수정", + "expected": "수정 모드로 전환 (URL에 ?mode=edit 추가)" + }, + { + "id": 22, + "name": "수정 모드 - URL 확인", + "action": "verify_url", + "target": "mode=edit", + "checks": [ + "URL에 mode=edit 파라미터 포함" + ], + "expected": "수정 모드 URL 정상" + }, + { + "id": 23, + "name": "수정 모드 - 필드 편집 가능 확인", + "action": "verify_edit_mode", + "checks": [ + "거래처명 입력 가능", + "대표자명 입력 가능", + "전화번호 입력 가능", + "이메일 입력 가능", + "저장 버튼 존재", + "취소 버튼 존재" + ], + "expected": "수정 모드에서 필드 편집 가능" + }, + { + "id": 24, + "name": "핵심 테스트: 거래처명 수정", + "description": "거래처명 input 필드에 테스트 접미사 추가. input에 id/name이 없으므로 value 기반 탐색 필요", + "actions": [ + { + "type": "evaluate", + "script": "(async () => { const inputs = document.querySelectorAll('input:not([type=\"hidden\"]):not([type=\"checkbox\"])'); let target = null; inputs.forEach(inp => { if(inp.value && inp.value.length > 1 && !inp.value.includes('수정테스트') && !inp.placeholder.includes('자동생성') && !inp.placeholder.includes('000-00')) { if(!target) target = inp; } }); if(!target) { for(const inp of inputs) { if(inp.value && inp.value.length > 1 && !inp.placeholder.includes('자동생성') && !inp.placeholder.includes('000-00')) { target = inp; break; } } } if(target) { const nset = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value').set; const origVal = target.value; nset.call(target, origVal + ' (수정테스트)'); target.dispatchEvent(new Event('input',{bubbles:true})); target.dispatchEvent(new Event('change',{bubbles:true})); window.__e2e_origVendorName = origVal; return 'modified: ' + origVal + ' → ' + target.value; } return 'no editable input found'; })()", + "description": "첫 번째 편집 가능 필드(거래처명)에 접미사 추가" + } + ], + "expected": "거래처명에 ' (수정테스트)' 추가" + }, + { + "id": 25, + "name": "핵심 테스트: 저장 버튼 클릭", + "description": "저장 버튼 클릭. 이 페이지는 다이얼로그 없이 직접 저장 후 목록으로 리다이렉트됨", + "actions": [ + { + "type": "evaluate", + "script": "(() => { window.__e2e_urlBeforeSave = window.location.href; return 'saved url: ' + window.__e2e_urlBeforeSave; })()", + "description": "저장 전 URL 기록" + }, + { "type": "click_if_exists", "target": "저장", "description": "저장 버튼 클릭" }, + { "type": "wait", "duration": 2000, "description": "저장 처리 대기" } + ], + "expected": "저장 완료 후 목록 페이지로 리다이렉트" + }, + { + "id": 26, + "name": "필수 검증 #2: 저장 완료 확인", + "description": "저장 후 URL 변경 및 에러 여부 확인 (다이얼로그 없이 직접 저장 방식)", + "actions": [ + { + "type": "evaluate", + "script": "(() => { const url = window.location.href; const isListPage = url.includes('/accounting/vendors') && !url.includes('mode='); const hasError = document.body.innerText.includes('404') || document.body.innerText.includes('500') || document.body.innerText.includes('Not Found'); const urlChanged = url !== window.__e2e_urlBeforeSave; return JSON.stringify({ url, isListPage, hasError, urlChanged, result: isListPage && !hasError ? 'PASS' : 'FAIL' }); })()", + "description": "저장 후 목록 페이지 복귀 및 에러 없음 확인" + } + ], + "expected": "목록 페이지로 복귀, 에러 없음" + }, + { + "id": 27, + "name": "수정 결과 확인 - 목록에서 검증", + "description": "목록 페이지에서 수정된 거래처명 확인", + "actions": [ + { + "type": "evaluate", + "script": "(() => { const found = document.body.innerText.includes('수정테스트'); const rows = document.querySelectorAll('table tbody tr').length; return JSON.stringify({ modifiedVisible: found, rowCount: rows, result: found ? 'PASS: 수정된 데이터 목록에 반영' : 'WARN: 수정 텍스트 미표시 (페이지네이션 또는 정렬 영향)' }); })()", + "description": "목록에서 수정된 거래처 확인" + } + ], + "expected": "수정된 거래처명이 목록에 표시" + }, + { + "id": 28, + "name": "원래 값 복원 - 수정된 거래처 클릭", + "description": "수정된 거래처를 찾아 클릭하여 상세 페이지 진입", + "actions": [ + { + "type": "evaluate", + "script": "(async () => { const rows = document.querySelectorAll('table tbody tr'); let target = null; rows.forEach(row => { if(row.innerText.includes('수정테스트')) target = row; }); if(target){ target.click(); await new Promise(r=>setTimeout(r,2000)); return 'clicked modified vendor, url=' + window.location.href; } rows[0]?.click(); await new Promise(r=>setTimeout(r,2000)); return 'modified vendor not found in current page, clicked first row. url=' + window.location.href; })()", + "description": "수정된 거래처 행 클릭" + } + ], + "expected": "수정된 거래처 상세 페이지 진입" + }, + { + "id": 29, + "name": "원래 값 복원 - 수정 버튼 클릭", + "actions": [ + { + "type": "evaluate", + "script": "(async () => { const btn = Array.from(document.querySelectorAll('button')).find(b=>b.innerText?.trim()==='수정'); if(btn){ btn.click(); await new Promise(r=>setTimeout(r,1500)); return 'edit mode, url=' + window.location.href; } return 'edit button not found'; })()", + "description": "수정 모드 진입" + } + ], + "expected": "수정 모드로 전환" + }, + { + "id": 30, + "name": "원래 값 복원 - 거래처명 원복", + "description": "거래처명에서 ' (수정테스트)' 제거", + "actions": [ + { + "type": "evaluate", + "script": "(async () => { const inputs = document.querySelectorAll('input:not([type=\"hidden\"]):not([type=\"checkbox\"])'); let restored = false; inputs.forEach(inp => { if(inp.value.includes('수정테스트')){ const nset = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value').set; const newVal = inp.value.replace(' (수정테스트)',''); nset.call(inp, newVal); inp.dispatchEvent(new Event('input',{bubbles:true})); inp.dispatchEvent(new Event('change',{bubbles:true})); restored = true; } }); return restored ? 'restored' : 'no field with 수정테스트 found'; })()", + "description": "접미사 제거하여 원래 값 복원" + } + ], + "expected": "거래처명에서 ' (수정테스트)' 제거" + }, + { + "id": 31, + "name": "원래 값 복원 - 저장", + "description": "복원 저장 (다이얼로그 없이 직접 저장)", + "actions": [ + { + "type": "evaluate", + "script": "(async () => { const btn = Array.from(document.querySelectorAll('button')).find(b=>b.innerText?.trim()==='저장'); if(btn){ btn.click(); await new Promise(r=>setTimeout(r,2000)); return 'saved, url=' + window.location.href; } return 'save button not found'; })()", + "description": "저장 버튼 클릭" + } + ], + "expected": "원래 값으로 복원 완료, 목록으로 리다이렉트" + }, + { + "id": 32, + "name": "원래 값 복원 - 완료 확인", + "description": "복원 후 목록 페이지에서 수정테스트 텍스트 제거 확인", + "actions": [ + { + "type": "evaluate", + "script": "(() => { const url = window.location.href; const isListPage = url.includes('/accounting/vendors') && !url.includes('mode='); const stillModified = document.body.innerText.includes('수정테스트'); return JSON.stringify({ url, isListPage, stillModified, result: isListPage && !stillModified ? 'PASS: 원복 완료' : 'WARN: 원복 확인 필요' }); })()", + "description": "목록에서 원복 확인" + } + ], + "expected": "수정테스트 텍스트 제거됨" + }, + { + "id": 33, + "name": "목록 페이지 최종 확인", + "action": "verify_url", + "target": "/accounting/vendors", + "expected": "거래처관리 목록 페이지 정상 표시" + }, + { + "id": 34, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body", + "expected": "심각한 콘솔 에러 없음" + } + ], + "requiredVerifications": [ + { + "id": 1, + "name": "등록/저장 버튼", + "steps": [25, 26], + "criteria": "저장 클릭 → 목록 리다이렉트 + 에러 없음 + 데이터 반영" + }, + { + "id": 2, + "name": "등록 버튼 (신규)", + "steps": [], + "criteria": "보류 - 추후 구현 예정" + }, + { + "id": 3, + "name": "검색/필터", + "steps": [6, 7, 8, 9, 10], + "criteria": "검색 및 필터 시 데이터 변화 확인" + }, + { + "id": 4, + "name": "삭제 기능", + "steps": [], + "criteria": "보류 - 테스트 대상에서 제외" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [3], + "criteria": "입력 필드, 동작 버튼, API 호출 확인" + } + ], + "testData": { + "searchKeyword": "가우스", + "editSuffix": " (수정테스트)" + }, + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/clients", + "description": "거래처 목록 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/clients/{id}", + "description": "거래처 상세 조회" + }, + { + "method": "PUT", + "endpoint": "/api/v1/clients/{id}", + "description": "거래처 수정" + } + ] +} diff --git a/_backup_before_enhance/withdrawal-management.json b/_backup_before_enhance/withdrawal-management.json new file mode 100644 index 0000000..499bd59 --- /dev/null +++ b/_backup_before_enhance/withdrawal-management.json @@ -0,0 +1,528 @@ +{ + "enabled": true, + "id": "withdrawal-management", + "name": "출금관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "출금관리 목록 조회, 계정과목명 일괄변경, 상세 수정 기능 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/ko/accounting/withdrawals", + "navigation": { + "targetUrl": "/accounting/withdrawals", + "urlPattern": "/accounting/withdrawals|/ko/accounting/withdrawals", + "menuHints": ["출금관리", "출금 관리", "회계관리"] + }, + "menuNavigation": { + "level1": "회계관리", + "level2": "출금관리", + "expectedUrl": "/ko/accounting/withdrawals", + "searchWithinParent": true, + "closeOtherMenus": true + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebarSelector": ".sidebar-scroll, [data-sidebar], nav[role='navigation']", + "scrollBehavior": { + "direction": "down", + "step": 200, + "maxScrolls": 10, + "waitAfterScroll": 300 + }, + "level1": { + "text": "회계관리", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "출금관리", + "waitAfterClick": 300 + }, + "fallbackUrl": "/ko/accounting/withdrawals", + "verification": { + "urlContains": "/accounting/withdrawals", + "timeout": 5000 + } + }, + "timeout": 60000, + "tags": ["accounting", "withdrawal", "crud"], + + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 메뉴 전체 펼치기", + "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 300 }, + { + "type": "evaluate", + "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" + }, + { "type": "wait", "duration": 2000 } + ], + "expect": { + "sidebarReady": true + } + }, + { + "id": "step-1", + "name": "출금관리 메뉴 진입", + "description": "회계관리 > 출금관리 메뉴로 이동 (scrollAndFind 패턴 사용)", + "menuNavigation": { + "useEnhanced": true, + "scrollAndFind": { + "level1": { + "text": "회계관리", + "scrollUntilVisible": true, + "clickToExpand": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "출금관리", + "scrollUntilVisible": true, + "waitAfterClick": 300 + } + }, + "fallback": { + "useDirectUrl": true, + "url": "/ko/accounting/withdrawals" + } + }, + "expect": { + "url": "/accounting/withdrawals", + "visible": ["출금관리", "총 출금"] + } + }, + { + "id": "step-2", + "name": "목록 페이지 구조 확인", + "description": "테이블 및 필터 요소 확인", + "expect": { + "visible": ["출금일", "출금계좌", "받는분", "출금금액", "거래처", "적요", "출금유형"], + "elements": { + "statisticsCards": ["총 출금", "당월 출금", "거래처 미설정", "출금유형 미설정"], + "filters": ["계정과목명", "저장", "새로고침"], + "pagination": true + } + } + }, + { + "id": "step-3", + "name": "계정과목명 드롭다운 옵션 확인", + "description": "계정과목명 일괄변경 드롭다운 옵션 검증", + "actions": [ + { "type": "click_if_exists", "target": "계정과목명 드롭다운", "description": "드롭다운 열기" } + ], + "expect": { + "options": ["미설정", "매입대금", "급여", "임차료", "수도광열비", "통신비", "소모품비", "운반비", "차량유지비", "보험료", "세금과공과", "이자비용", "수수료", "기타"] + }, + "note": "출금유형 옵션은 비용 계정 기준이므로 입금관리와 다름" + }, + { + "id": "step-4", + "name": "체크박스 선택 후 계정과목명 일괄변경", + "description": "테이블 행 선택 후 계정과목명 일괄변경 저장", + "actions": [ + { "type": "click_if_exists", "target": "첫 번째 행 체크박스", "description": "행 선택" }, + { "type": "click_if_exists", "target": "계정과목명 드롭다운", "description": "드롭다운 열기" }, + { "type": "click_if_exists", "target": "매입대금", "description": "매입대금 선택" }, + { "type": "click_if_exists", "target": "저장", "description": "저장 버튼 클릭" } + ], + "expect": { + "dialog": "확인 다이얼로그 표시", + "dialogMessage": "1개의 출금 유형을 매입대금(으)로 모두 변경하시겠습니까?", + "toast": "변경 완료 메시지" + }, + "checks": [ + "API 호출 확인 (PUT /api/v1/withdrawals/batch-update-account)", + "성공 토스트 메시지", + "URL 유지 확인" + ] + }, + { + "id": "step-4-1", + "name": "⚠️ 필수 검증: 계정과목명 변경 데이터 반영 확인", + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", + "description": "저장 후 테이블에서 변경된 출금유형 값 확인", + "expect": { + "tableCell": { + "row": 1, + "column": "출금유형", + "value": "매입대금" + } + }, + "checks": [ + "선택한 행의 출금유형 컬럼 값 확인", + "변경 전 값과 비교 (예: 미설정 → 매입대금)", + "페이지 새로고침 후에도 변경된 값 유지 확인", + "API 응답과 UI 표시값 일치 확인" + ], + "knownBugReference": "BUG-DEPOSIT-20260115-001 (입금관리 동일 버그 확인 필요)" + }, + { + "id": "step-5", + "name": "출금 상세 페이지 이동", + "description": "테이블 행 클릭하여 상세 페이지로 이동", + "actions": [ + { "type": "click_if_exists", "target": "테이블 첫 번째 행", "description": "행 클릭 (체크박스 제외 영역)" } + ], + "expect": { + "url": "/accounting/withdrawals/{id}", + "visible": ["출금 상세", "기본 정보", "목록", "삭제", "수정"] + } + }, + { + "id": "step-6", + "name": "상세 페이지 읽기 모드 필드 확인", + "description": "수정 전 필드들이 비활성화 상태인지 확인", + "expect": { + "fields": [ + { "name": "출금일", "disabled": true }, + { "name": "출금계좌", "disabled": true }, + { "name": "받는분", "disabled": true }, + { "name": "출금금액", "disabled": true }, + { "name": "적요", "disabled": true }, + { "name": "거래처", "disabled": true }, + { "name": "출금 유형", "disabled": true } + ] + } + }, + { + "id": "step-7", + "name": "수정 모드 전환", + "description": "수정 버튼 클릭하여 편집 모드로 전환", + "click": "수정", + "expect": { + "url": "/accounting/withdrawals/{id}?mode=edit", + "visible": ["출금 수정", "취소", "저장"], + "notVisible": ["목록", "삭제", "수정"] + } + }, + { + "id": "step-8", + "name": "수정 모드 필드 활성화 검증", + "description": "수정 가능한 필드와 불가능한 필드 확인", + "expect": { + "fields": [ + { "name": "출금일", "disabled": true, "note": "은행데이터 - 수정 불가" }, + { "name": "출금계좌", "disabled": true, "note": "은행데이터 - 수정 불가" }, + { "name": "받는분", "disabled": true, "note": "은행데이터 - 수정 불가" }, + { "name": "출금금액", "disabled": true, "note": "은행데이터 - 수정 불가" }, + { "name": "적요", "disabled": false, "editable": true }, + { "name": "거래처", "disabled": false, "type": "combobox", "editable": true }, + { "name": "출금 유형", "disabled": false, "type": "combobox", "editable": true } + ] + } + }, + { + "id": "step-9", + "name": "거래처 드롭다운 옵션 확인", + "description": "거래처 선택 드롭다운 옵션 검증", + "actions": [ + { "type": "click_if_exists", "target": "거래처 드롭다운", "description": "드롭다운 열기" } + ], + "expect": { + "options": ["거래처테스트", "아크더레드", "코브라브릿지", "가우스전자", "아크아크"], + "note": "거래처 옵션은 시스템 공통 데이터" + } + }, + { + "id": "step-10", + "name": "출금 유형 드롭다운 옵션 확인", + "description": "출금 유형 선택 드롭다운 옵션 검증", + "actions": [ + { "type": "click_if_exists", "target": "출금 유형 드롭다운", "description": "드롭다운 열기" } + ], + "expect": { + "options": ["미설정", "매입대금", "급여", "임차료", "수도광열비", "통신비", "소모품비", "운반비", "차량유지비", "보험료", "세금과공과", "이자비용", "수수료", "기타"] + } + }, + { + "id": "step-11", + "name": "수정 데이터 입력", + "description": "수정 가능한 필드에 테스트 데이터 입력", + "form": { + "fields": [ + { "name": "적요", "type": "text", "value": "테스트 적요 수정" } + ] + }, + "actions": [ + { "type": "click_if_exists", "target": "거래처 드롭다운", "description": "거래처 드롭다운 열기" }, + { "type": "click_if_exists", "target": "거래처테스트", "description": "거래처 선택" }, + { "type": "click_if_exists", "target": "출금 유형 드롭다운", "description": "출금 유형 드롭다운 열기" }, + { "type": "click_if_exists", "target": "매입대금", "description": "매입대금 선택" } + ] + }, + { + "id": "step-12", + "name": "저장 및 결과 확인", + "description": "저장 버튼 클릭 후 데이터 반영 확인", + "click": "저장", + "expect": { + "toast": "저장 완료 메시지", + "url": "/accounting/withdrawals/{id}", + "mode": "view" + }, + "checks": [ + "API 호출 확인 (PUT /api/v1/withdrawals/{id})", + "성공 토스트 메시지", + "URL 유지 확인 (에러 페이지 이동 금지)" + ] + }, + { + "id": "step-12-1", + "name": "⚠️ 필수 검증: 수정 데이터 반영 확인", + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", + "description": "저장 후 상세 페이지에서 변경된 값 확인", + "expect": { + "fields": [ + { "name": "적요", "value": "테스트 적요 수정" }, + { "name": "거래처", "value": "거래처테스트" }, + { "name": "출금 유형", "value": "매입대금" } + ] + } + }, + { + "id": "step-13", + "name": "취소 버튼 동작 확인", + "description": "수정 모드에서 취소 버튼 동작 검증", + "actions": [ + { "type": "click_if_exists", "target": "수정", "description": "수정 모드 진입" }, + { "type": "click_if_exists", "target": "취소", "description": "취소 버튼 클릭" } + ], + "expect": { + "url": "/accounting/withdrawals/{id}", + "mode": "view", + "visible": ["출금 상세", "목록", "삭제", "수정"] + } + }, + { + "id": "step-14", + "name": "목록 버튼 동작 확인", + "description": "목록 버튼 클릭하여 목록 페이지로 이동", + "click": "목록", + "expect": { + "url": "/accounting/withdrawals", + "visible": ["출금관리", "총 출금"] + } + }, + { + "id": "step-15", + "name": "필터 드롭다운 검증", + "description": "목록 페이지 필터 드롭다운 옵션 확인", + "note": "3개의 필터 드롭다운 존재 (거래처, 출금유형, 정렬)", + "expect": { + "filters": [ + { "name": "거래처 필터", "default": "전체" }, + { "name": "출금유형 필터", "default": "전체" }, + { "name": "정렬", "default": "최신순", "options": ["최신순", "등록순", "금액 높은순", "금액 낮은순"] } + ] + } + }, + { + "id": "step-16", + "name": "날짜 필터 검증", + "description": "날짜 필터 버튼 동작 확인", + "actions": [ + { "type": "click_if_exists", "target": "당해년도", "description": "당해년도 버튼 클릭" } + ], + "expect": { + "dateRange": { + "start": "2026-01-01", + "end": "2026-12-31" + } + } + }, + { + "id": "step-17", + "name": "페이지네이션 동작 확인", + "description": "페이지네이션 버튼 동작 검증 (데이터 존재 시)", + "condition": "데이터가 20건 이상인 경우에만 실행", + "expect": { + "pagination": { + "itemsPerPage": 20, + "currentPage": 1 + } + }, + "actions": [ + { "type": "click_if_exists", "target": "다음", "description": "다음 페이지로 이동" } + ], + "expectAfterAction": { + "currentPage": 2 + } + } + ], + + "skipTests": [ + { + "id": "delete-button", + "name": "삭제 버튼 테스트", + "reason": "사용자 요청에 따라 테스트 제외" + } + ], + + "knownBugs": [ + { + "id": "BUG-DEPOSIT-20260115-001", + "description": "계정과목명 일괄변경 시 API 오류 발생 (입금관리)", + "errorMessage": "존재하지 않는 URI 또는 데이터", + "relatedSteps": ["step-4-1"], + "note": "출금관리에서도 동일한 버그가 존재할 수 있으므로 step-4-1에서 검증 필수" + }, + { + "id": "BUG-SALES-20260115-001", + "description": "계정과목명 일괄변경 시 토스트 성공 표시되나 실제 데이터 미변경 (매출관리)", + "relatedSteps": ["step-4-1"], + "note": "유사한 UI 패턴이므로 동일 버그 가능성 있음" + } + ], + + "criticalValidationChecklist": [ + { + "id": 1, + "name": "파일 다운로드 검증", + "trigger": "다운로드, Export 버튼 발견 시", + "checks": ["Network API 호출", "실제 파일 다운로드"], + "note": "Console LOG만으로 PASS 판정 금지" + }, + { + "id": 2, + "name": "등록/저장 버튼 검증", + "trigger": "등록, 저장, 제출 버튼 클릭 시", + "checks": ["URL 유지 확인", "에러 페이지 없음", "성공 토스트"], + "note": "에러 페이지 이동 감지 필수" + }, + { + "id": 3, + "name": "일괄변경 데이터 반영 검증", + "trigger": "계정과목명 저장 후", + "checks": ["테이블 데이터 변경 확인", "새로고침 후 유지 확인"], + "note": "토스트만 확인하면 불충분" + }, + { + "id": 4, + "name": "목업 페이지 감지", + "trigger": "페이지 로드 시", + "checks": ["입력 필드 존재", "동작하는 버튼 존재", "API 호출 여부"], + "note": "버튼만 있고 동작 안하면 목업" + } + ], + + "testData": { + "sampleWithdrawal": { + "date": "2025-12-28", + "account": "운영계좌", + "recipient": "홍길동", + "amount": "1,500,000", + "vendor": "", + "description": "급여 지급", + "withdrawalType": "미설정" + }, + "modifiedData": { + "description": "테스트 적요 수정", + "vendor": "거래처테스트", + "withdrawalType": "매입대금" + } + }, + + "pageStructure": { + "listPage": { + "url": "/accounting/withdrawals", + "title": "출금관리", + "statistics": ["총 출금", "당월 출금", "거래처 미설정", "출금유형 미설정"], + "tableColumns": ["checkbox", "출금일", "출금계좌", "받는분", "출금금액", "거래처", "적요", "출금유형", "action"], + "batchUpdate": { + "label": "계정과목명", + "saveButton": "저장" + }, + "filters": ["거래처", "출금유형", "정렬"], + "dateFilters": ["당해년도", "전전월", "전월", "당월", "어제", "오늘"] + }, + "detailPage": { + "url": "/accounting/withdrawals/{id}", + "title": "출금 상세", + "buttons": ["목록", "삭제", "수정"], + "fields": { + "readOnly": ["출금일", "출금계좌", "받는분", "출금금액"], + "editable": ["적요", "거래처", "출금 유형"] + } + }, + "editPage": { + "url": "/accounting/withdrawals/{id}?mode=edit", + "title": "출금 수정", + "buttons": ["취소", "저장"] + } + }, + + "dropdownOptions": { + "accountSubject": { + "label": "계정과목명", + "options": ["미설정", "매입대금", "급여", "임차료", "수도광열비", "통신비", "소모품비", "운반비", "차량유지비", "보험료", "세금과공과", "이자비용", "수수료", "기타"], + "note": "출금유형은 비용 계정 기준 (입금유형과 다름)" + }, + "withdrawalType": { + "label": "출금 유형", + "options": ["미설정", "매입대금", "급여", "임차료", "수도광열비", "통신비", "소모품비", "운반비", "차량유지비", "보험료", "세금과공과", "이자비용", "수수료", "기타"] + }, + "vendor": { + "label": "거래처", + "options": ["거래처테스트", "아크더레드", "코브라브릿지", "가우스전자", "아크아크"] + }, + "sortOrder": { + "label": "정렬", + "options": ["최신순", "등록순", "금액 높은순", "금액 낮은순"] + } + }, + + "comparisonWithDeposit": { + "differences": [ + { + "field": "계정과목/유형 옵션", + "deposit": "매출대금, 선수금, 가수금, 임대수익, 이자수익, 보증금 반환, 차입금, 자본금, 부가세 환급, 기타 (수입 계정)", + "withdrawal": "매입대금, 급여, 임차료, 수도광열비, 통신비, 소모품비, 운반비, 차량유지비, 보험료, 세금과공과, 이자비용, 수수료, 기타 (비용 계정)" + }, + { + "field": "테이블 컬럼명", + "deposit": "입금일, 입금계좌, 입금자명, 입금금액, 입금유형", + "withdrawal": "출금일, 출금계좌, 받는분, 출금금액, 출금유형" + }, + { + "field": "통계 카드", + "deposit": "총 입금, 당월 입금, 입금유형 미설정", + "withdrawal": "총 출금, 당월 출금, 출금유형 미설정" + } + ], + "similarities": [ + "페이지 구조 (목록 → 상세 → 수정)", + "계정과목명 일괄변경 기능", + "거래처 드롭다운 옵션 (공통 데이터)", + "날짜 필터 버튼 (당해년도, 전전월, 전월, 당월, 어제, 오늘)", + "정렬 옵션 (최신순, 등록순, 금액 높은순, 금액 낮은순)", + "은행 데이터 필드 수정 불가 (날짜, 계좌, 금액 등)" + ] + }, + + "assertions": [ + { + "type": "url", + "expected": "/accounting/withdrawals", + "message": "목록 페이지 URL 확인" + }, + { + "type": "text", + "target": "body", + "expected": "출금관리", + "message": "페이지 타이틀 확인" + } + ] +} diff --git a/accounting-bad-debt.json b/accounting-bad-debt.json index 9a14f4c..ed8ee97 100644 --- a/accounting-bad-debt.json +++ b/accounting-bad-debt.json @@ -3,7 +3,14 @@ "name": "악성채권추심관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "회계관리 > 악성채권추심관리 메뉴의 악성채권 CRUD 기능 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -34,11 +41,22 @@ "level2": "악성채권추심관리", "expected": { "url_contains": "/accounting", - "visible": ["악성채권", "추심"] + "visible": [ + "악성채권", + "추심" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/accounting/bad-debt-collection" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -49,7 +67,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "악성채권 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -62,7 +86,13 @@ "expected": "악성채권 테이블 표시" }, { - "id": 4, + "id": 6, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 7, "name": "통계 카드 확인", "action": "verify_elements", "checks": [ @@ -73,7 +103,7 @@ "expected": "통계 정보 표시" }, { - "id": 5, + "id": 8, "phase": "CREATE", "name": "[CREATE] 채권 등록 버튼 클릭", "action": "click_if_exists", @@ -83,7 +113,7 @@ } }, { - "id": 6, + "id": 9, "phase": "CREATE", "name": "[CREATE] 거래처 선택", "action": "click_if_exists", @@ -91,14 +121,14 @@ "expected": "거래처 선택 가능" }, { - "id": 7, + "id": 10, "phase": "CREATE", "name": "[CREATE] 채권금액 입력", "action": "click_if_exists", "target": "input[name*='amount'], input[placeholder*='금액']" }, { - "id": 8, + "id": 11, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 채권 저장", "action": "click_if_exists", @@ -112,14 +142,23 @@ "expected": "채권 등록 완료" }, { - "id": 9, + "id": 12, + "phase": "CREATE", + "name": "[CREATE] 저장 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "등록|완료|성공|저장" + } + }, + { + "id": 13, "phase": "READ", "name": "[READ] 등록된 채권 검색", "action": "click_if_exists", "target": "input[type='search'], input[placeholder*='검색']" }, { - "id": 10, + "id": 14, "phase": "READ", "name": "[READ] 등록된 채권 확인", "action": "verify_detail", @@ -129,7 +168,7 @@ "expected": "등록된 채권 확인" }, { - "id": 11, + "id": 15, "phase": "READ", "name": "[READ] 채권 상세 조회", "action": "click_if_exists", @@ -139,7 +178,7 @@ } }, { - "id": 12, + "id": 16, "name": "상세 정보 확인", "action": "verify_elements", "checks": [ @@ -150,7 +189,7 @@ "expected": "상세 정보 표시" }, { - "id": 13, + "id": 17, "phase": "UPDATE", "name": "[UPDATE] 상태 변경", "action": "click_if_exists", @@ -158,14 +197,14 @@ "expected": "상태 변경 가능" }, { - "id": 14, + "id": 18, "phase": "UPDATE", "name": "[UPDATE] 추심 메모 추가", "action": "click_if_exists", "target": "textarea[name*='memo'], textarea[placeholder*='메모']" }, { - "id": 15, + "id": 19, "phase": "UPDATE", "name": "[UPDATE] 변경 저장", "action": "click_if_exists", @@ -177,7 +216,16 @@ "expected": "채권 정보 수정 완료" }, { - "id": 16, + "id": 20, + "phase": "UPDATE", + "name": "[UPDATE] 수정 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "수정|완료|성공|저장" + } + }, + { + "id": 21, "phase": "DELETE", "name": "[DELETE] 채권 삭제", "action": "click_if_exists", @@ -187,7 +235,7 @@ } }, { - "id": 17, + "id": 22, "phase": "DELETE", "name": "[DELETE] 삭제 확인", "action": "click_if_exists", @@ -199,7 +247,7 @@ "expected": "채권 삭제 완료" }, { - "id": 18, + "id": 23, "phase": "DELETE", "name": "[DELETE] 삭제 확인", "action": "verify_detail", @@ -207,6 +255,12 @@ "E2E_TEST_채권거래처 목록에서 제거" ], "expected": "채권 삭제 반영" + }, + { + "id": 24, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "expectedAPIs": [ @@ -240,13 +294,18 @@ { "id": 2, "name": "저장 버튼", - "steps": [8, 15], + "steps": [ + 8, + 15 + ], "criteria": "API 호출 + 성공 토스트 + 데이터 반영" }, { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "악성채권 목록, 등록 버튼, 상태 필터 존재" } ], diff --git a/accounting-bank-transaction.json b/accounting-bank-transaction.json index fe565a7..b69c274 100644 --- a/accounting-bank-transaction.json +++ b/accounting-bank-transaction.json @@ -3,7 +3,14 @@ "name": "입출금계좌조회 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "회계관리 > 입출금계좌조회 메뉴의 계좌 거래내역 조회/필터/엑셀 기능 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -27,11 +34,22 @@ "level2": "입출금계좌조회", "expected": { "url_contains": "/accounting", - "visible": ["입출금", "계좌"] + "visible": [ + "입출금", + "계좌" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/accounting/bank-transactions" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -42,7 +60,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "거래내역 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -55,7 +79,13 @@ "expected": "거래내역 테이블 표시" }, { - "id": 4, + "id": 6, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 7, "name": "계좌 선택 드롭다운 확인", "action": "verify_elements", "checks": [ @@ -64,7 +94,7 @@ "expected": "계좌 선택 가능" }, { - "id": 5, + "id": 8, "phase": "FILTER", "name": "[FILTER] 기간 필터 - 시작일", "action": "click_if_exists", @@ -72,7 +102,7 @@ "expected": "날짜 선택 열림" }, { - "id": 6, + "id": 9, "phase": "FILTER", "name": "[FILTER] 기간 필터 적용", "action": "click_if_exists", @@ -80,7 +110,7 @@ "expected": "필터 적용됨" }, { - "id": 7, + "id": 10, "phase": "FILTER", "name": "[FILTER] 필터 결과 확인", "action": "verify_detail", @@ -90,7 +120,7 @@ "expected": "필터 동작 확인" }, { - "id": 8, + "id": 11, "phase": "READ", "name": "[READ] 거래 상세 보기", "action": "click_if_exists", @@ -100,7 +130,7 @@ } }, { - "id": 9, + "id": 12, "name": "상세 정보 확인", "action": "verify_detail", "checks": [ @@ -111,14 +141,14 @@ "expected": "거래 상세 정보 표시" }, { - "id": 10, + "id": 13, "name": "목록으로 돌아가기", "action": "click_if_exists", "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", "expected": "목록 페이지로 복귀" }, { - "id": 11, + "id": 14, "name": "입금 합계 확인", "action": "verify_elements", "checks": [ @@ -127,7 +157,7 @@ "expected": "입금 합계 표시" }, { - "id": 12, + "id": 15, "name": "출금 합계 확인", "action": "verify_elements", "checks": [ @@ -136,7 +166,7 @@ "expected": "출금 합계 표시" }, { - "id": 13, + "id": 16, "name": "엑셀 다운로드 버튼 확인", "action": "verify_elements", "checks": [ @@ -145,7 +175,7 @@ "expected": "엑셀 다운로드 기능 표시" }, { - "id": 14, + "id": 17, "name": "인쇄 버튼 확인", "action": "verify_elements", "checks": [ @@ -154,13 +184,19 @@ "expected": "인쇄 기능 표시" }, { - "id": 15, + "id": 18, "name": "페이지네이션 확인", "action": "verify_elements", "checks": [ "페이지 번호 표시" ], "expected": "페이지네이션 표시" + }, + { + "id": 19, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "expectedAPIs": [ @@ -179,13 +215,19 @@ { "id": 3, "name": "검색/필터", - "steps": [5, 6, 7], + "steps": [ + 5, + 6, + 7 + ], "criteria": "기간 필터 동작" }, { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "거래내역 목록, 계좌 선택, 기간 필터 존재" } ], diff --git a/accounting-bill.json b/accounting-bill.json index 6ffc9a6..94ff9e0 100644 --- a/accounting-bill.json +++ b/accounting-bill.json @@ -3,7 +3,14 @@ "name": "어음관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "회계관리 > 어음관리 메뉴의 어음 조회/등록/수정/삭제 전체 CRUD 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -39,11 +46,22 @@ "level2": "어음관리", "expected": { "url_contains": "/accounting/bills", - "visible": ["어음관리", "어음"] + "visible": [ + "어음관리", + "어음" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/accounting/bills" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -54,7 +72,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "어음 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -67,7 +91,13 @@ "expected": "어음 테이블 컬럼 정상 표시" }, { - "id": 4, + "id": 6, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 7, "name": "검색 기능 테스트", "action": "click_if_exists", "target": "input[placeholder*='검색']", @@ -77,7 +107,7 @@ } }, { - "id": 5, + "id": 8, "phase": "CREATE", "name": "[CREATE] 어음 등록 버튼 클릭", "action": "click_if_exists", @@ -88,21 +118,41 @@ } }, { - "id": 6, + "id": 9, "phase": "CREATE", "name": "[CREATE] 어음 정보 입력", "action": "fill_form", "fields": [ - {"name": "어음번호", "type": "text", "value": "E2E_TEST_어음_{timestamp}"}, - {"name": "금액", "type": "number", "value": "1000000"}, - {"name": "만기일", "type": "date", "value": "2026-03-15"}, - {"name": "발행처", "type": "text", "value": "테스트발행처"}, - {"name": "메모", "type": "text", "value": "E2E 자동화 테스트 어음"} + { + "name": "어음번호", + "type": "text", + "value": "E2E_TEST_어음_{timestamp}" + }, + { + "name": "금액", + "type": "number", + "value": "1000000" + }, + { + "name": "만기일", + "type": "date", + "value": "2026-03-15" + }, + { + "name": "발행처", + "type": "text", + "value": "테스트발행처" + }, + { + "name": "메모", + "type": "text", + "value": "E2E 자동화 테스트 어음" + } ], "note": "타임스탬프로 고유성 보장" }, { - "id": 7, + "id": 10, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 등록 저장", "action": "click_if_exists", @@ -116,36 +166,52 @@ "expected": "어음 등록 완료" }, { - "id": "7-modal-close", + "id": 11, + "phase": "CREATE", + "name": "[CREATE] 저장 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "등록|완료|성공|저장" + } + }, + { + "id": 12, "phase": "CREATE", "name": "[CREATE] 모달 닫기 확인", "action": "close_modal_if_open", "expected": "모달 닫힘" }, { - "id": 8, + "id": 13, "phase": "CREATE", "name": "[CREATE] 등록 결과 확인", "action": "verify_detail", "search": "E2E_TEST_어음", "expected": { "row_exists": true, - "contains": ["E2E", "1,000,000"] + "contains": [ + "E2E", + "1,000,000" + ] } }, { - "id": 9, + "id": 14, "phase": "READ", "name": "[READ] 어음 상세 페이지 진입", "action": "click_if_exists", "target": "table tbody tr:has-text('E2E')", "expected": { "url_contains": "/accounting/bills/", - "visible": ["어음 상세", "수정", "삭제"] + "visible": [ + "어음 상세", + "수정", + "삭제" + ] } }, { - "id": 10, + "id": 15, "phase": "READ", "name": "[READ] 상세 정보 확인", "action": "verify_detail", @@ -158,7 +224,7 @@ "expected": "입력한 데이터와 일치" }, { - "id": 11, + "id": 16, "phase": "UPDATE", "name": "[UPDATE] 수정 모드 진입", "action": "click_if_exists", @@ -169,7 +235,7 @@ } }, { - "id": 12, + "id": 17, "phase": "UPDATE", "name": "[UPDATE] 메모 수정", "action": "click_if_exists", @@ -178,7 +244,7 @@ "clear": true }, { - "id": 13, + "id": 18, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 수정 저장", "action": "click_if_exists", @@ -192,7 +258,16 @@ "expected": "수정 완료" }, { - "id": 14, + "id": 19, + "phase": "UPDATE", + "name": "[UPDATE] 수정 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "수정|완료|성공|저장" + } + }, + { + "id": 20, "phase": "UPDATE", "name": "[UPDATE] 수정 결과 확인", "action": "verify_detail", @@ -202,7 +277,7 @@ "expected": "수정된 데이터 반영" }, { - "id": 15, + "id": 21, "phase": "DELETE", "name": "[DELETE] 삭제 버튼 클릭", "action": "click_if_exists", @@ -213,7 +288,7 @@ } }, { - "id": 16, + "id": 22, "phase": "DELETE", "name": "[DELETE] 필수 검증 #6: 삭제 확인", "action": "click_if_exists", @@ -226,7 +301,7 @@ "expected": "삭제 완료 및 목록 복귀" }, { - "id": 17, + "id": 23, "phase": "DELETE", "name": "[DELETE] 삭제 결과 확인", "action": "verify_detail", @@ -235,6 +310,12 @@ "row_exists": false, "message": "테스트 어음이 목록에서 제거됨" } + }, + { + "id": 24, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "expectedAPIs": [ @@ -268,19 +349,28 @@ { "id": 2, "name": "등록/저장 버튼", - "steps": [7, 13], + "steps": [ + 7, + 13 + ], "criteria": "API 호출 + 성공 토스트 + 데이터 반영" }, { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "어음 목록, 등록 버튼, 필터 존재" }, { "id": 6, "name": "삭제 기능", - "steps": [15, 16, 17], + "steps": [ + 15, + 16, + 17 + ], "criteria": "DELETE API + 목록에서 제거" } ], diff --git a/accounting-card-history.json b/accounting-card-history.json index b23b371..60ddaad 100644 --- a/accounting-card-history.json +++ b/accounting-card-history.json @@ -3,7 +3,14 @@ "name": "카드내역조회 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "회계관리 > 카드내역조회 메뉴의 카드 사용내역 조회/필터/엑셀 기능 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -27,11 +34,22 @@ "level2": "카드내역조회", "expected": { "url_contains": "/accounting", - "visible": ["카드", "내역"] + "visible": [ + "카드", + "내역" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/accounting/card-transactions" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -42,7 +60,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "카드내역 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -55,7 +79,13 @@ "expected": "카드내역 테이블 표시" }, { - "id": 4, + "id": 6, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 7, "name": "카드 선택 드롭다운 확인", "action": "verify_elements", "checks": [ @@ -64,7 +94,7 @@ "expected": "카드 선택 가능" }, { - "id": 5, + "id": 8, "phase": "FILTER", "name": "[FILTER] 기간 필터 - 시작일", "action": "click_if_exists", @@ -72,7 +102,7 @@ "expected": "날짜 선택 열림" }, { - "id": 6, + "id": 9, "phase": "FILTER", "name": "[FILTER] 기간 필터 적용", "action": "click_if_exists", @@ -80,7 +110,7 @@ "expected": "필터 적용됨" }, { - "id": 7, + "id": 10, "phase": "FILTER", "name": "[FILTER] 필터 결과 확인", "action": "verify_detail", @@ -90,7 +120,7 @@ "expected": "필터 동작 확인" }, { - "id": 8, + "id": 11, "phase": "READ", "name": "[READ] 카드 사용내역 상세 보기", "action": "click_if_exists", @@ -100,7 +130,7 @@ } }, { - "id": 9, + "id": 12, "name": "상세 정보 확인", "action": "verify_detail", "checks": [ @@ -112,14 +142,14 @@ "expected": "카드 사용 상세 정보 표시" }, { - "id": 10, + "id": 13, "name": "목록으로 돌아가기", "action": "click_if_exists", "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", "expected": "목록 페이지로 복귀" }, { - "id": 11, + "id": 14, "name": "사용금액 합계 확인", "action": "verify_elements", "checks": [ @@ -128,7 +158,7 @@ "expected": "사용금액 합계 표시" }, { - "id": 12, + "id": 15, "name": "카드별 사용 현황 확인", "action": "verify_elements", "checks": [ @@ -137,7 +167,7 @@ "expected": "카드별 현황 표시" }, { - "id": 13, + "id": 16, "name": "엑셀 다운로드 버튼 확인", "action": "verify_elements", "checks": [ @@ -146,7 +176,7 @@ "expected": "엑셀 다운로드 기능 표시" }, { - "id": 14, + "id": 17, "name": "인쇄 버튼 확인", "action": "verify_elements", "checks": [ @@ -155,13 +185,19 @@ "expected": "인쇄 기능 표시" }, { - "id": 15, + "id": 18, "name": "페이지네이션 확인", "action": "verify_elements", "checks": [ "페이지 번호 표시" ], "expected": "페이지네이션 표시" + }, + { + "id": 19, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "expectedAPIs": [ @@ -180,13 +216,19 @@ { "id": 3, "name": "검색/필터", - "steps": [5, 6, 7], + "steps": [ + 5, + 6, + 7 + ], "criteria": "기간 필터 동작" }, { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "카드내역 목록, 카드 선택, 기간 필터 존재" } ], diff --git a/accounting-client.json b/accounting-client.json index cea6d00..dc2b32b 100644 --- a/accounting-client.json +++ b/accounting-client.json @@ -49,6 +49,14 @@ }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/accounting/vendors" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -59,7 +67,7 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, "name": "통계 카드 확인", "action": "verify_elements", "checks": [ @@ -70,7 +78,7 @@ "expected": "통계 카드 표시" }, { - "id": 4, + "id": 5, "name": "거래처 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -83,7 +91,13 @@ "expected": "거래처 테이블 표시" }, { - "id": 5, + "id": 6, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 7, "phase": "CREATE", "name": "[CREATE] 거래처 등록 버튼 클릭", "action": "click_if_exists", @@ -93,7 +107,7 @@ } }, { - "id": 6, + "id": 8, "phase": "CREATE", "name": "[CREATE] 거래처명 입력", "action": "click_if_exists", @@ -102,7 +116,7 @@ "clear": true }, { - "id": 7, + "id": 9, "phase": "CREATE", "name": "[CREATE] 거래처 구분 선택", "action": "click_if_exists", @@ -110,7 +124,7 @@ "expected": "거래처 구분 선택" }, { - "id": 8, + "id": 10, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 거래처 저장", "action": "click_if_exists", @@ -124,7 +138,16 @@ "expected": "거래처 등록 완료" }, { - "id": 9, + "id": 11, + "phase": "CREATE", + "name": "[CREATE] 저장 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "등록|완료|성공|저장" + } + }, + { + "id": 12, "phase": "READ", "name": "[READ] 등록된 거래처 검색", "action": "click_if_exists", @@ -133,7 +156,7 @@ "submit": true }, { - "id": 10, + "id": 13, "phase": "READ", "name": "[READ] 등록된 거래처 목록 확인", "action": "verify_detail", @@ -143,7 +166,7 @@ "expected": "등록된 거래처 확인" }, { - "id": 11, + "id": 14, "phase": "READ", "name": "[READ] 거래처 상세 조회", "action": "click_if_exists", @@ -154,7 +177,7 @@ } }, { - "id": 12, + "id": 15, "name": "상세 페이지 정보 확인", "action": "verify_elements", "checks": [ @@ -165,7 +188,7 @@ "expected": "상세 정보 표시" }, { - "id": 13, + "id": 16, "phase": "UPDATE", "name": "[UPDATE] 수정 모드 진입", "action": "click_if_exists", @@ -175,7 +198,7 @@ } }, { - "id": 14, + "id": 17, "phase": "UPDATE", "name": "[UPDATE] 거래처 정보 수정", "action": "click_if_exists", @@ -184,7 +207,7 @@ "clear": true }, { - "id": 15, + "id": 18, "phase": "UPDATE", "name": "[UPDATE] 거래처 저장", "action": "click_if_exists", @@ -196,7 +219,16 @@ "expected": "거래처 수정 완료" }, { - "id": 16, + "id": 19, + "phase": "UPDATE", + "name": "[UPDATE] 수정 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "수정|완료|성공|저장" + } + }, + { + "id": 20, "phase": "DELETE", "name": "[DELETE] 거래처 삭제", "action": "click_if_exists", @@ -206,7 +238,7 @@ } }, { - "id": 17, + "id": 21, "phase": "DELETE", "name": "[DELETE] 삭제 확인", "action": "click_if_exists", @@ -218,7 +250,7 @@ "expected": "거래처 삭제 완료" }, { - "id": 18, + "id": 22, "phase": "DELETE", "name": "[DELETE] 삭제 확인", "action": "verify_detail", @@ -226,6 +258,12 @@ "E2E_TEST_회계거래처 목록에서 제거" ], "expected": "거래처 삭제 반영" + }, + { + "id": 23, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "expectedAPIs": [ @@ -280,4 +318,4 @@ "onDeleteFail": "수동 삭제 필요", "cleanupRequired": "E2E_TEST_회계거래처* 패턴 데이터 삭제" } -} \ No newline at end of file +} diff --git a/accounting-deposit.json b/accounting-deposit.json index b8e6ccf..2645900 100644 --- a/accounting-deposit.json +++ b/accounting-deposit.json @@ -3,7 +3,14 @@ "name": "입금관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "회계관리 > 입금관리 메뉴의 입금 조회/등록/수정/삭제 전체 CRUD 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -39,11 +46,22 @@ "level2": "입금관리", "expected": { "url_contains": "/accounting/deposits", - "visible": ["입금관리", "입금"] + "visible": [ + "입금관리", + "입금" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/accounting/deposits" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -54,7 +72,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "입금 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -67,7 +91,13 @@ "expected": "입금 테이블 컬럼 정상 표시" }, { - "id": 4, + "id": 6, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 7, "name": "검색 기능 테스트", "action": "click_if_exists", "target": "input[placeholder*='검색']", @@ -77,7 +107,7 @@ } }, { - "id": 5, + "id": 8, "phase": "CREATE", "name": "[CREATE] 입금 등록 버튼 클릭", "action": "click_if_exists", @@ -88,21 +118,41 @@ } }, { - "id": 6, + "id": 9, "phase": "CREATE", "name": "[CREATE] 입금 정보 입력", "action": "fill_form", "fields": [ - {"name": "거래처", "type": "select", "value": "E2E_TEST_입금거래처"}, - {"name": "입금일", "type": "date", "value": "2026-02-03"}, - {"name": "금액", "type": "number", "value": "100000"}, - {"name": "입금방법", "type": "select", "value": "계좌이체"}, - {"name": "메모", "type": "text", "value": "E2E 자동화 테스트 입금_{timestamp}"} + { + "name": "거래처", + "type": "select", + "value": "E2E_TEST_입금거래처" + }, + { + "name": "입금일", + "type": "date", + "value": "2026-02-03" + }, + { + "name": "금액", + "type": "number", + "value": "100000" + }, + { + "name": "입금방법", + "type": "select", + "value": "계좌이체" + }, + { + "name": "메모", + "type": "text", + "value": "E2E 자동화 테스트 입금_{timestamp}" + } ], "note": "타임스탬프로 고유성 보장" }, { - "id": 7, + "id": 10, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 등록 저장", "action": "click_if_exists", @@ -116,36 +166,52 @@ "expected": "입금 등록 완료" }, { - "id": "7-modal-close", + "id": 11, + "phase": "CREATE", + "name": "[CREATE] 저장 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "등록|완료|성공|저장" + } + }, + { + "id": 12, "phase": "CREATE", "name": "[CREATE] 모달 닫기 확인", "action": "close_modal_if_open", "expected": "모달 닫힘" }, { - "id": 8, + "id": 13, "phase": "CREATE", "name": "[CREATE] 등록 결과 확인", "action": "verify_detail", "search": "E2E 자동화 테스트 입금", "expected": { "row_exists": true, - "contains": ["E2E", "100,000"] + "contains": [ + "E2E", + "100,000" + ] } }, { - "id": 9, + "id": 14, "phase": "READ", "name": "[READ] 입금 상세 페이지 진입", "action": "click_if_exists", "target": "table tbody tr:has-text('E2E')", "expected": { "url_contains": "/accounting/deposits/", - "visible": ["입금 상세", "수정", "삭제"] + "visible": [ + "입금 상세", + "수정", + "삭제" + ] } }, { - "id": 10, + "id": 15, "phase": "READ", "name": "[READ] 상세 정보 확인", "action": "verify_detail", @@ -157,7 +223,7 @@ "expected": "입력한 데이터와 일치" }, { - "id": 11, + "id": 16, "phase": "UPDATE", "name": "[UPDATE] 수정 모드 진입", "action": "click_if_exists", @@ -168,7 +234,7 @@ } }, { - "id": 12, + "id": 17, "phase": "UPDATE", "name": "[UPDATE] 금액 수정", "action": "click_if_exists", @@ -177,7 +243,7 @@ "clear": true }, { - "id": 13, + "id": 18, "phase": "UPDATE", "name": "[UPDATE] 메모 수정", "action": "click_if_exists", @@ -186,7 +252,7 @@ "clear": true }, { - "id": 14, + "id": 19, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 수정 저장", "action": "click_if_exists", @@ -200,7 +266,16 @@ "expected": "수정 완료" }, { - "id": 15, + "id": 20, + "phase": "UPDATE", + "name": "[UPDATE] 수정 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "수정|완료|성공|저장" + } + }, + { + "id": 21, "phase": "UPDATE", "name": "[UPDATE] 수정 결과 확인", "action": "verify_detail", @@ -211,7 +286,7 @@ "expected": "수정된 데이터 반영" }, { - "id": 16, + "id": 22, "phase": "DELETE", "name": "[DELETE] 삭제 버튼 클릭", "action": "click_if_exists", @@ -222,7 +297,7 @@ } }, { - "id": 17, + "id": 23, "phase": "DELETE", "name": "[DELETE] 필수 검증 #6: 삭제 확인", "action": "click_if_exists", @@ -235,7 +310,7 @@ "expected": "삭제 완료 및 목록 복귀" }, { - "id": 18, + "id": 24, "phase": "DELETE", "name": "[DELETE] 삭제 결과 확인", "action": "verify_detail", @@ -244,6 +319,12 @@ "row_exists": false, "message": "테스트 데이터가 목록에서 제거됨" } + }, + { + "id": 25, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "expectedAPIs": [ @@ -277,19 +358,28 @@ { "id": 2, "name": "등록/저장 버튼", - "steps": [7, 14], + "steps": [ + 7, + 14 + ], "criteria": "API 호출 + 성공 토스트 + 데이터 반영" }, { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "입금 목록, 등록 버튼, 필터 존재" }, { "id": 6, "name": "삭제 기능", - "steps": [16, 17, 18], + "steps": [ + 16, + 17, + 18 + ], "criteria": "DELETE API + 목록에서 제거" } ], diff --git a/accounting-expense-forecast.json b/accounting-expense-forecast.json index ae963b9..a979165 100644 --- a/accounting-expense-forecast.json +++ b/accounting-expense-forecast.json @@ -3,7 +3,14 @@ "name": "지출예상내역서 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "회계관리 > 지출예상내역서 메뉴의 지출 예상 조회/필터/인쇄 기능 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -27,11 +34,22 @@ "level2": "지출예상내역서", "expected": { "url_contains": "/accounting", - "visible": ["지출", "예상"] + "visible": [ + "지출", + "예상" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/accounting/expected-expenses" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -42,7 +60,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "지출예상 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -54,7 +78,13 @@ "expected": "지출예상 테이블 표시" }, { - "id": 4, + "id": 6, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 7, "name": "기간 선택 확인", "action": "verify_elements", "checks": [ @@ -63,7 +93,7 @@ "expected": "기간 선택 가능" }, { - "id": 5, + "id": 8, "phase": "FILTER", "name": "[FILTER] 월 선택", "action": "click_if_exists", @@ -71,7 +101,7 @@ "expected": "월 선택 열림" }, { - "id": 6, + "id": 9, "phase": "FILTER", "name": "[FILTER] 조회 적용", "action": "click_if_exists", @@ -79,7 +109,7 @@ "expected": "필터 적용됨" }, { - "id": 7, + "id": 10, "phase": "FILTER", "name": "[FILTER] 필터 결과 확인", "action": "verify_detail", @@ -89,7 +119,7 @@ "expected": "필터 동작 확인" }, { - "id": 8, + "id": 11, "name": "지출 카테고리별 확인", "action": "verify_elements", "checks": [ @@ -98,7 +128,7 @@ "expected": "카테고리별 지출 표시" }, { - "id": 9, + "id": 12, "name": "합계 금액 확인", "action": "verify_detail", "checks": [ @@ -107,7 +137,7 @@ "expected": "합계 금액 표시" }, { - "id": 10, + "id": 13, "name": "일별 지출 예상 확인", "action": "verify_elements", "checks": [ @@ -116,7 +146,7 @@ "expected": "일별 지출 표시" }, { - "id": 11, + "id": 14, "name": "주요 지출 항목 확인", "action": "verify_elements", "checks": [ @@ -125,7 +155,7 @@ "expected": "주요 항목 표시" }, { - "id": 12, + "id": 15, "name": "인쇄 버튼 확인", "action": "verify_elements", "checks": [ @@ -134,7 +164,7 @@ "expected": "인쇄 기능 표시" }, { - "id": 13, + "id": 16, "name": "엑셀 다운로드 버튼 확인", "action": "verify_elements", "checks": [ @@ -143,7 +173,7 @@ "expected": "엑셀 다운로드 기능 표시" }, { - "id": 14, + "id": 17, "name": "PDF 내보내기 확인", "action": "verify_elements", "checks": [ @@ -152,13 +182,19 @@ "expected": "PDF 내보내기 기능 표시" }, { - "id": 15, + "id": 18, "name": "이전/다음 기간 네비게이션", "action": "verify_elements", "checks": [ "이전/다음 기간 이동 가능" ], "expected": "기간 네비게이션 표시" + }, + { + "id": 19, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "expectedAPIs": [ @@ -177,13 +213,19 @@ { "id": 3, "name": "검색/필터", - "steps": [5, 6, 7], + "steps": [ + 5, + 6, + 7 + ], "criteria": "기간 필터 동작" }, { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "지출예상 목록, 기간 선택, 합계 표시 존재" } ], diff --git a/accounting-payment.json b/accounting-payment.json index 39b5b1c..ee01c04 100644 --- a/accounting-payment.json +++ b/accounting-payment.json @@ -3,7 +3,14 @@ "name": "결제내역 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "회계관리 > 결제내역 메뉴의 결제 내역 조회/필터/검색/다운로드 기능 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -27,11 +34,22 @@ "level2": "결제내역", "expected": { "url_contains": "/payment", - "visible": ["결제내역", "결제"] + "visible": [ + "결제내역", + "결제" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/payment-history" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -42,7 +60,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "결제내역 페이지 구조 확인", "action": "verify_elements", "checks": [ @@ -54,7 +78,7 @@ "expected": "결제내역 조회 폼 정상 표시" }, { - "id": 4, + "id": 6, "phase": "FILTER", "name": "[FILTER] 기간 필터 - 시작일", "action": "click_if_exists", @@ -63,7 +87,7 @@ "expected": "시작일 입력" }, { - "id": 5, + "id": 7, "phase": "FILTER", "name": "[FILTER] 기간 필터 - 종료일", "action": "click_if_exists", @@ -72,7 +96,7 @@ "expected": "종료일 입력" }, { - "id": 6, + "id": 8, "phase": "FILTER", "name": "[FILTER] 조회 버튼 클릭", "action": "click_if_exists", @@ -83,7 +107,7 @@ } }, { - "id": 7, + "id": 9, "phase": "READ", "name": "[READ] 결제 테이블 구조 확인", "action": "verify_table", @@ -96,7 +120,13 @@ "expected": "결제 테이블 정상 표시" }, { - "id": 8, + "id": 10, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 11, "phase": "READ", "name": "[READ] 결제 데이터 표시 확인", "action": "verify_detail", @@ -107,7 +137,7 @@ "expected": "결제 데이터 정상 표시" }, { - "id": 9, + "id": 12, "phase": "FILTER", "name": "[FILTER] 결제방법 필터 테스트", "action": "click_if_exists", @@ -115,7 +145,7 @@ "expected": "결제방법 필터 옵션 표시" }, { - "id": 10, + "id": 13, "phase": "FILTER", "name": "[FILTER] 상태 필터 테스트", "action": "verify_elements", @@ -125,7 +155,7 @@ "expected": "상태 필터 기능 확인" }, { - "id": 11, + "id": 14, "phase": "READ", "name": "[READ] 결제 상세 조회", "action": "click_if_exists", @@ -135,7 +165,7 @@ } }, { - "id": 12, + "id": 15, "name": "결제 상세 정보 확인", "action": "verify_detail", "checks": [ @@ -147,14 +177,14 @@ "expected": "결제 상세 정보 표시" }, { - "id": 13, + "id": 16, "name": "목록으로 돌아가기", "action": "click_if_exists", "target": "button:has-text('목록'), button:has-text('뒤로'), [class*='back']", "expected": "목록 페이지로 복귀" }, { - "id": 14, + "id": 17, "name": "필수 검증 #1: 엑셀 다운로드", "action": "click_if_exists", "target": "button:has-text('엑셀'), button:has-text('Excel'), button:has-text('다운로드')", @@ -165,13 +195,19 @@ "expected": "엑셀 파일 다운로드" }, { - "id": 15, + "id": 18, "name": "합계 금액 표시 확인", "action": "verify_elements", "checks": [ "총 결제금액 합계 표시" ], "expected": "합계 영역 표시" + }, + { + "id": 19, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "expectedAPIs": [ @@ -195,13 +231,17 @@ { "id": 1, "name": "엑셀 다운로드", - "steps": [14], + "steps": [ + 14 + ], "criteria": "API 호출 + 파일 다운로드" }, { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "결제 내역 목록, 기간 필터, 검색 기능 존재" } ], diff --git a/accounting-purchase.json b/accounting-purchase.json index c6ec3b7..bffc99a 100644 --- a/accounting-purchase.json +++ b/accounting-purchase.json @@ -3,7 +3,14 @@ "name": "매입관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "회계관리 > 매입관리 메뉴의 매입 조회/필터/엑셀 기능 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -27,7 +34,10 @@ "level2": "매입관리", "expected": { "url_contains": "/accounting", - "visible": ["매입관리", "매입"] + "visible": [ + "매입관리", + "매입" + ] } }, { @@ -43,6 +53,12 @@ }, { "id": 3, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 4, "name": "매입 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -55,7 +71,13 @@ "expected": "매입 테이블 표시" }, { - "id": 4, + "id": 5, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 6, "name": "매입 통계 카드 확인", "action": "verify_elements", "checks": [ @@ -66,7 +88,7 @@ "expected": "통계 카드 표시" }, { - "id": 5, + "id": 7, "phase": "FILTER", "name": "[FILTER] 기간 필터 - 시작일", "action": "click_if_exists", @@ -74,7 +96,7 @@ "expected": "날짜 선택 열림" }, { - "id": 6, + "id": 8, "phase": "FILTER", "name": "[FILTER] 기간 필터 적용", "action": "click_if_exists", @@ -82,7 +104,7 @@ "expected": "필터 적용됨" }, { - "id": 7, + "id": 9, "phase": "FILTER", "name": "[FILTER] 필터 결과 확인", "action": "verify_detail", @@ -92,7 +114,7 @@ "expected": "필터 동작 확인" }, { - "id": 8, + "id": 10, "phase": "FILTER", "name": "[FILTER] 거래처별 필터", "action": "click_if_exists", @@ -100,7 +122,7 @@ "expected": "거래처 필터 가능" }, { - "id": 9, + "id": 11, "phase": "READ", "name": "[READ] 매입 상세 보기", "action": "click_if_exists", @@ -110,7 +132,7 @@ } }, { - "id": 10, + "id": 12, "name": "상세 정보 확인", "action": "verify_detail", "checks": [ @@ -121,14 +143,14 @@ "expected": "매입 상세 정보 표시" }, { - "id": 11, + "id": 13, "name": "목록으로 돌아가기", "action": "click_if_exists", "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", "expected": "목록 페이지로 복귀" }, { - "id": 12, + "id": 14, "name": "매입 합계 확인", "action": "verify_elements", "checks": [ @@ -137,7 +159,7 @@ "expected": "매입 합계 표시" }, { - "id": 13, + "id": 15, "name": "엑셀 다운로드 버튼 확인", "action": "verify_elements", "checks": [ @@ -146,7 +168,7 @@ "expected": "엑셀 다운로드 기능 표시" }, { - "id": 14, + "id": 16, "name": "인쇄 버튼 확인", "action": "verify_elements", "checks": [ @@ -155,13 +177,19 @@ "expected": "인쇄 기능 표시" }, { - "id": 15, + "id": 17, "name": "페이지네이션 확인", "action": "verify_elements", "checks": [ "페이지 번호 표시" ], "expected": "페이지네이션 표시" + }, + { + "id": 18, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "expectedAPIs": [ @@ -180,13 +208,20 @@ { "id": 3, "name": "검색/필터", - "steps": [5, 6, 7, 8], + "steps": [ + 5, + 6, + 7, + 8 + ], "criteria": "기간 필터 + 거래처 필터 동작" }, { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "매입 목록, 기간 필터, 합계 표시 존재" } ], diff --git a/accounting-receivable.json b/accounting-receivable.json index d362643..bb1bef1 100644 --- a/accounting-receivable.json +++ b/accounting-receivable.json @@ -3,7 +3,14 @@ "name": "미수금현황 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "회계관리 > 미수금현황 메뉴의 미수금 조회/필터/검색/엑셀 다운로드 기능 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -32,11 +39,22 @@ "level2": "미수금현황", "expected": { "url_contains": "/accounting/receivables", - "visible": ["미수금현황", "미수금"] + "visible": [ + "미수금현황", + "미수금" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/accounting/receivables-status" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -47,7 +65,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "미수금 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -60,7 +84,13 @@ "expected": "미수금 테이블 컬럼 정상 표시" }, { - "id": 4, + "id": 6, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 7, "name": "통계 카드 확인", "action": "verify_elements", "checks": [ @@ -71,20 +101,29 @@ "expected": "미수금 통계 카드 표시" }, { - "id": 5, + "id": 8, "phase": "FILTER", "name": "[FILTER] 기간 필터 적용", "actions": [ - { "type": "click_if_exists", "target": "input[type='date']:first-of-type, input[placeholder*='시작'], input[name*='start']" }, - { "type": "click_if_exists", "target": "input[type='date']:last-of-type, input[placeholder*='종료'], input[name*='end']" }, - { "type": "wait", "duration": 500 } + { + "type": "click_if_exists", + "target": "input[type='date']:first-of-type, input[placeholder*='시작'], input[name*='start']" + }, + { + "type": "click_if_exists", + "target": "input[type='date']:last-of-type, input[placeholder*='종료'], input[name*='end']" + }, + { + "type": "wait", + "duration": 500 + } ], "expected": { "filter_applied": true } }, { - "id": 6, + "id": 9, "phase": "FILTER", "name": "[FILTER] 필터 결과 확인", "action": "verify_detail", @@ -94,7 +133,7 @@ } }, { - "id": 7, + "id": 10, "phase": "SEARCH", "name": "[SEARCH] 거래처 검색", "action": "click_if_exists", @@ -105,7 +144,7 @@ } }, { - "id": 8, + "id": 11, "phase": "SEARCH", "name": "[SEARCH] 검색 결과 확인", "action": "verify_detail", @@ -116,18 +155,22 @@ } }, { - "id": 9, + "id": 12, "phase": "READ", "name": "[READ] 미수금 상세 클릭", "action": "click_if_exists", "target": "table tbody tr:first-child", "expected": { "detail_modal_or_page": true, - "visible": ["거래처", "미수금액", "상세"] + "visible": [ + "거래처", + "미수금액", + "상세" + ] } }, { - "id": 10, + "id": 13, "phase": "READ", "name": "[READ] 상세 정보 확인", "action": "verify_detail", @@ -139,7 +182,7 @@ "expected": "미수금 상세 정보 정상 표시" }, { - "id": 11, + "id": 14, "phase": "READ", "name": "[READ] 목록으로 복귀", "action": "click_if_exists", @@ -149,7 +192,7 @@ } }, { - "id": 12, + "id": 15, "phase": "EXPORT", "name": "[EXPORT] 엑셀 다운로드 버튼 확인", "action": "verify_elements", @@ -159,7 +202,7 @@ "expected": "다운로드 기능 존재" }, { - "id": 13, + "id": 16, "phase": "EXPORT", "name": "[EXPORT] 필수 검증 #1: 엑셀 다운로드", "action": "click_if_exists", @@ -172,7 +215,7 @@ "expected": "엑셀 파일 다운로드" }, { - "id": 14, + "id": 17, "phase": "SORT", "name": "[SORT] 컬럼 정렬 테스트", "action": "click_if_exists", @@ -183,7 +226,7 @@ } }, { - "id": 15, + "id": 18, "name": "연체 현황 탭 확인", "action": "click_if_exists", "target": "button:has-text('연체'), [role='tab']:has-text('연체')", @@ -191,6 +234,12 @@ "tab_active": true, "filtered_data": true } + }, + { + "id": 19, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "expectedAPIs": [ @@ -219,19 +268,28 @@ { "id": 1, "name": "파일 다운로드", - "steps": [13], + "steps": [ + 13 + ], "criteria": "엑셀 파일 다운로드 동작" }, { "id": 3, "name": "검색/필터", - "steps": [5, 6, 7, 8], + "steps": [ + 5, + 6, + 7, + 8 + ], "criteria": "기간 필터 및 검색 기능" }, { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "미수금 목록, 필터, 다운로드 버튼 존재" } ], diff --git a/accounting-sales.json b/accounting-sales.json index a8116a2..5261b42 100644 --- a/accounting-sales.json +++ b/accounting-sales.json @@ -3,7 +3,14 @@ "name": "매출관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "회계관리 > 매출관리 메뉴의 매출 조회/필터/엑셀 기능 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -27,7 +34,10 @@ "level2": "매출관리", "expected": { "url_contains": "/accounting", - "visible": ["매출관리", "매출"] + "visible": [ + "매출관리", + "매출" + ] } }, { @@ -43,6 +53,12 @@ }, { "id": 3, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 4, "name": "매출 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -55,7 +71,13 @@ "expected": "매출 테이블 표시" }, { - "id": 4, + "id": 5, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 6, "name": "매출 통계 카드 확인", "action": "verify_elements", "checks": [ @@ -66,7 +88,7 @@ "expected": "통계 카드 표시" }, { - "id": 5, + "id": 7, "phase": "FILTER", "name": "[FILTER] 기간 필터 - 시작일", "action": "click_if_exists", @@ -74,7 +96,7 @@ "expected": "날짜 선택 열림" }, { - "id": 6, + "id": 8, "phase": "FILTER", "name": "[FILTER] 기간 필터 적용", "action": "click_if_exists", @@ -82,7 +104,7 @@ "expected": "필터 적용됨" }, { - "id": 7, + "id": 9, "phase": "FILTER", "name": "[FILTER] 필터 결과 확인", "action": "verify_detail", @@ -92,7 +114,7 @@ "expected": "필터 동작 확인" }, { - "id": 8, + "id": 10, "phase": "FILTER", "name": "[FILTER] 거래처별 필터", "action": "click_if_exists", @@ -100,7 +122,7 @@ "expected": "거래처 필터 가능" }, { - "id": 9, + "id": 11, "phase": "READ", "name": "[READ] 매출 상세 보기", "action": "click_if_exists", @@ -110,7 +132,7 @@ } }, { - "id": 10, + "id": 12, "name": "상세 정보 확인", "action": "verify_detail", "checks": [ @@ -121,14 +143,14 @@ "expected": "매출 상세 정보 표시" }, { - "id": 11, + "id": 13, "name": "목록으로 돌아가기", "action": "click_if_exists", "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", "expected": "목록 페이지로 복귀" }, { - "id": 12, + "id": 14, "name": "매출 합계 확인", "action": "verify_elements", "checks": [ @@ -137,7 +159,7 @@ "expected": "매출 합계 표시" }, { - "id": 13, + "id": 15, "name": "엑셀 다운로드 버튼 확인", "action": "verify_elements", "checks": [ @@ -146,7 +168,7 @@ "expected": "엑셀 다운로드 기능 표시" }, { - "id": 14, + "id": 16, "name": "인쇄 버튼 확인", "action": "verify_elements", "checks": [ @@ -155,13 +177,19 @@ "expected": "인쇄 기능 표시" }, { - "id": 15, + "id": 17, "name": "페이지네이션 확인", "action": "verify_elements", "checks": [ "페이지 번호 표시" ], "expected": "페이지네이션 표시" + }, + { + "id": 18, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "expectedAPIs": [ @@ -180,13 +208,20 @@ { "id": 3, "name": "검색/필터", - "steps": [5, 6, 7, 8], + "steps": [ + 5, + 6, + 7, + 8 + ], "criteria": "기간 필터 + 거래처 필터 동작" }, { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "매출 목록, 기간 필터, 합계 표시 존재" } ], diff --git a/accounting-withdrawal.json b/accounting-withdrawal.json index f3988c6..c220a61 100644 --- a/accounting-withdrawal.json +++ b/accounting-withdrawal.json @@ -3,7 +3,14 @@ "name": "출금관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "회계관리 > 출금관리 메뉴의 출금 조회/등록/수정/삭제 전체 CRUD 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -39,11 +46,22 @@ "level2": "출금관리", "expected": { "url_contains": "/accounting/withdrawals", - "visible": ["출금관리", "출금"] + "visible": [ + "출금관리", + "출금" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/accounting/withdrawals" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -54,7 +72,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "출금 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -67,7 +91,13 @@ "expected": "출금 테이블 컬럼 정상 표시" }, { - "id": 4, + "id": 6, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 7, "name": "검색 기능 테스트", "action": "click_if_exists", "target": "input[placeholder*='검색']", @@ -77,7 +107,7 @@ } }, { - "id": 5, + "id": 8, "phase": "CREATE", "name": "[CREATE] 출금 등록 버튼 클릭", "action": "click_if_exists", @@ -88,21 +118,41 @@ } }, { - "id": 6, + "id": 9, "phase": "CREATE", "name": "[CREATE] 출금 정보 입력", "action": "fill_form", "fields": [ - {"name": "거래처", "type": "select", "value": "E2E_TEST_출금거래처"}, - {"name": "출금일", "type": "date", "value": "2026-02-03"}, - {"name": "금액", "type": "number", "value": "50000"}, - {"name": "출금방법", "type": "select", "value": "계좌이체"}, - {"name": "메모", "type": "text", "value": "E2E 자동화 테스트 출금_{timestamp}"} + { + "name": "거래처", + "type": "select", + "value": "E2E_TEST_출금거래처" + }, + { + "name": "출금일", + "type": "date", + "value": "2026-02-03" + }, + { + "name": "금액", + "type": "number", + "value": "50000" + }, + { + "name": "출금방법", + "type": "select", + "value": "계좌이체" + }, + { + "name": "메모", + "type": "text", + "value": "E2E 자동화 테스트 출금_{timestamp}" + } ], "note": "타임스탬프로 고유성 보장" }, { - "id": 7, + "id": 10, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 등록 저장", "action": "click_if_exists", @@ -116,36 +166,52 @@ "expected": "출금 등록 완료" }, { - "id": "7-modal-close", + "id": 11, + "phase": "CREATE", + "name": "[CREATE] 저장 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "등록|완료|성공|저장" + } + }, + { + "id": 12, "phase": "CREATE", "name": "[CREATE] 모달 닫기 확인", "action": "close_modal_if_open", "expected": "모달 닫힘" }, { - "id": 8, + "id": 13, "phase": "CREATE", "name": "[CREATE] 등록 결과 확인", "action": "verify_detail", "search": "E2E 자동화 테스트 출금", "expected": { "row_exists": true, - "contains": ["E2E", "50,000"] + "contains": [ + "E2E", + "50,000" + ] } }, { - "id": 9, + "id": 14, "phase": "READ", "name": "[READ] 출금 상세 페이지 진입", "action": "click_if_exists", "target": "table tbody tr:has-text('E2E')", "expected": { "url_contains": "/accounting/withdrawals/", - "visible": ["출금 상세", "수정", "삭제"] + "visible": [ + "출금 상세", + "수정", + "삭제" + ] } }, { - "id": 10, + "id": 15, "phase": "READ", "name": "[READ] 상세 정보 확인", "action": "verify_detail", @@ -157,7 +223,7 @@ "expected": "입력한 데이터와 일치" }, { - "id": 11, + "id": 16, "phase": "UPDATE", "name": "[UPDATE] 수정 모드 진입", "action": "click_if_exists", @@ -168,7 +234,7 @@ } }, { - "id": 12, + "id": 17, "phase": "UPDATE", "name": "[UPDATE] 금액 수정", "action": "click_if_exists", @@ -177,7 +243,7 @@ "clear": true }, { - "id": 13, + "id": 18, "phase": "UPDATE", "name": "[UPDATE] 메모 수정", "action": "click_if_exists", @@ -186,7 +252,7 @@ "clear": true }, { - "id": 14, + "id": 19, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 수정 저장", "action": "click_if_exists", @@ -200,7 +266,16 @@ "expected": "수정 완료" }, { - "id": 15, + "id": 20, + "phase": "UPDATE", + "name": "[UPDATE] 수정 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "수정|완료|성공|저장" + } + }, + { + "id": 21, "phase": "UPDATE", "name": "[UPDATE] 수정 결과 확인", "action": "verify_detail", @@ -211,7 +286,7 @@ "expected": "수정된 데이터 반영" }, { - "id": 16, + "id": 22, "phase": "DELETE", "name": "[DELETE] 삭제 버튼 클릭", "action": "click_if_exists", @@ -222,7 +297,7 @@ } }, { - "id": 17, + "id": 23, "phase": "DELETE", "name": "[DELETE] 필수 검증 #6: 삭제 확인", "action": "click_if_exists", @@ -235,7 +310,7 @@ "expected": "삭제 완료 및 목록 복귀" }, { - "id": 18, + "id": 24, "phase": "DELETE", "name": "[DELETE] 삭제 결과 확인", "action": "verify_detail", @@ -244,6 +319,12 @@ "row_exists": false, "message": "테스트 데이터가 목록에서 제거됨" } + }, + { + "id": 25, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "expectedAPIs": [ @@ -277,19 +358,28 @@ { "id": 2, "name": "등록/저장 버튼", - "steps": [7, 14], + "steps": [ + 7, + 14 + ], "criteria": "API 호출 + 성공 토스트 + 데이터 반영" }, { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "출금 목록, 등록 버튼, 필터 존재" }, { "id": 6, "name": "삭제 기능", - "steps": [16, 17, 18], + "steps": [ + 16, + 17, + 18 + ], "criteria": "DELETE API + 목록에서 제거" } ], diff --git a/approval-box.json b/approval-box.json index 0d58d12..a289863 100644 --- a/approval-box.json +++ b/approval-box.json @@ -63,7 +63,7 @@ }, "steps": [ { - "id": 0, + "id": 1, "name": "사이드바 메뉴 전체 펼치기", "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", "actions": [ @@ -92,7 +92,7 @@ ] }, { - "id": 1, + "id": 2, "name": "1차 메뉴 찾기: 결재관리 (스크롤 포함)", "description": "사이드바를 스크롤하며 '결재관리' 메뉴를 찾아 클릭", "actions": [ @@ -139,7 +139,7 @@ } }, { - "id": 2, + "id": 3, "name": "2차 메뉴 찾기: 결재함 (스크롤 포함)", "description": "서브메뉴에서 '결재함'을 찾아 클릭", "actions": [ @@ -181,7 +181,7 @@ ] }, { - "id": 3, + "id": 4, "name": "404 에러 감지 및 대체 경로 시도", "description": "페이지 로드 후 404 에러 여부 확인, 404시 대체 경로 탐색", "actions": [ @@ -231,7 +231,7 @@ } }, { - "id": 4, + "id": 5, "name": "페이지 정상 로드 확인", "description": "결재함 페이지가 정상적으로 로드되었는지 확인", "actions": [ @@ -269,7 +269,7 @@ } }, { - "id": 5, + "id": 6, "name": "통계 카드 확인", "action": "verify_element", "target": "[class*='card'], [class*='stat']", @@ -281,7 +281,7 @@ ] }, { - "id": 6, + "id": 7, "name": "탭 구조 확인", "action": "verify_element", "target": "[role='tab'], button[role='tab']", @@ -293,7 +293,7 @@ ] }, { - "id": 7, + "id": 8, "name": "테이블 데이터 확인", "action": "verify_table", "target": "table", @@ -304,7 +304,13 @@ ] }, { - "id": 8, + "id": 9, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 10, "name": "⚠️ 필수 검증: 결재 문서 상세 보기", "description": "테이블에서 결재 문서 클릭하여 상세 모달/페이지 확인", "actions": [ @@ -345,7 +351,7 @@ "note": "결재 문서가 없으면 데이터 생성 또는 SKIP" }, { - "id": "8-pdf-1", + "id": 11, "name": "⚠️ 필수 검증: PDF 다운로드 전 모달 스크린샷", "description": "PDF 생성 전 모달 상태를 스크린샷으로 캡처하여 CSS 문제 감지용 기준 이미지 확보", "prerequisite": "step-8의 문서 상세 모달이 열려있는 상태에서 실행", @@ -365,7 +371,7 @@ } }, { - "id": "8-pdf-2", + "id": 12, "name": "⚠️ 필수 검증: PDF 다운로드 실행 및 파일 보관", "description": "PDF 다운로드 후 파일을 지정 폴더에 보관하여 수동 검증 가능하게 함", "actions": [ @@ -414,7 +420,7 @@ } }, { - "id": "8-pdf-3", + "id": 13, "name": "⚠️ PDF 파일 유효성 검증", "description": "다운로드된 PDF 파일의 기본 유효성 검사", "actions": [ @@ -434,7 +440,7 @@ } }, { - "id": "8-pdf-4", + "id": 14, "name": "📋 PDF 스타일 수동 확인 체크리스트", "type": "manualVerification", "description": "개발자가 다운로드된 PDF를 열어 시각적으로 확인해야 하는 항목", @@ -500,7 +506,7 @@ } }, { - "id": 9, + "id": 15, "name": "⚠️ 필수 검증 #4: 결재 승인 실제 수행", "description": "미결재 문서에 대해 실제 승인 처리 수행", "actions": [ @@ -533,7 +539,7 @@ "note": "⚠️ 버튼 존재만 확인하면 불완전! 실제 승인까지 검증 필수!" }, { - "id": "9-1", + "id": 16, "name": "결재 승인 결과 확인", "description": "승인 후 결재완료 탭에서 해당 문서 확인", "actions": [ @@ -552,7 +558,7 @@ } }, { - "id": 10, + "id": 17, "name": "⚠️ 필수 검증 #4: 결재 반려 실제 수행", "description": "미결재 문서에 대해 실제 반려 처리 수행", "actions": [ @@ -603,7 +609,7 @@ "note": "⚠️ 반려 버튼 존재만 확인하면 불완전! 실제 반려까지 검증 필수!" }, { - "id": "10-1", + "id": 18, "name": "결재 반려 결과 확인", "description": "반려 후 결재반려 탭에서 해당 문서 확인", "actions": [ @@ -623,7 +629,7 @@ } }, { - "id": 11, + "id": 19, "name": "검색 기능 테스트", "description": "검색 필터로 결재 문서 검색", "actions": [ @@ -644,6 +650,12 @@ "searchApplied": true, "filteredResults": "검색어에 맞는 결과 표시" } + }, + { + "id": 20, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "mandatoryVerifications": { @@ -676,4 +688,4 @@ "메뉴 계층: 결재관리 > 결재함", "탭 전환 시 URL 변경 없이 데이터만 필터링됨" ] -} \ No newline at end of file +} diff --git a/attendance-checkin.json b/attendance-checkin.json index bfd58bc..8b56bed 100644 --- a/attendance-checkin.json +++ b/attendance-checkin.json @@ -3,16 +3,27 @@ "name": "근태현황 출퇴근 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "위치 정보 권한 허용 후 출근/퇴근 기록을 테스트하는 E2E 테스트", "baseUrl": "https://dev.codebridge-x.com", "url": "/hr/attendance", - "navigation": { "targetUrl": "/hr/attendance", "urlPattern": "/hr/attendance|/ko/hr/attendance", - "menuHints": ["근태현황", "근태 현황", "출퇴근", "인사관리"] + "menuHints": [ + "근태현황", + "근태 현황", + "출퇴근", + "인사관리" + ] }, "menuNavigation": { "level1": "인사관리", @@ -26,8 +37,21 @@ "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", "level1": "인사관리", "level2": "근태현황", - "alternativeLevel1Names": ["인사관리", "인사 관리", "HR", "Human Resource", "HR관리"], - "alternativeLevel2Names": ["근태현황", "근태 현황", "출퇴근", "Attendance", "출퇴근현황", "근태관리"], + "alternativeLevel1Names": [ + "인사관리", + "인사 관리", + "HR", + "Human Resource", + "HR관리" + ], + "alternativeLevel2Names": [ + "근태현황", + "근태 현황", + "출퇴근", + "Attendance", + "출퇴근현황", + "근태관리" + ], "fallbackUrls": [ "/hr/attendance", "/ko/hr/attendance", @@ -43,15 +67,18 @@ "scrollDelay": 300 } }, - "timeout": 120000, - "tags": ["hr", "attendance", "geolocation", "checkin", "checkout"], - + "tags": [ + "hr", + "attendance", + "geolocation", + "checkin", + "checkout" + ], "auth": { "username": "TestUser5", "password": "password123!" }, - "browserConfig": { "permissions": { "geolocation": { @@ -70,14 +97,17 @@ "latitude": 37.557358, "longitude": 126.864414 }, - "permissions": ["geolocation"] + "permissions": [ + "geolocation" + ] } }, - "preTestSetup": { "description": "테스트 시작 전 Playwright 브라우저 컨텍스트에서 위치 권한 설정", "playwright": { - "grantPermissions": ["geolocation"], + "grantPermissions": [ + "geolocation" + ], "setGeolocation": { "latitude": 37.557358, "longitude": 126.864414, @@ -91,10 +121,9 @@ ] } }, - "steps": [ { - "id": "step-0", + "id": 1, "name": "🔐 Geolocation API 모킹 (권한 팝업 방지)", "description": "페이지 로드 직후 Geolocation API를 모킹하여 브라우저 권한 팝업이 나타나지 않도록 함", "executeBeforeNavigation": false, @@ -105,22 +134,34 @@ "script": "(() => { const mockPosition = { coords: { latitude: 37.557358, longitude: 126.864414, accuracy: 100, altitude: null, altitudeAccuracy: null, heading: null, speed: null }, timestamp: Date.now() }; const mockGeolocation = { getCurrentPosition: (success, error, options) => { console.log('[E2E] Geolocation.getCurrentPosition - 모킹된 위치 반환'); setTimeout(() => success(mockPosition), 50); }, watchPosition: (success, error, options) => { console.log('[E2E] Geolocation.watchPosition - 모킹된 위치 반환'); setTimeout(() => success(mockPosition), 50); return 1; }, clearWatch: (id) => {} }; Object.defineProperty(navigator, 'geolocation', { value: mockGeolocation, writable: false, configurable: true }); console.log('[E2E] Geolocation API 모킹 완료 - 서울 영등포구 좌표'); return { success: true, coords: mockPosition.coords }; })()", "description": "Geolocation API 모킹 (서울 영등포구 좌표: 37.557358, 126.864414)" }, - { "type": "wait", "duration": 300, "description": "모킹 적용 대기" } + { + "type": "wait", + "duration": 300, + "description": "모킹 적용 대기" + } ], "note": "Geolocation API를 모킹하면 브라우저가 위치 권한을 요청하지 않음" }, { - "id": "step-0-1", + "id": 2, "name": "🗺️ 브라우저 위치 권한 팝업 클릭 (좌측 상단)", "description": "Chrome 브라우저 좌측 상단에 나타나는 '사이트에 있는 동안 허용' 팝업 클릭", "actions": [ - { "type": "wait", "duration": 1500, "description": "위치 권한 팝업 표시 대기" }, + { + "type": "wait", + "duration": 1500, + "description": "위치 권한 팝업 표시 대기" + }, { "type": "evaluate", "script": "(async function() { const permissionSelectors = [ '[class*=\"permission\"][class*=\"allow\"]', '[class*=\"infobar\"] button', '[aria-label*=\"허용\"]', '[aria-label*=\"Allow\"]', 'button:has-text(\"사이트에 있는 동안 허용\")', 'button:has-text(\"허용\")', 'button:has-text(\"Allow\")', '[data-testid*=\"permission\"]', '.permission-prompt button', '[class*=\"PermissionPrompt\"] button' ]; for (const sel of permissionSelectors) { try { const btn = document.querySelector(sel); if (btn && btn.offsetParent !== null) { btn.click(); console.log('[E2E] 위치 권한 팝업 클릭 성공:', sel); await new Promise(r => setTimeout(r, 500)); return { clicked: true, selector: sel }; } } catch(e) {} } const allButtons = Array.from(document.querySelectorAll('button, [role=\"button\"]')); const allowBtn = allButtons.find(b => { const text = b.innerText || b.textContent || ''; return text.includes('사이트에 있는 동안 허용') || text.includes('허용') || text.includes('Allow'); }); if (allowBtn && allowBtn.offsetParent !== null) { allowBtn.click(); console.log('[E2E] 위치 권한 팝업 텍스트 검색으로 클릭'); return { clicked: true, method: 'textSearch' }; } console.log('[E2E] 위치 권한 팝업 없음 (이미 허용되었거나 모킹으로 우회됨)'); return { clicked: false, reason: 'no_popup_found' }; })()", "description": "좌측 상단 권한 팝업 찾아서 클릭" }, - { "type": "wait", "duration": 500, "description": "권한 설정 적용 대기" } + { + "type": "wait", + "duration": 500, + "description": "권한 설정 적용 대기" + } ], "errorHandling": { "onTimeout": "continue", @@ -129,7 +170,7 @@ } }, { - "id": "step-0-2", + "id": 3, "name": "📂 사이드바 메뉴 전체 펼치기", "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴 펼침", "actions": [ @@ -137,35 +178,63 @@ "type": "evaluate", "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" }, - { "type": "wait", "duration": 300 }, + { + "type": "wait", + "duration": 300 + }, { "type": "evaluate", "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" }, - { "type": "wait", "duration": 2000 }, - { "type": "screenshot", "name": "after_permission_grant_and_menu_expanded" } + { + "type": "wait", + "duration": 2000 + }, + { + "type": "screenshot", + "name": "after_permission_grant_and_menu_expanded" + } ], "verification": [ "사이드바 메뉴가 펼쳐졌는지 확인" ] }, { - "id": 2, + "id": 4, "name": "1차 메뉴 찾기: 인사관리 (스크롤 포함)", "description": "사이드바를 스크롤하며 '인사관리' 메뉴를 찾아 클릭", "actions": [ { "type": "scrollAndFind", "target": "인사관리", - "alternativeTexts": ["인사관리", "인사 관리", "HR", "Human Resource"], + "alternativeTexts": [ + "인사관리", + "인사 관리", + "HR", + "Human Resource" + ], "scrollContainer": "sidebar", "maxAttempts": 10, "description": "스크롤하며 인사관리 메뉴 찾기" }, - { "type": "wait", "duration": 300 }, - { "type": "click_if_exists", "target": "인사관리", "description": "인사관리 메뉴 클릭" }, - { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, - { "type": "screenshot", "name": "hr_menu_expanded" } + { + "type": "wait", + "duration": 300 + }, + { + "type": "click_if_exists", + "target": "인사관리", + "description": "인사관리 메뉴 클릭" + }, + { + "type": "wait", + "duration": 500, + "description": "서브메뉴 펼쳐지기 대기" + }, + { + "type": "screenshot", + "name": "hr_menu_expanded" + } ], "verification": [ "인사관리 메뉴가 클릭되었는지 확인", @@ -178,22 +247,41 @@ } }, { - "id": 3, + "id": 5, "name": "2차 메뉴 찾기: 근태현황 (스크롤 포함)", "description": "서브메뉴에서 '근태현황'을 찾아 클릭", "actions": [ { "type": "scrollAndFind", "target": "근태현황", - "alternativeTexts": ["근태현황", "근태 현황", "출퇴근", "Attendance"], + "alternativeTexts": [ + "근태현황", + "근태 현황", + "출퇴근", + "Attendance" + ], "scrollContainer": "submenu", "maxAttempts": 5, "description": "서브메뉴에서 근태현황 찾기" }, - { "type": "wait", "duration": 200 }, - { "type": "click_if_exists", "target": "근태현황", "description": "근태현황 메뉴 클릭" }, - { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 }, - { "type": "screenshot", "name": "attendance_page" } + { + "type": "wait", + "duration": 200 + }, + { + "type": "click_if_exists", + "target": "근태현황", + "description": "근태현황 메뉴 클릭" + }, + { + "type": "wait", + "target": "페이지 로드 완료", + "timeout": 10000 + }, + { + "type": "screenshot", + "name": "attendance_page" + } ], "verification": [ "근태현황 메뉴 클릭 성공", @@ -201,18 +289,27 @@ ] }, { - "id": 4, + "id": 6, "name": "404 에러 감지 및 대체 경로 시도", "description": "페이지 로드 후 404 에러 여부 확인, 404시 대체 경로 탐색", "actions": [ - { "type": "wait", "duration": 1000 }, - { "type": "checkFor404", "indicators": [ - "페이지를 찾을 수 없습니다", - "404", - "Not Found", - "존재하지 않거나" - ]}, - { "type": "screenshot", "name": "page_load_result" } + { + "type": "wait", + "duration": 1000 + }, + { + "type": "checkFor404", + "indicators": [ + "페이지를 찾을 수 없습니다", + "404", + "Not Found", + "존재하지 않거나" + ] + }, + { + "type": "screenshot", + "name": "page_load_result" + } ], "verification": [ "현재 페이지가 404인지 확인" @@ -220,7 +317,10 @@ "onError404": { "description": "404 에러 발생 시 대체 URL 시도", "actions": [ - { "type": "log", "message": "404 감지 - 대체 경로 탐색 시작" }, + { + "type": "log", + "message": "404 감지 - 대체 경로 탐색 시작" + }, { "type": "tryAlternativeUrls", "urls": [ @@ -239,12 +339,28 @@ } }, { - "id": 5, + "id": 7, "name": "페이지 정상 로드 확인", "description": "근태현황 페이지가 정상적으로 로드되었는지 확인", "actions": [ - { "type": "verify", "target": "pageTitle", "contains": ["근태현황", "출퇴근", "Attendance"] }, - { "type": "verify", "target": "pageContent", "notContains": ["404", "찾을 수 없습니다", "Not Found"] } + { + "type": "verify", + "target": "pageTitle", + "contains": [ + "근태현황", + "출퇴근", + "Attendance" + ] + }, + { + "type": "verify", + "target": "pageContent", + "notContains": [ + "404", + "찾을 수 없습니다", + "Not Found" + ] + } ], "verification": [ "페이지 제목 '근태현황' 또는 관련 텍스트 표시", @@ -253,11 +369,16 @@ ], "successCriteria": { "urlPattern": "/hr/attendance", - "requiredElements": ["출퇴근", "출근", "퇴근", "현재 시간"] + "requiredElements": [ + "출퇴근", + "출근", + "퇴근", + "현재 시간" + ] } }, { - "id": "step-5", + "id": 8, "name": "브라우저 위치 권한 설정", "description": "Playwright context에서 위치 정보 권한을 허용하고 가상 위치 설정", "playwright": { @@ -272,7 +393,7 @@ } }, { - "id": "step-6", + "id": 9, "name": "위치 정보 로딩 대기", "description": "Google Map 로딩 및 현재 위치 표시 대기", "waitFor": { @@ -286,7 +407,7 @@ } }, { - "id": "step-7", + "id": 10, "name": "사용자 정보 확인", "description": "출퇴근 패널에서 로그인한 사용자 정보 확인", "verify": { @@ -301,27 +422,39 @@ } }, { - "id": "step-8", + "id": 11, "name": "출근 상태 확인", "description": "현재 출퇴근 상태 확인 (출근 전/출근 후)", "capture": { "variable": "attendanceStatus", "checkElements": [ - { "selector": "button:has-text('출근하기')", "status": "not_checked_in" }, - { "selector": "text=출근 완료", "status": "checked_in" }, - { "selector": "button:has-text('퇴근하기')", "status": "ready_to_checkout" } + { + "selector": "button:has-text('출근하기')", + "status": "not_checked_in" + }, + { + "selector": "text=출근 완료", + "status": "checked_in" + }, + { + "selector": "button:has-text('퇴근하기')", + "status": "ready_to_checkout" + } ] } }, { - "id": "step-9", + "id": 12, "name": "출근하기 (미출근 상태인 경우)", "description": "출근하기 버튼이 활성화된 경우 클릭하여 출근 기록", "condition": { "if": "{attendanceStatus} == 'not_checked_in'" }, "actions": [ - { "type": "click_if_exists", "target": "출근하기" } + { + "type": "click_if_exists", + "target": "출근하기" + } ], "waitFor": { "type": "text", @@ -329,16 +462,25 @@ "timeout": 5000 }, "expect": { - "toast": ["출근", "완료", "성공"], - "visible": ["출근 완료", "출근 시간"] + "toast": [ + "출근", + "완료", + "성공" + ], + "visible": [ + "출근 완료", + "출근 시간" + ] } }, { - "id": "step-10", + "id": 13, "name": "출근 완료 상태 확인", "description": "출근 완료 후 상태 및 출근 시간 표시 확인", "verify": { - "visible": ["출근 완료"], + "visible": [ + "출근 완료" + ], "checkInTime": { "format": "HH:mm:ss", "displayed": true @@ -350,7 +492,7 @@ } }, { - "id": "step-11", + "id": 14, "name": "퇴근하기 버튼 상태 확인", "description": "출근 완료 후 퇴근하기 버튼 활성화 여부 확인", "verify": { @@ -362,7 +504,7 @@ } }, { - "id": "step-12", + "id": 15, "name": "퇴근하기 (선택적)", "description": "퇴근하기 버튼이 활성화된 경우 클릭하여 퇴근 기록", "optional": true, @@ -370,20 +512,33 @@ "if": "button[name='퇴근하기']:enabled" }, "actions": [ - { "type": "click_if_exists", "target": "퇴근하기" } + { + "type": "click_if_exists", + "target": "퇴근하기" + } ], "waitFor": { "type": "text", - "content": ["퇴근 완료", "퇴근 시간"], + "content": [ + "퇴근 완료", + "퇴근 시간" + ], "timeout": 5000 }, "expect": { - "toast": ["퇴근", "완료", "성공"], - "visible": ["퇴근 완료", "퇴근 시간"] + "toast": [ + "퇴근", + "완료", + "성공" + ], + "visible": [ + "퇴근 완료", + "퇴근 시간" + ] } }, { - "id": "step-13", + "id": 16, "name": "최종 상태 확인", "description": "출퇴근 기록 후 최종 상태 확인", "verify": { @@ -391,9 +546,14 @@ "mapDisplayed": true, "attendanceRecorded": true } + }, + { + "id": 17, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], - "assertions": [ { "type": "url", @@ -417,20 +577,17 @@ "message": "현재 시간이 표시되어야 함" } ], - "cleanup": { "enabled": false, "description": "출퇴근 기록은 삭제하지 않음 (업무 데이터)", "note": "테스트 후 수동으로 관리자가 삭제 필요시 처리" }, - "notes": { "testScope": "위치 권한 허용 -> 근태현황 페이지 이동 -> 출근/퇴근 기록 테스트", "antiPattern404": "직접 URL 접근 금지 - 반드시 메뉴 클릭으로 페이지 진입", "scrollRequired": "사이드바 스크롤을 통해 메뉴 항목 탐색 필수", "correctUrl": "/hr/attendance (기존 /ko/hr/attendance에서 수정됨)" }, - "playwrightMcpInstructions": { "description": "Playwright MCP를 사용한 위치 권한 설정 방법", "beforeNavigation": [ diff --git a/board-management.json b/board-management.json index 13f937c..c651070 100644 --- a/board-management.json +++ b/board-management.json @@ -3,7 +3,14 @@ "name": "게시판 관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "게시판 > 게시판 관리 목록/검색/상세 기능 검증", "baseUrl": "https://dev.codebridge-x.com", @@ -25,54 +32,136 @@ "action": "menu_navigate", "level1": "게시판", "level2": "게시판 관리", - "expected": { "url_contains": "/board" } + "expected": { + "url_contains": "/board" + } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/board/board-management" + } + }, + { + "id": 3, "name": "목업 감지", "action": "verify_not_mockup" }, { - "id": 3, - "name": "게시판 관리 페이지 확인", - "action": "verify_detail", - "checks": ["visible_text:게시판"] + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" }, { - "id": 4, + "id": 5, + "name": "게시판 관리 페이지 확인", + "action": "verify_detail", + "checks": [ + "visible_text:게시판" + ] + }, + { + "id": 6, "name": "테이블 확인", "action": "verify_table" }, { - "id": 5, + "id": 7, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 8, "name": "검색 기능", "action": "search", "value": "테스트" }, { - "id": 6, - "name": "검색 후 확인", - "action": "verify_detail", - "checks": ["visible_text:게시판"] + "id": 9, + "name": "검색 결과 대기", + "action": "wait", + "duration": 1000 }, { - "id": 7, + "id": 10, + "name": "검색 결과 데이터 검증", + "action": "evaluate", + "script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Search result: ' + rows.length + ' rows';\n })()" + }, + { + "id": 11, + "name": "검색 초기화", + "action": "evaluate", + "script": "(() => {\n const selectors = ['input[type=\"search\"]', 'input[placeholder*=\"검색\"]', 'input[placeholder*=\"Search\"]', 'input[role=\"searchbox\"]', '[class*=\"search\"] input'];\n for (const sel of selectors) {\n const el = document.querySelector(sel);\n if (el) {\n const nativeSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;\n if (nativeSetter) nativeSetter.call(el, '');\n else el.value = '';\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n return 'Search cleared';\n }\n }\n return 'No search input found (ok)';\n })()" + }, + { + "id": 12, + "name": "검색 초기화 결과 대기", + "action": "wait", + "duration": 1000 + }, + { + "id": 13, + "name": "검색 초기화 및 복원 확인", + "action": "evaluate", + "script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Restored: ' + rows.length + ' rows';\n })()" + }, + { + "id": 14, + "name": "검색 후 확인", + "action": "verify_detail", + "checks": [ + "visible_text:게시판" + ] + }, + { + "id": 15, "name": "첫 번째 행 클릭", "action": "click_first_row" }, { - "id": 8, - "name": "상세 확인", - "action": "verify_detail", - "checks": ["visible_text:게시판"] + "id": 16, + "name": "상세 페이지 로딩 대기", + "action": "wait", + "duration": 1000 }, { - "id": 9, + "id": 17, + "name": "상세 페이지 - 콘텐츠 확인", + "action": "evaluate", + "script": "(() => {\n const inputs = document.querySelectorAll('input:not([type=\"hidden\"]), textarea, select');\n const buttons = document.querySelectorAll('button');\n const hasDetail = inputs.length > 0 || document.body.innerText.includes('상세') || document.body.innerText.includes('수정');\n return hasDetail ? 'Detail page: ' + inputs.length + ' inputs, ' + buttons.length + ' buttons' : 'List page (no detail view)';\n })()" + }, + { + "id": 18, + "name": "상세 확인", + "action": "verify_detail", + "checks": [ + "visible_text:게시판" + ] + }, + { + "id": 19, "name": "모달 닫기", "action": "close_modal_if_open" }, { - "id": 10, + "id": 20, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 21, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 22, "name": "목록 복귀", "action": "click_if_exists", "target": "button:has-text('목록'), a:has-text('목록')" diff --git a/company-info.json b/company-info.json index 7d3dc90..c7a2acf 100644 --- a/company-info.json +++ b/company-info.json @@ -3,15 +3,25 @@ "name": "설정 - 회사정보", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "회사 정보 관리 기능 테스트 - 회사 정보 조회, 수정, 회사 추가 기능", "baseUrl": "https://dev.codebridge-x.com", - "navigation": { "targetUrl": "/company-info", "urlPattern": "/company-info|/ko/company-info|/settings/company-info", - "menuHints": ["회사정보", "회사 정보", "설정"] + "menuHints": [ + "회사정보", + "회사 정보", + "설정" + ] }, "menuNavigation": { "level1": "설정", @@ -29,8 +39,20 @@ "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", "level1": "설정", "level2": "회사정보", - "alternativeLevel1Names": ["설정", "Settings", "환경설정", "시스템설정", "관리"], - "alternativeLevel2Names": ["회사정보", "회사 정보", "Company Info", "회사관리", "기업정보"], + "alternativeLevel1Names": [ + "설정", + "Settings", + "환경설정", + "시스템설정", + "관리" + ], + "alternativeLevel2Names": [ + "회사정보", + "회사 정보", + "Company Info", + "회사관리", + "기업정보" + ], "fallbackUrls": [ "/company-info", "/ko/company-info", @@ -45,7 +67,6 @@ "scrollDelay": 300 } }, - "expectedAPIs": [ { "method": "GET", @@ -63,17 +84,30 @@ "description": "회사 추가" } ], - "steps": [ { - "id": 0, + "id": 1, "name": "사이드바 메뉴 전체 펼치기", "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", "actions": [ - { "type": "scroll", "target": "sidebar", "direction": "top", "description": "사이드바 최상단으로 스크롤" }, - { "type": "wait", "duration": 300 }, - { "type": "evaluate", "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" }, - { "type": "wait", "duration": 2000 } + { + "type": "scroll", + "target": "sidebar", + "direction": "top", + "description": "사이드바 최상단으로 스크롤" + }, + { + "type": "wait", + "duration": 300 + }, + { + "type": "evaluate", + "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" + }, + { + "type": "wait", + "duration": 2000 + } ], "verification": [ "사이드바가 화면에 보이는지 확인", @@ -81,22 +115,41 @@ ] }, { - "id": 1, + "id": 2, "name": "1차 메뉴 찾기: 설정 (스크롤 포함)", "description": "사이드바를 스크롤하며 '설정' 메뉴를 찾아 클릭", "actions": [ { "type": "scrollAndFind", "target": "설정", - "alternativeTexts": ["설정", "Settings", "환경설정", "시스템설정"], + "alternativeTexts": [ + "설정", + "Settings", + "환경설정", + "시스템설정" + ], "scrollContainer": "sidebar", "maxAttempts": 10, "description": "스크롤하며 설정 메뉴 찾기" }, - { "type": "wait", "duration": 300 }, - { "type": "click_if_exists", "target": "설정", "description": "설정 메뉴 클릭" }, - { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, - { "type": "screenshot", "name": "settings_menu_expanded" } + { + "type": "wait", + "duration": 300 + }, + { + "type": "click_if_exists", + "target": "설정", + "description": "설정 메뉴 클릭" + }, + { + "type": "wait", + "duration": 500, + "description": "서브메뉴 펼쳐지기 대기" + }, + { + "type": "screenshot", + "name": "settings_menu_expanded" + } ], "verification": [ "설정 메뉴가 클릭되었는지 확인", @@ -109,22 +162,41 @@ } }, { - "id": 2, + "id": 3, "name": "2차 메뉴 찾기: 회사정보 (스크롤 포함)", "description": "서브메뉴에서 '회사정보'를 찾아 클릭", "actions": [ { "type": "scrollAndFind", "target": "회사정보", - "alternativeTexts": ["회사정보", "회사 정보", "Company Info", "회사관리"], + "alternativeTexts": [ + "회사정보", + "회사 정보", + "Company Info", + "회사관리" + ], "scrollContainer": "submenu", "maxAttempts": 5, "description": "서브메뉴에서 회사정보 찾기" }, - { "type": "wait", "duration": 200 }, - { "type": "click_if_exists", "target": "회사정보", "description": "회사정보 메뉴 클릭" }, - { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 }, - { "type": "screenshot", "name": "company_info_page" } + { + "type": "wait", + "duration": 200 + }, + { + "type": "click_if_exists", + "target": "회사정보", + "description": "회사정보 메뉴 클릭" + }, + { + "type": "wait", + "target": "페이지 로드 완료", + "timeout": 10000 + }, + { + "type": "screenshot", + "name": "company_info_page" + } ], "verification": [ "회사정보 메뉴 클릭 성공", @@ -132,18 +204,27 @@ ] }, { - "id": 3, + "id": 4, "name": "404 에러 감지 및 대체 경로 시도", "description": "페이지 로드 후 404 에러 여부 확인, 404시 대체 경로 탐색", "actions": [ - { "type": "wait", "duration": 1000 }, - { "type": "checkFor404", "indicators": [ - "페이지를 찾을 수 없습니다", - "404", - "Not Found", - "존재하지 않거나" - ]}, - { "type": "screenshot", "name": "page_load_result" } + { + "type": "wait", + "duration": 1000 + }, + { + "type": "checkFor404", + "indicators": [ + "페이지를 찾을 수 없습니다", + "404", + "Not Found", + "존재하지 않거나" + ] + }, + { + "type": "screenshot", + "name": "page_load_result" + } ], "verification": [ "현재 페이지가 404인지 확인" @@ -151,7 +232,10 @@ "onError404": { "description": "404 에러 발생 시 대체 URL 시도", "actions": [ - { "type": "log", "message": "404 감지 - 대체 경로 탐색 시작" }, + { + "type": "log", + "message": "404 감지 - 대체 경로 탐색 시작" + }, { "type": "tryAlternativeUrls", "urls": [ @@ -169,12 +253,28 @@ } }, { - "id": 4, + "id": 5, "name": "페이지 정상 로드 확인", "description": "회사정보 페이지가 정상적으로 로드되었는지 확인", "actions": [ - { "type": "verify", "target": "pageTitle", "contains": ["회사정보", "회사 정보", "Company"] }, - { "type": "verify", "target": "pageContent", "notContains": ["404", "찾을 수 없습니다", "Not Found"] } + { + "type": "verify", + "target": "pageTitle", + "contains": [ + "회사정보", + "회사 정보", + "Company" + ] + }, + { + "type": "verify", + "target": "pageContent", + "notContains": [ + "404", + "찾을 수 없습니다", + "Not Found" + ] + } ], "verification": [ "페이지 제목 '회사정보' 또는 관련 텍스트 표시", @@ -183,7 +283,11 @@ ], "successCriteria": { "urlPattern": "/company-info", - "requiredElements": ["회사", "회사명", "대표자명"] + "requiredElements": [ + "회사", + "회사명", + "대표자명" + ] } }, { @@ -192,7 +296,8 @@ "action": "verify", "target": "heading", "expected": "회사정보", - "validation": "페이지 제목이 '회사정보'로 표시됨" + "validation": "페이지 제목이 '회사정보'로 표시됨", + "id": 6 }, { "step": 6, @@ -200,7 +305,8 @@ "action": "verify", "target": "button[text='회사 추가']", "expected": "button exists", - "validation": "회사 추가 버튼이 표시됨" + "validation": "회사 추가 버튼이 표시됨", + "id": 7 }, { "step": 7, @@ -208,7 +314,8 @@ "action": "verify", "target": "button[text='수정']", "expected": "button exists", - "validation": "수정 버튼이 표시됨" + "validation": "수정 버튼이 표시됨", + "id": 8 }, { "step": 8, @@ -216,7 +323,8 @@ "action": "verify", "target": "textbox[label='회사명'][disabled]", "expected": "프론트_테스트회사", - "validation": "회사명이 표시되고 비활성화 상태" + "validation": "회사명이 표시되고 비활성화 상태", + "id": 9 }, { "step": 9, @@ -224,7 +332,8 @@ "action": "verify", "target": "textbox[label='대표자명'][disabled]", "expected": "프론트", - "validation": "대표자명이 표시되고 비활성화 상태" + "validation": "대표자명이 표시되고 비활성화 상태", + "id": 10 }, { "step": 10, @@ -232,7 +341,8 @@ "action": "verify", "target": "textbox[label='업태'][disabled]", "expected": "업태명", - "validation": "업태가 표시되고 비활성화 상태" + "validation": "업태가 표시되고 비활성화 상태", + "id": 11 }, { "step": 11, @@ -240,7 +350,8 @@ "action": "verify", "target": "textbox[label='업종'][disabled]", "expected": "업종명", - "validation": "업종이 표시되고 비활성화 상태" + "validation": "업종이 표시되고 비활성화 상태", + "id": 12 }, { "step": 12, @@ -248,7 +359,8 @@ "action": "verify", "target": "textbox[label='주소명'][disabled]", "expected": "주소 표시", - "validation": "주소가 표시되고 비활성화 상태" + "validation": "주소가 표시되고 비활성화 상태", + "id": 13 }, { "step": 13, @@ -256,7 +368,8 @@ "action": "verify", "target": "textbox[label='이메일 (아이디)'][disabled]", "expected": "이메일 표시", - "validation": "이메일이 표시되고 비활성화 상태" + "validation": "이메일이 표시되고 비활성화 상태", + "id": 14 }, { "step": 14, @@ -264,7 +377,8 @@ "action": "verify", "target": "textbox[label='사업자등록번호'][disabled]", "expected": "사업자등록번호 표시", - "validation": "사업자등록번호가 표시되고 비활성화 상태" + "validation": "사업자등록번호가 표시되고 비활성화 상태", + "id": 15 }, { "step": 15, @@ -272,7 +386,8 @@ "action": "click_if_exists", "target": "button[text='수정']", "expected": "edit mode enabled", - "validation": "수정 모드로 전환됨" + "validation": "수정 모드로 전환됨", + "id": 16 }, { "step": 16, @@ -280,7 +395,8 @@ "action": "verify", "target": "textbox:not([disabled])", "expected": "fields enabled", - "validation": "텍스트 필드들이 활성화됨" + "validation": "텍스트 필드들이 활성화됨", + "id": 17 }, { "step": 17, @@ -288,7 +404,8 @@ "action": "click_if_exists", "target": "button[text='취소']", "expected": "edit mode disabled", - "validation": "조회 모드로 복귀" + "validation": "조회 모드로 복귀", + "id": 18 }, { "step": 18, @@ -296,7 +413,8 @@ "action": "click_if_exists", "target": "button[text='회사 추가']", "expected": "dialog opened", - "validation": "회사 추가 다이얼로그가 열림" + "validation": "회사 추가 다이얼로그가 열림", + "id": 19 }, { "step": 19, @@ -304,7 +422,8 @@ "action": "verify", "target": "dialog", "expected": "회사 추가 다이얼로그 표시", - "validation": "다이얼로그 제목, 입력 필드, 버튼 확인" + "validation": "다이얼로그 제목, 입력 필드, 버튼 확인", + "id": 20 }, { "step": 20, @@ -312,34 +431,51 @@ "action": "click_if_exists", "target": "dialog button[text='취소']", "expected": "dialog closed", - "validation": "다이얼로그가 닫힘" + "validation": "다이얼로그가 닫힘", + "id": 21 }, { "step": 21, "name": "수정 모드에서 데이터 변경 테스트", "description": "실제 데이터를 수정하고 저장 기능 검증", "actions": [ - { "type": "click_if_exists", "target": "수정", "description": "수정 모드 진입" } + { + "type": "click_if_exists", + "target": "수정", + "description": "수정 모드 진입" + } ], "expect": { "fieldsEnabled": true - } + }, + "id": 22 }, { "step": 22, "name": "업태 필드 수정", "description": "업태 필드 값 변경", "actions": [ - { "type": "clear", "target": "업태" }, - { "type": "fill", "target": "업태", "value": "테스트업태_수정" } - ] + { + "type": "clear", + "target": "업태" + }, + { + "type": "fill", + "target": "업태", + "value": "테스트업태_수정" + } + ], + "id": 23 }, { "step": 23, "name": "저장 버튼 클릭", "description": "수정된 회사 정보 저장", "actions": [ - { "type": "click_if_exists", "target": "저장" } + { + "type": "click_if_exists", + "target": "저장" + } ], "waitFor": { "type": "apiResponse", @@ -347,8 +483,14 @@ "timeout": 5000 }, "expect": { - "toast": ["수정", "완료", "성공", "저장"] - } + "toast": [ + "수정", + "완료", + "성공", + "저장" + ] + }, + "id": 24 }, { "step": 24, @@ -360,36 +502,63 @@ "target": "업태", "expected": "테스트업태_수정" } - } + }, + "id": 25 }, { "step": 25, "name": "회사 추가 다이얼로그 열기", "description": "회사 추가 버튼 클릭하여 다이얼로그 열기", "actions": [ - { "type": "click_if_exists", "target": "회사 추가" } + { + "type": "click_if_exists", + "target": "회사 추가" + } ], "expect": { "dialog": true, - "visible": ["회사명", "대표자명", "사업자등록번호", "등록", "취소"] - } + "visible": [ + "회사명", + "대표자명", + "사업자등록번호", + "등록", + "취소" + ] + }, + "id": 26 }, { "step": 26, "name": "새 회사 정보 입력", "description": "회사 추가 다이얼로그에서 필수 정보 입력", "actions": [ - { "type": "fill", "target": "회사명", "value": "테스트회사_{timestamp}" }, - { "type": "fill", "target": "대표자명", "value": "테스트대표" }, - { "type": "fill", "target": "사업자등록번호", "value": "123-45-67890" } - ] + { + "type": "fill", + "target": "회사명", + "value": "테스트회사_{timestamp}" + }, + { + "type": "fill", + "target": "대표자명", + "value": "테스트대표" + }, + { + "type": "fill", + "target": "사업자등록번호", + "value": "123-45-67890" + } + ], + "id": 27 }, { "step": 27, "name": "회사 등록", "description": "등록 버튼 클릭하여 새 회사 등록", "actions": [ - { "type": "click_if_exists", "target": "등록" } + { + "type": "click_if_exists", + "target": "등록" + } ], "waitFor": { "type": "apiResponse", @@ -397,9 +566,14 @@ "timeout": 5000 }, "expect": { - "toast": ["등록", "완료", "성공"], + "toast": [ + "등록", + "완료", + "성공" + ], "dialogClosed": true - } + }, + "id": 28 }, { "step": 28, @@ -408,24 +582,49 @@ "description": "등록된 회사가 목록에 표시되는지 확인", "verify": { "visible": "테스트회사" - } + }, + "id": 29 }, { "step": 29, "name": "원복: 업태 필드 원래 값으로 복구", "description": "테스트 후 원래 값으로 복구", "actions": [ - { "type": "click_if_exists", "target": "수정" }, - { "type": "clear", "target": "업태" }, - { "type": "fill", "target": "업태", "value": "업태명" }, - { "type": "click_if_exists", "target": "저장" } + { + "type": "click_if_exists", + "target": "수정" + }, + { + "type": "clear", + "target": "업태" + }, + { + "type": "fill", + "target": "업태", + "value": "업태명" + }, + { + "type": "click_if_exists", + "target": "저장" + } ], "expect": { - "toast": ["수정", "완료", "성공", "저장"] - } + "toast": [ + "수정", + "완료", + "성공", + "저장" + ] + }, + "id": 30 + }, + { + "id": 31, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], - "notes": [ "직접 URL 접근 금지: 반드시 메뉴 클릭으로 페이지 진입 (404 방지)", "스크롤 필수: 사이드바가 길 경우 메뉴가 화면 밖에 있을 수 있음", diff --git a/customer-event.json b/customer-event.json index 5eeae78..ddbf378 100644 --- a/customer-event.json +++ b/customer-event.json @@ -3,7 +3,14 @@ "name": "이벤트 게시판 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "고객센터 > 이벤트 게시판 메뉴의 이벤트 목록 조회/상세보기 기능 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -27,11 +34,21 @@ "level2": "이벤트 게시판", "expected": { "url_contains": "/customer-center", - "visible": ["이벤트"] + "visible": [ + "이벤트" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/customer-center/events" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -41,7 +58,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "이벤트 페이지 구조 확인", "action": "verify_elements", "checks": [ @@ -52,7 +75,7 @@ "expected": "이벤트 페이지 구조 정상" }, { - "id": 4, + "id": 6, "phase": "READ", "name": "[READ] 이벤트 목록 데이터 확인", "action": "verify_detail", @@ -62,7 +85,7 @@ "expected": "이벤트 데이터 표시" }, { - "id": 5, + "id": 7, "phase": "FILTER", "name": "[FILTER] 상태별 필터", "action": "verify_elements", @@ -72,7 +95,7 @@ "expected": "상태 필터 표시" }, { - "id": 6, + "id": 8, "phase": "READ", "name": "[READ] 이벤트 상세 보기", "action": "click_if_exists", @@ -82,7 +105,7 @@ } }, { - "id": 7, + "id": 9, "name": "이벤트 상세 정보 확인", "action": "verify_detail", "checks": [ @@ -94,7 +117,7 @@ "expected": "이벤트 상세 정보 표시" }, { - "id": 8, + "id": 10, "name": "이벤트 참여 버튼 확인", "action": "verify_elements", "checks": [ @@ -103,7 +126,7 @@ "expected": "참여 버튼 확인" }, { - "id": 9, + "id": 11, "name": "공유 기능 확인", "action": "verify_elements", "checks": [ @@ -112,14 +135,14 @@ "expected": "공유 기능 표시" }, { - "id": 10, + "id": 12, "name": "목록으로 돌아가기", "action": "click_if_exists", "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", "expected": "목록 페이지로 복귀" }, { - "id": 11, + "id": 13, "name": "페이지네이션 확인", "action": "verify_elements", "checks": [ @@ -128,7 +151,35 @@ "expected": "페이지네이션 표시" }, { - "id": 12, + "id": 14, + "name": "테이블 행 클릭 - 상세 페이지 이동", + "action": "click_first_row" + }, + { + "id": 15, + "name": "상세 페이지 로딩 대기", + "action": "wait", + "duration": 1000 + }, + { + "id": 16, + "name": "상세 페이지 - 콘텐츠 확인", + "action": "evaluate", + "script": "(() => {\n const inputs = document.querySelectorAll('input:not([type=\"hidden\"]), textarea, select');\n const buttons = document.querySelectorAll('button');\n const hasDetail = inputs.length > 0 || document.body.innerText.includes('상세') || document.body.innerText.includes('수정');\n return hasDetail ? 'Detail page: ' + inputs.length + ' inputs, ' + buttons.length + ' buttons' : 'List page (no detail view)';\n })()" + }, + { + "id": 17, + "name": "모달/상세 닫기", + "action": "close_modal_if_open" + }, + { + "id": 18, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 19, "name": "이벤트 기간 정보 확인", "action": "verify_detail", "checks": [ @@ -154,7 +205,9 @@ { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "이벤트 목록, 이벤트 카드/리스트 존재" } ], diff --git a/customer-faq.json b/customer-faq.json index fc1387f..a78f85f 100644 --- a/customer-faq.json +++ b/customer-faq.json @@ -42,6 +42,14 @@ }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/customer-center/faq" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -51,7 +59,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "FAQ 페이지 구조 확인", "action": "verify_elements", "checks": [ @@ -62,7 +76,7 @@ "expected": "FAQ 페이지 구조 정상" }, { - "id": 4, + "id": 6, "phase": "READ", "name": "[READ] FAQ 목록 데이터 확인", "action": "verify_detail", @@ -72,7 +86,7 @@ "expected": "FAQ 데이터 표시" }, { - "id": 5, + "id": 7, "phase": "READ", "name": "[READ] FAQ 항목 펼치기", "action": "click_if_exists", @@ -82,7 +96,7 @@ } }, { - "id": 6, + "id": 8, "name": "FAQ 답변 내용 확인", "action": "verify_detail", "checks": [ @@ -91,7 +105,7 @@ "expected": "FAQ 답변 표시" }, { - "id": 7, + "id": 9, "phase": "FILTER", "name": "[FILTER] 카테고리 필터", "action": "verify_element", @@ -99,7 +113,7 @@ "expected": "카테고리 선택 가능" }, { - "id": 8, + "id": 10, "phase": "FILTER", "name": "[FILTER] 카테고리 선택 후 결과", "action": "verify_detail", @@ -109,7 +123,7 @@ "expected": "카테고리 필터 동작" }, { - "id": 9, + "id": 11, "phase": "SEARCH", "name": "[SEARCH] FAQ 검색", "action": "click_if_exists", @@ -118,7 +132,7 @@ "submit": true }, { - "id": 10, + "id": 12, "phase": "SEARCH", "name": "[SEARCH] 검색 결과 확인", "action": "verify_detail", @@ -128,7 +142,7 @@ "expected": "검색 기능 동작" }, { - "id": 11, + "id": 13, "name": "FAQ 접기/펼치기 토글", "action": "verify_elements", "checks": [ @@ -137,7 +151,19 @@ "expected": "토글 기능 표시" }, { - "id": 12, + "id": 14, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 15, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 16, "name": "전체 보기/접기", "action": "verify_elements", "checks": [ diff --git a/customer-notice.json b/customer-notice.json index c94c686..feb7e34 100644 --- a/customer-notice.json +++ b/customer-notice.json @@ -3,7 +3,14 @@ "name": "공지사항 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "고객센터 > 공지사항 메뉴의 공지사항 조회/검색/상세보기 기능 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -27,11 +34,21 @@ "level2": "공지사항", "expected": { "url_contains": "/customer-center/notices", - "visible": ["공지사항"] + "visible": [ + "공지사항" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/customer-center/notices" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -42,7 +59,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "공지사항 목록 구조 확인", "action": "verify_table", "checks": [ @@ -55,7 +78,13 @@ "expected": "공지사항 목록 표시" }, { - "id": 4, + "id": 6, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 7, "phase": "READ", "name": "[READ] 공지사항 목록 데이터 확인", "action": "verify_detail", @@ -65,7 +94,7 @@ "expected": "공지사항 데이터 표시" }, { - "id": 5, + "id": 8, "phase": "SEARCH", "name": "[SEARCH] 공지사항 검색", "action": "fill", @@ -74,7 +103,7 @@ "submit": true }, { - "id": 6, + "id": 9, "phase": "SEARCH", "name": "[SEARCH] 검색 결과 확인", "action": "verify_detail", @@ -84,7 +113,7 @@ "expected": "검색 기능 동작" }, { - "id": 7, + "id": 10, "phase": "SEARCH", "name": "[SEARCH] 검색 초기화", "action": "click_if_exists", @@ -92,7 +121,7 @@ "expected": "검색 초기화" }, { - "id": 8, + "id": 11, "phase": "READ", "name": "[READ] 공지사항 상세 보기", "action": "click_if_exists", @@ -103,7 +132,7 @@ } }, { - "id": 9, + "id": 12, "name": "상세 페이지 구조 확인", "action": "verify_elements", "checks": [ @@ -115,7 +144,7 @@ "expected": "상세 페이지 정상 표시" }, { - "id": 10, + "id": 13, "name": "첨부파일 확인", "action": "verify_elements", "checks": [ @@ -124,7 +153,7 @@ "expected": "첨부파일 영역 확인" }, { - "id": 11, + "id": 14, "name": "이전/다음 글 네비게이션", "action": "verify_elements", "checks": [ @@ -134,14 +163,14 @@ "expected": "글 네비게이션 표시" }, { - "id": 12, + "id": 15, "name": "목록으로 돌아가기", "action": "click_if_exists", "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", "expected": "목록 페이지로 복귀" }, { - "id": 13, + "id": 16, "name": "페이지네이션 확인", "action": "verify_elements", "checks": [ @@ -151,7 +180,7 @@ "expected": "페이지네이션 표시" }, { - "id": 14, + "id": 17, "name": "정렬 기능 확인", "action": "verify_elements", "checks": [ @@ -160,13 +189,19 @@ "expected": "정렬 기능 표시" }, { - "id": 15, + "id": 18, "name": "중요 공지 표시 확인", "action": "verify_elements", "checks": [ "중요/고정 공지 상단 표시 여부" ], "expected": "중요 공지 표시 확인" + }, + { + "id": 19, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "expectedAPIs": [ @@ -185,13 +220,19 @@ { "id": 3, "name": "검색/필터", - "steps": [5, 6, 7], + "steps": [ + 5, + 6, + 7 + ], "criteria": "검색 기능 동작" }, { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "공지사항 목록, 검색 기능, 목록 표시 존재" } ], diff --git a/department-add.json b/department-add.json index fd1dfc2..201f44c 100644 --- a/department-add.json +++ b/department-add.json @@ -4,7 +4,14 @@ "name": "부서관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "인사관리 > 부서관리 메뉴의 부서 트리/목록 조회 및 UI 검증 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -28,11 +35,21 @@ "level2": "부서관리", "expected": { "url_contains": "/hr/department", - "visible": ["부서관리"] + "visible": [ + "부서관리" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/hr/department-management" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -42,7 +59,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "부서 트리/목록 구조 확인", "action": "verify_elements", "checks": [ @@ -53,7 +76,7 @@ "expected": "부서관리 페이지 정상 표시" }, { - "id": 4, + "id": 6, "phase": "READ", "name": "[READ] 부서 목록 데이터 확인", "action": "verify_detail", @@ -63,14 +86,14 @@ "expected": "부서 목록 정상" }, { - "id": 5, + "id": 7, "phase": "READ", "name": "[READ] 첫 번째 부서 노드 클릭", "action": "click_if_exists", "target": "table tbody tr:first-child, [class*='tree'] > *:first-child, li:first-child" }, { - "id": 6, + "id": 8, "phase": "READ", "name": "[READ] 부서 상세 정보 확인", "action": "verify_detail", @@ -80,13 +103,13 @@ "expected": "부서 상세 정보 확인" }, { - "id": 7, + "id": 9, "name": "부서 추가 버튼 확인", "action": "click_if_exists", "target": "button:has-text('추가'), button:has-text('등록'), button:has-text('부서 추가')" }, { - "id": 8, + "id": 10, "name": "추가 폼/모달 확인", "action": "verify_elements", "checks": [ @@ -96,13 +119,13 @@ "expected": "부서 추가 폼 표시" }, { - "id": 9, + "id": 11, "name": "추가 모달 닫기", "action": "close_modal_if_open", "expected": "모달 닫힘" }, { - "id": 10, + "id": 12, "name": "부서 트리 구조 확인", "action": "verify_elements", "checks": [ @@ -111,7 +134,7 @@ "expected": "트리 구조 확인" }, { - "id": 11, + "id": 13, "name": "삭제 버튼 존재 확인", "action": "verify_elements", "checks": [ @@ -120,7 +143,19 @@ "expected": "삭제 기능 확인" }, { - "id": 12, + "id": 14, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 15, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 16, "name": "부서관리 페이지 최종 확인", "action": "verify_detail", "checks": [ @@ -140,7 +175,9 @@ { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "부서 목록, 추가 버튼 존재" } ], diff --git a/deposit-management.json b/deposit-management.json index 1fcd55f..5a2098c 100644 --- a/deposit-management.json +++ b/deposit-management.json @@ -3,7 +3,14 @@ "name": "입금관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "입금관리 목록 조회, 계정과목명 일괄변경, 상세 수정 기능 E2E 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -11,7 +18,11 @@ "navigation": { "targetUrl": "/accounting/deposits", "urlPattern": "/accounting/deposits|/ko/accounting/deposits", - "menuHints": ["입금관리", "입금 관리", "회계관리"] + "menuHints": [ + "입금관리", + "입금 관리", + "회계관리" + ] }, "menuNavigation": { "level1": "회계관리", @@ -25,8 +36,16 @@ "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", "level1": "회계관리", "level2": "입금관리", - "alternativeLevel1Names": ["회계관리", "회계 관리", "Accounting"], - "alternativeLevel2Names": ["입금관리", "입금 관리", "Deposits"], + "alternativeLevel1Names": [ + "회계관리", + "회계 관리", + "Accounting" + ], + "alternativeLevel2Names": [ + "입금관리", + "입금 관리", + "Deposits" + ], "scrollConfig": { "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", @@ -36,54 +55,97 @@ } }, "timeout": 60000, - "tags": ["accounting", "deposit", "crud"], - + "tags": [ + "accounting", + "deposit", + "crud" + ], "auth": { "username": "TestUser5", "password": "password123!" }, - "steps": [ { - "id": "step-0", + "id": 1, "name": "사이드바 메뉴 전체 펼치기", "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", "actions": [ - { "type": "scroll", "target": "sidebar", "direction": "top", "description": "사이드바 최상단으로 스크롤" }, - { "type": "wait", "duration": 300 }, - { "type": "evaluate", "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" }, - { "type": "wait", "duration": 2000 } + { + "type": "scroll", + "target": "sidebar", + "direction": "top", + "description": "사이드바 최상단으로 스크롤" + }, + { + "type": "wait", + "duration": 300 + }, + { + "type": "evaluate", + "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" + }, + { + "type": "wait", + "duration": 2000 + } ] }, { - "id": "step-1", + "id": 2, "name": "2단계 메뉴 진입: 회계관리 > 입금관리", "description": "사이드바를 스크롤하며 회계관리 > 입금관리 메뉴를 찾아 클릭", "actions": [ { "type": "scrollAndFind", "target": "회계관리", - "alternativeTexts": ["회계관리", "회계 관리", "Accounting"], + "alternativeTexts": [ + "회계관리", + "회계 관리", + "Accounting" + ], "scrollContainer": "sidebar", "maxAttempts": 10, "description": "스크롤하며 회계관리 메뉴 찾기" }, - { "type": "click_if_exists", "target": "회계관리", "description": "회계관리 메뉴 클릭" }, - { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, + { + "type": "click_if_exists", + "target": "회계관리", + "description": "회계관리 메뉴 클릭" + }, + { + "type": "wait", + "duration": 500, + "description": "서브메뉴 펼쳐지기 대기" + }, { "type": "scrollAndFind", "target": "입금관리", - "alternativeTexts": ["입금관리", "입금 관리", "Deposits"], + "alternativeTexts": [ + "입금관리", + "입금 관리", + "Deposits" + ], "scrollContainer": "submenu", "maxAttempts": 5, "description": "서브메뉴에서 입금관리 찾기" }, - { "type": "click_if_exists", "target": "입금관리", "description": "입금관리 메뉴 클릭" }, - { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 } + { + "type": "click_if_exists", + "target": "입금관리", + "description": "입금관리 메뉴 클릭" + }, + { + "type": "wait", + "target": "페이지 로드 완료", + "timeout": 10000 + } ], "expect": { "url": "/accounting/deposits", - "visible": ["입금관리", "총 입금"] + "visible": [ + "입금관리", + "총 입금" + ] }, "verification": [ "회계관리 메뉴가 펼쳐졌는지 확인", @@ -92,38 +154,87 @@ ] }, { - "id": "step-2", + "id": 3, "name": "목록 페이지 구조 확인", "description": "테이블 및 필터 요소 확인", "expect": { - "visible": ["입금일", "입금계좌", "입금자명", "입금금액", "거래처", "적요", "입금유형"], + "visible": [ + "입금일", + "입금계좌", + "입금자명", + "입금금액", + "거래처", + "적요", + "입금유형" + ], "elements": { - "statisticsCards": ["총 입금", "당월 입금", "거래처 미설정", "입금유형 미설정"], - "filters": ["계정과목명", "저장", "새로고침"], + "statisticsCards": [ + "총 입금", + "당월 입금", + "거래처 미설정", + "입금유형 미설정" + ], + "filters": [ + "계정과목명", + "저장", + "새로고침" + ], "pagination": true } } }, { - "id": "step-3", + "id": 4, "name": "계정과목명 드롭다운 옵션 확인", "description": "계정과목명 일괄변경 드롭다운 옵션 검증", "actions": [ - { "type": "click_if_exists", "target": "계정과목명 드롭다운", "description": "드롭다운 열기" } + { + "type": "click_if_exists", + "target": "계정과목명 드롭다운", + "description": "드롭다운 열기" + } ], "expect": { - "options": ["미설정", "매출대금", "선수금", "가수금", "임대수익", "이자수익", "보증금 반환", "차입금", "자본금", "부가세 환급", "기타"] + "options": [ + "미설정", + "매출대금", + "선수금", + "가수금", + "임대수익", + "이자수익", + "보증금 반환", + "차입금", + "자본금", + "부가세 환급", + "기타" + ] } }, { - "id": "step-4", + "id": 5, "name": "체크박스 선택 후 계정과목명 일괄변경", "description": "테이블 행 선택 후 계정과목명 일괄변경 저장", "actions": [ - { "type": "click_if_exists", "target": "첫 번째 행 체크박스", "description": "행 선택" }, - { "type": "click_if_exists", "target": "계정과목명 드롭다운", "description": "드롭다운 열기" }, - { "type": "click_if_exists", "target": "매출대금", "description": "매출대금 선택" }, - { "type": "click_if_exists", "target": "저장", "description": "저장 버튼 클릭" } + { + "type": "click_if_exists", + "target": "첫 번째 행 체크박스", + "description": "행 선택" + }, + { + "type": "click_if_exists", + "target": "계정과목명 드롭다운", + "description": "드롭다운 열기" + }, + { + "type": "click_if_exists", + "target": "매출대금", + "description": "매출대금 선택" + }, + { + "type": "click_if_exists", + "target": "저장", + "description": "저장 버튼 클릭" + } ], "expect": { "dialog": "확인 다이얼로그 표시", @@ -131,7 +242,7 @@ } }, { - "id": "step-4-1", + "id": 6, "name": "⚠️ 필수 검증: 계정과목명 변경 데이터 반영 확인", "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", "description": "저장 후 테이블에서 변경된 입금유형 값 확인", @@ -145,100 +256,215 @@ "knownBugReference": "BUG-SALES-20260115-001 (매출관리 동일 버그 확인 필요)" }, { - "id": "step-5", + "id": 7, "name": "입금 상세 페이지 이동", "description": "테이블 행 클릭하여 상세 페이지로 이동", "actions": [ - { "type": "click_if_exists", "target": "테이블 첫 번째 행", "description": "행 클릭 (체크박스 제외 영역)" } + { + "type": "click_if_exists", + "target": "테이블 첫 번째 행", + "description": "행 클릭 (체크박스 제외 영역)" + } ], "expect": { "url": "/accounting/deposits/{id}", - "visible": ["입금 상세", "기본 정보", "목록", "삭제", "수정"] - } - }, - { - "id": "step-6", - "name": "상세 페이지 읽기 모드 필드 확인", - "description": "수정 전 필드들이 비활성화 상태인지 확인", - "expect": { - "fields": [ - { "name": "입금일", "disabled": true }, - { "name": "입금계좌", "disabled": true }, - { "name": "입금자명", "disabled": true }, - { "name": "입금금액", "disabled": true }, - { "name": "적요", "disabled": true }, - { "name": "거래처", "disabled": true }, - { "name": "입금 유형", "disabled": true } + "visible": [ + "입금 상세", + "기본 정보", + "목록", + "삭제", + "수정" ] } }, { - "id": "step-7", + "id": 8, + "name": "상세 페이지 읽기 모드 필드 확인", + "description": "수정 전 필드들이 비활성화 상태인지 확인", + "expect": { + "fields": [ + { + "name": "입금일", + "disabled": true + }, + { + "name": "입금계좌", + "disabled": true + }, + { + "name": "입금자명", + "disabled": true + }, + { + "name": "입금금액", + "disabled": true + }, + { + "name": "적요", + "disabled": true + }, + { + "name": "거래처", + "disabled": true + }, + { + "name": "입금 유형", + "disabled": true + } + ] + } + }, + { + "id": 9, "name": "수정 모드 전환", "description": "수정 버튼 클릭하여 편집 모드로 전환", "click": "수정", "expect": { "url": "/accounting/deposits/{id}?mode=edit", - "visible": ["입금 수정", "취소", "저장"], - "notVisible": ["목록", "삭제", "수정"] + "visible": [ + "입금 수정", + "취소", + "저장" + ], + "notVisible": [ + "목록", + "삭제", + "수정" + ] } }, { - "id": "step-8", + "id": 10, "name": "수정 모드 필드 활성화 검증", "description": "수정 가능한 필드와 불가능한 필드 확인", "expect": { "fields": [ - { "name": "입금일", "disabled": true, "note": "은행데이터 - 수정 불가" }, - { "name": "입금계좌", "disabled": true, "note": "은행데이터 - 수정 불가" }, - { "name": "입금자명", "disabled": true, "note": "은행데이터 - 수정 불가" }, - { "name": "입금금액", "disabled": true, "note": "은행데이터 - 수정 불가" }, - { "name": "적요", "disabled": false, "editable": true }, - { "name": "거래처", "disabled": false, "type": "combobox", "editable": true }, - { "name": "입금 유형", "disabled": false, "type": "combobox", "editable": true } + { + "name": "입금일", + "disabled": true, + "note": "은행데이터 - 수정 불가" + }, + { + "name": "입금계좌", + "disabled": true, + "note": "은행데이터 - 수정 불가" + }, + { + "name": "입금자명", + "disabled": true, + "note": "은행데이터 - 수정 불가" + }, + { + "name": "입금금액", + "disabled": true, + "note": "은행데이터 - 수정 불가" + }, + { + "name": "적요", + "disabled": false, + "editable": true + }, + { + "name": "거래처", + "disabled": false, + "type": "combobox", + "editable": true + }, + { + "name": "입금 유형", + "disabled": false, + "type": "combobox", + "editable": true + } ] } }, { - "id": "step-9", + "id": 11, "name": "거래처 드롭다운 옵션 확인", "description": "거래처 선택 드롭다운 옵션 검증", "actions": [ - { "type": "click_if_exists", "target": "거래처 드롭다운", "description": "드롭다운 열기" } + { + "type": "click_if_exists", + "target": "거래처 드롭다운", + "description": "드롭다운 열기" + } ], "expect": { - "options": ["거래처테스트", "아크더레드", "코브라브릿지", "가우스전자", "아크아크"] + "options": [ + "거래처테스트", + "아크더레드", + "코브라브릿지", + "가우스전자", + "아크아크" + ] } }, { - "id": "step-10", + "id": 12, "name": "입금 유형 드롭다운 옵션 확인", "description": "입금 유형 선택 드롭다운 옵션 검증", "actions": [ - { "type": "click_if_exists", "target": "입금 유형 드롭다운", "description": "드롭다운 열기" } + { + "type": "click_if_exists", + "target": "입금 유형 드롭다운", + "description": "드롭다운 열기" + } ], "expect": { - "options": ["미설정", "매출대금", "선수금", "가수금", "임대수익", "이자수익", "보증금 반환", "차입금", "자본금", "부가세 환급", "기타"] + "options": [ + "미설정", + "매출대금", + "선수금", + "가수금", + "임대수익", + "이자수익", + "보증금 반환", + "차입금", + "자본금", + "부가세 환급", + "기타" + ] } }, { - "id": "step-11", + "id": 13, "name": "수정 데이터 입력", "description": "수정 가능한 필드에 테스트 데이터 입력", "form": { "fields": [ - { "name": "적요", "type": "text", "value": "테스트 적요 수정" } + { + "name": "적요", + "type": "text", + "value": "테스트 적요 수정" + } ] }, "actions": [ - { "type": "click_if_exists", "target": "거래처 드롭다운", "description": "거래처 드롭다운 열기" }, - { "type": "click_if_exists", "target": "거래처테스트", "description": "거래처 선택" }, - { "type": "click_if_exists", "target": "입금 유형 드롭다운", "description": "입금 유형 드롭다운 열기" }, - { "type": "click_if_exists", "target": "매출대금", "description": "매출대금 선택" } + { + "type": "click_if_exists", + "target": "거래처 드롭다운", + "description": "거래처 드롭다운 열기" + }, + { + "type": "click_if_exists", + "target": "거래처테스트", + "description": "거래처 선택" + }, + { + "type": "click_if_exists", + "target": "입금 유형 드롭다운", + "description": "입금 유형 드롭다운 열기" + }, + { + "type": "click_if_exists", + "target": "매출대금", + "description": "매출대금 선택" + } ] }, { - "id": "step-12", + "id": 14, "name": "저장 및 결과 확인", "description": "저장 버튼 클릭 후 데이터 반영 확인", "click": "저장", @@ -249,61 +475,105 @@ } }, { - "id": "step-12-1", + "id": 15, "name": "⚠️ 필수 검증: 수정 데이터 반영 확인", "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", "description": "저장 후 상세 페이지에서 변경된 값 확인", "expect": { "fields": [ - { "name": "적요", "value": "테스트 적요 수정" }, - { "name": "거래처", "value": "거래처테스트" }, - { "name": "입금 유형", "value": "매출대금" } + { + "name": "적요", + "value": "테스트 적요 수정" + }, + { + "name": "거래처", + "value": "거래처테스트" + }, + { + "name": "입금 유형", + "value": "매출대금" + } ] } }, { - "id": "step-13", + "id": 16, "name": "취소 버튼 동작 확인", "description": "수정 모드에서 취소 버튼 동작 검증", "actions": [ - { "type": "click_if_exists", "target": "수정", "description": "수정 모드 진입" }, - { "type": "click_if_exists", "target": "취소", "description": "취소 버튼 클릭" } + { + "type": "click_if_exists", + "target": "수정", + "description": "수정 모드 진입" + }, + { + "type": "click_if_exists", + "target": "취소", + "description": "취소 버튼 클릭" + } ], "expect": { "url": "/accounting/deposits/{id}", "mode": "view", - "visible": ["입금 상세", "목록", "삭제", "수정"] + "visible": [ + "입금 상세", + "목록", + "삭제", + "수정" + ] } }, { - "id": "step-14", + "id": 17, "name": "목록 버튼 동작 확인", "description": "목록 버튼 클릭하여 목록 페이지로 이동", "click": "목록", "expect": { "url": "/accounting/deposits", - "visible": ["입금관리", "총 입금"] + "visible": [ + "입금관리", + "총 입금" + ] } }, { - "id": "step-15", + "id": 18, "name": "필터 드롭다운 검증", "description": "목록 페이지 필터 드롭다운 옵션 확인", "note": "3개의 필터 드롭다운 존재 (거래처, 입금유형, 정렬)", "expect": { "filters": [ - { "name": "거래처 필터", "default": "전체" }, - { "name": "입금유형 필터", "default": "전체" }, - { "name": "정렬", "default": "최신순", "options": ["최신순", "등록순", "금액 높은순", "금액 낮은순"] } + { + "name": "거래처 필터", + "default": "전체" + }, + { + "name": "입금유형 필터", + "default": "전체" + }, + { + "name": "정렬", + "default": "최신순", + "options": [ + "최신순", + "등록순", + "금액 높은순", + "금액 낮은순" + ] + } ] } }, { - "id": "step-16", + "id": 19, "name": "날짜 필터 검증", "description": "날짜 필터 버튼 동작 확인", "actions": [ - { "type": "click_if_exists", "target": "당해년도", "description": "당해년도 버튼 클릭" } + { + "type": "click_if_exists", + "target": "당해년도", + "description": "당해년도 버튼 클릭" + } ], "expect": { "dateRange": { @@ -313,7 +583,7 @@ } }, { - "id": "step-17", + "id": 20, "name": "페이지네이션 동작 확인", "description": "페이지네이션 버튼 동작 검증", "expect": { @@ -325,15 +595,24 @@ } }, "actions": [ - { "type": "click_if_exists", "target": "다음", "description": "다음 페이지로 이동" } + { + "type": "click_if_exists", + "target": "다음", + "description": "다음 페이지로 이동" + } ], "expectAfterAction": { "currentPage": 2, "displayText": "전체 60개 중 21-40개 표시" } + }, + { + "id": 21, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], - "deleteTest": { "note": "삭제 테스트 - 이전에 스킵되었으나 CRUD 완전성을 위해 추가", "steps": [ @@ -342,11 +621,17 @@ "name": "삭제 버튼 클릭", "description": "상세 페이지에서 삭제 버튼 클릭", "actions": [ - { "type": "click_if_exists", "target": "삭제" } + { + "type": "click_if_exists", + "target": "삭제" + } ], "expect": { "confirmDialog": true, - "dialogText": ["삭제", "하시겠습니까"] + "dialogText": [ + "삭제", + "하시겠습니까" + ] } }, { @@ -354,7 +639,11 @@ "name": "삭제 확인", "description": "삭제 확인 다이얼로그에서 확인 클릭", "actions": [ - { "type": "click_if_exists", "target": "확인", "description": "삭제 확인" } + { + "type": "click_if_exists", + "target": "확인", + "description": "삭제 확인" + } ], "waitFor": { "type": "navigation", @@ -362,7 +651,11 @@ "timeout": 5000 }, "expect": { - "toast": ["삭제", "완료", "성공"], + "toast": [ + "삭제", + "완료", + "성공" + ], "url": "/accounting/deposits" } }, @@ -377,16 +670,16 @@ } ] }, - "knownBugs": [ { "id": "BUG-SALES-20260115-001", "description": "계정과목명 일괄변경 시 토스트 성공 표시되나 실제 데이터 미변경 (매출관리)", - "relatedSteps": ["step-4-1"], + "relatedSteps": [ + "step-4-1" + ], "note": "입금관리에서도 동일한 버그가 존재할 수 있으므로 step-4-1에서 검증 필수" } ], - "testData": { "sampleDeposit": { "date": "2025-12-28", @@ -403,55 +696,129 @@ "depositType": "매출대금" } }, - "pageStructure": { "listPage": { "url": "/accounting/deposits", "title": "입금관리", - "statistics": ["총 입금", "당월 입금", "거래처 미설정", "입금유형 미설정"], - "tableColumns": ["checkbox", "입금일", "입금계좌", "입금자명", "입금금액", "거래처", "적요", "입금유형", "action"], + "statistics": [ + "총 입금", + "당월 입금", + "거래처 미설정", + "입금유형 미설정" + ], + "tableColumns": [ + "checkbox", + "입금일", + "입금계좌", + "입금자명", + "입금금액", + "거래처", + "적요", + "입금유형", + "action" + ], "batchUpdate": { "label": "계정과목명", "saveButton": "저장" }, - "filters": ["거래처", "입금유형", "정렬"], - "dateFilters": ["당해년도", "전전월", "전월", "당월", "어제", "오늘"] + "filters": [ + "거래처", + "입금유형", + "정렬" + ], + "dateFilters": [ + "당해년도", + "전전월", + "전월", + "당월", + "어제", + "오늘" + ] }, "detailPage": { "url": "/accounting/deposits/{id}", "title": "입금 상세", - "buttons": ["목록", "삭제", "수정"], + "buttons": [ + "목록", + "삭제", + "수정" + ], "fields": { - "readOnly": ["입금일", "입금계좌", "입금자명", "입금금액"], - "editable": ["적요", "거래처", "입금 유형"] + "readOnly": [ + "입금일", + "입금계좌", + "입금자명", + "입금금액" + ], + "editable": [ + "적요", + "거래처", + "입금 유형" + ] } }, "editPage": { "url": "/accounting/deposits/{id}?mode=edit", "title": "입금 수정", - "buttons": ["취소", "저장"] + "buttons": [ + "취소", + "저장" + ] } }, - "dropdownOptions": { "accountSubject": { "label": "계정과목명", - "options": ["미설정", "매출대금", "선수금", "가수금", "임대수익", "이자수익", "보증금 반환", "차입금", "자본금", "부가세 환급", "기타"] + "options": [ + "미설정", + "매출대금", + "선수금", + "가수금", + "임대수익", + "이자수익", + "보증금 반환", + "차입금", + "자본금", + "부가세 환급", + "기타" + ] }, "depositType": { "label": "입금 유형", - "options": ["미설정", "매출대금", "선수금", "가수금", "임대수익", "이자수익", "보증금 반환", "차입금", "자본금", "부가세 환급", "기타"] + "options": [ + "미설정", + "매출대금", + "선수금", + "가수금", + "임대수익", + "이자수익", + "보증금 반환", + "차입금", + "자본금", + "부가세 환급", + "기타" + ] }, "vendor": { "label": "거래처", - "options": ["거래처테스트", "아크더레드", "코브라브릿지", "가우스전자", "아크아크"] + "options": [ + "거래처테스트", + "아크더레드", + "코브라브릿지", + "가우스전자", + "아크아크" + ] }, "sortOrder": { "label": "정렬", - "options": ["최신순", "등록순", "금액 높은순", "금액 낮은순"] + "options": [ + "최신순", + "등록순", + "금액 높은순", + "금액 낮은순" + ] } }, - "assertions": [ { "type": "url", diff --git a/draft-box.json b/draft-box.json index f71c4ac..919e900 100644 --- a/draft-box.json +++ b/draft-box.json @@ -4,7 +4,14 @@ "name": "기안함 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "결재관리 > 기안함 메뉴의 문서 목록 조회, 검색, 필터 기능 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -28,11 +35,21 @@ "level2": "기안함", "expected": { "url_contains": "/approval/draft", - "visible": ["기안함"] + "visible": [ + "기안함" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/approval/draft" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -43,7 +60,7 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, "name": "통계 카드 확인", "action": "verify_element", "checks": [ @@ -55,7 +72,7 @@ "expected": "통계 카드 4개 표시" }, { - "id": 4, + "id": 5, "name": "기안함 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -69,7 +86,13 @@ "expected": "기안함 테이블 컬럼 정상 표시" }, { - "id": 5, + "id": 6, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 7, "name": "데이터 로드 확인", "action": "verify_detail", "checks": [ @@ -78,7 +101,7 @@ "expected": "기안 문서 데이터 표시" }, { - "id": 6, + "id": 8, "phase": "SEARCH", "name": "[SEARCH] 검색 기능 테스트", "action": "fill", @@ -87,7 +110,7 @@ "submit": true }, { - "id": 7, + "id": 9, "phase": "SEARCH", "name": "[SEARCH] 검색 결과 확인", "action": "verify_detail", @@ -97,7 +120,7 @@ "expected": "검색 기능 동작" }, { - "id": 8, + "id": 10, "phase": "SEARCH", "name": "[SEARCH] 검색 초기화", "action": "click_if_exists", @@ -105,14 +128,14 @@ "expected": "검색 초기화" }, { - "id": 9, + "id": 11, "name": "필터 기능 테스트", "action": "click_if_exists", "target": "select, [role='combobox'], button:has-text('임시저장')", "expected": "필터 옵션 표시" }, { - "id": 10, + "id": 12, "phase": "READ", "name": "[READ] 문서 상세 보기", "action": "click_if_exists", @@ -122,7 +145,7 @@ } }, { - "id": 11, + "id": 13, "name": "상세 페이지/모달 확인", "action": "verify_element", "checks": [ @@ -132,13 +155,13 @@ "expected": "상세 정보 표시" }, { - "id": 12, + "id": 14, "name": "모달/상세 닫기", "action": "close_modal_if_open", "expected": "모달 닫힘" }, { - "id": 13, + "id": 15, "name": "페이지네이션 확인", "action": "verify_element", "checks": [ @@ -147,13 +170,19 @@ "expected": "페이지네이션 표시" }, { - "id": 14, + "id": 16, "name": "문서 작성 버튼 확인", "action": "verify_element", "checks": [ "문서 작성 또는 신규 작성 버튼 존재" ], "expected": "문서 작성 버튼 확인" + }, + { + "id": 17, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "expectedAPIs": [ @@ -172,13 +201,19 @@ { "id": 3, "name": "검색/필터", - "steps": [6, 7, 8], + "steps": [ + 6, + 7, + 8 + ], "criteria": "검색 기능 동작" }, { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "기안함 목록, 검색 기능, 문서 작성 버튼 존재" } ], diff --git a/employee-register.json b/employee-register.json index 419f123..2f5992c 100644 --- a/employee-register.json +++ b/employee-register.json @@ -3,7 +3,14 @@ "name": "직원 등록 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "신규 직원 정보를 입력하고 등록하는 E2E 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -11,7 +18,11 @@ "navigation": { "targetUrl": "/hr/employee-management", "urlPattern": "/hr/employee-management|/ko/hr/employee-management", - "menuHints": ["사원관리", "사원 관리", "인사관리"] + "menuHints": [ + "사원관리", + "사원 관리", + "인사관리" + ] }, "menuNavigation": { "level1": "인사관리", @@ -36,16 +47,18 @@ "timeout": 10000 }, "timeout": 60000, - "tags": ["hr", "employee", "crud"], - + "tags": [ + "hr", + "employee", + "crud" + ], "auth": { "username": "TestUser5", "password": "password123!" }, - "steps": [ { - "id": "step-0", + "id": 1, "name": "사이드바 메뉴 전체 펼치기", "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", "actions": [ @@ -54,7 +67,10 @@ "script": "document.querySelector('.sidebar-scroll, [class*=\"sidebar\"], nav')?.scrollTo({top: 0, behavior: 'instant'})", "description": "사이드바 스크롤 최상단으로 이동" }, - { "type": "wait", "duration": 300 }, + { + "type": "wait", + "duration": 300 + }, { "type": "evaluate", "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()", @@ -68,7 +84,7 @@ ] }, { - "id": "step-1", + "id": 2, "name": "인사관리 메뉴 진입", "description": "인사관리 > 직원관리 메뉴로 이동 (scrollAndFind 패턴)", "menuNavigation": { @@ -86,92 +102,176 @@ }, "expect": { "url": "/hr/employee-management", - "visible": ["사원관리", "사원 등록"] + "visible": [ + "사원관리", + "사원 등록" + ] } }, { - "id": "step-2", + "id": 3, "name": "사원 등록 페이지 이동", "click": "사원 등록", "expect": { "url": "/hr/employee-management?mode=new", - "visible": ["사원 등록", "사원 정보"] + "visible": [ + "사원 등록", + "사원 정보" + ] } }, { - "id": "step-3", + "id": 4, "name": "사원 정보 입력", "description": "기본 사원 정보 입력", "form": { "fields": [ - { "name": "이름 *", "type": "text", "value": "홍길동" }, - { "name": "주민등록번호", "type": "text", "value": "900101-1234567" }, - { "name": "휴대폰", "type": "text", "value": "010-1234-5678" }, - { "name": "이메일 *", "type": "text", "value": "test.employee@codebridge-x.com" }, - { "name": "연봉", "type": "number", "value": "50000000" } + { + "name": "이름 *", + "type": "text", + "value": "홍길동" + }, + { + "name": "주민등록번호", + "type": "text", + "value": "900101-1234567" + }, + { + "name": "휴대폰", + "type": "text", + "value": "010-1234-5678" + }, + { + "name": "이메일 *", + "type": "text", + "value": "test.employee@codebridge-x.com" + }, + { + "name": "연봉", + "type": "number", + "value": "50000000" + } ] } }, { - "id": "step-4", + "id": 5, "name": "급여계좌 정보 입력", "form": { "fields": [ - { "name": "은행명", "type": "text", "value": "신한은행" }, - { "name": "계좌번호", "type": "text", "value": "110-123-456789" }, - { "name": "예금주", "type": "text", "value": "홍길동" } + { + "name": "은행명", + "type": "text", + "value": "신한은행" + }, + { + "name": "계좌번호", + "type": "text", + "value": "110-123-456789" + }, + { + "name": "예금주", + "type": "text", + "value": "홍길동" + } ] } }, { - "id": "step-5", + "id": 6, "name": "사원 상세 정보 입력", "form": { "fields": [ - { "name": "사원코드", "type": "text", "value": "EMP2026001" }, - { "name": "남성", "type": "radio", "value": "true" }, - { "name": "상세주소를 입력해주세요", "type": "text", "value": "123번지 4층" } + { + "name": "사원코드", + "type": "text", + "value": "EMP2026001" + }, + { + "name": "남성", + "type": "radio", + "value": "true" + }, + { + "name": "상세주소를 입력해주세요", + "type": "text", + "value": "123번지 4층" + } ] } }, { - "id": "step-6", + "id": 7, "name": "인사 정보 입력", "form": { "fields": [ - { "name": "입사일", "type": "date", "value": "2026-01-14" } + { + "name": "입사일", + "type": "date", + "value": "2026-01-14" + } ] }, "actions": [ - { "type": "click_if_exists", "target": "고용형태 선택", "description": "고용형태 드롭다운 열기" }, - { "type": "click_if_exists", "target": "정규직", "description": "정규직 선택" }, - { "type": "click_if_exists", "target": "직급 선택", "description": "직급 드롭다운 열기" }, - { "type": "click_if_exists", "target": "사원", "description": "사원 직급 선택" } + { + "type": "click_if_exists", + "target": "고용형태 선택", + "description": "고용형태 드롭다운 열기" + }, + { + "type": "click_if_exists", + "target": "정규직", + "description": "정규직 선택" + }, + { + "type": "click_if_exists", + "target": "직급 선택", + "description": "직급 드롭다운 열기" + }, + { + "type": "click_if_exists", + "target": "사원", + "description": "사원 직급 선택" + } ] }, { - "id": "step-7", + "id": 8, "name": "사용자 정보 입력", "form": { "fields": [ - { "name": "아이디 *", "type": "text", "value": "testuser2026" }, - { "name": "비밀번호 *", "type": "text", "value": "password123!" }, - { "name": "비밀번호 확인 *", "type": "text", "value": "password123!" } + { + "name": "아이디 *", + "type": "text", + "value": "testuser2026" + }, + { + "name": "비밀번호 *", + "type": "text", + "value": "password123!" + }, + { + "name": "비밀번호 확인 *", + "type": "text", + "value": "password123!" + } ] } }, { - "id": "step-8", + "id": 9, "name": "등록 완료", "click": "등록", "waitFor": "사원관리", "expect": { "url": "/hr/employee-management", - "text": ["홍길동"] + "text": [ + "홍길동" + ] } }, { - "id": "step-8-1", + "id": 10, "name": "검색 기간 설정 - 유효 기간", "description": "등록된 사원의 입사일(2026-01-14)이 포함되는 기간으로 검색", "actions": [ @@ -200,7 +300,7 @@ } }, { - "id": "step-8-2", + "id": 11, "name": "검색 기간 설정 - 범위 외 기간", "description": "등록된 사원의 입사일이 포함되지 않는 기간으로 검색하여 검색되지 않음을 확인", "actions": [ @@ -240,19 +340,26 @@ } }, { - "id": "step-8-3", + "id": 12, "name": "검색 기간 초기화 및 전체 조회", "description": "검색 조건 초기화하여 등록된 사원이 다시 표시되는지 확인", "actions": [ { "type": "click_if_exists", "target": "초기화", - "fallbackSelectors": ["button:has-text('초기화')", ".reset-btn", "button:has-text('Reset')"] + "fallbackSelectors": [ + "button:has-text('초기화')", + ".reset-btn", + "button:has-text('Reset')" + ] }, { "type": "click_if_exists", "target": "검색", - "fallbackSelectors": ["button:has-text('검색')", ".search-btn"] + "fallbackSelectors": [ + "button:has-text('검색')", + ".search-btn" + ] } ], "expect": { @@ -266,7 +373,7 @@ } }, { - "id": "step-9", + "id": 13, "name": "등록된 직원 상세 페이지 이동", "description": "등록된 직원을 클릭하여 상세 페이지로 이동", "actions": [ @@ -278,42 +385,61 @@ ], "expect": { "url": "/hr/employee-management/{id}", - "visible": ["사원 상세", "수정", "삭제", "목록"] + "visible": [ + "사원 상세", + "수정", + "삭제", + "목록" + ] } }, { - "id": "step-10", + "id": 14, "name": "직원 수정 모드 전환", "description": "수정 버튼 클릭하여 편집 모드로 전환", "click": "수정", "expect": { "url": "/hr/employee-management/{id}?mode=edit", - "visible": ["사원 수정", "취소", "저장"] - } - }, - { - "id": "step-11", - "name": "직원 정보 수정", - "description": "휴대폰 번호 변경", - "form": { - "fields": [ - { "name": "휴대폰", "type": "text", "value": "010-9999-8888", "clear": true } + "visible": [ + "사원 수정", + "취소", + "저장" ] } }, { - "id": "step-12", + "id": 15, + "name": "직원 정보 수정", + "description": "휴대폰 번호 변경", + "form": { + "fields": [ + { + "name": "휴대폰", + "type": "text", + "value": "010-9999-8888", + "clear": true + } + ] + } + }, + { + "id": 16, "name": "수정 저장", "description": "수정된 직원 정보 저장", "click": "저장", "waitFor": "사원 상세", "expect": { - "toast": ["수정", "완료", "성공", "저장"], + "toast": [ + "수정", + "완료", + "성공", + "저장" + ], "url": "/hr/employee-management/{id}" } }, { - "id": "step-12-1", + "id": 17, "name": "⚠️ 필수 검증: 수정 데이터 반영 확인", "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", "description": "상세 페이지에서 수정된 휴대폰 번호 확인", @@ -325,37 +451,49 @@ } }, { - "id": "step-13", + "id": 18, "name": "직원 삭제", "description": "삭제 버튼 클릭하여 직원 삭제", "click": "삭제", "expect": { "confirmDialog": true, - "dialogText": ["삭제", "하시겠습니까"] + "dialogText": [ + "삭제", + "하시겠습니까" + ] } }, { - "id": "step-14", + "id": 19, "name": "삭제 확인", "description": "삭제 확인 다이얼로그에서 확인 클릭", "click": "확인", "waitFor": "사원관리", "expect": { - "toast": ["삭제", "완료", "성공"], + "toast": [ + "삭제", + "완료", + "성공" + ], "url": "/hr/employee-management" } }, { - "id": "step-15", + "id": 20, "name": "⚠️ 필수 검증: 삭제 데이터 반영 확인", "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 삭제 확인 필수!", "description": "목록에서 삭제된 직원이 없어졌는지 확인", "verify": { "tableNotContains": "홍길동" } + }, + { + "id": 21, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], - "assertions": [ { "type": "url", diff --git a/free-board.json b/free-board.json index ba53d52..2eea8bd 100644 --- a/free-board.json +++ b/free-board.json @@ -4,7 +4,14 @@ "name": "자유게시판 E2E 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "자유게시판 목록/검색/상세 기능 검증", "baseUrl": "https://dev.codebridge-x.com", @@ -26,54 +33,136 @@ "action": "menu_navigate", "level1": "게시판", "level2": "자유게시판", - "expected": { "url_contains": "/boards/free" } + "expected": { + "url_contains": "/boards/free" + } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/boards/free" + } + }, + { + "id": 3, "name": "목업 감지", "action": "verify_not_mockup" }, { - "id": 3, - "name": "자유게시판 페이지 확인", - "action": "verify_detail", - "checks": ["visible_text:게시판"] + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" }, { - "id": 4, + "id": 5, + "name": "자유게시판 페이지 확인", + "action": "verify_detail", + "checks": [ + "visible_text:게시판" + ] + }, + { + "id": 6, "name": "테이블 확인", "action": "verify_table" }, { - "id": 5, + "id": 7, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 8, "name": "검색 기능", "action": "search", "value": "테스트" }, { - "id": 6, - "name": "검색 후 페이지 확인", - "action": "verify_detail", - "checks": ["visible_text:게시판"] + "id": 9, + "name": "검색 결과 대기", + "action": "wait", + "duration": 1000 }, { - "id": 7, + "id": 10, + "name": "검색 결과 데이터 검증", + "action": "evaluate", + "script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Search result: ' + rows.length + ' rows';\n })()" + }, + { + "id": 11, + "name": "검색 초기화", + "action": "evaluate", + "script": "(() => {\n const selectors = ['input[type=\"search\"]', 'input[placeholder*=\"검색\"]', 'input[placeholder*=\"Search\"]', 'input[role=\"searchbox\"]', '[class*=\"search\"] input'];\n for (const sel of selectors) {\n const el = document.querySelector(sel);\n if (el) {\n const nativeSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;\n if (nativeSetter) nativeSetter.call(el, '');\n else el.value = '';\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n return 'Search cleared';\n }\n }\n return 'No search input found (ok)';\n })()" + }, + { + "id": 12, + "name": "검색 초기화 결과 대기", + "action": "wait", + "duration": 1000 + }, + { + "id": 13, + "name": "검색 초기화 및 복원 확인", + "action": "evaluate", + "script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Restored: ' + rows.length + ' rows';\n })()" + }, + { + "id": 14, + "name": "검색 후 페이지 확인", + "action": "verify_detail", + "checks": [ + "visible_text:게시판" + ] + }, + { + "id": 15, "name": "첫 번째 행 클릭", "action": "click_first_row" }, { - "id": 8, - "name": "상세 페이지 확인", - "action": "verify_detail", - "checks": ["visible_text:게시"] + "id": 16, + "name": "상세 페이지 로딩 대기", + "action": "wait", + "duration": 1000 }, { - "id": 9, + "id": 17, + "name": "상세 페이지 - 콘텐츠 확인", + "action": "evaluate", + "script": "(() => {\n const inputs = document.querySelectorAll('input:not([type=\"hidden\"]), textarea, select');\n const buttons = document.querySelectorAll('button');\n const hasDetail = inputs.length > 0 || document.body.innerText.includes('상세') || document.body.innerText.includes('수정');\n return hasDetail ? 'Detail page: ' + inputs.length + ' inputs, ' + buttons.length + ' buttons' : 'List page (no detail view)';\n })()" + }, + { + "id": 18, + "name": "상세 페이지 확인", + "action": "verify_detail", + "checks": [ + "visible_text:게시" + ] + }, + { + "id": 19, "name": "모달 닫기", "action": "close_modal_if_open" }, { - "id": 10, + "id": 20, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 21, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 22, "name": "목록 복귀", "action": "click_if_exists", "target": "button:has-text('목록'), a:has-text('목록')" diff --git a/hr-attendance-admin.json b/hr-attendance-admin.json index 1d42ef8..2612a44 100644 --- a/hr-attendance-admin.json +++ b/hr-attendance-admin.json @@ -3,7 +3,14 @@ "name": "근태관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "인사관리 > 근태관리 목록/상세 기능 검증", "baseUrl": "https://dev.codebridge-x.com", @@ -25,59 +32,95 @@ "action": "menu_navigate", "level1": "인사관리", "level2": "근태관리", - "expected": { "url_contains": "/hr/attendance" } + "expected": { + "url_contains": "/hr/attendance" + } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/hr/attendance-management" + } + }, + { + "id": 3, "name": "목업 감지", "action": "verify_not_mockup" }, - { - "id": 3, - "name": "근태관리 페이지 확인", - "action": "verify_detail", - "checks": ["visible_text:근태"] - }, { "id": 4, - "name": "UI 요소 확인", - "action": "verify_detail", - "checks": ["visible_text:관리"] + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" }, { "id": 5, + "name": "근태관리 페이지 확인", + "action": "verify_detail", + "checks": [ + "visible_text:근태" + ] + }, + { + "id": 6, + "name": "UI 요소 확인", + "action": "verify_detail", + "checks": [ + "visible_text:관리" + ] + }, + { + "id": 7, "name": "필터/검색 시도", "action": "click_if_exists", "target": "input[type='search'], input[type='date'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input, [class*='filter'] input" }, { - "id": 6, + "id": 8, "name": "대기", "action": "wait", "duration": 1000 }, { - "id": 7, + "id": 9, "name": "행 클릭 시도", "action": "click_if_exists", "target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']" }, { - "id": 8, + "id": 10, "name": "상세 확인", "action": "verify_detail", - "checks": ["visible_text:근태"] + "checks": [ + "visible_text:근태" + ] }, { - "id": 9, + "id": 11, "name": "모달 닫기", "action": "close_modal_if_open" }, { - "id": 10, + "id": 12, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 13, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 14, "name": "최종 확인", "action": "verify_detail", - "checks": ["visible_text:근태"] + "checks": [ + "visible_text:근태" + ] } ] } diff --git a/hr-attendance-status.json b/hr-attendance-status.json index 13c99a0..1accbda 100644 --- a/hr-attendance-status.json +++ b/hr-attendance-status.json @@ -3,7 +3,14 @@ "name": "근태현황 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "인사관리 > 근태현황 메뉴의 출퇴근 현황 조회/필터/출퇴근 기능 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -27,11 +34,23 @@ "level2": "근태현황", "expected": { "url_contains": "/hr/attendance", - "visible": ["근태현황", "출근", "퇴근"] + "visible": [ + "근태현황", + "출근", + "퇴근" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/hr/attendance" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -42,7 +61,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "근태 현황 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -55,7 +80,13 @@ "expected": "근태 현황 테이블 표시" }, { - "id": 4, + "id": 6, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 7, "name": "오늘 근태 상태 확인", "action": "verify_elements", "checks": [ @@ -65,7 +96,7 @@ "expected": "오늘 근태 상태 표시" }, { - "id": 5, + "id": 8, "name": "출근 버튼 확인", "action": "verify_elements", "checks": [ @@ -74,7 +105,7 @@ "expected": "출근 버튼 표시" }, { - "id": 6, + "id": 9, "name": "퇴근 버튼 확인", "action": "verify_elements", "checks": [ @@ -83,7 +114,7 @@ "expected": "퇴근 버튼 표시" }, { - "id": 7, + "id": 10, "phase": "FILTER", "name": "[FILTER] 기간 필터 - 월 선택", "action": "click_if_exists", @@ -91,7 +122,7 @@ "expected": "월 선택 열림" }, { - "id": 8, + "id": 11, "phase": "FILTER", "name": "[FILTER] 조회 적용", "action": "click_if_exists", @@ -99,7 +130,7 @@ "expected": "필터 적용됨" }, { - "id": 9, + "id": 12, "phase": "FILTER", "name": "[FILTER] 필터 결과 확인", "action": "verify_detail", @@ -109,7 +140,7 @@ "expected": "필터 동작 확인" }, { - "id": 10, + "id": 13, "name": "근무 시간 통계 확인", "action": "verify_elements", "checks": [ @@ -119,7 +150,7 @@ "expected": "근무 통계 표시" }, { - "id": 11, + "id": 14, "name": "지각/조퇴/결근 통계 확인", "action": "verify_elements", "checks": [ @@ -130,7 +161,7 @@ "expected": "근태 이상 통계 표시" }, { - "id": 12, + "id": 15, "phase": "READ", "name": "[READ] 특정 일자 상세 보기", "action": "click_if_exists", @@ -140,7 +171,7 @@ } }, { - "id": 13, + "id": 16, "name": "상세 근태 정보 확인", "action": "verify_detail", "checks": [ @@ -151,7 +182,7 @@ "expected": "상세 근태 정보 표시" }, { - "id": 14, + "id": 17, "name": "엑셀 다운로드 버튼 확인", "action": "verify_elements", "checks": [ @@ -160,13 +191,19 @@ "expected": "엑셀 다운로드 기능 표시" }, { - "id": 15, + "id": 18, "name": "인쇄 버튼 확인", "action": "verify_elements", "checks": [ "인쇄 버튼 존재" ], "expected": "인쇄 기능 표시" + }, + { + "id": 19, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "expectedAPIs": [ @@ -195,13 +232,19 @@ { "id": 3, "name": "검색/필터", - "steps": [7, 8, 9], + "steps": [ + 7, + 8, + 9 + ], "criteria": "기간 필터 동작" }, { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "근태 현황, 날짜 선택, 출퇴근 버튼 존재" } ], diff --git a/hr-card.json b/hr-card.json index a2a8c97..524a813 100644 --- a/hr-card.json +++ b/hr-card.json @@ -3,7 +3,14 @@ "name": "카드관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "인사관리 > 카드관리 목록/검색/상세 기능 검증", "baseUrl": "https://dev.codebridge-x.com", @@ -25,54 +32,136 @@ "action": "menu_navigate", "level1": "인사관리", "level2": "카드관리", - "expected": { "url_contains": "/hr/card" } + "expected": { + "url_contains": "/hr/card" + } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/hr/card-management" + } + }, + { + "id": 3, "name": "목업 감지", "action": "verify_not_mockup" }, { - "id": 3, - "name": "카드관리 페이지 확인", - "action": "verify_detail", - "checks": ["visible_text:카드"] + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" }, { - "id": 4, + "id": 5, + "name": "카드관리 페이지 확인", + "action": "verify_detail", + "checks": [ + "visible_text:카드" + ] + }, + { + "id": 6, "name": "테이블 확인", "action": "verify_table" }, { - "id": 5, + "id": 7, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 8, "name": "검색 기능", "action": "search", "value": "테스트" }, { - "id": 6, - "name": "검색 후 확인", - "action": "verify_detail", - "checks": ["visible_text:카드"] + "id": 9, + "name": "검색 결과 대기", + "action": "wait", + "duration": 1000 }, { - "id": 7, + "id": 10, + "name": "검색 결과 데이터 검증", + "action": "evaluate", + "script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Search result: ' + rows.length + ' rows';\n })()" + }, + { + "id": 11, + "name": "검색 초기화", + "action": "evaluate", + "script": "(() => {\n const selectors = ['input[type=\"search\"]', 'input[placeholder*=\"검색\"]', 'input[placeholder*=\"Search\"]', 'input[role=\"searchbox\"]', '[class*=\"search\"] input'];\n for (const sel of selectors) {\n const el = document.querySelector(sel);\n if (el) {\n const nativeSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;\n if (nativeSetter) nativeSetter.call(el, '');\n else el.value = '';\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n return 'Search cleared';\n }\n }\n return 'No search input found (ok)';\n })()" + }, + { + "id": 12, + "name": "검색 초기화 결과 대기", + "action": "wait", + "duration": 1000 + }, + { + "id": 13, + "name": "검색 초기화 및 복원 확인", + "action": "evaluate", + "script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Restored: ' + rows.length + ' rows';\n })()" + }, + { + "id": 14, + "name": "검색 후 확인", + "action": "verify_detail", + "checks": [ + "visible_text:카드" + ] + }, + { + "id": 15, "name": "첫 번째 행 클릭", "action": "click_first_row" }, { - "id": 8, - "name": "상세 확인", - "action": "verify_detail", - "checks": ["visible_text:카드"] + "id": 16, + "name": "상세 페이지 로딩 대기", + "action": "wait", + "duration": 1000 }, { - "id": 9, + "id": 17, + "name": "상세 페이지 - 콘텐츠 확인", + "action": "evaluate", + "script": "(() => {\n const inputs = document.querySelectorAll('input:not([type=\"hidden\"]), textarea, select');\n const buttons = document.querySelectorAll('button');\n const hasDetail = inputs.length > 0 || document.body.innerText.includes('상세') || document.body.innerText.includes('수정');\n return hasDetail ? 'Detail page: ' + inputs.length + ' inputs, ' + buttons.length + ' buttons' : 'List page (no detail view)';\n })()" + }, + { + "id": 18, + "name": "상세 확인", + "action": "verify_detail", + "checks": [ + "visible_text:카드" + ] + }, + { + "id": 19, "name": "모달 닫기", "action": "close_modal_if_open" }, { - "id": 10, + "id": 20, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 21, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 22, "name": "목록 복귀", "action": "click_if_exists", "target": "button:has-text('목록'), a:has-text('목록')" diff --git a/hr-department.json b/hr-department.json index aa13811..da4e486 100644 --- a/hr-department.json +++ b/hr-department.json @@ -3,7 +3,14 @@ "name": "부서관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "인사관리 > 부서관리 목록/상세 기능 검증", "baseUrl": "https://dev.codebridge-x.com", @@ -25,59 +32,95 @@ "action": "menu_navigate", "level1": "인사관리", "level2": "부서관리", - "expected": { "url_contains": "/hr/department" } + "expected": { + "url_contains": "/hr/department" + } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/hr/department-management" + } + }, + { + "id": 3, "name": "목업 감지", "action": "verify_not_mockup" }, - { - "id": 3, - "name": "부서관리 페이지 확인", - "action": "verify_detail", - "checks": ["visible_text:부서"] - }, { "id": 4, - "name": "UI 요소 확인", - "action": "verify_detail", - "checks": ["visible_text:관리"] + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" }, { "id": 5, + "name": "부서관리 페이지 확인", + "action": "verify_detail", + "checks": [ + "visible_text:부서" + ] + }, + { + "id": 6, + "name": "UI 요소 확인", + "action": "verify_detail", + "checks": [ + "visible_text:관리" + ] + }, + { + "id": 7, "name": "검색 입력 시도", "action": "click_if_exists", "target": "input[type='search'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input" }, { - "id": 6, + "id": 8, "name": "대기", "action": "wait", "duration": 1000 }, { - "id": 7, + "id": 9, "name": "행 클릭 시도", "action": "click_if_exists", "target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']" }, { - "id": 8, + "id": 10, "name": "상세 확인", "action": "verify_detail", - "checks": ["visible_text:부서"] + "checks": [ + "visible_text:부서" + ] }, { - "id": 9, + "id": 11, "name": "모달 닫기", "action": "close_modal_if_open" }, { - "id": 10, + "id": 12, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 13, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 14, "name": "최종 확인", "action": "verify_detail", - "checks": ["visible_text:부서"] + "checks": [ + "visible_text:부서" + ] } ] } diff --git a/hr-employee.json b/hr-employee.json index 4e0a946..d817c77 100644 --- a/hr-employee.json +++ b/hr-employee.json @@ -3,7 +3,14 @@ "name": "사원관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "인사관리 > 사원관리 목록/검색/상세 기능 검증", "baseUrl": "https://dev.codebridge-x.com", @@ -25,54 +32,136 @@ "action": "menu_navigate", "level1": "인사관리", "level2": "사원관리", - "expected": { "url_contains": "/hr/employee" } + "expected": { + "url_contains": "/hr/employee" + } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/hr/employee-management" + } + }, + { + "id": 3, "name": "목업 감지", "action": "verify_not_mockup" }, { - "id": 3, - "name": "사원관리 페이지 확인", - "action": "verify_detail", - "checks": ["visible_text:사원"] + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" }, { - "id": 4, + "id": 5, + "name": "사원관리 페이지 확인", + "action": "verify_detail", + "checks": [ + "visible_text:사원" + ] + }, + { + "id": 6, "name": "테이블 확인", "action": "verify_table" }, { - "id": 5, + "id": 7, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 8, "name": "검색 기능", "action": "search", "value": "테스트" }, { - "id": 6, - "name": "검색 후 확인", - "action": "verify_detail", - "checks": ["visible_text:사원"] + "id": 9, + "name": "검색 결과 대기", + "action": "wait", + "duration": 1000 }, { - "id": 7, + "id": 10, + "name": "검색 결과 데이터 검증", + "action": "evaluate", + "script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Search result: ' + rows.length + ' rows';\n })()" + }, + { + "id": 11, + "name": "검색 초기화", + "action": "evaluate", + "script": "(() => {\n const selectors = ['input[type=\"search\"]', 'input[placeholder*=\"검색\"]', 'input[placeholder*=\"Search\"]', 'input[role=\"searchbox\"]', '[class*=\"search\"] input'];\n for (const sel of selectors) {\n const el = document.querySelector(sel);\n if (el) {\n const nativeSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;\n if (nativeSetter) nativeSetter.call(el, '');\n else el.value = '';\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n return 'Search cleared';\n }\n }\n return 'No search input found (ok)';\n })()" + }, + { + "id": 12, + "name": "검색 초기화 결과 대기", + "action": "wait", + "duration": 1000 + }, + { + "id": 13, + "name": "검색 초기화 및 복원 확인", + "action": "evaluate", + "script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Restored: ' + rows.length + ' rows';\n })()" + }, + { + "id": 14, + "name": "검색 후 확인", + "action": "verify_detail", + "checks": [ + "visible_text:사원" + ] + }, + { + "id": 15, "name": "첫 번째 행 클릭", "action": "click_first_row" }, { - "id": 8, - "name": "상세 확인", - "action": "verify_detail", - "checks": ["visible_text:사원"] + "id": 16, + "name": "상세 페이지 로딩 대기", + "action": "wait", + "duration": 1000 }, { - "id": 9, + "id": 17, + "name": "상세 페이지 - 콘텐츠 확인", + "action": "evaluate", + "script": "(() => {\n const inputs = document.querySelectorAll('input:not([type=\"hidden\"]), textarea, select');\n const buttons = document.querySelectorAll('button');\n const hasDetail = inputs.length > 0 || document.body.innerText.includes('상세') || document.body.innerText.includes('수정');\n return hasDetail ? 'Detail page: ' + inputs.length + ' inputs, ' + buttons.length + ' buttons' : 'List page (no detail view)';\n })()" + }, + { + "id": 18, + "name": "상세 확인", + "action": "verify_detail", + "checks": [ + "visible_text:사원" + ] + }, + { + "id": 19, "name": "모달 닫기", "action": "close_modal_if_open" }, { - "id": 10, + "id": 20, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 21, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 22, "name": "목록 복귀", "action": "click_if_exists", "target": "button:has-text('목록'), a:has-text('목록')" diff --git a/hr-salary.json b/hr-salary.json index 4c8141b..1f38709 100644 --- a/hr-salary.json +++ b/hr-salary.json @@ -3,7 +3,14 @@ "name": "급여관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "인사관리 > 급여관리 목록/검색/상세 기능 검증", "baseUrl": "https://dev.codebridge-x.com", @@ -25,54 +32,136 @@ "action": "menu_navigate", "level1": "인사관리", "level2": "급여관리", - "expected": { "url_contains": "/hr/salary" } + "expected": { + "url_contains": "/hr/salary" + } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/hr/salary-management" + } + }, + { + "id": 3, "name": "목업 감지", "action": "verify_not_mockup" }, { - "id": 3, - "name": "급여관리 페이지 확인", - "action": "verify_detail", - "checks": ["visible_text:급여"] + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" }, { - "id": 4, + "id": 5, + "name": "급여관리 페이지 확인", + "action": "verify_detail", + "checks": [ + "visible_text:급여" + ] + }, + { + "id": 6, "name": "테이블 확인", "action": "verify_table" }, { - "id": 5, + "id": 7, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 8, "name": "검색 기능", "action": "search", "value": "테스트" }, { - "id": 6, - "name": "검색 후 확인", - "action": "verify_detail", - "checks": ["visible_text:급여"] + "id": 9, + "name": "검색 결과 대기", + "action": "wait", + "duration": 1000 }, { - "id": 7, + "id": 10, + "name": "검색 결과 데이터 검증", + "action": "evaluate", + "script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Search result: ' + rows.length + ' rows';\n })()" + }, + { + "id": 11, + "name": "검색 초기화", + "action": "evaluate", + "script": "(() => {\n const selectors = ['input[type=\"search\"]', 'input[placeholder*=\"검색\"]', 'input[placeholder*=\"Search\"]', 'input[role=\"searchbox\"]', '[class*=\"search\"] input'];\n for (const sel of selectors) {\n const el = document.querySelector(sel);\n if (el) {\n const nativeSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;\n if (nativeSetter) nativeSetter.call(el, '');\n else el.value = '';\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n return 'Search cleared';\n }\n }\n return 'No search input found (ok)';\n })()" + }, + { + "id": 12, + "name": "검색 초기화 결과 대기", + "action": "wait", + "duration": 1000 + }, + { + "id": 13, + "name": "검색 초기화 및 복원 확인", + "action": "evaluate", + "script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Restored: ' + rows.length + ' rows';\n })()" + }, + { + "id": 14, + "name": "검색 후 확인", + "action": "verify_detail", + "checks": [ + "visible_text:급여" + ] + }, + { + "id": 15, "name": "첫 번째 행 클릭", "action": "click_first_row" }, { - "id": 8, - "name": "상세 확인", - "action": "verify_detail", - "checks": ["visible_text:급여"] + "id": 16, + "name": "상세 페이지 로딩 대기", + "action": "wait", + "duration": 1000 }, { - "id": 9, + "id": 17, + "name": "상세 페이지 - 콘텐츠 확인", + "action": "evaluate", + "script": "(() => {\n const inputs = document.querySelectorAll('input:not([type=\"hidden\"]), textarea, select');\n const buttons = document.querySelectorAll('button');\n const hasDetail = inputs.length > 0 || document.body.innerText.includes('상세') || document.body.innerText.includes('수정');\n return hasDetail ? 'Detail page: ' + inputs.length + ' inputs, ' + buttons.length + ' buttons' : 'List page (no detail view)';\n })()" + }, + { + "id": 18, + "name": "상세 확인", + "action": "verify_detail", + "checks": [ + "visible_text:급여" + ] + }, + { + "id": 19, "name": "모달 닫기", "action": "close_modal_if_open" }, { - "id": 10, + "id": 20, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 21, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 22, "name": "목록 복귀", "action": "click_if_exists", "target": "button:has-text('목록'), a:has-text('목록')" diff --git a/hr-vacation.json b/hr-vacation.json index c7df2ac..70c55c5 100644 --- a/hr-vacation.json +++ b/hr-vacation.json @@ -53,6 +53,14 @@ }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/hr/vacation-management" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -63,7 +71,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "휴가 현황 카드 확인", "action": "verify_elements", "checks": [ @@ -74,7 +88,7 @@ "expected": "휴가 현황 카드 정상 표시" }, { - "id": 4, + "id": 6, "name": "휴가 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -87,7 +101,13 @@ "expected": "휴가 테이블 컬럼 정상 표시" }, { - "id": 5, + "id": 7, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 8, "phase": "CREATE", "name": "[CREATE] 휴가 신청 버튼 클릭", "action": "click_if_exists", @@ -98,7 +118,7 @@ } }, { - "id": 6, + "id": 9, "phase": "CREATE", "name": "[CREATE] 휴가 정보 입력", "action": "click_if_exists", @@ -128,7 +148,7 @@ "target": "form, [role='dialog'], .modal" }, { - "id": 7, + "id": 10, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 신청 저장", "action": "click_if_exists", @@ -142,14 +162,23 @@ "expected": "휴가 신청 완료" }, { - "id": "7-modal-close", + "id": 11, + "phase": "CREATE", + "name": "[CREATE] 저장 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "등록|완료|성공|저장" + } + }, + { + "id": 12, "phase": "CREATE", "name": "[CREATE] 모달 닫기 확인", "action": "close_modal_if_open", "expected": "모달 닫힘" }, { - "id": 8, + "id": 13, "phase": "CREATE", "name": "[CREATE] 신청 결과 확인", "action": "verify_detail", @@ -164,7 +193,7 @@ } }, { - "id": 9, + "id": 14, "phase": "READ", "name": "[READ] 휴가 상세 페이지 진입", "action": "click_if_exists", @@ -179,7 +208,7 @@ } }, { - "id": 10, + "id": 15, "phase": "READ", "name": "[READ] 상세 정보 확인", "action": "verify_detail", @@ -193,7 +222,7 @@ "expected": "입력한 데이터와 일치" }, { - "id": 11, + "id": 16, "phase": "UPDATE", "name": "[UPDATE] 수정 모드 진입", "action": "click_if_exists", @@ -204,7 +233,7 @@ } }, { - "id": 12, + "id": 17, "phase": "UPDATE", "name": "[UPDATE] 사유 수정", "action": "click_if_exists", @@ -213,7 +242,7 @@ "clear": true }, { - "id": 13, + "id": 18, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 수정 저장", "action": "click_if_exists", @@ -227,7 +256,16 @@ "expected": "수정 완료" }, { - "id": 14, + "id": 19, + "phase": "UPDATE", + "name": "[UPDATE] 수정 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "수정|완료|성공|저장" + } + }, + { + "id": 20, "phase": "UPDATE", "name": "[UPDATE] 수정 결과 확인", "action": "verify_detail", @@ -237,7 +275,7 @@ "expected": "수정된 데이터 반영" }, { - "id": 15, + "id": 21, "phase": "DELETE", "name": "[DELETE] 취소 버튼 클릭", "action": "click_if_exists", @@ -248,7 +286,7 @@ } }, { - "id": 16, + "id": 22, "phase": "DELETE", "name": "[DELETE] 필수 검증 #6: 취소 확인", "action": "click_if_exists", @@ -261,7 +299,7 @@ "expected": "휴가 취소 완료 및 목록 복귀" }, { - "id": 17, + "id": 23, "phase": "DELETE", "name": "[DELETE] 취소 결과 확인", "action": "verify_detail", @@ -272,7 +310,7 @@ } }, { - "id": 18, + "id": 24, "phase": "VERIFY", "name": "[VERIFY] 연차 잔여일 확인", "action": "verify_elements", @@ -280,6 +318,12 @@ "잔여 연차가 원래 값으로 복원" ], "expected": "휴가 취소 후 연차 복원 확인" + }, + { + "id": 25, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "expectedAPIs": [ @@ -349,4 +393,4 @@ "onDeleteFail": "테스트 휴가 수동 취소 필요", "cleanupRequired": "E2E_ 접두사 휴가 신청은 테스트 데이터" } -} \ No newline at end of file +} diff --git a/inventory-status.json b/inventory-status.json index 3ad0a6a..dcb2e38 100644 --- a/inventory-status.json +++ b/inventory-status.json @@ -3,7 +3,14 @@ "name": "재고현황 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "자재관리 > 재고현황 페이지의 재고 조회 및 엑셀 다운로드 기능을 테스트하는 E2E 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -11,7 +18,11 @@ "navigation": { "targetUrl": "/material/stock-status", "urlPattern": "/material/stock-status|/ko/material/stock-status", - "menuHints": ["재고현황", "재고 현황", "자재관리"] + "menuHints": [ + "재고현황", + "재고 현황", + "자재관리" + ] }, "menuNavigation": { "level1": "자재관리", @@ -41,16 +52,18 @@ "expectedUrl": "/material/stock-status" }, "timeout": 90000, - "tags": ["material", "inventory", "read-only"], - + "tags": [ + "material", + "inventory", + "read-only" + ], "auth": { "username": "TestUser5", "password": "password123!" }, - "steps": [ { - "id": "step-0", + "id": 1, "name": "사이드바 메뉴 전체 펼치기", "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", "actions": [ @@ -58,16 +71,22 @@ "type": "evaluate", "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" }, - { "type": "wait", "duration": 300 }, + { + "type": "wait", + "duration": 300 + }, { "type": "evaluate", "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" }, - { "type": "wait", "duration": 2000 } + { + "type": "wait", + "duration": 2000 + } ] }, { - "id": "step-1", + "id": 2, "name": "자재관리 메뉴 진입", "description": "자재관리 > 재고현황 메뉴로 이동", "actions": [ @@ -78,13 +97,25 @@ "scrollStep": 200, "maxAttempts": 5 }, - { "type": "click_if_exists", "target": "자재관리" }, - { "type": "wait", "duration": 500 }, - { "type": "click_if_exists", "target": "재고현황" } + { + "type": "click_if_exists", + "target": "자재관리" + }, + { + "type": "wait", + "duration": 500 + }, + { + "type": "click_if_exists", + "target": "재고현황" + } ], "expect": { "url": "/material/stock-status", - "visible": ["재고 목록", "엑셀 다운로드"] + "visible": [ + "재고 목록", + "엑셀 다운로드" + ] }, "fallback": { "type": "navigate", @@ -92,21 +123,44 @@ } }, { - "id": "step-2", + "id": 3, "name": "페이지 구조 확인", "description": "통계 카드와 테이블 구조 확인", "verify": { - "visible": ["전체 품목", "정상 재고", "재고 부족", "재고 없음"], - "tableColumns": ["번호", "품목코드", "품목명", "품목유형", "단위", "재고량", "안전재고", "LOT", "상태", "위치"] + "visible": [ + "전체 품목", + "정상 재고", + "재고 부족", + "재고 없음" + ], + "tableColumns": [ + "번호", + "품목코드", + "품목명", + "품목유형", + "단위", + "재고량", + "안전재고", + "LOT", + "상태", + "위치" + ] } }, { - "id": "step-3", + "id": 4, "name": "필수 검증 #3: 품목유형 탭 필터 - 원자재", "description": "원자재 탭 클릭하여 필터링 확인", "actions": [ - { "type": "click_if_exists", "target": "원자재", "role": "tab" }, - { "type": "wait", "duration": 500 } + { + "type": "click_if_exists", + "target": "원자재", + "role": "tab" + }, + { + "type": "wait", + "duration": 500 + } ], "expect": { "tabActive": "원자재", @@ -114,12 +168,19 @@ } }, { - "id": "step-4", + "id": 5, "name": "필수 검증 #3: 품목유형 탭 필터 - 부자재", "description": "부자재 탭 클릭하여 필터링 확인", "actions": [ - { "type": "click_if_exists", "target": "부자재", "role": "tab" }, - { "type": "wait", "duration": 500 } + { + "type": "click_if_exists", + "target": "부자재", + "role": "tab" + }, + { + "type": "wait", + "duration": 500 + } ], "expect": { "tabActive": "부자재", @@ -127,12 +188,19 @@ } }, { - "id": "step-5", + "id": 6, "name": "필수 검증 #3: 품목유형 탭 필터 - 소모품", "description": "소모품 탭 클릭하여 필터링 확인", "actions": [ - { "type": "click_if_exists", "target": "소모품", "role": "tab" }, - { "type": "wait", "duration": 500 } + { + "type": "click_if_exists", + "target": "소모품", + "role": "tab" + }, + { + "type": "wait", + "duration": 500 + } ], "expect": { "tabActive": "소모품", @@ -140,12 +208,19 @@ } }, { - "id": "step-6", + "id": 7, "name": "전체 탭으로 복귀", "description": "전체 탭 클릭하여 모든 재고 표시", "actions": [ - { "type": "click_if_exists", "target": "전체", "role": "tab" }, - { "type": "wait", "duration": 300 } + { + "type": "click_if_exists", + "target": "전체", + "role": "tab" + }, + { + "type": "wait", + "duration": 300 + } ], "expect": { "tabActive": "전체", @@ -153,12 +228,18 @@ } }, { - "id": "step-7", + "id": 8, "name": "필수 검증 #1: 엑셀 다운로드", "description": "엑셀 다운로드 버튼 동작 확인", "actions": [ - { "type": "click_if_exists", "target": "엑셀 다운로드" }, - { "type": "wait", "duration": 1000 } + { + "type": "click_if_exists", + "target": "엑셀 다운로드" + }, + { + "type": "wait", + "duration": 1000 + } ], "expect": { "downloadTriggered": true, @@ -169,7 +250,7 @@ } }, { - "id": "step-8", + "id": 9, "name": "재고 상세 열기", "description": "재고 항목 클릭하여 상세 보기", "actions": [ @@ -180,32 +261,54 @@ ], "expect": { "pageOrModal": "재고 상세", - "visible": ["품목코드", "품목명", "재고량", "LOT"] + "visible": [ + "품목코드", + "품목명", + "재고량", + "LOT" + ] } }, { - "id": "step-9", + "id": 10, "name": "상세 닫기", "description": "ESC 키로 상세 닫기 또는 뒤로가기", "actions": [ - { "type": "press", "key": "Escape" }, - { "type": "wait", "duration": 300 } + { + "type": "press", + "key": "Escape" + }, + { + "type": "wait", + "duration": 300 + } ] }, { - "id": "step-10", + "id": 11, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 12, "name": "페이지네이션 확인", "description": "페이지네이션 동작 확인", "actions": [ - { "type": "click_if_exists", "target": "다음" }, - { "type": "wait", "duration": 500 } + { + "type": "click_if_exists", + "target": "다음" + }, + { + "type": "wait", + "duration": 500 + } ], "expect": { "pageChanged": true } } ], - "assertions": [ { "type": "url", @@ -218,7 +321,6 @@ "message": "엑셀 다운로드 버튼이 표시되어야 함" } ], - "mandatoryVerifications": { "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", "items": [ @@ -238,13 +340,33 @@ } ] }, - "notes": { "testScope": "재고현황 조회 및 필터링, 엑셀 다운로드 테스트", "pageType": "조회 전용 (입고관리에서 재고 증가, 출하관리에서 재고 감소)", - "statsCards": ["전체 품목", "정상 재고", "재고 부족", "재고 없음"], - "typeTabs": ["전체", "원자재", "부자재", "소모품"], - "tableColumns": ["번호", "품목코드", "품목명", "품목유형", "단위", "재고량", "안전재고", "LOT", "상태", "위치"], + "statsCards": [ + "전체 품목", + "정상 재고", + "재고 부족", + "재고 없음" + ], + "typeTabs": [ + "전체", + "원자재", + "부자재", + "소모품" + ], + "tableColumns": [ + "번호", + "품목코드", + "품목명", + "품목유형", + "단위", + "재고량", + "안전재고", + "LOT", + "상태", + "위치" + ], "prerequisites": "로그인된 사용자" } } diff --git a/item-management.json b/item-management.json index 95ad6d1..4dc479f 100644 --- a/item-management.json +++ b/item-management.json @@ -3,7 +3,14 @@ "name": "품목관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "생산관리 > 품목관리 메뉴의 품목 목록 조회 및 UI 검증 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -27,7 +34,10 @@ "level2": "품목관리", "expected": { "url_contains": "/production", - "visible": ["품목관리", "품목"] + "visible": [ + "품목관리", + "품목" + ] } }, { @@ -43,6 +53,12 @@ }, { "id": 3, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 4, "name": "품목 UI 구조 확인", "action": "verify_elements", "checks": [ @@ -54,7 +70,7 @@ "expected": "품목관리 UI 정상 표시" }, { - "id": 4, + "id": 5, "name": "테이블 구조 확인", "action": "verify_table", "checks": [ @@ -65,7 +81,13 @@ "expected": "품목 테이블 표시" }, { - "id": 5, + "id": 6, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 7, "phase": "READ", "name": "[READ] 품목 목록 데이터 확인", "action": "verify_detail", @@ -75,14 +97,14 @@ "expected": "품목 목록 정상" }, { - "id": 6, + "id": 8, "phase": "READ", "name": "[READ] 첫 번째 행 클릭", "action": "click_if_exists", "target": "table tbody tr:first-child, button:has-text('상세')" }, { - "id": 7, + "id": 9, "phase": "READ", "name": "[READ] 품목 상세 정보 확인", "action": "verify_detail", @@ -92,25 +114,25 @@ "expected": "품목 상세 정보 확인" }, { - "id": 8, + "id": 10, "name": "상세 모달/페이지 닫기", "action": "click_if_exists", "target": "button:has-text('닫기'), button:has-text('Close'), button:has-text('목록'), [class*='close']" }, { - "id": 9, + "id": 11, "name": "모달 닫기 확인", "action": "close_modal_if_open", "expected": "모달 닫힘" }, { - "id": 10, + "id": 12, "name": "탭/필터 기능 확인", "action": "click_if_exists", "target": "button:has-text('전체'), [class*='tab']:has-text('전체')" }, { - "id": 11, + "id": 13, "name": "등록 버튼 존재 확인", "action": "verify_elements", "checks": [ @@ -119,7 +141,19 @@ "expected": "등록 버튼 표시" }, { - "id": 12, + "id": 14, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 15, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 16, "name": "품목관리 페이지 최종 확인", "action": "verify_elements", "checks": [ @@ -140,7 +174,9 @@ { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "품목 목록, 등록 버튼, 검색 기능 존재" } ], diff --git a/item-master.json b/item-master.json index 30d98ac..ae74e8d 100644 --- a/item-master.json +++ b/item-master.json @@ -3,7 +3,14 @@ "name": "품목기준관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "품목관리 > 품목기준관리 목록/상세 기능 검증", "baseUrl": "https://dev.codebridge-x.com", @@ -25,59 +32,95 @@ "action": "menu_navigate", "level1": "품목관리", "level2": "품목기준관리", - "expected": { "url_contains": "/master-data" } + "expected": { + "url_contains": "/master-data" + } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/master-data/item-master-data-management" + } + }, + { + "id": 3, "name": "목업 감지", "action": "verify_not_mockup" }, - { - "id": 3, - "name": "품목기준관리 페이지 확인", - "action": "verify_detail", - "checks": ["visible_text:품목"] - }, { "id": 4, - "name": "UI 요소 확인", - "action": "verify_detail", - "checks": ["visible_text:관리"] + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" }, { "id": 5, + "name": "품목기준관리 페이지 확인", + "action": "verify_detail", + "checks": [ + "visible_text:품목" + ] + }, + { + "id": 6, + "name": "UI 요소 확인", + "action": "verify_detail", + "checks": [ + "visible_text:관리" + ] + }, + { + "id": 7, "name": "검색 입력 시도", "action": "click_if_exists", "target": "input[type='search'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input" }, { - "id": 6, + "id": 8, "name": "대기", "action": "wait", "duration": 1000 }, { - "id": 7, + "id": 9, "name": "행 클릭 시도", "action": "click_if_exists", "target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']" }, { - "id": 8, + "id": 10, "name": "상세 확인", "action": "verify_detail", - "checks": ["visible_text:품목"] + "checks": [ + "visible_text:품목" + ] }, { - "id": 9, + "id": 11, "name": "모달 닫기", "action": "close_modal_if_open" }, { - "id": 10, + "id": 12, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 13, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 14, "name": "최종 확인", "action": "verify_detail", - "checks": ["visible_text:품목"] + "checks": [ + "visible_text:품목" + ] } ] } diff --git a/login.json b/login.json index c17d35e..d58f26c 100644 --- a/login.json +++ b/login.json @@ -273,6 +273,12 @@ "홍킬동" ] } + }, + { + "id": 24, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "requiredVerifications": [ @@ -318,4 +324,4 @@ }, "invalidPassword": "wrongpassword" } -} \ No newline at end of file +} diff --git a/material-receiving.json b/material-receiving.json index 6d84fa5..efe53bd 100644 --- a/material-receiving.json +++ b/material-receiving.json @@ -3,7 +3,14 @@ "name": "입고관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "자재관리 > 입고관리 메뉴의 입고 조회/등록/수정/삭제 전체 CRUD 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -39,11 +46,22 @@ "level2": "입고관리", "expected": { "url_contains": "/material/receiving", - "visible": ["입고관리", "입고"] + "visible": [ + "입고관리", + "입고" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/material/receiving-management" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -54,7 +72,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "입고 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -67,7 +91,13 @@ "expected": "입고 테이블 컬럼 정상 표시" }, { - "id": 4, + "id": 6, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 7, "name": "검색 기능 테스트", "action": "click_if_exists", "target": "input[placeholder*='검색']", @@ -77,7 +107,7 @@ } }, { - "id": 5, + "id": 8, "phase": "CREATE", "name": "[CREATE] 입고 등록 버튼 클릭", "action": "click_if_exists", @@ -88,21 +118,41 @@ } }, { - "id": 6, + "id": 9, "phase": "CREATE", "name": "[CREATE] 입고 정보 입력", "action": "fill_form", "fields": [ - {"name": "입고일", "type": "date", "value": "2026-02-03"}, - {"name": "품목", "type": "select", "value": "E2E_TEST_입고품목"}, - {"name": "수량", "type": "number", "value": "100"}, - {"name": "거래처", "type": "select", "value": "테스트거래처"}, - {"name": "메모", "type": "text", "value": "E2E 자동화 테스트 입고_{timestamp}"} + { + "name": "입고일", + "type": "date", + "value": "2026-02-03" + }, + { + "name": "품목", + "type": "select", + "value": "E2E_TEST_입고품목" + }, + { + "name": "수량", + "type": "number", + "value": "100" + }, + { + "name": "거래처", + "type": "select", + "value": "테스트거래처" + }, + { + "name": "메모", + "type": "text", + "value": "E2E 자동화 테스트 입고_{timestamp}" + } ], "note": "타임스탬프로 고유성 보장" }, { - "id": 7, + "id": 10, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 등록 저장", "action": "click_if_exists", @@ -116,36 +166,52 @@ "expected": "입고 등록 완료" }, { - "id": "7-modal-close", + "id": 11, + "phase": "CREATE", + "name": "[CREATE] 저장 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "등록|완료|성공|저장" + } + }, + { + "id": 12, "phase": "CREATE", "name": "[CREATE] 모달 닫기 확인", "action": "close_modal_if_open", "expected": "모달 닫힘" }, { - "id": 8, + "id": 13, "phase": "CREATE", "name": "[CREATE] 등록 결과 확인", "action": "verify_detail", "search": "E2E 자동화 테스트 입고", "expected": { "row_exists": true, - "contains": ["E2E", "100"] + "contains": [ + "E2E", + "100" + ] } }, { - "id": 9, + "id": 14, "phase": "READ", "name": "[READ] 입고 상세 페이지 진입", "action": "click_if_exists", "target": "table tbody tr:has-text('E2E')", "expected": { "url_contains": "/material/receiving", - "visible": ["입고 상세", "수정", "삭제"] + "visible": [ + "입고 상세", + "수정", + "삭제" + ] } }, { - "id": 10, + "id": 15, "phase": "READ", "name": "[READ] 상세 정보 확인", "action": "verify_detail", @@ -156,7 +222,7 @@ "expected": "입력한 데이터와 일치" }, { - "id": 11, + "id": 16, "phase": "UPDATE", "name": "[UPDATE] 수정 모드 진입", "action": "click_if_exists", @@ -167,7 +233,7 @@ } }, { - "id": 12, + "id": 17, "phase": "UPDATE", "name": "[UPDATE] 수량 수정", "action": "click_if_exists", @@ -176,7 +242,7 @@ "clear": true }, { - "id": 13, + "id": 18, "phase": "UPDATE", "name": "[UPDATE] 메모 수정", "action": "click_if_exists", @@ -185,7 +251,7 @@ "clear": true }, { - "id": 14, + "id": 19, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 수정 저장", "action": "click_if_exists", @@ -199,7 +265,16 @@ "expected": "수정 완료" }, { - "id": 15, + "id": 20, + "phase": "UPDATE", + "name": "[UPDATE] 수정 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "수정|완료|성공|저장" + } + }, + { + "id": 21, "phase": "UPDATE", "name": "[UPDATE] 수정 결과 확인", "action": "verify_detail", @@ -210,7 +285,7 @@ "expected": "수정된 데이터 반영" }, { - "id": 16, + "id": 22, "phase": "DELETE", "name": "[DELETE] 삭제 버튼 클릭", "action": "click_if_exists", @@ -221,7 +296,7 @@ } }, { - "id": 17, + "id": 23, "phase": "DELETE", "name": "[DELETE] 필수 검증 #6: 삭제 확인", "action": "click_if_exists", @@ -234,7 +309,7 @@ "expected": "삭제 완료 및 목록 복귀" }, { - "id": 18, + "id": 24, "phase": "DELETE", "name": "[DELETE] 삭제 결과 확인", "action": "verify_detail", @@ -243,6 +318,12 @@ "row_exists": false, "message": "테스트 입고가 목록에서 제거됨" } + }, + { + "id": 25, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "expectedAPIs": [ @@ -276,19 +357,28 @@ { "id": 2, "name": "등록/저장 버튼", - "steps": [7, 14], + "steps": [ + 7, + 14 + ], "criteria": "API 호출 + 성공 토스트 + 데이터 반영" }, { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "입고 목록, 등록 버튼, 필터 존재" }, { "id": 6, "name": "삭제 기능", - "steps": [16, 17, 18], + "steps": [ + 16, + 17, + 18 + ], "criteria": "DELETE API + 목록에서 제거" } ], diff --git a/material-stock.json b/material-stock.json index 1253d71..2457624 100644 --- a/material-stock.json +++ b/material-stock.json @@ -3,7 +3,14 @@ "name": "재고현황 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "자재관리 > 재고현황 메뉴의 재고 현황 조회/검색/필터/다운로드 기능 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -27,11 +34,22 @@ "level2": "재고현황", "expected": { "url_contains": "/material/stock", - "visible": ["재고현황", "재고"] + "visible": [ + "재고현황", + "재고" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/material/stock-status" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -42,7 +60,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "재고현황 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -55,7 +79,13 @@ "expected": "재고 테이블 표시" }, { - "id": 4, + "id": 6, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 7, "phase": "READ", "name": "[READ] 재고 데이터 확인", "action": "verify_detail", @@ -65,7 +95,7 @@ "expected": "재고 데이터 표시" }, { - "id": 5, + "id": 8, "phase": "SEARCH", "name": "[SEARCH] 품목 검색", "action": "fill", @@ -74,7 +104,7 @@ "submit": true }, { - "id": 6, + "id": 9, "phase": "SEARCH", "name": "[SEARCH] 검색 결과 확인", "action": "verify_detail", @@ -84,7 +114,7 @@ "expected": "검색 기능 동작" }, { - "id": 7, + "id": 10, "phase": "SEARCH", "name": "[SEARCH] 검색 초기화", "action": "click_if_exists", @@ -92,7 +122,7 @@ "expected": "검색 초기화" }, { - "id": 8, + "id": 11, "phase": "FILTER", "name": "[FILTER] 창고/위치 필터", "action": "click_if_exists", @@ -100,7 +130,7 @@ "expected": "창고 필터 옵션 표시" }, { - "id": 9, + "id": 12, "phase": "FILTER", "name": "[FILTER] 재고 상태 필터", "action": "verify_elements", @@ -110,7 +140,7 @@ "expected": "재고 상태 필터 표시" }, { - "id": 10, + "id": 13, "name": "안전재고 이하 품목 확인", "action": "verify_elements", "checks": [ @@ -120,7 +150,7 @@ "expected": "안전재고 알림 표시" }, { - "id": 11, + "id": 14, "name": "재고 이동 이력 링크", "action": "verify_elements", "checks": [ @@ -129,7 +159,7 @@ "expected": "이력 조회 기능 표시" }, { - "id": 12, + "id": 15, "name": "재고 현황 요약", "action": "verify_elements", "checks": [ @@ -140,7 +170,7 @@ "expected": "요약 정보 표시" }, { - "id": 13, + "id": 16, "name": "필수 검증 #1: 엑셀 다운로드", "action": "click_if_exists", "target": "button:has-text('엑셀'), button:has-text('Excel'), button:has-text('다운로드')", @@ -151,7 +181,7 @@ "expected": "엑셀 파일 다운로드" }, { - "id": 14, + "id": 17, "name": "인쇄 기능 확인", "action": "verify_elements", "checks": [ @@ -160,13 +190,19 @@ "expected": "인쇄 기능 표시" }, { - "id": 15, + "id": 18, "name": "재고 조정 버튼 확인", "action": "verify_elements", "checks": [ "재고 조정 버튼 존재 여부" ], "expected": "재고 조정 기능 확인" + }, + { + "id": 19, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "expectedAPIs": [ @@ -190,19 +226,27 @@ { "id": 1, "name": "엑셀 다운로드", - "steps": [13], + "steps": [ + 13 + ], "criteria": "API 호출 + 파일 다운로드" }, { "id": 3, "name": "검색/필터", - "steps": [5, 6, 7], + "steps": [ + 5, + 6, + 7 + ], "criteria": "검색 + 필터 기능 동작" }, { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "재고 목록, 검색 기능, 필터 존재" } ], diff --git a/production-dashboard.json b/production-dashboard.json index 767d10a..ac133c4 100644 --- a/production-dashboard.json +++ b/production-dashboard.json @@ -3,7 +3,14 @@ "name": "생산 현황판 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "생산관리 > 생산 현황판 대시보드 기능 검증", "baseUrl": "https://dev.codebridge-x.com", @@ -25,47 +32,80 @@ "action": "menu_navigate", "level1": "생산관리", "level2": "생산 현황판", - "expected": { "url_contains": "/production" } + "expected": { + "url_contains": "/production" + } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/production/dashboard" + } + }, + { + "id": 3, "name": "목업 감지", "action": "verify_not_mockup" }, - { - "id": 3, - "name": "생산 현황판 페이지 확인", - "action": "verify_detail", - "checks": ["visible_text:생산"] - }, { "id": 4, - "name": "현황 데이터 확인", - "action": "verify_detail", - "checks": ["visible_text:현황"] + "name": "테이블 구조 검증", + "action": "verify_table" }, { "id": 5, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 6, + "name": "생산 현황판 페이지 확인", + "action": "verify_detail", + "checks": [ + "visible_text:생산" + ] + }, + { + "id": 7, + "name": "현황 데이터 확인", + "action": "verify_detail", + "checks": [ + "visible_text:현황" + ] + }, + { + "id": 8, "name": "필터 버튼 클릭", "action": "click_if_exists", "target": "button:has-text('필터'), select, [class*='filter'] button, [class*='date-picker']" }, { - "id": 6, + "id": 9, "name": "대기", "action": "wait", "duration": 1000 }, { - "id": 7, + "id": 10, "name": "모달 닫기", "action": "close_modal_if_open" }, { - "id": 8, + "id": 11, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 12, "name": "생산 현황판 최종 확인", "action": "verify_detail", - "checks": ["visible_text:생산"] + "checks": [ + "visible_text:생산" + ] } ] } diff --git a/production-item.json b/production-item.json index e89c08b..32a20a6 100644 --- a/production-item.json +++ b/production-item.json @@ -3,7 +3,14 @@ "name": "생산품목관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "품목관리 > 품목기준관리 목록/검색/상세 기능 검증", "baseUrl": "https://dev.codebridge-x.com", @@ -25,59 +32,95 @@ "action": "menu_navigate", "level1": "품목관리", "level2": "품목기준관리", - "expected": { "url_contains": "/master-data" } + "expected": { + "url_contains": "/master-data" + } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/master-data/item-master-data-management" + } + }, + { + "id": 3, "name": "목업 감지", "action": "verify_not_mockup" }, - { - "id": 3, - "name": "품목기준관리 페이지 확인", - "action": "verify_detail", - "checks": ["visible_text:품목"] - }, { "id": 4, - "name": "테이블 또는 목록 확인", - "action": "verify_detail", - "checks": ["visible_text:관리"] + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" }, { "id": 5, + "name": "품목기준관리 페이지 확인", + "action": "verify_detail", + "checks": [ + "visible_text:품목" + ] + }, + { + "id": 6, + "name": "테이블 또는 목록 확인", + "action": "verify_detail", + "checks": [ + "visible_text:관리" + ] + }, + { + "id": 7, "name": "검색 입력 시도", "action": "click_if_exists", "target": "input[type='search'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input" }, { - "id": 6, + "id": 8, "name": "대기", "action": "wait", "duration": 1000 }, { - "id": 7, + "id": 9, "name": "행 클릭 시도", "action": "click_if_exists", "target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']" }, { - "id": 8, + "id": 10, "name": "상세 확인", "action": "verify_detail", - "checks": ["visible_text:품목"] + "checks": [ + "visible_text:품목" + ] }, { - "id": 9, + "id": 11, "name": "모달 닫기", "action": "close_modal_if_open" }, { - "id": 10, + "id": 12, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 13, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 14, "name": "최종 확인", "action": "verify_detail", - "checks": ["visible_text:품목"] + "checks": [ + "visible_text:품목" + ] } ] } diff --git a/production-work-order.json b/production-work-order.json index bc9e21a..7f998c9 100644 --- a/production-work-order.json +++ b/production-work-order.json @@ -55,6 +55,14 @@ }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/production/work-orders" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -65,7 +73,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "작업지시 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -78,7 +92,13 @@ "expected": "작업지시 테이블 컬럼 정상 표시" }, { - "id": 4, + "id": 6, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 7, "name": "검색 기능 테스트", "action": "click_if_exists", "target": "input[placeholder*='검색']", @@ -88,7 +108,7 @@ } }, { - "id": 5, + "id": 8, "phase": "CREATE", "name": "[CREATE] 작업지시 등록 버튼 클릭", "action": "click_if_exists", @@ -99,7 +119,7 @@ } }, { - "id": 6, + "id": 9, "phase": "CREATE", "name": "[CREATE] 작업지시 정보 입력", "action": "click_if_exists", @@ -134,7 +154,7 @@ "target": "form, [role='dialog'], .modal" }, { - "id": 7, + "id": 10, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 등록 저장", "action": "click_if_exists", @@ -148,14 +168,23 @@ "expected": "작업지시 등록 완료" }, { - "id": "7-modal-close", + "id": 11, + "phase": "CREATE", + "name": "[CREATE] 저장 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "등록|완료|성공|저장" + } + }, + { + "id": 12, "phase": "CREATE", "name": "[CREATE] 모달 닫기 확인", "action": "close_modal_if_open", "expected": "모달 닫힘" }, { - "id": 8, + "id": 13, "phase": "CREATE", "name": "[CREATE] 등록 결과 확인", "action": "verify_detail", @@ -169,7 +198,7 @@ } }, { - "id": 9, + "id": 14, "phase": "READ", "name": "[READ] 작업지시 상세 페이지 진입", "action": "click_if_exists", @@ -184,7 +213,7 @@ } }, { - "id": 10, + "id": 15, "phase": "READ", "name": "[READ] 상세 정보 확인", "action": "verify_detail", @@ -196,7 +225,7 @@ "expected": "입력한 데이터와 일치" }, { - "id": 11, + "id": 16, "phase": "UPDATE", "name": "[UPDATE] 수정 모드 진입", "action": "click_if_exists", @@ -207,7 +236,7 @@ } }, { - "id": 12, + "id": 17, "phase": "UPDATE", "name": "[UPDATE] 수량 수정", "action": "click_if_exists", @@ -216,7 +245,7 @@ "clear": true }, { - "id": 13, + "id": 18, "phase": "UPDATE", "name": "[UPDATE] 메모 수정", "action": "click_if_exists", @@ -225,7 +254,7 @@ "clear": true }, { - "id": 14, + "id": 19, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 수정 저장", "action": "click_if_exists", @@ -239,7 +268,16 @@ "expected": "수정 완료" }, { - "id": 15, + "id": 20, + "phase": "UPDATE", + "name": "[UPDATE] 수정 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "수정|완료|성공|저장" + } + }, + { + "id": 21, "phase": "UPDATE", "name": "[UPDATE] 수정 결과 확인", "action": "verify_detail", @@ -250,7 +288,7 @@ "expected": "수정된 데이터 반영" }, { - "id": 16, + "id": 22, "phase": "DELETE", "name": "[DELETE] 삭제 버튼 클릭", "action": "click_if_exists", @@ -261,7 +299,7 @@ } }, { - "id": 17, + "id": 23, "phase": "DELETE", "name": "[DELETE] 필수 검증 #6: 삭제 확인", "action": "click_if_exists", @@ -274,7 +312,7 @@ "expected": "삭제 완료 및 목록 복귀" }, { - "id": 18, + "id": 24, "phase": "DELETE", "name": "[DELETE] 삭제 결과 확인", "action": "verify_detail", @@ -283,6 +321,12 @@ "row_exists": false, "message": "테스트 작업지시가 목록에서 제거됨" } + }, + { + "id": 25, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "expectedAPIs": [ @@ -347,4 +391,4 @@ "onDeleteFail": "테스트 작업지시 수동 삭제 필요", "cleanupRequired": "E2E_TEST_ 접두사 작업지시는 테스트 데이터" } -} \ No newline at end of file +} diff --git a/production-work-result.json b/production-work-result.json index e967e80..60916b6 100644 --- a/production-work-result.json +++ b/production-work-result.json @@ -4,7 +4,14 @@ "name": "작업실적 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "생산관리 > 작업실적 메뉴의 작업 실적 CRUD 기능 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -35,11 +42,21 @@ "level2": "작업실적", "expected": { "url_contains": "/production/work", - "visible": ["작업실적"] + "visible": [ + "작업실적" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/production/work-results" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -50,7 +67,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "작업실적 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -63,7 +86,7 @@ "expected": "작업실적 테이블 표시" }, { - "id": 4, + "id": 6, "phase": "FILTER", "name": "[FILTER] 기간 필터 - 시작일", "action": "click_if_exists", @@ -71,7 +94,7 @@ "value": "2025-01-01" }, { - "id": 5, + "id": 7, "phase": "FILTER", "name": "[FILTER] 기간 필터 - 종료일", "action": "click_if_exists", @@ -79,7 +102,7 @@ "value": "2025-12-31" }, { - "id": 6, + "id": 8, "phase": "FILTER", "name": "[FILTER] 조회 실행", "action": "click_if_exists", @@ -89,7 +112,7 @@ } }, { - "id": 7, + "id": 9, "phase": "CREATE", "name": "[CREATE] 실적 등록 버튼 클릭", "action": "click_if_exists", @@ -99,7 +122,7 @@ } }, { - "id": 8, + "id": 10, "phase": "CREATE", "name": "[CREATE] 작업 지시 선택", "action": "click_if_exists", @@ -107,7 +130,7 @@ "expected": "작업 지시 선택 가능" }, { - "id": 9, + "id": 11, "phase": "CREATE", "name": "[CREATE] 생산 수량 입력", "action": "click_if_exists", @@ -116,7 +139,7 @@ "clear": true }, { - "id": 10, + "id": 12, "phase": "CREATE", "name": "[CREATE] 불량 수량 입력", "action": "click_if_exists", @@ -125,7 +148,7 @@ "clear": true }, { - "id": 11, + "id": 13, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 실적 저장", "action": "click_if_exists", @@ -139,7 +162,16 @@ "expected": "실적 등록 완료" }, { - "id": 12, + "id": 14, + "phase": "CREATE", + "name": "[CREATE] 저장 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "등록|완료|성공|저장" + } + }, + { + "id": 15, "phase": "READ", "name": "[READ] 등록된 실적 확인", "action": "verify_detail", @@ -149,7 +181,7 @@ "expected": "등록된 실적 확인" }, { - "id": 13, + "id": 16, "phase": "READ", "name": "[READ] 실적 상세 조회", "action": "click_if_exists", @@ -159,7 +191,7 @@ } }, { - "id": 14, + "id": 17, "name": "실적 상세 정보 확인", "action": "verify_detail", "checks": [ @@ -172,7 +204,7 @@ "expected": "실적 상세 정보 표시" }, { - "id": 15, + "id": 18, "phase": "UPDATE", "name": "[UPDATE] 실적 수정", "action": "click_if_exists", @@ -182,7 +214,7 @@ } }, { - "id": 16, + "id": 19, "phase": "UPDATE", "name": "[UPDATE] 수량 수정", "action": "click_if_exists", @@ -191,7 +223,7 @@ "clear": true }, { - "id": 17, + "id": 20, "phase": "UPDATE", "name": "[UPDATE] 수정 저장", "action": "click_if_exists", @@ -203,7 +235,16 @@ "expected": "실적 수정 완료" }, { - "id": 18, + "id": 21, + "phase": "UPDATE", + "name": "[UPDATE] 수정 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "수정|완료|성공|저장" + } + }, + { + "id": 22, "name": "엑셀 다운로드 확인", "action": "click_if_exists", "target": "button:has-text('엑셀'), button:has-text('Excel'), button:has-text('다운로드')", @@ -211,6 +252,12 @@ "file_download": true }, "expected": "엑셀 파일 다운로드" + }, + { + "id": 23, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "expectedAPIs": [ @@ -239,13 +286,18 @@ { "id": 2, "name": "저장 버튼", - "steps": [11, 17], + "steps": [ + 11, + 17 + ], "criteria": "API 호출 + 성공 토스트 + 데이터 반영" }, { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "작업실적 목록, 등록 버튼, 기간 필터 존재" } ], diff --git a/production-worker.json b/production-worker.json index 4469dd7..6507830 100644 --- a/production-worker.json +++ b/production-worker.json @@ -3,7 +3,14 @@ "name": "작업자 화면 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "생산관리 > 작업자 화면 목록/상세 기능 검증", "baseUrl": "https://dev.codebridge-x.com", @@ -25,59 +32,95 @@ "action": "menu_navigate", "level1": "생산관리", "level2": "작업자 화면", - "expected": { "url_contains": "/production" } + "expected": { + "url_contains": "/production" + } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/production/worker-screen" + } + }, + { + "id": 3, "name": "목업 감지", "action": "verify_not_mockup" }, - { - "id": 3, - "name": "작업자 화면 페이지 확인", - "action": "verify_detail", - "checks": ["visible_text:작업"] - }, { "id": 4, - "name": "UI 요소 확인", - "action": "verify_detail", - "checks": ["visible_text:관리"] + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" }, { "id": 5, + "name": "작업자 화면 페이지 확인", + "action": "verify_detail", + "checks": [ + "visible_text:작업" + ] + }, + { + "id": 6, + "name": "UI 요소 확인", + "action": "verify_detail", + "checks": [ + "visible_text:관리" + ] + }, + { + "id": 7, "name": "검색 입력 시도", "action": "click_if_exists", "target": "input[type='search'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input" }, { - "id": 6, + "id": 8, "name": "대기", "action": "wait", "duration": 1000 }, { - "id": 7, + "id": 9, "name": "행 클릭 시도", "action": "click_if_exists", "target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']" }, { - "id": 8, + "id": 10, "name": "상세 확인", "action": "verify_detail", - "checks": ["visible_text:작업"] + "checks": [ + "visible_text:작업" + ] }, { - "id": 9, + "id": 11, "name": "모달 닫기", "action": "close_modal_if_open" }, { - "id": 10, + "id": 12, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 13, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 14, "name": "최종 확인", "action": "verify_detail", - "checks": ["visible_text:작업"] + "checks": [ + "visible_text:작업" + ] } ] } diff --git a/quality-certification.json b/quality-certification.json index 3181366..079a36b 100644 --- a/quality-certification.json +++ b/quality-certification.json @@ -3,7 +3,14 @@ "name": "품질인정심사 시스템 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "품질관리 > 품질인정심사 시스템 목록/상세 기능 검증", "baseUrl": "https://dev.codebridge-x.com", @@ -25,59 +32,95 @@ "action": "menu_navigate", "level1": "품질관리", "level2": "품질인정심사 시스템", - "expected": { "url_contains": "/quality" } + "expected": { + "url_contains": "/quality" + } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/quality/qms" + } + }, + { + "id": 3, "name": "목업 감지", "action": "verify_not_mockup" }, - { - "id": 3, - "name": "품질인정심사 페이지 확인", - "action": "verify_detail", - "checks": ["visible_text:품질"] - }, { "id": 4, - "name": "UI 요소 확인", - "action": "verify_detail", - "checks": ["visible_text:인정"] + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" }, { "id": 5, + "name": "품질인정심사 페이지 확인", + "action": "verify_detail", + "checks": [ + "visible_text:품질" + ] + }, + { + "id": 6, + "name": "UI 요소 확인", + "action": "verify_detail", + "checks": [ + "visible_text:인정" + ] + }, + { + "id": 7, "name": "검색 입력 시도", "action": "click_if_exists", "target": "input[type='search'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input" }, { - "id": 6, + "id": 8, "name": "대기", "action": "wait", "duration": 1000 }, { - "id": 7, + "id": 9, "name": "행 클릭 시도", "action": "click_if_exists", "target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']" }, { - "id": 8, + "id": 10, "name": "상세 확인", "action": "verify_detail", - "checks": ["visible_text:품질"] + "checks": [ + "visible_text:품질" + ] }, { - "id": 9, + "id": 11, "name": "모달 닫기", "action": "close_modal_if_open" }, { - "id": 10, + "id": 12, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 13, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 14, "name": "최종 확인", "action": "verify_detail", - "checks": ["visible_text:품질"] + "checks": [ + "visible_text:품질" + ] } ] } diff --git a/quality-inspection.json b/quality-inspection.json index ec8d42b..8402230 100644 --- a/quality-inspection.json +++ b/quality-inspection.json @@ -56,6 +56,14 @@ }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/quality/inspections" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -66,7 +74,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "제품검사 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -80,7 +94,13 @@ "expected": "제품검사 테이블 컬럼 정상 표시" }, { - "id": 4, + "id": 6, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 7, "name": "검색 기능 테스트", "action": "click_if_exists", "target": "input[placeholder*='검색']", @@ -90,7 +110,7 @@ } }, { - "id": 5, + "id": 8, "phase": "CREATE", "name": "[CREATE] 제품검사 등록 버튼 클릭", "action": "click_if_exists", @@ -101,7 +121,7 @@ } }, { - "id": 6, + "id": 9, "phase": "CREATE", "name": "[CREATE] 제품검사 정보 입력", "action": "fill_form", @@ -135,7 +155,7 @@ "note": "타임스탬프로 고유성 보장" }, { - "id": 7, + "id": 10, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 등록 저장", "action": "click_if_exists", @@ -149,14 +169,23 @@ "expected": "제품검사 등록 완료" }, { - "id": "7-modal-close", + "id": 11, + "phase": "CREATE", + "name": "[CREATE] 저장 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "등록|완료|성공|저장" + } + }, + { + "id": 12, "phase": "CREATE", "name": "[CREATE] 모달 닫기 확인", "action": "close_modal_if_open", "expected": "모달 닫힘" }, { - "id": 8, + "id": 13, "phase": "CREATE", "name": "[CREATE] 등록 결과 확인", "action": "verify_detail", @@ -170,7 +199,7 @@ } }, { - "id": 9, + "id": 14, "phase": "READ", "name": "[READ] 제품검사 상세 페이지 진입", "action": "click_if_exists", @@ -185,7 +214,7 @@ } }, { - "id": 10, + "id": 15, "phase": "READ", "name": "[READ] 상세 정보 확인", "action": "verify_detail", @@ -197,7 +226,7 @@ "expected": "입력한 데이터와 일치" }, { - "id": 11, + "id": 16, "phase": "UPDATE", "name": "[UPDATE] 수정 모드 진입", "action": "click_if_exists", @@ -208,7 +237,7 @@ } }, { - "id": 12, + "id": 17, "phase": "UPDATE", "name": "[UPDATE] 개소 수정", "action": "click_if_exists", @@ -217,7 +246,7 @@ "clear": true }, { - "id": 13, + "id": 18, "phase": "UPDATE", "name": "[UPDATE] 메모 수정", "action": "click_if_exists", @@ -226,7 +255,7 @@ "clear": true }, { - "id": 14, + "id": 19, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 수정 저장", "action": "click_if_exists", @@ -240,7 +269,16 @@ "expected": "수정 완료" }, { - "id": 15, + "id": 20, + "phase": "UPDATE", + "name": "[UPDATE] 수정 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "수정|완료|성공|저장" + } + }, + { + "id": 21, "phase": "UPDATE", "name": "[UPDATE] 수정 결과 확인", "action": "verify_detail", @@ -251,7 +289,7 @@ "expected": "수정된 데이터 반영" }, { - "id": 16, + "id": 22, "phase": "DELETE", "name": "[DELETE] 삭제 버튼 클릭", "action": "click_if_exists", @@ -262,7 +300,7 @@ } }, { - "id": 17, + "id": 23, "phase": "DELETE", "name": "[DELETE] 필수 검증 #6: 삭제 확인", "action": "click_if_exists", @@ -275,7 +313,7 @@ "expected": "삭제 완료 및 목록 복귀" }, { - "id": 18, + "id": 24, "phase": "DELETE", "name": "[DELETE] 삭제 결과 확인", "action": "verify_detail", @@ -284,6 +322,12 @@ "row_exists": false, "message": "테스트 제품검사가 목록에서 제거됨" } + }, + { + "id": 25, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "expectedAPIs": [ @@ -356,4 +400,4 @@ "onDeleteFail": "테스트 제품검사 수동 삭제 필요", "cleanupRequired": "E2E_TEST_ 접두사 제품검사는 테스트 데이터" } -} \ No newline at end of file +} diff --git a/receiving-management.json b/receiving-management.json index 3c69f16..dbfe19f 100644 --- a/receiving-management.json +++ b/receiving-management.json @@ -4,7 +4,14 @@ "name": "입고관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "자재관리 > 입고관리 페이지의 입고 조회 및 상태별 필터링 기능을 테스트하는 E2E 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -12,7 +19,11 @@ "navigation": { "targetUrl": "/material/receiving-management", "urlPattern": "/material/receiving-management|/ko/material/receiving-management", - "menuHints": ["입고관리", "입고 관리", "자재관리"] + "menuHints": [ + "입고관리", + "입고 관리", + "자재관리" + ] }, "menuNavigation": { "level1": "자재관리", @@ -42,16 +53,18 @@ "expectedUrl": "/material/receiving-management" }, "timeout": 90000, - "tags": ["material", "receiving", "read-only"], - + "tags": [ + "material", + "receiving", + "read-only" + ], "auth": { "username": "TestUser5", "password": "password123!" }, - "steps": [ { - "id": "step-0", + "id": 1, "name": "사이드바 메뉴 전체 펼치기", "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", "actions": [ @@ -59,16 +72,22 @@ "type": "evaluate", "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" }, - { "type": "wait", "duration": 300 }, + { + "type": "wait", + "duration": 300 + }, { "type": "evaluate", "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" }, - { "type": "wait", "duration": 2000 } + { + "type": "wait", + "duration": 2000 + } ] }, { - "id": "step-1", + "id": 2, "name": "자재관리 메뉴 진입", "description": "자재관리 > 입고관리 메뉴로 이동", "actions": [ @@ -79,13 +98,24 @@ "scrollStep": 200, "maxAttempts": 5 }, - { "type": "click_if_exists", "target": "자재관리" }, - { "type": "wait", "duration": 500 }, - { "type": "click_if_exists", "target": "입고관리" } + { + "type": "click_if_exists", + "target": "자재관리" + }, + { + "type": "wait", + "duration": 500 + }, + { + "type": "click_if_exists", + "target": "입고관리" + } ], "expect": { "url": "/material/receiving-management", - "visible": ["입고 목록"] + "visible": [ + "입고 목록" + ] }, "fallback": { "type": "navigate", @@ -93,21 +123,43 @@ } }, { - "id": "step-2", + "id": 3, "name": "페이지 구조 확인", "description": "통계 카드와 테이블 구조 확인", "verify": { - "visible": ["입고대기", "배송중", "검사대기", "금일입고"], - "tableColumns": ["번호", "발주번호", "품목코드", "품목명", "공급업체", "발주수량", "입고수량", "LOT번호", "상태"] + "visible": [ + "입고대기", + "배송중", + "검사대기", + "금일입고" + ], + "tableColumns": [ + "번호", + "발주번호", + "품목코드", + "품목명", + "공급업체", + "발주수량", + "입고수량", + "LOT번호", + "상태" + ] } }, { - "id": "step-3", + "id": 4, "name": "필수 검증 #3: 상태 탭 필터 - 입고대기", "description": "입고대기 탭 클릭하여 필터링 확인", "actions": [ - { "type": "click_if_exists", "target": "입고대기", "role": "tab" }, - { "type": "wait", "duration": 300 } + { + "type": "click_if_exists", + "target": "입고대기", + "role": "tab" + }, + { + "type": "wait", + "duration": 300 + } ], "expect": { "tabActive": "입고대기", @@ -115,12 +167,19 @@ } }, { - "id": "step-4", + "id": 5, "name": "필수 검증 #3: 상태 탭 필터 - 입고완료", "description": "입고완료 탭 클릭하여 필터링 확인", "actions": [ - { "type": "click_if_exists", "target": "입고완료", "role": "tab" }, - { "type": "wait", "duration": 300 } + { + "type": "click_if_exists", + "target": "입고완료", + "role": "tab" + }, + { + "type": "wait", + "duration": 300 + } ], "expect": { "tabActive": "입고완료", @@ -128,12 +187,19 @@ } }, { - "id": "step-5", + "id": 6, "name": "전체 탭으로 복귀", "description": "전체 탭 클릭하여 모든 입고 표시", "actions": [ - { "type": "click_if_exists", "target": "전체", "role": "tab" }, - { "type": "wait", "duration": 300 } + { + "type": "click_if_exists", + "target": "전체", + "role": "tab" + }, + { + "type": "wait", + "duration": 300 + } ], "expect": { "tabActive": "전체", @@ -141,7 +207,7 @@ } }, { - "id": "step-6", + "id": 7, "name": "빈 상태 확인", "description": "데이터가 없을 때 빈 상태 메시지 확인", "verify": { @@ -149,16 +215,26 @@ } }, { - "id": "step-7", + "id": 8, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 9, "name": "통계 카드 값 확인", "description": "입고대기/배송중/검사대기/금일입고 카운트 표시 확인", "verify": { - "statsCards": ["입고대기", "배송중", "검사대기", "금일입고"], + "statsCards": [ + "입고대기", + "배송중", + "검사대기", + "금일입고" + ], "countsDisplayed": true } } ], - "assertions": [ { "type": "url", @@ -171,7 +247,6 @@ "message": "입고 목록 제목이 표시되어야 함" } ], - "mandatoryVerifications": { "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", "items": [ @@ -184,13 +259,31 @@ } ] }, - "notes": { "testScope": "입고 목록 조회 및 상태별 필터링 테스트", "pageType": "조회 전용 (발주 기반 입고 처리)", - "statsCards": ["입고대기", "배송중", "검사대기", "금일입고"], - "statusTabs": ["전체", "입고대기", "입고완료"], - "tableColumns": ["번호", "발주번호", "품목코드", "품목명", "공급업체", "발주수량", "입고수량", "LOT번호", "상태"], + "statsCards": [ + "입고대기", + "배송중", + "검사대기", + "금일입고" + ], + "statusTabs": [ + "전체", + "입고대기", + "입고완료" + ], + "tableColumns": [ + "번호", + "발주번호", + "품목코드", + "품목명", + "공급업체", + "발주수량", + "입고수량", + "LOT번호", + "상태" + ], "workflow": "발주 → 배송중 → 검사대기 → 입고완료", "prerequisites": "로그인된 사용자, 발주 데이터 존재 시 입고 가능" } diff --git a/reference-box.json b/reference-box.json index 8caa532..cc37f0a 100644 --- a/reference-box.json +++ b/reference-box.json @@ -88,7 +88,7 @@ ], "steps": [ { - "id": 0, + "id": 1, "name": "사이드바 메뉴 전체 펼치기", "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", "actions": [ @@ -111,7 +111,7 @@ ] }, { - "id": 1, + "id": 2, "name": "2단계 메뉴 진입: 결재관리 > 참조함", "description": "사이드바를 스크롤하며 결재관리 > 참조함 메뉴를 찾아 클릭", "actions": [ @@ -176,7 +176,7 @@ ] }, { - "id": 2, + "id": 3, "name": "데이터 로딩 대기", "action": "wait", "duration": 3000, @@ -188,7 +188,7 @@ ] }, { - "id": 3, + "id": 4, "name": "통계 카드 데이터 확인", "action": "verify_element", "target": "[class*='card'], [class*='stat']", @@ -199,7 +199,7 @@ ] }, { - "id": 4, + "id": 5, "name": "탭 전환 - 열람 탭", "action": "click_if_exists", "target": "button:has-text('열람')", @@ -211,7 +211,7 @@ ] }, { - "id": 5, + "id": 6, "name": "탭 전환 - 미열람 탭", "action": "click_if_exists", "target": "button:has-text('미열람')", @@ -223,7 +223,7 @@ ] }, { - "id": 6, + "id": 7, "name": "탭 전환 - 전체 탭으로 복귀", "action": "click_if_exists", "target": "button:has-text('전체')", @@ -234,7 +234,7 @@ ] }, { - "id": 7, + "id": 8, "name": "⚠️ 필수 검증: 검색 기능 - 기안자 검색", "actions": [ { @@ -275,7 +275,7 @@ ] }, { - "id": "7-1", + "id": 9, "name": "검색 결과 데이터 검증", "description": "검색 결과의 모든 행이 검색어를 포함하는지 확인", "verify": { @@ -284,7 +284,7 @@ } }, { - "id": 8, + "id": 10, "name": "검색 초기화", "actions": [ { @@ -312,7 +312,7 @@ ] }, { - "id": 9, + "id": 11, "name": "필터 기능 - 문서유형 선택", "action": "select", "target": "select, [role='combobox']", @@ -324,7 +324,7 @@ ] }, { - "id": 10, + "id": 12, "name": "필터 초기화", "action": "select", "target": "select, [role='combobox']", @@ -334,7 +334,7 @@ ] }, { - "id": 11, + "id": 13, "name": "정렬 기능 - 오래된순", "action": "click_if_exists", "target": "select, [role='combobox']", @@ -345,7 +345,7 @@ ] }, { - "id": 12, + "id": 14, "name": "정렬 초기화", "action": "click_if_exists", "target": "select, [role='combobox']", @@ -354,7 +354,7 @@ ] }, { - "id": 13, + "id": 15, "name": "체크박스 - 단일 선택", "action": "click_if_exists", "target": "table tbody tr:first-child input[type='checkbox']", @@ -366,7 +366,7 @@ ] }, { - "id": 14, + "id": 16, "name": "체크박스 - 선택 해제", "action": "click_if_exists", "target": "table tbody tr:first-child input[type='checkbox']", @@ -376,7 +376,7 @@ ] }, { - "id": 15, + "id": 17, "name": "체크박스 - 다중 선택", "action": "click_if_exists", "target": "table tbody tr input[type='checkbox']", @@ -386,7 +386,7 @@ ] }, { - "id": 16, + "id": 18, "name": "문서 상세 모달 - 열기", "action": "click_if_exists", "target": "table tbody tr:first-child td:nth-child(2)", @@ -402,7 +402,7 @@ ] }, { - "id": "16-pdf-1", + "id": 19, "name": "⚠️ 필수 검증: PDF 다운로드 전 모달 스크린샷", "description": "PDF 생성 전 모달 상태를 스크린샷으로 캡처하여 CSS 문제 감지용 기준 이미지 확보", "prerequisite": "step-16의 문서 상세 모달이 열려있는 상태에서 실행", @@ -422,7 +422,7 @@ } }, { - "id": "16-pdf-2", + "id": 20, "name": "⚠️ 필수 검증: PDF 다운로드 실행 및 파일 보관", "description": "PDF 다운로드 후 파일을 지정 폴더에 보관하여 수동 검증 가능하게 함", "actions": [ @@ -471,7 +471,7 @@ } }, { - "id": "16-pdf-3", + "id": 21, "name": "⚠️ PDF 파일 유효성 검증", "description": "다운로드된 PDF 파일의 기본 유효성 검사", "actions": [ @@ -491,7 +491,7 @@ } }, { - "id": "16-pdf-4", + "id": 22, "name": "📋 PDF 스타일 수동 확인 체크리스트", "type": "manualVerification", "description": "개발자가 다운로드된 PDF를 열어 시각적으로 확인해야 하는 항목", @@ -557,7 +557,7 @@ } }, { - "id": 17, + "id": 23, "name": "문서 상세 모달 - 닫기", "action": "close_modal", "verification": [ @@ -567,7 +567,7 @@ ] }, { - "id": 18, + "id": 24, "name": "미열람 탭으로 이동", "action": "click_if_exists", "target": "button:has-text('미열람')", @@ -577,7 +577,7 @@ ] }, { - "id": 19, + "id": 25, "name": "열람 처리 - 문서 선택", "action": "click_if_exists", "target": "table tbody tr:first-child input[type='checkbox']", @@ -587,7 +587,7 @@ ] }, { - "id": 20, + "id": 26, "name": "열람 처리 - 확인 다이얼로그", "action": "click_if_exists", "target": "button:has-text('열람')", @@ -600,7 +600,7 @@ ] }, { - "id": 21, + "id": 27, "name": "열람 처리 - URL 안정성 검증 (⚠️ 필수 검증 #2)", "action": "save_url", "verification": [ @@ -628,7 +628,7 @@ } }, { - "id": 22, + "id": 28, "name": "열람 처리 후 데이터 검증", "action": "verify_detail", "verification": [ @@ -638,7 +638,7 @@ ] }, { - "id": 23, + "id": 29, "name": "열람 탭으로 이동하여 검증", "action": "click_if_exists", "target": "button:has-text('열람')", @@ -648,7 +648,7 @@ ] }, { - "id": 24, + "id": 30, "name": "미열람 처리 - 문서 선택", "action": "click_if_exists", "target": "table tbody tr:first-child input[type='checkbox']", @@ -658,7 +658,7 @@ ] }, { - "id": 25, + "id": 31, "name": "미열람 처리 - 확인 다이얼로그", "action": "click_if_exists", "target": "button:has-text('미열람')", @@ -671,7 +671,7 @@ ] }, { - "id": 26, + "id": 32, "name": "미열람 처리 - URL 안정성 검증 (⚠️ 필수 검증 #2)", "action": "save_url", "verification": [ @@ -698,7 +698,7 @@ } }, { - "id": 27, + "id": 33, "name": "미열람 처리 후 데이터 검증", "action": "verify_detail", "verification": [ @@ -708,7 +708,7 @@ ] }, { - "id": 28, + "id": 34, "name": "일괄 열람 처리 - 다중 선택", "action": "click_if_exists", "target": "button:has-text('미열람')", @@ -718,7 +718,7 @@ ] }, { - "id": 29, + "id": 35, "name": "일괄 열람 처리 - 실행", "action": "click_if_exists", "target": "button:has-text('열람')", @@ -732,7 +732,7 @@ ] }, { - "id": 30, + "id": 36, "name": "날짜 범위 선택기 테스트", "action": "click_if_exists", "target": "button:has-text('당월')", @@ -742,7 +742,7 @@ ] }, { - "id": 31, + "id": 37, "name": "페이지네이션 테스트", "action": "click_if_exists", "target": "button:has-text('2')", @@ -755,7 +755,7 @@ "conditional": "문서 개수가 20개 이상인 경우에만 실행" }, { - "id": 32, + "id": 38, "name": "Console 로그 확인", "action": "verify_element", "target": "body", @@ -766,7 +766,7 @@ ] }, { - "id": 33, + "id": 39, "name": "최종 통계 확인", "action": "click_if_exists", "target": "button:has-text('전체')", @@ -776,6 +776,12 @@ "전체 = 열람 + 미열람 수식 성립", "스크린샷 촬영하여 최종 상태 저장" ] + }, + { + "id": 40, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "mandatoryVerifications": [ diff --git a/sales-client.json b/sales-client.json index b5784a5..65248f9 100644 --- a/sales-client.json +++ b/sales-client.json @@ -50,6 +50,14 @@ }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/sales/client-management-sales-admin" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -60,7 +68,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "거래처 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -72,7 +86,13 @@ "expected": "거래처 테이블 표시" }, { - "id": 4, + "id": 6, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 7, "phase": "CREATE", "name": "[CREATE] 거래처 등록 버튼 클릭", "action": "click_if_exists", @@ -82,7 +102,7 @@ } }, { - "id": 5, + "id": 8, "phase": "CREATE", "name": "[CREATE] 거래처명 입력", "action": "fill", @@ -91,7 +111,7 @@ "clear": true }, { - "id": 6, + "id": 9, "phase": "CREATE", "name": "[CREATE] 사업자번호 입력", "action": "click_if_exists", @@ -100,7 +120,7 @@ "clear": true }, { - "id": 7, + "id": 10, "phase": "CREATE", "name": "[CREATE] 대표자명 입력", "action": "fill", @@ -109,7 +129,7 @@ "clear": true }, { - "id": 8, + "id": 11, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 거래처 저장", "action": "click_if_exists", @@ -123,7 +143,16 @@ "expected": "거래처 등록 완료" }, { - "id": 9, + "id": 12, + "phase": "CREATE", + "name": "[CREATE] 저장 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "등록|완료|성공|저장" + } + }, + { + "id": 13, "phase": "READ", "name": "[READ] 등록된 거래처 검색", "action": "click_if_exists", @@ -132,7 +161,7 @@ "submit": true }, { - "id": 10, + "id": 14, "phase": "READ", "name": "[READ] 등록된 거래처 확인", "action": "verify_detail", @@ -142,7 +171,7 @@ "expected": "등록된 거래처 확인" }, { - "id": 11, + "id": 15, "phase": "READ", "name": "[READ] 거래처 상세 조회", "action": "click_if_exists", @@ -152,7 +181,7 @@ } }, { - "id": 12, + "id": 16, "phase": "UPDATE", "name": "[UPDATE] 거래처 수정 모드 진입", "action": "click_if_exists", @@ -162,7 +191,7 @@ } }, { - "id": 13, + "id": 17, "phase": "UPDATE", "name": "[UPDATE] 대표자명 수정", "action": "fill", @@ -171,7 +200,7 @@ "clear": true }, { - "id": 14, + "id": 18, "phase": "UPDATE", "name": "[UPDATE] 거래처 저장", "action": "click_if_exists", @@ -183,7 +212,16 @@ "expected": "거래처 수정 완료" }, { - "id": 15, + "id": 19, + "phase": "UPDATE", + "name": "[UPDATE] 수정 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "수정|완료|성공|저장" + } + }, + { + "id": 20, "phase": "DELETE", "name": "[DELETE] 거래처 삭제", "action": "click_if_exists", @@ -193,7 +231,7 @@ } }, { - "id": 16, + "id": 21, "phase": "DELETE", "name": "[DELETE] 삭제 확인", "action": "click_if_exists", @@ -205,7 +243,7 @@ "expected": "거래처 삭제 완료" }, { - "id": 17, + "id": 22, "phase": "DELETE", "name": "[DELETE] 삭제 확인", "action": "verify_detail", @@ -215,13 +253,19 @@ "expected": "거래처 삭제 반영" }, { - "id": 18, + "id": 23, "name": "엑셀 다운로드 확인", "action": "verify_elements", "checks": [ "엑셀 다운로드 버튼 존재" ], "expected": "엑셀 다운로드 기능 표시" + }, + { + "id": 24, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "expectedAPIs": [ @@ -271,4 +315,4 @@ "onDeleteFail": "수동 삭제 필요", "cleanupRequired": "E2E_TEST_판매처_* 패턴 데이터 삭제" } -} \ No newline at end of file +} diff --git a/sales-management.json b/sales-management.json index 0095315..b17ec3f 100644 --- a/sales-management.json +++ b/sales-management.json @@ -63,7 +63,7 @@ }, "steps": [ { - "id": 0, + "id": 1, "name": "사이드바 메뉴 전체 펼치기", "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", "actions": [ @@ -87,14 +87,14 @@ "expected": "사이드바 전체 메뉴가 펼쳐짐" }, { - "id": 1, + "id": 2, "name": "로그인", "action": "click_if_exists", "target": "form, [role=\"dialog\"], .modal", "expected": "로그인 성공 후 메인 페이지 이동" }, { - "id": 2, + "id": 3, "name": "2단계 메뉴 진입: 회계관리 > 매출관리", "description": "스크롤하며 회계관리 메뉴를 찾아 클릭 후 매출관리 진입", "navigationPattern": "scrollAndFind", @@ -155,7 +155,7 @@ } }, { - "id": 3, + "id": 4, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -167,7 +167,7 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 4, + "id": 5, "name": "목록 페이지 - 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -186,7 +186,7 @@ "expected": "테이블 컬럼 구조 정상 표시" }, { - "id": 5, + "id": 6, "name": "계정과목명 드롭박스 확인", "action": "verify_elements", "checks": [ @@ -197,7 +197,7 @@ "expected": "계정과목 선택 UI 정상 표시" }, { - "id": 6, + "id": 7, "name": "계정과목명 드롭박스 옵션 확인", "action": "click_if_exists", "target": "accountSubject", @@ -210,14 +210,14 @@ "expected": "계정과목 옵션 목록 표시" }, { - "id": 7, + "id": 8, "name": "체크박스 선택 (계정과목 저장용)", "action": "click_if_exists", "target": "first_row", "expected": "첫 번째 행 체크박스 선택됨" }, { - "id": 8, + "id": 9, "name": "계정과목 변경 - 제품매출 선택", "action": "click_if_exists", "target": "accountSubject", @@ -225,7 +225,7 @@ "expected": "계정과목이 '제품매출'로 변경됨" }, { - "id": 9, + "id": 10, "name": "필수 검증 #2: 계정과목 저장 버튼 클릭", "action": "click_if_exists", "target": "저장", @@ -237,7 +237,7 @@ "expected": "저장 확인 다이얼로그 표시" }, { - "id": 10, + "id": 11, "name": "저장 확인 다이얼로그 - 확인 클릭", "action": "click_if_exists", "checks": [ @@ -248,7 +248,7 @@ "expected": "계정과목 저장 성공" }, { - "id": "10-1", + "id": 12, "name": "⚠️ 필수 검증: 계정과목명 변경 데이터 반영 확인", "action": "verify_data_update", "target": "first_row", @@ -262,21 +262,21 @@ "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!" }, { - "id": 11, + "id": 13, "name": "매출 등록 버튼 클릭", "action": "click_if_exists", "target": "매출 등록", "expected": "매출 등록 페이지로 이동 (/ko/accounting/sales?mode=new)" }, { - "id": 12, + "id": 14, "name": "매출 등록 페이지 - URL 확인", "action": "verify_url", "target": "/ko/accounting/sales?mode=new", "expected": "매출 등록 페이지 URL 정상" }, { - "id": 13, + "id": 15, "name": "매출 등록 페이지 - 기본정보 섹션 확인", "action": "verify_elements", "checks": [ @@ -288,7 +288,7 @@ "expected": "기본정보 섹션 정상 표시" }, { - "id": 14, + "id": 16, "name": "매출번호 자동생성 확인", "action": "verify_field", "target": "salesNo", @@ -299,14 +299,14 @@ "expected": "매출번호 자동생성 확인" }, { - "id": 15, + "id": 17, "name": "거래처명 드롭박스 클릭", "action": "click_if_exists", "target": "vendorId", "expected": "거래처 목록 표시" }, { - "id": 16, + "id": 18, "name": "거래처명 선택", "action": "click_if_exists", "target": "vendorId", @@ -314,7 +314,7 @@ "expected": "거래처가 선택됨" }, { - "id": 17, + "id": 19, "name": "매출유형 드롭박스 확인", "action": "click_if_exists", "target": "salesType", @@ -330,7 +330,7 @@ "expected": "매출유형 옵션 목록 표시" }, { - "id": 18, + "id": 20, "name": "매출유형 선택 - 제품매출", "action": "click_if_exists", "target": "salesType", @@ -338,7 +338,7 @@ "expected": "매출유형이 '제품매출'로 선택됨" }, { - "id": 19, + "id": 21, "name": "품목정보 섹션 확인", "action": "verify_elements", "checks": [ @@ -350,35 +350,35 @@ "expected": "품목정보 섹션 정상 표시" }, { - "id": 20, + "id": 22, "name": "품목 동적 추가 - 추가 버튼 클릭", "action": "click_if_exists", "target": "품목 추가", "expected": "새로운 품목 행 추가됨" }, { - "id": 21, + "id": 23, "name": "품목 행 개수 확인 (2개)", "action": "verify_row_count", "target": "items_table", "expected": "품목 행이 2개로 증가" }, { - "id": 22, + "id": 24, "name": "품목 동적 삭제 - 두 번째 행 삭제", "action": "click_if_exists", "target": "remove_item_row_2", "expected": "두 번째 품목 행 삭제됨" }, { - "id": 23, + "id": 25, "name": "품목 행 개수 확인 (1개)", "action": "verify_row_count", "target": "items_table", "expected": "품목 행이 1개로 감소" }, { - "id": 24, + "id": 26, "name": "품목명 입력", "action": "click_if_exists", "target": "items[0].itemName", @@ -386,7 +386,7 @@ "expected": "품목명 입력됨" }, { - "id": 25, + "id": 27, "name": "수량 입력", "action": "click_if_exists", "target": "items[0].quantity", @@ -394,7 +394,7 @@ "expected": "수량 입력됨" }, { - "id": 26, + "id": 28, "name": "단가 입력", "action": "click_if_exists", "target": "items[0].unitPrice", @@ -402,7 +402,7 @@ "expected": "단가 입력됨" }, { - "id": 27, + "id": 29, "name": "자동계산 검증 - 공급가액", "action": "verify_calculated_value", "target": "items[0].supplyAmount", @@ -415,7 +415,7 @@ "expected": "공급가액이 500,000원으로 자동 계산됨" }, { - "id": 28, + "id": 30, "name": "자동계산 검증 - 부가세", "action": "verify_calculated_value", "target": "items[0].vat", @@ -428,7 +428,7 @@ "expected": "부가세가 50,000원으로 자동 계산됨" }, { - "id": 29, + "id": 31, "name": "적요 입력 (선택사항)", "action": "click_if_exists", "target": "items[0].note", @@ -436,7 +436,7 @@ "expected": "적요 입력됨" }, { - "id": 30, + "id": 32, "name": "세금계산서 발행 Switch 확인", "action": "verify_elements", "target": "taxInvoice_section", @@ -448,7 +448,7 @@ "expected": "세금계산서 발행 Switch 정상 표시" }, { - "id": 31, + "id": 33, "name": "세금계산서 발행 Switch ON", "action": "click_if_exists", "target": "taxInvoiceSwitch", @@ -456,7 +456,7 @@ "expected": "세금계산서 발행 Switch가 ON으로 변경됨" }, { - "id": 32, + "id": 34, "name": "세금계산서 발행 Switch OFF", "action": "click_if_exists", "target": "taxInvoiceSwitch", @@ -464,7 +464,7 @@ "expected": "세금계산서 발행 Switch가 OFF로 변경됨" }, { - "id": 33, + "id": 35, "name": "거래명세서 발행 Switch 확인", "action": "verify_elements", "target": "transactionStatement_section", @@ -476,7 +476,7 @@ "expected": "거래명세서 발행 Switch 정상 표시" }, { - "id": 34, + "id": 36, "name": "거래명세서 발행 Switch ON", "action": "click_if_exists", "target": "transactionStatementSwitch", @@ -484,7 +484,7 @@ "expected": "거래명세서 발행 Switch가 ON으로 변경됨" }, { - "id": 35, + "id": 37, "name": "거래명세서 발행 Switch OFF", "action": "click_if_exists", "target": "transactionStatementSwitch", @@ -492,7 +492,7 @@ "expected": "거래명세서 발행 Switch가 OFF로 변경됨" }, { - "id": 36, + "id": 38, "name": "합계 금액 확인", "action": "verify_totals", "checks": [ @@ -503,28 +503,28 @@ "expected": "합계 금액 정상 표시" }, { - "id": 37, + "id": 39, "name": "취소 버튼 동작 테스트", "action": "click_if_exists", "target": "취소", "expected": "취소 확인 다이얼로그 또는 목록 페이지로 이동" }, { - "id": 38, + "id": 40, "name": "취소 확인 - 목록 페이지 복귀", "action": "verify_url", "target": "/ko/accounting/sales", "expected": "매출 목록 페이지로 복귀" }, { - "id": 39, + "id": 41, "name": "다시 매출 등록 페이지 진입", "action": "click_if_exists", "target": "매출 등록", "expected": "매출 등록 페이지로 이동" }, { - "id": 40, + "id": 42, "name": "등록 테스트용 데이터 입력 - 거래처 선택", "action": "click_if_exists", "target": "vendorId", @@ -532,7 +532,7 @@ "expected": "거래처 선택됨" }, { - "id": 41, + "id": 43, "name": "등록 테스트용 데이터 입력 - 매출유형", "action": "click_if_exists", "target": "salesType", @@ -540,7 +540,7 @@ "expected": "매출유형 선택됨" }, { - "id": 42, + "id": 44, "name": "등록 테스트용 데이터 입력 - 품목명", "action": "click_if_exists", "target": "items[0].itemName", @@ -548,7 +548,7 @@ "expected": "품목명 입력됨" }, { - "id": 43, + "id": 45, "name": "등록 테스트용 데이터 입력 - 수량", "action": "click_if_exists", "target": "items[0].quantity", @@ -556,7 +556,7 @@ "expected": "수량 입력됨" }, { - "id": 44, + "id": 46, "name": "등록 테스트용 데이터 입력 - 단가", "action": "click_if_exists", "target": "items[0].unitPrice", @@ -564,7 +564,7 @@ "expected": "단가 입력됨" }, { - "id": 45, + "id": 47, "name": "필수 검증 #2: 등록 버튼 클릭", "action": "click_if_exists", "target": "등록", @@ -577,21 +577,21 @@ "expected": "매출 등록 완료" }, { - "id": 46, + "id": 48, "name": "등록 성공 확인 - 토스트 메시지", "action": "verify_toast", "target": "매출이 등록되었습니다", "expected": "성공 토스트 메시지 표시" }, { - "id": 47, + "id": 49, "name": "등록 성공 확인 - 목록 페이지 이동", "action": "verify_url", "target": "/ko/accounting/sales", "expected": "매출 목록 페이지로 이동" }, { - "id": 48, + "id": 50, "name": "등록된 매출 목록 확인", "action": "verify_table_data", "checks": [ @@ -602,26 +602,32 @@ "expected": "등록된 매출이 목록에 표시됨" }, { - "id": 49, + "id": 51, "name": "거래처 미선택 시 유효성 검증 테스트", "action": "navigate", "target": "/ko/accounting/sales?mode=new", "expected": "매출 등록 페이지 이동" }, { - "id": 50, + "id": 52, "name": "거래처 미선택 상태에서 등록 시도", "action": "click_if_exists", "target": "등록", "expected": "유효성 검증 실패 - 경고 메시지" }, { - "id": 51, + "id": 53, "name": "유효성 검증 메시지 확인", "action": "verify_toast", "target": "거래처를 선택해주세요", "type": "warning", "expected": "거래처 선택 요청 경고 메시지 표시" + }, + { + "id": 54, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "requiredVerifications": [ @@ -723,4 +729,4 @@ "reason": "사용자 요청에 따라 삭제 테스트 제외" } ] -} \ No newline at end of file +} diff --git a/sales-order.json b/sales-order.json index e2d0a7c..a07745c 100644 --- a/sales-order.json +++ b/sales-order.json @@ -57,6 +57,14 @@ }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/sales/order-management-sales" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -67,7 +75,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "수주 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -81,7 +95,13 @@ "expected": "수주 테이블 컬럼 정상 표시" }, { - "id": 4, + "id": 6, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 7, "name": "검색 기능 테스트", "action": "click_if_exists", "target": "input[placeholder*='검색']", @@ -91,7 +111,7 @@ } }, { - "id": 5, + "id": 8, "phase": "CREATE", "name": "[CREATE] 수주 등록 버튼 클릭", "action": "click_if_exists", @@ -102,7 +122,7 @@ } }, { - "id": 6, + "id": 9, "phase": "CREATE", "name": "[CREATE] 수주 정보 입력", "action": "fill_form", @@ -146,7 +166,7 @@ "note": "타임스탬프로 고유성 보장" }, { - "id": 7, + "id": 10, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 등록 저장", "action": "click_if_exists", @@ -160,14 +180,23 @@ "expected": "수주 등록 완료" }, { - "id": "7-modal-close", + "id": 11, + "phase": "CREATE", + "name": "[CREATE] 저장 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "등록|완료|성공|저장" + } + }, + { + "id": 12, "phase": "CREATE", "name": "[CREATE] 모달 닫기 확인", "action": "close_modal_if_open", "expected": "모달 닫힘" }, { - "id": 8, + "id": 13, "phase": "CREATE", "name": "[CREATE] 등록 결과 확인", "action": "verify_detail", @@ -182,7 +211,7 @@ } }, { - "id": 9, + "id": 14, "phase": "READ", "name": "[READ] 수주 상세 페이지 진입", "action": "click_if_exists", @@ -197,7 +226,7 @@ } }, { - "id": 10, + "id": 15, "phase": "READ", "name": "[READ] 상세 정보 확인", "action": "verify_detail", @@ -210,7 +239,7 @@ "expected": "입력한 데이터와 일치" }, { - "id": 11, + "id": 16, "phase": "UPDATE", "name": "[UPDATE] 수정 모드 진입", "action": "click_if_exists", @@ -221,7 +250,7 @@ } }, { - "id": 12, + "id": 17, "phase": "UPDATE", "name": "[UPDATE] 수량 수정", "action": "click_if_exists", @@ -230,7 +259,7 @@ "clear": true }, { - "id": 13, + "id": 18, "phase": "UPDATE", "name": "[UPDATE] 메모 수정", "action": "click_if_exists", @@ -239,7 +268,7 @@ "clear": true }, { - "id": 14, + "id": 19, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 수정 저장", "action": "click_if_exists", @@ -253,7 +282,16 @@ "expected": "수정 완료" }, { - "id": 15, + "id": 20, + "phase": "UPDATE", + "name": "[UPDATE] 수정 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "수정|완료|성공|저장" + } + }, + { + "id": 21, "phase": "UPDATE", "name": "[UPDATE] 수정 결과 확인", "action": "verify_detail", @@ -264,7 +302,7 @@ "expected": "수정된 데이터 반영" }, { - "id": 16, + "id": 22, "phase": "DELETE", "name": "[DELETE] 삭제 버튼 클릭", "action": "click_if_exists", @@ -275,7 +313,7 @@ } }, { - "id": 17, + "id": 23, "phase": "DELETE", "name": "[DELETE] 필수 검증 #6: 삭제 확인", "action": "click_if_exists", @@ -288,7 +326,7 @@ "expected": "삭제 완료 및 목록 복귀" }, { - "id": 18, + "id": 24, "phase": "DELETE", "name": "[DELETE] 삭제 결과 확인", "action": "verify_detail", @@ -297,6 +335,12 @@ "row_exists": false, "message": "테스트 수주가 목록에서 제거됨" } + }, + { + "id": 25, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "expectedAPIs": [ @@ -369,4 +413,4 @@ "onDeleteFail": "테스트 수주 수동 삭제 필요", "cleanupRequired": "E2E_TEST_ 접두사 수주는 테스트 데이터" } -} \ No newline at end of file +} diff --git a/sales-pricing.json b/sales-pricing.json index eb06d9b..1c9ef1b 100644 --- a/sales-pricing.json +++ b/sales-pricing.json @@ -4,7 +4,14 @@ "name": "단가관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "판매관리 > 단가관리 메뉴의 단가 목록 조회 및 UI 검증 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -28,11 +35,22 @@ "level2": "단가관리", "expected": { "url_contains": "/sales/pricing", - "visible": ["단가관리", "단가"] + "visible": [ + "단가관리", + "단가" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/sales/pricing-management" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -43,7 +61,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "단가 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -55,7 +79,49 @@ "expected": "단가 테이블 표시" }, { - "id": 4, + "id": 6, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 7, + "name": "⚠️ 필수 검증: 검색 기능", + "action": "search", + "value": "가우스" + }, + { + "id": 8, + "name": "검색 결과 대기", + "action": "wait", + "duration": 1000 + }, + { + "id": 9, + "name": "검색 결과 데이터 검증", + "action": "evaluate", + "script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Search result: ' + rows.length + ' rows';\n })()" + }, + { + "id": 10, + "name": "검색 초기화", + "action": "evaluate", + "script": "(() => {\n const selectors = ['input[type=\"search\"]', 'input[placeholder*=\"검색\"]', 'input[placeholder*=\"Search\"]', 'input[role=\"searchbox\"]', '[class*=\"search\"] input'];\n for (const sel of selectors) {\n const el = document.querySelector(sel);\n if (el) {\n const nativeSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;\n if (nativeSetter) nativeSetter.call(el, '');\n else el.value = '';\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n return 'Search cleared';\n }\n }\n return 'No search input found (ok)';\n })()" + }, + { + "id": 11, + "name": "검색 초기화 결과 대기", + "action": "wait", + "duration": 1000 + }, + { + "id": 12, + "name": "검색 초기화 및 복원 확인", + "action": "evaluate", + "script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Restored: ' + rows.length + ' rows';\n })()" + }, + { + "id": 13, "name": "단가 UI 요소 확인", "action": "verify_elements", "checks": [ @@ -66,7 +132,7 @@ "expected": "단가 UI 정상 표시" }, { - "id": 5, + "id": 14, "phase": "READ", "name": "[READ] 단가 목록 확인", "action": "verify_detail", @@ -76,14 +142,14 @@ "expected": "단가 목록 정상" }, { - "id": 6, + "id": 15, "phase": "READ", "name": "[READ] 첫 번째 단가 클릭", "action": "click_if_exists", "target": "table tbody tr:first-child" }, { - "id": 7, + "id": 16, "phase": "READ", "name": "[READ] 단가 상세 정보 확인", "action": "verify_detail", @@ -93,19 +159,41 @@ "expected": "단가 상세 정보 확인" }, { - "id": 8, + "id": 17, "name": "상세 모달/페이지 닫기", "action": "click_if_exists", "target": "button:has-text('닫기'), button:has-text('목록'), button:has-text('Close')" }, { - "id": 9, + "id": 18, + "name": "테이블 행 클릭 - 상세 페이지 이동", + "action": "click_first_row" + }, + { + "id": 19, + "name": "상세 페이지 로딩 대기", + "action": "wait", + "duration": 1000 + }, + { + "id": 20, + "name": "상세 페이지 - 콘텐츠 확인", + "action": "evaluate", + "script": "(() => {\n const inputs = document.querySelectorAll('input:not([type=\"hidden\"]), textarea, select');\n const buttons = document.querySelectorAll('button');\n const hasDetail = inputs.length > 0 || document.body.innerText.includes('상세') || document.body.innerText.includes('수정');\n return hasDetail ? 'Detail page: ' + inputs.length + ' inputs, ' + buttons.length + ' buttons' : 'List page (no detail view)';\n })()" + }, + { + "id": 21, + "name": "모달/상세 닫기", + "action": "close_modal_if_open" + }, + { + "id": 22, "name": "모달 닫기 확인", "action": "close_modal_if_open", "expected": "모달 닫힘" }, { - "id": 10, + "id": 23, "name": "목록 복귀 확인", "action": "verify_table", "checks": [ @@ -114,7 +202,7 @@ "expected": "목록 복귀 확인" }, { - "id": 11, + "id": 24, "name": "엑셀 다운로드 버튼 확인", "action": "verify_elements", "checks": [ @@ -123,7 +211,19 @@ "expected": "엑셀 다운로드 기능 확인" }, { - "id": 12, + "id": 25, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 26, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 27, "name": "단가관리 페이지 최종 확인", "action": "verify_elements", "checks": [ @@ -144,7 +244,9 @@ { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "단가 목록, 등록 버튼, 검색 기능 존재" } ], diff --git a/sales-quotation.json b/sales-quotation.json index 2b39076..3da04ba 100644 --- a/sales-quotation.json +++ b/sales-quotation.json @@ -56,6 +56,14 @@ }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/sales/quote-management" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -66,7 +74,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "견적 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -80,7 +94,13 @@ "expected": "견적 테이블 컬럼 정상 표시" }, { - "id": 4, + "id": 6, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 7, "name": "검색 기능 테스트", "action": "click_if_exists", "target": "input[placeholder*='검색']", @@ -90,7 +110,7 @@ } }, { - "id": 5, + "id": 8, "phase": "CREATE", "name": "[CREATE] 견적 등록 버튼 클릭", "action": "click_if_exists", @@ -101,7 +121,7 @@ } }, { - "id": 6, + "id": 9, "phase": "CREATE", "name": "[CREATE] 견적 정보 입력", "action": "fill_form", @@ -140,7 +160,7 @@ "note": "타임스탬프로 고유성 보장" }, { - "id": 7, + "id": 10, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 등록 저장", "action": "click_if_exists", @@ -154,14 +174,23 @@ "expected": "견적 등록 완료" }, { - "id": "7-modal-close", + "id": 11, + "phase": "CREATE", + "name": "[CREATE] 저장 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "등록|완료|성공|저장" + } + }, + { + "id": 12, "phase": "CREATE", "name": "[CREATE] 모달 닫기 확인", "action": "close_modal_if_open", "expected": "모달 닫힘" }, { - "id": 8, + "id": 13, "phase": "CREATE", "name": "[CREATE] 등록 결과 확인", "action": "verify_detail", @@ -175,7 +204,7 @@ } }, { - "id": 9, + "id": 14, "phase": "READ", "name": "[READ] 견적 상세 페이지 진입", "action": "click_if_exists", @@ -190,7 +219,7 @@ } }, { - "id": 10, + "id": 15, "phase": "READ", "name": "[READ] 상세 정보 확인", "action": "verify_detail", @@ -202,7 +231,7 @@ "expected": "입력한 데이터와 일치" }, { - "id": 11, + "id": 16, "phase": "UPDATE", "name": "[UPDATE] 수정 모드 진입", "action": "click_if_exists", @@ -213,7 +242,7 @@ } }, { - "id": 12, + "id": 17, "phase": "UPDATE", "name": "[UPDATE] 수량 수정", "action": "click_if_exists", @@ -222,7 +251,7 @@ "clear": true }, { - "id": 13, + "id": 18, "phase": "UPDATE", "name": "[UPDATE] 메모 수정", "action": "click_if_exists", @@ -231,7 +260,7 @@ "clear": true }, { - "id": 14, + "id": 19, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 수정 저장", "action": "click_if_exists", @@ -245,7 +274,16 @@ "expected": "수정 완료" }, { - "id": 15, + "id": 20, + "phase": "UPDATE", + "name": "[UPDATE] 수정 완료 토스트 확인", + "action": "verify_toast", + "verify": { + "contains": "수정|완료|성공|저장" + } + }, + { + "id": 21, "phase": "UPDATE", "name": "[UPDATE] 수정 결과 확인", "action": "verify_detail", @@ -256,7 +294,7 @@ "expected": "수정된 데이터 반영" }, { - "id": 16, + "id": 22, "phase": "DELETE", "name": "[DELETE] 삭제 버튼 클릭", "action": "click_if_exists", @@ -267,7 +305,7 @@ } }, { - "id": 17, + "id": 23, "phase": "DELETE", "name": "[DELETE] 필수 검증 #6: 삭제 확인", "action": "click_if_exists", @@ -280,7 +318,7 @@ "expected": "삭제 완료 및 목록 복귀" }, { - "id": 18, + "id": 24, "phase": "DELETE", "name": "[DELETE] 삭제 결과 확인", "action": "verify_detail", @@ -289,6 +327,12 @@ "row_exists": false, "message": "테스트 견적이 목록에서 제거됨" } + }, + { + "id": 25, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "expectedAPIs": [ @@ -361,4 +405,4 @@ "onDeleteFail": "테스트 견적 수동 삭제 필요", "cleanupRequired": "E2E_TEST_ 접두사 견적은 테스트 데이터" } -} \ No newline at end of file +} diff --git a/settings-account.json b/settings-account.json index 3c0a8aa..197d1fa 100644 --- a/settings-account.json +++ b/settings-account.json @@ -3,7 +3,14 @@ "name": "계정정보 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "설정 > 계정정보 메뉴의 계정 정보 조회 및 UI 검증 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -27,11 +34,22 @@ "level2": "계정정보", "expected": { "url_contains": "/settings/account", - "visible": ["계정정보", "프로필"] + "visible": [ + "계정정보", + "프로필" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/settings/account-info" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -42,7 +60,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "계정 정보 폼 구조 확인", "action": "verify_elements", "checks": [ @@ -54,7 +78,7 @@ "expected": "계정 정보 폼 정상 표시" }, { - "id": 4, + "id": 6, "phase": "READ", "name": "[READ] 현재 계정 정보 확인", "action": "verify_detail", @@ -66,7 +90,7 @@ "expected": "현재 계정 정보 정상 표시" }, { - "id": 5, + "id": 7, "phase": "UPDATE", "name": "[UPDATE] 프로필 수정 모드 진입", "action": "click_if_exists", @@ -77,21 +101,21 @@ } }, { - "id": 6, + "id": 8, "phase": "UPDATE", "name": "[UPDATE] 표시 이름 필드 확인", "action": "click_if_exists", "target": "input[name*='displayName'], input[name*='name'], input[placeholder*='이름']" }, { - "id": 7, + "id": 9, "phase": "UPDATE", "name": "[UPDATE] 연락처 필드 확인", "action": "click_if_exists", "target": "input[name*='phone'], input[type='tel']" }, { - "id": 8, + "id": 10, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 프로필 저장", "action": "click_if_exists", @@ -104,7 +128,7 @@ "expected": "프로필 저장 완료" }, { - "id": 9, + "id": 11, "phase": "UPDATE", "name": "[UPDATE] 저장 결과 확인", "action": "verify_detail", @@ -114,7 +138,7 @@ "expected": "프로필 정보 표시" }, { - "id": 10, + "id": 12, "name": "비밀번호 변경 버튼 확인", "action": "verify_elements", "checks": [ @@ -123,13 +147,25 @@ "expected": "비밀번호 변경 버튼 표시" }, { - "id": 11, + "id": 13, "name": "비밀번호 변경 모달 열기", "action": "click_if_exists", "target": "button:has-text('비밀번호 변경'), button:has-text('비밀번호')" }, { - "id": 12, + "id": 14, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 15, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 16, "name": "비밀번호 변경 모달 닫기", "action": "close_modal_if_open", "expected": "모달 닫힘" @@ -146,7 +182,9 @@ { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "계정 정보 폼, 수정 버튼 존재" } ], diff --git a/settings-attendance.json b/settings-attendance.json index 88d253b..2399a6d 100644 --- a/settings-attendance.json +++ b/settings-attendance.json @@ -3,7 +3,14 @@ "name": "근태설정 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "설정 > 근태설정 메뉴의 근태 정책 조회 및 UI 검증 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -27,11 +34,22 @@ "level2": "근태설정", "expected": { "url_contains": "/settings/attendance", - "visible": ["근태설정", "근태"] + "visible": [ + "근태설정", + "근태" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/settings/attendance-settings" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -42,7 +60,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "근태 설정 폼 구조 확인", "action": "verify_elements", "checks": [ @@ -54,7 +78,7 @@ "expected": "근태 설정 폼 정상 표시" }, { - "id": 4, + "id": 6, "phase": "READ", "name": "[READ] 현재 근태 설정 확인", "action": "verify_detail", @@ -66,28 +90,28 @@ "expected": "현재 근태 설정 정상 표시" }, { - "id": 5, + "id": 7, "phase": "UPDATE", "name": "[UPDATE] 지각 기준 필드 확인", "action": "click_if_exists", "target": "input[name*='late'], input[placeholder*='지각']" }, { - "id": 6, + "id": 8, "phase": "UPDATE", "name": "[UPDATE] 조퇴 기준 필드 확인", "action": "click_if_exists", "target": "input[name*='early'], input[placeholder*='조퇴']" }, { - "id": 7, + "id": 9, "phase": "UPDATE", "name": "[UPDATE] 자동 퇴근 시간 필드 확인", "action": "click_if_exists", "target": "input[name*='autoCheckout'], input[type='time']" }, { - "id": 8, + "id": 10, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 근태 설정 저장", "action": "click_if_exists", @@ -100,7 +124,7 @@ "expected": "근태 설정 저장 완료" }, { - "id": 9, + "id": 11, "phase": "UPDATE", "name": "[UPDATE] 저장 결과 확인", "action": "verify_detail", @@ -110,7 +134,7 @@ "expected": "설정 정보 표시" }, { - "id": 10, + "id": 12, "name": "위치 기반 출퇴근 설정 확인", "action": "verify_elements", "checks": [ @@ -120,7 +144,7 @@ "expected": "위치 기반 설정 표시" }, { - "id": 11, + "id": 13, "name": "근태 이상 알림 설정 확인", "action": "verify_elements", "checks": [ @@ -130,7 +154,19 @@ "expected": "알림 설정 표시" }, { - "id": 12, + "id": 14, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 15, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 16, "name": "부서별 근태 설정 확인", "action": "verify_elements", "checks": [ @@ -150,7 +186,9 @@ { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "근태 설정 폼, 저장 버튼 존재" } ], diff --git a/settings-bank-account.json b/settings-bank-account.json index 8fd3892..d1adf23 100644 --- a/settings-bank-account.json +++ b/settings-bank-account.json @@ -3,7 +3,14 @@ "name": "계좌관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "설정 > 계좌관리 메뉴의 계좌 목록 조회 및 UI 검증 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -27,11 +34,22 @@ "level2": "계좌관리", "expected": { "url_contains": "/settings/accounts", - "visible": ["계좌관리", "계좌"] + "visible": [ + "계좌관리", + "계좌" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/settings/accounts" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -42,7 +60,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "계좌 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -54,7 +78,49 @@ "expected": "계좌 테이블 컬럼 정상 표시" }, { - "id": 4, + "id": 6, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 7, + "name": "⚠️ 필수 검증: 검색 기능", + "action": "search", + "value": "테스트" + }, + { + "id": 8, + "name": "검색 결과 대기", + "action": "wait", + "duration": 1000 + }, + { + "id": 9, + "name": "검색 결과 데이터 검증", + "action": "evaluate", + "script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Search result: ' + rows.length + ' rows';\n })()" + }, + { + "id": 10, + "name": "검색 초기화", + "action": "evaluate", + "script": "(() => {\n const selectors = ['input[type=\"search\"]', 'input[placeholder*=\"검색\"]', 'input[placeholder*=\"Search\"]', 'input[role=\"searchbox\"]', '[class*=\"search\"] input'];\n for (const sel of selectors) {\n const el = document.querySelector(sel);\n if (el) {\n const nativeSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;\n if (nativeSetter) nativeSetter.call(el, '');\n else el.value = '';\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n return 'Search cleared';\n }\n }\n return 'No search input found (ok)';\n })()" + }, + { + "id": 11, + "name": "검색 초기화 결과 대기", + "action": "wait", + "duration": 1000 + }, + { + "id": 12, + "name": "검색 초기화 및 복원 확인", + "action": "evaluate", + "script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Restored: ' + rows.length + ' rows';\n })()" + }, + { + "id": 13, "name": "검색 기능 확인", "action": "click_if_exists", "target": "input[placeholder*='검색']", @@ -63,7 +129,7 @@ } }, { - "id": 5, + "id": 14, "name": "계좌 등록 버튼 확인", "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('계좌 등록'), button:has-text('추가')", @@ -72,7 +138,7 @@ } }, { - "id": 6, + "id": 15, "name": "등록 폼/모달 확인", "action": "verify_elements", "checks": [ @@ -83,13 +149,13 @@ "expected": "계좌 등록 폼 표시" }, { - "id": 7, + "id": 16, "name": "등록 모달 닫기", "action": "close_modal_if_open", "expected": "모달 닫힘" }, { - "id": 8, + "id": 17, "phase": "READ", "name": "[READ] 계좌 목록 데이터 확인", "action": "verify_detail", @@ -99,14 +165,14 @@ "expected": "계좌 목록 정상 표시" }, { - "id": 9, + "id": 18, "phase": "READ", "name": "[READ] 첫 번째 행 클릭", "action": "click_if_exists", "target": "table tbody tr:first-child" }, { - "id": 10, + "id": 19, "phase": "READ", "name": "[READ] 계좌 상세 정보 확인", "action": "verify_detail", @@ -116,13 +182,25 @@ "expected": "계좌 상세 정보 확인" }, { - "id": 11, + "id": 20, "name": "상세 모달 닫기", "action": "close_modal_if_open", "expected": "모달 닫힘" }, { - "id": 12, + "id": 21, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 22, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 23, "name": "계좌 목록 최종 확인", "action": "verify_elements", "checks": [ @@ -143,7 +221,9 @@ { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "계좌 목록, 등록 버튼, 필터 존재" } ], diff --git a/settings-company.json b/settings-company.json index b774e58..635e095 100644 --- a/settings-company.json +++ b/settings-company.json @@ -3,7 +3,14 @@ "name": "회사정보 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "설정 > 회사정보 메뉴의 회사 정보 조회/수정 기능 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -33,11 +40,22 @@ "level2": "회사정보", "expected": { "url_contains": "/company", - "visible": ["회사정보", "회사"] + "visible": [ + "회사정보", + "회사" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/company-info" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -48,7 +66,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "회사 정보 폼 구조 확인", "action": "verify_elements", "checks": [ @@ -61,7 +85,7 @@ "expected": "회사 정보 폼 정상 표시" }, { - "id": 4, + "id": 6, "phase": "READ", "name": "[READ] 현재 회사 정보 확인", "action": "verify_detail", @@ -73,7 +97,7 @@ "expected": "현재 회사 정보 정상 표시" }, { - "id": 5, + "id": 7, "phase": "UPDATE", "name": "[UPDATE] 회사 정보 수정 모드 진입", "action": "click_if_exists", @@ -84,21 +108,21 @@ } }, { - "id": 6, + "id": 8, "phase": "UPDATE", "name": "[UPDATE] 회사 전화번호 수정", "action": "click_if_exists", "target": "input[name*='phone'], input[placeholder*='전화']" }, { - "id": 7, + "id": 9, "phase": "UPDATE", "name": "[UPDATE] 팩스번호 수정", "action": "click_if_exists", "target": "input[name*='fax'], input[placeholder*='팩스']" }, { - "id": 8, + "id": 10, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 회사 정보 저장", "action": "click_if_exists", @@ -112,7 +136,7 @@ "expected": "회사 정보 저장 완료" }, { - "id": 9, + "id": 11, "phase": "UPDATE", "name": "[UPDATE] 저장 결과 확인", "action": "verify_detail", @@ -123,7 +147,7 @@ "expected": "수정된 정보 반영" }, { - "id": 10, + "id": 12, "name": "로고 이미지 영역 확인", "action": "verify_elements", "checks": [ @@ -133,7 +157,7 @@ "expected": "로고 영역 표시" }, { - "id": 11, + "id": 13, "name": "사업자등록증 영역 확인", "action": "verify_elements", "checks": [ @@ -143,7 +167,19 @@ "expected": "첨부 영역 표시" }, { - "id": 12, + "id": 14, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 15, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 16, "name": "법인등록번호 확인", "action": "verify_elements", "checks": [ @@ -173,13 +209,17 @@ { "id": 2, "name": "저장 버튼", - "steps": [8], + "steps": [ + 8 + ], "criteria": "API 호출 + 성공 토스트 + 정보 반영" }, { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "회사 정보 폼, 수정 버튼 존재" } ], diff --git a/settings-notification.json b/settings-notification.json index 30b2658..2391d99 100644 --- a/settings-notification.json +++ b/settings-notification.json @@ -3,7 +3,14 @@ "name": "알림설정 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "설정 > 알림설정 메뉴의 알림 설정 조회/수정/저장 기능 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -35,11 +42,22 @@ "level2": "알림설정", "expected": { "url_contains": "/settings/notification-settings", - "visible": ["알림설정", "알림"] + "visible": [ + "알림설정", + "알림" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/settings/notification-settings" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -50,7 +68,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "알림 설정 폼 구조 확인", "action": "verify_elements", "checks": [ @@ -62,7 +86,7 @@ "expected": "알림 설정 폼 정상 표시" }, { - "id": 4, + "id": 6, "phase": "READ", "name": "[READ] 현재 알림 설정 확인", "action": "verify_detail", @@ -74,7 +98,7 @@ "expected": "현재 알림 설정 정상 표시" }, { - "id": 5, + "id": 7, "phase": "UPDATE", "name": "[UPDATE] 이메일 알림 토글", "action": "click_if_exists", @@ -84,7 +108,7 @@ } }, { - "id": 6, + "id": 8, "phase": "UPDATE", "name": "[UPDATE] 푸시 알림 토글", "action": "click_if_exists", @@ -94,7 +118,7 @@ } }, { - "id": 7, + "id": 9, "phase": "UPDATE", "name": "[UPDATE] 결재 알림 설정", "action": "click_if_exists", @@ -104,7 +128,7 @@ } }, { - "id": 8, + "id": 10, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 알림 설정 저장", "action": "click_if_exists", @@ -118,7 +142,7 @@ "expected": "알림 설정 저장 완료" }, { - "id": 9, + "id": 11, "phase": "UPDATE", "name": "[UPDATE] 저장 결과 확인", "action": "verify_detail", @@ -128,7 +152,7 @@ "expected": "수정된 설정 반영" }, { - "id": 10, + "id": 12, "name": "알림 유형별 설정 확인", "action": "verify_elements", "checks": [ @@ -139,7 +163,7 @@ "expected": "알림 유형별 설정 표시" }, { - "id": 11, + "id": 13, "name": "알림 수신 시간 설정 확인", "action": "verify_elements", "checks": [ @@ -149,7 +173,19 @@ "expected": "시간 설정 표시" }, { - "id": 12, + "id": 14, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 15, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 16, "name": "알림 테스트 전송 확인", "action": "verify_elements", "checks": [ @@ -179,13 +215,17 @@ { "id": 2, "name": "저장 버튼", - "steps": [8], + "steps": [ + 8 + ], "criteria": "API 호출 + 성공 토스트 + 설정 반영" }, { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "알림 설정 폼, 저장 버튼 존재" } ], diff --git a/settings-permission.json b/settings-permission.json index fe5eb66..2a1edd5 100644 --- a/settings-permission.json +++ b/settings-permission.json @@ -3,7 +3,14 @@ "name": "권한관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "설정 > 권한관리 메뉴의 권한 그룹 목록 조회 및 UI 검증 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -27,11 +34,22 @@ "level2": "권한관리", "expected": { "url_contains": "/settings/permissions", - "visible": ["권한관리", "권한"] + "visible": [ + "권한관리", + "권한" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/settings/permissions" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -42,7 +60,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "권한 그룹 목록 확인", "action": "verify_elements", "checks": [ @@ -53,17 +77,20 @@ "expected": "권한 목록 정상 표시" }, { - "id": 4, + "id": 6, "phase": "READ", "name": "[READ] 첫 번째 권한 그룹 클릭", "action": "click_if_exists", "target": "table tbody tr:first-child, li:first-child, [class*='list'] > *:first-child", "expected": { - "visible": ["메뉴 권한", "기능 권한"] + "visible": [ + "메뉴 권한", + "기능 권한" + ] } }, { - "id": 5, + "id": 7, "phase": "READ", "name": "[READ] 권한 체크박스 구조 확인", "action": "verify_elements", @@ -74,7 +101,7 @@ "expected": "권한 체크박스 매트릭스 표시" }, { - "id": 6, + "id": 8, "phase": "READ", "name": "[READ] 권한 상세 정보 확인", "action": "verify_detail", @@ -85,7 +112,7 @@ "expected": "권한 상세 정보 표시" }, { - "id": 7, + "id": 9, "name": "권한 추가 버튼 확인", "action": "click_if_exists", "target": "button:has-text('추가'), button:has-text('권한 추가'), button:has-text('역할 추가')", @@ -94,7 +121,7 @@ } }, { - "id": 8, + "id": 10, "name": "추가 모달 확인", "action": "verify_elements", "checks": [ @@ -104,13 +131,35 @@ "expected": "권한 추가 폼 표시" }, { - "id": 9, + "id": 11, + "name": "테이블 행 클릭 - 상세 페이지 이동", + "action": "click_first_row" + }, + { + "id": 12, + "name": "상세 페이지 로딩 대기", + "action": "wait", + "duration": 1000 + }, + { + "id": 13, + "name": "상세 페이지 - 콘텐츠 확인", + "action": "evaluate", + "script": "(() => {\n const inputs = document.querySelectorAll('input:not([type=\"hidden\"]), textarea, select');\n const buttons = document.querySelectorAll('button');\n const hasDetail = inputs.length > 0 || document.body.innerText.includes('상세') || document.body.innerText.includes('수정');\n return hasDetail ? 'Detail page: ' + inputs.length + ' inputs, ' + buttons.length + ' buttons' : 'List page (no detail view)';\n })()" + }, + { + "id": 14, + "name": "모달/상세 닫기", + "action": "close_modal_if_open" + }, + { + "id": 15, "name": "추가 모달 닫기", "action": "close_modal_if_open", "expected": "모달 닫힘" }, { - "id": 10, + "id": 16, "name": "저장 버튼 존재 확인", "action": "verify_elements", "checks": [ @@ -119,7 +168,7 @@ "expected": "저장 버튼 표시" }, { - "id": 11, + "id": 17, "name": "삭제 버튼 존재 확인", "action": "verify_elements", "checks": [ @@ -128,7 +177,19 @@ "expected": "삭제 기능 확인" }, { - "id": 12, + "id": 18, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 19, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 20, "name": "권한관리 페이지 최종 확인", "action": "verify_detail", "checks": [ @@ -148,7 +209,9 @@ { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "권한 목록, 추가 버튼, 권한 체크박스 존재" } ], diff --git a/settings-popup.json b/settings-popup.json index 77ce822..d033ee7 100644 --- a/settings-popup.json +++ b/settings-popup.json @@ -3,7 +3,14 @@ "name": "팝업관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "설정 > 팝업관리 메뉴의 팝업 목록 조회 및 UI 검증 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -27,11 +34,22 @@ "level2": "팝업관리", "expected": { "url_contains": "/settings/popup", - "visible": ["팝업관리", "팝업"] + "visible": [ + "팝업관리", + "팝업" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/settings/popup-management" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -42,7 +60,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "팝업 테이블 구조 확인", "action": "verify_table", "checks": [ @@ -54,7 +78,49 @@ "expected": "팝업 테이블 표시" }, { - "id": 4, + "id": 6, + "name": "목록 필터 테스트", + "action": "evaluate", + "script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()" + }, + { + "id": 7, + "name": "⚠️ 필수 검증: 검색 기능", + "action": "search", + "value": "테스트" + }, + { + "id": 8, + "name": "검색 결과 대기", + "action": "wait", + "duration": 1000 + }, + { + "id": 9, + "name": "검색 결과 데이터 검증", + "action": "evaluate", + "script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Search result: ' + rows.length + ' rows';\n })()" + }, + { + "id": 10, + "name": "검색 초기화", + "action": "evaluate", + "script": "(() => {\n const selectors = ['input[type=\"search\"]', 'input[placeholder*=\"검색\"]', 'input[placeholder*=\"Search\"]', 'input[role=\"searchbox\"]', '[class*=\"search\"] input'];\n for (const sel of selectors) {\n const el = document.querySelector(sel);\n if (el) {\n const nativeSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;\n if (nativeSetter) nativeSetter.call(el, '');\n else el.value = '';\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n return 'Search cleared';\n }\n }\n return 'No search input found (ok)';\n })()" + }, + { + "id": 11, + "name": "검색 초기화 결과 대기", + "action": "wait", + "duration": 1000 + }, + { + "id": 12, + "name": "검색 초기화 및 복원 확인", + "action": "evaluate", + "script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Restored: ' + rows.length + ' rows';\n })()" + }, + { + "id": 13, "name": "기존 팝업 확인", "action": "verify_elements", "checks": [ @@ -63,7 +129,7 @@ "expected": "팝업 목록 상태 확인" }, { - "id": 5, + "id": 14, "name": "팝업 등록 버튼 확인", "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('추가'), button:has-text('신규')", @@ -72,7 +138,7 @@ } }, { - "id": 6, + "id": 15, "name": "등록 폼 요소 확인", "action": "verify_elements", "checks": [ @@ -83,13 +149,13 @@ "expected": "팝업 등록 폼 표시" }, { - "id": 7, + "id": 16, "name": "등록 모달 닫기", "action": "close_modal_if_open", "expected": "모달 닫힘" }, { - "id": 8, + "id": 17, "phase": "READ", "name": "[READ] 팝업 목록 데이터 확인", "action": "verify_detail", @@ -99,14 +165,14 @@ "expected": "팝업 목록 정상 표시" }, { - "id": 9, + "id": 18, "phase": "READ", "name": "[READ] 첫 번째 행 클릭", "action": "click_if_exists", "target": "table tbody tr:first-child" }, { - "id": 10, + "id": 19, "phase": "READ", "name": "[READ] 팝업 상세 정보 확인", "action": "verify_detail", @@ -116,13 +182,25 @@ "expected": "팝업 상세 정보 확인" }, { - "id": 11, + "id": 20, "name": "상세 모달 닫기", "action": "close_modal_if_open", "expected": "모달 닫힘" }, { - "id": 12, + "id": 21, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 22, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 23, "name": "팝업관리 페이지 최종 확인", "action": "verify_elements", "checks": [ @@ -143,7 +221,9 @@ { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "팝업 목록, 등록 버튼, 상태 필터 존재" } ], diff --git a/settings-position.json b/settings-position.json index 66b7f41..24774a0 100644 --- a/settings-position.json +++ b/settings-position.json @@ -3,7 +3,14 @@ "name": "직책관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "설정 > 직책관리 목록/검색/상세 기능 검증", "baseUrl": "https://dev.codebridge-x.com", @@ -25,47 +32,80 @@ "action": "menu_navigate", "level1": "설정", "level2": "직책관리", - "expected": { "url_contains": "/settings" } + "expected": { + "url_contains": "/settings" + } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/settings/titles" + } + }, + { + "id": 3, "name": "목업 감지", "action": "verify_not_mockup" }, - { - "id": 3, - "name": "직책관리 페이지 확인", - "action": "verify_detail", - "checks": ["visible_text:직책"] - }, { "id": 4, - "name": "설정 페이지 확인", - "action": "verify_detail", - "checks": ["visible_text:설정"] + "name": "테이블 구조 검증", + "action": "verify_table" }, { "id": 5, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 6, + "name": "직책관리 페이지 확인", + "action": "verify_detail", + "checks": [ + "visible_text:직책" + ] + }, + { + "id": 7, + "name": "설정 페이지 확인", + "action": "verify_detail", + "checks": [ + "visible_text:설정" + ] + }, + { + "id": 8, "name": "추가 버튼 클릭", "action": "click_if_exists", "target": "button:has-text('추가'), button:has-text('등록'), button:has-text('신규')" }, { - "id": 6, + "id": 9, "name": "대기", "action": "wait", "duration": 1000 }, { - "id": 7, + "id": 10, "name": "모달 닫기", "action": "close_modal_if_open" }, { - "id": 8, + "id": 11, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 12, "name": "직책관리 최종 확인", "action": "verify_detail", - "checks": ["visible_text:직책"] + "checks": [ + "visible_text:직책" + ] } ] } diff --git a/settings-rank.json b/settings-rank.json index f9156d7..184ff1c 100644 --- a/settings-rank.json +++ b/settings-rank.json @@ -3,7 +3,14 @@ "name": "직급관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "설정 > 직급관리 목록/검색/상세 기능 검증", "baseUrl": "https://dev.codebridge-x.com", @@ -25,47 +32,80 @@ "action": "menu_navigate", "level1": "설정", "level2": "직급관리", - "expected": { "url_contains": "/settings" } + "expected": { + "url_contains": "/settings" + } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/settings/ranks" + } + }, + { + "id": 3, "name": "목업 감지", "action": "verify_not_mockup" }, - { - "id": 3, - "name": "직급관리 페이지 확인", - "action": "verify_detail", - "checks": ["visible_text:직급"] - }, { "id": 4, - "name": "설정 페이지 확인", - "action": "verify_detail", - "checks": ["visible_text:설정"] + "name": "테이블 구조 검증", + "action": "verify_table" }, { "id": 5, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 6, + "name": "직급관리 페이지 확인", + "action": "verify_detail", + "checks": [ + "visible_text:직급" + ] + }, + { + "id": 7, + "name": "설정 페이지 확인", + "action": "verify_detail", + "checks": [ + "visible_text:설정" + ] + }, + { + "id": 8, "name": "추가 버튼 클릭", "action": "click_if_exists", "target": "button:has-text('추가'), button:has-text('등록'), button:has-text('신규')" }, { - "id": 6, + "id": 9, "name": "대기", "action": "wait", "duration": 1000 }, { - "id": 7, + "id": 10, "name": "모달 닫기", "action": "close_modal_if_open" }, { - "id": 8, + "id": 11, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 12, "name": "직급관리 최종 확인", "action": "verify_detail", - "checks": ["visible_text:직급"] + "checks": [ + "visible_text:직급" + ] } ] } diff --git a/settings-subscription.json b/settings-subscription.json index 9210e1b..d57d64d 100644 --- a/settings-subscription.json +++ b/settings-subscription.json @@ -3,7 +3,14 @@ "name": "구독관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "설정 > 구독관리 메뉴의 구독 정보 조회/플랜 비교 기능 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -37,11 +44,22 @@ "level2": "구독관리", "expected": { "url_contains": "/subscription", - "visible": ["구독관리", "구독"] + "visible": [ + "구독관리", + "구독" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/subscription" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -52,62 +70,82 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "현재 플랜 카드 존재 확인", "action": "verify_element", "target": "planCard", - "verification": { "exists": true } + "verification": { + "exists": true + } }, { - "id": 4, + "id": 6, "name": "플랜/가격 정보 텍스트 확인", "action": "evaluate", "script": "(() => { const text = document.body.innerText; return text.includes('플랜') || text.includes('요금') || text.includes('Plan') || text.includes('구독'); })()" }, { - "id": 5, + "id": 7, "name": "구독 기간/날짜 정보 확인", "action": "evaluate", "script": "(() => { const text = document.body.innerText; return text.includes('기간') || text.includes('시작') || text.includes('종료') || /\\d{4}[-./]\\d{2}[-./]\\d{2}/.test(text); })()" }, { - "id": 6, + "id": 8, "name": "결제 관련 정보 표시 확인", "action": "evaluate", "script": "(() => { const text = document.body.innerText; return text.includes('결제') || text.includes('카드') || text.includes('금액') || text.includes('원'); })()" }, { - "id": 7, + "id": 9, "name": "플랜 비교/변경 UI 확인", "action": "verify_element", "target": "table, [class*='plan'], [class*='compare'], button:has-text('플랜'), button:has-text('변경'), button:has-text('업그레이드')" }, { - "id": 8, + "id": 10, "name": "사용량 현황 영역 확인", "action": "evaluate", "script": "(() => { const text = document.body.innerText; return text.includes('사용') || text.includes('용량') || text.includes('사용자') || text.includes('%'); })()" }, { - "id": 9, + "id": 11, "name": "결제 내역 영역 확인", "action": "verify_element", "target": "table tbody tr, [class*='history'], [class*='payment-list'], [class*='billing-history']" }, { - "id": 10, + "id": 12, "name": "다운로드/영수증 버튼 확인", "action": "verify_element", "target": "button:has-text('다운로드'), button:has-text('영수증'), button:has-text('Download'), a:has-text('영수증'), [class*='download']" }, { - "id": 11, + "id": 13, "name": "결제 수단 관련 UI 확인", "action": "evaluate", "script": "(() => { const text = document.body.innerText; return text.includes('결제 수단') || text.includes('카드') || text.includes('계좌') || document.querySelector('[class*=\"payment-method\"], [class*=\"card-info\"]'); })()" }, { - "id": 12, + "id": 14, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 15, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 16, "name": "구독 관리 버튼 확인 (취소/해지 포함)", "action": "verify_element", "target": "button:has-text('취소'), button:has-text('해지'), button:has-text('관리'), button:has-text('변경'), [class*='cancel'], [class*='manage']" @@ -124,7 +162,9 @@ { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "구독 정보, 플랜 정보, 결제 정보 존재" } ], diff --git a/settings-vacation-policy.json b/settings-vacation-policy.json index ef33a43..d8873a6 100644 --- a/settings-vacation-policy.json +++ b/settings-vacation-policy.json @@ -3,7 +3,14 @@ "name": "휴가정책 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "설정 > 휴가정책 메뉴의 휴가 정책 조회/수정/저장 기능 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -40,11 +47,22 @@ "level2": "휴가정책", "expected": { "url_contains": "/settings/leave-policy", - "visible": ["휴가정책", "휴가"] + "visible": [ + "휴가정책", + "휴가" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/settings/leave-policy" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -55,7 +73,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "휴가 정책 폼 구조 확인", "action": "verify_elements", "checks": [ @@ -67,7 +91,7 @@ "expected": "휴가 정책 폼 정상 표시" }, { - "id": 4, + "id": 6, "phase": "READ", "name": "[READ] 현재 정책 값 확인", "action": "verify_detail", @@ -79,14 +103,14 @@ "expected": "현재 정책 값 정상 표시" }, { - "id": 5, + "id": 7, "phase": "UPDATE", "name": "[UPDATE] 연차 부여 기준 수정", "action": "click_if_exists", "target": "input[name*='annual'], input[placeholder*='연차']" }, { - "id": 6, + "id": 8, "phase": "UPDATE", "name": "[UPDATE] 반차 사용 설정", "action": "click_if_exists", @@ -96,14 +120,14 @@ } }, { - "id": 7, + "id": 9, "phase": "UPDATE", "name": "[UPDATE] 이월 일수 수정", "action": "click_if_exists", "target": "input[name*='carryOver'], input[placeholder*='이월']" }, { - "id": 8, + "id": 10, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 정책 저장", "action": "click_if_exists", @@ -117,7 +141,7 @@ "expected": "휴가 정책 저장 완료" }, { - "id": 9, + "id": 11, "phase": "UPDATE", "name": "[UPDATE] 저장 결과 확인", "action": "verify_detail", @@ -128,7 +152,7 @@ "expected": "수정된 정책 반영" }, { - "id": 10, + "id": 12, "name": "휴가 유형 관리 확인", "action": "verify_elements", "checks": [ @@ -139,7 +163,7 @@ "expected": "휴가 유형 목록 표시" }, { - "id": 11, + "id": 13, "phase": "CREATE", "name": "[CREATE] 휴가 유형 추가 버튼 확인", "action": "verify_elements", @@ -149,7 +173,19 @@ "expected": "추가 버튼 표시" }, { - "id": 12, + "id": 14, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 15, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 16, "name": "정책 적용 대상 확인", "action": "verify_elements", "checks": [ @@ -180,13 +216,17 @@ { "id": 2, "name": "저장 버튼", - "steps": [8], + "steps": [ + 8 + ], "criteria": "API 호출 + 성공 토스트 + 설정 반영" }, { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "휴가 정책 폼, 저장 버튼 존재" } ], diff --git a/settings-work-schedule.json b/settings-work-schedule.json index c0d36bc..828eaff 100644 --- a/settings-work-schedule.json +++ b/settings-work-schedule.json @@ -3,7 +3,14 @@ "name": "근무일정 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "설정 > 근무일정 메뉴의 근무일정 조회/수정/저장 기능 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -35,11 +42,22 @@ "level2": "근무일정", "expected": { "url_contains": "/settings/work-schedule", - "visible": ["근무일정", "근무"] + "visible": [ + "근무일정", + "근무" + ] } }, { "id": 2, + "name": "URL 검증", + "action": "verify_url", + "expected": { + "url_contains": "/settings/work-schedule" + } + }, + { + "id": 3, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -50,7 +68,13 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 3, + "id": 4, + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" + }, + { + "id": 5, "name": "근무일정 폼 구조 확인", "action": "verify_elements", "checks": [ @@ -62,7 +86,7 @@ "expected": "근무일정 폼 정상 표시" }, { - "id": 4, + "id": 6, "phase": "READ", "name": "[READ] 현재 근무일정 확인", "action": "verify_detail", @@ -74,28 +98,28 @@ "expected": "현재 근무일정 정상 표시" }, { - "id": 5, + "id": 7, "phase": "UPDATE", "name": "[UPDATE] 출근 시간 수정", "action": "click_if_exists", "target": "input[name*='start'], input[type='time']:first-of-type" }, { - "id": 6, + "id": 8, "phase": "UPDATE", "name": "[UPDATE] 퇴근 시간 수정", "action": "click_if_exists", "target": "input[name*='end'], input[type='time']:last-of-type" }, { - "id": 7, + "id": 9, "phase": "UPDATE", "name": "[UPDATE] 휴게 시간 설정", "action": "click_if_exists", "target": "input[name*='break'], input[placeholder*='휴게']" }, { - "id": 8, + "id": 10, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 근무일정 저장", "action": "click_if_exists", @@ -109,7 +133,7 @@ "expected": "근무일정 저장 완료" }, { - "id": 9, + "id": 11, "phase": "UPDATE", "name": "[UPDATE] 저장 결과 확인", "action": "verify_detail", @@ -121,7 +145,7 @@ "expected": "수정된 일정 반영" }, { - "id": 10, + "id": 12, "name": "휴무일 설정 확인", "action": "verify_elements", "checks": [ @@ -132,7 +156,7 @@ "expected": "휴무일 설정 표시" }, { - "id": 11, + "id": 13, "name": "주간 근무시간 계산 확인", "action": "verify_elements", "checks": [ @@ -141,7 +165,19 @@ "expected": "근무시간 자동 계산" }, { - "id": 12, + "id": 14, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 15, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 16, "name": "부서별 근무일정 확인", "action": "verify_elements", "checks": [ @@ -171,13 +207,17 @@ { "id": 2, "name": "저장 버튼", - "steps": [8], + "steps": [ + 8 + ], "criteria": "API 호출 + 성공 토스트 + 설정 반영" }, { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "근무일정 폼, 저장 버튼 존재" } ], diff --git a/shipment-management.json b/shipment-management.json index 6772a88..7f8274d 100644 --- a/shipment-management.json +++ b/shipment-management.json @@ -3,7 +3,14 @@ "name": "출고관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "출고관리 > 출고관리 목록/상세 기능 검증", "baseUrl": "https://dev.codebridge-x.com", @@ -25,7 +32,9 @@ "action": "menu_navigate", "level1": "출고관리", "level2": "출고관리", - "expected": { "url_contains": "/outbound" } + "expected": { + "url_contains": "/outbound" + } }, { "id": 2, @@ -34,50 +43,76 @@ }, { "id": 3, - "name": "출고관리 페이지 확인", - "action": "verify_detail", - "checks": ["visible_text:출고"] + "name": "통계 카드 확인", + "action": "evaluate", + "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()" }, { "id": 4, - "name": "UI 요소 확인", + "name": "출고관리 페이지 확인", "action": "verify_detail", - "checks": ["visible_text:관리"] + "checks": [ + "visible_text:출고" + ] }, { "id": 5, + "name": "UI 요소 확인", + "action": "verify_detail", + "checks": [ + "visible_text:관리" + ] + }, + { + "id": 6, "name": "검색 입력 시도", "action": "click_if_exists", "target": "input[type='search'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input" }, { - "id": 6, + "id": 7, "name": "대기", "action": "wait", "duration": 1000 }, { - "id": 7, + "id": 8, "name": "행 클릭 시도", "action": "click_if_exists", "target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']" }, { - "id": 8, + "id": 9, "name": "상세 확인", "action": "verify_detail", - "checks": ["visible_text:출고"] + "checks": [ + "visible_text:출고" + ] }, { - "id": 9, + "id": 10, "name": "모달 닫기", "action": "close_modal_if_open" }, { - "id": 10, + "id": 11, + "name": "페이지네이션 확인", + "action": "evaluate", + "script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()" + }, + { + "id": 12, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" + }, + { + "id": 13, "name": "최종 확인", "action": "verify_detail", - "checks": ["visible_text:출고"] + "checks": [ + "visible_text:출고" + ] } ] } diff --git a/vendor-ledger.json b/vendor-ledger.json index 137f70f..bae1714 100644 --- a/vendor-ledger.json +++ b/vendor-ledger.json @@ -66,7 +66,7 @@ }, "steps": [ { - "id": 0, + "id": 1, "name": "사이드바 메뉴 전체 펼치기", "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", "actions": [ @@ -90,13 +90,13 @@ "expected": "사이드바 전체 메뉴가 펼쳐짐" }, { - "id": 1, + "id": 2, "name": "로그인 상태 확인", "action": "verify_page", "expected": "이미 로그인된 상태" }, { - "id": 2, + "id": 3, "name": "2단계 메뉴 진입: 회계관리 > 거래처원장", "description": "회계관리 > 거래처원장 메뉴로 이동하여 페이지 로드 확인 (scrollAndFind 패턴 사용)", "actions": [ @@ -161,7 +161,7 @@ } }, { - "id": 3, + "id": 4, "name": "필수 검증 #5: 목업 페이지 감지", "action": "verify_not_mockup", "checks": [ @@ -173,7 +173,7 @@ "expected": "정상 페이지 (목업 아님)" }, { - "id": 4, + "id": 5, "name": "통계 카드 확인", "action": "verify_elements", "checks": [ @@ -185,7 +185,7 @@ "expected": "4개 통계 카드 모두 표시, 금액 형식 확인" }, { - "id": 5, + "id": 6, "name": "테이블 구조 확인", "action": "verify_table", "checks": [ @@ -201,7 +201,7 @@ "expected": "8개 컬럼 존재, 합계 행 표시" }, { - "id": 6, + "id": 7, "name": "기간 설정 - 시작일 변경", "action": "click_if_exists", "target": "startDate", @@ -209,7 +209,7 @@ "expected": "시작일 변경 후 데이터 재조회" }, { - "id": 7, + "id": 8, "name": "기간 설정 - 종료일 변경", "action": "click_if_exists", "target": "endDate", @@ -217,7 +217,7 @@ "expected": "종료일 변경 후 데이터 재조회" }, { - "id": 8, + "id": 9, "name": "기간 설정 - 데이터 변화 확인", "action": "verify_detail", "checks": [ @@ -228,7 +228,7 @@ "expected": "기간에 맞는 데이터로 변경됨" }, { - "id": 9, + "id": 10, "name": "⚠️ 필수 검증: 검색 기능 테스트", "actions": [ { @@ -264,7 +264,7 @@ "expected": "검색어에 맞는 거래처만 필터링" }, { - "id": "9-1", + "id": 11, "name": "검색 결과 데이터 검증", "description": "검색 결과의 모든 행이 검색어를 포함하는지 확인", "verify": { @@ -273,7 +273,7 @@ } }, { - "id": 10, + "id": 12, "name": "검색 결과 확인", "action": "verify_search_result", "checks": [ @@ -283,7 +283,7 @@ "expected": "검색 결과 정상 표시" }, { - "id": 11, + "id": 13, "name": "검색 초기화", "actions": [ { @@ -308,28 +308,28 @@ "expected": "전체 데이터 다시 표시" }, { - "id": 12, + "id": 14, "name": "체크박스 선택", "action": "click_if_exists", "target": "first_row", "expected": "첫 번째 행 체크박스 선택됨" }, { - "id": 13, + "id": 15, "name": "전체 선택 체크박스", "action": "click_if_exists", "target": "select_all", "expected": "모든 행 체크박스 선택됨" }, { - "id": 14, + "id": 16, "name": "전체 선택 해제", "action": "click_if_exists", "target": "select_all", "expected": "모든 행 체크박스 해제됨" }, { - "id": 15, + "id": 17, "name": "필수 검증 #1: 엑셀 다운로드", "action": "click_download", "target": "엑셀 다운로드", @@ -343,14 +343,14 @@ "expected": "엑셀 파일 다운로드 완료" }, { - "id": 16, + "id": 18, "name": "테이블 행 클릭 - 상세 페이지 이동", "action": "click_if_exists", "target": "table tbody tr:first-child", "expected": "거래처원장 상세 페이지로 이동" }, { - "id": 17, + "id": 19, "name": "상세 페이지 - URL 파라미터 확인", "action": "verify_url", "checks": [ @@ -361,7 +361,7 @@ "expected": "URL 파라미터 정상 전달" }, { - "id": 18, + "id": 20, "name": "상세 페이지 - 헤더 확인", "action": "verify_elements", "checks": [ @@ -371,7 +371,7 @@ "expected": "상세 페이지 헤더 정상 표시" }, { - "id": 19, + "id": 21, "name": "상세 페이지 - 거래처 정보 카드 확인", "action": "verify_vendor_info", "checks": [ @@ -388,7 +388,7 @@ "expected": "거래처 정보 모두 표시" }, { - "id": 20, + "id": 22, "name": "상세 페이지 - 요약 통계 확인", "action": "verify_summary", "checks": [ @@ -400,7 +400,7 @@ "expected": "4개 요약 통계 정상 표시" }, { - "id": 21, + "id": 23, "name": "상세 페이지 - 판매/수금 내역 테이블 확인", "action": "verify_transaction_table", "checks": [ @@ -414,7 +414,7 @@ "expected": "판매/수금 내역 테이블 정상 표시" }, { - "id": 22, + "id": 24, "name": "상세 페이지 - 기간 변경", "action": "click_if_exists", "startDate": "2025-06-01", @@ -423,7 +423,7 @@ "target": "input[type='date'], [class*='date-picker']" }, { - "id": 23, + "id": 25, "name": "상세 페이지 - 거래 내역 데이터 변화 확인", "action": "verify_transactions_update", "checks": [ @@ -433,7 +433,7 @@ "expected": "변경된 기간의 데이터 표시" }, { - "id": 24, + "id": 26, "name": "⚠️ 필수 검증: PDF 다운로드 전 페이지 스크린샷", "description": "PDF 생성 전 페이지 상태를 스크린샷으로 캡처하여 CSS 문제 감지용 기준 이미지 확보", "actions": [ @@ -458,7 +458,7 @@ } }, { - "id": "24-1", + "id": 27, "name": "⚠️ 필수 검증: PDF 다운로드 실행 및 파일 보관", "description": "PDF 다운로드 후 파일을 지정 폴더에 보관하여 수동 검증 가능하게 함", "actions": [ @@ -500,7 +500,7 @@ } }, { - "id": "24-2", + "id": 28, "name": "⚠️ PDF 파일 유효성 검증", "description": "다운로드된 PDF 파일의 기본 유효성 검사", "actions": [ @@ -520,7 +520,7 @@ } }, { - "id": "24-3", + "id": 29, "name": "📋 PDF 스타일 수동 확인 체크리스트", "type": "manualVerification", "description": "개발자가 다운로드된 PDF를 열어 시각적으로 확인해야 하는 항목", @@ -586,7 +586,7 @@ } }, { - "id": 25, + "id": 30, "name": "상세 페이지 - 작업 버튼 확인 (어음 항목)", "action": "verify_action_buttons", "checks": [ @@ -596,21 +596,21 @@ "expected": "hasAction=true인 항목에만 버튼 표시" }, { - "id": 26, + "id": 31, "name": "상세 페이지 - 목록 버튼 클릭", "action": "click_if_exists", "target": "목록", "expected": "거래처원장 목록 페이지로 복귀" }, { - "id": 27, + "id": 32, "name": "목록 페이지 복귀 확인", "action": "verify_url", "target": "/ko/accounting/vendor-ledger", "expected": "목록 페이지 정상 표시" }, { - "id": 28, + "id": 33, "name": "페이지네이션 동작 확인", "action": "verify_pagination", "checks": [ @@ -619,6 +619,12 @@ "페이지 이동 버튼 동작" ], "expected": "페이지네이션 정상 동작" + }, + { + "id": 34, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], "requiredVerifications": [ diff --git a/vendor-management.json b/vendor-management.json index 28b1192..76fc005 100644 --- a/vendor-management.json +++ b/vendor-management.json @@ -3,14 +3,25 @@ "name": "거래처관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "회계관리 > 거래처관리 메뉴의 목록 조회, 필터, 검색, 상세 페이지 진입, 수정 및 저장 기능 테스트", "baseUrl": "https://dev.codebridge-x.com", "navigation": { "targetUrl": "/accounting/vendors", "urlPattern": "/accounting/vendors", - "menuHints": ["거래처관리", "거래처 관리", "회계관리"] + "menuHints": [ + "거래처관리", + "거래처 관리", + "회계관리" + ] }, "menuNavigation": { "level1": "회계관리", @@ -24,8 +35,16 @@ "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", "level1": "회계관리", "level2": "거래처관리", - "alternativeLevel1Names": ["회계관리", "회계 관리", "Accounting"], - "alternativeLevel2Names": ["거래처관리", "거래처 관리", "Vendors"], + "alternativeLevel1Names": [ + "회계관리", + "회계 관리", + "Accounting" + ], + "alternativeLevel2Names": [ + "거래처관리", + "거래처 관리", + "Vendors" + ], "scrollConfig": { "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", @@ -39,8 +58,15 @@ "password": "password123!" }, "notes": { - "skip": ["등록 버튼 (추후 구현 예정)", "삭제 기능 (보류)"], - "focus": ["테이블 행 클릭 → 상세 페이지", "수정 모드 진입", "수정 후 저장"], + "skip": [ + "등록 버튼 (추후 구현 예정)", + "삭제 기능 (보류)" + ], + "focus": [ + "테이블 행 클릭 → 상세 페이지", + "수정 모드 진입", + "수정 후 저장" + ], "uiNotes": [ "필터 드롭다운: Radix UI Select (button[role='combobox'])", "체크박스: Radix UI Checkbox (button[role='checkbox'])", @@ -49,7 +75,7 @@ }, "steps": [ { - "id": "step-0", + "id": 1, "name": "사이드바 메뉴 전체 펼치기", "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", "actions": [ @@ -57,44 +83,78 @@ "type": "evaluate", "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" }, - { "type": "wait", "duration": 300 }, + { + "type": "wait", + "duration": 300 + }, { "type": "evaluate", "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" }, - { "type": "wait", "duration": 2000 } + { + "type": "wait", + "duration": 2000 + } ] }, { - "id": "step-1", + "id": 2, "name": "2단계 메뉴 진입: 회계관리 > 거래처관리", "description": "사이드바를 스크롤하며 회계관리 > 거래처관리 메뉴를 찾아 클릭", "actions": [ { "type": "scrollAndFind", "target": "회계관리", - "alternativeTexts": ["회계관리", "회계 관리", "Accounting"], + "alternativeTexts": [ + "회계관리", + "회계 관리", + "Accounting" + ], "scrollContainer": "sidebar", "maxAttempts": 10, "description": "스크롤하며 회계관리 메뉴 찾기" }, - { "type": "click_if_exists", "target": "회계관리", "description": "회계관리 메뉴 클릭" }, - { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, + { + "type": "click_if_exists", + "target": "회계관리", + "description": "회계관리 메뉴 클릭" + }, + { + "type": "wait", + "duration": 500, + "description": "서브메뉴 펼쳐지기 대기" + }, { "type": "scrollAndFind", "target": "거래처관리", - "alternativeTexts": ["거래처관리", "거래처 관리", "Vendors"], + "alternativeTexts": [ + "거래처관리", + "거래처 관리", + "Vendors" + ], "scrollContainer": "submenu", "maxAttempts": 5, "description": "서브메뉴에서 거래처관리 찾기" }, - { "type": "click_if_exists", "target": "거래처관리", "description": "거래처관리 메뉴 클릭" }, - { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 } + { + "type": "click_if_exists", + "target": "거래처관리", + "description": "거래처관리 메뉴 클릭" + }, + { + "type": "wait", + "target": "페이지 로드 완료", + "timeout": 10000 + } ], "expect": { "url": "/accounting/vendors", "pageTitle": "거래처관리", - "elements": ["통계 카드", "테이블", "검색창"] + "elements": [ + "통계 카드", + "테이블", + "검색창" + ] }, "verification": [ "회계관리 메뉴가 펼쳐졌는지 확인", @@ -153,8 +213,17 @@ "script": "(() => { const c = document.querySelectorAll('table tbody tr').length; window.__e2e_beforeSearch = c; return 'beforeSearch=' + c; })()", "description": "검색 전 행 수 저장" }, - { "type": "fill", "target": "input[placeholder*='검색']", "value": "가우스", "description": "검색어 '가우스' 입력" }, - { "type": "wait", "duration": 1000, "description": "검색 결과 대기" }, + { + "type": "fill", + "target": "input[placeholder*='검색']", + "value": "가우스", + "description": "검색어 '가우스' 입력" + }, + { + "type": "wait", + "duration": 1000, + "description": "검색 결과 대기" + }, { "type": "evaluate", "script": "(() => { const c = document.querySelectorAll('table tbody tr').length; return 'afterSearch=' + c + ', filtered=' + (c < (window.__e2e_beforeSearch||999)); })()", @@ -194,7 +263,11 @@ "script": "(() => { const inp = document.querySelector('input[placeholder*=\"검색\"]'); if(inp){ const nset = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value').set; nset.call(inp,''); inp.dispatchEvent(new Event('input',{bubbles:true})); inp.dispatchEvent(new Event('change',{bubbles:true})); return 'cleared'; } return 'not found'; })()", "description": "검색어 삭제" }, - { "type": "wait", "duration": 1000, "description": "목록 복원 대기" }, + { + "type": "wait", + "duration": 1000, + "description": "목록 복원 대기" + }, { "type": "evaluate", "script": "(() => { const c = document.querySelectorAll('table tbody tr').length; return 'restored rows=' + c + ', restored=' + (c >= (window.__e2e_beforeSearch||1)); })()", @@ -275,74 +348,74 @@ { "id": 14, "name": "상세 페이지 - 기본 정보 카드 확인", - "action": "verify_detail_info", + "action": "verify_detail", "checks": [ - "사업자등록번호 필드", - "거래처코드 필드", - "거래처명 필드", - "대표자명 필드", - "업태 필드", - "업종 필드" + "visible_text:사업자등록번호", + "visible_text:거래처코드", + "visible_text:거래처명", + "visible_text:대표자명", + "visible_text:업태", + "visible_text:업종" ], "expected": "기본 정보 모두 표시 (읽기 전용)" }, { "id": 15, "name": "상세 페이지 - 연락처 정보 확인", - "action": "verify_detail_info", + "action": "verify_detail", "checks": [ - "주소 필드", - "전화번호 필드", - "모바일 필드", - "팩스 필드", - "이메일 필드" + "visible_text:주소", + "visible_text:전화번호", + "visible_text:모바일", + "visible_text:팩스", + "visible_text:이메일" ], "expected": "연락처 정보 모두 표시" }, { "id": 16, "name": "상세 페이지 - 담당자 정보 확인", - "action": "verify_detail_info", + "action": "verify_detail", "checks": [ - "담당자명 필드", - "담당자 전화 필드", - "시스템 관리자 필드" + "visible_text:담당자명", + "visible_text:담당자 전화", + "visible_text:시스템 관리자" ], "expected": "담당자 정보 모두 표시" }, { "id": 17, "name": "상세 페이지 - 회사 정보 확인", - "action": "verify_detail_info", + "action": "verify_detail", "checks": [ - "회사 로고 영역", - "매입 결제일 필드", - "매출 결제일 필드" + "visible_text:회사 로고 영역", + "visible_text:매입 결제일", + "visible_text:매출 결제일" ], "expected": "회사 정보 모두 표시" }, { "id": 18, "name": "상세 페이지 - 신용/거래 정보 확인", - "action": "verify_detail_info", + "action": "verify_detail", "checks": [ - "신용등급 필드", - "거래등급 필드", - "세금계산서 이메일 필드", - "입금계좌 은행 필드", - "계좌 필드", - "예금주 필드" + "visible_text:신용등급", + "visible_text:거래등급", + "visible_text:세금계산서 이메일", + "visible_text:입금계좌 은행", + "visible_text:계좌", + "visible_text:예금주" ], "expected": "신용/거래 정보 모두 표시" }, { "id": 19, "name": "상세 페이지 - 추가 정보 확인", - "action": "verify_detail_info", + "action": "verify_detail", "checks": [ - "미수금 필드", - "악성채권 금액 필드", - "악성채권 상태 필드" + "visible_text:미수금", + "visible_text:악성채권 금액", + "visible_text:악성채권 상태" ], "expected": "추가 정보 모두 표시" }, @@ -410,8 +483,16 @@ "script": "(() => { window.__e2e_urlBeforeSave = window.location.href; return 'saved url: ' + window.__e2e_urlBeforeSave; })()", "description": "저장 전 URL 기록" }, - { "type": "click_if_exists", "target": "저장", "description": "저장 버튼 클릭" }, - { "type": "wait", "duration": 2000, "description": "저장 처리 대기" } + { + "type": "click_if_exists", + "target": "저장", + "description": "저장 버튼 클릭" + }, + { + "type": "wait", + "duration": 2000, + "description": "저장 처리 대기" + } ], "expected": "저장 완료 후 목록 페이지로 리다이렉트" }, @@ -524,7 +605,10 @@ { "id": 1, "name": "등록/저장 버튼", - "steps": [25, 26], + "steps": [ + 25, + 26 + ], "criteria": "저장 클릭 → 목록 리다이렉트 + 에러 없음 + 데이터 반영" }, { @@ -536,7 +620,13 @@ { "id": 3, "name": "검색/필터", - "steps": [6, 7, 8, 9, 10], + "steps": [ + 6, + 7, + 8, + 9, + 10 + ], "criteria": "검색 및 필터 시 데이터 변화 확인" }, { @@ -548,7 +638,9 @@ { "id": 5, "name": "목업 페이지 감지", - "steps": [3], + "steps": [ + 3 + ], "criteria": "입력 필드, 동작 버튼, API 호출 확인" } ], diff --git a/withdrawal-management.json b/withdrawal-management.json index 499bd59..e2e9365 100644 --- a/withdrawal-management.json +++ b/withdrawal-management.json @@ -4,7 +4,14 @@ "name": "출금관리 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "출금관리 목록 조회, 계정과목명 일괄변경, 상세 수정 기능 E2E 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -12,7 +19,11 @@ "navigation": { "targetUrl": "/accounting/withdrawals", "urlPattern": "/accounting/withdrawals|/ko/accounting/withdrawals", - "menuHints": ["출금관리", "출금 관리", "회계관리"] + "menuHints": [ + "출금관리", + "출금 관리", + "회계관리" + ] }, "menuNavigation": { "level1": "회계관리", @@ -46,16 +57,18 @@ } }, "timeout": 60000, - "tags": ["accounting", "withdrawal", "crud"], - + "tags": [ + "accounting", + "withdrawal", + "crud" + ], "auth": { "username": "TestUser5", "password": "password123!" }, - "steps": [ { - "id": "step-0", + "id": 1, "name": "사이드바 메뉴 전체 펼치기", "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", "actions": [ @@ -63,19 +76,25 @@ "type": "evaluate", "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" }, - { "type": "wait", "duration": 300 }, + { + "type": "wait", + "duration": 300 + }, { "type": "evaluate", "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" }, - { "type": "wait", "duration": 2000 } + { + "type": "wait", + "duration": 2000 + } ], "expect": { "sidebarReady": true } }, { - "id": "step-1", + "id": 2, "name": "출금관리 메뉴 진입", "description": "회계관리 > 출금관리 메뉴로 이동 (scrollAndFind 패턴 사용)", "menuNavigation": { @@ -100,43 +119,98 @@ }, "expect": { "url": "/accounting/withdrawals", - "visible": ["출금관리", "총 출금"] + "visible": [ + "출금관리", + "총 출금" + ] } }, { - "id": "step-2", + "id": 3, "name": "목록 페이지 구조 확인", "description": "테이블 및 필터 요소 확인", "expect": { - "visible": ["출금일", "출금계좌", "받는분", "출금금액", "거래처", "적요", "출금유형"], + "visible": [ + "출금일", + "출금계좌", + "받는분", + "출금금액", + "거래처", + "적요", + "출금유형" + ], "elements": { - "statisticsCards": ["총 출금", "당월 출금", "거래처 미설정", "출금유형 미설정"], - "filters": ["계정과목명", "저장", "새로고침"], + "statisticsCards": [ + "총 출금", + "당월 출금", + "거래처 미설정", + "출금유형 미설정" + ], + "filters": [ + "계정과목명", + "저장", + "새로고침" + ], "pagination": true } } }, { - "id": "step-3", + "id": 4, "name": "계정과목명 드롭다운 옵션 확인", "description": "계정과목명 일괄변경 드롭다운 옵션 검증", "actions": [ - { "type": "click_if_exists", "target": "계정과목명 드롭다운", "description": "드롭다운 열기" } + { + "type": "click_if_exists", + "target": "계정과목명 드롭다운", + "description": "드롭다운 열기" + } ], "expect": { - "options": ["미설정", "매입대금", "급여", "임차료", "수도광열비", "통신비", "소모품비", "운반비", "차량유지비", "보험료", "세금과공과", "이자비용", "수수료", "기타"] + "options": [ + "미설정", + "매입대금", + "급여", + "임차료", + "수도광열비", + "통신비", + "소모품비", + "운반비", + "차량유지비", + "보험료", + "세금과공과", + "이자비용", + "수수료", + "기타" + ] }, "note": "출금유형 옵션은 비용 계정 기준이므로 입금관리와 다름" }, { - "id": "step-4", + "id": 5, "name": "체크박스 선택 후 계정과목명 일괄변경", "description": "테이블 행 선택 후 계정과목명 일괄변경 저장", "actions": [ - { "type": "click_if_exists", "target": "첫 번째 행 체크박스", "description": "행 선택" }, - { "type": "click_if_exists", "target": "계정과목명 드롭다운", "description": "드롭다운 열기" }, - { "type": "click_if_exists", "target": "매입대금", "description": "매입대금 선택" }, - { "type": "click_if_exists", "target": "저장", "description": "저장 버튼 클릭" } + { + "type": "click_if_exists", + "target": "첫 번째 행 체크박스", + "description": "행 선택" + }, + { + "type": "click_if_exists", + "target": "계정과목명 드롭다운", + "description": "드롭다운 열기" + }, + { + "type": "click_if_exists", + "target": "매입대금", + "description": "매입대금 선택" + }, + { + "type": "click_if_exists", + "target": "저장", + "description": "저장 버튼 클릭" + } ], "expect": { "dialog": "확인 다이얼로그 표시", @@ -150,7 +224,7 @@ ] }, { - "id": "step-4-1", + "id": 6, "name": "⚠️ 필수 검증: 계정과목명 변경 데이터 반영 확인", "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", "description": "저장 후 테이블에서 변경된 출금유형 값 확인", @@ -170,101 +244,219 @@ "knownBugReference": "BUG-DEPOSIT-20260115-001 (입금관리 동일 버그 확인 필요)" }, { - "id": "step-5", + "id": 7, "name": "출금 상세 페이지 이동", "description": "테이블 행 클릭하여 상세 페이지로 이동", "actions": [ - { "type": "click_if_exists", "target": "테이블 첫 번째 행", "description": "행 클릭 (체크박스 제외 영역)" } + { + "type": "click_if_exists", + "target": "테이블 첫 번째 행", + "description": "행 클릭 (체크박스 제외 영역)" + } ], "expect": { "url": "/accounting/withdrawals/{id}", - "visible": ["출금 상세", "기본 정보", "목록", "삭제", "수정"] - } - }, - { - "id": "step-6", - "name": "상세 페이지 읽기 모드 필드 확인", - "description": "수정 전 필드들이 비활성화 상태인지 확인", - "expect": { - "fields": [ - { "name": "출금일", "disabled": true }, - { "name": "출금계좌", "disabled": true }, - { "name": "받는분", "disabled": true }, - { "name": "출금금액", "disabled": true }, - { "name": "적요", "disabled": true }, - { "name": "거래처", "disabled": true }, - { "name": "출금 유형", "disabled": true } + "visible": [ + "출금 상세", + "기본 정보", + "목록", + "삭제", + "수정" ] } }, { - "id": "step-7", + "id": 8, + "name": "상세 페이지 읽기 모드 필드 확인", + "description": "수정 전 필드들이 비활성화 상태인지 확인", + "expect": { + "fields": [ + { + "name": "출금일", + "disabled": true + }, + { + "name": "출금계좌", + "disabled": true + }, + { + "name": "받는분", + "disabled": true + }, + { + "name": "출금금액", + "disabled": true + }, + { + "name": "적요", + "disabled": true + }, + { + "name": "거래처", + "disabled": true + }, + { + "name": "출금 유형", + "disabled": true + } + ] + } + }, + { + "id": 9, "name": "수정 모드 전환", "description": "수정 버튼 클릭하여 편집 모드로 전환", "click": "수정", "expect": { "url": "/accounting/withdrawals/{id}?mode=edit", - "visible": ["출금 수정", "취소", "저장"], - "notVisible": ["목록", "삭제", "수정"] + "visible": [ + "출금 수정", + "취소", + "저장" + ], + "notVisible": [ + "목록", + "삭제", + "수정" + ] } }, { - "id": "step-8", + "id": 10, "name": "수정 모드 필드 활성화 검증", "description": "수정 가능한 필드와 불가능한 필드 확인", "expect": { "fields": [ - { "name": "출금일", "disabled": true, "note": "은행데이터 - 수정 불가" }, - { "name": "출금계좌", "disabled": true, "note": "은행데이터 - 수정 불가" }, - { "name": "받는분", "disabled": true, "note": "은행데이터 - 수정 불가" }, - { "name": "출금금액", "disabled": true, "note": "은행데이터 - 수정 불가" }, - { "name": "적요", "disabled": false, "editable": true }, - { "name": "거래처", "disabled": false, "type": "combobox", "editable": true }, - { "name": "출금 유형", "disabled": false, "type": "combobox", "editable": true } + { + "name": "출금일", + "disabled": true, + "note": "은행데이터 - 수정 불가" + }, + { + "name": "출금계좌", + "disabled": true, + "note": "은행데이터 - 수정 불가" + }, + { + "name": "받는분", + "disabled": true, + "note": "은행데이터 - 수정 불가" + }, + { + "name": "출금금액", + "disabled": true, + "note": "은행데이터 - 수정 불가" + }, + { + "name": "적요", + "disabled": false, + "editable": true + }, + { + "name": "거래처", + "disabled": false, + "type": "combobox", + "editable": true + }, + { + "name": "출금 유형", + "disabled": false, + "type": "combobox", + "editable": true + } ] } }, { - "id": "step-9", + "id": 11, "name": "거래처 드롭다운 옵션 확인", "description": "거래처 선택 드롭다운 옵션 검증", "actions": [ - { "type": "click_if_exists", "target": "거래처 드롭다운", "description": "드롭다운 열기" } + { + "type": "click_if_exists", + "target": "거래처 드롭다운", + "description": "드롭다운 열기" + } ], "expect": { - "options": ["거래처테스트", "아크더레드", "코브라브릿지", "가우스전자", "아크아크"], + "options": [ + "거래처테스트", + "아크더레드", + "코브라브릿지", + "가우스전자", + "아크아크" + ], "note": "거래처 옵션은 시스템 공통 데이터" } }, { - "id": "step-10", + "id": 12, "name": "출금 유형 드롭다운 옵션 확인", "description": "출금 유형 선택 드롭다운 옵션 검증", "actions": [ - { "type": "click_if_exists", "target": "출금 유형 드롭다운", "description": "드롭다운 열기" } + { + "type": "click_if_exists", + "target": "출금 유형 드롭다운", + "description": "드롭다운 열기" + } ], "expect": { - "options": ["미설정", "매입대금", "급여", "임차료", "수도광열비", "통신비", "소모품비", "운반비", "차량유지비", "보험료", "세금과공과", "이자비용", "수수료", "기타"] + "options": [ + "미설정", + "매입대금", + "급여", + "임차료", + "수도광열비", + "통신비", + "소모품비", + "운반비", + "차량유지비", + "보험료", + "세금과공과", + "이자비용", + "수수료", + "기타" + ] } }, { - "id": "step-11", + "id": 13, "name": "수정 데이터 입력", "description": "수정 가능한 필드에 테스트 데이터 입력", "form": { "fields": [ - { "name": "적요", "type": "text", "value": "테스트 적요 수정" } + { + "name": "적요", + "type": "text", + "value": "테스트 적요 수정" + } ] }, "actions": [ - { "type": "click_if_exists", "target": "거래처 드롭다운", "description": "거래처 드롭다운 열기" }, - { "type": "click_if_exists", "target": "거래처테스트", "description": "거래처 선택" }, - { "type": "click_if_exists", "target": "출금 유형 드롭다운", "description": "출금 유형 드롭다운 열기" }, - { "type": "click_if_exists", "target": "매입대금", "description": "매입대금 선택" } + { + "type": "click_if_exists", + "target": "거래처 드롭다운", + "description": "거래처 드롭다운 열기" + }, + { + "type": "click_if_exists", + "target": "거래처테스트", + "description": "거래처 선택" + }, + { + "type": "click_if_exists", + "target": "출금 유형 드롭다운", + "description": "출금 유형 드롭다운 열기" + }, + { + "type": "click_if_exists", + "target": "매입대금", + "description": "매입대금 선택" + } ] }, { - "id": "step-12", + "id": 14, "name": "저장 및 결과 확인", "description": "저장 버튼 클릭 후 데이터 반영 확인", "click": "저장", @@ -280,61 +472,105 @@ ] }, { - "id": "step-12-1", + "id": 15, "name": "⚠️ 필수 검증: 수정 데이터 반영 확인", "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", "description": "저장 후 상세 페이지에서 변경된 값 확인", "expect": { "fields": [ - { "name": "적요", "value": "테스트 적요 수정" }, - { "name": "거래처", "value": "거래처테스트" }, - { "name": "출금 유형", "value": "매입대금" } + { + "name": "적요", + "value": "테스트 적요 수정" + }, + { + "name": "거래처", + "value": "거래처테스트" + }, + { + "name": "출금 유형", + "value": "매입대금" + } ] } }, { - "id": "step-13", + "id": 16, "name": "취소 버튼 동작 확인", "description": "수정 모드에서 취소 버튼 동작 검증", "actions": [ - { "type": "click_if_exists", "target": "수정", "description": "수정 모드 진입" }, - { "type": "click_if_exists", "target": "취소", "description": "취소 버튼 클릭" } + { + "type": "click_if_exists", + "target": "수정", + "description": "수정 모드 진입" + }, + { + "type": "click_if_exists", + "target": "취소", + "description": "취소 버튼 클릭" + } ], "expect": { "url": "/accounting/withdrawals/{id}", "mode": "view", - "visible": ["출금 상세", "목록", "삭제", "수정"] + "visible": [ + "출금 상세", + "목록", + "삭제", + "수정" + ] } }, { - "id": "step-14", + "id": 17, "name": "목록 버튼 동작 확인", "description": "목록 버튼 클릭하여 목록 페이지로 이동", "click": "목록", "expect": { "url": "/accounting/withdrawals", - "visible": ["출금관리", "총 출금"] + "visible": [ + "출금관리", + "총 출금" + ] } }, { - "id": "step-15", + "id": 18, "name": "필터 드롭다운 검증", "description": "목록 페이지 필터 드롭다운 옵션 확인", "note": "3개의 필터 드롭다운 존재 (거래처, 출금유형, 정렬)", "expect": { "filters": [ - { "name": "거래처 필터", "default": "전체" }, - { "name": "출금유형 필터", "default": "전체" }, - { "name": "정렬", "default": "최신순", "options": ["최신순", "등록순", "금액 높은순", "금액 낮은순"] } + { + "name": "거래처 필터", + "default": "전체" + }, + { + "name": "출금유형 필터", + "default": "전체" + }, + { + "name": "정렬", + "default": "최신순", + "options": [ + "최신순", + "등록순", + "금액 높은순", + "금액 낮은순" + ] + } ] } }, { - "id": "step-16", + "id": 19, "name": "날짜 필터 검증", "description": "날짜 필터 버튼 동작 확인", "actions": [ - { "type": "click_if_exists", "target": "당해년도", "description": "당해년도 버튼 클릭" } + { + "type": "click_if_exists", + "target": "당해년도", + "description": "당해년도 버튼 클릭" + } ], "expect": { "dateRange": { @@ -344,7 +580,7 @@ } }, { - "id": "step-17", + "id": 20, "name": "페이지네이션 동작 확인", "description": "페이지네이션 버튼 동작 검증 (데이터 존재 시)", "condition": "데이터가 20건 이상인 경우에만 실행", @@ -355,14 +591,23 @@ } }, "actions": [ - { "type": "click_if_exists", "target": "다음", "description": "다음 페이지로 이동" } + { + "type": "click_if_exists", + "target": "다음", + "description": "다음 페이지로 이동" + } ], "expectAfterAction": { "currentPage": 2 } + }, + { + "id": 21, + "name": "콘솔 에러 확인", + "action": "verify_element", + "target": "body" } ], - "skipTests": [ { "id": "delete-button", @@ -370,54 +615,69 @@ "reason": "사용자 요청에 따라 테스트 제외" } ], - "knownBugs": [ { "id": "BUG-DEPOSIT-20260115-001", "description": "계정과목명 일괄변경 시 API 오류 발생 (입금관리)", "errorMessage": "존재하지 않는 URI 또는 데이터", - "relatedSteps": ["step-4-1"], + "relatedSteps": [ + "step-4-1" + ], "note": "출금관리에서도 동일한 버그가 존재할 수 있으므로 step-4-1에서 검증 필수" }, { "id": "BUG-SALES-20260115-001", "description": "계정과목명 일괄변경 시 토스트 성공 표시되나 실제 데이터 미변경 (매출관리)", - "relatedSteps": ["step-4-1"], + "relatedSteps": [ + "step-4-1" + ], "note": "유사한 UI 패턴이므로 동일 버그 가능성 있음" } ], - "criticalValidationChecklist": [ { "id": 1, "name": "파일 다운로드 검증", "trigger": "다운로드, Export 버튼 발견 시", - "checks": ["Network API 호출", "실제 파일 다운로드"], + "checks": [ + "Network API 호출", + "실제 파일 다운로드" + ], "note": "Console LOG만으로 PASS 판정 금지" }, { "id": 2, "name": "등록/저장 버튼 검증", "trigger": "등록, 저장, 제출 버튼 클릭 시", - "checks": ["URL 유지 확인", "에러 페이지 없음", "성공 토스트"], + "checks": [ + "URL 유지 확인", + "에러 페이지 없음", + "성공 토스트" + ], "note": "에러 페이지 이동 감지 필수" }, { "id": 3, "name": "일괄변경 데이터 반영 검증", "trigger": "계정과목명 저장 후", - "checks": ["테이블 데이터 변경 확인", "새로고침 후 유지 확인"], + "checks": [ + "테이블 데이터 변경 확인", + "새로고침 후 유지 확인" + ], "note": "토스트만 확인하면 불충분" }, { "id": 4, "name": "목업 페이지 감지", "trigger": "페이지 로드 시", - "checks": ["입력 필드 존재", "동작하는 버튼 존재", "API 호출 여부"], + "checks": [ + "입력 필드 존재", + "동작하는 버튼 존재", + "API 호출 여부" + ], "note": "버튼만 있고 동작 안하면 목업" } ], - "testData": { "sampleWithdrawal": { "date": "2025-12-28", @@ -434,56 +694,136 @@ "withdrawalType": "매입대금" } }, - "pageStructure": { "listPage": { "url": "/accounting/withdrawals", "title": "출금관리", - "statistics": ["총 출금", "당월 출금", "거래처 미설정", "출금유형 미설정"], - "tableColumns": ["checkbox", "출금일", "출금계좌", "받는분", "출금금액", "거래처", "적요", "출금유형", "action"], + "statistics": [ + "총 출금", + "당월 출금", + "거래처 미설정", + "출금유형 미설정" + ], + "tableColumns": [ + "checkbox", + "출금일", + "출금계좌", + "받는분", + "출금금액", + "거래처", + "적요", + "출금유형", + "action" + ], "batchUpdate": { "label": "계정과목명", "saveButton": "저장" }, - "filters": ["거래처", "출금유형", "정렬"], - "dateFilters": ["당해년도", "전전월", "전월", "당월", "어제", "오늘"] + "filters": [ + "거래처", + "출금유형", + "정렬" + ], + "dateFilters": [ + "당해년도", + "전전월", + "전월", + "당월", + "어제", + "오늘" + ] }, "detailPage": { "url": "/accounting/withdrawals/{id}", "title": "출금 상세", - "buttons": ["목록", "삭제", "수정"], + "buttons": [ + "목록", + "삭제", + "수정" + ], "fields": { - "readOnly": ["출금일", "출금계좌", "받는분", "출금금액"], - "editable": ["적요", "거래처", "출금 유형"] + "readOnly": [ + "출금일", + "출금계좌", + "받는분", + "출금금액" + ], + "editable": [ + "적요", + "거래처", + "출금 유형" + ] } }, "editPage": { "url": "/accounting/withdrawals/{id}?mode=edit", "title": "출금 수정", - "buttons": ["취소", "저장"] + "buttons": [ + "취소", + "저장" + ] } }, - "dropdownOptions": { "accountSubject": { "label": "계정과목명", - "options": ["미설정", "매입대금", "급여", "임차료", "수도광열비", "통신비", "소모품비", "운반비", "차량유지비", "보험료", "세금과공과", "이자비용", "수수료", "기타"], + "options": [ + "미설정", + "매입대금", + "급여", + "임차료", + "수도광열비", + "통신비", + "소모품비", + "운반비", + "차량유지비", + "보험료", + "세금과공과", + "이자비용", + "수수료", + "기타" + ], "note": "출금유형은 비용 계정 기준 (입금유형과 다름)" }, "withdrawalType": { "label": "출금 유형", - "options": ["미설정", "매입대금", "급여", "임차료", "수도광열비", "통신비", "소모품비", "운반비", "차량유지비", "보험료", "세금과공과", "이자비용", "수수료", "기타"] + "options": [ + "미설정", + "매입대금", + "급여", + "임차료", + "수도광열비", + "통신비", + "소모품비", + "운반비", + "차량유지비", + "보험료", + "세금과공과", + "이자비용", + "수수료", + "기타" + ] }, "vendor": { "label": "거래처", - "options": ["거래처테스트", "아크더레드", "코브라브릿지", "가우스전자", "아크아크"] + "options": [ + "거래처테스트", + "아크더레드", + "코브라브릿지", + "가우스전자", + "아크아크" + ] }, "sortOrder": { "label": "정렬", - "options": ["최신순", "등록순", "금액 높은순", "금액 낮은순"] + "options": [ + "최신순", + "등록순", + "금액 높은순", + "금액 낮은순" + ] } }, - "comparisonWithDeposit": { "differences": [ { @@ -511,7 +851,6 @@ "은행 데이터 필드 수정 불가 (날짜, 계좌, 금액 등)" ] }, - "assertions": [ { "type": "url",