diff --git a/app/__tests__/components/Contact.test.tsx b/app/__tests__/components/Contact.test.tsx index a853957..5c13912 100644 --- a/app/__tests__/components/Contact.test.tsx +++ b/app/__tests__/components/Contact.test.tsx @@ -1,4 +1,4 @@ -import { render, screen, fireEvent } from '@testing-library/react'; +import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import Contact from '@/app/components/Contact'; import '@testing-library/jest-dom'; @@ -10,20 +10,34 @@ global.fetch = jest.fn(() => ) as jest.Mock; describe('Contact', () => { + beforeAll(() => { + jest.useFakeTimers('modern'); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + it('renders the contact form', () => { render(); - expect(screen.getByPlaceholderText('Name')).toBeInTheDocument(); - expect(screen.getByPlaceholderText('Email')).toBeInTheDocument(); - expect(screen.getByPlaceholderText('Message')).toBeInTheDocument(); + expect(screen.getByPlaceholderText('Your Name')).toBeInTheDocument(); + expect(screen.getByPlaceholderText('you@example.com')).toBeInTheDocument(); + expect(screen.getByPlaceholderText('Your Message...')).toBeInTheDocument(); + expect(screen.getByLabelText('I accept the privacy policy.')).toBeInTheDocument(); }); it('submits the form', async () => { render(); - fireEvent.change(screen.getByPlaceholderText('Name'), { target: { value: 'John Doe' } }); - fireEvent.change(screen.getByPlaceholderText('Email'), { target: { value: 'john@example.com' } }); - fireEvent.change(screen.getByPlaceholderText('Message'), { target: { value: 'Hello!' } }); - fireEvent.click(screen.getByText('Send')); - expect(await screen.findByText('Email sent')).toBeInTheDocument(); + // Fast forward time to ensure the timestamp check passes + jest.advanceTimersByTime(3000); + + fireEvent.change(screen.getByPlaceholderText('Your Name'), { target: { value: 'John Doe' } }); + fireEvent.change(screen.getByPlaceholderText('you@example.com'), { target: { value: 'john@example.com' } }); + fireEvent.change(screen.getByPlaceholderText('Your Message...'), { target: { value: 'Hello!' } }); + fireEvent.click(screen.getByLabelText('I accept the privacy policy.')); + fireEvent.click(screen.getByText('Send Message')); + + await waitFor(() => expect(screen.getByText('Email sent')).toBeInTheDocument()); }); }); \ No newline at end of file diff --git a/app/components/Contact.tsx b/app/components/Contact.tsx index 442b032..b7a7109 100644 --- a/app/components/Contact.tsx +++ b/app/components/Contact.tsx @@ -1,5 +1,6 @@ import React, { useEffect, useState } from "react"; import { sendEmail } from "@/app/utils/send-email"; +import Link from "next/link"; export type ContactFormData = { name: string; @@ -34,7 +35,7 @@ export default function Contact() { const form = e.currentTarget as HTMLFormElement; const formData = new FormData(form); - // Honeypot check: if the hidden field has a value, it's likely a bot. + // Honeypot check const honeypot = formData.get("hp-field"); if (honeypot) { setBanner({ @@ -42,14 +43,11 @@ export default function Contact() { message: "Bot detected", type: "error", }); - setTimeout(() => { - setBanner((prev) => ({ ...prev, show: false })); - }, 3000); + setTimeout(() => setBanner((prev) => ({ ...prev, show: false })), 3000); return; } - // Time based anti-bot check: - // Read the timestamp from the hidden field and ensure at least 3 seconds have passed. + // Time-based anti-bot check const timestampStr = formData.get("timestamp") as string; const timestamp = parseInt(timestampStr, 10); if (Date.now() - timestamp < 3000) { @@ -58,9 +56,7 @@ export default function Contact() { message: "Please take your time filling out the form.", type: "error", }); - setTimeout(() => { - setBanner((prev) => ({ ...prev, show: false })); - }, 3000); + setTimeout(() => setBanner((prev) => ({ ...prev, show: false })), 3000); return; } @@ -83,7 +79,7 @@ export default function Contact() { submitButton.textContent = "Sent!"; setTimeout(() => { submitButton.removeAttribute("disabled"); - submitButton.textContent = "Send"; + submitButton.textContent = "Send Message"; }, 2000); } setBanner({ @@ -91,70 +87,118 @@ export default function Contact() { message: response.message, type: response.success ? "success" : "error", }); - setTimeout(() => { - setBanner((prev) => ({ ...prev, show: false })); - }, 3000); + setTimeout(() => setBanner((prev) => ({ ...prev, show: false })), 3000); } } return (
-

- Contact Me +

+ Get in Touch

-
+
{banner.show && (
{banner.message}
)} -
- {/* Honeypot field: should remain empty */} + + {/* Honeypot field */} - {/* Hidden timestamp field to check how fast the form was filled */} + {/* Hidden timestamp field */} - - - + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+