feat(tests): fix candidate dashboard and apply form tests

This commit is contained in:
Tiago Yamamoto 2026-01-01 11:56:00 -03:00
parent 812affb803
commit 93ca6d64ef
3 changed files with 74 additions and 22 deletions

View file

@ -85,7 +85,7 @@ func ValidateJWT(secret, env string) error {
// warning is always returned if short. // warning is always returned if short.
// fatal error is returned if production. // fatal error is returned if production.
// Caller handles logging. // Caller handles logging.
return fmt.Errorf(msg) return fmt.Errorf("%s", msg)
} }
return nil return nil
} }

View file

@ -1,5 +1,6 @@
import { render, screen, fireEvent, waitFor } from "@testing-library/react"; import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import { Suspense } from "react";
import JobApplicationPage from "./page"; import JobApplicationPage from "./page";
import { jobsApi, applicationsApi } from "@/lib/api"; import { jobsApi, applicationsApi } from "@/lib/api";
import { useNotify } from "@/contexts/notification-context"; import { useNotify } from "@/contexts/notification-context";
@ -40,6 +41,25 @@ jest.mock("@/components/ui/progress", () => ({
Progress: () => <div data-testid="progress" />, Progress: () => <div data-testid="progress" />,
})); }));
jest.mock("framer-motion", () => ({
motion: { div: ({ children }: any) => <div>{children}</div> },
AnimatePresence: ({ children }: any) => <>{children}</>,
}));
jest.mock("@/components/ui/select", () => ({
Select: ({ onValueChange, children }: any) => (
<div data-testid="select">
<button data-testid="select-option-3k" onClick={() => onValueChange("up-to-3k")}>
Select 3k
</button>
{children}
</div>
),
SelectTrigger: ({ children }: any) => <button>{children}</button>,
SelectValue: ({ placeholder }: any) => <span>{placeholder}</span>,
SelectContent: ({ children }: any) => <div>{children}</div>,
SelectItem: ({ children }: any) => <div>{children}</div>,
}));
describe("JobApplicationPage", () => { describe("JobApplicationPage", () => {
const mockPush = jest.fn(); const mockPush = jest.fn();
@ -59,22 +79,34 @@ describe("JobApplicationPage", () => {
window.scrollTo = jest.fn(); 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 () => { it("renders job title and company info", async () => {
render(<JobApplicationPage params={params} />); const params: any = Promise.resolve({ id: "job-123" });
params.status = "fulfilled";
params.value = { id: "job-123" };
await waitFor(() => { render(
expect(screen.getByText("Application: Software Engineer")).toBeInTheDocument(); <Suspense fallback={<div>Loading...</div>}>
expect(screen.getByText(/Tech Corp/)).toBeInTheDocument(); <JobApplicationPage params={params} />
}); </Suspense>
);
expect(await screen.findByText("Application: Software Engineer")).toBeInTheDocument();
expect(screen.getByText(/Tech Corp/)).toBeInTheDocument();
}); });
it("validates step 1 fields", async () => { it("validates step 1 fields", async () => {
render(<JobApplicationPage params={params} />); 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(
<Suspense fallback={<div>Loading...</div>}>
<JobApplicationPage params={params} />
</Suspense>
);
// Wait for load
expect(await screen.findByText("Application: Software Engineer")).toBeInTheDocument();
// Try next empty // Try next empty
const nextBtn = screen.getByRole("button", { name: /Next step/i }); const nextBtn = screen.getByRole("button", { name: /Next step/i });
@ -91,19 +123,29 @@ describe("JobApplicationPage", () => {
expect(mockNotify.error).toHaveBeenCalledWith("Privacy policy", expect.any(String)); expect(mockNotify.error).toHaveBeenCalledWith("Privacy policy", expect.any(String));
// Check checkbox // 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); 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); fireEvent.click(nextBtn);
// Should move to step 2 // Should move to step 2
expect(screen.getByText("Resume & Documents")).toBeInTheDocument(); await waitFor(() => expect(screen.getByText("Resume (CV) *")).toBeInTheDocument());
}); });
it("submits application successfully", async () => { it("submits application successfully", async () => {
render(<JobApplicationPage params={params} />); const params: any = Promise.resolve({ id: "job-123" });
await waitFor(() => expect(jobsApi.getById).toHaveBeenCalled()); params.status = "fulfilled";
params.value = { id: "job-123" };
render(
<Suspense fallback={<div>Loading...</div>}>
<JobApplicationPage params={params} />
</Suspense>
);
// Wait for load
expect(await screen.findByText("Application: Software Engineer")).toBeInTheDocument();
await waitFor(() => expect(screen.getByLabelText(/Full name/i)).toBeInTheDocument());
// Step 1 // Step 1
fireEvent.change(screen.getByLabelText(/Full name/i), { target: { value: "John Doe" } }); 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 })); fireEvent.click(screen.getByRole("button", { name: /Next step/i }));
// Step 2 // Step 2
// Optional fields here or skip await waitFor(() => expect(screen.getByText("Resume (CV) *")).toBeInTheDocument());
// 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!)
fireEvent.click(screen.getByRole("button", { name: /Next step/i })); fireEvent.click(screen.getByRole("button", { name: /Next step/i }));
// Wait for Step 3
await waitFor(() => expect(screen.getByText("Salary expectation *")).toBeInTheDocument());
// Step 3 // Step 3
// Salary and Experience
const trigger = screen.getByText("Select a range"); const trigger = screen.getByText("Select a range");
fireEvent.click(trigger); 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.getByLabelText("Yes, I do")); // Radio
fireEvent.click(screen.getByRole("button", { name: /Next step/i })); fireEvent.click(screen.getByRole("button", { name: /Next step/i }));
await waitFor(() => expect(screen.getByLabelText(/Why do you want to work/i)).toBeInTheDocument());
// Step 4 // Step 4
// Why Us and Availability // Why Us and Availability
fireEvent.change(screen.getByLabelText(/Why do you want to work/i), { target: { value: "Because I like it." } }); fireEvent.change(screen.getByLabelText(/Why do you want to work/i), { target: { value: "Because I like it." } });

View file

@ -49,6 +49,14 @@ jest.mock("@/components/ui/skeleton", () => ({
Skeleton: () => <div>Loading...</div>, Skeleton: () => <div>Loading...</div>,
})) }))
jest.mock("@/components/stats-card", () => ({
StatsCard: () => <div>StatsCard</div>,
}))
jest.mock("@/components/job-card", () => ({
JobCard: () => <div>JobCard</div>,
}))
describe("CandidateDashboard", () => { describe("CandidateDashboard", () => {
it("renders welcome message", async () => { it("renders welcome message", async () => {
render(<CandidateDashboardContent />) render(<CandidateDashboardContent />)
@ -56,6 +64,6 @@ describe("CandidateDashboard", () => {
// Assuming dashboard has "Welcome" or similar // Assuming dashboard has "Welcome" or similar
// Or check for section headers "My Applications", "Recommended Jobs" // Or check for section headers "My Applications", "Recommended Jobs"
// Wait for potential async data loading // Wait for potential async data loading
expect(await screen.findByText(/Recommended Jobs/i)).toBeInTheDocument() expect(await screen.findByText("candidate.dashboard.recommended.title")).toBeInTheDocument()
}) })
}) })