diff --git a/src/__tests__/GoogleOAuthButton.test.tsx b/src/__tests__/GoogleOAuthButton.test.tsx index 4606ce5..5e84a46 100644 --- a/src/__tests__/GoogleOAuthButton.test.tsx +++ b/src/__tests__/GoogleOAuthButton.test.tsx @@ -3,7 +3,7 @@ import { render, screen, waitFor, act } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest'; -// ── Mocks ───────────────────────────────────────────────────────────────────── +// -- Mocks --------------------------------------------------------------------- const MockApiError = vi.hoisted(() => class extends Error { @@ -24,11 +24,11 @@ vi.mock('@/lib/api-client', () => ({ ApiError: MockApiError, })); -// ── Import after mocks ──────────────────────────────────────────────────────── +// -- Import after mocks -------------------------------------------------------- import { GoogleOAuthButton } from '@/features/auth/GoogleOAuthButton'; -// ── Google GIS mock helpers ─────────────────────────────────────────────────── +// -- Google GIS mock helpers --------------------------------------------------- function setupGoogleMock() { const mockPrompt = vi.fn(); @@ -56,7 +56,7 @@ afterEach(() => { clearGoogleMock(); }); -// ── Tests ───────────────────────────────────────────────────────────────────── +// -- Tests --------------------------------------------------------------------- describe('GoogleOAuthButton', () => { it('renders the Google sign-in button', () => { diff --git a/src/__tests__/ResetPasswordPage.test.tsx b/src/__tests__/ResetPasswordPage.test.tsx index 67528b8..93a2ce3 100644 --- a/src/__tests__/ResetPasswordPage.test.tsx +++ b/src/__tests__/ResetPasswordPage.test.tsx @@ -4,7 +4,7 @@ import userEvent from '@testing-library/user-event'; import { vi, describe, it, expect, beforeEach } from 'vitest'; import { ResetPasswordPage } from '@/features/auth/ResetPasswordPage'; -// ── Mocks ───────────────────────────────────────────────────────────────────── +// -- Mocks --------------------------------------------------------------------- const MockApiError = vi.hoisted(() => class extends Error { @@ -36,7 +36,7 @@ vi.mock('@/lib/routes', () => ({ ROUTES: { login: '/login', dashboard: '/dashboard' }, })); -// ── Helpers ─────────────────────────────────────────────────────────────────── +// -- Helpers ------------------------------------------------------------------- function renderPage(token = 'valid-token') { return render(); @@ -47,7 +47,7 @@ beforeEach(() => { mockResetPassword.mockResolvedValue(undefined); }); -// ── Tests ───────────────────────────────────────────────────────────────────── +// -- Tests --------------------------------------------------------------------- describe('ResetPasswordPage', () => { it('renders the form with disabled submit when token is missing', () => { diff --git a/src/__tests__/VerifyEmailConfirmPage.test.tsx b/src/__tests__/VerifyEmailConfirmPage.test.tsx index fe87de7..f2c8a2a 100644 --- a/src/__tests__/VerifyEmailConfirmPage.test.tsx +++ b/src/__tests__/VerifyEmailConfirmPage.test.tsx @@ -3,7 +3,7 @@ import { render, screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest'; -// ── Mocks ───────────────────────────────────────────────────────────────────── +// -- Mocks --------------------------------------------------------------------- const MockApiError = vi.hoisted(() => class extends Error { @@ -35,7 +35,7 @@ vi.mock('@/lib/routes', () => ({ ROUTES: { dashboard: '/dashboard', login: '/login', verifyEmail: '/verify-email' }, })); -// ── Import after mocks ──────────────────────────────────────────────────────── +// -- Import after mocks -------------------------------------------------------- import { VerifyEmailConfirmPage } from '@/features/auth/VerifyEmailConfirmPage'; @@ -48,7 +48,7 @@ afterEach(() => { vi.useRealTimers(); }); -// ── Tests ───────────────────────────────────────────────────────────────────── +// -- Tests --------------------------------------------------------------------- describe('VerifyEmailConfirmPage', () => { it('shows verifying spinner immediately', () => { diff --git a/src/__tests__/mobileResponsive.test.ts b/src/__tests__/mobileResponsive.test.ts new file mode 100644 index 0000000..5f95bdf --- /dev/null +++ b/src/__tests__/mobileResponsive.test.ts @@ -0,0 +1,35 @@ +import { describe, expect, it } from 'vitest'; +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; + +const css = readFileSync(join(process.cwd(), 'src/styles/globals.css'), 'utf8'); + +describe('mobile responsive safety rules', () => { + it('keeps app shell and page containers constrained on small screens', () => { + expect(css).toContain('max-width: 100vw'); + expect(css).toContain('.app__main'); + expect(css).toContain('min-width: 0'); + expect(css).toContain('@media (max-width: 720px)'); + expect(css).toContain('.app__sidebar { display: none; }'); + expect(css).toContain('.bottomnav { display: flex; }'); + }); + + it('stacks major desktop grids and converts wide rows for mobile', () => { + expect(css).toContain('.responsive-split'); + expect(css).toContain('.responsive-sidebar'); + expect(css).toContain('.responsive-room'); + expect(css).toContain('grid-template-columns: minmax(0, 1fr) !important'); + expect(css).toContain('.leaderboard-row'); + expect(css).toContain('.match-history-row'); + expect(css).toContain('.friends-row'); + }); + + it('keeps dropdowns, modals, tabs, and toasts inside the viewport', () => { + expect(css).toContain('.dropdown-panel'); + expect(css).toContain('max-width: calc(100vw - 24px)'); + expect(css).toContain('.modal, .modal-lg'); + expect(css).toContain('.tabs'); + expect(css).toContain('overflow-x: auto'); + expect(css).toContain('.toast-stack'); + }); +}); diff --git a/src/__tests__/profileApi.test.ts b/src/__tests__/profileApi.test.ts index 9302665..a76888b 100644 --- a/src/__tests__/profileApi.test.ts +++ b/src/__tests__/profileApi.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; -// ── profileApi unit tests ───────────────────────────────────────────────────── +// -- profileApi unit tests ----------------------------------------------------- // Tests use fetch mocking to verify the API client builds correct requests. const mockFetch = vi.fn(); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 735a713..7f452a3 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -4,28 +4,37 @@ import { Geist, Geist_Mono } from 'next/font/google'; import './globals.css'; import { ToastProvider } from '@/components/ui/Toast'; import { AuthProvider } from '@/features/auth/AuthProvider'; +import { ThemeProvider } from '@/lib/theme'; const geistSans = Geist({ variable: '--font-geist-sans', subsets: ['latin'] }); const geistMono = Geist_Mono({ variable: '--font-geist-mono', subsets: ['latin'] }); export const metadata: Metadata = { - title: 'SimPle — Social Gaming Platform', + title: 'SimPle - Social Gaming Platform', description: 'A premium social gaming platform. Play sharp little games with friends, climb leaderboards, and build your profile.', }; +const themeBootScript = `(function(){try{var s=localStorage.getItem('simple.theme');if(!s){s=(window.matchMedia&&window.matchMedia('(prefers-color-scheme: light)').matches)?'light':'dark';}document.documentElement.setAttribute('data-theme',s);}catch(e){document.documentElement.setAttribute('data-theme','dark');}})();`; + export default function RootLayout({ children }: { children: React.ReactNode }) { return ( - + - {/* Google Identity Services — loaded once for the whole app */} +