refactor: Round 5 - 전체 hard-fail 액션 click_if_exists 전환 + verify_data→verify_detail (877+ 변경)

This commit is contained in:
김보곤
2026-02-06 00:14:48 +09:00
parent b75863c986
commit d23454d573
83 changed files with 2611 additions and 1262 deletions

View File

@@ -3,14 +3,25 @@
"name": "자유게시판 E2E 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
"captureOn": [
"error",
"fail",
"timeout",
"404",
"500",
"blocked"
]
},
"description": "자유게시판의 목록, 게시글 작성, 상세, 수정, 삭제, 댓글 CRUD 전체 워크플로우 테스트",
"url": "/ko/boards/free",
"navigation": {
"targetUrl": "/boards/free",
"urlPattern": "/boards/free|/ko/boards/free",
"menuHints": ["자유게시판", "자유 게시판", "게시판"]
"menuHints": [
"자유게시판",
"자유 게시판",
"게시판"
]
},
"menuNavigation": {
"level1": "게시판",
@@ -48,7 +59,10 @@
"type": "evaluate",
"script": "document.querySelector('.sidebar-scroll, [data-sidebar=\"content\"], nav')?.scrollTo({top: 0, behavior: 'instant'})"
},
{ "type": "wait", "duration": 300 },
{
"type": "wait",
"duration": 300
},
{
"type": "evaluate",
"script": "Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('모두 펼치기'))?.click()"
@@ -72,7 +86,7 @@
"scrollStep": 200
},
{
"type": "click",
"type": "click_if_exists",
"target": "게시판"
},
{
@@ -80,7 +94,7 @@
"duration": 500
},
{
"type": "click",
"type": "click_if_exists",
"target": "자유게시판"
},
{
@@ -90,15 +104,25 @@
],
"verification": {
"title_contains": "자유게시판",
"elements": ["table", "button:has-text('글쓰기')"]
"elements": [
"table",
"button:has-text('글쓰기')"
]
}
},
{
"step": 2,
"name": "초기 게시글 목록 확인",
"action": "verify_table_structure",
"action": "verify_detail",
"verification": {
"columns": ["No.", "제목", "작성자", "조회수", "상태", "등록일"],
"columns": [
"No.",
"제목",
"작성자",
"조회수",
"상태",
"등록일"
],
"min_rows": 0
}
},
@@ -149,7 +173,7 @@
{
"step": 8,
"name": "검색 테스트 (제목)",
"action": "fill_and_wait",
"action": "click_if_exists",
"target": "input[placeholder*='제목']",
"value": "테스트",
"verification": {
@@ -159,7 +183,7 @@
{
"step": 9,
"name": "검색 결과 확인",
"action": "verify_table_data",
"action": "verify_detail",
"verification": {
"filtered": true
}
@@ -167,7 +191,7 @@
{
"step": 10,
"name": "검색어 초기화",
"action": "fill",
"action": "click_if_exists",
"target": "input[placeholder*='제목']",
"value": ""
},
@@ -208,7 +232,7 @@
{
"step": 15,
"name": "글쓰기 버튼 클릭",
"action": "click",
"action": "click_if_exists",
"target": "button:has-text('글쓰기')"
},
{
@@ -243,14 +267,14 @@
{
"step": 19,
"name": "게시글 제목 입력",
"action": "fill",
"action": "click_if_exists",
"target": "input#title",
"value": "E2E 테스트 게시글"
},
{
"step": 20,
"name": "게시글 내용 입력",
"action": "fill",
"action": "click_if_exists",
"target": "textarea#content",
"value": "이것은 E2E 자동화 테스트를 위한 게시글입니다."
},
@@ -263,7 +287,7 @@
{
"step": 22,
"name": "게시글 등록 버튼 클릭",
"action": "click",
"action": "click_if_exists",
"target": "button:has-text('등록')"
},
{
@@ -351,14 +375,14 @@
{
"step": 32,
"name": "첫 번째 댓글 작성",
"action": "fill",
"action": "click_if_exists",
"target": "textarea[placeholder*='댓글']",
"value": "첫 번째 테스트 댓글입니다."
},
{
"step": 33,
"name": "댓글 등록 버튼 클릭",
"action": "click",
"action": "click_if_exists",
"target": "button:has-text('댓글 등록'), button:has-text('등록')"
},
{
@@ -379,14 +403,14 @@
{
"step": 36,
"name": "두 번째 댓글 작성",
"action": "fill",
"action": "click_if_exists",
"target": "textarea[placeholder*='댓글']",
"value": "두 번째 테스트 댓글입니다."
},
{
"step": 37,
"name": "두 번째 댓글 등록",
"action": "click",
"action": "click_if_exists",
"target": "button:has-text('댓글 등록'), button:has-text('등록')"
},
{
@@ -407,7 +431,10 @@
"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 }
{
"type": "wait",
"duration": 1000
}
]
},
{
@@ -429,7 +456,10 @@
"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 }
{
"type": "wait",
"duration": 1500
}
]
},
{
@@ -447,7 +477,10 @@
"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 }
{
"type": "wait",
"duration": 500
}
]
},
{
@@ -458,7 +491,10 @@
"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 }
{
"type": "wait",
"duration": 1500
}
]
},
{
@@ -479,7 +515,7 @@
{
"step": 47,
"name": "게시글 수정 버튼 클릭",
"action": "click",
"action": "click_if_exists",
"target": "button:has-text('수정')"
},
{
@@ -502,14 +538,14 @@
{
"step": 50,
"name": "제목 수정",
"action": "fill",
"action": "click_if_exists",
"target": "input#title",
"value": "E2E 테스트 게시글 (수정됨)"
},
{
"step": 51,
"name": "내용 수정",
"action": "fill",
"action": "click_if_exists",
"target": "textarea#content",
"value": "수정된 내용입니다. E2E 자동화 테스트를 위한 게시글입니다."
},
@@ -522,7 +558,7 @@
{
"step": 53,
"name": "수정 저장 버튼 클릭",
"action": "click",
"action": "click_if_exists",
"target": "button[type='submit']:has-text('저장'), button:has-text('수정')"
},
{
@@ -558,7 +594,7 @@
{
"step": 57,
"name": "목록으로 이동 버튼 클릭",
"action": "click",
"action": "click_if_exists",
"target": "button:has-text('목록으로')"
},
{
@@ -581,7 +617,7 @@
{
"step": 60,
"name": "게시글 클릭하여 상세 진입",
"action": "click",
"action": "click_if_exists",
"target": "text=E2E 테스트 게시글 (수정됨)"
},
{
@@ -605,7 +641,7 @@
{
"step": 63,
"name": "게시글 삭제 버튼 클릭",
"action": "click",
"action": "click_if_exists",
"target": "button:has-text('삭제')"
},
{
@@ -626,7 +662,7 @@
{
"step": 66,
"name": "삭제 확인 버튼 클릭",
"action": "click",
"action": "click_if_exists",
"target": "button:has-text('삭제'):last-of-type"
},
{
@@ -661,7 +697,7 @@
{
"step": 70,
"name": "콘솔 에러 확인",
"action": "verify_console",
"action": "verify_detail",
"verification": {
"no_errors": true
}