54 lines
1.8 KiB
TypeScript
54 lines
1.8 KiB
TypeScript
import React from 'react';
|
|
import { act, renderHook } from '@testing-library/react';
|
|
import { AuthProvider, useAuth } from './auth-context';
|
|
import { getCurrentUser, login } from '../api/auth';
|
|
|
|
const mockPush = jest.fn();
|
|
jest.mock('next/navigation', () => ({ useRouter: () => ({ push: mockPush }) }));
|
|
jest.mock('../api/auth', () => ({
|
|
login: jest.fn(),
|
|
getCurrentUser: jest.fn(),
|
|
register: jest.fn(),
|
|
logout: jest.fn(),
|
|
}));
|
|
jest.mock('../api/client', () => ({ hasSession: () => false, clearAuthTokens: jest.fn() }));
|
|
|
|
describe('authenticated navigation', () => {
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
jest
|
|
.mocked(getCurrentUser)
|
|
.mockResolvedValue({ id: 'test-user' } as Awaited<ReturnType<typeof getCurrentUser>>);
|
|
});
|
|
|
|
it.each(['javascript:alert(1)', '//example.org', '/\\example.org', '/\n/example.org'])(
|
|
'does not navigate to attacker destination %j',
|
|
async destination => {
|
|
const { result } = renderHook(useAuth, { wrapper: AuthProvider });
|
|
await act(async () => {
|
|
await result.current.login('user@example.org', 'password', destination);
|
|
});
|
|
expect(login).toHaveBeenCalledWith({
|
|
email: 'user@example.org',
|
|
password: 'password',
|
|
rememberMe: false,
|
|
});
|
|
expect(mockPush).toHaveBeenCalledWith('/dashboard');
|
|
expect(result.current.isAuthenticated).toBe(true);
|
|
}
|
|
);
|
|
|
|
it('preserves localized navigation after successful login', async () => {
|
|
const { result } = renderHook(useAuth, { wrapper: AuthProvider });
|
|
await act(async () => {
|
|
await result.current.login(
|
|
'user@example.org',
|
|
'password',
|
|
'/fr/dashboard?tab=1#bookings',
|
|
true
|
|
);
|
|
});
|
|
expect(mockPush).toHaveBeenCalledWith('/fr/dashboard?tab=1#bookings');
|
|
});
|
|
});
|