✅ 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