Files
sam-scenarios/item-management.json
김보곤 b2509ee2dc refactor: 28개 시나리오 JSON 업데이트
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-06 14:14:09 +09:00

303 lines
11 KiB
JSON

{
"id": "item-management",
"name": "품목관리 (Item Management)",
"screenshotPolicy": {
"onErrorOnly": true,
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
},
"description": "생산관리 - 품목관리 메뉴의 전체 기능 테스트: 품목 조회, 검색, 필터, 등록, 상세보기, 수정, 삭제",
"priority": "High",
"baseUrl": "https://dev.codebridge-x.com",
"menuNavigation": {
"level1": "생산관리",
"level2": "품목관리",
"expectedUrl": "/ko/production/screen-production",
"searchWithinParent": true,
"closeOtherMenus": true
},
"auth": {
"username": "TestUser5",
"password": "password123!"
},
"selectors": {
"statCard": ".card, [class*='stat'], [class*='summary'], [class*='kpi']",
"searchInput": "input[type='search'], input[type='text'][placeholder*='검색'], input[placeholder*='Search'], input[placeholder*='품목']",
"tabButtons": "button[role='tab'], [class*='tab'] button, [class*='filter'] button",
"tableHeader": "table thead th, table th, [role='columnheader']",
"tableRow": "table tbody tr, [role='row']:not(:first-child)",
"pagination": "[class*='pagination'], [class*='Pagination'], nav[aria-label*='page']",
"actionButtons": "button:has-text('상세'), button:has-text('수정'), button:has-text('삭제')",
"registerButton": "button:has-text('품목 등록'), button:has-text('등록'), button[class*='add']",
"saveButton": "button:has-text('저장'), button[type='submit']",
"cancelButton": "button:has-text('취소'), button:has-text('닫기')",
"toast": "[class*='toast'], [class*='Toast'], [role='alert'], [class*='notification']",
"modal": "[role='dialog'], [class*='modal'], [class*='Modal']",
"formField": "input:not([type='hidden']), textarea, select",
"combobox": "select, [role='combobox'], [class*='select'], [class*='dropdown']",
"pageTitle": "h1, h2, [class*='title'], [class*='Title']"
},
"steps": [
{
"id": 0,
"name": "사이드바 메뉴 전체 펼치기",
"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": 1,
"name": "2단계 메뉴 진입: 생산관리 > 품목관리",
"action": "menu_navigate",
"level1": "생산관리",
"level2": "품목관리",
"expected": { "url_contains": "/production" }
},
{
"id": 2,
"name": "페이지 로드 및 404 확인",
"action": "evaluate",
"script": "(() => { const url = window.location.href; const text = document.body.innerText; return !text.includes('404') && !text.includes('Not Found') && (url.includes('production') || text.includes('품목')); })()"
},
{
"id": 3,
"name": "통계 카드 영역 존재 확인",
"action": "verify_element",
"target": "statCard"
},
{
"id": 4,
"name": "통계 카드 숫자 데이터 확인",
"action": "evaluate",
"script": "(() => { const cards = document.querySelectorAll('.card, [class*=\"stat\"]'); let hasNum = false; cards.forEach(c => { if (/\\d+/.test(c.innerText)) hasNum = true; }); return hasNum || document.body.innerText.includes('전체'); })()"
},
{
"id": 5,
"name": "품목 등록 버튼 존재 확인",
"action": "verify_element",
"target": "registerButton"
},
{
"id": 6,
"name": "검색 입력 필드 존재 확인",
"action": "evaluate",
"script": "(() => { const hasInput = document.querySelector('input[type=\"search\"], input[placeholder*=\"검색\"], input[placeholder*=\"품목\"]'); return hasInput || document.body.innerText.includes('검색'); })()"
},
{
"id": 7,
"name": "탭/필터 버튼 존재 확인",
"action": "evaluate",
"script": "(() => { const text = document.body.innerText; return text.includes('전체') && (text.includes('제품') || text.includes('부품') || text.includes('소모품')); })()"
},
{
"id": 8,
"name": "데이터 테이블 헤더 확인",
"action": "evaluate",
"script": "(() => { const th = document.querySelectorAll('table th, thead th, [role=\"columnheader\"]'); return th.length > 0 || document.body.innerText.includes('품목코드'); })()"
},
{
"id": 9,
"name": "데이터 행 존재 확인",
"action": "evaluate",
"script": "(() => { const rows = document.querySelectorAll('table tbody tr, [role=\"row\"]:not(:first-child)'); return rows.length > 0 || document.body.innerText.includes('데이터가 없습니다'); })()"
},
{
"id": 10,
"name": "페이지네이션 존재 확인",
"action": "evaluate",
"script": "(() => { const pag = document.querySelector('[class*=\"pagination\"], [class*=\"Pagination\"], nav[aria-label*=\"page\"]'); const hasPageNum = document.body.innerText.match(/\\d+\\s*-\\s*\\d+|페이지|Page/); return pag || hasPageNum; })()"
},
{
"id": 11,
"phase": "FILTER",
"name": "[FILTER] 검색 기능 테스트 - 검색어 입력",
"action": "evaluate",
"script": "(() => { const input = document.querySelector('input[type=\"search\"], input[placeholder*=\"검색\"], input[placeholder*=\"품목\"]'); if (input) { input.value = 'CS-001'; input.dispatchEvent(new Event('input', {bubbles:true})); return true; } return document.body.innerText.includes('품목'); })()"
},
{
"id": 12,
"phase": "FILTER",
"name": "[FILTER] 검색 결과 대기",
"action": "wait",
"duration": 1500
},
{
"id": 13,
"phase": "FILTER",
"name": "[FILTER] 검색 결과 테이블 확인",
"action": "evaluate",
"script": "(() => { const rows = document.querySelectorAll('table tbody tr'); return rows.length >= 0; })()"
},
{
"id": 14,
"phase": "FILTER",
"name": "[FILTER] 검색 초기화 - 검색창 클리어",
"action": "evaluate",
"script": "(() => { const input = document.querySelector('input[type=\"search\"], input[placeholder*=\"검색\"], input[placeholder*=\"품목\"]'); if (input) { input.value = ''; input.dispatchEvent(new Event('input', {bubbles:true})); return true; } return true; })()"
},
{
"id": 15,
"phase": "FILTER",
"name": "[FILTER] 제품 탭 클릭",
"action": "click_if_exists",
"target": "button:has-text('제품'), [class*='tab']:has-text('제품')"
},
{
"id": 16,
"phase": "FILTER",
"name": "[FILTER] 필터 결과 대기",
"action": "wait",
"duration": 1000
},
{
"id": 17,
"phase": "FILTER",
"name": "[FILTER] 전체 탭 클릭 (초기화)",
"action": "click_if_exists",
"target": "button:has-text('전체'), [class*='tab']:has-text('전체')"
},
{
"id": 18,
"name": "페이지네이션 - 2페이지 이동",
"action": "click_if_exists",
"target": "button:has-text('2'), [class*='pagination'] button:has-text('2'), a:has-text('2')"
},
{
"id": 19,
"name": "2페이지 로드 대기",
"action": "wait",
"duration": 1000
},
{
"id": 20,
"name": "1페이지로 복귀",
"action": "click_if_exists",
"target": "button:has-text('1'), [class*='pagination'] button:has-text('1'), a:has-text('1')"
},
{
"id": 21,
"phase": "CREATE",
"name": "[CREATE] 품목 등록 버튼 클릭",
"action": "click",
"target": "registerButton"
},
{
"id": 22,
"phase": "CREATE",
"name": "[CREATE] 등록 페이지/모달 로드 대기",
"action": "wait",
"duration": 1500
},
{
"id": 23,
"phase": "CREATE",
"name": "[CREATE] 등록 폼 존재 확인",
"action": "evaluate",
"script": "(() => { const hasForm = document.querySelectorAll('input, select, textarea').length > 0; const hasText = document.body.innerText.includes('등록') || document.body.innerText.includes('품목'); return hasForm || hasText; })()"
},
{
"id": 24,
"phase": "CREATE",
"name": "[CREATE] 품목 유형 선택 영역 확인",
"action": "verify_element",
"target": "combobox"
},
{
"id": 25,
"phase": "CREATE",
"name": "[CREATE] 품목 유형 드롭다운 클릭",
"action": "click_if_exists",
"target": "select, [class*='select'], [role='combobox']"
},
{
"id": 26,
"phase": "CREATE",
"name": "[CREATE] 제품 옵션 선택",
"action": "click_if_exists",
"target": "option:has-text('제품'), [role='option']:has-text('제품'), li:has-text('제품')"
},
{
"id": 27,
"phase": "CREATE",
"name": "[CREATE] 필수 입력 필드 존재 확인",
"action": "verify_element",
"target": "formField"
},
{
"id": 28,
"phase": "CREATE",
"name": "[CREATE] 취소 버튼 클릭 (등록 취소)",
"action": "click_if_exists",
"target": "cancelButton"
},
{
"id": 29,
"phase": "CREATE",
"name": "[CREATE] 목록 페이지 복귀 대기",
"action": "wait",
"duration": 1000
},
{
"id": 30,
"phase": "READ",
"name": "[READ] 첫 번째 행 상세보기 버튼 클릭",
"action": "click_if_exists",
"target": "button:has-text('상세'), button:has-text('보기'), table tbody tr:first-child button"
},
{
"id": 31,
"phase": "READ",
"name": "[READ] 상세 정보 로드 대기",
"action": "wait",
"duration": 1000
},
{
"id": 32,
"phase": "READ",
"name": "[READ] 상세 정보 표시 확인",
"action": "evaluate",
"script": "(() => { const text = document.body.innerText; return text.includes('품목') || text.includes('코드') || text.includes('상세') || document.querySelector('[role=\"dialog\"]'); })()"
},
{
"id": 33,
"phase": "READ",
"name": "[READ] 상세 모달/페이지 닫기",
"action": "click_if_exists",
"target": "button:has-text('닫기'), button:has-text('Close'), [class*='close'], button[aria-label='Close']"
},
{
"id": 34,
"phase": "READ",
"name": "[READ] 목록 페이지 복귀 대기",
"action": "wait",
"duration": 500
},
{
"id": 35,
"name": "테이블 구조 최종 확인",
"action": "verify_element",
"target": "table, [role='grid'], [class*='table']"
},
{
"id": 36,
"name": "액션 버튼 존재 확인",
"action": "evaluate",
"script": "(() => { const text = document.body.innerText; return text.includes('상세') || text.includes('수정') || text.includes('삭제'); })()"
},
{
"id": 37,
"name": "페이지 정상 동작 최종 확인",
"action": "evaluate",
"script": "(() => { const text = document.body.innerText; return text.includes('품목') || text.includes('전체') || text.includes('제품') || text.includes('부품'); })()"
}
],
"expectedAPIs": [
{ "method": "GET", "endpoint": "/api/items", "description": "품목 목록 조회" }
],
"rollbackPlan": {
"note": "테스트 데이터 생성하지 않음 - 조회 및 UI 검증만 수행"
}
}