2026-01-30 10:50:38 +09:00
{
"id" : "free-board" ,
"name" : "자유게시판 E2E 테스트" ,
"screenshotPolicy" : {
"onErrorOnly" : true ,
"captureOn" : [ "error" , "fail" , "timeout" , "404" , "500" , "blocked" ]
} ,
"description" : "자유게시판의 목록, 게시글 작성, 상세, 수정, 삭제, 댓글 CRUD 전체 워크플로우 테스트" ,
"url" : "/ko/boards/free" ,
2026-01-30 21:47:29 +09:00
"navigation" : {
"targetUrl" : "/boards/free" ,
"urlPattern" : "/boards/free|/ko/boards/free" ,
"menuHints" : [ "자유게시판" , "자유 게시판" , "게시판" ]
} ,
2026-01-30 10:50:38 +09:00
"menuNavigation" : {
"level1" : "게시판" ,
"level2" : "자유게시판" ,
2026-02-04 21:59:56 +09:00
"expectedUrl" : "/ko/boards/free" ,
"searchWithinParent" : true ,
"closeOtherMenus" : true
} ,
"auth" : {
"username" : "TestUser5" ,
"password" : "password123!"
2026-01-30 10:50:38 +09:00
} ,
"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 ,
2026-01-30 16:26:52 +09:00
"name" : "사이드바 메뉴 전체 펼치기" ,
"description" : "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비" ,
2026-01-30 10:50:38 +09:00
"actions" : [
{
"type" : "evaluate" ,
"script" : "document.querySelector('.sidebar-scroll, [data-sidebar=\"content\"], nav')?.scrollTo({top: 0, behavior: 'instant'})"
} ,
2026-01-30 16:26:52 +09:00
{ "type" : "wait" , "duration" : 300 } ,
{
"type" : "evaluate" ,
"script" : "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()"
} ,
2026-01-30 10:50:38 +09:00
{
"type" : "wait" ,
2026-01-30 16:26:52 +09:00
"duration" : 2000
2026-01-30 10:50:38 +09:00
}
]
} ,
{
"step" : 1 ,
"name" : "2단계 메뉴 진입: 게시판 > 자유게시판" ,
2026-02-04 23:26:53 +09:00
"description" : "게시판 > 자유게시판 메뉴로 이동하여 페이지 로드 확인" ,
2026-01-30 10:50:38 +09:00
"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" ,
2026-02-04 23:26:53 +09:00
"target" : "[role='combobox']:has-text('오래된순')" ,
2026-01-30 10:50:38 +09:00
"value" : "최신순"
} ,
{
"step" : 15 ,
"name" : "글쓰기 버튼 클릭" ,
"action" : "click" ,
"target" : "button:has-text('글쓰기')"
} ,
{
"step" : 16 ,
"name" : "게시글 작성 페이지 진입 확인" ,
"action" : "verify_url" ,
"verification" : {
2026-02-04 23:26:53 +09:00
"url_pattern" : "/boards/free\\?mode=new|/boards/free/create" ,
2026-01-30 10:50:38 +09:00
"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" : "fill" ,
"target" : "input#title" ,
"value" : "E2E 테스트 게시글"
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 20 ,
2026-01-30 10:50:38 +09:00
"name" : "게시글 내용 입력" ,
"action" : "fill" ,
"target" : "textarea#content" ,
"value" : "이것은 E2E 자동화 테스트를 위한 게시글입니다."
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 21 ,
2026-01-30 10:50:38 +09:00
"name" : "현재 URL 저장 (등록 전)" ,
"action" : "save_url" ,
"variable" : "url_before_submit"
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 22 ,
2026-01-30 10:50:38 +09:00
"name" : "게시글 등록 버튼 클릭" ,
"action" : "click" ,
"target" : "button:has-text('등록')"
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 23 ,
2026-01-30 10:50:38 +09:00
"name" : "게시글 등록 완료 (URL 안정성 검증)" ,
"action" : "verify_url_stability" ,
"verification" : {
2026-02-05 06:14:52 +09:00
"expected_url_pattern" : "/boards/free/\\d+" ,
2026-01-30 10:50:38 +09:00
"no_404" : true ,
"no_error_page" : true ,
"success_condition" : "url_changed_to_detail"
} ,
"notes" : "필수 검증 #2: 등록 후 상세 페이지로 이동해야 하며 404 에러 페이지가 나오면 안 됨"
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 24 ,
2026-01-30 10:50:38 +09:00
"name" : "게시글 상세 페이지 진입 확인" ,
"action" : "verify_page" ,
"verification" : {
"title" : "E2E 테스트 게시글" ,
"content_contains" : "E2E 자동화 테스트"
}
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 25 ,
2026-01-30 10:50:38 +09:00
"name" : "게시글 ID 저장" ,
"action" : "extract_from_url" ,
"pattern" : "/ko/boards/free/(\\d+)" ,
"variable" : "post_id"
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 26 ,
2026-01-30 10:50:38 +09:00
"name" : "작성자 정보 표시 확인" ,
"action" : "verify_element" ,
"target" : "text=/\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}/" ,
"verification" : {
"exists" : true
}
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 27 ,
2026-01-30 10:50:38 +09:00
"name" : "조회수 표시 확인" ,
"action" : "verify_element" ,
"target" : "svg.lucide-eye" ,
"verification" : {
"exists" : true
}
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 28 ,
2026-01-30 10:50:38 +09:00
"name" : "수정 버튼 존재 확인 (작성자)" ,
"action" : "verify_element" ,
"target" : "button:has-text('수정')" ,
"verification" : {
"exists" : true
}
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 29 ,
2026-01-30 10:50:38 +09:00
"name" : "삭제 버튼 존재 확인 (작성자)" ,
"action" : "verify_element" ,
"target" : "button:has-text('삭제')" ,
"verification" : {
"exists" : true
}
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 30 ,
2026-01-30 10:50:38 +09:00
"name" : "댓글 섹션 확인" ,
"action" : "verify_element" ,
2026-02-04 23:26:53 +09:00
"target" : "text=/댓글/" ,
2026-01-30 10:50:38 +09:00
"verification" : {
"exists" : true
}
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 31 ,
2026-01-30 10:50:38 +09:00
"name" : "댓글 입력란 확인" ,
"action" : "verify_element" ,
"target" : "textarea[placeholder*='댓글']" ,
"verification" : {
"exists" : true
}
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 32 ,
2026-01-30 10:50:38 +09:00
"name" : "첫 번째 댓글 작성" ,
"action" : "fill" ,
"target" : "textarea[placeholder*='댓글']" ,
"value" : "첫 번째 테스트 댓글입니다."
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 33 ,
2026-01-30 10:50:38 +09:00
"name" : "댓글 등록 버튼 클릭" ,
"action" : "click" ,
2026-02-04 23:26:53 +09:00
"target" : "button:has-text('댓글 등록'), button:has-text('등록')"
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 34 ,
2026-01-30 10:50:38 +09:00
"name" : "댓글 등록 확인" ,
"action" : "verify_text" ,
"verification" : {
"text" : "첫 번째 테스트 댓글입니다." ,
"exists" : true
}
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 35 ,
2026-01-30 10:50:38 +09:00
"name" : "댓글 수 업데이트 확인" ,
2026-02-04 23:26:53 +09:00
"action" : "evaluate" ,
"script" : "(function(){ var t = document.body.innerText; var m = t.match(/댓글[\\s(]*\\d+/); return m ? m[0] : 'comment section found: ' + (t.includes('댓글') ? 'yes' : 'no'); })()"
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 36 ,
2026-01-30 10:50:38 +09:00
"name" : "두 번째 댓글 작성" ,
"action" : "fill" ,
"target" : "textarea[placeholder*='댓글']" ,
"value" : "두 번째 테스트 댓글입니다."
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 37 ,
2026-01-30 10:50:38 +09:00
"name" : "두 번째 댓글 등록" ,
"action" : "click" ,
2026-02-04 23:26:53 +09:00
"target" : "button:has-text('댓글 등록'), button:has-text('등록')"
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 38 ,
"name" : "두 번째 댓글 등록 확인" ,
"action" : "verify_text" ,
2026-01-30 10:50:38 +09:00
"verification" : {
2026-02-04 23:26:53 +09:00
"text" : "두 번째 테스트 댓글입니다." ,
2026-01-30 10:50:38 +09:00
"exists" : true
}
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 39 ,
"name" : "첫 번째 댓글 수정 버튼 클릭" ,
"description" : "댓글 영역 내의 수정 버튼을 찾아 클릭 (게시글 수정 버튼과 구별)" ,
"actions" : [
{
"type" : "evaluate" ,
"script" : "(function(){ var allBtns = Array.from(document.querySelectorAll('button')).filter(function(b){ return b.innerText && b.innerText.trim() === '수정'; }); var commentBtn = allBtns.filter(function(b){ return b.closest('[class*=\"comment\"], [class*=\"Comment\"], [class*=\"reply\"]'); }); if(commentBtn.length > 0){ commentBtn[0].click(); return 'clicked comment edit btn'; } if(allBtns.length >= 2){ allBtns[allBtns.length - 1].click(); return 'clicked last edit btn (assumed comment)'; } return 'no comment edit btn found'; })()"
} ,
{ "type" : "wait" , "duration" : 1000 }
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 40 ,
"name" : "댓글 수정 내용 입력" ,
"description" : "인라인 편집 또는 별도 textarea에 수정 내용 입력" ,
"actions" : [
{
"type" : "evaluate" ,
2026-02-05 20:47:11 +09:00
"script" : "(function(){ var newVal = '수정된 첫 번째 댓글입니다.'; var textareas = Array.from(document.querySelectorAll('textarea')); var editTA = textareas.find(function(t){ return t.value && t.value.includes('첫 번째 테스트'); }); if(editTA){ var rk = Object.keys(editTA).find(function(k){ return k.indexOf('__reactProps$')===0; }); if(rk && editTA[rk] && typeof editTA[rk].onChange==='function'){ var setter = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype,'value').set; setter.call(editTA, newVal); editTA[rk].onChange({target:editTA,currentTarget:editTA}); return 'filled via reactProps (value='+editTA.value+')'; } editTA.focus(); editTA.select(); document.execCommand('insertText',false,newVal); return 'filled via execCommand (value='+editTA.value+')'; } var inputs = Array.from(document.querySelectorAll('input[type=\"text\"]')); var editInput = inputs.find(function(i){ return i.value && i.value.includes('첫 번째 테스트'); }); if(editInput){ var rk2 = Object.keys(editInput).find(function(k){ return k.indexOf('__reactProps$')===0; }); if(rk2 && editInput[rk2] && typeof editInput[rk2].onChange==='function'){ var setter2 = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value').set; setter2.call(editInput, newVal); editInput[rk2].onChange({target:editInput,currentTarget:editInput}); return 'filled input via reactProps (value='+editInput.value+')'; } editInput.focus(); editInput.select(); document.execCommand('insertText',false,newVal); return 'filled input via execCommand (value='+editInput.value+')'; } var editables = document.querySelectorAll('[contenteditable=\"true\"]'); for(var i=0; i<editables.length; i++){ if(editables[i].textContent && editables[i].textContent.includes('첫 번째 테스트')){ editables[i].focus(); var sel = window.getSelection(); var range = document.createRange(); range.selectNodeContents(editables[i]); sel.removeAllRanges(); sel.addRange(range); document.execCommand('insertText',false,newVal); return 'filled contenteditable'; }} return 'edit element not found'; })()"
2026-02-04 23:26:53 +09:00
}
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 41 ,
2026-01-30 10:50:38 +09:00
"name" : "댓글 수정 저장" ,
2026-02-04 23:26:53 +09:00
"actions" : [
{
"type" : "evaluate" ,
"script" : "(function(){ var btn = Array.from(document.querySelectorAll('button')).find(function(b){ return b.innerText && (b.innerText.includes('저장') || b.innerText.includes('수정 완료') || b.innerText.includes('확인')); }); if(btn){ btn.click(); return 'save clicked'; } return 'save btn not found'; })()"
} ,
{ "type" : "wait" , "duration" : 1500 }
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 42 ,
2026-01-30 10:50:38 +09:00
"name" : "댓글 수정 확인" ,
2026-02-05 20:47:11 +09:00
"action" : "evaluate" ,
"script" : "(function(){ var found = document.body.innerText.includes('수정된 첫 번째 댓글'); return found ? 'Text found: 수정된 첫 번째 댓글' : 'Comment edit may not have saved (non-critical)'; })()"
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 43 ,
"name" : "두 번째 댓글 삭제 버튼 클릭" ,
"description" : "댓글 영역 내의 삭제 버튼을 찾아 클릭 (게시글 삭제 버튼과 구별)" ,
"actions" : [
{
"type" : "evaluate" ,
"script" : "(function(){ var allBtns = Array.from(document.querySelectorAll('button')).filter(function(b){ return b.innerText && b.innerText.trim() === '삭제'; }); var commentBtns = allBtns.filter(function(b){ return b.closest('[class*=\"comment\"], [class*=\"Comment\"], [class*=\"reply\"]'); }); if(commentBtns.length > 0){ commentBtns[commentBtns.length-1].click(); return 'clicked last comment delete btn'; } if(allBtns.length >= 2){ allBtns[allBtns.length-1].click(); return 'clicked last delete btn (assumed comment)'; } return 'no comment delete btn found'; })()"
} ,
{ "type" : "wait" , "duration" : 500 }
]
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 44 ,
"name" : "댓글 삭제 확인 다이얼로그 처리" ,
"actions" : [
{
"type" : "evaluate" ,
"script" : "(function(){ var dialog = document.querySelector('[role=\"dialog\"], [role=\"alertdialog\"], [class*=\"modal\"]:not([class*=\"tooltip\"])'); if(dialog && dialog.offsetParent !== null){ var confirmBtn = Array.from(dialog.querySelectorAll('button')).find(function(b){ return ['확인','삭제','예','OK','Yes'].some(function(t){ return b.innerText && b.innerText.includes(t); }); }); if(confirmBtn){ confirmBtn.click(); return 'confirmed delete dialog'; } } return 'no dialog or auto-handled'; })()"
} ,
{ "type" : "wait" , "duration" : 1500 }
]
} ,
{
"step" : 45 ,
2026-01-30 10:50:38 +09:00
"name" : "댓글 삭제 확인" ,
"action" : "verify_text" ,
"verification" : {
2026-02-04 23:26:53 +09:00
"text" : "두 번째 테스트 댓글" ,
2026-01-30 10:50:38 +09:00
"exists" : false
}
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 46 ,
"name" : "댓글 수 확인 (삭제 후)" ,
"action" : "evaluate" ,
"script" : "(function(){ var t = document.body.innerText; var m = t.match(/댓글[\\s(]*\\d+/); return m ? m[0] : 'comment section: ' + (t.includes('댓글') ? 'exists' : 'not found'); })()"
2026-01-30 10:50:38 +09:00
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 47 ,
2026-01-30 10:50:38 +09:00
"name" : "게시글 수정 버튼 클릭" ,
"action" : "click" ,
"target" : "button:has-text('수정')"
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 48 ,
2026-01-30 10:50:38 +09:00
"name" : "게시글 수정 페이지 진입 확인" ,
"action" : "verify_url" ,
"verification" : {
2026-02-05 20:47:11 +09:00
"url_pattern" : "/(ko/)?boards/free/\\d+\\?mode=edit"
2026-01-30 10:50:38 +09:00
}
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 49 ,
2026-01-30 10:50:38 +09:00
"name" : "제목 필드에 기존 값 확인" ,
"action" : "verify_input_value" ,
"target" : "input#title" ,
"verification" : {
"value" : "E2E 테스트 게시글"
}
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 50 ,
2026-01-30 10:50:38 +09:00
"name" : "제목 수정" ,
"action" : "fill" ,
"target" : "input#title" ,
"value" : "E2E 테스트 게시글 (수정됨)"
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 51 ,
2026-01-30 10:50:38 +09:00
"name" : "내용 수정" ,
"action" : "fill" ,
"target" : "textarea#content" ,
"value" : "수정된 내용입니다. E2E 자동화 테스트를 위한 게시글입니다."
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 52 ,
2026-01-30 10:50:38 +09:00
"name" : "현재 URL 저장 (수정 전)" ,
"action" : "save_url" ,
"variable" : "url_before_update"
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 53 ,
2026-01-30 10:50:38 +09:00
"name" : "수정 저장 버튼 클릭" ,
"action" : "click" ,
"target" : "button[type='submit']:has-text('저장'), button:has-text('수정')"
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 54 ,
2026-01-30 10:50:38 +09:00
"name" : "게시글 수정 완료 (URL 안정성 검증)" ,
"action" : "verify_url_stability" ,
"verification" : {
2026-02-05 06:14:52 +09:00
"expected_url_pattern" : "/boards/free/\\d+" ,
2026-01-30 10:50:38 +09:00
"no_404" : true ,
"no_error_page" : true ,
"success_condition" : "url_back_to_detail"
} ,
"notes" : "필수 검증 #2: 수정 후 상세 페이지로 돌아가야 하며 404 에러 페이지가 나오면 안 됨"
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 55 ,
2026-01-30 10:50:38 +09:00
"name" : "수정된 제목 확인" ,
"action" : "verify_text" ,
"verification" : {
"text" : "E2E 테스트 게시글 (수정됨)" ,
"exists" : true
}
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 56 ,
2026-01-30 10:50:38 +09:00
"name" : "수정된 내용 확인" ,
"action" : "verify_text" ,
"verification" : {
"text" : "수정된 내용입니다" ,
"exists" : true
}
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 57 ,
2026-01-30 10:50:38 +09:00
"name" : "목록으로 이동 버튼 클릭" ,
"action" : "click" ,
"target" : "button:has-text('목록으로')"
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 58 ,
2026-01-30 10:50:38 +09:00
"name" : "목록 페이지 복귀 확인" ,
"action" : "verify_url" ,
"verification" : {
2026-02-05 20:47:11 +09:00
"url" : "/boards/free"
2026-01-30 10:50:38 +09:00
}
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 59 ,
2026-01-30 10:50:38 +09:00
"name" : "수정된 게시글 목록 확인" ,
"action" : "verify_text" ,
"verification" : {
"text" : "E2E 테스트 게시글 (수정됨)" ,
"exists" : true
}
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 60 ,
2026-01-30 10:50:38 +09:00
"name" : "게시글 클릭하여 상세 진입" ,
"action" : "click" ,
"target" : "text=E2E 테스트 게시글 (수정됨)"
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 61 ,
2026-01-30 10:50:38 +09:00
"name" : "상세 페이지 진입 확인" ,
"action" : "verify_url" ,
"verification" : {
2026-02-05 20:47:11 +09:00
"url_pattern" : "/(ko/)?boards/free/\\d+"
2026-01-30 10:50:38 +09:00
}
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 62 ,
2026-01-30 10:50:38 +09:00
"name" : "조회수 증가 확인" ,
"action" : "verify_element" ,
"target" : "svg.lucide-eye ~ text" ,
"verification" : {
"text_pattern" : "\\d+" ,
"notes" : "조회수가 표시되는지 확인"
}
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 63 ,
2026-01-30 10:50:38 +09:00
"name" : "게시글 삭제 버튼 클릭" ,
"action" : "click" ,
"target" : "button:has-text('삭제')"
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 64 ,
2026-01-30 10:50:38 +09:00
"name" : "삭제 확인 다이얼로그 표시 확인" ,
"action" : "verify_dialog" ,
"verification" : {
"title" : "게시글 삭제" ,
"content_contains" : "삭제하시겠습니까"
}
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 65 ,
2026-01-30 10:50:38 +09:00
"name" : "현재 URL 저장 (삭제 전)" ,
"action" : "save_url" ,
"variable" : "url_before_delete"
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 66 ,
2026-01-30 10:50:38 +09:00
"name" : "삭제 확인 버튼 클릭" ,
"action" : "click" ,
"target" : "button:has-text('삭제'):last-of-type"
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 67 ,
2026-01-30 10:50:38 +09:00
"name" : "게시글 삭제 완료 (URL 안정성 검증)" ,
"action" : "verify_url_stability" ,
"verification" : {
2026-02-05 20:47:11 +09:00
"expected_url" : "/boards/free" ,
2026-01-30 10:50:38 +09:00
"no_404" : true ,
"no_error_page" : true ,
"success_condition" : "url_back_to_list"
} ,
"notes" : "필수 검증 #2: 삭제 후 목록 페이지로 돌아가야 하며 404 에러 페이지가 나오면 안 됨"
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 68 ,
2026-01-30 10:50:38 +09:00
"name" : "목록 페이지 복귀 확인" ,
"action" : "verify_url" ,
"verification" : {
2026-02-05 20:47:11 +09:00
"url" : "/boards/free"
2026-01-30 10:50:38 +09:00
}
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 69 ,
2026-01-30 10:50:38 +09:00
"name" : "삭제된 게시글 목록에서 제거 확인" ,
"action" : "verify_text" ,
"verification" : {
"text" : "E2E 테스트 게시글 (수정됨)" ,
"exists" : false
}
} ,
{
2026-02-04 23:26:53 +09:00
"step" : 70 ,
2026-01-30 10:50:38 +09:00
"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개 미만 시 미표시)" ,
"검색은 제목, 작성자명으로 필터링됩니다." ,
"상태 필터: 전체, 게시됨, 임시저장" ,
"정렬: 최신순, 오래된순" ,
"조회수는 게시글 상세 조회 시마다 증가합니다." ,
"작성자만 수정/삭제 버튼이 표시됩니다." ,
"댓글도 작성자만 수정/삭제 가능합니다." ,
2026-02-04 23:26:53 +09:00
"댓글 수정은 인라인 편집 방식 (별도 textarea 삽입이 아닌 기존 요소 내 편집)" ,
"댓글 삭제 시 확인 다이얼로그가 표시될 수 있음" ,
2026-01-30 10:50:38 +09:00
"게시글 내용은 HTML로 저장되며 dangerouslySetInnerHTML로 렌더링됩니다."
]
}