Phase 6.3: SettingsForms on RHF + Zod

- SettingsCurrencies: RHF+Zod (code required max 3, name required, symbol required max 5)
- SettingsTaxes: RHF+Zod (name required, rate numeric 0-100, country max 2)
- SettingsSequences: RHF+Zod (name required, padding numeric 1-10)
- SettingsUsers: RHF+Zod (name required, email valid, password min 8)
- SettingsRoles: RHF+Zod for create form (name required)
- SettingsGroups: RHF+Zod for create form (name required, description optional)
- Error display under each field
- Preserved all existing functionality: CRUD, permissions, members
- Added 2 validation tests for SettingsCurrencies
This commit is contained in:
Agent Zero
2026-07-24 00:28:11 +02:00
parent 2931a850c0
commit c334d02989
7 changed files with 306 additions and 109 deletions
@@ -0,0 +1,55 @@
import React from 'react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
vi.mock('@/api/client', () => ({
apiGet: vi.fn().mockResolvedValue({ items: [], total: 0 }),
apiPost: vi.fn().mockResolvedValue({ id: '1', code: 'EUR', name: 'Euro', symbol: '€', is_default: true }),
apiPatch: vi.fn().mockResolvedValue({}),
apiDelete: vi.fn().mockResolvedValue({}),
}));
import { SettingsCurrenciesPage } from '@/pages/SettingsCurrencies';
import { apiPost } from '@/api/client';
beforeEach(() => {
vi.clearAllMocks();
});
describe('SettingsCurrencies validation (RHF + Zod)', () => {
it('shows validation error when code is empty', async () => {
render(<SettingsCurrenciesPage />);
await waitFor(() => {
expect(screen.getByTestId('settings-currencies')).toBeInTheDocument();
});
// Click add button to show form (German locale default in test env)
const addBtn = screen.getByText(/währung hinzufügen|add currency/i);
fireEvent.click(addBtn);
// Submit empty form
const saveBtn = screen.getByRole('button', { name: /speichern|^save$/i });
fireEvent.click(saveBtn);
await waitFor(() => {
const errors = screen.getAllByText(/erforderlich|required/i);
expect(errors.length).toBeGreaterThan(0);
});
});
it('submits successfully with valid data', async () => {
render(<SettingsCurrenciesPage />);
await waitFor(() => {
expect(screen.getByTestId('settings-currencies')).toBeInTheDocument();
});
const addBtn = screen.getByText(/währung hinzufügen|add currency/i);
fireEvent.click(addBtn);
// Fill form — code, symbol, name
const inputs = screen.getAllByRole('textbox');
fireEvent.change(inputs[0], { target: { value: 'EUR' } }); // code
fireEvent.change(inputs[1], { target: { value: '€' } }); // symbol
fireEvent.change(inputs[2], { target: { value: 'Euro' } }); // name
const saveBtn = screen.getByRole('button', { name: /speichern|^save$/i });
fireEvent.click(saveBtn);
await waitFor(() => {
expect(apiPost).toHaveBeenCalledWith('/currencies', expect.objectContaining({ code: 'EUR', name: 'Euro', symbol: '€' }));
});
});
});