gendesign/frontend/src/components/site-finder/__tests__/BestLayoutsBlock.dataQuality.test.tsx
bot-backend abe76ca980
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI / changes (pull_request) Successful in 10s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Successful in 1m4s
CI / openapi-codegen-check (pull_request) Successful in 2m7s
fix(frontend): нейтральный fallback ещё в 4 enum-lookup-местах
Follow-up #2451 (эпик #2445, B2/B3): тот же класс — object-literal lookup по
значению, пришедшему из API, без runtime-guard. Значение вне закрытого union'а
(schema drift / частичный деплой) даёт `undefined`, и дальше по-разному плохо:

- `BestLayoutsBlock` (DataQualityCard) — обращение `.fg`/`.bg` к undefined
  РОНЯЕТ рендер всей карточки качества данных;
- `ForecastChart.confidenceByHorizon` — подпись уверенности для горизонта
  пропадает МОЛЧА, при том что прогноз для него есть;
- `compare/CompareTable` и `ptica/compare/PticaCompare` — рядом с процентом
  остаётся «73% · » и обрыв: число выглядит недосказанным, а не неизвестным.

Везде нейтральный fallback по образцу VelocityBlock/ParcelDrawer: серый + «—»,
намеренно НЕ семантический цвет — неизвестное качество не должно читаться ни
как хорошее, ни как плохое.

Фон бейджа взят из существующего `--border-soft`: список токенов закрыт
(ui-tokens.md — «НЕ выдумывать новые»), нейтральной заливки в нём нет.

`DataQualityCard`, `confidenceByHorizon` и `METRIC_ROWS` (ptica) экспортированы
ради тестов — тем же приёмом, что уже применён к `isDeficitDegenerate`.

Тесты: 12 новых в 4 файлах. Проверены на слом — снял все четыре fallback'а,
упало ровно 5 проверок out-of-union, остальные 7 (известные значения,
отсутствующие данные) остались зелёными.

`vitest run src/components/site-finder` — 145 passed, `tsc --noEmit` чисто.

Closes #2452
Refs #2445, #2451
2026-08-27 19:35:56 +03:00

63 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* #2452 (follow-up #2445-B2/B3) — `CONFIDENCE_STYLES[dq.confidence]` не имел
* fallback'а: значение вне закрытого union'а (schema drift / частичный деплой)
* давало `undefined`, и следующее обращение `.fg`/`.bg` роняло рендер всей
* карточки качества данных.
*
* Тесты фиксируют нейтральный fallback (тот же паттерн, что VelocityBlock и
* ParcelDrawer): неизвестное качество рисуется серым и «—», а не падает и не
* выдаёт себя за хорошее или плохое.
*/
import { render, screen } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { DataQualityCard } from "../BestLayoutsBlock";
import type { BestLayoutsResponse } from "@/types/best-layouts";
type DataQuality = BestLayoutsResponse["data_quality"];
function makeDq(overrides: Partial<DataQuality> = {}): DataQuality {
return {
objects_with_velocity_data: 18,
objects_total_in_radius: 25,
raw_objects_total: 31,
groups_matched_by_core: 12,
groups_with_supply_only: 3,
velocity_coverage_pct: 72,
confidence: "high",
...overrides,
};
}
describe("DataQualityCard — нейтральный fallback (#2452)", () => {
it("известное значение рисуется своей меткой", () => {
render(<DataQualityCard dq={makeDq({ confidence: "low" })} />);
expect(screen.getByText("Низкое")).toBeInTheDocument();
});
it("значение вне union'а не роняет рендер и даёт «—»", () => {
const drifted = makeDq({
// Имитация schema drift: backend прислал значение вне
// "high"|"medium"|"low". Приведение через unknown, не через any.
confidence: "unverified" as unknown as DataQuality["confidence"],
});
expect(() => render(<DataQualityCard dq={drifted} />)).not.toThrow();
expect(screen.getByText("—")).toBeInTheDocument();
// Покрытие продолжает рисоваться — падает только метка, не вся карточка.
expect(screen.getByText(/покрытие 72%/)).toBeInTheDocument();
});
it("fallback не выглядит семантически (не зелёный и не красный)", () => {
const drifted = makeDq({
confidence: "unverified" as unknown as DataQuality["confidence"],
});
render(<DataQualityCard dq={drifted} />);
const badge = screen.getByText("—");
const style = badge.getAttribute("style") ?? "";
expect(style).not.toContain("--success");
expect(style).not.toContain("--danger");
expect(style).not.toContain("--warn");
});
});