99 lines
3.5 KiB
TypeScript
99 lines
3.5 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 SettingsView from './components/SettingsView';
|
|
import { I18nProvider } from './i18n/I18nProvider';
|
|
import { ToastProvider } from './toast';
|
|
import { api } from './api';
|
|
|
|
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.',
|
|
}));
|
|
|
|
jest.mock('./components/ImportExportJobs', () => () => <div>Import Export Stub</div>);
|
|
jest.mock('./components/GoogleAuthCard', () => () => <div>Google Auth Stub</div>);
|
|
jest.mock('./components/BackupCard', () => () => <div>Backup Stub</div>);
|
|
jest.mock('./components/AuthStatusCard', () => () => <div>Auth Status Stub</div>);
|
|
|
|
const mockedApi = api as jest.Mocked<typeof api>;
|
|
|
|
function renderView(onAccentColorChange = jest.fn()) {
|
|
return {
|
|
onAccentColorChange,
|
|
...render(
|
|
<MemoryRouter future={{ v7_startTransition: true, v7_relativeSplatPath: true }}>
|
|
<ToastProvider>
|
|
<I18nProvider>
|
|
<SettingsView
|
|
pageSize={20}
|
|
onPageSizeChange={jest.fn()}
|
|
columns={{ status: true, dateApplied: true, daysSince: true, jobUrl: false }}
|
|
onColumnsChange={jest.fn()}
|
|
themeMode="dark"
|
|
onThemeModeChange={jest.fn()}
|
|
accentColor="#15803d"
|
|
onAccentColorChange={onAccentColorChange}
|
|
onResetAccentColor={jest.fn()}
|
|
/>
|
|
</I18nProvider>
|
|
</ToastProvider>
|
|
</MemoryRouter>,
|
|
),
|
|
};
|
|
}
|
|
|
|
beforeEach(() => {
|
|
mockedApi.get.mockImplementation((url: string) => {
|
|
if (url === '/rules') {
|
|
return Promise.resolve({
|
|
data: {
|
|
id: 1,
|
|
appliedFollowUpDays: 14,
|
|
appliedGhostDays: 30,
|
|
offerFollowUpDays: 7,
|
|
offerGhostDays: 14,
|
|
feedbackFollowUpDays: 7,
|
|
feedbackGhostDays: 14,
|
|
},
|
|
} as any);
|
|
}
|
|
return Promise.resolve({ data: {} } as any);
|
|
});
|
|
window.localStorage.clear();
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
test('settings view uses one follow-up section, one notification section, and staged accent apply', async () => {
|
|
const { onAccentColorChange } = renderView();
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: /#15803D/i }));
|
|
const accentInput = (await screen.findAllByLabelText('Accent'))[1] as HTMLInputElement;
|
|
fireEvent.change(accentInput, { target: { value: '#2563eb' } });
|
|
expect(onAccentColorChange).not.toHaveBeenCalled();
|
|
fireEvent.click(screen.getByRole('button', { name: /^save$/i }));
|
|
expect(onAccentColorChange).toHaveBeenCalledWith('#2563eb');
|
|
|
|
fireEvent.click(screen.getByRole('tab', { name: /follow-ups/i }));
|
|
expect(await screen.findByText(/follow-up rules by scenario/i)).toBeInTheDocument();
|
|
expect(screen.queryAllByText(/open reminders/i)).toHaveLength(0);
|
|
|
|
fireEvent.click(screen.getByRole('tab', { name: /notifications/i }));
|
|
expect(screen.getByText(/notification settings/i)).toBeInTheDocument();
|
|
expect(screen.getAllByText(/check system status/i).length).toBe(1);
|
|
expect(screen.getByLabelText(/email reminders for follow-ups/i)).toBeInTheDocument();
|
|
expect(screen.getByLabelText(/email alerts for ghosted jobs/i)).toBeInTheDocument();
|
|
});
|