测试:覆盖批次授权并发状态

This commit is contained in:
焦龙言
2026-09-23 17:45:54 +08:00
parent a5cc91a92a
commit 4706c4afeb
@@ -5,6 +5,7 @@ import { test } from 'node:test'
import { fileURLToPath } from 'node:url'
import path from 'node:path'
import { promisify } from 'node:util'
import * as vm from 'node:vm'
import * as testBatchContext from '../src/utils/testBatchContext.js'
const frontendRoot = fileURLToPath(new URL('..', import.meta.url))
@@ -778,6 +779,85 @@ function extractNamedFunction(source, functionName) {
return source.slice(match.index, bodyEnd + 1)
}
function extractScriptSetup(source) {
const match = /<script\s+setup(?:\s+[^>]*)?>([\s\S]*?)<\/script>/.exec(source)
assert.ok(match, '缺少 script setup')
return match[1].replace(/^import\s+.*$/gm, '')
}
function createDeferred() {
let resolve
let reject
const promise = new Promise((resolvePromise, rejectPromise) => {
resolve = resolvePromise
reject = rejectPromise
})
return { promise, resolve, reject }
}
async function flushPromises() {
await Promise.resolve()
await Promise.resolve()
await Promise.resolve()
await Promise.resolve()
}
function createTestBatchPageHarness(pageSource, api = {}) {
const noopRequest = () => Promise.reject(new Error('未配置的测试请求'))
const ref = value => ({ value })
const context = vm.createContext({
Array,
Boolean,
Error,
JSON,
Number,
Object,
Promise,
Set,
String,
console,
ref,
reactive: value => value,
computed: getter => ({ get value() { return getter() } }),
getCurrentInstance: () => ({ proxy: { $modal: { confirm: () => Promise.resolve(), msgError: () => {}, msgSuccess: () => {} } } }),
onBeforeUnmount: () => {},
onMounted: () => {},
window: { innerWidth: 1280, addEventListener: () => {}, removeEventListener: () => {} },
useUserStore: () => ({ admin: true }),
useTestBatchStore: () => ({ currentBatchId: undefined, leaveTestMode: () => {}, refreshOptions: () => Promise.resolve() }),
addTestBatch: api.addTestBatch || noopRequest,
assignTestBatchUsers: api.assignTestBatchUsers || noopRequest,
closeTestBatch: api.closeTestBatch || noopRequest,
getTestBatch: api.getTestBatch || noopRequest,
listTestBatches: api.listTestBatches || noopRequest,
previewTestBatchPurge: api.previewTestBatchPurge || noopRequest,
purgeTestBatch: api.purgeTestBatch || noopRequest,
reopenTestBatch: api.reopenTestBatch || noopRequest,
updateTestBatch: api.updateTestBatch || noopRequest
})
const exposure = `
globalThis.__testBatchHarness = {
openAssign,
submitAssign,
getList,
hydrateParticipantCounts,
state: {
assignBatch,
assignError,
assignLoading,
assignVisible,
assignedUserIds,
batchList,
detailLoaded,
loadError,
loading
}
}
`
vm.runInContext(`${extractScriptSetup(pageSource)}\n${exposure}`, context, { filename: 'src/views/system/testBatch/index.vue' })
return context.__testBatchHarness
}
function extractPromiseCallback(functionSource, methodName, occurrence = 0) {
const sanitized = sanitizeSource(functionSource, { allowTemplateInterpolation: true })
const marker = `.${methodName}(`
@@ -2543,3 +2623,141 @@ test('测试批次授权、清理和对话框异步状态拒绝错误目标与
assert.match(adoption, /max-height:\s*90vh/)
assert.match(adoption, /overflow-y:\s*auto/)
})
test('测试批次管理页真实授权详情 success、failure 与 finally 不得覆盖新会话', async () => {
const management = await source('src/views/system/testBatch/index.vue')
const detailRequests = []
const page = createTestBatchPageHarness(management, {
getTestBatch: batchId => {
const request = createDeferred()
detailRequests.push({ batchId, request })
return request.promise
}
})
page.openAssign({ batchId: 1 })
page.openAssign({ batchId: 2 })
detailRequests[0].request.resolve({ data: { batchId: 1, batchName: '批次 A', userIds: [101] } })
await flushPromises()
assert.equal(page.state.assignBatch.value, null, '过期 success 不得写入新批次详情')
assert.equal(page.state.assignedUserIds.value, '', '过期 success 不得写入新批次用户')
assert.equal(page.state.detailLoaded.value, false, '过期 success 不得解锁保存')
assert.equal(page.state.assignLoading.value, true, '过期 finally 不得结束新会话 loading')
detailRequests[1].request.resolve({ data: { batchId: 2, batchName: '批次 B', userIds: [202] } })
await flushPromises()
assert.deepEqual(page.state.assignBatch.value, { batchId: 2, batchName: '批次 B', userIds: [202] })
assert.equal(page.state.assignedUserIds.value, '202')
assert.equal(page.state.detailLoaded.value, true)
assert.equal(page.state.assignLoading.value, false)
const failedRequests = []
const failedPage = createTestBatchPageHarness(management, {
getTestBatch: batchId => {
const request = createDeferred()
failedRequests.push({ batchId, request })
return request.promise
}
})
failedPage.openAssign({ batchId: 3 })
failedPage.openAssign({ batchId: 4 })
failedRequests[1].request.resolve({ data: { batchId: 4, batchName: '批次 B', userIds: [404] } })
await flushPromises()
failedRequests[0].request.reject(new Error('A 详情失败'))
await flushPromises()
assert.deepEqual(failedPage.state.assignBatch.value, { batchId: 4, batchName: '批次 B', userIds: [404] }, '过期 failure 不得清空已加载的新会话详情')
assert.equal(failedPage.state.assignedUserIds.value, '404', '过期 failure 不得改写新批次输入')
assert.equal(failedPage.state.detailLoaded.value, true)
assert.equal(failedPage.state.assignLoading.value, false)
assert.equal(failedPage.state.assignError.value, '', '过期 failure 不得展示旧批次错误')
})
test('测试批次管理页当前授权详情失败保持禁用并拒绝空数组提交', async () => {
const management = await source('src/views/system/testBatch/index.vue')
const detailRequest = createDeferred()
const assignments = []
const page = createTestBatchPageHarness(management, {
getTestBatch: () => detailRequest.promise,
assignTestBatchUsers: (batchId, payload) => {
assignments.push({ batchId, payload })
return Promise.resolve({ msg: '不应调用' })
}
})
page.openAssign({ batchId: 5 })
detailRequest.reject(new Error('详情失败'))
await flushPromises()
assert.equal(page.state.assignBatch.value, null)
assert.equal(page.state.assignedUserIds.value, '')
assert.equal(page.state.detailLoaded.value, false, '详情失败后不得解锁保存')
assert.equal(page.state.assignLoading.value, false)
assert.match(page.state.assignError.value, /详情加载失败/)
page.submitAssign()
assert.deepEqual(assignments, [], '详情未成功加载时不得提交移除全部用户的空数组')
})
test('测试批次管理页真实 hydration 拒绝旧版本回写并保留当前失败状态', async () => {
const management = await source('src/views/system/testBatch/index.vue')
const listRequests = []
const detailRequests = []
const page = createTestBatchPageHarness(management, {
listTestBatches: () => {
const request = createDeferred()
listRequests.push(request)
return request.promise
},
getTestBatch: batchId => {
const request = createDeferred()
detailRequests.push({ batchId, request })
return request.promise
}
})
page.getList()
listRequests[0].resolve({ rows: [{ batchId: 7, batchName: '旧列表' }], total: 1 })
await flushPromises()
page.getList()
listRequests[1].resolve({ rows: [{ batchId: 7, batchName: '新列表' }], total: 1 })
await flushPromises()
detailRequests[0].request.resolve({ data: { batchId: 7, userIds: [1, 2] } })
await flushPromises()
assert.equal(page.state.batchList.value[0].batchName, '新列表')
assert.equal(page.state.batchList.value[0].participantState, 'loading', '旧 success 不得写入新列表同 ID 行')
assert.equal(page.state.batchList.value[0].participantCount, undefined)
const failedListRequests = []
const failedDetailRequests = []
const failedPage = createTestBatchPageHarness(management, {
listTestBatches: () => {
const request = createDeferred()
failedListRequests.push(request)
return request.promise
},
getTestBatch: batchId => {
const request = createDeferred()
failedDetailRequests.push({ batchId, request })
return request.promise
}
})
failedPage.getList()
failedListRequests[0].resolve({ rows: [{ batchId: 8, batchName: '旧列表' }], total: 1 })
await flushPromises()
failedPage.getList()
failedListRequests[1].resolve({ rows: [{ batchId: 8, batchName: '新列表' }], total: 1 })
await flushPromises()
failedDetailRequests[0].request.reject(new Error('旧详情失败'))
await flushPromises()
assert.equal(failedPage.state.batchList.value[0].participantState, 'loading', '旧 failure 不得把新列表同 ID 行标为失败')
failedDetailRequests[1].request.reject(new Error('当前详情失败'))
await flushPromises()
assert.equal(failedPage.state.batchList.value.length, 1, '当前详情失败不得清空整个列表')
assert.equal(failedPage.state.batchList.value[0].participantState, 'error')
assert.equal(failedPage.state.batchList.value[0].participantCount, undefined)
assert.equal(failedPage.state.loadError.value, '', '当前详情失败必须是单项状态,不得升级为列表失败')
})