Files
tick-stock-panel/frontend/src/extensions/loading.test.tsx
T
shy3130 ab2b82da37 perf: 收窄缓存失效并优化异步任务与首屏加载
按 enriched 写入最早影响日期维护有界区间版本,保留全局发布一致性校验,使历史读取与回测缓存复用不受较晚行情更新影响;历史修订和未知范围变更仍失效。

将扩展数据解析、落盘与备份/策略导入移出事件循环;串行限制扩展数据写入,并在请求取消后保持执行中的写入限额及临时文件清理。

拆分 11 个扩展页面为轻量注册清单和按需页面,助手抽屉首次使用时加载;并发获取扩展后按固定顺序注册,为运行时脚本增加超时隔离。保持金融计算、业务数据格式与扩展 v1 契约不变。

验证:隔离全量后端 3923 项通过、7 项跳过、1 项预期失败;前端 447 项通过,TypeScript 与生产构建通过。Ruff 无新增告警,git diff --check 通过。浏览器验证 11 个扩展路由与脚本超时隔离。

性能:1000 标的、120 天、12 万行、30 次更新,历史面板读取中位数 7.19ms -> 0.14ms,计算 31 -> 1 次,结果一致。注入 250ms 解析耗时的心跳间隔中位最大值 269ms -> 11.3ms。启动必需 JS 2.33MB -> 0.65MB;10Mbps/40ms 网络与固定 API 响应下,登录页 3 次冷启动中位数 3.15s -> 1.12s。

Signed-off-by: shy3130 <shy3130@qq.com>
2026-11-01 22:32:34 +08:00

73 lines
2.9 KiB
TypeScript

// @vitest-environment jsdom
import { act, Suspense } from 'react'
import { createRoot } from 'react-dom/client'
import { MemoryRouter, Route, Routes } from 'react-router-dom'
import { afterEach, expect, it, vi } from 'vitest'
import type { FrontendExtensionModule } from './types'
const loads = vi.hoisted(() => ({ paper: vi.fn(), abnormal: vi.fn() }))
vi.mock('../custom/paper/Page', () => {
loads.paper()
return { Paper: () => <div>模拟盘页面</div> }
})
vi.mock('../custom/abnormal/Page', () => {
loads.abnormal()
return { AbnormalMoves: () => <div>异动页面</div> }
})
afterEach(() => { vi.resetModules() })
it('注册菜单不加载页面;直接访问扩展路由只加载该页面', async () => {
Object.assign(globalThis, { IS_REACT_ACT_ENVIRONMENT: true })
const registry = await import('./registry')
await registry.loadFrontendExtensions({
paper: () => import('../custom/paper/extension'),
abnormal: () => import('../custom/abnormal/extension'),
})
registry.finalizeFrontendExtensions(new Set(['/']))
expect(loads.paper).not.toHaveBeenCalled()
expect(loads.abnormal).not.toHaveBeenCalled()
expect(registry.getFrontendExtensionNavigation().map(n => n.label).sort()).toEqual(['异动监控', '模拟盘'].sort())
const container = document.createElement('div')
const root = createRoot(container)
try {
await act(async () => root.render(
<MemoryRouter initialEntries={['/paper']}>
<Suspense fallback={<span>加载中</span>}>
<Routes>{registry.getFrontendExtensionRoutes().map(route => {
const Page = route.component
return <Route key={route.id} path={route.path} element={<Page />} />
})}</Routes>
</Suspense>
</MemoryRouter>,
))
expect(container.textContent).toBe('模拟盘页面')
expect(loads.paper).toHaveBeenCalledTimes(1)
expect(loads.abnormal).not.toHaveBeenCalled()
} finally {
act(() => root.unmount())
}
})
it('并发加载仍按源顺序处理重复 ID,隔离同步抛错的模块', async () => {
const registry = await import('./registry')
let finish!: (m: FrontendExtensionModule) => void
const first = new Promise<FrontendExtensionModule>(resolve => { finish = resolve })
const second = vi.fn(async () => ({ default: { id: 'test.same', apiVersion: 1 as const } }))
const loading = registry.loadFrontendExtensions({
z: second,
a: () => first,
broken: () => { throw new Error('bad module') },
})
await Promise.resolve()
expect(second).toHaveBeenCalledTimes(1)
finish({ default: {
id: 'test.same', apiVersion: 1,
routes: [{ id: 'first', path: '/first', component: () => null }],
} })
await loading
registry.finalizeFrontendExtensions(new Set())
expect(registry.getFrontendExtensionRoutes().map(r => r.path)).toEqual(['/first'])
expect(registry.getFrontendExtensionLoadErrors().map(e => e.source)).toEqual(['broken', 'z'])
})