From c3dd547dd078efd4a44b1570d085915eae38151f Mon Sep 17 00:00:00 2001 From: lab1 Date: Fri, 30 Jan 2026 10:50:38 +0900 Subject: [PATCH] =?UTF-8?q?init:=20E2E=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=8B=9C=EB=82=98=EB=A6=AC=EC=98=A4=20=EC=B4=88=EA=B8=B0?= =?UTF-8?q?=ED=99=94=20(61=EA=B0=9C=20=ED=8C=8C=EC=9D=BC)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .claude/settings.local.json | 60 ++ _templates/pdf-download-verification.json | 189 ++++ account-info.json | 217 ++++ announcement-board.json | 186 ++++ approval-box.json | 440 ++++++++ attendance-checkin.json | 458 ++++++++ attendance-management.json | 471 +++++++++ attendance-settings.json | 391 +++++++ bad-debt-collection.json | 1090 +++++++++++++++++++ bank-account-management.json | 341 ++++++ bank-transactions.json | 439 ++++++++ bill-management.json | 294 ++++++ board-management.json | 801 ++++++++++++++ board-test.json | 741 +++++++++++++ card-add.json | 431 ++++++++ card-transactions.json | 413 ++++++++ company-info.json | 422 ++++++++ comprehensive-analysis.json | 263 +++++ crud-delete-freeboard.json | 339 ++++++ crud-delete-vendor.json | 404 ++++++++ customer-inquiry.json | 550 ++++++++++ daily-report.json | 929 +++++++++++++++++ department-add.json | 487 +++++++++ deposit-management.json | 466 +++++++++ draft-box.json | 1152 +++++++++++++++++++++ employee-register.json | 379 +++++++ event-board.json | 183 ++++ expected-expenses.json | 1059 +++++++++++++++++++ faq.json | 192 ++++ free-board.json | 776 ++++++++++++++ inspection-management.json | 348 +++++++ inventory-status.json | 242 +++++ item-management.json | 1025 ++++++++++++++++++ item-standard-management.json | 444 ++++++++ leave-policy.json | 607 +++++++++++ login.json | 276 +++++ notification-settings.json | 825 +++++++++++++++ order-management.json | 363 +++++++ payment-history.json | 213 ++++ pdf-download-test.json | 433 ++++++++ permission-management.json | 518 +++++++++ popup-management.json | 824 +++++++++++++++ position-management.json | 303 ++++++ price-management.json | 323 ++++++ process-management.json | 355 +++++++ production-dashboard.json | 715 +++++++++++++ quality-certification.json | 298 ++++++ rank-management.json | 553 ++++++++++ receivables-status.json | 343 ++++++ receiving-management.json | 188 ++++ reference-box.json | 678 ++++++++++++ salary-management.json | 399 +++++++ sales-management.json | 684 ++++++++++++ shipment-management.json | 245 +++++ subscription-management.json | 188 ++++ vacation-management.json | 533 ++++++++++ vendor-ledger.json | 675 ++++++++++++ vendor-management.json | 483 +++++++++ withdrawal-management.json | 526 ++++++++++ work-order-management.json | 656 ++++++++++++ work-performance.json | 162 +++ worker-screen.json | 167 +++ 62 files changed, 29155 insertions(+) create mode 100644 .claude/settings.local.json create mode 100644 _templates/pdf-download-verification.json create mode 100644 account-info.json create mode 100644 announcement-board.json create mode 100644 approval-box.json create mode 100644 attendance-checkin.json create mode 100644 attendance-management.json create mode 100644 attendance-settings.json create mode 100644 bad-debt-collection.json create mode 100644 bank-account-management.json create mode 100644 bank-transactions.json create mode 100644 bill-management.json create mode 100644 board-management.json create mode 100644 board-test.json create mode 100644 card-add.json create mode 100644 card-transactions.json create mode 100644 company-info.json create mode 100644 comprehensive-analysis.json create mode 100644 crud-delete-freeboard.json create mode 100644 crud-delete-vendor.json create mode 100644 customer-inquiry.json create mode 100644 daily-report.json create mode 100644 department-add.json create mode 100644 deposit-management.json create mode 100644 draft-box.json create mode 100644 employee-register.json create mode 100644 event-board.json create mode 100644 expected-expenses.json create mode 100644 faq.json create mode 100644 free-board.json create mode 100644 inspection-management.json create mode 100644 inventory-status.json create mode 100644 item-management.json create mode 100644 item-standard-management.json create mode 100644 leave-policy.json create mode 100644 login.json create mode 100644 notification-settings.json create mode 100644 order-management.json create mode 100644 payment-history.json create mode 100644 pdf-download-test.json create mode 100644 permission-management.json create mode 100644 popup-management.json create mode 100644 position-management.json create mode 100644 price-management.json create mode 100644 process-management.json create mode 100644 production-dashboard.json create mode 100644 quality-certification.json create mode 100644 rank-management.json create mode 100644 receivables-status.json create mode 100644 receiving-management.json create mode 100644 reference-box.json create mode 100644 salary-management.json create mode 100644 sales-management.json create mode 100644 shipment-management.json create mode 100644 subscription-management.json create mode 100644 vacation-management.json create mode 100644 vendor-ledger.json create mode 100644 vendor-management.json create mode 100644 withdrawal-management.json create mode 100644 work-order-management.json create mode 100644 work-performance.json create mode 100644 worker-screen.json diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..e12a94c --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,60 @@ +{ + "permissions": { + "allow": [ + "Bash", + "Read", + "Write", + "Edit", + "MultiEdit", + "Glob", + "Grep", + "WebFetch", + "WebSearch", + "TodoWrite", + "Task", + "NotebookEdit", + "mcp__playwright__playwright_navigate", + "mcp__playwright__playwright_screenshot", + "mcp__playwright__playwright_click", + "mcp__playwright__playwright_fill", + "mcp__playwright__playwright_select", + "mcp__playwright__playwright_hover", + "mcp__playwright__playwright_evaluate", + "mcp__playwright__playwright_console_logs", + "mcp__playwright__playwright_get_visible_text", + "mcp__playwright__playwright_get_visible_html", + "mcp__playwright__playwright_press_key", + "mcp__playwright__playwright_close", + "mcp__playwright__playwright_go_back", + "mcp__playwright__playwright_go_forward", + "mcp__playwright__playwright_resize", + "mcp__playwright__playwright_drag", + "mcp__playwright__playwright_upload_file", + "mcp__playwright__playwright_iframe_click", + "mcp__playwright__playwright_iframe_fill", + "mcp__playwright__playwright_save_as_pdf", + "mcp__playwright__playwright_click_and_switch_tab", + "mcp__playwright__playwright_get", + "mcp__playwright__playwright_post", + "mcp__playwright__playwright_put", + "mcp__playwright__playwright_patch", + "mcp__playwright__playwright_delete", + "mcp__playwright__playwright_expect_response", + "mcp__playwright__playwright_assert_response", + "mcp__playwright__playwright_custom_user_agent", + "mcp__playwright__start_codegen_session", + "mcp__playwright__end_codegen_session", + "mcp__playwright__get_codegen_session", + "mcp__playwright__clear_codegen_session", + "mcp__ide__getDiagnostics", + "mcp__ide__executeCode" + ], + "defaultMode": "bypassPermissions", + "deny": [], + "ask": [] + }, + "enableAllProjectMcpServers": true, + "enabledMcpjsonServers": [ + "playwright" + ] +} diff --git a/_templates/pdf-download-verification.json b/_templates/pdf-download-verification.json new file mode 100644 index 0000000..2182a0f --- /dev/null +++ b/_templates/pdf-download-verification.json @@ -0,0 +1,189 @@ +{ + "_templateInfo": { + "name": "PDF 다운로드 검증 템플릿", + "version": "1.0.0", + "description": "PDF 다운로드 기능이 있는 시나리오에 추가할 검증 스텝 템플릿", + "usage": "이 템플릿의 steps 배열을 PDF 다운로드가 있는 시나리오에 복사하여 사용", + "createdAt": "2026-01-28" + }, + + "steps": [ + { + "id": "{baseStepId}", + "name": "⚠️ 필수 검증: PDF 다운로드 전 페이지 스크린샷", + "critical": true, + "description": "PDF 생성 전 페이지 상태를 스크린샷으로 캡처하여 CSS 문제 감지용 기준 이미지 확보", + "actions": [ + { + "type": "screenshot", + "name": "pdf-preview-before-download-{scenarioId}", + "fullPage": true, + "savePath": "tests/e2e/results/hotfix/screenshots/", + "description": "PDF 생성 대상 페이지 전체 캡처" + }, + { + "type": "screenshot", + "name": "pdf-content-area-{scenarioId}", + "selector": "{pdfContentSelector}", + "savePath": "tests/e2e/results/hotfix/screenshots/", + "description": "PDF 콘텐츠 영역만 캡처" + } + ], + "verify": { + "screenshotCaptured": true, + "purpose": "PDF CSS 문제 감지를 위한 기준 이미지" + } + }, + { + "id": "{baseStepId}-1", + "name": "⚠️ 필수 검증: PDF 다운로드 실행 및 파일 보관", + "critical": true, + "description": "PDF 다운로드 후 파일을 지정 폴더에 보관하여 수동 검증 가능하게 함", + "actions": [ + { + "type": "expectResponse", + "id": "pdf-download-response-{scenarioId}", + "urlPattern": "{pdfApiEndpoint}", + "description": "PDF 다운로드 API 응답 대기 설정" + }, + { + "type": "click", + "target": "{pdfDownloadButtonSelector}", + "description": "PDF 다운로드 버튼 클릭" + }, + { + "type": "wait", + "duration": 3000, + "description": "PDF 생성 및 다운로드 대기" + }, + { + "type": "assertResponse", + "id": "pdf-download-response-{scenarioId}", + "checks": { + "status": 200, + "contentType": "application/pdf" + } + }, + { + "type": "saveDownloadedFile", + "targetPath": "tests/e2e/results/hotfix/pdf-samples/", + "fileNamePattern": "{scenarioId}-{timestamp}.pdf", + "description": "다운로드된 PDF 파일을 지정 폴더에 보관" + } + ], + "verify": { + "apiSuccess": true, + "fileDownloaded": true, + "fileSaved": "tests/e2e/results/hotfix/pdf-samples/" + } + }, + { + "id": "{baseStepId}-2", + "name": "⚠️ PDF 파일 유효성 검증", + "critical": true, + "description": "다운로드된 PDF 파일의 기본 유효성 검사", + "actions": [ + { + "type": "verifyDownloadedFile", + "checks": { + "fileExists": true, + "fileSize": "> 1024", + "pdfSignature": "%PDF-", + "description": "PDF 파일 헤더 검증" + } + } + ], + "verify": { + "pdfValid": true, + "minFileSize": "1KB 이상" + } + }, + { + "id": "{baseStepId}-3", + "name": "📋 PDF 스타일 수동 확인 체크리스트", + "type": "manualVerification", + "critical": true, + "description": "개발자가 다운로드된 PDF를 열어 시각적으로 확인해야 하는 항목", + "manualChecklist": [ + { + "id": "css-1", + "item": "테이블 경계선이 올바르게 표시되는가?", + "category": "테이블 스타일" + }, + { + "id": "css-2", + "item": "한글 폰트가 깨지지 않고 정상 표시되는가?", + "category": "폰트" + }, + { + "id": "css-3", + "item": "숫자/금액 정렬이 올바른가? (우측 정렬)", + "category": "정렬" + }, + { + "id": "css-4", + "item": "여백(margin/padding)이 적절한가?", + "category": "레이아웃" + }, + { + "id": "css-5", + "item": "헤더/푸터가 각 페이지에 올바르게 표시되는가?", + "category": "페이지 구조" + }, + { + "id": "css-6", + "item": "로고/이미지가 정상 표시되는가?", + "category": "이미지" + }, + { + "id": "css-7", + "item": "페이지 나눔(page break)이 적절한 위치에서 발생하는가?", + "category": "페이지 나눔" + }, + { + "id": "css-8", + "item": "배경색/강조색이 올바르게 적용되었는가?", + "category": "색상" + }, + { + "id": "css-9", + "item": "텍스트가 잘리거나 겹치지 않는가?", + "category": "오버플로우" + }, + { + "id": "css-10", + "item": "인쇄 시 레이아웃이 유지되는가?", + "category": "인쇄 호환성" + } + ], + "outputFiles": { + "screenshot": "tests/e2e/results/hotfix/screenshots/pdf-preview-before-download-{scenarioId}-*.png", + "pdfFile": "tests/e2e/results/hotfix/pdf-samples/{scenarioId}-*.pdf" + }, + "reportFlag": { + "requiresManualReview": true, + "message": "⚠️ PDF 스타일 수동 확인 필요 - 위 체크리스트 항목을 PDF 파일에서 직접 확인하세요" + } + } + ], + + "placeholders": { + "{baseStepId}": "PDF 다운로드 스텝의 기준 ID (예: 24, step-15 등)", + "{scenarioId}": "시나리오 ID (예: vendor-ledger, daily-report 등)", + "{pdfContentSelector}": "PDF 콘텐츠 영역 CSS 선택자 (예: .report-content, main)", + "{pdfApiEndpoint}": "PDF 다운로드 API 엔드포인트 패턴 (예: /api/v1/*/export-pdf)", + "{pdfDownloadButtonSelector}": "PDF 다운로드 버튼 선택자 (예: PDF 다운로드, button:has-text('PDF'))" + }, + + "requiredFolders": [ + "tests/e2e/results/hotfix/screenshots/", + "tests/e2e/results/hotfix/pdf-samples/" + ], + + "integrationNotes": [ + "이 템플릿을 사용할 때 모든 {placeholder}를 실제 값으로 교체하세요", + "PDF 다운로드 전에 스크린샷을 먼저 캡처해야 CSS 비교가 가능합니다", + "다운로드된 PDF 파일은 테스트 결과 폴더에 보관되어 수동 검토가 가능합니다", + "manualChecklist는 테스트 리포트에 포함되어 개발자가 확인할 수 있습니다" + ] +} diff --git a/account-info.json b/account-info.json new file mode 100644 index 0000000..ac73a03 --- /dev/null +++ b/account-info.json @@ -0,0 +1,217 @@ +{ + "id": "account-info", + "name": "계정정보 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "설정 > 계정정보 페이지의 계정 정보 조회/수정 기능을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/settings/account", + "menuNavigation": { + "level1": "설정", + "level2": "계정정보", + "expectedUrl": "/settings/account" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebar": { + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 5, + "waitAfterScroll": 300 + }, + "level1": { + "text": "설정", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "계정정보", + "waitAfterClick": 300 + }, + "fallbackUrl": "/settings/account", + "expectedUrl": "/settings/account" + }, + "timeout": 90000, + "tags": ["settings", "account", "profile"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 500 } + ] + }, + { + "id": "step-1", + "name": "설정 메뉴 진입", + "description": "설정 > 계정정보 메뉴로 이동", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "설정", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "설정" }, + { "type": "wait", "duration": 500 }, + { "type": "click", "target": "계정정보" } + ], + "expect": { + "url": "/settings/account", + "visible": ["계정정보", "계정 정보"] + }, + "fallback": { + "type": "navigate", + "url": "/settings/account" + } + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "계정 정보 필드 및 버튼 확인", + "verify": { + "visible": ["계정 정보", "프로필 사진", "아이디", "비밀번호", "권한", "상태"], + "buttons": ["탈퇴", "사용중지", "수정"] + } + }, + { + "id": "step-3", + "name": "프로필 사진 영역 확인", + "description": "프로필 사진 업로드 영역 확인", + "verify": { + "visible": ["프로필 사진", "클릭 또는 드래그", "PNG, JPEG, GIF"] + } + }, + { + "id": "step-4", + "name": "비밀번호 변경 버튼 확인", + "description": "비밀번호 변경 버튼 존재 확인", + "actions": [ + { "type": "click", "target": "변경" } + ], + "expect": { + "pageOrModal": "비밀번호 변경", + "visible": ["현재 비밀번호", "새 비밀번호"] + } + }, + { + "id": "step-5", + "name": "비밀번호 변경 취소", + "description": "ESC 키로 비밀번호 변경 모달 닫기", + "actions": [ + { "type": "press", "key": "Escape" }, + { "type": "wait", "duration": 300 } + ] + }, + { + "id": "step-6", + "name": "필수 검증 #2: 수정 버튼 클릭", + "description": "수정 버튼 클릭하여 정보 수정 모드 진입", + "actions": [ + { "type": "click", "target": "수정" } + ], + "expect": { + "editMode": true, + "visible": ["저장", "취소"] + } + }, + { + "id": "step-7", + "name": "수정 취소", + "description": "취소 버튼 클릭하여 수정 취소", + "actions": [ + { "type": "click", "target": "취소" } + ], + "expect": { + "editModeClosed": true + } + }, + { + "id": "step-8", + "name": "약관 동의 정보 확인", + "description": "마케팅 정보 수신 동의 섹션 확인", + "verify": { + "visible": ["약관 동의 정보", "마케팅 정보 수신 동의", "이메일 수신", "SMS 수신"] + } + }, + { + "id": "step-9", + "name": "사용중지 버튼 확인", + "description": "사용중지 버튼 존재 확인 (클릭하지 않음)", + "verify": { + "buttonExists": "사용중지" + } + }, + { + "id": "step-10", + "name": "탈퇴 버튼 확인", + "description": "탈퇴 버튼 존재 확인 (클릭하지 않음)", + "verify": { + "buttonExists": "탈퇴" + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/settings/account", + "message": "계정정보 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "button:has-text('수정')", + "message": "수정 버튼이 표시되어야 함" + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 2, + "name": "등록/저장 버튼", + "trigger": "수정 → 저장 버튼", + "verification": "URL 유지 + 에러 페이지 없음 + 성공 토스트", + "failCondition": "404/500 에러 페이지 이동" + }, + { + "id": 5, + "name": "목업/미완성 페이지 감지", + "trigger": "페이지 로드 시", + "verification": "입력 필드 + 동작하는 버튼 확인", + "failCondition": "버튼만 있고 입력 불가" + } + ] + }, + + "notes": { + "testScope": "계정 정보 조회 및 수정 버튼 동작 테스트", + "pageFeatures": { + "profilePhoto": "1250x250px, 10MB 이하 PNG/JPEG/GIF", + "accountFields": ["아이디", "비밀번호", "권한", "상태"], + "termsAgreement": ["마케팅 정보 수신 동의", "이메일 수신", "SMS 수신"] + }, + "buttons": ["탈퇴", "사용중지", "수정", "변경"], + "caution": "탈퇴/사용중지 버튼은 테스트 계정 보호를 위해 클릭하지 않음", + "prerequisites": "로그인된 사용자" + } +} diff --git a/announcement-board.json b/announcement-board.json new file mode 100644 index 0000000..1dc020b --- /dev/null +++ b/announcement-board.json @@ -0,0 +1,186 @@ +{ + "id": "announcement-board", + "name": "공지사항 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "고객센터 > 공지사항 페이지의 목록 조회, 상세 보기, 검색/필터 기능을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/support/announcements", + "menuNavigation": { + "level1": "고객센터", + "level2": "공지사항", + "expectedUrl": "/support/announcements" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebar": { + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 5, + "waitAfterScroll": 300 + }, + "level1": { + "text": "고객센터", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "공지사항", + "waitAfterClick": 300 + }, + "fallbackUrl": "/support/announcements", + "expectedUrl": "/support/announcements" + }, + "timeout": 90000, + "tags": ["support", "announcement", "read-only"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "사이드바를 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 500 } + ] + }, + { + "id": "step-1", + "name": "고객센터 메뉴 진입", + "description": "고객센터 > 공지사항 메뉴로 이동", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "고객센터", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "고객센터" }, + { "type": "wait", "duration": 500 }, + { "type": "click", "target": "공지사항" } + ], + "expect": { + "url": "/support/announcements", + "visible": ["공지사항"] + }, + "fallback": { + "type": "navigate", + "url": "/support/announcements" + } + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "공지사항 목록 페이지 구조 확인", + "verify": { + "visible": ["공지사항", "최신순"], + "tableColumns": ["No.", "제목", "작성자", "등록일", "조회수"] + } + }, + { + "id": "step-3", + "name": "날짜 필터 확인", + "description": "날짜 필터 버튼 동작 확인", + "actions": [ + { "type": "click", "target": "당월" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "filterApplied": true + } + }, + { + "id": "step-4", + "name": "정렬 옵션 확인", + "description": "정렬 드롭다운 옵션 확인", + "actions": [ + { "type": "click", "target": "최신순", "role": "combobox" } + ], + "expect": { + "optionsVisible": true + } + }, + { + "id": "step-5", + "name": "공지사항 상세 보기", + "description": "공지사항 항목 클릭하여 상세 페이지 이동", + "actions": [ + { "type": "press", "key": "Escape" }, + { "type": "wait", "duration": 300 }, + { + "type": "evaluate", + "script": "document.querySelector('tbody tr')?.click()" + } + ], + "expect": { + "pageChange": "/support/announcements/", + "visible": ["공지사항 상세", "목록으로"] + } + }, + { + "id": "step-6", + "name": "상세 페이지 구조 확인", + "description": "상세 페이지 정보 표시 확인", + "verify": { + "visible": ["제목", "작성자", "등록일", "조회수", "내용"] + } + }, + { + "id": "step-7", + "name": "목록으로 돌아가기", + "description": "목록으로 버튼 클릭하여 목록 페이지로 복귀", + "actions": [ + { "type": "click", "target": "목록으로" } + ], + "expect": { + "url": "/support/announcements", + "visible": ["공지사항"] + } + }, + { + "id": "step-8", + "name": "페이지네이션 확인", + "description": "페이지네이션 동작 확인", + "verify": { + "paginationExists": true, + "showsTotalItems": true + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/support/announcements", + "message": "공지사항 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "text=공지사항", + "message": "공지사항 제목이 표시되어야 함" + } + ], + + "notes": { + "testScope": "공지사항 목록 조회 → 상세 보기 → 필터/정렬 테스트", + "pageType": "조회 전용 (일반 사용자 등록/수정/삭제 불가)", + "tableColumns": ["No.", "제목", "작성자", "등록일", "조회수"], + "detailFields": ["제목", "작성자", "등록일", "조회수", "이미지", "내용"], + "prerequisites": "로그인된 사용자" + } +} diff --git a/approval-box.json b/approval-box.json new file mode 100644 index 0000000..0a8a228 --- /dev/null +++ b/approval-box.json @@ -0,0 +1,440 @@ +{ + "scenarioId": "approval-box", + "name": "결재함 E2E 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "결재함 페이지의 전체 기능을 검증합니다 (탭 전환, 검색, 필터, 승인/반려, 모달)", + "baseUrl": "https://dev.codebridge-x.com", + + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", + "level1": "결재관리", + "level2": "결재함", + "alternativeLevel2Names": ["결재함", "결재 함", "승인함", "Approval Box", "inbox"], + "fallbackUrls": [ + "/ko/approval/inbox", + "/ko/approval/box", + "/ko/approvals/inbox", + "/ko/approval-management/inbox", + "/approval/inbox" + ], + "scrollConfig": { + "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", + "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + } + }, + + "steps": [ + { + "id": 0, + "name": "사이드바 메뉴 탐색 준비", + "description": "사이드바를 최상단으로 스크롤하고 메뉴 구조 파악", + "actions": [ + { "type": "scroll", "target": "sidebar", "direction": "top", "description": "사이드바 최상단으로 스크롤" }, + { "type": "wait", "duration": 500 }, + { "type": "screenshot", "name": "sidebar_initial_state" } + ], + "verification": [ + "사이드바가 화면에 보이는지 확인", + "메뉴 항목들이 렌더링되었는지 확인" + ] + }, + { + "id": 1, + "name": "1차 메뉴 찾기: 결재관리 (스크롤 포함)", + "description": "사이드바를 스크롤하며 '결재관리' 메뉴를 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "결재관리", + "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" } + ], + "verification": [ + "결재관리 메뉴가 클릭되었는지 확인", + "서브메뉴가 펼쳐졌는지 확인", + "하위 메뉴 항목들이 보이는지 확인" + ], + "fallback": { + "if": "메뉴를 찾을 수 없음", + "then": "사이드바 전체를 스크롤하며 재탐색" + } + }, + { + "id": 2, + "name": "2차 메뉴 찾기: 결재함 (스크롤 포함)", + "description": "서브메뉴에서 '결재함'을 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "결재함", + "alternativeTexts": ["결재함", "결재 함", "Inbox", "승인함"], + "scrollContainer": "submenu", + "maxAttempts": 5, + "description": "서브메뉴에서 결재함 찾기" + }, + { "type": "wait", "duration": 200 }, + { "type": "click", "target": "결재함", "description": "결재함 메뉴 클릭" }, + { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 }, + { "type": "screenshot", "name": "approval_box_page" } + ], + "verification": [ + "결재함 메뉴 클릭 성공", + "페이지 이동 또는 컨텐츠 로드" + ] + }, + { + "id": 3, + "name": "404 에러 감지 및 대체 경로 시도", + "description": "페이지 로드 후 404 에러 여부 확인, 404시 대체 경로 탐색", + "actions": [ + { "type": "wait", "duration": 1000 }, + { "type": "checkFor404", "indicators": [ + "페이지를 찾을 수 없습니다", + "404", + "Not Found", + "존재하지 않거나" + ]}, + { "type": "screenshot", "name": "page_load_result" } + ], + "verification": [ + "현재 페이지가 404인지 확인" + ], + "onError404": { + "description": "404 에러 발생 시 대체 URL 시도", + "actions": [ + { "type": "log", "message": "404 감지 - 대체 경로 탐색 시작" }, + { + "type": "tryAlternativeUrls", + "urls": [ + "/ko/approval/inbox", + "/ko/approvals/inbox", + "/ko/approval-box" + ], + "stopOnSuccess": true + }, + { + "type": "ifStillFailed", + "action": "navigateViaMenuClick", + "description": "URL 직접 접근 실패 시 메뉴 클릭으로 재시도" + } + ] + } + }, + { + "id": 4, + "name": "페이지 정상 로드 확인", + "description": "결재함 페이지가 정상적으로 로드되었는지 확인", + "actions": [ + { "type": "verify", "target": "pageTitle", "contains": ["결재함", "결재", "Approval"] }, + { "type": "verify", "target": "pageContent", "notContains": ["404", "찾을 수 없습니다", "Not Found"] } + ], + "verification": [ + "페이지 제목 '결재함' 또는 관련 텍스트 표시", + "404 에러 메시지 미표시", + "콘텐츠가 정상 렌더링됨" + ], + "successCriteria": { + "urlPattern": "/approval", + "requiredElements": ["결재", "문서", "승인"] + } + }, + { + "id": 5, + "name": "통계 카드 확인", + "action": "현황 카드의 데이터 수집", + "verification": [ + "전체결재 건수 기록", + "미결재 건수 기록", + "결재완료 건수 기록", + "결재반려 건수 기록" + ] + }, + { + "id": 6, + "name": "탭 구조 확인", + "action": "4개 탭 존재 여부 확인", + "verification": [ + "'전체결재' 탭 존재 확인", + "'미결재' 탭 존재 확인", + "'결재완료' 탭 존재 확인", + "'결재반려' 탭 존재 확인" + ] + }, + { + "id": 7, + "name": "테이블 데이터 확인", + "action": "테이블에 데이터가 표시되는지 확인", + "verification": [ + "테이블 헤더 컬럼 확인", + "데이터 행 존재 여부 확인", + "페이지네이션 표시 확인" + ] + }, + { + "id": 8, + "name": "⚠️ 필수 검증: 결재 문서 상세 보기", + "description": "테이블에서 결재 문서 클릭하여 상세 모달/페이지 확인", + "critical": true, + "actions": [ + { "type": "click", "target": "미결재 탭", "description": "미결재 탭으로 이동" }, + { "type": "wait", "duration": 500 }, + { "type": "click", "target": "첫 번째 결재 문서 행", "description": "결재 문서 클릭" }, + { "type": "wait", "target": "상세 모달 또는 페이지" } + ], + "expect": { + "detailView": true, + "fields": ["문서 제목", "기안자", "기안일", "결재 상태"], + "buttons": ["승인", "반려", "PDF", "인쇄"] + }, + "note": "결재 문서가 없으면 데이터 생성 또는 SKIP" + }, + { + "id": "8-pdf-1", + "name": "⚠️ 필수 검증: PDF 다운로드 전 모달 스크린샷", + "critical": true, + "description": "PDF 생성 전 모달 상태를 스크린샷으로 캡처하여 CSS 문제 감지용 기준 이미지 확보", + "prerequisite": "step-8의 문서 상세 모달이 열려있는 상태에서 실행", + "actions": [ + { + "type": "screenshot", + "name": "pdf-preview-before-download-approval-box", + "fullPage": false, + "selector": "[role='dialog'], .modal, [data-state='open']", + "savePath": "tests/e2e/results/hotfix/screenshots/", + "description": "PDF 생성 대상 모달 전체 캡처" + } + ], + "verify": { + "screenshotCaptured": true, + "purpose": "PDF CSS 문제 감지를 위한 기준 이미지" + } + }, + { + "id": "8-pdf-2", + "name": "⚠️ 필수 검증: PDF 다운로드 실행 및 파일 보관", + "critical": true, + "description": "PDF 다운로드 후 파일을 지정 폴더에 보관하여 수동 검증 가능하게 함", + "actions": [ + { + "type": "verify", + "target": "PDF 버튼 존재", + "selector": "button:has-text('PDF'), [aria-label*='PDF']", + "description": "PDF 다운로드 버튼 존재 확인" + }, + { + "type": "expectResponse", + "id": "pdf-download-response-approval-box", + "urlPattern": "/api/v1/approvals/*/pdf", + "description": "PDF 다운로드 API 응답 대기 설정" + }, + { + "type": "click", + "target": "PDF 버튼", + "selector": "button:has-text('PDF')", + "description": "PDF 다운로드 버튼 클릭" + }, + { + "type": "wait", + "duration": 3000, + "description": "PDF 생성 및 다운로드 대기" + }, + { + "type": "assertResponse", + "id": "pdf-download-response-approval-box", + "checks": { + "status": 200, + "contentType": "application/pdf" + } + }, + { + "type": "saveDownloadedFile", + "targetPath": "tests/e2e/results/hotfix/pdf-samples/", + "fileNamePattern": "approval-box-{timestamp}.pdf", + "description": "다운로드된 PDF 파일을 지정 폴더에 보관" + } + ], + "verify": { + "apiSuccess": true, + "fileDownloaded": true, + "fileSaved": "tests/e2e/results/hotfix/pdf-samples/" + } + }, + { + "id": "8-pdf-3", + "name": "⚠️ PDF 파일 유효성 검증", + "critical": true, + "description": "다운로드된 PDF 파일의 기본 유효성 검사", + "actions": [ + { + "type": "verifyDownloadedFile", + "checks": { + "fileExists": true, + "fileSize": "> 1024", + "pdfSignature": "%PDF-", + "description": "PDF 파일 헤더 검증" + } + } + ], + "verify": { + "pdfValid": true, + "minFileSize": "1KB 이상" + } + }, + { + "id": "8-pdf-4", + "name": "📋 PDF 스타일 수동 확인 체크리스트", + "type": "manualVerification", + "critical": true, + "description": "개발자가 다운로드된 PDF를 열어 시각적으로 확인해야 하는 항목", + "manualChecklist": [ + {"id": "css-1", "item": "테이블 경계선이 올바르게 표시되는가?", "category": "테이블 스타일"}, + {"id": "css-2", "item": "한글 폰트가 깨지지 않고 정상 표시되는가?", "category": "폰트"}, + {"id": "css-3", "item": "숫자/금액 정렬이 올바른가? (우측 정렬)", "category": "정렬"}, + {"id": "css-4", "item": "여백(margin/padding)이 적절한가?", "category": "레이아웃"}, + {"id": "css-5", "item": "헤더/푸터가 각 페이지에 올바르게 표시되는가?", "category": "페이지 구조"}, + {"id": "css-6", "item": "로고/이미지가 정상 표시되는가?", "category": "이미지"}, + {"id": "css-7", "item": "페이지 나눔(page break)이 적절한 위치에서 발생하는가?", "category": "페이지 나눔"}, + {"id": "css-8", "item": "배경색/강조색이 올바르게 적용되었는가?", "category": "색상"}, + {"id": "css-9", "item": "텍스트가 잘리거나 겹치지 않는가?", "category": "오버플로우"}, + {"id": "css-10", "item": "결재선 정보가 정상적으로 표시되는가?", "category": "결재선"} + ], + "outputFiles": { + "screenshot": "tests/e2e/results/hotfix/screenshots/pdf-preview-before-download-approval-box-*.png", + "pdfFile": "tests/e2e/results/hotfix/pdf-samples/approval-box-*.pdf" + }, + "reportFlag": { + "requiresManualReview": true, + "message": "⚠️ PDF 스타일 수동 확인 필요 - 위 체크리스트 항목을 PDF 파일에서 직접 확인하세요" + } + }, + { + "id": 9, + "name": "⚠️ 필수 검증 #4: 결재 승인 실제 수행", + "description": "미결재 문서에 대해 실제 승인 처리 수행", + "critical": true, + "actions": [ + { "type": "verify", "target": "승인 버튼 존재" }, + { "type": "click", "target": "승인 버튼", "description": "결재 승인 클릭" }, + { "type": "wait", "target": "확인 다이얼로그" }, + { "type": "click", "target": "확인", "description": "승인 확인" } + ], + "expect": { + "urlMaintained": true, + "noErrorPage": true, + "apiCall": "POST /api/v1/approvals/{id}/approve", + "toast": "승인되었습니다", + "statusChange": "미결재 → 결재완료" + }, + "note": "⚠️ 버튼 존재만 확인하면 불완전! 실제 승인까지 검증 필수!" + }, + { + "id": "9-1", + "name": "결재 승인 결과 확인", + "description": "승인 후 결재완료 탭에서 해당 문서 확인", + "actions": [ + { "type": "click", "target": "결재완료 탭" }, + { "type": "wait", "duration": 500 } + ], + "verify": { + "documentMoved": "승인한 문서가 결재완료 탭에 표시", + "statusUpdated": "결재 상태가 '완료'로 변경" + } + }, + { + "id": 10, + "name": "⚠️ 필수 검증 #4: 결재 반려 실제 수행", + "description": "미결재 문서에 대해 실제 반려 처리 수행", + "critical": true, + "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": "반려 확인" } + ], + "expect": { + "urlMaintained": true, + "noErrorPage": true, + "apiCall": "POST /api/v1/approvals/{id}/reject", + "toast": "반려되었습니다", + "statusChange": "미결재 → 결재반려" + }, + "note": "⚠️ 반려 버튼 존재만 확인하면 불완전! 실제 반려까지 검증 필수!" + }, + { + "id": "10-1", + "name": "결재 반려 결과 확인", + "description": "반려 후 결재반려 탭에서 해당 문서 확인", + "actions": [ + { "type": "click", "target": "결재반려 탭" }, + { "type": "wait", "duration": 500 } + ], + "verify": { + "documentMoved": "반려한 문서가 결재반려 탭에 표시", + "statusUpdated": "결재 상태가 '반려'로 변경", + "rejectReason": "반려 사유가 표시" + } + }, + { + "id": 11, + "name": "검색 기능 테스트", + "description": "검색 필터로 결재 문서 검색", + "actions": [ + { "type": "click", "target": "전체결재 탭" }, + { "type": "type", "target": "검색 입력창", "value": "테스트" }, + { "type": "click", "target": "검색 버튼" } + ], + "verify": { + "searchApplied": true, + "filteredResults": "검색어에 맞는 결과 표시" + } + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 4, + "name": "결재 승인/반려 완료", + "trigger": "결재 문서 상세의 승인/반려 버튼", + "verification": "실제 승인/반려 동작 + API 호출 + 결과 확인", + "failCondition": "버튼 존재만 확인, 클릭하지 않음", + "steps": ["9", "10"] + } + ] + }, + + "expectedAPIs": [ + "GET /api/v1/approvals/inbox - 결재함 목록 조회", + "GET /api/v1/approvals/inbox/summary - 결재함 통계", + "GET /api/v1/approvals/{id} - 결재 문서 상세 조회", + "POST /api/v1/approvals/{id}/approve - 결재 승인", + "POST /api/v1/approvals/{id}/reject - 결재 반려" + ], + + "notes": [ + "⚠️ 404 방지: 반드시 메뉴 클릭으로 페이지 진입 (직접 URL 접근 금지)", + "⚠️ 스크롤 필수: 사이드바가 길 경우 메뉴가 화면 밖에 있을 수 있음", + "⚠️ 대체 경로: 메뉴명이 변경되었을 수 있으므로 다양한 이름으로 탐색", + "메뉴 계층: 결재관리 > 결재함", + "탭 전환 시 URL 변경 없이 데이터만 필터링됨" + ] +} diff --git a/attendance-checkin.json b/attendance-checkin.json new file mode 100644 index 0000000..df0a6ec --- /dev/null +++ b/attendance-checkin.json @@ -0,0 +1,458 @@ +{ + "id": "attendance-checkin", + "name": "근태현황 출퇴근 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "위치 정보 권한 허용 후 출근/퇴근 기록을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/hr/attendance", + + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", + "level1": "인사관리", + "level2": "근태현황", + "alternativeLevel1Names": ["인사관리", "인사 관리", "HR", "Human Resource", "HR관리"], + "alternativeLevel2Names": ["근태현황", "근태 현황", "출퇴근", "Attendance", "출퇴근현황", "근태관리"], + "fallbackUrls": [ + "/hr/attendance", + "/ko/hr/attendance", + "/ko/hr/attendance-status", + "/ko/hr/checkin", + "/ko/human-resource/attendance" + ], + "scrollConfig": { + "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", + "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + } + }, + + "timeout": 120000, + "tags": ["hr", "attendance", "geolocation", "checkin", "checkout"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "browserConfig": { + "permissions": { + "geolocation": { + "grant": true, + "description": "위치 정보 접근 권한 허용 - 출퇴근 기록에 필수", + "mockLocation": { + "enabled": true, + "latitude": 37.557358, + "longitude": 126.864414, + "accuracy": 100 + } + } + }, + "contextOptions": { + "geolocation": { + "latitude": 37.557358, + "longitude": 126.864414 + }, + "permissions": ["geolocation"] + } + }, + + "preTestSetup": { + "description": "테스트 시작 전 Playwright 브라우저 컨텍스트에서 위치 권한 설정", + "playwright": { + "grantPermissions": ["geolocation"], + "setGeolocation": { + "latitude": 37.557358, + "longitude": 126.864414, + "accuracy": 100 + }, + "code": [ + "// Playwright MCP 사용 시 브라우저 시작 직후 실행", + "// mcp__playwright__playwright_evaluate로 위치 권한 자동 허용", + "await context.grantPermissions(['geolocation']);", + "await context.setGeolocation({ latitude: 37.557358, longitude: 126.864414 });" + ] + } + }, + + "steps": [ + { + "id": "step-0", + "name": "🔐 위치 권한 사전 설정 (Playwright)", + "description": "Playwright 컨텍스트 레벨에서 위치 권한을 미리 허용하여 브라우저 팝업 방지", + "critical": true, + "executeBeforeNavigation": true, + "playwright": { + "commands": [ + { + "tool": "mcp__playwright__playwright_evaluate", + "script": "// 브라우저 시작 시 위치 권한 자동 허용 (Playwright 컨텍스트 레벨)" + } + ], + "contextSetup": { + "permissions": ["geolocation"], + "geolocation": { + "latitude": 37.557358, + "longitude": 126.864414 + } + } + }, + "note": "이 단계는 브라우저 컨텍스트 생성 시 자동으로 처리됨" + }, + { + "id": "step-0-1", + "name": "🗺️ 위치 권한 팝업 처리 (UI)", + "description": "브라우저 상단에 나타나는 위치 권한 요청 팝업에서 '항상 허용' 클릭", + "critical": true, + "actions": [ + { "type": "wait", "duration": 1000, "description": "페이지 로드 및 권한 팝업 표시 대기" }, + { + "type": "conditionalClick", + "description": "위치 권한 팝업이 나타나면 '허용' 또는 '항상 허용' 버튼 클릭", + "selectors": [ + "button:has-text('항상 허용')", + "button:has-text('허용')", + "button:has-text('Allow')", + "button:has-text('Always Allow')", + "[data-testid='allow-location']", + ".permission-allow-button", + "button[aria-label*='허용']", + "button[aria-label*='위치']" + ], + "fallback": { + "action": "skip", + "reason": "권한 팝업이 없으면 이미 허용된 상태" + } + }, + { "type": "wait", "duration": 500, "description": "권한 설정 적용 대기" }, + { "type": "screenshot", "name": "after_permission_grant" } + ], + "verification": [ + "위치 권한 팝업이 닫혔는지 확인", + "또는 팝업이 처음부터 없었는지 확인 (이미 허용된 경우)" + ], + "errorHandling": { + "onTimeout": "continue", + "onNotFound": "continue", + "reason": "팝업이 없으면 권한이 이미 허용된 상태로 간주" + } + }, + { + "id": 2, + "name": "1차 메뉴 찾기: 인사관리 (스크롤 포함)", + "description": "사이드바를 스크롤하며 '인사관리' 메뉴를 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "인사관리", + "alternativeTexts": ["인사관리", "인사 관리", "HR", "Human Resource"], + "scrollContainer": "sidebar", + "maxAttempts": 10, + "description": "스크롤하며 인사관리 메뉴 찾기" + }, + { "type": "wait", "duration": 300 }, + { "type": "click", "target": "인사관리", "description": "인사관리 메뉴 클릭" }, + { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, + { "type": "screenshot", "name": "hr_menu_expanded" } + ], + "verification": [ + "인사관리 메뉴가 클릭되었는지 확인", + "서브메뉴가 펼쳐졌는지 확인", + "하위 메뉴 항목들이 보이는지 확인" + ], + "fallback": { + "if": "메뉴를 찾을 수 없음", + "then": "사이드바 전체를 스크롤하며 재탐색" + } + }, + { + "id": 3, + "name": "2차 메뉴 찾기: 근태현황 (스크롤 포함)", + "description": "서브메뉴에서 '근태현황'을 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "근태현황", + "alternativeTexts": ["근태현황", "근태 현황", "출퇴근", "Attendance"], + "scrollContainer": "submenu", + "maxAttempts": 5, + "description": "서브메뉴에서 근태현황 찾기" + }, + { "type": "wait", "duration": 200 }, + { "type": "click", "target": "근태현황", "description": "근태현황 메뉴 클릭" }, + { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 }, + { "type": "screenshot", "name": "attendance_page" } + ], + "verification": [ + "근태현황 메뉴 클릭 성공", + "페이지 이동 또는 컨텐츠 로드" + ] + }, + { + "id": 4, + "name": "404 에러 감지 및 대체 경로 시도", + "description": "페이지 로드 후 404 에러 여부 확인, 404시 대체 경로 탐색", + "actions": [ + { "type": "wait", "duration": 1000 }, + { "type": "checkFor404", "indicators": [ + "페이지를 찾을 수 없습니다", + "404", + "Not Found", + "존재하지 않거나" + ]}, + { "type": "screenshot", "name": "page_load_result" } + ], + "verification": [ + "현재 페이지가 404인지 확인" + ], + "onError404": { + "description": "404 에러 발생 시 대체 URL 시도", + "actions": [ + { "type": "log", "message": "404 감지 - 대체 경로 탐색 시작" }, + { + "type": "tryAlternativeUrls", + "urls": [ + "/ko/hr/attendance", + "/ko/hr/attendance-status", + "/ko/hr/checkin" + ], + "stopOnSuccess": true + }, + { + "type": "ifStillFailed", + "action": "navigateViaMenuClick", + "description": "URL 직접 접근 실패 시 메뉴 클릭으로 재시도" + } + ] + } + }, + { + "id": 5, + "name": "페이지 정상 로드 확인", + "description": "근태현황 페이지가 정상적으로 로드되었는지 확인", + "actions": [ + { "type": "verify", "target": "pageTitle", "contains": ["근태현황", "출퇴근", "Attendance"] }, + { "type": "verify", "target": "pageContent", "notContains": ["404", "찾을 수 없습니다", "Not Found"] } + ], + "verification": [ + "페이지 제목 '근태현황' 또는 관련 텍스트 표시", + "404 에러 메시지 미표시", + "콘텐츠가 정상 렌더링됨" + ], + "successCriteria": { + "urlPattern": "/hr/attendance", + "requiredElements": ["출퇴근", "출근", "퇴근", "현재 시간"] + } + }, + { + "id": "step-5", + "name": "브라우저 위치 권한 설정", + "description": "Playwright context에서 위치 정보 권한을 허용하고 가상 위치 설정", + "playwright": { + "code": "await context.grantPermissions(['geolocation']);", + "setGeolocation": { + "latitude": 37.557358, + "longitude": 126.864414 + } + }, + "expect": { + "permissionGranted": "geolocation" + } + }, + { + "id": "step-6", + "name": "위치 정보 로딩 대기", + "description": "Google Map 로딩 및 현재 위치 표시 대기", + "waitFor": { + "type": "element", + "selector": "region[name='지도']", + "timeout": 10000 + }, + "expect": { + "mapLoaded": true, + "locationMarkerVisible": true + } + }, + { + "id": "step-7", + "name": "사용자 정보 확인", + "description": "출퇴근 패널에서 로그인한 사용자 정보 확인", + "verify": { + "userInfo": { + "name": "홍킬동", + "department": "부서명 · 개발중인 메뉴" + }, + "currentTime": { + "format": "HH:mm:ss", + "updating": true + } + } + }, + { + "id": "step-8", + "name": "출근 상태 확인", + "description": "현재 출퇴근 상태 확인 (출근 전/출근 후)", + "capture": { + "variable": "attendanceStatus", + "checkElements": [ + { "selector": "button:has-text('출근하기')", "status": "not_checked_in" }, + { "selector": "text=출근 완료", "status": "checked_in" }, + { "selector": "button:has-text('퇴근하기')", "status": "ready_to_checkout" } + ] + } + }, + { + "id": "step-9", + "name": "출근하기 (미출근 상태인 경우)", + "description": "출근하기 버튼이 활성화된 경우 클릭하여 출근 기록", + "condition": { + "if": "{attendanceStatus} == 'not_checked_in'" + }, + "actions": [ + { "type": "click", "target": "출근하기" } + ], + "waitFor": { + "type": "text", + "content": "출근 완료", + "timeout": 5000 + }, + "expect": { + "toast": ["출근", "완료", "성공"], + "visible": ["출근 완료", "출근 시간"] + } + }, + { + "id": "step-10", + "name": "출근 완료 상태 확인", + "description": "출근 완료 후 상태 및 출근 시간 표시 확인", + "verify": { + "visible": ["출근 완료"], + "checkInTime": { + "format": "HH:mm:ss", + "displayed": true + }, + "buttonState": { + "출근하기": "hidden_or_disabled", + "퇴근하기": "enabled_or_visible" + } + } + }, + { + "id": "step-11", + "name": "퇴근하기 버튼 상태 확인", + "description": "출근 완료 후 퇴근하기 버튼 활성화 여부 확인", + "verify": { + "button": { + "target": "퇴근하기", + "state": "visible", + "note": "일부 시스템에서는 최소 근무 시간 후에만 활성화될 수 있음" + } + } + }, + { + "id": "step-12", + "name": "퇴근하기 (선택적)", + "description": "퇴근하기 버튼이 활성화된 경우 클릭하여 퇴근 기록", + "optional": true, + "condition": { + "if": "button[name='퇴근하기']:enabled" + }, + "actions": [ + { "type": "click", "target": "퇴근하기" } + ], + "waitFor": { + "type": "text", + "content": ["퇴근 완료", "퇴근 시간"], + "timeout": 5000 + }, + "expect": { + "toast": ["퇴근", "완료", "성공"], + "visible": ["퇴근 완료", "퇴근 시간"] + } + }, + { + "id": "step-13", + "name": "최종 상태 확인", + "description": "출퇴근 기록 후 최종 상태 확인", + "verify": { + "url": "/hr/attendance", + "mapDisplayed": true, + "attendanceRecorded": true + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/hr/attendance", + "message": "근태현황 페이지에 머물러야 함" + }, + { + "type": "permission", + "name": "geolocation", + "state": "granted", + "message": "위치 정보 권한이 허용되어야 함" + }, + { + "type": "elementExists", + "selector": "region[name='지도']", + "message": "Google Map이 표시되어야 함" + }, + { + "type": "elementExists", + "selector": "text=현재 시간", + "message": "현재 시간이 표시되어야 함" + } + ], + + "cleanup": { + "enabled": false, + "description": "출퇴근 기록은 삭제하지 않음 (업무 데이터)", + "note": "테스트 후 수동으로 관리자가 삭제 필요시 처리" + }, + + "notes": { + "testScope": "위치 권한 허용 -> 근태현황 페이지 이동 -> 출근/퇴근 기록 테스트", + "antiPattern404": "직접 URL 접근 금지 - 반드시 메뉴 클릭으로 페이지 진입", + "scrollRequired": "사이드바 스크롤을 통해 메뉴 항목 탐색 필수", + "correctUrl": "/hr/attendance (기존 /ko/hr/attendance에서 수정됨)" + }, + + "playwrightMcpInstructions": { + "description": "Playwright MCP를 사용한 위치 권한 설정 방법", + "beforeNavigation": [ + "1. 브라우저 navigate 전에 위치 권한 설정이 필요함", + "2. Playwright MCP는 브라우저 컨텍스트 레벨에서 권한을 설정할 수 없으므로 UI 팝업 처리 필요" + ], + "uiPermissionHandling": { + "description": "위치 권한 팝업이 나타나면 '항상 허용' 버튼 클릭", + "selectors": [ + "button:has-text('항상 허용')", + "button:has-text('허용')", + "button:has-text('Allow')" + ], + "workflow": [ + "1. 페이지 로드 후 1-2초 대기", + "2. 위치 권한 팝업 존재 여부 확인", + "3. 팝업이 있으면 '항상 허용' 버튼 클릭", + "4. 팝업이 없으면 이미 권한이 허용된 상태로 간주하고 진행" + ] + }, + "mockGeolocation": { + "description": "테스트용 가상 위치 설정", + "latitude": 37.557358, + "longitude": 126.864414, + "note": "서울 영등포구 좌표 (테스트 회사 위치 가정)" + } + } +} diff --git a/attendance-management.json b/attendance-management.json new file mode 100644 index 0000000..8356b27 --- /dev/null +++ b/attendance-management.json @@ -0,0 +1,471 @@ +{ + "id": "attendance-management", + "name": "근태관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "근태 등록 및 사유 등록 기능을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/ko/hr/attendance-management", + "menuNavigation": { + "level1": "인사관리", + "level2": "근태관리", + "expectedUrl": "/ko/hr/attendance-management" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebar": { + "scrollContainer": ".sidebar-scroll, [data-sidebar-scroll], nav[role='navigation']", + "scrollStep": 200, + "maxScrollAttempts": 10 + }, + "level1": { + "text": "인사관리", + "selectors": [ + "button:has-text('인사관리')", + "[data-menu='인사관리']", + "nav button:has-text('인사관리')", + "aside button:has-text('인사관리')" + ] + }, + "level2": { + "text": "근태관리", + "selectors": [ + "a:has-text('근태관리')", + "[data-submenu='근태관리']", + "nav a:has-text('근태관리')", + "aside a:has-text('근태관리')" + ] + }, + "fallback": { + "directUrl": "/ko/hr/attendance-management", + "useAfterAttempts": 3 + } + }, + "timeout": 90000, + "tags": ["hr", "attendance", "management", "crud"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "testData": { + "attendance": { + "checkInHour": "9", + "checkInMinute": "0", + "checkOutHour": "18", + "checkOutMinute": "0", + "nightOvertimeHour": "0", + "nightOvertimeMinute": "0", + "weekendOvertimeHour": "0", + "weekendOvertimeMinute": "0" + }, + "reason": { + "type": { + "options": ["출장신청서", "휴가신청서", "외근신청서", "연장근무신청서"] + } + } + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 준비", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll, [data-sidebar-scroll], nav[role=\"navigation\"]')?.scrollTo({top: 0, behavior: 'instant'})", + "description": "사이드바 최상단으로 스크롤" + }, + { "type": "wait", "duration": 500, "description": "스크롤 완료 대기" } + ], + "expect": { + "sidebarReady": true + } + }, + { + "id": "step-1", + "name": "인사관리 메뉴 진입", + "description": "인사관리 > 근태관리 메뉴로 이동 (scrollAndFind 패턴 사용)", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll, [data-sidebar-scroll], nav[role='navigation']", + "target": "인사관리", + "scrollStep": 200, + "maxAttempts": 10, + "description": "스크롤하며 인사관리 메뉴 찾기" + }, + { "type": "click", "target": "인사관리", "description": "인사관리 메뉴 클릭" }, + { "type": "wait", "duration": 300, "description": "서브메뉴 열림 대기" }, + { + "type": "scrollAndFind", + "container": ".sidebar-scroll, [data-sidebar-scroll], nav[role='navigation']", + "target": "근태관리", + "scrollStep": 100, + "maxAttempts": 5, + "description": "스크롤하며 근태관리 서브메뉴 찾기" + }, + { "type": "click", "target": "근태관리", "description": "근태관리 서브메뉴 클릭" } + ], + "fallback": { + "type": "navigate", + "url": "/ko/hr/attendance-management", + "description": "메뉴 탐색 실패 시 직접 URL 이동" + }, + "expect": { + "url": "/hr/attendance-management", + "visible": ["근태관리", "근태 등록", "사유 등록"] + } + }, + { + "id": "step-1-1", + "name": "🗺️ GPS 위치 정보 모킹", + "description": "브라우저 Geolocation API를 모킹하여 GPS 권한 팝업 없이 위치 정보 제공", + "critical": true, + "actions": [ + { + "type": "evaluate", + "script": "(() => { const mockPosition = { coords: { latitude: 37.5665, longitude: 126.9780, accuracy: 100, altitude: null, altitudeAccuracy: null, heading: null, speed: null }, timestamp: Date.now() }; const mockGeolocation = { getCurrentPosition: (success, error, options) => { console.log('[GeolocationMock] getCurrentPosition called'); setTimeout(() => success(mockPosition), 100); }, watchPosition: (success, error, options) => { console.log('[GeolocationMock] watchPosition called'); setTimeout(() => success(mockPosition), 100); return 1; }, clearWatch: (id) => { console.log('[GeolocationMock] clearWatch called:', id); } }; Object.defineProperty(navigator, 'geolocation', { value: mockGeolocation, writable: false, configurable: true }); console.log('[GeolocationMock] GPS mocking complete - coords: 37.5665, 126.9780'); return { success: true, coords: { latitude: 37.5665, longitude: 126.9780 } }; })()", + "description": "Geolocation API 모킹 (서울시청 좌표: 37.5665, 126.9780)" + }, + { "type": "wait", "duration": 500, "description": "모킹 적용 대기" } + ], + "note": "브라우저 네이티브 권한 팝업은 Playwright로 클릭 불가. Geolocation API를 모킹하여 우회함." + }, + { + "id": "step-2", + "name": "근태 현황 대시보드 확인", + "description": "미출근, 정시출근, 지각, 휴가 카드 표시 확인", + "verify": { + "visible": ["미출근", "정시 출근", "지각", "휴가"], + "statsCards": true + } + }, + { + "id": "step-3", + "name": "기간 필터 확인", + "description": "당해년도, 전전월, 전월, 당월, 어제, 오늘 버튼 확인", + "verify": { + "visible": ["당해년도", "전전월", "전월", "당월", "어제", "오늘"], + "dateInputs": 2 + } + }, + { + "id": "step-4", + "name": "탭 필터 확인", + "description": "전체, 미출근, 정시출근, 지각, 결근, 휴가, 출장, 외근, 연장근무 탭 확인", + "verify": { + "tabs": ["전체", "미출근", "정시 출근", "지각", "결근", "휴가", "출장", "외근", "연장근무"] + } + }, + { + "id": "step-5", + "name": "근태 테이블 구조 확인", + "description": "테이블 컬럼 구조 검증", + "verify": { + "tableColumns": ["번호", "부서", "직책", "이름", "직급", "기준일", "출근", "퇴근", "휴게", "연장근무", "사유"] + } + }, + { + "id": "step-6", + "name": "근태 등록 모달 열기", + "description": "근태 등록 버튼 클릭하여 모달 열기", + "actions": [ + { "type": "openModal", "target": "근태 등록", "description": "근태 정보 모달 열기" } + ], + "modalConfig": { + "containerSelector": "[role='dialog'], .modal", + "animationDelay": 300, + "waitForSelector": "[role='dialog']" + }, + "expect": { + "modal": "근태 정보", + "visible": ["대상", "기준일", "출근 시간", "퇴근 시간", "야간 연장 시간", "주말 연장 시간"] + } + }, + { + "id": "step-7", + "name": "근태 등록 모달 필드 확인", + "description": "근태 등록 모달의 필드와 기본값 확인", + "verify": { + "modalFields": { + "대상": { "type": "combobox", "placeholder": "선택" }, + "기준일": { "type": "datepicker", "defaultValue": "today" }, + "출근 시간": { "type": "timepicker", "defaultValue": "9:00" }, + "퇴근 시간": { "type": "timepicker", "defaultValue": "18:00" }, + "야간 연장 시간": { "type": "timepicker", "defaultValue": "0:00" }, + "주말 연장 시간": { "type": "timepicker", "defaultValue": "0:00" } + }, + "buttons": ["취소", "저장"] + } + }, + { + "id": "step-8", + "name": "⚠️ 필수 검증 #4: 근태 등록 실제 수행", + "description": "근태 등록 모달에서 실제 데이터 입력 후 저장", + "critical": true, + "actions": [ + { "type": "selectInModal", "target": "대상", "value": "첫번째 사원", "description": "사원 선택", "options": { "waitAfter": 200 } }, + { "type": "verify", "target": "기준일", "description": "기준일 기본값 확인" }, + { "type": "clickInModal", "target": "저장", "options": { "waitAfter": 500 } } + ], + "expect": { + "urlMaintained": true, + "noErrorPage": true, + "modalClosed": true, + "toast": "등록이 완료되었습니다", + "apiCall": "POST /api/v1/attendances" + }, + "note": "⚠️ 모달 열기/닫기만 테스트하면 불완전! 실제 저장까지 검증 필수!" + }, + { + "id": "step-8-1", + "name": "근태 등록 결과 확인", + "description": "등록 후 테이블에 새 데이터 반영 확인", + "verify": { + "tableDataUpdated": true, + "newRowExists": "방금 등록한 근태 데이터가 테이블에 표시" + } + }, + { + "id": "step-9", + "name": "사유 등록 모달 열기", + "description": "사유 등록 버튼 클릭하여 모달 열기", + "actions": [ + { "type": "openModal", "target": "사유 등록", "description": "사유 정보 모달 열기" } + ], + "modalConfig": { + "containerSelector": "[role='dialog'], .modal", + "animationDelay": 300, + "waitForSelector": "[role='dialog']" + }, + "expect": { + "modal": "사유 정보", + "visible": ["대상", "기준일", "유형"] + } + }, + { + "id": "step-10", + "name": "사유 유형 옵션 확인", + "description": "사유 유형 드롭다운의 옵션 확인", + "actions": [ + { "type": "clickInModal", "target": "유형", "role": "combobox", "options": { "waitAfter": 200 } } + ], + "verify": { + "options": ["출장신청서", "휴가신청서", "외근신청서", "연장근무신청서"] + } + }, + { + "id": "step-11", + "name": "⚠️ 필수 검증 #4: 사유 등록 실제 수행", + "description": "사유 등록 모달에서 실제 데이터 입력 후 저장", + "critical": true, + "actions": [ + { "type": "selectInModal", "target": "대상", "value": "첫번째 사원", "description": "사원 선택", "options": { "waitAfter": 200 } }, + { "type": "selectInModal", "target": "유형", "value": "출장신청서", "description": "출장신청서 선택", "options": { "waitAfter": 200 } }, + { "type": "clickInModal", "target": "등록", "options": { "waitAfter": 500 } } + ], + "expect": { + "urlMaintained": true, + "noErrorPage": true, + "modalClosed": true, + "toast": "등록이 완료되었습니다", + "apiCall": "POST /api/v1/attendance-reasons" + }, + "note": "⚠️ ESC로 닫기만 하면 불완전! 실제 등록까지 검증 필수!" + }, + { + "id": "step-11-1", + "name": "사유 등록 결과 확인", + "description": "등록 후 테이블에 사유 컬럼 업데이트 확인", + "verify": { + "tableDataUpdated": true, + "reasonColumnUpdated": "사유 컬럼에 등록된 사유 표시" + } + }, + { + "id": "step-12", + "name": "⚠️ 필수 검증: 기간 필터 검색", + "critical": true, + "description": "날짜 범위를 설정하고 데이터가 필터링되는지 확인", + "actions": [ + { "type": "capture", "variable": "initialRowCount", "selector": "table tbody tr", "extract": "count", "description": "필터 전 행 수 저장" }, + { "type": "click", "target": "당월", "description": "당월 빠른 필터 클릭" }, + { "type": "wait", "duration": 500, "description": "필터 적용 대기" }, + { "type": "capture", "variable": "filteredRowCount", "selector": "table tbody tr", "extract": "count", "description": "필터 후 행 수 저장" } + ], + "verify": { + "dateFilterApplied": true, + "filterButtonActive": "당월" + }, + "note": "날짜 필터가 실제로 데이터를 필터링하는지 확인" + }, + { + "id": "step-12-1", + "name": "⚠️ 필수 검증: 검색 기능", + "critical": true, + "description": "검색어 입력 후 테이블 데이터가 필터링되는지 확인", + "actions": [ + { "type": "capture", "variable": "beforeSearchCount", "selector": "table tbody tr", "extract": "count", "description": "검색 전 행 수 저장" }, + { "type": "fill", "target": "검색", "value": "홍", "description": "검색어 '홍' 입력" }, + { "type": "wait", "duration": 500, "description": "검색 결과 대기" }, + { "type": "capture", "variable": "afterSearchCount", "selector": "table tbody tr", "extract": "count", "description": "검색 후 행 수 저장" } + ], + "verify": { + "searchApplied": true, + "tableContains": "홍", + "placeholder": "이름, 부서 검색..." + }, + "note": "⚠️ 검색어 입력 후 테이블에 검색어가 포함된 행만 표시되어야 함" + }, + { + "id": "step-12-2", + "name": "검색 결과 데이터 검증", + "description": "검색 결과의 각 행에 검색어가 포함되어 있는지 확인", + "verify": { + "allRowsContain": "홍", + "verifyMethod": "테이블의 모든 행이 검색어를 포함하는지 확인" + } + }, + { + "id": "step-12-3", + "name": "검색 초기화 확인", + "description": "검색어 삭제 후 전체 목록 복원 확인", + "actions": [ + { "type": "clear", "target": "검색", "description": "검색어 삭제" }, + { "type": "wait", "duration": 500, "description": "목록 복원 대기" } + ], + "verify": { + "dataRestored": true + } + }, + { + "id": "step-13", + "name": "엑셀 다운로드 버튼 확인", + "description": "엑셀 다운로드 버튼 존재 확인", + "verify": { + "visible": ["엑셀 다운로드"] + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/hr/attendance-management", + "message": "근태관리 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "button:has-text('근태 등록')", + "message": "근태 등록 버튼이 표시되어야 함" + }, + { + "type": "elementExists", + "selector": "button:has-text('사유 등록')", + "message": "사유 등록 버튼이 표시되어야 함" + }, + { + "type": "tableExists", + "message": "근태 목록 테이블이 표시되어야 함" + } + ], + + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/attendances", + "description": "근태 목록 조회" + }, + { + "method": "POST", + "endpoint": "/api/v1/attendances", + "description": "근태 등록" + }, + { + "method": "POST", + "endpoint": "/api/v1/attendance-reasons", + "description": "사유 등록" + }, + { + "method": "GET", + "endpoint": "/api/v1/attendances/export", + "description": "엑셀 다운로드" + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 1, + "name": "파일 다운로드", + "trigger": "엑셀 다운로드 버튼", + "verification": "Network API 호출 + 실제 파일 다운로드 확인", + "failCondition": "Console LOG만 존재, API 미호출", + "steps": ["step-13"] + }, + { + "id": 4, + "name": "모달 등록 완료", + "trigger": "근태 등록/사유 등록 모달의 저장 버튼", + "verification": "실제 등록 동작 + API 호출 + 결과 확인", + "failCondition": "열기/닫기만 테스트", + "steps": ["step-8", "step-11"] + }, + { + "id": 5, + "name": "목업/미완성 페이지 감지", + "trigger": "페이지 로드 시", + "verification": "입력 필드 + 동작하는 버튼 + API 호출 확인", + "failCondition": "버튼만 있고 입력 불가" + } + ] + }, + + "cleanup": { + "enabled": true, + "description": "테스트 후 등록한 근태/사유 데이터 삭제 (가능한 경우)" + }, + + "notes": { + "testScope": "근태관리 페이지 UI 요소 및 기능 검증", + "features": { + "dashboard": "미출근/정시출근/지각/휴가 현황 카드", + "dateFilter": "당해년도, 전전월, 전월, 당월, 어제, 오늘 빠른 선택", + "statusTabs": "전체, 미출근, 정시 출근, 지각, 결근, 휴가, 출장, 외근, 연장근무", + "attendanceRegister": "근태 등록 모달 (대상, 기준일, 출퇴근 시간, 연장근무)", + "reasonRegister": "사유 등록 모달 (출장/휴가/외근/연장근무 신청서)", + "search": "이름, 부서 검색", + "export": "엑셀 다운로드" + }, + "tableColumns": { + "번호": "순번", + "부서": "소속 부서", + "직책": "직책명", + "이름": "직원 이름", + "직급": "직급명", + "기준일": "근태 기준 날짜", + "출근": "출근 시간", + "퇴근": "퇴근 시간", + "휴게": "휴게 시간", + "연장근무": "연장근무 시간", + "사유": "근태 사유 (출장/휴가/외근 등)" + }, + "reasonTypes": [ + "출장신청서", + "휴가신청서", + "외근신청서", + "연장근무신청서" + ], + "prerequisites": "로그인된 사용자에게 근태 관리 권한 필요" + } +} diff --git a/attendance-settings.json b/attendance-settings.json new file mode 100644 index 0000000..ef83e77 --- /dev/null +++ b/attendance-settings.json @@ -0,0 +1,391 @@ +{ + "id": "attendance-settings", + "name": "설정 - 근태설정", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "url": "/ko/settings/attendance-settings", + "menuNavigation": { + "level1": "설정", + "level2": "근태설정", + "expectedUrl": "/ko/settings/attendance-settings" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebarSelector": ".sidebar-scroll, [data-testid='sidebar'], nav[role='navigation']", + "scrollStep": 200, + "maxScrollAttempts": 10, + "waitAfterScroll": 300, + "level1": { + "text": "설정", + "selectors": [ + "button:has-text('설정')", + "[data-menu='settings']", + "a:has-text('설정')" + ] + }, + "level2": { + "text": "출퇴근관리", + "selectors": [ + "a:has-text('출퇴근관리')", + "[href*='attendance-settings']", + "button:has-text('출퇴근관리')" + ] + }, + "fallbackUrl": "/ko/settings/attendance-settings" + }, + "expectedAPIs": [ + { + "method": "GET", + "path": "/api/v1/settings/attendance", + "description": "출퇴근 설정 조회" + }, + { + "method": "GET", + "path": "/api/v1/departments/tree", + "description": "부서 트리 조회" + }, + { + "method": "PUT", + "path": "/api/v1/settings/attendance", + "description": "출퇴근 설정 저장" + } + ], + "steps": [ + { + "id": 0, + "name": "사이드바 준비", + "description": "메뉴 탐색 전 사이드바 스크롤을 최상단으로 초기화", + "actions": [ + { "type": "evaluate", "script": "document.querySelector('.sidebar-scroll, [data-testid=\"sidebar\"], nav[role=\"navigation\"]')?.scrollTo({top:0,behavior:'instant'})" }, + { "type": "wait", "duration": 500 } + ], + "expected": { + "sidebarReady": true + } + }, + { + "id": 1, + "name": "2단계 메뉴 진입: 설정 > 출퇴근관리", + "description": "설정 > 출퇴근관리 메뉴로 이동하여 페이지 로드 확인 (scrollAndFind 패턴 사용)", + "actions": [ + { "type": "scrollAndFind", "target": "설정", "container": ".sidebar-scroll", "scrollStep": 200, "maxAttempts": 10 }, + { "type": "click", "target": "설정" }, + { "type": "wait", "duration": 500 }, + { "type": "scrollAndFind", "target": "출퇴근관리", "container": ".sidebar-scroll", "scrollStep": 200, "maxAttempts": 10 }, + { "type": "click", "target": "출퇴근관리" }, + { "type": "wait", "target": "페이지 로드 완료" } + ], + "expected": { + "url": "/ko/settings/attendance-settings", + "title": "출퇴근관리", + "authenticated": true + } + }, + { + "id": 2, + "name": "페이지 제목 확인", + "action": "verify", + "target": "heading", + "expected": "'출퇴근관리' 텍스트 표시" + }, + { + "id": 3, + "name": "설명 텍스트 확인", + "action": "verify", + "target": "description", + "expected": "'출퇴근 방법을 관리합니다.' 텍스트 표시" + }, + { + "id": 4, + "name": "GPS 출퇴근 카드 확인", + "action": "verify", + "target": "card", + "expected": "'GPS 출퇴근' 카드 표시" + }, + { + "id": 5, + "name": "자동 출퇴근 카드 확인", + "action": "verify", + "target": "card", + "expected": "'자동 출퇴근' 카드 표시" + }, + { + "id": 6, + "name": "저장 버튼 확인", + "action": "verify", + "target": "button", + "expected": "'저장' 버튼 표시 (초기 상태 비활성화 가능)" + }, + { + "id": 7, + "name": "초기 설정 상태 확인", + "action": "verify", + "target": "checkboxes", + "expected": "GPS 출퇴근, 자동 출퇴근 체크박스 상태 확인" + }, + { + "id": 8, + "name": "GPS 출퇴근 비활성화 상태 - 연동 부서 비활성화 확인", + "action": "verify", + "target": "combobox", + "expected": "GPS 체크박스가 OFF일 때 연동 부서 콤보박스 비활성화" + }, + { + "id": 9, + "name": "GPS 출퇴근 비활성화 상태 - 허용 반경 비활성화 확인", + "action": "verify", + "target": "select", + "expected": "GPS 체크박스가 OFF일 때 허용 반경 셀렉트 비활성화" + }, + { + "id": 10, + "name": "GPS 출퇴근 활성화", + "action": "click", + "target": "GPS 출퇴근 체크박스", + "expected": "체크박스 선택됨", + "critical": true + }, + { + "id": 11, + "name": "GPS 활성화 후 - 연동 부서 활성화 확인", + "action": "verify", + "target": "combobox", + "expected": "연동 부서 콤보박스 활성화됨" + }, + { + "id": 12, + "name": "GPS 활성화 후 - 허용 반경 활성화 확인", + "action": "verify", + "target": "select", + "expected": "허용 반경 셀렉트 활성화됨" + }, + { + "id": 13, + "name": "GPS 연동 부서 콤보박스 클릭", + "action": "click", + "target": "GPS 연동 부서 콤보박스", + "expected": "부서 선택 드롭다운 열림" + }, + { + "id": 14, + "name": "GPS 부서 선택 - 첫 번째 부서", + "action": "select", + "target": "부서 옵션", + "expected": "첫 번째 부서 선택됨" + }, + { + "id": 15, + "name": "GPS 부서 선택 - 두 번째 부서", + "action": "select", + "target": "부서 옵션", + "expected": "두 번째 부서 추가 선택됨 (다중 선택)" + }, + { + "id": 16, + "name": "GPS 연동 부서 드롭다운 닫기", + "action": "click", + "target": "외부 영역", + "expected": "드롭다운 닫힘, 선택된 부서 표시" + }, + { + "id": 17, + "name": "허용 반경 드롭다운 클릭", + "action": "click", + "target": "허용 반경 셀렉트", + "expected": "반경 옵션 드롭다운 열림" + }, + { + "id": 18, + "name": "허용 반경 변경", + "action": "select", + "target": "300M 옵션", + "expected": "'300M' 선택됨", + "critical": true + }, + { + "id": 19, + "name": "자동 출퇴근 활성화", + "action": "click", + "target": "자동 출퇴근 체크박스", + "expected": "체크박스 선택됨", + "critical": true + }, + { + "id": 20, + "name": "자동 출퇴근 활성화 후 - 연동 부서 활성화 확인", + "action": "verify", + "target": "combobox", + "expected": "자동 출퇴근 연동 부서 콤보박스 활성화됨" + }, + { + "id": 21, + "name": "자동 출퇴근 연동 부서 콤보박스 클릭", + "action": "click", + "target": "자동 출퇴근 연동 부서 콤보박스", + "expected": "부서 선택 드롭다운 열림" + }, + { + "id": 22, + "name": "자동 출퇴근 부서 선택", + "action": "select", + "target": "부서 옵션", + "expected": "부서 선택됨" + }, + { + "id": 23, + "name": "자동 출퇴근 연동 부서 드롭다운 닫기", + "action": "click", + "target": "외부 영역", + "expected": "드롭다운 닫힘" + }, + { + "id": 24, + "name": "저장 버튼 클릭", + "action": "click", + "target": "저장 버튼", + "expected": "저장 API 호출, 로딩 상태 표시", + "critical": true + }, + { + "id": 25, + "name": "저장 완료 토스트 확인", + "action": "verify", + "target": "toast", + "expected": "'출퇴근 설정이 저장되었습니다.' 토스트 표시" + }, + { + "id": 26, + "name": "URL 유지 확인", + "action": "verify", + "target": "url", + "expected": "URL이 /settings/attendance-settings 유지 (에러 페이지 이동 없음)" + }, + { + "id": 27, + "name": "페이지 새로고침", + "action": "reload", + "target": "page", + "expected": "페이지 새로고침 후 GET API 재호출" + }, + { + "id": 28, + "name": "설정 지속성 확인 - GPS 출퇴근", + "action": "verify", + "target": "checkbox", + "expected": "GPS 출퇴근 체크박스 선택 상태 유지" + }, + { + "id": 29, + "name": "설정 지속성 확인 - 허용 반경", + "action": "verify", + "target": "select", + "expected": "허용 반경 300M 선택 상태 유지" + }, + { + "id": 30, + "name": "GPS 출퇴근 비활성화", + "action": "click", + "target": "GPS 출퇴근 체크박스", + "expected": "체크박스 선택 해제" + }, + { + "id": 31, + "name": "GPS 비활성화 후 - 연동 부서 초기화 확인", + "action": "verify", + "target": "combobox", + "expected": "연동 부서 '부서 선택' 상태로 초기화 (UI 전용)" + }, + { + "id": 32, + "name": "GPS 비활성화 후 - 허용 반경 초기화 확인", + "action": "verify", + "target": "select", + "expected": "허용 반경 '100M'로 초기화 (UI 전용)" + }, + { + "id": 33, + "name": "GPS 비활성화 후 - 컨트롤 비활성화 확인", + "action": "verify", + "target": "controls", + "expected": "연동 부서 및 허용 반경 비활성화됨" + }, + { + "id": 34, + "name": "자동 출퇴근 비활성화", + "action": "click", + "target": "자동 출퇴근 체크박스", + "expected": "체크박스 선택 해제" + }, + { + "id": 35, + "name": "자동 출퇴근 비활성화 후 - 연동 부서 초기화 확인", + "action": "verify", + "target": "combobox", + "expected": "연동 부서 '부서 선택' 상태로 초기화 (UI 전용)" + }, + { + "id": 36, + "name": "자동 출퇴근 비활성화 후 - 컨트롤 비활성화 확인", + "action": "verify", + "target": "combobox", + "expected": "연동 부서 비활성화됨" + }, + { + "id": 37, + "name": "비활성화 상태 저장", + "action": "click", + "target": "저장 버튼", + "expected": "저장 API 호출" + }, + { + "id": 38, + "name": "비활성화 저장 완료 확인", + "action": "verify", + "target": "toast", + "expected": "'출퇴근 설정이 저장되었습니다.' 토스트 표시" + }, + { + "id": 39, + "name": "모든 허용 반경 옵션 테스트 - 50M", + "action": "test-options", + "target": "허용 반경 셀렉트", + "expected": "50M, 100M, 300M, 500M 옵션 모두 선택 가능" + }, + { + "id": 40, + "name": "콘솔 에러 확인", + "action": "verify", + "target": "console", + "expected": "콘솔에 에러 로그 없음" + }, + { + "id": 41, + "name": "안내 문구 확인", + "action": "verify", + "target": "help-text", + "expected": "3개의 안내 문구 표시" + }, + { + "id": 42, + "name": "최종 상태 확인", + "action": "verify", + "target": "page", + "expected": "페이지 정상 동작, 모든 설정 저장됨" + } + ], + "testData": { + "settings": { + "gpsEnabled": true, + "allowedRadius": 300, + "autoEnabled": true + }, + "radiusOptions": [ + { "value": 50, "label": "50M" }, + { "value": 100, "label": "100M" }, + { "value": 300, "label": "300M" }, + { "value": 500, "label": "500M" } + ] + } +} diff --git a/bad-debt-collection.json b/bad-debt-collection.json new file mode 100644 index 0000000..b61b6c0 --- /dev/null +++ b/bad-debt-collection.json @@ -0,0 +1,1090 @@ +{ + "id": "bad-debt-collection", + "name": "대손채권회수 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "회계관리 > 대손채권회수 메뉴의 체크박스 선택, 수정 모드, 입력 필드, 메모 추가, 서류 업로드, 이동 버튼 기능 전체 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/ko/accounting/bad-debt-collection", + "menuNavigation": { + "level1": "회계관리", + "level2": "악성채권추심관리", + "expectedUrl": "/ko/accounting/bad-debt-collection" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebarSelector": ".sidebar-scroll, [class*='sidebar'], nav[class*='menu']", + "scrollConfig": { + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + }, + "level1": { + "text": "회계관리", + "fallbackSelectors": [ + "span:has-text('회계관리')", + "[class*='menu-item']:has-text('회계관리')", + "a:has-text('회계관리')" + ] + }, + "level2": { + "text": "악성채권추심관리", + "fallbackSelectors": [ + "span:has-text('악성채권추심관리')", + "[class*='submenu']:has-text('악성채권추심관리')", + "a:has-text('악성채권추심관리')" + ] + }, + "expectedUrl": "/ko/accounting/bad-debt-collection" + }, + "testFocus": { + "primary": "수정 페이지 전체 입력 필드 및 메모/서류 추가 기능 검증", + "description": "체크박스 선택 시 수정 버튼 활성화, 수정 페이지 모든 입력 필드 테스트, 메모 추가/삭제, 추가 서류 업로드, 이동 버튼 동작 확인" + }, + "prerequisites": { + "authentication": true, + "testData": { + "vendorName": "테스트거래처", + "businessNumber": "123-45-67890", + "representativeName": "홍길동", + "address": { + "zipCode": "06234", + "address1": "서울특별시 강남구 테헤란로", + "address2": "123호" + }, + "contact": { + "phone": "02-1234-5678", + "mobile": "010-1234-5678", + "fax": "02-1234-5679", + "email": "test@example.com" + }, + "debtInfo": { + "amount": 5000000, + "overdueDays": 90, + "status": "collecting" + }, + "memo": "테스트 메모 내용입니다.", + "file": { + "businessRegistration": "사업자등록증.pdf", + "taxInvoice": "세금계산서.pdf", + "additional": "추가서류1.pdf" + } + } + }, + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/bad-debts", + "description": "대손채권 목록 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/bad-debts/{id}", + "description": "대손채권 상세 조회" + }, + { + "method": "PUT", + "endpoint": "/api/v1/bad-debts/{id}", + "description": "대손채권 수정" + }, + { + "method": "POST", + "endpoint": "/api/v1/bad-debts/{id}/memos", + "description": "메모 추가" + }, + { + "method": "DELETE", + "endpoint": "/api/v1/bad-debts/{id}/memos/{memoId}", + "description": "메모 삭제" + }, + { + "method": "POST", + "endpoint": "/api/v1/bad-debts/{id}/documents", + "description": "서류 업로드" + } + ], + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "메뉴 탐색 전 사이드바를 최상단으로 스크롤하여 초기화", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll, [class*=\"sidebar\"] > div')?.scrollTo({top: 0, behavior: 'instant'})" + }, + { "type": "wait", "duration": 500 } + ], + "expected": { + "sidebarReady": true + } + }, + { + "id": "step-1", + "name": "2단계 메뉴 진입: 회계관리 > 악성채권추심관리", + "description": "회계관리 > 악성채권추심관리 메뉴로 이동하여 페이지 로드 확인 (scrollAndFind 패턴 사용)", + "navigationPattern": "scrollAndFind", + "actions": [ + { + "type": "scrollAndFind", + "target": "회계관리", + "config": { + "scrollContainer": ".sidebar-scroll, [class*='sidebar']", + "scrollStep": 200, + "maxAttempts": 10, + "waitAfterScroll": 300 + } + }, + { "type": "click", "target": "회계관리" }, + { "type": "wait", "duration": 500 }, + { + "type": "scrollAndFind", + "target": "악성채권추심관리", + "config": { + "scrollContainer": ".sidebar-scroll, [class*='sidebar']", + "scrollStep": 200, + "maxAttempts": 10, + "waitAfterScroll": 300 + } + }, + { "type": "click", "target": "악성채권추심관리" }, + { "type": "wait", "target": "페이지 로드 완료" } + ], + "expected": { + "url": "/ko/accounting/bad-debt-collection", + "pageTitle": "악성채권추심관리", + "elements": ["통계 카드", "검색창", "필터", "테이블"], + "authenticated": true + } + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "통계 카드, 필터, 테이블 컬럼 구조 확인", + "actions": [ + { + "type": "verify", + "target": "페이지 구조" + } + ], + "expected": { + "stats": ["총 악성채권", "추심중", "법적조치", "회수완료"], + "filters": ["거래처 필터", "상태 필터", "정렬"], + "tableColumns": ["체크박스", "No.", "거래처", "채권금액", "발생일", "연체일수", "담당자", "상태", "설정", "작업"] + } + }, + { + "id": "step-3", + "name": "필터 및 검색 기능 테스트", + "description": "거래처 필터, 상태 필터, 정렬, 검색 기능 동작 확인", + "actions": [ + { + "type": "select", + "target": "상태 필터", + "value": "추심중" + }, + { + "type": "wait", + "target": "데이터 필터링" + }, + { + "type": "verify", + "target": "필터링된 데이터" + } + ], + "expected": { + "filterApplied": true, + "dataFiltered": "추심중 상태만 표시" + } + }, + { + "id": "step-4", + "name": "체크박스 선택 전 작업 버튼 확인", + "description": "체크박스 미선택 시 수정/삭제 버튼이 표시되지 않는지 확인", + "actions": [ + { + "type": "verify", + "target": "작업 컬럼" + } + ], + "expected": { + "editButtonVisible": false, + "deleteButtonVisible": false + } + }, + { + "id": "step-5", + "name": "첫 번째 행 체크박스 선택", + "description": "첫 번째 행의 체크박스 클릭하여 선택", + "actions": [ + { + "type": "click", + "target": "첫 번째 행 체크박스" + }, + { + "type": "wait", + "target": "체크박스 선택 반영" + } + ], + "expected": { + "checkboxChecked": true, + "rowHighlighted": true + } + }, + { + "id": "step-6", + "name": "수정 버튼 표시 확인", + "description": "체크박스 선택 후 작업 컬럼에 수정/삭제 버튼이 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "작업 컬럼 버튼" + } + ], + "expected": { + "editButtonVisible": true, + "deleteButtonVisible": true + } + }, + { + "id": "step-7", + "name": "수정 버튼 클릭", + "description": "수정 버튼 클릭하여 수정 페이지로 이동", + "actions": [ + { + "type": "click", + "target": "수정 버튼" + }, + { + "type": "wait", + "target": "페이지 이동" + } + ], + "expected": { + "urlPattern": "/accounting/bad-debt-collection/{id}?mode=edit", + "pageTitle": "악성채권추심관리 상세", + "formMode": "edit" + } + }, + { + "id": "step-8", + "name": "수정 페이지 구조 확인", + "description": "수정 페이지의 모든 섹션 및 버튼 확인", + "actions": [ + { + "type": "verify", + "target": "페이지 섹션 및 버튼" + } + ], + "expected": { + "sections": ["기본 정보", "연락처 정보", "담당자 정보", "필요 서류", "악성 채권 정보", "메모"], + "headerButtons": ["취소", "저장"], + "linkButtons": ["수취 어음 현황", "거래처 미수금 현황"] + } + }, + { + "id": "step-9", + "name": "기본 정보 섹션 - 사업자등록번호 (읽기전용)", + "description": "사업자등록번호 필드가 읽기 전용인지 확인", + "actions": [ + { + "type": "verify", + "target": "사업자등록번호 필드" + } + ], + "expected": { + "fieldDisabled": true, + "fieldValue": "존재" + } + }, + { + "id": "step-10", + "name": "기본 정보 섹션 - 거래처 코드 (읽기전용)", + "description": "거래처 코드 필드가 읽기 전용인지 확인", + "actions": [ + { + "type": "verify", + "target": "거래처 코드 필드" + } + ], + "expected": { + "fieldDisabled": true, + "fieldValue": "존재" + } + }, + { + "id": "step-11", + "name": "기본 정보 섹션 - 거래처명 입력", + "description": "거래처명 필드에 값 입력 테스트", + "actions": [ + { + "type": "clear", + "target": "거래처명 입력 필드" + }, + { + "type": "type", + "target": "거래처명 입력 필드", + "value": "수정된 테스트거래처" + } + ], + "expected": { + "fieldEditable": true, + "valueUpdated": "수정된 테스트거래처" + } + }, + { + "id": "step-12", + "name": "기본 정보 섹션 - 대표자명 입력", + "description": "대표자명 필드에 값 입력 테스트", + "actions": [ + { + "type": "clear", + "target": "대표자명 입력 필드" + }, + { + "type": "type", + "target": "대표자명 입력 필드", + "value": "홍길동" + } + ], + "expected": { + "fieldEditable": true, + "valueUpdated": "홍길동" + } + }, + { + "id": "step-13", + "name": "기본 정보 섹션 - 악성채권 등록 토글", + "description": "악성채권 등록 스위치 토글 동작 확인", + "actions": [ + { + "type": "click", + "target": "악성채권 등록 Switch" + }, + { + "type": "wait", + "target": "스위치 상태 변경" + } + ], + "expected": { + "switchToggled": true + } + }, + { + "id": "step-14", + "name": "기본 정보 섹션 - 업태/업종 입력", + "description": "업태, 업종 필드에 값 입력 테스트", + "actions": [ + { + "type": "clear", + "target": "업태 입력 필드" + }, + { + "type": "type", + "target": "업태 입력 필드", + "value": "도소매업" + }, + { + "type": "clear", + "target": "업종 입력 필드" + }, + { + "type": "type", + "target": "업종 입력 필드", + "value": "전자상거래" + } + ], + "expected": { + "businessTypeUpdated": "도소매업", + "businessCategoryUpdated": "전자상거래" + } + }, + { + "id": "step-15", + "name": "연락처 정보 섹션 - 우편번호 찾기 버튼", + "description": "우편번호 찾기 버튼 클릭 시 Daum 우편번호 서비스 팝업 표시", + "actions": [ + { + "type": "click", + "target": "우편번호 찾기 버튼" + }, + { + "type": "wait", + "target": "팝업 표시" + } + ], + "expected": { + "popupOpened": true, + "popupType": "Daum 우편번호 서비스" + } + }, + { + "id": "step-16", + "name": "연락처 정보 섹션 - 주소 입력", + "description": "기본주소 및 상세주소 입력 테스트 (우편번호 팝업 닫은 후)", + "actions": [ + { + "type": "escape", + "target": "팝업 닫기" + }, + { + "type": "clear", + "target": "상세주소 입력 필드" + }, + { + "type": "type", + "target": "상세주소 입력 필드", + "value": "456호" + } + ], + "expected": { + "address2Updated": "456호" + } + }, + { + "id": "step-17", + "name": "연락처 정보 섹션 - 전화번호 입력", + "description": "전화번호 필드에 값 입력 테스트", + "actions": [ + { + "type": "clear", + "target": "전화번호 입력 필드" + }, + { + "type": "type", + "target": "전화번호 입력 필드", + "value": "02-9999-8888" + } + ], + "expected": { + "phoneUpdated": "02-9999-8888" + } + }, + { + "id": "step-18", + "name": "연락처 정보 섹션 - 모바일 입력", + "description": "모바일 필드에 값 입력 테스트", + "actions": [ + { + "type": "clear", + "target": "모바일 입력 필드" + }, + { + "type": "type", + "target": "모바일 입력 필드", + "value": "010-9999-8888" + } + ], + "expected": { + "mobileUpdated": "010-9999-8888" + } + }, + { + "id": "step-19", + "name": "연락처 정보 섹션 - 팩스 입력", + "description": "팩스 필드에 값 입력 테스트", + "actions": [ + { + "type": "clear", + "target": "팩스 입력 필드" + }, + { + "type": "type", + "target": "팩스 입력 필드", + "value": "02-9999-8889" + } + ], + "expected": { + "faxUpdated": "02-9999-8889" + } + }, + { + "id": "step-20", + "name": "연락처 정보 섹션 - 이메일 입력", + "description": "이메일 필드에 값 입력 테스트", + "actions": [ + { + "type": "clear", + "target": "이메일 입력 필드" + }, + { + "type": "type", + "target": "이메일 입력 필드", + "value": "updated@example.com" + } + ], + "expected": { + "emailUpdated": "updated@example.com" + } + }, + { + "id": "step-21", + "name": "담당자 정보 섹션 - 담당자명 입력", + "description": "담당자명 필드에 값 입력 테스트", + "actions": [ + { + "type": "clear", + "target": "담당자명 입력 필드" + }, + { + "type": "type", + "target": "담당자명 입력 필드", + "value": "김담당" + } + ], + "expected": { + "contactNameUpdated": "김담당" + } + }, + { + "id": "step-22", + "name": "담당자 정보 섹션 - 담당자 전화 입력", + "description": "담당자 전화 필드에 값 입력 테스트", + "actions": [ + { + "type": "clear", + "target": "담당자 전화 입력 필드" + }, + { + "type": "type", + "target": "담당자 전화 입력 필드", + "value": "010-1111-2222" + } + ], + "expected": { + "contactPhoneUpdated": "010-1111-2222" + } + }, + { + "id": "step-23", + "name": "필요 서류 섹션 - 사업자등록증 업로드 확인", + "description": "사업자등록증 파일 업로드 필드 존재 확인", + "actions": [ + { + "type": "verify", + "target": "사업자등록증 파일 입력" + } + ], + "expected": { + "fileInputExists": true, + "acceptTypes": ".pdf,.jpg,.jpeg,.png" + } + }, + { + "id": "step-24", + "name": "필요 서류 섹션 - 세금계산서 업로드 확인", + "description": "세금계산서 파일 업로드 필드 존재 확인", + "actions": [ + { + "type": "verify", + "target": "세금계산서 파일 입력" + } + ], + "expected": { + "fileInputExists": true, + "acceptTypes": ".pdf,.jpg,.jpeg,.png" + } + }, + { + "id": "step-25", + "name": "필요 서류 섹션 - 추가 서류 추가 버튼 확인", + "description": "추가 서류 섹션에 추가 버튼이 존재하는지 확인", + "actions": [ + { + "type": "verify", + "target": "추가 서류 추가 버튼" + } + ], + "expected": { + "addButtonExists": true, + "buttonText": "추가" + } + }, + { + "id": "step-26", + "name": "악성 채권 정보 섹션 - 미수금 입력", + "description": "미수금 필드에 값 입력 테스트", + "actions": [ + { + "type": "clear", + "target": "미수금 입력 필드" + }, + { + "type": "type", + "target": "미수금 입력 필드", + "value": "7500000" + } + ], + "expected": { + "debtAmountUpdated": 7500000 + } + }, + { + "id": "step-27", + "name": "악성 채권 정보 섹션 - 상태 선택", + "description": "상태 드롭다운에서 값 선택 테스트", + "actions": [ + { + "type": "click", + "target": "상태 드롭다운" + }, + { + "type": "select", + "target": "상태 옵션", + "value": "법적조치" + } + ], + "expected": { + "statusUpdated": "legalAction", + "optionSelected": "법적조치" + } + }, + { + "id": "step-28", + "name": "악성 채권 정보 섹션 - 연체일수 입력", + "description": "연체일수 필드에 값 입력 테스트", + "actions": [ + { + "type": "clear", + "target": "연체일수 입력 필드" + }, + { + "type": "type", + "target": "연체일수 입력 필드", + "value": "120" + } + ], + "expected": { + "overdueDaysUpdated": 120 + } + }, + { + "id": "step-29", + "name": "악성 채권 정보 섹션 - 본사 담당자 선택", + "description": "본사 담당자 드롭다운에서 값 선택 테스트", + "actions": [ + { + "type": "click", + "target": "본사 담당자 드롭다운" + }, + { + "type": "select", + "target": "담당자 옵션", + "value": "첫 번째 담당자" + } + ], + "expected": { + "managerSelected": true, + "managerName": "존재" + } + }, + { + "id": "step-30", + "name": "악성 채권 정보 섹션 - 악성채권 발생일 입력", + "description": "악성채권 발생일 필드에 날짜 입력 테스트", + "actions": [ + { + "type": "clear", + "target": "악성채권 발생일 입력 필드" + }, + { + "type": "type", + "target": "악성채권 발생일 입력 필드", + "value": "2025-12-01" + } + ], + "expected": { + "occurrenceDateUpdated": "2025-12-01" + } + }, + { + "id": "step-31", + "name": "악성 채권 정보 섹션 - 악성채권 종료일 입력", + "description": "악성채권 종료일 필드에 날짜 입력 테스트", + "actions": [ + { + "type": "clear", + "target": "악성채권 종료일 입력 필드" + }, + { + "type": "type", + "target": "악성채권 종료일 입력 필드", + "value": "2026-03-01" + } + ], + "expected": { + "endDateUpdated": "2026-03-01" + } + }, + { + "id": "step-32", + "name": "이동 버튼 - 수취 어음 현황 버튼 확인", + "description": "수취 어음 현황 버튼 존재 확인", + "actions": [ + { + "type": "verify", + "target": "수취 어음 현황 버튼" + } + ], + "expected": { + "buttonExists": true, + "buttonText": "수취 어음 현황" + } + }, + { + "id": "step-33", + "name": "이동 버튼 - 수취 어음 현황 클릭", + "description": "수취 어음 현황 버튼 클릭 시 해당 페이지로 이동하는지 확인", + "actions": [ + { + "type": "click", + "target": "수취 어음 현황 버튼" + }, + { + "type": "wait", + "target": "페이지 이동" + } + ], + "expected": { + "urlPattern": "/accounting/bills?vendorId={id}&type=received", + "pageChanged": true + } + }, + { + "id": "step-34", + "name": "이동 후 뒤로가기 (수취 어음 현황 → 대손채권회수 수정)", + "description": "브라우저 뒤로가기로 대손채권회수 수정 페이지로 복귀", + "actions": [ + { + "type": "back", + "target": "브라우저 뒤로가기" + }, + { + "type": "wait", + "target": "페이지 로드" + } + ], + "expected": { + "urlPattern": "/accounting/bad-debt-collection/{id}?mode=edit", + "pageRestored": true + } + }, + { + "id": "step-35", + "name": "이동 버튼 - 거래처 미수금 현황 버튼 확인", + "description": "거래처 미수금 현황 버튼 존재 확인", + "actions": [ + { + "type": "verify", + "target": "거래처 미수금 현황 버튼" + } + ], + "expected": { + "buttonExists": true, + "buttonText": "거래처 미수금 현황" + } + }, + { + "id": "step-36", + "name": "이동 버튼 - 거래처 미수금 현황 클릭", + "description": "거래처 미수금 현황 버튼 클릭 시 해당 페이지로 이동하는지 확인", + "actions": [ + { + "type": "click", + "target": "거래처 미수금 현황 버튼" + }, + { + "type": "wait", + "target": "페이지 이동" + } + ], + "expected": { + "urlPattern": "/accounting/receivables-status?highlight={vendorId}", + "pageChanged": true + } + }, + { + "id": "step-37", + "name": "이동 후 뒤로가기 (거래처 미수금 현황 → 대손채권회수 수정)", + "description": "브라우저 뒤로가기로 대손채권회수 수정 페이지로 복귀", + "actions": [ + { + "type": "back", + "target": "브라우저 뒤로가기" + }, + { + "type": "wait", + "target": "페이지 로드" + } + ], + "expected": { + "urlPattern": "/accounting/bad-debt-collection/{id}?mode=edit", + "pageRestored": true + } + }, + { + "id": "step-38", + "name": "메모 섹션 - 메모 입력 필드 확인", + "description": "메모 입력 Textarea 존재 확인", + "actions": [ + { + "type": "verify", + "target": "메모 입력 Textarea" + } + ], + "expected": { + "textareaExists": true, + "placeholder": "메모를 입력하세요..." + } + }, + { + "id": "step-39", + "name": "메모 섹션 - 메모 추가 버튼 확인", + "description": "메모 추가 버튼 존재 확인", + "actions": [ + { + "type": "verify", + "target": "메모 추가 버튼" + } + ], + "expected": { + "buttonExists": true, + "buttonText": "추가" + } + }, + { + "id": "step-40", + "name": "메모 섹션 - 메모 입력", + "description": "메모 입력 필드에 텍스트 입력", + "actions": [ + { + "type": "clear", + "target": "메모 입력 Textarea" + }, + { + "type": "type", + "target": "메모 입력 Textarea", + "value": "이것은 테스트 메모입니다." + } + ], + "expected": { + "memoEntered": "이것은 테스트 메모입니다." + } + }, + { + "id": "step-41", + "name": "메모 섹션 - 메모 추가 버튼 클릭", + "description": "메모 추가 버튼 클릭하여 메모 추가", + "actions": [ + { + "type": "click", + "target": "메모 추가 버튼" + }, + { + "type": "wait", + "target": "메모 추가 완료" + } + ], + "expected": { + "memoAdded": true, + "memoListUpdated": "메모 카드 표시됨" + } + }, + { + "id": "step-42", + "name": "메모 섹션 - 추가된 메모 확인", + "description": "추가된 메모가 메모 리스트에 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "메모 리스트" + } + ], + "expected": { + "memoDisplayed": true, + "memoContent": "이것은 테스트 메모입니다." + } + }, + { + "id": "step-43", + "name": "메모 섹션 - 추가 메모 입력", + "description": "두 번째 메모 입력 및 추가", + "actions": [ + { + "type": "clear", + "target": "메모 입력 Textarea" + }, + { + "type": "type", + "target": "메모 입력 Textarea", + "value": "두 번째 테스트 메모" + }, + { + "type": "click", + "target": "메모 추가 버튼" + }, + { + "type": "wait", + "target": "메모 추가 완료" + } + ], + "expected": { + "memoAdded": true, + "memoCount": 2 + } + }, + { + "id": "step-44", + "name": "메모 섹션 - 메모 삭제 버튼 확인", + "description": "메모 카드에 삭제 버튼(X)이 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "메모 삭제 버튼" + } + ], + "expected": { + "deleteButtonExists": true + } + }, + { + "id": "step-45", + "name": "메모 섹션 - 메모 삭제", + "description": "첫 번째 메모의 삭제 버튼 클릭하여 삭제", + "actions": [ + { + "type": "click", + "target": "첫 번째 메모 삭제 버튼" + }, + { + "type": "wait", + "target": "메모 삭제 완료" + } + ], + "expected": { + "memoDeleted": true, + "memoCount": 1 + } + }, + { + "id": "step-46", + "name": "저장 버튼 클릭", + "description": "헤더의 저장 버튼 클릭하여 저장 확인 다이얼로그 표시", + "actions": [ + { + "type": "click", + "target": "저장 버튼" + }, + { + "type": "wait", + "target": "다이얼로그 표시" + } + ], + "expected": { + "dialogOpened": true, + "dialogTitle": "저장 확인", + "dialogMessage": "입력한 내용을 저장하시겠습니까?" + } + }, + { + "id": "step-47", + "name": "저장 확인 다이얼로그 - 취소 버튼", + "description": "저장 확인 다이얼로그에서 취소 버튼 클릭", + "actions": [ + { + "type": "click", + "target": "다이얼로그 취소 버튼" + }, + { + "type": "wait", + "target": "다이얼로그 닫힘" + } + ], + "expected": { + "dialogClosed": true, + "pageUnchanged": "여전히 수정 페이지" + } + }, + { + "id": "step-48", + "name": "저장 버튼 재클릭 및 확인", + "description": "저장 버튼 다시 클릭 후 저장 버튼 클릭하여 저장 수행", + "actions": [ + { + "type": "click", + "target": "저장 버튼" + }, + { + "type": "wait", + "target": "다이얼로그 표시" + }, + { + "type": "click", + "target": "다이얼로그 저장 버튼" + }, + { + "type": "wait", + "target": "저장 처리 및 페이지 이동" + } + ], + "expected": { + "apiCall": "PUT /api/v1/bad-debts/{id}", + "apiResponse": "200 OK", + "urlChanged": "/accounting/bad-debt-collection/{id}", + "successToast": "악성채권이 수정되었습니다." + } + }, + { + "id": "step-49", + "name": "상세 페이지 확인", + "description": "저장 후 상세 보기 페이지로 이동되었는지 확인", + "actions": [ + { + "type": "verify", + "target": "페이지 모드 및 버튼" + } + ], + "expected": { + "pageMode": "view", + "headerButtons": ["삭제", "수정"], + "fieldsDisabled": true + } + }, + { + "id": "step-50", + "name": "목록으로 돌아가기", + "description": "뒤로가기 버튼 클릭하여 목록 페이지로 복귀", + "actions": [ + { + "type": "click", + "target": "뒤로가기 버튼" + }, + { + "type": "wait", + "target": "페이지 이동" + } + ], + "expected": { + "url": "/accounting/bad-debt-collection", + "pageTitle": "악성채권추심관리" + } + } + ], + "cleanup": { + "description": "테스트 후 입력된 데이터는 DB에 저장됨 (필요시 수동 정리)", + "actions": [] + }, + "notes": [ + "체크박스 선택 시 수정/삭제 버튼이 표시되는지 확인", + "수정 페이지의 모든 입력 필드 (기본 정보, 연락처, 담당자, 악성 채권 정보) 입력 테스트", + "메모 추가 버튼 클릭 시 메모 리스트에 추가되는지 확인", + "메모 삭제 버튼(X) 클릭 시 메모가 삭제되는지 확인", + "추가 서류 추가 버튼이 존재하는지 확인 (실제 파일 업로드는 브라우저 제약으로 스킵)", + "수취 어음 현황 버튼 클릭 시 /accounting/bills 페이지로 이동하는지 확인", + "거래처 미수금 현황 버튼 클릭 시 /accounting/receivables-status 페이지로 이동하는지 확인", + "저장 버튼 클릭 시 저장 확인 다이얼로그 표시 및 API 호출 확인", + "저장 완료 후 상세 보기 페이지로 이동하는지 확인" + ] +} diff --git a/bank-account-management.json b/bank-account-management.json new file mode 100644 index 0000000..670eccf --- /dev/null +++ b/bank-account-management.json @@ -0,0 +1,341 @@ +{ + "id": "bank-account-management", + "name": "계좌관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "설정 > 계좌관리 페이지의 계좌 등록/조회/수정/삭제 기능을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/settings/bank-account", + "menuNavigation": { + "level1": "설정", + "level2": "계좌관리", + "expectedUrl": "/settings/bank-account" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebar": { + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 5, + "waitAfterScroll": 300 + }, + "level1": { + "text": "설정", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "계좌관리", + "waitAfterClick": 300 + }, + "fallbackUrl": "/settings/bank-account", + "expectedUrl": "/settings/bank-account" + }, + "timeout": 90000, + "tags": ["settings", "bank-account", "crud"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "testData": { + "bankAccount": { + "bank": "KB국민은행", + "accountNumber": "123-456-789012", + "accountName": "E2E 테스트 계좌", + "accountHolder": "테스트사용자", + "status": "사용" + } + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 500 } + ] + }, + { + "id": "step-1", + "name": "설정 메뉴 진입", + "description": "설정 > 계좌관리 메뉴로 이동", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "설정", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "설정" }, + { "type": "wait", "duration": 500 }, + { "type": "click", "target": "계좌관리" } + ], + "expect": { + "url": "/settings/bank-account", + "visible": ["계좌관리", "계좌 등록"] + }, + "fallback": { + "type": "navigate", + "url": "/settings/bank-account" + } + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "테이블 구조 확인", + "verify": { + "visible": ["계좌관리"], + "tableColumns": ["번호", "은행", "계좌번호", "계좌명", "예금주", "상태"] + } + }, + { + "id": "step-3", + "name": "필수 검증 #2: 계좌 등록 모달 열기", + "description": "계좌 등록 버튼 클릭하여 모달 열기", + "actions": [ + { "type": "openModal", "target": "계좌 등록", "description": "계좌 등록 모달 열기" } + ], + "modalConfig": { + "containerSelector": "[role='dialog'], .modal", + "animationDelay": 300, + "waitForSelector": "[role='dialog']" + }, + "expect": { + "modal": "계좌 등록", + "visible": ["은행", "계좌번호", "계좌명", "예금주"] + } + }, + { + "id": "step-4", + "name": "계좌 등록 폼 입력", + "description": "모달 내 계좌 정보 입력", + "actions": [ + { + "type": "selectInModal", + "target": "은행", + "value": "{testData.bankAccount.bank}", + "description": "모달 내 은행 콤보박스 선택", + "options": { "waitAfter": 300 } + }, + { + "type": "fillInModal", + "target": "계좌번호", + "value": "{testData.bankAccount.accountNumber}", + "description": "모달 내 계좌번호 입력", + "options": { "waitAfter": 100 } + }, + { + "type": "fillInModal", + "target": "계좌명", + "value": "{testData.bankAccount.accountName}", + "description": "모달 내 계좌명 입력", + "options": { "waitAfter": 100 } + }, + { + "type": "fillInModal", + "target": "예금주", + "value": "{testData.bankAccount.accountHolder}", + "description": "모달 내 예금주 입력", + "options": { "waitAfter": 100 } + } + ] + }, + { + "id": "step-5", + "name": "필수 검증 #2: 계좌 등록 저장", + "description": "모달 내 등록 버튼 클릭하여 계좌 저장", + "actions": [ + { + "type": "clickInModal", + "target": "등록", + "description": "모달 내 등록 버튼 클릭", + "options": { "waitAfter": 500 } + } + ], + "expect": { + "urlMaintained": true, + "noErrorPage": true, + "toast": ["등록", "완료", "성공"], + "modalClosed": true + }, + "verify": { + "apiCall": "POST /api/settings/bank-account" + } + }, + { + "id": "step-6", + "name": "필수 검증 #4: 등록 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 등록 확인 필수!", + "description": "테이블에서 등록된 계좌 확인", + "verify": { + "tableContains": ["{testData.bankAccount.accountName}"], + "recordCountIncreased": true + } + }, + { + "id": "step-7", + "name": "계좌 상세 열기", + "description": "등록된 계좌 항목 클릭하여 상세 보기", + "actions": [ + { + "type": "findRow", + "contains": "{testData.bankAccount.accountName}", + "then": { + "type": "click", + "target": "row" + } + } + ], + "expect": { + "modal": "계좌 상세", + "visible": ["수정", "삭제"] + } + }, + { + "id": "step-8", + "name": "계좌 정보 수정", + "description": "계좌 정보 수정 테스트", + "actions": [ + { "type": "click", "target": "수정" }, + { "type": "wait", "duration": 300 }, + { "type": "clear", "target": "계좌명" }, + { "type": "fill", "target": "계좌명", "value": "E2E 테스트 계좌 수정" }, + { "type": "click", "target": "저장" } + ], + "expect": { + "toast": ["수정", "완료", "성공"], + "modalClosed": true + } + }, + { + "id": "step-9", + "name": "필수 검증 #4: 수정 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", + "description": "테이블에서 수정된 계좌 확인", + "verify": { + "tableContains": "E2E 테스트 계좌 수정" + } + }, + { + "id": "step-10", + "name": "계좌 삭제 준비", + "description": "삭제할 계좌 선택", + "actions": [ + { + "type": "findRow", + "contains": "E2E 테스트 계좌 수정", + "then": { + "type": "click", + "target": "row" + } + } + ], + "expect": { + "modal": "계좌 상세", + "visible": ["삭제"] + } + }, + { + "id": "step-11", + "name": "계좌 삭제", + "description": "삭제 버튼 클릭하여 계좌 삭제", + "actions": [ + { "type": "click", "target": "삭제" } + ], + "expect": { + "confirmDialog": true, + "dialogText": ["삭제", "하시겠습니까"] + } + }, + { + "id": "step-12", + "name": "삭제 확인", + "description": "삭제 확인 다이얼로그에서 확인 클릭", + "actions": [ + { "type": "click", "target": "확인" } + ], + "expect": { + "toast": ["삭제", "완료", "성공"], + "modalClosed": true + } + }, + { + "id": "step-13", + "name": "필수 검증 #4: 삭제 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 삭제 확인 필수!", + "description": "테이블에서 삭제된 계좌가 없는지 확인", + "verify": { + "tableNotContains": "E2E 테스트 계좌", + "recordCountDecreased": true + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/settings/bank-account", + "message": "계좌관리 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "button:has-text('계좌 등록')", + "message": "계좌 등록 버튼이 표시되어야 함" + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 2, + "name": "등록/저장 버튼", + "trigger": "계좌 등록 버튼", + "verification": "URL 유지 + 에러 페이지 없음 + 성공 토스트 + 데이터 반영", + "failCondition": "404/500 에러 페이지 이동" + }, + { + "id": 4, + "name": "모달 등록 완료", + "trigger": "계좌 등록 모달", + "verification": "실제 저장 동작 + 결과 확인", + "failCondition": "열기/닫기만 테스트" + } + ] + }, + + "cleanup": { + "enabled": true, + "description": "테스트 중 생성된 계좌 데이터 삭제", + "actions": [ + { + "type": "deleteTestData", + "condition": "contains:E2E 테스트" + } + ] + }, + + "notes": { + "testScope": "계좌 등록 → 조회 → 수정 → 삭제 전체 CRUD 테스트", + "tableColumns": ["번호", "은행", "계좌번호", "계좌명", "예금주", "상태"], + "modalFields": ["은행", "계좌번호", "계좌명", "예금주", "상태"], + "prerequisites": "로그인된 사용자에게 계좌 관리 권한 필요" + } +} diff --git a/bank-transactions.json b/bank-transactions.json new file mode 100644 index 0000000..e89f96e --- /dev/null +++ b/bank-transactions.json @@ -0,0 +1,439 @@ +{ + "id": "bank-transactions", + "name": "은행거래 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "은행거래 목록 조회 및 기간별 데이터 필터링 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/ko/accounting/bank-transactions", + "menuNavigation": { + "level1": "회계관리", + "level2": "입출금계좌조회", + "expectedUrl": "/ko/accounting/bank-transactions" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebarSelector": ".sidebar-scroll, [class*='sidebar'], nav[class*='menu']", + "scrollConfig": { + "scrollToTopFirst": true, + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + }, + "level1": { + "text": "회계관리", + "fallbackSelectors": [ + "//span[contains(text(),'회계관리')]", + "//div[contains(text(),'회계관리')]", + "[data-menu='accounting']" + ] + }, + "level2": { + "text": "은행거래", + "fallbackSelectors": [ + "//span[contains(text(),'은행거래')]", + "//a[contains(text(),'은행거래')]", + "[href*='bank-transactions']" + ] + }, + "verification": { + "expectedUrl": "/ko/accounting/bank-transactions", + "timeout": 5000 + } + }, + "timeout": 60000, + "tags": ["accounting", "bank", "filter", "date-range"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "testFocus": { + "primary": "기간별 데이터 조회 검증", + "description": "각 기간 버튼 클릭 및 직접 날짜 입력 시 해당 기간의 데이터가 정확히 조회되는지 확인" + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 준비", + "description": "사이드바 스크롤을 최상단으로 이동하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll, [class*=\"sidebar\"] > div, nav[class*=\"menu\"]')?.scrollTo({top: 0, behavior: 'instant'})", + "description": "사이드바 스크롤 최상단 이동" + }, + { + "type": "wait", + "duration": 500, + "description": "스크롤 안정화 대기" + } + ], + "expect": { + "sidebarReady": true + } + }, + { + "id": "step-1", + "name": "은행거래 메뉴 진입", + "description": "회계관리 > 은행거래 메뉴로 이동 (scrollAndFind 패턴 사용)", + "menuNavigation": { + "method": "scrollAndFind", + "level1": { + "text": "회계관리", + "scrollSearch": true, + "clickAction": "expand" + }, + "level2": { + "text": "은행거래", + "scrollSearch": true, + "clickAction": "navigate" + }, + "fallbackUrl": "/ko/accounting/bank-transactions" + }, + "expect": { + "url": "/accounting/bank-transactions", + "visible": ["은행거래"] + } + }, + { + "id": "step-2", + "name": "목록 페이지 구조 확인", + "description": "테이블 컬럼 및 UI 요소 확인", + "expect": { + "visible": ["거래일", "계좌", "입금", "출금", "잔액", "적요"], + "elements": { + "dateFilter": { + "startDate": "시작일 입력 필드", + "endDate": "종료일 입력 필드" + }, + "periodButtons": ["당해년도", "전전월", "전월", "당월", "어제", "오늘"], + "pagination": true + } + } + }, + { + "id": "step-3", + "name": "기본 데이터 확인", + "description": "초기 로드된 데이터 및 현재 날짜 필터 상태 확인", + "actions": [ + { "type": "capture", "target": "dateRange", "description": "현재 설정된 날짜 범위 기록" }, + { "type": "capture", "target": "dataCount", "description": "현재 조회된 데이터 건수 기록" } + ], + "expect": { + "dataLoaded": true, + "dateRangeVisible": true + } + }, + { + "id": "step-4", + "name": "당해년도 버튼 클릭 테스트", + "description": "당해년도 버튼 클릭 시 해당 기간 데이터 조회 확인", + "actions": [ + { "type": "click", "target": "당해년도", "description": "당해년도 버튼 클릭" } + ], + "expect": { + "dateRange": { + "startDate": "2026-01-01", + "endDate": "2026-12-31" + }, + "buttonActive": "당해년도", + "dataRefreshed": true + }, + "validation": { + "type": "dateRangeMatch", + "description": "조회된 데이터의 거래일이 2026년 범위 내인지 확인" + } + }, + { + "id": "step-5", + "name": "전전월 버튼 클릭 테스트", + "description": "전전월 버튼 클릭 시 해당 기간 데이터 조회 확인", + "actions": [ + { "type": "click", "target": "전전월", "description": "전전월 버튼 클릭" } + ], + "expect": { + "dateRange": { + "startDate": "2025-11-01", + "endDate": "2025-11-30", + "note": "현재 날짜 기준 전전월 (2026-01-15 기준 → 2025년 11월)" + }, + "buttonActive": "전전월", + "dataRefreshed": true + }, + "validation": { + "type": "dateRangeMatch", + "description": "조회된 데이터의 거래일이 전전월 범위 내인지 확인" + } + }, + { + "id": "step-6", + "name": "전월 버튼 클릭 테스트", + "description": "전월 버튼 클릭 시 해당 기간 데이터 조회 확인", + "actions": [ + { "type": "click", "target": "전월", "description": "전월 버튼 클릭" } + ], + "expect": { + "dateRange": { + "startDate": "2025-12-01", + "endDate": "2025-12-31", + "note": "현재 날짜 기준 전월 (2026-01-15 기준 → 2025년 12월)" + }, + "buttonActive": "전월", + "dataRefreshed": true + }, + "validation": { + "type": "dateRangeMatch", + "description": "조회된 데이터의 거래일이 전월 범위 내인지 확인" + } + }, + { + "id": "step-7", + "name": "당월 버튼 클릭 테스트", + "description": "당월 버튼 클릭 시 해당 기간 데이터 조회 확인", + "actions": [ + { "type": "click", "target": "당월", "description": "당월 버튼 클릭" } + ], + "expect": { + "dateRange": { + "startDate": "2026-01-01", + "endDate": "2026-01-31", + "note": "현재 날짜 기준 당월 (2026년 1월)" + }, + "buttonActive": "당월", + "dataRefreshed": true + }, + "validation": { + "type": "dateRangeMatch", + "description": "조회된 데이터의 거래일이 당월 범위 내인지 확인" + } + }, + { + "id": "step-8", + "name": "어제 버튼 클릭 테스트", + "description": "어제 버튼 클릭 시 해당 날짜 데이터 조회 확인", + "actions": [ + { "type": "click", "target": "어제", "description": "어제 버튼 클릭" } + ], + "expect": { + "dateRange": { + "startDate": "2026-01-14", + "endDate": "2026-01-14", + "note": "현재 날짜 기준 어제 (2026-01-15 기준 → 2026-01-14)" + }, + "buttonActive": "어제", + "dataRefreshed": true + }, + "validation": { + "type": "dateRangeMatch", + "description": "조회된 데이터의 거래일이 어제인지 확인" + } + }, + { + "id": "step-9", + "name": "오늘 버튼 클릭 테스트", + "description": "오늘 버튼 클릭 시 해당 날짜 데이터 조회 확인", + "actions": [ + { "type": "click", "target": "오늘", "description": "오늘 버튼 클릭" } + ], + "expect": { + "dateRange": { + "startDate": "2026-01-15", + "endDate": "2026-01-15", + "note": "현재 날짜 (2026-01-15)" + }, + "buttonActive": "오늘", + "dataRefreshed": true + }, + "validation": { + "type": "dateRangeMatch", + "description": "조회된 데이터의 거래일이 오늘인지 확인" + } + }, + { + "id": "step-10", + "name": "직접 날짜 입력 테스트 - 특정 기간", + "description": "시작일/종료일 직접 입력 후 데이터 조회 확인", + "actions": [ + { "type": "clear", "target": "startDate", "description": "시작일 필드 초기화" }, + { "type": "type", "target": "startDate", "value": "2025-10-01", "description": "시작일 입력" }, + { "type": "clear", "target": "endDate", "description": "종료일 필드 초기화" }, + { "type": "type", "target": "endDate", "value": "2025-10-31", "description": "종료일 입력" }, + { "type": "click", "target": "새로고침 또는 검색", "description": "데이터 새로고침" } + ], + "expect": { + "dateRange": { + "startDate": "2025-10-01", + "endDate": "2025-10-31" + }, + "dataRefreshed": true + }, + "validation": { + "type": "dateRangeMatch", + "description": "조회된 데이터의 거래일이 2025-10-01 ~ 2025-10-31 범위 내인지 확인" + } + }, + { + "id": "step-11", + "name": "직접 날짜 입력 테스트 - 단일 날짜", + "description": "시작일/종료일 동일하게 입력하여 특정 날짜 데이터만 조회", + "actions": [ + { "type": "clear", "target": "startDate", "description": "시작일 필드 초기화" }, + { "type": "type", "target": "startDate", "value": "2025-12-25", "description": "시작일 입력" }, + { "type": "clear", "target": "endDate", "description": "종료일 필드 초기화" }, + { "type": "type", "target": "endDate", "value": "2025-12-25", "description": "종료일 입력" }, + { "type": "click", "target": "새로고침 또는 검색", "description": "데이터 새로고침" } + ], + "expect": { + "dateRange": { + "startDate": "2025-12-25", + "endDate": "2025-12-25" + }, + "dataRefreshed": true + }, + "validation": { + "type": "dateRangeMatch", + "description": "조회된 데이터의 거래일이 2025-12-25인지 확인" + } + }, + { + "id": "step-12", + "name": "데이터 없는 기간 조회 테스트", + "description": "데이터가 없는 기간 조회 시 빈 상태 표시 확인", + "actions": [ + { "type": "clear", "target": "startDate", "description": "시작일 필드 초기화" }, + { "type": "type", "target": "startDate", "value": "2020-01-01", "description": "시작일 입력 (과거)" }, + { "type": "clear", "target": "endDate", "description": "종료일 필드 초기화" }, + { "type": "type", "target": "endDate", "value": "2020-01-31", "description": "종료일 입력 (과거)" }, + { "type": "click", "target": "새로고침 또는 검색", "description": "데이터 새로고침" } + ], + "expect": { + "emptyState": true, + "message": "검색 결과가 없습니다" + } + }, + { + "id": "step-13", + "name": "넓은 기간 조회 테스트", + "description": "전체 데이터 조회를 위한 넓은 기간 설정", + "actions": [ + { "type": "clear", "target": "startDate", "description": "시작일 필드 초기화" }, + { "type": "type", "target": "startDate", "value": "2025-01-01", "description": "시작일 입력" }, + { "type": "clear", "target": "endDate", "description": "종료일 필드 초기화" }, + { "type": "type", "target": "endDate", "value": "2025-12-31", "description": "종료일 입력" }, + { "type": "click", "target": "새로고침 또는 검색", "description": "데이터 새로고침" } + ], + "expect": { + "dataLoaded": true, + "dataCountGreaterThan": 0 + }, + "validation": { + "type": "dateRangeMatch", + "description": "조회된 모든 데이터의 거래일이 2025년도 범위 내인지 확인" + } + }, + { + "id": "step-14", + "name": "테이블 데이터 검증", + "description": "조회된 데이터의 거래일이 설정된 기간 내인지 상세 검증", + "actions": [ + { "type": "verify", "target": "tableRows", "description": "테이블의 모든 행 거래일 확인" } + ], + "expect": { + "allRowsWithinDateRange": true + }, + "validation": { + "type": "rowByRowCheck", + "description": "각 행의 거래일 컬럼 값이 설정된 시작일~종료일 범위 내인지 확인" + } + }, + { + "id": "step-15", + "name": "페이지네이션과 날짜 필터 연동 확인", + "description": "페이지 이동 시에도 날짜 필터가 유지되는지 확인", + "precondition": "데이터가 여러 페이지에 걸쳐 있는 경우", + "actions": [ + { "type": "click", "target": "다음 페이지", "description": "다음 페이지 이동" } + ], + "expect": { + "dateRangePreserved": true, + "dataWithinRange": true + }, + "validation": { + "type": "dateRangeMatch", + "description": "다음 페이지의 데이터도 설정된 기간 내인지 확인" + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/accounting/bank-transactions", + "message": "은행거래 페이지 URL 확인" + }, + { + "type": "element", + "target": "dateFilter", + "expected": "존재", + "message": "날짜 필터 요소가 존재해야 함" + }, + { + "type": "element", + "target": "periodButtons", + "expected": ["당해년도", "전전월", "전월", "당월", "어제", "오늘"], + "message": "기간 버튼 6개가 존재해야 함" + } + ], + + "validationRules": { + "dateRangeMatch": { + "description": "테이블의 모든 거래일이 설정된 시작일~종료일 범위 내인지 확인", + "method": "각 행의 거래일 컬럼 값 추출 후 날짜 범위 비교", + "passCondition": "모든 행의 거래일이 범위 내 / 데이터 없음 (빈 상태)" + }, + "buttonActiveState": { + "description": "클릭된 기간 버튼이 활성화(active) 상태인지 확인", + "method": "버튼의 active 속성 또는 스타일 확인" + }, + "dateInputSync": { + "description": "기간 버튼 클릭 시 날짜 입력 필드가 동기화되는지 확인", + "method": "버튼 클릭 후 시작일/종료일 입력 필드 값 확인" + } + }, + + "testData": { + "expectedPeriodButtons": [ + { "name": "당해년도", "range": "해당 년도 1월 1일 ~ 12월 31일" }, + { "name": "전전월", "range": "현재 월 기준 2개월 전 1일 ~ 말일" }, + { "name": "전월", "range": "현재 월 기준 1개월 전 1일 ~ 말일" }, + { "name": "당월", "range": "현재 월 1일 ~ 말일" }, + { "name": "어제", "range": "어제 날짜 (단일)" }, + { "name": "오늘", "range": "오늘 날짜 (단일)" } + ], + "testDateRanges": [ + { "start": "2025-10-01", "end": "2025-10-31", "description": "특정 월 조회" }, + { "start": "2025-12-25", "end": "2025-12-25", "description": "단일 날짜 조회" }, + { "start": "2020-01-01", "end": "2020-01-31", "description": "데이터 없는 기간" }, + { "start": "2025-01-01", "end": "2025-12-31", "description": "전체 연도 조회" } + ] + }, + + "notes": { + "testPriority": "High", + "mainObjective": "기간별 필터링이 정확히 동작하는지 검증", + "criticalValidation": [ + "기간 버튼 클릭 시 날짜 입력 필드 자동 변경", + "설정된 기간 내 데이터만 조회되는지 확인", + "날짜 범위 외 데이터가 표시되지 않는지 확인" + ] + } +} diff --git a/bill-management.json b/bill-management.json new file mode 100644 index 0000000..7796897 --- /dev/null +++ b/bill-management.json @@ -0,0 +1,294 @@ +{ + "id": "bill-management", + "name": "어음관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "회계관리 > 어음관리 페이지의 어음 등록/조회/수정 기능을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/accounting/bill", + "menuNavigation": { + "level1": "회계관리", + "level2": "어음관리", + "expectedUrl": "/accounting/bill" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebar": { + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 5, + "waitAfterScroll": 300 + }, + "level1": { + "text": "회계관리", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "어음관리", + "waitAfterClick": 300 + }, + "fallbackUrl": "/accounting/bill", + "expectedUrl": "/accounting/bill" + }, + "timeout": 90000, + "tags": ["accounting", "bill", "crud"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "testData": { + "bill": { + "type": "수취", + "vendor": "코브라브릿지", + "amount": "10000000", + "issueDate": "2026-01-28", + "dueDate": "2026-04-28", + "note": "E2E 테스트 어음입니다" + } + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 500 } + ] + }, + { + "id": "step-1", + "name": "회계관리 메뉴 진입", + "description": "회계관리 > 어음관리 메뉴로 이동", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "회계관리", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "회계관리" }, + { "type": "wait", "duration": 500 }, + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "어음관리", + "scrollStep": 200, + "maxAttempts": 3 + }, + { "type": "click", "target": "어음관리" } + ], + "expect": { + "url": "/accounting/bill", + "visible": ["어음관리", "어음 등록"] + }, + "fallback": { + "type": "navigate", + "url": "/accounting/bill" + } + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "테이블 구조 및 탭 확인", + "verify": { + "visible": ["수취", "발행"], + "tableColumns": ["번호", "어음번호", "구분", "거래처", "금액", "발행일", "만기일", "차수", "상태"] + } + }, + { + "id": "step-3", + "name": "필수 검증 #3: 날짜 필터 테스트", + "description": "날짜 필터 버튼 동작 확인", + "actions": [ + { "type": "click", "target": "당월" }, + { "type": "wait", "duration": 500 } + ], + "expect": { + "filterApplied": true, + "dataFiltered": true + } + }, + { + "id": "step-4", + "name": "필수 검증 #3: 구분 탭 필터 - 수취", + "description": "수취 탭 클릭하여 필터링 확인", + "actions": [ + { "type": "click", "target": "수취", "role": "tab" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "tabActive": "수취", + "dataFiltered": true + } + }, + { + "id": "step-5", + "name": "필수 검증 #3: 구분 탭 필터 - 발행", + "description": "발행 탭 클릭하여 필터링 확인", + "actions": [ + { "type": "click", "target": "발행", "role": "tab" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "tabActive": "발행", + "dataFiltered": true + } + }, + { + "id": "step-6", + "name": "수취 탭으로 복귀", + "description": "수취 탭 클릭하여 수취 어음 표시", + "actions": [ + { "type": "click", "target": "수취", "role": "tab" }, + { "type": "wait", "duration": 300 } + ] + }, + { + "id": "step-7", + "name": "필수 검증 #2: 어음 등록 모달/페이지 열기", + "description": "어음 등록 버튼 클릭하여 등록 화면 열기", + "actions": [ + { "type": "click", "target": "어음 등록" } + ], + "expect": { + "pageOrModal": "어음 등록", + "visible": ["구분", "거래처", "금액", "발행일", "만기일"] + } + }, + { + "id": "step-8", + "name": "어음 등록 폼 입력", + "description": "어음 정보 입력", + "actions": [ + { "type": "click", "target": "거래처", "role": "combobox" }, + { "type": "click", "target": "{testData.bill.vendor}", "role": "option" }, + { "type": "fill", "target": "금액", "value": "{testData.bill.amount}" }, + { "type": "fill", "target": "발행일", "value": "{testData.bill.issueDate}" }, + { "type": "fill", "target": "만기일", "value": "{testData.bill.dueDate}" } + ] + }, + { + "id": "step-9", + "name": "필수 검증 #2: 어음 등록 저장", + "description": "등록/저장 버튼 클릭하여 어음 저장", + "actions": [ + { "type": "click", "target": "저장" } + ], + "expect": { + "urlMaintained": true, + "noErrorPage": true, + "toast": ["등록", "저장", "완료", "성공"] + }, + "verify": { + "apiCall": "POST /api/accounting/bill" + } + }, + { + "id": "step-10", + "name": "필수 검증 #4: 등록 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 등록 확인 필수!", + "description": "테이블에서 등록된 어음 확인", + "verify": { + "tableContains": ["{testData.bill.amount}"], + "recordCountIncreased": true + } + }, + { + "id": "step-11", + "name": "어음 상세 열기", + "description": "어음 항목 클릭하여 상세 보기", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('tbody tr')?.click()" + } + ], + "expect": { + "pageOrModal": "어음 상세", + "visible": ["어음번호", "구분", "거래처", "금액", "상태"] + } + }, + { + "id": "step-12", + "name": "어음 상세 정보 확인", + "description": "어음 상세 정보 표시 확인", + "verify": { + "visible": ["어음번호", "구분", "거래처", "금액", "발행일", "만기일", "상태"] + } + }, + { + "id": "step-13", + "name": "상세 닫기", + "description": "ESC 키로 상세 닫기", + "actions": [ + { "type": "press", "key": "Escape" }, + { "type": "wait", "duration": 300 } + ] + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/accounting/bill", + "message": "어음관리 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "button:has-text('어음 등록')", + "message": "어음 등록 버튼이 표시되어야 함" + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 2, + "name": "등록/저장 버튼", + "trigger": "어음 등록 버튼", + "verification": "URL 유지 + 에러 페이지 없음 + 성공 토스트 + 데이터 반영", + "failCondition": "404/500 에러 페이지 이동" + }, + { + "id": 3, + "name": "검색/필터", + "trigger": "날짜 필터, 구분 탭", + "verification": "데이터 변화 확인", + "failCondition": "필터 적용 후 데이터 무변화" + }, + { + "id": 4, + "name": "데이터 반영 확인", + "trigger": "어음 등록 완료 후", + "verification": "실제 데이터 등록 확인", + "failCondition": "토스트만 확인하고 데이터 미확인" + } + ] + }, + + "notes": { + "testScope": "어음 등록 → 조회 → 필터링 테스트", + "billTypes": ["수취", "발행"], + "dateFilters": ["당해년도", "전전월", "전월", "당월", "어제", "오늘"], + "statusTypes": ["보관중", "만기입금(7일전)", "만기결과", "결제완료"], + "tableColumns": ["번호", "어음번호", "구분", "거래처", "금액", "발행일", "만기일", "차수", "상태"], + "prerequisites": "로그인된 사용자에게 어음 관리 권한 필요" + } +} diff --git a/board-management.json b/board-management.json new file mode 100644 index 0000000..0e4cec5 --- /dev/null +++ b/board-management.json @@ -0,0 +1,801 @@ +{ + "scenarioId": "board-management", + "scenarioName": "게시판 관리 (Board Management)", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "게시판 관리 페이지의 전체 기능을 검증하는 E2E 테스트", + "url": "https://dev.codebridge-x.com/ko/board/board-management", + "menuNavigation": { + "level1": "게시판", + "level2": "게시판 관리", + "expectedUrl": "/ko/board/board-management" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "level1": { + "text": "게시판", + "expandable": true + }, + "level2": { + "text": "게시판 관리", + "clickable": true + }, + "scrollConfig": { + "container": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 5, + "scrollDelay": 300 + }, + "fallbackUrl": "/ko/board/board-management", + "expectedUrl": "/ko/board/board-management" + }, + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/boards/tenant", + "description": "테넌트 게시판 목록 조회 (시스템 게시판 제외)" + }, + { + "method": "POST", + "endpoint": "/api/v1/boards", + "description": "게시판 생성" + }, + { + "method": "PUT", + "endpoint": "/api/v1/boards/{id}", + "description": "게시판 수정" + }, + { + "method": "DELETE", + "endpoint": "/api/v1/boards/{id}", + "description": "게시판 삭제 (단건)" + }, + { + "method": "DELETE", + "endpoint": "/api/v1/boards/{id}", + "description": "게시판 삭제 (일괄 - 여러 번 호출)" + } + ], + "steps": [ + { + "step": 0, + "name": "사이드바 초기화", + "description": "메뉴 탐색 전 사이드바를 최상단으로 스크롤하여 초기화", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { + "type": "wait", + "duration": 500 + } + ], + "expected": { + "sidebarScrollTop": 0 + }, + "verification": [ + "사이드바가 최상단으로 스크롤됨" + ] + }, + { + "step": 1, + "name": "2단계 메뉴 진입: 게시판 > 게시판관리", + "description": "게시판 > 게시판관리 메뉴로 이동하여 페이지 로드 확인", + "navigationPattern": "scrollAndFind", + "actions": [ + { + "type": "scrollAndFind", + "target": "게시판", + "container": ".sidebar-scroll", + "scrollStep": 200, + "maxAttempts": 5 + }, + { + "type": "click", + "target": "게시판" + }, + { + "type": "wait", + "duration": 500 + }, + { + "type": "scrollAndFind", + "target": "게시판 관리", + "container": ".sidebar-scroll", + "scrollStep": 200, + "maxAttempts": 3 + }, + { + "type": "click", + "target": "게시판 관리" + }, + { + "type": "wait", + "target": "페이지 로드 완료" + } + ], + "fallback": { + "type": "directNavigation", + "url": "/ko/board/board-management" + }, + "expected": { + "url": "/ko/board/board-management", + "pageTitle": "게시판관리" + }, + "verification": [ + "페이지 제목 '게시판관리' 표시", + "설명 텍스트 '게시판 목록을 관리합니다' 표시", + "헤더에 '게시판 등록' 버튼 존재", + "검색 입력 필드 존재 (placeholder: '게시판명, 작성자, 대상 검색...')", + "탭 영역 존재 (전체/사용/미사용)", + "테이블 컬럼 헤더 존재: No., 대상, 게시판명, 상태, 작성자, 등록일시, 작업" + ], + "mandatoryCheck": "목업 감지 (#5)", + "screenshot": "step1_initial-load.png" + }, + { + "step": 2, + "name": "초기 데이터 로드 확인", + "action": "verify_data", + "verification": [ + "테이블에 게시판 목록 표시", + "각 행에 체크박스, 번호, 대상, 게시판명, 상태 뱃지, 작성자, 등록일시 표시", + "대상: '전사', '부서', '권한' 중 하나", + "상태 뱃지: '사용함'(초록색) 또는 '사용안함'(회색)" + ], + "note": "초기 데이터 개수 기록 (통계 카드 확인용)" + }, + { + "step": 3, + "name": "통계 카드 검증", + "action": "verify_stats", + "verification": [ + "전체 탭에 총 게시판 수 표시", + "사용 탭에 활성 게시판 수 표시", + "미사용 탭에 비활성 게시판 수 표시", + "전체 = 사용 + 미사용 합계" + ], + "note": "통계 수치 기록: 전체 N건, 사용 M건, 미사용 K건" + }, + { + "step": 4, + "name": "사용 탭 전환", + "action": "click_tab", + "target": "사용 탭", + "verification": [ + "탭 활성화 상태 변경", + "테이블에 '사용함' 상태 게시판만 표시", + "표시된 게시판 수 = 사용 탭 카운트" + ] + }, + { + "step": 5, + "name": "미사용 탭 전환", + "action": "click_tab", + "target": "미사용 탭", + "verification": [ + "탭 활성화 상태 변경", + "테이블에 '사용안함' 상태 게시판만 표시", + "표시된 게시판 수 = 미사용 탭 카운트" + ] + }, + { + "step": 6, + "name": "전체 탭으로 복귀", + "action": "click_tab", + "target": "전체 탭", + "verification": [ + "탭 활성화 상태 변경", + "모든 게시판 표시 (사용함 + 사용안함)", + "표시된 게시판 수 = 전체 탭 카운트" + ] + }, + { + "step": 7, + "name": "⚠️ 필수 검증: 검색 기능 테스트 - 게시판명", + "critical": true, + "actions": [ + { + "type": "capture", + "variable": "beforeSearchCount", + "selector": "table tbody tr", + "extract": "count", + "description": "검색 전 행 수 저장" + }, + { + "type": "fill", + "target": "검색 입력 필드", + "value": "{testData.searchKeyword}", + "description": "검색어 '공지' 입력" + }, + { + "type": "wait", + "duration": 1000, + "description": "검색 결과 로딩 대기" + }, + { + "type": "capture", + "variable": "afterSearchCount", + "selector": "table tbody tr", + "extract": "count", + "description": "검색 후 행 수 저장" + } + ], + "verify": { + "searchApplied": true, + "tableContains": "{testData.searchKeyword}", + "dataChanged": "beforeSearchCount may differ from afterSearchCount" + }, + "mandatoryCheck": "검색/필터 (#3)" + }, + { + "step": "7-1", + "name": "검색 결과 데이터 검증", + "critical": true, + "description": "검색 결과의 모든 행이 검색어를 포함하는지 확인", + "verify": { + "allRowsContain": "{testData.searchKeyword}", + "columnToCheck": "게시판명" + } + }, + { + "step": 8, + "name": "검색 초기화", + "actions": [ + { + "type": "clear", + "target": "검색 입력 필드" + }, + { + "type": "wait", + "duration": 500 + }, + { + "type": "capture", + "variable": "afterClearCount", + "selector": "table tbody tr", + "extract": "count" + } + ], + "verify": { + "dataRestored": "afterClearCount should equal beforeSearchCount", + "searchFieldEmpty": true + } + }, + { + "step": 9, + "name": "검색 기능 테스트 - 작성자", + "action": "search", + "target": "검색 입력 필드", + "input": "홍킬동", + "verification": [ + "검색어 '홍킬동' 입력됨", + "작성자가 '홍킬동'인 게시판만 표시" + ], + "mandatoryCheck": "검색/필터 (#3)" + }, + { + "step": 10, + "name": "검색 초기화 (2차)", + "action": "clear_search", + "verification": [ + "검색 필드 비어있음", + "전체 게시판 다시 표시" + ] + }, + { + "step": 11, + "name": "단일 게시판 체크박스 선택", + "action": "check_single", + "target": "첫 번째 게시판 체크박스", + "verification": [ + "체크박스 선택됨", + "작업 컬럼에 수정, 삭제 버튼 표시", + "선택된 항목 없음 표시 사라짐" + ] + }, + { + "step": 12, + "name": "단일 체크박스 해제", + "action": "uncheck_single", + "verification": [ + "체크박스 해제됨", + "작업 컬럼의 수정, 삭제 버튼 숨겨짐" + ] + }, + { + "step": 13, + "name": "다중 게시판 선택 (3개)", + "action": "check_multiple", + "target": "첫 3개 게시판 체크박스", + "verification": [ + "3개 체크박스 모두 선택됨", + "각 행의 작업 컬럼에 수정, 삭제 버튼 표시", + "상단에 일괄 삭제 버튼 또는 선택 카운트 표시" + ] + }, + { + "step": 14, + "name": "전체 선택 버튼 테스트", + "action": "check_all", + "target": "헤더 체크박스", + "verification": [ + "현재 페이지의 모든 체크박스 선택됨", + "모든 행에 수정, 삭제 버튼 표시" + ] + }, + { + "step": 15, + "name": "전체 선택 해제", + "action": "uncheck_all", + "target": "헤더 체크박스", + "verification": [ + "모든 체크박스 해제됨", + "모든 작업 버튼 숨겨짐" + ] + }, + { + "step": 16, + "name": "게시판 상세 모달 열기 (행 클릭)", + "action": "click_row", + "target": "첫 번째 게시판 행 (체크박스 제외 영역)", + "verification": [ + "상세 페이지로 이동 또는 모달 표시", + "URL 변경 확인: /ko/board/board-management/{id}" + ], + "note": "상세 페이지 이동 확인" + }, + { + "step": 17, + "name": "목록으로 돌아가기", + "action": "navigate_back", + "verification": [ + "게시판 관리 목록 페이지로 복귀", + "URL: /ko/board/board-management" + ] + }, + { + "step": 18, + "name": "게시판 등록 페이지 이동", + "action": "click_button", + "target": "게시판 등록 버튼", + "verification": [ + "게시판 등록 페이지로 이동", + "URL: /ko/board/board-management?mode=new", + "페이지 제목 '게시판 등록' 또는 유사 표시" + ] + }, + { + "step": 19, + "name": "게시판 등록 폼 검증", + "action": "verify_form", + "verification": [ + "대상 선택 필드 존재 (전사/부서/권한)", + "게시판명 입력 필드 존재", + "상태 선택 필드 존재 (사용함/사용안함)", + "등록/저장 버튼 존재", + "취소 버튼 존재" + ], + "mandatoryCheck": "목업 감지 (#5) - 입력 필드 및 동작 버튼 확인" + }, + { + "step": 20, + "name": "게시판 등록 - 필수 데이터 입력", + "action": "fill_form", + "inputs": [ + { + "field": "대상", + "value": "전사", + "type": "combobox" + }, + { + "field": "게시판명", + "value": "E2E 테스트 게시판", + "type": "textbox" + }, + { + "field": "상태", + "value": "사용함", + "type": "combobox" + } + ], + "verification": [ + "모든 필드 값 입력됨" + ] + }, + { + "step": 21, + "name": "게시판 등록 실행 (URL 안정성 검증)", + "action": "submit_form", + "target": "등록/저장 버튼", + "verification": [ + "**URL 유지 또는 목록으로 이동**: /ko/board/board-management", + "**404 에러 없음**: '페이지를 찾을 수 없습니다' 텍스트 없음", + "**성공 토스트**: '등록 완료' 또는 '생성되었습니다' 메시지 표시", + "목록 페이지에서 신규 게시판 표시 확인" + ], + "mandatoryCheck": "등록/저장 버튼 (#2) - URL 안정성 검증", + "criticalCheck": true, + "note": "등록 전 URL 저장 → 등록 후 URL 비교 → 에러 텍스트 스캔 → API 호출 확인" + }, + { + "step": 22, + "name": "신규 게시판 목록 확인", + "action": "verify_list", + "verification": [ + "목록에 'E2E 테스트 게시판' 표시", + "대상: '전사'", + "상태: '사용함' (초록색 뱃지)", + "통계 업데이트: 전체 +1, 사용 +1" + ] + }, + { + "step": 23, + "name": "신규 게시판 선택", + "action": "check_single", + "target": "'E2E 테스트 게시판' 체크박스", + "verification": [ + "체크박스 선택됨", + "수정, 삭제 버튼 표시" + ] + }, + { + "step": 24, + "name": "수정 버튼 클릭", + "action": "click_button", + "target": "수정 버튼", + "verification": [ + "수정 페이지로 이동", + "URL: /ko/board/board-management/{id}?mode=edit", + "폼에 기존 데이터 로드됨" + ] + }, + { + "step": 25, + "name": "게시판 정보 수정", + "action": "fill_form", + "inputs": [ + { + "field": "게시판명", + "value": "E2E 테스트 게시판 (수정됨)", + "type": "textbox" + }, + { + "field": "상태", + "value": "사용안함", + "type": "combobox" + } + ], + "verification": [ + "게시판명 변경됨", + "상태 '사용안함'으로 변경됨" + ] + }, + { + "step": 26, + "name": "수정 저장 (URL 안정성 검증)", + "action": "submit_form", + "target": "저장 버튼", + "verification": [ + "**URL 유지 또는 목록으로 이동**: /ko/board/board-management", + "**404 에러 없음**: '페이지를 찾을 수 없습니다' 텍스트 없음", + "**성공 토스트**: '수정 완료' 또는 '저장되었습니다' 메시지 표시" + ], + "mandatoryCheck": "등록/저장 버튼 (#2) - URL 안정성 검증", + "criticalCheck": true + }, + { + "step": 27, + "name": "수정 내용 확인", + "action": "verify_list", + "verification": [ + "게시판명 'E2E 테스트 게시판 (수정됨)' 표시", + "상태: '사용안함' (회색 뱃지)", + "통계 업데이트: 사용 -1, 미사용 +1" + ] + }, + { + "step": 28, + "name": "미사용 탭에서 확인", + "action": "click_tab", + "target": "미사용 탭", + "verification": [ + "'E2E 테스트 게시판 (수정됨)' 표시", + "미사용 탭 카운트 +1" + ] + }, + { + "step": 29, + "name": "전체 탭으로 복귀", + "action": "click_tab", + "target": "전체 탭", + "verification": [ + "모든 게시판 표시" + ] + }, + { + "step": 30, + "name": "단건 삭제 - 게시판 선택", + "action": "check_single", + "target": "'E2E 테스트 게시판 (수정됨)' 체크박스", + "verification": [ + "체크박스 선택됨", + "삭제 버튼 표시" + ] + }, + { + "step": 31, + "name": "단건 삭제 확인 다이얼로그 열기", + "action": "click_button", + "target": "삭제 버튼", + "verification": [ + "삭제 확인 다이얼로그 표시", + "제목: '게시판 삭제'", + "메시지: '\"E2E 테스트 게시판 (수정됨)\" 게시판을 삭제하시겠습니까?'", + "경고 텍스트: '삭제된 게시판 정보는 복구할 수 없습니다.'", + "취소, 삭제 버튼 존재" + ] + }, + { + "step": 32, + "name": "단건 삭제 실행 (URL 안정성 검증)", + "action": "click_button", + "target": "다이얼로그 삭제 버튼", + "verification": [ + "**URL 유지**: /ko/board/board-management", + "**404 에러 없음**", + "다이얼로그 자동 닫힘", + "목록에서 해당 게시판 사라짐", + "통계 업데이트: 전체 -1, 미사용 -1" + ], + "mandatoryCheck": "등록/저장 버튼 (#2) - URL 안정성 검증", + "criticalCheck": true + }, + { + "step": 33, + "name": "삭제 확인", + "action": "verify_list", + "verification": [ + "'E2E 테스트 게시판 (수정됨)' 목록에 없음", + "통계 수치 정확함" + ] + }, + { + "step": 34, + "name": "일괄 삭제 테스트 - 새 게시판 3개 등록 준비", + "action": "navigate", + "target": "/ko/board/board-management?mode=new", + "note": "일괄 삭제를 위한 테스트 데이터 생성" + }, + { + "step": 35, + "name": "테스트 게시판 1 등록", + "action": "fill_and_submit", + "inputs": [ + { + "field": "대상", + "value": "전사" + }, + { + "field": "게시판명", + "value": "일괄삭제테스트1" + }, + { + "field": "상태", + "value": "사용함" + } + ], + "verification": [ + "등록 성공", + "목록으로 복귀" + ], + "mandatoryCheck": "등록/저장 버튼 (#2)" + }, + { + "step": 36, + "name": "테스트 게시판 2 등록", + "action": "fill_and_submit", + "inputs": [ + { + "field": "대상", + "value": "전사" + }, + { + "field": "게시판명", + "value": "일괄삭제테스트2" + }, + { + "field": "상태", + "value": "사용함" + } + ], + "verification": [ + "등록 성공", + "목록으로 복귀" + ], + "mandatoryCheck": "등록/저장 버튼 (#2)" + }, + { + "step": 37, + "name": "테스트 게시판 3 등록", + "action": "fill_and_submit", + "inputs": [ + { + "field": "대상", + "value": "전사" + }, + { + "field": "게시판명", + "value": "일괄삭제테스트3" + }, + { + "field": "상태", + "value": "사용함" + } + ], + "verification": [ + "등록 성공", + "목록으로 복귀", + "통계: 전체 +3, 사용 +3" + ], + "mandatoryCheck": "등록/저장 버튼 (#2)" + }, + { + "step": 38, + "name": "일괄 삭제 - 3개 게시판 선택", + "action": "check_multiple", + "target": "일괄삭제테스트1, 일괄삭제테스트2, 일괄삭제테스트3 체크박스", + "verification": [ + "3개 체크박스 모두 선택됨", + "선택 카운트 '3개 항목 선택됨' 또는 일괄 삭제 버튼 활성화" + ] + }, + { + "step": 39, + "name": "일괄 삭제 버튼 클릭", + "action": "click_button", + "target": "일괄 삭제 버튼", + "verification": [ + "일괄 삭제 확인 다이얼로그 표시", + "메시지: '정말 3건을 삭제하시겠습니까?' 또는 유사 문구" + ], + "note": "IntegratedListTemplateV2의 onBulkDelete 사용" + }, + { + "step": 40, + "name": "일괄 삭제 실행 (URL 안정성 검증)", + "action": "click_button", + "target": "확인 버튼", + "verification": [ + "**URL 유지**: /ko/board/board-management", + "**404 에러 없음**", + "다이얼로그 닫힘", + "3개 게시판 모두 목록에서 사라짐", + "통계 업데이트: 전체 -3, 사용 -3" + ], + "mandatoryCheck": "등록/저장 버튼 (#2) - URL 안정성 검증", + "criticalCheck": true, + "note": "DELETE /api/v1/boards/{id} API 3번 호출 확인" + }, + { + "step": 41, + "name": "일괄 삭제 확인", + "action": "verify_list", + "verification": [ + "일괄삭제테스트1, 2, 3 모두 목록에 없음", + "통계 수치 원래대로 복귀" + ] + }, + { + "step": 42, + "name": "페이지네이션 테스트 (조건부)", + "action": "verify_pagination", + "condition": "총 게시판 수 > 20", + "verification": [ + "페이지네이션 컨트롤 표시", + "현재 페이지: 1", + "총 페이지 수 표시", + "다음 페이지 버튼 활성화" + ], + "note": "20개 미만이면 SKIP" + }, + { + "step": 43, + "name": "페이지네이션 - 2페이지 이동 (조건부)", + "action": "click_button", + "target": "다음 페이지 또는 페이지 2", + "condition": "총 게시판 수 > 20", + "verification": [ + "페이지 2로 이동", + "URL 쿼리 파라미터 또는 상태 업데이트", + "21~40번 게시판 표시" + ], + "note": "20개 미만이면 SKIP" + }, + { + "step": 44, + "name": "페이지네이션 - 1페이지로 복귀 (조건부)", + "action": "click_button", + "target": "이전 페이지 또는 페이지 1", + "condition": "총 게시판 수 > 20", + "verification": [ + "페이지 1로 복귀", + "1~20번 게시판 표시" + ], + "note": "20개 미만이면 SKIP" + }, + { + "step": 45, + "name": "콘솔 로그 확인", + "action": "verify_console", + "verification": [ + "JavaScript 에러 없음", + "경고 메시지 확인 및 기록" + ], + "note": "WARNING 레벨 이상 로그 추출" + } + ], + "testData": { + "searchKeyword": "공지", + "dateRange": { + "startDate": "2025-01-01", + "endDate": "2026-01-31" + }, + "newBoard": { + "target": "전사", + "boardName": "E2E 테스트 게시판", + "status": "사용함" + }, + "updateBoard": { + "boardName": "E2E 테스트 게시판 (수정됨)", + "status": "사용안함" + }, + "bulkTestBoards": [ + { + "boardName": "일괄삭제테스트1", + "target": "전사", + "status": "사용함" + }, + { + "boardName": "일괄삭제테스트2", + "target": "전사", + "status": "사용함" + }, + { + "boardName": "일괄삭제테스트3", + "target": "전사", + "status": "사용함" + } + ] + }, + "criticalChecks": [ + { + "step": 21, + "check": "게시판 등록 시 URL 안정성 및 404 에러 없음" + }, + { + "step": 26, + "check": "게시판 수정 시 URL 안정성 및 404 에러 없음" + }, + { + "step": 32, + "check": "게시판 삭제 시 URL 안정성 및 404 에러 없음" + }, + { + "step": 40, + "check": "일괄 삭제 시 URL 안정성 및 404 에러 없음" + } + ], + "notes": [ + "게시판 등록/수정/삭제 시 반드시 URL 안정성 검증 수행", + "통계 카드는 모든 작업 후 실시간 업데이트 확인", + "일괄 삭제는 deleteBoardsBulk 함수로 각 ID에 대해 DELETE API 순차 호출", + "IntegratedListTemplateV2 템플릿 사용으로 반응형 디자인 (데스크톱/모바일)", + "페이지네이션은 20개 단위로 동작 (20개 미만 시 미표시)", + "검색은 게시판명, 작성자명, 대상명 모두 포함", + "시스템 게시판(is_system=true)은 이 페이지에 표시되지 않음 (mng에서 관리)" + ] +} diff --git a/board-test.json b/board-test.json new file mode 100644 index 0000000..bf45322 --- /dev/null +++ b/board-test.json @@ -0,0 +1,741 @@ +{ + "id": "board-test", + "name": "게시판 테스트 E2E 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "게시판 테스트 메뉴의 목록, 게시글 작성, 상세, 수정, 삭제, 댓글 CRUD 전체 워크플로우 테스트", + "url": "/ko/boards/board_mjsgri54_1fmg", + "menuNavigation": { + "level1": "게시판", + "level2": "게시판 테스트", + "expectedUrl": "/ko/boards/board_mjsgri54_1fmg" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "level1": { + "text": "게시판", + "scrollToFind": true, + "maxScrollAttempts": 5 + }, + "level2": { + "text": "게시판 테스트", + "scrollToFind": true, + "maxScrollAttempts": 3 + }, + "expectedUrl": "/ko/boards/board_mjsgri54_1fmg", + "fallbackUrl": "/ko/boards/board_mjsgri54_1fmg" + }, + "steps": [ + { + "step": 0, + "name": "사이드바 준비", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll, [data-sidebar], nav')?.scrollTo({top: 0, behavior: 'instant'})" + }, + { "type": "wait", "duration": 500 } + ], + "expected": { + "sidebarReady": true + } + }, + { + "step": 1, + "name": "2단계 메뉴 진입: 게시판 > 게시판 테스트", + "description": "게시판 > 게시판 테스트 메뉴로 이동하여 페이지 로드 확인 (스크롤 탐색 포함)", + "navigationPattern": "scrollAndFind", + "actions": [ + { + "type": "scrollAndFind", + "target": "게시판", + "container": ".sidebar-scroll, [data-sidebar], nav", + "maxScrollAttempts": 5 + }, + { "type": "click", "target": "게시판" }, + { "type": "wait", "duration": 500 }, + { + "type": "scrollAndFind", + "target": "게시판 테스트", + "container": ".sidebar-scroll, [data-sidebar], nav", + "maxScrollAttempts": 3 + }, + { "type": "click", "target": "게시판 테스트" }, + { "type": "wait", "target": "페이지 로드 완료" } + ], + "expected": { + "url": "/ko/boards/board_mjsgri54_1fmg", + "title": "게시판 테스트", + "authenticated": true + }, + "verification": { + "url_contains": "/boards/board_mjsgri54_1fmg", + "elements": ["table", "button:has-text('글쓰기')"] + } + }, + { + "step": 2, + "name": "초기 게시글 목록 확인", + "action": "verify_table_structure", + "verification": { + "columns": ["No.", "제목", "작성자", "조회수", "상태", "등록일"], + "min_rows": 0 + } + }, + { + "step": 3, + "name": "게시글 총 건수 확인", + "action": "verify_text", + "verification": { + "text_pattern": "총 \\d+건" + } + }, + { + "step": 4, + "name": "검색창 존재 확인", + "action": "verify_element", + "target": "input[placeholder*='제목']", + "verification": { + "exists": true + } + }, + { + "step": 5, + "name": "상태 필터 드롭다운 확인", + "action": "verify_element", + "target": "[role='combobox']", + "verification": { + "exists": true, + "count": 2 + } + }, + { + "step": 6, + "name": "정렬 필터 드롭다운 확인", + "action": "verify_element", + "target": "[role='combobox']:has-text('최신순')", + "verification": { + "exists": true + } + }, + { + "step": 7, + "name": "날짜 범위 선택 버튼 확인", + "action": "verify_elements", + "verification": { + "buttons": ["당해년도", "전전월", "전월", "당월", "어제", "오늘"] + } + }, + { + "step": 8, + "name": "글쓰기 버튼 확인", + "action": "verify_element", + "target": "button:has-text('글쓰기')", + "verification": { + "exists": true, + "enabled": true + } + }, + { + "step": 9, + "name": "테이블 체크박스 확인", + "action": "verify_element", + "target": "table th checkbox", + "verification": { + "exists": true + } + }, + { + "step": 10, + "name": "테이블 컬럼 헤더 확인", + "action": "verify_table_headers", + "verification": { + "headers": ["No.", "제목", "작성자", "조회수", "상태", "등록일"] + } + }, + { + "step": 11, + "name": "초기 게시글 수 저장", + "action": "store_value", + "target": "총 건수", + "variable": "initial_post_count" + }, + { + "step": 12, + "name": "상태 필터 클릭 테스트", + "action": "click", + "target": "[role='combobox']:first", + "verification": { + "dropdown_opens": true + } + }, + { + "step": 13, + "name": "상태 필터 옵션 확인", + "action": "verify_dropdown_options", + "verification": { + "options": ["전체", "게시됨", "임시저장"] + } + }, + { + "step": 14, + "name": "상태 필터 닫기 (ESC)", + "action": "press_key", + "key": "Escape" + }, + { + "step": 15, + "name": "글쓰기 버튼 클릭", + "action": "click", + "target": "button:has-text('글쓰기')", + "verification": { + "url_contains": "/create" + } + }, + { + "step": 16, + "name": "글쓰기 페이지 URL 확인", + "action": "verify_url", + "expected": "/ko/boards/board_mjsgri54_1fmg/create" + }, + { + "step": 17, + "name": "작성 폼 구조 확인", + "action": "verify_form_structure", + "verification": { + "fields": ["제목", "내용"], + "checkboxes": ["비밀글로 등록"], + "buttons": ["취소", "등록"] + } + }, + { + "step": 18, + "name": "제목 입력 필드 확인", + "action": "verify_element", + "target": "#title", + "verification": { + "exists": true, + "type": "input" + } + }, + { + "step": 19, + "name": "내용 입력 필드 확인", + "action": "verify_element", + "target": "#content", + "verification": { + "exists": true, + "type": "textarea" + } + }, + { + "step": 20, + "name": "제목 입력", + "action": "fill", + "target": "#title", + "value": "E2E 테스트 게시글" + }, + { + "step": 21, + "name": "내용 입력", + "action": "fill", + "target": "#content", + "value": "E2E 자동화 테스트를 위한 게시글입니다." + }, + { + "step": 22, + "name": "등록 버튼 클릭", + "action": "click", + "target": "button:has-text('등록')", + "validation": "필수 검증 #2 (URL 안정성)" + }, + { + "step": 23, + "name": "페이지 이동 대기 (1초)", + "action": "wait", + "duration": 1000 + }, + { + "step": 24, + "name": "URL 안정성 검증 - 게시글 상세 페이지로 정상 이동", + "action": "verify_url_stability", + "validation": "필수 검증 #2", + "verification": { + "url_pattern": "/ko/boards/board_mjsgri54_1fmg/\\d+", + "no_error_page": true, + "no_404": true + } + }, + { + "step": 25, + "name": "게시글 제목 표시 확인", + "action": "verify_text", + "target": "h4", + "expected": "E2E 테스트 게시글" + }, + { + "step": 26, + "name": "게시글 내용 표시 확인", + "action": "verify_content", + "expected": "E2E 자동화 테스트를 위한 게시글입니다." + }, + { + "step": 27, + "name": "작성자 정보 확인", + "action": "verify_text", + "expected": "회원" + }, + { + "step": 28, + "name": "등록일 표시 확인", + "action": "verify_element", + "target": "text*='2026-01'" + }, + { + "step": 29, + "name": "조회수 확인", + "action": "verify_text", + "verification": { + "text_pattern": "\\d+" + } + }, + { + "step": 30, + "name": "수정 버튼 존재 확인 (작성자)", + "action": "verify_element", + "target": "button:has-text('수정')", + "verification": { + "exists": true + } + }, + { + "step": 31, + "name": "삭제 버튼 존재 확인 (작성자)", + "action": "verify_element", + "target": "button:has-text('삭제')", + "verification": { + "exists": true + } + }, + { + "step": 32, + "name": "목록으로 버튼 존재 확인", + "action": "verify_element", + "target": "button:has-text('목록으로')", + "verification": { + "exists": true + } + }, + { + "step": 33, + "name": "댓글 섹션 확인", + "action": "verify_element", + "target": "h4:has-text('댓글')", + "verification": { + "exists": true + } + }, + { + "step": 34, + "name": "초기 댓글 수 확인", + "action": "verify_text", + "target": "h4:has-text('댓글')", + "verification": { + "text_contains": "댓글 (0)" + } + }, + { + "step": 35, + "name": "첫 번째 댓글 입력", + "action": "fill", + "target": "textarea[placeholder*='댓글']", + "value": "첫 번째 테스트 댓글입니다." + }, + { + "step": 36, + "name": "댓글 등록 버튼 클릭", + "action": "click", + "target": "button:has-text('댓글 등록')" + }, + { + "step": 37, + "name": "댓글 수 업데이트 확인 (0 → 1)", + "action": "verify_text", + "target": "h4:has-text('댓글')", + "verification": { + "text_contains": "댓글 (1)" + } + }, + { + "step": 38, + "name": "두 번째 댓글 입력", + "action": "fill", + "target": "textarea[placeholder*='댓글']", + "value": "두 번째 테스트 댓글입니다." + }, + { + "step": 39, + "name": "두 번째 댓글 등록 및 댓글 수 확인 (1 → 2)", + "action": "click_and_verify", + "target": "button:has-text('댓글 등록')", + "verification": { + "text_contains": "댓글 (2)" + } + }, + { + "step": 40, + "name": "첫 번째 댓글 수정 버튼 클릭", + "action": "click", + "target": "button:has-text('수정'):first" + }, + { + "step": 41, + "name": "댓글 수정 폼 표시 확인", + "action": "verify_element", + "target": "textarea", + "verification": { + "value_contains": "첫 번째 테스트 댓글입니다." + } + }, + { + "step": 42, + "name": "댓글 내용 수정", + "action": "fill", + "target": "textarea:first", + "value": "수정된 첫 번째 댓글입니다." + }, + { + "step": 43, + "name": "댓글 저장 버튼 클릭", + "action": "click", + "target": "button:has-text('저장')" + }, + { + "step": 44, + "name": "수정된 댓글 내용 확인", + "action": "verify_text", + "expected": "수정된 첫 번째 댓글입니다." + }, + { + "step": 45, + "name": "두 번째 댓글 삭제 버튼 클릭", + "action": "click", + "target": "button:has-text('삭제'):nth(1)" + }, + { + "step": 46, + "name": "댓글 삭제 대기 (1초)", + "action": "wait", + "duration": 1000 + }, + { + "step": 47, + "name": "댓글 수 업데이트 확인 (2 → 1)", + "action": "verify_text", + "target": "h4:has-text('댓글')", + "verification": { + "text_contains": "댓글 (1)" + } + }, + { + "step": 48, + "name": "수정 버튼 클릭", + "action": "click", + "target": "button:has-text('수정'):first", + "note": "게시글 수정 버튼" + }, + { + "step": 49, + "name": "수정 페이지 URL 확인", + "action": "verify_url", + "verification": { + "url_contains": "?mode=edit" + } + }, + { + "step": 50, + "name": "수정 폼 기존 데이터 로드 확인 (제목)", + "action": "verify_input_value", + "target": "#title", + "expected": "E2E 테스트 게시글" + }, + { + "step": 51, + "name": "수정 폼 기존 데이터 로드 확인 (내용)", + "action": "verify_textarea_value", + "target": "#content", + "expected": "E2E 자동화 테스트를 위한 게시글입니다." + }, + { + "step": 52, + "name": "제목 수정", + "action": "fill", + "target": "#title", + "value": "E2E 테스트 게시글 (수정됨)" + }, + { + "step": 53, + "name": "내용 수정", + "action": "fill", + "target": "#content", + "value": "수정된 내용입니다. E2E 자동화 테스트를 위한 게시글입니다." + }, + { + "step": 54, + "name": "비밀글 체크박스 선택", + "action": "check", + "target": "#isSecret" + }, + { + "step": 55, + "name": "저장 버튼 클릭", + "action": "click", + "target": "button:has-text('저장')", + "validation": "필수 검증 #2 (URL 안정성)" + }, + { + "step": 56, + "name": "URL 안정성 검증 - 상세 페이지로 정상 이동", + "action": "verify_url_stability", + "validation": "필수 검증 #2", + "verification": { + "url_pattern": "/ko/boards/board_mjsgri54_1fmg/\\d+", + "no_error_page": true, + "no_404": true, + "no_url_change_to_unexpected": true + } + }, + { + "step": 57, + "name": "수정된 제목 표시 확인", + "action": "verify_text", + "target": "h4", + "expected": "E2E 테스트 게시글 (수정됨)" + }, + { + "step": 58, + "name": "수정된 내용 표시 확인", + "action": "verify_content", + "expected": "수정된 내용입니다. E2E 자동화 테스트를 위한 게시글입니다." + }, + { + "step": 59, + "name": "목록으로 버튼 클릭", + "action": "click", + "target": "button:has-text('목록으로')" + }, + { + "step": 60, + "name": "목록 페이지 URL 확인", + "action": "verify_url", + "expected": "/ko/boards/board_mjsgri54_1fmg" + }, + { + "step": 61, + "name": "목록에서 수정된 게시글 확인", + "action": "verify_table_row", + "verification": { + "contains": "E2E 테스트 게시글 (수정됨)" + } + }, + { + "step": 62, + "name": "게시글 클릭하여 상세 페이지 재진입", + "action": "click", + "target": "row:has-text('E2E 테스트 게시글 (수정됨)')" + }, + { + "step": 63, + "name": "삭제 버튼 클릭", + "action": "click", + "target": "button:has-text('삭제'):first" + }, + { + "step": 64, + "name": "삭제 확인 다이얼로그 표시 확인", + "action": "verify_dialog", + "verification": { + "title": "게시글 삭제", + "content_contains": "삭제하시겠습니까" + } + }, + { + "step": 65, + "name": "삭제 확인 버튼 클릭", + "action": "click", + "target": "button:has-text('삭제'):last", + "validation": "필수 검증 #2 (URL 안정성)" + }, + { + "step": 66, + "name": "페이지 이동 대기 (1초)", + "action": "wait", + "duration": 1000 + }, + { + "step": 67, + "name": "URL 안정성 검증 - 목록 페이지로 정상 이동", + "action": "verify_url_stability", + "validation": "필수 검증 #2", + "verification": { + "url": "/ko/boards/board_mjsgri54_1fmg", + "no_error_page": true, + "no_404": true + } + }, + { + "step": 68, + "name": "게시글 삭제 확인 (목록에서 제거됨)", + "action": "verify_table_not_contains", + "target": "E2E 테스트 게시글 (수정됨)" + }, + { + "step": 69, + "name": "게시글 수 감소 확인", + "action": "verify_count_decreased", + "verification": { + "previous": "initial_post_count + 1", + "current": "initial_post_count" + } + }, + { + "step": 70, + "name": "테이블 구조 유지 확인", + "action": "verify_table_structure", + "verification": { + "columns": ["No.", "제목", "작성자", "조회수", "상태", "등록일"] + } + }, + { + "step": 71, + "name": "검색창 기능 유지 확인", + "action": "verify_element", + "target": "input[placeholder*='제목']", + "verification": { + "exists": true, + "enabled": true + } + }, + { + "step": 72, + "name": "페이지네이션 확인 (조건부)", + "action": "verify_pagination", + "condition": "post_count >= 10", + "verification": { + "exists": true + } + }, + { + "step": 73, + "name": "전체 선택 체크박스 확인", + "action": "verify_element", + "target": "table th checkbox", + "verification": { + "exists": true, + "enabled": true + } + }, + { + "step": 74, + "name": "글쓰기 버튼 확인", + "action": "verify_element", + "target": "button:has-text('글쓰기')", + "verification": { + "exists": true, + "enabled": true + } + }, + { + "step": 75, + "name": "필터 드롭다운 확인", + "action": "verify_element", + "target": "[role='combobox']", + "verification": { + "count": 2 + } + }, + { + "step": 76, + "name": "콘솔 에러 확인", + "action": "check_console_errors", + "validation": "필수 검증", + "verification": { + "no_errors": true + } + }, + { + "step": 77, + "name": "테스트 완료 확인", + "action": "verify_test_completion", + "verification": { + "all_steps_passed": true + } + } + ], + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/boards/{boardCode}", + "description": "게시판 정보 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/boards/board_mjsgri54_1fmg/posts", + "description": "게시글 목록 조회" + }, + { + "method": "POST", + "endpoint": "/api/v1/boards/board_mjsgri54_1fmg/posts", + "description": "게시글 생성" + }, + { + "method": "GET", + "endpoint": "/api/v1/boards/board_mjsgri54_1fmg/posts/{id}", + "description": "게시글 상세 조회" + }, + { + "method": "PUT", + "endpoint": "/api/v1/boards/board_mjsgri54_1fmg/posts/{id}", + "description": "게시글 수정" + }, + { + "method": "DELETE", + "endpoint": "/api/v1/boards/board_mjsgri54_1fmg/posts/{id}", + "description": "게시글 삭제" + }, + { + "method": "GET", + "endpoint": "/api/v1/boards/board_mjsgri54_1fmg/posts/{id}/comments", + "description": "댓글 목록 조회" + }, + { + "method": "POST", + "endpoint": "/api/v1/boards/board_mjsgri54_1fmg/posts/{id}/comments", + "description": "댓글 생성" + }, + { + "method": "PUT", + "endpoint": "/api/v1/boards/board_mjsgri54_1fmg/posts/{id}/comments/{commentId}", + "description": "댓글 수정" + }, + { + "method": "DELETE", + "endpoint": "/api/v1/boards/board_mjsgri54_1fmg/posts/{id}/comments/{commentId}", + "description": "댓글 삭제" + } + ], + "notes": [ + "게시판 코드: board_mjsgri54_1fmg", + "자유게시판과 동일한 DynamicBoard 시스템 사용", + "게시판 등록/수정/삭제 시 반드시 URL 안정성 검증 수행", + "댓글 CRUD는 게시글 상세 페이지에서 수행", + "IntegratedListTemplateV2 템플릿 사용으로 반응형 디자인", + "검색은 게시판명, 작성자명 모두 포함", + "상태 필터: 전체, 게시됨, 임시저장", + "정렬 옵션: 최신순, 오래된순", + "페이지당 10개 게시글 표시 (ITEMS_PER_PAGE = 10)" + ] +} diff --git a/card-add.json b/card-add.json new file mode 100644 index 0000000..d5019ff --- /dev/null +++ b/card-add.json @@ -0,0 +1,431 @@ +{ + "id": "card-add", + "name": "카드 등록 테스트 (랜덤 데이터)", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "랜덤 카드 정보를 생성하여 법인카드를 등록하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/ko/hr/card-management", + "menuNavigation": { + "level1": "인사관리", + "level2": "카드관리", + "expectedUrl": "/ko/hr/card-management" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 10, + "waitAfterScroll": 300, + "level1": { + "text": "인사관리", + "selector": "a:has-text('인사관리'), button:has-text('인사관리')", + "expandable": true + }, + "level2": { + "text": "카드관리", + "selector": "a:has-text('카드관리')", + "waitAfterClick": 500 + }, + "fallbackUrl": "/ko/hr/card-management" + }, + "timeout": 90000, + "tags": ["hr", "card", "crud", "random"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "randomData": { + "cardCompany": { + "type": "random", + "options": ["신한카드", "KB국민카드", "삼성카드", "현대카드", "롯데카드", "BC카드", "우리카드", "하나카드", "NH농협카드", "IBK기업은행"] + }, + "cardNumber": { + "type": "composite", + "pattern": "{part1}-{part2}-{part3}-{part4}", + "components": { + "part1": { "type": "randomDigits", "length": 4 }, + "part2": { "type": "randomDigits", "length": 4 }, + "part3": { "type": "randomDigits", "length": 4 }, + "part4": { "type": "randomDigits", "length": 4 } + } + }, + "expiryDate": { + "type": "composite", + "pattern": "{month}{year}", + "components": { + "month": { "type": "random", "options": ["01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12"] }, + "year": { "type": "random", "options": ["27", "28", "29", "30"] } + } + }, + "cardPassword": { + "type": "randomDigits", + "length": 2 + }, + "cardName": { + "type": "composite", + "pattern": "{prefix} 법인카드_{timestamp}", + "components": { + "prefix": { + "type": "random", + "options": ["영업용", "관리용", "개발팀", "마케팅", "경영지원", "연구소", "생산팀", "품질팀"] + } + } + }, + "cardStatus": { + "type": "fixed", + "value": "사용" + } + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 준비", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 500 } + ] + }, + { + "id": "step-1", + "name": "인사관리 메뉴 진입", + "description": "인사관리 > 카드관리 메뉴로 이동 (scrollAndFind 패턴)", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "인사관리", + "scrollStep": 200, + "maxAttempts": 10, + "then": { "type": "click" } + }, + { "type": "wait", "duration": 300 }, + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "카드관리", + "scrollStep": 200, + "maxAttempts": 10, + "then": { "type": "click" } + } + ], + "expect": { + "url": "/hr/card-management", + "visible": ["카드관리", "카드 등록"] + }, + "fallback": { + "type": "navigate", + "url": "/ko/hr/card-management" + } + }, + { + "id": "step-2", + "name": "현재 카드 개수 저장", + "description": "테스트 전 카드 개수 기록", + "capture": { + "variable": "initialCount", + "selector": "전체 *", + "extract": "number" + } + }, + { + "id": "step-3", + "name": "카드 등록 페이지 이동", + "description": "카드 등록 버튼 클릭하여 등록 페이지로 이동", + "actions": [ + { "type": "click", "target": "카드 등록" } + ], + "expect": { + "url": "/hr/card-management?mode=new", + "visible": ["카드 등록", "기본 정보", "사용자 정보"] + } + }, + { + "id": "step-4", + "name": "카드사 선택", + "description": "랜덤으로 선택된 카드사 선택", + "actions": [ + { "type": "click", "target": "카드사", "role": "combobox" }, + { "type": "click", "target": "{randomData.cardCompany}", "role": "option" } + ], + "expect": { + "selected": "{randomData.cardCompany}" + } + }, + { + "id": "step-5", + "name": "카드번호 입력", + "description": "랜덤으로 생성된 카드번호 입력", + "actions": [ + { + "type": "fill", + "target": "카드번호", + "value": "{randomData.cardNumber}", + "placeholder": "1234-1234-1234-1234" + } + ] + }, + { + "id": "step-6", + "name": "유효기간 입력", + "description": "랜덤으로 생성된 유효기간 입력 (MMYY 형식)", + "actions": [ + { + "type": "fill", + "target": "유효기간", + "value": "{randomData.expiryDate}", + "placeholder": "MMYY" + } + ] + }, + { + "id": "step-7", + "name": "카드 비밀번호 앞 2자리 입력", + "description": "랜덤으로 생성된 비밀번호 앞 2자리 입력", + "actions": [ + { + "type": "fill", + "target": "카드 비밀번호 앞 2자리", + "value": "{randomData.cardPassword}", + "placeholder": "**" + } + ] + }, + { + "id": "step-8", + "name": "카드명 입력", + "description": "랜덤으로 생성된 카드명 입력", + "actions": [ + { + "type": "fill", + "target": "카드명", + "value": "{randomData.cardName}", + "placeholder": "카드명을 입력해주세요" + } + ] + }, + { + "id": "step-9", + "name": "상태 확인", + "description": "카드 상태가 '사용'으로 기본 설정되어 있는지 확인", + "verify": { + "comboboxValue": { + "target": "상태", + "expected": "사용" + } + } + }, + { + "id": "step-10", + "name": "카드 등록", + "description": "등록 버튼 클릭하여 카드 등록 완료", + "actions": [ + { "type": "click", "target": "등록" } + ], + "waitFor": { + "type": "navigation", + "url": "/hr/card-management", + "timeout": 5000 + }, + "expect": { + "url": "/hr/card-management", + "toast": ["등록", "완료", "성공"] + } + }, + { + "id": "step-11", + "name": "카드 등록 확인", + "description": "목록에서 새로 등록된 카드 확인", + "verify": { + "tableContains": "{randomData.cardName}", + "countIncreased": "{initialCount} + 1" + } + }, + { + "id": "step-12", + "name": "등록된 카드 상세 페이지 이동", + "description": "등록된 카드를 클릭하여 상세 페이지로 이동", + "actions": [ + { + "type": "findRow", + "contains": "{randomData.cardName}", + "then": { + "type": "click", + "target": "row", + "description": "해당 행 클릭하여 상세 페이지 이동" + } + } + ], + "expect": { + "url": "/hr/card-management/{id}", + "visible": ["카드 상세", "수정", "삭제", "목록"] + } + }, + { + "id": "step-13", + "name": "카드 수정 모드 전환", + "description": "수정 버튼 클릭하여 편집 모드로 전환", + "actions": [ + { "type": "click", "target": "수정" } + ], + "expect": { + "url": "/hr/card-management/{id}?mode=edit", + "visible": ["카드 수정", "취소", "저장"] + } + }, + { + "id": "step-14", + "name": "카드 정보 수정", + "description": "카드명 변경", + "actions": [ + { + "type": "clear", + "target": "카드명" + }, + { + "type": "fill", + "target": "카드명", + "value": "{randomData.cardName}_수정됨", + "description": "카드명 수정" + } + ] + }, + { + "id": "step-15", + "name": "수정 저장", + "description": "수정된 카드 정보 저장", + "actions": [ + { "type": "click", "target": "저장" } + ], + "waitFor": { + "type": "navigation", + "url": "/hr/card-management/{id}", + "timeout": 5000 + }, + "expect": { + "toast": ["수정", "완료", "성공", "저장"], + "url": "/hr/card-management/{id}" + } + }, + { + "id": "step-15-1", + "name": "⚠️ 필수 검증: 수정 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", + "description": "상세 페이지에서 수정된 카드명 확인", + "verify": { + "fieldValue": { + "target": "카드명", + "expected": "{randomData.cardName}_수정됨" + } + } + }, + { + "id": "step-16", + "name": "카드 삭제", + "description": "삭제 버튼 클릭하여 카드 삭제", + "actions": [ + { "type": "click", "target": "삭제" } + ], + "expect": { + "confirmDialog": true, + "dialogText": ["삭제", "하시겠습니까"] + } + }, + { + "id": "step-17", + "name": "삭제 확인", + "description": "삭제 확인 다이얼로그에서 확인 클릭", + "actions": [ + { "type": "click", "target": "확인", "description": "삭제 확인" } + ], + "waitFor": { + "type": "navigation", + "url": "/hr/card-management", + "timeout": 5000 + }, + "expect": { + "toast": ["삭제", "완료", "성공"], + "url": "/hr/card-management" + } + }, + { + "id": "step-18", + "name": "⚠️ 필수 검증: 삭제 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 삭제 확인 필수!", + "description": "목록에서 삭제된 카드가 없어졌는지 확인", + "verify": { + "tableNotContains": "{randomData.cardName}_수정됨", + "countDecreased": "{initialCount}" + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/hr/card-management", + "message": "등록 후 카드관리 페이지로 이동해야 함" + }, + { + "type": "elementExists", + "selector": "text={randomData.cardName}", + "message": "등록된 카드가 목록에 표시되어야 함" + }, + { + "type": "tableRow", + "contains": ["{randomData.cardCompany}", "{randomData.cardName}"], + "message": "카드사와 카드명이 테이블에 표시되어야 함" + } + ], + + "cleanup": { + "enabled": false, + "description": "테스트 후 생성된 카드 삭제 (필요시 활성화)", + "steps": [ + { + "action": "delete", + "target": "{randomData.cardName}", + "description": "등록된 카드 삭제" + } + ] + }, + + "notes": { + "testScope": "카드 등록 → 필수 정보 입력 → 등록 완료 → 목록 확인까지 전체 플로우 테스트", + "randomGeneration": { + "cardCompany": "10개 카드사 중 랜덤 선택", + "cardNumber": "4-4-4-4 형식의 랜덤 숫자", + "expiryDate": "MMYY 형식 (27~30년)", + "cardPassword": "2자리 랜덤 숫자", + "cardName": "prefix(영업용,관리용 등) + '법인카드' + timestamp" + }, + "duplicateHandling": "timestamp 포함으로 중복 방지", + "prerequisites": "로그인된 사용자에게 카드 등록 권한 필요", + "formFields": { + "required": ["카드사", "카드번호", "유효기간", "카드 비밀번호 앞 2자리", "카드명"], + "optional": ["상태", "사용자 정보"], + "defaultValues": { + "상태": "사용" + } + }, + "cardCompanyOptions": [ + "신한카드", "KB국민카드", "삼성카드", "현대카드", "롯데카드", + "BC카드", "우리카드", "하나카드", "NH농협카드", "IBK기업은행" + ] + } +} diff --git a/card-transactions.json b/card-transactions.json new file mode 100644 index 0000000..4155518 --- /dev/null +++ b/card-transactions.json @@ -0,0 +1,413 @@ +{ + "id": "card-transactions", + "name": "카드거래 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "회계관리 > 카드거래 메뉴의 기간 설정, 계정과목명 일괄변경, 모달 상세 수정 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/ko/accounting/card-transactions", + "menuNavigation": { + "level1": "회계관리", + "level2": "카드내역조회", + "expectedUrl": "/ko/accounting/card-transactions" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebarSelector": ".sidebar-scroll, [class*='sidebar'], nav[role='navigation']", + "scrollConfig": { + "scrollToTopFirst": true, + "scrollStep": 200, + "maxScrollAttempts": 10, + "waitAfterScroll": 300 + }, + "level1": { + "text": "회계관리", + "selectors": [ + "button:has-text('회계관리')", + "[data-menu='회계관리']", + ".menu-item:has-text('회계관리')" + ], + "expandWait": 500 + }, + "level2": { + "text": "카드거래", + "selectors": [ + "a:has-text('카드거래')", + "[data-submenu='카드거래']", + ".submenu-item:has-text('카드거래')" + ] + }, + "fallbackUrl": "/ko/accounting/card-transactions" + }, + "testFocus": { + "primary": "계정과목명 일괄변경 및 모달 수정 기능 검증", + "description": "2년 기간 설정 후 체크박스 선택하여 계정과목명 일괄변경, 행 클릭 시 모달창에서 적요/사용유형 수정 저장 확인" + }, + "prerequisites": { + "authentication": true, + "testData": { + "dateRange": { + "startDate": "2024-01-15", + "endDate": "2026-01-15", + "description": "현재일자(2026-01-15)에서 2년 전까지" + } + } + }, + "steps": [ + { + "id": "step-0", + "name": "사이드바 준비", + "description": "메뉴 탐색 전 사이드바를 최상단으로 스크롤하여 초기화", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll, [class*=\"sidebar\"], nav[role=\"navigation\"]')?.scrollTo({top: 0, behavior: 'instant'})" + }, + { "type": "wait", "duration": 500 } + ], + "expected": { + "sidebarScrollTop": 0, + "sidebarReady": true + } + }, + { + "id": "step-1", + "name": "2단계 메뉴 진입: 회계관리 > 카드거래", + "description": "사이드바 스크롤 탐색으로 회계관리 > 카드거래 메뉴 진입", + "navigationPattern": "scrollAndFind", + "actions": [ + { + "type": "scrollAndFind", + "target": "회계관리", + "scrollContainer": ".sidebar-scroll, [class*='sidebar'], nav[role='navigation']", + "scrollStep": 200, + "maxAttempts": 10 + }, + { "type": "click", "target": "회계관리" }, + { "type": "wait", "duration": 500 }, + { + "type": "scrollAndFind", + "target": "카드거래", + "scrollContainer": ".sidebar-scroll, [class*='sidebar'], nav[role='navigation']", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "카드거래" }, + { "type": "wait", "target": "페이지 로드 완료" } + ], + "fallback": { + "type": "directNavigation", + "url": "/ko/accounting/card-transactions" + }, + "expected": { + "url": "/ko/accounting/card-transactions", + "pageTitle": "카드거래", + "elements": ["페이지 타이틀", "날짜 필터", "테이블"], + "authenticated": true + } + }, + { + "id": "step-2", + "name": "목록 페이지 구조 확인", + "description": "통계 카드, 필터, 테이블 컬럼 구조 확인", + "actions": [ + { + "type": "verify", + "target": "페이지 구조" + } + ], + "expected": { + "statisticsCards": ["사용금액", "사용유형 미설정"], + "filters": ["기간 선택", "카드 선택", "정렬"], + "tableColumns": ["체크박스", "카드명", "사용일시", "가맹점명", "사용금액", "적요", "사용유형"], + "periodButtons": ["당해년도", "전전월", "전월", "당월", "어제", "오늘"] + } + }, + { + "id": "step-3", + "name": "2년 기간 설정 (2024-01-15 ~ 2026-01-15)", + "description": "현재일자에서 2년 전까지 날짜 범위 설정", + "actions": [ + { + "type": "clear", + "target": "시작일 입력 필드" + }, + { + "type": "type", + "target": "시작일 입력 필드", + "value": "2024-01-15" + }, + { + "type": "clear", + "target": "종료일 입력 필드" + }, + { + "type": "type", + "target": "종료일 입력 필드", + "value": "2026-01-15" + }, + { + "type": "click", + "target": "새로고침 버튼" + } + ], + "expected": { + "startDate": "2024-01-15", + "endDate": "2026-01-15", + "dataLoaded": true, + "tableHasData": "데이터 존재 여부 확인" + } + }, + { + "id": "step-4", + "name": "테이블 데이터 존재 확인", + "description": "2년 기간 내 카드거래 데이터가 테이블에 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "테이블 데이터" + } + ], + "expected": { + "tableRows": "> 0", + "statisticsUpdated": true, + "noEmptyMessage": "검색 결과가 없습니다 메시지 없음" + } + }, + { + "id": "step-5", + "name": "계정과목명 드롭다운 옵션 확인", + "description": "계정과목명(사용유형) 드롭다운의 옵션 목록 확인", + "actions": [ + { + "type": "click", + "target": "계정과목명 드롭다운" + }, + { + "type": "verify", + "target": "드롭다운 옵션" + } + ], + "expected": { + "options": ["미설정", "접대비", "복리후생비", "차량유지비", "소모품비", "통신비", "교통비", "광고선전비", "기타"] + } + }, + { + "id": "step-6", + "name": "체크박스 선택 (일괄변경용)", + "description": "테이블에서 1개 이상의 행 체크박스 선택", + "actions": [ + { + "type": "click", + "target": "첫 번째 행 체크박스" + }, + { + "type": "verify", + "target": "선택 상태" + } + ], + "expected": { + "checkboxSelected": true, + "selectedCount": 1 + } + }, + { + "id": "step-7", + "name": "계정과목명 일괄변경 실행", + "description": "선택된 행에 대해 계정과목명을 변경하고 저장", + "actions": [ + { + "type": "click", + "target": "계정과목명 드롭다운" + }, + { + "type": "select", + "target": "옵션", + "value": "접대비" + }, + { + "type": "click", + "target": "저장 버튼" + }, + { + "type": "wait", + "target": "확인 다이얼로그" + }, + { + "type": "click", + "target": "확인 버튼" + } + ], + "expected": { + "confirmDialog": "N개의 사용 유형을 접대비(으)로 모두 변경하시겠습니까?", + "apiCall": "POST /accounting/card-transactions", + "apiResponse": "200 OK", + "dataChanged": "미설정 → 접대비" + } + }, + { + "id": "step-8", + "name": "일괄변경 결과 확인", + "description": "계정과목명 변경 후 테이블 및 통계 카드 업데이트 확인", + "actions": [ + { + "type": "verify", + "target": "변경된 사용유형" + }, + { + "type": "verify", + "target": "통계 카드 (사용유형 미설정 건수)" + } + ], + "expected": { + "usageTypeChanged": "접대비", + "unsetCountDecreased": true, + "successToast": "변경 완료" + } + }, + { + "id": "step-9", + "name": "행 클릭하여 모달창 열기", + "description": "테이블의 특정 행을 클릭하여 상세 모달창 표시", + "actions": [ + { + "type": "click", + "target": "테이블 첫 번째 행" + }, + { + "type": "wait", + "target": "모달창 표시" + } + ], + "expected": { + "modalOpened": true, + "modalTitle": "카드거래 상세", + "modalFields": ["카드명", "사용일시", "가맹점명", "사용금액", "적요", "사용유형"] + } + }, + { + "id": "step-10", + "name": "모달창 필드 상태 확인", + "description": "모달창 내 각 필드의 읽기전용/편집가능 상태 확인", + "actions": [ + { + "type": "verify", + "target": "모달 필드 상태" + } + ], + "expected": { + "readOnlyFields": ["카드명", "사용일시", "가맹점명", "사용금액"], + "editableFields": ["적요", "사용유형"], + "buttons": ["취소", "저장"] + } + }, + { + "id": "step-11", + "name": "모달창에서 적요 수정", + "description": "적요 필드 값을 변경", + "actions": [ + { + "type": "clear", + "target": "적요 입력 필드" + }, + { + "type": "type", + "target": "적요 입력 필드", + "value": "테스트 적요 수정" + } + ], + "expected": { + "fieldValue": "테스트 적요 수정" + } + }, + { + "id": "step-12", + "name": "모달창에서 사용유형 수정", + "description": "사용유형 드롭다운에서 다른 옵션 선택", + "actions": [ + { + "type": "click", + "target": "사용유형 드롭다운" + }, + { + "type": "select", + "target": "옵션", + "value": "복리후생비" + } + ], + "expected": { + "selectedOption": "복리후생비" + } + }, + { + "id": "step-13", + "name": "모달창 저장 버튼 클릭", + "description": "수정된 내용 저장", + "actions": [ + { + "type": "click", + "target": "저장 버튼" + }, + { + "type": "wait", + "target": "저장 처리 완료" + } + ], + "expected": { + "apiCall": "PUT/PATCH /accounting/card-transactions/{id}", + "apiResponse": "200 OK", + "modalClosed": true, + "successToast": "저장 완료" + } + }, + { + "id": "step-14", + "name": "수정 데이터 반영 확인", + "description": "모달에서 수정한 내용이 테이블에 반영되었는지 확인", + "actions": [ + { + "type": "verify", + "target": "테이블 해당 행" + } + ], + "expected": { + "updatedMemo": "테스트 적요 수정", + "updatedUsageType": "복리후생비" + } + }, + { + "id": "step-15", + "name": "모달창 취소 버튼 동작 확인", + "description": "다른 행 클릭 후 취소 버튼으로 모달 닫기", + "actions": [ + { + "type": "click", + "target": "다른 행" + }, + { + "type": "wait", + "target": "모달창" + }, + { + "type": "click", + "target": "취소 버튼" + } + ], + "expected": { + "modalClosed": true, + "dataUnchanged": true + } + } + ], + "cleanup": { + "description": "테스트 후 변경된 데이터 원복 (필요시)", + "actions": [] + }, + "notes": [ + "기존 입금관리, 출금관리, 매출관리에서 계정과목명 일괄변경 시 데이터 미반영 버그 발견됨 (BUG-DEPOSIT-20260115-001 등)", + "카드거래에서도 동일 패턴 버그 발생 가능성 있음", + "모달창 저장 시 URL 변경 및 에러 페이지 이동 여부 반드시 확인 필요" + ] +} diff --git a/company-info.json b/company-info.json new file mode 100644 index 0000000..ab2e0fe --- /dev/null +++ b/company-info.json @@ -0,0 +1,422 @@ +{ + "id": "company-info", + "name": "설정 - 회사정보", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "회사 정보 관리 기능 테스트 - 회사 정보 조회, 수정, 회사 추가 기능", + "baseUrl": "https://dev.codebridge-x.com", + + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", + "level1": "설정", + "level2": "회사정보", + "alternativeLevel1Names": ["설정", "Settings", "환경설정", "시스템설정", "관리"], + "alternativeLevel2Names": ["회사정보", "회사 정보", "Company Info", "회사관리", "기업정보"], + "fallbackUrls": [ + "/ko/settings/company-info", + "/ko/settings/company", + "/ko/admin/company-info", + "/settings/company-info", + "/ko/management/company" + ], + "scrollConfig": { + "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", + "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + } + }, + + "expectedAPIs": [ + { + "method": "GET", + "path": "/api/v1/company-info", + "description": "회사 정보 조회" + }, + { + "method": "PUT", + "path": "/api/v1/company-info/:id", + "description": "회사 정보 수정" + }, + { + "method": "POST", + "path": "/api/v1/company-info", + "description": "회사 추가" + } + ], + + "steps": [ + { + "id": 0, + "name": "사이드바 메뉴 탐색 준비", + "description": "사이드바를 최상단으로 스크롤하고 메뉴 구조 파악", + "actions": [ + { "type": "scroll", "target": "sidebar", "direction": "top", "description": "사이드바 최상단으로 스크롤" }, + { "type": "wait", "duration": 500 }, + { "type": "screenshot", "name": "sidebar_initial_state" } + ], + "verification": [ + "사이드바가 화면에 보이는지 확인", + "메뉴 항목들이 렌더링되었는지 확인" + ] + }, + { + "id": 1, + "name": "1차 메뉴 찾기: 설정 (스크롤 포함)", + "description": "사이드바를 스크롤하며 '설정' 메뉴를 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "설정", + "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" } + ], + "verification": [ + "설정 메뉴가 클릭되었는지 확인", + "서브메뉴가 펼쳐졌는지 확인", + "하위 메뉴 항목들이 보이는지 확인" + ], + "fallback": { + "if": "메뉴를 찾을 수 없음", + "then": "사이드바 전체를 스크롤하며 재탐색" + } + }, + { + "id": 2, + "name": "2차 메뉴 찾기: 회사정보 (스크롤 포함)", + "description": "서브메뉴에서 '회사정보'를 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "회사정보", + "alternativeTexts": ["회사정보", "회사 정보", "Company Info", "회사관리"], + "scrollContainer": "submenu", + "maxAttempts": 5, + "description": "서브메뉴에서 회사정보 찾기" + }, + { "type": "wait", "duration": 200 }, + { "type": "click", "target": "회사정보", "description": "회사정보 메뉴 클릭" }, + { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 }, + { "type": "screenshot", "name": "company_info_page" } + ], + "verification": [ + "회사정보 메뉴 클릭 성공", + "페이지 이동 또는 컨텐츠 로드" + ] + }, + { + "id": 3, + "name": "404 에러 감지 및 대체 경로 시도", + "description": "페이지 로드 후 404 에러 여부 확인, 404시 대체 경로 탐색", + "actions": [ + { "type": "wait", "duration": 1000 }, + { "type": "checkFor404", "indicators": [ + "페이지를 찾을 수 없습니다", + "404", + "Not Found", + "존재하지 않거나" + ]}, + { "type": "screenshot", "name": "page_load_result" } + ], + "verification": [ + "현재 페이지가 404인지 확인" + ], + "onError404": { + "description": "404 에러 발생 시 대체 URL 시도", + "actions": [ + { "type": "log", "message": "404 감지 - 대체 경로 탐색 시작" }, + { + "type": "tryAlternativeUrls", + "urls": [ + "/ko/settings/company-info", + "/ko/settings/company", + "/ko/admin/company-info" + ], + "stopOnSuccess": true + }, + { + "type": "ifStillFailed", + "action": "navigateViaMenuClick", + "description": "URL 직접 접근 실패 시 메뉴 클릭으로 재시도" + } + ] + } + }, + { + "id": 4, + "name": "페이지 정상 로드 확인", + "description": "회사정보 페이지가 정상적으로 로드되었는지 확인", + "actions": [ + { "type": "verify", "target": "pageTitle", "contains": ["회사정보", "회사 정보", "Company"] }, + { "type": "verify", "target": "pageContent", "notContains": ["404", "찾을 수 없습니다", "Not Found"] } + ], + "verification": [ + "페이지 제목 '회사정보' 또는 관련 텍스트 표시", + "404 에러 메시지 미표시", + "콘텐츠가 정상 렌더링됨" + ], + "successCriteria": { + "urlPattern": "/settings/company", + "requiredElements": ["회사", "회사명", "대표자명"] + } + }, + { + "step": 5, + "name": "페이지 제목 확인", + "action": "verify", + "target": "heading", + "expected": "회사정보", + "validation": "페이지 제목이 '회사정보'로 표시됨" + }, + { + "step": 6, + "name": "회사 추가 버튼 존재 확인", + "action": "verify", + "target": "button[text='회사 추가']", + "expected": "button exists", + "validation": "회사 추가 버튼이 표시됨" + }, + { + "step": 7, + "name": "수정 버튼 존재 확인", + "action": "verify", + "target": "button[text='수정']", + "expected": "button exists", + "validation": "수정 버튼이 표시됨" + }, + { + "step": 8, + "name": "회사명 필드 확인", + "action": "verify", + "target": "textbox[label='회사명'][disabled]", + "expected": "프론트_테스트회사", + "validation": "회사명이 표시되고 비활성화 상태" + }, + { + "step": 9, + "name": "대표자명 필드 확인", + "action": "verify", + "target": "textbox[label='대표자명'][disabled]", + "expected": "프론트", + "validation": "대표자명이 표시되고 비활성화 상태" + }, + { + "step": 10, + "name": "업태 필드 확인", + "action": "verify", + "target": "textbox[label='업태'][disabled]", + "expected": "업태명", + "validation": "업태가 표시되고 비활성화 상태" + }, + { + "step": 11, + "name": "업종 필드 확인", + "action": "verify", + "target": "textbox[label='업종'][disabled]", + "expected": "업종명", + "validation": "업종이 표시되고 비활성화 상태" + }, + { + "step": 12, + "name": "주소 필드 확인", + "action": "verify", + "target": "textbox[label='주소명'][disabled]", + "expected": "주소 표시", + "validation": "주소가 표시되고 비활성화 상태" + }, + { + "step": 13, + "name": "이메일 필드 확인", + "action": "verify", + "target": "textbox[label='이메일 (아이디)'][disabled]", + "expected": "이메일 표시", + "validation": "이메일이 표시되고 비활성화 상태" + }, + { + "step": 14, + "name": "사업자등록번호 필드 확인", + "action": "verify", + "target": "textbox[label='사업자등록번호'][disabled]", + "expected": "사업자등록번호 표시", + "validation": "사업자등록번호가 표시되고 비활성화 상태" + }, + { + "step": 15, + "name": "수정 버튼 클릭", + "action": "click", + "target": "button[text='수정']", + "expected": "edit mode enabled", + "validation": "수정 모드로 전환됨" + }, + { + "step": 16, + "name": "수정 모드 - 필드 활성화 확인", + "action": "verify", + "target": "textbox:not([disabled])", + "expected": "fields enabled", + "validation": "텍스트 필드들이 활성화됨" + }, + { + "step": 17, + "name": "취소 버튼 클릭", + "action": "click", + "target": "button[text='취소']", + "expected": "edit mode disabled", + "validation": "조회 모드로 복귀" + }, + { + "step": 18, + "name": "회사 추가 버튼 클릭", + "action": "click", + "target": "button[text='회사 추가']", + "expected": "dialog opened", + "validation": "회사 추가 다이얼로그가 열림" + }, + { + "step": 19, + "name": "회사 추가 다이얼로그 확인", + "action": "verify", + "target": "dialog", + "expected": "회사 추가 다이얼로그 표시", + "validation": "다이얼로그 제목, 입력 필드, 버튼 확인" + }, + { + "step": 20, + "name": "다이얼로그 닫기", + "action": "click", + "target": "dialog button[text='취소']", + "expected": "dialog closed", + "validation": "다이얼로그가 닫힘" + }, + { + "step": 21, + "name": "수정 모드에서 데이터 변경 테스트", + "description": "실제 데이터를 수정하고 저장 기능 검증", + "actions": [ + { "type": "click", "target": "수정", "description": "수정 모드 진입" } + ], + "expect": { + "fieldsEnabled": true + } + }, + { + "step": 22, + "name": "업태 필드 수정", + "description": "업태 필드 값 변경", + "actions": [ + { "type": "clear", "target": "업태" }, + { "type": "fill", "target": "업태", "value": "테스트업태_수정" } + ] + }, + { + "step": 23, + "name": "저장 버튼 클릭", + "description": "수정된 회사 정보 저장", + "actions": [ + { "type": "click", "target": "저장" } + ], + "waitFor": { + "type": "apiResponse", + "method": "PUT", + "timeout": 5000 + }, + "expect": { + "toast": ["수정", "완료", "성공", "저장"] + } + }, + { + "step": 24, + "name": "⚠️ 필수 검증: 수정 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", + "description": "수정된 업태 값이 반영되었는지 확인", + "verify": { + "fieldValue": { + "target": "업태", + "expected": "테스트업태_수정" + } + } + }, + { + "step": 25, + "name": "회사 추가 다이얼로그 열기", + "description": "회사 추가 버튼 클릭하여 다이얼로그 열기", + "actions": [ + { "type": "click", "target": "회사 추가" } + ], + "expect": { + "dialog": true, + "visible": ["회사명", "대표자명", "사업자등록번호", "등록", "취소"] + } + }, + { + "step": 26, + "name": "새 회사 정보 입력", + "description": "회사 추가 다이얼로그에서 필수 정보 입력", + "actions": [ + { "type": "fill", "target": "회사명", "value": "테스트회사_{timestamp}" }, + { "type": "fill", "target": "대표자명", "value": "테스트대표" }, + { "type": "fill", "target": "사업자등록번호", "value": "123-45-67890" } + ] + }, + { + "step": 27, + "name": "회사 등록", + "description": "등록 버튼 클릭하여 새 회사 등록", + "actions": [ + { "type": "click", "target": "등록" } + ], + "waitFor": { + "type": "apiResponse", + "method": "POST", + "timeout": 5000 + }, + "expect": { + "toast": ["등록", "완료", "성공"], + "dialogClosed": true + } + }, + { + "step": 28, + "name": "⚠️ 필수 검증: 회사 등록 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 등록 확인 필수!", + "description": "등록된 회사가 목록에 표시되는지 확인", + "verify": { + "visible": "테스트회사" + } + }, + { + "step": 29, + "name": "원복: 업태 필드 원래 값으로 복구", + "description": "테스트 후 원래 값으로 복구", + "actions": [ + { "type": "click", "target": "수정" }, + { "type": "clear", "target": "업태" }, + { "type": "fill", "target": "업태", "value": "업태명" }, + { "type": "click", "target": "저장" } + ], + "expect": { + "toast": ["수정", "완료", "성공", "저장"] + } + } + ], + + "notes": [ + "직접 URL 접근 금지: 반드시 메뉴 클릭으로 페이지 진입 (404 방지)", + "스크롤 필수: 사이드바가 길 경우 메뉴가 화면 밖에 있을 수 있음", + "대체 경로: 메뉴명이 변경되었을 수 있으므로 다양한 이름으로 탐색", + "메뉴 계층: 설정 > 회사정보" + ] +} diff --git a/comprehensive-analysis.json b/comprehensive-analysis.json new file mode 100644 index 0000000..958a97b --- /dev/null +++ b/comprehensive-analysis.json @@ -0,0 +1,263 @@ +{ + "id": "comprehensive-analysis", + "name": "종합분석 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "리포트 > 종합분석 페이지의 CEO 대시보드 조회 및 필터링 기능을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/report/comprehensive", + "menuNavigation": { + "level1": "리포트", + "level2": "종합분석", + "expectedUrl": "/report/comprehensive" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebar": { + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 5, + "waitAfterScroll": 300 + }, + "level1": { + "text": "리포트", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "종합분석", + "waitAfterClick": 300 + }, + "fallbackUrl": "/report/comprehensive", + "expectedUrl": "/report/comprehensive" + }, + "timeout": 90000, + "tags": ["report", "dashboard", "read-only"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 500 } + ] + }, + { + "id": "step-1", + "name": "리포트 메뉴 진입", + "description": "리포트 > 종합분석 메뉴로 이동", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "리포트", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "리포트" }, + { "type": "wait", "duration": 500 }, + { "type": "click", "target": "종합분석" } + ], + "expect": { + "url": "/report/comprehensive", + "visible": ["CEO 대시보드", "전사 경영 현황"] + }, + "fallback": { + "type": "navigate", + "url": "/report/comprehensive" + } + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "대시보드 위젯 및 버튼 확인", + "verify": { + "visible": ["CEO 대시보드", "일일보고서", "경영분석"], + "widgets": ["견적", "수주", "생산", "검사", "출고예정", "출고완료"] + } + }, + { + "id": "step-3", + "name": "프로세스 현황 위젯 확인", + "description": "전체 업무 프로세스 현황 위젯 확인", + "verify": { + "visible": ["모든 프로세스 현황", "견적", "수주", "생산", "검사", "출고예정", "출고완료"] + } + }, + { + "id": "step-4", + "name": "매출 지표 확인", + "description": "매출 관련 지표 위젯 확인", + "verify": { + "visible": ["당일 출하매출", "매출총이익률", "일일 순손익", "일별 매출", "월별 매출", "연간 매출"] + } + }, + { + "id": "step-5", + "name": "미수금 현황 확인", + "description": "미수금 관련 위젯 확인", + "verify": { + "visible": ["총 미수금", "연체", "거래처별 미수금"] + } + }, + { + "id": "step-6", + "name": "재고 현황 확인", + "description": "재고 관련 위젯 확인", + "verify": { + "visible": ["재고 총액", "재고 회전율", "재고 부족 품목"] + } + }, + { + "id": "step-7", + "name": "캘린더 필터 테스트", + "description": "캘린더 필터 옵션 동작 확인", + "actions": [ + { "type": "click", "target": "필터 옵션" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "visible": ["입고", "출고", "자재", "일정", "휴가", "출장"] + } + }, + { + "id": "step-8", + "name": "필수 검증 #3: 필터 선택 테스트", + "description": "캘린더 필터 선택 동작 확인", + "actions": [ + { "type": "click", "target": "입고" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "filterApplied": true + } + }, + { + "id": "step-9", + "name": "인력 현황 확인", + "description": "출근율 및 결근 현황 위젯 확인", + "verify": { + "visible": ["일일 출근율", "결근", "지각"] + } + }, + { + "id": "step-10", + "name": "품질검사 결과 확인", + "description": "품질검사 결과 위젯 확인", + "verify": { + "visible": ["품질검사 결과", "적합률", "부적합률"] + } + }, + { + "id": "step-11", + "name": "승인 대기 품의서 확인", + "description": "승인 대기 중인 품의서 위젯 확인", + "verify": { + "visible": ["승인 대기 중인 품의서", "구매품의", "투자품의", "인사품의"] + } + }, + { + "id": "step-12", + "name": "휴가/출장 현황 확인", + "description": "휴가 및 출장 현황 위젯 확인", + "verify": { + "visible": ["휴가/출장 현황", "휴가 현황", "출장 현황", "부서별 현황"] + } + }, + { + "id": "step-13", + "name": "TOP 5 고객 매출 확인", + "description": "TOP 5 고객별 매출 현황 위젯 확인", + "verify": { + "visible": ["TOP 5 고객별 매출 현황"] + } + }, + { + "id": "step-14", + "name": "일일보고서 버튼 확인", + "description": "일일보고서 버튼 존재 확인", + "verify": { + "buttonExists": "일일보고서" + } + }, + { + "id": "step-15", + "name": "경영분석 버튼 확인", + "description": "경영분석 버튼 존재 확인", + "verify": { + "buttonExists": "경영분석" + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/report/comprehensive", + "message": "종합분석 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "text=CEO 대시보드", + "message": "CEO 대시보드 제목이 표시되어야 함" + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 3, + "name": "검색/필터", + "trigger": "캘린더 필터", + "verification": "필터 적용 확인", + "failCondition": "필터 미동작" + }, + { + "id": 5, + "name": "목업/미완성 페이지 감지", + "trigger": "페이지 로드 시", + "verification": "실제 데이터 표시 + 동작하는 위젯 확인", + "failCondition": "더미 데이터만 표시, 버튼 미동작" + } + ] + }, + + "notes": { + "testScope": "CEO 대시보드 조회 및 필터링 테스트", + "pageType": "조회 전용 대시보드 (실시간 데이터 표시)", + "dashboardWidgets": [ + "모든 프로세스 현황 (견적, 수주, 생산, 검사, 출고예정, 출고완료)", + "스마트 워크플로우 캘린더", + "매출 지표 (당일, 월별, 연간)", + "미수금 현황", + "현금 흐름", + "재고 현황", + "차량별 가동률", + "출근율/결근 현황", + "품질검사 결과", + "승인 대기 품의서", + "휴가/출장 현황", + "TOP 5 고객별 매출" + ], + "filterOptions": ["입고", "출고", "자재", "일정", "휴가", "출장"], + "buttons": ["일일보고서", "경영분석"], + "prerequisites": "로그인된 사용자" + } +} diff --git a/crud-delete-freeboard.json b/crud-delete-freeboard.json new file mode 100644 index 0000000..6403b12 --- /dev/null +++ b/crud-delete-freeboard.json @@ -0,0 +1,339 @@ +{ + "id": "crud-delete-freeboard", + "name": "자유게시판 CRUD 삭제 기능 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "자유게시판에서 생성 → 수정 → 삭제 전체 CRUD 흐름 테스트. 테스트용 게시글을 생성하고, 수정한 후, 삭제하여 기존 데이터에 영향 없이 삭제 기능을 검증", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "고객센터", + "level2": "자유게시판", + "expectedUrl": "/ko/customer-center/free-board" + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "testPolicy": { + "deleteAllowed": true, + "deleteCondition": "CRUD 흐름 내에서만 허용 (생성 → 수정 → 삭제)", + "protectExistingData": true, + "description": "이 시나리오에서 생성한 테스트 데이터만 삭제" + }, + "modalHandling": { + "description": "모달 창 처리 규칙", + "closeMethods": [ + {"priority": 1, "method": "완료 버튼", "selector": "button:has-text('확인'), button:has-text('등록'), button:has-text('저장')"}, + {"priority": 2, "method": "취소 버튼", "selector": "button:has-text('취소'), [class*='cancel']"}, + {"priority": 3, "method": "X 버튼", "selector": "button[class*='close'], [aria-label='닫기'], [aria-label='Close']"}, + {"priority": 4, "method": "ESC 키", "action": "press_key('Escape')"}, + {"priority": 5, "method": "외부 클릭", "selector": "[class*='backdrop'], [class*='overlay']"} + ], + "deleteDialogSelector": "[role='alertdialog'] button:has-text('삭제')", + "note": "삭제 확인 다이얼로그는 Playwright 네이티브 셀렉터 사용 필수 (JavaScript click 미동작)", + "rule": "모달이 열린 상태로 다음 단계 진행 금지" + }, + "testData": { + "newPost": { + "title": "E2E테스트_삭제용_", + "content": "이 게시글은 E2E 테스트용으로 생성되었습니다. 테스트 완료 후 자동 삭제됩니다." + }, + "updateData": { + "title": "E2E테스트_수정완료_" + }, + "uniqueIdentifier": "timestamp를 붙여서 고유성 보장 (MMDDHHmmss)" + }, + "steps": [ + { + "id": "step-0", + "name": "사이드바 메뉴 탐색", + "description": "고객센터 > 자유게시판 메뉴로 이동", + "actions": [ + {"type": "scroll", "target": "sidebar", "direction": "bottom", "description": "사이드바 하단으로 스크롤"}, + {"type": "wait", "duration": 300}, + {"type": "click", "target": "고객센터", "description": "1차 메뉴 클릭"}, + {"type": "wait", "duration": 500}, + {"type": "click", "target": "자유게시판", "description": "2차 메뉴 클릭"}, + {"type": "wait", "duration": 2000} + ], + "expect": { + "url": "/ko/customer-center/free-board", + "no404": true + } + }, + { + "id": "step-1", + "phase": "CREATE", + "name": "[CREATE] 초기 상태 확인", + "description": "등록 전 게시글 수 확인", + "actions": [ + {"type": "capture", "variable": "initialRowCount", "selector": "table tbody tr", "extract": "count", "description": "등록 전 행 수 저장"}, + {"type": "wait", "duration": 500} + ], + "expect": { + "tableExists": true + } + }, + { + "id": "step-2", + "phase": "CREATE", + "name": "[CREATE] 등록 버튼 클릭", + "description": "새 게시글을 등록하기 위해 등록 버튼 클릭", + "actions": [ + {"type": "click", "target": "button:has-text('등록')", "description": "등록 버튼 클릭"}, + {"type": "wait", "duration": 1500} + ], + "expect": { + "url": "/ko/customer-center/free-board/new", + "pageTitle": "게시글 작성" + } + }, + { + "id": "step-3", + "phase": "CREATE", + "name": "[CREATE] 게시글 정보 입력", + "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": "본문 입력"} + ], + "note": "타임스탬프를 사용하여 매 테스트마다 고유한 데이터 생성" + }, + { + "id": "step-4", + "phase": "CREATE", + "name": "[CREATE] 등록 실행", + "description": "입력된 정보로 게시글 등록 실행", + "actions": [ + {"type": "click", "target": "button:has-text('등록')", "description": "등록 버튼 클릭"}, + {"type": "wait", "duration": 2000} + ], + "expect": { + "toast": "등록|완료|성공", + "redirect": "/ko/customer-center/free-board" + }, + "verification": { + "level": 4, + "checks": ["성공 토스트 표시", "목록 페이지로 이동"] + } + }, + { + "id": "step-5", + "phase": "CREATE", + "name": "[CREATE] 등록 결과 확인", + "description": "테이블에 새로 등록한 게시글이 표시되는지 확인", + "actions": [ + {"type": "wait", "duration": 1000}, + {"type": "capture", "variable": "afterCreateCount", "selector": "table tbody tr", "extract": "count"}, + {"type": "verify", "condition": "afterCreateCount > initialRowCount", "description": "행 수 증가 확인"} + ], + "expect": { + "rowCountIncreased": true, + "rowContains": "E2E테스트_삭제용" + }, + "verification": { + "level": 4, + "description": "생성된 데이터가 목록에 표시되어야 함" + } + }, + { + "id": "step-6", + "phase": "UPDATE", + "name": "[UPDATE] 생성된 게시글 상세 페이지 진입", + "description": "생성한 테스트 게시글의 상세 페이지로 이동", + "actions": [ + {"type": "click", "target": "table tbody tr:first-child td:nth-child(2)", "description": "첫 번째 행 (방금 생성한 게시글) 클릭"}, + {"type": "wait", "duration": 2000} + ], + "expect": { + "url": "/ko/customer-center/free-board/", + "pageContains": "E2E테스트_삭제용" + } + }, + { + "id": "step-7", + "phase": "UPDATE", + "name": "[UPDATE] 수정 버튼 클릭", + "description": "수정 버튼을 클릭하여 편집 모드로 전환", + "actions": [ + {"type": "click", "target": "button:has-text('수정')", "description": "수정 버튼 클릭"}, + {"type": "wait", "duration": 1500} + ], + "expect": { + "url": "/edit", + "fieldsEditable": true + } + }, + { + "id": "step-8", + "phase": "UPDATE", + "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": "수정된 제목 입력"} + ] + }, + { + "id": "step-9", + "phase": "UPDATE", + "name": "[UPDATE] 수정 저장", + "description": "수정된 내용 저장", + "actions": [ + {"type": "click", "target": "button:has-text('수정')", "description": "수정 버튼 클릭"}, + {"type": "wait", "duration": 2000} + ], + "expect": { + "toast": "수정|완료|성공", + "redirect": "/ko/customer-center/free-board/" + }, + "verification": { + "level": 4, + "description": "수정 성공 토스트 확인" + } + }, + { + "id": "step-10", + "phase": "UPDATE", + "name": "[UPDATE] 수정 결과 확인", + "description": "수정된 내용이 반영되었는지 확인", + "actions": [ + {"type": "wait", "duration": 1000}, + {"type": "verify", "target": "page", "contains": "E2E테스트_수정완료", "description": "수정된 제목 표시 확인"} + ], + "expect": { + "contains": "E2E테스트_수정완료" + } + }, + { + "id": "step-11", + "phase": "DELETE", + "name": "[DELETE] 삭제 버튼 클릭", + "description": "테스트용으로 생성한 게시글 삭제 시작", + "critical": true, + "actions": [ + {"type": "click", "target": "button:has-text('삭제')", "description": "삭제 버튼 클릭"}, + {"type": "wait", "duration": 500} + ], + "expect": { + "confirmDialog": true, + "dialogRole": "alertdialog" + }, + "warning": "이 단계에서 삭제 확인 다이얼로그가 표시되어야 함" + }, + { + "id": "step-12", + "phase": "DELETE", + "name": "[DELETE] 삭제 확인", + "description": "삭제 확인 다이얼로그에서 삭제 버튼 클릭", + "critical": true, + "actions": [ + {"type": "click", "target": "[role='alertdialog'] button:has-text('삭제')", "usePlaywrightNative": true, "description": "삭제 확인 클릭 (Playwright 네이티브 셀렉터 필수)"}, + {"type": "wait", "duration": 2000} + ], + "expect": { + "toast": "삭제|완료|성공", + "redirect": "/ko/customer-center/free-board" + }, + "verification": { + "level": 4, + "checks": ["성공 토스트", "목록 페이지로 리다이렉트"] + }, + "note": "JavaScript click()은 동작하지 않음. Playwright playwright_click 사용 필수" + }, + { + "id": "step-13", + "phase": "VERIFY", + "name": "[VERIFY] 삭제 결과 확인", + "description": "삭제된 게시글이 목록에서 제거되었는지 확인", + "actions": [ + {"type": "wait", "duration": 1000}, + {"type": "verifyUrl", "contains": "/ko/customer-center/free-board", "description": "목록 페이지 확인"}, + {"type": "capture", "variable": "afterDeleteCount", "selector": "table tbody tr", "extract": "count"} + ], + "expect": { + "rowCountDecreased": true, + "condition": "afterDeleteCount === initialRowCount" + }, + "verification": { + "level": 4, + "description": "행 수가 원래대로 복구되어야 함" + } + }, + { + "id": "step-14", + "phase": "VERIFY", + "name": "[VERIFY] 최종 검증", + "description": "테스트 데이터가 완전히 삭제되었는지 확인", + "actions": [ + {"type": "verify", "target": "table", "notContains": "E2E테스트_수정완료", "description": "삭제된 게시글이 목록에 없음 확인"} + ], + "expect": { + "testDataDeleted": true, + "noTestDataInList": true + }, + "verification": { + "level": 4, + "description": "테스트 데이터가 완전히 삭제되어 목록에 표시되지 않아야 함" + } + } + ], + "expectedAPIs": [ + { + "phase": "CREATE", + "method": "POST", + "endpoint": "/api/v1/free-board", + "description": "게시글 등록" + }, + { + "phase": "UPDATE", + "method": "PUT", + "endpoint": "/api/v1/free-board/{id}", + "description": "게시글 수정" + }, + { + "phase": "DELETE", + "method": "DELETE", + "endpoint": "/api/v1/free-board/{id}", + "description": "게시글 삭제" + } + ], + "requiredVerifications": [ + { + "id": 1, + "name": "CREATE - 등록 기능", + "steps": ["step-2", "step-3", "step-4", "step-5"], + "criteria": "게시글 생성 + 목록에 표시" + }, + { + "id": 2, + "name": "UPDATE - 수정 기능", + "steps": ["step-7", "step-8", "step-9", "step-10"], + "criteria": "게시글 수정 + 변경 내용 반영" + }, + { + "id": 3, + "name": "DELETE - 삭제 기능", + "steps": ["step-11", "step-12", "step-13", "step-14"], + "criteria": "게시글 삭제 + 목록에서 제거" + } + ], + "knownIssues": [ + { + "issue": "삭제 확인 다이얼로그 JavaScript click 미동작", + "description": "JavaScript의 element.click() 또는 dispatchEvent로는 삭제가 실행되지 않음", + "workaround": "Playwright의 playwright_click 도구와 [role='alertdialog'] button:has-text('삭제') 셀렉터 사용", + "testedOn": "2026-01-29" + } + ], + "rollbackPlan": { + "description": "테스트 실패 시 롤백 계획", + "onCreateFail": "영향 없음 - 다음 테스트 정상 진행", + "onUpdateFail": "테스트 데이터 수동 삭제 필요", + "onDeleteFail": "테스트 데이터 수동 삭제 필요", + "cleanupRequired": "E2E테스트_ 로 시작하는 게시글은 테스트 데이터이므로 수동 삭제 가능" + } +} diff --git a/crud-delete-vendor.json b/crud-delete-vendor.json new file mode 100644 index 0000000..4e9ab61 --- /dev/null +++ b/crud-delete-vendor.json @@ -0,0 +1,404 @@ +{ + "id": "crud-delete-vendor", + "name": "거래처 CRUD 삭제 기능 테스트", + "status": "BLOCKED", + "blockedReason": "거래처관리 페이지에 등록 버튼이 없음 - CREATE 단계 불가", + "discoveredOn": "2026-01-29", + "alternative": "crud-delete-freeboard.json 사용 (자유게시판 CRUD 테스트)", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "거래처관리에서 생성 → 수정 → 삭제 전체 CRUD 흐름 테스트. 테스트용 데이터를 생성하고, 수정한 후, 삭제하여 기존 데이터에 영향 없이 삭제 기능을 검증", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "회계관리", + "level2": "거래처관리", + "expectedUrl": "/ko/accounting/vendors" + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "testPolicy": { + "deleteAllowed": true, + "deleteCondition": "CRUD 흐름 내에서만 허용 (생성 → 수정 → 삭제)", + "protectExistingData": true, + "description": "이 시나리오에서 생성한 테스트 데이터만 삭제" + }, + "modalHandling": { + "description": "모달 창 처리 규칙", + "closeMethods": [ + {"priority": 1, "method": "완료 버튼", "selector": "button:has-text('확인'), button:has-text('등록'), button:has-text('저장')"}, + {"priority": 2, "method": "취소 버튼", "selector": "button:has-text('취소'), [class*='cancel']"}, + {"priority": 3, "method": "X 버튼", "selector": "button[class*='close'], [aria-label='닫기'], [aria-label='Close']"}, + {"priority": 4, "method": "ESC 키", "action": "press_key('Escape')"}, + {"priority": 5, "method": "외부 클릭", "selector": "[class*='backdrop'], [class*='overlay']"} + ], + "rule": "모달이 열린 상태로 다음 단계 진행 금지" + }, + "testData": { + "newVendor": { + "vendorName": "E2E테스트_삭제용_" , + "businessNumber": "123-45-67890", + "representative": "테스트대표", + "vendorType": "매출", + "phone": "02-1234-5678", + "email": "test@e2etest.com", + "address": "서울시 테스트구 테스트동 123" + }, + "updateData": { + "vendorName": "E2E테스트_수정완료_", + "representative": "수정대표" + }, + "uniqueIdentifier": "timestamp를 붙여서 고유성 보장" + }, + "steps": [ + { + "id": "step-0", + "name": "사이드바 메뉴 탐색", + "description": "회계관리 > 거래처관리 메뉴로 이동", + "actions": [ + {"type": "scroll", "target": "sidebar", "direction": "top"}, + {"type": "wait", "duration": 300}, + {"type": "click", "target": "회계관리", "description": "1차 메뉴 클릭"}, + {"type": "wait", "duration": 500}, + {"type": "click", "target": "거래처관리", "description": "2차 메뉴 클릭"}, + {"type": "wait", "duration": 2000} + ], + "expect": { + "url": "/ko/accounting/vendors", + "no404": true + } + }, + { + "id": "step-1", + "phase": "CREATE", + "name": "📝 [CREATE] 거래처 등록 버튼 클릭", + "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": "wait", "duration": 1000} + ], + "expect": { + "modal": true, + "modalTitle": "거래처 등록" + } + }, + { + "id": "step-2", + "phase": "CREATE", + "name": "📝 [CREATE] 등록 모달 - 필수 정보 입력", + "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": "이메일 입력"} + ], + "note": "타임스탬프를 사용하여 매 테스트마다 고유한 데이터 생성" + }, + { + "id": "step-3", + "phase": "CREATE", + "name": "📝 [CREATE] 등록 모달 - 등록 버튼 클릭", + "description": "입력된 정보로 거래처 등록 실행", + "actions": [ + {"type": "click", "target": "button:has-text('등록'), button:has-text('저장')", "description": "모달 내 등록 버튼 클릭"}, + {"type": "wait", "duration": 2000} + ], + "expect": { + "toast": "등록|완료|성공", + "modalClosed": true, + "apiCall": "POST /api/v1/clients" + }, + "verification": { + "level": 4, + "checks": ["API 호출 확인", "성공 토스트 표시", "모달 닫힘"] + } + }, + { + "id": "step-3-modal-close", + "phase": "CREATE", + "name": "📝 [CREATE] 모달 닫기 확인", + "description": "모달이 열려있으면 닫기 시도", + "condition": "modalStillOpen", + "actions": [ + {"type": "checkModalOpen", "description": "모달 열림 상태 확인"}, + {"type": "closeModal", "methods": ["cancel", "xButton", "escape"], "description": "모달 닫기 시도"}, + {"type": "wait", "duration": 500} + ], + "expect": { + "modalClosed": true + } + }, + { + "id": "step-4", + "phase": "CREATE", + "name": "📝 [CREATE] 등록 결과 확인", + "description": "테이블에 새로 등록한 거래처가 표시되는지 확인", + "actions": [ + {"type": "fill", "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"} + ], + "expect": { + "rowExists": true, + "rowContains": "E2E테스트_삭제용" + }, + "verification": { + "level": 4, + "description": "생성된 데이터가 목록에 표시되어야 함" + } + }, + { + "id": "step-5", + "phase": "UPDATE", + "name": "✏️ [UPDATE] 생성된 거래처 상세 페이지 진입", + "description": "생성한 테스트 거래처의 상세 페이지로 이동", + "actions": [ + {"type": "click", "target": "table tbody tr:has-text('E2E테스트_삭제용')", "description": "생성한 거래처 행 클릭"}, + {"type": "wait", "duration": 2000} + ], + "expect": { + "url": "/ko/accounting/vendors/", + "pageTitle": "거래처 상세" + } + }, + { + "id": "step-6", + "phase": "UPDATE", + "name": "✏️ [UPDATE] 수정 모드 진입", + "description": "수정 버튼을 클릭하여 편집 모드로 전환", + "actions": [ + {"type": "click", "target": "button:has-text('수정')", "description": "수정 버튼 클릭"}, + {"type": "wait", "duration": 1000} + ], + "expect": { + "url": "mode=edit", + "fieldsEditable": true + } + }, + { + "id": "step-7", + "phase": "UPDATE", + "name": "✏️ [UPDATE] 거래처명 수정", + "description": "거래처명을 수정하여 UPDATE 동작 확인", + "actions": [ + {"type": "clear", "target": "거래처명", "description": "기존 값 삭제"}, + {"type": "fill", "target": "거래처명", "value": "E2E테스트_수정완료_{testTimestamp}", "description": "수정된 거래처명 입력"}, + {"type": "fill", "target": "대표자명", "value": "수정대표", "description": "대표자명 수정"} + ] + }, + { + "id": "step-8", + "phase": "UPDATE", + "name": "✏️ [UPDATE] 수정 저장", + "description": "수정된 내용 저장", + "actions": [ + {"type": "click", "target": "button:has-text('저장')", "description": "저장 버튼 클릭"}, + {"type": "wait", "duration": 500}, + {"type": "click", "target": "button:has-text('확인')", "description": "저장 확인 다이얼로그"}, + {"type": "wait", "duration": 2000} + ], + "expect": { + "toast": "수정|완료|성공", + "apiCall": "PUT /api/v1/clients/" + }, + "verification": { + "level": 4, + "description": "수정 API 호출 및 성공 토스트 확인" + } + }, + { + "id": "step-8-modal-close", + "phase": "UPDATE", + "name": "✏️ [UPDATE] 다이얼로그 닫기 확인", + "description": "다이얼로그가 열려있으면 닫기", + "condition": "dialogStillOpen", + "actions": [ + {"type": "checkModalOpen"}, + {"type": "closeModal", "methods": ["cancel", "xButton", "escape"]} + ] + }, + { + "id": "step-9", + "phase": "UPDATE", + "name": "✏️ [UPDATE] 수정 결과 확인", + "description": "수정된 내용이 반영되었는지 확인", + "actions": [ + {"type": "wait", "duration": 1000}, + {"type": "capture", "variable": "updatedVendorName", "selector": "[class*='vendor-name'], h1, h2", "extract": "text"} + ], + "expect": { + "contains": "E2E테스트_수정완료" + } + }, + { + "id": "step-10", + "phase": "DELETE", + "name": "🗑️ [DELETE] 삭제 버튼 클릭", + "description": "테스트용으로 생성한 거래처 삭제 시작", + "critical": true, + "actions": [ + {"type": "click", "target": "button:has-text('삭제')", "description": "삭제 버튼 클릭"}, + {"type": "wait", "duration": 500} + ], + "expect": { + "confirmDialog": true, + "dialogMessage": "삭제|정말|확인" + }, + "warning": "이 단계에서 삭제 확인 다이얼로그가 표시되어야 함" + }, + { + "id": "step-11", + "phase": "DELETE", + "name": "🗑️ [DELETE] 삭제 확인 다이얼로그 검증", + "description": "삭제 확인 다이얼로그 UI 요소 확인", + "actions": [ + {"type": "verify", "target": "dialog", "checks": ["삭제 확인 메시지", "취소 버튼", "확인/삭제 버튼"]} + ], + "expect": { + "dialogElements": ["취소", "확인|삭제"] + } + }, + { + "id": "step-12", + "phase": "DELETE", + "name": "🗑️ [DELETE] 삭제 확인 버튼 클릭", + "description": "삭제를 최종 확인하여 실행", + "critical": true, + "actions": [ + {"type": "click", "target": "button:has-text('확인'), button:has-text('삭제')", "description": "삭제 확인 클릭"}, + {"type": "wait", "duration": 2000} + ], + "expect": { + "toast": "삭제|완료|성공", + "apiCall": "DELETE /api/v1/clients/", + "redirect": "/ko/accounting/vendors" + }, + "verification": { + "level": 4, + "checks": ["DELETE API 호출", "성공 토스트", "목록 페이지로 리다이렉트"] + } + }, + { + "id": "step-12-modal-close", + "phase": "DELETE", + "name": "🗑️ [DELETE] 다이얼로그 닫기 확인", + "description": "다이얼로그가 열려있으면 닫기", + "condition": "dialogStillOpen", + "actions": [ + {"type": "checkModalOpen"}, + {"type": "closeModal", "methods": ["cancel", "xButton", "escape"]} + ] + }, + { + "id": "step-13", + "phase": "VERIFY", + "name": "✅ [VERIFY] 삭제 결과 확인 - 목록 페이지", + "description": "삭제된 거래처가 목록에서 제거되었는지 확인", + "actions": [ + {"type": "wait", "duration": 1000}, + {"type": "verifyUrl", "contains": "/ko/accounting/vendors", "description": "목록 페이지 확인"}, + {"type": "fill", "target": "검색", "value": "E2E테스트_수정완료", "description": "삭제된 거래처 검색"}, + {"type": "pressKey", "key": "Enter"}, + {"type": "wait", "duration": 1500} + ], + "expect": { + "noResults": true, + "rowNotExists": "E2E테스트_수정완료" + } + }, + { + "id": "step-14", + "phase": "VERIFY", + "name": "✅ [VERIFY] 최종 검증 - 데이터 삭제 확인", + "description": "검색 결과에서 테스트 데이터가 없음을 최종 확인", + "actions": [ + {"type": "capture", "variable": "searchResultCount", "selector": "table tbody tr", "extract": "count"}, + {"type": "verify", "condition": "searchResultCount === 0 OR no row contains 'E2E테스트'"} + ], + "expect": { + "testDataDeleted": true, + "noTestDataInList": true + }, + "verification": { + "level": 4, + "description": "테스트 데이터가 완전히 삭제되어 검색되지 않아야 함" + } + }, + { + "id": "step-15", + "phase": "CLEANUP", + "name": "🧹 [CLEANUP] 검색 초기화", + "description": "테스트 종료 후 검색어 삭제하여 원래 상태로 복원", + "actions": [ + {"type": "clear", "target": "검색"}, + {"type": "pressKey", "key": "Enter"}, + {"type": "wait", "duration": 1000} + ], + "expect": { + "originalListRestored": true + } + } + ], + "expectedAPIs": [ + { + "phase": "CREATE", + "method": "POST", + "endpoint": "/api/v1/clients", + "description": "거래처 등록" + }, + { + "phase": "UPDATE", + "method": "PUT", + "endpoint": "/api/v1/clients/{id}", + "description": "거래처 수정" + }, + { + "phase": "DELETE", + "method": "DELETE", + "endpoint": "/api/v1/clients/{id}", + "description": "거래처 삭제" + } + ], + "requiredVerifications": [ + { + "id": 1, + "name": "CREATE - 등록 기능", + "steps": ["step-1", "step-2", "step-3", "step-4"], + "criteria": "POST API 호출 + 성공 토스트 + 목록에 데이터 표시" + }, + { + "id": 2, + "name": "UPDATE - 수정 기능", + "steps": ["step-6", "step-7", "step-8", "step-9"], + "criteria": "PUT API 호출 + 성공 토스트 + 데이터 변경 반영" + }, + { + "id": 3, + "name": "DELETE - 삭제 기능", + "steps": ["step-10", "step-11", "step-12", "step-13", "step-14"], + "criteria": "DELETE API 호출 + 성공 토스트 + 목록에서 데이터 제거" + }, + { + "id": 4, + "name": "모달/다이얼로그 닫기", + "steps": ["step-3-modal-close", "step-8-modal-close", "step-12-modal-close"], + "criteria": "모든 모달/다이얼로그가 정상적으로 닫혀야 함" + } + ], + "rollbackPlan": { + "description": "테스트 실패 시 롤백 계획", + "onCreateFail": "모달 닫기 → 다음 테스트 영향 없음", + "onUpdateFail": "테스트 데이터 수동 삭제 필요 (DB 또는 UI)", + "onDeleteFail": "테스트 데이터 수동 삭제 필요", + "cleanupRequired": "E2E테스트_ 로 시작하는 거래처는 테스트 데이터이므로 수동 삭제 가능" + } +} diff --git a/customer-inquiry.json b/customer-inquiry.json new file mode 100644 index 0000000..e59bb27 --- /dev/null +++ b/customer-inquiry.json @@ -0,0 +1,550 @@ +{ + "id": "customer-inquiry", + "name": "고객센터 - 1:1 문의", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "1:1 문의 목록 조회, 문의 등록, 상세 조회, 수정, 삭제, 댓글 작성 전체 워크플로우 테스트", + "url": "/ko/customer-center/qna", + "menuNavigation": { + "level1": "고객센터", + "level2": "1:1 문의", + "expectedUrl": "/ko/customer-center/qna" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebarSelector": ".sidebar-scroll, [data-sidebar-content], nav[role='navigation']", + "scrollBehavior": { + "scrollToTop": true, + "scrollStep": 200, + "maxScrollAttempts": 10, + "waitAfterScroll": 300 + }, + "menuSearch": { + "level1": { + "text": "고객센터", + "selectors": ["[data-menu-item]", ".nav-item", "button", "a"], + "expandable": true + }, + "level2": { + "text": "1:1 문의", + "selectors": ["[data-submenu-item]", ".nav-subitem", "a"], + "waitForVisible": true + } + }, + "fallbackUrl": "/ko/customer-center/qna" + }, + "expectedAPIs": [ + { + "method": "GET", + "path": "/api/tenants/*/boards/qna/posts", + "description": "문의 목록 조회" + }, + { + "method": "POST", + "path": "/api/tenants/*/boards/qna/posts", + "description": "문의 등록" + }, + { + "method": "GET", + "path": "/api/tenants/*/boards/qna/posts/*", + "description": "문의 상세 조회" + }, + { + "method": "PUT", + "path": "/api/tenants/*/boards/qna/posts/*", + "description": "문의 수정" + }, + { + "method": "DELETE", + "path": "/api/tenants/*/boards/qna/posts/*", + "description": "문의 삭제" + }, + { + "method": "POST", + "path": "/api/tenants/*/boards/qna/posts/*/comments", + "description": "댓글 작성" + } + ], + "steps": [ + { + "id": "step-0", + "name": "사이드바 준비", + "description": "메뉴 탐색 전 사이드바를 초기 상태로 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll, [data-sidebar-content], nav[role=\"navigation\"]')?.scrollTo({top: 0, behavior: 'instant'})" + }, + { "type": "wait", "duration": 500 } + ], + "expected": { + "sidebarReady": true + }, + "verification": [ + "사이드바가 최상단으로 스크롤됨", + "메뉴 탐색 준비 완료" + ] + }, + { + "id": "step-01", + "name": "2단계 메뉴 진입: 고객센터 > 1:1 문의", + "description": "고객센터 > 1:1 문의 메뉴로 이동하여 페이지 로드 확인", + "navigationPattern": "scrollAndFind", + "actions": [ + { + "type": "scrollAndFind", + "target": "고객센터", + "scrollContainer": ".sidebar-scroll, [data-sidebar-content], nav[role='navigation']", + "scrollStep": 200, + "maxAttempts": 10 + }, + { "type": "click", "target": "고객센터" }, + { "type": "wait", "duration": 500 }, + { + "type": "scrollAndFind", + "target": "1:1 문의", + "scrollContainer": ".sidebar-scroll, [data-sidebar-content], nav[role='navigation']", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "1:1 문의" }, + { "type": "wait", "target": "페이지 로드 완료" } + ], + "expected": { + "url": "/ko/customer-center/qna", + "title": "1:1 문의", + "authenticated": true + }, + "verification": [ + "페이지 타이틀 '1:1 문의' 표시", + "페이지 설명 '1:1 문의를 등록하고 답변을 확인합니다.' 표시", + "문의 등록 버튼 존재" + ] + }, + { + "id": "step-02", + "name": "페이지 구조 확인", + "action": "verify", + "expectedResult": "모든 UI 요소가 정상적으로 표시됨", + "verification": [ + "날짜 범위 선택기 존재 (시작일/종료일)", + "기간 프리셋 버튼 존재 (당해년도, 전전월, 전월, 당월, 어제, 오늘)", + "문의 등록 버튼 존재", + "검색 입력 필드 존재 (placeholder: '제목, 작성자로 검색...')", + "필터 드롭다운 3개 존재 (상담분류, 상태, 정렬)" + ] + }, + { + "id": "step-03", + "name": "테이블 구조 확인", + "action": "verify", + "expectedResult": "테이블 헤더와 구조가 올바르게 표시됨", + "verification": [ + "체크박스 컬럼 존재", + "No. 컬럼 존재", + "상담분류 컬럼 존재", + "제목 컬럼 존재", + "상태 컬럼 존재", + "등록일 컬럼 존재" + ] + }, + { + "id": "step-04", + "name": "초기 데이터 로드 확인", + "action": "verify", + "expectedResult": "문의 목록 데이터가 로드됨 (있을 경우)", + "verification": [ + "총 N건 표시 확인", + "데이터가 있으면 테이블에 표시", + "데이터가 없으면 '검색 결과가 없습니다' 메시지 표시" + ] + }, + { + "id": "step-05", + "name": "상담분류 필터 동작 확인", + "action": "interact", + "target": "상담분류 드롭다운", + "expectedResult": "필터 옵션이 표시되고 선택 가능함", + "verification": [ + "드롭다운 클릭 시 옵션 목록 표시", + "옵션: 전체, 문의하기, 신고하기, 건의사항, 서비스오류", + "옵션 선택 시 필터가 적용됨" + ] + }, + { + "id": "step-06", + "name": "상태 필터 동작 확인", + "action": "interact", + "target": "상태 드롭다운", + "expectedResult": "상태 필터 옵션이 표시되고 선택 가능함", + "verification": [ + "드롭다운 클릭 시 옵션 목록 표시", + "옵션: 전체, 답변대기, 답변완료", + "옵션 선택 시 필터가 적용됨" + ] + }, + { + "id": "step-07", + "name": "정렬 옵션 동작 확인", + "action": "interact", + "target": "정렬 드롭다운", + "expectedResult": "정렬 옵션이 표시되고 선택 가능함", + "verification": [ + "드롭다운 클릭 시 옵션 목록 표시", + "옵션: 최신순, 오래된순", + "기본값: 최신순", + "옵션 선택 시 목록 정렬 변경" + ] + }, + { + "id": "step-08", + "name": "검색 기능 테스트", + "action": "interact", + "target": "검색 입력 필드", + "input": "테스트", + "expectedResult": "검색어에 맞는 문의가 필터링됨", + "verification": [ + "검색어 입력 가능", + "검색어가 제목 또는 작성자와 매칭되는 항목만 표시", + "검색 결과 건수 업데이트" + ] + }, + { + "id": "step-09", + "name": "날짜 범위 필터 테스트", + "action": "interact", + "target": "날짜 범위 선택기", + "expectedResult": "날짜 범위 선택이 가능하고 필터가 적용됨", + "verification": [ + "시작일 선택 가능", + "종료일 선택 가능", + "날짜 범위에 해당하는 문의만 표시", + "기간 프리셋 버튼 (당월, 어제 등) 클릭 시 날짜 자동 설정" + ] + }, + { + "id": "step-10", + "name": "문의 등록 버튼 클릭", + "action": "click", + "target": "문의 등록 버튼", + "expectedResult": "문의 등록 페이지로 이동", + "verification": [ + "URL이 /customer-center/qna/create로 변경", + "페이지 타이틀 '1:1 문의 등록' 표시", + "페이지 설명 '1:1 문의를 등록합니다.' 표시" + ] + }, + { + "id": "step-11", + "name": "문의 등록 폼 구조 확인", + "action": "verify", + "expectedResult": "등록 폼의 모든 필드가 표시됨", + "verification": [ + "상담분류 드롭다운 존재", + "제목 입력 필드 존재", + "내용 에디터 존재 (RichTextEditor)", + "첨부파일 업로드 버튼 존재", + "취소 버튼 존재", + "저장 버튼 존재" + ] + }, + { + "id": "step-12", + "name": "상담분류 선택", + "action": "interact", + "target": "상담분류 드롭다운", + "input": "inquiry", + "expectedResult": "상담분류가 선택됨", + "verification": [ + "드롭다운 클릭 시 옵션 표시", + "옵션: 문의하기, 신고하기, 건의사항, 서비스오류", + "'문의하기' 선택" + ] + }, + { + "id": "step-13", + "name": "제목 입력", + "action": "input", + "target": "제목 입력 필드", + "input": "E2E 테스트 문의입니다", + "expectedResult": "제목이 입력됨", + "verification": [ + "입력 필드에 텍스트 입력 가능", + "입력된 값이 표시됨" + ] + }, + { + "id": "step-14", + "name": "내용 입력", + "action": "input", + "target": "내용 에디터", + "input": "

E2E 테스트를 위한 문의 내용입니다.

정상적으로 등록되는지 확인합니다.

", + "expectedResult": "내용이 입력됨", + "verification": [ + "RichTextEditor에 HTML 내용 입력 가능", + "입력된 내용이 에디터에 표시됨" + ] + }, + { + "id": "step-15", + "name": "필수 입력값 누락 시 유효성 검사 확인", + "action": "verify", + "expectedResult": "필수값 미입력 시 에러 메시지 표시", + "verification": [ + "제목 미입력 시 '제목을 입력해주세요.' 에러 표시", + "내용 미입력 시 '내용을 입력해주세요.' 에러 표시", + "상담분류 미선택 시 '상담분류를 선택해주세요.' 에러 표시" + ] + }, + { + "id": "step-16", + "name": "문의 저장 (등록)", + "action": "click", + "target": "저장 버튼", + "expectedResult": "문의가 등록되고 목록 페이지로 이동", + "verification": [ + "POST /api/tenants/*/boards/qna/posts API 호출", + "API 응답 200 OK", + "URL이 /customer-center/qna로 변경", + "목록 페이지로 돌아감", + "성공 토스트 메시지 표시 (있을 경우)" + ], + "criticalValidation": { + "type": "registration", + "checkURL": true, + "checkErrorPage": true, + "checkAPI": true, + "expectedURL": "/customer-center/qna" + } + }, + { + "id": "step-17", + "name": "등록된 문의 확인", + "action": "verify", + "expectedResult": "새로 등록한 문의가 목록에 표시됨", + "verification": [ + "목록 최상단에 'E2E 테스트 문의입니다' 제목 표시", + "상담분류 '문의하기' 배지 표시", + "상태 '답변대기' 배지 표시", + "등록일이 오늘 날짜로 표시" + ] + }, + { + "id": "step-18", + "name": "등록한 문의 클릭 (상세 페이지 이동)", + "action": "click", + "target": "등록한 문의 행", + "expectedResult": "문의 상세 페이지로 이동", + "verification": [ + "URL이 /customer-center/qna/[id]로 변경", + "페이지 타이틀 '1:1 문의 상세' 표시", + "페이지 설명 '1:1 문의를 조회합니다.' 표시" + ] + }, + { + "id": "step-19", + "name": "문의 상세 내용 확인", + "action": "verify", + "expectedResult": "문의 상세 정보가 표시됨", + "verification": [ + "문의 제목 'E2E 테스트 문의입니다' 표시", + "작성자 이름 표시", + "작성일 표시", + "상담분류 '문의하기' 배지 표시", + "상태 '답변대기' 표시", + "문의 내용 표시 (HTML 렌더링)", + "수정 버튼 존재 (본인 문의인 경우)", + "삭제 버튼 존재 (본인 문의인 경우)" + ] + }, + { + "id": "step-20", + "name": "댓글 작성 영역 확인", + "action": "verify", + "expectedResult": "댓글 작성 UI가 표시됨", + "verification": [ + "댓글 입력 textarea 존재", + "댓글 등록 버튼 존재", + "기존 댓글 목록 영역 존재" + ] + }, + { + "id": "step-21", + "name": "댓글 작성", + "action": "interact", + "target": "댓글 입력 필드", + "input": "E2E 테스트 댓글입니다", + "expectedResult": "댓글이 입력되고 등록됨", + "verification": [ + "textarea에 댓글 내용 입력", + "등록 버튼 클릭", + "POST /api/tenants/*/boards/qna/posts/*/comments API 호출", + "댓글이 목록에 추가됨" + ] + }, + { + "id": "step-22", + "name": "수정 버튼 클릭", + "action": "click", + "target": "수정 버튼", + "expectedResult": "문의 수정 페이지로 이동", + "verification": [ + "URL이 /customer-center/qna/[id]?mode=edit로 변경", + "페이지 타이틀 '1:1 문의 수정' 표시", + "기존 입력값이 폼에 로드됨 (제목, 상담분류, 내용)" + ] + }, + { + "id": "step-23", + "name": "제목 수정", + "action": "input", + "target": "제목 입력 필드", + "input": "E2E 테스트 문의입니다 (수정됨)", + "expectedResult": "제목이 수정됨", + "verification": [ + "기존 제목이 표시됨", + "제목 수정 가능", + "수정된 값이 표시됨" + ] + }, + { + "id": "step-24", + "name": "문의 저장 (수정)", + "action": "click", + "target": "저장 버튼", + "expectedResult": "문의가 수정되고 목록 페이지로 이동", + "verification": [ + "PUT /api/tenants/*/boards/qna/posts/* API 호출", + "API 응답 200 OK", + "URL이 /customer-center/qna로 변경", + "목록 페이지로 돌아감" + ], + "criticalValidation": { + "type": "registration", + "checkURL": true, + "checkErrorPage": true, + "checkAPI": true, + "expectedURL": "/customer-center/qna" + } + }, + { + "id": "step-25", + "name": "수정된 문의 확인", + "action": "verify", + "expectedResult": "수정된 제목이 목록에 표시됨", + "verification": [ + "목록에서 'E2E 테스트 문의입니다 (수정됨)' 제목 확인" + ] + }, + { + "id": "step-26", + "name": "문의 다시 클릭 (삭제를 위해)", + "action": "click", + "target": "수정된 문의 행", + "expectedResult": "문의 상세 페이지로 이동", + "verification": [ + "URL이 /customer-center/qna/[id]로 변경", + "수정된 제목 표시" + ] + }, + { + "id": "step-27", + "name": "삭제 버튼 클릭", + "action": "click", + "target": "삭제 버튼", + "expectedResult": "삭제 확인 다이얼로그가 표시됨", + "verification": [ + "AlertDialog 표시", + "삭제 확인 메시지 표시", + "취소 버튼 존재", + "삭제 확인 버튼 존재" + ] + }, + { + "id": "step-28", + "name": "삭제 확인", + "action": "click", + "target": "삭제 확인 버튼", + "expectedResult": "문의가 삭제되고 목록 페이지로 이동", + "verification": [ + "DELETE /api/tenants/*/boards/qna/posts/* API 호출", + "API 응답 200 OK", + "URL이 /customer-center/qna로 변경", + "목록 페이지로 돌아감", + "삭제된 문의가 목록에서 사라짐" + ], + "criticalValidation": { + "type": "registration", + "checkURL": true, + "checkErrorPage": true, + "checkAPI": true, + "expectedURL": "/customer-center/qna" + } + }, + { + "id": "step-29", + "name": "삭제 확인 (목록에서)", + "action": "verify", + "expectedResult": "삭제된 문의가 목록에 없음", + "verification": [ + "'E2E 테스트 문의입니다 (수정됨)' 제목이 목록에 표시되지 않음", + "총 건수가 1개 감소" + ] + }, + { + "id": "step-30", + "name": "페이지네이션 테스트 (데이터 10개 이상일 경우)", + "action": "interact", + "target": "페이지네이션", + "expectedResult": "페이지 이동이 정상 동작함", + "verification": [ + "데이터가 10개 이상일 경우 페이지네이션 표시", + "다음 페이지 버튼 클릭 시 2페이지 데이터 표시", + "이전 페이지 버튼 클릭 시 1페이지로 돌아감", + "페이지 번호 버튼 클릭 시 해당 페이지로 이동" + ] + }, + { + "id": "step-31", + "name": "체크박스 선택 테스트", + "action": "interact", + "target": "체크박스", + "expectedResult": "행 선택이 정상 동작함", + "verification": [ + "개별 체크박스 클릭 시 해당 행 선택", + "전체 선택 체크박스 클릭 시 모든 행 선택", + "다시 클릭 시 선택 해제" + ] + }, + { + "id": "step-32", + "name": "모바일 반응형 테스트 (선택)", + "action": "verify", + "expectedResult": "모바일 화면에서 카드 형식으로 표시됨", + "verification": [ + "화면 크기 축소 시 테이블이 카드 형식으로 변경", + "카드에 필수 정보 표시 (제목, 상담분류, 상태, 날짜)", + "카드 클릭 시 상세 페이지 이동" + ] + } + ], + "testData": { + "inquiry": { + "category": "inquiry", + "title": "E2E 테스트 문의입니다", + "content": "

E2E 테스트를 위한 문의 내용입니다.

정상적으로 등록되는지 확인합니다.

" + }, + "updatedInquiry": { + "title": "E2E 테스트 문의입니다 (수정됨)" + }, + "comment": { + "content": "E2E 테스트 댓글입니다" + } + }, + "cleanupRequired": true, + "cleanupSteps": [ + "생성한 문의 삭제 (step-28에서 처리됨)" + ] +} diff --git a/daily-report.json b/daily-report.json new file mode 100644 index 0000000..4abf442 --- /dev/null +++ b/daily-report.json @@ -0,0 +1,929 @@ +{ + "id": "daily-report", + "name": "일일리포트 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "회계관리 > 일일리포트 메뉴의 날짜 선택, 테이블 데이터 표시, 새로고침, 엑셀 다운로드 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/ko/accounting/daily-report", + "menuNavigation": { + "level1": "회계관리", + "level2": "일일 일보", + "expectedUrl": "/ko/accounting/daily-report" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", + "level1": "회계관리", + "level2": "일일 일보", + "alternativeLevel1Names": ["회계관리", "회계 관리", "Accounting"], + "alternativeLevel2Names": ["일일 일보", "일일리포트", "일일 리포트", "Daily Report"], + "scrollConfig": { + "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", + "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + } + }, + "testFocus": { + "primary": "날짜별 데이터 조회 및 테이블 표시 검증", + "description": "날짜 선택, 어음 및 외상매출채권 테이블, 일자별 상세 테이블(KRW/USD 분리), 매칭 상태, 합계 검증" + }, + "prerequisites": { + "authentication": true, + "testData": { + "testDate": "2026-01-10", + "todayDate": "2026-01-15" + } + }, + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/daily-report/note-receivables", + "params": "date=YYYY-MM-DD", + "description": "어음 및 외상매출채권 데이터 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/daily-report/daily-accounts", + "params": "date=YYYY-MM-DD", + "description": "일자별 계좌 상세 데이터 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/daily-report/summary", + "params": "date=YYYY-MM-DD", + "description": "일일리포트 요약 데이터 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/daily-report/export", + "params": "date=YYYY-MM-DD", + "description": "엑셀 다운로드" + } + ], + "steps": [ + { + "id": "step-0", + "name": "사이드바 메뉴 탐색 준비", + "description": "사이드바를 최상단으로 스크롤하고 메뉴 구조 파악", + "actions": [ + { "type": "scroll", "target": "sidebar", "direction": "top", "description": "사이드바 최상단으로 스크롤" }, + { "type": "wait", "duration": 500 } + ] + }, + { + "id": "step-1", + "name": "2단계 메뉴 진입: 회계관리 > 일일리포트", + "description": "사이드바를 스크롤하며 회계관리 > 일일리포트 메뉴를 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "회계관리", + "alternativeTexts": ["회계관리", "회계 관리", "Accounting"], + "scrollContainer": "sidebar", + "maxAttempts": 10, + "description": "스크롤하며 회계관리 메뉴 찾기" + }, + { "type": "click", "target": "회계관리", "description": "회계관리 메뉴 클릭" }, + { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, + { + "type": "scrollAndFind", + "target": "일일리포트", + "alternativeTexts": ["일일리포트", "일일 리포트", "Daily Report"], + "scrollContainer": "submenu", + "maxAttempts": 5, + "description": "서브메뉴에서 일일리포트 찾기" + }, + { "type": "click", "target": "일일리포트", "description": "일일리포트 메뉴 클릭" }, + { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 } + ], + "expected": { + "url": "/ko/accounting/daily-report", + "pageTitle": "일일리포트", + "elements": ["페이지 타이틀", "날짜 선택", "테이블"], + "authenticated": true + }, + "verification": [ + "회계관리 메뉴가 펼쳐졌는지 확인", + "일일리포트 서브메뉴 클릭 성공", + "404 에러 없이 페이지 로드 완료" + ] + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "날짜 선택, 버튼, 테이블 구조 확인", + "actions": [ + { + "type": "verify", + "target": "페이지 구조" + } + ], + "expected": { + "datePicker": "조회 일자 입력 필드", + "buttons": ["새로고침", "엑셀 다운로드"], + "tables": ["어음 및 외상매출채권현황", "일자별 상세"] + } + }, + { + "id": "step-3", + "name": "날짜 선택 필드 기본값 확인", + "description": "날짜 입력 필드의 기본값이 오늘 날짜인지 확인", + "actions": [ + { + "type": "verify", + "target": "date input value" + } + ], + "expected": { + "dateValue": "2026-01-15", + "dateFormat": "YYYY-MM-DD" + } + }, + { + "id": "step-4", + "name": "페이지 타이틀 날짜 표시 확인", + "description": "페이지 타이틀에 선택된 날짜와 요일이 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "title date format" + } + ], + "expected": { + "titleFormat": "2026년 1월 15일 수요일", + "includesDayOfWeek": true + } + }, + { + "id": "step-5", + "name": "어음 및 외상매출채권현황 테이블 구조 확인", + "description": "첫 번째 테이블의 컬럼 헤더 확인", + "actions": [ + { + "type": "verify", + "target": "note receivables table columns" + } + ], + "expected": { + "tableTitle": "어음 및 외상매출채권현황", + "columns": ["내용", "현재 잔액", "발행일", "만기일"] + } + }, + { + "id": "step-6", + "name": "어음 및 외상매출채권 데이터 로드 확인", + "description": "테이블에 데이터가 표시되는지 확인 (또는 '데이터가 없습니다' 메시지)", + "actions": [ + { + "type": "verify", + "target": "table data or empty message" + } + ], + "expected": { + "dataExists": "데이터 행 존재 또는 '데이터가 없습니다' 메시지", + "apiCalled": "GET /api/v1/daily-report/note-receivables" + } + }, + { + "id": "step-7", + "name": "어음 및 외상매출채권 합계 확인", + "description": "테이블 하단의 합계 행이 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "table footer total" + } + ], + "expected": { + "footerExists": true, + "footerLabel": "합계", + "totalCalculation": "현재 잔액 합계 표시" + } + }, + { + "id": "step-8", + "name": "일자별 상세 테이블 구조 확인", + "description": "두 번째 테이블의 컬럼 헤더 확인", + "actions": [ + { + "type": "verify", + "target": "daily accounts table columns" + } + ], + "expected": { + "tableTitle": "일자별 상세", + "columns": ["구분", "상태", "전월 이월", "수입", "지출", "잔액"] + } + }, + { + "id": "step-9", + "name": "일자별 상세 데이터 로드 확인", + "description": "테이블에 데이터가 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "daily accounts data" + } + ], + "expected": { + "dataExists": "데이터 행 존재 또는 '데이터가 없습니다' 메시지", + "apiCalled": "GET /api/v1/daily-report/daily-accounts" + } + }, + { + "id": "step-10", + "name": "KRW 계좌 데이터 확인", + "description": "원화(KRW) 계좌 데이터가 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "KRW accounts" + } + ], + "expected": { + "krwAccountsExist": true, + "currencyType": "KRW" + } + }, + { + "id": "step-11", + "name": "USD 계좌 데이터 확인", + "description": "외화(USD) 계좌 데이터가 별도로 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "USD accounts" + } + ], + "expected": { + "usdAccountsExist": "USD 계좌 존재 또는 없음", + "currencyType": "USD" + } + }, + { + "id": "step-12", + "name": "매칭 상태 Badge 확인", + "description": "상태 컬럼의 매칭/비매칭 Badge가 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "match status badges" + } + ], + "expected": { + "badgeTypes": ["매칭", "비매칭"], + "matchedStyle": "green background", + "unmatchedStyle": "orange background" + } + }, + { + "id": "step-13", + "name": "외화원(USD) 합계 행 확인", + "description": "일자별 상세 테이블의 첫 번째 합계 행 확인", + "actions": [ + { + "type": "verify", + "target": "USD total row" + } + ], + "expected": { + "footerLabel": "외화원 (USD) 합계", + "totalCalculation": "USD 계좌 합계 표시" + } + }, + { + "id": "step-14", + "name": "현금성 자산 합계 행 확인", + "description": "일자별 상세 테이블의 두 번째 합계 행 확인", + "actions": [ + { + "type": "verify", + "target": "cash asset total row" + } + ], + "expected": { + "footerLabel": "현금성 자산 합계", + "totalCalculation": "전체 현금성 자산 합계 표시" + } + }, + { + "id": "step-15", + "name": "로딩 상태 확인 - 어음 테이블", + "description": "데이터 로딩 중 어음 테이블에 로딩 스피너가 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "note receivables loading state" + } + ], + "expected": { + "loadingSpinner": "Loader2 spinner", + "loadingMessage": "데이터를 불러오는 중..." + } + }, + { + "id": "step-16", + "name": "로딩 상태 확인 - 일자별 상세 테이블", + "description": "데이터 로딩 중 일자별 상세 테이블에 로딩 스피너가 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "daily accounts loading state" + } + ], + "expected": { + "loadingSpinner": "Loader2 spinner", + "loadingMessage": "데이터를 불러오는 중..." + } + }, + { + "id": "step-17", + "name": "날짜 변경 - 과거 날짜 선택", + "description": "날짜 선택 필드에서 과거 날짜(2026-01-10)로 변경", + "actions": [ + { + "type": "clear", + "target": "date input" + }, + { + "type": "type", + "target": "date input", + "value": "2026-01-10" + }, + { + "type": "wait", + "target": "데이터 로드" + } + ], + "expected": { + "dateChanged": "2026-01-10", + "dataReloaded": true + } + }, + { + "id": "step-18", + "name": "날짜 변경 후 페이지 타이틀 확인", + "description": "변경된 날짜가 페이지 타이틀에 반영되는지 확인", + "actions": [ + { + "type": "verify", + "target": "title date update" + } + ], + "expected": { + "titleFormat": "2026년 1월 10일 금요일", + "dateMatches": "2026-01-10" + } + }, + { + "id": "step-19", + "name": "날짜 변경 후 데이터 리로드 확인", + "description": "새로운 날짜에 대한 데이터가 로드되는지 확인", + "actions": [ + { + "type": "verify", + "target": "data reload for new date" + } + ], + "expected": { + "apiCalledWithDate": "2026-01-10", + "dataUpdated": true + } + }, + { + "id": "step-20", + "name": "날짜 변경 후 어음 테이블 데이터 확인", + "description": "변경된 날짜의 어음 데이터가 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "note receivables data for 2026-01-10" + } + ], + "expected": { + "dataForDate": "2026-01-10", + "tableUpdated": true + } + }, + { + "id": "step-21", + "name": "날짜 변경 후 일자별 상세 테이블 데이터 확인", + "description": "변경된 날짜의 계좌 상세 데이터가 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "daily accounts data for 2026-01-10" + } + ], + "expected": { + "dataForDate": "2026-01-10", + "tableUpdated": true + } + }, + { + "id": "step-22", + "name": "날짜를 오늘로 되돌리기", + "description": "날짜 선택 필드를 다시 오늘 날짜(2026-01-15)로 변경", + "actions": [ + { + "type": "clear", + "target": "date input" + }, + { + "type": "type", + "target": "date input", + "value": "2026-01-15" + }, + { + "type": "wait", + "target": "데이터 로드" + } + ], + "expected": { + "dateChanged": "2026-01-15", + "dataReloaded": true + } + }, + { + "id": "step-23", + "name": "새로고침 버튼 존재 확인", + "description": "새로고침 버튼이 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "refresh button" + } + ], + "expected": { + "buttonExists": true, + "buttonText": "새로고침", + "icon": "RefreshCw" + } + }, + { + "id": "step-24", + "name": "새로고침 버튼 클릭", + "description": "새로고침 버튼 클릭하여 데이터 리로드 확인", + "actions": [ + { + "type": "click", + "target": "새로고침 버튼" + }, + { + "type": "wait", + "target": "데이터 리로드" + } + ], + "expected": { + "buttonClicked": true, + "loadingStateShown": true, + "dataReloaded": true + } + }, + { + "id": "step-25", + "name": "새로고침 버튼 로딩 상태 확인", + "description": "새로고침 버튼 클릭 시 버튼이 비활성화되고 로딩 스피너가 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "refresh button loading state" + } + ], + "expected": { + "buttonDisabled": true, + "loadingSpinner": "Loader2 spinner", + "iconChanged": "RefreshCw → Loader2" + } + }, + { + "id": "step-26", + "name": "새로고침 후 API 호출 확인", + "description": "새로고침 버튼 클릭 후 모든 API가 다시 호출되는지 확인", + "actions": [ + { + "type": "verify", + "target": "API calls after refresh" + } + ], + "expected": { + "apiCalls": [ + "GET /api/v1/daily-report/note-receivables", + "GET /api/v1/daily-report/daily-accounts", + "GET /api/v1/daily-report/summary" + ] + } + }, + { + "id": "step-27", + "name": "새로고침 후 데이터 표시 확인", + "description": "새로고침 후 두 테이블 모두 데이터가 정상적으로 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "both tables data after refresh" + } + ], + "expected": { + "noteReceivablesTableUpdated": true, + "dailyAccountsTableUpdated": true, + "noError": "에러 없이 정상 동작" + } + }, + { + "id": "step-28", + "name": "엑셀 다운로드 버튼 존재 확인", + "description": "엑셀 다운로드 버튼이 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "excel download button" + } + ], + "expected": { + "buttonExists": true, + "buttonText": "엑셀 다운로드", + "icon": "Download" + } + }, + { + "id": "step-29", + "name": "엑셀 다운로드 버튼 클릭 전 Network 상태 확인", + "description": "다운로드 전 Network Request 기록", + "actions": [ + { + "type": "verify", + "target": "network state before download" + } + ], + "expected": { + "networkRecorded": true + } + }, + { + "id": "step-30", + "name": "엑셀 다운로드 버튼 클릭", + "description": "엑셀 다운로드 버튼 클릭 후 다운로드 처리 확인", + "actions": [ + { + "type": "click", + "target": "엑셀 다운로드 버튼" + }, + { + "type": "wait", + "target": "다운로드 처리" + } + ], + "expected": { + "buttonClicked": true, + "downloadInitiated": true + } + }, + { + "id": "step-31", + "name": "엑셀 다운로드 API 호출 확인", + "description": "다운로드 버튼 클릭 후 Export API가 호출되는지 확인", + "actions": [ + { + "type": "verify", + "target": "export API call" + } + ], + "expected": { + "apiCall": "GET /api/v1/daily-report/export?date=2026-01-15", + "apiResponse": "200 OK", + "contentType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" + } + }, + { + "id": "step-32", + "name": "엑셀 다운로드 이벤트 확인", + "description": "실제 파일 다운로드 이벤트가 발생하는지 확인", + "actions": [ + { + "type": "verify", + "target": "download event" + } + ], + "expected": { + "downloadEvent": "발생", + "fileFormat": "xlsx 또는 xls", + "filenamePattern": "daily_report_*.xlsx" + } + }, + { + "id": "step-33", + "name": "엑셀 다운로드 성공 토스트 확인", + "description": "다운로드 성공 시 토스트 메시지가 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "success toast message" + } + ], + "expected": { + "toastMessage": "엑셀 다운로드가 완료되었습니다.", + "toastType": "success" + } + }, + { + "id": "step-34", + "name": "어음 데이터 내용 형식 확인", + "description": "어음 테이블의 '내용' 컬럼 형식이 올바른지 확인", + "actions": [ + { + "type": "verify", + "target": "note receivable content format" + } + ], + "expected": { + "contentFormat": "(수취어음) 거래처명 - 어음번호", + "exampleFormat": "(수취어음) ABC상사 - N2026-001" + } + }, + { + "id": "step-35", + "name": "어음 현재 잔액 표시 확인", + "description": "어음 테이블의 현재 잔액이 올바른 형식으로 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "current balance format" + } + ], + "expected": { + "formatType": "통화 형식 (₩1,000,000)", + "alignment": "right-aligned" + } + }, + { + "id": "step-36", + "name": "어음 발행일/만기일 형식 확인", + "description": "날짜 형식이 올바르게 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "date format in note table" + } + ], + "expected": { + "dateFormat": "YYYY-MM-DD 또는 YYYY.MM.DD", + "exampleFormat": "2026-01-15 또는 2026.01.15" + } + }, + { + "id": "step-37", + "name": "일자별 상세 구분 컬럼 형식 확인", + "description": "계좌 '구분' 컬럼이 올바른 형식으로 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "account category format" + } + ], + "expected": { + "categoryFormat": "은행명 계좌번호 축약", + "exampleFormat": "국민은행 ***1234" + } + }, + { + "id": "step-38", + "name": "일자별 상세 금액 컬럼 정렬 확인", + "description": "전월 이월, 수입, 지출, 잔액 컬럼이 우측 정렬되는지 확인", + "actions": [ + { + "type": "verify", + "target": "amount columns alignment" + } + ], + "expected": { + "alignment": "right-aligned", + "columns": ["전월 이월", "수입", "지출", "잔액"] + } + }, + { + "id": "step-39", + "name": "일자별 상세 금액 형식 확인", + "description": "금액이 통화 형식으로 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "amount format" + } + ], + "expected": { + "formatType": "통화 형식 (₩1,000,000 또는 $1,000.00)", + "thousandsSeparator": "쉼표(,) 사용" + } + }, + { + "id": "step-40", + "name": "어음 테이블 빈 데이터 처리 확인", + "description": "어음 데이터가 없을 때 적절한 메시지가 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "empty state message for note table" + } + ], + "expected": { + "emptyMessage": "데이터가 없습니다.", + "messagePosition": "table center" + } + }, + { + "id": "step-41", + "name": "일자별 상세 테이블 빈 데이터 처리 확인", + "description": "계좌 데이터가 없을 때 적절한 메시지가 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "empty state message for daily accounts table" + } + ], + "expected": { + "emptyMessage": "데이터가 없습니다.", + "messagePosition": "table center" + } + }, + { + "id": "step-42", + "name": "매칭 상태 필터링 확인", + "description": "매칭/비매칭 상태에 따라 데이터가 올바르게 분류되는지 확인", + "actions": [ + { + "type": "verify", + "target": "match status filtering" + } + ], + "expected": { + "matchedItems": "매칭 Badge 표시", + "unmatchedItems": "비매칭 Badge 표시", + "correctColors": "매칭=green, 비매칭=orange" + } + }, + { + "id": "step-43", + "name": "KRW/USD 계좌 분리 확인", + "description": "원화와 외화 계좌가 올바르게 분리되어 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "currency separation" + } + ], + "expected": { + "krwAccountsFirst": "KRW 계좌 먼저 표시", + "usdAccountsAfter": "USD 계좌 나중에 표시", + "separateGroups": "통화별 그룹화" + } + }, + { + "id": "step-44", + "name": "외화원(USD) 합계 계산 확인", + "description": "외화원 합계가 USD 계좌만 합산하는지 확인", + "actions": [ + { + "type": "verify", + "target": "USD total calculation" + } + ], + "expected": { + "includesOnlyUSD": "USD 계좌만 합산", + "excludesKRW": "KRW 계좌 제외", + "correctTotal": "USD 계좌 잔액 합계" + } + }, + { + "id": "step-45", + "name": "현금성 자산 합계 계산 확인", + "description": "현금성 자산 합계가 전체 계좌를 합산하는지 확인", + "actions": [ + { + "type": "verify", + "target": "cash asset total calculation" + } + ], + "expected": { + "includesAllAccounts": "KRW + USD 계좌 전체 합산", + "correctTotal": "전체 계좌 잔액 합계" + } + }, + { + "id": "step-46", + "name": "어음 합계 계산 정확성 확인", + "description": "어음 테이블 합계가 현재 잔액의 정확한 합계인지 확인", + "actions": [ + { + "type": "verify", + "target": "note receivables total accuracy" + } + ], + "expected": { + "correctCalculation": "모든 어음 현재 잔액의 합계", + "matchesData": "개별 행 합계와 일치" + } + }, + { + "id": "step-47", + "name": "페이지 반응형 동작 확인", + "description": "브라우저 크기 조절 시 테이블이 올바르게 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "responsive behavior" + } + ], + "expected": { + "tableScrollable": "테이블 스크롤 가능", + "layoutPreserved": "레이아웃 유지" + } + }, + { + "id": "step-48", + "name": "날짜 선택 필드 제약 확인", + "description": "날짜 입력 필드에 유효하지 않은 날짜를 입력할 수 없는지 확인", + "actions": [ + { + "type": "verify", + "target": "date input validation" + } + ], + "expected": { + "inputType": "date", + "validationExists": "브라우저 기본 날짜 검증" + } + }, + { + "id": "step-49", + "name": "콘솔 에러 확인", + "description": "페이지 동작 중 콘솔에 에러가 발생하지 않는지 확인", + "actions": [ + { + "type": "verify", + "target": "console errors" + } + ], + "expected": { + "noErrors": "콘솔 에러 없음", + "warningsAcceptable": "경고는 허용" + } + }, + { + "id": "step-50", + "name": "전체 페이지 기능 통합 테스트", + "description": "날짜 변경, 새로고침, 다운로드를 순차적으로 실행하여 모든 기능이 정상 동작하는지 확인", + "actions": [ + { + "type": "type", + "target": "date input", + "value": "2026-01-12" + }, + { + "type": "wait", + "target": "데이터 로드" + }, + { + "type": "click", + "target": "새로고침 버튼" + }, + { + "type": "wait", + "target": "데이터 리로드" + }, + { + "type": "click", + "target": "엑셀 다운로드 버튼" + }, + { + "type": "wait", + "target": "다운로드 완료" + } + ], + "expected": { + "allFunctionsWork": "모든 기능 정상 동작", + "dataConsistency": "데이터 일관성 유지", + "noErrors": "에러 없이 완료" + } + } + ], + "cleanup": { + "description": "테스트 후 날짜를 오늘로 복원 (필요시)", + "actions": [] + }, + "notes": [ + "날짜 선택에 따른 데이터 필터링 정상 동작 확인", + "두 개의 테이블(어음, 일자별 상세) 모두 데이터 표시 확인", + "KRW/USD 계좌 분리 및 합계 계산 정확성 확인", + "매칭/비매칭 상태 Badge 표시 확인", + "새로고침 버튼 동작 및 로딩 상태 확인", + "엑셀 다운로드는 Network Request 및 실제 다운로드 이벤트 확인 필수", + "모든 금액 표시 형식 및 정렬 확인", + "빈 데이터 처리 및 로딩 상태 표시 확인" + ] +} diff --git a/department-add.json b/department-add.json new file mode 100644 index 0000000..ff38e26 --- /dev/null +++ b/department-add.json @@ -0,0 +1,487 @@ +{ + "id": "department-add", + "name": "부서 추가 테스트 (랜덤 + 하위부서)", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "랜덤 상위 부서 생성 후 하위 부서까지 추가하는 고도화된 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/ko/hr/department-management", + "menuNavigation": { + "level1": "인사관리", + "level2": "부서관리", + "expectedUrl": "/ko/hr/department-management" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "scrollContainer": ".sidebar-scroll, [data-sidebar], nav, aside", + "scrollStep": 200, + "maxScrollAttempts": 10, + "waitAfterScroll": 300, + "level1": { + "text": "인사관리", + "fallbackSelectors": [ + "text=인사관리", + "[data-menu='hr']", + "a:has-text('인사관리')", + "button:has-text('인사관리')" + ] + }, + "level2": { + "text": "부서관리", + "fallbackSelectors": [ + "text=부서관리", + "[data-menu='department']", + "a:has-text('부서관리')", + "button:has-text('부서관리')" + ] + }, + "expectedUrl": "/ko/hr/department-management", + "fallbackUrl": "https://dev.codebridge-x.com/ko/hr/department-management" + }, + "timeout": 90000, + "tags": ["hr", "department", "crud", "random", "hierarchy"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "randomData": { + "parentDepartment": { + "type": "composite", + "pattern": "{prefix}본부_{timestamp}", + "components": { + "prefix": { + "type": "random", + "options": ["신규", "테스트", "개발", "QA", "운영", "전략", "혁신", "글로벌"] + } + } + }, + "childDepartment": { + "type": "composite", + "pattern": "{prefix}팀_{timestamp}", + "components": { + "prefix": { + "type": "random", + "options": ["기획", "개발", "디자인", "마케팅", "영업", "지원", "품질", "연구"] + } + } + } + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "사이드바 스크롤을 최상단으로 초기화하고 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll, [data-sidebar], nav, aside')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 500 } + ], + "expect": { + "sidebarReady": true + } + }, + { + "id": "step-1", + "name": "인사관리 메뉴 진입", + "description": "인사관리 > 부서관리 메뉴로 이동 (scrollAndFind 패턴 사용)", + "navigationPattern": "scrollAndFind", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll, [data-sidebar], nav, aside", + "target": "인사관리", + "scrollStep": 200, + "maxAttempts": 10, + "waitAfterScroll": 300 + }, + { "type": "click", "target": "인사관리" }, + { "type": "wait", "duration": 500 }, + { + "type": "scrollAndFind", + "container": ".sidebar-scroll, [data-sidebar], nav, aside", + "target": "부서관리", + "scrollStep": 200, + "maxAttempts": 10, + "waitAfterScroll": 300 + }, + { "type": "click", "target": "부서관리" } + ], + "fallback": { + "type": "navigate", + "url": "https://dev.codebridge-x.com/ko/hr/department-management" + }, + "expect": { + "url": "/hr/department-management", + "visible": ["부서관리", "추가"] + } + }, + { + "id": "step-2", + "name": "현재 부서 개수 저장", + "description": "테스트 전 부서 개수 기록", + "capture": { + "variable": "initialCount", + "selector": "총 *건", + "extract": "number" + } + }, + { + "id": "step-3", + "name": "상위 부서 추가 모달 열기", + "description": "추가 버튼 클릭하여 부서 추가 모달 열기", + "actions": [ + { "type": "openModal", "target": "추가", "description": "부서 추가 모달 열기" } + ], + "modalConfig": { + "containerSelector": "[role='dialog'], .modal", + "animationDelay": 300, + "waitForSelector": "[role='dialog']" + }, + "expect": { + "modal": "부서 추가", + "visible": ["부서명", "등록", "취소"] + } + }, + { + "id": "step-4", + "name": "랜덤 상위 부서명 입력", + "description": "모달 내 부서명 입력", + "actions": [ + { + "type": "fillInModal", + "target": "부서명", + "value": "{randomData.parentDepartment}", + "options": { "waitAfter": 100 } + } + ], + "expect": { + "buttonEnabled": "등록" + } + }, + { + "id": "step-5", + "name": "상위 부서 등록", + "description": "모달 내 등록 버튼 클릭하여 상위 부서 추가 완료", + "actions": [ + { "type": "clickInModal", "target": "등록", "options": { "waitAfter": 500 } } + ], + "waitFor": { + "type": "modalClose", + "timeout": 5000 + }, + "expect": { + "toast": ["등록", "완료", "성공"], + "visible": ["{randomData.parentDepartment}"] + } + }, + { + "id": "step-6", + "name": "상위 부서 등록 확인", + "description": "목록에서 새로 추가된 상위 부서 확인", + "verify": { + "listContains": "{randomData.parentDepartment}", + "countIncreased": "{initialCount} + 1" + } + }, + { + "id": "step-7", + "name": "하위 부서 추가 버튼 클릭", + "description": "생성된 상위 부서의 '하위 부서 추가' 버튼 클릭", + "actions": [ + { + "type": "findRow", + "contains": "{randomData.parentDepartment}", + "then": { + "type": "click", + "target": "하위 부서 추가", + "tooltip": true + } + } + ], + "expect": { + "modal": "하위 부서 추가", + "visible": ["부서명", "상위 부서", "{randomData.parentDepartment}"] + } + }, + { + "id": "step-8", + "name": "랜덤 하위 부서명 입력", + "description": "모달 내 하위 부서명 입력", + "actions": [ + { + "type": "fillInModal", + "target": "부서명", + "value": "{randomData.childDepartment}", + "options": { "waitAfter": 100 } + } + ], + "expect": { + "buttonEnabled": "등록" + } + }, + { + "id": "step-9", + "name": "하위 부서 등록", + "description": "모달 내 등록 버튼 클릭하여 하위 부서 추가 완료", + "actions": [ + { "type": "clickInModal", "target": "등록", "options": { "waitAfter": 500 } } + ], + "waitFor": { + "type": "modalClose", + "timeout": 5000 + }, + "expect": { + "toast": ["등록", "완료", "성공"], + "visible": ["{randomData.childDepartment}"] + } + }, + { + "id": "step-10", + "name": "계층 구조 확인", + "description": "상위 부서 확장하여 하위 부서가 트리 구조로 표시되는지 확인", + "actions": [ + { + "type": "findRow", + "contains": "{randomData.parentDepartment}", + "then": { + "type": "click", + "target": "expand", + "description": "트리 확장 버튼 클릭" + } + } + ], + "verify": { + "hierarchy": { + "parent": "{randomData.parentDepartment}", + "children": ["{randomData.childDepartment}"] + }, + "totalCountIncreased": "{initialCount} + 2" + } + }, + { + "id": "step-11", + "name": "하위 부서 수정 모달 열기", + "description": "하위 부서의 수정 버튼 클릭", + "actions": [ + { + "type": "findRow", + "contains": "{randomData.childDepartment}", + "then": { + "type": "click", + "target": "수정", + "tooltip": true, + "description": "수정 아이콘 버튼 클릭" + } + } + ], + "expect": { + "modal": "부서 수정", + "visible": ["부서명", "상위 부서", "저장", "취소"] + } + }, + { + "id": "step-12", + "name": "하위 부서명 수정", + "description": "모달 내 하위 부서명 변경", + "actions": [ + { + "type": "fillInModal", + "target": "부서명", + "value": "{randomData.childDepartment}_수정됨", + "options": { "waitAfter": 100 } + } + ], + "expect": { + "buttonEnabled": "저장" + } + }, + { + "id": "step-13", + "name": "부서 수정 저장", + "description": "모달 내 저장 버튼 클릭하여 부서 수정 완료", + "actions": [ + { "type": "clickInModal", "target": "저장", "options": { "waitAfter": 500 } } + ], + "waitFor": { + "type": "modalClose", + "timeout": 5000 + }, + "expect": { + "toast": ["수정", "완료", "성공", "저장"], + "visible": ["{randomData.childDepartment}_수정됨"] + } + }, + { + "id": "step-13-1", + "name": "⚠️ 필수 검증: 수정 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", + "description": "목록에서 수정된 부서명 확인", + "verify": { + "listContains": "{randomData.childDepartment}_수정됨" + } + }, + { + "id": "step-14", + "name": "하위 부서 삭제", + "description": "하위 부서의 삭제 버튼 클릭", + "actions": [ + { + "type": "findRow", + "contains": "{randomData.childDepartment}_수정됨", + "then": { + "type": "click", + "target": "삭제", + "tooltip": true, + "description": "삭제 아이콘 버튼 클릭" + } + } + ], + "expect": { + "confirmDialog": true, + "dialogText": ["삭제", "하시겠습니까"] + } + }, + { + "id": "step-15", + "name": "하위 부서 삭제 확인", + "description": "삭제 확인 다이얼로그에서 확인 클릭", + "actions": [ + { "type": "click", "target": "확인", "description": "삭제 확인" } + ], + "waitFor": { + "type": "apiResponse", + "method": "DELETE", + "timeout": 5000 + }, + "expect": { + "toast": ["삭제", "완료", "성공"] + } + }, + { + "id": "step-15-1", + "name": "⚠️ 필수 검증: 하위 부서 삭제 반영 확인", + "critical": true, + "description": "목록에서 삭제된 하위 부서가 없어졌는지 확인", + "verify": { + "listNotContains": "{randomData.childDepartment}_수정됨" + } + }, + { + "id": "step-16", + "name": "상위 부서 삭제", + "description": "상위 부서의 삭제 버튼 클릭", + "actions": [ + { + "type": "findRow", + "contains": "{randomData.parentDepartment}", + "then": { + "type": "click", + "target": "삭제", + "tooltip": true, + "description": "삭제 아이콘 버튼 클릭" + } + } + ], + "expect": { + "confirmDialog": true, + "dialogText": ["삭제", "하시겠습니까"] + } + }, + { + "id": "step-17", + "name": "상위 부서 삭제 확인", + "description": "삭제 확인 다이얼로그에서 확인 클릭", + "actions": [ + { "type": "click", "target": "확인", "description": "삭제 확인" } + ], + "waitFor": { + "type": "apiResponse", + "method": "DELETE", + "timeout": 5000 + }, + "expect": { + "toast": ["삭제", "완료", "성공"] + } + }, + { + "id": "step-18", + "name": "⚠️ 필수 검증: 상위 부서 삭제 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 삭제 확인 필수!", + "description": "목록에서 삭제된 상위 부서가 없어졌는지 확인", + "verify": { + "listNotContains": "{randomData.parentDepartment}", + "countRestored": "{initialCount}" + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/hr/department-management", + "message": "부서관리 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "text={randomData.parentDepartment}", + "message": "상위 부서가 목록에 표시되어야 함" + }, + { + "type": "elementExists", + "selector": "text={randomData.childDepartment}", + "message": "하위 부서가 목록에 표시되어야 함" + }, + { + "type": "hierarchy", + "parent": "{randomData.parentDepartment}", + "child": "{randomData.childDepartment}", + "message": "하위 부서가 상위 부서 아래에 트리 구조로 표시되어야 함" + } + ], + + "cleanup": { + "enabled": false, + "description": "테스트 후 생성된 부서 삭제 (필요시 활성화)", + "order": "childFirst", + "steps": [ + { + "action": "delete", + "target": "{randomData.childDepartment}", + "description": "하위 부서 먼저 삭제" + }, + { + "action": "delete", + "target": "{randomData.parentDepartment}", + "description": "상위 부서 삭제" + } + ] + }, + + "notes": { + "testScope": "상위 부서 생성 → 하위 부서 추가 → 계층 구조 검증까지 전체 플로우 테스트", + "randomGeneration": { + "parent": "prefix(신규,테스트,개발 등) + '본부' + timestamp", + "child": "prefix(기획,개발,디자인 등) + '팀' + timestamp" + }, + "duplicateHandling": "timestamp 포함으로 중복 방지", + "prerequisites": "로그인된 사용자에게 부서 추가 권한 필요", + "uiElements": { + "addButton": "상단 '추가' 버튼 - 최상위 부서 추가", + "subAddButton": "각 행의 '하위 부서 추가' 아이콘 버튼", + "expandButton": "트리 구조 확장/축소 버튼" + } + } +} diff --git a/deposit-management.json b/deposit-management.json new file mode 100644 index 0000000..39957f2 --- /dev/null +++ b/deposit-management.json @@ -0,0 +1,466 @@ +{ + "id": "deposit-management", + "name": "입금관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "입금관리 목록 조회, 계정과목명 일괄변경, 상세 수정 기능 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/ko/accounting/deposits", + "menuNavigation": { + "level1": "회계관리", + "level2": "입금관리", + "expectedUrl": "/ko/accounting/deposits" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", + "level1": "회계관리", + "level2": "입금관리", + "alternativeLevel1Names": ["회계관리", "회계 관리", "Accounting"], + "alternativeLevel2Names": ["입금관리", "입금 관리", "Deposits"], + "scrollConfig": { + "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", + "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + } + }, + "timeout": 60000, + "tags": ["accounting", "deposit", "crud"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 메뉴 탐색 준비", + "description": "사이드바를 최상단으로 스크롤하고 메뉴 구조 파악", + "actions": [ + { "type": "scroll", "target": "sidebar", "direction": "top", "description": "사이드바 최상단으로 스크롤" }, + { "type": "wait", "duration": 500 } + ] + }, + { + "id": "step-1", + "name": "2단계 메뉴 진입: 회계관리 > 입금관리", + "description": "사이드바를 스크롤하며 회계관리 > 입금관리 메뉴를 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "회계관리", + "alternativeTexts": ["회계관리", "회계 관리", "Accounting"], + "scrollContainer": "sidebar", + "maxAttempts": 10, + "description": "스크롤하며 회계관리 메뉴 찾기" + }, + { "type": "click", "target": "회계관리", "description": "회계관리 메뉴 클릭" }, + { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, + { + "type": "scrollAndFind", + "target": "입금관리", + "alternativeTexts": ["입금관리", "입금 관리", "Deposits"], + "scrollContainer": "submenu", + "maxAttempts": 5, + "description": "서브메뉴에서 입금관리 찾기" + }, + { "type": "click", "target": "입금관리", "description": "입금관리 메뉴 클릭" }, + { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 } + ], + "expect": { + "url": "/accounting/deposits", + "visible": ["입금관리", "총 입금"] + }, + "verification": [ + "회계관리 메뉴가 펼쳐졌는지 확인", + "입금관리 서브메뉴 클릭 성공", + "404 에러 없이 페이지 로드 완료" + ] + }, + { + "id": "step-2", + "name": "목록 페이지 구조 확인", + "description": "테이블 및 필터 요소 확인", + "expect": { + "visible": ["입금일", "입금계좌", "입금자명", "입금금액", "거래처", "적요", "입금유형"], + "elements": { + "statisticsCards": ["총 입금", "당월 입금", "거래처 미설정", "입금유형 미설정"], + "filters": ["계정과목명", "저장", "새로고침"], + "pagination": true + } + } + }, + { + "id": "step-3", + "name": "계정과목명 드롭다운 옵션 확인", + "description": "계정과목명 일괄변경 드롭다운 옵션 검증", + "actions": [ + { "type": "click", "target": "계정과목명 드롭다운", "description": "드롭다운 열기" } + ], + "expect": { + "options": ["미설정", "매출대금", "선수금", "가수금", "임대수익", "이자수익", "보증금 반환", "차입금", "자본금", "부가세 환급", "기타"] + } + }, + { + "id": "step-4", + "name": "체크박스 선택 후 계정과목명 일괄변경", + "description": "테이블 행 선택 후 계정과목명 일괄변경 저장", + "actions": [ + { "type": "click", "target": "첫 번째 행 체크박스", "description": "행 선택" }, + { "type": "click", "target": "계정과목명 드롭다운", "description": "드롭다운 열기" }, + { "type": "click", "target": "매출대금", "description": "매출대금 선택" }, + { "type": "click", "target": "저장", "description": "저장 버튼 클릭" } + ], + "expect": { + "dialog": "확인 다이얼로그 표시", + "toast": "변경 완료 메시지" + } + }, + { + "id": "step-4-1", + "name": "⚠️ 필수 검증: 계정과목명 변경 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", + "description": "저장 후 테이블에서 변경된 입금유형 값 확인", + "expect": { + "tableCell": { + "row": 1, + "column": "입금유형", + "value": "매출대금" + } + }, + "knownBugReference": "BUG-SALES-20260115-001 (매출관리 동일 버그 확인 필요)" + }, + { + "id": "step-5", + "name": "입금 상세 페이지 이동", + "description": "테이블 행 클릭하여 상세 페이지로 이동", + "actions": [ + { "type": "click", "target": "테이블 첫 번째 행", "description": "행 클릭 (체크박스 제외 영역)" } + ], + "expect": { + "url": "/accounting/deposits/{id}", + "visible": ["입금 상세", "기본 정보", "목록", "삭제", "수정"] + } + }, + { + "id": "step-6", + "name": "상세 페이지 읽기 모드 필드 확인", + "description": "수정 전 필드들이 비활성화 상태인지 확인", + "expect": { + "fields": [ + { "name": "입금일", "disabled": true }, + { "name": "입금계좌", "disabled": true }, + { "name": "입금자명", "disabled": true }, + { "name": "입금금액", "disabled": true }, + { "name": "적요", "disabled": true }, + { "name": "거래처", "disabled": true }, + { "name": "입금 유형", "disabled": true } + ] + } + }, + { + "id": "step-7", + "name": "수정 모드 전환", + "description": "수정 버튼 클릭하여 편집 모드로 전환", + "click": "수정", + "expect": { + "url": "/accounting/deposits/{id}?mode=edit", + "visible": ["입금 수정", "취소", "저장"], + "notVisible": ["목록", "삭제", "수정"] + } + }, + { + "id": "step-8", + "name": "수정 모드 필드 활성화 검증", + "description": "수정 가능한 필드와 불가능한 필드 확인", + "expect": { + "fields": [ + { "name": "입금일", "disabled": true, "note": "은행데이터 - 수정 불가" }, + { "name": "입금계좌", "disabled": true, "note": "은행데이터 - 수정 불가" }, + { "name": "입금자명", "disabled": true, "note": "은행데이터 - 수정 불가" }, + { "name": "입금금액", "disabled": true, "note": "은행데이터 - 수정 불가" }, + { "name": "적요", "disabled": false, "editable": true }, + { "name": "거래처", "disabled": false, "type": "combobox", "editable": true }, + { "name": "입금 유형", "disabled": false, "type": "combobox", "editable": true } + ] + } + }, + { + "id": "step-9", + "name": "거래처 드롭다운 옵션 확인", + "description": "거래처 선택 드롭다운 옵션 검증", + "actions": [ + { "type": "click", "target": "거래처 드롭다운", "description": "드롭다운 열기" } + ], + "expect": { + "options": ["거래처테스트", "아크더레드", "코브라브릿지", "가우스전자", "아크아크"] + } + }, + { + "id": "step-10", + "name": "입금 유형 드롭다운 옵션 확인", + "description": "입금 유형 선택 드롭다운 옵션 검증", + "actions": [ + { "type": "click", "target": "입금 유형 드롭다운", "description": "드롭다운 열기" } + ], + "expect": { + "options": ["미설정", "매출대금", "선수금", "가수금", "임대수익", "이자수익", "보증금 반환", "차입금", "자본금", "부가세 환급", "기타"] + } + }, + { + "id": "step-11", + "name": "수정 데이터 입력", + "description": "수정 가능한 필드에 테스트 데이터 입력", + "form": { + "fields": [ + { "name": "적요", "type": "text", "value": "테스트 적요 수정" } + ] + }, + "actions": [ + { "type": "click", "target": "거래처 드롭다운", "description": "거래처 드롭다운 열기" }, + { "type": "click", "target": "거래처테스트", "description": "거래처 선택" }, + { "type": "click", "target": "입금 유형 드롭다운", "description": "입금 유형 드롭다운 열기" }, + { "type": "click", "target": "매출대금", "description": "매출대금 선택" } + ] + }, + { + "id": "step-12", + "name": "저장 및 결과 확인", + "description": "저장 버튼 클릭 후 데이터 반영 확인", + "click": "저장", + "expect": { + "toast": "저장 완료 메시지", + "url": "/accounting/deposits/{id}", + "mode": "view" + } + }, + { + "id": "step-12-1", + "name": "⚠️ 필수 검증: 수정 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", + "description": "저장 후 상세 페이지에서 변경된 값 확인", + "expect": { + "fields": [ + { "name": "적요", "value": "테스트 적요 수정" }, + { "name": "거래처", "value": "거래처테스트" }, + { "name": "입금 유형", "value": "매출대금" } + ] + } + }, + { + "id": "step-13", + "name": "취소 버튼 동작 확인", + "description": "수정 모드에서 취소 버튼 동작 검증", + "actions": [ + { "type": "click", "target": "수정", "description": "수정 모드 진입" }, + { "type": "click", "target": "취소", "description": "취소 버튼 클릭" } + ], + "expect": { + "url": "/accounting/deposits/{id}", + "mode": "view", + "visible": ["입금 상세", "목록", "삭제", "수정"] + } + }, + { + "id": "step-14", + "name": "목록 버튼 동작 확인", + "description": "목록 버튼 클릭하여 목록 페이지로 이동", + "click": "목록", + "expect": { + "url": "/accounting/deposits", + "visible": ["입금관리", "총 입금"] + } + }, + { + "id": "step-15", + "name": "필터 드롭다운 검증", + "description": "목록 페이지 필터 드롭다운 옵션 확인", + "note": "3개의 필터 드롭다운 존재 (거래처, 입금유형, 정렬)", + "expect": { + "filters": [ + { "name": "거래처 필터", "default": "전체" }, + { "name": "입금유형 필터", "default": "전체" }, + { "name": "정렬", "default": "최신순", "options": ["최신순", "등록순", "금액 높은순", "금액 낮은순"] } + ] + } + }, + { + "id": "step-16", + "name": "날짜 필터 검증", + "description": "날짜 필터 버튼 동작 확인", + "actions": [ + { "type": "click", "target": "당해년도", "description": "당해년도 버튼 클릭" } + ], + "expect": { + "dateRange": { + "start": "2026-01-01", + "end": "2026-12-31" + } + } + }, + { + "id": "step-17", + "name": "페이지네이션 동작 확인", + "description": "페이지네이션 버튼 동작 검증", + "expect": { + "pagination": { + "totalItems": 60, + "itemsPerPage": 20, + "currentPage": 1, + "totalPages": 3 + } + }, + "actions": [ + { "type": "click", "target": "다음", "description": "다음 페이지로 이동" } + ], + "expectAfterAction": { + "currentPage": 2, + "displayText": "전체 60개 중 21-40개 표시" + } + } + ], + + "deleteTest": { + "note": "삭제 테스트 - 이전에 스킵되었으나 CRUD 완전성을 위해 추가", + "steps": [ + { + "id": "step-delete-1", + "name": "삭제 버튼 클릭", + "description": "상세 페이지에서 삭제 버튼 클릭", + "actions": [ + { "type": "click", "target": "삭제" } + ], + "expect": { + "confirmDialog": true, + "dialogText": ["삭제", "하시겠습니까"] + } + }, + { + "id": "step-delete-2", + "name": "삭제 확인", + "description": "삭제 확인 다이얼로그에서 확인 클릭", + "actions": [ + { "type": "click", "target": "확인", "description": "삭제 확인" } + ], + "waitFor": { + "type": "navigation", + "url": "/accounting/deposits", + "timeout": 5000 + }, + "expect": { + "toast": ["삭제", "완료", "성공"], + "url": "/accounting/deposits" + } + }, + { + "id": "step-delete-3", + "name": "⚠️ 필수 검증: 삭제 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 삭제 확인 필수!", + "description": "목록에서 삭제된 입금 내역이 없어졌는지 확인", + "verify": { + "tableNotContains": "테스트 적요 수정" + } + } + ] + }, + + "knownBugs": [ + { + "id": "BUG-SALES-20260115-001", + "description": "계정과목명 일괄변경 시 토스트 성공 표시되나 실제 데이터 미변경 (매출관리)", + "relatedSteps": ["step-4-1"], + "note": "입금관리에서도 동일한 버그가 존재할 수 있으므로 step-4-1에서 검증 필수" + } + ], + + "testData": { + "sampleDeposit": { + "date": "2025-12-28", + "account": "운영계좌", + "depositor": "CJ대한통운", + "amount": "8,209,677", + "vendor": "CJ대한통운", + "description": "CJ대한통운 입금", + "depositType": "미설정" + }, + "modifiedData": { + "description": "테스트 적요 수정", + "vendor": "거래처테스트", + "depositType": "매출대금" + } + }, + + "pageStructure": { + "listPage": { + "url": "/accounting/deposits", + "title": "입금관리", + "statistics": ["총 입금", "당월 입금", "거래처 미설정", "입금유형 미설정"], + "tableColumns": ["checkbox", "입금일", "입금계좌", "입금자명", "입금금액", "거래처", "적요", "입금유형", "action"], + "batchUpdate": { + "label": "계정과목명", + "saveButton": "저장" + }, + "filters": ["거래처", "입금유형", "정렬"], + "dateFilters": ["당해년도", "전전월", "전월", "당월", "어제", "오늘"] + }, + "detailPage": { + "url": "/accounting/deposits/{id}", + "title": "입금 상세", + "buttons": ["목록", "삭제", "수정"], + "fields": { + "readOnly": ["입금일", "입금계좌", "입금자명", "입금금액"], + "editable": ["적요", "거래처", "입금 유형"] + } + }, + "editPage": { + "url": "/accounting/deposits/{id}?mode=edit", + "title": "입금 수정", + "buttons": ["취소", "저장"] + } + }, + + "dropdownOptions": { + "accountSubject": { + "label": "계정과목명", + "options": ["미설정", "매출대금", "선수금", "가수금", "임대수익", "이자수익", "보증금 반환", "차입금", "자본금", "부가세 환급", "기타"] + }, + "depositType": { + "label": "입금 유형", + "options": ["미설정", "매출대금", "선수금", "가수금", "임대수익", "이자수익", "보증금 반환", "차입금", "자본금", "부가세 환급", "기타"] + }, + "vendor": { + "label": "거래처", + "options": ["거래처테스트", "아크더레드", "코브라브릿지", "가우스전자", "아크아크"] + }, + "sortOrder": { + "label": "정렬", + "options": ["최신순", "등록순", "금액 높은순", "금액 낮은순"] + } + }, + + "assertions": [ + { + "type": "url", + "expected": "/accounting/deposits", + "message": "목록 페이지 URL 확인" + }, + { + "type": "text", + "target": "body", + "expected": "입금관리", + "message": "페이지 타이틀 확인" + } + ] +} diff --git a/draft-box.json b/draft-box.json new file mode 100644 index 0000000..8b63e64 --- /dev/null +++ b/draft-box.json @@ -0,0 +1,1152 @@ +{ + "id": "draft-box", + "name": "기안함 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "결재관리 > 기안함 메뉴의 문서 목록 조회, 검색, 필터, 정렬, 문서 상세, 상신, 삭제 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "testFocus": { + "primary": "기안 문서 목록 관리 및 결재 상신 프로세스 검증", + "description": "기안함 목록 표시, 통계 카드, 검색/필터/정렬, 체크박스 선택, 상신/삭제 버튼, 문서 상세 모달, 페이지네이션 동작 확인" + }, + "menuNavigation": { + "level1": "결재관리", + "level2": "기안함", + "expectedUrl": "/ko/approval/draft" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", + "level1": "결재관리", + "level2": "기안함", + "alternativeLevel1Names": ["결재관리", "결재 관리", "Approval", "전자결재"], + "alternativeLevel2Names": ["기안함", "기안 함", "Draft", "기안문서", "내 기안"], + "scrollConfig": { + "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", + "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + } + }, + "prerequisites": { + "authentication": true, + "testData": { + "description": "결재 문서 데이터가 최소 1개 이상 존재해야 함 (다양한 상태: 임시저장, 결재대기, 진행중, 완료, 반려)" + } + }, + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/approvals/drafts", + "params": "page=1&per_page=20&sort_by=created_at&sort_dir=desc", + "description": "기안함 목록 조회 (페이지네이션, 검색, 필터, 정렬)" + }, + { + "method": "GET", + "endpoint": "/api/v1/approvals/drafts/summary", + "params": "", + "description": "기안함 통계 카드 (전체, 진행, 완료, 반려, 임시저장 건수)" + }, + { + "method": "GET", + "endpoint": "/api/v1/approvals/{id}", + "params": "", + "description": "결재 문서 상세 조회 (content 포함)" + }, + { + "method": "POST", + "endpoint": "/api/v1/approvals/{id}/submit", + "params": "", + "description": "결재 상신" + }, + { + "method": "DELETE", + "endpoint": "/api/v1/approvals/{id}", + "params": "", + "description": "결재 문서 삭제 (임시저장 상태만)" + } + ], + "steps": [ + { + "id": "step-0", + "name": "사이드바 메뉴 탐색 준비", + "description": "사이드바를 최상단으로 스크롤하고 메뉴 구조 파악", + "actions": [ + { "type": "scroll", "target": "sidebar", "direction": "top", "description": "사이드바 최상단으로 스크롤" }, + { "type": "wait", "duration": 500 } + ] + }, + { + "id": "step-1", + "name": "2단계 메뉴 진입: 결재관리 > 기안함", + "description": "사이드바를 스크롤하며 결재관리 > 기안함 메뉴를 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "결재관리", + "alternativeTexts": ["결재관리", "결재 관리", "Approval", "전자결재"], + "scrollContainer": "sidebar", + "maxAttempts": 10, + "description": "스크롤하며 결재관리 메뉴 찾기" + }, + { "type": "click", "target": "결재관리", "description": "결재관리 메뉴 클릭" }, + { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, + { + "type": "scrollAndFind", + "target": "기안함", + "alternativeTexts": ["기안함", "기안 함", "Draft", "내 기안"], + "scrollContainer": "submenu", + "maxAttempts": 5, + "description": "서브메뉴에서 기안함 찾기" + }, + { "type": "click", "target": "기안함", "description": "기안함 메뉴 클릭" }, + { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 } + ], + "expected": { + "url": "/ko/approval/draft", + "pageTitle": "기안함", + "elements": ["통계 카드", "검색바", "테이블", "페이지네이션"] + }, + "verification": [ + "결재관리 메뉴가 펼쳐졌는지 확인", + "기안함 서브메뉴 클릭 성공", + "404 에러 없이 페이지 로드 완료" + ] + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "페이지 타이틀, 설명, 통계 카드, 헤더 액션 버튼 확인", + "actions": [ + { + "type": "verify", + "target": "페이지 구조" + } + ], + "expected": { + "pageTitle": "기안함", + "pageDescription": "작성한 결재 문서를 관리합니다", + "icon": "FileText", + "statCards": ["진행", "완료", "반려", "임시 저장"], + "headerActions": ["날짜 범위 선택", "문서 작성 버튼"] + } + }, + { + "id": "step-3", + "name": "통계 카드 표시 확인", + "description": "4개의 통계 카드(진행, 완료, 반려, 임시 저장) 표시 및 건수 확인", + "actions": [ + { + "type": "verify", + "target": "통계 카드" + } + ], + "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"} + ], + "apiCalled": "GET /api/v1/approvals/drafts/summary" + } + }, + { + "id": "step-4", + "name": "테이블 컬럼 구조 확인", + "description": "기안함 테이블의 컬럼 헤더 확인 (8개 컬럼)", + "actions": [ + { + "type": "verify", + "target": "table columns" + } + ], + "expected": { + "columns": [ + "번호", + "문서번호", + "문서유형", + "제목", + "결재자", + "기안일시", + "상태", + "작업" + ], + "hasCheckboxColumn": true + } + }, + { + "id": "step-5", + "name": "데이터 로드 확인", + "description": "기안 문서 데이터가 테이블에 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "table data" + } + ], + "expected": { + "dataExists": "데이터 행 존재 또는 '데이터가 없습니다' 메시지", + "apiCalled": "GET /api/v1/approvals/drafts?page=1&per_page=20", + "defaultSort": "최신순 (created_at desc)", + "defaultFilter": "전체" + } + }, + { + "id": "step-6", + "name": "문서번호 형식 확인", + "description": "문서번호가 정상적으로 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "document number format" + } + ], + "expected": { + "format": "문서번호 형식 (예: DR-2026-001)", + "column": "문서번호" + } + }, + { + "id": "step-7", + "name": "문서유형 뱃지 표시 확인", + "description": "문서유형이 뱃지 형태로 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "document type badge" + } + ], + "expected": { + "displayFormat": "Badge (outline)", + "possibleValues": ["품의서", "지출결의서", "예상지출내역"] + } + }, + { + "id": "step-8", + "name": "결재자 표시 형식 확인", + "description": "결재자가 '이름 외 N명' 형식으로 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "approvers format" + } + ], + "expected": { + "format": "단일: '홍길동', 복수: '홍길동 외 2명'", + "emptyFormat": "-" + } + }, + { + "id": "step-9", + "name": "상태 뱃지 색상 확인", + "description": "문서 상태별로 다른 색상의 뱃지가 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "status badge colors" + } + ], + "expected": { + "statusColors": { + "임시저장": "gray", + "결재대기": "yellow", + "진행중": "blue", + "완료": "green", + "반려": "red" + } + } + }, + { + "id": "step-10", + "name": "검색 기능 테스트", + "description": "검색바에 키워드 입력 후 필터링 확인", + "actions": [ + { + "type": "input", + "target": "검색 입력 필드", + "value": "테스트" + }, + { + "type": "wait", + "target": "검색 결과 로드" + } + ], + "expected": { + "searchPlaceholder": "문서번호, 제목, 기안자 검색...", + "apiCalled": "GET /api/v1/approvals/drafts?search=테스트", + "dataFiltered": "검색어 포함된 문서만 표시", + "pageReset": "1페이지로 초기화" + } + }, + { + "id": "step-11", + "name": "검색어 초기화", + "description": "검색어를 지우고 전체 목록으로 복귀", + "actions": [ + { + "type": "clear", + "target": "검색 입력 필드" + }, + { + "type": "wait", + "target": "데이터 로드" + } + ], + "expected": { + "dataRestored": "전체 목록 표시", + "apiCalled": "GET /api/v1/approvals/drafts?page=1" + } + }, + { + "id": "step-12", + "name": "필터 셀렉트박스 존재 확인", + "description": "상태 필터 드롭다운이 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "filter select" + } + ], + "expected": { + "selectExists": true, + "defaultValue": "전체", + "options": ["전체", "임시저장", "결재대기", "진행중", "완료", "반려"] + } + }, + { + "id": "step-13", + "name": "필터 적용 테스트 (임시저장)", + "description": "필터를 '임시저장'으로 변경하여 필터링 확인", + "actions": [ + { + "type": "select", + "target": "필터 셀렉트박스", + "value": "임시저장" + }, + { + "type": "wait", + "target": "데이터 로드" + } + ], + "expected": { + "apiCalled": "GET /api/v1/approvals/drafts?status=draft", + "dataFiltered": "임시저장 상태만 표시", + "pageReset": "1페이지로 초기화" + } + }, + { + "id": "step-14", + "name": "필터 초기화", + "description": "필터를 '전체'로 변경하여 전체 목록 표시", + "actions": [ + { + "type": "select", + "target": "필터 셀렉트박스", + "value": "전체" + }, + { + "type": "wait", + "target": "데이터 로드" + } + ], + "expected": { + "apiCalled": "GET /api/v1/approvals/drafts?page=1", + "dataRestored": "전체 상태 표시" + } + }, + { + "id": "step-15", + "name": "정렬 셀렉트박스 존재 확인", + "description": "정렬 옵션 드롭다운이 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "sort select" + } + ], + "expected": { + "selectExists": true, + "defaultValue": "최신순", + "options": ["최신순", "오래된순", "제목 오름차순", "제목 내림차순"] + } + }, + { + "id": "step-16", + "name": "정렬 변경 테스트 (제목 오름차순)", + "description": "정렬을 '제목 오름차순'으로 변경", + "actions": [ + { + "type": "select", + "target": "정렬 셀렉트박스", + "value": "제목 오름차순" + }, + { + "type": "wait", + "target": "데이터 로드" + } + ], + "expected": { + "apiCalled": "GET /api/v1/approvals/drafts?sort_by=title&sort_dir=asc", + "dataSorted": "제목 알파벳 순서로 정렬", + "pageReset": "1페이지로 초기화" + } + }, + { + "id": "step-17", + "name": "정렬 초기화", + "description": "정렬을 '최신순'으로 복귀", + "actions": [ + { + "type": "select", + "target": "정렬 셀렉트박스", + "value": "최신순" + }, + { + "type": "wait", + "target": "데이터 로드" + } + ], + "expected": { + "apiCalled": "GET /api/v1/approvals/drafts?sort_by=created_at&sort_dir=desc", + "dataRestored": "최신순 정렬" + } + }, + { + "id": "step-18", + "name": "체크박스 선택 (단일)", + "description": "첫 번째 문서의 체크박스 선택", + "actions": [ + { + "type": "click", + "target": "첫 번째 행 체크박스" + } + ], + "expected": { + "checkboxChecked": true, + "selectedCount": 1, + "actionButtonsVisible": "상신, 삭제 버튼 표시" + } + }, + { + "id": "step-19", + "name": "임시저장 문서 수정/삭제 버튼 표시 확인", + "description": "임시저장 상태 문서 선택 시 작업 컬럼에 수정/삭제 버튼 표시", + "actions": [ + { + "type": "verify", + "target": "action buttons for draft status" + } + ], + "expected": { + "condition": "status === 'draft' && isSelected", + "buttonsVisible": ["수정 (Pencil 아이콘)", "삭제 (Trash2 아이콘)"], + "buttonColors": { + "수정": "gray", + "삭제": "red" + } + } + }, + { + "id": "step-20", + "name": "체크박스 해제", + "description": "선택한 체크박스를 다시 클릭하여 해제", + "actions": [ + { + "type": "click", + "target": "첫 번째 행 체크박스" + } + ], + "expected": { + "checkboxChecked": false, + "selectedCount": 0, + "actionButtonsHidden": "상신, 삭제 버튼 숨김" + } + }, + { + "id": "step-21", + "name": "전체 선택 체크박스 클릭", + "description": "테이블 헤더의 전체 선택 체크박스 클릭", + "actions": [ + { + "type": "click", + "target": "헤더 체크박스 (전체 선택)" + } + ], + "expected": { + "allCheckboxesChecked": true, + "selectedCount": "현재 페이지의 모든 행 수", + "actionButtonsVisible": "상신, 삭제 버튼 표시" + } + }, + { + "id": "step-22", + "name": "전체 선택 해제", + "description": "전체 선택 체크박스를 다시 클릭하여 모두 해제", + "actions": [ + { + "type": "click", + "target": "헤더 체크박스 (전체 선택)" + } + ], + "expected": { + "allCheckboxesUnchecked": true, + "selectedCount": 0, + "actionButtonsHidden": "상신, 삭제 버튼 숨김" + } + }, + { + "id": "step-23", + "name": "문서 작성 버튼 확인", + "description": "헤더 액션에 '문서 작성' 버튼이 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "문서 작성 버튼" + } + ], + "expected": { + "buttonExists": true, + "buttonText": "문서 작성", + "icon": "Plus" + } + }, + { + "id": "step-24", + "name": "문서 클릭 (임시저장)", + "description": "임시저장 상태의 문서 행 클릭 (수정 모드로 이동)", + "actions": [ + { + "type": "click", + "target": "임시저장 상태의 문서 행" + } + ], + "expected": { + "urlChange": "/ko/approval/draft/new?id={id}&mode=edit", + "behavior": "문서 작성 페이지로 이동 (수정 모드)" + } + }, + { + "id": "step-25", + "name": "기안함으로 복귀", + "description": "문서 작성 페이지에서 기안함으로 돌아오기", + "actions": [ + { + "type": "navigate", + "target": "/ko/approval/draft" + } + ], + "expected": { + "url": "/ko/approval/draft", + "dataReloaded": "목록 재로드" + } + }, + { + "id": "step-26", + "name": "문서 클릭 (결재대기/진행중/완료)", + "description": "임시저장이 아닌 문서 행 클릭 (상세 모달 오픈)", + "actions": [ + { + "type": "click", + "target": "결재대기/진행중/완료 상태의 문서 행" + }, + { + "type": "wait", + "target": "모달 오픈 및 상세 데이터 로드" + } + ], + "expected": { + "modalOpened": true, + "apiCalled": "GET /api/v1/approvals/{id}", + "modalTitle": "문서 상세" + } + }, + { + "id": "step-27", + "name": "문서 상세 모달 구조 확인", + "description": "문서 상세 모달의 구조 및 내용 확인", + "actions": [ + { + "type": "verify", + "target": "document detail modal" + } + ], + "expected": { + "modalContent": [ + "문서번호", + "기안일시", + "결재자 목록 (최대 3명)", + "문서 내용 (문서 유형에 따라 다름)" + ], + "documentTypes": ["품의서 (proposal)", "지출결의서 (expenseReport)", "예상지출내역 (expenseEstimate)"] + } + }, + { + "id": "step-28", + "name": "모달 수정 버튼 확인", + "description": "모달 하단에 수정 버튼이 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "modal edit button" + } + ], + "expected": { + "buttonExists": true, + "buttonText": "수정", + "behavior": "클릭 시 문서 작성 페이지로 이동 (수정 모드)" + } + }, + { + "id": "step-29", + "name": "모달 복제 버튼 확인", + "description": "모달 하단에 복제 버튼이 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "modal copy button" + } + ], + "expected": { + "buttonExists": true, + "buttonText": "복제", + "behavior": "클릭 시 문서 작성 페이지로 이동 (복제 모드, copyFrom 파라미터)" + } + }, + { + "id": "step-30", + "name": "모달 상신 버튼 확인 (임시저장 시)", + "description": "임시저장 문서의 모달에서 상신 버튼 확인", + "actions": [ + { + "type": "verify", + "target": "modal submit button" + } + ], + "expected": { + "buttonExists": "임시저장 상태일 때만", + "buttonText": "상신", + "behavior": "클릭 시 결재 상신 (POST /api/v1/approvals/{id}/submit)" + } + }, + { + "id": "step-31", + "name": "모달 닫기", + "description": "문서 상세 모달을 닫기", + "actions": [ + { + "type": "click", + "target": "모달 외부 또는 닫기 버튼" + } + ], + "expected": { + "modalClosed": true, + "returnToList": "기안함 목록으로 복귀" + } + }, + { + "id": "step-31-pdf-1", + "name": "⚠️ 필수 검증: PDF 다운로드 전 모달 스크린샷", + "critical": true, + "description": "PDF 생성 전 모달 상태를 스크린샷으로 캡처하여 CSS 문제 감지용 기준 이미지 확보", + "prerequisite": "step-26의 문서 상세 모달이 열려있는 상태에서 실행", + "actions": [ + { + "type": "click", + "target": "결재대기/진행중/완료 상태의 문서 행", + "description": "모달 다시 열기" + }, + { + "type": "wait", + "duration": 1000, + "description": "모달 로드 대기" + }, + { + "type": "screenshot", + "name": "pdf-preview-before-download-draft-box", + "fullPage": false, + "selector": "[role='dialog'], .modal, [data-state='open']", + "savePath": "tests/e2e/results/hotfix/screenshots/", + "description": "PDF 생성 대상 모달 전체 캡처" + } + ], + "verify": { + "screenshotCaptured": true, + "purpose": "PDF CSS 문제 감지를 위한 기준 이미지" + } + }, + { + "id": "step-31-pdf-2", + "name": "⚠️ 필수 검증: PDF 다운로드 실행 및 파일 보관", + "critical": true, + "description": "PDF 다운로드 후 파일을 지정 폴더에 보관하여 수동 검증 가능하게 함", + "actions": [ + { + "type": "verify", + "target": "PDF 버튼 존재", + "selector": "button:has-text('PDF'), [aria-label*='PDF']", + "description": "PDF 다운로드 버튼 존재 확인" + }, + { + "type": "expectResponse", + "id": "pdf-download-response-draft-box", + "urlPattern": "/api/v1/approvals/*/pdf", + "description": "PDF 다운로드 API 응답 대기 설정" + }, + { + "type": "click", + "target": "PDF 버튼", + "selector": "button:has-text('PDF')", + "description": "PDF 다운로드 버튼 클릭" + }, + { + "type": "wait", + "duration": 3000, + "description": "PDF 생성 및 다운로드 대기" + }, + { + "type": "assertResponse", + "id": "pdf-download-response-draft-box", + "checks": { + "status": 200, + "contentType": "application/pdf" + } + }, + { + "type": "saveDownloadedFile", + "targetPath": "tests/e2e/results/hotfix/pdf-samples/", + "fileNamePattern": "draft-box-{timestamp}.pdf", + "description": "다운로드된 PDF 파일을 지정 폴더에 보관" + } + ], + "verify": { + "apiSuccess": true, + "fileDownloaded": true, + "fileSaved": "tests/e2e/results/hotfix/pdf-samples/" + } + }, + { + "id": "step-31-pdf-3", + "name": "⚠️ PDF 파일 유효성 검증", + "critical": true, + "description": "다운로드된 PDF 파일의 기본 유효성 검사", + "actions": [ + { + "type": "verifyDownloadedFile", + "checks": { + "fileExists": true, + "fileSize": "> 1024", + "pdfSignature": "%PDF-", + "description": "PDF 파일 헤더 검증" + } + } + ], + "verify": { + "pdfValid": true, + "minFileSize": "1KB 이상" + } + }, + { + "id": "step-31-pdf-4", + "name": "📋 PDF 스타일 수동 확인 체크리스트", + "type": "manualVerification", + "critical": true, + "description": "개발자가 다운로드된 PDF를 열어 시각적으로 확인해야 하는 항목", + "manualChecklist": [ + {"id": "css-1", "item": "테이블 경계선이 올바르게 표시되는가?", "category": "테이블 스타일"}, + {"id": "css-2", "item": "한글 폰트가 깨지지 않고 정상 표시되는가?", "category": "폰트"}, + {"id": "css-3", "item": "숫자/금액 정렬이 올바른가? (우측 정렬)", "category": "정렬"}, + {"id": "css-4", "item": "여백(margin/padding)이 적절한가?", "category": "레이아웃"}, + {"id": "css-5", "item": "헤더/푸터가 각 페이지에 올바르게 표시되는가?", "category": "페이지 구조"}, + {"id": "css-6", "item": "로고/이미지가 정상 표시되는가?", "category": "이미지"}, + {"id": "css-7", "item": "페이지 나눔(page break)이 적절한 위치에서 발생하는가?", "category": "페이지 나눔"}, + {"id": "css-8", "item": "배경색/강조색이 올바르게 적용되었는가?", "category": "색상"}, + {"id": "css-9", "item": "텍스트가 잘리거나 겹치지 않는가?", "category": "오버플로우"}, + {"id": "css-10", "item": "결재선 정보가 정상적으로 표시되는가?", "category": "결재선"} + ], + "outputFiles": { + "screenshot": "tests/e2e/results/hotfix/screenshots/pdf-preview-before-download-draft-box-*.png", + "pdfFile": "tests/e2e/results/hotfix/pdf-samples/draft-box-*.pdf" + }, + "reportFlag": { + "requiresManualReview": true, + "message": "⚠️ PDF 스타일 수동 확인 필요 - 위 체크리스트 항목을 PDF 파일에서 직접 확인하세요" + } + }, + { + "id": "step-31-pdf-5", + "name": "모달 닫기 (PDF 테스트 후)", + "description": "PDF 테스트 완료 후 모달 닫기", + "actions": [ + { + "type": "click", + "target": "모달 외부 또는 닫기 버튼" + } + ], + "expected": { + "modalClosed": true + } + }, + { + "id": "step-32", + "name": "날짜 범위 선택 확인", + "description": "헤더 액션에 날짜 범위 선택 컴포넌트가 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "date range selector" + } + ], + "expected": { + "componentExists": true, + "defaultStartDate": "2025-01-01", + "defaultEndDate": "2025-12-31", + "inputs": ["시작일 입력", "종료일 입력"] + } + }, + { + "id": "step-33", + "name": "페이지네이션 존재 확인", + "description": "테이블 하단에 페이지네이션이 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "pagination component" + } + ], + "expected": { + "paginationExists": true, + "showsCurrentPage": "현재 페이지 번호", + "showsTotalPages": "전체 페이지 수", + "showsTotalItems": "전체 항목 수", + "itemsPerPage": 20 + } + }, + { + "id": "step-34", + "name": "페이지네이션 이동 테스트", + "description": "2페이지가 있는 경우 페이지 이동 테스트", + "actions": [ + { + "type": "click", + "target": "페이지 2 버튼 (또는 다음 버튼)" + }, + { + "type": "wait", + "target": "데이터 로드" + } + ], + "expected": { + "currentPage": 2, + "apiCalled": "GET /api/v1/approvals/drafts?page=2", + "dataChanged": "2페이지 데이터 표시", + "scrollToTop": "페이지 상단으로 스크롤" + } + }, + { + "id": "step-35", + "name": "1페이지로 복귀", + "description": "페이지네이션에서 1페이지로 이동", + "actions": [ + { + "type": "click", + "target": "페이지 1 버튼" + }, + { + "type": "wait", + "target": "데이터 로드" + } + ], + "expected": { + "currentPage": 1, + "apiCalled": "GET /api/v1/approvals/drafts?page=1", + "dataRestored": "1페이지 데이터 표시" + } + }, + { + "id": "step-36", + "name": "테이블 hover 효과 확인", + "description": "테이블 행에 마우스 오버 시 배경색 변경 확인", + "actions": [ + { + "type": "hover", + "target": "첫 번째 테이블 행" + } + ], + "expected": { + "hoverEffect": "hover:bg-muted/50", + "backgroundChanges": true, + "cursorPointer": true + } + }, + { + "id": "step-37", + "name": "로딩 상태 확인", + "description": "데이터 로드 중 로딩 인디케이터 표시 확인", + "actions": [ + { + "type": "verify", + "target": "loading state" + } + ], + "expected": { + "loadingIndicator": "스피너 또는 로딩 메시지", + "isLoading": "true during data fetch" + } + }, + { + "id": "step-38", + "name": "빈 상태 메시지 확인", + "description": "검색/필터 결과가 없을 때 빈 상태 메시지 표시", + "actions": [ + { + "type": "input", + "target": "검색 입력 필드", + "value": "존재하지않는문서번호999999" + }, + { + "type": "wait", + "target": "검색 결과" + } + ], + "expected": { + "emptyMessage": "데이터가 없습니다.", + "messagePosition": "테이블 중앙" + } + }, + { + "id": "step-39", + "name": "검색어 초기화 (빈 상태 해제)", + "description": "검색어를 지워서 전체 목록으로 복귀", + "actions": [ + { + "type": "clear", + "target": "검색 입력 필드" + }, + { + "type": "wait", + "target": "데이터 로드" + } + ], + "expected": { + "dataRestored": "전체 목록 표시" + } + }, + { + "id": "step-40", + "name": "콘솔 에러 확인", + "description": "페이지 동작 중 콘솔에 에러가 발생하지 않는지 확인", + "actions": [ + { + "type": "verify", + "target": "console errors" + } + ], + "expected": { + "noErrors": "콘솔 에러 없음", + "warningsAcceptable": "경고는 허용" + } + }, + { + "id": "step-41", + "name": "반응형 레이아웃 확인", + "description": "모바일 뷰에서 카드 레이아웃으로 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "mobile card layout" + } + ], + "expected": { + "mobileCardExists": "화면 크기에 따라", + "cardTitle": "문서 제목", + "cardFields": ["문서번호", "기안일자", "기안자", "결재자"] + } + }, + { + "id": "step-42", + "name": "모바일 카드 액션 버튼 확인", + "description": "모바일 카드에서 임시저장 문서 선택 시 수정/삭제 버튼 표시", + "actions": [ + { + "type": "verify", + "target": "mobile card actions" + } + ], + "expected": { + "condition": "status === 'draft' && isSelected", + "buttons": ["수정", "삭제"], + "buttonIcons": ["Pencil", "Trash2"] + } + }, + { + "id": "step-43", + "name": "통계 카드 실시간 업데이트 확인", + "description": "문서 상신/삭제 후 통계 카드가 업데이트되는지 확인", + "actions": [ + { + "type": "verify", + "target": "stat cards update after action" + } + ], + "expected": { + "updateTriggers": ["상신 성공", "삭제 성공"], + "apiCalled": "GET /api/v1/approvals/drafts/summary" + } + }, + { + "id": "step-44", + "name": "IntegratedListTemplateV2 사용 확인", + "description": "IntegratedListTemplateV2 템플릿 컴포넌트 사용 확인", + "actions": [ + { + "type": "verify", + "target": "template component" + } + ], + "expected": { + "templateComponent": "IntegratedListTemplateV2", + "responsive": "모바일/데스크톱 대응" + } + }, + { + "id": "step-45", + "name": "상신 버튼 조건부 표시 확인", + "description": "항목 선택 시에만 상신 버튼이 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "submit button visibility" + } + ], + "expected": { + "condition": "selectedItems.size > 0", + "buttonVisible": true, + "buttonText": "상신", + "icon": "Send" + } + }, + { + "id": "step-46", + "name": "삭제 버튼 조건부 표시 확인", + "description": "항목 선택 시에만 삭제 버튼이 표시되는지 확인", + "actions": [ + { + "type": "verify", + "target": "delete button visibility" + } + ], + "expected": { + "condition": "selectedItems.size > 0", + "buttonVisible": true, + "buttonText": "삭제", + "icon": "Trash2", + "variant": "destructive (red)" + } + }, + { + "id": "step-47", + "name": "결재자 상태 뱃지 확인", + "description": "모달 내 결재자 목록의 상태 뱃지 색상 확인", + "actions": [ + { + "type": "verify", + "target": "approver status badges in modal" + } + ], + "expected": { + "statusColors": { + "none": "gray", + "pending": "yellow", + "approved": "green", + "rejected": "red" + } + } + }, + { + "id": "step-48", + "name": "문서 유형별 모달 내용 확인", + "description": "문서 유형(품의서, 지출결의서, 예상지출내역)에 따라 다른 내용 표시", + "actions": [ + { + "type": "verify", + "target": "document type specific content" + } + ], + "expected": { + "proposal": ["거래처", "거래처 지급일", "제목", "내용", "사유", "예상금액", "첨부파일"], + "expenseReport": ["신청일", "지급일", "지출 내역", "카드 정보", "총액", "첨부파일"], + "expenseEstimate": ["예상지급일", "카테고리", "금액", "거래처", "계좌", "총 지출", "계좌 잔액", "최종 차액"] + } + }, + { + "id": "step-49", + "name": "API 응답 구조 확인", + "description": "기안함 목록 API 응답이 올바른 구조인지 확인", + "actions": [ + { + "type": "verify", + "target": "API response structure" + } + ], + "expected": { + "responseStructure": { + "success": true, + "data": { + "current_page": "number", + "data": "Array", + "total": "number", + "per_page": "number", + "last_page": "number" + } + } + } + }, + { + "id": "step-50", + "name": "데이터 변환 확인", + "description": "API 데이터가 프론트엔드 형식으로 변환되는지 확인", + "actions": [ + { + "type": "verify", + "target": "data transformation" + } + ], + "expected": { + "apiFormat": "snake_case (document_number, created_at, approval_steps)", + "frontendFormat": "camelCase (documentNo, draftDate, approvers)", + "transformFunction": "transformApiToFrontend", + "statusMapping": { + "draft": "draft", + "pending": "pending", + "in_progress": "inProgress", + "approved": "approved", + "rejected": "rejected" + } + } + } + ], + "cleanup": { + "description": "테스트 후 정리 작업 (없음)", + "actions": [] + }, + "notes": [ + "20개씩 페이지네이션 (itemsPerPage: 20)", + "검색 필드: 문서번호, 제목, 기안자 검색 가능", + "필터: 전체, 임시저장, 결재대기, 진행중, 완료, 반려", + "정렬: 최신순, 오래된순, 제목 오름차순, 제목 내림차순", + "체크박스: 개별 선택, 전체 선택 가능", + "상신/삭제: 선택된 항목이 있을 때만 버튼 표시", + "임시저장 문서: 선택 시 작업 컬럼에 수정/삭제 버튼 표시", + "문서 클릭 동작: 임시저장 → 수정 페이지, 그 외 → 상세 모달", + "통계 카드: API summary로 실시간 업데이트", + "IntegratedListTemplateV2 템플릿 사용으로 반응형 지원", + "날짜 범위 선택 기본값: 2025-01-01 ~ 2025-12-31", + "결재자 표시: 단일(이름), 복수(이름 외 N명)", + "모달 버튼: 수정, 복제, 상신(임시저장만)", + "승인/반려 버튼 없음 (기안함에서는 본인 문서 승인/반려 불가)" + ] +} diff --git a/employee-register.json b/employee-register.json new file mode 100644 index 0000000..a0c816b --- /dev/null +++ b/employee-register.json @@ -0,0 +1,379 @@ +{ + "id": "employee-register", + "name": "직원 등록 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "신규 직원 정보를 입력하고 등록하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/ko/hr/employee-management", + "menuNavigation": { + "level1": "인사관리", + "level2": "사원관리", + "expectedUrl": "/ko/hr/employee-management" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "level1": { + "text": "인사관리", + "scrollContainer": ".sidebar-scroll, [class*='sidebar'], nav", + "maxScrollAttempts": 5, + "scrollStep": 200 + }, + "level2": { + "text": "사원관리", + "waitAfterLevel1": 500 + }, + "fallbackUrl": "/ko/hr/employee-management", + "timeout": 10000 + }, + "timeout": 60000, + "tags": ["hr", "employee", "crud"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "메뉴 탐색 전 사이드바 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll, [class*=\"sidebar\"], nav')?.scrollTo({top: 0, behavior: 'instant'})", + "description": "사이드바 스크롤 최상단으로 이동" + }, + { + "type": "wait", + "duration": 500, + "description": "스크롤 안정화 대기" + } + ] + }, + { + "id": "step-1", + "name": "인사관리 메뉴 진입", + "description": "인사관리 > 직원관리 메뉴로 이동 (scrollAndFind 패턴)", + "menuNavigation": { + "pattern": "scrollAndFind", + "level1": { + "text": "인사관리", + "scrollContainer": ".sidebar-scroll, [class*='sidebar'], nav", + "scrollStep": 200, + "maxAttempts": 5 + }, + "level2": { + "text": "직원관리", + "waitAfterLevel1Click": 500 + } + }, + "expect": { + "url": "/hr/employee-management", + "visible": ["사원관리", "사원 등록"] + } + }, + { + "id": "step-2", + "name": "사원 등록 페이지 이동", + "click": "사원 등록", + "expect": { + "url": "/hr/employee-management?mode=new", + "visible": ["사원 등록", "사원 정보"] + } + }, + { + "id": "step-3", + "name": "사원 정보 입력", + "description": "기본 사원 정보 입력", + "form": { + "fields": [ + { "name": "이름 *", "type": "text", "value": "홍길동" }, + { "name": "주민등록번호", "type": "text", "value": "900101-1234567" }, + { "name": "휴대폰", "type": "text", "value": "010-1234-5678" }, + { "name": "이메일 *", "type": "text", "value": "test.employee@codebridge-x.com" }, + { "name": "연봉", "type": "number", "value": "50000000" } + ] + } + }, + { + "id": "step-4", + "name": "급여계좌 정보 입력", + "form": { + "fields": [ + { "name": "은행명", "type": "text", "value": "신한은행" }, + { "name": "계좌번호", "type": "text", "value": "110-123-456789" }, + { "name": "예금주", "type": "text", "value": "홍길동" } + ] + } + }, + { + "id": "step-5", + "name": "사원 상세 정보 입력", + "form": { + "fields": [ + { "name": "사원코드", "type": "text", "value": "EMP2026001" }, + { "name": "남성", "type": "radio", "value": "true" }, + { "name": "상세주소를 입력해주세요", "type": "text", "value": "123번지 4층" } + ] + } + }, + { + "id": "step-6", + "name": "인사 정보 입력", + "form": { + "fields": [ + { "name": "입사일", "type": "date", "value": "2026-01-14" } + ] + }, + "actions": [ + { "type": "click", "target": "고용형태 선택", "description": "고용형태 드롭다운 열기" }, + { "type": "click", "target": "정규직", "description": "정규직 선택" }, + { "type": "click", "target": "직급 선택", "description": "직급 드롭다운 열기" }, + { "type": "click", "target": "사원", "description": "사원 직급 선택" } + ] + }, + { + "id": "step-7", + "name": "사용자 정보 입력", + "form": { + "fields": [ + { "name": "아이디 *", "type": "text", "value": "testuser2026" }, + { "name": "비밀번호 *", "type": "text", "value": "password123!" }, + { "name": "비밀번호 확인 *", "type": "text", "value": "password123!" } + ] + } + }, + { + "id": "step-8", + "name": "등록 완료", + "click": "등록", + "waitFor": "사원관리", + "expect": { + "url": "/hr/employee-management", + "text": ["홍길동"] + } + }, + { + "id": "step-8-1", + "name": "검색 기간 설정 - 유효 기간", + "description": "등록된 사원의 입사일(2026-01-14)이 포함되는 기간으로 검색", + "critical": true, + "actions": [ + { + "type": "setDateRange", + "startDate": { + "selector": "input[placeholder*='시작'], input[name*='startDate'], .date-picker-start", + "value": "2026-01-01" + }, + "endDate": { + "selector": "input[placeholder*='종료'], input[name*='endDate'], .date-picker-end", + "value": "2026-01-31" + } + }, + { + "type": "click", + "target": "검색", + "fallbackSelectors": ["button:has-text('검색')", ".search-btn", "[type='submit']"] + } + ], + "expect": { + "tableContains": "홍길동", + "rowCount": ">= 1" + }, + "onFail": { + "record": true, + "message": "검색 기간 2026-01-01 ~ 2026-01-31 내 입사일(2026-01-14) 사원이 검색되지 않음", + "severity": "HIGH", + "bugType": "검색 기간 필터링 오류" + } + }, + { + "id": "step-8-2", + "name": "검색 기간 설정 - 범위 외 기간", + "description": "등록된 사원의 입사일이 포함되지 않는 기간으로 검색하여 검색되지 않음을 확인", + "critical": true, + "actions": [ + { + "type": "setDateRange", + "startDate": { + "selector": "input[placeholder*='시작'], input[name*='startDate'], .date-picker-start", + "value": "2025-01-01" + }, + "endDate": { + "selector": "input[placeholder*='종료'], input[name*='endDate'], .date-picker-end", + "value": "2025-12-31" + } + }, + { + "type": "click", + "target": "검색", + "fallbackSelectors": ["button:has-text('검색')", ".search-btn", "[type='submit']"] + } + ], + "expect": { + "tableNotContains": "홍길동", + "alternativeExpect": { + "emptyResult": true, + "message": "검색 결과 없음" + } + }, + "verify": { + "searchPeriodValidation": { + "inputPeriod": "2025-01-01 ~ 2025-12-31", + "employeeJoinDate": "2026-01-14", + "expectedResult": "NOT_FOUND", + "actualResultCheck": true + } + }, + "onFail": { + "record": true, + "message": "검색 기간 2025-01-01 ~ 2025-12-31 범위 외 입사일(2026-01-14) 사원이 검색됨 - 기간 필터 미작동", + "severity": "HIGH", + "bugType": "검색 기간 필터링 미작동" + } + }, + { + "id": "step-8-3", + "name": "검색 기간 초기화 및 전체 조회", + "description": "검색 조건 초기화하여 등록된 사원이 다시 표시되는지 확인", + "actions": [ + { + "type": "click", + "target": "초기화", + "fallbackSelectors": ["button:has-text('초기화')", ".reset-btn", "button:has-text('Reset')"] + }, + { + "type": "click", + "target": "검색", + "fallbackSelectors": ["button:has-text('검색')", ".search-btn"] + } + ], + "expect": { + "tableContains": "홍길동" + }, + "onFail": { + "record": true, + "message": "검색 초기화 후 전체 조회에서 등록된 사원이 표시되지 않음", + "severity": "MEDIUM", + "bugType": "검색 초기화 오류" + } + }, + { + "id": "step-9", + "name": "등록된 직원 상세 페이지 이동", + "description": "등록된 직원을 클릭하여 상세 페이지로 이동", + "actions": [ + { + "type": "findRow", + "contains": "홍길동", + "then": { + "type": "click", + "target": "row", + "description": "해당 행 클릭하여 상세 페이지 이동" + } + } + ], + "expect": { + "url": "/hr/employee-management/{id}", + "visible": ["사원 상세", "수정", "삭제", "목록"] + } + }, + { + "id": "step-10", + "name": "직원 수정 모드 전환", + "description": "수정 버튼 클릭하여 편집 모드로 전환", + "click": "수정", + "expect": { + "url": "/hr/employee-management/{id}?mode=edit", + "visible": ["사원 수정", "취소", "저장"] + } + }, + { + "id": "step-11", + "name": "직원 정보 수정", + "description": "휴대폰 번호 변경", + "form": { + "fields": [ + { "name": "휴대폰", "type": "text", "value": "010-9999-8888", "clear": true } + ] + } + }, + { + "id": "step-12", + "name": "수정 저장", + "description": "수정된 직원 정보 저장", + "click": "저장", + "waitFor": "사원 상세", + "expect": { + "toast": ["수정", "완료", "성공", "저장"], + "url": "/hr/employee-management/{id}" + } + }, + { + "id": "step-12-1", + "name": "⚠️ 필수 검증: 수정 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", + "description": "상세 페이지에서 수정된 휴대폰 번호 확인", + "verify": { + "fieldValue": { + "target": "휴대폰", + "expected": "010-9999-8888" + } + } + }, + { + "id": "step-13", + "name": "직원 삭제", + "description": "삭제 버튼 클릭하여 직원 삭제", + "click": "삭제", + "expect": { + "confirmDialog": true, + "dialogText": ["삭제", "하시겠습니까"] + } + }, + { + "id": "step-14", + "name": "삭제 확인", + "description": "삭제 확인 다이얼로그에서 확인 클릭", + "click": "확인", + "waitFor": "사원관리", + "expect": { + "toast": ["삭제", "완료", "성공"], + "url": "/hr/employee-management" + } + }, + { + "id": "step-15", + "name": "⚠️ 필수 검증: 삭제 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 삭제 확인 필수!", + "description": "목록에서 삭제된 직원이 없어졌는지 확인", + "verify": { + "tableNotContains": "홍길동" + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/hr/employee-management", + "message": "등록 후 직원 목록 페이지로 이동해야 함" + }, + { + "type": "text", + "target": "body", + "expected": "홍길동", + "message": "등록된 직원이 목록에 표시되어야 함" + } + ] +} diff --git a/event-board.json b/event-board.json new file mode 100644 index 0000000..c86006e --- /dev/null +++ b/event-board.json @@ -0,0 +1,183 @@ +{ + "id": "event-board", + "name": "이벤트 게시판 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "고객센터 > 이벤트 게시판 페이지의 이벤트 목록 조회, 필터링 기능을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/support/events", + "menuNavigation": { + "level1": "고객센터", + "level2": "이벤트 게시판", + "expectedUrl": "/support/events" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebar": { + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 5, + "waitAfterScroll": 300 + }, + "level1": { + "text": "고객센터", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "이벤트 게시판", + "waitAfterClick": 300 + }, + "fallbackUrl": "/support/events", + "expectedUrl": "/support/events" + }, + "timeout": 90000, + "tags": ["support", "event", "read-only"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "사이드바를 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 500 } + ] + }, + { + "id": "step-1", + "name": "고객센터 메뉴 진입", + "description": "고객센터 > 이벤트 게시판 메뉴로 이동", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "고객센터", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "고객센터" }, + { "type": "wait", "duration": 500 }, + { "type": "click", "target": "이벤트 게시판" } + ], + "expect": { + "url": "/support/events", + "visible": ["이벤트"] + }, + "fallback": { + "type": "navigate", + "url": "/support/events" + } + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "이벤트 페이지 구조 확인", + "verify": { + "visible": ["이벤트", "진행중인 이벤트", "종료된 이벤트"], + "tableColumns": ["No.", "제목", "작성자", "기간", "조회수"] + } + }, + { + "id": "step-3", + "name": "통계 카드 확인", + "description": "진행중/종료된 이벤트 카운트 확인", + "verify": { + "statsCards": ["진행중인 이벤트", "종료된 이벤트"], + "countsDisplayed": true + } + }, + { + "id": "step-4", + "name": "필수 검증 #3: 날짜 필터 기능", + "description": "날짜 필터 버튼 동작 확인", + "actions": [ + { "type": "click", "target": "당해년도" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "filterApplied": true + } + }, + { + "id": "step-5", + "name": "필수 검증 #3: 정렬 옵션 확인", + "description": "정렬 드롭다운 동작 확인", + "actions": [ + { "type": "click", "target": "최신순", "role": "combobox" } + ], + "expect": { + "optionsVisible": true + } + }, + { + "id": "step-6", + "name": "필터 초기화", + "description": "초기화 버튼 클릭하여 필터 리셋", + "actions": [ + { "type": "press", "key": "Escape" }, + { "type": "wait", "duration": 300 }, + { "type": "click", "target": "초기화" } + ], + "expect": { + "filtersReset": true + } + }, + { + "id": "step-7", + "name": "빈 상태 확인", + "description": "데이터가 없을 때 빈 상태 메시지 확인", + "verify": { + "emptyStateVisible": "검색 결과가 없습니다" + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/support/events", + "message": "이벤트 게시판 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "text=이벤트", + "message": "이벤트 제목이 표시되어야 함" + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 3, + "name": "검색/필터", + "trigger": "날짜 필터, 정렬", + "verification": "데이터 변화 확인", + "failCondition": "필터 적용 후 데이터 무변화" + } + ] + }, + + "notes": { + "testScope": "이벤트 목록 조회 → 필터/정렬 테스트", + "pageType": "조회 전용 (일반 사용자 등록/수정/삭제 불가)", + "statistics": ["진행중인 이벤트", "종료된 이벤트"], + "tableColumns": ["No.", "제목", "작성자", "기간", "조회수"], + "prerequisites": "로그인된 사용자" + } +} diff --git a/expected-expenses.json b/expected-expenses.json new file mode 100644 index 0000000..c06a9fc --- /dev/null +++ b/expected-expenses.json @@ -0,0 +1,1059 @@ +{ + "id": "expected-expenses", + "name": "예상비용 관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "회계관리 > 예상비용 메뉴의 CRUD 전체 워크플로우, 일괄 작업, 전자결재 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/ko/accounting/expected-expenses", + "menuNavigation": { + "level1": "회계관리", + "level2": "지출예상내역서", + "expectedUrl": "/ko/accounting/expected-expenses" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebarSelector": ".sidebar-scroll, [data-sidebar], nav[role='navigation']", + "scrollConfig": { + "scrollToTopFirst": true, + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + }, + "level1": { + "text": "회계관리", + "selectors": [ + "button:has-text('회계관리')", + "[data-menu='accounting']", + "a:has-text('회계관리')" + ], + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "지출예상내역서", + "selectors": [ + "a:has-text('예상비용')", + "[href*='expected-expenses']", + "button:has-text('예상비용')" + ], + "waitAfterClick": 1000 + }, + "fallback": { + "directUrl": "/ko/accounting/expected-expenses", + "useAfterAttempts": 3 + } + }, + "testFocus": { + "primary": "등록/수정/삭제 전체 워크플로우 및 일괄 작업 기능 검증", + "description": "2년 기간 설정, 모달 등록/수정, 일괄 작업(예상 지급일 변경, 전자결재, 일괄삭제) 테스트" + }, + "prerequisites": { + "authentication": true, + "testData": { + "dateRange": { + "startDate": "2024-01-15", + "endDate": "2026-01-15" + }, + "newExpense": { + "transactionType": "purchase", + "amount": 5000000, + "vendorName": "테스트거래처", + "accountSubject": "매입비용", + "note": "테스트 예상비용 등록" + }, + "updateExpense": { + "amount": 7000000, + "note": "테스트 예상비용 수정" + }, + "bulkOperation": { + "newExpectedDate": "2026-02-15" + } + } + }, + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll, [data-sidebar], nav[role=\"navigation\"]')?.scrollTo({top: 0, behavior: 'instant'})" + }, + { "type": "wait", "duration": 500 } + ], + "expected": { + "sidebarReady": true, + "scrollPosition": "top" + } + }, + { + "id": "step-1", + "name": "2단계 메뉴 진입: 회계관리 > 예상비용", + "description": "회계관리 > 예상비용 메뉴로 이동하여 페이지 로드 확인 (scrollAndFind 패턴 적용)", + "navigationPattern": "scrollAndFind", + "actions": [ + { + "type": "scrollAndFind", + "target": "회계관리", + "container": ".sidebar-scroll, [data-sidebar], nav[role='navigation']", + "scrollStep": 200, + "maxAttempts": 10 + }, + { "type": "click", "target": "회계관리" }, + { "type": "wait", "duration": 500 }, + { + "type": "scrollAndFind", + "target": "예상비용", + "container": ".sidebar-scroll, [data-sidebar], nav[role='navigation']", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "예상비용" }, + { "type": "wait", "target": "페이지 로드 완료", "timeout": 5000 } + ], + "fallback": { + "action": "navigate", + "url": "/ko/accounting/expected-expenses" + }, + "expected": { + "url": "/ko/accounting/expected-expenses", + "pageTitle": "예상비용", + "elements": ["페이지 타이틀", "기간 선택", "필터", "테이블", "등록 버튼"], + "authenticated": true + } + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "통계 카드, 필터, 버튼, 테이블 구조 확인", + "actions": [ + { + "type": "verify", + "target": "페이지 구조" + } + ], + "expected": { + "statCards": ["총 지출 예상", "미지급 건수", "지급완료 건수"], + "filters": ["기간 선택", "거래유형", "지급상태", "정렬"], + "buttons": ["등록", "예상 지급일 변경", "전자결재", "일괄삭제"], + "tableColumns": ["선택", "예상 지급일", "결제일", "거래유형", "거래처명", "지출금액", "지급상태", "전자결재", "적요", "수정", "삭제"] + } + }, + { + "id": "step-3", + "name": "기간 설정 (2년)", + "description": "현재일자에서 2년 전까지 기간 설정 (2024-01-15 ~ 2026-01-15)", + "actions": [ + { + "type": "click", + "target": "기간 선택 버튼" + }, + { + "type": "selectDate", + "target": "시작일", + "value": "2024-01-15" + }, + { + "type": "selectDate", + "target": "종료일", + "value": "2026-01-15" + }, + { + "type": "click", + "target": "적용 버튼" + }, + { + "type": "wait", + "target": "데이터 로드" + } + ], + "expected": { + "dateRange": "2024-01-15 ~ 2026-01-15", + "dataLoaded": true, + "tableVisible": true + } + }, + { + "id": "step-4", + "name": "초기 데이터 확인", + "description": "2년 기간 데이터 로드 확인 및 통계 카드 값 확인", + "actions": [ + { + "type": "verify", + "target": "테이블 데이터 및 통계" + } + ], + "expected": { + "tableHasData": true, + "statCardsVisible": true, + "monthGrouping": "월별 그룹핑 표시" + } + }, + { + "id": "step-5", + "name": "등록 버튼 클릭", + "description": "등록 버튼 클릭하여 등록 모달 열기", + "actions": [ + { + "type": "click", + "target": "등록 버튼" + }, + { + "type": "wait", + "target": "모달 표시" + } + ], + "expected": { + "modalTitle": "예상비용 등록", + "modalVisible": true, + "formFieldsVisible": true + } + }, + { + "id": "step-6", + "name": "등록 모달 필드 확인", + "description": "등록 모달의 모든 입력 필드 존재 확인", + "actions": [ + { + "type": "verify", + "target": "모달 입력 필드" + } + ], + "expected": { + "fields": [ + "예상 지급일 (datepicker)", + "결제일 (datepicker)", + "거래유형 (select)", + "거래처 (combobox)", + "지출금액 (input number)", + "계좌 (select)", + "계정과목 (select)", + "적요/메모 (textarea)", + "등록 버튼", + "취소 버튼" + ] + } + }, + { + "id": "step-7", + "name": "예상 지급일 입력", + "description": "DatePicker로 예상 지급일 선택", + "actions": [ + { + "type": "click", + "target": "예상 지급일 필드" + }, + { + "type": "selectDate", + "target": "캘린더", + "value": "2026-02-01" + } + ], + "expected": { + "fieldValue": "2026-02-01", + "fieldFilled": true + } + }, + { + "id": "step-8", + "name": "결제일 입력", + "description": "DatePicker로 결제일 선택", + "actions": [ + { + "type": "click", + "target": "결제일 필드" + }, + { + "type": "selectDate", + "target": "캘린더", + "value": "2026-01-20" + } + ], + "expected": { + "fieldValue": "2026-01-20", + "fieldFilled": true + } + }, + { + "id": "step-9", + "name": "거래유형 선택", + "description": "Select로 거래유형 선택 (매입)", + "actions": [ + { + "type": "click", + "target": "거래유형 Select" + }, + { + "type": "select", + "target": "옵션 목록", + "value": "매입" + } + ], + "expected": { + "options": ["매입", "선급금", "가지급금", "임대료", "급여", "보험료", "세금", "공과금", "기타"], + "selectedValue": "매입" + } + }, + { + "id": "step-10", + "name": "거래처 입력", + "description": "Combobox로 거래처 검색 및 선택 또는 직접 입력", + "actions": [ + { + "type": "click", + "target": "거래처 Combobox" + }, + { + "type": "type", + "target": "거래처 입력 필드", + "value": "테스트" + }, + { + "type": "wait", + "target": "검색 결과" + }, + { + "type": "select", + "target": "거래처 옵션 또는 직접 입력" + } + ], + "expected": { + "searchWorking": true, + "optionsDisplayed": "거래처 목록 또는 새로 입력 가능", + "selectedVendor": "테스트거래처" + } + }, + { + "id": "step-11", + "name": "지출금액 입력", + "description": "숫자 입력 필드에 금액 직접 입력", + "actions": [ + { + "type": "click", + "target": "지출금액 필드" + }, + { + "type": "clear", + "target": "지출금액 필드" + }, + { + "type": "type", + "target": "지출금액 필드", + "value": "5000000" + } + ], + "expected": { + "fieldValue": "5000000 또는 5,000,000 (포맷)", + "validInput": true + } + }, + { + "id": "step-12", + "name": "계좌 선택", + "description": "Select로 계좌 선택", + "actions": [ + { + "type": "click", + "target": "계좌 Select" + }, + { + "type": "select", + "target": "계좌 옵션" + } + ], + "expected": { + "optionsLoaded": true, + "accountSelected": true + } + }, + { + "id": "step-13", + "name": "계정과목 선택", + "description": "Select로 계정과목 선택 (매입비용)", + "actions": [ + { + "type": "click", + "target": "계정과목 Select" + }, + { + "type": "select", + "target": "옵션 목록", + "value": "매입비용" + } + ], + "expected": { + "options": ["매입비용", "급여", "임차료", "공과금", "보험료", "세금과공과", "기타비용"], + "selectedValue": "매입비용" + } + }, + { + "id": "step-14", + "name": "적요 입력", + "description": "Textarea에 메모/적요 입력", + "actions": [ + { + "type": "click", + "target": "적요/메모 필드" + }, + { + "type": "type", + "target": "적요/메모 필드", + "value": "테스트 예상비용 등록" + } + ], + "expected": { + "fieldValue": "테스트 예상비용 등록", + "textareaFilled": true + } + }, + { + "id": "step-15", + "name": "등록 버튼 클릭 및 저장", + "description": "모달 내 등록 버튼 클릭하여 데이터 저장", + "actions": [ + { + "type": "click", + "target": "등록 버튼 (모달 내)" + }, + { + "type": "wait", + "target": "저장 처리 완료" + } + ], + "expected": { + "apiCall": "POST /api/accounting/expected-expenses", + "apiResponse": "200 OK", + "successToast": "예상비용이 등록되었습니다", + "modalClosed": true + } + }, + { + "id": "step-16", + "name": "등록 결과 확인", + "description": "테이블에 새로 등록된 데이터 표시 확인", + "actions": [ + { + "type": "wait", + "target": "테이블 리로드" + }, + { + "type": "verify", + "target": "신규 데이터 행" + } + ], + "expected": { + "newRowVisible": true, + "dataMatches": { + "amount": "5,000,000원", + "vendorName": "테스트거래처", + "transactionType": "매입", + "note": "테스트 예상비용 등록" + } + } + }, + { + "id": "step-17", + "name": "수정 아이콘 클릭", + "description": "방금 등록한 데이터의 수정 아이콘 클릭하여 수정 모달 열기", + "actions": [ + { + "type": "click", + "target": "신규 등록 행의 수정 아이콘" + }, + { + "type": "wait", + "target": "수정 모달 표시" + } + ], + "expected": { + "modalTitle": "예상비용 수정", + "modalVisible": true, + "prefilledData": { + "amount": "5000000", + "vendorName": "테스트거래처", + "note": "테스트 예상비용 등록" + } + } + }, + { + "id": "step-18", + "name": "지출금액 수정", + "description": "지출금액을 7,000,000원으로 변경", + "actions": [ + { + "type": "click", + "target": "지출금액 필드" + }, + { + "type": "clear", + "target": "지출금액 필드" + }, + { + "type": "type", + "target": "지출금액 필드", + "value": "7000000" + } + ], + "expected": { + "fieldValue": "7000000", + "valueChanged": true + } + }, + { + "id": "step-19", + "name": "적요 수정", + "description": "적요를 '테스트 예상비용 수정'으로 변경", + "actions": [ + { + "type": "click", + "target": "적요/메모 필드" + }, + { + "type": "clear", + "target": "적요/메모 필드" + }, + { + "type": "type", + "target": "적요/메모 필드", + "value": "테스트 예상비용 수정" + } + ], + "expected": { + "fieldValue": "테스트 예상비용 수정", + "textChanged": true + } + }, + { + "id": "step-20", + "name": "수정 버튼 클릭 및 저장", + "description": "모달 내 수정 버튼 클릭하여 변경사항 저장", + "actions": [ + { + "type": "click", + "target": "수정 버튼 (모달 내)" + }, + { + "type": "wait", + "target": "저장 처리 완료" + } + ], + "expected": { + "apiCall": "PUT /api/accounting/expected-expenses/:id", + "apiResponse": "200 OK", + "successToast": "예상비용이 수정되었습니다", + "modalClosed": true + } + }, + { + "id": "step-21", + "name": "수정 결과 확인", + "description": "테이블에서 수정된 데이터 반영 확인", + "actions": [ + { + "type": "wait", + "target": "테이블 리로드" + }, + { + "type": "verify", + "target": "수정된 데이터 행" + } + ], + "expected": { + "updatedRowVisible": true, + "dataMatches": { + "amount": "7,000,000원", + "note": "테스트 예상비용 수정" + } + } + }, + { + "id": "step-22", + "name": "체크박스 선택 (단일)", + "description": "방금 수정한 데이터의 체크박스 선택", + "actions": [ + { + "type": "click", + "target": "수정된 행의 체크박스" + } + ], + "expected": { + "checkboxChecked": true, + "selectedCount": 1, + "bulkButtonsEnabled": true + } + }, + { + "id": "step-23", + "name": "예상 지급일 변경 버튼 클릭", + "description": "일괄 작업: 예상 지급일 변경 다이얼로그 열기", + "actions": [ + { + "type": "click", + "target": "예상 지급일 변경 버튼" + }, + { + "type": "wait", + "target": "다이얼로그 표시" + } + ], + "expected": { + "dialogTitle": "예상 지급일 변경", + "dialogVisible": true, + "datepickerVisible": true, + "selectedItemsCount": "1건" + } + }, + { + "id": "step-24", + "name": "새로운 예상 지급일 선택", + "description": "DatePicker로 새로운 예상 지급일 선택 (2026-02-15)", + "actions": [ + { + "type": "click", + "target": "날짜 선택 필드" + }, + { + "type": "selectDate", + "target": "캘린더", + "value": "2026-02-15" + } + ], + "expected": { + "dateSelected": "2026-02-15", + "confirmButtonEnabled": true + } + }, + { + "id": "step-25", + "name": "예상 지급일 변경 확인", + "description": "확인 버튼 클릭하여 예상 지급일 일괄 변경", + "actions": [ + { + "type": "click", + "target": "확인 버튼" + }, + { + "type": "wait", + "target": "처리 완료" + } + ], + "expected": { + "apiCall": "PUT /api/accounting/expected-expenses/bulk-update-date", + "apiResponse": "200 OK", + "successToast": "1건의 예상 지급일이 변경되었습니다", + "dialogClosed": true + } + }, + { + "id": "step-26", + "name": "예상 지급일 변경 결과 확인", + "description": "테이블에서 예상 지급일이 변경되었는지 확인", + "actions": [ + { + "type": "wait", + "target": "테이블 리로드" + }, + { + "type": "verify", + "target": "변경된 데이터 행" + } + ], + "expected": { + "expectedPaymentDate": "2026-02-15", + "dateUpdated": true + } + }, + { + "id": "step-27", + "name": "전자결재 버튼 클릭", + "description": "일괄 작업: 전자결재 신청 (체크박스 선택 유지 상태)", + "actions": [ + { + "type": "verify", + "target": "체크박스 선택 상태 (여전히 선택됨)" + }, + { + "type": "click", + "target": "전자결재 버튼" + }, + { + "type": "wait", + "target": "처리 완료" + } + ], + "expected": { + "apiCall": "POST /api/accounting/expected-expenses/approval", + "apiResponse": "200 OK 또는 404/500 (미구현 가능)", + "resultToast": "전자결재가 신청되었습니다 또는 에러 메시지" + } + }, + { + "id": "step-28", + "name": "전자결재 결과 확인", + "description": "테이블에서 전자결재 상태 변경 확인", + "actions": [ + { + "type": "wait", + "target": "테이블 리로드" + }, + { + "type": "verify", + "target": "전자결재 상태 컬럼" + } + ], + "expected": { + "approvalStatus": "결재대기 또는 미신청 (API 미구현 시)", + "badgeVisible": true + } + }, + { + "id": "step-29", + "name": "추가 데이터 등록 (일괄삭제 테스트용)", + "description": "일괄삭제 테스트를 위해 추가 데이터 1건 더 등록", + "actions": [ + { + "type": "click", + "target": "등록 버튼" + }, + { + "type": "wait", + "target": "모달 표시" + }, + { + "type": "fillForm", + "target": "등록 폼", + "data": { + "expectedPaymentDate": "2026-03-01", + "transactionType": "급여", + "amount": "3000000", + "vendorName": "삭제테스트거래처", + "note": "일괄삭제 테스트용" + } + }, + { + "type": "click", + "target": "등록 버튼 (모달 내)" + }, + { + "type": "wait", + "target": "저장 완료" + } + ], + "expected": { + "successToast": "예상비용이 등록되었습니다", + "newRowVisible": true + } + }, + { + "id": "step-30", + "name": "복수 체크박스 선택", + "description": "일괄삭제를 위해 2개 행 체크박스 선택", + "actions": [ + { + "type": "click", + "target": "첫 번째 테스트 데이터 체크박스" + }, + { + "type": "click", + "target": "두 번째 테스트 데이터 체크박스" + } + ], + "expected": { + "selectedCount": 2, + "bulkDeleteButtonEnabled": true + } + }, + { + "id": "step-31", + "name": "일괄삭제 버튼 클릭", + "description": "일괄삭제 버튼 클릭하여 확인 다이얼로그 표시", + "actions": [ + { + "type": "click", + "target": "일괄삭제 버튼" + }, + { + "type": "wait", + "target": "확인 다이얼로그 표시" + } + ], + "expected": { + "dialogTitle": "일괄삭제 확인", + "dialogMessage": "2건의 예상비용을 삭제하시겠습니까?", + "confirmButtonVisible": true, + "cancelButtonVisible": true + } + }, + { + "id": "step-32", + "name": "일괄삭제 취소", + "description": "취소 버튼 클릭하여 삭제 취소 (워크플로우 분기 테스트)", + "actions": [ + { + "type": "click", + "target": "취소 버튼" + } + ], + "expected": { + "dialogClosed": true, + "dataPreserved": true, + "selectedItemsStillChecked": true + } + }, + { + "id": "step-33", + "name": "일괄삭제 재시도", + "description": "일괄삭제 버튼 다시 클릭", + "actions": [ + { + "type": "click", + "target": "일괄삭제 버튼" + }, + { + "type": "wait", + "target": "확인 다이얼로그 표시" + } + ], + "expected": { + "dialogVisible": true + } + }, + { + "id": "step-34", + "name": "일괄삭제 확인", + "description": "확인 버튼 클릭하여 2건 일괄 삭제 실행", + "actions": [ + { + "type": "click", + "target": "확인 버튼" + }, + { + "type": "wait", + "target": "삭제 처리 완료" + } + ], + "expected": { + "apiCall": "DELETE /api/accounting/expected-expenses/bulk", + "apiResponse": "200 OK", + "successToast": "2건이 삭제되었습니다", + "dialogClosed": true + } + }, + { + "id": "step-35", + "name": "일괄삭제 결과 확인", + "description": "테이블에서 삭제된 데이터가 사라졌는지 확인", + "actions": [ + { + "type": "wait", + "target": "테이블 리로드" + }, + { + "type": "verify", + "target": "삭제된 행 부재" + } + ], + "expected": { + "deletedRowsGone": true, + "tableUpdated": true, + "selectedItemsCleared": true + } + }, + { + "id": "step-36", + "name": "단일 삭제 테스트 준비", + "description": "단일 삭제 테스트를 위해 새 데이터 1건 등록", + "actions": [ + { + "type": "click", + "target": "등록 버튼" + }, + { + "type": "fillForm", + "target": "등록 폼", + "data": { + "expectedPaymentDate": "2026-04-01", + "transactionType": "기타", + "amount": "1000000", + "note": "단일삭제 테스트용" + } + }, + { + "type": "click", + "target": "등록 버튼 (모달 내)" + }, + { + "type": "wait", + "target": "저장 완료" + } + ], + "expected": { + "newRowVisible": true + } + }, + { + "id": "step-37", + "name": "단일 삭제 아이콘 클릭", + "description": "방금 등록한 데이터의 삭제 아이콘 클릭", + "actions": [ + { + "type": "click", + "target": "신규 등록 행의 삭제 아이콘" + }, + { + "type": "wait", + "target": "확인 다이얼로그 표시" + } + ], + "expected": { + "dialogTitle": "삭제 확인", + "dialogMessage": "이 예상비용을 삭제하시겠습니까?", + "confirmButtonVisible": true + } + }, + { + "id": "step-38", + "name": "단일 삭제 확인", + "description": "확인 버튼 클릭하여 단일 삭제 실행", + "actions": [ + { + "type": "click", + "target": "확인 버튼" + }, + { + "type": "wait", + "target": "삭제 처리 완료" + } + ], + "expected": { + "apiCall": "DELETE /api/accounting/expected-expenses/:id", + "apiResponse": "200 OK", + "successToast": "삭제되었습니다", + "dialogClosed": true + } + }, + { + "id": "step-39", + "name": "단일 삭제 결과 확인", + "description": "테이블에서 삭제된 데이터 확인", + "actions": [ + { + "type": "wait", + "target": "테이블 리로드" + }, + { + "type": "verify", + "target": "삭제된 행 부재" + } + ], + "expected": { + "deletedRowGone": true, + "tableUpdated": true + } + }, + { + "id": "step-40", + "name": "필터 기능 테스트 - 거래유형", + "description": "거래유형 필터 선택하여 데이터 필터링 확인", + "actions": [ + { + "type": "click", + "target": "거래유형 필터" + }, + { + "type": "select", + "target": "옵션", + "value": "매입" + }, + { + "type": "wait", + "target": "데이터 필터링" + } + ], + "expected": { + "filteredData": "매입 거래유형만 표시", + "tableUpdated": true + } + }, + { + "id": "step-41", + "name": "필터 기능 테스트 - 지급상태", + "description": "지급상태 필터 선택하여 데이터 필터링 확인", + "actions": [ + { + "type": "click", + "target": "지급상태 필터" + }, + { + "type": "select", + "target": "옵션", + "value": "미지급" + }, + { + "type": "wait", + "target": "데이터 필터링" + } + ], + "expected": { + "filteredData": "미지급 상태만 표시", + "tableUpdated": true + } + }, + { + "id": "step-42", + "name": "필터 초기화", + "description": "필터를 '전체'로 변경하여 초기화", + "actions": [ + { + "type": "click", + "target": "거래유형 필터" + }, + { + "type": "select", + "target": "전체" + }, + { + "type": "click", + "target": "지급상태 필터" + }, + { + "type": "select", + "target": "전체" + } + ], + "expected": { + "allDataVisible": true, + "filtersCleared": true + } + } + ], + "cleanup": { + "description": "테스트 후 생성된 테스트 데이터 정리 (필요시)", + "actions": [] + }, + "notes": [ + "2년 기간 설정으로 충분한 데이터 범위 확보", + "등록 모달의 모든 입력 필드 (datepicker, select, combobox, input, textarea) 테스트 필수", + "수정 모달의 기존 데이터 프리필 확인 필수", + "일괄 작업 (예상 지급일 변경, 전자결재, 일괄삭제) 모두 테스트", + "워크플로우 분기: 삭제 취소 → 재시도 → 확인 패턴 검증", + "단일 삭제와 일괄삭제 모두 테스트", + "필터 기능 (거래유형, 지급상태) 동작 확인", + "전자결재 기능은 API 미구현 가능성 있음 (404 에러 예상)" + ], + "expectedAPIs": [ + "GET /api/accounting/expected-expenses - 목록 조회", + "POST /api/accounting/expected-expenses - 등록", + "PUT /api/accounting/expected-expenses/:id - 수정", + "DELETE /api/accounting/expected-expenses/:id - 단일 삭제", + "DELETE /api/accounting/expected-expenses/bulk - 일괄 삭제", + "PUT /api/accounting/expected-expenses/bulk-update-date - 예상 지급일 일괄 변경", + "POST /api/accounting/expected-expenses/approval - 전자결재 신청", + "GET /api/clients - 거래처 목록", + "GET /api/bank-accounts - 계좌 목록" + ] +} diff --git a/faq.json b/faq.json new file mode 100644 index 0000000..2f7dcea --- /dev/null +++ b/faq.json @@ -0,0 +1,192 @@ +{ + "id": "faq", + "name": "FAQ 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "고객센터 > FAQ 페이지의 카테고리별 조회, 아코디언 펼치기/접기 기능을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/support/faq", + "menuNavigation": { + "level1": "고객센터", + "level2": "FAQ", + "expectedUrl": "/support/faq" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebar": { + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 5, + "waitAfterScroll": 300 + }, + "level1": { + "text": "고객센터", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "FAQ", + "waitAfterClick": 300 + }, + "fallbackUrl": "/support/faq", + "expectedUrl": "/support/faq" + }, + "timeout": 90000, + "tags": ["support", "faq", "read-only"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "사이드바를 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 500 } + ] + }, + { + "id": "step-1", + "name": "고객센터 메뉴 진입", + "description": "고객센터 > FAQ 메뉴로 이동", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "고객센터", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "고객센터" }, + { "type": "wait", "duration": 500 }, + { "type": "click", "target": "FAQ" } + ], + "expect": { + "url": "/support/faq", + "visible": ["FAQ"] + }, + "fallback": { + "type": "navigate", + "url": "/support/faq" + } + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "FAQ 페이지 카테고리 탭 확인", + "verify": { + "visible": ["FAQ"], + "categoryTabs": ["전체", "계정", "결제", "서비스", "인사관리", "회계", "기타"] + } + }, + { + "id": "step-3", + "name": "필수 검증 #3: 카테고리 탭 전환 - 계정", + "description": "계정 카테고리 탭 클릭하여 필터링 확인", + "actions": [ + { "type": "click", "target": "계정", "role": "tab" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "tabActive": "계정", + "dataFiltered": true + } + }, + { + "id": "step-4", + "name": "필수 검증 #3: 카테고리 탭 전환 - 서비스", + "description": "서비스 카테고리 탭 클릭하여 필터링 확인", + "actions": [ + { "type": "click", "target": "서비스", "role": "tab" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "tabActive": "서비스", + "dataFiltered": true + } + }, + { + "id": "step-5", + "name": "전체 탭으로 복귀", + "description": "전체 탭 클릭하여 모든 FAQ 표시", + "actions": [ + { "type": "click", "target": "전체", "role": "tab" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "tabActive": "전체", + "allDataShown": true + } + }, + { + "id": "step-6", + "name": "FAQ 항목 펼치기", + "description": "FAQ 질문 클릭하여 답변 펼치기", + "actions": [ + { "type": "click", "target": "FAQ 사용방법" } + ], + "expect": { + "accordionExpanded": true, + "answerVisible": true + } + }, + { + "id": "step-7", + "name": "FAQ 항목 접기", + "description": "펼쳐진 FAQ 다시 클릭하여 접기", + "actions": [ + { "type": "click", "target": "FAQ 사용방법" } + ], + "expect": { + "accordionCollapsed": true + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/support/faq", + "message": "FAQ 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "text=FAQ", + "message": "FAQ 제목이 표시되어야 함" + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 3, + "name": "검색/필터", + "trigger": "카테고리 탭", + "verification": "데이터 변화 확인", + "failCondition": "필터 적용 후 데이터 무변화" + } + ] + }, + + "notes": { + "testScope": "FAQ 카테고리 필터 → 아코디언 펼치기/접기 테스트", + "pageType": "조회 전용 (일반 사용자 등록/수정/삭제 불가)", + "categories": ["전체", "계정", "결제", "서비스", "인사관리", "회계", "기타"], + "faqFormat": "아코디언 (질문 클릭 시 답변 펼침)", + "prerequisites": "로그인된 사용자" + } +} diff --git a/free-board.json b/free-board.json new file mode 100644 index 0000000..ffd9202 --- /dev/null +++ b/free-board.json @@ -0,0 +1,776 @@ +{ + "id": "free-board", + "name": "자유게시판 E2E 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "자유게시판의 목록, 게시글 작성, 상세, 수정, 삭제, 댓글 CRUD 전체 워크플로우 테스트", + "url": "/ko/boards/free", + "menuNavigation": { + "level1": "게시판", + "level2": "자유게시판", + "expectedUrl": "/ko/boards/free" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "level1": { + "text": "게시판", + "scrollContainer": ".sidebar-scroll, [data-sidebar='content'], nav", + "maxScrollAttempts": 5, + "scrollStep": 200 + }, + "level2": { + "text": "자유게시판", + "waitAfterLevel1Click": 500 + }, + "expectedUrl": "/ko/boards/free", + "fallbackUrl": "/ko/boards/free" + }, + "steps": [ + { + "step": 0, + "name": "사이드바 초기화", + "description": "메뉴 탐색 전 사이드바를 최상단으로 스크롤", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll, [data-sidebar=\"content\"], nav')?.scrollTo({top: 0, behavior: 'instant'})" + }, + { + "type": "wait", + "duration": 500 + } + ] + }, + { + "step": 1, + "name": "2단계 메뉴 진입: 게시판 > 자유게시판", + "description": "게시판 > 자유게시판 메뉴로 이동하여 페이지 로드 확인 (scrollAndFind 패턴)", + "actions": [ + { + "type": "scrollAndFind", + "target": "게시판", + "container": ".sidebar-scroll, [data-sidebar='content'], nav", + "maxAttempts": 5, + "scrollStep": 200 + }, + { + "type": "click", + "target": "게시판" + }, + { + "type": "wait", + "duration": 500 + }, + { + "type": "click", + "target": "자유게시판" + }, + { + "type": "wait", + "target": "페이지 로드 완료" + } + ], + "verification": { + "title_contains": "자유게시판", + "elements": ["table", "button:has-text('글쓰기')"] + } + }, + { + "step": 2, + "name": "초기 게시글 목록 확인", + "action": "verify_table_structure", + "verification": { + "columns": ["No.", "제목", "작성자", "조회수", "상태", "등록일"], + "min_rows": 0 + } + }, + { + "step": 3, + "name": "게시글 총 건수 확인", + "action": "verify_text", + "verification": { + "text_pattern": "총 \\d+건" + } + }, + { + "step": 4, + "name": "검색 기능 확인 (검색창 존재)", + "action": "verify_element", + "target": "input[placeholder*='제목']", + "verification": { + "exists": true + } + }, + { + "step": 5, + "name": "필터 드롭다운 확인 (상태)", + "action": "verify_element", + "target": "select, [role='combobox']:has-text('상태')", + "verification": { + "exists": true + } + }, + { + "step": 6, + "name": "정렬 드롭다운 확인", + "action": "verify_element", + "target": "select, [role='combobox']:has-text('최신순')", + "verification": { + "exists": true + } + }, + { + "step": 7, + "name": "날짜 범위 선택기 확인", + "action": "verify_element", + "target": "input[type='date']", + "verification": { + "count": 2 + } + }, + { + "step": 8, + "name": "검색 테스트 (제목)", + "action": "fill_and_wait", + "target": "input[placeholder*='제목']", + "value": "테스트", + "verification": { + "wait_for_data_change": true + } + }, + { + "step": 9, + "name": "검색 결과 확인", + "action": "verify_table_data", + "verification": { + "filtered": true + } + }, + { + "step": 10, + "name": "검색어 초기화", + "action": "fill", + "target": "input[placeholder*='제목']", + "value": "" + }, + { + "step": 11, + "name": "상태 필터 테스트 (게시됨)", + "action": "select_dropdown", + "target": "[role='combobox']:has-text('전체')", + "value": "게시됨", + "verification": { + "wait_for_data_change": true + } + }, + { + "step": 12, + "name": "상태 필터 초기화 (전체)", + "action": "select_dropdown", + "target": "[role='combobox']", + "value": "전체" + }, + { + "step": 13, + "name": "정렬 변경 (오래된순)", + "action": "select_dropdown", + "target": "[role='combobox']:has-text('최신순')", + "value": "오래된순", + "verification": { + "wait_for_data_change": true + } + }, + { + "step": 14, + "name": "정렬 복원 (최신순)", + "action": "select_dropdown", + "target": "[role='combobox']", + "value": "최신순" + }, + { + "step": 15, + "name": "글쓰기 버튼 클릭", + "action": "click", + "target": "button:has-text('글쓰기')" + }, + { + "step": 16, + "name": "게시글 작성 페이지 진입 확인", + "action": "verify_url", + "verification": { + "url_pattern": "/ko/boards/free/create", + "title_contains": "자유게시판" + } + }, + { + "step": 17, + "name": "제목 필드 확인", + "action": "verify_element", + "target": "input#title", + "verification": { + "exists": true, + "required": true + } + }, + { + "step": 18, + "name": "내용 필드 확인", + "action": "verify_element", + "target": "textarea#content", + "verification": { + "exists": true, + "required": true + } + }, + { + "step": 19, + "name": "비밀글 체크박스 확인", + "action": "verify_element", + "target": "input#isSecret", + "verification": { + "exists": true + } + }, + { + "step": 20, + "name": "게시글 제목 입력", + "action": "fill", + "target": "input#title", + "value": "E2E 테스트 게시글" + }, + { + "step": 21, + "name": "게시글 내용 입력", + "action": "fill", + "target": "textarea#content", + "value": "이것은 E2E 자동화 테스트를 위한 게시글입니다." + }, + { + "step": 22, + "name": "현재 URL 저장 (등록 전)", + "action": "save_url", + "variable": "url_before_submit" + }, + { + "step": 23, + "name": "게시글 등록 버튼 클릭", + "action": "click", + "target": "button:has-text('등록')" + }, + { + "step": 24, + "name": "게시글 등록 완료 (URL 안정성 검증)", + "action": "verify_url_stability", + "critical": true, + "verification": { + "expected_url_pattern": "/ko/boards/free/\\d+", + "no_404": true, + "no_error_page": true, + "success_condition": "url_changed_to_detail" + }, + "notes": "필수 검증 #2: 등록 후 상세 페이지로 이동해야 하며 404 에러 페이지가 나오면 안 됨" + }, + { + "step": 25, + "name": "게시글 상세 페이지 진입 확인", + "action": "verify_page", + "verification": { + "title": "E2E 테스트 게시글", + "content_contains": "E2E 자동화 테스트" + } + }, + { + "step": 26, + "name": "게시글 ID 저장", + "action": "extract_from_url", + "pattern": "/ko/boards/free/(\\d+)", + "variable": "post_id" + }, + { + "step": 27, + "name": "작성자 정보 표시 확인", + "action": "verify_element", + "target": "text=/\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}/", + "verification": { + "exists": true + } + }, + { + "step": 28, + "name": "조회수 표시 확인", + "action": "verify_element", + "target": "svg.lucide-eye", + "verification": { + "exists": true + } + }, + { + "step": 29, + "name": "수정 버튼 존재 확인 (작성자)", + "action": "verify_element", + "target": "button:has-text('수정')", + "verification": { + "exists": true + } + }, + { + "step": 30, + "name": "삭제 버튼 존재 확인 (작성자)", + "action": "verify_element", + "target": "button:has-text('삭제')", + "verification": { + "exists": true + } + }, + { + "step": 31, + "name": "댓글 섹션 확인", + "action": "verify_element", + "target": "text=/댓글 \\(\\d+\\)/", + "verification": { + "exists": true + } + }, + { + "step": 32, + "name": "댓글 입력란 확인", + "action": "verify_element", + "target": "textarea[placeholder*='댓글']", + "verification": { + "exists": true + } + }, + { + "step": 33, + "name": "첫 번째 댓글 작성", + "action": "fill", + "target": "textarea[placeholder*='댓글']", + "value": "첫 번째 테스트 댓글입니다." + }, + { + "step": 34, + "name": "댓글 등록 버튼 클릭", + "action": "click", + "target": "button:has-text('댓글 등록')" + }, + { + "step": 35, + "name": "댓글 등록 확인", + "action": "verify_text", + "verification": { + "text": "첫 번째 테스트 댓글입니다.", + "exists": true + } + }, + { + "step": 36, + "name": "댓글 수 업데이트 확인", + "action": "verify_element", + "target": "text=/댓글 \\(1\\)/", + "verification": { + "exists": true + } + }, + { + "step": 37, + "name": "두 번째 댓글 작성", + "action": "fill", + "target": "textarea[placeholder*='댓글']", + "value": "두 번째 테스트 댓글입니다." + }, + { + "step": 38, + "name": "두 번째 댓글 등록", + "action": "click", + "target": "button:has-text('댓글 등록')" + }, + { + "step": 39, + "name": "댓글 수 업데이트 확인 (2개)", + "action": "verify_element", + "target": "text=/댓글 \\(2\\)/", + "verification": { + "exists": true + } + }, + { + "step": 40, + "name": "댓글 수정 버튼 클릭 (첫 번째 댓글)", + "action": "click_nth", + "target": "button:has-text('수정')", + "nth": 0 + }, + { + "step": 41, + "name": "댓글 수정 입력란 확인", + "action": "verify_element", + "target": "textarea", + "verification": { + "count": 2, + "notes": "댓글 입력란 + 수정 입력란" + } + }, + { + "step": 42, + "name": "댓글 내용 수정", + "action": "fill_nth", + "target": "textarea", + "nth": 0, + "value": "수정된 첫 번째 댓글입니다." + }, + { + "step": 43, + "name": "댓글 수정 저장", + "action": "click", + "target": "button:has-text('저장')" + }, + { + "step": 44, + "name": "댓글 수정 확인", + "action": "verify_text", + "verification": { + "text": "수정된 첫 번째 댓글입니다.", + "exists": true + } + }, + { + "step": 45, + "name": "댓글 삭제 버튼 클릭 (두 번째 댓글)", + "action": "click_nth", + "target": "button:has-text('삭제')", + "nth": 1 + }, + { + "step": 46, + "name": "댓글 삭제 확인", + "action": "verify_text", + "verification": { + "text": "두 번째 테스트 댓글입니다.", + "exists": false + } + }, + { + "step": 47, + "name": "댓글 수 업데이트 확인 (1개)", + "action": "verify_element", + "target": "text=/댓글 \\(1\\)/", + "verification": { + "exists": true + } + }, + { + "step": 48, + "name": "게시글 수정 버튼 클릭", + "action": "click", + "target": "button:has-text('수정')" + }, + { + "step": 49, + "name": "게시글 수정 페이지 진입 확인", + "action": "verify_url", + "verification": { + "url_pattern": "/ko/boards/free/\\d+\\?mode=edit" + } + }, + { + "step": 50, + "name": "제목 필드에 기존 값 확인", + "action": "verify_input_value", + "target": "input#title", + "verification": { + "value": "E2E 테스트 게시글" + } + }, + { + "step": 51, + "name": "제목 수정", + "action": "fill", + "target": "input#title", + "value": "E2E 테스트 게시글 (수정됨)" + }, + { + "step": 52, + "name": "내용 수정", + "action": "fill", + "target": "textarea#content", + "value": "수정된 내용입니다. E2E 자동화 테스트를 위한 게시글입니다." + }, + { + "step": 53, + "name": "비밀글 체크", + "action": "check", + "target": "input#isSecret" + }, + { + "step": 54, + "name": "현재 URL 저장 (수정 전)", + "action": "save_url", + "variable": "url_before_update" + }, + { + "step": 55, + "name": "수정 저장 버튼 클릭", + "action": "click", + "target": "button[type='submit']:has-text('저장'), button:has-text('수정')" + }, + { + "step": 56, + "name": "게시글 수정 완료 (URL 안정성 검증)", + "action": "verify_url_stability", + "critical": true, + "verification": { + "expected_url_pattern": "/ko/boards/free/\\d+", + "no_404": true, + "no_error_page": true, + "success_condition": "url_back_to_detail" + }, + "notes": "필수 검증 #2: 수정 후 상세 페이지로 돌아가야 하며 404 에러 페이지가 나오면 안 됨" + }, + { + "step": 57, + "name": "수정된 제목 확인", + "action": "verify_text", + "verification": { + "text": "E2E 테스트 게시글 (수정됨)", + "exists": true + } + }, + { + "step": 58, + "name": "수정된 내용 확인", + "action": "verify_text", + "verification": { + "text": "수정된 내용입니다", + "exists": true + } + }, + { + "step": 59, + "name": "목록으로 이동 버튼 클릭", + "action": "click", + "target": "button:has-text('목록으로')" + }, + { + "step": 60, + "name": "목록 페이지 복귀 확인", + "action": "verify_url", + "verification": { + "url": "/ko/boards/free" + } + }, + { + "step": 61, + "name": "수정된 게시글 목록 확인", + "action": "verify_text", + "verification": { + "text": "E2E 테스트 게시글 (수정됨)", + "exists": true + } + }, + { + "step": 62, + "name": "게시글 클릭하여 상세 진입", + "action": "click", + "target": "text=E2E 테스트 게시글 (수정됨)" + }, + { + "step": 63, + "name": "상세 페이지 진입 확인", + "action": "verify_url", + "verification": { + "url_pattern": "/ko/boards/free/\\d+" + } + }, + { + "step": 64, + "name": "조회수 증가 확인", + "action": "verify_element", + "target": "svg.lucide-eye ~ text", + "verification": { + "text_pattern": "\\d+", + "notes": "조회수가 표시되는지 확인" + } + }, + { + "step": 65, + "name": "게시글 삭제 버튼 클릭", + "action": "click", + "target": "button:has-text('삭제')" + }, + { + "step": 66, + "name": "삭제 확인 다이얼로그 표시 확인", + "action": "verify_dialog", + "verification": { + "title": "게시글 삭제", + "content_contains": "삭제하시겠습니까" + } + }, + { + "step": 67, + "name": "현재 URL 저장 (삭제 전)", + "action": "save_url", + "variable": "url_before_delete" + }, + { + "step": 68, + "name": "삭제 확인 버튼 클릭", + "action": "click", + "target": "button:has-text('삭제'):last-of-type" + }, + { + "step": 69, + "name": "게시글 삭제 완료 (URL 안정성 검증)", + "action": "verify_url_stability", + "critical": true, + "verification": { + "expected_url": "/ko/boards/free", + "no_404": true, + "no_error_page": true, + "success_condition": "url_back_to_list" + }, + "notes": "필수 검증 #2: 삭제 후 목록 페이지로 돌아가야 하며 404 에러 페이지가 나오면 안 됨" + }, + { + "step": 70, + "name": "목록 페이지 복귀 확인", + "action": "verify_url", + "verification": { + "url": "/ko/boards/free" + } + }, + { + "step": 71, + "name": "삭제된 게시글 목록에서 제거 확인", + "action": "verify_text", + "verification": { + "text": "E2E 테스트 게시글 (수정됨)", + "exists": false + } + }, + { + "step": 72, + "name": "페이지네이션 존재 확인 (조건부)", + "action": "verify_element", + "target": "nav[aria-label='pagination']", + "verification": { + "exists": "if_data_gt_10", + "notes": "10개 이상일 때만 페이지네이션 표시" + } + }, + { + "step": 73, + "name": "체크박스 선택 테스트 (첫 번째 항목)", + "action": "check_nth", + "target": "input[type='checkbox']", + "nth": 1, + "verification": { + "notes": "0번은 전체 선택, 1번은 첫 번째 데이터" + } + }, + { + "step": 74, + "name": "체크박스 선택 해제", + "action": "uncheck_nth", + "target": "input[type='checkbox']", + "nth": 1 + }, + { + "step": 75, + "name": "전체 선택 체크박스 클릭", + "action": "check_nth", + "target": "input[type='checkbox']", + "nth": 0 + }, + { + "step": 76, + "name": "전체 선택 해제", + "action": "uncheck_nth", + "target": "input[type='checkbox']", + "nth": 0 + }, + { + "step": 77, + "name": "콘솔 에러 확인", + "action": "verify_console", + "verification": { + "no_errors": true + } + } + ], + "expectedAPIs": [ + { + "endpoint": "GET /api/v1/boards/free", + "description": "자유게시판 정보 조회" + }, + { + "endpoint": "GET /api/v1/boards/free/posts", + "description": "게시글 목록 조회 (per_page=100)" + }, + { + "endpoint": "POST /api/v1/boards/free/posts", + "description": "게시글 등록", + "payload": { + "title": "string", + "content": "string", + "is_secret": "boolean" + } + }, + { + "endpoint": "GET /api/v1/boards/free/posts/{id}", + "description": "게시글 상세 조회 (조회수 증가)" + }, + { + "endpoint": "GET /api/v1/boards/free/posts/{id}/comments", + "description": "댓글 목록 조회" + }, + { + "endpoint": "POST /api/v1/boards/free/posts/{id}/comments", + "description": "댓글 등록", + "payload": { + "content": "string" + } + }, + { + "endpoint": "PUT /api/v1/boards/free/posts/{id}/comments/{commentId}", + "description": "댓글 수정", + "payload": { + "content": "string" + } + }, + { + "endpoint": "DELETE /api/v1/boards/free/posts/{id}/comments/{commentId}", + "description": "댓글 삭제" + }, + { + "endpoint": "PUT /api/v1/boards/free/posts/{id}", + "description": "게시글 수정", + "payload": { + "title": "string", + "content": "string", + "is_secret": "boolean" + } + }, + { + "endpoint": "DELETE /api/v1/boards/free/posts/{id}", + "description": "게시글 삭제" + } + ], + "notes": [ + "자유게시판은 boardCode='free'를 사용하는 동적 게시판입니다.", + "게시글 등록/수정/삭제 시 반드시 URL 안정성 검증 수행 (필수 검증 #2)", + "댓글 CRUD 기능 모두 테스트해야 합니다.", + "IntegratedListTemplateV2 템플릿 사용으로 반응형 디자인 (데스크톱/모바일)", + "페이지네이션은 10개 단위로 동작 (10개 미만 시 미표시)", + "검색은 제목, 작성자명으로 필터링됩니다.", + "상태 필터: 전체, 게시됨, 임시저장", + "정렬: 최신순, 오래된순", + "조회수는 게시글 상세 조회 시마다 증가합니다.", + "작성자만 수정/삭제 버튼이 표시됩니다.", + "댓글도 작성자만 수정/삭제 가능합니다.", + "비밀글 체크 시 is_secret=true로 전송됩니다.", + "게시글 내용은 HTML로 저장되며 dangerouslySetInnerHTML로 렌더링됩니다." + ] +} diff --git a/inspection-management.json b/inspection-management.json new file mode 100644 index 0000000..209a252 --- /dev/null +++ b/inspection-management.json @@ -0,0 +1,348 @@ +{ + "id": "inspection-management", + "name": "검사관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "품질관리 > 검사관리 페이지의 검사 등록/조회/수정/삭제 기능을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/quality/inspection", + "menuNavigation": { + "level1": "품질관리", + "level2": "검사관리", + "expectedUrl": "/quality/inspection" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebar": { + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 5, + "waitAfterScroll": 300 + }, + "level1": { + "text": "품질관리", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "검사관리", + "waitAfterClick": 300 + }, + "fallbackUrl": "/quality/inspection", + "expectedUrl": "/quality/inspection" + }, + "timeout": 90000, + "tags": ["quality", "inspection", "crud"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "testData": { + "inspection": { + "quantity": "100", + "worker": "홍킬동", + "note": "E2E 테스트 특이사항", + "height": "50.5", + "length": "120.3" + } + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 500 } + ], + "expect": { + "sidebarReady": true + } + }, + { + "id": "step-1", + "name": "품질관리 메뉴 진입", + "description": "품질관리 > 검사관리 메뉴로 이동", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "품질관리", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "품질관리" }, + { "type": "wait", "duration": 500 }, + { "type": "click", "target": "검사관리" } + ], + "expect": { + "url": "/quality/inspection", + "visible": ["검사 목록", "품질검사 관리", "검사 등록"] + }, + "fallback": { + "type": "navigate", + "url": "/quality/inspection" + } + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "통계 카드와 테이블 구조 확인", + "verify": { + "visible": ["금일 대기 건수", "진행 중 검사", "금일 완료 건수", "불량 발생률"], + "tableColumns": ["번호", "검사유형", "요청일", "품목명", "LOT NO", "상태", "담당자"] + } + }, + { + "id": "step-3", + "name": "탭 기능 확인", + "description": "전체/대기/진행중/완료 탭 동작 확인", + "actions": [ + { "type": "click", "target": "대기", "role": "tab" }, + { "type": "wait", "duration": 300 }, + { "type": "click", "target": "진행중", "role": "tab" }, + { "type": "wait", "duration": 300 }, + { "type": "click", "target": "완료", "role": "tab" }, + { "type": "wait", "duration": 300 }, + { "type": "click", "target": "전체", "role": "tab" } + ], + "expect": { + "tabsWork": true + } + }, + { + "id": "step-4", + "name": "필수 검증 #2: 검사 등록 모달 열기", + "description": "검사 등록 버튼 클릭하여 모달 열기", + "actions": [ + { "type": "openModal", "target": "검사 등록", "description": "품질검사 등록 모달 열기" } + ], + "modalConfig": { + "containerSelector": "[role='dialog'], .modal", + "animationDelay": 300, + "waitForSelector": "[role='dialog']" + }, + "expect": { + "modal": "품질검사 등록", + "visible": ["LOT NO", "품목명", "공정명", "수량", "작업자", "특이사항", "검사 데이터 입력"] + } + }, + { + "id": "step-5", + "name": "검사 등록 폼 입력", + "description": "모달 내 검사 정보 입력", + "actions": [ + { "type": "fillInModal", "target": "수량", "value": "{testData.inspection.quantity}", "options": { "waitAfter": 100 } }, + { "type": "selectInModal", "target": "작업자", "value": "{testData.inspection.worker}", "options": { "waitAfter": 200 } }, + { "type": "fillInModal", "target": "특이사항", "value": "{testData.inspection.note}", "options": { "waitAfter": 100 } }, + { "type": "clickInModal", "target": "양호", "description": "가공상태 양호 선택", "options": { "waitAfter": 100 } }, + { "type": "fillInModal", "target": "높이 측정값", "value": "{testData.inspection.height}", "options": { "waitAfter": 100 } }, + { "type": "fillInModal", "target": "길이 측정값", "value": "{testData.inspection.length}", "options": { "waitAfter": 100 } } + ] + }, + { + "id": "step-6", + "name": "필수 검증 #2: 검사 등록 저장", + "description": "모달 내 등록 버튼 클릭하여 검사 저장", + "actions": [ + { "type": "clickInModal", "target": "등록", "options": { "waitAfter": 500 } } + ], + "expect": { + "urlMaintained": true, + "noErrorPage": true, + "toast": ["등록", "완료", "성공"], + "modalClosed": true + }, + "verify": { + "apiCall": "POST /api/quality/inspection" + } + }, + { + "id": "step-7", + "name": "필수 검증 #4: 등록 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 등록 확인 필수!", + "description": "테이블에서 등록된 검사 확인", + "verify": { + "tableContains": ["대기", "{testData.inspection.worker}"], + "recordCountIncreased": true + } + }, + { + "id": "step-8", + "name": "등록된 검사 상세 열기", + "description": "등록된 검사 항목 클릭하여 상세 보기", + "actions": [ + { + "type": "findRow", + "contains": "{testData.inspection.worker}", + "then": { + "type": "click", + "target": "row" + } + } + ], + "expect": { + "modal": "검사 상세", + "visible": ["수정", "삭제"] + } + }, + { + "id": "step-9", + "name": "검사 정보 수정", + "description": "모달 내 검사 정보 수정 테스트", + "actions": [ + { "type": "clickInModal", "target": "수정", "options": { "waitAfter": 300 } }, + { "type": "fillInModal", "target": "특이사항", "value": "E2E 테스트 수정됨", "options": { "waitAfter": 100 } }, + { "type": "clickInModal", "target": "저장", "options": { "waitAfter": 500 } } + ], + "expect": { + "toast": ["수정", "완료", "성공"], + "modalClosed": true + } + }, + { + "id": "step-10", + "name": "필수 검증 #4: 수정 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", + "description": "테이블에서 수정된 검사 확인", + "verify": { + "dataModified": true + } + }, + { + "id": "step-11", + "name": "검사 삭제 준비", + "description": "삭제할 검사 선택", + "actions": [ + { + "type": "findRow", + "contains": "{testData.inspection.worker}", + "then": { + "type": "click", + "target": "row" + } + } + ], + "expect": { + "modal": "검사 상세", + "visible": ["삭제"] + } + }, + { + "id": "step-12", + "name": "검사 삭제", + "description": "삭제 버튼 클릭하여 검사 삭제", + "actions": [ + { "type": "click", "target": "삭제" } + ], + "expect": { + "confirmDialog": true, + "dialogText": ["삭제", "하시겠습니까"] + } + }, + { + "id": "step-13", + "name": "삭제 확인", + "description": "삭제 확인 다이얼로그에서 확인 클릭", + "actions": [ + { "type": "click", "target": "확인" } + ], + "expect": { + "toast": ["삭제", "완료", "성공"], + "modalClosed": true + } + }, + { + "id": "step-14", + "name": "필수 검증 #4: 삭제 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 삭제 확인 필수!", + "description": "테이블에서 삭제된 검사가 없는지 확인", + "verify": { + "tableNotContains": "E2E 테스트", + "recordCountDecreased": true + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/quality/inspection", + "message": "검사관리 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "button:has-text('검사 등록')", + "message": "검사 등록 버튼이 표시되어야 함" + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 2, + "name": "등록/저장 버튼", + "trigger": "검사 등록 버튼", + "verification": "URL 유지 + 에러 페이지 없음 + 성공 토스트 + 데이터 반영", + "failCondition": "404/500 에러 페이지 이동" + }, + { + "id": 4, + "name": "모달 등록 완료", + "trigger": "검사 등록 모달", + "verification": "실제 저장 동작 + 결과 확인", + "failCondition": "열기/닫기만 테스트" + }, + { + "id": 5, + "name": "목업/미완성 페이지 감지", + "trigger": "페이지 로드 시", + "verification": "입력 필드 + 동작하는 버튼 + API 호출 확인", + "failCondition": "버튼만 있고 입력 불가, Console LOG만 출력" + } + ] + }, + + "cleanup": { + "enabled": true, + "description": "테스트 중 생성된 검사 데이터 삭제", + "actions": [ + { + "type": "deleteTestData", + "condition": "contains:E2E 테스트" + } + ] + }, + + "notes": { + "testScope": "검사 등록 → 조회 → 수정 → 삭제 전체 CRUD 테스트", + "modalFields": { + "LOT NO": "자동 생성", + "품목명": "자동 연동", + "공정명": "자동 연동", + "수량": "숫자 입력", + "작업자": "드롭다운 선택", + "특이사항": "텍스트 입력", + "가공상태": "양호/불량 선택", + "높이": "측정값 입력 (mm)", + "길이": "측정값 입력 (mm)" + }, + "prerequisites": "로그인된 사용자에게 품질검사 등록 권한 필요" + } +} diff --git a/inventory-status.json b/inventory-status.json new file mode 100644 index 0000000..c4a9275 --- /dev/null +++ b/inventory-status.json @@ -0,0 +1,242 @@ +{ + "id": "inventory-status", + "name": "재고현황 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "자재관리 > 재고현황 페이지의 재고 조회 및 엑셀 다운로드 기능을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/material/inventory", + "menuNavigation": { + "level1": "자재관리", + "level2": "재고현황", + "expectedUrl": "/material/inventory" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebar": { + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 5, + "waitAfterScroll": 300 + }, + "level1": { + "text": "자재관리", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "재고현황", + "waitAfterClick": 300 + }, + "fallbackUrl": "/material/inventory", + "expectedUrl": "/material/inventory" + }, + "timeout": 90000, + "tags": ["material", "inventory", "read-only"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 500 } + ] + }, + { + "id": "step-1", + "name": "자재관리 메뉴 진입", + "description": "자재관리 > 재고현황 메뉴로 이동", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "자재관리", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "자재관리" }, + { "type": "wait", "duration": 500 }, + { "type": "click", "target": "재고현황" } + ], + "expect": { + "url": "/material/inventory", + "visible": ["재고 목록", "엑셀 다운로드"] + }, + "fallback": { + "type": "navigate", + "url": "/material/inventory" + } + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "통계 카드와 테이블 구조 확인", + "verify": { + "visible": ["전체 품목", "정상 재고", "재고 부족", "재고 없음"], + "tableColumns": ["번호", "품목코드", "품목명", "품목유형", "단위", "재고량", "안전재고", "LOT", "상태", "위치"] + } + }, + { + "id": "step-3", + "name": "필수 검증 #3: 품목유형 탭 필터 - 원자재", + "description": "원자재 탭 클릭하여 필터링 확인", + "actions": [ + { "type": "click", "target": "원자재", "role": "tab" }, + { "type": "wait", "duration": 500 } + ], + "expect": { + "tabActive": "원자재", + "dataFiltered": true + } + }, + { + "id": "step-4", + "name": "필수 검증 #3: 품목유형 탭 필터 - 부자재", + "description": "부자재 탭 클릭하여 필터링 확인", + "actions": [ + { "type": "click", "target": "부자재", "role": "tab" }, + { "type": "wait", "duration": 500 } + ], + "expect": { + "tabActive": "부자재", + "dataFiltered": true + } + }, + { + "id": "step-5", + "name": "필수 검증 #3: 품목유형 탭 필터 - 소모품", + "description": "소모품 탭 클릭하여 필터링 확인", + "actions": [ + { "type": "click", "target": "소모품", "role": "tab" }, + { "type": "wait", "duration": 500 } + ], + "expect": { + "tabActive": "소모품", + "dataFiltered": true + } + }, + { + "id": "step-6", + "name": "전체 탭으로 복귀", + "description": "전체 탭 클릭하여 모든 재고 표시", + "actions": [ + { "type": "click", "target": "전체", "role": "tab" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "tabActive": "전체", + "allDataShown": true + } + }, + { + "id": "step-7", + "name": "필수 검증 #1: 엑셀 다운로드", + "description": "엑셀 다운로드 버튼 동작 확인", + "actions": [ + { "type": "click", "target": "엑셀 다운로드" }, + { "type": "wait", "duration": 1000 } + ], + "expect": { + "downloadTriggered": true, + "noErrorPage": true + }, + "verify": { + "apiCall": "GET /api/material/inventory/export" + } + }, + { + "id": "step-8", + "name": "재고 상세 열기", + "description": "재고 항목 클릭하여 상세 보기", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('tbody tr')?.click()" + } + ], + "expect": { + "pageOrModal": "재고 상세", + "visible": ["품목코드", "품목명", "재고량", "LOT"] + } + }, + { + "id": "step-9", + "name": "상세 닫기", + "description": "ESC 키로 상세 닫기 또는 뒤로가기", + "actions": [ + { "type": "press", "key": "Escape" }, + { "type": "wait", "duration": 300 } + ] + }, + { + "id": "step-10", + "name": "페이지네이션 확인", + "description": "페이지네이션 동작 확인", + "actions": [ + { "type": "click", "target": "다음" }, + { "type": "wait", "duration": 500 } + ], + "expect": { + "pageChanged": true + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/material/inventory", + "message": "재고현황 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "button:has-text('엑셀 다운로드')", + "message": "엑셀 다운로드 버튼이 표시되어야 함" + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 1, + "name": "파일 다운로드", + "trigger": "엑셀 다운로드 버튼", + "verification": "Network API + 실제 다운로드 확인", + "failCondition": "Console LOG만으로 PASS 금지" + }, + { + "id": 3, + "name": "검색/필터", + "trigger": "품목유형 탭 필터", + "verification": "데이터 변화 확인", + "failCondition": "필터 적용 후 데이터 무변화" + } + ] + }, + + "notes": { + "testScope": "재고현황 조회 및 필터링, 엑셀 다운로드 테스트", + "pageType": "조회 전용 (입고관리에서 재고 증가, 출하관리에서 재고 감소)", + "statsCards": ["전체 품목", "정상 재고", "재고 부족", "재고 없음"], + "typeTabs": ["전체", "원자재", "부자재", "소모품"], + "tableColumns": ["번호", "품목코드", "품목명", "품목유형", "단위", "재고량", "안전재고", "LOT", "상태", "위치"], + "prerequisites": "로그인된 사용자" + } +} diff --git a/item-management.json b/item-management.json new file mode 100644 index 0000000..24a5c85 --- /dev/null +++ b/item-management.json @@ -0,0 +1,1025 @@ +{ + "scenarioId": "item-management", + "scenarioName": "품목관리 (Item Management)", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "생산관리 - 품목관리 메뉴의 전체 기능 테스트: 품목 조회, 검색, 필터, 등록(제품/부품/소모품), 상세보기, 수정, 삭제, 페이지네이션", + "priority": "High", + "tags": ["production", "item-management", "crud", "pagination", "search", "filter"], + "baseUrl": "https://dev.codebridge-x.com", + "url": "/ko/production/screen-production", + "menuNavigation": { + "level1": "생산관리", + "level2": "품목관리", + "expectedUrl": "/ko/production/screen-production" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", + "level1": "생산관리", + "level2": "품목관리", + "alternativeLevel1Names": ["생산관리", "생산 관리", "Production", "제조관리"], + "alternativeLevel2Names": ["품목관리", "품목 관리", "Item Management", "품목", "자재관리"], + "scrollConfig": { + "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", + "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + } + }, + "testData": { + "testProduct": { + "상품명": "테스트 프리미엄 스크린", + "품목명": "TEST-SCREEN-001", + "로트약자": "TSC", + "품목상태": "활성", + "비고": "E2E 테스트용 제품", + "인정번호": "TEST-CERT-2026-001" + }, + "testConsumable": { + "품목명": "테스트 라벨", + "규격": "100x50mm", + "단위": "EA", + "비고": "E2E 테스트용 소모품" + }, + "searchKeyword": "CS-001000" + }, + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/items", + "description": "품목 목록 조회", + "expectedStatus": 200 + }, + { + "method": "POST", + "endpoint": "/api/items", + "description": "품목 등록", + "expectedStatus": 201 + }, + { + "method": "GET", + "endpoint": "/api/items/:id", + "description": "품목 상세 조회", + "expectedStatus": 200 + }, + { + "method": "PATCH", + "endpoint": "/api/items/:id", + "description": "품목 수정", + "expectedStatus": 200 + }, + { + "method": "DELETE", + "endpoint": "/api/items/:id", + "description": "품목 삭제", + "expectedStatus": 200 + } + ], + "steps": [ + { + "step": 0, + "name": "사이드바 메뉴 탐색 준비", + "description": "사이드바를 최상단으로 스크롤하고 메뉴 구조 파악", + "actions": [ + { "type": "scroll", "target": "sidebar", "direction": "top", "description": "사이드바 최상단으로 스크롤" }, + { "type": "wait", "duration": 500 } + ] + }, + { + "step": 1, + "name": "2단계 메뉴 진입: 생산관리 > 품목관리", + "description": "사이드바를 스크롤하며 생산관리 > 품목관리 메뉴를 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "생산관리", + "alternativeTexts": ["생산관리", "생산 관리", "Production", "제조관리"], + "scrollContainer": "sidebar", + "maxAttempts": 10, + "description": "스크롤하며 생산관리 메뉴 찾기" + }, + { "type": "click", "target": "생산관리", "description": "생산관리 메뉴 클릭" }, + { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, + { + "type": "scrollAndFind", + "target": "품목관리", + "alternativeTexts": ["품목관리", "품목 관리", "Item Management", "품목"], + "scrollContainer": "submenu", + "maxAttempts": 5, + "description": "서브메뉴에서 품목관리 찾기" + }, + { "type": "click", "target": "품목관리", "description": "품목관리 메뉴 클릭" }, + { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 } + ], + "expected": { + "url": "/ko/production/screen-production", + "title": "품목 관리", + "authenticated": true + }, + "validation": { + "pageTitle": "품목 관리", + "pageDescription": "제품, 부품, 부자재, 원자재, 소모품 등록 및 관리" + }, + "verification": [ + "생산관리 메뉴가 펼쳐졌는지 확인", + "품목관리 서브메뉴 클릭 성공", + "404 에러 없이 페이지 로드 완료" + ] + }, + { + "step": 2, + "name": "통계 카드 표시 확인", + "action": "verify", + "target": "statistics-cards", + "expected": "6개 통계 카드가 올바른 데이터와 함께 표시됨", + "validation": { + "cards": [ + "전체 품목", + "제품", + "부품", + "부자재", + "원자재", + "소모품" + ], + "hasNumbers": true + } + }, + { + "step": 3, + "name": "품목 등록 버튼 표시 확인", + "action": "verify", + "target": "button:품목 등록", + "expected": "품목 등록 버튼이 표시됨" + }, + { + "step": 4, + "name": "검색 입력 필드 표시 확인", + "action": "verify", + "target": "textbox:품목코드, 품목명, 규격 검색...", + "expected": "검색 입력 필드가 표시됨" + }, + { + "step": 5, + "name": "탭 필터 버튼 표시 확인", + "action": "verify", + "target": "tab-buttons", + "expected": "6개 탭 필터 버튼이 표시됨", + "validation": { + "tabs": [ + "전체", + "제품", + "부품", + "부자재", + "원자재", + "소모품" + ] + } + }, + { + "step": 6, + "name": "데이터 테이블 헤더 확인", + "action": "verify", + "target": "table-headers", + "expected": "테이블 헤더가 올바르게 표시됨", + "validation": { + "columns": [ + "체크박스", + "번호", + "품목코드", + "품목유형", + "품목명", + "규격", + "단위", + "품목상태", + "액션" + ] + } + }, + { + "step": 7, + "name": "데이터 행 표시 확인", + "action": "verify", + "target": "table-rows", + "expected": "20개 데이터 행이 표시됨", + "validation": { + "minRows": 20, + "maxRows": 20 + } + }, + { + "step": 8, + "name": "페이지네이션 표시 확인", + "action": "verify", + "target": "pagination", + "expected": "페이지네이션 정보가 표시됨: '전체 10425개 중 1-20개 표시'" + }, + { + "step": 9, + "name": "액션 버튼 표시 확인 (첫 번째 행)", + "action": "verify", + "target": "row[1]:action-buttons", + "expected": "각 행에 '상세 보기', '수정', '삭제' 버튼이 표시됨" + }, + { + "step": 10, + "name": "⚠️ 필수 검증: 검색 기능 테스트", + "critical": true, + "actions": [ + { + "type": "capture", + "variable": "beforeSearchCount", + "selector": "table tbody tr", + "extract": "count", + "description": "검색 전 행 수 저장" + }, + { + "type": "fill", + "target": "textbox:품목코드, 품목명, 규격 검색...", + "value": "{testData.searchKeyword}", + "description": "검색어 CS-001000 입력" + }, + { + "type": "wait", + "duration": 1000, + "description": "검색 결과 로딩 대기" + }, + { + "type": "capture", + "variable": "afterSearchCount", + "selector": "table tbody tr", + "extract": "count", + "description": "검색 후 행 수 저장" + } + ], + "verify": { + "searchApplied": true, + "tableContains": "{testData.searchKeyword}", + "dataChanged": "beforeSearchCount may differ from afterSearchCount" + }, + "expected": "검색어가 입력되고 필터링됨" + }, + { + "step": 11, + "name": "검색 결과 확인", + "action": "wait", + "duration": 1000, + "expected": "검색 결과가 필터링되어 표시됨 (자동 검색 또는 Enter 키)" + }, + { + "step": 12, + "name": "검색 결과 데이터 검증", + "critical": true, + "description": "검색 결과의 모든 행이 검색어를 포함하는지 확인", + "action": "verify", + "target": "table-rows", + "expected": "검색어와 일치하는 품목만 표시됨", + "validation": { + "containsKeyword": "CS-001000" + }, + "verify": { + "allRowsContain": "{testData.searchKeyword}", + "columnToCheck": "품목코드" + } + }, + { + "step": 13, + "name": "검색 초기화", + "actions": [ + { + "type": "clear", + "target": "textbox:품목코드, 품목명, 규격 검색..." + }, + { + "type": "wait", + "duration": 500 + }, + { + "type": "capture", + "variable": "afterClearCount", + "selector": "table tbody tr", + "extract": "count" + } + ], + "verify": { + "dataRestored": "afterClearCount should equal beforeSearchCount" + }, + "expected": "검색어가 지워지고 전체 목록이 다시 표시됨" + }, + { + "step": 14, + "name": "탭 필터 테스트 - 제품 탭 클릭", + "action": "click", + "target": "button:제품", + "expected": "제품 탭이 활성화됨" + }, + { + "step": 15, + "name": "제품 탭 필터 결과 확인", + "action": "verify", + "target": "table-rows", + "expected": "품목유형이 '제품'인 항목만 표시됨", + "validation": { + "itemType": "제품" + } + }, + { + "step": 16, + "name": "탭 필터 테스트 - 소모품 탭 클릭", + "action": "click", + "target": "button:소모품", + "expected": "소모품 탭이 활성화됨" + }, + { + "step": 17, + "name": "소모품 탭 필터 결과 확인", + "action": "verify", + "target": "table-rows", + "expected": "품목유형이 '소모품'인 항목만 표시됨", + "validation": { + "itemType": "소모품" + } + }, + { + "step": 18, + "name": "탭 필터 초기화 - 전체 탭 클릭", + "action": "click", + "target": "button:전체", + "expected": "전체 탭이 활성화되고 모든 품목이 표시됨" + }, + { + "step": 19, + "name": "페이지네이션 테스트 - 2페이지 이동", + "action": "click", + "target": "button:2", + "expected": "2페이지로 이동됨" + }, + { + "step": 20, + "name": "2페이지 데이터 확인", + "action": "verify", + "target": "pagination", + "expected": "페이지네이션 정보가 '전체 10425개 중 21-40개 표시'로 변경됨" + }, + { + "step": 21, + "name": "다음 페이지 버튼 클릭", + "action": "click", + "target": "button:다음", + "expected": "3페이지로 이동됨" + }, + { + "step": 22, + "name": "3페이지 데이터 확인", + "action": "verify", + "target": "pagination", + "expected": "페이지네이션 정보가 '전체 10425개 중 41-60개 표시'로 변경됨" + }, + { + "step": 23, + "name": "1페이지로 복귀", + "action": "click", + "target": "button:1", + "expected": "1페이지로 복귀됨" + }, + { + "step": 24, + "name": "품목 등록 페이지 이동", + "action": "click", + "target": "button:품목 등록", + "expected": "품목 등록 페이지(/items/create)로 이동됨" + }, + { + "step": 25, + "name": "품목 등록 페이지 로딩 확인", + "action": "verify", + "target": "heading:품목 등록", + "expected": "품목 등록 페이지가 표시됨", + "validation": { + "pageTitle": "품목 등록", + "pageDescription": "품목 정보를 입력하세요" + } + }, + { + "step": 26, + "name": "초기 버튼 상태 확인", + "action": "verify", + "target": "buttons", + "expected": "'취소' 버튼은 활성화, '저장' 버튼은 비활성화 상태", + "validation": { + "cancelEnabled": true, + "saveDisabled": true + } + }, + { + "step": 27, + "name": "품목 유형 선택 전 경고 메시지 확인", + "action": "verify", + "target": "alert", + "expected": "'⚠️ 품목 유형을 먼저 선택해주세요' 경고 메시지가 표시됨" + }, + { + "step": 28, + "name": "품목 유형 필드 확인", + "action": "verify", + "target": "combobox:품목 유형", + "expected": "품목 유형 콤보박스가 필수 필드(*)로 표시됨" + }, + { + "step": 29, + "name": "제품(Finished Goods) 등록 테스트 시작", + "action": "click", + "target": "combobox:품목 유형", + "expected": "품목 유형 드롭다운이 열림" + }, + { + "step": 30, + "name": "제품 옵션 선택", + "action": "click", + "target": "option:제품 (Finished Goods)", + "expected": "제품 유형이 선택되고 제품 전용 입력 필드가 표시됨" + }, + { + "step": 31, + "name": "제품 입력 필드 표시 확인", + "action": "verify", + "target": "form-fields", + "expected": "제품 유형에 맞는 입력 필드들이 표시됨", + "validation": { + "fields": [ + "상품명*", + "품목명*", + "품목코드(자동생성)", + "로트 약자", + "품목상태", + "비고", + "인정번호", + "인정 유효기간 시작일", + "인정 유효기간 종료일", + "시방서 (PDF)", + "인정서 (PDF)", + "부품구성 (BOM) 필요" + ] + } + }, + { + "step": 32, + "name": "상품명 입력", + "action": "type", + "target": "textbox:상품명", + "value": "테스트 프리미엄 스크린", + "expected": "상품명이 입력됨" + }, + { + "step": 33, + "name": "품목명 입력", + "action": "type", + "target": "textbox:품목명", + "value": "TEST-SCREEN-001", + "expected": "품목명이 입력됨" + }, + { + "step": 34, + "name": "품목코드 자동생성 확인", + "action": "verify", + "target": "textbox:품목코드", + "expected": "품목코드가 품목명과 동일하게 'TEST-SCREEN-001'로 자동 생성됨", + "validation": { + "isDisabled": true, + "value": "TEST-SCREEN-001" + } + }, + { + "step": 35, + "name": "로트 약자 입력", + "action": "type", + "target": "textbox:로트 약자", + "value": "TSC", + "expected": "로트 약자가 입력됨" + }, + { + "step": 36, + "name": "품목상태 선택", + "action": "click", + "target": "combobox:품목상태", + "expected": "품목상태 드롭다운이 열림" + }, + { + "step": 37, + "name": "품목상태 '활성' 선택", + "action": "click", + "target": "option:활성", + "expected": "'활성' 상태가 선택됨" + }, + { + "step": 38, + "name": "비고 입력", + "action": "type", + "target": "textbox:비고", + "value": "E2E 테스트용 제품", + "expected": "비고가 입력됨" + }, + { + "step": 39, + "name": "인정번호 입력", + "action": "type", + "target": "textbox:인정번호", + "value": "TEST-CERT-2026-001", + "expected": "인정번호가 입력됨" + }, + { + "step": 40, + "name": "저장 버튼 활성화 확인", + "action": "verify", + "target": "button:저장", + "expected": "필수 필드 입력 완료 후 저장 버튼이 활성화됨", + "validation": { + "isEnabled": true + } + }, + { + "step": 41, + "name": "제품 등록 - URL 저장 (라우팅 오류 감지용)", + "action": "getCurrentUrl", + "expected": "현재 URL 저장: /items/create" + }, + { + "step": 42, + "name": "제품 등록 - 저장 버튼 클릭", + "action": "click", + "target": "button:저장", + "expected": "제품 등록 API 호출 및 성공 메시지 표시" + }, + { + "step": 43, + "name": "제품 등록 - URL 변경 여부 확인 (필수 검증 #2)", + "action": "verifyUrl", + "expected": "URL이 /production/screen-production으로 복귀 (404 에러 페이지 아님)", + "validation": { + "notContains": ["404", "not-found", "error"] + } + }, + { + "step": 44, + "name": "제품 등록 - 에러 페이지 텍스트 감지 (필수 검증 #2)", + "action": "verifyNoErrorPage", + "expected": "에러 텍스트가 없음", + "validation": { + "noErrorText": [ + "페이지를 찾을 수 없습니다", + "404", + "Not Found", + "서버 에러", + "500" + ] + } + }, + { + "step": 45, + "name": "제품 등록 - 성공 토스트 메시지 확인 (필수 검증 #2)", + "action": "verify", + "target": "toast-message", + "expected": "'등록되었습니다' 또는 유사한 성공 메시지가 표시됨" + }, + { + "step": 46, + "name": "제품 등록 - 목록 페이지 복귀 확인", + "action": "verify", + "target": "heading:품목 관리", + "expected": "품목 관리 목록 페이지로 정상 복귀됨" + }, + { + "step": 47, + "name": "제품 등록 - 신규 품목 검색", + "action": "type", + "target": "textbox:품목코드, 품목명, 규격 검색...", + "value": "TEST-SCREEN-001", + "expected": "등록한 제품 검색" + }, + { + "step": 48, + "name": "제품 등록 - 신규 품목 표시 확인", + "action": "wait", + "duration": 1000, + "expected": "등록한 제품이 목록에 표시됨" + }, + { + "step": 49, + "name": "제품 등록 - 데이터 검증", + "action": "verify", + "target": "table-row:TEST-SCREEN-001", + "expected": "등록한 제품 정보가 올바르게 표시됨", + "validation": { + "품목코드": "TEST-SCREEN-001", + "품목유형": "제품", + "품목상태": "활성" + } + }, + { + "step": 50, + "name": "소모품(Consumables) 등록 테스트 시작", + "action": "click", + "target": "button:품목 등록", + "expected": "품목 등록 페이지로 이동됨" + }, + { + "step": 51, + "name": "품목 유형에서 소모품 선택", + "action": "click", + "target": "combobox:품목 유형", + "expected": "품목 유형 드롭다운이 열림" + }, + { + "step": 52, + "name": "소모품 옵션 선택", + "action": "click", + "target": "option:소모품 (Consumables)", + "expected": "소모품 유형이 선택되고 소모품 전용 입력 필드가 표시됨" + }, + { + "step": 53, + "name": "소모품 입력 필드 표시 확인", + "action": "verify", + "target": "form-fields", + "expected": "소모품 유형에 맞는 입력 필드들이 표시됨", + "validation": { + "fields": [ + "품목명*", + "규격(사양)*", + "품목코드(자동생성)", + "단위*", + "비고" + ] + } + }, + { + "step": 54, + "name": "소모품 품목명 입력", + "action": "type", + "target": "textbox:품목명", + "value": "테스트 라벨", + "expected": "품목명이 입력됨" + }, + { + "step": 55, + "name": "소모품 규격 입력", + "action": "type", + "target": "textbox:규격(사양)", + "value": "100x50mm", + "expected": "규격이 입력됨" + }, + { + "step": 56, + "name": "소모품 품목코드 자동생성 확인", + "action": "verify", + "target": "textbox:품목코드", + "expected": "품목코드가 '테스트 라벨-100x50mm' 형식으로 자동 생성됨", + "validation": { + "isDisabled": true, + "contains": "테스트 라벨-100x50mm" + } + }, + { + "step": 57, + "name": "소모품 단위 선택", + "action": "click", + "target": "combobox:단위", + "expected": "단위 드롭다운이 열림" + }, + { + "step": 58, + "name": "단위 'EA' 선택", + "action": "click", + "target": "option:EA", + "expected": "'EA' 단위가 선택됨" + }, + { + "step": 59, + "name": "소모품 비고 입력", + "action": "type", + "target": "textbox:비고", + "value": "E2E 테스트용 소모품", + "expected": "비고가 입력됨" + }, + { + "step": 60, + "name": "소모품 등록 - URL 저장 (라우팅 오류 감지용)", + "action": "getCurrentUrl", + "expected": "현재 URL 저장: /items/create" + }, + { + "step": 61, + "name": "소모품 등록 - 저장 버튼 클릭", + "action": "click", + "target": "button:저장", + "expected": "소모품 등록 API 호출 및 성공 메시지 표시" + }, + { + "step": 62, + "name": "소모품 등록 - URL 변경 여부 확인 (필수 검증 #2)", + "action": "verifyUrl", + "expected": "URL이 /production/screen-production으로 복귀 (404 에러 페이지 아님)", + "validation": { + "notContains": ["404", "not-found", "error"] + } + }, + { + "step": 63, + "name": "소모품 등록 - 에러 페이지 텍스트 감지 (필수 검증 #2)", + "action": "verifyNoErrorPage", + "expected": "에러 텍스트가 없음" + }, + { + "step": 64, + "name": "소모품 등록 - 성공 토스트 메시지 확인", + "action": "verify", + "target": "toast-message", + "expected": "'등록되었습니다' 성공 메시지가 표시됨" + }, + { + "step": 65, + "name": "소모품 등록 - 신규 품목 검색", + "action": "type", + "target": "textbox:품목코드, 품목명, 규격 검색...", + "value": "테스트 라벨", + "expected": "등록한 소모품 검색" + }, + { + "step": 66, + "name": "소모품 등록 - 신규 품목 표시 확인", + "action": "wait", + "duration": 1000, + "expected": "등록한 소모품이 목록에 표시됨" + }, + { + "step": 67, + "name": "상세 보기 기능 테스트 - 첫 번째 품목 선택", + "action": "clearSearch", + "target": "textbox:품목코드, 품목명, 규격 검색...", + "expected": "검색어 초기화 및 전체 목록 표시" + }, + { + "step": 68, + "name": "상세 보기 버튼 클릭 (첫 번째 행)", + "action": "click", + "target": "button:상세 보기[row=1]", + "expected": "품목 상세 모달 또는 페이지가 열림" + }, + { + "step": 69, + "name": "상세 정보 표시 확인", + "action": "verify", + "target": "detail-modal-or-page", + "expected": "품목 상세 정보가 표시됨", + "validation": { + "hasItemCode": true, + "hasItemType": true, + "hasItemName": true + } + }, + { + "step": 70, + "name": "상세 보기 닫기", + "action": "click", + "target": "button:닫기 or ESC", + "expected": "상세 모달/페이지가 닫히고 목록으로 복귀" + }, + { + "step": 71, + "name": "수정 기능 테스트 - 등록한 제품 검색", + "action": "type", + "target": "textbox:품목코드, 품목명, 규격 검색...", + "value": "TEST-SCREEN-001", + "expected": "등록한 제품 검색" + }, + { + "step": 72, + "name": "수정 버튼 클릭", + "action": "click", + "target": "button:수정[row=TEST-SCREEN-001]", + "expected": "품목 수정 페이지(/items/:id?mode=edit)로 이동됨" + }, + { + "step": 73, + "name": "수정 페이지 로딩 확인", + "action": "verify", + "target": "heading:품목 수정", + "expected": "품목 수정 페이지가 표시되고 기존 데이터가 로드됨" + }, + { + "step": 74, + "name": "기존 데이터 로드 확인", + "action": "verify", + "target": "form-fields", + "expected": "등록했던 데이터가 폼에 채워져 있음", + "validation": { + "상품명": "테스트 프리미엄 스크린", + "품목명": "TEST-SCREEN-001", + "로트약자": "TSC" + } + }, + { + "step": 75, + "name": "비고 필드 수정", + "action": "clear-and-type", + "target": "textbox:비고", + "value": "E2E 테스트용 제품 - 수정됨", + "expected": "비고 내용이 수정됨" + }, + { + "step": 76, + "name": "수정 저장 - URL 저장", + "action": "getCurrentUrl", + "expected": "현재 URL 저장: /items/:id?mode=edit" + }, + { + "step": 77, + "name": "수정 저장 버튼 클릭", + "action": "click", + "target": "button:저장", + "expected": "수정 API 호출 및 성공 메시지 표시" + }, + { + "step": 78, + "name": "수정 저장 - URL 변경 여부 확인 (필수 검증 #2)", + "action": "verifyUrl", + "expected": "URL이 /production/screen-production으로 복귀", + "validation": { + "notContains": ["404", "not-found", "error"] + } + }, + { + "step": 79, + "name": "수정 저장 - 성공 토스트 메시지 확인", + "action": "verify", + "target": "toast-message", + "expected": "'수정되었습니다' 성공 메시지가 표시됨" + }, + { + "step": 80, + "name": "수정된 데이터 확인 - 제품 검색", + "action": "type", + "target": "textbox:품목코드, 품목명, 규격 검색...", + "value": "TEST-SCREEN-001", + "expected": "수정한 제품 검색" + }, + { + "step": 81, + "name": "수정된 데이터 확인 - 상세보기", + "action": "click", + "target": "button:상세 보기[row=TEST-SCREEN-001]", + "expected": "상세 정보 표시" + }, + { + "step": 82, + "name": "수정된 비고 내용 확인", + "action": "verify", + "target": "detail-modal:비고", + "expected": "비고가 'E2E 테스트용 제품 - 수정됨'으로 변경되었음을 확인" + }, + { + "step": 83, + "name": "상세 모달 닫기", + "action": "click", + "target": "button:닫기", + "expected": "상세 모달이 닫힘" + }, + { + "step": 84, + "name": "삭제 기능 테스트 - 소모품 검색", + "action": "clear-and-type", + "target": "textbox:품목코드, 품목명, 규격 검색...", + "value": "테스트 라벨", + "expected": "등록한 소모품 검색" + }, + { + "step": 85, + "name": "삭제 버튼 클릭", + "action": "click", + "target": "button:삭제[row=테스트 라벨]", + "expected": "삭제 확인 다이얼로그가 표시됨" + }, + { + "step": 86, + "name": "삭제 확인 다이얼로그 검증", + "action": "verify", + "target": "dialog:confirm-delete", + "expected": "'정말 삭제하시겠습니까?' 메시지가 표시됨" + }, + { + "step": 87, + "name": "삭제 취소 테스트 - 취소 버튼 클릭", + "action": "click", + "target": "button:취소[dialog]", + "expected": "다이얼로그가 닫히고 삭제되지 않음" + }, + { + "step": 88, + "name": "삭제 취소 확인 - 품목이 여전히 존재함", + "action": "verify", + "target": "table-row:테스트 라벨", + "expected": "소모품이 목록에 여전히 존재함" + }, + { + "step": 89, + "name": "삭제 재시도 - 삭제 버튼 클릭", + "action": "click", + "target": "button:삭제[row=테스트 라벨]", + "expected": "삭제 확인 다이얼로그가 다시 표시됨" + }, + { + "step": 90, + "name": "삭제 확인 버튼 클릭", + "action": "click", + "target": "button:확인[dialog]", + "expected": "삭제 API 호출 및 성공 메시지 표시" + }, + { + "step": 91, + "name": "소모품 삭제 - URL 변경 여부 확인 (필수 검증 #2)", + "action": "verifyUrl", + "expected": "URL이 /production/screen-production 유지", + "validation": { + "notContains": ["404", "not-found", "error"] + } + }, + { + "step": 92, + "name": "소모품 삭제 - 성공 토스트 메시지 확인", + "action": "verify", + "target": "toast-message", + "expected": "'삭제되었습니다' 성공 메시지가 표시됨" + }, + { + "step": 93, + "name": "소모품 삭제 확인 - 목록에서 사라짐", + "action": "verify", + "target": "table-rows", + "expected": "삭제한 소모품이 목록에서 사라짐", + "validation": { + "notContains": "테스트 라벨" + } + }, + { + "step": 94, + "name": "제품 삭제 - 제품 검색", + "action": "clear-and-type", + "target": "textbox:품목코드, 품목명, 규격 검색...", + "value": "TEST-SCREEN-001", + "expected": "등록한 제품 검색" + }, + { + "step": 95, + "name": "제품 삭제 버튼 클릭", + "action": "click", + "target": "button:삭제[row=TEST-SCREEN-001]", + "expected": "삭제 확인 다이얼로그가 표시됨" + }, + { + "step": 96, + "name": "제품 삭제 확인", + "action": "click", + "target": "button:확인[dialog]", + "expected": "삭제 API 호출 및 성공 메시지 표시" + }, + { + "step": 97, + "name": "제품 삭제 - URL 변경 여부 확인", + "action": "verifyUrl", + "expected": "URL이 /production/screen-production 유지" + }, + { + "step": 98, + "name": "제품 삭제 - 성공 토스트 메시지 확인", + "action": "verify", + "target": "toast-message", + "expected": "'삭제되었습니다' 성공 메시지가 표시됨" + }, + { + "step": 99, + "name": "제품 삭제 확인 - 목록에서 사라짐", + "action": "verify", + "target": "table-rows", + "expected": "삭제한 제품이 목록에서 사라짐", + "validation": { + "notContains": "TEST-SCREEN-001" + } + }, + { + "step": 100, + "name": "최종 테스트 완료 확인", + "action": "verify", + "target": "page", + "expected": "품목 관리 페이지가 정상 상태로 유지됨", + "validation": { + "pageTitle": "품목 관리", + "hasStatistics": true, + "hasTable": true, + "hasPagination": true + } + } + ] +} diff --git a/item-standard-management.json b/item-standard-management.json new file mode 100644 index 0000000..8b2cdd0 --- /dev/null +++ b/item-standard-management.json @@ -0,0 +1,444 @@ +{ + "id": "item-standard-management", + "name": "품목기준관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "기준정보 관리 > 품목기준관리 페이지의 섹션/항목 조회/추가/수정/삭제 기능을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/master-data/item-standard", + "menuNavigation": { + "level1": "기준정보 관리", + "level2": "품목기준관리", + "expectedUrl": "/master-data/item-standard" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebar": { + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 5, + "waitAfterScroll": 300 + }, + "level1": { + "text": "기준정보 관리", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "품목기준관리", + "waitAfterClick": 300 + }, + "fallbackUrl": "/master-data/item-standard", + "expectedUrl": "/master-data/item-standard" + }, + "timeout": 90000, + "tags": ["master-data", "item-standard", "builder", "crud"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "testData": { + "newSection": { + "name": "E2E 테스트 섹션" + }, + "newItem": { + "name": "E2E 테스트 항목", + "type": "텍스트", + "required": true + } + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 500 } + ], + "expect": { + "sidebarReady": true + } + }, + { + "id": "step-1", + "name": "기준정보 관리 메뉴 진입", + "description": "기준정보 관리 > 품목기준관리 메뉴로 이동", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "기준정보 관리", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "기준정보 관리" }, + { "type": "wait", "duration": 500 }, + { "type": "click", "target": "품목기준관리" } + ], + "expect": { + "url": "/master-data/item-standard", + "visible": ["품목기준관리", "계층구조", "섹션", "항목"] + }, + "fallback": { + "type": "navigate", + "url": "/master-data/item-standard" + } + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "탭과 섹션 목록 구조 확인", + "verify": { + "visible": ["계층구조", "섹션", "항목", "속성", "페이지"], + "sectionList": ["소모품 등록", "원자재 등록", "부자재 등록", "부품 등록", "제품 등록"] + } + }, + { + "id": "step-3", + "name": "섹션 선택", + "description": "소모품 등록 섹션 클릭하여 항목 확인", + "actions": [ + { "type": "click", "target": "소모품 등록" } + ], + "expect": { + "visible": ["소모품 등록", "섹션 추가", "항목 추가"], + "itemsLoaded": true + } + }, + { + "id": "step-4", + "name": "항목 목록 확인", + "description": "기본정보 항목들이 표시되는지 확인", + "verify": { + "visible": ["기본정보", "품목명", "규격(사양)", "단위", "비고"], + "itemDetails": ["텍스트", "드롭다운", "필수", "순서", "필드ID"] + } + }, + { + "id": "step-5", + "name": "필수 검증 #2: 섹션 추가 기능", + "description": "섹션 추가 버튼 클릭하여 모달 열기", + "actions": [ + { "type": "openModal", "target": "섹션 추가", "description": "섹션 추가 모달 열기" } + ], + "modalConfig": { + "containerSelector": "[role='dialog'], .modal", + "animationDelay": 300, + "waitForSelector": "[role='dialog']" + }, + "expect": { + "modal": "섹션 추가", + "visible": ["섹션명", "저장", "취소"] + } + }, + { + "id": "step-6", + "name": "새 섹션 정보 입력", + "description": "모달 내 섹션 이름 입력 및 저장", + "actions": [ + { "type": "fillInModal", "target": "섹션명", "value": "{testData.newSection.name}", "options": { "waitAfter": 100 } }, + { "type": "clickInModal", "target": "저장", "options": { "waitAfter": 500 } } + ], + "expect": { + "toast": ["추가", "등록", "성공"], + "modalClosed": true + }, + "verify": { + "apiCall": "POST /api/master-data/item-standard/section" + } + }, + { + "id": "step-7", + "name": "필수 검증 #4: 섹션 추가 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 추가 확인 필수!", + "description": "추가된 섹션이 목록에 표시되는지 확인", + "verify": { + "sectionListContains": "{testData.newSection.name}" + } + }, + { + "id": "step-8", + "name": "필수 검증 #2: 항목 추가 기능", + "description": "항목 추가 버튼 클릭하여 모달 열기", + "actions": [ + { "type": "click", "target": "{testData.newSection.name}" }, + { "type": "wait", "duration": 300 }, + { "type": "openModal", "target": "항목 추가", "description": "항목 추가 모달 열기" } + ], + "modalConfig": { + "containerSelector": "[role='dialog'], .modal", + "animationDelay": 300, + "waitForSelector": "[role='dialog']" + }, + "expect": { + "modal": "항목 추가", + "visible": ["항목명", "타입", "필수여부", "저장"] + } + }, + { + "id": "step-9", + "name": "새 항목 정보 입력", + "description": "모달 내 항목 정보 입력 및 저장", + "actions": [ + { "type": "fillInModal", "target": "항목명", "value": "{testData.newItem.name}", "options": { "waitAfter": 100 } }, + { "type": "selectInModal", "target": "타입", "value": "{testData.newItem.type}", "options": { "waitAfter": 200 } }, + { "type": "clickInModal", "target": "필수", "description": "필수여부 체크", "options": { "waitAfter": 100 } }, + { "type": "clickInModal", "target": "저장", "options": { "waitAfter": 500 } } + ], + "expect": { + "toast": ["추가", "등록", "성공"], + "modalClosed": true + }, + "verify": { + "apiCall": "POST /api/master-data/item-standard/item" + } + }, + { + "id": "step-10", + "name": "필수 검증 #4: 항목 추가 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 추가 확인 필수!", + "description": "추가된 항목이 목록에 표시되는지 확인", + "verify": { + "itemListContains": "{testData.newItem.name}" + } + }, + { + "id": "step-11", + "name": "항목 수정", + "description": "추가된 항목 클릭하여 수정 모달 열기", + "actions": [ + { "type": "click", "target": "{testData.newItem.name}" }, + { "type": "wait", "duration": 300 }, + { "type": "openModal", "target": "수정", "description": "항목 수정 모달 열기" } + ], + "modalConfig": { + "containerSelector": "[role='dialog'], .modal", + "animationDelay": 300, + "waitForSelector": "[role='dialog']" + }, + "expect": { + "modal": "항목 수정", + "fieldEditable": true + } + }, + { + "id": "step-12", + "name": "항목 정보 변경 및 저장", + "description": "모달 내 항목 이름 변경 후 저장", + "actions": [ + { "type": "fillInModal", "target": "항목명", "value": "E2E 테스트 항목 수정", "options": { "waitAfter": 100 } }, + { "type": "clickInModal", "target": "저장", "options": { "waitAfter": 500 } } + ], + "expect": { + "toast": ["수정", "저장", "성공"], + "modalClosed": true + } + }, + { + "id": "step-13", + "name": "필수 검증 #4: 항목 수정 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", + "description": "수정된 항목명이 목록에 표시되는지 확인", + "verify": { + "itemListContains": "E2E 테스트 항목 수정" + } + }, + { + "id": "step-14", + "name": "항목 삭제", + "description": "수정된 항목 삭제", + "actions": [ + { "type": "click", "target": "E2E 테스트 항목 수정" }, + { "type": "click", "target": "삭제" } + ], + "expect": { + "confirmDialog": true, + "dialogText": ["삭제", "하시겠습니까"] + } + }, + { + "id": "step-15", + "name": "항목 삭제 확인", + "description": "삭제 확인 다이얼로그에서 확인 클릭", + "actions": [ + { "type": "click", "target": "확인" } + ], + "expect": { + "toast": ["삭제", "성공"], + "modalClosed": true + } + }, + { + "id": "step-16", + "name": "필수 검증 #4: 항목 삭제 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 삭제 확인 필수!", + "description": "삭제된 항목이 목록에서 제거되었는지 확인", + "verify": { + "itemListNotContains": "E2E 테스트 항목 수정" + } + }, + { + "id": "step-17", + "name": "섹션 삭제", + "description": "테스트 섹션 삭제", + "actions": [ + { "type": "click", "target": "{testData.newSection.name}" }, + { "type": "click", "target": "섹션 삭제", "description": "섹션 삭제 버튼" } + ], + "expect": { + "confirmDialog": true, + "dialogText": ["삭제", "하시겠습니까"] + } + }, + { + "id": "step-18", + "name": "섹션 삭제 확인", + "description": "삭제 확인 다이얼로그에서 확인 클릭", + "actions": [ + { "type": "click", "target": "확인" } + ], + "expect": { + "toast": ["삭제", "성공"] + } + }, + { + "id": "step-19", + "name": "필수 검증 #4: 섹션 삭제 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 삭제 확인 필수!", + "description": "삭제된 섹션이 목록에서 제거되었는지 확인", + "verify": { + "sectionListNotContains": "{testData.newSection.name}" + } + }, + { + "id": "step-20", + "name": "다른 탭 확인 - 항목", + "description": "항목 탭 클릭하여 표시 확인", + "actions": [ + { "type": "click", "target": "항목", "role": "tab" } + ], + "expect": { + "tabActive": "항목", + "contentLoaded": true + } + }, + { + "id": "step-21", + "name": "다른 탭 확인 - 속성", + "description": "속성 탭 클릭하여 표시 확인", + "actions": [ + { "type": "click", "target": "속성", "role": "tab" } + ], + "expect": { + "tabActive": "속성", + "contentLoaded": true + } + }, + { + "id": "step-22", + "name": "불러오기 기능 확인", + "description": "불러오기 버튼 동작 확인", + "actions": [ + { "type": "click", "target": "섹션", "role": "tab" }, + { "type": "click", "target": "소모품 등록" }, + { "type": "wait", "duration": 300 }, + { "type": "click", "target": "불러오기" } + ], + "expect": { + "modal": "불러오기", + "visible": ["템플릿", "불러오기"] + } + }, + { + "id": "step-23", + "name": "불러오기 모달 닫기", + "description": "불러오기 모달 취소", + "actions": [ + { "type": "press", "key": "Escape" } + ], + "expect": { + "modalClosed": true + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/master-data/item-standard", + "message": "품목기준관리 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "text=품목기준관리", + "message": "품목기준관리 제목이 표시되어야 함" + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 2, + "name": "등록/저장 버튼", + "trigger": "섹션 추가, 항목 추가, 저장 버튼", + "verification": "URL 유지 + 에러 페이지 없음 + 성공 토스트 + 데이터 반영", + "failCondition": "404/500 에러 페이지 이동" + }, + { + "id": 4, + "name": "모달 등록 완료", + "trigger": "섹션/항목 추가 모달", + "verification": "실제 저장 동작 + 결과 확인", + "failCondition": "열기/닫기만 테스트" + }, + { + "id": 5, + "name": "목업/미완성 페이지 감지", + "trigger": "페이지 로드 시", + "verification": "입력 필드 + 동작하는 버튼 + API 호출 확인", + "failCondition": "버튼만 있고 입력 불가, Console LOG만 출력" + } + ] + }, + + "cleanup": { + "enabled": true, + "description": "테스트 중 생성된 섹션/항목 데이터 삭제", + "actions": [ + { + "type": "deleteTestData", + "condition": "contains:E2E 테스트" + } + ] + }, + + "notes": { + "testScope": "섹션 추가/수정/삭제 → 항목 추가/수정/삭제 전체 CRUD 테스트", + "pageStructure": { + "tabs": "계층구조, 섹션, 항목, 속성, 페이지", + "sections": "소모품 등록, 원자재 등록, 부자재 등록, 부품 등록, 제품 등록", + "itemFields": "항목명, 타입, 필수여부, 순서, 필드ID" + }, + "itemTypes": ["텍스트", "드롭다운", "날짜", "숫자", "체크박스"], + "prerequisites": "로그인된 사용자에게 기준정보 관리 권한 필요" + } +} diff --git a/leave-policy.json b/leave-policy.json new file mode 100644 index 0000000..6baf035 --- /dev/null +++ b/leave-policy.json @@ -0,0 +1,607 @@ +{ + "id": "leave-policy", + "name": "설정 - 휴가정책", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "url": "/ko/settings/leave-policy", + "menuNavigation": { + "level1": "설정", + "level2": "휴가정책", + "expectedUrl": "/ko/settings/leave-policy" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "level1": { + "text": "설정", + "alternativeNames": ["설정", "Settings", "환경설정"], + "scrollConfig": { + "maxScrollAttempts": 5, + "scrollAmount": 200, + "waitAfterScroll": 300 + } + }, + "level2": { + "text": "휴가관리", + "alternativeNames": ["휴가관리", "Leave Management", "휴가정책"], + "scrollConfig": { + "maxScrollAttempts": 3, + "scrollAmount": 150, + "waitAfterScroll": 300 + } + }, + "expectedUrl": "/ko/settings/leave-policy", + "fallbackUrl": "/ko/settings/leave-policy" + }, + "expectedAPIs": [ + { + "method": "GET", + "path": "/api/v1/leave-policy", + "description": "휴가 정책 조회" + }, + { + "method": "PUT", + "path": "/api/v1/leave-policy", + "description": "휴가 정책 저장" + } + ], + "steps": [ + { + "id": 0, + "name": "사이드바 준비", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "execute", + "script": "document.querySelector('.sidebar-scroll, [class*=\"sidebar\"], nav')?.scrollTo({top: 0, behavior: 'instant'})" + }, + { "type": "wait", "duration": 500 } + ], + "expected": { + "sidebarReady": true + } + }, + { + "id": 1, + "name": "2단계 메뉴 진입: 설정 > 휴가관리", + "description": "설정 > 휴가관리 메뉴로 이동하여 페이지 로드 확인 (scrollAndFind 패턴 사용)", + "actions": [ + { + "type": "scrollAndFind", + "target": "설정", + "scrollContainer": ".sidebar-scroll, [class*='sidebar'], nav", + "maxAttempts": 5, + "scrollAmount": 200, + "waitAfterScroll": 300, + "alternativeNames": ["설정", "Settings", "환경설정"] + }, + { "type": "click", "target": "설정" }, + { "type": "wait", "duration": 500 }, + { + "type": "scrollAndFind", + "target": "휴가관리", + "scrollContainer": ".sidebar-scroll, [class*='sidebar'], nav", + "maxAttempts": 3, + "scrollAmount": 150, + "waitAfterScroll": 300, + "alternativeNames": ["휴가관리", "Leave Management", "휴가정책"] + }, + { "type": "click", "target": "휴가관리" }, + { "type": "wait", "target": "페이지 로드 완료" } + ], + "expected": { + "url": "/ko/settings/leave-policy", + "title": "휴가관리", + "authenticated": true + } + }, + { + "id": 2, + "name": "로딩 스피너 표시 확인", + "action": "verify", + "target": "loading", + "expected": "'휴가 정책을 불러오는 중...' 표시" + }, + { + "id": 3, + "name": "페이지 제목 확인", + "action": "verify", + "target": "heading", + "expected": "'휴가관리' 텍스트 표시" + }, + { + "id": 4, + "name": "설명 텍스트 확인", + "action": "verify", + "target": "description", + "expected": "'휴가 정책을 관리합니다' 텍스트 표시" + }, + { + "id": 5, + "name": "저장 버튼 확인", + "action": "verify", + "target": "button", + "expected": "'저장' 버튼 표시" + }, + { + "id": 6, + "name": "기준 설정 카드 확인", + "action": "verify", + "target": "card", + "expected": "'기준 설정' 카드 표시" + }, + { + "id": 7, + "name": "연차 설정 카드 확인", + "action": "verify", + "target": "card", + "expected": "'연차 설정' 카드 표시" + }, + { + "id": 8, + "name": "이월 설정 카드 확인", + "action": "verify", + "target": "card", + "expected": "'이월 설정' 카드 표시" + }, + { + "id": 9, + "name": "초기 기준 타입 확인", + "action": "verify", + "target": "combobox", + "expected": "'회계연도' 선택 상태" + }, + { + "id": 10, + "name": "초기 기준일 - 월 확인", + "action": "verify", + "target": "combobox", + "expected": "'1월' 선택 상태, 활성화됨" + }, + { + "id": 11, + "name": "초기 기준일 - 일 확인", + "action": "verify", + "target": "combobox", + "expected": "'1일' 선택 상태, 활성화됨" + }, + { + "id": 12, + "name": "기준 셀렉트 클릭", + "action": "click", + "target": "기준 셀렉트", + "expected": "옵션 리스트 열림", + "critical": true + }, + { + "id": 13, + "name": "기준 옵션 개수 확인", + "action": "verify", + "target": "options", + "expected": "회계연도, 입사일 2개 옵션 표시" + }, + { + "id": 14, + "name": "입사일 기준 선택", + "action": "select", + "target": "입사일 옵션", + "expected": "'입사일' 선택됨", + "critical": true + }, + { + "id": 15, + "name": "입사일 선택 후 - 기준일 월 비활성화 확인", + "action": "verify", + "target": "combobox", + "expected": "기준일 '월' 셀렉트 비활성화 (disabled)" + }, + { + "id": 16, + "name": "입사일 선택 후 - 기준일 일 비활성화 확인", + "action": "verify", + "target": "combobox", + "expected": "기준일 '일' 셀렉트 비활성화 (disabled)" + }, + { + "id": 17, + "name": "회계연도로 다시 변경", + "action": "select", + "target": "회계연도 옵션", + "expected": "'회계연도' 선택됨" + }, + { + "id": 18, + "name": "회계연도 선택 후 - 기준일 월 활성화 확인", + "action": "verify", + "target": "combobox", + "expected": "기준일 '월' 셀렉트 활성화됨" + }, + { + "id": 19, + "name": "회계연도 선택 후 - 기준일 일 활성화 확인", + "action": "verify", + "target": "combobox", + "expected": "기준일 '일' 셀렉트 활성화됨" + }, + { + "id": 20, + "name": "기준일 월 셀렉트 클릭", + "action": "click", + "target": "기준일 월 셀렉트", + "expected": "월 옵션 리스트 열림" + }, + { + "id": 21, + "name": "기준일 월 옵션 개수 확인", + "action": "verify", + "target": "options", + "expected": "1월~12월, 12개 옵션 표시" + }, + { + "id": 22, + "name": "기준일 월 변경 (6월)", + "action": "select", + "target": "6월 옵션", + "expected": "'6월' 선택됨", + "critical": true + }, + { + "id": 23, + "name": "기준일 일 셀렉트 클릭", + "action": "click", + "target": "기준일 일 셀렉트", + "expected": "일 옵션 리스트 열림" + }, + { + "id": 24, + "name": "기준일 일 옵션 개수 확인", + "action": "verify", + "target": "options", + "expected": "1일~31일, 31개 옵션 표시" + }, + { + "id": 25, + "name": "기준일 일 변경 (15일)", + "action": "select", + "target": "15일 옵션", + "expected": "'15일' 선택됨", + "critical": true + }, + { + "id": 26, + "name": "기본 연차 일수 초기값 확인", + "action": "verify", + "target": "spinbutton", + "expected": "15일 표시" + }, + { + "id": 27, + "name": "기본 연차 일수 변경", + "action": "input", + "target": "기본 연차 일수 입력 필드", + "value": "20", + "expected": "20 입력됨", + "critical": true + }, + { + "id": 28, + "name": "근속년수당 추가 연차 초기값 확인", + "action": "verify", + "target": "spinbutton", + "expected": "1일 표시" + }, + { + "id": 29, + "name": "근속년수당 추가 연차 변경", + "action": "input", + "target": "근속년수당 추가 연차 입력 필드", + "value": "2", + "expected": "2 입력됨", + "critical": true + }, + { + "id": 30, + "name": "최대 연차 일수 초기값 확인", + "action": "verify", + "target": "spinbutton", + "expected": "25일 표시" + }, + { + "id": 31, + "name": "최대 연차 일수 변경", + "action": "input", + "target": "최대 연차 일수 입력 필드", + "value": "30", + "expected": "30 입력됨", + "critical": true + }, + { + "id": 32, + "name": "연차 이월 허용 스위치 초기 상태 확인", + "action": "verify", + "target": "switch", + "expected": "스위치 ON (checked) 상태" + }, + { + "id": 33, + "name": "이월 설정 필드 표시 확인", + "action": "verify", + "target": "fields", + "expected": "'최대 이월 일수', '이월 연차 소멸 기간' 필드 표시됨" + }, + { + "id": 34, + "name": "최대 이월 일수 초기값 확인", + "action": "verify", + "target": "spinbutton", + "expected": "10일 표시" + }, + { + "id": 35, + "name": "이월 연차 소멸 기간 초기값 확인", + "action": "verify", + "target": "spinbutton", + "expected": "3개월 표시" + }, + { + "id": 36, + "name": "최대 이월 일수 변경", + "action": "input", + "target": "최대 이월 일수 입력 필드", + "value": "15", + "expected": "15 입력됨", + "critical": true + }, + { + "id": 37, + "name": "이월 연차 소멸 기간 변경", + "action": "input", + "target": "이월 연차 소멸 기간 입력 필드", + "value": "6", + "expected": "6 입력됨", + "critical": true + }, + { + "id": 38, + "name": "저장 버튼 클릭", + "action": "click", + "target": "저장 버튼", + "expected": "저장 API 호출, 로딩 상태 표시", + "critical": true + }, + { + "id": 39, + "name": "저장 완료 토스트 확인", + "action": "verify", + "target": "toast", + "expected": "'휴가 정책이 저장되었습니다.' 토스트 표시" + }, + { + "id": 40, + "name": "URL 유지 확인", + "action": "verify", + "target": "url", + "expected": "URL이 /settings/leave-policy 유지 (에러 페이지 이동 없음)" + }, + { + "id": 41, + "name": "페이지 새로고침", + "action": "reload", + "target": "page", + "expected": "페이지 새로고침 후 GET API 재호출" + }, + { + "id": 42, + "name": "설정 지속성 - 기준 타입", + "action": "verify", + "target": "combobox", + "expected": "'회계연도' 선택 상태 유지" + }, + { + "id": 43, + "name": "설정 지속성 - 기준일 월", + "action": "verify", + "target": "combobox", + "expected": "'6월' 선택 상태 유지" + }, + { + "id": 44, + "name": "설정 지속성 - 기준일 일", + "action": "verify", + "target": "combobox", + "expected": "'15일' 선택 상태 유지" + }, + { + "id": 45, + "name": "설정 지속성 - 기본 연차 일수", + "action": "verify", + "target": "spinbutton", + "expected": "20일 유지" + }, + { + "id": 46, + "name": "설정 지속성 - 근속년수당 추가 연차", + "action": "verify", + "target": "spinbutton", + "expected": "2일 유지" + }, + { + "id": 47, + "name": "설정 지속성 - 최대 연차 일수", + "action": "verify", + "target": "spinbutton", + "expected": "30일 유지" + }, + { + "id": 48, + "name": "설정 지속성 - 이월 허용", + "action": "verify", + "target": "switch", + "expected": "스위치 ON 상태 유지" + }, + { + "id": 49, + "name": "설정 지속성 - 최대 이월 일수", + "action": "verify", + "target": "spinbutton", + "expected": "15일 유지" + }, + { + "id": 50, + "name": "설정 지속성 - 이월 소멸 기간", + "action": "verify", + "target": "spinbutton", + "expected": "6개월 유지" + }, + { + "id": 51, + "name": "연차 이월 허용 스위치 OFF", + "action": "click", + "target": "연차 이월 허용 스위치", + "expected": "스위치 OFF 상태", + "critical": true + }, + { + "id": 52, + "name": "이월 OFF 후 - 하위 필드 숨김 확인", + "action": "verify", + "target": "fields", + "expected": "'최대 이월 일수', '이월 연차 소멸 기간' 필드 숨겨짐" + }, + { + "id": 53, + "name": "이월 OFF 후 - 안내 문구 숨김 확인", + "action": "verify", + "target": "paragraph", + "expected": "'이월된 연차는...' 안내 문구 숨겨짐" + }, + { + "id": 54, + "name": "연차 이월 허용 스위치 다시 ON", + "action": "click", + "target": "연차 이월 허용 스위치", + "expected": "스위치 ON 상태" + }, + { + "id": 55, + "name": "이월 ON 후 - 하위 필드 표시 확인", + "action": "verify", + "target": "fields", + "expected": "'최대 이월 일수', '이월 연차 소멸 기간' 필드 표시됨" + }, + { + "id": 56, + "name": "이월 ON 후 - 이전 값 유지 확인", + "action": "verify", + "target": "spinbutton", + "expected": "최대 이월 15일, 소멸 기간 6개월 유지" + }, + { + "id": 57, + "name": "기준 타입 변경 (입사일)", + "action": "select", + "target": "입사일 옵션", + "expected": "'입사일' 선택됨" + }, + { + "id": 58, + "name": "입사일 선택 후 저장", + "action": "click", + "target": "저장 버튼", + "expected": "저장 성공 토스트 표시" + }, + { + "id": 59, + "name": "입사일 저장 후 새로고침", + "action": "reload", + "target": "page", + "expected": "페이지 새로고침" + }, + { + "id": 60, + "name": "입사일 설정 지속성 확인", + "action": "verify", + "target": "combobox", + "expected": "'입사일' 선택 상태 유지" + }, + { + "id": 61, + "name": "입사일 유지 시 - 기준일 비활성화 확인", + "action": "verify", + "target": "combobox", + "expected": "기준일 월/일 셀렉트 비활성화 (disabled)" + }, + { + "id": 62, + "name": "숫자 입력 유효성 - 음수 테스트", + "action": "input", + "target": "기본 연차 일수 입력 필드", + "value": "-5", + "expected": "음수 입력 불가 또는 0으로 변환" + }, + { + "id": 63, + "name": "숫자 입력 유효성 - 최대값 초과 테스트", + "action": "input", + "target": "기본 연차 일수 입력 필드", + "value": "150", + "expected": "최대값(100) 제한 적용" + }, + { + "id": 64, + "name": "숫자 입력 유효성 - 문자 입력 테스트", + "action": "input", + "target": "기본 연차 일수 입력 필드", + "value": "abc", + "expected": "숫자 이외 입력 불가 또는 0으로 변환" + }, + { + "id": 65, + "name": "콘솔 에러 확인", + "action": "verify", + "target": "console", + "expected": "콘솔에 에러 로그 없음" + }, + { + "id": 66, + "name": "안내 문구 개수 확인", + "action": "verify", + "target": "help-text", + "expected": "3개 카드별 안내 문구 표시" + }, + { + "id": 67, + "name": "최종 상태 확인", + "action": "verify", + "target": "page", + "expected": "페이지 정상 동작, 모든 설정 저장됨" + } + ], + "testData": { + "settings": { + "fiscal": { + "standardType": "fiscal", + "fiscalStartMonth": 6, + "fiscalStartDay": 15, + "defaultAnnualLeave": 20, + "additionalLeavePerYear": 2, + "maxAnnualLeave": 30, + "carryOverEnabled": true, + "carryOverMaxDays": 15, + "carryOverExpiryMonths": 6 + }, + "hire": { + "standardType": "hire", + "defaultAnnualLeave": 20, + "additionalLeavePerYear": 2, + "maxAnnualLeave": 30, + "carryOverEnabled": true, + "carryOverMaxDays": 15, + "carryOverExpiryMonths": 6 + } + }, + "monthOptions": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], + "dayOptions": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31] + } +} diff --git a/login.json b/login.json new file mode 100644 index 0000000..4fcc339 --- /dev/null +++ b/login.json @@ -0,0 +1,276 @@ +{ + "id": "login-test", + "name": "로그인 테스트 (끝판왕)", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "로그인 페이지 UI 검증, 로그인 실패/성공, 대시보드 진입, 로그아웃까지 전체 인증 플로우 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "timeout": 30000, + "tags": ["auth", "login", "critical"], + + "auth": { + "username": "TestUser5", + "password": "password123!", + "wrongPassword": "wrongpassword" + }, + + "selectors": { + "usernameInput": "#userId", + "passwordInput": "#password", + "rememberMeCheckbox": "input[type='checkbox']", + "forgotPasswordButton": "button:has-text('비밀번호를 잊으셨나요?')", + "loginButton": "button[type='submit']", + "passwordToggle": "button:has(.lucide-eye)", + "userProfileButton": "button:has-text('홍킬동')", + "logoutButton": "button:has-text('로그아웃')" + }, + + "steps": [ + { + "id": 1, + "name": "로그인 페이지 접속", + "action": "navigate", + "target": "/ko/login", + "expected": { + "url": "/ko/login", + "visible": ["로그인", "환영합니다", "SAM MES", "아이디", "비밀번호"] + } + }, + { + "id": 2, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "아이디 입력 필드 존재 및 입력 가능", + "비밀번호 입력 필드 존재 및 입력 가능", + "로그인 버튼 존재 및 클릭 가능" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 3, + "name": "UI 요소 검증 - 입력 필드", + "action": "verify_elements", + "checks": [ + "아이디 입력 필드 placeholder: '아이디를 입력하세요'", + "비밀번호 입력 필드 placeholder: '비밀번호를 입력하세요'", + "비밀번호 표시/숨김 토글 버튼 존재" + ], + "expected": "모든 입력 필드 정상" + }, + { + "id": 4, + "name": "UI 요소 검증 - 옵션", + "action": "verify_elements", + "checks": [ + "로그인 상태 유지 체크박스 존재", + "비밀번호 찾기 링크 존재", + "로그인 버튼 존재" + ], + "expected": "모든 옵션 요소 정상" + }, + { + "id": 5, + "name": "비밀번호 표시/숨김 토글 테스트", + "action": "click", + "target": "passwordToggle", + "expected": "비밀번호 필드 type이 'text'로 변경 (표시 모드)" + }, + { + "id": 6, + "name": "비밀번호 숨김 복원", + "action": "click", + "target": "passwordToggle", + "expected": "비밀번호 필드 type이 'password'로 복원 (숨김 모드)" + }, + { + "id": 7, + "name": "로그인 실패 테스트 - 빈 필드", + "action": "click", + "target": "loginButton", + "expected": "유효성 검사 에러 또는 로그인 실패 메시지" + }, + { + "id": 8, + "name": "아이디 입력", + "action": "fill", + "target": "usernameInput", + "value": "TestUser5", + "expected": "아이디 필드에 값 입력됨" + }, + { + "id": 9, + "name": "로그인 실패 테스트 - 잘못된 비밀번호", + "action": "fill", + "target": "passwordInput", + "value": "wrongpassword", + "expected": "비밀번호 필드에 값 입력됨" + }, + { + "id": 10, + "name": "잘못된 비밀번호로 로그인 시도", + "action": "click", + "target": "loginButton", + "expected": "로그인 실패 에러 메시지 표시", + "verify": { + "type": "error_message", + "contains": ["실패", "오류", "일치하지 않", "incorrect", "failed"] + } + }, + { + "id": 11, + "name": "비밀번호 필드 초기화", + "action": "clear", + "target": "passwordInput", + "expected": "비밀번호 필드 비워짐" + }, + { + "id": 12, + "name": "올바른 비밀번호 입력", + "action": "fill", + "target": "passwordInput", + "value": "password123!", + "expected": "올바른 비밀번호 입력됨" + }, + { + "id": 13, + "name": "필수 검증 #2: 로그인 버튼 클릭", + "action": "click", + "target": "loginButton", + "verify": { + "url_should_change": true, + "no_error_page": true, + "api_call": "POST /api/v1/auth/login" + }, + "expected": "로그인 성공 및 대시보드로 이동" + }, + { + "id": 14, + "name": "대시보드 페이지 확인", + "action": "wait_for_navigation", + "expected": { + "url_contains": "/dashboard", + "visible": ["대시보드", "홍킬동"] + } + }, + { + "id": 15, + "name": "사용자 정보 표시 확인", + "action": "verify_elements", + "checks": [ + "사용자명 '홍킬동' 표시", + "메뉴 영역 표시", + "SAM 로고 표시" + ], + "expected": "사용자 정보 및 메인 레이아웃 정상" + }, + { + "id": 16, + "name": "세션 유지 확인 - 페이지 새로고침", + "action": "reload", + "expected": "새로고침 후에도 로그인 상태 유지" + }, + { + "id": 17, + "name": "새로고침 후 대시보드 유지 확인", + "action": "verify_url", + "expected": { + "url_contains": "/dashboard", + "visible": ["대시보드", "홍킬동"] + } + }, + { + "id": 18, + "name": "사용자 프로필 메뉴 열기", + "action": "click", + "target": "userProfileButton", + "expected": "사용자 메뉴 드롭다운 열림" + }, + { + "id": 19, + "name": "로그아웃 버튼 클릭", + "action": "click", + "target": "logoutButton", + "expected": "로그아웃 처리 및 로그인 페이지로 이동" + }, + { + "id": 20, + "name": "로그아웃 후 로그인 페이지 확인", + "action": "verify_url", + "expected": { + "url_contains": "/login", + "visible": ["로그인", "아이디", "비밀번호"] + } + }, + { + "id": 21, + "name": "로그아웃 후 보호된 페이지 접근 시도", + "action": "navigate", + "target": "/ko/dashboard", + "expected": "로그인 페이지로 리다이렉트" + }, + { + "id": 22, + "name": "재로그인 테스트", + "actions": [ + { "type": "fill", "target": "usernameInput", "value": "TestUser5" }, + { "type": "fill", "target": "passwordInput", "value": "password123!" }, + { "type": "click", "target": "loginButton" } + ], + "expected": "재로그인 성공" + }, + { + "id": 23, + "name": "최종 확인 - 대시보드 진입", + "action": "verify_url", + "expected": { + "url_contains": "/dashboard", + "visible": ["대시보드", "홍킬동"] + } + } + ], + + "requiredVerifications": [ + { + "id": 2, + "name": "등록/저장 버튼 (로그인)", + "steps": [13], + "criteria": "로그인 버튼 클릭 → API 호출 → 대시보드 이동" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [2], + "criteria": "입력 필드 동작, 버튼 클릭 가능" + } + ], + + "expectedAPIs": [ + { + "method": "POST", + "endpoint": "/api/v1/auth/login", + "description": "로그인 인증" + }, + { + "method": "GET", + "endpoint": "/api/v1/auth/me", + "description": "현재 사용자 정보 조회" + }, + { + "method": "POST", + "endpoint": "/api/v1/auth/logout", + "description": "로그아웃" + } + ], + + "testData": { + "validUser": { + "username": "TestUser5", + "password": "password123!", + "displayName": "홍킬동" + }, + "invalidPassword": "wrongpassword" + } +} diff --git a/notification-settings.json b/notification-settings.json new file mode 100644 index 0000000..5649dd6 --- /dev/null +++ b/notification-settings.json @@ -0,0 +1,825 @@ +{ + "id": "notification-settings", + "name": "설정 - 알림설정", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "알림 설정 관리 기능 테스트 - 카테고리별 마스터 스위치, 개별 알림 스위치, 소리 선택, 이메일 알림 설정", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/ko/settings/notification-settings", + "menuNavigation": { + "level1": "설정", + "level2": "알림설정", + "expectedUrl": "/ko/settings/notification-settings" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebarSelector": ".sidebar-scroll, [class*='sidebar'], nav[class*='menu']", + "scrollConfig": { + "maxScrollAttempts": 5, + "scrollAmount": 200, + "scrollDelay": 300 + }, + "level1": { + "text": "설정", + "selectors": [ + "button:has-text('설정')", + "[role='button']:has-text('설정')", + "a:has-text('설정')", + "div[class*='menu-item']:has-text('설정')" + ], + "searchPattern": "scrollAndFind" + }, + "level2": { + "text": "알림설정", + "selectors": [ + "a:has-text('알림설정')", + "button:has-text('알림설정')", + "[role='menuitem']:has-text('알림설정')", + "div[class*='submenu'] a:has-text('알림설정')" + ], + "waitAfterLevel1": 500, + "searchPattern": "scrollAndFind" + }, + "fallbackUrl": "/ko/settings/notification-settings" + }, + "expectedAPIs": [ + { + "method": "GET", + "path": "/api/v1/settings/notifications", + "description": "알림 설정 조회" + }, + { + "method": "PUT", + "path": "/api/v1/settings/notifications", + "description": "알림 설정 저장" + } + ], + "steps": [ + { + "id": 0, + "name": "사이드바 초기화", + "description": "메뉴 탐색 전 사이드바를 최상단으로 스크롤하여 초기화", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll, [class*=\"sidebar\"], nav[class*=\"menu\"]')?.scrollTo({top: 0, behavior: 'instant'})" + }, + { "type": "wait", "duration": 500 } + ], + "expected": { + "sidebarScrollPosition": "top" + }, + "validation": "사이드바 스크롤 초기화" + }, + { + "id": 1, + "name": "2단계 메뉴 진입: 설정 > 알림설정", + "description": "설정 > 알림설정 메뉴로 이동하여 페이지 로드 확인 (scrollAndFind 패턴 사용)", + "navigationPattern": "scrollAndFind", + "actions": [ + { + "type": "scrollAndFind", + "target": "설정", + "selectors": ["button:has-text('설정')", "[role='button']:has-text('설정')", "a:has-text('설정')"], + "scrollContainer": ".sidebar-scroll, [class*='sidebar']", + "maxAttempts": 5 + }, + { "type": "click", "target": "설정" }, + { "type": "wait", "duration": 500 }, + { + "type": "scrollAndFind", + "target": "알림설정", + "selectors": ["a:has-text('알림설정')", "button:has-text('알림설정')", "[role='menuitem']:has-text('알림설정')"], + "scrollContainer": ".sidebar-scroll, [class*='sidebar']", + "maxAttempts": 3 + }, + { "type": "click", "target": "알림설정" }, + { "type": "wait", "target": "페이지 로드 완료" } + ], + "fallback": { + "type": "directNavigation", + "url": "/ko/settings/notification-settings" + }, + "expected": { + "url": "/ko/settings/notification-settings", + "title": "알림설정", + "authenticated": true + }, + "validation": "필수 검증 #5: 목업 페이지 감지" + }, + { + "id": 2, + "name": "페이지 제목 확인", + "action": "verify", + "target": "heading[level=1]", + "expected": "알림설정", + "validation": "텍스트 일치 확인" + }, + { + "id": 3, + "name": "설명 텍스트 확인", + "action": "verify", + "target": "paragraph", + "expected": "알림 설정을 관리합니다.", + "validation": "텍스트 일치 확인" + }, + { + "id": 4, + "name": "항목 설정 버튼 존재 확인", + "action": "verify", + "target": "button:has-text('항목 설정')", + "expected": "버튼 표시됨", + "validation": "UI 요소 존재" + }, + { + "id": 5, + "name": "저장 버튼 존재 확인", + "action": "verify", + "target": "button:has-text('저장')", + "expected": "버튼 표시됨", + "validation": "UI 요소 존재" + }, + { + "id": 6, + "name": "공지 알림 카테고리 확인", + "action": "verify", + "target": "heading:has-text('공지 알림')", + "expected": "카테고리 표시됨", + "validation": "카테고리 존재" + }, + { + "id": 7, + "name": "공지 알림 마스터 스위치 상태 확인", + "action": "verify", + "target": "heading:has-text('공지 알림') + switch", + "expected": "checked 상태", + "validation": "초기 상태: ON" + }, + { + "id": 8, + "name": "공지사항 알림 항목 확인", + "action": "verify", + "target": "text='공지사항 알림'", + "expected": "하위 알림 항목 표시됨", + "validation": "하위 항목 존재" + }, + { + "id": 9, + "name": "이벤트 알림 항목 확인", + "action": "verify", + "target": "text='이벤트 알림'", + "expected": "하위 알림 항목 표시됨", + "validation": "하위 항목 존재" + }, + { + "id": 10, + "name": "일정 알림 카테고리 확인", + "action": "verify", + "target": "heading:has-text('일정 알림')", + "expected": "카테고리 표시됨", + "validation": "카테고리 존재" + }, + { + "id": 11, + "name": "일정 알림 마스터 스위치 상태 확인", + "action": "verify", + "target": "heading:has-text('일정 알림') + switch", + "expected": "checked 상태", + "validation": "초기 상태: ON" + }, + { + "id": 12, + "name": "부가세 신고 알림 항목 확인", + "action": "verify", + "target": "text='부가세 신고 알림'", + "expected": "하위 알림 항목 표시됨", + "validation": "하위 항목 존재" + }, + { + "id": 13, + "name": "부가세 신고 알림 소리 설정 확인", + "action": "verify", + "target": "text='부가세 신고 알림' >> .. >> combobox", + "expected": "무음", + "validation": "초기값 확인" + }, + { + "id": 14, + "name": "종합소득세 신고 알림 항목 확인", + "action": "verify", + "target": "text='종합소득세 신고 알림'", + "expected": "하위 알림 항목 표시됨", + "validation": "하위 항목 존재" + }, + { + "id": 15, + "name": "종합소득세 신고 알림 소리 설정 확인", + "action": "verify", + "target": "text='종합소득세 신고 알림' >> .. >> combobox", + "expected": "SAM 보이스", + "validation": "초기값 확인" + }, + { + "id": 16, + "name": "거래처 알림 카테고리 확인", + "action": "verify", + "target": "heading:has-text('거래처 알림')", + "expected": "카테고리 표시됨", + "validation": "카테고리 존재" + }, + { + "id": 17, + "name": "거래처 알림 마스터 스위치 상태 확인", + "action": "verify", + "target": "heading:has-text('거래처 알림') + switch", + "expected": "checked 상태", + "validation": "초기 상태: ON" + }, + { + "id": 18, + "name": "신규 업체 등록 알림 항목 확인", + "action": "verify", + "target": "text='신규 업체 등록 알림'", + "expected": "하위 알림 항목 표시됨", + "validation": "하위 항목 존재" + }, + { + "id": 19, + "name": "신용등급 등록 알림 항목 확인", + "action": "verify", + "target": "text='신용등급 등록 알림'", + "expected": "하위 알림 항목 표시됨", + "validation": "하위 항목 존재" + }, + { + "id": 20, + "name": "근태 알림 카테고리 확인", + "action": "verify", + "target": "heading:has-text('근태 알림')", + "expected": "카테고리 표시됨", + "validation": "카테고리 존재" + }, + { + "id": 21, + "name": "근태 알림 마스터 스위치 상태 확인", + "action": "verify", + "target": "heading:has-text('근태 알림') + switch", + "expected": "checked 상태", + "validation": "초기 상태: ON" + }, + { + "id": 22, + "name": "연차 알림 항목 확인", + "action": "verify", + "target": "text='연차 알림'", + "expected": "하위 알림 항목 표시됨", + "validation": "하위 항목 존재" + }, + { + "id": 23, + "name": "출근 알림 항목 확인", + "action": "verify", + "target": "text='출근 알림'", + "expected": "하위 알림 항목 표시됨", + "validation": "하위 항목 존재" + }, + { + "id": 24, + "name": "지각 알림 항목 확인", + "action": "verify", + "target": "text='지각 알림'", + "expected": "하위 알림 항목 표시됨", + "validation": "하위 항목 존재" + }, + { + "id": 25, + "name": "결근 알림 항목 확인", + "action": "verify", + "target": "text='결근 알림'", + "expected": "하위 알림 항목 표시됨", + "validation": "하위 항목 존재" + }, + { + "id": 26, + "name": "수주/발주 알림 카테고리 확인", + "action": "verify", + "target": "heading:has-text('수주/발주 알림')", + "expected": "카테고리 표시됨", + "validation": "카테고리 존재" + }, + { + "id": 27, + "name": "수주/발주 알림 마스터 스위치 상태 확인", + "action": "verify", + "target": "heading:has-text('수주/발주 알림') + switch", + "expected": "unchecked 상태", + "validation": "초기 상태: OFF" + }, + { + "id": 28, + "name": "수주 등록 알림 스위치 disabled 확인", + "action": "verify", + "target": "text='수주 등록 알림' >> .. >> switch", + "expected": "disabled 상태", + "validation": "마스터 스위치 OFF 시 하위 항목 disabled" + }, + { + "id": 29, + "name": "발주 알림 스위치 disabled 확인", + "action": "verify", + "target": "text='발주 알림' >> .. >> switch", + "expected": "disabled 상태", + "validation": "마스터 스위치 OFF 시 하위 항목 disabled" + }, + { + "id": 30, + "name": "전자결재 알림 카테고리 확인", + "action": "verify", + "target": "heading:has-text('전자결재 알림')", + "expected": "카테고리 표시됨", + "validation": "카테고리 존재" + }, + { + "id": 31, + "name": "전자결재 알림 마스터 스위치 상태 확인", + "action": "verify", + "target": "heading:has-text('전자결재 알림') + switch", + "expected": "unchecked 상태", + "validation": "초기 상태: OFF" + }, + { + "id": 32, + "name": "결재요청 알림 항목 확인", + "action": "verify", + "target": "text='결재요청 알림'", + "expected": "하위 알림 항목 표시됨", + "validation": "하위 항목 존재" + }, + { + "id": 33, + "name": "기안 > 승인 알림 항목 확인", + "action": "verify", + "target": "text='기안 > 승인 알림'", + "expected": "하위 알림 항목 표시됨", + "validation": "하위 항목 존재" + }, + { + "id": 34, + "name": "기안 > 반려 알림 항목 확인", + "action": "verify", + "target": "text='기안 > 반려 알림'", + "expected": "하위 알림 항목 표시됨", + "validation": "하위 항목 존재" + }, + { + "id": 35, + "name": "기안 > 완료 알림 항목 확인", + "action": "verify", + "target": "text='기안 > 완료 알림'", + "expected": "하위 알림 항목 표시됨", + "validation": "하위 항목 존재" + }, + { + "id": 36, + "name": "생산 알림 카테고리 확인", + "action": "verify", + "target": "heading:has-text('생산 알림')", + "expected": "카테고리 표시됨", + "validation": "카테고리 존재" + }, + { + "id": 37, + "name": "생산 알림 마스터 스위치 상태 확인", + "action": "verify", + "target": "heading:has-text('생산 알림') + switch", + "expected": "unchecked 상태", + "validation": "초기 상태: OFF" + }, + { + "id": 38, + "name": "안전재고 알림 항목 확인", + "action": "verify", + "target": "text='안전재고 알림'", + "expected": "하위 알림 항목 표시됨", + "validation": "하위 항목 존재" + }, + { + "id": 39, + "name": "생산완료 알림 항목 확인", + "action": "verify", + "target": "text='생산완료 알림'", + "expected": "하위 알림 항목 표시됨", + "validation": "하위 항목 존재" + }, + { + "id": 40, + "name": "마스터 스위치 ON 테스트 - 수주/발주 알림 활성화", + "action": "click", + "target": "heading:has-text('수주/발주 알림') + switch", + "expected": "마스터 스위치가 ON으로 변경됨", + "validation": "스위치 상태 변경" + }, + { + "id": 41, + "name": "하위 스위치 활성화 확인 - 수주 등록 알림", + "action": "verify", + "target": "text='수주 등록 알림' >> .. >> switch", + "expected": "enabled 상태로 변경됨", + "validation": "필수 검증: Conditional Rendering - 마스터 ON 시 하위 활성화" + }, + { + "id": 42, + "name": "하위 스위치 활성화 확인 - 발주 알림", + "action": "verify", + "target": "text='발주 알림' >> .. >> switch", + "expected": "enabled 상태로 변경됨", + "validation": "필수 검증: Conditional Rendering - 마스터 ON 시 하위 활성화" + }, + { + "id": 43, + "name": "하위 알림 스위치 ON - 수주 등록 알림", + "action": "click", + "target": "text='수주 등록 알림' >> .. >> switch", + "expected": "스위치가 ON으로 변경됨", + "validation": "개별 알림 활성화" + }, + { + "id": 44, + "name": "알림 소리 combobox 활성화 확인", + "action": "verify", + "target": "text='수주 등록 알림' >> .. >> combobox", + "expected": "enabled 상태", + "validation": "필수 검증: Conditional Rendering - 알림 ON 시 combobox 활성화" + }, + { + "id": 45, + "name": "이메일 checkbox 활성화 확인", + "action": "verify", + "target": "text='수주 등록 알림' >> .. >> checkbox", + "expected": "enabled 상태", + "validation": "필수 검증: Conditional Rendering - 알림 ON 시 checkbox 활성화" + }, + { + "id": 46, + "name": "소리 테스트 버튼 활성화 확인", + "action": "verify", + "target": "text='수주 등록 알림' >> .. >> button:has(img)", + "expected": "enabled 상태", + "validation": "필수 검증: Conditional Rendering - 알림 ON 시 버튼 활성화" + }, + { + "id": 47, + "name": "알림 소리 선택 - combobox 클릭", + "action": "click", + "target": "text='수주 등록 알림' >> .. >> combobox", + "expected": "드롭다운 메뉴 표시됨", + "validation": "combobox 동작 확인" + }, + { + "id": 48, + "name": "알림 소리 옵션 확인 - 기본 알림음", + "action": "verify", + "target": "option:has-text('기본 알림음')", + "expected": "옵션 표시됨", + "validation": "combobox 옵션 존재" + }, + { + "id": 49, + "name": "알림 소리 옵션 확인 - SAM 보이스", + "action": "verify", + "target": "option:has-text('SAM 보이스')", + "expected": "옵션 표시됨", + "validation": "combobox 옵션 존재" + }, + { + "id": 50, + "name": "알림 소리 옵션 확인 - 무음", + "action": "verify", + "target": "option:has-text('무음')", + "expected": "옵션 표시됨", + "validation": "combobox 옵션 존재" + }, + { + "id": 51, + "name": "알림 소리 선택 - SAM 보이스", + "action": "click", + "target": "option:has-text('SAM 보이스')", + "expected": "SAM 보이스 선택됨", + "validation": "combobox 값 변경" + }, + { + "id": 52, + "name": "선택된 알림 소리 확인", + "action": "verify", + "target": "text='수주 등록 알림' >> .. >> combobox", + "expected": "SAM 보이스", + "validation": "combobox 선택값 반영" + }, + { + "id": 53, + "name": "이메일 알림 활성화", + "action": "click", + "target": "text='수주 등록 알림' >> .. >> checkbox", + "expected": "checkbox 체크됨", + "validation": "checkbox 상태 변경" + }, + { + "id": 54, + "name": "이메일 checkbox 상태 확인", + "action": "verify", + "target": "text='수주 등록 알림' >> .. >> checkbox", + "expected": "checked 상태", + "validation": "checkbox 선택 반영" + }, + { + "id": 55, + "name": "마스터 스위치 ON 테스트 - 전자결재 알림 활성화", + "action": "click", + "target": "heading:has-text('전자결재 알림') + switch", + "expected": "마스터 스위치가 ON으로 변경됨", + "validation": "스위치 상태 변경" + }, + { + "id": 56, + "name": "전자결재 하위 스위치 활성화 확인 - 결재요청 알림", + "action": "verify", + "target": "text='결재요청 알림' >> .. >> switch", + "expected": "enabled 상태로 변경됨", + "validation": "Conditional Rendering - 마스터 ON 시 하위 활성화" + }, + { + "id": 57, + "name": "전자결재 하위 스위치 활성화 확인 - 기안 > 승인 알림", + "action": "verify", + "target": "text='기안 > 승인 알림' >> .. >> switch", + "expected": "enabled 상태로 변경됨", + "validation": "Conditional Rendering - 마스터 ON 시 하위 활성화" + }, + { + "id": 58, + "name": "전자결재 하위 스위치 활성화 확인 - 기안 > 반려 알림", + "action": "verify", + "target": "text='기안 > 반려 알림' >> .. >> switch", + "expected": "enabled 상태로 변경됨", + "validation": "Conditional Rendering - 마스터 ON 시 하위 활성화" + }, + { + "id": 59, + "name": "전자결재 하위 스위치 활성화 확인 - 기안 > 완료 알림", + "action": "verify", + "target": "text='기안 > 완료 알림' >> .. >> switch", + "expected": "enabled 상태로 변경됨", + "validation": "Conditional Rendering - 마스터 ON 시 하위 활성화" + }, + { + "id": 60, + "name": "결재요청 알림 스위치 ON", + "action": "click", + "target": "text='결재요청 알림' >> .. >> switch", + "expected": "스위치가 ON으로 변경됨", + "validation": "개별 알림 활성화" + }, + { + "id": 61, + "name": "결재요청 알림 소리 변경 - combobox 클릭", + "action": "click", + "target": "text='결재요청 알림' >> .. >> combobox", + "expected": "드롭다운 메뉴 표시됨", + "validation": "combobox 동작 확인" + }, + { + "id": 62, + "name": "결재요청 알림 소리 선택 - 무음", + "action": "click", + "target": "option:has-text('무음')", + "expected": "무음 선택됨", + "validation": "combobox 값 변경" + }, + { + "id": 63, + "name": "결재요청 알림 이메일 활성화", + "action": "click", + "target": "text='결재요청 알림' >> .. >> checkbox", + "expected": "checkbox 체크됨", + "validation": "checkbox 상태 변경" + }, + { + "id": 64, + "name": "마스터 스위치 ON 테스트 - 생산 알림 활성화", + "action": "click", + "target": "heading:has-text('생산 알림') + switch", + "expected": "마스터 스위치가 ON으로 변경됨", + "validation": "스위치 상태 변경" + }, + { + "id": 65, + "name": "생산 하위 스위치 활성화 확인 - 안전재고 알림", + "action": "verify", + "target": "text='안전재고 알림' >> .. >> switch", + "expected": "enabled 상태로 변경됨", + "validation": "Conditional Rendering - 마스터 ON 시 하위 활성화" + }, + { + "id": 66, + "name": "생산 하위 스위치 활성화 확인 - 생산완료 알림", + "action": "verify", + "target": "text='생산완료 알림' >> .. >> switch", + "expected": "enabled 상태로 변경됨", + "validation": "Conditional Rendering - 마스터 ON 시 하위 활성화" + }, + { + "id": 67, + "name": "안전재고 알림 스위치 ON", + "action": "click", + "target": "text='안전재고 알림' >> .. >> switch", + "expected": "스위치가 ON으로 변경됨", + "validation": "개별 알림 활성화" + }, + { + "id": 68, + "name": "저장 버튼 클릭 전 URL 저장", + "action": "saveUrl", + "target": "currentUrl", + "expected": "/settings/notification-settings", + "validation": "필수 검증 #2: URL 변경 감지를 위한 저장" + }, + { + "id": 69, + "name": "저장 버튼 클릭", + "action": "click", + "target": "button:has-text('저장')", + "expected": "저장 처리 시작", + "validation": "필수 검증 #2: 등록/저장 버튼 동작" + }, + { + "id": 70, + "name": "저장 후 URL 변경 여부 확인", + "action": "verify", + "target": "currentUrl", + "expected": "/settings/notification-settings (변경 없음)", + "validation": "필수 검증 #2: 페이지 이동 없음 확인" + }, + { + "id": 71, + "name": "에러 페이지 텍스트 확인", + "action": "verify", + "target": "body", + "expected": "404, Not Found 텍스트 없음", + "validation": "필수 검증 #2: 에러 페이지 감지" + }, + { + "id": 72, + "name": "성공 토스트 메시지 확인", + "action": "wait", + "target": "text='알림 설정이 저장되었습니다' or text='저장되었습니다'", + "expected": "토스트 메시지 표시됨", + "validation": "필수 검증 #2: 성공 토스트 확인" + }, + { + "id": 73, + "name": "Network Request 확인 - PUT API", + "action": "verifyNetwork", + "target": "PUT /api/v1/settings/notifications", + "expected": "200 OK", + "validation": "필수 검증 #2: API 호출 성공" + }, + { + "id": 74, + "name": "페이지 새로고침", + "action": "reload", + "target": "page", + "expected": "페이지 다시 로드됨", + "validation": "데이터 지속성 테스트" + }, + { + "id": 75, + "name": "페이지 로드 대기", + "action": "wait", + "target": "heading:has-text('알림설정')", + "expected": "페이지 표시됨", + "validation": "새로고침 후 로드" + }, + { + "id": 76, + "name": "저장된 값 확인 - 수주/발주 알림 마스터 스위치", + "action": "verify", + "target": "heading:has-text('수주/발주 알림') + switch", + "expected": "checked 상태 유지", + "validation": "데이터 지속성: 마스터 스위치" + }, + { + "id": 77, + "name": "저장된 값 확인 - 수주 등록 알림 스위치", + "action": "verify", + "target": "text='수주 등록 알림' >> .. >> switch", + "expected": "checked 상태 유지", + "validation": "데이터 지속성: 하위 스위치" + }, + { + "id": 78, + "name": "저장된 값 확인 - 수주 등록 알림 소리", + "action": "verify", + "target": "text='수주 등록 알림' >> .. >> combobox", + "expected": "SAM 보이스", + "validation": "데이터 지속성: combobox 값" + }, + { + "id": 79, + "name": "저장된 값 확인 - 수주 등록 알림 이메일", + "action": "verify", + "target": "text='수주 등록 알림' >> .. >> checkbox", + "expected": "checked 상태 유지", + "validation": "데이터 지속성: checkbox 값" + }, + { + "id": 80, + "name": "저장된 값 확인 - 전자결재 알림 마스터 스위치", + "action": "verify", + "target": "heading:has-text('전자결재 알림') + switch", + "expected": "checked 상태 유지", + "validation": "데이터 지속성: 마스터 스위치" + }, + { + "id": 81, + "name": "저장된 값 확인 - 결재요청 알림 스위치", + "action": "verify", + "target": "text='결재요청 알림' >> .. >> switch", + "expected": "checked 상태 유지", + "validation": "데이터 지속성: 하위 스위치" + }, + { + "id": 82, + "name": "저장된 값 확인 - 결재요청 알림 소리", + "action": "verify", + "target": "text='결재요청 알림' >> .. >> combobox", + "expected": "무음", + "validation": "데이터 지속성: combobox 값" + }, + { + "id": 83, + "name": "저장된 값 확인 - 결재요청 알림 이메일", + "action": "verify", + "target": "text='결재요청 알림' >> .. >> checkbox", + "expected": "checked 상태 유지", + "validation": "데이터 지속성: checkbox 값" + }, + { + "id": 84, + "name": "저장된 값 확인 - 생산 알림 마스터 스위치", + "action": "verify", + "target": "heading:has-text('생산 알림') + switch", + "expected": "checked 상태 유지", + "validation": "데이터 지속성: 마스터 스위치" + }, + { + "id": 85, + "name": "저장된 값 확인 - 안전재고 알림 스위치", + "action": "verify", + "target": "text='안전재고 알림' >> .. >> switch", + "expected": "checked 상태 유지", + "validation": "데이터 지속성: 하위 스위치" + }, + { + "id": 86, + "name": "마스터 스위치 OFF 테스트 - 수주/발주 알림 비활성화", + "action": "click", + "target": "heading:has-text('수주/발주 알림') + switch", + "expected": "마스터 스위치가 OFF로 변경됨", + "validation": "스위치 상태 변경" + }, + { + "id": 87, + "name": "하위 스위치 비활성화 확인 - 수주 등록 알림", + "action": "verify", + "target": "text='수주 등록 알림' >> .. >> switch", + "expected": "disabled 상태로 변경됨", + "validation": "필수 검증: Conditional Rendering - 마스터 OFF 시 하위 비활성화" + }, + { + "id": 88, + "name": "하위 combobox 비활성화 확인", + "action": "verify", + "target": "text='수주 등록 알림' >> .. >> combobox", + "expected": "disabled 상태로 변경됨", + "validation": "필수 검증: Conditional Rendering - 마스터 OFF 시 combobox 비활성화" + }, + { + "id": 89, + "name": "하위 checkbox 비활성화 확인", + "action": "verify", + "target": "text='수주 등록 알림' >> .. >> checkbox", + "expected": "disabled 상태로 변경됨", + "validation": "필수 검증: Conditional Rendering - 마스터 OFF 시 checkbox 비활성화" + }, + { + "id": 90, + "name": "하위 소리 테스트 버튼 비활성화 확인", + "action": "verify", + "target": "text='수주 등록 알림' >> .. >> button:has(img)", + "expected": "disabled 상태로 변경됨", + "validation": "필수 검증: Conditional Rendering - 마스터 OFF 시 버튼 비활성화" + } + ] +} diff --git a/order-management.json b/order-management.json new file mode 100644 index 0000000..114553e --- /dev/null +++ b/order-management.json @@ -0,0 +1,363 @@ +{ + "id": "order-management", + "name": "수주관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "판매관리 > 수주관리 페이지의 수주 등록/조회/수정/삭제 기능을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/sales/order", + "menuNavigation": { + "level1": "판매관리", + "level2": "수주관리", + "expectedUrl": "/sales/order" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebar": { + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 5, + "waitAfterScroll": 300 + }, + "level1": { + "text": "판매관리", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "수주관리", + "waitAfterClick": 300 + }, + "fallbackUrl": "/sales/order", + "expectedUrl": "/sales/order" + }, + "timeout": 90000, + "tags": ["sales", "order", "crud"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "testData": { + "order": { + "customer": "(주)삼성전자", + "siteName": "E2E 테스트 현장", + "deliveryDate": "2026-02-28", + "deliveryMethod": "택배", + "note": "E2E 테스트 수주입니다" + } + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 500 } + ] + }, + { + "id": "step-1", + "name": "판매관리 메뉴 진입", + "description": "판매관리 > 수주관리 메뉴로 이동", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "판매관리", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "판매관리" }, + { "type": "wait", "duration": 500 }, + { "type": "click", "target": "수주관리" } + ], + "expect": { + "url": "/sales/order", + "visible": ["수주 목록", "수주 등록"] + }, + "fallback": { + "type": "navigate", + "url": "/sales/order" + } + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "통계 카드와 테이블 구조 확인", + "verify": { + "visible": ["이번 달 수주", "분할 대기", "생산지시 대기", "출하 대기"], + "tableColumns": ["번호", "로트번호", "견적번호", "발주처", "현장명", "상태", "출고예정일", "배송방식"] + } + }, + { + "id": "step-3", + "name": "필수 검증 #3: 상태 탭 필터 테스트", + "description": "상태별 탭 동작 확인", + "actions": [ + { "type": "click", "target": "수주등록", "role": "tab" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "tabActive": "수주등록", + "dataFiltered": true + } + }, + { + "id": "step-4", + "name": "필수 검증 #3: 수주확정 탭 필터", + "description": "수주확정 탭 클릭하여 필터링 확인", + "actions": [ + { "type": "click", "target": "수주확정", "role": "tab" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "tabActive": "수주확정", + "dataFiltered": true + } + }, + { + "id": "step-5", + "name": "전체 탭으로 복귀", + "description": "전체 탭 클릭하여 모든 수주 표시", + "actions": [ + { "type": "click", "target": "전체", "role": "tab" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "tabActive": "전체", + "allDataShown": true + } + }, + { + "id": "step-6", + "name": "필수 검증 #2: 수주 등록 모달/페이지 열기", + "description": "수주 등록 버튼 클릭하여 등록 화면 열기", + "actions": [ + { "type": "click", "target": "수주 등록" } + ], + "expect": { + "pageOrModal": "수주 등록", + "visible": ["발주처", "현장명", "출고예정일", "배송방식"] + } + }, + { + "id": "step-7", + "name": "수주 등록 폼 입력", + "description": "수주 정보 입력", + "actions": [ + { "type": "click", "target": "발주처", "role": "combobox" }, + { "type": "click", "target": "{testData.order.customer}", "role": "option" }, + { "type": "fill", "target": "현장명", "value": "{testData.order.siteName}" }, + { "type": "fill", "target": "출고예정일", "value": "{testData.order.deliveryDate}" }, + { "type": "click", "target": "배송방식", "role": "combobox" }, + { "type": "click", "target": "{testData.order.deliveryMethod}", "role": "option" } + ] + }, + { + "id": "step-8", + "name": "필수 검증 #2: 수주 등록 저장", + "description": "등록/저장 버튼 클릭하여 수주 저장", + "actions": [ + { "type": "click", "target": "저장" } + ], + "expect": { + "urlMaintained": true, + "noErrorPage": true, + "toast": ["등록", "저장", "완료", "성공"] + }, + "verify": { + "apiCall": "POST /api/sales/order" + } + }, + { + "id": "step-9", + "name": "필수 검증 #4: 등록 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 등록 확인 필수!", + "description": "테이블에서 등록된 수주 확인", + "actions": [ + { "type": "navigate", "url": "/sales/order" }, + { "type": "wait", "duration": 500 } + ], + "verify": { + "tableContains": ["{testData.order.siteName}"], + "recordCountIncreased": true + } + }, + { + "id": "step-10", + "name": "수주 상세 열기", + "description": "등록된 수주 항목 클릭하여 상세 보기", + "actions": [ + { + "type": "findRow", + "contains": "{testData.order.siteName}", + "then": { + "type": "click", + "target": "row" + } + } + ], + "expect": { + "pageOrModal": "수주 상세", + "visible": ["수정", "삭제", "수주확정", "생산지시"] + } + }, + { + "id": "step-11", + "name": "수주 정보 수정", + "description": "수주 정보 수정 테스트", + "actions": [ + { "type": "click", "target": "수정" }, + { "type": "wait", "duration": 300 }, + { "type": "click", "target": "배송방식", "role": "combobox" }, + { "type": "click", "target": "상차", "role": "option" }, + { "type": "click", "target": "저장" } + ], + "expect": { + "toast": ["수정", "저장", "완료", "성공"], + "noErrorPage": true + } + }, + { + "id": "step-12", + "name": "필수 검증 #4: 수정 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", + "description": "테이블에서 수정된 수주 확인", + "verify": { + "tableContains": "상차" + } + }, + { + "id": "step-13", + "name": "수주 삭제 준비", + "description": "삭제할 수주 선택", + "actions": [ + { + "type": "findRow", + "contains": "{testData.order.siteName}", + "then": { + "type": "click", + "target": "row" + } + } + ], + "expect": { + "pageOrModal": "수주 상세", + "visible": ["삭제"] + } + }, + { + "id": "step-14", + "name": "수주 삭제", + "description": "삭제 버튼 클릭하여 수주 삭제", + "actions": [ + { "type": "click", "target": "삭제" } + ], + "expect": { + "confirmDialog": true, + "dialogText": ["삭제", "하시겠습니까"] + } + }, + { + "id": "step-15", + "name": "삭제 확인", + "description": "삭제 확인 다이얼로그에서 확인 클릭", + "actions": [ + { "type": "click", "target": "확인" } + ], + "expect": { + "toast": ["삭제", "완료", "성공"], + "noErrorPage": true + } + }, + { + "id": "step-16", + "name": "필수 검증 #4: 삭제 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 삭제 확인 필수!", + "description": "테이블에서 삭제된 수주가 없는지 확인", + "verify": { + "tableNotContains": "{testData.order.siteName}", + "recordCountDecreased": true + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/sales/order", + "message": "수주관리 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "button:has-text('수주 등록')", + "message": "수주 등록 버튼이 표시되어야 함" + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 2, + "name": "등록/저장 버튼", + "trigger": "수주 등록 버튼", + "verification": "URL 유지 + 에러 페이지 없음 + 성공 토스트 + 데이터 반영", + "failCondition": "404/500 에러 페이지 이동" + }, + { + "id": 3, + "name": "검색/필터", + "trigger": "상태 탭 필터", + "verification": "데이터 변화 확인", + "failCondition": "필터 적용 후 데이터 무변화" + }, + { + "id": 4, + "name": "데이터 반영 확인", + "trigger": "CRUD 완료 후", + "verification": "실제 데이터 등록/수정/삭제 확인", + "failCondition": "토스트만 확인하고 데이터 미확인" + } + ] + }, + + "cleanup": { + "enabled": true, + "description": "테스트 중 생성된 수주 데이터 삭제", + "actions": [ + { + "type": "deleteTestData", + "condition": "contains:E2E 테스트" + } + ] + }, + + "notes": { + "testScope": "수주 등록 → 조회 → 수정 → 삭제 전체 CRUD 테스트", + "pageFeatures": { + "statsCards": ["이번 달 수주", "분할 대기", "생산지시 대기", "출하 대기"], + "statusTabs": ["전체", "수주등록", "수주확정", "생산지시완료", "미수"], + "viewModes": ["카드 뷰", "테이블 뷰"] + }, + "tableColumns": ["번호", "로트번호", "견적번호", "발주처", "현장명", "상태", "출고예정일", "배송방식"], + "workflow": "수주등록 → 수주확정 → 생산지시 → 생산완료 → 출하완료", + "prerequisites": "로그인된 사용자에게 수주 관리 권한 필요" + } +} diff --git a/payment-history.json b/payment-history.json new file mode 100644 index 0000000..414fefd --- /dev/null +++ b/payment-history.json @@ -0,0 +1,213 @@ +{ + "id": "payment-history", + "name": "결제내역 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "회계관리 > 결제내역 메뉴의 결제 내역 조회 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/payment-history", + + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", + "level1": "회계관리", + "level2": "결제내역", + "alternativeLevel1Names": ["회계관리", "회계 관리", "Accounting", "재무관리"], + "alternativeLevel2Names": ["결제내역", "결제 내역", "Payment History", "결제이력"], + "fallbackUrls": [ + "/payment-history", + "/accounting/payment-history", + "/ko/payment-history" + ], + "scrollConfig": { + "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar, .sidebar-scroll", + "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem'], span", + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + } + }, + + "testFocus": { + "primary": "결제 내역 데이터 표시 및 조회 기능 검증", + "description": "결제 내역 테이블 표시, 검색, 필터, 페이지네이션 동작 확인" + }, + + "prerequisites": { + "authentication": true, + "testData": { + "description": "결제 내역 데이터가 최소 1개 이상 존재해야 함" + } + }, + + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/accounting/payments", + "params": "page=1&per_page=20", + "description": "결제 내역 목록 조회" + } + ], + + "steps": [ + { + "id": 0, + "name": "사이드바 메뉴 탐색 준비", + "description": "사이드바를 스크롤하여 회계관리 메뉴 찾기", + "actions": [ + { "type": "scroll", "target": "sidebar", "direction": "bottom", "description": "사이드바 하단으로 스크롤" }, + { "type": "wait", "duration": 500 } + ], + "verification": [ + "사이드바가 화면에 보이는지 확인", + "회계관리 메뉴가 보이는지 확인" + ] + }, + { + "id": 1, + "name": "1차 메뉴 찾기: 회계관리", + "description": "사이드바에서 '회계관리' 메뉴를 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "회계관리", + "alternativeTexts": ["회계관리", "회계 관리", "Accounting"], + "scrollContainer": "sidebar", + "maxAttempts": 10, + "description": "스크롤하며 회계관리 메뉴 찾기" + }, + { "type": "click", "target": "회계관리", "description": "회계관리 메뉴 클릭" }, + { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" } + ], + "verification": [ + "회계관리 메뉴가 클릭되었는지 확인", + "서브메뉴가 펼쳐졌는지 확인" + ] + }, + { + "id": 2, + "name": "2차 메뉴 찾기: 결제내역", + "description": "서브메뉴에서 '결제내역'을 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "결제내역", + "alternativeTexts": ["결제내역", "결제 내역", "Payment History"], + "scrollContainer": "submenu", + "maxAttempts": 5, + "description": "서브메뉴에서 결제내역 찾기" + }, + { "type": "click", "target": "결제내역", "description": "결제내역 메뉴 클릭" }, + { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 } + ], + "verification": [ + "결제내역 메뉴 클릭 성공", + "페이지 이동 완료" + ] + }, + { + "id": 3, + "name": "404 에러 감지", + "description": "페이지 로드 후 404 에러 여부 확인", + "actions": [ + { "type": "wait", "duration": 1000 }, + { "type": "checkFor404", "indicators": [ + "페이지를 찾을 수 없습니다", + "404", + "Not Found", + "존재하지 않거나" + ]} + ], + "verification": [ + "404 에러 메시지가 없는지 확인" + ] + }, + { + "id": 4, + "name": "페이지 정상 로드 확인", + "description": "결제내역 페이지가 정상적으로 로드되었는지 확인", + "actions": [ + { "type": "verify", "target": "pageTitle", "contains": ["결제내역", "결제 내역", "Payment"] }, + { "type": "verify", "target": "pageContent", "notContains": ["404", "찾을 수 없습니다", "Not Found"] } + ], + "verification": [ + "페이지 제목 '결제내역' 표시", + "404 에러 메시지 미표시" + ], + "successCriteria": { + "urlPattern": "/accounting/payment", + "requiredElements": ["결제", "내역"] + } + }, + { + "id": 5, + "name": "페이지 구조 확인", + "description": "페이지 타이틀, 설명 확인", + "actions": [ + { "type": "verify", "target": "페이지 구조" } + ], + "expected": { + "pageTitle": "결제내역", + "pageDescription": "결제 내역을 확인합니다" + } + }, + { + "id": 6, + "name": "테이블 구조 확인", + "description": "결제 내역 테이블의 컬럼 헤더 확인", + "actions": [ + { "type": "verify", "target": "table columns" } + ], + "expected": { + "tableExists": true, + "hasColumns": true + } + }, + { + "id": 7, + "name": "데이터 로드 확인", + "description": "결제 내역 데이터가 테이블에 표시되는지 확인", + "actions": [ + { "type": "verify", "target": "table data" } + ], + "expected": { + "dataExists": "데이터 행 존재 또는 '데이터가 없습니다' 메시지" + } + }, + { + "id": 8, + "name": "페이지네이션 확인", + "description": "테이블 하단에 페이지네이션이 표시되는지 확인", + "actions": [ + { "type": "verify", "target": "pagination component" } + ], + "expected": { + "paginationExists": true + } + }, + { + "id": 9, + "name": "콘솔 에러 확인", + "description": "페이지 동작 중 콘솔에 에러가 발생하지 않는지 확인", + "actions": [ + { "type": "verify", "target": "console errors" } + ], + "expected": { + "noErrors": "콘솔 에러 없음" + } + } + ], + + "cleanup": { + "description": "테스트 후 정리 작업 (없음)", + "actions": [] + }, + + "notes": [ + "직접 URL 접근 금지: 반드시 메뉴 클릭으로 페이지 진입 (404 방지)", + "스크롤 필수: 회계관리 메뉴는 사이드바 하단에 위치", + "메뉴 계층: 회계관리 > 결제내역" + ] +} diff --git a/pdf-download-test.json b/pdf-download-test.json new file mode 100644 index 0000000..31cffc3 --- /dev/null +++ b/pdf-download-test.json @@ -0,0 +1,433 @@ +{ + "id": "pdf-download-test", + "name": "PDF 다운로드 전체 검사", + "description": "PDF 다운로드 버튼이 있는 모든 페이지를 순회하며 PDF 다운로드 기능과 품질을 검증하는 E2E 테스트", + "version": "1.0.0", + "createdAt": "2026-01-29", + "screenshotPolicy": { + "onErrorOnly": false, + "captureOn": ["before-download", "after-download", "error", "fail"] + }, + "baseUrl": "https://dev.codebridge-x.com", + "timeout": 180000, + "tags": ["pdf", "download", "quality", "export"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + } + }, + + "pdfTargets": [ + { + "id": "draft-box", + "name": "기안함 PDF", + "menuNavigation": { + "level1": "결재관리", + "level2": "기안함" + }, + "accessMethod": "modal", + "triggerAction": "clickFirstRow", + "pdfButtonSelector": "button:has-text('PDF')", + "pdfApiPattern": "/api/v1/approvals/*/pdf", + "expectedContentType": "application/pdf" + }, + { + "id": "approval-box", + "name": "결재함 PDF", + "menuNavigation": { + "level1": "결재관리", + "level2": "결재함" + }, + "accessMethod": "modal", + "triggerAction": "clickFirstRow", + "pdfButtonSelector": "button:has-text('PDF')", + "pdfApiPattern": "/api/v1/approvals/*/pdf", + "expectedContentType": "application/pdf" + }, + { + "id": "reference-box", + "name": "참조함 PDF", + "menuNavigation": { + "level1": "결재관리", + "level2": "참조함" + }, + "accessMethod": "modal", + "triggerAction": "clickFirstRow", + "pdfButtonSelector": "button:has-text('PDF')", + "pdfApiPattern": "/api/v1/approvals/*/pdf", + "expectedContentType": "application/pdf" + }, + { + "id": "vendor-ledger", + "name": "거래처원장 PDF", + "menuNavigation": { + "level1": "자재관리", + "level2": "거래처원장" + }, + "accessMethod": "detailPage", + "triggerAction": "clickFirstRow", + "pdfButtonSelector": "button:has-text('PDF 다운로드')", + "pdfApiPattern": "/api/v1/vendor-ledger/*/export-pdf", + "expectedContentType": "application/pdf" + } + ], + + "steps": [ + { + "id": "step-0", + "name": "PDF 샘플 폴더 준비", + "description": "PDF 파일 저장 폴더 확인", + "setup": { + "createFolders": [ + "react/tests/e2e/results/hotfix/pdf-samples/", + "react/tests/e2e/results/hotfix/screenshots/" + ] + } + }, + + { + "id": "pdf-test-1", + "name": "기안함 PDF 다운로드 테스트", + "target": "draft-box", + "critical": true, + "steps": [ + { + "id": "1-1", + "action": "navigate", + "menu": { "level1": "결재관리", "level2": "기안함" }, + "expect": { "url": "/approval/drafts" } + }, + { + "id": "1-2", + "action": "waitForTable", + "timeout": 5000, + "expect": { "rowCount": ">= 1" } + }, + { + "id": "1-3", + "action": "clickFirstRow", + "description": "첫 번째 문서 클릭하여 상세 모달 열기" + }, + { + "id": "1-4", + "action": "waitForModal", + "timeout": 3000, + "expect": { "modalVisible": true } + }, + { + "id": "1-5", + "action": "screenshot", + "name": "draft-box_pdf-preview_{timestamp}", + "selector": "[role='dialog'], .modal", + "savePath": "react/tests/e2e/results/hotfix/screenshots/", + "description": "PDF 생성 전 모달 스크린샷" + }, + { + "id": "1-6", + "action": "verifyButtonExists", + "selector": "button:has-text('PDF')", + "expect": { "visible": true } + }, + { + "id": "1-7", + "action": "setupDownloadListener", + "description": "다운로드 이벤트 리스너 설정" + }, + { + "id": "1-8", + "action": "click", + "selector": "button:has-text('PDF')", + "description": "PDF 버튼 클릭" + }, + { + "id": "1-9", + "action": "wait", + "duration": 3000, + "description": "PDF 생성 및 다운로드 대기" + }, + { + "id": "1-10", + "action": "verifyDownload", + "checks": { + "fileDownloaded": true, + "fileExtension": ".pdf", + "minFileSize": 1024, + "pdfSignature": "%PDF-" + } + }, + { + "id": "1-11", + "action": "saveDownloadedFile", + "targetPath": "react/tests/e2e/results/hotfix/pdf-samples/", + "fileName": "draft-box_{timestamp}.pdf" + }, + { + "id": "1-12", + "action": "closeModal", + "method": "pressEscape" + } + ], + "onSuccess": { + "log": "draft-box PDF 다운로드 성공", + "recordFile": true + }, + "onFail": { + "screenshot": true, + "continueToNext": true + } + }, + + { + "id": "pdf-test-2", + "name": "결재함 PDF 다운로드 테스트", + "target": "approval-box", + "critical": true, + "steps": [ + { + "id": "2-1", + "action": "navigate", + "menu": { "level1": "결재관리", "level2": "결재함" }, + "expect": { "url": "/approval/approval-box" } + }, + { + "id": "2-2", + "action": "waitForTable", + "timeout": 5000, + "expect": { "rowCount": ">= 1" } + }, + { + "id": "2-3", + "action": "clickFirstRow", + "description": "첫 번째 문서 클릭하여 상세 모달 열기" + }, + { + "id": "2-4", + "action": "waitForModal", + "timeout": 3000 + }, + { + "id": "2-5", + "action": "screenshot", + "name": "approval-box_pdf-preview_{timestamp}", + "selector": "[role='dialog'], .modal", + "savePath": "react/tests/e2e/results/hotfix/screenshots/" + }, + { + "id": "2-6", + "action": "click", + "selector": "button:has-text('PDF')" + }, + { + "id": "2-7", + "action": "wait", + "duration": 3000 + }, + { + "id": "2-8", + "action": "verifyDownload", + "checks": { + "fileDownloaded": true, + "fileExtension": ".pdf", + "minFileSize": 1024 + } + }, + { + "id": "2-9", + "action": "saveDownloadedFile", + "targetPath": "react/tests/e2e/results/hotfix/pdf-samples/", + "fileName": "approval-box_{timestamp}.pdf" + }, + { + "id": "2-10", + "action": "closeModal" + } + ] + }, + + { + "id": "pdf-test-3", + "name": "참조함 PDF 다운로드 테스트", + "target": "reference-box", + "critical": true, + "steps": [ + { + "id": "3-1", + "action": "navigate", + "menu": { "level1": "결재관리", "level2": "참조함" }, + "expect": { "url": "/approval/reference-box" } + }, + { + "id": "3-2", + "action": "waitForTable", + "timeout": 5000, + "expect": { "rowCount": ">= 1" } + }, + { + "id": "3-3", + "action": "clickFirstRow" + }, + { + "id": "3-4", + "action": "waitForModal", + "timeout": 3000 + }, + { + "id": "3-5", + "action": "screenshot", + "name": "reference-box_pdf-preview_{timestamp}", + "selector": "[role='dialog'], .modal", + "savePath": "react/tests/e2e/results/hotfix/screenshots/" + }, + { + "id": "3-6", + "action": "click", + "selector": "button:has-text('PDF')" + }, + { + "id": "3-7", + "action": "wait", + "duration": 3000 + }, + { + "id": "3-8", + "action": "verifyDownload", + "checks": { + "fileDownloaded": true, + "fileExtension": ".pdf", + "minFileSize": 1024 + } + }, + { + "id": "3-9", + "action": "saveDownloadedFile", + "targetPath": "react/tests/e2e/results/hotfix/pdf-samples/", + "fileName": "reference-box_{timestamp}.pdf" + }, + { + "id": "3-10", + "action": "closeModal" + } + ] + }, + + { + "id": "pdf-test-4", + "name": "거래처원장 PDF 다운로드 테스트", + "target": "vendor-ledger", + "critical": true, + "steps": [ + { + "id": "4-1", + "action": "navigate", + "menu": { "level1": "자재관리", "level2": "거래처원장" }, + "expect": { "url": "/accounting/vendor-ledger" } + }, + { + "id": "4-2", + "action": "waitForTable", + "timeout": 5000, + "expect": { "rowCount": ">= 1" } + }, + { + "id": "4-3", + "action": "clickFirstRow", + "description": "거래처 클릭하여 상세 페이지 이동" + }, + { + "id": "4-4", + "action": "waitForNavigation", + "timeout": 5000, + "expect": { "urlContains": "/accounting/vendor-ledger/" } + }, + { + "id": "4-5", + "action": "screenshot", + "name": "vendor-ledger_pdf-preview_{timestamp}", + "fullPage": true, + "savePath": "react/tests/e2e/results/hotfix/screenshots/" + }, + { + "id": "4-6", + "action": "verifyButtonExists", + "selector": "button:has-text('PDF 다운로드')", + "expect": { "visible": true, "count": ">= 1" } + }, + { + "id": "4-7", + "action": "click", + "selector": "button:has-text('PDF 다운로드')", + "index": 0, + "description": "첫 번째 PDF 다운로드 버튼 클릭" + }, + { + "id": "4-8", + "action": "wait", + "duration": 3000 + }, + { + "id": "4-9", + "action": "verifyDownload", + "checks": { + "fileDownloaded": true, + "fileExtension": ".pdf", + "minFileSize": 1024 + } + }, + { + "id": "4-10", + "action": "saveDownloadedFile", + "targetPath": "react/tests/e2e/results/hotfix/pdf-samples/", + "fileName": "vendor-ledger_{timestamp}.pdf" + }, + { + "id": "4-11", + "action": "navigateBack", + "description": "목록으로 돌아가기" + } + ] + } + ], + + "pdfQualityChecklist": { + "description": "다운로드된 PDF 파일 수동 검증 체크리스트", + "items": [ + { "id": "q1", "category": "파일", "item": "PDF 파일이 정상적으로 열리는가?" }, + { "id": "q2", "category": "폰트", "item": "한글 폰트가 깨지지 않고 정상 표시되는가?" }, + { "id": "q3", "category": "테이블", "item": "테이블 경계선이 올바르게 표시되는가?" }, + { "id": "q4", "category": "정렬", "item": "숫자/금액이 우측 정렬되어 있는가?" }, + { "id": "q5", "category": "레이아웃", "item": "여백(margin/padding)이 적절한가?" }, + { "id": "q6", "category": "페이지", "item": "헤더/푸터가 각 페이지에 올바르게 표시되는가?" }, + { "id": "q7", "category": "이미지", "item": "로고/이미지가 정상 표시되는가?" }, + { "id": "q8", "category": "페이지나눔", "item": "페이지 나눔이 적절한 위치에서 발생하는가?" }, + { "id": "q9", "category": "색상", "item": "배경색/강조색이 올바르게 적용되었는가?" }, + { "id": "q10", "category": "오버플로우", "item": "텍스트가 잘리거나 겹치지 않는가?" } + ] + }, + + "outputPaths": { + "pdfSamples": "react/tests/e2e/results/hotfix/pdf-samples/", + "screenshots": "react/tests/e2e/results/hotfix/screenshots/", + "report": "react/tests/e2e/results/hotfix/" + }, + + "reportTemplate": { + "successFile": "OK-pdf-download-test_{timestamp}.md", + "failFile": "Fail-pdf-download-test_{timestamp}.md", + "sections": [ + "테스트 요약", + "개별 PDF 테스트 결과", + "다운로드된 PDF 파일 목록", + "품질 체크리스트 (수동 확인 필요)" + ] + }, + + "executionNotes": [ + "이 시나리오는 'PDF 다운로드 전체 검사해줘' 명령으로 실행", + "각 PDF 버튼 클릭 후 3초 대기 (PDF 생성 시간)", + "다운로드된 PDF는 pdf-samples 폴더에 타임스탬프와 함께 저장", + "테스트 완료 후 PDF 파일을 열어 품질 체크리스트 수동 확인 필요" + ] +} diff --git a/permission-management.json b/permission-management.json new file mode 100644 index 0000000..dd8a8bc --- /dev/null +++ b/permission-management.json @@ -0,0 +1,518 @@ +{ + "id": "permission-management", + "name": "설정 - 권한관리", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "url": "/ko/settings/permissions", + "menuNavigation": { + "level1": "설정", + "level2": "권한관리", + "expectedUrl": "/ko/settings/permissions" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", + "level1": "설정", + "level2": "권한관리", + "alternativeLevel1Names": ["설정", "Settings", "환경설정", "시스템설정", "관리"], + "alternativeLevel2Names": ["권한관리", "권한 관리", "Permissions", "역할관리", "Role Management"], + "scrollConfig": { + "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", + "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + } + }, + "expectedAPIs": [ + { + "method": "GET", + "path": "/api/roles", + "description": "역할 목록 조회" + }, + { + "method": "GET", + "path": "/api/roles/stats", + "description": "역할 통계 조회" + }, + { + "method": "POST", + "path": "/api/roles", + "description": "역할 생성" + }, + { + "method": "GET", + "path": "/api/roles/{id}", + "description": "역할 상세 조회" + }, + { + "method": "PUT", + "path": "/api/roles/{id}", + "description": "역할 수정" + }, + { + "method": "DELETE", + "path": "/api/roles/{id}", + "description": "역할 삭제" + }, + { + "method": "GET", + "path": "/api/roles/{id}/permissions", + "description": "역할별 권한 매트릭스 조회" + }, + { + "method": "PUT", + "path": "/api/roles/{id}/permissions", + "description": "역할별 권한 저장" + } + ], + "steps": [ + { + "id": "step-00", + "name": "사이드바 메뉴 탐색 준비", + "description": "사이드바를 최상단으로 스크롤하고 메뉴 구조 파악", + "actions": [ + { "type": "scroll", "target": "sidebar", "direction": "top", "description": "사이드바 최상단으로 스크롤" }, + { "type": "wait", "duration": 500 } + ] + }, + { + "id": "step-01", + "name": "2단계 메뉴 진입: 설정 > 권한관리", + "description": "사이드바를 스크롤하며 설정 > 권한관리 메뉴를 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "설정", + "alternativeTexts": ["설정", "Settings", "환경설정", "시스템설정"], + "scrollContainer": "sidebar", + "maxAttempts": 10, + "description": "스크롤하며 설정 메뉴 찾기" + }, + { "type": "click", "target": "설정", "description": "설정 메뉴 클릭" }, + { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, + { + "type": "scrollAndFind", + "target": "권한관리", + "alternativeTexts": ["권한관리", "권한 관리", "Permissions", "역할관리"], + "scrollContainer": "submenu", + "maxAttempts": 5, + "description": "서브메뉴에서 권한관리 찾기" + }, + { "type": "click", "target": "권한관리", "description": "권한관리 메뉴 클릭" }, + { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 } + ], + "expected": { + "url": "/ko/settings/permissions", + "title": "권한관리", + "authenticated": true + }, + "verification": [ + "설정 메뉴가 펼쳐졌는지 확인", + "권한관리 서브메뉴 클릭 성공", + "페이지 타이틀 '권한관리' 확인", + "설명 '역할 기반 권한을 관리합니다' 확인", + "Shield 아이콘 표시", + "404 에러 없이 페이지 로드 완료" + ] + }, + { + "id": "step-02", + "name": "통계 카드 확인", + "action": "verify", + "verification": [ + "통계 카드 4개 표시: 전체 역할, 공개, 숨김, 사용 중", + "각 카드에 아이콘과 숫자 표시" + ] + }, + { + "id": "step-03", + "name": "탭 확인", + "action": "verify", + "verification": [ + "탭 3개 표시: 전체, 공개, 숨김", + "각 탭에 카운트 표시" + ] + }, + { + "id": "step-04", + "name": "테이블 구조 확인", + "action": "verify", + "verification": [ + "테이블 컬럼 확인: 체크박스, 번호, 역할, 설명, 상태, 등록일", + "역할 등록 버튼 존재", + "검색창 존재 (placeholder: '역할명, 설명 검색...')" + ] + }, + { + "id": "step-05", + "name": "탭 필터 테스트 - 공개", + "action": "click", + "target": "공개 탭", + "verification": [ + "공개 탭 활성화", + "공개 상태 역할만 표시", + "데이터 필터링 확인" + ] + }, + { + "id": "step-06", + "name": "탭 필터 테스트 - 숨김", + "action": "click", + "target": "숨김 탭", + "verification": [ + "숨김 탭 활성화", + "숨김 상태 역할만 표시", + "데이터 필터링 확인" + ] + }, + { + "id": "step-07", + "name": "탭 필터 테스트 - 전체", + "action": "click", + "target": "전체 탭", + "verification": [ + "전체 탭 활성화", + "모든 역할 표시" + ] + }, + { + "id": "step-08", + "name": "검색 기능 테스트", + "action": "type", + "target": "검색 입력 필드", + "value": "관리자", + "verification": [ + "'관리자' 포함된 역할만 표시", + "실시간 필터링 동작" + ] + }, + { + "id": "step-09", + "name": "검색 초기화", + "action": "clear", + "target": "검색 입력 필드", + "verification": [ + "검색어 제거됨", + "전체 목록 복원" + ] + }, + { + "id": "step-10", + "name": "역할 등록 페이지 이동", + "action": "click", + "target": "역할 등록 버튼", + "verification": [ + "URL 변경: /settings/permissions?mode=new", + "페이지 타이틀 변경 확인" + ] + }, + { + "id": "step-11", + "name": "역할명 입력", + "action": "type", + "target": "권한명 입력 필드", + "value": "E2E 테스트 역할", + "verification": [ + "입력 값 반영 확인" + ] + }, + { + "id": "step-12", + "name": "설명 입력", + "action": "type", + "target": "설명 입력 필드 (있는 경우)", + "value": "E2E 테스트를 위한 역할입니다", + "verification": [ + "입력 값 반영 확인" + ] + }, + { + "id": "step-13", + "name": "상태 선택", + "action": "select", + "target": "상태 드롭다운", + "value": "공개", + "verification": [ + "상태 선택 확인" + ] + }, + { + "id": "step-14", + "name": "역할 등록", + "action": "click", + "target": "등록 버튼", + "critical": true, + "verification": [ + "현재 URL 저장", + "등록 버튼 클릭", + "URL 변경 여부 확인 (에러 페이지 이동 방지)", + "에러 텍스트 없음 ('페이지를 찾을 수 없습니다', '404', 'Not Found' 등)", + "API 호출 확인: POST /api/roles", + "API 응답 200 OK 확인", + "목록 페이지로 리다이렉트 또는 상세 페이지 이동", + "성공 토스트 메시지 확인" + ] + }, + { + "id": "step-15", + "name": "목록에서 신규 역할 확인", + "action": "verify", + "verification": [ + "목록 페이지 진입 (URL: /settings/permissions)", + "'E2E 테스트 역할' 목록에 표시", + "통계 카드 숫자 증가 확인" + ] + }, + { + "id": "step-16", + "name": "역할 상세 페이지 이동", + "action": "click", + "target": "E2E 테스트 역할 행", + "verification": [ + "URL 변경: /settings/permissions/{id}", + "페이지 타이틀 '권한 상세' 확인", + "기본 정보 섹션 표시", + "메뉴별 권한 테이블 표시" + ] + }, + { + "id": "step-17", + "name": "기본 정보 확인", + "action": "verify", + "verification": [ + "권한명 입력 필드에 'E2E 테스트 역할' 표시", + "상태 드롭다운에 '공개' 선택됨", + "삭제 버튼 존재", + "수정 버튼 존재", + "목록으로 버튼 존재" + ] + }, + { + "id": "step-18", + "name": "권한 테이블 구조 확인", + "action": "verify", + "verification": [ + "테이블 헤더: 메뉴, 조회, 생성, 수정, 삭제, 승인, 내보내기, 관리", + "각 헤더에 전체 선택 체크박스 존재", + "메뉴 목록 표시 (부모 메뉴 접기/펼치기 아이콘 있음)", + "각 메뉴별 권한 체크박스 존재" + ] + }, + { + "id": "step-19", + "name": "부모 메뉴 펼치기", + "action": "click", + "target": "첫 번째 부모 메뉴 펼치기 아이콘", + "verification": [ + "자식 메뉴 목록 표시", + "아이콘이 ChevronDown으로 변경", + "자식 메뉴는 들여쓰기되어 표시" + ] + }, + { + "id": "step-20", + "name": "개별 권한 체크박스 토글", + "action": "click", + "target": "첫 번째 메뉴의 '조회' 체크박스", + "verification": [ + "체크박스 상태 변경", + "자동 저장 동작 (API 호출 확인)" + ] + }, + { + "id": "step-21", + "name": "컬럼 전체 선택", + "action": "click", + "target": "'조회' 헤더 체크박스", + "verification": [ + "모든 메뉴의 '조회' 권한 체크", + "자동 저장 동작 확인" + ] + }, + { + "id": "step-22", + "name": "권한명 수정", + "action": "type", + "target": "권한명 입력 필드", + "value": "E2E 테스트 역할 (수정됨)", + "verification": [ + "입력 값 변경 확인" + ] + }, + { + "id": "step-23", + "name": "권한명 수정 저장 (blur)", + "action": "blur", + "target": "권한명 입력 필드", + "verification": [ + "자동 저장 동작 (API 호출)", + "PUT /api/roles/{id} 확인" + ] + }, + { + "id": "step-24", + "name": "상태 변경", + "action": "select", + "target": "상태 드롭다운", + "value": "숨김", + "verification": [ + "상태 변경 확인", + "자동 저장 동작" + ] + }, + { + "id": "step-25", + "name": "목록으로 이동", + "action": "click", + "target": "목록으로 버튼", + "verification": [ + "URL 변경: /settings/permissions", + "목록 페이지 표시" + ] + }, + { + "id": "step-26", + "name": "수정된 역할 확인", + "action": "verify", + "verification": [ + "'E2E 테스트 역할 (수정됨)' 목록에 표시", + "상태 Badge '숨김'으로 표시" + ] + }, + { + "id": "step-27", + "name": "숨김 탭으로 이동", + "action": "click", + "target": "숨김 탭", + "verification": [ + "숨김 상태 역할만 표시", + "'E2E 테스트 역할 (수정됨)' 표시됨" + ] + }, + { + "id": "step-28", + "name": "전체 탭으로 복귀", + "action": "click", + "target": "전체 탭", + "verification": [ + "모든 역할 표시" + ] + }, + { + "id": "step-29", + "name": "체크박스 선택", + "action": "click", + "target": "E2E 테스트 역할 체크박스", + "verification": [ + "체크박스 선택됨", + "'1개 항목 선택됨' 표시", + "'선택 삭제 (1)' 버튼 표시", + "작업 컬럼 추가 (권한 설정, 수정, 삭제 버튼)" + ] + }, + { + "id": "step-30", + "name": "단일 삭제 - 작업 컬럼 삭제 버튼", + "action": "click", + "target": "작업 컬럼의 삭제 버튼", + "verification": [ + "삭제 확인 다이얼로그 표시", + "다이얼로그 제목: '역할 삭제'", + "경고 메시지: '이 역할을 사용 중인 사원이 있으면 해당 사원의 역할이 초기화됩니다.'" + ] + }, + { + "id": "step-31", + "name": "삭제 취소", + "action": "click", + "target": "다이얼로그 취소 버튼", + "verification": [ + "다이얼로그 닫힘", + "역할 삭제되지 않음" + ] + }, + { + "id": "step-32", + "name": "일괄 삭제 버튼 클릭", + "action": "click", + "target": "선택 삭제 버튼", + "verification": [ + "삭제 확인 다이얼로그 표시", + "'선택한 1개의 역할을 삭제하시겠습니까?' 메시지" + ] + }, + { + "id": "step-33", + "name": "일괄 삭제 실행", + "action": "click", + "target": "다이얼로그 삭제 버튼", + "critical": true, + "verification": [ + "삭제 중 로딩 표시 ('삭제 중...')", + "API 호출: DELETE /api/roles/{id}", + "API 응답 200 OK 확인", + "다이얼로그 닫힘", + "성공 토스트 메시지 확인", + "목록에서 삭제된 역할 제거됨", + "통계 카드 숫자 감소 확인" + ] + }, + { + "id": "step-34", + "name": "체크박스 전체 선택", + "action": "click", + "target": "테이블 헤더 체크박스", + "verification": [ + "현재 페이지의 모든 항목 선택", + "'N개 항목 선택됨' 표시", + "'선택 삭제 (N)' 버튼 표시" + ] + }, + { + "id": "step-35", + "name": "전체 선택 해제", + "action": "click", + "target": "테이블 헤더 체크박스", + "verification": [ + "모든 선택 해제", + "'선택 삭제' 버튼 숨김", + "작업 컬럼 제거" + ] + }, + { + "id": "step-36", + "name": "페이지네이션 테스트 (데이터 충분 시)", + "action": "verify", + "verification": [ + "페이지네이션 컨트롤 존재 여부 확인", + "페이지당 20개 항목 표시", + "총 페이지 수 표시" + ] + }, + { + "id": "step-37", + "name": "반응형 테스트 - 모바일", + "action": "resize", + "width": 375, + "height": 667, + "verification": [ + "모바일 카드 레이아웃으로 변경", + "각 카드에 역할명, 상태, 설명, 등록일 표시", + "카드에 권한 설정, 수정 버튼 존재" + ] + } + ], + "testData": { + "role": { + "name": "E2E 테스트 역할", + "description": "E2E 테스트를 위한 역할입니다", + "status": "active" + } + }, + "cleanup": { + "description": "Step-33에서 테스트 데이터 삭제 완료" + } +} diff --git a/popup-management.json b/popup-management.json new file mode 100644 index 0000000..c5100c8 --- /dev/null +++ b/popup-management.json @@ -0,0 +1,824 @@ +{ + "id": "popup-management", + "name": "설정 - 팝업관리", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "팝업 관리 기능 테스트 - 목록 조회, 검색, 등록, 수정, 삭제 기능", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/ko/settings/popup-management", + "menuNavigation": { + "level1": "설정", + "level2": "팝업관리", + "expectedUrl": "/ko/settings/popup-management" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebarSelector": ".sidebar-scroll, [data-testid='sidebar'], nav[role='navigation']", + "scrollConfig": { + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + }, + "level1": { + "text": "설정", + "fallbackSelectors": [ + "button:has-text('설정')", + "[data-menu='settings']", + "a[href*='settings']" + ] + }, + "level2": { + "text": "팝업관리", + "fallbackSelectors": [ + "a:has-text('팝업관리')", + "[data-submenu='popup-management']", + "a[href*='popup-management']" + ] + }, + "fallbackDirectUrl": "/ko/settings/popup-management" + }, + "expectedAPIs": [ + { + "method": "GET", + "path": "/api/v1/settings/popups", + "description": "팝업 목록 조회" + }, + { + "method": "GET", + "path": "/api/v1/settings/popups/:id", + "description": "팝업 상세 조회" + }, + { + "method": "POST", + "path": "/api/v1/settings/popups", + "description": "팝업 등록" + }, + { + "method": "PUT", + "path": "/api/v1/settings/popups/:id", + "description": "팝업 수정" + }, + { + "method": "DELETE", + "path": "/api/v1/settings/popups/:id", + "description": "팝업 삭제" + } + ], + "steps": [ + { + "step": 0, + "name": "사이드바 준비", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll, [data-testid=\"sidebar\"], nav[role=\"navigation\"]')?.scrollTo({top: 0, behavior: 'instant'})" + }, + { "type": "wait", "duration": 500 } + ], + "expected": { + "sidebarReady": true + }, + "validation": ["사이드바 스크롤 초기화"] + }, + { + "step": 1, + "name": "2단계 메뉴 진입: 설정 > 팝업관리", + "description": "설정 > 팝업관리 메뉴로 이동하여 페이지 로드 확인 (scrollAndFind 패턴 사용)", + "navigationPattern": "scrollAndFind", + "actions": [ + { + "type": "scrollAndFind", + "target": "설정", + "scrollContainer": ".sidebar-scroll, [data-testid='sidebar'], nav[role='navigation']", + "scrollStep": 200, + "maxAttempts": 10 + }, + { "type": "click", "target": "설정" }, + { "type": "wait", "duration": 500 }, + { + "type": "scrollAndFind", + "target": "팝업관리", + "scrollContainer": ".sidebar-scroll, [data-testid='sidebar'], nav[role='navigation']", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "팝업관리" }, + { "type": "wait", "target": "페이지 로드 완료" } + ], + "fallback": { + "type": "directNavigation", + "url": "/ko/settings/popup-management" + }, + "expected": { + "url": "/ko/settings/popup-management", + "title": "팝업관리", + "authenticated": true + }, + "validation": ["페이지 제목 확인", "테이블 표시 확인"] + }, + { + "step": 2, + "name": "페이지 제목 확인", + "action": "verify", + "target": "heading '팝업관리'", + "expected": "'팝업관리' 제목 표시됨", + "validation": ["UI 렌더링"] + }, + { + "step": 3, + "name": "페이지 설명 확인", + "action": "verify", + "target": "paragraph '팝업 목록을 관리합니다.'", + "expected": "설명 텍스트 표시됨", + "validation": ["UI 렌더링"] + }, + { + "step": 4, + "name": "팝업 등록 버튼 확인", + "action": "verify", + "target": "button '팝업 등록'", + "expected": "'팝업 등록' 버튼 표시됨", + "validation": ["UI 렌더링"] + }, + { + "step": 5, + "name": "검색 입력 필드 확인", + "action": "verify", + "target": "textbox '제목, 작성자로 검색...'", + "expected": "검색 필드 표시됨", + "validation": ["UI 렌더링"] + }, + { + "step": 6, + "name": "테이블 헤더 확인", + "action": "verify", + "target": "table headers", + "expected": "번호, 대상, 제목, 상태, 작성자, 등록일, 기간 컬럼 표시됨", + "validation": ["UI 렌더링"] + }, + { + "step": 7, + "name": "테이블 데이터 행 확인", + "action": "verify", + "target": "table rows", + "expected": "8개 데이터 행 표시됨", + "validation": ["데이터 로드"] + }, + { + "step": 8, + "name": "전체 항목 수 표시 확인", + "action": "verify", + "target": "text '전체 8개 중 1-8개 표시'", + "expected": "전체 항목 수 표시됨", + "validation": ["UI 렌더링"] + }, + { + "step": 9, + "name": "검색 기능 - 제목으로 검색", + "action": "type", + "target": "textbox '제목, 작성자로 검색...'", + "value": "시스템", + "expected": "검색어 입력됨", + "validation": ["검색/필터"] + }, + { + "step": 10, + "name": "검색 결과 확인", + "action": "verify", + "target": "table rows", + "expected": "'시스템' 키워드 포함 행만 표시됨", + "validation": ["검색/필터"] + }, + { + "step": 11, + "name": "검색어 초기화", + "action": "clear", + "target": "textbox '제목, 작성자로 검색...'", + "expected": "검색어 지워짐", + "validation": ["검색/필터"] + }, + { + "step": 12, + "name": "전체 목록 재표시 확인", + "action": "verify", + "target": "table rows", + "expected": "전체 8개 행 다시 표시됨", + "validation": ["검색/필터"] + }, + { + "step": 13, + "name": "팝업 등록 페이지 이동", + "action": "click", + "target": "button '팝업 등록'", + "expected": "/settings/popup-management?mode=new 페이지로 이동", + "validation": ["등록/저장"] + }, + { + "step": 14, + "name": "등록 페이지 URL 확인", + "action": "verify", + "target": "url", + "expected": "URL이 /settings/popup-management?mode=new", + "validation": ["등록/저장"] + }, + { + "step": 15, + "name": "등록 페이지 제목 확인", + "action": "verify", + "target": "heading '팝업관리 상세'", + "expected": "'팝업관리 상세' 제목 표시됨", + "validation": ["UI 렌더링"] + }, + { + "step": 16, + "name": "팝업 정보 섹션 확인", + "action": "verify", + "target": "heading '팝업 정보 *'", + "expected": "'팝업 정보 *' 섹션 표시됨", + "validation": ["UI 렌더링"] + }, + { + "step": 17, + "name": "대상 Combobox 확인", + "action": "verify", + "target": "combobox (대상)", + "expected": "대상 선택 combobox 표시됨 (기본값: 전사)", + "validation": ["UI 렌더링"] + }, + { + "step": 18, + "name": "대상 Combobox 클릭", + "action": "click", + "target": "combobox (대상)", + "expected": "드롭다운 옵션 표시됨", + "validation": ["UI 동작"] + }, + { + "step": 19, + "name": "대상 옵션 확인", + "action": "verify", + "target": "combobox options", + "expected": "'전사', '부서별' 옵션 표시됨", + "validation": ["UI 렌더링"] + }, + { + "step": 20, + "name": "대상 '부서별' 선택", + "action": "click", + "target": "option '부서별'", + "expected": "'부서별' 선택됨", + "validation": ["UI 동작"] + }, + { + "step": 21, + "name": "기간 시작일 필드 확인", + "action": "verify", + "target": "textbox (기간 시작일)", + "expected": "시작일 입력 필드 표시됨 (기본값: 오늘 날짜)", + "validation": ["UI 렌더링"] + }, + { + "step": 22, + "name": "기간 종료일 필드 확인", + "action": "verify", + "target": "textbox (기간 종료일)", + "expected": "종료일 입력 필드 표시됨 (기본값: 오늘 날짜)", + "validation": ["UI 렌더링"] + }, + { + "step": 23, + "name": "제목 필드 확인", + "action": "verify", + "target": "textbox '제목 *'", + "expected": "제목 입력 필드 표시됨", + "validation": ["UI 렌더링"] + }, + { + "step": 24, + "name": "제목 입력", + "action": "type", + "target": "textbox '제목 *'", + "value": "E2E 테스트 팝업", + "expected": "제목 입력됨", + "validation": ["데이터 입력"] + }, + { + "step": 25, + "name": "내용 편집기 확인", + "action": "verify", + "target": "editor toolbar", + "expected": "텍스트 편집 도구 모음 표시됨 (굵게, 기울임, 밑줄, 취소선, 정렬, 리스트, 링크, 이미지)", + "validation": ["UI 렌더링"] + }, + { + "step": 26, + "name": "내용 입력 영역 확인", + "action": "verify", + "target": "paragraph '내용을 입력해주세요'", + "expected": "내용 입력 영역 표시됨", + "validation": ["UI 렌더링"] + }, + { + "step": 27, + "name": "내용 입력", + "action": "type", + "target": "editor content area", + "value": "이것은 E2E 테스트용 팝업입니다.", + "expected": "내용 입력됨", + "validation": ["데이터 입력"] + }, + { + "step": 28, + "name": "상태 Radio 버튼 확인", + "action": "verify", + "target": "radiogroup (상태)", + "expected": "'사용안함', '사용함' 라디오 버튼 표시됨", + "validation": ["UI 렌더링"] + }, + { + "step": 29, + "name": "기본 상태 확인", + "action": "verify", + "target": "radio '사용안함'", + "expected": "'사용안함' 선택됨 (기본값)", + "validation": ["UI 렌더링"] + }, + { + "step": 30, + "name": "상태 '사용함' 선택", + "action": "click", + "target": "radio '사용함'", + "expected": "'사용함' 선택됨", + "validation": ["UI 동작"] + }, + { + "step": 31, + "name": "작성자 필드 확인", + "action": "verify", + "target": "textbox (작성자) [disabled]", + "expected": "작성자 필드 표시됨 (비활성화, 자동 설정: 홍길동)", + "validation": ["UI 렌더링"] + }, + { + "step": 32, + "name": "등록일시 필드 확인", + "action": "verify", + "target": "textbox (등록일시) [disabled]", + "expected": "등록일시 필드 표시됨 (비활성화, 자동 설정)", + "validation": ["UI 렌더링"] + }, + { + "step": 33, + "name": "취소 버튼 확인", + "action": "verify", + "target": "button '취소'", + "expected": "'취소' 버튼 표시됨", + "validation": ["UI 렌더링"] + }, + { + "step": 34, + "name": "등록 버튼 확인", + "action": "verify", + "target": "button '등록'", + "expected": "'등록' 버튼 표시됨", + "validation": ["UI 렌더링"] + }, + { + "step": 35, + "name": "등록 전 URL 저장", + "action": "store", + "target": "current url", + "expected": "URL 저장됨", + "validation": ["등록/저장"] + }, + { + "step": 36, + "name": "등록 버튼 클릭", + "action": "click", + "target": "button '등록'", + "expected": "팝업 등록 요청 전송", + "validation": ["등록/저장"] + }, + { + "step": 37, + "name": "등록 후 URL 확인", + "action": "verify", + "target": "url", + "expected": "URL이 /settings/popup-management (목록 페이지로 이동)", + "validation": ["등록/저장"] + }, + { + "step": 38, + "name": "등록 성공 토스트 확인", + "action": "verify", + "target": "toast message", + "expected": "'팝업이 등록되었습니다' 토스트 표시됨", + "validation": ["등록/저장"] + }, + { + "step": 39, + "name": "등록 API 호출 확인", + "action": "verify", + "target": "network request", + "expected": "POST /api/v1/settings/popups 호출됨 (200 OK)", + "validation": ["등록/저장"] + }, + { + "step": 40, + "name": "신규 팝업 목록 확인", + "action": "verify", + "target": "table rows", + "expected": "신규 등록된 팝업이 목록에 표시됨", + "validation": ["데이터 지속성"] + }, + { + "step": 41, + "name": "첫 번째 팝업 행 클릭", + "action": "click", + "target": "row (첫 번째 팝업)", + "expected": "상세 페이지로 이동", + "validation": ["UI 동작"] + }, + { + "step": 42, + "name": "상세 페이지 URL 확인", + "action": "verify", + "target": "url", + "expected": "URL이 /settings/popup-management/1", + "validation": ["UI 동작"] + }, + { + "step": 43, + "name": "상세 페이지 제목 확인", + "action": "verify", + "target": "heading '팝업관리 상세'", + "expected": "'팝업관리 상세' 제목 표시됨", + "validation": ["UI 렌더링"] + }, + { + "step": 44, + "name": "팝업 정보 섹션 확인", + "action": "verify", + "target": "heading '팝업 정보'", + "expected": "'팝업 정보' 섹션 표시됨", + "validation": ["UI 렌더링"] + }, + { + "step": 45, + "name": "상태 뱃지 확인", + "action": "verify", + "target": "badge (상태)", + "expected": "'사용함' 뱃지 표시됨", + "validation": ["UI 렌더링"] + }, + { + "step": 46, + "name": "대상 정보 확인", + "action": "verify", + "target": "definition (대상)", + "expected": "'전사' 표시됨", + "validation": ["데이터 로드"] + }, + { + "step": 47, + "name": "작성자 정보 확인", + "action": "verify", + "target": "definition (작성자)", + "expected": "작성자명 표시됨", + "validation": ["데이터 로드"] + }, + { + "step": 48, + "name": "제목 정보 확인", + "action": "verify", + "target": "definition (제목)", + "expected": "'시스템 점검 안내' 표시됨", + "validation": ["데이터 로드"] + }, + { + "step": 49, + "name": "상태 정보 확인", + "action": "verify", + "target": "definition (상태)", + "expected": "'사용함' 표시됨", + "validation": ["데이터 로드"] + }, + { + "step": 50, + "name": "기간 정보 확인", + "action": "verify", + "target": "definition (기간)", + "expected": "기간 표시됨 (예: 2025-12-24 ~ 2026-01-08)", + "validation": ["데이터 로드"] + }, + { + "step": 51, + "name": "등록일시 정보 확인", + "action": "verify", + "target": "definition (등록일시)", + "expected": "등록일 표시됨", + "validation": ["데이터 로드"] + }, + { + "step": 52, + "name": "내용 정보 확인", + "action": "verify", + "target": "definition (내용)", + "expected": "팝업 내용 표시됨", + "validation": ["데이터 로드"] + }, + { + "step": 53, + "name": "목록으로 버튼 확인", + "action": "verify", + "target": "button '목록으로'", + "expected": "'목록으로' 버튼 표시됨", + "validation": ["UI 렌더링"] + }, + { + "step": 54, + "name": "삭제 버튼 확인", + "action": "verify", + "target": "button '삭제'", + "expected": "'삭제' 버튼 표시됨", + "validation": ["UI 렌더링"] + }, + { + "step": 55, + "name": "수정 버튼 확인", + "action": "verify", + "target": "button '수정'", + "expected": "'수정' 버튼 표시됨", + "validation": ["UI 렌더링"] + }, + { + "step": 56, + "name": "수정 페이지 이동", + "action": "click", + "target": "button '수정'", + "expected": "/settings/popup-management/1?mode=edit 페이지로 이동", + "validation": ["등록/저장"] + }, + { + "step": 57, + "name": "수정 페이지 URL 확인", + "action": "verify", + "target": "url", + "expected": "URL이 /settings/popup-management/1?mode=edit", + "validation": ["등록/저장"] + }, + { + "step": 58, + "name": "수정 페이지 제목 확인", + "action": "verify", + "target": "heading '팝업관리 상세'", + "expected": "'팝업관리 상세' 제목 표시됨", + "validation": ["UI 렌더링"] + }, + { + "step": 59, + "name": "기존 데이터 로드 확인 - 대상", + "action": "verify", + "target": "combobox (대상)", + "expected": "'전사' 선택되어 있음", + "validation": ["데이터 로드"] + }, + { + "step": 60, + "name": "기존 데이터 로드 확인 - 제목", + "action": "verify", + "target": "textbox '제목 *'", + "expected": "'시스템 점검 안내' 입력되어 있음", + "validation": ["데이터 로드"] + }, + { + "step": 61, + "name": "기존 데이터 로드 확인 - 내용", + "action": "verify", + "target": "editor content area", + "expected": "기존 내용 표시됨", + "validation": ["데이터 로드"] + }, + { + "step": 62, + "name": "기존 데이터 로드 확인 - 상태", + "action": "verify", + "target": "radio '사용함'", + "expected": "'사용함' 선택되어 있음", + "validation": ["데이터 로드"] + }, + { + "step": 63, + "name": "제목 수정", + "action": "clear_and_type", + "target": "textbox '제목 *'", + "value": "시스템 점검 안내 (수정됨)", + "expected": "제목 수정됨", + "validation": ["데이터 입력"] + }, + { + "step": 64, + "name": "내용 수정", + "action": "clear_and_type", + "target": "editor content area", + "value": "수정된 내용입니다.", + "expected": "내용 수정됨", + "validation": ["데이터 입력"] + }, + { + "step": 65, + "name": "상태 변경 - 사용안함 선택", + "action": "click", + "target": "radio '사용안함'", + "expected": "'사용안함' 선택됨", + "validation": ["UI 동작"] + }, + { + "step": 66, + "name": "저장 버튼 확인", + "action": "verify", + "target": "button '저장'", + "expected": "'저장' 버튼 표시됨", + "validation": ["UI 렌더링"] + }, + { + "step": 67, + "name": "저장 전 URL 저장", + "action": "store", + "target": "current url", + "expected": "URL 저장됨", + "validation": ["등록/저장"] + }, + { + "step": 68, + "name": "저장 버튼 클릭", + "action": "click", + "target": "button '저장'", + "expected": "팝업 수정 요청 전송", + "validation": ["등록/저장"] + }, + { + "step": 69, + "name": "저장 후 URL 확인", + "action": "verify", + "target": "url", + "expected": "URL이 /settings/popup-management/1 (상세 페이지로 이동)", + "validation": ["등록/저장"] + }, + { + "step": 70, + "name": "저장 성공 토스트 확인", + "action": "verify", + "target": "toast message", + "expected": "'팝업이 수정되었습니다' 토스트 표시됨", + "validation": ["등록/저장"] + }, + { + "step": 71, + "name": "수정 API 호출 확인", + "action": "verify", + "target": "network request", + "expected": "PUT /api/v1/settings/popups/1 호출됨 (200 OK)", + "validation": ["등록/저장"] + }, + { + "step": 72, + "name": "수정된 데이터 확인 - 제목", + "action": "verify", + "target": "definition (제목)", + "expected": "'시스템 점검 안내 (수정됨)' 표시됨", + "validation": ["데이터 지속성"] + }, + { + "step": 73, + "name": "수정된 데이터 확인 - 내용", + "action": "verify", + "target": "definition (내용)", + "expected": "'수정된 내용입니다.' 표시됨", + "validation": ["데이터 지속성"] + }, + { + "step": 74, + "name": "수정된 데이터 확인 - 상태", + "action": "verify", + "target": "definition (상태)", + "expected": "'사용안함' 표시됨", + "validation": ["데이터 지속성"] + }, + { + "step": 75, + "name": "목록으로 이동", + "action": "click", + "target": "button '목록으로'", + "expected": "/settings/popup-management 페이지로 이동", + "validation": ["UI 동작"] + }, + { + "step": 76, + "name": "목록 페이지 URL 확인", + "action": "verify", + "target": "url", + "expected": "URL이 /settings/popup-management", + "validation": ["UI 동작"] + }, + { + "step": 77, + "name": "수정된 팝업 목록 확인", + "action": "verify", + "target": "table rows", + "expected": "수정된 팝업 정보가 목록에 반영됨", + "validation": ["데이터 지속성"] + }, + { + "step": 78, + "name": "페이지 새로고침", + "action": "refresh", + "target": "page", + "expected": "페이지 새로고침됨", + "validation": ["데이터 지속성"] + }, + { + "step": 79, + "name": "새로고침 후 데이터 유지 확인", + "action": "verify", + "target": "table rows", + "expected": "수정된 데이터가 유지됨", + "validation": ["데이터 지속성"] + }, + { + "step": 80, + "name": "삭제 테스트 - 팝업 상세 페이지 이동", + "action": "click", + "target": "row (수정한 팝업)", + "expected": "상세 페이지로 이동", + "validation": ["삭제 기능"] + }, + { + "step": 81, + "name": "삭제 버튼 클릭", + "action": "click", + "target": "button '삭제'", + "expected": "삭제 확인 다이얼로그 표시", + "validation": ["삭제 기능"] + }, + { + "step": 82, + "name": "삭제 확인 다이얼로그 확인", + "action": "verify", + "target": "dialog", + "expected": "삭제 확인 메시지 표시됨", + "validation": ["삭제 기능"] + }, + { + "step": 83, + "name": "삭제 확인", + "action": "click", + "target": "button '확인' (dialog)", + "expected": "팝업 삭제 요청 전송", + "validation": ["삭제 기능"] + }, + { + "step": 84, + "name": "삭제 후 URL 확인", + "action": "verify", + "target": "url", + "expected": "URL이 /settings/popup-management (목록 페이지로 이동)", + "validation": ["삭제 기능"] + }, + { + "step": 85, + "name": "삭제 성공 토스트 확인", + "action": "verify", + "target": "toast message", + "expected": "'팝업이 삭제되었습니다' 토스트 표시됨", + "validation": ["삭제 기능"] + }, + { + "step": 86, + "name": "삭제 API 호출 확인", + "action": "verify", + "target": "network request", + "expected": "DELETE /api/v1/settings/popups/:id 호출됨 (200 OK)", + "validation": ["삭제 기능"] + }, + { + "step": 87, + "name": "삭제된 팝업 목록에서 제거 확인", + "action": "verify", + "target": "table rows", + "expected": "삭제된 팝업이 목록에서 사라짐", + "validation": ["삭제 기능"] + }, + { + "step": 88, + "name": "전체 항목 수 갱신 확인", + "action": "verify", + "target": "text (전체 항목 수)", + "expected": "전체 항목 수가 1개 감소됨", + "validation": ["삭제 기능"] + } + ] +} diff --git a/position-management.json b/position-management.json new file mode 100644 index 0000000..640b10b --- /dev/null +++ b/position-management.json @@ -0,0 +1,303 @@ +{ + "id": "position-management", + "name": "직책관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "설정 > 직책관리 페이지의 직책 등록/조회/수정/삭제 기능을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/settings/position", + "menuNavigation": { + "level1": "설정", + "level2": "직책관리", + "expectedUrl": "/settings/position" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebar": { + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 5, + "waitAfterScroll": 300 + }, + "level1": { + "text": "설정", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "직책관리", + "waitAfterClick": 300 + }, + "fallbackUrl": "/settings/position", + "expectedUrl": "/settings/position" + }, + "timeout": 90000, + "tags": ["settings", "position", "crud"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "testData": { + "position": { + "name": "E2E테스트직책" + } + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 500 } + ] + }, + { + "id": "step-1", + "name": "설정 메뉴 진입", + "description": "설정 > 직책관리 메뉴로 이동", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "설정", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "설정" }, + { "type": "wait", "duration": 500 }, + { "type": "click", "target": "직책관리" } + ], + "expect": { + "url": "/settings/position", + "visible": ["직책관리", "추가"] + }, + "fallback": { + "type": "navigate", + "url": "/settings/position" + } + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "직책 목록 구조 확인", + "verify": { + "visible": ["직책관리", "추가", "드래그"], + "listExists": true + } + }, + { + "id": "step-3", + "name": "필수 검증 #2: 직책 추가 모달 열기", + "description": "추가 버튼 클릭하여 모달 열기", + "actions": [ + { "type": "openModal", "target": "추가", "description": "직책 추가 모달 열기" } + ], + "modalConfig": { + "containerSelector": "[role='dialog'], .modal", + "animationDelay": 300, + "waitForSelector": "[role='dialog']" + }, + "expect": { + "modal": "직책 추가", + "visible": ["직책명"] + } + }, + { + "id": "step-4", + "name": "직책 추가 폼 입력", + "description": "직책 정보 입력 (모달 내부)", + "actions": [ + { + "type": "fillInModal", + "target": "직책명", + "value": "{testData.position.name}", + "description": "모달 내 직책명 필드에 값 입력", + "options": { "waitAfter": 200, "retries": 2 } + } + ] + }, + { + "id": "step-5", + "name": "필수 검증 #2: 직책 추가 저장", + "description": "모달 내 저장 버튼 클릭하여 직책 저장", + "actions": [ + { + "type": "clickInModal", + "target": "저장", + "description": "모달 내 저장 버튼 클릭", + "options": { "waitAfter": 500 } + } + ], + "expect": { + "urlMaintained": true, + "noErrorPage": true, + "toast": ["등록", "추가", "완료", "성공"], + "modalClosed": true + }, + "verify": { + "apiCall": "POST /api/settings/position" + } + }, + { + "id": "step-6", + "name": "필수 검증 #4: 등록 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 등록 확인 필수!", + "description": "목록에서 등록된 직책 확인", + "verify": { + "listContains": ["{testData.position.name}"], + "recordCountIncreased": true + } + }, + { + "id": "step-7", + "name": "직책 수정 버튼 클릭", + "description": "등록된 직책의 수정 버튼 클릭", + "actions": [ + { + "type": "findItem", + "contains": "{testData.position.name}", + "then": { + "type": "click", + "target": "수정" + } + } + ], + "expect": { + "modal": "직책 수정", + "visible": ["직책명"] + } + }, + { + "id": "step-8", + "name": "직책 정보 수정", + "description": "직책 정보 수정 테스트", + "actions": [ + { "type": "clear", "target": "직책명" }, + { "type": "fill", "target": "직책명", "value": "E2E테스트직책수정" }, + { "type": "click", "target": "저장" } + ], + "expect": { + "toast": ["수정", "완료", "성공"], + "modalClosed": true + } + }, + { + "id": "step-9", + "name": "필수 검증 #4: 수정 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", + "description": "목록에서 수정된 직책 확인", + "verify": { + "listContains": "E2E테스트직책수정" + } + }, + { + "id": "step-10", + "name": "직책 삭제 버튼 클릭", + "description": "수정된 직책의 삭제 버튼 클릭", + "actions": [ + { + "type": "findItem", + "contains": "E2E테스트직책수정", + "then": { + "type": "click", + "target": "삭제" + } + } + ], + "expect": { + "confirmDialog": true, + "dialogText": ["삭제", "하시겠습니까"] + } + }, + { + "id": "step-11", + "name": "삭제 확인", + "description": "삭제 확인 다이얼로그에서 확인 클릭", + "actions": [ + { "type": "click", "target": "확인" } + ], + "expect": { + "toast": ["삭제", "완료", "성공"] + } + }, + { + "id": "step-12", + "name": "필수 검증 #4: 삭제 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 삭제 확인 필수!", + "description": "목록에서 삭제된 직책이 없는지 확인", + "verify": { + "listNotContains": "E2E테스트직책", + "recordCountDecreased": true + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/settings/position", + "message": "직책관리 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "button:has-text('추가')", + "message": "추가 버튼이 표시되어야 함" + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 2, + "name": "등록/저장 버튼", + "trigger": "추가 버튼", + "verification": "URL 유지 + 에러 페이지 없음 + 성공 토스트 + 데이터 반영", + "failCondition": "404/500 에러 페이지 이동" + }, + { + "id": 4, + "name": "모달 등록 완료", + "trigger": "직책 추가 모달", + "verification": "실제 저장 동작 + 결과 확인", + "failCondition": "열기/닫기만 테스트" + } + ] + }, + + "cleanup": { + "enabled": true, + "description": "테스트 중 생성된 직책 데이터 삭제", + "actions": [ + { + "type": "deleteTestData", + "condition": "contains:E2E테스트" + } + ] + }, + + "notes": { + "testScope": "직책 추가 → 조회 → 수정 → 삭제 전체 CRUD 테스트", + "pageFeatures": { + "dragAndDrop": "직책 순서 변경 가능", + "inlineButtons": "각 직책 항목에 수정/삭제 버튼" + }, + "prerequisites": "로그인된 사용자에게 직책 관리 권한 필요" + } +} diff --git a/price-management.json b/price-management.json new file mode 100644 index 0000000..0064040 --- /dev/null +++ b/price-management.json @@ -0,0 +1,323 @@ +{ + "id": "price-management", + "name": "단가관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "판매관리 > 단가관리 페이지의 품목별 단가 조회/등록/수정 기능을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/sales/price", + "menuNavigation": { + "level1": "판매관리", + "level2": "단가관리", + "expectedUrl": "/sales/price" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebar": { + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 5, + "waitAfterScroll": 300 + }, + "level1": { + "text": "판매관리", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "단가관리", + "waitAfterClick": 300 + }, + "fallbackUrl": "/sales/price", + "expectedUrl": "/sales/price" + }, + "timeout": 90000, + "tags": ["sales", "price", "crud"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "testData": { + "price": { + "purchasePrice": "10000", + "processingCost": "2000", + "sellingPrice": "15000", + "marginRate": "50" + } + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 500 } + ] + }, + { + "id": "step-1", + "name": "판매관리 메뉴 진입", + "description": "판매관리 > 단가관리 메뉴로 이동", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "판매관리", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "판매관리" }, + { "type": "wait", "duration": 500 }, + { "type": "click", "target": "단가관리" } + ], + "expect": { + "url": "/sales/price", + "visible": ["단가 목록", "품목 마스터 동기화"] + }, + "fallback": { + "type": "navigate", + "url": "/sales/price" + } + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "통계 카드와 테이블 구조 확인", + "verify": { + "visible": ["전체 품목", "단가 등록", "미등록", "확정"], + "tableColumns": ["번호", "품목유형", "품목코드", "품목명", "규격", "단위", "매입단가", "가공비", "판매단가", "마진율", "적용일", "상태"] + } + }, + { + "id": "step-3", + "name": "필수 검증 #3: 품목유형 탭 필터 - 제품", + "description": "제품 탭 클릭하여 필터링 확인", + "actions": [ + { "type": "click", "target": "제품", "role": "tab" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "tabActive": "제품", + "dataFiltered": true + } + }, + { + "id": "step-4", + "name": "필수 검증 #3: 품목유형 탭 필터 - 소모품", + "description": "소모품 탭 클릭하여 필터링 확인", + "actions": [ + { "type": "click", "target": "소모품", "role": "tab" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "tabActive": "소모품", + "dataFiltered": true + } + }, + { + "id": "step-5", + "name": "전체 탭으로 복귀", + "description": "전체 탭 클릭하여 모든 품목 표시", + "actions": [ + { "type": "click", "target": "전체", "role": "tab" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "tabActive": "전체", + "allDataShown": true + } + }, + { + "id": "step-6", + "name": "미등록 품목 선택", + "description": "단가가 미등록된 품목 클릭", + "actions": [ + { + "type": "findRow", + "contains": "미등록", + "then": { + "type": "click", + "target": "row" + } + } + ], + "expect": { + "pageOrModal": "단가 등록", + "visible": ["매입단가", "가공비", "판매단가", "마진율"] + } + }, + { + "id": "step-7", + "name": "필수 검증 #2: 단가 등록 폼 입력", + "description": "단가 정보 입력", + "actions": [ + { "type": "fill", "target": "매입단가", "value": "{testData.price.purchasePrice}" }, + { "type": "fill", "target": "가공비", "value": "{testData.price.processingCost}" }, + { "type": "fill", "target": "판매단가", "value": "{testData.price.sellingPrice}" } + ] + }, + { + "id": "step-8", + "name": "필수 검증 #2: 단가 등록 저장", + "description": "저장 버튼 클릭하여 단가 저장", + "actions": [ + { "type": "click", "target": "저장" } + ], + "expect": { + "urlMaintained": true, + "noErrorPage": true, + "toast": ["등록", "저장", "완료", "성공"] + }, + "verify": { + "apiCall": "POST /api/sales/price" + } + }, + { + "id": "step-9", + "name": "필수 검증 #4: 등록 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 등록 확인 필수!", + "description": "테이블에서 등록된 단가 확인", + "verify": { + "tableContains": ["{testData.price.sellingPrice}"], + "statusChanged": "초안" + } + }, + { + "id": "step-10", + "name": "등록된 단가 품목 선택", + "description": "단가가 등록된 품목 클릭하여 수정", + "actions": [ + { + "type": "findRow", + "contains": "초안", + "then": { + "type": "click", + "target": "row" + } + } + ], + "expect": { + "pageOrModal": "단가 상세", + "visible": ["수정", "확정"] + } + }, + { + "id": "step-11", + "name": "단가 정보 수정", + "description": "단가 정보 수정 테스트", + "actions": [ + { "type": "click", "target": "수정" }, + { "type": "wait", "duration": 300 }, + { "type": "clear", "target": "판매단가" }, + { "type": "fill", "target": "판매단가", "value": "20000" }, + { "type": "click", "target": "저장" } + ], + "expect": { + "toast": ["수정", "저장", "완료", "성공"], + "noErrorPage": true + } + }, + { + "id": "step-12", + "name": "필수 검증 #4: 수정 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", + "description": "테이블에서 수정된 단가 확인", + "verify": { + "tableContains": "20,000원" + } + }, + { + "id": "step-13", + "name": "품목 마스터 동기화 버튼 테스트", + "description": "품목 마스터 동기화 버튼 동작 확인", + "actions": [ + { "type": "click", "target": "품목 마스터 동기화" }, + { "type": "wait", "duration": 1000 } + ], + "expect": { + "toast": ["동기화", "완료", "성공"], + "noErrorPage": true + } + }, + { + "id": "step-14", + "name": "페이지네이션 확인", + "description": "페이지네이션 동작 확인", + "actions": [ + { "type": "click", "target": "다음" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "pageChanged": true + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/sales/price", + "message": "단가관리 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "button:has-text('품목 마스터 동기화')", + "message": "품목 마스터 동기화 버튼이 표시되어야 함" + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 2, + "name": "등록/저장 버튼", + "trigger": "단가 등록/수정", + "verification": "URL 유지 + 에러 페이지 없음 + 성공 토스트 + 데이터 반영", + "failCondition": "404/500 에러 페이지 이동" + }, + { + "id": 3, + "name": "검색/필터", + "trigger": "품목유형 탭 필터", + "verification": "데이터 변화 확인", + "failCondition": "필터 적용 후 데이터 무변화" + }, + { + "id": 4, + "name": "데이터 반영 확인", + "trigger": "단가 등록/수정 완료 후", + "verification": "실제 데이터 등록/수정 확인", + "failCondition": "토스트만 확인하고 데이터 미확인" + } + ] + }, + + "notes": { + "testScope": "단가 조회 → 등록 → 수정 테스트", + "pageFeatures": { + "statsCards": ["전체 품목", "단가 등록", "미등록", "확정"], + "typeTabs": ["전체", "제품", "부품", "부자재", "원자재", "소모품"], + "viewModes": ["카드 뷰", "테이블 뷰"], + "syncButton": "품목 마스터 동기화" + }, + "tableColumns": ["번호", "품목유형", "품목코드", "품목명", "규격", "단위", "매입단가", "가공비", "판매단가", "마진율", "적용일", "상태"], + "priceWorkflow": "미등록 → 초안 → 확정", + "prerequisites": "로그인된 사용자에게 단가 관리 권한 필요" + } +} diff --git a/process-management.json b/process-management.json new file mode 100644 index 0000000..9242644 --- /dev/null +++ b/process-management.json @@ -0,0 +1,355 @@ +{ + "id": "process-management", + "name": "공정관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "기준정보 관리 > 공정관리 페이지의 공정 등록/조회/수정/삭제 기능을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/master-data/process", + "menuNavigation": { + "level1": "기준정보 관리", + "level2": "공정관리", + "expectedUrl": "/master-data/process" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebar": { + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 5, + "waitAfterScroll": 300 + }, + "level1": { + "text": "기준정보 관리", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "공정관리", + "waitAfterClick": 300 + }, + "fallbackUrl": "/master-data/process", + "expectedUrl": "/master-data/process" + }, + "timeout": 90000, + "tags": ["master-data", "process", "crud"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "testData": { + "process": { + "code": "P-E2E-001", + "name": "E2E 테스트 공정", + "type": "생산", + "department": "개발팀", + "workers": "2" + } + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 500 } + ], + "expect": { + "sidebarReady": true + } + }, + { + "id": "step-1", + "name": "기준정보 관리 메뉴 진입", + "description": "기준정보 관리 > 공정관리 메뉴로 이동", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "기준정보 관리", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "기준정보 관리" }, + { "type": "wait", "duration": 500 }, + { "type": "click", "target": "공정관리" } + ], + "expect": { + "url": "/master-data/process", + "visible": ["공정 목록", "공정 등록"] + }, + "fallback": { + "type": "navigate", + "url": "/master-data/process" + } + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "통계 카드와 테이블 구조 확인", + "verify": { + "visible": ["전체", "사용중", "미사용"], + "tableColumns": ["번호", "공정코드", "공정명", "구분", "담당부서", "분류규칙", "인원", "상태"] + } + }, + { + "id": "step-3", + "name": "탭 기능 확인", + "description": "전체/사용중/미사용 탭 동작 확인", + "actions": [ + { "type": "click", "target": "사용중", "role": "tab" }, + { "type": "wait", "duration": 300 }, + { "type": "click", "target": "미사용", "role": "tab" }, + { "type": "wait", "duration": 300 }, + { "type": "click", "target": "전체", "role": "tab" } + ], + "expect": { + "tabsWork": true + } + }, + { + "id": "step-4", + "name": "필수 검증 #2: 공정 등록 모달 열기", + "description": "공정 등록 버튼 클릭하여 모달 열기", + "actions": [ + { "type": "openModal", "target": "공정 등록", "description": "공정 등록 모달 열기" } + ], + "modalConfig": { + "containerSelector": "[role='dialog'], .modal", + "animationDelay": 300, + "waitForSelector": "[role='dialog']" + }, + "expect": { + "modal": "공정 등록", + "visible": ["공정코드", "공정명", "구분", "담당부서", "인원"] + } + }, + { + "id": "step-5", + "name": "공정 등록 폼 입력", + "description": "모달 내 공정 정보 입력", + "actions": [ + { "type": "fillInModal", "target": "공정코드", "value": "{testData.process.code}", "options": { "waitAfter": 100 } }, + { "type": "fillInModal", "target": "공정명", "value": "{testData.process.name}", "options": { "waitAfter": 100 } }, + { "type": "selectInModal", "target": "구분", "value": "{testData.process.type}", "options": { "waitAfter": 200 } }, + { "type": "selectInModal", "target": "담당부서", "value": "{testData.process.department}", "options": { "waitAfter": 200 } }, + { "type": "fillInModal", "target": "인원", "value": "{testData.process.workers}", "options": { "waitAfter": 100 } } + ] + }, + { + "id": "step-6", + "name": "필수 검증 #2: 공정 등록 저장", + "description": "모달 내 등록 버튼 클릭하여 공정 저장", + "actions": [ + { "type": "clickInModal", "target": "등록", "options": { "waitAfter": 500 } } + ], + "expect": { + "urlMaintained": true, + "noErrorPage": true, + "toast": ["등록", "완료", "성공"], + "modalClosed": true + }, + "verify": { + "apiCall": "POST /api/master-data/process" + } + }, + { + "id": "step-7", + "name": "필수 검증 #4: 등록 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 등록 확인 필수!", + "description": "테이블에서 등록된 공정 확인", + "verify": { + "tableContains": ["{testData.process.code}", "{testData.process.name}"], + "recordCountIncreased": true + } + }, + { + "id": "step-8", + "name": "등록된 공정 상세 열기", + "description": "등록된 공정 항목 클릭하여 상세 보기", + "actions": [ + { + "type": "findRow", + "contains": "{testData.process.name}", + "then": { + "type": "click", + "target": "row" + } + } + ], + "expect": { + "modal": "공정 상세", + "visible": ["수정", "삭제"] + } + }, + { + "id": "step-9", + "name": "공정 정보 수정", + "description": "모달 내 공정 정보 수정 테스트", + "actions": [ + { "type": "clickInModal", "target": "수정", "options": { "waitAfter": 300 } }, + { "type": "fillInModal", "target": "공정명", "value": "E2E 테스트 공정 수정", "options": { "waitAfter": 100 } }, + { "type": "clickInModal", "target": "저장", "options": { "waitAfter": 500 } } + ], + "expect": { + "toast": ["수정", "완료", "성공"], + "modalClosed": true + } + }, + { + "id": "step-10", + "name": "필수 검증 #4: 수정 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", + "description": "테이블에서 수정된 공정 확인", + "verify": { + "tableContains": "E2E 테스트 공정 수정" + } + }, + { + "id": "step-11", + "name": "공정 삭제 준비", + "description": "삭제할 공정 선택", + "actions": [ + { + "type": "findRow", + "contains": "E2E 테스트 공정 수정", + "then": { + "type": "click", + "target": "row" + } + } + ], + "expect": { + "modal": "공정 상세", + "visible": ["삭제"] + } + }, + { + "id": "step-12", + "name": "공정 삭제", + "description": "삭제 버튼 클릭하여 공정 삭제", + "actions": [ + { "type": "click", "target": "삭제" } + ], + "expect": { + "confirmDialog": true, + "dialogText": ["삭제", "하시겠습니까"] + } + }, + { + "id": "step-13", + "name": "삭제 확인", + "description": "삭제 확인 다이얼로그에서 확인 클릭", + "actions": [ + { "type": "click", "target": "확인" } + ], + "expect": { + "toast": ["삭제", "완료", "성공"], + "modalClosed": true + } + }, + { + "id": "step-14", + "name": "필수 검증 #4: 삭제 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 삭제 확인 필수!", + "description": "테이블에서 삭제된 공정이 없는지 확인", + "verify": { + "tableNotContains": "E2E 테스트 공정", + "recordCountDecreased": true + } + }, + { + "id": "step-15", + "name": "날짜 필터 확인", + "description": "날짜 필터 버튼 동작 확인", + "actions": [ + { "type": "click", "target": "당월" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "filterApplied": true, + "dataFiltered": true + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/master-data/process", + "message": "공정관리 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "button:has-text('공정 등록')", + "message": "공정 등록 버튼이 표시되어야 함" + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 2, + "name": "등록/저장 버튼", + "trigger": "공정 등록 버튼", + "verification": "URL 유지 + 에러 페이지 없음 + 성공 토스트 + 데이터 반영", + "failCondition": "404/500 에러 페이지 이동" + }, + { + "id": 4, + "name": "모달 등록 완료", + "trigger": "공정 등록 모달", + "verification": "실제 저장 동작 + 결과 확인", + "failCondition": "열기/닫기만 테스트" + }, + { + "id": 5, + "name": "목업/미완성 페이지 감지", + "trigger": "페이지 로드 시", + "verification": "입력 필드 + 동작하는 버튼 + API 호출 확인", + "failCondition": "버튼만 있고 입력 불가, Console LOG만 출력" + } + ] + }, + + "cleanup": { + "enabled": true, + "description": "테스트 중 생성된 공정 데이터 삭제", + "actions": [ + { + "type": "deleteTestData", + "condition": "contains:E2E 테스트" + } + ] + }, + + "notes": { + "testScope": "공정 등록 → 조회 → 수정 → 삭제 전체 CRUD 테스트", + "modalFields": { + "공정코드": "고유 코드", + "공정명": "공정 이름", + "구분": "생산/검사 등", + "담당부서": "담당 부서 선택", + "인원": "작업 인원 수" + }, + "tableColumns": ["번호", "공정코드", "공정명", "구분", "담당부서", "분류규칙", "인원", "상태"], + "prerequisites": "로그인된 사용자에게 공정 관리 권한 필요" + } +} diff --git a/production-dashboard.json b/production-dashboard.json new file mode 100644 index 0000000..1eb2a6e --- /dev/null +++ b/production-dashboard.json @@ -0,0 +1,715 @@ +{ + "scenarioId": "production-dashboard", + "scenarioName": "생산 현황판 (Production Dashboard)", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "생산관리 - 생산 현황판 메뉴의 전체 기능 테스트: 통계 카드 확인, 공장별 탭 필터, 긴급/지연 작업 표시, 작업지시 상세보기, 작업자 화면 및 작업지시 목록 이동", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/production/dashboard", + "menuNavigation": { + "level1": "생산관리", + "level2": "생산 현황판", + "expectedUrl": "/production/dashboard" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebarSelector": ".sidebar-scroll, [class*='sidebar'], nav[role='navigation']", + "scrollConfig": { + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + }, + "level1": { + "text": "생산관리", + "fallbackSelectors": [ + "span:has-text('생산관리')", + "[data-menu='production']", + "a[href*='production']" + ] + }, + "level2": { + "text": "생산 현황판", + "fallbackSelectors": [ + "span:has-text('생산 현황판')", + "[data-submenu='dashboard']", + "a[href*='dashboard']" + ] + }, + "scrollAndFind": { + "enabled": true, + "scrollToTopFirst": true, + "searchWhileScrolling": true + } + }, + "testData": { + "existingWorkOrder": { + "작업지시번호": "WO202601150001", + "작업상태": "대기", + "발주처": "코브라브릿지", + "현장명": "테스트현장2", + "지연일수": "+2일 지연" + } + }, + "steps": [ + { + "step": 0, + "name": "사이드바 초기화", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll, [class*=\"sidebar\"], nav[role=\"navigation\"]')?.scrollTo({top: 0, behavior: 'instant'})" + }, + { "type": "wait", "duration": 500 } + ], + "expected": { + "sidebarReady": true + } + }, + { + "step": 1, + "name": "2단계 메뉴 진입: 생산관리 > 생산 현황판", + "description": "생산관리 > 생산 현황판 메뉴로 이동하여 페이지 로드 확인 (scrollAndFind 패턴 사용)", + "menuNavigation": { + "useEnhanced": true, + "pattern": "scrollAndFind" + }, + "actions": [ + { + "type": "scrollAndFind", + "target": "생산관리", + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxAttempts": 10 + }, + { "type": "click", "target": "생산관리" }, + { "type": "wait", "duration": 500 }, + { + "type": "scrollAndFind", + "target": "생산 현황판", + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxAttempts": 10 + }, + { "type": "click", "target": "생산 현황판" }, + { "type": "wait", "target": "페이지 로드 완료" } + ], + "expected": { + "url": "/ko/production/dashboard", + "title": "생산 현황판", + "authenticated": true + } + }, + { + "step": 2, + "name": "페이지 로딩 대기", + "action": "wait", + "duration": 3, + "expected": "데이터 로딩 완료" + }, + { + "step": 3, + "name": "페이지 제목 확인", + "action": "verifyText", + "selector": "h1", + "expected": "생산 현황판" + }, + { + "step": 4, + "name": "페이지 설명 확인", + "action": "verifyText", + "expected": "공장별 작업 현황을 확인합니다." + }, + { + "step": 5, + "name": "작업자 화면 버튼 존재 확인", + "action": "verifyElement", + "selector": "button", + "text": "작업자 화면", + "expected": "버튼 표시됨" + }, + { + "step": 6, + "name": "작업지시 목록 버튼 존재 확인", + "action": "verifyElement", + "selector": "button", + "text": "작업지시 목록", + "expected": "버튼 표시됨" + }, + { + "step": 7, + "name": "공장 탭 필터 확인 - 전체 탭", + "action": "verifyElement", + "selector": "tab", + "text": "전체", + "expected": "전체 탭 표시 및 선택됨" + }, + { + "step": 8, + "name": "공장 탭 필터 확인 - 스크린 탭", + "action": "verifyElement", + "selector": "tab", + "text": "스크린", + "expected": "스크린 탭 표시됨" + }, + { + "step": 9, + "name": "통계 카드 6개 확인", + "action": "verifyCount", + "selector": "통계 카드", + "expected": "6개의 통계 카드 표시 (전체 작업, 작업 대기, 작업중, 작업 완료, 긴급, 지연)" + }, + { + "step": 10, + "name": "전체 작업 통계 카드 확인", + "action": "verifyElement", + "text": "전체 작업", + "expected": "전체 작업 카드 표시 및 숫자 확인" + }, + { + "step": 11, + "name": "작업 대기 통계 카드 확인", + "action": "verifyElement", + "text": "작업 대기", + "expected": "작업 대기 카드 표시 및 숫자 확인" + }, + { + "step": 12, + "name": "작업중 통계 카드 확인", + "action": "verifyElement", + "text": "작업중", + "expected": "작업중 카드 표시 및 숫자 확인" + }, + { + "step": 13, + "name": "작업 완료 통계 카드 확인", + "action": "verifyElement", + "text": "작업 완료", + "expected": "작업 완료 카드 표시 및 숫자 확인" + }, + { + "step": 14, + "name": "긴급 통계 카드 확인", + "action": "verifyElement", + "text": "긴급", + "expected": "긴급 카드 표시 및 숫자 확인" + }, + { + "step": 15, + "name": "지연 통계 카드 확인", + "action": "verifyElement", + "text": "지연", + "expected": "지연 카드 표시 및 숫자 확인 (1개)" + }, + { + "step": 16, + "name": "긴급 작업 섹션 제목 확인", + "action": "verifyElement", + "selector": "h4", + "text": "긴급 작업", + "expected": "긴급 작업 섹션 표시" + }, + { + "step": 17, + "name": "긴급 작업 개수 확인", + "action": "verifyText", + "expected": "긴급 작업 0 또는 긴급 작업이 없습니다." + }, + { + "step": 18, + "name": "지연 작업 섹션 제목 확인", + "action": "verifyElement", + "selector": "h4", + "text": "지연 작업", + "expected": "지연 작업 섹션 표시" + }, + { + "step": 19, + "name": "지연 작업 개수 확인", + "action": "verifyText", + "expected": "지연 작업 1" + }, + { + "step": 20, + "name": "지연 작업 카드 존재 확인", + "action": "verifyElement", + "text": "WO202601150001", + "expected": "작업지시번호가 표시된 지연 작업 카드 확인" + }, + { + "step": 21, + "name": "지연 작업 카드 - 작업상태 확인", + "action": "verifyElement", + "text": "대기", + "expected": "작업상태 표시" + }, + { + "step": 22, + "name": "지연 작업 카드 - 발주처 확인", + "action": "verifyElement", + "text": "코브라브릿지", + "expected": "발주처 표시" + }, + { + "step": 23, + "name": "지연 작업 카드 - 지연일수 확인", + "action": "verifyElement", + "text": "+2일 지연", + "expected": "지연일수 표시" + }, + { + "step": 24, + "name": "작업자별 현황 섹션 제목 확인", + "action": "verifyElement", + "selector": "h4", + "text": "작업자별 현황", + "expected": "작업자별 현황 섹션 표시" + }, + { + "step": 25, + "name": "스크린 탭 클릭", + "action": "click", + "selector": "tab", + "text": "스크린", + "expected": "스크린 공장 필터 적용" + }, + { + "step": 26, + "name": "스크린 탭 선택 확인", + "action": "verifyAttribute", + "selector": "tab[text='스크린']", + "attribute": "selected", + "expected": "스크린 탭이 선택됨" + }, + { + "step": 27, + "name": "스크린 공장 - 통계 변경 확인", + "action": "verifyStatChange", + "expected": "통계 카드 숫자가 스크린 공장 데이터로 변경 (전체 0개)" + }, + { + "step": 28, + "name": "스크린 공장 - 지연 작업 확인", + "action": "verifyText", + "expected": "지연 작업이 없습니다." + }, + { + "step": 29, + "name": "전체 탭으로 복귀", + "action": "click", + "selector": "tab", + "text": "전체", + "expected": "전체 필터로 복귀" + }, + { + "step": 30, + "name": "전체 탭 선택 확인", + "action": "verifyAttribute", + "selector": "tab[text='전체']", + "attribute": "selected", + "expected": "전체 탭이 선택됨" + }, + { + "step": 31, + "name": "전체 필터 - 통계 복귀 확인", + "action": "verifyText", + "expected": "전체 작업 1개 복귀" + }, + { + "step": 32, + "name": "전체 필터 - 지연 작업 복귀 확인", + "action": "verifyText", + "expected": "지연 작업 1개 복귀" + }, + { + "step": 33, + "name": "지연 작업 카드 클릭", + "action": "click", + "text": "WO202601150001", + "expected": "작업지시 상세 페이지로 이동" + }, + { + "step": 34, + "name": "URL 변경 확인 (필수 검증 #2)", + "action": "verifyUrl", + "expected": "URL이 /production/work-orders/1로 변경 (404 에러 페이지 아님)", + "validation": { + "notContains": ["404", "not-found", "error"] + } + }, + { + "step": 35, + "name": "작업지시 상세 페이지 제목 확인", + "action": "verifyText", + "selector": "h1", + "expected": "작업지시 상세" + }, + { + "step": 36, + "name": "수정 버튼 존재 확인", + "action": "verifyElement", + "selector": "button", + "text": "수정", + "expected": "수정 버튼 표시" + }, + { + "step": 37, + "name": "작업일지 버튼 존재 확인", + "action": "verifyElement", + "selector": "button", + "text": "작업일지", + "expected": "작업일지 버튼 표시" + }, + { + "step": 38, + "name": "목록 버튼 존재 확인", + "action": "verifyElement", + "selector": "button", + "text": "목록", + "expected": "목록 버튼 표시" + }, + { + "step": 39, + "name": "기본 정보 섹션 확인", + "action": "verifyElement", + "selector": "h3", + "text": "기본 정보", + "expected": "기본 정보 섹션 표시" + }, + { + "step": 40, + "name": "작업지시번호 확인", + "action": "verifyData", + "field": "작업지시번호", + "expected": "WO202601150001" + }, + { + "step": 41, + "name": "로트번호 확인", + "action": "verifyData", + "field": "로트번호", + "expected": "ORD202601150001" + }, + { + "step": 42, + "name": "작업상태 확인", + "action": "verifyData", + "field": "작업상태", + "expected": "승인대기" + }, + { + "step": 43, + "name": "발주처 확인", + "action": "verifyData", + "field": "발주처", + "expected": "코브라브릿지" + }, + { + "step": 44, + "name": "현장명 확인", + "action": "verifyData", + "field": "현장명", + "expected": "테스트현장2" + }, + { + "step": 45, + "name": "납기일 확인", + "action": "verifyData", + "field": "납기일", + "expected": "2026-01-14" + }, + { + "step": 46, + "name": "우선순위 확인", + "action": "verifyData", + "field": "우선순위", + "expected": "5 (일반)" + }, + { + "step": 47, + "name": "공정 진행 섹션 확인", + "action": "verifyElement", + "selector": "h3", + "text": "공정 진행 (5단계)", + "expected": "공정 진행 섹션 표시" + }, + { + "step": 48, + "name": "공정 단계 1 - 원단절단 확인", + "action": "verifyElement", + "text": "원단절단", + "expected": "원단절단 공정 표시" + }, + { + "step": 49, + "name": "공정 단계 2 - 미싱 확인", + "action": "verifyElement", + "text": "미싱", + "expected": "미싱 공정 표시" + }, + { + "step": 50, + "name": "공정 단계 3 - 앤드락작업 확인", + "action": "verifyElement", + "text": "앤드락작업", + "expected": "앤드락작업 공정 표시" + }, + { + "step": 51, + "name": "공정 단계 4 - 중간검사 확인", + "action": "verifyElement", + "text": "중간검사", + "expected": "중간검사 공정 표시" + }, + { + "step": 52, + "name": "공정 단계 5 - 포장 확인", + "action": "verifyElement", + "text": "포장", + "expected": "포장 공정 표시" + }, + { + "step": 53, + "name": "작업 품목 섹션 확인", + "action": "verifyElement", + "selector": "h3", + "text": "작업 품목 (0건)", + "expected": "작업 품목 섹션 표시" + }, + { + "step": 54, + "name": "작업 품목 없음 메시지 확인", + "action": "verifyText", + "expected": "등록된 품목이 없습니다." + }, + { + "step": 55, + "name": "목록 버튼 클릭", + "action": "click", + "selector": "button", + "text": "목록", + "expected": "작업지시 목록 페이지로 이동" + }, + { + "step": 56, + "name": "URL 변경 확인 - 작업지시 목록", + "action": "verifyUrl", + "expected": "URL이 /production/work-orders로 변경", + "validation": { + "notContains": ["404", "not-found", "error"] + } + }, + { + "step": 57, + "name": "작업지시 목록 페이지 제목 확인", + "action": "verifyText", + "selector": "h1", + "expected": "작업지시 목록" + }, + { + "step": 58, + "name": "작업지시 목록 - 등록 버튼 확인", + "action": "verifyElement", + "selector": "button", + "text": "등록", + "expected": "등록 버튼 표시" + }, + { + "step": 59, + "name": "작업지시 목록 - 통계 카드 확인", + "action": "verifyCount", + "expected": "4개의 통계 카드 (전체, 작업대기, 작업중, 작업완료)" + }, + { + "step": 60, + "name": "작업지시 목록 - 검색창 확인", + "action": "verifyElement", + "selector": "textbox", + "placeholder": "작업지시번호, 발주처, 현장명 검색...", + "expected": "검색창 표시" + }, + { + "step": 61, + "name": "작업지시 목록 - 상태 탭 확인", + "action": "verifyElement", + "selector": "button", + "text": "전체", + "expected": "상태 필터 탭 표시" + }, + { + "step": 62, + "name": "작업지시 목록 - 테이블 확인", + "action": "verifyElement", + "selector": "table", + "expected": "작업지시 테이블 표시" + }, + { + "step": 63, + "name": "작업지시 목록 - 테이블 헤더 확인", + "action": "verifyTableHeaders", + "expected": "번호, 작업지시번호, 공정, 로트번호, 지시일, 배정, 작업, 시작, 작업상태, 현장순위, 작업자, 현장명, 출고예정일" + }, + { + "step": 64, + "name": "작업지시 목록 - 데이터 행 확인", + "action": "verifyElement", + "text": "WO202601150001", + "expected": "테스트 작업지시 데이터 표시" + }, + { + "step": 65, + "name": "생산 현황판으로 복귀", + "action": "navigate", + "url": "/production/dashboard", + "expected": "생산 현황판 페이지로 이동" + }, + { + "step": 66, + "name": "페이지 로딩 대기", + "action": "wait", + "duration": 3, + "expected": "데이터 로딩 완료" + }, + { + "step": 67, + "name": "작업자 화면 버튼 클릭", + "action": "click", + "selector": "button", + "text": "작업자 화면", + "expected": "작업자 화면 페이지로 이동" + }, + { + "step": 68, + "name": "URL 변경 확인 - 작업자 화면", + "action": "verifyUrl", + "expected": "URL이 작업자 화면 URL로 변경 (404 에러 페이지 아님)", + "validation": { + "contains": ["work", "worker", "operator"], + "notContains": ["404", "not-found", "error"] + } + }, + { + "step": 69, + "name": "작업자 화면 페이지 확인", + "action": "verifyPage", + "expected": "작업자 화면 페이지 정상 로딩" + }, + { + "step": 70, + "name": "생산 현황판으로 재복귀", + "action": "navigate", + "url": "/production/dashboard", + "expected": "생산 현황판 페이지로 이동" + }, + { + "step": 71, + "name": "페이지 로딩 대기", + "action": "wait", + "duration": 3, + "expected": "데이터 로딩 완료" + }, + { + "step": 72, + "name": "작업지시 목록 버튼 클릭", + "action": "click", + "selector": "button", + "text": "작업지시 목록", + "expected": "작업지시 목록 페이지로 이동" + }, + { + "step": 73, + "name": "URL 변경 확인 - 작업지시 목록", + "action": "verifyUrl", + "expected": "URL이 /production/work-orders로 변경", + "validation": { + "notContains": ["404", "not-found", "error"] + } + }, + { + "step": 74, + "name": "작업지시 목록 페이지 재확인", + "action": "verifyText", + "selector": "h1", + "expected": "작업지시 목록" + }, + { + "step": 75, + "name": "최종 검증 - 생산 현황판 복귀", + "action": "navigate", + "url": "/production/dashboard", + "expected": "생산 현황판 페이지로 최종 복귀" + }, + { + "step": 76, + "name": "페이지 로딩 대기", + "action": "wait", + "duration": 3, + "expected": "데이터 로딩 완료" + }, + { + "step": 77, + "name": "최종 검증 - 페이지 제목", + "action": "verifyText", + "selector": "h1", + "expected": "생산 현황판" + }, + { + "step": 78, + "name": "최종 검증 - 전체 탭 선택", + "action": "verifyAttribute", + "selector": "tab[text='전체']", + "attribute": "selected", + "expected": "전체 탭이 선택됨" + }, + { + "step": 79, + "name": "최종 검증 - 통계 카드", + "action": "verifyElement", + "text": "전체 작업", + "expected": "통계 카드 정상 표시" + }, + { + "step": 80, + "name": "최종 검증 - 지연 작업", + "action": "verifyElement", + "text": "지연 작업", + "expected": "지연 작업 섹션 정상 표시" + } + ], + "expectedAPIs": [ + { + "description": "생산 현황 데이터 조회", + "method": "GET", + "endpoint": "/api/production/dashboard", + "expectedStatus": 200 + }, + { + "description": "공장별 필터 데이터 조회", + "method": "GET", + "endpoint": "/api/production/dashboard?factory=screen", + "expectedStatus": 200 + }, + { + "description": "작업지시 상세 조회", + "method": "GET", + "endpoint": "/api/production/work-orders/1", + "expectedStatus": 200 + }, + { + "description": "작업지시 목록 조회", + "method": "GET", + "endpoint": "/api/production/work-orders", + "expectedStatus": 200 + } + ], + "notes": [ + "생산 현황판은 실시간 데이터를 표시하므로 통계 숫자는 변동될 수 있음", + "공장 탭은 현재 '전체'와 '스크린' 2개만 존재", + "지연 작업 카드 클릭 시 작업지시 상세 페이지로 이동해야 함", + "페이지 로딩 시 3초 대기 필요 (데이터 비동기 로딩)", + "작업자 화면 버튼은 별도 작업자 전용 화면으로 이동", + "작업지시 목록 버튼은 작업지시 관리 페이지로 이동" + ] +} diff --git a/quality-certification.json b/quality-certification.json new file mode 100644 index 0000000..fc5e01d --- /dev/null +++ b/quality-certification.json @@ -0,0 +1,298 @@ +{ + "id": "quality-certification", + "name": "품질인정심사 시스템 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "품질관리 > 품질인정심사 시스템 페이지의 점검표 조회/체크/필터 기능을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/quality/certification", + "menuNavigation": { + "level1": "품질관리", + "level2": "품질인정심사 시스템", + "expectedUrl": "/quality/certification" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebar": { + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 5, + "waitAfterScroll": 300 + }, + "level1": { + "text": "품질관리", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "품질인정심사 시스템", + "waitAfterClick": 300 + }, + "fallbackUrl": "/quality/certification", + "expectedUrl": "/quality/certification" + }, + "timeout": 90000, + "tags": ["quality", "certification", "checklist"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "testData": { + "filter": { + "year": "2025", + "quarter": "1분기" + } + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 500 } + ], + "expect": { + "sidebarReady": true + } + }, + { + "id": "step-1", + "name": "품질관리 메뉴 진입", + "description": "품질관리 > 품질인정심사 시스템 메뉴로 이동", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "품질관리", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "품질관리" }, + { "type": "wait", "duration": 500 }, + { "type": "click", "target": "품질인정심사 시스템" } + ], + "expect": { + "url": "/quality/certification", + "visible": ["품질인정심사 시스템", "점검표 항목"] + }, + "fallback": { + "type": "navigate", + "url": "/quality/certification" + } + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "통계 카드와 점검표 구조 확인", + "verify": { + "visible": ["1일차: 기준/매뉴얼", "2일차: 로트추적", "전체 심사"], + "checklistCategories": [ + "원재료 품질관리 기준", + "제조공정 관리 기준", + "제품 품질관리 기준", + "제조설비 관리", + "검사설비 관리", + "문서 및 인증 관리" + ] + } + }, + { + "id": "step-3", + "name": "필수 검증 #3: 년도 필터 기능", + "description": "년도 드롭다운에서 2025년 선택", + "actions": [ + { "type": "click", "target": "년도", "role": "combobox" }, + { "type": "click", "target": "2025", "role": "option" } + ], + "expect": { + "selected": "2025" + } + }, + { + "id": "step-4", + "name": "필수 검증 #3: 분기 필터 기능", + "description": "분기 선택으로 데이터 필터링", + "actions": [ + { "type": "click", "target": "1분기" } + ], + "expect": { + "filterApplied": true + } + }, + { + "id": "step-5", + "name": "필수 검증 #3: 조회 버튼 동작", + "description": "조회 버튼 클릭하여 데이터 로드", + "actions": [ + { "type": "click", "target": "조회" } + ], + "expect": { + "urlMaintained": true, + "noErrorPage": true, + "dataLoaded": true + }, + "verify": { + "apiCall": "GET /api/quality/certification" + } + }, + { + "id": "step-6", + "name": "점검표 항목 펼치기", + "description": "원재료 품질관리 기준 항목 펼쳐서 하위 항목 확인", + "actions": [ + { "type": "click", "target": "원재료 품질관리 기준" } + ], + "expect": { + "visible": ["수입검사 기준 확인", "불합격품 처리 기준 확인", "자재 보관 기준 확인"] + } + }, + { + "id": "step-7", + "name": "점검 항목 선택", + "description": "점검 항목 클릭하여 문서 미리보기 확인", + "actions": [ + { "type": "click", "target": "수입검사 기준 확인" } + ], + "expect": { + "documentPreview": true, + "visible": ["문서", "미리보기"] + } + }, + { + "id": "step-8", + "name": "필수 검증 #2: 체크 상태 변경", + "description": "미완료 항목을 완료로 체크", + "actions": [ + { "type": "click", "target": "자재 보관 기준 확인" }, + { "type": "wait", "duration": 300 }, + { "type": "click", "target": "완료", "description": "체크박스 또는 완료 버튼 클릭" } + ], + "expect": { + "toast": ["완료", "저장", "성공"], + "statusChanged": true + }, + "verify": { + "apiCall": "PUT /api/quality/certification/item" + } + }, + { + "id": "step-9", + "name": "필수 검증 #4: 체크 상태 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 상태 변경 확인 필수!", + "description": "점검 항목 상태가 '완료'로 변경되었는지 확인", + "verify": { + "itemStatus": { + "target": "자재 보관 기준 확인", + "expected": "완료" + }, + "progressUpdated": "3/3" + } + }, + { + "id": "step-10", + "name": "다른 카테고리 확인", + "description": "제조공정 관리 기준 펼쳐서 확인", + "actions": [ + { "type": "click", "target": "제조공정 관리 기준" } + ], + "expect": { + "visible": ["작업표준서 확인", "공정검사 기준 확인", "부적합품 처리 기준 확인"] + } + }, + { + "id": "step-11", + "name": "화면 설정 확인", + "description": "화면 설정 버튼이 있다면 동작 확인", + "actions": [ + { "type": "conditionalClick", "target": "화면 설정", "fallback": "skip" } + ], + "expect": { + "settingsPanel": true + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/quality/certification", + "message": "품질인정심사 시스템 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "text=점검표 항목", + "message": "점검표 항목이 표시되어야 함" + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 2, + "name": "등록/저장 버튼", + "trigger": "체크 상태 변경", + "verification": "URL 유지 + 에러 페이지 없음 + 상태 변경 반영", + "failCondition": "404/500 에러 페이지 이동" + }, + { + "id": 3, + "name": "검색/필터", + "trigger": "년도/분기 필터, 조회 버튼", + "verification": "데이터 변화 확인", + "failCondition": "필터 적용 후 데이터 무변화" + }, + { + "id": 5, + "name": "목업/미완성 페이지 감지", + "trigger": "페이지 로드 시", + "verification": "필터 동작 + 체크박스 동작 + API 호출 확인", + "failCondition": "버튼만 있고 동작 불가, Console LOG만 출력" + } + ] + }, + + "cleanup": { + "enabled": true, + "description": "테스트 중 변경한 체크 상태 원복", + "actions": [ + { + "type": "revertCheckStatus", + "target": "자재 보관 기준 확인" + } + ] + }, + + "notes": { + "testScope": "점검표 조회 → 필터 → 체크 상태 변경 → 문서 미리보기 테스트", + "pageStructure": { + "statistics": "1일차/2일차/전체 심사 진행률", + "filters": "년도, 분기 선택", + "checklist": "6개 카테고리 점검 항목", + "preview": "선택한 문서 미리보기 패널" + }, + "checklistCategories": [ + "원재료 품질관리 기준", + "제조공정 관리 기준", + "제품 품질관리 기준", + "제조설비 관리", + "검사설비 관리", + "문서 및 인증 관리" + ], + "prerequisites": "로그인된 사용자에게 품질인정심사 조회/수정 권한 필요" + } +} diff --git a/rank-management.json b/rank-management.json new file mode 100644 index 0000000..0de1b39 --- /dev/null +++ b/rank-management.json @@ -0,0 +1,553 @@ +{ + "id": "rank-management", + "name": "설정 - 직급관리", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "url": "/ko/settings/ranks", + "menuNavigation": { + "level1": "설정", + "level2": "직급관리", + "expectedUrl": "/ko/settings/ranks" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", + "level1": "설정", + "level2": "직급관리", + "alternativeLevel1Names": ["설정", "Settings", "환경설정", "시스템설정", "관리"], + "alternativeLevel2Names": ["직급관리", "직급 관리", "Ranks", "직급", "Position Management"], + "scrollConfig": { + "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", + "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + } + }, + "expectedAPIs": [ + { + "method": "GET", + "path": "/api/v1/positions?type=rank", + "description": "직급 목록 조회" + }, + { + "method": "POST", + "path": "/api/v1/positions", + "description": "직급 생성" + }, + { + "method": "PUT", + "path": "/api/v1/positions/{id}", + "description": "직급 수정" + }, + { + "method": "DELETE", + "path": "/api/v1/positions/{id}", + "description": "직급 삭제" + }, + { + "method": "PUT", + "path": "/api/v1/positions/reorder", + "description": "직급 순서 변경" + } + ], + "steps": [ + { + "id": "step-00", + "name": "사이드바 메뉴 탐색 준비", + "description": "사이드바를 최상단으로 스크롤하고 메뉴 구조 파악", + "actions": [ + { "type": "scroll", "target": "sidebar", "direction": "top", "description": "사이드바 최상단으로 스크롤" }, + { "type": "wait", "duration": 500 } + ] + }, + { + "id": "step-01", + "name": "2단계 메뉴 진입: 설정 > 직급관리", + "description": "사이드바를 스크롤하며 설정 > 직급관리 메뉴를 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "설정", + "alternativeTexts": ["설정", "Settings", "환경설정", "시스템설정"], + "scrollContainer": "sidebar", + "maxAttempts": 10, + "description": "스크롤하며 설정 메뉴 찾기" + }, + { "type": "click", "target": "설정", "description": "설정 메뉴 클릭" }, + { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, + { + "type": "scrollAndFind", + "target": "직급관리", + "alternativeTexts": ["직급관리", "직급 관리", "Ranks", "직급"], + "scrollContainer": "submenu", + "maxAttempts": 5, + "description": "서브메뉴에서 직급관리 찾기" + }, + { "type": "click", "target": "직급관리", "description": "직급관리 메뉴 클릭" }, + { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 } + ], + "expected": { + "url": "/ko/settings/ranks", + "title": "직급관리", + "authenticated": true + }, + "verification": [ + "설정 메뉴가 펼쳐졌는지 확인", + "직급관리 서브메뉴 클릭 성공", + "페이지 타이틀 '직급관리' 확인", + "설명 '사원의 직급을 관리합니다. 드래그하여 순서를 변경할 수 있습니다.' 확인", + "Award 아이콘 표시", + "404 에러 없이 페이지 로드 완료" + ] + }, + { + "id": "step-02", + "name": "직급 추가 입력 영역 확인", + "action": "verify", + "verification": [ + "입력 필드 존재 (placeholder: '직급명을 입력하세요')", + "추가 버튼 존재 (disabled 상태)", + "Plus 아이콘 표시" + ] + }, + { + "id": "step-03", + "name": "직급 목록 카드 확인", + "action": "verify", + "verification": [ + "직급 목록 카드 표시", + "기존 직급 목록 로드 확인", + "각 직급 항목 구조: 드래그 핸들, 순서 번호, 직급명, 수정 버튼, 삭제 버튼" + ] + }, + { + "id": "step-04", + "name": "드래그 핸들 아이콘 확인", + "action": "verify", + "verification": [ + "각 직급 항목에 GripVertical 아이콘 표시", + "순서 번호 표시 (1, 2, 3...)", + "cursor-move 스타일 적용 확인" + ] + }, + { + "id": "step-05", + "name": "안내 문구 확인", + "action": "verify", + "verification": [ + "하단 안내 문구 표시: '※ 직급 순서는 드래그 앤 드롭으로 변경할 수 있습니다.'" + ] + }, + { + "id": "step-06", + "name": "직급 추가 - 빈 값 입력 시도", + "action": "type", + "target": "직급명 입력 필드", + "value": "", + "verification": [ + "추가 버튼 disabled 상태 유지", + "입력 불가 확인" + ] + }, + { + "id": "step-07", + "name": "직급 추가 - 공백만 입력 시도", + "action": "type", + "target": "직급명 입력 필드", + "value": " ", + "verification": [ + "추가 버튼 클릭 가능하나 실제 추가 안됨", + "입력 필드 값 유지 또는 초기화" + ] + }, + { + "id": "step-08", + "name": "직급 추가 - 정상 입력", + "action": "type", + "target": "직급명 입력 필드", + "value": "E2E 테스트 직급1", + "verification": [ + "입력 값 반영 확인", + "추가 버튼 활성화 (enabled)" + ] + }, + { + "id": "step-09", + "name": "직급 추가 실행 (버튼 클릭)", + "action": "click", + "target": "추가 버튼", + "critical": true, + "verification": [ + "추가 버튼 클릭", + "로딩 상태 표시 (Loader2 아이콘)", + "API 호출: POST /api/v1/positions", + "API 응답 200 OK 확인", + "성공 토스트 메시지: '직급이 추가되었습니다.'", + "입력 필드 초기화", + "추가 버튼 다시 disabled 상태", + "목록에 신규 직급 표시 (하단에 추가)" + ] + }, + { + "id": "step-10", + "name": "신규 직급 확인", + "action": "verify", + "verification": [ + "'E2E 테스트 직급1' 목록에 표시", + "순서 번호 자동 할당 (마지막 순서)", + "드래그 핸들, 수정/삭제 버튼 존재" + ] + }, + { + "id": "step-11", + "name": "직급 추가 - Enter 키로 등록", + "action": "type", + "target": "직급명 입력 필드", + "value": "E2E 테스트 직급2", + "verification": [ + "입력 값 반영 확인" + ] + }, + { + "id": "step-12", + "name": "Enter 키 입력", + "action": "keypress", + "target": "직급명 입력 필드", + "key": "Enter", + "critical": true, + "verification": [ + "Enter 키로 직급 추가 실행", + "API 호출: POST /api/v1/positions", + "성공 토스트 메시지 확인", + "입력 필드 초기화", + "'E2E 테스트 직급2' 목록에 추가" + ] + }, + { + "id": "step-13", + "name": "세 번째 직급 추가 (드래그 테스트용)", + "action": "type+click", + "target": "직급명 입력 필드", + "value": "E2E 테스트 직급3", + "verification": [ + "입력 후 추가 버튼 클릭", + "API 호출 및 성공 확인", + "'E2E 테스트 직급3' 목록에 추가" + ] + }, + { + "id": "step-14", + "name": "직급 목록 상태 확인", + "action": "verify", + "verification": [ + "총 N개 직급 표시 (기존 + 신규 3개)", + "각 직급의 순서 번호 연속적 (1, 2, 3...)", + "신규 직급들이 하단에 순서대로 추가됨" + ] + }, + { + "id": "step-15", + "name": "직급 수정 다이얼로그 열기", + "action": "click", + "target": "E2E 테스트 직급1의 수정 버튼", + "verification": [ + "수정 다이얼로그 표시", + "다이얼로그 제목: '직급 수정'", + "직급명 입력 필드에 현재 값 표시: 'E2E 테스트 직급1'", + "취소 버튼 존재", + "수정 버튼 존재" + ] + }, + { + "id": "step-16", + "name": "직급명 수정 입력", + "action": "type", + "target": "다이얼로그 직급명 입력 필드", + "value": "E2E 테스트 직급1 (수정됨)", + "verification": [ + "입력 값 반영 확인", + "수정 버튼 활성화" + ] + }, + { + "id": "step-17", + "name": "직급 수정 실행", + "action": "click", + "target": "다이얼로그 수정 버튼", + "critical": true, + "verification": [ + "수정 버튼 클릭", + "로딩 상태 표시", + "API 호출: PUT /api/v1/positions/{id}", + "API 응답 200 OK 확인", + "성공 토스트 메시지: '직급이 수정되었습니다.'", + "다이얼로그 닫힘", + "목록에서 직급명 변경 확인: 'E2E 테스트 직급1 (수정됨)'" + ] + }, + { + "id": "step-18", + "name": "수정 취소 테스트 - 다이얼로그 열기", + "action": "click", + "target": "E2E 테스트 직급2의 수정 버튼", + "verification": [ + "다이얼로그 표시", + "현재 값: 'E2E 테스트 직급2'" + ] + }, + { + "id": "step-19", + "name": "수정 취소", + "action": "click", + "target": "다이얼로그 취소 버튼", + "verification": [ + "다이얼로그 닫힘", + "직급명 변경 없음 (API 호출 없음)" + ] + }, + { + "id": "step-20", + "name": "드래그 앤 드롭 - 첫 번째 항목 선택", + "action": "drag_start", + "target": "목록 마지막 직급 (E2E 테스트 직급3)", + "verification": [ + "드래그 시작", + "해당 항목 opacity 50% 및 배경색 변경", + "cursor-move 활성화" + ] + }, + { + "id": "step-21", + "name": "드래그 앤 드롭 - 상단으로 이동", + "action": "drag_over", + "target": "목록 첫 번째 위치", + "verification": [ + "드래그 중 위치 변경 시각적 피드백", + "실시간 순서 변경 (로컬 상태)" + ] + }, + { + "id": "step-22", + "name": "드래그 앤 드롭 - 드롭 실행", + "action": "drag_end", + "critical": true, + "verification": [ + "드래그 종료", + "API 호출: PUT /api/v1/positions/reorder", + "Request body에 전체 직급의 새로운 순서 포함", + "API 응답 200 OK 확인", + "성공 토스트 메시지: '순서가 변경되었습니다.'", + "순서 번호 업데이트 확인", + "'E2E 테스트 직급3'이 최상단으로 이동" + ] + }, + { + "id": "step-23", + "name": "순서 변경 확인", + "action": "verify", + "verification": [ + "변경된 순서가 화면에 반영됨", + "각 항목의 순서 번호 재할당 (1, 2, 3...)", + "드래그한 항목이 목표 위치에 정확히 배치" + ] + }, + { + "id": "step-24", + "name": "삭제 확인 다이얼로그 열기", + "action": "click", + "target": "E2E 테스트 직급3의 삭제 버튼", + "verification": [ + "삭제 확인 다이얼로그 표시", + "다이얼로그 제목: '직급 삭제'", + "메시지: '\"E2E 테스트 직급3\" 직급을 삭제하시겠습니까?'", + "경고 메시지: '이 직급을 사용 중인 사원이 있으면 해당 사원의 직급이 초기화됩니다.' (빨간색)", + "취소 버튼 존재", + "삭제 버튼 존재 (빨간색)" + ] + }, + { + "id": "step-25", + "name": "삭제 취소", + "action": "click", + "target": "다이얼로그 취소 버튼", + "verification": [ + "다이얼로그 닫힘", + "직급 삭제되지 않음 (API 호출 없음)", + "목록에 여전히 존재" + ] + }, + { + "id": "step-26", + "name": "삭제 실행 - 다이얼로그 재열기", + "action": "click", + "target": "E2E 테스트 직급3의 삭제 버튼", + "verification": [ + "삭제 확인 다이얼로그 표시" + ] + }, + { + "id": "step-27", + "name": "삭제 확인 실행", + "action": "click", + "target": "다이얼로그 삭제 버튼", + "critical": true, + "verification": [ + "삭제 버튼 클릭", + "로딩 상태 표시 (Loader2 아이콘)", + "API 호출: DELETE /api/v1/positions/{id}", + "API 응답 200 OK 확인", + "성공 토스트 메시지: '직급이 삭제되었습니다.'", + "다이얼로그 닫힘", + "목록에서 'E2E 테스트 직급3' 제거됨", + "순서 번호 재정렬" + ] + }, + { + "id": "step-28", + "name": "나머지 테스트 직급 삭제 - 직급2", + "action": "click+confirm", + "target": "E2E 테스트 직급2의 삭제 버튼", + "verification": [ + "삭제 다이얼로그 표시 → 삭제 버튼 클릭", + "API 호출 및 성공 확인", + "목록에서 제거" + ] + }, + { + "id": "step-29", + "name": "나머지 테스트 직급 삭제 - 직급1 (수정됨)", + "action": "click+confirm", + "target": "E2E 테스트 직급1 (수정됨)의 삭제 버튼", + "verification": [ + "삭제 다이얼로그 표시 → 삭제 버튼 클릭", + "API 호출 및 성공 확인", + "목록에서 제거", + "테스트 데이터 완전 정리" + ] + }, + { + "id": "step-30", + "name": "최종 상태 확인", + "action": "verify", + "verification": [ + "기존 직급만 남음 (테스트 데이터 모두 삭제)", + "순서 번호 정상", + "페이지 정상 동작" + ] + }, + { + "id": "step-31", + "name": "빈 목록 상태 테스트 (선택)", + "action": "verify", + "verification": [ + "만약 모든 직급 삭제 시: '등록된 직급이 없습니다.' 메시지 표시", + "입력 필드와 추가 버튼은 정상 표시" + ] + }, + { + "id": "step-32", + "name": "페이지 새로고침 후 데이터 확인", + "action": "reload", + "verification": [ + "페이지 새로고침", + "GET /api/v1/positions?type=rank 호출", + "저장된 순서대로 직급 목록 로드", + "이전 상태 유지 확인" + ] + }, + { + "id": "step-33", + "name": "한글 IME 입력 테스트", + "action": "type", + "target": "직급명 입력 필드", + "value": "부장", + "verification": [ + "한글 조합 중 Enter 키 입력 시 조합 완료 대기", + "조합 완료 후 추가 동작", + "isComposing 이벤트 처리 확인" + ] + }, + { + "id": "step-34", + "name": "특수문자 입력 테스트", + "action": "type+click", + "target": "직급명 입력 필드", + "value": "직급@#$%", + "verification": [ + "특수문자 포함 직급명 입력", + "추가 버튼 클릭", + "API 호출 및 저장 여부 확인", + "성공 시 목록에 표시, 실패 시 에러 메시지" + ] + }, + { + "id": "step-35", + "name": "긴 직급명 입력 테스트", + "action": "type+click", + "target": "직급명 입력 필드", + "value": "매우긴직급명테스트매우긴직급명테스트매우긴직급명테스트매우긴직급명테스트", + "verification": [ + "긴 직급명 입력", + "추가 시도", + "API 응답 확인 (길이 제한 있을 경우 에러)", + "UI에서 텍스트 오버플로우 처리 확인" + ] + }, + { + "id": "step-36", + "name": "중복 직급명 입력 테스트", + "action": "type+click", + "target": "직급명 입력 필드", + "value": "과장", + "verification": [ + "기존 직급명과 동일한 이름 입력", + "추가 버튼 클릭", + "API 응답 확인", + "중복 허용 시 성공, 중복 불가 시 에러 메시지" + ] + }, + { + "id": "step-37", + "name": "로딩 중 상태 테스트", + "action": "verify", + "verification": [ + "API 호출 중 버튼 disabled 상태", + "Loader2 아이콘 표시", + "중복 클릭 방지 확인" + ] + }, + { + "id": "step-38", + "name": "에러 처리 테스트 (네트워크 오류 시뮬레이션)", + "action": "verify", + "verification": [ + "API 호출 실패 시 에러 토스트 표시", + "에러 메시지 명확성 확인", + "페이지 상태 유지 (크래시 없음)" + ] + } + ], + "testData": { + "ranks": [ + { + "name": "E2E 테스트 직급1", + "expected_order": "last" + }, + { + "name": "E2E 테스트 직급2", + "expected_order": "last" + }, + { + "name": "E2E 테스트 직급3", + "expected_order": "last → first (after drag)" + } + ] + }, + "cleanup": { + "description": "Step-27~29에서 테스트 데이터 삭제 완료", + "method": "각 직급의 삭제 버튼 클릭 → 확인 다이얼로그에서 삭제 확인" + } +} diff --git a/receivables-status.json b/receivables-status.json new file mode 100644 index 0000000..9b3718f --- /dev/null +++ b/receivables-status.json @@ -0,0 +1,343 @@ +{ + "id": "receivables-status", + "name": "미수금현황 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "회계관리 > 미수금현황 메뉴의 연도 선택, 정렬, 검색, 뷰 전환, 버튼 동작, 메모 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", + "level1": "회계관리", + "level2": "미수금현황", + "alternativeLevel1Names": ["회계관리", "회계 관리", "Accounting", "재무관리", "재무/회계"], + "alternativeLevel2Names": ["미수금현황", "미수금 현황", "Receivables", "미수금", "미수금관리", "채권현황"], + "fallbackUrls": [ + "/ko/accounting/receivables", + "/ko/accounting/receivables-status", + "/ko/accounting/accounts-receivable", + "/accounting/receivables", + "/ko/finance/receivables" + ], + "scrollConfig": { + "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", + "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + } + }, + + "testFocus": { + "primary": "필터/정렬/뷰 전환 및 메모 저장 기능 검증", + "description": "연도 선택, 정렬, 거래처 검색, 거래처/연체 뷰 전환, 저장/새로고침/엑셀다운로드 버튼, 메모 기록 저장 확인" + }, + + "prerequisites": { + "authentication": true, + "testData": { + "searchKeyword": "테스트", + "memoText": "테스트 메모 입력" + } + }, + + "steps": [ + { + "id": 0, + "name": "사이드바 메뉴 탐색 준비", + "description": "사이드바를 최상단으로 스크롤하고 메뉴 구조 파악", + "actions": [ + { "type": "scroll", "target": "sidebar", "direction": "top", "description": "사이드바 최상단으로 스크롤" }, + { "type": "wait", "duration": 500 }, + { "type": "screenshot", "name": "sidebar_initial_state" } + ], + "verification": [ + "사이드바가 화면에 보이는지 확인", + "메뉴 항목들이 렌더링되었는지 확인" + ] + }, + { + "id": 1, + "name": "1차 메뉴 찾기: 회계관리 (스크롤 포함)", + "description": "사이드바를 스크롤하며 '회계관리' 메뉴를 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "회계관리", + "alternativeTexts": ["회계관리", "회계 관리", "Accounting", "재무관리"], + "scrollContainer": "sidebar", + "maxAttempts": 10, + "description": "스크롤하며 회계관리 메뉴 찾기" + }, + { "type": "wait", "duration": 300 }, + { "type": "click", "target": "회계관리", "description": "회계관리 메뉴 클릭" }, + { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, + { "type": "screenshot", "name": "accounting_menu_expanded" } + ], + "verification": [ + "회계관리 메뉴가 클릭되었는지 확인", + "서브메뉴가 펼쳐졌는지 확인", + "하위 메뉴 항목들이 보이는지 확인" + ], + "fallback": { + "if": "메뉴를 찾을 수 없음", + "then": "사이드바 전체를 스크롤하며 재탐색" + } + }, + { + "id": 2, + "name": "2차 메뉴 찾기: 미수금현황 (스크롤 포함)", + "description": "서브메뉴에서 '미수금현황'을 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "미수금현황", + "alternativeTexts": ["미수금현황", "미수금 현황", "Receivables", "미수금", "채권현황"], + "scrollContainer": "submenu", + "maxAttempts": 5, + "description": "서브메뉴에서 미수금현황 찾기" + }, + { "type": "wait", "duration": 200 }, + { "type": "click", "target": "미수금현황", "description": "미수금현황 메뉴 클릭" }, + { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 }, + { "type": "screenshot", "name": "receivables_status_page" } + ], + "verification": [ + "미수금현황 메뉴 클릭 성공", + "페이지 이동 또는 컨텐츠 로드" + ] + }, + { + "id": 3, + "name": "404 에러 감지 및 대체 경로 시도", + "description": "페이지 로드 후 404 에러 여부 확인, 404시 대체 경로 탐색", + "actions": [ + { "type": "wait", "duration": 1000 }, + { "type": "checkFor404", "indicators": [ + "페이지를 찾을 수 없습니다", + "404", + "Not Found", + "존재하지 않거나" + ]}, + { "type": "screenshot", "name": "page_load_result" } + ], + "verification": [ + "현재 페이지가 404인지 확인" + ], + "onError404": { + "description": "404 에러 발생 시 대체 URL 시도", + "actions": [ + { "type": "log", "message": "404 감지 - 대체 경로 탐색 시작" }, + { + "type": "tryAlternativeUrls", + "urls": [ + "/ko/accounting/receivables", + "/ko/accounting/receivables-status", + "/ko/accounting/accounts-receivable" + ], + "stopOnSuccess": true + }, + { + "type": "ifStillFailed", + "action": "navigateViaMenuClick", + "description": "URL 직접 접근 실패 시 메뉴 클릭으로 재시도" + } + ] + } + }, + { + "id": 4, + "name": "페이지 정상 로드 확인", + "description": "미수금현황 페이지가 정상적으로 로드되었는지 확인", + "actions": [ + { "type": "verify", "target": "pageTitle", "contains": ["미수금현황", "미수금", "Receivables"] }, + { "type": "verify", "target": "pageContent", "notContains": ["404", "찾을 수 없습니다", "Not Found"] } + ], + "verification": [ + "페이지 제목 '미수금현황' 또는 관련 텍스트 표시", + "404 에러 메시지 미표시", + "콘텐츠가 정상 렌더링됨" + ], + "successCriteria": { + "urlPattern": "/accounting/receivables", + "requiredElements": ["미수금", "거래처", "연도"] + } + }, + { + "id": "step-5", + "name": "목록 페이지 구조 확인", + "description": "통계 카드, 필터, 테이블 컬럼, 버튼 구조 확인", + "actions": [ + { + "type": "verify", + "target": "페이지 구조" + } + ], + "expected": { + "filters": ["연도 선택", "검색", "정렬"], + "buttons": ["저장", "새로고침", "엑셀 다운로드"], + "viewSwitch": ["거래처", "연체"], + "tableColumns": ["체크박스", "거래처명", "미수금액", "메모", "기타"] + } + }, + { + "id": "step-6", + "name": "연도 선택 드롭다운 옵션 확인", + "description": "연도 선택 드롭다운의 옵션 목록 확인", + "actions": [ + { "type": "click", "target": "연도 선택 드롭다운" }, + { "type": "verify", "target": "드롭다운 옵션" } + ], + "expected": { + "options": ["2024", "2025", "2026", "기타 연도"] + } + }, + { + "id": "step-7", + "name": "정렬 드롭다운 옵션 확인", + "description": "정렬 드롭다운의 옵션 목록 확인", + "actions": [ + { "type": "click", "target": "정렬 드롭다운" }, + { "type": "verify", "target": "드롭다운 옵션" } + ], + "expected": { + "options": ["미수금액 높은순", "미수금액 낮은순", "거래처명 가나다순", "최신순"] + } + }, + { + "id": "step-8", + "name": "⚠️ 필수 검증: 거래처 검색 기능 테스트", + "critical": true, + "description": "검색창에 거래처명 입력 후 필터링 확인", + "actions": [ + { + "type": "capture", + "variable": "beforeSearchCount", + "selector": "table tbody tr", + "extract": "count", + "description": "검색 전 행 수 저장" + }, + { "type": "clear", "target": "검색 입력 필드" }, + { "type": "type", "target": "검색 입력 필드", "value": "{prerequisites.testData.searchKeyword}" }, + { "type": "wait", "duration": 1000, "description": "검색 결과 로딩 대기" }, + { + "type": "capture", + "variable": "afterSearchCount", + "selector": "table tbody tr", + "extract": "count", + "description": "검색 후 행 수 저장" + } + ], + "verify": { + "searchApplied": true, + "tableContains": "{prerequisites.testData.searchKeyword}", + "dataChanged": "beforeSearchCount may differ from afterSearchCount" + }, + "expected": { + "searchApplied": true, + "filteredResults": "검색어 포함 거래처만 표시" + } + }, + { + "id": "step-8-1", + "name": "검색 결과 데이터 검증", + "critical": true, + "description": "검색 결과의 모든 행이 검색어를 포함하는지 확인", + "verify": { + "allRowsContain": "{prerequisites.testData.searchKeyword}", + "columnToCheck": "거래처명" + } + }, + { + "id": "step-8-2", + "name": "검색 초기화 확인", + "actions": [ + { "type": "clear", "target": "검색 입력 필드" }, + { "type": "wait", "duration": 500 }, + { + "type": "capture", + "variable": "afterClearCount", + "selector": "table tbody tr", + "extract": "count" + } + ], + "verify": { + "dataRestored": "afterClearCount should equal beforeSearchCount" + } + }, + { + "id": "step-9", + "name": "거래처/연체 Switch 버튼 전환 테스트", + "description": "Switch 버튼 클릭하여 뷰 전환 확인", + "actions": [ + { "type": "click", "target": "Switch 버튼" }, + { "type": "wait", "target": "뷰 전환 완료" } + ], + "expected": { + "viewChanged": true, + "tableColumnsChanged": "뷰에 맞는 컬럼 표시" + } + }, + { + "id": "step-10", + "name": "새로고침 버튼 동작 확인", + "description": "새로고침 버튼 클릭 시 데이터 리로드 확인", + "actions": [ + { "type": "click", "target": "새로고침 버튼" }, + { "type": "wait", "target": "데이터 리로드" } + ], + "expected": { + "buttonClicked": true, + "dataReloaded": true + } + }, + { + "id": "step-11", + "name": "엑셀 다운로드 버튼 동작 확인", + "description": "엑셀 다운로드 버튼 클릭 시 파일 다운로드 확인", + "actions": [ + { "type": "click", "target": "엑셀 다운로드 버튼" }, + { "type": "wait", "target": "다운로드 처리" }, + { "type": "verify", "target": "Network Request 또는 다운로드 이벤트" } + ], + "expected": { + "buttonClicked": true, + "apiCall": "GET/POST export API", + "downloadEvent": "파일 다운로드 발생" + } + }, + { + "id": "step-12", + "name": "메모 기록 입력 및 저장", + "description": "첫 번째 행의 메모 필드에 테스트 메모 입력 후 저장", + "actions": [ + { "type": "click", "target": "첫 번째 행 메모 필드" }, + { "type": "clear", "target": "메모 필드" }, + { "type": "type", "target": "메모 필드", "value": "테스트 메모 입력" }, + { "type": "click", "target": "저장 버튼" }, + { "type": "wait", "target": "저장 처리 완료" } + ], + "expected": { + "memoEntered": "테스트 메모 입력", + "apiCall": "POST/PUT /accounting/receivables", + "successMessage": "저장 완료 토스트" + } + } + ], + + "cleanup": { + "description": "테스트 후 입력된 메모 원복 (필요시)", + "actions": [] + }, + + "notes": [ + "직접 URL 접근 금지: 반드시 메뉴 클릭으로 페이지 진입 (404 방지)", + "스크롤 필수: 사이드바가 길 경우 메뉴가 화면 밖에 있을 수 있음", + "대체 경로: 메뉴명이 변경되었을 수 있으므로 다양한 이름으로 탐색", + "메뉴 계층: 회계관리 > 미수금현황", + "엑셀 다운로드는 Network Request 및 실제 다운로드 이벤트 확인 필수" + ] +} diff --git a/receiving-management.json b/receiving-management.json new file mode 100644 index 0000000..791c21c --- /dev/null +++ b/receiving-management.json @@ -0,0 +1,188 @@ +{ + "id": "receiving-management", + "name": "입고관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "자재관리 > 입고관리 페이지의 입고 조회 및 상태별 필터링 기능을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/material/receiving", + "menuNavigation": { + "level1": "자재관리", + "level2": "입고관리", + "expectedUrl": "/material/receiving" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebar": { + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 5, + "waitAfterScroll": 300 + }, + "level1": { + "text": "자재관리", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "입고관리", + "waitAfterClick": 300 + }, + "fallbackUrl": "/material/receiving", + "expectedUrl": "/material/receiving" + }, + "timeout": 90000, + "tags": ["material", "receiving", "read-only"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 500 } + ] + }, + { + "id": "step-1", + "name": "자재관리 메뉴 진입", + "description": "자재관리 > 입고관리 메뉴로 이동", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "자재관리", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "자재관리" }, + { "type": "wait", "duration": 500 }, + { "type": "click", "target": "입고관리" } + ], + "expect": { + "url": "/material/receiving", + "visible": ["입고 목록"] + }, + "fallback": { + "type": "navigate", + "url": "/material/receiving" + } + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "통계 카드와 테이블 구조 확인", + "verify": { + "visible": ["입고대기", "배송중", "검사대기", "금일입고"], + "tableColumns": ["번호", "발주번호", "품목코드", "품목명", "공급업체", "발주수량", "입고수량", "LOT번호", "상태"] + } + }, + { + "id": "step-3", + "name": "필수 검증 #3: 상태 탭 필터 - 입고대기", + "description": "입고대기 탭 클릭하여 필터링 확인", + "actions": [ + { "type": "click", "target": "입고대기", "role": "tab" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "tabActive": "입고대기", + "dataFiltered": true + } + }, + { + "id": "step-4", + "name": "필수 검증 #3: 상태 탭 필터 - 입고완료", + "description": "입고완료 탭 클릭하여 필터링 확인", + "actions": [ + { "type": "click", "target": "입고완료", "role": "tab" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "tabActive": "입고완료", + "dataFiltered": true + } + }, + { + "id": "step-5", + "name": "전체 탭으로 복귀", + "description": "전체 탭 클릭하여 모든 입고 표시", + "actions": [ + { "type": "click", "target": "전체", "role": "tab" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "tabActive": "전체", + "allDataShown": true + } + }, + { + "id": "step-6", + "name": "빈 상태 확인", + "description": "데이터가 없을 때 빈 상태 메시지 확인", + "verify": { + "emptyStateVisible": "검색 결과가 없습니다" + } + }, + { + "id": "step-7", + "name": "통계 카드 값 확인", + "description": "입고대기/배송중/검사대기/금일입고 카운트 표시 확인", + "verify": { + "statsCards": ["입고대기", "배송중", "검사대기", "금일입고"], + "countsDisplayed": true + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/material/receiving", + "message": "입고관리 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "text=입고 목록", + "message": "입고 목록 제목이 표시되어야 함" + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 3, + "name": "검색/필터", + "trigger": "상태 탭 필터", + "verification": "데이터 변화 확인", + "failCondition": "필터 적용 후 데이터 무변화" + } + ] + }, + + "notes": { + "testScope": "입고 목록 조회 및 상태별 필터링 테스트", + "pageType": "조회 전용 (발주 기반 입고 처리)", + "statsCards": ["입고대기", "배송중", "검사대기", "금일입고"], + "statusTabs": ["전체", "입고대기", "입고완료"], + "tableColumns": ["번호", "발주번호", "품목코드", "품목명", "공급업체", "발주수량", "입고수량", "LOT번호", "상태"], + "workflow": "발주 → 배송중 → 검사대기 → 입고완료", + "prerequisites": "로그인된 사용자, 발주 데이터 존재 시 입고 가능" + } +} diff --git a/reference-box.json b/reference-box.json new file mode 100644 index 0000000..866f34e --- /dev/null +++ b/reference-box.json @@ -0,0 +1,678 @@ +{ + "scenarioId": "reference-box", + "scenarioName": "참조함 E2E 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "참조함 페이지의 모든 기능 검증 (탭 전환, 검색, 필터, 정렬, 열람/미열람 처리, 문서 상세)", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "결재관리", + "level2": "참조함", + "expectedUrl": "/ko/approval/reference" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", + "level1": "결재관리", + "level2": "참조함", + "alternativeLevel1Names": ["결재관리", "결재 관리", "Approval", "전자결재"], + "alternativeLevel2Names": ["참조함", "참조 함", "Reference", "참조문서", "CC문서"], + "scrollConfig": { + "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", + "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + } + }, + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/approvals/reference", + "description": "참조함 목록 조회", + "queryParams": ["page", "per_page", "search", "is_read", "approval_type", "sort_by", "sort_dir"] + }, + { + "method": "POST", + "endpoint": "/api/v1/approvals/{id}/read", + "description": "열람 처리" + }, + { + "method": "POST", + "endpoint": "/api/v1/approvals/{id}/unread", + "description": "미열람 처리" + } + ], + "steps": [ + { + "id": 0, + "name": "사이드바 메뉴 탐색 준비", + "description": "사이드바를 최상단으로 스크롤하고 메뉴 구조 파악", + "actions": [ + { "type": "scroll", "target": "sidebar", "direction": "top", "description": "사이드바 최상단으로 스크롤" }, + { "type": "wait", "duration": 500 } + ] + }, + { + "id": 1, + "name": "2단계 메뉴 진입: 결재관리 > 참조함", + "description": "사이드바를 스크롤하며 결재관리 > 참조함 메뉴를 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "결재관리", + "alternativeTexts": ["결재관리", "결재 관리", "Approval", "전자결재"], + "scrollContainer": "sidebar", + "maxAttempts": 10, + "description": "스크롤하며 결재관리 메뉴 찾기" + }, + { "type": "click", "target": "결재관리", "description": "결재관리 메뉴 클릭" }, + { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, + { + "type": "scrollAndFind", + "target": "참조함", + "alternativeTexts": ["참조함", "참조 함", "Reference", "참조문서"], + "scrollContainer": "submenu", + "maxAttempts": 5, + "description": "서브메뉴에서 참조함 찾기" + }, + { "type": "click", "target": "참조함", "description": "참조함 메뉴 클릭" }, + { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 } + ], + "verification": [ + "페이지 URL이 /approval/reference인지 확인", + "페이지 제목 '참조함' 표시 확인", + "설명 텍스트 '참조로 지정된 문서를 확인합니다.' 표시 확인", + "통계 카드 3개 표시 (전체, 열람, 미열람)", + "날짜 범위 선택기 표시 확인", + "검색창 표시 확인 (placeholder: '제목, 기안자, 부서 검색...')", + "탭 버튼 3개 표시 (전체, 열람, 미열람)", + "필터 드롭다운 표시 (전체, 지출결의서, 품의서, 지출예상내역서)", + "정렬 드롭다운 표시 (최신순, 오래된순, 기안일 오름차순, 기안일 내림차순)", + "테이블 헤더 확인 (번호, 문서번호, 문서유형, 제목, 기안자, 기안일시, 상태)" + ] + }, + { + "id": 2, + "name": "데이터 로딩 대기", + "action": "3초 대기하여 API 데이터 로드 완료", + "verification": [ + "테이블에 데이터 행이 표시되는지 확인", + "통계 카드에 숫자가 표시되는지 확인 (N건)", + "'검색 결과가 없습니다' 메시지가 없는지 확인", + "각 문서의 열람 상태 배지 표시 확인 (열람/미열람)" + ] + }, + { + "id": 3, + "name": "통계 카드 데이터 확인", + "action": "통계 카드의 숫자 확인", + "verification": [ + "전체 건수 = 열람 건수 + 미열람 건수", + "각 카드의 아이콘 표시 확인 (Files, Eye, EyeOff)", + "스크린샷 촬영하여 초기 상태 저장" + ] + }, + { + "id": 4, + "name": "탭 전환 - 열람 탭", + "action": "'열람' 탭 클릭", + "verification": [ + "탭 활성화 상태 변경 확인", + "테이블에 '열람' 상태 문서만 표시", + "모든 행의 상태 배지가 '열람'인지 확인", + "표시된 문서 개수가 통계 카드의 '열람' 건수와 일치" + ] + }, + { + "id": 5, + "name": "탭 전환 - 미열람 탭", + "action": "'미열람' 탭 클릭", + "verification": [ + "탭 활성화 상태 변경 확인", + "테이블에 '미열람' 상태 문서만 표시", + "모든 행의 상태 배지가 '미열람'인지 확인", + "표시된 문서 개수가 통계 카드의 '미열람' 건수와 일치" + ] + }, + { + "id": 6, + "name": "탭 전환 - 전체 탭으로 복귀", + "action": "'전체' 탭 클릭", + "verification": [ + "탭 활성화 상태 변경 확인", + "테이블에 모든 문서 표시 (열람 + 미열람)", + "표시된 문서 개수가 통계 카드의 '전체' 건수와 일치" + ] + }, + { + "id": 7, + "name": "⚠️ 필수 검증: 검색 기능 - 기안자 검색", + "critical": true, + "actions": [ + { + "type": "capture", + "variable": "beforeSearchCount", + "selector": "table tbody tr", + "extract": "count", + "description": "검색 전 문서 수 저장" + }, + { + "type": "fill", + "target": "검색창", + "value": "김철수", + "description": "기안자 이름 검색" + }, + { + "type": "wait", + "duration": 1000, + "description": "검색 결과 로딩 대기" + }, + { + "type": "capture", + "variable": "afterSearchCount", + "selector": "table tbody tr", + "extract": "count", + "description": "검색 후 문서 수 저장" + } + ], + "verify": { + "searchApplied": true, + "tableContains": "김철수", + "dataChanged": "beforeSearchCount may differ from afterSearchCount" + }, + "verification": [ + "검색창에 입력한 텍스트 표시 확인", + "Enter 키 입력 또는 자동 검색 실행", + "검색어를 포함한 문서만 필터링되어 표시", + "검색 결과 건수 확인" + ] + }, + { + "id": "7-1", + "name": "검색 결과 데이터 검증", + "critical": true, + "description": "검색 결과의 모든 행이 검색어를 포함하는지 확인", + "verify": { + "allRowsContain": "김철수", + "columnToCheck": "기안자" + } + }, + { + "id": 8, + "name": "검색 초기화", + "actions": [ + { + "type": "clear", + "target": "검색창" + }, + { + "type": "wait", + "duration": 500 + }, + { + "type": "capture", + "variable": "afterClearCount", + "selector": "table tbody tr", + "extract": "count" + } + ], + "verify": { + "dataRestored": "afterClearCount should equal beforeSearchCount" + }, + "verification": [ + "전체 문서 목록 복원 확인", + "원래 문서 개수로 복원" + ] + }, + { + "id": 9, + "name": "필터 기능 - 문서유형 선택", + "action": "필터 드롭다운에서 '품의서' 선택", + "verification": [ + "필터 드롭다운 값이 '품의서'로 변경", + "테이블에 '품의서' 유형 문서만 표시", + "모든 행의 문서유형 배지가 '품의서'인지 확인" + ] + }, + { + "id": 10, + "name": "필터 초기화", + "action": "필터 드롭다운에서 '전체' 선택", + "verification": [ + "전체 문서 목록 복원 확인" + ] + }, + { + "id": 11, + "name": "정렬 기능 - 오래된순", + "action": "정렬 드롭다운에서 '오래된순' 선택", + "verification": [ + "정렬 드롭다운 값이 '오래된순'으로 변경", + "테이블 데이터가 오래된 날짜부터 표시", + "기안일시 컬럼 확인하여 오름차순 정렬 검증" + ] + }, + { + "id": 12, + "name": "정렬 초기화", + "action": "정렬 드롭다운에서 '최신순' 선택", + "verification": [ + "테이블 데이터가 최신 날짜부터 표시" + ] + }, + { + "id": 13, + "name": "체크박스 - 단일 선택", + "action": "첫 번째 문서의 체크박스 클릭", + "verification": [ + "체크박스 선택 상태 확인", + "'열람' 버튼 표시 확인", + "'미열람' 버튼 표시 확인", + "조건부 버튼이 헤더 영역에 표시됨" + ] + }, + { + "id": 14, + "name": "체크박스 - 선택 해제", + "action": "첫 번째 문서의 체크박스 다시 클릭", + "verification": [ + "체크박스 선택 해제 확인", + "'열람' 및 '미열람' 버튼 사라짐 확인" + ] + }, + { + "id": 15, + "name": "체크박스 - 다중 선택", + "action": "첫 번째와 두 번째 문서의 체크박스 클릭", + "verification": [ + "2개 문서 선택 확인", + "'열람' 및 '미열람' 버튼 표시 확인" + ] + }, + { + "id": 16, + "name": "문서 상세 모달 - 열기", + "action": "체크박스 선택 해제 후 문서 행 클릭 (체크박스 제외)", + "verification": [ + "문서 상세 모달 열림 확인", + "⚠️ 필수 검증 #5: 목업 페이지 감지", + "모달 제목 확인 (문서유형 + ' 상세')", + "문서번호 표시 확인", + "작성일자 표시 확인", + "기안자 정보 표시 확인", + "결재선 정보 표시 확인", + "PDF/인쇄 버튼 존재 확인" + ] + }, + { + "id": "16-pdf-1", + "name": "⚠️ 필수 검증: PDF 다운로드 전 모달 스크린샷", + "critical": true, + "description": "PDF 생성 전 모달 상태를 스크린샷으로 캡처하여 CSS 문제 감지용 기준 이미지 확보", + "prerequisite": "step-16의 문서 상세 모달이 열려있는 상태에서 실행", + "actions": [ + { + "type": "screenshot", + "name": "pdf-preview-before-download-reference-box", + "fullPage": false, + "selector": "[role='dialog'], .modal, [data-state='open']", + "savePath": "tests/e2e/results/hotfix/screenshots/", + "description": "PDF 생성 대상 모달 전체 캡처" + } + ], + "verify": { + "screenshotCaptured": true, + "purpose": "PDF CSS 문제 감지를 위한 기준 이미지" + } + }, + { + "id": "16-pdf-2", + "name": "⚠️ 필수 검증: PDF 다운로드 실행 및 파일 보관", + "critical": true, + "description": "PDF 다운로드 후 파일을 지정 폴더에 보관하여 수동 검증 가능하게 함", + "actions": [ + { + "type": "verify", + "target": "PDF 버튼 존재", + "selector": "button:has-text('PDF'), [aria-label*='PDF']", + "description": "PDF 다운로드 버튼 존재 확인" + }, + { + "type": "expectResponse", + "id": "pdf-download-response-reference-box", + "urlPattern": "/api/v1/approvals/*/pdf", + "description": "PDF 다운로드 API 응답 대기 설정" + }, + { + "type": "click", + "target": "PDF 버튼", + "selector": "button:has-text('PDF')", + "description": "PDF 다운로드 버튼 클릭" + }, + { + "type": "wait", + "duration": 3000, + "description": "PDF 생성 및 다운로드 대기" + }, + { + "type": "assertResponse", + "id": "pdf-download-response-reference-box", + "checks": { + "status": 200, + "contentType": "application/pdf" + } + }, + { + "type": "saveDownloadedFile", + "targetPath": "tests/e2e/results/hotfix/pdf-samples/", + "fileNamePattern": "reference-box-{timestamp}.pdf", + "description": "다운로드된 PDF 파일을 지정 폴더에 보관" + } + ], + "verify": { + "apiSuccess": true, + "fileDownloaded": true, + "fileSaved": "tests/e2e/results/hotfix/pdf-samples/" + } + }, + { + "id": "16-pdf-3", + "name": "⚠️ PDF 파일 유효성 검증", + "critical": true, + "description": "다운로드된 PDF 파일의 기본 유효성 검사", + "actions": [ + { + "type": "verifyDownloadedFile", + "checks": { + "fileExists": true, + "fileSize": "> 1024", + "pdfSignature": "%PDF-", + "description": "PDF 파일 헤더 검증" + } + } + ], + "verify": { + "pdfValid": true, + "minFileSize": "1KB 이상" + } + }, + { + "id": "16-pdf-4", + "name": "📋 PDF 스타일 수동 확인 체크리스트", + "type": "manualVerification", + "critical": true, + "description": "개발자가 다운로드된 PDF를 열어 시각적으로 확인해야 하는 항목", + "manualChecklist": [ + {"id": "css-1", "item": "테이블 경계선이 올바르게 표시되는가?", "category": "테이블 스타일"}, + {"id": "css-2", "item": "한글 폰트가 깨지지 않고 정상 표시되는가?", "category": "폰트"}, + {"id": "css-3", "item": "숫자/금액 정렬이 올바른가? (우측 정렬)", "category": "정렬"}, + {"id": "css-4", "item": "여백(margin/padding)이 적절한가?", "category": "레이아웃"}, + {"id": "css-5", "item": "헤더/푸터가 각 페이지에 올바르게 표시되는가?", "category": "페이지 구조"}, + {"id": "css-6", "item": "로고/이미지가 정상 표시되는가?", "category": "이미지"}, + {"id": "css-7", "item": "페이지 나눔(page break)이 적절한 위치에서 발생하는가?", "category": "페이지 나눔"}, + {"id": "css-8", "item": "배경색/강조색이 올바르게 적용되었는가?", "category": "색상"}, + {"id": "css-9", "item": "텍스트가 잘리거나 겹치지 않는가?", "category": "오버플로우"}, + {"id": "css-10", "item": "결재선 정보가 정상적으로 표시되는가?", "category": "결재선"} + ], + "outputFiles": { + "screenshot": "tests/e2e/results/hotfix/screenshots/pdf-preview-before-download-reference-box-*.png", + "pdfFile": "tests/e2e/results/hotfix/pdf-samples/reference-box-*.pdf" + }, + "reportFlag": { + "requiresManualReview": true, + "message": "⚠️ PDF 스타일 수동 확인 필요 - 위 체크리스트 항목을 PDF 파일에서 직접 확인하세요" + } + }, + { + "id": 17, + "name": "문서 상세 모달 - 닫기", + "action": "모달 닫기 버튼 (X) 클릭", + "verification": [ + "모달 닫힘 확인", + "원래 참조함 페이지로 복귀", + "페이지 URL 유지 (/approval/reference)" + ] + }, + { + "id": 18, + "name": "미열람 탭으로 이동", + "action": "'미열람' 탭 클릭", + "verification": [ + "미열람 문서만 표시 확인", + "모든 문서의 상태가 '미열람'" + ] + }, + { + "id": 19, + "name": "열람 처리 - 문서 선택", + "action": "미열람 탭에서 첫 번째 문서 체크박스 선택", + "verification": [ + "체크박스 선택 확인", + "'열람' 버튼 표시 확인" + ] + }, + { + "id": 20, + "name": "열람 처리 - 확인 다이얼로그", + "action": "'열람' 버튼 클릭", + "verification": [ + "확인 다이얼로그 표시", + "다이얼로그 제목: '열람 처리'", + "다이얼로그 메시지: '정말 1건을 열람 처리하시겠습니까?'", + "취소 버튼 표시", + "확인 버튼 표시" + ] + }, + { + "id": 21, + "name": "열람 처리 - URL 안정성 검증 (⚠️ 필수 검증 #2)", + "action": "현재 URL 저장 후 '확인' 버튼 클릭", + "verification": [ + "⚠️ URL 변경 여부 확인 (변경되면 안됨)", + "⚠️ 에러 페이지 텍스트 검색 ('페이지를 찾을 수 없습니다', '404', 'Not Found' 등)", + "⚠️ 원래 페이지 요소 존재 확인 (테이블, 탭 등)", + "성공 토스트 메시지 표시: '열람 처리 완료'", + "토스트 설명: '열람 처리가 완료되었습니다.'", + "다이얼로그 자동 닫힘", + "체크박스 선택 해제", + "미열람 탭의 문서 개수 1개 감소" + ], + "criticalCheck": { + "type": "URL_STABILITY", + "beforeURL": "/approval/reference", + "afterURL": "/approval/reference", + "errorPageTexts": ["페이지를 찾을 수 없습니다", "404", "Not Found", "서버 에러", "500"], + "successToast": "열람 처리 완료" + } + }, + { + "id": 22, + "name": "열람 처리 후 데이터 검증", + "action": "테이블 및 통계 확인", + "verification": [ + "미열람 탭에서 처리된 문서 제거 확인", + "통계 카드의 '미열람' 건수 1개 감소 (실시간 업데이트)", + "통계 카드의 '열람' 건수 1개 증가 (실시간 업데이트)" + ] + }, + { + "id": 23, + "name": "열람 탭으로 이동하여 검증", + "action": "'열람' 탭 클릭", + "verification": [ + "열람 탭에 방금 처리한 문서 표시 확인", + "해당 문서의 상태 배지가 '열람'으로 변경" + ] + }, + { + "id": 24, + "name": "미열람 처리 - 문서 선택", + "action": "열람 탭에서 방금 처리한 문서 체크박스 선택", + "verification": [ + "체크박스 선택 확인", + "'미열람' 버튼 표시 확인" + ] + }, + { + "id": 25, + "name": "미열람 처리 - 확인 다이얼로그", + "action": "'미열람' 버튼 클릭", + "verification": [ + "확인 다이얼로그 표시", + "다이얼로그 제목: '미열람 처리'", + "다이얼로그 메시지: '정말 1건을 미열람 처리하시겠습니까?'", + "취소 버튼 표시", + "확인 버튼 표시" + ] + }, + { + "id": 26, + "name": "미열람 처리 - URL 안정성 검증 (⚠️ 필수 검증 #2)", + "action": "현재 URL 저장 후 '확인' 버튼 클릭", + "verification": [ + "⚠️ URL 변경 여부 확인 (변경되면 안됨)", + "⚠️ 에러 페이지 텍스트 검색", + "성공 토스트 메시지 표시: '미열람 처리 완료'", + "토스트 설명: '미열람 처리가 완료되었습니다.'", + "다이얼로그 자동 닫힘", + "체크박스 선택 해제", + "열람 탭의 문서 개수 1개 감소" + ], + "criticalCheck": { + "type": "URL_STABILITY", + "beforeURL": "/approval/reference", + "afterURL": "/approval/reference", + "errorPageTexts": ["페이지를 찾을 수 없습니다", "404", "Not Found", "서버 에러", "500"], + "successToast": "미열람 처리 완료" + } + }, + { + "id": 27, + "name": "미열람 처리 후 데이터 검증", + "action": "테이블 및 통계 확인", + "verification": [ + "열람 탭에서 처리된 문서 제거 확인", + "통계 카드의 '열람' 건수 1개 감소 (실시간 업데이트)", + "통계 카드의 '미열람' 건수 1개 증가 (실시간 업데이트)" + ] + }, + { + "id": 28, + "name": "일괄 열람 처리 - 다중 선택", + "action": "'미열람' 탭으로 이동 후 2개 문서 체크박스 선택", + "verification": [ + "2개 문서 선택 확인", + "'열람' 버튼 표시 확인" + ] + }, + { + "id": 29, + "name": "일괄 열람 처리 - 실행", + "action": "'열람' 버튼 클릭 후 확인", + "verification": [ + "다이얼로그 메시지: '정말 2건을 열람 처리하시겠습니까?'", + "확인 버튼 클릭", + "⚠️ URL 안정성 확인", + "성공 토스트 메시지 표시", + "미열람 탭에서 2개 문서 제거", + "통계 카드 업데이트 (미열람 -2, 열람 +2)" + ] + }, + { + "id": 30, + "name": "날짜 범위 선택기 테스트", + "action": "날짜 범위 선택기의 '당월' 버튼 클릭", + "verification": [ + "시작일과 종료일이 당월로 변경", + "데이터 재로드 확인 (로딩 인디케이터 또는 데이터 변화)" + ] + }, + { + "id": 31, + "name": "페이지네이션 테스트", + "action": "문서가 20개 이상인 경우 2페이지로 이동", + "verification": [ + "페이지네이션 컨트롤 표시 확인", + "2페이지 버튼 클릭", + "페이지 번호 변경 확인", + "새로운 데이터 로드 확인" + ], + "conditional": "문서 개수가 20개 이상인 경우에만 실행" + }, + { + "id": 32, + "name": "Console 로그 확인", + "action": "브라우저 콘솔 로그 확인", + "verification": [ + "ERROR 로그 없는지 확인", + "WARNING 로그 확인 (접근성 경고 등)", + "Network 요청 확인 (GET /api/v1/approvals/reference)" + ] + }, + { + "id": 33, + "name": "최종 통계 확인", + "action": "'전체' 탭으로 이동하여 최종 상태 확인", + "verification": [ + "전체 문서 목록 표시", + "통계 카드 수치 정확성 확인", + "전체 = 열람 + 미열람 수식 성립", + "스크린샷 촬영하여 최종 상태 저장" + ] + } + ], + "mandatoryVerifications": [ + { + "id": "VERIFICATION_2", + "name": "등록/저장 동작 검증 (URL 안정성)", + "appliesTo": ["Step 21", "Step 26", "Step 29"], + "requirements": [ + "URL 변경 여부 확인 (처리 전 URL과 처리 후 URL 비교)", + "에러 페이지 텍스트 검색 ('페이지를 찾을 수 없습니다', '404', 'Not Found', '서버 에러', '500')", + "원래 페이지 요소 존재 확인 (테이블, 탭, 통계 카드 등)", + "성공 토스트 메시지 표시 확인", + "다이얼로그 자동 닫힘 확인" + ] + }, + { + "id": "VERIFICATION_5", + "name": "목업/미완성 페이지 감지", + "appliesTo": ["Step 1", "Step 16"], + "requirements": [ + "입력 필드 존재 여부 확인 (검색창 등)", + "동작하는 버튼 확인 (최소 2개 버튼 클릭 테스트)", + "API 호출 확인 (Console LOG만 아닌 실제 Network Request)", + "데이터 변경 가능 여부 확인 (열람/미열람 처리 등)", + "목업 판정 기준: 2개 이상 항목 해당 시 목업으로 판정" + ] + } + ], + "bugReportTemplate": { + "priority": "Critical/High/Medium/Low", + "component": "ReferenceBox", + "affectedArea": "react", + "relatedFiles": [ + "C:\\Users\\codeb\\react\\src\\components\\approval\\ReferenceBox\\index.tsx", + "C:\\Users\\codeb\\react\\src\\components\\approval\\ReferenceBox\\actions.ts", + "C:\\Users\\codeb\\react\\src\\components\\approval\\ReferenceBox\\types.ts" + ], + "changeApprovalPolicy": "✅ 즉시 가능 / ⚠️ 컨펌 필요 / 🔴 금지" + }, + "testData": { + "searchKeyword": "김철수", + "dateRange": { + "startDate": "2025-01-01", + "endDate": "2026-01-31" + } + }, + "notes": [ + "참조함은 열람/미열람 상태 관리가 핵심 기능입니다.", + "결재함과 달리 승인/반려 기능은 없고, 열람/미열람 처리만 가능합니다.", + "통계 카드의 실시간 업데이트 확인이 중요합니다.", + "URL 안정성 검증(필수 검증 #2)을 모든 처리 동작에서 수행해야 합니다.", + "문서 상세 모달은 읽기 전용(mode='reference')으로 표시됩니다." + ] +} diff --git a/salary-management.json b/salary-management.json new file mode 100644 index 0000000..e1e46ae --- /dev/null +++ b/salary-management.json @@ -0,0 +1,399 @@ +{ + "id": "salary-management", + "name": "급여관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "급여 현황 조회, 상태 변경, 엑셀 다운로드 기능을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/hr/salary-management", + "menuNavigation": { + "level1": "인사관리", + "level2": "급여관리", + "expectedUrl": "/hr/salary-management" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebar": { + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 5, + "waitAfterScroll": 300 + }, + "level1": { + "text": "인사관리", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "급여관리", + "waitAfterClick": 300 + }, + "fallbackUrl": "/hr/salary-management", + "expectedUrl": "/hr/salary-management" + }, + "timeout": 90000, + "tags": ["hr", "salary", "payroll", "management"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "testData": { + "searchKeyword": "홍", + "dateRange": { + "startDate": "2025-12-01", + "endDate": "2025-12-31" + } + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 500 } + ], + "expect": { + "sidebarReady": true + } + }, + { + "id": "step-1", + "name": "인사관리 메뉴 진입", + "description": "인사관리 > 급여관리 메뉴로 이동 (scrollAndFind 패턴 사용)", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "인사관리", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "인사관리" }, + { "type": "wait", "duration": 500 }, + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "급여관리", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "급여관리" } + ], + "expect": { + "url": "/hr/salary-management", + "visible": ["급여관리", "엑셀 다운로드"] + }, + "fallback": { + "type": "navigate", + "url": "/ko/hr/salary-management" + } + }, + { + "id": "step-2", + "name": "필수 검증 #5: 목업 페이지 감지", + "description": "페이지가 목업인지 실제 동작하는 페이지인지 감지", + "verify": { + "mockupDetection": { + "inputFields": ["검색창", "날짜 필터"], + "functionalButtons": ["엑셀 다운로드", "지급완료", "지급예정", "수정"], + "apiCalls": true, + "dataChangePossible": true + } + } + }, + { + "id": "step-3", + "name": "급여 현황 대시보드 확인", + "description": "총 실지급액, 총 기본급, 총 수당, 초과근무, 상여, 총 공제 카드 표시 확인", + "verify": { + "visible": ["총 실지급액", "총 기본급", "총 수당", "초과근무", "상여", "총 공제"], + "statsCards": true + } + }, + { + "id": "step-4", + "name": "급여 테이블 구조 확인", + "description": "테이블 컬럼 구조 검증", + "verify": { + "tableColumns": ["부서", "직책", "이름", "직급", "기본급", "수당", "초과근무", "상여", "공제", "실지급액", "일자", "상태", "작업"] + } + }, + { + "id": "step-5", + "name": "날짜 필터 확인", + "description": "시작일/종료일 날짜 필터 필드 확인", + "verify": { + "dateInputs": 2, + "dateRange": { + "startDate": "2025-12-01", + "endDate": "2025-12-31" + } + } + }, + { + "id": "step-5-1", + "name": "⚠️ 필수 검증: 날짜 필터 검색", + "critical": true, + "description": "날짜 범위 필터를 설정하고 데이터가 필터링되는지 확인", + "actions": [ + { "type": "capture", "variable": "initialRowCount", "selector": "table tbody tr", "extract": "count", "description": "필터 전 행 수 저장" }, + { "type": "fill", "target": "시작일", "value": "{testData.dateRange.startDate}", "description": "시작일 입력" }, + { "type": "fill", "target": "종료일", "value": "{testData.dateRange.endDate}", "description": "종료일 입력" }, + { "type": "wait", "duration": 500, "description": "필터 적용 대기" }, + { "type": "capture", "variable": "filteredRowCount", "selector": "table tbody tr", "extract": "count", "description": "필터 후 행 수 저장" } + ], + "verify": { + "dateFilterApplied": true, + "dataChanged": "initialRowCount may differ from filteredRowCount" + }, + "note": "날짜 필터가 실제로 데이터를 필터링하는지 확인 - 단순 UI 변경만 확인하면 FAIL" + }, + { + "id": "step-6", + "name": "⚠️ 필수 검증: 검색 기능 확인", + "critical": true, + "description": "검색어 입력 후 테이블 데이터가 필터링되는지 확인", + "actions": [ + { "type": "capture", "variable": "beforeSearchCount", "selector": "table tbody tr", "extract": "count", "description": "검색 전 행 수 저장" }, + { "type": "fill", "target": "검색", "value": "{testData.searchKeyword}", "description": "검색어 입력" }, + { "type": "wait", "duration": 500, "description": "검색 결과 대기" }, + { "type": "capture", "variable": "afterSearchCount", "selector": "table tbody tr", "extract": "count", "description": "검색 후 행 수 저장" } + ], + "verify": { + "searchApplied": true, + "tableContains": "{testData.searchKeyword}", + "dataFiltered": "검색 결과에 검색어가 포함된 행만 표시되어야 함" + }, + "expect": { + "searchPlaceholder": "이름, 부서 검색..." + }, + "note": "⚠️ 검색어 입력 후 테이블 데이터가 변경되지 않으면 FAIL" + }, + { + "id": "step-6-1", + "name": "검색 결과 데이터 검증", + "description": "검색 결과의 각 행에 검색어가 포함되어 있는지 확인", + "verify": { + "allRowsContain": "{testData.searchKeyword}", + "verifyMethod": "테이블의 모든 행이 검색어를 포함하는지 확인" + } + }, + { + "id": "step-6-2", + "name": "검색 초기화 확인", + "description": "검색어 삭제 후 전체 목록 복원 확인", + "actions": [ + { "type": "clear", "target": "검색", "description": "검색어 삭제" }, + { "type": "wait", "duration": 500, "description": "목록 복원 대기" } + ], + "verify": { + "dataRestored": true, + "rowCountRestored": "beforeSearchCount와 유사한 행 수로 복원" + } + }, + { + "id": "step-7", + "name": "정렬 옵션 확인", + "description": "정렬 드롭다운 옵션 확인", + "actions": [ + { "type": "click", "target": "정렬", "role": "combobox" } + ], + "verify": { + "options": ["직급순", "이름순", "부서순", "지급일순", "지급액순"] + } + }, + { + "id": "step-8", + "name": "급여 항목 선택", + "description": "체크박스로 급여 항목 선택", + "actions": [ + { "type": "click", "target": "첫번째 행 체크박스" } + ], + "expect": { + "visible": ["지급완료", "지급예정"], + "buttonsEnabled": true + } + }, + { + "id": "step-9", + "name": "필수 검증 #2: 지급완료 버튼 동작 확인", + "description": "지급완료 버튼 클릭 시 실제 상태 변경 확인", + "actions": [ + { "type": "click", "target": "지급완료" } + ], + "expect": { + "urlMaintained": true, + "noErrorPage": true, + "toast": "지급완료 처리되었습니다", + "apiCall": "bulkUpdateSalaryStatus" + } + }, + { + "id": "step-10", + "name": "수정 버튼 클릭 - 상세 다이얼로그 열기", + "description": "급여 항목의 수정 버튼 클릭하여 상세 다이얼로그 열기", + "actions": [ + { "type": "openModal", "target": "수정", "description": "급여 상세 모달 열기" } + ], + "modalConfig": { + "containerSelector": "[role='dialog'], .modal", + "animationDelay": 300, + "waitForSelector": "[role='dialog']" + }, + "expect": { + "modal": "급여 상세", + "visible": ["기본급", "수당", "초과근무", "상여", "공제", "실지급액"] + } + }, + { + "id": "step-11", + "name": "필수 검증 #4: 상세 다이얼로그 저장", + "description": "모달 내 급여 상세 저장 버튼 동작 확인", + "actions": [ + { "type": "clickInModal", "target": "저장", "options": { "waitAfter": 500 } } + ], + "expect": { + "urlMaintained": true, + "noErrorPage": true, + "modalClosed": true, + "toast": "저장되었습니다" + } + }, + { + "id": "step-12", + "name": "상세 다이얼로그 닫기", + "description": "다이얼로그 닫기", + "actions": [ + { "type": "press", "key": "Escape" } + ], + "expect": { + "modalClosed": true + } + }, + { + "id": "step-13", + "name": "필수 검증 #1: 엑셀 다운로드", + "description": "엑셀 다운로드 버튼 클릭 시 실제 다운로드 발생 확인", + "actions": [ + { "type": "click", "target": "엑셀 다운로드" } + ], + "verify": { + "networkRequest": { + "type": "download", + "apiPattern": "/api/export|/api/download|/api/salary" + }, + "downloadEvent": true + }, + "note": "⚠️ Console LOG만 출력되면 FAIL - Network API 호출 필수 확인" + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/hr/salary-management", + "message": "급여관리 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "button:has-text('엑셀 다운로드')", + "message": "엑셀 다운로드 버튼이 표시되어야 함" + }, + { + "type": "tableExists", + "message": "급여 목록 테이블이 표시되어야 함" + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 1, + "name": "파일 다운로드", + "trigger": "엑셀 다운로드 버튼", + "verification": "Network API 호출 + 실제 파일 다운로드 확인", + "failCondition": "Console LOG만 존재, API 미호출" + }, + { + "id": 2, + "name": "등록/저장 버튼", + "trigger": "지급완료, 지급예정, 저장 버튼", + "verification": "URL 유지 + 에러 페이지 없음 + 성공 토스트", + "failCondition": "404/500 에러 페이지 이동" + }, + { + "id": 3, + "name": "검색/필터", + "trigger": "검색창, 날짜 필터, 정렬", + "verification": "데이터 변화 확인", + "failCondition": "필터 적용 후 데이터 무변화" + }, + { + "id": 4, + "name": "모달 등록 완료", + "trigger": "급여 상세 다이얼로그 저장", + "verification": "실제 저장 동작 + 결과 확인", + "failCondition": "열기/닫기만 테스트" + }, + { + "id": 5, + "name": "목업/미완성 페이지 감지", + "trigger": "페이지 로드 시", + "verification": "입력 필드 + 동작하는 버튼 + API 호출 확인", + "failCondition": "버튼만 있고 입력 불가, Console LOG만 출력" + } + ] + }, + + "cleanup": { + "enabled": false, + "description": "조회/상태변경 테스트이므로 cleanup 불필요" + }, + + "notes": { + "testScope": "급여관리 페이지 UI 요소 및 기능 검증", + "features": { + "dashboard": "총 실지급액/기본급/수당/초과근무/상여/공제 현황 카드", + "dateFilter": "시작일~종료일 날짜 범위 필터", + "search": "이름, 부서 검색", + "sort": "직급순/이름순/부서순/지급일순/지급액순 정렬", + "statusChange": "지급완료/지급예정 일괄 상태 변경", + "detailDialog": "급여 상세 조회/수정 다이얼로그", + "export": "엑셀 다운로드" + }, + "tableColumns": { + "부서": "소속 부서", + "직책": "직책명", + "이름": "직원 이름", + "직급": "직급명", + "기본급": "기본급 금액", + "수당": "수당 금액", + "초과근무": "초과근무 수당", + "상여": "상여금", + "공제": "공제 금액", + "실지급액": "실제 지급 금액", + "일자": "지급 일자", + "상태": "지급완료/지급예정", + "작업": "수정 버튼" + }, + "knownIssues": [ + "엑셀 다운로드 기능이 toast.info('준비 중') 상태일 수 있음 - 목업 가능성", + "지급항목 추가 기능이 미구현 상태" + ], + "prerequisites": "로그인된 사용자에게 급여 관리 권한 필요" + } +} diff --git a/sales-management.json b/sales-management.json new file mode 100644 index 0000000..ed892c1 --- /dev/null +++ b/sales-management.json @@ -0,0 +1,684 @@ +{ + "id": "sales-management", + "name": "매출관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "회계관리 > 매출관리 메뉴의 매출등록, 계정과목 저장, 품목 동적 추가, 자동계산 로직 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "회계관리", + "level2": "매출관리", + "expectedUrl": "/ko/accounting/sales" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebarSelector": ".sidebar-scroll, [class*='sidebar'], nav[class*='menu']", + "scrollConfig": { + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + }, + "level1": { + "text": "회계관리", + "selectors": [ + "//span[contains(text(),'회계관리')]", + "//div[contains(@class,'menu')]//span[text()='회계관리']", + "[data-menu='accounting']" + ] + }, + "level2": { + "text": "매출관리", + "selectors": [ + "//a[contains(text(),'매출관리')]", + "//span[contains(text(),'매출관리')]", + "[href*='/accounting/sales']" + ] + }, + "fallbackUrl": "/ko/accounting/sales" + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 0, + "name": "사이드바 초기화", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll, [class*=\"sidebar\"] > div, nav[class*=\"menu\"]')?.scrollTo({top: 0, behavior: 'instant'})" + }, + { + "type": "wait", + "duration": 500 + } + ], + "expected": "사이드바가 최상단으로 스크롤됨" + }, + { + "id": 1, + "name": "로그인", + "action": "login", + "target": "/ko/login", + "expected": "로그인 성공 후 메인 페이지 이동" + }, + { + "id": 2, + "name": "2단계 메뉴 진입: 회계관리 > 매출관리", + "description": "스크롤하며 회계관리 메뉴를 찾아 클릭 후 매출관리 진입", + "navigationPattern": "scrollAndFind", + "actions": [ + { + "type": "scrollAndFind", + "description": "사이드바 스크롤하며 회계관리 메뉴 찾기", + "scrollContainer": ".sidebar-scroll, [class*='sidebar']", + "targetText": "회계관리", + "scrollStep": 200, + "maxAttempts": 10 + }, + { + "type": "click", + "target": "회계관리", + "selectors": [ + "//span[contains(text(),'회계관리')]", + "//div[contains(@class,'menu')]//span[text()='회계관리']" + ] + }, + { + "type": "wait", + "duration": 500 + }, + { + "type": "scrollAndFind", + "description": "확장된 서브메뉴에서 매출관리 찾기", + "scrollContainer": ".sidebar-scroll, [class*='sidebar']", + "targetText": "매출관리", + "scrollStep": 100, + "maxAttempts": 5 + }, + { + "type": "click", + "target": "매출관리", + "selectors": [ + "//a[contains(text(),'매출관리')]", + "//span[contains(text(),'매출관리')]", + "[href*='/accounting/sales']" + ] + }, + { + "type": "wait", + "target": "페이지 로드 완료" + } + ], + "fallback": { + "type": "navigate", + "url": "/ko/accounting/sales" + }, + "expected": { + "url": "/ko/accounting/sales", + "pageTitle": "매출관리", + "elements": ["매출 등록 버튼", "테이블"] + } + }, + { + "id": 3, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "입력 필드 존재 (검색창, 날짜 선택)", + "동작하는 버튼 존재 (매출 등록, 엑셀 다운로드)", + "테이블 데이터 표시", + "API 호출 확인" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 4, + "name": "목록 페이지 - 테이블 구조 확인", + "action": "verify_table", + "checks": [ + "체크박스 컬럼", + "No. 컬럼", + "매출번호 컬럼", + "거래처명 컬럼", + "매출일 컬럼", + "매출유형 컬럼", + "공급가액 컬럼", + "부가세 컬럼", + "합계금액 컬럼", + "계산서 컬럼", + "명세서 컬럼" + ], + "expected": "테이블 컬럼 구조 정상 표시" + }, + { + "id": 5, + "name": "계정과목명 드롭박스 확인", + "action": "verify_elements", + "checks": [ + "계정과목명 라벨 존재", + "드롭박스(Select) 존재", + "저장 버튼 존재" + ], + "expected": "계정과목 선택 UI 정상 표시" + }, + { + "id": 6, + "name": "계정과목명 드롭박스 옵션 확인", + "action": "click_dropdown", + "target": "accountSubject", + "checks": [ + "미설정 옵션", + "제품매출 옵션", + "상품매출 옵션", + "기타매출 옵션" + ], + "expected": "계정과목 옵션 목록 표시" + }, + { + "id": 7, + "name": "체크박스 선택 (계정과목 저장용)", + "action": "click_checkbox", + "target": "first_row", + "expected": "첫 번째 행 체크박스 선택됨" + }, + { + "id": 8, + "name": "계정과목 변경 - 제품매출 선택", + "action": "select_option", + "target": "accountSubject", + "value": "product", + "expected": "계정과목이 '제품매출'로 변경됨" + }, + { + "id": 9, + "name": "필수 검증 #2: 계정과목 저장 버튼 클릭", + "action": "click_button", + "target": "저장", + "checks": [ + "확인 다이얼로그 표시", + "선택된 항목 수 표시", + "계정과목명 표시" + ], + "expected": "저장 확인 다이얼로그 표시" + }, + { + "id": 10, + "name": "저장 확인 다이얼로그 - 확인 클릭", + "action": "confirm_dialog", + "checks": [ + "API 호출 확인 (PUT /api/v1/sales/batch-update-account)", + "성공 토스트 메시지", + "URL 유지 확인" + ], + "expected": "계정과목 저장 성공" + }, + { + "id": "10-1", + "name": "⚠️ 필수 검증: 계정과목명 변경 데이터 반영 확인", + "action": "verify_data_update", + "target": "first_row", + "checks": [ + "선택한 행의 매출유형 컬럼 값 확인", + "변경 전 값과 비교 (예: 기타 매출 → 제품 매출)", + "페이지 새로고침 후에도 변경된 값 유지 확인", + "API 응답과 UI 표시값 일치 확인" + ], + "expected": "선택한 행의 매출유형이 '제품매출'로 실제 변경되어야 함", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!" + }, + { + "id": 11, + "name": "매출 등록 버튼 클릭", + "action": "click_button", + "target": "매출 등록", + "expected": "매출 등록 페이지로 이동 (/ko/accounting/sales?mode=new)" + }, + { + "id": 12, + "name": "매출 등록 페이지 - URL 확인", + "action": "verify_url", + "target": "/ko/accounting/sales?mode=new", + "expected": "매출 등록 페이지 URL 정상" + }, + { + "id": 13, + "name": "매출 등록 페이지 - 기본정보 섹션 확인", + "action": "verify_elements", + "checks": [ + "매출번호 필드 (자동생성, readonly)", + "매출일 필드 (DatePicker)", + "거래처명 드롭박스", + "매출유형 드롭박스" + ], + "expected": "기본정보 섹션 정상 표시" + }, + { + "id": 14, + "name": "매출번호 자동생성 확인", + "action": "verify_field", + "target": "salesNo", + "checks": [ + "값이 자동 생성됨 (예: S-2026-0001)", + "readonly 상태" + ], + "expected": "매출번호 자동생성 확인" + }, + { + "id": 15, + "name": "거래처명 드롭박스 클릭", + "action": "click_dropdown", + "target": "vendorId", + "expected": "거래처 목록 표시" + }, + { + "id": 16, + "name": "거래처명 선택", + "action": "select_option", + "target": "vendorId", + "value": "first_available", + "expected": "거래처가 선택됨" + }, + { + "id": 17, + "name": "매출유형 드롭박스 확인", + "action": "click_dropdown", + "target": "salesType", + "checks": [ + "외상매출 옵션", + "제품매출 옵션", + "상품매출 옵션", + "부품매출 옵션", + "공사매출 옵션", + "임대매출 옵션", + "기타매출 옵션" + ], + "expected": "매출유형 옵션 목록 표시" + }, + { + "id": 18, + "name": "매출유형 선택 - 제품매출", + "action": "select_option", + "target": "salesType", + "value": "product", + "expected": "매출유형이 '제품매출'로 선택됨" + }, + { + "id": 19, + "name": "품목정보 섹션 확인", + "action": "verify_elements", + "checks": [ + "품목 정보 제목", + "품목 추가 버튼 (+)", + "품목 테이블 헤더 (품목명, 수량, 단가, 공급가액, 부가세, 적요)", + "기본 품목 행 1개 존재" + ], + "expected": "품목정보 섹션 정상 표시" + }, + { + "id": 20, + "name": "품목 동적 추가 - 추가 버튼 클릭", + "action": "click_button", + "target": "품목 추가", + "expected": "새로운 품목 행 추가됨" + }, + { + "id": 21, + "name": "품목 행 개수 확인 (2개)", + "action": "verify_row_count", + "target": "items_table", + "expected": "품목 행이 2개로 증가" + }, + { + "id": 22, + "name": "품목 동적 삭제 - 두 번째 행 삭제", + "action": "click_button", + "target": "remove_item_row_2", + "expected": "두 번째 품목 행 삭제됨" + }, + { + "id": 23, + "name": "품목 행 개수 확인 (1개)", + "action": "verify_row_count", + "target": "items_table", + "expected": "품목 행이 1개로 감소" + }, + { + "id": 24, + "name": "품목명 입력", + "action": "type_text", + "target": "items[0].itemName", + "value": "테스트 품목", + "expected": "품목명 입력됨" + }, + { + "id": 25, + "name": "수량 입력", + "action": "type_text", + "target": "items[0].quantity", + "value": "10", + "expected": "수량 입력됨" + }, + { + "id": 26, + "name": "단가 입력", + "action": "type_text", + "target": "items[0].unitPrice", + "value": "50000", + "expected": "단가 입력됨" + }, + { + "id": 27, + "name": "자동계산 검증 - 공급가액", + "action": "verify_calculated_value", + "target": "items[0].supplyAmount", + "formula": "quantity * unitPrice", + "expectedValue": "500000", + "checks": [ + "수량(10) × 단가(50000) = 공급가액(500,000)", + "자동으로 계산되어 표시됨" + ], + "expected": "공급가액이 500,000원으로 자동 계산됨" + }, + { + "id": 28, + "name": "자동계산 검증 - 부가세", + "action": "verify_calculated_value", + "target": "items[0].vat", + "formula": "supplyAmount * 0.1", + "expectedValue": "50000", + "checks": [ + "공급가액(500,000) × 10% = 부가세(50,000)", + "자동으로 계산되어 표시됨" + ], + "expected": "부가세가 50,000원으로 자동 계산됨" + }, + { + "id": 29, + "name": "적요 입력 (선택사항)", + "action": "type_text", + "target": "items[0].note", + "value": "테스트 적요", + "expected": "적요 입력됨" + }, + { + "id": 30, + "name": "세금계산서 발행 Switch 확인", + "action": "verify_elements", + "target": "taxInvoice_section", + "checks": [ + "세금계산서 발행 라벨", + "Switch 버튼 존재", + "기본값 OFF 상태" + ], + "expected": "세금계산서 발행 Switch 정상 표시" + }, + { + "id": 31, + "name": "세금계산서 발행 Switch ON", + "action": "toggle_switch", + "target": "taxInvoiceSwitch", + "value": "on", + "expected": "세금계산서 발행 Switch가 ON으로 변경됨" + }, + { + "id": 32, + "name": "세금계산서 발행 Switch OFF", + "action": "toggle_switch", + "target": "taxInvoiceSwitch", + "value": "off", + "expected": "세금계산서 발행 Switch가 OFF로 변경됨" + }, + { + "id": 33, + "name": "거래명세서 발행 Switch 확인", + "action": "verify_elements", + "target": "transactionStatement_section", + "checks": [ + "거래명세서 발행 라벨", + "Switch 버튼 존재", + "기본값 OFF 상태" + ], + "expected": "거래명세서 발행 Switch 정상 표시" + }, + { + "id": 34, + "name": "거래명세서 발행 Switch ON", + "action": "toggle_switch", + "target": "transactionStatementSwitch", + "value": "on", + "expected": "거래명세서 발행 Switch가 ON으로 변경됨" + }, + { + "id": 35, + "name": "거래명세서 발행 Switch OFF", + "action": "toggle_switch", + "target": "transactionStatementSwitch", + "value": "off", + "expected": "거래명세서 발행 Switch가 OFF로 변경됨" + }, + { + "id": 36, + "name": "합계 금액 확인", + "action": "verify_totals", + "checks": [ + "총 공급가액: 500,000원", + "총 부가세: 50,000원", + "총 합계금액: 550,000원" + ], + "expected": "합계 금액 정상 표시" + }, + { + "id": 37, + "name": "취소 버튼 동작 테스트", + "action": "click_button", + "target": "취소", + "expected": "취소 확인 다이얼로그 또는 목록 페이지로 이동" + }, + { + "id": 38, + "name": "취소 확인 - 목록 페이지 복귀", + "action": "verify_url", + "target": "/ko/accounting/sales", + "expected": "매출 목록 페이지로 복귀" + }, + { + "id": 39, + "name": "다시 매출 등록 페이지 진입", + "action": "click_button", + "target": "매출 등록", + "expected": "매출 등록 페이지로 이동" + }, + { + "id": 40, + "name": "등록 테스트용 데이터 입력 - 거래처 선택", + "action": "select_option", + "target": "vendorId", + "value": "first_available", + "expected": "거래처 선택됨" + }, + { + "id": 41, + "name": "등록 테스트용 데이터 입력 - 매출유형", + "action": "select_option", + "target": "salesType", + "value": "product", + "expected": "매출유형 선택됨" + }, + { + "id": 42, + "name": "등록 테스트용 데이터 입력 - 품목명", + "action": "type_text", + "target": "items[0].itemName", + "value": "E2E 테스트 품목", + "expected": "품목명 입력됨" + }, + { + "id": 43, + "name": "등록 테스트용 데이터 입력 - 수량", + "action": "type_text", + "target": "items[0].quantity", + "value": "5", + "expected": "수량 입력됨" + }, + { + "id": 44, + "name": "등록 테스트용 데이터 입력 - 단가", + "action": "type_text", + "target": "items[0].unitPrice", + "value": "100000", + "expected": "단가 입력됨" + }, + { + "id": 45, + "name": "필수 검증 #2: 등록 버튼 클릭", + "action": "click_button", + "target": "등록", + "checks": [ + "버튼 클릭 전 URL 저장", + "API 호출 확인 (POST /api/v1/sales)", + "에러 페이지 감지", + "성공 토스트 메시지 확인" + ], + "expected": "매출 등록 완료" + }, + { + "id": 46, + "name": "등록 성공 확인 - 토스트 메시지", + "action": "verify_toast", + "target": "매출이 등록되었습니다", + "expected": "성공 토스트 메시지 표시" + }, + { + "id": 47, + "name": "등록 성공 확인 - 목록 페이지 이동", + "action": "verify_url", + "target": "/ko/accounting/sales", + "expected": "매출 목록 페이지로 이동" + }, + { + "id": 48, + "name": "등록된 매출 목록 확인", + "action": "verify_table_data", + "checks": [ + "신규 등록된 매출이 목록에 표시됨", + "품목명: E2E 테스트 품목", + "공급가액: 500,000원" + ], + "expected": "등록된 매출이 목록에 표시됨" + }, + { + "id": 49, + "name": "거래처 미선택 시 유효성 검증 테스트", + "action": "navigate", + "target": "/ko/accounting/sales?mode=new", + "expected": "매출 등록 페이지 이동" + }, + { + "id": 50, + "name": "거래처 미선택 상태에서 등록 시도", + "action": "click_button", + "target": "등록", + "expected": "유효성 검증 실패 - 경고 메시지" + }, + { + "id": 51, + "name": "유효성 검증 메시지 확인", + "action": "verify_toast", + "target": "거래처를 선택해주세요", + "type": "warning", + "expected": "거래처 선택 요청 경고 메시지 표시" + } + ], + "requiredVerifications": [ + { + "id": 1, + "name": "파일 다운로드 (엑셀)", + "steps": [], + "criteria": "이 시나리오에서는 테스트하지 않음 (별도 시나리오)" + }, + { + "id": 2, + "name": "등록/저장 버튼", + "steps": [9, 10, 45, 46, 47], + "criteria": "계정과목 저장 + 매출 등록 시 API 호출 + 성공 토스트 + URL 유지/이동 확인" + }, + { + "id": 3, + "name": "검색/필터", + "steps": [], + "criteria": "이 시나리오에서는 테스트하지 않음 (등록 중심 테스트)" + }, + { + "id": 4, + "name": "모달 등록 완료", + "steps": [9, 10], + "criteria": "계정과목 저장 확인 다이얼로그 → 확인 클릭 → 저장 완료" + }, + { + "id": 6, + "name": "⚠️ 계정과목명 변경 데이터 반영 (필수)", + "steps": ["10-1"], + "criteria": "저장 후 실제 테이블 데이터가 변경되었는지 확인. 토스트만 확인하면 불충분!", + "priority": "critical", + "knownBug": { + "bugId": "BUG-SALES-20260115-001", + "status": "OPEN", + "description": "성공 토스트 표시되나 실제 데이터 미변경" + } + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [3], + "criteria": "입력 필드, 동작 버튼, API 호출 확인" + } + ], + "testData": { + "vendor": "첫 번째 사용 가능한 거래처", + "salesType": "product", + "item": { + "itemName": "E2E 테스트 품목", + "quantity": 5, + "unitPrice": 100000 + }, + "calculatedValues": { + "supplyAmount": 500000, + "vat": 50000, + "totalAmount": 550000 + } + }, + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/sales", + "description": "매출 목록 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/clients", + "description": "거래처 목록 조회 (드롭박스용)" + }, + { + "method": "POST", + "endpoint": "/api/v1/sales", + "description": "매출 등록" + }, + { + "method": "PUT", + "endpoint": "/api/v1/sales/batch-update-account", + "description": "계정과목 일괄 변경" + } + ], + "skipTests": [ + { + "feature": "삭제 기능", + "reason": "사용자 요청에 따라 삭제 테스트 제외" + } + ] +} diff --git a/shipment-management.json b/shipment-management.json new file mode 100644 index 0000000..5b548e2 --- /dev/null +++ b/shipment-management.json @@ -0,0 +1,245 @@ +{ + "id": "shipment-management", + "name": "출하관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "출고관리 > 출하관리 페이지의 출하 등록/조회/수정 기능을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/shipping/shipment", + "menuNavigation": { + "level1": "출고관리", + "level2": "출하관리", + "expectedUrl": "/shipping/shipment" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebar": { + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 5, + "waitAfterScroll": 300 + }, + "level1": { + "text": "출고관리", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "출하관리", + "waitAfterClick": 300 + }, + "fallbackUrl": "/shipping/shipment", + "expectedUrl": "/shipping/shipment" + }, + "timeout": 90000, + "tags": ["shipping", "shipment", "crud"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "testData": { + "shipment": { + "deliveryMethod": "택배", + "note": "E2E 테스트 출하입니다" + } + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 500 } + ] + }, + { + "id": "step-1", + "name": "출고관리 메뉴 진입", + "description": "출고관리 > 출하관리 메뉴로 이동", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "출고관리", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "출고관리" }, + { "type": "wait", "duration": 500 }, + { "type": "click", "target": "출하관리" } + ], + "expect": { + "url": "/shipping/shipment", + "visible": ["출하 목록", "출하 등록"] + }, + "fallback": { + "type": "navigate", + "url": "/shipping/shipment" + } + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "통계 카드와 테이블 구조 확인", + "verify": { + "visible": ["당일 출하", "출고 대기", "배송중", "긴급 출하"], + "tableColumns": ["번호", "출고번호", "로트번호", "출고예정일", "상태", "출하가능", "배송", "발주처", "현장명", "담당", "납기"] + } + }, + { + "id": "step-3", + "name": "필수 검증 #3: 상태 탭 필터 - 출고예정", + "description": "출고예정 탭 클릭하여 필터링 확인", + "actions": [ + { "type": "click", "target": "출고예정", "role": "tab" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "tabActive": "출고예정", + "dataFiltered": true + } + }, + { + "id": "step-4", + "name": "필수 검증 #3: 상태 탭 필터 - 배송완료", + "description": "배송완료 탭 클릭하여 필터링 확인", + "actions": [ + { "type": "click", "target": "배송완료", "role": "tab" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "tabActive": "배송완료", + "dataFiltered": true + } + }, + { + "id": "step-5", + "name": "전체 탭으로 복귀", + "description": "전체 탭 클릭하여 모든 출하 표시", + "actions": [ + { "type": "click", "target": "전체", "role": "tab" }, + { "type": "wait", "duration": 300 } + ], + "expect": { + "tabActive": "전체", + "allDataShown": true + } + }, + { + "id": "step-6", + "name": "필수 검증 #2: 출하 등록 모달/페이지 열기", + "description": "출하 등록 버튼 클릭하여 등록 화면 열기", + "actions": [ + { "type": "click", "target": "출하 등록" } + ], + "expect": { + "pageOrModal": "출하 등록", + "visible": ["수주", "출고예정일", "배송방식"] + } + }, + { + "id": "step-7", + "name": "출하 등록 폼 확인", + "description": "출하 등록 폼 필드 확인", + "verify": { + "formFields": ["수주", "출고예정일", "배송방식", "담당자"] + } + }, + { + "id": "step-8", + "name": "출하 등록 취소", + "description": "취소 버튼 또는 ESC로 등록 취소", + "actions": [ + { "type": "press", "key": "Escape" }, + { "type": "wait", "duration": 300 } + ] + }, + { + "id": "step-9", + "name": "출하 상세 열기", + "description": "출하 항목 클릭하여 상세 보기", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('tbody tr')?.click()" + } + ], + "expect": { + "pageOrModal": "출하 상세", + "visible": ["출고번호", "상태", "발주처", "현장명"] + } + }, + { + "id": "step-10", + "name": "출하 상세 정보 확인", + "description": "출하 상세 정보 표시 확인", + "verify": { + "visible": ["출고번호", "출고예정일", "상태", "배송방식", "발주처", "현장명"] + } + }, + { + "id": "step-11", + "name": "상세 닫기", + "description": "ESC 키로 상세 닫기", + "actions": [ + { "type": "press", "key": "Escape" }, + { "type": "wait", "duration": 300 } + ] + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/shipping/shipment", + "message": "출하관리 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "button:has-text('출하 등록')", + "message": "출하 등록 버튼이 표시되어야 함" + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 2, + "name": "등록/저장 버튼", + "trigger": "출하 등록 버튼", + "verification": "URL 유지 + 에러 페이지 없음 + 성공 토스트", + "failCondition": "404/500 에러 페이지 이동" + }, + { + "id": 3, + "name": "검색/필터", + "trigger": "상태 탭 필터", + "verification": "데이터 변화 확인", + "failCondition": "필터 적용 후 데이터 무변화" + } + ] + }, + + "notes": { + "testScope": "출하 등록 → 조회 → 상태 필터링 테스트", + "statsCards": ["당일 출하", "출고 대기", "배송중", "긴급 출하"], + "statusTabs": ["전체", "출고예정", "출하대기", "배송중", "배송완료"], + "tableColumns": ["번호", "출고번호", "로트번호", "출고예정일", "상태", "출하가능", "배송", "발주처", "현장명", "담당", "납기"], + "workflow": "수주확정 → 출고예정 → 출하대기 → 배송중 → 배송완료", + "prerequisites": "로그인된 사용자, 수주확정된 데이터 존재 시 출하 등록 가능" + } +} diff --git a/subscription-management.json b/subscription-management.json new file mode 100644 index 0000000..c9da87a --- /dev/null +++ b/subscription-management.json @@ -0,0 +1,188 @@ +{ + "id": "subscription-management", + "name": "구독관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "설정 > 구독관리 페이지의 구독 정보 조회 및 자료 내보내기 기능을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/settings/subscription", + "menuNavigation": { + "level1": "설정", + "level2": "구독관리", + "expectedUrl": "/settings/subscription" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebar": { + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 5, + "waitAfterScroll": 300 + }, + "level1": { + "text": "설정", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "구독관리", + "waitAfterClick": 300 + }, + "fallbackUrl": "/settings/subscription", + "expectedUrl": "/settings/subscription" + }, + "timeout": 90000, + "tags": ["settings", "subscription", "read-only"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 500 } + ] + }, + { + "id": "step-1", + "name": "설정 메뉴 진입", + "description": "설정 > 구독관리 메뉴로 이동", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "설정", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "설정" }, + { "type": "wait", "duration": 500 }, + { "type": "click", "target": "구독관리" } + ], + "expect": { + "url": "/settings/subscription", + "visible": ["구독관리", "구독 정보"] + }, + "fallback": { + "type": "navigate", + "url": "/settings/subscription" + } + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "구독 정보 필드 및 버튼 확인", + "verify": { + "visible": ["구독관리", "최근 결제일시", "다음 결제일시", "구독금액", "구독 정보"], + "buttons": ["자료 내보내기", "서비스 해지"] + } + }, + { + "id": "step-3", + "name": "결제 정보 확인", + "description": "결제 관련 정보 표시 확인", + "verify": { + "visible": ["최근 결제일시", "다음 결제일시", "구독금액"] + } + }, + { + "id": "step-4", + "name": "사용량 정보 확인", + "description": "사용자 수, 저장 공간, API 호출 정보 확인", + "verify": { + "visible": ["사용자 수", "저장 공간", "AI API 호출"] + } + }, + { + "id": "step-5", + "name": "필수 검증 #1: 자료 내보내기 버튼 동작", + "description": "자료 내보내기 버튼 클릭하여 다운로드 확인", + "actions": [ + { "type": "click", "target": "자료 내보내기" }, + { "type": "wait", "duration": 1000 } + ], + "expect": { + "downloadTriggered": true, + "noErrorPage": true + }, + "verify": { + "apiCall": "GET /api/settings/subscription/export" + } + }, + { + "id": "step-6", + "name": "서비스 해지 버튼 확인", + "description": "서비스 해지 버튼 존재 확인 (클릭하지 않음)", + "verify": { + "buttonExists": "서비스 해지" + } + }, + { + "id": "step-7", + "name": "구독 플랜 정보 확인", + "description": "현재 구독 플랜 정보 표시 확인", + "verify": { + "visible": ["무료", "무제한"] + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/settings/subscription", + "message": "구독관리 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "button:has-text('자료 내보내기')", + "message": "자료 내보내기 버튼이 표시되어야 함" + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 1, + "name": "파일 다운로드", + "trigger": "자료 내보내기 버튼", + "verification": "Network API + 실제 다운로드 확인", + "failCondition": "Console LOG만으로 PASS 금지" + }, + { + "id": 5, + "name": "목업/미완성 페이지 감지", + "trigger": "페이지 로드 시", + "verification": "실제 구독 정보 표시 + 동작하는 버튼 확인", + "failCondition": "더미 데이터만 표시" + } + ] + }, + + "notes": { + "testScope": "구독 정보 조회 및 자료 내보내기 테스트", + "pageFeatures": { + "paymentInfo": ["최근 결제일시", "다음 결제일시", "구독금액"], + "usageInfo": ["사용자 수", "저장 공간", "AI API 호출"], + "buttons": ["자료 내보내기", "서비스 해지"] + }, + "caution": "서비스 해지 버튼은 테스트 계정 보호를 위해 클릭하지 않음", + "prerequisites": "로그인된 사용자" + } +} diff --git a/vacation-management.json b/vacation-management.json new file mode 100644 index 0000000..85cdd81 --- /dev/null +++ b/vacation-management.json @@ -0,0 +1,533 @@ +{ + "id": "vacation-management", + "name": "휴가관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "휴가 사용현황, 부여현황, 신청현황 탭 기능과 부여등록/휴가신청 다이얼로그를 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/ko/hr/vacation-management", + "menuNavigation": { + "level1": "인사관리", + "level2": "휴가관리", + "expectedUrl": "/ko/hr/vacation-management" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebarSelector": ".sidebar-scroll, [data-sidebar], nav[class*='sidebar']", + "scrollConfig": { + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + }, + "level1": { + "text": "인사관리", + "selectors": [ + "button:has-text('인사관리')", + "[data-menu='hr']", + "a:has-text('인사관리')" + ] + }, + "level2": { + "text": "휴가관리", + "selectors": [ + "a:has-text('휴가관리')", + "[href*='vacation-management']", + "button:has-text('휴가관리')" + ] + }, + "fallbackUrl": "/ko/hr/vacation-management" + }, + "timeout": 120000, + "tags": ["hr", "vacation", "leave", "management"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "testData": { + "searchKeyword": "홍", + "grantData": { + "vacationType": "annual", + "grantDays": 3, + "reason": "E2E 테스트 부여" + }, + "requestData": { + "leaveType": "annual", + "daysRange": 2 + }, + "dateRange": { + "startDate": "2025-12-01", + "endDate": "2025-12-31" + } + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "사이드바 스크롤을 최상단으로 이동하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll, [data-sidebar], nav[class*=\"sidebar\"]')?.scrollTo({top: 0, behavior: 'instant'})" + }, + { "type": "wait", "duration": 500 } + ], + "expect": { + "sidebarReady": true + } + }, + { + "id": "step-1", + "name": "인사관리 메뉴 진입", + "description": "인사관리 > 휴가관리 메뉴로 이동 (scrollAndFind 패턴 사용)", + "navigationPattern": "scrollAndFind", + "actions": [ + { + "type": "scrollAndFind", + "target": "인사관리", + "container": ".sidebar-scroll, [data-sidebar], nav[class*='sidebar']", + "scrollStep": 200, + "maxAttempts": 10 + }, + { "type": "click", "target": "인사관리" }, + { "type": "wait", "duration": 300 }, + { + "type": "scrollAndFind", + "target": "휴가관리", + "container": ".sidebar-scroll, [data-sidebar], nav[class*='sidebar']", + "scrollStep": 200, + "maxAttempts": 10 + }, + { "type": "click", "target": "휴가관리" } + ], + "fallback": { + "type": "navigate", + "url": "/ko/hr/vacation-management" + }, + "expect": { + "url": "/hr/vacation-management", + "visible": ["휴가관리", "휴가 사용현황"] + } + }, + { + "id": "step-2", + "name": "필수 검증 #5: 목업 페이지 감지", + "description": "페이지가 목업인지 실제 동작하는 페이지인지 감지", + "verify": { + "mockupDetection": { + "inputFields": ["검색창", "날짜 필터"], + "functionalButtons": ["부여등록", "휴가신청", "승인", "거절"], + "apiCalls": true, + "dataChangePossible": true + } + } + }, + { + "id": "step-3", + "name": "통계 카드 대시보드 확인", + "description": "휴가 승인 대기, 연차, 경조사, 연간 연차 사용률 카드 표시 확인", + "verify": { + "visible": ["휴가 승인 대기", "연차", "경조사", "연간 연차 사용률"], + "statsCards": true + } + }, + { + "id": "step-4", + "name": "휴가 사용현황 탭 확인 (기본 탭)", + "description": "휴가 사용현황 탭의 테이블 컬럼 구조 검증", + "verify": { + "activeTab": "휴가 사용현황", + "tableColumns": ["번호", "부서", "직책", "이름", "직급", "입사일", "기본", "부여", "사용", "잔여"] + } + }, + { + "id": "step-4-1", + "name": "⚠️ 필수 검증: 날짜 필터 검색", + "critical": true, + "description": "날짜 범위 필터를 설정하고 데이터가 필터링되는지 확인", + "actions": [ + { "type": "capture", "variable": "initialRowCount", "selector": "table tbody tr", "extract": "count", "description": "필터 전 행 수 저장" }, + { "type": "fill", "target": "시작일", "value": "{testData.dateRange.startDate}", "description": "시작일 입력" }, + { "type": "fill", "target": "종료일", "value": "{testData.dateRange.endDate}", "description": "종료일 입력" }, + { "type": "wait", "duration": 500, "description": "필터 적용 대기" }, + { "type": "capture", "variable": "filteredRowCount", "selector": "table tbody tr", "extract": "count", "description": "필터 후 행 수 저장" } + ], + "verify": { + "dateFilterApplied": true, + "dataChanged": "필터 적용 후 데이터가 변경되어야 함" + }, + "note": "날짜 필터가 실제로 데이터를 필터링하는지 확인" + }, + { + "id": "step-5", + "name": "⚠️ 필수 검증: 검색 기능 확인 (사용현황)", + "critical": true, + "description": "검색어 입력 후 테이블 데이터가 필터링되는지 확인", + "actions": [ + { "type": "capture", "variable": "beforeSearchCount", "selector": "table tbody tr", "extract": "count", "description": "검색 전 행 수 저장" }, + { "type": "fill", "target": "검색", "value": "{testData.searchKeyword}", "description": "검색어 입력" }, + { "type": "wait", "duration": 500, "description": "검색 결과 대기" }, + { "type": "capture", "variable": "afterSearchCount", "selector": "table tbody tr", "extract": "count", "description": "검색 후 행 수 저장" } + ], + "verify": { + "searchApplied": true, + "tableContains": "{testData.searchKeyword}", + "dataFiltered": "검색 결과에 검색어가 포함된 행만 표시" + }, + "expect": { + "searchPlaceholder": "이름, 부서 검색..." + }, + "note": "⚠️ 검색어 입력 후 테이블에 검색어가 포함된 행만 표시되어야 함" + }, + { + "id": "step-5-1", + "name": "검색 결과 데이터 검증", + "description": "검색 결과의 각 행에 검색어가 포함되어 있는지 확인", + "verify": { + "allRowsContain": "{testData.searchKeyword}", + "verifyMethod": "테이블의 모든 행이 검색어를 포함하는지 확인" + } + }, + { + "id": "step-5-2", + "name": "검색 초기화 확인", + "description": "검색어 삭제 후 전체 목록 복원 확인", + "actions": [ + { "type": "clear", "target": "검색", "description": "검색어 삭제" }, + { "type": "wait", "duration": 500, "description": "목록 복원 대기" } + ], + "verify": { + "dataRestored": true + } + }, + { + "id": "step-6", + "name": "휴가 부여현황 탭 전환", + "description": "휴가 부여현황 탭 클릭 및 테이블 구조 확인", + "actions": [ + { "type": "click", "target": "휴가 부여현황" } + ], + "verify": { + "activeTab": "휴가 부여현황", + "tableColumns": ["번호", "부서", "직책", "이름", "직급", "유형", "부여일", "부여휴가일수", "사유"], + "visible": ["부여등록"] + } + }, + { + "id": "step-7", + "name": "부여등록 다이얼로그 열기", + "description": "부여등록 버튼 클릭하여 다이얼로그 열기", + "actions": [ + { "type": "openModal", "target": "부여등록", "description": "휴가 부여 등록 모달 열기" } + ], + "modalConfig": { + "containerSelector": "[role='dialog'], .modal", + "animationDelay": 300, + "waitForSelector": "[role='dialog']" + }, + "expect": { + "modal": "휴가 부여 등록", + "visible": ["사원 선택", "휴가 유형", "부여일", "부여 일수", "사유"] + } + }, + { + "id": "step-8", + "name": "부여등록 다이얼로그 입력 필드 확인", + "description": "다이얼로그 내 입력 필드들이 정상 동작하는지 확인", + "actions": [ + { "type": "clickInModal", "target": "사원 선택", "options": { "waitAfter": 200 } } + ], + "verify": { + "comboboxOptions": true, + "inputFields": ["사원 선택", "휴가 유형", "부여일", "부여 일수", "사유"] + } + }, + { + "id": "step-9", + "name": "필수 검증 #4: 부여등록 저장", + "description": "모달 내 부여등록 실제 등록 수행", + "actions": [ + { "type": "selectInModal", "target": "사원 선택", "value": "첫번째 사원", "options": { "waitAfter": 200 } }, + { "type": "selectInModal", "target": "휴가 유형", "value": "연차", "options": { "waitAfter": 200 } }, + { "type": "fillInModal", "target": "부여 일수", "value": "3", "options": { "waitAfter": 100 } }, + { "type": "fillInModal", "target": "사유", "value": "E2E 테스트 부여", "options": { "waitAfter": 100 } }, + { "type": "clickInModal", "target": "등록", "options": { "waitAfter": 500 } } + ], + "expect": { + "urlMaintained": true, + "noErrorPage": true, + "modalClosed": true, + "apiCall": "createLeaveGrant" + } + }, + { + "id": "step-10", + "name": "부여등록 다이얼로그 취소 테스트", + "description": "다이얼로그 취소 버튼 동작 확인", + "actions": [ + { "type": "click", "target": "부여등록" }, + { "type": "click", "target": "취소" } + ], + "expect": { + "modalClosed": true + } + }, + { + "id": "step-11", + "name": "휴가 신청현황 탭 전환", + "description": "휴가 신청현황 탭 클릭 및 테이블 구조 확인", + "actions": [ + { "type": "click", "target": "휴가 신청현황" } + ], + "verify": { + "activeTab": "휴가 신청현황", + "tableColumns": ["번호", "부서", "직책", "이름", "직급", "휴가기간", "휴가일수", "상태", "신청일"], + "visible": ["휴가신청"] + } + }, + { + "id": "step-12", + "name": "휴가신청 다이얼로그 열기", + "description": "휴가신청 버튼 클릭하여 다이얼로그 열기", + "actions": [ + { "type": "openModal", "target": "휴가신청", "description": "휴가 신청 모달 열기" } + ], + "modalConfig": { + "containerSelector": "[role='dialog'], .modal", + "animationDelay": 300, + "waitForSelector": "[role='dialog']" + }, + "expect": { + "modal": "휴가 신청", + "visible": ["사원 선택", "휴가 유형", "시작일", "종료일"] + } + }, + { + "id": "step-13", + "name": "휴가신청 다이얼로그 입력 필드 확인", + "description": "다이얼로그 내 입력 필드들이 정상 동작하는지 확인 (캘린더 포함)", + "actions": [ + { "type": "clickInModal", "target": "사원 선택", "options": { "waitAfter": 200 } } + ], + "verify": { + "comboboxOptions": true, + "inputFields": ["사원 선택", "휴가 유형", "시작일", "종료일"] + } + }, + { + "id": "step-14", + "name": "필수 검증 #4: 휴가신청 등록", + "description": "모달 내 휴가신청 실제 등록 수행", + "actions": [ + { "type": "selectInModal", "target": "사원 선택", "value": "첫번째 사원", "options": { "waitAfter": 200 } }, + { "type": "selectInModal", "target": "휴가 유형", "value": "연차", "options": { "waitAfter": 200 } }, + { "type": "clickInModal", "target": "시작일 선택", "options": { "waitAfter": 200 } }, + { "type": "clickInModal", "target": "캘린더 날짜 선택", "options": { "waitAfter": 200 } }, + { "type": "clickInModal", "target": "종료일 선택", "options": { "waitAfter": 200 } }, + { "type": "clickInModal", "target": "캘린더 날짜 선택", "options": { "waitAfter": 200 } }, + { "type": "clickInModal", "target": "신청", "options": { "waitAfter": 500 } } + ], + "expect": { + "urlMaintained": true, + "noErrorPage": true, + "modalClosed": true, + "apiCall": "createLeave" + } + }, + { + "id": "step-15", + "name": "휴가신청 다이얼로그 취소 테스트", + "description": "다이얼로그 취소 버튼 동작 확인", + "actions": [ + { "type": "click", "target": "휴가신청" }, + { "type": "click", "target": "취소" } + ], + "expect": { + "modalClosed": true + } + }, + { + "id": "step-16", + "name": "필수 검증 #2: 휴가 승인 버튼 동작", + "description": "신청현황에서 체크박스 선택 후 승인 버튼 동작 확인", + "actions": [ + { "type": "click", "target": "첫번째 행 체크박스" }, + { "type": "click", "target": "승인" } + ], + "expect": { + "modal": "휴가 승인", + "visible": ["승인하시겠습니까"] + } + }, + { + "id": "step-17", + "name": "승인 확인 다이얼로그 동작", + "description": "승인 확인 다이얼로그에서 승인 버튼 클릭", + "actions": [ + { "type": "click", "target": "승인", "context": "dialog" } + ], + "expect": { + "urlMaintained": true, + "noErrorPage": true, + "modalClosed": true, + "apiCall": "approveLeavesMany" + } + }, + { + "id": "step-18", + "name": "필수 검증 #2: 휴가 거절 버튼 동작", + "description": "신청현황에서 체크박스 선택 후 거절 버튼 동작 확인", + "actions": [ + { "type": "click", "target": "첫번째 행 체크박스" }, + { "type": "click", "target": "거절" } + ], + "expect": { + "modal": "휴가 거절", + "visible": ["거절하시겠습니까"] + } + }, + { + "id": "step-19", + "name": "거절 확인 다이얼로그 취소", + "description": "거절 확인 다이얼로그에서 취소 버튼 클릭", + "actions": [ + { "type": "click", "target": "취소", "context": "dialog" } + ], + "expect": { + "modalClosed": true + } + }, + { + "id": "step-20", + "name": "필터 및 정렬 셀렉트 동작 확인", + "description": "필터 및 정렬 셀렉트박스가 정상 동작하는지 확인", + "actions": [ + { "type": "click", "target": "필터 선택 콤보박스" } + ], + "verify": { + "comboboxOptions": ["전체", "대기중", "승인됨", "거절됨"] + } + }, + { + "id": "step-21", + "name": "날짜 범위 필터 확인", + "description": "시작일/종료일 날짜 필터 필드 동작 확인", + "verify": { + "dateInputs": 2, + "dateRange": { + "startDate": "2025-12-01", + "endDate": "2025-12-31" + } + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/hr/vacation-management", + "message": "휴가관리 페이지에 머물러야 함" + }, + { + "type": "tabsExist", + "expected": ["휴가 사용현황", "휴가 부여현황", "휴가 신청현황"], + "message": "3개의 탭이 모두 표시되어야 함" + }, + { + "type": "tableExists", + "message": "휴가 목록 테이블이 표시되어야 함" + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 2, + "name": "등록/저장 버튼", + "trigger": "부여등록, 휴가신청, 승인, 거절 버튼", + "verification": "URL 유지 + 에러 페이지 없음 + 모달 닫힘", + "failCondition": "404/500 에러 페이지 이동" + }, + { + "id": 3, + "name": "검색/필터", + "trigger": "검색창, 날짜 필터, 탭 전환", + "verification": "데이터 변화 확인", + "failCondition": "필터 적용 후 데이터 무변화" + }, + { + "id": 4, + "name": "모달 등록 완료", + "trigger": "부여등록/휴가신청 다이얼로그", + "verification": "실제 등록 동작 + API 호출 확인", + "failCondition": "열기/닫기만 테스트" + }, + { + "id": 5, + "name": "목업/미완성 페이지 감지", + "trigger": "페이지 로드 시", + "verification": "입력 필드 + 동작하는 버튼 + API 호출 확인", + "failCondition": "버튼만 있고 입력 불가, Console LOG만 출력" + } + ] + }, + + "cleanup": { + "enabled": false, + "description": "테스트 데이터는 수동 정리 필요" + }, + + "notes": { + "testScope": "휴가관리 페이지 UI 요소 및 기능 검증", + "features": { + "tabs": "휴가 사용현황 / 휴가 부여현황 / 휴가 신청현황", + "statsCards": "휴가 승인 대기 / 연차 / 경조사 / 연간 연차 사용률", + "grantDialog": "부여등록 다이얼로그 (사원 선택, 휴가 유형, 부여일, 부여 일수, 사유)", + "requestDialog": "휴가신청 다이얼로그 (사원 선택, 휴가 유형, 시작일, 종료일, 캘린더)", + "approveReject": "신청현황 탭에서 승인/거절 버튼", + "dateFilter": "시작일~종료일 날짜 범위 필터", + "search": "이름, 부서 검색", + "filterSort": "필터 및 정렬 셀렉트박스" + }, + "tableColumns": { + "usage": ["번호", "부서", "직책", "이름", "직급", "입사일", "기본", "부여", "사용", "잔여"], + "grant": ["번호", "부서", "직책", "이름", "직급", "유형", "부여일", "부여휴가일수", "사유"], + "request": ["번호", "부서", "직책", "이름", "직급", "휴가기간", "휴가일수", "상태", "신청일"] + }, + "dialogs": { + "grantDialog": { + "title": "휴가 부여 등록", + "fields": ["사원 선택", "휴가 유형", "부여일", "부여 일수", "사유"], + "buttons": ["취소", "등록"] + }, + "requestDialog": { + "title": "휴가 신청", + "fields": ["사원 선택", "휴가 유형", "시작일", "종료일"], + "buttons": ["취소", "신청"], + "note": "캘린더 컴포넌트 사용" + }, + "approveDialog": { + "title": "휴가 승인", + "buttons": ["취소", "승인"] + }, + "rejectDialog": { + "title": "휴가 거절", + "buttons": ["취소", "거절"] + } + }, + "knownIssues": [ + "엑셀 다운로드 버튼이 주석처리 되어 있음 (미구현)", + "휴가 유형 옵션: annual(연차), sick(병가), personal(개인사유), condolence(경조사), maternity(출산휴가), reward(포상휴가)" + ], + "prerequisites": "로그인된 사용자에게 휴가 관리 권한 필요" + } +} diff --git a/vendor-ledger.json b/vendor-ledger.json new file mode 100644 index 0000000..51481b5 --- /dev/null +++ b/vendor-ledger.json @@ -0,0 +1,675 @@ +{ + "id": "vendor-ledger", + "name": "거래처원장 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "회계관리 > 거래처원장 메뉴의 기간 설정, 검색, 테이블, 다운로드, 상세 페이지 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "회계관리", + "level2": "거래처원장", + "expectedUrl": "/ko/accounting/vendor-ledger" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "level1": { + "text": "회계관리", + "alternativeNames": ["회계", "Accounting"], + "scrollConfig": { + "direction": "down", + "maxScrollAttempts": 5, + "scrollAmount": 200 + } + }, + "level2": { + "text": "거래처원장", + "alternativeNames": ["거래처 원장", "Vendor Ledger"], + "scrollConfig": { + "direction": "down", + "maxScrollAttempts": 3, + "scrollAmount": 150 + } + }, + "expectedUrl": "/ko/accounting/vendor-ledger", + "fallbackUrl": "/ko/accounting/vendor-ledger" + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "steps": [ + { + "id": 0, + "name": "사이드바 준비", + "description": "메뉴 탐색 전 사이드바를 최상단으로 스크롤", + "actions": [ + { + "type": "scrollSidebarToTop", + "script": "document.querySelector('.sidebar-scroll, [class*=\"sidebar\"], nav')?.scrollTo({top:0,behavior:'smooth'})" + }, + { + "type": "wait", + "duration": 300 + } + ], + "expected": "사이드바 최상단으로 스크롤 완료" + }, + { + "id": 1, + "name": "로그인", + "action": "login", + "target": "/ko/login", + "expected": "로그인 성공 후 메인 페이지 이동" + }, + { + "id": 2, + "name": "2단계 메뉴 진입: 회계관리 > 거래처원장", + "description": "회계관리 > 거래처원장 메뉴로 이동하여 페이지 로드 확인 (scrollAndFind 패턴 사용)", + "actions": [ + { + "type": "scrollAndFind", + "level": 1, + "target": "회계관리", + "alternativeSelectors": [ + "text=회계관리", + "[data-menu='accounting']", + "a:has-text('회계관리')", + "span:has-text('회계관리')" + ], + "scrollConfig": { + "container": ".sidebar-scroll, [class*='sidebar'], nav", + "direction": "down", + "maxAttempts": 5, + "scrollAmount": 200 + } + }, + { + "type": "click", + "target": "회계관리" + }, + { + "type": "wait", + "duration": 500 + }, + { + "type": "scrollAndFind", + "level": 2, + "target": "거래처원장", + "alternativeSelectors": [ + "text=거래처원장", + "[data-menu='vendor-ledger']", + "a:has-text('거래처원장')", + "span:has-text('거래처원장')" + ], + "scrollConfig": { + "container": ".sidebar-scroll, [class*='sidebar'], nav", + "direction": "down", + "maxAttempts": 3, + "scrollAmount": 150 + } + }, + { + "type": "click", + "target": "거래처원장" + }, + { + "type": "wait", + "target": "페이지 로드 완료" + } + ], + "expected": { + "url": "/ko/accounting/vendor-ledger", + "pageTitle": "거래처원장", + "elements": ["통계 카드", "테이블"] + } + }, + { + "id": 3, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "입력 필드 존재 (검색창, 날짜 선택)", + "동작하는 버튼 존재 (엑셀 다운로드)", + "테이블 데이터 표시", + "API 호출 확인" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 4, + "name": "통계 카드 확인", + "action": "verify_elements", + "checks": [ + "전기 이월 카드 표시", + "매출 카드 표시", + "수금 카드 표시", + "잔액 카드 표시" + ], + "expected": "4개 통계 카드 모두 표시, 금액 형식 확인" + }, + { + "id": 5, + "name": "테이블 구조 확인", + "action": "verify_table", + "checks": [ + "체크박스 컬럼", + "No. 컬럼", + "거래처명 컬럼", + "이월잔액 컬럼", + "매출 컬럼", + "수금 컬럼", + "잔액 컬럼", + "결제일 컬럼" + ], + "expected": "8개 컬럼 존재, 합계 행 표시" + }, + { + "id": 6, + "name": "기간 설정 - 시작일 변경", + "action": "change_date", + "target": "startDate", + "value": "2025-01-01", + "expected": "시작일 변경 후 데이터 재조회" + }, + { + "id": 7, + "name": "기간 설정 - 종료일 변경", + "action": "change_date", + "target": "endDate", + "value": "2025-12-31", + "expected": "종료일 변경 후 데이터 재조회" + }, + { + "id": 8, + "name": "기간 설정 - 데이터 변화 확인", + "action": "verify_data_change", + "checks": [ + "테이블 데이터 갱신", + "통계 카드 값 갱신", + "합계 행 값 갱신" + ], + "expected": "기간에 맞는 데이터로 변경됨" + }, + { + "id": 9, + "name": "⚠️ 필수 검증: 검색 기능 테스트", + "critical": true, + "actions": [ + { + "type": "capture", + "variable": "beforeSearchCount", + "selector": "table tbody tr", + "extract": "count", + "description": "검색 전 행 수 저장" + }, + { + "type": "fill", + "target": "searchInput", + "value": "{testData.searchKeyword}", + "description": "검색어 입력" + }, + { + "type": "wait", + "duration": 1000, + "description": "검색 결과 로딩 대기" + }, + { + "type": "capture", + "variable": "afterSearchCount", + "selector": "table tbody tr", + "extract": "count", + "description": "검색 후 행 수 저장" + } + ], + "verify": { + "searchApplied": true, + "tableContains": "{testData.searchKeyword}", + "dataChanged": "beforeSearchCount may differ from afterSearchCount" + }, + "expected": "검색어에 맞는 거래처만 필터링" + }, + { + "id": "9-1", + "name": "검색 결과 데이터 검증", + "critical": true, + "description": "검색 결과의 모든 행이 검색어를 포함하는지 확인", + "verify": { + "allRowsContain": "{testData.searchKeyword}", + "columnToCheck": "거래처명" + } + }, + { + "id": 10, + "name": "검색 결과 확인", + "action": "verify_search_result", + "checks": [ + "테이블 행 수 변화", + "검색어 포함 거래처명 표시" + ], + "expected": "검색 결과 정상 표시" + }, + { + "id": 11, + "name": "검색 초기화", + "actions": [ + { + "type": "clear", + "target": "searchInput" + }, + { + "type": "wait", + "duration": 500 + }, + { + "type": "capture", + "variable": "afterClearCount", + "selector": "table tbody tr", + "extract": "count" + } + ], + "verify": { + "dataRestored": "afterClearCount should equal beforeSearchCount" + }, + "expected": "전체 데이터 다시 표시" + }, + { + "id": 12, + "name": "체크박스 선택", + "action": "click_checkbox", + "target": "first_row", + "expected": "첫 번째 행 체크박스 선택됨" + }, + { + "id": 13, + "name": "전체 선택 체크박스", + "action": "click_checkbox", + "target": "select_all", + "expected": "모든 행 체크박스 선택됨" + }, + { + "id": 14, + "name": "전체 선택 해제", + "action": "click_checkbox", + "target": "select_all", + "expected": "모든 행 체크박스 해제됨" + }, + { + "id": 15, + "name": "필수 검증 #1: 엑셀 다운로드", + "action": "click_download", + "target": "엑셀 다운로드", + "checks": [ + "버튼 클릭", + "Network API 호출 확인 (/api/v1/vendor-ledger/export)", + "Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "파일 다운로드 이벤트 발생", + "성공 토스트 메시지 (엑셀 파일이 다운로드되었습니다)" + ], + "expected": "엑셀 파일 다운로드 완료" + }, + { + "id": 16, + "name": "테이블 행 클릭 - 상세 페이지 이동", + "action": "click_row", + "target": "first_row", + "expected": "거래처원장 상세 페이지로 이동" + }, + { + "id": 17, + "name": "상세 페이지 - URL 파라미터 확인", + "action": "verify_url", + "checks": [ + "URL에 거래처 ID 포함", + "start_date 파라미터 포함", + "end_date 파라미터 포함" + ], + "expected": "URL 파라미터 정상 전달" + }, + { + "id": 18, + "name": "상세 페이지 - 헤더 확인", + "action": "verify_elements", + "checks": [ + "거래처원장 상세 (거래명세서별) 타이틀", + "목록 버튼 존재" + ], + "expected": "상세 페이지 헤더 정상 표시" + }, + { + "id": 19, + "name": "상세 페이지 - 거래처 정보 카드 확인", + "action": "verify_vendor_info", + "checks": [ + "회사명 표시", + "사업자등록번호 표시", + "대표자 표시", + "전화번호 표시", + "모바일 표시", + "팩스 표시", + "이메일 표시", + "주소 표시", + "기간 표시" + ], + "expected": "거래처 정보 모두 표시" + }, + { + "id": 20, + "name": "상세 페이지 - 요약 통계 확인", + "action": "verify_summary", + "checks": [ + "이월잔액 표시", + "매출 표시 (녹색)", + "수금 표시 (파란색)", + "잔액 표시" + ], + "expected": "4개 요약 통계 정상 표시" + }, + { + "id": 21, + "name": "상세 페이지 - 판매/수금 내역 테이블 확인", + "action": "verify_transaction_table", + "checks": [ + "일자 컬럼", + "적요 컬럼", + "판매 컬럼", + "수금 컬럼", + "잔액 컬럼", + "작업 컬럼" + ], + "expected": "판매/수금 내역 테이블 정상 표시" + }, + { + "id": 22, + "name": "상세 페이지 - 기간 변경", + "action": "change_date_range", + "startDate": "2025-06-01", + "endDate": "2025-06-30", + "expected": "기간 변경 후 거래 내역 재조회" + }, + { + "id": 23, + "name": "상세 페이지 - 거래 내역 데이터 변화 확인", + "action": "verify_transactions_update", + "checks": [ + "테이블 데이터 갱신", + "요약 통계 값 갱신" + ], + "expected": "변경된 기간의 데이터 표시" + }, + { + "id": 24, + "name": "⚠️ 필수 검증: PDF 다운로드 전 페이지 스크린샷", + "critical": true, + "description": "PDF 생성 전 페이지 상태를 스크린샷으로 캡처하여 CSS 문제 감지용 기준 이미지 확보", + "actions": [ + { + "type": "screenshot", + "name": "pdf-preview-before-download", + "fullPage": true, + "savePath": "tests/e2e/results/hotfix/screenshots/", + "description": "PDF 생성 대상 페이지 전체 캡처" + }, + { + "type": "screenshot", + "name": "pdf-content-area", + "selector": ".vendor-ledger-detail, .pdf-content, main", + "savePath": "tests/e2e/results/hotfix/screenshots/", + "description": "PDF 콘텐츠 영역만 캡처" + } + ], + "verify": { + "screenshotCaptured": true, + "purpose": "PDF CSS 문제 감지를 위한 기준 이미지" + } + }, + { + "id": "24-1", + "name": "⚠️ 필수 검증: PDF 다운로드 실행 및 파일 보관", + "critical": true, + "description": "PDF 다운로드 후 파일을 지정 폴더에 보관하여 수동 검증 가능하게 함", + "actions": [ + { + "type": "expectResponse", + "id": "pdf-download-response", + "urlPattern": "/api/v1/vendor-ledger/*/export-pdf", + "description": "PDF 다운로드 API 응답 대기 설정" + }, + { + "type": "click", + "target": "PDF 다운로드", + "description": "PDF 다운로드 버튼 클릭" + }, + { + "type": "wait", + "duration": 3000, + "description": "PDF 생성 및 다운로드 대기" + }, + { + "type": "assertResponse", + "id": "pdf-download-response", + "checks": { + "status": 200, + "contentType": "application/pdf" + } + }, + { + "type": "saveDownloadedFile", + "targetPath": "tests/e2e/results/hotfix/pdf-samples/", + "fileNamePattern": "vendor-ledger-{vendorId}-{timestamp}.pdf", + "description": "다운로드된 PDF 파일을 지정 폴더에 보관" + } + ], + "verify": { + "apiSuccess": true, + "fileDownloaded": true, + "fileSaved": "tests/e2e/results/hotfix/pdf-samples/" + } + }, + { + "id": "24-2", + "name": "⚠️ PDF 파일 유효성 검증", + "critical": true, + "description": "다운로드된 PDF 파일의 기본 유효성 검사", + "actions": [ + { + "type": "verifyDownloadedFile", + "checks": { + "fileExists": true, + "fileSize": "> 1024", + "pdfSignature": "%PDF-", + "description": "PDF 파일 헤더 검증" + } + } + ], + "verify": { + "pdfValid": true, + "minFileSize": "1KB 이상" + } + }, + { + "id": "24-3", + "name": "📋 PDF 스타일 수동 확인 체크리스트", + "type": "manualVerification", + "critical": true, + "description": "개발자가 다운로드된 PDF를 열어 시각적으로 확인해야 하는 항목", + "manualChecklist": [ + { + "id": "css-1", + "item": "테이블 경계선이 올바르게 표시되는가?", + "category": "테이블 스타일" + }, + { + "id": "css-2", + "item": "한글 폰트가 깨지지 않고 정상 표시되는가?", + "category": "폰트" + }, + { + "id": "css-3", + "item": "숫자/금액 정렬이 올바른가? (우측 정렬)", + "category": "정렬" + }, + { + "id": "css-4", + "item": "여백(margin/padding)이 적절한가?", + "category": "레이아웃" + }, + { + "id": "css-5", + "item": "헤더/푸터가 각 페이지에 올바르게 표시되는가?", + "category": "페이지 구조" + }, + { + "id": "css-6", + "item": "로고/이미지가 정상 표시되는가?", + "category": "이미지" + }, + { + "id": "css-7", + "item": "페이지 나눔(page break)이 적절한 위치에서 발생하는가?", + "category": "페이지 나눔" + }, + { + "id": "css-8", + "item": "배경색/강조색이 올바르게 적용되었는가?", + "category": "색상" + }, + { + "id": "css-9", + "item": "텍스트가 잘리거나 겹치지 않는가?", + "category": "오버플로우" + }, + { + "id": "css-10", + "item": "인쇄 시 레이아웃이 유지되는가?", + "category": "인쇄 호환성" + } + ], + "outputFiles": { + "screenshot": "tests/e2e/results/hotfix/screenshots/pdf-preview-before-download-*.png", + "pdfFile": "tests/e2e/results/hotfix/pdf-samples/vendor-ledger-*.pdf" + }, + "reportFlag": { + "requiresManualReview": true, + "message": "⚠️ PDF 스타일 수동 확인 필요 - 위 체크리스트 항목을 PDF 파일에서 직접 확인하세요" + } + }, + { + "id": 25, + "name": "상세 페이지 - 작업 버튼 확인 (어음 항목)", + "action": "verify_action_buttons", + "checks": [ + "어음 관련 항목에 수정 버튼(Pencil 아이콘) 존재", + "일반 항목에는 작업 버튼 없음" + ], + "expected": "hasAction=true인 항목에만 버튼 표시" + }, + { + "id": 26, + "name": "상세 페이지 - 목록 버튼 클릭", + "action": "click_button", + "target": "목록", + "expected": "거래처원장 목록 페이지로 복귀" + }, + { + "id": 27, + "name": "목록 페이지 복귀 확인", + "action": "verify_url", + "target": "/ko/accounting/vendor-ledger", + "expected": "목록 페이지 정상 표시" + }, + { + "id": 28, + "name": "페이지네이션 동작 확인", + "action": "verify_pagination", + "checks": [ + "현재 페이지 표시", + "전체 페이지 수 표시", + "페이지 이동 버튼 동작" + ], + "expected": "페이지네이션 정상 동작" + } + ], + "requiredVerifications": [ + { + "id": 1, + "name": "파일 다운로드 (엑셀/PDF)", + "steps": [15, 24, "24-1", "24-2", "24-3"], + "criteria": "Network API 호출 + 실제 파일 다운로드 + 성공 토스트 + PDF 스타일 검증" + }, + { + "id": "PDF-STYLE", + "name": "PDF 스타일/CSS 검증", + "steps": [24, "24-1", "24-2", "24-3"], + "criteria": "스크린샷 캡처 + PDF 파일 보관 + 수동 체크리스트 확인", + "manualReviewRequired": true, + "outputPaths": { + "screenshots": "tests/e2e/results/hotfix/screenshots/", + "pdfFiles": "tests/e2e/results/hotfix/pdf-samples/" + } + }, + { + "id": 2, + "name": "등록/저장 버튼", + "steps": [], + "criteria": "해당 없음 (조회 전용 페이지)" + }, + { + "id": 3, + "name": "검색/필터", + "steps": [6, 7, 8, 9, 10, 11], + "criteria": "기간 설정 및 검색 시 데이터 변화 확인" + }, + { + "id": 4, + "name": "모달 등록 완료", + "steps": [], + "criteria": "해당 없음 (조회 전용 페이지)" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [3], + "criteria": "입력 필드, 동작 버튼, API 호출 확인" + } + ], + "testData": { + "searchKeyword": "테스트", + "dateRange": { + "start": "2025-01-01", + "end": "2025-12-31" + }, + "detailDateRange": { + "start": "2025-06-01", + "end": "2025-06-30" + } + }, + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/vendor-ledger", + "description": "거래처원장 목록 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/vendor-ledger/summary", + "description": "거래처원장 요약 통계 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/vendor-ledger/{id}", + "description": "거래처원장 상세 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/vendor-ledger/export", + "description": "거래처원장 엑셀 다운로드" + }, + { + "method": "GET", + "endpoint": "/api/v1/vendor-ledger/{id}/export-pdf", + "description": "거래처원장 상세 PDF 다운로드" + } + ] +} diff --git a/vendor-management.json b/vendor-management.json new file mode 100644 index 0000000..716ce94 --- /dev/null +++ b/vendor-management.json @@ -0,0 +1,483 @@ +{ + "id": "vendor-management", + "name": "거래처관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "회계관리 > 거래처관리 메뉴의 목록 조회, 필터, 검색, 상세 페이지 진입, 수정 및 저장 기능 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "menuNavigation": { + "level1": "회계관리", + "level2": "거래처관리", + "expectedUrl": "/ko/accounting/vendors" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", + "level1": "회계관리", + "level2": "거래처관리", + "alternativeLevel1Names": ["회계관리", "회계 관리", "Accounting"], + "alternativeLevel2Names": ["거래처관리", "거래처 관리", "Vendors"], + "scrollConfig": { + "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", + "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + } + }, + "auth": { + "username": "TestUser5", + "password": "password123!" + }, + "notes": { + "skip": ["등록 버튼 (추후 구현 예정)", "삭제 기능 (보류)"], + "focus": ["테이블 행 클릭 → 상세 페이지", "수정 모드 진입", "수정 후 저장"] + }, + "steps": [ + { + "id": "step-0", + "name": "사이드바 메뉴 탐색 준비", + "description": "사이드바를 최상단으로 스크롤하고 메뉴 구조 파악", + "actions": [ + { "type": "scroll", "target": "sidebar", "direction": "top", "description": "사이드바 최상단으로 스크롤" }, + { "type": "wait", "duration": 500 } + ] + }, + { + "id": "step-1", + "name": "2단계 메뉴 진입: 회계관리 > 거래처관리", + "description": "사이드바를 스크롤하며 회계관리 > 거래처관리 메뉴를 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "회계관리", + "alternativeTexts": ["회계관리", "회계 관리", "Accounting"], + "scrollContainer": "sidebar", + "maxAttempts": 10, + "description": "스크롤하며 회계관리 메뉴 찾기" + }, + { "type": "click", "target": "회계관리", "description": "회계관리 메뉴 클릭" }, + { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, + { + "type": "scrollAndFind", + "target": "거래처관리", + "alternativeTexts": ["거래처관리", "거래처 관리", "Vendors"], + "scrollContainer": "submenu", + "maxAttempts": 5, + "description": "서브메뉴에서 거래처관리 찾기" + }, + { "type": "click", "target": "거래처관리", "description": "거래처관리 메뉴 클릭" }, + { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 } + ], + "expect": { + "url": "/ko/accounting/vendors", + "pageTitle": "거래처관리", + "elements": ["통계 카드", "테이블", "검색창"] + }, + "verification": [ + "회계관리 메뉴가 펼쳐졌는지 확인", + "거래처관리 서브메뉴 클릭 성공", + "404 에러 없이 페이지 로드 완료" + ] + }, + { + "id": 3, + "name": "필수 검증 #5: 목업 페이지 감지", + "action": "verify_not_mockup", + "checks": [ + "검색 입력 필드 존재", + "필터 드롭다운 존재 (구분, 신용등급, 거래등급, 악성채권, 정렬)", + "테이블 데이터 표시", + "API 호출 확인" + ], + "expected": "정상 페이지 (목업 아님)" + }, + { + "id": 4, + "name": "통계 카드 확인", + "action": "verify_elements", + "checks": [ + "전체 거래처 카드 표시", + "매출 거래처 카드 표시", + "매입 거래처 카드 표시" + ], + "expected": "3개 통계 카드 모두 표시" + }, + { + "id": 5, + "name": "테이블 구조 확인", + "action": "verify_table", + "checks": [ + "체크박스 컬럼", + "번호 컬럼", + "구분 컬럼 (매출/매입/매입매출)", + "거래처명 컬럼", + "매입 결제일 컬럼", + "매출 결제일 컬럼", + "신용등급 컬럼", + "거래등급 컬럼", + "미수금 컬럼", + "악성채권 컬럼", + "작업 컬럼" + ], + "expected": "11개 컬럼 존재" + }, + { + "id": 6, + "name": "⚠️ 필수 검증: 검색 기능", + "critical": true, + "description": "검색어 입력 후 테이블 데이터가 필터링되는지 확인", + "actions": [ + { "type": "capture", "variable": "beforeSearchCount", "selector": "table tbody tr", "extract": "count", "description": "검색 전 행 수 저장" }, + { "type": "fill", "target": "검색", "value": "가우스", "description": "검색어 '가우스' 입력" }, + { "type": "wait", "duration": 500, "description": "검색 결과 대기" }, + { "type": "capture", "variable": "afterSearchCount", "selector": "table tbody tr", "extract": "count", "description": "검색 후 행 수 저장" } + ], + "verify": { + "searchApplied": true, + "tableContains": "가우스", + "dataFiltered": "검색어에 맞는 거래처만 필터링되어야 함" + }, + "note": "⚠️ 검색어 입력 후 테이블에 '가우스'가 포함된 행만 표시되어야 함" + }, + { + "id": 7, + "name": "검색 결과 데이터 검증", + "description": "검색 결과의 각 행에 검색어가 포함되어 있는지 확인", + "actions": [ + { "type": "wait", "duration": 300 } + ], + "verify": { + "allRowsContain": "가우스", + "rowCountChanged": "beforeSearchCount !== afterSearchCount (데이터 필터링 확인)", + "verifyMethod": "테이블의 모든 행이 검색어 '가우스'를 포함하는지 확인" + }, + "note": "테이블 행 수 변화, 검색어 포함 거래처명 표시" + }, + { + "id": 8, + "name": "검색 초기화 및 복원 확인", + "description": "검색어 삭제 후 전체 목록 복원 확인", + "actions": [ + { "type": "clear", "target": "검색", "description": "검색어 삭제" }, + { "type": "wait", "duration": 500, "description": "목록 복원 대기" }, + { "type": "capture", "variable": "restoredRowCount", "selector": "table tbody tr", "extract": "count", "description": "복원 후 행 수 저장" } + ], + "verify": { + "dataRestored": true, + "rowCountRestored": "beforeSearchCount와 유사한 행 수로 복원" + } + }, + { + "id": 9, + "name": "구분 필터 테스트", + "action": "select_filter", + "target": "categoryFilter", + "value": "매출", + "expected": "매출 거래처만 필터링" + }, + { + "id": 10, + "name": "구분 필터 초기화", + "action": "select_filter", + "target": "categoryFilter", + "value": "전체", + "expected": "전체 데이터 다시 표시" + }, + { + "id": 11, + "name": "테이블 행 클릭 - 상세 페이지 이동", + "action": "click_row", + "target": "first_row", + "expected": "거래처 상세 페이지로 이동" + }, + { + "id": 12, + "name": "상세 페이지 - URL 확인", + "action": "verify_url", + "checks": [ + "URL에 거래처 ID 포함 (/accounting/vendors/{id})" + ], + "expected": "URL 정상 전달" + }, + { + "id": 13, + "name": "상세 페이지 - 헤더 확인", + "action": "verify_elements", + "checks": [ + "거래처 상세 타이틀", + "목록 버튼 존재", + "삭제 버튼 존재", + "수정 버튼 존재" + ], + "expected": "상세 페이지 헤더 정상 표시" + }, + { + "id": 14, + "name": "상세 페이지 - 기본 정보 카드 확인", + "action": "verify_detail_info", + "checks": [ + "사업자등록번호 필드", + "거래처코드 필드", + "거래처명 필드", + "대표자명 필드", + "거래처 유형 필드", + "업태 필드", + "업종 필드" + ], + "expected": "기본 정보 모두 표시 (읽기 전용)" + }, + { + "id": 15, + "name": "상세 페이지 - 연락처 정보 확인", + "action": "verify_detail_info", + "checks": [ + "주소 필드", + "전화번호 필드", + "모바일 필드", + "팩스 필드", + "이메일 필드" + ], + "expected": "연락처 정보 모두 표시" + }, + { + "id": 16, + "name": "상세 페이지 - 담당자 정보 확인", + "action": "verify_detail_info", + "checks": [ + "담당자명 필드", + "담당자 전화 필드", + "시스템 관리자 필드" + ], + "expected": "담당자 정보 모두 표시" + }, + { + "id": 17, + "name": "상세 페이지 - 회사 정보 확인", + "action": "verify_detail_info", + "checks": [ + "회사 로고 영역", + "매입 결제일 필드", + "매출 결제일 필드" + ], + "expected": "회사 정보 모두 표시" + }, + { + "id": 18, + "name": "상세 페이지 - 신용/거래 정보 확인", + "action": "verify_detail_info", + "checks": [ + "신용등급 필드", + "거래등급 필드", + "세금계산서 이메일 필드", + "입금계좌 은행 필드", + "계좌 필드", + "예금주 필드" + ], + "expected": "신용/거래 정보 모두 표시" + }, + { + "id": 19, + "name": "상세 페이지 - 추가 정보 확인", + "action": "verify_detail_info", + "checks": [ + "미수금 필드", + "악성채권 금액 필드", + "악성채권 상태 필드" + ], + "expected": "추가 정보 모두 표시" + }, + { + "id": 20, + "name": "상세 페이지 - 메모 섹션 확인", + "action": "verify_elements", + "checks": [ + "메모 카드 존재", + "메모 리스트 또는 빈 메시지" + ], + "expected": "메모 섹션 표시" + }, + { + "id": 21, + "name": "핵심 테스트: 수정 버튼 클릭", + "action": "click_button", + "target": "수정", + "expected": "수정 모드로 전환 (URL에 ?mode=edit 추가)" + }, + { + "id": 22, + "name": "수정 모드 - URL 확인", + "action": "verify_url", + "checks": [ + "URL에 mode=edit 파라미터 포함" + ], + "expected": "수정 모드 URL 정상" + }, + { + "id": 23, + "name": "수정 모드 - 필드 편집 가능 확인", + "action": "verify_edit_mode", + "checks": [ + "거래처명 입력 가능", + "대표자명 입력 가능", + "전화번호 입력 가능", + "이메일 입력 가능", + "저장 버튼 존재", + "취소 버튼 존재" + ], + "expected": "수정 모드에서 필드 편집 가능" + }, + { + "id": 24, + "name": "핵심 테스트: 거래처명 수정", + "action": "edit_field", + "target": "vendorName", + "value": " (수정테스트)", + "mode": "append", + "expected": "거래처명에 ' (수정테스트)' 추가" + }, + { + "id": 25, + "name": "핵심 테스트: 저장 버튼 클릭", + "action": "click_button", + "target": "저장", + "expected": "저장 확인 다이얼로그 표시" + }, + { + "id": 26, + "name": "핵심 테스트: 저장 확인 다이얼로그", + "action": "verify_dialog", + "checks": [ + "수정 확인 타이틀", + "확인 메시지 표시", + "취소 버튼 존재", + "확인 버튼 존재" + ], + "expected": "저장 확인 다이얼로그 정상 표시" + }, + { + "id": 27, + "name": "필수 검증 #2: 저장 확인 버튼 클릭", + "action": "click_dialog_confirm", + "target": "확인", + "checks": [ + "URL 변경 여부 확인", + "에러 페이지 감지", + "API 호출 확인 (PUT /api/v1/clients/{id})", + "성공 토스트 (수정이 완료되었습니다)", + "상세 페이지로 복귀" + ], + "expected": "수정 완료 및 상세 페이지 복귀" + }, + { + "id": 28, + "name": "수정 결과 확인", + "action": "verify_data_change", + "checks": [ + "거래처명에 수정된 값 반영", + "view 모드로 복귀 (수정 버튼 표시)" + ], + "expected": "수정된 데이터 정상 반영" + }, + { + "id": 29, + "name": "원래 값 복원 - 수정 버튼 클릭", + "action": "click_button", + "target": "수정", + "expected": "수정 모드로 전환" + }, + { + "id": 30, + "name": "원래 값 복원 - 거래처명 수정", + "action": "edit_field", + "target": "vendorName", + "value": "", + "mode": "remove_suffix", + "suffix": " (수정테스트)", + "expected": "거래처명에서 ' (수정테스트)' 제거" + }, + { + "id": 31, + "name": "원래 값 복원 - 저장", + "action": "click_button", + "target": "저장", + "expected": "저장 확인 다이얼로그 표시" + }, + { + "id": 32, + "name": "원래 값 복원 - 저장 확인", + "action": "click_dialog_confirm", + "target": "확인", + "expected": "원래 값으로 복원 완료" + }, + { + "id": 33, + "name": "목록 버튼 클릭 - 목록 복귀", + "action": "click_button", + "target": "목록", + "expected": "거래처관리 목록 페이지로 복귀" + }, + { + "id": 34, + "name": "목록 페이지 복귀 확인", + "action": "verify_url", + "target": "/ko/accounting/vendors", + "expected": "목록 페이지 정상 표시" + } + ], + "requiredVerifications": [ + { + "id": 1, + "name": "등록/저장 버튼", + "steps": [25, 26, 27], + "criteria": "저장 다이얼로그 확인 + API 호출 + 성공 토스트 + 데이터 반영" + }, + { + "id": 2, + "name": "등록 버튼 (신규)", + "steps": [], + "criteria": "보류 - 추후 구현 예정" + }, + { + "id": 3, + "name": "검색/필터", + "steps": [6, 7, 8, 9, 10], + "criteria": "검색 및 필터 시 데이터 변화 확인" + }, + { + "id": 4, + "name": "삭제 기능", + "steps": [], + "criteria": "보류 - 테스트 대상에서 제외" + }, + { + "id": 5, + "name": "목업 페이지 감지", + "steps": [3], + "criteria": "입력 필드, 동작 버튼, API 호출 확인" + } + ], + "testData": { + "searchKeyword": "가우스", + "editSuffix": " (수정테스트)" + }, + "expectedAPIs": [ + { + "method": "GET", + "endpoint": "/api/v1/clients", + "description": "거래처 목록 조회" + }, + { + "method": "GET", + "endpoint": "/api/v1/clients/{id}", + "description": "거래처 상세 조회" + }, + { + "method": "PUT", + "endpoint": "/api/v1/clients/{id}", + "description": "거래처 수정" + } + ] +} diff --git a/withdrawal-management.json b/withdrawal-management.json new file mode 100644 index 0000000..49e216d --- /dev/null +++ b/withdrawal-management.json @@ -0,0 +1,526 @@ +{ + "id": "withdrawal-management", + "name": "출금관리 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "출금관리 목록 조회, 계정과목명 일괄변경, 상세 수정 기능 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/ko/accounting/withdrawals", + "menuNavigation": { + "level1": "회계관리", + "level2": "출금관리", + "expectedUrl": "/ko/accounting/withdrawals" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebarSelector": ".sidebar-scroll, [data-sidebar], nav[role='navigation']", + "scrollBehavior": { + "direction": "down", + "step": 200, + "maxScrolls": 10, + "waitAfterScroll": 300 + }, + "level1": { + "text": "회계관리", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "출금관리", + "waitAfterClick": 300 + }, + "fallbackUrl": "/ko/accounting/withdrawals", + "verification": { + "urlContains": "/accounting/withdrawals", + "timeout": 5000 + } + }, + "timeout": 60000, + "tags": ["accounting", "withdrawal", "crud"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 준비", + "description": "사이드바 스크롤을 최상단으로 이동하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll, [data-sidebar], nav[role=\"navigation\"]')?.scrollTo({top: 0, behavior: 'instant'})", + "description": "사이드바 스크롤 최상단 이동" + }, + { + "type": "wait", + "duration": 500, + "description": "스크롤 완료 대기" + } + ], + "expect": { + "sidebarReady": true + } + }, + { + "id": "step-1", + "name": "출금관리 메뉴 진입", + "description": "회계관리 > 출금관리 메뉴로 이동 (scrollAndFind 패턴 사용)", + "menuNavigation": { + "useEnhanced": true, + "scrollAndFind": { + "level1": { + "text": "회계관리", + "scrollUntilVisible": true, + "clickToExpand": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "출금관리", + "scrollUntilVisible": true, + "waitAfterClick": 300 + } + }, + "fallback": { + "useDirectUrl": true, + "url": "/ko/accounting/withdrawals" + } + }, + "expect": { + "url": "/accounting/withdrawals", + "visible": ["출금관리", "총 출금"] + } + }, + { + "id": "step-2", + "name": "목록 페이지 구조 확인", + "description": "테이블 및 필터 요소 확인", + "expect": { + "visible": ["출금일", "출금계좌", "받는분", "출금금액", "거래처", "적요", "출금유형"], + "elements": { + "statisticsCards": ["총 출금", "당월 출금", "거래처 미설정", "출금유형 미설정"], + "filters": ["계정과목명", "저장", "새로고침"], + "pagination": true + } + } + }, + { + "id": "step-3", + "name": "계정과목명 드롭다운 옵션 확인", + "description": "계정과목명 일괄변경 드롭다운 옵션 검증", + "actions": [ + { "type": "click", "target": "계정과목명 드롭다운", "description": "드롭다운 열기" } + ], + "expect": { + "options": ["미설정", "매입대금", "급여", "임차료", "수도광열비", "통신비", "소모품비", "운반비", "차량유지비", "보험료", "세금과공과", "이자비용", "수수료", "기타"] + }, + "note": "출금유형 옵션은 비용 계정 기준이므로 입금관리와 다름" + }, + { + "id": "step-4", + "name": "체크박스 선택 후 계정과목명 일괄변경", + "description": "테이블 행 선택 후 계정과목명 일괄변경 저장", + "actions": [ + { "type": "click", "target": "첫 번째 행 체크박스", "description": "행 선택" }, + { "type": "click", "target": "계정과목명 드롭다운", "description": "드롭다운 열기" }, + { "type": "click", "target": "매입대금", "description": "매입대금 선택" }, + { "type": "click", "target": "저장", "description": "저장 버튼 클릭" } + ], + "expect": { + "dialog": "확인 다이얼로그 표시", + "dialogMessage": "1개의 출금 유형을 매입대금(으)로 모두 변경하시겠습니까?", + "toast": "변경 완료 메시지" + }, + "checks": [ + "API 호출 확인 (PUT /api/v1/withdrawals/batch-update-account)", + "성공 토스트 메시지", + "URL 유지 확인" + ] + }, + { + "id": "step-4-1", + "name": "⚠️ 필수 검증: 계정과목명 변경 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", + "description": "저장 후 테이블에서 변경된 출금유형 값 확인", + "expect": { + "tableCell": { + "row": 1, + "column": "출금유형", + "value": "매입대금" + } + }, + "checks": [ + "선택한 행의 출금유형 컬럼 값 확인", + "변경 전 값과 비교 (예: 미설정 → 매입대금)", + "페이지 새로고침 후에도 변경된 값 유지 확인", + "API 응답과 UI 표시값 일치 확인" + ], + "knownBugReference": "BUG-DEPOSIT-20260115-001 (입금관리 동일 버그 확인 필요)" + }, + { + "id": "step-5", + "name": "출금 상세 페이지 이동", + "description": "테이블 행 클릭하여 상세 페이지로 이동", + "actions": [ + { "type": "click", "target": "테이블 첫 번째 행", "description": "행 클릭 (체크박스 제외 영역)" } + ], + "expect": { + "url": "/accounting/withdrawals/{id}", + "visible": ["출금 상세", "기본 정보", "목록", "삭제", "수정"] + } + }, + { + "id": "step-6", + "name": "상세 페이지 읽기 모드 필드 확인", + "description": "수정 전 필드들이 비활성화 상태인지 확인", + "expect": { + "fields": [ + { "name": "출금일", "disabled": true }, + { "name": "출금계좌", "disabled": true }, + { "name": "받는분", "disabled": true }, + { "name": "출금금액", "disabled": true }, + { "name": "적요", "disabled": true }, + { "name": "거래처", "disabled": true }, + { "name": "출금 유형", "disabled": true } + ] + } + }, + { + "id": "step-7", + "name": "수정 모드 전환", + "description": "수정 버튼 클릭하여 편집 모드로 전환", + "click": "수정", + "expect": { + "url": "/accounting/withdrawals/{id}?mode=edit", + "visible": ["출금 수정", "취소", "저장"], + "notVisible": ["목록", "삭제", "수정"] + } + }, + { + "id": "step-8", + "name": "수정 모드 필드 활성화 검증", + "description": "수정 가능한 필드와 불가능한 필드 확인", + "expect": { + "fields": [ + { "name": "출금일", "disabled": true, "note": "은행데이터 - 수정 불가" }, + { "name": "출금계좌", "disabled": true, "note": "은행데이터 - 수정 불가" }, + { "name": "받는분", "disabled": true, "note": "은행데이터 - 수정 불가" }, + { "name": "출금금액", "disabled": true, "note": "은행데이터 - 수정 불가" }, + { "name": "적요", "disabled": false, "editable": true }, + { "name": "거래처", "disabled": false, "type": "combobox", "editable": true }, + { "name": "출금 유형", "disabled": false, "type": "combobox", "editable": true } + ] + } + }, + { + "id": "step-9", + "name": "거래처 드롭다운 옵션 확인", + "description": "거래처 선택 드롭다운 옵션 검증", + "actions": [ + { "type": "click", "target": "거래처 드롭다운", "description": "드롭다운 열기" } + ], + "expect": { + "options": ["거래처테스트", "아크더레드", "코브라브릿지", "가우스전자", "아크아크"], + "note": "거래처 옵션은 시스템 공통 데이터" + } + }, + { + "id": "step-10", + "name": "출금 유형 드롭다운 옵션 확인", + "description": "출금 유형 선택 드롭다운 옵션 검증", + "actions": [ + { "type": "click", "target": "출금 유형 드롭다운", "description": "드롭다운 열기" } + ], + "expect": { + "options": ["미설정", "매입대금", "급여", "임차료", "수도광열비", "통신비", "소모품비", "운반비", "차량유지비", "보험료", "세금과공과", "이자비용", "수수료", "기타"] + } + }, + { + "id": "step-11", + "name": "수정 데이터 입력", + "description": "수정 가능한 필드에 테스트 데이터 입력", + "form": { + "fields": [ + { "name": "적요", "type": "text", "value": "테스트 적요 수정" } + ] + }, + "actions": [ + { "type": "click", "target": "거래처 드롭다운", "description": "거래처 드롭다운 열기" }, + { "type": "click", "target": "거래처테스트", "description": "거래처 선택" }, + { "type": "click", "target": "출금 유형 드롭다운", "description": "출금 유형 드롭다운 열기" }, + { "type": "click", "target": "매입대금", "description": "매입대금 선택" } + ] + }, + { + "id": "step-12", + "name": "저장 및 결과 확인", + "description": "저장 버튼 클릭 후 데이터 반영 확인", + "click": "저장", + "expect": { + "toast": "저장 완료 메시지", + "url": "/accounting/withdrawals/{id}", + "mode": "view" + }, + "checks": [ + "API 호출 확인 (PUT /api/v1/withdrawals/{id})", + "성공 토스트 메시지", + "URL 유지 확인 (에러 페이지 이동 금지)" + ] + }, + { + "id": "step-12-1", + "name": "⚠️ 필수 검증: 수정 데이터 반영 확인", + "critical": true, + "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", + "description": "저장 후 상세 페이지에서 변경된 값 확인", + "expect": { + "fields": [ + { "name": "적요", "value": "테스트 적요 수정" }, + { "name": "거래처", "value": "거래처테스트" }, + { "name": "출금 유형", "value": "매입대금" } + ] + } + }, + { + "id": "step-13", + "name": "취소 버튼 동작 확인", + "description": "수정 모드에서 취소 버튼 동작 검증", + "actions": [ + { "type": "click", "target": "수정", "description": "수정 모드 진입" }, + { "type": "click", "target": "취소", "description": "취소 버튼 클릭" } + ], + "expect": { + "url": "/accounting/withdrawals/{id}", + "mode": "view", + "visible": ["출금 상세", "목록", "삭제", "수정"] + } + }, + { + "id": "step-14", + "name": "목록 버튼 동작 확인", + "description": "목록 버튼 클릭하여 목록 페이지로 이동", + "click": "목록", + "expect": { + "url": "/accounting/withdrawals", + "visible": ["출금관리", "총 출금"] + } + }, + { + "id": "step-15", + "name": "필터 드롭다운 검증", + "description": "목록 페이지 필터 드롭다운 옵션 확인", + "note": "3개의 필터 드롭다운 존재 (거래처, 출금유형, 정렬)", + "expect": { + "filters": [ + { "name": "거래처 필터", "default": "전체" }, + { "name": "출금유형 필터", "default": "전체" }, + { "name": "정렬", "default": "최신순", "options": ["최신순", "등록순", "금액 높은순", "금액 낮은순"] } + ] + } + }, + { + "id": "step-16", + "name": "날짜 필터 검증", + "description": "날짜 필터 버튼 동작 확인", + "actions": [ + { "type": "click", "target": "당해년도", "description": "당해년도 버튼 클릭" } + ], + "expect": { + "dateRange": { + "start": "2026-01-01", + "end": "2026-12-31" + } + } + }, + { + "id": "step-17", + "name": "페이지네이션 동작 확인", + "description": "페이지네이션 버튼 동작 검증 (데이터 존재 시)", + "condition": "데이터가 20건 이상인 경우에만 실행", + "expect": { + "pagination": { + "itemsPerPage": 20, + "currentPage": 1 + } + }, + "actions": [ + { "type": "click", "target": "다음", "description": "다음 페이지로 이동" } + ], + "expectAfterAction": { + "currentPage": 2 + } + } + ], + + "skipTests": [ + { + "id": "delete-button", + "name": "삭제 버튼 테스트", + "reason": "사용자 요청에 따라 테스트 제외" + } + ], + + "knownBugs": [ + { + "id": "BUG-DEPOSIT-20260115-001", + "description": "계정과목명 일괄변경 시 API 오류 발생 (입금관리)", + "errorMessage": "존재하지 않는 URI 또는 데이터", + "relatedSteps": ["step-4-1"], + "note": "출금관리에서도 동일한 버그가 존재할 수 있으므로 step-4-1에서 검증 필수" + }, + { + "id": "BUG-SALES-20260115-001", + "description": "계정과목명 일괄변경 시 토스트 성공 표시되나 실제 데이터 미변경 (매출관리)", + "relatedSteps": ["step-4-1"], + "note": "유사한 UI 패턴이므로 동일 버그 가능성 있음" + } + ], + + "criticalValidationChecklist": [ + { + "id": 1, + "name": "파일 다운로드 검증", + "trigger": "다운로드, Export 버튼 발견 시", + "checks": ["Network API 호출", "실제 파일 다운로드"], + "note": "Console LOG만으로 PASS 판정 금지" + }, + { + "id": 2, + "name": "등록/저장 버튼 검증", + "trigger": "등록, 저장, 제출 버튼 클릭 시", + "checks": ["URL 유지 확인", "에러 페이지 없음", "성공 토스트"], + "note": "에러 페이지 이동 감지 필수" + }, + { + "id": 3, + "name": "일괄변경 데이터 반영 검증", + "trigger": "계정과목명 저장 후", + "checks": ["테이블 데이터 변경 확인", "새로고침 후 유지 확인"], + "note": "토스트만 확인하면 불충분" + }, + { + "id": 4, + "name": "목업 페이지 감지", + "trigger": "페이지 로드 시", + "checks": ["입력 필드 존재", "동작하는 버튼 존재", "API 호출 여부"], + "note": "버튼만 있고 동작 안하면 목업" + } + ], + + "testData": { + "sampleWithdrawal": { + "date": "2025-12-28", + "account": "운영계좌", + "recipient": "홍길동", + "amount": "1,500,000", + "vendor": "", + "description": "급여 지급", + "withdrawalType": "미설정" + }, + "modifiedData": { + "description": "테스트 적요 수정", + "vendor": "거래처테스트", + "withdrawalType": "매입대금" + } + }, + + "pageStructure": { + "listPage": { + "url": "/accounting/withdrawals", + "title": "출금관리", + "statistics": ["총 출금", "당월 출금", "거래처 미설정", "출금유형 미설정"], + "tableColumns": ["checkbox", "출금일", "출금계좌", "받는분", "출금금액", "거래처", "적요", "출금유형", "action"], + "batchUpdate": { + "label": "계정과목명", + "saveButton": "저장" + }, + "filters": ["거래처", "출금유형", "정렬"], + "dateFilters": ["당해년도", "전전월", "전월", "당월", "어제", "오늘"] + }, + "detailPage": { + "url": "/accounting/withdrawals/{id}", + "title": "출금 상세", + "buttons": ["목록", "삭제", "수정"], + "fields": { + "readOnly": ["출금일", "출금계좌", "받는분", "출금금액"], + "editable": ["적요", "거래처", "출금 유형"] + } + }, + "editPage": { + "url": "/accounting/withdrawals/{id}?mode=edit", + "title": "출금 수정", + "buttons": ["취소", "저장"] + } + }, + + "dropdownOptions": { + "accountSubject": { + "label": "계정과목명", + "options": ["미설정", "매입대금", "급여", "임차료", "수도광열비", "통신비", "소모품비", "운반비", "차량유지비", "보험료", "세금과공과", "이자비용", "수수료", "기타"], + "note": "출금유형은 비용 계정 기준 (입금유형과 다름)" + }, + "withdrawalType": { + "label": "출금 유형", + "options": ["미설정", "매입대금", "급여", "임차료", "수도광열비", "통신비", "소모품비", "운반비", "차량유지비", "보험료", "세금과공과", "이자비용", "수수료", "기타"] + }, + "vendor": { + "label": "거래처", + "options": ["거래처테스트", "아크더레드", "코브라브릿지", "가우스전자", "아크아크"] + }, + "sortOrder": { + "label": "정렬", + "options": ["최신순", "등록순", "금액 높은순", "금액 낮은순"] + } + }, + + "comparisonWithDeposit": { + "differences": [ + { + "field": "계정과목/유형 옵션", + "deposit": "매출대금, 선수금, 가수금, 임대수익, 이자수익, 보증금 반환, 차입금, 자본금, 부가세 환급, 기타 (수입 계정)", + "withdrawal": "매입대금, 급여, 임차료, 수도광열비, 통신비, 소모품비, 운반비, 차량유지비, 보험료, 세금과공과, 이자비용, 수수료, 기타 (비용 계정)" + }, + { + "field": "테이블 컬럼명", + "deposit": "입금일, 입금계좌, 입금자명, 입금금액, 입금유형", + "withdrawal": "출금일, 출금계좌, 받는분, 출금금액, 출금유형" + }, + { + "field": "통계 카드", + "deposit": "총 입금, 당월 입금, 입금유형 미설정", + "withdrawal": "총 출금, 당월 출금, 출금유형 미설정" + } + ], + "similarities": [ + "페이지 구조 (목록 → 상세 → 수정)", + "계정과목명 일괄변경 기능", + "거래처 드롭다운 옵션 (공통 데이터)", + "날짜 필터 버튼 (당해년도, 전전월, 전월, 당월, 어제, 오늘)", + "정렬 옵션 (최신순, 등록순, 금액 높은순, 금액 낮은순)", + "은행 데이터 필드 수정 불가 (날짜, 계좌, 금액 등)" + ] + }, + + "assertions": [ + { + "type": "url", + "expected": "/accounting/withdrawals", + "message": "목록 페이지 URL 확인" + }, + { + "type": "text", + "target": "body", + "expected": "출금관리", + "message": "페이지 타이틀 확인" + } + ] +} diff --git a/work-order-management.json b/work-order-management.json new file mode 100644 index 0000000..6da2df5 --- /dev/null +++ b/work-order-management.json @@ -0,0 +1,656 @@ +{ + "scenarioId": "work-order-management", + "scenarioName": "작업지시 관리 (Work Order Management)", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "생산관리 - 작업지시 관리 메뉴의 전체 기능 테스트: 목록 조회, 통계 카드, 검색/필터, 등록 (수주 연동 / 수동 등록), 상세 조회, 수정, 작업일지", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/ko/production/work-orders", + "menuNavigation": { + "level1": "생산관리", + "level2": "작업지시 관리", + "expectedUrl": "/ko/production/work-orders" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", + "level1": "생산관리", + "level2": "작업지시 관리", + "alternativeLevel1Names": ["생산관리", "생산 관리", "Production", "제조관리"], + "alternativeLevel2Names": ["작업지시 목록", "작업지시목록", "Work Orders", "작업지시", "지시서 목록"], + "scrollConfig": { + "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", + "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", + "scrollStep": 200, + "maxScrollAttempts": 10, + "scrollDelay": 300 + } + }, + "testData": { + "existingWorkOrder": { + "작업지시번호": "WO202601150001", + "로트번호": "ORD202601150001", + "작업상태": "승인대기", + "발주처": "코브라브릿지", + "현장명": "테스트현장2", + "납기일": "2026-01-14", + "우선순위": "5 (일반)" + }, + "searchKeyword": "WO2026", + "manualRegistration": { + "발주처": "테스트발주처", + "현장명": "테스트현장", + "수주번호": "ORD202601170001", + "품목수": "5" + } + }, + "steps": [ + { + "step": 0, + "name": "사이드바 메뉴 탐색 준비", + "description": "사이드바를 최상단으로 스크롤하고 메뉴 구조 파악", + "actions": [ + { "type": "scroll", "target": "sidebar", "direction": "top", "description": "사이드바 최상단으로 스크롤" }, + { "type": "wait", "duration": 500 } + ] + }, + { + "step": 1, + "name": "2단계 메뉴 진입: 생산관리 > 작업지시 목록", + "description": "사이드바를 스크롤하며 생산관리 > 작업지시 목록 메뉴를 찾아 클릭", + "actions": [ + { + "type": "scrollAndFind", + "target": "생산관리", + "alternativeTexts": ["생산관리", "생산 관리", "Production", "제조관리"], + "scrollContainer": "sidebar", + "maxAttempts": 10, + "description": "스크롤하며 생산관리 메뉴 찾기" + }, + { "type": "click", "target": "생산관리", "description": "생산관리 메뉴 클릭" }, + { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, + { + "type": "scrollAndFind", + "target": "작업지시 목록", + "alternativeTexts": ["작업지시 목록", "작업지시목록", "Work Orders", "작업지시"], + "scrollContainer": "submenu", + "maxAttempts": 5, + "description": "서브메뉴에서 작업지시 목록 찾기" + }, + { "type": "click", "target": "작업지시 목록", "description": "작업지시 목록 메뉴 클릭" }, + { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 } + ], + "expected": { + "url": "/ko/production/work-orders", + "title": "작업지시 목록", + "authenticated": true + }, + "verification": [ + "생산관리 메뉴가 펼쳐졌는지 확인", + "작업지시 목록 서브메뉴 클릭 성공", + "404 에러 없이 페이지 로드 완료" + ] + }, + { + "step": 2, + "name": "페이지 제목 확인", + "action": "verifyText", + "selector": "h1", + "expected": "\"작업지시 목록\" 제목 표시" + }, + { + "step": 3, + "name": "페이지 설명 확인", + "action": "verifyText", + "selector": "p", + "expected": "\"생산 작업지시 관리\" 설명 표시" + }, + { + "step": 4, + "name": "등록 버튼 확인", + "action": "verifyElement", + "selector": "button[name='등록']", + "expected": "등록 버튼 표시됨" + }, + { + "step": 5, + "name": "통계 카드 4개 확인", + "action": "verifyElement", + "expected": "전체, 작업대기, 작업중, 작업완료 통계 카드 표시" + }, + { + "step": 6, + "name": "통계 카드 데이터 확인", + "action": "verifyText", + "expected": "전체(1), 작업대기(1), 작업중(0), 작업완료(0)" + }, + { + "step": 7, + "name": "검색 입력 필드 확인", + "action": "verifyElement", + "selector": "textbox[placeholder='작업지시번호, 발주처, 현장명 검색...']", + "expected": "검색 입력 필드 표시됨" + }, + { + "step": 8, + "name": "상태 필터 탭 6개 확인", + "action": "verifyElement", + "expected": "전체, 미배정, 승인대기, 작업대기, 작업중, 작업완료 탭 표시" + }, + { + "step": 9, + "name": "상태 필터 탭 데이터 확인", + "action": "verifyText", + "expected": "전체(1), 미배정(0), 승인대기(1), 작업대기(0), 작업중(0), 작업완료(0)" + }, + { + "step": 10, + "name": "테이블 헤더 13개 컬럼 확인", + "action": "verifyElement", + "expected": "체크박스, 번호, 작업지시번호, 공정, 로트번호, 지시일, 배정, 작업, 시작, 작업상태, 현장순위, 작업자, 현장명, 출고예정일" + }, + { + "step": 11, + "name": "테이블 데이터 행 확인", + "action": "verifyElement", + "expected": "1건의 작업지시 (WO202601150001) 표시" + }, + { + "step": 12, + "name": "작업지시 데이터 내용 확인", + "action": "verifyText", + "expected": "작업지시번호: WO202601150001, 로트번호: ORD202601150001, 작업상태: 승인대기, 현장명: 테스트현장2" + }, + { + "step": 13, + "name": "페이지네이션 정보 확인", + "action": "verifyText", + "expected": "전체 1개 중 1-1개 표시" + }, + { + "step": 14, + "name": "검색 기능 테스트 - 검색어 입력 (필수 검증 #3)", + "action": "type", + "selector": "textbox[placeholder='작업지시번호, 발주처, 현장명 검색...']", + "value": "WO2026", + "expected": "검색어 입력됨" + }, + { + "step": 15, + "name": "검색 결과 확인 (필수 검증 #3)", + "action": "verifyElement", + "expected": "검색어와 일치하는 데이터 (WO202601150001) 표시" + }, + { + "step": 16, + "name": "검색어 초기화", + "action": "clear", + "selector": "textbox[placeholder='작업지시번호, 발주처, 현장명 검색...']", + "expected": "검색어 초기화됨" + }, + { + "step": 17, + "name": "승인대기 탭 클릭 (필수 검증 #3)", + "action": "click", + "selector": "button[name='승인대기 1']", + "expected": "승인대기 탭 활성화" + }, + { + "step": 18, + "name": "승인대기 필터 결과 확인 (필수 검증 #3)", + "action": "verifyElement", + "expected": "승인대기 상태 작업지시 1건 표시" + }, + { + "step": 19, + "name": "전체 탭 클릭", + "action": "click", + "selector": "button[name='전체 1']", + "expected": "전체 탭 활성화" + }, + { + "step": 20, + "name": "전체 필터 결과 확인", + "action": "verifyElement", + "expected": "전체 작업지시 1건 표시" + }, + { + "step": 21, + "name": "등록 버튼 클릭", + "action": "click", + "selector": "button[name='등록']", + "expected": "등록 버튼 클릭됨" + }, + { + "step": 22, + "name": "URL 변경 확인 (필수 검증 #2)", + "action": "verifyUrl", + "expected": "URL이 /production/work-orders/create로 변경 (404 에러 페이지 아님)", + "validation": { + "notContains": ["404", "not-found", "error"] + } + }, + { + "step": 23, + "name": "작업지시 등록 페이지 제목 확인", + "action": "verifyText", + "selector": "h1", + "expected": "\"작업지시 등록\" 제목 표시" + }, + { + "step": 24, + "name": "취소/등록 버튼 확인", + "action": "verifyElement", + "expected": "취소, 등록 버튼 표시됨" + }, + { + "step": 25, + "name": "등록 방식 라디오 버튼 2개 확인", + "action": "verifyElement", + "expected": "수주 연동 등록, 수동 등록 라디오 버튼 표시" + }, + { + "step": 26, + "name": "기본 선택 상태 확인", + "action": "verifyElement", + "expected": "수주 연동 등록이 기본 선택됨" + }, + { + "step": 27, + "name": "수주 정보 섹션 확인", + "action": "verifyElement", + "expected": "수주 정보 섹션 표시, \"수주 선택\" 버튼 존재" + }, + { + "step": 28, + "name": "수주 연동 모드 - 기본 정보 필드 4개 확인", + "action": "verifyElement", + "expected": "발주처, 현장명, 수주번호, 품목수 필드 표시 (모두 disabled)" + }, + { + "step": 29, + "name": "작업지시 정보 섹션 확인", + "action": "verifyElement", + "expected": "공정구분, 출고예정일, 우선순위, 담당자 필드 표시" + }, + { + "step": 30, + "name": "비고 필드 확인", + "action": "verifyElement", + "expected": "비고 입력 필드 표시" + }, + { + "step": 31, + "name": "수주 선택 버튼 클릭", + "action": "click", + "selector": "button[name='수주 선택']", + "expected": "수주 선택 버튼 클릭됨" + }, + { + "step": 32, + "name": "수주 선택 모달 열림 확인", + "action": "verifyElement", + "selector": "dialog[aria-label='수주 선택']", + "expected": "수주 선택 모달 표시됨" + }, + { + "step": 33, + "name": "모달 제목 확인", + "action": "verifyText", + "selector": "h2", + "expected": "\"수주 선택\" 제목 표시" + }, + { + "step": 34, + "name": "모달 검색 필드 확인", + "action": "verifyElement", + "selector": "textbox[placeholder='수주번호, 거래처, 현장명 검색...']", + "expected": "검색 입력 필드 표시됨" + }, + { + "step": 35, + "name": "모달 상태 메시지 확인", + "action": "verifyText", + "expected": "작업지시 가능한 수주 0건 메시지 표시" + }, + { + "step": 36, + "name": "모달 닫기 버튼 확인", + "action": "verifyElement", + "selector": "button[name='Close']", + "expected": "닫기 버튼 표시됨" + }, + { + "step": 37, + "name": "모달 닫기", + "action": "click", + "selector": "button[name='Close']", + "expected": "모달 닫힘" + }, + { + "step": 38, + "name": "모달 닫힘 확인", + "action": "verifyElement", + "expected": "모달이 화면에서 사라짐" + }, + { + "step": 39, + "name": "수동 등록 라디오 버튼 클릭", + "action": "click", + "selector": "radio[name='수동 등록 (재고생산)']", + "expected": "수동 등록 라디오 버튼 클릭됨" + }, + { + "step": 40, + "name": "수동 등록 모드 활성화 확인", + "action": "verifyElement", + "expected": "수동 등록이 선택됨" + }, + { + "step": 41, + "name": "수주 정보 섹션 숨김 확인", + "action": "verifyElement", + "expected": "수주 정보 섹션이 화면에서 사라짐" + }, + { + "step": 42, + "name": "수동 등록 모드 - 기본 정보 필드 활성화 확인", + "action": "verifyElement", + "expected": "발주처, 현장명, 수주번호, 품목수 필드가 입력 가능 (enabled)" + }, + { + "step": 43, + "name": "발주처 필드 placeholder 확인", + "action": "verifyText", + "selector": "textbox[placeholder='발주처 입력']", + "expected": "\"발주처 입력\" placeholder 표시" + }, + { + "step": 44, + "name": "현장명 필드 placeholder 확인", + "action": "verifyText", + "selector": "textbox[placeholder='현장명 입력']", + "expected": "\"현장명 입력\" placeholder 표시" + }, + { + "step": 45, + "name": "공정구분 콤보박스 확인", + "action": "verifyElement", + "selector": "combobox[name='공정구분']", + "expected": "공정구분 드롭다운 표시, 기본값 \"스크린\"" + }, + { + "step": 46, + "name": "우선순위 콤보박스 확인", + "action": "verifyElement", + "selector": "combobox[name='우선순위']", + "expected": "우선순위 드롭다운 표시, 기본값 \"5 (일반)\"" + }, + { + "step": 47, + "name": "담당자 선택 필드 확인", + "action": "verifyElement", + "expected": "담당자 선택 필드 표시, placeholder \"담당자를 선택하세요 (팀/개인)\"" + }, + { + "step": 48, + "name": "취소 버튼 클릭", + "action": "click", + "selector": "button[name='취소']", + "expected": "취소 버튼 클릭됨" + }, + { + "step": 49, + "name": "URL 변경 확인 (필수 검증 #2)", + "action": "verifyUrl", + "expected": "URL이 /production/work-orders로 변경 (목록 페이지로 복귀)", + "validation": { + "notContains": ["404", "not-found", "error"] + } + }, + { + "step": 50, + "name": "목록 페이지 복귀 확인", + "action": "verifyText", + "selector": "h1", + "expected": "\"작업지시 목록\" 제목 표시" + }, + { + "step": 51, + "name": "작업지시 행 클릭 - 상세 페이지 이동", + "action": "click", + "selector": "row[name='WO202601150001']", + "expected": "작업지시 행 클릭됨" + }, + { + "step": 52, + "name": "URL 변경 확인 (필수 검증 #2)", + "action": "verifyUrl", + "expected": "URL이 /production/work-orders/1로 변경 (404 에러 페이지 아님)", + "validation": { + "notContains": ["404", "not-found", "error"] + } + }, + { + "step": 53, + "name": "작업지시 상세 페이지 제목 확인", + "action": "verifyText", + "selector": "h1", + "expected": "\"작업지시 상세\" 제목 표시" + }, + { + "step": 54, + "name": "상세 페이지 버튼 3개 확인", + "action": "verifyElement", + "expected": "수정, 작업일지, 목록 버튼 표시" + }, + { + "step": 55, + "name": "기본 정보 섹션 확인", + "action": "verifyElement", + "expected": "기본 정보 섹션 표시 (9개 필드)" + }, + { + "step": 56, + "name": "기본 정보 데이터 확인", + "action": "verifyText", + "expected": "작업지시번호: WO202601150001, 로트번호: ORD202601150001, 작업상태: 승인대기, 발주처: 코브라브릿지, 현장명: 테스트현장2, 납기일: 2026-01-14, 우선순위: 5 (일반)" + }, + { + "step": 57, + "name": "공정 진행 섹션 확인", + "action": "verifyElement", + "expected": "공정 진행 (5단계) 섹션 표시" + }, + { + "step": 58, + "name": "공정 5단계 확인", + "action": "verifyText", + "expected": "1. 원단절단, 2. 미싱, 3. 앤드락작업, 4. 중간검사, 5. 포장" + }, + { + "step": 59, + "name": "작업 품목 섹션 확인", + "action": "verifyElement", + "expected": "작업 품목 (0건) 섹션 표시" + }, + { + "step": 60, + "name": "작업 품목 빈 상태 메시지 확인", + "action": "verifyText", + "expected": "\"등록된 품목이 없습니다.\" 메시지 표시" + }, + { + "step": 61, + "name": "수정 버튼 클릭", + "action": "click", + "selector": "button[name='수정']", + "expected": "수정 버튼 클릭됨" + }, + { + "step": 62, + "name": "URL 변경 확인 (필수 검증 #2)", + "action": "verifyUrl", + "expected": "URL이 /production/work-orders/1?mode=edit로 변경 (404 에러 페이지 아님)", + "validation": { + "notContains": ["404", "not-found", "error"] + } + }, + { + "step": 63, + "name": "작업지시 수정 페이지 제목 확인", + "action": "verifyText", + "selector": "h1", + "expected": "\"작업지시 수정\" 제목 표시" + }, + { + "step": 64, + "name": "수정 페이지 작업지시번호 확인", + "action": "verifyText", + "expected": "\"(WO202601150001)\" 표시" + }, + { + "step": 65, + "name": "수정 페이지 버튼 2개 확인", + "action": "verifyElement", + "expected": "취소, 저장 버튼 표시" + }, + { + "step": 66, + "name": "수정 페이지 기본 정보 섹션 확인", + "action": "verifyElement", + "expected": "기본 정보 섹션 표시 (4개 필드)" + }, + { + "step": 67, + "name": "수정 페이지 필드 상태 확인", + "action": "verifyElement", + "expected": "발주처(disabled), 현장명(enabled), 수주번호(disabled), 품목수(disabled)" + }, + { + "step": 68, + "name": "수정 페이지 작업지시 정보 섹션 확인", + "action": "verifyElement", + "expected": "작업지시 정보 섹션 표시 (4개 필드)" + }, + { + "step": 69, + "name": "수정 페이지 공정구분 확인", + "action": "verifyElement", + "selector": "combobox[name='공정구분']", + "expected": "공정구분 드롭다운 표시" + }, + { + "step": 70, + "name": "수정 페이지 출고예정일 확인", + "action": "verifyElement", + "selector": "textbox[name='출고예정일']", + "expected": "출고예정일 필드 표시, 값: 2026-01-14" + }, + { + "step": 71, + "name": "수정 페이지 우선순위 확인", + "action": "verifyElement", + "selector": "combobox[name='우선순위']", + "expected": "우선순위 드롭다운 표시, 값: 5 (일반)" + }, + { + "step": 72, + "name": "수정 페이지 담당자 필드 확인", + "action": "verifyElement", + "expected": "담당자 선택 필드 표시" + }, + { + "step": 73, + "name": "수정 페이지 비고 필드 확인", + "action": "verifyElement", + "expected": "비고 입력 필드 표시" + }, + { + "step": 74, + "name": "취소 버튼 클릭 (수정 페이지)", + "action": "click", + "selector": "button[name='취소']", + "expected": "취소 버튼 클릭됨" + }, + { + "step": 75, + "name": "URL 변경 확인 (필수 검증 #2)", + "action": "verifyUrl", + "expected": "URL이 /production/work-orders/1로 변경 (상세 페이지로 복귀)", + "validation": { + "notContains": ["404", "not-found", "error"] + } + }, + { + "step": 76, + "name": "상세 페이지 복귀 확인", + "action": "verifyText", + "selector": "h1", + "expected": "\"작업지시 상세\" 제목 표시" + }, + { + "step": 77, + "name": "목록 버튼 클릭", + "action": "click", + "selector": "button[name='목록']", + "expected": "목록 버튼 클릭됨" + }, + { + "step": 78, + "name": "URL 변경 확인 (필수 검증 #2)", + "action": "verifyUrl", + "expected": "URL이 /production/work-orders로 변경 (목록 페이지로 복귀)", + "validation": { + "notContains": ["404", "not-found", "error"] + } + }, + { + "step": 79, + "name": "목록 페이지 복귀 확인", + "action": "verifyText", + "selector": "h1", + "expected": "\"작업지시 목록\" 제목 표시" + }, + { + "step": 80, + "name": "최종 데이터 확인", + "action": "verifyElement", + "expected": "1건의 작업지시 (WO202601150001) 표시" + } + ], + "expectedAPIs": [ + { + "description": "작업지시 목록 조회", + "method": "GET", + "endpoint": "/api/production/work-orders", + "expectedStatus": 200 + }, + { + "description": "작업지시 상세 조회", + "method": "GET", + "endpoint": "/api/production/work-orders/1", + "expectedStatus": 200 + }, + { + "description": "수주 목록 조회 (작업지시 가능)", + "method": "GET", + "endpoint": "/api/sales/orders?status=registered", + "expectedStatus": 200 + }, + { + "description": "작업지시 등록", + "method": "POST", + "endpoint": "/api/production/work-orders", + "expectedStatus": 201 + }, + { + "description": "작업지시 수정", + "method": "PUT", + "endpoint": "/api/production/work-orders/1", + "expectedStatus": 200 + } + ] +} diff --git a/work-performance.json b/work-performance.json new file mode 100644 index 0000000..47872c0 --- /dev/null +++ b/work-performance.json @@ -0,0 +1,162 @@ +{ + "id": "work-performance", + "name": "작업실적 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "생산관리 > 작업실적 페이지의 작업실적 조회 및 엑셀 다운로드 기능을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/production/performance", + "menuNavigation": { + "level1": "생산관리", + "level2": "작업실적", + "expectedUrl": "/production/performance" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebar": { + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 5, + "waitAfterScroll": 300 + }, + "level1": { + "text": "생산관리", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "작업실적", + "waitAfterClick": 300 + }, + "fallbackUrl": "/production/performance", + "expectedUrl": "/production/performance" + }, + "timeout": 90000, + "tags": ["production", "performance", "read-only"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 500 } + ] + }, + { + "id": "step-1", + "name": "생산관리 메뉴 진입", + "description": "생산관리 > 작업실적 메뉴로 이동", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "생산관리", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "생산관리" }, + { "type": "wait", "duration": 500 }, + { "type": "click", "target": "작업실적" } + ], + "expect": { + "url": "/production/performance", + "visible": ["작업실적 조회", "엑셀 다운로드"] + }, + "fallback": { + "type": "navigate", + "url": "/production/performance" + } + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "통계 카드와 테이블 구조 확인", + "verify": { + "visible": ["총 생산수량", "양품수량", "불량수량", "불량률"], + "tableColumns": ["번호", "로트번호", "작업일", "작업지시번호", "공정", "품목명", "규격", "생산수량", "양품수량", "불량수량", "불량률", "검사", "포장", "작업자"] + } + }, + { + "id": "step-3", + "name": "필수 검증 #1: 엑셀 다운로드 버튼 확인", + "description": "엑셀 다운로드 버튼 존재 및 동작 확인", + "actions": [ + { "type": "click", "target": "엑셀 다운로드" }, + { "type": "wait", "duration": 1000 } + ], + "expect": { + "downloadTriggered": true, + "noErrorPage": true + }, + "verify": { + "apiCall": "GET /api/production/performance/export" + } + }, + { + "id": "step-4", + "name": "빈 상태 확인", + "description": "데이터가 없을 때 빈 상태 메시지 확인", + "verify": { + "emptyStateVisible": "검색 결과가 없습니다" + } + }, + { + "id": "step-5", + "name": "테이블 헤더 정렬 확인", + "description": "테이블 컬럼 헤더가 올바르게 표시되는지 확인", + "verify": { + "tableHeadersVisible": ["로트번호", "작업일", "작업지시번호", "공정", "품목명"] + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/production/performance", + "message": "작업실적 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "button:has-text('엑셀 다운로드')", + "message": "엑셀 다운로드 버튼이 표시되어야 함" + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 1, + "name": "파일 다운로드", + "trigger": "엑셀 다운로드 버튼", + "verification": "Network API + 실제 다운로드 확인", + "failCondition": "Console LOG만으로 PASS 금지" + } + ] + }, + + "notes": { + "testScope": "작업실적 조회 및 엑셀 다운로드 테스트", + "pageType": "조회 전용 (생산 과정에서 자동 생성된 데이터)", + "statsCards": ["총 생산수량", "양품수량", "불량수량", "불량률"], + "tableColumns": ["번호", "로트번호", "작업일", "작업지시번호", "공정", "품목명", "규격", "생산수량", "양품수량", "불량수량", "불량률", "검사", "포장", "작업자"], + "prerequisites": "로그인된 사용자, 작업실적 데이터 존재 시 테스트 가능" + } +} diff --git a/worker-screen.json b/worker-screen.json new file mode 100644 index 0000000..37c071f --- /dev/null +++ b/worker-screen.json @@ -0,0 +1,167 @@ +{ + "id": "worker-screen", + "name": "작업자 화면 테스트", + "screenshotPolicy": { + "onErrorOnly": true, + "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] + }, + "description": "생산관리 > 작업자 화면 페이지의 내 작업 목록 조회 기능을 테스트하는 E2E 테스트", + "baseUrl": "https://dev.codebridge-x.com", + "url": "/production/worker", + "menuNavigation": { + "level1": "생산관리", + "level2": "작업자 화면", + "expectedUrl": "/production/worker" + }, + "menuNavigationEnhanced": { + "strategy": "scroll-and-search", + "sidebar": { + "scrollContainer": ".sidebar-scroll", + "scrollStep": 200, + "maxScrollAttempts": 5, + "waitAfterScroll": 300 + }, + "level1": { + "text": "생산관리", + "expandable": true, + "waitAfterClick": 500 + }, + "level2": { + "text": "작업자 화면", + "waitAfterClick": 300 + }, + "fallbackUrl": "/production/worker", + "expectedUrl": "/production/worker" + }, + "timeout": 90000, + "tags": ["production", "worker", "read-only"], + + "login": { + "url": "https://dev.codebridge-x.com/login", + "credentials": { + "id": "TestUser5", + "password": "password123!" + }, + "successIndicator": "대시보드" + }, + + "steps": [ + { + "id": "step-0", + "name": "사이드바 초기화", + "description": "사이드바를 최상단으로 스크롤하여 메뉴 탐색 준비", + "actions": [ + { + "type": "evaluate", + "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" + }, + { "type": "wait", "duration": 500 } + ] + }, + { + "id": "step-1", + "name": "생산관리 메뉴 진입", + "description": "생산관리 > 작업자 화면 메뉴로 이동", + "actions": [ + { + "type": "scrollAndFind", + "container": ".sidebar-scroll", + "target": "생산관리", + "scrollStep": 200, + "maxAttempts": 5 + }, + { "type": "click", "target": "생산관리" }, + { "type": "wait", "duration": 500 }, + { "type": "click", "target": "작업자 화면" } + ], + "expect": { + "url": "/production/worker", + "visible": ["작업자 화면", "내 작업 목록"] + }, + "fallback": { + "type": "navigate", + "url": "/production/worker" + } + }, + { + "id": "step-2", + "name": "페이지 구조 확인", + "description": "통계 카드와 작업 목록 구조 확인", + "verify": { + "visible": ["할일", "작업중", "완료", "긴급"], + "elements": ["내 작업 목록", "납기일순"] + } + }, + { + "id": "step-3", + "name": "필수 검증 #3: 정렬 옵션 확인", + "description": "정렬 드롭다운 옵션 확인", + "actions": [ + { "type": "click", "target": "납기일순", "role": "combobox" } + ], + "expect": { + "optionsVisible": true + } + }, + { + "id": "step-4", + "name": "정렬 옵션 닫기", + "description": "ESC 키로 드롭다운 닫기", + "actions": [ + { "type": "press", "key": "Escape" }, + { "type": "wait", "duration": 300 } + ] + }, + { + "id": "step-5", + "name": "빈 상태 확인", + "description": "배정된 작업이 없을 때 빈 상태 메시지 확인", + "verify": { + "emptyStateVisible": "배정된 작업이 없습니다" + } + }, + { + "id": "step-6", + "name": "통계 카드 값 확인", + "description": "할일/작업중/완료/긴급 카운트 표시 확인", + "verify": { + "statsCards": ["할일", "작업중", "완료", "긴급"], + "countsDisplayed": true + } + } + ], + + "assertions": [ + { + "type": "url", + "expected": "/production/worker", + "message": "작업자 화면 페이지에 머물러야 함" + }, + { + "type": "elementExists", + "selector": "text=내 작업 목록", + "message": "내 작업 목록 제목이 표시되어야 함" + } + ], + + "mandatoryVerifications": { + "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", + "items": [ + { + "id": 3, + "name": "검색/필터", + "trigger": "정렬 드롭다운", + "verification": "옵션 표시 확인", + "failCondition": "드롭다운 미동작" + } + ] + }, + + "notes": { + "testScope": "작업자 본인에게 배정된 작업 목록 조회 테스트", + "pageType": "조회 전용 (작업지시 관리에서 배정된 작업 표시)", + "statsCards": ["할일", "작업중", "완료", "긴급"], + "sortOptions": ["납기일순"], + "prerequisites": "로그인된 사용자, 작업자에게 배정된 작업 존재 시 목록 표시" + } +}