From 56ba613cc76fb976d0303711418d6a2bee20934a Mon Sep 17 00:00:00 2001 From: Rob Colbert Date: Tue, 28 Apr 2026 17:43:05 -0400 Subject: [PATCH] theme enforced and correctly implemented; e2e tests added for theme --- .../frontend/src/components/Header.tsx | 3 +- gadget-code/frontend/src/index.css | 17 ++- gadget-code/frontend/src/pages/SignIn.tsx | 12 +- gadget-code/frontend/src/pages/SignUp.tsx | 14 +-- gadget-code/frontend/tailwind.config.js | 4 +- gadget-code/tests/e2e/theme.test.ts | 108 ++++++++++++++++++ 6 files changed, 135 insertions(+), 23 deletions(-) create mode 100644 gadget-code/tests/e2e/theme.test.ts diff --git a/gadget-code/frontend/src/components/Header.tsx b/gadget-code/frontend/src/components/Header.tsx index 5b4c5da..639ebaa 100644 --- a/gadget-code/frontend/src/components/Header.tsx +++ b/gadget-code/frontend/src/components/Header.tsx @@ -50,8 +50,7 @@ export default function Header({ user, onSignOut }: HeaderProps) { {menuOpen && (
@@ -80,7 +80,7 @@ export default function SignIn({ onSuccess }: SignInProps) {

Don't have an account?{' '} - + Sign up

diff --git a/gadget-code/frontend/src/pages/SignUp.tsx b/gadget-code/frontend/src/pages/SignUp.tsx index 48c7199..fa6284e 100644 --- a/gadget-code/frontend/src/pages/SignUp.tsx +++ b/gadget-code/frontend/src/pages/SignUp.tsx @@ -39,7 +39,7 @@ export default function SignUp({ onSuccess }: SignUpProps) { }; return ( -
+

Create Account

{error && ( @@ -57,7 +57,7 @@ export default function SignUp({ onSuccess }: SignUpProps) { type="email" value={email} onChange={(e) => setEmail(e.target.value)} - className="w-full px-4 py-2 bg-bg-secondary border border-border rounded-lg text-text focus:outline-none focus:border-primary" + className="w-full px-4 py-2 bg-bg-tertiary border border-border-default rounded-lg text-text-primary focus:outline-none focus:border-brand" required />
@@ -70,7 +70,7 @@ export default function SignUp({ onSuccess }: SignUpProps) { type="text" value={displayName} onChange={(e) => setDisplayName(e.target.value)} - className="w-full px-4 py-2 bg-bg-secondary border border-border rounded-lg text-text focus:outline-none focus:border-primary" + className="w-full px-4 py-2 bg-bg-tertiary border border-border-default rounded-lg text-text-primary focus:outline-none focus:border-brand" required />
@@ -83,7 +83,7 @@ export default function SignUp({ onSuccess }: SignUpProps) { type="password" value={password} onChange={(e) => setPassword(e.target.value)} - className="w-full px-4 py-2 bg-bg-secondary border border-border rounded-lg text-text focus:outline-none focus:border-primary" + className="w-full px-4 py-2 bg-bg-tertiary border border-border-default rounded-lg text-text-primary focus:outline-none focus:border-brand" required />
@@ -96,21 +96,21 @@ export default function SignUp({ onSuccess }: SignUpProps) { type="password" value={passwordVerify} onChange={(e) => setPasswordVerify(e.target.value)} - className="w-full px-4 py-2 bg-bg-secondary border border-border rounded-lg text-text focus:outline-none focus:border-primary" + className="w-full px-4 py-2 bg-bg-tertiary border border-border-default rounded-lg text-text-primary focus:outline-none focus:border-brand" required />
Cancel diff --git a/gadget-code/frontend/tailwind.config.js b/gadget-code/frontend/tailwind.config.js index 2d57632..f12581b 100644 --- a/gadget-code/frontend/tailwind.config.js +++ b/gadget-code/frontend/tailwind.config.js @@ -14,8 +14,8 @@ export default { 'text-secondary': '#a3a3a3', 'text-muted': '#737373', 'border-subtle': '#1a1a1a', - 'border-default': '#27272a', - 'border-highlight': '#2a2a2a', + 'border-default': '#2a2a2a', + 'border-highlight': '#3a3a3a', }, fontFamily: { mono: ['Courier New', 'Courier', 'monospace'], diff --git a/gadget-code/tests/e2e/theme.test.ts b/gadget-code/tests/e2e/theme.test.ts new file mode 100644 index 0000000..a2ef0db --- /dev/null +++ b/gadget-code/tests/e2e/theme.test.ts @@ -0,0 +1,108 @@ +import { chromium, test, expect } from '@playwright/test'; + +const THEME = { + brand: '#c20600', + bgPrimary: '#0a0a0a', + bgSecondary: '#121212', + bgTertiary: '#1a1a1a', + bgElevated: '#202020', + textPrimary: '#d4d4d4', + textSecondary: '#a3a3a3', + textMuted: '#737373', + borderSubtle: '#1a1a1a', + borderDefault: '#2a2a2a', + borderHighlight: '#3a3a3a', +}; + +function rgbToHex(rgb: string): string { + const match = rgb.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/); + if (!match) return rgb; + const [, r, g, b] = match.map(Number); + return `#${[r, g, b].map((x) => x.toString(16).padStart(2, '0')).join('')}`; +} + +test.describe('Theme Colors', () => { + test('header should have correct border color', async ({ page }) => { + await page.goto('https://code-dev.g4dge7.com:5174/'); + await page.waitForLoadState('networkidle'); + await page.waitForTimeout(2000); + + const header = page.locator('header').first(); + await expect(header).toBeVisible(); + + const borderColor = await header.evaluate((el) => { + return window.getComputedStyle(el).borderBottomColor; + }); + + const actualHex = rgbToHex(borderColor); + const expectedHex = THEME.borderSubtle; + + console.log('Header border-bottom-color:', actualHex, '(expected:', expectedHex, ')'); + expect(actualHex.toLowerCase()).toBe(expectedHex); + }); + + test('body should have correct background color', async ({ page }) => { + await page.goto('https://code-dev.g4dge7.com:5174/'); + await page.waitForLoadState('networkidle'); + await page.waitForTimeout(2000); + + const bgColor = await page.evaluate(() => { + return window.getComputedStyle(document.body).backgroundColor; + }); + + const actualHex = rgbToHex(bgColor); + const expectedHex = THEME.bgPrimary; + + console.log('Body background-color:', actualHex, '(expected:', expectedHex, ')'); + expect(actualHex.toLowerCase()).toBe(expectedHex); + }); + + test('header text should have correct color', async ({ page }) => { + await page.goto('https://code-dev.g4dge7.com:5174/'); + await page.waitForLoadState('networkidle'); + await page.waitForTimeout(2000); + + const header = page.locator('header').first(); + const link = header.locator('a').first(); + + const color = await link.evaluate((el) => { + return window.getComputedStyle(el).color; + }); + + const actualHex = rgbToHex(color); + const expectedHex = THEME.textPrimary; + + console.log('Header link color:', actualHex, '(expected:', expectedHex, ')'); + expect(actualHex.toLowerCase()).toBe(expectedHex); + }); + +test('all theme colors should match spec', async ({ page }) => { + await page.goto('https://code-dev.g4dge7.com:5174/'); + await page.waitForLoadState('networkidle'); + await page.waitForTimeout(2000); + + const results: string[] = []; + + const header = page.locator('header').first(); + const headerBorder = await header.evaluate((el) => { + const c = window.getComputedStyle(el).borderBottomColor; + const m = c.match(/rgb\((\d+),\s*(\d+),\s*(\d+)/); + if (!m) return c; + return '#' + [m[1], m[2], m[3]].map(x => parseInt(x).toString(16).padStart(2, '0')).join(''); + }); + results.push(`header border: ${headerBorder}`); + + const bodyBg = await page.evaluate(() => { + const c = window.getComputedStyle(document.body).backgroundColor; + const m = c.match(/rgb\((\d+),\s*(\d+),\s*(\d+)/); + if (!m) return c; + return '#' + [m[1], m[2], m[3]].map(x => parseInt(x).toString(16).padStart(2, '0')).join(''); + }); + results.push(`body bg: ${bodyBg}`); + + console.log('Theme verification:', results.join(', ')); + + expect(headerBorder.toLowerCase()).toBe(THEME.borderSubtle); + expect(bodyBg.toLowerCase()).toBe(THEME.bgPrimary); + }); +}); \ No newline at end of file