Add dark theme, richer Markdown and readable plugin results. Improve Android input evidence, response integrity and diagnostics. Unify lifecycle, guidance receipts and evidence sources across clients. Keep direct action dispatch without an added AI quota approval layer. Align version metadata at 2.2.0 and bundle Helper 1.2.2. Validation: 3063 backend tests before the version bump; 44 after. Frontend: 317 tests and production build passed.
43 lines
2.9 KiB
TypeScript
43 lines
2.9 KiB
TypeScript
import { Component, computed, input } from '@angular/core';
|
||
import { CaseTestResult } from '../../core/models/session.model';
|
||
|
||
export const TEST_RESULT_LABELS: Record<CaseTestResult['status'], string> = {
|
||
passed: '测试通过', failed: '测试不通过', inconclusive: '未验证', blocked: '测试阻塞'
|
||
};
|
||
|
||
export function testResultSourceLabel(result: CaseTestResult | null | undefined): string {
|
||
return result?.source === 'human' ? '人工确认' : result?.verification_skipped ? 'AI 判定已关闭' : 'AI确认';
|
||
}
|
||
|
||
@Component({
|
||
selector: 'app-test-result-badge',
|
||
standalone: true,
|
||
template: `<span [class]="'test-result-badge ' + status()" [class.compact]="compact()" [class.prominent]="prominent()" [title]="sourceLabel() + ':' + (result()?.reason || '执行状态与测试结论分别记录')"><span class="material-symbols-outlined" aria-hidden="true">{{icon()}}</span>{{label()}}</span>`,
|
||
styles: [`
|
||
:host{display:inline-flex;vertical-align:middle;max-width:100%}
|
||
.test-result-badge{display:inline-flex;align-items:center;gap:4px;white-space:nowrap;border-radius:6px;border:1px solid var(--app-border-control, #cbd5e1);padding:3px 8px;font-size:12px;font-weight:700;line-height:1.5;background:var(--app-surface-soft, #f1f5f9);color:var(--app-text-secondary, #475569)}
|
||
.material-symbols-outlined{font-size:16px;line-height:1}
|
||
.passed{background:#15803d;color:#fff;border-color:#15803d}
|
||
.failed{background:#b91c1c;color:#fff;border-color:#b91c1c}
|
||
.inconclusive{background:var(--app-warning-soft, #fff7ed);color:var(--app-warning-text, #9a3412);border-color:var(--app-warning-border, #fdba74)}
|
||
.blocked{background:var(--app-warning-soft, #fef3c7);color:var(--app-warning-text, #92400e);border-color:#fbbf24}
|
||
.compact{box-sizing:border-box;height:20px;padding:0 6px;font-size:11px;line-height:14px;gap:3px;border-radius:5px}
|
||
.compact .material-symbols-outlined{font-size:13px}
|
||
.prominent{padding:4px 9px;font-size:14px;gap:5px}
|
||
.prominent .material-symbols-outlined{font-size:18px}
|
||
`]
|
||
})
|
||
export class TestResultBadgeComponent {
|
||
result = input<CaseTestResult | null | undefined>();
|
||
executionStatus = input('');
|
||
compact = input(false);
|
||
prominent = input(false);
|
||
status = computed(() => this.result()?.status || (['completed', 'success', 'failed', 'cancelled', 'interrupted', 'skipped'].includes(this.executionStatus()) ? 'inconclusive' : 'pending'));
|
||
sourceLabel = computed(() => testResultSourceLabel(this.result()));
|
||
label = computed(() => this.result()?.source === 'human'
|
||
? (this.status() === 'passed' ? '人工通过' : '人工不通过')
|
||
: this.status() === 'inconclusive' && this.result()?.verification_skipped ? '未做 AI 判定'
|
||
: this.status() === 'pending' ? '待判定' : TEST_RESULT_LABELS[this.status() as CaseTestResult['status']]);
|
||
icon = computed(() => ({passed: 'check_circle', failed: 'cancel', blocked: 'block', inconclusive: 'help', pending: 'hourglass_empty'})[this.status()]);
|
||
}
|