2026-03-19 16:29:26 +08:00
|
|
|
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'
|
|
|
|
|
|
import { setActivePinia, createPinia } from 'pinia'
|
|
|
|
|
|
import { mount, flushPromises } from '@vue/test-utils'
|
|
|
|
|
|
import { nextTick } from 'vue'
|
|
|
|
|
|
import { useOrderItemStore } from '@/stores/order-item'
|
|
|
|
|
|
import type { OrderItemRecord } from '@/stores/order'
|
|
|
|
|
|
|
|
|
|
|
|
// jsdom 不支持 matchMedia,Ant Design Vue 响应式布局需要
|
|
|
|
|
|
Object.defineProperty(window, 'matchMedia', {
|
|
|
|
|
|
writable: true,
|
|
|
|
|
|
value: (query: string) => ({
|
|
|
|
|
|
matches: false,
|
|
|
|
|
|
media: query,
|
|
|
|
|
|
onchange: null,
|
|
|
|
|
|
addListener: () => {},
|
|
|
|
|
|
removeListener: () => {},
|
|
|
|
|
|
addEventListener: () => {},
|
|
|
|
|
|
removeEventListener: () => {},
|
|
|
|
|
|
dispatchEvent: () => false,
|
|
|
|
|
|
}),
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
vi.mock('@/utils/request', () => ({
|
|
|
|
|
|
api: {
|
|
|
|
|
|
get: vi.fn(),
|
|
|
|
|
|
post: vi.fn(),
|
|
|
|
|
|
put: vi.fn(),
|
|
|
|
|
|
patch: vi.fn(),
|
|
|
|
|
|
},
|
|
|
|
|
|
}))
|
|
|
|
|
|
|
|
|
|
|
|
const mockOrderItems: OrderItemRecord[] = [
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 101,
|
|
|
|
|
|
company_id: 1,
|
|
|
|
|
|
platform_id: 1,
|
|
|
|
|
|
store_id: 1,
|
|
|
|
|
|
order_id: 1,
|
|
|
|
|
|
platform_order_id: 'ORD-20260101-001',
|
|
|
|
|
|
sub_order_id: 'SUB-001',
|
|
|
|
|
|
sub_order_type_id: 1,
|
|
|
|
|
|
product_id: 500,
|
|
|
|
|
|
platform_product_id: 'PROD-001',
|
|
|
|
|
|
product_sku: 'SKU-ABC-001',
|
|
|
|
|
|
product_barcode: '6901234567890',
|
|
|
|
|
|
unit_price: '99.99',
|
|
|
|
|
|
quantity: 2,
|
|
|
|
|
|
discount: '10.00',
|
|
|
|
|
|
total: '189.98',
|
|
|
|
|
|
created_date: '2026-01-01T10:00:00Z',
|
|
|
|
|
|
ext: null,
|
|
|
|
|
|
created_at: '2026-01-01T10:00:00Z',
|
|
|
|
|
|
updated_at: '2026-01-01T10:00:00Z',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
id: 102,
|
|
|
|
|
|
company_id: 2,
|
|
|
|
|
|
platform_id: 1,
|
|
|
|
|
|
store_id: 2,
|
|
|
|
|
|
order_id: 2,
|
|
|
|
|
|
platform_order_id: 'ORD-20260201-002',
|
|
|
|
|
|
sub_order_id: null,
|
|
|
|
|
|
sub_order_type_id: 1,
|
|
|
|
|
|
product_id: 501,
|
|
|
|
|
|
platform_product_id: 'PROD-002',
|
|
|
|
|
|
product_sku: null,
|
|
|
|
|
|
product_barcode: null,
|
|
|
|
|
|
unit_price: '59.00',
|
|
|
|
|
|
quantity: 1,
|
|
|
|
|
|
discount: '0',
|
|
|
|
|
|
total: '59.00',
|
|
|
|
|
|
created_date: '2026-02-01T14:00:00Z',
|
|
|
|
|
|
ext: { color: 'red' },
|
|
|
|
|
|
created_at: '2026-02-01T14:00:00Z',
|
|
|
|
|
|
updated_at: '2026-02-01T14:00:00Z',
|
|
|
|
|
|
},
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
const mockPaginatedResponse = {
|
|
|
|
|
|
items: mockOrderItems,
|
|
|
|
|
|
total: 2,
|
|
|
|
|
|
page: 1,
|
|
|
|
|
|
per_page: 15,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const mockItemDetail = {
|
|
|
|
|
|
...mockOrderItems[0],
|
|
|
|
|
|
parent_order: {
|
|
|
|
|
|
id: 1,
|
|
|
|
|
|
platform_order_id: 'ORD-20260101-001',
|
|
|
|
|
|
order_status_id: 3,
|
|
|
|
|
|
total_amount: '199.99',
|
|
|
|
|
|
total_paid: '189.99',
|
|
|
|
|
|
created_date: '2026-01-01T10:00:00Z',
|
|
|
|
|
|
paid_date: '2026-01-01T10:05:00Z',
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const mockLookupCompanies = [
|
|
|
|
|
|
{ id: 1, name: 'Company A', label: '公司A' },
|
|
|
|
|
|
{ id: 2, name: 'Company B', label: '公司B' },
|
|
|
|
|
|
]
|
|
|
|
|
|
const mockLookupPlatforms = [{ id: 1, developer_id: 1 }]
|
|
|
|
|
|
const mockLookupStores = [
|
|
|
|
|
|
{ id: 1, company_id: 1, platform_id: 1, name: 'Store 1', label: '店铺1' },
|
|
|
|
|
|
{ id: 2, company_id: 2, platform_id: 1, name: 'Store 2', label: '店铺2' },
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
// ─── Store Tests ───────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
describe('useOrderItemStore', () => {
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
|
setActivePinia(createPinia())
|
|
|
|
|
|
vi.restoreAllMocks()
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
describe('initial state', () => {
|
|
|
|
|
|
it('starts with empty orderItems and default pagination', () => {
|
|
|
|
|
|
const store = useOrderItemStore()
|
|
|
|
|
|
expect(store.orderItems).toEqual([])
|
|
|
|
|
|
expect(store.loading).toBe(false)
|
|
|
|
|
|
expect(store.pagination.page).toBe(1)
|
|
|
|
|
|
expect(store.pagination.per_page).toBe(15)
|
|
|
|
|
|
expect(store.pagination.total).toBe(0)
|
|
|
|
|
|
expect(store.filters.platform_order_id).toBe('')
|
|
|
|
|
|
expect(store.filters.platform_product_id).toBe('')
|
|
|
|
|
|
expect(store.filters.product_sku).toBe('')
|
|
|
|
|
|
expect(store.filters.created_date_range).toBeNull()
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
describe('fetchOrderItems', () => {
|
|
|
|
|
|
it('loads data and updates state', async () => {
|
|
|
|
|
|
const { api } = await import('@/utils/request')
|
|
|
|
|
|
vi.mocked(api.get).mockResolvedValueOnce(mockPaginatedResponse)
|
|
|
|
|
|
|
|
|
|
|
|
const store = useOrderItemStore()
|
|
|
|
|
|
await store.fetchOrderItems()
|
|
|
|
|
|
|
|
|
|
|
|
expect(api.get).toHaveBeenCalledWith(
|
|
|
|
|
|
'/api/v1/order-items',
|
|
|
|
|
|
expect.objectContaining({
|
|
|
|
|
|
page: 1,
|
|
|
|
|
|
per_page: 15,
|
|
|
|
|
|
platform_order_id: undefined,
|
|
|
|
|
|
platform_product_id: undefined,
|
|
|
|
|
|
product_sku: undefined,
|
|
|
|
|
|
created_date_from: undefined,
|
|
|
|
|
|
created_date_to: undefined,
|
|
|
|
|
|
}),
|
|
|
|
|
|
)
|
|
|
|
|
|
expect(store.orderItems).toEqual(mockOrderItems)
|
|
|
|
|
|
expect(store.pagination.total).toBe(2)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
it('passes filter params correctly', async () => {
|
|
|
|
|
|
const { api } = await import('@/utils/request')
|
|
|
|
|
|
vi.mocked(api.get).mockResolvedValueOnce(mockPaginatedResponse)
|
|
|
|
|
|
|
|
|
|
|
|
const store = useOrderItemStore()
|
|
|
|
|
|
store.filters.platform_order_id = 'ORD-001'
|
|
|
|
|
|
store.filters.platform_product_id = 'PROD-001'
|
|
|
|
|
|
store.filters.product_sku = 'SKU-ABC'
|
|
|
|
|
|
store.filters.created_date_range = ['2026-01-01', '2026-01-31']
|
|
|
|
|
|
store.cascadeValue.company_id = 1
|
|
|
|
|
|
store.cascadeValue.platform_id = 2
|
|
|
|
|
|
store.cascadeValue.store_id = 3
|
|
|
|
|
|
|
|
|
|
|
|
await store.fetchOrderItems()
|
|
|
|
|
|
|
|
|
|
|
|
expect(api.get).toHaveBeenCalledWith(
|
|
|
|
|
|
'/api/v1/order-items',
|
|
|
|
|
|
expect.objectContaining({
|
|
|
|
|
|
platform_order_id: 'ORD-001',
|
|
|
|
|
|
platform_product_id: 'PROD-001',
|
|
|
|
|
|
product_sku: 'SKU-ABC',
|
|
|
|
|
|
created_date_from: '2026-01-01',
|
|
|
|
|
|
created_date_to: '2026-01-31',
|
|
|
|
|
|
company_id: 1,
|
|
|
|
|
|
platform_id: 2,
|
|
|
|
|
|
store_id: 3,
|
|
|
|
|
|
}),
|
|
|
|
|
|
)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
it('shows error message on failure', async () => {
|
|
|
|
|
|
const { api } = await import('@/utils/request')
|
|
|
|
|
|
const { message } = await import('ant-design-vue')
|
|
|
|
|
|
const errorSpy = vi.spyOn(message, 'error')
|
|
|
|
|
|
vi.mocked(api.get).mockRejectedValueOnce(new Error('Network error'))
|
|
|
|
|
|
|
|
|
|
|
|
const store = useOrderItemStore()
|
|
|
|
|
|
await store.fetchOrderItems()
|
|
|
|
|
|
|
|
|
|
|
|
expect(errorSpy).toHaveBeenCalledWith('Network error')
|
|
|
|
|
|
expect(store.orderItems).toEqual([])
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
it('clears orderItems on fetch failure', async () => {
|
|
|
|
|
|
const { api } = await import('@/utils/request')
|
|
|
|
|
|
const { message } = await import('ant-design-vue')
|
|
|
|
|
|
vi.spyOn(message, 'error')
|
|
|
|
|
|
|
|
|
|
|
|
const store = useOrderItemStore()
|
|
|
|
|
|
vi.mocked(api.get).mockResolvedValueOnce(mockPaginatedResponse)
|
|
|
|
|
|
await store.fetchOrderItems()
|
|
|
|
|
|
expect(store.orderItems.length).toBe(2)
|
|
|
|
|
|
|
|
|
|
|
|
vi.mocked(api.get).mockRejectedValueOnce(new Error('Server error'))
|
|
|
|
|
|
await store.fetchOrderItems()
|
|
|
|
|
|
|
|
|
|
|
|
expect(store.orderItems).toEqual([])
|
|
|
|
|
|
expect(store.pagination.total).toBe(0)
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
describe('resetFilters', () => {
|
|
|
|
|
|
it('clears all filters and resets page to 1', () => {
|
|
|
|
|
|
const store = useOrderItemStore()
|
|
|
|
|
|
store.filters.platform_order_id = 'ORD-001'
|
|
|
|
|
|
store.filters.platform_product_id = 'PROD-001'
|
|
|
|
|
|
store.filters.product_sku = 'SKU-ABC'
|
|
|
|
|
|
store.filters.created_date_range = ['2026-01-01', '2026-01-31']
|
|
|
|
|
|
store.cascadeValue.company_id = 1
|
|
|
|
|
|
store.cascadeValue.platform_id = 2
|
|
|
|
|
|
store.cascadeValue.store_id = 3
|
|
|
|
|
|
store.pagination.page = 5
|
|
|
|
|
|
|
|
|
|
|
|
store.resetFilters()
|
|
|
|
|
|
|
|
|
|
|
|
expect(store.filters.platform_order_id).toBe('')
|
|
|
|
|
|
expect(store.filters.platform_product_id).toBe('')
|
|
|
|
|
|
expect(store.filters.product_sku).toBe('')
|
|
|
|
|
|
expect(store.filters.created_date_range).toBeNull()
|
|
|
|
|
|
expect(store.cascadeValue.company_id).toBeUndefined()
|
|
|
|
|
|
expect(store.cascadeValue.platform_id).toBeUndefined()
|
|
|
|
|
|
expect(store.cascadeValue.store_id).toBeUndefined()
|
|
|
|
|
|
expect(store.pagination.page).toBe(1)
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
describe('name maps', () => {
|
|
|
|
|
|
it('builds lookup maps from loaded data', async () => {
|
|
|
|
|
|
const { api } = await import('@/utils/request')
|
|
|
|
|
|
vi.mocked(api.get)
|
|
|
|
|
|
.mockResolvedValueOnce(mockLookupCompanies)
|
|
|
|
|
|
.mockResolvedValueOnce(mockLookupPlatforms)
|
|
|
|
|
|
.mockResolvedValueOnce(mockLookupStores)
|
|
|
|
|
|
|
|
|
|
|
|
const store = useOrderItemStore()
|
|
|
|
|
|
await store.loadLookups()
|
|
|
|
|
|
|
|
|
|
|
|
expect(store.companyMap.get(1)).toBe('公司A')
|
|
|
|
|
|
expect(store.platformMap.get(1)).toBe('平台 #1')
|
|
|
|
|
|
expect(store.storeMap.get(1)).toBe('店铺1')
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
it('handles loadLookups failure gracefully', async () => {
|
|
|
|
|
|
const { api } = await import('@/utils/request')
|
|
|
|
|
|
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {})
|
|
|
|
|
|
vi.mocked(api.get).mockRejectedValueOnce(new Error('Network error'))
|
|
|
|
|
|
|
|
|
|
|
|
const store = useOrderItemStore()
|
|
|
|
|
|
await store.loadLookups()
|
|
|
|
|
|
|
|
|
|
|
|
expect(warnSpy).toHaveBeenCalledWith('加载查找表数据失败', expect.any(Error))
|
|
|
|
|
|
expect(store.companyMap.size).toBe(0)
|
|
|
|
|
|
warnSpy.mockRestore()
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// ─── Page Component Tests ──────────────────────────────
|
|
|
|
|
|
|
|
|
|
|
|
describe('OrderItemsPage', () => {
|
|
|
|
|
|
let wrapper: ReturnType<typeof mount>
|
|
|
|
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
|
setActivePinia(createPinia())
|
|
|
|
|
|
vi.restoreAllMocks()
|
|
|
|
|
|
document.body.innerHTML = ''
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
|
|
wrapper?.unmount()
|
|
|
|
|
|
document.body.innerHTML = ''
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
async function mountPage() {
|
|
|
|
|
|
const { api } = await import('@/utils/request')
|
|
|
|
|
|
vi.mocked(api.get).mockImplementation((url: string) => {
|
|
|
|
|
|
if (url === '/api/v1/order-items') return Promise.resolve(mockPaginatedResponse) as never
|
|
|
|
|
|
if (url === '/api/v1/companies') return Promise.resolve(mockLookupCompanies) as never
|
|
|
|
|
|
if (url === '/api/v1/platforms') return Promise.resolve(mockLookupPlatforms) as never
|
|
|
|
|
|
if (url === '/api/v1/stores') return Promise.resolve(mockLookupStores) as never
|
|
|
|
|
|
return Promise.resolve([]) as never
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const { default: OrderItemsPage } = await import('../index.vue')
|
|
|
|
|
|
|
|
|
|
|
|
wrapper = mount(OrderItemsPage, {
|
|
|
|
|
|
attachTo: document.body,
|
|
|
|
|
|
global: {
|
|
|
|
|
|
stubs: {
|
|
|
|
|
|
SearchOutlined: { template: '<span />' },
|
|
|
|
|
|
ReloadOutlined: { template: '<span />' },
|
|
|
|
|
|
EyeOutlined: { template: '<span />' },
|
2026-03-20 09:09:41 +08:00
|
|
|
|
CopyOutlined: { template: '<span class="copy-icon-stub" />' },
|
2026-03-19 16:29:26 +08:00
|
|
|
|
CascadeFilter: { template: '<div class="cascade-filter-stub" />' },
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
|
|
|
|
|
await flushPromises()
|
|
|
|
|
|
await nextTick()
|
|
|
|
|
|
return { wrapper, api }
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
it('renders page title and action buttons', async () => {
|
|
|
|
|
|
await mountPage()
|
|
|
|
|
|
|
2026-03-20 09:09:41 +08:00
|
|
|
|
expect(wrapper.text()).toContain('订单项管理')
|
2026-03-19 16:29:26 +08:00
|
|
|
|
const buttons = wrapper.findAll('.ant-btn')
|
|
|
|
|
|
const buttonTexts = buttons.map((b) => b.text())
|
|
|
|
|
|
expect(buttonTexts.some((t) => t.includes('搜索'))).toBe(true)
|
|
|
|
|
|
expect(buttonTexts.some((t) => t.includes('重置'))).toBe(true)
|
|
|
|
|
|
}, 15000)
|
|
|
|
|
|
|
|
|
|
|
|
it('calls fetchOrderItems and loadLookups on mount', async () => {
|
|
|
|
|
|
const { api } = await mountPage()
|
|
|
|
|
|
|
|
|
|
|
|
expect(api.get).toHaveBeenCalledWith(
|
|
|
|
|
|
'/api/v1/order-items',
|
|
|
|
|
|
expect.objectContaining({
|
|
|
|
|
|
page: 1,
|
|
|
|
|
|
per_page: 15,
|
|
|
|
|
|
}),
|
|
|
|
|
|
)
|
|
|
|
|
|
expect(api.get).toHaveBeenCalledWith('/api/v1/companies')
|
|
|
|
|
|
expect(api.get).toHaveBeenCalledWith('/api/v1/platforms')
|
|
|
|
|
|
expect(api.get).toHaveBeenCalledWith('/api/v1/stores')
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
it('renders table with order item data', async () => {
|
|
|
|
|
|
await mountPage()
|
|
|
|
|
|
|
|
|
|
|
|
const html = wrapper.html()
|
|
|
|
|
|
expect(html).toContain('ORD-20260101-001')
|
|
|
|
|
|
expect(html).toContain('SKU-ABC-001')
|
|
|
|
|
|
expect(html).toContain('99.99')
|
|
|
|
|
|
expect(html).toContain('189.98')
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
it('renders company/platform/store names from lookup maps', async () => {
|
|
|
|
|
|
await mountPage()
|
|
|
|
|
|
|
|
|
|
|
|
const html = wrapper.html()
|
|
|
|
|
|
expect(html).toContain('公司A')
|
|
|
|
|
|
expect(html).toContain('店铺1')
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
it('formats amounts correctly (dash for zero values)', async () => {
|
|
|
|
|
|
await mountPage()
|
|
|
|
|
|
|
|
|
|
|
|
// mockOrderItems[1] has discount '0' which should render as '-'
|
|
|
|
|
|
const html = wrapper.html()
|
|
|
|
|
|
expect(html).toContain('59.00')
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
it('renders CascadeFilter component', async () => {
|
|
|
|
|
|
await mountPage()
|
|
|
|
|
|
|
|
|
|
|
|
expect(wrapper.find('.cascade-filter-stub').exists()).toBe(true)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
it('search button resets page to 1 and fetches', async () => {
|
|
|
|
|
|
const { api } = await mountPage()
|
|
|
|
|
|
|
|
|
|
|
|
const store = useOrderItemStore()
|
|
|
|
|
|
store.pagination.page = 3
|
|
|
|
|
|
store.filters.platform_order_id = 'ORD-001'
|
|
|
|
|
|
vi.mocked(api.get).mockClear()
|
|
|
|
|
|
vi.mocked(api.get).mockResolvedValue(mockPaginatedResponse)
|
|
|
|
|
|
|
|
|
|
|
|
const buttons = Array.from(document.body.querySelectorAll('.ant-btn'))
|
|
|
|
|
|
const searchBtn = buttons.find((b) => b.textContent?.trim().includes('搜索'))
|
|
|
|
|
|
searchBtn?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
|
|
|
|
|
await flushPromises()
|
|
|
|
|
|
await nextTick()
|
|
|
|
|
|
|
|
|
|
|
|
expect(store.pagination.page).toBe(1)
|
|
|
|
|
|
expect(api.get).toHaveBeenCalledWith(
|
|
|
|
|
|
'/api/v1/order-items',
|
|
|
|
|
|
expect.objectContaining({
|
|
|
|
|
|
page: 1,
|
|
|
|
|
|
platform_order_id: 'ORD-001',
|
|
|
|
|
|
}),
|
|
|
|
|
|
)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
it('resets filters when clicking reset button', async () => {
|
|
|
|
|
|
await mountPage()
|
|
|
|
|
|
|
|
|
|
|
|
const store = useOrderItemStore()
|
|
|
|
|
|
store.filters.platform_order_id = 'ORD-001'
|
|
|
|
|
|
store.filters.platform_product_id = 'PROD-001'
|
|
|
|
|
|
store.filters.product_sku = 'SKU-ABC'
|
|
|
|
|
|
store.filters.created_date_range = ['2026-01-01', '2026-01-31']
|
|
|
|
|
|
|
|
|
|
|
|
const buttons = Array.from(document.body.querySelectorAll('.ant-btn'))
|
|
|
|
|
|
const resetBtn = buttons.find((b) => b.textContent?.trim().includes('重置'))
|
|
|
|
|
|
expect(resetBtn).toBeDefined()
|
|
|
|
|
|
|
|
|
|
|
|
resetBtn?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
|
|
|
|
|
await flushPromises()
|
|
|
|
|
|
await nextTick()
|
|
|
|
|
|
|
|
|
|
|
|
expect(store.filters.platform_order_id).toBe('')
|
|
|
|
|
|
expect(store.filters.platform_product_id).toBe('')
|
|
|
|
|
|
expect(store.filters.product_sku).toBe('')
|
|
|
|
|
|
expect(store.filters.created_date_range).toBeNull()
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
it('opens detail drawer when clicking view button', async () => {
|
|
|
|
|
|
const { api } = await mountPage()
|
|
|
|
|
|
vi.mocked(api.get).mockResolvedValueOnce(mockItemDetail)
|
|
|
|
|
|
|
|
|
|
|
|
const buttons = Array.from(document.body.querySelectorAll('.ant-btn'))
|
|
|
|
|
|
const viewBtn = buttons.find((b) => b.textContent?.trim() === '查看')
|
|
|
|
|
|
expect(viewBtn).toBeDefined()
|
|
|
|
|
|
|
|
|
|
|
|
viewBtn?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
|
|
|
|
|
await flushPromises()
|
|
|
|
|
|
await nextTick()
|
|
|
|
|
|
|
|
|
|
|
|
const drawer = document.body.querySelector('.ant-drawer')
|
|
|
|
|
|
expect(drawer).toBeTruthy()
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
it('displays parent order summary in drawer', async () => {
|
|
|
|
|
|
const { api } = await mountPage()
|
|
|
|
|
|
vi.mocked(api.get).mockResolvedValueOnce(mockItemDetail)
|
|
|
|
|
|
|
|
|
|
|
|
const buttons = Array.from(document.body.querySelectorAll('.ant-btn'))
|
|
|
|
|
|
const viewBtn = buttons.find((b) => b.textContent?.trim() === '查看')
|
|
|
|
|
|
viewBtn?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
|
|
|
|
|
await flushPromises()
|
|
|
|
|
|
await nextTick()
|
|
|
|
|
|
|
|
|
|
|
|
const drawerHtml = document.body.querySelector('.ant-drawer')?.innerHTML || ''
|
2026-03-20 09:09:41 +08:00
|
|
|
|
expect(drawerHtml).toContain('所属订单')
|
2026-03-19 16:29:26 +08:00
|
|
|
|
expect(drawerHtml).toContain('ORD-20260101-001')
|
|
|
|
|
|
expect(drawerHtml).toContain('199.99')
|
|
|
|
|
|
expect(drawerHtml).toContain('189.99')
|
|
|
|
|
|
expect(drawerHtml).toContain('已支付')
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
it('displays ext JSON in drawer when available', async () => {
|
|
|
|
|
|
const { api } = await mountPage()
|
|
|
|
|
|
const detailWithExt = {
|
|
|
|
|
|
...mockItemDetail,
|
|
|
|
|
|
ext: { color: 'red', size: 'XL' },
|
|
|
|
|
|
}
|
|
|
|
|
|
vi.mocked(api.get).mockResolvedValueOnce(detailWithExt)
|
|
|
|
|
|
|
|
|
|
|
|
const buttons = Array.from(document.body.querySelectorAll('.ant-btn'))
|
|
|
|
|
|
const viewBtn = buttons.find((b) => b.textContent?.trim() === '查看')
|
|
|
|
|
|
viewBtn?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
|
|
|
|
|
await flushPromises()
|
|
|
|
|
|
await nextTick()
|
|
|
|
|
|
|
|
|
|
|
|
const drawerHtml = document.body.querySelector('.ant-drawer')?.innerHTML || ''
|
|
|
|
|
|
expect(drawerHtml).toContain('color')
|
|
|
|
|
|
expect(drawerHtml).toContain('red')
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
it('shows placeholder when ext is null', async () => {
|
|
|
|
|
|
const { api } = await mountPage()
|
|
|
|
|
|
vi.mocked(api.get).mockResolvedValueOnce({ ...mockItemDetail, ext: null })
|
|
|
|
|
|
|
|
|
|
|
|
const buttons = Array.from(document.body.querySelectorAll('.ant-btn'))
|
|
|
|
|
|
const viewBtn = buttons.find((b) => b.textContent?.trim() === '查看')
|
|
|
|
|
|
viewBtn?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
|
|
|
|
|
await flushPromises()
|
|
|
|
|
|
await nextTick()
|
|
|
|
|
|
|
|
|
|
|
|
const drawerHtml = document.body.querySelector('.ant-drawer')?.innerHTML || ''
|
|
|
|
|
|
expect(drawerHtml).toContain('暂无数据')
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2026-03-20 09:09:41 +08:00
|
|
|
|
it('shows placeholder when parent_order is null', async () => {
|
|
|
|
|
|
const { api } = await mountPage()
|
|
|
|
|
|
vi.mocked(api.get).mockResolvedValueOnce({ ...mockItemDetail, parent_order: null })
|
|
|
|
|
|
|
|
|
|
|
|
const buttons = Array.from(document.body.querySelectorAll('.ant-btn'))
|
|
|
|
|
|
const viewBtn = buttons.find((b) => b.textContent?.trim() === '查看')
|
|
|
|
|
|
viewBtn?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
|
|
|
|
|
await flushPromises()
|
|
|
|
|
|
await nextTick()
|
|
|
|
|
|
|
|
|
|
|
|
const drawerHtml = document.body.querySelector('.ant-drawer')?.innerHTML || ''
|
|
|
|
|
|
expect(drawerHtml).toContain('无关联订单')
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2026-03-19 16:29:26 +08:00
|
|
|
|
it('shows error message when detail request fails', async () => {
|
|
|
|
|
|
const { api } = await mountPage()
|
|
|
|
|
|
const { message } = await import('ant-design-vue')
|
|
|
|
|
|
const errorSpy = vi.spyOn(message, 'error')
|
|
|
|
|
|
vi.mocked(api.get).mockRejectedValueOnce(new Error('Not found'))
|
|
|
|
|
|
|
|
|
|
|
|
const buttons = Array.from(document.body.querySelectorAll('.ant-btn'))
|
|
|
|
|
|
const viewBtn = buttons.find((b) => b.textContent?.trim() === '查看')
|
|
|
|
|
|
viewBtn?.dispatchEvent(new MouseEvent('click', { bubbles: true }))
|
|
|
|
|
|
await flushPromises()
|
|
|
|
|
|
await nextTick()
|
|
|
|
|
|
|
|
|
|
|
|
expect(errorSpy).toHaveBeenCalledWith('获取订单项详情失败')
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|