{ "id": "crud-delete-vendor", "name": "거래처 CRUD 삭제 기능 테스트", "status": "BLOCKED", "blockedReason": "거래처관리 페이지에 등록 버튼이 없음 - CREATE 단계 불가", "discoveredOn": "2026-01-29", "alternative": "crud-delete-freeboard.json 사용 (자유게시판 CRUD 테스트)", "screenshotPolicy": { "onErrorOnly": true, "captureOn": [ "error", "fail", "timeout", "404", "500", "blocked" ] }, "description": "거래처관리에서 생성 → 수정 → 삭제 전체 CRUD 흐름 테스트. 테스트용 데이터를 생성하고, 수정한 후, 삭제하여 기존 데이터에 영향 없이 삭제 기능을 검증", "baseUrl": "https://dev.codebridge-x.com", "menuNavigation": { "level1": "회계관리", "level2": "거래처관리", "expectedUrl": "/ko/accounting/vendors" }, "auth": { "username": "TestUser5", "password": "password123!" }, "testPolicy": { "deleteAllowed": true, "deleteCondition": "CRUD 흐름 내에서만 허용 (생성 → 수정 → 삭제)", "protectExistingData": true, "description": "이 시나리오에서 생성한 테스트 데이터만 삭제" }, "modalHandling": { "description": "모달 창 처리 규칙", "closeMethods": [ { "priority": 1, "method": "완료 버튼", "selector": "button:has-text('확인'), button:has-text('등록'), button:has-text('저장')" }, { "priority": 2, "method": "취소 버튼", "selector": "button:has-text('취소'), [class*='cancel']" }, { "priority": 3, "method": "X 버튼", "selector": "button[class*='close'], [aria-label='닫기'], [aria-label='Close']" }, { "priority": 4, "method": "ESC 키", "action": "press_key", "value": "Escape" }, { "priority": 5, "method": "외부 클릭", "selector": "[class*='backdrop'], [class*='overlay']" } ], "rule": "모달이 열린 상태로 다음 단계 진행 금지" }, "testData": { "newVendor": { "vendorName": "E2E테스트_삭제용_", "businessNumber": "123-45-67890", "representative": "테스트대표", "vendorType": "매출", "phone": "02-1234-5678", "email": "test@e2etest.com", "address": "서울시 테스트구 테스트동 123" }, "updateData": { "vendorName": "E2E테스트_수정완료_", "representative": "수정대표" }, "uniqueIdentifier": "timestamp를 붙여서 고유성 보장" }, "steps": [ { "id": "step-0", "name": "사이드바 메뉴 탐색", "description": "회계관리 > 거래처관리 메뉴로 이동", "actions": [ { "type": "scroll", "target": "sidebar", "direction": "top" }, { "type": "wait", "duration": 300 }, { "type": "click_if_exists", "target": "회계관리", "description": "1차 메뉴 클릭" }, { "type": "wait", "duration": 500 }, { "type": "click_if_exists", "target": "거래처관리", "description": "2차 메뉴 클릭" }, { "type": "wait", "duration": 2000 } ], "expect": { "url": "/ko/accounting/vendors", "no404": true } }, { "id": "step-1", "phase": "CREATE", "name": "📝 [CREATE] 거래처 등록 버튼 클릭", "description": "새 거래처를 등록하기 위해 등록 버튼 클릭", "actions": [ { "type": "capture", "variable": "initialRowCount", "selector": "table tbody tr", "extract": "count", "description": "등록 전 행 수 저장" }, { "type": "click_if_exists", "target": "button:has-text('등록'), button:has-text('추가'), [class*='add'], [class*='register']", "description": "등록 버튼 클릭" }, { "type": "wait", "duration": 1000 } ], "expect": { "modal": true, "modalTitle": "거래처 등록" } }, { "id": "step-2", "phase": "CREATE", "name": "📝 [CREATE] 등록 모달 - 필수 정보 입력", "description": "테스트용 거래처 정보 입력 (타임스탬프로 고유성 보장)", "actions": [ { "type": "generateTimestamp", "variable": "testTimestamp", "format": "MMDDHHmmss" }, { "type": "click_if_exists", "target": "거래처명", "description": "거래처명 입력 필드 확인" }, { "type": "click_if_exists", "target": "사업자등록번호", "description": "사업자번호 입력 필드 확인" }, { "type": "click_if_exists", "target": "대표자명", "description": "대표자명 입력 필드 확인" }, { "type": "click_if_exists", "target": "거래처 유형", "description": "거래처 유형 필드 확인" }, { "type": "click_if_exists", "target": "전화번호", "description": "전화번호 입력 필드 확인" }, { "type": "click_if_exists", "target": "이메일", "description": "이메일 입력 필드 확인" } ], "note": "타임스탬프를 사용하여 매 테스트마다 고유한 데이터 생성" }, { "id": "step-3", "phase": "CREATE", "name": "📝 [CREATE] 등록 모달 - 등록 버튼 클릭", "description": "입력된 정보로 거래처 등록 실행", "actions": [ { "type": "click_if_exists", "target": "button:has-text('등록'), button:has-text('저장')", "description": "모달 내 등록 버튼 클릭" }, { "type": "wait", "duration": 2000 } ], "expect": { "toast": "등록|완료|성공", "modalClosed": true, "apiCall": "POST /api/v1/clients" }, "verification": { "level": 4, "checks": [ "API 호출 확인", "성공 토스트 표시", "모달 닫힘" ] } }, { "id": "step-3-modal-close", "phase": "CREATE", "name": "📝 [CREATE] 모달 닫기 확인", "description": "모달이 열려있으면 닫기 시도", "condition": "modalStillOpen", "actions": [ { "type": "click_if_exists", "target": "[role='dialog'] button[class*='close'], [aria-label='닫기'], [aria-label='Close']", "description": "모달 닫기 시도" }, { "type": "wait", "duration": 500 } ], "expect": { "modalClosed": true } }, { "id": "step-4", "phase": "CREATE", "name": "📝 [CREATE] 등록 결과 확인", "description": "테이블에 새로 등록한 거래처가 표시되는지 확인", "actions": [ { "type": "fill", "target": "검색", "value": "E2E테스트_삭제용", "description": "생성한 거래처 검색" }, { "type": "pressKey", "key": "Enter" }, { "type": "wait", "duration": 1500 }, { "type": "capture", "variable": "createdVendorRow", "selector": "table tbody tr", "extract": "exists", "description": "테스트 거래처 행 존재 여부 확인" } ], "expect": { "rowExists": true, "rowContains": "E2E테스트_삭제용" }, "verification": { "level": 4, "description": "생성된 데이터가 목록에 표시되어야 함" } }, { "id": "step-5", "phase": "UPDATE", "name": "✏️ [UPDATE] 생성된 거래처 상세 페이지 진입", "description": "생성한 테스트 거래처의 상세 페이지로 이동", "actions": [ { "type": "click_if_exists", "target": "table tbody tr", "description": "첫 번째 거래처 행 클릭" }, { "type": "wait", "duration": 2000 } ], "expect": { "url": "/ko/accounting/vendors/", "pageTitle": "거래처 상세" } }, { "id": "step-6", "phase": "UPDATE", "name": "✏️ [UPDATE] 수정 모드 진입", "description": "수정 버튼을 클릭하여 편집 모드로 전환", "actions": [ { "type": "click_if_exists", "target": "button:has-text('수정')", "description": "수정 버튼 클릭" }, { "type": "wait", "duration": 1000 } ], "expect": { "url": "mode=edit", "fieldsEditable": true } }, { "id": "step-7", "phase": "UPDATE", "name": "✏️ [UPDATE] 거래처명 수정", "description": "거래처명을 수정하여 UPDATE 동작 확인", "actions": [ { "type": "clear", "target": "거래처명", "description": "기존 값 삭제" }, { "type": "fill", "target": "거래처명", "value": "E2E테스트_수정완료_{testTimestamp}", "description": "수정된 거래처명 입력" }, { "type": "fill", "target": "대표자명", "value": "수정대표", "description": "대표자명 수정" } ] }, { "id": "step-8", "phase": "UPDATE", "name": "✏️ [UPDATE] 수정 저장", "description": "수정된 내용 저장", "actions": [ { "type": "click_if_exists", "target": "button:has-text('저장')", "description": "저장 버튼 클릭" }, { "type": "wait", "duration": 500 }, { "type": "click_if_exists", "target": "button:has-text('확인')", "description": "저장 확인 다이얼로그" }, { "type": "wait", "duration": 2000 } ], "expect": { "toast": "수정|완료|성공", "apiCall": "PUT /api/v1/clients/" }, "verification": { "level": 4, "description": "수정 API 호출 및 성공 토스트 확인" } }, { "id": "step-8-modal-close", "phase": "UPDATE", "name": "✏️ [UPDATE] 다이얼로그 닫기 확인", "description": "다이얼로그가 열려있으면 닫기", "condition": "dialogStillOpen", "actions": [ { "type": "click_if_exists", "target": "[role='dialog'] button[class*='close'], [aria-label='닫기'], [aria-label='Close']", "description": "다이얼로그 닫기 시도" } ] }, { "id": "step-9", "phase": "UPDATE", "name": "✏️ [UPDATE] 수정 결과 확인", "description": "수정된 내용이 반영되었는지 확인", "actions": [ { "type": "wait", "duration": 1000 }, { "type": "capture", "variable": "updatedVendorName", "selector": "[class*='vendor-name'], h1, h2", "extract": "text" } ], "expect": { "contains": "E2E테스트_수정완료" } }, { "id": "step-10", "phase": "DELETE", "name": "🗑️ [DELETE] 삭제 버튼 클릭", "description": "테스트용으로 생성한 거래처 삭제 시작", "actions": [ { "type": "click_if_exists", "target": "button:has-text('삭제')", "description": "삭제 버튼 클릭" }, { "type": "wait", "duration": 500 } ], "expect": { "confirmDialog": true, "dialogMessage": "삭제|정말|확인" }, "warning": "이 단계에서 삭제 확인 다이얼로그가 표시되어야 함" }, { "id": "step-11", "phase": "DELETE", "name": "🗑️ [DELETE] 삭제 확인 다이얼로그 검증", "description": "삭제 확인 다이얼로그 UI 요소 확인", "actions": [ { "type": "verify", "target": "dialog", "checks": [ "삭제 확인 메시지", "취소 버튼", "확인/삭제 버튼" ] } ], "expect": { "dialogElements": [ "취소", "확인|삭제" ] } }, { "id": "step-12", "phase": "DELETE", "name": "🗑️ [DELETE] 삭제 확인 버튼 클릭", "description": "삭제를 최종 확인하여 실행", "actions": [ { "type": "click_if_exists", "target": "button:has-text('확인'), button:has-text('삭제')", "description": "삭제 확인 클릭" }, { "type": "wait", "duration": 2000 } ], "expect": { "toast": "삭제|완료|성공", "apiCall": "DELETE /api/v1/clients/", "redirect": "/ko/accounting/vendors" }, "verification": { "level": 4, "checks": [ "DELETE API 호출", "성공 토스트", "목록 페이지로 리다이렉트" ] } }, { "id": "step-12-modal-close", "phase": "DELETE", "name": "🗑️ [DELETE] 다이얼로그 닫기 확인", "description": "다이얼로그가 열려있으면 닫기", "condition": "dialogStillOpen", "actions": [ { "type": "click_if_exists", "target": "[role='dialog'] button[class*='close'], [aria-label='닫기'], [aria-label='Close']", "description": "다이얼로그 닫기 시도" } ] }, { "id": "step-13", "phase": "VERIFY", "name": "✅ [VERIFY] 삭제 결과 확인 - 목록 페이지", "description": "삭제된 거래처가 목록에서 제거되었는지 확인", "actions": [ { "type": "wait", "duration": 1000 }, { "type": "verifyUrl", "contains": "/ko/accounting/vendors", "description": "목록 페이지 확인" }, { "type": "fill", "target": "검색", "value": "E2E테스트_수정완료", "description": "삭제된 거래처 검색" }, { "type": "pressKey", "key": "Enter" }, { "type": "wait", "duration": 1500 } ], "expect": { "noResults": true, "rowNotExists": "E2E테스트_수정완료" } }, { "id": "step-14", "phase": "VERIFY", "name": "✅ [VERIFY] 최종 검증 - 데이터 삭제 확인", "description": "검색 결과에서 테스트 데이터가 없음을 최종 확인", "actions": [ { "type": "capture", "variable": "searchResultCount", "selector": "table tbody tr", "extract": "count" }, { "type": "verify", "condition": "searchResultCount === 0 OR no row contains 'E2E테스트'" } ], "expect": { "testDataDeleted": true, "noTestDataInList": true }, "verification": { "level": 4, "description": "테스트 데이터가 완전히 삭제되어 검색되지 않아야 함" } }, { "id": "step-15", "phase": "CLEANUP", "name": "🧹 [CLEANUP] 검색 초기화", "description": "테스트 종료 후 검색어 삭제하여 원래 상태로 복원", "actions": [ { "type": "clear", "target": "검색" }, { "type": "pressKey", "key": "Enter" }, { "type": "wait", "duration": 1000 } ], "expect": { "originalListRestored": true } } ], "expectedAPIs": [ { "phase": "CREATE", "method": "POST", "endpoint": "/api/v1/clients", "description": "거래처 등록" }, { "phase": "UPDATE", "method": "PUT", "endpoint": "/api/v1/clients/{id}", "description": "거래처 수정" }, { "phase": "DELETE", "method": "DELETE", "endpoint": "/api/v1/clients/{id}", "description": "거래처 삭제" } ], "requiredVerifications": [ { "id": 1, "name": "CREATE - 등록 기능", "steps": [ "step-1", "step-2", "step-3", "step-4" ], "criteria": "POST API 호출 + 성공 토스트 + 목록에 데이터 표시" }, { "id": 2, "name": "UPDATE - 수정 기능", "steps": [ "step-6", "step-7", "step-8", "step-9" ], "criteria": "PUT API 호출 + 성공 토스트 + 데이터 변경 반영" }, { "id": 3, "name": "DELETE - 삭제 기능", "steps": [ "step-10", "step-11", "step-12", "step-13", "step-14" ], "criteria": "DELETE API 호출 + 성공 토스트 + 목록에서 데이터 제거" }, { "id": 4, "name": "모달/다이얼로그 닫기", "steps": [ "step-3-modal-close", "step-8-modal-close", "step-12-modal-close" ], "criteria": "모든 모달/다이얼로그가 정상적으로 닫혀야 함" } ], "rollbackPlan": { "description": "테스트 실패 시 롤백 계획", "onCreateFail": "모달 닫기 → 다음 테스트 영향 없음", "onUpdateFail": "테스트 데이터 수동 삭제 필요 (DB 또는 UI)", "onDeleteFail": "테스트 데이터 수동 삭제 필요", "cleanupRequired": "E2E테스트_ 로 시작하는 거래처는 테스트 데이터이므로 수동 삭제 가능" } }