✅ Add unit tests for router guards to verify route protection with authentication and feature flags
This commit is contained in:
@@ -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();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user