Compare commits
7 Commits
6de7f5a9b5
...
7c66dcad3a
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c66dcad3a | |||
| 01a321caa9 | |||
| 2d1604492d | |||
| 3742364956 | |||
| 36dfea34cc | |||
| 66525484a6 | |||
| e5614ccf30 |
@@ -53,4 +53,11 @@ describe('ProgressSidebar', () => {
|
|||||||
expect(screen.getByTestId('step-2')).not.toHaveAttribute('aria-current');
|
expect(screen.getByTestId('step-2')).not.toHaveAttribute('aria-current');
|
||||||
expect(screen.getByTestId('step-3')).not.toHaveAttribute('aria-current');
|
expect(screen.getByTestId('step-3')).not.toHaveAttribute('aria-current');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('step 3 active: steps 1 and 2 are both completed', () => {
|
||||||
|
render(ProgressSidebar, { props: { currentStep: 3 } });
|
||||||
|
expect(screen.getByTestId('step-1')).toHaveAttribute('data-state', 'completed');
|
||||||
|
expect(screen.getByTestId('step-2')).toHaveAttribute('data-state', 'completed');
|
||||||
|
expect(screen.getByTestId('step-3')).toHaveAttribute('aria-current', 'step');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -38,8 +38,8 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<form method="POST" novalidate use:enhance onsubmit={handleSubmit}>
|
<form method="POST" novalidate use:enhance onsubmit={handleSubmit}>
|
||||||
<h1 class="mb-[8px] font-['var(--font-display)'] text-[24px] font-semibold">Haushalt benennen</h1>
|
<h1 class="mb-[8px] font-[var(--font-display)] text-[18px] font-medium md:text-[28px]">Haushalt benennen</h1>
|
||||||
<p class="mb-[24px] text-[14px] text-[var(--color-text-muted)]">
|
<p class="mb-[24px] text-[12px] text-[var(--color-text-muted)] md:text-[14px]">
|
||||||
Gib deinem Haushalt einen Namen, damit du ihn leicht wiederfindest.
|
Gib deinem Haushalt einen Namen, damit du ihn leicht wiederfindest.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,9 @@ describe('HouseholdSetupForm', () => {
|
|||||||
const user = userEvent.setup();
|
const user = userEvent.setup();
|
||||||
render(HouseholdSetupForm);
|
render(HouseholdSetupForm);
|
||||||
|
|
||||||
// override disabled to allow submit attempt by typing then clearing
|
// Type then clear: sets touched=true, which makes the $derived error visible
|
||||||
|
// as soon as the field is empty. The button is disabled so the click is a no-op,
|
||||||
|
// but the error is already shown from the touched+empty state.
|
||||||
const input = screen.getByLabelText('Haushaltsname');
|
const input = screen.getByLabelText('Haushaltsname');
|
||||||
await user.type(input, 'a');
|
await user.type(input, 'a');
|
||||||
await user.clear(input);
|
await user.clear(input);
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ export const actions = {
|
|||||||
return fail(400, { errors: { name: 'Haushaltsname ist erforderlich' }, name: '' });
|
return fail(400, { errors: { name: 'Haushaltsname ist erforderlich' }, name: '' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (name.length > 100) {
|
||||||
|
return fail(400, { errors: { name: 'Haushaltsname darf maximal 100 Zeichen lang sein' }, name });
|
||||||
|
}
|
||||||
|
|
||||||
const api = apiClient(fetch);
|
const api = apiClient(fetch);
|
||||||
const { data, error } = await api.POST('/v1/households', {
|
const { data, error } = await api.POST('/v1/households', {
|
||||||
body: { name }
|
body: { name }
|
||||||
|
|||||||
@@ -18,7 +18,6 @@
|
|||||||
<!-- Desktop progress sidebar — hidden on mobile -->
|
<!-- Desktop progress sidebar — hidden on mobile -->
|
||||||
<aside
|
<aside
|
||||||
class="hidden md:flex w-[300px] flex-shrink-0 flex-col bg-[var(--color-surface)] border-r border-[var(--color-border)] p-[40px_28px]"
|
class="hidden md:flex w-[300px] flex-shrink-0 flex-col bg-[var(--color-surface)] border-r border-[var(--color-border)] p-[40px_28px]"
|
||||||
aria-hidden="true"
|
|
||||||
>
|
>
|
||||||
<ProgressSidebar currentStep={1} />
|
<ProgressSidebar currentStep={1} />
|
||||||
</aside>
|
</aside>
|
||||||
|
|||||||
@@ -71,8 +71,7 @@ describe('household setup — form action', () => {
|
|||||||
function mockSuccess() {
|
function mockSuccess() {
|
||||||
return {
|
return {
|
||||||
data: { data: { id: 'hh-123', name: 'Smith family', members: [] } },
|
data: { data: { id: 'hh-123', name: 'Smith family', members: [] } },
|
||||||
error: undefined,
|
error: undefined
|
||||||
response: { headers: { get: vi.fn().mockReturnValue(null) } }
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,6 +122,28 @@ describe('household setup — form action', () => {
|
|||||||
expect(result.data.name).toBe('');
|
expect(result.data.name).toBe('');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns fail(400) when name exceeds 100 characters', async () => {
|
||||||
|
const longName = 'a'.repeat(101);
|
||||||
|
const result = await actions.default(createRequest({ name: longName }));
|
||||||
|
|
||||||
|
expect(result.status).toBe(400);
|
||||||
|
expect(result.data.errors.name).toBeTruthy();
|
||||||
|
expect(mockPost).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('accepts name at exactly 100 characters', async () => {
|
||||||
|
mockPost.mockResolvedValue(mockSuccess());
|
||||||
|
const maxName = 'a'.repeat(100);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await actions.default(createRequest({ name: maxName }));
|
||||||
|
} catch {
|
||||||
|
// redirect throws
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(mockPost).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
it('returns fail with form error on API failure', async () => {
|
it('returns fail with form error on API failure', async () => {
|
||||||
mockPost.mockResolvedValue({
|
mockPost.mockResolvedValue({
|
||||||
data: undefined,
|
data: undefined,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ vi.mock('$app/forms', () => ({
|
|||||||
describe('household setup page', () => {
|
describe('household setup page', () => {
|
||||||
it('renders the form heading', () => {
|
it('renders the form heading', () => {
|
||||||
render(Page);
|
render(Page);
|
||||||
expect(screen.getByText('Haushalt benennen')).toBeInTheDocument();
|
expect(screen.getByRole('heading', { name: 'Haushalt benennen' })).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders the household name input', () => {
|
it('renders the household name input', () => {
|
||||||
|
|||||||
@@ -1,7 +1 @@
|
|||||||
import '@testing-library/jest-dom/vitest';
|
import '@testing-library/jest-dom/vitest';
|
||||||
import { configure } from '@testing-library/dom';
|
|
||||||
|
|
||||||
// Exclude elements inside aria-hidden containers from text queries,
|
|
||||||
// so that visually-hidden sidebars (e.g. ProgressSidebar in onboarding pages)
|
|
||||||
// don't create duplicate text matches when the same text appears in the main content.
|
|
||||||
configure({ defaultIgnore: 'script, style, [aria-hidden="true"] *' });
|
|
||||||
|
|||||||
Reference in New Issue
Block a user