108 lines
3.9 KiB
TypeScript
108 lines
3.9 KiB
TypeScript
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
import { render, screen } from '@testing-library/svelte';
|
|
import { userEvent } from '@testing-library/user-event';
|
|
import StaplesManager from './StaplesManager.svelte';
|
|
|
|
const mockCategories = [
|
|
{
|
|
id: 'cat-1',
|
|
name: 'Öle & Fette',
|
|
ingredients: [
|
|
{ id: 'ing-1', name: 'Olivenöl', isStaple: true },
|
|
{ id: 'ing-2', name: 'Butter', isStaple: false }
|
|
]
|
|
},
|
|
{
|
|
id: 'cat-2',
|
|
name: 'Gewürze',
|
|
ingredients: [
|
|
{ id: 'ing-3', name: 'Salz', isStaple: true },
|
|
{ id: 'ing-4', name: 'Pfeffer', isStaple: true }
|
|
]
|
|
}
|
|
];
|
|
|
|
describe('StaplesManager', () => {
|
|
beforeEach(() => {
|
|
vi.useFakeTimers();
|
|
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ ok: true }));
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers();
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
it('renders all categories', () => {
|
|
render(StaplesManager, { props: { categories: mockCategories, context: 'onboarding' } });
|
|
expect(screen.getByText('Öle & Fette')).toBeInTheDocument();
|
|
expect(screen.getByText('Gewürze')).toBeInTheDocument();
|
|
});
|
|
|
|
it('renders all chips with correct initial aria-pressed state', () => {
|
|
render(StaplesManager, { props: { categories: mockCategories, context: 'onboarding' } });
|
|
expect(screen.getByRole('button', { name: 'Olivenöl' })).toHaveAttribute('aria-pressed', 'true');
|
|
expect(screen.getByRole('button', { name: 'Butter' })).toHaveAttribute('aria-pressed', 'false');
|
|
expect(screen.getByRole('button', { name: 'Salz' })).toHaveAttribute('aria-pressed', 'true');
|
|
});
|
|
|
|
it('clicking a chip immediately updates aria-pressed (optimistic)', async () => {
|
|
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
|
render(StaplesManager, { props: { categories: mockCategories, context: 'onboarding' } });
|
|
|
|
const butter = screen.getByRole('button', { name: 'Butter' });
|
|
expect(butter).toHaveAttribute('aria-pressed', 'false');
|
|
await user.click(butter);
|
|
expect(butter).toHaveAttribute('aria-pressed', 'true');
|
|
});
|
|
|
|
it('rapid clicks on same chip result in exactly one fetch call after debounce', async () => {
|
|
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
|
render(StaplesManager, { props: { categories: mockCategories, context: 'onboarding' } });
|
|
|
|
const butter = screen.getByRole('button', { name: 'Butter' });
|
|
await user.click(butter);
|
|
await user.click(butter);
|
|
await user.click(butter);
|
|
|
|
expect(fetch).not.toHaveBeenCalled();
|
|
vi.advanceTimersByTime(300);
|
|
await vi.runAllTimersAsync();
|
|
|
|
expect(fetch).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('reverts chip and shows error when PATCH fails', async () => {
|
|
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({ ok: false }));
|
|
const user = userEvent.setup({ advanceTimers: vi.advanceTimersByTime });
|
|
render(StaplesManager, { props: { categories: mockCategories, context: 'onboarding' } });
|
|
|
|
const butter = screen.getByRole('button', { name: 'Butter' });
|
|
await user.click(butter);
|
|
expect(butter).toHaveAttribute('aria-pressed', 'true');
|
|
|
|
vi.advanceTimersByTime(300);
|
|
await vi.runAllTimersAsync();
|
|
|
|
expect(butter).toHaveAttribute('aria-pressed', 'false');
|
|
expect(screen.getByText(/konnte nicht gespeichert werden/i)).toBeInTheDocument();
|
|
});
|
|
|
|
it('uses 2-column grid class in onboarding context', () => {
|
|
render(StaplesManager, { props: { categories: mockCategories, context: 'onboarding' } });
|
|
const grid = screen.getByTestId('category-grid');
|
|
expect(grid.className).toContain('md:grid-cols-2');
|
|
});
|
|
|
|
it('uses 3-column grid class in settings context', () => {
|
|
render(StaplesManager, { props: { categories: mockCategories, context: 'settings' } });
|
|
const grid = screen.getByTestId('category-grid');
|
|
expect(grid.className).toContain('md:grid-cols-3');
|
|
});
|
|
|
|
it('renders without crashing when categories is empty', () => {
|
|
render(StaplesManager, { props: { categories: [], context: 'onboarding' } });
|
|
expect(screen.queryByRole('button')).not.toBeInTheDocument();
|
|
});
|
|
});
|