@sun-asterisk/sungen 3.2.28 → 3.2.29
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/capabilities/discover.d.ts +29 -0
- package/dist/capabilities/discover.d.ts.map +1 -1
- package/dist/capabilities/discover.js +144 -10
- package/dist/capabilities/discover.js.map +1 -1
- package/dist/capabilities/registry.d.ts +18 -0
- package/dist/capabilities/registry.d.ts.map +1 -1
- package/dist/capabilities/registry.js +25 -0
- package/dist/capabilities/registry.js.map +1 -1
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/audit.js +9 -0
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/capability.js +1 -1
- package/dist/cli/commands/capability.js.map +1 -1
- package/dist/dashboard/templates/index.html +1 -1
- package/dist/exporters/feature-parser.d.ts +1 -1
- package/dist/exporters/feature-parser.js +1 -1
- package/dist/exporters/matrix/build.d.ts.map +1 -1
- package/dist/exporters/matrix/build.js +14 -1
- package/dist/exporters/matrix/build.js.map +1 -1
- package/dist/exporters/matrix/item-labels.d.ts +35 -0
- package/dist/exporters/matrix/item-labels.d.ts.map +1 -0
- package/dist/exporters/matrix/item-labels.js +90 -0
- package/dist/exporters/matrix/item-labels.js.map +1 -0
- package/dist/exporters/matrix/render-coverage-sheet.d.ts +14 -0
- package/dist/exporters/matrix/render-coverage-sheet.d.ts.map +1 -0
- package/dist/exporters/matrix/render-coverage-sheet.js +160 -0
- package/dist/exporters/matrix/render-coverage-sheet.js.map +1 -0
- package/dist/exporters/matrix/render-overview-sheet.d.ts +20 -0
- package/dist/exporters/matrix/render-overview-sheet.d.ts.map +1 -0
- package/dist/exporters/matrix/render-overview-sheet.js +36 -0
- package/dist/exporters/matrix/render-overview-sheet.js.map +1 -0
- package/dist/exporters/matrix/render-testcase-sheet.d.ts +30 -0
- package/dist/exporters/matrix/render-testcase-sheet.d.ts.map +1 -0
- package/dist/exporters/matrix/render-testcase-sheet.js +156 -0
- package/dist/exporters/matrix/render-testcase-sheet.js.map +1 -0
- package/dist/exporters/matrix/render-xlsx.d.ts +41 -31
- package/dist/exporters/matrix/render-xlsx.d.ts.map +1 -1
- package/dist/exporters/matrix/render-xlsx.js +72 -425
- package/dist/exporters/matrix/render-xlsx.js.map +1 -1
- package/dist/exporters/matrix/testcase-summary-band.d.ts +30 -0
- package/dist/exporters/matrix/testcase-summary-band.d.ts.map +1 -0
- package/dist/exporters/matrix/testcase-summary-band.js +75 -0
- package/dist/exporters/matrix/testcase-summary-band.js.map +1 -0
- package/dist/exporters/matrix/wording.d.ts.map +1 -1
- package/dist/exporters/matrix/wording.js +13 -0
- package/dist/exporters/matrix/wording.js.map +1 -1
- package/dist/exporters/matrix/xlsx-cells.d.ts +38 -0
- package/dist/exporters/matrix/xlsx-cells.d.ts.map +1 -0
- package/dist/exporters/matrix/xlsx-cells.js +52 -0
- package/dist/exporters/matrix/xlsx-cells.js.map +1 -0
- package/dist/exporters/xlsx-report-builder.d.ts +12 -1
- package/dist/exporters/xlsx-report-builder.d.ts.map +1 -1
- package/dist/exporters/xlsx-report-builder.js +10 -2
- package/dist/exporters/xlsx-report-builder.js.map +1 -1
- package/dist/generators/gherkin-parser/index.d.ts +7 -0
- package/dist/generators/gherkin-parser/index.d.ts.map +1 -1
- package/dist/generators/gherkin-parser/index.js +3 -3
- package/dist/generators/gherkin-parser/index.js.map +1 -1
- package/dist/generators/test-generator/adapters/appium/os-support.d.ts +53 -0
- package/dist/generators/test-generator/adapters/appium/os-support.d.ts.map +1 -0
- package/dist/generators/test-generator/adapters/appium/os-support.js +200 -0
- package/dist/generators/test-generator/adapters/appium/os-support.js.map +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/alert-fill-action.hbs +9 -2
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/capture-variable.hbs +24 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/hover-action.hbs +6 -2
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/hover-element-with-text.hbs +4 -3
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/unknown-element-action.hbs +9 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/have-value-assertion.hbs +34 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/label-value-assertion.hbs +14 -6
- package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/go-back-action.hbs +21 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/set-geolocation-action.hbs +16 -0
- package/dist/generators/test-generator/adapters/appium/templates/test-file.hbs +8 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row-nth.hbs +4 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row.hbs +4 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/selected-assertion.hbs +7 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/state-with-filter-assertion.hbs +7 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-empty.hbs +4 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-match-data.hbs +4 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-count.hbs +4 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-exists.hbs +12 -4
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-not-exists.hbs +14 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +8 -8
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/id.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/testid.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +8 -8
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +5 -1
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/generators/test-generator/diagnostics.d.ts +38 -2
- package/dist/generators/test-generator/diagnostics.d.ts.map +1 -1
- package/dist/generators/test-generator/diagnostics.js +37 -1
- package/dist/generators/test-generator/diagnostics.js.map +1 -1
- package/dist/generators/test-generator/patterns/index.d.ts +33 -6
- package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/index.js +71 -7
- package/dist/generators/test-generator/patterns/index.js.map +1 -1
- package/dist/generators/test-generator/patterns/types.d.ts +8 -0
- package/dist/generators/test-generator/patterns/types.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.d.ts +40 -1
- package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.js +100 -17
- package/dist/generators/test-generator/step-mapper.js.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.d.ts +23 -1
- package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.js +78 -12
- package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
- package/dist/harness/audit.d.ts +3 -1
- package/dist/harness/audit.d.ts.map +1 -1
- package/dist/harness/audit.js +51 -28
- package/dist/harness/audit.js.map +1 -1
- package/dist/harness/capability-plan.d.ts +4 -0
- package/dist/harness/capability-plan.d.ts.map +1 -1
- package/dist/harness/capability-plan.js +32 -2
- package/dist/harness/capability-plan.js.map +1 -1
- package/dist/harness/catalog/drivers.yaml +1 -1
- package/dist/harness/catalog/universal-viewpoints.yaml +145 -0
- package/dist/harness/parse.d.ts +6 -0
- package/dist/harness/parse.d.ts.map +1 -1
- package/dist/harness/parse.js +1 -1
- package/dist/harness/parse.js.map +1 -1
- package/dist/harness/repair.d.ts.map +1 -1
- package/dist/harness/repair.js +8 -1
- package/dist/harness/repair.js.map +1 -1
- package/dist/harness/sensors.d.ts +48 -0
- package/dist/harness/sensors.d.ts.map +1 -1
- package/dist/harness/sensors.js +26 -1
- package/dist/harness/sensors.js.map +1 -1
- package/dist/harness/viewpoint-ledger.d.ts +10 -0
- package/dist/harness/viewpoint-ledger.d.ts.map +1 -1
- package/dist/harness/viewpoint-ledger.js +23 -2
- package/dist/harness/viewpoint-ledger.js.map +1 -1
- package/dist/orchestrator/templates/ai-src/commands/delivery.md +4 -3
- package/dist/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +14 -5
- package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +48 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +18 -3
- package/dist/orchestrator/templates/ai-src/skills/sungen-mobile-gestures/SKILL.md +16 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +11 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-selector-keys/SKILL.md +10 -3
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +88 -2
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +58 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-test-design-techniques/SKILL.md +13 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-viewpoint/SKILL.md +59 -10
- package/dist/orchestrator/templates/ai-src/skills/sungen-viewpoint/group-f-mobile-native.md +419 -0
- package/dist/orchestrator/templates/readme.md +1 -1
- package/dist/orchestrator/templates/specs-pw-shape-reporter.ts +0 -0
- package/dist/orchestrator/templates/wdio.conf.ts +105 -1
- package/package.json +3 -3
- package/src/capabilities/discover.ts +146 -10
- package/src/capabilities/registry.ts +30 -0
- package/src/cli/commands/audit.ts +9 -0
- package/src/cli/commands/capability.ts +1 -1
- package/src/dashboard/templates/index.html +1 -1
- package/src/exporters/feature-parser.ts +1 -1
- package/src/exporters/matrix/build.ts +13 -1
- package/src/exporters/matrix/item-labels.ts +78 -0
- package/src/exporters/matrix/render-coverage-sheet.ts +168 -0
- package/src/exporters/matrix/render-overview-sheet.ts +56 -0
- package/src/exporters/matrix/render-testcase-sheet.ts +178 -0
- package/src/exporters/matrix/render-xlsx.ts +67 -448
- package/src/exporters/matrix/testcase-summary-band.ts +79 -0
- package/src/exporters/matrix/wording.ts +13 -0
- package/src/exporters/matrix/xlsx-cells.ts +56 -0
- package/src/exporters/xlsx-report-builder.ts +16 -2
- package/src/generators/gherkin-parser/index.ts +1 -1
- package/src/generators/test-generator/adapters/appium/os-support.ts +176 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/alert-fill-action.hbs +9 -2
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/capture-variable.hbs +24 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/hover-action.hbs +6 -2
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/hover-element-with-text.hbs +4 -3
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/unknown-element-action.hbs +9 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/have-value-assertion.hbs +34 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/label-value-assertion.hbs +14 -6
- package/src/generators/test-generator/adapters/appium/templates/steps/gestures/go-back-action.hbs +21 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/gestures/set-geolocation-action.hbs +16 -0
- package/src/generators/test-generator/adapters/appium/templates/test-file.hbs +8 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row-nth.hbs +4 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row.hbs +4 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/selected-assertion.hbs +7 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/state-with-filter-assertion.hbs +7 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-empty.hbs +4 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-match-data.hbs +4 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-count.hbs +4 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-exists.hbs +12 -4
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-not-exists.hbs +14 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +8 -8
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/id.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/testid.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +8 -8
- package/src/generators/test-generator/code-generator.ts +5 -1
- package/src/generators/test-generator/diagnostics.ts +39 -2
- package/src/generators/test-generator/patterns/index.ts +78 -8
- package/src/generators/test-generator/patterns/types.ts +8 -0
- package/src/generators/test-generator/step-mapper.ts +107 -17
- package/src/generators/test-generator/utils/selector-resolver.ts +88 -12
- package/src/harness/audit.ts +55 -30
- package/src/harness/capability-plan.ts +33 -1
- package/src/harness/catalog/drivers.yaml +1 -1
- package/src/harness/catalog/universal-viewpoints.yaml +145 -0
- package/src/harness/parse.ts +7 -1
- package/src/harness/repair.ts +8 -1
- package/src/harness/sensors.ts +75 -1
- package/src/harness/viewpoint-ledger.ts +26 -5
- package/src/orchestrator/templates/ai-src/commands/delivery.md +4 -3
- package/src/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +14 -5
- package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +48 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +18 -3
- package/src/orchestrator/templates/ai-src/skills/sungen-mobile-gestures/SKILL.md +16 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +11 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-selector-keys/SKILL.md +10 -3
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +88 -2
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +58 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-test-design-techniques/SKILL.md +13 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-viewpoint/SKILL.md +59 -10
- package/src/orchestrator/templates/ai-src/skills/sungen-viewpoint/group-f-mobile-native.md +419 -0
- package/src/orchestrator/templates/readme.md +1 -1
- package/src/orchestrator/templates/specs-pw-shape-reporter.ts +0 -0
- package/src/orchestrator/templates/wdio.conf.ts +105 -1
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sheet 4 — Coverage: requirement coverage (id ↔ items ↔ status), the
|
|
3
|
+
* target × category grid (with explicit `—` gaps), the mock-verified case
|
|
4
|
+
* tracker, dispositions, and the generation manifest.
|
|
5
|
+
*
|
|
6
|
+
* Reports on the WHOLE model — it is deliberately not split by execution mode,
|
|
7
|
+
* because a requirement is covered or not regardless of who runs the case.
|
|
8
|
+
*
|
|
9
|
+
* Moved out of `render-xlsx.ts` unchanged when the workbook grew to four sheets.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import * as ExcelJS from 'exceljs';
|
|
13
|
+
import { renderReportHeaderBand } from '../xlsx-report-builder';
|
|
14
|
+
import { variantState } from './build';
|
|
15
|
+
import { MatrixModel, MapCategory } from './types';
|
|
16
|
+
import { GAP_FILL, SOFT_BLUE, dataCell, headerCell, draftBanner } from './xlsx-cells';
|
|
17
|
+
|
|
18
|
+
const CATEGORY_ORDER: MapCategory[] = ['normal', 'abnormal', 'security', 'nfr'];
|
|
19
|
+
|
|
20
|
+
export function addCoverageSheet(wb: ExcelJS.Workbook, model: MatrixModel, sungenVersion?: string): void {
|
|
21
|
+
const ws = wb.addWorksheet('Coverage');
|
|
22
|
+
ws.columns = [
|
|
23
|
+
{ width: 3 }, { width: 22 }, { width: 18 }, { width: 16 }, { width: 16 }, { width: 16 },
|
|
24
|
+
{ width: 16 }, { width: 44 },
|
|
25
|
+
];
|
|
26
|
+
renderReportHeaderBand(wb, ws, `${model.unit.toUpperCase()} COVERAGE OVERVIEW`, sungenVersion, model.formNo);
|
|
27
|
+
draftBanner(ws, model);
|
|
28
|
+
|
|
29
|
+
let rowIdx = 7;
|
|
30
|
+
|
|
31
|
+
// --- Performance budget (#569) — advisory: config (qa/perf.yaml) vs measured runs.
|
|
32
|
+
if (model.perf) {
|
|
33
|
+
const p = model.perf;
|
|
34
|
+
dataCell(ws.getRow(rowIdx).getCell(2), 'Performance budget', { bold: true });
|
|
35
|
+
rowIdx++;
|
|
36
|
+
const r = ws.getRow(rowIdx);
|
|
37
|
+
dataCell(r.getCell(2), `p${p.percentile} ${p.metric.replace('_ms', ' time')}`);
|
|
38
|
+
dataCell(r.getCell(3), p.pass ? 'PASS' : 'OVER BUDGET', { bold: true });
|
|
39
|
+
dataCell(r.getCell(4), `measured ${p.measuredMs}ms`);
|
|
40
|
+
dataCell(r.getCell(5), `budget ${p.budgetMs}ms`);
|
|
41
|
+
dataCell(r.getCell(6), `${p.samples} runs`);
|
|
42
|
+
if (!p.pass && p.slowest.length) dataCell(r.getCell(8), `slowest: ${p.slowest.map((x) => `${x.ms}ms ${x.title.slice(0, 40)}`).join(' · ')}`);
|
|
43
|
+
rowIdx += 2;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// --- External Dependency Tracking (#580, ISTQB system-test practice): every case that
|
|
47
|
+
// ran against a MOCK is listed centrally. Its Result stays in the canonical vocabulary
|
|
48
|
+
// (the roll-up formulas depend on it); THIS table is where "Pass (Mocked) ≠ Pass (Real)"
|
|
49
|
+
// is visible — a case verified only against mocks is not Done until re-verified real.
|
|
50
|
+
{
|
|
51
|
+
const mockedVars = model.items.flatMap((it) => it.variants.filter((v) => v.mocked).map((v) => ({ it, v })));
|
|
52
|
+
if (mockedVars.length > 0) {
|
|
53
|
+
dataCell(ws.getRow(rowIdx).getCell(2), 'External Dependency Tracking — mock-verified cases (Pass here = Pass (Mocked), not Pass (Real))', { bold: true });
|
|
54
|
+
rowIdx++;
|
|
55
|
+
const hd = ws.getRow(rowIdx);
|
|
56
|
+
dataCell(hd.getCell(2), 'Variant');
|
|
57
|
+
dataCell(hd.getCell(3), 'Mocked dependency');
|
|
58
|
+
dataCell(hd.getCell(4), 'Delivery item');
|
|
59
|
+
dataCell(hd.getCell(5), 'Result');
|
|
60
|
+
dataCell(hd.getCell(8), 'Real-environment re-verify');
|
|
61
|
+
rowIdx++;
|
|
62
|
+
for (const { it, v } of mockedVars) {
|
|
63
|
+
const r = ws.getRow(rowIdx);
|
|
64
|
+
dataCell(r.getCell(2), v.ref);
|
|
65
|
+
dataCell(r.getCell(3), (v.mockDeps ?? []).join(', ') || '(scenario-level @mock)');
|
|
66
|
+
dataCell(r.getCell(4), it.id);
|
|
67
|
+
dataCell(r.getCell(5), variantState(v));
|
|
68
|
+
dataCell(r.getCell(8), '');
|
|
69
|
+
rowIdx++;
|
|
70
|
+
}
|
|
71
|
+
rowIdx++;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// --- Requirement coverage (review §6): every requirement id gets ONE explicit status.
|
|
76
|
+
if (model.requirements.length > 0) {
|
|
77
|
+
dataCell(ws.getRow(rowIdx).getCell(2), 'Requirement coverage — every id has an explicit status', { bold: true });
|
|
78
|
+
rowIdx += 1;
|
|
79
|
+
const rh = ws.getRow(rowIdx);
|
|
80
|
+
ws.mergeCells(rowIdx, 4, rowIdx, 6);
|
|
81
|
+
headerCell(rh.getCell(2), 'Requirement');
|
|
82
|
+
headerCell(rh.getCell(3), 'Status');
|
|
83
|
+
headerCell(rh.getCell(4), 'Delivery items');
|
|
84
|
+
headerCell(rh.getCell(7), 'Variants');
|
|
85
|
+
headerCell(rh.getCell(8), 'Note');
|
|
86
|
+
rowIdx += 1;
|
|
87
|
+
for (const r of model.requirements) {
|
|
88
|
+
const row = ws.getRow(rowIdx++);
|
|
89
|
+
dataCell(row.getCell(2), r.id, { bold: true });
|
|
90
|
+
dataCell(row.getCell(3), r.status, {
|
|
91
|
+
center: true,
|
|
92
|
+
...(r.status === 'gap' ? { fill: GAP_FILL, bold: true } : {}),
|
|
93
|
+
});
|
|
94
|
+
ws.mergeCells(rowIdx - 1, 4, rowIdx - 1, 6);
|
|
95
|
+
dataCell(row.getCell(4), r.items.join(', '));
|
|
96
|
+
dataCell(row.getCell(7), r.variantCount > 0 ? String(r.variantCount) : '', { center: true });
|
|
97
|
+
dataCell(row.getCell(8), r.note);
|
|
98
|
+
}
|
|
99
|
+
rowIdx += 1;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// --- Target × category grid ---
|
|
103
|
+
dataCell(ws.getRow(rowIdx).getCell(2), 'Coverage matrix — delivery items (coverage variants) per target × category', { bold: true });
|
|
104
|
+
rowIdx += 1;
|
|
105
|
+
const gridHeader = ws.getRow(rowIdx++);
|
|
106
|
+
headerCell(gridHeader.getCell(2), 'Target');
|
|
107
|
+
CATEGORY_ORDER.forEach((cat, i) => headerCell(gridHeader.getCell(3 + i), cat));
|
|
108
|
+
headerCell(gridHeader.getCell(3 + CATEGORY_ORDER.length), 'Total');
|
|
109
|
+
|
|
110
|
+
const targets = Array.from(new Set(model.items.map((i) => i.target)));
|
|
111
|
+
for (const target of targets) {
|
|
112
|
+
const row = ws.getRow(rowIdx++);
|
|
113
|
+
dataCell(row.getCell(2), target, { bold: true });
|
|
114
|
+
let totalItems = 0;
|
|
115
|
+
let totalVariants = 0;
|
|
116
|
+
CATEGORY_ORDER.forEach((cat, i) => {
|
|
117
|
+
const cell = row.getCell(3 + i);
|
|
118
|
+
const items = model.items.filter((it) => it.target === target && it.category === cat);
|
|
119
|
+
const variants = items.reduce((a, it) => a + it.variants.length, 0);
|
|
120
|
+
totalItems += items.length;
|
|
121
|
+
totalVariants += variants;
|
|
122
|
+
// `—` is an explicit statement: this target × category cell has NO coverage.
|
|
123
|
+
dataCell(cell, items.length === 0 ? '—' : `${items.length} (${variants})`, {
|
|
124
|
+
center: true,
|
|
125
|
+
...(items.length === 0 ? { fill: GAP_FILL } : {}),
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
dataCell(row.getCell(3 + CATEGORY_ORDER.length), `${totalItems} (${totalVariants})`, { center: true, bold: true });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// --- Dispositions ---
|
|
132
|
+
if (model.dispositions.length > 0) {
|
|
133
|
+
rowIdx += 1;
|
|
134
|
+
dataCell(ws.getRow(rowIdx).getCell(2), 'Not in the matrix (explicit dispositions)', { bold: true });
|
|
135
|
+
rowIdx += 1;
|
|
136
|
+
const dh = ws.getRow(rowIdx++);
|
|
137
|
+
['Source', 'Disposition', 'Reason', 'Title'].forEach((l, i) => headerCell(dh.getCell(2 + i), l));
|
|
138
|
+
for (const d of model.dispositions) {
|
|
139
|
+
const row = ws.getRow(rowIdx++);
|
|
140
|
+
dataCell(row.getCell(2), d.vpId);
|
|
141
|
+
dataCell(row.getCell(3), d.as, { center: true });
|
|
142
|
+
dataCell(row.getCell(4), d.reason);
|
|
143
|
+
dataCell(row.getCell(5), d.title);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// --- Manifest ---
|
|
148
|
+
rowIdx += 1;
|
|
149
|
+
dataCell(ws.getRow(rowIdx).getCell(2), 'Generation manifest', { bold: true });
|
|
150
|
+
rowIdx += 1;
|
|
151
|
+
const m = model.manifest;
|
|
152
|
+
const manifestRows: Array<[string, string]> = [
|
|
153
|
+
['Unit', m.unit],
|
|
154
|
+
['Approval state', m.approvalState],
|
|
155
|
+
['Delivery items', String(m.itemCount)],
|
|
156
|
+
['Coverage variants', String(m.variantCount)],
|
|
157
|
+
['Source fingerprint', m.sourceFingerprint],
|
|
158
|
+
['Map fingerprint', m.mapFingerprint],
|
|
159
|
+
['Transformer version', m.transformerVersion],
|
|
160
|
+
['Max variants / item (warn)', String(m.thresholds.maxVariantsPerItem)],
|
|
161
|
+
];
|
|
162
|
+
for (const [k, v] of manifestRows) {
|
|
163
|
+
const row = ws.getRow(rowIdx++);
|
|
164
|
+
dataCell(row.getCell(2), k, { bold: true, fill: SOFT_BLUE });
|
|
165
|
+
ws.mergeCells(rowIdx - 1, 3, rowIdx - 1, 7);
|
|
166
|
+
dataCell(row.getCell(3), v);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sheet 1 — Overview: the roll-up table over the Auto and Manual sheets.
|
|
3
|
+
*
|
|
4
|
+
* Thin adapter. The table itself is `addOverviewSheet` in the shared report
|
|
5
|
+
* builder — the same one the legacy path uses — so both delivery formats show
|
|
6
|
+
* the customer one identical summary. All this module does is turn the matrix
|
|
7
|
+
* model plus the per-sheet item projections into the summary shape it wants.
|
|
8
|
+
*
|
|
9
|
+
* Every count in that table is a LIVE reference into the detail sheet's band
|
|
10
|
+
* (`'Auto'!D7`), never a frozen number, so it recomputes when QA edits results.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import * as ExcelJS from 'exceljs';
|
|
14
|
+
import { addOverviewSheet } from '../xlsx-report-builder';
|
|
15
|
+
import { DeliveryItem, MatrixModel } from './types';
|
|
16
|
+
|
|
17
|
+
/** One detail sheet: its tab name and the items projected onto it. */
|
|
18
|
+
export interface SheetProjection {
|
|
19
|
+
sheetName: string;
|
|
20
|
+
items: DeliveryItem[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* `total` counts VARIANTS only — parent rows are delivery items, not executable
|
|
25
|
+
* cases. It must match what the detail band's `C7=COUNTA(K14:K…)` counts, since
|
|
26
|
+
* a cached value and the live formula beside it have to tell one story. See
|
|
27
|
+
* `testcase-summary-band.ts` for why the count anchors on column K.
|
|
28
|
+
*/
|
|
29
|
+
function summaryOf(unit: string, items: DeliveryItem[]): {
|
|
30
|
+
screen: string; total: number; passed: number; failed: number; pending: number; na: number;
|
|
31
|
+
} {
|
|
32
|
+
const sum = (k: 'passed' | 'failed' | 'notRun' | 'na'): number =>
|
|
33
|
+
items.reduce((a, i) => a + i.resultCounts[k], 0);
|
|
34
|
+
return {
|
|
35
|
+
screen: unit,
|
|
36
|
+
total: items.reduce((a, i) => a + i.variants.length, 0),
|
|
37
|
+
passed: sum('passed'),
|
|
38
|
+
failed: sum('failed'),
|
|
39
|
+
pending: sum('notRun'),
|
|
40
|
+
na: sum('na'),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function addMatrixOverviewSheet(
|
|
45
|
+
wb: ExcelJS.Workbook,
|
|
46
|
+
model: MatrixModel,
|
|
47
|
+
sheets: SheetProjection[],
|
|
48
|
+
sungenVersion?: string,
|
|
49
|
+
): void {
|
|
50
|
+
addOverviewSheet(
|
|
51
|
+
wb,
|
|
52
|
+
sheets.map((s) => ({ sheetName: s.sheetName, summary: summaryOf(model.unit, s.items) })),
|
|
53
|
+
sungenVersion,
|
|
54
|
+
{ titleLabel: `${model.unit.toUpperCase()} TEST CASE & COVERAGE MATRIX`, formNo: model.formNo },
|
|
55
|
+
);
|
|
56
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One BM-2-901-13 testcase sheet of the matrix workbook (Auto or Manual).
|
|
3
|
+
*
|
|
4
|
+
* Layout is the company template, cell for cell: masthead rows 1-4, the summary
|
|
5
|
+
* band on rows 6/7/8, the `*: Mandatory` legend on row 12, the 16 column headers
|
|
6
|
+
* `A..P` on row 13, and data from row 14.
|
|
7
|
+
*
|
|
8
|
+
* Two row levels: one parent row per delivery item, each followed by its variant
|
|
9
|
+
* sub-rows on Excel outline level 1 — the sub-row is where source traceability
|
|
10
|
+
* (VP-id), test data and the manual result/evidence entry live (review B-05/B-06).
|
|
11
|
+
* Variant Result cells carry the state dropdown; the parent Result is a live
|
|
12
|
+
* formula over its children, so a parent can never contradict its variants even
|
|
13
|
+
* after QA edits results by hand.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import * as ExcelJS from 'exceljs';
|
|
17
|
+
import { renderReportHeaderBand, ARIAL_FONT } from '../xlsx-report-builder';
|
|
18
|
+
import { redactSecrets } from '../api-testcase-formatter';
|
|
19
|
+
import { clipNoteField } from '../playwright-report-parser';
|
|
20
|
+
import { variantState } from './build';
|
|
21
|
+
import { DeliveryItem, MatrixModel } from './types';
|
|
22
|
+
import { SOFT_BLUE, DRAFT_RED, RESULT_STATES, dataCell, headerCell, draftBanner } from './xlsx-cells';
|
|
23
|
+
import { addSummaryBand } from './testcase-summary-band';
|
|
24
|
+
import {
|
|
25
|
+
coverageSummary, expectedWithVerification, isoDate, itemResultLabel, numbered, variantTypeLabel,
|
|
26
|
+
} from './item-labels';
|
|
27
|
+
|
|
28
|
+
/** Column headers on row 13 — `A..P`, matching the template exactly. */
|
|
29
|
+
export const TESTCASE_HEADERS = [
|
|
30
|
+
'ID*', 'Target', 'Test Intent / Condition', 'Category', 'Priority', 'Precondition',
|
|
31
|
+
'Coverage / Test Data', 'Action / Trigger*', 'Expected results*', 'Trace', 'Testcase type',
|
|
32
|
+
'Test Result*', 'Executed Date*', 'Test Executor*', 'Test Environment',
|
|
33
|
+
'Note\n(Test evidence, DefectID, Actual result)',
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
/** Column widths `A..P`, from the template (H widened — Action / Trigger is the longest cell). */
|
|
37
|
+
const COLUMN_WIDTHS = [20, 12.5, 15.38, 16.25, 17, 25.51, 24.88, 50, 41, 12, 13.5, 15.88, 13.5, 16.5, 23, 29];
|
|
38
|
+
|
|
39
|
+
export const HEADER_ROW = 13;
|
|
40
|
+
/** Column L — the one the band's COUNTIFs and the parent roll-ups read. */
|
|
41
|
+
export const RESULT_COL = 12;
|
|
42
|
+
|
|
43
|
+
/** Variant sub-row — only what distinguishes it from its parent (review GAP-01). */
|
|
44
|
+
function variantValues(item: DeliveryItem, v: DeliveryItem['variants'][number], model: MatrixModel): ExcelJS.CellValue[] {
|
|
45
|
+
return [
|
|
46
|
+
v.ref,
|
|
47
|
+
'',
|
|
48
|
+
v.condition,
|
|
49
|
+
'', '',
|
|
50
|
+
numbered(item.preconditionDeltas[v.ref] ?? []),
|
|
51
|
+
numbered(v.data),
|
|
52
|
+
// Only the steps after the parent's shared prefix, numbered so the parent's
|
|
53
|
+
// steps + these read as one continuous procedure.
|
|
54
|
+
numbered(item.triggerDeltas[v.ref] ?? [], item.trigger.length + 1),
|
|
55
|
+
expectedWithVerification(v.oracle, v.verification),
|
|
56
|
+
v.traces.join(', '),
|
|
57
|
+
variantTypeLabel(v),
|
|
58
|
+
variantState(v),
|
|
59
|
+
isoDate(v.result?.startTime),
|
|
60
|
+
'',
|
|
61
|
+
'',
|
|
62
|
+
// Redact BEFORE clipping — same denylist as the testcases CSV Note column.
|
|
63
|
+
v.result?.error ? clipNoteField(redactSecrets(String(v.result.error), model.secretValues)) : '',
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Live parent roll-up over the child Result cells — precedence
|
|
69
|
+
* failed → blocked → pending → partial → passed. N/A variants leave the
|
|
70
|
+
* denominator (review GAP-06): eff = n − COUNTIF(N/A); all-N/A → "All N/A".
|
|
71
|
+
* The range covers only the children present in THIS sheet, so a mixed item's
|
|
72
|
+
* two parent rows legitimately report two different numbers.
|
|
73
|
+
*/
|
|
74
|
+
function rollUpFormula(n: number, firstChild: number, lastChild: number): string {
|
|
75
|
+
const rng = `L${firstChild}:L${lastChild}`;
|
|
76
|
+
const EFF = `(${n}-COUNTIF(${rng},"N/A"))`;
|
|
77
|
+
const NA = `IF(COUNTIF(${rng},"N/A")>0," · "&COUNTIF(${rng},"N/A")&" N/A","")`;
|
|
78
|
+
return (
|
|
79
|
+
`IF(COUNTIF(${rng},"Failed")>0,COUNTIF(${rng},"Passed")&"/"&${EFF}&" Passed · "&COUNTIF(${rng},"Failed")&" Failed"&${NA},` +
|
|
80
|
+
`IF(COUNTIF(${rng},"Blocked")>0,COUNTIF(${rng},"Passed")&"/"&${EFF}&" Passed · "&COUNTIF(${rng},"Blocked")&" Blocked"&${NA},` +
|
|
81
|
+
`IF(${EFF}=0,"All N/A",` +
|
|
82
|
+
`COUNTIF(${rng},"Passed")&"/"&${EFF}&" Passed"&${NA})))`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Parent row — only values genuinely COMMON to the whole item (review M-04). */
|
|
87
|
+
function parentValues(item: DeliveryItem, formula: string): ExcelJS.CellValue[] {
|
|
88
|
+
return [
|
|
89
|
+
item.id,
|
|
90
|
+
item.target,
|
|
91
|
+
item.intent,
|
|
92
|
+
item.category,
|
|
93
|
+
item.priority,
|
|
94
|
+
numbered(item.precondition),
|
|
95
|
+
coverageSummary(item),
|
|
96
|
+
// The shared leading steps; each variant's remaining steps sit on its sub-row.
|
|
97
|
+
numbered(item.trigger),
|
|
98
|
+
item.oracle,
|
|
99
|
+
item.traces.join(', '),
|
|
100
|
+
// K (Testcase type) stays blank on a parent — it is not an executable case.
|
|
101
|
+
'',
|
|
102
|
+
{ formula, result: itemResultLabel(item) } as ExcelJS.CellValue,
|
|
103
|
+
'',
|
|
104
|
+
'',
|
|
105
|
+
'',
|
|
106
|
+
item.review === 'proposed' ? 'REVIEW REQUIRED' : '',
|
|
107
|
+
];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Append one testcase sheet holding `items` (already projected onto this sheet's
|
|
112
|
+
* execution mode). Always called for both Auto and Manual, even when one side is
|
|
113
|
+
* empty: Overview then always has exactly two rows, and QA has somewhere to add
|
|
114
|
+
* hand-written cases. An empty sheet's band reads 0 and its percentages resolve
|
|
115
|
+
* through IFERROR — never `#DIV/0!`.
|
|
116
|
+
*/
|
|
117
|
+
export function addTestcaseSheet(
|
|
118
|
+
wb: ExcelJS.Workbook,
|
|
119
|
+
sheetName: string,
|
|
120
|
+
model: MatrixModel,
|
|
121
|
+
items: DeliveryItem[],
|
|
122
|
+
sungenVersion?: string,
|
|
123
|
+
): void {
|
|
124
|
+
const ws = wb.addWorksheet(sheetName);
|
|
125
|
+
ws.properties.outlineLevelRow = 1;
|
|
126
|
+
ws.columns = COLUMN_WIDTHS.map((width) => ({ width }));
|
|
127
|
+
|
|
128
|
+
renderReportHeaderBand(wb, ws, `${model.unit.toUpperCase()} TEST CASE & COVERAGE MATRIX`, sungenVersion, model.formNo);
|
|
129
|
+
draftBanner(ws, model);
|
|
130
|
+
addSummaryBand(ws, items);
|
|
131
|
+
|
|
132
|
+
const legend = ws.getCell('A12');
|
|
133
|
+
legend.value = '*: Mandatory';
|
|
134
|
+
legend.font = { italic: true, size: 10, name: ARIAL_FONT, color: { argb: 'FF808080' } };
|
|
135
|
+
|
|
136
|
+
const hr = ws.getRow(HEADER_ROW);
|
|
137
|
+
hr.height = 38;
|
|
138
|
+
TESTCASE_HEADERS.forEach((label, i) => headerCell(hr.getCell(1 + i), label));
|
|
139
|
+
|
|
140
|
+
let rowIdx = HEADER_ROW + 1;
|
|
141
|
+
for (const item of items) {
|
|
142
|
+
const parentRowIdx = rowIdx++;
|
|
143
|
+
|
|
144
|
+
// Variant sub-rows FIRST — the parent's formula needs their cell range.
|
|
145
|
+
const firstChild = rowIdx;
|
|
146
|
+
for (const v of item.variants) {
|
|
147
|
+
const vr = ws.getRow(rowIdx++);
|
|
148
|
+
vr.outlineLevel = 1;
|
|
149
|
+
variantValues(item, v, model).forEach((val, i) => dataCell(vr.getCell(1 + i), val, { center: i === 10 }));
|
|
150
|
+
// Manual-execution entry: constrain the Result cell to the known states.
|
|
151
|
+
vr.getCell(RESULT_COL).dataValidation = {
|
|
152
|
+
type: 'list', allowBlank: true, formulae: [`"${RESULT_STATES}"`],
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const pr = ws.getRow(parentRowIdx);
|
|
157
|
+
const values = parentValues(item, rollUpFormula(item.variants.length, firstChild, rowIdx - 1));
|
|
158
|
+
values.forEach((v, i) => dataCell(pr.getCell(1 + i), v, {
|
|
159
|
+
bold: true,
|
|
160
|
+
fill: SOFT_BLUE,
|
|
161
|
+
center: i === 3 || i === 4,
|
|
162
|
+
...(i === 15 && item.review === 'proposed' ? { color: DRAFT_RED } : {}),
|
|
163
|
+
}));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const lastRow = Math.max(rowIdx - 1, HEADER_ROW);
|
|
167
|
+
ws.autoFilter = { from: { row: HEADER_ROW, column: 1 }, to: { row: lastRow, column: 16 } };
|
|
168
|
+
ws.views = [{ state: 'frozen', xSplit: 0, ySplit: HEADER_ROW }];
|
|
169
|
+
// Print: landscape, fit width only (never squash all rows into one page),
|
|
170
|
+
// repeat the masthead + band + header rows on every page (review GAP-08).
|
|
171
|
+
ws.pageSetup = {
|
|
172
|
+
orientation: 'landscape',
|
|
173
|
+
fitToPage: true,
|
|
174
|
+
fitToWidth: 1,
|
|
175
|
+
fitToHeight: 0,
|
|
176
|
+
printTitlesRow: `1:${HEADER_ROW}`,
|
|
177
|
+
};
|
|
178
|
+
}
|