feat(tradein/v2): позиция квартиры на рынке — плашка на карточке рекомендованной цены (#2899) #3024

Merged
bot-backend merged 3 commits from feat/2899-market-percentile-ui into main 2026-08-21 12:34:08 +00:00
Showing only changes of commit 07655a2642 - Show all commits

View file

@ -0,0 +1,58 @@
// #2899 — позиция квартиры внутри когорты аналогов на карточке «РЕКОМЕНДОВАННАЯ
// ЦЕНА». Бэкенд считает market_percentile (1..99, null при когорте < 15); маппер
// обязан превратить его в лейбл по терцилям + подпись с числом и размером когорты,
// а при null — не рисовать ничего (не «В рынке» по умолчанию). На main карточка 1
// плашки не имеет вовсе — первый тест красный по значению (delta === undefined).
import { describe, expect, it } from "vitest";
import { mapResultPanel } from "../mappers";
import { FIXTURE_ESTIMATE } from "@/app/ui-preview/estimate/fixture";
import type { AggregatedEstimate } from "@/types/trade-in";
function est(over: Partial<AggregatedEstimate>): AggregatedEstimate {
return { ...FIXTURE_ESTIMATE, n_analogs: 16, ...over };
}
function card1(e: AggregatedEstimate) {
return mapResultPanel(e).cards[0];
}
describe("#2899 market_percentile → плашка карточки 1", () => {
it("69-й перцентиль среди 16 аналогов → «Верх рынка» + честная подпись", () => {
const c = card1(est({ market_percentile: 69 }));
expect(c.delta).toBe("Верх рынка");
expect(c.deltaLabel).toBe("69-й перцентиль среди 16 аналогов");
});
it("терцили: ≤33 — низ, 34..66 — в рынке, ≥67 — верх", () => {
expect(card1(est({ market_percentile: 20 })).delta).toBe("Низ рынка");
expect(card1(est({ market_percentile: 33 })).delta).toBe("Низ рынка");
expect(card1(est({ market_percentile: 34 })).delta).toBe("В рынке");
expect(card1(est({ market_percentile: 50 })).delta).toBe("В рынке");
expect(card1(est({ market_percentile: 66 })).delta).toBe("В рынке");
expect(card1(est({ market_percentile: 67 })).delta).toBe("Верх рынка");
});
it("null / отсутствие поля → плашки нет; delta карточки 2 не тронут", () => {
const withNull = mapResultPanel(est({ market_percentile: null })).cards;
const legacy = mapResultPanel(est({})).cards;
expect(withNull[0].delta).toBeUndefined();
expect(withNull[0].deltaLabel).toBeUndefined();
expect(legacy[0].delta).toBeUndefined();
// контроль: плашка «к цене объявления» карточки 2 живёт своей жизнью
expect(withNull[1].delta).toBe(
mapResultPanel(FIXTURE_ESTIMATE).cards[1].delta,
);
expect(withNull[1].deltaLabel).toBe("к цене объявления");
});
it("размер когорты в подписи берётся из n_analogs, склонение по числу", () => {
expect(
card1(est({ market_percentile: 50, n_analogs: 21 })).deltaLabel,
).toBe("50-й перцентиль среди 21 аналога");
expect(
card1(est({ market_percentile: 50, n_analogs: 23 })).deltaLabel,
).toBe("50-й перцентиль среди 23 аналогов");
});
});