From 6e02104bc5a0f245c2e6dadbe4a30e110547dfac Mon Sep 17 00:00:00 2001 From: bot-backend Date: Wed, 19 Aug 2026 20:47:49 +0500 Subject: [PATCH] =?UTF-8?q?fix(ptica):=20=C2=AB=D0=A0=D0=B8=D1=81=D0=BA:?= =?UTF-8?q?=20=D0=9D=D0=B8=D0=B7=D0=BA=D0=B8=D0=B9=C2=BB=20=D0=B1=D0=BE?= =?UTF-8?q?=D0=BB=D1=8C=D1=88=D0=B5=20=D0=BD=D0=B5=20=D0=B2=D1=8B=D0=B3?= =?UTF-8?q?=D0=BB=D1=8F=D0=B4=D0=B8=D1=82=20=D0=B2=D0=B5=D1=80=D0=B4=D0=B8?= =?UTF-8?q?=D0=BA=D1=82=D0=BE=D0=BC=20(#2934)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit adaptRiskGauge считает риск как `100 - buildability` — это обратная застраиваемость, к природным опасностям (затопление, оползни, карст) отношения не имеющая. Сама функция помечена isReal: false и в докстринге названа PLACEHOLDER «until the real risk model is wired». В интерфейсе это выглядело как вердикт: значение «Низкий» красилось зелёным (riskTone: "good"), а пометка «предв.» лежала ТОЛЬКО в атрибуте title — на экране её не видно, на печати тем более. Читалось это как «рисков нет», при том что источники природного риска пусты: cad_risk_zones 0 строк, слои risk_* НСПД 0 объектов на 669 дампов (замер 19.08). Две правки, обе согласованы с соглашением этого же файла: 1. riskTone → "none". Ровно так уже приглушён potentialTone: пока значение не настоящее, цвета-вердикта у него нет. 2. Подпись «предв.» рендерится видимым текстом через styles.fieldCaption — тот же приём, что у score.caption рядом. Тесты двусторонние: * адаптер — на origin/main `expected 'good' to be 'none'`; * рендер — на origin/main `Unable to find an element with the text: предв.`. Оба несут предусловие: участок с gate_verdict, иначе застраиваемость null, тон был бы "none" по другой причине и проверка прошла бы вхолостую. tsc --noEmit: rc=0 vitest run: 35 файлов, 275 тестов, rc=0 (перепрогнано после prettier) Refs #2934 --- .../site-finder/ptica/InvestScoreBlock.tsx | 11 ++-- .../__tests__/InvestScoreBlock.risk.test.tsx | 50 +++++++++++++++++++ .../__tests__/ptica-adapt.honesty.test.ts | 43 ++++++++++++++++ .../site-finder/ptica/ptica-adapt.ts | 13 +++-- 4 files changed, 110 insertions(+), 7 deletions(-) create mode 100644 frontend/src/components/site-finder/ptica/__tests__/InvestScoreBlock.risk.test.tsx diff --git a/frontend/src/components/site-finder/ptica/InvestScoreBlock.tsx b/frontend/src/components/site-finder/ptica/InvestScoreBlock.tsx index 5824e9ff..45c0e6b7 100644 --- a/frontend/src/components/site-finder/ptica/InvestScoreBlock.tsx +++ b/frontend/src/components/site-finder/ptica/InvestScoreBlock.tsx @@ -80,11 +80,14 @@ export function InvestScoreBlock({
Риск - + {inv.risk.value} + {/* #2934: «предв.» было только в атрибуте title — на экране его не + видно, а на печати и подавно. Пометка о том, что оценка + предварительная, должна читаться там же, где значение. */} + {inv.risk.caption && ( + {inv.risk.caption} + )}
diff --git a/frontend/src/components/site-finder/ptica/__tests__/InvestScoreBlock.risk.test.tsx b/frontend/src/components/site-finder/ptica/__tests__/InvestScoreBlock.risk.test.tsx new file mode 100644 index 00000000..d7c87468 --- /dev/null +++ b/frontend/src/components/site-finder/ptica/__tests__/InvestScoreBlock.risk.test.tsx @@ -0,0 +1,50 @@ +/** + * #2934: пометка «предв.» у строки «Риск» должна читаться на экране. + * + * Значение берётся из adaptRiskGauge = `100 - buildability`, то есть обратной + * застраиваемости. К природным опасностям (затопление, оползни, карст) оно + * отношения не имеет, а источники природного риска пусты: cad_risk_zones 0 строк, + * слои risk_* НСПД 0 объектов на 669 дампов (замер 19.08.2026). + * + * До правки «предв.» лежало ТОЛЬКО в атрибуте title — на экране невидимо, при + * печати тем более, а значение при этом красилось зелёным. + */ +import { render, screen } from "@testing-library/react"; +import { describe, expect, it } from "vitest"; + +import { InvestScoreBlock } from "../InvestScoreBlock"; +import analyzeFixture from "@/lib/mocks/parcel-analyze.json"; +import type { ParcelAnalysis } from "@/types/site-finder"; + +/** Участок с gate_verdict — иначе застраиваемость null и строка «Риск» пуста. */ +function analysisWithGate(): ParcelAnalysis { + return { + ...(analyzeFixture as unknown as ParcelAnalysis), + gate_verdict: { + can_build_mkd: true, + verdict_label: "Можно", + blockers: [], + warnings: [], + checks_performed: [], + source: "nspd_dump", + }, + } as unknown as ParcelAnalysis; +} + +describe("InvestScoreBlock — строка «Риск» (#2934)", () => { + it("показывает пометку «предв.» видимым текстом", () => { + render( + {}} + />, + ); + + // Предусловие: строка «Риск» вообще отрисована со значением. + expect(screen.getByText("Риск")).toBeInTheDocument(); + expect(screen.getByText("Низкий")).toBeInTheDocument(); + + // На origin/main «предв.» живёт только в атрибуте title — getByText не найдёт. + expect(screen.getByText("предв.")).toBeInTheDocument(); + }); +}); diff --git a/frontend/src/components/site-finder/ptica/__tests__/ptica-adapt.honesty.test.ts b/frontend/src/components/site-finder/ptica/__tests__/ptica-adapt.honesty.test.ts index c6c7a3cd..618f3f11 100644 --- a/frontend/src/components/site-finder/ptica/__tests__/ptica-adapt.honesty.test.ts +++ b/frontend/src/components/site-finder/ptica/__tests__/ptica-adapt.honesty.test.ts @@ -17,6 +17,8 @@ import { describe, expect, it } from "vitest"; import { adaptBuySignalGauge, adaptEconomyCard, + adaptInvestScore, + adaptRiskGauge, adaptFinanceDrawer, adaptInvestmentClearance, adaptLegalDrawer, @@ -352,3 +354,44 @@ describe("ptica-adapt gate (#1892 ЗОУИТ area-gate)", () => { expect(v.tone).toBe("good"); }); }); + +describe("ptica-adapt risk placeholder (#2934)", () => { + /** Участок, у которого застраиваемость СЧИТАЕТСЯ: без gate_verdict она null, + * и тон был бы "none" просто из-за отсутствия данных — проверка прошла бы + * вхолостую и на неисправленном коде тоже. */ + function analysisWithGate(): ParcelAnalysis { + return { + ...analysis, + gate_verdict: { + can_build_mkd: true, + verdict_label: "Можно", + blockers: [], + warnings: [], + checks_performed: [], + source: "nspd_dump", + }, + } as unknown as ParcelAnalysis; + } + + it("«Риск» не окрашивается как вердикт, пока значение не настоящее", () => { + const a = analysisWithGate(); + const gauge = adaptRiskGauge(a); + // Предусловие: значение реально посчитано, иначе тон был бы "none" по другой + // причине и проверка ничего бы не доказывала. + expect(gauge.value).not.toBeNull(); + expect(gauge.tone).toBe("good"); // именно тот случай, что красился зелёным + expect(gauge.isReal).toBe(false); + + expect(adaptInvestScore(a).riskTone).toBe("none"); + }); + + it("подпись «предв.» доезжает до поля, а не только до тултипа", () => { + const inv = adaptInvestScore(analysisWithGate()); + expect(inv.risk.caption).toBe("предв."); + expect(inv.risk.isReal).toBe(false); + }); + + it("контроль: «Потенциал» приглушён как и раньше", () => { + expect(adaptInvestScore(analysis).potentialTone).toBe("none"); + }); +}); diff --git a/frontend/src/components/site-finder/ptica/ptica-adapt.ts b/frontend/src/components/site-finder/ptica/ptica-adapt.ts index c8ac2df0..615dd149 100644 --- a/frontend/src/components/site-finder/ptica/ptica-adapt.ts +++ b/frontend/src/components/site-finder/ptica/ptica-adapt.ts @@ -353,9 +353,16 @@ export function adaptInvestScore( risk.value != null ? { value: risk.label, isReal: false, caption: "предв." } : placeholder("предв."); - // Hero «Риск» value color (prototype .v.warn): green when low, yellow otherwise. - const riskTone: PticaValueTone = - risk.value == null ? "none" : risk.tone === "good" ? "good" : "warn"; + // #2934: плейсхолдер не красим как вердикт. `adaptRiskGauge` считает риск как + // `100 - buildability`, то есть обратную застраиваемость — к природным опасностям + // (затопление, оползни, карст) он отношения не имеет, и сам помечен + // `isReal: false`. Зелёное «Низкий» читалось как «рисков нет», хотя источники + // природного риска пусты: cad_risk_zones 0 строк, слои risk_* НСПД 0 объектов на + // 669 дампов (замер 19.08). + // + // Приглушаем так же, как уже приглушён `potentialTone` — соглашение в этом же + // файле: пока значение не настоящее, цвета-вердикта у него нет. + const riskTone: PticaValueTone = "none"; const overall = report?.scoring?.overall ?? report?.exec_summary.key_numbers.overall_score; -- 2.45.3