// @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(); 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(); expect(siguiente().disabled).toBe(true); }); it("Siguiente habilitado con datos válidos y navega; los datos inferidos comparten pantalla; Atrás conserva", () => { render(); 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(); fireEvent.keyDown(screen.getByRole("dialog"), { key: "Escape" }); expect(propsBase.onCerrar).toHaveBeenCalledTimes(1); }); it("✕ cierra el modal", () => { render(); fireEvent.click(screen.getByRole("button", { name: "Cerrar" })); expect(propsBase.onCerrar).toHaveBeenCalledTimes(1); }); it("foco inicial en el primer control de cada pantalla", () => { render(); 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(); // 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(); 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(); }); });