diff options
| author | Somhairle H. Marisol <[email protected]> | 2026-09-18 08:26:57 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-09-18 08:26:57 +0800 |
| commit | 45ecb4e9dfe225bc982e9af9315590bcb0783df7 (patch) | |
| tree | e046f42ace89c09d60aa4f07db8450507364fdb5 /frontend/src/lib/datasetCardWarnings.test.ts | |
| parent | 33cac3eef7d9962d8803cd812553549d6f555516 (diff) | |
| download | strategy-lab-45ecb4e9dfe225bc982e9af9315590bcb0783df7.tar.gz | |
feat(frontend): 结果/实验/策略/数据页迁移 shadcn 与新 Chart API
[变更性质]
四个业务标签页与数据集卡片迁移 shadcn 组件,图表消费切换到
layerchart 长格式 API,功能与数据语义不变。
[新增功能]
结果页权益曲线(策略权益/现金/基准,基准可缺省)与回撤曲线(desc
面积)改由 Chart.svelte data/series 渲染;实验记录页多回测归一对比
曲线改为快速长格式行构造。空数据行允许 undefined 列(基准缺口),
Chart data 类型放宽 [key:string]: string|number|undefined。
[实现方案]
RunsPage:查看入口改 shadcn Button(href 语义保留 hash 路由),
对比图数据由 compareOption(echarts option)化为 compare。
ProjectResultsTab:图表开关、日志折叠用 Button;equityData/
drawdownData $derived 计算峰值回撤。ProjectStrategyTab:快照输入、
冲突/AI/版本操作迁移 Button/Input。ProjectDataTab 与 DatasetCard:
shadcn 组件 + 保留 banner error/warn/info、retry-row 等 legacy class,
"使用相同请求重新获取"重试入口与 onRecreated 逻辑不变,配套
datasetCardRetry/datasetCardWarnings/projectDataTabRecreated 回归
测试。resultsAccounting.test.ts:layerchart 在 jsdom 挂载约 3 秒,
suite 全量下易撞默认 5s 超时,首测提高至 20s。
[影响范围]
npm run check 0 error、58 tests 全绿、npm run build 通过;真浏览器
375/768/1440 图表页零溢出(artifacts/ui-shadcn 截图与探针 JSON)。
Diffstat (limited to 'frontend/src/lib/datasetCardWarnings.test.ts')
| -rw-r--r-- | frontend/src/lib/datasetCardWarnings.test.ts | 91 |
1 files changed, 91 insertions, 0 deletions
diff --git a/frontend/src/lib/datasetCardWarnings.test.ts b/frontend/src/lib/datasetCardWarnings.test.ts new file mode 100644 index 0000000..8e79e84 --- /dev/null +++ b/frontend/src/lib/datasetCardWarnings.test.ts @@ -0,0 +1,91 @@ +import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest'; +import { render, screen, fireEvent, waitFor, cleanup } from '@testing-library/svelte'; + +vi.mock('../lib/client', () => { + const real = vi.importActual<typeof import('../lib/client')>('../lib/client'); + return { + ...real, + getDataset: vi.fn(), + getDatasetPreview: vi.fn(), + createDataset: vi.fn() + }; +}); + +import DatasetCard from '../components/DatasetCard.svelte'; +import { getDataset, getDatasetPreview } from '../lib/client'; +import type { Dataset, DatasetPreview } from '../lib/types'; + +const readyDs: Dataset = { + id: 'ds-ready-1', + name: '国泰自由现金流-20251231-20260917', + request: { + instruments: [ + { symbol: '159399', market: 'SZ', asset_type: 'etf', name: '国泰自由现金流ETF', currency: null } + ], + start_date: '2025-12-31', + end_date: '2026-09-17', + frequency: 'daily', + adjustment: 'none', + fields: ['open', 'high', 'low', 'close', 'volume'] + }, + status: 'ready', + created_at: '2026-09-17T01:00:00Z' +}; + +const preview: DatasetPreview = { + columns: ['date', 'open', 'high', 'low', 'close', 'volume'], + rows: [ + { date: '2026-09-16', open: 1.001, high: 1.001, low: 0.982, close: 0.992, volume: 144526400 } + ], + coverage: [ + { + instrument: '159399', + market: 'SZ', + asset_type: 'etf', + requested_start: '2025-12-31', + requested_end: '2026-09-17', + actual_start: '2025-12-31', + actual_end: '2026-09-16', + row_count: 173, + warnings: [ + 'sina volume unit is 股 (shares)', + 'provider_fallback: eastmoney attempt failed; served by sina', + 'actual coverage 2025-12-31..2026-09-16 differs from request; gaps kept' + ] + } + ], + warnings: [] +}; + +beforeEach(() => { + vi.mocked(getDataset).mockResolvedValue(readyDs); + vi.mocked(getDatasetPreview).mockReset(); +}); + +afterEach(() => cleanup()); + +describe('per-instrument provenance warnings must be visible even without the range warning banner (159399 units/provider_fallback disclosure)', () => { + it('renders 夲-instrument sina volume-unit and provider_fallback warnings even when the common range banner shows 覆盖平稳', async () => { + vi.mocked(getDatasetPreview).mockResolvedValue(preview); + render(DatasetCard, { props: { dsId: 'ds-ready-1' } }); + const btn = await screen.findByRole('button', { name: /查看数据覆盖预览/ }); + await fireEvent.click(btn); + await waitFor(() => { + expect(screen.getByText(/provider_fallback/)).toBeTruthy(); + expect(screen.getByText(/volume unit is 股/)).toBeTruthy(); + expect(screen.getByText(/differs from request/)).toBeTruthy(); + }); + }); + + it('no warnings: shows the plain 覆盖平稳 banner and no warning list', async () => { + vi.mocked(getDatasetPreview).mockResolvedValue({ + ...preview, + coverage: [{ ...preview.coverage[0], warnings: [] }] + }); + render(DatasetCard, { props: { dsId: 'ds-ready-1' } }); + const btn = await screen.findByRole('button', { name: /查看数据覆盖预览/ }); + await fireEvent.click(btn); + await screen.findByText(/覆盖平稳/); + expect(screen.queryByText(/provider_fallback/)).toBeNull(); + }); +}); |
