From 03bf0f38d635d457a148a2099e4c6e866206705c Mon Sep 17 00:00:00 2001 From: FragginWagon Date: Thu, 29 Jan 2026 02:07:47 +0000 Subject: [PATCH] =?UTF-8?q?=E2=9C=85=20Add=20unit=20tests=20for=20router?= =?UTF-8?q?=20guards=20to=20verify=20route=20protection=20with=20authentic?= =?UTF-8?q?ation=20and=20feature=20flags?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tests/unit/router/guards.test.js | 191 ++++++++++++++++++ 1 file changed, 191 insertions(+) diff --git a/code/websites/pokedex.online/tests/unit/router/guards.test.js b/code/websites/pokedex.online/tests/unit/router/guards.test.js index e69de29..7476f26 100644 --- a/code/websites/pokedex.online/tests/unit/router/guards.test.js +++ b/code/websites/pokedex.online/tests/unit/router/guards.test.js @@ -0,0 +1,191 @@ +/** + * Router Guards Tests + * Verifies route protection with auth and feature flags + */ + +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { setupAuthGuards } from '../../../src/router/guards.js'; + +// Mock composables +vi.mock('../../../src/composables/useAuth.js', () => ({ + useAuth: vi.fn() +})); + +vi.mock('../../../src/composables/useFeatureFlags.js', () => ({ + useFeatureFlags: vi.fn() +})); + +import { useAuth } from '../../../src/composables/useAuth.js'; +import { useFeatureFlags } from '../../../src/composables/useFeatureFlags.js'; + +describe('Router Guards', () => { + let router; + let beforeEachGuard; + + beforeEach(() => { + // Create mock router + router = { + beforeEach: vi.fn(guard => { + beforeEachGuard = guard; + }), + afterEach: vi.fn() + }; + + // Default mock implementations + useAuth.mockReturnValue({ + isAuthenticated: { value: false }, + initializeAuth: vi.fn() + }); + + useFeatureFlags.mockReturnValue({ + isEnabled: { value: vi.fn(() => true) } + }); + }); + + it('registers beforeEach and afterEach guards', () => { + setupAuthGuards(router); + + expect(router.beforeEach).toHaveBeenCalled(); + expect(router.afterEach).toHaveBeenCalled(); + }); + + it('allows navigation to public routes', async () => { + setupAuthGuards(router); + + const to = { path: '/', meta: {} }; + const from = { path: '/previous' }; + const next = vi.fn(); + + await beforeEachGuard(to, from, next); + + expect(next).toHaveBeenCalledWith(); + }); + + it('blocks access to feature-flagged routes when flag is disabled', async () => { + useFeatureFlags.mockReturnValue({ + isEnabled: { value: vi.fn(() => false) } + }); + + setupAuthGuards(router); + + const to = { + path: '/experimental', + meta: { featureFlag: 'experimental-feature' } + }; + const from = { path: '/' }; + const next = vi.fn(); + + await beforeEachGuard(to, from, next); + + expect(next).toHaveBeenCalledWith({ name: 'Home', replace: true }); + }); + + it('allows access to feature-flagged routes when flag is enabled', async () => { + useFeatureFlags.mockReturnValue({ + isEnabled: { value: vi.fn(() => true) } + }); + + setupAuthGuards(router); + + const to = { + path: '/experimental', + meta: { featureFlag: 'experimental-feature' } + }; + const from = { path: '/' }; + const next = vi.fn(); + + await beforeEachGuard(to, from, next); + + expect(next).toHaveBeenCalledWith(); + }); + + it('redirects to login for admin routes when not authenticated', async () => { + useAuth.mockReturnValue({ + isAuthenticated: { value: false }, + initializeAuth: vi.fn() + }); + + setupAuthGuards(router); + + const to = { + path: '/admin', + fullPath: '/admin?tab=settings', + meta: { requiresAdmin: true } + }; + const from = { path: '/' }; + const next = vi.fn(); + + await beforeEachGuard(to, from, next); + + expect(next).toHaveBeenCalledWith({ + name: 'admin-login', + query: { redirect: '/admin?tab=settings' } + }); + }); + + it('allows navigation to admin routes when authenticated', async () => { + useAuth.mockReturnValue({ + isAuthenticated: { value: true }, + initializeAuth: vi.fn() + }); + + setupAuthGuards(router); + + const to = { + path: '/admin', + meta: { requiresAdmin: true } + }; + const from = { path: '/' }; + const next = vi.fn(); + + await beforeEachGuard(to, from, next); + + expect(next).toHaveBeenCalledWith(); + }); + + it('initializes auth before checking routes', async () => { + const initializeAuth = vi.fn(); + useAuth.mockReturnValue({ + isAuthenticated: { value: false }, + initializeAuth + }); + + setupAuthGuards(router); + + const to = { path: '/', meta: {} }; + const from = { path: '/' }; + const next = vi.fn(); + + await beforeEachGuard(to, from, next); + + expect(initializeAuth).toHaveBeenCalled(); + }); + + it('handles both auth and feature flag requirements', async () => { + useAuth.mockReturnValue({ + isAuthenticated: { value: true }, + initializeAuth: vi.fn() + }); + + useFeatureFlags.mockReturnValue({ + isEnabled: { value: vi.fn(() => true) } + }); + + setupAuthGuards(router); + + const to = { + path: '/admin-feature', + meta: { + requiresAdmin: true, + featureFlag: 'admin-panel' + } + }; + const from = { path: '/' }; + const next = vi.fn(); + + await beforeEachGuard(to, from, next); + + // Should pass both checks + expect(next).toHaveBeenCalledWith(); + }); +});