feat(selection): 重构选股结果筛选与状态入口
This commit is contained in:
@@ -99,6 +99,7 @@ describe("SelectionResultsPage", () => {
|
||||
render(<SelectionResultsPage />)
|
||||
|
||||
expect(screen.getByText("执行完成")).toBeInTheDocument()
|
||||
expect(screen.queryByText("目标股票数")).not.toBeInTheDocument()
|
||||
const table = screen.getByRole("table")
|
||||
expect(table).toBeInTheDocument()
|
||||
expect(within(table).getByText("原始 B1")).toBeInTheDocument()
|
||||
@@ -130,11 +131,25 @@ describe("SelectionResultsPage", () => {
|
||||
const trigger = screen.getByRole("button", {
|
||||
name: "查看执行状态:部分成功,未完成评估 1 条",
|
||||
})
|
||||
expect(trigger).toHaveAttribute(
|
||||
"aria-controls",
|
||||
"selection-execution-status-drawer",
|
||||
)
|
||||
expect(trigger).toHaveAttribute("aria-expanded", "false")
|
||||
fireEvent.click(trigger)
|
||||
expect(trigger).toHaveAttribute("aria-expanded", "true")
|
||||
|
||||
const dialog = await screen.findByRole("dialog", {
|
||||
name: "知行 B1 执行状态",
|
||||
})
|
||||
const summary = within(dialog).getByRole("region", { name: "执行摘要" })
|
||||
expect(within(summary).getByText("目标股票数")).toBeInTheDocument()
|
||||
expect(within(summary).getByText("实际参与数")).toBeInTheDocument()
|
||||
expect(within(summary).getByText("命中股票数")).toBeInTheDocument()
|
||||
expect(within(summary).getByText("信号条数")).toBeInTheDocument()
|
||||
expect(within(summary).getByText("数据覆盖率")).toBeInTheDocument()
|
||||
expect(within(summary).getByText("100.00%")).toBeInTheDocument()
|
||||
expect(within(summary).getByText("部分成功")).toBeInTheDocument()
|
||||
const table = within(dialog).getByRole("table")
|
||||
expect(
|
||||
within(table).getByRole("columnheader", { name: "股票名称" }),
|
||||
@@ -153,6 +168,7 @@ describe("SelectionResultsPage", () => {
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole("dialog")).not.toBeInTheDocument()
|
||||
})
|
||||
expect(trigger).toHaveAttribute("aria-expanded", "false")
|
||||
expect(document.activeElement).toBe(trigger)
|
||||
})
|
||||
|
||||
@@ -175,6 +191,111 @@ describe("SelectionResultsPage", () => {
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByRole("dialog")).not.toBeInTheDocument()
|
||||
})
|
||||
expect(
|
||||
screen.getByRole("button", {
|
||||
name: "查看执行状态:执行完成,未完成评估 0 条",
|
||||
}),
|
||||
).toHaveFocus()
|
||||
})
|
||||
|
||||
it("keeps execution status accessible when no signals are hit", async () => {
|
||||
useSelectionResults.mockReturnValue({
|
||||
data: {
|
||||
...selectedResult,
|
||||
signal_count: 0,
|
||||
signals: [],
|
||||
signals_total: 0,
|
||||
},
|
||||
isError: false,
|
||||
isPending: false,
|
||||
})
|
||||
|
||||
render(<SelectionResultsPage />)
|
||||
|
||||
const trigger = screen.getByRole("button", {
|
||||
name: "查看执行状态:执行完成,未完成评估 0 条",
|
||||
})
|
||||
fireEvent.click(trigger)
|
||||
|
||||
const dialog = await screen.findByRole("dialog", {
|
||||
name: "知行 B1 执行状态",
|
||||
})
|
||||
expect(within(dialog).getByText("信号条数")).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("keeps execution status accessible for a failed result", async () => {
|
||||
useSelectionResults.mockReturnValue({
|
||||
data: {
|
||||
...selectedResult,
|
||||
failed_count: 2,
|
||||
failures: [
|
||||
{
|
||||
name: "招商银行",
|
||||
reason: "目标日数据读取失败",
|
||||
status: "data_error",
|
||||
ts_code: "600036.SZ",
|
||||
},
|
||||
{
|
||||
name: "宁德时代",
|
||||
reason: "指标计算失败",
|
||||
status: "calculation_error",
|
||||
ts_code: "300750.SZ",
|
||||
},
|
||||
],
|
||||
signal_count: 0,
|
||||
signals: [],
|
||||
status: "failed",
|
||||
},
|
||||
isError: false,
|
||||
isPending: false,
|
||||
})
|
||||
|
||||
render(<SelectionResultsPage />)
|
||||
|
||||
fireEvent.click(
|
||||
screen.getByRole("button", {
|
||||
name: "查看执行状态:执行失败,未完成评估 2 条",
|
||||
}),
|
||||
)
|
||||
const dialog = await screen.findByRole("dialog", {
|
||||
name: "知行 B1 执行状态",
|
||||
})
|
||||
expect(within(dialog).getByText("执行失败")).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("uses shared selects and resets pagination when the category changes", async () => {
|
||||
render(<SelectionResultsPage />)
|
||||
|
||||
expect(screen.getByRole("combobox", { name: "策略" })).toHaveTextContent(
|
||||
"知行 B1",
|
||||
)
|
||||
const categoryTrigger = screen.getByRole("combobox", {
|
||||
name: "筛选信号分类",
|
||||
})
|
||||
expect(categoryTrigger).toHaveTextContent("全部信号")
|
||||
|
||||
fireEvent.click(categoryTrigger)
|
||||
const oversoldOption = await screen.findByRole("option", { name: "超卖类" })
|
||||
fireEvent.pointerDown(oversoldOption, { pointerType: "mouse" })
|
||||
fireEvent.click(oversoldOption)
|
||||
|
||||
expect(routerNavigate).toHaveBeenCalled()
|
||||
const lastCall =
|
||||
routerNavigate.mock.calls[routerNavigate.mock.calls.length - 1]
|
||||
const searchUpdate = lastCall[0].search as (previous: {
|
||||
category: string
|
||||
page: number
|
||||
pageSize: number
|
||||
search: string
|
||||
}) => Record<string, unknown>
|
||||
expect(
|
||||
searchUpdate({ category: "all", page: 4, pageSize: 5, search: "" }),
|
||||
).toEqual({
|
||||
category: "oversold",
|
||||
page: 1,
|
||||
pageSize: 5,
|
||||
search: "",
|
||||
})
|
||||
})
|
||||
|
||||
it("filters signals, updates the detail panel, and expands mobile details", () => {
|
||||
@@ -298,14 +419,14 @@ describe("SelectionResultsPage", () => {
|
||||
).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("renders a no-data state and triggers an initial run after date selection", () => {
|
||||
it("renders a no-data state and triggers an initial run after date selection", async () => {
|
||||
useSelectionResults.mockReturnValue({
|
||||
data: {
|
||||
...selectedResult,
|
||||
run_id: null,
|
||||
signals: [],
|
||||
status: "no_data",
|
||||
target_trade_date: null,
|
||||
target_trade_date: "2026-08-08",
|
||||
},
|
||||
isError: false,
|
||||
isPending: false,
|
||||
@@ -314,9 +435,12 @@ describe("SelectionResultsPage", () => {
|
||||
render(<SelectionResultsPage />)
|
||||
|
||||
expect(screen.getByText("暂无策略结果")).toBeInTheDocument()
|
||||
fireEvent.change(screen.getByLabelText("目标交易日"), {
|
||||
target: { value: "2026-08-08" },
|
||||
fireEvent.click(screen.getByRole("button", { name: "目标交易日" }))
|
||||
const calendar = await screen.findByRole("grid")
|
||||
const dateButton = within(calendar).getByRole("button", {
|
||||
name: "2026年8月9日 星期日",
|
||||
})
|
||||
fireEvent.click(dateButton)
|
||||
fireEvent.click(screen.getByRole("button", { name: "执行策略" }))
|
||||
|
||||
expect(mutate).toHaveBeenCalledWith(
|
||||
@@ -324,13 +448,28 @@ describe("SelectionResultsPage", () => {
|
||||
request: {
|
||||
rerun: false,
|
||||
strategy: "zhixing_b1",
|
||||
target_trade_date: "2026-08-08",
|
||||
target_trade_date: "2026-08-09",
|
||||
},
|
||||
},
|
||||
expect.any(Object),
|
||||
)
|
||||
})
|
||||
|
||||
it("keeps the target trade date empty after clearing the date picker", async () => {
|
||||
render(<SelectionResultsPage />)
|
||||
|
||||
const dateTrigger = screen.getByRole("button", { name: "目标交易日" })
|
||||
fireEvent.click(dateTrigger)
|
||||
const calendar = await screen.findByRole("grid")
|
||||
fireEvent.click(
|
||||
within(calendar).getByRole("button", {
|
||||
name: "2026年8月8日 星期六,已选择",
|
||||
}),
|
||||
)
|
||||
|
||||
expect(dateTrigger).toHaveTextContent("选择日期")
|
||||
})
|
||||
|
||||
it("continues polling a persisted running run after page reload", () => {
|
||||
useSelectionResults.mockReturnValue({
|
||||
data: { ...selectedResult, signals: [], status: "running" },
|
||||
|
||||
Reference in New Issue
Block a user