mirror of
https://github.com/s-frick/effigenix.git
synced 2026-03-28 17:04:49 +01:00
phase 0
This commit is contained in:
parent
c84629cc4e
commit
6a672705c2
4 changed files with 285 additions and 6 deletions
|
|
@ -0,0 +1,76 @@
|
|||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { render } from 'ink-testing-library';
|
||||
import React from 'react';
|
||||
import { CategoryCreateScreen } from '../../../components/masterdata/categories/CategoryCreateScreen.js';
|
||||
import { useCategories } from '../../../hooks/useCategories.js';
|
||||
|
||||
vi.mock('../../../state/navigation-context.js', () => ({
|
||||
useNavigation: () => ({
|
||||
replace: vi.fn(),
|
||||
back: vi.fn(),
|
||||
navigate: vi.fn(),
|
||||
current: 'category-create',
|
||||
params: {},
|
||||
canGoBack: true,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('../../../hooks/useCategories.js', () => ({
|
||||
useCategories: vi.fn(),
|
||||
}));
|
||||
|
||||
const createCategoryMock = vi.fn();
|
||||
|
||||
const defaultState = {
|
||||
categories: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
fetchCategories: vi.fn(),
|
||||
createCategory: createCategoryMock,
|
||||
updateCategory: vi.fn(),
|
||||
deleteCategory: vi.fn(),
|
||||
clearError: vi.fn(),
|
||||
};
|
||||
|
||||
describe('TC-CAT: Produktkategorie-Formular', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.mocked(useCategories).mockReturnValue(defaultState);
|
||||
});
|
||||
|
||||
it('TC-CAT-01: Rendert Formular korrekt (Happy-Path-Vorbedingung)', () => {
|
||||
const { lastFrame } = render(React.createElement(CategoryCreateScreen));
|
||||
expect(lastFrame()).toContain('Neue Produktkategorie');
|
||||
expect(lastFrame()).toContain('Name *');
|
||||
expect(lastFrame()).toContain('Beschreibung');
|
||||
});
|
||||
|
||||
it('TC-CAT-04: Doppelter Name → API-Fehler wird angezeigt', () => {
|
||||
vi.mocked(useCategories).mockReturnValue({
|
||||
...defaultState,
|
||||
error: 'Kategorie mit diesem Namen existiert bereits.',
|
||||
});
|
||||
const { lastFrame } = render(React.createElement(CategoryCreateScreen));
|
||||
expect(lastFrame()).toContain('Kategorie mit diesem Namen existiert bereits.');
|
||||
});
|
||||
|
||||
it('TC-CAT-06: Leerer Name → Fehlermeldung, kein API-Call', async () => {
|
||||
createCategoryMock.mockResolvedValue(null);
|
||||
vi.mocked(useCategories).mockReturnValue({ ...defaultState, createCategory: createCategoryMock });
|
||||
|
||||
const { stdin, lastFrame } = render(React.createElement(CategoryCreateScreen));
|
||||
// Warten bis useInput-Effects (ink 5, readable-Listener) registriert sind
|
||||
await new Promise((r) => setTimeout(r, 20));
|
||||
|
||||
// Enter auf Feld 0 (name) → springt zu Feld 1 (description)
|
||||
stdin.write('\r');
|
||||
// Warten bis React re-rendert (description bekommt focus=true)
|
||||
await new Promise((r) => setTimeout(r, 30));
|
||||
// Enter auf Feld 1 (description, letztes Feld) → löst handleSubmit aus
|
||||
stdin.write('\r');
|
||||
await new Promise((r) => setTimeout(r, 50));
|
||||
|
||||
expect(lastFrame()).toContain('Name ist erforderlich');
|
||||
expect(createCategoryMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,90 @@
|
|||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { render } from 'ink-testing-library';
|
||||
import React from 'react';
|
||||
import { SupplierCreateScreen } from '../../../components/masterdata/suppliers/SupplierCreateScreen.js';
|
||||
import { useSuppliers } from '../../../hooks/useSuppliers.js';
|
||||
|
||||
vi.mock('../../../state/navigation-context.js', () => ({
|
||||
useNavigation: () => ({
|
||||
replace: vi.fn(),
|
||||
back: vi.fn(),
|
||||
navigate: vi.fn(),
|
||||
current: 'supplier-create',
|
||||
params: {},
|
||||
canGoBack: true,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('../../../hooks/useSuppliers.js', () => ({
|
||||
useSuppliers: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('../../../utils/api-client.js', () => ({
|
||||
client: {
|
||||
countries: {
|
||||
search: vi.fn().mockResolvedValue([]),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
const createSupplierMock = vi.fn();
|
||||
|
||||
const defaultState = {
|
||||
suppliers: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
fetchSuppliers: vi.fn(),
|
||||
createSupplier: createSupplierMock,
|
||||
updateSupplier: vi.fn(),
|
||||
activateSupplier: vi.fn(),
|
||||
deactivateSupplier: vi.fn(),
|
||||
rateSupplier: vi.fn(),
|
||||
addCertificate: vi.fn(),
|
||||
removeCertificate: vi.fn(),
|
||||
clearError: vi.fn(),
|
||||
};
|
||||
|
||||
describe('TC-SUP: Lieferanten-Formular', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.mocked(useSuppliers).mockReturnValue(defaultState);
|
||||
});
|
||||
|
||||
it('TC-SUP-01: Rendert Formular mit Pflichtfeld-Labels', () => {
|
||||
const { lastFrame } = render(React.createElement(SupplierCreateScreen));
|
||||
expect(lastFrame()).toContain('Neuer Lieferant');
|
||||
expect(lastFrame()).toContain('Name *');
|
||||
expect(lastFrame()).toContain('Telefon *');
|
||||
});
|
||||
|
||||
it('TC-SUP-02: API-Fehler wird im Formular angezeigt', () => {
|
||||
vi.mocked(useSuppliers).mockReturnValue({
|
||||
...defaultState,
|
||||
error: 'Lieferant mit diesem Namen existiert bereits.',
|
||||
});
|
||||
const { lastFrame } = render(React.createElement(SupplierCreateScreen));
|
||||
expect(lastFrame()).toContain('Lieferant mit diesem Namen existiert bereits.');
|
||||
});
|
||||
|
||||
it('TC-SUP-03: Leerer Name → Fehlermeldung, kein API-Call', async () => {
|
||||
createSupplierMock.mockResolvedValue(null);
|
||||
vi.mocked(useSuppliers).mockReturnValue({ ...defaultState, createSupplier: createSupplierMock });
|
||||
|
||||
const { stdin, lastFrame } = render(React.createElement(SupplierCreateScreen));
|
||||
// Warten bis useInput-Effects (ink 5, readable-Listener) registriert sind
|
||||
await new Promise((r) => setTimeout(r, 20));
|
||||
|
||||
// Tab durch alle 9 Feldübergänge bis zum letzten Feld (paymentDueDays)
|
||||
for (let i = 0; i < 9; i++) {
|
||||
stdin.write('\t');
|
||||
}
|
||||
// Warten bis React re-rendert und das fokussierte Feld aktualisiert
|
||||
await new Promise((r) => setTimeout(r, 30));
|
||||
// Enter auf letztem Feld (paymentDueDays) löst handleSubmit aus
|
||||
stdin.write('\r');
|
||||
await new Promise((r) => setTimeout(r, 50));
|
||||
|
||||
expect(lastFrame()).toContain('Name ist erforderlich');
|
||||
expect(createSupplierMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { render } from 'ink-testing-library';
|
||||
import React from 'react';
|
||||
import { SupplierListScreen } from '../../../components/masterdata/suppliers/SupplierListScreen.js';
|
||||
import { useSuppliers } from '../../../hooks/useSuppliers.js';
|
||||
import type { SupplierDTO } from '@effigenix/api-client';
|
||||
|
||||
vi.mock('../../../state/navigation-context.js', () => ({
|
||||
useNavigation: () => ({
|
||||
replace: vi.fn(),
|
||||
back: vi.fn(),
|
||||
navigate: vi.fn(),
|
||||
current: 'supplier-list',
|
||||
params: {},
|
||||
canGoBack: true,
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('../../../hooks/useSuppliers.js', () => ({
|
||||
useSuppliers: vi.fn(),
|
||||
}));
|
||||
|
||||
const mockSuppliers: SupplierDTO[] = [
|
||||
{
|
||||
id: '1',
|
||||
name: 'Frisch AG',
|
||||
status: 'ACTIVE',
|
||||
rating: null,
|
||||
certificates: [],
|
||||
} as unknown as SupplierDTO,
|
||||
{
|
||||
id: '2',
|
||||
name: 'Alt GmbH',
|
||||
status: 'INACTIVE',
|
||||
rating: null,
|
||||
certificates: [],
|
||||
} as unknown as SupplierDTO,
|
||||
];
|
||||
|
||||
const defaultState = {
|
||||
suppliers: mockSuppliers,
|
||||
loading: false,
|
||||
error: null,
|
||||
fetchSuppliers: vi.fn(),
|
||||
createSupplier: vi.fn(),
|
||||
updateSupplier: vi.fn(),
|
||||
activateSupplier: vi.fn(),
|
||||
deactivateSupplier: vi.fn(),
|
||||
rateSupplier: vi.fn(),
|
||||
addCertificate: vi.fn(),
|
||||
removeCertificate: vi.fn(),
|
||||
clearError: vi.fn(),
|
||||
};
|
||||
|
||||
describe('TC-SUP-06: Lieferantenliste – Filter', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
vi.mocked(useSuppliers).mockReturnValue(defaultState);
|
||||
});
|
||||
|
||||
it('Zeigt initial alle Lieferanten (Filter: Alle)', () => {
|
||||
const { lastFrame } = render(React.createElement(SupplierListScreen));
|
||||
expect(lastFrame()).toContain('Lieferanten');
|
||||
expect(lastFrame()).toContain('Alle');
|
||||
expect(lastFrame()).toContain('Frisch AG');
|
||||
expect(lastFrame()).toContain('Alt GmbH');
|
||||
});
|
||||
|
||||
it('[A] filtert auf aktive Lieferanten', async () => {
|
||||
const { stdin, lastFrame } = render(React.createElement(SupplierListScreen));
|
||||
await new Promise((r) => setTimeout(r, 20));
|
||||
|
||||
stdin.write('A');
|
||||
await new Promise((r) => setTimeout(r, 30));
|
||||
|
||||
expect(lastFrame()).toContain('Aktiv');
|
||||
expect(lastFrame()).toContain('Frisch AG');
|
||||
expect(lastFrame()).not.toContain('Alt GmbH');
|
||||
});
|
||||
|
||||
it('[I] filtert auf inaktive Lieferanten', async () => {
|
||||
const { stdin, lastFrame } = render(React.createElement(SupplierListScreen));
|
||||
await new Promise((r) => setTimeout(r, 20));
|
||||
|
||||
stdin.write('I');
|
||||
await new Promise((r) => setTimeout(r, 30));
|
||||
|
||||
expect(lastFrame()).toContain('Inaktiv');
|
||||
expect(lastFrame()).toContain('Alt GmbH');
|
||||
expect(lastFrame()).not.toContain('Frisch AG');
|
||||
});
|
||||
|
||||
it('[a] setzt Filter zurück auf Alle', async () => {
|
||||
const { stdin, lastFrame } = render(React.createElement(SupplierListScreen));
|
||||
await new Promise((r) => setTimeout(r, 20));
|
||||
|
||||
// Erst auf Aktiv filtern, dann zurücksetzen
|
||||
stdin.write('A');
|
||||
await new Promise((r) => setTimeout(r, 30));
|
||||
stdin.write('a');
|
||||
await new Promise((r) => setTimeout(r, 30));
|
||||
|
||||
expect(lastFrame()).toContain('Alle');
|
||||
expect(lastFrame()).toContain('Frisch AG');
|
||||
expect(lastFrame()).toContain('Alt GmbH');
|
||||
});
|
||||
|
||||
it('Zeigt Lade-Spinner wenn loading=true', () => {
|
||||
vi.mocked(useSuppliers).mockReturnValue({ ...defaultState, loading: true, suppliers: [] });
|
||||
const { lastFrame } = render(React.createElement(SupplierListScreen));
|
||||
expect(lastFrame()).toContain('Lade Lieferanten');
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue