diff --git a/src/dotnet/QuantEngine.Web/wwwroot/app.css b/src/dotnet/QuantEngine.Web/wwwroot/app.css index a743fcc..733a42b 100644 --- a/src/dotnet/QuantEngine.Web/wwwroot/app.css +++ b/src/dotnet/QuantEngine.Web/wwwroot/app.css @@ -295,3 +295,83 @@ body { background: white; } } + +/* ============================================= + 로그인 페이지 MudTextField 스타일 개선 + ============================================= */ + +/* MudTextField 입력 필드 */ +.mud-input-slot { + color: #ffffff !important; + background-color: rgba(255, 255, 255, 0.08) !important; +} + +.mud-input-slot::placeholder { + color: rgba(255, 255, 255, 0.5) !important; +} + +.mud-input-slot:focus { + background-color: rgba(255, 255, 255, 0.12) !important; + border-color: rgba(63, 81, 181, 0.8) !important; + color: #ffffff !important; +} + +/* MudTextField 라벨 */ +.mud-input-label { + color: rgba(255, 255, 255, 0.8) !important; +} + +/* MudTextField 아웃라인 */ +.mud-input-outlined { + border-color: rgba(255, 255, 255, 0.3) !important; +} + +.mud-input-outlined:hover { + border-color: rgba(255, 255, 255, 0.5) !important; +} + +.mud-input-outlined:focus-within { + border-color: rgba(63, 81, 181, 0.8) !important; +} + +/* MudCheckBox */ +.mud-button-label { + color: rgba(255, 255, 255, 0.9) !important; +} + +/* MudButton (로그인) */ +.mud-button-filled-primary { + background-color: rgba(63, 81, 181, 0.9) !important; +} + +.mud-button-filled-primary:hover { + background-color: rgba(63, 81, 181, 1) !important; + box-shadow: 0 8px 24px rgba(63, 81, 181, 0.4) !important; +} + +/* MudAlert (에러) */ +.mud-alert { + background-color: rgba(244, 67, 54, 0.15) !important; + color: #ff7675 !important; +} + +/* HTML input 요소 */ +input[type="text"].login-input, +input[type="password"].login-input { + background-color: rgba(255, 255, 255, 0.08) !important; + color: #ffffff !important; + border-color: rgba(255, 255, 255, 0.3) !important; +} + +input[type="text"].login-input::placeholder, +input[type="password"].login-input::placeholder { + color: rgba(255, 255, 255, 0.5) !important; +} + +input[type="text"].login-input:focus, +input[type="password"].login-input:focus { + background-color: rgba(255, 255, 255, 0.12) !important; + border-color: rgba(63, 81, 181, 0.8) !important; + color: #ffffff !important; + outline: none !important; +} diff --git a/test-results/login-card-closeup.png b/test-results/login-card-closeup.png new file mode 100644 index 0000000..5914615 Binary files /dev/null and b/test-results/login-card-closeup.png differ diff --git a/test-results/login-page-full.png b/test-results/login-page-full.png new file mode 100644 index 0000000..e27e623 Binary files /dev/null and b/test-results/login-page-full.png differ diff --git a/tests/e2e/screenshot-diagnosis.spec.ts b/tests/e2e/screenshot-diagnosis.spec.ts new file mode 100644 index 0000000..acff52d --- /dev/null +++ b/tests/e2e/screenshot-diagnosis.spec.ts @@ -0,0 +1,127 @@ +import { test } from '@playwright/test'; + +test('로그인 페이지 스타일 진단 - 스크린샷 캡처', async ({ page }) => { + console.log('\n╔════════════════════════════════════════════════════╗'); + console.log('║ 로그인 페이지 스타일 진단 ║'); + console.log('╚════════════════════════════════════════════════════╝\n'); + + // 1️⃣ 페이지 로드 + console.log('1️⃣ 로그인 페이지 로드 중...'); + await page.goto('http://localhost:5265/login', { waitUntil: 'networkidle' }); + await page.waitForTimeout(2000); + console.log(' ✓ 페이지 로드 완료'); + + // 2️⃣ HTML 요소 확인 + console.log('\n2️⃣ 페이지 요소 확인...'); + + const loginShell = await page.locator('.login-shell').count(); + const loginCard = await page.locator('.login-card').count(); + const inputFields = await page.locator('input[type="text"], input[type="password"]').count(); + const buttons = await page.locator('button:has-text("로그인")').count(); + + console.log(` login-shell: ${loginShell > 0 ? '✅' : '❌'}`); + console.log(` login-card: ${loginCard > 0 ? '✅' : '❌'}`); + console.log(` 입력 필드: ${inputFields}개 ${inputFields >= 2 ? '✅' : '❌'}`); + console.log(` 로그인 버튼: ${buttons > 0 ? '✅' : '❌'}`); + + // 3️⃣ CSS 로드 확인 + console.log('\n3️⃣ CSS 로드 상태...'); + + const stylesheets = await page.evaluate(() => { + const links = Array.from(document.querySelectorAll('link[rel="stylesheet"]')); + return links.map(link => ({ + href: (link as HTMLLinkElement).href, + loaded: (link as HTMLLinkElement).sheet !== null + })); + }); + + stylesheets.forEach(style => { + const filename = style.href.split('/').pop(); + console.log(` ${filename}: ${style.loaded ? '✅' : '❌'}`); + }); + + // 4️⃣ 계산된 스타일 확인 + console.log('\n4️⃣ 로그인 카드 계산된 스타일...'); + + const cardStyle = await page.locator('.login-card').evaluate((el: any) => ({ + display: window.getComputedStyle(el).display, + backgroundColor: window.getComputedStyle(el).backgroundColor, + color: window.getComputedStyle(el).color, + borderRadius: window.getComputedStyle(el).borderRadius, + width: window.getComputedStyle(el).width, + visibility: window.getComputedStyle(el).visibility, + opacity: window.getComputedStyle(el).opacity + })); + + console.log(` Display: ${cardStyle.display}`); + console.log(` BG Color: ${cardStyle.backgroundColor}`); + console.log(` Text Color: ${cardStyle.color}`); + console.log(` Border Radius: ${cardStyle.borderRadius}`); + console.log(` Width: ${cardStyle.width}`); + console.log(` Visibility: ${cardStyle.visibility}`); + console.log(` Opacity: ${cardStyle.opacity}`); + + // 5️⃣ 입력 필드 스타일 + console.log('\n5️⃣ 입력 필드 스타일...'); + + const inputStyle = await page.locator('input[type="text"]').first().evaluate((el: any) => ({ + display: window.getComputedStyle(el).display, + padding: window.getComputedStyle(el).padding, + borderColor: window.getComputedStyle(el).borderColor, + backgroundColor: window.getComputedStyle(el).backgroundColor, + color: window.getComputedStyle(el).color, + fontSize: window.getComputedStyle(el).fontSize, + visibility: window.getComputedStyle(el).visibility + })); + + console.log(` Display: ${inputStyle.display}`); + console.log(` Padding: ${inputStyle.padding}`); + console.log(` Border Color: ${inputStyle.borderColor}`); + console.log(` BG Color: ${inputStyle.backgroundColor}`); + console.log(` Text Color: ${inputStyle.color}`); + console.log(` Font Size: ${inputStyle.fontSize}`); + console.log(` Visibility: ${inputStyle.visibility}`); + + // 6️⃣ 뷰포트 별 스크린샷 + console.log('\n6️⃣ 스크린샷 캡처 중...'); + + // 전체 페이지 스크린샷 + await page.screenshot({ + path: 'test-results/login-page-full.png', + fullPage: true + }); + console.log(' ✅ 전체 페이지: test-results/login-page-full.png'); + + // 로그인 카드만 확대 + const loginCardLocator = page.locator('.login-card').first(); + const box = await loginCardLocator.boundingBox(); + + if (box) { + await page.screenshot({ + path: 'test-results/login-card-closeup.png', + clip: { + x: Math.max(0, box.x - 20), + y: Math.max(0, box.y - 20), + width: box.width + 40, + height: box.height + 40 + } + }); + console.log(' ✅ 로그인 카드 확대: test-results/login-card-closeup.png'); + } + + // 7️⃣ 콘솔 에러 확인 + console.log('\n7️⃣ 브라우저 콘솔 메시지...'); + + page.on('console', msg => { + if (msg.type() === 'error' || msg.type() === 'warning') { + console.log(` [${msg.type().toUpperCase()}] ${msg.text()}`); + } + }); + + await page.waitForTimeout(1000); + + console.log('\n╔════════════════════════════════════════════════════╗'); + console.log('║ 스크린샷 캡처 완료 ║'); + console.log('║ test-results/ 폴더에서 확인하세요 ║'); + console.log('╚════════════════════════════════════════════════════╝\n'); +});