





















@@ -37,7 +37,7 @@ type QaCoverageScenarioReference = QaCoverageScenarioSummary & {
3737intent: QaCoverageIntent;
3838};
393940-type QaCoverageFeatureSummary = {
40+type QaCoverageIdSummary = {
4141id: string;
4242scenarios: QaCoverageScenarioReference[];
4343};
@@ -55,11 +55,11 @@ type QaCoverageInventory = {
5555coverageIdCount: number;
5656primaryCoverageIdCount: number;
5757secondaryCoverageIdCount: number;
58-features: QaCoverageFeatureSummary[];
59-overlappingCoverage: QaCoverageFeatureSummary[];
58+coverageIds: QaCoverageIdSummary[];
59+overlappingCoverage: QaCoverageIdSummary[];
6060missingCoverage: QaCoverageScenarioSummary[];
61-byTheme: Record<string, QaCoverageFeatureSummary[]>;
62-bySurface: Record<string, QaCoverageFeatureSummary[]>;
61+byTheme: Record<string, QaCoverageIdSummary[]>;
62+bySurface: Record<string, QaCoverageIdSummary[]>;
6363scenarioPacks: QaCoverageScenarioPackSummary[];
6464liveTransportLanes: LiveTransportCoverageLaneSummary[];
6565scorecardTaxonomy: QaScorecardTaxonomyReport;
@@ -166,8 +166,8 @@ export function findQaScenarioMatches(
166166.toSorted((left, right) => left.id.localeCompare(right.id));
167167}
168168169-function sortFeatures(features: readonly QaCoverageFeatureSummary[]) {
170-return features.toSorted((left, right) => left.id.localeCompare(right.id));
169+function sortCoverageIds(coverageIds: readonly QaCoverageIdSummary[]) {
170+return coverageIds.toSorted((left, right) => left.id.localeCompare(right.id));
171171}
172172173173function buildScenarioPackSummaries(
@@ -203,24 +203,24 @@ function buildScenarioPackSummaries(
203203export function buildQaCoverageInventory(
204204scenarios: readonly QaSeedScenarioWithSource[],
205205): QaCoverageInventory {
206-const byCoverageId = new Map<string, QaCoverageFeatureSummary>();
206+const byCoverageId = new Map<string, QaCoverageIdSummary>();
207207const primaryCoverageIds = new Set<string>();
208208const secondaryCoverageIds = new Set<string>();
209209const missingCoverage: QaCoverageScenarioSummary[] = [];
210210211-const addCoverage = (
211+const addFeatureCoverage = (
212212scenario: QaSeedScenarioWithSource,
213213coverageIds: readonly string[] | undefined,
214214intent: QaCoverageIntent,
215215) => {
216216const summary = summarizeScenario(scenario);
217217for (const coverageId of coverageIds ?? []) {
218-const feature = byCoverageId.get(coverageId) ?? {
218+const coverage = byCoverageId.get(coverageId) ?? {
219219id: coverageId,
220220scenarios: [],
221221};
222-feature.scenarios.push({ ...summary, intent });
223-byCoverageId.set(coverageId, feature);
222+coverage.scenarios.push({ ...summary, intent });
223+byCoverageId.set(coverageId, coverage);
224224if (intent === "primary") {
225225primaryCoverageIds.add(coverageId);
226226} else {
@@ -234,40 +234,40 @@ export function buildQaCoverageInventory(
234234missingCoverage.push(summarizeScenario(scenario));
235235continue;
236236}
237-addCoverage(scenario, scenario.coverage.primary, "primary");
238-addCoverage(scenario, scenario.coverage.secondary, "secondary");
237+addFeatureCoverage(scenario, scenario.coverage.primary, "primary");
238+addFeatureCoverage(scenario, scenario.coverage.secondary, "secondary");
239239}
240240241-const features = sortFeatures([...byCoverageId.values()]);
242-const overlappingCoverage = features.filter((feature) => feature.scenarios.length > 1);
243-const byTheme: Record<string, QaCoverageFeatureSummary[]> = {};
244-const bySurface: Record<string, QaCoverageFeatureSummary[]> = {};
241+const coverageIds = sortCoverageIds([...byCoverageId.values()]);
242+const overlappingCoverage = coverageIds.filter((coverage) => coverage.scenarios.length > 1);
243+const byTheme: Record<string, QaCoverageIdSummary[]> = {};
244+const bySurface: Record<string, QaCoverageIdSummary[]> = {};
245245246-for (const feature of features) {
247-const themes = new Set(feature.scenarios.map((scenario) => scenario.theme));
246+for (const coverage of coverageIds) {
247+const themes = new Set(coverage.scenarios.map((scenario) => scenario.theme));
248248for (const theme of themes) {
249249byTheme[theme] ??= [];
250250byTheme[theme].push({
251- ...feature,
252-scenarios: feature.scenarios.filter((scenario) => scenario.theme === theme),
251+ ...coverage,
252+scenarios: coverage.scenarios.filter((scenario) => scenario.theme === theme),
253253});
254254}
255-const surfaces = new Set(feature.scenarios.flatMap((scenario) => scenario.surfaces));
255+const surfaces = new Set(coverage.scenarios.flatMap((scenario) => scenario.surfaces));
256256for (const surface of surfaces) {
257257bySurface[surface] ??= [];
258258bySurface[surface].push({
259- ...feature,
260-scenarios: feature.scenarios.filter((scenario) => scenario.surfaces.includes(surface)),
259+ ...coverage,
260+scenarios: coverage.scenarios.filter((scenario) => scenario.surfaces.includes(surface)),
261261});
262262}
263263}
264264265265return {
266266scenarioCount: scenarios.length,
267-coverageIdCount: features.length,
267+coverageIdCount: coverageIds.length,
268268primaryCoverageIdCount: primaryCoverageIds.size,
269269secondaryCoverageIdCount: secondaryCoverageIds.size,
270-features,
270+coverageIds,
271271 overlappingCoverage,
272272 missingCoverage,
273273 byTheme,
@@ -278,12 +278,12 @@ export function buildQaCoverageInventory(
278278};
279279}
280280281-function pushFeatureLines(lines: string[], features: readonly QaCoverageFeatureSummary[]) {
282-for (const feature of sortFeatures(features)) {
283-const scenarios = feature.scenarios
281+function pushCoverageIdLines(lines: string[], coverageIds: readonly QaCoverageIdSummary[]) {
282+for (const coverage of sortCoverageIds(coverageIds)) {
283+const scenarios = coverage.scenarios
284284.map((scenario) => `${scenario.intent}: ${scenario.id} (${scenario.sourcePath})`)
285285.join(", ");
286-lines.push(`- ${feature.id}: ${scenarios}`);
286+lines.push(`- ${coverage.id}: ${scenarios}`);
287287}
288288}
289289@@ -314,28 +314,26 @@ function pushScenarioPackLines(lines: string[], packs: readonly QaCoverageScenar
314314const missing =
315315pack.missingScenarioIds.length > 0 ? pack.missingScenarioIds.join(", ") : "none";
316316lines.push(
317-`- ${pack.id} (${pack.title}): ${pack.scenarioIds.length} scenarios; coverage: ${pack.coverageIds.join(", ")}; missing scenarios: ${missing}`,
317+`- ${pack.id} (${pack.title}): ${pack.scenarioIds.length} scenarios; coverage IDs: ${pack.coverageIds.join(", ")}; missing scenarios: ${missing}`,
318318);
319319lines.push(` - scenarios: ${pack.scenarioIds.join(", ")}`);
320320}
321321}
322322323323function pushScorecardTaxonomyLines(lines: string[], report: QaScorecardTaxonomyReport) {
324324lines.push("## Scorecard Taxonomy", "");
325-lines.push(`- Mapping: ${report.taxonomyPath ?? "missing"}`);
326-lines.push(`- Mapping ID: ${report.taxonomyId ?? "missing"}`);
327-lines.push(`- Maturity taxonomy: ${report.taxonomy?.sourcePath ?? "missing"}`);
328-if (report.scoreSnapshotRef) {
329-lines.push(`- Maturity score snapshot: ${report.scoreSnapshotRef}`);
330-}
325+lines.push(`- Taxonomy: ${report.taxonomyPath ?? "missing"}`);
326+lines.push(`- Categories: ${report.categoryCount}`);
327+lines.push(`- Profiles: ${report.profileCount}`);
331328lines.push(
332-`- Categories: ${report.categoryCount} (${report.ltsIncludedCategoryCount} LTS-included, ${report.deferredCategoryCount} deferred, ${report.advisoryCategoryCount} advisory)`,
329+`- Fulfilled taxonomy categories: ${report.fulfilledCategoryCount}/${report.requiredCategoryCount} (${report.categoryFulfillmentPercent}%)`,
333330);
334-lines.push(`- Profiles: ${report.profileCount}`);
335-lines.push(`- Release-blocking categories: ${report.releaseBlockingCategoryCount}`);
336-lines.push(`- Mapped coverage IDs: ${report.mappedCoverageIdCount}`);
337-lines.push(`- Mapped scenarios: ${report.mappedScenarioCount}`);
338-lines.push(`- Unmapped coverage IDs: ${report.unmappedCoverageIdCount}`);
331+lines.push(
332+`- Fulfilled taxonomy features: ${report.fulfilledFeatureCount}/${report.requiredFeatureCount} (${report.taxonomyFulfillmentPercent}%)`,
333+);
334+lines.push(`- Evidence refs: ${report.evidenceRefCount}`);
335+lines.push(`- Scenario coverage IDs: ${report.scenarioCoverageIdCount}`);
336+lines.push(`- Unmapped scenario coverage IDs: ${report.unmappedCoverageIdCount}`);
339337lines.push(`- Validation warnings: ${report.validationIssueCount}`, "");
340338341339if (report.profiles.length > 0) {
@@ -350,13 +348,20 @@ function pushScorecardTaxonomyLines(lines: string[], report: QaScorecardTaxonomy
350348if (report.categories.length > 0) {
351349lines.push("### Category Mapping", "");
352350for (const category of report.categories) {
353-const blocking = category.releaseBlocking ? "release-blocking" : "non-blocking";
354-const coverage = category.coverageIds.length > 0 ? category.coverageIds.join(", ") : "none";
355-const scenarios =
356-category.scenarioRefs.length > 0 ? category.scenarioRefs.join(", ") : "none";
351+const coverageIds =
352+category.coverageIds.length > 0 ? category.coverageIds.join(", ") : "none";
353+const evidence =
354+category.evidence.length > 0
355+ ? category.evidence
356+.map((ref) => {
357+const target = ref.path ?? (ref.scenarioRefs.join("|") || "discovered");
358+return `${ref.role}:${ref.kind}:${target} (${ref.coverageId})`;
359+})
360+.join(", ")
361+ : "none";
357362const profiles = category.profiles.length > 0 ? category.profiles.join(", ") : "none";
358363lines.push(
359-`- ${category.id} (${category.taxonomySurfaceId} / ${category.taxonomyCategoryName}; ${category.supportStatus}, ${blocking}, ${category.mappingStatus}): profiles: ${profiles}; coverage: ${coverage}; scenarios: ${scenarios}`,
364+`- ${category.id} (${category.taxonomySurfaceId} / ${category.taxonomyCategoryName}; ${category.mappingStatus}): profiles: ${profiles}; coverage IDs: ${coverageIds}; evidence: ${evidence}`,
360365);
361366}
362367lines.push("");
@@ -372,7 +377,7 @@ function pushScorecardTaxonomyLines(lines: string[], report: QaScorecardTaxonomy
372377}
373378374379if (report.unmappedCoverageIds.length > 0) {
375-lines.push("### Unmapped Coverage IDs", "");
380+lines.push("### Unmapped Scenario Coverage IDs", "");
376381lines.push(report.unmappedCoverageIds.join(", "));
377382lines.push("");
378383}
@@ -383,7 +388,7 @@ export function renderQaCoverageMarkdownReport(inventory: QaCoverageInventory):
383388"# QA Coverage Inventory",
384389"",
385390`- Scenarios: ${inventory.scenarioCount}`,
386-`- Coverage IDs: ${inventory.coverageIdCount}`,
391+`- Taxonomy coverage IDs: ${inventory.coverageIdCount}`,
387392`- Primary coverage IDs: ${inventory.primaryCoverageIdCount}`,
388393`- Secondary coverage IDs: ${inventory.secondaryCoverageIdCount}`,
389394`- Overlapping coverage IDs: ${inventory.overlappingCoverage.length}`,
@@ -400,14 +405,14 @@ export function renderQaCoverageMarkdownReport(inventory: QaCoverageInventory):
400405lines.push("## By Theme", "");
401406for (const theme of Object.keys(inventory.byTheme).toSorted()) {
402407lines.push(`### ${theme}`, "");
403-pushFeatureLines(lines, inventory.byTheme[theme] ?? []);
408+pushCoverageIdLines(lines, inventory.byTheme[theme] ?? []);
404409lines.push("");
405410}
406411407412lines.push("## By Surface", "");
408413for (const surface of Object.keys(inventory.bySurface).toSorted()) {
409414lines.push(`### ${surface}`, "");
410-pushFeatureLines(lines, inventory.bySurface[surface] ?? []);
415+pushCoverageIdLines(lines, inventory.bySurface[surface] ?? []);
411416lines.push("");
412417}
413418@@ -421,7 +426,7 @@ export function renderQaCoverageMarkdownReport(inventory: QaCoverageInventory):
421426422427if (inventory.overlappingCoverage.length > 0) {
423428lines.push("## Overlap", "");
424-pushFeatureLines(lines, inventory.overlappingCoverage);
429+pushCoverageIdLines(lines, inventory.overlappingCoverage);
425430lines.push("");
426431}
427432@@ -456,31 +461,29 @@ function formatSuiteCommand(matches: readonly QaScenarioSearchMatch[]) {
456461function scenarioMatchCommandGroups(matches: readonly QaScenarioSearchMatch[]) {
457462const groups = new Map<QaScenarioSearchMatch["executionKind"], QaScenarioSearchMatch[]>();
458463for (const match of matches) {
459-const existing = groups.get(match.executionKind) ?? [];
460-existing.push(match);
461-groups.set(match.executionKind, existing);
464+const group = groups.get(match.executionKind) ?? [];
465+group.push(match);
466+groups.set(match.executionKind, group);
462467}
463468const executionOrder: QaScenarioSearchMatch["executionKind"][] = ["flow", "vitest", "playwright"];
464-return executionOrder
465-.map((executionKind) => ({
466- executionKind,
467-matches: groups.get(executionKind) ?? [],
468-}))
469-.filter((group) => group.matches.length > 0);
469+return executionOrder.flatMap((executionKind) => {
470+const group = groups.get(executionKind);
471+return group && group.length > 0 ? [{ executionKind, matches: group }] : [];
472+});
470473}
471474472475export function renderQaScenarioMatchesMarkdownReport(params: {
473476query: string;
474477matches: readonly QaScenarioSearchMatch[];
475478}) {
479+const commandGroups = scenarioMatchCommandGroups(params.matches);
476480const lines = [
477481"# QA Scenario Matches",
478482"",
479483`- Query: ${params.query}`,
480484`- Matches: ${params.matches.length}`,
481485];
482486483-const commandGroups = scenarioMatchCommandGroups(params.matches);
484487if (commandGroups.length === 1) {
485488lines.push(`- Suite command: \`${formatSuiteCommand(commandGroups[0].matches)}\``);
486489} else if (commandGroups.length > 1) {
@@ -502,10 +505,10 @@ export function renderQaScenarioMatchesMarkdownReport(params: {
502505lines.push(` - surface: ${match.surfaces.join(", ")}`);
503506lines.push(
504507match.executionKind === "flow"
505- ? " - execution: flow (qa-flow block)"
508+ ? " - execution: qa-flow"
506509 : ` - execution: ${match.executionKind} ${match.executionPath ?? "missing"}`,
507510);
508-lines.push(` - coverage: ${match.coverageIds.join(", ") || "none"}`);
511+lines.push(` - coverage IDs: ${match.coverageIds.join(", ") || "none"}`);
509512lines.push(` - live requirements: ${formatOptionalScenarioMetadata(match)}`);
510513if (match.codeRefs.length > 0) {
511514lines.push(` - code refs: ${match.codeRefs.join(", ")}`);
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。