{ "id": "rank-management", "name": "설정 - 직급관리", "screenshotPolicy": { "onErrorOnly": true, "captureOn": ["error", "fail", "timeout", "404", "500", "blocked"] }, "url": "/ko/settings/ranks", "navigation": { "targetUrl": "/settings/ranks", "urlPattern": "/settings/ranks|/ko/settings/ranks", "menuHints": ["직급관리", "직급 관리", "설정"] }, "menuNavigation": { "level1": "설정", "level2": "직급관리", "expectedUrl": "/ko/settings/ranks", "searchWithinParent": true, "closeOtherMenus": true }, "auth": { "username": "TestUser5", "password": "password123!" }, "menuNavigationEnhanced": { "strategy": "scroll-and-search", "description": "사이드바를 스크롤하며 메뉴를 찾고 클릭하여 404를 방지", "level1": "설정", "level2": "직급관리", "alternativeLevel1Names": ["설정", "Settings", "환경설정", "시스템설정", "관리"], "alternativeLevel2Names": ["직급관리", "직급 관리", "Ranks", "직급", "Position Management"], "scrollConfig": { "sidebarSelector": "nav, aside, [role='navigation'], .sidebar, #sidebar", "menuItemSelector": "a, button, [role='menuitem'], [role='treeitem']", "scrollStep": 200, "maxScrollAttempts": 10, "scrollDelay": 300 } }, "expectedAPIs": [ { "method": "GET", "path": "/api/v1/positions?type=rank", "description": "직급 목록 조회" }, { "method": "POST", "path": "/api/v1/positions", "description": "직급 생성" }, { "method": "PUT", "path": "/api/v1/positions/{id}", "description": "직급 수정" }, { "method": "DELETE", "path": "/api/v1/positions/{id}", "description": "직급 삭제" }, { "method": "PUT", "path": "/api/v1/positions/reorder", "description": "직급 순서 변경" } ], "steps": [ { "id": "step-00", "name": "사이드바 메뉴 전체 펼치기", "description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비", "actions": [ { "type": "evaluate", "script": "document.querySelector('.sidebar-scroll')?.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": "step-01", "name": "2단계 메뉴 진입: 설정 > 직급관리", "description": "사이드바를 스크롤하며 설정 > 직급관리 메뉴를 찾아 클릭", "actions": [ { "type": "scrollAndFind", "target": "설정", "alternativeTexts": ["설정", "Settings", "환경설정", "시스템설정"], "scrollContainer": "sidebar", "maxAttempts": 10, "description": "스크롤하며 설정 메뉴 찾기" }, { "type": "click", "target": "설정", "description": "설정 메뉴 클릭" }, { "type": "wait", "duration": 500, "description": "서브메뉴 펼쳐지기 대기" }, { "type": "scrollAndFind", "target": "직급관리", "alternativeTexts": ["직급관리", "직급 관리", "Ranks", "직급"], "scrollContainer": "submenu", "maxAttempts": 5, "description": "서브메뉴에서 직급관리 찾기" }, { "type": "click", "target": "직급관리", "description": "직급관리 메뉴 클릭" }, { "type": "wait", "target": "페이지 로드 완료", "timeout": 10000 } ], "expected": { "url": "/ko/settings/ranks", "title": "직급관리", "authenticated": true }, "verification": [ "설정 메뉴가 펼쳐졌는지 확인", "직급관리 서브메뉴 클릭 성공", "페이지 타이틀 '직급관리' 확인", "설명 '사원의 직급을 관리합니다. 드래그하여 순서를 변경할 수 있습니다.' 확인", "Award 아이콘 표시", "404 에러 없이 페이지 로드 완료" ] }, { "id": "step-02", "name": "직급 추가 입력 영역 확인", "action": "verify", "verification": [ "입력 필드 존재 (placeholder: '직급명을 입력하세요')", "추가 버튼 존재 (disabled 상태)", "Plus 아이콘 표시" ] }, { "id": "step-03", "name": "직급 목록 카드 확인", "action": "verify", "verification": [ "직급 목록 카드 표시", "기존 직급 목록 로드 확인", "각 직급 항목 구조: 드래그 핸들, 순서 번호, 직급명, 수정 버튼, 삭제 버튼" ] }, { "id": "step-04", "name": "드래그 핸들 아이콘 확인", "action": "verify", "verification": [ "각 직급 항목에 GripVertical 아이콘 표시", "순서 번호 표시 (1, 2, 3...)", "cursor-move 스타일 적용 확인" ] }, { "id": "step-05", "name": "안내 문구 확인", "action": "verify", "verification": [ "하단 안내 문구 표시: '※ 직급 순서는 드래그 앤 드롭으로 변경할 수 있습니다.'" ] }, { "id": "step-06", "name": "직급 추가 - 빈 값 입력 시도", "action": "type", "target": "직급명 입력 필드", "value": "", "verification": [ "추가 버튼 disabled 상태 유지", "입력 불가 확인" ] }, { "id": "step-07", "name": "직급 추가 - 공백만 입력 시도", "action": "type", "target": "직급명 입력 필드", "value": " ", "verification": [ "추가 버튼 클릭 가능하나 실제 추가 안됨", "입력 필드 값 유지 또는 초기화" ] }, { "id": "step-08", "name": "직급 추가 - 정상 입력", "action": "type", "target": "직급명 입력 필드", "value": "E2E 테스트 직급1", "verification": [ "입력 값 반영 확인", "추가 버튼 활성화 (enabled)" ] }, { "id": "step-09", "name": "직급 추가 실행 (버튼 클릭)", "action": "click_if_exists", "target": "추가 버튼", "verification": [ "추가 버튼 클릭", "로딩 상태 표시 (Loader2 아이콘)", "API 호출: POST /api/v1/positions", "API 응답 200 OK 확인", "성공 토스트 메시지: '직급이 추가되었습니다.'", "입력 필드 초기화", "추가 버튼 다시 disabled 상태", "목록에 신규 직급 표시 (하단에 추가)" ] }, { "id": "step-10", "name": "신규 직급 확인", "action": "verify", "verification": [ "'E2E 테스트 직급1' 목록에 표시", "순서 번호 자동 할당 (마지막 순서)", "드래그 핸들, 수정/삭제 버튼 존재" ] }, { "id": "step-11", "name": "직급 추가 - Enter 키로 등록", "action": "type", "target": "직급명 입력 필드", "value": "E2E 테스트 직급2", "verification": [ "입력 값 반영 확인" ] }, { "id": "step-12", "name": "Enter 키 입력", "action": "keypress", "target": "직급명 입력 필드", "key": "Enter", "verification": [ "Enter 키로 직급 추가 실행", "API 호출: POST /api/v1/positions", "성공 토스트 메시지 확인", "입력 필드 초기화", "'E2E 테스트 직급2' 목록에 추가" ] }, { "id": "step-13", "name": "세 번째 직급 추가 (드래그 테스트용)", "action": "type+click", "target": "직급명 입력 필드", "value": "E2E 테스트 직급3", "verification": [ "입력 후 추가 버튼 클릭", "API 호출 및 성공 확인", "'E2E 테스트 직급3' 목록에 추가" ] }, { "id": "step-14", "name": "직급 목록 상태 확인", "action": "verify", "verification": [ "총 N개 직급 표시 (기존 + 신규 3개)", "각 직급의 순서 번호 연속적 (1, 2, 3...)", "신규 직급들이 하단에 순서대로 추가됨" ] }, { "id": "step-15", "name": "직급 수정 다이얼로그 열기", "action": "click", "target": "E2E 테스트 직급1의 수정 버튼", "verification": [ "수정 다이얼로그 표시", "다이얼로그 제목: '직급 수정'", "직급명 입력 필드에 현재 값 표시: 'E2E 테스트 직급1'", "취소 버튼 존재", "수정 버튼 존재" ] }, { "id": "step-16", "name": "직급명 수정 입력", "action": "type", "target": "다이얼로그 직급명 입력 필드", "value": "E2E 테스트 직급1 (수정됨)", "verification": [ "입력 값 반영 확인", "수정 버튼 활성화" ] }, { "id": "step-17", "name": "직급 수정 실행", "action": "click_if_exists", "target": "다이얼로그 수정 버튼", "verification": [ "수정 버튼 클릭", "로딩 상태 표시", "API 호출: PUT /api/v1/positions/{id}", "API 응답 200 OK 확인", "성공 토스트 메시지: '직급이 수정되었습니다.'", "다이얼로그 닫힘", "목록에서 직급명 변경 확인: 'E2E 테스트 직급1 (수정됨)'" ] }, { "id": "step-18", "name": "수정 취소 테스트 - 다이얼로그 열기", "action": "click", "target": "E2E 테스트 직급2의 수정 버튼", "verification": [ "다이얼로그 표시", "현재 값: 'E2E 테스트 직급2'" ] }, { "id": "step-19", "name": "수정 취소", "action": "click", "target": "다이얼로그 취소 버튼", "verification": [ "다이얼로그 닫힘", "직급명 변경 없음 (API 호출 없음)" ] }, { "id": "step-20", "name": "드래그 앤 드롭 - 첫 번째 항목 선택", "action": "drag_start", "target": "목록 마지막 직급 (E2E 테스트 직급3)", "verification": [ "드래그 시작", "해당 항목 opacity 50% 및 배경색 변경", "cursor-move 활성화" ] }, { "id": "step-21", "name": "드래그 앤 드롭 - 상단으로 이동", "action": "drag_over", "target": "목록 첫 번째 위치", "verification": [ "드래그 중 위치 변경 시각적 피드백", "실시간 순서 변경 (로컬 상태)" ] }, { "id": "step-22", "name": "드래그 앤 드롭 - 드롭 실행", "action": "drag_end", "verification": [ "드래그 종료", "API 호출: PUT /api/v1/positions/reorder", "Request body에 전체 직급의 새로운 순서 포함", "API 응답 200 OK 확인", "성공 토스트 메시지: '순서가 변경되었습니다.'", "순서 번호 업데이트 확인", "'E2E 테스트 직급3'이 최상단으로 이동" ] }, { "id": "step-23", "name": "순서 변경 확인", "action": "verify", "verification": [ "변경된 순서가 화면에 반영됨", "각 항목의 순서 번호 재할당 (1, 2, 3...)", "드래그한 항목이 목표 위치에 정확히 배치" ] }, { "id": "step-24", "name": "삭제 확인 다이얼로그 열기", "action": "click", "target": "E2E 테스트 직급3의 삭제 버튼", "verification": [ "삭제 확인 다이얼로그 표시", "다이얼로그 제목: '직급 삭제'", "메시지: '\"E2E 테스트 직급3\" 직급을 삭제하시겠습니까?'", "경고 메시지: '이 직급을 사용 중인 사원이 있으면 해당 사원의 직급이 초기화됩니다.' (빨간색)", "취소 버튼 존재", "삭제 버튼 존재 (빨간색)" ] }, { "id": "step-25", "name": "삭제 취소", "action": "click", "target": "다이얼로그 취소 버튼", "verification": [ "다이얼로그 닫힘", "직급 삭제되지 않음 (API 호출 없음)", "목록에 여전히 존재" ] }, { "id": "step-26", "name": "삭제 실행 - 다이얼로그 재열기", "action": "click", "target": "E2E 테스트 직급3의 삭제 버튼", "verification": [ "삭제 확인 다이얼로그 표시" ] }, { "id": "step-27", "name": "삭제 확인 실행", "action": "click_if_exists", "target": "다이얼로그 삭제 버튼", "verification": [ "삭제 버튼 클릭", "로딩 상태 표시 (Loader2 아이콘)", "API 호출: DELETE /api/v1/positions/{id}", "API 응답 200 OK 확인", "성공 토스트 메시지: '직급이 삭제되었습니다.'", "다이얼로그 닫힘", "목록에서 'E2E 테스트 직급3' 제거됨", "순서 번호 재정렬" ] }, { "id": "step-28", "name": "나머지 테스트 직급 삭제 - 직급2", "action": "click+confirm", "target": "E2E 테스트 직급2의 삭제 버튼", "verification": [ "삭제 다이얼로그 표시 → 삭제 버튼 클릭", "API 호출 및 성공 확인", "목록에서 제거" ] }, { "id": "step-29", "name": "나머지 테스트 직급 삭제 - 직급1 (수정됨)", "action": "click+confirm", "target": "E2E 테스트 직급1 (수정됨)의 삭제 버튼", "verification": [ "삭제 다이얼로그 표시 → 삭제 버튼 클릭", "API 호출 및 성공 확인", "목록에서 제거", "테스트 데이터 완전 정리" ] }, { "id": "step-30", "name": "최종 상태 확인", "action": "verify", "verification": [ "기존 직급만 남음 (테스트 데이터 모두 삭제)", "순서 번호 정상", "페이지 정상 동작" ] }, { "id": "step-31", "name": "빈 목록 상태 테스트 (선택)", "action": "verify", "verification": [ "만약 모든 직급 삭제 시: '등록된 직급이 없습니다.' 메시지 표시", "입력 필드와 추가 버튼은 정상 표시" ] }, { "id": "step-32", "name": "페이지 새로고침 후 데이터 확인", "action": "reload", "verification": [ "페이지 새로고침", "GET /api/v1/positions?type=rank 호출", "저장된 순서대로 직급 목록 로드", "이전 상태 유지 확인" ] }, { "id": "step-33", "name": "한글 IME 입력 테스트", "action": "type", "target": "직급명 입력 필드", "value": "부장", "verification": [ "한글 조합 중 Enter 키 입력 시 조합 완료 대기", "조합 완료 후 추가 동작", "isComposing 이벤트 처리 확인" ] }, { "id": "step-34", "name": "특수문자 입력 테스트", "action": "type+click", "target": "직급명 입력 필드", "value": "직급@#$%", "verification": [ "특수문자 포함 직급명 입력", "추가 버튼 클릭", "API 호출 및 저장 여부 확인", "성공 시 목록에 표시, 실패 시 에러 메시지" ] }, { "id": "step-35", "name": "긴 직급명 입력 테스트", "action": "type+click", "target": "직급명 입력 필드", "value": "매우긴직급명테스트매우긴직급명테스트매우긴직급명테스트매우긴직급명테스트", "verification": [ "긴 직급명 입력", "추가 시도", "API 응답 확인 (길이 제한 있을 경우 에러)", "UI에서 텍스트 오버플로우 처리 확인" ] }, { "id": "step-36", "name": "중복 직급명 입력 테스트", "action": "type+click", "target": "직급명 입력 필드", "value": "과장", "verification": [ "기존 직급명과 동일한 이름 입력", "추가 버튼 클릭", "API 응답 확인", "중복 허용 시 성공, 중복 불가 시 에러 메시지" ] }, { "id": "step-37", "name": "로딩 중 상태 테스트", "action": "verify", "verification": [ "API 호출 중 버튼 disabled 상태", "Loader2 아이콘 표시", "중복 클릭 방지 확인" ] }, { "id": "step-38", "name": "에러 처리 테스트 (네트워크 오류 시뮬레이션)", "action": "verify", "verification": [ "API 호출 실패 시 에러 토스트 표시", "에러 메시지 명확성 확인", "페이지 상태 유지 (크래시 없음)" ] } ], "testData": { "ranks": [ { "name": "E2E 테스트 직급1", "expected_order": "last" }, { "name": "E2E 테스트 직급2", "expected_order": "last" }, { "name": "E2E 테스트 직급3", "expected_order": "last → first (after drag)" } ] }, "cleanup": { "description": "Step-27~29에서 테스트 데이터 삭제 완료", "method": "각 직급의 삭제 버튼 클릭 → 확인 다이얼로그에서 삭제 확인" } }