eae0a68f06
Key changes: - Add @rendermode InteractiveWebAssembly to Dashboard.razor (CLAUDE.md mandates Interactive WebAssembly as default) - Reorder MapRazorComponents: WebAssembly first, then Server - Simplify login.html: just redirect after 2s (no fetch verification) Root cause of 302 redirect loop: - Dashboard was rendering server-side (no @rendermode specified) - Server-side rendering can't access localStorage - CustomAuthenticationStateProvider read empty token - Dashboard redirected to /login - Result: 302 loop Solution: Force client-side WASM rendering so: 1. Blazor WASM loads in browser 2. CustomAuthenticationStateProvider accesses localStorage 3. Token is read from localStorage 4. /api/auth/me validates token 5. User is authenticated 6. Dashboard displays Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
128 lines
4.7 KiB
JavaScript
128 lines
4.7 KiB
JavaScript
import { chromium } from "@playwright/test";
|
|
|
|
(async () => {
|
|
console.log("════════════════════════════════════════════════════════");
|
|
console.log(" 🔐 COMPLETE LOGIN FLOW TEST");
|
|
console.log("════════════════════════════════════════════════════════\n");
|
|
|
|
const b = await chromium.launch({ headless: false });
|
|
const p = await b.newPage();
|
|
|
|
// 모든 콘솔 로그 캡처
|
|
const consoleLogs = [];
|
|
p.on("console", msg => {
|
|
const text = msg.text();
|
|
consoleLogs.push(text);
|
|
if (text.includes("[Login]") || text.includes("[Dashboard]") || text.includes("[Auth]")) {
|
|
console.log(` 📝 ${text}`);
|
|
}
|
|
});
|
|
|
|
// 요청/응답 모니터링
|
|
p.on("response", res => {
|
|
if (res.url().includes("auth") || res.url().includes("dashboard")) {
|
|
console.log(` 📡 ${res.status()} ${res.url().split('/').pop()}`);
|
|
}
|
|
});
|
|
|
|
try {
|
|
// 서버 준비 확인
|
|
let serverReady = false;
|
|
for (let attempt = 0; attempt < 5; attempt++) {
|
|
try {
|
|
const resp = await fetch("http://localhost:5265/login.html");
|
|
if (resp.ok) {
|
|
serverReady = true;
|
|
break;
|
|
}
|
|
} catch (e) {}
|
|
console.log(` [대기] 서버 시작 확인 중... (${attempt + 1}/5)`);
|
|
await new Promise(r => setTimeout(r, 5000));
|
|
}
|
|
|
|
if (!serverReady) {
|
|
console.log(" ❌ 서버가 시작되지 않음");
|
|
await b.close();
|
|
return;
|
|
}
|
|
|
|
console.log("\n✅ 서버 준비 완료!\n");
|
|
|
|
// STEP 1: 로그인 페이지 로드
|
|
console.log("1️⃣ 로그인 페이지 로드");
|
|
await p.goto("http://localhost:5265/login.html", { waitUntil: "networkidle" });
|
|
console.log(" ✓ 페이지 로드됨\n");
|
|
|
|
// STEP 2: 폼 입력
|
|
console.log("2️⃣ 로그인 폼 입력 (admin/admin)");
|
|
await p.fill("input[name='username']", "admin");
|
|
await p.fill("input[name='password']", "admin");
|
|
console.log(" ✓ 입력 완료\n");
|
|
|
|
// STEP 3: 로그인 제출
|
|
console.log("3️⃣ 로그인 버튼 클릭");
|
|
await p.click("button[type='submit']");
|
|
console.log(" ✓ 클릭됨\n");
|
|
|
|
// STEP 4: 상태 모니터링 (10초)
|
|
console.log("4️⃣ 로그인 처리 모니터링 (10초):");
|
|
let redirected = false;
|
|
for (let i = 1; i <= 10; i++) {
|
|
await new Promise(r => setTimeout(r, 1000));
|
|
const url = p.url();
|
|
const title = await p.title();
|
|
|
|
process.stdout.write(` [${i}s] URL: ${url}`);
|
|
|
|
if (!url.includes("login")) {
|
|
console.log(" ✅ REDIRECTED!");
|
|
redirected = true;
|
|
break;
|
|
} else {
|
|
console.log("");
|
|
}
|
|
}
|
|
|
|
console.log("\n5️⃣ 최종 상태:");
|
|
const finalUrl = p.url();
|
|
const finalTitle = await p.title();
|
|
|
|
console.log(` 📍 URL: ${finalUrl}`);
|
|
console.log(` 📄 Page Title: ${finalTitle}`);
|
|
|
|
if (finalUrl.includes("/dashboard")) {
|
|
console.log(" ✅ 대시보드 URL 확인됨!");
|
|
|
|
const content = await p.content();
|
|
if (content.includes("관리자 대시보드")) {
|
|
console.log(" ✅ 대시보드 콘텐츠 확인됨!");
|
|
console.log("\n🎉 로그인 성공! 대시보드 정상 로드!\n");
|
|
} else if (content.includes("Not Found")) {
|
|
console.log(" ❌ Not Found 에러");
|
|
} else {
|
|
console.log(" ⚠️ 대시보드 콘텐츠 미확인");
|
|
}
|
|
} else if (finalUrl.includes("/login")) {
|
|
console.log(" ❌ 다시 로그인 페이지로 리다이렉트됨");
|
|
console.log(" → 대시보드 인증 체크에서 실패한 것 같습니다");
|
|
} else if (finalUrl.includes("/not-found")) {
|
|
console.log(" ❌ /not-found 에러");
|
|
} else {
|
|
console.log(" ⚠️ 예상치 못한 페이지");
|
|
}
|
|
|
|
// 스크린샷
|
|
await p.screenshot({ path: "./direct-test-result.png", fullPage: true });
|
|
console.log(" 📷 스크린샷: direct-test-result.png");
|
|
|
|
console.log("\n════════════════════════════════════════════════════════");
|
|
console.log(" 테스트 완료");
|
|
console.log("════════════════════════════════════════════════════════");
|
|
|
|
} catch (e) {
|
|
console.error("❌ 테스트 에러:", e.message);
|
|
} finally {
|
|
await b.close();
|
|
}
|
|
})();
|