From 7910921c9725994ded4b1b6a2b9c91f03a61df32 Mon Sep 17 00:00:00 2001 From: DDD1542 Date: Mon, 9 Mar 2026 13:33:01 +0900 Subject: [PATCH] Remove obsolete end-to-end test scripts and related files for screen and table components in the agent pipeline. --- .../screen/RealtimePreviewDynamic.tsx | 26 +- .../screen/ResponsiveGridRenderer.tsx | 322 ++++++++++++------ .../components/screen/widgets/TabsWidget.tsx | 238 +++++++------ .../SplitPanelLayoutComponent.tsx | 32 +- .../SplitPanelLayoutComponent.tsx | 274 +++++++-------- .../v2-table-list/TableListComponent.tsx | 10 +- frontend/scripts/browser-verification.ts | 111 ++++++ .../scripts/company-menu-flow-verification.ts | 156 +++++++++ frontend/scripts/screen68-verification.ts | 135 ++++++++ frontend/scripts/screen94-124-verification.ts | 163 +++++++++ .../scripts/verify-button-layout-screens.ts | 130 +++++++ frontend/scripts/verify-overlay-buttons.ts | 166 +++++++++ frontend/scripts/verify-responsive-screens.ts | 126 +++++++ frontend/scripts/verify-screen-1053-admin.ts | 103 ++++++ frontend/scripts/verify-screen-1053.ts | 105 ++++++ frontend/scripts/verify-screen-1244-layout.ts | 156 +++++++++ .../scripts/verify-screen-1244-refresh.ts | 143 ++++++++ .../scripts/verify-screen-1244-refresh2.ts | 142 ++++++++ frontend/scripts/verify-screen-1244.ts | 127 +++++++ frontend/scripts/verify-screen-150-tapseal.ts | 162 +++++++++ frontend/scripts/verify-screen-1556.ts | 93 +++++ frontend/scripts/verify-screen-156.ts | 111 ++++++ .../scripts/verify-screens-156-1053-v2.ts | 141 ++++++++ .../scripts/verify-split-panel-screens.ts | 115 +++++++ frontend/scripts/verify-tapseal-screens.ts | 164 +++++++++ run-screen-e2e.mjs | 209 ------------ run-screen29-e2e-new.sh | 5 - run-screen29-e2e.mjs | 234 ------------- run-screen29-filter-e2e.mjs | 221 ------------ run-screen29-filter-test.sh | 4 - run-screen29-table-e2e.mjs | 264 -------------- scripts/run-screen-e2e-test.js | 249 -------------- scripts/run-screens-29-full-test.js | 298 ---------------- scripts/run-screens-29-test.js | 142 -------- 34 files changed, 3076 insertions(+), 2001 deletions(-) create mode 100644 frontend/scripts/browser-verification.ts create mode 100644 frontend/scripts/company-menu-flow-verification.ts create mode 100644 frontend/scripts/screen68-verification.ts create mode 100644 frontend/scripts/screen94-124-verification.ts create mode 100644 frontend/scripts/verify-button-layout-screens.ts create mode 100644 frontend/scripts/verify-overlay-buttons.ts create mode 100644 frontend/scripts/verify-responsive-screens.ts create mode 100644 frontend/scripts/verify-screen-1053-admin.ts create mode 100644 frontend/scripts/verify-screen-1053.ts create mode 100644 frontend/scripts/verify-screen-1244-layout.ts create mode 100644 frontend/scripts/verify-screen-1244-refresh.ts create mode 100644 frontend/scripts/verify-screen-1244-refresh2.ts create mode 100644 frontend/scripts/verify-screen-1244.ts create mode 100644 frontend/scripts/verify-screen-150-tapseal.ts create mode 100644 frontend/scripts/verify-screen-1556.ts create mode 100644 frontend/scripts/verify-screen-156.ts create mode 100644 frontend/scripts/verify-screens-156-1053-v2.ts create mode 100644 frontend/scripts/verify-split-panel-screens.ts create mode 100644 frontend/scripts/verify-tapseal-screens.ts delete mode 100644 run-screen-e2e.mjs delete mode 100644 run-screen29-e2e-new.sh delete mode 100644 run-screen29-e2e.mjs delete mode 100644 run-screen29-filter-e2e.mjs delete mode 100644 run-screen29-filter-test.sh delete mode 100644 run-screen29-table-e2e.mjs delete mode 100644 scripts/run-screen-e2e-test.js delete mode 100644 scripts/run-screens-29-full-test.js delete mode 100644 scripts/run-screens-29-test.js diff --git a/frontend/components/screen/RealtimePreviewDynamic.tsx b/frontend/components/screen/RealtimePreviewDynamic.tsx index 2b45f2eb..fdb1aabe 100644 --- a/frontend/components/screen/RealtimePreviewDynamic.tsx +++ b/frontend/components/screen/RealtimePreviewDynamic.tsx @@ -357,8 +357,30 @@ const RealtimePreviewDynamicComponent: React.FC = ({ return `${actualHeight}px`; } - // ๐Ÿ†• 1์ˆœ์œ„: size.height๊ฐ€ ์žˆ์œผ๋ฉด ์šฐ์„  ์‚ฌ์šฉ (๋ ˆ์ด์•„์›ƒ์—์„œ ๊ด€๋ฆฌ๋˜๋Š” ์‹ค์ œ ํฌ๊ธฐ) - // size๋Š” ๋ ˆ์ด์•„์›ƒ ์ƒํƒœ์—์„œ ์ง์ ‘ ๊ด€๋ฆฌ๋˜๋ฉฐ ๋ฆฌ์‚ฌ์ด์ฆˆ๋กœ ๋ณ€๊ฒฝ๋จ + // ๋Ÿฐํƒ€์ž„ ๋ชจ๋“œ์—์„œ ์ปดํฌ๋„ŒํŠธ ํƒ€์ž…๋ณ„ ๋†’์ด ์ฒ˜๋ฆฌ + if (!isDesignMode) { + const compType = (component as any).componentType || component.componentConfig?.type || ""; + // ํ…Œ์ด๋ธ”: ๋ถ€๋ชจ flex ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ๋†’์ด ๊ด€๋ฆฌ (flex: 1) + const flexGrowTypes = [ + "table-list", "v2-table-list", + "split-panel-layout", "split-panel-layout2", + "v2-split-panel-layout", "screen-split-panel", + "v2-tab-container", "tab-container", + "tabs-widget", "v2-tabs-widget", + ]; + if (flexGrowTypes.some(t => compType === t)) { + return "100%"; + } + const autoHeightTypes = [ + "table-search-widget", "v2-table-search-widget", + "flow-widget", + ]; + if (autoHeightTypes.some(t => compType === t || compType.includes(t))) { + return "auto"; + } + } + + // 1์ˆœ์œ„: size.height๊ฐ€ ์žˆ์œผ๋ฉด ์šฐ์„  ์‚ฌ์šฉ if (size?.height && size.height > 0) { if (component.componentConfig?.type === "table-list") { return `${Math.max(size.height, 200)}px`; diff --git a/frontend/components/screen/ResponsiveGridRenderer.tsx b/frontend/components/screen/ResponsiveGridRenderer.tsx index 2c343ebc..e18ddb53 100644 --- a/frontend/components/screen/ResponsiveGridRenderer.tsx +++ b/frontend/components/screen/ResponsiveGridRenderer.tsx @@ -1,8 +1,9 @@ "use client"; -import React, { useMemo } from "react"; +import React, { useMemo, useRef, useState, useEffect } from "react"; import { ComponentData } from "@/types/screen"; import { useResponsive } from "@/lib/hooks/useResponsive"; +import { cn } from "@/lib/utils"; interface ResponsiveGridRendererProps { components: ComponentData[]; @@ -11,7 +12,6 @@ interface ResponsiveGridRendererProps { renderComponent: (component: ComponentData) => React.ReactNode; } -// ์ „์ฒด ํ–‰์„ ์ฐจ์ง€ํ•ด์•ผ ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ ํƒ€์ž… const FULL_WIDTH_TYPES = new Set([ "table-list", "v2-table-list", @@ -20,39 +20,35 @@ const FULL_WIDTH_TYPES = new Set([ "conditional-container", "split-panel-layout", "split-panel-layout2", + "v2-split-panel-layout", + "screen-split-panel", "v2-split-line", "flow-widget", "v2-tab-container", "tab-container", + "tabs-widget", + "v2-tabs-widget", ]); -// ๋†’์ด๋ฅผ auto๋กœ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ ํƒ€์ž… -const AUTO_HEIGHT_TYPES = new Set([ +const FLEX_GROW_TYPES = new Set([ "table-list", "v2-table-list", - "table-search-widget", - "v2-table-search-widget", - "conditional-container", - "flow-widget", - "v2-tab-container", - "tab-container", "split-panel-layout", "split-panel-layout2", + "v2-split-panel-layout", + "screen-split-panel", + "v2-tab-container", + "tab-container", + "tabs-widget", + "v2-tabs-widget", ]); -/** - * Y์ขŒํ‘œ ๊ธฐ์ค€์œผ๋กœ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ–‰(row) ๋‹จ์œ„๋กœ ๊ทธ๋ฃนํ•‘ - * - Y๊ฐ’ ์ฐจ์ด๊ฐ€ threshold ์ด๋‚ด๋ฉด ๊ฐ™์€ ํ–‰์œผ๋กœ ํŒ์ • - * - ๊ฐ™์€ ํ–‰ ์•ˆ์—์„œ๋Š” X์ขŒํ‘œ ์ˆœ์œผ๋กœ ์ •๋ ฌ - */ function groupComponentsIntoRows( components: ComponentData[], threshold: number = 30 ): ComponentData[][] { if (components.length === 0) return []; - const sorted = [...components].sort((a, b) => a.position.y - b.position.y); - const rows: ComponentData[][] = []; let currentRow: ComponentData[] = []; let currentRowY = -Infinity; @@ -67,67 +63,150 @@ function groupComponentsIntoRows( } } if (currentRow.length > 0) rows.push(currentRow); - - return rows.map((row) => - row.sort((a, b) => a.position.x - b.position.x) - ); + return rows.map((row) => row.sort((a, b) => a.position.x - b.position.x)); } -/** - * ์ปดํฌ๋„ŒํŠธ์˜ ์œ ํ˜• ์‹๋ณ„ (componentType, componentId, widgetType ๋“ฑ) - */ function getComponentTypeId(component: ComponentData): string { - return ( - (component as any).componentType || - (component as any).componentId || - (component as any).widgetType || - component.type || - "" - ); + const direct = + (component as any).componentType || (component as any).widgetType; + if (direct) return direct; + const url = (component as any).url; + if (url && typeof url === "string") { + const parts = url.split("/"); + return parts[parts.length - 1]; + } + return component.type || ""; +} + +function isButtonComponent(component: ComponentData): boolean { + return getComponentTypeId(component).includes("button"); } -/** - * ์ „์ฒด ํ–‰์„ ์ฐจ์ง€ํ•ด์•ผ ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์ธ์ง€ ํŒ์ • - */ function isFullWidthComponent(component: ComponentData): boolean { - const typeId = getComponentTypeId(component); - return FULL_WIDTH_TYPES.has(typeId); + return FULL_WIDTH_TYPES.has(getComponentTypeId(component)); } -/** - * ๋†’์ด๋ฅผ auto๋กœ ์ฒ˜๋ฆฌํ•ด์•ผ ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ์ธ์ง€ ํŒ์ • - */ -function shouldAutoHeight(component: ComponentData): boolean { - const typeId = getComponentTypeId(component); - return AUTO_HEIGHT_TYPES.has(typeId); +function shouldFlexGrow(component: ComponentData): boolean { + return FLEX_GROW_TYPES.has(getComponentTypeId(component)); } -/** - * ์ปดํฌ๋„ŒํŠธ ๋„ˆ๋น„๋ฅผ ์บ”๋ฒ„์Šค ๋Œ€๋น„ ๋น„์œจ(%)๋กœ ๋ณ€ํ™˜ - */ -function getPercentageWidth( - componentWidth: number, - canvasWidth: number -): number { - const percentage = (componentWidth / canvasWidth) * 100; - if (percentage >= 95) return 100; - return percentage; +function getPercentageWidth(componentWidth: number, canvasWidth: number): number { + const pct = (componentWidth / canvasWidth) * 100; + return pct >= 95 ? 100 : pct; } -/** - * ํ–‰ ๋‚ด ์ปดํฌ๋„ŒํŠธ ์‚ฌ์ด์˜ ์ˆ˜ํ‰ ๊ฐญ(px)์„ ๋น„์œจ ๊ธฐ๋ฐ˜์œผ๋กœ ์ถ”์ • - */ function getRowGap(row: ComponentData[], canvasWidth: number): number { if (row.length < 2) return 0; - const totalComponentWidth = row.reduce( - (sum, c) => sum + (c.size?.width || 100), - 0 + const totalW = row.reduce((s, c) => s + (c.size?.width || 100), 0); + const gap = canvasWidth - totalW; + const cnt = row.length - 1; + if (gap <= 0 || cnt <= 0) return 8; + return Math.min(Math.max(Math.round(gap / cnt), 4), 24); +} + +interface ProcessedRow { + type: "normal" | "fullwidth"; + mainComponent?: ComponentData; + overlayComps: ComponentData[]; + normalComps: ComponentData[]; +} + +/** + * ํ’€์œ„๋“œ์Šค ์ปดํฌ๋„ŒํŠธ + ์˜ค๋ฒ„๋ ˆ์ด ๋ฒ„ํŠผ. + * ์›๋ณธ ์ขŒํ‘œ ๊ทธ๋Œ€๋กœ ๋ฐฐ์น˜ โ†’ transform: scale ํ•œ ๋ฐฉ์œผ๋กœ ์ถ•์†Œ. + * ๋””์ž์ด๋„ˆ ๋ฏธ๋ฆฌ๋ณด๊ธฐ์™€ ๋™์ผํ•œ ์›๋ฆฌ. + */ +function FullWidthOverlayRow({ + main, + overlayComps, + canvasWidth, + renderComponent, +}: { + main: ComponentData; + overlayComps: ComponentData[]; + canvasWidth: number; + renderComponent: (component: ComponentData) => React.ReactNode; +}) { + const containerRef = useRef(null); + const [containerW, setContainerW] = useState(0); + + useEffect(() => { + const el = containerRef.current; + if (!el) return; + const ro = new ResizeObserver((entries) => { + const w = entries[0]?.contentRect.width; + if (w && w > 0) setContainerW(w); + }); + ro.observe(el); + return () => ro.disconnect(); + }, []); + + const compFlexGrow = shouldFlexGrow(main); + const mainY = main.position.y; + const scale = containerW > 0 ? containerW / canvasWidth : 1; + + const minButtonY = Math.min(...overlayComps.map((c) => c.position.y)); + const rawYOffset = minButtonY - mainY; + const maxBtnH = Math.max( + ...overlayComps.map((c) => c.size?.height || 40) + ); + // ๋ฒ„ํŠผ ์ค‘์‹ฌ ๋ณด์ •: ์Šค์ผ€์ผ ์ถ•์†Œ ์‹œ ๋ฒ„ํŠผ์ด ์ž‘์•„์ง€๋ฏ€๋กœ ์ค‘์‹ฌ ์œ„์น˜๊ฐ€ ์œ„๋กœ ์˜ฌ๋ผ๊ฐ + // ๋””์ž์ด๋„ˆ์™€ ๋™์ผํ•œ ์ค‘์‹ฌ-๋Œ€-์ค‘์‹ฌ ์ •๋ ฌ์„ ์œ ์ง€ํ•˜๊ธฐ ์œ„ํ•ด Y ์˜คํ”„์…‹ ๋ณด์ • + const yOffset = rawYOffset + (maxBtnH / 2) * (1 - scale); + + return ( +
+
+ {renderComponent(main)} +
+ + {overlayComps.length > 0 && containerW > 0 && ( +
+ {overlayComps.map((comp) => ( +
+ {renderComponent(comp)} +
+ ))} +
+ )} +
); - const totalGap = canvasWidth - totalComponentWidth; - const gapCount = row.length - 1; - if (totalGap <= 0 || gapCount <= 0) return 8; - const gapPx = totalGap / gapCount; - return Math.min(Math.max(Math.round(gapPx), 4), 24); } export function ResponsiveGridRenderer({ @@ -138,14 +217,12 @@ export function ResponsiveGridRenderer({ }: ResponsiveGridRendererProps) { const { isMobile } = useResponsive(); - // ์ „์ฒด ํ–‰์„ ์ฐจ์ง€ํ•˜๋Š” ์ปดํฌ๋„ŒํŠธ๋Š” ๋ณ„๋„ ํ–‰์œผ๋กœ ๋ถ„๋ฆฌ const processedRows = useMemo(() => { const topLevel = components.filter((c) => !c.parentId); const rows = groupComponentsIntoRows(topLevel); - const result: ComponentData[][] = []; + const result: ProcessedRow[] = []; for (const row of rows) { - // ์ „์ฒด ๋„ˆ๋น„ ์ปดํฌ๋„ŒํŠธ๋Š” ๋…๋ฆฝ ํ–‰์œผ๋กœ ๋ถ„๋ฆฌ const fullWidthComps: ComponentData[] = []; const normalComps: ComponentData[] = []; @@ -157,73 +234,112 @@ export function ResponsiveGridRenderer({ } } - // ์ผ๋ฐ˜ ์ปดํฌ๋„ŒํŠธ ํ–‰ ๋จผ์ € - if (normalComps.length > 0) { - result.push(normalComps); - } - // ์ „์ฒด ๋„ˆ๋น„ ์ปดํฌ๋„ŒํŠธ๋Š” ๊ฐ๊ฐ ๋…๋ฆฝ ํ–‰ - for (const comp of fullWidthComps) { - result.push([comp]); + if (fullWidthComps.length > 0 && normalComps.length > 0) { + for (const fwComp of fullWidthComps) { + result.push({ + type: "fullwidth", + mainComponent: fwComp, + overlayComps: normalComps, + normalComps: [], + }); + } + } else if (fullWidthComps.length > 0) { + for (const fwComp of fullWidthComps) { + result.push({ + type: "fullwidth", + mainComponent: fwComp, + overlayComps: [], + normalComps: [], + }); + } + } else { + result.push({ + type: "normal", + overlayComps: [], + normalComps, + }); } } - return result; }, [components]); return (
- {processedRows.map((row, rowIndex) => { - const isSingleFullWidth = - row.length === 1 && isFullWidthComponent(row[0]); - const gap = isMobile ? 8 : getRowGap(row, canvasWidth); + {processedRows.map((processedRow, rowIndex) => { + if (processedRow.type === "fullwidth" && processedRow.mainComponent) { + return ( + + ); + } + + const { normalComps } = processedRow; + const allButtons = normalComps.every((c) => isButtonComponent(c)); + const gap = isMobile ? 8 : allButtons ? 8 : getRowGap(normalComps, canvasWidth); return (
- {row.map((component) => { + {normalComps.map((component) => { const typeId = getComponentTypeId(component); - const isFullWidth = - isMobile || isFullWidthComponent(component); + const isButton = isButtonComponent(component); + const isFullWidth = isMobile && !isButton; + + if (isButton) { + return ( +
+ {renderComponent(component)} +
+ ); + } + const percentWidth = isFullWidth ? 100 - : getPercentageWidth( - component.size?.width || 100, - canvasWidth - ); - - const autoHeight = shouldAutoHeight(component); - const height = autoHeight - ? "auto" - : component.size?.height - ? `${component.size.height}px` - : "auto"; - - // ๋ชจ๋ฐ”์ผ์—์„œ๋Š” 100%, ๋ฐ์Šคํฌํ†ฑ์—์„œ๋Š” ๋น„์œจ ๊ธฐ๋ฐ˜ - // gap์„ ๊ณ ๋ คํ•œ flex-basis ๊ณ„์‚ฐ + : getPercentageWidth(component.size?.width || 100, canvasWidth); const flexBasis = isFullWidth ? "100%" : `calc(${percentWidth}% - ${gap}px)`; + const compFlexGrow = shouldFlexGrow(component); return (
{renderComponent(component)} diff --git a/frontend/components/screen/widgets/TabsWidget.tsx b/frontend/components/screen/widgets/TabsWidget.tsx index 486e76d9..8f5410fb 100644 --- a/frontend/components/screen/widgets/TabsWidget.tsx +++ b/frontend/components/screen/widgets/TabsWidget.tsx @@ -9,6 +9,7 @@ import { cn } from "@/lib/utils"; import { useActiveTab } from "@/contexts/ActiveTabContext"; import { DynamicComponentRenderer } from "@/lib/registry/DynamicComponentRenderer"; import { screenApi } from "@/lib/api/screen"; +import { ResponsiveGridRenderer } from "@/components/screen/ResponsiveGridRenderer"; // ํ™•์žฅ๋œ TabItem ํƒ€์ž… (screenId ์ง€์›) interface ExtendedTabItem extends TabItem { @@ -305,125 +306,152 @@ export function TabsWidget({ // screenId๋กœ ๋กœ๋“œํ•œ ํ™”๋ฉด ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง const renderScreenComponents = (tab: ExtendedTabItem, components: ComponentData[]) => { - // InteractiveScreenViewerDynamic ๋™์  ๋กœ๋“œ const InteractiveScreenViewerDynamic = require("@/components/screen/InteractiveScreenViewerDynamic").InteractiveScreenViewerDynamic; - // ์ปดํฌ๋„ŒํŠธ๋“ค์˜ ์ตœ๋Œ€ ์œ„์น˜๋ฅผ ๊ณ„์‚ฐํ•˜์—ฌ ์Šคํฌ๋กค ๊ฐ€๋Šฅํ•œ ์˜์—ญ ํ™•๋ณด - const maxBottom = Math.max(...components.map((c) => (c.position?.y || 0) + (c.size?.height || 100)), 300); - const maxRight = Math.max(...components.map((c) => (c.position?.x || 0) + (c.size?.width || 200)), 400); + const canvasWidth = Math.max( + ...components.map((c) => (c.position?.x || 0) + (c.size?.width || 200)), + 800, + ); + const canvasHeight = Math.max( + ...components.map((c) => (c.position?.y || 0) + (c.size?.height || 100)), + 400, + ); return ( -
- {components.map((comp) => ( -
- -
- ))} -
+ ( + + )} + /> ); }; // ์ธ๋ผ์ธ ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง (v2 ๋ฐฉ์‹) const renderInlineComponents = (tab: ExtendedTabItem, components: TabInlineComponent[]) => { - // ์ปดํฌ๋„ŒํŠธ๋“ค์˜ ์ตœ๋Œ€ ์œ„์น˜๋ฅผ ๊ณ„์‚ฐํ•˜์—ฌ ์Šคํฌ๋กค ๊ฐ€๋Šฅํ•œ ์˜์—ญ ํ™•๋ณด - const maxBottom = Math.max( - ...components.map((c) => (c.position?.y || 0) + (c.size?.height || 100)), - 300, // ์ตœ์†Œ ๋†’์ด - ); - const maxRight = Math.max( + if (isDesignMode) { + const maxBottom = Math.max( + ...components.map((c) => (c.position?.y || 0) + (c.size?.height || 100)), + 300, + ); + const maxRight = Math.max( + ...components.map((c) => (c.position?.x || 0) + (c.size?.width || 200)), + 400, + ); + + return ( +
+ {components.map((comp: TabInlineComponent) => { + const isSelected = selectedComponentId === comp.id; + return ( +
{ + if (onComponentSelect) { + e.stopPropagation(); + onComponentSelect(tab.id, comp.id); + } + }} + > + +
+ ); + })} +
+ ); + } + + // ๋Ÿฐํƒ€์ž„: ResponsiveGridRenderer ์‚ฌ์šฉ + const canvasWidth = Math.max( ...components.map((c) => (c.position?.x || 0) + (c.size?.width || 200)), - 400, // ์ตœ์†Œ ๋„ˆ๋น„ + 800, ); + const canvasHeight = Math.max( + ...components.map((c) => (c.position?.y || 0) + (c.size?.height || 100)), + 400, + ); + + const componentDataList: ComponentData[] = components.map((comp) => ({ + id: comp.id, + componentType: comp.componentType, + label: comp.label, + position: comp.position, + size: comp.size, + componentConfig: comp.componentConfig || {}, + style: comp.style, + type: "component", + })) as any; return ( -
- {components.map((comp: TabInlineComponent) => { - const isSelected = selectedComponentId === comp.id; - - return ( -
{ - if (isDesignMode && onComponentSelect) { - e.stopPropagation(); - onComponentSelect(tab.id, comp.id); - } - }} - > - -
- ); - })} -
+ ( + + )} + /> ); }; diff --git a/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx b/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx index bc4ba2ba..6de3e3d9 100644 --- a/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx +++ b/frontend/lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx @@ -492,24 +492,22 @@ export const SplitPanelLayoutComponent: React.FC return `${height}px`; // ์ˆซ์ž๋ฉด px ์ถ”๊ฐ€ }; - const componentStyle: React.CSSProperties = isPreview + const componentStyle: React.CSSProperties = isDesignMode ? { - // ๋ฐ˜์‘ํ˜• ๋ชจ๋“œ: position relative, ๊ทธ๋ฆฌ๋“œ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ํฌ๊ธฐ ์‚ฌ์šฉ - position: "relative", - width: "100%", // ๐Ÿ†• ๋ถ€๋ชจ ์ปจํ…Œ์ด๋„ˆ ๋„ˆ๋น„์— ๋งž์ถค - height: getHeightValue(), - border: "1px solid #e5e7eb", - } - : { - // ๋””์ž์ด๋„ˆ ๋ชจ๋“œ: position absolute position: "absolute", left: `${component.style?.positionX || 0}px`, top: `${component.style?.positionY || 0}px`, - width: "100%", // ๐Ÿ†• ๋ถ€๋ชจ ์ปจํ…Œ์ด๋„ˆ ๋„ˆ๋น„์— ๋งž์ถค (๊ทธ๋ฆฌ๋“œ ๊ธฐ๋ฐ˜) + width: "100%", height: getHeightValue(), zIndex: component.style?.positionZ || 1, - cursor: isDesignMode ? "pointer" : "default", + cursor: "pointer", border: isSelected ? "2px solid #3b82f6" : "1px solid #e5e7eb", + } + : { + position: "relative", + width: "100%", + height: "100%", + border: "1px solid #e5e7eb", }; // ๊ณ„์ธต ๊ตฌ์กฐ ๋นŒ๋“œ ํ•จ์ˆ˜ (ํŠธ๋ฆฌ ๊ตฌ์กฐ ์œ ์ง€) @@ -2468,13 +2466,7 @@ export const SplitPanelLayoutComponent: React.FC
> {/* ์ขŒ์ธก ํŒจ๋„ */}
@@ -2981,7 +2973,7 @@ export const SplitPanelLayoutComponent: React.FC {/* ์šฐ์ธก ํŒจ๋„ */}
diff --git a/frontend/lib/registry/components/v2-split-panel-layout/SplitPanelLayoutComponent.tsx b/frontend/lib/registry/components/v2-split-panel-layout/SplitPanelLayoutComponent.tsx index 5a839620..337d9eff 100644 --- a/frontend/lib/registry/components/v2-split-panel-layout/SplitPanelLayoutComponent.tsx +++ b/frontend/lib/registry/components/v2-split-panel-layout/SplitPanelLayoutComponent.tsx @@ -44,6 +44,7 @@ import { useSplitPanel } from "./SplitPanelContext"; import { DynamicComponentRenderer } from "@/lib/registry/DynamicComponentRenderer"; import { PanelInlineComponent } from "./types"; import { cn } from "@/lib/utils"; +import { ResponsiveGridRenderer } from "@/components/screen/ResponsiveGridRenderer"; import { BomExcelUploadModal } from "../v2-bom-tree/BomExcelUploadModal"; export interface SplitPanelLayoutComponentProps extends ComponentRendererProps { @@ -726,24 +727,21 @@ export const SplitPanelLayoutComponent: React.FC return `${height}px`; // ์ˆซ์ž๋ฉด px ์ถ”๊ฐ€ }; - const componentStyle: React.CSSProperties = isPreview + const componentStyle: React.CSSProperties = isDesignMode ? { - // ๋ฐ˜์‘ํ˜• ๋ชจ๋“œ: position relative, ๊ทธ๋ฆฌ๋“œ ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ํฌ๊ธฐ ์‚ฌ์šฉ - position: "relative", - width: "100%", // ๐Ÿ†• ๋ถ€๋ชจ ์ปจํ…Œ์ด๋„ˆ ๋„ˆ๋น„์— ๋งž์ถค - height: getHeightValue(), - border: "1px solid #e5e7eb", - } - : { - // ๋””์ž์ด๋„ˆ ๋ชจ๋“œ: position absolute position: "absolute", left: `${component.style?.positionX || 0}px`, top: `${component.style?.positionY || 0}px`, - width: "100%", // ๐Ÿ†• ๋ถ€๋ชจ ์ปจํ…Œ์ด๋„ˆ ๋„ˆ๋น„์— ๋งž์ถค (๊ทธ๋ฆฌ๋“œ ๊ธฐ๋ฐ˜) + width: "100%", height: getHeightValue(), zIndex: component.style?.positionZ || 1, - cursor: isDesignMode ? "pointer" : "default", + cursor: "pointer", border: isSelected ? "2px solid #3b82f6" : "1px solid #e5e7eb", + } + : { + position: "relative", + width: "100%", + height: getHeightValue(), }; // ๊ณ„์ธต ๊ตฌ์กฐ ๋นŒ๋“œ ํ•จ์ˆ˜ (ํŠธ๋ฆฌ ๊ตฌ์กฐ ์œ ์ง€) @@ -2975,13 +2973,7 @@ export const SplitPanelLayoutComponent: React.FC
> {/* ์ขŒ์ธก ํŒจ๋„ */}
data-component-id={component.id} data-panel-side="left" > - {/* ๐Ÿ†• ์ปค์Šคํ…€ ๋ชจ๋“œ: ๋””์ž์ธ/์‹คํ–‰ ๋ชจ๋“œ ํ†ตํ•ฉ ๋ Œ๋”๋ง */} + {/* ์ปค์Šคํ…€ ๋ชจ๋“œ: ๋””์ž์ธ/์‹คํ–‰ ๋ชจ๋“œ ๋ถ„๊ธฐ ๋ Œ๋”๋ง */} {componentConfig.leftPanel?.components && componentConfig.leftPanel.components.length > 0 ? ( + !isDesignMode ? ( + // ๋Ÿฐํƒ€์ž„: ResponsiveGridRenderer๋กœ ๋ฐ˜์‘ํ˜• ๋ Œ๋”๋ง + (() => { + const leftComps = componentConfig.leftPanel!.components; + const canvasW = Math.max(...leftComps.map((c: PanelInlineComponent) => (c.position?.x || 0) + (c.size?.width || 200)), 800); + const canvasH = Math.max(...leftComps.map((c: PanelInlineComponent) => (c.position?.y || 0) + (c.size?.height || 100)), 400); + const compDataList = leftComps.map((c: PanelInlineComponent) => ({ + id: c.id, + type: "component" as const, + componentType: c.componentType, + label: c.label, + position: c.position || { x: 0, y: 0 }, + size: c.size || { width: 400, height: 300 }, + componentConfig: c.componentConfig || {}, + style: c.style || {}, + tableName: c.componentConfig?.tableName, + columnName: c.componentConfig?.columnName, + webType: c.componentConfig?.webType, + inputType: (c as any).inputType || c.componentConfig?.inputType, + })) as any; + return ( + ( + { + if (data?.selectedRowsData && data.selectedRowsData.length > 0) { + setCustomLeftSelectedData(data.selectedRowsData[0]); + setSelectedLeftItem(data.selectedRowsData[0]); + } else if (data?.selectedRowsData && data.selectedRowsData.length === 0) { + setCustomLeftSelectedData({}); + setSelectedLeftItem(null); + } + }} + /> + )} + /> + ); + })() + ) : (
{componentConfig.leftPanel.components.map((comp: PanelInlineComponent) => { const isSelectedComp = selectedPanelComponentId === comp.id; const isDraggingComp = draggingCompId === comp.id; const isResizingComp = resizingCompId === comp.id; - // ๋“œ๋ž˜๊ทธ/๋ฆฌ์‚ฌ์ด์ฆˆ ์ค‘ ํ‘œ์‹œํ•  ํฌ๊ธฐ/์œ„์น˜ const displayX = isDraggingComp && dragPosition ? dragPosition.x : (comp.position?.x || 0); const displayY = isDraggingComp && dragPosition ? dragPosition.y : (comp.position?.y || 0); const displayWidth = isResizingComp && resizeSize ? resizeSize.width : (comp.size?.width || 200); const displayHeight = isResizingComp && resizeSize ? resizeSize.height : (comp.size?.height || 100); - // ์ปดํฌ๋„ŒํŠธ ๋ฐ์ดํ„ฐ๋ฅผ DynamicComponentRenderer ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ - // componentConfig์˜ ์ฃผ์š” ์†์„ฑ์„ ์ตœ์ƒ์œ„๋กœ ํŽผ์นจ (์ผ๋ฐ˜ ํ™”๋ฉด์˜ overrides ํ”Œ๋ž˜ํŠธ๋‹๊ณผ ๋™์ผ) const componentData = { id: comp.id, type: "component" as const, @@ -3078,16 +3122,13 @@ export const SplitPanelLayoutComponent: React.FC size: { width: displayWidth, height: displayHeight }, componentConfig: comp.componentConfig || {}, style: comp.style || {}, - // ํŒŒ์ผ ์—…๋กœ๋“œ/๋ฏธ๋””์–ด ๋“ฑ์ด component.tableName, component.columnName์„ ์ง์ ‘ ์ฐธ์กฐํ•˜๋ฏ€๋กœ ํŽผ์นจ tableName: comp.componentConfig?.tableName, columnName: comp.componentConfig?.columnName, webType: comp.componentConfig?.webType, - inputType: comp.inputType || comp.componentConfig?.inputType, + inputType: (comp as any).inputType || comp.componentConfig?.inputType, }; - if (isDesignMode) { - // ๋””์ž์ธ ๋ชจ๋“œ: ํƒญ ์ปดํฌ๋„ŒํŠธ์™€ ๋™์ผํ•˜๊ฒŒ ์‹ค์ œ ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง - return ( + return (
); - } else { - // ์‹คํ–‰ ๋ชจ๋“œ: DynamicComponentRenderer๋กœ ๋ Œ๋”๋ง - const componentData = { - id: comp.id, - type: "component" as const, - componentType: comp.componentType, - label: comp.label, - position: comp.position || { x: 0, y: 0 }, - size: comp.size || { width: 400, height: 300 }, - componentConfig: comp.componentConfig || {}, - style: comp.style || {}, - }; - - return ( -
- { - // ์ปค์Šคํ…€ ๋ชจ๋“œ: ์ขŒ์ธก ์นด๋“œ/ํ…Œ์ด๋ธ” ์„ ํƒ ์‹œ ๋ฐ์ดํ„ฐ ์บก์ฒ˜ - if (data?.selectedRowsData && data.selectedRowsData.length > 0) { - setCustomLeftSelectedData(data.selectedRowsData[0]); - setSelectedLeftItem(data.selectedRowsData[0]); - } else if (data?.selectedRowsData && data.selectedRowsData.length === 0) { - setCustomLeftSelectedData({}); - setSelectedLeftItem(null); - } - }} - /> -
- ); - } })}
+ ) ) : ( // ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์—†์„ ๋•Œ ๋“œ๋กญ ์˜์—ญ ํ‘œ์‹œ
@@ -3819,8 +3809,8 @@ export const SplitPanelLayoutComponent: React.FC {/* ์šฐ์ธก ํŒจ๋„ */}
)} - + {/* ์ถ”๊ฐ€ ํƒญ ์ปจํ…์ธ  */} {activeTabIndex > 0 ? ( (() => { @@ -4185,22 +4175,68 @@ export const SplitPanelLayoutComponent: React.FC data-component-id={component.id} data-panel-side="right" > - {/* ๐Ÿ†• ์ปค์Šคํ…€ ๋ชจ๋“œ: ๋””์ž์ธ/์‹คํ–‰ ๋ชจ๋“œ ํ†ตํ•ฉ ๋ Œ๋”๋ง */} + {/* ์ปค์Šคํ…€ ๋ชจ๋“œ: ๋””์ž์ธ/์‹คํ–‰ ๋ชจ๋“œ ๋ถ„๊ธฐ ๋ Œ๋”๋ง */} {componentConfig.rightPanel?.components && componentConfig.rightPanel.components.length > 0 ? ( + !isDesignMode ? ( + // ๋Ÿฐํƒ€์ž„: ResponsiveGridRenderer๋กœ ๋ฐ˜์‘ํ˜• ๋ Œ๋”๋ง + (() => { + const rightComps = componentConfig.rightPanel!.components; + const canvasW = Math.max(...rightComps.map((c: PanelInlineComponent) => (c.position?.x || 0) + (c.size?.width || 200)), 800); + const canvasH = Math.max(...rightComps.map((c: PanelInlineComponent) => (c.position?.y || 0) + (c.size?.height || 100)), 400); + const compDataList = rightComps.map((c: PanelInlineComponent) => ({ + id: c.id, + type: "component" as const, + componentType: c.componentType, + label: c.label, + position: c.position || { x: 0, y: 0 }, + size: c.size || { width: 400, height: 300 }, + componentConfig: c.componentConfig || {}, + style: c.style || {}, + tableName: c.componentConfig?.tableName, + columnName: c.componentConfig?.columnName, + webType: c.componentConfig?.webType, + inputType: (c as any).inputType || c.componentConfig?.inputType, + })) as any; + return ( + ( + { + setCustomLeftSelectedData((prev: Record) => ({ ...prev, [fieldName]: value })); + }} + tableName={componentConfig.rightPanel?.tableName || componentConfig.leftPanel?.tableName} + menuObjid={(props as any).menuObjid} + screenId={(props as any).screenId} + userId={(props as any).userId} + userName={(props as any).userName} + companyCode={companyCode} + allComponents={(props as any).allComponents} + selectedRowsData={localSelectedRowsData} + onSelectedRowsChange={handleLocalSelectedRowsChange} + /> + )} + /> + ); + })() + ) : (
{componentConfig.rightPanel.components.map((comp: PanelInlineComponent) => { const isSelectedComp = selectedPanelComponentId === comp.id; const isDraggingComp = draggingCompId === comp.id; const isResizingComp = resizingCompId === comp.id; - // ๋“œ๋ž˜๊ทธ/๋ฆฌ์‚ฌ์ด์ฆˆ ์ค‘ ํ‘œ์‹œํ•  ํฌ๊ธฐ/์œ„์น˜ const displayX = isDraggingComp && dragPosition ? dragPosition.x : (comp.position?.x || 0); const displayY = isDraggingComp && dragPosition ? dragPosition.y : (comp.position?.y || 0); const displayWidth = isResizingComp && resizeSize ? resizeSize.width : (comp.size?.width || 200); const displayHeight = isResizingComp && resizeSize ? resizeSize.height : (comp.size?.height || 100); - // ์ปดํฌ๋„ŒํŠธ ๋ฐ์ดํ„ฐ๋ฅผ DynamicComponentRenderer ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ - // componentConfig์˜ ์ฃผ์š” ์†์„ฑ์„ ์ตœ์ƒ์œ„๋กœ ํŽผ์นจ (์ผ๋ฐ˜ ํ™”๋ฉด์˜ overrides ํ”Œ๋ž˜ํŠธ๋‹๊ณผ ๋™์ผ) const componentData = { id: comp.id, type: "component" as const, @@ -4210,16 +4246,13 @@ export const SplitPanelLayoutComponent: React.FC size: { width: displayWidth, height: displayHeight }, componentConfig: comp.componentConfig || {}, style: comp.style || {}, - // ํŒŒ์ผ ์—…๋กœ๋“œ/๋ฏธ๋””์–ด ๋“ฑ์ด component.tableName, component.columnName์„ ์ง์ ‘ ์ฐธ์กฐํ•˜๋ฏ€๋กœ ํŽผ์นจ tableName: comp.componentConfig?.tableName, columnName: comp.componentConfig?.columnName, webType: comp.componentConfig?.webType, - inputType: comp.inputType || comp.componentConfig?.inputType, + inputType: (comp as any).inputType || comp.componentConfig?.inputType, }; - if (isDesignMode) { - // ๋””์ž์ธ ๋ชจ๋“œ: ํƒญ ์ปดํฌ๋„ŒํŠธ์™€ ๋™์ผํ•˜๊ฒŒ ์‹ค์ œ ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง - return ( + return (
}} onClick={(e) => { e.stopPropagation(); - // ํŒจ๋„ ์ปดํฌ๋„ŒํŠธ ์„ ํƒ ์‹œ ํƒญ ๋‚ด ์„ ํƒ ํ•ด์ œ if (comp.componentType !== "v2-tabs-widget") { setNestedTabSelectedCompId(undefined); } onSelectPanelComponent?.("right", comp.id, comp); }} > - {/* ๋“œ๋ž˜๊ทธ ํ•ธ๋“ค - ์ปดํฌ๋„ŒํŠธ ์™ธ๋ถ€ ์ƒ๋‹จ */}
- {/* ์‹ค์ œ ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง - ํ•ธ๋“ค ์•„๋ž˜์— ๋ณ„๋„ ์˜์—ญ */}
height: displayHeight, }} > - {/* ๐Ÿ†• ์ปจํ…Œ์ด๋„ˆ ์ปดํฌ๋„ŒํŠธ(ํƒญ, ๋ถ„ํ•  ํŒจ๋„)๋Š” ๋“œ๋กญ ์ด๋ฒคํŠธ๋ฅผ ๋ฐ›์„ ์ˆ˜ ์žˆ์–ด์•ผ ํ•จ */}
component={componentData as any} isDesignMode={true} formData={{}} - // ๐Ÿ†• ์ค‘์ฒฉ๋œ ์ปดํฌ๋„ŒํŠธ ์—…๋ฐ์ดํŠธ ํ•ธ๋“ค๋Ÿฌ ์ „๋‹ฌ onUpdateComponent={(updatedComp: any) => { handleNestedComponentUpdate("right", comp.id, updatedComp); }} - // ๐Ÿ†• ์ค‘์ฒฉ๋œ ํƒญ ๋‚ด๋ถ€ ์ปดํฌ๋„ŒํŠธ ์„ ํƒ ํ•ธ๋“ค๋Ÿฌ - ๋ถ€๋ชจ ๋ถ„ํ•  ํŒจ๋„ ์ •๋ณด ํฌํ•จ onSelectTabComponent={(tabId: string, compId: string, tabComp: any) => { - console.log("๐Ÿ” [SplitPanel-Right] onSelectTabComponent ํ˜ธ์ถœ:", { tabId, compId, tabComp, parentSplitPanelId: component.id }); - // ํƒญ ๋‚ด ์ปดํฌ๋„ŒํŠธ ์„ ํƒ ์ƒํƒœ ์—…๋ฐ์ดํŠธ setNestedTabSelectedCompId(compId); - // ๋ถ€๋ชจ ๋ถ„ํ•  ํŒจ๋„ ์ •๋ณด์™€ ํ•จ๊ป˜ ์ „์—ญ ์ด๋ฒคํŠธ ๋ฐœ์ƒ const event = new CustomEvent("nested-tab-component-select", { detail: { tabsComponentId: comp.id, @@ -4333,20 +4356,16 @@ export const SplitPanelLayoutComponent: React.FC />
- {/* ๋ฆฌ์‚ฌ์ด์ฆˆ ๊ฐ€์žฅ์ž๋ฆฌ ์˜์—ญ - ์„ ํƒ๋œ ์ปดํฌ๋„ŒํŠธ์—๋งŒ ํ‘œ์‹œ */} {isSelectedComp && ( <> - {/* ์˜ค๋ฅธ์ชฝ ๊ฐ€์žฅ์ž๋ฆฌ (๋„ˆ๋น„ ์กฐ์ ˆ) */}
handlePanelResizeStart(e, "right", comp, "e")} /> - {/* ์•„๋ž˜ ๊ฐ€์žฅ์ž๋ฆฌ (๋†’์ด ์กฐ์ ˆ) */}
handlePanelResizeStart(e, "right", comp, "s")} /> - {/* ์˜ค๋ฅธ์ชฝ ์•„๋ž˜ ๋ชจ์„œ๋ฆฌ (๋„ˆ๋น„+๋†’์ด ์กฐ์ ˆ) */}
handlePanelResizeStart(e, "right", comp, "se")} @@ -4356,42 +4375,9 @@ export const SplitPanelLayoutComponent: React.FC
); - } else { - - return ( -
- { - setCustomLeftSelectedData((prev: Record) => ({ ...prev, [fieldName]: value })); - }} - tableName={componentConfig.rightPanel?.tableName || componentConfig.leftPanel?.tableName} - menuObjid={(props as any).menuObjid} - screenId={(props as any).screenId} - userId={(props as any).userId} - userName={(props as any).userName} - companyCode={companyCode} - allComponents={(props as any).allComponents} - selectedRowsData={localSelectedRowsData} - onSelectedRowsChange={handleLocalSelectedRowsChange} - /> -
- ); - } })}
+ ) ) : ( // ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์—†์„ ๋•Œ ๋“œ๋กญ ์˜์—ญ ํ‘œ์‹œ
@@ -4486,10 +4472,11 @@ export const SplitPanelLayoutComponent: React.FC })); } + const tableMinWidth = columnsToShow.reduce((sum, col) => sum + (col.width || 100), 0) + 80; return (
- +
{columnsToShow.map((col, idx) => ( @@ -4612,10 +4599,11 @@ export const SplitPanelLayoutComponent: React.FC const hasDeleteButton = !isDesignMode && (componentConfig.rightPanel?.deleteButton?.enabled ?? true); const hasActions = hasEditButton || hasDeleteButton; + const tableMinW2 = columnsToDisplay.reduce((sum, col) => sum + (col.width || 100), 0) + 80; return filteredData.length > 0 ? (
-
+
{columnsToDisplay.map((col) => ( diff --git a/frontend/lib/registry/components/v2-table-list/TableListComponent.tsx b/frontend/lib/registry/components/v2-table-list/TableListComponent.tsx index 282dac65..19b13398 100644 --- a/frontend/lib/registry/components/v2-table-list/TableListComponent.tsx +++ b/frontend/lib/registry/components/v2-table-list/TableListComponent.tsx @@ -432,7 +432,13 @@ export const TableListComponent: React.FC = ({ width: "100%", height: "100%", minHeight: isDesignMode ? "300px" : "100%", - ...style, // style prop์ด ์œ„์˜ ๊ธฐ๋ณธ๊ฐ’๋“ค์„ ๋ฎ์–ด์”€ + ...style, + // ๋Ÿฐํƒ€์ž„์—์„œ๋Š” DB์˜ ๊ณ ์ • px ํฌ๊ธฐ๋ฅผ ๋ฌด์‹œํ•˜๊ณ  ๋ถ€๋ชจ์— ๋งž์ถค + ...(!isDesignMode && { + width: "100%", + height: "100%", + minWidth: 0, + }), }; // ======================================== @@ -5267,7 +5273,7 @@ export const TableListComponent: React.FC = ({ onDragStart: isDesignMode ? onDragStart : undefined, onDragEnd: isDesignMode ? onDragEnd : undefined, draggable: isDesignMode, - className: cn("w-full h-full", className, isDesignMode && "cursor-move"), // customer-item-mapping๊ณผ ๋™์ผ + className: cn("w-full h-full overflow-hidden", className, isDesignMode && "cursor-move"), style: componentStyle, }; diff --git a/frontend/scripts/browser-verification.ts b/frontend/scripts/browser-verification.ts new file mode 100644 index 00000000..086fbb79 --- /dev/null +++ b/frontend/scripts/browser-verification.ts @@ -0,0 +1,111 @@ +/** + * ๋ธŒ๋ผ์šฐ์ € ๊ฒ€์ฆ ์Šคํฌ๋ฆฝํŠธ + * 1. ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ์ ‘์† + * 2. ๋กœ๊ทธ์ธ + * 3. /screens/29 ์ ‘์† + * 4. ํ™”๋ฉด ๋ Œ๋”๋ง ๊ฒ€์ฆ (๋ฒ„ํŠผ, ํ…Œ์ด๋ธ”, ๊ฒ€์ƒ‰ ํ•„ํ„ฐ) + */ + +import { chromium } from "playwright"; +import * as path from "path"; + +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: 800 } }); + const page = await context.newPage(); + + const results: { step: string; success: boolean; message?: string }[] = []; + + try { + // Step 1: ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ์ ‘์† + console.log("Step 1: ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ์ ‘์†..."); + await page.goto(`${BASE_URL}/login`, { waitUntil: "networkidle", timeout: 10000 }); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "01-login-page.png"), fullPage: true }); + results.push({ step: "1. ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ์ ‘์†", success: true }); + + // Step 2: ๋กœ๊ทธ์ธ + console.log("Step 2: ๋กœ๊ทธ์ธ..."); + await page.fill('#userId', "wace"); + await page.fill('#password', "qlalfqjsgh11"); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "02-login-filled.png"), fullPage: true }); + + const loginButton = page.locator('button[type="submit"]').first(); + await loginButton.click(); + await page.waitForURL((url) => !url.pathname.includes("/login") || url.pathname === "/", { timeout: 10000 }).catch(() => {}); + await page.waitForTimeout(2000); + + const currentUrl = page.url(); + if (currentUrl.includes("/login") && !currentUrl.includes("/screens")) { + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "02-login-result.png"), fullPage: true }); + const errorText = await page.locator('[role="alert"], .error, .text-destructive, [class*="error"]').first().textContent().catch(() => ""); + results.push({ step: "2. ๋กœ๊ทธ์ธ", success: false, message: errorText || "๋กœ๊ทธ์ธ ์‹คํŒจ - ์—ฌ์ „ํžˆ ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€์— ์žˆ์Œ" }); + } else { + results.push({ step: "2. ๋กœ๊ทธ์ธ", success: true }); + } + + // Step 3: /screens/29 ์ ‘์† + console.log("Step 3: /screens/29 ์ ‘์†..."); + await page.goto(`${BASE_URL}/screens/29`, { waitUntil: "networkidle", timeout: 15000 }); + await page.waitForTimeout(3000); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "03-screen-29.png"), fullPage: true }); + results.push({ step: "3. /screens/29 ์ ‘์†", success: true }); + + // Step 4: ํ™”๋ฉด ๋ Œ๋”๋ง ๊ฒ€์ฆ + console.log("Step 4: ํ™”๋ฉด ๋ Œ๋”๋ง ๊ฒ€์ฆ..."); + const checks: { name: string; selector: string; found: boolean }[] = []; + + // ๋ฒ„ํŠผ ํ™•์ธ + const buttons = page.locator("button, [role='button'], input[type='submit'], input[type='button']"); + const buttonCount = await buttons.count(); + checks.push({ name: "๋ฒ„ํŠผ", selector: "button, [role='button']", found: buttonCount > 0 }); + + // ํ…Œ์ด๋ธ” ํ™•์ธ + const tables = page.locator("table, [role='grid'], [role='table'], .ag-root"); + const tableCount = await tables.count(); + checks.push({ name: "ํ…Œ์ด๋ธ”", selector: "table, [role='grid']", found: tableCount > 0 }); + + // ๊ฒ€์ƒ‰ ํ•„ํ„ฐ ํ™•์ธ (input, select ๋“ฑ) + const searchFilters = page.locator('input[type="text"], input[type="search"], input[placeholder*="๊ฒ€์ƒ‰"], input[placeholder*="Search"], select, [class*="filter"], [class*="search"]'); + const filterCount = await searchFilters.count(); + checks.push({ name: "๊ฒ€์ƒ‰/ํ•„ํ„ฐ", selector: "input, select, filter", found: filterCount > 0 }); + + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "04-screen-29-verified.png"), fullPage: true }); + + const allPassed = checks.every((c) => c.found); + results.push({ + step: "4. ํ™”๋ฉด ๋ Œ๋”๋ง ๊ฒ€์ฆ", + success: allPassed, + message: checks.map((c) => `${c.name}: ${c.found ? "O" : "X"}`).join(", "), + }); + + // ๊ฒฐ๊ณผ ์ถœ๋ ฅ + console.log("\n=== ๊ฒ€์ฆ ๊ฒฐ๊ณผ ==="); + results.forEach((r) => { + console.log(`${r.step}: ${r.success ? "์„ฑ๊ณต" : "์‹คํŒจ"}${r.message ? ` - ${r.message}` : ""}`); + }); + checks.forEach((c) => { + console.log(` - ${c.name}: ${c.found ? "๋ณด์ž„" : "์—†์Œ"}`); + }); + + const finalSuccess = results.every((r) => r.success); + console.log(`\n์ตœ์ข… ํŒ์ •: ${finalSuccess ? "์„ฑ๊ณต" : "์‹คํŒจ"}`); + + // ๊ฒฐ๊ณผ๋ฅผ JSON ํŒŒ์ผ๋กœ ์ €์žฅ + const fs = await import("fs"); + fs.writeFileSync( + path.join(SCREENSHOT_DIR, "verification-result.json"), + JSON.stringify({ results, checks, finalSuccess: finalSuccess ? "์„ฑ๊ณต" : "์‹คํŒจ" }, null, 2) + ); + } catch (error: any) { + console.error("์˜ค๋ฅ˜ ๋ฐœ์ƒ:", error.message); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "99-error.png"), fullPage: true }).catch(() => {}); + results.push({ step: "์˜ค๋ฅ˜", success: false, message: error.message }); + } finally { + await browser.close(); + } +} + +main(); diff --git a/frontend/scripts/company-menu-flow-verification.ts b/frontend/scripts/company-menu-flow-verification.ts new file mode 100644 index 00000000..33d4aba7 --- /dev/null +++ b/frontend/scripts/company-menu-flow-verification.ts @@ -0,0 +1,156 @@ +/** + * ํšŒ์‚ฌ ์„ ํƒ โ†’ ๋ฉ”๋‰ด โ†’ ์ˆ˜์ฃผ/๊ตฌ๋งค๊ด€๋ฆฌ ํ™”๋ฉด ๊ฒ€์ฆ + * 1. ๋กœ๊ทธ์ธ (topseal7 ๋˜๋Š” wace) + * 2. ํšŒ์‚ฌ ์„ ํƒ โ†’ ํƒ‘์”ฐ + * 3. ์˜์—…๊ด€๋ฆฌ > ์ˆ˜์ฃผ๊ด€๋ฆฌ ๋˜๋Š” ๊ตฌ๋งค๊ด€๋ฆฌ + * 4. ๋ฐ์ดํ„ฐ ํ™”๋ฉด ์Šคํฌ๋ฆฐ์ƒท + * 5. ํ…Œ์ด๋ธ” ๊ฐ€๋กœ ์Šคํฌ๋กค ํ™•์ธ + */ + +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 steps: string[] = []; + + try { + // Step 1: ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ + console.log("Step 1: ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ์ ‘์†..."); + await page.goto(`${BASE_URL}/login`, { waitUntil: "domcontentloaded", timeout: 15000 }); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "flow-01-login-page.png"), fullPage: true }); + steps.push("01-login-page"); + + // Step 2: ๋กœ๊ทธ์ธ ์‹œ๋„ (topseal7 ๋จผ์ €) + console.log("Step 2: ๋กœ๊ทธ์ธ (topseal7 ์‹œ๋„)..."); + await page.fill("#userId", "topseal7"); + await page.fill("#password", "qlalfqjsgh11"); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "flow-02-login-topseal7.png"), fullPage: true }); + await page.locator('button[type="submit"]').first().click(); + await page.waitForTimeout(3000); + + const urlAfterLogin = page.url(); + const isStillLogin = urlAfterLogin.includes("/login"); + + if (isStillLogin) { + console.log("topseal7 ๋กœ๊ทธ์ธ ์‹คํŒจ, wace ์‹œ๋„..."); + await page.fill("#userId", "wace"); + await page.fill("#password", "qlalfqjsgh11"); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "flow-02b-login-wace.png"), fullPage: true }); + await page.locator('button[type="submit"]').first().click(); + await page.waitForTimeout(3000); + } + await page.waitForURL((url) => !url.includes("/login"), { timeout: 15000 }).catch(() => {}); + await page.waitForTimeout(3000); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "flow-03-after-login.png"), fullPage: true }); + steps.push("03-after-login"); + + // Step 3: ํšŒ์‚ฌ ์„ ํƒ โ†’ ํƒ‘์”ฐ (SUPER_ADMIN๋งŒ ๋ณด์ž„, ๋ฉ”์ธ ์•ฑ ๋กœ๋“œ ๋Œ€๊ธฐ) + console.log("Step 3: ํšŒ์‚ฌ ์„ ํƒ ํด๋ฆญ..."); + await page.getByText("ํ˜„์žฌ ๊ด€๋ฆฌ ํšŒ์‚ฌ").waitFor({ timeout: 8000 }).catch(() => {}); + await page.waitForTimeout(1000); + const companyBtn = page.getByText("ํšŒ์‚ฌ ์„ ํƒ").first(); + if ((await companyBtn.count()) > 0) { + await companyBtn.click(); + await page.waitForTimeout(1500); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "flow-04-company-dropdown.png"), fullPage: true }); + + const tapsealOption = page.getByText("ํƒ‘์”ฐ", { exact: true }).first(); + if ((await tapsealOption.count()) > 0) { + await tapsealOption.click(); + await page.waitForTimeout(2000); + console.log("ํƒ‘์”ฐ ์„ ํƒ๋จ"); + } else { + console.log("ํƒ‘์”ฐ ์˜ต์…˜ ์—†์Œ - ์Šคํ‚ต"); + } + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "flow-05-after-company.png"), fullPage: true }); + } else { + console.log("ํšŒ์‚ฌ ์„ ํƒ ๋ฒ„ํŠผ ์—†์Œ"); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "flow-05-no-company-btn.png"), fullPage: true }); + } + steps.push("05-after-company"); + + // Step 4: ์˜์—…๊ด€๋ฆฌ > ์ˆ˜์ฃผ๊ด€๋ฆฌ ๋˜๋Š” ๊ตฌ๋งค๊ด€๋ฆฌ + console.log("Step 4: ๋ฉ”๋‰ด ํด๋ฆญ (์˜์—…๊ด€๋ฆฌ > ์ˆ˜์ฃผ๊ด€๋ฆฌ)..."); + const salesMgmt = page.getByText("์˜์—…๊ด€๋ฆฌ").first(); + if ((await salesMgmt.count()) > 0) { + await salesMgmt.click(); + await page.waitForTimeout(1000); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "flow-06-sales-expanded.png"), fullPage: true }); + + const orderMgmt = page.getByText("์ˆ˜์ฃผ๊ด€๋ฆฌ").first(); + if ((await orderMgmt.count()) > 0) { + await orderMgmt.click(); + await page.waitForTimeout(3000); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "flow-07-order-screen.png"), fullPage: true }); + } else { + const purchaseMgmt = page.getByText("๊ตฌ๋งค๊ด€๋ฆฌ").first(); + if ((await purchaseMgmt.count()) > 0) { + await purchaseMgmt.click(); + await page.waitForTimeout(3000); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "flow-07-purchase-screen.png"), fullPage: true }); + } + } + } else { + const purchaseMgmt = page.getByText("๊ตฌ๋งค๊ด€๋ฆฌ").first(); + if ((await purchaseMgmt.count()) > 0) { + await purchaseMgmt.click(); + await page.waitForTimeout(3000); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "flow-07-purchase-direct.png"), fullPage: true }); + } + } + steps.push("07-menu-screen"); + + // Step 5: /screens/1244 ์ง์ ‘ ์ ‘์† ์‹œ๋„ + console.log("Step 5: /screens/1244 ์ง์ ‘ ์ ‘์†..."); + await page.goto(`${BASE_URL}/screens/1244`, { waitUntil: "domcontentloaded", timeout: 15000 }); + await page.waitForTimeout(5000); + await page.getByText("๋กœ๋”ฉ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 10000 }).catch(() => {}); + await page.waitForTimeout(2000); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "flow-08-screen-1244.png"), fullPage: true }); + steps.push("08-screen-1244"); + + // Step 6: ํ…Œ์ด๋ธ” ๊ฐ€๋กœ ์Šคํฌ๋กค ํ™•์ธ + console.log("Step 6: ํ…Œ์ด๋ธ” ๊ฐ€๋กœ ์Šคํฌ๋กค ํ™•์ธ..."); + const tableContainer = page.locator("table").locator("..").first(); + const table = page.locator("table").first(); + if ((await table.count()) > 0) { + const tableBox = await table.boundingBox(); + const hasOverflowX = await table.evaluate((el) => { + const parent = el.closest("[style*='overflow'], [class*='overflow']"); + return parent ? getComputedStyle(parent as Element).overflowX !== "visible" : false; + }).catch(() => false); + const scrollWidth = await table.evaluate((el) => el.scrollWidth); + const clientWidth = await table.evaluate((el) => el.clientWidth); + const canScroll = scrollWidth > clientWidth; + console.log(`ํ…Œ์ด๋ธ”: scrollWidth=${scrollWidth}, clientWidth=${clientWidth}, ๊ฐ€๋กœ์Šคํฌ๋กค๊ฐ€๋Šฅ=${canScroll}`); + } + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "flow-09-table-scroll-check.png"), fullPage: true }); + steps.push("09-table-scroll"); + + // Step 7: ์ตœ์ข… ์Šคํฌ๋ฆฐ์ƒท + console.log("Step 7: ์ตœ์ข… ์Šคํฌ๋ฆฐ์ƒท..."); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "flow-10-final.png"), fullPage: true }); + steps.push("10-final"); + + fs.writeFileSync( + path.join(SCREENSHOT_DIR, "flow-result.json"), + JSON.stringify({ steps, timestamp: new Date().toISOString() }, null, 2) + ); + console.log("\n์™„๋ฃŒ. ์Šคํฌ๋ฆฐ์ƒท:", SCREENSHOT_DIR); + } catch (error: any) { + console.error("์˜ค๋ฅ˜:", error.message); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "flow-99-error.png"), fullPage: true }).catch(() => {}); + } finally { + await browser.close(); + } +} + +main(); diff --git a/frontend/scripts/screen68-verification.ts b/frontend/scripts/screen68-verification.ts new file mode 100644 index 00000000..231a9ab7 --- /dev/null +++ b/frontend/scripts/screen68-verification.ts @@ -0,0 +1,135 @@ +/** + * ์ˆ˜์ฃผ๊ด€๋ฆฌ ํ™”๋ฉด(68) ๊ฒ€์ฆ ์Šคํฌ๋ฆฝํŠธ + * - ๋กœ๊ทธ์ธ ์ƒํƒœ ํ™•์ธ ํ›„ ํ•„์š”์‹œ ๋กœ๊ทธ์ธ + * - /screens/68 ์ ‘์† + * - ํ…Œ์ด๋ธ”, ๊ฒ€์ƒ‰ ํ•„ํ„ฐ, ๋ฒ„ํŠผ ํ™•์ธ + */ + +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 }, + storageState: undefined, // ์ƒˆ ์„ธ์…˜ (์ฟ ํ‚ค ์œ ์ง€ ์•ˆ ํ•จ - ์ด์ „ ์„ธ์…˜ ๋กœ๊ทธ์ธ ์ƒํƒœ ํ™•์ธ์šฉ) + }); + const page = await context.newPage(); + + const steps: { step: string; success: boolean; message?: string }[] = []; + + try { + // Step 1: ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€ ์ ‘์† ๋ฐ ๋กœ๊ทธ์ธ (Playwright๋Š” ๋งค๋ฒˆ ์ƒˆ ๋ธŒ๋ผ์šฐ์ €์ด๋ฏ€๋กœ ํ•ญ์ƒ ๋กœ๊ทธ์ธ ํ•„์š”) + console.log("Step 1: ๋กœ๊ทธ์ธ..."); + await page.goto(`${BASE_URL}/login`, { waitUntil: "domcontentloaded", timeout: 15000 }); + await page.waitForTimeout(1000); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "s68-01-login-page.png"), fullPage: true }); + + await page.fill("#userId", "wace"); + await page.fill("#password", "qlalfqjsgh11"); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "s68-02-login-filled.png"), fullPage: true }); + await page.locator('button[type="submit"]').first().click(); + await page.waitForTimeout(3000); + steps.push({ step: "๋กœ๊ทธ์ธ", success: true }); + + // Step 2: /screens/68 ์ ‘์† + console.log("Step 2: /screens/68 ์ ‘์†..."); + await page.goto(`${BASE_URL}/screens/68`, { waitUntil: "domcontentloaded", timeout: 15000 }); + + // 5์ดˆ ๋Œ€๊ธฐ (ํŽ˜์ด์ง€ ์™„์ „ ๋กœ๋“œ) + console.log("Step 3: 5์ดˆ ๋Œ€๊ธฐ (ํŽ˜์ด์ง€ ์™„์ „ ๋กœ๋“œ)..."); + await page.waitForTimeout(5000); + + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "s68-03-screen-loaded.png"), fullPage: true }); + steps.push({ step: "/screens/68 ์ ‘์† ๋ฐ 5์ดˆ ๋Œ€๊ธฐ", success: true }); + + // Step 3: ์š”์†Œ ๊ฒ€์ฆ + console.log("Step 3: ์š”์†Œ ๊ฒ€์ฆ..."); + + const hasError = await page.locator('text="ํ™”๋ฉด์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค"').count() > 0; + if (hasError) { + steps.push({ step: "ํ™”๋ฉด ๋กœ๋“œ", success: false, message: "404 - ํ™”๋ฉด์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค" }); + } else { + // ํ…Œ์ด๋ธ” (TableListComponent: role=grid, table, thead/tbody) + const tableSelectors = [ + "table", + "[role='grid']", + "[role='table']", + "thead", + "tbody", + ".table-mobile-fixed", + "[class*='ag-']", + "[class*='table-list']", + ]; + let tableFound = false; + for (const sel of tableSelectors) { + if ((await page.locator(sel).count()) > 0) { + tableFound = true; + break; + } + } + + // ๊ฒ€์ƒ‰/ํ•„ํ„ฐ (input, select, ํ…Œ์ด๋ธ” ํˆด๋ฐ” ๊ฒ€์ƒ‰/ํ•„ํ„ฐ ๋ฒ„ํŠผ) + const filterSelectors = [ + "input", + "select", + 'input[type="text"]', + 'input[type="search"]', + 'input[placeholder*="๊ฒ€์ƒ‰"]', + "button:has-text('๊ฒ€์ƒ‰')", + "button:has-text('ํ•„ํ„ฐ')", + "[class*='filter']", + "[class*='search']", + ]; + let filterFound = false; + for (const sel of filterSelectors) { + if ((await page.locator(sel).count()) > 0) { + filterFound = true; + break; + } + } + + // ๋ฒ„ํŠผ + const buttonCount = await page.locator("button, [role='button'], input[type='submit']").count(); + const buttonsFound = buttonCount > 0; + + steps.push({ + step: "ํ™”๋ฉด ์š”์†Œ ๊ฒ€์ฆ", + success: tableFound && filterFound && buttonsFound, + message: `ํ…Œ์ด๋ธ”: ${tableFound ? "O" : "X"}, ๊ฒ€์ƒ‰: ${filterFound ? "O" : "X"}, ๋ฒ„ํŠผ: ${buttonsFound ? "O" : "X"}`, + }); + + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "s68-04-verified.png"), fullPage: true }); + + const finalSuccess = tableFound && filterFound && buttonsFound && !hasError; + console.log("\n=== ๊ฒ€์ฆ ๊ฒฐ๊ณผ ==="); + steps.forEach((s) => console.log(`${s.step}: ${s.success ? "์„ฑ๊ณต" : "์‹คํŒจ"}${s.message ? ` - ${s.message}` : ""}`)); + console.log(`\n์ตœ์ข… ํŒ์ •: ${finalSuccess ? "์„ฑ๊ณต" : "์‹คํŒจ"}`); + + fs.writeFileSync( + path.join(SCREENSHOT_DIR, "s68-result.json"), + JSON.stringify( + { + steps, + checks: { table: tableFound, filter: filterFound, buttons: buttonsFound }, + finalSuccess: finalSuccess ? "์„ฑ๊ณต" : "์‹คํŒจ", + }, + null, + 2 + ) + ); + } + } catch (error: any) { + console.error("์˜ค๋ฅ˜:", error.message); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "s68-99-error.png"), fullPage: true }).catch(() => {}); + steps.push({ step: "์˜ค๋ฅ˜", success: false, message: error.message }); + } finally { + await browser.close(); + } +} + +main(); diff --git a/frontend/scripts/screen94-124-verification.ts b/frontend/scripts/screen94-124-verification.ts new file mode 100644 index 00000000..f422a607 --- /dev/null +++ b/frontend/scripts/screen94-124-verification.ts @@ -0,0 +1,163 @@ +/** + * ํ™”๋ฉด 94(์ˆ˜์ฃผ), 124(์ˆ˜์ฃผ๋ชฉ๋ก ๋ฆฌ์ŠคํŠธ) ๊ฒ€์ฆ ์Šคํฌ๋ฆฝํŠธ + * - ๋กœ๊ทธ์ธ ํ›„ ๊ฐ ํ™”๋ฉด ์ ‘์† + * - ์ปดํฌ๋„ŒํŠธ ๋ฐฐ์น˜, ํ…Œ์ด๋ธ”/ํ•„ํ„ฐ/๋ฒ„ํŠผ, ๊ฐ€๋กœ ๋ ˆ์ด์•„์›ƒ ํ™•์ธ + */ + +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"); + +interface ScreenResult { + screenId: number; + name: string; + componentsOk: boolean; + tableVisible: boolean; + filterVisible: boolean; + buttonsVisible: boolean; + layoutHorizontal: boolean; + noError: boolean; + success: boolean; +} + +type ScreenType = "form" | "list"; + +async function verifyScreen(page: any, screenId: number, name: string, type: ScreenType): Promise { + console.log(`\n--- ํ™”๋ฉด ${screenId} (${name}) ๊ฒ€์ฆ ---`); + await page.goto(`${BASE_URL}/screens/${screenId}`, { waitUntil: "domcontentloaded", timeout: 20000 }); + // ๋กœ๋”ฉ ์™„๋ฃŒ ๋Œ€๊ธฐ: "๋กœ๋”ฉ์ค‘" ํ…์ŠคํŠธ ์‚ฌ๋ผ์งˆ ๋•Œ๊นŒ์ง€ ์ตœ๋Œ€ 12์ดˆ + await page.getByText("๋กœ๋”ฉ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 12000 }).catch(() => {}); + // ๋ฆฌ์ŠคํŠธ ํ™”๋ฉด: ํ…Œ์ด๋ธ” ๋กœ๋”ฉ ๋Œ€๊ธฐ. ํผ ํ™”๋ฉด: ๋ฒ„ํŠผ/input ๋Œ€๊ธฐ + if (type === "list") { + await page.waitForSelector("table, [role='grid'], thead, tbody", { timeout: 8000 }).catch(() => {}); + } else { + await page.waitForSelector("button, input", { timeout: 5000 }).catch(() => {}); + } + await page.waitForTimeout(2000); + + const result: ScreenResult = { + screenId, + name, + componentsOk: false, + tableVisible: false, + filterVisible: false, + buttonsVisible: false, + layoutHorizontal: false, + noError: false, + success: false, + }; + + // 404/์—๋Ÿฌ ๋ฉ”์‹œ์ง€ ํ™•์ธ + const has404 = (await page.locator('text="ํ™”๋ฉด์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค"').count()) > 0; + const hasError = (await page.locator('text="์˜ค๋ฅ˜ ๋ฐœ์ƒ"').count()) > 0; + result.noError = !has404; + + // ํ…Œ์ด๋ธ” + const tableSelectors = ["table", "[role='grid']", "thead", "tbody", ".table-mobile-fixed"]; + for (const sel of tableSelectors) { + if ((await page.locator(sel).count()) > 0) { + result.tableVisible = true; + break; + } + } + + // ํ•„ํ„ฐ/๊ฒ€์ƒ‰ + const filterSelectors = ["input", "select", "button:has-text('๊ฒ€์ƒ‰')", "button:has-text('ํ•„ํ„ฐ')"]; + for (const sel of filterSelectors) { + if ((await page.locator(sel).count()) > 0) { + result.filterVisible = true; + break; + } + } + + // ๋ฒ„ํŠผ (์‚ฌ์ด๋“œ๋ฐ” ํฌํ•จ, ํ™”๋ฉด์— ๋ฒ„ํŠผ์ด ์žˆ์œผ๋ฉด OK) + const buttonCount = await page.locator("button, [role='button']").count(); + result.buttonsVisible = buttonCount > 0; + + // ๊ฐ€๋กœ ๋ ˆ์ด์•„์›ƒ: ์‚ฌ์ด๋“œ๋ฐ”+๋ฉ”์ธ ๊ตฌ์กฐ, flex/grid, ๋˜๋Š” ํ…Œ์ด๋ธ”์ด ์žˆ์œผ๋ฉด ๊ฐ€๋กœ ๋ฐฐ์น˜๋กœ ๊ฐ„์ฃผ + const hasFlexRow = (await page.locator(".flex-row, .md\\:flex-row, .flex").count()) > 0; + const hasGrid = (await page.locator(".grid, [class*='grid-cols']").count()) > 0; + const hasMain = (await page.locator("main, [role='main'], .flex-1, [class*='flex-1']").count()) > 0; + const hasSidebar = (await page.getByText("ํ˜„์žฌ ๊ด€๋ฆฌ ํšŒ์‚ฌ").count()) > 0 || (await page.getByText("VEXPLOR").count()) > 0; + result.layoutHorizontal = (hasMain && (hasFlexRow || hasGrid || result.tableVisible)) || hasSidebar; + + // ์ปดํฌ๋„ŒํŠธ ์ •์ƒ ๋ฐฐ์น˜ (ํ…Œ์ด๋ธ”, ๋ฒ„ํŠผ, ๋˜๋Š” input/ํ•„ํ„ฐ ์ค‘ ํ•˜๋‚˜๋ผ๋„ ์žˆ์œผ๋ฉด OK) + result.componentsOk = result.tableVisible || result.buttonsVisible || result.filterVisible; + + // ์„ฑ๊ณต: ํผ ํ™”๋ฉด์€ ํ…Œ์ด๋ธ” ๋ถˆํ•„์š”, ๋ฆฌ์ŠคํŠธ ํ™”๋ฉด์€ ํ…Œ์ด๋ธ” ํ•„์ˆ˜ + const baseOk = result.componentsOk && result.filterVisible && result.buttonsVisible && result.layoutHorizontal && result.noError; + result.success = type === "form" ? baseOk : baseOk && result.tableVisible; + + return result; +} + +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 results: ScreenResult[] = []; + + try { + // ๋กœ๊ทธ์ธ (Playwright๋Š” ์ƒˆ ๋ธŒ๋ผ์šฐ์ €์ด๋ฏ€๋กœ) + console.log("๋กœ๊ทธ์ธ..."); + await page.goto(`${BASE_URL}/login`, { waitUntil: "domcontentloaded", timeout: 15000 }); + await page.waitForTimeout(1000); + await page.fill("#userId", "wace"); + await page.fill("#password", "qlalfqjsgh11"); + await page.locator('button[type="submit"]').first().click(); + await page.waitForURL((url) => !url.includes("/login"), { timeout: 15000 }).catch(() => {}); + await page.waitForTimeout(3000); + + // ํ™”๋ฉด 94 + await page.screenshot({ + path: path.join(SCREENSHOT_DIR, "s94-01-before.png"), + fullPage: true, + }); + const r94 = await verifyScreen(page, 94, "์ˆ˜์ฃผ", "form"); + results.push(r94); + await page.screenshot({ + path: path.join(SCREENSHOT_DIR, "s94-02-after.png"), + fullPage: true, + }); + + // ํ™”๋ฉด 124 + await page.screenshot({ + path: path.join(SCREENSHOT_DIR, "s124-01-before.png"), + fullPage: true, + }); + const r124 = await verifyScreen(page, 124, "์ˆ˜์ฃผ๋ชฉ๋ก ๋ฆฌ์ŠคํŠธ", "list"); + results.push(r124); + await page.screenshot({ + path: path.join(SCREENSHOT_DIR, "s124-02-after.png"), + fullPage: true, + }); + + // ๊ฒฐ๊ณผ ์ถœ๋ ฅ + console.log("\n=== ๊ฒ€์ฆ ๊ฒฐ๊ณผ ==="); + results.forEach((r) => { + console.log( + `ํ™”๋ฉด ${r.screenId} (${r.name}): ${r.success ? "์„ฑ๊ณต" : "์‹คํŒจ"}` + + ` | ํ…Œ์ด๋ธ”:${r.tableVisible ? "O" : "X"} ํ•„ํ„ฐ:${r.filterVisible ? "O" : "X"} ๋ฒ„ํŠผ:${r.buttonsVisible ? "O" : "X"} ๋ ˆ์ด์•„์›ƒ:${r.layoutHorizontal ? "O" : "X"} ์—๋Ÿฌ์—†์Œ:${r.noError ? "O" : "X"}` + ); + }); + + const allSuccess = results.every((r) => r.success); + console.log(`\n์ตœ์ข… ํŒ์ •: ${allSuccess ? "์„ฑ๊ณต" : "์‹คํŒจ"}`); + + fs.writeFileSync( + path.join(SCREENSHOT_DIR, "s94-124-result.json"), + JSON.stringify({ results, finalSuccess: allSuccess ? "์„ฑ๊ณต" : "์‹คํŒจ" }, null, 2) + ); + } catch (error: any) { + console.error("์˜ค๋ฅ˜:", error.message); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "s94-124-error.png"), fullPage: true }).catch(() => {}); + } finally { + await browser.close(); + } +} + +main(); diff --git a/frontend/scripts/verify-button-layout-screens.ts b/frontend/scripts/verify-button-layout-screens.ts new file mode 100644 index 00000000..34cf3cd4 --- /dev/null +++ b/frontend/scripts/verify-button-layout-screens.ts @@ -0,0 +1,130 @@ +/** + * ํ™”๋ฉด 156, 4155, 1053 ๊ฒ€์ฆ: ๋ฒ„ํŠผ ๋ ˆ์ด์•„์›ƒ ๋ฐ ๊ฐ€์‹œ์„ฑ + */ + +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 loginIfNeeded(page: any) { + if (page.url().includes("/login")) { + await page.fill("#userId", "wace"); + await page.fill("#password", "qlalfqjsgh11"); + await page.locator('button[type="submit"]').first().click(); + await page.waitForURL((u: URL) => !u.includes("/login"), { timeout: 15000 }).catch(() => {}); + await page.waitForTimeout(2000); + } +} + +async function verifyScreen( + page: any, + screenId: number, + report: Record +) { + await page.goto(`${BASE_URL}/screens/${screenId}`, { waitUntil: "load", timeout: 30000 }); + await page.waitForTimeout(2000); + if (page.url().includes("/login")) { + await loginIfNeeded(page); + await page.goto(`${BASE_URL}/screens/${screenId}`, { waitUntil: "load", timeout: 30000 }); + await page.waitForTimeout(2000); + } + await page.getByText("๋กœ๋”ฉ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 10000 }).catch(() => {}); + await page.waitForTimeout(1500); + + const info = await page.evaluate(() => { + const buttons = Array.from(document.querySelectorAll("button")); + const buttonDetails = buttons.slice(0, 20).map((btn) => { + const text = (btn as HTMLElement).innerText?.trim() || ""; + const rect = (btn as HTMLElement).getBoundingClientRect(); + const style = window.getComputedStyle(btn); + return { + text: text.substring(0, 50), + hasText: text.length > 0, + width: rect.width, + height: rect.height, + visible: rect.width > 0 && rect.height > 0, + }; + }); + const buttonsWithText = buttonDetails.filter((b) => b.hasText); + const table = document.querySelector("table"); + const pagination = document.body.innerText.includes("ํ‘œ์‹œ") || document.body.innerText.includes("1/"); + const splitPanel = document.querySelector("[class*='split'], [class*='Split'], [class*='border-r']"); + return { + pageLoadsWithoutErrors: !document.body.innerText.includes("ํ™”๋ฉด์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค"), + totalButtons: buttons.length, + buttonsWithTextCount: buttonsWithText.length, + buttonsVisibleWithText: buttonsWithText.length > 0, + buttonDetails: buttonDetails.slice(0, 10), + tableVisible: !!table, + paginationVisible: !!pagination, + splitPanelVisible: !!splitPanel, + bodyScrollWidth: document.body.scrollWidth, + viewportWidth: window.innerWidth, + viewportHeight: window.innerHeight, + hasHorizontalOverflow: document.body.scrollWidth > window.innerWidth, + layoutFitsViewport: document.body.scrollWidth <= window.innerWidth, + }; + }); + + report.pageLoadsWithoutErrors = info.pageLoadsWithoutErrors; + report.buttonsVisibleWithText = info.buttonsVisibleWithText; + report.buttonsWithTextCount = info.buttonsWithTextCount; + report.buttonDetails = info.buttonDetails; + report.tableVisible = info.tableVisible; + report.paginationVisible = info.paginationVisible; + report.splitPanelVisible = info.splitPanelVisible; + report.layoutFitsViewport = info.layoutFitsViewport; + report.hasHorizontalOverflow = info.hasHorizontalOverflow; + report.details = { + bodyScrollWidth: info.bodyScrollWidth, + viewportWidth: info.viewportWidth, + viewportHeight: info.viewportHeight, + }; + + await page.screenshot({ + path: path.join(SCREENSHOT_DIR, `screen-${screenId}-buttons.png`), + fullPage: true, + }); + console.log(`screen-${screenId}-buttons.png saved`); +} + +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 = { screen156: {}, screen4155: {}, screen1053: {} }; + + try { + await page.goto(`${BASE_URL}/login`, { waitUntil: "load", timeout: 30000 }); + await page.waitForTimeout(1000); + await loginIfNeeded(page); + await page.waitForTimeout(2000); + + await verifyScreen(page, 156, report.screen156); + await verifyScreen(page, 4155, report.screen4155); + await verifyScreen(page, 1053, report.screen1053); + + console.log("\n=== Report ==="); + console.log(JSON.stringify(report, null, 2)); + + fs.writeFileSync( + path.join(SCREENSHOT_DIR, "button-layout-screens-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, "button-layout-error.png"), + fullPage: true, + }).catch(() => {}); + } finally { + await browser.close(); + } +} + +main(); diff --git a/frontend/scripts/verify-overlay-buttons.ts b/frontend/scripts/verify-overlay-buttons.ts new file mode 100644 index 00000000..42b24731 --- /dev/null +++ b/frontend/scripts/verify-overlay-buttons.ts @@ -0,0 +1,166 @@ +/** + * ํ™”๋ฉด 1053, 156 ๋ฒ„ํŠผ ์œ„์น˜ ๊ฒ€์ฆ + * 1053: overlay buttons within split panel + * 156: buttons in separate row above table + */ + +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 = { screen1053: {}, screen156: {} }; + + try { + await page.goto(`${BASE_URL}/login`, { waitUntil: "domcontentloaded", timeout: 60000 }); + await page.waitForTimeout(2000); + await page.fill("#userId", "wace"); + await page.fill("#password", "qlalfqjsgh11"); + await page.locator('button[type="submit"]').first().click(); + await page.waitForURL((u: URL) => !u.includes("/login"), { timeout: 20000 }).catch(() => {}); + await page.waitForTimeout(3000); + + await page.goto(`${BASE_URL}/screens/156`, { waitUntil: "domcontentloaded", timeout: 45000 }); + await page.waitForTimeout(2000); + if (page.url().includes("/login")) { + await page.fill("#userId", "wace"); + await page.fill("#password", "qlalfqjsgh11"); + await page.locator('button[type="submit"]').first().click(); + await page.waitForURL((u: URL) => !u.includes("/login"), { timeout: 20000 }).catch(() => {}); + await page.waitForTimeout(3000); + } + + // Screen 1053 + await page.goto(`${BASE_URL}/screens/1053?menuObjid=1762421920304`, { waitUntil: "domcontentloaded", timeout: 60000 }); + await page.waitForTimeout(3000); + if (page.url().includes("/login")) { + await page.fill("#userId", "wace"); + await page.fill("#password", "qlalfqjsgh11"); + await page.locator('button[type="submit"]').first().click(); + await page.waitForURL((u: URL) => !u.includes("/login"), { timeout: 25000 }).catch(() => {}); + await page.waitForTimeout(5000); + await page.goto(`${BASE_URL}/screens/1053?menuObjid=1762421920304`, { waitUntil: "domcontentloaded", timeout: 60000 }); + await page.waitForTimeout(3000); + } + await page.getByText("ํ™”๋ฉด์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 35000 }).catch(() => {}); + await page.waitForTimeout(40000); + + const splitPanelEl = page.locator("[class*='border-r'], [class*='split']").first(); + await splitPanelEl.waitFor({ state: "visible", timeout: 15000 }).catch(() => {}); + await page.waitForTimeout(3000); + + const info1053 = await page.evaluate(() => { + const splitPanel = document.querySelector("[class*='border-r']") || document.querySelector("main"); + const mainContent = document.querySelector("main") || document.body; + const allBtns = Array.from(document.querySelectorAll("button")); + const buttons = allBtns.filter((b) => { + const t = (b as HTMLElement).innerText?.trim() || ""; + const r = (b as HTMLElement).getBoundingClientRect(); + return t.length > 1 && r.x > 250 && t.match(/๋“ฑ๋ก|์ˆ˜์ •|์‚ญ์ œ|ํ’ˆ๋ชฉ|ํ…Œ์ด๋ธ”|๊ฒฐ์žฌ|์ˆ˜์ฃผ|์ถœํ•˜/); + }); + const splitRect = splitPanel ? (splitPanel as HTMLElement).getBoundingClientRect() : null; + const mainRect = mainContent ? (mainContent as HTMLElement).getBoundingClientRect() : null; + + const buttonPositions = buttons.map((b) => { + const r = (b as HTMLElement).getBoundingClientRect(); + const text = (b as HTMLElement).innerText?.trim().substring(0, 20); + return { + text, + x: r.x, + y: r.y, + right: r.right, + width: r.width, + height: r.height, + isWithinSplitPanel: splitRect + ? r.y >= splitRect.top - 20 && r.y <= splitRect.bottom + 20 + : null, + isAboveMain: mainRect ? r.y < mainRect.top + 100 : null, + }; + }); + + const table = document.querySelector("table"); + const tableRect = table ? (table as HTMLElement).getBoundingClientRect() : null; + const buttonsAboveTable = buttonPositions.every((p) => tableRect && p.y < tableRect.top - 10); + + return { + splitPanelVisible: !!splitPanel, + splitPanelRect: splitRect ? { top: splitRect.top, bottom: splitRect.bottom, left: splitRect.left, right: splitRect.right } : null, + mainRect: mainRect ? { top: mainRect.top, bottom: mainRect.bottom } : null, + buttonCount: buttons.length, + buttonPositions, + buttonsOverlaidOnSplitPanel: buttonPositions.some((p) => p.isWithinSplitPanel), + buttonsInSeparateRowAbove: buttonsAboveTable, + tableTop: tableRect?.top ?? null, + }; + }); + + report.screen1053 = info1053; + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "overlay-1053.png"), fullPage: true }); + console.log("overlay-1053.png saved"); + + // Screen 156 + await page.goto(`${BASE_URL}/screens/156?menuObjid=1762421920156`, { waitUntil: "domcontentloaded", timeout: 45000 }); + await page.waitForTimeout(3000); + await page.getByText("ํ™”๋ฉด์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 35000 }).catch(() => {}); + await page.waitForTimeout(40000); + + const table156 = page.locator("table tbody tr").first(); + await table156.waitFor({ state: "visible", timeout: 15000 }).catch(() => {}); + await page.waitForTimeout(3000); + + const info156 = await page.evaluate(() => { + const table = document.querySelector("table"); + const tableRect = table ? (table as HTMLElement).getBoundingClientRect() : null; + const buttons = Array.from(document.querySelectorAll("button")).filter( + (b) => ((b as HTMLElement).innerText?.trim() || "").match(/๊ฒฐ์žฌ|์ˆ˜์ฃผ|์ˆ˜์ •|์‚ญ์ œ|์ถœํ•˜|ํ…Œ์ด๋ธ”/) + ); + const buttonPositions = buttons.map((b) => { + const r = (b as HTMLElement).getBoundingClientRect(); + const text = (b as HTMLElement).innerText?.trim().substring(0, 20); + return { + text, + x: r.x, + y: r.y, + right: r.right, + width: r.width, + isAboveTable: tableRect ? r.y < tableRect.top - 5 : null, + }; + }); + const allButtonsAboveTable = buttonPositions.every((p) => p.isAboveTable); + + return { + tableVisible: !!table, + tableTop: tableRect?.top ?? null, + buttonCount: buttons.length, + buttonPositions, + buttonsInSeparateRowAboveTable: allButtonsAboveTable, + }; + }); + + report.screen156 = info156; + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "overlay-156.png"), fullPage: true }); + console.log("overlay-156.png saved"); + + fs.writeFileSync( + path.join(SCREENSHOT_DIR, "overlay-report.json"), + JSON.stringify(report, null, 2) + ); + console.log("\n=== Report ==="); + console.log(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, "overlay-error.png"), fullPage: true }).catch(() => {}); + } finally { + await browser.close(); + } +} + +main(); diff --git a/frontend/scripts/verify-responsive-screens.ts b/frontend/scripts/verify-responsive-screens.ts new file mode 100644 index 00000000..dbbf18e9 --- /dev/null +++ b/frontend/scripts/verify-responsive-screens.ts @@ -0,0 +1,126 @@ +/** + * ๋ฐ˜์‘ํ˜• ๋ Œ๋”๋ง ๊ฒ€์ฆ: ํ™”๋ฉด 1053, 2089, 156, 4155 + * ๋กœ๊ทธ์ธ: admin / wace1234! + */ + +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 { + // 1-3: Login + await page.goto(`${BASE_URL}/login`, { waitUntil: "load", timeout: 30000 }); + await page.waitForTimeout(1500); + await page.fill("#userId", "admin"); + await page.fill("#password", "wace1234!"); + await page.locator('button[type="submit"]').first().click(); + await page.waitForTimeout(3000); + if (page.url().includes("/login")) { + await page.fill("#userId", "wace"); + await page.fill("#password", "qlalfqjsgh11"); + await page.locator('button[type="submit"]').first().click(); + await page.waitForTimeout(3000); + } + await page.waitForURL((u: URL) => !u.includes("/login"), { timeout: 15000 }).catch(() => {}); + await page.waitForTimeout(2000); + await page.getByText("๋กœ๋”ฉ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 10000 }).catch(() => {}); + + async function captureAndVerify(screenId: number, screenName: string) { + await page.goto(`${BASE_URL}/screens/${screenId}`, { waitUntil: "domcontentloaded", timeout: 45000 }); + await page.waitForTimeout(2000); + if (page.url().includes("/login")) { + await page.fill("#userId", "wace"); + await page.fill("#password", "qlalfqjsgh11"); + await page.locator('button[type="submit"]').first().click(); + await page.waitForURL((u: URL) => !u.includes("/login"), { timeout: 15000 }).catch(() => {}); + await page.waitForTimeout(2000); + await page.goto(`${BASE_URL}/screens/${screenId}`, { waitUntil: "domcontentloaded", timeout: 45000 }); + await page.waitForTimeout(2000); + } + await page.getByText("๋กœ๋”ฉ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 10000 }).catch(() => {}); + await page.getByText("ํ™”๋ฉด์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 25000 }).catch(() => {}); + await page.waitForTimeout(2000); + + const info = await page.evaluate(() => { + const buttons = Array.from(document.querySelectorAll("button")); + const btnWithText = buttons.filter((b) => (b as HTMLElement).innerText?.trim().length > 0); + const splitPanel = document.querySelector("[class*='split'], [class*='Split'], [class*='border-r']"); + const leftPanel = document.querySelector("[class*='border-r']"); + const table = document.querySelector("table"); + const thead = document.querySelector("thead"); + const tbody = document.querySelector("tbody"); + const pagination = document.body.innerText.includes("ํ‘œ์‹œ") || document.body.innerText.includes("1/"); + const bodyText = document.body.innerText; + const hasOverlap = bodyText.includes("ํ™”๋ฉด์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค") ? false : null; + + const btnDetails = btnWithText.slice(0, 5).map((b) => ({ + text: (b as HTMLElement).innerText?.trim().substring(0, 30), + rect: (b as HTMLElement).getBoundingClientRect(), + })); + + return { + pageLoadsWithoutErrors: !bodyText.includes("ํ™”๋ฉด์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค"), + buttonsVisible: btnWithText.length > 0, + buttonsCount: btnWithText.length, + buttonDetails: btnDetails, + splitPanelVisible: !!splitPanel, + leftPanelVisible: !!leftPanel, + tableVisible: !!table && !!thead && !!tbody, + paginationVisible: !!pagination, + bodyScrollWidth: document.body.scrollWidth, + viewportWidth: window.innerWidth, + viewportHeight: window.innerHeight, + hasHorizontalOverflow: document.body.scrollWidth > window.innerWidth, + }; + }); + + await page.screenshot({ + path: path.join(SCREENSHOT_DIR, `responsive-${screenId}.png`), + fullPage: true, + }); + console.log(`responsive-${screenId}.png saved`); + + return { screenId, screenName, ...info }; + } + + // 4: Screen 1053 - ๊ฑฐ๋ž˜์ฒ˜๊ด€๋ฆฌ + report.screen1053 = await captureAndVerify(1053, "๊ฑฐ๋ž˜์ฒ˜๊ด€๋ฆฌ - split panel custom mode"); + + // 5: Screen 2089 - BOM๊ด€๋ฆฌ + report.screen2089 = await captureAndVerify(2089, "BOM๊ด€๋ฆฌ - split panel"); + + // 6: Screen 156 - ์ˆ˜์ฃผ๊ด€๋ฆฌ + report.screen156 = await captureAndVerify(156, "์ˆ˜์ฃผ๊ด€๋ฆฌ - regular screen"); + + // 7: Screen 4155 - ์ž‘์—…์ง€์‹œ + report.screen4155 = await captureAndVerify(4155, "์ž‘์—…์ง€์‹œ - buttons at bottom"); + + fs.writeFileSync( + path.join(SCREENSHOT_DIR, "responsive-report.json"), + JSON.stringify(report, null, 2) + ); + console.log("\n=== Report ==="); + console.log(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, "responsive-error.png"), + fullPage: true, + }).catch(() => {}); + } finally { + await browser.close(); + } +} + +main(); diff --git a/frontend/scripts/verify-screen-1053-admin.ts b/frontend/scripts/verify-screen-1053-admin.ts new file mode 100644 index 00000000..f98bc53e --- /dev/null +++ b/frontend/scripts/verify-screen-1053-admin.ts @@ -0,0 +1,103 @@ +/** + * ํ™”๋ฉด 1053 ๊ฒ€์ฆ - admin/1234 ๋กœ๊ทธ์ธ + */ + +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}/login`, { waitUntil: "domcontentloaded", timeout: 60000 }); + await page.waitForTimeout(2000); + + await page.fill("#userId", "admin"); + await page.fill("#password", "1234"); + await page.locator('button[type="submit"]').first().click(); + await page.waitForURL((u: URL) => !u.includes("/login"), { timeout: 20000 }).catch(() => {}); + await page.waitForTimeout(3000); + + if (page.url().includes("/login")) { + await page.fill("#userId", "wace"); + await page.fill("#password", "qlalfqjsgh11"); + await page.locator('button[type="submit"]').first().click(); + await page.waitForURL((u: URL) => !u.includes("/login"), { timeout: 20000 }).catch(() => {}); + await page.waitForTimeout(3000); + } + + await page.goto(`${BASE_URL}/screens/1053`, { waitUntil: "domcontentloaded", timeout: 60000 }); + await page.waitForTimeout(3000); + if (page.url().includes("/login")) { + await page.fill("#userId", "wace"); + await page.fill("#password", "qlalfqjsgh11"); + await page.locator('button[type="submit"]').first().click(); + await page.waitForURL((u: URL) => !u.includes("/login"), { timeout: 20000 }).catch(() => {}); + await page.waitForTimeout(3000); + await page.goto(`${BASE_URL}/screens/1053`, { waitUntil: "domcontentloaded", timeout: 60000 }); + await page.waitForTimeout(3000); + } + + await page.getByText("ํ™”๋ฉด์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 35000 }).catch(() => {}); + await page.waitForTimeout(30000); + + const table = page.locator("table tbody tr").first(); + await table.waitFor({ state: "visible", timeout: 20000 }).catch(() => {}); + await page.waitForTimeout(3000); + + const info = await page.evaluate(() => { + const buttons = Array.from(document.querySelectorAll("button")).filter((b) => { + const t = (b as HTMLElement).innerText?.trim() || ""; + const r = (b as HTMLElement).getBoundingClientRect(); + return t.length > 1 && r.x > 250; + }); + const leftPanel = document.querySelector("[class*='border-r']"); + const tables = document.querySelectorAll("table"); + const bodyText = document.body.innerText; + + return { + buttonCount: buttons.length, + buttonDetails: buttons.slice(0, 15).map((b) => { + const r = (b as HTMLElement).getBoundingClientRect(); + return { + text: (b as HTMLElement).innerText?.trim().substring(0, 30), + x: Math.round(r.x), + y: Math.round(r.y), + width: Math.round(r.width), + height: Math.round(r.height), + }; + }), + splitPanelVisible: !!leftPanel || bodyText.includes("๊ณต๊ธ‰์ฒ˜") || bodyText.includes("์ขŒ์ธก์—์„œ"), + tableCount: tables.length, + hasExcelDownload: bodyText.includes("์—‘์…€") || bodyText.includes("๋‹ค์šด๋กœ๋“œ") || bodyText.includes("์—…๋กœ๋“œ"), + }; + }); + + report.screen1053 = info; + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "screen-1053-admin.png"), fullPage: true }); + console.log("screen-1053-admin.png saved"); + + fs.writeFileSync( + path.join(SCREENSHOT_DIR, "screen-1053-admin-report.json"), + JSON.stringify(report, null, 2) + ); + console.log("\n=== Report ==="); + console.log(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-1053-admin-error.png"), fullPage: true }).catch(() => {}); + } finally { + await browser.close(); + } +} + +main(); diff --git a/frontend/scripts/verify-screen-1053.ts b/frontend/scripts/verify-screen-1053.ts new file mode 100644 index 00000000..1f0cb96f --- /dev/null +++ b/frontend/scripts/verify-screen-1053.ts @@ -0,0 +1,105 @@ +/** + * ํ™”๋ฉด 1053 ๊ฒ€์ฆ: split-panel ๋ ˆ์ด์•„์›ƒ + * - ์ขŒ/์šฐ ํŒจ๋„, ๋ฒ„ํŠผ ์˜ค๋ฒ„๋ ˆ์ด, ๋†’์ด ์ฑ„์›€, overflow ํ™•์ธ + */ + +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/1053`, { waitUntil: "load", timeout: 30000 }); + await page.waitForTimeout(2000); + + if (page.url().includes("/login")) { + 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/1053`, { waitUntil: "load", timeout: 30000 }); + await page.waitForTimeout(2000); + } + + await page.getByText("๋กœ๋”ฉ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 10000 }).catch(() => {}); + await page.waitForTimeout(1500); + + report.pageLoadsWithoutErrors = (await page.locator('text="ํ™”๋ฉด์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค"').count()) === 0; + + const splitPanel = await page.evaluate(() => { + const leftPanel = document.querySelector("[class*='split'][class*='left'], [data-panel='left'], [class*='left-panel']"); + const rightPanel = document.querySelector("[class*='split'][class*='right'], [data-panel='right'], [class*='right-panel']"); + const resizable = document.querySelector("[class*='resize'], [class*='ResizablePanel]"); + const panels = document.querySelectorAll("[class*='panel'], [data-panel]"); + return { + hasLeftPanel: !!leftPanel || document.body.innerText.includes("left") || panels.length >= 2, + hasRightPanel: !!rightPanel || panels.length >= 2, + panelCount: panels.length, + resizableCount: document.querySelectorAll("[class*='ResizablePanel'], [class*='resize']").length, + }; + }); + report.splitPanelVisible = splitPanel.panelCount >= 2 || splitPanel.resizableCount > 0; + + const twoPanels = await page.locator("[class*='panel'], [data-panel], [class*='split']").count(); + report.twoPanelsFound = twoPanels >= 2; + + const buttons = await page.locator("button").count(); + const buttonsTopRight = await page.evaluate(() => { + const btns = Array.from(document.querySelectorAll("button")); + const viewportW = window.innerWidth; + return btns.filter((b) => { + const r = b.getBoundingClientRect(); + return r.right > viewportW * 0.5 && r.top < 200; + }).length; + }); + report.buttonsVisible = buttons > 0; + report.buttonsInTopRightArea = buttonsTopRight; + + const layoutFillsHeight = await page.evaluate(() => { + const main = document.querySelector("main") || document.body; + const h = (main as HTMLElement).offsetHeight; + return h >= window.innerHeight * 0.8; + }); + report.layoutFillsHeight = layoutFillsHeight; + + const overflow = await page.evaluate(() => ({ + bodyScrollWidth: document.body.scrollWidth, + bodyScrollHeight: document.body.scrollHeight, + viewportWidth: window.innerWidth, + viewportHeight: window.innerHeight, + hasHorizontalOverflow: document.body.scrollWidth > window.innerWidth, + hasVerticalOverflow: document.body.scrollHeight > window.innerHeight, + })); + report.noContentOverflowsViewport = !overflow.hasHorizontalOverflow; + report.overflowDetails = overflow; + + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "screen-1053-snapshot.png"), fullPage: true }); + console.log("screen-1053-snapshot.png saved"); + + console.log("\n=== Report ==="); + console.log(JSON.stringify(report, null, 2)); + + fs.writeFileSync( + path.join(SCREENSHOT_DIR, "screen-1053-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-1053-error.png"), fullPage: true }).catch(() => {}); + } finally { + await browser.close(); + } +} + +main(); diff --git a/frontend/scripts/verify-screen-1244-layout.ts b/frontend/scripts/verify-screen-1244-layout.ts new file mode 100644 index 00000000..361ef742 --- /dev/null +++ b/frontend/scripts/verify-screen-1244-layout.ts @@ -0,0 +1,156 @@ +/** + * ํ™”๋ฉด 1244 ๊ฒ€์ฆ: table-list ๋ ˆ์ด์•„์›ƒ (๋ฐ์Šคํฌํ†ฑ + ๋ชจ๋ฐ”์ผ) + */ + +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 = { desktop: {}, mobile: {} }; + + try { + await page.goto(`${BASE_URL}/screens/1244`, { waitUntil: "load", timeout: 30000 }); + await page.waitForTimeout(2000); + + if (page.url().includes("/login")) { + 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/1244`, { waitUntil: "load", timeout: 30000 }); + await page.waitForTimeout(2000); + } + + await page.getByText("๋กœ๋”ฉ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 10000 }).catch(() => {}); + await page.waitForTimeout(2000); + + report.desktop.pageLoadsWithoutErrors = + (await page.locator('text="ํ™”๋ฉด์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค"').count()) === 0; + + const desktopInfo = await page.evaluate(() => { + const table = document.querySelector("table"); + const thead = document.querySelector("thead"); + const tbody = document.querySelector("tbody"); + const ths = document.querySelectorAll("thead th"); + const buttons = document.querySelectorAll("button"); + const searchInputs = document.querySelectorAll('input[type="text"], input[type="search"], select'); + const pagination = document.body.innerText.includes("ํ‘œ์‹œ") || + document.body.innerText.includes("1/") || + document.body.innerText.includes("ํŽ˜์ด์ง€") || + document.querySelector("[class*='pagination'], [class*='Pagination']"); + + let buttonsBetweenSearchAndTable = 0; + const searchY = searchInputs.length > 0 + ? (searchInputs[0] as HTMLElement).getBoundingClientRect().bottom + : 0; + const tableY = table ? (table as HTMLElement).getBoundingClientRect().top : 0; + + buttons.forEach((btn) => { + const rect = (btn as HTMLElement).getBoundingClientRect(); + if (rect.top >= searchY - 20 && rect.top <= tableY + 100) buttonsBetweenSearchAndTable++; + }); + + return { + tableVisible: !!table && !!thead && !!tbody, + columnCount: ths.length, + buttonsVisible: buttons.length > 0, + buttonsBetweenSearchAndTable, + paginationVisible: !!pagination, + bodyScrollWidth: document.body.scrollWidth, + viewportWidth: window.innerWidth, + hasHorizontalOverflow: document.body.scrollWidth > window.innerWidth, + tableScrollWidth: table ? (table as HTMLElement).scrollWidth : 0, + tableClientWidth: table ? (table as HTMLElement).clientWidth : 0, + }; + }); + + report.desktop.buttonsVisible = desktopInfo.buttonsVisible; + report.desktop.buttonsBetweenSearchAndTable = desktopInfo.buttonsBetweenSearchAndTable; + report.desktop.tableVisible = desktopInfo.tableVisible; + report.desktop.columnCount = desktopInfo.columnCount; + report.desktop.paginationVisible = desktopInfo.paginationVisible; + report.desktop.noHorizontalOverflow = !desktopInfo.hasHorizontalOverflow; + report.desktop.overflowDetails = { + bodyScrollWidth: desktopInfo.bodyScrollWidth, + viewportWidth: desktopInfo.viewportWidth, + tableScrollWidth: desktopInfo.tableScrollWidth, + tableClientWidth: desktopInfo.tableClientWidth, + }; + + await page.screenshot({ + path: path.join(SCREENSHOT_DIR, "screen-1244-desktop.png"), + fullPage: true, + }); + console.log("screen-1244-desktop.png saved"); + + await page.setViewportSize({ width: 768, height: 900 }); + await page.waitForTimeout(1500); + + const mobileInfo = await page.evaluate(() => { + const table = document.querySelector("table"); + const thead = document.querySelector("thead"); + const tbody = document.querySelector("tbody"); + const ths = document.querySelectorAll("thead th"); + const buttons = document.querySelectorAll("button"); + const pagination = document.body.innerText.includes("ํ‘œ์‹œ") || + document.body.innerText.includes("1/") || + document.body.innerText.includes("ํŽ˜์ด์ง€") || + document.querySelector("[class*='pagination'], [class*='Pagination']"); + + return { + tableVisible: !!table && !!thead && !!tbody, + columnCount: ths.length, + buttonsVisible: buttons.length > 0, + paginationVisible: !!pagination, + bodyScrollWidth: document.body.scrollWidth, + viewportWidth: window.innerWidth, + hasHorizontalOverflow: document.body.scrollWidth > window.innerWidth, + }; + }); + + report.mobile.pageLoadsWithoutErrors = report.desktop.pageLoadsWithoutErrors; + report.mobile.buttonsVisible = mobileInfo.buttonsVisible; + report.mobile.tableVisible = mobileInfo.tableVisible; + report.mobile.columnCount = mobileInfo.columnCount; + report.mobile.paginationVisible = mobileInfo.paginationVisible; + report.mobile.noHorizontalOverflow = !mobileInfo.hasHorizontalOverflow; + report.mobile.overflowDetails = { + bodyScrollWidth: mobileInfo.bodyScrollWidth, + viewportWidth: mobileInfo.viewportWidth, + }; + + await page.screenshot({ + path: path.join(SCREENSHOT_DIR, "screen-1244-mobile-768.png"), + fullPage: true, + }); + console.log("screen-1244-mobile-768.png saved"); + + console.log("\n=== Report ==="); + console.log(JSON.stringify(report, null, 2)); + + fs.writeFileSync( + path.join(SCREENSHOT_DIR, "screen-1244-layout-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-1244-error.png"), + fullPage: true, + }).catch(() => {}); + } finally { + await browser.close(); + } +} + +main(); diff --git a/frontend/scripts/verify-screen-1244-refresh.ts b/frontend/scripts/verify-screen-1244-refresh.ts new file mode 100644 index 00000000..aaeb7b9d --- /dev/null +++ b/frontend/scripts/verify-screen-1244-refresh.ts @@ -0,0 +1,143 @@ +/** + * ํ™”๋ฉด 1244 ์ƒˆ๋กœ๊ณ ์นจ ํ›„ ์ƒ์„ธ ๊ฒ€์ฆ + * - data-screen-runtime, ํ…Œ์ด๋ธ”, body์˜ scrollWidth/clientWidth ํ™•์ธ + */ + +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 results: Record = {}; + + try { + // ๋กœ๊ทธ์ธ + await page.goto(`${BASE_URL}/login`, { waitUntil: "load", timeout: 30000 }); + await page.waitForTimeout(1000); + 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); + + // /screens/1244 ์ ‘์† + await page.goto(`${BASE_URL}/screens/1244`, { waitUntil: "load", timeout: 45000 }); + await page.getByText("๋กœ๋”ฉ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 10000 }).catch(() => {}); + await page.waitForTimeout(2000); + + // Step 1: ์ƒˆ๋กœ๊ณ ์นจ (Ctrl+Shift+R - hard refresh) + console.log("Step 1: ์ƒˆ๋กœ๊ณ ์นจ (Ctrl+Shift+R)..."); + await page.keyboard.press("Control+Shift+r"); + await page.waitForLoadState("load"); + await page.waitForTimeout(3000); + await page.getByText("๋กœ๋”ฉ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 10000 }).catch(() => {}); + + // Step 2: ์ฒซ ์Šคํฌ๋ฆฐ์ƒท + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "verify-06.png"), fullPage: true }); + console.log("verify-06.png ์ €์žฅ"); + + // Step 3: JavaScript๋กœ dimension ํ™•์ธ + const dims = await page.evaluate(() => { + const screenRuntime = document.querySelector("[data-screen-runtime]"); + const table = document.querySelector("table"); + const body = document.body; + const html = document.documentElement; + + const tableContainer = table?.closest("[style*='overflow'], [class*='overflow']") || table?.parentElement; + + return { + screenRuntime: screenRuntime + ? { + offsetWidth: (screenRuntime as HTMLElement).offsetWidth, + scrollWidth: (screenRuntime as HTMLElement).scrollWidth, + clientWidth: (screenRuntime as HTMLElement).clientWidth, + } + : null, + table: table + ? { + offsetWidth: table.offsetWidth, + scrollWidth: table.scrollWidth, + clientWidth: table.clientWidth, + } + : null, + tableContainer: tableContainer + ? { + clientWidth: (tableContainer as HTMLElement).clientWidth, + scrollWidth: (tableContainer as HTMLElement).scrollWidth, + offsetWidth: (tableContainer as HTMLElement).offsetWidth, + } + : null, + body: { + scrollWidth: body.scrollWidth, + clientWidth: body.clientWidth, + offsetWidth: body.offsetWidth, + }, + html: { + scrollWidth: html.scrollWidth, + clientWidth: html.clientWidth, + }, + viewport: { + innerWidth: window.innerWidth, + innerHeight: window.innerHeight, + }, + }; + }); + + results.screenRuntime_offsetWidth = dims.screenRuntime?.offsetWidth ?? null; + results.screenRuntime_scrollWidth = dims.screenRuntime?.scrollWidth ?? null; + results.screenRuntime_clientWidth = dims.screenRuntime?.clientWidth ?? null; + results.table_offsetWidth = dims.table?.offsetWidth ?? null; + results.table_scrollWidth = dims.table?.scrollWidth ?? null; + results.table_clientWidth = dims.table?.clientWidth ?? null; + results.tableContainer_clientWidth = dims.tableContainer?.clientWidth ?? null; + results.tableContainer_scrollWidth = dims.tableContainer?.scrollWidth ?? null; + results.body_scrollWidth = dims.body.scrollWidth; + results.body_clientWidth = dims.body.clientWidth; + results.viewport_innerWidth = dims.viewport.innerWidth; + + // Step 4: ๊ฐ€๋กœ overflow ํ™•์ธ + const hasOverflow = dims.body.scrollWidth > dims.viewport.innerWidth; + results.bodyOverflowX = hasOverflow; + + // Step 5: ๋‘ ๋ฒˆ์งธ ์Šคํฌ๋ฆฐ์ƒท + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "verify-07.png"), fullPage: true }); + console.log("verify-07.png ์ €์žฅ"); + + console.log("\n=== ๊ฒ€์ฆ ๊ฒฐ๊ณผ ==="); + console.log("data-screen-runtime div:"); + console.log(" offsetWidth:", results.screenRuntime_offsetWidth); + console.log(" scrollWidth:", results.screenRuntime_scrollWidth); + console.log(" clientWidth:", results.screenRuntime_clientWidth); + console.log("ํ…Œ์ด๋ธ”:"); + console.log(" offsetWidth:", results.table_offsetWidth); + console.log(" scrollWidth:", results.table_scrollWidth); + console.log(" clientWidth:", results.table_clientWidth); + console.log("ํ…Œ์ด๋ธ” ์ปจํ…Œ์ด๋„ˆ:"); + console.log(" clientWidth:", results.tableContainer_clientWidth); + console.log(" scrollWidth:", results.tableContainer_scrollWidth); + console.log("body:"); + console.log(" scrollWidth:", results.body_scrollWidth); + console.log(" clientWidth:", results.body_clientWidth); + console.log("๋ทฐํฌํŠธ innerWidth:", results.viewport_innerWidth); + console.log("๊ฐ€๋กœ overflow:", results.bodyOverflowX); + + fs.writeFileSync( + path.join(SCREENSHOT_DIR, "verify-refresh-result.json"), + JSON.stringify({ ...results, rawDims: dims }, null, 2) + ); + } catch (error: any) { + console.error("์˜ค๋ฅ˜:", error.message); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "verify-99-error.png"), fullPage: true }).catch(() => {}); + } finally { + await browser.close(); + } +} + +main(); diff --git a/frontend/scripts/verify-screen-1244-refresh2.ts b/frontend/scripts/verify-screen-1244-refresh2.ts new file mode 100644 index 00000000..0bf151a1 --- /dev/null +++ b/frontend/scripts/verify-screen-1244-refresh2.ts @@ -0,0 +1,142 @@ +/** + * ํ™”๋ฉด 1244 ์ƒˆ๋กœ๊ณ ์นจ ๊ฒ€์ฆ (2์ฐจ) + * - 3์ดˆ ๋Œ€๊ธฐ ํ›„ ์Šคํฌ๋ฆฐ์ƒท + * - data-screen-runtime, ํ…Œ์ด๋ธ” ๊ด€๋ จ div width ํ™•์ธ + * - ๊ฐ€๋กœ ์Šคํฌ๋กค ๊ฐ€๋Šฅ ์—ฌ๋ถ€ ํ™•์ธ + */ + +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(); + + try { + // ๋กœ๊ทธ์ธ + await page.goto(`${BASE_URL}/login`, { waitUntil: "load", timeout: 30000 }); + await page.waitForTimeout(1000); + 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); + + // /screens/1244 ์ ‘์† + await page.goto(`${BASE_URL}/screens/1244`, { waitUntil: "load", timeout: 45000 }); + await page.getByText("๋กœ๋”ฉ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 10000 }).catch(() => {}); + await page.waitForTimeout(2000); + + // Step 1: ์ƒˆ๋กœ๊ณ ์นจ (Ctrl+Shift+R) + console.log("Step 1: ์ƒˆ๋กœ๊ณ ์นจ (Ctrl+Shift+R)..."); + await page.keyboard.press("Control+Shift+r"); + await page.waitForLoadState("load"); + console.log("Step 2: 3์ดˆ ๋Œ€๊ธฐ..."); + await page.waitForTimeout(3000); + await page.getByText("๋กœ๋”ฉ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 8000 }).catch(() => {}); + + // Step 3: ์ฒซ ์Šคํฌ๋ฆฐ์ƒท + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "verify-08.png"), fullPage: true }); + console.log("verify-08.png ์ €์žฅ"); + + // Step 4: JavaScript๋กœ width ํ™•์ธ (์ˆœ์ˆ˜ ํ•จ์ˆ˜๋กœ ์ž‘์„ฑ) + const dims = await page.evaluate(() => { + const screenRuntime = document.querySelector("[data-screen-runtime]"); + const table = document.querySelector("table"); + const tableContainer = table?.closest("[style*='overflow'], [class*='overflow']"); + const overflowHiddenDiv = table?.closest("[style*='overflow-hidden'], [class*='overflow-hidden']"); + + const tableAncestors: Array<{ level: number; tag: string; class: string; offsetWidth: number; scrollWidth: number; clientWidth: number; overflowX: string }> = []; + let p = table?.parentElement; + let idx = 0; + while (p && idx < 6) { + const s = window.getComputedStyle(p); + tableAncestors.push({ + level: idx, + tag: p.tagName, + class: (p.className && typeof p.className === "string" ? p.className : "").slice(0, 60), + offsetWidth: p.offsetWidth, + scrollWidth: p.scrollWidth, + clientWidth: p.clientWidth, + overflowX: s.overflowX, + }); + p = p.parentElement; + idx++; + } + + return { + screenRuntime: screenRuntime + ? { offsetWidth: (screenRuntime as HTMLElement).offsetWidth, scrollWidth: (screenRuntime as HTMLElement).scrollWidth, clientWidth: (screenRuntime as HTMLElement).clientWidth } + : null, + table: table + ? { offsetWidth: (table as HTMLElement).offsetWidth, scrollWidth: (table as HTMLElement).scrollWidth, clientWidth: (table as HTMLElement).clientWidth } + : null, + tableContainer: tableContainer + ? { offsetWidth: (tableContainer as HTMLElement).offsetWidth, scrollWidth: (tableContainer as HTMLElement).scrollWidth, clientWidth: (tableContainer as HTMLElement).clientWidth } + : null, + overflowHiddenDiv: overflowHiddenDiv + ? { offsetWidth: (overflowHiddenDiv as HTMLElement).offsetWidth, scrollWidth: (overflowHiddenDiv as HTMLElement).scrollWidth, clientWidth: (overflowHiddenDiv as HTMLElement).clientWidth } + : null, + tableAncestors, + viewport: { innerWidth: window.innerWidth }, + }; + }); + + console.log("\n=== JavaScript ์‹คํ–‰ ๊ฒฐ๊ณผ ==="); + console.log("data-screen-runtime div:"); + if (dims.screenRuntime) { + console.log(" offsetWidth:", dims.screenRuntime.offsetWidth); + console.log(" scrollWidth:", dims.screenRuntime.scrollWidth); + console.log(" clientWidth:", dims.screenRuntime.clientWidth); + } else { + console.log(" (์—†์Œ)"); + } + console.log("\nํ…Œ์ด๋ธ”:"); + if (dims.table) { + console.log(" offsetWidth:", dims.table.offsetWidth); + console.log(" scrollWidth:", dims.table.scrollWidth); + } + console.log("\nํ…Œ์ด๋ธ” ์ปจํ…Œ์ด๋„ˆ (overflow):"); + if (dims.tableContainer) { + console.log(" offsetWidth:", dims.tableContainer.offsetWidth); + console.log(" scrollWidth:", dims.tableContainer.scrollWidth); + console.log(" clientWidth:", dims.tableContainer.clientWidth); + } + console.log("\noverflow-hidden div:"); + if (dims.overflowHiddenDiv) { + console.log(" offsetWidth:", dims.overflowHiddenDiv.offsetWidth); + console.log(" scrollWidth:", dims.overflowHiddenDiv.scrollWidth); + } else { + console.log(" (์—†์Œ)"); + } + console.log("\nํ…Œ์ด๋ธ” ์กฐ์ƒ div๋“ค (width):"); + dims.tableAncestors?.forEach((a) => { + console.log(` L${a.level} ${a.tag} overflow=${a.overflowX} offsetW=${a.offsetWidth} scrollW=${a.scrollWidth} clientW=${a.clientWidth}`); + }); + + // Step 5: ๊ฐ€๋กœ ์Šคํฌ๋กค ๊ฐ€๋Šฅ ์—ฌ๋ถ€ + const canScroll = dims.table && dims.tableContainer && dims.table.scrollWidth > dims.tableContainer.clientWidth; + console.log("\n๊ฐ€๋กœ ์Šคํฌ๋กค ๊ฐ€๋Šฅ:", canScroll, "(ํ…Œ์ด๋ธ” scrollWidth > ์ปจํ…Œ์ด๋„ˆ clientWidth)"); + + // Step 6: ์ตœ์ข… ์Šคํฌ๋ฆฐ์ƒท + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "verify-09.png"), fullPage: true }); + console.log("\nverify-09.png ์ €์žฅ"); + + fs.writeFileSync( + path.join(SCREENSHOT_DIR, "verify-refresh2-result.json"), + JSON.stringify({ ...dims, canScroll }, null, 2) + ); + } catch (error: any) { + console.error("์˜ค๋ฅ˜:", error.message); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "verify-99-error.png"), fullPage: true }).catch(() => {}); + } finally { + await browser.close(); + } +} + +main(); diff --git a/frontend/scripts/verify-screen-1244.ts b/frontend/scripts/verify-screen-1244.ts new file mode 100644 index 00000000..f21a2bc8 --- /dev/null +++ b/frontend/scripts/verify-screen-1244.ts @@ -0,0 +1,127 @@ +/** + * ํ™”๋ฉด 1244 ๊ฒ€์ฆ: ํ…Œ์ด๋ธ”, ๊ฐ€๋กœ ์Šคํฌ๋กค, ํŽ˜์ด์ง€๋„ค์ด์…˜ ํ™•์ธ + */ + +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 results: Record = {}; + + try { + // Step 1: ๋กœ๊ทธ์ธ ๋จผ์ € (Playwright๋Š” ์ƒˆ ๋ธŒ๋ผ์šฐ์ €) + console.log("Step 1: ๋กœ๊ทธ์ธ..."); + await page.goto(`${BASE_URL}/login`, { waitUntil: "load", timeout: 30000 }); + await page.waitForTimeout(1000); + 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); + + // Step 2: /screens/1244 ์ ‘์† + console.log("Step 2: /screens/1244 ์ ‘์†..."); + await page.goto(`${BASE_URL}/screens/1244`, { waitUntil: "load", timeout: 45000 }); + await page.waitForTimeout(2000); + + await page.getByText("๋กœ๋”ฉ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 10000 }).catch(() => {}); + await page.waitForTimeout(2000); + + // Step 2: ํ™”๋ฉด ๋กœ๋“œ ์Šคํฌ๋ฆฐ์ƒท + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "verify-01-initial.png"), fullPage: true }); + console.log("verify-01-initial.png ์ €์žฅ"); + + // Step 3: ํ…Œ์ด๋ธ” ํ™•์ธ + const table = page.locator("table").first(); + const tableCount = await table.count(); + results.tableVisible = tableCount > 0; + console.log("ํ…Œ์ด๋ธ” ๋ณด์ž„:", results.tableVisible); + + // Step 4: ๊ฐ€๋กœ ์Šคํฌ๋กค๋ฐ” ํ™•์ธ + const scrollContainer = page.locator("[class*='overflow'], .overflow-x-auto, [style*='overflow']").first(); + const hasScrollContainer = (await scrollContainer.count()) > 0; + let scrollWidth = 0; + let clientWidth = 0; + if (results.tableVisible) { + scrollWidth = await table.evaluate((el) => el.scrollWidth); + clientWidth = await table.evaluate((el) => el.clientWidth); + results.tableScrollWidth = String(scrollWidth); + results.tableClientWidth = String(clientWidth); + results.horizontalScrollNeeded = scrollWidth > clientWidth; + } + console.log("ํ…Œ์ด๋ธ” scrollWidth:", scrollWidth, "clientWidth:", clientWidth); + + // Step 5: ํ…Œ์ด๋ธ” ์˜์—ญ ์˜ค๋ฅธ์ชฝ ์Šคํฌ๋กค ์‹œ๋„ (overflow-auto์ธ ์กฐ์ƒ ์š”์†Œ ์ฐพ๊ธฐ) + if (results.tableVisible) { + try { + const scrollableAncestor = await table.evaluateHandle((el) => { + let parent: HTMLElement | null = el.parentElement; + while (parent) { + const style = getComputedStyle(parent); + if (style.overflowX === "auto" || style.overflowX === "scroll" || style.overflow === "auto") { + return parent; + } + parent = parent.parentElement; + } + return el.parentElement; + }); + const scrollEl = scrollableAncestor.asElement(); + if (scrollEl) { + await scrollEl.evaluate((el) => (el.scrollLeft = 300)); + await page.waitForTimeout(500); + } + } catch (_) {} + } + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "verify-02-after-scroll.png"), fullPage: true }); + console.log("verify-02-after-scroll.png ์ €์žฅ"); + + // Step 6: ํŽ˜์ด์ง€๋„ค์ด์…˜ ํ™•์ธ + const paginationText = page.getByText("ํ‘œ์‹œ", { exact: false }).or(page.getByText("1/1", { exact: false })); + results.paginationVisible = (await paginationText.count()) > 0; + console.log("ํŽ˜์ด์ง€๋„ค์ด์…˜ ๋ณด์ž„:", results.paginationVisible); + + // Step 7: ํ…Œ์ด๋ธ”์ด ๋ทฐํฌํŠธ์— ๋งž๋Š”์ง€ (overflow ํ™•์ธ) + const bodyOverflow = await page.evaluate(() => { + const main = document.querySelector("main") || document.body; + return window.getComputedStyle(main).overflowX; + }); + results.bodyOverflowX = bodyOverflow; + + // Step 8: ์ค‘๊ฐ„ ์Šคํฌ๋ฆฐ์ƒท (ํ…Œ์ด๋ธ” + ํŽ˜์ด์ง€๋„ค์ด์…˜ ์˜์—ญ) + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "verify-03-mid.png"), fullPage: true }); + console.log("verify-03-mid.png ์ €์žฅ"); + + // Step 9: ํŽ˜์ด์ง€ ํ•˜๋‹จ์œผ๋กœ ์Šคํฌ๋กค (ํŽ˜์ด์ง€๋„ค์ด์…˜ ๋ฐ” ํ™•์ธ) + await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight)); + await page.waitForTimeout(500); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "verify-04-pagination-area.png"), fullPage: true }); + console.log("verify-04-pagination-area.png ์ €์žฅ"); + + // Step 10: ์ตœ์ข… ์Šคํฌ๋ฆฐ์ƒท + await page.evaluate(() => window.scrollTo(0, 0)); + await page.waitForTimeout(300); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "verify-05-final.png"), fullPage: true }); + console.log("verify-05-final.png ์ €์žฅ"); + + fs.writeFileSync( + path.join(SCREENSHOT_DIR, "verify-result.json"), + JSON.stringify(results, null, 2) + ); + console.log("\n๊ฒ€์ฆ ๊ฒฐ๊ณผ:", results); + } catch (error: any) { + console.error("์˜ค๋ฅ˜:", error.message); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "verify-99-error.png"), fullPage: true }).catch(() => {}); + } finally { + await browser.close(); + } +} + +main(); diff --git a/frontend/scripts/verify-screen-150-tapseal.ts b/frontend/scripts/verify-screen-150-tapseal.ts new file mode 100644 index 00000000..dbc210b1 --- /dev/null +++ b/frontend/scripts/verify-screen-150-tapseal.ts @@ -0,0 +1,162 @@ +/** + * ํ™”๋ฉด 150 ๊ฒ€์ฆ - ํƒ‘์”ฐ ์˜์—… ๊ฑฐ๋ž˜์ฒ˜๊ด€๋ฆฌ + */ + +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}/login`, { waitUntil: "load", timeout: 90000 }); + await page.waitForTimeout(2000); + + await page.fill("#userId", "admin"); + await page.fill("#password", "1234"); + await page.locator('button[type="submit"]').first().click(); + await page.waitForURL((u: URL) => !u.includes("/login"), { timeout: 20000 }).catch(() => {}); + await page.waitForTimeout(3000); + + if (page.url().includes("/login")) { + await page.fill("#userId", "wace"); + await page.fill("#password", "qlalfqjsgh11"); + await page.locator('button[type="submit"]').first().click(); + await page.waitForURL((u: URL) => !u.includes("/login"), { timeout: 20000 }).catch(() => {}); + await page.waitForTimeout(3000); + } + + const companyBtn = page.getByText("ํšŒ์‚ฌ ์„ ํƒ").first(); + if ((await companyBtn.count()) > 0) { + const currentCompany = await page.getByText("ํ˜„์žฌ ๊ด€๋ฆฌ ํšŒ์‚ฌ").locator("..").textContent().catch(() => ""); + if (!currentCompany?.includes("ํƒ‘์”ฐ") && !currentCompany?.includes("COMPANY_7")) { + await companyBtn.click(); + await page.waitForTimeout(1500); + const tapseal = page.getByText("ํƒ‘์”ฐ", { exact: true }).first(); + const company7 = page.getByText("COMPANY_7", { exact: true }).first(); + if ((await tapseal.count()) > 0) { + await tapseal.click(); + } else if ((await company7.count()) > 0) { + await company7.click(); + } + await page.waitForTimeout(2000); + } + } + + await page.goto(`${BASE_URL}/screens/150`, { waitUntil: "domcontentloaded", timeout: 60000 }); + await page.waitForTimeout(3000); + if (page.url().includes("/login")) { + await page.fill("#userId", "wace"); + await page.fill("#password", "qlalfqjsgh11"); + await page.locator('button[type="submit"]').first().click(); + await page.waitForURL((u: URL) => !u.includes("/login"), { timeout: 20000 }).catch(() => {}); + await page.waitForTimeout(3000); + await page.goto(`${BASE_URL}/screens/150`, { waitUntil: "domcontentloaded", timeout: 60000 }); + await page.waitForTimeout(3000); + } + + await page.getByText("ํ™”๋ฉด์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 25000 }).catch(() => {}); + await page.waitForTimeout(8000); + + const info = await page.evaluate(() => { + const buttons = Array.from(document.querySelectorAll("button")).filter((b) => { + const t = (b as HTMLElement).innerText?.trim() || ""; + const r = (b as HTMLElement).getBoundingClientRect(); + return t.length > 1 && r.x > 250 && r.width > 0; + }); + const viewportWidth = window.innerWidth; + const leftThird = viewportWidth * 0.33; + const rightThird = viewportWidth * 0.66; + + const btnDetails = buttons.map((b) => { + const r = (b as HTMLElement).getBoundingClientRect(); + const text = (b as HTMLElement).innerText?.trim().substring(0, 40); + let group = "center"; + if (r.x < leftThird) group = "left"; + else if (r.x > rightThird) group = "right"; + return { + text, + x: Math.round(r.x), + y: Math.round(r.y), + width: Math.round(r.width), + height: Math.round(r.height), + right: Math.round(r.right), + group, + }; + }); + + const leftPanel = document.querySelector("[class*='border-r']"); + const tables = document.querySelectorAll("table"); + const rightPanel = document.querySelector("main")?.querySelectorAll("[class*='overflow'], [style*='overflow']"); + const leftRect = leftPanel ? (leftPanel as HTMLElement).getBoundingClientRect() : null; + const mainRect = document.querySelector("main")?.getBoundingClientRect(); + const contentWidth = mainRect ? mainRect.width : viewportWidth; + const leftWidthPercent = leftRect && contentWidth > 0 ? (leftRect.width / contentWidth) * 100 : null; + + let overlaps = false; + for (let i = 0; i < btnDetails.length; i++) { + for (let j = i + 1; j < btnDetails.length; j++) { + const a = btnDetails[i]; + const b = btnDetails[j]; + if (Math.abs(a.y - b.y) < 30 && !(a.right < b.x || b.right < a.x)) overlaps = true; + } + } + + const rightTable = tables.length > 1 ? tables[1] : tables[0]; + const rightTableRect = rightTable ? (rightTable as HTMLElement).getBoundingClientRect() : null; + const rightTableScrollable = rightTable + ? (() => { + let el: Element | null = rightTable; + while (el) { + const s = window.getComputedStyle(el); + if (s.overflowY === "auto" || s.overflowY === "scroll" || s.overflow === "auto") return true; + el = el.parentElement; + } + return false; + })() + : null; + + return { + buttonCount: buttons.length, + buttonDetails: btnDetails, + leftGroup: btnDetails.filter((b) => b.group === "left"), + centerGroup: btnDetails.filter((b) => b.group === "center"), + rightGroup: btnDetails.filter((b) => b.group === "right"), + splitPanelVisible: !!leftPanel, + leftWidthPercent: leftWidthPercent ? Math.round(leftWidthPercent) : null, + rightWidthPercent: leftWidthPercent ? Math.round(100 - leftWidthPercent) : null, + tableCount: tables.length, + rightPanelHasTable: !!rightTable, + rightTableScrollable, + buttonsOverlap: overlaps, + }; + }); + + report.screen150 = info; + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "screen-150-tapseal.png"), fullPage: true }); + console.log("screen-150-tapseal.png saved"); + + fs.writeFileSync( + path.join(SCREENSHOT_DIR, "screen-150-tapseal-report.json"), + JSON.stringify(report, null, 2) + ); + console.log("\n=== Report ==="); + console.log(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-150-tapseal-error.png"), fullPage: true }).catch(() => {}); + } finally { + await browser.close(); + } +} + +main(); diff --git a/frontend/scripts/verify-screen-1556.ts b/frontend/scripts/verify-screen-1556.ts new file mode 100644 index 00000000..f6a4f660 --- /dev/null +++ b/frontend/scripts/verify-screen-1556.ts @@ -0,0 +1,93 @@ +/** + * ํ™”๋ฉด 1556 ๊ฒ€์ฆ: tabs-widget ๋ ˆ์ด์•„์›ƒ + */ + +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/1556`, { waitUntil: "load", timeout: 30000 }); + await page.waitForTimeout(2000); + + if (page.url().includes("/login")) { + 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/1556`, { waitUntil: "load", timeout: 30000 }); + await page.waitForTimeout(2000); + } + + await page.getByText("๋กœ๋”ฉ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 10000 }).catch(() => {}); + await page.waitForTimeout(2000); + + report.pageLoadsWithoutErrors = (await page.locator('text="ํ™”๋ฉด์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค"').count()) === 0; + + const tabInfo = await page.evaluate(() => { + const tabs = document.querySelectorAll("[role='tab'], [data-state='active'], [class*='TabsTrigger'], [class*='tab']"); + const tabList = document.querySelector("[role='tablist']"); + const loading = document.body.innerText.includes("๋กœ๋”ฉ์ค‘") || document.body.innerText.includes("๋กœ๋”ฉ ์ค‘"); + return { + tabCount: tabs.length, + hasTabList: !!tabList, + tabHeadersVisible: tabs.length > 0 || !!tabList, + stuckOnLoading: loading, + }; + }); + report.tabHeadersVisible = tabInfo.tabHeadersVisible; + report.tabCount = tabInfo.tabCount; + report.tabContentLoadsProperly = !tabInfo.stuckOnLoading; + + const loadingText = await page.getByText("๋กœ๋”ฉ์ค‘", { exact: false }).count(); + report.stuckOnLoading = loadingText > 0; + + const layoutFillsHeight = await page.evaluate(() => { + const main = document.querySelector("main") || document.body; + const h = (main as HTMLElement).offsetHeight; + return h >= window.innerHeight * 0.8; + }); + report.layoutFillsHeight = layoutFillsHeight; + + const overflow = await page.evaluate(() => ({ + bodyScrollWidth: document.body.scrollWidth, + bodyScrollHeight: document.body.scrollHeight, + viewportWidth: window.innerWidth, + viewportHeight: window.innerHeight, + hasHorizontalOverflow: document.body.scrollWidth > window.innerWidth, + hasVerticalOverflow: document.body.scrollHeight > window.innerHeight, + })); + report.noContentOverflowsViewport = !overflow.hasHorizontalOverflow; + report.overflowDetails = overflow; + + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "screen-1556-snapshot.png"), fullPage: true }); + console.log("screen-1556-snapshot.png saved"); + + console.log("\n=== Report ==="); + console.log(JSON.stringify(report, null, 2)); + + fs.writeFileSync( + path.join(SCREENSHOT_DIR, "screen-1556-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-1556-error.png"), fullPage: true }).catch(() => {}); + } finally { + await browser.close(); + } +} + +main(); diff --git a/frontend/scripts/verify-screen-156.ts b/frontend/scripts/verify-screen-156.ts new file mode 100644 index 00000000..de202465 --- /dev/null +++ b/frontend/scripts/verify-screen-156.ts @@ -0,0 +1,111 @@ +/** + * ํ™”๋ฉด 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(); diff --git a/frontend/scripts/verify-screens-156-1053-v2.ts b/frontend/scripts/verify-screens-156-1053-v2.ts new file mode 100644 index 00000000..0042a99b --- /dev/null +++ b/frontend/scripts/verify-screens-156-1053-v2.ts @@ -0,0 +1,141 @@ +/** + * ํ™”๋ฉด 156, 1053 ์žฌ๊ฒ€์ฆ - ๋กœ๋”ฉ ์™„๋ฃŒ ํ›„ ์Šคํฌ๋ฆฐ์ƒท + */ + +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 = { screen156: {}, screen1053: {} }; + + try { + // 1-2: Ensure logged in - goto login first, then login + await page.goto(`${BASE_URL}/login`, { waitUntil: "domcontentloaded", timeout: 45000 }); + await page.waitForTimeout(2000); + await page.fill("#userId", "wace"); + await page.fill("#password", "qlalfqjsgh11"); + await page.locator('button[type="submit"]').first().click(); + await page.waitForURL((u: URL) => !u.includes("/login"), { timeout: 20000 }).catch(() => {}); + await page.waitForTimeout(3000); + + await page.goto(`${BASE_URL}/screens/156`, { waitUntil: "domcontentloaded", timeout: 45000 }); + await page.waitForTimeout(2000); + if (page.url().includes("/login")) { + await page.fill("#userId", "wace"); + await page.fill("#password", "qlalfqjsgh11"); + await page.locator('button[type="submit"]').first().click(); + await page.waitForURL((u: URL) => !u.includes("/login"), { timeout: 20000 }).catch(() => {}); + await page.waitForTimeout(3000); + } + + // 3: Company selection if present + const companyBtn = page.getByText("ํšŒ์‚ฌ ์„ ํƒ").first(); + if ((await companyBtn.count()) > 0) { + await companyBtn.click(); + await page.waitForTimeout(1500); + const companyOption = page.getByText("company7", { exact: true }).first(); + if ((await companyOption.count()) > 0) { + await companyOption.click(); + } else { + const anyOption = page.locator("[role='menuitem'], [role='option'], button").filter({ hasText: /ํšŒ์‚ฌ|company/i }).first(); + if ((await anyOption.count()) > 0) await anyOption.click(); + } + await page.waitForTimeout(2000); + } + + // 4: Screen 156 with menuObjid + await page.goto(`${BASE_URL}/screens/156?menuObjid=1762421920156`, { waitUntil: "domcontentloaded", timeout: 45000 }); + await page.waitForTimeout(3000); + + if (page.url().includes("/login")) { + await page.fill("#userId", "wace"); + await page.fill("#password", "qlalfqjsgh11"); + await page.locator('button[type="submit"]').first().click(); + await page.waitForURL((u: URL) => !u.includes("/login"), { timeout: 15000 }).catch(() => {}); + await page.waitForTimeout(2000); + await page.goto(`${BASE_URL}/screens/156?menuObjid=1762421920156`, { waitUntil: "domcontentloaded", timeout: 45000 }); + await page.waitForTimeout(3000); + } + + await page.getByText("ํ™”๋ฉด์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 35000 }).catch(() => {}); + await page.getByText("๋กœ๋”ฉ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 15000 }).catch(() => {}); + await page.waitForTimeout(30000); + + const table156 = page.locator("table tbody tr"); + await table156.first().waitFor({ state: "visible", timeout: 15000 }).catch(() => {}); + await page.waitForTimeout(3000); + + const info156 = await page.evaluate(() => { + const table = document.querySelector("table"); + const tbody = document.querySelector("tbody"); + const rows = document.querySelectorAll("tbody tr"); + const buttons = Array.from(document.querySelectorAll("button")).filter((b) => (b as HTMLElement).innerText?.trim().length > 2); + const pagination = document.body.innerText.includes("ํ‘œ์‹œ") || document.body.innerText.includes("1/"); + return { + tableVisible: !!table && !!tbody, + dataRowCount: rows.length, + buttonsWithText: buttons.length, + paginationVisible: !!pagination, + buttonLabels: buttons.slice(0, 8).map((b) => (b as HTMLElement).innerText?.trim().substring(0, 20)), + }; + }); + + report.screen156 = info156; + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "responsive-156-v2.png"), fullPage: true }); + console.log("responsive-156-v2.png saved"); + + // 5: Screen 1053 with menuObjid + await page.goto(`${BASE_URL}/screens/1053?menuObjid=1762421920304`, { waitUntil: "domcontentloaded", timeout: 45000 }); + await page.waitForTimeout(3000); + + await page.getByText("ํ™”๋ฉด์„ ๋ถˆ๋Ÿฌ์˜ค๋Š” ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 35000 }).catch(() => {}); + await page.getByText("๋กœ๋”ฉ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 15000 }).catch(() => {}); + await page.waitForTimeout(30000); + + const splitPanel = page.locator("[class*='border-r'], [class*='split']").first(); + await splitPanel.waitFor({ state: "visible", timeout: 15000 }).catch(() => {}); + await page.waitForTimeout(3000); + + const info1053 = await page.evaluate(() => { + const leftPanel = document.querySelector("[class*='border-r']"); + const rightPanel = document.querySelector("main")?.querySelectorAll("div") || []; + const buttons = Array.from(document.querySelectorAll("button")).filter((b) => (b as HTMLElement).innerText?.trim().length > 2); + const table = document.querySelector("table"); + const bodyText = document.body.innerText; + const hasSplitContent = bodyText.includes("์ขŒ์ธก์—์„œ") || bodyText.includes("๊ณต๊ธ‰์ฒ˜") || bodyText.includes("ํ’ˆ๋ชฉ"); + return { + splitPanelVisible: !!leftPanel || hasSplitContent, + buttonsWithText: buttons.length, + tableVisible: !!table, + buttonLabels: buttons.slice(0, 8).map((b) => (b as HTMLElement).innerText?.trim().substring(0, 25)), + }; + }); + + report.screen1053 = info1053; + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "responsive-1053-v2.png"), fullPage: true }); + console.log("responsive-1053-v2.png saved"); + + fs.writeFileSync( + path.join(SCREENSHOT_DIR, "verify-156-1053-v2-report.json"), + JSON.stringify(report, null, 2) + ); + console.log("\n=== Report ==="); + console.log(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, "verify-v2-error.png"), fullPage: true }).catch(() => {}); + } finally { + await browser.close(); + } +} + +main(); diff --git a/frontend/scripts/verify-split-panel-screens.ts b/frontend/scripts/verify-split-panel-screens.ts new file mode 100644 index 00000000..db8eb227 --- /dev/null +++ b/frontend/scripts/verify-split-panel-screens.ts @@ -0,0 +1,115 @@ +/** + * ํ™”๋ฉด 1722, 2089 ๊ฒ€์ฆ: split-panel-layout2 + */ + +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 loginIfNeeded(page: any) { + if (page.url().includes("/login")) { + await page.fill("#userId", "wace"); + await page.fill("#password", "qlalfqjsgh11"); + await page.locator('button[type="submit"]').first().click(); + await page.waitForURL((u: URL) => !u.includes("/login"), { timeout: 15000 }).catch(() => {}); + await page.waitForTimeout(2000); + } +} + +async function verifyScreen( + page: any, + screenId: number, + report: Record +) { + await page.goto(`${BASE_URL}/screens/${screenId}`, { waitUntil: "load", timeout: 30000 }); + await page.waitForTimeout(2000); + if (page.url().includes("/login")) { + await loginIfNeeded(page); + await page.goto(`${BASE_URL}/screens/${screenId}`, { waitUntil: "load", timeout: 30000 }); + await page.waitForTimeout(2000); + } + + await page.getByText("๋กœ๋”ฉ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 10000 }).catch(() => {}); + await page.waitForTimeout(2000); + + const info = await page.evaluate(() => { + const splitPanels = document.querySelectorAll("[class*='split'], [class*='Split'], [data-panel], [class*='panel']"); + const hasSplitLayout = document.querySelector("[class*='split-panel'], [class*='SplitPanel'], [data-split]") !== null; + const panels = document.querySelectorAll("[class*='panel'], [class*='Panel'], [class*='resize']"); + const leftPanelBorder = document.querySelectorAll("[class*='border-r']"); + const bodyText = document.body.innerText; + const hasLeftRightPanels = bodyText.includes("์™ผ์ชฝ ๋ชฉ๋ก์—์„œ") || bodyText.includes("ํ’ˆ๋ชฉ ๋ชฉ๋ก") || bodyText.includes("์„ ํƒํ•˜์„ธ์š”"); + const buttons = document.querySelectorAll("button"); + const main = document.querySelector("main") || document.body; + const mainHeight = (main as HTMLElement).offsetHeight; + + return { + pageLoadsWithoutErrors: !document.body.innerText.includes("ํ™”๋ฉด์„ ์ฐพ์„ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค"), + splitPanelCount: splitPanels.length, + panelCount: panels.length, + leftPanelBorderCount: leftPanelBorder.length, + bothPanelsVisible: panels.length >= 2 || splitPanels.length >= 2 || hasSplitLayout || (leftPanelBorder.length >= 1 && hasLeftRightPanels), + buttonsVisible: buttons.length > 0, + layoutFillsHeight: mainHeight >= window.innerHeight * 0.7, + bodyScrollWidth: document.body.scrollWidth, + bodyScrollHeight: document.body.scrollHeight, + viewportWidth: window.innerWidth, + viewportHeight: window.innerHeight, + hasHorizontalOverflow: document.body.scrollWidth > window.innerWidth, + hasVerticalOverflow: document.body.scrollHeight > window.innerHeight, + }; + }); + + report.pageLoadsWithoutErrors = info.pageLoadsWithoutErrors; + report.splitPanelVisible = info.bothPanelsVisible || info.splitPanelCount > 0 || info.panelCount >= 2 || (info.leftPanelBorderCount >= 1 && info.pageLoadsWithoutErrors); + report.buttonsVisible = info.buttonsVisible; + report.layoutFillsHeight = info.layoutFillsHeight; + report.noContentOverflowsViewport = !info.hasHorizontalOverflow; + report.details = info; + + await page.screenshot({ + path: path.join(SCREENSHOT_DIR, `screen-${screenId}-snapshot.png`), + fullPage: true, + }); + console.log(`screen-${screenId}-snapshot.png saved`); +} + +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 = { screen1722: {}, screen2089: {} }; + + try { + await page.goto(`${BASE_URL}/login`, { waitUntil: "load", timeout: 30000 }); + await page.waitForTimeout(1000); + await loginIfNeeded(page); + await page.waitForTimeout(2000); + + await verifyScreen(page, 1722, report.screen1722); + await verifyScreen(page, 2089, report.screen2089); + + console.log("\n=== Report ==="); + console.log(JSON.stringify(report, null, 2)); + + fs.writeFileSync( + path.join(SCREENSHOT_DIR, "split-panel-screens-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, "split-panel-error.png"), + fullPage: true, + }).catch(() => {}); + } finally { + await browser.close(); + } +} + +main(); diff --git a/frontend/scripts/verify-tapseal-screens.ts b/frontend/scripts/verify-tapseal-screens.ts new file mode 100644 index 00000000..f9fd7bc2 --- /dev/null +++ b/frontend/scripts/verify-tapseal-screens.ts @@ -0,0 +1,164 @@ +/** + * ํƒ‘์”ฐ ํšŒ์‚ฌ ์‹ค์ œ ๋ฐ์ดํ„ฐ ํ™”๋ฉด ๊ฒ€์ฆ + * - ํšŒ์‚ฌ ์„ ํƒ โ†’ ํƒ‘์”ฐ + * - ๊ตฌ๋งค๊ด€๋ฆฌ > ๋ฐœ์ฃผ๊ด€๋ฆฌ + * - ์ˆ˜์ฃผ๊ด€๋ฆฌ + * - ํ…Œ์ด๋ธ” ๊ฐ€๋กœ ์Šคํฌ๋กค, ํŽ˜์ด์ง€๋„ค์ด์…˜ ํ™•์ธ + */ + +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 results: Record = {}; + + try { + // Step 1: ์ ‘์† + console.log("Step 1: ์ ‘์†..."); + await page.goto(BASE_URL, { waitUntil: "load", timeout: 30000 }); + await page.waitForTimeout(2000); + + const url = page.url(); + if (url.includes("/login")) { + console.log("๋กœ๊ทธ์ธ ํ•„์š”..."); + 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(3000); + } + + await page.getByText("ํ˜„์žฌ ๊ด€๋ฆฌ ํšŒ์‚ฌ").waitFor({ timeout: 8000 }).catch(() => {}); + + // Step 2: ํšŒ์‚ฌ ์„ ํƒ โ†’ ํƒ‘์”ฐ + console.log("Step 2: ํšŒ์‚ฌ ์„ ํƒ โ†’ ํƒ‘์”ฐ..."); + const companyBtn = page.getByText("ํšŒ์‚ฌ ์„ ํƒ").first(); + if ((await companyBtn.count()) > 0) { + await companyBtn.click(); + await page.waitForTimeout(1500); + const tapseal = page.getByText("ํƒ‘์”ฐ", { exact: true }).first(); + if ((await tapseal.count()) > 0) { + await tapseal.click(); + await page.waitForTimeout(2500); + console.log("ํƒ‘์”ฐ ์„ ํƒ๋จ"); + } + } + + // Step 3: ๊ตฌ๋งค๊ด€๋ฆฌ > ๋ฐœ์ฃผ๊ด€๋ฆฌ + console.log("Step 3: ๊ตฌ๋งค๊ด€๋ฆฌ > ๋ฐœ์ฃผ๊ด€๋ฆฌ ํด๋ฆญ..."); + const purchaseMgmt = page.getByText("๊ตฌ๋งค๊ด€๋ฆฌ").first(); + if ((await purchaseMgmt.count()) > 0) { + await purchaseMgmt.click(); + await page.waitForTimeout(800); + const orderMgmt = page.getByText("๋ฐœ์ฃผ๊ด€๋ฆฌ").first(); + if ((await orderMgmt.count()) > 0) { + await orderMgmt.click(); + await page.waitForTimeout(4000); + } + } + await page.getByText("๋กœ๋”ฉ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 10000 }).catch(() => {}); + await page.waitForTimeout(2000); + + // Step 4: ๋ฐœ์ฃผ๊ด€๋ฆฌ ์Šคํฌ๋ฆฐ์ƒท + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "verify-10.png"), fullPage: true }); + console.log("verify-10.png ์ €์žฅ (๋ฐœ์ฃผ๊ด€๋ฆฌ)"); + + // Step 5: ๋ฐœ์ฃผ๊ด€๋ฆฌ - ํ…Œ์ด๋ธ”/์Šคํฌ๋กค/ํŽ˜์ด์ง€๋„ค์ด์…˜ ํ™•์ธ + const orderDims = await page.evaluate(() => { + const table = document.querySelector("table"); + const tableContainer = table?.closest("[style*='overflow'], [class*='overflow']"); + const pagination = document.body.innerText.includes("ํ‘œ์‹œ") || document.body.innerText.includes("1/"); + return { + tableScrollWidth: table ? (table as HTMLElement).scrollWidth : 0, + tableClientWidth: table ? (table as HTMLElement).clientWidth : 0, + containerClientWidth: tableContainer ? (tableContainer as HTMLElement).clientWidth : 0, + hasPagination: pagination, + dataRows: table ? table.querySelectorAll("tbody tr").length : 0, + }; + }); + results.orderMgmt = orderDims; + console.log("๋ฐœ์ฃผ๊ด€๋ฆฌ - ํ…Œ์ด๋ธ”:", orderDims.tableScrollWidth, "x", orderDims.tableClientWidth, "๋ฐ์ดํ„ฐํ–‰:", orderDims.dataRows, "ํŽ˜์ด์ง€๋„ค์ด์…˜:", orderDims.hasPagination); + + // Step 6: ํ…Œ์ด๋ธ” ๊ฐ€๋กœ ์Šคํฌ๋กค ์‹œ๋„ + const scrollResult = await page.evaluate(() => { + const table = document.querySelector("table"); + const scrollable = table?.closest("[style*='overflow'], [class*='overflow']") as HTMLElement; + if (scrollable && scrollable.scrollWidth > scrollable.clientWidth) { + scrollable.scrollLeft = 200; + return { scrolled: true, scrollLeft: scrollable.scrollLeft }; + } + return { scrolled: false }; + }); + results.orderScroll = scrollResult; + await page.waitForTimeout(500); + + // Step 7: ์ˆ˜์ฃผ๊ด€๋ฆฌ ๋ฉ”๋‰ด ํด๋ฆญ + console.log("Step 7: ์ˆ˜์ฃผ๊ด€๋ฆฌ ํด๋ฆญ..."); + const salesMgmt = page.getByText("์˜์—…๊ด€๋ฆฌ").first(); + if ((await salesMgmt.count()) > 0) { + await salesMgmt.click(); + await page.waitForTimeout(600); + } + const orderScreen = page.getByText("์ˆ˜์ฃผ๊ด€๋ฆฌ").first(); + if ((await orderScreen.count()) > 0) { + await orderScreen.click(); + await page.waitForTimeout(4000); + } + await page.getByText("๋กœ๋”ฉ์ค‘", { exact: false }).waitFor({ state: "hidden", timeout: 10000 }).catch(() => {}); + await page.waitForTimeout(2000); + + // Step 8: ์ˆ˜์ฃผ๊ด€๋ฆฌ ์Šคํฌ๋ฆฐ์ƒท + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "verify-11.png"), fullPage: true }); + console.log("verify-11.png ์ €์žฅ (์ˆ˜์ฃผ๊ด€๋ฆฌ)"); + + // Step 9: ์ˆ˜์ฃผ๊ด€๋ฆฌ - ํ…Œ์ด๋ธ”/ํŽ˜์ด์ง€๋„ค์ด์…˜ ํ™•์ธ + const salesDims = await page.evaluate(() => { + const table = document.querySelector("table"); + const pagination = document.body.innerText.includes("ํ‘œ์‹œ") || document.body.innerText.includes("1/"); + return { + tableScrollWidth: table ? (table as HTMLElement).scrollWidth : 0, + tableClientWidth: table ? (table as HTMLElement).clientWidth : 0, + hasPagination: pagination, + dataRows: table ? table.querySelectorAll("tbody tr").length : 0, + }; + }); + results.salesOrderMgmt = salesDims; + console.log("์ˆ˜์ฃผ๊ด€๋ฆฌ - ํ…Œ์ด๋ธ”:", salesDims.tableScrollWidth, "x", salesDims.tableClientWidth, "๋ฐ์ดํ„ฐํ–‰:", salesDims.dataRows, "ํŽ˜์ด์ง€๋„ค์ด์…˜:", salesDims.hasPagination); + + // ์ด์ „ ๋ฌธ์ œ ํ•ด๊ฒฐ ์—ฌ๋ถ€ + const orderTableFits = orderDims.tableScrollWidth <= (orderDims.containerClientWidth || orderDims.tableClientWidth + 100); + const salesTableFits = salesDims.tableScrollWidth <= salesDims.tableClientWidth + 100; + results.issuesResolved = { + orderTableOverflow: orderTableFits, + orderPaginationVisible: orderDims.hasPagination, + salesTableOverflow: salesTableFits, + salesPaginationVisible: salesDims.hasPagination, + }; + + console.log("\n=== ์ด์ „ ๋ฌธ์ œ ํ•ด๊ฒฐ ์—ฌ๋ถ€ ==="); + console.log("๋ฐœ์ฃผ๊ด€๋ฆฌ - ํ…Œ์ด๋ธ” ๋„˜์นจ ํ•ด๊ฒฐ:", orderTableFits); + console.log("๋ฐœ์ฃผ๊ด€๋ฆฌ - ํŽ˜์ด์ง€๋„ค์ด์…˜ ๋ณด์ž„:", orderDims.hasPagination); + console.log("์ˆ˜์ฃผ๊ด€๋ฆฌ - ํ…Œ์ด๋ธ” ๋„˜์นจ ํ•ด๊ฒฐ:", salesTableFits); + console.log("์ˆ˜์ฃผ๊ด€๋ฆฌ - ํŽ˜์ด์ง€๋„ค์ด์…˜ ๋ณด์ž„:", salesDims.hasPagination); + + fs.writeFileSync( + path.join(SCREENSHOT_DIR, "verify-tapseal-result.json"), + JSON.stringify(results, null, 2) + ); + } catch (error: any) { + console.error("์˜ค๋ฅ˜:", error.message); + await page.screenshot({ path: path.join(SCREENSHOT_DIR, "verify-99-error.png"), fullPage: true }).catch(() => {}); + } finally { + await browser.close(); + } +} + +main(); diff --git a/run-screen-e2e.mjs b/run-screen-e2e.mjs deleted file mode 100644 index 9f00b383..00000000 --- a/run-screen-e2e.mjs +++ /dev/null @@ -1,209 +0,0 @@ -import { chromium } from '/Users/gbpark/ERP-node/node_modules/playwright/index.mjs'; -import { writeFileSync } from 'fs'; - -const results = []; -let passed = true; -let failReason = ''; - -async function login(page) { - await page.goto('http://localhost:9771/login'); - await page.waitForLoadState('networkidle'); - - await page.getByPlaceholder('์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”').fill('wace'); - await page.getByPlaceholder('๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”').fill('qlalfqjsgh11'); - - await Promise.all([ - page.waitForURL(url => !url.toString().includes('/login'), { timeout: 30000 }), - page.getByRole('button', { name: '๋กœ๊ทธ์ธ' }).click(), - ]); - await page.waitForLoadState('networkidle'); - results.push('โœ… ๋กœ๊ทธ์ธ ์„ฑ๊ณต'); -} - -async function run() { - const browser = await chromium.launch({ headless: true }); - - // === ํ…Œ์ŠคํŠธ 1: /screens/29 ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง ํ™•์ธ === - { - const page = await browser.newPage(); - try { - await login(page); - - await page.goto('http://localhost:9771/screens/29'); - await page.waitForLoadState('domcontentloaded'); - await page.waitForTimeout(5000); - results.push('โœ… /screens/29 ์ ‘์† ์„ฑ๊ณต'); - - // ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ํ™•์ธ - const hasError = await page.locator('[id="__next"] .nextjs-container-errors-body').isVisible().catch(() => false); - if (hasError) throw new Error('/screens/29 ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ๋ฐœ๊ฒฌ'); - results.push('โœ… /screens/29 ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ์—†์Œ'); - - // body ํ™•์ธ - const bodyVisible = await page.locator('body').isVisible(); - if (!bodyVisible) throw new Error('body๊ฐ€ ๋ณด์ด์ง€ ์•Š์Œ'); - results.push('โœ… /screens/29 body ๋ Œ๋”๋ง ํ™•์ธ'); - - // ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง ํ™•์ธ - const selectors = [ - '[data-screen-id]', - '[data-widget-id]', - '[data-component-id]', - '.screen-container', - '[class*="widget"]', - '[class*="component"]', - '[class*="screen"]', - '[style*="position: absolute"]', - '[style*="position:absolute"]', - ]; - - let componentFound = false; - let foundInfo = ''; - for (const sel of selectors) { - const count = await page.locator(sel).count(); - if (count > 0) { - componentFound = true; - foundInfo = `${sel} (${count}๊ฐœ)`; - break; - } - } - - if (componentFound) { - results.push(`โœ… /screens/29 ์ปดํฌ๋„ŒํŠธ ๋ฐœ๊ฒฌ: ${foundInfo}`); - } else { - const pageContent = await page.locator('body').innerText().catch(() => ''); - results.push(`โœ… /screens/29 ํŽ˜์ด์ง€ ๋กœ๋“œ๋จ (๋‚ด์šฉ ๊ธธ์ด: ${pageContent.trim().length})`); - } - - // URL ํ™•์ธ - const currentUrl = page.url(); - results.push(`โœ… ํ˜„์žฌ URL: ${currentUrl}`); - - await page.screenshot({ path: '/Users/gbpark/ERP-node/.agent-pipeline/browser-tests/result-screens29.png', fullPage: true }); - results.push('โœ… /screens/29 ์Šคํฌ๋ฆฐ์ƒท ์ €์žฅ'); - - } catch (err) { - passed = false; - failReason = err.message; - results.push(`โŒ /screens/29 ํ…Œ์ŠคํŠธ ์‹คํŒจ: ${err.message}`); - await page.screenshot({ path: '/Users/gbpark/ERP-node/.agent-pipeline/browser-tests/result-screens29-fail.png', fullPage: true }).catch(() => {}); - } finally { - await page.close(); - } - } - - // === ํ…Œ์ŠคํŠธ 2: /admin/screen-management ํ™”๋ฉด ๋””์ž์ด๋„ˆ ํ™•์ธ === - { - const page = await browser.newPage(); - try { - await login(page); - - await page.goto('http://localhost:9771/admin/screen-management'); - await page.waitForLoadState('domcontentloaded'); - await page.waitForTimeout(5000); - results.push('โœ… /admin/screen-management ์ ‘์† ์„ฑ๊ณต'); - - // ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ํ™•์ธ - const hasError = await page.locator('[id="__next"] .nextjs-container-errors-body').isVisible().catch(() => false); - if (hasError) throw new Error('/admin/screen-management ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ๋ฐœ๊ฒฌ'); - results.push('โœ… /admin/screen-management ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ์—†์Œ'); - - // ํ™”๋ฉด ๋ชฉ๋ก ํ™•์ธ - const tableRows = page.locator('table tbody tr, [role="row"]:not([role="columnheader"])'); - const rowCount = await tableRows.count(); - results.push(`โœ… ํ™”๋ฉด ๋ชฉ๋ก ํ–‰ ์ˆ˜: ${rowCount}๊ฐœ`); - - if (rowCount > 0) { - // ํŽธ์ง‘ ๋ฒ„ํŠผ ์ฐพ๊ธฐ - const editSelectors = [ - 'button:has-text("ํŽธ์ง‘")', - 'button:has-text("์ˆ˜์ •")', - 'button:has-text("์—ด๊ธฐ")', - '[data-action="edit"]', - '[title="ํŽธ์ง‘"]', - 'td button', - ]; - - let editFound = false; - for (const sel of editSelectors) { - const editBtn = page.locator(sel).first(); - const isVisible = await editBtn.isVisible({ timeout: 2000 }).catch(() => false); - if (isVisible) { - await editBtn.click(); - await page.waitForLoadState('domcontentloaded'); - await page.waitForTimeout(5000); - editFound = true; - results.push(`โœ… ํŽธ์ง‘ ๋ฒ„ํŠผ ํด๋ฆญ ์„ฑ๊ณต (์…€๋ ‰ํ„ฐ: ${sel})`); - break; - } - } - - if (!editFound) { - // ์ฒซ ํ–‰ ํด๋ฆญ - await tableRows.first().click().catch(() => {}); - await page.waitForTimeout(3000); - results.push('โœ… ํ…Œ์ด๋ธ” ์ฒซ ํ–‰ ํด๋ฆญ ์‹œ๋„'); - } - - // ํŽธ์ง‘ ํ›„ ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ์žฌํ™•์ธ - const hasErrorAfterEdit = await page.locator('[id="__next"] .nextjs-container-errors-body').isVisible().catch(() => false); - if (hasErrorAfterEdit) throw new Error('ํŽธ์ง‘ ํ›„ ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ๋ฐœ๊ฒฌ'); - results.push('โœ… ํŽธ์ง‘ ํ›„ ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ์—†์Œ'); - - // ์ ˆ๋Œ€ ์ขŒํ‘œ ์ปดํฌ๋„ŒํŠธ ํ™•์ธ - const absoluteCount = await page.locator('[style*="position: absolute"], [style*="position:absolute"]').count(); - results.push(`โœ… ์ ˆ๋Œ€ ์ขŒํ‘œ ์š”์†Œ ์ˆ˜: ${absoluteCount}๊ฐœ`); - - // ๋””์ž์ด๋„ˆ UI ํ™•์ธ - const designerSelectors = [ - '[class*="canvas"]', - '[class*="designer"]', - '[class*="editor"]', - '[data-designer]', - '[class*="drag"]', - '[class*="drop"]', - '[class*="palette"]', - ]; - - for (const sel of designerSelectors) { - const count = await page.locator(sel).count(); - if (count > 0) { - results.push(`โœ… ๋””์ž์ด๋„ˆ UI ๋ฐœ๊ฒฌ: ${sel} (${count}๊ฐœ)`); - break; - } - } - - } else { - const pageText = await page.locator('body').innerText().catch(() => ''); - results.push(`โœ… /admin/screen-management ๋กœ๋“œ๋จ (๋‚ด์šฉ ๊ธธ์ด: ${pageText.trim().length})`); - } - - await page.screenshot({ path: '/Users/gbpark/ERP-node/.agent-pipeline/browser-tests/result.png', fullPage: true }); - results.push('โœ… /admin/screen-management ์Šคํฌ๋ฆฐ์ƒท ์ €์žฅ'); - - } catch (err) { - passed = false; - if (!failReason) failReason = err.message; - results.push(`โŒ /admin/screen-management ํ…Œ์ŠคํŠธ ์‹คํŒจ: ${err.message}`); - await page.screenshot({ path: '/Users/gbpark/ERP-node/.agent-pipeline/browser-tests/result-screen-mgmt-fail.png', fullPage: true }).catch(() => {}); - } finally { - await page.close(); - } - } - - await browser.close(); -} - -run().then(() => { - const output = results.join('\n'); - console.log(output); - const resultLine = passed ? 'RESULT: PASS' : `RESULT: FAIL - ${failReason}`; - writeFileSync('/tmp/screen-e2e-result.txt', output + '\n' + resultLine); - console.log(resultLine); - process.exit(passed ? 0 : 1); -}).catch(err => { - const msg = `์น˜๋ช…์  ์˜ค๋ฅ˜: ${err.message}`; - console.error(msg); - writeFileSync('/tmp/screen-e2e-result.txt', msg + '\nRESULT: FAIL - ' + err.message); - process.exit(1); -}); diff --git a/run-screen29-e2e-new.sh b/run-screen29-e2e-new.sh deleted file mode 100644 index bae8ff88..00000000 --- a/run-screen29-e2e-new.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash -cd /Users/gbpark/ERP-node -node run-screen29-e2e.mjs 2>&1 | tee /tmp/screen29-e2e-result.txt -echo "EXIT_CODE: $?" >> /tmp/screen29-e2e-result.txt -cat /tmp/screen29-e2e-result.txt diff --git a/run-screen29-e2e.mjs b/run-screen29-e2e.mjs deleted file mode 100644 index 6f2c6b1d..00000000 --- a/run-screen29-e2e.mjs +++ /dev/null @@ -1,234 +0,0 @@ -import { chromium } from '/Users/gbpark/ERP-node/node_modules/playwright/index.mjs'; - -const results = []; -let passed = true; -let failReason = ''; - -async function run() { - const browser = await chromium.launch({ headless: true }); - let page; - - try { - const context = await browser.newContext({ viewport: { width: 1280, height: 720 } }); - page = await context.newPage(); - - // โ”€โ”€ 1. ๋กœ๊ทธ์ธ โ”€โ”€ - await page.goto('http://localhost:9771/login'); - await page.waitForLoadState('networkidle'); - - await page.getByPlaceholder('์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”').fill('wace'); - await page.getByPlaceholder('๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”').fill('qlalfqjsgh11'); - - await Promise.all([ - page.waitForURL(url => !url.toString().includes('/login'), { timeout: 30000 }), - page.getByRole('button', { name: '๋กœ๊ทธ์ธ' }).click(), - ]); - await page.waitForLoadState('networkidle'); - - const loginUrl = page.url(); - if (loginUrl.includes('/login')) throw new Error('๋กœ๊ทธ์ธ ์‹คํŒจ: /login ํŽ˜์ด์ง€์— ๋จธ๋ฌด๋ฆ„'); - results.push(`PASS: ๋กœ๊ทธ์ธ ์„ฑ๊ณต (URL: ${loginUrl})`); - - // โ”€โ”€ 2. /screens/29 ์ ‘์† โ”€โ”€ - await page.goto('http://localhost:9771/screens/29'); - await page.waitForLoadState('domcontentloaded'); - await page.waitForTimeout(5000); - - results.push(`INFO: ํ˜„์žฌ URL = ${page.url()}`); - - // ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ์ฒดํฌ - const hasError = await page.locator('[id="__next"] .nextjs-container-errors-body').isVisible().catch(() => false); - if (hasError) throw new Error('/screens/29์—์„œ ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ๋ฐœ๊ฒฌ'); - results.push('PASS: ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ์—†์Œ'); - - // ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ ๋Œ€๊ธฐ - await page.waitForSelector('.animate-spin', { state: 'hidden', timeout: 20000 }).catch(() => { - results.push('INFO: ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ ํƒ€์ž„์•„์›ƒ (๊ณ„์† ์ง„ํ–‰)'); - }); - await page.waitForTimeout(2000); - - // โ”€โ”€ 3. ํ…Œ์ด๋ธ” ๋ฐ์ดํ„ฐ ํ‘œ์‹œ ํ™•์ธ โ”€โ”€ - // table ์š”์†Œ ๋˜๋Š” grid ์—ญํ•  ํ™•์ธ - const tableLocator = page.locator('table').first(); - const tableVisible = await tableLocator.isVisible().catch(() => false); - results.push(`INFO: table ์š”์†Œ ์กด์žฌ = ${tableVisible}`); - - if (tableVisible) { - results.push('PASS: ํ…Œ์ด๋ธ” ์š”์†Œ ์ •์ƒ ํ‘œ์‹œ'); - - // tbody ํ–‰ ๊ฐœ์ˆ˜ ํ™•์ธ - const rows = page.locator('table tbody tr'); - const rowCount = await rows.count(); - results.push(`INFO: ํ…Œ์ด๋ธ” ํ–‰ ์ˆ˜ = ${rowCount}`); - - if (rowCount > 0) { - results.push(`PASS: ํ…Œ์ด๋ธ” ๋ฐ์ดํ„ฐ ์ •์ƒ ํ‘œ์‹œ (${rowCount}๊ฐœ ํ–‰)`); - } else { - // ํ–‰์ด 0๊ฐœ์—ฌ๋„ ํ…Œ์ด๋ธ” ๊ตฌ์กฐ๋Š” ์žˆ์œผ๋ฉด OK (๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋Š” ํ™”๋ฉด์ผ ์ˆ˜ ์žˆ์Œ) - results.push('INFO: ํ…Œ์ด๋ธ” ํ–‰์ด ์—†์Œ (๋นˆ ํ™”๋ฉด ๋˜๋Š” ๋ฐ์ดํ„ฐ ์—†์Œ)'); - - // ๋นˆ ์ƒํƒœ ๋ฉ”์‹œ์ง€ ํ™•์ธ - const emptyMsg = page.locator('[class*="empty"], [class*="no-data"], td[colspan]'); - const emptyVisible = await emptyMsg.isVisible().catch(() => false); - results.push(`INFO: ๋นˆ ์ƒํƒœ ๋ฉ”์‹œ์ง€ = ${emptyVisible}`); - results.push('PASS: ํ…Œ์ด๋ธ” ๊ตฌ์กฐ ์ •์ƒ ํ™•์ธ (๋ฐ์ดํ„ฐ ์—†์Œ ์ƒํƒœ)'); - } - - // โ”€โ”€ 4. ์ปฌ๋Ÿผ ํ—ค๋” ํด๋ฆญ (์ •๋ ฌ) ํ™•์ธ โ”€โ”€ - const headers = page.locator('table thead th'); - const headerCount = await headers.count(); - results.push(`INFO: ํ…Œ์ด๋ธ” ํ—ค๋” ์ˆ˜ = ${headerCount}`); - - if (headerCount > 0) { - // ์ฒซ ๋ฒˆ์งธ ํด๋ฆญ ๊ฐ€๋Šฅํ•œ ํ—ค๋” ํด๋ฆญ - const firstHeader = headers.first(); - await firstHeader.click(); - await page.waitForTimeout(1000); - results.push('PASS: ์ฒซ ๋ฒˆ์งธ ์ปฌ๋Ÿผ ํ—ค๋” ํด๋ฆญ ์„ฑ๊ณต'); - - // ํด๋ฆญ ํ›„ ์—๋Ÿฌ ์—†๋Š”์ง€ ํ™•์ธ - const errorAfterSort = await page.locator('[id="__next"] .nextjs-container-errors-body').isVisible().catch(() => false); - if (errorAfterSort) throw new Error('์ปฌ๋Ÿผ ์ •๋ ฌ ํด๋ฆญ ํ›„ ์—๋Ÿฌ ๋ฐœ์ƒ'); - results.push('PASS: ์ปฌ๋Ÿผ ์ •๋ ฌ ํด๋ฆญ ํ›„ ์—๋Ÿฌ ์—†์Œ'); - - // ๋‘ ๋ฒˆ์งธ ํ—ค๋”๋„ ํด๋ฆญ (์žˆ์œผ๋ฉด) - if (headerCount > 1) { - const secondHeader = headers.nth(1); - await secondHeader.click(); - await page.waitForTimeout(1000); - results.push('PASS: ๋‘ ๋ฒˆ์งธ ์ปฌ๋Ÿผ ํ—ค๋” ํด๋ฆญ ์„ฑ๊ณต'); - } - } else { - results.push('INFO: ํ…Œ์ด๋ธ” ํ—ค๋” ์—†์Œ - ์ •๋ ฌ ํ…Œ์ŠคํŠธ ์Šคํ‚ต'); - } - } else { - // table ์š”์†Œ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ - ๋‹ค๋ฅธ ํ˜•ํƒœ์˜ ๊ทธ๋ฆฌ๋“œ์ผ ์ˆ˜ ์žˆ์Œ - const gridRoles = page.locator('[role="grid"], [role="table"]'); - const gridCount = await gridRoles.count(); - results.push(`INFO: grid/table role ์š”์†Œ ์ˆ˜ = ${gridCount}`); - - // ํ™”๋ฉด์— ์–ด๋–ค ์ปจํ…์ธ ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ - const bodyText = await page.locator('body').innerText().catch(() => ''); - results.push(`INFO: ํŽ˜์ด์ง€ ํ…์ŠคํŠธ ๊ธธ์ด = ${bodyText.length}`); - - if (bodyText.length > 10) { - results.push('PASS: ํ™”๋ฉด ์ปจํ…์ธ  ์ •์ƒ ๋ Œ๋”๋ง ํ™•์ธ'); - } else { - throw new Error('ํ™”๋ฉด ๋ Œ๋”๋ง ์‹คํŒจ: ์ปจํ…์ธ ๊ฐ€ ๋„ˆ๋ฌด ์ ์Œ'); - } - } - - // ๋ฐ์Šคํฌํ†ฑ ์Šคํฌ๋ฆฐ์ƒท - await page.screenshot({ path: '/Users/gbpark/ERP-node/.agent-pipeline/browser-tests/result-desktop.png', fullPage: true }); - results.push('PASS: ๋ฐ์Šคํฌํ†ฑ ์Šคํฌ๋ฆฐ์ƒท ์ €์žฅ'); - - // โ”€โ”€ 5. ๋ธŒ๋ผ์šฐ์ € ๋„ˆ๋น„ 375px๋กœ ๋ณ€๊ฒฝ โ”€โ”€ - await page.setViewportSize({ width: 375, height: 812 }); - await page.waitForTimeout(2000); - - const viewportWidth = await page.evaluate(() => window.innerWidth); - if (viewportWidth !== 375) throw new Error(`๋ทฐํฌํŠธ ๋„ˆ๋น„ ๋ณ€๊ฒฝ ์‹คํŒจ: ${viewportWidth}px (์˜ˆ์ƒ: 375px)`); - results.push('PASS: ๋ทฐํฌํŠธ 375px ๋ณ€๊ฒฝ ์™„๋ฃŒ'); - - // ๋ชจ๋ฐ”์ผ ์—๋Ÿฌ ์ฒดํฌ - const mobileError = await page.locator('[id="__next"] .nextjs-container-errors-body').isVisible().catch(() => false); - if (mobileError) throw new Error('๋ชจ๋ฐ”์ผ ๋ทฐ์—์„œ ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ๋ฐœ๊ฒฌ'); - results.push('PASS: ๋ชจ๋ฐ”์ผ ๋ทฐ ์—๋Ÿฌ ์—†์Œ'); - - // โ”€โ”€ 6. ํ…Œ์ด๋ธ” ๊ฐ€๋กœ ์Šคํฌ๋กค ๊ฐ€๋Šฅํ•œ์ง€ ํ™•์ธ โ”€โ”€ - const scrollable = await page.evaluate(() => { - const table = document.querySelector('table'); - if (!table) { - // table์ด ์—†์œผ๋ฉด ๋‹ค๋ฅธ ์Šคํฌ๋กค ๊ฐ€๋Šฅํ•œ ์ปจํ…Œ์ด๋„ˆ ํ™•์ธ - const scrollContainers = document.querySelectorAll('[class*="overflow-x"], [style*="overflow-x"]'); - return scrollContainers.length > 0; - } - - // ํ…Œ์ด๋ธ” ๋„ˆ๋น„๊ฐ€ ๋ทฐํฌํŠธ๋ณด๋‹ค ํฐ์ง€ - if (table.scrollWidth > window.innerWidth) return true; - - // ๋ถ€๋ชจ ์š”์†Œ์— overflow-x: auto/scroll์ด ์žˆ๋Š”์ง€ ํ™•์ธ - let el = table.parentElement; - while (el && el !== document.body) { - const style = window.getComputedStyle(el); - const overflowX = style.overflowX; - if (overflowX === 'auto' || overflowX === 'scroll') return true; - // overflow-x: hidden์€ ์Šคํฌ๋กค ๋ถˆ๊ฐ€ - el = el.parentElement; - } - - // table ์ž์ฒด์˜ overflow ํ™•์ธ - const tableStyle = window.getComputedStyle(table); - return tableStyle.overflowX === 'auto' || tableStyle.overflowX === 'scroll'; - }); - - results.push(`INFO: ๊ฐ€๋กœ ์Šคํฌ๋กค ๊ฐ€๋Šฅ ์—ฌ๋ถ€ = ${scrollable}`); - - if (scrollable) { - results.push('PASS: ํ…Œ์ด๋ธ” ๊ฐ€๋กœ ์Šคํฌ๋กค ๊ฐ€๋Šฅ ํ™•์ธ'); - } else { - // ์Šคํฌ๋กค์ด ์—†๋”๋ผ๋„ ๋ชจ๋ฐ”์ผ์—์„œ ๋ฐ˜์‘ํ˜•์œผ๋กœ ์ฒ˜๋ฆฌ๋œ ๊ฒฝ์šฐ์ผ ์ˆ˜ ์žˆ์Œ - // ํ…Œ์ด๋ธ”์ด ์ถ•์†Œ/์ˆจ๊ฒจ์ง€๋Š” ๋ฐ˜์‘ํ˜• UI์ผ ๊ฐ€๋Šฅ์„ฑ - const mobileTableVisible = await page.locator('table').first().isVisible().catch(() => false); - results.push(`INFO: ๋ชจ๋ฐ”์ผ์—์„œ ํ…Œ์ด๋ธ” ํ‘œ์‹œ = ${mobileTableVisible}`); - - // ๊ฐ€๋กœ ์Šคํฌ๋กค ์ปจํ…Œ์ด๋„ˆ๊ฐ€ ์žˆ๋Š”์ง€ ๋„“๊ฒŒ ํƒ์ƒ‰ - const hasOverflowContainer = await page.evaluate(() => { - const elements = document.querySelectorAll('*'); - for (const el of elements) { - const style = window.getComputedStyle(el); - if (style.overflowX === 'auto' || style.overflowX === 'scroll') { - return true; - } - } - return false; - }); - - results.push(`INFO: ํŽ˜์ด์ง€ ๋‚ด overflow-x ์ปจํ…Œ์ด๋„ˆ ์กด์žฌ = ${hasOverflowContainer}`); - - if (hasOverflowContainer) { - results.push('PASS: ํŽ˜์ด์ง€ ๋‚ด ๊ฐ€๋กœ ์Šคํฌ๋กค ์ปจํ…Œ์ด๋„ˆ ์กด์žฌ ํ™•์ธ'); - } else { - // ํ…Œ์ด๋ธ”์ด 375px์— ๋งž๊ฒŒ ๋ฐ˜์‘ํ˜•์œผ๋กœ ๋ Œ๋”๋ง๋œ ๊ฒฝ์šฐ๋„ ํ—ˆ์šฉ - results.push('INFO: ๊ฐ€๋กœ ์Šคํฌ๋กค ์ปจํ…Œ์ด๋„ˆ ์—†์Œ - ๋ฐ˜์‘ํ˜• ๋ ˆ์ด์•„์›ƒ์œผ๋กœ ์ฒ˜๋ฆฌ๋œ ๊ฒƒ์œผ๋กœ ํŒ๋‹จ'); - results.push('PASS: ๋ชจ๋ฐ”์ผ ๋ฐ˜์‘ํ˜• ํ…Œ์ด๋ธ” ๋ ˆ์ด์•„์›ƒ ํ™•์ธ'); - } - } - - // ์ตœ์ข… ๋ชจ๋ฐ”์ผ ์Šคํฌ๋ฆฐ์ƒท - await page.screenshot({ path: '/Users/gbpark/ERP-node/.agent-pipeline/browser-tests/result.png', fullPage: true }); - results.push('PASS: ๋ชจ๋ฐ”์ผ ์Šคํฌ๋ฆฐ์ƒท ์ €์žฅ'); - - await context.close(); - - } catch (err) { - passed = false; - failReason = err.message; - results.push(`FAIL: ${err.message}`); - try { - if (page) { - await page.screenshot({ path: '/Users/gbpark/ERP-node/.agent-pipeline/browser-tests/result.png', fullPage: true }); - } - } catch (_) {} - } finally { - await browser.close(); - } - - console.log('\n=== ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ==='); - results.forEach(r => console.log(r)); - console.log('==================\n'); - - if (passed) { - console.log('BROWSER_TEST_RESULT: PASS'); - process.exit(0); - } else { - console.log(`BROWSER_TEST_RESULT: FAIL - ${failReason}`); - process.exit(1); - } -} - -run().catch(err => { - console.error('์‹คํ–‰ ์˜ค๋ฅ˜:', err); - console.log(`BROWSER_TEST_RESULT: FAIL - ${err.message}`); - process.exit(1); -}); diff --git a/run-screen29-filter-e2e.mjs b/run-screen29-filter-e2e.mjs deleted file mode 100644 index 908bbfef..00000000 --- a/run-screen29-filter-e2e.mjs +++ /dev/null @@ -1,221 +0,0 @@ -import { chromium } from '/Users/gbpark/ERP-node/node_modules/playwright/index.mjs'; -import { mkdirSync } from 'fs'; - -// ๊ฒฐ๊ณผ ๋””๋ ‰ํ† ๋ฆฌ ์ƒ์„ฑ -try { - mkdirSync('/Users/gbpark/ERP-node/.agent-pipeline/browser-tests', { recursive: true }); -} catch (e) {} - -const results = []; -let passed = true; -let failReason = ''; - -async function run() { - const browser = await chromium.launch({ headless: true }); - let page; - - try { - const context = await browser.newContext({ viewport: { width: 1280, height: 720 } }); - page = await context.newPage(); - - // โ”€โ”€ 1. ๋กœ๊ทธ์ธ โ”€โ”€ - await page.goto('http://localhost:9771/login'); - await page.waitForLoadState('networkidle'); - - await page.getByPlaceholder('์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”').fill('wace'); - await page.getByPlaceholder('๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”').fill('qlalfqjsgh11'); - - await Promise.all([ - page.waitForURL(url => !url.toString().includes('/login'), { timeout: 30000 }), - page.getByRole('button', { name: '๋กœ๊ทธ์ธ' }).click(), - ]); - await page.waitForLoadState('networkidle'); - - const loginUrl = page.url(); - if (loginUrl.includes('/login')) throw new Error('๋กœ๊ทธ์ธ ์‹คํŒจ: /login ํŽ˜์ด์ง€์— ๋จธ๋ฌด๋ฆ„'); - results.push(`PASS: ๋กœ๊ทธ์ธ ์„ฑ๊ณต (URL: ${loginUrl})`); - - // โ”€โ”€ 2. /screens/29 ์ ‘์† โ”€โ”€ - await page.goto('http://localhost:9771/screens/29'); - await page.waitForLoadState('domcontentloaded'); - await page.waitForTimeout(5000); - - results.push(`INFO: ํ˜„์žฌ URL = ${page.url()}`); - - // ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ์ฒดํฌ - const hasError = await page.locator('[id="__next"] .nextjs-container-errors-body').isVisible().catch(() => false); - if (hasError) throw new Error('/screens/29์—์„œ ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ๋ฐœ๊ฒฌ'); - results.push('PASS: ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ์—†์Œ'); - - // ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ ๋Œ€๊ธฐ - await page.waitForSelector('.animate-spin', { state: 'hidden', timeout: 15000 }).catch(() => { - results.push('INFO: ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ ํƒ€์ž„์•„์›ƒ (๊ณ„์† ์ง„ํ–‰)'); - }); - await page.waitForTimeout(2000); - - // โ”€โ”€ 3. ๊ฒ€์ƒ‰ ํ•„ํ„ฐ ์˜์—ญ์ด ์ •์ƒ ํ‘œ์‹œ๋˜๋Š”์ง€ ํ™•์ธ โ”€โ”€ - // TableSearchWidget์€ border-b ํด๋ž˜์Šค๋ฅผ ๊ฐ€์ง„ ์ปจํ…Œ์ด๋„ˆ๋กœ ๋ Œ๋”๋ง๋จ - const borderBContainers = page.locator('.border-b'); - const borderBCount = await borderBContainers.count(); - results.push(`INFO: border-b ์ปจํ…Œ์ด๋„ˆ ์ˆ˜ = ${borderBCount}`); - - // "ํ…Œ์ด๋ธ” ์„ค์ •" ๋ฒ„ํŠผ ํ™•์ธ (dynamic ๋ชจ๋“œ) - const settingsBtn = page.locator('button:has-text("ํ…Œ์ด๋ธ” ์„ค์ •")'); - const settingsBtnVisible = await settingsBtn.isVisible().catch(() => false); - results.push(`INFO: ํ…Œ์ด๋ธ” ์„ค์ • ๋ฒ„ํŠผ ํ‘œ์‹œ = ${settingsBtnVisible}`); - - // ๊ฒ€์ƒ‰ ์œ„์ ฏ์ด ์žˆ๋Š”์ง€ ํ™•์ธ - if (settingsBtnVisible) { - results.push('PASS: ๊ฒ€์ƒ‰ ํ•„ํ„ฐ ์œ„์ ฏ (ํ…Œ์ด๋ธ” ์„ค์ • ๋ฒ„ํŠผ) ์ •์ƒ ํ‘œ์‹œ'); - } else if (borderBCount > 0) { - results.push('PASS: ๊ฒ€์ƒ‰ ํ•„ํ„ฐ ์ปจํ…Œ์ด๋„ˆ (border-b) ์ •์ƒ ํ‘œ์‹œ'); - } else { - // ํŽ˜์ด์ง€์— ์ปจํ…์ธ ๊ฐ€ ์žˆ๋Š”์ง€ ํ™•์ธ - const bodyText = await page.locator('body').innerText().catch(() => ''); - if (bodyText.length > 10) { - results.push('PASS: ํ™”๋ฉด ์ปจํ…์ธ  ์ •์ƒ ๋ Œ๋”๋ง ํ™•์ธ'); - } else { - throw new Error('๊ฒ€์ƒ‰ ํ•„ํ„ฐ ๋˜๋Š” ํ™”๋ฉด ์ปจํ…์ธ ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Œ'); - } - } - - // โ”€โ”€ 4. ํ•„ํ„ฐ ์ž…๋ ฅ ํ•„๋“œ ํ™•์ธ ๋ฐ ๊ฐ’ ์ž…๋ ฅ โ”€โ”€ - const filterInputs = page.locator('.border-b input[type="text"], .border-b input[type="number"]'); - const filterInputCount = await filterInputs.count(); - results.push(`INFO: ํ•„ํ„ฐ Input ์ˆ˜ = ${filterInputCount}`); - - if (filterInputCount > 0) { - // ์ฒซ ๋ฒˆ์งธ ํ•„ํ„ฐ input์— ๊ฐ’ ์ž…๋ ฅ - const firstInput = filterInputs.first(); - await firstInput.fill('ํ…Œ์ŠคํŠธ'); - await page.waitForTimeout(1000); - - // ์ž…๋ ฅ๊ฐ’์ด ๋ฐ˜์˜๋˜์—ˆ๋Š”์ง€ ํ™•์ธ - const inputValue = await firstInput.inputValue(); - if (inputValue === 'ํ…Œ์ŠคํŠธ') { - results.push('PASS: ํ•„ํ„ฐ ๊ฐ’ ์ž…๋ ฅ ๋ฐ ๋ฐ˜์˜ ํ™•์ธ'); - } else { - results.push(`WARN: ํ•„ํ„ฐ ๊ฐ’ ์ž…๋ ฅ ํ™•์ธ ์‹คํŒจ (์ž…๋ ฅ๊ฐ’: "${inputValue}")`); - } - - // ์ž…๋ ฅ ํ›„ ์—๋Ÿฌ ์—†๋Š”์ง€ ํ™•์ธ - const errorAfterInput = await page.locator('[id="__next"] .nextjs-container-errors-body').isVisible().catch(() => false); - if (errorAfterInput) throw new Error('ํ•„ํ„ฐ ๊ฐ’ ์ž…๋ ฅ ํ›„ ์—๋Ÿฌ ๋ฐœ์ƒ'); - results.push('PASS: ํ•„ํ„ฐ ์ž…๋ ฅ ํ›„ ์—๋Ÿฌ ์—†์Œ'); - - // ์ดˆ๊ธฐํ™” ๋ฒ„ํŠผ ํด๋ฆญ - const resetBtn = page.locator('button:has-text("์ดˆ๊ธฐํ™”")'); - const resetBtnVisible = await resetBtn.isVisible().catch(() => false); - if (resetBtnVisible) { - await resetBtn.click(); - await page.waitForTimeout(500); - results.push('PASS: ์ดˆ๊ธฐํ™” ๋ฒ„ํŠผ ํด๋ฆญ ์„ฑ๊ณต'); - } - } else { - // ํ•„ํ„ฐ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ - ํ…Œ์ด๋ธ” ์„ค์ • ๋ฒ„ํŠผ๋งŒ ์žˆ๊ฑฐ๋‚˜ ์•„์˜ˆ ์—†๋Š” ๊ฒฝ์šฐ - results.push('INFO: ํ•„ํ„ฐ Input ์—†์Œ - ํ•„ํ„ฐ ๋ฏธ์„ค์ • ์ƒํƒœ๋กœ ํŒ๋‹จ'); - results.push('PASS: ํ•„ํ„ฐ ๋ฏธ์„ค์ • ์ƒํƒœ ํ™•์ธ (์ •์ƒ)'); - } - - // ๋ฐ์Šคํฌํ†ฑ ์Šคํฌ๋ฆฐ์ƒท - await page.screenshot({ path: '/Users/gbpark/ERP-node/.agent-pipeline/browser-tests/result-desktop.png', fullPage: true }); - results.push('PASS: ๋ฐ์Šคํฌํ†ฑ ์Šคํฌ๋ฆฐ์ƒท ์ €์žฅ'); - - // โ”€โ”€ 5. ๋ธŒ๋ผ์šฐ์ € ๋„ˆ๋น„ 375px๋กœ ๋ณ€๊ฒฝ โ”€โ”€ - await page.setViewportSize({ width: 375, height: 812 }); - await page.waitForTimeout(2000); - - const viewportWidth = await page.evaluate(() => window.innerWidth); - if (viewportWidth !== 375) throw new Error(`๋ทฐํฌํŠธ ๋„ˆ๋น„ ๋ณ€๊ฒฝ ์‹คํŒจ: ${viewportWidth}px (์˜ˆ์ƒ: 375px)`); - results.push('PASS: ๋ทฐํฌํŠธ 375px ๋ณ€๊ฒฝ ์™„๋ฃŒ'); - - // ๋ชจ๋ฐ”์ผ ์—๋Ÿฌ ์ฒดํฌ - const mobileError = await page.locator('[id="__next"] .nextjs-container-errors-body').isVisible().catch(() => false); - if (mobileError) throw new Error('๋ชจ๋ฐ”์ผ ๋ทฐ์—์„œ ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ๋ฐœ๊ฒฌ'); - results.push('PASS: ๋ชจ๋ฐ”์ผ ๋ทฐ ์—๋Ÿฌ ์—†์Œ'); - - // โ”€โ”€ 6. ๋ชจ๋ฐ”์ผ์—์„œ ํ•„ํ„ฐ ์ž…๋ ฅ ํ•„๋“œ๊ฐ€ ์„ธ๋กœ๋กœ ์Œ“์ด๋Š”์ง€ ํ™•์ธ โ”€โ”€ - // TableSearchWidget์˜ ํ•„ํ„ฐ ์ปจํ…Œ์ด๋„ˆ: "flex flex-1 flex-col gap-2 sm:flex-row sm:flex-wrap sm:items-center" - // ๋ชจ๋ฐ”์ผ(375px)์—์„œ๋Š” flex-col์ด ์ ์šฉ๋˜์–ด ์„ธ๋กœ ๋ฐฐ์น˜ - const mobileFilterInputs = page.locator('.border-b input[type="text"], .border-b input[type="number"]'); - const mobileFilterCount = await mobileFilterInputs.count(); - results.push(`INFO: ๋ชจ๋ฐ”์ผ ํ•„ํ„ฐ Input ์ˆ˜ = ${mobileFilterCount}`); - - if (mobileFilterCount >= 2) { - // ๋‘ ๊ฐœ ์ด์ƒ์˜ ํ•„ํ„ฐ๊ฐ€ ์žˆ์œผ๋ฉด ์„ธ๋กœ ์Œ“์ž„ ํ™•์ธ - const firstInputBox = await mobileFilterInputs.first().boundingBox(); - const secondInputBox = await mobileFilterInputs.nth(1).boundingBox(); - - if (firstInputBox && secondInputBox) { - if (secondInputBox.y > firstInputBox.y) { - results.push('PASS: ํ•„ํ„ฐ ์ž…๋ ฅ ํ•„๋“œ๊ฐ€ ์„ธ๋กœ๋กœ ์Œ“์ž„ ํ™•์ธ (๋ชจ๋ฐ”์ผ ๋ฐ˜์‘ํ˜•)'); - } else { - results.push(`WARN: ์„ธ๋กœ ์Œ“์ž„ ๋ถˆํ™•์‹ค (y1=${firstInputBox.y}, y2=${secondInputBox.y})`); - } - } - } else if (mobileFilterCount === 1) { - // ํ•„ํ„ฐ 1๊ฐœ์ธ ๊ฒฝ์šฐ w-full๋กœ ์ „์ฒด ๋„ˆ๋น„ ์‚ฌ์šฉํ•˜๋Š”์ง€ ํ™•์ธ - const inputBox = await mobileFilterInputs.first().boundingBox(); - if (inputBox && inputBox.width > 200) { - results.push('PASS: ๋‹จ์ผ ํ•„ํ„ฐ ์ž…๋ ฅ ํ•„๋“œ ๋ชจ๋ฐ”์ผ ์ „์ฒด ๋„ˆ๋น„ ํ™•์ธ'); - } else { - results.push('INFO: ๋‹จ์ผ ํ•„ํ„ฐ - ๋„ˆ๋น„ ์ œํ•œ ์ƒํƒœ'); - } - } else { - // ํ•„ํ„ฐ๊ฐ€ ์—†๋Š” ๊ฒฝ์šฐ - flex-col ํด๋ž˜์Šค ์ปจํ…Œ์ด๋„ˆ ํ™•์ธ์œผ๋กœ ๋Œ€์ฒด - const flexColExists = await page.evaluate(() => { - const containers = document.querySelectorAll('.border-b .flex-col, [class*="flex-col"]'); - return containers.length > 0; - }); - results.push(`INFO: flex-col ์ปจํ…Œ์ด๋„ˆ ์กด์žฌ = ${flexColExists}`); - - if (flexColExists) { - results.push('PASS: ๋ชจ๋ฐ”์ผ ์„ธ๋กœ ๋ ˆ์ด์•„์›ƒ ์ปจํ…Œ์ด๋„ˆ ํ™•์ธ'); - } else { - // ํŽ˜์ด์ง€๊ฐ€ ์ •์ƒ ๋ Œ๋”๋ง๋˜์–ด ์žˆ์œผ๋ฉด ํ†ต๊ณผ - const mobileBodyText = await page.locator('body').innerText().catch(() => ''); - if (mobileBodyText.length > 10) { - results.push('PASS: ๋ชจ๋ฐ”์ผ ๋ทฐ ์ •์ƒ ๋ Œ๋”๋ง ํ™•์ธ'); - } else { - throw new Error('๋ชจ๋ฐ”์ผ ๋ทฐ ๋ Œ๋”๋ง ์‹คํŒจ'); - } - } - } - - // ์ตœ์ข… ๋ชจ๋ฐ”์ผ ์Šคํฌ๋ฆฐ์ƒท - await page.screenshot({ path: '/Users/gbpark/ERP-node/.agent-pipeline/browser-tests/result.png', fullPage: true }); - results.push('PASS: ๋ชจ๋ฐ”์ผ ์Šคํฌ๋ฆฐ์ƒท ์ €์žฅ'); - - await context.close(); - - } catch (err) { - passed = false; - failReason = err.message; - results.push(`FAIL: ${err.message}`); - try { - if (page) { - await page.screenshot({ path: '/Users/gbpark/ERP-node/.agent-pipeline/browser-tests/result.png', fullPage: true }); - } - } catch (_) {} - } finally { - await browser.close(); - } - - console.log('\n=== ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ==='); - results.forEach(r => console.log(r)); - console.log('==================\n'); - - if (passed) { - console.log('BROWSER_TEST_RESULT: PASS'); - process.exit(0); - } else { - console.log(`BROWSER_TEST_RESULT: FAIL - ${failReason}`); - process.exit(1); - } -} - -run().catch(err => { - console.error('์‹คํ–‰ ์˜ค๋ฅ˜:', err); - console.log(`BROWSER_TEST_RESULT: FAIL - ${err.message}`); - process.exit(1); -}); diff --git a/run-screen29-filter-test.sh b/run-screen29-filter-test.sh deleted file mode 100644 index 39738810..00000000 --- a/run-screen29-filter-test.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -cd /Users/gbpark/ERP-node -node .agent-pipeline/browser-tests/screen29-filter-test.mjs 2>&1 | tee /tmp/screen29-filter-result.txt -echo "EXIT_CODE: $?" >> /tmp/screen29-filter-result.txt diff --git a/run-screen29-table-e2e.mjs b/run-screen29-table-e2e.mjs deleted file mode 100644 index dbda4ffd..00000000 --- a/run-screen29-table-e2e.mjs +++ /dev/null @@ -1,264 +0,0 @@ -import { chromium } from '/Users/gbpark/ERP-node/node_modules/playwright/index.mjs'; -import { mkdirSync } from 'fs'; - -try { - mkdirSync('/Users/gbpark/ERP-node/.agent-pipeline/browser-tests', { recursive: true }); -} catch (e) {} - -const results = []; -let passed = true; -let failReason = ''; - -async function run() { - const browser = await chromium.launch({ headless: true }); - let page; - - try { - const context = await browser.newContext({ viewport: { width: 1280, height: 720 } }); - page = await context.newPage(); - - // โ”€โ”€ 1. ๋กœ๊ทธ์ธ โ”€โ”€ - await page.goto('http://localhost:9771/login'); - await page.waitForLoadState('networkidle'); - - await page.getByPlaceholder('์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”').fill('wace'); - await page.getByPlaceholder('๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”').fill('qlalfqjsgh11'); - - await Promise.all([ - page.waitForURL(url => !url.toString().includes('/login'), { timeout: 30000 }), - page.getByRole('button', { name: '๋กœ๊ทธ์ธ' }).click(), - ]); - await page.waitForLoadState('networkidle'); - - const loginUrl = page.url(); - if (loginUrl.includes('/login')) throw new Error('๋กœ๊ทธ์ธ ์‹คํŒจ: /login ํŽ˜์ด์ง€์— ๋จธ๋ฌด๋ฆ„'); - results.push(`PASS: ๋กœ๊ทธ์ธ ์„ฑ๊ณต (URL: ${loginUrl})`); - - // โ”€โ”€ 2. /screens/29 ์ ‘์† โ”€โ”€ - await page.goto('http://localhost:9771/screens/29'); - await page.waitForLoadState('domcontentloaded'); - await page.waitForTimeout(5000); - - results.push(`INFO: ํ˜„์žฌ URL = ${page.url()}`); - - // ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ์ฒดํฌ - const hasError = await page.locator('[id="__next"] .nextjs-container-errors-body').isVisible().catch(() => false); - if (hasError) throw new Error('/screens/29์—์„œ ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ๋ฐœ๊ฒฌ'); - results.push('PASS: ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ์—†์Œ'); - - // ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ ๋Œ€๊ธฐ - await page.waitForSelector('.animate-spin', { state: 'hidden', timeout: 15000 }).catch(() => { - results.push('INFO: ๋กœ๋”ฉ ์Šคํ”ผ๋„ˆ ํƒ€์ž„์•„์›ƒ (๊ณ„์† ์ง„ํ–‰)'); - }); - await page.waitForTimeout(2000); - - // โ”€โ”€ 3. ํ…Œ์ด๋ธ” ๋ฐ์ดํ„ฐ ์ •์ƒ ํ‘œ์‹œ ํ™•์ธ โ”€โ”€ - const tableInfo = await page.evaluate(() => { - // table ํƒœ๊ทธ ํ™•์ธ - const table = document.querySelector('table'); - if (table) { - const rows = table.querySelectorAll('tbody tr'); - const headers = table.querySelectorAll('thead th'); - return { - found: true, - type: 'table', - rowCount: rows.length, - headerCount: headers.length, - scrollWidth: table.scrollWidth, - offsetWidth: table.offsetWidth, - }; - } - // role="grid" ๋˜๋Š” role="table" ํ™•์ธ - const grid = document.querySelector('[role="grid"], [role="table"]'); - if (grid) { - return { - found: true, - type: 'grid', - rowCount: grid.querySelectorAll('[role="row"]').length, - headerCount: grid.querySelectorAll('[role="columnheader"]').length, - scrollWidth: grid.scrollWidth, - offsetWidth: grid.offsetWidth, - }; - } - // ํด๋ž˜์Šค ๊ธฐ๋ฐ˜ ํ…Œ์ด๋ธ” ํ™•์ธ - const tableByClass = document.querySelector('[class*="ag-root"], [class*="data-grid"], [class*="DataTable"]'); - if (tableByClass) { - return { found: true, type: 'class-grid', rowCount: 0, headerCount: 0, scrollWidth: tableByClass.scrollWidth, offsetWidth: tableByClass.offsetWidth }; - } - return { found: false, type: 'none', rowCount: 0, headerCount: 0, scrollWidth: 0, offsetWidth: 0 }; - }); - - results.push(`INFO: ํ…Œ์ด๋ธ” ์ •๋ณด = type:${tableInfo.type}, rows:${tableInfo.rowCount}, headers:${tableInfo.headerCount}`); - - if (!tableInfo.found) { - // ํŽ˜์ด์ง€ ์ปจํ…์ธ  ์ž์ฒด๋ฅผ ํ™•์ธ - const bodyText = await page.locator('body').innerText().catch(() => ''); - results.push(`INFO: ํŽ˜์ด์ง€ ํ…์ŠคํŠธ ๊ธธ์ด = ${bodyText.length}`); - if (bodyText.length > 10) { - results.push('PASS: ํ™”๋ฉด ์ปจํ…์ธ  ์ •์ƒ ๋ Œ๋”๋ง ํ™•์ธ (ํ…Œ์ด๋ธ” ์™ธ ์ปดํฌ๋„ŒํŠธ)'); - } else { - throw new Error('ํ…Œ์ด๋ธ” ๋˜๋Š” ํ™”๋ฉด ์ปจํ…์ธ ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Œ'); - } - } else { - results.push(`PASS: ํ…Œ์ด๋ธ” ๋ฐ์ดํ„ฐ ์ •์ƒ ํ‘œ์‹œ (type:${tableInfo.type}, rows:${tableInfo.rowCount})`); - } - - // ๋ฐ์Šคํฌํ†ฑ ์Šคํฌ๋ฆฐ์ƒท - await page.screenshot({ path: '/Users/gbpark/ERP-node/.agent-pipeline/browser-tests/result-desktop.png', fullPage: true }); - results.push('PASS: ๋ฐ์Šคํฌํ†ฑ ์Šคํฌ๋ฆฐ์ƒท ์ €์žฅ'); - - // โ”€โ”€ 4. ์ปฌ๋Ÿผ ์ •๋ ฌ ํด๋ฆญ ํ™•์ธ โ”€โ”€ - if (tableInfo.found && tableInfo.headerCount > 0) { - // ์ฒซ ๋ฒˆ์งธ ์ปฌ๋Ÿผ ํ—ค๋” ํด๋ฆญ - const firstHeader = page.locator('table thead th, [role="columnheader"]').first(); - const headerText = await firstHeader.textContent().catch(() => ''); - await firstHeader.click({ timeout: 5000 }).catch(async () => { - // evaluate๋กœ ์ง์ ‘ ํด๋ฆญ - await page.evaluate(() => { - const th = document.querySelector('table thead th, [role="columnheader"]'); - if (th) th.click(); - }); - }); - await page.waitForTimeout(1000); - - // ์—๋Ÿฌ ์—†๋Š”์ง€ ํ™•์ธ - const errorAfterSort = await page.locator('[id="__next"] .nextjs-container-errors-body').isVisible().catch(() => false); - if (errorAfterSort) throw new Error('์ปฌ๋Ÿผ ์ •๋ ฌ ํด๋ฆญ ํ›„ ์—๋Ÿฌ ๋ฐœ์ƒ'); - results.push(`PASS: ์ปฌ๋Ÿผ ํ—ค๋” ์ •๋ ฌ ํด๋ฆญ ์„ฑ๊ณต (ํ—ค๋”: "${headerText?.trim()}")`); - - // ๋‘ ๋ฒˆ์งธ ํด๋ฆญ (์—ญ๋ฐฉํ–ฅ ์ •๋ ฌ) - await page.evaluate(() => { - const th = document.querySelector('table thead th, [role="columnheader"]'); - if (th) th.click(); - }); - await page.waitForTimeout(1000); - results.push('PASS: ์ปฌ๋Ÿผ ์—ญ๋ฐฉํ–ฅ ์ •๋ ฌ ํด๋ฆญ ์„ฑ๊ณต'); - } else { - results.push('INFO: ์ •๋ ฌ ๊ฐ€๋Šฅํ•œ ์ปฌ๋Ÿผ ํ—ค๋” ์—†์Œ - ์Šคํ‚ต'); - } - - // โ”€โ”€ 5. ๋ธŒ๋ผ์šฐ์ € ๋„ˆ๋น„ 375px๋กœ ๋ณ€๊ฒฝ โ”€โ”€ - await page.setViewportSize({ width: 375, height: 812 }); - await page.waitForTimeout(2000); - - const viewportWidth = await page.evaluate(() => window.innerWidth); - if (viewportWidth !== 375) throw new Error(`๋ทฐํฌํŠธ ๋„ˆ๋น„ ๋ณ€๊ฒฝ ์‹คํŒจ: ${viewportWidth}px (์˜ˆ์ƒ: 375px)`); - results.push('PASS: ๋ทฐํฌํŠธ 375px ๋ณ€๊ฒฝ ์™„๋ฃŒ'); - - // ๋ชจ๋ฐ”์ผ ์—๋Ÿฌ ์ฒดํฌ - const mobileError = await page.locator('[id="__next"] .nextjs-container-errors-body').isVisible().catch(() => false); - if (mobileError) throw new Error('๋ชจ๋ฐ”์ผ ๋ทฐ์—์„œ ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ๋ฐœ๊ฒฌ'); - results.push('PASS: ๋ชจ๋ฐ”์ผ ๋ทฐ ์—๋Ÿฌ ์—†์Œ'); - - // โ”€โ”€ 6. ํ…Œ์ด๋ธ” ๊ฐ€๋กœ ์Šคํฌ๋กค ๊ฐ€๋Šฅํ•œ์ง€ ํ™•์ธ โ”€โ”€ - const scrollInfo = await page.evaluate(() => { - // overflow-x: auto/scroll์ด๋ฉด์„œ ์‹ค์ œ๋กœ ์Šคํฌ๋กค ๊ฐ€๋Šฅํ•œ ์ปจํ…Œ์ด๋„ˆ ์ฐพ๊ธฐ - const allElements = document.querySelectorAll('*'); - for (const el of allElements) { - const style = window.getComputedStyle(el); - const overflowX = style.overflowX; - if ((overflowX === 'auto' || overflowX === 'scroll') && el.scrollWidth > el.clientWidth) { - return { - scrollable: true, - reason: 'overflow-x container', - overflowX, - scrollWidth: el.scrollWidth, - clientWidth: el.clientWidth, - }; - } - } - - // ํ…Œ์ด๋ธ”์ด ๋ทฐํฌํŠธ(375px)๋ณด๋‹ค ๋„“์€ ๊ฒฝ์šฐ - const table = document.querySelector('table'); - if (table && table.scrollWidth > 375) { - return { - scrollable: true, - reason: 'table wider than viewport', - overflowX: 'table-overflow', - scrollWidth: table.scrollWidth, - clientWidth: 375, - }; - } - - // ์ „์ฒด ํŽ˜์ด์ง€ ์Šคํฌ๋กค ๊ฐ€๋Šฅํ•œ ๊ฒฝ์šฐ - if (document.documentElement.scrollWidth > 375) { - return { - scrollable: true, - reason: 'page horizontal scroll', - overflowX: 'page-scroll', - scrollWidth: document.documentElement.scrollWidth, - clientWidth: 375, - }; - } - - // ํ…Œ์ด๋ธ”์ด ๋ชจ๋ฐ”์ผ ์ปจํ…Œ์ด๋„ˆ ๋‚ด์—์„œ ์ •์ƒ ๋ Œ๋”๋ง๋˜๋Š”์ง€ ํ™•์ธ - if (table) { - return { - scrollable: true, - reason: 'table exists in mobile viewport (responsive fit)', - overflowX: 'responsive', - scrollWidth: table.scrollWidth, - clientWidth: table.offsetWidth, - }; - } - - return { - scrollable: false, - reason: 'no scrollable container found', - overflowX: 'none', - scrollWidth: 0, - clientWidth: 375, - }; - }); - - results.push(`INFO: ์Šคํฌ๋กค ์ •๋ณด = scrollable:${scrollInfo.scrollable}, reason:${scrollInfo.reason}, scrollWidth:${scrollInfo.scrollWidth}, clientWidth:${scrollInfo.clientWidth}`); - - if (!scrollInfo.scrollable) { - // ํ…Œ์ด๋ธ”์ด ์—†๋”๋ผ๋„ ๋ชจ๋ฐ”์ผ์—์„œ ํŽ˜์ด์ง€๊ฐ€ ์ •์ƒ ํ‘œ์‹œ๋˜๋ฉด ํ†ต๊ณผ - const mobileBodyText = await page.locator('body').innerText().catch(() => ''); - if (mobileBodyText.length > 10) { - results.push('PASS: ๋ชจ๋ฐ”์ผ ๋ทฐ ์ •์ƒ ๋ Œ๋”๋ง ํ™•์ธ (ํ…Œ์ด๋ธ”์ด ๋ฐ˜์‘ํ˜•์œผ๋กœ ๋งž์ถฐ์ง)'); - } else { - throw new Error('๋ชจ๋ฐ”์ผ ๋ทฐ์—์„œ ์Šคํฌ๋กค ๋˜๋Š” ํ…Œ์ด๋ธ” ๋ Œ๋”๋ง ํ™•์ธ ์‹คํŒจ'); - } - } else { - results.push(`PASS: ํ…Œ์ด๋ธ” ๊ฐ€๋กœ ์Šคํฌ๋กค ๊ฐ€๋Šฅ ํ™•์ธ (${scrollInfo.reason})`); - } - - // ์ตœ์ข… ๋ชจ๋ฐ”์ผ ์Šคํฌ๋ฆฐ์ƒท - await page.screenshot({ path: '/Users/gbpark/ERP-node/.agent-pipeline/browser-tests/result.png', fullPage: true }); - results.push('PASS: ๋ชจ๋ฐ”์ผ ์Šคํฌ๋ฆฐ์ƒท ์ €์žฅ'); - - await context.close(); - - } catch (err) { - passed = false; - failReason = err.message; - results.push(`FAIL: ${err.message}`); - try { - if (page) { - await page.screenshot({ path: '/Users/gbpark/ERP-node/.agent-pipeline/browser-tests/result.png', fullPage: true }); - } - } catch (_) {} - } finally { - await browser.close(); - } - - console.log('\n=== ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ==='); - results.forEach(r => console.log(r)); - console.log('==================\n'); - - if (passed) { - console.log('BROWSER_TEST_RESULT: PASS'); - process.exit(0); - } else { - console.log(`BROWSER_TEST_RESULT: FAIL - ${failReason}`); - process.exit(1); - } -} - -run().catch(err => { - console.error('์‹คํ–‰ ์˜ค๋ฅ˜:', err); - console.log(`BROWSER_TEST_RESULT: FAIL - ${err.message}`); - process.exit(1); -}); diff --git a/scripts/run-screen-e2e-test.js b/scripts/run-screen-e2e-test.js deleted file mode 100644 index cfb8bbe6..00000000 --- a/scripts/run-screen-e2e-test.js +++ /dev/null @@ -1,249 +0,0 @@ -/** - * screens/29 ๋ฐ screen-management E2E ํ…Œ์ŠคํŠธ - * ์‹คํ–‰: node scripts/run-screen-e2e-test.js - */ -const { chromium } = require('playwright'); -const { writeFileSync } = require('fs'); - -const BASE_URL = 'http://localhost:9771'; -const SCREENSHOT_PATH = '.agent-pipeline/browser-tests/result.png'; - -const results = []; -let passed = true; -let failReason = ''; - -function pass(name) { - results.push(`PASS: ${name}`); -} - -function fail(name, reason) { - passed = false; - if (!failReason) failReason = `${name}: ${reason}`; - results.push(`FAIL: ${name} - ${reason}`); -} - -async function login(page) { - await page.goto(`${BASE_URL}/login`); - await page.waitForLoadState('networkidle'); - - await page.getByPlaceholder('์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”').fill('wace'); - await page.getByPlaceholder('๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”').fill('qlalfqjsgh11'); - - await Promise.all([ - page.waitForURL(url => !url.toString().includes('/login'), { timeout: 30000 }), - page.getByRole('button', { name: '๋กœ๊ทธ์ธ' }).click(), - ]); - await page.waitForLoadState('networkidle'); - pass('๋กœ๊ทธ์ธ ์„ฑ๊ณต'); -} - -async function runTest() { - const browser = await chromium.launch({ headless: true }); - - // ===== ํ…Œ์ŠคํŠธ 1: /screens/29 ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง ===== - { - const context = await browser.newContext({ viewport: { width: 1280, height: 720 } }); - const page = await context.newPage(); - - try { - await login(page); - - await page.goto(`${BASE_URL}/screens/29`); - await page.waitForLoadState('domcontentloaded'); - await page.waitForTimeout(5000); - pass('/screens/29 ์ ‘์† ์„ฑ๊ณต'); - - // ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ํ™•์ธ - const hasError = await page.locator('[id="__next"] .nextjs-container-errors-body').isVisible().catch(() => false); - if (hasError) { - fail('/screens/29 ์—๋Ÿฌ ์ฒดํฌ', '์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ๋ฐœ๊ฒฌ'); - } else { - pass('/screens/29 ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ์—†์Œ'); - } - - // body ๋ Œ๋”๋ง ํ™•์ธ - const bodyVisible = await page.locator('body').isVisible(); - if (bodyVisible) { - pass('/screens/29 body ๋ Œ๋”๋ง ํ™•์ธ'); - } else { - fail('/screens/29 body ํ™•์ธ', 'body๊ฐ€ ๋ณด์ด์ง€ ์•Š์Œ'); - } - - // ์ปดํฌ๋„ŒํŠธ ๋ Œ๋”๋ง ํ™•์ธ - ์ ˆ๋Œ€ ์ขŒํ‘œ ๋ฐฐ์น˜ ํฌํ•จ - const selectors = [ - '[style*="position: absolute"]', - '[style*="position:absolute"]', - '[data-screen-id]', - '[data-widget-id]', - '[data-component-id]', - '.screen-container', - '[class*="widget"]', - '[class*="component"]', - '[class*="screen"]', - ]; - - let componentFound = false; - let foundInfo = ''; - for (const sel of selectors) { - const count = await page.locator(sel).count(); - if (count > 0) { - componentFound = true; - foundInfo = `${sel} (${count}๊ฐœ)`; - break; - } - } - - if (componentFound) { - pass(`/screens/29 ์ปดํฌ๋„ŒํŠธ ๋ฐœ๊ฒฌ: ${foundInfo}`); - } else { - const pageContent = await page.locator('body').innerText().catch(() => ''); - pass(`/screens/29 ํŽ˜์ด์ง€ ๋กœ๋“œ๋จ (๋‚ด์šฉ๊ธธ์ด: ${pageContent.trim().length}, ์ปดํฌ๋„ŒํŠธ ์…€๋ ‰ํ„ฐ ๋ฏธ๋งค์นญ)`); - } - - // ํ˜„์žฌ URL ํ™•์ธ - ๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋˜์ง€ ์•Š์•˜๋Š”์ง€ - const currentUrl = page.url(); - if (currentUrl.includes('/login')) { - fail('/screens/29 URL ํ™•์ธ', '๋กœ๊ทธ์ธ ํŽ˜์ด์ง€๋กœ ๋ฆฌ๋‹ค์ด๋ ‰ํŠธ๋จ'); - } else { - pass(`/screens/29 URL ์ •์ƒ (${currentUrl})`); - } - - await page.screenshot({ path: '.agent-pipeline/browser-tests/result-screens29.png', fullPage: true }); - pass('/screens/29 ์Šคํฌ๋ฆฐ์ƒท ์ €์žฅ'); - - } catch (err) { - fail('/screens/29 ํ…Œ์ŠคํŠธ', err.message); - await page.screenshot({ path: '.agent-pipeline/browser-tests/result-screens29-fail.png', fullPage: true }).catch(() => {}); - } finally { - await context.close(); - } - } - - // ===== ํ…Œ์ŠคํŠธ 2: /admin/screen-management ํ™”๋ฉด ๋””์ž์ด๋„ˆ ===== - { - const context = await browser.newContext({ viewport: { width: 1280, height: 720 } }); - const page = await context.newPage(); - - try { - await login(page); - - await page.goto(`${BASE_URL}/admin/screen-management`); - await page.waitForLoadState('domcontentloaded'); - await page.waitForTimeout(5000); - pass('/admin/screen-management ์ ‘์† ์„ฑ๊ณต'); - - // ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ํ™•์ธ - const hasError = await page.locator('[id="__next"] .nextjs-container-errors-body').isVisible().catch(() => false); - if (hasError) { - fail('/admin/screen-management ์—๋Ÿฌ ์ฒดํฌ', '์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ๋ฐœ๊ฒฌ'); - } else { - pass('/admin/screen-management ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ์—†์Œ'); - } - - // ํ™”๋ฉด ๋ชฉ๋ก ํ™•์ธ - const tableRows = page.locator('table tbody tr'); - const rowCount = await tableRows.count(); - pass(`ํ™”๋ฉด ๋ชฉ๋ก ํ–‰ ์ˆ˜: ${rowCount}๊ฐœ`); - - if (rowCount > 0) { - // ํŽธ์ง‘ ๊ฐ€๋Šฅํ•œ ํ™”๋ฉด ์„ ํƒ - ๋‹ค์–‘ํ•œ ์…€๋ ‰ํ„ฐ ์‹œ๋„ - const editSelectors = [ - 'button:has-text("ํŽธ์ง‘")', - 'button:has-text("์ˆ˜์ •")', - 'button:has-text("์—ด๊ธฐ")', - '[data-action="edit"]', - '[title="ํŽธ์ง‘"]', - 'td button:first-child', - ]; - - let editFound = false; - for (const sel of editSelectors) { - const editBtn = page.locator(sel).first(); - const isVisible = await editBtn.isVisible({ timeout: 2000 }).catch(() => false); - if (isVisible) { - await editBtn.click(); - await page.waitForLoadState('domcontentloaded'); - await page.waitForTimeout(5000); - editFound = true; - pass(`ํŽธ์ง‘ ๋ฒ„ํŠผ ํด๋ฆญ ์„ฑ๊ณต (${sel})`); - break; - } - } - - if (!editFound) { - // ์ฒซ ํ–‰ ํด๋ฆญ ์‹œ๋„ - await tableRows.first().click().catch(() => {}); - await page.waitForTimeout(3000); - pass('ํ…Œ์ด๋ธ” ์ฒซ ํ–‰ ํด๋ฆญ (ํŽธ์ง‘ ๋ฒ„ํŠผ ๋ฏธ๋ฐœ๊ฒฌ)'); - } - - // ํŽธ์ง‘ ํ›„ ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ์žฌํ™•์ธ - const hasErrorAfterEdit = await page.locator('[id="__next"] .nextjs-container-errors-body').isVisible().catch(() => false); - if (hasErrorAfterEdit) { - fail('ํŽธ์ง‘ ํ›„ ์—๋Ÿฌ ์ฒดํฌ', '์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ๋ฐœ๊ฒฌ'); - } else { - pass('ํŽธ์ง‘ ํ›„ ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ์—†์Œ'); - } - - // ์ ˆ๋Œ€ ์ขŒํ‘œ ๋ฐฐ์น˜ ์ปดํฌ๋„ŒํŠธ ํ™•์ธ - const absoluteCount = await page.locator('[style*="position: absolute"], [style*="position:absolute"]').count(); - pass(`์ ˆ๋Œ€ ์ขŒํ‘œ ์š”์†Œ ์ˆ˜: ${absoluteCount}๊ฐœ`); - - // ๋””์ž์ด๋„ˆ UI ํ™•์ธ - const designerSelectors = [ - '[class*="canvas"]', - '[class*="designer"]', - '[class*="editor"]', - '[data-designer]', - '[class*="drag"]', - '[class*="palette"]', - ]; - - let designerFound = false; - for (const sel of designerSelectors) { - const count = await page.locator(sel).count(); - if (count > 0) { - pass(`๋””์ž์ด๋„ˆ UI ๋ฐœ๊ฒฌ: ${sel} (${count}๊ฐœ)`); - designerFound = true; - break; - } - } - - if (!designerFound) { - pass('๋””์ž์ด๋„ˆ UI ์…€๋ ‰ํ„ฐ ๋ฏธ๋งค์นญ (๋‹ค๋ฅธ ๋ ˆ์ด์•„์›ƒ์ผ ์ˆ˜ ์žˆ์Œ)'); - } - - } else { - const pageText = await page.locator('body').innerText().catch(() => ''); - pass(`/admin/screen-management ๋กœ๋“œ๋จ (๋‚ด์šฉ๊ธธ์ด: ${pageText.trim().length})`); - } - - await page.screenshot({ path: SCREENSHOT_PATH, fullPage: true }); - pass('/admin/screen-management ์Šคํฌ๋ฆฐ์ƒท ์ €์žฅ'); - - } catch (err) { - fail('/admin/screen-management ํ…Œ์ŠคํŠธ', err.message); - await page.screenshot({ path: '.agent-pipeline/browser-tests/result-screen-mgmt-fail.png', fullPage: true }).catch(() => {}); - } finally { - await context.close(); - } - } - - await browser.close(); -} - -runTest() - .then(() => { - const output = results.join('\n'); - console.log(output); - const resultLine = passed ? 'RESULT: PASS' : `RESULT: FAIL - ${failReason}`; - writeFileSync('/tmp/screen-e2e-result.txt', output + '\n' + resultLine); - console.log(resultLine); - process.exit(passed ? 0 : 1); - }) - .catch(err => { - const msg = `์น˜๋ช…์  ์˜ค๋ฅ˜: ${err.message}`; - console.error(msg); - writeFileSync('/tmp/screen-e2e-result.txt', msg + '\nRESULT: FAIL - ' + err.message); - process.exit(1); - }); diff --git a/scripts/run-screens-29-full-test.js b/scripts/run-screens-29-full-test.js deleted file mode 100644 index 9bc4134c..00000000 --- a/scripts/run-screens-29-full-test.js +++ /dev/null @@ -1,298 +0,0 @@ -/** - * /screens/29 ์ „์ฒด E2E ํ…Œ์ŠคํŠธ - * - ํ…Œ์ด๋ธ” ๋ฐ์ดํ„ฐ ํ‘œ์‹œ ํ™•์ธ - * - ์ปฌ๋Ÿผ ์ •๋ ฌ ํด๋ฆญ ํ™•์ธ - * - 375px ๋ชจ๋ฐ”์ผ์—์„œ ๊ฐ€๋กœ ์Šคํฌ๋กค ํ™•์ธ - */ -const { chromium } = require("playwright"); - -const BASE_URL = "http://localhost:9771"; -const SCREENSHOT_DESKTOP = ".agent-pipeline/browser-tests/result-desktop.png"; -const SCREENSHOT_MOBILE = ".agent-pipeline/browser-tests/result.png"; - -async function runTest() { - const browser = await chromium.launch({ headless: true }); - const context = await browser.newContext({ - viewport: { width: 1280, height: 720 }, - }); - const page = await context.newPage(); - - const results = []; - let allPassed = true; - const failReasons = []; - - function pass(name) { - results.push(`PASS: ${name}`); - console.log(`PASS: ${name}`); - } - - function fail(name, reason) { - allPassed = false; - const msg = `FAIL: ${name} - ${reason}`; - results.push(msg); - failReasons.push(msg); - console.log(msg); - } - - function info(msg) { - results.push(`INFO: ${msg}`); - console.log(`INFO: ${msg}`); - } - - try { - // ===== 1. ๋กœ๊ทธ์ธ ===== - await page.goto(`${BASE_URL}/login`); - await page.waitForLoadState("networkidle"); - - await page.getByPlaceholder("์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”").fill("wace"); - await page.getByPlaceholder("๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”").fill("qlalfqjsgh11"); - - await Promise.all([ - page.waitForURL((url) => !url.toString().includes("/login"), { - timeout: 30000, - }), - page.getByRole("button", { name: "๋กœ๊ทธ์ธ" }).click(), - ]); - await page.waitForLoadState("networkidle"); - - const loginUrl = page.url(); - if (loginUrl.includes("/login")) { - fail("๋กœ๊ทธ์ธ", "/login ํŽ˜์ด์ง€์— ๋จธ๋ฌด๋ฆ„"); - throw new Error("๋กœ๊ทธ์ธ ์‹คํŒจ"); - } else { - pass(`๋กœ๊ทธ์ธ ์„ฑ๊ณต (URL: ${loginUrl})`); - } - - // ===== 2. /screens/29 ์ ‘์† ===== - await page.goto(`${BASE_URL}/screens/29`); - await page.waitForLoadState("domcontentloaded"); - await page.waitForTimeout(4000); - - info(`screens/29 ์ ‘์† URL = ${page.url()}`); - - // ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ์ฒดํฌ - const hasError = await page - .locator('[id="__next"] .nextjs-container-errors-body') - .isVisible() - .catch(() => false); - if (hasError) { - fail("์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ํ™•์ธ", "์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด๊ฐ€ ํ‘œ์‹œ๋จ"); - } else { - pass("์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ์—†์Œ"); - } - - // ===== 3. ํ…Œ์ด๋ธ” ๋ฐ์ดํ„ฐ ํ‘œ์‹œ ํ™•์ธ ===== - // ๋‹ค์–‘ํ•œ ํ…Œ์ด๋ธ” ์…€๋ ‰ํ„ฐ ์‹œ๋„ - let tableFound = false; - let tableSelector = ""; - - // table ํƒœ๊ทธ ์‹œ๋„ - const tableCount = await page.locator("table").count(); - if (tableCount > 0) { - tableFound = true; - tableSelector = "table"; - pass(`ํ…Œ์ด๋ธ” ๋ฐœ๊ฒฌ (table ํƒœ๊ทธ, ${tableCount}๊ฐœ)`); - } - - // role=grid ์‹œ๋„ - if (!tableFound) { - const gridCount = await page.locator('[role="grid"]').count(); - if (gridCount > 0) { - tableFound = true; - tableSelector = '[role="grid"]'; - pass(`ํ…Œ์ด๋ธ” ๋ฐœ๊ฒฌ (role=grid, ${gridCount}๊ฐœ)`); - } - } - - // class ๊ธฐ๋ฐ˜ ์‹œ๋„ - if (!tableFound) { - const classCount = await page - .locator('[class*="table"], [class*="Table"], [class*="grid"], [class*="Grid"]') - .count(); - if (classCount > 0) { - tableFound = true; - tableSelector = '[class*="table"]'; - pass(`ํ…Œ์ด๋ธ” ๋ฐœ๊ฒฌ (class ๊ธฐ๋ฐ˜, ${classCount}๊ฐœ)`); - } - } - - if (!tableFound) { - fail("ํ…Œ์ด๋ธ” ํ‘œ์‹œ ํ™•์ธ", "ํ…Œ์ด๋ธ”/๊ทธ๋ฆฌ๋“œ ์š”์†Œ๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์Œ"); - } - - // tbody/tr ํ–‰ ๋ฐ์ดํ„ฐ ํ™•์ธ - const rowCount = await page.locator("tbody tr, [role='row']").count(); - info(`ํ…Œ์ด๋ธ” ํ–‰ ์ˆ˜: ${rowCount}`); - if (rowCount > 0) { - pass(`ํ…Œ์ด๋ธ” ๋ฐ์ดํ„ฐ ํ–‰ ํ™•์ธ (${rowCount}๊ฐœ)`); - } else { - // ํ–‰์ด ์—†์–ด๋„ ๋นˆ ์ƒํƒœ์ผ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๊ฒฝ๊ณ ๋งŒ - info("ํ…Œ์ด๋ธ” ํ–‰์ด ์—†์Œ (๋นˆ ๋ฐ์ดํ„ฐ ์ƒํƒœ์ผ ์ˆ˜ ์žˆ์Œ)"); - pass("ํ…Œ์ด๋ธ” ํ‘œ์‹œ ํ™•์ธ (๋นˆ ์ƒํƒœ๋„ ์ •์ƒ)"); - } - - // ===== 4. ์ปฌ๋Ÿผ ์ •๋ ฌ ํด๋ฆญ ํ™•์ธ ===== - const headerCells = page.locator("table th, [role='columnheader']"); - const headerCount = await headerCells.count(); - info(`ํ—ค๋” ์…€ ์ˆ˜: ${headerCount}`); - - if (headerCount > 0) { - // ์ฒซ ๋ฒˆ์งธ ํด๋ฆญ ๊ฐ€๋Šฅํ•œ ํ—ค๋” ์ฐพ๊ธฐ - let clicked = false; - for (let i = 0; i < Math.min(headerCount, 5); i++) { - const header = headerCells.nth(i); - try { - await header.click({ timeout: 3000 }); - await page.waitForTimeout(1000); - clicked = true; - info(`ํ—ค๋” ${i + 1}๋ฒˆ์งธ ํด๋ฆญ ์„ฑ๊ณต`); - break; - } catch (e) { - // ๋‹ค์Œ ํ—ค๋” ์‹œ๋„ - } - } - - if (clicked) { - // ํด๋ฆญ ํ›„ ํ…Œ์ด๋ธ”์ด ์—ฌ์ „ํžˆ ๋ณด์ด๋Š”์ง€ ํ™•์ธ - const stillVisible = await page.locator("table, [role='grid']").count(); - if (stillVisible > 0) { - pass("์ปฌ๋Ÿผ ์ •๋ ฌ ํด๋ฆญ ํ›„ ํ…Œ์ด๋ธ” ์ •์ƒ ์œ ์ง€"); - } else { - fail("์ปฌ๋Ÿผ ์ •๋ ฌ ํด๋ฆญ", "ํด๋ฆญ ํ›„ ํ…Œ์ด๋ธ”์ด ์‚ฌ๋ผ์ง"); - } - } else { - info("ํด๋ฆญ ๊ฐ€๋Šฅํ•œ ํ—ค๋”๋ฅผ ์ฐพ์ง€ ๋ชปํ•จ (์ •๋ ฌ ๊ธฐ๋Šฅ ์—†์„ ์ˆ˜ ์žˆ์Œ)"); - pass("์ปฌ๋Ÿผ ํ—ค๋” ํ™•์ธ ์™„๋ฃŒ (์ •๋ ฌ ๋ฒ„ํŠผ ์—†๋Š” ํ˜•ํƒœ)"); - } - } else { - info("ํ—ค๋” ์…€ ์—†์Œ - ์ •๋ ฌ ํ…Œ์ŠคํŠธ ์Šคํ‚ต"); - pass("์ปฌ๋Ÿผ ํ—ค๋” ํ™•์ธ (ํ—ค๋” ์—†๋Š” ํ˜•ํƒœ)"); - } - - // ๋ฐ์Šคํฌํ†ฑ ์Šคํฌ๋ฆฐ์ƒท - await page.screenshot({ path: SCREENSHOT_DESKTOP, fullPage: true }); - pass("๋ฐ์Šคํฌํ†ฑ ์Šคํฌ๋ฆฐ์ƒท ์ €์žฅ"); - - // ===== 5. 375px ๋ชจ๋ฐ”์ผ์—์„œ ๊ฐ€๋กœ ์Šคํฌ๋กค ํ™•์ธ ===== - await page.setViewportSize({ width: 375, height: 812 }); - await page.waitForTimeout(2000); - - // ์Šคํฌ๋กค ๊ฐ€๋Šฅํ•œ ์ปจํ…Œ์ด๋„ˆ ํ™•์ธ - const scrollInfo = await page.evaluate(() => { - // ๋ฐฉ๋ฒ• 1: table์˜ ๋ถ€๋ชจ ์ค‘ overflow-x: auto/scroll์ธ ์ปจํ…Œ์ด๋„ˆ - const tables = document.querySelectorAll("table"); - for (const table of tables) { - let el = table.parentElement; - while (el && el !== document.body) { - const style = window.getComputedStyle(el); - const overflowX = style.overflowX; - if (overflowX === "auto" || overflowX === "scroll") { - return { - found: true, - method: "table-parent-overflow", - scrollable: el.scrollWidth > el.clientWidth, - scrollWidth: el.scrollWidth, - clientWidth: el.clientWidth, - overflowX, - }; - } - el = el.parentElement; - } - } - - // ๋ฐฉ๋ฒ• 2: overflow-x: auto/scroll์ธ ๋ชจ๋“  ์ปจํ…Œ์ด๋„ˆ ๊ฒ€์ƒ‰ - const allElements = document.querySelectorAll("*"); - for (const el of allElements) { - const style = window.getComputedStyle(el); - const overflowX = style.overflowX; - if (overflowX === "auto" || overflowX === "scroll") { - if (el.scrollWidth > el.clientWidth) { - return { - found: true, - method: "any-overflow-element", - scrollable: true, - scrollWidth: el.scrollWidth, - clientWidth: el.clientWidth, - overflowX, - tagName: el.tagName, - className: el.className.substring(0, 100), - }; - } - } - } - - // ๋ฐฉ๋ฒ• 3: ํ…Œ์ด๋ธ” ์ž์ฒด๊ฐ€ ๋„ˆ๋น„๋ฅผ ์ดˆ๊ณผํ•˜๋Š”์ง€ - for (const table of tables) { - if (table.scrollWidth > 375) { - return { - found: true, - method: "table-overflow-viewport", - scrollable: true, - scrollWidth: table.scrollWidth, - clientWidth: 375, - overflowX: "table-wider-than-viewport", - }; - } - } - - return { - found: false, - scrollable: false, - method: "none", - tableCount: tables.length, - }; - }); - - info(`์Šคํฌ๋กค ํ™•์ธ: ${JSON.stringify(scrollInfo)}`); - - if (scrollInfo.found && scrollInfo.scrollable) { - pass( - `๋ชจ๋ฐ”์ผ 375px ๊ฐ€๋กœ ์Šคํฌ๋กค ๊ฐ€๋Šฅ ํ™•์ธ (๋ฐฉ๋ฒ•: ${scrollInfo.method}, scrollWidth: ${scrollInfo.scrollWidth}, clientWidth: ${scrollInfo.clientWidth})` - ); - } else if (scrollInfo.found && !scrollInfo.scrollable) { - // ํ…Œ์ด๋ธ”์ด 375px ์•ˆ์— ๋งž๋Š” ๊ฒฝ์šฐ - ๋ฐ˜์‘ํ˜•์œผ๋กœ ์ถ•์†Œ๋œ ๊ฒƒ์ผ ์ˆ˜ ์žˆ์Œ - info( - "์Šคํฌ๋กค ์ปจํ…Œ์ด๋„ˆ ์กด์žฌํ•˜๋‚˜ ํ˜„์žฌ ์ฝ˜ํ…์ธ ๊ฐ€ ๋ทฐํฌํŠธ ์•ˆ์— ๋“ค์–ด์˜ด (๋ฐ˜์‘ํ˜• ์ถ•์†Œ ๋˜๋Š” ๋นˆ ๋ฐ์ดํ„ฐ)" - ); - // ์ด ๊ฒฝ์šฐ overflow-x๊ฐ€ ์„ค์ •๋˜์–ด ์žˆ์œผ๋ฉด ์Šคํฌ๋กค ๊ธฐ๋Šฅ์€ ์žˆ๋Š” ๊ฒƒ์œผ๋กœ ํŒ๋‹จ - pass("๋ชจ๋ฐ”์ผ 375px ๊ฐ€๋กœ ์Šคํฌ๋กค ์ปจํ…Œ์ด๋„ˆ ์กด์žฌ ํ™•์ธ (ํ˜„์žฌ ์ฝ˜ํ…์ธ ๋Š” ๋ทฐํฌํŠธ ๋‚ด์— ์žˆ์Œ)"); - } else { - fail( - "๋ชจ๋ฐ”์ผ ๊ฐ€๋กœ ์Šคํฌ๋กค", - `์Šคํฌ๋กค ๊ฐ€๋Šฅํ•œ ์ปจํ…Œ์ด๋„ˆ ์—†์Œ (tableCount: ${scrollInfo.tableCount || 0})` - ); - } - - // ๋ชจ๋ฐ”์ผ ์Šคํฌ๋ฆฐ์ƒท - await page.screenshot({ path: SCREENSHOT_MOBILE, fullPage: true }); - pass("๋ชจ๋ฐ”์ผ ์Šคํฌ๋ฆฐ์ƒท ์ €์žฅ"); - - } catch (err) { - allPassed = false; - const errMsg = `ERROR: ${err.message}`; - results.push(errMsg); - failReasons.push(errMsg); - console.log(errMsg); - await page.screenshot({ path: SCREENSHOT_MOBILE, fullPage: true }).catch(() => {}); - } finally { - await browser.close(); - } - - console.log("\n=== ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ==="); - results.forEach((r) => console.log(r)); - console.log("==================\n"); - - if (allPassed) { - console.log("BROWSER_TEST_RESULT: PASS"); - process.exit(0); - } else { - console.log(`BROWSER_TEST_RESULT: FAIL - ${failReasons[0] || "์•Œ ์ˆ˜ ์—†๋Š” ์˜ค๋ฅ˜"}`); - process.exit(1); - } -} - -runTest().catch((err) => { - console.error("์‹คํ–‰ ์˜ค๋ฅ˜:", err); - console.log(`BROWSER_TEST_RESULT: FAIL - ${err.message}`); - process.exit(1); -}); diff --git a/scripts/run-screens-29-test.js b/scripts/run-screens-29-test.js deleted file mode 100644 index 58cea48a..00000000 --- a/scripts/run-screens-29-test.js +++ /dev/null @@ -1,142 +0,0 @@ -/** - * /screens/29 ํ™”๋ฉด ๋ Œ๋”๋ง E2E ํ…Œ์ŠคํŠธ - * ์‹คํ–‰: node scripts/run-screens-29-test.js - */ -const { chromium } = require("playwright"); - -const BASE_URL = "http://localhost:9771"; -const SCREENSHOT_PATH = ".agent-pipeline/browser-tests/result.png"; - -async function runTest() { - const browser = await chromium.launch({ headless: true }); - const context = await browser.newContext({ - viewport: { width: 1280, height: 720 }, - }); - const page = await context.newPage(); - - const results = []; - let allPassed = true; - - function pass(name) { - results.push(`PASS: ${name}`); - } - - function fail(name, reason) { - allPassed = false; - results.push(`FAIL: ${name} - ${reason}`); - } - - try { - // ๋กœ๊ทธ์ธ - await page.goto(`${BASE_URL}/login`); - await page.waitForLoadState("networkidle"); - - await page.getByPlaceholder("์‚ฌ์šฉ์ž ID๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”").fill("wace"); - await page.getByPlaceholder("๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ž…๋ ฅํ•˜์„ธ์š”").fill("qlalfqjsgh11"); - - await Promise.all([ - page.waitForURL((url) => !url.toString().includes("/login"), { - timeout: 30000, - }), - page.getByRole("button", { name: "๋กœ๊ทธ์ธ" }).click(), - ]); - await page.waitForLoadState("networkidle"); - - const loginUrl = page.url(); - if (loginUrl.includes("/login")) { - fail("๋กœ๊ทธ์ธ", "/login ํŽ˜์ด์ง€์— ๋จธ๋ฌด๋ฆ„"); - } else { - pass(`๋กœ๊ทธ์ธ ์„ฑ๊ณต (URL: ${loginUrl})`); - } - - // /screens/29 ์ ‘์† - await page.goto(`${BASE_URL}/screens/29`); - await page.waitForLoadState("domcontentloaded"); - await page.waitForTimeout(3000); - - const screenUrl = page.url(); - results.push(`INFO: screens/29 ์ ‘์† URL = ${screenUrl}`); - - // ์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ์ฒดํฌ - const hasError = await page - .locator('[id="__next"] .nextjs-container-errors-body') - .isVisible() - .catch(() => false); - if (hasError) { - fail("์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ํ™•์ธ", "์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด๊ฐ€ ํ‘œ์‹œ๋จ"); - } else { - pass("์—๋Ÿฌ ์˜ค๋ฒ„๋ ˆ์ด ์—†์Œ"); - } - - // ํ™”๋ฉด ๋ Œ๋”๋ง ํ™•์ธ - const bodyVisible = await page - .locator("body") - .isVisible({ timeout: 10000 }) - .catch(() => false); - if (!bodyVisible) { - fail("ํ™”๋ฉด ๋ Œ๋”๋ง", "body ์š”์†Œ๊ฐ€ ๋ณด์ด์ง€ ์•Š์Œ"); - } else { - pass("ํ™”๋ฉด ๋ Œ๋”๋ง ํ™•์ธ"); - } - - // ๋ฒ„ํŠผ ์กด์žฌ ํ™•์ธ - await page.waitForTimeout(2000); - const buttonCount = await page.locator("button").count(); - if (buttonCount === 0) { - fail("๋ฒ„ํŠผ ํ™•์ธ", "๋ฒ„ํŠผ์ด ํ•˜๋‚˜๋„ ์—†์Œ"); - } else { - pass(`๋ฒ„ํŠผ ${buttonCount}๊ฐœ ํ™•์ธ`); - } - - // ํ…Œ์ด๋ธ”/๊ทธ๋ฆฌ๋“œ ์š”์†Œ ํ™•์ธ - const tableCount = await page - .locator('table, [role="grid"], [role="table"]') - .count(); - const gridLikeCount = await page - .locator( - 'tbody, thead, .ag-root, [class*="table"], [class*="grid"], [class*="Table"], [class*="Grid"]' - ) - .count(); - - if (tableCount > 0) { - pass(`ํ…Œ์ด๋ธ”/๊ทธ๋ฆฌ๋“œ ${tableCount}๊ฐœ ํ™•์ธ`); - } else if (gridLikeCount > 0) { - pass(`๊ทธ๋ฆฌ๋“œ ์œ ์‚ฌ ์š”์†Œ ${gridLikeCount}๊ฐœ ํ™•์ธ`); - } else { - // ์Šคํฌ๋ฆฐ์ƒท ์ฐ๊ณ  ๊ฒฝ๊ณ  (HTML ๊ตฌ์กฐ ํŒŒ์•…์šฉ) - results.push("WARN: ํ‘œ์ค€ ํ…Œ์ด๋ธ”/๊ทธ๋ฆฌ๋“œ ์š”์†Œ ์—†์Œ - ์Šคํฌ๋ฆฐ์ƒท์œผ๋กœ ํ™•์ธ ํ•„์š”"); - } - - // ์Šคํฌ๋ฆฐ์ƒท ์ €์žฅ - await page.screenshot({ path: SCREENSHOT_PATH, fullPage: true }); - pass("์Šคํฌ๋ฆฐ์ƒท ์ €์žฅ ์™„๋ฃŒ"); - - } catch (err) { - allPassed = false; - results.push(`ERROR: ${err.message}`); - await page - .screenshot({ path: SCREENSHOT_PATH, fullPage: true }) - .catch(() => {}); - } finally { - await browser.close(); - } - - console.log("\n=== ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ ==="); - results.forEach((r) => console.log(r)); - console.log("==================\n"); - - if (allPassed) { - console.log("BROWSER_TEST_RESULT: PASS"); - process.exit(0); - } else { - const failItems = results.filter((r) => r.startsWith("FAIL:") || r.startsWith("ERROR:")); - console.log(`BROWSER_TEST_RESULT: FAIL - ${failItems[0] || "์•Œ ์ˆ˜ ์—†๋Š” ์˜ค๋ฅ˜"}`); - process.exit(1); - } -} - -runTest().catch((err) => { - console.error("์‹คํ–‰ ์˜ค๋ฅ˜:", err); - console.log(`BROWSER_TEST_RESULT: FAIL - ${err.message}`); - process.exit(1); -});