feat(sector-radar): align dual ranking radar interactions
This commit is contained in:
@@ -115,6 +115,18 @@ const rankingsResponse: RadarRankingsResponse = {
|
||||
],
|
||||
}
|
||||
|
||||
const bottomRankingsResponse: RadarRankingsResponse = {
|
||||
...rankingsResponse,
|
||||
side: "bottom",
|
||||
rows: rankingsResponse.rows.map((row, index) => ({
|
||||
...row,
|
||||
sector_code: `BOTTOM-${index + 1}`,
|
||||
sector_name: index === 0 ? "新能源" : "医药商业",
|
||||
metric_value: index === 0 ? -8.4 : null,
|
||||
rank_position: index + 1,
|
||||
})),
|
||||
}
|
||||
|
||||
function rankingQueryResult(overrides: Record<string, unknown> = {}) {
|
||||
return {
|
||||
data: { pageParams: [1], pages: [rankingsResponse] },
|
||||
@@ -161,10 +173,10 @@ describe("SectorRadarPage", () => {
|
||||
it("renders ranking rows without the description card or pagination", () => {
|
||||
render(<SectorRadarPage />)
|
||||
|
||||
expect(screen.getByText("12.5 亿元")).toBeInTheDocument()
|
||||
expect(screen.getByText("机器人")).toBeInTheDocument()
|
||||
expect(screen.getByText("样本有限")).toBeInTheDocument()
|
||||
expect(screen.getByText("95%")).toBeInTheDocument()
|
||||
expect(screen.getAllByText("+12.5 亿元")[0]).toBeInTheDocument()
|
||||
expect(screen.getAllByText("机器人")[0]).toBeInTheDocument()
|
||||
expect(screen.getAllByText("样本有限")[0]).toBeInTheDocument()
|
||||
expect(screen.getAllByText("95%")[0]).toBeInTheDocument()
|
||||
expect(screen.queryByText("收盘后板块资金排名")).not.toBeInTheDocument()
|
||||
expect(
|
||||
screen.queryByText("知行独立实现,非 OneChartLab 原站公式。"),
|
||||
@@ -197,6 +209,38 @@ describe("SectorRadarPage", () => {
|
||||
})
|
||||
})
|
||||
|
||||
it("renders accessible tabs, writes tab state to the URL, and queries both sides", () => {
|
||||
render(<SectorRadarPage />)
|
||||
|
||||
expect(
|
||||
screen.getByRole("tablist", { name: "板块类型" }),
|
||||
).toBeInTheDocument()
|
||||
expect(
|
||||
screen.getByRole("tablist", { name: "指标视角" }),
|
||||
).toBeInTheDocument()
|
||||
expect(screen.getByRole("tab", { name: "单日净额" })).toHaveAttribute(
|
||||
"aria-selected",
|
||||
"true",
|
||||
)
|
||||
expect(useSectorRadarRankings).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
expect.objectContaining({ side: "top", view: "amount" }),
|
||||
)
|
||||
expect(useSectorRadarRankings).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
expect.objectContaining({ side: "bottom", view: "amount" }),
|
||||
)
|
||||
|
||||
fireEvent.click(screen.getByRole("tab", { name: "行业" }))
|
||||
const searchCall = navigate.mock.calls[0]?.[0].search as (
|
||||
previous: SectorRadarRouteSearch,
|
||||
) => SectorRadarRouteSearch
|
||||
expect(searchCall({ ...routeSearch, page: 4 })).toMatchObject({
|
||||
page: 1,
|
||||
sectorType: "industry",
|
||||
})
|
||||
})
|
||||
|
||||
it("loads the next page once when repeated scroll events reach the bottom", () => {
|
||||
fetchNextPage.mockReturnValue(new Promise(() => undefined))
|
||||
useSectorRadarRankings.mockReturnValue(
|
||||
@@ -230,7 +274,7 @@ describe("SectorRadarPage", () => {
|
||||
render(<SectorRadarPage />)
|
||||
|
||||
expect(screen.getByRole("status")).toHaveTextContent("正在加载更多板块")
|
||||
expect(screen.getByText("机器人")).toBeInTheDocument()
|
||||
expect(screen.getAllByText("机器人")[0]).toBeInTheDocument()
|
||||
expect(screen.queryByText("雷达数据正在刷新")).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
@@ -257,9 +301,9 @@ describe("SectorRadarPage", () => {
|
||||
|
||||
render(<SectorRadarPage />)
|
||||
|
||||
expect(screen.getByText("机器人")).toBeInTheDocument()
|
||||
expect(screen.getByText("低空经济")).toBeInTheDocument()
|
||||
expect(screen.getByText("商业航天")).toBeInTheDocument()
|
||||
expect(screen.getAllByText("机器人")[0]).toBeInTheDocument()
|
||||
expect(screen.getAllByText("低空经济")[0]).toBeInTheDocument()
|
||||
expect(screen.getAllByText("商业航天")[0]).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("keeps loaded rows and offers retry when loading more fails", () => {
|
||||
@@ -273,15 +317,44 @@ describe("SectorRadarPage", () => {
|
||||
|
||||
render(<SectorRadarPage />)
|
||||
|
||||
expect(screen.getByText("机器人")).toBeInTheDocument()
|
||||
expect(screen.getAllByText("机器人")[0]).toBeInTheDocument()
|
||||
expect(screen.getByRole("alert")).toHaveTextContent(
|
||||
"加载更多板块失败,已加载结果仍保留",
|
||||
"强榜加载更多失败,弱榜加载更多失败,已加载结果仍保留",
|
||||
)
|
||||
expect(screen.queryByText("雷达数据刷新失败")).not.toBeInTheDocument()
|
||||
fireEvent.click(screen.getByRole("button", { name: "重试加载更多" }))
|
||||
fireEvent.click(screen.getByRole("button", { name: "重试强榜" }))
|
||||
expect(fetchNextPage).toHaveBeenCalledOnce()
|
||||
})
|
||||
|
||||
it("keeps the healthy side visible when only the other side fails to load more", () => {
|
||||
const topFetchNextPage = vi.fn().mockResolvedValue(undefined)
|
||||
const bottomFetchNextPage = vi.fn().mockResolvedValue(undefined)
|
||||
useSectorRadarRankings.mockImplementation((query: { side: string }) =>
|
||||
rankingQueryResult({
|
||||
data: {
|
||||
pageParams: [1],
|
||||
pages: [
|
||||
query.side === "bottom" ? bottomRankingsResponse : rankingsResponse,
|
||||
],
|
||||
},
|
||||
fetchNextPage:
|
||||
query.side === "bottom" ? bottomFetchNextPage : topFetchNextPage,
|
||||
isFetchNextPageError: query.side === "bottom",
|
||||
hasNextPage: true,
|
||||
}),
|
||||
)
|
||||
|
||||
render(<SectorRadarPage />)
|
||||
|
||||
expect(screen.getAllByText("机器人")[0]).toBeInTheDocument()
|
||||
expect(screen.getByText("新能源")).toBeInTheDocument()
|
||||
expect(screen.getByRole("alert")).toHaveTextContent("弱榜加载更多失败")
|
||||
expect(screen.getByRole("button", { name: "重试弱榜" })).toBeInTheDocument()
|
||||
fireEvent.click(screen.getByRole("button", { name: "重试弱榜" }))
|
||||
expect(bottomFetchNextPage).toHaveBeenCalledOnce()
|
||||
expect(topFetchNextPage).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("shows rank changes without inventing missing history", () => {
|
||||
routeSearch = { ...routeSearch, view: "rank_change", rankChangeDays: 5 }
|
||||
useSectorRadarRankings.mockReturnValue(
|
||||
@@ -307,10 +380,10 @@ describe("SectorRadarPage", () => {
|
||||
render(<SectorRadarPage />)
|
||||
|
||||
expect(
|
||||
screen.getByRole("columnheader", { name: "5 日排名变化" }),
|
||||
screen.getByRole("columnheader", { name: "排名上升" }),
|
||||
).toBeInTheDocument()
|
||||
expect(screen.getByText("+3")).toBeInTheDocument()
|
||||
expect(screen.getByText("暂无可比历史")).toBeInTheDocument()
|
||||
expect(screen.getAllByText("+3 名")[0]).toBeInTheDocument()
|
||||
expect(screen.getAllByText("暂无可比历史")[0]).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("warns when a partial attempt has not replaced last-good", () => {
|
||||
@@ -350,7 +423,7 @@ describe("SectorRadarPage", () => {
|
||||
expect(screen.getByRole("status")).toHaveTextContent(
|
||||
"当前继续展示上一次已读取的有效结果",
|
||||
)
|
||||
expect(screen.getByText("机器人")).toBeInTheDocument()
|
||||
expect(screen.getAllByText("机器人")[0]).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("keeps stale data visible when a background refresh fails", () => {
|
||||
@@ -361,10 +434,38 @@ describe("SectorRadarPage", () => {
|
||||
render(<SectorRadarPage />)
|
||||
|
||||
expect(screen.getByRole("status")).toHaveTextContent("雷达数据刷新失败")
|
||||
expect(screen.getByText("机器人")).toBeInTheDocument()
|
||||
expect(screen.getAllByText("机器人")[0]).toBeInTheDocument()
|
||||
expect(screen.queryByText("板块资金雷达暂时不可用")).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("shows a fatal error when one side has no data and the other has no response", () => {
|
||||
useSectorRadarRankings.mockImplementation((query: { side: string }) =>
|
||||
rankingQueryResult(
|
||||
query.side === "top"
|
||||
? {
|
||||
data: {
|
||||
pageParams: [1],
|
||||
pages: [
|
||||
{
|
||||
...rankingsResponse,
|
||||
side: "top",
|
||||
status: "no_data",
|
||||
publication: null,
|
||||
rows: [],
|
||||
total: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
: { data: undefined, isError: true },
|
||||
),
|
||||
)
|
||||
|
||||
render(<SectorRadarPage />)
|
||||
|
||||
expect(screen.getByText("板块资金雷达暂时不可用")).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("renders loading, error with retry, and no-data states", () => {
|
||||
useSectorRadarDates.mockReturnValue({
|
||||
data: undefined,
|
||||
@@ -408,7 +509,7 @@ describe("SectorRadarPage", () => {
|
||||
rerender(<SectorRadarPage />)
|
||||
fireEvent.click(screen.getByRole("button", { name: "重试" }))
|
||||
expect(refetchDates).toHaveBeenCalledOnce()
|
||||
expect(refetchRankings).toHaveBeenCalledOnce()
|
||||
expect(refetchRankings).toHaveBeenCalledTimes(2)
|
||||
|
||||
useSectorRadarDates.mockReturnValue({
|
||||
data: {
|
||||
|
||||
Reference in New Issue
Block a user