diff --git a/package.json b/package.json index 20ece96..68a4d1d 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint" + "lint": "next lint", + "test": "vitest" }, "keywords": [], "author": "", diff --git a/tests/contact.test.tsx b/tests/contact.test.tsx new file mode 100644 index 0000000..cf3b522 --- /dev/null +++ b/tests/contact.test.tsx @@ -0,0 +1,97 @@ +import { render, screen, fireEvent, waitFor } from '@testing-library/react' +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' +import Contact from '../app/kontakt/page' + +// Mock fetch +const fetchMock = vi.fn() +global.fetch = fetchMock + +// Mock alert +const alertMock = vi.fn() +global.alert = alertMock + +describe('Contact Page', () => { + beforeEach(() => { + fetchMock.mockClear() + alertMock.mockClear() + }) + + it('renders the contact form correctly', () => { + render() + + expect(screen.getByLabelText(/Name \*/i)).toBeInTheDocument() + expect(screen.getByLabelText(/Firma/i)).toBeInTheDocument() + expect(screen.getByLabelText(/E-Mail \*/i)).toBeInTheDocument() + expect(screen.getByLabelText(/Nachricht \*/i)).toBeInTheDocument() + expect(screen.getByRole('button', { name: /Nachricht senden/i })).toBeInTheDocument() + }) + + it('submits the form successfully', async () => { + fetchMock.mockResolvedValueOnce({ + ok: true, + json: async () => ({ success: true }), + }) + + render() + + fireEvent.change(screen.getByLabelText(/Name \*/i), { target: { value: 'John Doe' } }) + fireEvent.change(screen.getByLabelText(/Firma/i), { target: { value: 'Acme Corp' } }) + fireEvent.change(screen.getByLabelText(/E-Mail \*/i), { target: { value: 'john@example.com' } }) + fireEvent.change(screen.getByLabelText(/Nachricht \*/i), { target: { value: 'This is a test message that is long enough.' } }) + + fireEvent.click(screen.getByRole('button', { name: /Nachricht senden/i })) + + await waitFor(() => { + expect(fetchMock).toHaveBeenCalledTimes(1) + expect(fetchMock).toHaveBeenCalledWith('/api/contact', expect.objectContaining({ + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + name: 'John Doe', + company: 'Acme Corp', + email: 'john@example.com', + message: 'This is a test message that is long enough.', + website: '' + }), + })) + }) + + expect(screen.getByText(/Nachricht gesendet/i)).toBeInTheDocument() + expect(screen.getByText(/Vielen Dank für Ihre Anfrage/i)).toBeInTheDocument() + }) + + it('handles submission errors', async () => { + fetchMock.mockResolvedValueOnce({ + ok: false, + json: async () => ({ error: 'Server error' }), + }) + + render() + + fireEvent.change(screen.getByLabelText(/Name \*/i), { target: { value: 'John Doe' } }) + fireEvent.change(screen.getByLabelText(/E-Mail \*/i), { target: { value: 'john@example.com' } }) + fireEvent.change(screen.getByLabelText(/Nachricht \*/i), { target: { value: 'This is a test message that is long enough.' } }) + + fireEvent.click(screen.getByRole('button', { name: /Nachricht senden/i })) + + await waitFor(() => { + expect(alertMock).toHaveBeenCalledWith('Fehler: Server error') + }) + }) + + it('handles network errors', async () => { + fetchMock.mockRejectedValueOnce(new Error('Network error')) + + render() + + fireEvent.change(screen.getByLabelText(/Name \*/i), { target: { value: 'John Doe' } }) + fireEvent.change(screen.getByLabelText(/E-Mail \*/i), { target: { value: 'john@example.com' } }) + fireEvent.change(screen.getByLabelText(/Nachricht \*/i), { target: { value: 'This is a test message that is long enough.' } }) + + fireEvent.click(screen.getByRole('button', { name: /Nachricht senden/i })) + + await waitFor(() => { + expect(alertMock).toHaveBeenCalledWith('Es gab einen Fehler beim Senden Ihrer Nachricht.') + }) + }) +})