feat(propuesta): pasarela paso a paso con autodetección de ubicación y FAB
- Campo universal de ubicación (Google Maps/Waze/OSM/coordenadas/dirección) sobre proveedores extensibles (src/utils/ubicacion) con resolver e inferencia inversa Nominatim (nombre/provincia pre-rellenados, alias cooficiales) - FAB flotante abajo a la derecha que abre la pasarela en un modal accesible (Escape, foco contenido, responsive); sin descripción ni puntuación; avisos de duplicado y filtro de palabras dentro del modal - Aviso PWA reposicionado: tarjeta abajo a la izquierda en móvil, botón compacto arriba a la izquierda en PC (no tapa el FAB) - Suite de pruebas: 88 unitarias/componente (vitest + RTL, offline) y 10 de integración (supertest + SQLite :memory: y Nominatim real opt-in) - CI: workflow de Gitea Actions con npm test + build en push/PR e integración manual
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import ProveedorUbicacion from "../../src/utils/ubicacion/ProveedorUbicacion.js";
|
||||
|
||||
describe("ProveedorUbicacion (clase base)", () => {
|
||||
it("normaliza: minúsculas, sin tildes, sin espacios extremos", () => {
|
||||
const p = new ProveedorUbicacion();
|
||||
expect(p.normalizar(" ÁLAVA ")).toBe("alava");
|
||||
expect(p.normalizar("Guipúzcoa")).toBe("guipuzcoa");
|
||||
expect(ProveedorUbicacion.normalizar("MA DRID")).toBe("ma drid");
|
||||
expect(p.normalizar(null)).toBe("");
|
||||
});
|
||||
|
||||
describe("emparejarProvincia", () => {
|
||||
const p = new ProveedorUbicacion();
|
||||
|
||||
it("coincidencia exacta insensible a acentos y mayúsculas", () => {
|
||||
expect(p.emparejarProvincia("ALAVA")).toBe("Álava");
|
||||
expect(p.emparejarProvincia("Guipuzcoa")).toBe("Guipúzcoa");
|
||||
});
|
||||
|
||||
it("coincidencia parcial dentro de un texto (Comunidad de Madrid)", () => {
|
||||
expect(p.emparejarProvincia("Comunidad de Madrid")).toBe("Madrid");
|
||||
expect(p.emparejarProvincia("Santiago, A Coruña, Galicia")).toBe("La Coruña");
|
||||
});
|
||||
|
||||
it("tolera artículos anteuestos (A Coruña → La Coruña)", () => {
|
||||
expect(p.emparejarProvincia("A Coruña")).toBe("La Coruña");
|
||||
expect(p.emparejarProvincia("Ourense")).toBe("Orense");
|
||||
});
|
||||
|
||||
it("alias de nombres cooficiales (D12)", () => {
|
||||
expect(p.emparejarProvincia("Bizkaia")).toBe("Vizcaya");
|
||||
expect(p.emparejarProvincia("Gipuzkoa")).toBe("Guipúzcoa");
|
||||
expect(p.emparejarProvincia("Girona")).toBe("Gerona");
|
||||
expect(p.emparejarProvincia("Lleida")).toBe("Lérida");
|
||||
expect(p.emparejarProvincia("Illes Balears")).toBe("Islas Baleares");
|
||||
expect(p.emparejarProvincia("Asturies")).toBe("Asturias");
|
||||
});
|
||||
|
||||
it("devuelve \"\" cuando no casa con ninguna provincia", () => {
|
||||
expect(p.emparejarProvincia("Île-de-France")).toBe("");
|
||||
expect(p.emparejarProvincia("")).toBe("");
|
||||
expect(p.emparejarProvincia("Lisboa")).toBe("");
|
||||
});
|
||||
});
|
||||
|
||||
it("el contrato base no acepta ninguna entrada", () => {
|
||||
const p = new ProveedorUbicacion();
|
||||
expect(p.detecta("41.38, 2.17")).toBe(false);
|
||||
expect(p.detecta("https://maps.google.com")).toBe(false);
|
||||
});
|
||||
|
||||
it("extrae() de la base lanza \"sin implementar\" (método abstracto)", async () => {
|
||||
const p = new ProveedorUbicacion();
|
||||
await expect(p.extrae("x")).rejects.toThrow("sin implementar");
|
||||
});
|
||||
|
||||
describe("fetchNominatim degrada errores a null", () => {
|
||||
it("devuelve null si fetch lanza (sin red)", async () => {
|
||||
const p = new ProveedorUbicacion();
|
||||
vi.stubGlobal("fetch", vi.fn(() => Promise.reject(new Error("sin red"))));
|
||||
expect(await p.fetchNominatim("https://nominatim.openstreetmap.org/x")).toBeNull();
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
it("devuelve null si la respuesta HTTP no es ok", async () => {
|
||||
const p = new ProveedorUbicacion();
|
||||
vi.stubGlobal("fetch", vi.fn(() => Promise.resolve({ ok: false, status: 503 })));
|
||||
expect(await p.fetchNominatim("https://nominatim.openstreetmap.org/x")).toBeNull();
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
it("devuelve el JSON cuando todo va bien y manda cabeceras", async () => {
|
||||
const p = new ProveedorUbicacion();
|
||||
const fetchMock = vi.fn(() => Promise.resolve({ ok: true, json: () => Promise.resolve([{ lat: "1" }]) }));
|
||||
vi.stubGlobal("fetch", fetchMock);
|
||||
expect(await p.fetchNominatim("https://nominatim.openstreetmap.org/x")).toEqual([{ lat: "1" }]);
|
||||
const [, init] = fetchMock.mock.calls[0];
|
||||
expect(init.headers["Accept-Language"]).toBe("es");
|
||||
expect(init.headers["User-Agent"]).toBe("LocalesEspanoles/1.0");
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user