179 lines
6.5 KiB
TypeScript
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)
|
|
})
|
|
})
|