{ "id": "crud-delete-freeboard", "name": "자유게시판 CRUD 삭제 기능 테스트", "screenshotPolicy": { "onErrorOnly": true, "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] }, "description": "자유게시판에서 생성 → 수정 → 삭제 전체 CRUD 흐름 테스트. 테스트용 게시글을 생성하고, 수정한 후, 삭제하여 기존 데이터에 영향 없이 삭제 기능을 검증", "baseUrl": "https://dev.codebridge-x.com", "menuNavigation": { "level1": "게시판", "level2": "자유게시판", "expectedUrl": "/ko/boards/free" }, "auth": { "username": "TestUser5", "password": "password123!" }, "testPolicy": { "deleteAllowed": true, "deleteCondition": "CRUD 흐름 내에서만 허용 (생성 → 수정 → 삭제)", "protectExistingData": true, "description": "이 시나리오에서 생성한 테스트 데이터만 삭제" }, "modalHandling": { "description": "모달 창 처리 규칙", "closeMethods": [ {"priority": 1, "method": "완료 버튼", "selector": "button:has-text('확인'), button:has-text('등록'), button:has-text('저장')"}, {"priority": 2, "method": "취소 버튼", "selector": "button:has-text('취소'), [class*='cancel']"}, {"priority": 3, "method": "X 버튼", "selector": "button[class*='close'], [aria-label='닫기'], [aria-label='Close']"}, {"priority": 4, "method": "ESC 키", "action": "press_key('Escape')"}, {"priority": 5, "method": "외부 클릭", "selector": "[class*='backdrop'], [class*='overlay']"} ], "deleteDialogSelector": "[role='alertdialog'] button:has-text('삭제')", "note": "삭제 확인 다이얼로그는 Playwright 네이티브 셀렉터 사용 필수 (JavaScript click 미동작)", "rule": "모달이 열린 상태로 다음 단계 진행 금지" }, "testData": { "newPost": { "title": "E2E테스트_삭제용_", "content": "이 게시글은 E2E 테스트용으로 생성되었습니다. 테스트 완료 후 자동 삭제됩니다." }, "updateData": { "title": "E2E테스트_수정완료_" }, "uniqueIdentifier": "timestamp를 붙여서 고유성 보장 (MMDDHHmmss)" }, "steps": [ { "id": "step-0", "name": "사이드바 메뉴 탐색", "description": "게시판 > 자유게시판 메뉴로 이동", "actions": [ {"type": "scroll", "target": "sidebar", "direction": "top", "description": "사이드바 상단으로 스크롤"}, {"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/boards/free", "no404": true } }, { "id": "step-1", "phase": "CREATE", "name": "[CREATE] 초기 상태 확인", "description": "등록 전 게시글 수 확인", "actions": [ {"type": "capture", "variable": "initialRowCount", "selector": "table tbody tr", "extract": "count", "description": "등록 전 행 수 저장"}, {"type": "wait", "duration": 500} ], "expect": { "tableExists": true } }, { "id": "step-2", "phase": "CREATE", "name": "[CREATE] 등록 버튼 클릭", "description": "새 게시글을 등록하기 위해 등록 버튼 클릭", "actions": [ {"type": "click_if_exists", "target": "button:has-text('등록')", "description": "등록 버튼 클릭"}, {"type": "wait", "duration": 1500} ], "expect": { "url": "/ko/boards/free/new", "pageTitle": "게시글 작성" } }, { "id": "step-3", "phase": "CREATE", "name": "[CREATE] 게시글 정보 입력", "description": "테스트용 게시글 정보 입력 (타임스탬프로 고유성 보장)", "actions": [ {"type": "generateTimestamp", "variable": "testTimestamp", "format": "MMDDHHmmss"}, {"type": "fill", "target": "input[name='title'], input[placeholder*='제목']", "value": "E2E테스트_삭제용_{testTimestamp}", "description": "고유한 제목 입력"}, {"type": "fill", "target": "textarea, [class*='editor'], [contenteditable='true']", "value": "E2E 테스트용 게시글입니다. 자동 삭제 예정.", "description": "본문 입력"} ], "note": "타임스탬프를 사용하여 매 테스트마다 고유한 데이터 생성" }, { "id": "step-4", "phase": "CREATE", "name": "[CREATE] 등록 실행", "description": "입력된 정보로 게시글 등록 실행", "actions": [ {"type": "click_if_exists", "target": "button:has-text('등록')", "description": "등록 버튼 클릭"}, {"type": "wait", "duration": 2000} ], "expect": { "toast": "등록|완료|성공", "redirect": "/ko/boards/free" }, "verification": { "level": 4, "checks": ["성공 토스트 표시", "목록 페이지로 이동"] } }, { "id": "step-5", "phase": "CREATE", "name": "[CREATE] 등록 결과 확인", "description": "테이블에 새로 등록한 게시글이 표시되는지 확인", "actions": [ {"type": "wait", "duration": 1000}, {"type": "capture", "variable": "afterCreateCount", "selector": "table tbody tr", "extract": "count"}, {"type": "verify", "condition": "afterCreateCount > initialRowCount", "description": "행 수 증가 확인"} ], "expect": { "rowCountIncreased": true, "rowContains": "E2E테스트_삭제용" }, "verification": { "level": 4, "description": "생성된 데이터가 목록에 표시되어야 함" } }, { "id": "step-6", "phase": "UPDATE", "name": "[UPDATE] 생성된 게시글 상세 페이지 진입", "description": "생성한 테스트 게시글의 상세 페이지로 이동", "actions": [ {"type": "click_if_exists", "target": "table tbody tr:first-child td:nth-child(2)", "description": "첫 번째 행 (방금 생성한 게시글) 클릭"}, {"type": "wait", "duration": 2000} ], "expect": { "url": "/ko/boards/free/", "pageContains": "E2E테스트_삭제용" } }, { "id": "step-7", "phase": "UPDATE", "name": "[UPDATE] 수정 버튼 클릭", "description": "수정 버튼을 클릭하여 편집 모드로 전환", "actions": [ {"type": "click_if_exists", "target": "button:has-text('수정')", "description": "수정 버튼 클릭"}, {"type": "wait", "duration": 1500} ], "expect": { "url": "/edit", "fieldsEditable": true } }, { "id": "step-8", "phase": "UPDATE", "name": "[UPDATE] 제목 수정", "description": "게시글 제목을 수정하여 UPDATE 동작 확인", "actions": [ {"type": "clear", "target": "input[name='title'], input[placeholder*='제목']", "description": "기존 제목 삭제"}, {"type": "fill", "target": "input[name='title'], input[placeholder*='제목']", "value": "E2E테스트_수정완료_{testTimestamp}", "description": "수정된 제목 입력"} ] }, { "id": "step-9", "phase": "UPDATE", "name": "[UPDATE] 수정 저장", "description": "수정된 내용 저장", "actions": [ {"type": "click_if_exists", "target": "button:has-text('수정')", "description": "수정 버튼 클릭"}, {"type": "wait", "duration": 2000} ], "expect": { "toast": "수정|완료|성공", "redirect": "/ko/boards/free/" }, "verification": { "level": 4, "description": "수정 성공 토스트 확인" } }, { "id": "step-10", "phase": "UPDATE", "name": "[UPDATE] 수정 결과 확인", "description": "수정된 내용이 반영되었는지 확인", "actions": [ {"type": "wait", "duration": 1000}, {"type": "verify", "target": "page", "contains": "E2E테스트_수정완료", "description": "수정된 제목 표시 확인"} ], "expect": { "contains": "E2E테스트_수정완료" } }, { "id": "step-11", "phase": "DELETE", "name": "[DELETE] 삭제 버튼 클릭", "description": "테스트용으로 생성한 게시글 삭제 시작", "actions": [ {"type": "click_if_exists", "target": "button:has-text('삭제')", "description": "삭제 버튼 클릭"}, {"type": "wait", "duration": 500} ], "expect": { "confirmDialog": true, "dialogRole": "alertdialog" }, "warning": "이 단계에서 삭제 확인 다이얼로그가 표시되어야 함" }, { "id": "step-12", "phase": "DELETE", "name": "[DELETE] 삭제 확인", "description": "삭제 확인 다이얼로그에서 삭제 버튼 클릭", "actions": [ {"type": "click_if_exists", "target": "[role='alertdialog'] button:has-text('삭제')", "usePlaywrightNative": true, "description": "삭제 확인 클릭 (Playwright 네이티브 셀렉터 필수)"}, {"type": "wait", "duration": 2000} ], "expect": { "toast": "삭제|완료|성공", "redirect": "/ko/boards/free" }, "verification": { "level": 4, "checks": ["성공 토스트", "목록 페이지로 리다이렉트"] }, "note": "JavaScript click()은 동작하지 않음. Playwright playwright_click 사용 필수" }, { "id": "step-13", "phase": "VERIFY", "name": "[VERIFY] 삭제 결과 확인", "description": "삭제된 게시글이 목록에서 제거되었는지 확인", "actions": [ {"type": "wait", "duration": 1000}, {"type": "verifyUrl", "contains": "/ko/boards/free", "description": "목록 페이지 확인"}, {"type": "capture", "variable": "afterDeleteCount", "selector": "table tbody tr", "extract": "count"} ], "expect": { "rowCountDecreased": true, "condition": "afterDeleteCount === initialRowCount" }, "verification": { "level": 4, "description": "행 수가 원래대로 복구되어야 함" } }, { "id": "step-14", "phase": "VERIFY", "name": "[VERIFY] 최종 검증", "description": "테스트 데이터가 완전히 삭제되었는지 확인", "actions": [ {"type": "verify", "target": "table", "notContains": "E2E테스트_수정완료", "description": "삭제된 게시글이 목록에 없음 확인"} ], "expect": { "testDataDeleted": true, "noTestDataInList": true }, "verification": { "level": 4, "description": "테스트 데이터가 완전히 삭제되어 목록에 표시되지 않아야 함" } } ], "expectedAPIs": [ { "phase": "CREATE", "method": "POST", "endpoint": "/api/v1/free-board", "description": "게시글 등록" }, { "phase": "UPDATE", "method": "PUT", "endpoint": "/api/v1/free-board/{id}", "description": "게시글 수정" }, { "phase": "DELETE", "method": "DELETE", "endpoint": "/api/v1/free-board/{id}", "description": "게시글 삭제" } ], "requiredVerifications": [ { "id": 1, "name": "CREATE - 등록 기능", "steps": ["step-2", "step-3", "step-4", "step-5"], "criteria": "게시글 생성 + 목록에 표시" }, { "id": 2, "name": "UPDATE - 수정 기능", "steps": ["step-7", "step-8", "step-9", "step-10"], "criteria": "게시글 수정 + 변경 내용 반영" }, { "id": 3, "name": "DELETE - 삭제 기능", "steps": ["step-11", "step-12", "step-13", "step-14"], "criteria": "게시글 삭제 + 목록에서 제거" } ], "knownIssues": [ { "issue": "삭제 확인 다이얼로그 JavaScript click 미동작", "description": "JavaScript의 element.click() 또는 dispatchEvent로는 삭제가 실행되지 않음", "workaround": "Playwright의 playwright_click 도구와 [role='alertdialog'] button:has-text('삭제') 셀렉터 사용", "testedOn": "2026-01-29" } ], "rollbackPlan": { "description": "테스트 실패 시 롤백 계획", "onCreateFail": "영향 없음 - 다음 테스트 정상 진행", "onUpdateFail": "테스트 데이터 수동 삭제 필요", "onDeleteFail": "테스트 데이터 수동 삭제 필요", "cleanupRequired": "E2E테스트_ 로 시작하는 게시글은 테스트 데이터이므로 수동 삭제 가능" } }