核心改动: - TestCase 主归属 versionId,requirementId 改为可选语义标签 - Bug 新增 versionId(直接挂版本)+ requirementId(可选追溯) - 新增 calcVersionExecutionStatus() 版本执行态推导函数 规则:DevTask全部提测→已提测,TestCase全部通过+Bug全关→可发布 - 概览页展示推导出的执行态标签 - BugTab 改为直接 versionId 查询(不再走 testCase→req 链路) - 统计卡片"参与人员"替换为"未关闭Bug" 心智模型: 版本(执行线)→ DevTask/TestCase/Bug 需求(语义线)→ 解释为什么做,不驱动流程 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
71 lines
2.4 KiB
TypeScript
71 lines
2.4 KiB
TypeScript
import type { Priority } from './derive';
|
|
|
|
export type TestCaseStatus = 'pending' | 'running' | 'passed' | 'failed' | 'blocked';
|
|
|
|
export interface TestCase {
|
|
id: string;
|
|
caseNo: string;
|
|
versionId: string;
|
|
requirementId?: string;
|
|
title: string;
|
|
description?: string;
|
|
priority: Priority;
|
|
assigneeId?: string;
|
|
status: TestCaseStatus;
|
|
executedAt?: string;
|
|
executedBy?: string;
|
|
failReason?: string;
|
|
blockReason?: string;
|
|
createdBy: string;
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export const TEST_CASE_STATUS_LABEL: Record<TestCaseStatus, string> = {
|
|
pending: '待执行',
|
|
running: '执行中',
|
|
passed: '通过',
|
|
failed: '失败',
|
|
blocked: '阻塞',
|
|
};
|
|
|
|
export const TEST_CASE_STATUS_COLOR: Record<TestCaseStatus, string> = {
|
|
pending: 'bg-zinc-100 text-zinc-600',
|
|
running: 'bg-blue-50 text-blue-600',
|
|
passed: 'bg-emerald-50 text-emerald-600',
|
|
failed: 'bg-red-50 text-red-600',
|
|
blocked: 'bg-orange-50 text-orange-600',
|
|
};
|
|
|
|
export const TC_ALLOWED_TRANSITIONS: Record<TestCaseStatus, TestCaseStatus[]> = {
|
|
pending: ['running'],
|
|
running: ['passed', 'failed', 'blocked'],
|
|
passed: ['running'],
|
|
failed: ['running'],
|
|
blocked: ['running'],
|
|
};
|
|
|
|
export function canTcTransition(from: TestCaseStatus, to: TestCaseStatus): boolean {
|
|
return TC_ALLOWED_TRANSITIONS[from].includes(to);
|
|
}
|
|
|
|
export function generateCaseNo(existingCases: TestCase[]): string {
|
|
const maxNum = existingCases.reduce((max, c) => {
|
|
const num = parseInt(c.caseNo.replace('TC-', ''), 10);
|
|
return isNaN(num) ? max : Math.max(max, num);
|
|
}, 0);
|
|
return `TC-${String(maxNum + 1).padStart(3, '0')}`;
|
|
}
|
|
|
|
export function calcTestProgress(cases: TestCase[]): { total: number; executed: number; passed: number; failed: number; blocked: number; passRate: number; completionRate: number } {
|
|
const total = cases.length;
|
|
if (total === 0) return { total: 0, executed: 0, passed: 0, failed: 0, blocked: 0, passRate: 0, completionRate: 0 };
|
|
const passed = cases.filter((c) => c.status === 'passed').length;
|
|
const failed = cases.filter((c) => c.status === 'failed').length;
|
|
const blocked = cases.filter((c) => c.status === 'blocked').length;
|
|
const executed = passed + failed + blocked;
|
|
const passRate = (passed + failed) > 0 ? Math.round((passed / (passed + failed)) * 100) : 0;
|
|
const completionRate = Math.round((executed / total) * 100);
|
|
return { total, executed, passed, failed, blocked, passRate, completionRate };
|
|
}
|