Files
memory-infrastructure-palace/code/websites/pokedex.online/tests/unit/router/guards.test.js

192 lines
4.5 KiB
JavaScript

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