测试:覆盖批次授权并发状态
This commit is contained in:
@@ -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, '', '当前详情失败必须是单项状态,不得升级为列表失败')
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user