feat(propuesta): pasarela paso a paso con autodetección de ubicación y FAB
deploy-branch / deploy (push) Failing after 6s
deploy-branch / teardown (push) Skipped

- 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:
2026-08-16 01:35:02 +02:00
parent d48b74cbd4
commit 003b1e209f
53 changed files with 4896 additions and 253 deletions
+67
View File
@@ -0,0 +1,67 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import ProveedorWaze from "../../src/utils/ubicacion/proveedorWaze.js";
const respuestaNominatim = (resultados) => ({
ok: true,
json: async () => resultados,
});
afterEach(() => vi.unstubAllGlobals());
describe("ProveedorWaze", () => {
const p = new ProveedorWaze();
it("detecta URLs de Waze", () => {
expect(p.detecta("https://waze.com/ul?ll=41.38,2.17&navigate=yes")).toBe(true);
expect(p.detecta("https://www.waze.com/live-map/")).toBe(true);
expect(p.detecta("waze.to/abc")).toBe(true);
expect(p.detecta("https://google.com/maps")).toBe(false);
});
it("extrae de ll=lat,lng", async () => {
expect(await p.extrae("https://waze.com/ul?ll=41.3851,2.1734&navigate=yes"))
.toEqual({ proveedor: "waze", lat: 41.3851, lng: 2.1734 });
// sin esquema https://
expect(await p.extrae("waze.com/ul?ll=43.263,-2.935"))
.toEqual({ proveedor: "waze", lat: 43.263, lng: -2.935 });
});
it("ll malformado → null sin consultar Nominatim", async () => {
const fetchMock = vi.fn();
vi.stubGlobal("fetch", fetchMock);
expect(await p.extrae("https://waze.com/ul?ll=abc,def")).toBeNull();
expect(fetchMock).not.toHaveBeenCalled();
});
it("q=texto delega en la búsqueda de Nominatim marcando proveedor waze", async () => {
const fetchMock = vi.fn(() => respuestaNominatim([{
lat: "40.4828632", lon: "-3.3652613",
display_name: "5, Calle Mayor, Alcalá de Henares, Comunidad de Madrid, 28801, España",
address: { city: "Alcalá de Henares", state: "Comunidad de Madrid" },
namedetails: { name: "" },
}]));
vi.stubGlobal("fetch", fetchMock);
const r = await p.extrae("https://waze.com/ul?q=Calle%20Mayor%205%2C%20Alcal%C3%A1%20de%20Henares");
expect(r).toEqual({
proveedor: "waze",
lat: 40.4828632,
lng: -3.3652613,
nombre: "",
provincia: "Madrid",
direccion: "5, Calle Mayor, Alcalá de Henares, Comunidad de Madrid, 28801, España",
});
// la petición lleva countrycodes=es y las cabeceras de Nominatim
const [url, init] = fetchMock.mock.calls[0];
expect(String(url)).toContain("countrycodes=es");
expect(init.headers["User-Agent"]).toBe("LocalesEspanoles/1.0");
});
it("q=texto sin resultados en Nominatim → null", async () => {
vi.stubGlobal("fetch", vi.fn(() => respuestaNominatim([])));
expect(await p.extrae("https://waze.com/ul?q=calle%20inexistente%20zzzz")).toBeNull();
});
it("URL de Waze sin ll ni q → null", async () => {
expect(await p.extrae("https://waze.com/live-map/")).toBeNull();
});
});