summaryrefslogtreecommitdiff
path: root/frontend/src
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src')
-rw-r--r--frontend/src/lib/assumptions.test.ts85
1 files changed, 85 insertions, 0 deletions
diff --git a/frontend/src/lib/assumptions.test.ts b/frontend/src/lib/assumptions.test.ts
new file mode 100644
index 0000000..26bb916
--- /dev/null
+++ b/frontend/src/lib/assumptions.test.ts
@@ -0,0 +1,85 @@
+// @ts-nocheck
+import { describe, expect, it, vi } from 'vitest';
+import { mount, unmount } from 'svelte';
+import resultsRaw from '../pages/ProjectResultsTab.svelte?raw';
+import backtestRaw from '../pages/ProjectBacktestTab.svelte?raw';
+
+// 用户拍板方案 1:执行假设长文不再混入用户面 warnings。
+// worker 已把 EXECUTION_ASSUMPTIONS 移到 result.assumptions(tests/worker/test_backtest.py
+// test_assumptions_out_of_warnings 守门 worker 侧);本文件守门前端渲染侧:
+// 技术口径只在 assumptions 字段驱动下渲染,且默认折叠(Collapsible)。
+
+const PROJECT_ID = '7630e296-0b2f-44c3-85f1-fe695e7a7e4b';
+const RUN_ID = '569b23d0-1111-4222-8333-4444444444442';
+const ASSUMPTIONS = { slippage: '0.1%', 'next_bar_open_fill': true };
+
+function resultBody(withAssumptions) {
+ return {
+ id: RUN_ID, project_id: PROJECT_ID, version_id: 'v', dataset_id: 'd',
+ status: 'succeeded',
+ config: { capital: 100000, commission: 0.0003, slippage: 0.001 },
+ created_at: '2026-01-01T00:00:00Z',
+ result: {
+ engine: { name: 'backtrader', version: '1.9' },
+ metrics: { total_return: 0.0123, annual_return: 0.05, max_drawdown: -0.033, sharpe: 0.9, trade_count: 1, final_equity: 101234.5 },
+ equity: [{ date: '2024-01-02', equity: 100000, cash: 100000 }],
+ orders: [], trades: [], closed_trades: [],
+ logs: [], warnings: ['真实警告:数据源公告休市'],
+ assumptions: withAssumptions ? ASSUMPTIONS : undefined,
+ elapsed_ms: 1,
+ },
+ };
+}
+
+const fetchMock = (body) => (url) => {
+ if (/\/api\/runs\/[0-9a-f-]+$/.test(String(url))) return Promise.resolve(new Response(JSON.stringify(body), { status: 200, headers: { 'content-type': 'application/json' } }));
+ if (/\/api\/projects\/[0-9a-f-]+$/.test(String(url)))
+ return Promise.resolve(new Response(JSON.stringify({ id: PROJECT_ID, name: '端到端验收项目', draft_code: 'x=1', draft_generation: 2 }), { status: 200, headers: { 'content-type': 'application/json' } }));
+ return Promise.resolve(new Response('{"items":[]}', { status: 200, headers: { 'content-type': 'application/json' } }));
+};
+
+async function mountResultsTab(body) {
+ vi.stubGlobal('ResizeObserver', class { observe() {} unobserve() {} disconnect() {} });
+ vi.stubGlobal('fetch', fetchMock(body));
+ const ProjectResultsTab = (await import('../pages/ProjectResultsTab.svelte')).default;
+ const host = document.createElement('div');
+ document.body.appendChild(host);
+ const inst = mount(ProjectResultsTab, { target: host, props: { projectId: PROJECT_ID, runId: RUN_ID } });
+ await new Promise((r) => setTimeout(r, 80));
+ return { inst, host };
+}
+
+describe('技术口径:assumptions 独立折叠区,不混入 warnings', () => {
+ it('assumptions 渲染在折叠区;warnings 列表只含真实警告', { timeout: 20000 }, async () => {
+ const { inst, host } = await mountResultsTab(resultBody(true));
+ try {
+ const text = host.textContent.replace(/\s+/g, ' ');
+ // warnings Alert 渲染真实警告
+ expect(text).toContain('真实警告:数据源公告休市');
+ // assumptions keys/values 不出现在 warnings <li>
+ const warnLis = [...host.querySelectorAll('li')].map((n) => n.textContent);
+ for (const k of Object.keys(ASSUMPTIONS)) {
+ expect(warnLis.some((w) => w.includes(k))).toBe(false);
+ }
+ // 折叠区标题存在(默认收起,仅结构存在不展开内容)
+ expect(host.querySelector('[data-testid="assumptions-toggle"]')?.textContent).toContain('技术口径');
+ expect(host.querySelector('[data-testid="assumptions-list"]')).toBeTruthy();
+ } finally { unmount(inst); host.remove(); vi.unstubAllGlobals(); }
+ });
+
+ it('没有 assumptions 字段则不渲染技术口径区', async () => {
+ const { inst, host } = await mountResultsTab(resultBody(false));
+ try {
+ expect(host.querySelector('[data-testid="assumptions-toggle"]')).toBeNull();
+ expect(host.querySelector('[data-testid="assumptions-list"]')).toBeNull();
+ } finally { unmount(inst); host.remove(); vi.unstubAllGlobals(); }
+ });
+
+ it('渲染路径源码不含 "execution assumptions" 旧拼串文案', () => {
+ for (const src of [resultsRaw, backtestRaw]) {
+ expect(src).not.toContain('execution assumptions');
+ // assumptions 数据只进 技术口径 折叠区(一个 dl 列表锚点)
+ expect(src).toContain('assumptions-list');
+ }
+ });
+});