- 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.
197 lines
6.8 KiB
JavaScript
197 lines
6.8 KiB
JavaScript
/**
|
|
* 구매관리 - 공급업체관리 / 구매품목정보 CRUD 브라우저 테스트
|
|
* 실행: node scripts/browser-test-purchase-supplier.js
|
|
* 브라우저 표시: HEADLESS=0 node scripts/browser-test-purchase-supplier.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}`);
|
|
return path;
|
|
};
|
|
|
|
const clickMenu = async (text) => {
|
|
const loc = page.getByText(text, { exact: true }).first();
|
|
if ((await loc.count()) > 0) {
|
|
await loc.click();
|
|
return true;
|
|
}
|
|
const alt = page.getByRole("link", { name: text }).or(page.locator(`a:has-text("${text}")`)).first();
|
|
if ((await alt.count()) > 0) {
|
|
await alt.click();
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
const clickRow = async () => {
|
|
const rows = await page.$$('tbody tr, table tr, [role="row"]');
|
|
for (const r of rows) {
|
|
const t = await r.textContent();
|
|
if (t && !t.includes("데이터가 없습니다") && !t.includes("로딩")) {
|
|
await r.click();
|
|
return true;
|
|
}
|
|
}
|
|
if (rows.length > 0) {
|
|
await rows[0].click();
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
const clickButton = async (regex) => {
|
|
const btn = page.locator("button").filter({ hasText: regex }).first();
|
|
try {
|
|
if ((await btn.count()) > 0 && !(await btn.isDisabled())) {
|
|
await btn.click();
|
|
return true;
|
|
}
|
|
} catch (_) {}
|
|
return false;
|
|
};
|
|
|
|
try {
|
|
console.log("\n=== 로그인 확인 ===\n");
|
|
await page.goto(`${BASE_URL}/login`, { waitUntil: "networkidle", timeout: 15000 });
|
|
if (page.url().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");
|
|
|
|
console.log("단계 1: 구매관리 메뉴 열기");
|
|
if (await clickMenu("구매관리")) {
|
|
await page.waitForTimeout(3000);
|
|
results.success.push("구매관리 메뉴 클릭");
|
|
} else {
|
|
results.failed.push("구매관리 메뉴 미발견");
|
|
}
|
|
await screenshot("p1_01_purchase_menu");
|
|
|
|
console.log("단계 2: 공급업체관리 서브메뉴 클릭");
|
|
if (await clickMenu("공급업체관리")) {
|
|
await page.waitForTimeout(8000);
|
|
results.success.push("공급업체관리 메뉴 클릭");
|
|
} else {
|
|
results.failed.push("공급업체관리 메뉴 미발견");
|
|
}
|
|
await screenshot("p1_02_supplier_screen");
|
|
|
|
console.log("단계 3: 공급업체 선택");
|
|
if (await clickRow()) {
|
|
await page.waitForTimeout(5000);
|
|
results.success.push("공급업체 행 클릭");
|
|
} else {
|
|
results.failed.push("공급업체 테이블 행 미발견");
|
|
}
|
|
await screenshot("p1_03_after_supplier_select");
|
|
|
|
console.log("단계 4: 납품품목 탭/영역 확인");
|
|
const itemTab = page.getByText(/납품품목|품목/).first();
|
|
if ((await itemTab.count()) > 0) {
|
|
await itemTab.click();
|
|
await page.waitForTimeout(3000);
|
|
results.success.push("납품품목/품목 탭 클릭");
|
|
} else {
|
|
results.failed.push("납품품목 탭 미발견");
|
|
}
|
|
await screenshot("p1_04_item_tab");
|
|
|
|
console.log("단계 5: 품목 추가 시도");
|
|
const addBtn = page.locator("button").filter({ hasText: /추가|\+ 추가/ }).first();
|
|
let addBtnEnabled = false;
|
|
try {
|
|
addBtnEnabled = (await addBtn.count()) > 0 && !(await addBtn.isDisabled());
|
|
} catch (_) {}
|
|
if (addBtnEnabled) {
|
|
await addBtn.click();
|
|
await page.waitForTimeout(2000);
|
|
const modal = await page.$('[role="dialog"], .modal, [class*="modal"]');
|
|
if (modal) {
|
|
const modalRow = await page.$('[role="dialog"] tbody tr, .modal tbody tr');
|
|
if (modalRow) {
|
|
await modalRow.click();
|
|
await page.waitForTimeout(1500);
|
|
}
|
|
}
|
|
await page.waitForTimeout(1500);
|
|
results.success.push("추가 버튼 클릭 및 품목 선택 시도");
|
|
} else {
|
|
results.failed.push("추가 버튼 미발견 또는 비활성화");
|
|
}
|
|
await screenshot("p1_05_add_item");
|
|
|
|
// ========== 테스트 2: 구매품목정보 ==========
|
|
console.log("\n=== 테스트 2: 구매품목정보 ===\n");
|
|
|
|
console.log("단계 6: 구매품목정보 메뉴 클릭");
|
|
if (await clickMenu("구매품목정보")) {
|
|
await page.waitForTimeout(8000);
|
|
results.success.push("구매품목정보 메뉴 클릭");
|
|
} else {
|
|
results.failed.push("구매품목정보 메뉴 미발견");
|
|
}
|
|
await screenshot("p2_01_item_screen");
|
|
|
|
console.log("단계 7: 품목 선택 및 공급업체 확인");
|
|
if (await clickRow()) {
|
|
await page.waitForTimeout(5000);
|
|
results.success.push("구매품목 행 클릭");
|
|
} else {
|
|
results.failed.push("구매품목 테이블 행 미발견");
|
|
}
|
|
await screenshot("p2_02_after_item_select");
|
|
|
|
// SelectedItemsDetailInput 컴포넌트 확인
|
|
const hasDetailInput = await page.$('input[placeholder*="품번"], [class*="selected-items"], input[name*="품번"]');
|
|
results.success.push(hasDetailInput ? "SelectedItemsDetailInput 렌더링 확인" : "SelectedItemsDetailInput 미확인");
|
|
await screenshot("p2_03_final");
|
|
|
|
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);
|
|
});
|