Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions frontend/tests/unit/hooks/useJobsData.extended.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,38 @@ describe("useJobsData extended", () => {
expect(result.current.error).toBe("falha jobs");
});

it("aplica os metadados padrão quando a API retorna payload parcial", async () => {
mocks.fetchJobsByAPIMock.mockResolvedValueOnce({});

const { result } = renderHook(() => useJobsData(3, 10));

await waitFor(() => {
expect(result.current.loading).toBe(false);
expect(result.current.meta).toEqual({
total: 0,
hasNext: false,
hasPrev: false,
page: 3,
limit: 10,
totalPages: 0,
});
});

expect(result.current.jobs).toEqual([]);
});

it("usa mensagem padrão quando carregar vagas rejeita sem Error", async () => {
mocks.fetchJobsByAPIMock.mockRejectedValueOnce("offline");

const { result } = renderHook(() => useJobsData());

await waitFor(() => {
expect(result.current.error).toBe(
"Erro inesperado ao carregar vagas.",
);
});
});

it("triggerScraper carrega jobs após executar request", async () => {
const { result } = renderHook(() => useJobsData());

Expand All @@ -78,4 +110,23 @@ describe("useJobsData extended", () => {
expect(mocks.fetchJobsByAPIMock).toHaveBeenCalled();
expect(result.current.scraping).toBe(false);
});

it("usa mensagem padrão quando o scraper rejeita sem Error", async () => {
mocks.runScraperRequestMock.mockRejectedValueOnce("indisponível");

const { result } = renderHook(() => useJobsData());

await waitFor(() => {
expect(result.current.loading).toBe(false);
});

await act(async () => {
await result.current.triggerScraper();
});

expect(result.current.error).toBe(
"Erro inesperado ao executar o scraper.",
);
expect(result.current.scraping).toBe(false);
});
});
22 changes: 22 additions & 0 deletions frontend/tests/unit/new_dashboard/branch-coverage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Header } from "@/domains/new_dashboard/components/layout/Header";
import { MessageDetailModal } from "@/domains/new_dashboard/components/layout/MessageDetailModal";
import { JobDetailModal } from "@/domains/new_dashboard/components/jobs/JobDetailModal";
import { JobRow } from "@/domains/new_dashboard/components/jobs/JobRow";
import { MentoringTab } from "@/domains/new_dashboard/components/mentoring/MentoringTab";
import { ProfileForm } from "@/domains/new_dashboard/components/profile/ProfileForm";
import { Modal } from "@/domains/new_dashboard/components/shared/Modal";
import {
Expand Down Expand Up @@ -236,6 +237,27 @@ describe("new_dashboard branch coverage", () => {
expect(onClose).toHaveBeenCalledOnce();
});

it("abre, confirma e fecha os detalhes de uma mentoria", () => {
render(<MentoringTab />);

fireEvent.click(screen.getAllByRole("button", { name: "Ver Mais" })[0]);

expect(
screen.getByRole("heading", { name: "Julio Silva", level: 2 }),
).toBeInTheDocument();
expect(screen.queryByText(/participação confirmada/i)).not.toBeInTheDocument();

fireEvent.click(
screen.getByRole("button", { name: /confirmar participação/i }),
);
expect(screen.getByText(/participação confirmada/i)).toBeInTheDocument();

fireEvent.click(screen.getByRole("button", { name: "Fechar" }));
expect(
screen.queryByRole("heading", { name: "Julio Silva", level: 2 }),
).not.toBeInTheDocument();
});

it("usa origem sistema como fallback no detalhe da mensagem", () => {
render(
<MessageDetailModal
Expand Down
6 changes: 6 additions & 0 deletions frontend/tests/unit/new_dashboard/theme.toast.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

useEffect(() => {
triggerToast("Mensagem temporária");
}, []);

Check warning on line 25 in frontend/tests/unit/new_dashboard/theme.toast.test.tsx

View workflow job for this annotation

GitHub Actions / validate

React Hook useEffect has a missing dependency: 'triggerToast'. Either include it or remove the dependency array

return (
<div>
Expand Down Expand Up @@ -105,6 +105,12 @@
expect(container).toBeEmptyDOMElement();
});

it("renderiza toast quando recebe uma mensagem", () => {
render(<Toast message="Perfil atualizado" />);

expect(screen.getByText("Perfil atualizado")).toBeInTheDocument();
});

it("lança erro quando o contexto de toast é usado fora do provider", () => {
expect(() => render(<ToastHookConsumer />)).toThrow(
/useToastContext must be used within ToastProvider/i,
Expand Down
44 changes: 44 additions & 0 deletions frontend/tests/unit/services/auth.service.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,50 @@ describe("authService", () => {
beforeEach(() => {
fetchMock.mockReset();
vi.restoreAllMocks();
vi.unstubAllEnvs();
});

describe("response parsing and URL normalization", () => {
it("uses the configured API URL without trailing slashes", async () => {
vi.stubEnv("VITE_API_BASE_URL", "https://api.example.com///");
fetchMock.mockResolvedValueOnce(mockResponse({ jsonData: { ok: true } }));

await expect(auth.logout()).resolves.toEqual({ ok: true });

expect(fetchMock).toHaveBeenCalledWith(
"https://api.example.com/auth/logout",
expect.objectContaining({ method: "POST" }),
);
});

it("parses non-JSON response bodies as messages", async () => {
fetchMock.mockResolvedValueOnce(
mockResponse({
contentType: "text/plain",
textData: "Logout concluído",
}),
);

await expect(auth.logout()).resolves.toEqual({
message: "Logout concluído",
});
});

it("returns an empty payload for an empty response body", async () => {
fetchMock.mockResolvedValueOnce(
mockResponse({ contentType: "", textData: "" }),
);

await expect(auth.logout()).resolves.toEqual({});
});

it("returns an empty payload when response parsing fails", async () => {
const response = mockResponse({ contentType: "application/json" });
response.json.mockRejectedValueOnce(new SyntaxError("invalid JSON"));
fetchMock.mockResolvedValueOnce(response);

await expect(auth.logout()).resolves.toEqual({});
});
});

describe("login", () => {
Expand Down
Loading