// @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
  • 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'); } }); });