// @vitest-environment jsdom import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { cleanup, fireEvent, render, screen } from "@testing-library/react"; import BotonFlotante from "../../src/components/BotonFlotante.jsx"; const matchMediaMock = (esMovil) => (_query) => ({ matches: esMovil, media: _query, addEventListener: () => {}, removeEventListener: () => {}, addListener: () => {}, removeListener: () => {}, }); beforeEach(() => { // por defecto PC (botón grande); cada prueba puede sobreescribirlo vi.stubGlobal("matchMedia", matchMediaMock(false)); }); afterEach(() => { vi.unstubAllGlobals(); cleanup(); }); describe("BotonFlotante", () => { it("expone su propósito con aria-label (accesible sin hover)", () => { render( {}} />); expect(screen.getByRole("button", { name: "Añade un nuevo local al directorio" })).toBeTruthy(); }); it("tooltip oculto por defecto y visible al recibir foco", () => { render( {}} />); expect(screen.queryByRole("tooltip")).toBeNull(); fireEvent.focus(screen.getByRole("button")); expect(screen.getByRole("tooltip").textContent).toBe("Añade un nuevo local al directorio"); fireEvent.blur(screen.getByRole("button")); expect(screen.queryByRole("tooltip")).toBeNull(); }); it("tooltip también en hover", () => { render( {}} />); fireEvent.mouseEnter(screen.getByRole("button")); expect(screen.getByRole("tooltip")).toBeTruthy(); fireEvent.mouseLeave(screen.getByRole("button")); expect(screen.queryByRole("tooltip")).toBeNull(); }); it("al pulsarlo abre la pasarela (onClick)", () => { const onClick = vi.fn(); render(); fireEvent.click(screen.getByRole("button")); expect(onClick).toHaveBeenCalledTimes(1); }); it("está abajo a la derecha, alineado con el borde derecho del cuerpo central, y en PC es más grande", () => { render( {}} />); const boton = screen.getByRole("button"); const contenedor = boton.closest("div").parentElement; // button → relativo → fijo expect(contenedor.style.position).toBe("fixed"); expect(contenedor.style.bottom).toBe("0px"); // en pantallas anchas acompaña a la columna de 720px; mínimo 16px expect(contenedor.getAttribute("style")).toMatch(/right:\s*max\(16px,\s*[^;]*100vw[^;]*720px[^;]*16px[^;]*\)/); // en PC el FAB mide 64px (en móvil 54) expect(boton.style.width).toBe("64px"); expect(boton.style.height).toBe("64px"); expect(boton.style.fontSize).toBe("28px"); }); it("en móvil mantiene el tamaño compacto (54px)", () => { vi.stubGlobal("matchMedia", matchMediaMock(true)); render( {}} />); const boton = screen.getByRole("button"); expect(boton.style.width).toBe("54px"); expect(boton.style.fontSize).toBe("24px"); }); });