import { mount } from '@vue/test-utils'; import { createMemoryHistory, createRouter } from 'vue-router'; import { beforeEach, describe, expect, it, vi } from 'vitest'; import MarketDetailPage from './MarketDetailPage.vue'; const { getIndexDetail, getSectorDetail } = vi.hoisted(() => ({ getIndexDetail: vi.fn(), getSectorDetail: vi.fn(), })); vi.mock('@/api/market-overview/detail', () => ({ default: { getIndexDetail, getSectorDetail }, })); function createTestRouter() { return createRouter({ history: createMemoryHistory(), routes: [ { path: '/market-overview/indices/:code', component: MarketDetailPage, meta: { detailType: 'index' }, }, ], }); } const freshResponse = { snapshotId: '20260818-030000', asOf: '2026-08-18T00:00:00.000Z', source: 'tdx' as const, status: 'fresh' as const, warnings: [], data: { instrument: { code: '000001.SH', name: '上证指数', type: 'index' as const }, quote: { price: 3380, changePercent: 0.89, direction: 'up' as const, tradeDate: '20260818' }, series: [ { time: '20260817', open: 3350, high: 3385, low: 3340, close: 3350, amount: 100, volume: 1000, }, { time: '20260818', open: 3370, high: 3400, low: 3360, close: 3380, amount: 120, volume: 1100, }, ], availableViews: { kline: true, intraday: false }, }, }; describe('市场详情页', () => { beforeEach(() => { getIndexDetail.mockReset(); getSectorDetail.mockReset(); }); it('按路由加载指数详情并显示上海时间、K 线和分时边界', async () => { getIndexDetail.mockResolvedValue(freshResponse); const router = createTestRouter(); await router.push('/market-overview/indices/000001.SH'); await router.isReady(); const wrapper = mount(MarketDetailPage, { global: { plugins: [router] } }); await vi.waitFor(() => expect(getIndexDetail).toHaveBeenCalledWith('000001.SH', { period: '1d', count: 120 }), ); await vi.waitFor(() => expect(wrapper.text()).toContain('上证指数')); expect(wrapper.text()).toContain('上证指数'); expect(wrapper.text()).toContain('2026-08-18 08:00:00'); expect(wrapper.text()).toContain('日 K 线'); expect(wrapper.text()).toContain('当前数据源未提供分时数据'); }); it('路由代码格式非法时不调用 API 并显示不可用边界', async () => { const router = createTestRouter(); await router.push('/market-overview/indices/not-a-code'); await router.isReady(); const wrapper = mount(MarketDetailPage, { global: { plugins: [router] } }); expect(getIndexDetail).not.toHaveBeenCalled(); await vi.waitFor(() => expect(wrapper.text()).toContain('详情代码不可用')); }); it('TDX 返回 unavailable 时显示 warning 而不是伪造图表', async () => { getIndexDetail.mockResolvedValue({ ...freshResponse, status: 'unavailable', data: null, warnings: ['指数数据不可用'], }); const router = createTestRouter(); await router.push('/market-overview/indices/000001.SH'); await router.isReady(); const wrapper = mount(MarketDetailPage, { global: { plugins: [router] } }); await vi.waitFor(() => expect(getIndexDetail).toHaveBeenCalled()); await vi.waitFor(() => expect(wrapper.text()).toContain('指数数据不可用')); expect(wrapper.text()).toContain('指数数据不可用'); expect(wrapper.find('svg').exists()).toBe(false); }); });