feat(pasarela): datos inferidos juntos en una pantalla "Datos del local"
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
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
This commit is contained in:
@@ -26,7 +26,7 @@ El sistema SHALL presentar la propuesta de un local como una pasarela (wizard) d
|
|||||||
- **THEN** la pasarela se cierra y se descarta el borrador en curso
|
- **THEN** la pasarela se cierra y se descarta el borrador en curso
|
||||||
|
|
||||||
### Requirement: Orden de las pantallas empezando por Ubicación
|
### Requirement: Orden de las pantallas empezando por Ubicación
|
||||||
La pasarela SHALL presentar las pantallas en este orden: 1) Ubicación, 2) Nombre, 3) Provincia, 4) Categoría, 5) Tipo específico (opcional), 6) Revisión y envío.
|
La pasarela SHALL presentar las pantallas en este orden: 1) Ubicación, 2) Datos del local (nombre, provincia y dirección inferidos, juntos en una misma pantalla), 3) Categoría, 4) Tipo específico (opcional), 5) Revisión y envío.
|
||||||
|
|
||||||
#### Scenario: Primera pantalla
|
#### Scenario: Primera pantalla
|
||||||
- **WHEN** se abre la pasarela
|
- **WHEN** se abre la pasarela
|
||||||
@@ -36,12 +36,12 @@ La pasarela SHALL presentar las pantallas en este orden: 1) Ubicación, 2) Nombr
|
|||||||
- **WHEN** el usuario está en la pantalla de Tipo específico
|
- **WHEN** el usuario está en la pantalla de Tipo específico
|
||||||
- **THEN** puede continuar sin seleccionar ninguna subcategoría
|
- **THEN** puede continuar sin seleccionar ninguna subcategoría
|
||||||
|
|
||||||
### Requirement: Inferencia de Nombre y Provincia desde la Ubicación
|
### Requirement: Inferencia de Nombre, Provincia y Dirección desde la Ubicación
|
||||||
El sistema SHALL pre-rellenar los campos Nombre y Provincia a partir de la ubicación introducida en la primera pantalla (geocodificación inversa), permitiendo al usuario corregirlos.
|
El sistema SHALL pre-rellenar los campos Nombre, Provincia y Dirección a partir de la ubicación introducida en la primera pantalla (geocodificación inversa), mostrándolos juntos en la pantalla de Datos del local y permitiendo al usuario corregirlos (incluida la dirección).
|
||||||
|
|
||||||
#### Scenario: Nombre y provincia pre-rellenados
|
#### Scenario: Datos pre-rellenados en su pantalla conjunta
|
||||||
- **WHEN** el usuario introduce una ubicación válida en la primera pantalla y esta se resuelve con éxito
|
- **WHEN** el usuario introduce una ubicación válida en la primera pantalla y esta se resuelve con éxito
|
||||||
- **THEN** al llegar a las pantallas de Nombre y Provincia ambos campos aparecen pre-rellenados con los valores inferidos
|
- **THEN** al llegar a la pantalla de Datos del local los campos nombre, provincia y dirección aparecen pre-rellenados con los valores inferidos
|
||||||
|
|
||||||
#### Scenario: Valores corregibles
|
#### Scenario: Valores corregibles
|
||||||
- **WHEN** los valores inferidos no son correctos
|
- **WHEN** los valores inferidos no son correctos
|
||||||
@@ -49,7 +49,7 @@ El sistema SHALL pre-rellenar los campos Nombre y Provincia a partir de la ubica
|
|||||||
|
|
||||||
#### Scenario: Degradación sin inferencia
|
#### Scenario: Degradación sin inferencia
|
||||||
- **WHEN** la inferencia falla o devuelve campos vacíos
|
- **WHEN** la inferencia falla o devuelve campos vacíos
|
||||||
- **THEN** las pantallas de Nombre y Provincia aparecen vacías y el usuario los rellena manualmente, sin bloquear la pasarela
|
- **THEN** la pantalla de Datos del local aparece vacía y el usuario la rellena manualmente, sin bloquear la pasarela
|
||||||
|
|
||||||
### Requirement: Sin campo de comentario ni puntuación
|
### Requirement: Sin campo de comentario ni puntuación
|
||||||
La pasarela SHALL NOT incluir el campo de comentario/descripción ni el campo de puntuación; la propuesta se envía sin esos datos.
|
La pasarela SHALL NOT incluir el campo de comentario/descripción ni el campo de puntuación; la propuesta se envía sin esos datos.
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { useEffect, useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import useEsMovil from "../hooks/useEsMovil.js";
|
import useEsMovil from "../hooks/useEsMovil.js";
|
||||||
import PasoUbicacion from "./PasoUbicacion.jsx";
|
import PasoUbicacion from "./PasoUbicacion.jsx";
|
||||||
import PasoNombre from "./PasoNombre.jsx";
|
import PasoDatosLocal from "./PasoDatosLocal.jsx";
|
||||||
import PasoProvincia from "./PasoProvincia.jsx";
|
|
||||||
import PasoCategoria from "./PasoCategoria.jsx";
|
import PasoCategoria from "./PasoCategoria.jsx";
|
||||||
import PasoSubcategoria from "./PasoSubcategoria.jsx";
|
import PasoSubcategoria from "./PasoSubcategoria.jsx";
|
||||||
import PasoResumen from "./PasoResumen.jsx";
|
import PasoResumen from "./PasoResumen.jsx";
|
||||||
@@ -12,8 +11,7 @@ import PasoResumen from "./PasoResumen.jsx";
|
|||||||
// gestiona su propio botón de envío en PasoResumen.
|
// gestiona su propio botón de envío en PasoResumen.
|
||||||
const PASOS = [
|
const PASOS = [
|
||||||
{ id: "ubicacion", titulo: "Ubicación", Componente: PasoUbicacion, puedeContinuar: (f) => f.lat != null && f.lng != null },
|
{ id: "ubicacion", titulo: "Ubicación", Componente: PasoUbicacion, puedeContinuar: (f) => f.lat != null && f.lng != null },
|
||||||
{ id: "nombre", titulo: "Nombre", Componente: PasoNombre, puedeContinuar: (f) => (f.nombre || "").trim().length >= 2 },
|
{ id: "datos", titulo: "Datos del local", Componente: PasoDatosLocal, puedeContinuar: (f) => (f.nombre || "").trim().length >= 2 && !!f.provincia },
|
||||||
{ id: "provincia", titulo: "Provincia", Componente: PasoProvincia, puedeContinuar: (f) => !!f.provincia },
|
|
||||||
{ id: "categoria", titulo: "Categoría", Componente: PasoCategoria, puedeContinuar: (f) => !!f.categoria },
|
{ id: "categoria", titulo: "Categoría", Componente: PasoCategoria, puedeContinuar: (f) => !!f.categoria },
|
||||||
{ id: "subcategoria", titulo: "Tipo específico", Componente: PasoSubcategoria, puedeContinuar: () => true },
|
{ id: "subcategoria", titulo: "Tipo específico", Componente: PasoSubcategoria, puedeContinuar: () => true },
|
||||||
{ id: "resumen", titulo: "Revisión y envío", Componente: PasoResumen, puedeContinuar: () => true },
|
{ id: "resumen", titulo: "Revisión y envío", Componente: PasoResumen, puedeContinuar: () => true },
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import { inputStyle } from "../styles/shared.js";
|
||||||
|
import { PROVINCIAS } from "../constants/provincias.js";
|
||||||
|
|
||||||
|
const etiqueta = { fontSize: 12, color: "var(--color-text-secondary)", display: "block", marginBottom: 4 };
|
||||||
|
|
||||||
|
// Paso 2 de la pasarela: los datos inferidos desde la ubicación (nombre,
|
||||||
|
// provincia y dirección), pre-rellenados y editables en una misma pantalla.
|
||||||
|
// Si la inferencia falló o no casó con el listado, llegan vacíos y se
|
||||||
|
// rellenan a mano, sin bloquear la pasarela.
|
||||||
|
export default function PasoDatosLocal({ form, setForm }) {
|
||||||
|
const algunoInferido = !!(form.nombre || form.provincia || form.direccion);
|
||||||
|
return (
|
||||||
|
<div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
|
||||||
|
{algunoInferido && (
|
||||||
|
<div role="status" style={{
|
||||||
|
display: "flex", alignItems: "flex-start", gap: 6,
|
||||||
|
background: "#EAFAF1", color: "#1E8449",
|
||||||
|
padding: "6px 10px", borderRadius: "var(--border-radius-md)", fontSize: 12,
|
||||||
|
}}>
|
||||||
|
<i className="ti ti-sparkles" aria-hidden="true" style={{ fontSize: 14, flexShrink: 0, marginTop: 1 }}></i>
|
||||||
|
<span style={{ lineHeight: 1.4 }}>
|
||||||
|
Rellenados automáticamente desde la ubicación: compruébalos y corrige lo que haga falta.
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label htmlFor="paso-nombre" style={etiqueta}>
|
||||||
|
Nombre del local <span style={{ color: "#8B0000" }}>*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="paso-nombre"
|
||||||
|
style={inputStyle}
|
||||||
|
autoComplete="off"
|
||||||
|
placeholder="Bar El Olivo, Clínica San José..."
|
||||||
|
value={form.nombre}
|
||||||
|
onChange={(e) => setForm((f) => ({ ...f, nombre: e.target.value }))}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label htmlFor="paso-provincia" style={etiqueta}>
|
||||||
|
Provincia <span style={{ color: "#8B0000" }}>*</span>
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
id="paso-provincia"
|
||||||
|
style={inputStyle}
|
||||||
|
value={form.provincia}
|
||||||
|
onChange={(e) => setForm((f) => ({ ...f, provincia: e.target.value }))}
|
||||||
|
>
|
||||||
|
<option value="">Selecciona provincia...</option>
|
||||||
|
{PROVINCIAS.map((p) => <option key={p} value={p}>{p}</option>)}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label htmlFor="paso-direccion" style={etiqueta}>Dirección</label>
|
||||||
|
<input
|
||||||
|
id="paso-direccion"
|
||||||
|
style={inputStyle}
|
||||||
|
autoComplete="off"
|
||||||
|
placeholder="Calle, número, ciudad"
|
||||||
|
value={form.direccion}
|
||||||
|
onChange={(e) => setForm((f) => ({ ...f, direccion: e.target.value }))}
|
||||||
|
aria-describedby="paso-direccion-ayuda"
|
||||||
|
/>
|
||||||
|
<p id="paso-direccion-ayuda" style={{ fontSize: 11, color: "var(--color-text-tertiary)", margin: "4px 0 0" }}>
|
||||||
|
Opcional: se rellena automáticamente desde la ubicación; corrígela si hace falta.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import { inputStyle } from "../styles/shared.js";
|
|
||||||
|
|
||||||
// Paso 2 de la pasarela: nombre del local, pre-rellenado con lo inferido
|
|
||||||
// desde la ubicación (editable). Si la inferencia falló, llega vacío.
|
|
||||||
export default function PasoNombre({ form, setForm }) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<label htmlFor="paso-nombre" style={{ fontSize: 12, color: "var(--color-text-secondary)", display: "block", marginBottom: 4 }}>
|
|
||||||
Nombre del local <span style={{ color: "#8B0000" }}>*</span>
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
id="paso-nombre"
|
|
||||||
style={inputStyle}
|
|
||||||
autoComplete="off"
|
|
||||||
placeholder="Bar El Olivo, Clínica San José..."
|
|
||||||
value={form.nombre}
|
|
||||||
onChange={(e) => setForm((f) => ({ ...f, nombre: e.target.value }))}
|
|
||||||
/>
|
|
||||||
<p style={{ fontSize: 11, color: "var(--color-text-tertiary)", margin: "6px 0 0" }}>
|
|
||||||
{form.nombre
|
|
||||||
? "Si el nombre se rellenó automáticamente desde la ubicación, comprueba que es correcto antes de continuar."
|
|
||||||
: "El nombre con el que la gente conoce el negocio."}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import { inputStyle } from "../styles/shared.js";
|
|
||||||
import { PROVINCIAS } from "../constants/provincias.js";
|
|
||||||
|
|
||||||
// Paso 3 de la pasarela: provincia, pre-rellenada con lo inferido desde la
|
|
||||||
// ubicación (si la inferencia falló o no casó con el listado, llega vacía).
|
|
||||||
export default function PasoProvincia({ form, setForm }) {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<label htmlFor="paso-provincia" style={{ fontSize: 12, color: "var(--color-text-secondary)", display: "block", marginBottom: 4 }}>
|
|
||||||
Provincia <span style={{ color: "#8B0000" }}>*</span>
|
|
||||||
</label>
|
|
||||||
<select
|
|
||||||
id="paso-provincia"
|
|
||||||
style={inputStyle}
|
|
||||||
value={form.provincia}
|
|
||||||
onChange={(e) => setForm((f) => ({ ...f, provincia: e.target.value }))}
|
|
||||||
>
|
|
||||||
<option value="">Selecciona provincia...</option>
|
|
||||||
{PROVINCIAS.map((p) => <option key={p} value={p}>{p}</option>)}
|
|
||||||
</select>
|
|
||||||
<p style={{ fontSize: 11, color: "var(--color-text-tertiary)", margin: "6px 0 0" }}>
|
|
||||||
Si se rellenó automáticamente desde la ubicación, verifica que es correcta.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -20,6 +20,7 @@ const FORM_RESUELTO = {
|
|||||||
proveedorUbicacion: "coordenadas",
|
proveedorUbicacion: "coordenadas",
|
||||||
lat: 41.3851, lng: 2.1734,
|
lat: 41.3851, lng: 2.1734,
|
||||||
nombre: "Bar X", provincia: "Barcelona",
|
nombre: "Bar X", provincia: "Barcelona",
|
||||||
|
direccion: "Calle Mayor 5, Alcalá de Henares",
|
||||||
categoria: "Restauración", subcategoria: "Tapas y raciones",
|
categoria: "Restauración", subcategoria: "Tapas y raciones",
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -41,14 +42,13 @@ const propsBase = {
|
|||||||
const siguiente = () => screen.getByRole("button", { name: "Siguiente" });
|
const siguiente = () => screen.getByRole("button", { name: "Siguiente" });
|
||||||
|
|
||||||
describe("ModalPasarela", () => {
|
describe("ModalPasarela", () => {
|
||||||
it("arranca en la pantalla de Ubicación con las 6 pantallas declaradas", () => {
|
it("arranca en la pantalla de Ubicación con las 5 pantallas declaradas", () => {
|
||||||
render(<ModalPasarela {...propsBase} form={FORM_VACIO} />);
|
render(<ModalPasarela {...propsBase} form={FORM_VACIO} />);
|
||||||
expect(screen.getByLabelText(/ubicación del local/i)).toBeTruthy();
|
expect(screen.getByLabelText(/ubicación del local/i)).toBeTruthy();
|
||||||
expect(screen.getByText(/paso 1 de 6/i)).toBeTruthy();
|
expect(screen.getByText(/paso 1 de 5/i)).toBeTruthy();
|
||||||
expect(screen.getAllByRole("generic", { hidden: true })).toBeTruthy();
|
// indicador de progreso: 5 barras
|
||||||
// indicador de progreso: 6 barras
|
|
||||||
const barras = document.querySelectorAll('[aria-hidden="true"] span[style*="width: 16px"]');
|
const barras = document.querySelectorAll('[aria-hidden="true"] span[style*="width: 16px"]');
|
||||||
expect(barras.length).toBe(6);
|
expect(barras.length).toBe(5);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Siguiente deshabilitado sin resolución de ubicación válida", () => {
|
it("Siguiente deshabilitado sin resolución de ubicación válida", () => {
|
||||||
@@ -56,12 +56,14 @@ describe("ModalPasarela", () => {
|
|||||||
expect(siguiente().disabled).toBe(true);
|
expect(siguiente().disabled).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Siguiente habilitado con datos válidos y navega; Atrás conserva los datos", () => {
|
it("Siguiente habilitado con datos válidos y navega; los datos inferidos comparten pantalla; Atrás conserva", () => {
|
||||||
render(<ModalPasarela {...propsBase} form={FORM_RESUELTO} />);
|
render(<ModalPasarela {...propsBase} form={FORM_RESUELTO} />);
|
||||||
expect(siguiente().disabled).toBe(false);
|
expect(siguiente().disabled).toBe(false);
|
||||||
fireEvent.click(siguiente());
|
fireEvent.click(siguiente());
|
||||||
// paso 2: nombre pre-rellenado con lo inferido
|
// 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(/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 }));
|
fireEvent.click(screen.getByRole("button", { name: /atrás/i }));
|
||||||
// vuelve a ubicación con la entrada conservada
|
// vuelve a ubicación con la entrada conservada
|
||||||
@@ -105,7 +107,7 @@ describe("ModalPasarela", () => {
|
|||||||
|
|
||||||
it("en el paso final muestra el resumen con su propio botón de envío", () => {
|
it("en el paso final muestra el resumen con su propio botón de envío", () => {
|
||||||
render(<ModalPasarela {...propsBase} form={FORM_RESUELTO} />);
|
render(<ModalPasarela {...propsBase} form={FORM_RESUELTO} />);
|
||||||
for (let i = 0; i < 5; i++) fireEvent.click(siguiente());
|
for (let i = 0; i < 4; i++) fireEvent.click(siguiente());
|
||||||
expect(screen.getByRole("button", { name: /enviar propuesta/i })).toBeTruthy();
|
expect(screen.getByRole("button", { name: /enviar propuesta/i })).toBeTruthy();
|
||||||
expect(screen.queryByRole("button", { name: "Siguiente" })).toBeNull();
|
expect(screen.queryByRole("button", { name: "Siguiente" })).toBeNull();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,8 +2,7 @@
|
|||||||
import { afterEach, describe, expect, it } from "vitest";
|
import { afterEach, describe, expect, it } from "vitest";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
|
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
|
||||||
import PasoNombre from "../../src/components/PasoNombre.jsx";
|
import PasoDatosLocal from "../../src/components/PasoDatosLocal.jsx";
|
||||||
import PasoProvincia from "../../src/components/PasoProvincia.jsx";
|
|
||||||
import PasoCategoria from "../../src/components/PasoCategoria.jsx";
|
import PasoCategoria from "../../src/components/PasoCategoria.jsx";
|
||||||
import PasoSubcategoria from "../../src/components/PasoSubcategoria.jsx";
|
import PasoSubcategoria from "../../src/components/PasoSubcategoria.jsx";
|
||||||
import { PROVINCIAS } from "../../src/constants/provincias.js";
|
import { PROVINCIAS } from "../../src/constants/provincias.js";
|
||||||
@@ -12,8 +11,7 @@ import { CATEGORIAS, NOMBRES_CATEGORIAS } from "../../src/constants/categorias.j
|
|||||||
afterEach(cleanup);
|
afterEach(cleanup);
|
||||||
|
|
||||||
// Harness con useState real: los pasos escriben a través de setForm y el
|
// Harness con useState real: los pasos escriben a través de setForm y el
|
||||||
// estado resultante se lee desde fuera (más fiel que espiar el updater,
|
// estado resultante se lee desde fuera (más fiel que espiar el updater).
|
||||||
// cuya evaluación diferida pierde el valor del evento en React 18).
|
|
||||||
function renderPaso(Componente, inicial) {
|
function renderPaso(Componente, inicial) {
|
||||||
const estado = { form: inicial };
|
const estado = { form: inicial };
|
||||||
function Wrapper() {
|
function Wrapper() {
|
||||||
@@ -25,35 +23,38 @@ function renderPaso(Componente, inicial) {
|
|||||||
return estado;
|
return estado;
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("PasoNombre", () => {
|
describe("PasoDatosLocal (datos inferidos en una pantalla)", () => {
|
||||||
it("muestra el nombre inferido y es editable", () => {
|
it("muestra los tres campos pre-rellenados con lo inferido, en la misma pantalla", () => {
|
||||||
const estado = renderPaso(PasoNombre, { nombre: "Bar El Olivo" });
|
renderPaso(PasoDatosLocal, { nombre: "Puerta del Sol", provincia: "Madrid", direccion: "Puerta del Sol, 28013 Madrid" });
|
||||||
const input = screen.getByLabelText(/nombre del local/i);
|
expect(screen.getByLabelText(/nombre del local/i).value).toBe("Puerta del Sol");
|
||||||
expect(input.value).toBe("Bar El Olivo");
|
expect(screen.getByLabelText(/provincia/i).value).toBe("Madrid");
|
||||||
fireEvent.change(input, { target: { value: "Bar La Plaza" } });
|
expect(screen.getByLabelText(/^dirección/i).value).toBe("Puerta del Sol, 28013 Madrid");
|
||||||
|
// aviso de que vienen de la inferencia
|
||||||
|
expect(screen.getByText(/Rellenados automáticamente desde la ubicación/i)).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("todos editables: nombre (input), provincia (select PROVINCIAS), dirección (input)", () => {
|
||||||
|
const estado = renderPaso(PasoDatosLocal, { nombre: "Bar X", provincia: "Barcelona", direccion: "Calle 1" });
|
||||||
|
|
||||||
|
fireEvent.change(screen.getByLabelText(/nombre del local/i), { target: { value: "Bar La Plaza" } });
|
||||||
expect(estado.form.nombre).toBe("Bar La Plaza");
|
expect(estado.form.nombre).toBe("Bar La Plaza");
|
||||||
});
|
|
||||||
|
|
||||||
it("vacío si la inferencia falló", () => {
|
|
||||||
renderPaso(PasoNombre, { nombre: "" });
|
|
||||||
expect(screen.getByLabelText(/nombre del local/i).value).toBe("");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("PasoProvincia", () => {
|
|
||||||
it("es un select con el listado PROVINCIAS y la provincia pre-rellenada", () => {
|
|
||||||
renderPaso(PasoProvincia, { provincia: "La Coruña" });
|
|
||||||
const select = screen.getByLabelText(/provincia/i);
|
const select = screen.getByLabelText(/provincia/i);
|
||||||
expect(select.value).toBe("La Coruña");
|
|
||||||
const opciones = [...select.options].map((o) => o.value);
|
const opciones = [...select.options].map((o) => o.value);
|
||||||
expect(opciones).toEqual(["", ...PROVINCIAS]);
|
expect(opciones).toEqual(["", ...PROVINCIAS]);
|
||||||
fireEvent.change(select, { target: { value: "Madrid" } });
|
fireEvent.change(select, { target: { value: "Gerona" } });
|
||||||
// (la aserción de estado se cubre con el harness en el resto de pasos)
|
expect(estado.form.provincia).toBe("Gerona");
|
||||||
|
|
||||||
|
fireEvent.change(screen.getByLabelText(/^dirección/i), { target: { value: "Calle Nueva 2" } });
|
||||||
|
expect(estado.form.direccion).toBe("Calle Nueva 2");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("sin coincidencia queda vacío (selección manual)", () => {
|
it("vacíos y sin aviso de inferencia cuando la inferencia falló", () => {
|
||||||
renderPaso(PasoProvincia, { provincia: "" });
|
renderPaso(PasoDatosLocal, { nombre: "", provincia: "", direccion: "" });
|
||||||
|
expect(screen.getByLabelText(/nombre del local/i).value).toBe("");
|
||||||
expect(screen.getByLabelText(/provincia/i).value).toBe("");
|
expect(screen.getByLabelText(/provincia/i).value).toBe("");
|
||||||
|
expect(screen.getByLabelText(/^dirección/i).value).toBe("");
|
||||||
|
expect(screen.queryByText(/Rellenados automáticamente/i)).toBeNull();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user