diff --git a/frontend/src/routes/(public)/signup/+page.server.ts b/frontend/src/routes/(public)/signup/+page.server.ts index 383c855..8fa6ff5 100644 --- a/frontend/src/routes/(public)/signup/+page.server.ts +++ b/frontend/src/routes/(public)/signup/+page.server.ts @@ -5,17 +5,40 @@ import type { Actions } from './$types'; export const actions = { default: async ({ request, fetch }) => { const formData = await request.formData(); - const displayName = formData.get('displayName') as string; - const email = formData.get('email') as string; - const password = formData.get('password') as string; + const displayName = (formData.get('displayName') ?? '').toString().trim(); + const email = (formData.get('email') ?? '').toString().trim(); + const password = (formData.get('password') ?? '').toString(); + + const errors: Record = {}; + + if (!displayName) { + errors.displayName = 'Name ist erforderlich'; + } + + const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailPattern.test(email)) { + errors.email = 'Ungültige E-Mail-Adresse'; + } + + if (password.length < 8) { + errors.password = 'Mindestens 8 Zeichen'; + } + + if (Object.keys(errors).length > 0) { + return fail(400, { errors, displayName, email }); + } const api = apiClient(fetch); - const { data, error } = await api.POST('/v1/auth/signup', { + const { error } = await api.POST('/v1/auth/signup', { body: { displayName, email, password } }); if (error) { - return fail(400, { error: 'Registrierung fehlgeschlagen. Bitte versuche es erneut.' }); + return fail(400, { + errors: { form: 'Registrierung fehlgeschlagen. Bitte versuche es erneut.' }, + displayName, + email + }); } redirect(303, '/household/setup'); diff --git a/frontend/src/routes/(public)/signup/page.server.test.ts b/frontend/src/routes/(public)/signup/page.server.test.ts index 0a18c07..defc113 100644 --- a/frontend/src/routes/(public)/signup/page.server.test.ts +++ b/frontend/src/routes/(public)/signup/page.server.test.ts @@ -68,6 +68,56 @@ describe('signup form action', () => { } }); + it('rejects empty displayName with validation error', async () => { + const result = await actions.default(createRequest({ + displayName: '', + email: 'sarah@example.com', + password: 'password123' + })); + + expect(result.status).toBe(400); + expect(result.data.errors.displayName).toBe('Name ist erforderlich'); + expect(mockPost).not.toHaveBeenCalled(); + }); + + it('rejects invalid email with validation error', async () => { + const result = await actions.default(createRequest({ + displayName: 'Sarah', + email: 'notanemail', + password: 'password123' + })); + + expect(result.status).toBe(400); + expect(result.data.errors.email).toBe('Ungültige E-Mail-Adresse'); + expect(mockPost).not.toHaveBeenCalled(); + }); + + it('rejects short password with validation error', async () => { + const result = await actions.default(createRequest({ + displayName: 'Sarah', + email: 'sarah@example.com', + password: 'short' + })); + + expect(result.status).toBe(400); + expect(result.data.errors.password).toBe('Mindestens 8 Zeichen'); + expect(mockPost).not.toHaveBeenCalled(); + }); + + it('handles missing form fields without crashing', async () => { + const fd = new FormData(); + const event = { + request: { formData: () => Promise.resolve(fd) }, + fetch: vi.fn(), + cookies: { get: vi.fn(), set: vi.fn() } + } as any; + + const result = await actions.default(event); + expect(result.status).toBe(400); + expect(result.data.errors.displayName).toBe('Name ist erforderlich'); + expect(mockPost).not.toHaveBeenCalled(); + }); + it('returns fail with error message on API error', async () => { mockPost.mockResolvedValue({ data: undefined, @@ -81,5 +131,6 @@ describe('signup form action', () => { })); expect(result.status).toBe(400); + expect(result.data.errors.form).toBe('Registrierung fehlgeschlagen. Bitte versuche es erneut.'); }); });