feat: focus collection page on account activity and progress
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import CollectionTaskProgress from './CollectionTaskProgress.vue'
|
||||
import CollectionProgressGauge from './CollectionProgressGauge.vue'
|
||||
import type { Task, TaskDetail, StageEstimate, WarehouseOverview } from '../types'
|
||||
const task: Task = { taskNo: 'TASK-W', warehouseCode: 'W', taskType: 'BACKFILL', dataFamily: 'OUTBOUND', taskStatus: 'WAITING_SOURCE', dependencyState: 'WAITING_CONFIRMED', progressSourceTaskNo: 'TASK-S', queryFrom: null, queryUntil: null, applyState: null, attemptCount: 0, latestAttemptNo: null, resultPublicationState: 'NOT_PUBLISHED', nextRetryAt: null, allowedActions: [], actionBlockReason: null, progress: null }
|
||||
const progress = { phase: 'DETAIL', progressScope: 'ACCOUNT_SHARED', countBasis: 'TASK_CUMULATIVE', attemptNo: 2, scanRound: 1, completed: 339, total: 2787, totalKnown: true, unit: 'ORDER', sliceFrom: null, sliceUntil: null, progressUpdatedAt: '2026-10-13T01:01:00Z' }
|
||||
const source: Task = { ...task, taskNo: 'TASK-S', taskStatus: 'RUNNING', dependencyState: 'NONE', progressSourceTaskNo: null, attemptCount: 2, progress }
|
||||
const detail = { task, sharedUpstream: source, observedAt: '2026-10-13T01:02:00Z', protection: {} } as TaskDetail
|
||||
const wrappers: ReturnType<typeof mount>[] = []
|
||||
afterEach(() => { wrappers.splice(0).forEach(w => w.unmount()); vi.useRealTimers() })
|
||||
describe('collection task progress evidence', () => {
|
||||
it('uses only a confirmed source and does not infer earlier stage completion from the current phase', async () => {
|
||||
const w = mount(CollectionTaskProgress, { props: { task, detail } }); wrappers.push(w)
|
||||
expect(w.text()).toContain('TASK-S'); expect(w.text()).toContain('12.1%'); expect(w.text()).toContain('2448')
|
||||
expect(w.text()).toContain('账号共享'); expect(w.findAll('li')[0]?.text()).toContain('状态未确认')
|
||||
expect(w.text()).not.toContain('✓')
|
||||
await w.setProps({ task: { ...task, dependencyState: 'WAITING_UNCONFIRMED', progressSourceTaskNo: null } })
|
||||
expect(w.text()).toContain('关联进度尚未确认'); expect(w.text()).not.toContain('339'); expect(w.text()).not.toContain('12.1%')
|
||||
})
|
||||
it('accepts only explicit matching stage completion and keeps publication independent', () => {
|
||||
const w = mount(CollectionTaskProgress, { props: { task, detail: { ...detail, sharedUpstream: { ...source, progress: { ...progress, stages: [{ ...progress, phase: 'DIRECTORY', stageFinishedAt: '2026-10-13T01:00:00Z' }, { ...progress, phase: 'PUBLISH', stageFinishedAt: '2026-10-13T01:00:00Z' }] } } } } }); wrappers.push(w)
|
||||
expect(w.findAll('li')[0]?.text()).toContain('✓ 已结束')
|
||||
expect(w.findAll('li')[4]?.text()).not.toContain('✓')
|
||||
expect(w.text()).toContain('报表可用状态待确认')
|
||||
})
|
||||
it('retains confirmed source evidence after dependency failure without borrowing its current count', () => {
|
||||
const w = mount(CollectionTaskProgress, { props: { task: { ...task, dependencyState: 'FAILED', progressSourceTaskNo: task.taskNo, blockingReason: 'UPSTREAM_FAILED' }, detail } }); wrappers.push(w)
|
||||
expect(w.text()).toContain('已确认关联来源:TASK-S')
|
||||
expect(w.text()).toContain('关联来源失败,等待恢复')
|
||||
expect(w.text()).not.toContain('12.1%')
|
||||
})
|
||||
it('treats COUNT_COMPLETE including zero of zero only as completed counts', () => {
|
||||
const stageEstimate: StageEstimate = { status: 'UNAVAILABLE', progressIdentity: 'zero-count', completed: '0', total: '0', sampleFrom: null, sampleUntil: null, completedDelta: null, ratePerSecond: null, remainingSeconds: null, estimatedAt: null, validUntil: null, unavailableReason: 'COUNT_COMPLETE' }
|
||||
const w = mount(CollectionTaskProgress, { props: { task, visible: true, detail: { ...detail, sharedUpstream: { ...source, progress: { ...progress, completed: 0, total: 0 }, stageEstimate } } } }); wrappers.push(w)
|
||||
expect(w.text()).toContain('计数已齐,等待阶段确认')
|
||||
expect(w.text()).not.toContain('✓ 已结束')
|
||||
expect(w.text()).not.toContain('预计还需')
|
||||
expect(w.find('[role="progressbar"]').exists()).toBe(false)
|
||||
})
|
||||
it('uses unavailable reasons as the primary estimate text and prioritizes explicit stage evidence', async () => {
|
||||
const e: StageEstimate = { status: 'UNAVAILABLE', progressIdentity: 'identity', completed: 339, total: 2787, sampleFrom: null, sampleUntil: null, completedDelta: null, ratePerSecond: null, remainingSeconds: null, estimatedAt: null, validUntil: null, unavailableReason: 'TERMINAL' }
|
||||
const w = mount(CollectionTaskProgress, { props: { task, visible: true, detail: { ...detail, sharedUpstream: { ...source, stageEstimate: e } } } }); wrappers.push(w)
|
||||
for (const [reason, expected] of [['TERMINAL', '任务已结束,不再估算'], ['COUNT_COMPLETE', '计数已齐,等待阶段确认'], ['NO_RECENT_PROGRESS', '近期无新增进度,暂不估算'], ['SAMPLING', '正在测速,可靠样本尚未齐全'], ['UNRECOGNIZED', '估算依据待确认']]) {
|
||||
await w.setProps({ detail: { ...detail, sharedUpstream: { ...source, stageEstimate: { ...e, unavailableReason: reason } } } })
|
||||
expect(w.find('.collection-estimate strong').text()).toBe(expected)
|
||||
}
|
||||
await w.setProps({ detail: { ...detail, sharedUpstream: { ...source, progress: { ...progress, stageFinishedAt: '2026-10-13T01:02:00Z' }, stageEstimate: { ...e, unavailableReason: 'SAMPLING' } } } })
|
||||
expect(w.find('.collection-estimate strong').text()).toContain('本阶段已结束')
|
||||
expect(w.text()).not.toContain('正在测速')
|
||||
})
|
||||
it('distinguishes unobserved warehouse metadata from a confirmed missing publication', async () => {
|
||||
const w = mount(CollectionTaskProgress, { props: { task, detail } }); wrappers.push(w)
|
||||
expect(w.text()).toContain('报表可用状态待确认')
|
||||
expect(w.text()).not.toContain('尚无可用数据')
|
||||
await w.setProps({ warehouse: { lastPublishedAt: null } as WarehouseOverview })
|
||||
expect(w.text()).toContain('尚无可用数据')
|
||||
expect(w.text()).not.toContain('报表可用状态待确认')
|
||||
await w.setProps({ warehouse: { lastPublishedAt: '2026-10-13T01:00:00Z' } as WarehouseOverview })
|
||||
expect(w.text()).toContain('已发布数据')
|
||||
expect(w.text()).not.toContain('尚无可用数据')
|
||||
})
|
||||
it('withdraws ETA when expired, count identity changes, hidden, or reads fail', async () => {
|
||||
vi.useFakeTimers(); vi.setSystemTime(new Date('2026-10-13T01:02:00Z'))
|
||||
const stageEstimate: StageEstimate = { status: 'AVAILABLE', progressIdentity: 'source-attempt-2-detail-1', completed: 339, total: 2787, sampleFrom: '2026-10-13T01:00:00Z', sampleUntil: '2026-10-13T01:02:00Z', completedDelta: 60, ratePerSecond: 0.5, remainingSeconds: 4896, estimatedAt: '2026-10-13T01:02:00Z', validUntil: '2026-10-13T01:02:10Z', unavailableReason: null }
|
||||
const d = { ...detail, sharedUpstream: { ...source, stageEstimate } }
|
||||
const w = mount(CollectionTaskProgress, { props: { task, detail: d, visible: true } }); wrappers.push(w)
|
||||
expect(w.text()).toContain('约82分钟')
|
||||
await w.setProps({ stale: true }); expect(w.text()).not.toContain('约82分钟'); expect(w.text()).toContain('状态更新失败')
|
||||
await w.setProps({ stale: false, visible: false }); expect(w.text()).not.toContain('约82分钟')
|
||||
await w.setProps({ visible: true, detail: { ...d, sharedUpstream: { ...d.sharedUpstream, progress: { ...progress, completed: 340 } } } }); expect(w.text()).not.toContain('约82分钟')
|
||||
await w.setProps({ detail: d }); await vi.advanceTimersByTimeAsync(10001); expect(w.text()).toContain('估算已过期'); expect(w.text()).not.toContain('约82分钟')
|
||||
})
|
||||
it('never paints a percentage for unknown, zero, invalid, or changing denominators', async () => {
|
||||
const w = mount(CollectionProgressGauge, { props: { taskNo: source.taskNo, progress } }); wrappers.push(w)
|
||||
expect(w.find('[role="progressbar"]').attributes('aria-valuenow')).toBe('12.1')
|
||||
await w.setProps({ progress: { ...progress, total: 3000 } }); expect(w.find('[role="progressbar"]').exists()).toBe(false); expect(w.text()).toContain('正在复核')
|
||||
await w.setProps({ progress: { ...progress, attemptNo: 3, totalKnown: false } }); expect(w.find('[role="progressbar"]').exists()).toBe(false)
|
||||
await w.setProps({ progress: { ...progress, attemptNo: 4, total: 0, completed: 0 } }); expect(w.text()).toContain('本阶段数量为零'); expect(w.find('[role="progressbar"]').exists()).toBe(false)
|
||||
await w.setProps({ progress: { ...progress, attemptNo: 5, total: 1, completed: 2 } }); expect(w.find('[role="progressbar"]').exists()).toBe(false)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user