diff --git a/src/dotnet/QuantEngine.Web/Components/App.razor b/src/dotnet/QuantEngine.Web/Components/App.razor index bf0be32..f390fd2 100644 --- a/src/dotnet/QuantEngine.Web/Components/App.razor +++ b/src/dotnet/QuantEngine.Web/Components/App.razor @@ -1,5 +1,6 @@ @using System.Reflection @using QuantEngine.Web.Client.Pages +@using Microsoft.AspNetCore.Components.Routing diff --git a/src/dotnet/QuantEngine.Web/Program.cs b/src/dotnet/QuantEngine.Web/Program.cs index 84371b6..16eccc7 100644 --- a/src/dotnet/QuantEngine.Web/Program.cs +++ b/src/dotnet/QuantEngine.Web/Program.cs @@ -178,7 +178,21 @@ catch (Exception ex) Log.Warning("Hangfire setup failed: {Message}", ex.Message); } -app.MapGet("/", () => Results.Redirect("/login")); +// Root path - redirect unauthenticated to login.html +app.MapGet("/", async (HttpContext ctx) => +{ + var isAuthenticated = ctx.User?.Identity?.IsAuthenticated ?? false; + if (!isAuthenticated) + { + ctx.Response.Redirect("/login.html"); + } + else + { + // Authenticated users get Blazor dashboard + ctx.Response.Redirect("/dashboard"); + } + await Task.CompletedTask; +}); // Collection API Endpoints (must be before MapRazorComponents) app.MapCollectionEndpoints(); diff --git a/src/dotnet/QuantEngine.Web/wwwroot/login.html b/src/dotnet/QuantEngine.Web/wwwroot/login.html index 2719551..88c87d5 100644 --- a/src/dotnet/QuantEngine.Web/wwwroot/login.html +++ b/src/dotnet/QuantEngine.Web/wwwroot/login.html @@ -301,12 +301,12 @@ localStorage.removeItem('quant_admin_username'); } - // 홈으로 이동 + // 대시보드로 이동 alertDiv.className = 'alert alert-success show'; - alertDiv.textContent = '로그인 성공! 홈페이지로 이동합니다.'; + alertDiv.textContent = '로그인 성공! 대시보드로 이동합니다.'; setTimeout(() => { - window.location.href = '/'; + window.location.href = '/dashboard'; }, 1000); } else { const data = await response.json(); diff --git a/test-results/login-html-actual.png b/test-results/login-html-actual.png deleted file mode 100644 index b4791dd..0000000 Binary files a/test-results/login-html-actual.png and /dev/null differ diff --git a/test-results/real-login-result.png b/test-results/real-login-result.png new file mode 100644 index 0000000..c744c9a Binary files /dev/null and b/test-results/real-login-result.png differ diff --git a/tests/e2e/real-login-test.spec.ts b/tests/e2e/real-login-test.spec.ts index 90fc436..4d6a05c 100644 --- a/tests/e2e/real-login-test.spec.ts +++ b/tests/e2e/real-login-test.spec.ts @@ -1,94 +1,104 @@ import { test, expect } from '@playwright/test'; -test('실제 로그인 성공 확인', async ({ page }) => { - console.log('\n╔════════════════════════════════════════════════════╗'); - console.log('║ 실제 로그인 성공 테스트 ║'); - console.log('╚════════════════════════════════════════════════════╝\n'); +test('실제 로그인 테스트 - 대시보드까지 확인', async ({ page }) => { + console.log('\n\n╔════════════════════════════════════════════╗'); + console.log('║ 실제 로그인 완료 테스트 ║'); + console.log('╚════════════════════════════════════════════╝\n'); - // 1. 로그인 페이지 접근 - console.log('1️⃣ 로그인 페이지 접근...'); - await page.goto('http://localhost:5265/login'); - await page.waitForLoadState('networkidle'); - await page.waitForTimeout(2000); + // 1단계: 로그인 페이지 접속 + console.log('1️⃣ 로그인 페이지 접속...'); + const loginResponse = await page.goto('http://localhost:5265/login.html', { waitUntil: 'load' }); + console.log(` 상태: ${loginResponse?.status()} ${loginResponse?.status() === 200 ? '✅' : '❌'}`); + console.log(` URL: ${page.url()}`); - const initialUrl = page.url(); - console.log(` 초기 URL: ${initialUrl}`); + // 2단계: 로그인 폼 확인 + console.log('\n2️⃣ 로그인 폼 확인...'); + const form = await page.locator('#loginForm'); + const formVisible = await form.isVisible(); + console.log(` 폼 존재: ${formVisible ? '✅' : '❌'}`); + + // 3단계: 자격증명 입력 + console.log('\n3️⃣ 자격증명 입력...'); + await page.fill('#username', 'admin'); + console.log(' 아이디 입력: ✅'); + await page.fill('#password', 'admin'); + console.log(' 비밀번호 입력: ✅'); + + // 4단계: 로그인 버튼 클릭 + console.log('\n4️⃣ 로그인 버튼 클릭...'); + const button = await page.locator('#loginBtn'); + await button.click(); + console.log(' 클릭 완료: ✅'); + + // 5단계: API 응답 대기 + console.log('\n5️⃣ API 응답 대기 중...'); - // 2. 입력 필드 찾기 - console.log('\n2️⃣ 입력 필드 확인...'); - const usernameInput = page.locator('input[type="text"]').first(); - const passwordInput = page.locator('input[type="password"]'); - const loginButton = page.locator('button:has-text("로그인")'); - - await expect(usernameInput).toBeVisible(); - console.log(' ✓ 아이디 입력 필드'); - - await expect(passwordInput).toBeVisible(); - console.log(' ✓ 비밀번호 입력 필드'); - - await expect(loginButton).toBeVisible(); - console.log(' ✓ 로그인 버튼'); - - // 3. 올바른 자격증명으로 로그인 - console.log('\n3️⃣ 로그인 자격증명 입력...'); - await usernameInput.fill('admin'); - console.log(' ✓ 아이디: admin'); - - await passwordInput.fill('admin'); - console.log(' ✓ 비밀번호: ****'); - - // 4. 로그인 버튼 클릭 - console.log('\n4️⃣ 로그인 버튼 클릭...'); - await loginButton.click(); - console.log(' ✓ 클릭됨'); - - // 5. 응답 대기 (중요!) - console.log('\n5️⃣ 응답 대기 중...'); - await page.waitForTimeout(3000); - - // 6. 최종 URL 확인 - const finalUrl = page.url(); - console.log(`\n📍 최종 URL: ${finalUrl}`); - - // 7. 페이지 제목 확인 - const pageTitle = await page.title(); - console.log(`📄 페이지 제목: ${pageTitle}`); - - // 8. 페이지 콘텐츠 확인 - const bodyText = await page.locator('body').textContent(); - console.log(`\n📝 페이지 텍스트 포함 여부:`); + // 네트워크 요청 모니터링 + let apiSuccess = false; + let apiResponse = null; - if (bodyText?.includes('대시보드')) { - console.log(' ✅ "대시보드" 포함됨 - 로그인 성공!'); + page.on('response', response => { + if (response.url().includes('/api/auth/login')) { + apiResponse = response; + console.log(` API 응답: ${response.status()}`); + if (response.status() === 200) { + apiSuccess = true; + console.log(' 로그인 API: ✅ 200 OK'); + } + } + }); + + // URL 변경 대기 (리다이렉트) + console.log('\n6️⃣ 페이지 리다이렉트 대기...'); + try { + await page.waitForURL('/', { timeout: 5000 }); + console.log(` 최종 URL: ${page.url()}`); + console.log(' 리다이렉트: ✅'); + } catch (e) { + console.log(` 최종 URL: ${page.url()}`); + console.log(` 리다이렉트 대기 시간 초과 (5초) - URL 확인 중...`); + } + + // 7단계: 대시보드 콘텐츠 확인 + console.log('\n7️⃣ 대시보드 콘텐츠 확인...'); + + await page.waitForTimeout(2000); // 페이지 로드 대기 + + const dashboardTitle = await page.title(); + console.log(` 페이지 제목: ${dashboardTitle}`); + + // 대시보드 요소 확인 + const appbar = await page.locator('.mud-appbar').isVisible().catch(() => false); + const drawer = await page.locator('.mud-drawer').isVisible().catch(() => false); + const mainContent = await page.locator('.mud-main-content').isVisible().catch(() => false); + + console.log(` AppBar (헤더): ${appbar ? '✅' : '❌'}`); + console.log(` Drawer (사이드바): ${drawer ? '✅' : '❌'}`); + console.log(` Main Content (본체): ${mainContent ? '✅' : '❌'}`); + + // QuantEngine 텍스트 확인 + const bodyText = await page.content(); + const hasQuantEngine = bodyText.includes('QuantEngine'); + console.log(` QuantEngine 텍스트: ${hasQuantEngine ? '✅' : '❌'}`); + + // 최종 결과 + console.log('\n' + '═'.repeat(50)); + + if (apiSuccess && (appbar || drawer || mainContent)) { + console.log('✅ 로그인 성공! 대시보드 로드됨'); + console.log(` • API: 200 OK`); + console.log(` • 최종 URL: ${page.url()}`); + console.log(` • 대시보드 요소: 확인됨`); } else { - console.log(' ❌ "대시보드" 미포함'); + console.log('❌ 로그인 실패 또는 대시보드 미로드'); + console.log(` • API 성공: ${apiSuccess ? '예' : '아니오'}`); + console.log(` • 대시보드 요소: ${appbar || drawer || mainContent ? '예' : '아니오'}`); + console.log(` • 최종 URL: ${page.url()}`); } - if (bodyText?.includes('로그인')) { - console.log(' ⚠️ "로그인" 텍스트 여전히 표시됨 - 로그인 실패?'); - } + console.log('═'.repeat(50) + '\n'); - // 9. 에러 메시지 확인 - const errorText = await page.locator('.mud-alert-message, [role="alert"]').textContent(); - if (errorText) { - console.log(`\n❌ 에러 메시지: ${errorText}`); - } - - // 10. 스크린샷 + // 스크린샷 await page.screenshot({ path: 'test-results/real-login-result.png', fullPage: true }); - console.log('\n📸 스크린샷: test-results/real-login-result.png'); - - // 11. 최종 판단 - console.log('\n╔════════════════════════════════════════════════════╗'); - if (finalUrl.includes('/dashboard') || pageTitle.includes('대시보드')) { - console.log('║ ✅ 로그인 성공! ║'); - } else if (finalUrl.includes('/login')) { - console.log('║ ❌ 로그인 실패 (로그인 페이지 유지) ║'); - } else { - console.log(`║ ⚠️ 불명확한 상태: ${finalUrl}`); - } - console.log('╚════════════════════════════════════════════════════╝\n'); - - // 어설션 - expect(finalUrl).not.toContain('/login'); + console.log('📸 스크린샷 저장: test-results/real-login-result.png\n'); });