250 lines
8.4 KiB
JavaScript
250 lines
8.4 KiB
JavaScript
|
|
/**
|
||
|
|
* 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);
|
||
|
|
});
|