{ "id": "department-add", "name": "부서 추가 테스트 (랜덤 + 하위부서)", "screenshotPolicy": { "onErrorOnly": true, "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] }, "description": "랜덤 상위 부서 생성 후 하위 부서까지 추가하는 고도화된 E2E 테스트", "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", "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": ["기획", "개발", "디자인", "마케팅", "영업", "지원", "품질", "연구"] } } } }, "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 } ], "expect": { "sidebarReady": true } }, { "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", "target": "인사관리" }, { "type": "wait", "duration": 500 }, { "type": "scrollAndFind", "container": ".sidebar-scroll, [data-sidebar], nav, aside", "target": "부서관리", "scrollStep": 200, "maxAttempts": 10, "waitAfterScroll": 300 }, { "type": "click", "target": "부서관리" } ], "fallback": { "type": "navigate", "url": "https://dev.codebridge-x.com/ko/hr/department-management" }, "expect": { "url": "/hr/department-management", "visible": ["부서관리", "추가"] } }, { "id": "step-2", "name": "현재 부서 개수 저장", "description": "테스트 전 부서 개수 기록", "capture": { "variable": "initialCount", "selector": "총 *건", "extract": "number" } }, { "id": "step-3", "name": "상위 부서 추가 모달 열기", "description": "추가 버튼 클릭하여 부서 추가 모달 열기", "actions": [ { "type": "openModal", "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": "clickInModal", "target": "등록", "options": { "waitAfter": 500 } } ], "waitFor": { "type": "modalClose", "timeout": 5000 }, "expect": { "toast": ["등록", "완료", "성공"], "visible": ["{randomData.parentDepartment}"] } }, { "id": "step-6", "name": "상위 부서 등록 확인", "description": "목록에서 새로 추가된 상위 부서 확인", "verify": { "listContains": "{randomData.parentDepartment}", "countIncreased": "{initialCount} + 1" } }, { "id": "step-7", "name": "하위 부서 추가 버튼 클릭", "description": "생성된 상위 부서의 '하위 부서 추가' 버튼 클릭", "actions": [ { "type": "findRow", "contains": "{randomData.parentDepartment}", "then": { "type": "click", "target": "하위 부서 추가", "tooltip": true } } ], "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": "clickInModal", "target": "등록", "options": { "waitAfter": 500 } } ], "waitFor": { "type": "modalClose", "timeout": 5000 }, "expect": { "toast": ["등록", "완료", "성공"], "visible": ["{randomData.childDepartment}"] } }, { "id": "step-10", "name": "계층 구조 확인", "description": "상위 부서 확장하여 하위 부서가 트리 구조로 표시되는지 확인", "actions": [ { "type": "findRow", "contains": "{randomData.parentDepartment}", "then": { "type": "click", "target": "expand", "description": "트리 확장 버튼 클릭" } } ], "verify": { "hierarchy": { "parent": "{randomData.parentDepartment}", "children": ["{randomData.childDepartment}"] }, "totalCountIncreased": "{initialCount} + 2" } }, { "id": "step-11", "name": "하위 부서 수정 모달 열기", "description": "하위 부서의 수정 버튼 클릭", "actions": [ { "type": "findRow", "contains": "{randomData.childDepartment}", "then": { "type": "click", "target": "수정", "tooltip": true, "description": "수정 아이콘 버튼 클릭" } } ], "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": "clickInModal", "target": "저장", "options": { "waitAfter": 500 } } ], "waitFor": { "type": "modalClose", "timeout": 5000 }, "expect": { "toast": ["수정", "완료", "성공", "저장"], "visible": ["{randomData.childDepartment}_수정됨"] } }, { "id": "step-13-1", "name": "⚠️ 필수 검증: 수정 데이터 반영 확인", "critical": true, "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 변경 확인 필수!", "description": "목록에서 수정된 부서명 확인", "verify": { "listContains": "{randomData.childDepartment}_수정됨" } }, { "id": "step-14", "name": "하위 부서 삭제", "description": "하위 부서의 삭제 버튼 클릭", "actions": [ { "type": "findRow", "contains": "{randomData.childDepartment}_수정됨", "then": { "type": "click", "target": "삭제", "tooltip": true, "description": "삭제 아이콘 버튼 클릭" } } ], "expect": { "confirmDialog": true, "dialogText": ["삭제", "하시겠습니까"] } }, { "id": "step-15", "name": "하위 부서 삭제 확인", "description": "삭제 확인 다이얼로그에서 확인 클릭", "actions": [ { "type": "click", "target": "확인", "description": "삭제 확인" } ], "waitFor": { "type": "apiResponse", "method": "DELETE", "timeout": 5000 }, "expect": { "toast": ["삭제", "완료", "성공"] } }, { "id": "step-15-1", "name": "⚠️ 필수 검증: 하위 부서 삭제 반영 확인", "critical": true, "description": "목록에서 삭제된 하위 부서가 없어졌는지 확인", "verify": { "listNotContains": "{randomData.childDepartment}_수정됨" } }, { "id": "step-16", "name": "상위 부서 삭제", "description": "상위 부서의 삭제 버튼 클릭", "actions": [ { "type": "findRow", "contains": "{randomData.parentDepartment}", "then": { "type": "click", "target": "삭제", "tooltip": true, "description": "삭제 아이콘 버튼 클릭" } } ], "expect": { "confirmDialog": true, "dialogText": ["삭제", "하시겠습니까"] } }, { "id": "step-17", "name": "상위 부서 삭제 확인", "description": "삭제 확인 다이얼로그에서 확인 클릭", "actions": [ { "type": "click", "target": "확인", "description": "삭제 확인" } ], "waitFor": { "type": "apiResponse", "method": "DELETE", "timeout": 5000 }, "expect": { "toast": ["삭제", "완료", "성공"] } }, { "id": "step-18", "name": "⚠️ 필수 검증: 상위 부서 삭제 반영 확인", "critical": true, "note": "토스트 성공 메시지만으로 PASS 판정 불가. 실제 데이터 삭제 확인 필수!", "description": "목록에서 삭제된 상위 부서가 없어졌는지 확인", "verify": { "listNotContains": "{randomData.parentDepartment}", "countRestored": "{initialCount}" } } ], "assertions": [ { "type": "url", "expected": "/hr/department-management", "message": "부서관리 페이지에 머물러야 함" }, { "type": "elementExists", "selector": "text={randomData.parentDepartment}", "message": "상위 부서가 목록에 표시되어야 함" }, { "type": "elementExists", "selector": "text={randomData.childDepartment}", "message": "하위 부서가 목록에 표시되어야 함" }, { "type": "hierarchy", "parent": "{randomData.parentDepartment}", "child": "{randomData.childDepartment}", "message": "하위 부서가 상위 부서 아래에 트리 구조로 표시되어야 함" } ], "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" }, "duplicateHandling": "timestamp 포함으로 중복 방지", "prerequisites": "로그인된 사용자에게 부서 추가 권한 필요", "uiElements": { "addButton": "상단 '추가' 버튼 - 최상위 부서 추가", "subAddButton": "각 행의 '하위 부서 추가' 아이콘 버튼", "expandButton": "트리 구조 확장/축소 버튼" } } }