Files
client-web/src/modules/dashboard/overview.test.mjs
T
2026-09-20 21:52:15 +08:00

145 lines
6.3 KiB
JavaScript

import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import test from 'node:test'
import { loadDashboardOverview } from './overview.ts'
const dashboardView = readFileSync(new URL('./views/DashboardView.vue', import.meta.url), 'utf8')
const profile = 'company:profile:view'
const list = 'order:first-leg:list'
const edit = 'order:first-leg:edit'
const company = { tenantName: '示例企业', status: 'ACTIVE' }
function order(overrides = {}) {
return {
orderId: 101,
orderNo: 'ORDER-101',
inboundCode: 'INBOUND-101',
destinationWarehouseName: '示例目的仓',
channelName: '示例渠道',
mainStatus: 'WAIT_MEASURE',
customerFeeConfirmationRequired: false,
createdAt: '2026-09-18T08:00:00Z',
...overrides,
}
}
function draft(overrides = {}) {
return { ...order(), status: 'DRAFT', ...overrides }
}
function requests(overrides = {}) {
return {
company: async () => company,
orders: async () => ({ records: [order()] }),
drafts: async () => ({ records: [draft()] }),
...overrides,
}
}
test('only calls sources allowed by their complete permission requirements', async (t) => {
const cases = [
{ permissions: [], expected: [] },
{ permissions: [profile], expected: ['company'] },
{ permissions: [list], expected: ['orders'] },
{ permissions: [edit], expected: [] },
{ permissions: [list, edit], expected: ['orders', 'drafts'] },
{ permissions: [profile, list, edit], expected: ['company', 'orders', 'drafts'] },
]
for (const { permissions, expected } of cases) {
await t.test(permissions.join(',') || 'no permissions', async () => {
const calls = []
const result = await loadDashboardOverview(permissions, requests({
company: async () => { calls.push('company'); return company },
orders: async () => { calls.push('orders'); return { records: [order()] } },
drafts: async () => { calls.push('drafts'); return { records: [draft()] } },
}))
assert.deepEqual(calls, expected)
assert.equal(result.company, expected.includes('company') ? company : null)
assert.equal(result.orders.length, expected.includes('orders') ? 1 : 0)
assert.equal(result.drafts.length, expected.includes('drafts') ? 1 : 0)
assert.deepEqual(result.errors, {})
})
}
})
test('one failed source preserves successful sources and exposes its error', async (t) => {
for (const failedSource of ['company', 'orders', 'drafts']) {
await t.test(failedSource, async () => {
const result = await loadDashboardOverview([profile, list, edit], requests({
[failedSource]: async () => { throw new Error(`${failedSource} unavailable`) },
}))
assert.deepEqual(result.errors, { [failedSource]: `${failedSource} unavailable` })
assert.equal(result.company, failedSource === 'company' ? null : company)
assert.equal(result.orders.length, failedSource === 'orders' ? 0 : 1)
assert.equal(result.drafts.length, failedSource === 'drafts' ? 0 : 1)
})
}
})
test('loads sources concurrently and distinguishes a failed source from an empty result', async () => {
const pending = new Map()
const operation = loadDashboardOverview([profile, list, edit], requests(Object.fromEntries(
['company', 'orders', 'drafts'].map((source) => [source, () => new Promise((resolve, reject) => pending.set(source, { resolve, reject }))]),
)))
assert.deepEqual([...pending.keys()], ['company', 'orders', 'drafts'])
pending.get('company').resolve(company)
pending.get('orders').resolve({ records: [] })
pending.get('drafts').reject(null)
const result = await operation
assert.deepEqual(result.orders, [])
assert.equal(result.errors.orders, undefined)
assert.deepEqual(result.drafts, [])
assert.equal(result.errors.drafts, '订单草稿加载失败,请重试')
})
test('sorts each source by creation time and includes only actual drafts', async () => {
const orders = [
order({ orderId: 1, createdAt: '2026-09-16T08:00:00Z' }),
order({ orderId: 2, createdAt: '2026-09-18T08:00:00Z', mainStatus: 'COMPLETED', customerFeeConfirmationRequired: true }),
]
const drafts = [
draft({ orderId: 3, createdAt: '2026-09-15T08:00:00Z' }),
draft({ orderId: 4, status: 'SUBMITTED', createdAt: '2026-09-19T08:00:00Z' }),
draft({ orderId: 5, createdAt: '2026-09-17T08:00:00Z' }),
]
const result = await loadDashboardOverview([list, edit], requests({
orders: async () => ({ records: orders }),
drafts: async () => ({ records: drafts }),
}))
assert.deepEqual(result.orders.map(({ id }) => id), ['2', '1'])
assert.deepEqual(result.drafts.map(({ id }) => id), ['5', '3'])
assert.equal(result.orders[0].status, 'COMPLETED')
assert.equal(result.orders[0].requiresFeeConfirmation, true)
assert.ok(result.drafts.every(({ status, requiresFeeConfirmation }) => status === 'DRAFT' && !requiresFeeConfirmation))
assert.deepEqual(orders.map(({ orderId }) => orderId), [1, 2])
assert.deepEqual(drafts.map(({ orderId }) => orderId), [3, 4, 5])
})
test('keeps absent business numbers null and internal identifiers out of display fields', async () => {
const missing = { orderId: 987654, orderNo: null, inboundCode: null, destinationWarehouseName: null, channelName: null }
const result = await loadDashboardOverview([list, edit], requests({
orders: async () => ({ records: [order(missing)] }),
drafts: async () => ({ records: [draft(missing)] }),
}))
for (const item of [...result.orders, ...result.drafts]) {
assert.equal(item.id, '987654')
assert.equal(item.orderNo, null)
assert.equal(item.inboundCode, null)
assert.equal(item.destination, null)
assert.equal(item.channel, null)
}
})
test('matches the updated six-action icon and motion treatment', () => {
assert.match(dashboardView, /icon: Forklift/)
assert.doesNotMatch(dashboardView, /PanelsTopLeft/)
assert.match(dashboardView, /featured: true/)
assert.match(dashboardView, /quick-action--featured/)
assert.match(dashboardView, /featured-aura 5s ease-in-out infinite/)
assert.match(dashboardView, /featured-shine 3\.6s ease-in-out infinite/)
assert.match(dashboardView, /\.quick-action:hover \{[^}]*border-color:[^}]*transform: translateY\(-9px\);/)
assert.match(dashboardView, /prefers-reduced-motion: reduce/)
assert.match(dashboardView, /\.client-dashboard a:focus-visible/)
})