/** * 메뉴 복사 자동화 스크립트 * * 실행: npx ts-node scripts/menu-copy-automation.ts * 또는: npx playwright test scripts/menu-copy-automation.ts (playwright test 모드) * * 요구사항: playwright 설치 (npm install playwright) */ import { chromium, type Browser, type Page } from "playwright"; import * as fs from "fs"; import * as path from "path"; const BASE_URL = "http://localhost:9771"; const SCREENSHOT_DIR = path.join(__dirname, "../screenshots-menu-copy"); // 스크린샷 저장 async function takeScreenshot(page: Page, stepName: string): Promise { if (!fs.existsSync(SCREENSHOT_DIR)) { fs.mkdirSync(SCREENSHOT_DIR, { recursive: true }); } const filename = `${Date.now()}_${stepName}.png`; const filepath = path.join(SCREENSHOT_DIR, filename); await page.screenshot({ path: filepath, fullPage: true }); console.log(`[스크린샷] ${stepName} -> ${filepath}`); return filepath; } async function main() { let browser: Browser | null = null; const screenshots: { step: string; path: string }[] = []; try { console.log("=== 메뉴 복사 자동화 시작 ===\n"); browser = await chromium.launch({ headless: false }); const context = await browser.newContext({ viewport: { width: 1280, height: 900 }, ignoreHTTPSErrors: true, }); const page = await context.newPage(); // 1. 로그인 console.log("1. 로그인 페이지 이동..."); await page.goto(`${BASE_URL}/login`, { waitUntil: "networkidle" }); await takeScreenshot(page, "01_login_page").then((p) => screenshots.push({ step: "로그인 페이지", path: p }) ); await page.fill('#userId', "admin"); await page.fill('#password', "1234"); await page.click('button[type="submit"]'); await page.waitForTimeout(3000); await takeScreenshot(page, "02_after_login").then((p) => screenshots.push({ step: "로그인 후", path: p }) ); // 로그인 실패 시 wace 계정 시도 (admin이 DB에 없을 수 있음) const currentUrl = page.url(); if (currentUrl.includes("/login")) { console.log("admin 로그인 실패, wace 계정으로 재시도..."); await page.fill('#userId', "wace"); await page.fill('#password', "1234"); await page.click('button[type="submit"]'); await page.waitForTimeout(3000); } // 2. 메뉴 관리 페이지로 이동 console.log("2. 메뉴 관리 페이지 이동..."); await page.goto(`${BASE_URL}/admin/menu`, { waitUntil: "networkidle" }); await page.waitForTimeout(2000); await takeScreenshot(page, "03_menu_page").then((p) => screenshots.push({ step: "메뉴 관리 페이지", path: p }) ); // 3. 회사 선택 - 탑씰 (COMPANY_7) console.log("3. 회사 선택: 탑씰 (COMPANY_7)..."); const companyDropdown = page.locator('.company-dropdown button, button:has(svg)').first(); await companyDropdown.click(); await page.waitForTimeout(500); const topsealOption = page.getByText("탑씰", { exact: false }).first(); await topsealOption.click(); await page.waitForTimeout(1500); await takeScreenshot(page, "04_company_selected").then((p) => screenshots.push({ step: "탑씰 선택 후", path: p }) ); // 4. "사용자" 메뉴 찾기 및 복사 버튼 클릭 console.log("4. 사용자 메뉴 찾기 및 복사 버튼 클릭..."); const userMenuRow = page.locator('tr').filter({ hasText: "사용자" }).first(); await userMenuRow.waitFor({ timeout: 10000 }); const copyButton = userMenuRow.getByRole("button", { name: "복사" }); await copyButton.click(); await page.waitForTimeout(1500); await takeScreenshot(page, "05_copy_dialog_open").then((p) => screenshots.push({ step: "복사 다이얼로그", path: p }) ); // 5. 대상 회사 선택: 두바이 강정 단단 (COMPANY_18) console.log("5. 대상 회사 선택: 두바이 강정 단단 (COMPANY_18)..."); const targetCompanyTrigger = page.locator('[id="company"]').or(page.getByRole("combobox")).first(); await targetCompanyTrigger.click(); await page.waitForTimeout(500); const dubaiOption = page.getByText("두바이 강정 단단", { exact: false }).first(); await dubaiOption.click(); await page.waitForTimeout(500); await takeScreenshot(page, "06_target_company_selected").then((p) => screenshots.push({ step: "대상 회사 선택 후", path: p }) ); // 6. 복사 시작 버튼 클릭 console.log("6. 복사 시작..."); const copyStartButton = page.getByRole("button", { name: /복사 시작|확인/ }).first(); await copyStartButton.click(); // 7. 복사 완료 대기 (최대 5분) console.log("7. 복사 완료 대기 (최대 5분)..."); try { await page.waitForSelector('text=완료, text=성공, [role="status"]', { timeout: 300000 }); await page.waitForTimeout(3000); } catch { console.log("타임아웃 또는 완료 메시지 대기 중..."); } await takeScreenshot(page, "07_copy_result").then((p) => screenshots.push({ step: "복사 결과", path: p }) ); // 결과 확인 const resultText = await page.locator("body").textContent(); if (resultText?.includes("완료") || resultText?.includes("성공")) { console.log("\n=== 메뉴 복사 성공 ==="); } else if (resultText?.includes("오류") || resultText?.includes("실패") || resultText?.includes("error")) { console.log("\n=== 에러 발생 가능 - 스크린샷 확인 필요 ==="); } console.log("\n=== 스크린샷 목록 ==="); screenshots.forEach((s) => console.log(` - ${s.step}: ${s.path}`)); } catch (error) { console.error("오류 발생:", error); if (browser) { const pages = (browser as any).contexts?.()?.[0]?.pages?.() || []; for (const p of pages) { try { await takeScreenshot(p, "error_state").then((path) => screenshots.push({ step: "에러 상태", path }) ); } catch {} } } throw error; } finally { if (browser) { await browser.close(); } } } main().catch(console.error);