5203ddea72
Wrap pathological content, paginate oversized entries, measure A4 and Letter previews correctly, unify section ordering, and gate stored-output actions on saved state.
102 lines
3.8 KiB
TypeScript
102 lines
3.8 KiB
TypeScript
import React from 'react';
|
|
import '@testing-library/jest-dom';
|
|
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
import { MemoryRouter } from 'react-router-dom';
|
|
|
|
import CvBuilderPage from './views/CvBuilderPage';
|
|
import { I18nProvider } from './i18n/I18nProvider';
|
|
import { ToastProvider } from './toast';
|
|
import { api } from './api';
|
|
import { ConfirmProvider } from './confirm';
|
|
import { PromptProvider } from './prompt';
|
|
|
|
const mockNavigate = jest.fn();
|
|
jest.mock('react-router-dom', () => ({
|
|
...jest.requireActual('react-router-dom'),
|
|
useNavigate: () => mockNavigate,
|
|
}));
|
|
|
|
jest.mock('./api', () => ({
|
|
api: {
|
|
get: jest.fn(),
|
|
post: jest.fn(),
|
|
put: jest.fn(),
|
|
patch: jest.fn(),
|
|
delete: jest.fn(),
|
|
interceptors: { request: { use: jest.fn() }, response: { use: jest.fn() } },
|
|
},
|
|
getApiErrorMessage: (_error: any, fallback?: string) => fallback || 'Request failed.',
|
|
}));
|
|
|
|
const mockedApi = api as jest.Mocked<typeof api>;
|
|
|
|
function renderPage() {
|
|
return render(
|
|
<MemoryRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }}>
|
|
<ToastProvider>
|
|
<I18nProvider>
|
|
<ConfirmProvider>
|
|
<PromptProvider>
|
|
<CvBuilderPage />
|
|
</PromptProvider>
|
|
</ConfirmProvider>
|
|
</I18nProvider>
|
|
</ToastProvider>
|
|
</MemoryRouter>,
|
|
);
|
|
}
|
|
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
test('lists existing CVs from the variants API', async () => {
|
|
mockedApi.get.mockResolvedValueOnce({
|
|
data: [
|
|
{ id: 1, name: 'Frontend CV', themeId: 'modern', publicSlug: 'abc', isPublic: true, version: 2, jobApplicationId: null, updatedAtUtc: new Date().toISOString() },
|
|
],
|
|
} as any);
|
|
|
|
renderPage();
|
|
|
|
expect(await screen.findByText('Frontend CV')).toBeInTheDocument();
|
|
expect(screen.getByText('Public')).toBeInTheDocument();
|
|
expect(mockedApi.get).toHaveBeenCalledWith('/cv/variants');
|
|
});
|
|
|
|
test('shows the empty state and creates a CV then navigates to the editor', async () => {
|
|
mockedApi.get.mockResolvedValueOnce({ data: [] } as any);
|
|
mockedApi.post.mockResolvedValueOnce({ data: { id: 42 } } as any);
|
|
|
|
renderPage();
|
|
|
|
expect(await screen.findByText('No CVs yet')).toBeInTheDocument();
|
|
|
|
fireEvent.click(screen.getAllByRole('button', { name: /New CV/i })[0]);
|
|
|
|
await waitFor(() => expect(mockedApi.post).toHaveBeenCalledWith('/cv/variants', expect.objectContaining({ name: 'Untitled CV' })));
|
|
await waitFor(() => expect(mockNavigate).toHaveBeenCalledWith('/career/builder/42'));
|
|
});
|
|
|
|
test('CV deletion requires confirmation and supports cancellation', async () => {
|
|
mockedApi.get.mockResolvedValueOnce({ data: [
|
|
{ id: 1, name: 'Frontend CV', themeId: 'modern', publicSlug: 'abc', isPublic: false, version: 2, jobApplicationId: null, updatedAtUtc: new Date().toISOString() },
|
|
] } as any);
|
|
mockedApi.delete.mockResolvedValue({ data: null } as any);
|
|
renderPage();
|
|
|
|
await screen.findByText('Frontend CV');
|
|
fireEvent.click(screen.getByRole('button', { name: 'Actions for Frontend CV' }));
|
|
fireEvent.click(await screen.findByRole('menuitem', { name: 'Delete' }));
|
|
expect(await screen.findByRole('dialog', { name: 'Delete CV' })).toBeInTheDocument();
|
|
fireEvent.click(screen.getByRole('button', { name: 'Cancel' }));
|
|
await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument());
|
|
expect(mockedApi.delete).not.toHaveBeenCalled();
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: 'Actions for Frontend CV' }));
|
|
fireEvent.click(await screen.findByRole('menuitem', { name: 'Delete' }));
|
|
fireEvent.click(await screen.findByRole('button', { name: 'Delete CV' }));
|
|
await waitFor(() => expect(mockedApi.delete).toHaveBeenCalledWith('/cv/variants/1'));
|
|
await waitFor(() => expect(screen.queryByText('Frontend CV')).not.toBeInTheDocument());
|
|
});
|