diff --git a/web/src/components/AiDisclosure.jsx b/web/src/components/AiDisclosure.jsx index 8c52c6456..b97bad109 100644 --- a/web/src/components/AiDisclosure.jsx +++ b/web/src/components/AiDisclosure.jsx @@ -114,18 +114,6 @@ export function AiDecisionPath({ decision }) { : "판정 원장이 늦게 갱신되어 현재 배당의 시장 최유력 후보를 안전 규칙으로 다시 계산했습니다. 구조 AI와 레거시 추천값은 반영하지 않았습니다."}
)} - {decision.action === "withhold" && !!decision.withholdReasons?.length && ( -{gateText[visibleGate]}
)} diff --git a/web/src/components/PredictionPanel.jsx b/web/src/components/PredictionPanel.jsx index 13ff8fece..09ad2be42 100644 --- a/web/src/components/PredictionPanel.jsx +++ b/web/src/components/PredictionPanel.jsx @@ -86,7 +86,7 @@ export default function PredictionPanel({ analysis, scoreForecast }) { const summary = signalSummary?.narrative || ( decision?.action === "market_reference" ? "추천 방향과 함께 최근 경기력·선수 정보·반대 신호를 비교해 확인하세요." - : "아래 위험이 해소되지 않아 이 픽은 추천하지 않습니다." + : "경기 자료와 현재 판정 상태를 함께 확인하세요." ); return ({commentary}
diff --git a/web/src/components/pick-avoidance-display.test.mjs b/web/src/components/pick-avoidance-display.test.mjs new file mode 100644 index 000000000..5bd10e918 --- /dev/null +++ b/web/src/components/pick-avoidance-display.test.mjs @@ -0,0 +1,55 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { fileURLToPath } from "node:url"; +import { createElement } from "react"; +import { renderToStaticMarkup } from "react-dom/server"; +import { createServer } from "vite"; + +test("withhold explanation is absent from both panels without hiding other information", async () => { + const server = await createServer({ + configFile: false, + root: fileURLToPath(new URL("../../", import.meta.url)), + esbuild: { jsx: "automatic" }, + optimizeDeps: { noDiscovery: true, include: [] }, + server: { middlewareMode: true, watch: null }, + appType: "custom", + }); + try { + const { AiDecisionPath } = await server.ssrLoadModule("/src/components/AiDisclosure.jsx"); + const { default: PredictionPanel } = await server.ssrLoadModule("/src/components/PredictionPanel.jsx"); + const decision = { + action: "withhold", probability: { market: .6, final: .6, aiDeltaApplied: 0 }, + stages: [], evidence: [], + withholdReasons: [{ + title: "현재 안전조건을 만족하는 선택이 없음", + body: "배당은 있지만 마켓 유효성·가격 범위·동일 마켓 최유력 조건을 함께 통과한 선택이 없습니다. 어느 조건을 어겼는지 확정되기 전에는 가지 않습니다.", + }], + }; + const before = structuredClone(decision); + const path = renderToStaticMarkup(createElement(AiDecisionPath, { decision })); + const prediction = renderToStaticMarkup(createElement(PredictionPanel, { + analysis: { decision, reasons: ["최근 전적 — 3승 2패"], cautions: ["선발 변경 가능성"] }, + })); + for (const html of [path, prediction]) { + assert.doesNotMatch(html, /이 픽을 가지 말아야 하는 이유/); + assert.doesNotMatch(html, /현재 안전조건을 만족하는 선택이 없음|어느 조건을 어겼는지/); + } + assert.match(path, /최종 확률 계산 경로/); + assert.match(prediction, /최근 전적|3승 2패/); + assert.match(prediction, /반대 근거·변수/); + assert.match(prediction, /선발 변경 가능성/); + assert.doesNotMatch(prediction, /아래 위험/); + assert.deepEqual(decision, before); + + const closed = renderToStaticMarkup(createElement(AiDecisionPath, { + decision: { ...decision, action: "closed" }, + })); + assert.match(closed, /경기 시작을 확인/); + const stale = renderToStaticMarkup(createElement(AiDecisionPath, { + decision: { ...decision, action: "recalculating" }, + })); + assert.match(stale, /실시간 배당이 바뀌어/); + } finally { + await server.close(); + } +});