test(7.1): add tests for untested settings pages
- SettingsGroups: page render, create/edit buttons, group list - SettingsCurrencies: page render, form, API calls, error display - SettingsTaxes: page render, form, API calls, error display - SettingsSequences: page render, form, API calls, error display - SettingsNotifications: page render, notification types, toggles - SettingsPlugins: page render, install/activate/deactivate buttons - SettingsSystem: page render, form fields, save button All 82 settings tests passing.
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
import React from 'react';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import { SettingsCurrenciesPage } from '@/pages/SettingsCurrencies';
|
||||
|
||||
const mockCurrencies = [
|
||||
{ id: 'c1', code: 'EUR', name: 'Euro', symbol: '€', is_default: true },
|
||||
{ id: 'c2', code: 'USD', name: 'US Dollar', symbol: '$', is_default: false },
|
||||
];
|
||||
|
||||
const apiGetMock = vi.fn();
|
||||
const apiPostMock = vi.fn();
|
||||
const apiPatchMock = vi.fn();
|
||||
const apiDeleteMock = vi.fn();
|
||||
|
||||
vi.mock('@/api/client', () => ({
|
||||
apiGet: (...args: any[]) => apiGetMock(...args),
|
||||
apiPost: (...args: any[]) => apiPostMock(...args),
|
||||
apiPatch: (...args: any[]) => apiPatchMock(...args),
|
||||
apiDelete: (...args: any[]) => apiDeleteMock(...args),
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
apiGetMock.mockResolvedValue({ items: mockCurrencies, total: 2 });
|
||||
apiPostMock.mockResolvedValue({ id: 'c3', code: 'GBP', name: 'British Pound', symbol: '£', is_default: false });
|
||||
apiPatchMock.mockResolvedValue({});
|
||||
apiDeleteMock.mockResolvedValue({});
|
||||
});
|
||||
|
||||
describe('SettingsCurrenciesPage', () => {
|
||||
it('renders currencies page', async () => {
|
||||
render(<SettingsCurrenciesPage />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('settings-currencies')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders add currency button', async () => {
|
||||
render(<SettingsCurrenciesPage />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('settings-currencies')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText(/währung hinzufügen|add currency/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders currency list from API', async () => {
|
||||
render(<SettingsCurrenciesPage />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('EUR')).toBeInTheDocument();
|
||||
expect(screen.getByText('USD')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('shows form when add button clicked', async () => {
|
||||
render(<SettingsCurrenciesPage />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('settings-currencies')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(screen.getByText(/währung hinzufügen|add currency/i));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('button', { name: /speichern|^save$/i })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('displays error when API fails', async () => {
|
||||
apiGetMock.mockRejectedValueOnce(new Error('Network error'));
|
||||
render(<SettingsCurrenciesPage />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Network error')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('calls apiPost on valid submit', async () => {
|
||||
render(<SettingsCurrenciesPage />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('settings-currencies')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(screen.getByText(/währung hinzufügen|add currency/i));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('button', { name: /speichern|^save$/i })).toBeInTheDocument();
|
||||
});
|
||||
const inputs = screen.getAllByRole('textbox');
|
||||
fireEvent.change(inputs[0], { target: { value: 'GBP' } });
|
||||
fireEvent.change(inputs[1], { target: { value: '£' } });
|
||||
fireEvent.change(inputs[2], { target: { value: 'British Pound' } });
|
||||
fireEvent.click(screen.getByRole('button', { name: /speichern|^save$/i }));
|
||||
await waitFor(() => {
|
||||
expect(apiPostMock).toHaveBeenCalledWith('/currencies', expect.objectContaining({ code: 'GBP' }));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,86 @@
|
||||
import React from 'react';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { SettingsGroupsPage } from '@/pages/SettingsGroups';
|
||||
|
||||
const mockGroups = [
|
||||
{ id: 'g1', name: 'Admins', description: 'Admin group', permissions: { all: true }, denied_permissions: [], field_permissions: {}, member_count: 2 },
|
||||
{ id: 'g2', name: 'Users', description: 'Regular users', permissions: { contacts: { read: true } }, denied_permissions: ['contacts:delete'], field_permissions: {}, member_count: 5 },
|
||||
];
|
||||
|
||||
const mockPermissions = [
|
||||
{ key: 'contacts:read', label: 'Contacts Read', module: 'contacts' },
|
||||
{ key: 'contacts:write', label: 'Contacts Write', module: 'contacts' },
|
||||
{ key: 'contacts:delete', label: 'Contacts Delete', module: 'contacts' },
|
||||
];
|
||||
|
||||
vi.mock('@/api/hooks', () => ({
|
||||
useGroups: () => ({ data: { items: mockGroups }, isLoading: false, isError: false, error: null, refetch: vi.fn() }),
|
||||
useCreateGroup: () => ({ mutateAsync: vi.fn().mockResolvedValue({ id: 'g3' }), isPending: false }),
|
||||
useUpdateGroup: () => ({ mutateAsync: vi.fn(), isPending: false }),
|
||||
useDeleteGroup: () => ({ mutateAsync: vi.fn(), isPending: false }),
|
||||
usePermissions: () => ({ data: mockPermissions, isLoading: false }),
|
||||
useGroupMembers: () => ({ data: { items: [] }, isLoading: false }),
|
||||
useAddGroupMember: () => ({ mutateAsync: vi.fn(), isPending: false }),
|
||||
useRemoveGroupMember: () => ({ mutateAsync: vi.fn(), isPending: false }),
|
||||
useUsers: () => ({ data: { items: [] }, isLoading: false }),
|
||||
}));
|
||||
|
||||
vi.mock('@/components/ui/Toast', () => ({
|
||||
useToast: () => ({ success: vi.fn(), error: vi.fn(), info: vi.fn() }),
|
||||
}));
|
||||
|
||||
function renderPage() {
|
||||
return render(<MemoryRouter><SettingsGroupsPage /></MemoryRouter>);
|
||||
}
|
||||
|
||||
describe('SettingsGroupsPage', () => {
|
||||
it('renders groups page', () => {
|
||||
renderPage();
|
||||
expect(screen.getByTestId('settings-groups-page')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders create group button', () => {
|
||||
renderPage();
|
||||
expect(screen.getByTestId('create-group-btn')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders existing groups', () => {
|
||||
renderPage();
|
||||
expect(screen.getByText('Admins')).toBeInTheDocument();
|
||||
expect(screen.getByText('Users')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders edit button for each group', () => {
|
||||
renderPage();
|
||||
expect(screen.getByTestId('edit-group-g1')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('edit-group-g2')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders delete button for each group', () => {
|
||||
renderPage();
|
||||
expect(screen.getByTestId('delete-group-g1')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('delete-group-g2')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('opens create group form when create button clicked', async () => {
|
||||
renderPage();
|
||||
fireEvent.click(screen.getByTestId('create-group-btn'));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('create-group-form')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByTestId('new-group-name')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('new-group-description')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('opens edit group form when edit button clicked', async () => {
|
||||
renderPage();
|
||||
fireEvent.click(screen.getByTestId('edit-group-g1'));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('edit-group-form')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByTestId('tab-details')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('tab-permissions')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,54 @@
|
||||
import React from 'react';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import { SettingsNotificationsPage } from '@/pages/SettingsNotifications';
|
||||
|
||||
const mockNotificationTypes = {
|
||||
items: [
|
||||
{ type_key: 'mail.new', plugin_name: 'mail', category: 'mail', label: 'Neue E-Mail', description: 'Bei neuen E-Mails', is_enabled_by_default: true, is_enabled: true },
|
||||
{ type_key: 'task.overdue', plugin_name: 'tasks', category: 'tasks', label: 'Überfällige Aufgabe', description: 'Aufgabe ist überfällig', is_enabled_by_default: false, is_enabled: false },
|
||||
],
|
||||
};
|
||||
|
||||
vi.mock('@/api/hooks', () => ({
|
||||
useNotificationTypes: () => ({ data: mockNotificationTypes, isLoading: false, error: null }),
|
||||
useUpdateNotificationPreference: () => ({
|
||||
mutate: vi.fn(),
|
||||
isPending: false,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('@/components/ui/Toast', () => ({
|
||||
useToast: () => ({ success: vi.fn(), error: vi.fn(), info: vi.fn() }),
|
||||
}));
|
||||
|
||||
describe('SettingsNotificationsPage', () => {
|
||||
it('renders notifications page', () => {
|
||||
render(<SettingsNotificationsPage />);
|
||||
expect(screen.getByTestId('settings-notifications-page')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders notification type labels', () => {
|
||||
render(<SettingsNotificationsPage />);
|
||||
expect(screen.getByText('Neue E-Mail')).toBeInTheDocument();
|
||||
expect(screen.getByText('Überfällige Aufgabe')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders notification descriptions', () => {
|
||||
render(<SettingsNotificationsPage />);
|
||||
expect(screen.getByText('Bei neuen E-Mails')).toBeInTheDocument();
|
||||
expect(screen.getByText('Aufgabe ist überfällig')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders plugin group headers', () => {
|
||||
render(<SettingsNotificationsPage />);
|
||||
expect(screen.getByText('mail')).toBeInTheDocument();
|
||||
expect(screen.getByText('tasks')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders toggle switches for each notification type', () => {
|
||||
render(<SettingsNotificationsPage />);
|
||||
const switches = screen.getAllByRole('switch');
|
||||
expect(switches.length).toBeGreaterThanOrEqual(2);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,81 @@
|
||||
import React from 'react';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { SettingsPluginsPage } from '@/pages/SettingsPlugins';
|
||||
|
||||
const mockPlugins = [
|
||||
{ name: 'mail-plugin', display_name: 'Mail Plugin', description: 'Mail integration', version: '1.0.0', status: 'active', installed: true, active: true },
|
||||
{ name: 'export-plugin', display_name: 'Export Plugin', description: 'Export functionality', version: '2.1.0', status: 'inactive', installed: true, active: false },
|
||||
{ name: 'beta-plugin', display_name: 'Beta Plugin', description: 'Beta feature', version: '0.1.0', status: 'discovered', installed: false, active: false },
|
||||
];
|
||||
|
||||
vi.mock('@/api/hooks', () => ({
|
||||
usePlugins: () => ({ data: { plugins: mockPlugins }, isLoading: false, isError: false, error: null, refetch: vi.fn() }),
|
||||
useInstallPlugin: () => ({ mutateAsync: vi.fn(), isPending: false }),
|
||||
useActivatePlugin: () => ({ mutateAsync: vi.fn(), isPending: false }),
|
||||
useDeactivatePlugin: () => ({ mutateAsync: vi.fn(), isPending: false }),
|
||||
useUninstallPlugin: () => ({ mutateAsync: vi.fn(), isPending: false }),
|
||||
useUploadPlugin: () => ({ mutateAsync: vi.fn(), isPending: false }),
|
||||
useInstallPluginFromUrl: () => ({ mutateAsync: vi.fn(), isPending: false }),
|
||||
}));
|
||||
|
||||
vi.mock('@/components/ui/Toast', () => ({
|
||||
useToast: () => ({ success: vi.fn(), error: vi.fn(), info: vi.fn() }),
|
||||
}));
|
||||
|
||||
function renderPage() {
|
||||
return render(<MemoryRouter><SettingsPluginsPage /></MemoryRouter>);
|
||||
}
|
||||
|
||||
describe('SettingsPluginsPage', () => {
|
||||
it('renders plugins page', () => {
|
||||
renderPage();
|
||||
expect(screen.getByTestId('settings-plugins-page')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders refresh button', () => {
|
||||
renderPage();
|
||||
expect(screen.getByTestId('refresh-plugins-btn')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders plugin names', () => {
|
||||
renderPage();
|
||||
expect(screen.getByText('Mail Plugin')).toBeInTheDocument();
|
||||
expect(screen.getByText('Export Plugin')).toBeInTheDocument();
|
||||
expect(screen.getByText('Beta Plugin')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders install button for discovered plugins', () => {
|
||||
renderPage();
|
||||
expect(screen.getByTestId('install-plugin-beta-plugin')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders activate button for inactive plugins', () => {
|
||||
renderPage();
|
||||
expect(screen.getByTestId('activate-plugin-export-plugin')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders deactivate button for active plugins', () => {
|
||||
renderPage();
|
||||
expect(screen.getByTestId('deactivate-plugin-mail-plugin')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders uninstall button for installed plugins', () => {
|
||||
renderPage();
|
||||
expect(screen.getByTestId('uninstall-plugin-mail-plugin')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('uninstall-plugin-export-plugin')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders upload section', () => {
|
||||
renderPage();
|
||||
expect(screen.getByTestId('plugin-zip-upload-input')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('plugin-upload-btn')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders URL install section', () => {
|
||||
renderPage();
|
||||
expect(screen.getByTestId('plugin-url-input')).toBeInTheDocument();
|
||||
expect(screen.getByTestId('plugin-install-url-btn')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,91 @@
|
||||
import React from 'react';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import { SettingsSequencesPage } from '@/pages/SettingsSequences';
|
||||
|
||||
const mockSequences = [
|
||||
{ id: 's1', name: 'Rechnungen', prefix: 'RE-', next_number: 1, padding: 4 },
|
||||
{ id: 's2', name: 'Angebote', prefix: 'AN-', next_number: 5, padding: 3 },
|
||||
];
|
||||
|
||||
const apiGetMock = vi.fn();
|
||||
const apiPostMock = vi.fn();
|
||||
const apiPatchMock = vi.fn();
|
||||
const apiDeleteMock = vi.fn();
|
||||
|
||||
vi.mock('@/api/client', () => ({
|
||||
apiGet: (...args: any[]) => apiGetMock(...args),
|
||||
apiPost: (...args: any[]) => apiPostMock(...args),
|
||||
apiPatch: (...args: any[]) => apiPatchMock(...args),
|
||||
apiDelete: (...args: any[]) => apiDeleteMock(...args),
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
apiGetMock.mockResolvedValue({ items: mockSequences, total: 2 });
|
||||
apiPostMock.mockResolvedValue({ id: 's3', name: 'Aufträge', prefix: 'AU-', next_number: 1, padding: 4 });
|
||||
apiPatchMock.mockResolvedValue({});
|
||||
apiDeleteMock.mockResolvedValue({});
|
||||
});
|
||||
|
||||
describe('SettingsSequencesPage', () => {
|
||||
it('renders sequences page', async () => {
|
||||
render(<SettingsSequencesPage />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('settings-sequences')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders add sequence button', async () => {
|
||||
render(<SettingsSequencesPage />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('settings-sequences')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText(/nummernkreis hinzufügen|add sequence/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders sequence list from API', async () => {
|
||||
render(<SettingsSequencesPage />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Rechnungen')).toBeInTheDocument();
|
||||
expect(screen.getByText('Angebote')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('shows form when add button clicked', async () => {
|
||||
render(<SettingsSequencesPage />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('settings-sequences')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(screen.getByText(/nummernkreis hinzufügen|add sequence/i));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('button', { name: /speichern|^save$/i })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('displays error when API fails', async () => {
|
||||
apiGetMock.mockRejectedValueOnce(new Error('Network error'));
|
||||
render(<SettingsSequencesPage />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Network error')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('calls apiPost on valid submit', async () => {
|
||||
render(<SettingsSequencesPage />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('settings-sequences')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(screen.getByText(/nummernkreis hinzufügen|add sequence/i));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('button', { name: /speichern|^save$/i })).toBeInTheDocument();
|
||||
});
|
||||
const inputs = screen.getAllByRole('textbox');
|
||||
fireEvent.change(inputs[0], { target: { value: 'Aufträge' } });
|
||||
fireEvent.change(inputs[1], { target: { value: 'AU-' } });
|
||||
fireEvent.click(screen.getByRole('button', { name: /speichern|^save$/i }));
|
||||
await waitFor(() => {
|
||||
expect(apiPostMock).toHaveBeenCalledWith('/sequences', expect.objectContaining({ name: 'Aufträge' }));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,70 @@
|
||||
import React from 'react';
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { SettingsSystemPage } from '@/pages/SettingsSystem';
|
||||
|
||||
const mockSettings = {
|
||||
company_name: 'Test GmbH',
|
||||
company_legal_form: 'GmbH',
|
||||
company_street: 'Teststr. 1',
|
||||
company_city: 'Berlin',
|
||||
company_zip: '10115',
|
||||
company_country: 'DE',
|
||||
tax_number: '12345',
|
||||
vat_id: 'DE123456789',
|
||||
iban: '',
|
||||
bic: '',
|
||||
bank_name: '',
|
||||
ceo: 'Max Mustermann',
|
||||
trade_register: 'HRB 123',
|
||||
default_currency_id: 'c1',
|
||||
default_tax_id: 't1',
|
||||
invoice_prefix: 'RE-',
|
||||
quote_prefix: 'AN-',
|
||||
payment_terms_days: 14,
|
||||
};
|
||||
|
||||
vi.mock('@/api/hooks', () => ({
|
||||
useSystemSettings: () => ({ data: mockSettings, isLoading: false }),
|
||||
useUpdateSystemSettings: () => ({ mutateAsync: vi.fn(), isPending: false }),
|
||||
useCurrencies: () => ({ data: { items: [{ id: 'c1', code: 'EUR', name: 'Euro', symbol: '€', is_default: true }] } }),
|
||||
useTaxes: () => ({ data: { items: [{ id: 't1', name: 'MwSt 19%', rate: 19, is_default: true }] } }),
|
||||
}));
|
||||
|
||||
vi.mock('@/components/ui/Toast', () => ({
|
||||
useToast: () => ({ success: vi.fn(), error: vi.fn(), info: vi.fn() }),
|
||||
}));
|
||||
|
||||
function renderPage() {
|
||||
return render(<MemoryRouter><SettingsSystemPage /></MemoryRouter>);
|
||||
}
|
||||
|
||||
describe('SettingsSystemPage', () => {
|
||||
it('renders system settings page', () => {
|
||||
renderPage();
|
||||
expect(screen.getByTestId('settings-system-page')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders company name input with loaded value', () => {
|
||||
renderPage();
|
||||
expect(screen.getByTestId('settings-company-name')).toBeInTheDocument();
|
||||
expect(screen.getByDisplayValue('Test GmbH')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders save button', () => {
|
||||
renderPage();
|
||||
expect(screen.getByTestId('settings-system-save')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders currency and tax selects', () => {
|
||||
renderPage();
|
||||
const selects = screen.getAllByRole('combobox');
|
||||
expect(selects.length).toBeGreaterThanOrEqual(2);
|
||||
});
|
||||
|
||||
it('renders payment terms input', () => {
|
||||
renderPage();
|
||||
expect(screen.getByDisplayValue('14')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,92 @@
|
||||
import React from 'react';
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
||||
import { SettingsTaxesPage } from '@/pages/SettingsTaxes';
|
||||
|
||||
const mockTaxes = [
|
||||
{ id: 't1', name: 'MwSt 19%', rate: 19, is_default: true, country: 'DE' },
|
||||
{ id: 't2', name: 'MwSt 7%', rate: 7, is_default: false, country: 'DE' },
|
||||
];
|
||||
|
||||
const apiGetMock = vi.fn();
|
||||
const apiPostMock = vi.fn();
|
||||
const apiPatchMock = vi.fn();
|
||||
const apiDeleteMock = vi.fn();
|
||||
|
||||
vi.mock('@/api/client', () => ({
|
||||
apiGet: (...args: any[]) => apiGetMock(...args),
|
||||
apiPost: (...args: any[]) => apiPostMock(...args),
|
||||
apiPatch: (...args: any[]) => apiPatchMock(...args),
|
||||
apiDelete: (...args: any[]) => apiDeleteMock(...args),
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
apiGetMock.mockResolvedValue({ items: mockTaxes, total: 2 });
|
||||
apiPostMock.mockResolvedValue({ id: 't3', name: 'MwSt 0%', rate: 0, is_default: false, country: null });
|
||||
apiPatchMock.mockResolvedValue({});
|
||||
apiDeleteMock.mockResolvedValue({});
|
||||
});
|
||||
|
||||
describe('SettingsTaxesPage', () => {
|
||||
it('renders taxes page', async () => {
|
||||
render(<SettingsTaxesPage />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('settings-taxes')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders add tax button', async () => {
|
||||
render(<SettingsTaxesPage />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('settings-taxes')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText(/steuersatz hinzufügen|add tax/i)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders tax list from API', async () => {
|
||||
render(<SettingsTaxesPage />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('MwSt 19%')).toBeInTheDocument();
|
||||
expect(screen.getByText('MwSt 7%')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('shows form when add button clicked', async () => {
|
||||
render(<SettingsTaxesPage />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('settings-taxes')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(screen.getByText(/steuersatz hinzufügen|add tax/i));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('button', { name: /speichern|^save$/i })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('displays error when API fails', async () => {
|
||||
apiGetMock.mockRejectedValueOnce(new Error('Network error'));
|
||||
render(<SettingsTaxesPage />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Network error')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('calls apiPost on valid submit', async () => {
|
||||
render(<SettingsTaxesPage />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('settings-taxes')).toBeInTheDocument();
|
||||
});
|
||||
fireEvent.click(screen.getByText(/steuersatz hinzufügen|add tax/i));
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('button', { name: /speichern|^save$/i })).toBeInTheDocument();
|
||||
});
|
||||
const inputs = screen.getAllByRole('textbox');
|
||||
fireEvent.change(inputs[0], { target: { value: 'MwSt 0%' } });
|
||||
const numberInput = screen.getByRole('spinbutton');
|
||||
fireEvent.change(numberInput, { target: { value: '0' } });
|
||||
fireEvent.click(screen.getByRole('button', { name: /speichern|^save$/i }));
|
||||
await waitFor(() => {
|
||||
expect(apiPostMock).toHaveBeenCalledWith('/taxes', expect.objectContaining({ name: 'MwSt 0%' }));
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user