Skip to content

Commit d62d44b

Browse files
committed
feat(tests): correção d os testes no sistema
1 parent 6fc96ee commit d62d44b

13 files changed

Lines changed: 1151 additions & 0 deletions
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/* eslint-disable @typescript-eslint/no-explicit-any */
2+
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
3+
import { MemoryRouter } from "react-router-dom";
4+
import { beforeEach, describe, expect, it, vi } from "vitest";
5+
6+
const themeState = vi.hoisted(() => ({
7+
resolvedTheme: "light",
8+
toggleTheme: vi.fn(),
9+
}));
10+
11+
vi.mock("@/hooks/useTheme", () => ({
12+
useTheme: () => ({
13+
resolvedTheme: themeState.resolvedTheme,
14+
toggleTheme: themeState.toggleTheme,
15+
}),
16+
}));
17+
18+
import { Navbar } from "@/components/landing/Navbar";
19+
20+
describe("Navbar", () => {
21+
beforeEach(() => {
22+
themeState.resolvedTheme = "light";
23+
themeState.toggleTheme.mockClear();
24+
document.body.innerHTML = "";
25+
26+
["features", "time", "how-it-works", "status"].forEach((id) => {
27+
const section = document.createElement("section");
28+
section.id = id;
29+
section.scrollIntoView = vi.fn();
30+
document.body.appendChild(section);
31+
});
32+
});
33+
34+
it("renderiza links principais e aciona troca de tema", () => {
35+
render(
36+
<MemoryRouter>
37+
<Navbar />
38+
</MemoryRouter>,
39+
);
40+
41+
expect(screen.getAllByText(/funcionalidades/i).length).toBeGreaterThan(0);
42+
expect(screen.getByRole("link", { name: /realizar o login/i })).toHaveAttribute("href", "/login");
43+
44+
fireEvent.click(screen.getAllByLabelText(/ativar tema escuro/i)[0]);
45+
expect(themeState.toggleTheme).toHaveBeenCalledTimes(1);
46+
});
47+
48+
it("abre menu mobile e navega para seção ao clicar no link", () => {
49+
render(
50+
<MemoryRouter>
51+
<Navbar />
52+
</MemoryRouter>,
53+
);
54+
55+
fireEvent.click(screen.getByRole("button", { name: /abrir menu/i }));
56+
57+
const mobileFeatureLink = screen.getAllByRole("link", { name: /funcionalidades/i })[1];
58+
fireEvent.click(mobileFeatureLink);
59+
60+
const featuresSection = document.getElementById("features") as any;
61+
expect(featuresSection.scrollIntoView).toHaveBeenCalled();
62+
expect(document.querySelector(".dynamic-mobile-menu")).not.toBeInTheDocument();
63+
});
64+
65+
it("aplica classe dark e estilo de navbar após scroll", () => {
66+
themeState.resolvedTheme = "dark";
67+
68+
render(
69+
<MemoryRouter>
70+
<Navbar />
71+
</MemoryRouter>,
72+
);
73+
74+
Object.defineProperty(window, "scrollY", { configurable: true, value: 50 });
75+
fireEvent.scroll(window);
76+
77+
expect(document.documentElement.classList.contains("dark")).toBe(true);
78+
expect(document.querySelector("nav")?.className).toContain("bg-white/80");
79+
});
80+
81+
it("remove active section quando scrollY < 100", async () => {
82+
render(
83+
<MemoryRouter>
84+
<Navbar />
85+
</MemoryRouter>,
86+
);
87+
88+
await waitFor(() => {
89+
Object.defineProperty(window, "scrollY", { configurable: true, value: 50 });
90+
fireEvent.scroll(window);
91+
});
92+
93+
// Quando scrollY < 100, activeSection deve ser vazio
94+
const featuresLink = screen.getAllByRole("link", { name: /funcionalidades/i })[0];
95+
expect(featuresLink.className).not.toContain("ring-");
96+
});
97+
98+
it("atualiza active section quando seção fica visível com scrollY > 100", async () => {
99+
render(
100+
<MemoryRouter>
101+
<Navbar />
102+
</MemoryRouter>,
103+
);
104+
105+
await waitFor(() => {
106+
Object.defineProperty(window, "scrollY", { configurable: true, value: 150 });
107+
fireEvent.scroll(window);
108+
});
109+
});
110+
111+
it("retorna quando elemento nao existe ao clicar em link", () => {
112+
const { rerender } = render(
113+
<MemoryRouter>
114+
<Navbar />
115+
</MemoryRouter>,
116+
);
117+
118+
const featureSection = document.getElementById("features");
119+
if (featureSection) {
120+
featureSection.remove();
121+
}
122+
123+
const featureLink = screen.getAllByRole("link", { name: /funcionalidades/i })[0];
124+
fireEvent.click(featureLink);
125+
126+
expect(document.querySelector(".dynamic-mobile-menu")).not.toBeInTheDocument();
127+
});
128+
});
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import { render, screen, waitFor } from "@testing-library/react";
2+
import { afterEach, describe, expect, it, vi } from "vitest";
3+
4+
vi.mock("swiper/react", () => ({
5+
Swiper: ({ children }: any) => <div data-testid="swiper">{children}</div>,
6+
SwiperSlide: ({ children }: any) => <div data-testid="swiper-slide">{children}</div>,
7+
}));
8+
9+
vi.mock("swiper/modules", () => ({
10+
Pagination: {},
11+
Autoplay: {},
12+
}));
13+
14+
import TeamSection from "@/components/landing/TeamSection";
15+
16+
describe("TeamSection", () => {
17+
afterEach(() => {
18+
vi.restoreAllMocks();
19+
});
20+
21+
it("renderiza lista da API, filtra bots e garante presença do Pedro", async () => {
22+
vi.spyOn(global, "fetch").mockResolvedValue({
23+
ok: true,
24+
json: async () => [
25+
{
26+
id: 2,
27+
login: "Maria",
28+
avatar_url: "https://github.com/maria.png",
29+
html_url: "https://github.com/maria",
30+
contributions: 1,
31+
type: "User",
32+
},
33+
{
34+
id: 3,
35+
login: "bot-user",
36+
avatar_url: "x",
37+
html_url: "x",
38+
contributions: 10,
39+
type: "Bot",
40+
},
41+
],
42+
} as any);
43+
44+
render(<TeamSection />);
45+
46+
await waitFor(() => {
47+
expect(screen.getByText("Maria")).toBeInTheDocument();
48+
});
49+
50+
expect(screen.queryByText("bot-user")).not.toBeInTheDocument();
51+
expect(screen.getByText("PedroLucas1337")).toBeInTheDocument();
52+
expect(screen.getByText("QA")).toBeInTheDocument();
53+
expect(screen.getByText("1 commit")).toBeInTheDocument();
54+
});
55+
56+
it("mantém fallback local quando API retorna resposta inválida", async () => {
57+
vi.spyOn(global, "fetch").mockResolvedValue({
58+
ok: true,
59+
json: async () => ({ invalid: true }),
60+
} as any);
61+
62+
render(<TeamSection />);
63+
64+
await waitFor(() => {
65+
expect(screen.getByText("Benevanio")).toBeInTheDocument();
66+
});
67+
});
68+
69+
it("mantém fallback local e emite warning quando falha na API", async () => {
70+
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
71+
vi.spyOn(global, "fetch").mockRejectedValue(new Error("offline"));
72+
73+
render(<TeamSection />);
74+
75+
await waitFor(() => {
76+
expect(screen.getByText("Benevanio")).toBeInTheDocument();
77+
});
78+
79+
expect(warnSpy).toHaveBeenCalled();
80+
});
81+
82+
it("atualiza avatar de PedroLucas1337 quando vem da API com avatar incorreto", async () => {
83+
vi.spyOn(global, "fetch").mockResolvedValue({
84+
ok: true,
85+
json: async () => [
86+
{
87+
id: 104951475,
88+
login: "PedroLucas1337",
89+
avatar_url: "https://wrong-avatar.com/pedro.png",
90+
html_url: "https://github.com/PedroLucas1337",
91+
contributions: 5,
92+
type: "User",
93+
},
94+
],
95+
} as any);
96+
97+
render(<TeamSection />);
98+
99+
await waitFor(() => {
100+
const pedroImg = screen.getByAltText("PedroLucas1337");
101+
expect(pedroImg).toHaveAttribute(
102+
"src",
103+
"https://github.com/PedroLucas1337.png"
104+
);
105+
});
106+
});
107+
108+
it("trata erro HTTP com status nao ok", async () => {
109+
vi.spyOn(global, "fetch").mockResolvedValue({
110+
ok: false,
111+
status: 403,
112+
} as any);
113+
114+
render(<TeamSection />);
115+
116+
await waitFor(() => {
117+
expect(screen.getByText("Benevanio")).toBeInTheDocument();
118+
expect(screen.getByText("PedroLucas1337")).toBeInTheDocument();
119+
});
120+
});
121+
122+
it("exibe plural de commits corretamente", async () => {
123+
vi.spyOn(global, "fetch").mockResolvedValue({
124+
ok: true,
125+
json: async () => [
126+
{
127+
id: 5,
128+
login: "TestUser",
129+
avatar_url: "https://github.com/test.png",
130+
html_url: "https://github.com/test",
131+
contributions: 1,
132+
type: "User",
133+
},
134+
],
135+
} as any);
136+
137+
render(<TeamSection />);
138+
139+
await waitFor(() => {
140+
expect(screen.getByText("1 commit")).toBeInTheDocument();
141+
});
142+
});
143+
144+
it("exibe plural de commits quando > 1", async () => {
145+
vi.spyOn(global, "fetch").mockResolvedValue({
146+
ok: true,
147+
json: async () => [
148+
{
149+
id: 5,
150+
login: "TestUser",
151+
avatar_url: "https://github.com/test.png",
152+
html_url: "https://github.com/test",
153+
contributions: 10,
154+
type: "User",
155+
},
156+
],
157+
} as any);
158+
159+
render(<TeamSection />);
160+
161+
await waitFor(() => {
162+
expect(screen.getByText("10 commits")).toBeInTheDocument();
163+
});
164+
});
165+
166+
it("nao atualiza contributors quando lista vazia", async () => {
167+
vi.spyOn(global, "fetch").mockResolvedValue({
168+
ok: true,
169+
json: async () => [],
170+
} as any);
171+
172+
render(<TeamSection />);
173+
174+
await waitFor(() => {
175+
expect(screen.getByText("Benevanio")).toBeInTheDocument();
176+
expect(screen.getByText("PedroLucas1337")).toBeInTheDocument();
177+
});
178+
});
179+
});
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/* eslint-disable @typescript-eslint/no-explicit-any */
2+
import { render, screen } from "@testing-library/react";
3+
import { describe, expect, it, vi } from "vitest";
4+
5+
vi.mock("@unpic/react", () => ({
6+
Image: (props: any) => <img {...props} alt={props.alt} />,
7+
}));
8+
9+
import LeftSide from "@/components/login/LeftSide";
10+
11+
describe("LeftSide", () => {
12+
it("renderiza conteúdo principal e links sociais", () => {
13+
render(<LeftSide />);
14+
15+
expect(screen.getByText(/conectando talentos/i)).toBeInTheDocument();
16+
expect(screen.getByText(/desenvolvimento profissional/i)).toBeInTheDocument();
17+
expect(screen.getByAltText(/profissionais de tecnologia/i)).toBeInTheDocument();
18+
19+
const links = screen.getAllByRole("link");
20+
expect(links.length).toBeGreaterThanOrEqual(2);
21+
expect(links[0]).toHaveAttribute("href", "https://instagram.com/");
22+
});
23+
});

0 commit comments

Comments
 (0)