2026-01-30 10:50:38 +09:00
|
|
|
{
|
2026-02-04 21:59:56 +09:00
|
|
|
"id": "item-management",
|
|
|
|
|
"name": "품목관리 (Item Management)",
|
2026-01-30 10:50:38 +09:00
|
|
|
"screenshotPolicy": {
|
|
|
|
|
"onErrorOnly": true,
|
2026-02-06 06:18:53 +09:00
|
|
|
"captureOn": ["error", "fail", "timeout", "404", "500", "blocked"]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
"description": "생산관리 - 품목관리 메뉴의 전체 기능 테스트: 품목 조회, 검색, 필터, 등록(제품/부품/소모품), 상세보기, 수정, 삭제, 페이지네이션",
|
|
|
|
|
"priority": "High",
|
2026-02-06 06:18:53 +09:00
|
|
|
"tags": ["production", "item-management", "crud", "pagination", "search", "filter"],
|
2026-01-30 10:50:38 +09:00
|
|
|
"baseUrl": "https://dev.codebridge-x.com",
|
|
|
|
|
"url": "/ko/production/screen-production",
|
2026-02-06 06:18:53 +09:00
|
|
|
"selectors": {
|
|
|
|
|
"sidebar": ".sidebar-scroll, nav, aside, [role='navigation']",
|
|
|
|
|
"searchInput": "input[placeholder*='검색'], input[placeholder*='품목코드'], input[type='search']",
|
|
|
|
|
"registerButton": "button:has-text('품목 등록'), a:has-text('품목 등록')",
|
|
|
|
|
"table": "table, [role='table'], [class*='table']",
|
|
|
|
|
"tableBody": "table tbody, [role='rowgroup']",
|
|
|
|
|
"tableRows": "table tbody tr, [role='row']",
|
|
|
|
|
"tableHeaders": "table thead th, [role='columnheader']",
|
|
|
|
|
"pagination": "[class*='pagination'], nav[aria-label*='pagination'], .flex.items-center.justify-between",
|
|
|
|
|
"statisticsCards": "[class*='card'], [class*='stat'], .grid > div",
|
|
|
|
|
"tabButtons": "[role='tablist'] button, [class*='tab'] button, button[class*='variant']",
|
|
|
|
|
"toast": "[class*='toast'], [role='alert'], [class*='Toastify']",
|
|
|
|
|
"modal": "[role='dialog'], [class*='modal'], [class*='Modal']",
|
|
|
|
|
"modalClose": "[role='dialog'] button[class*='close'], [role='dialog'] button:has-text('닫기'), [role='dialog'] button:has-text('취소')",
|
|
|
|
|
"confirmDialog": "[role='alertdialog'], [class*='confirm'], [class*='dialog']",
|
|
|
|
|
"confirmButton": "[role='alertdialog'] button:has-text('확인'), [role='dialog'] button:has-text('확인')",
|
|
|
|
|
"cancelButton": "[role='alertdialog'] button:has-text('취소'), [role='dialog'] button:has-text('취소')",
|
|
|
|
|
"saveButton": "button:has-text('저장'), button[type='submit']",
|
|
|
|
|
"formFields": "form input, form select, form textarea, [class*='form'] input",
|
|
|
|
|
"itemTypeSelect": "select[name*='type'], [class*='combobox'], button[aria-haspopup='listbox']",
|
|
|
|
|
"dropdown": "[role='listbox'], [class*='dropdown'], [class*='menu']",
|
|
|
|
|
"dropdownOption": "[role='option'], [class*='option'], li",
|
|
|
|
|
"pageTitle": "h1, h2, [class*='title'], [class*='heading']",
|
|
|
|
|
"actionButtons": "button:has-text('상세'), button:has-text('수정'), button:has-text('삭제')",
|
|
|
|
|
"detailButton": "button:has-text('상세 보기'), button:has-text('상세'), a:has-text('상세')",
|
|
|
|
|
"editButton": "button:has-text('수정'), a:has-text('수정')",
|
|
|
|
|
"deleteButton": "button:has-text('삭제')"
|
|
|
|
|
},
|
2026-01-30 21:47:29 +09:00
|
|
|
"navigation": {
|
|
|
|
|
"targetUrl": "/production/screen-production",
|
|
|
|
|
"urlPattern": "/production/screen-production|/ko/production/screen-production",
|
2026-02-06 06:18:53 +09:00
|
|
|
"menuHints": ["품목관리", "품목 관리", "생산관리"]
|
2026-01-30 21:47:29 +09:00
|
|
|
},
|
2026-01-30 10:50:38 +09:00
|
|
|
"menuNavigation": {
|
|
|
|
|
"level1": "생산관리",
|
|
|
|
|
"level2": "품목관리",
|
2026-02-04 21:59:56 +09:00
|
|
|
"expectedUrl": "/ko/production/screen-production",
|
|
|
|
|
"searchWithinParent": true,
|
|
|
|
|
"closeOtherMenus": true
|
|
|
|
|
},
|
|
|
|
|
"auth": {
|
|
|
|
|
"username": "TestUser5",
|
|
|
|
|
"password": "password123!"
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
"testData": {
|
|
|
|
|
"testProduct": {
|
|
|
|
|
"상품명": "테스트 프리미엄 스크린",
|
|
|
|
|
"품목명": "TEST-SCREEN-001",
|
|
|
|
|
"로트약자": "TSC",
|
|
|
|
|
"품목상태": "활성",
|
|
|
|
|
"비고": "E2E 테스트용 제품",
|
|
|
|
|
"인정번호": "TEST-CERT-2026-001"
|
|
|
|
|
},
|
|
|
|
|
"testConsumable": {
|
|
|
|
|
"품목명": "테스트 라벨",
|
|
|
|
|
"규격": "100x50mm",
|
|
|
|
|
"단위": "EA",
|
|
|
|
|
"비고": "E2E 테스트용 소모품"
|
|
|
|
|
},
|
|
|
|
|
"searchKeyword": "CS-001000"
|
|
|
|
|
},
|
|
|
|
|
"expectedAPIs": [
|
2026-02-06 06:18:53 +09:00
|
|
|
{"method": "GET", "endpoint": "/api/items", "description": "품목 목록 조회", "expectedStatus": 200},
|
|
|
|
|
{"method": "POST", "endpoint": "/api/items", "description": "품목 등록", "expectedStatus": 201},
|
|
|
|
|
{"method": "GET", "endpoint": "/api/items/:id", "description": "품목 상세 조회", "expectedStatus": 200},
|
|
|
|
|
{"method": "PATCH", "endpoint": "/api/items/:id", "description": "품목 수정", "expectedStatus": 200},
|
|
|
|
|
{"method": "DELETE", "endpoint": "/api/items/:id", "description": "품목 삭제", "expectedStatus": 200}
|
2026-01-30 10:50:38 +09:00
|
|
|
],
|
|
|
|
|
"steps": [
|
|
|
|
|
{
|
|
|
|
|
"step": 0,
|
2026-01-30 16:26:52 +09:00
|
|
|
"name": "사이드바 메뉴 전체 펼치기",
|
|
|
|
|
"description": "모두 펼치기 버튼을 클릭하여 전체 메뉴를 펼친 후 메뉴 탐색 준비",
|
2026-01-30 10:50:38 +09:00
|
|
|
"actions": [
|
2026-02-06 06:18:53 +09:00
|
|
|
{"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}
|
2026-01-30 10:50:38 +09:00
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 1,
|
|
|
|
|
"name": "2단계 메뉴 진입: 생산관리 > 품목관리",
|
|
|
|
|
"description": "사이드바를 스크롤하며 생산관리 > 품목관리 메뉴를 찾아 클릭",
|
|
|
|
|
"actions": [
|
2026-02-06 06:18:53 +09:00
|
|
|
{"type": "menu_navigate", "level1": "생산관리", "level2": "품목관리"},
|
|
|
|
|
{"type": "wait", "duration": 2000},
|
|
|
|
|
{"type": "verify_url", "pattern": "/production/screen-production"}
|
2026-01-30 10:50:38 +09:00
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 2,
|
|
|
|
|
"name": "통계 카드 표시 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "VERIFY",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const cards = document.querySelectorAll('[class*=\"card\"], [class*=\"stat\"], .grid > div'); const texts = ['전체', '제품', '부품', '부자재', '원자재', '소모품']; let found = 0; cards.forEach(c => { if(texts.some(t => c.innerText?.includes(t))) found++; }); return JSON.stringify({cardCount: cards.length, matchedTexts: found, pass: found >= 4}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 3,
|
|
|
|
|
"name": "품목 등록 버튼 표시 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "VERIFY",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "verify_element",
|
|
|
|
|
"selector": "button",
|
|
|
|
|
"textContains": "품목 등록",
|
|
|
|
|
"timeout": 5000
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 4,
|
|
|
|
|
"name": "검색 입력 필드 표시 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "VERIFY",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "verify_element",
|
|
|
|
|
"selector": "input[placeholder*='검색'], input[placeholder*='품목'], input[type='search']",
|
|
|
|
|
"timeout": 5000
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 5,
|
|
|
|
|
"name": "탭 필터 버튼 표시 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "VERIFY",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const tabs = ['전체', '제품', '부품', '부자재', '원자재', '소모품']; const buttons = Array.from(document.querySelectorAll('button')); let found = tabs.filter(t => buttons.some(b => b.innerText?.trim() === t)); return JSON.stringify({expected: tabs.length, found: found.length, tabs: found, pass: found.length >= 5}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 6,
|
|
|
|
|
"name": "데이터 테이블 헤더 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "VERIFY",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "verify_element",
|
|
|
|
|
"selector": "table thead, [role='table'] [role='columnheader'], table th",
|
|
|
|
|
"timeout": 5000
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const headers = document.querySelectorAll('table th, [role=\"columnheader\"]'); const texts = Array.from(headers).map(h => h.innerText?.trim()).filter(Boolean); return JSON.stringify({headerCount: headers.length, texts: texts.slice(0,10), pass: headers.length >= 5}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 7,
|
|
|
|
|
"name": "데이터 행 표시 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "VERIFY",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "wait_for_element",
|
|
|
|
|
"selector": "table tbody tr, [role='row']",
|
|
|
|
|
"timeout": 10000
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const rows = document.querySelectorAll('table tbody tr'); return JSON.stringify({rowCount: rows.length, pass: rows.length >= 1}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 8,
|
|
|
|
|
"name": "페이지네이션 표시 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "VERIFY",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const paginationTexts = ['전체', '개', '표시', 'of', 'page']; const body = document.body.innerText.toLowerCase(); const hasPagination = paginationTexts.some(t => body.includes(t.toLowerCase())); const navButtons = document.querySelectorAll('nav button, [class*=\"pagination\"] button, button[aria-label*=\"page\"]'); return JSON.stringify({hasPaginationText: hasPagination, buttonCount: navButtons.length, pass: hasPagination || navButtons.length > 0}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 9,
|
|
|
|
|
"name": "액션 버튼 표시 확인 (첫 번째 행)",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "VERIFY",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const row = document.querySelector('table tbody tr'); if(!row) return JSON.stringify({pass: false, error: 'no row'}); const buttons = row.querySelectorAll('button, a'); const hasDetail = Array.from(buttons).some(b => b.innerText?.includes('상세') || b.title?.includes('상세')); const hasEdit = Array.from(buttons).some(b => b.innerText?.includes('수정') || b.title?.includes('수정')); const hasDelete = Array.from(buttons).some(b => b.innerText?.includes('삭제') || b.title?.includes('삭제')); return JSON.stringify({hasDetail, hasEdit, hasDelete, buttonCount: buttons.length, pass: buttons.length >= 1}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 10,
|
2026-02-06 06:18:53 +09:00
|
|
|
"name": "검색 기능 테스트 - 검색어 입력",
|
|
|
|
|
"phase": "SEARCH",
|
2026-01-30 10:50:38 +09:00
|
|
|
"actions": [
|
2026-02-06 06:18:53 +09:00
|
|
|
{"type": "save_url", "variable": "url_before_search"},
|
2026-01-30 10:50:38 +09:00
|
|
|
{
|
2026-02-06 06:18:53 +09:00
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const rows = document.querySelectorAll('table tbody tr'); return rows.length; })()",
|
|
|
|
|
"saveAs": "beforeSearchCount"
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
2026-02-06 06:18:53 +09:00
|
|
|
"type": "fill",
|
|
|
|
|
"selector": "input[placeholder*='검색'], input[placeholder*='품목'], input[type='search']",
|
|
|
|
|
"value": "CS-001000"
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
2026-02-06 06:18:53 +09:00
|
|
|
{"type": "wait", "duration": 1500}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 11,
|
2026-02-06 06:18:53 +09:00
|
|
|
"name": "검색 결과 대기",
|
|
|
|
|
"phase": "SEARCH",
|
|
|
|
|
"actions": [
|
|
|
|
|
{"type": "wait", "duration": 1000},
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const rows = document.querySelectorAll('table tbody tr'); return JSON.stringify({rowCount: rows.length, pass: true}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 12,
|
|
|
|
|
"name": "검색 결과 데이터 검증",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "SEARCH",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const rows = document.querySelectorAll('table tbody tr'); const keyword = 'CS-001000'; let matchCount = 0; rows.forEach(r => { if(r.innerText?.includes(keyword) || r.innerText?.toLowerCase().includes(keyword.toLowerCase())) matchCount++; }); return JSON.stringify({totalRows: rows.length, matchingRows: matchCount, keyword, pass: rows.length === 0 || matchCount > 0}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 13,
|
|
|
|
|
"name": "검색 초기화",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "SEARCH",
|
2026-01-30 10:50:38 +09:00
|
|
|
"actions": [
|
|
|
|
|
{
|
2026-02-06 06:18:53 +09:00
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const input = document.querySelector('input[placeholder*=\"검색\"], input[placeholder*=\"품목\"], input[type=\"search\"]'); if(input) { input.value = ''; input.dispatchEvent(new Event('input', {bubbles: true})); input.dispatchEvent(new Event('change', {bubbles: true})); } return JSON.stringify({cleared: !!input}); })()"
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
2026-02-06 06:18:53 +09:00
|
|
|
{"type": "wait", "duration": 1000}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 14,
|
|
|
|
|
"name": "탭 필터 테스트 - 제품 탭 클릭",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "FILTER",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "click",
|
|
|
|
|
"selector": "button",
|
|
|
|
|
"textContains": "제품",
|
|
|
|
|
"textExact": true
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 1000}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 15,
|
|
|
|
|
"name": "제품 탭 필터 결과 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "FILTER",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const rows = document.querySelectorAll('table tbody tr'); let productCount = 0; rows.forEach(r => { const cells = r.querySelectorAll('td'); cells.forEach(c => { if(c.innerText?.trim() === '제품') productCount++; }); }); return JSON.stringify({rowCount: rows.length, productMatches: productCount, pass: rows.length >= 0}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 16,
|
|
|
|
|
"name": "탭 필터 테스트 - 소모품 탭 클릭",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "FILTER",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "click",
|
|
|
|
|
"selector": "button",
|
|
|
|
|
"textContains": "소모품",
|
|
|
|
|
"textExact": true
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 1000}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 17,
|
|
|
|
|
"name": "소모품 탭 필터 결과 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "FILTER",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const rows = document.querySelectorAll('table tbody tr'); return JSON.stringify({rowCount: rows.length, pass: true}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 18,
|
|
|
|
|
"name": "탭 필터 초기화 - 전체 탭 클릭",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "FILTER",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "click",
|
|
|
|
|
"selector": "button",
|
|
|
|
|
"textContains": "전체",
|
|
|
|
|
"textExact": true
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 1000}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 19,
|
|
|
|
|
"name": "페이지네이션 테스트 - 2페이지 이동",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "PAGINATION",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const btn = Array.from(document.querySelectorAll('button, a')).find(b => b.innerText?.trim() === '2' || b.getAttribute('aria-label')?.includes('2')); if(btn) { btn.click(); return JSON.stringify({clicked: true}); } return JSON.stringify({clicked: false, error: 'page 2 button not found'}); })()"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 1500}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 20,
|
|
|
|
|
"name": "2페이지 데이터 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "PAGINATION",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const rows = document.querySelectorAll('table tbody tr'); const pageText = document.body.innerText; const hasPageInfo = pageText.includes('21') || pageText.includes('페이지') || pageText.includes('page'); return JSON.stringify({rowCount: rows.length, hasPageInfo, pass: rows.length >= 1}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 21,
|
|
|
|
|
"name": "다음 페이지 버튼 클릭",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "PAGINATION",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const btn = Array.from(document.querySelectorAll('button, a')).find(b => b.innerText?.includes('다음') || b.getAttribute('aria-label')?.includes('Next') || b.innerText?.includes('>')); if(btn) { btn.click(); return JSON.stringify({clicked: true}); } return JSON.stringify({clicked: false}); })()"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 1500}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 22,
|
|
|
|
|
"name": "3페이지 데이터 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "PAGINATION",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const rows = document.querySelectorAll('table tbody tr'); return JSON.stringify({rowCount: rows.length, pass: rows.length >= 1}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 23,
|
|
|
|
|
"name": "1페이지로 복귀",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "PAGINATION",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const btn = Array.from(document.querySelectorAll('button, a')).find(b => b.innerText?.trim() === '1' || b.getAttribute('aria-label')?.includes('1')); if(btn) { btn.click(); return JSON.stringify({clicked: true}); } return JSON.stringify({clicked: false}); })()"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 1500}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 24,
|
|
|
|
|
"name": "품목 등록 페이지 이동",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "click",
|
|
|
|
|
"selector": "button, a",
|
|
|
|
|
"textContains": "품목 등록"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 2000}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 25,
|
|
|
|
|
"name": "품목 등록 페이지 로딩 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const heading = document.querySelector('h1, h2, [class*=\"title\"]'); const text = heading?.innerText || document.body.innerText.substring(0, 500); const isCreatePage = text.includes('등록') || text.includes('추가') || text.includes('Create'); return JSON.stringify({headingText: heading?.innerText, isCreatePage, pass: isCreatePage}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 26,
|
|
|
|
|
"name": "초기 버튼 상태 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const cancelBtn = Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('취소')); const saveBtn = Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('저장')); return JSON.stringify({cancelExists: !!cancelBtn, saveExists: !!saveBtn, saveDisabled: saveBtn?.disabled, pass: !!cancelBtn && !!saveBtn}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 27,
|
|
|
|
|
"name": "품목 유형 선택 전 경고 메시지 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const text = document.body.innerText; const hasWarning = text.includes('유형') && (text.includes('선택') || text.includes('먼저')); return JSON.stringify({hasWarning, pass: true}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 28,
|
|
|
|
|
"name": "품목 유형 필드 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "verify_element",
|
|
|
|
|
"selector": "select, [role='combobox'], button[aria-haspopup='listbox'], [class*='select']",
|
|
|
|
|
"timeout": 5000
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 29,
|
2026-02-06 06:18:53 +09:00
|
|
|
"name": "제품(Finished Goods) 등록 테스트 시작 - 품목 유형 드롭다운 열기",
|
|
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const selects = document.querySelectorAll('select, [role=\"combobox\"], button[aria-haspopup=\"listbox\"]'); const typeSelect = Array.from(selects).find(s => { const label = s.closest('label, [class*=\"field\"], div')?.innerText; return label?.includes('유형') || label?.includes('타입'); }); if(typeSelect) { typeSelect.click(); return JSON.stringify({clicked: true}); } if(selects[0]) { selects[0].click(); return JSON.stringify({clicked: true, firstSelect: true}); } return JSON.stringify({clicked: false}); })()"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 500}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 30,
|
|
|
|
|
"name": "제품 옵션 선택",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const options = document.querySelectorAll('[role=\"option\"], option, li[class*=\"option\"], [class*=\"menu\"] li'); const productOpt = Array.from(options).find(o => o.innerText?.includes('제품') || o.innerText?.includes('Finished')); if(productOpt) { productOpt.click(); return JSON.stringify({selected: true, text: productOpt.innerText}); } return JSON.stringify({selected: false, optionCount: options.length}); })()"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 1000}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 31,
|
|
|
|
|
"name": "제품 입력 필드 표시 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const inputs = document.querySelectorAll('input:not([type=\"hidden\"]), textarea, select'); const labels = document.body.innerText; const hasProductFields = labels.includes('상품명') || labels.includes('품목명') || labels.includes('품목코드'); return JSON.stringify({inputCount: inputs.length, hasProductFields, pass: inputs.length >= 3 && hasProductFields}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 32,
|
|
|
|
|
"name": "상품명 입력",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const inputs = document.querySelectorAll('input, textarea'); const productInput = Array.from(inputs).find(i => { const label = i.closest('label, div, [class*=\"field\"]')?.innerText; return label?.includes('상품명') && !label?.includes('품목명'); }); if(productInput) { productInput.value = '테스트 프리미엄 스크린'; productInput.dispatchEvent(new Event('input', {bubbles: true})); productInput.dispatchEvent(new Event('change', {bubbles: true})); return JSON.stringify({filled: true}); } return JSON.stringify({filled: false}); })()"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 33,
|
|
|
|
|
"name": "품목명 입력",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const inputs = document.querySelectorAll('input, textarea'); const itemInput = Array.from(inputs).find(i => { const label = i.closest('label, div, [class*=\"field\"]')?.innerText; return label?.includes('품목명') && !i.disabled && !i.readOnly; }); if(itemInput) { itemInput.value = 'TEST-SCREEN-001'; itemInput.dispatchEvent(new Event('input', {bubbles: true})); itemInput.dispatchEvent(new Event('change', {bubbles: true})); return JSON.stringify({filled: true}); } return JSON.stringify({filled: false}); })()"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 34,
|
|
|
|
|
"name": "품목코드 자동생성 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const inputs = document.querySelectorAll('input'); const codeInput = Array.from(inputs).find(i => { const label = i.closest('label, div, [class*=\"field\"]')?.innerText; return label?.includes('품목코드') || label?.includes('코드'); }); if(codeInput) { return JSON.stringify({value: codeInput.value, disabled: codeInput.disabled, readOnly: codeInput.readOnly, pass: true}); } return JSON.stringify({pass: true, note: 'code field may be auto-generated'}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 35,
|
|
|
|
|
"name": "로트 약자 입력",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const inputs = document.querySelectorAll('input'); const lotInput = Array.from(inputs).find(i => { const label = i.closest('label, div, [class*=\"field\"]')?.innerText; return label?.includes('로트') || label?.includes('Lot'); }); if(lotInput) { lotInput.value = 'TSC'; lotInput.dispatchEvent(new Event('input', {bubbles: true})); return JSON.stringify({filled: true}); } return JSON.stringify({filled: false, note: 'lot field not found or optional'}); })()"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 36,
|
2026-02-06 06:18:53 +09:00
|
|
|
"name": "품목상태 드롭다운 열기",
|
|
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const selects = document.querySelectorAll('select, [role=\"combobox\"], button[aria-haspopup=\"listbox\"]'); const statusSelect = Array.from(selects).find(s => { const label = s.closest('label, div, [class*=\"field\"]')?.innerText; return label?.includes('상태') || label?.includes('Status'); }); if(statusSelect) { statusSelect.click(); return JSON.stringify({clicked: true}); } return JSON.stringify({clicked: false}); })()"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 500}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 37,
|
|
|
|
|
"name": "품목상태 '활성' 선택",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const options = document.querySelectorAll('[role=\"option\"], option, li[class*=\"option\"]'); const activeOpt = Array.from(options).find(o => o.innerText?.includes('활성') || o.innerText?.includes('Active')); if(activeOpt) { activeOpt.click(); return JSON.stringify({selected: true}); } return JSON.stringify({selected: false}); })()"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 300}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 38,
|
|
|
|
|
"name": "비고 입력",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const inputs = document.querySelectorAll('input, textarea'); const noteInput = Array.from(inputs).find(i => { const label = i.closest('label, div, [class*=\"field\"]')?.innerText; return label?.includes('비고') || label?.includes('Note') || label?.includes('메모'); }); if(noteInput) { noteInput.value = 'E2E 테스트용 제품'; noteInput.dispatchEvent(new Event('input', {bubbles: true})); return JSON.stringify({filled: true}); } return JSON.stringify({filled: false}); })()"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 39,
|
|
|
|
|
"name": "인정번호 입력",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const inputs = document.querySelectorAll('input'); const certInput = Array.from(inputs).find(i => { const label = i.closest('label, div, [class*=\"field\"]')?.innerText; return label?.includes('인정') || label?.includes('Cert'); }); if(certInput) { certInput.value = 'TEST-CERT-2026-001'; certInput.dispatchEvent(new Event('input', {bubbles: true})); return JSON.stringify({filled: true}); } return JSON.stringify({filled: false, note: 'cert field may be optional'}); })()"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 40,
|
|
|
|
|
"name": "저장 버튼 활성화 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const saveBtn = Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('저장') || b.innerText?.includes('Save')); if(saveBtn) { return JSON.stringify({exists: true, disabled: saveBtn.disabled, pass: true}); } return JSON.stringify({exists: false, pass: false}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 41,
|
2026-02-06 06:18:53 +09:00
|
|
|
"name": "제품 등록 - URL 저장",
|
|
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{"type": "save_url", "variable": "url_before_product_save"}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 42,
|
|
|
|
|
"name": "제품 등록 - 저장 버튼 클릭",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "click",
|
|
|
|
|
"selector": "button",
|
|
|
|
|
"textContains": "저장"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 2000}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 43,
|
2026-02-06 06:18:53 +09:00
|
|
|
"name": "제품 등록 - URL 변경 여부 확인",
|
|
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const url = window.location.href; const has404 = url.includes('404') || document.body.innerText.includes('404') || document.body.innerText.includes('Not Found'); return JSON.stringify({url, has404, pass: !has404}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 44,
|
2026-02-06 06:18:53 +09:00
|
|
|
"name": "제품 등록 - 에러 페이지 텍스트 감지",
|
|
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const text = document.body.innerText; const errorTexts = ['404', 'Not Found', '페이지를 찾을 수 없', '500', '서버 에러']; const hasError = errorTexts.some(e => text.includes(e)); return JSON.stringify({hasError, pass: !hasError}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 45,
|
2026-02-06 06:18:53 +09:00
|
|
|
"name": "제품 등록 - 성공 토스트 메시지 확인",
|
|
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const toasts = document.querySelectorAll('[class*=\"toast\"], [role=\"alert\"], [class*=\"Toastify\"], [class*=\"notification\"]'); const successTexts = ['등록', '저장', '성공', 'success', '완료']; let found = false; toasts.forEach(t => { if(successTexts.some(s => t.innerText?.toLowerCase().includes(s.toLowerCase()))) found = true; }); if(!found) { found = successTexts.some(s => document.body.innerText.includes(s)); } return JSON.stringify({toastFound: found, pass: true}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 46,
|
|
|
|
|
"name": "제품 등록 - 목록 페이지 복귀 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const heading = document.querySelector('h1, h2, [class*=\"title\"]'); const isListPage = heading?.innerText?.includes('관리') || heading?.innerText?.includes('목록') || document.querySelector('table'); return JSON.stringify({heading: heading?.innerText, isListPage: !!isListPage, pass: true}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 47,
|
|
|
|
|
"name": "제품 등록 - 신규 품목 검색",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "fill",
|
|
|
|
|
"selector": "input[placeholder*='검색'], input[placeholder*='품목'], input[type='search']",
|
|
|
|
|
"value": "TEST-SCREEN-001"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 1500}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 48,
|
|
|
|
|
"name": "제품 등록 - 신규 품목 표시 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{"type": "wait", "duration": 1000}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 49,
|
|
|
|
|
"name": "제품 등록 - 데이터 검증",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const text = document.body.innerText; const hasTestProduct = text.includes('TEST-SCREEN-001') || text.includes('테스트 프리미엄 스크린'); return JSON.stringify({hasTestProduct, pass: true}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 50,
|
2026-02-06 06:18:53 +09:00
|
|
|
"name": "소모품 등록 테스트 시작 - 품목 등록 버튼 클릭",
|
|
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "click",
|
|
|
|
|
"selector": "button, a",
|
|
|
|
|
"textContains": "품목 등록"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 2000}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 51,
|
2026-02-06 06:18:53 +09:00
|
|
|
"name": "품목 유형 드롭다운 열기",
|
|
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const selects = document.querySelectorAll('select, [role=\"combobox\"], button[aria-haspopup=\"listbox\"]'); if(selects[0]) { selects[0].click(); return JSON.stringify({clicked: true}); } return JSON.stringify({clicked: false}); })()"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 500}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 52,
|
|
|
|
|
"name": "소모품 옵션 선택",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const options = document.querySelectorAll('[role=\"option\"], option, li[class*=\"option\"]'); const consumableOpt = Array.from(options).find(o => o.innerText?.includes('소모품') || o.innerText?.includes('Consumable')); if(consumableOpt) { consumableOpt.click(); return JSON.stringify({selected: true, text: consumableOpt.innerText}); } return JSON.stringify({selected: false}); })()"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 1000}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 53,
|
|
|
|
|
"name": "소모품 입력 필드 표시 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const inputs = document.querySelectorAll('input:not([type=\"hidden\"]), textarea'); const labels = document.body.innerText; const hasFields = labels.includes('품목명') || labels.includes('규격') || labels.includes('단위'); return JSON.stringify({inputCount: inputs.length, hasFields, pass: inputs.length >= 2}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 54,
|
|
|
|
|
"name": "소모품 품목명 입력",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const inputs = document.querySelectorAll('input, textarea'); const itemInput = Array.from(inputs).find(i => { const label = i.closest('label, div, [class*=\"field\"]')?.innerText; return label?.includes('품목명') && !i.disabled; }); if(itemInput) { itemInput.value = '테스트 라벨'; itemInput.dispatchEvent(new Event('input', {bubbles: true})); return JSON.stringify({filled: true}); } return JSON.stringify({filled: false}); })()"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 55,
|
|
|
|
|
"name": "소모품 규격 입력",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const inputs = document.querySelectorAll('input, textarea'); const specInput = Array.from(inputs).find(i => { const label = i.closest('label, div, [class*=\"field\"]')?.innerText; return label?.includes('규격') || label?.includes('사양') || label?.includes('Spec'); }); if(specInput) { specInput.value = '100x50mm'; specInput.dispatchEvent(new Event('input', {bubbles: true})); return JSON.stringify({filled: true}); } return JSON.stringify({filled: false}); })()"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 56,
|
|
|
|
|
"name": "소모품 품목코드 자동생성 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ return JSON.stringify({pass: true, note: 'auto-generated code check'}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 57,
|
2026-02-06 06:18:53 +09:00
|
|
|
"name": "소모품 단위 드롭다운 열기",
|
|
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const selects = document.querySelectorAll('select, [role=\"combobox\"], button[aria-haspopup=\"listbox\"]'); const unitSelect = Array.from(selects).find(s => { const label = s.closest('label, div, [class*=\"field\"]')?.innerText; return label?.includes('단위') || label?.includes('Unit'); }); if(unitSelect) { unitSelect.click(); return JSON.stringify({clicked: true}); } return JSON.stringify({clicked: false}); })()"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 500}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 58,
|
|
|
|
|
"name": "단위 'EA' 선택",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const options = document.querySelectorAll('[role=\"option\"], option, li[class*=\"option\"]'); const eaOpt = Array.from(options).find(o => o.innerText?.includes('EA') || o.value === 'EA'); if(eaOpt) { eaOpt.click(); return JSON.stringify({selected: true}); } return JSON.stringify({selected: false}); })()"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 300}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 59,
|
|
|
|
|
"name": "소모품 비고 입력",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const inputs = document.querySelectorAll('input, textarea'); const noteInput = Array.from(inputs).find(i => { const label = i.closest('label, div, [class*=\"field\"]')?.innerText; return label?.includes('비고'); }); if(noteInput) { noteInput.value = 'E2E 테스트용 소모품'; noteInput.dispatchEvent(new Event('input', {bubbles: true})); return JSON.stringify({filled: true}); } return JSON.stringify({filled: false}); })()"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 60,
|
2026-02-06 06:18:53 +09:00
|
|
|
"name": "소모품 등록 - URL 저장",
|
|
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{"type": "save_url", "variable": "url_before_consumable_save"}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 61,
|
|
|
|
|
"name": "소모품 등록 - 저장 버튼 클릭",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "click",
|
|
|
|
|
"selector": "button",
|
|
|
|
|
"textContains": "저장"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 2000}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 62,
|
2026-02-06 06:18:53 +09:00
|
|
|
"name": "소모품 등록 - URL 변경 여부 확인",
|
|
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const url = window.location.href; const has404 = url.includes('404') || document.body.innerText.includes('404'); return JSON.stringify({url, has404, pass: !has404}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 63,
|
2026-02-06 06:18:53 +09:00
|
|
|
"name": "소모품 등록 - 에러 페이지 텍스트 감지",
|
|
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const text = document.body.innerText; const errorTexts = ['404', 'Not Found', '500']; const hasError = errorTexts.some(e => text.includes(e)); return JSON.stringify({hasError, pass: !hasError}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 64,
|
|
|
|
|
"name": "소모품 등록 - 성공 토스트 메시지 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const toasts = document.querySelectorAll('[class*=\"toast\"], [role=\"alert\"]'); const found = Array.from(toasts).some(t => t.innerText?.includes('등록') || t.innerText?.includes('저장')); return JSON.stringify({toastFound: found, pass: true}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 65,
|
|
|
|
|
"name": "소모품 등록 - 신규 품목 검색",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "fill",
|
|
|
|
|
"selector": "input[placeholder*='검색'], input[placeholder*='품목'], input[type='search']",
|
|
|
|
|
"value": "테스트 라벨"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 1500}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 66,
|
|
|
|
|
"name": "소모품 등록 - 신규 품목 표시 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "CREATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{"type": "wait", "duration": 1000},
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const text = document.body.innerText; const found = text.includes('테스트 라벨'); return JSON.stringify({found, pass: true}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 67,
|
2026-02-06 06:18:53 +09:00
|
|
|
"name": "상세 보기 기능 테스트 - 검색 초기화",
|
|
|
|
|
"phase": "READ",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const input = document.querySelector('input[placeholder*=\"검색\"], input[type=\"search\"]'); if(input) { input.value = ''; input.dispatchEvent(new Event('input', {bubbles: true})); } return JSON.stringify({cleared: !!input}); })()"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 1000}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 68,
|
|
|
|
|
"name": "상세 보기 버튼 클릭 (첫 번째 행)",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "READ",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const row = document.querySelector('table tbody tr'); if(!row) return JSON.stringify({clicked: false, error: 'no row'}); const detailBtn = Array.from(row.querySelectorAll('button, a')).find(b => b.innerText?.includes('상세') || b.title?.includes('상세') || b.getAttribute('aria-label')?.includes('상세')); if(detailBtn) { detailBtn.click(); return JSON.stringify({clicked: true}); } row.click(); return JSON.stringify({clicked: true, rowClick: true}); })()"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 1500}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 69,
|
|
|
|
|
"name": "상세 정보 표시 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "READ",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const modal = document.querySelector('[role=\"dialog\"], [class*=\"modal\"], [class*=\"Modal\"]'); const hasDetail = document.body.innerText.includes('품목코드') || document.body.innerText.includes('품목명') || document.body.innerText.includes('상세'); return JSON.stringify({modalOpen: !!modal, hasDetail, pass: hasDetail || !!modal}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 70,
|
|
|
|
|
"name": "상세 보기 닫기",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "READ",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const closeBtn = document.querySelector('[role=\"dialog\"] button[class*=\"close\"], [role=\"dialog\"] button:first-child, button[aria-label=\"닫기\"]'); if(closeBtn) { closeBtn.click(); return JSON.stringify({clicked: true}); } const escEvent = new KeyboardEvent('keydown', {key: 'Escape', keyCode: 27, bubbles: true}); document.dispatchEvent(escEvent); return JSON.stringify({escaped: true}); })()"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 500}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 71,
|
|
|
|
|
"name": "수정 기능 테스트 - 등록한 제품 검색",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "UPDATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "fill",
|
|
|
|
|
"selector": "input[placeholder*='검색'], input[placeholder*='품목'], input[type='search']",
|
|
|
|
|
"value": "TEST-SCREEN-001"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 1500}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 72,
|
|
|
|
|
"name": "수정 버튼 클릭",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "UPDATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const rows = document.querySelectorAll('table tbody tr'); for(const row of rows) { if(row.innerText?.includes('TEST-SCREEN-001')) { const editBtn = Array.from(row.querySelectorAll('button, a')).find(b => b.innerText?.includes('수정') || b.title?.includes('수정')); if(editBtn) { editBtn.click(); return JSON.stringify({clicked: true}); } } } const anyEditBtn = document.querySelector('button:has-text(\"수정\")') || Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('수정')); if(anyEditBtn) { anyEditBtn.click(); return JSON.stringify({clicked: true, anyBtn: true}); } return JSON.stringify({clicked: false}); })()"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 2000}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 73,
|
|
|
|
|
"name": "수정 페이지 로딩 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "UPDATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const heading = document.querySelector('h1, h2, [class*=\"title\"]'); const url = window.location.href; const isEditPage = heading?.innerText?.includes('수정') || url.includes('edit') || url.includes('mode=edit'); return JSON.stringify({heading: heading?.innerText, url, isEditPage, pass: true}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 74,
|
|
|
|
|
"name": "기존 데이터 로드 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "UPDATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const inputs = document.querySelectorAll('input:not([type=\"hidden\"])'); const filledInputs = Array.from(inputs).filter(i => i.value?.length > 0); return JSON.stringify({totalInputs: inputs.length, filledInputs: filledInputs.length, pass: filledInputs.length >= 1}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 75,
|
|
|
|
|
"name": "비고 필드 수정",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "UPDATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const inputs = document.querySelectorAll('input, textarea'); const noteInput = Array.from(inputs).find(i => { const label = i.closest('label, div, [class*=\"field\"]')?.innerText; return label?.includes('비고'); }); if(noteInput) { noteInput.value = 'E2E 테스트용 제품 - 수정됨'; noteInput.dispatchEvent(new Event('input', {bubbles: true})); return JSON.stringify({modified: true}); } return JSON.stringify({modified: false}); })()"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 76,
|
|
|
|
|
"name": "수정 저장 - URL 저장",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "UPDATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{"type": "save_url", "variable": "url_before_edit_save"}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 77,
|
|
|
|
|
"name": "수정 저장 버튼 클릭",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "UPDATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "click",
|
|
|
|
|
"selector": "button",
|
|
|
|
|
"textContains": "저장"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 2000}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 78,
|
2026-02-06 06:18:53 +09:00
|
|
|
"name": "수정 저장 - URL 변경 여부 확인",
|
|
|
|
|
"phase": "UPDATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const url = window.location.href; const has404 = url.includes('404') || document.body.innerText.includes('404'); return JSON.stringify({url, has404, pass: !has404}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 79,
|
|
|
|
|
"name": "수정 저장 - 성공 토스트 메시지 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "UPDATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const toasts = document.querySelectorAll('[class*=\"toast\"], [role=\"alert\"]'); const found = Array.from(toasts).some(t => t.innerText?.includes('수정') || t.innerText?.includes('저장')); return JSON.stringify({toastFound: found, pass: true}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 80,
|
|
|
|
|
"name": "수정된 데이터 확인 - 제품 검색",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "UPDATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "fill",
|
|
|
|
|
"selector": "input[placeholder*='검색'], input[placeholder*='품목'], input[type='search']",
|
|
|
|
|
"value": "TEST-SCREEN-001"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 1500}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 81,
|
|
|
|
|
"name": "수정된 데이터 확인 - 상세보기",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "UPDATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const row = document.querySelector('table tbody tr'); if(!row) return JSON.stringify({clicked: false}); const detailBtn = Array.from(row.querySelectorAll('button, a')).find(b => b.innerText?.includes('상세')); if(detailBtn) { detailBtn.click(); return JSON.stringify({clicked: true}); } row.click(); return JSON.stringify({clicked: true, rowClick: true}); })()"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 1500}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 82,
|
|
|
|
|
"name": "수정된 비고 내용 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "UPDATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const text = document.body.innerText; const hasModified = text.includes('수정됨') || text.includes('E2E'); return JSON.stringify({hasModified, pass: true}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 83,
|
|
|
|
|
"name": "상세 모달 닫기",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "UPDATE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{"type": "close_modal"},
|
|
|
|
|
{"type": "wait", "duration": 500}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 84,
|
|
|
|
|
"name": "삭제 기능 테스트 - 소모품 검색",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "DELETE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "fill",
|
|
|
|
|
"selector": "input[placeholder*='검색'], input[placeholder*='품목'], input[type='search']",
|
|
|
|
|
"value": "테스트 라벨"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 1500}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 85,
|
|
|
|
|
"name": "삭제 버튼 클릭",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "DELETE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const rows = document.querySelectorAll('table tbody tr'); for(const row of rows) { if(row.innerText?.includes('테스트 라벨')) { const deleteBtn = Array.from(row.querySelectorAll('button')).find(b => b.innerText?.includes('삭제')); if(deleteBtn) { deleteBtn.click(); return JSON.stringify({clicked: true}); } } } return JSON.stringify({clicked: false}); })()"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 1000}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 86,
|
|
|
|
|
"name": "삭제 확인 다이얼로그 검증",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "DELETE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const dialog = document.querySelector('[role=\"alertdialog\"], [role=\"dialog\"], [class*=\"confirm\"], [class*=\"dialog\"]'); const text = document.body.innerText; const hasConfirm = dialog || text.includes('삭제') && (text.includes('확인') || text.includes('하시겠습니까')); return JSON.stringify({dialogOpen: !!dialog, hasConfirm, pass: hasConfirm}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 87,
|
|
|
|
|
"name": "삭제 취소 테스트 - 취소 버튼 클릭",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "DELETE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const cancelBtn = Array.from(document.querySelectorAll('button')).find(b => b.innerText?.includes('취소') || b.innerText?.includes('Cancel')); if(cancelBtn) { cancelBtn.click(); return JSON.stringify({clicked: true}); } return JSON.stringify({clicked: false}); })()"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 500}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 88,
|
|
|
|
|
"name": "삭제 취소 확인 - 품목이 여전히 존재함",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "DELETE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const text = document.body.innerText; const exists = text.includes('테스트 라벨'); return JSON.stringify({exists, pass: exists}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 89,
|
|
|
|
|
"name": "삭제 재시도 - 삭제 버튼 클릭",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "DELETE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const rows = document.querySelectorAll('table tbody tr'); for(const row of rows) { if(row.innerText?.includes('테스트 라벨')) { const deleteBtn = Array.from(row.querySelectorAll('button')).find(b => b.innerText?.includes('삭제')); if(deleteBtn) { deleteBtn.click(); return JSON.stringify({clicked: true}); } } } return JSON.stringify({clicked: false}); })()"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 1000}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 90,
|
|
|
|
|
"name": "삭제 확인 버튼 클릭",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "DELETE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "click_dialog_confirm"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 2000}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 91,
|
2026-02-06 06:18:53 +09:00
|
|
|
"name": "소모품 삭제 - URL 변경 여부 확인",
|
|
|
|
|
"phase": "DELETE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const url = window.location.href; const has404 = url.includes('404'); return JSON.stringify({url, has404, pass: !has404}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 92,
|
|
|
|
|
"name": "소모품 삭제 - 성공 토스트 메시지 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "DELETE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const toasts = document.querySelectorAll('[class*=\"toast\"], [role=\"alert\"]'); const found = Array.from(toasts).some(t => t.innerText?.includes('삭제')); return JSON.stringify({toastFound: found, pass: true}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 93,
|
|
|
|
|
"name": "소모품 삭제 확인 - 목록에서 사라짐",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "DELETE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{"type": "wait", "duration": 1000},
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const text = document.body.innerText; const notExists = !text.includes('테스트 라벨'); return JSON.stringify({notExists, pass: true}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 94,
|
|
|
|
|
"name": "제품 삭제 - 제품 검색",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "DELETE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "fill",
|
|
|
|
|
"selector": "input[placeholder*='검색'], input[placeholder*='품목'], input[type='search']",
|
|
|
|
|
"value": "TEST-SCREEN-001"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 1500}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 95,
|
|
|
|
|
"name": "제품 삭제 버튼 클릭",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "DELETE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const rows = document.querySelectorAll('table tbody tr'); for(const row of rows) { if(row.innerText?.includes('TEST-SCREEN-001')) { const deleteBtn = Array.from(row.querySelectorAll('button')).find(b => b.innerText?.includes('삭제')); if(deleteBtn) { deleteBtn.click(); return JSON.stringify({clicked: true}); } } } return JSON.stringify({clicked: false}); })()"
|
|
|
|
|
},
|
|
|
|
|
{"type": "wait", "duration": 1000}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 96,
|
|
|
|
|
"name": "제품 삭제 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "DELETE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{"type": "click_dialog_confirm"},
|
|
|
|
|
{"type": "wait", "duration": 2000}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 97,
|
|
|
|
|
"name": "제품 삭제 - URL 변경 여부 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "DELETE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const url = window.location.href; const has404 = url.includes('404'); return JSON.stringify({url, has404, pass: !has404}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 98,
|
|
|
|
|
"name": "제품 삭제 - 성공 토스트 메시지 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "DELETE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const toasts = document.querySelectorAll('[class*=\"toast\"], [role=\"alert\"]'); const found = Array.from(toasts).some(t => t.innerText?.includes('삭제')); return JSON.stringify({toastFound: found, pass: true}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 99,
|
|
|
|
|
"name": "제품 삭제 확인 - 목록에서 사라짐",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "DELETE",
|
|
|
|
|
"actions": [
|
|
|
|
|
{"type": "wait", "duration": 1000},
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const text = document.body.innerText; const notExists = !text.includes('TEST-SCREEN-001'); return JSON.stringify({notExists, pass: true}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"step": 100,
|
|
|
|
|
"name": "최종 테스트 완료 확인",
|
2026-02-06 06:18:53 +09:00
|
|
|
"phase": "VERIFY",
|
|
|
|
|
"actions": [
|
|
|
|
|
{
|
|
|
|
|
"type": "evaluate",
|
|
|
|
|
"script": "(function(){ const hasTitle = document.querySelector('h1, h2')?.innerText?.includes('품목') || document.body.innerText.includes('품목 관리'); const hasTable = !!document.querySelector('table'); const hasPagination = document.body.innerText.includes('전체') || document.body.innerText.includes('페이지'); return JSON.stringify({hasTitle, hasTable, hasPagination, pass: hasTitle && hasTable}); })()",
|
|
|
|
|
"expected": "pass:true"
|
|
|
|
|
}
|
|
|
|
|
]
|
2026-01-30 10:50:38 +09:00
|
|
|
}
|
|
|
|
|
]
|
2026-02-06 06:18:53 +09:00
|
|
|
}
|