Files
jobtrackingapp/job-tracker-ui/src/quick-capture.test.tsx
T
2026-07-30 22:27:10 +02:00

90 lines
3.5 KiB
TypeScript

import React from 'react';
import '@testing-library/jest-dom';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { ToastProvider } from './toast';
import { I18nProvider } from './i18n/I18nProvider';
import { api } from './api';
// Avoid pulling the date-fns v4 ESM adapter into Jest; the picker isn't under test here.
jest.mock('@mui/x-date-pickers/DatePicker', () => ({
DatePicker: ({ label }: any) => <div>{label}</div>,
}));
// eslint-disable-next-line import/first
import AddJobModal from './components/AddJobModal';
jest.setTimeout(15000);
jest.mock('./api', () => ({
api: {
get: jest.fn(() => Promise.resolve({ data: [] })),
post: jest.fn(() => Promise.resolve({ data: {} })),
put: jest.fn(() => Promise.resolve({ data: {} })),
patch: jest.fn(() => Promise.resolve({ data: {} })),
delete: jest.fn(() => Promise.resolve({ data: {} })),
interceptors: { request: { use: jest.fn() }, response: { use: jest.fn() } },
},
getApiErrorMessage: jest.fn(() => 'error'),
}));
const mockedApi = api as jest.Mocked<typeof api>;
function renderModal(initialUrl?: string) {
return render(
<ToastProvider>
<I18nProvider>
<AddJobModal open initialUrl={initialUrl} onClose={() => {}} onCreated={() => {}} />
</I18nProvider>
</ToastProvider>,
);
}
beforeEach(() => {
mockedApi.get.mockResolvedValue({ data: [] } as any);
mockedApi.post.mockImplementation((url: string) => {
if (url === '/jobimport/preview') {
return Promise.resolve({ data: { success: true, title: 'Imported Backend Role', company: 'Acme', location: 'Oslo', description: 'desc', tags: ['C#'] } } as any);
}
return Promise.resolve({ data: {} } as any);
});
});
afterEach(() => jest.clearAllMocks());
test('auto-imports from initialUrl and prefills the form', async () => {
renderModal('https://example.com/jobs/123');
await waitFor(() => {
expect(mockedApi.post).toHaveBeenCalledWith('/jobimport/preview', { url: 'https://example.com/jobs/123' });
});
expect(await screen.findByDisplayValue('Imported Backend Role')).toBeInTheDocument();
});
test('does not auto-import when no initialUrl is given', async () => {
renderModal(undefined);
// Wait for the modal to render, then confirm no import was triggered.
expect(await screen.findByRole('dialog')).toBeInTheDocument();
expect(mockedApi.post).not.toHaveBeenCalledWith('/jobimport/preview', expect.anything());
});
test('can generate a tailored CV after creating a job', async () => {
mockedApi.post.mockImplementation((url: string) => {
if (url === '/jobimport/preview') return Promise.resolve({ data: { success: true, title: 'Imported Backend Role', company: 'Acme', description: 'desc' } } as any);
if (url === '/companies') return Promise.resolve({ data: { id: 1, name: 'Acme' } } as any);
if (url === '/jobapplications') return Promise.resolve({ data: { id: 42 } } as any);
return Promise.resolve({ data: {} } as any);
});
renderModal('https://example.com/jobs/123');
expect(await screen.findByDisplayValue('Imported Backend Role')).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: 'Continue' }));
fireEvent.click(await screen.findByLabelText('Generate a tailored CV draft after creating this job'));
for (let step = 0; step < 3; step += 1) fireEvent.click(screen.getByRole('button', { name: 'Skip and continue' }));
fireEvent.click(screen.getByRole('button', { name: /create job/i }));
await waitFor(() => expect(mockedApi.post).toHaveBeenCalledWith('/jobapplications/42/generate-tailored-cv-draft'));
});