/** * 화면 156 검증: 로드, 버튼, 테이블, 페이지네이션, 레이아웃 */ import { chromium } from "playwright"; import * as path from "path"; import * as fs from "fs"; const BASE_URL = "http://localhost:9771"; const SCREENSHOT_DIR = path.join(__dirname, "../verification-screenshots"); async function main() { const browser = await chromium.launch({ headless: true }); const context = await browser.newContext({ viewport: { width: 1280, height: 900 } }); const page = await context.newPage(); const report: Record = {}; try { await page.goto(`${BASE_URL}/screens/156`, { waitUntil: "load", timeout: 30000 }); await page.waitForTimeout(2000); const url = page.url(); if (url.includes("/login")) { report.loginRequired = true; await page.screenshot({ path: path.join(SCREENSHOT_DIR, "screen-156-login.png"), fullPage: true }); console.log("Login page - logging in with wace..."); await page.fill("#userId", "wace"); await page.fill("#password", "qlalfqjsgh11"); await page.locator('button[type="submit"]').first().click(); await page.waitForURL((u) => !u.includes("/login"), { timeout: 15000 }).catch(() => {}); await page.waitForTimeout(2000); await page.goto(`${BASE_URL}/screens/156`, { waitUntil: "load", timeout: 30000 }); await page.waitForTimeout(2000); } const currentUrl = page.url(); report.loginRequired = currentUrl.includes("/login"); if (!currentUrl.includes("/login")) { await page.getByText("로딩중", { exact: false }).waitFor({ state: "hidden", timeout: 10000 }).catch(() => {}); await page.waitForTimeout(2000); const hasError = (await page.locator('text="화면을 찾을 수 없습니다"').count()) > 0; report.pageLoadsWithoutErrors = !hasError; const buttonCount = await page.locator("button").count(); const buttonsBetween = await page.evaluate(() => { const searchWidget = document.querySelector("[class*='search'], [class*='filter']"); const table = document.querySelector("table"); const buttons = document.querySelectorAll("button"); let between = 0; buttons.forEach((btn) => { const rect = btn.getBoundingClientRect(); if (searchWidget && table) { const sRect = searchWidget.getBoundingClientRect(); const tRect = table.getBoundingClientRect(); if (rect.top > sRect.bottom && rect.top < tRect.top) between++; } }); return between; }); report.buttonsVisible = buttonCount > 0; report.buttonsBetweenSearchAndTable = buttonsBetween; const table = page.locator("table").first(); const tableVisible = (await table.count()) > 0; report.tableVisible = tableVisible; let tableOverflow = false; if (tableVisible) { const dims = await table.evaluate((el) => ({ scrollWidth: el.scrollWidth, clientWidth: el.clientWidth, })); tableOverflow = dims.scrollWidth > dims.clientWidth; report.tableScrollWidth = dims.scrollWidth; report.tableClientWidth = dims.clientWidth; } report.tableOverflowsHorizontally = tableOverflow; const paginationVisible = (await page.getByText("표시", { exact: false }).count()) > 0 || (await page.getByText("1/", { exact: false }).count()) > 0; report.paginationBarVisible = paginationVisible; const bodyScrollWidth = await page.evaluate(() => document.body.scrollWidth); const viewportWidth = 1280; report.bodyScrollWidth = bodyScrollWidth; report.hasHorizontalScrollbar = bodyScrollWidth > viewportWidth; report.layoutResponsive = !report.hasHorizontalScrollbar; await page.screenshot({ path: path.join(SCREENSHOT_DIR, "screen-156-snapshot.png"), fullPage: true }); console.log("screen-156-snapshot.png saved"); } console.log("\n=== Report ==="); console.log(JSON.stringify(report, null, 2)); fs.writeFileSync( path.join(SCREENSHOT_DIR, "screen-156-report.json"), JSON.stringify(report, null, 2) ); } catch (error: any) { console.error("Error:", error.message); report.error = error.message; await page.screenshot({ path: path.join(SCREENSHOT_DIR, "screen-156-error.png"), fullPage: true }).catch(() => {}); } finally { await browser.close(); } } main();