- Implemented automatic detection of sourceKeyField based on component configuration, improving flexibility in data handling. - Enhanced the SelectedItemsDetailInputConfigPanel to support automatic FK detection and mapping, streamlining the configuration process. - Updated the database connection logic to handle DATE types correctly, preventing timezone-related issues. - Improved overall component performance by optimizing memoization and state management for better user experience.
158 lines
6.4 KiB
JavaScript
158 lines
6.4 KiB
JavaScript
/**
|
|
* 거래처관리 메뉴 경유 브라우저 테스트
|
|
* 영업관리 > 거래처관리 메뉴 클릭 후 상세 화면 진입
|
|
* 실행: node scripts/browser-test-customer-via-menu.js
|
|
* 브라우저 표시: HEADLESS=0 node scripts/browser-test-customer-via-menu.js
|
|
*/
|
|
const { chromium } = require("playwright");
|
|
|
|
const BASE_URL = "http://localhost:9771";
|
|
const SCREENSHOT_DIR = "test-screenshots";
|
|
const CREDENTIALS = { userId: "topseal_admin", password: "qlalfqjsgh11" };
|
|
|
|
async function runTest() {
|
|
const results = { success: [], failed: [], screenshots: [] };
|
|
const browser = await chromium.launch({ headless: process.env.HEADLESS !== "0" });
|
|
const context = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
|
const page = await context.newPage();
|
|
|
|
const fs = require("fs");
|
|
if (!fs.existsSync(SCREENSHOT_DIR)) fs.mkdirSync(SCREENSHOT_DIR, { recursive: true });
|
|
|
|
const screenshot = async (name) => {
|
|
const path = `${SCREENSHOT_DIR}/${name}.png`;
|
|
await page.screenshot({ path, fullPage: true });
|
|
results.screenshots.push(path);
|
|
console.log(` [스크린샷] ${path}`);
|
|
};
|
|
|
|
try {
|
|
// 로그인 (이미 로그인된 상태면 자동 리다이렉트됨)
|
|
console.log("\n=== 로그인 확인 ===\n");
|
|
await page.goto(`${BASE_URL}/login`, { waitUntil: "networkidle", timeout: 15000 });
|
|
const currentUrl = page.url();
|
|
if (currentUrl.includes("/login") && !(await page.$('input#userId'))) {
|
|
// 로그인 폼이 있으면 로그인
|
|
await page.fill("#userId", CREDENTIALS.userId);
|
|
await page.fill("#password", CREDENTIALS.password);
|
|
await page.click('button[type="submit"]');
|
|
await page.waitForTimeout(3000);
|
|
} else if (currentUrl.includes("/login")) {
|
|
await page.fill("#userId", CREDENTIALS.userId);
|
|
await page.fill("#password", CREDENTIALS.password);
|
|
await page.click('button[type="submit"]');
|
|
await page.waitForTimeout(3000);
|
|
}
|
|
results.success.push("로그인/세션 확인");
|
|
|
|
// 단계 1: 영업관리 메뉴 클릭
|
|
console.log("\n=== 단계 1: 영업관리 메뉴 클릭 ===\n");
|
|
const salesMenu = page.locator('nav, aside').getByText('영업관리', { exact: true }).first();
|
|
if (await salesMenu.count() > 0) {
|
|
await salesMenu.click();
|
|
await page.waitForTimeout(2000);
|
|
results.success.push("영업관리 메뉴 클릭");
|
|
} else {
|
|
const salesAlt = page.getByRole('button', { name: /영업관리/ }).or(page.getByText('영업관리').first());
|
|
if (await salesAlt.count() > 0) {
|
|
await salesAlt.first().click();
|
|
await page.waitForTimeout(2000);
|
|
results.success.push("영업관리 메뉴 클릭 (대안)");
|
|
} else {
|
|
results.failed.push("영업관리 메뉴를 찾을 수 없음");
|
|
}
|
|
}
|
|
await screenshot("01_after_sales_menu");
|
|
|
|
// 단계 2: 거래처관리 서브메뉴 클릭
|
|
console.log("\n=== 단계 2: 거래처관리 서브메뉴 클릭 ===\n");
|
|
const customerMenu = page.getByText("거래처관리", { exact: true }).first();
|
|
if (await customerMenu.count() > 0) {
|
|
await customerMenu.click();
|
|
await page.waitForTimeout(5000);
|
|
results.success.push("거래처관리 메뉴 클릭");
|
|
} else {
|
|
results.failed.push("거래처관리 메뉴를 찾을 수 없음");
|
|
}
|
|
await screenshot("02_after_customer_menu");
|
|
|
|
// 단계 3: 거래처 목록 확인 및 행 클릭
|
|
console.log("\n=== 단계 3: 거래처 목록 확인 ===\n");
|
|
const rows = await page.$$('tbody tr, table tr, [role="row"]');
|
|
const clickableRows = rows.length > 0 ? rows : [];
|
|
if (clickableRows.length > 0) {
|
|
await clickableRows[0].click();
|
|
await page.waitForTimeout(5000);
|
|
results.success.push(`거래처 행 클릭 (${clickableRows.length}개 행 중 첫 번째)`);
|
|
} else {
|
|
results.failed.push("거래처 테이블 행을 찾을 수 없음");
|
|
}
|
|
await screenshot("03_after_row_click");
|
|
|
|
// 단계 4: 편집/수정 버튼 또는 더블클릭 (분할 패널이면 행 선택만으로 우측에 상세 표시될 수 있음)
|
|
console.log("\n=== 단계 4: 상세 화면 진입 시도 ===\n");
|
|
const editBtn = page.locator('button').filter({ hasText: /편집|수정|상세/ }).first();
|
|
let editEnabled = false;
|
|
try {
|
|
if (await editBtn.count() > 0) {
|
|
editEnabled = !(await editBtn.isDisabled());
|
|
}
|
|
} catch (_) {}
|
|
try {
|
|
if (editEnabled) {
|
|
await editBtn.click();
|
|
results.success.push("편집/수정 버튼 클릭");
|
|
} else {
|
|
const row = await page.$('tbody tr, table tr');
|
|
if (row) {
|
|
await row.dblclick();
|
|
results.success.push("행 더블클릭 시도");
|
|
} else if (await editBtn.count() > 0) {
|
|
results.success.push("수정 버튼 비활성화 - 분할 패널 우측 상세 확인");
|
|
} else {
|
|
results.failed.push("편집 버튼/행을 찾을 수 없음");
|
|
}
|
|
}
|
|
} catch (e) {
|
|
results.success.push("상세 진입 스킵 - 우측 패널에 상세 표시 여부 확인");
|
|
}
|
|
await page.waitForTimeout(5000);
|
|
await screenshot("04_after_detail_enter");
|
|
|
|
// 단계 5: 품목 관련 영역 확인
|
|
console.log("\n=== 단계 5: 품목 관련 영역 확인 ===\n");
|
|
const hasItemSection = await page.getByText(/품목|납품품목|거래처 품번|거래처 품명/).first().count() > 0;
|
|
const hasDetailInput = await page.$('input[placeholder*="품번"], input[name*="품번"], [class*="selected-items"]');
|
|
if (hasItemSection || hasDetailInput) {
|
|
results.success.push("품목 관련 UI 확인됨");
|
|
} else {
|
|
results.failed.push("품목 관련 영역 미확인");
|
|
}
|
|
await screenshot("05_item_section");
|
|
|
|
console.log("\n========== 테스트 결과 ==========\n");
|
|
console.log("성공:", results.success);
|
|
console.log("실패:", results.failed);
|
|
console.log("스크린샷:", results.screenshots);
|
|
|
|
} catch (err) {
|
|
results.failed.push(`예외: ${err.message}`);
|
|
try {
|
|
await page.screenshot({ path: `${SCREENSHOT_DIR}/error.png`, fullPage: true });
|
|
results.screenshots.push(`${SCREENSHOT_DIR}/error.png`);
|
|
} catch (_) {}
|
|
console.error(err);
|
|
} finally {
|
|
await browser.close();
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
runTest()
|
|
.then((r) => process.exit(r.failed.length > 0 ? 1 : 0))
|
|
.catch((e) => {
|
|
console.error(e);
|
|
process.exit(1);
|
|
});
|