diff --git a/full-login-test.mjs b/full-login-test.mjs new file mode 100644 index 0000000..81ad882 --- /dev/null +++ b/full-login-test.mjs @@ -0,0 +1,95 @@ +import { chromium } from "@playwright/test"; + +(async () => { + console.log("=== FULL LOGIN FLOW TEST WITH DETAILED LOGGING ===\n"); + + const b = await chromium.launch({ + headless: false, // 브라우저 화면 표시 + args: ["--disable-blink-features=AutomationControlled"] + }); + + const p = await b.newPage(); + + // 모든 콘솔 메시지 캡처 + p.on("console", msg => { + const type = msg.type(); + const text = msg.text(); + console.log(` [BROWSER-${type.toUpperCase()}] ${text}`); + }); + + // 모든 요청/응답 로그 + p.on("request", req => { + if (req.url().includes("auth")) { + console.log(` [REQUEST] ${req.method()} ${req.url()}`); + } + }); + + p.on("response", res => { + if (res.url().includes("auth")) { + console.log(` [RESPONSE] ${res.status()} ${res.url()}`); + } + }); + + try { + console.log("1️⃣ STEP 1: Loading login page..."); + await p.goto("http://localhost:5265/login.html", { waitUntil: "networkidle" }); + console.log(" ✓ Page loaded\n"); + + console.log("2️⃣ STEP 2: Filling form (admin/admin)..."); + const userInput = await p.$("input[name='username']"); + if (!userInput) { + console.log(" ✗ Username input NOT FOUND"); + console.log(" Page content snippet:"); + const html = await p.content(); + const snippet = html.substring(0, 500); + console.log(snippet); + } else { + await p.fill("input[name='username']", "admin"); + await p.fill("input[name='password']", "admin"); + console.log(" ✓ Form filled\n"); + + console.log("3️⃣ STEP 3: Clicking login button..."); + await p.click("button[type='submit']"); + console.log(" ✓ Button clicked\n"); + + console.log("4️⃣ STEP 4: Waiting 7 seconds for auth flow..."); + for (let i = 1; i <= 7; i++) { + await new Promise(r => setTimeout(r, 1000)); + const url = p.url(); + console.log(` [${i}s] Current URL: ${url}`); + } + + console.log("\n5️⃣ FINAL RESULT:"); + const finalUrl = p.url(); + const finalContent = await p.content(); + + console.log(` URL: ${finalUrl}`); + + if (finalUrl.includes("/dashboard")) { + if (finalContent.includes("관리자 대시보드")) { + console.log(" ✓✓✓ SUCCESS! Dashboard loaded with content!"); + } else if (finalContent.includes("Not Found")) { + console.log(" ✗ Dashboard URL but 'Not Found' error"); + } else { + console.log(" ✓ Dashboard page (content varies)"); + } + } else if (finalUrl.includes("/not-found")) { + console.log(" ✗ FAILED: Redirected to /not-found"); + console.log(" This means authentication failed"); + } else if (finalUrl.includes("/login")) { + console.log(" ✗ Back at login page"); + } else { + console.log(" ? Other page"); + } + + // 스크린샷 저장 + await p.screenshot({ path: "./playwright-test-result.png", fullPage: true }); + console.log("\n📷 Screenshot saved: playwright-test-result.png"); + } + + } catch (e) { + console.error("❌ Error:", e.message); + } finally { + await b.close(); + } +})(); diff --git a/playwright-test-result.png b/playwright-test-result.png new file mode 100644 index 0000000..507c623 Binary files /dev/null and b/playwright-test-result.png differ diff --git a/simple-test.mjs b/simple-test.mjs new file mode 100644 index 0000000..7f5adf4 --- /dev/null +++ b/simple-test.mjs @@ -0,0 +1,45 @@ +import { chromium } from "@playwright/test"; + +(async () => { + console.log("=== SIMPLE DIRECT TEST ===\n"); + + const b = await chromium.launch({ headless: false }); + const p = await b.newPage(); + + // 모든 콘솔 로그 출력 + p.on("console", msg => console.log(` [${msg.type()}] ${msg.text()}`)); + + try { + console.log("1. Navigate to login..."); + // URL에 타임스탐프 추가 (캐시 무시) + await p.goto("http://localhost:5265/login.html?v=" + Date.now()); + + console.log("2. Submit form..."); + await p.fill("input[name='username']", "admin"); + await p.fill("input[name='password']", "admin"); + + // Before submit - 현재 URL + console.log(" URL before submit: " + p.url()); + + await p.click("button[type='submit']"); + + // 8초 동안 URL 변화 감시 + console.log("3. Monitoring for 8 seconds..."); + let lastUrl = ""; + for (let i = 0; i < 8; i++) { + await new Promise(r => setTimeout(r, 1000)); + const currentUrl = p.url(); + if (currentUrl !== lastUrl) { + console.log(` [${i+1}s] ➜ ${currentUrl}`); + lastUrl = currentUrl; + } + } + + console.log("\n4. RESULT: " + p.url()); + + } catch (e) { + console.error("Error:", e.message); + } + + await b.close(); +})(); diff --git a/src/dotnet/QuantEngine.Web/Client/Pages/Dashboard.razor b/src/dotnet/QuantEngine.Web/Client/Pages/Dashboard.razor index 2d5e9fa..3e28bfc 100644 --- a/src/dotnet/QuantEngine.Web/Client/Pages/Dashboard.razor +++ b/src/dotnet/QuantEngine.Web/Client/Pages/Dashboard.razor @@ -1,7 +1,9 @@ @page "/dashboard" -@attribute [Authorize] @using QuantEngine.Core.Infrastructure +@using Microsoft.AspNetCore.Components.Authorization @inject HttpClient Http +@inject AuthenticationStateProvider AuthStateProvider +@inject NavigationManager NavManager QuantEngine - Admin Dashboard @@ -240,6 +242,15 @@ protected override async Task OnInitializedAsync() { + // Check authentication + var authState = await AuthStateProvider.GetAuthenticationStateAsync(); + if (!authState.User.Identity?.IsAuthenticated ?? true) + { + // Not authenticated - redirect to login + NavManager.NavigateTo("/login.html", forceLoad: true); + return; + } + try { // Load operational report diff --git a/src/dotnet/QuantEngine.Web/wwwroot/login.html b/src/dotnet/QuantEngine.Web/wwwroot/login.html index 1584fbe..760b82d 100644 --- a/src/dotnet/QuantEngine.Web/wwwroot/login.html +++ b/src/dotnet/QuantEngine.Web/wwwroot/login.html @@ -324,14 +324,13 @@ // 로그인 성공 메시지 표시 alertDiv.className = 'alert alert-success show'; - alertDiv.textContent = '로그인 성공! 대시보드로 이동합니다. (Blazor 초기화 중...)'; + alertDiv.textContent = '로그인 성공! 대시보드로 이동합니다.'; - // Blazor 앱이 로드되고 localStorage에서 토큰을 읽을 시간을 충분히 제공 - // 3초 대기 후 대시보드로 이동 - console.log('[Login] Waiting 3 seconds for Blazor to initialize...'); + // Blazor 앱이 로드될 시간 제공 + // (대시보드는 이제 [Authorize] 없이 로드되고, 내부에서 인증 체크) + console.log('[Login] Token saved. Redirecting in 3 seconds...'); setTimeout(() => { - console.log('[Login] Redirecting to dashboard'); - console.log('[Login] Token in localStorage:', localStorage.getItem('quant_admin_access_token') ? 'YES' : 'NO'); + console.log('[Login] Navigating to dashboard'); window.location.href = '/dashboard'; }, 3000); } else { diff --git a/test-no-cache.mjs b/test-no-cache.mjs new file mode 100644 index 0000000..48ecf0f --- /dev/null +++ b/test-no-cache.mjs @@ -0,0 +1,58 @@ +import { chromium } from "@playwright/test"; + +(async () => { + console.log("=== TEST WITH CACHE BYPASS ===\n"); + + const b = await chromium.launch({ headless: false }); + const ctx = await b.createIncognitoBrowserContext(); // Private mode = no cache + const p = await ctx.newPage(); + + p.on("console", msg => { + if (msg.text().includes("[Login]")) { + console.log(` ✓ ${msg.text()}`); + } + }); + + try { + // 캐시 무시하고 로드 + console.log("1. Loading login page (no cache)..."); + await p.goto("http://localhost:5265/login.html?nocache=" + Date.now(), { + waitUntil: "networkidle" + }); + console.log(" ✓ Loaded\n"); + + console.log("2. Submitting login..."); + await p.fill("input[name='username']", "admin"); + await p.fill("input[name='password']", "admin"); + await p.click("button[type='submit']"); + console.log(" ✓ Submitted\n"); + + console.log("3. Waiting 8 seconds..."); + for (let i = 1; i <= 8; i++) { + await new Promise(r => setTimeout(r, 1000)); + const url = p.url(); + process.stdout.write(` [${i}s] ${url}\r`); + } + + console.log("\n\n4. FINAL CHECK:"); + const finalUrl = p.url(); + if (finalUrl.includes("/dashboard")) { + console.log(" ✓✓✓ SUCCESS: Dashboard loaded!"); + const content = await p.content(); + if (content.includes("관리자 대시보드")) { + console.log(" ✓ Dashboard content confirmed!"); + } + } else if (finalUrl.includes("/not-found")) { + console.log(" ✗ /not-found (auth failed)"); + } else { + console.log(" URL: " + finalUrl); + } + + await p.screenshot({ path: "./final-test-no-cache.png", fullPage: true }); + + } catch (e) { + console.error("Error:", e.message); + } finally { + await b.close(); + } +})();