deploy-branch / deploy (push) Successful in 2m18s
deploy-branch / teardown (push) Skipped
tests / Unitarias + componente + build (pull_request) Successful in 2m23s
tests / Integración (Nominatim real, opt-in) (pull_request) Skipped
tests / Unitarias + componente + build (push) Successful in 2m30s
tests / Integración (Nominatim real, opt-in) (push) Skipped
- Nuevo PasoDatosLocal con nombre, provincia y dirección (ahora editable) en la misma pantalla, con aviso cuando vienen de la inferencia - El wizard pasa de 6 a 5 pasos: Ubicación → Datos del local → Categoría → Tipo específico → Resumen - Eliminados PasoNombre y PasoProvincia (absorbidos) - Spec principal actualizada al nuevo orden de pantallas
115 lines
4.6 KiB
React
115 lines
4.6 KiB
React
// @vitest-environment jsdom
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
|
|
import ModalPasarela from "../../src/components/ModalPasarela.jsx";
|
|
|
|
beforeEach(() => {
|
|
vi.clearAllMocks(); // los mocks de propsBase se comparten entre pruebas
|
|
});
|
|
afterEach(cleanup);
|
|
|
|
const FORM_VACIO = {
|
|
nombre: "", provincia: "", categoria: "", subcategoria: "",
|
|
direccion: "", enlaceGoogleMaps: "", lat: null, lng: null,
|
|
entradaUbicacion: "", proveedorUbicacion: "",
|
|
};
|
|
|
|
const FORM_RESUELTO = {
|
|
...FORM_VACIO,
|
|
entradaUbicacion: "41.3851, 2.1734",
|
|
proveedorUbicacion: "coordenadas",
|
|
lat: 41.3851, lng: 2.1734,
|
|
nombre: "Bar X", provincia: "Barcelona",
|
|
direccion: "Calle Mayor 5, Alcalá de Henares",
|
|
categoria: "Restauración", subcategoria: "Tapas y raciones",
|
|
};
|
|
|
|
const propsBase = {
|
|
setForm: vi.fn(),
|
|
onCerrar: vi.fn(),
|
|
onEnviar: vi.fn(),
|
|
onConfirmarDuplicado: vi.fn(),
|
|
onDescartarDuplicado: vi.fn(),
|
|
aceptaPrivacidad: false,
|
|
setAceptaPrivacidad: vi.fn(),
|
|
onAbrirPrivacidad: vi.fn(),
|
|
errorFiltro: "",
|
|
posibleDuplicado: null,
|
|
comprobandoDuplicado: false,
|
|
saving: false,
|
|
};
|
|
|
|
const siguiente = () => screen.getByRole("button", { name: "Siguiente" });
|
|
|
|
describe("ModalPasarela", () => {
|
|
it("arranca en la pantalla de Ubicación con las 5 pantallas declaradas", () => {
|
|
render(<ModalPasarela {...propsBase} form={FORM_VACIO} />);
|
|
expect(screen.getByLabelText(/ubicación del local/i)).toBeTruthy();
|
|
expect(screen.getByText(/paso 1 de 5/i)).toBeTruthy();
|
|
// indicador de progreso: 5 barras
|
|
const barras = document.querySelectorAll('[aria-hidden="true"] span[style*="width: 16px"]');
|
|
expect(barras.length).toBe(5);
|
|
});
|
|
|
|
it("Siguiente deshabilitado sin resolución de ubicación válida", () => {
|
|
render(<ModalPasarela {...propsBase} form={FORM_VACIO} />);
|
|
expect(siguiente().disabled).toBe(true);
|
|
});
|
|
|
|
it("Siguiente habilitado con datos válidos y navega; los datos inferidos comparten pantalla; Atrás conserva", () => {
|
|
render(<ModalPasarela {...propsBase} form={FORM_RESUELTO} />);
|
|
expect(siguiente().disabled).toBe(false);
|
|
fireEvent.click(siguiente());
|
|
// paso 2: datos del local (nombre, provincia y dirección) en la misma pantalla
|
|
expect(screen.getByLabelText(/nombre del local/i).value).toBe("Bar X");
|
|
expect(screen.getByLabelText(/provincia/i).value).toBe("Barcelona");
|
|
expect(screen.getByLabelText(/^dirección/i).value).toBe("Calle Mayor 5, Alcalá de Henares");
|
|
|
|
fireEvent.click(screen.getByRole("button", { name: /atrás/i }));
|
|
// vuelve a ubicación con la entrada conservada
|
|
expect(screen.getByLabelText(/ubicación del local/i).value).toBe("41.3851, 2.1734");
|
|
});
|
|
|
|
it("Escape cierra el modal (descarta el borrador vía onCerrar)", () => {
|
|
render(<ModalPasarela {...propsBase} form={FORM_RESUELTO} />);
|
|
fireEvent.keyDown(screen.getByRole("dialog"), { key: "Escape" });
|
|
expect(propsBase.onCerrar).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("✕ cierra el modal", () => {
|
|
render(<ModalPasarela {...propsBase} form={FORM_RESUELTO} />);
|
|
fireEvent.click(screen.getByRole("button", { name: "Cerrar" }));
|
|
expect(propsBase.onCerrar).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("foco inicial en el primer control de cada pantalla", () => {
|
|
render(<ModalPasarela {...propsBase} form={FORM_RESUELTO} />);
|
|
expect(document.activeElement).toBe(screen.getByLabelText(/ubicación del local/i));
|
|
|
|
fireEvent.click(siguiente());
|
|
expect(document.activeElement).toBe(screen.getByLabelText(/nombre del local/i));
|
|
});
|
|
|
|
it("Tab queda contenido dentro del modal (wrap del primer al último focusable)", () => {
|
|
render(<ModalPasarela {...propsBase} form={FORM_RESUELTO} />);
|
|
// primer focusable del panel (el ✕ precede al cuerpo en el DOM)
|
|
const cerrar = screen.getByRole("button", { name: "Cerrar" });
|
|
cerrar.focus();
|
|
|
|
// Shift+Tab desde el primero → salta al último (Siguiente)
|
|
fireEvent.keyDown(document.activeElement, { key: "Tab", shiftKey: true });
|
|
expect(document.activeElement).toBe(siguiente());
|
|
|
|
// Tab desde el último → vuelve al primero
|
|
fireEvent.keyDown(document.activeElement, { key: "Tab" });
|
|
expect(document.activeElement).toBe(cerrar);
|
|
});
|
|
|
|
it("en el paso final muestra el resumen con su propio botón de envío", () => {
|
|
render(<ModalPasarela {...propsBase} form={FORM_RESUELTO} />);
|
|
for (let i = 0; i < 4; i++) fireEvent.click(siguiente());
|
|
expect(screen.getByRole("button", { name: /enviar propuesta/i })).toBeTruthy();
|
|
expect(screen.queryByRole("button", { name: "Siguiente" })).toBeNull();
|
|
});
|
|
});
|