test: restore and fix tests broken by lazy-loading
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 4s
Build & Deploy / 🏗️ Build (push) Successful in 1m41s
Build & Deploy / 🧪 QA (push) Successful in 2m1s
Build & Deploy / 🚀 Deploy (push) Failing after 7s
Build & Deploy / 🩺 Health Check (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
Some checks failed
Build & Deploy / 🔍 Prepare (push) Successful in 4s
Build & Deploy / 🏗️ Build (push) Successful in 1m41s
Build & Deploy / 🧪 QA (push) Successful in 2m1s
Build & Deploy / 🚀 Deploy (push) Failing after 7s
Build & Deploy / 🩺 Health Check (push) Has been skipped
Build & Deploy / 🔔 Notify (push) Successful in 2s
This commit is contained in:
129
tests/contact.test.tsx
Normal file
129
tests/contact.test.tsx
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
|
||||||
|
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||||
|
import { NextIntlClientProvider } from "next-intl";
|
||||||
|
import messages from "../messages/de.json";
|
||||||
|
|
||||||
|
// Mocks MUST be defined before component import to ensure they are picked up
|
||||||
|
vi.mock("../components/Reveal", () => ({
|
||||||
|
Reveal: ({ children }: any) => <>{children}</>,
|
||||||
|
Stagger: ({ children }: any) => <>{children}</>,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Better FormData mock for happy-dom
|
||||||
|
global.FormData = class MockFormData {
|
||||||
|
private data = new Map();
|
||||||
|
constructor(form?: HTMLFormElement) {
|
||||||
|
if (form) {
|
||||||
|
const elements = form.elements as any;
|
||||||
|
for (let i = 0; i < elements.length; i++) {
|
||||||
|
const item = elements.item(i);
|
||||||
|
if (item.name && item.value) {
|
||||||
|
this.data.set(item.name, item.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
append(key: string, value: any) {
|
||||||
|
this.data.set(key, value);
|
||||||
|
}
|
||||||
|
get(key: string) {
|
||||||
|
return this.data.get(key);
|
||||||
|
}
|
||||||
|
entries() {
|
||||||
|
return Array.from(this.data.entries())[Symbol.iterator]();
|
||||||
|
}
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
// Mock alert
|
||||||
|
const alertMock = vi.fn();
|
||||||
|
global.alert = alertMock;
|
||||||
|
|
||||||
|
// Import component AFTER mocks
|
||||||
|
import Contact from "../components/ContactContent";
|
||||||
|
|
||||||
|
// Mock fetch
|
||||||
|
const fetchMock = vi.fn();
|
||||||
|
global.fetch = fetchMock;
|
||||||
|
|
||||||
|
const renderContact = () => {
|
||||||
|
return render(
|
||||||
|
<NextIntlClientProvider locale="de" messages={messages}>
|
||||||
|
<Contact />
|
||||||
|
</NextIntlClientProvider>,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("Contact Page", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
fetchMock.mockReset();
|
||||||
|
fetchMock.mockResolvedValue({
|
||||||
|
ok: true,
|
||||||
|
json: async () => ({ success: true }),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders the contact form correctly", () => {
|
||||||
|
renderContact();
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("submits the form successfully", async () => {
|
||||||
|
renderContact();
|
||||||
|
|
||||||
|
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." },
|
||||||
|
});
|
||||||
|
|
||||||
|
const form = screen.getByRole("form");
|
||||||
|
fireEvent.submit(form);
|
||||||
|
|
||||||
|
await waitFor(
|
||||||
|
() => {
|
||||||
|
expect(fetchMock).toHaveBeenCalled();
|
||||||
|
},
|
||||||
|
{ timeout: 2000 },
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
(await screen.findAllByText(/Anfrage erfolgreich übermittelt/i)).length,
|
||||||
|
).toBeGreaterThanOrEqual(1);
|
||||||
|
expect(
|
||||||
|
(await screen.findAllByText(/Ihr Anliegen wurde erfasst/i)).length,
|
||||||
|
).toBeGreaterThanOrEqual(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("handles submission errors", async () => {
|
||||||
|
fetchMock.mockResolvedValueOnce({
|
||||||
|
ok: false,
|
||||||
|
json: async () => ({ error: "Server error" }),
|
||||||
|
});
|
||||||
|
|
||||||
|
renderContact();
|
||||||
|
|
||||||
|
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." },
|
||||||
|
});
|
||||||
|
|
||||||
|
const form = screen.getByRole("form");
|
||||||
|
fireEvent.submit(form);
|
||||||
|
|
||||||
|
expect(await screen.findByText(/Server error/i)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
37
tests/home.test.tsx
Normal file
37
tests/home.test.tsx
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { render, screen } from "@testing-library/react";
|
||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import Home from "../components/HomeContent";
|
||||||
|
import { NextIntlClientProvider } from "next-intl";
|
||||||
|
import messages from "../messages/de.json";
|
||||||
|
|
||||||
|
const renderHome = () => {
|
||||||
|
return render(
|
||||||
|
<NextIntlClientProvider locale="de" messages={messages}>
|
||||||
|
<Home />
|
||||||
|
</NextIntlClientProvider>,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe("Home Page", () => {
|
||||||
|
it("renders the hero section with correct title", () => {
|
||||||
|
renderHome();
|
||||||
|
expect(
|
||||||
|
screen.getByRole("heading", { name: /Spezialisierter Partner/i }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("contains the CTA button", () => {
|
||||||
|
renderHome();
|
||||||
|
const ctaButton = screen.getByRole("link", { name: /Projekt anfragen/i });
|
||||||
|
expect(ctaButton).toBeInTheDocument();
|
||||||
|
expect(ctaButton).toHaveAttribute("href", "/kontakt");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("renders the portfolio section", async () => {
|
||||||
|
renderHome();
|
||||||
|
expect(await screen.findByText(/Unsere Leistungen/i)).toBeInTheDocument();
|
||||||
|
// Use getAllByText because it appears in both hero description and card title
|
||||||
|
const elements = await screen.findAllByText(/Technische Beratung/i);
|
||||||
|
expect(elements.length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
56
tests/setup.tsx
Normal file
56
tests/setup.tsx
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
import "@testing-library/jest-dom/vitest";
|
||||||
|
import React from "react";
|
||||||
|
import { vi } from "vitest";
|
||||||
|
|
||||||
|
// Mock next/navigation
|
||||||
|
vi.mock("next/navigation", () => ({
|
||||||
|
usePathname: () => "/",
|
||||||
|
useRouter: () => ({
|
||||||
|
push: vi.fn(),
|
||||||
|
replace: vi.fn(),
|
||||||
|
prefetch: vi.fn(),
|
||||||
|
back: vi.fn(),
|
||||||
|
}),
|
||||||
|
useSearchParams: () => new URLSearchParams(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Mock next-intl to avoid transitive next/server issues
|
||||||
|
vi.mock("next-intl/middleware", () => ({
|
||||||
|
default: vi.fn(() => (req: any) => req),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("next-intl/server", () => ({
|
||||||
|
getRequestConfig: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Mock next/server
|
||||||
|
vi.mock("next/server", () => ({
|
||||||
|
NextResponse: {
|
||||||
|
json: vi.fn(),
|
||||||
|
next: vi.fn(),
|
||||||
|
redirect: vi.fn(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Mock next/dynamic to be synchronous in tests
|
||||||
|
vi.mock("next/dynamic", () => ({
|
||||||
|
default: vi.fn((loader) => {
|
||||||
|
return (props: any) => {
|
||||||
|
const [Component, setComponent] = React.useState<any>(null);
|
||||||
|
React.useEffect(() => {
|
||||||
|
loader().then((mod: any) => {
|
||||||
|
setComponent(
|
||||||
|
() =>
|
||||||
|
mod.default ||
|
||||||
|
mod.PortfolioSection ||
|
||||||
|
mod.ExpertiseSection ||
|
||||||
|
mod.TechnicalSpecsSection ||
|
||||||
|
mod.CTASection ||
|
||||||
|
mod,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
return Component ? <Component {...props} /> : null;
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
}));
|
||||||
Reference in New Issue
Block a user