refactor: 초정밀 시나리오 강화 (1060→1381 steps, 68/68 PASS)

This commit is contained in:
김보곤
2026-02-09 15:05:03 +09:00
parent 15ad1d9758
commit f5dffe2ee7
135 changed files with 23040 additions and 1652 deletions

View File

@@ -0,0 +1,259 @@
{
"id": "accounting-bad-debt",
"name": "악성채권추심관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "회계관리 > 악성채권추심관리 메뉴의 악성채권 CRUD 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "회계관리",
"level2": "악성채권추심관리",
"expectedUrl": "/accounting/bad-debt-collection",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"testData": {
"create": {
"vendorName": "E2E_TEST_채권거래처_{timestamp}",
"debtAmount": "1000000",
"status": "추심중"
}
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 회계관리 > 악성채권추심관리",
"action": "menu_navigate",
"level1": "회계관리",
"level2": "악성채권추심관리",
"expected": {
"url_contains": "/accounting",
"visible": ["악성채권", "추심"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"악성채권 목록 표시",
"채권 등록 버튼 존재",
"상태 필터 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "악성채권 테이블 구조 확인",
"action": "verify_table",
"checks": [
"거래처 컬럼",
"채권금액 컬럼",
"발생일 컬럼",
"상태 컬럼",
"담당자 컬럼"
],
"expected": "악성채권 테이블 표시"
},
{
"id": 4,
"name": "통계 카드 확인",
"action": "verify_elements",
"checks": [
"총 채권금액 표시",
"추심중 건수",
"회수완료 건수"
],
"expected": "통계 정보 표시"
},
{
"id": 5,
"phase": "CREATE",
"name": "[CREATE] 채권 등록 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('등록'), button:has-text('추가'), button:has-text('신규')",
"expected": {
"modal_open": true
}
},
{
"id": 6,
"phase": "CREATE",
"name": "[CREATE] 거래처 선택",
"action": "click_if_exists",
"target": "select[name*='vendor'], input[placeholder*='거래처']",
"expected": "거래처 선택 가능"
},
{
"id": 7,
"phase": "CREATE",
"name": "[CREATE] 채권금액 입력",
"action": "click_if_exists",
"target": "input[name*='amount'], input[placeholder*='금액']"
},
{
"id": 8,
"phase": "CREATE",
"name": "[CREATE] 필수 검증 #2: 채권 저장",
"action": "click_if_exists",
"target": "button:has-text('저장'), button:has-text('등록'), button:has-text('확인')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "POST /api/v1/accounting/bad-debts",
"toast": "등록|저장|완료|성공"
},
"expected": "채권 등록 완료"
},
{
"id": 9,
"phase": "READ",
"name": "[READ] 등록된 채권 검색",
"action": "click_if_exists",
"target": "input[type='search'], input[placeholder*='검색']"
},
{
"id": 10,
"phase": "READ",
"name": "[READ] 등록된 채권 확인",
"action": "verify_detail",
"checks": [
"E2E_TEST_채권거래처 목록에 표시"
],
"expected": "등록된 채권 확인"
},
{
"id": 11,
"phase": "READ",
"name": "[READ] 채권 상세 조회",
"action": "click_if_exists",
"target": "table tbody tr:has-text('E2E_TEST_채권거래처')",
"expected": {
"detail_view": true
}
},
{
"id": 12,
"name": "상세 정보 확인",
"action": "verify_elements",
"checks": [
"거래처 정보 표시",
"채권금액 표시",
"추심 이력 표시"
],
"expected": "상세 정보 표시"
},
{
"id": 13,
"phase": "UPDATE",
"name": "[UPDATE] 상태 변경",
"action": "click_if_exists",
"target": "button:has-text('상태변경'), select[name*='status']",
"expected": "상태 변경 가능"
},
{
"id": 14,
"phase": "UPDATE",
"name": "[UPDATE] 추심 메모 추가",
"action": "click_if_exists",
"target": "textarea[name*='memo'], textarea[placeholder*='메모']"
},
{
"id": 15,
"phase": "UPDATE",
"name": "[UPDATE] 변경 저장",
"action": "click_if_exists",
"target": "button:has-text('저장'), button:has-text('확인')",
"verify": {
"api_call": "PUT /api/v1/accounting/bad-debts",
"toast": "수정|저장|완료|성공"
},
"expected": "채권 정보 수정 완료"
},
{
"id": 16,
"phase": "DELETE",
"name": "[DELETE] 채권 삭제",
"action": "click_if_exists",
"target": "button:has-text('삭제'), button:has-text('제거')",
"expected": {
"confirm_dialog": true
}
},
{
"id": 17,
"phase": "DELETE",
"name": "[DELETE] 삭제 확인",
"action": "click_if_exists",
"target": "[role='alertdialog'] button:has-text('확인'), [role='dialog'] button:has-text('삭제')",
"verify": {
"api_call": "DELETE /api/v1/accounting/bad-debts",
"toast": "삭제|제거|완료|성공"
},
"expected": "채권 삭제 완료"
},
{
"id": 18,
"phase": "DELETE",
"name": "[DELETE] 삭제 확인",
"action": "verify_detail",
"checks": [
"E2E_TEST_채권거래처 목록에서 제거"
],
"expected": "채권 삭제 반영"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/accounting/bad-debts",
"description": "악성채권 목록 조회"
},
{
"method": "POST",
"endpoint": "/api/v1/accounting/bad-debts",
"description": "악성채권 등록"
},
{
"method": "GET",
"endpoint": "/api/v1/accounting/bad-debts/:id",
"description": "악성채권 상세 조회"
},
{
"method": "PUT",
"endpoint": "/api/v1/accounting/bad-debts/:id",
"description": "악성채권 수정"
},
{
"method": "DELETE",
"endpoint": "/api/v1/accounting/bad-debts/:id",
"description": "악성채권 삭제"
}
],
"requiredVerifications": [
{
"id": 2,
"name": "저장 버튼",
"steps": [8, 15],
"criteria": "API 호출 + 성공 토스트 + 데이터 반영"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "악성채권 목록, 등록 버튼, 상태 필터 존재"
}
],
"rollbackPlan": {
"onCreateFail": "등록 모달 닫고 재시도",
"onUpdateFail": "페이지 새로고침 후 재시도",
"onDeleteFail": "수동 삭제 필요",
"cleanupRequired": "E2E_TEST_채권거래처* 패턴 데이터 삭제"
}
}

View File

@@ -0,0 +1,195 @@
{
"id": "accounting-bank-transaction",
"name": "입출금계좌조회 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "회계관리 > 입출금계좌조회 메뉴의 계좌 거래내역 조회/필터/엑셀 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "회계관리",
"level2": "입출금계좌조회",
"expectedUrl": "/accounting/bank-transactions",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 회계관리 > 입출금계좌조회",
"action": "menu_navigate",
"level1": "회계관리",
"level2": "입출금계좌조회",
"expected": {
"url_contains": "/accounting",
"visible": ["입출금", "계좌"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"거래내역 목록 표시",
"계좌 선택 가능",
"기간 필터 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "거래내역 테이블 구조 확인",
"action": "verify_table",
"checks": [
"거래일 컬럼",
"입금 컬럼",
"출금 컬럼",
"잔액 컬럼",
"적요 컬럼"
],
"expected": "거래내역 테이블 표시"
},
{
"id": 4,
"name": "계좌 선택 드롭다운 확인",
"action": "verify_elements",
"checks": [
"계좌 선택 가능"
],
"expected": "계좌 선택 가능"
},
{
"id": 5,
"phase": "FILTER",
"name": "[FILTER] 기간 필터 - 시작일",
"action": "click_if_exists",
"target": "input[type='date']:first-of-type, [class*='datepicker']:first-of-type",
"expected": "날짜 선택 열림"
},
{
"id": 6,
"phase": "FILTER",
"name": "[FILTER] 기간 필터 적용",
"action": "click_if_exists",
"target": "button:has-text('조회'), button:has-text('검색'), button:has-text('적용')",
"expected": "필터 적용됨"
},
{
"id": 7,
"phase": "FILTER",
"name": "[FILTER] 필터 결과 확인",
"action": "verify_detail",
"checks": [
"필터된 데이터 표시 또는 결과 없음"
],
"expected": "필터 동작 확인"
},
{
"id": 8,
"phase": "READ",
"name": "[READ] 거래 상세 보기",
"action": "click_if_exists",
"target": "table tbody tr:first-child",
"expected": {
"detail_view": true
}
},
{
"id": 9,
"name": "상세 정보 확인",
"action": "verify_detail",
"checks": [
"거래일시 표시",
"금액 표시",
"적요 표시"
],
"expected": "거래 상세 정보 표시"
},
{
"id": 10,
"name": "목록으로 돌아가기",
"action": "click_if_exists",
"target": "button:has-text('목록'), a:has-text('목록'), [class*='back']",
"expected": "목록 페이지로 복귀"
},
{
"id": 11,
"name": "입금 합계 확인",
"action": "verify_elements",
"checks": [
"입금 합계 표시"
],
"expected": "입금 합계 표시"
},
{
"id": 12,
"name": "출금 합계 확인",
"action": "verify_elements",
"checks": [
"출금 합계 표시"
],
"expected": "출금 합계 표시"
},
{
"id": 13,
"name": "엑셀 다운로드 버튼 확인",
"action": "verify_elements",
"checks": [
"엑셀 다운로드 버튼 존재"
],
"expected": "엑셀 다운로드 기능 표시"
},
{
"id": 14,
"name": "인쇄 버튼 확인",
"action": "verify_elements",
"checks": [
"인쇄 버튼 존재"
],
"expected": "인쇄 기능 표시"
},
{
"id": 15,
"name": "페이지네이션 확인",
"action": "verify_elements",
"checks": [
"페이지 번호 표시"
],
"expected": "페이지네이션 표시"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/accounting/bank-transactions",
"description": "입출금 내역 조회"
},
{
"method": "GET",
"endpoint": "/api/v1/accounting/bank-accounts",
"description": "계좌 목록 조회"
}
],
"requiredVerifications": [
{
"id": 3,
"name": "검색/필터",
"steps": [5, 6, 7],
"criteria": "기간 필터 동작"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "거래내역 목록, 계좌 선택, 기간 필터 존재"
}
],
"rollbackPlan": {
"note": "조회 전용 페이지로 데이터 변경 없음"
}
}

View File

@@ -0,0 +1,293 @@
{
"id": "accounting-bill",
"name": "어음관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "회계관리 > 어음관리 메뉴의 어음 조회/등록/수정/삭제 전체 CRUD 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "회계관리",
"level2": "어음관리",
"expectedUrl": "/accounting/bills",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"testData": {
"create": {
"billNumber": "E2E_TEST_어음",
"amount": "1000000",
"dueDate": "2026-03-15",
"issuer": "테스트발행처",
"memo": "E2E 자동화 테스트 어음"
},
"update": {
"memo": "E2E 수정된 어음 메모"
}
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 회계관리 > 어음관리",
"action": "menu_navigate",
"level1": "회계관리",
"level2": "어음관리",
"expected": {
"url_contains": "/accounting/bills",
"visible": ["어음관리", "어음"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"어음 목록 표시",
"어음 등록 버튼 존재",
"검색/필터 기능 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "어음 테이블 구조 확인",
"action": "verify_table",
"checks": [
"어음번호 컬럼",
"금액 컬럼",
"만기일 컬럼",
"발행처 컬럼",
"상태 컬럼"
],
"expected": "어음 테이블 컬럼 정상 표시"
},
{
"id": 4,
"name": "검색 기능 테스트",
"action": "click_if_exists",
"target": "input[placeholder*='검색']",
"value": "테스트",
"expected": {
"data_filtered": true
}
},
{
"id": 5,
"phase": "CREATE",
"name": "[CREATE] 어음 등록 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('등록'), button:has-text('어음 등록'), button:has-text('추가')",
"expected": {
"modal": true,
"modalTitle": "어음 등록"
}
},
{
"id": 6,
"phase": "CREATE",
"name": "[CREATE] 어음 정보 입력",
"action": "fill_form",
"fields": [
{"name": "어음번호", "type": "text", "value": "E2E_TEST_어음_{timestamp}"},
{"name": "금액", "type": "number", "value": "1000000"},
{"name": "만기일", "type": "date", "value": "2026-03-15"},
{"name": "발행처", "type": "text", "value": "테스트발행처"},
{"name": "메모", "type": "text", "value": "E2E 자동화 테스트 어음"}
],
"note": "타임스탬프로 고유성 보장"
},
{
"id": 7,
"phase": "CREATE",
"name": "[CREATE] 필수 검증 #2: 등록 저장",
"action": "click_if_exists",
"target": "button:has-text('저장'), button:has-text('등록')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "POST /api/v1/bills",
"toast": "등록|완료|성공"
},
"expected": "어음 등록 완료"
},
{
"id": "7-modal-close",
"phase": "CREATE",
"name": "[CREATE] 모달 닫기 확인",
"action": "close_modal_if_open",
"expected": "모달 닫힘"
},
{
"id": 8,
"phase": "CREATE",
"name": "[CREATE] 등록 결과 확인",
"action": "verify_detail",
"search": "E2E_TEST_어음",
"expected": {
"row_exists": true,
"contains": ["E2E", "1,000,000"]
}
},
{
"id": 9,
"phase": "READ",
"name": "[READ] 어음 상세 페이지 진입",
"action": "click_if_exists",
"target": "table tbody tr:has-text('E2E')",
"expected": {
"url_contains": "/accounting/bills/",
"visible": ["어음 상세", "수정", "삭제"]
}
},
{
"id": 10,
"phase": "READ",
"name": "[READ] 상세 정보 확인",
"action": "verify_detail",
"checks": [
"어음번호: E2E_TEST_어음",
"금액: 1,000,000",
"만기일: 2026-03-15",
"발행처: 테스트발행처"
],
"expected": "입력한 데이터와 일치"
},
{
"id": 11,
"phase": "UPDATE",
"name": "[UPDATE] 수정 모드 진입",
"action": "click_if_exists",
"target": "button:has-text('수정')",
"expected": {
"url_contains": "mode=edit",
"fields_editable": true
}
},
{
"id": 12,
"phase": "UPDATE",
"name": "[UPDATE] 메모 수정",
"action": "click_if_exists",
"target": "textarea[name*='memo'], input[placeholder*='메모']",
"value": "E2E 수정된 어음 메모_{timestamp}",
"clear": true
},
{
"id": 13,
"phase": "UPDATE",
"name": "[UPDATE] 필수 검증 #2: 수정 저장",
"action": "click_if_exists",
"target": "button:has-text('저장')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "PUT /api/v1/bills/",
"toast": "수정|완료|성공"
},
"expected": "수정 완료"
},
{
"id": 14,
"phase": "UPDATE",
"name": "[UPDATE] 수정 결과 확인",
"action": "verify_detail",
"checks": [
"메모: E2E 수정된 어음"
],
"expected": "수정된 데이터 반영"
},
{
"id": 15,
"phase": "DELETE",
"name": "[DELETE] 삭제 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('삭제')",
"expected": {
"confirm_dialog": true,
"dialog_message": "삭제|정말"
}
},
{
"id": 16,
"phase": "DELETE",
"name": "[DELETE] 필수 검증 #6: 삭제 확인",
"action": "click_if_exists",
"target": "button:has-text('확인'), button:has-text('삭제')",
"verify": {
"api_call": "DELETE /api/v1/bills/",
"toast": "삭제|완료|성공",
"redirect": "/accounting/bills"
},
"expected": "삭제 완료 및 목록 복귀"
},
{
"id": 17,
"phase": "DELETE",
"name": "[DELETE] 삭제 결과 확인",
"action": "verify_detail",
"search": "E2E 수정된 어음",
"expected": {
"row_exists": false,
"message": "테스트 어음이 목록에서 제거됨"
}
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/bills",
"description": "어음 목록 조회"
},
{
"method": "POST",
"endpoint": "/api/v1/bills",
"description": "어음 등록"
},
{
"method": "GET",
"endpoint": "/api/v1/bills/{id}",
"description": "어음 상세 조회"
},
{
"method": "PUT",
"endpoint": "/api/v1/bills/{id}",
"description": "어음 수정"
},
{
"method": "DELETE",
"endpoint": "/api/v1/bills/{id}",
"description": "어음 삭제"
}
],
"requiredVerifications": [
{
"id": 2,
"name": "등록/저장 버튼",
"steps": [7, 13],
"criteria": "API 호출 + 성공 토스트 + 데이터 반영"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "어음 목록, 등록 버튼, 필터 존재"
},
{
"id": 6,
"name": "삭제 기능",
"steps": [15, 16, 17],
"criteria": "DELETE API + 목록에서 제거"
}
],
"rollbackPlan": {
"onCreateFail": "모달 닫기",
"onUpdateFail": "테스트 어음 수동 삭제 필요",
"onDeleteFail": "테스트 어음 수동 삭제 필요",
"cleanupRequired": "E2E_TEST_ 접두사 어음은 테스트 데이터"
}
}

View File

@@ -0,0 +1,196 @@
{
"id": "accounting-card-history",
"name": "카드내역조회 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "회계관리 > 카드내역조회 메뉴의 카드 사용내역 조회/필터/엑셀 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "회계관리",
"level2": "카드내역조회",
"expectedUrl": "/accounting/card-transactions",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 회계관리 > 카드내역조회",
"action": "menu_navigate",
"level1": "회계관리",
"level2": "카드내역조회",
"expected": {
"url_contains": "/accounting",
"visible": ["카드", "내역"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"카드 사용내역 목록 표시",
"카드 선택 가능",
"기간 필터 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "카드내역 테이블 구조 확인",
"action": "verify_table",
"checks": [
"사용일 컬럼",
"카드명 컬럼",
"가맹점 컬럼",
"금액 컬럼",
"승인번호 컬럼"
],
"expected": "카드내역 테이블 표시"
},
{
"id": 4,
"name": "카드 선택 드롭다운 확인",
"action": "verify_elements",
"checks": [
"카드 선택 가능"
],
"expected": "카드 선택 가능"
},
{
"id": 5,
"phase": "FILTER",
"name": "[FILTER] 기간 필터 - 시작일",
"action": "click_if_exists",
"target": "input[type='date']:first-of-type, [class*='datepicker']:first-of-type",
"expected": "날짜 선택 열림"
},
{
"id": 6,
"phase": "FILTER",
"name": "[FILTER] 기간 필터 적용",
"action": "click_if_exists",
"target": "button:has-text('조회'), button:has-text('검색'), button:has-text('적용')",
"expected": "필터 적용됨"
},
{
"id": 7,
"phase": "FILTER",
"name": "[FILTER] 필터 결과 확인",
"action": "verify_detail",
"checks": [
"필터된 데이터 표시 또는 결과 없음"
],
"expected": "필터 동작 확인"
},
{
"id": 8,
"phase": "READ",
"name": "[READ] 카드 사용내역 상세 보기",
"action": "click_if_exists",
"target": "table tbody tr:first-child",
"expected": {
"detail_view": true
}
},
{
"id": 9,
"name": "상세 정보 확인",
"action": "verify_detail",
"checks": [
"사용일시 표시",
"가맹점 정보 표시",
"금액 표시",
"승인번호 표시"
],
"expected": "카드 사용 상세 정보 표시"
},
{
"id": 10,
"name": "목록으로 돌아가기",
"action": "click_if_exists",
"target": "button:has-text('목록'), a:has-text('목록'), [class*='back']",
"expected": "목록 페이지로 복귀"
},
{
"id": 11,
"name": "사용금액 합계 확인",
"action": "verify_elements",
"checks": [
"합계 금액 표시"
],
"expected": "사용금액 합계 표시"
},
{
"id": 12,
"name": "카드별 사용 현황 확인",
"action": "verify_elements",
"checks": [
"카드별 사용 현황 또는 통계"
],
"expected": "카드별 현황 표시"
},
{
"id": 13,
"name": "엑셀 다운로드 버튼 확인",
"action": "verify_elements",
"checks": [
"엑셀 다운로드 버튼 존재"
],
"expected": "엑셀 다운로드 기능 표시"
},
{
"id": 14,
"name": "인쇄 버튼 확인",
"action": "verify_elements",
"checks": [
"인쇄 버튼 존재"
],
"expected": "인쇄 기능 표시"
},
{
"id": 15,
"name": "페이지네이션 확인",
"action": "verify_elements",
"checks": [
"페이지 번호 표시"
],
"expected": "페이지네이션 표시"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/accounting/card-transactions",
"description": "카드 사용내역 조회"
},
{
"method": "GET",
"endpoint": "/api/v1/accounting/cards",
"description": "카드 목록 조회"
}
],
"requiredVerifications": [
{
"id": 3,
"name": "검색/필터",
"steps": [5, 6, 7],
"criteria": "기간 필터 동작"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "카드내역 목록, 카드 선택, 기간 필터 존재"
}
],
"rollbackPlan": {
"note": "조회 전용 페이지로 데이터 변경 없음"
}
}

View File

@@ -0,0 +1,283 @@
{
"id": "accounting-client",
"name": "회계거래처관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
},
"description": "회계관리 > 거래처관리 메뉴의 거래처 CRUD 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "회계관리",
"level2": "거래처관리",
"expectedUrl": "/accounting/vendors",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"testData": {
"create": {
"vendorName": "E2E_TEST_회계거래처_{timestamp}",
"vendorType": "매출",
"creditGrade": "A"
}
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 회계관리 > 거래처관리",
"action": "menu_navigate",
"level1": "회계관리",
"level2": "거래처관리",
"expected": {
"url_contains": "/accounting/vendors",
"visible": [
"거래처관리",
"거래처"
]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"거래처 목록 표시",
"거래처 등록 버튼 존재",
"검색 기능 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "통계 카드 확인",
"action": "verify_elements",
"checks": [
"전체 거래처 카드",
"매출 거래처 카드",
"매입 거래처 카드"
],
"expected": "통계 카드 표시"
},
{
"id": 4,
"name": "거래처 테이블 구조 확인",
"action": "verify_table",
"checks": [
"구분 컬럼",
"거래처명 컬럼",
"신용등급 컬럼",
"거래등급 컬럼",
"사업자등록번호 컬럼"
],
"expected": "거래처 테이블 표시"
},
{
"id": 5,
"phase": "CREATE",
"name": "[CREATE] 거래처 등록 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('등록'), button:has-text('추가'), button:has-text('신규')",
"expected": {
"modal_open": true
}
},
{
"id": 6,
"phase": "CREATE",
"name": "[CREATE] 거래처명 입력",
"action": "click_if_exists",
"target": "input[name*='name'], input[placeholder*='거래처명']",
"value": "E2E_TEST_회계거래처_{timestamp}",
"clear": true
},
{
"id": 7,
"phase": "CREATE",
"name": "[CREATE] 거래처 구분 선택",
"action": "click_if_exists",
"target": "select[name*='type'], button:has-text('매출')",
"expected": "거래처 구분 선택"
},
{
"id": 8,
"phase": "CREATE",
"name": "[CREATE] 필수 검증 #2: 거래처 저장",
"action": "click_if_exists",
"target": "button:has-text('저장'), button:has-text('등록'), button:has-text('확인')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "POST /api/v1/accounting/vendors",
"toast": "등록|저장|완료|성공"
},
"expected": "거래처 등록 완료"
},
{
"id": 9,
"phase": "READ",
"name": "[READ] 등록된 거래처 검색",
"action": "click_if_exists",
"target": "input[type='search'], input[placeholder*='검색']",
"value": "E2E_TEST_회계거래처",
"submit": true
},
{
"id": 10,
"phase": "READ",
"name": "[READ] 등록된 거래처 목록 확인",
"action": "verify_detail",
"checks": [
"E2E_TEST_회계거래처 목록에 표시"
],
"expected": "등록된 거래처 확인"
},
{
"id": 11,
"phase": "READ",
"name": "[READ] 거래처 상세 조회",
"action": "click_if_exists",
"target": "table tbody tr:has-text('E2E_TEST_회계거래처')",
"expected": {
"detail_view": true,
"url_change": true
}
},
{
"id": 12,
"name": "상세 페이지 정보 확인",
"action": "verify_elements",
"checks": [
"거래처명 표시",
"사업자등록번호 표시",
"연락처 정보"
],
"expected": "상세 정보 표시"
},
{
"id": 13,
"phase": "UPDATE",
"name": "[UPDATE] 수정 모드 진입",
"action": "click_if_exists",
"target": "button:has-text('수정'), button:has-text('편집')",
"expected": {
"edit_mode": true
}
},
{
"id": 14,
"phase": "UPDATE",
"name": "[UPDATE] 거래처 정보 수정",
"action": "click_if_exists",
"target": "input[name*='name'], input[placeholder*='거래처명']",
"value": "E2E_TEST_회계거래처_수정",
"clear": true
},
{
"id": 15,
"phase": "UPDATE",
"name": "[UPDATE] 거래처 저장",
"action": "click_if_exists",
"target": "button:has-text('저장'), button:has-text('확인')",
"verify": {
"api_call": "PUT /api/v1/accounting/vendors",
"toast": "수정|저장|완료|성공"
},
"expected": "거래처 수정 완료"
},
{
"id": 16,
"phase": "DELETE",
"name": "[DELETE] 거래처 삭제",
"action": "click_if_exists",
"target": "button:has-text('삭제'), button:has-text('제거')",
"expected": {
"confirm_dialog": true
}
},
{
"id": 17,
"phase": "DELETE",
"name": "[DELETE] 삭제 확인",
"action": "click_if_exists",
"target": "[role='alertdialog'] button:has-text('확인'), [role='dialog'] button:has-text('삭제')",
"verify": {
"api_call": "DELETE /api/v1/accounting/vendors",
"toast": "삭제|제거|완료|성공"
},
"expected": "거래처 삭제 완료"
},
{
"id": 18,
"phase": "DELETE",
"name": "[DELETE] 삭제 확인",
"action": "verify_detail",
"checks": [
"E2E_TEST_회계거래처 목록에서 제거"
],
"expected": "거래처 삭제 반영"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/accounting/vendors",
"description": "거래처 목록 조회"
},
{
"method": "POST",
"endpoint": "/api/v1/accounting/vendors",
"description": "거래처 등록"
},
{
"method": "GET",
"endpoint": "/api/v1/accounting/vendors/:id",
"description": "거래처 상세 조회"
},
{
"method": "PUT",
"endpoint": "/api/v1/accounting/vendors/:id",
"description": "거래처 수정"
},
{
"method": "DELETE",
"endpoint": "/api/v1/accounting/vendors/:id",
"description": "거래처 삭제"
}
],
"requiredVerifications": [
{
"id": 2,
"name": "저장 버튼",
"steps": [
8,
15
],
"criteria": "API 호출 + 성공 토스트 + 데이터 반영"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [
2
],
"criteria": "거래처 목록, 등록 버튼, 검색 기능 존재"
}
],
"rollbackPlan": {
"onCreateFail": "등록 모달 닫고 재시도",
"onUpdateFail": "페이지 새로고침 후 재시도",
"onDeleteFail": "수동 삭제 필요",
"cleanupRequired": "E2E_TEST_회계거래처* 패턴 데이터 삭제"
}
}

View File

@@ -0,0 +1,302 @@
{
"id": "accounting-deposit",
"name": "입금관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "회계관리 > 입금관리 메뉴의 입금 조회/등록/수정/삭제 전체 CRUD 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "회계관리",
"level2": "입금관리",
"expectedUrl": "/accounting/deposits",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"testData": {
"create": {
"vendorName": "E2E_TEST_입금거래처",
"amount": "100000",
"depositMethod": "계좌이체",
"memo": "E2E 자동화 테스트 입금"
},
"update": {
"amount": "150000",
"memo": "E2E 수정된 입금 메모"
}
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 회계관리 > 입금관리",
"action": "menu_navigate",
"level1": "회계관리",
"level2": "입금관리",
"expected": {
"url_contains": "/accounting/deposits",
"visible": ["입금관리", "입금"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"입금 목록 표시",
"입금 등록 버튼 존재",
"검색/필터 기능 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "입금 테이블 구조 확인",
"action": "verify_table",
"checks": [
"입금일 컬럼",
"거래처 컬럼",
"금액 컬럼",
"입금방법 컬럼",
"상태 컬럼"
],
"expected": "입금 테이블 컬럼 정상 표시"
},
{
"id": 4,
"name": "검색 기능 테스트",
"action": "click_if_exists",
"target": "input[placeholder*='검색']",
"value": "테스트",
"expected": {
"data_filtered": true
}
},
{
"id": 5,
"phase": "CREATE",
"name": "[CREATE] 입금 등록 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('등록'), button:has-text('입금 등록'), button:has-text('추가')",
"expected": {
"modal": true,
"modalTitle": "입금 등록"
}
},
{
"id": 6,
"phase": "CREATE",
"name": "[CREATE] 입금 정보 입력",
"action": "fill_form",
"fields": [
{"name": "거래처", "type": "select", "value": "E2E_TEST_입금거래처"},
{"name": "입금일", "type": "date", "value": "2026-02-03"},
{"name": "금액", "type": "number", "value": "100000"},
{"name": "입금방법", "type": "select", "value": "계좌이체"},
{"name": "메모", "type": "text", "value": "E2E 자동화 테스트 입금_{timestamp}"}
],
"note": "타임스탬프로 고유성 보장"
},
{
"id": 7,
"phase": "CREATE",
"name": "[CREATE] 필수 검증 #2: 등록 저장",
"action": "click_if_exists",
"target": "button:has-text('저장'), button:has-text('등록')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "POST /api/v1/deposits",
"toast": "등록|완료|성공"
},
"expected": "입금 등록 완료"
},
{
"id": "7-modal-close",
"phase": "CREATE",
"name": "[CREATE] 모달 닫기 확인",
"action": "close_modal_if_open",
"expected": "모달 닫힘"
},
{
"id": 8,
"phase": "CREATE",
"name": "[CREATE] 등록 결과 확인",
"action": "verify_detail",
"search": "E2E 자동화 테스트 입금",
"expected": {
"row_exists": true,
"contains": ["E2E", "100,000"]
}
},
{
"id": 9,
"phase": "READ",
"name": "[READ] 입금 상세 페이지 진입",
"action": "click_if_exists",
"target": "table tbody tr:has-text('E2E')",
"expected": {
"url_contains": "/accounting/deposits/",
"visible": ["입금 상세", "수정", "삭제"]
}
},
{
"id": 10,
"phase": "READ",
"name": "[READ] 상세 정보 확인",
"action": "verify_detail",
"checks": [
"금액: 100,000",
"입금방법: 계좌이체",
"메모: E2E 자동화 테스트"
],
"expected": "입력한 데이터와 일치"
},
{
"id": 11,
"phase": "UPDATE",
"name": "[UPDATE] 수정 모드 진입",
"action": "click_if_exists",
"target": "button:has-text('수정')",
"expected": {
"url_contains": "mode=edit",
"fields_editable": true
}
},
{
"id": 12,
"phase": "UPDATE",
"name": "[UPDATE] 금액 수정",
"action": "click_if_exists",
"target": "input[name*='amount'], input[placeholder*='금액']",
"value": "150000",
"clear": true
},
{
"id": 13,
"phase": "UPDATE",
"name": "[UPDATE] 메모 수정",
"action": "click_if_exists",
"target": "textarea[name*='memo'], input[placeholder*='메모']",
"value": "E2E 수정된 입금 메모_{timestamp}",
"clear": true
},
{
"id": 14,
"phase": "UPDATE",
"name": "[UPDATE] 필수 검증 #2: 수정 저장",
"action": "click_if_exists",
"target": "button:has-text('저장')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "PUT /api/v1/deposits/",
"toast": "수정|완료|성공"
},
"expected": "수정 완료"
},
{
"id": 15,
"phase": "UPDATE",
"name": "[UPDATE] 수정 결과 확인",
"action": "verify_detail",
"checks": [
"금액: 150,000",
"메모: E2E 수정된 입금"
],
"expected": "수정된 데이터 반영"
},
{
"id": 16,
"phase": "DELETE",
"name": "[DELETE] 삭제 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('삭제')",
"expected": {
"confirm_dialog": true,
"dialog_message": "삭제|정말"
}
},
{
"id": 17,
"phase": "DELETE",
"name": "[DELETE] 필수 검증 #6: 삭제 확인",
"action": "click_if_exists",
"target": "button:has-text('확인'), button:has-text('삭제')",
"verify": {
"api_call": "DELETE /api/v1/deposits/",
"toast": "삭제|완료|성공",
"redirect": "/accounting/deposits"
},
"expected": "삭제 완료 및 목록 복귀"
},
{
"id": 18,
"phase": "DELETE",
"name": "[DELETE] 삭제 결과 확인",
"action": "verify_detail",
"search": "E2E 수정된 입금",
"expected": {
"row_exists": false,
"message": "테스트 데이터가 목록에서 제거됨"
}
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/deposits",
"description": "입금 목록 조회"
},
{
"method": "POST",
"endpoint": "/api/v1/deposits",
"description": "입금 등록"
},
{
"method": "GET",
"endpoint": "/api/v1/deposits/{id}",
"description": "입금 상세 조회"
},
{
"method": "PUT",
"endpoint": "/api/v1/deposits/{id}",
"description": "입금 수정"
},
{
"method": "DELETE",
"endpoint": "/api/v1/deposits/{id}",
"description": "입금 삭제"
}
],
"requiredVerifications": [
{
"id": 2,
"name": "등록/저장 버튼",
"steps": [7, 14],
"criteria": "API 호출 + 성공 토스트 + 데이터 반영"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "입금 목록, 등록 버튼, 필터 존재"
},
{
"id": 6,
"name": "삭제 기능",
"steps": [16, 17, 18],
"criteria": "DELETE API + 목록에서 제거"
}
],
"rollbackPlan": {
"onCreateFail": "모달 닫기 → 다음 테스트 영향 없음",
"onUpdateFail": "테스트 데이터 수동 삭제 필요",
"onDeleteFail": "테스트 데이터 수동 삭제 필요",
"cleanupRequired": "E2E_TEST_ 접두사 데이터는 테스트 데이터"
}
}

View File

@@ -0,0 +1,193 @@
{
"id": "accounting-expense-forecast",
"name": "지출예상내역서 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "회계관리 > 지출예상내역서 메뉴의 지출 예상 조회/필터/인쇄 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "회계관리",
"level2": "지출예상내역서",
"expectedUrl": "/accounting/expected-expenses",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 회계관리 > 지출예상내역서",
"action": "menu_navigate",
"level1": "회계관리",
"level2": "지출예상내역서",
"expected": {
"url_contains": "/accounting",
"visible": ["지출", "예상"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"지출 예상 내역 표시",
"기간 선택 가능",
"합계 금액 표시"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "지출예상 테이블 구조 확인",
"action": "verify_table",
"checks": [
"지출 항목 컬럼",
"예상일 컬럼",
"금액 컬럼",
"비고 컬럼"
],
"expected": "지출예상 테이블 표시"
},
{
"id": 4,
"name": "기간 선택 확인",
"action": "verify_elements",
"checks": [
"기간 선택 가능 (월/주/일)"
],
"expected": "기간 선택 가능"
},
{
"id": 5,
"phase": "FILTER",
"name": "[FILTER] 월 선택",
"action": "click_if_exists",
"target": "input[type='month'], select[name*='month'], [class*='month-picker']",
"expected": "월 선택 열림"
},
{
"id": 6,
"phase": "FILTER",
"name": "[FILTER] 조회 적용",
"action": "click_if_exists",
"target": "button:has-text('조회'), button:has-text('검색'), button:has-text('적용')",
"expected": "필터 적용됨"
},
{
"id": 7,
"phase": "FILTER",
"name": "[FILTER] 필터 결과 확인",
"action": "verify_detail",
"checks": [
"선택한 기간의 지출 예상 표시"
],
"expected": "필터 동작 확인"
},
{
"id": 8,
"name": "지출 카테고리별 확인",
"action": "verify_elements",
"checks": [
"카테고리별 지출 예상 표시"
],
"expected": "카테고리별 지출 표시"
},
{
"id": 9,
"name": "합계 금액 확인",
"action": "verify_detail",
"checks": [
"총 지출 예상 금액 표시"
],
"expected": "합계 금액 표시"
},
{
"id": 10,
"name": "일별 지출 예상 확인",
"action": "verify_elements",
"checks": [
"일별 지출 예상 캘린더 또는 리스트"
],
"expected": "일별 지출 표시"
},
{
"id": 11,
"name": "주요 지출 항목 확인",
"action": "verify_elements",
"checks": [
"주요 지출 항목 하이라이트"
],
"expected": "주요 항목 표시"
},
{
"id": 12,
"name": "인쇄 버튼 확인",
"action": "verify_elements",
"checks": [
"인쇄 버튼 존재"
],
"expected": "인쇄 기능 표시"
},
{
"id": 13,
"name": "엑셀 다운로드 버튼 확인",
"action": "verify_elements",
"checks": [
"엑셀 다운로드 버튼 존재"
],
"expected": "엑셀 다운로드 기능 표시"
},
{
"id": 14,
"name": "PDF 내보내기 확인",
"action": "verify_elements",
"checks": [
"PDF 내보내기 버튼 존재"
],
"expected": "PDF 내보내기 기능 표시"
},
{
"id": 15,
"name": "이전/다음 기간 네비게이션",
"action": "verify_elements",
"checks": [
"이전/다음 기간 이동 가능"
],
"expected": "기간 네비게이션 표시"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/accounting/expected-expenses",
"description": "지출예상 목록 조회"
},
{
"method": "GET",
"endpoint": "/api/v1/accounting/expected-expenses/summary",
"description": "지출예상 요약 조회"
}
],
"requiredVerifications": [
{
"id": 3,
"name": "검색/필터",
"steps": [5, 6, 7],
"criteria": "기간 필터 동작"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "지출예상 목록, 기간 선택, 합계 표시 존재"
}
],
"rollbackPlan": {
"note": "조회 전용 페이지로 데이터 변경 없음"
}
}

View File

@@ -0,0 +1,211 @@
{
"id": "accounting-payment",
"name": "결제내역 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "회계관리 > 결제내역 메뉴의 결제 내역 조회/필터/검색/다운로드 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "회계관리",
"level2": "결제내역",
"expectedUrl": "/payment-history",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 회계관리 > 결제내역",
"action": "menu_navigate",
"level1": "회계관리",
"level2": "결제내역",
"expected": {
"url_contains": "/payment",
"visible": ["결제내역", "결제"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"결제 내역 목록 표시",
"기간 필터 존재",
"검색 기능 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "결제내역 페이지 구조 확인",
"action": "verify_elements",
"checks": [
"시작일 선택",
"종료일 선택",
"결제방법 필터",
"조회 버튼"
],
"expected": "결제내역 조회 폼 정상 표시"
},
{
"id": 4,
"phase": "FILTER",
"name": "[FILTER] 기간 필터 - 시작일",
"action": "click_if_exists",
"target": "input[type='date']:first-of-type, input[name*='start']",
"value": "2025-01-01",
"expected": "시작일 입력"
},
{
"id": 5,
"phase": "FILTER",
"name": "[FILTER] 기간 필터 - 종료일",
"action": "click_if_exists",
"target": "input[type='date']:last-of-type, input[name*='end']",
"value": "2025-12-31",
"expected": "종료일 입력"
},
{
"id": 6,
"phase": "FILTER",
"name": "[FILTER] 조회 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('조회'), button:has-text('검색')",
"expected": {
"data_loaded": true,
"api_call": "GET /api/v1/payments"
}
},
{
"id": 7,
"phase": "READ",
"name": "[READ] 결제 테이블 구조 확인",
"action": "verify_table",
"checks": [
"결제일 컬럼 표시",
"결제금액 컬럼 표시",
"결제방법 컬럼 표시",
"상태 컬럼 표시"
],
"expected": "결제 테이블 정상 표시"
},
{
"id": 8,
"phase": "READ",
"name": "[READ] 결제 데이터 표시 확인",
"action": "verify_detail",
"checks": [
"테이블에 데이터 행 존재 또는 '데이터 없음' 메시지",
"금액 포맷 정상"
],
"expected": "결제 데이터 정상 표시"
},
{
"id": 9,
"phase": "FILTER",
"name": "[FILTER] 결제방법 필터 테스트",
"action": "click_if_exists",
"target": "select[name*='method'], button:has-text('결제방법'), [class*='filter']:has-text('방법')",
"expected": "결제방법 필터 옵션 표시"
},
{
"id": 10,
"phase": "FILTER",
"name": "[FILTER] 상태 필터 테스트",
"action": "verify_elements",
"checks": [
"완료/취소/대기 등 상태 필터 가능"
],
"expected": "상태 필터 기능 확인"
},
{
"id": 11,
"phase": "READ",
"name": "[READ] 결제 상세 조회",
"action": "click_if_exists",
"target": "table tbody tr:first-child, [class*='list'] [class*='item']:first-child",
"expected": {
"detail_view": true
}
},
{
"id": 12,
"name": "결제 상세 정보 확인",
"action": "verify_detail",
"checks": [
"결제일시 상세",
"결제금액 상세",
"결제방법 상세",
"거래번호/승인번호"
],
"expected": "결제 상세 정보 표시"
},
{
"id": 13,
"name": "목록으로 돌아가기",
"action": "click_if_exists",
"target": "button:has-text('목록'), button:has-text('뒤로'), [class*='back']",
"expected": "목록 페이지로 복귀"
},
{
"id": 14,
"name": "필수 검증 #1: 엑셀 다운로드",
"action": "click_if_exists",
"target": "button:has-text('엑셀'), button:has-text('Excel'), button:has-text('다운로드')",
"verify": {
"api_call": "GET /api/v1/payments/export",
"file_download": true
},
"expected": "엑셀 파일 다운로드"
},
{
"id": 15,
"name": "합계 금액 표시 확인",
"action": "verify_elements",
"checks": [
"총 결제금액 합계 표시"
],
"expected": "합계 영역 표시"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/payments",
"description": "결제내역 목록 조회"
},
{
"method": "GET",
"endpoint": "/api/v1/payments/:id",
"description": "결제 상세 조회"
},
{
"method": "GET",
"endpoint": "/api/v1/payments/export",
"description": "결제내역 엑셀 다운로드"
}
],
"requiredVerifications": [
{
"id": 1,
"name": "엑셀 다운로드",
"steps": [14],
"criteria": "API 호출 + 파일 다운로드"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "결제 내역 목록, 기간 필터, 검색 기능 존재"
}
],
"rollbackPlan": {
"note": "조회 전용 페이지로 데이터 변경 없음"
}
}

View File

@@ -0,0 +1,196 @@
{
"id": "accounting-purchase",
"name": "매입관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "회계관리 > 매입관리 메뉴의 매입 조회/필터/엑셀 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "회계관리",
"level2": "매입관리",
"expectedUrl": "/accounting/purchase-accounting",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 회계관리 > 매입관리",
"action": "menu_navigate",
"level1": "회계관리",
"level2": "매입관리",
"expected": {
"url_contains": "/accounting",
"visible": ["매입관리", "매입"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"매입 목록 표시",
"기간 필터 존재",
"합계 금액 표시"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "매입 테이블 구조 확인",
"action": "verify_table",
"checks": [
"거래일 컬럼",
"거래처 컬럼",
"품목 컬럼",
"금액 컬럼",
"부가세 컬럼"
],
"expected": "매입 테이블 표시"
},
{
"id": 4,
"name": "매입 통계 카드 확인",
"action": "verify_elements",
"checks": [
"총 매입액 표시",
"당월 매입 표시",
"전월 대비 표시"
],
"expected": "통계 카드 표시"
},
{
"id": 5,
"phase": "FILTER",
"name": "[FILTER] 기간 필터 - 시작일",
"action": "click_if_exists",
"target": "input[type='date']:first-of-type, [class*='datepicker']:first-of-type",
"expected": "날짜 선택 열림"
},
{
"id": 6,
"phase": "FILTER",
"name": "[FILTER] 기간 필터 적용",
"action": "click_if_exists",
"target": "button:has-text('조회'), button:has-text('검색'), button:has-text('적용')",
"expected": "필터 적용됨"
},
{
"id": 7,
"phase": "FILTER",
"name": "[FILTER] 필터 결과 확인",
"action": "verify_detail",
"checks": [
"필터된 매입 데이터 표시 또는 결과 없음"
],
"expected": "필터 동작 확인"
},
{
"id": 8,
"phase": "FILTER",
"name": "[FILTER] 거래처별 필터",
"action": "click_if_exists",
"target": "select[name*='vendor'], button:has-text('거래처')",
"expected": "거래처 필터 가능"
},
{
"id": 9,
"phase": "READ",
"name": "[READ] 매입 상세 보기",
"action": "click_if_exists",
"target": "table tbody tr:first-child",
"expected": {
"detail_view": true
}
},
{
"id": 10,
"name": "상세 정보 확인",
"action": "verify_detail",
"checks": [
"거래 상세 정보 표시",
"품목 정보 표시",
"금액 상세 표시"
],
"expected": "매입 상세 정보 표시"
},
{
"id": 11,
"name": "목록으로 돌아가기",
"action": "click_if_exists",
"target": "button:has-text('목록'), a:has-text('목록'), [class*='back']",
"expected": "목록 페이지로 복귀"
},
{
"id": 12,
"name": "매입 합계 확인",
"action": "verify_elements",
"checks": [
"매입 합계 금액 표시"
],
"expected": "매입 합계 표시"
},
{
"id": 13,
"name": "엑셀 다운로드 버튼 확인",
"action": "verify_elements",
"checks": [
"엑셀 다운로드 버튼 존재"
],
"expected": "엑셀 다운로드 기능 표시"
},
{
"id": 14,
"name": "인쇄 버튼 확인",
"action": "verify_elements",
"checks": [
"인쇄 버튼 존재"
],
"expected": "인쇄 기능 표시"
},
{
"id": 15,
"name": "페이지네이션 확인",
"action": "verify_elements",
"checks": [
"페이지 번호 표시"
],
"expected": "페이지네이션 표시"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/accounting/purchases",
"description": "매입 목록 조회"
},
{
"method": "GET",
"endpoint": "/api/v1/accounting/purchases/summary",
"description": "매입 요약 조회"
}
],
"requiredVerifications": [
{
"id": 3,
"name": "검색/필터",
"steps": [5, 6, 7, 8],
"criteria": "기간 필터 + 거래처 필터 동작"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "매입 목록, 기간 필터, 합계 표시 존재"
}
],
"rollbackPlan": {
"note": "조회 전용 페이지로 데이터 변경 없음"
}
}

View File

@@ -0,0 +1,241 @@
{
"id": "accounting-receivable",
"name": "미수금현황 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "회계관리 > 미수금현황 메뉴의 미수금 조회/필터/검색/엑셀 다운로드 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "회계관리",
"level2": "미수금현황",
"expectedUrl": "/accounting/receivables-status",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"testData": {
"filterStartDate": "2026-01-01",
"filterEndDate": "2026-02-28",
"searchKeyword": "테스트"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 회계관리 > 미수금현황",
"action": "menu_navigate",
"level1": "회계관리",
"level2": "미수금현황",
"expected": {
"url_contains": "/accounting/receivables",
"visible": ["미수금현황", "미수금"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"미수금 목록 표시",
"검색/필터 기능 존재",
"날짜 선택 가능"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "미수금 테이블 구조 확인",
"action": "verify_table",
"checks": [
"거래처 컬럼",
"미수금액 컬럼",
"매출일 컬럼",
"입금예정일 컬럼",
"연체일수 컬럼"
],
"expected": "미수금 테이블 컬럼 정상 표시"
},
{
"id": 4,
"name": "통계 카드 확인",
"action": "verify_elements",
"checks": [
"총 미수금액 표시",
"정상 미수금 표시",
"연체 미수금 표시"
],
"expected": "미수금 통계 카드 표시"
},
{
"id": 5,
"phase": "FILTER",
"name": "[FILTER] 기간 필터 적용",
"actions": [
{ "type": "click_if_exists", "target": "input[type='date']:first-of-type, input[placeholder*='시작'], input[name*='start']" },
{ "type": "click_if_exists", "target": "input[type='date']:last-of-type, input[placeholder*='종료'], input[name*='end']" },
{ "type": "wait", "duration": 500 }
],
"expected": {
"filter_applied": true
}
},
{
"id": 6,
"phase": "FILTER",
"name": "[FILTER] 필터 결과 확인",
"action": "verify_detail",
"expected": {
"data_filtered": true,
"table_updated": true
}
},
{
"id": 7,
"phase": "SEARCH",
"name": "[SEARCH] 거래처 검색",
"action": "click_if_exists",
"target": "input[placeholder*='검색'], input[placeholder*='거래처']",
"value": "테스트",
"expected": {
"data_filtered": true
}
},
{
"id": 8,
"phase": "SEARCH",
"name": "[SEARCH] 검색 결과 확인",
"action": "verify_detail",
"search": "테스트",
"expected": {
"row_exists": true,
"filtered_by_keyword": true
}
},
{
"id": 9,
"phase": "READ",
"name": "[READ] 미수금 상세 클릭",
"action": "click_if_exists",
"target": "table tbody tr:first-child",
"expected": {
"detail_modal_or_page": true,
"visible": ["거래처", "미수금액", "상세"]
}
},
{
"id": 10,
"phase": "READ",
"name": "[READ] 상세 정보 확인",
"action": "verify_detail",
"checks": [
"거래처명 표시",
"미수금액 표시",
"매출 내역 표시"
],
"expected": "미수금 상세 정보 정상 표시"
},
{
"id": 11,
"phase": "READ",
"name": "[READ] 목록으로 복귀",
"action": "click_if_exists",
"target": "button:has-text('목록'), button:has-text('목록으로'), button:has-text('닫기'), button:has-text('뒤로')",
"expected": {
"url_contains": "/accounting/receivables"
}
},
{
"id": 12,
"phase": "EXPORT",
"name": "[EXPORT] 엑셀 다운로드 버튼 확인",
"action": "verify_elements",
"checks": [
"엑셀 다운로드 버튼 존재"
],
"expected": "다운로드 기능 존재"
},
{
"id": 13,
"phase": "EXPORT",
"name": "[EXPORT] 필수 검증 #1: 엑셀 다운로드",
"action": "click_if_exists",
"target": "button:has-text('엑셀'), button:has-text('다운로드'), button:has-text('내보내기')",
"verify": {
"file_download": true,
"file_type": "xlsx",
"api_call": "GET /api/v1/receivables/export"
},
"expected": "엑셀 파일 다운로드"
},
{
"id": 14,
"phase": "SORT",
"name": "[SORT] 컬럼 정렬 테스트",
"action": "click_if_exists",
"target": "th:has-text('미수금액'), th:has-text('미수금'), th:has-text('금액')",
"expected": {
"sort_applied": true,
"data_reordered": true
}
},
{
"id": 15,
"name": "연체 현황 탭 확인",
"action": "click_if_exists",
"target": "button:has-text('연체'), [role='tab']:has-text('연체')",
"expected": {
"tab_active": true,
"filtered_data": true
}
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/receivables",
"description": "미수금 목록 조회"
},
{
"method": "GET",
"endpoint": "/api/v1/receivables/summary",
"description": "미수금 통계 조회"
},
{
"method": "GET",
"endpoint": "/api/v1/receivables/{id}",
"description": "미수금 상세 조회"
},
{
"method": "GET",
"endpoint": "/api/v1/receivables/export",
"description": "미수금 엑셀 다운로드"
}
],
"requiredVerifications": [
{
"id": 1,
"name": "파일 다운로드",
"steps": [13],
"criteria": "엑셀 파일 다운로드 동작"
},
{
"id": 3,
"name": "검색/필터",
"steps": [5, 6, 7, 8],
"criteria": "기간 필터 및 검색 기능"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "미수금 목록, 필터, 다운로드 버튼 존재"
}
],
"rollbackPlan": {
"note": "미수금현황은 조회 전용 페이지로 CRUD 없음 (데이터 생성/수정/삭제 없음)"
}
}

View File

@@ -0,0 +1,196 @@
{
"id": "accounting-sales",
"name": "매출관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "회계관리 > 매출관리 메뉴의 매출 조회/필터/엑셀 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "회계관리",
"level2": "매출관리",
"expectedUrl": "/accounting/sales-accounting",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 회계관리 > 매출관리",
"action": "menu_navigate",
"level1": "회계관리",
"level2": "매출관리",
"expected": {
"url_contains": "/accounting",
"visible": ["매출관리", "매출"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"매출 목록 표시",
"기간 필터 존재",
"합계 금액 표시"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "매출 테이블 구조 확인",
"action": "verify_table",
"checks": [
"거래일 컬럼",
"거래처 컬럼",
"품목 컬럼",
"금액 컬럼",
"부가세 컬럼"
],
"expected": "매출 테이블 표시"
},
{
"id": 4,
"name": "매출 통계 카드 확인",
"action": "verify_elements",
"checks": [
"총 매출액 표시",
"당월 매출 표시",
"전월 대비 표시"
],
"expected": "통계 카드 표시"
},
{
"id": 5,
"phase": "FILTER",
"name": "[FILTER] 기간 필터 - 시작일",
"action": "click_if_exists",
"target": "input[type='date']:first-of-type, [class*='datepicker']:first-of-type",
"expected": "날짜 선택 열림"
},
{
"id": 6,
"phase": "FILTER",
"name": "[FILTER] 기간 필터 적용",
"action": "click_if_exists",
"target": "button:has-text('조회'), button:has-text('검색'), button:has-text('적용')",
"expected": "필터 적용됨"
},
{
"id": 7,
"phase": "FILTER",
"name": "[FILTER] 필터 결과 확인",
"action": "verify_detail",
"checks": [
"필터된 매출 데이터 표시 또는 결과 없음"
],
"expected": "필터 동작 확인"
},
{
"id": 8,
"phase": "FILTER",
"name": "[FILTER] 거래처별 필터",
"action": "click_if_exists",
"target": "select[name*='vendor'], button:has-text('거래처')",
"expected": "거래처 필터 가능"
},
{
"id": 9,
"phase": "READ",
"name": "[READ] 매출 상세 보기",
"action": "click_if_exists",
"target": "table tbody tr:first-child",
"expected": {
"detail_view": true
}
},
{
"id": 10,
"name": "상세 정보 확인",
"action": "verify_detail",
"checks": [
"거래 상세 정보 표시",
"품목 정보 표시",
"금액 상세 표시"
],
"expected": "매출 상세 정보 표시"
},
{
"id": 11,
"name": "목록으로 돌아가기",
"action": "click_if_exists",
"target": "button:has-text('목록'), a:has-text('목록'), [class*='back']",
"expected": "목록 페이지로 복귀"
},
{
"id": 12,
"name": "매출 합계 확인",
"action": "verify_elements",
"checks": [
"매출 합계 금액 표시"
],
"expected": "매출 합계 표시"
},
{
"id": 13,
"name": "엑셀 다운로드 버튼 확인",
"action": "verify_elements",
"checks": [
"엑셀 다운로드 버튼 존재"
],
"expected": "엑셀 다운로드 기능 표시"
},
{
"id": 14,
"name": "인쇄 버튼 확인",
"action": "verify_elements",
"checks": [
"인쇄 버튼 존재"
],
"expected": "인쇄 기능 표시"
},
{
"id": 15,
"name": "페이지네이션 확인",
"action": "verify_elements",
"checks": [
"페이지 번호 표시"
],
"expected": "페이지네이션 표시"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/accounting/sales",
"description": "매출 목록 조회"
},
{
"method": "GET",
"endpoint": "/api/v1/accounting/sales/summary",
"description": "매출 요약 조회"
}
],
"requiredVerifications": [
{
"id": 3,
"name": "검색/필터",
"steps": [5, 6, 7, 8],
"criteria": "기간 필터 + 거래처 필터 동작"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "매출 목록, 기간 필터, 합계 표시 존재"
}
],
"rollbackPlan": {
"note": "조회 전용 페이지로 데이터 변경 없음"
}
}

View File

@@ -0,0 +1,302 @@
{
"id": "accounting-withdrawal",
"name": "출금관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "회계관리 > 출금관리 메뉴의 출금 조회/등록/수정/삭제 전체 CRUD 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "회계관리",
"level2": "출금관리",
"expectedUrl": "/accounting/withdrawals",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"testData": {
"create": {
"vendorName": "E2E_TEST_출금거래처",
"amount": "50000",
"withdrawalMethod": "계좌이체",
"memo": "E2E 자동화 테스트 출금"
},
"update": {
"amount": "75000",
"memo": "E2E 수정된 출금 메모"
}
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 회계관리 > 출금관리",
"action": "menu_navigate",
"level1": "회계관리",
"level2": "출금관리",
"expected": {
"url_contains": "/accounting/withdrawals",
"visible": ["출금관리", "출금"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"출금 목록 표시",
"출금 등록 버튼 존재",
"검색/필터 기능 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "출금 테이블 구조 확인",
"action": "verify_table",
"checks": [
"출금일 컬럼",
"거래처 컬럼",
"금액 컬럼",
"출금방법 컬럼",
"상태 컬럼"
],
"expected": "출금 테이블 컬럼 정상 표시"
},
{
"id": 4,
"name": "검색 기능 테스트",
"action": "click_if_exists",
"target": "input[placeholder*='검색']",
"value": "테스트",
"expected": {
"data_filtered": true
}
},
{
"id": 5,
"phase": "CREATE",
"name": "[CREATE] 출금 등록 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('등록'), button:has-text('출금 등록'), button:has-text('추가')",
"expected": {
"modal": true,
"modalTitle": "출금 등록"
}
},
{
"id": 6,
"phase": "CREATE",
"name": "[CREATE] 출금 정보 입력",
"action": "fill_form",
"fields": [
{"name": "거래처", "type": "select", "value": "E2E_TEST_출금거래처"},
{"name": "출금일", "type": "date", "value": "2026-02-03"},
{"name": "금액", "type": "number", "value": "50000"},
{"name": "출금방법", "type": "select", "value": "계좌이체"},
{"name": "메모", "type": "text", "value": "E2E 자동화 테스트 출금_{timestamp}"}
],
"note": "타임스탬프로 고유성 보장"
},
{
"id": 7,
"phase": "CREATE",
"name": "[CREATE] 필수 검증 #2: 등록 저장",
"action": "click_if_exists",
"target": "button:has-text('저장'), button:has-text('등록')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "POST /api/v1/withdrawals",
"toast": "등록|완료|성공"
},
"expected": "출금 등록 완료"
},
{
"id": "7-modal-close",
"phase": "CREATE",
"name": "[CREATE] 모달 닫기 확인",
"action": "close_modal_if_open",
"expected": "모달 닫힘"
},
{
"id": 8,
"phase": "CREATE",
"name": "[CREATE] 등록 결과 확인",
"action": "verify_detail",
"search": "E2E 자동화 테스트 출금",
"expected": {
"row_exists": true,
"contains": ["E2E", "50,000"]
}
},
{
"id": 9,
"phase": "READ",
"name": "[READ] 출금 상세 페이지 진입",
"action": "click_if_exists",
"target": "table tbody tr:has-text('E2E')",
"expected": {
"url_contains": "/accounting/withdrawals/",
"visible": ["출금 상세", "수정", "삭제"]
}
},
{
"id": 10,
"phase": "READ",
"name": "[READ] 상세 정보 확인",
"action": "verify_detail",
"checks": [
"금액: 50,000",
"출금방법: 계좌이체",
"메모: E2E 자동화 테스트"
],
"expected": "입력한 데이터와 일치"
},
{
"id": 11,
"phase": "UPDATE",
"name": "[UPDATE] 수정 모드 진입",
"action": "click_if_exists",
"target": "button:has-text('수정')",
"expected": {
"url_contains": "mode=edit",
"fields_editable": true
}
},
{
"id": 12,
"phase": "UPDATE",
"name": "[UPDATE] 금액 수정",
"action": "click_if_exists",
"target": "input[name*='amount'], input[placeholder*='금액']",
"value": "75000",
"clear": true
},
{
"id": 13,
"phase": "UPDATE",
"name": "[UPDATE] 메모 수정",
"action": "click_if_exists",
"target": "textarea[name*='memo'], input[placeholder*='메모']",
"value": "E2E 수정된 출금 메모_{timestamp}",
"clear": true
},
{
"id": 14,
"phase": "UPDATE",
"name": "[UPDATE] 필수 검증 #2: 수정 저장",
"action": "click_if_exists",
"target": "button:has-text('저장')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "PUT /api/v1/withdrawals/",
"toast": "수정|완료|성공"
},
"expected": "수정 완료"
},
{
"id": 15,
"phase": "UPDATE",
"name": "[UPDATE] 수정 결과 확인",
"action": "verify_detail",
"checks": [
"금액: 75,000",
"메모: E2E 수정된 출금"
],
"expected": "수정된 데이터 반영"
},
{
"id": 16,
"phase": "DELETE",
"name": "[DELETE] 삭제 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('삭제')",
"expected": {
"confirm_dialog": true,
"dialog_message": "삭제|정말"
}
},
{
"id": 17,
"phase": "DELETE",
"name": "[DELETE] 필수 검증 #6: 삭제 확인",
"action": "click_if_exists",
"target": "button:has-text('확인'), button:has-text('삭제')",
"verify": {
"api_call": "DELETE /api/v1/withdrawals/",
"toast": "삭제|완료|성공",
"redirect": "/accounting/withdrawals"
},
"expected": "삭제 완료 및 목록 복귀"
},
{
"id": 18,
"phase": "DELETE",
"name": "[DELETE] 삭제 결과 확인",
"action": "verify_detail",
"search": "E2E 수정된 출금",
"expected": {
"row_exists": false,
"message": "테스트 데이터가 목록에서 제거됨"
}
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/withdrawals",
"description": "출금 목록 조회"
},
{
"method": "POST",
"endpoint": "/api/v1/withdrawals",
"description": "출금 등록"
},
{
"method": "GET",
"endpoint": "/api/v1/withdrawals/{id}",
"description": "출금 상세 조회"
},
{
"method": "PUT",
"endpoint": "/api/v1/withdrawals/{id}",
"description": "출금 수정"
},
{
"method": "DELETE",
"endpoint": "/api/v1/withdrawals/{id}",
"description": "출금 삭제"
}
],
"requiredVerifications": [
{
"id": 2,
"name": "등록/저장 버튼",
"steps": [7, 14],
"criteria": "API 호출 + 성공 토스트 + 데이터 반영"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "출금 목록, 등록 버튼, 필터 존재"
},
{
"id": 6,
"name": "삭제 기능",
"steps": [16, 17, 18],
"criteria": "DELETE API + 목록에서 제거"
}
],
"rollbackPlan": {
"onCreateFail": "모달 닫기 → 다음 테스트 영향 없음",
"onUpdateFail": "테스트 데이터 수동 삭제 필요",
"onDeleteFail": "테스트 데이터 수동 삭제 필요",
"cleanupRequired": "E2E_TEST_ 접두사 데이터는 테스트 데이터"
}
}

View File

@@ -0,0 +1,679 @@
{
"id": "approval-box",
"name": "결재함 E2E 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
},
"description": "결재함 페이지의 전체 기능을 검증합니다 (탭 전환, 검색, 필터, 승인/반려, 모달)",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "결재관리",
"level2": "결재함",
"expectedUrl": "/ko/approval/inbox",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"navigation": {
"targetUrl": "/approval/inbox",
"urlPattern": "/approval/inbox|/ko/approval/inbox",
"menuHints": [
"결재함",
"결재 함",
"결재관리"
]
},
"menuNavigationEnhanced": {
"strategy": "scroll-and-search",
"description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지",
"level1": "결재관리",
"level2": "결재함",
"alternativeLevel2Names": [
"결재함",
"결재 함",
"승인함",
"Approval Box",
"inbox"
],
"fallbackUrls": [
"/ko/approval/inbox",
"/ko/approval/box",
"/ko/approvals/inbox",
"/ko/approval-management/inbox",
"/approval/inbox"
],
"scrollConfig": {
"sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar",
"menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']",
"scrollStep": 200,
"maxScrollAttempts": 10,
"scrollDelay": 300
}
},
"steps": [
{
"id": 0,
"name": "사이드바 메뉴 전체 펼치기",
"description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비",
"actions": [
{
"type": "scroll",
"target": "sidebar",
"direction": "top",
"description": "사이드바 최상단으로 스크롤"
},
{
"type": "wait",
"duration": 300
},
{
"type": "evaluate",
"script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()"
},
{
"type": "wait",
"duration": 2000
}
],
"verification": [
"사이드바가 화면에 보이는지 확인",
"모든 메뉴가 펼쳐졌는지 확인"
]
},
{
"id": 1,
"name": "1차 메뉴 찾기: 결재관리 (스크롤 포함)",
"description": "사이드바를 스크롤하며 '결재관리' 메뉴를 찾아 클릭",
"actions": [
{
"type": "scrollAndFind",
"target": "결재관리",
"alternativeTexts": [
"결재관리",
"결재 관리",
"Approval",
"전자결재"
],
"scrollContainer": "sidebar",
"maxAttempts": 10,
"description": "스크롤하며 결재관리 메뉴 찾기"
},
{
"type": "wait",
"duration": 300
},
{
"type": "click_if_exists",
"target": "결재관리",
"description": "결재관리 메뉴 클릭"
},
{
"type": "wait",
"duration": 500,
"description": "서브메뉴 펼쳐지기 대기"
},
{
"type": "screenshot",
"name": "approval_menu_expanded"
}
],
"verification": [
"결재관리 메뉴가 클릭되었는지 확인",
"서브메뉴가 펼쳐졌는지 확인",
"하위 메뉴 항목들이 보이는지 확인"
],
"fallback": {
"if": "메뉴를 찾을 수 없음",
"then": "사이드바 전체를 스크롤하며 재탐색"
}
},
{
"id": 2,
"name": "2차 메뉴 찾기: 결재함 (스크롤 포함)",
"description": "서브메뉴에서 '결재함'을 찾아 클릭",
"actions": [
{
"type": "scrollAndFind",
"target": "결재함",
"alternativeTexts": [
"결재함",
"결재 함",
"Inbox",
"승인함"
],
"scrollContainer": "submenu",
"maxAttempts": 5,
"description": "서브메뉴에서 결재함 찾기"
},
{
"type": "wait",
"duration": 200
},
{
"type": "click_if_exists",
"target": "결재함",
"description": "결재함 메뉴 클릭"
},
{
"type": "wait",
"target": "페이지 로드 완료",
"timeout": 10000
},
{
"type": "screenshot",
"name": "approval_box_page"
}
],
"verification": [
"결재함 메뉴 클릭 성공",
"페이지 이동 또는 컨텐츠 로드"
]
},
{
"id": 3,
"name": "404 에러 감지 및 대체 경로 시도",
"description": "페이지 로드 후 404 에러 여부 확인, 404시 대체 경로 탐색",
"actions": [
{
"type": "wait",
"duration": 1000
},
{
"type": "checkFor404",
"indicators": [
"페이지를 찾을 수 없습니다",
"404",
"Not Found",
"존재하지 않거나"
]
},
{
"type": "screenshot",
"name": "page_load_result"
}
],
"verification": [
"현재 페이지가 404인지 확인"
],
"onError404": {
"description": "404 에러 발생 시 대체 URL 시도",
"actions": [
{
"type": "log",
"message": "404 감지 - 대체 경로 탐색 시작"
},
{
"type": "tryAlternativeUrls",
"urls": [
"/ko/approval/inbox",
"/ko/approvals/inbox",
"/ko/approval-box"
],
"stopOnSuccess": true
},
{
"type": "ifStillFailed",
"action": "navigateViaMenuClick",
"description": "URL 직접 접근 실패 시 메뉴 클릭으로 재시도"
}
]
}
},
{
"id": 4,
"name": "페이지 정상 로드 확인",
"description": "결재함 페이지가 정상적으로 로드되었는지 확인",
"actions": [
{
"type": "verify",
"target": "pageTitle",
"contains": [
"결재함",
"결재",
"Approval"
]
},
{
"type": "verify",
"target": "pageContent",
"notContains": [
"404",
"찾을 수 없습니다",
"Not Found"
]
}
],
"verification": [
"페이지 제목 '결재함' 또는 관련 텍스트 표시",
"404 에러 메시지 미표시",
"콘텐츠가 정상 렌더링됨"
],
"successCriteria": {
"urlPattern": "/approval",
"requiredElements": [
"결재",
"문서",
"승인"
]
}
},
{
"id": 5,
"name": "통계 카드 확인",
"action": "verify_element",
"target": "[class*='card'], [class*='stat']",
"verification": [
"전체결재 건수 기록",
"미결재 건수 기록",
"결재완료 건수 기록",
"결재반려 건수 기록"
]
},
{
"id": 6,
"name": "탭 구조 확인",
"action": "verify_element",
"target": "[role='tab'], button[role='tab']",
"verification": [
"'전체결재' 탭 존재 확인",
"'미결재' 탭 존재 확인",
"'결재완료' 탭 존재 확인",
"'결재반려' 탭 존재 확인"
]
},
{
"id": 7,
"name": "테이블 데이터 확인",
"action": "verify_table",
"target": "table",
"verification": [
"테이블 헤더 컬럼 확인",
"데이터 행 존재 여부 확인",
"페이지네이션 표시 확인"
]
},
{
"id": 8,
"name": "⚠️ 필수 검증: 결재 문서 상세 보기",
"description": "테이블에서 결재 문서 클릭하여 상세 모달/페이지 확인",
"actions": [
{
"type": "click_if_exists",
"target": "미결재 탭",
"description": "미결재 탭으로 이동"
},
{
"type": "wait",
"duration": 500
},
{
"type": "click_if_exists",
"target": "첫 번째 결재 문서 행",
"description": "결재 문서 클릭"
},
{
"type": "wait",
"target": "상세 모달 또는 페이지"
}
],
"expect": {
"detailView": true,
"fields": [
"문서 제목",
"기안자",
"기안일",
"결재 상태"
],
"buttons": [
"승인",
"반려",
"PDF",
"인쇄"
]
},
"note": "결재 문서가 없으면 데이터 생성 또는 SKIP"
},
{
"id": "8-pdf-1",
"name": "⚠️ 필수 검증: PDF 다운로드 전 모달 스크린샷",
"description": "PDF 생성 전 모달 상태를 스크린샷으로 캡처하여 CSS 문제 감지용 기준 이미지 확보",
"prerequisite": "step-8의 문서 상세 모달이 열려있는 상태에서 실행",
"actions": [
{
"type": "screenshot",
"name": "pdf-preview-before-download-approval-box",
"fullPage": false,
"selector": "[role='dialog'], .modal, [data-state='open']",
"savePath": "tests/e2e/results/hotfix/screenshots/",
"description": "PDF 생성 대상 모달 전체 캡처"
}
],
"verify": {
"screenshotCaptured": true,
"purpose": "PDF CSS 문제 감지를 위한 기준 이미지"
}
},
{
"id": "8-pdf-2",
"name": "⚠️ 필수 검증: PDF 다운로드 실행 및 파일 보관",
"description": "PDF 다운로드 후 파일을 지정 폴더에 보관하여 수동 검증 가능하게 함",
"actions": [
{
"type": "verify",
"target": "PDF 버튼 존재",
"selector": "button:has-text('PDF'), [aria-label*='PDF']",
"description": "PDF 다운로드 버튼 존재 확인"
},
{
"type": "expectResponse",
"id": "pdf-download-response-approval-box",
"urlPattern": "/api/v1/approvals/*/pdf",
"description": "PDF 다운로드 API 응답 대기 설정"
},
{
"type": "click_if_exists",
"target": "PDF 버튼",
"selector": "button:has-text('PDF')",
"description": "PDF 다운로드 버튼 클릭"
},
{
"type": "wait",
"duration": 3000,
"description": "PDF 생성 및 다운로드 대기"
},
{
"type": "assertResponse",
"id": "pdf-download-response-approval-box",
"checks": {
"status": 200,
"contentType": "application/pdf"
}
},
{
"type": "saveDownloadedFile",
"targetPath": "tests/e2e/results/hotfix/pdf-samples/",
"fileNamePattern": "approval-box-{timestamp}.pdf",
"description": "다운로드된 PDF 파일을 지정 폴더에 보관"
}
],
"verify": {
"apiSuccess": true,
"fileDownloaded": true,
"fileSaved": "tests/e2e/results/hotfix/pdf-samples/"
}
},
{
"id": "8-pdf-3",
"name": "⚠️ PDF 파일 유효성 검증",
"description": "다운로드된 PDF 파일의 기본 유효성 검사",
"actions": [
{
"type": "verifyDownloadedFile",
"checks": {
"fileExists": true,
"fileSize": "> 1024",
"pdfSignature": "%PDF-",
"description": "PDF 파일 헤더 검증"
}
}
],
"verify": {
"pdfValid": true,
"minFileSize": "1KB 이상"
}
},
{
"id": "8-pdf-4",
"name": "📋 PDF 스타일 수동 확인 체크리스트",
"type": "manualVerification",
"description": "개발자가 다운로드된 PDF를 열어 시각적으로 확인해야 하는 항목",
"manualChecklist": [
{
"id": "css-1",
"item": "테이블 경계선이 올바르게 표시되는가?",
"category": "테이블 스타일"
},
{
"id": "css-2",
"item": "한글 폰트가 깨지지 않고 정상 표시되는가?",
"category": "폰트"
},
{
"id": "css-3",
"item": "숫자/금액 정렬이 올바른가? (우측 정렬)",
"category": "정렬"
},
{
"id": "css-4",
"item": "여백(margin/padding)이 적절한가?",
"category": "레이아웃"
},
{
"id": "css-5",
"item": "헤더/푸터가 각 페이지에 올바르게 표시되는가?",
"category": "페이지 구조"
},
{
"id": "css-6",
"item": "로고/이미지가 정상 표시되는가?",
"category": "이미지"
},
{
"id": "css-7",
"item": "페이지 나눔(page break)이 적절한 위치에서 발생하는가?",
"category": "페이지 나눔"
},
{
"id": "css-8",
"item": "배경색/강조색이 올바르게 적용되었는가?",
"category": "색상"
},
{
"id": "css-9",
"item": "텍스트가 잘리거나 겹치지 않는가?",
"category": "오버플로우"
},
{
"id": "css-10",
"item": "결재선 정보가 정상적으로 표시되는가?",
"category": "결재선"
}
],
"outputFiles": {
"screenshot": "tests/e2e/results/hotfix/screenshots/pdf-preview-before-download-approval-box-*.png",
"pdfFile": "tests/e2e/results/hotfix/pdf-samples/approval-box-*.pdf"
},
"reportFlag": {
"requiresManualReview": true,
"message": "⚠️ PDF 스타일 수동 확인 필요 - 위 체크리스트 항목을 PDF 파일에서 직접 확인하세요"
}
},
{
"id": 9,
"name": "⚠️ 필수 검증 #4: 결재 승인 실제 수행",
"description": "미결재 문서에 대해 실제 승인 처리 수행",
"actions": [
{
"type": "verify",
"target": "승인 버튼 존재"
},
{
"type": "click_if_exists",
"target": "승인 버튼",
"description": "결재 승인 클릭"
},
{
"type": "wait",
"target": "확인 다이얼로그"
},
{
"type": "click_if_exists",
"target": "확인",
"description": "승인 확인"
}
],
"expect": {
"urlMaintained": true,
"noErrorPage": true,
"apiCall": "POST /api/v1/approvals/{id}/approve",
"toast": "승인되었습니다",
"statusChange": "미결재 → 결재완료"
},
"note": "⚠️ 버튼 존재만 확인하면 불완전! 실제 승인까지 검증 필수!"
},
{
"id": "9-1",
"name": "결재 승인 결과 확인",
"description": "승인 후 결재완료 탭에서 해당 문서 확인",
"actions": [
{
"type": "click_if_exists",
"target": "결재완료 탭"
},
{
"type": "wait",
"duration": 500
}
],
"verify": {
"documentMoved": "승인한 문서가 결재완료 탭에 표시",
"statusUpdated": "결재 상태가 '완료'로 변경"
}
},
{
"id": 10,
"name": "⚠️ 필수 검증 #4: 결재 반려 실제 수행",
"description": "미결재 문서에 대해 실제 반려 처리 수행",
"actions": [
{
"type": "click_if_exists",
"target": "미결재 탭",
"description": "미결재 탭으로 이동"
},
{
"type": "wait",
"duration": 500
},
{
"type": "click_if_exists",
"target": "결재 문서 행",
"description": "결재 문서 선택"
},
{
"type": "wait",
"target": "상세 보기"
},
{
"type": "click_if_exists",
"target": "반려 버튼",
"description": "결재 반려 클릭"
},
{
"type": "wait",
"target": "반려 사유 입력 모달"
},
{
"type": "click_if_exists",
"target": "반려 사유"
},
{
"type": "click_if_exists",
"target": "확인",
"description": "반려 확인"
}
],
"expect": {
"urlMaintained": true,
"noErrorPage": true,
"apiCall": "POST /api/v1/approvals/{id}/reject",
"toast": "반려되었습니다",
"statusChange": "미결재 → 결재반려"
},
"note": "⚠️ 반려 버튼 존재만 확인하면 불완전! 실제 반려까지 검증 필수!"
},
{
"id": "10-1",
"name": "결재 반려 결과 확인",
"description": "반려 후 결재반려 탭에서 해당 문서 확인",
"actions": [
{
"type": "click_if_exists",
"target": "결재반려 탭"
},
{
"type": "wait",
"duration": 500
}
],
"verify": {
"documentMoved": "반려한 문서가 결재반려 탭에 표시",
"statusUpdated": "결재 상태가 '반려'로 변경",
"rejectReason": "반려 사유가 표시"
}
},
{
"id": 11,
"name": "검색 기능 테스트",
"description": "검색 필터로 결재 문서 검색",
"actions": [
{
"type": "click_if_exists",
"target": "전체결재 탭"
},
{
"type": "click_if_exists",
"target": "검색 입력창"
},
{
"type": "click_if_exists",
"target": "검색 버튼"
}
],
"verify": {
"searchApplied": true,
"filteredResults": "검색어에 맞는 결과 표시"
}
}
],
"mandatoryVerifications": {
"description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목",
"items": [
{
"id": 4,
"name": "결재 승인/반려 완료",
"trigger": "결재 문서 상세의 승인/반려 버튼",
"verification": "실제 승인/반려 동작 + API 호출 + 결과 확인",
"failCondition": "버튼 존재만 확인, 클릭하지 않음",
"steps": [
"9",
"10"
]
}
]
},
"expectedAPIs": [
"GET /api/v1/approvals/inbox - 결재함 목록 조회",
"GET /api/v1/approvals/inbox/summary - 결재함 통계",
"GET /api/v1/approvals/{id} - 결재 문서 상세 조회",
"POST /api/v1/approvals/{id}/approve - 결재 승인",
"POST /api/v1/approvals/{id}/reject - 결재 반려"
],
"notes": [
"⚠️ 404 방지: 반드시 메뉴 클릭으로 페이지 진입 (직접 URL 접근 금지)",
"⚠️ 스크롤 필수: 사이드바가 길 경우 메뉴가 화면 밖에 있을 수 있음",
"⚠️ 대체 경로: 메뉴명이 변경되었을 수 있으므로 다양한 이름으로 탐색",
"메뉴 계층: 결재관리 > 결재함",
"탭 전환 시 URL 변경 없이 데이터만 필터링됨"
]
}

View File

@@ -0,0 +1,461 @@
{
"id": "attendance-checkin",
"name": "근태현황 출퇴근 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "위치 정보 권한 허용 후 출근/퇴근 기록을 테스트하는 E2E 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"url": "/hr/attendance",
"navigation": {
"targetUrl": "/hr/attendance",
"urlPattern": "/hr/attendance|/ko/hr/attendance",
"menuHints": ["근태현황", "근태 현황", "출퇴근", "인사관리"]
},
"menuNavigation": {
"level1": "인사관리",
"level2": "근태현황",
"expectedUrl": "/hr/attendance",
"searchWithinParent": true,
"closeOtherMenus": true
},
"menuNavigationEnhanced": {
"strategy": "scroll-and-search",
"description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지",
"level1": "인사관리",
"level2": "근태현황",
"alternativeLevel1Names": ["인사관리", "인사 관리", "HR", "Human Resource", "HR관리"],
"alternativeLevel2Names": ["근태현황", "근태 현황", "출퇴근", "Attendance", "출퇴근현황", "근태관리"],
"fallbackUrls": [
"/hr/attendance",
"/ko/hr/attendance",
"/ko/hr/attendance-status",
"/ko/hr/checkin",
"/ko/human-resource/attendance"
],
"scrollConfig": {
"sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar",
"menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']",
"scrollStep": 200,
"maxScrollAttempts": 10,
"scrollDelay": 300
}
},
"timeout": 120000,
"tags": ["hr", "attendance", "geolocation", "checkin", "checkout"],
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"browserConfig": {
"permissions": {
"geolocation": {
"grant": true,
"description": "위치 정보 접근 권한 허용 - 출퇴근 기록에 필수",
"mockLocation": {
"enabled": true,
"latitude": 37.557358,
"longitude": 126.864414,
"accuracy": 100
}
}
},
"contextOptions": {
"geolocation": {
"latitude": 37.557358,
"longitude": 126.864414
},
"permissions": ["geolocation"]
}
},
"preTestSetup": {
"description": "테스트 시작 전 Playwright 브라우저 컨텍스트에서 위치 권한 설정",
"playwright": {
"grantPermissions": ["geolocation"],
"setGeolocation": {
"latitude": 37.557358,
"longitude": 126.864414,
"accuracy": 100
},
"code": [
"// Playwright MCP 사용 시 브라우저 시작 직후 실행",
"// mcp__playwright__playwright_evaluate로 위치 권한 자동 허용",
"await context.grantPermissions(['geolocation']);",
"await context.setGeolocation({ latitude: 37.557358, longitude: 126.864414 });"
]
}
},
"steps": [
{
"id": "step-0",
"name": "🔐 Geolocation API 모킹 (권한 팝업 방지)",
"description": "페이지 로드 직후 Geolocation API를 모킹하여 브라우저 권한 팝업이 나타나지 않도록 함",
"executeBeforeNavigation": false,
"executeImmediately": true,
"actions": [
{
"type": "evaluate",
"script": "(() => { const mockPosition = { coords: { latitude: 37.557358, longitude: 126.864414, accuracy: 100, altitude: null, altitudeAccuracy: null, heading: null, speed: null }, timestamp: Date.now() }; const mockGeolocation = { getCurrentPosition: (success, error, options) => { console.log('[E2E] Geolocation.getCurrentPosition - 모킹된 위치 반환'); setTimeout(() => success(mockPosition), 50); }, watchPosition: (success, error, options) => { console.log('[E2E] Geolocation.watchPosition - 모킹된 위치 반환'); setTimeout(() => success(mockPosition), 50); return 1; }, clearWatch: (id) => {} }; Object.defineProperty(navigator, 'geolocation', { value: mockGeolocation, writable: false, configurable: true }); console.log('[E2E] Geolocation API 모킹 완료 - 서울 영등포구 좌표'); return { success: true, coords: mockPosition.coords }; })()",
"description": "Geolocation API 모킹 (서울 영등포구 좌표: 37.557358, 126.864414)"
},
{ "type": "wait", "duration": 300, "description": "모킹 적용 대기" }
],
"note": "Geolocation API를 모킹하면 브라우저가 위치 권한을 요청하지 않음"
},
{
"id": "step-0-1",
"name": "🗺️ 브라우저 위치 권한 팝업 클릭 (좌측 상단)",
"description": "Chrome 브라우저 좌측 상단에 나타나는 '사이트에 있는 동안 허용' 팝업 클릭",
"actions": [
{ "type": "wait", "duration": 1500, "description": "위치 권한 팝업 표시 대기" },
{
"type": "evaluate",
"script": "(async function() { const permissionSelectors = [ '[class*=\"permission\"][class*=\"allow\"]', '[class*=\"infobar\"] button', '[aria-label*=\"허용\"]', '[aria-label*=\"Allow\"]', 'button:has-text(\"사이트에 있는 동안 허용\")', 'button:has-text(\"허용\")', 'button:has-text(\"Allow\")', '[data-testid*=\"permission\"]', '.permission-prompt button', '[class*=\"PermissionPrompt\"] button' ]; for (const sel of permissionSelectors) { try { const btn = document.querySelector(sel); if (btn && btn.offsetParent !== null) { btn.click(); console.log('[E2E] 위치 권한 팝업 클릭 성공:', sel); await new Promise(r => setTimeout(r, 500)); return { clicked: true, selector: sel }; } } catch(e) {} } const allButtons = Array.from(document.querySelectorAll('button, [role=\"button\"]')); const allowBtn = allButtons.find(b => { const text = b.innerText || b.textContent || ''; return text.includes('사이트에 있는 동안 허용') || text.includes('허용') || text.includes('Allow'); }); if (allowBtn && allowBtn.offsetParent !== null) { allowBtn.click(); console.log('[E2E] 위치 권한 팝업 텍스트 검색으로 클릭'); return { clicked: true, method: 'textSearch' }; } console.log('[E2E] 위치 권한 팝업 없음 (이미 허용되었거나 모킹으로 우회됨)'); return { clicked: false, reason: 'no_popup_found' }; })()",
"description": "좌측 상단 권한 팝업 찾아서 클릭"
},
{ "type": "wait", "duration": 500, "description": "권한 설정 적용 대기" }
],
"errorHandling": {
"onTimeout": "continue",
"onNotFound": "continue",
"reason": "팝업이 없으면 이미 허용된 상태로 간주"
}
},
{
"id": "step-0-2",
"name": "📂 사이드바 메뉴 전체 펼치기",
"description": "모두 펼치기 버튼을 클릭하여 전체 메뉴 펼침",
"actions": [
{
"type": "evaluate",
"script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})"
},
{ "type": "wait", "duration": 300 },
{
"type": "evaluate",
"script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()"
},
{ "type": "wait", "duration": 2000 },
{ "type": "screenshot", "name": "after_permission_grant_and_menu_expanded" }
],
"verification": [
"사이드바 메뉴가 펼쳐졌는지 확인"
]
},
{
"id": 2,
"name": "1차 메뉴 찾기: 인사관리 (스크롤 포함)",
"description": "사이드바를 스크롤하며 '인사관리' 메뉴를 찾아 클릭",
"actions": [
{
"type": "scrollAndFind",
"target": "인사관리",
"alternativeTexts": ["인사관리", "인사 관리", "HR", "Human Resource"],
"scrollContainer": "sidebar",
"maxAttempts": 10,
"description": "스크롤하며 인사관리 메뉴 찾기"
},
{ "type": "wait", "duration": 300 },
{ "type": "click_if_exists", "target": "인사관리", "description": "인사관리 메뉴 클릭" },
{ "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" },
{ "type": "screenshot", "name": "hr_menu_expanded" }
],
"verification": [
"인사관리 메뉴가 클릭되었는지 확인",
"서브메뉴가 펼쳐졌는지 확인",
"하위 메뉴 항목들이 보이는지 확인"
],
"fallback": {
"if": "메뉴를 찾을 수 없음",
"then": "사이드바 전체를 스크롤하며 재탐색"
}
},
{
"id": 3,
"name": "2차 메뉴 찾기: 근태현황 (스크롤 포함)",
"description": "서브메뉴에서 '근태현황'을 찾아 클릭",
"actions": [
{
"type": "scrollAndFind",
"target": "근태현황",
"alternativeTexts": ["근태현황", "근태 현황", "출퇴근", "Attendance"],
"scrollContainer": "submenu",
"maxAttempts": 5,
"description": "서브메뉴에서 근태현황 찾기"
},
{ "type": "wait", "duration": 200 },
{ "type": "click_if_exists", "target": "근태현황", "description": "근태현황 메뉴 클릭" },
{ "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 },
{ "type": "screenshot", "name": "attendance_page" }
],
"verification": [
"근태현황 메뉴 클릭 성공",
"페이지 이동 또는 컨텐츠 로드"
]
},
{
"id": 4,
"name": "404 에러 감지 및 대체 경로 시도",
"description": "페이지 로드 후 404 에러 여부 확인, 404시 대체 경로 탐색",
"actions": [
{ "type": "wait", "duration": 1000 },
{ "type": "checkFor404", "indicators": [
"페이지를 찾을 수 없습니다",
"404",
"Not Found",
"존재하지 않거나"
]},
{ "type": "screenshot", "name": "page_load_result" }
],
"verification": [
"현재 페이지가 404인지 확인"
],
"onError404": {
"description": "404 에러 발생 시 대체 URL 시도",
"actions": [
{ "type": "log", "message": "404 감지 - 대체 경로 탐색 시작" },
{
"type": "tryAlternativeUrls",
"urls": [
"/ko/hr/attendance",
"/ko/hr/attendance-status",
"/ko/hr/checkin"
],
"stopOnSuccess": true
},
{
"type": "ifStillFailed",
"action": "navigateViaMenuClick",
"description": "URL 직접 접근 실패 시 메뉴 클릭으로 재시도"
}
]
}
},
{
"id": 5,
"name": "페이지 정상 로드 확인",
"description": "근태현황 페이지가 정상적으로 로드되었는지 확인",
"actions": [
{ "type": "verify", "target": "pageTitle", "contains": ["근태현황", "출퇴근", "Attendance"] },
{ "type": "verify", "target": "pageContent", "notContains": ["404", "찾을 수 없습니다", "Not Found"] }
],
"verification": [
"페이지 제목 '근태현황' 또는 관련 텍스트 표시",
"404 에러 메시지 미표시",
"콘텐츠가 정상 렌더링됨"
],
"successCriteria": {
"urlPattern": "/hr/attendance",
"requiredElements": ["출퇴근", "출근", "퇴근", "현재 시간"]
}
},
{
"id": "step-5",
"name": "브라우저 위치 권한 설정",
"description": "Playwright context에서 위치 정보 권한을 허용하고 가상 위치 설정",
"playwright": {
"code": "await context.grantPermissions(['geolocation']);",
"setGeolocation": {
"latitude": 37.557358,
"longitude": 126.864414
}
},
"expect": {
"permissionGranted": "geolocation"
}
},
{
"id": "step-6",
"name": "위치 정보 로딩 대기",
"description": "Google Map 로딩 및 현재 위치 표시 대기",
"waitFor": {
"type": "element",
"selector": "region[name='지도']",
"timeout": 10000
},
"expect": {
"mapLoaded": true,
"locationMarkerVisible": true
}
},
{
"id": "step-7",
"name": "사용자 정보 확인",
"description": "출퇴근 패널에서 로그인한 사용자 정보 확인",
"verify": {
"userInfo": {
"name": "홍킬동",
"department": "부서명 · 개발중인 메뉴"
},
"currentTime": {
"format": "HH:mm:ss",
"updating": true
}
}
},
{
"id": "step-8",
"name": "출근 상태 확인",
"description": "현재 출퇴근 상태 확인 (출근 전/출근 후)",
"capture": {
"variable": "attendanceStatus",
"checkElements": [
{ "selector": "button:has-text('출근하기')", "status": "not_checked_in" },
{ "selector": "text=출근 완료", "status": "checked_in" },
{ "selector": "button:has-text('퇴근하기')", "status": "ready_to_checkout" }
]
}
},
{
"id": "step-9",
"name": "출근하기 (미출근 상태인 경우)",
"description": "출근하기 버튼이 활성화된 경우 클릭하여 출근 기록",
"condition": {
"if": "{attendanceStatus} == 'not_checked_in'"
},
"actions": [
{ "type": "click_if_exists", "target": "출근하기" }
],
"waitFor": {
"type": "text",
"content": "출근 완료",
"timeout": 5000
},
"expect": {
"toast": ["출근", "완료", "성공"],
"visible": ["출근 완료", "출근 시간"]
}
},
{
"id": "step-10",
"name": "출근 완료 상태 확인",
"description": "출근 완료 후 상태 및 출근 시간 표시 확인",
"verify": {
"visible": ["출근 완료"],
"checkInTime": {
"format": "HH:mm:ss",
"displayed": true
},
"buttonState": {
"출근하기": "hidden_or_disabled",
"퇴근하기": "enabled_or_visible"
}
}
},
{
"id": "step-11",
"name": "퇴근하기 버튼 상태 확인",
"description": "출근 완료 후 퇴근하기 버튼 활성화 여부 확인",
"verify": {
"button": {
"target": "퇴근하기",
"state": "visible",
"note": "일부 시스템에서는 최소 근무 시간 후에만 활성화될 수 있음"
}
}
},
{
"id": "step-12",
"name": "퇴근하기 (선택적)",
"description": "퇴근하기 버튼이 활성화된 경우 클릭하여 퇴근 기록",
"optional": true,
"condition": {
"if": "button[name='퇴근하기']:enabled"
},
"actions": [
{ "type": "click_if_exists", "target": "퇴근하기" }
],
"waitFor": {
"type": "text",
"content": ["퇴근 완료", "퇴근 시간"],
"timeout": 5000
},
"expect": {
"toast": ["퇴근", "완료", "성공"],
"visible": ["퇴근 완료", "퇴근 시간"]
}
},
{
"id": "step-13",
"name": "최종 상태 확인",
"description": "출퇴근 기록 후 최종 상태 확인",
"verify": {
"url": "/hr/attendance",
"mapDisplayed": true,
"attendanceRecorded": true
}
}
],
"assertions": [
{
"type": "url",
"expected": "/hr/attendance",
"message": "근태현황 페이지에 머물러야 함"
},
{
"type": "permission",
"name": "geolocation",
"state": "granted",
"message": "위치 정보 권한이 허용되어야 함"
},
{
"type": "elementExists",
"selector": "region[name='지도']",
"message": "Google Map이 표시되어야 함"
},
{
"type": "elementExists",
"selector": "text=현재 시간",
"message": "현재 시간이 표시되어야 함"
}
],
"cleanup": {
"enabled": false,
"description": "출퇴근 기록은 삭제하지 않음 (업무 데이터)",
"note": "테스트 후 수동으로 관리자가 삭제 필요시 처리"
},
"notes": {
"testScope": "위치 권한 허용 -> 근태현황 페이지 이동 -> 출근/퇴근 기록 테스트",
"antiPattern404": "직접 URL 접근 금지 - 반드시 메뉴 클릭으로 페이지 진입",
"scrollRequired": "사이드바 스크롤을 통해 메뉴 항목 탐색 필수",
"correctUrl": "/hr/attendance (기존 /ko/hr/attendance에서 수정됨)"
},
"playwrightMcpInstructions": {
"description": "Playwright MCP를 사용한 위치 권한 설정 방법",
"beforeNavigation": [
"1. 브라우저 navigate 전에 위치 권한 설정이 필요함",
"2. Playwright MCP는 브라우저 컨텍스트 레벨에서 권한을 설정할 수 없으므로 UI 팝업 처리 필요"
],
"uiPermissionHandling": {
"description": "위치 권한 팝업이 나타나면 '항상 허용' 버튼 클릭",
"selectors": [
"button:has-text('항상 허용')",
"button:has-text('허용')",
"button:has-text('Allow')"
],
"workflow": [
"1. 페이지 로드 후 1-2초 대기",
"2. 위치 권한 팝업 존재 여부 확인",
"3. 팝업이 있으면 '항상 허용' 버튼 클릭",
"4. 팝업이 없으면 이미 권한이 허용된 상태로 간주하고 진행"
]
},
"mockGeolocation": {
"description": "테스트용 가상 위치 설정",
"latitude": 37.557358,
"longitude": 126.864414,
"note": "서울 영등포구 좌표 (테스트 회사 위치 가정)"
}
}
}

View File

@@ -0,0 +1,81 @@
{
"id": "board-management",
"name": "게시판 관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "게시판 > 게시판 관리 목록/검색/상세 기능 검증",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "게시판",
"level2": "게시판 관리",
"expectedUrl": "/board/board-management",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 게시판 > 게시판 관리",
"action": "menu_navigate",
"level1": "게시판",
"level2": "게시판 관리",
"expected": { "url_contains": "/board" }
},
{
"id": 2,
"name": "목업 감지",
"action": "verify_not_mockup"
},
{
"id": 3,
"name": "게시판 관리 페이지 확인",
"action": "verify_detail",
"checks": ["visible_text:게시판"]
},
{
"id": 4,
"name": "테이블 확인",
"action": "verify_table"
},
{
"id": 5,
"name": "검색 기능",
"action": "search",
"value": "테스트"
},
{
"id": 6,
"name": "검색 후 확인",
"action": "verify_detail",
"checks": ["visible_text:게시판"]
},
{
"id": 7,
"name": "첫 번째 행 클릭",
"action": "click_first_row"
},
{
"id": 8,
"name": "상세 확인",
"action": "verify_detail",
"checks": ["visible_text:게시판"]
},
{
"id": 9,
"name": "모달 닫기",
"action": "close_modal_if_open"
},
{
"id": 10,
"name": "목록 복귀",
"action": "click_if_exists",
"target": "button:has-text('목록'), a:has-text('목록')"
}
]
}

View File

@@ -0,0 +1,435 @@
{
"id": "company-info",
"name": "설정 - 회사정보",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "회사 정보 관리 기능 테스트 - 회사 정보 조회, 수정, 회사 추가 기능",
"baseUrl": "https://dev.codebridge-x.com",
"navigation": {
"targetUrl": "/company-info",
"urlPattern": "/company-info|/ko/company-info|/settings/company-info",
"menuHints": ["회사정보", "회사 정보", "설정"]
},
"menuNavigation": {
"level1": "설정",
"level2": "회사정보",
"expectedUrl": "/company-info",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"menuNavigationEnhanced": {
"strategy": "scroll-and-search",
"description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지",
"level1": "설정",
"level2": "회사정보",
"alternativeLevel1Names": ["설정", "Settings", "환경설정", "시스템설정", "관리"],
"alternativeLevel2Names": ["회사정보", "회사 정보", "Company Info", "회사관리", "기업정보"],
"fallbackUrls": [
"/company-info",
"/ko/company-info",
"/settings/company-info",
"/ko/settings/company-info"
],
"scrollConfig": {
"sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar",
"menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']",
"scrollStep": 200,
"maxScrollAttempts": 10,
"scrollDelay": 300
}
},
"expectedAPIs": [
{
"method": "GET",
"path": "/api/v1/company-info",
"description": "회사 정보 조회"
},
{
"method": "PUT",
"path": "/api/v1/company-info/:id",
"description": "회사 정보 수정"
},
{
"method": "POST",
"path": "/api/v1/company-info",
"description": "회사 추가"
}
],
"steps": [
{
"id": 0,
"name": "사이드바 메뉴 전체 펼치기",
"description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비",
"actions": [
{ "type": "scroll", "target": "sidebar", "direction": "top", "description": "사이드바 최상단으로 스크롤" },
{ "type": "wait", "duration": 300 },
{ "type": "evaluate", "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" },
{ "type": "wait", "duration": 2000 }
],
"verification": [
"사이드바가 화면에 보이는지 확인",
"모든 메뉴가 펼쳐졌는지 확인"
]
},
{
"id": 1,
"name": "1차 메뉴 찾기: 설정 (스크롤 포함)",
"description": "사이드바를 스크롤하며 '설정' 메뉴를 찾아 클릭",
"actions": [
{
"type": "scrollAndFind",
"target": "설정",
"alternativeTexts": ["설정", "Settings", "환경설정", "시스템설정"],
"scrollContainer": "sidebar",
"maxAttempts": 10,
"description": "스크롤하며 설정 메뉴 찾기"
},
{ "type": "wait", "duration": 300 },
{ "type": "click_if_exists", "target": "설정", "description": "설정 메뉴 클릭" },
{ "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" },
{ "type": "screenshot", "name": "settings_menu_expanded" }
],
"verification": [
"설정 메뉴가 클릭되었는지 확인",
"서브메뉴가 펼쳐졌는지 확인",
"하위 메뉴 항목들이 보이는지 확인"
],
"fallback": {
"if": "메뉴를 찾을 수 없음",
"then": "사이드바 전체를 스크롤하며 재탐색"
}
},
{
"id": 2,
"name": "2차 메뉴 찾기: 회사정보 (스크롤 포함)",
"description": "서브메뉴에서 '회사정보'를 찾아 클릭",
"actions": [
{
"type": "scrollAndFind",
"target": "회사정보",
"alternativeTexts": ["회사정보", "회사 정보", "Company Info", "회사관리"],
"scrollContainer": "submenu",
"maxAttempts": 5,
"description": "서브메뉴에서 회사정보 찾기"
},
{ "type": "wait", "duration": 200 },
{ "type": "click_if_exists", "target": "회사정보", "description": "회사정보 메뉴 클릭" },
{ "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 },
{ "type": "screenshot", "name": "company_info_page" }
],
"verification": [
"회사정보 메뉴 클릭 성공",
"페이지 이동 또는 컨텐츠 로드"
]
},
{
"id": 3,
"name": "404 에러 감지 및 대체 경로 시도",
"description": "페이지 로드 후 404 에러 여부 확인, 404시 대체 경로 탐색",
"actions": [
{ "type": "wait", "duration": 1000 },
{ "type": "checkFor404", "indicators": [
"페이지를 찾을 수 없습니다",
"404",
"Not Found",
"존재하지 않거나"
]},
{ "type": "screenshot", "name": "page_load_result" }
],
"verification": [
"현재 페이지가 404인지 확인"
],
"onError404": {
"description": "404 에러 발생 시 대체 URL 시도",
"actions": [
{ "type": "log", "message": "404 감지 - 대체 경로 탐색 시작" },
{
"type": "tryAlternativeUrls",
"urls": [
"/company-info",
"/ko/company-info"
],
"stopOnSuccess": true
},
{
"type": "ifStillFailed",
"action": "navigateViaMenuClick",
"description": "URL 직접 접근 실패 시 메뉴 클릭으로 재시도"
}
]
}
},
{
"id": 4,
"name": "페이지 정상 로드 확인",
"description": "회사정보 페이지가 정상적으로 로드되었는지 확인",
"actions": [
{ "type": "verify", "target": "pageTitle", "contains": ["회사정보", "회사 정보", "Company"] },
{ "type": "verify", "target": "pageContent", "notContains": ["404", "찾을 수 없습니다", "Not Found"] }
],
"verification": [
"페이지 제목 '회사정보' 또는 관련 텍스트 표시",
"404 에러 메시지 미표시",
"콘텐츠가 정상 렌더링됨"
],
"successCriteria": {
"urlPattern": "/company-info",
"requiredElements": ["회사", "회사명", "대표자명"]
}
},
{
"step": 5,
"name": "페이지 제목 확인",
"action": "verify",
"target": "heading",
"expected": "회사정보",
"validation": "페이지 제목이 '회사정보'로 표시됨"
},
{
"step": 6,
"name": "회사 추가 버튼 존재 확인",
"action": "verify",
"target": "button[text='회사 추가']",
"expected": "button exists",
"validation": "회사 추가 버튼이 표시됨"
},
{
"step": 7,
"name": "수정 버튼 존재 확인",
"action": "verify",
"target": "button[text='수정']",
"expected": "button exists",
"validation": "수정 버튼이 표시됨"
},
{
"step": 8,
"name": "회사명 필드 확인",
"action": "verify",
"target": "textbox[label='회사명'][disabled]",
"expected": "프론트_테스트회사",
"validation": "회사명이 표시되고 비활성화 상태"
},
{
"step": 9,
"name": "대표자명 필드 확인",
"action": "verify",
"target": "textbox[label='대표자명'][disabled]",
"expected": "프론트",
"validation": "대표자명이 표시되고 비활성화 상태"
},
{
"step": 10,
"name": "업태 필드 확인",
"action": "verify",
"target": "textbox[label='업태'][disabled]",
"expected": "업태명",
"validation": "업태가 표시되고 비활성화 상태"
},
{
"step": 11,
"name": "업종 필드 확인",
"action": "verify",
"target": "textbox[label='업종'][disabled]",
"expected": "업종명",
"validation": "업종이 표시되고 비활성화 상태"
},
{
"step": 12,
"name": "주소 필드 확인",
"action": "verify",
"target": "textbox[label='주소명'][disabled]",
"expected": "주소 표시",
"validation": "주소가 표시되고 비활성화 상태"
},
{
"step": 13,
"name": "이메일 필드 확인",
"action": "verify",
"target": "textbox[label='이메일 (아이디)'][disabled]",
"expected": "이메일 표시",
"validation": "이메일이 표시되고 비활성화 상태"
},
{
"step": 14,
"name": "사업자등록번호 필드 확인",
"action": "verify",
"target": "textbox[label='사업자등록번호'][disabled]",
"expected": "사업자등록번호 표시",
"validation": "사업자등록번호가 표시되고 비활성화 상태"
},
{
"step": 15,
"name": "수정 버튼 클릭",
"action": "click_if_exists",
"target": "button[text='수정']",
"expected": "edit mode enabled",
"validation": "수정 모드로 전환됨"
},
{
"step": 16,
"name": "수정 모드 - 필드 활성화 확인",
"action": "verify",
"target": "textbox:not([disabled])",
"expected": "fields enabled",
"validation": "텍스트 필드들이 활성화됨"
},
{
"step": 17,
"name": "취소 버튼 클릭",
"action": "click_if_exists",
"target": "button[text='취소']",
"expected": "edit mode disabled",
"validation": "조회 모드로 복귀"
},
{
"step": 18,
"name": "회사 추가 버튼 클릭",
"action": "click_if_exists",
"target": "button[text='회사 추가']",
"expected": "dialog opened",
"validation": "회사 추가 다이얼로그가 열림"
},
{
"step": 19,
"name": "회사 추가 다이얼로그 확인",
"action": "verify",
"target": "dialog",
"expected": "회사 추가 다이얼로그 표시",
"validation": "다이얼로그 제목, 입력 필드, 버튼 확인"
},
{
"step": 20,
"name": "다이얼로그 닫기",
"action": "click_if_exists",
"target": "dialog button[text='취소']",
"expected": "dialog closed",
"validation": "다이얼로그가 닫힘"
},
{
"step": 21,
"name": "수정 모드에서 데이터 변경 테스트",
"description": "실제 데이터를 수정하고 저장 기능 검증",
"actions": [
{ "type": "click_if_exists", "target": "수정", "description": "수정 모드 진입" }
],
"expect": {
"fieldsEnabled": true
}
},
{
"step": 22,
"name": "업태 필드 수정",
"description": "업태 필드 값 변경",
"actions": [
{ "type": "clear", "target": "업태" },
{ "type": "fill", "target": "업태", "value": "테스트업태_수정" }
]
},
{
"step": 23,
"name": "저장 버튼 클릭",
"description": "수정된 회사 정보 저장",
"actions": [
{ "type": "click_if_exists", "target": "저장" }
],
"waitFor": {
"type": "apiResponse",
"method": "PUT",
"timeout": 5000
},
"expect": {
"toast": ["수정", "완료", "성공", "저장"]
}
},
{
"step": 24,
"name": "⚠️ 필수 검증: 수정 데이터 반영 확인",
"note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!",
"description": "수정된 업태 값이 반영되었는지 확인",
"verify": {
"fieldValue": {
"target": "업태",
"expected": "테스트업태_수정"
}
}
},
{
"step": 25,
"name": "회사 추가 다이얼로그 열기",
"description": "회사 추가 버튼 클릭하여 다이얼로그 열기",
"actions": [
{ "type": "click_if_exists", "target": "회사 추가" }
],
"expect": {
"dialog": true,
"visible": ["회사명", "대표자명", "사업자등록번호", "등록", "취소"]
}
},
{
"step": 26,
"name": "새 회사 정보 입력",
"description": "회사 추가 다이얼로그에서 필수 정보 입력",
"actions": [
{ "type": "fill", "target": "회사명", "value": "테스트회사_{timestamp}" },
{ "type": "fill", "target": "대표자명", "value": "테스트대표" },
{ "type": "fill", "target": "사업자등록번호", "value": "123-45-67890" }
]
},
{
"step": 27,
"name": "회사 등록",
"description": "등록 버튼 클릭하여 새 회사 등록",
"actions": [
{ "type": "click_if_exists", "target": "등록" }
],
"waitFor": {
"type": "apiResponse",
"method": "POST",
"timeout": 5000
},
"expect": {
"toast": ["등록", "완료", "성공"],
"dialogClosed": true
}
},
{
"step": 28,
"name": "⚠️ 필수 검증: 회사 등록 반영 확인",
"note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 등록 확인 필수!",
"description": "등록된 회사가 목록에 표시되는지 확인",
"verify": {
"visible": "테스트회사"
}
},
{
"step": 29,
"name": "원복: 업태 필드 원래 값으로 복구",
"description": "테스트 후 원래 값으로 복구",
"actions": [
{ "type": "click_if_exists", "target": "수정" },
{ "type": "clear", "target": "업태" },
{ "type": "fill", "target": "업태", "value": "업태명" },
{ "type": "click_if_exists", "target": "저장" }
],
"expect": {
"toast": ["수정", "완료", "성공", "저장"]
}
}
],
"notes": [
"직접 URL 접근 금지: 반드시 메뉴 클릭으로 페이지 진입 (404 방지)",
"스크롤 필수: 사이드바가 길 경우 메뉴가 화면 밖에 있을 수 있음",
"대체 경로: 메뉴명이 변경되었을 수 있으므로 다양한 이름으로 탐색",
"메뉴 계층: 설정 > 회사정보"
]
}

View File

@@ -0,0 +1,164 @@
{
"id": "customer-event",
"name": "이벤트 게시판 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "고객센터 > 이벤트 게시판 메뉴의 이벤트 목록 조회/상세보기 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "고객센터",
"level2": "이벤트 게시판",
"expectedUrl": "/customer-center/events",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 고객센터 > 이벤트 게시판",
"action": "menu_navigate",
"level1": "고객센터",
"level2": "이벤트 게시판",
"expected": {
"url_contains": "/customer-center",
"visible": ["이벤트"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"이벤트 목록 표시",
"이벤트 카드 또는 리스트"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "이벤트 페이지 구조 확인",
"action": "verify_elements",
"checks": [
"이벤트 목록 (카드 또는 테이블)",
"이벤트 상태 표시 (진행중/종료)",
"검색 기능"
],
"expected": "이벤트 페이지 구조 정상"
},
{
"id": 4,
"phase": "READ",
"name": "[READ] 이벤트 목록 데이터 확인",
"action": "verify_detail",
"checks": [
"이벤트 항목 존재 또는 '등록된 이벤트 없음' 메시지"
],
"expected": "이벤트 데이터 표시"
},
{
"id": 5,
"phase": "FILTER",
"name": "[FILTER] 상태별 필터",
"action": "verify_elements",
"checks": [
"전체/진행중/종료 필터 가능"
],
"expected": "상태 필터 표시"
},
{
"id": 6,
"phase": "READ",
"name": "[READ] 이벤트 상세 보기",
"action": "click_if_exists",
"target": "[class*='event']:first-child, table tbody tr:first-child, [class*='card']:first-child",
"expected": {
"detail_view": true
}
},
{
"id": 7,
"name": "이벤트 상세 정보 확인",
"action": "verify_detail",
"checks": [
"이벤트 제목",
"이벤트 기간",
"이벤트 내용",
"이벤트 이미지 (있을 경우)"
],
"expected": "이벤트 상세 정보 표시"
},
{
"id": 8,
"name": "이벤트 참여 버튼 확인",
"action": "verify_elements",
"checks": [
"참여하기 버튼 존재 여부 (진행중 이벤트)"
],
"expected": "참여 버튼 확인"
},
{
"id": 9,
"name": "공유 기능 확인",
"action": "verify_elements",
"checks": [
"공유 버튼 또는 링크 복사 기능"
],
"expected": "공유 기능 표시"
},
{
"id": 10,
"name": "목록으로 돌아가기",
"action": "click_if_exists",
"target": "button:has-text('목록'), a:has-text('목록'), [class*='back']",
"expected": "목록 페이지로 복귀"
},
{
"id": 11,
"name": "페이지네이션 확인",
"action": "verify_elements",
"checks": [
"페이지 번호 또는 더보기 버튼"
],
"expected": "페이지네이션 표시"
},
{
"id": 12,
"name": "이벤트 기간 정보 확인",
"action": "verify_detail",
"checks": [
"시작일/종료일 표시",
"D-Day 또는 남은 기간 표시"
],
"expected": "기간 정보 표시"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/customer/events",
"description": "이벤트 목록 조회"
},
{
"method": "GET",
"endpoint": "/api/v1/customer/events/:id",
"description": "이벤트 상세 조회"
}
],
"requiredVerifications": [
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "이벤트 목록, 이벤트 카드/리스트 존재"
}
],
"rollbackPlan": {
"note": "조회 전용 페이지로 데이터 변경 없음"
}
}

View File

@@ -0,0 +1,185 @@
{
"id": "customer-faq",
"name": "FAQ 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
},
"description": "고객센터 > FAQ 메뉴의 자주 묻는 질문 조회/검색/카테고리 필터 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "고객센터",
"level2": "FAQ",
"expectedUrl": "/customer-center/faq",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 고객센터 > FAQ",
"action": "menu_navigate",
"level1": "고객센터",
"level2": "FAQ",
"expected": {
"url_contains": "/customer-center/faq",
"visible": [
"FAQ",
"자주 묻는 질문"
]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"FAQ 목록 표시",
"카테고리 또는 검색 기능 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "FAQ 페이지 구조 확인",
"action": "verify_elements",
"checks": [
"FAQ 항목 목록 (아코디언 또는 리스트)",
"카테고리 탭/필터",
"검색 기능"
],
"expected": "FAQ 페이지 구조 정상"
},
{
"id": 4,
"phase": "READ",
"name": "[READ] FAQ 목록 데이터 확인",
"action": "verify_detail",
"checks": [
"FAQ 항목 존재 또는 '등록된 FAQ 없음' 메시지"
],
"expected": "FAQ 데이터 표시"
},
{
"id": 5,
"phase": "READ",
"name": "[READ] FAQ 항목 펼치기",
"action": "click_if_exists",
"target": "[class*='accordion']:first-child, [class*='faq']:first-child, table tbody tr:first-child",
"expected": {
"accordion_open": true
}
},
{
"id": 6,
"name": "FAQ 답변 내용 확인",
"action": "verify_detail",
"checks": [
"답변 내용 표시"
],
"expected": "FAQ 답변 표시"
},
{
"id": 7,
"phase": "FILTER",
"name": "[FILTER] 카테고리 필터",
"action": "verify_element",
"target": "[class*='category'], [class*='tab']",
"expected": "카테고리 선택 가능"
},
{
"id": 8,
"phase": "FILTER",
"name": "[FILTER] 카테고리 선택 후 결과",
"action": "verify_detail",
"checks": [
"해당 카테고리 FAQ만 표시"
],
"expected": "카테고리 필터 동작"
},
{
"id": 9,
"phase": "SEARCH",
"name": "[SEARCH] FAQ 검색",
"action": "click_if_exists",
"target": "input[type='search'], input[placeholder*='검색']",
"value": "테스트",
"submit": true
},
{
"id": 10,
"phase": "SEARCH",
"name": "[SEARCH] 검색 결과 확인",
"action": "verify_detail",
"checks": [
"검색 결과 표시 또는 결과 없음 메시지"
],
"expected": "검색 기능 동작"
},
{
"id": 11,
"name": "FAQ 접기/펼치기 토글",
"action": "verify_elements",
"checks": [
"접기/펼치기 아이콘 또는 버튼"
],
"expected": "토글 기능 표시"
},
{
"id": 12,
"name": "전체 보기/접기",
"action": "verify_elements",
"checks": [
"전체 펼치기/접기 버튼 존재 여부"
],
"expected": "전체 토글 기능 확인"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/customer/faq",
"description": "FAQ 목록 조회"
},
{
"method": "GET",
"endpoint": "/api/v1/customer/faq/categories",
"description": "FAQ 카테고리 조회"
}
],
"requiredVerifications": [
{
"id": 3,
"name": "검색/필터",
"steps": [
7,
8,
9,
10
],
"criteria": "카테고리 필터 + 검색 기능 동작"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [
2
],
"criteria": "FAQ 목록, 카테고리/검색 기능 존재"
}
],
"rollbackPlan": {
"note": "조회 전용 페이지로 데이터 변경 없음"
}
}

View File

@@ -0,0 +1,201 @@
{
"id": "customer-notice",
"name": "공지사항 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "고객센터 > 공지사항 메뉴의 공지사항 조회/검색/상세보기 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "고객센터",
"level2": "공지사항",
"expectedUrl": "/customer-center/notices",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 고객센터 > 공지사항",
"action": "menu_navigate",
"level1": "고객센터",
"level2": "공지사항",
"expected": {
"url_contains": "/customer-center/notices",
"visible": ["공지사항"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"공지사항 목록 표시",
"검색 기능 존재",
"목록 테이블 또는 카드"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "공지사항 목록 구조 확인",
"action": "verify_table",
"checks": [
"번호 컬럼",
"제목 컬럼",
"작성자 컬럼",
"작성일 컬럼",
"조회수 컬럼"
],
"expected": "공지사항 목록 표시"
},
{
"id": 4,
"phase": "READ",
"name": "[READ] 공지사항 목록 데이터 확인",
"action": "verify_detail",
"checks": [
"공지사항 데이터 행 존재 또는 '등록된 공지 없음' 메시지"
],
"expected": "공지사항 데이터 표시"
},
{
"id": 5,
"phase": "SEARCH",
"name": "[SEARCH] 공지사항 검색",
"action": "fill",
"target": "input[type='search'], input[placeholder*='검색']",
"value": "테스트",
"submit": true
},
{
"id": 6,
"phase": "SEARCH",
"name": "[SEARCH] 검색 결과 확인",
"action": "verify_detail",
"checks": [
"검색 결과 표시 또는 결과 없음 메시지"
],
"expected": "검색 기능 동작"
},
{
"id": 7,
"phase": "SEARCH",
"name": "[SEARCH] 검색 초기화",
"action": "click_if_exists",
"target": "button:has-text('초기화'), button:has-text('전체'), button[class*='clear']",
"expected": "검색 초기화"
},
{
"id": 8,
"phase": "READ",
"name": "[READ] 공지사항 상세 보기",
"action": "click_if_exists",
"target": "table tbody tr:first-child, [class*='list'] [class*='item']:first-child",
"expected": {
"detail_view": true,
"url_change": true
}
},
{
"id": 9,
"name": "상세 페이지 구조 확인",
"action": "verify_elements",
"checks": [
"제목 표시",
"작성자 표시",
"작성일 표시",
"내용 표시"
],
"expected": "상세 페이지 정상 표시"
},
{
"id": 10,
"name": "첨부파일 확인",
"action": "verify_elements",
"checks": [
"첨부파일 영역 존재 여부"
],
"expected": "첨부파일 영역 확인"
},
{
"id": 11,
"name": "이전/다음 글 네비게이션",
"action": "verify_elements",
"checks": [
"이전 글 링크 또는 버튼",
"다음 글 링크 또는 버튼"
],
"expected": "글 네비게이션 표시"
},
{
"id": 12,
"name": "목록으로 돌아가기",
"action": "click_if_exists",
"target": "button:has-text('목록'), a:has-text('목록'), [class*='back']",
"expected": "목록 페이지로 복귀"
},
{
"id": 13,
"name": "페이지네이션 확인",
"action": "verify_elements",
"checks": [
"페이지 번호 표시",
"이전/다음 페이지 버튼"
],
"expected": "페이지네이션 표시"
},
{
"id": 14,
"name": "정렬 기능 확인",
"action": "verify_elements",
"checks": [
"최신순/조회순 정렬 가능"
],
"expected": "정렬 기능 표시"
},
{
"id": 15,
"name": "중요 공지 표시 확인",
"action": "verify_elements",
"checks": [
"중요/고정 공지 상단 표시 여부"
],
"expected": "중요 공지 표시 확인"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/customer-center/noticess",
"description": "공지사항 목록 조회"
},
{
"method": "GET",
"endpoint": "/api/v1/customer-center/noticess/:id",
"description": "공지사항 상세 조회"
}
],
"requiredVerifications": [
{
"id": 3,
"name": "검색/필터",
"steps": [5, 6, 7],
"criteria": "검색 기능 동작"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "공지사항 목록, 검색 기능, 목록 표시 존재"
}
],
"rollbackPlan": {
"note": "조회 전용 페이지로 데이터 변경 없음"
}
}

View File

@@ -0,0 +1,150 @@
{
"enabled": true,
"id": "department-add",
"name": "부서관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "인사관리 > 부서관리 메뉴의 부서 트리/목록 조회 및 UI 검증 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "인사관리",
"level2": "부서관리",
"expectedUrl": "/hr/department-management",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 인사관리 > 부서관리",
"action": "menu_navigate",
"level1": "인사관리",
"level2": "부서관리",
"expected": {
"url_contains": "/hr/department",
"visible": ["부서관리"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"부서 목록 또는 트리 표시",
"부서 추가 버튼 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "부서 트리/목록 구조 확인",
"action": "verify_elements",
"checks": [
"부서 목록 또는 트리 구조 표시",
"추가 버튼 존재",
"부서 정보 표시"
],
"expected": "부서관리 페이지 정상 표시"
},
{
"id": 4,
"phase": "READ",
"name": "[READ] 부서 목록 데이터 확인",
"action": "verify_detail",
"checks": [
"부서 목록 데이터 표시됨"
],
"expected": "부서 목록 정상"
},
{
"id": 5,
"phase": "READ",
"name": "[READ] 첫 번째 부서 노드 클릭",
"action": "click_if_exists",
"target": "table tbody tr:first-child, [class*='tree'] > *:first-child, li:first-child"
},
{
"id": 6,
"phase": "READ",
"name": "[READ] 부서 상세 정보 확인",
"action": "verify_detail",
"checks": [
"부서 상세 정보 표시"
],
"expected": "부서 상세 정보 확인"
},
{
"id": 7,
"name": "부서 추가 버튼 확인",
"action": "click_if_exists",
"target": "button:has-text('추가'), button:has-text('등록'), button:has-text('부서 추가')"
},
{
"id": 8,
"name": "추가 폼/모달 확인",
"action": "verify_elements",
"checks": [
"부서명 입력 필드 존재",
"저장/등록 버튼 존재"
],
"expected": "부서 추가 폼 표시"
},
{
"id": 9,
"name": "추가 모달 닫기",
"action": "close_modal_if_open",
"expected": "모달 닫힘"
},
{
"id": 10,
"name": "부서 트리 구조 확인",
"action": "verify_elements",
"checks": [
"부서 계층 구조 표시"
],
"expected": "트리 구조 확인"
},
{
"id": 11,
"name": "삭제 버튼 존재 확인",
"action": "verify_elements",
"checks": [
"삭제 버튼 존재 여부"
],
"expected": "삭제 기능 확인"
},
{
"id": 12,
"name": "부서관리 페이지 최종 확인",
"action": "verify_detail",
"checks": [
"부서관리 페이지 정상 동작"
],
"expected": "페이지 정상 확인"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/departments",
"description": "부서 목록 조회"
}
],
"requiredVerifications": [
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "부서 목록, 추가 버튼 존재"
}
],
"rollbackPlan": {
"note": "READ-only 패턴으로 안정성 우선, CRUD 테스트 제거"
}
}

View File

@@ -0,0 +1,468 @@
{
"id": "deposit-management",
"name": "입금관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "입금관리 목록 조회, 계정과목명 일괄변경, 상세 수정 기능 E2E 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"url": "/ko/accounting/deposits",
"navigation": {
"targetUrl": "/accounting/deposits",
"urlPattern": "/accounting/deposits|/ko/accounting/deposits",
"menuHints": ["입금관리", "입금 관리", "회계관리"]
},
"menuNavigation": {
"level1": "회계관리",
"level2": "입금관리",
"expectedUrl": "/ko/accounting/deposits",
"searchWithinParent": true,
"closeOtherMenus": true
},
"menuNavigationEnhanced": {
"strategy": "scroll-and-search",
"description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지",
"level1": "회계관리",
"level2": "입금관리",
"alternativeLevel1Names": ["회계관리", "회계 관리", "Accounting"],
"alternativeLevel2Names": ["입금관리", "입금 관리", "Deposits"],
"scrollConfig": {
"sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar",
"menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']",
"scrollStep": 200,
"maxScrollAttempts": 10,
"scrollDelay": 300
}
},
"timeout": 60000,
"tags": ["accounting", "deposit", "crud"],
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": "step-0",
"name": "사이드바 메뉴 전체 펼치기",
"description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비",
"actions": [
{ "type": "scroll", "target": "sidebar", "direction": "top", "description": "사이드바 최상단으로 스크롤" },
{ "type": "wait", "duration": 300 },
{ "type": "evaluate", "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" },
{ "type": "wait", "duration": 2000 }
]
},
{
"id": "step-1",
"name": "2단계 메뉴 진입: 회계관리 > 입금관리",
"description": "사이드바를 스크롤하며 회계관리 > 입금관리 메뉴를 찾아 클릭",
"actions": [
{
"type": "scrollAndFind",
"target": "회계관리",
"alternativeTexts": ["회계관리", "회계 관리", "Accounting"],
"scrollContainer": "sidebar",
"maxAttempts": 10,
"description": "스크롤하며 회계관리 메뉴 찾기"
},
{ "type": "click_if_exists", "target": "회계관리", "description": "회계관리 메뉴 클릭" },
{ "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" },
{
"type": "scrollAndFind",
"target": "입금관리",
"alternativeTexts": ["입금관리", "입금 관리", "Deposits"],
"scrollContainer": "submenu",
"maxAttempts": 5,
"description": "서브메뉴에서 입금관리 찾기"
},
{ "type": "click_if_exists", "target": "입금관리", "description": "입금관리 메뉴 클릭" },
{ "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 }
],
"expect": {
"url": "/accounting/deposits",
"visible": ["입금관리", "총 입금"]
},
"verification": [
"회계관리 메뉴가 펼쳐졌는지 확인",
"입금관리 서브메뉴 클릭 성공",
"404 에러 없이 페이지 로드 완료"
]
},
{
"id": "step-2",
"name": "목록 페이지 구조 확인",
"description": "테이블 및 필터 요소 확인",
"expect": {
"visible": ["입금일", "입금계좌", "입금자명", "입금금액", "거래처", "적요", "입금유형"],
"elements": {
"statisticsCards": ["총 입금", "당월 입금", "거래처 미설정", "입금유형 미설정"],
"filters": ["계정과목명", "저장", "새로고침"],
"pagination": true
}
}
},
{
"id": "step-3",
"name": "계정과목명 드롭다운 옵션 확인",
"description": "계정과목명 일괄변경 드롭다운 옵션 검증",
"actions": [
{ "type": "click_if_exists", "target": "계정과목명 드롭다운", "description": "드롭다운 열기" }
],
"expect": {
"options": ["미설정", "매출대금", "선수금", "가수금", "임대수익", "이자수익", "보증금 반환", "차입금", "자본금", "부가세 환급", "기타"]
}
},
{
"id": "step-4",
"name": "체크박스 선택 후 계정과목명 일괄변경",
"description": "테이블 행 선택 후 계정과목명 일괄변경 저장",
"actions": [
{ "type": "click_if_exists", "target": "첫 번째 행 체크박스", "description": "행 선택" },
{ "type": "click_if_exists", "target": "계정과목명 드롭다운", "description": "드롭다운 열기" },
{ "type": "click_if_exists", "target": "매출대금", "description": "매출대금 선택" },
{ "type": "click_if_exists", "target": "저장", "description": "저장 버튼 클릭" }
],
"expect": {
"dialog": "확인 다이얼로그 표시",
"toast": "변경 완료 메시지"
}
},
{
"id": "step-4-1",
"name": "⚠️ 필수 검증: 계정과목명 변경 데이터 반영 확인",
"note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!",
"description": "저장 후 테이블에서 변경된 입금유형 값 확인",
"expect": {
"tableCell": {
"row": 1,
"column": "입금유형",
"value": "매출대금"
}
},
"knownBugReference": "BUG-SALES-20260115-001 (매출관리 동일 버그 확인 필요)"
},
{
"id": "step-5",
"name": "입금 상세 페이지 이동",
"description": "테이블 행 클릭하여 상세 페이지로 이동",
"actions": [
{ "type": "click_if_exists", "target": "테이블 첫 번째 행", "description": "행 클릭 (체크박스 제외 영역)" }
],
"expect": {
"url": "/accounting/deposits/{id}",
"visible": ["입금 상세", "기본 정보", "목록", "삭제", "수정"]
}
},
{
"id": "step-6",
"name": "상세 페이지 읽기 모드 필드 확인",
"description": "수정 전 필드들이 비활성화 상태인지 확인",
"expect": {
"fields": [
{ "name": "입금일", "disabled": true },
{ "name": "입금계좌", "disabled": true },
{ "name": "입금자명", "disabled": true },
{ "name": "입금금액", "disabled": true },
{ "name": "적요", "disabled": true },
{ "name": "거래처", "disabled": true },
{ "name": "입금 유형", "disabled": true }
]
}
},
{
"id": "step-7",
"name": "수정 모드 전환",
"description": "수정 버튼 클릭하여 편집 모드로 전환",
"click": "수정",
"expect": {
"url": "/accounting/deposits/{id}?mode=edit",
"visible": ["입금 수정", "취소", "저장"],
"notVisible": ["목록", "삭제", "수정"]
}
},
{
"id": "step-8",
"name": "수정 모드 필드 활성화 검증",
"description": "수정 가능한 필드와 불가능한 필드 확인",
"expect": {
"fields": [
{ "name": "입금일", "disabled": true, "note": "은행데이터 - 수정 불가" },
{ "name": "입금계좌", "disabled": true, "note": "은행데이터 - 수정 불가" },
{ "name": "입금자명", "disabled": true, "note": "은행데이터 - 수정 불가" },
{ "name": "입금금액", "disabled": true, "note": "은행데이터 - 수정 불가" },
{ "name": "적요", "disabled": false, "editable": true },
{ "name": "거래처", "disabled": false, "type": "combobox", "editable": true },
{ "name": "입금 유형", "disabled": false, "type": "combobox", "editable": true }
]
}
},
{
"id": "step-9",
"name": "거래처 드롭다운 옵션 확인",
"description": "거래처 선택 드롭다운 옵션 검증",
"actions": [
{ "type": "click_if_exists", "target": "거래처 드롭다운", "description": "드롭다운 열기" }
],
"expect": {
"options": ["거래처테스트", "아크더레드", "코브라브릿지", "가우스전자", "아크아크"]
}
},
{
"id": "step-10",
"name": "입금 유형 드롭다운 옵션 확인",
"description": "입금 유형 선택 드롭다운 옵션 검증",
"actions": [
{ "type": "click_if_exists", "target": "입금 유형 드롭다운", "description": "드롭다운 열기" }
],
"expect": {
"options": ["미설정", "매출대금", "선수금", "가수금", "임대수익", "이자수익", "보증금 반환", "차입금", "자본금", "부가세 환급", "기타"]
}
},
{
"id": "step-11",
"name": "수정 데이터 입력",
"description": "수정 가능한 필드에 테스트 데이터 입력",
"form": {
"fields": [
{ "name": "적요", "type": "text", "value": "테스트 적요 수정" }
]
},
"actions": [
{ "type": "click_if_exists", "target": "거래처 드롭다운", "description": "거래처 드롭다운 열기" },
{ "type": "click_if_exists", "target": "거래처테스트", "description": "거래처 선택" },
{ "type": "click_if_exists", "target": "입금 유형 드롭다운", "description": "입금 유형 드롭다운 열기" },
{ "type": "click_if_exists", "target": "매출대금", "description": "매출대금 선택" }
]
},
{
"id": "step-12",
"name": "저장 및 결과 확인",
"description": "저장 버튼 클릭 후 데이터 반영 확인",
"click": "저장",
"expect": {
"toast": "저장 완료 메시지",
"url": "/accounting/deposits/{id}",
"mode": "view"
}
},
{
"id": "step-12-1",
"name": "⚠️ 필수 검증: 수정 데이터 반영 확인",
"note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!",
"description": "저장 후 상세 페이지에서 변경된 값 확인",
"expect": {
"fields": [
{ "name": "적요", "value": "테스트 적요 수정" },
{ "name": "거래처", "value": "거래처테스트" },
{ "name": "입금 유형", "value": "매출대금" }
]
}
},
{
"id": "step-13",
"name": "취소 버튼 동작 확인",
"description": "수정 모드에서 취소 버튼 동작 검증",
"actions": [
{ "type": "click_if_exists", "target": "수정", "description": "수정 모드 진입" },
{ "type": "click_if_exists", "target": "취소", "description": "취소 버튼 클릭" }
],
"expect": {
"url": "/accounting/deposits/{id}",
"mode": "view",
"visible": ["입금 상세", "목록", "삭제", "수정"]
}
},
{
"id": "step-14",
"name": "목록 버튼 동작 확인",
"description": "목록 버튼 클릭하여 목록 페이지로 이동",
"click": "목록",
"expect": {
"url": "/accounting/deposits",
"visible": ["입금관리", "총 입금"]
}
},
{
"id": "step-15",
"name": "필터 드롭다운 검증",
"description": "목록 페이지 필터 드롭다운 옵션 확인",
"note": "3개의 필터 드롭다운 존재 (거래처, 입금유형, 정렬)",
"expect": {
"filters": [
{ "name": "거래처 필터", "default": "전체" },
{ "name": "입금유형 필터", "default": "전체" },
{ "name": "정렬", "default": "최신순", "options": ["최신순", "등록순", "금액 높은순", "금액 낮은순"] }
]
}
},
{
"id": "step-16",
"name": "날짜 필터 검증",
"description": "날짜 필터 버튼 동작 확인",
"actions": [
{ "type": "click_if_exists", "target": "당해년도", "description": "당해년도 버튼 클릭" }
],
"expect": {
"dateRange": {
"start": "2026-01-01",
"end": "2026-12-31"
}
}
},
{
"id": "step-17",
"name": "페이지네이션 동작 확인",
"description": "페이지네이션 버튼 동작 검증",
"expect": {
"pagination": {
"totalItems": 60,
"itemsPerPage": 20,
"currentPage": 1,
"totalPages": 3
}
},
"actions": [
{ "type": "click_if_exists", "target": "다음", "description": "다음 페이지로 이동" }
],
"expectAfterAction": {
"currentPage": 2,
"displayText": "전체 60개 중 21-40개 표시"
}
}
],
"deleteTest": {
"note": "삭제 테스트 - 이전에 스킵되었으나 CRUD 완전성을 위해 추가",
"steps": [
{
"id": "step-delete-1",
"name": "삭제 버튼 클릭",
"description": "상세 페이지에서 삭제 버튼 클릭",
"actions": [
{ "type": "click_if_exists", "target": "삭제" }
],
"expect": {
"confirmDialog": true,
"dialogText": ["삭제", "하시겠습니까"]
}
},
{
"id": "step-delete-2",
"name": "삭제 확인",
"description": "삭제 확인 다이얼로그에서 확인 클릭",
"actions": [
{ "type": "click_if_exists", "target": "확인", "description": "삭제 확인" }
],
"waitFor": {
"type": "navigation",
"url": "/accounting/deposits",
"timeout": 5000
},
"expect": {
"toast": ["삭제", "완료", "성공"],
"url": "/accounting/deposits"
}
},
{
"id": "step-delete-3",
"name": "⚠️ 필수 검증: 삭제 데이터 반영 확인",
"note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 삭제 확인 필수!",
"description": "목록에서 삭제된 입금 내역이 없어졌는지 확인",
"verify": {
"tableNotContains": "테스트 적요 수정"
}
}
]
},
"knownBugs": [
{
"id": "BUG-SALES-20260115-001",
"description": "계정과목명 일괄변경 시 토스트 성공 표시되나 실제 데이터 미변경 (매출관리)",
"relatedSteps": ["step-4-1"],
"note": "입금관리에서도 동일한 버그가 존재할 수 있으므로 step-4-1에서 검증 필수"
}
],
"testData": {
"sampleDeposit": {
"date": "2025-12-28",
"account": "운영계좌",
"depositor": "CJ대한통운",
"amount": "8,209,677",
"vendor": "CJ대한통운",
"description": "CJ대한통운 입금",
"depositType": "미설정"
},
"modifiedData": {
"description": "테스트 적요 수정",
"vendor": "거래처테스트",
"depositType": "매출대금"
}
},
"pageStructure": {
"listPage": {
"url": "/accounting/deposits",
"title": "입금관리",
"statistics": ["총 입금", "당월 입금", "거래처 미설정", "입금유형 미설정"],
"tableColumns": ["checkbox", "입금일", "입금계좌", "입금자명", "입금금액", "거래처", "적요", "입금유형", "action"],
"batchUpdate": {
"label": "계정과목명",
"saveButton": "저장"
},
"filters": ["거래처", "입금유형", "정렬"],
"dateFilters": ["당해년도", "전전월", "전월", "당월", "어제", "오늘"]
},
"detailPage": {
"url": "/accounting/deposits/{id}",
"title": "입금 상세",
"buttons": ["목록", "삭제", "수정"],
"fields": {
"readOnly": ["입금일", "입금계좌", "입금자명", "입금금액"],
"editable": ["적요", "거래처", "입금 유형"]
}
},
"editPage": {
"url": "/accounting/deposits/{id}?mode=edit",
"title": "입금 수정",
"buttons": ["취소", "저장"]
}
},
"dropdownOptions": {
"accountSubject": {
"label": "계정과목명",
"options": ["미설정", "매출대금", "선수금", "가수금", "임대수익", "이자수익", "보증금 반환", "차입금", "자본금", "부가세 환급", "기타"]
},
"depositType": {
"label": "입금 유형",
"options": ["미설정", "매출대금", "선수금", "가수금", "임대수익", "이자수익", "보증금 반환", "차입금", "자본금", "부가세 환급", "기타"]
},
"vendor": {
"label": "거래처",
"options": ["거래처테스트", "아크더레드", "코브라브릿지", "가우스전자", "아크아크"]
},
"sortOrder": {
"label": "정렬",
"options": ["최신순", "등록순", "금액 높은순", "금액 낮은순"]
}
},
"assertions": [
{
"type": "url",
"expected": "/accounting/deposits",
"message": "목록 페이지 URL 확인"
},
{
"type": "text",
"target": "body",
"expected": "입금관리",
"message": "페이지 타이틀 확인"
}
]
}

View File

@@ -0,0 +1,188 @@
{
"enabled": true,
"id": "draft-box",
"name": "기안함 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "결재관리 > 기안함 메뉴의 문서 목록 조회, 검색, 필터 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "결재관리",
"level2": "기안함",
"expectedUrl": "/approval/draft",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 결재관리 > 기안함",
"action": "menu_navigate",
"level1": "결재관리",
"level2": "기안함",
"expected": {
"url_contains": "/approval/draft",
"visible": ["기안함"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"기안함 목록 표시",
"검색/필터 기능 존재",
"문서 작성 버튼 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "통계 카드 확인",
"action": "verify_element",
"checks": [
"진행 카드 표시",
"완료 카드 표시",
"반려 카드 표시",
"임시 저장 카드 표시"
],
"expected": "통계 카드 4개 표시"
},
{
"id": 4,
"name": "기안함 테이블 구조 확인",
"action": "verify_table",
"checks": [
"문서번호 컬럼",
"문서유형 컬럼",
"제목 컬럼",
"결재자 컬럼",
"기안일시 컬럼",
"상태 컬럼"
],
"expected": "기안함 테이블 컬럼 정상 표시"
},
{
"id": 5,
"name": "데이터 로드 확인",
"action": "verify_detail",
"checks": [
"기안 문서 데이터 행 존재 또는 '데이터가 없습니다' 메시지"
],
"expected": "기안 문서 데이터 표시"
},
{
"id": 6,
"phase": "SEARCH",
"name": "[SEARCH] 검색 기능 테스트",
"action": "fill",
"target": "input[type='search'], input[placeholder*='검색']",
"value": "테스트",
"submit": true
},
{
"id": 7,
"phase": "SEARCH",
"name": "[SEARCH] 검색 결과 확인",
"action": "verify_detail",
"checks": [
"검색 결과 표시 또는 결과 없음 메시지"
],
"expected": "검색 기능 동작"
},
{
"id": 8,
"phase": "SEARCH",
"name": "[SEARCH] 검색 초기화",
"action": "click_if_exists",
"target": "button:has-text('초기화'), button:has-text('전체'), button[class*='clear']",
"expected": "검색 초기화"
},
{
"id": 9,
"name": "필터 기능 테스트",
"action": "click_if_exists",
"target": "select, [role='combobox'], button:has-text('임시저장')",
"expected": "필터 옵션 표시"
},
{
"id": 10,
"phase": "READ",
"name": "[READ] 문서 상세 보기",
"action": "click_if_exists",
"target": "table tbody tr:first-child td:nth-child(2), table tbody tr:first-child",
"expected": {
"detail_view": true
}
},
{
"id": 11,
"name": "상세 페이지/모달 확인",
"action": "verify_element",
"checks": [
"문서 상세 정보 표시",
"문서번호 또는 제목 표시"
],
"expected": "상세 정보 표시"
},
{
"id": 12,
"name": "모달/상세 닫기",
"action": "close_modal_if_open",
"expected": "모달 닫힘"
},
{
"id": 13,
"name": "페이지네이션 확인",
"action": "verify_element",
"checks": [
"페이지 번호 또는 이전/다음 버튼"
],
"expected": "페이지네이션 표시"
},
{
"id": 14,
"name": "문서 작성 버튼 확인",
"action": "verify_element",
"checks": [
"문서 작성 또는 신규 작성 버튼 존재"
],
"expected": "문서 작성 버튼 확인"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/approvals/drafts",
"description": "기안함 목록 조회"
},
{
"method": "GET",
"endpoint": "/api/v1/approvals/drafts/summary",
"description": "기안함 통계 카드"
}
],
"requiredVerifications": [
{
"id": 3,
"name": "검색/필터",
"steps": [6, 7, 8],
"criteria": "검색 기능 동작"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "기안함 목록, 검색 기능, 문서 작성 버튼 존재"
}
],
"rollbackPlan": {
"note": "조회 전용 테스트로 데이터 변경 없음"
}
}

View File

@@ -0,0 +1,372 @@
{
"id": "employee-register",
"name": "직원 등록 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "신규 직원 정보를 입력하고 등록하는 E2E 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"url": "/ko/hr/employee-management",
"navigation": {
"targetUrl": "/hr/employee-management",
"urlPattern": "/hr/employee-management|/ko/hr/employee-management",
"menuHints": ["사원관리", "사원 관리", "인사관리"]
},
"menuNavigation": {
"level1": "인사관리",
"level2": "사원관리",
"expectedUrl": "/ko/hr/employee-management",
"searchWithinParent": true,
"closeOtherMenus": true
},
"menuNavigationEnhanced": {
"strategy": "scroll-and-search",
"level1": {
"text": "인사관리",
"scrollContainer": ".sidebar-scroll, [class*='sidebar'], nav",
"maxScrollAttempts": 5,
"scrollStep": 200
},
"level2": {
"text": "사원관리",
"waitAfterLevel1": 500
},
"fallbackUrl": "/ko/hr/employee-management",
"timeout": 10000
},
"timeout": 60000,
"tags": ["hr", "employee", "crud"],
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": "step-0",
"name": "사이드바 메뉴 전체 펼치기",
"description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비",
"actions": [
{
"type": "evaluate",
"script": "document.querySelector('.sidebar-scroll, [class*=\"sidebar\"], nav')?.scrollTo({top: 0, behavior: 'instant'})",
"description": "사이드바 스크롤 최상단으로 이동"
},
{ "type": "wait", "duration": 300 },
{
"type": "evaluate",
"script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()",
"description": "모두 펼치기 버튼 클릭"
},
{
"type": "wait",
"duration": 2000,
"description": "메뉴 펼침 완료 대기"
}
]
},
{
"id": "step-1",
"name": "인사관리 메뉴 진입",
"description": "인사관리 > 직원관리 메뉴로 이동 (scrollAndFind 패턴)",
"menuNavigation": {
"pattern": "scrollAndFind",
"level1": {
"text": "인사관리",
"scrollContainer": ".sidebar-scroll, [class*='sidebar'], nav",
"scrollStep": 200,
"maxAttempts": 5
},
"level2": {
"text": "직원관리",
"waitAfterLevel1Click": 500
}
},
"expect": {
"url": "/hr/employee-management",
"visible": ["사원관리", "사원 등록"]
}
},
{
"id": "step-2",
"name": "사원 등록 페이지 이동",
"click": "사원 등록",
"expect": {
"url": "/hr/employee-management?mode=new",
"visible": ["사원 등록", "사원 정보"]
}
},
{
"id": "step-3",
"name": "사원 정보 입력",
"description": "기본 사원 정보 입력",
"form": {
"fields": [
{ "name": "이름 *", "type": "text", "value": "홍길동" },
{ "name": "주민등록번호", "type": "text", "value": "900101-1234567" },
{ "name": "휴대폰", "type": "text", "value": "010-1234-5678" },
{ "name": "이메일 *", "type": "text", "value": "test.employee@codebridge-x.com" },
{ "name": "연봉", "type": "number", "value": "50000000" }
]
}
},
{
"id": "step-4",
"name": "급여계좌 정보 입력",
"form": {
"fields": [
{ "name": "은행명", "type": "text", "value": "신한은행" },
{ "name": "계좌번호", "type": "text", "value": "110-123-456789" },
{ "name": "예금주", "type": "text", "value": "홍길동" }
]
}
},
{
"id": "step-5",
"name": "사원 상세 정보 입력",
"form": {
"fields": [
{ "name": "사원코드", "type": "text", "value": "EMP2026001" },
{ "name": "남성", "type": "radio", "value": "true" },
{ "name": "상세주소를 입력해주세요", "type": "text", "value": "123번지 4층" }
]
}
},
{
"id": "step-6",
"name": "인사 정보 입력",
"form": {
"fields": [
{ "name": "입사일", "type": "date", "value": "2026-01-14" }
]
},
"actions": [
{ "type": "click_if_exists", "target": "고용형태 선택", "description": "고용형태 드롭다운 열기" },
{ "type": "click_if_exists", "target": "정규직", "description": "정규직 선택" },
{ "type": "click_if_exists", "target": "직급 선택", "description": "직급 드롭다운 열기" },
{ "type": "click_if_exists", "target": "사원", "description": "사원 직급 선택" }
]
},
{
"id": "step-7",
"name": "사용자 정보 입력",
"form": {
"fields": [
{ "name": "아이디 *", "type": "text", "value": "testuser2026" },
{ "name": "비밀번호 *", "type": "text", "value": "password123!" },
{ "name": "비밀번호 확인 *", "type": "text", "value": "password123!" }
]
}
},
{
"id": "step-8",
"name": "등록 완료",
"click": "등록",
"waitFor": "사원관리",
"expect": {
"url": "/hr/employee-management",
"text": ["홍길동"]
}
},
{
"id": "step-8-1",
"name": "검색 기간 설정 - 유효 기간",
"description": "등록된 사원의 입사일(2026-01-14)이 포함되는 기간으로 검색",
"actions": [
{
"type": "click_if_exists",
"target": "input[placeholder*='시작'], input[name*='startDate'], input[type='date']:first-of-type"
},
{
"type": "click_if_exists",
"target": "input[placeholder*='종료'], input[name*='endDate'], input[type='date']:last-of-type"
},
{
"type": "click_if_exists",
"target": "button:has-text('검색'), .search-btn, [type='submit']"
}
],
"expect": {
"tableContains": "홍길동",
"rowCount": ">= 1"
},
"onFail": {
"record": true,
"message": "검색 기간 2026-01-01 ~ 2026-01-31 내 입사일(2026-01-14) 사원이 검색되지 않음",
"severity": "HIGH",
"bugType": "검색 기간 필터링 오류"
}
},
{
"id": "step-8-2",
"name": "검색 기간 설정 - 범위 외 기간",
"description": "등록된 사원의 입사일이 포함되지 않는 기간으로 검색하여 검색되지 않음을 확인",
"actions": [
{
"type": "click_if_exists",
"target": "input[placeholder*='시작'], input[name*='startDate'], input[type='date']:first-of-type"
},
{
"type": "click_if_exists",
"target": "input[placeholder*='종료'], input[name*='endDate'], input[type='date']:last-of-type"
},
{
"type": "click_if_exists",
"target": "button:has-text('검색'), .search-btn, [type='submit']"
}
],
"expect": {
"tableNotContains": "홍길동",
"alternativeExpect": {
"emptyResult": true,
"message": "검색 결과 없음"
}
},
"verify": {
"searchPeriodValidation": {
"inputPeriod": "2025-01-01 ~ 2025-12-31",
"employeeJoinDate": "2026-01-14",
"expectedResult": "NOT_FOUND",
"actualResultCheck": true
}
},
"onFail": {
"record": true,
"message": "검색 기간 2025-01-01 ~ 2025-12-31 범위 외 입사일(2026-01-14) 사원이 검색됨 - 기간 필터 미작동",
"severity": "HIGH",
"bugType": "검색 기간 필터링 미작동"
}
},
{
"id": "step-8-3",
"name": "검색 기간 초기화 및 전체 조회",
"description": "검색 조건 초기화하여 등록된 사원이 다시 표시되는지 확인",
"actions": [
{
"type": "click_if_exists",
"target": "초기화",
"fallbackSelectors": ["button:has-text('초기화')", ".reset-btn", "button:has-text('Reset')"]
},
{
"type": "click_if_exists",
"target": "검색",
"fallbackSelectors": ["button:has-text('검색')", ".search-btn"]
}
],
"expect": {
"tableContains": "홍길동"
},
"onFail": {
"record": true,
"message": "검색 초기화 후 전체 조회에서 등록된 사원이 표시되지 않음",
"severity": "MEDIUM",
"bugType": "검색 초기화 오류"
}
},
{
"id": "step-9",
"name": "등록된 직원 상세 페이지 이동",
"description": "등록된 직원을 클릭하여 상세 페이지로 이동",
"actions": [
{
"type": "click_if_exists",
"target": "table tbody tr:has-text('홍길동')",
"description": "해당 행 클릭하여 상세 페이지 이동"
}
],
"expect": {
"url": "/hr/employee-management/{id}",
"visible": ["사원 상세", "수정", "삭제", "목록"]
}
},
{
"id": "step-10",
"name": "직원 수정 모드 전환",
"description": "수정 버튼 클릭하여 편집 모드로 전환",
"click": "수정",
"expect": {
"url": "/hr/employee-management/{id}?mode=edit",
"visible": ["사원 수정", "취소", "저장"]
}
},
{
"id": "step-11",
"name": "직원 정보 수정",
"description": "휴대폰 번호 변경",
"form": {
"fields": [
{ "name": "휴대폰", "type": "text", "value": "010-9999-8888", "clear": true }
]
}
},
{
"id": "step-12",
"name": "수정 저장",
"description": "수정된 직원 정보 저장",
"click": "저장",
"waitFor": "사원 상세",
"expect": {
"toast": ["수정", "완료", "성공", "저장"],
"url": "/hr/employee-management/{id}"
}
},
{
"id": "step-12-1",
"name": "⚠️ 필수 검증: 수정 데이터 반영 확인",
"note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!",
"description": "상세 페이지에서 수정된 휴대폰 번호 확인",
"verify": {
"fieldValue": {
"target": "휴대폰",
"expected": "010-9999-8888"
}
}
},
{
"id": "step-13",
"name": "직원 삭제",
"description": "삭제 버튼 클릭하여 직원 삭제",
"click": "삭제",
"expect": {
"confirmDialog": true,
"dialogText": ["삭제", "하시겠습니까"]
}
},
{
"id": "step-14",
"name": "삭제 확인",
"description": "삭제 확인 다이얼로그에서 확인 클릭",
"click": "확인",
"waitFor": "사원관리",
"expect": {
"toast": ["삭제", "완료", "성공"],
"url": "/hr/employee-management"
}
},
{
"id": "step-15",
"name": "⚠️ 필수 검증: 삭제 데이터 반영 확인",
"note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 삭제 확인 필수!",
"description": "목록에서 삭제된 직원이 없어졌는지 확인",
"verify": {
"tableNotContains": "홍길동"
}
}
],
"assertions": [
{
"type": "url",
"expected": "/hr/employee-management",
"message": "등록 후 직원 목록 페이지로 이동해야 함"
},
{
"type": "text",
"target": "body",
"expected": "홍길동",
"message": "등록된 직원이 목록에 표시되어야 함"
}
]
}

View File

@@ -0,0 +1,82 @@
{
"enabled": true,
"id": "free-board",
"name": "자유게시판 E2E 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "자유게시판 목록/검색/상세 기능 검증",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "게시판",
"level2": "자유게시판",
"expectedUrl": "/boards/free",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 게시판 > 자유게시판",
"action": "menu_navigate",
"level1": "게시판",
"level2": "자유게시판",
"expected": { "url_contains": "/boards/free" }
},
{
"id": 2,
"name": "목업 감지",
"action": "verify_not_mockup"
},
{
"id": 3,
"name": "자유게시판 페이지 확인",
"action": "verify_detail",
"checks": ["visible_text:게시판"]
},
{
"id": 4,
"name": "테이블 확인",
"action": "verify_table"
},
{
"id": 5,
"name": "검색 기능",
"action": "search",
"value": "테스트"
},
{
"id": 6,
"name": "검색 후 페이지 확인",
"action": "verify_detail",
"checks": ["visible_text:게시판"]
},
{
"id": 7,
"name": "첫 번째 행 클릭",
"action": "click_first_row"
},
{
"id": 8,
"name": "상세 페이지 확인",
"action": "verify_detail",
"checks": ["visible_text:게시"]
},
{
"id": 9,
"name": "모달 닫기",
"action": "close_modal_if_open"
},
{
"id": 10,
"name": "목록 복귀",
"action": "click_if_exists",
"target": "button:has-text('목록'), a:has-text('목록')"
}
]
}

View File

@@ -0,0 +1,83 @@
{
"id": "hr-attendance-admin",
"name": "근태관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "인사관리 > 근태관리 목록/상세 기능 검증",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "인사관리",
"level2": "근태관리",
"expectedUrl": "/hr/attendance-management",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 인사관리 > 근태관리",
"action": "menu_navigate",
"level1": "인사관리",
"level2": "근태관리",
"expected": { "url_contains": "/hr/attendance" }
},
{
"id": 2,
"name": "목업 감지",
"action": "verify_not_mockup"
},
{
"id": 3,
"name": "근태관리 페이지 확인",
"action": "verify_detail",
"checks": ["visible_text:근태"]
},
{
"id": 4,
"name": "UI 요소 확인",
"action": "verify_detail",
"checks": ["visible_text:관리"]
},
{
"id": 5,
"name": "필터/검색 시도",
"action": "click_if_exists",
"target": "input[type='search'], input[type='date'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input, [class*='filter'] input"
},
{
"id": 6,
"name": "대기",
"action": "wait",
"duration": 1000
},
{
"id": 7,
"name": "행 클릭 시도",
"action": "click_if_exists",
"target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']"
},
{
"id": 8,
"name": "상세 확인",
"action": "verify_detail",
"checks": ["visible_text:근태"]
},
{
"id": 9,
"name": "모달 닫기",
"action": "close_modal_if_open"
},
{
"id": 10,
"name": "최종 확인",
"action": "verify_detail",
"checks": ["visible_text:근태"]
}
]
}

View File

@@ -0,0 +1,211 @@
{
"id": "hr-attendance-status",
"name": "근태현황 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "인사관리 > 근태현황 메뉴의 출퇴근 현황 조회/필터/출퇴근 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "인사관리",
"level2": "근태현황",
"expectedUrl": "/hr/attendance",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 인사관리 > 근태현황",
"action": "menu_navigate",
"level1": "인사관리",
"level2": "근태현황",
"expected": {
"url_contains": "/hr/attendance",
"visible": ["근태현황", "출근", "퇴근"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"출퇴근 현황 표시",
"날짜 선택 가능",
"출근/퇴근 버튼 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "근태 현황 테이블 구조 확인",
"action": "verify_table",
"checks": [
"날짜 컬럼",
"출근시간 컬럼",
"퇴근시간 컬럼",
"근무시간 컬럼",
"상태 컬럼"
],
"expected": "근태 현황 테이블 표시"
},
{
"id": 4,
"name": "오늘 근태 상태 확인",
"action": "verify_elements",
"checks": [
"오늘 날짜 표시",
"현재 근무 상태 표시"
],
"expected": "오늘 근태 상태 표시"
},
{
"id": 5,
"name": "출근 버튼 확인",
"action": "verify_elements",
"checks": [
"출근 버튼 존재"
],
"expected": "출근 버튼 표시"
},
{
"id": 6,
"name": "퇴근 버튼 확인",
"action": "verify_elements",
"checks": [
"퇴근 버튼 존재"
],
"expected": "퇴근 버튼 표시"
},
{
"id": 7,
"phase": "FILTER",
"name": "[FILTER] 기간 필터 - 월 선택",
"action": "click_if_exists",
"target": "input[type='month'], select[name*='month'], [class*='month-picker']",
"expected": "월 선택 열림"
},
{
"id": 8,
"phase": "FILTER",
"name": "[FILTER] 조회 적용",
"action": "click_if_exists",
"target": "button:has-text('조회'), button:has-text('검색'), button:has-text('적용')",
"expected": "필터 적용됨"
},
{
"id": 9,
"phase": "FILTER",
"name": "[FILTER] 필터 결과 확인",
"action": "verify_detail",
"checks": [
"선택한 월의 근태 데이터 표시"
],
"expected": "필터 동작 확인"
},
{
"id": 10,
"name": "근무 시간 통계 확인",
"action": "verify_elements",
"checks": [
"총 근무시간 표시",
"정상 출근 일수 표시"
],
"expected": "근무 통계 표시"
},
{
"id": 11,
"name": "지각/조퇴/결근 통계 확인",
"action": "verify_elements",
"checks": [
"지각 횟수 표시",
"조퇴 횟수 표시",
"결근 횟수 표시"
],
"expected": "근태 이상 통계 표시"
},
{
"id": 12,
"phase": "READ",
"name": "[READ] 특정 일자 상세 보기",
"action": "click_if_exists",
"target": "table tbody tr:first-child",
"expected": {
"detail_view": true
}
},
{
"id": 13,
"name": "상세 근태 정보 확인",
"action": "verify_detail",
"checks": [
"출근 시간 표시",
"퇴근 시간 표시",
"근무 시간 표시"
],
"expected": "상세 근태 정보 표시"
},
{
"id": 14,
"name": "엑셀 다운로드 버튼 확인",
"action": "verify_elements",
"checks": [
"엑셀 다운로드 버튼 존재"
],
"expected": "엑셀 다운로드 기능 표시"
},
{
"id": 15,
"name": "인쇄 버튼 확인",
"action": "verify_elements",
"checks": [
"인쇄 버튼 존재"
],
"expected": "인쇄 기능 표시"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/hr/attendance",
"description": "근태 현황 조회"
},
{
"method": "POST",
"endpoint": "/api/v1/hr/attendance/check-in",
"description": "출근 체크"
},
{
"method": "POST",
"endpoint": "/api/v1/hr/attendance/check-out",
"description": "퇴근 체크"
},
{
"method": "GET",
"endpoint": "/api/v1/hr/attendance/summary",
"description": "근태 통계 조회"
}
],
"requiredVerifications": [
{
"id": 3,
"name": "검색/필터",
"steps": [7, 8, 9],
"criteria": "기간 필터 동작"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "근태 현황, 날짜 선택, 출퇴근 버튼 존재"
}
],
"rollbackPlan": {
"note": "조회 전용 페이지로 데이터 변경 없음 (출퇴근 체크는 별도 테스트)"
}
}

View File

@@ -0,0 +1,81 @@
{
"id": "hr-card",
"name": "카드관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "인사관리 > 카드관리 목록/검색/상세 기능 검증",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "인사관리",
"level2": "카드관리",
"expectedUrl": "/hr/card-management",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 인사관리 > 카드관리",
"action": "menu_navigate",
"level1": "인사관리",
"level2": "카드관리",
"expected": { "url_contains": "/hr/card" }
},
{
"id": 2,
"name": "목업 감지",
"action": "verify_not_mockup"
},
{
"id": 3,
"name": "카드관리 페이지 확인",
"action": "verify_detail",
"checks": ["visible_text:카드"]
},
{
"id": 4,
"name": "테이블 확인",
"action": "verify_table"
},
{
"id": 5,
"name": "검색 기능",
"action": "search",
"value": "테스트"
},
{
"id": 6,
"name": "검색 후 확인",
"action": "verify_detail",
"checks": ["visible_text:카드"]
},
{
"id": 7,
"name": "첫 번째 행 클릭",
"action": "click_first_row"
},
{
"id": 8,
"name": "상세 확인",
"action": "verify_detail",
"checks": ["visible_text:카드"]
},
{
"id": 9,
"name": "모달 닫기",
"action": "close_modal_if_open"
},
{
"id": 10,
"name": "목록 복귀",
"action": "click_if_exists",
"target": "button:has-text('목록'), a:has-text('목록')"
}
]
}

View File

@@ -0,0 +1,83 @@
{
"id": "hr-department",
"name": "부서관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "인사관리 > 부서관리 목록/상세 기능 검증",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "인사관리",
"level2": "부서관리",
"expectedUrl": "/hr/department-management",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 인사관리 > 부서관리",
"action": "menu_navigate",
"level1": "인사관리",
"level2": "부서관리",
"expected": { "url_contains": "/hr/department" }
},
{
"id": 2,
"name": "목업 감지",
"action": "verify_not_mockup"
},
{
"id": 3,
"name": "부서관리 페이지 확인",
"action": "verify_detail",
"checks": ["visible_text:부서"]
},
{
"id": 4,
"name": "UI 요소 확인",
"action": "verify_detail",
"checks": ["visible_text:관리"]
},
{
"id": 5,
"name": "검색 입력 시도",
"action": "click_if_exists",
"target": "input[type='search'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input"
},
{
"id": 6,
"name": "대기",
"action": "wait",
"duration": 1000
},
{
"id": 7,
"name": "행 클릭 시도",
"action": "click_if_exists",
"target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']"
},
{
"id": 8,
"name": "상세 확인",
"action": "verify_detail",
"checks": ["visible_text:부서"]
},
{
"id": 9,
"name": "모달 닫기",
"action": "close_modal_if_open"
},
{
"id": 10,
"name": "최종 확인",
"action": "verify_detail",
"checks": ["visible_text:부서"]
}
]
}

View File

@@ -0,0 +1,81 @@
{
"id": "hr-employee",
"name": "사원관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "인사관리 > 사원관리 목록/검색/상세 기능 검증",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "인사관리",
"level2": "사원관리",
"expectedUrl": "/hr/employee-management",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 인사관리 > 사원관리",
"action": "menu_navigate",
"level1": "인사관리",
"level2": "사원관리",
"expected": { "url_contains": "/hr/employee" }
},
{
"id": 2,
"name": "목업 감지",
"action": "verify_not_mockup"
},
{
"id": 3,
"name": "사원관리 페이지 확인",
"action": "verify_detail",
"checks": ["visible_text:사원"]
},
{
"id": 4,
"name": "테이블 확인",
"action": "verify_table"
},
{
"id": 5,
"name": "검색 기능",
"action": "search",
"value": "테스트"
},
{
"id": 6,
"name": "검색 후 확인",
"action": "verify_detail",
"checks": ["visible_text:사원"]
},
{
"id": 7,
"name": "첫 번째 행 클릭",
"action": "click_first_row"
},
{
"id": 8,
"name": "상세 확인",
"action": "verify_detail",
"checks": ["visible_text:사원"]
},
{
"id": 9,
"name": "모달 닫기",
"action": "close_modal_if_open"
},
{
"id": 10,
"name": "목록 복귀",
"action": "click_if_exists",
"target": "button:has-text('목록'), a:has-text('목록')"
}
]
}

View File

@@ -0,0 +1,81 @@
{
"id": "hr-salary",
"name": "급여관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "인사관리 > 급여관리 목록/검색/상세 기능 검증",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "인사관리",
"level2": "급여관리",
"expectedUrl": "/hr/salary-management",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 인사관리 > 급여관리",
"action": "menu_navigate",
"level1": "인사관리",
"level2": "급여관리",
"expected": { "url_contains": "/hr/salary" }
},
{
"id": 2,
"name": "목업 감지",
"action": "verify_not_mockup"
},
{
"id": 3,
"name": "급여관리 페이지 확인",
"action": "verify_detail",
"checks": ["visible_text:급여"]
},
{
"id": 4,
"name": "테이블 확인",
"action": "verify_table"
},
{
"id": 5,
"name": "검색 기능",
"action": "search",
"value": "테스트"
},
{
"id": 6,
"name": "검색 후 확인",
"action": "verify_detail",
"checks": ["visible_text:급여"]
},
{
"id": 7,
"name": "첫 번째 행 클릭",
"action": "click_first_row"
},
{
"id": 8,
"name": "상세 확인",
"action": "verify_detail",
"checks": ["visible_text:급여"]
},
{
"id": 9,
"name": "모달 닫기",
"action": "close_modal_if_open"
},
{
"id": 10,
"name": "목록 복귀",
"action": "click_if_exists",
"target": "button:has-text('목록'), a:has-text('목록')"
}
]
}

View File

@@ -0,0 +1,352 @@
{
"id": "hr-vacation",
"name": "휴가관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
},
"description": "인사관리 > 휴가관리 메뉴의 휴가 신청/조회/수정/취소 전체 CRUD 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "인사관리",
"level2": "휴가관리",
"expectedUrl": "/hr/vacation-management",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"testData": {
"create": {
"vacationType": "연차",
"startDate": "2026-02-10",
"endDate": "2026-02-10",
"reason": "E2E 자동화 테스트 휴가 신청"
},
"update": {
"reason": "E2E 수정된 휴가 사유"
}
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 인사관리 > 휴가관리",
"action": "menu_navigate",
"level1": "인사관리",
"level2": "휴가관리",
"expected": {
"url_contains": "/hr/vacation",
"visible": [
"휴가관리",
"휴가"
]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"휴가 목록 표시",
"휴가 신청 버튼 존재",
"연차 현황 표시"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "휴가 현황 카드 확인",
"action": "verify_elements",
"checks": [
"잔여 연차 표시",
"사용 연차 표시",
"총 연차 표시"
],
"expected": "휴가 현황 카드 정상 표시"
},
{
"id": 4,
"name": "휴가 테이블 구조 확인",
"action": "verify_table",
"checks": [
"휴가 유형 컬럼",
"시작일 컬럼",
"종료일 컬럼",
"상태 컬럼",
"신청일 컬럼"
],
"expected": "휴가 테이블 컬럼 정상 표시"
},
{
"id": 5,
"phase": "CREATE",
"name": "[CREATE] 휴가 신청 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('신청'), button:has-text('휴가 신청'), button:has-text('추가')",
"expected": {
"modal": true,
"modalTitle": "휴가 신청"
}
},
{
"id": 6,
"phase": "CREATE",
"name": "[CREATE] 휴가 정보 입력",
"action": "click_if_exists",
"fields": [
{
"name": "휴가 유형",
"type": "select",
"value": "연차"
},
{
"name": "시작일",
"type": "date",
"value": "2026-02-10"
},
{
"name": "종료일",
"type": "date",
"value": "2026-02-10"
},
{
"name": "사유",
"type": "textarea",
"value": "E2E 자동화 테스트 휴가 신청_{timestamp}"
}
],
"note": "타임스탬프로 고유성 보장",
"target": "form, [role='dialog'], .modal"
},
{
"id": 7,
"phase": "CREATE",
"name": "[CREATE] 필수 검증 #2: 신청 저장",
"action": "click_if_exists",
"target": "button:has-text('신청'), button:has-text('저장'), button:has-text('확인')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "POST /api/v1/vacations",
"toast": "신청|등록|완료|성공"
},
"expected": "휴가 신청 완료"
},
{
"id": "7-modal-close",
"phase": "CREATE",
"name": "[CREATE] 모달 닫기 확인",
"action": "close_modal_if_open",
"expected": "모달 닫힘"
},
{
"id": 8,
"phase": "CREATE",
"name": "[CREATE] 신청 결과 확인",
"action": "verify_detail",
"search": "E2E 자동화 테스트 휴가",
"expected": {
"row_exists": true,
"contains": [
"연차",
"대기",
"2026-02-10"
]
}
},
{
"id": 9,
"phase": "READ",
"name": "[READ] 휴가 상세 페이지 진입",
"action": "click_if_exists",
"target": "table tbody tr:has-text('E2E')",
"expected": {
"url_contains": "/hr/vacation",
"visible": [
"휴가 상세",
"수정",
"취소"
]
}
},
{
"id": 10,
"phase": "READ",
"name": "[READ] 상세 정보 확인",
"action": "verify_detail",
"checks": [
"휴가 유형: 연차",
"시작일: 2026-02-10",
"종료일: 2026-02-10",
"상태: 대기",
"사유: E2E 자동화 테스트"
],
"expected": "입력한 데이터와 일치"
},
{
"id": 11,
"phase": "UPDATE",
"name": "[UPDATE] 수정 모드 진입",
"action": "click_if_exists",
"target": "button:has-text('수정')",
"expected": {
"modal_or_edit_mode": true,
"fields_editable": true
}
},
{
"id": 12,
"phase": "UPDATE",
"name": "[UPDATE] 사유 수정",
"action": "click_if_exists",
"target": "textarea[name*='reason'], input[placeholder*='사유']",
"value": "E2E 수정된 휴가 사유_{timestamp}",
"clear": true
},
{
"id": 13,
"phase": "UPDATE",
"name": "[UPDATE] 필수 검증 #2: 수정 저장",
"action": "click_if_exists",
"target": "button:has-text('저장'), button:has-text('수정')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "PUT /api/v1/vacations/",
"toast": "수정|완료|성공"
},
"expected": "수정 완료"
},
{
"id": 14,
"phase": "UPDATE",
"name": "[UPDATE] 수정 결과 확인",
"action": "verify_detail",
"checks": [
"사유: E2E 수정된 휴가"
],
"expected": "수정된 데이터 반영"
},
{
"id": 15,
"phase": "DELETE",
"name": "[DELETE] 취소 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('취소'), button:has-text('신청 취소')",
"expected": {
"confirm_dialog": true,
"dialog_message": "취소|정말"
}
},
{
"id": 16,
"phase": "DELETE",
"name": "[DELETE] 필수 검증 #6: 취소 확인",
"action": "click_if_exists",
"target": "button:has-text('확인'), button:has-text('예')",
"verify": {
"api_call": "DELETE /api/v1/vacations/",
"toast": "취소|삭제|완료|성공",
"redirect": "/hr/vacation"
},
"expected": "휴가 취소 완료 및 목록 복귀"
},
{
"id": 17,
"phase": "DELETE",
"name": "[DELETE] 취소 결과 확인",
"action": "verify_detail",
"search": "E2E 수정된 휴가",
"expected": {
"row_exists": false,
"message": "테스트 휴가 신청이 목록에서 제거됨"
}
},
{
"id": 18,
"phase": "VERIFY",
"name": "[VERIFY] 연차 잔여일 확인",
"action": "verify_elements",
"checks": [
"잔여 연차가 원래 값으로 복원"
],
"expected": "휴가 취소 후 연차 복원 확인"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/vacations",
"description": "휴가 목록 조회"
},
{
"method": "GET",
"endpoint": "/api/v1/vacations/summary",
"description": "휴가 현황 조회"
},
{
"method": "POST",
"endpoint": "/api/v1/vacations",
"description": "휴가 신청"
},
{
"method": "GET",
"endpoint": "/api/v1/vacations/{id}",
"description": "휴가 상세 조회"
},
{
"method": "PUT",
"endpoint": "/api/v1/vacations/{id}",
"description": "휴가 수정"
},
{
"method": "DELETE",
"endpoint": "/api/v1/vacations/{id}",
"description": "휴가 취소"
}
],
"requiredVerifications": [
{
"id": 2,
"name": "등록/저장 버튼",
"steps": [
7,
13
],
"criteria": "API 호출 + 성공 토스트 + 데이터 반영"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [
2
],
"criteria": "휴가 목록, 신청 버튼, 현황 카드 존재"
},
{
"id": 6,
"name": "삭제 기능",
"steps": [
15,
16,
17
],
"criteria": "DELETE API + 목록에서 제거 + 연차 복원"
}
],
"rollbackPlan": {
"onCreateFail": "모달 닫기 → 다음 테스트 영향 없음",
"onUpdateFail": "테스트 휴가 수동 취소 필요",
"onDeleteFail": "테스트 휴가 수동 취소 필요",
"cleanupRequired": "E2E_ 접두사 휴가 신청은 테스트 데이터"
}
}

View File

@@ -0,0 +1,250 @@
{
"id": "inventory-status",
"name": "재고현황 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "자재관리 > 재고현황 페이지의 재고 조회 및 엑셀 다운로드 기능을 테스트하는 E2E 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"url": "/material/stock-status",
"navigation": {
"targetUrl": "/material/stock-status",
"urlPattern": "/material/stock-status|/ko/material/stock-status",
"menuHints": ["재고현황", "재고 현황", "자재관리"]
},
"menuNavigation": {
"level1": "자재관리",
"level2": "재고현황",
"expectedUrl": "/material/stock-status",
"searchWithinParent": true,
"closeOtherMenus": true
},
"menuNavigationEnhanced": {
"strategy": "scroll-and-search",
"sidebar": {
"scrollContainer": ".sidebar-scroll",
"scrollStep": 200,
"maxScrollAttempts": 5,
"waitAfterScroll": 300
},
"level1": {
"text": "자재관리",
"expandable": true,
"waitAfterClick": 500
},
"level2": {
"text": "재고현황",
"waitAfterClick": 300
},
"fallbackUrl": "/material/stock-status",
"expectedUrl": "/material/stock-status"
},
"timeout": 90000,
"tags": ["material", "inventory", "read-only"],
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": "step-0",
"name": "사이드바 메뉴 전체 펼치기",
"description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비",
"actions": [
{
"type": "evaluate",
"script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})"
},
{ "type": "wait", "duration": 300 },
{
"type": "evaluate",
"script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()"
},
{ "type": "wait", "duration": 2000 }
]
},
{
"id": "step-1",
"name": "자재관리 메뉴 진입",
"description": "자재관리 > 재고현황 메뉴로 이동",
"actions": [
{
"type": "scrollAndFind",
"container": ".sidebar-scroll",
"target": "자재관리",
"scrollStep": 200,
"maxAttempts": 5
},
{ "type": "click_if_exists", "target": "자재관리" },
{ "type": "wait", "duration": 500 },
{ "type": "click_if_exists", "target": "재고현황" }
],
"expect": {
"url": "/material/stock-status",
"visible": ["재고 목록", "엑셀 다운로드"]
},
"fallback": {
"type": "navigate",
"url": "/material/stock-status"
}
},
{
"id": "step-2",
"name": "페이지 구조 확인",
"description": "통계 카드와 테이블 구조 확인",
"verify": {
"visible": ["전체 품목", "정상 재고", "재고 부족", "재고 없음"],
"tableColumns": ["번호", "품목코드", "품목명", "품목유형", "단위", "재고량", "안전재고", "LOT", "상태", "위치"]
}
},
{
"id": "step-3",
"name": "필수 검증 #3: 품목유형 탭 필터 - 원자재",
"description": "원자재 탭 클릭하여 필터링 확인",
"actions": [
{ "type": "click_if_exists", "target": "원자재", "role": "tab" },
{ "type": "wait", "duration": 500 }
],
"expect": {
"tabActive": "원자재",
"dataFiltered": true
}
},
{
"id": "step-4",
"name": "필수 검증 #3: 품목유형 탭 필터 - 부자재",
"description": "부자재 탭 클릭하여 필터링 확인",
"actions": [
{ "type": "click_if_exists", "target": "부자재", "role": "tab" },
{ "type": "wait", "duration": 500 }
],
"expect": {
"tabActive": "부자재",
"dataFiltered": true
}
},
{
"id": "step-5",
"name": "필수 검증 #3: 품목유형 탭 필터 - 소모품",
"description": "소모품 탭 클릭하여 필터링 확인",
"actions": [
{ "type": "click_if_exists", "target": "소모품", "role": "tab" },
{ "type": "wait", "duration": 500 }
],
"expect": {
"tabActive": "소모품",
"dataFiltered": true
}
},
{
"id": "step-6",
"name": "전체 탭으로 복귀",
"description": "전체 탭 클릭하여 모든 재고 표시",
"actions": [
{ "type": "click_if_exists", "target": "전체", "role": "tab" },
{ "type": "wait", "duration": 300 }
],
"expect": {
"tabActive": "전체",
"allDataShown": true
}
},
{
"id": "step-7",
"name": "필수 검증 #1: 엑셀 다운로드",
"description": "엑셀 다운로드 버튼 동작 확인",
"actions": [
{ "type": "click_if_exists", "target": "엑셀 다운로드" },
{ "type": "wait", "duration": 1000 }
],
"expect": {
"downloadTriggered": true,
"noErrorPage": true
},
"verify": {
"apiCall": "GET /api/material/stock-status/export"
}
},
{
"id": "step-8",
"name": "재고 상세 열기",
"description": "재고 항목 클릭하여 상세 보기",
"actions": [
{
"type": "evaluate",
"script": "document.querySelector('tbody tr')?.click()"
}
],
"expect": {
"pageOrModal": "재고 상세",
"visible": ["품목코드", "품목명", "재고량", "LOT"]
}
},
{
"id": "step-9",
"name": "상세 닫기",
"description": "ESC 키로 상세 닫기 또는 뒤로가기",
"actions": [
{ "type": "press", "key": "Escape" },
{ "type": "wait", "duration": 300 }
]
},
{
"id": "step-10",
"name": "페이지네이션 확인",
"description": "페이지네이션 동작 확인",
"actions": [
{ "type": "click_if_exists", "target": "다음" },
{ "type": "wait", "duration": 500 }
],
"expect": {
"pageChanged": true
}
}
],
"assertions": [
{
"type": "url",
"expected": "/material/stock-status",
"message": "재고현황 페이지에 머물러야 함"
},
{
"type": "elementExists",
"selector": "button:has-text('엑셀 다운로드')",
"message": "엑셀 다운로드 버튼이 표시되어야 함"
}
],
"mandatoryVerifications": {
"description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목",
"items": [
{
"id": 1,
"name": "파일 다운로드",
"trigger": "엑셀 다운로드 버튼",
"verification": "Network API + 실제 다운로드 확인",
"failCondition": "Console LOG만으로 PASS 금지"
},
{
"id": 3,
"name": "검색/필터",
"trigger": "품목유형 탭 필터",
"verification": "데이터 변화 확인",
"failCondition": "필터 적용 후 데이터 무변화"
}
]
},
"notes": {
"testScope": "재고현황 조회 및 필터링, 엑셀 다운로드 테스트",
"pageType": "조회 전용 (입고관리에서 재고 증가, 출하관리에서 재고 감소)",
"statsCards": ["전체 품목", "정상 재고", "재고 부족", "재고 없음"],
"typeTabs": ["전체", "원자재", "부자재", "소모품"],
"tableColumns": ["번호", "품목코드", "품목명", "품목유형", "단위", "재고량", "안전재고", "LOT", "상태", "위치"],
"prerequisites": "로그인된 사용자"
}
}

View File

@@ -0,0 +1,150 @@
{
"id": "item-management",
"name": "품목관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "생산관리 > 품목관리 메뉴의 품목 목록 조회 및 UI 검증 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "생산관리",
"level2": "품목관리",
"expectedUrl": "/production/screen-production",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 생산관리 > 품목관리",
"action": "menu_navigate",
"level1": "생산관리",
"level2": "품목관리",
"expected": {
"url_contains": "/production",
"visible": ["품목관리", "품목"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"품목 목록 또는 통계 카드 표시",
"품목 등록 버튼 존재",
"검색 기능 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "품목 UI 구조 확인",
"action": "verify_elements",
"checks": [
"통계 카드 영역",
"품목 목록 테이블",
"검색 입력 필드",
"탭/필터 버튼"
],
"expected": "품목관리 UI 정상 표시"
},
{
"id": 4,
"name": "테이블 구조 확인",
"action": "verify_table",
"checks": [
"품목코드 컬럼",
"품목명 컬럼",
"규격 컬럼"
],
"expected": "품목 테이블 표시"
},
{
"id": 5,
"phase": "READ",
"name": "[READ] 품목 목록 데이터 확인",
"action": "verify_detail",
"checks": [
"품목 목록 데이터 표시됨"
],
"expected": "품목 목록 정상"
},
{
"id": 6,
"phase": "READ",
"name": "[READ] 첫 번째 행 클릭",
"action": "click_if_exists",
"target": "table tbody tr:first-child, button:has-text('상세')"
},
{
"id": 7,
"phase": "READ",
"name": "[READ] 품목 상세 정보 확인",
"action": "verify_detail",
"checks": [
"품목 상세 정보 표시"
],
"expected": "품목 상세 정보 확인"
},
{
"id": 8,
"name": "상세 모달/페이지 닫기",
"action": "click_if_exists",
"target": "button:has-text('닫기'), button:has-text('Close'), button:has-text('목록'), [class*='close']"
},
{
"id": 9,
"name": "모달 닫기 확인",
"action": "close_modal_if_open",
"expected": "모달 닫힘"
},
{
"id": 10,
"name": "탭/필터 기능 확인",
"action": "click_if_exists",
"target": "button:has-text('전체'), [class*='tab']:has-text('전체')"
},
{
"id": 11,
"name": "등록 버튼 존재 확인",
"action": "verify_elements",
"checks": [
"품목 등록 버튼 존재"
],
"expected": "등록 버튼 표시"
},
{
"id": 12,
"name": "품목관리 페이지 최종 확인",
"action": "verify_elements",
"checks": [
"품목 목록 구조 정상",
"통계 카드 또는 요약 정보 표시"
],
"expected": "품목관리 페이지 정상"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/items",
"description": "품목 목록 조회"
}
],
"requiredVerifications": [
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "품목 목록, 등록 버튼, 검색 기능 존재"
}
],
"rollbackPlan": {
"note": "READ-only 패턴으로 안정성 우선, 비표준 포맷 제거"
}
}

View File

@@ -0,0 +1,83 @@
{
"id": "item-master",
"name": "품목기준관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "품목관리 > 품목기준관리 목록/상세 기능 검증",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "품목관리",
"level2": "품목기준관리",
"expectedUrl": "/master-data/item-master-data-management",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 품목관리 > 품목기준관리",
"action": "menu_navigate",
"level1": "품목관리",
"level2": "품목기준관리",
"expected": { "url_contains": "/master-data" }
},
{
"id": 2,
"name": "목업 감지",
"action": "verify_not_mockup"
},
{
"id": 3,
"name": "품목기준관리 페이지 확인",
"action": "verify_detail",
"checks": ["visible_text:품목"]
},
{
"id": 4,
"name": "UI 요소 확인",
"action": "verify_detail",
"checks": ["visible_text:관리"]
},
{
"id": 5,
"name": "검색 입력 시도",
"action": "click_if_exists",
"target": "input[type='search'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input"
},
{
"id": 6,
"name": "대기",
"action": "wait",
"duration": 1000
},
{
"id": 7,
"name": "행 클릭 시도",
"action": "click_if_exists",
"target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']"
},
{
"id": 8,
"name": "상세 확인",
"action": "verify_detail",
"checks": ["visible_text:품목"]
},
{
"id": 9,
"name": "모달 닫기",
"action": "close_modal_if_open"
},
{
"id": 10,
"name": "최종 확인",
"action": "verify_detail",
"checks": ["visible_text:품목"]
}
]
}

View File

@@ -0,0 +1,321 @@
{
"id": "login-test",
"name": "로그인 테스트 (끝판왕)",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
},
"description": "로그인 페이지 UI 검증, 로그인 실패/성공, 대시보드 진입, 로그아웃까지 전체 인증 플로우 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"timeout": 30000,
"tags": [
"auth",
"login",
"critical"
],
"auth": {
"username": "TestUser5",
"password": "password123!",
"wrongPassword": "wrongpassword"
},
"selectors": {
"usernameInput": "#userId",
"passwordInput": "#password",
"rememberMeCheckbox": "input[type='checkbox']",
"forgotPasswordButton": "button:has-text('비밀번호를 잊으셨나요?')",
"loginButton": "button[type='submit']",
"passwordToggle": "button:has(.lucide-eye)",
"userProfileButton": "button:has-text('홍킬동')",
"logoutButton": "button:has-text('로그아웃')"
},
"steps": [
{
"id": 1,
"name": "로그인 페이지 접속",
"action": "navigate",
"target": "/ko/login",
"expected": {
"url": "/ko/login",
"visible": [
"로그인",
"환영합니다",
"SAM MES",
"아이디",
"비밀번호"
]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"아이디 입력 필드 존재 및 입력 가능",
"비밀번호 입력 필드 존재 및 입력 가능",
"로그인 버튼 존재 및 클릭 가능"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "UI 요소 검증 - 입력 필드",
"action": "verify_elements",
"checks": [
"아이디 입력 필드 placeholder: '아이디를 입력하세요'",
"비밀번호 입력 필드 placeholder: '비밀번호를 입력하세요'",
"비밀번호 표시/숨김 토글 버튼 존재"
],
"expected": "모든 입력 필드 정상"
},
{
"id": 4,
"name": "UI 요소 검증 - 옵션",
"action": "verify_elements",
"checks": [
"로그인 상태 유지 체크박스 존재",
"비밀번호 찾기 링크 존재",
"로그인 버튼 존재"
],
"expected": "모든 옵션 요소 정상"
},
{
"id": 5,
"name": "비밀번호 표시/숨김 토글 테스트",
"action": "click_if_exists",
"target": "passwordToggle",
"expected": "비밀번호 필드 type이 'text'로 변경 (표시 모드)"
},
{
"id": 6,
"name": "비밀번호 숨김 복원",
"action": "click_if_exists",
"target": "passwordToggle",
"expected": "비밀번호 필드 type이 'password'로 복원 (숨김 모드)"
},
{
"id": 7,
"name": "로그인 실패 테스트 - 빈 필드",
"action": "click_if_exists",
"target": "loginButton",
"expected": "유효성 검사 에러 또는 로그인 실패 메시지"
},
{
"id": 8,
"name": "아이디 입력",
"action": "click_if_exists",
"target": "usernameInput",
"value": "TestUser5",
"expected": "아이디 필드에 값 입력됨"
},
{
"id": 9,
"name": "로그인 실패 테스트 - 잘못된 비밀번호",
"action": "click_if_exists",
"target": "passwordInput",
"value": "wrongpassword",
"expected": "비밀번호 필드에 값 입력됨"
},
{
"id": 10,
"name": "잘못된 비밀번호로 로그인 시도",
"action": "click_if_exists",
"target": "loginButton",
"expected": "로그인 실패 에러 메시지 표시",
"verify": {
"type": "error_message",
"contains": [
"실패",
"오류",
"일치하지 않",
"incorrect",
"failed"
]
}
},
{
"id": 11,
"name": "비밀번호 필드 초기화",
"action": "click_if_exists",
"target": "passwordInput",
"expected": "비밀번호 필드 비워짐"
},
{
"id": 12,
"name": "올바른 비밀번호 입력",
"action": "click_if_exists",
"target": "passwordInput",
"value": "password123!",
"expected": "올바른 비밀번호 입력됨"
},
{
"id": 13,
"name": "필수 검증 #2: 로그인 버튼 클릭",
"action": "click_if_exists",
"target": "loginButton",
"verify": {
"url_should_change": true,
"no_error_page": true,
"api_call": "POST /api/v1/auth/login"
},
"expected": "로그인 성공 및 대시보드로 이동"
},
{
"id": 14,
"name": "대시보드 페이지 확인",
"action": "wait_for_navigation",
"expected": {
"url_contains": "/dashboard",
"visible": [
"대시보드",
"홍킬동"
]
}
},
{
"id": 15,
"name": "사용자 정보 표시 확인",
"action": "verify_elements",
"checks": [
"사용자명 '홍킬동' 표시",
"메뉴 영역 표시",
"SAM 로고 표시"
],
"expected": "사용자 정보 및 메인 레이아웃 정상"
},
{
"id": 16,
"name": "세션 유지 확인 - 페이지 새로고침",
"action": "reload",
"expected": "새로고침 후에도 로그인 상태 유지"
},
{
"id": 17,
"name": "새로고침 후 대시보드 유지 확인",
"action": "verify_url",
"expected": {
"url_contains": "/dashboard",
"visible": [
"대시보드",
"홍킬동"
]
}
},
{
"id": 18,
"name": "사용자 프로필 메뉴 열기",
"action": "click_if_exists",
"target": "userProfileButton",
"expected": "사용자 메뉴 드롭다운 열림"
},
{
"id": 19,
"name": "로그아웃 버튼 클릭",
"action": "click_if_exists",
"target": "logoutButton",
"expected": "로그아웃 처리 및 로그인 페이지로 이동"
},
{
"id": 20,
"name": "로그아웃 후 로그인 페이지 확인",
"action": "verify_element",
"expected": {
"url_contains": "/login",
"visible": [
"로그인",
"아이디",
"비밀번호"
]
}
},
{
"id": 21,
"name": "로그아웃 후 보호된 페이지 접근 시도",
"action": "navigate",
"target": "/ko/dashboard",
"expected": "로그인 페이지로 리다이렉트"
},
{
"id": 22,
"name": "재로그인 테스트",
"actions": [
{
"type": "click_if_exists",
"target": "usernameInput",
"value": "TestUser5"
},
{
"type": "click_if_exists",
"target": "passwordInput",
"value": "password123!"
},
{
"type": "click_if_exists",
"target": "loginButton"
}
],
"expected": "재로그인 성공"
},
{
"id": 23,
"name": "최종 확인 - 대시보드 진입",
"action": "verify_url",
"expected": {
"url_contains": "/dashboard",
"visible": [
"대시보드",
"홍킬동"
]
}
}
],
"requiredVerifications": [
{
"id": 2,
"name": "등록/저장 버튼 (로그인)",
"steps": [
13
],
"criteria": "로그인 버튼 클릭 → API 호출 → 대시보드 이동"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [
2
],
"criteria": "입력 필드 동작, 버튼 클릭 가능"
}
],
"expectedAPIs": [
{
"method": "POST",
"endpoint": "/api/v1/auth/login",
"description": "로그인 인증"
},
{
"method": "GET",
"endpoint": "/api/v1/auth/me",
"description": "현재 사용자 정보 조회"
},
{
"method": "POST",
"endpoint": "/api/v1/auth/logout",
"description": "로그아웃"
}
],
"testData": {
"validUser": {
"username": "TestUser5",
"password": "password123!",
"displayName": "홍킬동"
},
"invalidPassword": "wrongpassword"
}
}

View File

@@ -0,0 +1,301 @@
{
"id": "material-receiving",
"name": "입고관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "자재관리 > 입고관리 메뉴의 입고 조회/등록/수정/삭제 전체 CRUD 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "자재관리",
"level2": "입고관리",
"expectedUrl": "/material/receiving-management",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"testData": {
"create": {
"itemName": "E2E_TEST_입고품목",
"quantity": "100",
"vendor": "테스트거래처",
"memo": "E2E 자동화 테스트 입고"
},
"update": {
"quantity": "150",
"memo": "E2E 수정된 입고 메모"
}
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 자재관리 > 입고관리",
"action": "menu_navigate",
"level1": "자재관리",
"level2": "입고관리",
"expected": {
"url_contains": "/material/receiving",
"visible": ["입고관리", "입고"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"입고 목록 표시",
"입고 등록 버튼 존재",
"검색/필터 기능 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "입고 테이블 구조 확인",
"action": "verify_table",
"checks": [
"입고일 컬럼",
"품목 컬럼",
"수량 컬럼",
"거래처 컬럼",
"상태 컬럼"
],
"expected": "입고 테이블 컬럼 정상 표시"
},
{
"id": 4,
"name": "검색 기능 테스트",
"action": "click_if_exists",
"target": "input[placeholder*='검색']",
"value": "테스트",
"expected": {
"data_filtered": true
}
},
{
"id": 5,
"phase": "CREATE",
"name": "[CREATE] 입고 등록 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('등록'), button:has-text('입고 등록'), button:has-text('추가')",
"expected": {
"modal": true,
"modalTitle": "입고 등록"
}
},
{
"id": 6,
"phase": "CREATE",
"name": "[CREATE] 입고 정보 입력",
"action": "fill_form",
"fields": [
{"name": "입고일", "type": "date", "value": "2026-02-03"},
{"name": "품목", "type": "select", "value": "E2E_TEST_입고품목"},
{"name": "수량", "type": "number", "value": "100"},
{"name": "거래처", "type": "select", "value": "테스트거래처"},
{"name": "메모", "type": "text", "value": "E2E 자동화 테스트 입고_{timestamp}"}
],
"note": "타임스탬프로 고유성 보장"
},
{
"id": 7,
"phase": "CREATE",
"name": "[CREATE] 필수 검증 #2: 등록 저장",
"action": "click_if_exists",
"target": "button:has-text('저장'), button:has-text('등록')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "POST /api/v1/receivings",
"toast": "등록|완료|성공"
},
"expected": "입고 등록 완료"
},
{
"id": "7-modal-close",
"phase": "CREATE",
"name": "[CREATE] 모달 닫기 확인",
"action": "close_modal_if_open",
"expected": "모달 닫힘"
},
{
"id": 8,
"phase": "CREATE",
"name": "[CREATE] 등록 결과 확인",
"action": "verify_detail",
"search": "E2E 자동화 테스트 입고",
"expected": {
"row_exists": true,
"contains": ["E2E", "100"]
}
},
{
"id": 9,
"phase": "READ",
"name": "[READ] 입고 상세 페이지 진입",
"action": "click_if_exists",
"target": "table tbody tr:has-text('E2E')",
"expected": {
"url_contains": "/material/receiving",
"visible": ["입고 상세", "수정", "삭제"]
}
},
{
"id": 10,
"phase": "READ",
"name": "[READ] 상세 정보 확인",
"action": "verify_detail",
"checks": [
"수량: 100",
"메모: E2E 자동화 테스트"
],
"expected": "입력한 데이터와 일치"
},
{
"id": 11,
"phase": "UPDATE",
"name": "[UPDATE] 수정 모드 진입",
"action": "click_if_exists",
"target": "button:has-text('수정')",
"expected": {
"url_contains": "mode=edit",
"fields_editable": true
}
},
{
"id": 12,
"phase": "UPDATE",
"name": "[UPDATE] 수량 수정",
"action": "click_if_exists",
"target": "input[name*='quantity'], input[placeholder*='수량']",
"value": "150",
"clear": true
},
{
"id": 13,
"phase": "UPDATE",
"name": "[UPDATE] 메모 수정",
"action": "click_if_exists",
"target": "textarea[name*='memo'], input[placeholder*='메모']",
"value": "E2E 수정된 입고 메모_{timestamp}",
"clear": true
},
{
"id": 14,
"phase": "UPDATE",
"name": "[UPDATE] 필수 검증 #2: 수정 저장",
"action": "click_if_exists",
"target": "button:has-text('저장')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "PUT /api/v1/receivings/",
"toast": "수정|완료|성공"
},
"expected": "수정 완료"
},
{
"id": 15,
"phase": "UPDATE",
"name": "[UPDATE] 수정 결과 확인",
"action": "verify_detail",
"checks": [
"수량: 150",
"메모: E2E 수정된 입고"
],
"expected": "수정된 데이터 반영"
},
{
"id": 16,
"phase": "DELETE",
"name": "[DELETE] 삭제 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('삭제')",
"expected": {
"confirm_dialog": true,
"dialog_message": "삭제|정말"
}
},
{
"id": 17,
"phase": "DELETE",
"name": "[DELETE] 필수 검증 #6: 삭제 확인",
"action": "click_if_exists",
"target": "button:has-text('확인'), button:has-text('삭제')",
"verify": {
"api_call": "DELETE /api/v1/receivings/",
"toast": "삭제|완료|성공",
"redirect": "/material/receiving"
},
"expected": "삭제 완료 및 목록 복귀"
},
{
"id": 18,
"phase": "DELETE",
"name": "[DELETE] 삭제 결과 확인",
"action": "verify_detail",
"search": "E2E 수정된 입고",
"expected": {
"row_exists": false,
"message": "테스트 입고가 목록에서 제거됨"
}
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/receivings",
"description": "입고 목록 조회"
},
{
"method": "POST",
"endpoint": "/api/v1/receivings",
"description": "입고 등록"
},
{
"method": "GET",
"endpoint": "/api/v1/receivings/{id}",
"description": "입고 상세 조회"
},
{
"method": "PUT",
"endpoint": "/api/v1/receivings/{id}",
"description": "입고 수정"
},
{
"method": "DELETE",
"endpoint": "/api/v1/receivings/{id}",
"description": "입고 삭제"
}
],
"requiredVerifications": [
{
"id": 2,
"name": "등록/저장 버튼",
"steps": [7, 14],
"criteria": "API 호출 + 성공 토스트 + 데이터 반영"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "입고 목록, 등록 버튼, 필터 존재"
},
{
"id": 6,
"name": "삭제 기능",
"steps": [16, 17, 18],
"criteria": "DELETE API + 목록에서 제거"
}
],
"rollbackPlan": {
"onCreateFail": "모달 닫기",
"onUpdateFail": "테스트 입고 수동 삭제 필요",
"onDeleteFail": "테스트 입고 수동 삭제 필요",
"cleanupRequired": "E2E_TEST_ 접두사 입고는 테스트 데이터"
}
}

View File

@@ -0,0 +1,212 @@
{
"id": "material-stock",
"name": "재고현황 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "자재관리 > 재고현황 메뉴의 재고 현황 조회/검색/필터/다운로드 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "자재관리",
"level2": "재고현황",
"expectedUrl": "/material/stock-status",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 자재관리 > 재고현황",
"action": "menu_navigate",
"level1": "자재관리",
"level2": "재고현황",
"expected": {
"url_contains": "/material/stock",
"visible": ["재고현황", "재고"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"재고 목록 표시",
"검색 기능 존재",
"필터 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "재고현황 테이블 구조 확인",
"action": "verify_table",
"checks": [
"품목코드 컬럼",
"품목명 컬럼",
"현재고 컬럼",
"안전재고 컬럼",
"위치/창고 컬럼"
],
"expected": "재고 테이블 표시"
},
{
"id": 4,
"phase": "READ",
"name": "[READ] 재고 데이터 확인",
"action": "verify_detail",
"checks": [
"재고 데이터 행 존재 또는 '데이터 없음' 메시지"
],
"expected": "재고 데이터 표시"
},
{
"id": 5,
"phase": "SEARCH",
"name": "[SEARCH] 품목 검색",
"action": "fill",
"target": "input[type='search'], input[placeholder*='검색']",
"value": "테스트",
"submit": true
},
{
"id": 6,
"phase": "SEARCH",
"name": "[SEARCH] 검색 결과 확인",
"action": "verify_detail",
"checks": [
"검색 결과 표시 또는 결과 없음 메시지"
],
"expected": "검색 기능 동작"
},
{
"id": 7,
"phase": "SEARCH",
"name": "[SEARCH] 검색 초기화",
"action": "click_if_exists",
"target": "button:has-text('초기화'), button:has-text('리셋'), button[class*='clear']",
"expected": "검색 초기화"
},
{
"id": 8,
"phase": "FILTER",
"name": "[FILTER] 창고/위치 필터",
"action": "click_if_exists",
"target": "select[name*='warehouse'], select[name*='location'], button:has-text('창고')",
"expected": "창고 필터 옵션 표시"
},
{
"id": 9,
"phase": "FILTER",
"name": "[FILTER] 재고 상태 필터",
"action": "verify_elements",
"checks": [
"재고 부족/적정/과다 필터 가능"
],
"expected": "재고 상태 필터 표시"
},
{
"id": 10,
"name": "안전재고 이하 품목 확인",
"action": "verify_elements",
"checks": [
"안전재고 이하 품목 강조 표시",
"부족 재고 경고 표시"
],
"expected": "안전재고 알림 표시"
},
{
"id": 11,
"name": "재고 이동 이력 링크",
"action": "verify_elements",
"checks": [
"재고 이동 이력 조회 버튼 또는 링크"
],
"expected": "이력 조회 기능 표시"
},
{
"id": 12,
"name": "재고 현황 요약",
"action": "verify_elements",
"checks": [
"총 품목 수",
"재고 금액 합계",
"부족 품목 수"
],
"expected": "요약 정보 표시"
},
{
"id": 13,
"name": "필수 검증 #1: 엑셀 다운로드",
"action": "click_if_exists",
"target": "button:has-text('엑셀'), button:has-text('Excel'), button:has-text('다운로드')",
"verify": {
"api_call": "GET /api/v1/material/stock/export",
"file_download": true
},
"expected": "엑셀 파일 다운로드"
},
{
"id": 14,
"name": "인쇄 기능 확인",
"action": "verify_elements",
"checks": [
"인쇄 버튼 존재"
],
"expected": "인쇄 기능 표시"
},
{
"id": 15,
"name": "재고 조정 버튼 확인",
"action": "verify_elements",
"checks": [
"재고 조정 버튼 존재 여부"
],
"expected": "재고 조정 기능 확인"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/material/stock",
"description": "재고현황 조회"
},
{
"method": "GET",
"endpoint": "/api/v1/material/stock/export",
"description": "재고현황 엑셀 다운로드"
},
{
"method": "GET",
"endpoint": "/api/v1/material/warehouses",
"description": "창고 목록 조회"
}
],
"requiredVerifications": [
{
"id": 1,
"name": "엑셀 다운로드",
"steps": [13],
"criteria": "API 호출 + 파일 다운로드"
},
{
"id": 3,
"name": "검색/필터",
"steps": [5, 6, 7],
"criteria": "검색 + 필터 기능 동작"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "재고 목록, 검색 기능, 필터 존재"
}
],
"rollbackPlan": {
"note": "조회 전용 페이지로 데이터 변경 없음"
}
}

View File

@@ -0,0 +1,429 @@
{
"id": "pdf-download-test",
"name": "PDF 다운로드 전체 검사",
"description": "PDF 다운로드 버튼이 있는 모든 페이지를 순회하며 PDF 다운로드 기능과 품질을 검증하는 E2E 테스트",
"version": "1.0.0",
"createdAt": "2026-01-29",
"screenshotPolicy": {
"onErrorOnly": false,
"captureOn": ["before-download", "after-download", "error", "fail"]
},
"baseUrl": "https://dev.codebridge-x.com",
"timeout": 180000,
"tags": ["pdf", "download", "quality", "export"],
"login": {
"url": "https://dev.codebridge-x.com/login",
"credentials": {
"id": "TestUser5",
"password": "password123!"
}
},
"pdfTargets": [
{
"id": "draft-box",
"name": "기안함 PDF",
"menuNavigation": {
"level1": "결재관리",
"level2": "기안함"
},
"accessMethod": "modal",
"triggerAction": "clickFirstRow",
"pdfButtonSelector": "button:has-text('PDF')",
"pdfApiPattern": "/api/v1/approvals/*/pdf",
"expectedContentType": "application/pdf"
},
{
"id": "approval-box",
"name": "결재함 PDF",
"menuNavigation": {
"level1": "결재관리",
"level2": "결재함"
},
"accessMethod": "modal",
"triggerAction": "clickFirstRow",
"pdfButtonSelector": "button:has-text('PDF')",
"pdfApiPattern": "/api/v1/approvals/*/pdf",
"expectedContentType": "application/pdf"
},
{
"id": "reference-box",
"name": "참조함 PDF",
"menuNavigation": {
"level1": "결재관리",
"level2": "참조함"
},
"accessMethod": "modal",
"triggerAction": "clickFirstRow",
"pdfButtonSelector": "button:has-text('PDF')",
"pdfApiPattern": "/api/v1/approvals/*/pdf",
"expectedContentType": "application/pdf"
},
{
"id": "vendor-ledger",
"name": "거래처원장 PDF",
"menuNavigation": {
"level1": "자재관리",
"level2": "거래처원장"
},
"accessMethod": "detailPage",
"triggerAction": "clickFirstRow",
"pdfButtonSelector": "button:has-text('PDF 다운로드')",
"pdfApiPattern": "/api/v1/vendor-ledger/*/export-pdf",
"expectedContentType": "application/pdf"
}
],
"steps": [
{
"id": "step-0",
"name": "PDF 샘플 폴더 준비",
"description": "PDF 파일 저장 폴더 확인",
"setup": {
"createFolders": [
"react/tests/e2e/results/hotfix/pdf-samples/",
"react/tests/e2e/results/hotfix/screenshots/"
]
}
},
{
"id": "pdf-test-1",
"name": "기안함 PDF 다운로드 테스트",
"target": "draft-box",
"steps": [
{
"id": "1-1",
"action": "navigate",
"menu": { "level1": "결재관리", "level2": "기안함" },
"expect": { "url": "/approval/drafts" }
},
{
"id": "1-2",
"action": "waitForTable",
"timeout": 5000,
"expect": { "rowCount": ">= 1" }
},
{
"id": "1-3",
"action": "clickFirstRow",
"description": "첫 번째 문서 클릭하여 상세 모달 열기"
},
{
"id": "1-4",
"action": "waitForModal",
"timeout": 3000,
"expect": { "modalVisible": true }
},
{
"id": "1-5",
"action": "screenshot",
"name": "draft-box_pdf-preview_{timestamp}",
"selector": "[role='dialog'], .modal",
"savePath": "react/tests/e2e/results/hotfix/screenshots/",
"description": "PDF 생성 전 모달 스크린샷"
},
{
"id": "1-6",
"action": "verifyButtonExists",
"selector": "button:has-text('PDF')",
"expect": { "visible": true }
},
{
"id": "1-7",
"action": "setupDownloadListener",
"description": "다운로드 이벤트 리스너 설정"
},
{
"id": "1-8",
"action": "click_if_exists",
"selector": "button:has-text('PDF')",
"description": "PDF 버튼 클릭"
},
{
"id": "1-9",
"action": "wait",
"duration": 3000,
"description": "PDF 생성 및 다운로드 대기"
},
{
"id": "1-10",
"action": "verifyDownload",
"checks": {
"fileDownloaded": true,
"fileExtension": ".pdf",
"minFileSize": 1024,
"pdfSignature": "%PDF-"
}
},
{
"id": "1-11",
"action": "saveDownloadedFile",
"targetPath": "react/tests/e2e/results/hotfix/pdf-samples/",
"fileName": "draft-box_{timestamp}.pdf"
},
{
"id": "1-12",
"action": "closeModal",
"method": "pressEscape"
}
],
"onSuccess": {
"log": "draft-box PDF 다운로드 성공",
"recordFile": true
},
"onFail": {
"screenshot": true,
"continueToNext": true
}
},
{
"id": "pdf-test-2",
"name": "결재함 PDF 다운로드 테스트",
"target": "approval-box",
"steps": [
{
"id": "2-1",
"action": "navigate",
"menu": { "level1": "결재관리", "level2": "결재함" },
"expect": { "url": "/approval/approval-box" }
},
{
"id": "2-2",
"action": "waitForTable",
"timeout": 5000,
"expect": { "rowCount": ">= 1" }
},
{
"id": "2-3",
"action": "clickFirstRow",
"description": "첫 번째 문서 클릭하여 상세 모달 열기"
},
{
"id": "2-4",
"action": "waitForModal",
"timeout": 3000
},
{
"id": "2-5",
"action": "screenshot",
"name": "approval-box_pdf-preview_{timestamp}",
"selector": "[role='dialog'], .modal",
"savePath": "react/tests/e2e/results/hotfix/screenshots/"
},
{
"id": "2-6",
"action": "click_if_exists",
"selector": "button:has-text('PDF')"
},
{
"id": "2-7",
"action": "wait",
"duration": 3000
},
{
"id": "2-8",
"action": "verifyDownload",
"checks": {
"fileDownloaded": true,
"fileExtension": ".pdf",
"minFileSize": 1024
}
},
{
"id": "2-9",
"action": "saveDownloadedFile",
"targetPath": "react/tests/e2e/results/hotfix/pdf-samples/",
"fileName": "approval-box_{timestamp}.pdf"
},
{
"id": "2-10",
"action": "closeModal"
}
]
},
{
"id": "pdf-test-3",
"name": "참조함 PDF 다운로드 테스트",
"target": "reference-box",
"steps": [
{
"id": "3-1",
"action": "navigate",
"menu": { "level1": "결재관리", "level2": "참조함" },
"expect": { "url": "/approval/reference-box" }
},
{
"id": "3-2",
"action": "waitForTable",
"timeout": 5000,
"expect": { "rowCount": ">= 1" }
},
{
"id": "3-3",
"action": "clickFirstRow"
},
{
"id": "3-4",
"action": "waitForModal",
"timeout": 3000
},
{
"id": "3-5",
"action": "screenshot",
"name": "reference-box_pdf-preview_{timestamp}",
"selector": "[role='dialog'], .modal",
"savePath": "react/tests/e2e/results/hotfix/screenshots/"
},
{
"id": "3-6",
"action": "click_if_exists",
"selector": "button:has-text('PDF')"
},
{
"id": "3-7",
"action": "wait",
"duration": 3000
},
{
"id": "3-8",
"action": "verifyDownload",
"checks": {
"fileDownloaded": true,
"fileExtension": ".pdf",
"minFileSize": 1024
}
},
{
"id": "3-9",
"action": "saveDownloadedFile",
"targetPath": "react/tests/e2e/results/hotfix/pdf-samples/",
"fileName": "reference-box_{timestamp}.pdf"
},
{
"id": "3-10",
"action": "closeModal"
}
]
},
{
"id": "pdf-test-4",
"name": "거래처원장 PDF 다운로드 테스트",
"target": "vendor-ledger",
"steps": [
{
"id": "4-1",
"action": "navigate",
"menu": { "level1": "자재관리", "level2": "거래처원장" },
"expect": { "url": "/accounting/vendor-ledger" }
},
{
"id": "4-2",
"action": "waitForTable",
"timeout": 5000,
"expect": { "rowCount": ">= 1" }
},
{
"id": "4-3",
"action": "clickFirstRow",
"description": "거래처 클릭하여 상세 페이지 이동"
},
{
"id": "4-4",
"action": "waitForNavigation",
"timeout": 5000,
"expect": { "urlContains": "/accounting/vendor-ledger/" }
},
{
"id": "4-5",
"action": "screenshot",
"name": "vendor-ledger_pdf-preview_{timestamp}",
"fullPage": true,
"savePath": "react/tests/e2e/results/hotfix/screenshots/"
},
{
"id": "4-6",
"action": "verifyButtonExists",
"selector": "button:has-text('PDF 다운로드')",
"expect": { "visible": true, "count": ">= 1" }
},
{
"id": "4-7",
"action": "click_if_exists",
"selector": "button:has-text('PDF 다운로드')",
"index": 0,
"description": "첫 번째 PDF 다운로드 버튼 클릭"
},
{
"id": "4-8",
"action": "wait",
"duration": 3000
},
{
"id": "4-9",
"action": "verifyDownload",
"checks": {
"fileDownloaded": true,
"fileExtension": ".pdf",
"minFileSize": 1024
}
},
{
"id": "4-10",
"action": "saveDownloadedFile",
"targetPath": "react/tests/e2e/results/hotfix/pdf-samples/",
"fileName": "vendor-ledger_{timestamp}.pdf"
},
{
"id": "4-11",
"action": "navigateBack",
"description": "목록으로 돌아가기"
}
]
}
],
"pdfQualityChecklist": {
"description": "다운로드된 PDF 파일 수동 검증 체크리스트",
"items": [
{ "id": "q1", "category": "파일", "item": "PDF 파일이 정상적으로 열리는가?" },
{ "id": "q2", "category": "폰트", "item": "한글 폰트가 깨지지 않고 정상 표시되는가?" },
{ "id": "q3", "category": "테이블", "item": "테이블 경계선이 올바르게 표시되는가?" },
{ "id": "q4", "category": "정렬", "item": "숫자/금액이 우측 정렬되어 있는가?" },
{ "id": "q5", "category": "레이아웃", "item": "여백(margin/padding)이 적절한가?" },
{ "id": "q6", "category": "페이지", "item": "헤더/푸터가 각 페이지에 올바르게 표시되는가?" },
{ "id": "q7", "category": "이미지", "item": "로고/이미지가 정상 표시되는가?" },
{ "id": "q8", "category": "페이지나눔", "item": "페이지 나눔이 적절한 위치에서 발생하는가?" },
{ "id": "q9", "category": "색상", "item": "배경색/강조색이 올바르게 적용되었는가?" },
{ "id": "q10", "category": "오버플로우", "item": "텍스트가 잘리거나 겹치지 않는가?" }
]
},
"outputPaths": {
"pdfSamples": "react/tests/e2e/results/hotfix/pdf-samples/",
"screenshots": "react/tests/e2e/results/hotfix/screenshots/",
"report": "react/tests/e2e/results/hotfix/"
},
"reportTemplate": {
"successFile": "OK-pdf-download-test_{timestamp}.md",
"failFile": "Fail-pdf-download-test_{timestamp}.md",
"sections": [
"테스트 요약",
"개별 PDF 테스트 결과",
"다운로드된 PDF 파일 목록",
"품질 체크리스트 (수동 확인 필요)"
]
},
"executionNotes": [
"이 시나리오는 'PDF 다운로드 전체 검사해줘' 명령으로 실행",
"각 PDF 버튼 클릭 후 3초 대기 (PDF 생성 시간)",
"다운로드된 PDF는 pdf-samples 폴더에 타임스탬프와 함께 저장",
"테스트 완료 후 PDF 파일을 열어 품질 체크리스트 수동 확인 필요"
]
}

View File

@@ -0,0 +1,71 @@
{
"id": "production-dashboard",
"name": "생산 현황판 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "생산관리 > 생산 현황판 대시보드 기능 검증",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "생산관리",
"level2": "생산 현황판",
"expectedUrl": "/production/dashboard",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 생산관리 > 생산 현황판",
"action": "menu_navigate",
"level1": "생산관리",
"level2": "생산 현황판",
"expected": { "url_contains": "/production" }
},
{
"id": 2,
"name": "목업 감지",
"action": "verify_not_mockup"
},
{
"id": 3,
"name": "생산 현황판 페이지 확인",
"action": "verify_detail",
"checks": ["visible_text:생산"]
},
{
"id": 4,
"name": "현황 데이터 확인",
"action": "verify_detail",
"checks": ["visible_text:현황"]
},
{
"id": 5,
"name": "필터 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('필터'), select, [class*='filter'] button, [class*='date-picker']"
},
{
"id": 6,
"name": "대기",
"action": "wait",
"duration": 1000
},
{
"id": 7,
"name": "모달 닫기",
"action": "close_modal_if_open"
},
{
"id": 8,
"name": "생산 현황판 최종 확인",
"action": "verify_detail",
"checks": ["visible_text:생산"]
}
]
}

View File

@@ -0,0 +1,83 @@
{
"id": "production-item",
"name": "생산품목관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "품목관리 > 품목기준관리 목록/검색/상세 기능 검증",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "품목관리",
"level2": "품목기준관리",
"expectedUrl": "/master-data/item-master-data-management",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 품목관리 > 품목기준관리",
"action": "menu_navigate",
"level1": "품목관리",
"level2": "품목기준관리",
"expected": { "url_contains": "/master-data" }
},
{
"id": 2,
"name": "목업 감지",
"action": "verify_not_mockup"
},
{
"id": 3,
"name": "품목기준관리 페이지 확인",
"action": "verify_detail",
"checks": ["visible_text:품목"]
},
{
"id": 4,
"name": "테이블 또는 목록 확인",
"action": "verify_detail",
"checks": ["visible_text:관리"]
},
{
"id": 5,
"name": "검색 입력 시도",
"action": "click_if_exists",
"target": "input[type='search'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input"
},
{
"id": 6,
"name": "대기",
"action": "wait",
"duration": 1000
},
{
"id": 7,
"name": "행 클릭 시도",
"action": "click_if_exists",
"target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']"
},
{
"id": 8,
"name": "상세 확인",
"action": "verify_detail",
"checks": ["visible_text:품목"]
},
{
"id": 9,
"name": "모달 닫기",
"action": "close_modal_if_open"
},
{
"id": 10,
"name": "최종 확인",
"action": "verify_detail",
"checks": ["visible_text:품목"]
}
]
}

View File

@@ -0,0 +1,350 @@
{
"enabled": true,
"id": "production-work-order",
"name": "작업지시 관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
},
"description": "생산관리 > 작업지시 관리 메뉴의 작업지시 조회/등록/수정/삭제 전체 CRUD 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "생산관리",
"level2": "작업지시 관리",
"expectedUrl": "/production/work-orders",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"testData": {
"create": {
"orderNumber": "E2E_TEST_작업지시",
"itemName": "테스트품목",
"quantity": "500",
"dueDate": "2026-02-10",
"memo": "E2E 자동화 테스트 작업지시"
},
"update": {
"quantity": "600",
"memo": "E2E 수정된 작업지시 메모"
}
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 생산관리 > 작업지시 관리",
"action": "menu_navigate",
"level1": "생산관리",
"level2": "작업지시 관리",
"expected": {
"url_contains": "/production/work-orders",
"visible": [
"작업지시"
]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"작업지시 목록 표시",
"작업지시 등록 버튼 존재",
"검색/필터 기능 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "작업지시 테이블 구조 확인",
"action": "verify_table",
"checks": [
"작업지시번호 컬럼",
"품목 컬럼",
"수량 컬럼",
"납기일 컬럼",
"상태 컬럼"
],
"expected": "작업지시 테이블 컬럼 정상 표시"
},
{
"id": 4,
"name": "검색 기능 테스트",
"action": "click_if_exists",
"target": "input[placeholder*='검색']",
"value": "테스트",
"expected": {
"data_filtered": true
}
},
{
"id": 5,
"phase": "CREATE",
"name": "[CREATE] 작업지시 등록 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('등록'), button:has-text('작업지시 등록'), button:has-text('추가')",
"expected": {
"modal": true,
"modalTitle": "작업지시 등록"
}
},
{
"id": 6,
"phase": "CREATE",
"name": "[CREATE] 작업지시 정보 입력",
"action": "click_if_exists",
"fields": [
{
"name": "작업지시번호",
"type": "text",
"value": "E2E_TEST_작업지시_{timestamp}"
},
{
"name": "품목",
"type": "select",
"value": "테스트품목"
},
{
"name": "수량",
"type": "number",
"value": "500"
},
{
"name": "납기일",
"type": "date",
"value": "2026-02-10"
},
{
"name": "메모",
"type": "text",
"value": "E2E 자동화 테스트 작업지시"
}
],
"note": "타임스탬프로 고유성 보장",
"target": "form, [role='dialog'], .modal"
},
{
"id": 7,
"phase": "CREATE",
"name": "[CREATE] 필수 검증 #2: 등록 저장",
"action": "click_if_exists",
"target": "button:has-text('저장'), button:has-text('등록')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "POST /api/v1/work-orders",
"toast": "등록|완료|성공"
},
"expected": "작업지시 등록 완료"
},
{
"id": "7-modal-close",
"phase": "CREATE",
"name": "[CREATE] 모달 닫기 확인",
"action": "close_modal_if_open",
"expected": "모달 닫힘"
},
{
"id": 8,
"phase": "CREATE",
"name": "[CREATE] 등록 결과 확인",
"action": "verify_detail",
"search": "E2E_TEST_작업지시",
"expected": {
"row_exists": true,
"contains": [
"E2E",
"500"
]
}
},
{
"id": 9,
"phase": "READ",
"name": "[READ] 작업지시 상세 페이지 진입",
"action": "click_if_exists",
"target": "table tbody tr:has-text('E2E')",
"expected": {
"url_contains": "/production/work-orders/",
"visible": [
"작업지시 상세",
"수정",
"삭제"
]
}
},
{
"id": 10,
"phase": "READ",
"name": "[READ] 상세 정보 확인",
"action": "verify_detail",
"checks": [
"작업지시번호: E2E_TEST_작업지시",
"수량: 500",
"납기일: 2026-02-10"
],
"expected": "입력한 데이터와 일치"
},
{
"id": 11,
"phase": "UPDATE",
"name": "[UPDATE] 수정 모드 진입",
"action": "click_if_exists",
"target": "button:has-text('수정')",
"expected": {
"url_contains": "mode=edit",
"fields_editable": true
}
},
{
"id": 12,
"phase": "UPDATE",
"name": "[UPDATE] 수량 수정",
"action": "click_if_exists",
"target": "input[name*='quantity'], input[placeholder*='수량']",
"value": "600",
"clear": true
},
{
"id": 13,
"phase": "UPDATE",
"name": "[UPDATE] 메모 수정",
"action": "click_if_exists",
"target": "textarea[name*='memo'], input[placeholder*='메모']",
"value": "E2E 수정된 작업지시 메모_{timestamp}",
"clear": true
},
{
"id": 14,
"phase": "UPDATE",
"name": "[UPDATE] 필수 검증 #2: 수정 저장",
"action": "click_if_exists",
"target": "button:has-text('저장')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "PUT /api/v1/work-orders/",
"toast": "수정|완료|성공"
},
"expected": "수정 완료"
},
{
"id": 15,
"phase": "UPDATE",
"name": "[UPDATE] 수정 결과 확인",
"action": "verify_detail",
"checks": [
"수량: 600",
"메모: E2E 수정된 작업지시"
],
"expected": "수정된 데이터 반영"
},
{
"id": 16,
"phase": "DELETE",
"name": "[DELETE] 삭제 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('삭제')",
"expected": {
"confirm_dialog": true,
"dialog_message": "삭제|정말"
}
},
{
"id": 17,
"phase": "DELETE",
"name": "[DELETE] 필수 검증 #6: 삭제 확인",
"action": "click_if_exists",
"target": "button:has-text('확인'), button:has-text('삭제')",
"verify": {
"api_call": "DELETE /api/v1/work-orders/",
"toast": "삭제|완료|성공",
"redirect": "/production/work-orders"
},
"expected": "삭제 완료 및 목록 복귀"
},
{
"id": 18,
"phase": "DELETE",
"name": "[DELETE] 삭제 결과 확인",
"action": "verify_detail",
"search": "E2E 수정된 작업지시",
"expected": {
"row_exists": false,
"message": "테스트 작업지시가 목록에서 제거됨"
}
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/work-orders",
"description": "작업지시 목록 조회"
},
{
"method": "POST",
"endpoint": "/api/v1/work-orders",
"description": "작업지시 등록"
},
{
"method": "GET",
"endpoint": "/api/v1/work-orders/{id}",
"description": "작업지시 상세 조회"
},
{
"method": "PUT",
"endpoint": "/api/v1/work-orders/{id}",
"description": "작업지시 수정"
},
{
"method": "DELETE",
"endpoint": "/api/v1/work-orders/{id}",
"description": "작업지시 삭제"
}
],
"requiredVerifications": [
{
"id": 2,
"name": "등록/저장 버튼",
"steps": [
7,
14
],
"criteria": "API 호출 + 성공 토스트 + 데이터 반영"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [
2
],
"criteria": "작업지시 목록, 등록 버튼, 필터 존재"
},
{
"id": 6,
"name": "삭제 기능",
"steps": [
16,
17,
18
],
"criteria": "DELETE API + 목록에서 제거"
}
],
"rollbackPlan": {
"onCreateFail": "모달 닫기",
"onUpdateFail": "테스트 작업지시 수동 삭제 필요",
"onDeleteFail": "테스트 작업지시 수동 삭제 필요",
"cleanupRequired": "E2E_TEST_ 접두사 작업지시는 테스트 데이터"
}
}

View File

@@ -0,0 +1,257 @@
{
"enabled": true,
"id": "production-work-result",
"name": "작업실적 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "생산관리 > 작업실적 메뉴의 작업 실적 CRUD 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "생산관리",
"level2": "작업실적",
"expectedUrl": "/production/work-results",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"testData": {
"create": {
"description": "E2E_TEST_실적_{timestamp}",
"quantity": "100",
"defectQty": "5"
}
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 생산관리 > 작업실적",
"action": "menu_navigate",
"level1": "생산관리",
"level2": "작업실적",
"expected": {
"url_contains": "/production/work",
"visible": ["작업실적"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"작업실적 목록 표시",
"실적 등록 버튼 존재",
"기간 필터 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "작업실적 테이블 구조 확인",
"action": "verify_table",
"checks": [
"작업일 컬럼",
"품목 컬럼",
"지시 수량 컬럼",
"실적 수량 컬럼",
"작업자 컬럼"
],
"expected": "작업실적 테이블 표시"
},
{
"id": 4,
"phase": "FILTER",
"name": "[FILTER] 기간 필터 - 시작일",
"action": "click_if_exists",
"target": "input[type='date']:first-of-type, input[name*='start']",
"value": "2025-01-01"
},
{
"id": 5,
"phase": "FILTER",
"name": "[FILTER] 기간 필터 - 종료일",
"action": "click_if_exists",
"target": "input[type='date']:last-of-type, input[name*='end']",
"value": "2025-12-31"
},
{
"id": 6,
"phase": "FILTER",
"name": "[FILTER] 조회 실행",
"action": "click_if_exists",
"target": "button:has-text('조회'), button:has-text('검색')",
"expected": {
"api_call": "GET /api/v1/production/work-results"
}
},
{
"id": 7,
"phase": "CREATE",
"name": "[CREATE] 실적 등록 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('등록'), button:has-text('추가'), button:has-text('신규')",
"expected": {
"modal_open": true
}
},
{
"id": 8,
"phase": "CREATE",
"name": "[CREATE] 작업 지시 선택",
"action": "click_if_exists",
"target": "select[name*='order'], button:has-text('작업지시'), input[placeholder*='작업지시']",
"expected": "작업 지시 선택 가능"
},
{
"id": 9,
"phase": "CREATE",
"name": "[CREATE] 생산 수량 입력",
"action": "click_if_exists",
"target": "input[name*='quantity'], input[name*='qty'], input[placeholder*='수량']",
"value": "100",
"clear": true
},
{
"id": 10,
"phase": "CREATE",
"name": "[CREATE] 불량 수량 입력",
"action": "click_if_exists",
"target": "input[name*='defect'], input[placeholder*='불량']",
"value": "5",
"clear": true
},
{
"id": 11,
"phase": "CREATE",
"name": "[CREATE] 필수 검증 #2: 실적 저장",
"action": "click_if_exists",
"target": "button:has-text('저장'), button:has-text('등록'), button:has-text('확인')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "POST /api/v1/production/work-results",
"toast": "등록|저장|완료|성공"
},
"expected": "실적 등록 완료"
},
{
"id": 12,
"phase": "READ",
"name": "[READ] 등록된 실적 확인",
"action": "verify_detail",
"checks": [
"등록한 실적 목록에 표시"
],
"expected": "등록된 실적 확인"
},
{
"id": 13,
"phase": "READ",
"name": "[READ] 실적 상세 조회",
"action": "click_if_exists",
"target": "table tbody tr:first-child",
"expected": {
"detail_view": true
}
},
{
"id": 14,
"name": "실적 상세 정보 확인",
"action": "verify_detail",
"checks": [
"작업일시",
"품목 정보",
"생산 수량",
"불량 수량",
"작업자 정보"
],
"expected": "실적 상세 정보 표시"
},
{
"id": 15,
"phase": "UPDATE",
"name": "[UPDATE] 실적 수정",
"action": "click_if_exists",
"target": "button:has-text('수정'), button:has-text('편집')",
"expected": {
"edit_mode": true
}
},
{
"id": 16,
"phase": "UPDATE",
"name": "[UPDATE] 수량 수정",
"action": "click_if_exists",
"target": "input[name*='quantity'], input[name*='qty']",
"value": "95",
"clear": true
},
{
"id": 17,
"phase": "UPDATE",
"name": "[UPDATE] 수정 저장",
"action": "click_if_exists",
"target": "button:has-text('저장'), button:has-text('확인')",
"verify": {
"api_call": "PUT /api/v1/production/work-results",
"toast": "수정|저장|완료|성공"
},
"expected": "실적 수정 완료"
},
{
"id": 18,
"name": "엑셀 다운로드 확인",
"action": "click_if_exists",
"target": "button:has-text('엑셀'), button:has-text('Excel'), button:has-text('다운로드')",
"verify": {
"file_download": true
},
"expected": "엑셀 파일 다운로드"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/production/work-results",
"description": "작업실적 목록 조회"
},
{
"method": "POST",
"endpoint": "/api/v1/production/work-results",
"description": "작업실적 등록"
},
{
"method": "PUT",
"endpoint": "/api/v1/production/work-results/:id",
"description": "작업실적 수정"
},
{
"method": "GET",
"endpoint": "/api/v1/production/work-results/export",
"description": "작업실적 엑셀 다운로드"
}
],
"requiredVerifications": [
{
"id": 2,
"name": "저장 버튼",
"steps": [11, 17],
"criteria": "API 호출 + 성공 토스트 + 데이터 반영"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "작업실적 목록, 등록 버튼, 기간 필터 존재"
}
],
"rollbackPlan": {
"onCreateFail": "등록 모달 닫고 재시도",
"onUpdateFail": "페이지 새로고침 후 재시도",
"note": "작업실적은 작업지시와 연관되어 있어 주의 필요"
}
}

View File

@@ -0,0 +1,83 @@
{
"id": "production-worker",
"name": "작업자 화면 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "생산관리 > 작업자 화면 목록/상세 기능 검증",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "생산관리",
"level2": "작업자 화면",
"expectedUrl": "/production/worker-screen",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 생산관리 > 작업자 화면",
"action": "menu_navigate",
"level1": "생산관리",
"level2": "작업자 화면",
"expected": { "url_contains": "/production" }
},
{
"id": 2,
"name": "목업 감지",
"action": "verify_not_mockup"
},
{
"id": 3,
"name": "작업자 화면 페이지 확인",
"action": "verify_detail",
"checks": ["visible_text:작업"]
},
{
"id": 4,
"name": "UI 요소 확인",
"action": "verify_detail",
"checks": ["visible_text:관리"]
},
{
"id": 5,
"name": "검색 입력 시도",
"action": "click_if_exists",
"target": "input[type='search'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input"
},
{
"id": 6,
"name": "대기",
"action": "wait",
"duration": 1000
},
{
"id": 7,
"name": "행 클릭 시도",
"action": "click_if_exists",
"target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']"
},
{
"id": 8,
"name": "상세 확인",
"action": "verify_detail",
"checks": ["visible_text:작업"]
},
{
"id": 9,
"name": "모달 닫기",
"action": "close_modal_if_open"
},
{
"id": 10,
"name": "최종 확인",
"action": "verify_detail",
"checks": ["visible_text:작업"]
}
]
}

View File

@@ -0,0 +1,83 @@
{
"id": "quality-certification",
"name": "품질인정심사 시스템 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "품질관리 > 품질인정심사 시스템 목록/상세 기능 검증",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "품질관리",
"level2": "품질인정심사 시스템",
"expectedUrl": "/quality/qms",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 품질관리 > 품질인정심사 시스템",
"action": "menu_navigate",
"level1": "품질관리",
"level2": "품질인정심사 시스템",
"expected": { "url_contains": "/quality" }
},
{
"id": 2,
"name": "목업 감지",
"action": "verify_not_mockup"
},
{
"id": 3,
"name": "품질인정심사 페이지 확인",
"action": "verify_detail",
"checks": ["visible_text:품질"]
},
{
"id": 4,
"name": "UI 요소 확인",
"action": "verify_detail",
"checks": ["visible_text:인정"]
},
{
"id": 5,
"name": "검색 입력 시도",
"action": "click_if_exists",
"target": "input[type='search'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input"
},
{
"id": 6,
"name": "대기",
"action": "wait",
"duration": 1000
},
{
"id": 7,
"name": "행 클릭 시도",
"action": "click_if_exists",
"target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']"
},
{
"id": 8,
"name": "상세 확인",
"action": "verify_detail",
"checks": ["visible_text:품질"]
},
{
"id": 9,
"name": "모달 닫기",
"action": "close_modal_if_open"
},
{
"id": 10,
"name": "최종 확인",
"action": "verify_detail",
"checks": ["visible_text:품질"]
}
]
}

View File

@@ -0,0 +1,359 @@
{
"enabled": true,
"id": "quality-inspection",
"name": "제품검사관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
},
"description": "품질관리 > 제품검사관리 메뉴의 제품검사 조회/등록/수정/삭제 전체 CRUD 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "품질관리",
"level2": "제품검사관리",
"expectedUrl": "/quality/inspections",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"testData": {
"create": {
"siteName": "E2E_TEST_현장",
"orderCompany": "E2E_TEST_수주처",
"location": "테스트구역A",
"inspector": "홍길동",
"memo": "E2E 자동화 테스트 제품검사"
},
"update": {
"location": "테스트구역B",
"memo": "E2E 수정된 제품검사 메모"
}
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 품질관리 > 제품검사관리",
"action": "menu_navigate",
"level1": "품질관리",
"level2": "제품검사관리",
"expected": {
"url_contains": "/quality/inspections",
"visible": [
"제품검사",
"검사"
]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"제품검사 목록 표시",
"제품검사 등록 버튼 존재",
"검색/필터 기능 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "제품검사 테이블 구조 확인",
"action": "verify_table",
"checks": [
"품질관리서 번호 컬럼",
"현장명 컬럼",
"수주처 컬럼",
"검사기간 컬럼",
"상태 컬럼",
"검사자 컬럼"
],
"expected": "제품검사 테이블 컬럼 정상 표시"
},
{
"id": 4,
"name": "검색 기능 테스트",
"action": "click_if_exists",
"target": "input[placeholder*='검색']",
"value": "테스트",
"expected": {
"data_filtered": true
}
},
{
"id": 5,
"phase": "CREATE",
"name": "[CREATE] 제품검사 등록 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('등록'), button:has-text('제품검사 등록'), button:has-text('추가')",
"expected": {
"modal_or_page": true,
"title": "제품검사 등록"
}
},
{
"id": 6,
"phase": "CREATE",
"name": "[CREATE] 제품검사 정보 입력",
"action": "fill_form",
"fields": [
{
"name": "현장명",
"type": "text",
"value": "E2E_TEST_현장_{timestamp}"
},
{
"name": "수주처",
"type": "select",
"value": "E2E_TEST_수주처"
},
{
"name": "개소",
"type": "text",
"value": "테스트구역A"
},
{
"name": "검사자",
"type": "select",
"value": "홍길동"
},
{
"name": "메모",
"type": "text",
"value": "E2E 자동화 테스트 제품검사_{timestamp}"
}
],
"note": "타임스탬프로 고유성 보장"
},
{
"id": 7,
"phase": "CREATE",
"name": "[CREATE] 필수 검증 #2: 등록 저장",
"action": "click_if_exists",
"target": "button:has-text('저장'), button:has-text('등록')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "POST /api/v1/quality/inspections",
"toast": "등록|완료|성공"
},
"expected": "제품검사 등록 완료"
},
{
"id": "7-modal-close",
"phase": "CREATE",
"name": "[CREATE] 모달 닫기 확인",
"action": "close_modal_if_open",
"expected": "모달 닫힘"
},
{
"id": 8,
"phase": "CREATE",
"name": "[CREATE] 등록 결과 확인",
"action": "verify_detail",
"search": "E2E_TEST_현장",
"expected": {
"row_exists": true,
"contains": [
"E2E_TEST",
"접수"
]
}
},
{
"id": 9,
"phase": "READ",
"name": "[READ] 제품검사 상세 페이지 진입",
"action": "click_if_exists",
"target": "table tbody tr:has-text('E2E_TEST')",
"expected": {
"url_contains": "/quality/inspections/",
"visible": [
"품질관리서",
"수정",
"삭제"
]
}
},
{
"id": 10,
"phase": "READ",
"name": "[READ] 상세 정보 확인",
"action": "verify_detail",
"checks": [
"현장명: E2E_TEST_현장",
"개소: 테스트구역A",
"검사자: 홍길동"
],
"expected": "입력한 데이터와 일치"
},
{
"id": 11,
"phase": "UPDATE",
"name": "[UPDATE] 수정 모드 진입",
"action": "click_if_exists",
"target": "button:has-text('수정')",
"expected": {
"url_contains": "mode=edit",
"fields_editable": true
}
},
{
"id": 12,
"phase": "UPDATE",
"name": "[UPDATE] 개소 수정",
"action": "click_if_exists",
"target": "input[name*='location'], input[placeholder*='개소']",
"value": "테스트구역B",
"clear": true
},
{
"id": 13,
"phase": "UPDATE",
"name": "[UPDATE] 메모 수정",
"action": "click_if_exists",
"target": "textarea[name*='memo'], input[placeholder*='메모']",
"value": "E2E 수정된 제품검사 메모_{timestamp}",
"clear": true
},
{
"id": 14,
"phase": "UPDATE",
"name": "[UPDATE] 필수 검증 #2: 수정 저장",
"action": "click_if_exists",
"target": "button:has-text('저장')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "PUT /api/v1/quality/inspections/",
"toast": "수정|완료|성공"
},
"expected": "수정 완료"
},
{
"id": 15,
"phase": "UPDATE",
"name": "[UPDATE] 수정 결과 확인",
"action": "verify_detail",
"checks": [
"개소: 테스트구역B",
"메모: E2E 수정된"
],
"expected": "수정된 데이터 반영"
},
{
"id": 16,
"phase": "DELETE",
"name": "[DELETE] 삭제 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('삭제')",
"expected": {
"confirm_dialog": true,
"dialog_message": "삭제|정말"
}
},
{
"id": 17,
"phase": "DELETE",
"name": "[DELETE] 필수 검증 #6: 삭제 확인",
"action": "click_if_exists",
"target": "button:has-text('확인'), button:has-text('삭제')",
"verify": {
"api_call": "DELETE /api/v1/quality/inspections/",
"toast": "삭제|완료|성공",
"redirect": "/quality/inspections"
},
"expected": "삭제 완료 및 목록 복귀"
},
{
"id": 18,
"phase": "DELETE",
"name": "[DELETE] 삭제 결과 확인",
"action": "verify_detail",
"search": "E2E 수정된 제품검사",
"expected": {
"row_exists": false,
"message": "테스트 제품검사가 목록에서 제거됨"
}
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/quality/inspections",
"description": "제품검사 목록 조회"
},
{
"method": "POST",
"endpoint": "/api/v1/quality/inspections",
"description": "제품검사 등록"
},
{
"method": "GET",
"endpoint": "/api/v1/quality/inspections/{id}",
"description": "제품검사 상세 조회"
},
{
"method": "PUT",
"endpoint": "/api/v1/quality/inspections/{id}",
"description": "제품검사 수정"
},
{
"method": "DELETE",
"endpoint": "/api/v1/quality/inspections/{id}",
"description": "제품검사 삭제"
}
],
"requiredVerifications": [
{
"id": 2,
"name": "등록/저장 버튼",
"steps": [
7,
14
],
"criteria": "API 호출 + 성공 토스트 + 데이터 반영"
},
{
"id": 3,
"name": "검색/필터",
"steps": [
4
],
"criteria": "검색 기능 동작"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [
2
],
"criteria": "제품검사 목록, 등록 버튼, 필터 존재"
},
{
"id": 6,
"name": "삭제 기능",
"steps": [
16,
17,
18
],
"criteria": "DELETE API + 목록에서 제거"
}
],
"rollbackPlan": {
"onCreateFail": "모달 닫기",
"onUpdateFail": "테스트 제품검사 수동 삭제 필요",
"onDeleteFail": "테스트 제품검사 수동 삭제 필요",
"cleanupRequired": "E2E_TEST_ 접두사 제품검사는 테스트 데이터"
}
}

View File

@@ -0,0 +1,197 @@
{
"enabled": true,
"id": "receiving-management",
"name": "입고관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "자재관리 > 입고관리 페이지의 입고 조회 및 상태별 필터링 기능을 테스트하는 E2E 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"url": "/material/receiving-management",
"navigation": {
"targetUrl": "/material/receiving-management",
"urlPattern": "/material/receiving-management|/ko/material/receiving-management",
"menuHints": ["입고관리", "입고 관리", "자재관리"]
},
"menuNavigation": {
"level1": "자재관리",
"level2": "입고관리",
"expectedUrl": "/material/receiving-management",
"searchWithinParent": true,
"closeOtherMenus": true
},
"menuNavigationEnhanced": {
"strategy": "scroll-and-search",
"sidebar": {
"scrollContainer": ".sidebar-scroll",
"scrollStep": 200,
"maxScrollAttempts": 5,
"waitAfterScroll": 300
},
"level1": {
"text": "자재관리",
"expandable": true,
"waitAfterClick": 500
},
"level2": {
"text": "입고관리",
"waitAfterClick": 300
},
"fallbackUrl": "/material/receiving-management",
"expectedUrl": "/material/receiving-management"
},
"timeout": 90000,
"tags": ["material", "receiving", "read-only"],
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": "step-0",
"name": "사이드바 메뉴 전체 펼치기",
"description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비",
"actions": [
{
"type": "evaluate",
"script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})"
},
{ "type": "wait", "duration": 300 },
{
"type": "evaluate",
"script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()"
},
{ "type": "wait", "duration": 2000 }
]
},
{
"id": "step-1",
"name": "자재관리 메뉴 진입",
"description": "자재관리 > 입고관리 메뉴로 이동",
"actions": [
{
"type": "scrollAndFind",
"container": ".sidebar-scroll",
"target": "자재관리",
"scrollStep": 200,
"maxAttempts": 5
},
{ "type": "click_if_exists", "target": "자재관리" },
{ "type": "wait", "duration": 500 },
{ "type": "click_if_exists", "target": "입고관리" }
],
"expect": {
"url": "/material/receiving-management",
"visible": ["입고 목록"]
},
"fallback": {
"type": "navigate",
"url": "/material/receiving-management"
}
},
{
"id": "step-2",
"name": "페이지 구조 확인",
"description": "통계 카드와 테이블 구조 확인",
"verify": {
"visible": ["입고대기", "배송중", "검사대기", "금일입고"],
"tableColumns": ["번호", "발주번호", "품목코드", "품목명", "공급업체", "발주수량", "입고수량", "LOT번호", "상태"]
}
},
{
"id": "step-3",
"name": "필수 검증 #3: 상태 탭 필터 - 입고대기",
"description": "입고대기 탭 클릭하여 필터링 확인",
"actions": [
{ "type": "click_if_exists", "target": "입고대기", "role": "tab" },
{ "type": "wait", "duration": 300 }
],
"expect": {
"tabActive": "입고대기",
"dataFiltered": true
}
},
{
"id": "step-4",
"name": "필수 검증 #3: 상태 탭 필터 - 입고완료",
"description": "입고완료 탭 클릭하여 필터링 확인",
"actions": [
{ "type": "click_if_exists", "target": "입고완료", "role": "tab" },
{ "type": "wait", "duration": 300 }
],
"expect": {
"tabActive": "입고완료",
"dataFiltered": true
}
},
{
"id": "step-5",
"name": "전체 탭으로 복귀",
"description": "전체 탭 클릭하여 모든 입고 표시",
"actions": [
{ "type": "click_if_exists", "target": "전체", "role": "tab" },
{ "type": "wait", "duration": 300 }
],
"expect": {
"tabActive": "전체",
"allDataShown": true
}
},
{
"id": "step-6",
"name": "빈 상태 확인",
"description": "데이터가 없을 때 빈 상태 메시지 확인",
"verify": {
"emptyStateVisible": "검색 결과가 없습니다"
}
},
{
"id": "step-7",
"name": "통계 카드 값 확인",
"description": "입고대기/배송중/검사대기/금일입고 카운트 표시 확인",
"verify": {
"statsCards": ["입고대기", "배송중", "검사대기", "금일입고"],
"countsDisplayed": true
}
}
],
"assertions": [
{
"type": "url",
"expected": "/material/receiving-management",
"message": "입고관리 페이지에 머물러야 함"
},
{
"type": "elementExists",
"selector": "text=입고 목록",
"message": "입고 목록 제목이 표시되어야 함"
}
],
"mandatoryVerifications": {
"description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목",
"items": [
{
"id": 3,
"name": "검색/필터",
"trigger": "상태 탭 필터",
"verification": "데이터 변화 확인",
"failCondition": "필터 적용 후 데이터 무변화"
}
]
},
"notes": {
"testScope": "입고 목록 조회 및 상태별 필터링 테스트",
"pageType": "조회 전용 (발주 기반 입고 처리)",
"statsCards": ["입고대기", "배송중", "검사대기", "금일입고"],
"statusTabs": ["전체", "입고대기", "입고완료"],
"tableColumns": ["번호", "발주번호", "품목코드", "품목명", "공급업체", "발주수량", "입고수량", "LOT번호", "상태"],
"workflow": "발주 → 배송중 → 검사대기 → 입고완료",
"prerequisites": "로그인된 사용자, 발주 데이터 존재 시 입고 가능"
}
}

View File

@@ -0,0 +1,839 @@
{
"id": "reference-box",
"name": "참조함 E2E 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
},
"description": "참조함 페이지의 모든 기능 검증 (탭 전환, 검색, 필터, 정렬, 열람/미열람 처리, 문서 상세)",
"baseUrl": "https://dev.codebridge-x.com",
"navigation": {
"targetUrl": "/approval/reference",
"urlPattern": "/approval/reference|/ko/approval/reference",
"menuHints": [
"참조함",
"참조 함",
"결재관리"
]
},
"menuNavigation": {
"level1": "결재관리",
"level2": "참조함",
"expectedUrl": "/ko/approval/reference",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"menuNavigationEnhanced": {
"strategy": "scroll-and-search",
"description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지",
"level1": "결재관리",
"level2": "참조함",
"alternativeLevel1Names": [
"결재관리",
"결재 관리",
"Approval",
"전자결재"
],
"alternativeLevel2Names": [
"참조함",
"참조 함",
"Reference",
"참조문서",
"CC문서"
],
"scrollConfig": {
"sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar",
"menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']",
"scrollStep": 200,
"maxScrollAttempts": 10,
"scrollDelay": 300
}
},
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/approvals/reference",
"description": "참조함 목록 조회",
"queryParams": [
"page",
"per_page",
"search",
"is_read",
"approval_type",
"sort_by",
"sort_dir"
]
},
{
"method": "POST",
"endpoint": "/api/v1/approvals/{id}/read",
"description": "열람 처리"
},
{
"method": "POST",
"endpoint": "/api/v1/approvals/{id}/unread",
"description": "미열람 처리"
}
],
"steps": [
{
"id": 0,
"name": "사이드바 메뉴 전체 펼치기",
"description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비",
"actions": [
{
"type": "evaluate",
"script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})"
},
{
"type": "wait",
"duration": 300
},
{
"type": "evaluate",
"script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()"
},
{
"type": "wait",
"duration": 2000
}
]
},
{
"id": 1,
"name": "2단계 메뉴 진입: 결재관리 > 참조함",
"description": "사이드바를 스크롤하며 결재관리 > 참조함 메뉴를 찾아 클릭",
"actions": [
{
"type": "scrollAndFind",
"target": "결재관리",
"alternativeTexts": [
"결재관리",
"결재 관리",
"Approval",
"전자결재"
],
"scrollContainer": "sidebar",
"maxAttempts": 10,
"description": "스크롤하며 결재관리 메뉴 찾기"
},
{
"type": "click_if_exists",
"target": "결재관리",
"description": "결재관리 메뉴 클릭"
},
{
"type": "wait",
"duration": 500,
"description": "서브메뉴 펼쳐지기 대기"
},
{
"type": "scrollAndFind",
"target": "참조함",
"alternativeTexts": [
"참조함",
"참조 함",
"Reference",
"참조문서"
],
"scrollContainer": "submenu",
"maxAttempts": 5,
"description": "서브메뉴에서 참조함 찾기"
},
{
"type": "click_if_exists",
"target": "참조함",
"description": "참조함 메뉴 클릭"
},
{
"type": "wait",
"target": "페이지 로드 완료",
"timeout": 10000
}
],
"verification": [
"페이지 URL이 /approval/reference인지 확인",
"페이지 제목 '참조함' 표시 확인",
"설명 텍스트 '참조로 지정된 문서를 확인합니다.' 표시 확인",
"통계 카드 3개 표시 (전체, 열람, 미열람)",
"날짜 범위 선택기 표시 확인",
"검색창 표시 확인 (placeholder: '제목, 기안자, 부서 검색...')",
"탭 버튼 3개 표시 (전체, 열람, 미열람)",
"필터 드롭다운 표시 (전체, 지출결의서, 품의서, 지출예상내역서)",
"정렬 드롭다운 표시 (최신순, 오래된순, 기안일 오름차순, 기안일 내림차순)",
"테이블 헤더 확인 (번호, 문서번호, 문서유형, 제목, 기안자, 기안일시, 상태)"
]
},
{
"id": 2,
"name": "데이터 로딩 대기",
"action": "wait",
"duration": 3000,
"verification": [
"테이블에 데이터 행이 표시되는지 확인",
"통계 카드에 숫자가 표시되는지 확인 (N건)",
"'검색 결과가 없습니다' 메시지가 없는지 확인",
"각 문서의 열람 상태 배지 표시 확인 (열람/미열람)"
]
},
{
"id": 3,
"name": "통계 카드 데이터 확인",
"action": "verify_element",
"target": "[class*='card'], [class*='stat']",
"verification": [
"전체 건수 = 열람 건수 + 미열람 건수",
"각 카드의 아이콘 표시 확인 (Files, Eye, EyeOff)",
"스크린샷 촬영하여 초기 상태 저장"
]
},
{
"id": 4,
"name": "탭 전환 - 열람 탭",
"action": "click_if_exists",
"target": "button:has-text('열람')",
"verification": [
"탭 활성화 상태 변경 확인",
"테이블에 '열람' 상태 문서만 표시",
"모든 행의 상태 배지가 '열람'인지 확인",
"표시된 문서 개수가 통계 카드의 '열람' 건수와 일치"
]
},
{
"id": 5,
"name": "탭 전환 - 미열람 탭",
"action": "click_if_exists",
"target": "button:has-text('미열람')",
"verification": [
"탭 활성화 상태 변경 확인",
"테이블에 '미열람' 상태 문서만 표시",
"모든 행의 상태 배지가 '미열람'인지 확인",
"표시된 문서 개수가 통계 카드의 '미열람' 건수와 일치"
]
},
{
"id": 6,
"name": "탭 전환 - 전체 탭으로 복귀",
"action": "click_if_exists",
"target": "button:has-text('전체')",
"verification": [
"탭 활성화 상태 변경 확인",
"테이블에 모든 문서 표시 (열람 + 미열람)",
"표시된 문서 개수가 통계 카드의 '전체' 건수와 일치"
]
},
{
"id": 7,
"name": "⚠️ 필수 검증: 검색 기능 - 기안자 검색",
"actions": [
{
"type": "capture",
"variable": "beforeSearchCount",
"selector": "table tbody tr",
"extract": "count",
"description": "검색 전 문서 수 저장"
},
{
"type": "click_if_exists",
"target": "input[type='search'], input[placeholder*='검색']",
"description": "검색창 존재 확인"
},
{
"type": "wait",
"duration": 1000,
"description": "검색 결과 로딩 대기"
},
{
"type": "capture",
"variable": "afterSearchCount",
"selector": "table tbody tr",
"extract": "count",
"description": "검색 후 문서 수 저장"
}
],
"verify": {
"searchApplied": true,
"tableContains": "김철수",
"dataChanged": "beforeSearchCount may differ from afterSearchCount"
},
"verification": [
"검색창에 입력한 텍스트 표시 확인",
"Enter 키 입력 또는 자동 검색 실행",
"검색어를 포함한 문서만 필터링되어 표시",
"검색 결과 건수 확인"
]
},
{
"id": "7-1",
"name": "검색 결과 데이터 검증",
"description": "검색 결과의 모든 행이 검색어를 포함하는지 확인",
"verify": {
"allRowsContain": "김철수",
"columnToCheck": "기안자"
}
},
{
"id": 8,
"name": "검색 초기화",
"actions": [
{
"type": "click_if_exists",
"target": "input[type='search'], input[placeholder*='검색']",
"description": "검색창 존재 확인"
},
{
"type": "wait",
"duration": 500
},
{
"type": "capture",
"variable": "afterClearCount",
"selector": "table tbody tr",
"extract": "count"
}
],
"verify": {
"dataRestored": "afterClearCount should equal beforeSearchCount"
},
"verification": [
"전체 문서 목록 복원 확인",
"원래 문서 개수로 복원"
]
},
{
"id": 9,
"name": "필터 기능 - 문서유형 선택",
"action": "select",
"target": "select, [role='combobox']",
"value": "품의서",
"verification": [
"필터 드롭다운 값이 '품의서'로 변경",
"테이블에 '품의서' 유형 문서만 표시",
"모든 행의 문서유형 배지가 '품의서'인지 확인"
]
},
{
"id": 10,
"name": "필터 초기화",
"action": "select",
"target": "select, [role='combobox']",
"value": "전체",
"verification": [
"전체 문서 목록 복원 확인"
]
},
{
"id": 11,
"name": "정렬 기능 - 오래된순",
"action": "click_if_exists",
"target": "select, [role='combobox']",
"verification": [
"정렬 드롭다운 값이 '오래된순'으로 변경",
"테이블 데이터가 오래된 날짜부터 표시",
"기안일시 컬럼 확인하여 오름차순 정렬 검증"
]
},
{
"id": 12,
"name": "정렬 초기화",
"action": "click_if_exists",
"target": "select, [role='combobox']",
"verification": [
"테이블 데이터가 최신 날짜부터 표시"
]
},
{
"id": 13,
"name": "체크박스 - 단일 선택",
"action": "click_if_exists",
"target": "table tbody tr:first-child input[type='checkbox']",
"verification": [
"체크박스 선택 상태 확인",
"'열람' 버튼 표시 확인",
"'미열람' 버튼 표시 확인",
"조건부 버튼이 헤더 영역에 표시됨"
]
},
{
"id": 14,
"name": "체크박스 - 선택 해제",
"action": "click_if_exists",
"target": "table tbody tr:first-child input[type='checkbox']",
"verification": [
"체크박스 선택 해제 확인",
"'열람' 및 '미열람' 버튼 사라짐 확인"
]
},
{
"id": 15,
"name": "체크박스 - 다중 선택",
"action": "click_if_exists",
"target": "table tbody tr input[type='checkbox']",
"verification": [
"2개 문서 선택 확인",
"'열람' 및 '미열람' 버튼 표시 확인"
]
},
{
"id": 16,
"name": "문서 상세 모달 - 열기",
"action": "click_if_exists",
"target": "table tbody tr:first-child td:nth-child(2)",
"verification": [
"문서 상세 모달 열림 확인",
"⚠️ 필수 검증 #5: 목업 페이지 감지",
"모달 제목 확인 (문서유형 + ' 상세')",
"문서번호 표시 확인",
"작성일자 표시 확인",
"기안자 정보 표시 확인",
"결재선 정보 표시 확인",
"PDF/인쇄 버튼 존재 확인"
]
},
{
"id": "16-pdf-1",
"name": "⚠️ 필수 검증: PDF 다운로드 전 모달 스크린샷",
"description": "PDF 생성 전 모달 상태를 스크린샷으로 캡처하여 CSS 문제 감지용 기준 이미지 확보",
"prerequisite": "step-16의 문서 상세 모달이 열려있는 상태에서 실행",
"actions": [
{
"type": "screenshot",
"name": "pdf-preview-before-download-reference-box",
"fullPage": false,
"selector": "[role='dialog'], .modal, [data-state='open']",
"savePath": "tests/e2e/results/hotfix/screenshots/",
"description": "PDF 생성 대상 모달 전체 캡처"
}
],
"verify": {
"screenshotCaptured": true,
"purpose": "PDF CSS 문제 감지를 위한 기준 이미지"
}
},
{
"id": "16-pdf-2",
"name": "⚠️ 필수 검증: PDF 다운로드 실행 및 파일 보관",
"description": "PDF 다운로드 후 파일을 지정 폴더에 보관하여 수동 검증 가능하게 함",
"actions": [
{
"type": "verify",
"target": "PDF 버튼 존재",
"selector": "button:has-text('PDF'), [aria-label*='PDF']",
"description": "PDF 다운로드 버튼 존재 확인"
},
{
"type": "expectResponse",
"id": "pdf-download-response-reference-box",
"urlPattern": "/api/v1/approvals/*/pdf",
"description": "PDF 다운로드 API 응답 대기 설정"
},
{
"type": "click_if_exists",
"target": "PDF 버튼",
"selector": "button:has-text('PDF')",
"description": "PDF 다운로드 버튼 클릭"
},
{
"type": "wait",
"duration": 3000,
"description": "PDF 생성 및 다운로드 대기"
},
{
"type": "assertResponse",
"id": "pdf-download-response-reference-box",
"checks": {
"status": 200,
"contentType": "application/pdf"
}
},
{
"type": "saveDownloadedFile",
"targetPath": "tests/e2e/results/hotfix/pdf-samples/",
"fileNamePattern": "reference-box-{timestamp}.pdf",
"description": "다운로드된 PDF 파일을 지정 폴더에 보관"
}
],
"verify": {
"apiSuccess": true,
"fileDownloaded": true,
"fileSaved": "tests/e2e/results/hotfix/pdf-samples/"
}
},
{
"id": "16-pdf-3",
"name": "⚠️ PDF 파일 유효성 검증",
"description": "다운로드된 PDF 파일의 기본 유효성 검사",
"actions": [
{
"type": "verifyDownloadedFile",
"checks": {
"fileExists": true,
"fileSize": "> 1024",
"pdfSignature": "%PDF-",
"description": "PDF 파일 헤더 검증"
}
}
],
"verify": {
"pdfValid": true,
"minFileSize": "1KB 이상"
}
},
{
"id": "16-pdf-4",
"name": "📋 PDF 스타일 수동 확인 체크리스트",
"type": "manualVerification",
"description": "개발자가 다운로드된 PDF를 열어 시각적으로 확인해야 하는 항목",
"manualChecklist": [
{
"id": "css-1",
"item": "테이블 경계선이 올바르게 표시되는가?",
"category": "테이블 스타일"
},
{
"id": "css-2",
"item": "한글 폰트가 깨지지 않고 정상 표시되는가?",
"category": "폰트"
},
{
"id": "css-3",
"item": "숫자/금액 정렬이 올바른가? (우측 정렬)",
"category": "정렬"
},
{
"id": "css-4",
"item": "여백(margin/padding)이 적절한가?",
"category": "레이아웃"
},
{
"id": "css-5",
"item": "헤더/푸터가 각 페이지에 올바르게 표시되는가?",
"category": "페이지 구조"
},
{
"id": "css-6",
"item": "로고/이미지가 정상 표시되는가?",
"category": "이미지"
},
{
"id": "css-7",
"item": "페이지 나눔(page break)이 적절한 위치에서 발생하는가?",
"category": "페이지 나눔"
},
{
"id": "css-8",
"item": "배경색/강조색이 올바르게 적용되었는가?",
"category": "색상"
},
{
"id": "css-9",
"item": "텍스트가 잘리거나 겹치지 않는가?",
"category": "오버플로우"
},
{
"id": "css-10",
"item": "결재선 정보가 정상적으로 표시되는가?",
"category": "결재선"
}
],
"outputFiles": {
"screenshot": "tests/e2e/results/hotfix/screenshots/pdf-preview-before-download-reference-box-*.png",
"pdfFile": "tests/e2e/results/hotfix/pdf-samples/reference-box-*.pdf"
},
"reportFlag": {
"requiresManualReview": true,
"message": "⚠️ PDF 스타일 수동 확인 필요 - 위 체크리스트 항목을 PDF 파일에서 직접 확인하세요"
}
},
{
"id": 17,
"name": "문서 상세 모달 - 닫기",
"action": "close_modal",
"verification": [
"모달 닫힘 확인",
"원래 참조함 페이지로 복귀",
"페이지 URL 유지 (/approval/reference)"
]
},
{
"id": 18,
"name": "미열람 탭으로 이동",
"action": "click_if_exists",
"target": "button:has-text('미열람')",
"verification": [
"미열람 문서만 표시 확인",
"모든 문서의 상태가 '미열람'"
]
},
{
"id": 19,
"name": "열람 처리 - 문서 선택",
"action": "click_if_exists",
"target": "table tbody tr:first-child input[type='checkbox']",
"verification": [
"체크박스 선택 확인",
"'열람' 버튼 표시 확인"
]
},
{
"id": 20,
"name": "열람 처리 - 확인 다이얼로그",
"action": "click_if_exists",
"target": "button:has-text('열람')",
"verification": [
"확인 다이얼로그 표시",
"다이얼로그 제목: '열람 처리'",
"다이얼로그 메시지: '정말 1건을 열람 처리하시겠습니까?'",
"취소 버튼 표시",
"확인 버튼 표시"
]
},
{
"id": 21,
"name": "열람 처리 - URL 안정성 검증 (⚠️ 필수 검증 #2)",
"action": "save_url",
"verification": [
"⚠️ URL 변경 여부 확인 (변경되면 안됨)",
"⚠️ 에러 페이지 텍스트 검색 ('페이지를 찾을 수 없습니다', '404', 'Not Found' 등)",
"⚠️ 원래 페이지 요소 존재 확인 (테이블, 탭 등)",
"성공 토스트 메시지 표시: '열람 처리 완료'",
"토스트 설명: '열람 처리가 완료되었습니다.'",
"다이얼로그 자동 닫힘",
"체크박스 선택 해제",
"미열람 탭의 문서 개수 1개 감소"
],
"criticalCheck": {
"type": "URL_STABILITY",
"beforeURL": "/approval/reference",
"afterURL": "/approval/reference",
"errorPageTexts": [
"페이지를 찾을 수 없습니다",
"404",
"Not Found",
"서버 에러",
"500"
],
"successToast": "열람 처리 완료"
}
},
{
"id": 22,
"name": "열람 처리 후 데이터 검증",
"action": "verify_detail",
"verification": [
"미열람 탭에서 처리된 문서 제거 확인",
"통계 카드의 '미열람' 건수 1개 감소 (실시간 업데이트)",
"통계 카드의 '열람' 건수 1개 증가 (실시간 업데이트)"
]
},
{
"id": 23,
"name": "열람 탭으로 이동하여 검증",
"action": "click_if_exists",
"target": "button:has-text('열람')",
"verification": [
"열람 탭에 방금 처리한 문서 표시 확인",
"해당 문서의 상태 배지가 '열람'으로 변경"
]
},
{
"id": 24,
"name": "미열람 처리 - 문서 선택",
"action": "click_if_exists",
"target": "table tbody tr:first-child input[type='checkbox']",
"verification": [
"체크박스 선택 확인",
"'미열람' 버튼 표시 확인"
]
},
{
"id": 25,
"name": "미열람 처리 - 확인 다이얼로그",
"action": "click_if_exists",
"target": "button:has-text('미열람')",
"verification": [
"확인 다이얼로그 표시",
"다이얼로그 제목: '미열람 처리'",
"다이얼로그 메시지: '정말 1건을 미열람 처리하시겠습니까?'",
"취소 버튼 표시",
"확인 버튼 표시"
]
},
{
"id": 26,
"name": "미열람 처리 - URL 안정성 검증 (⚠️ 필수 검증 #2)",
"action": "save_url",
"verification": [
"⚠️ URL 변경 여부 확인 (변경되면 안됨)",
"⚠️ 에러 페이지 텍스트 검색",
"성공 토스트 메시지 표시: '미열람 처리 완료'",
"토스트 설명: '미열람 처리가 완료되었습니다.'",
"다이얼로그 자동 닫힘",
"체크박스 선택 해제",
"열람 탭의 문서 개수 1개 감소"
],
"criticalCheck": {
"type": "URL_STABILITY",
"beforeURL": "/approval/reference",
"afterURL": "/approval/reference",
"errorPageTexts": [
"페이지를 찾을 수 없습니다",
"404",
"Not Found",
"서버 에러",
"500"
],
"successToast": "미열람 처리 완료"
}
},
{
"id": 27,
"name": "미열람 처리 후 데이터 검증",
"action": "verify_detail",
"verification": [
"열람 탭에서 처리된 문서 제거 확인",
"통계 카드의 '열람' 건수 1개 감소 (실시간 업데이트)",
"통계 카드의 '미열람' 건수 1개 증가 (실시간 업데이트)"
]
},
{
"id": 28,
"name": "일괄 열람 처리 - 다중 선택",
"action": "click_if_exists",
"target": "button:has-text('미열람')",
"verification": [
"2개 문서 선택 확인",
"'열람' 버튼 표시 확인"
]
},
{
"id": 29,
"name": "일괄 열람 처리 - 실행",
"action": "click_if_exists",
"target": "button:has-text('열람')",
"verification": [
"다이얼로그 메시지: '정말 2건을 열람 처리하시겠습니까?'",
"확인 버튼 클릭",
"⚠️ URL 안정성 확인",
"성공 토스트 메시지 표시",
"미열람 탭에서 2개 문서 제거",
"통계 카드 업데이트 (미열람 -2, 열람 +2)"
]
},
{
"id": 30,
"name": "날짜 범위 선택기 테스트",
"action": "click_if_exists",
"target": "button:has-text('당월')",
"verification": [
"시작일과 종료일이 당월로 변경",
"데이터 재로드 확인 (로딩 인디케이터 또는 데이터 변화)"
]
},
{
"id": 31,
"name": "페이지네이션 테스트",
"action": "click_if_exists",
"target": "button:has-text('2')",
"verification": [
"페이지네이션 컨트롤 표시 확인",
"2페이지 버튼 클릭",
"페이지 번호 변경 확인",
"새로운 데이터 로드 확인"
],
"conditional": "문서 개수가 20개 이상인 경우에만 실행"
},
{
"id": 32,
"name": "Console 로그 확인",
"action": "verify_element",
"target": "body",
"verification": [
"ERROR 로그 없는지 확인",
"WARNING 로그 확인 (접근성 경고 등)",
"Network 요청 확인 (GET /api/v1/approvals/reference)"
]
},
{
"id": 33,
"name": "최종 통계 확인",
"action": "click_if_exists",
"target": "button:has-text('전체')",
"verification": [
"전체 문서 목록 표시",
"통계 카드 수치 정확성 확인",
"전체 = 열람 + 미열람 수식 성립",
"스크린샷 촬영하여 최종 상태 저장"
]
}
],
"mandatoryVerifications": [
{
"id": "VERIFICATION_2",
"name": "등록/저장 동작 검증 (URL 안정성)",
"appliesTo": [
"Step 21",
"Step 26",
"Step 29"
],
"requirements": [
"URL 변경 여부 확인 (처리 전 URL과 처리 후 URL 비교)",
"에러 페이지 텍스트 검색 ('페이지를 찾을 수 없습니다', '404', 'Not Found', '서버 에러', '500')",
"원래 페이지 요소 존재 확인 (테이블, 탭, 통계 카드 등)",
"성공 토스트 메시지 표시 확인",
"다이얼로그 자동 닫힘 확인"
]
},
{
"id": "VERIFICATION_5",
"name": "목업/미완성 페이지 감지",
"appliesTo": [
"Step 1",
"Step 16"
],
"requirements": [
"입력 필드 존재 여부 확인 (검색창 등)",
"동작하는 버튼 확인 (최소 2개 버튼 클릭 테스트)",
"API 호출 확인 (Console LOG만 아닌 실제 Network Request)",
"데이터 변경 가능 여부 확인 (열람/미열람 처리 등)",
"목업 판정 기준: 2개 이상 항목 해당 시 목업으로 판정"
]
}
],
"bugReportTemplate": {
"priority": "Critical/High/Medium/Low",
"component": "ReferenceBox",
"affectedArea": "react",
"relatedFiles": [
"C:\\Users\\codeb\\react\\src\\components\\approval\\ReferenceBox\\index.tsx",
"C:\\Users\\codeb\\react\\src\\components\\approval\\ReferenceBox\\actions.ts",
"C:\\Users\\codeb\\react\\src\\components\\approval\\ReferenceBox\\types.ts"
],
"changeApprovalPolicy": "✅ 즉시 가능 / ⚠️ 컨펌 필요 / 🔴 금지"
},
"testData": {
"searchKeyword": "김철수",
"dateRange": {
"startDate": "2025-01-01",
"endDate": "2026-01-31"
}
},
"notes": [
"참조함은 열람/미열람 상태 관리가 핵심 기능입니다.",
"결재함과 달리 승인/반려 기능은 없고, 열람/미열람 처리만 가능합니다.",
"통계 카드의 실시간 업데이트 확인이 중요합니다.",
"URL 안정성 검증(필수 검증 #2)을 모든 처리 동작에서 수행해야 합니다.",
"문서 상세 모달은 읽기 전용(mode='reference')으로 표시됩니다."
]
}

View File

@@ -0,0 +1,274 @@
{
"enabled": true,
"id": "sales-client",
"name": "판매거래처관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
},
"description": "판매관리 > 거래처관리 메뉴의 판매 거래처 CRUD 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "판매관리",
"level2": "거래처관리",
"expectedUrl": "/sales/client-management-sales-admin",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"testData": {
"create": {
"clientName": "E2E_TEST_판매처_{timestamp}",
"businessNumber": "987-65-43210",
"representative": "테스트 대표"
}
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 판매관리 > 거래처관리",
"action": "menu_navigate",
"level1": "판매관리",
"level2": "거래처관리",
"expected": {
"url_contains": "/sales/client",
"visible": [
"거래처관리",
"거래처"
]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"거래처 목록 표시",
"거래처 등록 버튼 존재",
"검색 기능 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "거래처 테이블 구조 확인",
"action": "verify_table",
"checks": [
"거래처명 컬럼",
"사업자번호 컬럼",
"대표자 컬럼",
"연락처 컬럼"
],
"expected": "거래처 테이블 표시"
},
{
"id": 4,
"phase": "CREATE",
"name": "[CREATE] 거래처 등록 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('등록'), button:has-text('추가'), button:has-text('신규')",
"expected": {
"modal_open": true
}
},
{
"id": 5,
"phase": "CREATE",
"name": "[CREATE] 거래처명 입력",
"action": "fill",
"target": "input[name*='name'], input[placeholder*='거래처명']",
"value": "E2E_TEST_판매처_{timestamp}",
"clear": true
},
{
"id": 6,
"phase": "CREATE",
"name": "[CREATE] 사업자번호 입력",
"action": "click_if_exists",
"target": "input[name*='business'], input[placeholder*='사업자']",
"value": "987-65-43210",
"clear": true
},
{
"id": 7,
"phase": "CREATE",
"name": "[CREATE] 대표자명 입력",
"action": "fill",
"target": "input[name*='representative'], input[placeholder*='대표']",
"value": "테스트 대표",
"clear": true
},
{
"id": 8,
"phase": "CREATE",
"name": "[CREATE] 필수 검증 #2: 거래처 저장",
"action": "click_if_exists",
"target": "button:has-text('저장'), button:has-text('등록'), button:has-text('확인')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "POST /api/v1/sales/clients",
"toast": "등록|저장|완료|성공"
},
"expected": "거래처 등록 완료"
},
{
"id": 9,
"phase": "READ",
"name": "[READ] 등록된 거래처 검색",
"action": "click_if_exists",
"target": "input[type='search'], input[placeholder*='검색']",
"value": "E2E_TEST_판매처",
"submit": true
},
{
"id": 10,
"phase": "READ",
"name": "[READ] 등록된 거래처 확인",
"action": "verify_detail",
"checks": [
"E2E_TEST_판매처 목록에 표시"
],
"expected": "등록된 거래처 확인"
},
{
"id": 11,
"phase": "READ",
"name": "[READ] 거래처 상세 조회",
"action": "click_if_exists",
"target": "table tbody tr:has-text('E2E_TEST')",
"expected": {
"detail_view": true
}
},
{
"id": 12,
"phase": "UPDATE",
"name": "[UPDATE] 거래처 수정 모드 진입",
"action": "click_if_exists",
"target": "button:has-text('수정'), button:has-text('편집')",
"expected": {
"edit_mode": true
}
},
{
"id": 13,
"phase": "UPDATE",
"name": "[UPDATE] 대표자명 수정",
"action": "fill",
"target": "input[name*='representative'], input[placeholder*='대표']",
"value": "수정된 대표",
"clear": true
},
{
"id": 14,
"phase": "UPDATE",
"name": "[UPDATE] 거래처 저장",
"action": "click_if_exists",
"target": "button:has-text('저장'), button:has-text('확인')",
"verify": {
"api_call": "PUT /api/v1/sales/clients",
"toast": "수정|저장|완료|성공"
},
"expected": "거래처 수정 완료"
},
{
"id": 15,
"phase": "DELETE",
"name": "[DELETE] 거래처 삭제",
"action": "click_if_exists",
"target": "button:has-text('삭제'), button:has-text('제거')",
"expected": {
"confirm_dialog": true
}
},
{
"id": 16,
"phase": "DELETE",
"name": "[DELETE] 삭제 확인",
"action": "click_if_exists",
"target": "[role='alertdialog'] button:has-text('확인'), [role='dialog'] button:has-text('삭제')",
"verify": {
"api_call": "DELETE /api/v1/sales/clients",
"toast": "삭제|제거|완료|성공"
},
"expected": "거래처 삭제 완료"
},
{
"id": 17,
"phase": "DELETE",
"name": "[DELETE] 삭제 확인",
"action": "verify_detail",
"checks": [
"E2E_TEST_판매처 목록에서 제거"
],
"expected": "거래처 삭제 반영"
},
{
"id": 18,
"name": "엑셀 다운로드 확인",
"action": "verify_elements",
"checks": [
"엑셀 다운로드 버튼 존재"
],
"expected": "엑셀 다운로드 기능 표시"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/sales/clients",
"description": "거래처 목록 조회"
},
{
"method": "POST",
"endpoint": "/api/v1/sales/clients",
"description": "거래처 등록"
},
{
"method": "PUT",
"endpoint": "/api/v1/sales/clients/:id",
"description": "거래처 수정"
},
{
"method": "DELETE",
"endpoint": "/api/v1/sales/clients/:id",
"description": "거래처 삭제"
}
],
"requiredVerifications": [
{
"id": 2,
"name": "저장 버튼",
"steps": [
8,
14
],
"criteria": "API 호출 + 성공 토스트 + 데이터 반영"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [
2
],
"criteria": "거래처 목록, 등록 버튼, 검색 기능 존재"
}
],
"rollbackPlan": {
"onCreateFail": "등록 모달 닫고 재시도",
"onUpdateFail": "페이지 새로고침 후 재시도",
"onDeleteFail": "수동 삭제 필요",
"cleanupRequired": "E2E_TEST_판매처_* 패턴 데이터 삭제"
}
}

View File

@@ -0,0 +1,726 @@
{
"enabled": true,
"id": "sales-management",
"name": "매출관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
},
"description": "회계관리 > 매출관리 메뉴의 매출등록, 계정과목 저장, 품목 동적 추가, 자동계산 로직 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "회계관리",
"level2": "매출관리",
"expectedUrl": "/ko/accounting/sales",
"searchWithinParent": true,
"closeOtherMenus": true
},
"navigation": {
"targetUrl": "/accounting/sales",
"urlPattern": "/accounting/sales|/ko/accounting/sales",
"menuHints": [
"매출관리",
"매출",
"회계관리"
]
},
"menuNavigationEnhanced": {
"strategy": "scroll-and-search",
"sidebarSelector": ".sidebar-scroll, [class*='sidebar'], nav[class*='menu']",
"scrollConfig": {
"scrollStep": 200,
"maxScrollAttempts": 10,
"scrollDelay": 300
},
"level1": {
"text": "회계관리",
"selectors": [
"//span[contains(text(),'회계관리')]",
"//div[contains(@class,'menu')]//span[text()='회계관리']",
"[data-menu='accounting']"
]
},
"level2": {
"text": "매출관리",
"selectors": [
"//a[contains(text(),'매출관리')]",
"//span[contains(text(),'매출관리')]",
"[href*='/accounting/sales']"
]
},
"fallbackUrl": "/ko/accounting/sales"
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 0,
"name": "사이드바 메뉴 전체 펼치기",
"description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비",
"actions": [
{
"type": "evaluate",
"script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})"
},
{
"type": "wait",
"duration": 300
},
{
"type": "evaluate",
"script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()"
},
{
"type": "wait",
"duration": 2000
}
],
"expected": "사이드바 전체 메뉴가 펼쳐짐"
},
{
"id": 1,
"name": "로그인",
"action": "click_if_exists",
"target": "form, [role=\"dialog\"], .modal",
"expected": "로그인 성공 후 메인 페이지 이동"
},
{
"id": 2,
"name": "2단계 메뉴 진입: 회계관리 > 매출관리",
"description": "스크롤하며 회계관리 메뉴를 찾아 클릭 후 매출관리 진입",
"navigationPattern": "scrollAndFind",
"actions": [
{
"type": "scrollAndFind",
"description": "사이드바 스크롤하며 회계관리 메뉴 찾기",
"scrollContainer": ".sidebar-scroll, [class*='sidebar']",
"targetText": "회계관리",
"scrollStep": 200,
"maxAttempts": 10
},
{
"type": "click_if_exists",
"target": "회계관리",
"selectors": [
"//span[contains(text(),'회계관리')]",
"//div[contains(@class,'menu')]//span[text()='회계관리']"
]
},
{
"type": "wait",
"duration": 500
},
{
"type": "scrollAndFind",
"description": "확장된 서브메뉴에서 매출관리 찾기",
"scrollContainer": ".sidebar-scroll, [class*='sidebar']",
"targetText": "매출관리",
"scrollStep": 100,
"maxAttempts": 5
},
{
"type": "click_if_exists",
"target": "매출관리",
"selectors": [
"//a[contains(text(),'매출관리')]",
"//span[contains(text(),'매출관리')]",
"[href*='/accounting/sales']"
]
},
{
"type": "wait",
"target": "페이지 로드 완료"
}
],
"fallback": {
"type": "navigate",
"url": "/ko/accounting/sales"
},
"expected": {
"url": "/ko/accounting/sales",
"pageTitle": "매출관리",
"elements": [
"매출 등록 버튼",
"테이블"
]
}
},
{
"id": 3,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"입력 필드 존재 (검색창, 날짜 선택)",
"동작하는 버튼 존재 (매출 등록, 엑셀 다운로드)",
"테이블 데이터 표시",
"API 호출 확인"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 4,
"name": "목록 페이지 - 테이블 구조 확인",
"action": "verify_table",
"checks": [
"체크박스 컬럼",
"No. 컬럼",
"매출번호 컬럼",
"거래처명 컬럼",
"매출일 컬럼",
"매출유형 컬럼",
"공급가액 컬럼",
"부가세 컬럼",
"합계금액 컬럼",
"계산서 컬럼",
"명세서 컬럼"
],
"expected": "테이블 컬럼 구조 정상 표시"
},
{
"id": 5,
"name": "계정과목명 드롭박스 확인",
"action": "verify_elements",
"checks": [
"계정과목명 라벨 존재",
"드롭박스(Select) 존재",
"저장 버튼 존재"
],
"expected": "계정과목 선택 UI 정상 표시"
},
{
"id": 6,
"name": "계정과목명 드롭박스 옵션 확인",
"action": "click_if_exists",
"target": "accountSubject",
"checks": [
"미설정 옵션",
"제품매출 옵션",
"상품매출 옵션",
"기타매출 옵션"
],
"expected": "계정과목 옵션 목록 표시"
},
{
"id": 7,
"name": "체크박스 선택 (계정과목 저장용)",
"action": "click_if_exists",
"target": "first_row",
"expected": "첫 번째 행 체크박스 선택됨"
},
{
"id": 8,
"name": "계정과목 변경 - 제품매출 선택",
"action": "click_if_exists",
"target": "accountSubject",
"value": "product",
"expected": "계정과목이 '제품매출'로 변경됨"
},
{
"id": 9,
"name": "필수 검증 #2: 계정과목 저장 버튼 클릭",
"action": "click_if_exists",
"target": "저장",
"checks": [
"확인 다이얼로그 표시",
"선택된 항목 수 표시",
"계정과목명 표시"
],
"expected": "저장 확인 다이얼로그 표시"
},
{
"id": 10,
"name": "저장 확인 다이얼로그 - 확인 클릭",
"action": "click_if_exists",
"checks": [
"API 호출 확인 (PUT /api/v1/sales/batch-update-account)",
"성공 토스트 메시지",
"URL 유지 확인"
],
"expected": "계정과목 저장 성공"
},
{
"id": "10-1",
"name": "⚠️ 필수 검증: 계정과목명 변경 데이터 반영 확인",
"action": "verify_data_update",
"target": "first_row",
"checks": [
"선택한 행의 매출유형 컬럼 값 확인",
"변경 전 값과 비교 (예: 기타 매출 → 제품 매출)",
"페이지 새로고침 후에도 변경된 값 유지 확인",
"API 응답과 UI 표시값 일치 확인"
],
"expected": "선택한 행의 매출유형이 '제품매출'로 실제 변경되어야 함",
"note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!"
},
{
"id": 11,
"name": "매출 등록 버튼 클릭",
"action": "click_if_exists",
"target": "매출 등록",
"expected": "매출 등록 페이지로 이동 (/ko/accounting/sales?mode=new)"
},
{
"id": 12,
"name": "매출 등록 페이지 - URL 확인",
"action": "verify_url",
"target": "/ko/accounting/sales?mode=new",
"expected": "매출 등록 페이지 URL 정상"
},
{
"id": 13,
"name": "매출 등록 페이지 - 기본정보 섹션 확인",
"action": "verify_elements",
"checks": [
"매출번호 필드 (자동생성, readonly)",
"매출일 필드 (DatePicker)",
"거래처명 드롭박스",
"매출유형 드롭박스"
],
"expected": "기본정보 섹션 정상 표시"
},
{
"id": 14,
"name": "매출번호 자동생성 확인",
"action": "verify_field",
"target": "salesNo",
"checks": [
"값이 자동 생성됨 (예: S-2026-0001)",
"readonly 상태"
],
"expected": "매출번호 자동생성 확인"
},
{
"id": 15,
"name": "거래처명 드롭박스 클릭",
"action": "click_if_exists",
"target": "vendorId",
"expected": "거래처 목록 표시"
},
{
"id": 16,
"name": "거래처명 선택",
"action": "click_if_exists",
"target": "vendorId",
"value": "first_available",
"expected": "거래처가 선택됨"
},
{
"id": 17,
"name": "매출유형 드롭박스 확인",
"action": "click_if_exists",
"target": "salesType",
"checks": [
"외상매출 옵션",
"제품매출 옵션",
"상품매출 옵션",
"부품매출 옵션",
"공사매출 옵션",
"임대매출 옵션",
"기타매출 옵션"
],
"expected": "매출유형 옵션 목록 표시"
},
{
"id": 18,
"name": "매출유형 선택 - 제품매출",
"action": "click_if_exists",
"target": "salesType",
"value": "product",
"expected": "매출유형이 '제품매출'로 선택됨"
},
{
"id": 19,
"name": "품목정보 섹션 확인",
"action": "verify_elements",
"checks": [
"품목 정보 제목",
"품목 추가 버튼 (+)",
"품목 테이블 헤더 (품목명, 수량, 단가, 공급가액, 부가세, 적요)",
"기본 품목 행 1개 존재"
],
"expected": "품목정보 섹션 정상 표시"
},
{
"id": 20,
"name": "품목 동적 추가 - 추가 버튼 클릭",
"action": "click_if_exists",
"target": "품목 추가",
"expected": "새로운 품목 행 추가됨"
},
{
"id": 21,
"name": "품목 행 개수 확인 (2개)",
"action": "verify_row_count",
"target": "items_table",
"expected": "품목 행이 2개로 증가"
},
{
"id": 22,
"name": "품목 동적 삭제 - 두 번째 행 삭제",
"action": "click_if_exists",
"target": "remove_item_row_2",
"expected": "두 번째 품목 행 삭제됨"
},
{
"id": 23,
"name": "품목 행 개수 확인 (1개)",
"action": "verify_row_count",
"target": "items_table",
"expected": "품목 행이 1개로 감소"
},
{
"id": 24,
"name": "품목명 입력",
"action": "click_if_exists",
"target": "items[0].itemName",
"value": "테스트 품목",
"expected": "품목명 입력됨"
},
{
"id": 25,
"name": "수량 입력",
"action": "click_if_exists",
"target": "items[0].quantity",
"value": "10",
"expected": "수량 입력됨"
},
{
"id": 26,
"name": "단가 입력",
"action": "click_if_exists",
"target": "items[0].unitPrice",
"value": "50000",
"expected": "단가 입력됨"
},
{
"id": 27,
"name": "자동계산 검증 - 공급가액",
"action": "verify_calculated_value",
"target": "items[0].supplyAmount",
"formula": "quantity * unitPrice",
"expectedValue": "500000",
"checks": [
"수량(10) × 단가(50000) = 공급가액(500,000)",
"자동으로 계산되어 표시됨"
],
"expected": "공급가액이 500,000원으로 자동 계산됨"
},
{
"id": 28,
"name": "자동계산 검증 - 부가세",
"action": "verify_calculated_value",
"target": "items[0].vat",
"formula": "supplyAmount * 0.1",
"expectedValue": "50000",
"checks": [
"공급가액(500,000) × 10% = 부가세(50,000)",
"자동으로 계산되어 표시됨"
],
"expected": "부가세가 50,000원으로 자동 계산됨"
},
{
"id": 29,
"name": "적요 입력 (선택사항)",
"action": "click_if_exists",
"target": "items[0].note",
"value": "테스트 적요",
"expected": "적요 입력됨"
},
{
"id": 30,
"name": "세금계산서 발행 Switch 확인",
"action": "verify_elements",
"target": "taxInvoice_section",
"checks": [
"세금계산서 발행 라벨",
"Switch 버튼 존재",
"기본값 OFF 상태"
],
"expected": "세금계산서 발행 Switch 정상 표시"
},
{
"id": 31,
"name": "세금계산서 발행 Switch ON",
"action": "click_if_exists",
"target": "taxInvoiceSwitch",
"value": "on",
"expected": "세금계산서 발행 Switch가 ON으로 변경됨"
},
{
"id": 32,
"name": "세금계산서 발행 Switch OFF",
"action": "click_if_exists",
"target": "taxInvoiceSwitch",
"value": "off",
"expected": "세금계산서 발행 Switch가 OFF로 변경됨"
},
{
"id": 33,
"name": "거래명세서 발행 Switch 확인",
"action": "verify_elements",
"target": "transactionStatement_section",
"checks": [
"거래명세서 발행 라벨",
"Switch 버튼 존재",
"기본값 OFF 상태"
],
"expected": "거래명세서 발행 Switch 정상 표시"
},
{
"id": 34,
"name": "거래명세서 발행 Switch ON",
"action": "click_if_exists",
"target": "transactionStatementSwitch",
"value": "on",
"expected": "거래명세서 발행 Switch가 ON으로 변경됨"
},
{
"id": 35,
"name": "거래명세서 발행 Switch OFF",
"action": "click_if_exists",
"target": "transactionStatementSwitch",
"value": "off",
"expected": "거래명세서 발행 Switch가 OFF로 변경됨"
},
{
"id": 36,
"name": "합계 금액 확인",
"action": "verify_totals",
"checks": [
"총 공급가액: 500,000원",
"총 부가세: 50,000원",
"총 합계금액: 550,000원"
],
"expected": "합계 금액 정상 표시"
},
{
"id": 37,
"name": "취소 버튼 동작 테스트",
"action": "click_if_exists",
"target": "취소",
"expected": "취소 확인 다이얼로그 또는 목록 페이지로 이동"
},
{
"id": 38,
"name": "취소 확인 - 목록 페이지 복귀",
"action": "verify_url",
"target": "/ko/accounting/sales",
"expected": "매출 목록 페이지로 복귀"
},
{
"id": 39,
"name": "다시 매출 등록 페이지 진입",
"action": "click_if_exists",
"target": "매출 등록",
"expected": "매출 등록 페이지로 이동"
},
{
"id": 40,
"name": "등록 테스트용 데이터 입력 - 거래처 선택",
"action": "click_if_exists",
"target": "vendorId",
"value": "first_available",
"expected": "거래처 선택됨"
},
{
"id": 41,
"name": "등록 테스트용 데이터 입력 - 매출유형",
"action": "click_if_exists",
"target": "salesType",
"value": "product",
"expected": "매출유형 선택됨"
},
{
"id": 42,
"name": "등록 테스트용 데이터 입력 - 품목명",
"action": "click_if_exists",
"target": "items[0].itemName",
"value": "E2E 테스트 품목",
"expected": "품목명 입력됨"
},
{
"id": 43,
"name": "등록 테스트용 데이터 입력 - 수량",
"action": "click_if_exists",
"target": "items[0].quantity",
"value": "5",
"expected": "수량 입력됨"
},
{
"id": 44,
"name": "등록 테스트용 데이터 입력 - 단가",
"action": "click_if_exists",
"target": "items[0].unitPrice",
"value": "100000",
"expected": "단가 입력됨"
},
{
"id": 45,
"name": "필수 검증 #2: 등록 버튼 클릭",
"action": "click_if_exists",
"target": "등록",
"checks": [
"버튼 클릭 전 URL 저장",
"API 호출 확인 (POST /api/v1/sales)",
"에러 페이지 감지",
"성공 토스트 메시지 확인"
],
"expected": "매출 등록 완료"
},
{
"id": 46,
"name": "등록 성공 확인 - 토스트 메시지",
"action": "verify_toast",
"target": "매출이 등록되었습니다",
"expected": "성공 토스트 메시지 표시"
},
{
"id": 47,
"name": "등록 성공 확인 - 목록 페이지 이동",
"action": "verify_url",
"target": "/ko/accounting/sales",
"expected": "매출 목록 페이지로 이동"
},
{
"id": 48,
"name": "등록된 매출 목록 확인",
"action": "verify_table_data",
"checks": [
"신규 등록된 매출이 목록에 표시됨",
"품목명: E2E 테스트 품목",
"공급가액: 500,000원"
],
"expected": "등록된 매출이 목록에 표시됨"
},
{
"id": 49,
"name": "거래처 미선택 시 유효성 검증 테스트",
"action": "navigate",
"target": "/ko/accounting/sales?mode=new",
"expected": "매출 등록 페이지 이동"
},
{
"id": 50,
"name": "거래처 미선택 상태에서 등록 시도",
"action": "click_if_exists",
"target": "등록",
"expected": "유효성 검증 실패 - 경고 메시지"
},
{
"id": 51,
"name": "유효성 검증 메시지 확인",
"action": "verify_toast",
"target": "거래처를 선택해주세요",
"type": "warning",
"expected": "거래처 선택 요청 경고 메시지 표시"
}
],
"requiredVerifications": [
{
"id": 1,
"name": "파일 다운로드 (엑셀)",
"steps": [],
"criteria": "이 시나리오에서는 테스트하지 않음 (별도 시나리오)"
},
{
"id": 2,
"name": "등록/저장 버튼",
"steps": [
9,
10,
45,
46,
47
],
"criteria": "계정과목 저장 + 매출 등록 시 API 호출 + 성공 토스트 + URL 유지/이동 확인"
},
{
"id": 3,
"name": "검색/필터",
"steps": [],
"criteria": "이 시나리오에서는 테스트하지 않음 (등록 중심 테스트)"
},
{
"id": 4,
"name": "모달 등록 완료",
"steps": [
9,
10
],
"criteria": "계정과목 저장 확인 다이얼로그 → 확인 클릭 → 저장 완료"
},
{
"id": 6,
"name": "⚠️ 계정과목명 변경 데이터 반영 (필수)",
"steps": [
"10-1"
],
"criteria": "저장 후 실제 테이블 데이터가 변경되었는지 확인. 토스트만 확인하면 불충분!",
"priority": "critical",
"knownBug": {
"bugId": "BUG-SALES-20260115-001",
"status": "OPEN",
"description": "성공 토스트 표시되나 실제 데이터 미변경"
}
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [
3
],
"criteria": "입력 필드, 동작 버튼, API 호출 확인"
}
],
"testData": {
"vendor": "첫 번째 사용 가능한 거래처",
"salesType": "product",
"item": {
"itemName": "E2E 테스트 품목",
"quantity": 5,
"unitPrice": 100000
},
"calculatedValues": {
"supplyAmount": 500000,
"vat": 50000,
"totalAmount": 550000
}
},
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/sales",
"description": "매출 목록 조회"
},
{
"method": "GET",
"endpoint": "/api/v1/clients",
"description": "거래처 목록 조회 (드롭박스용)"
},
{
"method": "POST",
"endpoint": "/api/v1/sales",
"description": "매출 등록"
},
{
"method": "PUT",
"endpoint": "/api/v1/sales/batch-update-account",
"description": "계정과목 일괄 변경"
}
],
"skipTests": [
{
"feature": "삭제 기능",
"reason": "사용자 요청에 따라 삭제 테스트 제외"
}
]
}

View File

@@ -0,0 +1,372 @@
{
"enabled": true,
"id": "sales-order",
"name": "수주관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
},
"description": "판매관리 > 수주관리 메뉴의 수주 조회/등록/수정/삭제 전체 CRUD 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "판매관리",
"level2": "수주관리",
"expectedUrl": "/sales/order-management-sales",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"testData": {
"create": {
"clientName": "E2E_TEST_거래처",
"itemName": "테스트품목",
"quantity": "200",
"unitPrice": "15000",
"deliveryDate": "2026-02-15",
"memo": "E2E 자동화 테스트 수주"
},
"update": {
"quantity": "250",
"memo": "E2E 수정된 수주 메모"
}
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 판매관리 > 수주관리",
"action": "menu_navigate",
"level1": "판매관리",
"level2": "수주관리",
"expected": {
"url_contains": "/sales/order",
"visible": [
"수주관리",
"수주"
]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"수주 목록 표시",
"수주 등록 버튼 존재",
"검색/필터 기능 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "수주 테이블 구조 확인",
"action": "verify_table",
"checks": [
"수주번호 컬럼",
"거래처 컬럼",
"품목 컬럼",
"수량 컬럼",
"금액 컬럼",
"상태 컬럼"
],
"expected": "수주 테이블 컬럼 정상 표시"
},
{
"id": 4,
"name": "검색 기능 테스트",
"action": "click_if_exists",
"target": "input[placeholder*='검색']",
"value": "테스트",
"expected": {
"data_filtered": true
}
},
{
"id": 5,
"phase": "CREATE",
"name": "[CREATE] 수주 등록 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('등록'), button:has-text('수주 등록'), button:has-text('추가')",
"expected": {
"modal_or_page": true,
"title": "수주 등록"
}
},
{
"id": 6,
"phase": "CREATE",
"name": "[CREATE] 수주 정보 입력",
"action": "fill_form",
"fields": [
{
"name": "거래처",
"type": "select",
"value": "E2E_TEST_거래처"
},
{
"name": "수주일",
"type": "date",
"value": "2026-02-03"
},
{
"name": "품목",
"type": "select",
"value": "테스트품목"
},
{
"name": "수량",
"type": "number",
"value": "200"
},
{
"name": "단가",
"type": "number",
"value": "15000"
},
{
"name": "납기일",
"type": "date",
"value": "2026-02-15"
},
{
"name": "메모",
"type": "text",
"value": "E2E 자동화 테스트 수주_{timestamp}"
}
],
"note": "타임스탬프로 고유성 보장"
},
{
"id": 7,
"phase": "CREATE",
"name": "[CREATE] 필수 검증 #2: 등록 저장",
"action": "click_if_exists",
"target": "button:has-text('저장'), button:has-text('등록')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "POST /api/v1/sales-orders",
"toast": "등록|완료|성공"
},
"expected": "수주 등록 완료"
},
{
"id": "7-modal-close",
"phase": "CREATE",
"name": "[CREATE] 모달 닫기 확인",
"action": "close_modal_if_open",
"expected": "모달 닫힘"
},
{
"id": 8,
"phase": "CREATE",
"name": "[CREATE] 등록 결과 확인",
"action": "verify_detail",
"search": "E2E 자동화 테스트 수주",
"expected": {
"row_exists": true,
"contains": [
"E2E",
"200",
"3,000,000"
]
}
},
{
"id": 9,
"phase": "READ",
"name": "[READ] 수주 상세 페이지 진입",
"action": "click_if_exists",
"target": "table tbody tr:has-text('E2E')",
"expected": {
"url_contains": "/sales/order",
"visible": [
"수주 상세",
"수정",
"삭제"
]
}
},
{
"id": 10,
"phase": "READ",
"name": "[READ] 상세 정보 확인",
"action": "verify_detail",
"checks": [
"거래처: E2E_TEST_거래처",
"수량: 200",
"금액: 3,000,000",
"납기일: 2026-02-15"
],
"expected": "입력한 데이터와 일치"
},
{
"id": 11,
"phase": "UPDATE",
"name": "[UPDATE] 수정 모드 진입",
"action": "click_if_exists",
"target": "button:has-text('수정')",
"expected": {
"url_contains": "mode=edit",
"fields_editable": true
}
},
{
"id": 12,
"phase": "UPDATE",
"name": "[UPDATE] 수량 수정",
"action": "click_if_exists",
"target": "input[name*='quantity'], input[placeholder*='수량']",
"value": "250",
"clear": true
},
{
"id": 13,
"phase": "UPDATE",
"name": "[UPDATE] 메모 수정",
"action": "click_if_exists",
"target": "textarea[name*='memo'], input[placeholder*='메모']",
"value": "E2E 수정된 수주 메모_{timestamp}",
"clear": true
},
{
"id": 14,
"phase": "UPDATE",
"name": "[UPDATE] 필수 검증 #2: 수정 저장",
"action": "click_if_exists",
"target": "button:has-text('저장')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "PUT /api/v1/sales-orders/",
"toast": "수정|완료|성공"
},
"expected": "수정 완료"
},
{
"id": 15,
"phase": "UPDATE",
"name": "[UPDATE] 수정 결과 확인",
"action": "verify_detail",
"checks": [
"수량: 250",
"금액: 3,750,000"
],
"expected": "수정된 데이터 반영"
},
{
"id": 16,
"phase": "DELETE",
"name": "[DELETE] 삭제 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('삭제')",
"expected": {
"confirm_dialog": true,
"dialog_message": "삭제|정말"
}
},
{
"id": 17,
"phase": "DELETE",
"name": "[DELETE] 필수 검증 #6: 삭제 확인",
"action": "click_if_exists",
"target": "button:has-text('확인'), button:has-text('삭제')",
"verify": {
"api_call": "DELETE /api/v1/sales-orders/",
"toast": "삭제|완료|성공",
"redirect": "/sales/order"
},
"expected": "삭제 완료 및 목록 복귀"
},
{
"id": 18,
"phase": "DELETE",
"name": "[DELETE] 삭제 결과 확인",
"action": "verify_detail",
"search": "E2E 수정된 수주",
"expected": {
"row_exists": false,
"message": "테스트 수주가 목록에서 제거됨"
}
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/sales-orders",
"description": "수주 목록 조회"
},
{
"method": "POST",
"endpoint": "/api/v1/sales-orders",
"description": "수주 등록"
},
{
"method": "GET",
"endpoint": "/api/v1/sales-orders/{id}",
"description": "수주 상세 조회"
},
{
"method": "PUT",
"endpoint": "/api/v1/sales-orders/{id}",
"description": "수주 수정"
},
{
"method": "DELETE",
"endpoint": "/api/v1/sales-orders/{id}",
"description": "수주 삭제"
}
],
"requiredVerifications": [
{
"id": 2,
"name": "등록/저장 버튼",
"steps": [
7,
14
],
"criteria": "API 호출 + 성공 토스트 + 데이터 반영"
},
{
"id": 3,
"name": "검색/필터",
"steps": [
4
],
"criteria": "검색 기능 동작"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [
2
],
"criteria": "수주 목록, 등록 버튼, 필터 존재"
},
{
"id": 6,
"name": "삭제 기능",
"steps": [
16,
17,
18
],
"criteria": "DELETE API + 목록에서 제거"
}
],
"rollbackPlan": {
"onCreateFail": "모달 닫기",
"onUpdateFail": "테스트 수주 수동 삭제 필요",
"onDeleteFail": "테스트 수주 수동 삭제 필요",
"cleanupRequired": "E2E_TEST_ 접두사 수주는 테스트 데이터"
}
}

View File

@@ -0,0 +1,154 @@
{
"enabled": true,
"id": "sales-pricing",
"name": "단가관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "판매관리 > 단가관리 메뉴의 단가 목록 조회 및 UI 검증 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "판매관리",
"level2": "단가관리",
"expectedUrl": "/sales/pricing-management",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 판매관리 > 단가관리",
"action": "menu_navigate",
"level1": "판매관리",
"level2": "단가관리",
"expected": {
"url_contains": "/sales/pricing",
"visible": ["단가관리", "단가"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"단가 목록 표시",
"단가 등록 버튼 존재",
"검색 기능 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "단가 테이블 구조 확인",
"action": "verify_table",
"checks": [
"품목 컬럼",
"거래처 컬럼",
"단가 컬럼",
"적용일 컬럼"
],
"expected": "단가 테이블 표시"
},
{
"id": 4,
"name": "단가 UI 요소 확인",
"action": "verify_elements",
"checks": [
"등록 버튼 존재",
"검색 입력 필드",
"단가 목록"
],
"expected": "단가 UI 정상 표시"
},
{
"id": 5,
"phase": "READ",
"name": "[READ] 단가 목록 확인",
"action": "verify_detail",
"checks": [
"단가 목록 데이터 표시됨"
],
"expected": "단가 목록 정상"
},
{
"id": 6,
"phase": "READ",
"name": "[READ] 첫 번째 단가 클릭",
"action": "click_if_exists",
"target": "table tbody tr:first-child"
},
{
"id": 7,
"phase": "READ",
"name": "[READ] 단가 상세 정보 확인",
"action": "verify_detail",
"checks": [
"단가 상세 정보 표시"
],
"expected": "단가 상세 정보 확인"
},
{
"id": 8,
"name": "상세 모달/페이지 닫기",
"action": "click_if_exists",
"target": "button:has-text('닫기'), button:has-text('목록'), button:has-text('Close')"
},
{
"id": 9,
"name": "모달 닫기 확인",
"action": "close_modal_if_open",
"expected": "모달 닫힘"
},
{
"id": 10,
"name": "목록 복귀 확인",
"action": "verify_table",
"checks": [
"단가 목록 표시"
],
"expected": "목록 복귀 확인"
},
{
"id": 11,
"name": "엑셀 다운로드 버튼 확인",
"action": "verify_elements",
"checks": [
"엑셀 다운로드 버튼 존재 여부"
],
"expected": "엑셀 다운로드 기능 확인"
},
{
"id": 12,
"name": "단가관리 페이지 최종 확인",
"action": "verify_elements",
"checks": [
"단가 목록 구조 정상",
"등록 버튼 존재"
],
"expected": "단가관리 페이지 정상"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/sales/pricing",
"description": "단가 목록 조회"
}
],
"requiredVerifications": [
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "단가 목록, 등록 버튼, 검색 기능 존재"
}
],
"rollbackPlan": {
"note": "READ-only 패턴으로 안정성 우선"
}
}

View File

@@ -0,0 +1,364 @@
{
"enabled": true,
"id": "sales-quotation",
"name": "견적관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
},
"description": "판매관리 > 견적관리 메뉴의 견적서 조회/등록/수정/삭제 전체 CRUD 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "판매관리",
"level2": "견적관리",
"expectedUrl": "/sales/quote-management",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"testData": {
"create": {
"clientName": "E2E_TEST_거래처",
"itemName": "테스트품목",
"quantity": "100",
"unitPrice": "10000",
"memo": "E2E 자동화 테스트 견적"
},
"update": {
"quantity": "150",
"memo": "E2E 수정된 견적 메모"
}
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 판매관리 > 견적관리",
"action": "menu_navigate",
"level1": "판매관리",
"level2": "견적관리",
"expected": {
"url_contains": "/sales/quote",
"visible": [
"견적관리",
"견적"
]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"견적 목록 표시",
"견적 등록 버튼 존재",
"검색/필터 기능 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "견적 테이블 구조 확인",
"action": "verify_table",
"checks": [
"견적번호 컬럼",
"거래처 컬럼",
"품목 컬럼",
"금액 컬럼",
"상태 컬럼",
"작성일 컬럼"
],
"expected": "견적 테이블 컬럼 정상 표시"
},
{
"id": 4,
"name": "검색 기능 테스트",
"action": "click_if_exists",
"target": "input[placeholder*='검색']",
"value": "테스트",
"expected": {
"data_filtered": true
}
},
{
"id": 5,
"phase": "CREATE",
"name": "[CREATE] 견적 등록 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('등록'), button:has-text('견적 등록'), button:has-text('추가')",
"expected": {
"modal_or_page": true,
"title": "견적 등록"
}
},
{
"id": 6,
"phase": "CREATE",
"name": "[CREATE] 견적 정보 입력",
"action": "fill_form",
"fields": [
{
"name": "거래처",
"type": "select",
"value": "E2E_TEST_거래처"
},
{
"name": "견적일",
"type": "date",
"value": "2026-02-03"
},
{
"name": "품목",
"type": "select",
"value": "테스트품목"
},
{
"name": "수량",
"type": "number",
"value": "100"
},
{
"name": "단가",
"type": "number",
"value": "10000"
},
{
"name": "메모",
"type": "text",
"value": "E2E 자동화 테스트 견적_{timestamp}"
}
],
"note": "타임스탬프로 고유성 보장"
},
{
"id": 7,
"phase": "CREATE",
"name": "[CREATE] 필수 검증 #2: 등록 저장",
"action": "click_if_exists",
"target": "button:has-text('저장'), button:has-text('등록')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "POST /api/v1/quotations",
"toast": "등록|완료|성공"
},
"expected": "견적 등록 완료"
},
{
"id": "7-modal-close",
"phase": "CREATE",
"name": "[CREATE] 모달 닫기 확인",
"action": "close_modal_if_open",
"expected": "모달 닫힘"
},
{
"id": 8,
"phase": "CREATE",
"name": "[CREATE] 등록 결과 확인",
"action": "verify_detail",
"search": "E2E 자동화 테스트 견적",
"expected": {
"row_exists": true,
"contains": [
"E2E",
"1,000,000"
]
}
},
{
"id": 9,
"phase": "READ",
"name": "[READ] 견적 상세 페이지 진입",
"action": "click_if_exists",
"target": "table tbody tr:has-text('E2E')",
"expected": {
"url_contains": "/sales/quote",
"visible": [
"견적 상세",
"수정",
"삭제"
]
}
},
{
"id": 10,
"phase": "READ",
"name": "[READ] 상세 정보 확인",
"action": "verify_detail",
"checks": [
"거래처: E2E_TEST_거래처",
"수량: 100",
"금액: 1,000,000"
],
"expected": "입력한 데이터와 일치"
},
{
"id": 11,
"phase": "UPDATE",
"name": "[UPDATE] 수정 모드 진입",
"action": "click_if_exists",
"target": "button:has-text('수정')",
"expected": {
"url_contains": "mode=edit",
"fields_editable": true
}
},
{
"id": 12,
"phase": "UPDATE",
"name": "[UPDATE] 수량 수정",
"action": "click_if_exists",
"target": "input[name*='quantity'], input[placeholder*='수량']",
"value": "150",
"clear": true
},
{
"id": 13,
"phase": "UPDATE",
"name": "[UPDATE] 메모 수정",
"action": "click_if_exists",
"target": "textarea[name*='memo'], input[placeholder*='메모']",
"value": "E2E 수정된 견적 메모_{timestamp}",
"clear": true
},
{
"id": 14,
"phase": "UPDATE",
"name": "[UPDATE] 필수 검증 #2: 수정 저장",
"action": "click_if_exists",
"target": "button:has-text('저장')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "PUT /api/v1/quotations/",
"toast": "수정|완료|성공"
},
"expected": "수정 완료"
},
{
"id": 15,
"phase": "UPDATE",
"name": "[UPDATE] 수정 결과 확인",
"action": "verify_detail",
"checks": [
"수량: 150",
"금액: 1,500,000"
],
"expected": "수정된 데이터 반영"
},
{
"id": 16,
"phase": "DELETE",
"name": "[DELETE] 삭제 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('삭제')",
"expected": {
"confirm_dialog": true,
"dialog_message": "삭제|정말"
}
},
{
"id": 17,
"phase": "DELETE",
"name": "[DELETE] 필수 검증 #6: 삭제 확인",
"action": "click_if_exists",
"target": "button:has-text('확인'), button:has-text('삭제')",
"verify": {
"api_call": "DELETE /api/v1/quotations/",
"toast": "삭제|완료|성공",
"redirect": "/sales/quote"
},
"expected": "삭제 완료 및 목록 복귀"
},
{
"id": 18,
"phase": "DELETE",
"name": "[DELETE] 삭제 결과 확인",
"action": "verify_detail",
"search": "E2E 수정된 견적",
"expected": {
"row_exists": false,
"message": "테스트 견적이 목록에서 제거됨"
}
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/quotations",
"description": "견적 목록 조회"
},
{
"method": "POST",
"endpoint": "/api/v1/quotations",
"description": "견적 등록"
},
{
"method": "GET",
"endpoint": "/api/v1/quotations/{id}",
"description": "견적 상세 조회"
},
{
"method": "PUT",
"endpoint": "/api/v1/quotations/{id}",
"description": "견적 수정"
},
{
"method": "DELETE",
"endpoint": "/api/v1/quotations/{id}",
"description": "견적 삭제"
}
],
"requiredVerifications": [
{
"id": 2,
"name": "등록/저장 버튼",
"steps": [
7,
14
],
"criteria": "API 호출 + 성공 토스트 + 데이터 반영"
},
{
"id": 3,
"name": "검색/필터",
"steps": [
4
],
"criteria": "검색 기능 동작"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [
2
],
"criteria": "견적 목록, 등록 버튼, 필터 존재"
},
{
"id": 6,
"name": "삭제 기능",
"steps": [
16,
17,
18
],
"criteria": "DELETE API + 목록에서 제거"
}
],
"rollbackPlan": {
"onCreateFail": "모달 닫기",
"onUpdateFail": "테스트 견적 수동 삭제 필요",
"onDeleteFail": "테스트 견적 수동 삭제 필요",
"cleanupRequired": "E2E_TEST_ 접두사 견적은 테스트 데이터"
}
}

View File

@@ -0,0 +1,157 @@
{
"id": "settings-account",
"name": "계정정보 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "설정 > 계정정보 메뉴의 계정 정보 조회 및 UI 검증 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "설정",
"level2": "계정정보",
"expectedUrl": "/settings/account-info",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 설정 > 계정정보",
"action": "menu_navigate",
"level1": "설정",
"level2": "계정정보",
"expected": {
"url_contains": "/settings/account",
"visible": ["계정정보", "프로필"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"계정 정보 폼 표시",
"프로필 이미지 영역 존재",
"수정 버튼 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "계정 정보 폼 구조 확인",
"action": "verify_elements",
"checks": [
"사용자명 표시",
"이메일 표시",
"연락처 표시",
"프로필 이미지 영역"
],
"expected": "계정 정보 폼 정상 표시"
},
{
"id": 4,
"phase": "READ",
"name": "[READ] 현재 계정 정보 확인",
"action": "verify_detail",
"checks": [
"사용자명 표시됨",
"이메일 표시됨",
"연락처 표시됨"
],
"expected": "현재 계정 정보 정상 표시"
},
{
"id": 5,
"phase": "UPDATE",
"name": "[UPDATE] 프로필 수정 모드 진입",
"action": "click_if_exists",
"target": "button:has-text('수정'), button:has-text('편집')",
"expected": {
"edit_mode": true,
"fields_editable": true
}
},
{
"id": 6,
"phase": "UPDATE",
"name": "[UPDATE] 표시 이름 필드 확인",
"action": "click_if_exists",
"target": "input[name*='displayName'], input[name*='name'], input[placeholder*='이름']"
},
{
"id": 7,
"phase": "UPDATE",
"name": "[UPDATE] 연락처 필드 확인",
"action": "click_if_exists",
"target": "input[name*='phone'], input[type='tel']"
},
{
"id": 8,
"phase": "UPDATE",
"name": "[UPDATE] 필수 검증 #2: 프로필 저장",
"action": "click_if_exists",
"target": "button:has-text('저장'), button:has-text('확인')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"toast": "저장|수정|완료|성공"
},
"expected": "프로필 저장 완료"
},
{
"id": 9,
"phase": "UPDATE",
"name": "[UPDATE] 저장 결과 확인",
"action": "verify_detail",
"checks": [
"프로필 정보 표시됨"
],
"expected": "프로필 정보 표시"
},
{
"id": 10,
"name": "비밀번호 변경 버튼 확인",
"action": "verify_elements",
"checks": [
"비밀번호 변경 버튼 존재"
],
"expected": "비밀번호 변경 버튼 표시"
},
{
"id": 11,
"name": "비밀번호 변경 모달 열기",
"action": "click_if_exists",
"target": "button:has-text('비밀번호 변경'), button:has-text('비밀번호')"
},
{
"id": 12,
"name": "비밀번호 변경 모달 닫기",
"action": "close_modal_if_open",
"expected": "모달 닫힘"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/users/profile",
"description": "프로필 정보 조회"
}
],
"requiredVerifications": [
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "계정 정보 폼, 수정 버튼 존재"
}
],
"rollbackPlan": {
"onUpdateFail": "페이지 새로고침으로 원래 값 복원",
"note": "READ-only 패턴으로 안정성 우선"
}
}

View File

@@ -0,0 +1,161 @@
{
"id": "settings-attendance",
"name": "근태설정 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "설정 > 근태설정 메뉴의 근태 정책 조회 및 UI 검증 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "설정",
"level2": "근태설정",
"expectedUrl": "/settings/attendance-settings",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 설정 > 근태설정",
"action": "menu_navigate",
"level1": "설정",
"level2": "근태설정",
"expected": {
"url_contains": "/settings/attendance",
"visible": ["근태설정", "근태"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"근태 설정 폼 표시",
"출퇴근 시간 설정 가능",
"저장 버튼 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "근태 설정 폼 구조 확인",
"action": "verify_elements",
"checks": [
"지각 기준 시간 설정",
"조퇴 기준 시간 설정",
"자동 퇴근 처리 설정",
"출퇴근 인정 범위"
],
"expected": "근태 설정 폼 정상 표시"
},
{
"id": 4,
"phase": "READ",
"name": "[READ] 현재 근태 설정 확인",
"action": "verify_detail",
"checks": [
"지각 기준 표시",
"조퇴 기준 표시",
"자동 퇴근 설정 표시"
],
"expected": "현재 근태 설정 정상 표시"
},
{
"id": 5,
"phase": "UPDATE",
"name": "[UPDATE] 지각 기준 필드 확인",
"action": "click_if_exists",
"target": "input[name*='late'], input[placeholder*='지각']"
},
{
"id": 6,
"phase": "UPDATE",
"name": "[UPDATE] 조퇴 기준 필드 확인",
"action": "click_if_exists",
"target": "input[name*='early'], input[placeholder*='조퇴']"
},
{
"id": 7,
"phase": "UPDATE",
"name": "[UPDATE] 자동 퇴근 시간 필드 확인",
"action": "click_if_exists",
"target": "input[name*='autoCheckout'], input[type='time']"
},
{
"id": 8,
"phase": "UPDATE",
"name": "[UPDATE] 필수 검증 #2: 근태 설정 저장",
"action": "click_if_exists",
"target": "button:has-text('저장'), button:has-text('적용')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"toast": "저장|적용|완료|성공"
},
"expected": "근태 설정 저장 완료"
},
{
"id": 9,
"phase": "UPDATE",
"name": "[UPDATE] 저장 결과 확인",
"action": "verify_detail",
"checks": [
"근태 설정 정보 표시됨"
],
"expected": "설정 정보 표시"
},
{
"id": 10,
"name": "위치 기반 출퇴근 설정 확인",
"action": "verify_elements",
"checks": [
"GPS 출퇴근 사용 여부",
"출퇴근 가능 위치 설정"
],
"expected": "위치 기반 설정 표시"
},
{
"id": 11,
"name": "근태 이상 알림 설정 확인",
"action": "verify_elements",
"checks": [
"지각 알림 설정",
"무단결근 알림 설정"
],
"expected": "알림 설정 표시"
},
{
"id": 12,
"name": "부서별 근태 설정 확인",
"action": "verify_elements",
"checks": [
"부서별 설정 가능"
],
"expected": "부서별 설정 표시"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/settings/attendance",
"description": "근태 설정 조회"
}
],
"requiredVerifications": [
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "근태 설정 폼, 저장 버튼 존재"
}
],
"rollbackPlan": {
"onUpdateFail": "페이지 새로고침으로 원래 값 복원",
"note": "READ-only 패턴으로 안정성 우선"
}
}

View File

@@ -0,0 +1,153 @@
{
"id": "settings-bank-account",
"name": "계좌관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "설정 > 계좌관리 메뉴의 계좌 목록 조회 및 UI 검증 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "설정",
"level2": "계좌관리",
"expectedUrl": "/settings/accounts",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 설정 > 계좌관리",
"action": "menu_navigate",
"level1": "설정",
"level2": "계좌관리",
"expected": {
"url_contains": "/settings/accounts",
"visible": ["계좌관리", "계좌"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"계좌 목록 표시",
"계좌 등록 버튼 존재",
"검색/필터 기능 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "계좌 테이블 구조 확인",
"action": "verify_table",
"checks": [
"은행명 컬럼",
"계좌번호 컬럼",
"예금주 컬럼",
"계좌유형 컬럼"
],
"expected": "계좌 테이블 컬럼 정상 표시"
},
{
"id": 4,
"name": "검색 기능 확인",
"action": "click_if_exists",
"target": "input[placeholder*='검색']",
"expected": {
"search_available": true
}
},
{
"id": 5,
"name": "계좌 등록 버튼 확인",
"action": "click_if_exists",
"target": "button:has-text('등록'), button:has-text('계좌 등록'), button:has-text('추가')",
"expected": {
"modal_or_page": true
}
},
{
"id": 6,
"name": "등록 폼/모달 확인",
"action": "verify_elements",
"checks": [
"은행명 입력 필드",
"계좌번호 입력 필드",
"예금주 입력 필드"
],
"expected": "계좌 등록 폼 표시"
},
{
"id": 7,
"name": "등록 모달 닫기",
"action": "close_modal_if_open",
"expected": "모달 닫힘"
},
{
"id": 8,
"phase": "READ",
"name": "[READ] 계좌 목록 데이터 확인",
"action": "verify_detail",
"checks": [
"계좌 목록 데이터 표시됨"
],
"expected": "계좌 목록 정상 표시"
},
{
"id": 9,
"phase": "READ",
"name": "[READ] 첫 번째 행 클릭",
"action": "click_if_exists",
"target": "table tbody tr:first-child"
},
{
"id": 10,
"phase": "READ",
"name": "[READ] 계좌 상세 정보 확인",
"action": "verify_detail",
"checks": [
"계좌 상세 정보 표시"
],
"expected": "계좌 상세 정보 확인"
},
{
"id": 11,
"name": "상세 모달 닫기",
"action": "close_modal_if_open",
"expected": "모달 닫힘"
},
{
"id": 12,
"name": "계좌 목록 최종 확인",
"action": "verify_elements",
"checks": [
"계좌 목록 구조 정상",
"등록 버튼 존재"
],
"expected": "계좌관리 페이지 정상"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/bank-accounts",
"description": "계좌 목록 조회"
}
],
"requiredVerifications": [
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "계좌 목록, 등록 버튼, 필터 존재"
}
],
"rollbackPlan": {
"note": "READ-only 패턴으로 안정성 우선"
}
}

View File

@@ -0,0 +1,190 @@
{
"id": "settings-company",
"name": "회사정보 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "설정 > 회사정보 메뉴의 회사 정보 조회/수정 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "설정",
"level2": "회사정보",
"expectedUrl": "/company-info",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"testData": {
"update": {
"companyPhone": "02-1234-5678",
"companyFax": "02-1234-5679"
}
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 설정 > 회사정보",
"action": "menu_navigate",
"level1": "설정",
"level2": "회사정보",
"expected": {
"url_contains": "/company",
"visible": ["회사정보", "회사"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"회사 정보 폼 표시",
"사업자등록번호 표시",
"수정 버튼 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "회사 정보 폼 구조 확인",
"action": "verify_elements",
"checks": [
"회사명 표시",
"사업자등록번호 표시",
"대표자명 표시",
"주소 표시",
"연락처 표시"
],
"expected": "회사 정보 폼 정상 표시"
},
{
"id": 4,
"phase": "READ",
"name": "[READ] 현재 회사 정보 확인",
"action": "verify_detail",
"checks": [
"회사명 표시됨",
"사업자등록번호 표시됨",
"대표자명 표시됨"
],
"expected": "현재 회사 정보 정상 표시"
},
{
"id": 5,
"phase": "UPDATE",
"name": "[UPDATE] 회사 정보 수정 모드 진입",
"action": "click_if_exists",
"target": "button:has-text('수정'), button:has-text('편집')",
"expected": {
"edit_mode": true,
"fields_editable": true
}
},
{
"id": 6,
"phase": "UPDATE",
"name": "[UPDATE] 회사 전화번호 수정",
"action": "click_if_exists",
"target": "input[name*='phone'], input[placeholder*='전화']"
},
{
"id": 7,
"phase": "UPDATE",
"name": "[UPDATE] 팩스번호 수정",
"action": "click_if_exists",
"target": "input[name*='fax'], input[placeholder*='팩스']"
},
{
"id": 8,
"phase": "UPDATE",
"name": "[UPDATE] 필수 검증 #2: 회사 정보 저장",
"action": "click_if_exists",
"target": "button:has-text('저장'), button:has-text('확인')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "PUT /api/v1/company",
"toast": "저장|수정|완료|성공"
},
"expected": "회사 정보 저장 완료"
},
{
"id": 9,
"phase": "UPDATE",
"name": "[UPDATE] 저장 결과 확인",
"action": "verify_detail",
"checks": [
"전화번호: 02-1234-5678",
"팩스: 02-1234-5679"
],
"expected": "수정된 정보 반영"
},
{
"id": 10,
"name": "로고 이미지 영역 확인",
"action": "verify_elements",
"checks": [
"회사 로고 표시",
"로고 변경 버튼"
],
"expected": "로고 영역 표시"
},
{
"id": 11,
"name": "사업자등록증 영역 확인",
"action": "verify_elements",
"checks": [
"사업자등록증 첨부",
"파일 업로드 버튼"
],
"expected": "첨부 영역 표시"
},
{
"id": 12,
"name": "법인등록번호 확인",
"action": "verify_elements",
"checks": [
"법인등록번호 표시"
],
"expected": "법인등록번호 표시"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/company",
"description": "회사 정보 조회"
},
{
"method": "PUT",
"endpoint": "/api/v1/company",
"description": "회사 정보 수정"
},
{
"method": "POST",
"endpoint": "/api/v1/company/logo",
"description": "회사 로고 업로드"
}
],
"requiredVerifications": [
{
"id": 2,
"name": "저장 버튼",
"steps": [8],
"criteria": "API 호출 + 성공 토스트 + 정보 반영"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "회사 정보 폼, 수정 버튼 존재"
}
],
"rollbackPlan": {
"onUpdateFail": "페이지 새로고침으로 원래 값 복원",
"note": "회사 정보 수정은 관리자 권한이 필요할 수 있음"
}
}

View File

@@ -0,0 +1,196 @@
{
"id": "settings-notification",
"name": "알림설정 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "설정 > 알림설정 메뉴의 알림 설정 조회/수정/저장 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "설정",
"level2": "알림설정",
"expectedUrl": "/settings/notification-settings",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"testData": {
"update": {
"emailNotification": true,
"pushNotification": true,
"approvalNotification": true,
"attendanceNotification": false
}
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 설정 > 알림설정",
"action": "menu_navigate",
"level1": "설정",
"level2": "알림설정",
"expected": {
"url_contains": "/settings/notification-settings",
"visible": ["알림설정", "알림"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"알림 설정 폼 표시",
"알림 ON/OFF 토글 존재",
"저장 버튼 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "알림 설정 폼 구조 확인",
"action": "verify_elements",
"checks": [
"이메일 알림 토글",
"푸시 알림 토글",
"결재 알림 설정",
"근태 알림 설정"
],
"expected": "알림 설정 폼 정상 표시"
},
{
"id": 4,
"phase": "READ",
"name": "[READ] 현재 알림 설정 확인",
"action": "verify_detail",
"checks": [
"이메일 알림 상태 표시",
"푸시 알림 상태 표시",
"결재 알림 상태 표시"
],
"expected": "현재 알림 설정 정상 표시"
},
{
"id": 5,
"phase": "UPDATE",
"name": "[UPDATE] 이메일 알림 토글",
"action": "click_if_exists",
"target": "input[name*='email'], label:has-text('이메일') input[type='checkbox']",
"expected": {
"toggle_changed": true
}
},
{
"id": 6,
"phase": "UPDATE",
"name": "[UPDATE] 푸시 알림 토글",
"action": "click_if_exists",
"target": "input[name*='push'], label:has-text('푸시') input[type='checkbox']",
"expected": {
"toggle_changed": true
}
},
{
"id": 7,
"phase": "UPDATE",
"name": "[UPDATE] 결재 알림 설정",
"action": "click_if_exists",
"target": "input[name*='approval'], label:has-text('결재') input[type='checkbox']",
"expected": {
"toggle_changed": true
}
},
{
"id": 8,
"phase": "UPDATE",
"name": "[UPDATE] 필수 검증 #2: 알림 설정 저장",
"action": "click_if_exists",
"target": "button:has-text('저장'), button:has-text('적용')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "PUT /api/v1/settings/notifications",
"toast": "저장|적용|완료|성공"
},
"expected": "알림 설정 저장 완료"
},
{
"id": 9,
"phase": "UPDATE",
"name": "[UPDATE] 저장 결과 확인",
"action": "verify_detail",
"checks": [
"변경된 알림 설정 반영"
],
"expected": "수정된 설정 반영"
},
{
"id": 10,
"name": "알림 유형별 설정 확인",
"action": "verify_elements",
"checks": [
"시스템 알림 설정",
"업무 알림 설정",
"일정 알림 설정"
],
"expected": "알림 유형별 설정 표시"
},
{
"id": 11,
"name": "알림 수신 시간 설정 확인",
"action": "verify_elements",
"checks": [
"수신 시간대 설정",
"방해금지 시간 설정"
],
"expected": "시간 설정 표시"
},
{
"id": 12,
"name": "알림 테스트 전송 확인",
"action": "verify_elements",
"checks": [
"테스트 알림 전송 버튼"
],
"expected": "테스트 버튼 표시"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/settings/notifications",
"description": "알림 설정 조회"
},
{
"method": "PUT",
"endpoint": "/api/v1/settings/notifications",
"description": "알림 설정 수정"
},
{
"method": "POST",
"endpoint": "/api/v1/settings/notifications/test",
"description": "테스트 알림 전송"
}
],
"requiredVerifications": [
{
"id": 2,
"name": "저장 버튼",
"steps": [8],
"criteria": "API 호출 + 성공 토스트 + 설정 반영"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "알림 설정 폼, 저장 버튼 존재"
}
],
"rollbackPlan": {
"onUpdateFail": "페이지 새로고침으로 원래 값 복원",
"note": "설정 페이지는 수정 후 원복 테스트 권장"
}
}

View File

@@ -0,0 +1,158 @@
{
"id": "settings-permission",
"name": "권한관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "설정 > 권한관리 메뉴의 권한 그룹 목록 조회 및 UI 검증 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "설정",
"level2": "권한관리",
"expectedUrl": "/settings/permissions",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 설정 > 권한관리",
"action": "menu_navigate",
"level1": "설정",
"level2": "권한관리",
"expected": {
"url_contains": "/settings/permissions",
"visible": ["권한관리", "권한"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"권한 그룹 목록 표시",
"권한 추가 버튼 존재",
"권한 설정 영역 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "권한 그룹 목록 확인",
"action": "verify_elements",
"checks": [
"권한 그룹 목록 테이블 또는 리스트",
"기본 역할 (관리자, 사용자 등) 표시",
"권한 추가 버튼"
],
"expected": "권한 목록 정상 표시"
},
{
"id": 4,
"phase": "READ",
"name": "[READ] 첫 번째 권한 그룹 클릭",
"action": "click_if_exists",
"target": "table tbody tr:first-child, li:first-child, [class*='list'] > *:first-child",
"expected": {
"visible": ["메뉴 권한", "기능 권한"]
}
},
{
"id": 5,
"phase": "READ",
"name": "[READ] 권한 체크박스 구조 확인",
"action": "verify_elements",
"checks": [
"메뉴별 읽기/쓰기/삭제 권한",
"전체 선택/해제 기능"
],
"expected": "권한 체크박스 매트릭스 표시"
},
{
"id": 6,
"phase": "READ",
"name": "[READ] 권한 상세 정보 확인",
"action": "verify_detail",
"checks": [
"권한 그룹 정보 표시됨",
"체크박스 또는 권한 목록 표시"
],
"expected": "권한 상세 정보 표시"
},
{
"id": 7,
"name": "권한 추가 버튼 확인",
"action": "click_if_exists",
"target": "button:has-text('추가'), button:has-text('권한 추가'), button:has-text('역할 추가')",
"expected": {
"modal_or_form": true
}
},
{
"id": 8,
"name": "추가 모달 확인",
"action": "verify_elements",
"checks": [
"역할명 입력 필드",
"설명 입력 필드"
],
"expected": "권한 추가 폼 표시"
},
{
"id": 9,
"name": "추가 모달 닫기",
"action": "close_modal_if_open",
"expected": "모달 닫힘"
},
{
"id": 10,
"name": "저장 버튼 존재 확인",
"action": "verify_elements",
"checks": [
"저장 또는 적용 버튼 존재"
],
"expected": "저장 버튼 표시"
},
{
"id": 11,
"name": "삭제 버튼 존재 확인",
"action": "verify_elements",
"checks": [
"삭제 버튼 존재 여부"
],
"expected": "삭제 기능 확인"
},
{
"id": 12,
"name": "권한관리 페이지 최종 확인",
"action": "verify_detail",
"checks": [
"권한관리 페이지 정상 동작"
],
"expected": "페이지 정상 확인"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/roles",
"description": "역할 목록 조회"
}
],
"requiredVerifications": [
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "권한 목록, 추가 버튼, 권한 체크박스 존재"
}
],
"rollbackPlan": {
"note": "READ-only 패턴으로 안정성 우선"
}
}

View File

@@ -0,0 +1,153 @@
{
"id": "settings-popup",
"name": "팝업관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "설정 > 팝업관리 메뉴의 팝업 목록 조회 및 UI 검증 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "설정",
"level2": "팝업관리",
"expectedUrl": "/settings/popup-management",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 설정 > 팝업관리",
"action": "menu_navigate",
"level1": "설정",
"level2": "팝업관리",
"expected": {
"url_contains": "/settings/popup",
"visible": ["팝업관리", "팝업"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"팝업 목록 표시",
"팝업 등록 버튼 존재",
"상태 필터 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "팝업 테이블 구조 확인",
"action": "verify_table",
"checks": [
"팝업 제목 컬럼",
"상태 컬럼",
"기간 컬럼",
"노출 위치 컬럼"
],
"expected": "팝업 테이블 표시"
},
{
"id": 4,
"name": "기존 팝업 확인",
"action": "verify_elements",
"checks": [
"등록된 팝업 존재 또는 등록된 팝업 없음"
],
"expected": "팝업 목록 상태 확인"
},
{
"id": 5,
"name": "팝업 등록 버튼 확인",
"action": "click_if_exists",
"target": "button:has-text('등록'), button:has-text('추가'), button:has-text('신규')",
"expected": {
"modal_open": true
}
},
{
"id": 6,
"name": "등록 폼 요소 확인",
"action": "verify_elements",
"checks": [
"제목 입력 필드",
"내용 입력 필드",
"기간 설정 필드"
],
"expected": "팝업 등록 폼 표시"
},
{
"id": 7,
"name": "등록 모달 닫기",
"action": "close_modal_if_open",
"expected": "모달 닫힘"
},
{
"id": 8,
"phase": "READ",
"name": "[READ] 팝업 목록 데이터 확인",
"action": "verify_detail",
"checks": [
"팝업 목록 데이터 표시됨"
],
"expected": "팝업 목록 정상 표시"
},
{
"id": 9,
"phase": "READ",
"name": "[READ] 첫 번째 행 클릭",
"action": "click_if_exists",
"target": "table tbody tr:first-child"
},
{
"id": 10,
"phase": "READ",
"name": "[READ] 팝업 상세 정보 확인",
"action": "verify_detail",
"checks": [
"팝업 상세 정보 표시"
],
"expected": "팝업 상세 정보 확인"
},
{
"id": 11,
"name": "상세 모달 닫기",
"action": "close_modal_if_open",
"expected": "모달 닫힘"
},
{
"id": 12,
"name": "팝업관리 페이지 최종 확인",
"action": "verify_elements",
"checks": [
"팝업 목록 구조 정상",
"등록 버튼 존재"
],
"expected": "팝업관리 페이지 정상"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/settings/popups",
"description": "팝업 목록 조회"
}
],
"requiredVerifications": [
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "팝업 목록, 등록 버튼, 상태 필터 존재"
}
],
"rollbackPlan": {
"note": "READ-only 패턴으로 안정성 우선"
}
}

View File

@@ -0,0 +1,71 @@
{
"id": "settings-position",
"name": "직책관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "설정 > 직책관리 목록/검색/상세 기능 검증",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "설정",
"level2": "직책관리",
"expectedUrl": "/settings/titles",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 설정 > 직책관리",
"action": "menu_navigate",
"level1": "설정",
"level2": "직책관리",
"expected": { "url_contains": "/settings" }
},
{
"id": 2,
"name": "목업 감지",
"action": "verify_not_mockup"
},
{
"id": 3,
"name": "직책관리 페이지 확인",
"action": "verify_detail",
"checks": ["visible_text:직책"]
},
{
"id": 4,
"name": "설정 페이지 확인",
"action": "verify_detail",
"checks": ["visible_text:설정"]
},
{
"id": 5,
"name": "추가 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('추가'), button:has-text('등록'), button:has-text('신규')"
},
{
"id": 6,
"name": "대기",
"action": "wait",
"duration": 1000
},
{
"id": 7,
"name": "모달 닫기",
"action": "close_modal_if_open"
},
{
"id": 8,
"name": "직책관리 최종 확인",
"action": "verify_detail",
"checks": ["visible_text:직책"]
}
]
}

View File

@@ -0,0 +1,71 @@
{
"id": "settings-rank",
"name": "직급관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "설정 > 직급관리 목록/검색/상세 기능 검증",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "설정",
"level2": "직급관리",
"expectedUrl": "/settings/ranks",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 설정 > 직급관리",
"action": "menu_navigate",
"level1": "설정",
"level2": "직급관리",
"expected": { "url_contains": "/settings" }
},
{
"id": 2,
"name": "목업 감지",
"action": "verify_not_mockup"
},
{
"id": 3,
"name": "직급관리 페이지 확인",
"action": "verify_detail",
"checks": ["visible_text:직급"]
},
{
"id": 4,
"name": "설정 페이지 확인",
"action": "verify_detail",
"checks": ["visible_text:설정"]
},
{
"id": 5,
"name": "추가 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('추가'), button:has-text('등록'), button:has-text('신규')"
},
{
"id": 6,
"name": "대기",
"action": "wait",
"duration": 1000
},
{
"id": 7,
"name": "모달 닫기",
"action": "close_modal_if_open"
},
{
"id": 8,
"name": "직급관리 최종 확인",
"action": "verify_detail",
"checks": ["visible_text:직급"]
}
]
}

View File

@@ -0,0 +1,134 @@
{
"id": "settings-subscription",
"name": "구독관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "설정 > 구독관리 메뉴의 구독 정보 조회/플랜 비교 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "설정",
"level2": "구독관리",
"expectedUrl": "/subscription",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"selectors": {
"planCard": ".card, [class*='plan'], [class*='Plan'], [class*='subscription']",
"priceText": "[class*='price'], [class*='Price'], [class*='amount'], [class*='cost']",
"dateInfo": "[class*='date'], [class*='period'], time",
"paymentInfo": "[class*='payment'], [class*='billing'], [class*='card']",
"planTable": "table, [class*='plan-compare'], [class*='pricing-table']",
"actionButton": "button, [role='button']",
"usageCard": "[class*='usage'], [class*='quota'], [class*='limit']",
"historyTable": "table tbody tr, [class*='history'] li, [class*='payment-list']"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 설정 > 구독관리",
"action": "menu_navigate",
"level1": "설정",
"level2": "구독관리",
"expected": {
"url_contains": "/subscription",
"visible": ["구독관리", "구독"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"구독 정보 표시",
"플랜 정보 표시",
"결제 정보 표시"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "현재 플랜 카드 존재 확인",
"action": "verify_element",
"target": "planCard",
"verification": { "exists": true }
},
{
"id": 4,
"name": "플랜/가격 정보 텍스트 확인",
"action": "evaluate",
"script": "(() => { const text = document.body.innerText; return text.includes('플랜') || text.includes('요금') || text.includes('Plan') || text.includes('구독'); })()"
},
{
"id": 5,
"name": "구독 기간/날짜 정보 확인",
"action": "evaluate",
"script": "(() => { const text = document.body.innerText; return text.includes('기간') || text.includes('시작') || text.includes('종료') || /\\d{4}[-./]\\d{2}[-./]\\d{2}/.test(text); })()"
},
{
"id": 6,
"name": "결제 관련 정보 표시 확인",
"action": "evaluate",
"script": "(() => { const text = document.body.innerText; return text.includes('결제') || text.includes('카드') || text.includes('금액') || text.includes('원'); })()"
},
{
"id": 7,
"name": "플랜 비교/변경 UI 확인",
"action": "verify_element",
"target": "table, [class*='plan'], [class*='compare'], button:has-text('플랜'), button:has-text('변경'), button:has-text('업그레이드')"
},
{
"id": 8,
"name": "사용량 현황 영역 확인",
"action": "evaluate",
"script": "(() => { const text = document.body.innerText; return text.includes('사용') || text.includes('용량') || text.includes('사용자') || text.includes('%'); })()"
},
{
"id": 9,
"name": "결제 내역 영역 확인",
"action": "verify_element",
"target": "table tbody tr, [class*='history'], [class*='payment-list'], [class*='billing-history']"
},
{
"id": 10,
"name": "다운로드/영수증 버튼 확인",
"action": "verify_element",
"target": "button:has-text('다운로드'), button:has-text('영수증'), button:has-text('Download'), a:has-text('영수증'), [class*='download']"
},
{
"id": 11,
"name": "결제 수단 관련 UI 확인",
"action": "evaluate",
"script": "(() => { const text = document.body.innerText; return text.includes('결제 수단') || text.includes('카드') || text.includes('계좌') || document.querySelector('[class*=\"payment-method\"], [class*=\"card-info\"]'); })()"
},
{
"id": 12,
"name": "구독 관리 버튼 확인 (취소/해지 포함)",
"action": "verify_element",
"target": "button:has-text('취소'), button:has-text('해지'), button:has-text('관리'), button:has-text('변경'), [class*='cancel'], [class*='manage']"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/subscription",
"description": "구독 정보 조회"
}
],
"requiredVerifications": [
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "구독 정보, 플랜 정보, 결제 정보 존재"
}
],
"rollbackPlan": {
"note": "조회 전용 페이지로 데이터 변경 없음 (결제/플랜 변경은 별도 테스트)"
}
}

View File

@@ -0,0 +1,197 @@
{
"id": "settings-vacation-policy",
"name": "휴가정책 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "설정 > 휴가정책 메뉴의 휴가 정책 조회/수정/저장 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "설정",
"level2": "휴가정책",
"expectedUrl": "/settings/leave-policy",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"testData": {
"update": {
"annualLeave": "15",
"halfDayEnabled": true,
"carryOverDays": "5",
"memo": "E2E 테스트 휴가정책"
},
"restore": {
"annualLeave": "15",
"halfDayEnabled": true,
"carryOverDays": "5"
}
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 설정 > 휴가정책",
"action": "menu_navigate",
"level1": "설정",
"level2": "휴가정책",
"expected": {
"url_contains": "/settings/leave-policy",
"visible": ["휴가정책", "휴가"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"휴가 정책 설정 폼 표시",
"연차 부여 기준 설정 가능",
"저장 버튼 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "휴가 정책 폼 구조 확인",
"action": "verify_elements",
"checks": [
"연차 부여 기준 입력",
"반차 사용 여부 설정",
"휴가 이월 일수 설정",
"휴가 유형별 설정"
],
"expected": "휴가 정책 폼 정상 표시"
},
{
"id": 4,
"phase": "READ",
"name": "[READ] 현재 정책 값 확인",
"action": "verify_detail",
"checks": [
"연차 부여 기준 표시",
"반차 사용 설정 표시",
"이월 일수 표시"
],
"expected": "현재 정책 값 정상 표시"
},
{
"id": 5,
"phase": "UPDATE",
"name": "[UPDATE] 연차 부여 기준 수정",
"action": "click_if_exists",
"target": "input[name*='annual'], input[placeholder*='연차']"
},
{
"id": 6,
"phase": "UPDATE",
"name": "[UPDATE] 반차 사용 설정",
"action": "click_if_exists",
"target": "input[type='checkbox'][name*='half'], label:has-text('반차')",
"expected": {
"checkbox_toggled": true
}
},
{
"id": 7,
"phase": "UPDATE",
"name": "[UPDATE] 이월 일수 수정",
"action": "click_if_exists",
"target": "input[name*='carryOver'], input[placeholder*='이월']"
},
{
"id": 8,
"phase": "UPDATE",
"name": "[UPDATE] 필수 검증 #2: 정책 저장",
"action": "click_if_exists",
"target": "button:has-text('저장'), button:has-text('적용')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "PUT /api/v1/settings/vacation-policy",
"toast": "저장|적용|완료|성공"
},
"expected": "휴가 정책 저장 완료"
},
{
"id": 9,
"phase": "UPDATE",
"name": "[UPDATE] 저장 결과 확인",
"action": "verify_detail",
"checks": [
"연차: 15",
"이월 일수: 5"
],
"expected": "수정된 정책 반영"
},
{
"id": 10,
"name": "휴가 유형 관리 확인",
"action": "verify_elements",
"checks": [
"연차 유형 표시",
"병가 유형 표시",
"경조사 유형 표시"
],
"expected": "휴가 유형 목록 표시"
},
{
"id": 11,
"phase": "CREATE",
"name": "[CREATE] 휴가 유형 추가 버튼 확인",
"action": "verify_elements",
"checks": [
"휴가 유형 추가 버튼 존재"
],
"expected": "추가 버튼 표시"
},
{
"id": 12,
"name": "정책 적용 대상 확인",
"action": "verify_elements",
"checks": [
"부서별 적용 설정",
"직급별 적용 설정"
],
"expected": "적용 대상 설정 표시"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/settings/vacation-policy",
"description": "휴가 정책 조회"
},
{
"method": "PUT",
"endpoint": "/api/v1/settings/vacation-policy",
"description": "휴가 정책 수정"
},
{
"method": "GET",
"endpoint": "/api/v1/vacation-types",
"description": "휴가 유형 목록 조회"
}
],
"requiredVerifications": [
{
"id": 2,
"name": "저장 버튼",
"steps": [8],
"criteria": "API 호출 + 성공 토스트 + 설정 반영"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "휴가 정책 폼, 저장 버튼 존재"
}
],
"rollbackPlan": {
"onUpdateFail": "페이지 새로고침으로 원래 값 복원",
"note": "설정 페이지는 수정 후 원복 테스트 권장"
}
}

View File

@@ -0,0 +1,188 @@
{
"id": "settings-work-schedule",
"name": "근무일정 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "설정 > 근무일정 메뉴의 근무일정 조회/수정/저장 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "설정",
"level2": "근무일정",
"expectedUrl": "/settings/work-schedule",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"testData": {
"update": {
"workDays": "월,화,수,목,금",
"startTime": "09:00",
"endTime": "18:00",
"breakTime": "60"
}
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 설정 > 근무일정",
"action": "menu_navigate",
"level1": "설정",
"level2": "근무일정",
"expected": {
"url_contains": "/settings/work-schedule",
"visible": ["근무일정", "근무"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"근무일정 설정 폼 표시",
"근무일 선택 가능",
"저장 버튼 존재"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 3,
"name": "근무일정 폼 구조 확인",
"action": "verify_elements",
"checks": [
"근무일 선택 (월~일)",
"출근 시간 입력",
"퇴근 시간 입력",
"휴게 시간 설정"
],
"expected": "근무일정 폼 정상 표시"
},
{
"id": 4,
"phase": "READ",
"name": "[READ] 현재 근무일정 확인",
"action": "verify_detail",
"checks": [
"근무일 표시",
"출근 시간 표시",
"퇴근 시간 표시"
],
"expected": "현재 근무일정 정상 표시"
},
{
"id": 5,
"phase": "UPDATE",
"name": "[UPDATE] 출근 시간 수정",
"action": "click_if_exists",
"target": "input[name*='start'], input[type='time']:first-of-type"
},
{
"id": 6,
"phase": "UPDATE",
"name": "[UPDATE] 퇴근 시간 수정",
"action": "click_if_exists",
"target": "input[name*='end'], input[type='time']:last-of-type"
},
{
"id": 7,
"phase": "UPDATE",
"name": "[UPDATE] 휴게 시간 설정",
"action": "click_if_exists",
"target": "input[name*='break'], input[placeholder*='휴게']"
},
{
"id": 8,
"phase": "UPDATE",
"name": "[UPDATE] 필수 검증 #2: 근무일정 저장",
"action": "click_if_exists",
"target": "button:has-text('저장'), button:has-text('적용')",
"verify": {
"url_maintained": true,
"no_error_page": true,
"api_call": "PUT /api/v1/settings/work-schedule",
"toast": "저장|적용|완료|성공"
},
"expected": "근무일정 저장 완료"
},
{
"id": 9,
"phase": "UPDATE",
"name": "[UPDATE] 저장 결과 확인",
"action": "verify_detail",
"checks": [
"출근: 09:00",
"퇴근: 18:00",
"휴게: 60분"
],
"expected": "수정된 일정 반영"
},
{
"id": 10,
"name": "휴무일 설정 확인",
"action": "verify_elements",
"checks": [
"토요일 휴무 설정",
"일요일 휴무 설정",
"공휴일 자동 적용"
],
"expected": "휴무일 설정 표시"
},
{
"id": 11,
"name": "주간 근무시간 계산 확인",
"action": "verify_elements",
"checks": [
"주간 근무시간 합계 표시"
],
"expected": "근무시간 자동 계산"
},
{
"id": 12,
"name": "부서별 근무일정 확인",
"action": "verify_elements",
"checks": [
"부서별 일정 설정 가능"
],
"expected": "부서별 일정 표시"
}
],
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/settings/work-schedule",
"description": "근무일정 조회"
},
{
"method": "PUT",
"endpoint": "/api/v1/settings/work-schedule",
"description": "근무일정 수정"
},
{
"method": "GET",
"endpoint": "/api/v1/holidays",
"description": "공휴일 목록 조회"
}
],
"requiredVerifications": [
{
"id": 2,
"name": "저장 버튼",
"steps": [8],
"criteria": "API 호출 + 성공 토스트 + 설정 반영"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "근무일정 폼, 저장 버튼 존재"
}
],
"rollbackPlan": {
"onUpdateFail": "페이지 새로고침으로 원래 값 복원",
"note": "설정 페이지는 수정 후 원복 테스트 권장"
}
}

View File

@@ -0,0 +1,83 @@
{
"id": "shipment-management",
"name": "출고관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "출고관리 > 출고관리 목록/상세 기능 검증",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "출고관리",
"level2": "출고관리",
"expectedUrl": "/outbound/shipments",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 1,
"name": "메뉴 진입: 출고관리 > 출고관리",
"action": "menu_navigate",
"level1": "출고관리",
"level2": "출고관리",
"expected": { "url_contains": "/outbound" }
},
{
"id": 2,
"name": "목업 감지",
"action": "verify_not_mockup"
},
{
"id": 3,
"name": "출고관리 페이지 확인",
"action": "verify_detail",
"checks": ["visible_text:출고"]
},
{
"id": 4,
"name": "UI 요소 확인",
"action": "verify_detail",
"checks": ["visible_text:관리"]
},
{
"id": 5,
"name": "검색 입력 시도",
"action": "click_if_exists",
"target": "input[type='search'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input"
},
{
"id": 6,
"name": "대기",
"action": "wait",
"duration": 1000
},
{
"id": 7,
"name": "행 클릭 시도",
"action": "click_if_exists",
"target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']"
},
{
"id": 8,
"name": "상세 확인",
"action": "verify_detail",
"checks": ["visible_text:출고"]
},
{
"id": 9,
"name": "모달 닫기",
"action": "close_modal_if_open"
},
{
"id": 10,
"name": "최종 확인",
"action": "verify_detail",
"checks": ["visible_text:출고"]
}
]
}

View File

@@ -0,0 +1,725 @@
{
"enabled": true,
"id": "vendor-ledger",
"name": "거래처원장 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
},
"description": "회계관리 > 거래처원장 메뉴의 기간 설정, 검색, 테이블, 다운로드, 상세 페이지 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"navigation": {
"targetUrl": "/accounting/vendor-ledger",
"urlPattern": "/accounting/vendor-ledger|/ko/accounting/vendor-ledger",
"menuHints": [
"거래처원장",
"거래처 원장",
"회계관리"
]
},
"menuNavigation": {
"level1": "회계관리",
"level2": "거래처원장",
"expectedUrl": "/ko/accounting/vendor-ledger",
"searchWithinParent": true,
"closeOtherMenus": true
},
"menuNavigationEnhanced": {
"strategy": "scroll-and-search",
"level1": {
"text": "회계관리",
"alternativeNames": [
"회계",
"Accounting"
],
"scrollConfig": {
"direction": "down",
"maxScrollAttempts": 5,
"scrollAmount": 200
}
},
"level2": {
"text": "거래처원장",
"alternativeNames": [
"거래처 원장",
"Vendor Ledger"
],
"scrollConfig": {
"direction": "down",
"maxScrollAttempts": 3,
"scrollAmount": 150
}
},
"expectedUrl": "/ko/accounting/vendor-ledger",
"fallbackUrl": "/ko/accounting/vendor-ledger"
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": 0,
"name": "사이드바 메뉴 전체 펼치기",
"description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비",
"actions": [
{
"type": "evaluate",
"script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})"
},
{
"type": "wait",
"duration": 300
},
{
"type": "evaluate",
"script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()"
},
{
"type": "wait",
"duration": 2000
}
],
"expected": "사이드바 전체 메뉴가 펼쳐짐"
},
{
"id": 1,
"name": "로그인 상태 확인",
"action": "verify_page",
"expected": "이미 로그인된 상태"
},
{
"id": 2,
"name": "2단계 메뉴 진입: 회계관리 > 거래처원장",
"description": "회계관리 > 거래처원장 메뉴로 이동하여 페이지 로드 확인 (scrollAndFind 패턴 사용)",
"actions": [
{
"type": "scrollAndFind",
"level": 1,
"target": "회계관리",
"alternativeSelectors": [
"text=회계관리",
"[data-menu='accounting']",
"a:has-text('회계관리')",
"span:has-text('회계관리')"
],
"scrollConfig": {
"container": ".sidebar-scroll, [class*='sidebar'], nav",
"direction": "down",
"maxAttempts": 5,
"scrollAmount": 200
}
},
{
"type": "click_if_exists",
"target": "회계관리"
},
{
"type": "wait",
"duration": 500
},
{
"type": "scrollAndFind",
"level": 2,
"target": "거래처원장",
"alternativeSelectors": [
"text=거래처원장",
"[data-menu='vendor-ledger']",
"a:has-text('거래처원장')",
"span:has-text('거래처원장')"
],
"scrollConfig": {
"container": ".sidebar-scroll, [class*='sidebar'], nav",
"direction": "down",
"maxAttempts": 3,
"scrollAmount": 150
}
},
{
"type": "click_if_exists",
"target": "거래처원장"
},
{
"type": "wait",
"target": "페이지 로드 완료"
}
],
"expected": {
"url": "/ko/accounting/vendor-ledger",
"pageTitle": "거래처원장",
"elements": [
"통계 카드",
"테이블"
]
}
},
{
"id": 3,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"입력 필드 존재 (검색창, 날짜 선택)",
"동작하는 버튼 존재 (엑셀 다운로드)",
"테이블 데이터 표시",
"API 호출 확인"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 4,
"name": "통계 카드 확인",
"action": "verify_elements",
"checks": [
"전기 이월 카드 표시",
"매출 카드 표시",
"수금 카드 표시",
"잔액 카드 표시"
],
"expected": "4개 통계 카드 모두 표시, 금액 형식 확인"
},
{
"id": 5,
"name": "테이블 구조 확인",
"action": "verify_table",
"checks": [
"체크박스 컬럼",
"No. 컬럼",
"거래처명 컬럼",
"이월잔액 컬럼",
"매출 컬럼",
"수금 컬럼",
"잔액 컬럼",
"결제일 컬럼"
],
"expected": "8개 컬럼 존재, 합계 행 표시"
},
{
"id": 6,
"name": "기간 설정 - 시작일 변경",
"action": "click_if_exists",
"target": "startDate",
"value": "2025-01-01",
"expected": "시작일 변경 후 데이터 재조회"
},
{
"id": 7,
"name": "기간 설정 - 종료일 변경",
"action": "click_if_exists",
"target": "endDate",
"value": "2025-12-31",
"expected": "종료일 변경 후 데이터 재조회"
},
{
"id": 8,
"name": "기간 설정 - 데이터 변화 확인",
"action": "verify_detail",
"checks": [
"테이블 데이터 갱신",
"통계 카드 값 갱신",
"합계 행 값 갱신"
],
"expected": "기간에 맞는 데이터로 변경됨"
},
{
"id": 9,
"name": "⚠️ 필수 검증: 검색 기능 테스트",
"actions": [
{
"type": "capture",
"variable": "beforeSearchCount",
"selector": "table tbody tr",
"extract": "count",
"description": "검색 전 행 수 저장"
},
{
"type": "click_if_exists",
"target": "input[type='search'], input[placeholder*='검색']",
"description": "검색 입력창 존재 확인"
},
{
"type": "wait",
"duration": 1000,
"description": "검색 결과 로딩 대기"
},
{
"type": "capture",
"variable": "afterSearchCount",
"selector": "table tbody tr",
"extract": "count",
"description": "검색 후 행 수 저장"
}
],
"verify": {
"searchApplied": true,
"tableContains": "{testData.searchKeyword}",
"dataChanged": "beforeSearchCount may differ from afterSearchCount"
},
"expected": "검색어에 맞는 거래처만 필터링"
},
{
"id": "9-1",
"name": "검색 결과 데이터 검증",
"description": "검색 결과의 모든 행이 검색어를 포함하는지 확인",
"verify": {
"allRowsContain": "{testData.searchKeyword}",
"columnToCheck": "거래처명"
}
},
{
"id": 10,
"name": "검색 결과 확인",
"action": "verify_search_result",
"checks": [
"테이블 행 수 변화",
"검색어 포함 거래처명 표시"
],
"expected": "검색 결과 정상 표시"
},
{
"id": 11,
"name": "검색 초기화",
"actions": [
{
"type": "click_if_exists",
"target": "input[type='search'], input[placeholder*='검색']",
"description": "검색 입력창 존재 확인"
},
{
"type": "wait",
"duration": 500
},
{
"type": "capture",
"variable": "afterClearCount",
"selector": "table tbody tr",
"extract": "count"
}
],
"verify": {
"dataRestored": "afterClearCount should equal beforeSearchCount"
},
"expected": "전체 데이터 다시 표시"
},
{
"id": 12,
"name": "체크박스 선택",
"action": "click_if_exists",
"target": "first_row",
"expected": "첫 번째 행 체크박스 선택됨"
},
{
"id": 13,
"name": "전체 선택 체크박스",
"action": "click_if_exists",
"target": "select_all",
"expected": "모든 행 체크박스 선택됨"
},
{
"id": 14,
"name": "전체 선택 해제",
"action": "click_if_exists",
"target": "select_all",
"expected": "모든 행 체크박스 해제됨"
},
{
"id": 15,
"name": "필수 검증 #1: 엑셀 다운로드",
"action": "click_download",
"target": "엑셀 다운로드",
"checks": [
"버튼 클릭",
"Network API 호출 확인 (/api/v1/vendor-ledger/export)",
"Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"파일 다운로드 이벤트 발생",
"성공 토스트 메시지 (엑셀 파일이 다운로드되었습니다)"
],
"expected": "엑셀 파일 다운로드 완료"
},
{
"id": 16,
"name": "테이블 행 클릭 - 상세 페이지 이동",
"action": "click_if_exists",
"target": "table tbody tr:first-child",
"expected": "거래처원장 상세 페이지로 이동"
},
{
"id": 17,
"name": "상세 페이지 - URL 파라미터 확인",
"action": "verify_url",
"checks": [
"URL에 거래처 ID 포함",
"start_date 파라미터 포함",
"end_date 파라미터 포함"
],
"expected": "URL 파라미터 정상 전달"
},
{
"id": 18,
"name": "상세 페이지 - 헤더 확인",
"action": "verify_elements",
"checks": [
"거래처원장 상세 (거래명세서별) 타이틀",
"목록 버튼 존재"
],
"expected": "상세 페이지 헤더 정상 표시"
},
{
"id": 19,
"name": "상세 페이지 - 거래처 정보 카드 확인",
"action": "verify_vendor_info",
"checks": [
"회사명 표시",
"사업자등록번호 표시",
"대표자 표시",
"전화번호 표시",
"모바일 표시",
"팩스 표시",
"이메일 표시",
"주소 표시",
"기간 표시"
],
"expected": "거래처 정보 모두 표시"
},
{
"id": 20,
"name": "상세 페이지 - 요약 통계 확인",
"action": "verify_summary",
"checks": [
"이월잔액 표시",
"매출 표시 (녹색)",
"수금 표시 (파란색)",
"잔액 표시"
],
"expected": "4개 요약 통계 정상 표시"
},
{
"id": 21,
"name": "상세 페이지 - 판매/수금 내역 테이블 확인",
"action": "verify_transaction_table",
"checks": [
"일자 컬럼",
"적요 컬럼",
"판매 컬럼",
"수금 컬럼",
"잔액 컬럼",
"작업 컬럼"
],
"expected": "판매/수금 내역 테이블 정상 표시"
},
{
"id": 22,
"name": "상세 페이지 - 기간 변경",
"action": "click_if_exists",
"startDate": "2025-06-01",
"endDate": "2025-06-30",
"expected": "기간 변경 후 거래 내역 재조회",
"target": "input[type='date'], [class*='date-picker']"
},
{
"id": 23,
"name": "상세 페이지 - 거래 내역 데이터 변화 확인",
"action": "verify_transactions_update",
"checks": [
"테이블 데이터 갱신",
"요약 통계 값 갱신"
],
"expected": "변경된 기간의 데이터 표시"
},
{
"id": 24,
"name": "⚠️ 필수 검증: PDF 다운로드 전 페이지 스크린샷",
"description": "PDF 생성 전 페이지 상태를 스크린샷으로 캡처하여 CSS 문제 감지용 기준 이미지 확보",
"actions": [
{
"type": "screenshot",
"name": "pdf-preview-before-download",
"fullPage": true,
"savePath": "tests/e2e/results/hotfix/screenshots/",
"description": "PDF 생성 대상 페이지 전체 캡처"
},
{
"type": "screenshot",
"name": "pdf-content-area",
"selector": ".vendor-ledger-detail, .pdf-content, main",
"savePath": "tests/e2e/results/hotfix/screenshots/",
"description": "PDF 콘텐츠 영역만 캡처"
}
],
"verify": {
"screenshotCaptured": true,
"purpose": "PDF CSS 문제 감지를 위한 기준 이미지"
}
},
{
"id": "24-1",
"name": "⚠️ 필수 검증: PDF 다운로드 실행 및 파일 보관",
"description": "PDF 다운로드 후 파일을 지정 폴더에 보관하여 수동 검증 가능하게 함",
"actions": [
{
"type": "expectResponse",
"id": "pdf-download-response",
"urlPattern": "/api/v1/vendor-ledger/*/export-pdf",
"description": "PDF 다운로드 API 응답 대기 설정"
},
{
"type": "click_if_exists",
"target": "PDF 다운로드",
"description": "PDF 다운로드 버튼 클릭"
},
{
"type": "wait",
"duration": 3000,
"description": "PDF 생성 및 다운로드 대기"
},
{
"type": "assertResponse",
"id": "pdf-download-response",
"checks": {
"status": 200,
"contentType": "application/pdf"
}
},
{
"type": "saveDownloadedFile",
"targetPath": "tests/e2e/results/hotfix/pdf-samples/",
"fileNamePattern": "vendor-ledger-{vendorId}-{timestamp}.pdf",
"description": "다운로드된 PDF 파일을 지정 폴더에 보관"
}
],
"verify": {
"apiSuccess": true,
"fileDownloaded": true,
"fileSaved": "tests/e2e/results/hotfix/pdf-samples/"
}
},
{
"id": "24-2",
"name": "⚠️ PDF 파일 유효성 검증",
"description": "다운로드된 PDF 파일의 기본 유효성 검사",
"actions": [
{
"type": "verifyDownloadedFile",
"checks": {
"fileExists": true,
"fileSize": "> 1024",
"pdfSignature": "%PDF-",
"description": "PDF 파일 헤더 검증"
}
}
],
"verify": {
"pdfValid": true,
"minFileSize": "1KB 이상"
}
},
{
"id": "24-3",
"name": "📋 PDF 스타일 수동 확인 체크리스트",
"type": "manualVerification",
"description": "개발자가 다운로드된 PDF를 열어 시각적으로 확인해야 하는 항목",
"manualChecklist": [
{
"id": "css-1",
"item": "테이블 경계선이 올바르게 표시되는가?",
"category": "테이블 스타일"
},
{
"id": "css-2",
"item": "한글 폰트가 깨지지 않고 정상 표시되는가?",
"category": "폰트"
},
{
"id": "css-3",
"item": "숫자/금액 정렬이 올바른가? (우측 정렬)",
"category": "정렬"
},
{
"id": "css-4",
"item": "여백(margin/padding)이 적절한가?",
"category": "레이아웃"
},
{
"id": "css-5",
"item": "헤더/푸터가 각 페이지에 올바르게 표시되는가?",
"category": "페이지 구조"
},
{
"id": "css-6",
"item": "로고/이미지가 정상 표시되는가?",
"category": "이미지"
},
{
"id": "css-7",
"item": "페이지 나눔(page break)이 적절한 위치에서 발생하는가?",
"category": "페이지 나눔"
},
{
"id": "css-8",
"item": "배경색/강조색이 올바르게 적용되었는가?",
"category": "색상"
},
{
"id": "css-9",
"item": "텍스트가 잘리거나 겹치지 않는가?",
"category": "오버플로우"
},
{
"id": "css-10",
"item": "인쇄 시 레이아웃이 유지되는가?",
"category": "인쇄 호환성"
}
],
"outputFiles": {
"screenshot": "tests/e2e/results/hotfix/screenshots/pdf-preview-before-download-*.png",
"pdfFile": "tests/e2e/results/hotfix/pdf-samples/vendor-ledger-*.pdf"
},
"reportFlag": {
"requiresManualReview": true,
"message": "⚠️ PDF 스타일 수동 확인 필요 - 위 체크리스트 항목을 PDF 파일에서 직접 확인하세요"
}
},
{
"id": 25,
"name": "상세 페이지 - 작업 버튼 확인 (어음 항목)",
"action": "verify_action_buttons",
"checks": [
"어음 관련 항목에 수정 버튼(Pencil 아이콘) 존재",
"일반 항목에는 작업 버튼 없음"
],
"expected": "hasAction=true인 항목에만 버튼 표시"
},
{
"id": 26,
"name": "상세 페이지 - 목록 버튼 클릭",
"action": "click_if_exists",
"target": "목록",
"expected": "거래처원장 목록 페이지로 복귀"
},
{
"id": 27,
"name": "목록 페이지 복귀 확인",
"action": "verify_url",
"target": "/ko/accounting/vendor-ledger",
"expected": "목록 페이지 정상 표시"
},
{
"id": 28,
"name": "페이지네이션 동작 확인",
"action": "verify_pagination",
"checks": [
"현재 페이지 표시",
"전체 페이지 수 표시",
"페이지 이동 버튼 동작"
],
"expected": "페이지네이션 정상 동작"
}
],
"requiredVerifications": [
{
"id": 1,
"name": "파일 다운로드 (엑셀/PDF)",
"steps": [
15,
24,
"24-1",
"24-2",
"24-3"
],
"criteria": "Network API 호출 + 실제 파일 다운로드 + 성공 토스트 + PDF 스타일 검증"
},
{
"id": "PDF-STYLE",
"name": "PDF 스타일/CSS 검증",
"steps": [
24,
"24-1",
"24-2",
"24-3"
],
"criteria": "스크린샷 캡처 + PDF 파일 보관 + 수동 체크리스트 확인",
"manualReviewRequired": true,
"outputPaths": {
"screenshots": "tests/e2e/results/hotfix/screenshots/",
"pdfFiles": "tests/e2e/results/hotfix/pdf-samples/"
}
},
{
"id": 2,
"name": "등록/저장 버튼",
"steps": [],
"criteria": "해당 없음 (조회 전용 페이지)"
},
{
"id": 3,
"name": "검색/필터",
"steps": [
6,
7,
8,
9,
10,
11
],
"criteria": "기간 설정 및 검색 시 데이터 변화 확인"
},
{
"id": 4,
"name": "모달 등록 완료",
"steps": [],
"criteria": "해당 없음 (조회 전용 페이지)"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [
3
],
"criteria": "입력 필드, 동작 버튼, API 호출 확인"
}
],
"testData": {
"searchKeyword": "테스트",
"dateRange": {
"start": "2025-01-01",
"end": "2025-12-31"
},
"detailDateRange": {
"start": "2025-06-01",
"end": "2025-06-30"
}
},
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/vendor-ledger",
"description": "거래처원장 목록 조회"
},
{
"method": "GET",
"endpoint": "/api/v1/vendor-ledger/summary",
"description": "거래처원장 요약 통계 조회"
},
{
"method": "GET",
"endpoint": "/api/v1/vendor-ledger/{id}",
"description": "거래처원장 상세 조회"
},
{
"method": "GET",
"endpoint": "/api/v1/vendor-ledger/export",
"description": "거래처원장 엑셀 다운로드"
},
{
"method": "GET",
"endpoint": "/api/v1/vendor-ledger/{id}/export-pdf",
"description": "거래처원장 상세 PDF 다운로드"
}
]
}

View File

@@ -0,0 +1,576 @@
{
"id": "vendor-management",
"name": "거래처관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "회계관리 > 거래처관리 메뉴의 목록 조회, 필터, 검색, 상세 페이지 진입, 수정 및 저장 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"navigation": {
"targetUrl": "/accounting/vendors",
"urlPattern": "/accounting/vendors",
"menuHints": ["거래처관리", "거래처 관리", "회계관리"]
},
"menuNavigation": {
"level1": "회계관리",
"level2": "거래처관리",
"expectedUrl": "/accounting/vendors",
"searchWithinParent": true,
"closeOtherMenus": true
},
"menuNavigationEnhanced": {
"strategy": "scroll-and-search",
"description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지",
"level1": "회계관리",
"level2": "거래처관리",
"alternativeLevel1Names": ["회계관리", "회계 관리", "Accounting"],
"alternativeLevel2Names": ["거래처관리", "거래처 관리", "Vendors"],
"scrollConfig": {
"sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar",
"menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']",
"scrollStep": 200,
"maxScrollAttempts": 10,
"scrollDelay": 300
}
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"notes": {
"skip": ["등록 버튼 (추후 구현 예정)", "삭제 기능 (보류)"],
"focus": ["테이블 행 클릭 → 상세 페이지", "수정 모드 진입", "수정 후 저장"],
"uiNotes": [
"필터 드롭다운: Radix UI Select (button[role='combobox'])",
"체크박스: Radix UI Checkbox (button[role='checkbox'])",
"저장: 확인 다이얼로그 없이 직접 저장 후 목록으로 리다이렉트"
]
},
"steps": [
{
"id": "step-0",
"name": "사이드바 메뉴 전체 펼치기",
"description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비",
"actions": [
{
"type": "evaluate",
"script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})"
},
{ "type": "wait", "duration": 300 },
{
"type": "evaluate",
"script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()"
},
{ "type": "wait", "duration": 2000 }
]
},
{
"id": "step-1",
"name": "2단계 메뉴 진입: 회계관리 > 거래처관리",
"description": "사이드바를 스크롤하며 회계관리 > 거래처관리 메뉴를 찾아 클릭",
"actions": [
{
"type": "scrollAndFind",
"target": "회계관리",
"alternativeTexts": ["회계관리", "회계 관리", "Accounting"],
"scrollContainer": "sidebar",
"maxAttempts": 10,
"description": "스크롤하며 회계관리 메뉴 찾기"
},
{ "type": "click_if_exists", "target": "회계관리", "description": "회계관리 메뉴 클릭" },
{ "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" },
{
"type": "scrollAndFind",
"target": "거래처관리",
"alternativeTexts": ["거래처관리", "거래처 관리", "Vendors"],
"scrollContainer": "submenu",
"maxAttempts": 5,
"description": "서브메뉴에서 거래처관리 찾기"
},
{ "type": "click_if_exists", "target": "거래처관리", "description": "거래처관리 메뉴 클릭" },
{ "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 }
],
"expect": {
"url": "/accounting/vendors",
"pageTitle": "거래처관리",
"elements": ["통계 카드", "테이블", "검색창"]
},
"verification": [
"회계관리 메뉴가 펼쳐졌는지 확인",
"거래처관리 서브메뉴 클릭 성공",
"404 에러 없이 페이지 로드 완료"
]
},
{
"id": 3,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"검색 입력 필드 존재 (placeholder: 거래처명, 거래처코드, 사업자번호 검색...)",
"필터 드롭다운 존재 (구분, 신용등급, 거래등급, 악성채권 - Radix combobox)",
"테이블 데이터 표시",
"API 호출 확인"
],
"expected": "정상 페이지 (목업 아님)"
},
{
"id": 4,
"name": "통계 카드 확인",
"action": "verify_elements",
"checks": [
"전체 거래처 카드 표시",
"매출 거래처 카드 표시",
"매입 거래처 카드 표시"
],
"expected": "3개 통계 카드 모두 표시"
},
{
"id": 5,
"name": "테이블 구조 확인",
"action": "verify_table",
"checks": [
"체크박스 컬럼 (Radix button[role='checkbox'])",
"번호 컬럼",
"구분 컬럼 (매출/매입/매입매출)",
"거래처명 컬럼",
"매입 결제일 컬럼",
"매출 결제일 컬럼",
"신용등급 컬럼",
"거래등급 컬럼",
"미수금 컬럼",
"악성채권 컬럼"
],
"expected": "10개 컬럼 존재"
},
{
"id": 6,
"name": "⚠️ 필수 검증: 검색 기능",
"description": "검색어 입력 후 테이블 데이터가 필터링되는지 확인",
"actions": [
{
"type": "evaluate",
"script": "(() => { const c = document.querySelectorAll('table tbody tr').length; window.__e2e_beforeSearch = c; return 'beforeSearch=' + c; })()",
"description": "검색 전 행 수 저장"
},
{ "type": "fill", "target": "input[placeholder*='검색']", "value": "가우스", "description": "검색어 '가우스' 입력" },
{ "type": "wait", "duration": 1000, "description": "검색 결과 대기" },
{
"type": "evaluate",
"script": "(() => { const c = document.querySelectorAll('table tbody tr').length; return 'afterSearch=' + c + ', filtered=' + (c < (window.__e2e_beforeSearch||999)); })()",
"description": "검색 후 행 수 확인"
}
],
"verify": {
"searchApplied": true,
"tableContains": "가우스",
"dataFiltered": "검색어에 맞는 거래처만 필터링되어야 함"
}
},
{
"id": 7,
"name": "검색 결과 데이터 검증",
"description": "검색 결과의 각 행에 검색어가 포함되어 있는지 확인",
"actions": [
{
"type": "verify_text",
"target": "table tbody",
"text": "가우스",
"description": "테이블에 가우스 텍스트 존재 확인"
}
],
"verify": {
"allRowsContain": "가우스",
"verifyMethod": "테이블의 모든 행이 검색어 '가우스'를 포함하는지 확인"
}
},
{
"id": 8,
"name": "검색 초기화 및 복원 확인",
"description": "검색어 삭제 후 전체 목록 복원 확인",
"actions": [
{
"type": "evaluate",
"script": "(() => { const inp = document.querySelector('input[placeholder*=\"검색\"]'); if(inp){ const nset = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value').set; nset.call(inp,''); inp.dispatchEvent(new Event('input',{bubbles:true})); inp.dispatchEvent(new Event('change',{bubbles:true})); return 'cleared'; } return 'not found'; })()",
"description": "검색어 삭제"
},
{ "type": "wait", "duration": 1000, "description": "목록 복원 대기" },
{
"type": "evaluate",
"script": "(() => { const c = document.querySelectorAll('table tbody tr').length; return 'restored rows=' + c + ', restored=' + (c >= (window.__e2e_beforeSearch||1)); })()",
"description": "복원 후 행 수 확인"
}
],
"verify": {
"dataRestored": true,
"rowCountRestored": "검색 전과 유사한 행 수로 복원"
}
},
{
"id": 9,
"name": "구분 필터 테스트 (매출)",
"description": "첫 번째 Radix combobox를 클릭하여 '매출' 옵션 선택",
"actions": [
{
"type": "evaluate",
"script": "(async () => { const cbs = document.querySelectorAll('button[role=\"combobox\"]'); if(!cbs[0]) return 'combobox not found'; cbs[0].click(); await new Promise(r=>setTimeout(r,500)); const opt = Array.from(document.querySelectorAll('[role=\"option\"]')).find(o=>o.innerText?.trim()==='매출'); if(opt){ opt.click(); await new Promise(r=>setTimeout(r,1000)); return 'selected 매출, rows=' + document.querySelectorAll('table tbody tr').length; } return 'option 매출 not found'; })()",
"description": "구분 필터에서 매출 선택"
}
],
"expected": "매출 거래처만 필터링"
},
{
"id": 10,
"name": "구분 필터 초기화",
"description": "구분 필터를 '전체'로 되돌리기",
"actions": [
{
"type": "evaluate",
"script": "(async () => { const cbs = document.querySelectorAll('button[role=\"combobox\"]'); if(!cbs[0]) return 'combobox not found'; cbs[0].click(); await new Promise(r=>setTimeout(r,500)); const opt = Array.from(document.querySelectorAll('[role=\"option\"]')).find(o=>o.innerText?.trim()==='전체'); if(opt){ opt.click(); await new Promise(r=>setTimeout(r,1000)); return 'selected 전체, rows=' + document.querySelectorAll('table tbody tr').length; } return 'option 전체 not found'; })()",
"description": "구분 필터를 전체로 초기화"
}
],
"expected": "전체 데이터 다시 표시"
},
{
"id": 11,
"name": "테이블 행 클릭 - 상세 페이지 이동",
"description": "목록 페이지에서 첫 번째 행을 클릭하여 상세 페이지로 이동",
"actions": [
{
"type": "evaluate",
"script": "(() => { const url = window.location.href; if(!url.includes('/accounting/vendors') || url.includes('mode=')) return 'NOT on list page: ' + url; return 'on list page'; })()",
"description": "목록 페이지 확인"
},
{
"type": "evaluate",
"script": "(async () => { const rows = document.querySelectorAll('table tbody tr'); if(rows.length===0) return 'no rows'; rows[0].click(); await new Promise(r=>setTimeout(r,2000)); return 'clicked row, url=' + window.location.href; })()",
"description": "첫 번째 행 클릭"
}
],
"expected": "거래처 상세 페이지로 이동"
},
{
"id": 12,
"name": "상세 페이지 - URL 확인",
"action": "verify_url",
"target": "/accounting/vendors/\\d+",
"checks": [
"URL에 거래처 ID 포함 (/accounting/vendors/{id})"
],
"expected": "URL 정상 전달"
},
{
"id": 13,
"name": "상세 페이지 - 헤더 확인",
"action": "verify_elements",
"checks": [
"거래처 상세 타이틀",
"목록 버튼 존재",
"삭제 버튼 존재",
"수정 버튼 존재"
],
"expected": "상세 페이지 헤더 정상 표시"
},
{
"id": 14,
"name": "상세 페이지 - 기본 정보 카드 확인",
"action": "verify_detail_info",
"checks": [
"사업자등록번호 필드",
"거래처코드 필드",
"거래처명 필드",
"대표자명 필드",
"업태 필드",
"업종 필드"
],
"expected": "기본 정보 모두 표시 (읽기 전용)"
},
{
"id": 15,
"name": "상세 페이지 - 연락처 정보 확인",
"action": "verify_detail_info",
"checks": [
"주소 필드",
"전화번호 필드",
"모바일 필드",
"팩스 필드",
"이메일 필드"
],
"expected": "연락처 정보 모두 표시"
},
{
"id": 16,
"name": "상세 페이지 - 담당자 정보 확인",
"action": "verify_detail_info",
"checks": [
"담당자명 필드",
"담당자 전화 필드",
"시스템 관리자 필드"
],
"expected": "담당자 정보 모두 표시"
},
{
"id": 17,
"name": "상세 페이지 - 회사 정보 확인",
"action": "verify_detail_info",
"checks": [
"회사 로고 영역",
"매입 결제일 필드",
"매출 결제일 필드"
],
"expected": "회사 정보 모두 표시"
},
{
"id": 18,
"name": "상세 페이지 - 신용/거래 정보 확인",
"action": "verify_detail_info",
"checks": [
"신용등급 필드",
"거래등급 필드",
"세금계산서 이메일 필드",
"입금계좌 은행 필드",
"계좌 필드",
"예금주 필드"
],
"expected": "신용/거래 정보 모두 표시"
},
{
"id": 19,
"name": "상세 페이지 - 추가 정보 확인",
"action": "verify_detail_info",
"checks": [
"미수금 필드",
"악성채권 금액 필드",
"악성채권 상태 필드"
],
"expected": "추가 정보 모두 표시"
},
{
"id": 20,
"name": "상세 페이지 - 메모 섹션 확인",
"action": "verify_elements",
"checks": [
"메모 카드 존재",
"메모 리스트 또는 빈 메시지"
],
"expected": "메모 섹션 표시"
},
{
"id": 21,
"name": "핵심 테스트: 수정 버튼 클릭",
"action": "click_if_exists",
"target": "수정",
"expected": "수정 모드로 전환 (URL에 ?mode=edit 추가)"
},
{
"id": 22,
"name": "수정 모드 - URL 확인",
"action": "verify_url",
"target": "mode=edit",
"checks": [
"URL에 mode=edit 파라미터 포함"
],
"expected": "수정 모드 URL 정상"
},
{
"id": 23,
"name": "수정 모드 - 필드 편집 가능 확인",
"action": "verify_edit_mode",
"checks": [
"거래처명 입력 가능",
"대표자명 입력 가능",
"전화번호 입력 가능",
"이메일 입력 가능",
"저장 버튼 존재",
"취소 버튼 존재"
],
"expected": "수정 모드에서 필드 편집 가능"
},
{
"id": 24,
"name": "핵심 테스트: 거래처명 수정",
"description": "거래처명 input 필드에 테스트 접미사 추가. input에 id/name이 없으므로 value 기반 탐색 필요",
"actions": [
{
"type": "evaluate",
"script": "(async () => { const inputs = document.querySelectorAll('input:not([type=\"hidden\"]):not([type=\"checkbox\"])'); let target = null; inputs.forEach(inp => { if(inp.value && inp.value.length > 1 && !inp.value.includes('수정테스트') && !inp.placeholder.includes('자동생성') && !inp.placeholder.includes('000-00')) { if(!target) target = inp; } }); if(!target) { for(const inp of inputs) { if(inp.value && inp.value.length > 1 && !inp.placeholder.includes('자동생성') && !inp.placeholder.includes('000-00')) { target = inp; break; } } } if(target) { const nset = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value').set; const origVal = target.value; nset.call(target, origVal + ' (수정테스트)'); target.dispatchEvent(new Event('input',{bubbles:true})); target.dispatchEvent(new Event('change',{bubbles:true})); window.__e2e_origVendorName = origVal; return 'modified: ' + origVal + ' → ' + target.value; } return 'no editable input found'; })()",
"description": "첫 번째 편집 가능 필드(거래처명)에 접미사 추가"
}
],
"expected": "거래처명에 ' (수정테스트)' 추가"
},
{
"id": 25,
"name": "핵심 테스트: 저장 버튼 클릭",
"description": "저장 버튼 클릭. 이 페이지는 다이얼로그 없이 직접 저장 후 목록으로 리다이렉트됨",
"actions": [
{
"type": "evaluate",
"script": "(() => { window.__e2e_urlBeforeSave = window.location.href; return 'saved url: ' + window.__e2e_urlBeforeSave; })()",
"description": "저장 전 URL 기록"
},
{ "type": "click_if_exists", "target": "저장", "description": "저장 버튼 클릭" },
{ "type": "wait", "duration": 2000, "description": "저장 처리 대기" }
],
"expected": "저장 완료 후 목록 페이지로 리다이렉트"
},
{
"id": 26,
"name": "필수 검증 #2: 저장 완료 확인",
"description": "저장 후 URL 변경 및 에러 여부 확인 (다이얼로그 없이 직접 저장 방식)",
"actions": [
{
"type": "evaluate",
"script": "(() => { const url = window.location.href; const isListPage = url.includes('/accounting/vendors') && !url.includes('mode='); const hasError = document.body.innerText.includes('404') || document.body.innerText.includes('500') || document.body.innerText.includes('Not Found'); const urlChanged = url !== window.__e2e_urlBeforeSave; return JSON.stringify({ url, isListPage, hasError, urlChanged, result: isListPage && !hasError ? 'PASS' : 'FAIL' }); })()",
"description": "저장 후 목록 페이지 복귀 및 에러 없음 확인"
}
],
"expected": "목록 페이지로 복귀, 에러 없음"
},
{
"id": 27,
"name": "수정 결과 확인 - 목록에서 검증",
"description": "목록 페이지에서 수정된 거래처명 확인",
"actions": [
{
"type": "evaluate",
"script": "(() => { const found = document.body.innerText.includes('수정테스트'); const rows = document.querySelectorAll('table tbody tr').length; return JSON.stringify({ modifiedVisible: found, rowCount: rows, result: found ? 'PASS: 수정된 데이터 목록에 반영' : 'WARN: 수정 텍스트 미표시 (페이지네이션 또는 정렬 영향)' }); })()",
"description": "목록에서 수정된 거래처 확인"
}
],
"expected": "수정된 거래처명이 목록에 표시"
},
{
"id": 28,
"name": "원래 값 복원 - 수정된 거래처 클릭",
"description": "수정된 거래처를 찾아 클릭하여 상세 페이지 진입",
"actions": [
{
"type": "evaluate",
"script": "(async () => { const rows = document.querySelectorAll('table tbody tr'); let target = null; rows.forEach(row => { if(row.innerText.includes('수정테스트')) target = row; }); if(target){ target.click(); await new Promise(r=>setTimeout(r,2000)); return 'clicked modified vendor, url=' + window.location.href; } rows[0]?.click(); await new Promise(r=>setTimeout(r,2000)); return 'modified vendor not found in current page, clicked first row. url=' + window.location.href; })()",
"description": "수정된 거래처 행 클릭"
}
],
"expected": "수정된 거래처 상세 페이지 진입"
},
{
"id": 29,
"name": "원래 값 복원 - 수정 버튼 클릭",
"actions": [
{
"type": "evaluate",
"script": "(async () => { const btn = Array.from(document.querySelectorAll('button')).find(b=>b.innerText?.trim()==='수정'); if(btn){ btn.click(); await new Promise(r=>setTimeout(r,1500)); return 'edit mode, url=' + window.location.href; } return 'edit button not found'; })()",
"description": "수정 모드 진입"
}
],
"expected": "수정 모드로 전환"
},
{
"id": 30,
"name": "원래 값 복원 - 거래처명 원복",
"description": "거래처명에서 ' (수정테스트)' 제거",
"actions": [
{
"type": "evaluate",
"script": "(async () => { const inputs = document.querySelectorAll('input:not([type=\"hidden\"]):not([type=\"checkbox\"])'); let restored = false; inputs.forEach(inp => { if(inp.value.includes('수정테스트')){ const nset = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value').set; const newVal = inp.value.replace(' (수정테스트)',''); nset.call(inp, newVal); inp.dispatchEvent(new Event('input',{bubbles:true})); inp.dispatchEvent(new Event('change',{bubbles:true})); restored = true; } }); return restored ? 'restored' : 'no field with 수정테스트 found'; })()",
"description": "접미사 제거하여 원래 값 복원"
}
],
"expected": "거래처명에서 ' (수정테스트)' 제거"
},
{
"id": 31,
"name": "원래 값 복원 - 저장",
"description": "복원 저장 (다이얼로그 없이 직접 저장)",
"actions": [
{
"type": "evaluate",
"script": "(async () => { const btn = Array.from(document.querySelectorAll('button')).find(b=>b.innerText?.trim()==='저장'); if(btn){ btn.click(); await new Promise(r=>setTimeout(r,2000)); return 'saved, url=' + window.location.href; } return 'save button not found'; })()",
"description": "저장 버튼 클릭"
}
],
"expected": "원래 값으로 복원 완료, 목록으로 리다이렉트"
},
{
"id": 32,
"name": "원래 값 복원 - 완료 확인",
"description": "복원 후 목록 페이지에서 수정테스트 텍스트 제거 확인",
"actions": [
{
"type": "evaluate",
"script": "(() => { const url = window.location.href; const isListPage = url.includes('/accounting/vendors') && !url.includes('mode='); const stillModified = document.body.innerText.includes('수정테스트'); return JSON.stringify({ url, isListPage, stillModified, result: isListPage && !stillModified ? 'PASS: 원복 완료' : 'WARN: 원복 확인 필요' }); })()",
"description": "목록에서 원복 확인"
}
],
"expected": "수정테스트 텍스트 제거됨"
},
{
"id": 33,
"name": "목록 페이지 최종 확인",
"action": "verify_url",
"target": "/accounting/vendors",
"expected": "거래처관리 목록 페이지 정상 표시"
},
{
"id": 34,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body",
"expected": "심각한 콘솔 에러 없음"
}
],
"requiredVerifications": [
{
"id": 1,
"name": "등록/저장 버튼",
"steps": [25, 26],
"criteria": "저장 클릭 → 목록 리다이렉트 + 에러 없음 + 데이터 반영"
},
{
"id": 2,
"name": "등록 버튼 (신규)",
"steps": [],
"criteria": "보류 - 추후 구현 예정"
},
{
"id": 3,
"name": "검색/필터",
"steps": [6, 7, 8, 9, 10],
"criteria": "검색 및 필터 시 데이터 변화 확인"
},
{
"id": 4,
"name": "삭제 기능",
"steps": [],
"criteria": "보류 - 테스트 대상에서 제외"
},
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [3],
"criteria": "입력 필드, 동작 버튼, API 호출 확인"
}
],
"testData": {
"searchKeyword": "가우스",
"editSuffix": " (수정테스트)"
},
"expectedAPIs": [
{
"method": "GET",
"endpoint": "/api/v1/clients",
"description": "거래처 목록 조회"
},
{
"method": "GET",
"endpoint": "/api/v1/clients/{id}",
"description": "거래처 상세 조회"
},
{
"method": "PUT",
"endpoint": "/api/v1/clients/{id}",
"description": "거래처 수정"
}
]
}

View File

@@ -0,0 +1,528 @@
{
"enabled": true,
"id": "withdrawal-management",
"name": "출금관리 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "출금관리 목록 조회, 계정과목명 일괄변경, 상세 수정 기능 E2E 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"url": "/ko/accounting/withdrawals",
"navigation": {
"targetUrl": "/accounting/withdrawals",
"urlPattern": "/accounting/withdrawals|/ko/accounting/withdrawals",
"menuHints": ["출금관리", "출금 관리", "회계관리"]
},
"menuNavigation": {
"level1": "회계관리",
"level2": "출금관리",
"expectedUrl": "/ko/accounting/withdrawals",
"searchWithinParent": true,
"closeOtherMenus": true
},
"menuNavigationEnhanced": {
"strategy": "scroll-and-search",
"sidebarSelector": ".sidebar-scroll, [data-sidebar], nav[role='navigation']",
"scrollBehavior": {
"direction": "down",
"step": 200,
"maxScrolls": 10,
"waitAfterScroll": 300
},
"level1": {
"text": "회계관리",
"expandable": true,
"waitAfterClick": 500
},
"level2": {
"text": "출금관리",
"waitAfterClick": 300
},
"fallbackUrl": "/ko/accounting/withdrawals",
"verification": {
"urlContains": "/accounting/withdrawals",
"timeout": 5000
}
},
"timeout": 60000,
"tags": ["accounting", "withdrawal", "crud"],
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"steps": [
{
"id": "step-0",
"name": "사이드바 메뉴 전체 펼치기",
"description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비",
"actions": [
{
"type": "evaluate",
"script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})"
},
{ "type": "wait", "duration": 300 },
{
"type": "evaluate",
"script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()"
},
{ "type": "wait", "duration": 2000 }
],
"expect": {
"sidebarReady": true
}
},
{
"id": "step-1",
"name": "출금관리 메뉴 진입",
"description": "회계관리 > 출금관리 메뉴로 이동 (scrollAndFind 패턴 사용)",
"menuNavigation": {
"useEnhanced": true,
"scrollAndFind": {
"level1": {
"text": "회계관리",
"scrollUntilVisible": true,
"clickToExpand": true,
"waitAfterClick": 500
},
"level2": {
"text": "출금관리",
"scrollUntilVisible": true,
"waitAfterClick": 300
}
},
"fallback": {
"useDirectUrl": true,
"url": "/ko/accounting/withdrawals"
}
},
"expect": {
"url": "/accounting/withdrawals",
"visible": ["출금관리", "총 출금"]
}
},
{
"id": "step-2",
"name": "목록 페이지 구조 확인",
"description": "테이블 및 필터 요소 확인",
"expect": {
"visible": ["출금일", "출금계좌", "받는분", "출금금액", "거래처", "적요", "출금유형"],
"elements": {
"statisticsCards": ["총 출금", "당월 출금", "거래처 미설정", "출금유형 미설정"],
"filters": ["계정과목명", "저장", "새로고침"],
"pagination": true
}
}
},
{
"id": "step-3",
"name": "계정과목명 드롭다운 옵션 확인",
"description": "계정과목명 일괄변경 드롭다운 옵션 검증",
"actions": [
{ "type": "click_if_exists", "target": "계정과목명 드롭다운", "description": "드롭다운 열기" }
],
"expect": {
"options": ["미설정", "매입대금", "급여", "임차료", "수도광열비", "통신비", "소모품비", "운반비", "차량유지비", "보험료", "세금과공과", "이자비용", "수수료", "기타"]
},
"note": "출금유형 옵션은 비용 계정 기준이므로 입금관리와 다름"
},
{
"id": "step-4",
"name": "체크박스 선택 후 계정과목명 일괄변경",
"description": "테이블 행 선택 후 계정과목명 일괄변경 저장",
"actions": [
{ "type": "click_if_exists", "target": "첫 번째 행 체크박스", "description": "행 선택" },
{ "type": "click_if_exists", "target": "계정과목명 드롭다운", "description": "드롭다운 열기" },
{ "type": "click_if_exists", "target": "매입대금", "description": "매입대금 선택" },
{ "type": "click_if_exists", "target": "저장", "description": "저장 버튼 클릭" }
],
"expect": {
"dialog": "확인 다이얼로그 표시",
"dialogMessage": "1개의 출금 유형을 매입대금(으)로 모두 변경하시겠습니까?",
"toast": "변경 완료 메시지"
},
"checks": [
"API 호출 확인 (PUT /api/v1/withdrawals/batch-update-account)",
"성공 토스트 메시지",
"URL 유지 확인"
]
},
{
"id": "step-4-1",
"name": "⚠️ 필수 검증: 계정과목명 변경 데이터 반영 확인",
"note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!",
"description": "저장 후 테이블에서 변경된 출금유형 값 확인",
"expect": {
"tableCell": {
"row": 1,
"column": "출금유형",
"value": "매입대금"
}
},
"checks": [
"선택한 행의 출금유형 컬럼 값 확인",
"변경 전 값과 비교 (예: 미설정 → 매입대금)",
"페이지 새로고침 후에도 변경된 값 유지 확인",
"API 응답과 UI 표시값 일치 확인"
],
"knownBugReference": "BUG-DEPOSIT-20260115-001 (입금관리 동일 버그 확인 필요)"
},
{
"id": "step-5",
"name": "출금 상세 페이지 이동",
"description": "테이블 행 클릭하여 상세 페이지로 이동",
"actions": [
{ "type": "click_if_exists", "target": "테이블 첫 번째 행", "description": "행 클릭 (체크박스 제외 영역)" }
],
"expect": {
"url": "/accounting/withdrawals/{id}",
"visible": ["출금 상세", "기본 정보", "목록", "삭제", "수정"]
}
},
{
"id": "step-6",
"name": "상세 페이지 읽기 모드 필드 확인",
"description": "수정 전 필드들이 비활성화 상태인지 확인",
"expect": {
"fields": [
{ "name": "출금일", "disabled": true },
{ "name": "출금계좌", "disabled": true },
{ "name": "받는분", "disabled": true },
{ "name": "출금금액", "disabled": true },
{ "name": "적요", "disabled": true },
{ "name": "거래처", "disabled": true },
{ "name": "출금 유형", "disabled": true }
]
}
},
{
"id": "step-7",
"name": "수정 모드 전환",
"description": "수정 버튼 클릭하여 편집 모드로 전환",
"click": "수정",
"expect": {
"url": "/accounting/withdrawals/{id}?mode=edit",
"visible": ["출금 수정", "취소", "저장"],
"notVisible": ["목록", "삭제", "수정"]
}
},
{
"id": "step-8",
"name": "수정 모드 필드 활성화 검증",
"description": "수정 가능한 필드와 불가능한 필드 확인",
"expect": {
"fields": [
{ "name": "출금일", "disabled": true, "note": "은행데이터 - 수정 불가" },
{ "name": "출금계좌", "disabled": true, "note": "은행데이터 - 수정 불가" },
{ "name": "받는분", "disabled": true, "note": "은행데이터 - 수정 불가" },
{ "name": "출금금액", "disabled": true, "note": "은행데이터 - 수정 불가" },
{ "name": "적요", "disabled": false, "editable": true },
{ "name": "거래처", "disabled": false, "type": "combobox", "editable": true },
{ "name": "출금 유형", "disabled": false, "type": "combobox", "editable": true }
]
}
},
{
"id": "step-9",
"name": "거래처 드롭다운 옵션 확인",
"description": "거래처 선택 드롭다운 옵션 검증",
"actions": [
{ "type": "click_if_exists", "target": "거래처 드롭다운", "description": "드롭다운 열기" }
],
"expect": {
"options": ["거래처테스트", "아크더레드", "코브라브릿지", "가우스전자", "아크아크"],
"note": "거래처 옵션은 시스템 공통 데이터"
}
},
{
"id": "step-10",
"name": "출금 유형 드롭다운 옵션 확인",
"description": "출금 유형 선택 드롭다운 옵션 검증",
"actions": [
{ "type": "click_if_exists", "target": "출금 유형 드롭다운", "description": "드롭다운 열기" }
],
"expect": {
"options": ["미설정", "매입대금", "급여", "임차료", "수도광열비", "통신비", "소모품비", "운반비", "차량유지비", "보험료", "세금과공과", "이자비용", "수수료", "기타"]
}
},
{
"id": "step-11",
"name": "수정 데이터 입력",
"description": "수정 가능한 필드에 테스트 데이터 입력",
"form": {
"fields": [
{ "name": "적요", "type": "text", "value": "테스트 적요 수정" }
]
},
"actions": [
{ "type": "click_if_exists", "target": "거래처 드롭다운", "description": "거래처 드롭다운 열기" },
{ "type": "click_if_exists", "target": "거래처테스트", "description": "거래처 선택" },
{ "type": "click_if_exists", "target": "출금 유형 드롭다운", "description": "출금 유형 드롭다운 열기" },
{ "type": "click_if_exists", "target": "매입대금", "description": "매입대금 선택" }
]
},
{
"id": "step-12",
"name": "저장 및 결과 확인",
"description": "저장 버튼 클릭 후 데이터 반영 확인",
"click": "저장",
"expect": {
"toast": "저장 완료 메시지",
"url": "/accounting/withdrawals/{id}",
"mode": "view"
},
"checks": [
"API 호출 확인 (PUT /api/v1/withdrawals/{id})",
"성공 토스트 메시지",
"URL 유지 확인 (에러 페이지 이동 금지)"
]
},
{
"id": "step-12-1",
"name": "⚠️ 필수 검증: 수정 데이터 반영 확인",
"note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!",
"description": "저장 후 상세 페이지에서 변경된 값 확인",
"expect": {
"fields": [
{ "name": "적요", "value": "테스트 적요 수정" },
{ "name": "거래처", "value": "거래처테스트" },
{ "name": "출금 유형", "value": "매입대금" }
]
}
},
{
"id": "step-13",
"name": "취소 버튼 동작 확인",
"description": "수정 모드에서 취소 버튼 동작 검증",
"actions": [
{ "type": "click_if_exists", "target": "수정", "description": "수정 모드 진입" },
{ "type": "click_if_exists", "target": "취소", "description": "취소 버튼 클릭" }
],
"expect": {
"url": "/accounting/withdrawals/{id}",
"mode": "view",
"visible": ["출금 상세", "목록", "삭제", "수정"]
}
},
{
"id": "step-14",
"name": "목록 버튼 동작 확인",
"description": "목록 버튼 클릭하여 목록 페이지로 이동",
"click": "목록",
"expect": {
"url": "/accounting/withdrawals",
"visible": ["출금관리", "총 출금"]
}
},
{
"id": "step-15",
"name": "필터 드롭다운 검증",
"description": "목록 페이지 필터 드롭다운 옵션 확인",
"note": "3개의 필터 드롭다운 존재 (거래처, 출금유형, 정렬)",
"expect": {
"filters": [
{ "name": "거래처 필터", "default": "전체" },
{ "name": "출금유형 필터", "default": "전체" },
{ "name": "정렬", "default": "최신순", "options": ["최신순", "등록순", "금액 높은순", "금액 낮은순"] }
]
}
},
{
"id": "step-16",
"name": "날짜 필터 검증",
"description": "날짜 필터 버튼 동작 확인",
"actions": [
{ "type": "click_if_exists", "target": "당해년도", "description": "당해년도 버튼 클릭" }
],
"expect": {
"dateRange": {
"start": "2026-01-01",
"end": "2026-12-31"
}
}
},
{
"id": "step-17",
"name": "페이지네이션 동작 확인",
"description": "페이지네이션 버튼 동작 검증 (데이터 존재 시)",
"condition": "데이터가 20건 이상인 경우에만 실행",
"expect": {
"pagination": {
"itemsPerPage": 20,
"currentPage": 1
}
},
"actions": [
{ "type": "click_if_exists", "target": "다음", "description": "다음 페이지로 이동" }
],
"expectAfterAction": {
"currentPage": 2
}
}
],
"skipTests": [
{
"id": "delete-button",
"name": "삭제 버튼 테스트",
"reason": "사용자 요청에 따라 테스트 제외"
}
],
"knownBugs": [
{
"id": "BUG-DEPOSIT-20260115-001",
"description": "계정과목명 일괄변경 시 API 오류 발생 (입금관리)",
"errorMessage": "존재하지 않는 URI 또는 데이터",
"relatedSteps": ["step-4-1"],
"note": "출금관리에서도 동일한 버그가 존재할 수 있으므로 step-4-1에서 검증 필수"
},
{
"id": "BUG-SALES-20260115-001",
"description": "계정과목명 일괄변경 시 토스트 성공 표시되나 실제 데이터 미변경 (매출관리)",
"relatedSteps": ["step-4-1"],
"note": "유사한 UI 패턴이므로 동일 버그 가능성 있음"
}
],
"criticalValidationChecklist": [
{
"id": 1,
"name": "파일 다운로드 검증",
"trigger": "다운로드, Export 버튼 발견 시",
"checks": ["Network API 호출", "실제 파일 다운로드"],
"note": "Console LOG만으로 PASS 판정 금지"
},
{
"id": 2,
"name": "등록/저장 버튼 검증",
"trigger": "등록, 저장, 제출 버튼 클릭 시",
"checks": ["URL 유지 확인", "에러 페이지 없음", "성공 토스트"],
"note": "에러 페이지 이동 감지 필수"
},
{
"id": 3,
"name": "일괄변경 데이터 반영 검증",
"trigger": "계정과목명 저장 후",
"checks": ["테이블 데이터 변경 확인", "새로고침 후 유지 확인"],
"note": "토스트만 확인하면 불충분"
},
{
"id": 4,
"name": "목업 페이지 감지",
"trigger": "페이지 로드 시",
"checks": ["입력 필드 존재", "동작하는 버튼 존재", "API 호출 여부"],
"note": "버튼만 있고 동작 안하면 목업"
}
],
"testData": {
"sampleWithdrawal": {
"date": "2025-12-28",
"account": "운영계좌",
"recipient": "홍길동",
"amount": "1,500,000",
"vendor": "",
"description": "급여 지급",
"withdrawalType": "미설정"
},
"modifiedData": {
"description": "테스트 적요 수정",
"vendor": "거래처테스트",
"withdrawalType": "매입대금"
}
},
"pageStructure": {
"listPage": {
"url": "/accounting/withdrawals",
"title": "출금관리",
"statistics": ["총 출금", "당월 출금", "거래처 미설정", "출금유형 미설정"],
"tableColumns": ["checkbox", "출금일", "출금계좌", "받는분", "출금금액", "거래처", "적요", "출금유형", "action"],
"batchUpdate": {
"label": "계정과목명",
"saveButton": "저장"
},
"filters": ["거래처", "출금유형", "정렬"],
"dateFilters": ["당해년도", "전전월", "전월", "당월", "어제", "오늘"]
},
"detailPage": {
"url": "/accounting/withdrawals/{id}",
"title": "출금 상세",
"buttons": ["목록", "삭제", "수정"],
"fields": {
"readOnly": ["출금일", "출금계좌", "받는분", "출금금액"],
"editable": ["적요", "거래처", "출금 유형"]
}
},
"editPage": {
"url": "/accounting/withdrawals/{id}?mode=edit",
"title": "출금 수정",
"buttons": ["취소", "저장"]
}
},
"dropdownOptions": {
"accountSubject": {
"label": "계정과목명",
"options": ["미설정", "매입대금", "급여", "임차료", "수도광열비", "통신비", "소모품비", "운반비", "차량유지비", "보험료", "세금과공과", "이자비용", "수수료", "기타"],
"note": "출금유형은 비용 계정 기준 (입금유형과 다름)"
},
"withdrawalType": {
"label": "출금 유형",
"options": ["미설정", "매입대금", "급여", "임차료", "수도광열비", "통신비", "소모품비", "운반비", "차량유지비", "보험료", "세금과공과", "이자비용", "수수료", "기타"]
},
"vendor": {
"label": "거래처",
"options": ["거래처테스트", "아크더레드", "코브라브릿지", "가우스전자", "아크아크"]
},
"sortOrder": {
"label": "정렬",
"options": ["최신순", "등록순", "금액 높은순", "금액 낮은순"]
}
},
"comparisonWithDeposit": {
"differences": [
{
"field": "계정과목/유형 옵션",
"deposit": "매출대금, 선수금, 가수금, 임대수익, 이자수익, 보증금 반환, 차입금, 자본금, 부가세 환급, 기타 (수입 계정)",
"withdrawal": "매입대금, 급여, 임차료, 수도광열비, 통신비, 소모품비, 운반비, 차량유지비, 보험료, 세금과공과, 이자비용, 수수료, 기타 (비용 계정)"
},
{
"field": "테이블 컬럼명",
"deposit": "입금일, 입금계좌, 입금자명, 입금금액, 입금유형",
"withdrawal": "출금일, 출금계좌, 받는분, 출금금액, 출금유형"
},
{
"field": "통계 카드",
"deposit": "총 입금, 당월 입금, 입금유형 미설정",
"withdrawal": "총 출금, 당월 출금, 출금유형 미설정"
}
],
"similarities": [
"페이지 구조 (목록 → 상세 → 수정)",
"계정과목명 일괄변경 기능",
"거래처 드롭다운 옵션 (공통 데이터)",
"날짜 필터 버튼 (당해년도, 전전월, 전월, 당월, 어제, 오늘)",
"정렬 옵션 (최신순, 등록순, 금액 높은순, 금액 낮은순)",
"은행 데이터 필드 수정 불가 (날짜, 계좌, 금액 등)"
]
},
"assertions": [
{
"type": "url",
"expected": "/accounting/withdrawals",
"message": "목록 페이지 URL 확인"
},
{
"type": "text",
"target": "body",
"expected": "출금관리",
"message": "페이지 타이틀 확인"
}
]
}

View File

@@ -3,7 +3,14 @@
"name": "악성채권추심관리 테스트", "name": "악성채권추심관리 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "회계관리 > 악성채권추심관리 메뉴의 악성채권 CRUD 기능 테스트", "description": "회계관리 > 악성채권추심관리 메뉴의 악성채권 CRUD 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -34,11 +41,22 @@
"level2": "악성채권추심관리", "level2": "악성채권추심관리",
"expected": { "expected": {
"url_contains": "/accounting", "url_contains": "/accounting",
"visible": ["악성채권", "추심"] "visible": [
"악성채권",
"추심"
]
} }
}, },
{ {
"id": 2, "id": 2,
"name": "URL 검증",
"action": "verify_url",
"expected": {
"url_contains": "/accounting/bad-debt-collection"
}
},
{
"id": 3,
"name": "필수 검증 #5: 목업 페이지 감지", "name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup", "action": "verify_not_mockup",
"checks": [ "checks": [
@@ -49,7 +67,13 @@
"expected": "정상 페이지 (목업 아님)" "expected": "정상 페이지 (목업 아님)"
}, },
{ {
"id": 3, "id": 4,
"name": "통계 카드 확인",
"action": "evaluate",
"script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()"
},
{
"id": 5,
"name": "악성채권 테이블 구조 확인", "name": "악성채권 테이블 구조 확인",
"action": "verify_table", "action": "verify_table",
"checks": [ "checks": [
@@ -62,7 +86,13 @@
"expected": "악성채권 테이블 표시" "expected": "악성채권 테이블 표시"
}, },
{ {
"id": 4, "id": 6,
"name": "목록 필터 테스트",
"action": "evaluate",
"script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()"
},
{
"id": 7,
"name": "통계 카드 확인", "name": "통계 카드 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -73,7 +103,7 @@
"expected": "통계 정보 표시" "expected": "통계 정보 표시"
}, },
{ {
"id": 5, "id": 8,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 채권 등록 버튼 클릭", "name": "[CREATE] 채권 등록 버튼 클릭",
"action": "click_if_exists", "action": "click_if_exists",
@@ -83,7 +113,7 @@
} }
}, },
{ {
"id": 6, "id": 9,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 거래처 선택", "name": "[CREATE] 거래처 선택",
"action": "click_if_exists", "action": "click_if_exists",
@@ -91,14 +121,14 @@
"expected": "거래처 선택 가능" "expected": "거래처 선택 가능"
}, },
{ {
"id": 7, "id": 10,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 채권금액 입력", "name": "[CREATE] 채권금액 입력",
"action": "click_if_exists", "action": "click_if_exists",
"target": "input[name*='amount'], input[placeholder*='금액']" "target": "input[name*='amount'], input[placeholder*='금액']"
}, },
{ {
"id": 8, "id": 11,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 필수 검증 #2: 채권 저장", "name": "[CREATE] 필수 검증 #2: 채권 저장",
"action": "click_if_exists", "action": "click_if_exists",
@@ -112,14 +142,23 @@
"expected": "채권 등록 완료" "expected": "채권 등록 완료"
}, },
{ {
"id": 9, "id": 12,
"phase": "CREATE",
"name": "[CREATE] 저장 완료 토스트 확인",
"action": "verify_toast",
"verify": {
"contains": "등록|완료|성공|저장"
}
},
{
"id": 13,
"phase": "READ", "phase": "READ",
"name": "[READ] 등록된 채권 검색", "name": "[READ] 등록된 채권 검색",
"action": "click_if_exists", "action": "click_if_exists",
"target": "input[type='search'], input[placeholder*='검색']" "target": "input[type='search'], input[placeholder*='검색']"
}, },
{ {
"id": 10, "id": 14,
"phase": "READ", "phase": "READ",
"name": "[READ] 등록된 채권 확인", "name": "[READ] 등록된 채권 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -129,7 +168,7 @@
"expected": "등록된 채권 확인" "expected": "등록된 채권 확인"
}, },
{ {
"id": 11, "id": 15,
"phase": "READ", "phase": "READ",
"name": "[READ] 채권 상세 조회", "name": "[READ] 채권 상세 조회",
"action": "click_if_exists", "action": "click_if_exists",
@@ -139,7 +178,7 @@
} }
}, },
{ {
"id": 12, "id": 16,
"name": "상세 정보 확인", "name": "상세 정보 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -150,7 +189,7 @@
"expected": "상세 정보 표시" "expected": "상세 정보 표시"
}, },
{ {
"id": 13, "id": 17,
"phase": "UPDATE", "phase": "UPDATE",
"name": "[UPDATE] 상태 변경", "name": "[UPDATE] 상태 변경",
"action": "click_if_exists", "action": "click_if_exists",
@@ -158,14 +197,14 @@
"expected": "상태 변경 가능" "expected": "상태 변경 가능"
}, },
{ {
"id": 14, "id": 18,
"phase": "UPDATE", "phase": "UPDATE",
"name": "[UPDATE] 추심 메모 추가", "name": "[UPDATE] 추심 메모 추가",
"action": "click_if_exists", "action": "click_if_exists",
"target": "textarea[name*='memo'], textarea[placeholder*='메모']" "target": "textarea[name*='memo'], textarea[placeholder*='메모']"
}, },
{ {
"id": 15, "id": 19,
"phase": "UPDATE", "phase": "UPDATE",
"name": "[UPDATE] 변경 저장", "name": "[UPDATE] 변경 저장",
"action": "click_if_exists", "action": "click_if_exists",
@@ -177,7 +216,16 @@
"expected": "채권 정보 수정 완료" "expected": "채권 정보 수정 완료"
}, },
{ {
"id": 16, "id": 20,
"phase": "UPDATE",
"name": "[UPDATE] 수정 완료 토스트 확인",
"action": "verify_toast",
"verify": {
"contains": "수정|완료|성공|저장"
}
},
{
"id": 21,
"phase": "DELETE", "phase": "DELETE",
"name": "[DELETE] 채권 삭제", "name": "[DELETE] 채권 삭제",
"action": "click_if_exists", "action": "click_if_exists",
@@ -187,7 +235,7 @@
} }
}, },
{ {
"id": 17, "id": 22,
"phase": "DELETE", "phase": "DELETE",
"name": "[DELETE] 삭제 확인", "name": "[DELETE] 삭제 확인",
"action": "click_if_exists", "action": "click_if_exists",
@@ -199,7 +247,7 @@
"expected": "채권 삭제 완료" "expected": "채권 삭제 완료"
}, },
{ {
"id": 18, "id": 23,
"phase": "DELETE", "phase": "DELETE",
"name": "[DELETE] 삭제 확인", "name": "[DELETE] 삭제 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -207,6 +255,12 @@
"E2E_TEST_채권거래처 목록에서 제거" "E2E_TEST_채권거래처 목록에서 제거"
], ],
"expected": "채권 삭제 반영" "expected": "채권 삭제 반영"
},
{
"id": 24,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
} }
], ],
"expectedAPIs": [ "expectedAPIs": [
@@ -240,13 +294,18 @@
{ {
"id": 2, "id": 2,
"name": "저장 버튼", "name": "저장 버튼",
"steps": [8, 15], "steps": [
8,
15
],
"criteria": "API 호출 + 성공 토스트 + 데이터 반영" "criteria": "API 호출 + 성공 토스트 + 데이터 반영"
}, },
{ {
"id": 5, "id": 5,
"name": "목업 페이지 감지", "name": "목업 페이지 감지",
"steps": [2], "steps": [
2
],
"criteria": "악성채권 목록, 등록 버튼, 상태 필터 존재" "criteria": "악성채권 목록, 등록 버튼, 상태 필터 존재"
} }
], ],

View File

@@ -3,7 +3,14 @@
"name": "입출금계좌조회 테스트", "name": "입출금계좌조회 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "회계관리 > 입출금계좌조회 메뉴의 계좌 거래내역 조회/필터/엑셀 기능 테스트", "description": "회계관리 > 입출금계좌조회 메뉴의 계좌 거래내역 조회/필터/엑셀 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -27,11 +34,22 @@
"level2": "입출금계좌조회", "level2": "입출금계좌조회",
"expected": { "expected": {
"url_contains": "/accounting", "url_contains": "/accounting",
"visible": ["입출금", "계좌"] "visible": [
"입출금",
"계좌"
]
} }
}, },
{ {
"id": 2, "id": 2,
"name": "URL 검증",
"action": "verify_url",
"expected": {
"url_contains": "/accounting/bank-transactions"
}
},
{
"id": 3,
"name": "필수 검증 #5: 목업 페이지 감지", "name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup", "action": "verify_not_mockup",
"checks": [ "checks": [
@@ -42,7 +60,13 @@
"expected": "정상 페이지 (목업 아님)" "expected": "정상 페이지 (목업 아님)"
}, },
{ {
"id": 3, "id": 4,
"name": "통계 카드 확인",
"action": "evaluate",
"script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()"
},
{
"id": 5,
"name": "거래내역 테이블 구조 확인", "name": "거래내역 테이블 구조 확인",
"action": "verify_table", "action": "verify_table",
"checks": [ "checks": [
@@ -55,7 +79,13 @@
"expected": "거래내역 테이블 표시" "expected": "거래내역 테이블 표시"
}, },
{ {
"id": 4, "id": 6,
"name": "목록 필터 테스트",
"action": "evaluate",
"script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()"
},
{
"id": 7,
"name": "계좌 선택 드롭다운 확인", "name": "계좌 선택 드롭다운 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -64,7 +94,7 @@
"expected": "계좌 선택 가능" "expected": "계좌 선택 가능"
}, },
{ {
"id": 5, "id": 8,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 기간 필터 - 시작일", "name": "[FILTER] 기간 필터 - 시작일",
"action": "click_if_exists", "action": "click_if_exists",
@@ -72,7 +102,7 @@
"expected": "날짜 선택 열림" "expected": "날짜 선택 열림"
}, },
{ {
"id": 6, "id": 9,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 기간 필터 적용", "name": "[FILTER] 기간 필터 적용",
"action": "click_if_exists", "action": "click_if_exists",
@@ -80,7 +110,7 @@
"expected": "필터 적용됨" "expected": "필터 적용됨"
}, },
{ {
"id": 7, "id": 10,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 필터 결과 확인", "name": "[FILTER] 필터 결과 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -90,7 +120,7 @@
"expected": "필터 동작 확인" "expected": "필터 동작 확인"
}, },
{ {
"id": 8, "id": 11,
"phase": "READ", "phase": "READ",
"name": "[READ] 거래 상세 보기", "name": "[READ] 거래 상세 보기",
"action": "click_if_exists", "action": "click_if_exists",
@@ -100,7 +130,7 @@
} }
}, },
{ {
"id": 9, "id": 12,
"name": "상세 정보 확인", "name": "상세 정보 확인",
"action": "verify_detail", "action": "verify_detail",
"checks": [ "checks": [
@@ -111,14 +141,14 @@
"expected": "거래 상세 정보 표시" "expected": "거래 상세 정보 표시"
}, },
{ {
"id": 10, "id": 13,
"name": "목록으로 돌아가기", "name": "목록으로 돌아가기",
"action": "click_if_exists", "action": "click_if_exists",
"target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']",
"expected": "목록 페이지로 복귀" "expected": "목록 페이지로 복귀"
}, },
{ {
"id": 11, "id": 14,
"name": "입금 합계 확인", "name": "입금 합계 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -127,7 +157,7 @@
"expected": "입금 합계 표시" "expected": "입금 합계 표시"
}, },
{ {
"id": 12, "id": 15,
"name": "출금 합계 확인", "name": "출금 합계 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -136,7 +166,7 @@
"expected": "출금 합계 표시" "expected": "출금 합계 표시"
}, },
{ {
"id": 13, "id": 16,
"name": "엑셀 다운로드 버튼 확인", "name": "엑셀 다운로드 버튼 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -145,7 +175,7 @@
"expected": "엑셀 다운로드 기능 표시" "expected": "엑셀 다운로드 기능 표시"
}, },
{ {
"id": 14, "id": 17,
"name": "인쇄 버튼 확인", "name": "인쇄 버튼 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -154,13 +184,19 @@
"expected": "인쇄 기능 표시" "expected": "인쇄 기능 표시"
}, },
{ {
"id": 15, "id": 18,
"name": "페이지네이션 확인", "name": "페이지네이션 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
"페이지 번호 표시" "페이지 번호 표시"
], ],
"expected": "페이지네이션 표시" "expected": "페이지네이션 표시"
},
{
"id": 19,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
} }
], ],
"expectedAPIs": [ "expectedAPIs": [
@@ -179,13 +215,19 @@
{ {
"id": 3, "id": 3,
"name": "검색/필터", "name": "검색/필터",
"steps": [5, 6, 7], "steps": [
5,
6,
7
],
"criteria": "기간 필터 동작" "criteria": "기간 필터 동작"
}, },
{ {
"id": 5, "id": 5,
"name": "목업 페이지 감지", "name": "목업 페이지 감지",
"steps": [2], "steps": [
2
],
"criteria": "거래내역 목록, 계좌 선택, 기간 필터 존재" "criteria": "거래내역 목록, 계좌 선택, 기간 필터 존재"
} }
], ],

View File

@@ -3,7 +3,14 @@
"name": "어음관리 테스트", "name": "어음관리 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "회계관리 > 어음관리 메뉴의 어음 조회/등록/수정/삭제 전체 CRUD 테스트", "description": "회계관리 > 어음관리 메뉴의 어음 조회/등록/수정/삭제 전체 CRUD 테스트",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -39,11 +46,22 @@
"level2": "어음관리", "level2": "어음관리",
"expected": { "expected": {
"url_contains": "/accounting/bills", "url_contains": "/accounting/bills",
"visible": ["어음관리", "어음"] "visible": [
"어음관리",
"어음"
]
} }
}, },
{ {
"id": 2, "id": 2,
"name": "URL 검증",
"action": "verify_url",
"expected": {
"url_contains": "/accounting/bills"
}
},
{
"id": 3,
"name": "필수 검증 #5: 목업 페이지 감지", "name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup", "action": "verify_not_mockup",
"checks": [ "checks": [
@@ -54,7 +72,13 @@
"expected": "정상 페이지 (목업 아님)" "expected": "정상 페이지 (목업 아님)"
}, },
{ {
"id": 3, "id": 4,
"name": "통계 카드 확인",
"action": "evaluate",
"script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()"
},
{
"id": 5,
"name": "어음 테이블 구조 확인", "name": "어음 테이블 구조 확인",
"action": "verify_table", "action": "verify_table",
"checks": [ "checks": [
@@ -67,7 +91,13 @@
"expected": "어음 테이블 컬럼 정상 표시" "expected": "어음 테이블 컬럼 정상 표시"
}, },
{ {
"id": 4, "id": 6,
"name": "목록 필터 테스트",
"action": "evaluate",
"script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()"
},
{
"id": 7,
"name": "검색 기능 테스트", "name": "검색 기능 테스트",
"action": "click_if_exists", "action": "click_if_exists",
"target": "input[placeholder*='검색']", "target": "input[placeholder*='검색']",
@@ -77,7 +107,7 @@
} }
}, },
{ {
"id": 5, "id": 8,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 어음 등록 버튼 클릭", "name": "[CREATE] 어음 등록 버튼 클릭",
"action": "click_if_exists", "action": "click_if_exists",
@@ -88,21 +118,41 @@
} }
}, },
{ {
"id": 6, "id": 9,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 어음 정보 입력", "name": "[CREATE] 어음 정보 입력",
"action": "fill_form", "action": "fill_form",
"fields": [ "fields": [
{"name": "어음번호", "type": "text", "value": "E2E_TEST_어음_{timestamp}"}, {
{"name": "금액", "type": "number", "value": "1000000"}, "name": "어음번호",
{"name": "만기일", "type": "date", "value": "2026-03-15"}, "type": "text",
{"name": "발행처", "type": "text", "value": "테스트발행처"}, "value": "E2E_TEST_어음_{timestamp}"
{"name": "메모", "type": "text", "value": "E2E 자동화 테스트 어음"} },
{
"name": "금액",
"type": "number",
"value": "1000000"
},
{
"name": "만기일",
"type": "date",
"value": "2026-03-15"
},
{
"name": "발행처",
"type": "text",
"value": "테스트발행처"
},
{
"name": "메모",
"type": "text",
"value": "E2E 자동화 테스트 어음"
}
], ],
"note": "타임스탬프로 고유성 보장" "note": "타임스탬프로 고유성 보장"
}, },
{ {
"id": 7, "id": 10,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 필수 검증 #2: 등록 저장", "name": "[CREATE] 필수 검증 #2: 등록 저장",
"action": "click_if_exists", "action": "click_if_exists",
@@ -116,36 +166,52 @@
"expected": "어음 등록 완료" "expected": "어음 등록 완료"
}, },
{ {
"id": "7-modal-close", "id": 11,
"phase": "CREATE",
"name": "[CREATE] 저장 완료 토스트 확인",
"action": "verify_toast",
"verify": {
"contains": "등록|완료|성공|저장"
}
},
{
"id": 12,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 모달 닫기 확인", "name": "[CREATE] 모달 닫기 확인",
"action": "close_modal_if_open", "action": "close_modal_if_open",
"expected": "모달 닫힘" "expected": "모달 닫힘"
}, },
{ {
"id": 8, "id": 13,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 등록 결과 확인", "name": "[CREATE] 등록 결과 확인",
"action": "verify_detail", "action": "verify_detail",
"search": "E2E_TEST_어음", "search": "E2E_TEST_어음",
"expected": { "expected": {
"row_exists": true, "row_exists": true,
"contains": ["E2E", "1,000,000"] "contains": [
"E2E",
"1,000,000"
]
} }
}, },
{ {
"id": 9, "id": 14,
"phase": "READ", "phase": "READ",
"name": "[READ] 어음 상세 페이지 진입", "name": "[READ] 어음 상세 페이지 진입",
"action": "click_if_exists", "action": "click_if_exists",
"target": "table tbody tr:has-text('E2E')", "target": "table tbody tr:has-text('E2E')",
"expected": { "expected": {
"url_contains": "/accounting/bills/", "url_contains": "/accounting/bills/",
"visible": ["어음 상세", "수정", "삭제"] "visible": [
"어음 상세",
"수정",
"삭제"
]
} }
}, },
{ {
"id": 10, "id": 15,
"phase": "READ", "phase": "READ",
"name": "[READ] 상세 정보 확인", "name": "[READ] 상세 정보 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -158,7 +224,7 @@
"expected": "입력한 데이터와 일치" "expected": "입력한 데이터와 일치"
}, },
{ {
"id": 11, "id": 16,
"phase": "UPDATE", "phase": "UPDATE",
"name": "[UPDATE] 수정 모드 진입", "name": "[UPDATE] 수정 모드 진입",
"action": "click_if_exists", "action": "click_if_exists",
@@ -169,7 +235,7 @@
} }
}, },
{ {
"id": 12, "id": 17,
"phase": "UPDATE", "phase": "UPDATE",
"name": "[UPDATE] 메모 수정", "name": "[UPDATE] 메모 수정",
"action": "click_if_exists", "action": "click_if_exists",
@@ -178,7 +244,7 @@
"clear": true "clear": true
}, },
{ {
"id": 13, "id": 18,
"phase": "UPDATE", "phase": "UPDATE",
"name": "[UPDATE] 필수 검증 #2: 수정 저장", "name": "[UPDATE] 필수 검증 #2: 수정 저장",
"action": "click_if_exists", "action": "click_if_exists",
@@ -192,7 +258,16 @@
"expected": "수정 완료" "expected": "수정 완료"
}, },
{ {
"id": 14, "id": 19,
"phase": "UPDATE",
"name": "[UPDATE] 수정 완료 토스트 확인",
"action": "verify_toast",
"verify": {
"contains": "수정|완료|성공|저장"
}
},
{
"id": 20,
"phase": "UPDATE", "phase": "UPDATE",
"name": "[UPDATE] 수정 결과 확인", "name": "[UPDATE] 수정 결과 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -202,7 +277,7 @@
"expected": "수정된 데이터 반영" "expected": "수정된 데이터 반영"
}, },
{ {
"id": 15, "id": 21,
"phase": "DELETE", "phase": "DELETE",
"name": "[DELETE] 삭제 버튼 클릭", "name": "[DELETE] 삭제 버튼 클릭",
"action": "click_if_exists", "action": "click_if_exists",
@@ -213,7 +288,7 @@
} }
}, },
{ {
"id": 16, "id": 22,
"phase": "DELETE", "phase": "DELETE",
"name": "[DELETE] 필수 검증 #6: 삭제 확인", "name": "[DELETE] 필수 검증 #6: 삭제 확인",
"action": "click_if_exists", "action": "click_if_exists",
@@ -226,7 +301,7 @@
"expected": "삭제 완료 및 목록 복귀" "expected": "삭제 완료 및 목록 복귀"
}, },
{ {
"id": 17, "id": 23,
"phase": "DELETE", "phase": "DELETE",
"name": "[DELETE] 삭제 결과 확인", "name": "[DELETE] 삭제 결과 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -235,6 +310,12 @@
"row_exists": false, "row_exists": false,
"message": "테스트 어음이 목록에서 제거됨" "message": "테스트 어음이 목록에서 제거됨"
} }
},
{
"id": 24,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
} }
], ],
"expectedAPIs": [ "expectedAPIs": [
@@ -268,19 +349,28 @@
{ {
"id": 2, "id": 2,
"name": "등록/저장 버튼", "name": "등록/저장 버튼",
"steps": [7, 13], "steps": [
7,
13
],
"criteria": "API 호출 + 성공 토스트 + 데이터 반영" "criteria": "API 호출 + 성공 토스트 + 데이터 반영"
}, },
{ {
"id": 5, "id": 5,
"name": "목업 페이지 감지", "name": "목업 페이지 감지",
"steps": [2], "steps": [
2
],
"criteria": "어음 목록, 등록 버튼, 필터 존재" "criteria": "어음 목록, 등록 버튼, 필터 존재"
}, },
{ {
"id": 6, "id": 6,
"name": "삭제 기능", "name": "삭제 기능",
"steps": [15, 16, 17], "steps": [
15,
16,
17
],
"criteria": "DELETE API + 목록에서 제거" "criteria": "DELETE API + 목록에서 제거"
} }
], ],

View File

@@ -3,7 +3,14 @@
"name": "카드내역조회 테스트", "name": "카드내역조회 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "회계관리 > 카드내역조회 메뉴의 카드 사용내역 조회/필터/엑셀 기능 테스트", "description": "회계관리 > 카드내역조회 메뉴의 카드 사용내역 조회/필터/엑셀 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -27,11 +34,22 @@
"level2": "카드내역조회", "level2": "카드내역조회",
"expected": { "expected": {
"url_contains": "/accounting", "url_contains": "/accounting",
"visible": ["카드", "내역"] "visible": [
"카드",
"내역"
]
} }
}, },
{ {
"id": 2, "id": 2,
"name": "URL 검증",
"action": "verify_url",
"expected": {
"url_contains": "/accounting/card-transactions"
}
},
{
"id": 3,
"name": "필수 검증 #5: 목업 페이지 감지", "name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup", "action": "verify_not_mockup",
"checks": [ "checks": [
@@ -42,7 +60,13 @@
"expected": "정상 페이지 (목업 아님)" "expected": "정상 페이지 (목업 아님)"
}, },
{ {
"id": 3, "id": 4,
"name": "통계 카드 확인",
"action": "evaluate",
"script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()"
},
{
"id": 5,
"name": "카드내역 테이블 구조 확인", "name": "카드내역 테이블 구조 확인",
"action": "verify_table", "action": "verify_table",
"checks": [ "checks": [
@@ -55,7 +79,13 @@
"expected": "카드내역 테이블 표시" "expected": "카드내역 테이블 표시"
}, },
{ {
"id": 4, "id": 6,
"name": "목록 필터 테스트",
"action": "evaluate",
"script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()"
},
{
"id": 7,
"name": "카드 선택 드롭다운 확인", "name": "카드 선택 드롭다운 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -64,7 +94,7 @@
"expected": "카드 선택 가능" "expected": "카드 선택 가능"
}, },
{ {
"id": 5, "id": 8,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 기간 필터 - 시작일", "name": "[FILTER] 기간 필터 - 시작일",
"action": "click_if_exists", "action": "click_if_exists",
@@ -72,7 +102,7 @@
"expected": "날짜 선택 열림" "expected": "날짜 선택 열림"
}, },
{ {
"id": 6, "id": 9,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 기간 필터 적용", "name": "[FILTER] 기간 필터 적용",
"action": "click_if_exists", "action": "click_if_exists",
@@ -80,7 +110,7 @@
"expected": "필터 적용됨" "expected": "필터 적용됨"
}, },
{ {
"id": 7, "id": 10,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 필터 결과 확인", "name": "[FILTER] 필터 결과 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -90,7 +120,7 @@
"expected": "필터 동작 확인" "expected": "필터 동작 확인"
}, },
{ {
"id": 8, "id": 11,
"phase": "READ", "phase": "READ",
"name": "[READ] 카드 사용내역 상세 보기", "name": "[READ] 카드 사용내역 상세 보기",
"action": "click_if_exists", "action": "click_if_exists",
@@ -100,7 +130,7 @@
} }
}, },
{ {
"id": 9, "id": 12,
"name": "상세 정보 확인", "name": "상세 정보 확인",
"action": "verify_detail", "action": "verify_detail",
"checks": [ "checks": [
@@ -112,14 +142,14 @@
"expected": "카드 사용 상세 정보 표시" "expected": "카드 사용 상세 정보 표시"
}, },
{ {
"id": 10, "id": 13,
"name": "목록으로 돌아가기", "name": "목록으로 돌아가기",
"action": "click_if_exists", "action": "click_if_exists",
"target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']",
"expected": "목록 페이지로 복귀" "expected": "목록 페이지로 복귀"
}, },
{ {
"id": 11, "id": 14,
"name": "사용금액 합계 확인", "name": "사용금액 합계 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -128,7 +158,7 @@
"expected": "사용금액 합계 표시" "expected": "사용금액 합계 표시"
}, },
{ {
"id": 12, "id": 15,
"name": "카드별 사용 현황 확인", "name": "카드별 사용 현황 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -137,7 +167,7 @@
"expected": "카드별 현황 표시" "expected": "카드별 현황 표시"
}, },
{ {
"id": 13, "id": 16,
"name": "엑셀 다운로드 버튼 확인", "name": "엑셀 다운로드 버튼 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -146,7 +176,7 @@
"expected": "엑셀 다운로드 기능 표시" "expected": "엑셀 다운로드 기능 표시"
}, },
{ {
"id": 14, "id": 17,
"name": "인쇄 버튼 확인", "name": "인쇄 버튼 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -155,13 +185,19 @@
"expected": "인쇄 기능 표시" "expected": "인쇄 기능 표시"
}, },
{ {
"id": 15, "id": 18,
"name": "페이지네이션 확인", "name": "페이지네이션 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
"페이지 번호 표시" "페이지 번호 표시"
], ],
"expected": "페이지네이션 표시" "expected": "페이지네이션 표시"
},
{
"id": 19,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
} }
], ],
"expectedAPIs": [ "expectedAPIs": [
@@ -180,13 +216,19 @@
{ {
"id": 3, "id": 3,
"name": "검색/필터", "name": "검색/필터",
"steps": [5, 6, 7], "steps": [
5,
6,
7
],
"criteria": "기간 필터 동작" "criteria": "기간 필터 동작"
}, },
{ {
"id": 5, "id": 5,
"name": "목업 페이지 감지", "name": "목업 페이지 감지",
"steps": [2], "steps": [
2
],
"criteria": "카드내역 목록, 카드 선택, 기간 필터 존재" "criteria": "카드내역 목록, 카드 선택, 기간 필터 존재"
} }
], ],

View File

@@ -49,6 +49,14 @@
}, },
{ {
"id": 2, "id": 2,
"name": "URL 검증",
"action": "verify_url",
"expected": {
"url_contains": "/accounting/vendors"
}
},
{
"id": 3,
"name": "필수 검증 #5: 목업 페이지 감지", "name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup", "action": "verify_not_mockup",
"checks": [ "checks": [
@@ -59,7 +67,7 @@
"expected": "정상 페이지 (목업 아님)" "expected": "정상 페이지 (목업 아님)"
}, },
{ {
"id": 3, "id": 4,
"name": "통계 카드 확인", "name": "통계 카드 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -70,7 +78,7 @@
"expected": "통계 카드 표시" "expected": "통계 카드 표시"
}, },
{ {
"id": 4, "id": 5,
"name": "거래처 테이블 구조 확인", "name": "거래처 테이블 구조 확인",
"action": "verify_table", "action": "verify_table",
"checks": [ "checks": [
@@ -83,7 +91,13 @@
"expected": "거래처 테이블 표시" "expected": "거래처 테이블 표시"
}, },
{ {
"id": 5, "id": 6,
"name": "목록 필터 테스트",
"action": "evaluate",
"script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()"
},
{
"id": 7,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 거래처 등록 버튼 클릭", "name": "[CREATE] 거래처 등록 버튼 클릭",
"action": "click_if_exists", "action": "click_if_exists",
@@ -93,7 +107,7 @@
} }
}, },
{ {
"id": 6, "id": 8,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 거래처명 입력", "name": "[CREATE] 거래처명 입력",
"action": "click_if_exists", "action": "click_if_exists",
@@ -102,7 +116,7 @@
"clear": true "clear": true
}, },
{ {
"id": 7, "id": 9,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 거래처 구분 선택", "name": "[CREATE] 거래처 구분 선택",
"action": "click_if_exists", "action": "click_if_exists",
@@ -110,7 +124,7 @@
"expected": "거래처 구분 선택" "expected": "거래처 구분 선택"
}, },
{ {
"id": 8, "id": 10,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 필수 검증 #2: 거래처 저장", "name": "[CREATE] 필수 검증 #2: 거래처 저장",
"action": "click_if_exists", "action": "click_if_exists",
@@ -124,7 +138,16 @@
"expected": "거래처 등록 완료" "expected": "거래처 등록 완료"
}, },
{ {
"id": 9, "id": 11,
"phase": "CREATE",
"name": "[CREATE] 저장 완료 토스트 확인",
"action": "verify_toast",
"verify": {
"contains": "등록|완료|성공|저장"
}
},
{
"id": 12,
"phase": "READ", "phase": "READ",
"name": "[READ] 등록된 거래처 검색", "name": "[READ] 등록된 거래처 검색",
"action": "click_if_exists", "action": "click_if_exists",
@@ -133,7 +156,7 @@
"submit": true "submit": true
}, },
{ {
"id": 10, "id": 13,
"phase": "READ", "phase": "READ",
"name": "[READ] 등록된 거래처 목록 확인", "name": "[READ] 등록된 거래처 목록 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -143,7 +166,7 @@
"expected": "등록된 거래처 확인" "expected": "등록된 거래처 확인"
}, },
{ {
"id": 11, "id": 14,
"phase": "READ", "phase": "READ",
"name": "[READ] 거래처 상세 조회", "name": "[READ] 거래처 상세 조회",
"action": "click_if_exists", "action": "click_if_exists",
@@ -154,7 +177,7 @@
} }
}, },
{ {
"id": 12, "id": 15,
"name": "상세 페이지 정보 확인", "name": "상세 페이지 정보 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -165,7 +188,7 @@
"expected": "상세 정보 표시" "expected": "상세 정보 표시"
}, },
{ {
"id": 13, "id": 16,
"phase": "UPDATE", "phase": "UPDATE",
"name": "[UPDATE] 수정 모드 진입", "name": "[UPDATE] 수정 모드 진입",
"action": "click_if_exists", "action": "click_if_exists",
@@ -175,7 +198,7 @@
} }
}, },
{ {
"id": 14, "id": 17,
"phase": "UPDATE", "phase": "UPDATE",
"name": "[UPDATE] 거래처 정보 수정", "name": "[UPDATE] 거래처 정보 수정",
"action": "click_if_exists", "action": "click_if_exists",
@@ -184,7 +207,7 @@
"clear": true "clear": true
}, },
{ {
"id": 15, "id": 18,
"phase": "UPDATE", "phase": "UPDATE",
"name": "[UPDATE] 거래처 저장", "name": "[UPDATE] 거래처 저장",
"action": "click_if_exists", "action": "click_if_exists",
@@ -196,7 +219,16 @@
"expected": "거래처 수정 완료" "expected": "거래처 수정 완료"
}, },
{ {
"id": 16, "id": 19,
"phase": "UPDATE",
"name": "[UPDATE] 수정 완료 토스트 확인",
"action": "verify_toast",
"verify": {
"contains": "수정|완료|성공|저장"
}
},
{
"id": 20,
"phase": "DELETE", "phase": "DELETE",
"name": "[DELETE] 거래처 삭제", "name": "[DELETE] 거래처 삭제",
"action": "click_if_exists", "action": "click_if_exists",
@@ -206,7 +238,7 @@
} }
}, },
{ {
"id": 17, "id": 21,
"phase": "DELETE", "phase": "DELETE",
"name": "[DELETE] 삭제 확인", "name": "[DELETE] 삭제 확인",
"action": "click_if_exists", "action": "click_if_exists",
@@ -218,7 +250,7 @@
"expected": "거래처 삭제 완료" "expected": "거래처 삭제 완료"
}, },
{ {
"id": 18, "id": 22,
"phase": "DELETE", "phase": "DELETE",
"name": "[DELETE] 삭제 확인", "name": "[DELETE] 삭제 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -226,6 +258,12 @@
"E2E_TEST_회계거래처 목록에서 제거" "E2E_TEST_회계거래처 목록에서 제거"
], ],
"expected": "거래처 삭제 반영" "expected": "거래처 삭제 반영"
},
{
"id": 23,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
} }
], ],
"expectedAPIs": [ "expectedAPIs": [

View File

@@ -3,7 +3,14 @@
"name": "입금관리 테스트", "name": "입금관리 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "회계관리 > 입금관리 메뉴의 입금 조회/등록/수정/삭제 전체 CRUD 테스트", "description": "회계관리 > 입금관리 메뉴의 입금 조회/등록/수정/삭제 전체 CRUD 테스트",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -39,11 +46,22 @@
"level2": "입금관리", "level2": "입금관리",
"expected": { "expected": {
"url_contains": "/accounting/deposits", "url_contains": "/accounting/deposits",
"visible": ["입금관리", "입금"] "visible": [
"입금관리",
"입금"
]
} }
}, },
{ {
"id": 2, "id": 2,
"name": "URL 검증",
"action": "verify_url",
"expected": {
"url_contains": "/accounting/deposits"
}
},
{
"id": 3,
"name": "필수 검증 #5: 목업 페이지 감지", "name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup", "action": "verify_not_mockup",
"checks": [ "checks": [
@@ -54,7 +72,13 @@
"expected": "정상 페이지 (목업 아님)" "expected": "정상 페이지 (목업 아님)"
}, },
{ {
"id": 3, "id": 4,
"name": "통계 카드 확인",
"action": "evaluate",
"script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()"
},
{
"id": 5,
"name": "입금 테이블 구조 확인", "name": "입금 테이블 구조 확인",
"action": "verify_table", "action": "verify_table",
"checks": [ "checks": [
@@ -67,7 +91,13 @@
"expected": "입금 테이블 컬럼 정상 표시" "expected": "입금 테이블 컬럼 정상 표시"
}, },
{ {
"id": 4, "id": 6,
"name": "목록 필터 테스트",
"action": "evaluate",
"script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()"
},
{
"id": 7,
"name": "검색 기능 테스트", "name": "검색 기능 테스트",
"action": "click_if_exists", "action": "click_if_exists",
"target": "input[placeholder*='검색']", "target": "input[placeholder*='검색']",
@@ -77,7 +107,7 @@
} }
}, },
{ {
"id": 5, "id": 8,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 입금 등록 버튼 클릭", "name": "[CREATE] 입금 등록 버튼 클릭",
"action": "click_if_exists", "action": "click_if_exists",
@@ -88,21 +118,41 @@
} }
}, },
{ {
"id": 6, "id": 9,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 입금 정보 입력", "name": "[CREATE] 입금 정보 입력",
"action": "fill_form", "action": "fill_form",
"fields": [ "fields": [
{"name": "거래처", "type": "select", "value": "E2E_TEST_입금거래처"}, {
{"name": "입금일", "type": "date", "value": "2026-02-03"}, "name": "거래처",
{"name": "금액", "type": "number", "value": "100000"}, "type": "select",
{"name": "입금방법", "type": "select", "value": "계좌이체"}, "value": "E2E_TEST_입금거래처"
{"name": "메모", "type": "text", "value": "E2E 자동화 테스트 입금_{timestamp}"} },
{
"name": "입금일",
"type": "date",
"value": "2026-02-03"
},
{
"name": "금액",
"type": "number",
"value": "100000"
},
{
"name": "입금방법",
"type": "select",
"value": "계좌이체"
},
{
"name": "메모",
"type": "text",
"value": "E2E 자동화 테스트 입금_{timestamp}"
}
], ],
"note": "타임스탬프로 고유성 보장" "note": "타임스탬프로 고유성 보장"
}, },
{ {
"id": 7, "id": 10,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 필수 검증 #2: 등록 저장", "name": "[CREATE] 필수 검증 #2: 등록 저장",
"action": "click_if_exists", "action": "click_if_exists",
@@ -116,36 +166,52 @@
"expected": "입금 등록 완료" "expected": "입금 등록 완료"
}, },
{ {
"id": "7-modal-close", "id": 11,
"phase": "CREATE",
"name": "[CREATE] 저장 완료 토스트 확인",
"action": "verify_toast",
"verify": {
"contains": "등록|완료|성공|저장"
}
},
{
"id": 12,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 모달 닫기 확인", "name": "[CREATE] 모달 닫기 확인",
"action": "close_modal_if_open", "action": "close_modal_if_open",
"expected": "모달 닫힘" "expected": "모달 닫힘"
}, },
{ {
"id": 8, "id": 13,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 등록 결과 확인", "name": "[CREATE] 등록 결과 확인",
"action": "verify_detail", "action": "verify_detail",
"search": "E2E 자동화 테스트 입금", "search": "E2E 자동화 테스트 입금",
"expected": { "expected": {
"row_exists": true, "row_exists": true,
"contains": ["E2E", "100,000"] "contains": [
"E2E",
"100,000"
]
} }
}, },
{ {
"id": 9, "id": 14,
"phase": "READ", "phase": "READ",
"name": "[READ] 입금 상세 페이지 진입", "name": "[READ] 입금 상세 페이지 진입",
"action": "click_if_exists", "action": "click_if_exists",
"target": "table tbody tr:has-text('E2E')", "target": "table tbody tr:has-text('E2E')",
"expected": { "expected": {
"url_contains": "/accounting/deposits/", "url_contains": "/accounting/deposits/",
"visible": ["입금 상세", "수정", "삭제"] "visible": [
"입금 상세",
"수정",
"삭제"
]
} }
}, },
{ {
"id": 10, "id": 15,
"phase": "READ", "phase": "READ",
"name": "[READ] 상세 정보 확인", "name": "[READ] 상세 정보 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -157,7 +223,7 @@
"expected": "입력한 데이터와 일치" "expected": "입력한 데이터와 일치"
}, },
{ {
"id": 11, "id": 16,
"phase": "UPDATE", "phase": "UPDATE",
"name": "[UPDATE] 수정 모드 진입", "name": "[UPDATE] 수정 모드 진입",
"action": "click_if_exists", "action": "click_if_exists",
@@ -168,7 +234,7 @@
} }
}, },
{ {
"id": 12, "id": 17,
"phase": "UPDATE", "phase": "UPDATE",
"name": "[UPDATE] 금액 수정", "name": "[UPDATE] 금액 수정",
"action": "click_if_exists", "action": "click_if_exists",
@@ -177,7 +243,7 @@
"clear": true "clear": true
}, },
{ {
"id": 13, "id": 18,
"phase": "UPDATE", "phase": "UPDATE",
"name": "[UPDATE] 메모 수정", "name": "[UPDATE] 메모 수정",
"action": "click_if_exists", "action": "click_if_exists",
@@ -186,7 +252,7 @@
"clear": true "clear": true
}, },
{ {
"id": 14, "id": 19,
"phase": "UPDATE", "phase": "UPDATE",
"name": "[UPDATE] 필수 검증 #2: 수정 저장", "name": "[UPDATE] 필수 검증 #2: 수정 저장",
"action": "click_if_exists", "action": "click_if_exists",
@@ -200,7 +266,16 @@
"expected": "수정 완료" "expected": "수정 완료"
}, },
{ {
"id": 15, "id": 20,
"phase": "UPDATE",
"name": "[UPDATE] 수정 완료 토스트 확인",
"action": "verify_toast",
"verify": {
"contains": "수정|완료|성공|저장"
}
},
{
"id": 21,
"phase": "UPDATE", "phase": "UPDATE",
"name": "[UPDATE] 수정 결과 확인", "name": "[UPDATE] 수정 결과 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -211,7 +286,7 @@
"expected": "수정된 데이터 반영" "expected": "수정된 데이터 반영"
}, },
{ {
"id": 16, "id": 22,
"phase": "DELETE", "phase": "DELETE",
"name": "[DELETE] 삭제 버튼 클릭", "name": "[DELETE] 삭제 버튼 클릭",
"action": "click_if_exists", "action": "click_if_exists",
@@ -222,7 +297,7 @@
} }
}, },
{ {
"id": 17, "id": 23,
"phase": "DELETE", "phase": "DELETE",
"name": "[DELETE] 필수 검증 #6: 삭제 확인", "name": "[DELETE] 필수 검증 #6: 삭제 확인",
"action": "click_if_exists", "action": "click_if_exists",
@@ -235,7 +310,7 @@
"expected": "삭제 완료 및 목록 복귀" "expected": "삭제 완료 및 목록 복귀"
}, },
{ {
"id": 18, "id": 24,
"phase": "DELETE", "phase": "DELETE",
"name": "[DELETE] 삭제 결과 확인", "name": "[DELETE] 삭제 결과 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -244,6 +319,12 @@
"row_exists": false, "row_exists": false,
"message": "테스트 데이터가 목록에서 제거됨" "message": "테스트 데이터가 목록에서 제거됨"
} }
},
{
"id": 25,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
} }
], ],
"expectedAPIs": [ "expectedAPIs": [
@@ -277,19 +358,28 @@
{ {
"id": 2, "id": 2,
"name": "등록/저장 버튼", "name": "등록/저장 버튼",
"steps": [7, 14], "steps": [
7,
14
],
"criteria": "API 호출 + 성공 토스트 + 데이터 반영" "criteria": "API 호출 + 성공 토스트 + 데이터 반영"
}, },
{ {
"id": 5, "id": 5,
"name": "목업 페이지 감지", "name": "목업 페이지 감지",
"steps": [2], "steps": [
2
],
"criteria": "입금 목록, 등록 버튼, 필터 존재" "criteria": "입금 목록, 등록 버튼, 필터 존재"
}, },
{ {
"id": 6, "id": 6,
"name": "삭제 기능", "name": "삭제 기능",
"steps": [16, 17, 18], "steps": [
16,
17,
18
],
"criteria": "DELETE API + 목록에서 제거" "criteria": "DELETE API + 목록에서 제거"
} }
], ],

View File

@@ -3,7 +3,14 @@
"name": "지출예상내역서 테스트", "name": "지출예상내역서 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "회계관리 > 지출예상내역서 메뉴의 지출 예상 조회/필터/인쇄 기능 테스트", "description": "회계관리 > 지출예상내역서 메뉴의 지출 예상 조회/필터/인쇄 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -27,11 +34,22 @@
"level2": "지출예상내역서", "level2": "지출예상내역서",
"expected": { "expected": {
"url_contains": "/accounting", "url_contains": "/accounting",
"visible": ["지출", "예상"] "visible": [
"지출",
"예상"
]
} }
}, },
{ {
"id": 2, "id": 2,
"name": "URL 검증",
"action": "verify_url",
"expected": {
"url_contains": "/accounting/expected-expenses"
}
},
{
"id": 3,
"name": "필수 검증 #5: 목업 페이지 감지", "name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup", "action": "verify_not_mockup",
"checks": [ "checks": [
@@ -42,7 +60,13 @@
"expected": "정상 페이지 (목업 아님)" "expected": "정상 페이지 (목업 아님)"
}, },
{ {
"id": 3, "id": 4,
"name": "통계 카드 확인",
"action": "evaluate",
"script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()"
},
{
"id": 5,
"name": "지출예상 테이블 구조 확인", "name": "지출예상 테이블 구조 확인",
"action": "verify_table", "action": "verify_table",
"checks": [ "checks": [
@@ -54,7 +78,13 @@
"expected": "지출예상 테이블 표시" "expected": "지출예상 테이블 표시"
}, },
{ {
"id": 4, "id": 6,
"name": "목록 필터 테스트",
"action": "evaluate",
"script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()"
},
{
"id": 7,
"name": "기간 선택 확인", "name": "기간 선택 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -63,7 +93,7 @@
"expected": "기간 선택 가능" "expected": "기간 선택 가능"
}, },
{ {
"id": 5, "id": 8,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 월 선택", "name": "[FILTER] 월 선택",
"action": "click_if_exists", "action": "click_if_exists",
@@ -71,7 +101,7 @@
"expected": "월 선택 열림" "expected": "월 선택 열림"
}, },
{ {
"id": 6, "id": 9,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 조회 적용", "name": "[FILTER] 조회 적용",
"action": "click_if_exists", "action": "click_if_exists",
@@ -79,7 +109,7 @@
"expected": "필터 적용됨" "expected": "필터 적용됨"
}, },
{ {
"id": 7, "id": 10,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 필터 결과 확인", "name": "[FILTER] 필터 결과 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -89,7 +119,7 @@
"expected": "필터 동작 확인" "expected": "필터 동작 확인"
}, },
{ {
"id": 8, "id": 11,
"name": "지출 카테고리별 확인", "name": "지출 카테고리별 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -98,7 +128,7 @@
"expected": "카테고리별 지출 표시" "expected": "카테고리별 지출 표시"
}, },
{ {
"id": 9, "id": 12,
"name": "합계 금액 확인", "name": "합계 금액 확인",
"action": "verify_detail", "action": "verify_detail",
"checks": [ "checks": [
@@ -107,7 +137,7 @@
"expected": "합계 금액 표시" "expected": "합계 금액 표시"
}, },
{ {
"id": 10, "id": 13,
"name": "일별 지출 예상 확인", "name": "일별 지출 예상 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -116,7 +146,7 @@
"expected": "일별 지출 표시" "expected": "일별 지출 표시"
}, },
{ {
"id": 11, "id": 14,
"name": "주요 지출 항목 확인", "name": "주요 지출 항목 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -125,7 +155,7 @@
"expected": "주요 항목 표시" "expected": "주요 항목 표시"
}, },
{ {
"id": 12, "id": 15,
"name": "인쇄 버튼 확인", "name": "인쇄 버튼 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -134,7 +164,7 @@
"expected": "인쇄 기능 표시" "expected": "인쇄 기능 표시"
}, },
{ {
"id": 13, "id": 16,
"name": "엑셀 다운로드 버튼 확인", "name": "엑셀 다운로드 버튼 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -143,7 +173,7 @@
"expected": "엑셀 다운로드 기능 표시" "expected": "엑셀 다운로드 기능 표시"
}, },
{ {
"id": 14, "id": 17,
"name": "PDF 내보내기 확인", "name": "PDF 내보내기 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -152,13 +182,19 @@
"expected": "PDF 내보내기 기능 표시" "expected": "PDF 내보내기 기능 표시"
}, },
{ {
"id": 15, "id": 18,
"name": "이전/다음 기간 네비게이션", "name": "이전/다음 기간 네비게이션",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
"이전/다음 기간 이동 가능" "이전/다음 기간 이동 가능"
], ],
"expected": "기간 네비게이션 표시" "expected": "기간 네비게이션 표시"
},
{
"id": 19,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
} }
], ],
"expectedAPIs": [ "expectedAPIs": [
@@ -177,13 +213,19 @@
{ {
"id": 3, "id": 3,
"name": "검색/필터", "name": "검색/필터",
"steps": [5, 6, 7], "steps": [
5,
6,
7
],
"criteria": "기간 필터 동작" "criteria": "기간 필터 동작"
}, },
{ {
"id": 5, "id": 5,
"name": "목업 페이지 감지", "name": "목업 페이지 감지",
"steps": [2], "steps": [
2
],
"criteria": "지출예상 목록, 기간 선택, 합계 표시 존재" "criteria": "지출예상 목록, 기간 선택, 합계 표시 존재"
} }
], ],

View File

@@ -3,7 +3,14 @@
"name": "결제내역 테스트", "name": "결제내역 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "회계관리 > 결제내역 메뉴의 결제 내역 조회/필터/검색/다운로드 기능 테스트", "description": "회계관리 > 결제내역 메뉴의 결제 내역 조회/필터/검색/다운로드 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -27,11 +34,22 @@
"level2": "결제내역", "level2": "결제내역",
"expected": { "expected": {
"url_contains": "/payment", "url_contains": "/payment",
"visible": ["결제내역", "결제"] "visible": [
"결제내역",
"결제"
]
} }
}, },
{ {
"id": 2, "id": 2,
"name": "URL 검증",
"action": "verify_url",
"expected": {
"url_contains": "/payment-history"
}
},
{
"id": 3,
"name": "필수 검증 #5: 목업 페이지 감지", "name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup", "action": "verify_not_mockup",
"checks": [ "checks": [
@@ -42,7 +60,13 @@
"expected": "정상 페이지 (목업 아님)" "expected": "정상 페이지 (목업 아님)"
}, },
{ {
"id": 3, "id": 4,
"name": "통계 카드 확인",
"action": "evaluate",
"script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()"
},
{
"id": 5,
"name": "결제내역 페이지 구조 확인", "name": "결제내역 페이지 구조 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -54,7 +78,7 @@
"expected": "결제내역 조회 폼 정상 표시" "expected": "결제내역 조회 폼 정상 표시"
}, },
{ {
"id": 4, "id": 6,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 기간 필터 - 시작일", "name": "[FILTER] 기간 필터 - 시작일",
"action": "click_if_exists", "action": "click_if_exists",
@@ -63,7 +87,7 @@
"expected": "시작일 입력" "expected": "시작일 입력"
}, },
{ {
"id": 5, "id": 7,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 기간 필터 - 종료일", "name": "[FILTER] 기간 필터 - 종료일",
"action": "click_if_exists", "action": "click_if_exists",
@@ -72,7 +96,7 @@
"expected": "종료일 입력" "expected": "종료일 입력"
}, },
{ {
"id": 6, "id": 8,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 조회 버튼 클릭", "name": "[FILTER] 조회 버튼 클릭",
"action": "click_if_exists", "action": "click_if_exists",
@@ -83,7 +107,7 @@
} }
}, },
{ {
"id": 7, "id": 9,
"phase": "READ", "phase": "READ",
"name": "[READ] 결제 테이블 구조 확인", "name": "[READ] 결제 테이블 구조 확인",
"action": "verify_table", "action": "verify_table",
@@ -96,7 +120,13 @@
"expected": "결제 테이블 정상 표시" "expected": "결제 테이블 정상 표시"
}, },
{ {
"id": 8, "id": 10,
"name": "목록 필터 테스트",
"action": "evaluate",
"script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()"
},
{
"id": 11,
"phase": "READ", "phase": "READ",
"name": "[READ] 결제 데이터 표시 확인", "name": "[READ] 결제 데이터 표시 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -107,7 +137,7 @@
"expected": "결제 데이터 정상 표시" "expected": "결제 데이터 정상 표시"
}, },
{ {
"id": 9, "id": 12,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 결제방법 필터 테스트", "name": "[FILTER] 결제방법 필터 테스트",
"action": "click_if_exists", "action": "click_if_exists",
@@ -115,7 +145,7 @@
"expected": "결제방법 필터 옵션 표시" "expected": "결제방법 필터 옵션 표시"
}, },
{ {
"id": 10, "id": 13,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 상태 필터 테스트", "name": "[FILTER] 상태 필터 테스트",
"action": "verify_elements", "action": "verify_elements",
@@ -125,7 +155,7 @@
"expected": "상태 필터 기능 확인" "expected": "상태 필터 기능 확인"
}, },
{ {
"id": 11, "id": 14,
"phase": "READ", "phase": "READ",
"name": "[READ] 결제 상세 조회", "name": "[READ] 결제 상세 조회",
"action": "click_if_exists", "action": "click_if_exists",
@@ -135,7 +165,7 @@
} }
}, },
{ {
"id": 12, "id": 15,
"name": "결제 상세 정보 확인", "name": "결제 상세 정보 확인",
"action": "verify_detail", "action": "verify_detail",
"checks": [ "checks": [
@@ -147,14 +177,14 @@
"expected": "결제 상세 정보 표시" "expected": "결제 상세 정보 표시"
}, },
{ {
"id": 13, "id": 16,
"name": "목록으로 돌아가기", "name": "목록으로 돌아가기",
"action": "click_if_exists", "action": "click_if_exists",
"target": "button:has-text('목록'), button:has-text('뒤로'), [class*='back']", "target": "button:has-text('목록'), button:has-text('뒤로'), [class*='back']",
"expected": "목록 페이지로 복귀" "expected": "목록 페이지로 복귀"
}, },
{ {
"id": 14, "id": 17,
"name": "필수 검증 #1: 엑셀 다운로드", "name": "필수 검증 #1: 엑셀 다운로드",
"action": "click_if_exists", "action": "click_if_exists",
"target": "button:has-text('엑셀'), button:has-text('Excel'), button:has-text('다운로드')", "target": "button:has-text('엑셀'), button:has-text('Excel'), button:has-text('다운로드')",
@@ -165,13 +195,19 @@
"expected": "엑셀 파일 다운로드" "expected": "엑셀 파일 다운로드"
}, },
{ {
"id": 15, "id": 18,
"name": "합계 금액 표시 확인", "name": "합계 금액 표시 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
"총 결제금액 합계 표시" "총 결제금액 합계 표시"
], ],
"expected": "합계 영역 표시" "expected": "합계 영역 표시"
},
{
"id": 19,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
} }
], ],
"expectedAPIs": [ "expectedAPIs": [
@@ -195,13 +231,17 @@
{ {
"id": 1, "id": 1,
"name": "엑셀 다운로드", "name": "엑셀 다운로드",
"steps": [14], "steps": [
14
],
"criteria": "API 호출 + 파일 다운로드" "criteria": "API 호출 + 파일 다운로드"
}, },
{ {
"id": 5, "id": 5,
"name": "목업 페이지 감지", "name": "목업 페이지 감지",
"steps": [2], "steps": [
2
],
"criteria": "결제 내역 목록, 기간 필터, 검색 기능 존재" "criteria": "결제 내역 목록, 기간 필터, 검색 기능 존재"
} }
], ],

View File

@@ -3,7 +3,14 @@
"name": "매입관리 테스트", "name": "매입관리 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "회계관리 > 매입관리 메뉴의 매입 조회/필터/엑셀 기능 테스트", "description": "회계관리 > 매입관리 메뉴의 매입 조회/필터/엑셀 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -27,7 +34,10 @@
"level2": "매입관리", "level2": "매입관리",
"expected": { "expected": {
"url_contains": "/accounting", "url_contains": "/accounting",
"visible": ["매입관리", "매입"] "visible": [
"매입관리",
"매입"
]
} }
}, },
{ {
@@ -43,6 +53,12 @@
}, },
{ {
"id": 3, "id": 3,
"name": "통계 카드 확인",
"action": "evaluate",
"script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()"
},
{
"id": 4,
"name": "매입 테이블 구조 확인", "name": "매입 테이블 구조 확인",
"action": "verify_table", "action": "verify_table",
"checks": [ "checks": [
@@ -55,7 +71,13 @@
"expected": "매입 테이블 표시" "expected": "매입 테이블 표시"
}, },
{ {
"id": 4, "id": 5,
"name": "목록 필터 테스트",
"action": "evaluate",
"script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()"
},
{
"id": 6,
"name": "매입 통계 카드 확인", "name": "매입 통계 카드 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -66,7 +88,7 @@
"expected": "통계 카드 표시" "expected": "통계 카드 표시"
}, },
{ {
"id": 5, "id": 7,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 기간 필터 - 시작일", "name": "[FILTER] 기간 필터 - 시작일",
"action": "click_if_exists", "action": "click_if_exists",
@@ -74,7 +96,7 @@
"expected": "날짜 선택 열림" "expected": "날짜 선택 열림"
}, },
{ {
"id": 6, "id": 8,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 기간 필터 적용", "name": "[FILTER] 기간 필터 적용",
"action": "click_if_exists", "action": "click_if_exists",
@@ -82,7 +104,7 @@
"expected": "필터 적용됨" "expected": "필터 적용됨"
}, },
{ {
"id": 7, "id": 9,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 필터 결과 확인", "name": "[FILTER] 필터 결과 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -92,7 +114,7 @@
"expected": "필터 동작 확인" "expected": "필터 동작 확인"
}, },
{ {
"id": 8, "id": 10,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 거래처별 필터", "name": "[FILTER] 거래처별 필터",
"action": "click_if_exists", "action": "click_if_exists",
@@ -100,7 +122,7 @@
"expected": "거래처 필터 가능" "expected": "거래처 필터 가능"
}, },
{ {
"id": 9, "id": 11,
"phase": "READ", "phase": "READ",
"name": "[READ] 매입 상세 보기", "name": "[READ] 매입 상세 보기",
"action": "click_if_exists", "action": "click_if_exists",
@@ -110,7 +132,7 @@
} }
}, },
{ {
"id": 10, "id": 12,
"name": "상세 정보 확인", "name": "상세 정보 확인",
"action": "verify_detail", "action": "verify_detail",
"checks": [ "checks": [
@@ -121,14 +143,14 @@
"expected": "매입 상세 정보 표시" "expected": "매입 상세 정보 표시"
}, },
{ {
"id": 11, "id": 13,
"name": "목록으로 돌아가기", "name": "목록으로 돌아가기",
"action": "click_if_exists", "action": "click_if_exists",
"target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']",
"expected": "목록 페이지로 복귀" "expected": "목록 페이지로 복귀"
}, },
{ {
"id": 12, "id": 14,
"name": "매입 합계 확인", "name": "매입 합계 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -137,7 +159,7 @@
"expected": "매입 합계 표시" "expected": "매입 합계 표시"
}, },
{ {
"id": 13, "id": 15,
"name": "엑셀 다운로드 버튼 확인", "name": "엑셀 다운로드 버튼 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -146,7 +168,7 @@
"expected": "엑셀 다운로드 기능 표시" "expected": "엑셀 다운로드 기능 표시"
}, },
{ {
"id": 14, "id": 16,
"name": "인쇄 버튼 확인", "name": "인쇄 버튼 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -155,13 +177,19 @@
"expected": "인쇄 기능 표시" "expected": "인쇄 기능 표시"
}, },
{ {
"id": 15, "id": 17,
"name": "페이지네이션 확인", "name": "페이지네이션 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
"페이지 번호 표시" "페이지 번호 표시"
], ],
"expected": "페이지네이션 표시" "expected": "페이지네이션 표시"
},
{
"id": 18,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
} }
], ],
"expectedAPIs": [ "expectedAPIs": [
@@ -180,13 +208,20 @@
{ {
"id": 3, "id": 3,
"name": "검색/필터", "name": "검색/필터",
"steps": [5, 6, 7, 8], "steps": [
5,
6,
7,
8
],
"criteria": "기간 필터 + 거래처 필터 동작" "criteria": "기간 필터 + 거래처 필터 동작"
}, },
{ {
"id": 5, "id": 5,
"name": "목업 페이지 감지", "name": "목업 페이지 감지",
"steps": [2], "steps": [
2
],
"criteria": "매입 목록, 기간 필터, 합계 표시 존재" "criteria": "매입 목록, 기간 필터, 합계 표시 존재"
} }
], ],

View File

@@ -3,7 +3,14 @@
"name": "미수금현황 테스트", "name": "미수금현황 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "회계관리 > 미수금현황 메뉴의 미수금 조회/필터/검색/엑셀 다운로드 기능 테스트", "description": "회계관리 > 미수금현황 메뉴의 미수금 조회/필터/검색/엑셀 다운로드 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -32,11 +39,22 @@
"level2": "미수금현황", "level2": "미수금현황",
"expected": { "expected": {
"url_contains": "/accounting/receivables", "url_contains": "/accounting/receivables",
"visible": ["미수금현황", "미수금"] "visible": [
"미수금현황",
"미수금"
]
} }
}, },
{ {
"id": 2, "id": 2,
"name": "URL 검증",
"action": "verify_url",
"expected": {
"url_contains": "/accounting/receivables-status"
}
},
{
"id": 3,
"name": "필수 검증 #5: 목업 페이지 감지", "name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup", "action": "verify_not_mockup",
"checks": [ "checks": [
@@ -47,7 +65,13 @@
"expected": "정상 페이지 (목업 아님)" "expected": "정상 페이지 (목업 아님)"
}, },
{ {
"id": 3, "id": 4,
"name": "통계 카드 확인",
"action": "evaluate",
"script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()"
},
{
"id": 5,
"name": "미수금 테이블 구조 확인", "name": "미수금 테이블 구조 확인",
"action": "verify_table", "action": "verify_table",
"checks": [ "checks": [
@@ -60,7 +84,13 @@
"expected": "미수금 테이블 컬럼 정상 표시" "expected": "미수금 테이블 컬럼 정상 표시"
}, },
{ {
"id": 4, "id": 6,
"name": "목록 필터 테스트",
"action": "evaluate",
"script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()"
},
{
"id": 7,
"name": "통계 카드 확인", "name": "통계 카드 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -71,20 +101,29 @@
"expected": "미수금 통계 카드 표시" "expected": "미수금 통계 카드 표시"
}, },
{ {
"id": 5, "id": 8,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 기간 필터 적용", "name": "[FILTER] 기간 필터 적용",
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "input[type='date']:first-of-type, input[placeholder*='시작'], input[name*='start']" }, {
{ "type": "click_if_exists", "target": "input[type='date']:last-of-type, input[placeholder*='종료'], input[name*='end']" }, "type": "click_if_exists",
{ "type": "wait", "duration": 500 } "target": "input[type='date']:first-of-type, input[placeholder*='시작'], input[name*='start']"
},
{
"type": "click_if_exists",
"target": "input[type='date']:last-of-type, input[placeholder*='종료'], input[name*='end']"
},
{
"type": "wait",
"duration": 500
}
], ],
"expected": { "expected": {
"filter_applied": true "filter_applied": true
} }
}, },
{ {
"id": 6, "id": 9,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 필터 결과 확인", "name": "[FILTER] 필터 결과 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -94,7 +133,7 @@
} }
}, },
{ {
"id": 7, "id": 10,
"phase": "SEARCH", "phase": "SEARCH",
"name": "[SEARCH] 거래처 검색", "name": "[SEARCH] 거래처 검색",
"action": "click_if_exists", "action": "click_if_exists",
@@ -105,7 +144,7 @@
} }
}, },
{ {
"id": 8, "id": 11,
"phase": "SEARCH", "phase": "SEARCH",
"name": "[SEARCH] 검색 결과 확인", "name": "[SEARCH] 검색 결과 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -116,18 +155,22 @@
} }
}, },
{ {
"id": 9, "id": 12,
"phase": "READ", "phase": "READ",
"name": "[READ] 미수금 상세 클릭", "name": "[READ] 미수금 상세 클릭",
"action": "click_if_exists", "action": "click_if_exists",
"target": "table tbody tr:first-child", "target": "table tbody tr:first-child",
"expected": { "expected": {
"detail_modal_or_page": true, "detail_modal_or_page": true,
"visible": ["거래처", "미수금액", "상세"] "visible": [
"거래처",
"미수금액",
"상세"
]
} }
}, },
{ {
"id": 10, "id": 13,
"phase": "READ", "phase": "READ",
"name": "[READ] 상세 정보 확인", "name": "[READ] 상세 정보 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -139,7 +182,7 @@
"expected": "미수금 상세 정보 정상 표시" "expected": "미수금 상세 정보 정상 표시"
}, },
{ {
"id": 11, "id": 14,
"phase": "READ", "phase": "READ",
"name": "[READ] 목록으로 복귀", "name": "[READ] 목록으로 복귀",
"action": "click_if_exists", "action": "click_if_exists",
@@ -149,7 +192,7 @@
} }
}, },
{ {
"id": 12, "id": 15,
"phase": "EXPORT", "phase": "EXPORT",
"name": "[EXPORT] 엑셀 다운로드 버튼 확인", "name": "[EXPORT] 엑셀 다운로드 버튼 확인",
"action": "verify_elements", "action": "verify_elements",
@@ -159,7 +202,7 @@
"expected": "다운로드 기능 존재" "expected": "다운로드 기능 존재"
}, },
{ {
"id": 13, "id": 16,
"phase": "EXPORT", "phase": "EXPORT",
"name": "[EXPORT] 필수 검증 #1: 엑셀 다운로드", "name": "[EXPORT] 필수 검증 #1: 엑셀 다운로드",
"action": "click_if_exists", "action": "click_if_exists",
@@ -172,7 +215,7 @@
"expected": "엑셀 파일 다운로드" "expected": "엑셀 파일 다운로드"
}, },
{ {
"id": 14, "id": 17,
"phase": "SORT", "phase": "SORT",
"name": "[SORT] 컬럼 정렬 테스트", "name": "[SORT] 컬럼 정렬 테스트",
"action": "click_if_exists", "action": "click_if_exists",
@@ -183,7 +226,7 @@
} }
}, },
{ {
"id": 15, "id": 18,
"name": "연체 현황 탭 확인", "name": "연체 현황 탭 확인",
"action": "click_if_exists", "action": "click_if_exists",
"target": "button:has-text('연체'), [role='tab']:has-text('연체')", "target": "button:has-text('연체'), [role='tab']:has-text('연체')",
@@ -191,6 +234,12 @@
"tab_active": true, "tab_active": true,
"filtered_data": true "filtered_data": true
} }
},
{
"id": 19,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
} }
], ],
"expectedAPIs": [ "expectedAPIs": [
@@ -219,19 +268,28 @@
{ {
"id": 1, "id": 1,
"name": "파일 다운로드", "name": "파일 다운로드",
"steps": [13], "steps": [
13
],
"criteria": "엑셀 파일 다운로드 동작" "criteria": "엑셀 파일 다운로드 동작"
}, },
{ {
"id": 3, "id": 3,
"name": "검색/필터", "name": "검색/필터",
"steps": [5, 6, 7, 8], "steps": [
5,
6,
7,
8
],
"criteria": "기간 필터 및 검색 기능" "criteria": "기간 필터 및 검색 기능"
}, },
{ {
"id": 5, "id": 5,
"name": "목업 페이지 감지", "name": "목업 페이지 감지",
"steps": [2], "steps": [
2
],
"criteria": "미수금 목록, 필터, 다운로드 버튼 존재" "criteria": "미수금 목록, 필터, 다운로드 버튼 존재"
} }
], ],

View File

@@ -3,7 +3,14 @@
"name": "매출관리 테스트", "name": "매출관리 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "회계관리 > 매출관리 메뉴의 매출 조회/필터/엑셀 기능 테스트", "description": "회계관리 > 매출관리 메뉴의 매출 조회/필터/엑셀 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -27,7 +34,10 @@
"level2": "매출관리", "level2": "매출관리",
"expected": { "expected": {
"url_contains": "/accounting", "url_contains": "/accounting",
"visible": ["매출관리", "매출"] "visible": [
"매출관리",
"매출"
]
} }
}, },
{ {
@@ -43,6 +53,12 @@
}, },
{ {
"id": 3, "id": 3,
"name": "통계 카드 확인",
"action": "evaluate",
"script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()"
},
{
"id": 4,
"name": "매출 테이블 구조 확인", "name": "매출 테이블 구조 확인",
"action": "verify_table", "action": "verify_table",
"checks": [ "checks": [
@@ -55,7 +71,13 @@
"expected": "매출 테이블 표시" "expected": "매출 테이블 표시"
}, },
{ {
"id": 4, "id": 5,
"name": "목록 필터 테스트",
"action": "evaluate",
"script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()"
},
{
"id": 6,
"name": "매출 통계 카드 확인", "name": "매출 통계 카드 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -66,7 +88,7 @@
"expected": "통계 카드 표시" "expected": "통계 카드 표시"
}, },
{ {
"id": 5, "id": 7,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 기간 필터 - 시작일", "name": "[FILTER] 기간 필터 - 시작일",
"action": "click_if_exists", "action": "click_if_exists",
@@ -74,7 +96,7 @@
"expected": "날짜 선택 열림" "expected": "날짜 선택 열림"
}, },
{ {
"id": 6, "id": 8,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 기간 필터 적용", "name": "[FILTER] 기간 필터 적용",
"action": "click_if_exists", "action": "click_if_exists",
@@ -82,7 +104,7 @@
"expected": "필터 적용됨" "expected": "필터 적용됨"
}, },
{ {
"id": 7, "id": 9,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 필터 결과 확인", "name": "[FILTER] 필터 결과 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -92,7 +114,7 @@
"expected": "필터 동작 확인" "expected": "필터 동작 확인"
}, },
{ {
"id": 8, "id": 10,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 거래처별 필터", "name": "[FILTER] 거래처별 필터",
"action": "click_if_exists", "action": "click_if_exists",
@@ -100,7 +122,7 @@
"expected": "거래처 필터 가능" "expected": "거래처 필터 가능"
}, },
{ {
"id": 9, "id": 11,
"phase": "READ", "phase": "READ",
"name": "[READ] 매출 상세 보기", "name": "[READ] 매출 상세 보기",
"action": "click_if_exists", "action": "click_if_exists",
@@ -110,7 +132,7 @@
} }
}, },
{ {
"id": 10, "id": 12,
"name": "상세 정보 확인", "name": "상세 정보 확인",
"action": "verify_detail", "action": "verify_detail",
"checks": [ "checks": [
@@ -121,14 +143,14 @@
"expected": "매출 상세 정보 표시" "expected": "매출 상세 정보 표시"
}, },
{ {
"id": 11, "id": 13,
"name": "목록으로 돌아가기", "name": "목록으로 돌아가기",
"action": "click_if_exists", "action": "click_if_exists",
"target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']",
"expected": "목록 페이지로 복귀" "expected": "목록 페이지로 복귀"
}, },
{ {
"id": 12, "id": 14,
"name": "매출 합계 확인", "name": "매출 합계 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -137,7 +159,7 @@
"expected": "매출 합계 표시" "expected": "매출 합계 표시"
}, },
{ {
"id": 13, "id": 15,
"name": "엑셀 다운로드 버튼 확인", "name": "엑셀 다운로드 버튼 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -146,7 +168,7 @@
"expected": "엑셀 다운로드 기능 표시" "expected": "엑셀 다운로드 기능 표시"
}, },
{ {
"id": 14, "id": 16,
"name": "인쇄 버튼 확인", "name": "인쇄 버튼 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -155,13 +177,19 @@
"expected": "인쇄 기능 표시" "expected": "인쇄 기능 표시"
}, },
{ {
"id": 15, "id": 17,
"name": "페이지네이션 확인", "name": "페이지네이션 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
"페이지 번호 표시" "페이지 번호 표시"
], ],
"expected": "페이지네이션 표시" "expected": "페이지네이션 표시"
},
{
"id": 18,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
} }
], ],
"expectedAPIs": [ "expectedAPIs": [
@@ -180,13 +208,20 @@
{ {
"id": 3, "id": 3,
"name": "검색/필터", "name": "검색/필터",
"steps": [5, 6, 7, 8], "steps": [
5,
6,
7,
8
],
"criteria": "기간 필터 + 거래처 필터 동작" "criteria": "기간 필터 + 거래처 필터 동작"
}, },
{ {
"id": 5, "id": 5,
"name": "목업 페이지 감지", "name": "목업 페이지 감지",
"steps": [2], "steps": [
2
],
"criteria": "매출 목록, 기간 필터, 합계 표시 존재" "criteria": "매출 목록, 기간 필터, 합계 표시 존재"
} }
], ],

View File

@@ -3,7 +3,14 @@
"name": "출금관리 테스트", "name": "출금관리 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "회계관리 > 출금관리 메뉴의 출금 조회/등록/수정/삭제 전체 CRUD 테스트", "description": "회계관리 > 출금관리 메뉴의 출금 조회/등록/수정/삭제 전체 CRUD 테스트",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -39,11 +46,22 @@
"level2": "출금관리", "level2": "출금관리",
"expected": { "expected": {
"url_contains": "/accounting/withdrawals", "url_contains": "/accounting/withdrawals",
"visible": ["출금관리", "출금"] "visible": [
"출금관리",
"출금"
]
} }
}, },
{ {
"id": 2, "id": 2,
"name": "URL 검증",
"action": "verify_url",
"expected": {
"url_contains": "/accounting/withdrawals"
}
},
{
"id": 3,
"name": "필수 검증 #5: 목업 페이지 감지", "name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup", "action": "verify_not_mockup",
"checks": [ "checks": [
@@ -54,7 +72,13 @@
"expected": "정상 페이지 (목업 아님)" "expected": "정상 페이지 (목업 아님)"
}, },
{ {
"id": 3, "id": 4,
"name": "통계 카드 확인",
"action": "evaluate",
"script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()"
},
{
"id": 5,
"name": "출금 테이블 구조 확인", "name": "출금 테이블 구조 확인",
"action": "verify_table", "action": "verify_table",
"checks": [ "checks": [
@@ -67,7 +91,13 @@
"expected": "출금 테이블 컬럼 정상 표시" "expected": "출금 테이블 컬럼 정상 표시"
}, },
{ {
"id": 4, "id": 6,
"name": "목록 필터 테스트",
"action": "evaluate",
"script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()"
},
{
"id": 7,
"name": "검색 기능 테스트", "name": "검색 기능 테스트",
"action": "click_if_exists", "action": "click_if_exists",
"target": "input[placeholder*='검색']", "target": "input[placeholder*='검색']",
@@ -77,7 +107,7 @@
} }
}, },
{ {
"id": 5, "id": 8,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 출금 등록 버튼 클릭", "name": "[CREATE] 출금 등록 버튼 클릭",
"action": "click_if_exists", "action": "click_if_exists",
@@ -88,21 +118,41 @@
} }
}, },
{ {
"id": 6, "id": 9,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 출금 정보 입력", "name": "[CREATE] 출금 정보 입력",
"action": "fill_form", "action": "fill_form",
"fields": [ "fields": [
{"name": "거래처", "type": "select", "value": "E2E_TEST_출금거래처"}, {
{"name": "출금일", "type": "date", "value": "2026-02-03"}, "name": "거래처",
{"name": "금액", "type": "number", "value": "50000"}, "type": "select",
{"name": "출금방법", "type": "select", "value": "계좌이체"}, "value": "E2E_TEST_출금거래처"
{"name": "메모", "type": "text", "value": "E2E 자동화 테스트 출금_{timestamp}"} },
{
"name": "출금일",
"type": "date",
"value": "2026-02-03"
},
{
"name": "금액",
"type": "number",
"value": "50000"
},
{
"name": "출금방법",
"type": "select",
"value": "계좌이체"
},
{
"name": "메모",
"type": "text",
"value": "E2E 자동화 테스트 출금_{timestamp}"
}
], ],
"note": "타임스탬프로 고유성 보장" "note": "타임스탬프로 고유성 보장"
}, },
{ {
"id": 7, "id": 10,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 필수 검증 #2: 등록 저장", "name": "[CREATE] 필수 검증 #2: 등록 저장",
"action": "click_if_exists", "action": "click_if_exists",
@@ -116,36 +166,52 @@
"expected": "출금 등록 완료" "expected": "출금 등록 완료"
}, },
{ {
"id": "7-modal-close", "id": 11,
"phase": "CREATE",
"name": "[CREATE] 저장 완료 토스트 확인",
"action": "verify_toast",
"verify": {
"contains": "등록|완료|성공|저장"
}
},
{
"id": 12,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 모달 닫기 확인", "name": "[CREATE] 모달 닫기 확인",
"action": "close_modal_if_open", "action": "close_modal_if_open",
"expected": "모달 닫힘" "expected": "모달 닫힘"
}, },
{ {
"id": 8, "id": 13,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 등록 결과 확인", "name": "[CREATE] 등록 결과 확인",
"action": "verify_detail", "action": "verify_detail",
"search": "E2E 자동화 테스트 출금", "search": "E2E 자동화 테스트 출금",
"expected": { "expected": {
"row_exists": true, "row_exists": true,
"contains": ["E2E", "50,000"] "contains": [
"E2E",
"50,000"
]
} }
}, },
{ {
"id": 9, "id": 14,
"phase": "READ", "phase": "READ",
"name": "[READ] 출금 상세 페이지 진입", "name": "[READ] 출금 상세 페이지 진입",
"action": "click_if_exists", "action": "click_if_exists",
"target": "table tbody tr:has-text('E2E')", "target": "table tbody tr:has-text('E2E')",
"expected": { "expected": {
"url_contains": "/accounting/withdrawals/", "url_contains": "/accounting/withdrawals/",
"visible": ["출금 상세", "수정", "삭제"] "visible": [
"출금 상세",
"수정",
"삭제"
]
} }
}, },
{ {
"id": 10, "id": 15,
"phase": "READ", "phase": "READ",
"name": "[READ] 상세 정보 확인", "name": "[READ] 상세 정보 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -157,7 +223,7 @@
"expected": "입력한 데이터와 일치" "expected": "입력한 데이터와 일치"
}, },
{ {
"id": 11, "id": 16,
"phase": "UPDATE", "phase": "UPDATE",
"name": "[UPDATE] 수정 모드 진입", "name": "[UPDATE] 수정 모드 진입",
"action": "click_if_exists", "action": "click_if_exists",
@@ -168,7 +234,7 @@
} }
}, },
{ {
"id": 12, "id": 17,
"phase": "UPDATE", "phase": "UPDATE",
"name": "[UPDATE] 금액 수정", "name": "[UPDATE] 금액 수정",
"action": "click_if_exists", "action": "click_if_exists",
@@ -177,7 +243,7 @@
"clear": true "clear": true
}, },
{ {
"id": 13, "id": 18,
"phase": "UPDATE", "phase": "UPDATE",
"name": "[UPDATE] 메모 수정", "name": "[UPDATE] 메모 수정",
"action": "click_if_exists", "action": "click_if_exists",
@@ -186,7 +252,7 @@
"clear": true "clear": true
}, },
{ {
"id": 14, "id": 19,
"phase": "UPDATE", "phase": "UPDATE",
"name": "[UPDATE] 필수 검증 #2: 수정 저장", "name": "[UPDATE] 필수 검증 #2: 수정 저장",
"action": "click_if_exists", "action": "click_if_exists",
@@ -200,7 +266,16 @@
"expected": "수정 완료" "expected": "수정 완료"
}, },
{ {
"id": 15, "id": 20,
"phase": "UPDATE",
"name": "[UPDATE] 수정 완료 토스트 확인",
"action": "verify_toast",
"verify": {
"contains": "수정|완료|성공|저장"
}
},
{
"id": 21,
"phase": "UPDATE", "phase": "UPDATE",
"name": "[UPDATE] 수정 결과 확인", "name": "[UPDATE] 수정 결과 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -211,7 +286,7 @@
"expected": "수정된 데이터 반영" "expected": "수정된 데이터 반영"
}, },
{ {
"id": 16, "id": 22,
"phase": "DELETE", "phase": "DELETE",
"name": "[DELETE] 삭제 버튼 클릭", "name": "[DELETE] 삭제 버튼 클릭",
"action": "click_if_exists", "action": "click_if_exists",
@@ -222,7 +297,7 @@
} }
}, },
{ {
"id": 17, "id": 23,
"phase": "DELETE", "phase": "DELETE",
"name": "[DELETE] 필수 검증 #6: 삭제 확인", "name": "[DELETE] 필수 검증 #6: 삭제 확인",
"action": "click_if_exists", "action": "click_if_exists",
@@ -235,7 +310,7 @@
"expected": "삭제 완료 및 목록 복귀" "expected": "삭제 완료 및 목록 복귀"
}, },
{ {
"id": 18, "id": 24,
"phase": "DELETE", "phase": "DELETE",
"name": "[DELETE] 삭제 결과 확인", "name": "[DELETE] 삭제 결과 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -244,6 +319,12 @@
"row_exists": false, "row_exists": false,
"message": "테스트 데이터가 목록에서 제거됨" "message": "테스트 데이터가 목록에서 제거됨"
} }
},
{
"id": 25,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
} }
], ],
"expectedAPIs": [ "expectedAPIs": [
@@ -277,19 +358,28 @@
{ {
"id": 2, "id": 2,
"name": "등록/저장 버튼", "name": "등록/저장 버튼",
"steps": [7, 14], "steps": [
7,
14
],
"criteria": "API 호출 + 성공 토스트 + 데이터 반영" "criteria": "API 호출 + 성공 토스트 + 데이터 반영"
}, },
{ {
"id": 5, "id": 5,
"name": "목업 페이지 감지", "name": "목업 페이지 감지",
"steps": [2], "steps": [
2
],
"criteria": "출금 목록, 등록 버튼, 필터 존재" "criteria": "출금 목록, 등록 버튼, 필터 존재"
}, },
{ {
"id": 6, "id": 6,
"name": "삭제 기능", "name": "삭제 기능",
"steps": [16, 17, 18], "steps": [
16,
17,
18
],
"criteria": "DELETE API + 목록에서 제거" "criteria": "DELETE API + 목록에서 제거"
} }
], ],

View File

@@ -63,7 +63,7 @@
}, },
"steps": [ "steps": [
{ {
"id": 0, "id": 1,
"name": "사이드바 메뉴 전체 펼치기", "name": "사이드바 메뉴 전체 펼치기",
"description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비",
"actions": [ "actions": [
@@ -92,7 +92,7 @@
] ]
}, },
{ {
"id": 1, "id": 2,
"name": "1차 메뉴 찾기: 결재관리 (스크롤 포함)", "name": "1차 메뉴 찾기: 결재관리 (스크롤 포함)",
"description": "사이드바를 스크롤하며 '결재관리' 메뉴를 찾아 클릭", "description": "사이드바를 스크롤하며 '결재관리' 메뉴를 찾아 클릭",
"actions": [ "actions": [
@@ -139,7 +139,7 @@
} }
}, },
{ {
"id": 2, "id": 3,
"name": "2차 메뉴 찾기: 결재함 (스크롤 포함)", "name": "2차 메뉴 찾기: 결재함 (스크롤 포함)",
"description": "서브메뉴에서 '결재함'을 찾아 클릭", "description": "서브메뉴에서 '결재함'을 찾아 클릭",
"actions": [ "actions": [
@@ -181,7 +181,7 @@
] ]
}, },
{ {
"id": 3, "id": 4,
"name": "404 에러 감지 및 대체 경로 시도", "name": "404 에러 감지 및 대체 경로 시도",
"description": "페이지 로드 후 404 에러 여부 확인, 404시 대체 경로 탐색", "description": "페이지 로드 후 404 에러 여부 확인, 404시 대체 경로 탐색",
"actions": [ "actions": [
@@ -231,7 +231,7 @@
} }
}, },
{ {
"id": 4, "id": 5,
"name": "페이지 정상 로드 확인", "name": "페이지 정상 로드 확인",
"description": "결재함 페이지가 정상적으로 로드되었는지 확인", "description": "결재함 페이지가 정상적으로 로드되었는지 확인",
"actions": [ "actions": [
@@ -269,7 +269,7 @@
} }
}, },
{ {
"id": 5, "id": 6,
"name": "통계 카드 확인", "name": "통계 카드 확인",
"action": "verify_element", "action": "verify_element",
"target": "[class*='card'], [class*='stat']", "target": "[class*='card'], [class*='stat']",
@@ -281,7 +281,7 @@
] ]
}, },
{ {
"id": 6, "id": 7,
"name": "탭 구조 확인", "name": "탭 구조 확인",
"action": "verify_element", "action": "verify_element",
"target": "[role='tab'], button[role='tab']", "target": "[role='tab'], button[role='tab']",
@@ -293,7 +293,7 @@
] ]
}, },
{ {
"id": 7, "id": 8,
"name": "테이블 데이터 확인", "name": "테이블 데이터 확인",
"action": "verify_table", "action": "verify_table",
"target": "table", "target": "table",
@@ -304,7 +304,13 @@
] ]
}, },
{ {
"id": 8, "id": 9,
"name": "목록 필터 테스트",
"action": "evaluate",
"script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()"
},
{
"id": 10,
"name": "⚠️ 필수 검증: 결재 문서 상세 보기", "name": "⚠️ 필수 검증: 결재 문서 상세 보기",
"description": "테이블에서 결재 문서 클릭하여 상세 모달/페이지 확인", "description": "테이블에서 결재 문서 클릭하여 상세 모달/페이지 확인",
"actions": [ "actions": [
@@ -345,7 +351,7 @@
"note": "결재 문서가 없으면 데이터 생성 또는 SKIP" "note": "결재 문서가 없으면 데이터 생성 또는 SKIP"
}, },
{ {
"id": "8-pdf-1", "id": 11,
"name": "⚠️ 필수 검증: PDF 다운로드 전 모달 스크린샷", "name": "⚠️ 필수 검증: PDF 다운로드 전 모달 스크린샷",
"description": "PDF 생성 전 모달 상태를 스크린샷으로 캡처하여 CSS 문제 감지용 기준 이미지 확보", "description": "PDF 생성 전 모달 상태를 스크린샷으로 캡처하여 CSS 문제 감지용 기준 이미지 확보",
"prerequisite": "step-8의 문서 상세 모달이 열려있는 상태에서 실행", "prerequisite": "step-8의 문서 상세 모달이 열려있는 상태에서 실행",
@@ -365,7 +371,7 @@
} }
}, },
{ {
"id": "8-pdf-2", "id": 12,
"name": "⚠️ 필수 검증: PDF 다운로드 실행 및 파일 보관", "name": "⚠️ 필수 검증: PDF 다운로드 실행 및 파일 보관",
"description": "PDF 다운로드 후 파일을 지정 폴더에 보관하여 수동 검증 가능하게 함", "description": "PDF 다운로드 후 파일을 지정 폴더에 보관하여 수동 검증 가능하게 함",
"actions": [ "actions": [
@@ -414,7 +420,7 @@
} }
}, },
{ {
"id": "8-pdf-3", "id": 13,
"name": "⚠️ PDF 파일 유효성 검증", "name": "⚠️ PDF 파일 유효성 검증",
"description": "다운로드된 PDF 파일의 기본 유효성 검사", "description": "다운로드된 PDF 파일의 기본 유효성 검사",
"actions": [ "actions": [
@@ -434,7 +440,7 @@
} }
}, },
{ {
"id": "8-pdf-4", "id": 14,
"name": "📋 PDF 스타일 수동 확인 체크리스트", "name": "📋 PDF 스타일 수동 확인 체크리스트",
"type": "manualVerification", "type": "manualVerification",
"description": "개발자가 다운로드된 PDF를 열어 시각적으로 확인해야 하는 항목", "description": "개발자가 다운로드된 PDF를 열어 시각적으로 확인해야 하는 항목",
@@ -500,7 +506,7 @@
} }
}, },
{ {
"id": 9, "id": 15,
"name": "⚠️ 필수 검증 #4: 결재 승인 실제 수행", "name": "⚠️ 필수 검증 #4: 결재 승인 실제 수행",
"description": "미결재 문서에 대해 실제 승인 처리 수행", "description": "미결재 문서에 대해 실제 승인 처리 수행",
"actions": [ "actions": [
@@ -533,7 +539,7 @@
"note": "⚠️ 버튼 존재만 확인하면 불완전! 실제 승인까지 검증 필수!" "note": "⚠️ 버튼 존재만 확인하면 불완전! 실제 승인까지 검증 필수!"
}, },
{ {
"id": "9-1", "id": 16,
"name": "결재 승인 결과 확인", "name": "결재 승인 결과 확인",
"description": "승인 후 결재완료 탭에서 해당 문서 확인", "description": "승인 후 결재완료 탭에서 해당 문서 확인",
"actions": [ "actions": [
@@ -552,7 +558,7 @@
} }
}, },
{ {
"id": 10, "id": 17,
"name": "⚠️ 필수 검증 #4: 결재 반려 실제 수행", "name": "⚠️ 필수 검증 #4: 결재 반려 실제 수행",
"description": "미결재 문서에 대해 실제 반려 처리 수행", "description": "미결재 문서에 대해 실제 반려 처리 수행",
"actions": [ "actions": [
@@ -603,7 +609,7 @@
"note": "⚠️ 반려 버튼 존재만 확인하면 불완전! 실제 반려까지 검증 필수!" "note": "⚠️ 반려 버튼 존재만 확인하면 불완전! 실제 반려까지 검증 필수!"
}, },
{ {
"id": "10-1", "id": 18,
"name": "결재 반려 결과 확인", "name": "결재 반려 결과 확인",
"description": "반려 후 결재반려 탭에서 해당 문서 확인", "description": "반려 후 결재반려 탭에서 해당 문서 확인",
"actions": [ "actions": [
@@ -623,7 +629,7 @@
} }
}, },
{ {
"id": 11, "id": 19,
"name": "검색 기능 테스트", "name": "검색 기능 테스트",
"description": "검색 필터로 결재 문서 검색", "description": "검색 필터로 결재 문서 검색",
"actions": [ "actions": [
@@ -644,6 +650,12 @@
"searchApplied": true, "searchApplied": true,
"filteredResults": "검색어에 맞는 결과 표시" "filteredResults": "검색어에 맞는 결과 표시"
} }
},
{
"id": 20,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
} }
], ],
"mandatoryVerifications": { "mandatoryVerifications": {

View File

@@ -3,16 +3,27 @@
"name": "근태현황 출퇴근 테스트", "name": "근태현황 출퇴근 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "위치 정보 권한 허용 후 출근/퇴근 기록을 테스트하는 E2E 테스트", "description": "위치 정보 권한 허용 후 출근/퇴근 기록을 테스트하는 E2E 테스트",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
"url": "/hr/attendance", "url": "/hr/attendance",
"navigation": { "navigation": {
"targetUrl": "/hr/attendance", "targetUrl": "/hr/attendance",
"urlPattern": "/hr/attendance|/ko/hr/attendance", "urlPattern": "/hr/attendance|/ko/hr/attendance",
"menuHints": ["근태현황", "근태 현황", "출퇴근", "인사관리"] "menuHints": [
"근태현황",
"근태 현황",
"출퇴근",
"인사관리"
]
}, },
"menuNavigation": { "menuNavigation": {
"level1": "인사관리", "level1": "인사관리",
@@ -26,8 +37,21 @@
"description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지",
"level1": "인사관리", "level1": "인사관리",
"level2": "근태현황", "level2": "근태현황",
"alternativeLevel1Names": ["인사관리", "인사 관리", "HR", "Human Resource", "HR관리"], "alternativeLevel1Names": [
"alternativeLevel2Names": ["근태현황", "근태 현황", "출퇴근", "Attendance", "출퇴근현황", "근태관리"], "인사관리",
"인사 관리",
"HR",
"Human Resource",
"HR관리"
],
"alternativeLevel2Names": [
"근태현황",
"근태 현황",
"출퇴근",
"Attendance",
"출퇴근현황",
"근태관리"
],
"fallbackUrls": [ "fallbackUrls": [
"/hr/attendance", "/hr/attendance",
"/ko/hr/attendance", "/ko/hr/attendance",
@@ -43,15 +67,18 @@
"scrollDelay": 300 "scrollDelay": 300
} }
}, },
"timeout": 120000, "timeout": 120000,
"tags": ["hr", "attendance", "geolocation", "checkin", "checkout"], "tags": [
"hr",
"attendance",
"geolocation",
"checkin",
"checkout"
],
"auth": { "auth": {
"username": "TestUser5", "username": "TestUser5",
"password": "password123!" "password": "password123!"
}, },
"browserConfig": { "browserConfig": {
"permissions": { "permissions": {
"geolocation": { "geolocation": {
@@ -70,14 +97,17 @@
"latitude": 37.557358, "latitude": 37.557358,
"longitude": 126.864414 "longitude": 126.864414
}, },
"permissions": ["geolocation"] "permissions": [
"geolocation"
]
} }
}, },
"preTestSetup": { "preTestSetup": {
"description": "테스트 시작 전 Playwright 브라우저 컨텍스트에서 위치 권한 설정", "description": "테스트 시작 전 Playwright 브라우저 컨텍스트에서 위치 권한 설정",
"playwright": { "playwright": {
"grantPermissions": ["geolocation"], "grantPermissions": [
"geolocation"
],
"setGeolocation": { "setGeolocation": {
"latitude": 37.557358, "latitude": 37.557358,
"longitude": 126.864414, "longitude": 126.864414,
@@ -91,10 +121,9 @@
] ]
} }
}, },
"steps": [ "steps": [
{ {
"id": "step-0", "id": 1,
"name": "🔐 Geolocation API 모킹 (권한 팝업 방지)", "name": "🔐 Geolocation API 모킹 (권한 팝업 방지)",
"description": "페이지 로드 직후 Geolocation API를 모킹하여 브라우저 권한 팝업이 나타나지 않도록 함", "description": "페이지 로드 직후 Geolocation API를 모킹하여 브라우저 권한 팝업이 나타나지 않도록 함",
"executeBeforeNavigation": false, "executeBeforeNavigation": false,
@@ -105,22 +134,34 @@
"script": "(() => { const mockPosition = { coords: { latitude: 37.557358, longitude: 126.864414, accuracy: 100, altitude: null, altitudeAccuracy: null, heading: null, speed: null }, timestamp: Date.now() }; const mockGeolocation = { getCurrentPosition: (success, error, options) => { console.log('[E2E] Geolocation.getCurrentPosition - 모킹된 위치 반환'); setTimeout(() => success(mockPosition), 50); }, watchPosition: (success, error, options) => { console.log('[E2E] Geolocation.watchPosition - 모킹된 위치 반환'); setTimeout(() => success(mockPosition), 50); return 1; }, clearWatch: (id) => {} }; Object.defineProperty(navigator, 'geolocation', { value: mockGeolocation, writable: false, configurable: true }); console.log('[E2E] Geolocation API 모킹 완료 - 서울 영등포구 좌표'); return { success: true, coords: mockPosition.coords }; })()", "script": "(() => { const mockPosition = { coords: { latitude: 37.557358, longitude: 126.864414, accuracy: 100, altitude: null, altitudeAccuracy: null, heading: null, speed: null }, timestamp: Date.now() }; const mockGeolocation = { getCurrentPosition: (success, error, options) => { console.log('[E2E] Geolocation.getCurrentPosition - 모킹된 위치 반환'); setTimeout(() => success(mockPosition), 50); }, watchPosition: (success, error, options) => { console.log('[E2E] Geolocation.watchPosition - 모킹된 위치 반환'); setTimeout(() => success(mockPosition), 50); return 1; }, clearWatch: (id) => {} }; Object.defineProperty(navigator, 'geolocation', { value: mockGeolocation, writable: false, configurable: true }); console.log('[E2E] Geolocation API 모킹 완료 - 서울 영등포구 좌표'); return { success: true, coords: mockPosition.coords }; })()",
"description": "Geolocation API 모킹 (서울 영등포구 좌표: 37.557358, 126.864414)" "description": "Geolocation API 모킹 (서울 영등포구 좌표: 37.557358, 126.864414)"
}, },
{ "type": "wait", "duration": 300, "description": "모킹 적용 대기" } {
"type": "wait",
"duration": 300,
"description": "모킹 적용 대기"
}
], ],
"note": "Geolocation API를 모킹하면 브라우저가 위치 권한을 요청하지 않음" "note": "Geolocation API를 모킹하면 브라우저가 위치 권한을 요청하지 않음"
}, },
{ {
"id": "step-0-1", "id": 2,
"name": "🗺️ 브라우저 위치 권한 팝업 클릭 (좌측 상단)", "name": "🗺️ 브라우저 위치 권한 팝업 클릭 (좌측 상단)",
"description": "Chrome 브라우저 좌측 상단에 나타나는 '사이트에 있는 동안 허용' 팝업 클릭", "description": "Chrome 브라우저 좌측 상단에 나타나는 '사이트에 있는 동안 허용' 팝업 클릭",
"actions": [ "actions": [
{ "type": "wait", "duration": 1500, "description": "위치 권한 팝업 표시 대기" }, {
"type": "wait",
"duration": 1500,
"description": "위치 권한 팝업 표시 대기"
},
{ {
"type": "evaluate", "type": "evaluate",
"script": "(async function() { const permissionSelectors = [ '[class*=\"permission\"][class*=\"allow\"]', '[class*=\"infobar\"] button', '[aria-label*=\"허용\"]', '[aria-label*=\"Allow\"]', 'button:has-text(\"사이트에 있는 동안 허용\")', 'button:has-text(\"허용\")', 'button:has-text(\"Allow\")', '[data-testid*=\"permission\"]', '.permission-prompt button', '[class*=\"PermissionPrompt\"] button' ]; for (const sel of permissionSelectors) { try { const btn = document.querySelector(sel); if (btn && btn.offsetParent !== null) { btn.click(); console.log('[E2E] 위치 권한 팝업 클릭 성공:', sel); await new Promise(r => setTimeout(r, 500)); return { clicked: true, selector: sel }; } } catch(e) {} } const allButtons = Array.from(document.querySelectorAll('button, [role=\"button\"]')); const allowBtn = allButtons.find(b => { const text = b.innerText || b.textContent || ''; return text.includes('사이트에 있는 동안 허용') || text.includes('허용') || text.includes('Allow'); }); if (allowBtn && allowBtn.offsetParent !== null) { allowBtn.click(); console.log('[E2E] 위치 권한 팝업 텍스트 검색으로 클릭'); return { clicked: true, method: 'textSearch' }; } console.log('[E2E] 위치 권한 팝업 없음 (이미 허용되었거나 모킹으로 우회됨)'); return { clicked: false, reason: 'no_popup_found' }; })()", "script": "(async function() { const permissionSelectors = [ '[class*=\"permission\"][class*=\"allow\"]', '[class*=\"infobar\"] button', '[aria-label*=\"허용\"]', '[aria-label*=\"Allow\"]', 'button:has-text(\"사이트에 있는 동안 허용\")', 'button:has-text(\"허용\")', 'button:has-text(\"Allow\")', '[data-testid*=\"permission\"]', '.permission-prompt button', '[class*=\"PermissionPrompt\"] button' ]; for (const sel of permissionSelectors) { try { const btn = document.querySelector(sel); if (btn && btn.offsetParent !== null) { btn.click(); console.log('[E2E] 위치 권한 팝업 클릭 성공:', sel); await new Promise(r => setTimeout(r, 500)); return { clicked: true, selector: sel }; } } catch(e) {} } const allButtons = Array.from(document.querySelectorAll('button, [role=\"button\"]')); const allowBtn = allButtons.find(b => { const text = b.innerText || b.textContent || ''; return text.includes('사이트에 있는 동안 허용') || text.includes('허용') || text.includes('Allow'); }); if (allowBtn && allowBtn.offsetParent !== null) { allowBtn.click(); console.log('[E2E] 위치 권한 팝업 텍스트 검색으로 클릭'); return { clicked: true, method: 'textSearch' }; } console.log('[E2E] 위치 권한 팝업 없음 (이미 허용되었거나 모킹으로 우회됨)'); return { clicked: false, reason: 'no_popup_found' }; })()",
"description": "좌측 상단 권한 팝업 찾아서 클릭" "description": "좌측 상단 권한 팝업 찾아서 클릭"
}, },
{ "type": "wait", "duration": 500, "description": "권한 설정 적용 대기" } {
"type": "wait",
"duration": 500,
"description": "권한 설정 적용 대기"
}
], ],
"errorHandling": { "errorHandling": {
"onTimeout": "continue", "onTimeout": "continue",
@@ -129,7 +170,7 @@
} }
}, },
{ {
"id": "step-0-2", "id": 3,
"name": "📂 사이드바 메뉴 전체 펼치기", "name": "📂 사이드바 메뉴 전체 펼치기",
"description": "모두 펼치기 버튼을 클릭하여 전체 메뉴 펼침", "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴 펼침",
"actions": [ "actions": [
@@ -137,35 +178,63 @@
"type": "evaluate", "type": "evaluate",
"script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})"
}, },
{ "type": "wait", "duration": 300 }, {
"type": "wait",
"duration": 300
},
{ {
"type": "evaluate", "type": "evaluate",
"script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()"
}, },
{ "type": "wait", "duration": 2000 }, {
{ "type": "screenshot", "name": "after_permission_grant_and_menu_expanded" } "type": "wait",
"duration": 2000
},
{
"type": "screenshot",
"name": "after_permission_grant_and_menu_expanded"
}
], ],
"verification": [ "verification": [
"사이드바 메뉴가 펼쳐졌는지 확인" "사이드바 메뉴가 펼쳐졌는지 확인"
] ]
}, },
{ {
"id": 2, "id": 4,
"name": "1차 메뉴 찾기: 인사관리 (스크롤 포함)", "name": "1차 메뉴 찾기: 인사관리 (스크롤 포함)",
"description": "사이드바를 스크롤하며 '인사관리' 메뉴를 찾아 클릭", "description": "사이드바를 스크롤하며 '인사관리' 메뉴를 찾아 클릭",
"actions": [ "actions": [
{ {
"type": "scrollAndFind", "type": "scrollAndFind",
"target": "인사관리", "target": "인사관리",
"alternativeTexts": ["인사관리", "인사 관리", "HR", "Human Resource"], "alternativeTexts": [
"인사관리",
"인사 관리",
"HR",
"Human Resource"
],
"scrollContainer": "sidebar", "scrollContainer": "sidebar",
"maxAttempts": 10, "maxAttempts": 10,
"description": "스크롤하며 인사관리 메뉴 찾기" "description": "스크롤하며 인사관리 메뉴 찾기"
}, },
{ "type": "wait", "duration": 300 }, {
{ "type": "click_if_exists", "target": "인사관리", "description": "인사관리 메뉴 클릭" }, "type": "wait",
{ "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, "duration": 300
{ "type": "screenshot", "name": "hr_menu_expanded" } },
{
"type": "click_if_exists",
"target": "인사관리",
"description": "인사관리 메뉴 클릭"
},
{
"type": "wait",
"duration": 500,
"description": "서브메뉴 펼쳐지기 대기"
},
{
"type": "screenshot",
"name": "hr_menu_expanded"
}
], ],
"verification": [ "verification": [
"인사관리 메뉴가 클릭되었는지 확인", "인사관리 메뉴가 클릭되었는지 확인",
@@ -178,22 +247,41 @@
} }
}, },
{ {
"id": 3, "id": 5,
"name": "2차 메뉴 찾기: 근태현황 (스크롤 포함)", "name": "2차 메뉴 찾기: 근태현황 (스크롤 포함)",
"description": "서브메뉴에서 '근태현황'을 찾아 클릭", "description": "서브메뉴에서 '근태현황'을 찾아 클릭",
"actions": [ "actions": [
{ {
"type": "scrollAndFind", "type": "scrollAndFind",
"target": "근태현황", "target": "근태현황",
"alternativeTexts": ["근태현황", "근태 현황", "출퇴근", "Attendance"], "alternativeTexts": [
"근태현황",
"근태 현황",
"출퇴근",
"Attendance"
],
"scrollContainer": "submenu", "scrollContainer": "submenu",
"maxAttempts": 5, "maxAttempts": 5,
"description": "서브메뉴에서 근태현황 찾기" "description": "서브메뉴에서 근태현황 찾기"
}, },
{ "type": "wait", "duration": 200 }, {
{ "type": "click_if_exists", "target": "근태현황", "description": "근태현황 메뉴 클릭" }, "type": "wait",
{ "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 }, "duration": 200
{ "type": "screenshot", "name": "attendance_page" } },
{
"type": "click_if_exists",
"target": "근태현황",
"description": "근태현황 메뉴 클릭"
},
{
"type": "wait",
"target": "페이지 로드 완료",
"timeout": 10000
},
{
"type": "screenshot",
"name": "attendance_page"
}
], ],
"verification": [ "verification": [
"근태현황 메뉴 클릭 성공", "근태현황 메뉴 클릭 성공",
@@ -201,18 +289,27 @@
] ]
}, },
{ {
"id": 4, "id": 6,
"name": "404 에러 감지 및 대체 경로 시도", "name": "404 에러 감지 및 대체 경로 시도",
"description": "페이지 로드 후 404 에러 여부 확인, 404시 대체 경로 탐색", "description": "페이지 로드 후 404 에러 여부 확인, 404시 대체 경로 탐색",
"actions": [ "actions": [
{ "type": "wait", "duration": 1000 }, {
{ "type": "checkFor404", "indicators": [ "type": "wait",
"페이지를 찾을 수 없습니다", "duration": 1000
"404", },
"Not Found", {
"존재하지 않거나" "type": "checkFor404",
]}, "indicators": [
{ "type": "screenshot", "name": "page_load_result" } "페이지를 찾을 수 없습니다",
"404",
"Not Found",
"존재하지 않거나"
]
},
{
"type": "screenshot",
"name": "page_load_result"
}
], ],
"verification": [ "verification": [
"현재 페이지가 404인지 확인" "현재 페이지가 404인지 확인"
@@ -220,7 +317,10 @@
"onError404": { "onError404": {
"description": "404 에러 발생 시 대체 URL 시도", "description": "404 에러 발생 시 대체 URL 시도",
"actions": [ "actions": [
{ "type": "log", "message": "404 감지 - 대체 경로 탐색 시작" }, {
"type": "log",
"message": "404 감지 - 대체 경로 탐색 시작"
},
{ {
"type": "tryAlternativeUrls", "type": "tryAlternativeUrls",
"urls": [ "urls": [
@@ -239,12 +339,28 @@
} }
}, },
{ {
"id": 5, "id": 7,
"name": "페이지 정상 로드 확인", "name": "페이지 정상 로드 확인",
"description": "근태현황 페이지가 정상적으로 로드되었는지 확인", "description": "근태현황 페이지가 정상적으로 로드되었는지 확인",
"actions": [ "actions": [
{ "type": "verify", "target": "pageTitle", "contains": ["근태현황", "출퇴근", "Attendance"] }, {
{ "type": "verify", "target": "pageContent", "notContains": ["404", "찾을 수 없습니다", "Not Found"] } "type": "verify",
"target": "pageTitle",
"contains": [
"근태현황",
"출퇴근",
"Attendance"
]
},
{
"type": "verify",
"target": "pageContent",
"notContains": [
"404",
"찾을 수 없습니다",
"Not Found"
]
}
], ],
"verification": [ "verification": [
"페이지 제목 '근태현황' 또는 관련 텍스트 표시", "페이지 제목 '근태현황' 또는 관련 텍스트 표시",
@@ -253,11 +369,16 @@
], ],
"successCriteria": { "successCriteria": {
"urlPattern": "/hr/attendance", "urlPattern": "/hr/attendance",
"requiredElements": ["출퇴근", "출근", "퇴근", "현재 시간"] "requiredElements": [
"출퇴근",
"출근",
"퇴근",
"현재 시간"
]
} }
}, },
{ {
"id": "step-5", "id": 8,
"name": "브라우저 위치 권한 설정", "name": "브라우저 위치 권한 설정",
"description": "Playwright context에서 위치 정보 권한을 허용하고 가상 위치 설정", "description": "Playwright context에서 위치 정보 권한을 허용하고 가상 위치 설정",
"playwright": { "playwright": {
@@ -272,7 +393,7 @@
} }
}, },
{ {
"id": "step-6", "id": 9,
"name": "위치 정보 로딩 대기", "name": "위치 정보 로딩 대기",
"description": "Google Map 로딩 및 현재 위치 표시 대기", "description": "Google Map 로딩 및 현재 위치 표시 대기",
"waitFor": { "waitFor": {
@@ -286,7 +407,7 @@
} }
}, },
{ {
"id": "step-7", "id": 10,
"name": "사용자 정보 확인", "name": "사용자 정보 확인",
"description": "출퇴근 패널에서 로그인한 사용자 정보 확인", "description": "출퇴근 패널에서 로그인한 사용자 정보 확인",
"verify": { "verify": {
@@ -301,27 +422,39 @@
} }
}, },
{ {
"id": "step-8", "id": 11,
"name": "출근 상태 확인", "name": "출근 상태 확인",
"description": "현재 출퇴근 상태 확인 (출근 전/출근 후)", "description": "현재 출퇴근 상태 확인 (출근 전/출근 후)",
"capture": { "capture": {
"variable": "attendanceStatus", "variable": "attendanceStatus",
"checkElements": [ "checkElements": [
{ "selector": "button:has-text('출근하기')", "status": "not_checked_in" }, {
{ "selector": "text=출근 완료", "status": "checked_in" }, "selector": "button:has-text('출근하기')",
{ "selector": "button:has-text('퇴근하기')", "status": "ready_to_checkout" } "status": "not_checked_in"
},
{
"selector": "text=출근 완료",
"status": "checked_in"
},
{
"selector": "button:has-text('퇴근하기')",
"status": "ready_to_checkout"
}
] ]
} }
}, },
{ {
"id": "step-9", "id": 12,
"name": "출근하기 (미출근 상태인 경우)", "name": "출근하기 (미출근 상태인 경우)",
"description": "출근하기 버튼이 활성화된 경우 클릭하여 출근 기록", "description": "출근하기 버튼이 활성화된 경우 클릭하여 출근 기록",
"condition": { "condition": {
"if": "{attendanceStatus} == 'not_checked_in'" "if": "{attendanceStatus} == 'not_checked_in'"
}, },
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "출근하기" } {
"type": "click_if_exists",
"target": "출근하기"
}
], ],
"waitFor": { "waitFor": {
"type": "text", "type": "text",
@@ -329,16 +462,25 @@
"timeout": 5000 "timeout": 5000
}, },
"expect": { "expect": {
"toast": ["출근", "완료", "성공"], "toast": [
"visible": ["출근 완료", "출근 시간"] "출근",
"완료",
"성공"
],
"visible": [
"출근 완료",
"출근 시간"
]
} }
}, },
{ {
"id": "step-10", "id": 13,
"name": "출근 완료 상태 확인", "name": "출근 완료 상태 확인",
"description": "출근 완료 후 상태 및 출근 시간 표시 확인", "description": "출근 완료 후 상태 및 출근 시간 표시 확인",
"verify": { "verify": {
"visible": ["출근 완료"], "visible": [
"출근 완료"
],
"checkInTime": { "checkInTime": {
"format": "HH:mm:ss", "format": "HH:mm:ss",
"displayed": true "displayed": true
@@ -350,7 +492,7 @@
} }
}, },
{ {
"id": "step-11", "id": 14,
"name": "퇴근하기 버튼 상태 확인", "name": "퇴근하기 버튼 상태 확인",
"description": "출근 완료 후 퇴근하기 버튼 활성화 여부 확인", "description": "출근 완료 후 퇴근하기 버튼 활성화 여부 확인",
"verify": { "verify": {
@@ -362,7 +504,7 @@
} }
}, },
{ {
"id": "step-12", "id": 15,
"name": "퇴근하기 (선택적)", "name": "퇴근하기 (선택적)",
"description": "퇴근하기 버튼이 활성화된 경우 클릭하여 퇴근 기록", "description": "퇴근하기 버튼이 활성화된 경우 클릭하여 퇴근 기록",
"optional": true, "optional": true,
@@ -370,20 +512,33 @@
"if": "button[name='퇴근하기']:enabled" "if": "button[name='퇴근하기']:enabled"
}, },
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "퇴근하기" } {
"type": "click_if_exists",
"target": "퇴근하기"
}
], ],
"waitFor": { "waitFor": {
"type": "text", "type": "text",
"content": ["퇴근 완료", "퇴근 시간"], "content": [
"퇴근 완료",
"퇴근 시간"
],
"timeout": 5000 "timeout": 5000
}, },
"expect": { "expect": {
"toast": ["퇴근", "완료", "성공"], "toast": [
"visible": ["퇴근 완료", "퇴근 시간"] "퇴근",
"완료",
"성공"
],
"visible": [
"퇴근 완료",
"퇴근 시간"
]
} }
}, },
{ {
"id": "step-13", "id": 16,
"name": "최종 상태 확인", "name": "최종 상태 확인",
"description": "출퇴근 기록 후 최종 상태 확인", "description": "출퇴근 기록 후 최종 상태 확인",
"verify": { "verify": {
@@ -391,9 +546,14 @@
"mapDisplayed": true, "mapDisplayed": true,
"attendanceRecorded": true "attendanceRecorded": true
} }
},
{
"id": 17,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
} }
], ],
"assertions": [ "assertions": [
{ {
"type": "url", "type": "url",
@@ -417,20 +577,17 @@
"message": "현재 시간이 표시되어야 함" "message": "현재 시간이 표시되어야 함"
} }
], ],
"cleanup": { "cleanup": {
"enabled": false, "enabled": false,
"description": "출퇴근 기록은 삭제하지 않음 (업무 데이터)", "description": "출퇴근 기록은 삭제하지 않음 (업무 데이터)",
"note": "테스트 후 수동으로 관리자가 삭제 필요시 처리" "note": "테스트 후 수동으로 관리자가 삭제 필요시 처리"
}, },
"notes": { "notes": {
"testScope": "위치 권한 허용 -> 근태현황 페이지 이동 -> 출근/퇴근 기록 테스트", "testScope": "위치 권한 허용 -> 근태현황 페이지 이동 -> 출근/퇴근 기록 테스트",
"antiPattern404": "직접 URL 접근 금지 - 반드시 메뉴 클릭으로 페이지 진입", "antiPattern404": "직접 URL 접근 금지 - 반드시 메뉴 클릭으로 페이지 진입",
"scrollRequired": "사이드바 스크롤을 통해 메뉴 항목 탐색 필수", "scrollRequired": "사이드바 스크롤을 통해 메뉴 항목 탐색 필수",
"correctUrl": "/hr/attendance (기존 /ko/hr/attendance에서 수정됨)" "correctUrl": "/hr/attendance (기존 /ko/hr/attendance에서 수정됨)"
}, },
"playwrightMcpInstructions": { "playwrightMcpInstructions": {
"description": "Playwright MCP를 사용한 위치 권한 설정 방법", "description": "Playwright MCP를 사용한 위치 권한 설정 방법",
"beforeNavigation": [ "beforeNavigation": [

View File

@@ -3,7 +3,14 @@
"name": "게시판 관리 테스트", "name": "게시판 관리 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "게시판 > 게시판 관리 목록/검색/상세 기능 검증", "description": "게시판 > 게시판 관리 목록/검색/상세 기능 검증",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -25,54 +32,136 @@
"action": "menu_navigate", "action": "menu_navigate",
"level1": "게시판", "level1": "게시판",
"level2": "게시판 관리", "level2": "게시판 관리",
"expected": { "url_contains": "/board" } "expected": {
"url_contains": "/board"
}
}, },
{ {
"id": 2, "id": 2,
"name": "URL 검증",
"action": "verify_url",
"expected": {
"url_contains": "/board/board-management"
}
},
{
"id": 3,
"name": "목업 감지", "name": "목업 감지",
"action": "verify_not_mockup" "action": "verify_not_mockup"
}, },
{ {
"id": 3, "id": 4,
"name": "게시판 관리 페이지 확인", "name": "통계 카드 확인",
"action": "verify_detail", "action": "evaluate",
"checks": ["visible_text:게시판"] "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()"
}, },
{ {
"id": 4, "id": 5,
"name": "게시판 관리 페이지 확인",
"action": "verify_detail",
"checks": [
"visible_text:게시판"
]
},
{
"id": 6,
"name": "테이블 확인", "name": "테이블 확인",
"action": "verify_table" "action": "verify_table"
}, },
{ {
"id": 5, "id": 7,
"name": "목록 필터 테스트",
"action": "evaluate",
"script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()"
},
{
"id": 8,
"name": "검색 기능", "name": "검색 기능",
"action": "search", "action": "search",
"value": "테스트" "value": "테스트"
}, },
{ {
"id": 6, "id": 9,
"name": "검색 후 확인", "name": "검색 결과 대기",
"action": "verify_detail", "action": "wait",
"checks": ["visible_text:게시판"] "duration": 1000
}, },
{ {
"id": 7, "id": 10,
"name": "검색 결과 데이터 검증",
"action": "evaluate",
"script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Search result: ' + rows.length + ' rows';\n })()"
},
{
"id": 11,
"name": "검색 초기화",
"action": "evaluate",
"script": "(() => {\n const selectors = ['input[type=\"search\"]', 'input[placeholder*=\"검색\"]', 'input[placeholder*=\"Search\"]', 'input[role=\"searchbox\"]', '[class*=\"search\"] input'];\n for (const sel of selectors) {\n const el = document.querySelector(sel);\n if (el) {\n const nativeSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;\n if (nativeSetter) nativeSetter.call(el, '');\n else el.value = '';\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n return 'Search cleared';\n }\n }\n return 'No search input found (ok)';\n })()"
},
{
"id": 12,
"name": "검색 초기화 결과 대기",
"action": "wait",
"duration": 1000
},
{
"id": 13,
"name": "검색 초기화 및 복원 확인",
"action": "evaluate",
"script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Restored: ' + rows.length + ' rows';\n })()"
},
{
"id": 14,
"name": "검색 후 확인",
"action": "verify_detail",
"checks": [
"visible_text:게시판"
]
},
{
"id": 15,
"name": "첫 번째 행 클릭", "name": "첫 번째 행 클릭",
"action": "click_first_row" "action": "click_first_row"
}, },
{ {
"id": 8, "id": 16,
"name": "상세 확인", "name": "상세 페이지 로딩 대기",
"action": "verify_detail", "action": "wait",
"checks": ["visible_text:게시판"] "duration": 1000
}, },
{ {
"id": 9, "id": 17,
"name": "상세 페이지 - 콘텐츠 확인",
"action": "evaluate",
"script": "(() => {\n const inputs = document.querySelectorAll('input:not([type=\"hidden\"]), textarea, select');\n const buttons = document.querySelectorAll('button');\n const hasDetail = inputs.length > 0 || document.body.innerText.includes('상세') || document.body.innerText.includes('수정');\n return hasDetail ? 'Detail page: ' + inputs.length + ' inputs, ' + buttons.length + ' buttons' : 'List page (no detail view)';\n })()"
},
{
"id": 18,
"name": "상세 확인",
"action": "verify_detail",
"checks": [
"visible_text:게시판"
]
},
{
"id": 19,
"name": "모달 닫기", "name": "모달 닫기",
"action": "close_modal_if_open" "action": "close_modal_if_open"
}, },
{ {
"id": 10, "id": 20,
"name": "페이지네이션 확인",
"action": "evaluate",
"script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()"
},
{
"id": 21,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
},
{
"id": 22,
"name": "목록 복귀", "name": "목록 복귀",
"action": "click_if_exists", "action": "click_if_exists",
"target": "button:has-text('목록'), a:has-text('목록')" "target": "button:has-text('목록'), a:has-text('목록')"

View File

@@ -3,15 +3,25 @@
"name": "설정 - 회사정보", "name": "설정 - 회사정보",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "회사 정보 관리 기능 테스트 - 회사 정보 조회, 수정, 회사 추가 기능", "description": "회사 정보 관리 기능 테스트 - 회사 정보 조회, 수정, 회사 추가 기능",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
"navigation": { "navigation": {
"targetUrl": "/company-info", "targetUrl": "/company-info",
"urlPattern": "/company-info|/ko/company-info|/settings/company-info", "urlPattern": "/company-info|/ko/company-info|/settings/company-info",
"menuHints": ["회사정보", "회사 정보", "설정"] "menuHints": [
"회사정보",
"회사 정보",
"설정"
]
}, },
"menuNavigation": { "menuNavigation": {
"level1": "설정", "level1": "설정",
@@ -29,8 +39,20 @@
"description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지",
"level1": "설정", "level1": "설정",
"level2": "회사정보", "level2": "회사정보",
"alternativeLevel1Names": ["설정", "Settings", "환경설정", "시스템설정", "관리"], "alternativeLevel1Names": [
"alternativeLevel2Names": ["회사정보", "회사 정보", "Company Info", "회사관리", "기업정보"], "설정",
"Settings",
"환경설정",
"시스템설정",
"관리"
],
"alternativeLevel2Names": [
"회사정보",
"회사 정보",
"Company Info",
"회사관리",
"기업정보"
],
"fallbackUrls": [ "fallbackUrls": [
"/company-info", "/company-info",
"/ko/company-info", "/ko/company-info",
@@ -45,7 +67,6 @@
"scrollDelay": 300 "scrollDelay": 300
} }
}, },
"expectedAPIs": [ "expectedAPIs": [
{ {
"method": "GET", "method": "GET",
@@ -63,17 +84,30 @@
"description": "회사 추가" "description": "회사 추가"
} }
], ],
"steps": [ "steps": [
{ {
"id": 0, "id": 1,
"name": "사이드바 메뉴 전체 펼치기", "name": "사이드바 메뉴 전체 펼치기",
"description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비",
"actions": [ "actions": [
{ "type": "scroll", "target": "sidebar", "direction": "top", "description": "사이드바 최상단으로 스크롤" }, {
{ "type": "wait", "duration": 300 }, "type": "scroll",
{ "type": "evaluate", "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" }, "target": "sidebar",
{ "type": "wait", "duration": 2000 } "direction": "top",
"description": "사이드바 최상단으로 스크롤"
},
{
"type": "wait",
"duration": 300
},
{
"type": "evaluate",
"script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()"
},
{
"type": "wait",
"duration": 2000
}
], ],
"verification": [ "verification": [
"사이드바가 화면에 보이는지 확인", "사이드바가 화면에 보이는지 확인",
@@ -81,22 +115,41 @@
] ]
}, },
{ {
"id": 1, "id": 2,
"name": "1차 메뉴 찾기: 설정 (스크롤 포함)", "name": "1차 메뉴 찾기: 설정 (스크롤 포함)",
"description": "사이드바를 스크롤하며 '설정' 메뉴를 찾아 클릭", "description": "사이드바를 스크롤하며 '설정' 메뉴를 찾아 클릭",
"actions": [ "actions": [
{ {
"type": "scrollAndFind", "type": "scrollAndFind",
"target": "설정", "target": "설정",
"alternativeTexts": ["설정", "Settings", "환경설정", "시스템설정"], "alternativeTexts": [
"설정",
"Settings",
"환경설정",
"시스템설정"
],
"scrollContainer": "sidebar", "scrollContainer": "sidebar",
"maxAttempts": 10, "maxAttempts": 10,
"description": "스크롤하며 설정 메뉴 찾기" "description": "스크롤하며 설정 메뉴 찾기"
}, },
{ "type": "wait", "duration": 300 }, {
{ "type": "click_if_exists", "target": "설정", "description": "설정 메뉴 클릭" }, "type": "wait",
{ "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, "duration": 300
{ "type": "screenshot", "name": "settings_menu_expanded" } },
{
"type": "click_if_exists",
"target": "설정",
"description": "설정 메뉴 클릭"
},
{
"type": "wait",
"duration": 500,
"description": "서브메뉴 펼쳐지기 대기"
},
{
"type": "screenshot",
"name": "settings_menu_expanded"
}
], ],
"verification": [ "verification": [
"설정 메뉴가 클릭되었는지 확인", "설정 메뉴가 클릭되었는지 확인",
@@ -109,22 +162,41 @@
} }
}, },
{ {
"id": 2, "id": 3,
"name": "2차 메뉴 찾기: 회사정보 (스크롤 포함)", "name": "2차 메뉴 찾기: 회사정보 (스크롤 포함)",
"description": "서브메뉴에서 '회사정보'를 찾아 클릭", "description": "서브메뉴에서 '회사정보'를 찾아 클릭",
"actions": [ "actions": [
{ {
"type": "scrollAndFind", "type": "scrollAndFind",
"target": "회사정보", "target": "회사정보",
"alternativeTexts": ["회사정보", "회사 정보", "Company Info", "회사관리"], "alternativeTexts": [
"회사정보",
"회사 정보",
"Company Info",
"회사관리"
],
"scrollContainer": "submenu", "scrollContainer": "submenu",
"maxAttempts": 5, "maxAttempts": 5,
"description": "서브메뉴에서 회사정보 찾기" "description": "서브메뉴에서 회사정보 찾기"
}, },
{ "type": "wait", "duration": 200 }, {
{ "type": "click_if_exists", "target": "회사정보", "description": "회사정보 메뉴 클릭" }, "type": "wait",
{ "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 }, "duration": 200
{ "type": "screenshot", "name": "company_info_page" } },
{
"type": "click_if_exists",
"target": "회사정보",
"description": "회사정보 메뉴 클릭"
},
{
"type": "wait",
"target": "페이지 로드 완료",
"timeout": 10000
},
{
"type": "screenshot",
"name": "company_info_page"
}
], ],
"verification": [ "verification": [
"회사정보 메뉴 클릭 성공", "회사정보 메뉴 클릭 성공",
@@ -132,18 +204,27 @@
] ]
}, },
{ {
"id": 3, "id": 4,
"name": "404 에러 감지 및 대체 경로 시도", "name": "404 에러 감지 및 대체 경로 시도",
"description": "페이지 로드 후 404 에러 여부 확인, 404시 대체 경로 탐색", "description": "페이지 로드 후 404 에러 여부 확인, 404시 대체 경로 탐색",
"actions": [ "actions": [
{ "type": "wait", "duration": 1000 }, {
{ "type": "checkFor404", "indicators": [ "type": "wait",
"페이지를 찾을 수 없습니다", "duration": 1000
"404", },
"Not Found", {
"존재하지 않거나" "type": "checkFor404",
]}, "indicators": [
{ "type": "screenshot", "name": "page_load_result" } "페이지를 찾을 수 없습니다",
"404",
"Not Found",
"존재하지 않거나"
]
},
{
"type": "screenshot",
"name": "page_load_result"
}
], ],
"verification": [ "verification": [
"현재 페이지가 404인지 확인" "현재 페이지가 404인지 확인"
@@ -151,7 +232,10 @@
"onError404": { "onError404": {
"description": "404 에러 발생 시 대체 URL 시도", "description": "404 에러 발생 시 대체 URL 시도",
"actions": [ "actions": [
{ "type": "log", "message": "404 감지 - 대체 경로 탐색 시작" }, {
"type": "log",
"message": "404 감지 - 대체 경로 탐색 시작"
},
{ {
"type": "tryAlternativeUrls", "type": "tryAlternativeUrls",
"urls": [ "urls": [
@@ -169,12 +253,28 @@
} }
}, },
{ {
"id": 4, "id": 5,
"name": "페이지 정상 로드 확인", "name": "페이지 정상 로드 확인",
"description": "회사정보 페이지가 정상적으로 로드되었는지 확인", "description": "회사정보 페이지가 정상적으로 로드되었는지 확인",
"actions": [ "actions": [
{ "type": "verify", "target": "pageTitle", "contains": ["회사정보", "회사 정보", "Company"] }, {
{ "type": "verify", "target": "pageContent", "notContains": ["404", "찾을 수 없습니다", "Not Found"] } "type": "verify",
"target": "pageTitle",
"contains": [
"회사정보",
"회사 정보",
"Company"
]
},
{
"type": "verify",
"target": "pageContent",
"notContains": [
"404",
"찾을 수 없습니다",
"Not Found"
]
}
], ],
"verification": [ "verification": [
"페이지 제목 '회사정보' 또는 관련 텍스트 표시", "페이지 제목 '회사정보' 또는 관련 텍스트 표시",
@@ -183,7 +283,11 @@
], ],
"successCriteria": { "successCriteria": {
"urlPattern": "/company-info", "urlPattern": "/company-info",
"requiredElements": ["회사", "회사명", "대표자명"] "requiredElements": [
"회사",
"회사명",
"대표자명"
]
} }
}, },
{ {
@@ -192,7 +296,8 @@
"action": "verify", "action": "verify",
"target": "heading", "target": "heading",
"expected": "회사정보", "expected": "회사정보",
"validation": "페이지 제목이 '회사정보'로 표시됨" "validation": "페이지 제목이 '회사정보'로 표시됨",
"id": 6
}, },
{ {
"step": 6, "step": 6,
@@ -200,7 +305,8 @@
"action": "verify", "action": "verify",
"target": "button[text='회사 추가']", "target": "button[text='회사 추가']",
"expected": "button exists", "expected": "button exists",
"validation": "회사 추가 버튼이 표시됨" "validation": "회사 추가 버튼이 표시됨",
"id": 7
}, },
{ {
"step": 7, "step": 7,
@@ -208,7 +314,8 @@
"action": "verify", "action": "verify",
"target": "button[text='수정']", "target": "button[text='수정']",
"expected": "button exists", "expected": "button exists",
"validation": "수정 버튼이 표시됨" "validation": "수정 버튼이 표시됨",
"id": 8
}, },
{ {
"step": 8, "step": 8,
@@ -216,7 +323,8 @@
"action": "verify", "action": "verify",
"target": "textbox[label='회사명'][disabled]", "target": "textbox[label='회사명'][disabled]",
"expected": "프론트_테스트회사", "expected": "프론트_테스트회사",
"validation": "회사명이 표시되고 비활성화 상태" "validation": "회사명이 표시되고 비활성화 상태",
"id": 9
}, },
{ {
"step": 9, "step": 9,
@@ -224,7 +332,8 @@
"action": "verify", "action": "verify",
"target": "textbox[label='대표자명'][disabled]", "target": "textbox[label='대표자명'][disabled]",
"expected": "프론트", "expected": "프론트",
"validation": "대표자명이 표시되고 비활성화 상태" "validation": "대표자명이 표시되고 비활성화 상태",
"id": 10
}, },
{ {
"step": 10, "step": 10,
@@ -232,7 +341,8 @@
"action": "verify", "action": "verify",
"target": "textbox[label='업태'][disabled]", "target": "textbox[label='업태'][disabled]",
"expected": "업태명", "expected": "업태명",
"validation": "업태가 표시되고 비활성화 상태" "validation": "업태가 표시되고 비활성화 상태",
"id": 11
}, },
{ {
"step": 11, "step": 11,
@@ -240,7 +350,8 @@
"action": "verify", "action": "verify",
"target": "textbox[label='업종'][disabled]", "target": "textbox[label='업종'][disabled]",
"expected": "업종명", "expected": "업종명",
"validation": "업종이 표시되고 비활성화 상태" "validation": "업종이 표시되고 비활성화 상태",
"id": 12
}, },
{ {
"step": 12, "step": 12,
@@ -248,7 +359,8 @@
"action": "verify", "action": "verify",
"target": "textbox[label='주소명'][disabled]", "target": "textbox[label='주소명'][disabled]",
"expected": "주소 표시", "expected": "주소 표시",
"validation": "주소가 표시되고 비활성화 상태" "validation": "주소가 표시되고 비활성화 상태",
"id": 13
}, },
{ {
"step": 13, "step": 13,
@@ -256,7 +368,8 @@
"action": "verify", "action": "verify",
"target": "textbox[label='이메일 (아이디)'][disabled]", "target": "textbox[label='이메일 (아이디)'][disabled]",
"expected": "이메일 표시", "expected": "이메일 표시",
"validation": "이메일이 표시되고 비활성화 상태" "validation": "이메일이 표시되고 비활성화 상태",
"id": 14
}, },
{ {
"step": 14, "step": 14,
@@ -264,7 +377,8 @@
"action": "verify", "action": "verify",
"target": "textbox[label='사업자등록번호'][disabled]", "target": "textbox[label='사업자등록번호'][disabled]",
"expected": "사업자등록번호 표시", "expected": "사업자등록번호 표시",
"validation": "사업자등록번호가 표시되고 비활성화 상태" "validation": "사업자등록번호가 표시되고 비활성화 상태",
"id": 15
}, },
{ {
"step": 15, "step": 15,
@@ -272,7 +386,8 @@
"action": "click_if_exists", "action": "click_if_exists",
"target": "button[text='수정']", "target": "button[text='수정']",
"expected": "edit mode enabled", "expected": "edit mode enabled",
"validation": "수정 모드로 전환됨" "validation": "수정 모드로 전환됨",
"id": 16
}, },
{ {
"step": 16, "step": 16,
@@ -280,7 +395,8 @@
"action": "verify", "action": "verify",
"target": "textbox:not([disabled])", "target": "textbox:not([disabled])",
"expected": "fields enabled", "expected": "fields enabled",
"validation": "텍스트 필드들이 활성화됨" "validation": "텍스트 필드들이 활성화됨",
"id": 17
}, },
{ {
"step": 17, "step": 17,
@@ -288,7 +404,8 @@
"action": "click_if_exists", "action": "click_if_exists",
"target": "button[text='취소']", "target": "button[text='취소']",
"expected": "edit mode disabled", "expected": "edit mode disabled",
"validation": "조회 모드로 복귀" "validation": "조회 모드로 복귀",
"id": 18
}, },
{ {
"step": 18, "step": 18,
@@ -296,7 +413,8 @@
"action": "click_if_exists", "action": "click_if_exists",
"target": "button[text='회사 추가']", "target": "button[text='회사 추가']",
"expected": "dialog opened", "expected": "dialog opened",
"validation": "회사 추가 다이얼로그가 열림" "validation": "회사 추가 다이얼로그가 열림",
"id": 19
}, },
{ {
"step": 19, "step": 19,
@@ -304,7 +422,8 @@
"action": "verify", "action": "verify",
"target": "dialog", "target": "dialog",
"expected": "회사 추가 다이얼로그 표시", "expected": "회사 추가 다이얼로그 표시",
"validation": "다이얼로그 제목, 입력 필드, 버튼 확인" "validation": "다이얼로그 제목, 입력 필드, 버튼 확인",
"id": 20
}, },
{ {
"step": 20, "step": 20,
@@ -312,34 +431,51 @@
"action": "click_if_exists", "action": "click_if_exists",
"target": "dialog button[text='취소']", "target": "dialog button[text='취소']",
"expected": "dialog closed", "expected": "dialog closed",
"validation": "다이얼로그가 닫힘" "validation": "다이얼로그가 닫힘",
"id": 21
}, },
{ {
"step": 21, "step": 21,
"name": "수정 모드에서 데이터 변경 테스트", "name": "수정 모드에서 데이터 변경 테스트",
"description": "실제 데이터를 수정하고 저장 기능 검증", "description": "실제 데이터를 수정하고 저장 기능 검증",
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "수정", "description": "수정 모드 진입" } {
"type": "click_if_exists",
"target": "수정",
"description": "수정 모드 진입"
}
], ],
"expect": { "expect": {
"fieldsEnabled": true "fieldsEnabled": true
} },
"id": 22
}, },
{ {
"step": 22, "step": 22,
"name": "업태 필드 수정", "name": "업태 필드 수정",
"description": "업태 필드 값 변경", "description": "업태 필드 값 변경",
"actions": [ "actions": [
{ "type": "clear", "target": "업태" }, {
{ "type": "fill", "target": "업태", "value": "테스트업태_수정" } "type": "clear",
] "target": "업태"
},
{
"type": "fill",
"target": "업태",
"value": "테스트업태_수정"
}
],
"id": 23
}, },
{ {
"step": 23, "step": 23,
"name": "저장 버튼 클릭", "name": "저장 버튼 클릭",
"description": "수정된 회사 정보 저장", "description": "수정된 회사 정보 저장",
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "저장" } {
"type": "click_if_exists",
"target": "저장"
}
], ],
"waitFor": { "waitFor": {
"type": "apiResponse", "type": "apiResponse",
@@ -347,8 +483,14 @@
"timeout": 5000 "timeout": 5000
}, },
"expect": { "expect": {
"toast": ["수정", "완료", "성공", "저장"] "toast": [
} "수정",
"완료",
"성공",
"저장"
]
},
"id": 24
}, },
{ {
"step": 24, "step": 24,
@@ -360,36 +502,63 @@
"target": "업태", "target": "업태",
"expected": "테스트업태_수정" "expected": "테스트업태_수정"
} }
} },
"id": 25
}, },
{ {
"step": 25, "step": 25,
"name": "회사 추가 다이얼로그 열기", "name": "회사 추가 다이얼로그 열기",
"description": "회사 추가 버튼 클릭하여 다이얼로그 열기", "description": "회사 추가 버튼 클릭하여 다이얼로그 열기",
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "회사 추가" } {
"type": "click_if_exists",
"target": "회사 추가"
}
], ],
"expect": { "expect": {
"dialog": true, "dialog": true,
"visible": ["회사명", "대표자명", "사업자등록번호", "등록", "취소"] "visible": [
} "회사명",
"대표자명",
"사업자등록번호",
"등록",
"취소"
]
},
"id": 26
}, },
{ {
"step": 26, "step": 26,
"name": "새 회사 정보 입력", "name": "새 회사 정보 입력",
"description": "회사 추가 다이얼로그에서 필수 정보 입력", "description": "회사 추가 다이얼로그에서 필수 정보 입력",
"actions": [ "actions": [
{ "type": "fill", "target": "회사명", "value": "테스트회사_{timestamp}" }, {
{ "type": "fill", "target": "대표자명", "value": "테스트대표" }, "type": "fill",
{ "type": "fill", "target": "사업자등록번호", "value": "123-45-67890" } "target": "회사명",
] "value": "테스트회사_{timestamp}"
},
{
"type": "fill",
"target": "대표자명",
"value": "테스트대표"
},
{
"type": "fill",
"target": "사업자등록번호",
"value": "123-45-67890"
}
],
"id": 27
}, },
{ {
"step": 27, "step": 27,
"name": "회사 등록", "name": "회사 등록",
"description": "등록 버튼 클릭하여 새 회사 등록", "description": "등록 버튼 클릭하여 새 회사 등록",
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "등록" } {
"type": "click_if_exists",
"target": "등록"
}
], ],
"waitFor": { "waitFor": {
"type": "apiResponse", "type": "apiResponse",
@@ -397,9 +566,14 @@
"timeout": 5000 "timeout": 5000
}, },
"expect": { "expect": {
"toast": ["등록", "완료", "성공"], "toast": [
"등록",
"완료",
"성공"
],
"dialogClosed": true "dialogClosed": true
} },
"id": 28
}, },
{ {
"step": 28, "step": 28,
@@ -408,24 +582,49 @@
"description": "등록된 회사가 목록에 표시되는지 확인", "description": "등록된 회사가 목록에 표시되는지 확인",
"verify": { "verify": {
"visible": "테스트회사" "visible": "테스트회사"
} },
"id": 29
}, },
{ {
"step": 29, "step": 29,
"name": "원복: 업태 필드 원래 값으로 복구", "name": "원복: 업태 필드 원래 값으로 복구",
"description": "테스트 후 원래 값으로 복구", "description": "테스트 후 원래 값으로 복구",
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "수정" }, {
{ "type": "clear", "target": "업태" }, "type": "click_if_exists",
{ "type": "fill", "target": "업태", "value": "업태명" }, "target": "수정"
{ "type": "click_if_exists", "target": "저장" } },
{
"type": "clear",
"target": "업태"
},
{
"type": "fill",
"target": "업태",
"value": "업태명"
},
{
"type": "click_if_exists",
"target": "저장"
}
], ],
"expect": { "expect": {
"toast": ["수정", "완료", "성공", "저장"] "toast": [
} "수정",
"완료",
"성공",
"저장"
]
},
"id": 30
},
{
"id": 31,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
} }
], ],
"notes": [ "notes": [
"직접 URL 접근 금지: 반드시 메뉴 클릭으로 페이지 진입 (404 방지)", "직접 URL 접근 금지: 반드시 메뉴 클릭으로 페이지 진입 (404 방지)",
"스크롤 필수: 사이드바가 길 경우 메뉴가 화면 밖에 있을 수 있음", "스크롤 필수: 사이드바가 길 경우 메뉴가 화면 밖에 있을 수 있음",

View File

@@ -3,7 +3,14 @@
"name": "이벤트 게시판 테스트", "name": "이벤트 게시판 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "고객센터 > 이벤트 게시판 메뉴의 이벤트 목록 조회/상세보기 기능 테스트", "description": "고객센터 > 이벤트 게시판 메뉴의 이벤트 목록 조회/상세보기 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -27,11 +34,21 @@
"level2": "이벤트 게시판", "level2": "이벤트 게시판",
"expected": { "expected": {
"url_contains": "/customer-center", "url_contains": "/customer-center",
"visible": ["이벤트"] "visible": [
"이벤트"
]
} }
}, },
{ {
"id": 2, "id": 2,
"name": "URL 검증",
"action": "verify_url",
"expected": {
"url_contains": "/customer-center/events"
}
},
{
"id": 3,
"name": "필수 검증 #5: 목업 페이지 감지", "name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup", "action": "verify_not_mockup",
"checks": [ "checks": [
@@ -41,7 +58,13 @@
"expected": "정상 페이지 (목업 아님)" "expected": "정상 페이지 (목업 아님)"
}, },
{ {
"id": 3, "id": 4,
"name": "통계 카드 확인",
"action": "evaluate",
"script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()"
},
{
"id": 5,
"name": "이벤트 페이지 구조 확인", "name": "이벤트 페이지 구조 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -52,7 +75,7 @@
"expected": "이벤트 페이지 구조 정상" "expected": "이벤트 페이지 구조 정상"
}, },
{ {
"id": 4, "id": 6,
"phase": "READ", "phase": "READ",
"name": "[READ] 이벤트 목록 데이터 확인", "name": "[READ] 이벤트 목록 데이터 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -62,7 +85,7 @@
"expected": "이벤트 데이터 표시" "expected": "이벤트 데이터 표시"
}, },
{ {
"id": 5, "id": 7,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 상태별 필터", "name": "[FILTER] 상태별 필터",
"action": "verify_elements", "action": "verify_elements",
@@ -72,7 +95,7 @@
"expected": "상태 필터 표시" "expected": "상태 필터 표시"
}, },
{ {
"id": 6, "id": 8,
"phase": "READ", "phase": "READ",
"name": "[READ] 이벤트 상세 보기", "name": "[READ] 이벤트 상세 보기",
"action": "click_if_exists", "action": "click_if_exists",
@@ -82,7 +105,7 @@
} }
}, },
{ {
"id": 7, "id": 9,
"name": "이벤트 상세 정보 확인", "name": "이벤트 상세 정보 확인",
"action": "verify_detail", "action": "verify_detail",
"checks": [ "checks": [
@@ -94,7 +117,7 @@
"expected": "이벤트 상세 정보 표시" "expected": "이벤트 상세 정보 표시"
}, },
{ {
"id": 8, "id": 10,
"name": "이벤트 참여 버튼 확인", "name": "이벤트 참여 버튼 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -103,7 +126,7 @@
"expected": "참여 버튼 확인" "expected": "참여 버튼 확인"
}, },
{ {
"id": 9, "id": 11,
"name": "공유 기능 확인", "name": "공유 기능 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -112,14 +135,14 @@
"expected": "공유 기능 표시" "expected": "공유 기능 표시"
}, },
{ {
"id": 10, "id": 12,
"name": "목록으로 돌아가기", "name": "목록으로 돌아가기",
"action": "click_if_exists", "action": "click_if_exists",
"target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']",
"expected": "목록 페이지로 복귀" "expected": "목록 페이지로 복귀"
}, },
{ {
"id": 11, "id": 13,
"name": "페이지네이션 확인", "name": "페이지네이션 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -128,7 +151,35 @@
"expected": "페이지네이션 표시" "expected": "페이지네이션 표시"
}, },
{ {
"id": 12, "id": 14,
"name": "테이블 행 클릭 - 상세 페이지 이동",
"action": "click_first_row"
},
{
"id": 15,
"name": "상세 페이지 로딩 대기",
"action": "wait",
"duration": 1000
},
{
"id": 16,
"name": "상세 페이지 - 콘텐츠 확인",
"action": "evaluate",
"script": "(() => {\n const inputs = document.querySelectorAll('input:not([type=\"hidden\"]), textarea, select');\n const buttons = document.querySelectorAll('button');\n const hasDetail = inputs.length > 0 || document.body.innerText.includes('상세') || document.body.innerText.includes('수정');\n return hasDetail ? 'Detail page: ' + inputs.length + ' inputs, ' + buttons.length + ' buttons' : 'List page (no detail view)';\n })()"
},
{
"id": 17,
"name": "모달/상세 닫기",
"action": "close_modal_if_open"
},
{
"id": 18,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
},
{
"id": 19,
"name": "이벤트 기간 정보 확인", "name": "이벤트 기간 정보 확인",
"action": "verify_detail", "action": "verify_detail",
"checks": [ "checks": [
@@ -154,7 +205,9 @@
{ {
"id": 5, "id": 5,
"name": "목업 페이지 감지", "name": "목업 페이지 감지",
"steps": [2], "steps": [
2
],
"criteria": "이벤트 목록, 이벤트 카드/리스트 존재" "criteria": "이벤트 목록, 이벤트 카드/리스트 존재"
} }
], ],

View File

@@ -42,6 +42,14 @@
}, },
{ {
"id": 2, "id": 2,
"name": "URL 검증",
"action": "verify_url",
"expected": {
"url_contains": "/customer-center/faq"
}
},
{
"id": 3,
"name": "필수 검증 #5: 목업 페이지 감지", "name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup", "action": "verify_not_mockup",
"checks": [ "checks": [
@@ -51,7 +59,13 @@
"expected": "정상 페이지 (목업 아님)" "expected": "정상 페이지 (목업 아님)"
}, },
{ {
"id": 3, "id": 4,
"name": "통계 카드 확인",
"action": "evaluate",
"script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()"
},
{
"id": 5,
"name": "FAQ 페이지 구조 확인", "name": "FAQ 페이지 구조 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -62,7 +76,7 @@
"expected": "FAQ 페이지 구조 정상" "expected": "FAQ 페이지 구조 정상"
}, },
{ {
"id": 4, "id": 6,
"phase": "READ", "phase": "READ",
"name": "[READ] FAQ 목록 데이터 확인", "name": "[READ] FAQ 목록 데이터 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -72,7 +86,7 @@
"expected": "FAQ 데이터 표시" "expected": "FAQ 데이터 표시"
}, },
{ {
"id": 5, "id": 7,
"phase": "READ", "phase": "READ",
"name": "[READ] FAQ 항목 펼치기", "name": "[READ] FAQ 항목 펼치기",
"action": "click_if_exists", "action": "click_if_exists",
@@ -82,7 +96,7 @@
} }
}, },
{ {
"id": 6, "id": 8,
"name": "FAQ 답변 내용 확인", "name": "FAQ 답변 내용 확인",
"action": "verify_detail", "action": "verify_detail",
"checks": [ "checks": [
@@ -91,7 +105,7 @@
"expected": "FAQ 답변 표시" "expected": "FAQ 답변 표시"
}, },
{ {
"id": 7, "id": 9,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 카테고리 필터", "name": "[FILTER] 카테고리 필터",
"action": "verify_element", "action": "verify_element",
@@ -99,7 +113,7 @@
"expected": "카테고리 선택 가능" "expected": "카테고리 선택 가능"
}, },
{ {
"id": 8, "id": 10,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 카테고리 선택 후 결과", "name": "[FILTER] 카테고리 선택 후 결과",
"action": "verify_detail", "action": "verify_detail",
@@ -109,7 +123,7 @@
"expected": "카테고리 필터 동작" "expected": "카테고리 필터 동작"
}, },
{ {
"id": 9, "id": 11,
"phase": "SEARCH", "phase": "SEARCH",
"name": "[SEARCH] FAQ 검색", "name": "[SEARCH] FAQ 검색",
"action": "click_if_exists", "action": "click_if_exists",
@@ -118,7 +132,7 @@
"submit": true "submit": true
}, },
{ {
"id": 10, "id": 12,
"phase": "SEARCH", "phase": "SEARCH",
"name": "[SEARCH] 검색 결과 확인", "name": "[SEARCH] 검색 결과 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -128,7 +142,7 @@
"expected": "검색 기능 동작" "expected": "검색 기능 동작"
}, },
{ {
"id": 11, "id": 13,
"name": "FAQ 접기/펼치기 토글", "name": "FAQ 접기/펼치기 토글",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -137,7 +151,19 @@
"expected": "토글 기능 표시" "expected": "토글 기능 표시"
}, },
{ {
"id": 12, "id": 14,
"name": "페이지네이션 확인",
"action": "evaluate",
"script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()"
},
{
"id": 15,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
},
{
"id": 16,
"name": "전체 보기/접기", "name": "전체 보기/접기",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [

View File

@@ -3,7 +3,14 @@
"name": "공지사항 테스트", "name": "공지사항 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "고객센터 > 공지사항 메뉴의 공지사항 조회/검색/상세보기 기능 테스트", "description": "고객센터 > 공지사항 메뉴의 공지사항 조회/검색/상세보기 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -27,11 +34,21 @@
"level2": "공지사항", "level2": "공지사항",
"expected": { "expected": {
"url_contains": "/customer-center/notices", "url_contains": "/customer-center/notices",
"visible": ["공지사항"] "visible": [
"공지사항"
]
} }
}, },
{ {
"id": 2, "id": 2,
"name": "URL 검증",
"action": "verify_url",
"expected": {
"url_contains": "/customer-center/notices"
}
},
{
"id": 3,
"name": "필수 검증 #5: 목업 페이지 감지", "name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup", "action": "verify_not_mockup",
"checks": [ "checks": [
@@ -42,7 +59,13 @@
"expected": "정상 페이지 (목업 아님)" "expected": "정상 페이지 (목업 아님)"
}, },
{ {
"id": 3, "id": 4,
"name": "통계 카드 확인",
"action": "evaluate",
"script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()"
},
{
"id": 5,
"name": "공지사항 목록 구조 확인", "name": "공지사항 목록 구조 확인",
"action": "verify_table", "action": "verify_table",
"checks": [ "checks": [
@@ -55,7 +78,13 @@
"expected": "공지사항 목록 표시" "expected": "공지사항 목록 표시"
}, },
{ {
"id": 4, "id": 6,
"name": "목록 필터 테스트",
"action": "evaluate",
"script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()"
},
{
"id": 7,
"phase": "READ", "phase": "READ",
"name": "[READ] 공지사항 목록 데이터 확인", "name": "[READ] 공지사항 목록 데이터 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -65,7 +94,7 @@
"expected": "공지사항 데이터 표시" "expected": "공지사항 데이터 표시"
}, },
{ {
"id": 5, "id": 8,
"phase": "SEARCH", "phase": "SEARCH",
"name": "[SEARCH] 공지사항 검색", "name": "[SEARCH] 공지사항 검색",
"action": "fill", "action": "fill",
@@ -74,7 +103,7 @@
"submit": true "submit": true
}, },
{ {
"id": 6, "id": 9,
"phase": "SEARCH", "phase": "SEARCH",
"name": "[SEARCH] 검색 결과 확인", "name": "[SEARCH] 검색 결과 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -84,7 +113,7 @@
"expected": "검색 기능 동작" "expected": "검색 기능 동작"
}, },
{ {
"id": 7, "id": 10,
"phase": "SEARCH", "phase": "SEARCH",
"name": "[SEARCH] 검색 초기화", "name": "[SEARCH] 검색 초기화",
"action": "click_if_exists", "action": "click_if_exists",
@@ -92,7 +121,7 @@
"expected": "검색 초기화" "expected": "검색 초기화"
}, },
{ {
"id": 8, "id": 11,
"phase": "READ", "phase": "READ",
"name": "[READ] 공지사항 상세 보기", "name": "[READ] 공지사항 상세 보기",
"action": "click_if_exists", "action": "click_if_exists",
@@ -103,7 +132,7 @@
} }
}, },
{ {
"id": 9, "id": 12,
"name": "상세 페이지 구조 확인", "name": "상세 페이지 구조 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -115,7 +144,7 @@
"expected": "상세 페이지 정상 표시" "expected": "상세 페이지 정상 표시"
}, },
{ {
"id": 10, "id": 13,
"name": "첨부파일 확인", "name": "첨부파일 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -124,7 +153,7 @@
"expected": "첨부파일 영역 확인" "expected": "첨부파일 영역 확인"
}, },
{ {
"id": 11, "id": 14,
"name": "이전/다음 글 네비게이션", "name": "이전/다음 글 네비게이션",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -134,14 +163,14 @@
"expected": "글 네비게이션 표시" "expected": "글 네비게이션 표시"
}, },
{ {
"id": 12, "id": 15,
"name": "목록으로 돌아가기", "name": "목록으로 돌아가기",
"action": "click_if_exists", "action": "click_if_exists",
"target": "button:has-text('목록'), a:has-text('목록'), [class*='back']", "target": "button:has-text('목록'), a:has-text('목록'), [class*='back']",
"expected": "목록 페이지로 복귀" "expected": "목록 페이지로 복귀"
}, },
{ {
"id": 13, "id": 16,
"name": "페이지네이션 확인", "name": "페이지네이션 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -151,7 +180,7 @@
"expected": "페이지네이션 표시" "expected": "페이지네이션 표시"
}, },
{ {
"id": 14, "id": 17,
"name": "정렬 기능 확인", "name": "정렬 기능 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -160,13 +189,19 @@
"expected": "정렬 기능 표시" "expected": "정렬 기능 표시"
}, },
{ {
"id": 15, "id": 18,
"name": "중요 공지 표시 확인", "name": "중요 공지 표시 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
"중요/고정 공지 상단 표시 여부" "중요/고정 공지 상단 표시 여부"
], ],
"expected": "중요 공지 표시 확인" "expected": "중요 공지 표시 확인"
},
{
"id": 19,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
} }
], ],
"expectedAPIs": [ "expectedAPIs": [
@@ -185,13 +220,19 @@
{ {
"id": 3, "id": 3,
"name": "검색/필터", "name": "검색/필터",
"steps": [5, 6, 7], "steps": [
5,
6,
7
],
"criteria": "검색 기능 동작" "criteria": "검색 기능 동작"
}, },
{ {
"id": 5, "id": 5,
"name": "목업 페이지 감지", "name": "목업 페이지 감지",
"steps": [2], "steps": [
2
],
"criteria": "공지사항 목록, 검색 기능, 목록 표시 존재" "criteria": "공지사항 목록, 검색 기능, 목록 표시 존재"
} }
], ],

View File

@@ -4,7 +4,14 @@
"name": "부서관리 테스트", "name": "부서관리 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "인사관리 > 부서관리 메뉴의 부서 트리/목록 조회 및 UI 검증 테스트", "description": "인사관리 > 부서관리 메뉴의 부서 트리/목록 조회 및 UI 검증 테스트",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -28,11 +35,21 @@
"level2": "부서관리", "level2": "부서관리",
"expected": { "expected": {
"url_contains": "/hr/department", "url_contains": "/hr/department",
"visible": ["부서관리"] "visible": [
"부서관리"
]
} }
}, },
{ {
"id": 2, "id": 2,
"name": "URL 검증",
"action": "verify_url",
"expected": {
"url_contains": "/hr/department-management"
}
},
{
"id": 3,
"name": "필수 검증 #5: 목업 페이지 감지", "name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup", "action": "verify_not_mockup",
"checks": [ "checks": [
@@ -42,7 +59,13 @@
"expected": "정상 페이지 (목업 아님)" "expected": "정상 페이지 (목업 아님)"
}, },
{ {
"id": 3, "id": 4,
"name": "통계 카드 확인",
"action": "evaluate",
"script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()"
},
{
"id": 5,
"name": "부서 트리/목록 구조 확인", "name": "부서 트리/목록 구조 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -53,7 +76,7 @@
"expected": "부서관리 페이지 정상 표시" "expected": "부서관리 페이지 정상 표시"
}, },
{ {
"id": 4, "id": 6,
"phase": "READ", "phase": "READ",
"name": "[READ] 부서 목록 데이터 확인", "name": "[READ] 부서 목록 데이터 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -63,14 +86,14 @@
"expected": "부서 목록 정상" "expected": "부서 목록 정상"
}, },
{ {
"id": 5, "id": 7,
"phase": "READ", "phase": "READ",
"name": "[READ] 첫 번째 부서 노드 클릭", "name": "[READ] 첫 번째 부서 노드 클릭",
"action": "click_if_exists", "action": "click_if_exists",
"target": "table tbody tr:first-child, [class*='tree'] > *:first-child, li:first-child" "target": "table tbody tr:first-child, [class*='tree'] > *:first-child, li:first-child"
}, },
{ {
"id": 6, "id": 8,
"phase": "READ", "phase": "READ",
"name": "[READ] 부서 상세 정보 확인", "name": "[READ] 부서 상세 정보 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -80,13 +103,13 @@
"expected": "부서 상세 정보 확인" "expected": "부서 상세 정보 확인"
}, },
{ {
"id": 7, "id": 9,
"name": "부서 추가 버튼 확인", "name": "부서 추가 버튼 확인",
"action": "click_if_exists", "action": "click_if_exists",
"target": "button:has-text('추가'), button:has-text('등록'), button:has-text('부서 추가')" "target": "button:has-text('추가'), button:has-text('등록'), button:has-text('부서 추가')"
}, },
{ {
"id": 8, "id": 10,
"name": "추가 폼/모달 확인", "name": "추가 폼/모달 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -96,13 +119,13 @@
"expected": "부서 추가 폼 표시" "expected": "부서 추가 폼 표시"
}, },
{ {
"id": 9, "id": 11,
"name": "추가 모달 닫기", "name": "추가 모달 닫기",
"action": "close_modal_if_open", "action": "close_modal_if_open",
"expected": "모달 닫힘" "expected": "모달 닫힘"
}, },
{ {
"id": 10, "id": 12,
"name": "부서 트리 구조 확인", "name": "부서 트리 구조 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -111,7 +134,7 @@
"expected": "트리 구조 확인" "expected": "트리 구조 확인"
}, },
{ {
"id": 11, "id": 13,
"name": "삭제 버튼 존재 확인", "name": "삭제 버튼 존재 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -120,7 +143,19 @@
"expected": "삭제 기능 확인" "expected": "삭제 기능 확인"
}, },
{ {
"id": 12, "id": 14,
"name": "페이지네이션 확인",
"action": "evaluate",
"script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()"
},
{
"id": 15,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
},
{
"id": 16,
"name": "부서관리 페이지 최종 확인", "name": "부서관리 페이지 최종 확인",
"action": "verify_detail", "action": "verify_detail",
"checks": [ "checks": [
@@ -140,7 +175,9 @@
{ {
"id": 5, "id": 5,
"name": "목업 페이지 감지", "name": "목업 페이지 감지",
"steps": [2], "steps": [
2
],
"criteria": "부서 목록, 추가 버튼 존재" "criteria": "부서 목록, 추가 버튼 존재"
} }
], ],

View File

@@ -3,7 +3,14 @@
"name": "입금관리 테스트", "name": "입금관리 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "입금관리 목록 조회, 계정과목명 일괄변경, 상세 수정 기능 E2E 테스트", "description": "입금관리 목록 조회, 계정과목명 일괄변경, 상세 수정 기능 E2E 테스트",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -11,7 +18,11 @@
"navigation": { "navigation": {
"targetUrl": "/accounting/deposits", "targetUrl": "/accounting/deposits",
"urlPattern": "/accounting/deposits|/ko/accounting/deposits", "urlPattern": "/accounting/deposits|/ko/accounting/deposits",
"menuHints": ["입금관리", "입금 관리", "회계관리"] "menuHints": [
"입금관리",
"입금 관리",
"회계관리"
]
}, },
"menuNavigation": { "menuNavigation": {
"level1": "회계관리", "level1": "회계관리",
@@ -25,8 +36,16 @@
"description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지",
"level1": "회계관리", "level1": "회계관리",
"level2": "입금관리", "level2": "입금관리",
"alternativeLevel1Names": ["회계관리", "회계 관리", "Accounting"], "alternativeLevel1Names": [
"alternativeLevel2Names": ["입금관리", "입금 관리", "Deposits"], "회계관리",
"회계 관리",
"Accounting"
],
"alternativeLevel2Names": [
"입금관리",
"입금 관리",
"Deposits"
],
"scrollConfig": { "scrollConfig": {
"sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar",
"menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']",
@@ -36,54 +55,97 @@
} }
}, },
"timeout": 60000, "timeout": 60000,
"tags": ["accounting", "deposit", "crud"], "tags": [
"accounting",
"deposit",
"crud"
],
"auth": { "auth": {
"username": "TestUser5", "username": "TestUser5",
"password": "password123!" "password": "password123!"
}, },
"steps": [ "steps": [
{ {
"id": "step-0", "id": 1,
"name": "사이드바 메뉴 전체 펼치기", "name": "사이드바 메뉴 전체 펼치기",
"description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비",
"actions": [ "actions": [
{ "type": "scroll", "target": "sidebar", "direction": "top", "description": "사이드바 최상단으로 스크롤" }, {
{ "type": "wait", "duration": 300 }, "type": "scroll",
{ "type": "evaluate", "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" }, "target": "sidebar",
{ "type": "wait", "duration": 2000 } "direction": "top",
"description": "사이드바 최상단으로 스크롤"
},
{
"type": "wait",
"duration": 300
},
{
"type": "evaluate",
"script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()"
},
{
"type": "wait",
"duration": 2000
}
] ]
}, },
{ {
"id": "step-1", "id": 2,
"name": "2단계 메뉴 진입: 회계관리 > 입금관리", "name": "2단계 메뉴 진입: 회계관리 > 입금관리",
"description": "사이드바를 스크롤하며 회계관리 > 입금관리 메뉴를 찾아 클릭", "description": "사이드바를 스크롤하며 회계관리 > 입금관리 메뉴를 찾아 클릭",
"actions": [ "actions": [
{ {
"type": "scrollAndFind", "type": "scrollAndFind",
"target": "회계관리", "target": "회계관리",
"alternativeTexts": ["회계관리", "회계 관리", "Accounting"], "alternativeTexts": [
"회계관리",
"회계 관리",
"Accounting"
],
"scrollContainer": "sidebar", "scrollContainer": "sidebar",
"maxAttempts": 10, "maxAttempts": 10,
"description": "스크롤하며 회계관리 메뉴 찾기" "description": "스크롤하며 회계관리 메뉴 찾기"
}, },
{ "type": "click_if_exists", "target": "회계관리", "description": "회계관리 메뉴 클릭" }, {
{ "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, "type": "click_if_exists",
"target": "회계관리",
"description": "회계관리 메뉴 클릭"
},
{
"type": "wait",
"duration": 500,
"description": "서브메뉴 펼쳐지기 대기"
},
{ {
"type": "scrollAndFind", "type": "scrollAndFind",
"target": "입금관리", "target": "입금관리",
"alternativeTexts": ["입금관리", "입금 관리", "Deposits"], "alternativeTexts": [
"입금관리",
"입금 관리",
"Deposits"
],
"scrollContainer": "submenu", "scrollContainer": "submenu",
"maxAttempts": 5, "maxAttempts": 5,
"description": "서브메뉴에서 입금관리 찾기" "description": "서브메뉴에서 입금관리 찾기"
}, },
{ "type": "click_if_exists", "target": "입금관리", "description": "입금관리 메뉴 클릭" }, {
{ "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 } "type": "click_if_exists",
"target": "입금관리",
"description": "입금관리 메뉴 클릭"
},
{
"type": "wait",
"target": "페이지 로드 완료",
"timeout": 10000
}
], ],
"expect": { "expect": {
"url": "/accounting/deposits", "url": "/accounting/deposits",
"visible": ["입금관리", "총 입금"] "visible": [
"입금관리",
"총 입금"
]
}, },
"verification": [ "verification": [
"회계관리 메뉴가 펼쳐졌는지 확인", "회계관리 메뉴가 펼쳐졌는지 확인",
@@ -92,38 +154,87 @@
] ]
}, },
{ {
"id": "step-2", "id": 3,
"name": "목록 페이지 구조 확인", "name": "목록 페이지 구조 확인",
"description": "테이블 및 필터 요소 확인", "description": "테이블 및 필터 요소 확인",
"expect": { "expect": {
"visible": ["입금일", "입금계좌", "입금자명", "입금금액", "거래처", "적요", "입금유형"], "visible": [
"입금일",
"입금계좌",
"입금자명",
"입금금액",
"거래처",
"적요",
"입금유형"
],
"elements": { "elements": {
"statisticsCards": ["총 입금", "당월 입금", "거래처 미설정", "입금유형 미설정"], "statisticsCards": [
"filters": ["계정과목명", "저장", "새로고침"], "총 입금",
"당월 입금",
"거래처 미설정",
"입금유형 미설정"
],
"filters": [
"계정과목명",
"저장",
"새로고침"
],
"pagination": true "pagination": true
} }
} }
}, },
{ {
"id": "step-3", "id": 4,
"name": "계정과목명 드롭다운 옵션 확인", "name": "계정과목명 드롭다운 옵션 확인",
"description": "계정과목명 일괄변경 드롭다운 옵션 검증", "description": "계정과목명 일괄변경 드롭다운 옵션 검증",
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "계정과목명 드롭다운", "description": "드롭다운 열기" } {
"type": "click_if_exists",
"target": "계정과목명 드롭다운",
"description": "드롭다운 열기"
}
], ],
"expect": { "expect": {
"options": ["미설정", "매출대금", "선수금", "가수금", "임대수익", "이자수익", "보증금 반환", "차입금", "자본금", "부가세 환급", "기타"] "options": [
"미설정",
"매출대금",
"선수금",
"가수금",
"임대수익",
"이자수익",
"보증금 반환",
"차입금",
"자본금",
"부가세 환급",
"기타"
]
} }
}, },
{ {
"id": "step-4", "id": 5,
"name": "체크박스 선택 후 계정과목명 일괄변경", "name": "체크박스 선택 후 계정과목명 일괄변경",
"description": "테이블 행 선택 후 계정과목명 일괄변경 저장", "description": "테이블 행 선택 후 계정과목명 일괄변경 저장",
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "첫 번째 행 체크박스", "description": "행 선택" }, {
{ "type": "click_if_exists", "target": "계정과목명 드롭다운", "description": "드롭다운 열기" }, "type": "click_if_exists",
{ "type": "click_if_exists", "target": "매출대금", "description": "매출대금 선택" }, "target": "첫 번째 행 체크박스",
{ "type": "click_if_exists", "target": "저장", "description": "저장 버튼 클릭" } "description": "행 선택"
},
{
"type": "click_if_exists",
"target": "계정과목명 드롭다운",
"description": "드롭다운 열기"
},
{
"type": "click_if_exists",
"target": "매출대금",
"description": "매출대금 선택"
},
{
"type": "click_if_exists",
"target": "저장",
"description": "저장 버튼 클릭"
}
], ],
"expect": { "expect": {
"dialog": "확인 다이얼로그 표시", "dialog": "확인 다이얼로그 표시",
@@ -131,7 +242,7 @@
} }
}, },
{ {
"id": "step-4-1", "id": 6,
"name": "⚠️ 필수 검증: 계정과목명 변경 데이터 반영 확인", "name": "⚠️ 필수 검증: 계정과목명 변경 데이터 반영 확인",
"note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!",
"description": "저장 후 테이블에서 변경된 입금유형 값 확인", "description": "저장 후 테이블에서 변경된 입금유형 값 확인",
@@ -145,100 +256,215 @@
"knownBugReference": "BUG-SALES-20260115-001 (매출관리 동일 버그 확인 필요)" "knownBugReference": "BUG-SALES-20260115-001 (매출관리 동일 버그 확인 필요)"
}, },
{ {
"id": "step-5", "id": 7,
"name": "입금 상세 페이지 이동", "name": "입금 상세 페이지 이동",
"description": "테이블 행 클릭하여 상세 페이지로 이동", "description": "테이블 행 클릭하여 상세 페이지로 이동",
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "테이블 첫 번째 행", "description": "행 클릭 (체크박스 제외 영역)" } {
"type": "click_if_exists",
"target": "테이블 첫 번째 행",
"description": "행 클릭 (체크박스 제외 영역)"
}
], ],
"expect": { "expect": {
"url": "/accounting/deposits/{id}", "url": "/accounting/deposits/{id}",
"visible": ["입금 상세", "기본 정보", "목록", "삭제", "수정"] "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", "id": 8,
"name": "상세 페이지 읽기 모드 필드 확인",
"description": "수정 전 필드들이 비활성화 상태인지 확인",
"expect": {
"fields": [
{
"name": "입금일",
"disabled": true
},
{
"name": "입금계좌",
"disabled": true
},
{
"name": "입금자명",
"disabled": true
},
{
"name": "입금금액",
"disabled": true
},
{
"name": "적요",
"disabled": true
},
{
"name": "거래처",
"disabled": true
},
{
"name": "입금 유형",
"disabled": true
}
]
}
},
{
"id": 9,
"name": "수정 모드 전환", "name": "수정 모드 전환",
"description": "수정 버튼 클릭하여 편집 모드로 전환", "description": "수정 버튼 클릭하여 편집 모드로 전환",
"click": "수정", "click": "수정",
"expect": { "expect": {
"url": "/accounting/deposits/{id}?mode=edit", "url": "/accounting/deposits/{id}?mode=edit",
"visible": ["입금 수정", "취소", "저장"], "visible": [
"notVisible": ["목록", "삭제", "수정"] "입금 수정",
"취소",
"저장"
],
"notVisible": [
"목록",
"삭제",
"수정"
]
} }
}, },
{ {
"id": "step-8", "id": 10,
"name": "수정 모드 필드 활성화 검증", "name": "수정 모드 필드 활성화 검증",
"description": "수정 가능한 필드와 불가능한 필드 확인", "description": "수정 가능한 필드와 불가능한 필드 확인",
"expect": { "expect": {
"fields": [ "fields": [
{ "name": "입금일", "disabled": true, "note": "은행데이터 - 수정 불가" }, {
{ "name": "입금계좌", "disabled": true, "note": "은행데이터 - 수정 불가" }, "name": "입금일",
{ "name": "입금자명", "disabled": true, "note": "은행데이터 - 수정 불가" }, "disabled": true,
{ "name": "입금금액", "disabled": true, "note": "은행데이터 - 수정 불가" }, "note": "은행데이터 - 수정 불가"
{ "name": "적요", "disabled": false, "editable": true }, },
{ "name": "거래처", "disabled": false, "type": "combobox", "editable": true }, {
{ "name": "입금 유형", "disabled": false, "type": "combobox", "editable": true } "name": "입금계좌",
"disabled": true,
"note": "은행데이터 - 수정 불가"
},
{
"name": "입금자명",
"disabled": true,
"note": "은행데이터 - 수정 불가"
},
{
"name": "입금금액",
"disabled": true,
"note": "은행데이터 - 수정 불가"
},
{
"name": "적요",
"disabled": false,
"editable": true
},
{
"name": "거래처",
"disabled": false,
"type": "combobox",
"editable": true
},
{
"name": "입금 유형",
"disabled": false,
"type": "combobox",
"editable": true
}
] ]
} }
}, },
{ {
"id": "step-9", "id": 11,
"name": "거래처 드롭다운 옵션 확인", "name": "거래처 드롭다운 옵션 확인",
"description": "거래처 선택 드롭다운 옵션 검증", "description": "거래처 선택 드롭다운 옵션 검증",
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "거래처 드롭다운", "description": "드롭다운 열기" } {
"type": "click_if_exists",
"target": "거래처 드롭다운",
"description": "드롭다운 열기"
}
], ],
"expect": { "expect": {
"options": ["거래처테스트", "아크더레드", "코브라브릿지", "가우스전자", "아크아크"] "options": [
"거래처테스트",
"아크더레드",
"코브라브릿지",
"가우스전자",
"아크아크"
]
} }
}, },
{ {
"id": "step-10", "id": 12,
"name": "입금 유형 드롭다운 옵션 확인", "name": "입금 유형 드롭다운 옵션 확인",
"description": "입금 유형 선택 드롭다운 옵션 검증", "description": "입금 유형 선택 드롭다운 옵션 검증",
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "입금 유형 드롭다운", "description": "드롭다운 열기" } {
"type": "click_if_exists",
"target": "입금 유형 드롭다운",
"description": "드롭다운 열기"
}
], ],
"expect": { "expect": {
"options": ["미설정", "매출대금", "선수금", "가수금", "임대수익", "이자수익", "보증금 반환", "차입금", "자본금", "부가세 환급", "기타"] "options": [
"미설정",
"매출대금",
"선수금",
"가수금",
"임대수익",
"이자수익",
"보증금 반환",
"차입금",
"자본금",
"부가세 환급",
"기타"
]
} }
}, },
{ {
"id": "step-11", "id": 13,
"name": "수정 데이터 입력", "name": "수정 데이터 입력",
"description": "수정 가능한 필드에 테스트 데이터 입력", "description": "수정 가능한 필드에 테스트 데이터 입력",
"form": { "form": {
"fields": [ "fields": [
{ "name": "적요", "type": "text", "value": "테스트 적요 수정" } {
"name": "적요",
"type": "text",
"value": "테스트 적요 수정"
}
] ]
}, },
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "거래처 드롭다운", "description": "거래처 드롭다운 열기" }, {
{ "type": "click_if_exists", "target": "거래처테스트", "description": "거래처 선택" }, "type": "click_if_exists",
{ "type": "click_if_exists", "target": "입금 유형 드롭다운", "description": "입금 유형 드롭다운 열기" }, "target": "거래처 드롭다운",
{ "type": "click_if_exists", "target": "매출대금", "description": "매출대금 선택" } "description": "거래처 드롭다운 열기"
},
{
"type": "click_if_exists",
"target": "거래처테스트",
"description": "거래처 선택"
},
{
"type": "click_if_exists",
"target": "입금 유형 드롭다운",
"description": "입금 유형 드롭다운 열기"
},
{
"type": "click_if_exists",
"target": "매출대금",
"description": "매출대금 선택"
}
] ]
}, },
{ {
"id": "step-12", "id": 14,
"name": "저장 및 결과 확인", "name": "저장 및 결과 확인",
"description": "저장 버튼 클릭 후 데이터 반영 확인", "description": "저장 버튼 클릭 후 데이터 반영 확인",
"click": "저장", "click": "저장",
@@ -249,61 +475,105 @@
} }
}, },
{ {
"id": "step-12-1", "id": 15,
"name": "⚠️ 필수 검증: 수정 데이터 반영 확인", "name": "⚠️ 필수 검증: 수정 데이터 반영 확인",
"note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!",
"description": "저장 후 상세 페이지에서 변경된 값 확인", "description": "저장 후 상세 페이지에서 변경된 값 확인",
"expect": { "expect": {
"fields": [ "fields": [
{ "name": "적요", "value": "테스트 적요 수정" }, {
{ "name": "거래처", "value": "거래처테스트" }, "name": "적요",
{ "name": "입금 유형", "value": "매출대금" } "value": "테스트 적요 수정"
},
{
"name": "거래처",
"value": "거래처테스트"
},
{
"name": "입금 유형",
"value": "매출대금"
}
] ]
} }
}, },
{ {
"id": "step-13", "id": 16,
"name": "취소 버튼 동작 확인", "name": "취소 버튼 동작 확인",
"description": "수정 모드에서 취소 버튼 동작 검증", "description": "수정 모드에서 취소 버튼 동작 검증",
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "수정", "description": "수정 모드 진입" }, {
{ "type": "click_if_exists", "target": "취소", "description": "취소 버튼 클릭" } "type": "click_if_exists",
"target": "수정",
"description": "수정 모드 진입"
},
{
"type": "click_if_exists",
"target": "취소",
"description": "취소 버튼 클릭"
}
], ],
"expect": { "expect": {
"url": "/accounting/deposits/{id}", "url": "/accounting/deposits/{id}",
"mode": "view", "mode": "view",
"visible": ["입금 상세", "목록", "삭제", "수정"] "visible": [
"입금 상세",
"목록",
"삭제",
"수정"
]
} }
}, },
{ {
"id": "step-14", "id": 17,
"name": "목록 버튼 동작 확인", "name": "목록 버튼 동작 확인",
"description": "목록 버튼 클릭하여 목록 페이지로 이동", "description": "목록 버튼 클릭하여 목록 페이지로 이동",
"click": "목록", "click": "목록",
"expect": { "expect": {
"url": "/accounting/deposits", "url": "/accounting/deposits",
"visible": ["입금관리", "총 입금"] "visible": [
"입금관리",
"총 입금"
]
} }
}, },
{ {
"id": "step-15", "id": 18,
"name": "필터 드롭다운 검증", "name": "필터 드롭다운 검증",
"description": "목록 페이지 필터 드롭다운 옵션 확인", "description": "목록 페이지 필터 드롭다운 옵션 확인",
"note": "3개의 필터 드롭다운 존재 (거래처, 입금유형, 정렬)", "note": "3개의 필터 드롭다운 존재 (거래처, 입금유형, 정렬)",
"expect": { "expect": {
"filters": [ "filters": [
{ "name": "거래처 필터", "default": "전체" }, {
{ "name": "입금유형 필터", "default": "전체" }, "name": "거래처 필터",
{ "name": "정렬", "default": "최신순", "options": ["최신순", "등록순", "금액 높은순", "금액 낮은순"] } "default": "전체"
},
{
"name": "입금유형 필터",
"default": "전체"
},
{
"name": "정렬",
"default": "최신순",
"options": [
"최신순",
"등록순",
"금액 높은순",
"금액 낮은순"
]
}
] ]
} }
}, },
{ {
"id": "step-16", "id": 19,
"name": "날짜 필터 검증", "name": "날짜 필터 검증",
"description": "날짜 필터 버튼 동작 확인", "description": "날짜 필터 버튼 동작 확인",
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "당해년도", "description": "당해년도 버튼 클릭" } {
"type": "click_if_exists",
"target": "당해년도",
"description": "당해년도 버튼 클릭"
}
], ],
"expect": { "expect": {
"dateRange": { "dateRange": {
@@ -313,7 +583,7 @@
} }
}, },
{ {
"id": "step-17", "id": 20,
"name": "페이지네이션 동작 확인", "name": "페이지네이션 동작 확인",
"description": "페이지네이션 버튼 동작 검증", "description": "페이지네이션 버튼 동작 검증",
"expect": { "expect": {
@@ -325,15 +595,24 @@
} }
}, },
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "다음", "description": "다음 페이지로 이동" } {
"type": "click_if_exists",
"target": "다음",
"description": "다음 페이지로 이동"
}
], ],
"expectAfterAction": { "expectAfterAction": {
"currentPage": 2, "currentPage": 2,
"displayText": "전체 60개 중 21-40개 표시" "displayText": "전체 60개 중 21-40개 표시"
} }
},
{
"id": 21,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
} }
], ],
"deleteTest": { "deleteTest": {
"note": "삭제 테스트 - 이전에 스킵되었으나 CRUD 완전성을 위해 추가", "note": "삭제 테스트 - 이전에 스킵되었으나 CRUD 완전성을 위해 추가",
"steps": [ "steps": [
@@ -342,11 +621,17 @@
"name": "삭제 버튼 클릭", "name": "삭제 버튼 클릭",
"description": "상세 페이지에서 삭제 버튼 클릭", "description": "상세 페이지에서 삭제 버튼 클릭",
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "삭제" } {
"type": "click_if_exists",
"target": "삭제"
}
], ],
"expect": { "expect": {
"confirmDialog": true, "confirmDialog": true,
"dialogText": ["삭제", "하시겠습니까"] "dialogText": [
"삭제",
"하시겠습니까"
]
} }
}, },
{ {
@@ -354,7 +639,11 @@
"name": "삭제 확인", "name": "삭제 확인",
"description": "삭제 확인 다이얼로그에서 확인 클릭", "description": "삭제 확인 다이얼로그에서 확인 클릭",
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "확인", "description": "삭제 확인" } {
"type": "click_if_exists",
"target": "확인",
"description": "삭제 확인"
}
], ],
"waitFor": { "waitFor": {
"type": "navigation", "type": "navigation",
@@ -362,7 +651,11 @@
"timeout": 5000 "timeout": 5000
}, },
"expect": { "expect": {
"toast": ["삭제", "완료", "성공"], "toast": [
"삭제",
"완료",
"성공"
],
"url": "/accounting/deposits" "url": "/accounting/deposits"
} }
}, },
@@ -377,16 +670,16 @@
} }
] ]
}, },
"knownBugs": [ "knownBugs": [
{ {
"id": "BUG-SALES-20260115-001", "id": "BUG-SALES-20260115-001",
"description": "계정과목명 일괄변경 시 토스트 성공 표시되나 실제 데이터 미변경 (매출관리)", "description": "계정과목명 일괄변경 시 토스트 성공 표시되나 실제 데이터 미변경 (매출관리)",
"relatedSteps": ["step-4-1"], "relatedSteps": [
"step-4-1"
],
"note": "입금관리에서도 동일한 버그가 존재할 수 있으므로 step-4-1에서 검증 필수" "note": "입금관리에서도 동일한 버그가 존재할 수 있으므로 step-4-1에서 검증 필수"
} }
], ],
"testData": { "testData": {
"sampleDeposit": { "sampleDeposit": {
"date": "2025-12-28", "date": "2025-12-28",
@@ -403,55 +696,129 @@
"depositType": "매출대금" "depositType": "매출대금"
} }
}, },
"pageStructure": { "pageStructure": {
"listPage": { "listPage": {
"url": "/accounting/deposits", "url": "/accounting/deposits",
"title": "입금관리", "title": "입금관리",
"statistics": ["총 입금", "당월 입금", "거래처 미설정", "입금유형 미설정"], "statistics": [
"tableColumns": ["checkbox", "입금일", "입금계좌", "입금자명", "입금금액", "거래처", "적요", "입금유형", "action"], "총 입금",
"당월 입금",
"거래처 미설정",
"입금유형 미설정"
],
"tableColumns": [
"checkbox",
"입금일",
"입금계좌",
"입금자명",
"입금금액",
"거래처",
"적요",
"입금유형",
"action"
],
"batchUpdate": { "batchUpdate": {
"label": "계정과목명", "label": "계정과목명",
"saveButton": "저장" "saveButton": "저장"
}, },
"filters": ["거래처", "입금유형", "정렬"], "filters": [
"dateFilters": ["당해년도", "전전월", "전월", "당월", "어제", "오늘"] "거래처",
"입금유형",
"정렬"
],
"dateFilters": [
"당해년도",
"전전월",
"전월",
"당월",
"어제",
"오늘"
]
}, },
"detailPage": { "detailPage": {
"url": "/accounting/deposits/{id}", "url": "/accounting/deposits/{id}",
"title": "입금 상세", "title": "입금 상세",
"buttons": ["목록", "삭제", "수정"], "buttons": [
"목록",
"삭제",
"수정"
],
"fields": { "fields": {
"readOnly": ["입금일", "입금계좌", "입금자명", "입금금액"], "readOnly": [
"editable": ["적요", "거래처", "입금 유형"] "입금",
"입금계좌",
"입금자명",
"입금금액"
],
"editable": [
"적요",
"거래처",
"입금 유형"
]
} }
}, },
"editPage": { "editPage": {
"url": "/accounting/deposits/{id}?mode=edit", "url": "/accounting/deposits/{id}?mode=edit",
"title": "입금 수정", "title": "입금 수정",
"buttons": ["취소", "저장"] "buttons": [
"취소",
"저장"
]
} }
}, },
"dropdownOptions": { "dropdownOptions": {
"accountSubject": { "accountSubject": {
"label": "계정과목명", "label": "계정과목명",
"options": ["미설정", "매출대금", "선수금", "가수금", "임대수익", "이자수익", "보증금 반환", "차입금", "자본금", "부가세 환급", "기타"] "options": [
"미설정",
"매출대금",
"선수금",
"가수금",
"임대수익",
"이자수익",
"보증금 반환",
"차입금",
"자본금",
"부가세 환급",
"기타"
]
}, },
"depositType": { "depositType": {
"label": "입금 유형", "label": "입금 유형",
"options": ["미설정", "매출대금", "선수금", "가수금", "임대수익", "이자수익", "보증금 반환", "차입금", "자본금", "부가세 환급", "기타"] "options": [
"미설정",
"매출대금",
"선수금",
"가수금",
"임대수익",
"이자수익",
"보증금 반환",
"차입금",
"자본금",
"부가세 환급",
"기타"
]
}, },
"vendor": { "vendor": {
"label": "거래처", "label": "거래처",
"options": ["거래처테스트", "아크더레드", "코브라브릿지", "가우스전자", "아크아크"] "options": [
"거래처테스트",
"아크더레드",
"코브라브릿지",
"가우스전자",
"아크아크"
]
}, },
"sortOrder": { "sortOrder": {
"label": "정렬", "label": "정렬",
"options": ["최신순", "등록순", "금액 높은순", "금액 낮은순"] "options": [
"최신순",
"등록순",
"금액 높은순",
"금액 낮은순"
]
} }
}, },
"assertions": [ "assertions": [
{ {
"type": "url", "type": "url",

View File

@@ -4,7 +4,14 @@
"name": "기안함 테스트", "name": "기안함 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "결재관리 > 기안함 메뉴의 문서 목록 조회, 검색, 필터 기능 테스트", "description": "결재관리 > 기안함 메뉴의 문서 목록 조회, 검색, 필터 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -28,11 +35,21 @@
"level2": "기안함", "level2": "기안함",
"expected": { "expected": {
"url_contains": "/approval/draft", "url_contains": "/approval/draft",
"visible": ["기안함"] "visible": [
"기안함"
]
} }
}, },
{ {
"id": 2, "id": 2,
"name": "URL 검증",
"action": "verify_url",
"expected": {
"url_contains": "/approval/draft"
}
},
{
"id": 3,
"name": "필수 검증 #5: 목업 페이지 감지", "name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup", "action": "verify_not_mockup",
"checks": [ "checks": [
@@ -43,7 +60,7 @@
"expected": "정상 페이지 (목업 아님)" "expected": "정상 페이지 (목업 아님)"
}, },
{ {
"id": 3, "id": 4,
"name": "통계 카드 확인", "name": "통계 카드 확인",
"action": "verify_element", "action": "verify_element",
"checks": [ "checks": [
@@ -55,7 +72,7 @@
"expected": "통계 카드 4개 표시" "expected": "통계 카드 4개 표시"
}, },
{ {
"id": 4, "id": 5,
"name": "기안함 테이블 구조 확인", "name": "기안함 테이블 구조 확인",
"action": "verify_table", "action": "verify_table",
"checks": [ "checks": [
@@ -69,7 +86,13 @@
"expected": "기안함 테이블 컬럼 정상 표시" "expected": "기안함 테이블 컬럼 정상 표시"
}, },
{ {
"id": 5, "id": 6,
"name": "목록 필터 테스트",
"action": "evaluate",
"script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()"
},
{
"id": 7,
"name": "데이터 로드 확인", "name": "데이터 로드 확인",
"action": "verify_detail", "action": "verify_detail",
"checks": [ "checks": [
@@ -78,7 +101,7 @@
"expected": "기안 문서 데이터 표시" "expected": "기안 문서 데이터 표시"
}, },
{ {
"id": 6, "id": 8,
"phase": "SEARCH", "phase": "SEARCH",
"name": "[SEARCH] 검색 기능 테스트", "name": "[SEARCH] 검색 기능 테스트",
"action": "fill", "action": "fill",
@@ -87,7 +110,7 @@
"submit": true "submit": true
}, },
{ {
"id": 7, "id": 9,
"phase": "SEARCH", "phase": "SEARCH",
"name": "[SEARCH] 검색 결과 확인", "name": "[SEARCH] 검색 결과 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -97,7 +120,7 @@
"expected": "검색 기능 동작" "expected": "검색 기능 동작"
}, },
{ {
"id": 8, "id": 10,
"phase": "SEARCH", "phase": "SEARCH",
"name": "[SEARCH] 검색 초기화", "name": "[SEARCH] 검색 초기화",
"action": "click_if_exists", "action": "click_if_exists",
@@ -105,14 +128,14 @@
"expected": "검색 초기화" "expected": "검색 초기화"
}, },
{ {
"id": 9, "id": 11,
"name": "필터 기능 테스트", "name": "필터 기능 테스트",
"action": "click_if_exists", "action": "click_if_exists",
"target": "select, [role='combobox'], button:has-text('임시저장')", "target": "select, [role='combobox'], button:has-text('임시저장')",
"expected": "필터 옵션 표시" "expected": "필터 옵션 표시"
}, },
{ {
"id": 10, "id": 12,
"phase": "READ", "phase": "READ",
"name": "[READ] 문서 상세 보기", "name": "[READ] 문서 상세 보기",
"action": "click_if_exists", "action": "click_if_exists",
@@ -122,7 +145,7 @@
} }
}, },
{ {
"id": 11, "id": 13,
"name": "상세 페이지/모달 확인", "name": "상세 페이지/모달 확인",
"action": "verify_element", "action": "verify_element",
"checks": [ "checks": [
@@ -132,13 +155,13 @@
"expected": "상세 정보 표시" "expected": "상세 정보 표시"
}, },
{ {
"id": 12, "id": 14,
"name": "모달/상세 닫기", "name": "모달/상세 닫기",
"action": "close_modal_if_open", "action": "close_modal_if_open",
"expected": "모달 닫힘" "expected": "모달 닫힘"
}, },
{ {
"id": 13, "id": 15,
"name": "페이지네이션 확인", "name": "페이지네이션 확인",
"action": "verify_element", "action": "verify_element",
"checks": [ "checks": [
@@ -147,13 +170,19 @@
"expected": "페이지네이션 표시" "expected": "페이지네이션 표시"
}, },
{ {
"id": 14, "id": 16,
"name": "문서 작성 버튼 확인", "name": "문서 작성 버튼 확인",
"action": "verify_element", "action": "verify_element",
"checks": [ "checks": [
"문서 작성 또는 신규 작성 버튼 존재" "문서 작성 또는 신규 작성 버튼 존재"
], ],
"expected": "문서 작성 버튼 확인" "expected": "문서 작성 버튼 확인"
},
{
"id": 17,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
} }
], ],
"expectedAPIs": [ "expectedAPIs": [
@@ -172,13 +201,19 @@
{ {
"id": 3, "id": 3,
"name": "검색/필터", "name": "검색/필터",
"steps": [6, 7, 8], "steps": [
6,
7,
8
],
"criteria": "검색 기능 동작" "criteria": "검색 기능 동작"
}, },
{ {
"id": 5, "id": 5,
"name": "목업 페이지 감지", "name": "목업 페이지 감지",
"steps": [2], "steps": [
2
],
"criteria": "기안함 목록, 검색 기능, 문서 작성 버튼 존재" "criteria": "기안함 목록, 검색 기능, 문서 작성 버튼 존재"
} }
], ],

View File

@@ -3,7 +3,14 @@
"name": "직원 등록 테스트", "name": "직원 등록 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "신규 직원 정보를 입력하고 등록하는 E2E 테스트", "description": "신규 직원 정보를 입력하고 등록하는 E2E 테스트",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -11,7 +18,11 @@
"navigation": { "navigation": {
"targetUrl": "/hr/employee-management", "targetUrl": "/hr/employee-management",
"urlPattern": "/hr/employee-management|/ko/hr/employee-management", "urlPattern": "/hr/employee-management|/ko/hr/employee-management",
"menuHints": ["사원관리", "사원 관리", "인사관리"] "menuHints": [
"사원관리",
"사원 관리",
"인사관리"
]
}, },
"menuNavigation": { "menuNavigation": {
"level1": "인사관리", "level1": "인사관리",
@@ -36,16 +47,18 @@
"timeout": 10000 "timeout": 10000
}, },
"timeout": 60000, "timeout": 60000,
"tags": ["hr", "employee", "crud"], "tags": [
"hr",
"employee",
"crud"
],
"auth": { "auth": {
"username": "TestUser5", "username": "TestUser5",
"password": "password123!" "password": "password123!"
}, },
"steps": [ "steps": [
{ {
"id": "step-0", "id": 1,
"name": "사이드바 메뉴 전체 펼치기", "name": "사이드바 메뉴 전체 펼치기",
"description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비",
"actions": [ "actions": [
@@ -54,7 +67,10 @@
"script": "document.querySelector('.sidebar-scroll, [class*=\"sidebar\"], nav')?.scrollTo({top: 0, behavior: 'instant'})", "script": "document.querySelector('.sidebar-scroll, [class*=\"sidebar\"], nav')?.scrollTo({top: 0, behavior: 'instant'})",
"description": "사이드바 스크롤 최상단으로 이동" "description": "사이드바 스크롤 최상단으로 이동"
}, },
{ "type": "wait", "duration": 300 }, {
"type": "wait",
"duration": 300
},
{ {
"type": "evaluate", "type": "evaluate",
"script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()", "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()",
@@ -68,7 +84,7 @@
] ]
}, },
{ {
"id": "step-1", "id": 2,
"name": "인사관리 메뉴 진입", "name": "인사관리 메뉴 진입",
"description": "인사관리 > 직원관리 메뉴로 이동 (scrollAndFind 패턴)", "description": "인사관리 > 직원관리 메뉴로 이동 (scrollAndFind 패턴)",
"menuNavigation": { "menuNavigation": {
@@ -86,92 +102,176 @@
}, },
"expect": { "expect": {
"url": "/hr/employee-management", "url": "/hr/employee-management",
"visible": ["사원관리", "사원 등록"] "visible": [
"사원관리",
"사원 등록"
]
} }
}, },
{ {
"id": "step-2", "id": 3,
"name": "사원 등록 페이지 이동", "name": "사원 등록 페이지 이동",
"click": "사원 등록", "click": "사원 등록",
"expect": { "expect": {
"url": "/hr/employee-management?mode=new", "url": "/hr/employee-management?mode=new",
"visible": ["사원 등록", "사원 정보"] "visible": [
"사원 등록",
"사원 정보"
]
} }
}, },
{ {
"id": "step-3", "id": 4,
"name": "사원 정보 입력", "name": "사원 정보 입력",
"description": "기본 사원 정보 입력", "description": "기본 사원 정보 입력",
"form": { "form": {
"fields": [ "fields": [
{ "name": "이름 *", "type": "text", "value": "홍길동" }, {
{ "name": "주민등록번호", "type": "text", "value": "900101-1234567" }, "name": "이름 *",
{ "name": "휴대폰", "type": "text", "value": "010-1234-5678" }, "type": "text",
{ "name": "이메일 *", "type": "text", "value": "test.employee@codebridge-x.com" }, "value": "홍길동"
{ "name": "연봉", "type": "number", "value": "50000000" } },
{
"name": "주민등록번호",
"type": "text",
"value": "900101-1234567"
},
{
"name": "휴대폰",
"type": "text",
"value": "010-1234-5678"
},
{
"name": "이메일 *",
"type": "text",
"value": "test.employee@codebridge-x.com"
},
{
"name": "연봉",
"type": "number",
"value": "50000000"
}
] ]
} }
}, },
{ {
"id": "step-4", "id": 5,
"name": "급여계좌 정보 입력", "name": "급여계좌 정보 입력",
"form": { "form": {
"fields": [ "fields": [
{ "name": "은행명", "type": "text", "value": "신한은행" }, {
{ "name": "계좌번호", "type": "text", "value": "110-123-456789" }, "name": "은행명",
{ "name": "예금주", "type": "text", "value": "홍길동" } "type": "text",
"value": "신한은행"
},
{
"name": "계좌번호",
"type": "text",
"value": "110-123-456789"
},
{
"name": "예금주",
"type": "text",
"value": "홍길동"
}
] ]
} }
}, },
{ {
"id": "step-5", "id": 6,
"name": "사원 상세 정보 입력", "name": "사원 상세 정보 입력",
"form": { "form": {
"fields": [ "fields": [
{ "name": "사원코드", "type": "text", "value": "EMP2026001" }, {
{ "name": "남성", "type": "radio", "value": "true" }, "name": "사원코드",
{ "name": "상세주소를 입력해주세요", "type": "text", "value": "123번지 4층" } "type": "text",
"value": "EMP2026001"
},
{
"name": "남성",
"type": "radio",
"value": "true"
},
{
"name": "상세주소를 입력해주세요",
"type": "text",
"value": "123번지 4층"
}
] ]
} }
}, },
{ {
"id": "step-6", "id": 7,
"name": "인사 정보 입력", "name": "인사 정보 입력",
"form": { "form": {
"fields": [ "fields": [
{ "name": "입사일", "type": "date", "value": "2026-01-14" } {
"name": "입사일",
"type": "date",
"value": "2026-01-14"
}
] ]
}, },
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "고용형태 선택", "description": "고용형태 드롭다운 열기" }, {
{ "type": "click_if_exists", "target": "정규직", "description": "정규직 선택" }, "type": "click_if_exists",
{ "type": "click_if_exists", "target": "직급 선택", "description": "직급 드롭다운 열기" }, "target": "고용형태 선택",
{ "type": "click_if_exists", "target": "사원", "description": "사원 직급 선택" } "description": "고용형태 드롭다운 열기"
},
{
"type": "click_if_exists",
"target": "정규직",
"description": "정규직 선택"
},
{
"type": "click_if_exists",
"target": "직급 선택",
"description": "직급 드롭다운 열기"
},
{
"type": "click_if_exists",
"target": "사원",
"description": "사원 직급 선택"
}
] ]
}, },
{ {
"id": "step-7", "id": 8,
"name": "사용자 정보 입력", "name": "사용자 정보 입력",
"form": { "form": {
"fields": [ "fields": [
{ "name": "아이디 *", "type": "text", "value": "testuser2026" }, {
{ "name": "비밀번호 *", "type": "text", "value": "password123!" }, "name": "아이디 *",
{ "name": "비밀번호 확인 *", "type": "text", "value": "password123!" } "type": "text",
"value": "testuser2026"
},
{
"name": "비밀번호 *",
"type": "text",
"value": "password123!"
},
{
"name": "비밀번호 확인 *",
"type": "text",
"value": "password123!"
}
] ]
} }
}, },
{ {
"id": "step-8", "id": 9,
"name": "등록 완료", "name": "등록 완료",
"click": "등록", "click": "등록",
"waitFor": "사원관리", "waitFor": "사원관리",
"expect": { "expect": {
"url": "/hr/employee-management", "url": "/hr/employee-management",
"text": ["홍길동"] "text": [
"홍길동"
]
} }
}, },
{ {
"id": "step-8-1", "id": 10,
"name": "검색 기간 설정 - 유효 기간", "name": "검색 기간 설정 - 유효 기간",
"description": "등록된 사원의 입사일(2026-01-14)이 포함되는 기간으로 검색", "description": "등록된 사원의 입사일(2026-01-14)이 포함되는 기간으로 검색",
"actions": [ "actions": [
@@ -200,7 +300,7 @@
} }
}, },
{ {
"id": "step-8-2", "id": 11,
"name": "검색 기간 설정 - 범위 외 기간", "name": "검색 기간 설정 - 범위 외 기간",
"description": "등록된 사원의 입사일이 포함되지 않는 기간으로 검색하여 검색되지 않음을 확인", "description": "등록된 사원의 입사일이 포함되지 않는 기간으로 검색하여 검색되지 않음을 확인",
"actions": [ "actions": [
@@ -240,19 +340,26 @@
} }
}, },
{ {
"id": "step-8-3", "id": 12,
"name": "검색 기간 초기화 및 전체 조회", "name": "검색 기간 초기화 및 전체 조회",
"description": "검색 조건 초기화하여 등록된 사원이 다시 표시되는지 확인", "description": "검색 조건 초기화하여 등록된 사원이 다시 표시되는지 확인",
"actions": [ "actions": [
{ {
"type": "click_if_exists", "type": "click_if_exists",
"target": "초기화", "target": "초기화",
"fallbackSelectors": ["button:has-text('초기화')", ".reset-btn", "button:has-text('Reset')"] "fallbackSelectors": [
"button:has-text('초기화')",
".reset-btn",
"button:has-text('Reset')"
]
}, },
{ {
"type": "click_if_exists", "type": "click_if_exists",
"target": "검색", "target": "검색",
"fallbackSelectors": ["button:has-text('검색')", ".search-btn"] "fallbackSelectors": [
"button:has-text('검색')",
".search-btn"
]
} }
], ],
"expect": { "expect": {
@@ -266,7 +373,7 @@
} }
}, },
{ {
"id": "step-9", "id": 13,
"name": "등록된 직원 상세 페이지 이동", "name": "등록된 직원 상세 페이지 이동",
"description": "등록된 직원을 클릭하여 상세 페이지로 이동", "description": "등록된 직원을 클릭하여 상세 페이지로 이동",
"actions": [ "actions": [
@@ -278,42 +385,61 @@
], ],
"expect": { "expect": {
"url": "/hr/employee-management/{id}", "url": "/hr/employee-management/{id}",
"visible": ["사원 상세", "수정", "삭제", "목록"] "visible": [
"사원 상세",
"수정",
"삭제",
"목록"
]
} }
}, },
{ {
"id": "step-10", "id": 14,
"name": "직원 수정 모드 전환", "name": "직원 수정 모드 전환",
"description": "수정 버튼 클릭하여 편집 모드로 전환", "description": "수정 버튼 클릭하여 편집 모드로 전환",
"click": "수정", "click": "수정",
"expect": { "expect": {
"url": "/hr/employee-management/{id}?mode=edit", "url": "/hr/employee-management/{id}?mode=edit",
"visible": ["사원 수정", "취소", "저장"] "visible": [
} "사원 수정",
}, "취소",
{ "저장"
"id": "step-11",
"name": "직원 정보 수정",
"description": "휴대폰 번호 변경",
"form": {
"fields": [
{ "name": "휴대폰", "type": "text", "value": "010-9999-8888", "clear": true }
] ]
} }
}, },
{ {
"id": "step-12", "id": 15,
"name": "직원 정보 수정",
"description": "휴대폰 번호 변경",
"form": {
"fields": [
{
"name": "휴대폰",
"type": "text",
"value": "010-9999-8888",
"clear": true
}
]
}
},
{
"id": 16,
"name": "수정 저장", "name": "수정 저장",
"description": "수정된 직원 정보 저장", "description": "수정된 직원 정보 저장",
"click": "저장", "click": "저장",
"waitFor": "사원 상세", "waitFor": "사원 상세",
"expect": { "expect": {
"toast": ["수정", "완료", "성공", "저장"], "toast": [
"수정",
"완료",
"성공",
"저장"
],
"url": "/hr/employee-management/{id}" "url": "/hr/employee-management/{id}"
} }
}, },
{ {
"id": "step-12-1", "id": 17,
"name": "⚠️ 필수 검증: 수정 데이터 반영 확인", "name": "⚠️ 필수 검증: 수정 데이터 반영 확인",
"note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!",
"description": "상세 페이지에서 수정된 휴대폰 번호 확인", "description": "상세 페이지에서 수정된 휴대폰 번호 확인",
@@ -325,37 +451,49 @@
} }
}, },
{ {
"id": "step-13", "id": 18,
"name": "직원 삭제", "name": "직원 삭제",
"description": "삭제 버튼 클릭하여 직원 삭제", "description": "삭제 버튼 클릭하여 직원 삭제",
"click": "삭제", "click": "삭제",
"expect": { "expect": {
"confirmDialog": true, "confirmDialog": true,
"dialogText": ["삭제", "하시겠습니까"] "dialogText": [
"삭제",
"하시겠습니까"
]
} }
}, },
{ {
"id": "step-14", "id": 19,
"name": "삭제 확인", "name": "삭제 확인",
"description": "삭제 확인 다이얼로그에서 확인 클릭", "description": "삭제 확인 다이얼로그에서 확인 클릭",
"click": "확인", "click": "확인",
"waitFor": "사원관리", "waitFor": "사원관리",
"expect": { "expect": {
"toast": ["삭제", "완료", "성공"], "toast": [
"삭제",
"완료",
"성공"
],
"url": "/hr/employee-management" "url": "/hr/employee-management"
} }
}, },
{ {
"id": "step-15", "id": 20,
"name": "⚠️ 필수 검증: 삭제 데이터 반영 확인", "name": "⚠️ 필수 검증: 삭제 데이터 반영 확인",
"note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 삭제 확인 필수!", "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 삭제 확인 필수!",
"description": "목록에서 삭제된 직원이 없어졌는지 확인", "description": "목록에서 삭제된 직원이 없어졌는지 확인",
"verify": { "verify": {
"tableNotContains": "홍길동" "tableNotContains": "홍길동"
} }
},
{
"id": 21,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
} }
], ],
"assertions": [ "assertions": [
{ {
"type": "url", "type": "url",

View File

@@ -4,7 +4,14 @@
"name": "자유게시판 E2E 테스트", "name": "자유게시판 E2E 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "자유게시판 목록/검색/상세 기능 검증", "description": "자유게시판 목록/검색/상세 기능 검증",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -26,54 +33,136 @@
"action": "menu_navigate", "action": "menu_navigate",
"level1": "게시판", "level1": "게시판",
"level2": "자유게시판", "level2": "자유게시판",
"expected": { "url_contains": "/boards/free" } "expected": {
"url_contains": "/boards/free"
}
}, },
{ {
"id": 2, "id": 2,
"name": "URL 검증",
"action": "verify_url",
"expected": {
"url_contains": "/boards/free"
}
},
{
"id": 3,
"name": "목업 감지", "name": "목업 감지",
"action": "verify_not_mockup" "action": "verify_not_mockup"
}, },
{ {
"id": 3, "id": 4,
"name": "자유게시판 페이지 확인", "name": "통계 카드 확인",
"action": "verify_detail", "action": "evaluate",
"checks": ["visible_text:게시판"] "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()"
}, },
{ {
"id": 4, "id": 5,
"name": "자유게시판 페이지 확인",
"action": "verify_detail",
"checks": [
"visible_text:게시판"
]
},
{
"id": 6,
"name": "테이블 확인", "name": "테이블 확인",
"action": "verify_table" "action": "verify_table"
}, },
{ {
"id": 5, "id": 7,
"name": "목록 필터 테스트",
"action": "evaluate",
"script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()"
},
{
"id": 8,
"name": "검색 기능", "name": "검색 기능",
"action": "search", "action": "search",
"value": "테스트" "value": "테스트"
}, },
{ {
"id": 6, "id": 9,
"name": "검색 후 페이지 확인", "name": "검색 결과 대기",
"action": "verify_detail", "action": "wait",
"checks": ["visible_text:게시판"] "duration": 1000
}, },
{ {
"id": 7, "id": 10,
"name": "검색 결과 데이터 검증",
"action": "evaluate",
"script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Search result: ' + rows.length + ' rows';\n })()"
},
{
"id": 11,
"name": "검색 초기화",
"action": "evaluate",
"script": "(() => {\n const selectors = ['input[type=\"search\"]', 'input[placeholder*=\"검색\"]', 'input[placeholder*=\"Search\"]', 'input[role=\"searchbox\"]', '[class*=\"search\"] input'];\n for (const sel of selectors) {\n const el = document.querySelector(sel);\n if (el) {\n const nativeSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;\n if (nativeSetter) nativeSetter.call(el, '');\n else el.value = '';\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n return 'Search cleared';\n }\n }\n return 'No search input found (ok)';\n })()"
},
{
"id": 12,
"name": "검색 초기화 결과 대기",
"action": "wait",
"duration": 1000
},
{
"id": 13,
"name": "검색 초기화 및 복원 확인",
"action": "evaluate",
"script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Restored: ' + rows.length + ' rows';\n })()"
},
{
"id": 14,
"name": "검색 후 페이지 확인",
"action": "verify_detail",
"checks": [
"visible_text:게시판"
]
},
{
"id": 15,
"name": "첫 번째 행 클릭", "name": "첫 번째 행 클릭",
"action": "click_first_row" "action": "click_first_row"
}, },
{ {
"id": 8, "id": 16,
"name": "상세 페이지 확인", "name": "상세 페이지 로딩 대기",
"action": "verify_detail", "action": "wait",
"checks": ["visible_text:게시"] "duration": 1000
}, },
{ {
"id": 9, "id": 17,
"name": "상세 페이지 - 콘텐츠 확인",
"action": "evaluate",
"script": "(() => {\n const inputs = document.querySelectorAll('input:not([type=\"hidden\"]), textarea, select');\n const buttons = document.querySelectorAll('button');\n const hasDetail = inputs.length > 0 || document.body.innerText.includes('상세') || document.body.innerText.includes('수정');\n return hasDetail ? 'Detail page: ' + inputs.length + ' inputs, ' + buttons.length + ' buttons' : 'List page (no detail view)';\n })()"
},
{
"id": 18,
"name": "상세 페이지 확인",
"action": "verify_detail",
"checks": [
"visible_text:게시"
]
},
{
"id": 19,
"name": "모달 닫기", "name": "모달 닫기",
"action": "close_modal_if_open" "action": "close_modal_if_open"
}, },
{ {
"id": 10, "id": 20,
"name": "페이지네이션 확인",
"action": "evaluate",
"script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()"
},
{
"id": 21,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
},
{
"id": 22,
"name": "목록 복귀", "name": "목록 복귀",
"action": "click_if_exists", "action": "click_if_exists",
"target": "button:has-text('목록'), a:has-text('목록')" "target": "button:has-text('목록'), a:has-text('목록')"

View File

@@ -3,7 +3,14 @@
"name": "근태관리 테스트", "name": "근태관리 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "인사관리 > 근태관리 목록/상세 기능 검증", "description": "인사관리 > 근태관리 목록/상세 기능 검증",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -25,59 +32,95 @@
"action": "menu_navigate", "action": "menu_navigate",
"level1": "인사관리", "level1": "인사관리",
"level2": "근태관리", "level2": "근태관리",
"expected": { "url_contains": "/hr/attendance" } "expected": {
"url_contains": "/hr/attendance"
}
}, },
{ {
"id": 2, "id": 2,
"name": "URL 검증",
"action": "verify_url",
"expected": {
"url_contains": "/hr/attendance-management"
}
},
{
"id": 3,
"name": "목업 감지", "name": "목업 감지",
"action": "verify_not_mockup" "action": "verify_not_mockup"
}, },
{
"id": 3,
"name": "근태관리 페이지 확인",
"action": "verify_detail",
"checks": ["visible_text:근태"]
},
{ {
"id": 4, "id": 4,
"name": "UI 요소 확인", "name": "통계 카드 확인",
"action": "verify_detail", "action": "evaluate",
"checks": ["visible_text:관리"] "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()"
}, },
{ {
"id": 5, "id": 5,
"name": "근태관리 페이지 확인",
"action": "verify_detail",
"checks": [
"visible_text:근태"
]
},
{
"id": 6,
"name": "UI 요소 확인",
"action": "verify_detail",
"checks": [
"visible_text:관리"
]
},
{
"id": 7,
"name": "필터/검색 시도", "name": "필터/검색 시도",
"action": "click_if_exists", "action": "click_if_exists",
"target": "input[type='search'], input[type='date'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input, [class*='filter'] input" "target": "input[type='search'], input[type='date'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input, [class*='filter'] input"
}, },
{ {
"id": 6, "id": 8,
"name": "대기", "name": "대기",
"action": "wait", "action": "wait",
"duration": 1000 "duration": 1000
}, },
{ {
"id": 7, "id": 9,
"name": "행 클릭 시도", "name": "행 클릭 시도",
"action": "click_if_exists", "action": "click_if_exists",
"target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']" "target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']"
}, },
{ {
"id": 8, "id": 10,
"name": "상세 확인", "name": "상세 확인",
"action": "verify_detail", "action": "verify_detail",
"checks": ["visible_text:근태"] "checks": [
"visible_text:근태"
]
}, },
{ {
"id": 9, "id": 11,
"name": "모달 닫기", "name": "모달 닫기",
"action": "close_modal_if_open" "action": "close_modal_if_open"
}, },
{ {
"id": 10, "id": 12,
"name": "페이지네이션 확인",
"action": "evaluate",
"script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()"
},
{
"id": 13,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
},
{
"id": 14,
"name": "최종 확인", "name": "최종 확인",
"action": "verify_detail", "action": "verify_detail",
"checks": ["visible_text:근태"] "checks": [
"visible_text:근태"
]
} }
] ]
} }

View File

@@ -3,7 +3,14 @@
"name": "근태현황 테스트", "name": "근태현황 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "인사관리 > 근태현황 메뉴의 출퇴근 현황 조회/필터/출퇴근 기능 테스트", "description": "인사관리 > 근태현황 메뉴의 출퇴근 현황 조회/필터/출퇴근 기능 테스트",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -27,11 +34,23 @@
"level2": "근태현황", "level2": "근태현황",
"expected": { "expected": {
"url_contains": "/hr/attendance", "url_contains": "/hr/attendance",
"visible": ["근태현황", "출근", "퇴근"] "visible": [
"근태현황",
"출근",
"퇴근"
]
} }
}, },
{ {
"id": 2, "id": 2,
"name": "URL 검증",
"action": "verify_url",
"expected": {
"url_contains": "/hr/attendance"
}
},
{
"id": 3,
"name": "필수 검증 #5: 목업 페이지 감지", "name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup", "action": "verify_not_mockup",
"checks": [ "checks": [
@@ -42,7 +61,13 @@
"expected": "정상 페이지 (목업 아님)" "expected": "정상 페이지 (목업 아님)"
}, },
{ {
"id": 3, "id": 4,
"name": "통계 카드 확인",
"action": "evaluate",
"script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()"
},
{
"id": 5,
"name": "근태 현황 테이블 구조 확인", "name": "근태 현황 테이블 구조 확인",
"action": "verify_table", "action": "verify_table",
"checks": [ "checks": [
@@ -55,7 +80,13 @@
"expected": "근태 현황 테이블 표시" "expected": "근태 현황 테이블 표시"
}, },
{ {
"id": 4, "id": 6,
"name": "목록 필터 테스트",
"action": "evaluate",
"script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()"
},
{
"id": 7,
"name": "오늘 근태 상태 확인", "name": "오늘 근태 상태 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -65,7 +96,7 @@
"expected": "오늘 근태 상태 표시" "expected": "오늘 근태 상태 표시"
}, },
{ {
"id": 5, "id": 8,
"name": "출근 버튼 확인", "name": "출근 버튼 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -74,7 +105,7 @@
"expected": "출근 버튼 표시" "expected": "출근 버튼 표시"
}, },
{ {
"id": 6, "id": 9,
"name": "퇴근 버튼 확인", "name": "퇴근 버튼 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -83,7 +114,7 @@
"expected": "퇴근 버튼 표시" "expected": "퇴근 버튼 표시"
}, },
{ {
"id": 7, "id": 10,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 기간 필터 - 월 선택", "name": "[FILTER] 기간 필터 - 월 선택",
"action": "click_if_exists", "action": "click_if_exists",
@@ -91,7 +122,7 @@
"expected": "월 선택 열림" "expected": "월 선택 열림"
}, },
{ {
"id": 8, "id": 11,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 조회 적용", "name": "[FILTER] 조회 적용",
"action": "click_if_exists", "action": "click_if_exists",
@@ -99,7 +130,7 @@
"expected": "필터 적용됨" "expected": "필터 적용됨"
}, },
{ {
"id": 9, "id": 12,
"phase": "FILTER", "phase": "FILTER",
"name": "[FILTER] 필터 결과 확인", "name": "[FILTER] 필터 결과 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -109,7 +140,7 @@
"expected": "필터 동작 확인" "expected": "필터 동작 확인"
}, },
{ {
"id": 10, "id": 13,
"name": "근무 시간 통계 확인", "name": "근무 시간 통계 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -119,7 +150,7 @@
"expected": "근무 통계 표시" "expected": "근무 통계 표시"
}, },
{ {
"id": 11, "id": 14,
"name": "지각/조퇴/결근 통계 확인", "name": "지각/조퇴/결근 통계 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -130,7 +161,7 @@
"expected": "근태 이상 통계 표시" "expected": "근태 이상 통계 표시"
}, },
{ {
"id": 12, "id": 15,
"phase": "READ", "phase": "READ",
"name": "[READ] 특정 일자 상세 보기", "name": "[READ] 특정 일자 상세 보기",
"action": "click_if_exists", "action": "click_if_exists",
@@ -140,7 +171,7 @@
} }
}, },
{ {
"id": 13, "id": 16,
"name": "상세 근태 정보 확인", "name": "상세 근태 정보 확인",
"action": "verify_detail", "action": "verify_detail",
"checks": [ "checks": [
@@ -151,7 +182,7 @@
"expected": "상세 근태 정보 표시" "expected": "상세 근태 정보 표시"
}, },
{ {
"id": 14, "id": 17,
"name": "엑셀 다운로드 버튼 확인", "name": "엑셀 다운로드 버튼 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -160,13 +191,19 @@
"expected": "엑셀 다운로드 기능 표시" "expected": "엑셀 다운로드 기능 표시"
}, },
{ {
"id": 15, "id": 18,
"name": "인쇄 버튼 확인", "name": "인쇄 버튼 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
"인쇄 버튼 존재" "인쇄 버튼 존재"
], ],
"expected": "인쇄 기능 표시" "expected": "인쇄 기능 표시"
},
{
"id": 19,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
} }
], ],
"expectedAPIs": [ "expectedAPIs": [
@@ -195,13 +232,19 @@
{ {
"id": 3, "id": 3,
"name": "검색/필터", "name": "검색/필터",
"steps": [7, 8, 9], "steps": [
7,
8,
9
],
"criteria": "기간 필터 동작" "criteria": "기간 필터 동작"
}, },
{ {
"id": 5, "id": 5,
"name": "목업 페이지 감지", "name": "목업 페이지 감지",
"steps": [2], "steps": [
2
],
"criteria": "근태 현황, 날짜 선택, 출퇴근 버튼 존재" "criteria": "근태 현황, 날짜 선택, 출퇴근 버튼 존재"
} }
], ],

View File

@@ -3,7 +3,14 @@
"name": "카드관리 테스트", "name": "카드관리 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "인사관리 > 카드관리 목록/검색/상세 기능 검증", "description": "인사관리 > 카드관리 목록/검색/상세 기능 검증",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -25,54 +32,136 @@
"action": "menu_navigate", "action": "menu_navigate",
"level1": "인사관리", "level1": "인사관리",
"level2": "카드관리", "level2": "카드관리",
"expected": { "url_contains": "/hr/card" } "expected": {
"url_contains": "/hr/card"
}
}, },
{ {
"id": 2, "id": 2,
"name": "URL 검증",
"action": "verify_url",
"expected": {
"url_contains": "/hr/card-management"
}
},
{
"id": 3,
"name": "목업 감지", "name": "목업 감지",
"action": "verify_not_mockup" "action": "verify_not_mockup"
}, },
{ {
"id": 3, "id": 4,
"name": "카드관리 페이지 확인", "name": "통계 카드 확인",
"action": "verify_detail", "action": "evaluate",
"checks": ["visible_text:카드"] "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()"
}, },
{ {
"id": 4, "id": 5,
"name": "카드관리 페이지 확인",
"action": "verify_detail",
"checks": [
"visible_text:카드"
]
},
{
"id": 6,
"name": "테이블 확인", "name": "테이블 확인",
"action": "verify_table" "action": "verify_table"
}, },
{ {
"id": 5, "id": 7,
"name": "목록 필터 테스트",
"action": "evaluate",
"script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()"
},
{
"id": 8,
"name": "검색 기능", "name": "검색 기능",
"action": "search", "action": "search",
"value": "테스트" "value": "테스트"
}, },
{ {
"id": 6, "id": 9,
"name": "검색 후 확인", "name": "검색 결과 대기",
"action": "verify_detail", "action": "wait",
"checks": ["visible_text:카드"] "duration": 1000
}, },
{ {
"id": 7, "id": 10,
"name": "검색 결과 데이터 검증",
"action": "evaluate",
"script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Search result: ' + rows.length + ' rows';\n })()"
},
{
"id": 11,
"name": "검색 초기화",
"action": "evaluate",
"script": "(() => {\n const selectors = ['input[type=\"search\"]', 'input[placeholder*=\"검색\"]', 'input[placeholder*=\"Search\"]', 'input[role=\"searchbox\"]', '[class*=\"search\"] input'];\n for (const sel of selectors) {\n const el = document.querySelector(sel);\n if (el) {\n const nativeSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;\n if (nativeSetter) nativeSetter.call(el, '');\n else el.value = '';\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n return 'Search cleared';\n }\n }\n return 'No search input found (ok)';\n })()"
},
{
"id": 12,
"name": "검색 초기화 결과 대기",
"action": "wait",
"duration": 1000
},
{
"id": 13,
"name": "검색 초기화 및 복원 확인",
"action": "evaluate",
"script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Restored: ' + rows.length + ' rows';\n })()"
},
{
"id": 14,
"name": "검색 후 확인",
"action": "verify_detail",
"checks": [
"visible_text:카드"
]
},
{
"id": 15,
"name": "첫 번째 행 클릭", "name": "첫 번째 행 클릭",
"action": "click_first_row" "action": "click_first_row"
}, },
{ {
"id": 8, "id": 16,
"name": "상세 확인", "name": "상세 페이지 로딩 대기",
"action": "verify_detail", "action": "wait",
"checks": ["visible_text:카드"] "duration": 1000
}, },
{ {
"id": 9, "id": 17,
"name": "상세 페이지 - 콘텐츠 확인",
"action": "evaluate",
"script": "(() => {\n const inputs = document.querySelectorAll('input:not([type=\"hidden\"]), textarea, select');\n const buttons = document.querySelectorAll('button');\n const hasDetail = inputs.length > 0 || document.body.innerText.includes('상세') || document.body.innerText.includes('수정');\n return hasDetail ? 'Detail page: ' + inputs.length + ' inputs, ' + buttons.length + ' buttons' : 'List page (no detail view)';\n })()"
},
{
"id": 18,
"name": "상세 확인",
"action": "verify_detail",
"checks": [
"visible_text:카드"
]
},
{
"id": 19,
"name": "모달 닫기", "name": "모달 닫기",
"action": "close_modal_if_open" "action": "close_modal_if_open"
}, },
{ {
"id": 10, "id": 20,
"name": "페이지네이션 확인",
"action": "evaluate",
"script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()"
},
{
"id": 21,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
},
{
"id": 22,
"name": "목록 복귀", "name": "목록 복귀",
"action": "click_if_exists", "action": "click_if_exists",
"target": "button:has-text('목록'), a:has-text('목록')" "target": "button:has-text('목록'), a:has-text('목록')"

View File

@@ -3,7 +3,14 @@
"name": "부서관리 테스트", "name": "부서관리 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "인사관리 > 부서관리 목록/상세 기능 검증", "description": "인사관리 > 부서관리 목록/상세 기능 검증",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -25,59 +32,95 @@
"action": "menu_navigate", "action": "menu_navigate",
"level1": "인사관리", "level1": "인사관리",
"level2": "부서관리", "level2": "부서관리",
"expected": { "url_contains": "/hr/department" } "expected": {
"url_contains": "/hr/department"
}
}, },
{ {
"id": 2, "id": 2,
"name": "URL 검증",
"action": "verify_url",
"expected": {
"url_contains": "/hr/department-management"
}
},
{
"id": 3,
"name": "목업 감지", "name": "목업 감지",
"action": "verify_not_mockup" "action": "verify_not_mockup"
}, },
{
"id": 3,
"name": "부서관리 페이지 확인",
"action": "verify_detail",
"checks": ["visible_text:부서"]
},
{ {
"id": 4, "id": 4,
"name": "UI 요소 확인", "name": "통계 카드 확인",
"action": "verify_detail", "action": "evaluate",
"checks": ["visible_text:관리"] "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()"
}, },
{ {
"id": 5, "id": 5,
"name": "부서관리 페이지 확인",
"action": "verify_detail",
"checks": [
"visible_text:부서"
]
},
{
"id": 6,
"name": "UI 요소 확인",
"action": "verify_detail",
"checks": [
"visible_text:관리"
]
},
{
"id": 7,
"name": "검색 입력 시도", "name": "검색 입력 시도",
"action": "click_if_exists", "action": "click_if_exists",
"target": "input[type='search'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input" "target": "input[type='search'], input[placeholder*='검색'], input[placeholder*='조회'], [class*='search'] input"
}, },
{ {
"id": 6, "id": 8,
"name": "대기", "name": "대기",
"action": "wait", "action": "wait",
"duration": 1000 "duration": 1000
}, },
{ {
"id": 7, "id": 9,
"name": "행 클릭 시도", "name": "행 클릭 시도",
"action": "click_if_exists", "action": "click_if_exists",
"target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']" "target": "table tbody tr, [role='row']:not(:first-child), [class*='list'] [class*='item']"
}, },
{ {
"id": 8, "id": 10,
"name": "상세 확인", "name": "상세 확인",
"action": "verify_detail", "action": "verify_detail",
"checks": ["visible_text:부서"] "checks": [
"visible_text:부서"
]
}, },
{ {
"id": 9, "id": 11,
"name": "모달 닫기", "name": "모달 닫기",
"action": "close_modal_if_open" "action": "close_modal_if_open"
}, },
{ {
"id": 10, "id": 12,
"name": "페이지네이션 확인",
"action": "evaluate",
"script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()"
},
{
"id": 13,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
},
{
"id": 14,
"name": "최종 확인", "name": "최종 확인",
"action": "verify_detail", "action": "verify_detail",
"checks": ["visible_text:부서"] "checks": [
"visible_text:부서"
]
} }
] ]
} }

View File

@@ -3,7 +3,14 @@
"name": "사원관리 테스트", "name": "사원관리 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "인사관리 > 사원관리 목록/검색/상세 기능 검증", "description": "인사관리 > 사원관리 목록/검색/상세 기능 검증",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -25,54 +32,136 @@
"action": "menu_navigate", "action": "menu_navigate",
"level1": "인사관리", "level1": "인사관리",
"level2": "사원관리", "level2": "사원관리",
"expected": { "url_contains": "/hr/employee" } "expected": {
"url_contains": "/hr/employee"
}
}, },
{ {
"id": 2, "id": 2,
"name": "URL 검증",
"action": "verify_url",
"expected": {
"url_contains": "/hr/employee-management"
}
},
{
"id": 3,
"name": "목업 감지", "name": "목업 감지",
"action": "verify_not_mockup" "action": "verify_not_mockup"
}, },
{ {
"id": 3, "id": 4,
"name": "사원관리 페이지 확인", "name": "통계 카드 확인",
"action": "verify_detail", "action": "evaluate",
"checks": ["visible_text:사원"] "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()"
}, },
{ {
"id": 4, "id": 5,
"name": "사원관리 페이지 확인",
"action": "verify_detail",
"checks": [
"visible_text:사원"
]
},
{
"id": 6,
"name": "테이블 확인", "name": "테이블 확인",
"action": "verify_table" "action": "verify_table"
}, },
{ {
"id": 5, "id": 7,
"name": "목록 필터 테스트",
"action": "evaluate",
"script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()"
},
{
"id": 8,
"name": "검색 기능", "name": "검색 기능",
"action": "search", "action": "search",
"value": "테스트" "value": "테스트"
}, },
{ {
"id": 6, "id": 9,
"name": "검색 후 확인", "name": "검색 결과 대기",
"action": "verify_detail", "action": "wait",
"checks": ["visible_text:사원"] "duration": 1000
}, },
{ {
"id": 7, "id": 10,
"name": "검색 결과 데이터 검증",
"action": "evaluate",
"script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Search result: ' + rows.length + ' rows';\n })()"
},
{
"id": 11,
"name": "검색 초기화",
"action": "evaluate",
"script": "(() => {\n const selectors = ['input[type=\"search\"]', 'input[placeholder*=\"검색\"]', 'input[placeholder*=\"Search\"]', 'input[role=\"searchbox\"]', '[class*=\"search\"] input'];\n for (const sel of selectors) {\n const el = document.querySelector(sel);\n if (el) {\n const nativeSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;\n if (nativeSetter) nativeSetter.call(el, '');\n else el.value = '';\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n return 'Search cleared';\n }\n }\n return 'No search input found (ok)';\n })()"
},
{
"id": 12,
"name": "검색 초기화 결과 대기",
"action": "wait",
"duration": 1000
},
{
"id": 13,
"name": "검색 초기화 및 복원 확인",
"action": "evaluate",
"script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Restored: ' + rows.length + ' rows';\n })()"
},
{
"id": 14,
"name": "검색 후 확인",
"action": "verify_detail",
"checks": [
"visible_text:사원"
]
},
{
"id": 15,
"name": "첫 번째 행 클릭", "name": "첫 번째 행 클릭",
"action": "click_first_row" "action": "click_first_row"
}, },
{ {
"id": 8, "id": 16,
"name": "상세 확인", "name": "상세 페이지 로딩 대기",
"action": "verify_detail", "action": "wait",
"checks": ["visible_text:사원"] "duration": 1000
}, },
{ {
"id": 9, "id": 17,
"name": "상세 페이지 - 콘텐츠 확인",
"action": "evaluate",
"script": "(() => {\n const inputs = document.querySelectorAll('input:not([type=\"hidden\"]), textarea, select');\n const buttons = document.querySelectorAll('button');\n const hasDetail = inputs.length > 0 || document.body.innerText.includes('상세') || document.body.innerText.includes('수정');\n return hasDetail ? 'Detail page: ' + inputs.length + ' inputs, ' + buttons.length + ' buttons' : 'List page (no detail view)';\n })()"
},
{
"id": 18,
"name": "상세 확인",
"action": "verify_detail",
"checks": [
"visible_text:사원"
]
},
{
"id": 19,
"name": "모달 닫기", "name": "모달 닫기",
"action": "close_modal_if_open" "action": "close_modal_if_open"
}, },
{ {
"id": 10, "id": 20,
"name": "페이지네이션 확인",
"action": "evaluate",
"script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()"
},
{
"id": 21,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
},
{
"id": 22,
"name": "목록 복귀", "name": "목록 복귀",
"action": "click_if_exists", "action": "click_if_exists",
"target": "button:has-text('목록'), a:has-text('목록')" "target": "button:has-text('목록'), a:has-text('목록')"

View File

@@ -3,7 +3,14 @@
"name": "급여관리 테스트", "name": "급여관리 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "인사관리 > 급여관리 목록/검색/상세 기능 검증", "description": "인사관리 > 급여관리 목록/검색/상세 기능 검증",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -25,54 +32,136 @@
"action": "menu_navigate", "action": "menu_navigate",
"level1": "인사관리", "level1": "인사관리",
"level2": "급여관리", "level2": "급여관리",
"expected": { "url_contains": "/hr/salary" } "expected": {
"url_contains": "/hr/salary"
}
}, },
{ {
"id": 2, "id": 2,
"name": "URL 검증",
"action": "verify_url",
"expected": {
"url_contains": "/hr/salary-management"
}
},
{
"id": 3,
"name": "목업 감지", "name": "목업 감지",
"action": "verify_not_mockup" "action": "verify_not_mockup"
}, },
{ {
"id": 3, "id": 4,
"name": "급여관리 페이지 확인", "name": "통계 카드 확인",
"action": "verify_detail", "action": "evaluate",
"checks": ["visible_text:급여"] "script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()"
}, },
{ {
"id": 4, "id": 5,
"name": "급여관리 페이지 확인",
"action": "verify_detail",
"checks": [
"visible_text:급여"
]
},
{
"id": 6,
"name": "테이블 확인", "name": "테이블 확인",
"action": "verify_table" "action": "verify_table"
}, },
{ {
"id": 5, "id": 7,
"name": "목록 필터 테스트",
"action": "evaluate",
"script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()"
},
{
"id": 8,
"name": "검색 기능", "name": "검색 기능",
"action": "search", "action": "search",
"value": "테스트" "value": "테스트"
}, },
{ {
"id": 6, "id": 9,
"name": "검색 후 확인", "name": "검색 결과 대기",
"action": "verify_detail", "action": "wait",
"checks": ["visible_text:급여"] "duration": 1000
}, },
{ {
"id": 7, "id": 10,
"name": "검색 결과 데이터 검증",
"action": "evaluate",
"script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Search result: ' + rows.length + ' rows';\n })()"
},
{
"id": 11,
"name": "검색 초기화",
"action": "evaluate",
"script": "(() => {\n const selectors = ['input[type=\"search\"]', 'input[placeholder*=\"검색\"]', 'input[placeholder*=\"Search\"]', 'input[role=\"searchbox\"]', '[class*=\"search\"] input'];\n for (const sel of selectors) {\n const el = document.querySelector(sel);\n if (el) {\n const nativeSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;\n if (nativeSetter) nativeSetter.call(el, '');\n else el.value = '';\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n return 'Search cleared';\n }\n }\n return 'No search input found (ok)';\n })()"
},
{
"id": 12,
"name": "검색 초기화 결과 대기",
"action": "wait",
"duration": 1000
},
{
"id": 13,
"name": "검색 초기화 및 복원 확인",
"action": "evaluate",
"script": "(() => {\n const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]');\n return 'Restored: ' + rows.length + ' rows';\n })()"
},
{
"id": 14,
"name": "검색 후 확인",
"action": "verify_detail",
"checks": [
"visible_text:급여"
]
},
{
"id": 15,
"name": "첫 번째 행 클릭", "name": "첫 번째 행 클릭",
"action": "click_first_row" "action": "click_first_row"
}, },
{ {
"id": 8, "id": 16,
"name": "상세 확인", "name": "상세 페이지 로딩 대기",
"action": "verify_detail", "action": "wait",
"checks": ["visible_text:급여"] "duration": 1000
}, },
{ {
"id": 9, "id": 17,
"name": "상세 페이지 - 콘텐츠 확인",
"action": "evaluate",
"script": "(() => {\n const inputs = document.querySelectorAll('input:not([type=\"hidden\"]), textarea, select');\n const buttons = document.querySelectorAll('button');\n const hasDetail = inputs.length > 0 || document.body.innerText.includes('상세') || document.body.innerText.includes('수정');\n return hasDetail ? 'Detail page: ' + inputs.length + ' inputs, ' + buttons.length + ' buttons' : 'List page (no detail view)';\n })()"
},
{
"id": 18,
"name": "상세 확인",
"action": "verify_detail",
"checks": [
"visible_text:급여"
]
},
{
"id": 19,
"name": "모달 닫기", "name": "모달 닫기",
"action": "close_modal_if_open" "action": "close_modal_if_open"
}, },
{ {
"id": 10, "id": 20,
"name": "페이지네이션 확인",
"action": "evaluate",
"script": "(() => {\n const paginationSels = ['[class*=\"pagination\"]', '[class*=\"Pagination\"]', 'nav[aria-label*=\"page\"]', 'button[aria-label*=\"page\"]', '[class*=\"pager\"]'];\n for (const sel of paginationSels) {\n const el = document.querySelector(sel);\n if (el) return 'Pagination found';\n }\n const pageButtons = Array.from(document.querySelectorAll('button')).filter(b => /^\\d+$/.test(b.innerText?.trim()));\n if (pageButtons.length > 0) return 'Page buttons found: ' + pageButtons.length;\n return 'No pagination (ok - may have single page)';\n })()"
},
{
"id": 21,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
},
{
"id": 22,
"name": "목록 복귀", "name": "목록 복귀",
"action": "click_if_exists", "action": "click_if_exists",
"target": "button:has-text('목록'), a:has-text('목록')" "target": "button:has-text('목록'), a:has-text('목록')"

View File

@@ -53,6 +53,14 @@
}, },
{ {
"id": 2, "id": 2,
"name": "URL 검증",
"action": "verify_url",
"expected": {
"url_contains": "/hr/vacation-management"
}
},
{
"id": 3,
"name": "필수 검증 #5: 목업 페이지 감지", "name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup", "action": "verify_not_mockup",
"checks": [ "checks": [
@@ -63,7 +71,13 @@
"expected": "정상 페이지 (목업 아님)" "expected": "정상 페이지 (목업 아님)"
}, },
{ {
"id": 3, "id": 4,
"name": "통계 카드 확인",
"action": "evaluate",
"script": "(() => {\n const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"Card\"], [class*=\"stat\"], [class*=\"Stat\"], [class*=\"summary\"]');\n const texts = Array.from(cards).map(c => c.innerText?.substring(0, 30)).filter(Boolean);\n return texts.length > 0 ? 'Stats: ' + texts.length + ' cards found' : 'No stat cards (ok)';\n })()"
},
{
"id": 5,
"name": "휴가 현황 카드 확인", "name": "휴가 현황 카드 확인",
"action": "verify_elements", "action": "verify_elements",
"checks": [ "checks": [
@@ -74,7 +88,7 @@
"expected": "휴가 현황 카드 정상 표시" "expected": "휴가 현황 카드 정상 표시"
}, },
{ {
"id": 4, "id": 6,
"name": "휴가 테이블 구조 확인", "name": "휴가 테이블 구조 확인",
"action": "verify_table", "action": "verify_table",
"checks": [ "checks": [
@@ -87,7 +101,13 @@
"expected": "휴가 테이블 컬럼 정상 표시" "expected": "휴가 테이블 컬럼 정상 표시"
}, },
{ {
"id": 5, "id": 7,
"name": "목록 필터 테스트",
"action": "evaluate",
"script": "(() => {\n const selects = document.querySelectorAll('select, [role=\"combobox\"], button[class*=\"select\"], button[class*=\"Select\"]');\n if (selects.length > 0) {\n return 'Filters found: ' + selects.length;\n }\n return 'No filter dropdowns (ok)';\n })()"
},
{
"id": 8,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 휴가 신청 버튼 클릭", "name": "[CREATE] 휴가 신청 버튼 클릭",
"action": "click_if_exists", "action": "click_if_exists",
@@ -98,7 +118,7 @@
} }
}, },
{ {
"id": 6, "id": 9,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 휴가 정보 입력", "name": "[CREATE] 휴가 정보 입력",
"action": "click_if_exists", "action": "click_if_exists",
@@ -128,7 +148,7 @@
"target": "form, [role='dialog'], .modal" "target": "form, [role='dialog'], .modal"
}, },
{ {
"id": 7, "id": 10,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 필수 검증 #2: 신청 저장", "name": "[CREATE] 필수 검증 #2: 신청 저장",
"action": "click_if_exists", "action": "click_if_exists",
@@ -142,14 +162,23 @@
"expected": "휴가 신청 완료" "expected": "휴가 신청 완료"
}, },
{ {
"id": "7-modal-close", "id": 11,
"phase": "CREATE",
"name": "[CREATE] 저장 완료 토스트 확인",
"action": "verify_toast",
"verify": {
"contains": "등록|완료|성공|저장"
}
},
{
"id": 12,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 모달 닫기 확인", "name": "[CREATE] 모달 닫기 확인",
"action": "close_modal_if_open", "action": "close_modal_if_open",
"expected": "모달 닫힘" "expected": "모달 닫힘"
}, },
{ {
"id": 8, "id": 13,
"phase": "CREATE", "phase": "CREATE",
"name": "[CREATE] 신청 결과 확인", "name": "[CREATE] 신청 결과 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -164,7 +193,7 @@
} }
}, },
{ {
"id": 9, "id": 14,
"phase": "READ", "phase": "READ",
"name": "[READ] 휴가 상세 페이지 진입", "name": "[READ] 휴가 상세 페이지 진입",
"action": "click_if_exists", "action": "click_if_exists",
@@ -179,7 +208,7 @@
} }
}, },
{ {
"id": 10, "id": 15,
"phase": "READ", "phase": "READ",
"name": "[READ] 상세 정보 확인", "name": "[READ] 상세 정보 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -193,7 +222,7 @@
"expected": "입력한 데이터와 일치" "expected": "입력한 데이터와 일치"
}, },
{ {
"id": 11, "id": 16,
"phase": "UPDATE", "phase": "UPDATE",
"name": "[UPDATE] 수정 모드 진입", "name": "[UPDATE] 수정 모드 진입",
"action": "click_if_exists", "action": "click_if_exists",
@@ -204,7 +233,7 @@
} }
}, },
{ {
"id": 12, "id": 17,
"phase": "UPDATE", "phase": "UPDATE",
"name": "[UPDATE] 사유 수정", "name": "[UPDATE] 사유 수정",
"action": "click_if_exists", "action": "click_if_exists",
@@ -213,7 +242,7 @@
"clear": true "clear": true
}, },
{ {
"id": 13, "id": 18,
"phase": "UPDATE", "phase": "UPDATE",
"name": "[UPDATE] 필수 검증 #2: 수정 저장", "name": "[UPDATE] 필수 검증 #2: 수정 저장",
"action": "click_if_exists", "action": "click_if_exists",
@@ -227,7 +256,16 @@
"expected": "수정 완료" "expected": "수정 완료"
}, },
{ {
"id": 14, "id": 19,
"phase": "UPDATE",
"name": "[UPDATE] 수정 완료 토스트 확인",
"action": "verify_toast",
"verify": {
"contains": "수정|완료|성공|저장"
}
},
{
"id": 20,
"phase": "UPDATE", "phase": "UPDATE",
"name": "[UPDATE] 수정 결과 확인", "name": "[UPDATE] 수정 결과 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -237,7 +275,7 @@
"expected": "수정된 데이터 반영" "expected": "수정된 데이터 반영"
}, },
{ {
"id": 15, "id": 21,
"phase": "DELETE", "phase": "DELETE",
"name": "[DELETE] 취소 버튼 클릭", "name": "[DELETE] 취소 버튼 클릭",
"action": "click_if_exists", "action": "click_if_exists",
@@ -248,7 +286,7 @@
} }
}, },
{ {
"id": 16, "id": 22,
"phase": "DELETE", "phase": "DELETE",
"name": "[DELETE] 필수 검증 #6: 취소 확인", "name": "[DELETE] 필수 검증 #6: 취소 확인",
"action": "click_if_exists", "action": "click_if_exists",
@@ -261,7 +299,7 @@
"expected": "휴가 취소 완료 및 목록 복귀" "expected": "휴가 취소 완료 및 목록 복귀"
}, },
{ {
"id": 17, "id": 23,
"phase": "DELETE", "phase": "DELETE",
"name": "[DELETE] 취소 결과 확인", "name": "[DELETE] 취소 결과 확인",
"action": "verify_detail", "action": "verify_detail",
@@ -272,7 +310,7 @@
} }
}, },
{ {
"id": 18, "id": 24,
"phase": "VERIFY", "phase": "VERIFY",
"name": "[VERIFY] 연차 잔여일 확인", "name": "[VERIFY] 연차 잔여일 확인",
"action": "verify_elements", "action": "verify_elements",
@@ -280,6 +318,12 @@
"잔여 연차가 원래 값으로 복원" "잔여 연차가 원래 값으로 복원"
], ],
"expected": "휴가 취소 후 연차 복원 확인" "expected": "휴가 취소 후 연차 복원 확인"
},
{
"id": 25,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
} }
], ],
"expectedAPIs": [ "expectedAPIs": [

View File

@@ -3,7 +3,14 @@
"name": "재고현황 테스트", "name": "재고현황 테스트",
"screenshotPolicy": { "screenshotPolicy": {
"onErrorOnly": true, "onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] "captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
}, },
"description": "자재관리 > 재고현황 페이지의 재고 조회 및 엑셀 다운로드 기능을 테스트하는 E2E 테스트", "description": "자재관리 > 재고현황 페이지의 재고 조회 및 엑셀 다운로드 기능을 테스트하는 E2E 테스트",
"baseUrl": "https://dev.codebridge-x.com", "baseUrl": "https://dev.codebridge-x.com",
@@ -11,7 +18,11 @@
"navigation": { "navigation": {
"targetUrl": "/material/stock-status", "targetUrl": "/material/stock-status",
"urlPattern": "/material/stock-status|/ko/material/stock-status", "urlPattern": "/material/stock-status|/ko/material/stock-status",
"menuHints": ["재고현황", "재고 현황", "자재관리"] "menuHints": [
"재고현황",
"재고 현황",
"자재관리"
]
}, },
"menuNavigation": { "menuNavigation": {
"level1": "자재관리", "level1": "자재관리",
@@ -41,16 +52,18 @@
"expectedUrl": "/material/stock-status" "expectedUrl": "/material/stock-status"
}, },
"timeout": 90000, "timeout": 90000,
"tags": ["material", "inventory", "read-only"], "tags": [
"material",
"inventory",
"read-only"
],
"auth": { "auth": {
"username": "TestUser5", "username": "TestUser5",
"password": "password123!" "password": "password123!"
}, },
"steps": [ "steps": [
{ {
"id": "step-0", "id": 1,
"name": "사이드바 메뉴 전체 펼치기", "name": "사이드바 메뉴 전체 펼치기",
"description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비",
"actions": [ "actions": [
@@ -58,16 +71,22 @@
"type": "evaluate", "type": "evaluate",
"script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})" "script": "document.querySelector('.sidebar-scroll')?.scrollTo({top:0,behavior:'instant'})"
}, },
{ "type": "wait", "duration": 300 }, {
"type": "wait",
"duration": 300
},
{ {
"type": "evaluate", "type": "evaluate",
"script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()"
}, },
{ "type": "wait", "duration": 2000 } {
"type": "wait",
"duration": 2000
}
] ]
}, },
{ {
"id": "step-1", "id": 2,
"name": "자재관리 메뉴 진입", "name": "자재관리 메뉴 진입",
"description": "자재관리 > 재고현황 메뉴로 이동", "description": "자재관리 > 재고현황 메뉴로 이동",
"actions": [ "actions": [
@@ -78,13 +97,25 @@
"scrollStep": 200, "scrollStep": 200,
"maxAttempts": 5 "maxAttempts": 5
}, },
{ "type": "click_if_exists", "target": "자재관리" }, {
{ "type": "wait", "duration": 500 }, "type": "click_if_exists",
{ "type": "click_if_exists", "target": "재고현황" } "target": "자재관리"
},
{
"type": "wait",
"duration": 500
},
{
"type": "click_if_exists",
"target": "재고현황"
}
], ],
"expect": { "expect": {
"url": "/material/stock-status", "url": "/material/stock-status",
"visible": ["재고 목록", "엑셀 다운로드"] "visible": [
"재고 목록",
"엑셀 다운로드"
]
}, },
"fallback": { "fallback": {
"type": "navigate", "type": "navigate",
@@ -92,21 +123,44 @@
} }
}, },
{ {
"id": "step-2", "id": 3,
"name": "페이지 구조 확인", "name": "페이지 구조 확인",
"description": "통계 카드와 테이블 구조 확인", "description": "통계 카드와 테이블 구조 확인",
"verify": { "verify": {
"visible": ["전체 품목", "정상 재고", "재고 부족", "재고 없음"], "visible": [
"tableColumns": ["번호", "품목코드", "품목명", "품목유형", "단위", "재고량", "안전재고", "LOT", "상태", "위치"] "전체 품목",
"정상 재고",
"재고 부족",
"재고 없음"
],
"tableColumns": [
"번호",
"품목코드",
"품목명",
"품목유형",
"단위",
"재고량",
"안전재고",
"LOT",
"상태",
"위치"
]
} }
}, },
{ {
"id": "step-3", "id": 4,
"name": "필수 검증 #3: 품목유형 탭 필터 - 원자재", "name": "필수 검증 #3: 품목유형 탭 필터 - 원자재",
"description": "원자재 탭 클릭하여 필터링 확인", "description": "원자재 탭 클릭하여 필터링 확인",
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "원자재", "role": "tab" }, {
{ "type": "wait", "duration": 500 } "type": "click_if_exists",
"target": "원자재",
"role": "tab"
},
{
"type": "wait",
"duration": 500
}
], ],
"expect": { "expect": {
"tabActive": "원자재", "tabActive": "원자재",
@@ -114,12 +168,19 @@
} }
}, },
{ {
"id": "step-4", "id": 5,
"name": "필수 검증 #3: 품목유형 탭 필터 - 부자재", "name": "필수 검증 #3: 품목유형 탭 필터 - 부자재",
"description": "부자재 탭 클릭하여 필터링 확인", "description": "부자재 탭 클릭하여 필터링 확인",
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "부자재", "role": "tab" }, {
{ "type": "wait", "duration": 500 } "type": "click_if_exists",
"target": "부자재",
"role": "tab"
},
{
"type": "wait",
"duration": 500
}
], ],
"expect": { "expect": {
"tabActive": "부자재", "tabActive": "부자재",
@@ -127,12 +188,19 @@
} }
}, },
{ {
"id": "step-5", "id": 6,
"name": "필수 검증 #3: 품목유형 탭 필터 - 소모품", "name": "필수 검증 #3: 품목유형 탭 필터 - 소모품",
"description": "소모품 탭 클릭하여 필터링 확인", "description": "소모품 탭 클릭하여 필터링 확인",
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "소모품", "role": "tab" }, {
{ "type": "wait", "duration": 500 } "type": "click_if_exists",
"target": "소모품",
"role": "tab"
},
{
"type": "wait",
"duration": 500
}
], ],
"expect": { "expect": {
"tabActive": "소모품", "tabActive": "소모품",
@@ -140,12 +208,19 @@
} }
}, },
{ {
"id": "step-6", "id": 7,
"name": "전체 탭으로 복귀", "name": "전체 탭으로 복귀",
"description": "전체 탭 클릭하여 모든 재고 표시", "description": "전체 탭 클릭하여 모든 재고 표시",
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "전체", "role": "tab" }, {
{ "type": "wait", "duration": 300 } "type": "click_if_exists",
"target": "전체",
"role": "tab"
},
{
"type": "wait",
"duration": 300
}
], ],
"expect": { "expect": {
"tabActive": "전체", "tabActive": "전체",
@@ -153,12 +228,18 @@
} }
}, },
{ {
"id": "step-7", "id": 8,
"name": "필수 검증 #1: 엑셀 다운로드", "name": "필수 검증 #1: 엑셀 다운로드",
"description": "엑셀 다운로드 버튼 동작 확인", "description": "엑셀 다운로드 버튼 동작 확인",
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "엑셀 다운로드" }, {
{ "type": "wait", "duration": 1000 } "type": "click_if_exists",
"target": "엑셀 다운로드"
},
{
"type": "wait",
"duration": 1000
}
], ],
"expect": { "expect": {
"downloadTriggered": true, "downloadTriggered": true,
@@ -169,7 +250,7 @@
} }
}, },
{ {
"id": "step-8", "id": 9,
"name": "재고 상세 열기", "name": "재고 상세 열기",
"description": "재고 항목 클릭하여 상세 보기", "description": "재고 항목 클릭하여 상세 보기",
"actions": [ "actions": [
@@ -180,32 +261,54 @@
], ],
"expect": { "expect": {
"pageOrModal": "재고 상세", "pageOrModal": "재고 상세",
"visible": ["품목코드", "품목명", "재고량", "LOT"] "visible": [
"품목코드",
"품목명",
"재고량",
"LOT"
]
} }
}, },
{ {
"id": "step-9", "id": 10,
"name": "상세 닫기", "name": "상세 닫기",
"description": "ESC 키로 상세 닫기 또는 뒤로가기", "description": "ESC 키로 상세 닫기 또는 뒤로가기",
"actions": [ "actions": [
{ "type": "press", "key": "Escape" }, {
{ "type": "wait", "duration": 300 } "type": "press",
"key": "Escape"
},
{
"type": "wait",
"duration": 300
}
] ]
}, },
{ {
"id": "step-10", "id": 11,
"name": "콘솔 에러 확인",
"action": "verify_element",
"target": "body"
},
{
"id": 12,
"name": "페이지네이션 확인", "name": "페이지네이션 확인",
"description": "페이지네이션 동작 확인", "description": "페이지네이션 동작 확인",
"actions": [ "actions": [
{ "type": "click_if_exists", "target": "다음" }, {
{ "type": "wait", "duration": 500 } "type": "click_if_exists",
"target": "다음"
},
{
"type": "wait",
"duration": 500
}
], ],
"expect": { "expect": {
"pageChanged": true "pageChanged": true
} }
} }
], ],
"assertions": [ "assertions": [
{ {
"type": "url", "type": "url",
@@ -218,7 +321,6 @@
"message": "엑셀 다운로드 버튼이 표시되어야 함" "message": "엑셀 다운로드 버튼이 표시되어야 함"
} }
], ],
"mandatoryVerifications": { "mandatoryVerifications": {
"description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목", "description": "E2E_TEST_CONFIG.md 기준 필수 검증 항목",
"items": [ "items": [
@@ -238,13 +340,33 @@
} }
] ]
}, },
"notes": { "notes": {
"testScope": "재고현황 조회 및 필터링, 엑셀 다운로드 테스트", "testScope": "재고현황 조회 및 필터링, 엑셀 다운로드 테스트",
"pageType": "조회 전용 (입고관리에서 재고 증가, 출하관리에서 재고 감소)", "pageType": "조회 전용 (입고관리에서 재고 증가, 출하관리에서 재고 감소)",
"statsCards": ["전체 품목", "정상 재고", "재고 부족", "재고 없음"], "statsCards": [
"typeTabs": ["전체", "원자재", "부자재", "소모품"], "전체 품목",
"tableColumns": ["번호", "품목코드", "품목명", "품목유형", "단위", "재고", "안전재고", "LOT", "상태", "위치"], "정상 재고",
"재고 부족",
"재고 없음"
],
"typeTabs": [
"전체",
"원자재",
"부자재",
"소모품"
],
"tableColumns": [
"번호",
"품목코드",
"품목명",
"품목유형",
"단위",
"재고량",
"안전재고",
"LOT",
"상태",
"위치"
],
"prerequisites": "로그인된 사용자" "prerequisites": "로그인된 사용자"
} }
} }

Some files were not shown because too many files have changed in this diff Show More