{ "id": "board-test", "name": "게시판 테스트 E2E 테스트", "screenshotPolicy": { "onErrorOnly": true, "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] }, "description": "게시판 테스트 메뉴의 목록, 게시글 작성, 상세, 수정, 삭제, 댓글 CRUD 전체 워크플로우 테스트", "url": "/ko/boards/board_mjsgri54_1fmg", "menuNavigation": { "level1": "게시판", "level2": "게시판 테스트", "expectedUrl": "/ko/boards/board_mjsgri54_1fmg" }, "menuNavigationEnhanced": { "strategy": "scroll-and-search", "level1": { "text": "게시판", "scrollToFind": true, "maxScrollAttempts": 5 }, "level2": { "text": "게시판 테스트", "scrollToFind": true, "maxScrollAttempts": 3 }, "expectedUrl": "/ko/boards/board_mjsgri54_1fmg", "fallbackUrl": "/ko/boards/board_mjsgri54_1fmg" }, "steps": [ { "step": 0, "name": "사이드바 메뉴 전체 펼치기", "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", "actions": [ { "type": "evaluate", "script": "document.querySelector('.sidebar-scroll, [data-sidebar], nav')?.scrollTo({top: 0, behavior: 'instant'})" }, { "type": "wait", "duration": 300 }, { "type": "evaluate", "script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()" }, { "type": "wait", "duration": 2000 } ], "expected": { "sidebarReady": true } }, { "step": 1, "name": "2단계 메뉴 진입: 게시판 > 게시판 테스트", "description": "게시판 > 게시판 테스트 메뉴로 이동하여 페이지 로드 확인 (스크롤 탐색 포함)", "navigationPattern": "scrollAndFind", "actions": [ { "type": "scrollAndFind", "target": "게시판", "container": ".sidebar-scroll, [data-sidebar], nav", "maxScrollAttempts": 5 }, { "type": "click", "target": "게시판" }, { "type": "wait", "duration": 500 }, { "type": "scrollAndFind", "target": "게시판 테스트", "container": ".sidebar-scroll, [data-sidebar], nav", "maxScrollAttempts": 3 }, { "type": "click", "target": "게시판 테스트" }, { "type": "wait", "target": "페이지 로드 완료" } ], "expected": { "url": "/ko/boards/board_mjsgri54_1fmg", "title": "게시판 테스트", "authenticated": true }, "verification": { "url_contains": "/boards/board_mjsgri54_1fmg", "elements": ["table", "button:has-text('글쓰기')"] } }, { "step": 2, "name": "초기 게시글 목록 확인", "action": "verify_table_structure", "verification": { "columns": ["No.", "제목", "작성자", "조회수", "상태", "등록일"], "min_rows": 0 } }, { "step": 3, "name": "게시글 총 건수 확인", "action": "verify_text", "verification": { "text_pattern": "총 \\d+건" } }, { "step": 4, "name": "검색창 존재 확인", "action": "verify_element", "target": "input[placeholder*='제목']", "verification": { "exists": true } }, { "step": 5, "name": "상태 필터 드롭다운 확인", "action": "verify_element", "target": "[role='combobox']", "verification": { "exists": true, "count": 2 } }, { "step": 6, "name": "정렬 필터 드롭다운 확인", "action": "verify_element", "target": "[role='combobox']:has-text('최신순')", "verification": { "exists": true } }, { "step": 7, "name": "날짜 범위 선택 버튼 확인", "action": "verify_elements", "verification": { "buttons": ["당해년도", "전전월", "전월", "당월", "어제", "오늘"] } }, { "step": 8, "name": "글쓰기 버튼 확인", "action": "verify_element", "target": "button:has-text('글쓰기')", "verification": { "exists": true, "enabled": true } }, { "step": 9, "name": "테이블 체크박스 확인", "action": "verify_element", "target": "table th checkbox", "verification": { "exists": true } }, { "step": 10, "name": "테이블 컬럼 헤더 확인", "action": "verify_table_headers", "verification": { "headers": ["No.", "제목", "작성자", "조회수", "상태", "등록일"] } }, { "step": 11, "name": "초기 게시글 수 저장", "action": "store_value", "target": "총 건수", "variable": "initial_post_count" }, { "step": 12, "name": "상태 필터 클릭 테스트", "action": "click", "target": "[role='combobox']:first", "verification": { "dropdown_opens": true } }, { "step": 13, "name": "상태 필터 옵션 확인", "action": "verify_dropdown_options", "verification": { "options": ["전체", "게시됨", "임시저장"] } }, { "step": 14, "name": "상태 필터 닫기 (ESC)", "action": "press_key", "key": "Escape" }, { "step": 15, "name": "글쓰기 버튼 클릭", "action": "click", "target": "button:has-text('글쓰기')", "verification": { "url_contains": "/create" } }, { "step": 16, "name": "글쓰기 페이지 URL 확인", "action": "verify_url", "expected": "/ko/boards/board_mjsgri54_1fmg/create" }, { "step": 17, "name": "작성 폼 구조 확인", "action": "verify_form_structure", "verification": { "fields": ["제목", "내용"], "checkboxes": ["비밀글로 등록"], "buttons": ["취소", "등록"] } }, { "step": 18, "name": "제목 입력 필드 확인", "action": "verify_element", "target": "#title", "verification": { "exists": true, "type": "input" } }, { "step": 19, "name": "내용 입력 필드 확인", "action": "verify_element", "target": "#content", "verification": { "exists": true, "type": "textarea" } }, { "step": 20, "name": "제목 입력", "action": "fill", "target": "#title", "value": "E2E 테스트 게시글" }, { "step": 21, "name": "내용 입력", "action": "fill", "target": "#content", "value": "E2E 자동화 테스트를 위한 게시글입니다." }, { "step": 22, "name": "등록 버튼 클릭", "action": "click", "target": "button:has-text('등록')", "validation": "필수 검증 #2 (URL 안정성)" }, { "step": 23, "name": "페이지 이동 대기 (1초)", "action": "wait", "duration": 1000 }, { "step": 24, "name": "URL 안정성 검증 - 게시글 상세 페이지로 정상 이동", "action": "verify_url_stability", "validation": "필수 검증 #2", "verification": { "url_pattern": "/ko/boards/board_mjsgri54_1fmg/\\d+", "no_error_page": true, "no_404": true } }, { "step": 25, "name": "게시글 제목 표시 확인", "action": "verify_text", "target": "h4", "expected": "E2E 테스트 게시글" }, { "step": 26, "name": "게시글 내용 표시 확인", "action": "verify_content", "expected": "E2E 자동화 테스트를 위한 게시글입니다." }, { "step": 27, "name": "작성자 정보 확인", "action": "verify_text", "expected": "회원" }, { "step": 28, "name": "등록일 표시 확인", "action": "verify_element", "target": "text*='2026-01'" }, { "step": 29, "name": "조회수 확인", "action": "verify_text", "verification": { "text_pattern": "\\d+" } }, { "step": 30, "name": "수정 버튼 존재 확인 (작성자)", "action": "verify_element", "target": "button:has-text('수정')", "verification": { "exists": true } }, { "step": 31, "name": "삭제 버튼 존재 확인 (작성자)", "action": "verify_element", "target": "button:has-text('삭제')", "verification": { "exists": true } }, { "step": 32, "name": "목록으로 버튼 존재 확인", "action": "verify_element", "target": "button:has-text('목록으로')", "verification": { "exists": true } }, { "step": 33, "name": "댓글 섹션 확인", "action": "verify_element", "target": "h4:has-text('댓글')", "verification": { "exists": true } }, { "step": 34, "name": "초기 댓글 수 확인", "action": "verify_text", "target": "h4:has-text('댓글')", "verification": { "text_contains": "댓글 (0)" } }, { "step": 35, "name": "첫 번째 댓글 입력", "action": "fill", "target": "textarea[placeholder*='댓글']", "value": "첫 번째 테스트 댓글입니다." }, { "step": 36, "name": "댓글 등록 버튼 클릭", "action": "click", "target": "button:has-text('댓글 등록')" }, { "step": 37, "name": "댓글 수 업데이트 확인 (0 → 1)", "action": "verify_text", "target": "h4:has-text('댓글')", "verification": { "text_contains": "댓글 (1)" } }, { "step": 38, "name": "두 번째 댓글 입력", "action": "fill", "target": "textarea[placeholder*='댓글']", "value": "두 번째 테스트 댓글입니다." }, { "step": 39, "name": "두 번째 댓글 등록 및 댓글 수 확인 (1 → 2)", "action": "click_and_verify", "target": "button:has-text('댓글 등록')", "verification": { "text_contains": "댓글 (2)" } }, { "step": 40, "name": "첫 번째 댓글 수정 버튼 클릭", "action": "click", "target": "button:has-text('수정'):first" }, { "step": 41, "name": "댓글 수정 폼 표시 확인", "action": "verify_element", "target": "textarea", "verification": { "value_contains": "첫 번째 테스트 댓글입니다." } }, { "step": 42, "name": "댓글 내용 수정", "action": "fill", "target": "textarea:first", "value": "수정된 첫 번째 댓글입니다." }, { "step": 43, "name": "댓글 저장 버튼 클릭", "action": "click", "target": "button:has-text('저장')" }, { "step": 44, "name": "수정된 댓글 내용 확인", "action": "verify_text", "expected": "수정된 첫 번째 댓글입니다." }, { "step": 45, "name": "두 번째 댓글 삭제 버튼 클릭", "action": "click", "target": "button:has-text('삭제'):nth(1)" }, { "step": 46, "name": "댓글 삭제 대기 (1초)", "action": "wait", "duration": 1000 }, { "step": 47, "name": "댓글 수 업데이트 확인 (2 → 1)", "action": "verify_text", "target": "h4:has-text('댓글')", "verification": { "text_contains": "댓글 (1)" } }, { "step": 48, "name": "수정 버튼 클릭", "action": "click", "target": "button:has-text('수정'):first", "note": "게시글 수정 버튼" }, { "step": 49, "name": "수정 페이지 URL 확인", "action": "verify_url", "verification": { "url_contains": "?mode=edit" } }, { "step": 50, "name": "수정 폼 기존 데이터 로드 확인 (제목)", "action": "verify_input_value", "target": "#title", "expected": "E2E 테스트 게시글" }, { "step": 51, "name": "수정 폼 기존 데이터 로드 확인 (내용)", "action": "verify_textarea_value", "target": "#content", "expected": "E2E 자동화 테스트를 위한 게시글입니다." }, { "step": 52, "name": "제목 수정", "action": "fill", "target": "#title", "value": "E2E 테스트 게시글 (수정됨)" }, { "step": 53, "name": "내용 수정", "action": "fill", "target": "#content", "value": "수정된 내용입니다. E2E 자동화 테스트를 위한 게시글입니다." }, { "step": 54, "name": "비밀글 체크박스 선택", "action": "check", "target": "#isSecret" }, { "step": 55, "name": "저장 버튼 클릭", "action": "click", "target": "button:has-text('저장')", "validation": "필수 검증 #2 (URL 안정성)" }, { "step": 56, "name": "URL 안정성 검증 - 상세 페이지로 정상 이동", "action": "verify_url_stability", "validation": "필수 검증 #2", "verification": { "url_pattern": "/ko/boards/board_mjsgri54_1fmg/\\d+", "no_error_page": true, "no_404": true, "no_url_change_to_unexpected": true } }, { "step": 57, "name": "수정된 제목 표시 확인", "action": "verify_text", "target": "h4", "expected": "E2E 테스트 게시글 (수정됨)" }, { "step": 58, "name": "수정된 내용 표시 확인", "action": "verify_content", "expected": "수정된 내용입니다. E2E 자동화 테스트를 위한 게시글입니다." }, { "step": 59, "name": "목록으로 버튼 클릭", "action": "click", "target": "button:has-text('목록으로')" }, { "step": 60, "name": "목록 페이지 URL 확인", "action": "verify_url", "expected": "/ko/boards/board_mjsgri54_1fmg" }, { "step": 61, "name": "목록에서 수정된 게시글 확인", "action": "verify_table_row", "verification": { "contains": "E2E 테스트 게시글 (수정됨)" } }, { "step": 62, "name": "게시글 클릭하여 상세 페이지 재진입", "action": "click", "target": "row:has-text('E2E 테스트 게시글 (수정됨)')" }, { "step": 63, "name": "삭제 버튼 클릭", "action": "click", "target": "button:has-text('삭제'):first" }, { "step": 64, "name": "삭제 확인 다이얼로그 표시 확인", "action": "verify_dialog", "verification": { "title": "게시글 삭제", "content_contains": "삭제하시겠습니까" } }, { "step": 65, "name": "삭제 확인 버튼 클릭", "action": "click", "target": "button:has-text('삭제'):last", "validation": "필수 검증 #2 (URL 안정성)" }, { "step": 66, "name": "페이지 이동 대기 (1초)", "action": "wait", "duration": 1000 }, { "step": 67, "name": "URL 안정성 검증 - 목록 페이지로 정상 이동", "action": "verify_url_stability", "validation": "필수 검증 #2", "verification": { "url": "/ko/boards/board_mjsgri54_1fmg", "no_error_page": true, "no_404": true } }, { "step": 68, "name": "게시글 삭제 확인 (목록에서 제거됨)", "action": "verify_table_not_contains", "target": "E2E 테스트 게시글 (수정됨)" }, { "step": 69, "name": "게시글 수 감소 확인", "action": "verify_count_decreased", "verification": { "previous": "initial_post_count + 1", "current": "initial_post_count" } }, { "step": 70, "name": "테이블 구조 유지 확인", "action": "verify_table_structure", "verification": { "columns": ["No.", "제목", "작성자", "조회수", "상태", "등록일"] } }, { "step": 71, "name": "검색창 기능 유지 확인", "action": "verify_element", "target": "input[placeholder*='제목']", "verification": { "exists": true, "enabled": true } }, { "step": 72, "name": "페이지네이션 확인 (조건부)", "action": "verify_pagination", "condition": "post_count >= 10", "verification": { "exists": true } }, { "step": 73, "name": "전체 선택 체크박스 확인", "action": "verify_element", "target": "table th checkbox", "verification": { "exists": true, "enabled": true } }, { "step": 74, "name": "글쓰기 버튼 확인", "action": "verify_element", "target": "button:has-text('글쓰기')", "verification": { "exists": true, "enabled": true } }, { "step": 75, "name": "필터 드롭다운 확인", "action": "verify_element", "target": "[role='combobox']", "verification": { "count": 2 } }, { "step": 76, "name": "콘솔 에러 확인", "action": "check_console_errors", "validation": "필수 검증", "verification": { "no_errors": true } }, { "step": 77, "name": "테스트 완료 확인", "action": "verify_test_completion", "verification": { "all_steps_passed": true } } ], "expectedAPIs": [ { "method": "GET", "endpoint": "/api/v1/boards/{boardCode}", "description": "게시판 정보 조회" }, { "method": "GET", "endpoint": "/api/v1/boards/board_mjsgri54_1fmg/posts", "description": "게시글 목록 조회" }, { "method": "POST", "endpoint": "/api/v1/boards/board_mjsgri54_1fmg/posts", "description": "게시글 생성" }, { "method": "GET", "endpoint": "/api/v1/boards/board_mjsgri54_1fmg/posts/{id}", "description": "게시글 상세 조회" }, { "method": "PUT", "endpoint": "/api/v1/boards/board_mjsgri54_1fmg/posts/{id}", "description": "게시글 수정" }, { "method": "DELETE", "endpoint": "/api/v1/boards/board_mjsgri54_1fmg/posts/{id}", "description": "게시글 삭제" }, { "method": "GET", "endpoint": "/api/v1/boards/board_mjsgri54_1fmg/posts/{id}/comments", "description": "댓글 목록 조회" }, { "method": "POST", "endpoint": "/api/v1/boards/board_mjsgri54_1fmg/posts/{id}/comments", "description": "댓글 생성" }, { "method": "PUT", "endpoint": "/api/v1/boards/board_mjsgri54_1fmg/posts/{id}/comments/{commentId}", "description": "댓글 수정" }, { "method": "DELETE", "endpoint": "/api/v1/boards/board_mjsgri54_1fmg/posts/{id}/comments/{commentId}", "description": "댓글 삭제" } ], "notes": [ "게시판 코드: board_mjsgri54_1fmg", "자유게시판과 동일한 DynamicBoard 시스템 사용", "게시판 등록/수정/삭제 시 반드시 URL 안정성 검증 수행", "댓글 CRUD는 게시글 상세 페이지에서 수행", "IntegratedListTemplateV2 템플릿 사용으로 반응형 디자인", "검색은 게시판명, 작성자명 모두 포함", "상태 필터: 전체, 게시됨, 임시저장", "정렬 옵션: 최신순, 오래된순", "페이지당 10개 게시글 표시 (ITEMS_PER_PAGE = 10)" ] }