157 lines
3.8 KiB
TypeScript
157 lines
3.8 KiB
TypeScript
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
import { mockNuxtImport } from '@nuxt/test-utils/runtime';
|
|
import type { RouteLocationNormalized } from 'vue-router';
|
|
|
|
/**
|
|
* Tests for auth middleware
|
|
* Based on specs from private/specs.md:
|
|
*
|
|
* Scenario: Access protected page without auth
|
|
* Given I am not logged in
|
|
* When I try to access "/dashboard"
|
|
* Then I should be redirected to "/login"
|
|
*/
|
|
|
|
// Create mocks at module level to avoid hoisting issues
|
|
const mockState = {
|
|
isAuthenticated: false,
|
|
navigateToSpy: vi.fn(),
|
|
};
|
|
|
|
// Mock useAuth
|
|
mockNuxtImport('useAuth', () => {
|
|
return () => ({
|
|
isAuthenticated: {
|
|
get value() {
|
|
return mockState.isAuthenticated;
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
// Mock navigateTo
|
|
mockNuxtImport('navigateTo', () => {
|
|
return (path: string) => mockState.navigateToSpy(path);
|
|
});
|
|
|
|
describe('auth middleware', () => {
|
|
beforeEach(async () => {
|
|
// Reset state
|
|
mockState.isAuthenticated = false;
|
|
mockState.navigateToSpy.mockClear();
|
|
});
|
|
|
|
it('should redirect to /login when user is not authenticated', async () => {
|
|
mockState.isAuthenticated = false;
|
|
|
|
const { default: authMiddleware } = await import('../auth.global');
|
|
|
|
const to = {
|
|
path: '/dashboard',
|
|
fullPath: '/dashboard',
|
|
} as RouteLocationNormalized;
|
|
|
|
const from = {
|
|
path: '/',
|
|
fullPath: '/',
|
|
} as RouteLocationNormalized;
|
|
|
|
await authMiddleware(to, from);
|
|
|
|
expect(mockState.navigateToSpy).toHaveBeenCalledWith({
|
|
path: '/login',
|
|
query: {
|
|
redirect: '/dashboard',
|
|
},
|
|
});
|
|
});
|
|
|
|
it('should allow access when user is authenticated', async () => {
|
|
mockState.isAuthenticated = true;
|
|
|
|
const { default: authMiddleware } = await import('../auth.global');
|
|
|
|
const to = {
|
|
path: '/dashboard',
|
|
fullPath: '/dashboard',
|
|
} as RouteLocationNormalized;
|
|
|
|
const from = {
|
|
path: '/login',
|
|
fullPath: '/login',
|
|
} as RouteLocationNormalized;
|
|
|
|
const result = await authMiddleware(to, from);
|
|
|
|
expect(mockState.navigateToSpy).not.toHaveBeenCalled();
|
|
expect(result).toBeUndefined(); // No redirect = allow access
|
|
});
|
|
|
|
it('should not redirect if already on login page', async () => {
|
|
mockState.isAuthenticated = false;
|
|
|
|
const { default: authMiddleware } = await import('../auth.global');
|
|
|
|
const to = {
|
|
path: '/login',
|
|
fullPath: '/login',
|
|
} as RouteLocationNormalized;
|
|
|
|
const from = {
|
|
path: '/dashboard',
|
|
fullPath: '/dashboard',
|
|
} as RouteLocationNormalized;
|
|
|
|
const result = await authMiddleware(to, from);
|
|
|
|
expect(mockState.navigateToSpy).not.toHaveBeenCalled();
|
|
expect(result).toBeUndefined();
|
|
});
|
|
|
|
it('should allow access to home page without authentication', async () => {
|
|
mockState.isAuthenticated = false;
|
|
|
|
const { default: authMiddleware } = await import('../auth.global');
|
|
|
|
const to = {
|
|
path: '/',
|
|
fullPath: '/',
|
|
} as RouteLocationNormalized;
|
|
|
|
const from = {
|
|
path: '/somewhere',
|
|
fullPath: '/somewhere',
|
|
} as RouteLocationNormalized;
|
|
|
|
const result = await authMiddleware(to, from);
|
|
|
|
expect(mockState.navigateToSpy).not.toHaveBeenCalled();
|
|
expect(result).toBeUndefined();
|
|
});
|
|
|
|
it('should redirect to /login for any protected route', async () => {
|
|
mockState.isAuthenticated = false;
|
|
|
|
const { default: authMiddleware } = await import('../auth.global');
|
|
|
|
const to = {
|
|
path: '/projects',
|
|
fullPath: '/projects',
|
|
} as RouteLocationNormalized;
|
|
|
|
const from = {
|
|
path: '/',
|
|
fullPath: '/',
|
|
} as RouteLocationNormalized;
|
|
|
|
await authMiddleware(to, from);
|
|
|
|
expect(mockState.navigateToSpy).toHaveBeenCalledWith({
|
|
path: '/login',
|
|
query: {
|
|
redirect: '/projects',
|
|
},
|
|
});
|
|
});
|
|
});
|