From 93ca6d64ef887d30b2fe062a467ea043a3fb694a Mon Sep 17 00:00:00 2001 From: Tiago Yamamoto Date: Thu, 1 Jan 2026 11:56:00 -0300 Subject: [PATCH] feat(tests): fix candidate dashboard and apply form tests --- backend/cmd/api/main.go | 2 +- .../src/app/jobs/[id]/apply/page.test.tsx | 84 ++++++++++++++----- .../candidate-dashboard.test.tsx | 10 ++- 3 files changed, 74 insertions(+), 22 deletions(-) diff --git a/backend/cmd/api/main.go b/backend/cmd/api/main.go index 75bdda1..70dda9f 100755 --- a/backend/cmd/api/main.go +++ b/backend/cmd/api/main.go @@ -85,7 +85,7 @@ func ValidateJWT(secret, env string) error { // warning is always returned if short. // fatal error is returned if production. // Caller handles logging. - return fmt.Errorf(msg) + return fmt.Errorf("%s", msg) } return nil } diff --git a/frontend/src/app/jobs/[id]/apply/page.test.tsx b/frontend/src/app/jobs/[id]/apply/page.test.tsx index cd71c55..3776f15 100644 --- a/frontend/src/app/jobs/[id]/apply/page.test.tsx +++ b/frontend/src/app/jobs/[id]/apply/page.test.tsx @@ -1,5 +1,6 @@ import { render, screen, fireEvent, waitFor } from "@testing-library/react"; +import { Suspense } from "react"; import JobApplicationPage from "./page"; import { jobsApi, applicationsApi } from "@/lib/api"; import { useNotify } from "@/contexts/notification-context"; @@ -40,6 +41,25 @@ jest.mock("@/components/ui/progress", () => ({ Progress: () =>
, })); +jest.mock("framer-motion", () => ({ + motion: { div: ({ children }: any) =>
{children}
}, + AnimatePresence: ({ children }: any) => <>{children}, +})); + +jest.mock("@/components/ui/select", () => ({ + Select: ({ onValueChange, children }: any) => ( +
+ + {children} +
+ ), + SelectTrigger: ({ children }: any) => , + SelectValue: ({ placeholder }: any) => {placeholder}, + SelectContent: ({ children }: any) =>
{children}
, + SelectItem: ({ children }: any) =>
{children}
, +})); describe("JobApplicationPage", () => { const mockPush = jest.fn(); @@ -59,22 +79,34 @@ describe("JobApplicationPage", () => { window.scrollTo = jest.fn(); }); - // Since component uses `use(params)`, we need to wrap or pass a promise - const params = Promise.resolve({ id: "job-123" }); - it("renders job title and company info", async () => { - render(); + const params: any = Promise.resolve({ id: "job-123" }); + params.status = "fulfilled"; + params.value = { id: "job-123" }; - await waitFor(() => { - expect(screen.getByText("Application: Software Engineer")).toBeInTheDocument(); - expect(screen.getByText(/Tech Corp/)).toBeInTheDocument(); - }); + render( + Loading...
}> + + + ); + + expect(await screen.findByText("Application: Software Engineer")).toBeInTheDocument(); + expect(screen.getByText(/Tech Corp/)).toBeInTheDocument(); }); it("validates step 1 fields", async () => { - render(); + const params: any = Promise.resolve({ id: "job-123" }); + params.status = "fulfilled"; + params.value = { id: "job-123" }; - await waitFor(() => expect(screen.getByText("Application: Software Engineer")).toBeInTheDocument()); + render( + Loading...}> + + + ); + + // Wait for load + expect(await screen.findByText("Application: Software Engineer")).toBeInTheDocument(); // Try next empty const nextBtn = screen.getByRole("button", { name: /Next step/i }); @@ -91,19 +123,29 @@ describe("JobApplicationPage", () => { expect(mockNotify.error).toHaveBeenCalledWith("Privacy policy", expect.any(String)); // Check checkbox - // Finding checkbox by ID might be easiest or by label text click const policyLabel = screen.getByText(/I have read and agree to the/i); - fireEvent.click(policyLabel); // Should toggle checkbox associated with label + fireEvent.click(policyLabel); fireEvent.click(nextBtn); // Should move to step 2 - expect(screen.getByText("Resume & Documents")).toBeInTheDocument(); + await waitFor(() => expect(screen.getByText("Resume (CV) *")).toBeInTheDocument()); }); it("submits application successfully", async () => { - render(); - await waitFor(() => expect(jobsApi.getById).toHaveBeenCalled()); + const params: any = Promise.resolve({ id: "job-123" }); + params.status = "fulfilled"; + params.value = { id: "job-123" }; + + render( + Loading...}> + + + ); + + // Wait for load + expect(await screen.findByText("Application: Software Engineer")).toBeInTheDocument(); + await waitFor(() => expect(screen.getByLabelText(/Full name/i)).toBeInTheDocument()); // Step 1 fireEvent.change(screen.getByLabelText(/Full name/i), { target: { value: "John Doe" } }); @@ -113,21 +155,23 @@ describe("JobApplicationPage", () => { fireEvent.click(screen.getByRole("button", { name: /Next step/i })); // Step 2 - // Optional fields here or skip - // Resume is technically required (Step 2 logic: case 2 returns true currently in the simplified code? - // Looking at source: case 2: return true; (lines 122). So no validation on Step 2!) + await waitFor(() => expect(screen.getByText("Resume (CV) *")).toBeInTheDocument()); fireEvent.click(screen.getByRole("button", { name: /Next step/i })); + // Wait for Step 3 + await waitFor(() => expect(screen.getByText("Salary expectation *")).toBeInTheDocument()); + // Step 3 - // Salary and Experience const trigger = screen.getByText("Select a range"); fireEvent.click(trigger); - fireEvent.click(screen.getByText("Up to R$ 3,000")); // SelectOption + fireEvent.click(screen.getByTestId("select-option-3k")); fireEvent.click(screen.getByLabelText("Yes, I do")); // Radio fireEvent.click(screen.getByRole("button", { name: /Next step/i })); + await waitFor(() => expect(screen.getByLabelText(/Why do you want to work/i)).toBeInTheDocument()); + // Step 4 // Why Us and Availability fireEvent.change(screen.getByLabelText(/Why do you want to work/i), { target: { value: "Because I like it." } }); diff --git a/frontend/src/components/dashboard-contents/candidate-dashboard.test.tsx b/frontend/src/components/dashboard-contents/candidate-dashboard.test.tsx index c2b9b8a..f68ce17 100644 --- a/frontend/src/components/dashboard-contents/candidate-dashboard.test.tsx +++ b/frontend/src/components/dashboard-contents/candidate-dashboard.test.tsx @@ -49,6 +49,14 @@ jest.mock("@/components/ui/skeleton", () => ({ Skeleton: () =>
Loading...
, })) +jest.mock("@/components/stats-card", () => ({ + StatsCard: () =>
StatsCard
, +})) + +jest.mock("@/components/job-card", () => ({ + JobCard: () =>
JobCard
, +})) + describe("CandidateDashboard", () => { it("renders welcome message", async () => { render() @@ -56,6 +64,6 @@ describe("CandidateDashboard", () => { // Assuming dashboard has "Welcome" or similar // Or check for section headers "My Applications", "Recommended Jobs" // Wait for potential async data loading - expect(await screen.findByText(/Recommended Jobs/i)).toBeInTheDocument() + expect(await screen.findByText("candidate.dashboard.recommended.title")).toBeInTheDocument() }) })