@oscharko-dev/keiko-quality-intelligence 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -0
- package/dist/__tests__/_fixtureLoader.d.ts +9 -0
- package/dist/__tests__/_fixtureLoader.d.ts.map +1 -0
- package/dist/__tests__/_fixtureLoader.js +75 -0
- package/dist/domain/assertions.d.ts +61 -0
- package/dist/domain/assertions.d.ts.map +1 -0
- package/dist/domain/assertions.js +134 -0
- package/dist/domain/coverageRelevance.d.ts +73 -0
- package/dist/domain/coverageRelevance.d.ts.map +1 -0
- package/dist/domain/coverageRelevance.js +155 -0
- package/dist/domain/deduplication.d.ts +17 -0
- package/dist/domain/deduplication.d.ts.map +1 -0
- package/dist/domain/deduplication.js +95 -0
- package/dist/domain/figma/a11yBaseline.d.ts +17 -0
- package/dist/domain/figma/a11yBaseline.d.ts.map +1 -0
- package/dist/domain/figma/a11yBaseline.js +218 -0
- package/dist/domain/figma/cleanToScreenIr.d.ts +3 -0
- package/dist/domain/figma/cleanToScreenIr.d.ts.map +1 -0
- package/dist/domain/figma/cleanToScreenIr.js +62 -0
- package/dist/domain/figma/codeTargetAdapter.d.ts +30 -0
- package/dist/domain/figma/codeTargetAdapter.d.ts.map +1 -0
- package/dist/domain/figma/codeTargetAdapter.js +27 -0
- package/dist/domain/figma/color.d.ts +31 -0
- package/dist/domain/figma/color.d.ts.map +1 -0
- package/dist/domain/figma/color.js +99 -0
- package/dist/domain/figma/emissionPlan.d.ts +56 -0
- package/dist/domain/figma/emissionPlan.d.ts.map +1 -0
- package/dist/domain/figma/emissionPlan.js +87 -0
- package/dist/domain/figma/htmlCssAdapter.d.ts +13 -0
- package/dist/domain/figma/htmlCssAdapter.d.ts.map +1 -0
- package/dist/domain/figma/htmlCssAdapter.js +452 -0
- package/dist/domain/figma/index.d.ts +19 -0
- package/dist/domain/figma/index.d.ts.map +1 -0
- package/dist/domain/figma/index.js +31 -0
- package/dist/domain/figma/irTypes.d.ts +156 -0
- package/dist/domain/figma/irTypes.d.ts.map +1 -0
- package/dist/domain/figma/irTypes.js +8 -0
- package/dist/domain/figma/links.d.ts +6 -0
- package/dist/domain/figma/links.d.ts.map +1 -0
- package/dist/domain/figma/links.js +102 -0
- package/dist/domain/figma/navGraph.d.ts +74 -0
- package/dist/domain/figma/navGraph.d.ts.map +1 -0
- package/dist/domain/figma/navGraph.js +315 -0
- package/dist/domain/figma/normalize.d.ts +7 -0
- package/dist/domain/figma/normalize.d.ts.map +1 -0
- package/dist/domain/figma/normalize.js +252 -0
- package/dist/domain/figma/prune.d.ts +15 -0
- package/dist/domain/figma/prune.d.ts.map +1 -0
- package/dist/domain/figma/prune.js +65 -0
- package/dist/domain/figma/screenDetect.d.ts +8 -0
- package/dist/domain/figma/screenDetect.d.ts.map +1 -0
- package/dist/domain/figma/screenDetect.js +35 -0
- package/dist/domain/figma/screenIrTestBaseline.d.ts +52 -0
- package/dist/domain/figma/screenIrTestBaseline.d.ts.map +1 -0
- package/dist/domain/figma/screenIrTestBaseline.js +326 -0
- package/dist/domain/figma/semanticNaming.d.ts +24 -0
- package/dist/domain/figma/semanticNaming.d.ts.map +1 -0
- package/dist/domain/figma/semanticNaming.js +67 -0
- package/dist/domain/figma/sourceNode.d.ts +24 -0
- package/dist/domain/figma/sourceNode.d.ts.map +1 -0
- package/dist/domain/figma/sourceNode.js +26 -0
- package/dist/domain/figma/tokens.d.ts +11 -0
- package/dist/domain/figma/tokens.d.ts.map +1 -0
- package/dist/domain/figma/tokens.js +148 -0
- package/dist/domain/figma/visionAugmentation.d.ts +14 -0
- package/dist/domain/figma/visionAugmentation.d.ts.map +1 -0
- package/dist/domain/figma/visionAugmentation.js +48 -0
- package/dist/domain/intentDerivation.d.ts +21 -0
- package/dist/domain/intentDerivation.d.ts.map +1 -0
- package/dist/domain/intentDerivation.js +126 -0
- package/dist/domain/policyProfile.d.ts +37 -0
- package/dist/domain/policyProfile.d.ts.map +1 -0
- package/dist/domain/policyProfile.js +94 -0
- package/dist/domain/requirementExcerpt.d.ts +9 -0
- package/dist/domain/requirementExcerpt.d.ts.map +1 -0
- package/dist/domain/requirementExcerpt.js +39 -0
- package/dist/domain/staleness.d.ts +56 -0
- package/dist/domain/staleness.d.ts.map +1 -0
- package/dist/domain/staleness.js +313 -0
- package/dist/domain/testDesignModel.d.ts +38 -0
- package/dist/domain/testDesignModel.d.ts.map +1 -0
- package/dist/domain/testDesignModel.js +264 -0
- package/dist/domain/testQualityRubric.d.ts +20 -0
- package/dist/domain/testQualityRubric.d.ts.map +1 -0
- package/dist/domain/testQualityRubric.js +38 -0
- package/dist/domain/validation.d.ts +7 -0
- package/dist/domain/validation.d.ts.map +1 -0
- package/dist/domain/validation.js +145 -0
- package/dist/export/adapters/alm.d.ts +4 -0
- package/dist/export/adapters/alm.d.ts.map +1 -0
- package/dist/export/adapters/alm.js +75 -0
- package/dist/export/adapters/csv.d.ts +5 -0
- package/dist/export/adapters/csv.d.ts.map +1 -0
- package/dist/export/adapters/csv.js +55 -0
- package/dist/export/adapters/index.d.ts +13 -0
- package/dist/export/adapters/index.d.ts.map +1 -0
- package/dist/export/adapters/index.js +15 -0
- package/dist/export/adapters/jira.d.ts +5 -0
- package/dist/export/adapters/jira.d.ts.map +1 -0
- package/dist/export/adapters/jira.js +79 -0
- package/dist/export/adapters/json.d.ts +3 -0
- package/dist/export/adapters/json.d.ts.map +1 -0
- package/dist/export/adapters/json.js +54 -0
- package/dist/export/adapters/markdown.d.ts +3 -0
- package/dist/export/adapters/markdown.d.ts.map +1 -0
- package/dist/export/adapters/markdown.js +88 -0
- package/dist/export/adapters/plaintext.d.ts +3 -0
- package/dist/export/adapters/plaintext.d.ts.map +1 -0
- package/dist/export/adapters/plaintext.js +65 -0
- package/dist/export/adapters/polarion.d.ts +4 -0
- package/dist/export/adapters/polarion.d.ts.map +1 -0
- package/dist/export/adapters/polarion.js +67 -0
- package/dist/export/adapters/qtest.d.ts +4 -0
- package/dist/export/adapters/qtest.d.ts.map +1 -0
- package/dist/export/adapters/qtest.js +78 -0
- package/dist/export/adapters/qualityCenter.d.ts +3 -0
- package/dist/export/adapters/qualityCenter.d.ts.map +1 -0
- package/dist/export/adapters/qualityCenter.js +56 -0
- package/dist/export/adapters/spreadsheetSafeCsv.d.ts +36 -0
- package/dist/export/adapters/spreadsheetSafeCsv.d.ts.map +1 -0
- package/dist/export/adapters/spreadsheetSafeCsv.js +157 -0
- package/dist/export/adapters/traceability.d.ts +34 -0
- package/dist/export/adapters/traceability.d.ts.map +1 -0
- package/dist/export/adapters/traceability.js +142 -0
- package/dist/export/adapters/xray.d.ts +4 -0
- package/dist/export/adapters/xray.d.ts.map +1 -0
- package/dist/export/adapters/xray.js +72 -0
- package/dist/export/formats.d.ts +29 -0
- package/dist/export/formats.d.ts.map +1 -0
- package/dist/export/formats.js +34 -0
- package/dist/export/index.d.ts +4 -0
- package/dist/export/index.d.ts.map +1 -0
- package/dist/export/index.js +10 -0
- package/dist/export/serialize.d.ts +17 -0
- package/dist/export/serialize.d.ts.map +1 -0
- package/dist/export/serialize.js +56 -0
- package/dist/export/textSafety.d.ts +15 -0
- package/dist/export/textSafety.d.ts.map +1 -0
- package/dist/export/textSafety.js +30 -0
- package/dist/generation/candidateBounds.d.ts +10 -0
- package/dist/generation/candidateBounds.d.ts.map +1 -0
- package/dist/generation/candidateBounds.js +14 -0
- package/dist/generation/index.d.ts +4 -0
- package/dist/generation/index.d.ts.map +1 -0
- package/dist/generation/index.js +20 -0
- package/dist/generation/parseGeneratedCandidates.d.ts +27 -0
- package/dist/generation/parseGeneratedCandidates.d.ts.map +1 -0
- package/dist/generation/parseGeneratedCandidates.js +253 -0
- package/dist/generation/prompt.d.ts +16 -0
- package/dist/generation/prompt.d.ts.map +1 -0
- package/dist/generation/prompt.js +151 -0
- package/dist/generation/requirementsIngestion.d.ts +21 -0
- package/dist/generation/requirementsIngestion.d.ts.map +1 -0
- package/dist/generation/requirementsIngestion.js +70 -0
- package/dist/hardening/index.d.ts +6 -0
- package/dist/hardening/index.d.ts.map +1 -0
- package/dist/hardening/index.js +8 -0
- package/dist/hardening/oversizeGuards.d.ts +21 -0
- package/dist/hardening/oversizeGuards.d.ts.map +1 -0
- package/dist/hardening/oversizeGuards.js +35 -0
- package/dist/hardening/pathSafety.d.ts +19 -0
- package/dist/hardening/pathSafety.d.ts.map +1 -0
- package/dist/hardening/pathSafety.js +61 -0
- package/dist/hardening/promptInjectionScrub.d.ts +17 -0
- package/dist/hardening/promptInjectionScrub.d.ts.map +1 -0
- package/dist/hardening/promptInjectionScrub.js +72 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +44 -0
- package/dist/ingestion/adfParser.d.ts +61 -0
- package/dist/ingestion/adfParser.d.ts.map +1 -0
- package/dist/ingestion/adfParser.js +262 -0
- package/dist/ingestion/index.d.ts +6 -0
- package/dist/ingestion/index.d.ts.map +1 -0
- package/dist/ingestion/index.js +10 -0
- package/dist/ingestion/sourceMixPlanning.d.ts +36 -0
- package/dist/ingestion/sourceMixPlanning.d.ts.map +1 -0
- package/dist/ingestion/sourceMixPlanning.js +65 -0
- package/dist/ingestion/sourceReconciliation.d.ts +39 -0
- package/dist/ingestion/sourceReconciliation.d.ts.map +1 -0
- package/dist/ingestion/sourceReconciliation.js +74 -0
- package/dist/ingestion/untrustedContentNormalisation.d.ts +23 -0
- package/dist/ingestion/untrustedContentNormalisation.d.ts.map +1 -0
- package/dist/ingestion/untrustedContentNormalisation.js +121 -0
- package/dist/ingestion/workspaceAdapter.d.ts +55 -0
- package/dist/ingestion/workspaceAdapter.d.ts.map +1 -0
- package/dist/ingestion/workspaceAdapter.js +113 -0
- package/dist/review/auditEvents.d.ts +61 -0
- package/dist/review/auditEvents.d.ts.map +1 -0
- package/dist/review/auditEvents.js +50 -0
- package/dist/review/fourEyes.d.ts +24 -0
- package/dist/review/fourEyes.d.ts.map +1 -0
- package/dist/review/fourEyes.js +45 -0
- package/dist/review/index.d.ts +5 -0
- package/dist/review/index.d.ts.map +1 -0
- package/dist/review/index.js +14 -0
- package/dist/review/lifecyclePolicy.d.ts +21 -0
- package/dist/review/lifecyclePolicy.d.ts.map +1 -0
- package/dist/review/lifecyclePolicy.js +38 -0
- package/dist/review/stateMachine.d.ts +28 -0
- package/dist/review/stateMachine.d.ts.map +1 -0
- package/dist/review/stateMachine.js +71 -0
- package/package.json +31 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Plain-text export adapter (Epic #711).
|
|
2
|
+
//
|
|
3
|
+
// Produces a deterministic plain-text document with one section per candidate,
|
|
4
|
+
// sorted by candidateId ascending. No timestamps, no random content. Pure
|
|
5
|
+
// string output — byte-identical for identical input.
|
|
6
|
+
//
|
|
7
|
+
// Pure-domain leaf. NO IO. NO node:* imports. NO new runtime dependency.
|
|
8
|
+
import { assertExportBundleInvariant } from "@oscharko-dev/keiko-contracts";
|
|
9
|
+
import { inlineField, inlineFields } from "../textSafety.js";
|
|
10
|
+
const byCandidateIdAsc = (a, b) => a.candidateId < b.candidateId ? -1 : a.candidateId > b.candidateId ? 1 : 0;
|
|
11
|
+
const RULE = "=".repeat(60);
|
|
12
|
+
const DIVIDER = "-".repeat(40);
|
|
13
|
+
const listItems = (items, indent = " ") => items.length === 0
|
|
14
|
+
? `${indent}(none)\n`
|
|
15
|
+
: items.map((item) => `${indent}- ${item}`).join("\n") + "\n";
|
|
16
|
+
const numberedList = (items, indent = " ") => items.length === 0
|
|
17
|
+
? `${indent}(none)\n`
|
|
18
|
+
: items.map((item, i) => `${indent}${String(i + 1)}. ${item}`).join("\n") + "\n";
|
|
19
|
+
function renderCandidate(candidate, index) {
|
|
20
|
+
const lines = [];
|
|
21
|
+
lines.push(`${DIVIDER}\n`);
|
|
22
|
+
lines.push(`CANDIDATE ${String(index + 1)}: ${inlineField(candidate.title)}\n`);
|
|
23
|
+
lines.push(` ID: ${candidate.id}`);
|
|
24
|
+
lines.push(` Priority: ${candidate.priority}`);
|
|
25
|
+
lines.push(` Risk class: ${candidate.riskClass}`);
|
|
26
|
+
lines.push(` Status: ${candidate.status}`);
|
|
27
|
+
lines.push(` Tags: ${candidate.tags.length > 0 ? inlineFields(candidate.tags).join(", ") : "(none)"}`);
|
|
28
|
+
lines.push("");
|
|
29
|
+
lines.push(" Preconditions:");
|
|
30
|
+
lines.push(listItems(inlineFields(candidate.preconditions), " ").trimEnd());
|
|
31
|
+
lines.push("");
|
|
32
|
+
lines.push(" Steps:");
|
|
33
|
+
lines.push(numberedList(inlineFields(candidate.steps), " ").trimEnd());
|
|
34
|
+
lines.push("");
|
|
35
|
+
lines.push(" Expected results:");
|
|
36
|
+
lines.push(listItems(inlineFields(candidate.expectedResults), " ").trimEnd());
|
|
37
|
+
return lines.join("\n") + "\n";
|
|
38
|
+
}
|
|
39
|
+
export function adaptToPlainText(bundle, candidates) {
|
|
40
|
+
assertExportBundleInvariant(bundle);
|
|
41
|
+
const byId = new Map();
|
|
42
|
+
for (const candidate of candidates) {
|
|
43
|
+
byId.set(candidate.id, candidate);
|
|
44
|
+
}
|
|
45
|
+
const sortedEntries = [...bundle.contents].sort(byCandidateIdAsc);
|
|
46
|
+
const sections = [];
|
|
47
|
+
sections.push(`${RULE}\n`);
|
|
48
|
+
sections.push("QUALITY INTELLIGENCE EXPORT\n");
|
|
49
|
+
sections.push(`${RULE}\n`);
|
|
50
|
+
sections.push(`Bundle: ${bundle.id}`);
|
|
51
|
+
sections.push(`Run: ${bundle.runId}`);
|
|
52
|
+
sections.push(`Format: ${bundle.targetAdapter}`);
|
|
53
|
+
sections.push("");
|
|
54
|
+
let index = 0;
|
|
55
|
+
for (const entry of sortedEntries) {
|
|
56
|
+
const candidate = byId.get(entry.candidateId);
|
|
57
|
+
if (candidate === undefined) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
sections.push(renderCandidate(candidate, index));
|
|
61
|
+
index += 1;
|
|
62
|
+
}
|
|
63
|
+
sections.push(`${RULE}\n`);
|
|
64
|
+
return sections.join("\n") + "\n";
|
|
65
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { QualityIntelligenceExportBundle, QualityIntelligenceTestCaseCandidate } from "@oscharko-dev/keiko-contracts";
|
|
2
|
+
export declare const POLARION_CSV_HEADERS: readonly string[];
|
|
3
|
+
export declare function adaptToPolarion(bundle: QualityIntelligenceExportBundle, candidates: readonly QualityIntelligenceTestCaseCandidate[]): string;
|
|
4
|
+
//# sourceMappingURL=polarion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"polarion.d.ts","sourceRoot":"","sources":["../../../src/export/adapters/polarion.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,+BAA+B,EAC/B,oCAAoC,EACrC,MAAM,+BAA+B,CAAC;AAIvC,eAAO,MAAM,oBAAoB,EAAE,SAAS,MAAM,EAOhD,CAAC;AA2BH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,+BAA+B,EACvC,UAAU,EAAE,SAAS,oCAAoC,EAAE,GAC1D,MAAM,CA0BR"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Polarion CSV export adapter (Epic #270, Issue #283).
|
|
2
|
+
//
|
|
3
|
+
// Polarion's bulk-import canonical shape: ID, Title, Type, Severity, Description,
|
|
4
|
+
// TestSteps (semicolon-joined). One row per test case (Polarion stores steps as a
|
|
5
|
+
// single repeating attribute via its Excel/CSV importer).
|
|
6
|
+
//
|
|
7
|
+
// TMS-bound: invariant asserted first. Pure-domain — NO HTTP, NO Polarion SDK.
|
|
8
|
+
import { assertExportBundleInvariant } from "@oscharko-dev/keiko-contracts";
|
|
9
|
+
import { encodeSpreadsheetSafeRow } from "./spreadsheetSafeCsv.js";
|
|
10
|
+
export const POLARION_CSV_HEADERS = Object.freeze([
|
|
11
|
+
"ID",
|
|
12
|
+
"Title",
|
|
13
|
+
"Type",
|
|
14
|
+
"Severity",
|
|
15
|
+
"Description",
|
|
16
|
+
"TestSteps",
|
|
17
|
+
]);
|
|
18
|
+
const mapSeverity = (priority) => {
|
|
19
|
+
// Polarion default severities; closest mapping to QI's P0..P3.
|
|
20
|
+
switch (priority) {
|
|
21
|
+
case "P0":
|
|
22
|
+
return "blocker";
|
|
23
|
+
case "P1":
|
|
24
|
+
return "critical";
|
|
25
|
+
case "P2":
|
|
26
|
+
return "major";
|
|
27
|
+
case "P3":
|
|
28
|
+
return "minor";
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
const buildDescription = (candidate) => {
|
|
32
|
+
const parts = [];
|
|
33
|
+
if (candidate.preconditions.length > 0) {
|
|
34
|
+
parts.push(`Preconditions: ${candidate.preconditions.join(" ; ")}`);
|
|
35
|
+
}
|
|
36
|
+
if (candidate.expectedResults.length > 0) {
|
|
37
|
+
parts.push(`Expected: ${candidate.expectedResults.join(" ; ")}`);
|
|
38
|
+
}
|
|
39
|
+
return parts.join(" || ");
|
|
40
|
+
};
|
|
41
|
+
export function adaptToPolarion(bundle, candidates) {
|
|
42
|
+
assertExportBundleInvariant(bundle);
|
|
43
|
+
const byId = new Map();
|
|
44
|
+
for (const candidate of candidates) {
|
|
45
|
+
byId.set(candidate.id, candidate);
|
|
46
|
+
}
|
|
47
|
+
const sortedIds = bundle.contents
|
|
48
|
+
.map((entry) => entry.candidateId)
|
|
49
|
+
.slice()
|
|
50
|
+
.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
51
|
+
let body = encodeSpreadsheetSafeRow(POLARION_CSV_HEADERS);
|
|
52
|
+
for (const id of sortedIds) {
|
|
53
|
+
const candidate = byId.get(id);
|
|
54
|
+
if (candidate === undefined) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
body += encodeSpreadsheetSafeRow([
|
|
58
|
+
candidate.id,
|
|
59
|
+
candidate.title,
|
|
60
|
+
"testcase",
|
|
61
|
+
mapSeverity(candidate.priority),
|
|
62
|
+
buildDescription(candidate),
|
|
63
|
+
candidate.steps.join(" ; "),
|
|
64
|
+
]);
|
|
65
|
+
}
|
|
66
|
+
return body;
|
|
67
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { QualityIntelligenceExportBundle, QualityIntelligenceTestCaseCandidate } from "@oscharko-dev/keiko-contracts";
|
|
2
|
+
export declare const QTEST_CSV_HEADERS: readonly string[];
|
|
3
|
+
export declare function adaptToQtest(bundle: QualityIntelligenceExportBundle, candidates: readonly QualityIntelligenceTestCaseCandidate[]): string;
|
|
4
|
+
//# sourceMappingURL=qtest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qtest.d.ts","sourceRoot":"","sources":["../../../src/export/adapters/qtest.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,+BAA+B,EAC/B,oCAAoC,EACrC,MAAM,+BAA+B,CAAC;AAIvC,eAAO,MAAM,iBAAiB,EAAE,SAAS,MAAM,EAS7C,CAAC;AA0CH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,+BAA+B,EACvC,UAAU,EAAE,SAAS,oCAAoC,EAAE,GAC1D,MAAM,CAkBR"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
// qTest CSV export adapter (Epic #270, Issue #283).
|
|
2
|
+
//
|
|
3
|
+
// qTest's canonical bulk-import shape carries ONE row per (test case, step) pair so
|
|
4
|
+
// the test-case header columns (Name, Description, Type, Priority) repeat across
|
|
5
|
+
// step rows of the same test case. A test case with N steps occupies N rows; a
|
|
6
|
+
// test case with zero steps occupies one row (with empty Step Number / Action /
|
|
7
|
+
// Expected). Ordering is deterministic: candidate id ASC, then 1-based step index.
|
|
8
|
+
//
|
|
9
|
+
// TMS-bound: `assertExportBundleInvariant` runs first. Pure-domain leaf — NO HTTP,
|
|
10
|
+
// NO qTest SDK.
|
|
11
|
+
import { assertExportBundleInvariant } from "@oscharko-dev/keiko-contracts";
|
|
12
|
+
import { encodeSpreadsheetSafeRow } from "./spreadsheetSafeCsv.js";
|
|
13
|
+
export const QTEST_CSV_HEADERS = Object.freeze([
|
|
14
|
+
"Name",
|
|
15
|
+
"Description",
|
|
16
|
+
"Type",
|
|
17
|
+
"Priority",
|
|
18
|
+
"Status",
|
|
19
|
+
"StepNumber",
|
|
20
|
+
"Action",
|
|
21
|
+
"Expected",
|
|
22
|
+
]);
|
|
23
|
+
const buildDescription = (candidate) => {
|
|
24
|
+
if (candidate.preconditions.length === 0) {
|
|
25
|
+
return "";
|
|
26
|
+
}
|
|
27
|
+
return `Preconditions: ${candidate.preconditions.join(" ; ")}`;
|
|
28
|
+
};
|
|
29
|
+
const mapPriority = (priority) => {
|
|
30
|
+
// qTest default priorities are P-prefixed; pass through verbatim.
|
|
31
|
+
return priority;
|
|
32
|
+
};
|
|
33
|
+
// Build the qTest rows for a single candidate. One row per (step, expected) pair — the row count is
|
|
34
|
+
// the longer of `steps`/`expectedResults` so a trailing expected result is never dropped (Issue
|
|
35
|
+
// #283); a candidate with neither yields one empty-step row.
|
|
36
|
+
function qtestRowsFor(candidate) {
|
|
37
|
+
const head = [
|
|
38
|
+
candidate.title,
|
|
39
|
+
buildDescription(candidate),
|
|
40
|
+
"Manual",
|
|
41
|
+
mapPriority(candidate.priority),
|
|
42
|
+
candidate.status,
|
|
43
|
+
];
|
|
44
|
+
const rowCount = Math.max(candidate.steps.length, candidate.expectedResults.length);
|
|
45
|
+
if (rowCount === 0) {
|
|
46
|
+
return encodeSpreadsheetSafeRow([...head, "", "", ""]);
|
|
47
|
+
}
|
|
48
|
+
let rows = "";
|
|
49
|
+
for (let i = 0; i < rowCount; i += 1) {
|
|
50
|
+
const stepNumber = i < candidate.steps.length ? String(i + 1) : "";
|
|
51
|
+
rows += encodeSpreadsheetSafeRow([
|
|
52
|
+
...head,
|
|
53
|
+
stepNumber,
|
|
54
|
+
candidate.steps[i] ?? "",
|
|
55
|
+
candidate.expectedResults[i] ?? "",
|
|
56
|
+
]);
|
|
57
|
+
}
|
|
58
|
+
return rows;
|
|
59
|
+
}
|
|
60
|
+
export function adaptToQtest(bundle, candidates) {
|
|
61
|
+
assertExportBundleInvariant(bundle);
|
|
62
|
+
const byId = new Map();
|
|
63
|
+
for (const candidate of candidates) {
|
|
64
|
+
byId.set(candidate.id, candidate);
|
|
65
|
+
}
|
|
66
|
+
const sortedIds = bundle.contents
|
|
67
|
+
.map((entry) => entry.candidateId)
|
|
68
|
+
.slice()
|
|
69
|
+
.sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
70
|
+
let body = encodeSpreadsheetSafeRow(QTEST_CSV_HEADERS);
|
|
71
|
+
for (const id of sortedIds) {
|
|
72
|
+
const candidate = byId.get(id);
|
|
73
|
+
if (candidate !== undefined) {
|
|
74
|
+
body += qtestRowsFor(candidate);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return body;
|
|
78
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { QualityIntelligenceExportBundle, QualityIntelligenceTestCaseCandidate } from "@oscharko-dev/keiko-contracts";
|
|
2
|
+
export declare function adaptToQualityCenter(bundle: QualityIntelligenceExportBundle, candidates: readonly QualityIntelligenceTestCaseCandidate[]): string;
|
|
3
|
+
//# sourceMappingURL=qualityCenter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qualityCenter.d.ts","sourceRoot":"","sources":["../../../src/export/adapters/qualityCenter.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EACV,+BAA+B,EAC/B,oCAAoC,EACrC,MAAM,+BAA+B,CAAC;AA2BvC,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,+BAA+B,EACvC,UAAU,EAAE,SAAS,oCAAoC,EAAE,GAC1D,MAAM,CAyBR"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Quality Center (ALM Octane) export adapter (Epic #711).
|
|
2
|
+
//
|
|
3
|
+
// TMS-bound adapter: produces a deterministic dry-run preview of the payload
|
|
4
|
+
// that would be submitted to Quality Center / ALM Octane. NO outbound write
|
|
5
|
+
// is ever performed here — this is a preview-only leaf. The route layer enforces
|
|
6
|
+
// the 403 QI_EXTERNAL_EXPORT_DISABLED guard on non-dry-run requests.
|
|
7
|
+
//
|
|
8
|
+
// Output format: a simple key-value text report, one test case per block, sorted
|
|
9
|
+
// by candidateId ascending. No timestamps. No random content. Byte-stable.
|
|
10
|
+
//
|
|
11
|
+
// Pure-domain leaf. NO IO. NO node:* imports. NO new runtime dependency.
|
|
12
|
+
import { assertExportBundleInvariant } from "@oscharko-dev/keiko-contracts";
|
|
13
|
+
import { inlineField, inlineFields } from "../textSafety.js";
|
|
14
|
+
const byCandidateIdAsc = (a, b) => a.candidateId < b.candidateId ? -1 : a.candidateId > b.candidateId ? 1 : 0;
|
|
15
|
+
const DIVIDER = "-".repeat(60);
|
|
16
|
+
const joinPipe = (items) => items.length === 0 ? "(none)" : inlineFields(items).join(" | ");
|
|
17
|
+
function renderEntry(candidate, index) {
|
|
18
|
+
const lines = [];
|
|
19
|
+
lines.push(DIVIDER);
|
|
20
|
+
lines.push(`QC-${String(index + 1).padStart(4, "0")} ${inlineField(candidate.title)}`);
|
|
21
|
+
lines.push(` ID: ${candidate.id}`);
|
|
22
|
+
lines.push(` Priority: ${candidate.priority}`);
|
|
23
|
+
lines.push(` Risk class: ${candidate.riskClass}`);
|
|
24
|
+
lines.push(` Status: ${candidate.status}`);
|
|
25
|
+
lines.push(` Tags: ${joinPipe(candidate.tags)}`);
|
|
26
|
+
lines.push(` Precond: ${joinPipe(candidate.preconditions)}`);
|
|
27
|
+
lines.push(` Steps: ${joinPipe(candidate.steps)}`);
|
|
28
|
+
lines.push(` Expected: ${joinPipe(candidate.expectedResults)}`);
|
|
29
|
+
return lines.join("\n");
|
|
30
|
+
}
|
|
31
|
+
export function adaptToQualityCenter(bundle, candidates) {
|
|
32
|
+
assertExportBundleInvariant(bundle);
|
|
33
|
+
const byId = new Map();
|
|
34
|
+
for (const candidate of candidates) {
|
|
35
|
+
byId.set(candidate.id, candidate);
|
|
36
|
+
}
|
|
37
|
+
const sortedEntries = [...bundle.contents].sort(byCandidateIdAsc);
|
|
38
|
+
const header = [
|
|
39
|
+
`Quality Center Export Preview`,
|
|
40
|
+
`Bundle: ${bundle.id}`,
|
|
41
|
+
`Run: ${bundle.runId}`,
|
|
42
|
+
`NOTE: This is a dry-run preview. Live export requires a configured connector.`,
|
|
43
|
+
"",
|
|
44
|
+
].join("\n");
|
|
45
|
+
const rows = [];
|
|
46
|
+
let index = 0;
|
|
47
|
+
for (const entry of sortedEntries) {
|
|
48
|
+
const candidate = byId.get(entry.candidateId);
|
|
49
|
+
if (candidate === undefined) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
rows.push(renderEntry(candidate, index));
|
|
53
|
+
index += 1;
|
|
54
|
+
}
|
|
55
|
+
return header + rows.join("\n") + "\n";
|
|
56
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { QualityIntelligenceExportBundle } from "@oscharko-dev/keiko-contracts";
|
|
2
|
+
import type { QualityIntelligenceTestCaseCandidate } from "@oscharko-dev/keiko-contracts";
|
|
3
|
+
/**
|
|
4
|
+
* Lead characters that a spreadsheet may interpret as a formula or DDE invocation.
|
|
5
|
+
* Frozen for reference-stability; consumers should not mutate.
|
|
6
|
+
*/
|
|
7
|
+
export declare const SPREADSHEET_FORMULA_LEAD_CHARS: ReadonlySet<string>;
|
|
8
|
+
/**
|
|
9
|
+
* Returns `true` if `value` would be interpreted as a formula or DDE invocation
|
|
10
|
+
* by a typical spreadsheet because of its leading character — including the case where a leading
|
|
11
|
+
* whitespace run precedes a formula lead, which importers may trim before evaluating.
|
|
12
|
+
*/
|
|
13
|
+
export declare function startsWithFormulaLead(value: string): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Encodes a single cell value as RFC-4180 CSV with formula-injection
|
|
16
|
+
* mitigation applied. Pure — input string yields the same output every time.
|
|
17
|
+
*/
|
|
18
|
+
export declare function encodeSpreadsheetSafeCell(value: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Encodes a row by joining cell-encoded values with `,` and terminating with
|
|
21
|
+
* `\r\n` (RFC 4180 line ending).
|
|
22
|
+
*/
|
|
23
|
+
export declare function encodeSpreadsheetSafeRow(cells: readonly string[]): string;
|
|
24
|
+
/**
|
|
25
|
+
* Schema headers for the generic spreadsheet-safe CSV format. Deliberately
|
|
26
|
+
* minimal — TMS-specific shape lives in the TMS-specific adapters.
|
|
27
|
+
*/
|
|
28
|
+
export declare const SPREADSHEET_SAFE_CSV_HEADERS: readonly string[];
|
|
29
|
+
/**
|
|
30
|
+
* Builds the spreadsheet-safe-CSV body for a bundle. The TMS invariant from
|
|
31
|
+
* the contracts package is asserted up front so a non-attested TMS-targeted
|
|
32
|
+
* bundle cannot slip through. `candidates` is filtered to the entries
|
|
33
|
+
* referenced by the bundle, sorted deterministically by candidate id.
|
|
34
|
+
*/
|
|
35
|
+
export declare function adaptToSpreadsheetSafeCsv(bundle: QualityIntelligenceExportBundle, candidates: readonly QualityIntelligenceTestCaseCandidate[]): string;
|
|
36
|
+
//# sourceMappingURL=spreadsheetSafeCsv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spreadsheetSafeCsv.d.ts","sourceRoot":"","sources":["../../../src/export/adapters/spreadsheetSafeCsv.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,+BAA+B,CAAC;AAErF,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,+BAA+B,CAAC;AAE1F;;;GAGG;AACH,eAAO,MAAM,8BAA8B,EAAE,WAAW,CAAC,MAAM,CAQ7D,CAAC;AAuCH;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAQ5D;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAa/D;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAMzE;AAED;;;GAGG;AACH,eAAO,MAAM,4BAA4B,EAAE,SAAS,MAAM,EAUxD,CAAC;AAIH;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,+BAA+B,EACvC,UAAU,EAAE,SAAS,oCAAoC,EAAE,GAC1D,MAAM,CA2BR"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// Spreadsheet-safe CSV cell encoding (Epic #270, Issue #283).
|
|
2
|
+
//
|
|
3
|
+
// Mitigates CSV formula-injection (OWASP CSV Injection / "DDE injection") by
|
|
4
|
+
// prefixing any cell whose first character is one of `=`, `+`, `-`, `@`, `\t`,
|
|
5
|
+
// `\r`, `\n` with a single quote so a spreadsheet (Excel, LibreOffice Calc,
|
|
6
|
+
// Google Sheets) renders it as literal text rather than evaluating a formula
|
|
7
|
+
// or invoking an external DDE link.
|
|
8
|
+
//
|
|
9
|
+
// The escape rules:
|
|
10
|
+
// - Cells starting with one of the dangerous lead characters get a `'`
|
|
11
|
+
// prefix (single straight quote).
|
|
12
|
+
// - Cells containing `"`, `,`, `\r`, or `\n` are wrapped in `"` quotes; any
|
|
13
|
+
// embedded `"` is doubled per RFC 4180.
|
|
14
|
+
//
|
|
15
|
+
// The two rules compose — a cell like `=cmd|"calc"` becomes `"'=cmd|""calc"""`.
|
|
16
|
+
//
|
|
17
|
+
// Pure-domain leaf. NO IO, NO new runtime dependency, NO regex.
|
|
18
|
+
import { assertExportBundleInvariant } from "@oscharko-dev/keiko-contracts";
|
|
19
|
+
/**
|
|
20
|
+
* Lead characters that a spreadsheet may interpret as a formula or DDE invocation.
|
|
21
|
+
* Frozen for reference-stability; consumers should not mutate.
|
|
22
|
+
*/
|
|
23
|
+
export const SPREADSHEET_FORMULA_LEAD_CHARS = new Set([
|
|
24
|
+
"=",
|
|
25
|
+
"+",
|
|
26
|
+
"-",
|
|
27
|
+
"@",
|
|
28
|
+
"\t",
|
|
29
|
+
"\r",
|
|
30
|
+
"\n",
|
|
31
|
+
]);
|
|
32
|
+
// Whitespace code points a spreadsheet import may strip BEFORE formula detection. Several importers
|
|
33
|
+
// (Excel/LibreOffice/Sheets) trim a leading whitespace run, so a cell like " =1+1" or a NBSP/tab-
|
|
34
|
+
// prefixed formula can still evaluate even though its literal first char is not a lead char.
|
|
35
|
+
const LEADING_WHITESPACE_CODE_POINTS = new Set([
|
|
36
|
+
0x09, // tab
|
|
37
|
+
0x0a, // line feed
|
|
38
|
+
0x0b, // vertical tab
|
|
39
|
+
0x0c, // form feed
|
|
40
|
+
0x0d, // carriage return
|
|
41
|
+
0x20, // space
|
|
42
|
+
0xa0, // no-break space
|
|
43
|
+
0x2007, // figure space
|
|
44
|
+
0x2028, // line separator
|
|
45
|
+
0x2029, // paragraph separator
|
|
46
|
+
0x202f, // narrow no-break space
|
|
47
|
+
0xfeff, // zero-width no-break space / BOM
|
|
48
|
+
]);
|
|
49
|
+
// The formula/DDE lead characters that remain dangerous once a leading whitespace run is stripped.
|
|
50
|
+
// The whitespace leads themselves (TAB/CR/LF) stay covered by the literal first-char check.
|
|
51
|
+
const FORMULA_LEAD_AFTER_WHITESPACE = new Set(["=", "+", "-", "@"]);
|
|
52
|
+
// Scan past a leading whitespace run; return true when the first non-whitespace character is a
|
|
53
|
+
// formula lead. Pure, no regex — scans UTF-16 code units (every listed whitespace point is BMP).
|
|
54
|
+
function firstNonWhitespaceIsFormulaLead(value) {
|
|
55
|
+
let index = 0;
|
|
56
|
+
while (index < value.length && LEADING_WHITESPACE_CODE_POINTS.has(value.charCodeAt(index))) {
|
|
57
|
+
index += 1;
|
|
58
|
+
}
|
|
59
|
+
// index === 0 → no leading whitespace (the literal first-char check already handled it);
|
|
60
|
+
// index >= length → the cell is all whitespace.
|
|
61
|
+
if (index === 0 || index >= value.length) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
return FORMULA_LEAD_AFTER_WHITESPACE.has(value.charAt(index));
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Returns `true` if `value` would be interpreted as a formula or DDE invocation
|
|
68
|
+
* by a typical spreadsheet because of its leading character — including the case where a leading
|
|
69
|
+
* whitespace run precedes a formula lead, which importers may trim before evaluating.
|
|
70
|
+
*/
|
|
71
|
+
export function startsWithFormulaLead(value) {
|
|
72
|
+
if (value.length === 0) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
if (SPREADSHEET_FORMULA_LEAD_CHARS.has(value.charAt(0))) {
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
return firstNonWhitespaceIsFormulaLead(value);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Encodes a single cell value as RFC-4180 CSV with formula-injection
|
|
82
|
+
* mitigation applied. Pure — input string yields the same output every time.
|
|
83
|
+
*/
|
|
84
|
+
export function encodeSpreadsheetSafeCell(value) {
|
|
85
|
+
const prefixed = startsWithFormulaLead(value) ? `'${value}` : value;
|
|
86
|
+
const needsQuoting = prefixed.includes(",") ||
|
|
87
|
+
prefixed.includes('"') ||
|
|
88
|
+
prefixed.includes("\r") ||
|
|
89
|
+
prefixed.includes("\n");
|
|
90
|
+
if (!needsQuoting) {
|
|
91
|
+
return prefixed;
|
|
92
|
+
}
|
|
93
|
+
// Double every embedded quote per RFC 4180.
|
|
94
|
+
const doubled = prefixed.split('"').join('""');
|
|
95
|
+
return `"${doubled}"`;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Encodes a row by joining cell-encoded values with `,` and terminating with
|
|
99
|
+
* `\r\n` (RFC 4180 line ending).
|
|
100
|
+
*/
|
|
101
|
+
export function encodeSpreadsheetSafeRow(cells) {
|
|
102
|
+
const encoded = [];
|
|
103
|
+
for (const cell of cells) {
|
|
104
|
+
encoded.push(encodeSpreadsheetSafeCell(cell));
|
|
105
|
+
}
|
|
106
|
+
return `${encoded.join(",")}\r\n`;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Schema headers for the generic spreadsheet-safe CSV format. Deliberately
|
|
110
|
+
* minimal — TMS-specific shape lives in the TMS-specific adapters.
|
|
111
|
+
*/
|
|
112
|
+
export const SPREADSHEET_SAFE_CSV_HEADERS = Object.freeze([
|
|
113
|
+
"CandidateId",
|
|
114
|
+
"Title",
|
|
115
|
+
"Priority",
|
|
116
|
+
"RiskClass",
|
|
117
|
+
"Status",
|
|
118
|
+
"Tags",
|
|
119
|
+
"Preconditions",
|
|
120
|
+
"Steps",
|
|
121
|
+
"ExpectedResults",
|
|
122
|
+
]);
|
|
123
|
+
const joinSemicolon = (values) => values.join(" ; ");
|
|
124
|
+
/**
|
|
125
|
+
* Builds the spreadsheet-safe-CSV body for a bundle. The TMS invariant from
|
|
126
|
+
* the contracts package is asserted up front so a non-attested TMS-targeted
|
|
127
|
+
* bundle cannot slip through. `candidates` is filtered to the entries
|
|
128
|
+
* referenced by the bundle, sorted deterministically by candidate id.
|
|
129
|
+
*/
|
|
130
|
+
export function adaptToSpreadsheetSafeCsv(bundle, candidates) {
|
|
131
|
+
assertExportBundleInvariant(bundle);
|
|
132
|
+
const byId = new Map();
|
|
133
|
+
for (const candidate of candidates) {
|
|
134
|
+
byId.set(candidate.id, candidate);
|
|
135
|
+
}
|
|
136
|
+
const entryIds = bundle.contents.map((entry) => entry.candidateId);
|
|
137
|
+
const sortedIds = [...entryIds].sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
138
|
+
let body = encodeSpreadsheetSafeRow(SPREADSHEET_SAFE_CSV_HEADERS);
|
|
139
|
+
for (const id of sortedIds) {
|
|
140
|
+
const candidate = byId.get(id);
|
|
141
|
+
if (candidate === undefined) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
body += encodeSpreadsheetSafeRow([
|
|
145
|
+
candidate.id,
|
|
146
|
+
candidate.title,
|
|
147
|
+
candidate.priority,
|
|
148
|
+
candidate.riskClass,
|
|
149
|
+
candidate.status,
|
|
150
|
+
joinSemicolon(candidate.tags),
|
|
151
|
+
joinSemicolon(candidate.preconditions),
|
|
152
|
+
joinSemicolon(candidate.steps),
|
|
153
|
+
joinSemicolon(candidate.expectedResults),
|
|
154
|
+
]);
|
|
155
|
+
}
|
|
156
|
+
return body;
|
|
157
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { CoverageStatus } from "../../domain/coverageRelevance.js";
|
|
2
|
+
/**
|
|
3
|
+
* One requirement row of the traceability matrix: refs + status, plus an optional short REDACTED
|
|
4
|
+
* requirement excerpt (#790) so an auditor can read WHICH requirement a row traces without
|
|
5
|
+
* cross-referencing atom ids. Absent on runs recorded before the excerpt existed.
|
|
6
|
+
*/
|
|
7
|
+
export interface QualityIntelligenceTraceabilityRow {
|
|
8
|
+
readonly atomId: string;
|
|
9
|
+
readonly status: CoverageStatus;
|
|
10
|
+
readonly confidence: number;
|
|
11
|
+
readonly coveringCandidateIds: readonly string[];
|
|
12
|
+
readonly requirementExcerptRedacted?: string;
|
|
13
|
+
}
|
|
14
|
+
/** Optional display enrichment: candidate id -> already-redacted candidate title (#790). */
|
|
15
|
+
export interface QualityIntelligenceTraceabilityDisplayOptions {
|
|
16
|
+
readonly candidateTitleById?: ReadonlyMap<string, string>;
|
|
17
|
+
}
|
|
18
|
+
/** CSV header row for the requirement -> tests direction. */
|
|
19
|
+
export declare const TRACEABILITY_HEADERS: readonly string[];
|
|
20
|
+
/** CSV header row for the test -> requirements (reverse) direction. */
|
|
21
|
+
export declare const TRACEABILITY_REVERSE_HEADERS: readonly string[];
|
|
22
|
+
/**
|
|
23
|
+
* Render the coverage matrix as a spreadsheet-safe, BIDIRECTIONAL CSV traceability matrix: a
|
|
24
|
+
* requirement->tests section followed by a blank line and a tests->requirements section. Each cell
|
|
25
|
+
* is formula-injection-safe via the shared encoder.
|
|
26
|
+
*/
|
|
27
|
+
export declare function adaptToTraceabilityCsv(rows: readonly QualityIntelligenceTraceabilityRow[], display?: QualityIntelligenceTraceabilityDisplayOptions): string;
|
|
28
|
+
/**
|
|
29
|
+
* Render the coverage matrix as a BIDIRECTIONAL Markdown traceability document: a Requirements ->
|
|
30
|
+
* Tests table followed by a Tests -> Requirements table. Deterministic, pipe-escaped and
|
|
31
|
+
* formula-lead-neutralised.
|
|
32
|
+
*/
|
|
33
|
+
export declare function adaptToTraceabilityMarkdown(rows: readonly QualityIntelligenceTraceabilityRow[], display?: QualityIntelligenceTraceabilityDisplayOptions): string;
|
|
34
|
+
//# sourceMappingURL=traceability.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"traceability.d.ts","sourceRoot":"","sources":["../../../src/export/adapters/traceability.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAC;AAIxE;;;;GAIG;AACH,MAAM,WAAW,kCAAkC;IACjD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,oBAAoB,EAAE,SAAS,MAAM,EAAE,CAAC;IACjD,QAAQ,CAAC,0BAA0B,CAAC,EAAE,MAAM,CAAC;CAC9C;AAED,4FAA4F;AAC5F,MAAM,WAAW,6CAA6C;IAC5D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3D;AAED,6DAA6D;AAC7D,eAAO,MAAM,oBAAoB,EAAE,SAAS,MAAM,EAOhD,CAAC;AAEH,uEAAuE;AACvE,eAAO,MAAM,4BAA4B,EAAE,SAAS,MAAM,EAKxD,CAAC;AA2DH;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,SAAS,kCAAkC,EAAE,EACnD,OAAO,GAAE,6CAAkD,GAC1D,MAAM,CA0BR;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,SAAS,kCAAkC,EAAE,EACnD,OAAO,GAAE,6CAAkD,GAC1D,MAAM,CA0CR"}
|