69 lines
2.9 KiB
TypeScript
69 lines
2.9 KiB
TypeScript
import React from 'react';
|
|
import '@testing-library/jest-dom';
|
|
import { render, screen } from '@testing-library/react';
|
|
import { MemoryRouter } from 'react-router-dom';
|
|
|
|
import VerifyEmailPage from './views/VerifyEmailPage';
|
|
import { I18nProvider } from './i18n/I18nProvider';
|
|
import { api, getApiErrorMessage } from './api';
|
|
|
|
const mockedApi = api as jest.Mocked<typeof api>;
|
|
// CRA's jest config sets resetMocks: true, which wipes the initial implementation given to
|
|
// jest.fn() in setupTests.ts before every test -- re-arm it here so error-derived text is testable.
|
|
const mockedGetApiErrorMessage = getApiErrorMessage as jest.Mock;
|
|
|
|
function renderVerifyEmailPage(search: string, emailChange = false) {
|
|
const path = emailChange ? '/confirm-email-change' : '/verify-email';
|
|
window.history.pushState({}, '', `${path}${search}`);
|
|
return render(
|
|
<MemoryRouter initialEntries={[`${path}${search}`]} future={{ v7_startTransition: true, v7_relativeSplatPath: true }}>
|
|
<I18nProvider>
|
|
<VerifyEmailPage emailChange={emailChange} />
|
|
</I18nProvider>
|
|
</MemoryRouter>,
|
|
);
|
|
}
|
|
|
|
describe('VerifyEmailPage', () => {
|
|
beforeEach(() => {
|
|
mockedApi.post.mockReset();
|
|
mockedGetApiErrorMessage.mockImplementation((e: any, fallback?: string) => {
|
|
const data = e?.response?.data;
|
|
return typeof data === 'string' && data.trim() ? data.trim() : fallback;
|
|
});
|
|
});
|
|
|
|
it('confirms the account and shows success when the link is valid', async () => {
|
|
mockedApi.post.mockResolvedValueOnce({ data: {} } as any);
|
|
|
|
renderVerifyEmailPage('?userId=user-1&token=good-token');
|
|
|
|
expect(await screen.findByText('Your email has been verified. You can now sign in.')).toBeInTheDocument();
|
|
expect(mockedApi.post).toHaveBeenCalledWith('/auth/verify-email', { userId: 'user-1', token: 'good-token' });
|
|
});
|
|
|
|
it('shows an error when the link is invalid or expired', async () => {
|
|
mockedApi.post.mockRejectedValueOnce({ response: { status: 400, data: 'Invalid or expired link.' } });
|
|
|
|
renderVerifyEmailPage('?userId=user-1&token=bad-token');
|
|
|
|
expect(await screen.findByText('Invalid or expired link.')).toBeInTheDocument();
|
|
});
|
|
|
|
it('shows an error without calling the API when the link is missing userId/token', async () => {
|
|
renderVerifyEmailPage('');
|
|
|
|
expect(await screen.findByText('Missing user/token in link.')).toBeInTheDocument();
|
|
expect(mockedApi.post).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('confirms a pending email change and requires a fresh sign-in', async () => {
|
|
mockedApi.post.mockResolvedValueOnce({ data: {} } as any);
|
|
|
|
renderVerifyEmailPage('?userId=user-1&email=new%40example.com&token=change-token', true);
|
|
|
|
expect(await screen.findByText('Your email has changed. Sign in again with the new address.')).toBeInTheDocument();
|
|
expect(mockedApi.post).toHaveBeenCalledWith('/auth/email-change/confirm', { userId: 'user-1', email: 'new@example.com', token: 'change-token' });
|
|
});
|
|
});
|