refactor: E2E 시나리오 전면 개선 (43파일)

- Phase 0: 미구현 모듈 시나리오 13개 삭제 (구매관리, 중복, 라우트 없음)
- Phase 2: Settings URL 불일치 수정 (position, attendance, vacation-policy, bank-account, account, notification)
- Phase 3-4: 비설정 시나리오 URL/메뉴/UI 수정 (inventory-status, receiving-management, price-management, customer-inquiry, shipment-management, sales-client, quality-certification, customer-notice, production-* 등)
- Phase 5-6: 복잡 시나리오 재작성 (draft-box 50→14스텝, department-add 18→10스텝, free-board 70→22스텝, crud-delete-freeboard 14→17스텝)
- 16개 disabled 시나리오 enabled 전환
- 비표준 액션(fillInModal, randomData, usePlaywrightNative 등) → step-executor 표준 액션으로 통일
This commit is contained in:
김보곤
2026-02-06 17:37:35 +09:00
parent b2509ee2dc
commit 3bc23c5884
43 changed files with 696 additions and 8094 deletions

View File

@@ -1,464 +1,183 @@
{
"enabled": true,
"id": "department-add",
"name": "부서 추가 테스트 (랜덤 + 하위부서)",
"name": "부서 추가 테스트",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "랜덤 상위 부서 생성 후 하위 부서까지 추가하는 고도화된 E2E 테스트",
"description": "인사관리 > 부서관리 메뉴에서 부서 추가/수정/삭제 CRUD 테스트",
"baseUrl": "https://dev.codebridge-x.com",
"url": "/ko/hr/department-management",
"navigation": {
"targetUrl": "/hr/department-management",
"urlPattern": "/hr/department-management|/ko/hr/department-management",
"menuHints": ["부서관리", "부서 관리", "인사관리"]
},
"menuNavigation": {
"level1": "인사관리",
"level2": "부서관리",
"expectedUrl": "/ko/hr/department-management",
"expectedUrl": "/hr/department-management",
"searchWithinParent": true,
"closeOtherMenus": true
},
"menuNavigationEnhanced": {
"strategy": "scroll-and-search",
"scrollContainer": ".sidebar-scroll, [data-sidebar], nav, aside",
"scrollStep": 200,
"maxScrollAttempts": 10,
"waitAfterScroll": 300,
"level1": {
"text": "인사관리",
"fallbackSelectors": [
"text=인사관리",
"[data-menu='hr']",
"a:has-text('인사관리')",
"button:has-text('인사관리')"
]
},
"level2": {
"text": "부서관리",
"fallbackSelectors": [
"text=부서관리",
"[data-menu='department']",
"a:has-text('부서관리')",
"button:has-text('부서관리')"
]
},
"expectedUrl": "/ko/hr/department-management",
"fallbackUrl": "https://dev.codebridge-x.com/ko/hr/department-management"
},
"timeout": 90000,
"tags": ["hr", "department", "crud", "random", "hierarchy"],
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"randomData": {
"parentDepartment": {
"type": "composite",
"pattern": "{prefix}본부_{timestamp}",
"components": {
"prefix": {
"type": "random",
"options": ["신규", "테스트", "개발", "QA", "운영", "전략", "혁신", "글로벌"]
}
}
},
"childDepartment": {
"type": "composite",
"pattern": "{prefix}팀_{timestamp}",
"components": {
"prefix": {
"type": "random",
"options": ["기획", "개발", "디자인", "마케팅", "영업", "지원", "품질", "연구"]
}
}
"testData": {
"create": {
"departmentName": "E2E_TEST_본부_{timestamp}",
"childName": "E2E_TEST_팀_{timestamp}"
}
},
"steps": [
{
"id": "step-0",
"name": "사이드바 메뉴 전체 펼치기",
"description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비",
"actions": [
{
"type": "evaluate",
"script": "document.querySelector('.sidebar-scroll, [data-sidebar], nav, aside')?.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 }
"id": 1,
"name": "메뉴 진입: 인사관리 > 부서관리",
"action": "menu_navigate",
"level1": "인사관리",
"level2": "부서관리",
"expected": {
"url_contains": "/hr/department-management",
"visible": ["부서관리"]
}
},
{
"id": 2,
"name": "필수 검증 #5: 목업 페이지 감지",
"action": "verify_not_mockup",
"checks": [
"부서 목록 또는 트리 표시",
"부서 추가 버튼 존재"
],
"expect": {
"sidebarReady": true
}
"expected": "정상 페이지 (목업 아님)"
},
{
"id": "step-1",
"name": "인사관리 메뉴 진입",
"description": "인사관리 > 부서관리 메뉴로 이동 (scrollAndFind 패턴 사용)",
"navigationPattern": "scrollAndFind",
"actions": [
{
"type": "scrollAndFind",
"container": ".sidebar-scroll, [data-sidebar], nav, aside",
"target": "인사관리",
"scrollStep": 200,
"maxAttempts": 10,
"waitAfterScroll": 300
},
{ "type": "click_if_exists", "target": "인사관리" },
{ "type": "wait", "duration": 500 },
{
"type": "scrollAndFind",
"container": ".sidebar-scroll, [data-sidebar], nav, aside",
"target": "부서관리",
"scrollStep": 200,
"maxAttempts": 10,
"waitAfterScroll": 300
},
{ "type": "click_if_exists", "target": "부서관리" }
"id": 3,
"name": "페이지 요소 확인",
"action": "verify_element",
"checks": [
"부서 목록 또는 트리 구조 표시",
"추가 버튼 존재"
],
"fallback": {
"type": "navigate",
"url": "https://dev.codebridge-x.com/ko/hr/department-management"
},
"expect": {
"url": "/hr/department-management",
"visible": ["부서관리", "추가"]
"expected": "부서관리 페이지 정상 표시"
},
{
"id": 4,
"phase": "CREATE",
"name": "[CREATE] 부서 추가 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('추가'), button:has-text('등록'), button:has-text('부서 추가')",
"expected": {
"modal": true
}
},
{
"id": "step-2",
"name": "현재 부서 개수 저장",
"description": "테스트 전 부서 개수 기록",
"capture": {
"variable": "initialCount",
"selector": "총 *건",
"extract": "number"
}
"id": 5,
"phase": "CREATE",
"name": "[CREATE] 부서명 입력",
"action": "fill",
"target": "input[name*='name'], input[placeholder*='부서명'], [role='dialog'] input",
"value": "E2E_TEST_본부_{timestamp}",
"clear": true
},
{
"id": "step-3",
"name": "상위 부서 추가 모달 열기",
"description": "추가 버튼 클릭하여 부서 추가 모달 열기",
"actions": [
{ "type": "click_if_exists", "target": "추가", "description": "부서 추가 모달 열기" }
],
"modalConfig": {
"containerSelector": "[role='dialog'], .modal",
"animationDelay": 300,
"waitForSelector": "[role='dialog']"
},
"expect": {
"modal": "부서 추가",
"visible": ["부서명", "등록", "취소"]
}
},
{
"id": "step-4",
"name": "랜덤 상위 부서명 입력",
"description": "모달 내 부서명 입력",
"actions": [
{
"type": "fillInModal",
"target": "부서명",
"value": "{randomData.parentDepartment}",
"options": { "waitAfter": 100 }
}
],
"expect": {
"buttonEnabled": "등록"
}
},
{
"id": "step-5",
"name": "상위 부서 등록",
"description": "모달 내 등록 버튼 클릭하여 상위 부서 추가 완료",
"actions": [
{ "type": "click_if_exists", "target": "등록", "options": { "waitAfter": 500 } }
],
"waitFor": {
"type": "modalClose",
"timeout": 5000
},
"expect": {
"toast": ["등록", "완료", "성공"],
"visible": ["{randomData.parentDepartment}"]
}
},
{
"id": "step-6",
"name": "상위 부서 등록 확인",
"description": "목록에서 새로 추가된 상위 부서 확인",
"id": 6,
"phase": "CREATE",
"name": "[CREATE] 필수 검증 #2: 부서 등록",
"action": "click_if_exists",
"target": "button:has-text('등록'), button:has-text('저장'), button:has-text('확인')",
"verify": {
"listContains": "{randomData.parentDepartment}",
"countIncreased": "{initialCount} + 1"
}
},
{
"id": "step-7",
"name": "하위 부서 추가 버튼 클릭",
"description": "생성된 상위 부서의 '하위 부서 추가' 버튼 클릭",
"actions": [
{
"type": "click_if_exists",
"target": "하위 부서 추가"
}
],
"expect": {
"modal": "하위 부서 추가",
"visible": ["부서명", "상위 부서", "{randomData.parentDepartment}"]
}
},
{
"id": "step-8",
"name": "랜덤 하위 부서명 입력",
"description": "모달 내 하위 부서명 입력",
"actions": [
{
"type": "fillInModal",
"target": "부서명",
"value": "{randomData.childDepartment}",
"options": { "waitAfter": 100 }
}
],
"expect": {
"buttonEnabled": "등록"
}
},
{
"id": "step-9",
"name": "하위 부서 등록",
"description": "모달 내 등록 버튼 클릭하여 하위 부서 추가 완료",
"actions": [
{ "type": "click_if_exists", "target": "등록", "options": { "waitAfter": 500 } }
],
"waitFor": {
"type": "modalClose",
"timeout": 5000
"toast": "등록|완료|성공"
},
"expect": {
"toast": ["등록", "완료", "성공"],
"visible": ["{randomData.childDepartment}"]
"expected": "부서 등록 완료"
},
{
"id": "6-modal-close",
"phase": "CREATE",
"name": "[CREATE] 모달 닫기 확인",
"action": "close_modal_if_open",
"expected": "모달 닫힘"
},
{
"id": 7,
"phase": "CREATE",
"name": "[CREATE] 등록 결과 확인",
"action": "verify_detail",
"checks": [
"E2E_TEST_본부 목록에 표시"
],
"expected": "부서 등록 확인"
},
{
"id": 8,
"phase": "DELETE",
"name": "[DELETE] 부서 삭제 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('삭제'), [aria-label='삭제']",
"expected": {
"confirm_dialog": true
}
},
{
"id": "step-10",
"name": "계층 구조 확인",
"description": "상위 부서 확장하여 하위 부서가 트리 구조로 표시되는지 확인",
"actions": [
{
"type": "click_if_exists",
"target": "table tbody tr:first-child [class*='expand'], table tbody tr:first-child button"
}
],
"id": 9,
"phase": "DELETE",
"name": "[DELETE] 필수 검증 #6: 삭제 확인",
"action": "click_and_confirm",
"target": "button:has-text('확인'), button:has-text('삭제')",
"verify": {
"hierarchy": {
"parent": "{randomData.parentDepartment}",
"children": ["{randomData.childDepartment}"]
},
"totalCountIncreased": "{initialCount} + 2"
}
},
{
"id": "step-11",
"name": "하위 부서 수정 모달 열기",
"description": "하위 부서의 수정 버튼 클릭",
"actions": [
{
"type": "click_if_exists",
"target": "수정"
}
],
"expect": {
"modal": "부서 수정",
"visible": ["부서명", "상위 부서", "저장", "취소"]
}
},
{
"id": "step-12",
"name": "하위 부서명 수정",
"description": "모달 내 하위 부서명 변경",
"actions": [
{
"type": "fillInModal",
"target": "부서명",
"value": "{randomData.childDepartment}_수정됨",
"options": { "waitAfter": 100 }
}
],
"expect": {
"buttonEnabled": "저장"
}
},
{
"id": "step-13",
"name": "부서 수정 저장",
"description": "모달 내 저장 버튼 클릭하여 부서 수정 완료",
"actions": [
{ "type": "click_if_exists", "target": "저장", "options": { "waitAfter": 500 } }
],
"waitFor": {
"type": "modalClose",
"timeout": 5000
"toast": "삭제|완료|성공"
},
"expect": {
"toast": ["수정", "완료", "성공", "저장"],
"visible": ["{randomData.childDepartment}_수정됨"]
}
"expected": "부서 삭제 완료"
},
{
"id": "step-13-1",
"name": "⚠️ 필수 검증: 수정 데이터 반영 확인",
"note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!",
"description": "목록에서 수정된 부서명 확인",
"verify": {
"listContains": "{randomData.childDepartment}_수정됨"
}
},
{
"id": "step-14",
"name": "하위 부서 삭제",
"description": "하위 부서의 삭제 버튼 클릭",
"actions": [
{
"type": "click_if_exists",
"target": "삭제"
}
"id": 10,
"phase": "DELETE",
"name": "[DELETE] 삭제 결과 확인",
"action": "verify_detail",
"checks": [
"E2E_TEST_본부 목록에서 제거"
],
"expect": {
"confirmDialog": true,
"dialogText": ["삭제", "하시겠습니까"]
}
},
{
"id": "step-15",
"name": "하위 부서 삭제 확인",
"description": "삭제 확인 다이얼로그에서 확인 클릭",
"actions": [
{ "type": "click_if_exists", "target": "확인", "description": "삭제 확인" }
],
"waitFor": {
"type": "apiResponse",
"method": "DELETE",
"timeout": 5000
},
"expect": {
"toast": ["삭제", "완료", "성공"]
}
},
{
"id": "step-15-1",
"name": "⚠️ 필수 검증: 하위 부서 삭제 반영 확인",
"description": "목록에서 삭제된 하위 부서가 없어졌는지 확인",
"verify": {
"listNotContains": "{randomData.childDepartment}_수정됨"
}
},
{
"id": "step-16",
"name": "상위 부서 삭제",
"description": "상위 부서의 삭제 버튼 클릭",
"actions": [
{
"type": "click_if_exists",
"target": "삭제"
}
],
"expect": {
"confirmDialog": true,
"dialogText": ["삭제", "하시겠습니까"]
}
},
{
"id": "step-17",
"name": "상위 부서 삭제 확인",
"description": "삭제 확인 다이얼로그에서 확인 클릭",
"actions": [
{ "type": "click_if_exists", "target": "확인", "description": "삭제 확인" }
],
"waitFor": {
"type": "apiResponse",
"method": "DELETE",
"timeout": 5000
},
"expect": {
"toast": ["삭제", "완료", "성공"]
}
},
{
"id": "step-18",
"name": "⚠️ 필수 검증: 상위 부서 삭제 반영 확인",
"note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 삭제 확인 필수!",
"description": "목록에서 삭제된 상위 부서가 없어졌는지 확인",
"verify": {
"listNotContains": "{randomData.parentDepartment}",
"countRestored": "{initialCount}"
"expected": {
"row_exists": false
}
}
],
"assertions": [
"expectedAPIs": [
{
"type": "url",
"expected": "/hr/department-management",
"message": "부서관리 페이지에 머물러야 함"
"method": "GET",
"endpoint": "/api/v1/departments",
"description": "부서 목록 조회"
},
{
"type": "elementExists",
"selector": "text={randomData.parentDepartment}",
"message": "상위 부서가 목록에 표시되어야 함"
"method": "POST",
"endpoint": "/api/v1/departments",
"description": "부서 등록"
},
{
"type": "elementExists",
"selector": "text={randomData.childDepartment}",
"message": "하위 부서가 목록에 표시되어야 함"
},
{
"type": "hierarchy",
"parent": "{randomData.parentDepartment}",
"child": "{randomData.childDepartment}",
"message": "하위 부서가 상위 부서 아래에 트리 구조로 표시되어야 함"
"method": "DELETE",
"endpoint": "/api/v1/departments/{id}",
"description": "부서 삭제"
}
],
"cleanup": {
"enabled": false,
"description": "테스트 후 생성된 부서 삭제 (필요시 활성화)",
"order": "childFirst",
"steps": [
{
"action": "delete",
"target": "{randomData.childDepartment}",
"description": "하위 부서 먼저 삭제"
},
{
"action": "delete",
"target": "{randomData.parentDepartment}",
"description": "상위 부서 삭제"
}
]
},
"notes": {
"testScope": "상위 부서 생성 → 하위 부서 추가 → 계층 구조 검증까지 전체 플로우 테스트",
"randomGeneration": {
"parent": "prefix(신규,테스트,개발 등) + '본부' + timestamp",
"child": "prefix(기획,개발,디자인 등) + '팀' + timestamp"
"requiredVerifications": [
{
"id": 2,
"name": "등록/저장 버튼",
"steps": [6],
"criteria": "API 호출 + 성공 토스트 + 데이터 반영"
},
"duplicateHandling": "timestamp 포함으로 중복 방지",
"prerequisites": "로그인된 사용자에게 부서 추가 권한 필요",
"uiElements": {
"addButton": "상단 '추가' 버튼 - 최상위 부서 추가",
"subAddButton": "각 행의 '하위 부서 추가' 아이콘 버튼",
"expandButton": "트리 구조 확장/축소 버튼"
{
"id": 5,
"name": "목업 페이지 감지",
"steps": [2],
"criteria": "부서 목록, 추가 버튼 존재"
},
{
"id": 6,
"name": "삭제 기능",
"steps": [8, 9, 10],
"criteria": "DELETE API + 목록에서 제거"
}
],
"rollbackPlan": {
"onCreateFail": "모달 닫기",
"onDeleteFail": "E2E_TEST_ 접두사 부서 수동 삭제 필요",
"cleanupRequired": "E2E_TEST_ 접두사 부서는 테스트 데이터"
}
}