feat(web): 完善选股详情图表与案例图
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
import { fireEvent, render, screen } from "@testing-library/react"
|
||||
import { describe, expect, it } from "vitest"
|
||||
|
||||
import type { SelectionPatternScore } from "../api/selection.types"
|
||||
import { PatternCaseImage } from "./pattern-case-image"
|
||||
|
||||
function score(
|
||||
caseId = "case_004",
|
||||
caseName = "方正科技",
|
||||
): SelectionPatternScore {
|
||||
return {
|
||||
breakdown: {
|
||||
kdj_state: 91,
|
||||
price_shape: 92,
|
||||
trend_structure: 86,
|
||||
volume_pattern: 89,
|
||||
},
|
||||
case: {
|
||||
breakout_date: "2025-07-23",
|
||||
id: caseId,
|
||||
name: caseName,
|
||||
},
|
||||
reason: null,
|
||||
status: "matched",
|
||||
threshold: 60,
|
||||
value: 90.17,
|
||||
version: "zhixing_b1_pattern_fastdtw_v1",
|
||||
}
|
||||
}
|
||||
|
||||
describe("PatternCaseImage", () => {
|
||||
it("renders only the best case image and falls back after a load error", () => {
|
||||
const view = render(<PatternCaseImage score={score()} />)
|
||||
|
||||
const image = screen.getByRole("img", {
|
||||
name: "方正科技 B1 参考案例图",
|
||||
})
|
||||
expect(screen.getAllByRole("img")).toHaveLength(1)
|
||||
expect(image).toHaveAttribute(
|
||||
"src",
|
||||
"/patterns/b1-cases/case_004_600601_方正科技_2025-07-23.jpg",
|
||||
)
|
||||
|
||||
fireEvent.error(image)
|
||||
expect(screen.queryByRole("img")).not.toBeInTheDocument()
|
||||
expect(screen.getByText("案例图片暂不可用。")).toBeInTheDocument()
|
||||
|
||||
view.rerender(<PatternCaseImage score={score("case_001", "华纳药厂")} />)
|
||||
expect(
|
||||
screen.getByRole("img", { name: "华纳药厂 B1 参考案例图" }),
|
||||
).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("does not guess an asset for an unknown case id", () => {
|
||||
render(<PatternCaseImage score={score("case_unknown", "未知案例")} />)
|
||||
|
||||
expect(screen.queryByRole("img")).not.toBeInTheDocument()
|
||||
expect(screen.getByText("案例图片暂不可用。")).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("does not render an image for uncomputed or failed scores", () => {
|
||||
const failed: SelectionPatternScore = {
|
||||
breakdown: null,
|
||||
case: null,
|
||||
reason: "scoring unavailable",
|
||||
status: "failed",
|
||||
threshold: null,
|
||||
value: null,
|
||||
version: null,
|
||||
}
|
||||
const view = render(<PatternCaseImage score={null} />)
|
||||
expect(screen.queryByRole("img")).not.toBeInTheDocument()
|
||||
|
||||
view.rerender(<PatternCaseImage score={failed} />)
|
||||
expect(screen.queryByRole("img")).not.toBeInTheDocument()
|
||||
expect(screen.queryByText("案例图片暂不可用。")).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user