diff options
| author | Somhairle H. Marisol <[email protected]> | 2026-09-18 09:50:37 +0800 |
|---|---|---|
| committer | Somhairle H. Marisol <[email protected]> | 2026-09-18 09:50:37 +0800 |
| commit | 0b1ad75571b312dd60f9ae0099b47cc02ac54c78 (patch) | |
| tree | 1fdcdaa6085f25f6c28ea5cdb071d01c0bc50515 /frontend/src/lib/metricRow.test.ts | |
| parent | 18b2d336d058015341e93dd95fa6a8ac8e2ec67d (diff) | |
| download | strategy-lab-0b1ad75571b312dd60f9ae0099b47cc02ac54c78.tar.gz | |
feat(frontend): 首页指标卡与项目行 sparkline(前端重设计第二轮之一)
[变更性质] 新增能力,不动后端。
[新增功能] dashboardMetrics 聚合(项目/数据集/实验/AI 用量统计 + 最近回测首末净值),实盘数据优先、缺数以占位符 '-' 兜底;ProjectMetricRow 渲染 4 张 metric-card;Sparkline 组件输出 up/down 方向线段与端点圆点。
[实现方案] 聚合逻辑与展示组件分离,sparkline 仅依赖 first/last 两个字段,无图表库依赖。
[影响范围] frontend/src/lib 与 components;配套 5 组单测(dashboardMetrics/sparkline/metricRow)。
Diffstat (limited to 'frontend/src/lib/metricRow.test.ts')
| -rw-r--r-- | frontend/src/lib/metricRow.test.ts | 63 |
1 files changed, 63 insertions, 0 deletions
diff --git a/frontend/src/lib/metricRow.test.ts b/frontend/src/lib/metricRow.test.ts new file mode 100644 index 0000000..bc41b54 --- /dev/null +++ b/frontend/src/lib/metricRow.test.ts @@ -0,0 +1,63 @@ +// @ts-nocheck +import { describe, expect, it, vi, afterEach } from 'vitest'; +import { cleanup } from '@testing-library/svelte'; +import { render, screen } from '@testing-library/svelte'; + +import ProjectMetricRow from '../components/ProjectMetricRow.svelte'; + +// Leader round2: 首页改为指标卡行——当前项目数、数据集总数、 +// 运行中/已完成回测数、AI 用量;取不到的字段占位 "-"。 +// 真实渲染断言,测试数据通过 props 注入。 + +afterEach(() => cleanup()); + +const projects = [{ id: 'p1' }, { id: 'p2' }, { id: 'p3' }]; +const datasets = [{ id: 'd1' }, { id: 'd2' }]; +const runs = [ + { id: 'r1', status: 'running', result: null }, + { id: 'r2', status: 'queued', result: null }, + { id: 'r3', status: 'succeeded', result: { metrics: {} } }, + { id: 'r4', status: 'succeeded', result: { metrics: {} } } +]; + +describe('ProjectMetricRow (real render, 4 metric cards)', () => { + it('renders a metric-card row: 项目/数据集/回测 running+done/AI 用量, values visible', () => { + const { container } = render(ProjectMetricRow, { + props: { + projects, datasets, runs, + usage: { totals: { input_tokens: 1200, output_tokens: 340, requests: 7 } } + } + }); + const cards = container.querySelectorAll('[data-testid="metric-card"]'); + expect(cards.length).toBe(4); + expect(screen.getByText('我的项目')).toBeTruthy(); + expect(screen.getByText('数据集')).toBeTruthy(); + expect(screen.getByText('回测')).toBeTruthy(); + expect(screen.getByText('AI 用量')).toBeTruthy(); + // 数字直接可见:项目卡/数据集卡与回测拆分值 + const values = [...container.querySelectorAll('.metric-card .mvalue')].map((n) => n.textContent!.replace(/\s+/g, ' ').trim()); + expect(values[0]).toBe('3'); + expect(values[1]).toBe('2'); + expect(values[2]).toContain('运行中'); + expect(values[2]).toContain('2'); + expect(values[2]).toContain('已完成'); + expect(values[2]).toContain('2'); + expect(screen.getByText('7 次')).toBeTruthy(); + }); + + it('usage unavailable → "-" placeholder + TODO marker on the card', () => { + const { container } = render(ProjectMetricRow, { + props: { projects, datasets, runs, usage: null } + }); + const cards = [...container.querySelectorAll('[data-testid="metric-card"]')]; + const usage = cards.at(-1)!; + expect(usage.textContent).toContain('-'); + expect(usage.getAttribute('data-todo') ?? '').toContain('TODO'); + }); + + it('runs card splits 运行中 / 已完成', () => { + render(ProjectMetricRow, { props: { projects, datasets, runs, usage: null } }); + expect(screen.getByText(/运行中/)).toBeTruthy(); + expect(screen.getByText(/已完成/)).toBeTruthy(); + }); +}); |
