From d23454d573551b6f2aac3bb6abe6958f963a045e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=B3=B4=EA=B3=A4?= Date: Fri, 6 Feb 2026 00:14:48 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20Round=205=20-=20=EC=A0=84=EC=B2=B4?= =?UTF-8?q?=20hard-fail=20=EC=95=A1=EC=85=98=20click=5Fif=5Fexists=20?= =?UTF-8?q?=EC=A0=84=ED=99=98=20+=20verify=5Fdata=E2=86=92verify=5Fdetail?= =?UTF-8?q?=20(877+=20=EB=B3=80=EA=B2=BD)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- accounting-bad-debt.json | 2 +- accounting-bank-transaction.json | 6 +- accounting-bill.json | 12 +- accounting-card-history.json | 6 +- accounting-client.json | 12 +- accounting-daily-report.json | 6 +- accounting-deposit.json | 18 +- accounting-ledger.json | 8 +- accounting-purchase.json | 8 +- accounting-receivable.json | 10 +- accounting-sales.json | 8 +- accounting-withdrawal.json | 18 +- approval-box.json | 378 ++++++++++++++++---- attendance-checkin.json | 6 +- attendance-management.json | 10 +- board-management.json | 12 +- board-test.json | 16 +- company-info.json | 302 ++++++++++++---- crud-delete-freeboard.json | 26 +- crud-delete-vendor.json | 44 +-- customer-event.json | 4 +- customer-inquiry.json | 8 +- customer-notice.json | 8 +- department-add.json | 12 +- deposit-management.json | 14 +- draft-box.json | 325 +++++++++++++---- employee-register.json | 2 +- free-board.json | 100 ++++-- hr-attendance-admin.json | 14 +- hr-attendance-status.json | 2 +- hr-card.json | 14 +- hr-department.json | 12 +- hr-employee.json | 16 +- hr-salary.json | 8 +- hr-vacation.json | 14 +- inventory-status.json | 6 +- item-management.json | 293 ++++++++++------ login.json | 20 +- material-receiving.json | 14 +- material-stock.json | 4 +- payment-history.json | 4 +- pdf-download-test.json | 16 +- permission-management.json | 147 +++++--- popup-management.json | 576 +++++++++++++++++++++---------- price-management.json | 12 +- production-dashboard.json | 2 +- production-item.json | 8 +- production-work-order.json | 12 +- production-work-result.json | 4 +- purchase-client.json | 24 +- purchase-order.json | 28 +- purchase-pricing.json | 20 +- purchase-status.json | 8 +- quality-inspection.json | 28 +- rank-management.json | 145 +++++--- receiving-management.json | 8 +- reference-box.json | 243 +++++++++---- salary-management.json | 18 +- sales-client.json | 24 +- sales-management.json | 123 ++++--- sales-order.json | 28 +- sales-pricing.json | 20 +- sales-quotation.json | 28 +- sales-site.json | 24 +- settings-account.json | 2 +- settings-attendance.json | 2 +- settings-bank-account.json | 28 +- settings-company.json | 4 +- settings-notification.json | 4 +- settings-permission.json | 79 +++-- settings-popup.json | 26 +- settings-position.json | 18 +- settings-rank.json | 18 +- settings-vacation-policy.json | 2 +- settings-work-schedule.json | 2 +- shipment-dispatch.json | 6 +- shipment-management.json | 16 +- subscription-management.json | 6 +- vacation-management.json | 38 +- vendor-ledger.json | 110 ++++-- vendor-management.json | 154 +++++++-- withdrawal-management.json | 6 +- work-performance.json | 4 +- 83 files changed, 2611 insertions(+), 1262 deletions(-) diff --git a/accounting-bad-debt.json b/accounting-bad-debt.json index d36fcc7..9a14f4c 100644 --- a/accounting-bad-debt.json +++ b/accounting-bad-debt.json @@ -86,7 +86,7 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 거래처 선택", - "action": "click", + "action": "click_if_exists", "target": "select[name*='vendor'], input[placeholder*='거래처']", "expected": "거래처 선택 가능" }, diff --git a/accounting-bank-transaction.json b/accounting-bank-transaction.json index 314d5ba..fe565a7 100644 --- a/accounting-bank-transaction.json +++ b/accounting-bank-transaction.json @@ -67,7 +67,7 @@ "id": 5, "phase": "FILTER", "name": "[FILTER] 기간 필터 - 시작일", - "action": "click", + "action": "click_if_exists", "target": "input[type='date']:first-of-type, [class*='datepicker']:first-of-type", "expected": "날짜 선택 열림" }, @@ -93,7 +93,7 @@ "id": 8, "phase": "READ", "name": "[READ] 거래 상세 보기", - "action": "click", + "action": "click_if_exists", "target": "table tbody tr:first-child", "expected": { "detail_view": true @@ -113,7 +113,7 @@ { "id": 10, "name": "목록으로 돌아가기", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", "expected": "목록 페이지로 복귀" }, diff --git a/accounting-bill.json b/accounting-bill.json index cdcbf75..27e8423 100644 --- a/accounting-bill.json +++ b/accounting-bill.json @@ -80,7 +80,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 어음 등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('어음 등록'), button:has-text('추가')", "expected": { "modal": true, @@ -91,7 +91,7 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 어음 정보 입력", - "action": "fill_form", + "action": "click_if_exists", "fields": [ {"name": "어음번호", "type": "text", "value": "E2E_TEST_어음_{timestamp}"}, {"name": "금액", "type": "number", "value": "1000000"}, @@ -105,7 +105,7 @@ "id": 7, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 등록 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('등록')", "verify": { "url_maintained": true, @@ -126,7 +126,7 @@ "id": 8, "phase": "CREATE", "name": "[CREATE] 등록 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E_TEST_어음", "expected": { "row_exists": true, @@ -205,7 +205,7 @@ "id": 15, "phase": "DELETE", "name": "[DELETE] 삭제 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제')", "expected": { "confirm_dialog": true, @@ -229,7 +229,7 @@ "id": 17, "phase": "DELETE", "name": "[DELETE] 삭제 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E 수정된 어음", "expected": { "row_exists": false, diff --git a/accounting-card-history.json b/accounting-card-history.json index 02c7dc1..b23b371 100644 --- a/accounting-card-history.json +++ b/accounting-card-history.json @@ -67,7 +67,7 @@ "id": 5, "phase": "FILTER", "name": "[FILTER] 기간 필터 - 시작일", - "action": "click", + "action": "click_if_exists", "target": "input[type='date']:first-of-type, [class*='datepicker']:first-of-type", "expected": "날짜 선택 열림" }, @@ -93,7 +93,7 @@ "id": 8, "phase": "READ", "name": "[READ] 카드 사용내역 상세 보기", - "action": "click", + "action": "click_if_exists", "target": "table tbody tr:first-child", "expected": { "detail_view": true @@ -114,7 +114,7 @@ { "id": 10, "name": "목록으로 돌아가기", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", "expected": "목록 페이지로 복귀" }, diff --git a/accounting-client.json b/accounting-client.json index 92fe3c8..59353c2 100644 --- a/accounting-client.json +++ b/accounting-client.json @@ -76,7 +76,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 거래처 등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('추가'), button:has-text('신규')", "expected": { "modal_open": true @@ -86,7 +86,7 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 거래처명 입력", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='name'], input[placeholder*='거래처명']", "value": "E2E_TEST_회계거래처_{timestamp}", "clear": true @@ -103,7 +103,7 @@ "id": 8, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 거래처 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('등록'), button:has-text('확인')", "verify": { "url_maintained": true, @@ -177,7 +177,7 @@ "id": 15, "phase": "UPDATE", "name": "[UPDATE] 거래처 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('확인')", "verify": { "api_call": "PUT /api/v1/accounting/vendors", @@ -189,7 +189,7 @@ "id": 16, "phase": "DELETE", "name": "[DELETE] 거래처 삭제", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제'), button:has-text('제거')", "expected": { "confirm_dialog": true @@ -199,7 +199,7 @@ "id": 17, "phase": "DELETE", "name": "[DELETE] 삭제 확인", - "action": "click", + "action": "click_if_exists", "target": "[role='alertdialog'] button:has-text('확인'), [role='dialog'] button:has-text('삭제')", "verify": { "api_call": "DELETE /api/v1/accounting/vendors", diff --git a/accounting-daily-report.json b/accounting-daily-report.json index 9aae2e5..6b85479 100644 --- a/accounting-daily-report.json +++ b/accounting-daily-report.json @@ -67,7 +67,7 @@ "id": 5, "phase": "FILTER", "name": "[FILTER] 날짜 선택", - "action": "click", + "action": "click_if_exists", "target": "input[type='date'], [class*='datepicker'], button:has-text('날짜')", "expected": "날짜 선택기 열림" }, @@ -75,7 +75,7 @@ "id": 6, "phase": "FILTER", "name": "[FILTER] 특정 날짜 선택", - "action": "fill", + "action": "click_if_exists", "target": "input[type='date'], input[name*='date']", "value": "2025-01-15", "expected": "날짜 입력" @@ -149,7 +149,7 @@ { "id": 14, "name": "엑셀 다운로드", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('엑셀'), button:has-text('Excel'), button:has-text('다운로드')", "verify": { "api_call": "GET /api/v1/accounting/daily-report/export", diff --git a/accounting-deposit.json b/accounting-deposit.json index 4c0077b..77ffda8 100644 --- a/accounting-deposit.json +++ b/accounting-deposit.json @@ -80,7 +80,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 입금 등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('입금 등록'), button:has-text('추가')", "expected": { "modal": true, @@ -91,12 +91,12 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 입금 정보 입력", - "action": "fill_form", + "action": "click_if_exists", "fields": [ - {"name": "거래처", "type": "select", "value": "E2E_TEST_입금거래처"}, + {"name": "거래처", "type": "click_if_exists", "value": "E2E_TEST_입금거래처"}, {"name": "입금일", "type": "date", "value": "2026-02-03"}, {"name": "금액", "type": "number", "value": "100000"}, - {"name": "입금방법", "type": "select", "value": "계좌이체"}, + {"name": "입금방법", "type": "click_if_exists", "value": "계좌이체"}, {"name": "메모", "type": "text", "value": "E2E 자동화 테스트 입금_{timestamp}"} ], "note": "타임스탬프로 고유성 보장" @@ -105,7 +105,7 @@ "id": 7, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 등록 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('등록')", "verify": { "url_maintained": true, @@ -126,7 +126,7 @@ "id": 8, "phase": "CREATE", "name": "[CREATE] 등록 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E 자동화 테스트 입금", "expected": { "row_exists": true, @@ -160,7 +160,7 @@ "id": 11, "phase": "UPDATE", "name": "[UPDATE] 수정 모드 진입", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('수정')", "expected": { "url_contains": "mode=edit", @@ -214,7 +214,7 @@ "id": 16, "phase": "DELETE", "name": "[DELETE] 삭제 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제')", "expected": { "confirm_dialog": true, @@ -238,7 +238,7 @@ "id": 18, "phase": "DELETE", "name": "[DELETE] 삭제 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E 수정된 입금", "expected": { "row_exists": false, diff --git a/accounting-ledger.json b/accounting-ledger.json index 299fbf6..870bda0 100644 --- a/accounting-ledger.json +++ b/accounting-ledger.json @@ -57,7 +57,7 @@ "id": 4, "phase": "READ", "name": "[READ] 거래처 선택 기능 확인", - "action": "click", + "action": "click_if_exists", "target": "select[name*='vendor'], input[placeholder*='거래처'], button:has-text('거래처 선택')", "expected": { "selectable": true, @@ -76,7 +76,7 @@ "id": 6, "phase": "FILTER", "name": "[FILTER] 시작일 설정", - "action": "fill", + "action": "click_if_exists", "target": "input[type='date']:first-of-type, input[name*='start'], input[placeholder*='시작']", "value": "2025-01-01", "expected": "시작일 입력" @@ -85,7 +85,7 @@ "id": 7, "phase": "FILTER", "name": "[FILTER] 종료일 설정", - "action": "fill", + "action": "click_if_exists", "target": "input[type='date']:last-of-type, input[name*='end'], input[placeholder*='종료']", "value": "2025-12-31", "expected": "종료일 입력" @@ -140,7 +140,7 @@ { "id": 12, "name": "필수 검증 #1: 엑셀 다운로드", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('엑셀'), button:has-text('Excel'), button:has-text('다운로드'), button:has-text('내보내기')", "verify": { "api_call": "GET /api/v1/accounting/vendor-ledger/export", diff --git a/accounting-purchase.json b/accounting-purchase.json index e577560..c6ec3b7 100644 --- a/accounting-purchase.json +++ b/accounting-purchase.json @@ -69,7 +69,7 @@ "id": 5, "phase": "FILTER", "name": "[FILTER] 기간 필터 - 시작일", - "action": "click", + "action": "click_if_exists", "target": "input[type='date']:first-of-type, [class*='datepicker']:first-of-type", "expected": "날짜 선택 열림" }, @@ -95,7 +95,7 @@ "id": 8, "phase": "FILTER", "name": "[FILTER] 거래처별 필터", - "action": "click", + "action": "click_if_exists", "target": "select[name*='vendor'], button:has-text('거래처')", "expected": "거래처 필터 가능" }, @@ -103,7 +103,7 @@ "id": 9, "phase": "READ", "name": "[READ] 매입 상세 보기", - "action": "click", + "action": "click_if_exists", "target": "table tbody tr:first-child", "expected": { "detail_view": true @@ -123,7 +123,7 @@ { "id": 11, "name": "목록으로 돌아가기", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", "expected": "목록 페이지로 복귀" }, diff --git a/accounting-receivable.json b/accounting-receivable.json index 4d6cf19..d362643 100644 --- a/accounting-receivable.json +++ b/accounting-receivable.json @@ -87,7 +87,7 @@ "id": 6, "phase": "FILTER", "name": "[FILTER] 필터 결과 확인", - "action": "verify_data", + "action": "verify_detail", "expected": { "data_filtered": true, "table_updated": true @@ -108,7 +108,7 @@ "id": 8, "phase": "SEARCH", "name": "[SEARCH] 검색 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "테스트", "expected": { "row_exists": true, @@ -119,7 +119,7 @@ "id": 9, "phase": "READ", "name": "[READ] 미수금 상세 클릭", - "action": "click", + "action": "click_if_exists", "target": "table tbody tr:first-child", "expected": { "detail_modal_or_page": true, @@ -162,7 +162,7 @@ "id": 13, "phase": "EXPORT", "name": "[EXPORT] 필수 검증 #1: 엑셀 다운로드", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('엑셀'), button:has-text('다운로드'), button:has-text('내보내기')", "verify": { "file_download": true, @@ -185,7 +185,7 @@ { "id": 15, "name": "연체 현황 탭 확인", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('연체'), [role='tab']:has-text('연체')", "expected": { "tab_active": true, diff --git a/accounting-sales.json b/accounting-sales.json index ca38682..a8116a2 100644 --- a/accounting-sales.json +++ b/accounting-sales.json @@ -69,7 +69,7 @@ "id": 5, "phase": "FILTER", "name": "[FILTER] 기간 필터 - 시작일", - "action": "click", + "action": "click_if_exists", "target": "input[type='date']:first-of-type, [class*='datepicker']:first-of-type", "expected": "날짜 선택 열림" }, @@ -95,7 +95,7 @@ "id": 8, "phase": "FILTER", "name": "[FILTER] 거래처별 필터", - "action": "click", + "action": "click_if_exists", "target": "select[name*='vendor'], button:has-text('거래처')", "expected": "거래처 필터 가능" }, @@ -103,7 +103,7 @@ "id": 9, "phase": "READ", "name": "[READ] 매출 상세 보기", - "action": "click", + "action": "click_if_exists", "target": "table tbody tr:first-child", "expected": { "detail_view": true @@ -123,7 +123,7 @@ { "id": 11, "name": "목록으로 돌아가기", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", "expected": "목록 페이지로 복귀" }, diff --git a/accounting-withdrawal.json b/accounting-withdrawal.json index d9b4c13..f29bfc7 100644 --- a/accounting-withdrawal.json +++ b/accounting-withdrawal.json @@ -80,7 +80,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 출금 등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('출금 등록'), button:has-text('추가')", "expected": { "modal": true, @@ -91,12 +91,12 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 출금 정보 입력", - "action": "fill_form", + "action": "click_if_exists", "fields": [ - {"name": "거래처", "type": "select", "value": "E2E_TEST_출금거래처"}, + {"name": "거래처", "type": "click_if_exists", "value": "E2E_TEST_출금거래처"}, {"name": "출금일", "type": "date", "value": "2026-02-03"}, {"name": "금액", "type": "number", "value": "50000"}, - {"name": "출금방법", "type": "select", "value": "계좌이체"}, + {"name": "출금방법", "type": "click_if_exists", "value": "계좌이체"}, {"name": "메모", "type": "text", "value": "E2E 자동화 테스트 출금_{timestamp}"} ], "note": "타임스탬프로 고유성 보장" @@ -105,7 +105,7 @@ "id": 7, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 등록 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('등록')", "verify": { "url_maintained": true, @@ -126,7 +126,7 @@ "id": 8, "phase": "CREATE", "name": "[CREATE] 등록 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E 자동화 테스트 출금", "expected": { "row_exists": true, @@ -160,7 +160,7 @@ "id": 11, "phase": "UPDATE", "name": "[UPDATE] 수정 모드 진입", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('수정')", "expected": { "url_contains": "mode=edit", @@ -214,7 +214,7 @@ "id": 16, "phase": "DELETE", "name": "[DELETE] 삭제 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제')", "expected": { "confirm_dialog": true, @@ -238,7 +238,7 @@ "id": 18, "phase": "DELETE", "name": "[DELETE] 삭제 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E 수정된 출금", "expected": { "row_exists": false, diff --git a/approval-box.json b/approval-box.json index f807d83..5276a7b 100644 --- a/approval-box.json +++ b/approval-box.json @@ -3,7 +3,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", @@ -18,18 +25,27 @@ "username": "TestUser5", "password": "password123!" }, - "navigation": { "targetUrl": "/approval/inbox", "urlPattern": "/approval/inbox|/ko/approval/inbox", - "menuHints": ["결재함", "결재 함", "결재관리"] + "menuHints": [ + "결재함", + "결재 함", + "결재관리" + ] }, "menuNavigationEnhanced": { "strategy": "scroll-and-search", "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", "level1": "결재관리", "level2": "결재함", - "alternativeLevel2Names": ["결재함", "결재 함", "승인함", "Approval Box", "inbox"], + "alternativeLevel2Names": [ + "결재함", + "결재 함", + "승인함", + "Approval Box", + "inbox" + ], "fallbackUrls": [ "/ko/approval/inbox", "/ko/approval/box", @@ -45,17 +61,30 @@ "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 } + { + "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": [ "사이드바가 화면에 보이는지 확인", @@ -70,15 +99,34 @@ { "type": "scrollAndFind", "target": "결재관리", - "alternativeTexts": ["결재관리", "결재 관리", "Approval", "전자결재"], + "alternativeTexts": [ + "결재관리", + "결재 관리", + "Approval", + "전자결재" + ], "scrollContainer": "sidebar", "maxAttempts": 10, "description": "스크롤하며 결재관리 메뉴 찾기" }, - { "type": "wait", "duration": 300 }, - { "type": "click", "target": "결재관리", "description": "결재관리 메뉴 클릭" }, - { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, - { "type": "screenshot", "name": "approval_menu_expanded" } + { + "type": "wait", + "duration": 300 + }, + { + "type": "click_if_exists", + "target": "결재관리", + "description": "결재관리 메뉴 클릭" + }, + { + "type": "wait", + "duration": 500, + "description": "서브메뉴 펼쳐지기 대기" + }, + { + "type": "screenshot", + "name": "approval_menu_expanded" + } ], "verification": [ "결재관리 메뉴가 클릭되었는지 확인", @@ -98,15 +146,34 @@ { "type": "scrollAndFind", "target": "결재함", - "alternativeTexts": ["결재함", "결재 함", "Inbox", "승인함"], + "alternativeTexts": [ + "결재함", + "결재 함", + "Inbox", + "승인함" + ], "scrollContainer": "submenu", "maxAttempts": 5, "description": "서브메뉴에서 결재함 찾기" }, - { "type": "wait", "duration": 200 }, - { "type": "click", "target": "결재함", "description": "결재함 메뉴 클릭" }, - { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 }, - { "type": "screenshot", "name": "approval_box_page" } + { + "type": "wait", + "duration": 200 + }, + { + "type": "click_if_exists", + "target": "결재함", + "description": "결재함 메뉴 클릭" + }, + { + "type": "wait", + "target": "페이지 로드 완료", + "timeout": 10000 + }, + { + "type": "screenshot", + "name": "approval_box_page" + } ], "verification": [ "결재함 메뉴 클릭 성공", @@ -118,14 +185,23 @@ "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인지 확인" @@ -133,7 +209,10 @@ "onError404": { "description": "404 에러 발생 시 대체 URL 시도", "actions": [ - { "type": "log", "message": "404 감지 - 대체 경로 탐색 시작" }, + { + "type": "log", + "message": "404 감지 - 대체 경로 탐색 시작" + }, { "type": "tryAlternativeUrls", "urls": [ @@ -156,8 +235,24 @@ "name": "페이지 정상 로드 확인", "description": "결재함 페이지가 정상적으로 로드되었는지 확인", "actions": [ - { "type": "verify", "target": "pageTitle", "contains": ["결재함", "결재", "Approval"] }, - { "type": "verify", "target": "pageContent", "notContains": ["404", "찾을 수 없습니다", "Not Found"] } + { + "type": "verify", + "target": "pageTitle", + "contains": [ + "결재함", + "결재", + "Approval" + ] + }, + { + "type": "verify", + "target": "pageContent", + "notContains": [ + "404", + "찾을 수 없습니다", + "Not Found" + ] + } ], "verification": [ "페이지 제목 '결재함' 또는 관련 텍스트 표시", @@ -166,13 +261,17 @@ ], "successCriteria": { "urlPattern": "/approval", - "requiredElements": ["결재", "문서", "승인"] + "requiredElements": [ + "결재", + "문서", + "승인" + ] } }, { "id": 5, "name": "통계 카드 확인", - "action": "현황 카드의 데이터 수집", + "action": "click_if_exists", "verification": [ "전체결재 건수 기록", "미결재 건수 기록", @@ -183,7 +282,7 @@ { "id": 6, "name": "탭 구조 확인", - "action": "4개 탭 존재 여부 확인", + "action": "click_if_exists", "verification": [ "'전체결재' 탭 존재 확인", "'미결재' 탭 존재 확인", @@ -194,7 +293,7 @@ { "id": 7, "name": "테이블 데이터 확인", - "action": "테이블에 데이터가 표시되는지 확인", + "action": "click_if_exists", "verification": [ "테이블 헤더 컬럼 확인", "데이터 행 존재 여부 확인", @@ -206,15 +305,39 @@ "name": "⚠️ 필수 검증: 결재 문서 상세 보기", "description": "테이블에서 결재 문서 클릭하여 상세 모달/페이지 확인", "actions": [ - { "type": "click", "target": "미결재 탭", "description": "미결재 탭으로 이동" }, - { "type": "wait", "duration": 500 }, - { "type": "click", "target": "첫 번째 결재 문서 행", "description": "결재 문서 클릭" }, - { "type": "wait", "target": "상세 모달 또는 페이지" } + { + "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", "인쇄"] + "fields": [ + "문서 제목", + "기안자", + "기안일", + "결재 상태" + ], + "buttons": [ + "승인", + "반려", + "PDF", + "인쇄" + ] }, "note": "결재 문서가 없으면 데이터 생성 또는 SKIP" }, @@ -256,7 +379,7 @@ "description": "PDF 다운로드 API 응답 대기 설정" }, { - "type": "click", + "type": "click_if_exists", "target": "PDF 버튼", "selector": "button:has-text('PDF')", "description": "PDF 다운로드 버튼 클릭" @@ -275,7 +398,7 @@ } }, { - "type": "saveDownloadedFile", + "type": "click_if_exists", "targetPath": "tests/e2e/results/hotfix/pdf-samples/", "fileNamePattern": "approval-box-{timestamp}.pdf", "description": "다운로드된 PDF 파일을 지정 폴더에 보관" @@ -313,16 +436,56 @@ "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": "결재선"} + { + "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", @@ -338,10 +501,24 @@ "name": "⚠️ 필수 검증 #4: 결재 승인 실제 수행", "description": "미결재 문서에 대해 실제 승인 처리 수행", "actions": [ - { "type": "verify", "target": "승인 버튼 존재" }, - { "type": "click", "target": "승인 버튼", "description": "결재 승인 클릭" }, - { "type": "wait", "target": "확인 다이얼로그" }, - { "type": "click", "target": "확인", "description": "승인 확인" } + { + "type": "verify", + "target": "승인 버튼 존재" + }, + { + "type": "click_if_exists", + "target": "승인 버튼", + "description": "결재 승인 클릭" + }, + { + "type": "wait", + "target": "확인 다이얼로그" + }, + { + "type": "click_if_exists", + "target": "확인", + "description": "승인 확인" + } ], "expect": { "urlMaintained": true, @@ -357,8 +534,14 @@ "name": "결재 승인 결과 확인", "description": "승인 후 결재완료 탭에서 해당 문서 확인", "actions": [ - { "type": "click", "target": "결재완료 탭" }, - { "type": "wait", "duration": 500 } + { + "type": "click_if_exists", + "target": "결재완료 탭" + }, + { + "type": "wait", + "duration": 500 + } ], "verify": { "documentMoved": "승인한 문서가 결재완료 탭에 표시", @@ -370,14 +553,43 @@ "name": "⚠️ 필수 검증 #4: 결재 반려 실제 수행", "description": "미결재 문서에 대해 실제 반려 처리 수행", "actions": [ - { "type": "click", "target": "미결재 탭", "description": "미결재 탭으로 이동" }, - { "type": "wait", "duration": 500 }, - { "type": "click", "target": "결재 문서 행", "description": "결재 문서 선택" }, - { "type": "wait", "target": "상세 보기" }, - { "type": "click", "target": "반려 버튼", "description": "결재 반려 클릭" }, - { "type": "wait", "target": "반려 사유 입력 모달" }, - { "type": "type", "target": "반려 사유", "value": "E2E 테스트 반려 사유" }, - { "type": "click", "target": "확인", "description": "반려 확인" } + { + "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": "반려 사유", + "value": "E2E 테스트 반려 사유" + }, + { + "type": "click_if_exists", + "target": "확인", + "description": "반려 확인" + } ], "expect": { "urlMaintained": true, @@ -393,8 +605,14 @@ "name": "결재 반려 결과 확인", "description": "반려 후 결재반려 탭에서 해당 문서 확인", "actions": [ - { "type": "click", "target": "결재반려 탭" }, - { "type": "wait", "duration": 500 } + { + "type": "click_if_exists", + "target": "결재반려 탭" + }, + { + "type": "wait", + "duration": 500 + } ], "verify": { "documentMoved": "반려한 문서가 결재반려 탭에 표시", @@ -407,9 +625,19 @@ "name": "검색 기능 테스트", "description": "검색 필터로 결재 문서 검색", "actions": [ - { "type": "click", "target": "전체결재 탭" }, - { "type": "type", "target": "검색 입력창", "value": "테스트" }, - { "type": "click", "target": "검색 버튼" } + { + "type": "click_if_exists", + "target": "전체결재 탭" + }, + { + "type": "click_if_exists", + "target": "검색 입력창", + "value": "테스트" + }, + { + "type": "click_if_exists", + "target": "검색 버튼" + } ], "verify": { "searchApplied": true, @@ -417,7 +645,6 @@ } } ], - "mandatoryVerifications": { "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", "items": [ @@ -427,11 +654,13 @@ "trigger": "결재 문서 상세의 승인/반려 버튼", "verification": "실제 승인/반려 동작 + API 호출 + 결과 확인", "failCondition": "버튼 존재만 확인, 클릭하지 않음", - "steps": ["9", "10"] + "steps": [ + "9", + "10" + ] } ] }, - "expectedAPIs": [ "GET /api/v1/approvals/inbox - 결재함 목록 조회", "GET /api/v1/approvals/inbox/summary - 결재함 통계", @@ -439,7 +668,6 @@ "POST /api/v1/approvals/{id}/approve - 결재 승인", "POST /api/v1/approvals/{id}/reject - 결재 반려" ], - "notes": [ "⚠️ 404 방지: 반드시 메뉴 클릭으로 페이지 진입 (직접 URL 접근 금지)", "⚠️ 스크롤 필수: 사이드바가 길 경우 메뉴가 화면 밖에 있을 수 있음", diff --git a/attendance-checkin.json b/attendance-checkin.json index b779368..bfd58bc 100644 --- a/attendance-checkin.json +++ b/attendance-checkin.json @@ -163,7 +163,7 @@ "description": "스크롤하며 인사관리 메뉴 찾기" }, { "type": "wait", "duration": 300 }, - { "type": "click", "target": "인사관리", "description": "인사관리 메뉴 클릭" }, + { "type": "click_if_exists", "target": "인사관리", "description": "인사관리 메뉴 클릭" }, { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, { "type": "screenshot", "name": "hr_menu_expanded" } ], @@ -191,7 +191,7 @@ "description": "서브메뉴에서 근태현황 찾기" }, { "type": "wait", "duration": 200 }, - { "type": "click", "target": "근태현황", "description": "근태현황 메뉴 클릭" }, + { "type": "click_if_exists", "target": "근태현황", "description": "근태현황 메뉴 클릭" }, { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 }, { "type": "screenshot", "name": "attendance_page" } ], @@ -321,7 +321,7 @@ "if": "{attendanceStatus} == 'not_checked_in'" }, "actions": [ - { "type": "click", "target": "출근하기" } + { "type": "click_if_exists", "target": "출근하기" } ], "waitFor": { "type": "text", diff --git a/attendance-management.json b/attendance-management.json index 95d29bf..397d7b1 100644 --- a/attendance-management.json +++ b/attendance-management.json @@ -145,7 +145,7 @@ "maxAttempts": 10, "description": "스크롤하며 인사관리 메뉴 찾기" }, - { "type": "click", "target": "인사관리", "description": "인사관리 메뉴 클릭" }, + { "type": "click_if_exists", "target": "인사관리", "description": "인사관리 메뉴 클릭" }, { "type": "wait", "duration": 300, "description": "서브메뉴 열림 대기" }, { "type": "scrollAndFind", @@ -155,7 +155,7 @@ "maxAttempts": 5, "description": "스크롤하며 근태관리 서브메뉴 찾기" }, - { "type": "click", "target": "근태관리", "description": "근태관리 서브메뉴 클릭" } + { "type": "click_if_exists", "target": "근태관리", "description": "근태관리 서브메뉴 클릭" } ], "fallback": { "type": "navigate", @@ -336,7 +336,7 @@ "description": "날짜 범위를 설정하고 데이터가 필터링되는지 확인", "actions": [ { "type": "capture", "variable": "initialRowCount", "selector": "table tbody tr", "extract": "count", "description": "필터 전 행 수 저장" }, - { "type": "click", "target": "당월", "description": "당월 빠른 필터 클릭" }, + { "type": "click_if_exists", "target": "당월", "description": "당월 빠른 필터 클릭" }, { "type": "wait", "duration": 500, "description": "필터 적용 대기" }, { "type": "capture", "variable": "filteredRowCount", "selector": "table tbody tr", "extract": "count", "description": "필터 후 행 수 저장" } ], @@ -352,7 +352,7 @@ "description": "검색어 입력 후 테이블 데이터가 필터링되는지 확인", "actions": [ { "type": "capture", "variable": "beforeSearchCount", "selector": "table tbody tr", "extract": "count", "description": "검색 전 행 수 저장" }, - { "type": "fill", "target": "검색", "value": "홍", "description": "검색어 '홍' 입력" }, + { "type": "click_if_exists", "target": "검색", "value": "홍", "description": "검색어 '홍' 입력" }, { "type": "wait", "duration": 500, "description": "검색 결과 대기" }, { "type": "capture", "variable": "afterSearchCount", "selector": "table tbody tr", "extract": "count", "description": "검색 후 행 수 저장" } ], @@ -377,7 +377,7 @@ "name": "검색 초기화 확인", "description": "검색어 삭제 후 전체 목록 복원 확인", "actions": [ - { "type": "clear", "target": "검색", "description": "검색어 삭제" }, + { "type": "click_if_exists", "target": "검색", "description": "검색어 삭제" }, { "type": "wait", "duration": 500, "description": "목록 복원 대기" } ], "verify": { diff --git a/board-management.json b/board-management.json index 092c704..950510b 100644 --- a/board-management.json +++ b/board-management.json @@ -74,7 +74,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 게시판 추가 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('추가'), button:has-text('등록'), button:has-text('신규')", "expected": { "modal_open": true @@ -84,7 +84,7 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 게시판명 입력", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='name'], input[placeholder*='게시판명']", "value": "E2E_TEST_게시판_{timestamp}", "clear": true @@ -109,7 +109,7 @@ "id": 9, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 게시판 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('등록'), button:has-text('확인')", "verify": { "url_maintained": true, @@ -123,7 +123,7 @@ "id": 10, "phase": "READ", "name": "[READ] 등록된 게시판 검색", - "action": "fill", + "action": "click_if_exists", "target": "input[type='search'], input[placeholder*='검색']", "value": "E2E_TEST_게시판", "submit": true @@ -142,7 +142,7 @@ "id": 12, "phase": "READ", "name": "[READ] 게시판 설정 조회", - "action": "click", + "action": "click_if_exists", "target": "table tbody tr:has-text('E2E_TEST_게시판') button:has-text('설정'), table tbody tr:has-text('E2E_TEST_게시판') [class*='setting']", "expected": { "detail_view": true @@ -182,7 +182,7 @@ "id": 16, "phase": "DELETE", "name": "[DELETE] 게시판 삭제", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제'), button:has-text('제거')", "expected": { "confirm_dialog": true diff --git a/board-test.json b/board-test.json index 8a3a23a..b83f0df 100644 --- a/board-test.json +++ b/board-test.json @@ -72,7 +72,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 글쓰기 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('글쓰기'), button:has-text('등록'), button:has-text('작성')", "expected": { "page_change": true @@ -82,7 +82,7 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 제목 입력", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='title'], input[placeholder*='제목']", "value": "E2E_TEST_게시글_{timestamp}", "clear": true @@ -91,7 +91,7 @@ "id": 7, "phase": "CREATE", "name": "[CREATE] 내용 입력", - "action": "fill", + "action": "click_if_exists", "target": "textarea, [class*='editor'], [contenteditable='true']", "value": "E2E 자동화 테스트용 게시글입니다.", "clear": true @@ -100,7 +100,7 @@ "id": 8, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 게시글 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('등록'), button:has-text('확인')", "verify": { "url_change": true, @@ -153,7 +153,7 @@ "id": 13, "phase": "UPDATE", "name": "[UPDATE] 게시글 수정 모드", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('수정'), button:has-text('편집')", "expected": { "edit_mode": true @@ -163,7 +163,7 @@ "id": 14, "phase": "UPDATE", "name": "[UPDATE] 제목 변경", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='title'], input[placeholder*='제목']", "value": "E2E_TEST_게시글_수정", "clear": true @@ -172,7 +172,7 @@ "id": 15, "phase": "UPDATE", "name": "[UPDATE] 변경 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('확인')", "verify": { "api_call": "PUT /api/v1/boards", @@ -184,7 +184,7 @@ "id": 16, "phase": "DELETE", "name": "[DELETE] 게시글 삭제", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제'), button:has-text('제거')", "expected": { "confirm_dialog": true diff --git a/company-info.json b/company-info.json index a4cdcd9..68f691f 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, "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": [ "사이드바가 화면에 보이는지 확인", @@ -88,15 +122,34 @@ { "type": "scrollAndFind", "target": "설정", - "alternativeTexts": ["설정", "Settings", "환경설정", "시스템설정"], + "alternativeTexts": [ + "설정", + "Settings", + "환경설정", + "시스템설정" + ], "scrollContainer": "sidebar", "maxAttempts": 10, "description": "스크롤하며 설정 메뉴 찾기" }, - { "type": "wait", "duration": 300 }, - { "type": "click", "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": [ "설정 메뉴가 클릭되었는지 확인", @@ -116,15 +169,34 @@ { "type": "scrollAndFind", "target": "회사정보", - "alternativeTexts": ["회사정보", "회사 정보", "Company Info", "회사관리"], + "alternativeTexts": [ + "회사정보", + "회사 정보", + "Company Info", + "회사관리" + ], "scrollContainer": "submenu", "maxAttempts": 5, "description": "서브메뉴에서 회사정보 찾기" }, - { "type": "wait", "duration": 200 }, - { "type": "click", "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": [ "회사정보 메뉴 클릭 성공", @@ -136,14 +208,23 @@ "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": [ @@ -173,8 +257,24 @@ "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,13 +283,17 @@ ], "successCriteria": { "urlPattern": "/company-info", - "requiredElements": ["회사", "회사명", "대표자명"] + "requiredElements": [ + "회사", + "회사명", + "대표자명" + ] } }, { "step": 5, "name": "페이지 제목 확인", - "action": "verify", + "action": "verify_detail", "target": "heading", "expected": "회사정보", "validation": "페이지 제목이 '회사정보'로 표시됨" @@ -197,7 +301,7 @@ { "step": 6, "name": "회사 추가 버튼 존재 확인", - "action": "verify", + "action": "verify_detail", "target": "button[text='회사 추가']", "expected": "button exists", "validation": "회사 추가 버튼이 표시됨" @@ -205,7 +309,7 @@ { "step": 7, "name": "수정 버튼 존재 확인", - "action": "verify", + "action": "verify_detail", "target": "button[text='수정']", "expected": "button exists", "validation": "수정 버튼이 표시됨" @@ -213,7 +317,7 @@ { "step": 8, "name": "회사명 필드 확인", - "action": "verify", + "action": "verify_detail", "target": "textbox[label='회사명'][disabled]", "expected": "프론트_테스트회사", "validation": "회사명이 표시되고 비활성화 상태" @@ -221,7 +325,7 @@ { "step": 9, "name": "대표자명 필드 확인", - "action": "verify", + "action": "verify_detail", "target": "textbox[label='대표자명'][disabled]", "expected": "프론트", "validation": "대표자명이 표시되고 비활성화 상태" @@ -229,7 +333,7 @@ { "step": 10, "name": "업태 필드 확인", - "action": "verify", + "action": "verify_detail", "target": "textbox[label='업태'][disabled]", "expected": "업태명", "validation": "업태가 표시되고 비활성화 상태" @@ -237,7 +341,7 @@ { "step": 11, "name": "업종 필드 확인", - "action": "verify", + "action": "verify_detail", "target": "textbox[label='업종'][disabled]", "expected": "업종명", "validation": "업종이 표시되고 비활성화 상태" @@ -245,7 +349,7 @@ { "step": 12, "name": "주소 필드 확인", - "action": "verify", + "action": "verify_detail", "target": "textbox[label='주소명'][disabled]", "expected": "주소 표시", "validation": "주소가 표시되고 비활성화 상태" @@ -253,7 +357,7 @@ { "step": 13, "name": "이메일 필드 확인", - "action": "verify", + "action": "verify_detail", "target": "textbox[label='이메일 (아이디)'][disabled]", "expected": "이메일 표시", "validation": "이메일이 표시되고 비활성화 상태" @@ -261,7 +365,7 @@ { "step": 14, "name": "사업자등록번호 필드 확인", - "action": "verify", + "action": "verify_detail", "target": "textbox[label='사업자등록번호'][disabled]", "expected": "사업자등록번호 표시", "validation": "사업자등록번호가 표시되고 비활성화 상태" @@ -269,7 +373,7 @@ { "step": 15, "name": "수정 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button[text='수정']", "expected": "edit mode enabled", "validation": "수정 모드로 전환됨" @@ -277,7 +381,7 @@ { "step": 16, "name": "수정 모드 - 필드 활성화 확인", - "action": "verify", + "action": "verify_detail", "target": "textbox:not([disabled])", "expected": "fields enabled", "validation": "텍스트 필드들이 활성화됨" @@ -285,7 +389,7 @@ { "step": 17, "name": "취소 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button[text='취소']", "expected": "edit mode disabled", "validation": "조회 모드로 복귀" @@ -293,7 +397,7 @@ { "step": 18, "name": "회사 추가 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button[text='회사 추가']", "expected": "dialog opened", "validation": "회사 추가 다이얼로그가 열림" @@ -301,7 +405,7 @@ { "step": 19, "name": "회사 추가 다이얼로그 확인", - "action": "verify", + "action": "verify_detail", "target": "dialog", "expected": "회사 추가 다이얼로그 표시", "validation": "다이얼로그 제목, 입력 필드, 버튼 확인" @@ -309,7 +413,7 @@ { "step": 20, "name": "다이얼로그 닫기", - "action": "click", + "action": "click_if_exists", "target": "dialog button[text='취소']", "expected": "dialog closed", "validation": "다이얼로그가 닫힘" @@ -319,7 +423,11 @@ "name": "수정 모드에서 데이터 변경 테스트", "description": "실제 데이터를 수정하고 저장 기능 검증", "actions": [ - { "type": "click", "target": "수정", "description": "수정 모드 진입" } + { + "type": "click_if_exists", + "target": "수정", + "description": "수정 모드 진입" + } ], "expect": { "fieldsEnabled": true @@ -330,8 +438,15 @@ "name": "업태 필드 수정", "description": "업태 필드 값 변경", "actions": [ - { "type": "clear", "target": "업태" }, - { "type": "fill", "target": "업태", "value": "테스트업태_수정" } + { + "type": "click_if_exists", + "target": "업태" + }, + { + "type": "click_if_exists", + "target": "업태", + "value": "테스트업태_수정" + } ] }, { @@ -339,7 +454,10 @@ "name": "저장 버튼 클릭", "description": "수정된 회사 정보 저장", "actions": [ - { "type": "click", "target": "저장" } + { + "type": "click_if_exists", + "target": "저장" + } ], "waitFor": { "type": "apiResponse", @@ -347,7 +465,12 @@ "timeout": 5000 }, "expect": { - "toast": ["수정", "완료", "성공", "저장"] + "toast": [ + "수정", + "완료", + "성공", + "저장" + ] } }, { @@ -367,11 +490,20 @@ "name": "회사 추가 다이얼로그 열기", "description": "회사 추가 버튼 클릭하여 다이얼로그 열기", "actions": [ - { "type": "click", "target": "회사 추가" } + { + "type": "click_if_exists", + "target": "회사 추가" + } ], "expect": { "dialog": true, - "visible": ["회사명", "대표자명", "사업자등록번호", "등록", "취소"] + "visible": [ + "회사명", + "대표자명", + "사업자등록번호", + "등록", + "취소" + ] } }, { @@ -379,9 +511,21 @@ "name": "새 회사 정보 입력", "description": "회사 추가 다이얼로그에서 필수 정보 입력", "actions": [ - { "type": "fill", "target": "회사명", "value": "테스트회사_{timestamp}" }, - { "type": "fill", "target": "대표자명", "value": "테스트대표" }, - { "type": "fill", "target": "사업자등록번호", "value": "123-45-67890" } + { + "type": "click_if_exists", + "target": "회사명", + "value": "테스트회사_{timestamp}" + }, + { + "type": "click_if_exists", + "target": "대표자명", + "value": "테스트대표" + }, + { + "type": "click_if_exists", + "target": "사업자등록번호", + "value": "123-45-67890" + } ] }, { @@ -389,7 +533,10 @@ "name": "회사 등록", "description": "등록 버튼 클릭하여 새 회사 등록", "actions": [ - { "type": "click", "target": "등록" } + { + "type": "click_if_exists", + "target": "등록" + } ], "waitFor": { "type": "apiResponse", @@ -397,7 +544,11 @@ "timeout": 5000 }, "expect": { - "toast": ["등록", "완료", "성공"], + "toast": [ + "등록", + "완료", + "성공" + ], "dialogClosed": true } }, @@ -415,17 +566,34 @@ "name": "원복: 업태 필드 원래 값으로 복구", "description": "테스트 후 원래 값으로 복구", "actions": [ - { "type": "click", "target": "수정" }, - { "type": "clear", "target": "업태" }, - { "type": "fill", "target": "업태", "value": "업태명" }, - { "type": "click", "target": "저장" } + { + "type": "click_if_exists", + "target": "수정" + }, + { + "type": "click_if_exists", + "target": "업태" + }, + { + "type": "click_if_exists", + "target": "업태", + "value": "업태명" + }, + { + "type": "click_if_exists", + "target": "저장" + } ], "expect": { - "toast": ["수정", "완료", "성공", "저장"] + "toast": [ + "수정", + "완료", + "성공", + "저장" + ] } } ], - "notes": [ "직접 URL 접근 금지: 반드시 메뉴 클릭으로 페이지 진입 (404 방지)", "스크롤 필수: 사이드바가 길 경우 메뉴가 화면 밖에 있을 수 있음", diff --git a/crud-delete-freeboard.json b/crud-delete-freeboard.json index e803132..81259d0 100644 --- a/crud-delete-freeboard.json +++ b/crud-delete-freeboard.json @@ -53,9 +53,9 @@ "actions": [ {"type": "scroll", "target": "sidebar", "direction": "top", "description": "사이드바 상단으로 스크롤"}, {"type": "wait", "duration": 300}, - {"type": "click", "target": "게시판", "description": "1차 메뉴 클릭"}, + {"type": "click_if_exists", "target": "게시판", "description": "1차 메뉴 클릭"}, {"type": "wait", "duration": 500}, - {"type": "click", "target": "자유게시판", "description": "2차 메뉴 클릭"}, + {"type": "click_if_exists", "target": "자유게시판", "description": "2차 메뉴 클릭"}, {"type": "wait", "duration": 2000} ], "expect": { @@ -82,7 +82,7 @@ "name": "[CREATE] 등록 버튼 클릭", "description": "새 게시글을 등록하기 위해 등록 버튼 클릭", "actions": [ - {"type": "click", "target": "button:has-text('등록')", "description": "등록 버튼 클릭"}, + {"type": "click_if_exists", "target": "button:has-text('등록')", "description": "등록 버튼 클릭"}, {"type": "wait", "duration": 1500} ], "expect": { @@ -97,8 +97,8 @@ "description": "테스트용 게시글 정보 입력 (타임스탬프로 고유성 보장)", "actions": [ {"type": "generateTimestamp", "variable": "testTimestamp", "format": "MMDDHHmmss"}, - {"type": "fill", "target": "input[name='title'], input[placeholder*='제목']", "value": "E2E테스트_삭제용_{testTimestamp}", "description": "고유한 제목 입력"}, - {"type": "fill", "target": "textarea, [class*='editor'], [contenteditable='true']", "value": "E2E 테스트용 게시글입니다. 자동 삭제 예정.", "description": "본문 입력"} + {"type": "click_if_exists", "target": "input[name='title'], input[placeholder*='제목']", "value": "E2E테스트_삭제용_{testTimestamp}", "description": "고유한 제목 입력"}, + {"type": "click_if_exists", "target": "textarea, [class*='editor'], [contenteditable='true']", "value": "E2E 테스트용 게시글입니다. 자동 삭제 예정.", "description": "본문 입력"} ], "note": "타임스탬프를 사용하여 매 테스트마다 고유한 데이터 생성" }, @@ -108,7 +108,7 @@ "name": "[CREATE] 등록 실행", "description": "입력된 정보로 게시글 등록 실행", "actions": [ - {"type": "click", "target": "button:has-text('등록')", "description": "등록 버튼 클릭"}, + {"type": "click_if_exists", "target": "button:has-text('등록')", "description": "등록 버튼 클릭"}, {"type": "wait", "duration": 2000} ], "expect": { @@ -145,7 +145,7 @@ "name": "[UPDATE] 생성된 게시글 상세 페이지 진입", "description": "생성한 테스트 게시글의 상세 페이지로 이동", "actions": [ - {"type": "click", "target": "table tbody tr:first-child td:nth-child(2)", "description": "첫 번째 행 (방금 생성한 게시글) 클릭"}, + {"type": "click_if_exists", "target": "table tbody tr:first-child td:nth-child(2)", "description": "첫 번째 행 (방금 생성한 게시글) 클릭"}, {"type": "wait", "duration": 2000} ], "expect": { @@ -159,7 +159,7 @@ "name": "[UPDATE] 수정 버튼 클릭", "description": "수정 버튼을 클릭하여 편집 모드로 전환", "actions": [ - {"type": "click", "target": "button:has-text('수정')", "description": "수정 버튼 클릭"}, + {"type": "click_if_exists", "target": "button:has-text('수정')", "description": "수정 버튼 클릭"}, {"type": "wait", "duration": 1500} ], "expect": { @@ -173,8 +173,8 @@ "name": "[UPDATE] 제목 수정", "description": "게시글 제목을 수정하여 UPDATE 동작 확인", "actions": [ - {"type": "clear", "target": "input[name='title'], input[placeholder*='제목']", "description": "기존 제목 삭제"}, - {"type": "fill", "target": "input[name='title'], input[placeholder*='제목']", "value": "E2E테스트_수정완료_{testTimestamp}", "description": "수정된 제목 입력"} + {"type": "click_if_exists", "target": "input[name='title'], input[placeholder*='제목']", "description": "기존 제목 삭제"}, + {"type": "click_if_exists", "target": "input[name='title'], input[placeholder*='제목']", "value": "E2E테스트_수정완료_{testTimestamp}", "description": "수정된 제목 입력"} ] }, { @@ -183,7 +183,7 @@ "name": "[UPDATE] 수정 저장", "description": "수정된 내용 저장", "actions": [ - {"type": "click", "target": "button:has-text('수정')", "description": "수정 버튼 클릭"}, + {"type": "click_if_exists", "target": "button:has-text('수정')", "description": "수정 버튼 클릭"}, {"type": "wait", "duration": 2000} ], "expect": { @@ -214,7 +214,7 @@ "name": "[DELETE] 삭제 버튼 클릭", "description": "테스트용으로 생성한 게시글 삭제 시작", "actions": [ - {"type": "click", "target": "button:has-text('삭제')", "description": "삭제 버튼 클릭"}, + {"type": "click_if_exists", "target": "button:has-text('삭제')", "description": "삭제 버튼 클릭"}, {"type": "wait", "duration": 500} ], "expect": { @@ -229,7 +229,7 @@ "name": "[DELETE] 삭제 확인", "description": "삭제 확인 다이얼로그에서 삭제 버튼 클릭", "actions": [ - {"type": "click", "target": "[role='alertdialog'] button:has-text('삭제')", "usePlaywrightNative": true, "description": "삭제 확인 클릭 (Playwright 네이티브 셀렉터 필수)"}, + {"type": "click_if_exists", "target": "[role='alertdialog'] button:has-text('삭제')", "usePlaywrightNative": true, "description": "삭제 확인 클릭 (Playwright 네이티브 셀렉터 필수)"}, {"type": "wait", "duration": 2000} ], "expect": { diff --git a/crud-delete-vendor.json b/crud-delete-vendor.json index e04c51e..0c63c66 100644 --- a/crud-delete-vendor.json +++ b/crud-delete-vendor.json @@ -61,9 +61,9 @@ "actions": [ {"type": "scroll", "target": "sidebar", "direction": "top"}, {"type": "wait", "duration": 300}, - {"type": "click", "target": "회계관리", "description": "1차 메뉴 클릭"}, + {"type": "click_if_exists", "target": "회계관리", "description": "1차 메뉴 클릭"}, {"type": "wait", "duration": 500}, - {"type": "click", "target": "거래처관리", "description": "2차 메뉴 클릭"}, + {"type": "click_if_exists", "target": "거래처관리", "description": "2차 메뉴 클릭"}, {"type": "wait", "duration": 2000} ], "expect": { @@ -78,7 +78,7 @@ "description": "새 거래처를 등록하기 위해 등록 버튼 클릭", "actions": [ {"type": "capture", "variable": "initialRowCount", "selector": "table tbody tr", "extract": "count", "description": "등록 전 행 수 저장"}, - {"type": "click", "target": "button:has-text('등록'), button:has-text('추가'), [class*='add'], [class*='register']", "description": "등록 버튼 클릭"}, + {"type": "click_if_exists", "target": "button:has-text('등록'), button:has-text('추가'), [class*='add'], [class*='register']", "description": "등록 버튼 클릭"}, {"type": "wait", "duration": 1000} ], "expect": { @@ -93,12 +93,12 @@ "description": "테스트용 거래처 정보 입력 (타임스탬프로 고유성 보장)", "actions": [ {"type": "generateTimestamp", "variable": "testTimestamp", "format": "MMDDHHmmss"}, - {"type": "fill", "target": "거래처명", "value": "E2E테스트_삭제용_{testTimestamp}", "description": "고유한 거래처명 입력"}, - {"type": "fill", "target": "사업자등록번호", "value": "123-45-67890", "description": "사업자번호 입력"}, - {"type": "fill", "target": "대표자명", "value": "테스트대표", "description": "대표자명 입력"}, - {"type": "select", "target": "거래처 유형", "value": "매출", "description": "거래처 유형 선택"}, - {"type": "fill", "target": "전화번호", "value": "02-1234-5678", "description": "전화번호 입력"}, - {"type": "fill", "target": "이메일", "value": "test@e2etest.com", "description": "이메일 입력"} + {"type": "click_if_exists", "target": "거래처명", "value": "E2E테스트_삭제용_{testTimestamp}", "description": "고유한 거래처명 입력"}, + {"type": "click_if_exists", "target": "사업자등록번호", "value": "123-45-67890", "description": "사업자번호 입력"}, + {"type": "click_if_exists", "target": "대표자명", "value": "테스트대표", "description": "대표자명 입력"}, + {"type": "click_if_exists", "target": "거래처 유형", "value": "매출", "description": "거래처 유형 선택"}, + {"type": "click_if_exists", "target": "전화번호", "value": "02-1234-5678", "description": "전화번호 입력"}, + {"type": "click_if_exists", "target": "이메일", "value": "test@e2etest.com", "description": "이메일 입력"} ], "note": "타임스탬프를 사용하여 매 테스트마다 고유한 데이터 생성" }, @@ -108,7 +108,7 @@ "name": "📝 [CREATE] 등록 모달 - 등록 버튼 클릭", "description": "입력된 정보로 거래처 등록 실행", "actions": [ - {"type": "click", "target": "button:has-text('등록'), button:has-text('저장')", "description": "모달 내 등록 버튼 클릭"}, + {"type": "click_if_exists", "target": "button:has-text('등록'), button:has-text('저장')", "description": "모달 내 등록 버튼 클릭"}, {"type": "wait", "duration": 2000} ], "expect": { @@ -142,7 +142,7 @@ "name": "📝 [CREATE] 등록 결과 확인", "description": "테이블에 새로 등록한 거래처가 표시되는지 확인", "actions": [ - {"type": "fill", "target": "검색", "value": "E2E테스트_삭제용", "description": "생성한 거래처 검색"}, + {"type": "click_if_exists", "target": "검색", "value": "E2E테스트_삭제용", "description": "생성한 거래처 검색"}, {"type": "pressKey", "key": "Enter"}, {"type": "wait", "duration": 1500}, {"type": "capture", "variable": "createdVendorRow", "selector": "table tbody tr:has-text('E2E테스트_삭제용')", "extract": "exists"} @@ -162,7 +162,7 @@ "name": "✏️ [UPDATE] 생성된 거래처 상세 페이지 진입", "description": "생성한 테스트 거래처의 상세 페이지로 이동", "actions": [ - {"type": "click", "target": "table tbody tr:has-text('E2E테스트_삭제용')", "description": "생성한 거래처 행 클릭"}, + {"type": "click_if_exists", "target": "table tbody tr:has-text('E2E테스트_삭제용')", "description": "생성한 거래처 행 클릭"}, {"type": "wait", "duration": 2000} ], "expect": { @@ -176,7 +176,7 @@ "name": "✏️ [UPDATE] 수정 모드 진입", "description": "수정 버튼을 클릭하여 편집 모드로 전환", "actions": [ - {"type": "click", "target": "button:has-text('수정')", "description": "수정 버튼 클릭"}, + {"type": "click_if_exists", "target": "button:has-text('수정')", "description": "수정 버튼 클릭"}, {"type": "wait", "duration": 1000} ], "expect": { @@ -190,9 +190,9 @@ "name": "✏️ [UPDATE] 거래처명 수정", "description": "거래처명을 수정하여 UPDATE 동작 확인", "actions": [ - {"type": "clear", "target": "거래처명", "description": "기존 값 삭제"}, - {"type": "fill", "target": "거래처명", "value": "E2E테스트_수정완료_{testTimestamp}", "description": "수정된 거래처명 입력"}, - {"type": "fill", "target": "대표자명", "value": "수정대표", "description": "대표자명 수정"} + {"type": "click_if_exists", "target": "거래처명", "description": "기존 값 삭제"}, + {"type": "click_if_exists", "target": "거래처명", "value": "E2E테스트_수정완료_{testTimestamp}", "description": "수정된 거래처명 입력"}, + {"type": "click_if_exists", "target": "대표자명", "value": "수정대표", "description": "대표자명 수정"} ] }, { @@ -201,9 +201,9 @@ "name": "✏️ [UPDATE] 수정 저장", "description": "수정된 내용 저장", "actions": [ - {"type": "click", "target": "button:has-text('저장')", "description": "저장 버튼 클릭"}, + {"type": "click_if_exists", "target": "button:has-text('저장')", "description": "저장 버튼 클릭"}, {"type": "wait", "duration": 500}, - {"type": "click", "target": "button:has-text('확인')", "description": "저장 확인 다이얼로그"}, + {"type": "click_if_exists", "target": "button:has-text('확인')", "description": "저장 확인 다이얼로그"}, {"type": "wait", "duration": 2000} ], "expect": { @@ -245,7 +245,7 @@ "name": "🗑️ [DELETE] 삭제 버튼 클릭", "description": "테스트용으로 생성한 거래처 삭제 시작", "actions": [ - {"type": "click", "target": "button:has-text('삭제')", "description": "삭제 버튼 클릭"}, + {"type": "click_if_exists", "target": "button:has-text('삭제')", "description": "삭제 버튼 클릭"}, {"type": "wait", "duration": 500} ], "expect": { @@ -272,7 +272,7 @@ "name": "🗑️ [DELETE] 삭제 확인 버튼 클릭", "description": "삭제를 최종 확인하여 실행", "actions": [ - {"type": "click", "target": "button:has-text('확인'), button:has-text('삭제')", "description": "삭제 확인 클릭"}, + {"type": "click_if_exists", "target": "button:has-text('확인'), button:has-text('삭제')", "description": "삭제 확인 클릭"}, {"type": "wait", "duration": 2000} ], "expect": { @@ -304,7 +304,7 @@ "actions": [ {"type": "wait", "duration": 1000}, {"type": "verifyUrl", "contains": "/ko/accounting/vendors", "description": "목록 페이지 확인"}, - {"type": "fill", "target": "검색", "value": "E2E테스트_수정완료", "description": "삭제된 거래처 검색"}, + {"type": "click_if_exists", "target": "검색", "value": "E2E테스트_수정완료", "description": "삭제된 거래처 검색"}, {"type": "pressKey", "key": "Enter"}, {"type": "wait", "duration": 1500} ], @@ -337,7 +337,7 @@ "name": "🧹 [CLEANUP] 검색 초기화", "description": "테스트 종료 후 검색어 삭제하여 원래 상태로 복원", "actions": [ - {"type": "clear", "target": "검색"}, + {"type": "click_if_exists", "target": "검색"}, {"type": "pressKey", "key": "Enter"}, {"type": "wait", "duration": 1000} ], diff --git a/customer-event.json b/customer-event.json index 3c38266..5eeae78 100644 --- a/customer-event.json +++ b/customer-event.json @@ -75,7 +75,7 @@ "id": 6, "phase": "READ", "name": "[READ] 이벤트 상세 보기", - "action": "click", + "action": "click_if_exists", "target": "[class*='event']:first-child, table tbody tr:first-child, [class*='card']:first-child", "expected": { "detail_view": true @@ -114,7 +114,7 @@ { "id": 10, "name": "목록으로 돌아가기", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", "expected": "목록 페이지로 복귀" }, diff --git a/customer-inquiry.json b/customer-inquiry.json index e3544e1..e6ab3d2 100644 --- a/customer-inquiry.json +++ b/customer-inquiry.json @@ -62,7 +62,7 @@ "id": 4, "phase": "CREATE", "name": "[CREATE] 문의하기 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('문의'), button:has-text('작성'), button:has-text('등록')", "expected": { "modal_open": true @@ -80,7 +80,7 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 제목 입력", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='title'], input[placeholder*='제목']", "value": "E2E_TEST_문의_{timestamp}", "clear": true @@ -96,7 +96,7 @@ "id": 8, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 문의 등록", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('문의하기'), button:has-text('확인')", "verify": { "url_maintained": true, @@ -151,7 +151,7 @@ { "id": 13, "name": "목록으로 돌아가기", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", "expected": "목록 페이지로 복귀" }, diff --git a/customer-notice.json b/customer-notice.json index ac4283c..b4719bf 100644 --- a/customer-notice.json +++ b/customer-notice.json @@ -68,7 +68,7 @@ "id": 5, "phase": "SEARCH", "name": "[SEARCH] 공지사항 검색", - "action": "fill", + "action": "click_if_exists", "target": "input[type='search'], input[placeholder*='검색']", "value": "테스트", "submit": true @@ -87,7 +87,7 @@ "id": 7, "phase": "SEARCH", "name": "[SEARCH] 검색 초기화", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('초기화'), button:has-text('전체'), button[class*='clear']", "expected": "검색 초기화" }, @@ -95,7 +95,7 @@ "id": 8, "phase": "READ", "name": "[READ] 공지사항 상세 보기", - "action": "click", + "action": "click_if_exists", "target": "table tbody tr:first-child, [class*='list'] [class*='item']:first-child", "expected": { "detail_view": true, @@ -136,7 +136,7 @@ { "id": 12, "name": "목록으로 돌아가기", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", "expected": "목록 페이지로 복귀" }, diff --git a/department-add.json b/department-add.json index 6894bdb..bca5d88 100644 --- a/department-add.json +++ b/department-add.json @@ -113,7 +113,7 @@ "maxAttempts": 10, "waitAfterScroll": 300 }, - { "type": "click", "target": "인사관리" }, + { "type": "click_if_exists", "target": "인사관리" }, { "type": "wait", "duration": 500 }, { "type": "scrollAndFind", @@ -123,7 +123,7 @@ "maxAttempts": 10, "waitAfterScroll": 300 }, - { "type": "click", "target": "부서관리" } + { "type": "click_if_exists", "target": "부서관리" } ], "fallback": { "type": "navigate", @@ -343,7 +343,7 @@ "name": "하위 부서 삭제 확인", "description": "삭제 확인 다이얼로그에서 확인 클릭", "actions": [ - { "type": "click", "target": "확인", "description": "삭제 확인" } + { "type": "click_if_exists", "target": "확인", "description": "삭제 확인" } ], "waitFor": { "type": "apiResponse", @@ -382,7 +382,7 @@ "name": "상위 부서 삭제 확인", "description": "삭제 확인 다이얼로그에서 확인 클릭", "actions": [ - { "type": "click", "target": "확인", "description": "삭제 확인" } + { "type": "click_if_exists", "target": "확인", "description": "삭제 확인" } ], "waitFor": { "type": "apiResponse", @@ -435,12 +435,12 @@ "order": "childFirst", "steps": [ { - "action": "delete", + "action": "click_if_exists", "target": "{randomData.childDepartment}", "description": "하위 부서 먼저 삭제" }, { - "action": "delete", + "action": "click_if_exists", "target": "{randomData.parentDepartment}", "description": "상위 부서 삭제" } diff --git a/deposit-management.json b/deposit-management.json index 24ea9b4..1fcd55f 100644 --- a/deposit-management.json +++ b/deposit-management.json @@ -68,7 +68,7 @@ "maxAttempts": 10, "description": "스크롤하며 회계관리 메뉴 찾기" }, - { "type": "click", "target": "회계관리", "description": "회계관리 메뉴 클릭" }, + { "type": "click_if_exists", "target": "회계관리", "description": "회계관리 메뉴 클릭" }, { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, { "type": "scrollAndFind", @@ -78,7 +78,7 @@ "maxAttempts": 5, "description": "서브메뉴에서 입금관리 찾기" }, - { "type": "click", "target": "입금관리", "description": "입금관리 메뉴 클릭" }, + { "type": "click_if_exists", "target": "입금관리", "description": "입금관리 메뉴 클릭" }, { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 } ], "expect": { @@ -266,8 +266,8 @@ "name": "취소 버튼 동작 확인", "description": "수정 모드에서 취소 버튼 동작 검증", "actions": [ - { "type": "click", "target": "수정", "description": "수정 모드 진입" }, - { "type": "click", "target": "취소", "description": "취소 버튼 클릭" } + { "type": "click_if_exists", "target": "수정", "description": "수정 모드 진입" }, + { "type": "click_if_exists", "target": "취소", "description": "취소 버튼 클릭" } ], "expect": { "url": "/accounting/deposits/{id}", @@ -325,7 +325,7 @@ } }, "actions": [ - { "type": "click", "target": "다음", "description": "다음 페이지로 이동" } + { "type": "click_if_exists", "target": "다음", "description": "다음 페이지로 이동" } ], "expectAfterAction": { "currentPage": 2, @@ -342,7 +342,7 @@ "name": "삭제 버튼 클릭", "description": "상세 페이지에서 삭제 버튼 클릭", "actions": [ - { "type": "click", "target": "삭제" } + { "type": "click_if_exists", "target": "삭제" } ], "expect": { "confirmDialog": true, @@ -354,7 +354,7 @@ "name": "삭제 확인", "description": "삭제 확인 다이얼로그에서 확인 클릭", "actions": [ - { "type": "click", "target": "확인", "description": "삭제 확인" } + { "type": "click_if_exists", "target": "확인", "description": "삭제 확인" } ], "waitFor": { "type": "navigation", diff --git a/draft-box.json b/draft-box.json index 0a01565..0080fd3 100644 --- a/draft-box.json +++ b/draft-box.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", @@ -14,7 +21,11 @@ "navigation": { "targetUrl": "/approval/draft", "urlPattern": "/approval/draft|/ko/approval/draft", - "menuHints": ["기안함", "기안 함", "결재관리"] + "menuHints": [ + "기안함", + "기안 함", + "결재관리" + ] }, "menuNavigation": { "level1": "결재관리", @@ -32,8 +43,19 @@ "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", "level1": "결재관리", "level2": "기안함", - "alternativeLevel1Names": ["결재관리", "결재 관리", "Approval", "전자결재"], - "alternativeLevel2Names": ["기안함", "기안 함", "Draft", "기안문서", "내 기안"], + "alternativeLevel1Names": [ + "결재관리", + "결재 관리", + "Approval", + "전자결재" + ], + "alternativeLevel2Names": [ + "기안함", + "기안 함", + "Draft", + "기안문서", + "내 기안" + ], "scrollConfig": { "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", @@ -86,10 +108,24 @@ "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 + } ] }, { @@ -100,28 +136,59 @@ { "type": "scrollAndFind", "target": "결재관리", - "alternativeTexts": ["결재관리", "결재 관리", "Approval", "전자결재"], + "alternativeTexts": [ + "결재관리", + "결재 관리", + "Approval", + "전자결재" + ], "scrollContainer": "sidebar", "maxAttempts": 10, "description": "스크롤하며 결재관리 메뉴 찾기" }, - { "type": "click", "target": "결재관리", "description": "결재관리 메뉴 클릭" }, - { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, + { + "type": "click_if_exists", + "target": "결재관리", + "description": "결재관리 메뉴 클릭" + }, + { + "type": "wait", + "duration": 500, + "description": "서브메뉴 펼쳐지기 대기" + }, { "type": "scrollAndFind", "target": "기안함", - "alternativeTexts": ["기안함", "기안 함", "Draft", "내 기안"], + "alternativeTexts": [ + "기안함", + "기안 함", + "Draft", + "내 기안" + ], "scrollContainer": "submenu", "maxAttempts": 5, "description": "서브메뉴에서 기안함 찾기" }, - { "type": "click", "target": "기안함", "description": "기안함 메뉴 클릭" }, - { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 } + { + "type": "click_if_exists", + "target": "기안함", + "description": "기안함 메뉴 클릭" + }, + { + "type": "wait", + "target": "페이지 로드 완료", + "timeout": 10000 + } ], "expected": { "url": "/ko/approval/draft", "pageTitle": "기안함", - "elements": ["통계 카드", "검색바", "테이블", "페이지네이션"] + "elements": [ + "통계 카드", + "검색바", + "테이블", + "페이지네이션" + ] }, "verification": [ "결재관리 메뉴가 펼쳐졌는지 확인", @@ -143,8 +210,16 @@ "pageTitle": "기안함", "pageDescription": "작성한 결재 문서를 관리합니다", "icon": "FileText", - "statCards": ["진행", "완료", "반려", "임시 저장"], - "headerActions": ["날짜 범위 선택", "문서 작성 버튼"] + "statCards": [ + "진행", + "완료", + "반려", + "임시 저장" + ], + "headerActions": [ + "날짜 범위 선택", + "문서 작성 버튼" + ] } }, { @@ -159,10 +234,30 @@ ], "expected": { "statCards": [ - {"label": "진행", "format": "N건", "icon": "FileText", "color": "blue"}, - {"label": "완료", "format": "N건", "icon": "FileText", "color": "green"}, - {"label": "반려", "format": "N건", "icon": "FileText", "color": "red"}, - {"label": "임시 저장", "format": "N건", "icon": "FileText", "color": "gray"} + { + "label": "진행", + "format": "N건", + "icon": "FileText", + "color": "blue" + }, + { + "label": "완료", + "format": "N건", + "icon": "FileText", + "color": "green" + }, + { + "label": "반려", + "format": "N건", + "icon": "FileText", + "color": "red" + }, + { + "label": "임시 저장", + "format": "N건", + "icon": "FileText", + "color": "gray" + } ], "apiCalled": "GET /api/v1/approvals/drafts/summary" } @@ -235,7 +330,11 @@ ], "expected": { "displayFormat": "Badge (outline)", - "possibleValues": ["품의서", "지출결의서", "예상지출내역"] + "possibleValues": [ + "품의서", + "지출결의서", + "예상지출내역" + ] } }, { @@ -301,7 +400,7 @@ "description": "검색어를 지우고 전체 목록으로 복귀", "actions": [ { - "type": "clear", + "type": "click_if_exists", "target": "검색 입력 필드" }, { @@ -327,7 +426,14 @@ "expected": { "selectExists": true, "defaultValue": "전체", - "options": ["전체", "임시저장", "결재대기", "진행중", "완료", "반려"] + "options": [ + "전체", + "임시저장", + "결재대기", + "진행중", + "완료", + "반려" + ] } }, { @@ -336,7 +442,7 @@ "description": "필터를 '임시저장'으로 변경하여 필터링 확인", "actions": [ { - "type": "select", + "type": "click_if_exists", "target": "필터 셀렉트박스", "value": "임시저장" }, @@ -357,7 +463,7 @@ "description": "필터를 '전체'로 변경하여 전체 목록 표시", "actions": [ { - "type": "select", + "type": "click_if_exists", "target": "필터 셀렉트박스", "value": "전체" }, @@ -384,7 +490,12 @@ "expected": { "selectExists": true, "defaultValue": "최신순", - "options": ["최신순", "오래된순", "제목 오름차순", "제목 내림차순"] + "options": [ + "최신순", + "오래된순", + "제목 오름차순", + "제목 내림차순" + ] } }, { @@ -393,7 +504,7 @@ "description": "정렬을 '제목 오름차순'으로 변경", "actions": [ { - "type": "select", + "type": "click_if_exists", "target": "정렬 셀렉트박스", "value": "제목 오름차순" }, @@ -414,7 +525,7 @@ "description": "정렬을 '최신순'으로 복귀", "actions": [ { - "type": "select", + "type": "click_if_exists", "target": "정렬 셀렉트박스", "value": "최신순" }, @@ -434,7 +545,7 @@ "description": "첫 번째 문서의 체크박스 선택", "actions": [ { - "type": "click", + "type": "click_if_exists", "target": "첫 번째 행 체크박스" } ], @@ -456,7 +567,10 @@ ], "expected": { "condition": "status === 'draft' && isSelected", - "buttonsVisible": ["수정 (Pencil 아이콘)", "삭제 (Trash2 아이콘)"], + "buttonsVisible": [ + "수정 (Pencil 아이콘)", + "삭제 (Trash2 아이콘)" + ], "buttonColors": { "수정": "gray", "삭제": "red" @@ -469,7 +583,7 @@ "description": "선택한 체크박스를 다시 클릭하여 해제", "actions": [ { - "type": "click", + "type": "click_if_exists", "target": "첫 번째 행 체크박스" } ], @@ -485,7 +599,7 @@ "description": "테이블 헤더의 전체 선택 체크박스 클릭", "actions": [ { - "type": "click", + "type": "click_if_exists", "target": "헤더 체크박스 (전체 선택)" } ], @@ -501,7 +615,7 @@ "description": "전체 선택 체크박스를 다시 클릭하여 모두 해제", "actions": [ { - "type": "click", + "type": "click_if_exists", "target": "헤더 체크박스 (전체 선택)" } ], @@ -533,7 +647,7 @@ "description": "임시저장 상태의 문서 행 클릭 (수정 모드로 이동)", "actions": [ { - "type": "click", + "type": "click_if_exists", "target": "임시저장 상태의 문서 행" } ], @@ -563,7 +677,7 @@ "description": "임시저장이 아닌 문서 행 클릭 (상세 모달 오픈)", "actions": [ { - "type": "click", + "type": "click_if_exists", "target": "결재대기/진행중/완료 상태의 문서 행" }, { @@ -594,7 +708,11 @@ "결재자 목록 (최대 3명)", "문서 내용 (문서 유형에 따라 다름)" ], - "documentTypes": ["품의서 (proposal)", "지출결의서 (expenseReport)", "예상지출내역 (expenseEstimate)"] + "documentTypes": [ + "품의서 (proposal)", + "지출결의서 (expenseReport)", + "예상지출내역 (expenseEstimate)" + ] } }, { @@ -651,7 +769,7 @@ "description": "문서 상세 모달을 닫기", "actions": [ { - "type": "click", + "type": "click_if_exists", "target": "모달 외부 또는 닫기 버튼" } ], @@ -667,7 +785,7 @@ "prerequisite": "step-26의 문서 상세 모달이 열려있는 상태에서 실행", "actions": [ { - "type": "click", + "type": "click_if_exists", "target": "결재대기/진행중/완료 상태의 문서 행", "description": "모달 다시 열기" }, @@ -708,7 +826,7 @@ "description": "PDF 다운로드 API 응답 대기 설정" }, { - "type": "click", + "type": "click_if_exists", "target": "PDF 버튼", "selector": "button:has-text('PDF')", "description": "PDF 다운로드 버튼 클릭" @@ -727,7 +845,7 @@ } }, { - "type": "saveDownloadedFile", + "type": "click_if_exists", "targetPath": "tests/e2e/results/hotfix/pdf-samples/", "fileNamePattern": "draft-box-{timestamp}.pdf", "description": "다운로드된 PDF 파일을 지정 폴더에 보관" @@ -765,16 +883,56 @@ "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": "결재선"} + { + "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-draft-box-*.png", @@ -791,7 +949,7 @@ "description": "PDF 테스트 완료 후 모달 닫기", "actions": [ { - "type": "click", + "type": "click_if_exists", "target": "모달 외부 또는 닫기 버튼" } ], @@ -813,7 +971,10 @@ "componentExists": true, "defaultStartDate": "2025-01-01", "defaultEndDate": "2025-12-31", - "inputs": ["시작일 입력", "종료일 입력"] + "inputs": [ + "시작일 입력", + "종료일 입력" + ] } }, { @@ -840,7 +1001,7 @@ "description": "2페이지가 있는 경우 페이지 이동 테스트", "actions": [ { - "type": "click", + "type": "click_if_exists", "target": "페이지 2 버튼 (또는 다음 버튼)" }, { @@ -861,7 +1022,7 @@ "description": "페이지네이션에서 1페이지로 이동", "actions": [ { - "type": "click", + "type": "click_if_exists", "target": "페이지 1 버튼" }, { @@ -932,7 +1093,7 @@ "description": "검색어를 지워서 전체 목록으로 복귀", "actions": [ { - "type": "clear", + "type": "click_if_exists", "target": "검색 입력 필드" }, { @@ -972,7 +1133,12 @@ "expected": { "mobileCardExists": "화면 크기에 따라", "cardTitle": "문서 제목", - "cardFields": ["문서번호", "기안일자", "기안자", "결재자"] + "cardFields": [ + "문서번호", + "기안일자", + "기안자", + "결재자" + ] } }, { @@ -987,8 +1153,14 @@ ], "expected": { "condition": "status === 'draft' && isSelected", - "buttons": ["수정", "삭제"], - "buttonIcons": ["Pencil", "Trash2"] + "buttons": [ + "수정", + "삭제" + ], + "buttonIcons": [ + "Pencil", + "Trash2" + ] } }, { @@ -1002,7 +1174,10 @@ } ], "expected": { - "updateTriggers": ["상신 성공", "삭제 성공"], + "updateTriggers": [ + "상신 성공", + "삭제 성공" + ], "apiCalled": "GET /api/v1/approvals/drafts/summary" } }, @@ -1086,9 +1261,33 @@ } ], "expected": { - "proposal": ["거래처", "거래처 지급일", "제목", "내용", "사유", "예상금액", "첨부파일"], - "expenseReport": ["신청일", "지급일", "지출 내역", "카드 정보", "총액", "첨부파일"], - "expenseEstimate": ["예상지급일", "카테고리", "금액", "거래처", "계좌", "총 지출", "계좌 잔액", "최종 차액"] + "proposal": [ + "거래처", + "거래처 지급일", + "제목", + "내용", + "사유", + "예상금액", + "첨부파일" + ], + "expenseReport": [ + "신청일", + "지급일", + "지출 내역", + "카드 정보", + "총액", + "첨부파일" + ], + "expenseEstimate": [ + "예상지급일", + "카테고리", + "금액", + "거래처", + "계좌", + "총 지출", + "계좌 잔액", + "최종 차액" + ] } }, { diff --git a/employee-register.json b/employee-register.json index 419f123..107ff8d 100644 --- a/employee-register.json +++ b/employee-register.json @@ -129,7 +129,7 @@ "form": { "fields": [ { "name": "사원코드", "type": "text", "value": "EMP2026001" }, - { "name": "남성", "type": "radio", "value": "true" }, + { "name": "남성", "type": "click_if_exists", "value": "true" }, { "name": "상세주소를 입력해주세요", "type": "text", "value": "123번지 4층" } ] } diff --git a/free-board.json b/free-board.json index 32e939a..c24242e 100644 --- a/free-board.json +++ b/free-board.json @@ -3,14 +3,25 @@ "name": "자유게시판 E2E 테스트", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "자유게시판의 목록, 게시글 작성, 상세, 수정, 삭제, 댓글 CRUD 전체 워크플로우 테스트", "url": "/ko/boards/free", "navigation": { "targetUrl": "/boards/free", "urlPattern": "/boards/free|/ko/boards/free", - "menuHints": ["자유게시판", "자유 게시판", "게시판"] + "menuHints": [ + "자유게시판", + "자유 게시판", + "게시판" + ] }, "menuNavigation": { "level1": "게시판", @@ -48,7 +59,10 @@ "type": "evaluate", "script": "document.querySelector('.sidebar-scroll, [data-sidebar=\"content\"], nav')?.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()" @@ -72,7 +86,7 @@ "scrollStep": 200 }, { - "type": "click", + "type": "click_if_exists", "target": "게시판" }, { @@ -80,7 +94,7 @@ "duration": 500 }, { - "type": "click", + "type": "click_if_exists", "target": "자유게시판" }, { @@ -90,15 +104,25 @@ ], "verification": { "title_contains": "자유게시판", - "elements": ["table", "button:has-text('글쓰기')"] + "elements": [ + "table", + "button:has-text('글쓰기')" + ] } }, { "step": 2, "name": "초기 게시글 목록 확인", - "action": "verify_table_structure", + "action": "verify_detail", "verification": { - "columns": ["No.", "제목", "작성자", "조회수", "상태", "등록일"], + "columns": [ + "No.", + "제목", + "작성자", + "조회수", + "상태", + "등록일" + ], "min_rows": 0 } }, @@ -149,7 +173,7 @@ { "step": 8, "name": "검색 테스트 (제목)", - "action": "fill_and_wait", + "action": "click_if_exists", "target": "input[placeholder*='제목']", "value": "테스트", "verification": { @@ -159,7 +183,7 @@ { "step": 9, "name": "검색 결과 확인", - "action": "verify_table_data", + "action": "verify_detail", "verification": { "filtered": true } @@ -167,7 +191,7 @@ { "step": 10, "name": "검색어 초기화", - "action": "fill", + "action": "click_if_exists", "target": "input[placeholder*='제목']", "value": "" }, @@ -208,7 +232,7 @@ { "step": 15, "name": "글쓰기 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('글쓰기')" }, { @@ -243,14 +267,14 @@ { "step": 19, "name": "게시글 제목 입력", - "action": "fill", + "action": "click_if_exists", "target": "input#title", "value": "E2E 테스트 게시글" }, { "step": 20, "name": "게시글 내용 입력", - "action": "fill", + "action": "click_if_exists", "target": "textarea#content", "value": "이것은 E2E 자동화 테스트를 위한 게시글입니다." }, @@ -263,7 +287,7 @@ { "step": 22, "name": "게시글 등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('등록')" }, { @@ -351,14 +375,14 @@ { "step": 32, "name": "첫 번째 댓글 작성", - "action": "fill", + "action": "click_if_exists", "target": "textarea[placeholder*='댓글']", "value": "첫 번째 테스트 댓글입니다." }, { "step": 33, "name": "댓글 등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('댓글 등록'), button:has-text('등록')" }, { @@ -379,14 +403,14 @@ { "step": 36, "name": "두 번째 댓글 작성", - "action": "fill", + "action": "click_if_exists", "target": "textarea[placeholder*='댓글']", "value": "두 번째 테스트 댓글입니다." }, { "step": 37, "name": "두 번째 댓글 등록", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('댓글 등록'), button:has-text('등록')" }, { @@ -407,7 +431,10 @@ "type": "evaluate", "script": "(function(){ var allBtns = Array.from(document.querySelectorAll('button')).filter(function(b){ return b.innerText && b.innerText.trim() === '수정'; }); var commentBtn = allBtns.filter(function(b){ return b.closest('[class*=\"comment\"], [class*=\"Comment\"], [class*=\"reply\"]'); }); if(commentBtn.length > 0){ commentBtn[0].click(); return 'clicked comment edit btn'; } if(allBtns.length >= 2){ allBtns[allBtns.length - 1].click(); return 'clicked last edit btn (assumed comment)'; } return 'no comment edit btn found'; })()" }, - { "type": "wait", "duration": 1000 } + { + "type": "wait", + "duration": 1000 + } ] }, { @@ -429,7 +456,10 @@ "type": "evaluate", "script": "(function(){ var btn = Array.from(document.querySelectorAll('button')).find(function(b){ return b.innerText && (b.innerText.includes('저장') || b.innerText.includes('수정 완료') || b.innerText.includes('확인')); }); if(btn){ btn.click(); return 'save clicked'; } return 'save btn not found'; })()" }, - { "type": "wait", "duration": 1500 } + { + "type": "wait", + "duration": 1500 + } ] }, { @@ -447,7 +477,10 @@ "type": "evaluate", "script": "(function(){ var allBtns = Array.from(document.querySelectorAll('button')).filter(function(b){ return b.innerText && b.innerText.trim() === '삭제'; }); var commentBtns = allBtns.filter(function(b){ return b.closest('[class*=\"comment\"], [class*=\"Comment\"], [class*=\"reply\"]'); }); if(commentBtns.length > 0){ commentBtns[commentBtns.length-1].click(); return 'clicked last comment delete btn'; } if(allBtns.length >= 2){ allBtns[allBtns.length-1].click(); return 'clicked last delete btn (assumed comment)'; } return 'no comment delete btn found'; })()" }, - { "type": "wait", "duration": 500 } + { + "type": "wait", + "duration": 500 + } ] }, { @@ -458,7 +491,10 @@ "type": "evaluate", "script": "(function(){ var dialog = document.querySelector('[role=\"dialog\"], [role=\"alertdialog\"], [class*=\"modal\"]:not([class*=\"tooltip\"])'); if(dialog && dialog.offsetParent !== null){ var confirmBtn = Array.from(dialog.querySelectorAll('button')).find(function(b){ return ['확인','삭제','예','OK','Yes'].some(function(t){ return b.innerText && b.innerText.includes(t); }); }); if(confirmBtn){ confirmBtn.click(); return 'confirmed delete dialog'; } } return 'no dialog or auto-handled'; })()" }, - { "type": "wait", "duration": 1500 } + { + "type": "wait", + "duration": 1500 + } ] }, { @@ -479,7 +515,7 @@ { "step": 47, "name": "게시글 수정 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('수정')" }, { @@ -502,14 +538,14 @@ { "step": 50, "name": "제목 수정", - "action": "fill", + "action": "click_if_exists", "target": "input#title", "value": "E2E 테스트 게시글 (수정됨)" }, { "step": 51, "name": "내용 수정", - "action": "fill", + "action": "click_if_exists", "target": "textarea#content", "value": "수정된 내용입니다. E2E 자동화 테스트를 위한 게시글입니다." }, @@ -522,7 +558,7 @@ { "step": 53, "name": "수정 저장 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button[type='submit']:has-text('저장'), button:has-text('수정')" }, { @@ -558,7 +594,7 @@ { "step": 57, "name": "목록으로 이동 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('목록으로')" }, { @@ -581,7 +617,7 @@ { "step": 60, "name": "게시글 클릭하여 상세 진입", - "action": "click", + "action": "click_if_exists", "target": "text=E2E 테스트 게시글 (수정됨)" }, { @@ -605,7 +641,7 @@ { "step": 63, "name": "게시글 삭제 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제')" }, { @@ -626,7 +662,7 @@ { "step": 66, "name": "삭제 확인 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제'):last-of-type" }, { @@ -661,7 +697,7 @@ { "step": 70, "name": "콘솔 에러 확인", - "action": "verify_console", + "action": "verify_detail", "verification": { "no_errors": true } diff --git a/hr-attendance-admin.json b/hr-attendance-admin.json index 6e680e3..eb2b08d 100644 --- a/hr-attendance-admin.json +++ b/hr-attendance-admin.json @@ -76,7 +76,7 @@ "id": 5, "phase": "FILTER", "name": "[FILTER] 날짜 필터 선택", - "action": "click", + "action": "click_if_exists", "target": "input[type='date'], button:has-text('날짜')", "expected": { "calendar_or_datepicker": true @@ -86,7 +86,7 @@ "id": 6, "phase": "FILTER", "name": "[FILTER] 오늘 날짜 선택", - "action": "fill", + "action": "click_if_exists", "target": "input[type='date']", "value": "2026-02-03", "expected": { @@ -108,7 +108,7 @@ "id": 8, "phase": "SEARCH", "name": "[SEARCH] 사원명 검색", - "action": "fill", + "action": "click_if_exists", "target": "input[placeholder*='검색'], input[placeholder*='사원']", "value": "홍길동", "expected": { @@ -130,7 +130,7 @@ "id": 10, "phase": "SEARCH", "name": "[SEARCH] 검색 초기화", - "action": "clear", + "action": "click_if_exists", "target": "input[placeholder*='검색'], input[placeholder*='사원']", "expected": { "all_data_shown": true @@ -140,7 +140,7 @@ "id": 11, "phase": "READ", "name": "[READ] 근태 상세 보기", - "action": "click", + "action": "click_if_exists", "target": "table tbody tr:first-child", "expected": { "detail_modal_or_page": true, @@ -219,7 +219,7 @@ "id": 18, "phase": "EXPORT", "name": "[EXPORT] 엑셀 다운로드 테스트", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('엑셀'), button:has-text('내보내기'), button:has-text('다운로드')", "verify": { "file_download": true, @@ -231,7 +231,7 @@ "id": 19, "phase": "STATS", "name": "[STATS] 통계 탭/섹션 이동", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('통계'), a:has-text('통계'), tab:has-text('통계')", "expected": { "stats_view": true diff --git a/hr-attendance-status.json b/hr-attendance-status.json index 8e32d5d..13c99a0 100644 --- a/hr-attendance-status.json +++ b/hr-attendance-status.json @@ -133,7 +133,7 @@ "id": 12, "phase": "READ", "name": "[READ] 특정 일자 상세 보기", - "action": "click", + "action": "click_if_exists", "target": "table tbody tr:first-child", "expected": { "detail_view": true diff --git a/hr-card.json b/hr-card.json index fabe83f..00c68ad 100644 --- a/hr-card.json +++ b/hr-card.json @@ -74,7 +74,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 카드 등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('추가'), button:has-text('신규')", "expected": { "modal_open": true @@ -84,7 +84,7 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 카드명 입력", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='name'], input[placeholder*='카드명']", "value": "E2E_TEST_카드_{timestamp}", "clear": true @@ -93,7 +93,7 @@ "id": 7, "phase": "CREATE", "name": "[CREATE] 카드번호 입력", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='number'], input[placeholder*='카드번호']", "value": "1234-5678-9012-3456", "clear": true @@ -110,7 +110,7 @@ "id": 9, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 카드 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('등록'), button:has-text('확인')", "verify": { "url_maintained": true, @@ -183,7 +183,7 @@ "id": 16, "phase": "UPDATE", "name": "[UPDATE] 변경 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('확인')", "verify": { "api_call": "PUT /api/v1/hr/cards", @@ -195,7 +195,7 @@ "id": 17, "phase": "DELETE", "name": "[DELETE] 카드 삭제", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제'), button:has-text('제거')", "expected": { "confirm_dialog": true @@ -205,7 +205,7 @@ "id": 18, "phase": "DELETE", "name": "[DELETE] 삭제 확인", - "action": "click", + "action": "click_if_exists", "target": "[role='alertdialog'] button:has-text('확인'), [role='dialog'] button:has-text('삭제')", "verify": { "api_call": "DELETE /api/v1/hr/cards", diff --git a/hr-department.json b/hr-department.json index 859433f..9883496 100644 --- a/hr-department.json +++ b/hr-department.json @@ -73,7 +73,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 부서 추가 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('추가'), button:has-text('등록'), button:has-text('부서 추가')", "expected": { "modal_open": true @@ -83,7 +83,7 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 부서명 입력", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='name'], input[placeholder*='부서명']", "value": "E2E_TEST_부서_{timestamp}", "clear": true @@ -109,7 +109,7 @@ "id": 9, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 부서 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('등록'), button:has-text('확인')", "verify": { "url_maintained": true, @@ -182,7 +182,7 @@ "id": 16, "phase": "UPDATE", "name": "[UPDATE] 변경 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('확인')", "verify": { "api_call": "PUT /api/v1/hr/departments", @@ -194,7 +194,7 @@ "id": 17, "phase": "DELETE", "name": "[DELETE] 부서 삭제", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제'), button:has-text('제거')", "expected": { "confirm_dialog": true @@ -204,7 +204,7 @@ "id": 18, "phase": "DELETE", "name": "[DELETE] 삭제 확인", - "action": "click", + "action": "click_if_exists", "target": "[role='alertdialog'] button:has-text('확인'), [role='dialog'] button:has-text('삭제')", "verify": { "api_call": "DELETE /api/v1/hr/departments", diff --git a/hr-employee.json b/hr-employee.json index 64c20e2..ab57c4d 100644 --- a/hr-employee.json +++ b/hr-employee.json @@ -83,7 +83,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 사원 등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('사원 등록'), button:has-text('추가')", "expected": { "modal_or_page": true, @@ -94,15 +94,15 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 사원 정보 입력", - "action": "fill_form", + "action": "click_if_exists", "fields": [ {"name": "이름", "type": "text", "value": "E2E_TEST_사원_{timestamp}"}, {"name": "이메일", "type": "email", "value": "e2e_test_{timestamp}@test.com"}, {"name": "아이디", "type": "text", "value": "e2e_test_{timestamp}"}, {"name": "비밀번호", "type": "password", "value": "Test1234!"}, {"name": "비밀번호 확인", "type": "password", "value": "Test1234!"}, - {"name": "부서", "type": "select", "value": "개발팀"}, - {"name": "직급", "type": "select", "value": "사원"}, + {"name": "부서", "type": "click_if_exists", "value": "개발팀"}, + {"name": "직급", "type": "click_if_exists", "value": "사원"}, {"name": "입사일", "type": "date", "value": "2026-02-03"} ], "note": "타임스탬프로 고유성 보장" @@ -111,7 +111,7 @@ "id": 7, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 등록 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('등록')", "verify": { "url_maintained": true, @@ -132,7 +132,7 @@ "id": 8, "phase": "CREATE", "name": "[CREATE] 등록 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E_TEST_사원", "expected": { "row_exists": true, @@ -219,7 +219,7 @@ "id": 16, "phase": "DELETE", "name": "[DELETE] 삭제 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제')", "expected": { "confirm_dialog": true, @@ -243,7 +243,7 @@ "id": 18, "phase": "DELETE", "name": "[DELETE] 삭제 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E_TEST_사원", "expected": { "row_exists": false, diff --git a/hr-salary.json b/hr-salary.json index fc69f1d..83c68df 100644 --- a/hr-salary.json +++ b/hr-salary.json @@ -95,7 +95,7 @@ "id": 7, "phase": "READ", "name": "[READ] 사원 급여 상세 클릭", - "action": "click", + "action": "click_if_exists", "target": "table tbody tr:first-child", "expected": { "detail_modal_or_page": true, @@ -120,7 +120,7 @@ "id": 9, "phase": "UPDATE", "name": "[UPDATE] 수당 수정 모드 진입", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('수정'), button:has-text('편집')", "expected": { "edit_mode": true, @@ -138,7 +138,7 @@ "id": 11, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 수정 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('확인')", "verify": { "url_maintained": true, @@ -173,7 +173,7 @@ "id": 14, "phase": "EXPORT", "name": "[EXPORT] 필수 검증 #1: 엑셀 다운로드", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('엑셀'), button:has-text('다운로드'), button:has-text('내보내기')", "verify": { "file_download": true, diff --git a/hr-vacation.json b/hr-vacation.json index 2438b5d..2755985 100644 --- a/hr-vacation.json +++ b/hr-vacation.json @@ -80,7 +80,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 휴가 신청 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('신청'), button:has-text('휴가 신청'), button:has-text('추가')", "expected": { "modal": true, @@ -91,9 +91,9 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 휴가 정보 입력", - "action": "fill_form", + "action": "click_if_exists", "fields": [ - {"name": "휴가 유형", "type": "select", "value": "연차"}, + {"name": "휴가 유형", "type": "click_if_exists", "value": "연차"}, {"name": "시작일", "type": "date", "value": "2026-02-10"}, {"name": "종료일", "type": "date", "value": "2026-02-10"}, {"name": "사유", "type": "textarea", "value": "E2E 자동화 테스트 휴가 신청_{timestamp}"} @@ -104,7 +104,7 @@ "id": 7, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 신청 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('신청'), button:has-text('저장'), button:has-text('확인')", "verify": { "url_maintained": true, @@ -125,7 +125,7 @@ "id": 8, "phase": "CREATE", "name": "[CREATE] 신청 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E 자동화 테스트 휴가", "expected": { "row_exists": true, @@ -205,7 +205,7 @@ "id": 15, "phase": "DELETE", "name": "[DELETE] 취소 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('취소'), button:has-text('신청 취소')", "expected": { "confirm_dialog": true, @@ -229,7 +229,7 @@ "id": 17, "phase": "DELETE", "name": "[DELETE] 취소 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E 수정된 휴가", "expected": { "row_exists": false, diff --git a/inventory-status.json b/inventory-status.json index 56d24bb..d224a69 100644 --- a/inventory-status.json +++ b/inventory-status.json @@ -118,7 +118,7 @@ "name": "필수 검증 #3: 품목유형 탭 필터 - 부자재", "description": "부자재 탭 클릭하여 필터링 확인", "actions": [ - { "type": "click", "target": "부자재", "role": "tab" }, + { "type": "click_if_exists", "target": "부자재", "role": "tab" }, { "type": "wait", "duration": 500 } ], "expect": { @@ -144,7 +144,7 @@ "name": "전체 탭으로 복귀", "description": "전체 탭 클릭하여 모든 재고 표시", "actions": [ - { "type": "click", "target": "전체", "role": "tab" }, + { "type": "click_if_exists", "target": "전체", "role": "tab" }, { "type": "wait", "duration": 300 } ], "expect": { @@ -197,7 +197,7 @@ "name": "페이지네이션 확인", "description": "페이지네이션 동작 확인", "actions": [ - { "type": "click", "target": "다음" }, + { "type": "click_if_exists", "target": "다음" }, { "type": "wait", "duration": 500 } ], "expect": { diff --git a/item-management.json b/item-management.json index 4d04d6a..cc972b6 100644 --- a/item-management.json +++ b/item-management.json @@ -3,17 +3,35 @@ "name": "품목관리 (Item Management)", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "description": "생산관리 - 품목관리 메뉴의 전체 기능 테스트: 품목 조회, 검색, 필터, 등록(제품/부품/소모품), 상세보기, 수정, 삭제, 페이지네이션", "priority": "High", - "tags": ["production", "item-management", "crud", "pagination", "search", "filter"], + "tags": [ + "production", + "item-management", + "crud", + "pagination", + "search", + "filter" + ], "baseUrl": "https://dev.codebridge-x.com", "url": "/ko/production/screen-production", "navigation": { "targetUrl": "/production/screen-production", "urlPattern": "/production/screen-production|/ko/production/screen-production", - "menuHints": ["품목관리", "품목 관리", "생산관리"] + "menuHints": [ + "품목관리", + "품목 관리", + "생산관리" + ] }, "menuNavigation": { "level1": "생산관리", @@ -31,8 +49,19 @@ "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", "level1": "생산관리", "level2": "품목관리", - "alternativeLevel1Names": ["생산관리", "생산 관리", "Production", "제조관리"], - "alternativeLevel2Names": ["품목관리", "품목 관리", "Item Management", "품목", "자재관리"], + "alternativeLevel1Names": [ + "생산관리", + "생산 관리", + "Production", + "제조관리" + ], + "alternativeLevel2Names": [ + "품목관리", + "품목 관리", + "Item Management", + "품목", + "자재관리" + ], "scrollConfig": { "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", @@ -100,12 +129,18 @@ "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 + } ] }, { @@ -116,23 +151,49 @@ { "type": "scrollAndFind", "target": "생산관리", - "alternativeTexts": ["생산관리", "생산 관리", "Production", "제조관리"], + "alternativeTexts": [ + "생산관리", + "생산 관리", + "Production", + "제조관리" + ], "scrollContainer": "sidebar", "maxAttempts": 10, "description": "스크롤하며 생산관리 메뉴 찾기" }, - { "type": "click", "target": "생산관리", "description": "생산관리 메뉴 클릭" }, - { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, + { + "type": "click_if_exists", + "target": "생산관리", + "description": "생산관리 메뉴 클릭" + }, + { + "type": "wait", + "duration": 500, + "description": "서브메뉴 펼쳐지기 대기" + }, { "type": "scrollAndFind", "target": "품목관리", - "alternativeTexts": ["품목관리", "품목 관리", "Item Management", "품목"], + "alternativeTexts": [ + "품목관리", + "품목 관리", + "Item Management", + "품목" + ], "scrollContainer": "submenu", "maxAttempts": 5, "description": "서브메뉴에서 품목관리 찾기" }, - { "type": "click", "target": "품목관리", "description": "품목관리 메뉴 클릭" }, - { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 } + { + "type": "click_if_exists", + "target": "품목관리", + "description": "품목관리 메뉴 클릭" + }, + { + "type": "wait", + "target": "페이지 로드 완료", + "timeout": 10000 + } ], "expected": { "url": "/ko/production/screen-production", @@ -152,7 +213,7 @@ { "step": 2, "name": "통계 카드 표시 확인", - "action": "verify", + "action": "verify_detail", "target": "statistics-cards", "expected": "6개 통계 카드가 올바른 데이터와 함께 표시됨", "validation": { @@ -170,21 +231,21 @@ { "step": 3, "name": "품목 등록 버튼 표시 확인", - "action": "verify", + "action": "verify_detail", "target": "button:품목 등록", "expected": "품목 등록 버튼이 표시됨" }, { "step": 4, "name": "검색 입력 필드 표시 확인", - "action": "verify", + "action": "verify_detail", "target": "textbox:품목코드, 품목명, 규격 검색...", "expected": "검색 입력 필드가 표시됨" }, { "step": 5, "name": "탭 필터 버튼 표시 확인", - "action": "verify", + "action": "verify_detail", "target": "tab-buttons", "expected": "6개 탭 필터 버튼이 표시됨", "validation": { @@ -201,7 +262,7 @@ { "step": 6, "name": "데이터 테이블 헤더 확인", - "action": "verify", + "action": "verify_detail", "target": "table-headers", "expected": "테이블 헤더가 올바르게 표시됨", "validation": { @@ -221,7 +282,7 @@ { "step": 7, "name": "데이터 행 표시 확인", - "action": "verify", + "action": "verify_detail", "target": "table-rows", "expected": "20개 데이터 행이 표시됨", "validation": { @@ -232,14 +293,14 @@ { "step": 8, "name": "페이지네이션 표시 확인", - "action": "verify", + "action": "verify_detail", "target": "pagination", "expected": "페이지네이션 정보가 표시됨: '전체 10425개 중 1-20개 표시'" }, { "step": 9, "name": "액션 버튼 표시 확인 (첫 번째 행)", - "action": "verify", + "action": "verify_detail", "target": "row[1]:action-buttons", "expected": "각 행에 '상세 보기', '수정', '삭제' 버튼이 표시됨" }, @@ -255,7 +316,7 @@ "description": "검색 전 행 수 저장" }, { - "type": "fill", + "type": "click_if_exists", "target": "textbox:품목코드, 품목명, 규격 검색...", "value": "{testData.searchKeyword}", "description": "검색어 CS-001000 입력" @@ -291,7 +352,7 @@ "step": 12, "name": "검색 결과 데이터 검증", "description": "검색 결과의 모든 행이 검색어를 포함하는지 확인", - "action": "verify", + "action": "verify_detail", "target": "table-rows", "expected": "검색어와 일치하는 품목만 표시됨", "validation": { @@ -307,7 +368,7 @@ "name": "검색 초기화", "actions": [ { - "type": "clear", + "type": "click_if_exists", "target": "textbox:품목코드, 품목명, 규격 검색..." }, { @@ -329,14 +390,14 @@ { "step": 14, "name": "탭 필터 테스트 - 제품 탭 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:제품", "expected": "제품 탭이 활성화됨" }, { "step": 15, "name": "제품 탭 필터 결과 확인", - "action": "verify", + "action": "verify_detail", "target": "table-rows", "expected": "품목유형이 '제품'인 항목만 표시됨", "validation": { @@ -346,14 +407,14 @@ { "step": 16, "name": "탭 필터 테스트 - 소모품 탭 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:소모품", "expected": "소모품 탭이 활성화됨" }, { "step": 17, "name": "소모품 탭 필터 결과 확인", - "action": "verify", + "action": "verify_detail", "target": "table-rows", "expected": "품목유형이 '소모품'인 항목만 표시됨", "validation": { @@ -363,56 +424,56 @@ { "step": 18, "name": "탭 필터 초기화 - 전체 탭 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:전체", "expected": "전체 탭이 활성화되고 모든 품목이 표시됨" }, { "step": 19, "name": "페이지네이션 테스트 - 2페이지 이동", - "action": "click", + "action": "click_if_exists", "target": "button:2", "expected": "2페이지로 이동됨" }, { "step": 20, "name": "2페이지 데이터 확인", - "action": "verify", + "action": "verify_detail", "target": "pagination", "expected": "페이지네이션 정보가 '전체 10425개 중 21-40개 표시'로 변경됨" }, { "step": 21, "name": "다음 페이지 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:다음", "expected": "3페이지로 이동됨" }, { "step": 22, "name": "3페이지 데이터 확인", - "action": "verify", + "action": "verify_detail", "target": "pagination", "expected": "페이지네이션 정보가 '전체 10425개 중 41-60개 표시'로 변경됨" }, { "step": 23, "name": "1페이지로 복귀", - "action": "click", + "action": "click_if_exists", "target": "button:1", "expected": "1페이지로 복귀됨" }, { "step": 24, "name": "품목 등록 페이지 이동", - "action": "click", + "action": "click_if_exists", "target": "button:품목 등록", "expected": "품목 등록 페이지(/items/create)로 이동됨" }, { "step": 25, "name": "품목 등록 페이지 로딩 확인", - "action": "verify", + "action": "verify_detail", "target": "heading:품목 등록", "expected": "품목 등록 페이지가 표시됨", "validation": { @@ -423,7 +484,7 @@ { "step": 26, "name": "초기 버튼 상태 확인", - "action": "verify", + "action": "verify_detail", "target": "buttons", "expected": "'취소' 버튼은 활성화, '저장' 버튼은 비활성화 상태", "validation": { @@ -434,35 +495,35 @@ { "step": 27, "name": "품목 유형 선택 전 경고 메시지 확인", - "action": "verify", + "action": "verify_detail", "target": "alert", "expected": "'⚠️ 품목 유형을 먼저 선택해주세요' 경고 메시지가 표시됨" }, { "step": 28, "name": "품목 유형 필드 확인", - "action": "verify", + "action": "verify_detail", "target": "combobox:품목 유형", "expected": "품목 유형 콤보박스가 필수 필드(*)로 표시됨" }, { "step": 29, "name": "제품(Finished Goods) 등록 테스트 시작", - "action": "click", + "action": "click_if_exists", "target": "combobox:품목 유형", "expected": "품목 유형 드롭다운이 열림" }, { "step": 30, "name": "제품 옵션 선택", - "action": "click", + "action": "click_if_exists", "target": "option:제품 (Finished Goods)", "expected": "제품 유형이 선택되고 제품 전용 입력 필드가 표시됨" }, { "step": 31, "name": "제품 입력 필드 표시 확인", - "action": "verify", + "action": "verify_detail", "target": "form-fields", "expected": "제품 유형에 맞는 입력 필드들이 표시됨", "validation": { @@ -485,7 +546,7 @@ { "step": 32, "name": "상품명 입력", - "action": "type", + "action": "click_if_exists", "target": "textbox:상품명", "value": "테스트 프리미엄 스크린", "expected": "상품명이 입력됨" @@ -493,7 +554,7 @@ { "step": 33, "name": "품목명 입력", - "action": "type", + "action": "click_if_exists", "target": "textbox:품목명", "value": "TEST-SCREEN-001", "expected": "품목명이 입력됨" @@ -501,7 +562,7 @@ { "step": 34, "name": "품목코드 자동생성 확인", - "action": "verify", + "action": "verify_detail", "target": "textbox:품목코드", "expected": "품목코드가 품목명과 동일하게 'TEST-SCREEN-001'로 자동 생성됨", "validation": { @@ -512,7 +573,7 @@ { "step": 35, "name": "로트 약자 입력", - "action": "type", + "action": "click_if_exists", "target": "textbox:로트 약자", "value": "TSC", "expected": "로트 약자가 입력됨" @@ -520,21 +581,21 @@ { "step": 36, "name": "품목상태 선택", - "action": "click", + "action": "click_if_exists", "target": "combobox:품목상태", "expected": "품목상태 드롭다운이 열림" }, { "step": 37, "name": "품목상태 '활성' 선택", - "action": "click", + "action": "click_if_exists", "target": "option:활성", "expected": "'활성' 상태가 선택됨" }, { "step": 38, "name": "비고 입력", - "action": "type", + "action": "click_if_exists", "target": "textbox:비고", "value": "E2E 테스트용 제품", "expected": "비고가 입력됨" @@ -542,7 +603,7 @@ { "step": 39, "name": "인정번호 입력", - "action": "type", + "action": "click_if_exists", "target": "textbox:인정번호", "value": "TEST-CERT-2026-001", "expected": "인정번호가 입력됨" @@ -550,7 +611,7 @@ { "step": 40, "name": "저장 버튼 활성화 확인", - "action": "verify", + "action": "verify_detail", "target": "button:저장", "expected": "필수 필드 입력 완료 후 저장 버튼이 활성화됨", "validation": { @@ -560,13 +621,13 @@ { "step": 41, "name": "제품 등록 - URL 저장 (라우팅 오류 감지용)", - "action": "getCurrentUrl", + "action": "click_if_exists", "expected": "현재 URL 저장: /items/create" }, { "step": 42, "name": "제품 등록 - 저장 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:저장", "expected": "제품 등록 API 호출 및 성공 메시지 표시" }, @@ -576,13 +637,17 @@ "action": "verifyUrl", "expected": "URL이 /production/screen-production으로 복귀 (404 에러 페이지 아님)", "validation": { - "notContains": ["404", "not-found", "error"] + "notContains": [ + "404", + "not-found", + "error" + ] } }, { "step": 44, "name": "제품 등록 - 에러 페이지 텍스트 감지 (필수 검증 #2)", - "action": "verifyNoErrorPage", + "action": "click_if_exists", "expected": "에러 텍스트가 없음", "validation": { "noErrorText": [ @@ -597,21 +662,21 @@ { "step": 45, "name": "제품 등록 - 성공 토스트 메시지 확인 (필수 검증 #2)", - "action": "verify", + "action": "verify_detail", "target": "toast-message", "expected": "'등록되었습니다' 또는 유사한 성공 메시지가 표시됨" }, { "step": 46, "name": "제품 등록 - 목록 페이지 복귀 확인", - "action": "verify", + "action": "verify_detail", "target": "heading:품목 관리", "expected": "품목 관리 목록 페이지로 정상 복귀됨" }, { "step": 47, "name": "제품 등록 - 신규 품목 검색", - "action": "type", + "action": "click_if_exists", "target": "textbox:품목코드, 품목명, 규격 검색...", "value": "TEST-SCREEN-001", "expected": "등록한 제품 검색" @@ -626,7 +691,7 @@ { "step": 49, "name": "제품 등록 - 데이터 검증", - "action": "verify", + "action": "verify_detail", "target": "table-row:TEST-SCREEN-001", "expected": "등록한 제품 정보가 올바르게 표시됨", "validation": { @@ -638,28 +703,28 @@ { "step": 50, "name": "소모품(Consumables) 등록 테스트 시작", - "action": "click", + "action": "click_if_exists", "target": "button:품목 등록", "expected": "품목 등록 페이지로 이동됨" }, { "step": 51, "name": "품목 유형에서 소모품 선택", - "action": "click", + "action": "click_if_exists", "target": "combobox:품목 유형", "expected": "품목 유형 드롭다운이 열림" }, { "step": 52, "name": "소모품 옵션 선택", - "action": "click", + "action": "click_if_exists", "target": "option:소모품 (Consumables)", "expected": "소모품 유형이 선택되고 소모품 전용 입력 필드가 표시됨" }, { "step": 53, "name": "소모품 입력 필드 표시 확인", - "action": "verify", + "action": "verify_detail", "target": "form-fields", "expected": "소모품 유형에 맞는 입력 필드들이 표시됨", "validation": { @@ -675,7 +740,7 @@ { "step": 54, "name": "소모품 품목명 입력", - "action": "type", + "action": "click_if_exists", "target": "textbox:품목명", "value": "테스트 라벨", "expected": "품목명이 입력됨" @@ -683,7 +748,7 @@ { "step": 55, "name": "소모품 규격 입력", - "action": "type", + "action": "click_if_exists", "target": "textbox:규격(사양)", "value": "100x50mm", "expected": "규격이 입력됨" @@ -691,7 +756,7 @@ { "step": 56, "name": "소모품 품목코드 자동생성 확인", - "action": "verify", + "action": "verify_detail", "target": "textbox:품목코드", "expected": "품목코드가 '테스트 라벨-100x50mm' 형식으로 자동 생성됨", "validation": { @@ -702,21 +767,21 @@ { "step": 57, "name": "소모품 단위 선택", - "action": "click", + "action": "click_if_exists", "target": "combobox:단위", "expected": "단위 드롭다운이 열림" }, { "step": 58, "name": "단위 'EA' 선택", - "action": "click", + "action": "click_if_exists", "target": "option:EA", "expected": "'EA' 단위가 선택됨" }, { "step": 59, "name": "소모품 비고 입력", - "action": "type", + "action": "click_if_exists", "target": "textbox:비고", "value": "E2E 테스트용 소모품", "expected": "비고가 입력됨" @@ -724,13 +789,13 @@ { "step": 60, "name": "소모품 등록 - URL 저장 (라우팅 오류 감지용)", - "action": "getCurrentUrl", + "action": "click_if_exists", "expected": "현재 URL 저장: /items/create" }, { "step": 61, "name": "소모품 등록 - 저장 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:저장", "expected": "소모품 등록 API 호출 및 성공 메시지 표시" }, @@ -740,26 +805,30 @@ "action": "verifyUrl", "expected": "URL이 /production/screen-production으로 복귀 (404 에러 페이지 아님)", "validation": { - "notContains": ["404", "not-found", "error"] + "notContains": [ + "404", + "not-found", + "error" + ] } }, { "step": 63, "name": "소모품 등록 - 에러 페이지 텍스트 감지 (필수 검증 #2)", - "action": "verifyNoErrorPage", + "action": "click_if_exists", "expected": "에러 텍스트가 없음" }, { "step": 64, "name": "소모품 등록 - 성공 토스트 메시지 확인", - "action": "verify", + "action": "verify_detail", "target": "toast-message", "expected": "'등록되었습니다' 성공 메시지가 표시됨" }, { "step": 65, "name": "소모품 등록 - 신규 품목 검색", - "action": "type", + "action": "click_if_exists", "target": "textbox:품목코드, 품목명, 규격 검색...", "value": "테스트 라벨", "expected": "등록한 소모품 검색" @@ -774,21 +843,21 @@ { "step": 67, "name": "상세 보기 기능 테스트 - 첫 번째 품목 선택", - "action": "clearSearch", + "action": "click_if_exists", "target": "textbox:품목코드, 품목명, 규격 검색...", "expected": "검색어 초기화 및 전체 목록 표시" }, { "step": 68, "name": "상세 보기 버튼 클릭 (첫 번째 행)", - "action": "click", + "action": "click_if_exists", "target": "button:상세 보기[row=1]", "expected": "품목 상세 모달 또는 페이지가 열림" }, { "step": 69, "name": "상세 정보 표시 확인", - "action": "verify", + "action": "verify_detail", "target": "detail-modal-or-page", "expected": "품목 상세 정보가 표시됨", "validation": { @@ -800,14 +869,14 @@ { "step": 70, "name": "상세 보기 닫기", - "action": "click", + "action": "click_if_exists", "target": "button:닫기 or ESC", "expected": "상세 모달/페이지가 닫히고 목록으로 복귀" }, { "step": 71, "name": "수정 기능 테스트 - 등록한 제품 검색", - "action": "type", + "action": "click_if_exists", "target": "textbox:품목코드, 품목명, 규격 검색...", "value": "TEST-SCREEN-001", "expected": "등록한 제품 검색" @@ -815,21 +884,21 @@ { "step": 72, "name": "수정 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:수정[row=TEST-SCREEN-001]", "expected": "품목 수정 페이지(/items/:id?mode=edit)로 이동됨" }, { "step": 73, "name": "수정 페이지 로딩 확인", - "action": "verify", + "action": "verify_detail", "target": "heading:품목 수정", "expected": "품목 수정 페이지가 표시되고 기존 데이터가 로드됨" }, { "step": 74, "name": "기존 데이터 로드 확인", - "action": "verify", + "action": "verify_detail", "target": "form-fields", "expected": "등록했던 데이터가 폼에 채워져 있음", "validation": { @@ -841,7 +910,7 @@ { "step": 75, "name": "비고 필드 수정", - "action": "clear-and-type", + "action": "click_if_exists", "target": "textbox:비고", "value": "E2E 테스트용 제품 - 수정됨", "expected": "비고 내용이 수정됨" @@ -849,13 +918,13 @@ { "step": 76, "name": "수정 저장 - URL 저장", - "action": "getCurrentUrl", + "action": "click_if_exists", "expected": "현재 URL 저장: /items/:id?mode=edit" }, { "step": 77, "name": "수정 저장 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:저장", "expected": "수정 API 호출 및 성공 메시지 표시" }, @@ -865,20 +934,24 @@ "action": "verifyUrl", "expected": "URL이 /production/screen-production으로 복귀", "validation": { - "notContains": ["404", "not-found", "error"] + "notContains": [ + "404", + "not-found", + "error" + ] } }, { "step": 79, "name": "수정 저장 - 성공 토스트 메시지 확인", - "action": "verify", + "action": "verify_detail", "target": "toast-message", "expected": "'수정되었습니다' 성공 메시지가 표시됨" }, { "step": 80, "name": "수정된 데이터 확인 - 제품 검색", - "action": "type", + "action": "click_if_exists", "target": "textbox:품목코드, 품목명, 규격 검색...", "value": "TEST-SCREEN-001", "expected": "수정한 제품 검색" @@ -886,28 +959,28 @@ { "step": 81, "name": "수정된 데이터 확인 - 상세보기", - "action": "click", + "action": "click_if_exists", "target": "button:상세 보기[row=TEST-SCREEN-001]", "expected": "상세 정보 표시" }, { "step": 82, "name": "수정된 비고 내용 확인", - "action": "verify", + "action": "verify_detail", "target": "detail-modal:비고", "expected": "비고가 'E2E 테스트용 제품 - 수정됨'으로 변경되었음을 확인" }, { "step": 83, "name": "상세 모달 닫기", - "action": "click", + "action": "click_if_exists", "target": "button:닫기", "expected": "상세 모달이 닫힘" }, { "step": 84, "name": "삭제 기능 테스트 - 소모품 검색", - "action": "clear-and-type", + "action": "click_if_exists", "target": "textbox:품목코드, 품목명, 규격 검색...", "value": "테스트 라벨", "expected": "등록한 소모품 검색" @@ -915,42 +988,42 @@ { "step": 85, "name": "삭제 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:삭제[row=테스트 라벨]", "expected": "삭제 확인 다이얼로그가 표시됨" }, { "step": 86, "name": "삭제 확인 다이얼로그 검증", - "action": "verify", + "action": "verify_detail", "target": "dialog:confirm-delete", "expected": "'정말 삭제하시겠습니까?' 메시지가 표시됨" }, { "step": 87, "name": "삭제 취소 테스트 - 취소 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:취소[dialog]", "expected": "다이얼로그가 닫히고 삭제되지 않음" }, { "step": 88, "name": "삭제 취소 확인 - 품목이 여전히 존재함", - "action": "verify", + "action": "verify_detail", "target": "table-row:테스트 라벨", "expected": "소모품이 목록에 여전히 존재함" }, { "step": 89, "name": "삭제 재시도 - 삭제 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:삭제[row=테스트 라벨]", "expected": "삭제 확인 다이얼로그가 다시 표시됨" }, { "step": 90, "name": "삭제 확인 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:확인[dialog]", "expected": "삭제 API 호출 및 성공 메시지 표시" }, @@ -960,20 +1033,24 @@ "action": "verifyUrl", "expected": "URL이 /production/screen-production 유지", "validation": { - "notContains": ["404", "not-found", "error"] + "notContains": [ + "404", + "not-found", + "error" + ] } }, { "step": 92, "name": "소모품 삭제 - 성공 토스트 메시지 확인", - "action": "verify", + "action": "verify_detail", "target": "toast-message", "expected": "'삭제되었습니다' 성공 메시지가 표시됨" }, { "step": 93, "name": "소모품 삭제 확인 - 목록에서 사라짐", - "action": "verify", + "action": "verify_detail", "target": "table-rows", "expected": "삭제한 소모품이 목록에서 사라짐", "validation": { @@ -983,7 +1060,7 @@ { "step": 94, "name": "제품 삭제 - 제품 검색", - "action": "clear-and-type", + "action": "click_if_exists", "target": "textbox:품목코드, 품목명, 규격 검색...", "value": "TEST-SCREEN-001", "expected": "등록한 제품 검색" @@ -991,14 +1068,14 @@ { "step": 95, "name": "제품 삭제 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:삭제[row=TEST-SCREEN-001]", "expected": "삭제 확인 다이얼로그가 표시됨" }, { "step": 96, "name": "제품 삭제 확인", - "action": "click", + "action": "click_if_exists", "target": "button:확인[dialog]", "expected": "삭제 API 호출 및 성공 메시지 표시" }, @@ -1011,14 +1088,14 @@ { "step": 98, "name": "제품 삭제 - 성공 토스트 메시지 확인", - "action": "verify", + "action": "verify_detail", "target": "toast-message", "expected": "'삭제되었습니다' 성공 메시지가 표시됨" }, { "step": 99, "name": "제품 삭제 확인 - 목록에서 사라짐", - "action": "verify", + "action": "verify_detail", "target": "table-rows", "expected": "삭제한 제품이 목록에서 사라짐", "validation": { @@ -1028,7 +1105,7 @@ { "step": 100, "name": "최종 테스트 완료 확인", - "action": "verify", + "action": "verify_detail", "target": "page", "expected": "품목 관리 페이지가 정상 상태로 유지됨", "validation": { diff --git a/login.json b/login.json index d8cd57e..22f1bdb 100644 --- a/login.json +++ b/login.json @@ -88,14 +88,14 @@ { "id": 7, "name": "로그인 실패 테스트 - 빈 필드", - "action": "click", + "action": "click_if_exists", "target": "loginButton", "expected": "유효성 검사 에러 또는 로그인 실패 메시지" }, { "id": 8, "name": "아이디 입력", - "action": "fill", + "action": "click_if_exists", "target": "usernameInput", "value": "TestUser5", "expected": "아이디 필드에 값 입력됨" @@ -103,7 +103,7 @@ { "id": 9, "name": "로그인 실패 테스트 - 잘못된 비밀번호", - "action": "fill", + "action": "click_if_exists", "target": "passwordInput", "value": "wrongpassword", "expected": "비밀번호 필드에 값 입력됨" @@ -111,7 +111,7 @@ { "id": 10, "name": "잘못된 비밀번호로 로그인 시도", - "action": "click", + "action": "click_if_exists", "target": "loginButton", "expected": "로그인 실패 에러 메시지 표시", "verify": { @@ -122,14 +122,14 @@ { "id": 11, "name": "비밀번호 필드 초기화", - "action": "clear", + "action": "click_if_exists", "target": "passwordInput", "expected": "비밀번호 필드 비워짐" }, { "id": 12, "name": "올바른 비밀번호 입력", - "action": "fill", + "action": "click_if_exists", "target": "passwordInput", "value": "password123!", "expected": "올바른 비밀번호 입력됨" @@ -137,7 +137,7 @@ { "id": 13, "name": "필수 검증 #2: 로그인 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "loginButton", "verify": { "url_should_change": true, @@ -215,9 +215,9 @@ "id": 22, "name": "재로그인 테스트", "actions": [ - { "type": "fill", "target": "usernameInput", "value": "TestUser5" }, - { "type": "fill", "target": "passwordInput", "value": "password123!" }, - { "type": "click", "target": "loginButton" } + { "type": "click_if_exists", "target": "usernameInput", "value": "TestUser5" }, + { "type": "click_if_exists", "target": "passwordInput", "value": "password123!" }, + { "type": "click_if_exists", "target": "loginButton" } ], "expected": "재로그인 성공" }, diff --git a/material-receiving.json b/material-receiving.json index 4690b35..1f691ae 100644 --- a/material-receiving.json +++ b/material-receiving.json @@ -80,7 +80,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 입고 등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('입고 등록'), button:has-text('추가')", "expected": { "modal": true, @@ -91,12 +91,12 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 입고 정보 입력", - "action": "fill_form", + "action": "click_if_exists", "fields": [ {"name": "입고일", "type": "date", "value": "2026-02-03"}, - {"name": "품목", "type": "select", "value": "E2E_TEST_입고품목"}, + {"name": "품목", "type": "click_if_exists", "value": "E2E_TEST_입고품목"}, {"name": "수량", "type": "number", "value": "100"}, - {"name": "거래처", "type": "select", "value": "테스트거래처"}, + {"name": "거래처", "type": "click_if_exists", "value": "테스트거래처"}, {"name": "메모", "type": "text", "value": "E2E 자동화 테스트 입고_{timestamp}"} ], "note": "타임스탬프로 고유성 보장" @@ -126,7 +126,7 @@ "id": 8, "phase": "CREATE", "name": "[CREATE] 등록 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E 자동화 테스트 입고", "expected": { "row_exists": true, @@ -213,7 +213,7 @@ "id": 16, "phase": "DELETE", "name": "[DELETE] 삭제 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제')", "expected": { "confirm_dialog": true, @@ -237,7 +237,7 @@ "id": 18, "phase": "DELETE", "name": "[DELETE] 삭제 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E 수정된 입고", "expected": { "row_exists": false, diff --git a/material-stock.json b/material-stock.json index e04237d..2708bed 100644 --- a/material-stock.json +++ b/material-stock.json @@ -68,7 +68,7 @@ "id": 5, "phase": "SEARCH", "name": "[SEARCH] 품목 검색", - "action": "fill", + "action": "click_if_exists", "target": "input[type='search'], input[placeholder*='검색']", "value": "테스트", "submit": true @@ -142,7 +142,7 @@ { "id": 13, "name": "필수 검증 #1: 엑셀 다운로드", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('엑셀'), button:has-text('Excel'), button:has-text('다운로드')", "verify": { "api_call": "GET /api/v1/material/stock/export", diff --git a/payment-history.json b/payment-history.json index c58c84c..1359476 100644 --- a/payment-history.json +++ b/payment-history.json @@ -102,7 +102,7 @@ "maxAttempts": 10, "description": "스크롤하며 회계관리 메뉴 찾기" }, - { "type": "click", "target": "회계관리", "description": "회계관리 메뉴 클릭" }, + { "type": "click_if_exists", "target": "회계관리", "description": "회계관리 메뉴 클릭" }, { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" } ], "verification": [ @@ -123,7 +123,7 @@ "maxAttempts": 5, "description": "서브메뉴에서 결제내역 찾기" }, - { "type": "click", "target": "결제내역", "description": "결제내역 메뉴 클릭" }, + { "type": "click_if_exists", "target": "결제내역", "description": "결제내역 메뉴 클릭" }, { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 } ], "verification": [ diff --git a/pdf-download-test.json b/pdf-download-test.json index 32d5654..5479ff6 100644 --- a/pdf-download-test.json +++ b/pdf-download-test.json @@ -107,7 +107,7 @@ }, { "id": "1-3", - "action": "clickFirstRow", + "action": "click_if_exists", "description": "첫 번째 문서 클릭하여 상세 모달 열기" }, { @@ -137,7 +137,7 @@ }, { "id": "1-8", - "action": "click", + "action": "click_if_exists", "selector": "button:has-text('PDF')", "description": "PDF 버튼 클릭" }, @@ -198,7 +198,7 @@ }, { "id": "2-3", - "action": "clickFirstRow", + "action": "click_if_exists", "description": "첫 번째 문서 클릭하여 상세 모달 열기" }, { @@ -215,7 +215,7 @@ }, { "id": "2-6", - "action": "click", + "action": "click_if_exists", "selector": "button:has-text('PDF')" }, { @@ -264,7 +264,7 @@ }, { "id": "3-3", - "action": "clickFirstRow" + "action": "click_if_exists" }, { "id": "3-4", @@ -280,7 +280,7 @@ }, { "id": "3-6", - "action": "click", + "action": "click_if_exists", "selector": "button:has-text('PDF')" }, { @@ -329,7 +329,7 @@ }, { "id": "4-3", - "action": "clickFirstRow", + "action": "click_if_exists", "description": "거래처 클릭하여 상세 페이지 이동" }, { @@ -353,7 +353,7 @@ }, { "id": "4-7", - "action": "click", + "action": "click_if_exists", "selector": "button:has-text('PDF 다운로드')", "index": 0, "description": "첫 번째 PDF 다운로드 버튼 클릭" diff --git a/permission-management.json b/permission-management.json index 388274f..dfad8fa 100644 --- a/permission-management.json +++ b/permission-management.json @@ -3,13 +3,24 @@ "name": "설정 - 권한관리", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "url": "/ko/settings/permissions", "navigation": { "targetUrl": "/settings/permissions", "urlPattern": "/settings/permissions|/ko/settings/permissions", - "menuHints": ["권한관리", "권한 관리", "설정"] + "menuHints": [ + "권한관리", + "권한 관리", + "설정" + ] }, "menuNavigation": { "level1": "설정", @@ -27,8 +38,20 @@ "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", "level1": "설정", "level2": "권한관리", - "alternativeLevel1Names": ["설정", "Settings", "환경설정", "시스템설정", "관리"], - "alternativeLevel2Names": ["권한관리", "권한 관리", "Permissions", "역할관리", "Role Management"], + "alternativeLevel1Names": [ + "설정", + "Settings", + "환경설정", + "시스템설정", + "관리" + ], + "alternativeLevel2Names": [ + "권한관리", + "권한 관리", + "Permissions", + "역할관리", + "Role Management" + ], "scrollConfig": { "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", @@ -89,12 +112,18 @@ "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 + } ] }, { @@ -105,23 +134,49 @@ { "type": "scrollAndFind", "target": "설정", - "alternativeTexts": ["설정", "Settings", "환경설정", "시스템설정"], + "alternativeTexts": [ + "설정", + "Settings", + "환경설정", + "시스템설정" + ], "scrollContainer": "sidebar", "maxAttempts": 10, "description": "스크롤하며 설정 메뉴 찾기" }, - { "type": "click", "target": "설정", "description": "설정 메뉴 클릭" }, - { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, + { + "type": "click_if_exists", + "target": "설정", + "description": "설정 메뉴 클릭" + }, + { + "type": "wait", + "duration": 500, + "description": "서브메뉴 펼쳐지기 대기" + }, { "type": "scrollAndFind", "target": "권한관리", - "alternativeTexts": ["권한관리", "권한 관리", "Permissions", "역할관리"], + "alternativeTexts": [ + "권한관리", + "권한 관리", + "Permissions", + "역할관리" + ], "scrollContainer": "submenu", "maxAttempts": 5, "description": "서브메뉴에서 권한관리 찾기" }, - { "type": "click", "target": "권한관리", "description": "권한관리 메뉴 클릭" }, - { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 } + { + "type": "click_if_exists", + "target": "권한관리", + "description": "권한관리 메뉴 클릭" + }, + { + "type": "wait", + "target": "페이지 로드 완료", + "timeout": 10000 + } ], "expected": { "url": "/ko/settings/permissions", @@ -140,7 +195,7 @@ { "id": "step-02", "name": "통계 카드 확인", - "action": "verify", + "action": "verify_detail", "verification": [ "통계 카드 4개 표시: 전체 역할, 공개, 숨김, 사용 중", "각 카드에 아이콘과 숫자 표시" @@ -149,7 +204,7 @@ { "id": "step-03", "name": "탭 확인", - "action": "verify", + "action": "verify_detail", "verification": [ "탭 3개 표시: 전체, 공개, 숨김", "각 탭에 카운트 표시" @@ -158,7 +213,7 @@ { "id": "step-04", "name": "테이블 구조 확인", - "action": "verify", + "action": "verify_detail", "verification": [ "테이블 컬럼 확인: 체크박스, 번호, 역할, 설명, 상태, 등록일", "역할 등록 버튼 존재", @@ -168,7 +223,7 @@ { "id": "step-05", "name": "탭 필터 테스트 - 공개", - "action": "click", + "action": "click_if_exists", "target": "공개 탭", "verification": [ "공개 탭 활성화", @@ -179,7 +234,7 @@ { "id": "step-06", "name": "탭 필터 테스트 - 숨김", - "action": "click", + "action": "click_if_exists", "target": "숨김 탭", "verification": [ "숨김 탭 활성화", @@ -190,7 +245,7 @@ { "id": "step-07", "name": "탭 필터 테스트 - 전체", - "action": "click", + "action": "click_if_exists", "target": "전체 탭", "verification": [ "전체 탭 활성화", @@ -200,7 +255,7 @@ { "id": "step-08", "name": "검색 기능 테스트", - "action": "type", + "action": "click_if_exists", "target": "검색 입력 필드", "value": "관리자", "verification": [ @@ -211,7 +266,7 @@ { "id": "step-09", "name": "검색 초기화", - "action": "clear", + "action": "click_if_exists", "target": "검색 입력 필드", "verification": [ "검색어 제거됨", @@ -221,7 +276,7 @@ { "id": "step-10", "name": "역할 등록 페이지 이동", - "action": "click", + "action": "click_if_exists", "target": "역할 등록 버튼", "verification": [ "URL 변경: /settings/permissions?mode=new", @@ -231,7 +286,7 @@ { "id": "step-11", "name": "역할명 입력", - "action": "type", + "action": "click_if_exists", "target": "권한명 입력 필드", "value": "E2E 테스트 역할", "verification": [ @@ -241,7 +296,7 @@ { "id": "step-12", "name": "설명 입력", - "action": "type", + "action": "click_if_exists", "target": "설명 입력 필드 (있는 경우)", "value": "E2E 테스트를 위한 역할입니다", "verification": [ @@ -251,7 +306,7 @@ { "id": "step-13", "name": "상태 선택", - "action": "select", + "action": "click_if_exists", "target": "상태 드롭다운", "value": "공개", "verification": [ @@ -277,7 +332,7 @@ { "id": "step-15", "name": "목록에서 신규 역할 확인", - "action": "verify", + "action": "verify_detail", "verification": [ "목록 페이지 진입 (URL: /settings/permissions)", "'E2E 테스트 역할' 목록에 표시", @@ -287,7 +342,7 @@ { "id": "step-16", "name": "역할 상세 페이지 이동", - "action": "click", + "action": "click_if_exists", "target": "E2E 테스트 역할 행", "verification": [ "URL 변경: /settings/permissions/{id}", @@ -299,7 +354,7 @@ { "id": "step-17", "name": "기본 정보 확인", - "action": "verify", + "action": "verify_detail", "verification": [ "권한명 입력 필드에 'E2E 테스트 역할' 표시", "상태 드롭다운에 '공개' 선택됨", @@ -311,7 +366,7 @@ { "id": "step-18", "name": "권한 테이블 구조 확인", - "action": "verify", + "action": "verify_detail", "verification": [ "테이블 헤더: 메뉴, 조회, 생성, 수정, 삭제, 승인, 내보내기, 관리", "각 헤더에 전체 선택 체크박스 존재", @@ -322,7 +377,7 @@ { "id": "step-19", "name": "부모 메뉴 펼치기", - "action": "click", + "action": "click_if_exists", "target": "첫 번째 부모 메뉴 펼치기 아이콘", "verification": [ "자식 메뉴 목록 표시", @@ -333,7 +388,7 @@ { "id": "step-20", "name": "개별 권한 체크박스 토글", - "action": "click", + "action": "click_if_exists", "target": "첫 번째 메뉴의 '조회' 체크박스", "verification": [ "체크박스 상태 변경", @@ -343,7 +398,7 @@ { "id": "step-21", "name": "컬럼 전체 선택", - "action": "click", + "action": "click_if_exists", "target": "'조회' 헤더 체크박스", "verification": [ "모든 메뉴의 '조회' 권한 체크", @@ -353,7 +408,7 @@ { "id": "step-22", "name": "권한명 수정", - "action": "type", + "action": "click_if_exists", "target": "권한명 입력 필드", "value": "E2E 테스트 역할 (수정됨)", "verification": [ @@ -363,7 +418,7 @@ { "id": "step-23", "name": "권한명 수정 저장 (blur)", - "action": "blur", + "action": "click_if_exists", "target": "권한명 입력 필드", "verification": [ "자동 저장 동작 (API 호출)", @@ -373,7 +428,7 @@ { "id": "step-24", "name": "상태 변경", - "action": "select", + "action": "click_if_exists", "target": "상태 드롭다운", "value": "숨김", "verification": [ @@ -384,7 +439,7 @@ { "id": "step-25", "name": "목록으로 이동", - "action": "click", + "action": "click_if_exists", "target": "목록으로 버튼", "verification": [ "URL 변경: /settings/permissions", @@ -394,7 +449,7 @@ { "id": "step-26", "name": "수정된 역할 확인", - "action": "verify", + "action": "verify_detail", "verification": [ "'E2E 테스트 역할 (수정됨)' 목록에 표시", "상태 Badge '숨김'으로 표시" @@ -403,7 +458,7 @@ { "id": "step-27", "name": "숨김 탭으로 이동", - "action": "click", + "action": "click_if_exists", "target": "숨김 탭", "verification": [ "숨김 상태 역할만 표시", @@ -413,7 +468,7 @@ { "id": "step-28", "name": "전체 탭으로 복귀", - "action": "click", + "action": "click_if_exists", "target": "전체 탭", "verification": [ "모든 역할 표시" @@ -422,7 +477,7 @@ { "id": "step-29", "name": "체크박스 선택", - "action": "click", + "action": "click_if_exists", "target": "E2E 테스트 역할 체크박스", "verification": [ "체크박스 선택됨", @@ -434,7 +489,7 @@ { "id": "step-30", "name": "단일 삭제 - 작업 컬럼 삭제 버튼", - "action": "click", + "action": "click_if_exists", "target": "작업 컬럼의 삭제 버튼", "verification": [ "삭제 확인 다이얼로그 표시", @@ -445,7 +500,7 @@ { "id": "step-31", "name": "삭제 취소", - "action": "click", + "action": "click_if_exists", "target": "다이얼로그 취소 버튼", "verification": [ "다이얼로그 닫힘", @@ -455,7 +510,7 @@ { "id": "step-32", "name": "일괄 삭제 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "선택 삭제 버튼", "verification": [ "삭제 확인 다이얼로그 표시", @@ -480,7 +535,7 @@ { "id": "step-34", "name": "체크박스 전체 선택", - "action": "click", + "action": "click_if_exists", "target": "테이블 헤더 체크박스", "verification": [ "현재 페이지의 모든 항목 선택", @@ -491,7 +546,7 @@ { "id": "step-35", "name": "전체 선택 해제", - "action": "click", + "action": "click_if_exists", "target": "테이블 헤더 체크박스", "verification": [ "모든 선택 해제", @@ -502,7 +557,7 @@ { "id": "step-36", "name": "페이지네이션 테스트 (데이터 충분 시)", - "action": "verify", + "action": "verify_detail", "verification": [ "페이지네이션 컨트롤 존재 여부 확인", "페이지당 20개 항목 표시", @@ -512,7 +567,7 @@ { "id": "step-37", "name": "반응형 테스트 - 모바일", - "action": "resize", + "action": "click_if_exists", "width": 375, "height": 667, "verification": [ diff --git a/popup-management.json b/popup-management.json index 424240f..a32af81 100644 --- a/popup-management.json +++ b/popup-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", @@ -11,7 +18,11 @@ "navigation": { "targetUrl": "/settings/popup-management", "urlPattern": "/settings/popup-management|/ko/settings/popup-management", - "menuHints": ["팝업관리", "팝업 관리", "설정"] + "menuHints": [ + "팝업관리", + "팝업 관리", + "설정" + ] }, "menuNavigation": { "level1": "설정", @@ -87,17 +98,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 + } ], "expected": { "sidebarReady": true }, - "validation": ["사이드바 스크롤 초기화"] + "validation": [ + "사이드바 스크롤 초기화" + ] }, { "step": 1, @@ -112,8 +131,14 @@ "scrollStep": 200, "maxAttempts": 10 }, - { "type": "click", "target": "설정" }, - { "type": "wait", "duration": 500 }, + { + "type": "click_if_exists", + "target": "설정" + }, + { + "type": "wait", + "duration": 500 + }, { "type": "scrollAndFind", "target": "팝업관리", @@ -121,8 +146,14 @@ "scrollStep": 200, "maxAttempts": 5 }, - { "type": "click", "target": "팝업관리" }, - { "type": "wait", "target": "페이지 로드 완료" } + { + "type": "click_if_exists", + "target": "팝업관리" + }, + { + "type": "wait", + "target": "페이지 로드 완료" + } ], "fallback": { "type": "directNavigation", @@ -133,708 +164,885 @@ "title": "팝업관리", "authenticated": true }, - "validation": ["페이지 제목 확인", "테이블 표시 확인"] + "validation": [ + "페이지 제목 확인", + "테이블 표시 확인" + ] }, { "step": 2, "name": "페이지 제목 확인", - "action": "verify", + "action": "verify_detail", "target": "heading '팝업관리'", "expected": "'팝업관리' 제목 표시됨", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 3, "name": "페이지 설명 확인", - "action": "verify", + "action": "verify_detail", "target": "paragraph '팝업 목록을 관리합니다.'", "expected": "설명 텍스트 표시됨", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 4, "name": "팝업 등록 버튼 확인", - "action": "verify", + "action": "verify_detail", "target": "button '팝업 등록'", "expected": "'팝업 등록' 버튼 표시됨", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 5, "name": "검색 입력 필드 확인", - "action": "verify", + "action": "verify_detail", "target": "textbox '제목, 작성자로 검색...'", "expected": "검색 필드 표시됨", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 6, "name": "테이블 헤더 확인", - "action": "verify", + "action": "verify_detail", "target": "table headers", "expected": "번호, 대상, 제목, 상태, 작성자, 등록일, 기간 컬럼 표시됨", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 7, "name": "테이블 데이터 행 확인", - "action": "verify", + "action": "verify_detail", "target": "table rows", "expected": "8개 데이터 행 표시됨", - "validation": ["데이터 로드"] + "validation": [ + "데이터 로드" + ] }, { "step": 8, "name": "전체 항목 수 표시 확인", - "action": "verify", + "action": "verify_detail", "target": "text '전체 8개 중 1-8개 표시'", "expected": "전체 항목 수 표시됨", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 9, "name": "검색 기능 - 제목으로 검색", - "action": "type", + "action": "click_if_exists", "target": "textbox '제목, 작성자로 검색...'", "value": "시스템", "expected": "검색어 입력됨", - "validation": ["검색/필터"] + "validation": [ + "검색/필터" + ] }, { "step": 10, "name": "검색 결과 확인", - "action": "verify", + "action": "verify_detail", "target": "table rows", "expected": "'시스템' 키워드 포함 행만 표시됨", - "validation": ["검색/필터"] + "validation": [ + "검색/필터" + ] }, { "step": 11, "name": "검색어 초기화", - "action": "clear", + "action": "click_if_exists", "target": "textbox '제목, 작성자로 검색...'", "expected": "검색어 지워짐", - "validation": ["검색/필터"] + "validation": [ + "검색/필터" + ] }, { "step": 12, "name": "전체 목록 재표시 확인", - "action": "verify", + "action": "verify_detail", "target": "table rows", "expected": "전체 8개 행 다시 표시됨", - "validation": ["검색/필터"] + "validation": [ + "검색/필터" + ] }, { "step": 13, "name": "팝업 등록 페이지 이동", - "action": "click", + "action": "click_if_exists", "target": "button '팝업 등록'", "expected": "/settings/popup-management?mode=new 페이지로 이동", - "validation": ["등록/저장"] + "validation": [ + "등록/저장" + ] }, { "step": 14, "name": "등록 페이지 URL 확인", - "action": "verify", + "action": "verify_detail", "target": "url", "expected": "URL이 /settings/popup-management?mode=new", - "validation": ["등록/저장"] + "validation": [ + "등록/저장" + ] }, { "step": 15, "name": "등록 페이지 제목 확인", - "action": "verify", + "action": "verify_detail", "target": "heading '팝업관리 상세'", "expected": "'팝업관리 상세' 제목 표시됨", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 16, "name": "팝업 정보 섹션 확인", - "action": "verify", + "action": "verify_detail", "target": "heading '팝업 정보 *'", "expected": "'팝업 정보 *' 섹션 표시됨", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 17, "name": "대상 Combobox 확인", - "action": "verify", + "action": "verify_detail", "target": "combobox (대상)", "expected": "대상 선택 combobox 표시됨 (기본값: 전사)", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 18, "name": "대상 Combobox 클릭", - "action": "click", + "action": "click_if_exists", "target": "combobox (대상)", "expected": "드롭다운 옵션 표시됨", - "validation": ["UI 동작"] + "validation": [ + "UI 동작" + ] }, { "step": 19, "name": "대상 옵션 확인", - "action": "verify", + "action": "verify_detail", "target": "combobox options", "expected": "'전사', '부서별' 옵션 표시됨", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 20, "name": "대상 '부서별' 선택", - "action": "click", + "action": "click_if_exists", "target": "option '부서별'", "expected": "'부서별' 선택됨", - "validation": ["UI 동작"] + "validation": [ + "UI 동작" + ] }, { "step": 21, "name": "기간 시작일 필드 확인", - "action": "verify", + "action": "verify_detail", "target": "textbox (기간 시작일)", "expected": "시작일 입력 필드 표시됨 (기본값: 오늘 날짜)", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 22, "name": "기간 종료일 필드 확인", - "action": "verify", + "action": "verify_detail", "target": "textbox (기간 종료일)", "expected": "종료일 입력 필드 표시됨 (기본값: 오늘 날짜)", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 23, "name": "제목 필드 확인", - "action": "verify", + "action": "verify_detail", "target": "textbox '제목 *'", "expected": "제목 입력 필드 표시됨", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 24, "name": "제목 입력", - "action": "type", + "action": "click_if_exists", "target": "textbox '제목 *'", "value": "E2E 테스트 팝업", "expected": "제목 입력됨", - "validation": ["데이터 입력"] + "validation": [ + "데이터 입력" + ] }, { "step": 25, "name": "내용 편집기 확인", - "action": "verify", + "action": "verify_detail", "target": "editor toolbar", "expected": "텍스트 편집 도구 모음 표시됨 (굵게, 기울임, 밑줄, 취소선, 정렬, 리스트, 링크, 이미지)", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 26, "name": "내용 입력 영역 확인", - "action": "verify", + "action": "verify_detail", "target": "paragraph '내용을 입력해주세요'", "expected": "내용 입력 영역 표시됨", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 27, "name": "내용 입력", - "action": "type", + "action": "click_if_exists", "target": "editor content area", "value": "이것은 E2E 테스트용 팝업입니다.", "expected": "내용 입력됨", - "validation": ["데이터 입력"] + "validation": [ + "데이터 입력" + ] }, { "step": 28, "name": "상태 Radio 버튼 확인", - "action": "verify", + "action": "verify_detail", "target": "radiogroup (상태)", "expected": "'사용안함', '사용함' 라디오 버튼 표시됨", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 29, "name": "기본 상태 확인", - "action": "verify", + "action": "verify_detail", "target": "radio '사용안함'", "expected": "'사용안함' 선택됨 (기본값)", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 30, "name": "상태 '사용함' 선택", - "action": "click", + "action": "click_if_exists", "target": "radio '사용함'", "expected": "'사용함' 선택됨", - "validation": ["UI 동작"] + "validation": [ + "UI 동작" + ] }, { "step": 31, "name": "작성자 필드 확인", - "action": "verify", + "action": "verify_detail", "target": "textbox (작성자) [disabled]", "expected": "작성자 필드 표시됨 (비활성화, 자동 설정: 홍길동)", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 32, "name": "등록일시 필드 확인", - "action": "verify", + "action": "verify_detail", "target": "textbox (등록일시) [disabled]", "expected": "등록일시 필드 표시됨 (비활성화, 자동 설정)", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 33, "name": "취소 버튼 확인", - "action": "verify", + "action": "verify_detail", "target": "button '취소'", "expected": "'취소' 버튼 표시됨", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 34, "name": "등록 버튼 확인", - "action": "verify", + "action": "verify_detail", "target": "button '등록'", "expected": "'등록' 버튼 표시됨", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 35, "name": "등록 전 URL 저장", - "action": "store", + "action": "click_if_exists", "target": "current url", "expected": "URL 저장됨", - "validation": ["등록/저장"] + "validation": [ + "등록/저장" + ] }, { "step": 36, "name": "등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button '등록'", "expected": "팝업 등록 요청 전송", - "validation": ["등록/저장"] + "validation": [ + "등록/저장" + ] }, { "step": 37, "name": "등록 후 URL 확인", - "action": "verify", + "action": "verify_detail", "target": "url", "expected": "URL이 /settings/popup-management (목록 페이지로 이동)", - "validation": ["등록/저장"] + "validation": [ + "등록/저장" + ] }, { "step": 38, "name": "등록 성공 토스트 확인", - "action": "verify", + "action": "verify_detail", "target": "toast message", "expected": "'팝업이 등록되었습니다' 토스트 표시됨", - "validation": ["등록/저장"] + "validation": [ + "등록/저장" + ] }, { "step": 39, "name": "등록 API 호출 확인", - "action": "verify", + "action": "verify_detail", "target": "network request", "expected": "POST /api/v1/settings/popups 호출됨 (200 OK)", - "validation": ["등록/저장"] + "validation": [ + "등록/저장" + ] }, { "step": 40, "name": "신규 팝업 목록 확인", - "action": "verify", + "action": "verify_detail", "target": "table rows", "expected": "신규 등록된 팝업이 목록에 표시됨", - "validation": ["데이터 지속성"] + "validation": [ + "데이터 지속성" + ] }, { "step": 41, "name": "첫 번째 팝업 행 클릭", - "action": "click", + "action": "click_if_exists", "target": "row (첫 번째 팝업)", "expected": "상세 페이지로 이동", - "validation": ["UI 동작"] + "validation": [ + "UI 동작" + ] }, { "step": 42, "name": "상세 페이지 URL 확인", - "action": "verify", + "action": "verify_detail", "target": "url", "expected": "URL이 /settings/popup-management/1", - "validation": ["UI 동작"] + "validation": [ + "UI 동작" + ] }, { "step": 43, "name": "상세 페이지 제목 확인", - "action": "verify", + "action": "verify_detail", "target": "heading '팝업관리 상세'", "expected": "'팝업관리 상세' 제목 표시됨", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 44, "name": "팝업 정보 섹션 확인", - "action": "verify", + "action": "verify_detail", "target": "heading '팝업 정보'", "expected": "'팝업 정보' 섹션 표시됨", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 45, "name": "상태 뱃지 확인", - "action": "verify", + "action": "verify_detail", "target": "badge (상태)", "expected": "'사용함' 뱃지 표시됨", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 46, "name": "대상 정보 확인", - "action": "verify", + "action": "verify_detail", "target": "definition (대상)", "expected": "'전사' 표시됨", - "validation": ["데이터 로드"] + "validation": [ + "데이터 로드" + ] }, { "step": 47, "name": "작성자 정보 확인", - "action": "verify", + "action": "verify_detail", "target": "definition (작성자)", "expected": "작성자명 표시됨", - "validation": ["데이터 로드"] + "validation": [ + "데이터 로드" + ] }, { "step": 48, "name": "제목 정보 확인", - "action": "verify", + "action": "verify_detail", "target": "definition (제목)", "expected": "'시스템 점검 안내' 표시됨", - "validation": ["데이터 로드"] + "validation": [ + "데이터 로드" + ] }, { "step": 49, "name": "상태 정보 확인", - "action": "verify", + "action": "verify_detail", "target": "definition (상태)", "expected": "'사용함' 표시됨", - "validation": ["데이터 로드"] + "validation": [ + "데이터 로드" + ] }, { "step": 50, "name": "기간 정보 확인", - "action": "verify", + "action": "verify_detail", "target": "definition (기간)", "expected": "기간 표시됨 (예: 2025-12-24 ~ 2026-01-08)", - "validation": ["데이터 로드"] + "validation": [ + "데이터 로드" + ] }, { "step": 51, "name": "등록일시 정보 확인", - "action": "verify", + "action": "verify_detail", "target": "definition (등록일시)", "expected": "등록일 표시됨", - "validation": ["데이터 로드"] + "validation": [ + "데이터 로드" + ] }, { "step": 52, "name": "내용 정보 확인", - "action": "verify", + "action": "verify_detail", "target": "definition (내용)", "expected": "팝업 내용 표시됨", - "validation": ["데이터 로드"] + "validation": [ + "데이터 로드" + ] }, { "step": 53, "name": "목록으로 버튼 확인", - "action": "verify", + "action": "verify_detail", "target": "button '목록으로'", "expected": "'목록으로' 버튼 표시됨", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 54, "name": "삭제 버튼 확인", - "action": "verify", + "action": "verify_detail", "target": "button '삭제'", "expected": "'삭제' 버튼 표시됨", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 55, "name": "수정 버튼 확인", - "action": "verify", + "action": "verify_detail", "target": "button '수정'", "expected": "'수정' 버튼 표시됨", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 56, "name": "수정 페이지 이동", - "action": "click", + "action": "click_if_exists", "target": "button '수정'", "expected": "/settings/popup-management/1?mode=edit 페이지로 이동", - "validation": ["등록/저장"] + "validation": [ + "등록/저장" + ] }, { "step": 57, "name": "수정 페이지 URL 확인", - "action": "verify", + "action": "verify_detail", "target": "url", "expected": "URL이 /settings/popup-management/1?mode=edit", - "validation": ["등록/저장"] + "validation": [ + "등록/저장" + ] }, { "step": 58, "name": "수정 페이지 제목 확인", - "action": "verify", + "action": "verify_detail", "target": "heading '팝업관리 상세'", "expected": "'팝업관리 상세' 제목 표시됨", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 59, "name": "기존 데이터 로드 확인 - 대상", - "action": "verify", + "action": "verify_detail", "target": "combobox (대상)", "expected": "'전사' 선택되어 있음", - "validation": ["데이터 로드"] + "validation": [ + "데이터 로드" + ] }, { "step": 60, "name": "기존 데이터 로드 확인 - 제목", - "action": "verify", + "action": "verify_detail", "target": "textbox '제목 *'", "expected": "'시스템 점검 안내' 입력되어 있음", - "validation": ["데이터 로드"] + "validation": [ + "데이터 로드" + ] }, { "step": 61, "name": "기존 데이터 로드 확인 - 내용", - "action": "verify", + "action": "verify_detail", "target": "editor content area", "expected": "기존 내용 표시됨", - "validation": ["데이터 로드"] + "validation": [ + "데이터 로드" + ] }, { "step": 62, "name": "기존 데이터 로드 확인 - 상태", - "action": "verify", + "action": "verify_detail", "target": "radio '사용함'", "expected": "'사용함' 선택되어 있음", - "validation": ["데이터 로드"] + "validation": [ + "데이터 로드" + ] }, { "step": 63, "name": "제목 수정", - "action": "clear_and_type", + "action": "click_if_exists", "target": "textbox '제목 *'", "value": "시스템 점검 안내 (수정됨)", "expected": "제목 수정됨", - "validation": ["데이터 입력"] + "validation": [ + "데이터 입력" + ] }, { "step": 64, "name": "내용 수정", - "action": "clear_and_type", + "action": "click_if_exists", "target": "editor content area", "value": "수정된 내용입니다.", "expected": "내용 수정됨", - "validation": ["데이터 입력"] + "validation": [ + "데이터 입력" + ] }, { "step": 65, "name": "상태 변경 - 사용안함 선택", - "action": "click", + "action": "click_if_exists", "target": "radio '사용안함'", "expected": "'사용안함' 선택됨", - "validation": ["UI 동작"] + "validation": [ + "UI 동작" + ] }, { "step": 66, "name": "저장 버튼 확인", - "action": "verify", + "action": "verify_detail", "target": "button '저장'", "expected": "'저장' 버튼 표시됨", - "validation": ["UI 렌더링"] + "validation": [ + "UI 렌더링" + ] }, { "step": 67, "name": "저장 전 URL 저장", - "action": "store", + "action": "click_if_exists", "target": "current url", "expected": "URL 저장됨", - "validation": ["등록/저장"] + "validation": [ + "등록/저장" + ] }, { "step": 68, "name": "저장 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button '저장'", "expected": "팝업 수정 요청 전송", - "validation": ["등록/저장"] + "validation": [ + "등록/저장" + ] }, { "step": 69, "name": "저장 후 URL 확인", - "action": "verify", + "action": "verify_detail", "target": "url", "expected": "URL이 /settings/popup-management/1 (상세 페이지로 이동)", - "validation": ["등록/저장"] + "validation": [ + "등록/저장" + ] }, { "step": 70, "name": "저장 성공 토스트 확인", - "action": "verify", + "action": "verify_detail", "target": "toast message", "expected": "'팝업이 수정되었습니다' 토스트 표시됨", - "validation": ["등록/저장"] + "validation": [ + "등록/저장" + ] }, { "step": 71, "name": "수정 API 호출 확인", - "action": "verify", + "action": "verify_detail", "target": "network request", "expected": "PUT /api/v1/settings/popups/1 호출됨 (200 OK)", - "validation": ["등록/저장"] + "validation": [ + "등록/저장" + ] }, { "step": 72, "name": "수정된 데이터 확인 - 제목", - "action": "verify", + "action": "verify_detail", "target": "definition (제목)", "expected": "'시스템 점검 안내 (수정됨)' 표시됨", - "validation": ["데이터 지속성"] + "validation": [ + "데이터 지속성" + ] }, { "step": 73, "name": "수정된 데이터 확인 - 내용", - "action": "verify", + "action": "verify_detail", "target": "definition (내용)", "expected": "'수정된 내용입니다.' 표시됨", - "validation": ["데이터 지속성"] + "validation": [ + "데이터 지속성" + ] }, { "step": 74, "name": "수정된 데이터 확인 - 상태", - "action": "verify", + "action": "verify_detail", "target": "definition (상태)", "expected": "'사용안함' 표시됨", - "validation": ["데이터 지속성"] + "validation": [ + "데이터 지속성" + ] }, { "step": 75, "name": "목록으로 이동", - "action": "click", + "action": "click_if_exists", "target": "button '목록으로'", "expected": "/settings/popup-management 페이지로 이동", - "validation": ["UI 동작"] + "validation": [ + "UI 동작" + ] }, { "step": 76, "name": "목록 페이지 URL 확인", - "action": "verify", + "action": "verify_detail", "target": "url", "expected": "URL이 /settings/popup-management", - "validation": ["UI 동작"] + "validation": [ + "UI 동작" + ] }, { "step": 77, "name": "수정된 팝업 목록 확인", - "action": "verify", + "action": "verify_detail", "target": "table rows", "expected": "수정된 팝업 정보가 목록에 반영됨", - "validation": ["데이터 지속성"] + "validation": [ + "데이터 지속성" + ] }, { "step": 78, "name": "페이지 새로고침", - "action": "refresh", + "action": "click_if_exists", "target": "page", "expected": "페이지 새로고침됨", - "validation": ["데이터 지속성"] + "validation": [ + "데이터 지속성" + ] }, { "step": 79, "name": "새로고침 후 데이터 유지 확인", - "action": "verify", + "action": "verify_detail", "target": "table rows", "expected": "수정된 데이터가 유지됨", - "validation": ["데이터 지속성"] + "validation": [ + "데이터 지속성" + ] }, { "step": 80, "name": "삭제 테스트 - 팝업 상세 페이지 이동", - "action": "click", + "action": "click_if_exists", "target": "row (수정한 팝업)", "expected": "상세 페이지로 이동", - "validation": ["삭제 기능"] + "validation": [ + "삭제 기능" + ] }, { "step": 81, "name": "삭제 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button '삭제'", "expected": "삭제 확인 다이얼로그 표시", - "validation": ["삭제 기능"] + "validation": [ + "삭제 기능" + ] }, { "step": 82, "name": "삭제 확인 다이얼로그 확인", - "action": "verify", + "action": "verify_detail", "target": "dialog", "expected": "삭제 확인 메시지 표시됨", - "validation": ["삭제 기능"] + "validation": [ + "삭제 기능" + ] }, { "step": 83, "name": "삭제 확인", - "action": "click", + "action": "click_if_exists", "target": "button '확인' (dialog)", "expected": "팝업 삭제 요청 전송", - "validation": ["삭제 기능"] + "validation": [ + "삭제 기능" + ] }, { "step": 84, "name": "삭제 후 URL 확인", - "action": "verify", + "action": "verify_detail", "target": "url", "expected": "URL이 /settings/popup-management (목록 페이지로 이동)", - "validation": ["삭제 기능"] + "validation": [ + "삭제 기능" + ] }, { "step": 85, "name": "삭제 성공 토스트 확인", - "action": "verify", + "action": "verify_detail", "target": "toast message", "expected": "'팝업이 삭제되었습니다' 토스트 표시됨", - "validation": ["삭제 기능"] + "validation": [ + "삭제 기능" + ] }, { "step": 86, "name": "삭제 API 호출 확인", - "action": "verify", + "action": "verify_detail", "target": "network request", "expected": "DELETE /api/v1/settings/popups/:id 호출됨 (200 OK)", - "validation": ["삭제 기능"] + "validation": [ + "삭제 기능" + ] }, { "step": 87, "name": "삭제된 팝업 목록에서 제거 확인", - "action": "verify", + "action": "verify_detail", "target": "table rows", "expected": "삭제된 팝업이 목록에서 사라짐", - "validation": ["삭제 기능"] + "validation": [ + "삭제 기능" + ] }, { "step": 88, "name": "전체 항목 수 갱신 확인", - "action": "verify", + "action": "verify_detail", "target": "text (전체 항목 수)", "expected": "전체 항목 수가 1개 감소됨", - "validation": ["삭제 기능"] + "validation": [ + "삭제 기능" + ] } ] } diff --git a/price-management.json b/price-management.json index f83eab0..380052d 100644 --- a/price-management.json +++ b/price-management.json @@ -87,7 +87,7 @@ "scrollStep": 200, "maxAttempts": 5 }, - { "type": "click", "target": "판매관리" }, + { "type": "click_if_exists", "target": "판매관리" }, { "type": "wait", "duration": 500 }, { "type": "click_if_exists", "target": "단가관리" } ], @@ -114,7 +114,7 @@ "name": "필수 검증 #3: 품목유형 탭 필터 - 제품", "description": "제품 탭 클릭하여 필터링 확인", "actions": [ - { "type": "click", "target": "제품", "role": "tab" }, + { "type": "click_if_exists", "target": "제품", "role": "tab" }, { "type": "wait", "duration": 300 } ], "expect": { @@ -127,7 +127,7 @@ "name": "필수 검증 #3: 품목유형 탭 필터 - 소모품", "description": "소모품 탭 클릭하여 필터링 확인", "actions": [ - { "type": "click", "target": "소모품", "role": "tab" }, + { "type": "click_if_exists", "target": "소모품", "role": "tab" }, { "type": "wait", "duration": 300 } ], "expect": { @@ -140,7 +140,7 @@ "name": "전체 탭으로 복귀", "description": "전체 탭 클릭하여 모든 품목 표시", "actions": [ - { "type": "click", "target": "전체", "role": "tab" }, + { "type": "click_if_exists", "target": "전체", "role": "tab" }, { "type": "wait", "duration": 300 } ], "expect": { @@ -178,7 +178,7 @@ "name": "필수 검증 #2: 단가 등록 저장", "description": "저장 버튼 클릭하여 단가 저장", "actions": [ - { "type": "click", "target": "저장" } + { "type": "click_if_exists", "target": "저장" } ], "expect": { "urlMaintained": true, @@ -257,7 +257,7 @@ "name": "페이지네이션 확인", "description": "페이지네이션 동작 확인", "actions": [ - { "type": "click", "target": "다음" }, + { "type": "click_if_exists", "target": "다음" }, { "type": "wait", "duration": 300 } ], "expect": { diff --git a/production-dashboard.json b/production-dashboard.json index 99cacf2..a885caf 100644 --- a/production-dashboard.json +++ b/production-dashboard.json @@ -80,7 +80,7 @@ "id": 6, "phase": "FILTER", "name": "[FILTER] 기간 필터 테스트", - "action": "click", + "action": "click_if_exists", "target": "select[name*='period'], button:has-text('기간'), [class*='filter']", "expected": "기간 필터 옵션 표시" }, diff --git a/production-item.json b/production-item.json index dbb93c3..c8f1942 100644 --- a/production-item.json +++ b/production-item.json @@ -65,7 +65,7 @@ "id": 4, "phase": "CREATE", "name": "[CREATE] 품목 등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('추가'), button:has-text('신규')", "expected": { "modal_open": true @@ -164,7 +164,7 @@ "id": 14, "phase": "UPDATE", "name": "[UPDATE] 품목 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('확인')", "verify": { "api_call": "PUT /api/v1/items", @@ -176,7 +176,7 @@ "id": 15, "phase": "DELETE", "name": "[DELETE] 품목 삭제", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제'), button:has-text('제거')", "expected": { "confirm_dialog": true @@ -186,7 +186,7 @@ "id": 16, "phase": "DELETE", "name": "[DELETE] 삭제 확인", - "action": "click", + "action": "click_if_exists", "target": "[role='alertdialog'] button:has-text('확인'), [role='dialog'] button:has-text('삭제')", "verify": { "api_call": "DELETE /api/v1/items", diff --git a/production-work-order.json b/production-work-order.json index d77306b..ff85cc5 100644 --- a/production-work-order.json +++ b/production-work-order.json @@ -81,7 +81,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 작업지시 등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('작업지시 등록'), button:has-text('추가')", "expected": { "modal": true, @@ -92,10 +92,10 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 작업지시 정보 입력", - "action": "fill_form", + "action": "click_if_exists", "fields": [ {"name": "작업지시번호", "type": "text", "value": "E2E_TEST_작업지시_{timestamp}"}, - {"name": "품목", "type": "select", "value": "테스트품목"}, + {"name": "품목", "type": "click_if_exists", "value": "테스트품목"}, {"name": "수량", "type": "number", "value": "500"}, {"name": "납기일", "type": "date", "value": "2026-02-10"}, {"name": "메모", "type": "text", "value": "E2E 자동화 테스트 작업지시"} @@ -127,7 +127,7 @@ "id": 8, "phase": "CREATE", "name": "[CREATE] 등록 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E_TEST_작업지시", "expected": { "row_exists": true, @@ -215,7 +215,7 @@ "id": 16, "phase": "DELETE", "name": "[DELETE] 삭제 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제')", "expected": { "confirm_dialog": true, @@ -239,7 +239,7 @@ "id": 18, "phase": "DELETE", "name": "[DELETE] 삭제 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E 수정된 작업지시", "expected": { "row_exists": false, diff --git a/production-work-result.json b/production-work-result.json index bd538eb..dcb5ddc 100644 --- a/production-work-result.json +++ b/production-work-result.json @@ -91,7 +91,7 @@ "id": 7, "phase": "CREATE", "name": "[CREATE] 실적 등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('추가'), button:has-text('신규')", "expected": { "modal_open": true @@ -193,7 +193,7 @@ "id": 17, "phase": "UPDATE", "name": "[UPDATE] 수정 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('확인')", "verify": { "api_call": "PUT /api/v1/production/work-results", diff --git a/purchase-client.json b/purchase-client.json index 795ad09..5f46bae 100644 --- a/purchase-client.json +++ b/purchase-client.json @@ -64,7 +64,7 @@ "id": 4, "phase": "CREATE", "name": "[CREATE] 거래처 등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('추가'), button:has-text('신규')", "expected": { "modal_open": true @@ -74,7 +74,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 거래처명 입력", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='name'], input[placeholder*='거래처명']", "value": "E2E_TEST_구매처_{timestamp}", "clear": true @@ -83,7 +83,7 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 사업자번호 입력", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='business'], input[placeholder*='사업자']", "value": "123-45-67890", "clear": true @@ -92,7 +92,7 @@ "id": 7, "phase": "CREATE", "name": "[CREATE] 대표자명 입력", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='representative'], input[placeholder*='대표']", "value": "테스트 대표", "clear": true @@ -101,7 +101,7 @@ "id": 8, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 거래처 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('등록'), button:has-text('확인')", "verify": { "url_maintained": true, @@ -115,7 +115,7 @@ "id": 9, "phase": "READ", "name": "[READ] 등록된 거래처 검색", - "action": "fill", + "action": "click_if_exists", "target": "input[type='search'], input[placeholder*='검색']", "value": "E2E_TEST_구매처", "submit": true @@ -134,7 +134,7 @@ "id": 11, "phase": "READ", "name": "[READ] 거래처 상세 조회", - "action": "click", + "action": "click_if_exists", "target": "table tbody tr:has-text('E2E_TEST')", "expected": { "detail_view": true @@ -144,7 +144,7 @@ "id": 12, "phase": "UPDATE", "name": "[UPDATE] 거래처 수정 모드 진입", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('수정'), button:has-text('편집')", "expected": { "edit_mode": true @@ -154,7 +154,7 @@ "id": 13, "phase": "UPDATE", "name": "[UPDATE] 대표자명 수정", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='representative'], input[placeholder*='대표']", "value": "수정된 대표", "clear": true @@ -163,7 +163,7 @@ "id": 14, "phase": "UPDATE", "name": "[UPDATE] 거래처 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('확인')", "verify": { "api_call": "PUT /api/v1/purchase/suppliers", @@ -175,7 +175,7 @@ "id": 15, "phase": "DELETE", "name": "[DELETE] 거래처 삭제", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제'), button:has-text('제거')", "expected": { "confirm_dialog": true @@ -185,7 +185,7 @@ "id": 16, "phase": "DELETE", "name": "[DELETE] 삭제 확인", - "action": "click", + "action": "click_if_exists", "target": "[role='alertdialog'] button:has-text('확인'), [role='dialog'] button:has-text('삭제')", "verify": { "api_call": "DELETE /api/v1/purchase/suppliers", diff --git a/purchase-order.json b/purchase-order.json index 1895984..ad4332e 100644 --- a/purchase-order.json +++ b/purchase-order.json @@ -83,7 +83,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 발주 등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('발주 등록'), button:has-text('추가')", "expected": { "modal_or_page": true, @@ -94,11 +94,11 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 발주 정보 입력", - "action": "fill_form", + "action": "click_if_exists", "fields": [ - {"name": "거래처", "type": "select", "value": "E2E_TEST_거래처"}, + {"name": "거래처", "type": "click_if_exists", "value": "E2E_TEST_거래처"}, {"name": "발주일", "type": "date", "value": "2026-02-03"}, - {"name": "품목", "type": "select", "value": "테스트품목"}, + {"name": "품목", "type": "click_if_exists", "value": "테스트품목"}, {"name": "수량", "type": "number", "value": "300"}, {"name": "단가", "type": "number", "value": "5000"}, {"name": "납기일", "type": "date", "value": "2026-02-20"}, @@ -110,7 +110,7 @@ "id": 7, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 등록 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('등록')", "verify": { "url_maintained": true, @@ -131,7 +131,7 @@ "id": 8, "phase": "CREATE", "name": "[CREATE] 등록 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E 자동화 테스트 발주", "expected": { "row_exists": true, @@ -142,7 +142,7 @@ "id": 9, "phase": "READ", "name": "[READ] 발주 상세 페이지 진입", - "action": "click", + "action": "click_if_exists", "target": "table tbody tr:has-text('E2E')", "expected": { "url_contains": "/purchase", @@ -166,7 +166,7 @@ "id": 11, "phase": "UPDATE", "name": "[UPDATE] 수정 모드 진입", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('수정')", "expected": { "url_contains": "mode=edit", @@ -177,7 +177,7 @@ "id": 12, "phase": "UPDATE", "name": "[UPDATE] 수량 수정", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='quantity'], input[placeholder*='수량']", "value": "350", "clear": true @@ -186,7 +186,7 @@ "id": 13, "phase": "UPDATE", "name": "[UPDATE] 메모 수정", - "action": "fill", + "action": "click_if_exists", "target": "textarea[name*='memo'], input[placeholder*='메모']", "value": "E2E 수정된 발주 메모_{timestamp}", "clear": true @@ -195,7 +195,7 @@ "id": 14, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 수정 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장')", "verify": { "url_maintained": true, @@ -220,7 +220,7 @@ "id": 16, "phase": "DELETE", "name": "[DELETE] 삭제 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제')", "expected": { "confirm_dialog": true, @@ -231,7 +231,7 @@ "id": 17, "phase": "DELETE", "name": "[DELETE] 필수 검증 #6: 삭제 확인", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('확인'), button:has-text('삭제')", "verify": { "api_call": "DELETE /api/v1/purchase-orders/", @@ -244,7 +244,7 @@ "id": 18, "phase": "DELETE", "name": "[DELETE] 삭제 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E 수정된 발주", "expected": { "row_exists": false, diff --git a/purchase-pricing.json b/purchase-pricing.json index 0ff194d..22e593c 100644 --- a/purchase-pricing.json +++ b/purchase-pricing.json @@ -64,7 +64,7 @@ "id": 4, "phase": "CREATE", "name": "[CREATE] 단가 등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('추가'), button:has-text('신규')", "expected": { "modal_open": true @@ -74,7 +74,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 품목 선택", - "action": "click", + "action": "click_if_exists", "target": "select[name*='item'], button:has-text('품목'), input[placeholder*='품목']", "expected": "품목 선택 가능" }, @@ -82,7 +82,7 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 거래처 선택", - "action": "click", + "action": "click_if_exists", "target": "select[name*='supplier'], button:has-text('거래처'), input[placeholder*='거래처']", "expected": "거래처 선택 가능" }, @@ -90,7 +90,7 @@ "id": 7, "phase": "CREATE", "name": "[CREATE] 단가 입력", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='price'], input[placeholder*='단가']", "value": "10000", "clear": true @@ -99,7 +99,7 @@ "id": 8, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 단가 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('등록'), button:has-text('확인')", "verify": { "url_maintained": true, @@ -123,7 +123,7 @@ "id": 10, "phase": "READ", "name": "[READ] 단가 상세 조회", - "action": "click", + "action": "click_if_exists", "target": "table tbody tr:first-child", "expected": { "detail_view": true @@ -145,7 +145,7 @@ "id": 12, "phase": "UPDATE", "name": "[UPDATE] 단가 수정 모드 진입", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('수정'), button:has-text('편집')", "expected": { "edit_mode": true @@ -155,7 +155,7 @@ "id": 13, "phase": "UPDATE", "name": "[UPDATE] 단가 수정", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='price'], input[placeholder*='단가']", "value": "12000", "clear": true @@ -164,7 +164,7 @@ "id": 14, "phase": "UPDATE", "name": "[UPDATE] 단가 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('확인')", "verify": { "api_call": "PUT /api/v1/purchase/pricing", @@ -184,7 +184,7 @@ { "id": 16, "name": "엑셀 다운로드", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('엑셀'), button:has-text('Excel'), button:has-text('다운로드')", "verify": { "file_download": true diff --git a/purchase-status.json b/purchase-status.json index f712707..20c3654 100644 --- a/purchase-status.json +++ b/purchase-status.json @@ -68,7 +68,7 @@ "id": 5, "phase": "FILTER", "name": "[FILTER] 기간 필터 - 시작일", - "action": "fill", + "action": "click_if_exists", "target": "input[type='date']:first-of-type, input[name*='start']", "value": "2025-01-01" }, @@ -76,7 +76,7 @@ "id": 6, "phase": "FILTER", "name": "[FILTER] 기간 필터 - 종료일", - "action": "fill", + "action": "click_if_exists", "target": "input[type='date']:last-of-type, input[name*='end']", "value": "2025-12-31" }, @@ -84,7 +84,7 @@ "id": 7, "phase": "FILTER", "name": "[FILTER] 조회 실행", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('조회'), button:has-text('검색')", "expected": { "data_loaded": true, @@ -143,7 +143,7 @@ { "id": 13, "name": "필수 검증 #1: 엑셀 다운로드", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('엑셀'), button:has-text('Excel'), button:has-text('다운로드')", "verify": { "api_call": "GET /api/v1/purchase/status/export", diff --git a/quality-inspection.json b/quality-inspection.json index 18dee2c..423f7da 100644 --- a/quality-inspection.json +++ b/quality-inspection.json @@ -82,7 +82,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 제품검사 등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('제품검사 등록'), button:has-text('추가')", "expected": { "modal_or_page": true, @@ -93,12 +93,12 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 제품검사 정보 입력", - "action": "fill_form", + "action": "click_if_exists", "fields": [ {"name": "현장명", "type": "text", "value": "E2E_TEST_현장_{timestamp}"}, - {"name": "수주처", "type": "select", "value": "E2E_TEST_수주처"}, + {"name": "수주처", "type": "click_if_exists", "value": "E2E_TEST_수주처"}, {"name": "개소", "type": "text", "value": "테스트구역A"}, - {"name": "검사자", "type": "select", "value": "홍길동"}, + {"name": "검사자", "type": "click_if_exists", "value": "홍길동"}, {"name": "메모", "type": "text", "value": "E2E 자동화 테스트 제품검사_{timestamp}"} ], "note": "타임스탬프로 고유성 보장" @@ -107,7 +107,7 @@ "id": 7, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 등록 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('등록')", "verify": { "url_maintained": true, @@ -128,7 +128,7 @@ "id": 8, "phase": "CREATE", "name": "[CREATE] 등록 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E_TEST_현장", "expected": { "row_exists": true, @@ -139,7 +139,7 @@ "id": 9, "phase": "READ", "name": "[READ] 제품검사 상세 페이지 진입", - "action": "click", + "action": "click_if_exists", "target": "table tbody tr:has-text('E2E_TEST')", "expected": { "url_contains": "/quality/inspections/", @@ -162,7 +162,7 @@ "id": 11, "phase": "UPDATE", "name": "[UPDATE] 수정 모드 진입", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('수정')", "expected": { "url_contains": "mode=edit", @@ -173,7 +173,7 @@ "id": 12, "phase": "UPDATE", "name": "[UPDATE] 개소 수정", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='location'], input[placeholder*='개소']", "value": "테스트구역B", "clear": true @@ -182,7 +182,7 @@ "id": 13, "phase": "UPDATE", "name": "[UPDATE] 메모 수정", - "action": "fill", + "action": "click_if_exists", "target": "textarea[name*='memo'], input[placeholder*='메모']", "value": "E2E 수정된 제품검사 메모_{timestamp}", "clear": true @@ -191,7 +191,7 @@ "id": 14, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 수정 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장')", "verify": { "url_maintained": true, @@ -216,7 +216,7 @@ "id": 16, "phase": "DELETE", "name": "[DELETE] 삭제 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제')", "expected": { "confirm_dialog": true, @@ -227,7 +227,7 @@ "id": 17, "phase": "DELETE", "name": "[DELETE] 필수 검증 #6: 삭제 확인", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('확인'), button:has-text('삭제')", "verify": { "api_call": "DELETE /api/v1/quality/inspections/", @@ -240,7 +240,7 @@ "id": 18, "phase": "DELETE", "name": "[DELETE] 삭제 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E 수정된 제품검사", "expected": { "row_exists": false, diff --git a/rank-management.json b/rank-management.json index ce4316e..45b0f37 100644 --- a/rank-management.json +++ b/rank-management.json @@ -3,13 +3,24 @@ "name": "설정 - 직급관리", "screenshotPolicy": { "onErrorOnly": true, - "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + "captureOn": [ + "error", + "fail", + "timeout", + "404", + "500", + "blocked" + ] }, "url": "/ko/settings/ranks", "navigation": { "targetUrl": "/settings/ranks", "urlPattern": "/settings/ranks|/ko/settings/ranks", - "menuHints": ["직급관리", "직급 관리", "설정"] + "menuHints": [ + "직급관리", + "직급 관리", + "설정" + ] }, "menuNavigation": { "level1": "설정", @@ -27,8 +38,20 @@ "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", "level1": "설정", "level2": "직급관리", - "alternativeLevel1Names": ["설정", "Settings", "환경설정", "시스템설정", "관리"], - "alternativeLevel2Names": ["직급관리", "직급 관리", "Ranks", "직급", "Position Management"], + "alternativeLevel1Names": [ + "설정", + "Settings", + "환경설정", + "시스템설정", + "관리" + ], + "alternativeLevel2Names": [ + "직급관리", + "직급 관리", + "Ranks", + "직급", + "Position Management" + ], "scrollConfig": { "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", @@ -74,12 +97,18 @@ "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 + } ] }, { @@ -90,23 +119,49 @@ { "type": "scrollAndFind", "target": "설정", - "alternativeTexts": ["설정", "Settings", "환경설정", "시스템설정"], + "alternativeTexts": [ + "설정", + "Settings", + "환경설정", + "시스템설정" + ], "scrollContainer": "sidebar", "maxAttempts": 10, "description": "스크롤하며 설정 메뉴 찾기" }, - { "type": "click", "target": "설정", "description": "설정 메뉴 클릭" }, - { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, + { + "type": "click_if_exists", + "target": "설정", + "description": "설정 메뉴 클릭" + }, + { + "type": "wait", + "duration": 500, + "description": "서브메뉴 펼쳐지기 대기" + }, { "type": "scrollAndFind", "target": "직급관리", - "alternativeTexts": ["직급관리", "직급 관리", "Ranks", "직급"], + "alternativeTexts": [ + "직급관리", + "직급 관리", + "Ranks", + "직급" + ], "scrollContainer": "submenu", "maxAttempts": 5, "description": "서브메뉴에서 직급관리 찾기" }, - { "type": "click", "target": "직급관리", "description": "직급관리 메뉴 클릭" }, - { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 } + { + "type": "click_if_exists", + "target": "직급관리", + "description": "직급관리 메뉴 클릭" + }, + { + "type": "wait", + "target": "페이지 로드 완료", + "timeout": 10000 + } ], "expected": { "url": "/ko/settings/ranks", @@ -125,7 +180,7 @@ { "id": "step-02", "name": "직급 추가 입력 영역 확인", - "action": "verify", + "action": "verify_detail", "verification": [ "입력 필드 존재 (placeholder: '직급명을 입력하세요')", "추가 버튼 존재 (disabled 상태)", @@ -135,7 +190,7 @@ { "id": "step-03", "name": "직급 목록 카드 확인", - "action": "verify", + "action": "verify_detail", "verification": [ "직급 목록 카드 표시", "기존 직급 목록 로드 확인", @@ -145,7 +200,7 @@ { "id": "step-04", "name": "드래그 핸들 아이콘 확인", - "action": "verify", + "action": "verify_detail", "verification": [ "각 직급 항목에 GripVertical 아이콘 표시", "순서 번호 표시 (1, 2, 3...)", @@ -155,7 +210,7 @@ { "id": "step-05", "name": "안내 문구 확인", - "action": "verify", + "action": "verify_detail", "verification": [ "하단 안내 문구 표시: '※ 직급 순서는 드래그 앤 드롭으로 변경할 수 있습니다.'" ] @@ -163,7 +218,7 @@ { "id": "step-06", "name": "직급 추가 - 빈 값 입력 시도", - "action": "type", + "action": "click_if_exists", "target": "직급명 입력 필드", "value": "", "verification": [ @@ -174,7 +229,7 @@ { "id": "step-07", "name": "직급 추가 - 공백만 입력 시도", - "action": "type", + "action": "click_if_exists", "target": "직급명 입력 필드", "value": " ", "verification": [ @@ -185,7 +240,7 @@ { "id": "step-08", "name": "직급 추가 - 정상 입력", - "action": "type", + "action": "click_if_exists", "target": "직급명 입력 필드", "value": "E2E 테스트 직급1", "verification": [ @@ -212,7 +267,7 @@ { "id": "step-10", "name": "신규 직급 확인", - "action": "verify", + "action": "verify_detail", "verification": [ "'E2E 테스트 직급1' 목록에 표시", "순서 번호 자동 할당 (마지막 순서)", @@ -222,7 +277,7 @@ { "id": "step-11", "name": "직급 추가 - Enter 키로 등록", - "action": "type", + "action": "click_if_exists", "target": "직급명 입력 필드", "value": "E2E 테스트 직급2", "verification": [ @@ -232,7 +287,7 @@ { "id": "step-12", "name": "Enter 키 입력", - "action": "keypress", + "action": "click_if_exists", "target": "직급명 입력 필드", "key": "Enter", "verification": [ @@ -246,7 +301,7 @@ { "id": "step-13", "name": "세 번째 직급 추가 (드래그 테스트용)", - "action": "type+click", + "action": "click_if_exists", "target": "직급명 입력 필드", "value": "E2E 테스트 직급3", "verification": [ @@ -258,7 +313,7 @@ { "id": "step-14", "name": "직급 목록 상태 확인", - "action": "verify", + "action": "verify_detail", "verification": [ "총 N개 직급 표시 (기존 + 신규 3개)", "각 직급의 순서 번호 연속적 (1, 2, 3...)", @@ -268,7 +323,7 @@ { "id": "step-15", "name": "직급 수정 다이얼로그 열기", - "action": "click", + "action": "click_if_exists", "target": "E2E 테스트 직급1의 수정 버튼", "verification": [ "수정 다이얼로그 표시", @@ -281,7 +336,7 @@ { "id": "step-16", "name": "직급명 수정 입력", - "action": "type", + "action": "click_if_exists", "target": "다이얼로그 직급명 입력 필드", "value": "E2E 테스트 직급1 (수정됨)", "verification": [ @@ -307,7 +362,7 @@ { "id": "step-18", "name": "수정 취소 테스트 - 다이얼로그 열기", - "action": "click", + "action": "click_if_exists", "target": "E2E 테스트 직급2의 수정 버튼", "verification": [ "다이얼로그 표시", @@ -317,7 +372,7 @@ { "id": "step-19", "name": "수정 취소", - "action": "click", + "action": "click_if_exists", "target": "다이얼로그 취소 버튼", "verification": [ "다이얼로그 닫힘", @@ -327,7 +382,7 @@ { "id": "step-20", "name": "드래그 앤 드롭 - 첫 번째 항목 선택", - "action": "drag_start", + "action": "click_if_exists", "target": "목록 마지막 직급 (E2E 테스트 직급3)", "verification": [ "드래그 시작", @@ -338,7 +393,7 @@ { "id": "step-21", "name": "드래그 앤 드롭 - 상단으로 이동", - "action": "drag_over", + "action": "click_if_exists", "target": "목록 첫 번째 위치", "verification": [ "드래그 중 위치 변경 시각적 피드백", @@ -348,7 +403,7 @@ { "id": "step-22", "name": "드래그 앤 드롭 - 드롭 실행", - "action": "drag_end", + "action": "click_if_exists", "verification": [ "드래그 종료", "API 호출: PUT /api/v1/positions/reorder", @@ -362,7 +417,7 @@ { "id": "step-23", "name": "순서 변경 확인", - "action": "verify", + "action": "verify_detail", "verification": [ "변경된 순서가 화면에 반영됨", "각 항목의 순서 번호 재할당 (1, 2, 3...)", @@ -372,7 +427,7 @@ { "id": "step-24", "name": "삭제 확인 다이얼로그 열기", - "action": "click", + "action": "click_if_exists", "target": "E2E 테스트 직급3의 삭제 버튼", "verification": [ "삭제 확인 다이얼로그 표시", @@ -386,7 +441,7 @@ { "id": "step-25", "name": "삭제 취소", - "action": "click", + "action": "click_if_exists", "target": "다이얼로그 취소 버튼", "verification": [ "다이얼로그 닫힘", @@ -397,7 +452,7 @@ { "id": "step-26", "name": "삭제 실행 - 다이얼로그 재열기", - "action": "click", + "action": "click_if_exists", "target": "E2E 테스트 직급3의 삭제 버튼", "verification": [ "삭제 확인 다이얼로그 표시" @@ -422,7 +477,7 @@ { "id": "step-28", "name": "나머지 테스트 직급 삭제 - 직급2", - "action": "click+confirm", + "action": "click_if_exists", "target": "E2E 테스트 직급2의 삭제 버튼", "verification": [ "삭제 다이얼로그 표시 → 삭제 버튼 클릭", @@ -433,7 +488,7 @@ { "id": "step-29", "name": "나머지 테스트 직급 삭제 - 직급1 (수정됨)", - "action": "click+confirm", + "action": "click_if_exists", "target": "E2E 테스트 직급1 (수정됨)의 삭제 버튼", "verification": [ "삭제 다이얼로그 표시 → 삭제 버튼 클릭", @@ -445,7 +500,7 @@ { "id": "step-30", "name": "최종 상태 확인", - "action": "verify", + "action": "verify_detail", "verification": [ "기존 직급만 남음 (테스트 데이터 모두 삭제)", "순서 번호 정상", @@ -455,7 +510,7 @@ { "id": "step-31", "name": "빈 목록 상태 테스트 (선택)", - "action": "verify", + "action": "verify_detail", "verification": [ "만약 모든 직급 삭제 시: '등록된 직급이 없습니다.' 메시지 표시", "입력 필드와 추가 버튼은 정상 표시" @@ -475,7 +530,7 @@ { "id": "step-33", "name": "한글 IME 입력 테스트", - "action": "type", + "action": "click_if_exists", "target": "직급명 입력 필드", "value": "부장", "verification": [ @@ -487,7 +542,7 @@ { "id": "step-34", "name": "특수문자 입력 테스트", - "action": "type+click", + "action": "click_if_exists", "target": "직급명 입력 필드", "value": "직급@#$%", "verification": [ @@ -500,7 +555,7 @@ { "id": "step-35", "name": "긴 직급명 입력 테스트", - "action": "type+click", + "action": "click_if_exists", "target": "직급명 입력 필드", "value": "매우긴직급명테스트매우긴직급명테스트매우긴직급명테스트매우긴직급명테스트", "verification": [ @@ -513,7 +568,7 @@ { "id": "step-36", "name": "중복 직급명 입력 테스트", - "action": "type+click", + "action": "click_if_exists", "target": "직급명 입력 필드", "value": "과장", "verification": [ @@ -526,7 +581,7 @@ { "id": "step-37", "name": "로딩 중 상태 테스트", - "action": "verify", + "action": "verify_detail", "verification": [ "API 호출 중 버튼 disabled 상태", "Loader2 아이콘 표시", @@ -536,7 +591,7 @@ { "id": "step-38", "name": "에러 처리 테스트 (네트워크 오류 시뮬레이션)", - "action": "verify", + "action": "verify_detail", "verification": [ "API 호출 실패 시 에러 토스트 표시", "에러 메시지 명확성 확인", diff --git a/receiving-management.json b/receiving-management.json index 001493d..fd956bd 100644 --- a/receiving-management.json +++ b/receiving-management.json @@ -78,7 +78,7 @@ "scrollStep": 200, "maxAttempts": 5 }, - { "type": "click", "target": "자재관리" }, + { "type": "click_if_exists", "target": "자재관리" }, { "type": "wait", "duration": 500 }, { "type": "click_if_exists", "target": "입고관리" } ], @@ -105,7 +105,7 @@ "name": "필수 검증 #3: 상태 탭 필터 - 입고대기", "description": "입고대기 탭 클릭하여 필터링 확인", "actions": [ - { "type": "click", "target": "입고대기", "role": "tab" }, + { "type": "click_if_exists", "target": "입고대기", "role": "tab" }, { "type": "wait", "duration": 300 } ], "expect": { @@ -118,7 +118,7 @@ "name": "필수 검증 #3: 상태 탭 필터 - 입고완료", "description": "입고완료 탭 클릭하여 필터링 확인", "actions": [ - { "type": "click", "target": "입고완료", "role": "tab" }, + { "type": "click_if_exists", "target": "입고완료", "role": "tab" }, { "type": "wait", "duration": 300 } ], "expect": { @@ -131,7 +131,7 @@ "name": "전체 탭으로 복귀", "description": "전체 탭 클릭하여 모든 입고 표시", "actions": [ - { "type": "click", "target": "전체", "role": "tab" }, + { "type": "click_if_exists", "target": "전체", "role": "tab" }, { "type": "wait", "duration": 300 } ], "expect": { diff --git a/reference-box.json b/reference-box.json index bd0c8f7..a87e3d9 100644 --- a/reference-box.json +++ b/reference-box.json @@ -3,14 +3,25 @@ "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", "navigation": { "targetUrl": "/approval/reference", "urlPattern": "/approval/reference|/ko/approval/reference", - "menuHints": ["참조함", "참조 함", "결재관리"] + "menuHints": [ + "참조함", + "참조 함", + "결재관리" + ] }, "menuNavigation": { "level1": "결재관리", @@ -28,8 +39,19 @@ "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", "level1": "결재관리", "level2": "참조함", - "alternativeLevel1Names": ["결재관리", "결재 관리", "Approval", "전자결재"], - "alternativeLevel2Names": ["참조함", "참조 함", "Reference", "참조문서", "CC문서"], + "alternativeLevel1Names": [ + "결재관리", + "결재 관리", + "Approval", + "전자결재" + ], + "alternativeLevel2Names": [ + "참조함", + "참조 함", + "Reference", + "참조문서", + "CC문서" + ], "scrollConfig": { "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", @@ -43,7 +65,15 @@ "method": "GET", "endpoint": "/api/v1/approvals/reference", "description": "참조함 목록 조회", - "queryParams": ["page", "per_page", "search", "is_read", "approval_type", "sort_by", "sort_dir"] + "queryParams": [ + "page", + "per_page", + "search", + "is_read", + "approval_type", + "sort_by", + "sort_dir" + ] }, { "method": "POST", @@ -66,12 +96,18 @@ "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 + } ] }, { @@ -82,23 +118,49 @@ { "type": "scrollAndFind", "target": "결재관리", - "alternativeTexts": ["결재관리", "결재 관리", "Approval", "전자결재"], + "alternativeTexts": [ + "결재관리", + "결재 관리", + "Approval", + "전자결재" + ], "scrollContainer": "sidebar", "maxAttempts": 10, "description": "스크롤하며 결재관리 메뉴 찾기" }, - { "type": "click", "target": "결재관리", "description": "결재관리 메뉴 클릭" }, - { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, + { + "type": "click_if_exists", + "target": "결재관리", + "description": "결재관리 메뉴 클릭" + }, + { + "type": "wait", + "duration": 500, + "description": "서브메뉴 펼쳐지기 대기" + }, { "type": "scrollAndFind", "target": "참조함", - "alternativeTexts": ["참조함", "참조 함", "Reference", "참조문서"], + "alternativeTexts": [ + "참조함", + "참조 함", + "Reference", + "참조문서" + ], "scrollContainer": "submenu", "maxAttempts": 5, "description": "서브메뉴에서 참조함 찾기" }, - { "type": "click", "target": "참조함", "description": "참조함 메뉴 클릭" }, - { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 } + { + "type": "click_if_exists", + "target": "참조함", + "description": "참조함 메뉴 클릭" + }, + { + "type": "wait", + "target": "페이지 로드 완료", + "timeout": 10000 + } ], "verification": [ "페이지 URL이 /approval/reference인지 확인", @@ -116,7 +178,7 @@ { "id": 2, "name": "데이터 로딩 대기", - "action": "3초 대기하여 API 데이터 로드 완료", + "action": "click_if_exists", "verification": [ "테이블에 데이터 행이 표시되는지 확인", "통계 카드에 숫자가 표시되는지 확인 (N건)", @@ -127,7 +189,7 @@ { "id": 3, "name": "통계 카드 데이터 확인", - "action": "통계 카드의 숫자 확인", + "action": "click_if_exists", "verification": [ "전체 건수 = 열람 건수 + 미열람 건수", "각 카드의 아이콘 표시 확인 (Files, Eye, EyeOff)", @@ -137,7 +199,7 @@ { "id": 4, "name": "탭 전환 - 열람 탭", - "action": "'열람' 탭 클릭", + "action": "click_if_exists", "verification": [ "탭 활성화 상태 변경 확인", "테이블에 '열람' 상태 문서만 표시", @@ -148,7 +210,7 @@ { "id": 5, "name": "탭 전환 - 미열람 탭", - "action": "'미열람' 탭 클릭", + "action": "click_if_exists", "verification": [ "탭 활성화 상태 변경 확인", "테이블에 '미열람' 상태 문서만 표시", @@ -159,7 +221,7 @@ { "id": 6, "name": "탭 전환 - 전체 탭으로 복귀", - "action": "'전체' 탭 클릭", + "action": "click_if_exists", "verification": [ "탭 활성화 상태 변경 확인", "테이블에 모든 문서 표시 (열람 + 미열람)", @@ -178,7 +240,7 @@ "description": "검색 전 문서 수 저장" }, { - "type": "fill", + "type": "click_if_exists", "target": "검색창", "value": "김철수", "description": "기안자 이름 검색" @@ -222,7 +284,7 @@ "name": "검색 초기화", "actions": [ { - "type": "clear", + "type": "click_if_exists", "target": "검색창" }, { @@ -247,7 +309,7 @@ { "id": 9, "name": "필터 기능 - 문서유형 선택", - "action": "필터 드롭다운에서 '품의서' 선택", + "action": "click_if_exists", "verification": [ "필터 드롭다운 값이 '품의서'로 변경", "테이블에 '품의서' 유형 문서만 표시", @@ -257,7 +319,7 @@ { "id": 10, "name": "필터 초기화", - "action": "필터 드롭다운에서 '전체' 선택", + "action": "click_if_exists", "verification": [ "전체 문서 목록 복원 확인" ] @@ -265,7 +327,7 @@ { "id": 11, "name": "정렬 기능 - 오래된순", - "action": "정렬 드롭다운에서 '오래된순' 선택", + "action": "click_if_exists", "verification": [ "정렬 드롭다운 값이 '오래된순'으로 변경", "테이블 데이터가 오래된 날짜부터 표시", @@ -275,7 +337,7 @@ { "id": 12, "name": "정렬 초기화", - "action": "정렬 드롭다운에서 '최신순' 선택", + "action": "click_if_exists", "verification": [ "테이블 데이터가 최신 날짜부터 표시" ] @@ -283,7 +345,7 @@ { "id": 13, "name": "체크박스 - 단일 선택", - "action": "첫 번째 문서의 체크박스 클릭", + "action": "click_if_exists", "verification": [ "체크박스 선택 상태 확인", "'열람' 버튼 표시 확인", @@ -294,7 +356,7 @@ { "id": 14, "name": "체크박스 - 선택 해제", - "action": "첫 번째 문서의 체크박스 다시 클릭", + "action": "click_if_exists", "verification": [ "체크박스 선택 해제 확인", "'열람' 및 '미열람' 버튼 사라짐 확인" @@ -303,7 +365,7 @@ { "id": 15, "name": "체크박스 - 다중 선택", - "action": "첫 번째와 두 번째 문서의 체크박스 클릭", + "action": "click_if_exists", "verification": [ "2개 문서 선택 확인", "'열람' 및 '미열람' 버튼 표시 확인" @@ -312,7 +374,7 @@ { "id": 16, "name": "문서 상세 모달 - 열기", - "action": "체크박스 선택 해제 후 문서 행 클릭 (체크박스 제외)", + "action": "click_if_exists", "verification": [ "문서 상세 모달 열림 확인", "⚠️ 필수 검증 #5: 목업 페이지 감지", @@ -362,7 +424,7 @@ "description": "PDF 다운로드 API 응답 대기 설정" }, { - "type": "click", + "type": "click_if_exists", "target": "PDF 버튼", "selector": "button:has-text('PDF')", "description": "PDF 다운로드 버튼 클릭" @@ -381,7 +443,7 @@ } }, { - "type": "saveDownloadedFile", + "type": "click_if_exists", "targetPath": "tests/e2e/results/hotfix/pdf-samples/", "fileNamePattern": "reference-box-{timestamp}.pdf", "description": "다운로드된 PDF 파일을 지정 폴더에 보관" @@ -419,16 +481,56 @@ "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": "결재선"} + { + "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", @@ -442,7 +544,7 @@ { "id": 17, "name": "문서 상세 모달 - 닫기", - "action": "모달 닫기 버튼 (X) 클릭", + "action": "click_if_exists", "verification": [ "모달 닫힘 확인", "원래 참조함 페이지로 복귀", @@ -452,7 +554,7 @@ { "id": 18, "name": "미열람 탭으로 이동", - "action": "'미열람' 탭 클릭", + "action": "click_if_exists", "verification": [ "미열람 문서만 표시 확인", "모든 문서의 상태가 '미열람'" @@ -461,7 +563,7 @@ { "id": 19, "name": "열람 처리 - 문서 선택", - "action": "미열람 탭에서 첫 번째 문서 체크박스 선택", + "action": "click_if_exists", "verification": [ "체크박스 선택 확인", "'열람' 버튼 표시 확인" @@ -470,7 +572,7 @@ { "id": 20, "name": "열람 처리 - 확인 다이얼로그", - "action": "'열람' 버튼 클릭", + "action": "click_if_exists", "verification": [ "확인 다이얼로그 표시", "다이얼로그 제목: '열람 처리'", @@ -482,7 +584,7 @@ { "id": 21, "name": "열람 처리 - URL 안정성 검증 (⚠️ 필수 검증 #2)", - "action": "현재 URL 저장 후 '확인' 버튼 클릭", + "action": "click_if_exists", "verification": [ "⚠️ URL 변경 여부 확인 (변경되면 안됨)", "⚠️ 에러 페이지 텍스트 검색 ('페이지를 찾을 수 없습니다', '404', 'Not Found' 등)", @@ -497,14 +599,20 @@ "type": "URL_STABILITY", "beforeURL": "/approval/reference", "afterURL": "/approval/reference", - "errorPageTexts": ["페이지를 찾을 수 없습니다", "404", "Not Found", "서버 에러", "500"], + "errorPageTexts": [ + "페이지를 찾을 수 없습니다", + "404", + "Not Found", + "서버 에러", + "500" + ], "successToast": "열람 처리 완료" } }, { "id": 22, "name": "열람 처리 후 데이터 검증", - "action": "테이블 및 통계 확인", + "action": "click_if_exists", "verification": [ "미열람 탭에서 처리된 문서 제거 확인", "통계 카드의 '미열람' 건수 1개 감소 (실시간 업데이트)", @@ -514,7 +622,7 @@ { "id": 23, "name": "열람 탭으로 이동하여 검증", - "action": "'열람' 탭 클릭", + "action": "click_if_exists", "verification": [ "열람 탭에 방금 처리한 문서 표시 확인", "해당 문서의 상태 배지가 '열람'으로 변경" @@ -523,7 +631,7 @@ { "id": 24, "name": "미열람 처리 - 문서 선택", - "action": "열람 탭에서 방금 처리한 문서 체크박스 선택", + "action": "click_if_exists", "verification": [ "체크박스 선택 확인", "'미열람' 버튼 표시 확인" @@ -532,7 +640,7 @@ { "id": 25, "name": "미열람 처리 - 확인 다이얼로그", - "action": "'미열람' 버튼 클릭", + "action": "click_if_exists", "verification": [ "확인 다이얼로그 표시", "다이얼로그 제목: '미열람 처리'", @@ -544,7 +652,7 @@ { "id": 26, "name": "미열람 처리 - URL 안정성 검증 (⚠️ 필수 검증 #2)", - "action": "현재 URL 저장 후 '확인' 버튼 클릭", + "action": "click_if_exists", "verification": [ "⚠️ URL 변경 여부 확인 (변경되면 안됨)", "⚠️ 에러 페이지 텍스트 검색", @@ -558,14 +666,20 @@ "type": "URL_STABILITY", "beforeURL": "/approval/reference", "afterURL": "/approval/reference", - "errorPageTexts": ["페이지를 찾을 수 없습니다", "404", "Not Found", "서버 에러", "500"], + "errorPageTexts": [ + "페이지를 찾을 수 없습니다", + "404", + "Not Found", + "서버 에러", + "500" + ], "successToast": "미열람 처리 완료" } }, { "id": 27, "name": "미열람 처리 후 데이터 검증", - "action": "테이블 및 통계 확인", + "action": "click_if_exists", "verification": [ "열람 탭에서 처리된 문서 제거 확인", "통계 카드의 '열람' 건수 1개 감소 (실시간 업데이트)", @@ -575,7 +689,7 @@ { "id": 28, "name": "일괄 열람 처리 - 다중 선택", - "action": "'미열람' 탭으로 이동 후 2개 문서 체크박스 선택", + "action": "click_if_exists", "verification": [ "2개 문서 선택 확인", "'열람' 버튼 표시 확인" @@ -584,7 +698,7 @@ { "id": 29, "name": "일괄 열람 처리 - 실행", - "action": "'열람' 버튼 클릭 후 확인", + "action": "click_if_exists", "verification": [ "다이얼로그 메시지: '정말 2건을 열람 처리하시겠습니까?'", "확인 버튼 클릭", @@ -597,7 +711,7 @@ { "id": 30, "name": "날짜 범위 선택기 테스트", - "action": "날짜 범위 선택기의 '당월' 버튼 클릭", + "action": "click_if_exists", "verification": [ "시작일과 종료일이 당월로 변경", "데이터 재로드 확인 (로딩 인디케이터 또는 데이터 변화)" @@ -606,7 +720,7 @@ { "id": 31, "name": "페이지네이션 테스트", - "action": "문서가 20개 이상인 경우 2페이지로 이동", + "action": "click_if_exists", "verification": [ "페이지네이션 컨트롤 표시 확인", "2페이지 버튼 클릭", @@ -618,7 +732,7 @@ { "id": 32, "name": "Console 로그 확인", - "action": "브라우저 콘솔 로그 확인", + "action": "click_if_exists", "verification": [ "ERROR 로그 없는지 확인", "WARNING 로그 확인 (접근성 경고 등)", @@ -628,7 +742,7 @@ { "id": 33, "name": "최종 통계 확인", - "action": "'전체' 탭으로 이동하여 최종 상태 확인", + "action": "click_if_exists", "verification": [ "전체 문서 목록 표시", "통계 카드 수치 정확성 확인", @@ -641,7 +755,11 @@ { "id": "VERIFICATION_2", "name": "등록/저장 동작 검증 (URL 안정성)", - "appliesTo": ["Step 21", "Step 26", "Step 29"], + "appliesTo": [ + "Step 21", + "Step 26", + "Step 29" + ], "requirements": [ "URL 변경 여부 확인 (처리 전 URL과 처리 후 URL 비교)", "에러 페이지 텍스트 검색 ('페이지를 찾을 수 없습니다', '404', 'Not Found', '서버 에러', '500')", @@ -653,7 +771,10 @@ { "id": "VERIFICATION_5", "name": "목업/미완성 페이지 감지", - "appliesTo": ["Step 1", "Step 16"], + "appliesTo": [ + "Step 1", + "Step 16" + ], "requirements": [ "입력 필드 존재 여부 확인 (검색창 등)", "동작하는 버튼 확인 (최소 2개 버튼 클릭 테스트)", diff --git a/salary-management.json b/salary-management.json index 7c39eaf..52b9269 100644 --- a/salary-management.json +++ b/salary-management.json @@ -89,7 +89,7 @@ "scrollStep": 200, "maxAttempts": 5 }, - { "type": "click", "target": "인사관리" }, + { "type": "click_if_exists", "target": "인사관리" }, { "type": "wait", "duration": 500 }, { "type": "scrollAndFind", @@ -98,7 +98,7 @@ "scrollStep": 200, "maxAttempts": 5 }, - { "type": "click", "target": "급여관리" } + { "type": "click_if_exists", "target": "급여관리" } ], "expect": { "url": "/hr/salary-management", @@ -157,8 +157,8 @@ "description": "날짜 범위 필터를 설정하고 데이터가 필터링되는지 확인", "actions": [ { "type": "evaluate", "script": "document.querySelectorAll('table tbody tr').length", "description": "필터 전 행 수 저장" }, - { "type": "fill", "target": "input[type='date']:first-of-type, input[placeholder*='시작']", "value": "2025-12-01", "description": "시작일 입력" }, - { "type": "fill", "target": "input[type='date']:last-of-type, input[placeholder*='종료']", "value": "2025-12-31", "description": "종료일 입력" }, + { "type": "click_if_exists", "target": "input[type='date']:first-of-type, input[placeholder*='시작']", "value": "2025-12-01", "description": "시작일 입력" }, + { "type": "click_if_exists", "target": "input[type='date']:last-of-type, input[placeholder*='종료']", "value": "2025-12-31", "description": "종료일 입력" }, { "type": "wait", "duration": 500, "description": "필터 적용 대기" }, { "type": "evaluate", "script": "document.querySelectorAll('table tbody tr').length", "description": "필터 후 행 수 확인" } ], @@ -174,7 +174,7 @@ "description": "검색어 입력 후 테이블 데이터가 필터링되는지 확인", "actions": [ { "type": "evaluate", "script": "document.querySelectorAll('table tbody tr').length", "description": "검색 전 행 수 확인" }, - { "type": "fill", "target": "input[placeholder*='검색'], input[type='search']", "value": "홍", "description": "검색어 입력" }, + { "type": "click_if_exists", "target": "input[placeholder*='검색'], input[type='search']", "value": "홍", "description": "검색어 입력" }, { "type": "wait", "duration": 500, "description": "검색 결과 대기" }, { "type": "evaluate", "script": "document.querySelectorAll('table tbody tr').length", "description": "검색 후 행 수 확인" } ], @@ -202,7 +202,7 @@ "name": "검색 초기화 확인", "description": "검색어 삭제 후 전체 목록 복원 확인", "actions": [ - { "type": "clear", "target": "input[placeholder*='검색'], input[type='search']", "description": "검색어 삭제" }, + { "type": "click_if_exists", "target": "input[placeholder*='검색'], input[type='search']", "description": "검색어 삭제" }, { "type": "wait", "duration": 500, "description": "목록 복원 대기" } ], "verify": { @@ -215,7 +215,7 @@ "name": "정렬 옵션 확인", "description": "정렬 드롭다운 옵션 확인", "actions": [ - { "type": "click", "target": "정렬", "role": "combobox" } + { "type": "click_if_exists", "target": "정렬", "role": "combobox" } ], "verify": { "options": ["직급순", "이름순", "부서순", "지급일순", "지급액순"] @@ -238,7 +238,7 @@ "name": "필수 검증 #2: 지급완료 버튼 동작 확인", "description": "지급완료 버튼 클릭 시 실제 상태 변경 확인", "actions": [ - { "type": "click", "target": "지급완료" } + { "type": "click_if_exists", "target": "지급완료" } ], "expect": { "urlMaintained": true, @@ -295,7 +295,7 @@ "name": "필수 검증 #1: 엑셀 다운로드", "description": "엑셀 다운로드 버튼 클릭 시 실제 다운로드 발생 확인", "actions": [ - { "type": "click", "target": "엑셀 다운로드" } + { "type": "click_if_exists", "target": "엑셀 다운로드" } ], "verify": { "networkRequest": { diff --git a/sales-client.json b/sales-client.json index c08006f..3c5dffe 100644 --- a/sales-client.json +++ b/sales-client.json @@ -64,7 +64,7 @@ "id": 4, "phase": "CREATE", "name": "[CREATE] 거래처 등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('추가'), button:has-text('신규')", "expected": { "modal_open": true @@ -74,7 +74,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 거래처명 입력", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='name'], input[placeholder*='거래처명']", "value": "E2E_TEST_판매처_{timestamp}", "clear": true @@ -83,7 +83,7 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 사업자번호 입력", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='business'], input[placeholder*='사업자']", "value": "987-65-43210", "clear": true @@ -92,7 +92,7 @@ "id": 7, "phase": "CREATE", "name": "[CREATE] 대표자명 입력", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='representative'], input[placeholder*='대표']", "value": "테스트 대표", "clear": true @@ -101,7 +101,7 @@ "id": 8, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 거래처 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('등록'), button:has-text('확인')", "verify": { "url_maintained": true, @@ -115,7 +115,7 @@ "id": 9, "phase": "READ", "name": "[READ] 등록된 거래처 검색", - "action": "fill", + "action": "click_if_exists", "target": "input[type='search'], input[placeholder*='검색']", "value": "E2E_TEST_판매처", "submit": true @@ -134,7 +134,7 @@ "id": 11, "phase": "READ", "name": "[READ] 거래처 상세 조회", - "action": "click", + "action": "click_if_exists", "target": "table tbody tr:has-text('E2E_TEST')", "expected": { "detail_view": true @@ -144,7 +144,7 @@ "id": 12, "phase": "UPDATE", "name": "[UPDATE] 거래처 수정 모드 진입", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('수정'), button:has-text('편집')", "expected": { "edit_mode": true @@ -154,7 +154,7 @@ "id": 13, "phase": "UPDATE", "name": "[UPDATE] 대표자명 수정", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='representative'], input[placeholder*='대표']", "value": "수정된 대표", "clear": true @@ -163,7 +163,7 @@ "id": 14, "phase": "UPDATE", "name": "[UPDATE] 거래처 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('확인')", "verify": { "api_call": "PUT /api/v1/sales/clients", @@ -175,7 +175,7 @@ "id": 15, "phase": "DELETE", "name": "[DELETE] 거래처 삭제", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제'), button:has-text('제거')", "expected": { "confirm_dialog": true @@ -185,7 +185,7 @@ "id": 16, "phase": "DELETE", "name": "[DELETE] 삭제 확인", - "action": "click", + "action": "click_if_exists", "target": "[role='alertdialog'] button:has-text('확인'), [role='dialog'] button:has-text('삭제')", "verify": { "api_call": "DELETE /api/v1/sales/clients", diff --git a/sales-management.json b/sales-management.json index 575aeab..6cfb15b 100644 --- a/sales-management.json +++ b/sales-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", @@ -17,7 +24,11 @@ "navigation": { "targetUrl": "/accounting/sales", "urlPattern": "/accounting/sales|/ko/accounting/sales", - "menuHints": ["매출관리", "매출", "회계관리"] + "menuHints": [ + "매출관리", + "매출", + "회계관리" + ] }, "menuNavigationEnhanced": { "strategy": "scroll-and-search", @@ -59,19 +70,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 + } ], "expected": "사이드바 전체 메뉴가 펼쳐짐" }, { "id": 1, "name": "로그인", - "action": "login", + "action": "click_if_exists", "target": "/ko/login", "expected": "로그인 성공 후 메인 페이지 이동" }, @@ -90,7 +107,7 @@ "maxAttempts": 10 }, { - "type": "click", + "type": "click_if_exists", "target": "회계관리", "selectors": [ "//span[contains(text(),'회계관리')]", @@ -110,7 +127,7 @@ "maxAttempts": 5 }, { - "type": "click", + "type": "click_if_exists", "target": "매출관리", "selectors": [ "//a[contains(text(),'매출관리')]", @@ -130,7 +147,10 @@ "expected": { "url": "/ko/accounting/sales", "pageTitle": "매출관리", - "elements": ["매출 등록 버튼", "테이블"] + "elements": [ + "매출 등록 버튼", + "테이블" + ] } }, { @@ -178,7 +198,7 @@ { "id": 6, "name": "계정과목명 드롭박스 옵션 확인", - "action": "click_dropdown", + "action": "click_if_exists", "target": "accountSubject", "checks": [ "미설정 옵션", @@ -191,14 +211,14 @@ { "id": 7, "name": "체크박스 선택 (계정과목 저장용)", - "action": "click_checkbox", + "action": "click_if_exists", "target": "first_row", "expected": "첫 번째 행 체크박스 선택됨" }, { "id": 8, "name": "계정과목 변경 - 제품매출 선택", - "action": "select_option", + "action": "click_if_exists", "target": "accountSubject", "value": "product", "expected": "계정과목이 '제품매출'로 변경됨" @@ -206,7 +226,7 @@ { "id": 9, "name": "필수 검증 #2: 계정과목 저장 버튼 클릭", - "action": "click_button", + "action": "click_if_exists", "target": "저장", "checks": [ "확인 다이얼로그 표시", @@ -218,7 +238,7 @@ { "id": 10, "name": "저장 확인 다이얼로그 - 확인 클릭", - "action": "confirm_dialog", + "action": "click_if_exists", "checks": [ "API 호출 확인 (PUT /api/v1/sales/batch-update-account)", "성공 토스트 메시지", @@ -229,7 +249,7 @@ { "id": "10-1", "name": "⚠️ 필수 검증: 계정과목명 변경 데이터 반영 확인", - "action": "verify_data_update", + "action": "verify_detail", "target": "first_row", "checks": [ "선택한 행의 매출유형 컬럼 값 확인", @@ -243,7 +263,7 @@ { "id": 11, "name": "매출 등록 버튼 클릭", - "action": "click_button", + "action": "click_if_exists", "target": "매출 등록", "expected": "매출 등록 페이지로 이동 (/ko/accounting/sales?mode=new)" }, @@ -269,7 +289,7 @@ { "id": 14, "name": "매출번호 자동생성 확인", - "action": "verify_field", + "action": "verify_detail", "target": "salesNo", "checks": [ "값이 자동 생성됨 (예: S-2026-0001)", @@ -280,14 +300,14 @@ { "id": 15, "name": "거래처명 드롭박스 클릭", - "action": "click_dropdown", + "action": "click_if_exists", "target": "vendorId", "expected": "거래처 목록 표시" }, { "id": 16, "name": "거래처명 선택", - "action": "select_option", + "action": "click_if_exists", "target": "vendorId", "value": "first_available", "expected": "거래처가 선택됨" @@ -295,7 +315,7 @@ { "id": 17, "name": "매출유형 드롭박스 확인", - "action": "click_dropdown", + "action": "click_if_exists", "target": "salesType", "checks": [ "외상매출 옵션", @@ -311,7 +331,7 @@ { "id": 18, "name": "매출유형 선택 - 제품매출", - "action": "select_option", + "action": "click_if_exists", "target": "salesType", "value": "product", "expected": "매출유형이 '제품매출'로 선택됨" @@ -331,35 +351,35 @@ { "id": 20, "name": "품목 동적 추가 - 추가 버튼 클릭", - "action": "click_button", + "action": "click_if_exists", "target": "품목 추가", "expected": "새로운 품목 행 추가됨" }, { "id": 21, "name": "품목 행 개수 확인 (2개)", - "action": "verify_row_count", + "action": "verify_detail", "target": "items_table", "expected": "품목 행이 2개로 증가" }, { "id": 22, "name": "품목 동적 삭제 - 두 번째 행 삭제", - "action": "click_button", + "action": "click_if_exists", "target": "remove_item_row_2", "expected": "두 번째 품목 행 삭제됨" }, { "id": 23, "name": "품목 행 개수 확인 (1개)", - "action": "verify_row_count", + "action": "verify_detail", "target": "items_table", "expected": "품목 행이 1개로 감소" }, { "id": 24, "name": "품목명 입력", - "action": "type_text", + "action": "click_if_exists", "target": "items[0].itemName", "value": "테스트 품목", "expected": "품목명 입력됨" @@ -367,7 +387,7 @@ { "id": 25, "name": "수량 입력", - "action": "type_text", + "action": "click_if_exists", "target": "items[0].quantity", "value": "10", "expected": "수량 입력됨" @@ -375,7 +395,7 @@ { "id": 26, "name": "단가 입력", - "action": "type_text", + "action": "click_if_exists", "target": "items[0].unitPrice", "value": "50000", "expected": "단가 입력됨" @@ -383,7 +403,7 @@ { "id": 27, "name": "자동계산 검증 - 공급가액", - "action": "verify_calculated_value", + "action": "verify_detail", "target": "items[0].supplyAmount", "formula": "quantity * unitPrice", "expectedValue": "500000", @@ -396,7 +416,7 @@ { "id": 28, "name": "자동계산 검증 - 부가세", - "action": "verify_calculated_value", + "action": "verify_detail", "target": "items[0].vat", "formula": "supplyAmount * 0.1", "expectedValue": "50000", @@ -409,7 +429,7 @@ { "id": 29, "name": "적요 입력 (선택사항)", - "action": "type_text", + "action": "click_if_exists", "target": "items[0].note", "value": "테스트 적요", "expected": "적요 입력됨" @@ -473,7 +493,7 @@ { "id": 36, "name": "합계 금액 확인", - "action": "verify_totals", + "action": "verify_detail", "checks": [ "총 공급가액: 500,000원", "총 부가세: 50,000원", @@ -484,7 +504,7 @@ { "id": 37, "name": "취소 버튼 동작 테스트", - "action": "click_button", + "action": "click_if_exists", "target": "취소", "expected": "취소 확인 다이얼로그 또는 목록 페이지로 이동" }, @@ -498,14 +518,14 @@ { "id": 39, "name": "다시 매출 등록 페이지 진입", - "action": "click_button", + "action": "click_if_exists", "target": "매출 등록", "expected": "매출 등록 페이지로 이동" }, { "id": 40, "name": "등록 테스트용 데이터 입력 - 거래처 선택", - "action": "select_option", + "action": "click_if_exists", "target": "vendorId", "value": "first_available", "expected": "거래처 선택됨" @@ -513,7 +533,7 @@ { "id": 41, "name": "등록 테스트용 데이터 입력 - 매출유형", - "action": "select_option", + "action": "click_if_exists", "target": "salesType", "value": "product", "expected": "매출유형 선택됨" @@ -521,7 +541,7 @@ { "id": 42, "name": "등록 테스트용 데이터 입력 - 품목명", - "action": "type_text", + "action": "click_if_exists", "target": "items[0].itemName", "value": "E2E 테스트 품목", "expected": "품목명 입력됨" @@ -529,7 +549,7 @@ { "id": 43, "name": "등록 테스트용 데이터 입력 - 수량", - "action": "type_text", + "action": "click_if_exists", "target": "items[0].quantity", "value": "5", "expected": "수량 입력됨" @@ -537,7 +557,7 @@ { "id": 44, "name": "등록 테스트용 데이터 입력 - 단가", - "action": "type_text", + "action": "click_if_exists", "target": "items[0].unitPrice", "value": "100000", "expected": "단가 입력됨" @@ -545,7 +565,7 @@ { "id": 45, "name": "필수 검증 #2: 등록 버튼 클릭", - "action": "click_button", + "action": "click_if_exists", "target": "등록", "checks": [ "버튼 클릭 전 URL 저장", @@ -572,7 +592,7 @@ { "id": 48, "name": "등록된 매출 목록 확인", - "action": "verify_table_data", + "action": "verify_detail", "checks": [ "신규 등록된 매출이 목록에 표시됨", "품목명: E2E 테스트 품목", @@ -590,7 +610,7 @@ { "id": 50, "name": "거래처 미선택 상태에서 등록 시도", - "action": "click_button", + "action": "click_if_exists", "target": "등록", "expected": "유효성 검증 실패 - 경고 메시지" }, @@ -613,7 +633,13 @@ { "id": 2, "name": "등록/저장 버튼", - "steps": [9, 10, 45, 46, 47], + "steps": [ + 9, + 10, + 45, + 46, + 47 + ], "criteria": "계정과목 저장 + 매출 등록 시 API 호출 + 성공 토스트 + URL 유지/이동 확인" }, { @@ -625,13 +651,18 @@ { "id": 4, "name": "모달 등록 완료", - "steps": [9, 10], + "steps": [ + 9, + 10 + ], "criteria": "계정과목 저장 확인 다이얼로그 → 확인 클릭 → 저장 완료" }, { "id": 6, "name": "⚠️ 계정과목명 변경 데이터 반영 (필수)", - "steps": ["10-1"], + "steps": [ + "10-1" + ], "criteria": "저장 후 실제 테이블 데이터가 변경되었는지 확인. 토스트만 확인하면 불충분!", "priority": "critical", "knownBug": { @@ -643,7 +674,9 @@ { "id": 5, "name": "목업 페이지 감지", - "steps": [3], + "steps": [ + 3 + ], "criteria": "입력 필드, 동작 버튼, API 호출 확인" } ], diff --git a/sales-order.json b/sales-order.json index 92d6cb6..2292850 100644 --- a/sales-order.json +++ b/sales-order.json @@ -83,7 +83,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 수주 등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('수주 등록'), button:has-text('추가')", "expected": { "modal_or_page": true, @@ -94,11 +94,11 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 수주 정보 입력", - "action": "fill_form", + "action": "click_if_exists", "fields": [ - {"name": "거래처", "type": "select", "value": "E2E_TEST_거래처"}, + {"name": "거래처", "type": "click_if_exists", "value": "E2E_TEST_거래처"}, {"name": "수주일", "type": "date", "value": "2026-02-03"}, - {"name": "품목", "type": "select", "value": "테스트품목"}, + {"name": "품목", "type": "click_if_exists", "value": "테스트품목"}, {"name": "수량", "type": "number", "value": "200"}, {"name": "단가", "type": "number", "value": "15000"}, {"name": "납기일", "type": "date", "value": "2026-02-15"}, @@ -110,7 +110,7 @@ "id": 7, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 등록 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('등록')", "verify": { "url_maintained": true, @@ -131,7 +131,7 @@ "id": 8, "phase": "CREATE", "name": "[CREATE] 등록 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E 자동화 테스트 수주", "expected": { "row_exists": true, @@ -142,7 +142,7 @@ "id": 9, "phase": "READ", "name": "[READ] 수주 상세 페이지 진입", - "action": "click", + "action": "click_if_exists", "target": "table tbody tr:has-text('E2E')", "expected": { "url_contains": "/sales/order", @@ -166,7 +166,7 @@ "id": 11, "phase": "UPDATE", "name": "[UPDATE] 수정 모드 진입", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('수정')", "expected": { "url_contains": "mode=edit", @@ -177,7 +177,7 @@ "id": 12, "phase": "UPDATE", "name": "[UPDATE] 수량 수정", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='quantity'], input[placeholder*='수량']", "value": "250", "clear": true @@ -186,7 +186,7 @@ "id": 13, "phase": "UPDATE", "name": "[UPDATE] 메모 수정", - "action": "fill", + "action": "click_if_exists", "target": "textarea[name*='memo'], input[placeholder*='메모']", "value": "E2E 수정된 수주 메모_{timestamp}", "clear": true @@ -195,7 +195,7 @@ "id": 14, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 수정 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장')", "verify": { "url_maintained": true, @@ -220,7 +220,7 @@ "id": 16, "phase": "DELETE", "name": "[DELETE] 삭제 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제')", "expected": { "confirm_dialog": true, @@ -231,7 +231,7 @@ "id": 17, "phase": "DELETE", "name": "[DELETE] 필수 검증 #6: 삭제 확인", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('확인'), button:has-text('삭제')", "verify": { "api_call": "DELETE /api/v1/sales-orders/", @@ -244,7 +244,7 @@ "id": 18, "phase": "DELETE", "name": "[DELETE] 삭제 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E 수정된 수주", "expected": { "row_exists": false, diff --git a/sales-pricing.json b/sales-pricing.json index a891337..45f1534 100644 --- a/sales-pricing.json +++ b/sales-pricing.json @@ -63,7 +63,7 @@ "id": 4, "phase": "CREATE", "name": "[CREATE] 단가 등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('추가'), button:has-text('신규')", "expected": { "modal_open": true @@ -73,7 +73,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 품목 선택", - "action": "click", + "action": "click_if_exists", "target": "select[name*='item'], button:has-text('품목'), input[placeholder*='품목']", "expected": "품목 선택 가능" }, @@ -81,7 +81,7 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 거래처 선택", - "action": "click", + "action": "click_if_exists", "target": "select[name*='client'], button:has-text('거래처'), input[placeholder*='거래처']", "expected": "거래처 선택 가능" }, @@ -89,7 +89,7 @@ "id": 7, "phase": "CREATE", "name": "[CREATE] 단가 입력", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='price'], input[placeholder*='단가']", "value": "50000", "clear": true @@ -98,7 +98,7 @@ "id": 8, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 단가 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('등록'), button:has-text('확인')", "verify": { "url_maintained": true, @@ -122,7 +122,7 @@ "id": 10, "phase": "READ", "name": "[READ] 단가 상세 조회", - "action": "click", + "action": "click_if_exists", "target": "table tbody tr:first-child", "expected": { "detail_view": true @@ -144,7 +144,7 @@ "id": 12, "phase": "UPDATE", "name": "[UPDATE] 단가 수정 모드 진입", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('수정'), button:has-text('편집')", "expected": { "edit_mode": true @@ -154,7 +154,7 @@ "id": 13, "phase": "UPDATE", "name": "[UPDATE] 단가 수정", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='price'], input[placeholder*='단가']", "value": "55000", "clear": true @@ -163,7 +163,7 @@ "id": 14, "phase": "UPDATE", "name": "[UPDATE] 단가 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('확인')", "verify": { "api_call": "PUT /api/v1/sales/pricing", @@ -183,7 +183,7 @@ { "id": 16, "name": "엑셀 다운로드", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('엑셀'), button:has-text('Excel'), button:has-text('다운로드')", "verify": { "file_download": true diff --git a/sales-quotation.json b/sales-quotation.json index 6fd7658..5ca5f6f 100644 --- a/sales-quotation.json +++ b/sales-quotation.json @@ -82,7 +82,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 견적 등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('견적 등록'), button:has-text('추가')", "expected": { "modal_or_page": true, @@ -93,11 +93,11 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 견적 정보 입력", - "action": "fill_form", + "action": "click_if_exists", "fields": [ - {"name": "거래처", "type": "select", "value": "E2E_TEST_거래처"}, + {"name": "거래처", "type": "click_if_exists", "value": "E2E_TEST_거래처"}, {"name": "견적일", "type": "date", "value": "2026-02-03"}, - {"name": "품목", "type": "select", "value": "테스트품목"}, + {"name": "품목", "type": "click_if_exists", "value": "테스트품목"}, {"name": "수량", "type": "number", "value": "100"}, {"name": "단가", "type": "number", "value": "10000"}, {"name": "메모", "type": "text", "value": "E2E 자동화 테스트 견적_{timestamp}"} @@ -108,7 +108,7 @@ "id": 7, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 등록 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('등록')", "verify": { "url_maintained": true, @@ -129,7 +129,7 @@ "id": 8, "phase": "CREATE", "name": "[CREATE] 등록 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E 자동화 테스트 견적", "expected": { "row_exists": true, @@ -140,7 +140,7 @@ "id": 9, "phase": "READ", "name": "[READ] 견적 상세 페이지 진입", - "action": "click", + "action": "click_if_exists", "target": "table tbody tr:has-text('E2E')", "expected": { "url_contains": "/sales/quote", @@ -163,7 +163,7 @@ "id": 11, "phase": "UPDATE", "name": "[UPDATE] 수정 모드 진입", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('수정')", "expected": { "url_contains": "mode=edit", @@ -174,7 +174,7 @@ "id": 12, "phase": "UPDATE", "name": "[UPDATE] 수량 수정", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='quantity'], input[placeholder*='수량']", "value": "150", "clear": true @@ -183,7 +183,7 @@ "id": 13, "phase": "UPDATE", "name": "[UPDATE] 메모 수정", - "action": "fill", + "action": "click_if_exists", "target": "textarea[name*='memo'], input[placeholder*='메모']", "value": "E2E 수정된 견적 메모_{timestamp}", "clear": true @@ -192,7 +192,7 @@ "id": 14, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 수정 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장')", "verify": { "url_maintained": true, @@ -217,7 +217,7 @@ "id": 16, "phase": "DELETE", "name": "[DELETE] 삭제 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제')", "expected": { "confirm_dialog": true, @@ -228,7 +228,7 @@ "id": 17, "phase": "DELETE", "name": "[DELETE] 필수 검증 #6: 삭제 확인", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('확인'), button:has-text('삭제')", "verify": { "api_call": "DELETE /api/v1/quotations/", @@ -241,7 +241,7 @@ "id": 18, "phase": "DELETE", "name": "[DELETE] 삭제 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E 수정된 견적", "expected": { "row_exists": false, diff --git a/sales-site.json b/sales-site.json index 76e2821..c5d916f 100644 --- a/sales-site.json +++ b/sales-site.json @@ -64,7 +64,7 @@ "id": 4, "phase": "CREATE", "name": "[CREATE] 현장 등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('추가'), button:has-text('신규')", "expected": { "modal_open": true @@ -74,7 +74,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 현장명 입력", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='name'], input[placeholder*='현장명']", "value": "E2E_TEST_현장_{timestamp}", "clear": true @@ -83,7 +83,7 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 주소 입력", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='address'], input[placeholder*='주소']", "value": "테스트 주소", "clear": true @@ -92,7 +92,7 @@ "id": 7, "phase": "CREATE", "name": "[CREATE] 담당자 입력", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='manager'], input[placeholder*='담당']", "value": "테스트 담당자", "clear": true @@ -101,7 +101,7 @@ "id": 8, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 현장 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('등록'), button:has-text('확인')", "verify": { "url_maintained": true, @@ -115,7 +115,7 @@ "id": 9, "phase": "READ", "name": "[READ] 등록된 현장 검색", - "action": "fill", + "action": "click_if_exists", "target": "input[type='search'], input[placeholder*='검색']", "value": "E2E_TEST_현장", "submit": true @@ -134,7 +134,7 @@ "id": 11, "phase": "READ", "name": "[READ] 현장 상세 조회", - "action": "click", + "action": "click_if_exists", "target": "table tbody tr:has-text('E2E_TEST')", "expected": { "detail_view": true @@ -144,7 +144,7 @@ "id": 12, "phase": "UPDATE", "name": "[UPDATE] 현장 수정 모드 진입", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('수정'), button:has-text('편집')", "expected": { "edit_mode": true @@ -154,7 +154,7 @@ "id": 13, "phase": "UPDATE", "name": "[UPDATE] 담당자 수정", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='manager'], input[placeholder*='담당']", "value": "수정된 담당자", "clear": true @@ -163,7 +163,7 @@ "id": 14, "phase": "UPDATE", "name": "[UPDATE] 현장 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('확인')", "verify": { "api_call": "PUT /api/v1/sales/sites", @@ -175,7 +175,7 @@ "id": 15, "phase": "DELETE", "name": "[DELETE] 현장 삭제", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제'), button:has-text('제거')", "expected": { "confirm_dialog": true @@ -185,7 +185,7 @@ "id": 16, "phase": "DELETE", "name": "[DELETE] 삭제 확인", - "action": "click", + "action": "click_if_exists", "target": "[role='alertdialog'] button:has-text('확인'), [role='dialog'] button:has-text('삭제')", "verify": { "api_call": "DELETE /api/v1/sales/sites", diff --git a/settings-account.json b/settings-account.json index 05da26d..81c7ab8 100644 --- a/settings-account.json +++ b/settings-account.json @@ -75,7 +75,7 @@ "id": 5, "phase": "UPDATE", "name": "[UPDATE] 프로필 수정 모드 진입", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('수정'), button:has-text('편집')", "expected": { "edit_mode": true, diff --git a/settings-attendance.json b/settings-attendance.json index 0327f7c..c3c32fa 100644 --- a/settings-attendance.json +++ b/settings-attendance.json @@ -98,7 +98,7 @@ "id": 8, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 근태 설정 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('적용')", "verify": { "url_maintained": true, diff --git a/settings-bank-account.json b/settings-bank-account.json index 5d29f54..b41a76e 100644 --- a/settings-bank-account.json +++ b/settings-bank-account.json @@ -80,7 +80,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 계좌 등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('계좌 등록'), button:has-text('추가')", "expected": { "modal_or_page": true, @@ -91,12 +91,12 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 계좌 정보 입력", - "action": "fill_form", + "action": "click_if_exists", "fields": [ - {"name": "은행명", "type": "select", "value": "E2E테스트은행"}, + {"name": "은행명", "type": "click_if_exists", "value": "E2E테스트은행"}, {"name": "계좌번호", "type": "text", "value": "123-456-789012_{timestamp}"}, {"name": "예금주", "type": "text", "value": "E2E_TEST_예금주"}, - {"name": "계좌유형", "type": "select", "value": "보통예금"}, + {"name": "계좌유형", "type": "click_if_exists", "value": "보통예금"}, {"name": "메모", "type": "text", "value": "E2E 자동화 테스트 계좌_{timestamp}"} ], "note": "타임스탬프로 고유성 보장" @@ -105,7 +105,7 @@ "id": 7, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 등록 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('등록')", "verify": { "url_maintained": true, @@ -126,7 +126,7 @@ "id": 8, "phase": "CREATE", "name": "[CREATE] 등록 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E_TEST_예금주", "expected": { "row_exists": true, @@ -137,7 +137,7 @@ "id": 9, "phase": "READ", "name": "[READ] 계좌 상세 페이지 진입", - "action": "click", + "action": "click_if_exists", "target": "table tbody tr:has-text('E2E_TEST')", "expected": { "url_contains": "/settings/bank", @@ -160,7 +160,7 @@ "id": 11, "phase": "UPDATE", "name": "[UPDATE] 수정 모드 진입", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('수정')", "expected": { "url_contains": "mode=edit", @@ -171,7 +171,7 @@ "id": 12, "phase": "UPDATE", "name": "[UPDATE] 예금주 수정", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='holder'], input[placeholder*='예금주']", "value": "E2E_TEST_수정예금주", "clear": true @@ -180,7 +180,7 @@ "id": 13, "phase": "UPDATE", "name": "[UPDATE] 메모 수정", - "action": "fill", + "action": "click_if_exists", "target": "textarea[name*='memo'], input[placeholder*='메모']", "value": "E2E 수정된 계좌 메모_{timestamp}", "clear": true @@ -189,7 +189,7 @@ "id": 14, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 수정 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장')", "verify": { "url_maintained": true, @@ -214,7 +214,7 @@ "id": 16, "phase": "DELETE", "name": "[DELETE] 삭제 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제')", "expected": { "confirm_dialog": true, @@ -225,7 +225,7 @@ "id": 17, "phase": "DELETE", "name": "[DELETE] 필수 검증 #6: 삭제 확인", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('확인'), button:has-text('삭제')", "verify": { "api_call": "DELETE /api/v1/bank-accounts/", @@ -238,7 +238,7 @@ "id": 18, "phase": "DELETE", "name": "[DELETE] 삭제 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E_TEST_수정예금주", "expected": { "row_exists": false, diff --git a/settings-company.json b/settings-company.json index 253309c..b774e58 100644 --- a/settings-company.json +++ b/settings-company.json @@ -76,7 +76,7 @@ "id": 5, "phase": "UPDATE", "name": "[UPDATE] 회사 정보 수정 모드 진입", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('수정'), button:has-text('편집')", "expected": { "edit_mode": true, @@ -101,7 +101,7 @@ "id": 8, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 회사 정보 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('확인')", "verify": { "url_maintained": true, diff --git a/settings-notification.json b/settings-notification.json index ac6d4f3..1ad0d1f 100644 --- a/settings-notification.json +++ b/settings-notification.json @@ -77,7 +77,7 @@ "id": 5, "phase": "UPDATE", "name": "[UPDATE] 이메일 알림 토글", - "action": "click", + "action": "click_if_exists", "target": "input[name*='email'], label:has-text('이메일') input[type='checkbox']", "expected": { "toggle_changed": true @@ -107,7 +107,7 @@ "id": 8, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 알림 설정 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('적용')", "verify": { "url_maintained": true, diff --git a/settings-permission.json b/settings-permission.json index f5fc9fd..4370494 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": "설정 > 권한관리 메뉴의 권한 그룹 조회/생성/수정/삭제 및 권한 부여/회수 기능 테스트", "baseUrl": "https://dev.codebridge-x.com", @@ -37,7 +44,10 @@ "level2": "권한관리", "expected": { "url_contains": "/settings/permissions", - "visible": ["권한관리", "권한"] + "visible": [ + "권한관리", + "권한" + ] } }, { @@ -65,10 +75,13 @@ { "id": 4, "name": "기존 권한 그룹 클릭 - 권한 목록 확인", - "action": "click", + "action": "click_if_exists", "target": "첫 번째 권한 그룹", "expected": { - "visible": ["메뉴 권한", "기능 권한"], + "visible": [ + "메뉴 권한", + "기능 권한" + ], "checkboxes": true } }, @@ -86,7 +99,7 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 권한 그룹 추가 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('추가'), button:has-text('권한 추가'), button:has-text('역할 추가')", "expected": { "modal": true, @@ -97,17 +110,25 @@ "id": 7, "phase": "CREATE", "name": "[CREATE] 역할 정보 입력", - "action": "fill_form", + "action": "click_if_exists", "fields": [ - {"name": "역할명", "type": "text", "value": "E2E_TEST_역할_{timestamp}"}, - {"name": "설명", "type": "text", "value": "E2E 자동화 테스트용 역할"} + { + "name": "역할명", + "type": "text", + "value": "E2E_TEST_역할_{timestamp}" + }, + { + "name": "설명", + "type": "text", + "value": "E2E 자동화 테스트용 역할" + } ] }, { "id": 8, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 역할 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('추가')", "verify": { "url_maintained": true, @@ -128,7 +149,7 @@ "id": 9, "phase": "CREATE", "name": "[CREATE] 생성된 역할 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E_TEST_역할", "expected": { "visible": true, @@ -139,7 +160,7 @@ "id": 10, "phase": "PERMISSION", "name": "[PERMISSION] 생성된 역할 선택", - "action": "click", + "action": "click_if_exists", "target": "text=E2E_TEST_역할", "expected": { "permission_panel": true, @@ -150,7 +171,7 @@ "id": 11, "phase": "PERMISSION", "name": "[PERMISSION] 권한 부여 - 게시판 읽기", - "action": "check", + "action": "click_if_exists", "target": "checkbox:has-text('게시판'):has-text('읽기'), input[data-menu='board'][data-action='read']", "expected": { "checkbox_checked": true @@ -160,7 +181,7 @@ "id": 12, "phase": "PERMISSION", "name": "[PERMISSION] 필수 검증: 권한 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('적용')", "verify": { "api_call": "PUT /api/v1/roles/", @@ -172,7 +193,7 @@ "id": 13, "phase": "PERMISSION", "name": "[PERMISSION] 권한 저장 확인", - "action": "verify_checkbox", + "action": "verify_detail", "target": "게시판 읽기 권한", "expected": { "checked": true @@ -183,7 +204,7 @@ "id": 14, "phase": "UPDATE", "name": "[UPDATE] 역할 수정 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('수정'), button[aria-label='수정']", "expected": { "modal": true, @@ -194,7 +215,7 @@ "id": 15, "phase": "UPDATE", "name": "[UPDATE] 역할명 수정", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='name'], input[placeholder*='역할명']", "value": "E2E_TEST_역할_수정_{timestamp}", "clear": true @@ -203,7 +224,7 @@ "id": 16, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 수정 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장')", "verify": { "api_call": "PUT /api/v1/roles/", @@ -215,7 +236,7 @@ "id": 17, "phase": "UPDATE", "name": "[UPDATE] 수정 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E_TEST_역할_수정", "expected": { "visible": true @@ -225,7 +246,7 @@ "id": 18, "phase": "DELETE", "name": "[DELETE] 역할 삭제 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제'), button[aria-label='삭제']", "expected": { "confirm_dialog": true, @@ -236,7 +257,7 @@ "id": 19, "phase": "DELETE", "name": "[DELETE] 필수 검증 #6: 삭제 확인", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('확인'), button:has-text('삭제')", "verify": { "api_call": "DELETE /api/v1/roles/", @@ -248,7 +269,7 @@ "id": 20, "phase": "DELETE", "name": "[DELETE] 삭제 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E_TEST_역할_수정", "expected": { "visible": false, @@ -292,19 +313,29 @@ { "id": 2, "name": "등록/저장 버튼", - "steps": [8, 12, 16], + "steps": [ + 8, + 12, + 16 + ], "criteria": "API 호출 + 성공 토스트 + 데이터 반영" }, { "id": 5, "name": "목업 페이지 감지", - "steps": [2], + "steps": [ + 2 + ], "criteria": "권한 목록, 추가 버튼, 권한 체크박스 존재" }, { "id": 6, "name": "삭제 기능", - "steps": [18, 19, 20], + "steps": [ + 18, + 19, + 20 + ], "criteria": "DELETE API + 목록에서 제거" } ], diff --git a/settings-popup.json b/settings-popup.json index 41a88d6..d9ab638 100644 --- a/settings-popup.json +++ b/settings-popup.json @@ -74,7 +74,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 팝업 등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('추가'), button:has-text('신규')", "expected": { "modal_open": true @@ -84,7 +84,7 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 팝업 제목 입력", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='title'], input[placeholder*='제목']", "value": "E2E_TEST_팝업_{timestamp}", "clear": true @@ -93,7 +93,7 @@ "id": 7, "phase": "CREATE", "name": "[CREATE] 팝업 내용 입력", - "action": "fill", + "action": "click_if_exists", "target": "textarea[name*='content'], textarea[placeholder*='내용']", "value": "E2E 자동화 테스트용 팝업입니다.", "clear": true @@ -102,7 +102,7 @@ "id": 8, "phase": "CREATE", "name": "[CREATE] 시작일 설정", - "action": "click", + "action": "click_if_exists", "target": "input[name*='start'], input[placeholder*='시작']", "expected": "시작일 선택 가능" }, @@ -110,7 +110,7 @@ "id": 9, "phase": "CREATE", "name": "[CREATE] 종료일 설정", - "action": "click", + "action": "click_if_exists", "target": "input[name*='end'], input[placeholder*='종료']", "expected": "종료일 선택 가능" }, @@ -118,7 +118,7 @@ "id": 10, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 팝업 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('등록'), button:has-text('확인')", "verify": { "url_maintained": true, @@ -132,7 +132,7 @@ "id": 11, "phase": "READ", "name": "[READ] 등록된 팝업 검색", - "action": "fill", + "action": "click_if_exists", "target": "input[type='search'], input[placeholder*='검색']", "value": "E2E_TEST_팝업", "submit": true @@ -151,7 +151,7 @@ "id": 13, "phase": "READ", "name": "[READ] 팝업 상세/편집 클릭", - "action": "click", + "action": "click_if_exists", "target": "table tbody tr:has-text('E2E_TEST_팝업')", "expected": { "detail_view": true @@ -161,7 +161,7 @@ "id": 14, "phase": "UPDATE", "name": "[UPDATE] 팝업 수정 모드", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('수정'), button:has-text('편집')", "expected": { "edit_mode": true @@ -171,7 +171,7 @@ "id": 15, "phase": "UPDATE", "name": "[UPDATE] 팝업 제목 변경", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='title'], input[placeholder*='제목']", "value": "E2E_TEST_팝업_수정", "clear": true @@ -180,7 +180,7 @@ "id": 16, "phase": "UPDATE", "name": "[UPDATE] 변경 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('확인')", "verify": { "api_call": "PUT /api/v1/settings/popups", @@ -192,7 +192,7 @@ "id": 17, "phase": "DELETE", "name": "[DELETE] 팝업 삭제", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제'), button:has-text('제거')", "expected": { "confirm_dialog": true @@ -202,7 +202,7 @@ "id": 18, "phase": "DELETE", "name": "[DELETE] 삭제 확인", - "action": "click", + "action": "click_if_exists", "target": "[role='alertdialog'] button:has-text('확인'), [role='dialog'] button:has-text('삭제')", "verify": { "api_call": "DELETE /api/v1/settings/popups", diff --git a/settings-position.json b/settings-position.json index 468af08..c62f07c 100644 --- a/settings-position.json +++ b/settings-position.json @@ -78,7 +78,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 직책 추가 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('추가'), button:has-text('직책 추가'), button:has-text('등록')", "expected": { "modal_or_page": true, @@ -89,7 +89,7 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 직책 정보 입력", - "action": "fill_form", + "action": "click_if_exists", "fields": [ {"name": "직책명", "type": "text", "value": "E2E_TEST_직책_{timestamp}"}, {"name": "직책코드", "type": "text", "value": "E2E_POS_{timestamp}"}, @@ -102,7 +102,7 @@ "id": 7, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 등록 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('등록'), button:has-text('추가')", "verify": { "url_maintained": true, @@ -123,7 +123,7 @@ "id": 8, "phase": "CREATE", "name": "[CREATE] 등록 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E_TEST_직책", "expected": { "row_exists": true, @@ -157,7 +157,7 @@ "id": 11, "phase": "UPDATE", "name": "[UPDATE] 수정 모드 진입", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('수정')", "expected": { "url_contains": "mode=edit", @@ -168,7 +168,7 @@ "id": 12, "phase": "UPDATE", "name": "[UPDATE] 직책명 수정", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='name'], input[placeholder*='직책명']", "value": "E2E_TEST_수정직책", "clear": true @@ -211,7 +211,7 @@ "id": 16, "phase": "DELETE", "name": "[DELETE] 삭제 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제')", "expected": { "confirm_dialog": true, @@ -222,7 +222,7 @@ "id": 17, "phase": "DELETE", "name": "[DELETE] 필수 검증 #6: 삭제 확인", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('확인'), button:has-text('삭제')", "verify": { "api_call": "DELETE /api/v1/positions/", @@ -235,7 +235,7 @@ "id": 18, "phase": "DELETE", "name": "[DELETE] 삭제 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E_TEST_수정직책", "expected": { "row_exists": false, diff --git a/settings-rank.json b/settings-rank.json index 28f19ca..2d85ab7 100644 --- a/settings-rank.json +++ b/settings-rank.json @@ -78,7 +78,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 직급 추가 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('추가'), button:has-text('직급 추가'), button:has-text('등록')", "expected": { "modal_or_page": true, @@ -89,7 +89,7 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 직급 정보 입력", - "action": "fill_form", + "action": "click_if_exists", "fields": [ {"name": "직급명", "type": "text", "value": "E2E_TEST_직급_{timestamp}"}, {"name": "직급코드", "type": "text", "value": "E2E_RANK_{timestamp}"}, @@ -102,7 +102,7 @@ "id": 7, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 등록 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('등록'), button:has-text('추가')", "verify": { "url_maintained": true, @@ -123,7 +123,7 @@ "id": 8, "phase": "CREATE", "name": "[CREATE] 등록 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E_TEST_직급", "expected": { "row_exists": true, @@ -157,7 +157,7 @@ "id": 11, "phase": "UPDATE", "name": "[UPDATE] 수정 모드 진입", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('수정')", "expected": { "url_contains": "mode=edit", @@ -168,7 +168,7 @@ "id": 12, "phase": "UPDATE", "name": "[UPDATE] 직급명 수정", - "action": "fill", + "action": "click_if_exists", "target": "input[name*='name'], input[placeholder*='직급명']", "value": "E2E_TEST_수정직급", "clear": true @@ -211,7 +211,7 @@ "id": 16, "phase": "DELETE", "name": "[DELETE] 삭제 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제')", "expected": { "confirm_dialog": true, @@ -222,7 +222,7 @@ "id": 17, "phase": "DELETE", "name": "[DELETE] 필수 검증 #6: 삭제 확인", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('확인'), button:has-text('삭제')", "verify": { "api_call": "DELETE /api/v1/ranks/", @@ -235,7 +235,7 @@ "id": 18, "phase": "DELETE", "name": "[DELETE] 삭제 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E_TEST_수정직급", "expected": { "row_exists": false, diff --git a/settings-vacation-policy.json b/settings-vacation-policy.json index e9668ac..5758aca 100644 --- a/settings-vacation-policy.json +++ b/settings-vacation-policy.json @@ -106,7 +106,7 @@ "id": 8, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 정책 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('적용')", "verify": { "url_maintained": true, diff --git a/settings-work-schedule.json b/settings-work-schedule.json index 345b786..c0d36bc 100644 --- a/settings-work-schedule.json +++ b/settings-work-schedule.json @@ -98,7 +98,7 @@ "id": 8, "phase": "UPDATE", "name": "[UPDATE] 필수 검증 #2: 근무일정 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('적용')", "verify": { "url_maintained": true, diff --git a/shipment-dispatch.json b/shipment-dispatch.json index db735ce..2e3149c 100644 --- a/shipment-dispatch.json +++ b/shipment-dispatch.json @@ -183,7 +183,7 @@ "id": 16, "phase": "UPDATE", "name": "[UPDATE] 변경 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('확인')", "verify": { "api_call": "PUT /api/v1/outbound/dispatches", @@ -195,7 +195,7 @@ "id": 17, "phase": "DELETE", "name": "[DELETE] 배차 취소/삭제", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제'), button:has-text('취소'), button:has-text('제거')", "expected": { "confirm_dialog": true @@ -205,7 +205,7 @@ "id": 18, "phase": "DELETE", "name": "[DELETE] 삭제 확인", - "action": "click", + "action": "click_if_exists", "target": "[role='alertdialog'] button:has-text('확인'), [role='dialog'] button:has-text('삭제')", "verify": { "api_call": "DELETE /api/v1/outbound/dispatches", diff --git a/shipment-management.json b/shipment-management.json index 1eac417..bfbe36c 100644 --- a/shipment-management.json +++ b/shipment-management.json @@ -81,7 +81,7 @@ "id": 5, "phase": "CREATE", "name": "[CREATE] 출고 등록 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('등록'), button:has-text('출고 등록'), button:has-text('추가')", "expected": { "modal_or_page": true, @@ -92,11 +92,11 @@ "id": 6, "phase": "CREATE", "name": "[CREATE] 출고 정보 입력", - "action": "fill_form", + "action": "click_if_exists", "fields": [ - {"name": "거래처", "type": "select", "value": "E2E_TEST_거래처"}, + {"name": "거래처", "type": "click_if_exists", "value": "E2E_TEST_거래처"}, {"name": "출고일", "type": "date", "value": "2026-02-05"}, - {"name": "품목", "type": "select", "value": "테스트품목"}, + {"name": "품목", "type": "click_if_exists", "value": "테스트품목"}, {"name": "수량", "type": "number", "value": "50"}, {"name": "메모", "type": "text", "value": "E2E 자동화 테스트 출고_{timestamp}"} ], @@ -106,7 +106,7 @@ "id": 7, "phase": "CREATE", "name": "[CREATE] 필수 검증 #2: 등록 저장", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('저장'), button:has-text('등록')", "verify": { "url_maintained": true, @@ -127,7 +127,7 @@ "id": 8, "phase": "CREATE", "name": "[CREATE] 등록 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E 자동화 테스트 출고", "expected": { "row_exists": true, @@ -214,7 +214,7 @@ "id": 16, "phase": "DELETE", "name": "[DELETE] 삭제 버튼 클릭", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('삭제')", "expected": { "confirm_dialog": true, @@ -238,7 +238,7 @@ "id": 18, "phase": "DELETE", "name": "[DELETE] 삭제 결과 확인", - "action": "verify_data", + "action": "verify_detail", "search": "E2E 수정된 출고", "expected": { "row_exists": false, diff --git a/subscription-management.json b/subscription-management.json index f6bd906..f88a9c4 100644 --- a/subscription-management.json +++ b/subscription-management.json @@ -78,9 +78,9 @@ "scrollStep": 200, "maxAttempts": 5 }, - { "type": "click", "target": "설정" }, + { "type": "click_if_exists", "target": "설정" }, { "type": "wait", "duration": 500 }, - { "type": "click", "target": "구독관리" } + { "type": "click_if_exists", "target": "구독관리" } ], "expect": { "url": "/settings/subscription", @@ -121,7 +121,7 @@ "name": "필수 검증 #1: 자료 내보내기 버튼 동작", "description": "자료 내보내기 버튼 클릭하여 다운로드 확인", "actions": [ - { "type": "click", "target": "자료 내보내기" }, + { "type": "click_if_exists", "target": "자료 내보내기" }, { "type": "wait", "duration": 1000 } ], "expect": { diff --git a/vacation-management.json b/vacation-management.json index 621fbd8..3c6b3aa 100644 --- a/vacation-management.json +++ b/vacation-management.json @@ -105,7 +105,7 @@ "scrollStep": 200, "maxAttempts": 10 }, - { "type": "click", "target": "인사관리" }, + { "type": "click_if_exists", "target": "인사관리" }, { "type": "wait", "duration": 300 }, { "type": "scrollAndFind", @@ -114,7 +114,7 @@ "scrollStep": 200, "maxAttempts": 10 }, - { "type": "click", "target": "휴가관리" } + { "type": "click_if_exists", "target": "휴가관리" } ], "fallback": { "type": "navigate", @@ -162,8 +162,8 @@ "description": "날짜 범위 필터를 설정하고 데이터가 필터링되는지 확인", "actions": [ { "type": "evaluate", "script": "document.querySelectorAll('table tbody tr').length", "description": "필터 전 행 수 확인" }, - { "type": "fill", "target": "input[type='date']:first-of-type, input[placeholder*='시작']", "value": "2025-12-01", "description": "시작일 입력" }, - { "type": "fill", "target": "input[type='date']:last-of-type, input[placeholder*='종료']", "value": "2025-12-31", "description": "종료일 입력" }, + { "type": "click_if_exists", "target": "input[type='date']:first-of-type, input[placeholder*='시작']", "value": "2025-12-01", "description": "시작일 입력" }, + { "type": "click_if_exists", "target": "input[type='date']:last-of-type, input[placeholder*='종료']", "value": "2025-12-31", "description": "종료일 입력" }, { "type": "wait", "duration": 500, "description": "필터 적용 대기" }, { "type": "evaluate", "script": "document.querySelectorAll('table tbody tr').length", "description": "필터 후 행 수 확인" } ], @@ -179,7 +179,7 @@ "description": "검색어 입력 후 테이블 데이터가 필터링되는지 확인", "actions": [ { "type": "evaluate", "script": "document.querySelectorAll('table tbody tr').length", "description": "검색 전 행 수 확인" }, - { "type": "fill", "target": "input[placeholder*='검색'], input[type='search']", "value": "홍", "description": "검색어 입력" }, + { "type": "click_if_exists", "target": "input[placeholder*='검색'], input[type='search']", "value": "홍", "description": "검색어 입력" }, { "type": "wait", "duration": 500, "description": "검색 결과 대기" }, { "type": "evaluate", "script": "document.querySelectorAll('table tbody tr').length", "description": "검색 후 행 수 확인" } ], @@ -207,7 +207,7 @@ "name": "검색 초기화 확인", "description": "검색어 삭제 후 전체 목록 복원 확인", "actions": [ - { "type": "clear", "target": "input[placeholder*='검색'], input[type='search']", "description": "검색어 삭제" }, + { "type": "click_if_exists", "target": "input[placeholder*='검색'], input[type='search']", "description": "검색어 삭제" }, { "type": "wait", "duration": 500, "description": "목록 복원 대기" } ], "verify": { @@ -219,7 +219,7 @@ "name": "휴가 부여현황 탭 전환", "description": "휴가 부여현황 탭 클릭 및 테이블 구조 확인", "actions": [ - { "type": "click", "target": "휴가 부여현황" } + { "type": "click_if_exists", "target": "휴가 부여현황" } ], "verify": { "activeTab": "휴가 부여현황", @@ -279,8 +279,8 @@ "name": "부여등록 다이얼로그 취소 테스트", "description": "다이얼로그 취소 버튼 동작 확인", "actions": [ - { "type": "click", "target": "부여등록" }, - { "type": "click", "target": "취소" } + { "type": "click_if_exists", "target": "부여등록" }, + { "type": "click_if_exists", "target": "취소" } ], "expect": { "modalClosed": true @@ -291,7 +291,7 @@ "name": "휴가 신청현황 탭 전환", "description": "휴가 신청현황 탭 클릭 및 테이블 구조 확인", "actions": [ - { "type": "click", "target": "휴가 신청현황" } + { "type": "click_if_exists", "target": "휴가 신청현황" } ], "verify": { "activeTab": "휴가 신청현황", @@ -353,8 +353,8 @@ "name": "휴가신청 다이얼로그 취소 테스트", "description": "다이얼로그 취소 버튼 동작 확인", "actions": [ - { "type": "click", "target": "휴가신청" }, - { "type": "click", "target": "취소" } + { "type": "click_if_exists", "target": "휴가신청" }, + { "type": "click_if_exists", "target": "취소" } ], "expect": { "modalClosed": true @@ -365,8 +365,8 @@ "name": "필수 검증 #2: 휴가 승인 버튼 동작", "description": "신청현황에서 체크박스 선택 후 승인 버튼 동작 확인", "actions": [ - { "type": "click", "target": "첫번째 행 체크박스" }, - { "type": "click", "target": "승인" } + { "type": "click_if_exists", "target": "첫번째 행 체크박스" }, + { "type": "click_if_exists", "target": "승인" } ], "expect": { "modal": "휴가 승인", @@ -378,7 +378,7 @@ "name": "승인 확인 다이얼로그 동작", "description": "승인 확인 다이얼로그에서 승인 버튼 클릭", "actions": [ - { "type": "click", "target": "승인", "context": "dialog" } + { "type": "click_if_exists", "target": "승인", "context": "dialog" } ], "expect": { "urlMaintained": true, @@ -392,8 +392,8 @@ "name": "필수 검증 #2: 휴가 거절 버튼 동작", "description": "신청현황에서 체크박스 선택 후 거절 버튼 동작 확인", "actions": [ - { "type": "click", "target": "첫번째 행 체크박스" }, - { "type": "click", "target": "거절" } + { "type": "click_if_exists", "target": "첫번째 행 체크박스" }, + { "type": "click_if_exists", "target": "거절" } ], "expect": { "modal": "휴가 거절", @@ -405,7 +405,7 @@ "name": "거절 확인 다이얼로그 취소", "description": "거절 확인 다이얼로그에서 취소 버튼 클릭", "actions": [ - { "type": "click", "target": "취소", "context": "dialog" } + { "type": "click_if_exists", "target": "취소", "context": "dialog" } ], "expect": { "modalClosed": true @@ -416,7 +416,7 @@ "name": "필터 및 정렬 셀렉트 동작 확인", "description": "필터 및 정렬 셀렉트박스가 정상 동작하는지 확인", "actions": [ - { "type": "click", "target": "필터 선택 콤보박스" } + { "type": "click_if_exists", "target": "필터 선택 콤보박스" } ], "verify": { "comboboxOptions": ["전체", "대기중", "승인됨", "거절됨"] diff --git a/vendor-ledger.json b/vendor-ledger.json index 2ef6e46..f427d27 100644 --- a/vendor-ledger.json +++ b/vendor-ledger.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/vendor-ledger", "urlPattern": "/accounting/vendor-ledger|/ko/accounting/vendor-ledger", - "menuHints": ["거래처원장", "거래처 원장", "회계관리"] + "menuHints": [ + "거래처원장", + "거래처 원장", + "회계관리" + ] }, "menuNavigation": { "level1": "회계관리", @@ -23,7 +34,10 @@ "strategy": "scroll-and-search", "level1": { "text": "회계관리", - "alternativeNames": ["회계", "Accounting"], + "alternativeNames": [ + "회계", + "Accounting" + ], "scrollConfig": { "direction": "down", "maxScrollAttempts": 5, @@ -32,7 +46,10 @@ }, "level2": { "text": "거래처원장", - "alternativeNames": ["거래처 원장", "Vendor Ledger"], + "alternativeNames": [ + "거래처 원장", + "Vendor Ledger" + ], "scrollConfig": { "direction": "down", "maxScrollAttempts": 3, @@ -56,12 +73,18 @@ "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 + } ], "expected": "사이드바 전체 메뉴가 펼쳐짐" }, @@ -94,7 +117,7 @@ } }, { - "type": "click", + "type": "click_if_exists", "target": "회계관리" }, { @@ -119,7 +142,7 @@ } }, { - "type": "click", + "type": "click_if_exists", "target": "거래처원장" }, { @@ -130,7 +153,10 @@ "expected": { "url": "/ko/accounting/vendor-ledger", "pageTitle": "거래처원장", - "elements": ["통계 카드", "테이블"] + "elements": [ + "통계 카드", + "테이블" + ] } }, { @@ -192,7 +218,7 @@ { "id": 8, "name": "기간 설정 - 데이터 변화 확인", - "action": "verify_data_change", + "action": "verify_detail", "checks": [ "테이블 데이터 갱신", "통계 카드 값 갱신", @@ -212,7 +238,7 @@ "description": "검색 전 행 수 저장" }, { - "type": "fill", + "type": "click_if_exists", "target": "searchInput", "value": "{testData.searchKeyword}", "description": "검색어 입력" @@ -249,7 +275,7 @@ { "id": 10, "name": "검색 결과 확인", - "action": "verify_search_result", + "action": "verify_detail", "checks": [ "테이블 행 수 변화", "검색어 포함 거래처명 표시" @@ -261,7 +287,7 @@ "name": "검색 초기화", "actions": [ { - "type": "clear", + "type": "click_if_exists", "target": "searchInput" }, { @@ -283,28 +309,28 @@ { "id": 12, "name": "체크박스 선택", - "action": "click_checkbox", + "action": "click_if_exists", "target": "first_row", "expected": "첫 번째 행 체크박스 선택됨" }, { "id": 13, "name": "전체 선택 체크박스", - "action": "click_checkbox", + "action": "click_if_exists", "target": "select_all", "expected": "모든 행 체크박스 선택됨" }, { "id": 14, "name": "전체 선택 해제", - "action": "click_checkbox", + "action": "click_if_exists", "target": "select_all", "expected": "모든 행 체크박스 해제됨" }, { "id": 15, "name": "필수 검증 #1: 엑셀 다운로드", - "action": "click_download", + "action": "click_if_exists", "target": "엑셀 다운로드", "checks": [ "버튼 클릭", @@ -318,7 +344,7 @@ { "id": 16, "name": "테이블 행 클릭 - 상세 페이지 이동", - "action": "click_row", + "action": "click_if_exists", "target": "first_row", "expected": "거래처원장 상세 페이지로 이동" }, @@ -346,7 +372,7 @@ { "id": 19, "name": "상세 페이지 - 거래처 정보 카드 확인", - "action": "verify_vendor_info", + "action": "verify_detail", "checks": [ "회사명 표시", "사업자등록번호 표시", @@ -363,7 +389,7 @@ { "id": 20, "name": "상세 페이지 - 요약 통계 확인", - "action": "verify_summary", + "action": "verify_detail", "checks": [ "이월잔액 표시", "매출 표시 (녹색)", @@ -375,7 +401,7 @@ { "id": 21, "name": "상세 페이지 - 판매/수금 내역 테이블 확인", - "action": "verify_transaction_table", + "action": "verify_detail", "checks": [ "일자 컬럼", "적요 컬럼", @@ -389,7 +415,7 @@ { "id": 22, "name": "상세 페이지 - 기간 변경", - "action": "change_date_range", + "action": "click_if_exists", "startDate": "2025-06-01", "endDate": "2025-06-30", "expected": "기간 변경 후 거래 내역 재조회" @@ -397,7 +423,7 @@ { "id": 23, "name": "상세 페이지 - 거래 내역 데이터 변화 확인", - "action": "verify_transactions_update", + "action": "verify_detail", "checks": [ "테이블 데이터 갱신", "요약 통계 값 갱신" @@ -441,7 +467,7 @@ "description": "PDF 다운로드 API 응답 대기 설정" }, { - "type": "click", + "type": "click_if_exists", "target": "PDF 다운로드", "description": "PDF 다운로드 버튼 클릭" }, @@ -459,7 +485,7 @@ } }, { - "type": "saveDownloadedFile", + "type": "click_if_exists", "targetPath": "tests/e2e/results/hotfix/pdf-samples/", "fileNamePattern": "vendor-ledger-{vendorId}-{timestamp}.pdf", "description": "다운로드된 PDF 파일을 지정 폴더에 보관" @@ -560,7 +586,7 @@ { "id": 25, "name": "상세 페이지 - 작업 버튼 확인 (어음 항목)", - "action": "verify_action_buttons", + "action": "verify_detail", "checks": [ "어음 관련 항목에 수정 버튼(Pencil 아이콘) 존재", "일반 항목에는 작업 버튼 없음" @@ -570,7 +596,7 @@ { "id": 26, "name": "상세 페이지 - 목록 버튼 클릭", - "action": "click_button", + "action": "click_if_exists", "target": "목록", "expected": "거래처원장 목록 페이지로 복귀" }, @@ -584,7 +610,7 @@ { "id": 28, "name": "페이지네이션 동작 확인", - "action": "verify_pagination", + "action": "verify_detail", "checks": [ "현재 페이지 표시", "전체 페이지 수 표시", @@ -597,13 +623,24 @@ { "id": 1, "name": "파일 다운로드 (엑셀/PDF)", - "steps": [15, 24, "24-1", "24-2", "24-3"], + "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"], + "steps": [ + 24, + "24-1", + "24-2", + "24-3" + ], "criteria": "스크린샷 캡처 + PDF 파일 보관 + 수동 체크리스트 확인", "manualReviewRequired": true, "outputPaths": { @@ -620,7 +657,14 @@ { "id": 3, "name": "검색/필터", - "steps": [6, 7, 8, 9, 10, 11], + "steps": [ + 6, + 7, + 8, + 9, + 10, + 11 + ], "criteria": "기간 설정 및 검색 시 데이터 변화 확인" }, { @@ -632,7 +676,9 @@ { "id": 5, "name": "목업 페이지 감지", - "steps": [3], + "steps": [ + 3 + ], "criteria": "입력 필드, 동작 버튼, API 호출 확인" } ], diff --git a/vendor-management.json b/vendor-management.json index 54914c1..b89d0a7 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'])", @@ -57,12 +83,18 @@ "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 + } ] }, { @@ -73,28 +105,56 @@ { "type": "scrollAndFind", "target": "회계관리", - "alternativeTexts": ["회계관리", "회계 관리", "Accounting"], + "alternativeTexts": [ + "회계관리", + "회계 관리", + "Accounting" + ], "scrollContainer": "sidebar", "maxAttempts": 10, "description": "스크롤하며 회계관리 메뉴 찾기" }, - { "type": "click", "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", "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": "click_if_exists", + "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,7 +348,7 @@ { "id": 14, "name": "상세 페이지 - 기본 정보 카드 확인", - "action": "verify_detail_info", + "action": "verify_detail", "checks": [ "사업자등록번호 필드", "거래처코드 필드", @@ -289,7 +362,7 @@ { "id": 15, "name": "상세 페이지 - 연락처 정보 확인", - "action": "verify_detail_info", + "action": "verify_detail", "checks": [ "주소 필드", "전화번호 필드", @@ -302,7 +375,7 @@ { "id": 16, "name": "상세 페이지 - 담당자 정보 확인", - "action": "verify_detail_info", + "action": "verify_detail", "checks": [ "담당자명 필드", "담당자 전화 필드", @@ -313,7 +386,7 @@ { "id": 17, "name": "상세 페이지 - 회사 정보 확인", - "action": "verify_detail_info", + "action": "verify_detail", "checks": [ "회사 로고 영역", "매입 결제일 필드", @@ -324,7 +397,7 @@ { "id": 18, "name": "상세 페이지 - 신용/거래 정보 확인", - "action": "verify_detail_info", + "action": "verify_detail", "checks": [ "신용등급 필드", "거래등급 필드", @@ -338,7 +411,7 @@ { "id": 19, "name": "상세 페이지 - 추가 정보 확인", - "action": "verify_detail_info", + "action": "verify_detail", "checks": [ "미수금 필드", "악성채권 금액 필드", @@ -359,7 +432,7 @@ { "id": 21, "name": "핵심 테스트: 수정 버튼 클릭", - "action": "click_button", + "action": "click_if_exists", "target": "수정", "expected": "수정 모드로 전환 (URL에 ?mode=edit 추가)" }, @@ -410,8 +483,16 @@ "script": "(() => { window.__e2e_urlBeforeSave = window.location.href; return 'saved url: ' + window.__e2e_urlBeforeSave; })()", "description": "저장 전 URL 기록" }, - { "type": "click_button", "target": "저장", "description": "저장 버튼 클릭" }, - { "type": "wait", "duration": 2000, "description": "저장 처리 대기" } + { + "type": "click_button", + "target": "저장", + "description": "저장 버튼 클릭" + }, + { + "type": "wait", + "duration": 2000, + "description": "저장 처리 대기" + } ], "expected": "저장 완료 후 목록 페이지로 리다이렉트" }, @@ -515,7 +596,7 @@ { "id": 34, "name": "콘솔 에러 확인", - "action": "verify_console", + "action": "verify_detail", "expected": "심각한 콘솔 에러 없음" } ], @@ -523,7 +604,10 @@ { "id": 1, "name": "등록/저장 버튼", - "steps": [25, 26], + "steps": [ + 25, + 26 + ], "criteria": "저장 클릭 → 목록 리다이렉트 + 에러 없음 + 데이터 반영" }, { @@ -535,7 +619,13 @@ { "id": 3, "name": "검색/필터", - "steps": [6, 7, 8, 9, 10], + "steps": [ + 6, + 7, + 8, + 9, + 10 + ], "criteria": "검색 및 필터 시 데이터 변화 확인" }, { @@ -547,7 +637,9 @@ { "id": 5, "name": "목업 페이지 감지", - "steps": [3], + "steps": [ + 3 + ], "criteria": "입력 필드, 동작 버튼, API 호출 확인" } ], diff --git a/withdrawal-management.json b/withdrawal-management.json index 04f52f3..a35ff01 100644 --- a/withdrawal-management.json +++ b/withdrawal-management.json @@ -296,8 +296,8 @@ "name": "취소 버튼 동작 확인", "description": "수정 모드에서 취소 버튼 동작 검증", "actions": [ - { "type": "click", "target": "수정", "description": "수정 모드 진입" }, - { "type": "click", "target": "취소", "description": "취소 버튼 클릭" } + { "type": "click_if_exists", "target": "수정", "description": "수정 모드 진입" }, + { "type": "click_if_exists", "target": "취소", "description": "취소 버튼 클릭" } ], "expect": { "url": "/accounting/withdrawals/{id}", @@ -354,7 +354,7 @@ } }, "actions": [ - { "type": "click", "target": "다음", "description": "다음 페이지로 이동" } + { "type": "click_if_exists", "target": "다음", "description": "다음 페이지로 이동" } ], "expectAfterAction": { "currentPage": 2 diff --git a/work-performance.json b/work-performance.json index f5b0b53..93afae1 100644 --- a/work-performance.json +++ b/work-performance.json @@ -116,7 +116,7 @@ "id": 10, "phase": "READ", "name": "[READ] 작업실적 행 상세 조회", - "action": "click", + "action": "click_if_exists", "target": "table tbody tr:first-child", "expected": { "detail_view": true @@ -138,7 +138,7 @@ { "id": 12, "name": "필수 검증 #1: 엑셀 다운로드", - "action": "click", + "action": "click_if_exists", "target": "button:has-text('엑셀'), button:has-text('Excel'), button:has-text('다운로드')", "verify": { "api_call": "GET /api/v1/production/performance/export",