Files
taxbaik/tests/e2e/login-test.spec.ts
T
kjh2064 e5981769b9
TaxBaik CI/CD / build-and-deploy (push) Successful in 2m11s
fix: per-page WASM render mode, Contact checkbox binding, Telegram inquiry channel
- Admin: replace the global @rendermode on <Routes>/<Router> with per-page
  render mode. Login.razor now prerenders (form visible before WASM loads);
  every other [Authorize] page stays prerender: false to avoid the
  AuthorizeRouteView blank-render regression from earlier attempts. Adds a
  "준비 중" -> "로그인" splash tied to WASM boot completion, and lets the
  authenticated-shell loading overlay stay up until AdminShell actually renders.
- Contact.cshtml: fix the "Agree" checkbox missing value="true" - a checked
  box sent the browser-default "on", which bool model binding can't parse,
  so ModelState.IsValid silently went false and OnPostAsync returned a blank
  form with no visible error on every submission. Validation summary widened
  from ModelOnly to All so this class of failure isn't silent again.
- TelegramInquiryNotificationService: read Telegram:InquiryChatId (falling
  back to ChatId) instead of only ChatId, matching the channel routing
  CLAUDE.md documents and deploy.yml already provisions as separate secrets.
- Reconcile CLAUDE.md's self-contradicting Phase 8 prerender notes (Phase 9),
  rewrite validate_admin_render.sh for the per-page design, and add a
  SmartAdmin 5.5 design reference section to DOUZONE_UX_GUIDE.md for future
  admin screens (existing screens unchanged, tracked as WBS P4-03).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-03 10:15:27 +09:00

101 lines
4.6 KiB
TypeScript

import { expect, test } from '@playwright/test';
const baseUrl = 'https://www.taxbaik.com/taxbaik';
const username = 'test_admin';
const password = 'TestAdmin@123456';
test('Admin Login Page - prerendered HTML contains the form before JS runs', async ({ request }) => {
// Login.razor is @rendermode ...(prerender: true), so the raw HTTP response
// (no JS execution) must already contain the login form markup. This is the
// regression check for the WASM-boot white-screen bug: if Router/Routes ever
// regains a global @rendermode, this prerender is silently dropped and this
// test catches it without needing a browser.
const response = await request.get(`${baseUrl}/admin/login`);
expect(response.ok()).toBeTruthy();
const html = await response.text();
expect(html).toContain('name="username"');
expect(html).toContain('name="password"');
expect(html).toContain('admin-login-form');
});
test('Admin Login Page - Full Flow Test', async ({ page }) => {
// 콘솔 에러 캡처
page.on('console', msg => {
if (msg.type() === 'error') console.log('🔴 CONSOLE ERROR:', msg.text());
});
page.on('pageerror', err => console.log('🔴 PAGE ERROR:', err.message));
page.on('response', res => {
if (!res.ok() && res.status() !== 304)
console.log('🔴 HTTP ERROR:', res.status(), res.url());
});
console.log('\n=== 1단계: 로그인 페이지 방문 ===');
await page.goto(`${baseUrl}/admin/login`, { waitUntil: 'networkidle', timeout: 30000 });
const pageTitle = await page.title();
console.log('✅ 페이지 제목:', pageTitle);
// 페이지 콘텐츠 확인
const pageContent = await page.content();
console.log('✅ 페이지 길이:', pageContent.length, '바이트');
console.log('\n=== 2단계: 로그인 필드 확인 ===');
// 사용자명 입력 필드 찾기
const usernameField = await page.locator('input[type="text"], input[placeholder*="사용자"], input[placeholder*="이름"]').first();
const usernameVisible = await usernameField.isVisible().catch(() => false);
console.log(`✅ 사용자명 필드: ${usernameVisible ? '보임 ✨' : '안 보임 ❌'}`);
// 비밀번호 입력 필드 찾기
const passwordField = await page.locator('input[type="password"], input[placeholder*="비밀"], input[placeholder*="암호"]').first();
const passwordVisible = await passwordField.isVisible().catch(() => false);
console.log(`✅ 비밀번호 필드: ${passwordVisible ? '보임 ✨' : '안 보임 ❌'}`);
// 로그인 버튼 찾기
const loginButton = await page.locator('button:has-text("로그인"), button:has-text("로그인하기"), input[type="submit"]').first();
const loginButtonVisible = await loginButton.isVisible().catch(() => false);
console.log(`✅ 로그인 버튼: ${loginButtonVisible ? '보임 ✨' : '안 보임 ❌'}`);
console.log('\n=== 3단계: 로그인 입력 & 제출 ===');
if (usernameVisible && passwordVisible && loginButtonVisible) {
await usernameField.fill(username);
console.log(`✅ 사용자명 입력: ${username}`);
await passwordField.fill(password);
console.log(`✅ 비밀번호 입력: ••••••••`);
await loginButton.click();
console.log('✅ 로그인 버튼 클릭');
console.log('\n=== 4단계: 로그인 결과 대기 ===');
await page.waitForNavigation({ waitUntil: 'networkidle', timeout: 15000 }).catch(() => {
console.log('⚠️ 네비게이션 타임아웃 (이미 로그인됨 또는 리다이렉트 중)');
});
const finalUrl = page.url();
console.log('✅ 최종 URL:', finalUrl);
if (finalUrl.includes('/admin/dashboard') || finalUrl.includes('/admin')) {
console.log('✅ 로그인 성공! 대시보드로 이동됨');
} else {
console.log('⚠️ 예상과 다른 URL');
}
} else {
console.log('❌ 필수 필드가 렌더링되지 않음');
console.log('📋 페이지 구조 분석:');
console.log(pageContent.substring(0, 2000));
}
console.log('\n=== 5단계: 스크린샷 ===');
await page.screenshot({ path: 'test-results/login-page.png' });
console.log('✅ 스크린샷 저장: test-results/login-page.png');
console.log('\n=== 🎯 테스트 결과 ===');
console.log(`✅ 페이지 로드: 성공`);
console.log(`${usernameVisible ? '✅' : '❌'} 사용자명 필드: ${usernameVisible ? '렌더링됨' : '미렌더링'}`);
console.log(`${passwordVisible ? '✅' : '❌'} 비밀번호 필드: ${passwordVisible ? '렌더링됨' : '미렌더링'}`);
console.log(`${loginButtonVisible ? '✅' : '❌'} 로그인 버튼: ${loginButtonVisible ? '렌더링됨' : '미렌더링'}`);
});