chore(tradein/frontend): удалить остаточный слой фикстур (#2755) #2785

Merged
bot-backend merged 2 commits from chore/2755-fixture-layer into main 2026-08-07 10:00:49 +00:00
Showing only changes of commit fd1affca8b - Show all commits

View file

@ -0,0 +1,94 @@
// #2755 — колонка ссылки в таблице аналогов «05 РЫНОК».
//
// Здесь до этого жила третья ветка: когда `url` приходил `undefined`, рисовался
// НЕкликабельный <span>, стилизованный под кнопку-ссылку «Росреестр ↗». Ветка
// была недостижима (mapSources всегда ставит `string | null`), но выглядела как
// рабочий элемент интерфейса. Ветку убрали, поле сделали обязательным — этот
// тест держит обе половины: ссылка есть только там, где есть URL, а строка без
// URL получает прочерк, а не кнопку-обманку.
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import SourcesView from "../SourcesView";
import { mapSources } from "../mappers";
import { FIXTURE_ESTIMATE } from "@/app/ui-preview/estimate/fixture";
import type { AggregatedEstimate, AnalogLot } from "@/types/trade-in";
function lot(over: Partial<AnalogLot>): AnalogLot {
return {
address: "ул. Репина, 73",
area_m2: 54,
rooms: 2,
floor: 7,
total_floors: 16,
price_rub: 9_700_000,
price_per_m2: 179_629,
listing_date: "2026-05-12",
days_on_market: 18,
photo_url: null,
source: "avito",
source_url: null,
distance_m: 120,
tier: null,
lat: 56.8401,
lon: 60.5702,
...over,
};
}
// Оффлайн-фикстура оценки репозитория с подменёнными аналогами: остальные поля
// (sources_used, коридоры, KPI) настоящие, меняем ровно то, что проверяем.
function estimateWith(analogs: AnalogLot[]): AggregatedEstimate {
return {
...FIXTURE_ESTIMATE,
analogs,
n_analogs: analogs.length,
actual_deals: [],
};
}
describe("SourcesView — колонка ссылки на оригинал", () => {
it("есть URL — кликабельная ссылка на него", () => {
const estimate = estimateWith([
lot({
address: "ул. Репина, 73",
source_url: "https://www.avito.ru/ekaterinburg/kvartiry/1",
}),
]);
render(<SourcesView data={mapSources(estimate, null)} />);
const link = screen.getByRole("link", { name: /Источник/ });
expect(link).toHaveAttribute(
"href",
"https://www.avito.ru/ekaterinburg/kvartiry/1",
);
});
it("URL нет — прочерк, а не кнопка-обманка «Росреестр ↗»", () => {
const estimate = estimateWith([
lot({ address: "ул. Викулова, 33", source_url: null }),
]);
render(<SourcesView data={mapSources(estimate, null)} />);
expect(screen.queryByText(/Росреестр ↗/)).toBeNull();
expect(screen.queryByRole("link", { name: /Источник/ })).toBeNull();
// Прочерк — единственное содержимое ячейки ссылки в строке аналога.
const row = screen.getByText("ул. Викулова, 33").closest("div");
expect(row?.textContent).toContain("—");
});
it("javascript:-схема отбрасывается safeUrl — прочерк, а не ссылка", () => {
const estimate = estimateWith([
lot({ address: "ул. Кирова, 28", source_url: "javascript:alert(1)" }),
]);
render(<SourcesView data={mapSources(estimate, null)} />);
expect(screen.queryByRole("link", { name: /Источник/ })).toBeNull();
const row = screen.getByText("ул. Кирова, 28").closest("div");
expect(row?.textContent).toContain("—");
});
});