2026-07-13 11:04:55 +08:00
|
|
|
import assert from 'node:assert/strict'
|
|
|
|
|
import { readFile } from 'node:fs/promises'
|
|
|
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
|
import path from 'node:path'
|
|
|
|
|
import { parse as parseSfc } from '@vue/compiler-sfc'
|
|
|
|
|
|
|
|
|
|
const scriptDir = path.dirname(fileURLToPath(import.meta.url))
|
|
|
|
|
const sourceRoot = path.resolve(scriptDir, '../src')
|
2026-07-23 15:10:13 +08:00
|
|
|
const [detailSource, listSource, routerSource, apiSource, typesSource] = await Promise.all([
|
2026-07-13 11:04:55 +08:00
|
|
|
readFile(path.join(sourceRoot, 'views/data-process/DataProcessDetailView.vue'), 'utf8'),
|
|
|
|
|
readFile(path.join(sourceRoot, 'views/data-process/DataProcessListView.vue'), 'utf8'),
|
|
|
|
|
readFile(path.join(sourceRoot, 'router/index.ts'), 'utf8'),
|
2026-07-23 15:10:13 +08:00
|
|
|
readFile(path.join(sourceRoot, 'api/modules/dataProcess.ts'), 'utf8'),
|
|
|
|
|
readFile(path.join(sourceRoot, 'types/dataProcess.ts'), 'utf8'),
|
2026-07-13 11:04:55 +08:00
|
|
|
])
|
|
|
|
|
|
|
|
|
|
const { descriptor, errors } = parseSfc(detailSource, { filename: 'DataProcessDetailView.vue' })
|
|
|
|
|
assert.equal(errors.length, 0, `数据处理详情页无法解析:${errors[0]}`)
|
|
|
|
|
assert.ok(descriptor.template?.content.trim(), '数据处理详情页缺少可渲染模板')
|
|
|
|
|
|
|
|
|
|
assert.match(routerSource, /path:\s*['"]data-process\/:id['"]/, '缺少数据处理详情动态路由')
|
|
|
|
|
assert.match(routerSource, /name:\s*['"]data-process-detail['"]/, '数据处理详情路由缺少名称')
|
|
|
|
|
assert.match(routerSource, /DataProcessDetailView\.vue/, '数据处理详情路由未加载详情页面')
|
|
|
|
|
assert.match(routerSource, /title:\s*['"]数据处理详情['"]/, '数据处理详情路由标题不正确')
|
|
|
|
|
|
|
|
|
|
assert.match(listSource, /name:\s*['"]data-process-detail['"]/, '列表详情按钮未使用详情命名路由')
|
|
|
|
|
assert.match(listSource, /params:\s*\{\s*id:\s*taskId\s*\}/, '列表详情按钮未传递任务 ID')
|
|
|
|
|
assert.doesNotMatch(listSource, /查看详情功能开发中/, '详情按钮仍保留开发中提示')
|
|
|
|
|
|
|
|
|
|
for (const requiredCopy of [
|
|
|
|
|
'处理耗时',
|
|
|
|
|
'开始时间',
|
|
|
|
|
'完成时间',
|
|
|
|
|
'输入数据',
|
|
|
|
|
'输出结果',
|
|
|
|
|
'处理统计',
|
|
|
|
|
'处理配置',
|
|
|
|
|
'结果明细',
|
|
|
|
|
]) {
|
|
|
|
|
assert.match(detailSource, new RegExp(requiredCopy), `详情页缺少必要信息:${requiredCopy}`)
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-23 15:10:13 +08:00
|
|
|
assert.match(typesSource, /export type DataProcessStatus = 'pending' \| 'running' \| 'completed' \| 'failed' \| 'stopped'/, '任务状态契约不完整')
|
|
|
|
|
assert.match(detailSource, /getDataProcessTask\(taskId\.value\)/, '详情页没有通过真实 API 加载任务')
|
|
|
|
|
assert.match(detailSource, /getDataProcessResults\(taskId\.value,\s*\{[\s\S]*?page:[\s\S]*?page_size:[\s\S]*?keyword:[\s\S]*?status:/, '结果列表没有接入服务端分页、搜索和状态筛选')
|
|
|
|
|
assert.match(detailSource, /getDataProcessProgress\(taskId\.value\)/, '运行中任务没有查询真实进度')
|
|
|
|
|
assert.match(detailSource, /usePolling\(refreshRuntime,\s*3000/, '运行中任务没有启用进度轮询')
|
|
|
|
|
assert.match(detailSource, /:data="results"/, '结果表格未绑定服务端结果数据')
|
2026-07-13 11:04:55 +08:00
|
|
|
assert.match(detailSource, /v-model="keyword"/, '结果明细缺少搜索能力')
|
|
|
|
|
assert.match(detailSource, /v-model="statusFilter"/, '结果明细缺少状态筛选')
|
2026-07-23 15:10:13 +08:00
|
|
|
assert.match(detailSource, /updateDataProcessResult\(taskId\.value,[\s\S]*?expected_updated_at:/, '结果编辑没有携带并发版本时间')
|
|
|
|
|
assert.match(detailSource, /restoreDataProcessResult\(taskId\.value,\s*result\.id\)/, '结果恢复没有调用真实 API')
|
|
|
|
|
assert.match(detailSource, /publishDataProcess\(taskId\.value,/, '发布数据集没有调用真实 API')
|
2026-07-25 17:04:21 +08:00
|
|
|
assert.match(detailSource, /path: '\/dataset', query: \{ tab: 'task', task_id: taskId\.value \}/, '发布成功后未进入数据任务列表')
|
|
|
|
|
assert.match(detailSource, /published\.datasets \|\| published\.output_datasets/, '发布成功后未读取三个独立数据集')
|
|
|
|
|
assert.match(detailSource, /v-for="dataset in outputDatasets"/, '任务详情未展示三个输出数据集入口')
|
|
|
|
|
assert.match(detailSource, /将发布三个独立数据集/, '发布说明仍未明确生成三个独立数据集')
|
|
|
|
|
assert.match(detailSource, /outputDatasetBaseName\.value/, '重新同步时未使用单一基础名称')
|
|
|
|
|
assert.doesNotMatch(detailSource, /label="数据集类型"/, '发布三个数据集时不应再选择单一数据集类型')
|
2026-07-23 15:10:13 +08:00
|
|
|
assert.match(detailSource, /无法加载数据处理任务/, '未知任务或加载失败缺少明确错误状态')
|
|
|
|
|
assert.match(detailSource, /结果明细加载失败/, '结果加载失败缺少明确错误状态')
|
2026-07-13 11:04:55 +08:00
|
|
|
assert.match(detailSource, /width:\s*100%/, '详情页没有铺满内容区域')
|
|
|
|
|
assert.doesNotMatch(detailSource, /^\s*max-width:\s*\d+px/m, '详情页不应使用固定最大宽度')
|
2026-07-23 15:10:13 +08:00
|
|
|
assert.match(detailSource, /!\/\(\?:password\|secret\|token\|api_key\)\/i\.test\(key\)/, '处理配置没有过滤敏感凭据字段')
|
2026-07-24 16:30:29 +08:00
|
|
|
assert.match(detailSource, /key !== 'generation_model_snapshot'/, '处理配置仍直接展示内部模型快照')
|
|
|
|
|
assert.match(detailSource, /preprocessOptionLabelMap/, '处理配置没有把预处理内部枚举转换为中文')
|
|
|
|
|
assert.match(detailSource, /const parsed = typeof value === 'number' \? value : Number\(value\)/, '详情页不兼容 PostgreSQL 数字字符串')
|
|
|
|
|
assert.match(detailSource, /new Date\(startTime\.value\)\.getTime\(\)/, '详情页没有在后端耗时缺失时按开始、完成时间回算')
|
|
|
|
|
assert.match(detailSource, /outputCount \+ numeric\(detail\.value\?\.filtered_count\)/, '结果保留率没有使用输出和过滤结果的同口径分母')
|
|
|
|
|
assert.doesNotMatch(detailSource, /numeric\(detail\.value\?\.output_count\) \/ inputCount/, '结果保留率仍在用输出结果除以源文件记录数')
|
|
|
|
|
assert.match(detailSource, /:percentage="retentionPercentage"/, '结果保留率进度条仍绑定任务执行进度')
|
|
|
|
|
assert.match(detailSource, /preview_count/, '非结构化任务没有展示实际输入切片数')
|
|
|
|
|
assert.match(detailSource, /const configExpanded = ref\(false\)/, '处理配置没有默认收起')
|
|
|
|
|
assert.match(detailSource, /:aria-expanded="configExpanded"/, '处理配置折叠按钮缺少无障碍状态')
|
|
|
|
|
assert.match(detailSource, /<el-collapse-transition>[\s\S]*?v-show="configExpanded"/, '处理配置没有折叠过渡或内容状态')
|
2026-07-23 15:10:13 +08:00
|
|
|
assert.doesNotMatch(detailSource, /const (?:detailMap|completedResults)\b|TODO: 接入真实接口/, '详情页仍包含本地 Mock 数据')
|
|
|
|
|
|
|
|
|
|
for (const apiName of [
|
|
|
|
|
'getDataProcessTask',
|
|
|
|
|
'getDataProcessProgress',
|
|
|
|
|
'getDataProcessResults',
|
|
|
|
|
'updateDataProcessResult',
|
|
|
|
|
'restoreDataProcessResult',
|
|
|
|
|
'publishDataProcess',
|
|
|
|
|
]) {
|
|
|
|
|
assert.match(
|
|
|
|
|
apiSource,
|
|
|
|
|
new RegExp(`export (?:const|(?:async )?function) ${apiName}\\b`),
|
|
|
|
|
`API 模块缺少 ${apiName}`,
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
assert.match(apiSource, /keyword\?: string; status\?: string; split\?: string/, '结果列表 API 缺少服务端筛选参数')
|
|
|
|
|
assert.match(apiSource, /\/results\/\$\{encodeURIComponent\(resultId\)\}/, '结果资源路径没有安全编码结果 ID')
|
|
|
|
|
assert.match(apiSource, /`\/data-process\/\$\{encodeURIComponent\(taskId\)\}\/publish`/, '发布 API 路径不正确')
|
2026-07-13 11:04:55 +08:00
|
|
|
|
2026-07-23 15:10:13 +08:00
|
|
|
console.log('数据处理任务详情真实 API 回归检查通过')
|