implement testsNameItemEditor funktionalität
This commit is contained in:
@@ -11,11 +11,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
export let list: ListItem[] = [];
|
export let list: ListItem[] = [];
|
||||||
export let currentName: string = '';
|
export let currentName: string;
|
||||||
export let onSave: (n: string, o: string) => unknown = () => {};
|
export let onSave: (n: string, o: string) => unknown = () => {};
|
||||||
export let onDelete: (n: string) => unknown = () => {};
|
export let onDelete: (n: string) => unknown = () => {};
|
||||||
|
|
||||||
// lokaler State
|
|
||||||
let localName = currentName;
|
let localName = currentName;
|
||||||
let isEditing = false;
|
let isEditing = false;
|
||||||
let inputRef: HTMLInputElement | null = null;
|
let inputRef: HTMLInputElement | null = null;
|
||||||
|
|||||||
@@ -26,7 +26,6 @@
|
|||||||
// add other properties as needed
|
// add other properties as needed
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) Lokaler, reaktiver State mit $state
|
|
||||||
let crimesList = $state<ListItem[]>(data.crimesList);
|
let crimesList = $state<ListItem[]>(data.crimesList);
|
||||||
let vorgangName: string = data.vorgang.vorgangName;
|
let vorgangName: string = data.vorgang.vorgangName;
|
||||||
const vorgangPIN: string = data.vorgang.vorgangPIN;
|
const vorgangPIN: string = data.vorgang.vorgangPIN;
|
||||||
@@ -38,7 +37,6 @@
|
|||||||
let inProgress = $state(false);
|
let inProgress = $state(false);
|
||||||
let isError = $state(false);
|
let isError = $state(false);
|
||||||
|
|
||||||
//Variable um nur admin UI anzuzeigen
|
|
||||||
let admin = $state(data?.user?.admin);
|
let admin = $state(data?.user?.admin);
|
||||||
|
|
||||||
async function handleSave(newName: string, oldName: string) {
|
async function handleSave(newName: string, oldName: string) {
|
||||||
|
|||||||
144
tests/ComponentNameItemEditor.test.ts
Normal file
144
tests/ComponentNameItemEditor.test.ts
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
import { fireEvent, render, screen } from '@testing-library/svelte';
|
||||||
|
import { describe, expect, it, test, vi } from "vitest";
|
||||||
|
import NameItemEditor from '$lib/components/NameItemEditor.svelte';
|
||||||
|
import { baseData } from './fixtures';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const testCrimesListIndex = 0;
|
||||||
|
const testItem = baseData.crimesList[testCrimesListIndex];
|
||||||
|
const testCurrentName = testItem.name;
|
||||||
|
const testLocalName = 'Fall-C'
|
||||||
|
|
||||||
|
|
||||||
|
describe('NameItemEditor - Funktionalität', () => {
|
||||||
|
const onSave = vi.fn();
|
||||||
|
const onDelete = vi.fn();
|
||||||
|
const baseProps = {
|
||||||
|
list: baseData.crimesList,
|
||||||
|
currentName: testCurrentName,
|
||||||
|
onSave,
|
||||||
|
onDelete}
|
||||||
|
|
||||||
|
test.todo('FocusIn nach Klick auf edit')
|
||||||
|
|
||||||
|
it('zeigt initial Edit/Delete Buttons und aktuellen Namen', () => {
|
||||||
|
render(NameItemEditor, {props: baseProps});
|
||||||
|
|
||||||
|
expect(screen.getByTestId('edit-button')).toBeInTheDocument();
|
||||||
|
expect(screen.getByTestId('delete-button')).toBeInTheDocument();
|
||||||
|
expect(screen.queryByTestId('commit-button')).toBeNull();
|
||||||
|
expect(screen.queryByTestId('cancel-button')).toBeNull();
|
||||||
|
expect(screen.getByText(testCurrentName)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('wechselt zu Commit/Cancel nach Klick auf Edit', async () => {
|
||||||
|
render(NameItemEditor, {props: baseProps});
|
||||||
|
await fireEvent.click(screen.getByTestId('edit-button'));
|
||||||
|
const input = screen.getByTestId('test-input');
|
||||||
|
|
||||||
|
expect(screen.getByTestId('commit-button')).toBeInTheDocument();
|
||||||
|
expect(screen.getByTestId('cancel-button')).toBeInTheDocument();
|
||||||
|
expect(screen.queryByTestId('edit-button')).toBeNull();
|
||||||
|
expect(screen.queryByTestId('delete-button')).toBeNull();
|
||||||
|
expect(screen.getAllByRole('textbox')).toHaveLength(1);
|
||||||
|
expect(input).toHaveValue(testCurrentName);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('zeigt Fehlermeldung bei leerem Namen', async () => {
|
||||||
|
render(NameItemEditor, { props: baseProps });
|
||||||
|
await fireEvent.click(screen.getByTestId('edit-button'));
|
||||||
|
|
||||||
|
const input = screen.getByTestId('test-input');
|
||||||
|
await fireEvent.input(input, { target: { value: '' } });
|
||||||
|
|
||||||
|
expect(screen.getByText('Name darf nicht leer sein.')).toBeInTheDocument();
|
||||||
|
expect(onSave).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('entfernt Fehlermeldung live beim nächsten gültigen Tastendruck', async () => {
|
||||||
|
render(NameItemEditor, {
|
||||||
|
props: {
|
||||||
|
list: baseData.crimesList,
|
||||||
|
currentName: baseData.crimesList[0].name,
|
||||||
|
onSave: vi.fn(),
|
||||||
|
onDelete: vi.fn()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await fireEvent.click(screen.getByTestId('edit-button'));
|
||||||
|
const input = screen.getByTestId('test-input');
|
||||||
|
|
||||||
|
await fireEvent.input(input, { target: { value: '' } });
|
||||||
|
expect(screen.getByText('Name darf nicht leer sein.')).toBeInTheDocument();
|
||||||
|
|
||||||
|
await fireEvent.input(input, { target: { value: 'Fall-C' } });
|
||||||
|
|
||||||
|
expect(screen.queryByText('Name darf nicht leer sein.')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('zeigt Fehlermeldung bei Duplikat', async () => {
|
||||||
|
const duplicateName = baseData.crimesList[1].name;
|
||||||
|
render(NameItemEditor, { props: baseProps });
|
||||||
|
await fireEvent.click(screen.getByTestId('edit-button'));
|
||||||
|
|
||||||
|
const input = screen.getByTestId('test-input');
|
||||||
|
await fireEvent.input(input, { target: { value: duplicateName } });
|
||||||
|
|
||||||
|
expect(screen.getByText('Name existiert bereits.')).toBeInTheDocument();
|
||||||
|
expect(onSave).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ruft onSave korrekt auf bei gültigem Namen', async () => {
|
||||||
|
render(NameItemEditor, { props: baseProps });
|
||||||
|
await fireEvent.click(screen.getByTestId('edit-button'));
|
||||||
|
|
||||||
|
const input = screen.getByTestId('test-input');
|
||||||
|
await fireEvent.input(input, { target: { value: testLocalName } });
|
||||||
|
await fireEvent.click(screen.getByTestId('commit-button'));
|
||||||
|
|
||||||
|
expect(onSave).toHaveBeenCalledWith(testLocalName, testCurrentName);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('ruft onDelete korrekt auf', async () => {
|
||||||
|
render(NameItemEditor, { props: baseProps });
|
||||||
|
await fireEvent.click(screen.getByTestId('delete-button'));
|
||||||
|
|
||||||
|
expect(onDelete).toHaveBeenCalledWith(testCurrentName);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setzt Zustand zurück bei Cancel', async () => {
|
||||||
|
render(NameItemEditor, { props: baseProps });
|
||||||
|
await fireEvent.click(screen.getByTestId('edit-button'));
|
||||||
|
|
||||||
|
const input = screen.getByTestId('test-input');
|
||||||
|
await fireEvent.input(input, { target: { value: 'Zwischentext' } });
|
||||||
|
await fireEvent.click(screen.getByTestId('cancel-button'));
|
||||||
|
|
||||||
|
expect(screen.getByText(testCurrentName)).toBeInTheDocument();
|
||||||
|
expect(screen.getByTestId('edit-button')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('triggert Save bei Enter-Taste', async () => {
|
||||||
|
render(NameItemEditor, { props: baseProps });
|
||||||
|
await fireEvent.click(screen.getByTestId('edit-button'));
|
||||||
|
|
||||||
|
const input = screen.getByTestId('test-input');
|
||||||
|
await fireEvent.input(input, { target: { value: 'ViaEnter' } });
|
||||||
|
await fireEvent.keyDown(input, { key: 'Enter' });
|
||||||
|
|
||||||
|
expect(onSave).toHaveBeenCalledWith('ViaEnter', testCurrentName);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('bricht ab bei Escape-Taste', async () => {
|
||||||
|
render(NameItemEditor, { props: baseProps });
|
||||||
|
await fireEvent.click(screen.getByTestId('edit-button'));
|
||||||
|
|
||||||
|
const input = screen.getByTestId('test-input');
|
||||||
|
await fireEvent.input(input, { target: { value: 'Zwischentext' } });
|
||||||
|
await fireEvent.keyDown(input, { key: 'Escape' });
|
||||||
|
|
||||||
|
expect(screen.getByText(testCurrentName)).toBeInTheDocument();
|
||||||
|
expect(onSave).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import { fireEvent, render } from '@testing-library/svelte';
|
|
||||||
import { describe, expect, it, vi } from "vitest";
|
|
||||||
import NameItemEditor from '$lib/components/NameItemEditor.svelte';
|
|
||||||
import { baseData } from './fixtures';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const testCrimesListIndex = 0;
|
|
||||||
const testItem = baseData.crimesList[testCrimesListIndex];
|
|
||||||
const editedName = baseData.crimeNames[testCrimesListIndex];
|
|
||||||
const currentName = testItem.name;
|
|
||||||
const onSave = vi.fn();
|
|
||||||
const onDelete = vi.fn();
|
|
||||||
|
|
||||||
describe('Button-Anzeige mit Icons', () => {
|
|
||||||
const baseProps = {list: baseData.crimesList, editedName, currentName, onDelete, onSave}
|
|
||||||
it('zeigt Edit/Delete Buttons initial', () => {
|
|
||||||
const { getByTestId, queryByTestId } = render(NameItemEditor, {
|
|
||||||
props: baseProps
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(getByTestId('edit-button')).toBeInTheDocument();
|
|
||||||
expect(getByTestId('delete-button')).toBeInTheDocument();
|
|
||||||
expect(queryByTestId('commit-button')).toBeNull();
|
|
||||||
expect(queryByTestId('cancel-button')).toBeNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('zeigt Commit/Cancel Buttons nach Klick auf Edit', async () => {
|
|
||||||
const { getByTestId, queryByTestId } = render(NameItemEditor, {
|
|
||||||
props: baseProps
|
|
||||||
});
|
|
||||||
|
|
||||||
await fireEvent.click(getByTestId('edit-button'));
|
|
||||||
|
|
||||||
expect(getByTestId('commit-button')).toBeInTheDocument();
|
|
||||||
expect(getByTestId('cancel-button')).toBeInTheDocument();
|
|
||||||
expect(queryByTestId('edit-button')).toBeNull();
|
|
||||||
expect(queryByTestId('delete-button')).toBeNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
// @vitest-environment jsdom
|
|
||||||
import { render, fireEvent, screen, within } from '@testing-library/svelte';
|
import { render, fireEvent, screen, within } from '@testing-library/svelte';
|
||||||
import { describe, it, expect, vi, test } from 'vitest';
|
import { describe, it, expect, vi, test } from 'vitest';
|
||||||
import * as nav from '$app/navigation';
|
import * as nav from '$app/navigation';
|
||||||
@@ -6,7 +5,6 @@ import TatortListPage from '../src/routes/(token-based)/list/[vorgang]/+page.sve
|
|||||||
import { baseData } from './fixtures';
|
import { baseData } from './fixtures';
|
||||||
import { tick } from 'svelte';
|
import { tick } from 'svelte';
|
||||||
|
|
||||||
// Mock für invalidateAll
|
|
||||||
vi.spyOn(nav, 'invalidateAll').mockResolvedValue();
|
vi.spyOn(nav, 'invalidateAll').mockResolvedValue();
|
||||||
global.fetch = vi.fn().mockResolvedValue({ ok: true });
|
global.fetch = vi.fn().mockResolvedValue({ ok: true });
|
||||||
|
|
||||||
@@ -16,7 +14,6 @@ describe('Seite: Vorgangsansicht', () => {
|
|||||||
describe('Szenario: Admin + Liste gefüllt - Funktionalität', () => {
|
describe('Szenario: Admin + Liste gefüllt - Funktionalität', () => {
|
||||||
test.todo('Share Link Link generierung richtig');
|
test.todo('Share Link Link generierung richtig');
|
||||||
|
|
||||||
|
|
||||||
it('führt PUT-Request aus und aktualisiert UI nach onSave', async () => {
|
it('führt PUT-Request aus und aktualisiert UI nach onSave', async () => {
|
||||||
const data = structuredClone(baseData);
|
const data = structuredClone(baseData);
|
||||||
const oldName = data.crimesList[0].name;
|
const oldName = data.crimesList[0].name;
|
||||||
@@ -24,19 +21,15 @@ describe('Seite: Vorgangsansicht', () => {
|
|||||||
|
|
||||||
render(TatortListPage, { props: { data } });
|
render(TatortListPage, { props: { data } });
|
||||||
const listItem = screen.getAllByTestId('test-list-item')[0];
|
const listItem = screen.getAllByTestId('test-list-item')[0];
|
||||||
// teste ob alter Name angezeigt:
|
|
||||||
expect(listItem).toHaveTextContent(oldName);
|
expect(listItem).toHaveTextContent(oldName);
|
||||||
|
|
||||||
// Editmodus
|
|
||||||
await fireEvent.click(within(listItem).getByTestId('edit-button'));
|
await fireEvent.click(within(listItem).getByTestId('edit-button'));
|
||||||
const input = within(listItem).getByTestId('test-input');
|
const input = within(listItem).getByTestId('test-input');
|
||||||
await fireEvent.input(input, { target: { value: newName } });
|
await fireEvent.input(input, { target: { value: newName } });
|
||||||
|
|
||||||
// Commit
|
|
||||||
await fireEvent.click(within(listItem).getByTestId('commit-button'));
|
await fireEvent.click(within(listItem).getByTestId('commit-button'));
|
||||||
await Promise.resolve(); // wartet reaktive Updates ab
|
await tick();
|
||||||
|
|
||||||
// FETCH-CHECK
|
|
||||||
expect(global.fetch).toHaveBeenCalledWith(
|
expect(global.fetch).toHaveBeenCalledWith(
|
||||||
`/api/list/${data.vorgang.vorgangToken}/${oldName}`,
|
`/api/list/${data.vorgang.vorgangToken}/${oldName}`,
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@@ -50,10 +43,7 @@ describe('Seite: Vorgangsansicht', () => {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
// INVALIDATE-CHECK
|
|
||||||
expect(nav.invalidateAll).toHaveBeenCalled();
|
expect(nav.invalidateAll).toHaveBeenCalled();
|
||||||
|
|
||||||
// UI-UPDATE
|
|
||||||
expect(within(listItem).getByText(newName)).toBeInTheDocument();
|
expect(within(listItem).getByText(newName)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -61,26 +51,21 @@ describe('Seite: Vorgangsansicht', () => {
|
|||||||
const testData = structuredClone(baseData);
|
const testData = structuredClone(baseData);
|
||||||
const oldName = testData.crimesList[0].name;
|
const oldName = testData.crimesList[0].name;
|
||||||
|
|
||||||
// Rendern und initiale Liste prüfen
|
|
||||||
render(TatortListPage, { props: { data: testData } });
|
render(TatortListPage, { props: { data: testData } });
|
||||||
const initialItems = screen.getAllByTestId('test-list-item');
|
const initialItems = screen.getAllByTestId('test-list-item');
|
||||||
expect(initialItems).toHaveLength(testData.crimesList.length);
|
expect(initialItems).toHaveLength(testData.crimesList.length);
|
||||||
|
|
||||||
const listItem = screen.getAllByTestId('test-list-item')[0];
|
const listItem = screen.getAllByTestId('test-list-item')[0];
|
||||||
// teste ob alter Name angezeigt:
|
|
||||||
expect(listItem).toHaveTextContent(oldName);
|
expect(listItem).toHaveTextContent(oldName);
|
||||||
// Delete-Button klicken
|
|
||||||
const del = within(listItem).getByTestId('delete-button');
|
const del = within(listItem).getByTestId('delete-button');
|
||||||
expect(del).toBeInTheDocument()
|
expect(del).toBeInTheDocument()
|
||||||
await fireEvent.click(within(listItem).getByTestId('delete-button'));
|
await fireEvent.click(within(listItem).getByTestId('delete-button'));
|
||||||
// auf reaktive Updates warten
|
|
||||||
await tick();
|
await tick();
|
||||||
|
|
||||||
// FETCH-CHECK: URL & Payload
|
let expectedPath = new URL(testData.url).pathname;
|
||||||
// entspricht: new URL(data.url).pathname + '/' + oldName
|
expectedPath += `/${oldName}`
|
||||||
const expectedPath = new URL(testData.url).pathname;
|
|
||||||
expect(global.fetch).toHaveBeenCalledWith(
|
expect(global.fetch).toHaveBeenCalledWith(
|
||||||
`/api${expectedPath}/${oldName}`,
|
`/api${expectedPath}`,
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
@@ -90,14 +75,10 @@ describe('Seite: Vorgangsansicht', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
// INVALIDATE-CHECK
|
|
||||||
expect(nav.invalidateAll).toHaveBeenCalled();
|
expect(nav.invalidateAll).toHaveBeenCalled();
|
||||||
|
const updatedItems = screen.queryAllByTestId('test-list-item');
|
||||||
// UI-UPDATE: Element entfernt
|
expect(updatedItems).toHaveLength(testData.crimesList.length - 1);
|
||||||
const updatedItems = screen.queryAllByTestId('test-list-item');
|
expect(screen.queryByText(oldName)).toBeNull();
|
||||||
expect(updatedItems).toHaveLength(testData.crimesList.length - 1);
|
|
||||||
expect(screen.queryByText(oldName)).toBeNull();
|
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user