/** * 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(); }); });