Files
sam-scenarios/free-board.json

787 lines
21 KiB
JSON
Raw Normal View History

{
"id": "free-board",
"name": "자유게시판 E2E 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "자유게시판의 목록, 게시글 작성, 상세, 수정, 삭제, 댓글 CRUD 전체 워크플로우 테스트",
"url": "/ko/boards/free",
"navigation": {
"targetUrl": "/boards/free",
"urlPattern": "/boards/free|/ko/boards/free",
"menuHints": ["자유게시판", "자유 게시판", "게시판"]
},
"menuNavigation": {
"level1": "게시판",
"level2": "자유게시판",
"expectedUrl": "/ko/boards/free"
},
"menuNavigationEnhanced": {
"strategy": "scroll-and-search",
"level1": {
"text": "게시판",
"scrollContainer": ".sidebar-scroll, [data-sidebar='content'], nav",
"maxScrollAttempts": 5,
"scrollStep": 200
},
"level2": {
"text": "자유게시판",
"waitAfterLevel1Click": 500
},
"expectedUrl": "/ko/boards/free",
"fallbackUrl": "/ko/boards/free"
},
"steps": [
{
"step": 0,
"name": "사이드바 메뉴 전체 펼치기",
"description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비",
"actions": [
{
"type": "evaluate",
"script": "document.querySelector('.sidebar-scroll, [data-sidebar=\"content\"], nav')?.scrollTo({top: 0, behavior: 'instant'})"
},
{ "type": "wait", "duration": 300 },
{
"type": "evaluate",
"script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()"
},
{
"type": "wait",
"duration": 2000
}
]
},
{
"step": 1,
"name": "2단계 메뉴 진입: 게시판 > 자유게시판",
"description": "게시판 > 자유게시판 메뉴로 이동하여 페이지 로드 확인 (scrollAndFind 패턴)",
"actions": [
{
"type": "scrollAndFind",
"target": "게시판",
"container": ".sidebar-scroll, [data-sidebar='content'], nav",
"maxAttempts": 5,
"scrollStep": 200
},
{
"type": "click",
"target": "게시판"
},
{
"type": "wait",
"duration": 500
},
{
"type": "click",
"target": "자유게시판"
},
{
"type": "wait",
"target": "페이지 로드 완료"
}
],
"verification": {
"title_contains": "자유게시판",
"elements": ["table", "button:has-text('글쓰기')"]
}
},
{
"step": 2,
"name": "초기 게시글 목록 확인",
"action": "verify_table_structure",
"verification": {
"columns": ["No.", "제목", "작성자", "조회수", "상태", "등록일"],
"min_rows": 0
}
},
{
"step": 3,
"name": "게시글 총 건수 확인",
"action": "verify_text",
"verification": {
"text_pattern": "총 \\d+건"
}
},
{
"step": 4,
"name": "검색 기능 확인 (검색창 존재)",
"action": "verify_element",
"target": "input[placeholder*='제목']",
"verification": {
"exists": true
}
},
{
"step": 5,
"name": "필터 드롭다운 확인 (상태)",
"action": "verify_element",
"target": "select, [role='combobox']:has-text('상태')",
"verification": {
"exists": true
}
},
{
"step": 6,
"name": "정렬 드롭다운 확인",
"action": "verify_element",
"target": "select, [role='combobox']:has-text('최신순')",
"verification": {
"exists": true
}
},
{
"step": 7,
"name": "날짜 범위 선택기 확인",
"action": "verify_element",
"target": "input[type='date']",
"verification": {
"count": 2
}
},
{
"step": 8,
"name": "검색 테스트 (제목)",
"action": "fill_and_wait",
"target": "input[placeholder*='제목']",
"value": "테스트",
"verification": {
"wait_for_data_change": true
}
},
{
"step": 9,
"name": "검색 결과 확인",
"action": "verify_table_data",
"verification": {
"filtered": true
}
},
{
"step": 10,
"name": "검색어 초기화",
"action": "fill",
"target": "input[placeholder*='제목']",
"value": ""
},
{
"step": 11,
"name": "상태 필터 테스트 (게시됨)",
"action": "select_dropdown",
"target": "[role='combobox']:has-text('전체')",
"value": "게시됨",
"verification": {
"wait_for_data_change": true
}
},
{
"step": 12,
"name": "상태 필터 초기화 (전체)",
"action": "select_dropdown",
"target": "[role='combobox']",
"value": "전체"
},
{
"step": 13,
"name": "정렬 변경 (오래된순)",
"action": "select_dropdown",
"target": "[role='combobox']:has-text('최신순')",
"value": "오래된순",
"verification": {
"wait_for_data_change": true
}
},
{
"step": 14,
"name": "정렬 복원 (최신순)",
"action": "select_dropdown",
"target": "[role='combobox']",
"value": "최신순"
},
{
"step": 15,
"name": "글쓰기 버튼 클릭",
"action": "click",
"target": "button:has-text('글쓰기')"
},
{
"step": 16,
"name": "게시글 작성 페이지 진입 확인",
"action": "verify_url",
"verification": {
"url_pattern": "/ko/boards/free/create",
"title_contains": "자유게시판"
}
},
{
"step": 17,
"name": "제목 필드 확인",
"action": "verify_element",
"target": "input#title",
"verification": {
"exists": true,
"required": true
}
},
{
"step": 18,
"name": "내용 필드 확인",
"action": "verify_element",
"target": "textarea#content",
"verification": {
"exists": true,
"required": true
}
},
{
"step": 19,
"name": "비밀글 체크박스 확인",
"action": "verify_element",
"target": "input#isSecret",
"verification": {
"exists": true
}
},
{
"step": 20,
"name": "게시글 제목 입력",
"action": "fill",
"target": "input#title",
"value": "E2E 테스트 게시글"
},
{
"step": 21,
"name": "게시글 내용 입력",
"action": "fill",
"target": "textarea#content",
"value": "이것은 E2E 자동화 테스트를 위한 게시글입니다."
},
{
"step": 22,
"name": "현재 URL 저장 (등록 전)",
"action": "save_url",
"variable": "url_before_submit"
},
{
"step": 23,
"name": "게시글 등록 버튼 클릭",
"action": "click",
"target": "button:has-text('등록')"
},
{
"step": 24,
"name": "게시글 등록 완료 (URL 안정성 검증)",
"action": "verify_url_stability",
"critical": true,
"verification": {
"expected_url_pattern": "/ko/boards/free/\\d+",
"no_404": true,
"no_error_page": true,
"success_condition": "url_changed_to_detail"
},
"notes": "필수 검증 #2: 등록 후 상세 페이지로 이동해야 하며 404 에러 페이지가 나오면 안 됨"
},
{
"step": 25,
"name": "게시글 상세 페이지 진입 확인",
"action": "verify_page",
"verification": {
"title": "E2E 테스트 게시글",
"content_contains": "E2E 자동화 테스트"
}
},
{
"step": 26,
"name": "게시글 ID 저장",
"action": "extract_from_url",
"pattern": "/ko/boards/free/(\\d+)",
"variable": "post_id"
},
{
"step": 27,
"name": "작성자 정보 표시 확인",
"action": "verify_element",
"target": "text=/\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}/",
"verification": {
"exists": true
}
},
{
"step": 28,
"name": "조회수 표시 확인",
"action": "verify_element",
"target": "svg.lucide-eye",
"verification": {
"exists": true
}
},
{
"step": 29,
"name": "수정 버튼 존재 확인 (작성자)",
"action": "verify_element",
"target": "button:has-text('수정')",
"verification": {
"exists": true
}
},
{
"step": 30,
"name": "삭제 버튼 존재 확인 (작성자)",
"action": "verify_element",
"target": "button:has-text('삭제')",
"verification": {
"exists": true
}
},
{
"step": 31,
"name": "댓글 섹션 확인",
"action": "verify_element",
"target": "text=/댓글 \\(\\d+\\)/",
"verification": {
"exists": true
}
},
{
"step": 32,
"name": "댓글 입력란 확인",
"action": "verify_element",
"target": "textarea[placeholder*='댓글']",
"verification": {
"exists": true
}
},
{
"step": 33,
"name": "첫 번째 댓글 작성",
"action": "fill",
"target": "textarea[placeholder*='댓글']",
"value": "첫 번째 테스트 댓글입니다."
},
{
"step": 34,
"name": "댓글 등록 버튼 클릭",
"action": "click",
"target": "button:has-text('댓글 등록')"
},
{
"step": 35,
"name": "댓글 등록 확인",
"action": "verify_text",
"verification": {
"text": "첫 번째 테스트 댓글입니다.",
"exists": true
}
},
{
"step": 36,
"name": "댓글 수 업데이트 확인",
"action": "verify_element",
"target": "text=/댓글 \\(1\\)/",
"verification": {
"exists": true
}
},
{
"step": 37,
"name": "두 번째 댓글 작성",
"action": "fill",
"target": "textarea[placeholder*='댓글']",
"value": "두 번째 테스트 댓글입니다."
},
{
"step": 38,
"name": "두 번째 댓글 등록",
"action": "click",
"target": "button:has-text('댓글 등록')"
},
{
"step": 39,
"name": "댓글 수 업데이트 확인 (2개)",
"action": "verify_element",
"target": "text=/댓글 \\(2\\)/",
"verification": {
"exists": true
}
},
{
"step": 40,
"name": "댓글 수정 버튼 클릭 (첫 번째 댓글)",
"action": "click_nth",
"target": "button:has-text('수정')",
"nth": 0
},
{
"step": 41,
"name": "댓글 수정 입력란 확인",
"action": "verify_element",
"target": "textarea",
"verification": {
"count": 2,
"notes": "댓글 입력란 + 수정 입력란"
}
},
{
"step": 42,
"name": "댓글 내용 수정",
"action": "fill_nth",
"target": "textarea",
"nth": 0,
"value": "수정된 첫 번째 댓글입니다."
},
{
"step": 43,
"name": "댓글 수정 저장",
"action": "click",
"target": "button:has-text('저장')"
},
{
"step": 44,
"name": "댓글 수정 확인",
"action": "verify_text",
"verification": {
"text": "수정된 첫 번째 댓글입니다.",
"exists": true
}
},
{
"step": 45,
"name": "댓글 삭제 버튼 클릭 (두 번째 댓글)",
"action": "click_nth",
"target": "button:has-text('삭제')",
"nth": 1
},
{
"step": 46,
"name": "댓글 삭제 확인",
"action": "verify_text",
"verification": {
"text": "두 번째 테스트 댓글입니다.",
"exists": false
}
},
{
"step": 47,
"name": "댓글 수 업데이트 확인 (1개)",
"action": "verify_element",
"target": "text=/댓글 \\(1\\)/",
"verification": {
"exists": true
}
},
{
"step": 48,
"name": "게시글 수정 버튼 클릭",
"action": "click",
"target": "button:has-text('수정')"
},
{
"step": 49,
"name": "게시글 수정 페이지 진입 확인",
"action": "verify_url",
"verification": {
"url_pattern": "/ko/boards/free/\\d+\\?mode=edit"
}
},
{
"step": 50,
"name": "제목 필드에 기존 값 확인",
"action": "verify_input_value",
"target": "input#title",
"verification": {
"value": "E2E 테스트 게시글"
}
},
{
"step": 51,
"name": "제목 수정",
"action": "fill",
"target": "input#title",
"value": "E2E 테스트 게시글 (수정됨)"
},
{
"step": 52,
"name": "내용 수정",
"action": "fill",
"target": "textarea#content",
"value": "수정된 내용입니다. E2E 자동화 테스트를 위한 게시글입니다."
},
{
"step": 53,
"name": "비밀글 체크",
"action": "check",
"target": "input#isSecret"
},
{
"step": 54,
"name": "현재 URL 저장 (수정 전)",
"action": "save_url",
"variable": "url_before_update"
},
{
"step": 55,
"name": "수정 저장 버튼 클릭",
"action": "click",
"target": "button[type='submit']:has-text('저장'), button:has-text('수정')"
},
{
"step": 56,
"name": "게시글 수정 완료 (URL 안정성 검증)",
"action": "verify_url_stability",
"critical": true,
"verification": {
"expected_url_pattern": "/ko/boards/free/\\d+",
"no_404": true,
"no_error_page": true,
"success_condition": "url_back_to_detail"
},
"notes": "필수 검증 #2: 수정 후 상세 페이지로 돌아가야 하며 404 에러 페이지가 나오면 안 됨"
},
{
"step": 57,
"name": "수정된 제목 확인",
"action": "verify_text",
"verification": {
"text": "E2E 테스트 게시글 (수정됨)",
"exists": true
}
},
{
"step": 58,
"name": "수정된 내용 확인",
"action": "verify_text",
"verification": {
"text": "수정된 내용입니다",
"exists": true
}
},
{
"step": 59,
"name": "목록으로 이동 버튼 클릭",
"action": "click",
"target": "button:has-text('목록으로')"
},
{
"step": 60,
"name": "목록 페이지 복귀 확인",
"action": "verify_url",
"verification": {
"url": "/ko/boards/free"
}
},
{
"step": 61,
"name": "수정된 게시글 목록 확인",
"action": "verify_text",
"verification": {
"text": "E2E 테스트 게시글 (수정됨)",
"exists": true
}
},
{
"step": 62,
"name": "게시글 클릭하여 상세 진입",
"action": "click",
"target": "text=E2E 테스트 게시글 (수정됨)"
},
{
"step": 63,
"name": "상세 페이지 진입 확인",
"action": "verify_url",
"verification": {
"url_pattern": "/ko/boards/free/\\d+"
}
},
{
"step": 64,
"name": "조회수 증가 확인",
"action": "verify_element",
"target": "svg.lucide-eye ~ text",
"verification": {
"text_pattern": "\\d+",
"notes": "조회수가 표시되는지 확인"
}
},
{
"step": 65,
"name": "게시글 삭제 버튼 클릭",
"action": "click",
"target": "button:has-text('삭제')"
},
{
"step": 66,
"name": "삭제 확인 다이얼로그 표시 확인",
"action": "verify_dialog",
"verification": {
"title": "게시글 삭제",
"content_contains": "삭제하시겠습니까"
}
},
{
"step": 67,
"name": "현재 URL 저장 (삭제 전)",
"action": "save_url",
"variable": "url_before_delete"
},
{
"step": 68,
"name": "삭제 확인 버튼 클릭",
"action": "click",
"target": "button:has-text('삭제'):last-of-type"
},
{
"step": 69,
"name": "게시글 삭제 완료 (URL 안정성 검증)",
"action": "verify_url_stability",
"critical": true,
"verification": {
"expected_url": "/ko/boards/free",
"no_404": true,
"no_error_page": true,
"success_condition": "url_back_to_list"
},
"notes": "필수 검증 #2: 삭제 후 목록 페이지로 돌아가야 하며 404 에러 페이지가 나오면 안 됨"
},
{
"step": 70,
"name": "목록 페이지 복귀 확인",
"action": "verify_url",
"verification": {
"url": "/ko/boards/free"
}
},
{
"step": 71,
"name": "삭제된 게시글 목록에서 제거 확인",
"action": "verify_text",
"verification": {
"text": "E2E 테스트 게시글 (수정됨)",
"exists": false
}
},
{
"step": 72,
"name": "페이지네이션 존재 확인 (조건부)",
"action": "verify_element",
"target": "nav[aria-label='pagination']",
"verification": {
"exists": "if_data_gt_10",
"notes": "10개 이상일 때만 페이지네이션 표시"
}
},
{
"step": 73,
"name": "체크박스 선택 테스트 (첫 번째 항목)",
"action": "check_nth",
"target": "input[type='checkbox']",
"nth": 1,
"verification": {
"notes": "0번은 전체 선택, 1번은 첫 번째 데이터"
}
},
{
"step": 74,
"name": "체크박스 선택 해제",
"action": "uncheck_nth",
"target": "input[type='checkbox']",
"nth": 1
},
{
"step": 75,
"name": "전체 선택 체크박스 클릭",
"action": "check_nth",
"target": "input[type='checkbox']",
"nth": 0
},
{
"step": 76,
"name": "전체 선택 해제",
"action": "uncheck_nth",
"target": "input[type='checkbox']",
"nth": 0
},
{
"step": 77,
"name": "콘솔 에러 확인",
"action": "verify_console",
"verification": {
"no_errors": true
}
}
],
"expectedAPIs": [
{
"endpoint": "GET /api/v1/boards/free",
"description": "자유게시판 정보 조회"
},
{
"endpoint": "GET /api/v1/boards/free/posts",
"description": "게시글 목록 조회 (per_page=100)"
},
{
"endpoint": "POST /api/v1/boards/free/posts",
"description": "게시글 등록",
"payload": {
"title": "string",
"content": "string",
"is_secret": "boolean"
}
},
{
"endpoint": "GET /api/v1/boards/free/posts/{id}",
"description": "게시글 상세 조회 (조회수 증가)"
},
{
"endpoint": "GET /api/v1/boards/free/posts/{id}/comments",
"description": "댓글 목록 조회"
},
{
"endpoint": "POST /api/v1/boards/free/posts/{id}/comments",
"description": "댓글 등록",
"payload": {
"content": "string"
}
},
{
"endpoint": "PUT /api/v1/boards/free/posts/{id}/comments/{commentId}",
"description": "댓글 수정",
"payload": {
"content": "string"
}
},
{
"endpoint": "DELETE /api/v1/boards/free/posts/{id}/comments/{commentId}",
"description": "댓글 삭제"
},
{
"endpoint": "PUT /api/v1/boards/free/posts/{id}",
"description": "게시글 수정",
"payload": {
"title": "string",
"content": "string",
"is_secret": "boolean"
}
},
{
"endpoint": "DELETE /api/v1/boards/free/posts/{id}",
"description": "게시글 삭제"
}
],
"notes": [
"자유게시판은 boardCode='free'를 사용하는 동적 게시판입니다.",
"게시글 등록/수정/삭제 시 반드시 URL 안정성 검증 수행 (필수 검증 #2)",
"댓글 CRUD 기능 모두 테스트해야 합니다.",
"IntegratedListTemplateV2 템플릿 사용으로 반응형 디자인 (데스크톱/모바일)",
"페이지네이션은 10개 단위로 동작 (10개 미만 시 미표시)",
"검색은 제목, 작성자명으로 필터링됩니다.",
"상태 필터: 전체, 게시됨, 임시저장",
"정렬: 최신순, 오래된순",
"조회수는 게시글 상세 조회 시마다 증가합니다.",
"작성자만 수정/삭제 버튼이 표시됩니다.",
"댓글도 작성자만 수정/삭제 가능합니다.",
"비밀글 체크 시 is_secret=true로 전송됩니다.",
"게시글 내용은 HTML로 저장되며 dangerouslySetInnerHTML로 렌더링됩니다."
]
}