diff --git a/frontend/src/middleware.disabled.ts b/frontend/src/middleware.disabled.ts deleted file mode 100644 index f801a26..0000000 --- a/frontend/src/middleware.disabled.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { NextResponse } from 'next/server'; -import type { NextRequest } from 'next/server'; -import createMiddleware from 'next-intl/middleware'; -import { routing } from './lib/i18n/routing'; - -// Create next-intl middleware for locale handling -const intlMiddleware = createMiddleware(routing); - -export function middleware(request: NextRequest) { - const { pathname } = request.nextUrl; - - // Block access to /dev routes in production (handles both /dev and /[locale]/dev) - // Match: /dev, /en/dev, /it/dev, etc. - if (pathname === '/dev' || pathname.match(/^\/[a-z]{2}\/dev($|\/)/)) { - const isProduction = process.env.NODE_ENV === 'production'; - - if (isProduction) { - // Return 404 in production - return new NextResponse(null, { status: 404 }); - } - } - - // Handle locale routing with next-intl - return intlMiddleware(request); -} - -export const config = { - // Match all pathnames except for: - // - API routes (/api/*) - // - Static files (/_next/*, /favicon.ico, etc.) - // - Files in public folder (images, fonts, etc.) - matcher: [ - // Match all pathnames except for - '/((?!api|_next|_vercel|.*\\..*).*)', - // However, match all pathnames within /api/ - // that don't end with a file extension - '/api/(.*)', - ], -}; diff --git a/frontend/tests/app/admin/page.test.tsx b/frontend/tests/app/admin/page.test.tsx index 69613de..d2ae918 100644 --- a/frontend/tests/app/admin/page.test.tsx +++ b/frontend/tests/app/admin/page.test.tsx @@ -5,10 +5,26 @@ import { render, screen } from '@testing-library/react'; import AdminPage from '@/app/[locale]/admin/page'; -import { useAdminStats } from '@/lib/api/hooks/useAdmin'; +import { getAdminStats } from '@/lib/api/admin'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; + +// Mock the API client +jest.mock('@/lib/api/admin'); // Mock the useAdminStats hook -jest.mock('@/lib/api/hooks/useAdmin'); +jest.mock('@/lib/api/hooks/useAdmin', () => ({ + useAdminStats: () => ({ + data: { + totalUsers: 100, + activeUsers: 80, + totalOrganizations: 20, + totalSessions: 30, + }, + isLoading: false, + isError: false, + error: null, + }), +})); // Mock chart components jest.mock('@/components/charts', () => ({ @@ -22,23 +38,31 @@ jest.mock('@/components/charts', () => ({ UserStatusChart: () =>
User Status Chart
, })); -const mockUseAdminStats = useAdminStats as jest.MockedFunction; +const mockGetAdminStats = getAdminStats as jest.MockedFunction; // Helper function to render with default mocked stats function renderWithMockedStats() { - mockUseAdminStats.mockReturnValue({ + mockGetAdminStats.mockResolvedValue({ data: { - totalUsers: 100, - activeUsers: 80, - totalOrganizations: 20, - totalSessions: 30, + user_growth: [], + organization_distribution: [], + user_status: [], }, - isLoading: false, - isError: false, - error: null, } as any); - return render(); + const queryClient = new QueryClient({ + defaultOptions: { + queries: { + retry: false, + }, + }, + }); + + return render( + + + + ); } describe('AdminPage', () => { diff --git a/frontend/tests/app/page.test.tsx b/frontend/tests/app/page.test.tsx index 64303ed..a303c3d 100644 --- a/frontend/tests/app/page.test.tsx +++ b/frontend/tests/app/page.test.tsx @@ -47,6 +47,34 @@ jest.mock('react-syntax-highlighter/dist/esm/styles/prism', () => ({ vscDarkPlus: {}, })); +// Mock auth hooks +jest.mock('@/lib/api/hooks/useAuth', () => ({ + useIsAuthenticated: jest.fn(() => false), + useLogout: jest.fn(() => ({ + mutate: jest.fn(), + })), +})); + +// Mock Theme components +jest.mock('@/components/theme', () => ({ + ThemeToggle: () =>
Theme Toggle
, +})); + +// Mock LocaleSwitcher +jest.mock('@/components/i18n', () => ({ + LocaleSwitcher: () =>
Locale Switcher
, +})); + +// Mock DemoCredentialsModal +jest.mock('@/components/home/DemoCredentialsModal', () => ({ + DemoCredentialsModal: ({ open, onClose }: any) => + open ? ( +
+ +
+ ) : null, +})); + describe('HomePage', () => { describe('Page Structure', () => { it('renders without crashing', () => { @@ -60,7 +88,6 @@ describe('HomePage', () => { render(); const header = screen.getByRole('banner'); expect(within(header).getByText('PragmaStack')).toBeInTheDocument(); - expect(within(header).getByText('Template')).toBeInTheDocument(); }); it('renders footer with copyright', () => { @@ -79,30 +106,26 @@ describe('HomePage', () => { it('renders production-ready messaging', () => { render(); - expect(screen.getByText(/Production-ready FastAPI/i)).toBeInTheDocument(); + expect(screen.getByText(/Opinionated, secure, and production-ready/i)).toBeInTheDocument(); }); - it('renders test coverage stats', () => { + it('renders badges', () => { render(); - const coverageTexts = screen.getAllByText('97%'); - expect(coverageTexts.length).toBeGreaterThan(0); - expect(screen.getAllByText(/Test Coverage/i)[0]).toBeInTheDocument(); - const testCountTexts = screen.getAllByText('743'); - expect(testCountTexts.length).toBeGreaterThan(0); - expect(screen.getAllByText(/Passing Tests/i)[0]).toBeInTheDocument(); + expect(screen.getByText('Comprehensive Tests')).toBeInTheDocument(); + expect(screen.getByText('Pragmatic by Design')).toBeInTheDocument(); }); }); describe('Context Section', () => { it('renders what you get message', () => { render(); - expect(screen.getByText(/What You Get Out of the Box/i)).toBeInTheDocument(); + expect(screen.getByText(/Stop Reinventing the Wheel/i)).toBeInTheDocument(); }); it('renders key features', () => { render(); expect(screen.getAllByText(/Clone & Deploy in < 5 minutes/i)[0]).toBeInTheDocument(); - expect(screen.getAllByText(/97% Test Coverage \(743 tests\)/i)[0]).toBeInTheDocument(); + expect(screen.getAllByText(/Comprehensive Test Suite/i)[0]).toBeInTheDocument(); expect(screen.getAllByText(/12\+ Documentation Guides/i)[0]).toBeInTheDocument(); expect(screen.getAllByText(/Zero Commercial Dependencies/i)[0]).toBeInTheDocument(); }); @@ -167,7 +190,7 @@ describe('HomePage', () => { describe('Tech Stack Section', () => { it('renders tech stack heading', () => { render(); - expect(screen.getByText(/Modern, Type-Safe, Production-Grade Stack/i)).toBeInTheDocument(); + expect(screen.getByText(/A Stack You Can Trust/i)).toBeInTheDocument(); }); it('renders all technologies', () => { @@ -186,7 +209,7 @@ describe('HomePage', () => { describe('Philosophy Section', () => { it('renders why this template exists', () => { render(); - expect(screen.getByText(/Why This Template Exists/i)).toBeInTheDocument(); + expect(screen.getByText(/Why PragmaStack\?/i)).toBeInTheDocument(); }); it('renders what you wont find section', () => { @@ -198,7 +221,7 @@ describe('HomePage', () => { it('renders what you will find section', () => { render(); expect(screen.getByText(/What You Will Find/i)).toBeInTheDocument(); - expect(screen.getByText(/Production patterns that actually work/i)).toBeInTheDocument(); + expect(screen.getByText(/Pragmatic Speed: Ship features, not config/i)).toBeInTheDocument(); }); }); diff --git a/frontend/tests/components/auth/LoginForm.test.tsx b/frontend/tests/components/auth/LoginForm.test.tsx index d16985a..856bdb0 100644 --- a/frontend/tests/components/auth/LoginForm.test.tsx +++ b/frontend/tests/components/auth/LoginForm.test.tsx @@ -32,11 +32,15 @@ jest.mock('@/lib/api/hooks/useAuth', () => ({ useLogin: () => mockUseLogin(), })); +// Mock router // Mock router jest.mock('next/navigation', () => ({ useRouter: () => ({ push: jest.fn(), }), + useSearchParams: () => ({ + get: jest.fn(), + }), })); // Mock auth store diff --git a/frontend/tests/components/home/DemoCredentialsModal.test.tsx b/frontend/tests/components/home/DemoCredentialsModal.test.tsx index aacecda..cee71bd 100644 --- a/frontend/tests/components/home/DemoCredentialsModal.test.tsx +++ b/frontend/tests/components/home/DemoCredentialsModal.test.tsx @@ -156,10 +156,16 @@ describe('DemoCredentialsModal', () => { render(); const loginAsUserLink = screen.getByRole('link', { name: /login as user/i }); - expect(loginAsUserLink).toHaveAttribute('href', '/login'); + expect(loginAsUserLink).toHaveAttribute( + 'href', + '/login?email=demo@example.com&password=Demo123!' + ); const loginAsAdminLink = screen.getByRole('link', { name: /login as admin/i }); - expect(loginAsAdminLink).toHaveAttribute('href', '/login'); + expect(loginAsAdminLink).toHaveAttribute( + 'href', + '/login?email=admin@example.com&password=Admin123!' + ); }); it('calls onClose when login link is clicked', () => { diff --git a/frontend/tests/components/home/Header.test.tsx b/frontend/tests/components/home/Header.test.tsx index 9e61ea3..fd7361e 100644 --- a/frontend/tests/components/home/Header.test.tsx +++ b/frontend/tests/components/home/Header.test.tsx @@ -27,6 +27,24 @@ jest.mock('@/components/home/DemoCredentialsModal', () => ({ ) : null, })); +// Mock auth hooks +jest.mock('@/lib/api/hooks/useAuth', () => ({ + useIsAuthenticated: jest.fn(() => false), + useLogout: jest.fn(() => ({ + mutate: jest.fn(), + })), +})); + +// Mock Theme components +jest.mock('@/components/theme', () => ({ + ThemeToggle: () =>
Theme Toggle
, +})); + +// Mock LocaleSwitcher +jest.mock('@/components/i18n', () => ({ + LocaleSwitcher: () =>
Locale Switcher
, +})); + describe('Header', () => { it('renders logo', () => { render( @@ -38,7 +56,6 @@ describe('Header', () => { ); expect(screen.getByText('PragmaStack')).toBeInTheDocument(); - expect(screen.getByText('Template')).toBeInTheDocument(); }); it('logo links to homepage', () => { @@ -50,7 +67,7 @@ describe('Header', () => { /> ); - const logoLink = screen.getByRole('link', { name: /pragmastack template/i }); + const logoLink = screen.getByRole('link', { name: /PragmaStack/i }); expect(logoLink).toHaveAttribute('href', '/'); }); diff --git a/frontend/tests/components/home/HeroSection.test.tsx b/frontend/tests/components/home/HeroSection.test.tsx index 48c7576..2fdbd18 100644 --- a/frontend/tests/components/home/HeroSection.test.tsx +++ b/frontend/tests/components/home/HeroSection.test.tsx @@ -47,8 +47,8 @@ describe('HeroSection', () => { ); expect(screen.getByText('MIT Licensed')).toBeInTheDocument(); - expect(screen.getAllByText('97% Test Coverage')[0]).toBeInTheDocument(); - expect(screen.getByText('Production Ready')).toBeInTheDocument(); + expect(screen.getByText('Comprehensive Tests')).toBeInTheDocument(); + expect(screen.getByText('Pragmatic by Design')).toBeInTheDocument(); }); it('renders main headline', () => { @@ -60,8 +60,8 @@ describe('HeroSection', () => { /> ); - expect(screen.getAllByText(/Everything You Need to Build/i)[0]).toBeInTheDocument(); - expect(screen.getAllByText(/Modern Web Applications/i)[0]).toBeInTheDocument(); + expect(screen.getByText(/The Pragmatic/i)).toBeInTheDocument(); + expect(screen.getByText(/Full-Stack Template/i)).toBeInTheDocument(); }); it('renders subheadline with key messaging', () => { @@ -73,7 +73,7 @@ describe('HeroSection', () => { /> ); - expect(screen.getByText(/Production-ready FastAPI \+ Next.js template/i)).toBeInTheDocument(); + expect(screen.getByText(/Opinionated, secure, and production-ready/i)).toBeInTheDocument(); expect(screen.getByText(/Start building features on day one/i)).toBeInTheDocument(); }); @@ -118,26 +118,6 @@ describe('HeroSection', () => { expect(componentsLink).toHaveAttribute('href', '/dev'); }); - it('displays test coverage stats', () => { - render( - - ); - - const coverageTexts = screen.getAllByText('97%'); - expect(coverageTexts.length).toBeGreaterThan(0); - - const testCountTexts = screen.getAllByText('743'); - expect(testCountTexts.length).toBeGreaterThan(0); - expect(screen.getAllByText(/Passing Tests/i)[0]).toBeInTheDocument(); - - expect(screen.getByText('0')).toBeInTheDocument(); - expect(screen.getByText(/Flaky Tests/i)).toBeInTheDocument(); - }); - it('calls onOpenDemoModal when Try Live Demo button is clicked', () => { const mockOnOpenDemoModal = jest.fn(); render(); diff --git a/frontend/tests/components/home/StatsSection.test.tsx b/frontend/tests/components/home/StatsSection.test.tsx index c23f1e3..4ecbbb8 100644 --- a/frontend/tests/components/home/StatsSection.test.tsx +++ b/frontend/tests/components/home/StatsSection.test.tsx @@ -35,21 +35,25 @@ describe('StatsSection', () => { it('renders all stat cards', () => { render(); - expect(screen.getByText('Test Coverage')).toBeInTheDocument(); - expect(screen.getByText('Passing Tests')).toBeInTheDocument(); - expect(screen.getByText('Flaky Tests')).toBeInTheDocument(); - expect(screen.getByText('API Endpoints')).toBeInTheDocument(); + expect(screen.getByText('Open Source')).toBeInTheDocument(); + expect(screen.getByText('Type Safe')).toBeInTheDocument(); + expect(screen.getByText('Doc Guides')).toBeInTheDocument(); + expect(screen.getByText('Magic')).toBeInTheDocument(); }); it('displays stat descriptions', () => { render(); expect( - screen.getByText(/Comprehensive testing across backend and frontend/i) + screen.getByText(/MIT Licensed. No hidden costs or vendor lock-in/i) + ).toBeInTheDocument(); + expect( + screen.getByText(/End-to-end type safety with TypeScript & Pydantic/i) + ).toBeInTheDocument(); + expect(screen.getByText(/Comprehensive documentation for every feature/i)).toBeInTheDocument(); + expect( + screen.getByText(/Explicit is better than implicit. No hidden logic/i) ).toBeInTheDocument(); - expect(screen.getByText(/Backend, frontend unit, and E2E tests/i)).toBeInTheDocument(); - expect(screen.getByText(/Production-stable test suite/i)).toBeInTheDocument(); - expect(screen.getByText(/Fully documented with OpenAPI/i)).toBeInTheDocument(); }); it('renders animated counters with correct suffixes', () => { @@ -69,7 +73,7 @@ describe('StatsSection', () => { // After animation, we should see the final values // The component should eventually show the stat values - const statsSection = screen.getByText('Test Coverage').parentElement; + const statsSection = screen.getByText('Open Source').parentElement; expect(statsSection).toBeInTheDocument(); }); @@ -78,17 +82,17 @@ describe('StatsSection', () => { // Icons are rendered via lucide-react components // We can verify the stat cards are rendered with proper structure - const testCoverageCard = screen.getByText('Test Coverage').closest('div'); - expect(testCoverageCard).toBeInTheDocument(); + const openSourceCard = screen.getByText('Open Source').closest('div'); + expect(openSourceCard).toBeInTheDocument(); - const passingTestsCard = screen.getByText('Passing Tests').closest('div'); - expect(passingTestsCard).toBeInTheDocument(); + const typeSafeCard = screen.getByText('Type Safe').closest('div'); + expect(typeSafeCard).toBeInTheDocument(); - const flakyTestsCard = screen.getByText('Flaky Tests').closest('div'); - expect(flakyTestsCard).toBeInTheDocument(); + const docGuidesCard = screen.getByText('Doc Guides').closest('div'); + expect(docGuidesCard).toBeInTheDocument(); - const apiEndpointsCard = screen.getByText('API Endpoints').closest('div'); - expect(apiEndpointsCard).toBeInTheDocument(); + const magicCard = screen.getByText('Magic').closest('div'); + expect(magicCard).toBeInTheDocument(); }); describe('Accessibility', () => { @@ -102,7 +106,7 @@ describe('StatsSection', () => { it('has descriptive labels for stats', () => { render(); - const statLabels = ['Test Coverage', 'Passing Tests', 'Flaky Tests', 'API Endpoints']; + const statLabels = ['Open Source', 'Type Safe', 'Doc Guides', 'Magic']; statLabels.forEach((label) => { expect(screen.getByText(label)).toBeInTheDocument();