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();
|
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
|
// Черновик кладём в sessionStorage, а НЕ в query-параметры: адрес в URL
|
||||||
// попал бы в access-логи Caddy рядом с IP посетителя (см. estimate-draft.ts).
|
// попал бы в access-логи Caddy рядом с IP посетителя (см. estimate-draft.ts).
|
||||||
// Этаж не передаём — его не использует ни проба покрытия, ни экран
|
// Этаж не передаём — его не использует ни проба покрытия, ни экран
|
||||||
// /estimate, и повторно набирать одно короткое поле дешевле, чем хранить
|
// Всё, что человек ввёл обязательным полем, доезжает до следующего шага.
|
||||||
// лишнее. Состояние передаём: его спросили обязательным полем здесь.
|
// Раньше этаж здесь терялся под доводом «повторно набрать дешевле, чем
|
||||||
|
// хранить лишнее» — довод был неверен: поля этажа на целевой странице нет,
|
||||||
|
// переспросить негде, и обязательное поле работало чистой помехой.
|
||||||
saveDraft({
|
saveDraft({
|
||||||
address: trimmedAddress,
|
address: trimmedAddress,
|
||||||
rooms: trimmedRooms,
|
rooms: trimmedRooms,
|
||||||
area: trimmedArea,
|
area: trimmedArea,
|
||||||
city: resolvedCity,
|
city: resolvedCity,
|
||||||
condition,
|
condition,
|
||||||
|
floor: trimmedFloor,
|
||||||
});
|
});
|
||||||
window.location.assign(PUBLIC_ROUTES.estimate);
|
window.location.assign(PUBLIC_ROUTES.estimate);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,17 @@ export interface EstimateDraft {
|
||||||
* он ждёт платного шага; хранится ровно один переход, как и всё остальное.
|
* он ждёт платного шага; хранится ровно один переход, как и всё остальное.
|
||||||
*/
|
*/
|
||||||
condition?: string;
|
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,
|
city: typeof value.city === "string" ? value.city : undefined,
|
||||||
condition:
|
condition:
|
||||||
typeof value.condition === "string" ? value.condition : undefined,
|
typeof value.condition === "string" ? value.condition : undefined,
|
||||||
|
floor: typeof value.floor === "string" ? value.floor : undefined,
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue