test(07-05): add failing VehicleTable tests (RED — DKV-03)
Tessera CI/CD / Lint & Type Check (push) Failing after 39s
Tessera CI/CD / Tests (push) Has been skipped
Tessera CI/CD / Build & Deploy (push) Has been skipped

- Covers: render list, empty state, delete with confirm, inline edit + save
- Mocks @/lib/dkv-api + next-intl (mirrors calendar-settings.test.tsx pattern)
- Verifies all 4 UI-SPEC aria-labels on icon-only action buttons
This commit is contained in:
2026-06-27 00:38:38 +02:00
parent 45e9a3591f
commit c739d2788b
@@ -0,0 +1,204 @@
import { cleanup, render, screen, waitFor, fireEvent } from '@testing-library/react';
import { afterEach, describe, expect, it, vi } from 'vitest';
// Mock next-intl — echo keys as short strings
vi.mock('next-intl', () => ({
useTranslations: (ns: string) => (key: string, params?: Record<string, unknown>) => {
const map: Record<string, Record<string, string>> = {
dkvFleet: {
emptyVehicles: 'Keine Fahrzeuge hinterlegt',
emptyVehiclesBody: 'Füge Fahrzeuge manuell hinzu.',
vehicleCount: `${params?.count ?? 0} Fahrzeuge`,
addVehicle: 'Fahrzeug hinzufügen',
editVehicle: 'Fahrzeug bearbeiten',
deleteVehicle: 'Fahrzeug löschen',
deleteVehicleBody: `Fahrzeug ${params?.plate ?? ''} wird gelöscht.`,
importCsv: 'CSV importieren',
'col.licensePlate': 'Kennzeichen',
'col.brand': 'Marke',
'col.model': 'Modell',
'col.driver': 'Fahrer',
'col.actions': 'Aktionen',
'form.cancelDialog': 'Abbrechen',
'form.save': 'Speichern',
'form.cancel': 'Bearbeitung abbrechen',
},
};
// Support dot-notation keys
const nsMap = map[ns] ?? {};
return nsMap[key] ?? key;
},
}));
// Mock @/lib/dkv-api
const mockFetchVehicles = vi.fn();
const mockCreateVehicle = vi.fn();
const mockUpdateVehicle = vi.fn();
const mockDeleteVehicle = vi.fn();
const mockImportVehiclesCsv = vi.fn();
vi.mock('@/lib/dkv-api', () => ({
fetchVehicles: (...args: unknown[]) => mockFetchVehicles(...args),
createVehicle: (...args: unknown[]) => mockCreateVehicle(...args),
updateVehicle: (...args: unknown[]) => mockUpdateVehicle(...args),
deleteVehicle: (...args: unknown[]) => mockDeleteVehicle(...args),
importVehiclesCsv: (...args: unknown[]) => mockImportVehiclesCsv(...args),
}));
afterEach(() => {
cleanup();
vi.restoreAllMocks();
});
describe('VehicleTable', () => {
it('renders a list of two mocked vehicles (Kennzeichen, Marke, Modell, Fahrer)', async () => {
mockFetchVehicles.mockResolvedValue([
{ id: 'v1', kennzeichen: 'M-AB 123', marke: 'VW', modell: 'Golf', fahrer: 'Hans Müller' },
{ id: 'v2', kennzeichen: 'B-CD 456', marke: 'BMW', modell: '3er', fahrer: 'Anna Schmidt' },
]);
const { VehicleTable } = await import(
'@/app/(portal)/modules/dkv-fleet/settings/components/VehicleTable'
);
render(<VehicleTable />);
await waitFor(() => {
expect(screen.getByText('M-AB 123')).toBeInTheDocument();
});
expect(screen.getByText('B-CD 456')).toBeInTheDocument();
expect(screen.getByText('VW')).toBeInTheDocument();
expect(screen.getByText('BMW')).toBeInTheDocument();
expect(screen.getByText('Hans Müller')).toBeInTheDocument();
expect(screen.getByText('Anna Schmidt')).toBeInTheDocument();
});
it('shows the empty-state heading when fetchVehicles resolves []', async () => {
mockFetchVehicles.mockResolvedValue([]);
const { VehicleTable } = await import(
'@/app/(portal)/modules/dkv-fleet/settings/components/VehicleTable'
);
render(<VehicleTable />);
await waitFor(() => {
expect(screen.getByText('Keine Fahrzeuge hinterlegt')).toBeInTheDocument();
});
});
it('clicking trash icon opens confirm dialog; confirming calls deleteVehicle with vehicle id', async () => {
mockFetchVehicles.mockResolvedValue([
{ id: 'v1', kennzeichen: 'M-AB 123', marke: 'VW', modell: 'Golf', fahrer: 'Hans Müller' },
]);
mockDeleteVehicle.mockResolvedValue(undefined);
mockFetchVehicles.mockResolvedValueOnce([
{ id: 'v1', kennzeichen: 'M-AB 123', marke: 'VW', modell: 'Golf', fahrer: 'Hans Müller' },
]);
const { VehicleTable } = await import(
'@/app/(portal)/modules/dkv-fleet/settings/components/VehicleTable'
);
render(<VehicleTable />);
await waitFor(() => {
expect(screen.getByText('M-AB 123')).toBeInTheDocument();
});
// Click the delete (trash) icon button
const deleteBtn = screen.getByRole('button', { name: 'Fahrzeug löschen' });
fireEvent.click(deleteBtn);
// Confirm dialog should appear
await waitFor(() => {
expect(screen.getByText(/wird gelöscht/i)).toBeInTheDocument();
});
// Confirm the deletion
const confirmBtn = screen.getByRole('button', { name: 'Löschen' });
fireEvent.click(confirmBtn);
await waitFor(() => {
expect(mockDeleteVehicle).toHaveBeenCalledWith('v1');
});
});
it('clicking pencil icon switches row to edit mode; saving calls updateVehicle', async () => {
mockFetchVehicles.mockResolvedValue([
{ id: 'v1', kennzeichen: 'M-AB 123', marke: 'VW', modell: 'Golf', fahrer: 'Hans Müller' },
]);
mockUpdateVehicle.mockResolvedValue({
id: 'v1',
kennzeichen: 'M-AB 123',
marke: 'VW',
modell: 'Golf',
fahrer: 'Max Mustermann',
});
// fetchVehicles called again after update
mockFetchVehicles.mockResolvedValue([
{ id: 'v1', kennzeichen: 'M-AB 123', marke: 'VW', modell: 'Golf', fahrer: 'Max Mustermann' },
]);
const { VehicleTable } = await import(
'@/app/(portal)/modules/dkv-fleet/settings/components/VehicleTable'
);
render(<VehicleTable />);
await waitFor(() => {
expect(screen.getByText('Hans Müller')).toBeInTheDocument();
});
// Click edit (pencil) icon
const editBtn = screen.getByRole('button', { name: 'Fahrzeug bearbeiten' });
fireEvent.click(editBtn);
// Row should now be in edit mode — find the Fahrer input
await waitFor(() => {
const fahrerInputs = screen.getAllByRole('textbox');
expect(fahrerInputs.length).toBeGreaterThan(0);
});
// Change the Fahrer field
const inputs = screen.getAllByRole('textbox');
// Fahrer is the 4th input (Kennzeichen, Marke, Modell, Fahrer)
const fahrerInput = inputs.find((i) => (i as HTMLInputElement).value === 'Hans Müller');
expect(fahrerInput).toBeTruthy();
fireEvent.change(fahrerInput!, { target: { value: 'Max Mustermann' } });
// Click save (checkmark) button
const saveRowBtn = screen.getByRole('button', { name: 'Änderungen speichern' });
fireEvent.click(saveRowBtn);
await waitFor(() => {
expect(mockUpdateVehicle).toHaveBeenCalledWith('v1', expect.objectContaining({
fahrer: 'Max Mustermann',
}));
});
});
it('action buttons carry the UI-SPEC aria-labels', async () => {
mockFetchVehicles.mockResolvedValue([
{ id: 'v1', kennzeichen: 'M-AB 123', marke: 'VW', modell: 'Golf', fahrer: 'Hans Müller' },
]);
const { VehicleTable } = await import(
'@/app/(portal)/modules/dkv-fleet/settings/components/VehicleTable'
);
render(<VehicleTable />);
await waitFor(() => {
expect(screen.getByText('M-AB 123')).toBeInTheDocument();
});
// View mode: pencil + trash
expect(screen.getByRole('button', { name: 'Fahrzeug bearbeiten' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Fahrzeug löschen' })).toBeInTheDocument();
// Switch to edit mode
fireEvent.click(screen.getByRole('button', { name: 'Fahrzeug bearbeiten' }));
await waitFor(() => {
expect(screen.getByRole('button', { name: 'Änderungen speichern' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Bearbeitung abbrechen' })).toBeInTheDocument();
});
});
});