feat(版本详情): 优化任务统计与测试轮次
This commit is contained in:
@@ -5,7 +5,12 @@ import {
|
||||
TEST_CASE_STATUS_LABEL,
|
||||
aggregateTestCaseHours,
|
||||
calcTestProgress,
|
||||
canStartNextTestRound,
|
||||
copyTestCaseToRound,
|
||||
getNextTestRoundNo,
|
||||
getRequirementDeliveryStatus,
|
||||
getTestCaseEstimateHours,
|
||||
getTestCaseRoundNo,
|
||||
normalizeTestCase,
|
||||
} from './test-case';
|
||||
import { DEFAULT_TEST_CATEGORY_ID } from './task-category';
|
||||
@@ -43,6 +48,104 @@ test('normalizeTestCase keeps existing categoryId', () => {
|
||||
assert.equal(tc.categoryId, 'cat-test-api');
|
||||
});
|
||||
|
||||
test('normalizeTestCase backfills missing roundNo to the first test round', () => {
|
||||
const tc = normalizeTestCase({
|
||||
id: 'tc-1',
|
||||
caseNo: 'TC-001',
|
||||
versionId: 'v1',
|
||||
title: '第一轮用例',
|
||||
priority: 'P2',
|
||||
status: 'pending',
|
||||
categoryId: 'cat-test-api',
|
||||
createdBy: 'tester',
|
||||
createdAt: '2026-06-25',
|
||||
updatedAt: '2026-06-25',
|
||||
} as any);
|
||||
|
||||
assert.equal(tc.roundNo, 1);
|
||||
assert.equal(getTestCaseRoundNo(tc), 1);
|
||||
});
|
||||
|
||||
test('getNextTestRoundNo returns one more than the current max round', () => {
|
||||
const cases = [
|
||||
normalizeTestCase({ id: 'tc-1', caseNo: 'TC-001', versionId: 'v1', title: 'R1', priority: 'P2', status: 'passed', categoryId: 'cat-test-api', roundNo: 1, createdBy: 'tester', createdAt: '2026-06-25', updatedAt: '2026-06-25' } as any),
|
||||
normalizeTestCase({ id: 'tc-2', caseNo: 'TC-002', versionId: 'v1', title: 'R3', priority: 'P2', status: 'passed', categoryId: 'cat-test-api', roundNo: 3, createdBy: 'tester', createdAt: '2026-06-25', updatedAt: '2026-06-25' } as any),
|
||||
];
|
||||
|
||||
assert.equal(getNextTestRoundNo(cases), 4);
|
||||
});
|
||||
|
||||
test('canStartNextTestRound requires latest round cases all tested', () => {
|
||||
assert.equal(canStartNextTestRound([]), false);
|
||||
assert.equal(canStartNextTestRound([
|
||||
normalizeTestCase({ id: 'tc-1', caseNo: 'TC-001', versionId: 'v1', title: '通过', priority: 'P2', status: 'passed', categoryId: 'cat-test-api', roundNo: 1, createdBy: 'tester', createdAt: '2026-06-25', updatedAt: '2026-06-25' } as any),
|
||||
normalizeTestCase({ id: 'tc-2', caseNo: 'TC-002', versionId: 'v1', title: '待测', priority: 'P2', status: 'pending', categoryId: 'cat-test-api', roundNo: 1, createdBy: 'tester', createdAt: '2026-06-25', updatedAt: '2026-06-25' } as any),
|
||||
]), false);
|
||||
assert.equal(canStartNextTestRound([
|
||||
normalizeTestCase({ id: 'tc-1', caseNo: 'TC-001', versionId: 'v1', title: '通过', priority: 'P2', status: 'passed', categoryId: 'cat-test-api', roundNo: 1, createdBy: 'tester', createdAt: '2026-06-25', updatedAt: '2026-06-25' } as any),
|
||||
normalizeTestCase({ id: 'tc-2', caseNo: 'TC-002', versionId: 'v1', title: '不通过', priority: 'P2', status: 'failed', categoryId: 'cat-test-api', roundNo: 1, createdBy: 'tester', createdAt: '2026-06-25', updatedAt: '2026-06-25' } as any),
|
||||
]), true);
|
||||
assert.equal(canStartNextTestRound([
|
||||
normalizeTestCase({ id: 'tc-1', caseNo: 'TC-001', versionId: 'v1', title: '首轮通过', priority: 'P2', status: 'passed', categoryId: 'cat-test-api', roundNo: 1, createdBy: 'tester', createdAt: '2026-06-25', updatedAt: '2026-06-25' } as any),
|
||||
normalizeTestCase({ id: 'tc-2', caseNo: 'TC-002', versionId: 'v1', title: '二轮待测', priority: 'P2', status: 'running', categoryId: 'cat-test-api', roundNo: 2, createdBy: 'tester', createdAt: '2026-06-25', updatedAt: '2026-06-25' } as any),
|
||||
]), false);
|
||||
});
|
||||
|
||||
test('getRequirementDeliveryStatus marks a requirement submitted only when all its dev tasks are submitted', () => {
|
||||
const tasks = [
|
||||
{ requirementId: 'req-1', status: 'submitted' },
|
||||
{ requirementId: 'req-1', status: 'submitted' },
|
||||
{ requirementId: 'req-2', status: 'testing' },
|
||||
] as any;
|
||||
|
||||
assert.equal(getRequirementDeliveryStatus('req-1', tasks), 'submitted');
|
||||
assert.equal(getRequirementDeliveryStatus('req-2', tasks), 'pending');
|
||||
assert.equal(getRequirementDeliveryStatus('req-empty', tasks), 'pending');
|
||||
});
|
||||
|
||||
test('copyTestCaseToRound preserves estimates and references but clears execution state', () => {
|
||||
const source = normalizeTestCase({
|
||||
id: 'tc-source',
|
||||
caseNo: 'TC-001',
|
||||
versionId: 'v1',
|
||||
requirementId: 'req-1',
|
||||
title: '登录正常',
|
||||
description: '步骤',
|
||||
priority: 'P1',
|
||||
status: 'passed',
|
||||
categoryId: 'cat-test-functional',
|
||||
estimateHours: 0.75,
|
||||
aiEstimateHours: 0.25,
|
||||
assigneeId: 'QA',
|
||||
startedAt: '2026-06-25T01:00:00.000Z',
|
||||
completedAt: '2026-06-25T02:00:00.000Z',
|
||||
executedAt: '2026-06-25T02:00:00.000Z',
|
||||
failReason: 'old fail',
|
||||
blockReason: 'old block',
|
||||
references: [{ type: 'requirement', id: 'req-1', label: 'REQ-001 登录' }],
|
||||
aiDraft: true,
|
||||
aiDraftAt: '2026-06-25T00:00:00.000Z',
|
||||
roundNo: 1,
|
||||
createdBy: 'AI',
|
||||
createdAt: '2026-06-25',
|
||||
updatedAt: '2026-06-25',
|
||||
} as any);
|
||||
|
||||
const copied = copyTestCaseToRound(source, 2, 'tester');
|
||||
|
||||
assert.equal(copied.roundNo, 2);
|
||||
assert.equal(copied.sourceCaseId, 'tc-source');
|
||||
assert.equal(copied.aiEstimateHours, 0.25);
|
||||
assert.equal(copied.estimateHours, 0.75);
|
||||
assert.deepEqual(copied.references, source.references);
|
||||
assert.equal(copied.startedAt, undefined);
|
||||
assert.equal(copied.completedAt, undefined);
|
||||
assert.equal(copied.executedAt, undefined);
|
||||
assert.equal(copied.failReason, undefined);
|
||||
assert.equal(copied.blockReason, undefined);
|
||||
assert.equal(copied.createdBy, 'tester');
|
||||
});
|
||||
|
||||
test('normalizeTestCase keeps missing executor estimate empty', () => {
|
||||
const tc = normalizeTestCase({
|
||||
id: 'tc-1',
|
||||
@@ -137,6 +240,91 @@ test('calcTestProgress uses estimate-weighted completion', () => {
|
||||
assert.equal(progress.completionRate, 75);
|
||||
});
|
||||
|
||||
test('calcTestProgress calculates pass rate from passed cases that never had effective bugs', () => {
|
||||
const progress = calcTestProgress([
|
||||
normalizeTestCase({
|
||||
id: 'tc-clean',
|
||||
caseNo: 'TC-001',
|
||||
versionId: 'v1',
|
||||
title: '无 Bug 通过',
|
||||
priority: 'P2',
|
||||
status: 'passed',
|
||||
categoryId: 'cat-test-functional',
|
||||
createdBy: 'tester',
|
||||
createdAt: '2026-06-25',
|
||||
updatedAt: '2026-06-25',
|
||||
} as any),
|
||||
normalizeTestCase({
|
||||
id: 'tc-bug-fixed',
|
||||
caseNo: 'TC-002',
|
||||
versionId: 'v1',
|
||||
title: '提过 Bug 后通过',
|
||||
priority: 'P2',
|
||||
status: 'passed',
|
||||
categoryId: 'cat-test-functional',
|
||||
createdBy: 'tester',
|
||||
createdAt: '2026-06-25',
|
||||
updatedAt: '2026-06-25',
|
||||
} as any),
|
||||
normalizeTestCase({
|
||||
id: 'tc-failed',
|
||||
caseNo: 'TC-003',
|
||||
versionId: 'v1',
|
||||
title: '不通过',
|
||||
priority: 'P2',
|
||||
status: 'failed',
|
||||
categoryId: 'cat-test-functional',
|
||||
createdBy: 'tester',
|
||||
createdAt: '2026-06-25',
|
||||
updatedAt: '2026-06-25',
|
||||
} as any),
|
||||
normalizeTestCase({
|
||||
id: 'tc-running-with-bug',
|
||||
caseNo: 'TC-004',
|
||||
versionId: 'v1',
|
||||
title: '测试中已提 Bug',
|
||||
priority: 'P2',
|
||||
status: 'running',
|
||||
categoryId: 'cat-test-functional',
|
||||
createdBy: 'tester',
|
||||
createdAt: '2026-06-25',
|
||||
updatedAt: '2026-06-25',
|
||||
} as any),
|
||||
normalizeTestCase({
|
||||
id: 'tc-rejected-only',
|
||||
caseNo: 'TC-005',
|
||||
versionId: 'v1',
|
||||
title: '仅有驳回 Bug',
|
||||
priority: 'P2',
|
||||
status: 'passed',
|
||||
categoryId: 'cat-test-functional',
|
||||
createdBy: 'tester',
|
||||
createdAt: '2026-06-25',
|
||||
updatedAt: '2026-06-25',
|
||||
} as any),
|
||||
normalizeTestCase({
|
||||
id: 'tc-pending',
|
||||
caseNo: 'TC-006',
|
||||
versionId: 'v1',
|
||||
title: '未执行',
|
||||
priority: 'P2',
|
||||
status: 'pending',
|
||||
categoryId: 'cat-test-functional',
|
||||
createdBy: 'tester',
|
||||
createdAt: '2026-06-25',
|
||||
updatedAt: '2026-06-25',
|
||||
} as any),
|
||||
], [
|
||||
{ testCaseId: 'tc-bug-fixed', status: 'closed' },
|
||||
{ testCaseId: 'tc-running-with-bug', status: 'open' },
|
||||
{ testCaseId: 'tc-rejected-only', status: 'rejected' },
|
||||
]);
|
||||
|
||||
assert.equal(progress.executed, 5);
|
||||
assert.equal(progress.passed, 3);
|
||||
assert.equal(progress.passRate, 40);
|
||||
});
|
||||
|
||||
test('aggregateTestCaseHours returns estimate and actual totals', () => {
|
||||
const hours = aggregateTestCaseHours([
|
||||
normalizeTestCase({
|
||||
|
||||
Reference in New Issue
Block a user