Merge pull request 'лендинг: этаж перестал теряться на переходе к проверке' (#3279) from fix/mera-keep-floor-in-draft into main
All checks were successful
Deploy Trade-In / changes (push) Successful in 13s
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / test (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-frontend (push) Successful in 2m9s
Deploy Trade-In / deploy (push) Successful in 58s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 12s
All checks were successful
Deploy Trade-In / changes (push) Successful in 13s
Deploy Trade-In / build-backend (push) Has been skipped
Deploy Trade-In / test (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-frontend (push) Successful in 2m9s
Deploy Trade-In / deploy (push) Successful in 58s
Deploy Trade-In / deploy-status (push) Successful in 1s
Deploy Trade-In / perimeter-smoke (push) Successful in 12s
This commit is contained in:
commit
65dfdee63d
4 changed files with 112 additions and 2 deletions
|
|
@ -82,3 +82,38 @@ describe("черновик", () => {
|
|||
expect(draft?.area).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Этаж — обязательное поле карточки лендинга, и до 30.08.2026 оно терялось на
|
||||
* переходе: `saveDraft` его не клал, а на целевой странице поля этажа нет
|
||||
* вовсе. Комментарий в коде объяснял это тем, что «повторно набрать одно
|
||||
* короткое поле дешевле» — переспросить было негде.
|
||||
*
|
||||
* Проверяем ОБА конца: и запись, и чтение. Поле, которое сохраняется, но не
|
||||
* читается обратно, теряется ровно так же, только незаметнее.
|
||||
*/
|
||||
describe("этаж переживает переход на целевую страницу", () => {
|
||||
it("сохранённый этаж читается обратно тем же значением", () => {
|
||||
saveDraft({ address: "Ленина 5", rooms: "2", area: "54", floor: "7/16" });
|
||||
expect(takeDraft()?.floor).toBe("7/16");
|
||||
});
|
||||
|
||||
it("черновик без этажа читается, а не отбрасывается целиком", () => {
|
||||
// Старые черновики в sessionStorage поля не имеют — они обязаны
|
||||
// пережить выкатку, а не обнулить человеку всю форму.
|
||||
saveDraft({ address: "Ленина 5", rooms: "2" });
|
||||
const d = takeDraft();
|
||||
expect(d?.address).toBe("Ленина 5");
|
||||
expect(d?.floor).toBeUndefined();
|
||||
});
|
||||
|
||||
it("этаж не строка — поле выпадает, остальной черновик цел", () => {
|
||||
window.sessionStorage.setItem(
|
||||
"mera:estimate-draft",
|
||||
JSON.stringify({ address: "Ленина 5", floor: 7 }),
|
||||
);
|
||||
const d = takeDraft();
|
||||
expect(d?.address).toBe("Ленина 5");
|
||||
expect(d?.floor).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* Проводка этажа ЧЕРЕЗ САМУ ФОРМУ, а не через хранилище черновика.
|
||||
*
|
||||
* Первая попытка защитить эту правку была сторожем, который не сторожит:
|
||||
* тест звал `saveDraft({ floor })` напрямую, поэтому проверял round-trip
|
||||
* хранилища и оставался ЗЕЛЁНЫМ, когда из `FreeCheckCard` убирали передачу
|
||||
* этажа — то есть ровно при возврате чинимого дефекта. Здесь форма
|
||||
* заполняется и отправляется целиком, и утверждение проверяется на том пути,
|
||||
* которым идёт человек.
|
||||
*/
|
||||
import { fireEvent, render, screen } from "@testing-library/react";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { FreeCheckCard } from "../_components/v3/FreeCheckCard";
|
||||
import { takeDraft } from "../estimate-draft";
|
||||
|
||||
/**
|
||||
* Отправка формы делает `window.location.assign`. В jsdom переход не
|
||||
* реализован и печатает «Not implemented: navigation» — глушим, чтобы шум не
|
||||
* читался как отказ теста.
|
||||
*/
|
||||
beforeEach(() => {
|
||||
window.sessionStorage.clear();
|
||||
Object.defineProperty(window, "location", {
|
||||
configurable: true,
|
||||
value: { ...window.location, assign: vi.fn() },
|
||||
});
|
||||
});
|
||||
|
||||
function fill(labelPart: string, value: string) {
|
||||
const field = screen.getByLabelText(new RegExp(labelPart, "i"));
|
||||
fireEvent.change(field, { target: { value } });
|
||||
}
|
||||
|
||||
describe("FreeCheckCard: обязательные поля доезжают до следующего шага", () => {
|
||||
it("этаж, набранный в форме, оказывается в черновике", () => {
|
||||
render(<FreeCheckCard />);
|
||||
|
||||
// Подписи ровно те, что видит человек: «Город», «Адрес», «М²»,
|
||||
// «Комнат», «Этаж». Искать по ним, а не по своим догадкам о названиях, —
|
||||
// иначе тест разъедется с формой молча.
|
||||
const city = screen.getByLabelText(/город/i);
|
||||
fireEvent.change(city, { target: { value: "Екатеринбург" } });
|
||||
|
||||
fill("адрес", "Екатеринбург, Ленина 5");
|
||||
fill("М²", "54");
|
||||
fill("комнат", "2");
|
||||
fill("этаж", "7/16");
|
||||
|
||||
// Состояние — группа кнопок с aria-pressed, а не радиогруппа.
|
||||
const conditions = screen.getByRole("group", { name: /состояние/i });
|
||||
fireEvent.click(conditions.querySelectorAll("button")[0]);
|
||||
|
||||
fireEvent.click(screen.getByRole("button", { name: /мо[её]му дому/i }));
|
||||
|
||||
const draft = takeDraft();
|
||||
expect(draft, "форма не сохранила черновик — проверь валидацию").not.toBeNull();
|
||||
expect(draft?.floor).toBe("7/16");
|
||||
});
|
||||
});
|
||||
|
|
@ -202,14 +202,17 @@ export function FreeCheckCard() {
|
|||
// Черновик кладём в sessionStorage, а НЕ в query-параметры: адрес в URL
|
||||
// попал бы в access-логи Caddy рядом с IP посетителя (см. estimate-draft.ts).
|
||||
// Этаж не передаём — его не использует ни проба покрытия, ни экран
|
||||
// /estimate, и повторно набирать одно короткое поле дешевле, чем хранить
|
||||
// лишнее. Состояние передаём: его спросили обязательным полем здесь.
|
||||
// Всё, что человек ввёл обязательным полем, доезжает до следующего шага.
|
||||
// Раньше этаж здесь терялся под доводом «повторно набрать дешевле, чем
|
||||
// хранить лишнее» — довод был неверен: поля этажа на целевой странице нет,
|
||||
// переспросить негде, и обязательное поле работало чистой помехой.
|
||||
saveDraft({
|
||||
address: trimmedAddress,
|
||||
rooms: trimmedRooms,
|
||||
area: trimmedArea,
|
||||
city: resolvedCity,
|
||||
condition,
|
||||
floor: trimmedFloor,
|
||||
});
|
||||
window.location.assign(PUBLIC_ROUTES.estimate);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,17 @@ export interface EstimateDraft {
|
|||
* он ждёт платного шага; хранится ровно один переход, как и всё остальное.
|
||||
*/
|
||||
condition?: string;
|
||||
/**
|
||||
* Этаж, как его набрал человек («7/16» или «7»). Строкой — по тому же
|
||||
* правилу, что комнаты и площадь: это ровно введённое, без домыслов о числе.
|
||||
*
|
||||
* Хранится по той же причине, что и `condition`, и она здесь ЖЁСТЧЕ. Раньше
|
||||
* этаж не сохраняли, объясняя это тем, что «повторно набрать одно короткое
|
||||
* поле дешевле, чем хранить лишнее». Утверждение было неверным: набирать
|
||||
* негде — на целевой странице поля этажа нет вовсе. То есть лендинг требовал
|
||||
* этаж обязательным полем, выбрасывал его и больше не спрашивал.
|
||||
*/
|
||||
floor?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -69,6 +80,7 @@ export function takeDraft(): EstimateDraft | null {
|
|||
city: typeof value.city === "string" ? value.city : undefined,
|
||||
condition:
|
||||
typeof value.condition === "string" ? value.condition : undefined,
|
||||
floor: typeof value.floor === "string" ? value.floor : undefined,
|
||||
};
|
||||
} catch {
|
||||
return null;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue