Source: App.integration.test.js

import { render, screen, fireEvent } from "@testing-library/react";
import App from "./App";

/**
 * @file App.integration.test.js
 *
 * Integration tests for the User form.
 *
 * These tests simulate real user interaction and verify:
 * - Rendering of the form
 * - Validation logic integration
 * - Visual feedback in the DOM (role="alert")
 * - Visual state (color red for error, green for success)
 * - Button disabled state (grey button when invalid)
 * - localStorage persistence (jsdom simulation)
 *
 * We test the full chain:
 * App.js → validator.js → module.js
 *
 * The goal is to validate real user behaviour,
 * not internal implementation details.
 */

describe("Integration test - User form", () => {

  beforeEach(() => {
    localStorage.clear();
  });

  test("should render the form fields and submit button", () => {
    render(<App />);

    expect(screen.getByLabelText(/prénom/i)).toBeInTheDocument();
    expect(screen.getByLabelText(/^Nom$/i)).toBeInTheDocument();
    expect(screen.getByLabelText(/email/i)).toBeInTheDocument();
    expect(screen.getByLabelText(/date/i)).toBeInTheDocument();
    expect(screen.getByLabelText(/ville/i)).toBeInTheDocument();
    expect(screen.getByLabelText(/code postal/i)).toBeInTheDocument();

    expect(screen.getByRole("button")).toBeInTheDocument();
  });

  test("should submit valid form and show success message in green", () => {

    render(<App />);

    fireEvent.change(screen.getByLabelText(/prénom/i), {
      target: { value: "Jean" }
    });

    fireEvent.change(screen.getByLabelText(/^Nom$/i), {
      target: { value: "Dupont" }
    });

    fireEvent.change(screen.getByLabelText(/email/i), {
      target: { value: "jean@test.com" }
    });

    fireEvent.change(screen.getByLabelText(/date/i), {
      target: { value: "2000-01-01" }
    });

    fireEvent.change(screen.getByLabelText(/code postal/i), {
      target: { value: "75000" }
    });

    fireEvent.submit(screen.getByRole("button"));

    const alert = screen.getByRole("alert");

    expect(alert).toHaveTextContent("Utilisateur valide");
    expect(alert).toHaveStyle("color: green");
  });

  test("should show error in red if user is under 18", () => {

    render(<App />);

    fireEvent.change(screen.getByLabelText(/prénom/i), {
      target: { value: "Tom" }
    });

    fireEvent.change(screen.getByLabelText(/^Nom$/i), {
      target: { value: "Junior" }
    });

    fireEvent.change(screen.getByLabelText(/email/i), {
      target: { value: "tom@test.com" }
    });

    fireEvent.change(screen.getByLabelText(/date/i), {
      target: { value: "2015-01-01" }
    });

    fireEvent.change(screen.getByLabelText(/code postal/i), {
      target: { value: "75000" }
    });

    fireEvent.submit(screen.getByRole("button"));

    const alert = screen.getByRole("alert");

    expect(alert).toHaveTextContent("AGE_UNDER_18");
    expect(alert).toHaveStyle("color: red");
  });

  test("should show error in red if email format is invalid", () => {

    render(<App />);

    fireEvent.change(screen.getByLabelText(/prénom/i), {
      target: { value: "Marie" }
    });

    fireEvent.change(screen.getByLabelText(/^Nom$/i), {
      target: { value: "Martin" }
    });

    fireEvent.change(screen.getByLabelText(/email/i), {
      target: { value: "marie-test.com" }
    });

    fireEvent.change(screen.getByLabelText(/date/i), {
      target: { value: "1995-05-10" }
    });

    fireEvent.change(screen.getByLabelText(/code postal/i), {
      target: { value: "75000" }
    });

    fireEvent.submit(screen.getByRole("button"));

    const alert = screen.getByRole("alert");

    expect(alert).toHaveTextContent("INVALID_EMAIL");
    expect(alert).toHaveStyle("color: red");
  });

  test("should show error in red if postal code format is invalid", () => {

    render(<App />);

    fireEvent.change(screen.getByLabelText(/prénom/i), {
      target: { value: "Paul" }
    });

    fireEvent.change(screen.getByLabelText(/^Nom$/i), {
      target: { value: "Durand" }
    });

    fireEvent.change(screen.getByLabelText(/email/i), {
      target: { value: "paul@test.com" }
    });

    fireEvent.change(screen.getByLabelText(/date/i), {
      target: { value: "1990-10-10" }
    });

    fireEvent.change(screen.getByLabelText(/code postal/i), {
      target: { value: "75A00" }
    });

    fireEvent.submit(screen.getByRole("button"));

    const alert = screen.getByRole("alert");

    expect(alert).toHaveTextContent("INVALID_POSTAL_CODE");
    expect(alert).toHaveStyle("color: red");
  });

  test("should handle chaotic user: invalid → correction → success with color change", () => {

    render(<App />);

    fireEvent.change(screen.getByLabelText(/prénom/i), {
      target: { value: "Alice" }
    });

    fireEvent.change(screen.getByLabelText(/^Nom$/i), {
      target: { value: "Durand" }
    });

    fireEvent.change(screen.getByLabelText(/email/i), {
      target: { value: "alice-test.com" }
    });

    fireEvent.change(screen.getByLabelText(/date/i), {
      target: { value: "1995-01-01" }
    });

    fireEvent.change(screen.getByLabelText(/code postal/i), {
      target: { value: "75000" }
    });

    fireEvent.submit(screen.getByRole("button"));

    let alert = screen.getByRole("alert");

    expect(alert).toHaveTextContent("INVALID_EMAIL");
    expect(alert).toHaveStyle("color: red");

    fireEvent.change(screen.getByLabelText(/email/i), {
      target: { value: "alice@test.com" }
    });

    fireEvent.submit(screen.getByRole("button"));

    alert = screen.getByRole("alert");

    expect(alert).toHaveTextContent("Utilisateur valide");
    expect(alert).toHaveStyle("color: green");
  });

  test("should disable button and show grey style when form is invalid", () => {

    render(<App />);

    const button = screen.getByRole("button");

    expect(button).toBeDisabled();
    expect(button).toHaveClass("button-disabled");
  });

  test("should store correct user data in localStorage after successful submission", () => {

    const setItemSpy = jest.spyOn(Storage.prototype, "setItem");

    render(<App />);

    fireEvent.change(screen.getByLabelText(/prénom/i), {
      target: { value: "Laura" }
    });

    fireEvent.change(screen.getByLabelText(/^Nom$/i), {
      target: { value: "Petit" }
    });

    fireEvent.change(screen.getByLabelText(/email/i), {
      target: { value: "laura@test.com" }
    });

    fireEvent.change(screen.getByLabelText(/date/i), {
      target: { value: "1990-01-01" }
    });

    fireEvent.change(screen.getByLabelText(/code postal/i), {
      target: { value: "75000" }
    });

    fireEvent.submit(screen.getByRole("button"));

    expect(setItemSpy).toHaveBeenCalledWith(
      "user",
      JSON.stringify({
        firstname: "Laura",
        lastname: "Petit",
        email: "laura@test.com",
        birth: new Date("1990-01-01"),
        postalCode: "75000"
      })
    );

    setItemSpy.mockRestore();
  });

  test("should display email error under email field", () => {

    render(<App />);

    fireEvent.change(screen.getByLabelText(/email/i), {
      target: { value: "anna-test.com" }
    });

    fireEvent.blur(screen.getByLabelText(/email/i));

    expect(screen.getByText("Email invalide")).toBeInTheDocument();
  });

  test("should display postal code error under postal code field", () => {

    render(<App />);

    fireEvent.change(screen.getByLabelText(/code postal/i), {
      target: { value: "75A00" }
    });

    fireEvent.blur(screen.getByLabelText(/code postal/i));

    expect(screen.getByText("Code postal invalide")).toBeInTheDocument();
  });

  test("should display identity error under firstname field", () => {

    render(<App />);

    fireEvent.change(screen.getByLabelText(/prénom/i), {
      target: { value: "123" }
    });

    fireEvent.blur(screen.getByLabelText(/prénom/i));

    expect(screen.getByText("Identité invalide")).toBeInTheDocument();
  });

  test("should display age error under birth field on submit", () => {

    render(<App />);

    fireEvent.change(screen.getByLabelText(/prénom/i), {
      target: { value: "Paul" }
    });

    fireEvent.change(screen.getByLabelText(/^Nom$/i), {
      target: { value: "Martin" }
    });

    fireEvent.change(screen.getByLabelText(/email/i), {
      target: { value: "paul@test.com" }
    });

    fireEvent.change(screen.getByLabelText(/date/i), {
      target: { value: "2015-01-01" }
    });

    fireEvent.change(screen.getByLabelText(/code postal/i), {
      target: { value: "75000" }
    });

    fireEvent.submit(screen.getByRole("button"));

    expect(screen.getByText("Vous devez avoir au moins 18 ans")).toBeInTheDocument();
  });

    test("should clear email error after valid blur", () => {

    render(<App />);

    const emailInput = screen.getByLabelText(/email/i);

    // First blur with invalid email
    fireEvent.change(emailInput, {
      target: { value: "bad-email" }
    });

    fireEvent.blur(emailInput);

    expect(screen.getByText("Email invalide")).toBeInTheDocument();

    // Then correct email and blur again
    fireEvent.change(emailInput, {
      target: { value: "good@test.com" }
    });

    fireEvent.blur(emailInput);

    expect(screen.queryByText("Email invalide")).not.toBeInTheDocument();
  });

    test("should display identity error under firstname on submit", () => {

    render(<App />);

    fireEvent.change(screen.getByLabelText(/prénom/i), {
      target: { value: "12" }   // Invalid identity
    });

    fireEvent.change(screen.getByLabelText(/^Nom$/i), {
      target: { value: "Valid" }
    });

    fireEvent.change(screen.getByLabelText(/email/i), {
      target: { value: "valid@test.com" }
    });

    fireEvent.change(screen.getByLabelText(/date/i), {
      target: { value: "1990-01-01" }
    });

    fireEvent.change(screen.getByLabelText(/code postal/i), {
      target: { value: "75000" }
    });

    fireEvent.submit(screen.getByRole("button"));

    expect(screen.getByText("Identité invalide")).toBeInTheDocument();
  });

});