Skip to content

Commit 313c2ef

Browse files
committed
refactor: enhance product component tests with improved setup and additional assertions
1 parent 7ac47a9 commit 313c2ef

File tree

1 file changed

+143
-31
lines changed

1 file changed

+143
-31
lines changed
Lines changed: 143 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,171 @@
1-
import { describe, it } from "vitest";
1+
import { describe, expect, it, vi } from "vitest";
2+
3+
import { createTestProduct } from "@/lib/utils.tests";
4+
import type { Product as ProductType } from "@/models/product.model";
5+
import { render, screen } from "@testing-library/react";
6+
7+
import { useNavigation } from "react-router";
8+
import Product from ".";
9+
import type { Route } from "./+types";
10+
11+
// Mock Container component since we just want to verify it's used correctly
12+
vi.mock("@/components/ui", () => ({
13+
Container: vi.fn(({ children }) => (
14+
<div data-testid="mock-container">{children}</div>
15+
)),
16+
Button: vi.fn(({ children, ...props }) => (
17+
<button data-testid="mock-button" {...props}>
18+
{children}
19+
</button>
20+
)),
21+
Separator: vi.fn(() => <hr data-testid="mock-separator" />),
22+
}));
23+
24+
// Helper function to create a test navigation object
25+
const createTestNavigation = (overrides = {}) => ({
26+
state: "idle",
27+
location: undefined,
28+
formMethod: undefined,
29+
formAction: undefined,
30+
formEncType: undefined,
31+
formData: undefined,
32+
json: undefined,
33+
text: undefined,
34+
...overrides,
35+
});
36+
37+
// Mock de react-router
38+
vi.mock("react-router", () => ({
39+
Form: vi.fn(({ children }) => <form>{children}</form>),
40+
useNavigation: vi.fn(() => createTestNavigation()),
41+
}));
42+
43+
const createTestProps = (
44+
productData: Partial<ProductType> = {}
45+
): Route.ComponentProps => ({
46+
loaderData: { product: createTestProduct(productData) },
47+
params: vi.fn() as any,
48+
matches: vi.fn() as any,
49+
});
250

351
describe("Product Component", () => {
452
describe("Rendering with valid product data", () => {
5-
// Crear un mock de producto de prueba con todos los campos necesarios
6-
// const mockProduct: Product = { id: 1, title: "Test Product", price: 99.99, ... }
7-
853
it("should render product title correctly", () => {
9-
// 1. Renderizar el componente Product con mockProduct usando render()
10-
// 2. Buscar el elemento h1 que contiene el título del producto
11-
// 3. Verificar que el texto coincida con mockProduct.title usando expect().toHaveTextContent()
54+
// Step 1: Setup - Create test props
55+
const props = createTestProps({ title: "Awesome Product" });
56+
// Step 2: Mock - Component mocks already set up above
57+
// Step 3: Call - Render component
58+
render(<Product {...props} />);
59+
// Step 4: Verify - Check title is rendered correctly
60+
const titleElement = screen.getByRole("heading", { level: 1 });
61+
expect(titleElement).toHaveTextContent("Awesome Product");
1262
});
1363

1464
it("should render product price with dollar sign", () => {
15-
// 1. Renderizar el componente Product con mockProduct
16-
// 2. Buscar el elemento que muestra el precio (probablemente un <p> con el precio)
17-
// 3. Verificar que el texto incluya el símbolo $ y el precio correcto
18-
// Tip: usar toHaveTextContent() con el formato "$99.99"
65+
// Step 1: Setup - Create test props
66+
const props = createTestProps({ price: 150.99 });
67+
// Step 2: Mock - Component mocks already set up above
68+
// Step 3: Call - Render component
69+
render(<Product {...props} />);
70+
// Step 4: Verify - Check price is rendered correctly
71+
expect(screen.getByText("$150.99")).toBeInTheDocument();
1972
});
2073

2174
it("should render product description", () => {
22-
// 1. Renderizar el componente Product con mockProduct
23-
// 2. Buscar el párrafo que contiene la descripción del producto
24-
// 3. Verificar que el texto coincida con mockProduct.description
25-
// Nota: considerar el caso donde description puede ser null
75+
// Step 1: Setup - Create test props
76+
const props = createTestProps({
77+
description: "Amazing product",
78+
});
79+
// Step 2: Mock - Component mocks already set up above
80+
// Step 3: Call - Render component
81+
render(<Product {...props} />);
82+
// Step 4: Verify - Check description is rendered
83+
expect(screen.getByText("Amazing product")).toBeInTheDocument();
2684
});
2785

2886
it("should render product image with correct src and alt attributes", () => {
29-
// 1. Renderizar el componente Product con mockProduct
30-
// 2. Buscar la imagen usando getByRole('img') o getByAltText()
31-
// 3. Verificar que el atributo src coincida con mockProduct.imgSrc usando toHaveAttribute()
32-
// 4. Verificar que el atributo alt coincida con mockProduct.title
87+
// Step 1: Setup - Create test props
88+
const props = createTestProps({
89+
imgSrc: "/test-image.jpg",
90+
alt: "Test Product",
91+
});
92+
// Step 2: Mock - Component mocks already set up above
93+
// Step 3: Call - Render component
94+
render(<Product {...props} />);
95+
// Step 4: Verify - Check image attributes
96+
const image = screen.getByRole("img");
97+
expect(image).toHaveAttribute("src", "/test-image.jpg");
98+
expect(image).toHaveAttribute("alt", "Test Product");
3399
});
34100

35101
it("should render all product features as list items", () => {
36-
// 1. Renderizar el componente Product con mockProduct que tenga un array de features
37-
// 2. Buscar todos los elementos <li> dentro de la lista de características
38-
// 3. Verificar que el número de elementos li coincida con mockProduct.features.length
39-
// 4. Verificar que cada feature aparezca en el DOM usando getAllByText() o similar
102+
// Step 1: Setup - Create test props
103+
const features = ["Feature 1", "Feature 2", "Feature 3"];
104+
const props = createTestProps({ features });
105+
// Step 2: Mock - Component mocks already set up above
106+
// Step 3: Call - Render component
107+
render(<Product {...props} />);
108+
// Step 4: Verify - Check features are rendered
109+
features.forEach((feature) => {
110+
expect(screen.getByText(feature)).toBeInTheDocument();
111+
});
40112
});
41113

42114
it('should render "Agregar al Carrito" button', () => {
43-
// 1. Renderizar el componente Product con mockProduct
44-
// 2. Buscar el botón usando getByRole('button') o getByText('Agregar al Carrito')
45-
// 3. Verificar que el botón esté presente en el documento usando toBeInTheDocument()
46-
// 4. Verificar que el botón tenga el valor correcto (productId) usando toHaveAttribute()
115+
// Step 1: Setup - Create test props
116+
const props = createTestProps();
117+
// Step 2: Mock - Component mocks already set up above
118+
// Step 3: Call - Render component
119+
render(<Product {...props} />);
120+
// Step 4: Verify - Check button is present
121+
expect(
122+
screen.getByRole("button", { name: "Agregar al Carrito" })
123+
).toBeInTheDocument();
47124
});
48125
});
49126

50127
describe("Form interactions", () => {
51-
it("should include hidden redirectTo input with correct value");
52-
it("should include productId as button value");
53-
it("should disable button when cart is loading");
128+
it("should include hidden redirectTo input with correct value", () => {
129+
// Step 1: Setup
130+
const productId = 123;
131+
const props = createTestProps({ id: productId });
132+
// Step 2: Mock - Component mocks already set up above
133+
// Step 3: Call
134+
render(<Product {...props} />);
135+
// Step 4: Verify
136+
const redirectInput = screen.getByDisplayValue(`/products/${productId}`);
137+
expect(redirectInput).toBeInTheDocument();
138+
});
139+
140+
it("should disable button when cart is loading", () => {
141+
// Step 1: Setup
142+
const props = createTestProps();
143+
const expectedNavigation = createTestNavigation({ state: "submitting" });
144+
// Step 2: Mock - Override navigation state to simulate loading
145+
vi.mocked(useNavigation).mockReturnValue(expectedNavigation as any);
146+
// Step 3: Call
147+
render(<Product {...props} />);
148+
// Step 4: Verify
149+
const button = screen.getByRole("button");
150+
expect(button).toBeDisabled();
151+
expect(button).toHaveTextContent("Agregando...");
152+
});
54153
});
55154

56155
describe("Error handling", () => {
57-
it("should render NotFound component when product is not provided");
156+
it("should render NotFound component when product is not provided", () => {
157+
// Step 1: Setup - Create props without product
158+
const props = createTestProps();
159+
props.loaderData.product = undefined;
160+
161+
// Step 2: Mock - Mock NotFound component
162+
vi.mock("../not-found", () => ({
163+
default: () => <div data-testid="not-found">Not Found Page</div>,
164+
}));
165+
// Step 3: Call
166+
render(<Product {...props} />);
167+
// Step 4: Verify
168+
expect(screen.getByTestId("not-found")).toBeInTheDocument();
169+
});
58170
});
59171
});

0 commit comments

Comments
 (0)