diff --git a/tradein-mvp/frontend/src/app/mera-public/__tests__/estimate-draft-survives-estimate.test.tsx b/tradein-mvp/frontend/src/app/mera-public/__tests__/estimate-draft-survives-estimate.test.tsx
index 0539e722..146f70ca 100644
--- a/tradein-mvp/frontend/src/app/mera-public/__tests__/estimate-draft-survives-estimate.test.tsx
+++ b/tradein-mvp/frontend/src/app/mera-public/__tests__/estimate-draft-survives-estimate.test.tsx
@@ -45,6 +45,9 @@ function submitLandingCard(): string {
// не из своих догадок о наборе вариантов.
const conditions = screen.getByRole("group", { name: /состояние/i });
const chosen = conditions.querySelectorAll("button")[0];
+ // Без этой проверки пустая группа роняет тест TypeError'ом на textContent —
+ // отказом, по которому не видно, что именно разъехалось с формой.
+ expect(chosen, "в группе «Состояние» не нашлось кнопок").toBeTruthy();
const label = chosen.textContent ?? "";
fireEvent.click(chosen);
@@ -56,17 +59,16 @@ function submitLandingCard(): string {
describe("Черновик: обязательные поля переживают переход на /estimate", () => {
it("этаж и состояние доступны после монтирования целевой страницы", () => {
const condition = submitLandingCard();
- expect(condition, "в группе «Состояние» не нашлось кнопок").not.toBe("");
// Тот самый шаг, который раньше уничтожал оба поля.
render();
- const extras = readDraftExtras();
+ // Читаем так же, как будет читать платный шаг: под тот адрес, о котором
+ // спрашивают. Чужому адресу хвост не отдаётся — проверено отдельно в
+ // estimate-draft.test.ts.
+ const extras = readDraftExtras("Екатеринбург, Ленина 5");
expect(extras, "после /estimate от черновика не осталось ничего").not.toBeNull();
expect(extras?.floor).toBe("7/16");
expect(extras?.condition).toBe(condition);
- // Хвост без адреса нельзя предъявлять платному шагу: он не отличит этаж
- // этой квартиры от этажа предыдущей в той же вкладке.
- expect(extras?.address).toBe("Екатеринбург, Ленина 5");
});
});
diff --git a/tradein-mvp/frontend/src/app/mera-public/__tests__/estimate-draft.test.ts b/tradein-mvp/frontend/src/app/mera-public/__tests__/estimate-draft.test.ts
index 8449c935..5e5044a2 100644
--- a/tradein-mvp/frontend/src/app/mera-public/__tests__/estimate-draft.test.ts
+++ b/tradein-mvp/frontend/src/app/mera-public/__tests__/estimate-draft.test.ts
@@ -1,6 +1,11 @@
import { beforeEach, describe, expect, it } from "vitest";
-import { normalizeDraftRooms, saveDraft, takeDraft } from "../estimate-draft";
+import {
+ normalizeDraftRooms,
+ readDraftExtras,
+ saveDraft,
+ takeDraft,
+} from "../estimate-draft";
/**
* Черновик, который лэндинг передаёт на экран проверки.
@@ -107,6 +112,24 @@ describe("этаж переживает переход на целевую ст
expect(d?.floor).toBeUndefined();
});
+ it("черновик без этажа и состояния убирает прошлый хвост", () => {
+ // Иначе этаж от предыдущей квартиры пережил бы свой черновик и лежал бы,
+ // дожидаясь потребителя.
+ saveDraft({ address: "Ленина 5", floor: "7/16", condition: "Хорошее" });
+ takeDraft();
+ saveDraft({ address: "Малышева 51", rooms: "2" });
+ takeDraft();
+ expect(readDraftExtras("Ленина 5")).toBeNull();
+ expect(readDraftExtras("Малышева 51")).toBeNull();
+ });
+
+ it("хвост не отдаётся другому адресу", () => {
+ saveDraft({ address: "Ленина 5", floor: "7/16" });
+ takeDraft();
+ expect(readDraftExtras("Малышева 51")).toBeNull();
+ expect(readDraftExtras("Ленина 5")?.floor).toBe("7/16");
+ });
+
it("этаж не строка — поле выпадает, остальной черновик цел", () => {
window.sessionStorage.setItem(
"mera:estimate-draft",
diff --git a/tradein-mvp/frontend/src/app/mera-public/estimate-draft.ts b/tradein-mvp/frontend/src/app/mera-public/estimate-draft.ts
index d36314ef..8063187d 100644
--- a/tradein-mvp/frontend/src/app/mera-public/estimate-draft.ts
+++ b/tradein-mvp/frontend/src/app/mera-public/estimate-draft.ts
@@ -54,11 +54,7 @@ export interface EstimateDraft {
/**
* Хвост черновика: то, что собрали на лэндинге, но на `/estimate` не читают.
- *
- * Адрес лежит здесь ВМЕСТЕ с полями, а не подразумевается: иначе этаж «7/16»
- * от квартиры на Ленина переживёт свой черновик и на платном шаге приклеится к
- * другому дому в той же вкладке. Потребитель обязан сверить адрес, а не верить
- * тому, что нашёл.
+ * Адрес лежит здесь вместе с полями — сверку делает `readDraftExtras`.
*/
export interface EstimateDraftExtras {
address: string;
@@ -137,18 +133,25 @@ function saveDraftExtras(draft: EstimateDraft): void {
* Читает, но НЕ забирает: платный шаг может открыться не с первого раза, а
* второе чтение того же хвоста ничего не портит. Вкладка закроется — исчезнет
* само.
+ *
+ * `expectedAddress` обязателен, а не «желателен»: `takeDraft` выходит раньше
+ * записи хвоста на пустом/битом черновике, поэтому хвост от ПРЕДЫДУЩЕЙ
+ * квартиры доживает до следующего захода в той же вкладке. Сверку нельзя
+ * оставлять на совесть потребителя — не тот адрес, значит ничего нет.
*/
-export function readDraftExtras(): EstimateDraftExtras | null {
+export function readDraftExtras(
+ expectedAddress: string,
+): EstimateDraftExtras | null {
try {
const raw = window.sessionStorage.getItem(EXTRAS_KEY);
if (!raw) return null;
const parsed: unknown = JSON.parse(raw);
if (typeof parsed !== "object" || parsed === null) return null;
const value = parsed as Record;
- if (typeof value.address !== "string") return null;
+ if (value.address !== expectedAddress) return null;
return {
- address: value.address,
+ address: expectedAddress,
condition:
typeof value.condition === "string" ? value.condition : undefined,
floor: typeof value.floor === "string" ? value.floor : undefined,