gongxiangp2/components/department-dashboard/PerformanceConversionDashboard.test.tsx

179 lines
6.5 KiB
TypeScript

import type { ReactNode } from 'react'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import PerformanceConversionDashboard from './PerformanceConversionDashboard'
import { BUSINESS_TREND_BRANDS } from './performance-conversion'
jest.mock('./DepartmentShell', () => ({
__esModule: true,
default: ({ children }: { children: ReactNode }) => <>{children}</>,
}))
const emptyList = {
brands: BUSINESS_TREND_BRANDS.map((brand) => ({ brand, count: 0 })),
items: [],
total: 0,
invalidProductIdCount: 0,
page: 1,
pageSize: 20,
hasMore: false,
workflowSync: {
status: 'ready' as const,
lastCompletedAt: '2099-09-17T01:00:00.000Z',
lastFullReconciledAt: '2099-09-17T00:00:00.000Z',
},
}
const workflowItem = {
key: 'flow-1:1041432226288',
workflowId: 'flow-1',
approvalNumber: '202609170001',
processCode: 'art-design',
title: '商品主图美术设计申请',
brand: '亿家达' as const,
productId: '1041432226288',
spu: 'SPU-001',
platform: '天猫',
completedAt: '2026-07-12T08:00:00.000Z',
monitorStartDate: '2026-07-12',
monitorEndDate: '2027-01-12',
monitoringStatus: 'monitoring' as const,
}
describe('PerformanceConversionDashboard filters', () => {
const originalFetch = global.fetch
beforeEach(() => {
window.history.replaceState(null, '', '/workspaces/business/performance-conversion')
})
afterEach(() => {
global.fetch = originalFetch
})
it('sends the selected completion date range with the list request', async () => {
const fetchMock = jest.fn().mockResolvedValue({
ok: true,
json: async () => ({ success: true, data: emptyList }),
} as Response)
global.fetch = fetchMock
render(<PerformanceConversionDashboard />)
await waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(1))
fireEvent.change(screen.getByLabelText('完结开始日期'), { target: { value: '2026-07-01' } })
fireEvent.change(screen.getByLabelText('完结结束日期'), { target: { value: '2026-07-31' } })
fireEvent.click(screen.getByRole('button', { name: '查询' }))
await waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(2))
const requestUrl = String(fetchMock.mock.calls[1][0])
expect(requestUrl).toContain('completedFrom=2026-07-01')
expect(requestUrl).toContain('completedTo=2026-07-31')
expect(window.location.search).toContain('completedFrom=2026-07-01')
expect(window.location.search).toContain('completedTo=2026-07-31')
expect(screen.getByText('流程与商品 ID 每日增量同步')).toBeInTheDocument()
expect(screen.getByText('最近成功 2099/09/17 09:00')).toBeInTheDocument()
})
it.each([
['syncing', '正在同步流程与商品 ID'],
['error', '流程与商品 ID 同步异常'],
['pending', '等待首次流程与商品 ID 同步'],
] as const)('shows the %s workflow synchronization state', async (status, label) => {
global.fetch = jest.fn().mockResolvedValue({
ok: true,
json: async () => ({
success: true,
data: {
...emptyList,
workflowSync: {
status,
lastCompletedAt: status === 'pending' ? null : '2099-09-17T01:00:00.000Z',
lastFullReconciledAt: status === 'pending' ? null : '2099-09-17T00:00:00.000Z',
},
},
}),
} as Response)
render(<PerformanceConversionDashboard />)
expect(await screen.findByText(label)).toBeInTheDocument()
})
it('shows the business data incremental synchronization state', async () => {
global.fetch = jest.fn().mockResolvedValue({
ok: true,
json: async () => ({
success: true,
data: {
...emptyList,
performanceSync: {
status: 'ready',
lastCompletedAt: '2099-09-17T01:00:00.000Z',
lastSourceDate: '2099-09-17',
},
},
}),
} as Response)
render(<PerformanceConversionDashboard />)
expect(await screen.findByText('经营数据每日增量同步')).toBeInTheDocument()
expect(screen.getByText(/已扫描至 2099\/09\/17/)).toBeInTheDocument()
})
it('opens each product ID on a separate detail page and preserves the list context', async () => {
window.history.replaceState(null, '', '/workspaces/business/performance-conversion?brand=%E4%BA%BF%E5%AE%B6%E8%BE%BE&q=%E4%B8%BB%E5%9B%BE&completedFrom=2026-07-01&completedTo=2026-07-31&page=3')
const fetchMock = jest.fn().mockResolvedValue({
ok: true,
json: async () => ({
success: true,
data: {
...emptyList,
brands: BUSINESS_TREND_BRANDS.map((brand) => ({ brand, count: brand === '亿家达' ? 1 : 0 })),
items: [workflowItem],
total: 41,
page: 3,
},
}),
} as Response)
global.fetch = fetchMock
render(<PerformanceConversionDashboard />)
await waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(1))
expect(String(fetchMock.mock.calls[0][0])).toContain('page=3')
expect(String(fetchMock.mock.calls[0][0])).toContain('q=%E4%B8%BB%E5%9B%BE')
const links = await screen.findAllByRole('link', { name: new RegExp(workflowItem.productId) })
expect(links[0]).toHaveAttribute(
'href',
expect.stringContaining('/workspaces/business/performance-conversion/detail?'),
)
const detailUrl = new URL(links[0].getAttribute('href') || '', 'http://localhost')
expect(detailUrl.searchParams.get('workflowId')).toBe(workflowItem.workflowId)
expect(detailUrl.searchParams.get('productId')).toBe(workflowItem.productId)
expect(detailUrl.searchParams.get('brand')).toBe('亿家达')
expect(detailUrl.searchParams.get('q')).toBe('主图')
expect(detailUrl.searchParams.get('completedFrom')).toBe('2026-07-01')
expect(detailUrl.searchParams.get('completedTo')).toBe('2026-07-31')
expect(detailUrl.searchParams.get('page')).toBe('3')
expect(screen.getAllByText('平台 天猫')).toHaveLength(2)
expect(screen.queryByText(`实例 ${workflowItem.workflowId}`)).not.toBeInTheDocument()
expect(fetchMock).not.toHaveBeenCalledWith(expect.stringContaining('/timeseries'), expect.anything())
})
it('shows an explicit placeholder when the workflow platform is missing', async () => {
global.fetch = jest.fn().mockResolvedValue({
ok: true,
json: async () => ({
success: true,
data: { ...emptyList, items: [{ ...workflowItem, platform: null }], total: 1 },
}),
} as Response)
render(<PerformanceConversionDashboard />)
expect(await screen.findAllByText('平台 —')).toHaveLength(2)
})
})