1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
|
// @ts-nocheck
import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
import { render, cleanup, waitFor } from '@testing-library/svelte';
vi.mock('../lib/client', async (importOriginal) => {
const actual = await importOriginal<typeof import('../lib/client')>();
return {
...actual,
listProjects: vi.fn(),
listDatasets: vi.fn(),
listRuns: vi.fn(),
getAIUsage: vi.fn()
};
});
import ProjectsPage from '../pages/ProjectsPage.svelte';
import { listProjects, listDatasets, listRuns, getAIUsage } from '../lib/client';
beforeEach(() => {
vi.mocked(listProjects).mockReset().mockResolvedValue({ items: [
{ id: 'pA', name: '项目A', description: null, draft_code: '', draft_generation: 1, created_at: '2026-01-01T00:00:00Z', updated_at: '2026-01-01T00:00:00Z' },
{ id: 'pB', name: '项目B', description: null, draft_code: '', draft_generation: 0, created_at: '2026-01-01T00:00:00Z', updated_at: '2026-01-01T00:00:00Z' }
]});
vi.mocked(listDatasets).mockReset().mockResolvedValue({ items: [{ id: 'd1' }, { id: 'd2' }, { id: 'd3' }] });
vi.mocked(listRuns).mockReset().mockResolvedValue({ items: [
{ id: 'r1', project_id: 'pA', status: 'succeeded', created_at: '2026-02-01T00:00:00Z', result: { equity: [{ equity: 100 }, { equity: 101 }, { equity: 112 }] } },
{ id: 'r2', project_id: 'pB', status: 'succeeded', created_at: '2026-02-01T00:00:00Z', result: null }
]});
vi.mocked(getAIUsage).mockReset().mockResolvedValue({ totals: { input_tokens: 500, output_tokens: 200, requests: 2 } });
});
afterEach(() => cleanup());
// round6 旅程重排:首页去 4 张大卡,改为「问候 + 一行指标摘要(小字)
// + 新建主 CTA + 项目卡列表(名称/状态 badge/迷你 sparkline)」。
describe('ProjectsPage journey round6 (greeting + metrics summary + project cards)', () => {
it('renders a one-line metrics summary, not 4 big cards', async () => {
render(ProjectsPage);
await waitFor(() => expect(vi.mocked(getAIUsage)).toHaveBeenCalled());
await waitFor(() => expect(document.querySelector('[data-testid="metrics-summary"]')).toBeTruthy());
expect(document.querySelectorAll('[data-testid="metric-card"]').length).toBe(0);
const summary = document.querySelector('[data-testid="metrics-summary"]')!.textContent!.replace(/\s+/g, ' ');
expect(summary).toContain('2 个项目');
expect(summary).toContain('3 个数据集');
expect(summary).toContain('2 次回测');
expect(summary).toContain('AI 请求 2');
});
it('renders project cards with status badge and inline sparkline for equity runs', async () => {
render(ProjectsPage);
await waitFor(() => expect(vi.mocked(listRuns)).toHaveBeenCalled());
await waitFor(() => expect(document.querySelector('[data-testid="sparkline"]')).toBeTruthy());
const linkA = document.querySelector('[data-testid="project-link"]');
expect(linkA).toBeTruthy();
expect(linkA!.textContent).toContain('项目A');
// 项目B has a succeeded run without equity → “完成” badge, no sparkline
const cardB = [...document.querySelectorAll('[role="listitem"]')].find((n) => n.textContent!.includes('项目B'))!;
expect(cardB.querySelector('[data-testid="sparkline"]')).toBeNull();
expect(cardB.textContent).toContain('完成');
});
});
|