@sun-asterisk/sungen 3.2.33 → 3.2.34-beta.1
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/cli/commands/audit.d.ts +4 -0
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/audit.js +54 -10
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/capability.d.ts.map +1 -1
- package/dist/cli/commands/capability.js +5 -1
- package/dist/cli/commands/capability.js.map +1 -1
- package/dist/cli/commands/changed.d.ts.map +1 -1
- package/dist/cli/commands/changed.js +2 -3
- package/dist/cli/commands/changed.js.map +1 -1
- package/dist/cli/commands/dashboard.d.ts.map +1 -1
- package/dist/cli/commands/dashboard.js +2 -28
- package/dist/cli/commands/dashboard.js.map +1 -1
- package/dist/cli/commands/defect.d.ts +7 -0
- package/dist/cli/commands/defect.d.ts.map +1 -0
- package/dist/cli/commands/defect.js +68 -0
- package/dist/cli/commands/defect.js.map +1 -0
- package/dist/cli/commands/delivery.d.ts.map +1 -1
- package/dist/cli/commands/delivery.js +75 -42
- package/dist/cli/commands/delivery.js.map +1 -1
- package/dist/cli/commands/results-merge.d.ts +9 -0
- package/dist/cli/commands/results-merge.d.ts.map +1 -1
- package/dist/cli/commands/results-merge.js +7 -0
- package/dist/cli/commands/results-merge.js.map +1 -1
- package/dist/cli/commands/select.d.ts +12 -0
- package/dist/cli/commands/select.d.ts.map +1 -0
- package/dist/cli/commands/select.js +122 -0
- package/dist/cli/commands/select.js.map +1 -0
- package/dist/cli/commands/triage.d.ts +7 -0
- package/dist/cli/commands/triage.d.ts.map +1 -0
- package/dist/cli/commands/triage.js +98 -0
- package/dist/cli/commands/triage.js.map +1 -0
- package/dist/cli/commands/update.d.ts.map +1 -1
- package/dist/cli/commands/update.js +3 -0
- package/dist/cli/commands/update.js.map +1 -1
- package/dist/cli/commands/viewpoint.d.ts +7 -0
- package/dist/cli/commands/viewpoint.d.ts.map +1 -0
- package/dist/cli/commands/viewpoint.js +70 -0
- package/dist/cli/commands/viewpoint.js.map +1 -0
- package/dist/cli/index.js +13 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/dashboard/templates/index.html +1 -1
- package/dist/dashboard/types.d.ts +1 -0
- package/dist/dashboard/types.d.ts.map +1 -1
- package/dist/exporters/csv-exporter.d.ts +4 -0
- package/dist/exporters/csv-exporter.d.ts.map +1 -1
- package/dist/exporters/csv-exporter.js +12 -11
- package/dist/exporters/csv-exporter.js.map +1 -1
- package/dist/exporters/feature-parser.d.ts +8 -0
- package/dist/exporters/feature-parser.d.ts.map +1 -1
- package/dist/exporters/feature-parser.js +12 -0
- package/dist/exporters/feature-parser.js.map +1 -1
- package/dist/exporters/json-exporter.d.ts.map +1 -1
- package/dist/exporters/json-exporter.js +7 -0
- package/dist/exporters/json-exporter.js.map +1 -1
- package/dist/exporters/manual-hybrid-oracle.d.ts +54 -0
- package/dist/exporters/manual-hybrid-oracle.d.ts.map +1 -0
- package/dist/exporters/manual-hybrid-oracle.js +114 -0
- package/dist/exporters/manual-hybrid-oracle.js.map +1 -0
- package/dist/exporters/matrix/build.d.ts.map +1 -1
- package/dist/exporters/matrix/build.js +55 -12
- package/dist/exporters/matrix/build.js.map +1 -1
- package/dist/exporters/matrix/carry-manual-results.d.ts +19 -0
- package/dist/exporters/matrix/carry-manual-results.d.ts.map +1 -1
- package/dist/exporters/matrix/carry-manual-results.js +76 -6
- package/dist/exporters/matrix/carry-manual-results.js.map +1 -1
- package/dist/exporters/matrix/export.d.ts +8 -2
- package/dist/exporters/matrix/export.d.ts.map +1 -1
- package/dist/exporters/matrix/export.js +15 -3
- package/dist/exporters/matrix/export.js.map +1 -1
- package/dist/exporters/matrix/gates.js +6 -0
- package/dist/exporters/matrix/gates.js.map +1 -1
- package/dist/exporters/matrix/render-csv.js +1 -1
- package/dist/exporters/matrix/render-csv.js.map +1 -1
- package/dist/exporters/matrix/render-testcase-sheet.d.ts.map +1 -1
- package/dist/exporters/matrix/render-testcase-sheet.js +10 -4
- package/dist/exporters/matrix/render-testcase-sheet.js.map +1 -1
- package/dist/exporters/matrix/render-xlsx.d.ts.map +1 -1
- package/dist/exporters/matrix/render-xlsx.js +3 -0
- package/dist/exporters/matrix/render-xlsx.js.map +1 -1
- package/dist/exporters/matrix/types.d.ts +13 -1
- package/dist/exporters/matrix/types.d.ts.map +1 -1
- package/dist/exporters/matrix/types.js.map +1 -1
- package/dist/exporters/matrix/wording.d.ts +1 -1
- package/dist/exporters/matrix/wording.d.ts.map +1 -1
- package/dist/exporters/matrix/wording.js +10 -1
- package/dist/exporters/matrix/wording.js.map +1 -1
- package/dist/exporters/playwright-report-parser.d.ts.map +1 -1
- package/dist/exporters/playwright-report-parser.js +1 -0
- package/dist/exporters/playwright-report-parser.js.map +1 -1
- package/dist/exporters/run-environment.d.ts +32 -0
- package/dist/exporters/run-environment.d.ts.map +1 -0
- package/dist/exporters/run-environment.js +125 -0
- package/dist/exporters/run-environment.js.map +1 -0
- package/dist/exporters/scenario-merger.d.ts +18 -0
- package/dist/exporters/scenario-merger.d.ts.map +1 -1
- package/dist/exporters/scenario-merger.js +49 -3
- package/dist/exporters/scenario-merger.js.map +1 -1
- package/dist/exporters/test-data-resolver.d.ts +0 -4
- package/dist/exporters/test-data-resolver.d.ts.map +1 -1
- package/dist/exporters/test-data-resolver.js +0 -7
- package/dist/exporters/test-data-resolver.js.map +1 -1
- package/dist/exporters/types.d.ts +5 -0
- package/dist/exporters/types.d.ts.map +1 -1
- package/dist/generators/gherkin-parser/index.d.ts +11 -4
- package/dist/generators/gherkin-parser/index.d.ts.map +1 -1
- package/dist/generators/gherkin-parser/index.js +49 -19
- package/dist/generators/gherkin-parser/index.js.map +1 -1
- package/dist/generators/test-generator/adapters/adapter-interface.d.ts +4 -0
- package/dist/generators/test-generator/adapters/adapter-interface.d.ts.map +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/click-select-action.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/select-action.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/pick-option.hbs +13 -0
- package/dist/generators/test-generator/adapters/playwright/templates/test-file.hbs +16 -0
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +23 -2
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/generators/test-generator/diagnostics.d.ts +3 -1
- package/dist/generators/test-generator/diagnostics.d.ts.map +1 -1
- package/dist/generators/test-generator/diagnostics.js +3 -1
- package/dist/generators/test-generator/diagnostics.js.map +1 -1
- package/dist/harness/audit.d.ts +3 -0
- package/dist/harness/audit.d.ts.map +1 -1
- package/dist/harness/audit.js +48 -15
- package/dist/harness/audit.js.map +1 -1
- package/dist/harness/counted-assertions.d.ts +2 -0
- package/dist/harness/counted-assertions.d.ts.map +1 -0
- package/dist/harness/counted-assertions.js +38 -0
- package/dist/harness/counted-assertions.js.map +1 -0
- package/dist/harness/data-driven-lint.d.ts.map +1 -1
- package/dist/harness/data-driven-lint.js +69 -27
- package/dist/harness/data-driven-lint.js.map +1 -1
- package/dist/harness/defect-draft.d.ts +14 -0
- package/dist/harness/defect-draft.d.ts.map +1 -0
- package/dist/harness/defect-draft.js +143 -0
- package/dist/harness/defect-draft.js.map +1 -0
- package/dist/harness/defects.d.ts +24 -0
- package/dist/harness/defects.d.ts.map +1 -0
- package/dist/harness/defects.js +118 -0
- package/dist/harness/defects.js.map +1 -0
- package/dist/harness/manifest.d.ts.map +1 -1
- package/dist/harness/manifest.js +2 -2
- package/dist/harness/manifest.js.map +1 -1
- package/dist/harness/next-step.d.ts +2 -4
- package/dist/harness/next-step.d.ts.map +1 -1
- package/dist/harness/next-step.js +50 -5
- package/dist/harness/next-step.js.map +1 -1
- package/dist/harness/parse.d.ts +45 -0
- package/dist/harness/parse.d.ts.map +1 -1
- package/dist/harness/parse.js +142 -24
- package/dist/harness/parse.js.map +1 -1
- package/dist/harness/quality-gates.d.ts.map +1 -1
- package/dist/harness/quality-gates.js +6 -4
- package/dist/harness/quality-gates.js.map +1 -1
- package/dist/harness/read-text.d.ts +15 -0
- package/dist/harness/read-text.d.ts.map +1 -1
- package/dist/harness/read-text.js +25 -0
- package/dist/harness/read-text.js.map +1 -1
- package/dist/harness/script-check.d.ts.map +1 -1
- package/dist/harness/script-check.js +7 -1
- package/dist/harness/script-check.js.map +1 -1
- package/dist/harness/select-by-ids.d.ts +31 -0
- package/dist/harness/select-by-ids.d.ts.map +1 -0
- package/dist/harness/select-by-ids.js +106 -0
- package/dist/harness/select-by-ids.js.map +1 -0
- package/dist/harness/sensors.d.ts +8 -1
- package/dist/harness/sensors.d.ts.map +1 -1
- package/dist/harness/sensors.js +92 -21
- package/dist/harness/sensors.js.map +1 -1
- package/dist/harness/spec-branches.js +1 -1
- package/dist/harness/spec-branches.js.map +1 -1
- package/dist/harness/triage-signals.d.ts +20 -0
- package/dist/harness/triage-signals.d.ts.map +1 -0
- package/dist/harness/triage-signals.js +30 -0
- package/dist/harness/triage-signals.js.map +1 -0
- package/dist/harness/triage-unit.d.ts +23 -0
- package/dist/harness/triage-unit.d.ts.map +1 -0
- package/dist/harness/triage-unit.js +142 -0
- package/dist/harness/triage-unit.js.map +1 -0
- package/dist/harness/triage.d.ts +28 -0
- package/dist/harness/triage.d.ts.map +1 -0
- package/dist/harness/triage.js +99 -0
- package/dist/harness/triage.js.map +1 -0
- package/dist/harness/ui-inventory.d.ts +40 -0
- package/dist/harness/ui-inventory.d.ts.map +1 -0
- package/dist/harness/ui-inventory.js +191 -0
- package/dist/harness/ui-inventory.js.map +1 -0
- package/dist/harness/viewpoint-baseline.d.ts.map +1 -1
- package/dist/harness/viewpoint-baseline.js +7 -1
- package/dist/harness/viewpoint-baseline.js.map +1 -1
- package/dist/harness/viewpoint-draft-header.d.ts +7 -0
- package/dist/harness/viewpoint-draft-header.d.ts.map +1 -0
- package/dist/harness/viewpoint-draft-header.js +100 -0
- package/dist/harness/viewpoint-draft-header.js.map +1 -0
- package/dist/harness/viewpoint-ledger.d.ts +13 -3
- package/dist/harness/viewpoint-ledger.d.ts.map +1 -1
- package/dist/harness/viewpoint-ledger.js +104 -18
- package/dist/harness/viewpoint-ledger.js.map +1 -1
- package/dist/harness/viewpoint-mode.d.ts +9 -0
- package/dist/harness/viewpoint-mode.d.ts.map +1 -0
- package/dist/harness/viewpoint-mode.js +52 -0
- package/dist/harness/viewpoint-mode.js.map +1 -0
- package/dist/harness/viewpoint-priority.d.ts +19 -0
- package/dist/harness/viewpoint-priority.d.ts.map +1 -0
- package/dist/harness/viewpoint-priority.js +36 -0
- package/dist/harness/viewpoint-priority.js.map +1 -0
- package/dist/harness/viewpoint-review-open.d.ts +6 -0
- package/dist/harness/viewpoint-review-open.d.ts.map +1 -0
- package/dist/harness/viewpoint-review-open.js +90 -0
- package/dist/harness/viewpoint-review-open.js.map +1 -0
- package/dist/harness/viewpoint-scan-refs.d.ts +50 -0
- package/dist/harness/viewpoint-scan-refs.d.ts.map +1 -0
- package/dist/harness/viewpoint-scan-refs.js +200 -0
- package/dist/harness/viewpoint-scan-refs.js.map +1 -0
- package/dist/harness/viewpoint-scan.d.ts +41 -0
- package/dist/harness/viewpoint-scan.d.ts.map +1 -0
- package/dist/harness/viewpoint-scan.js +118 -0
- package/dist/harness/viewpoint-scan.js.map +1 -0
- package/dist/orchestrator/flow-manager.d.ts +0 -1
- package/dist/orchestrator/flow-manager.d.ts.map +1 -1
- package/dist/orchestrator/flow-manager.js +3 -52
- package/dist/orchestrator/flow-manager.js.map +1 -1
- package/dist/orchestrator/screen-manager.d.ts.map +1 -1
- package/dist/orchestrator/screen-manager.js +8 -62
- package/dist/orchestrator/screen-manager.js.map +1 -1
- package/dist/orchestrator/templates/ai-src/agents/discovery.md +11 -3
- package/dist/orchestrator/templates/ai-src/agents/reviewer.md +8 -0
- package/dist/orchestrator/templates/ai-src/commands/create-test.md +32 -24
- package/dist/orchestrator/templates/ai-src/commands/review.md +2 -2
- package/dist/orchestrator/templates/ai-src/commands/run-test.md +78 -15
- package/dist/orchestrator/templates/ai-src/skills/sungen-capture/mode-local.md +2 -2
- package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +22 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +18 -9
- package/dist/orchestrator/templates/ai-src/skills/sungen-harness-audit/SKILL.md +5 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +58 -14
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +42 -2
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +7 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-viewpoint/SKILL.md +18 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-viewpoint/review-checklist.md +63 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-viewpoint/viewpoint-template.md +158 -0
- package/dist/orchestrator/templates/playwright.config.ts +4 -2
- package/dist/orchestrator/templates/specs-url-assert.d.ts.map +1 -1
- package/dist/orchestrator/templates/specs-url-assert.js +24 -2
- package/dist/orchestrator/templates/specs-url-assert.js.map +1 -1
- package/dist/orchestrator/templates/specs-url-assert.ts +23 -2
- package/dist/orchestrator/trace-mode-install.d.ts +14 -0
- package/dist/orchestrator/trace-mode-install.d.ts.map +1 -0
- package/dist/orchestrator/trace-mode-install.js +132 -0
- package/dist/orchestrator/trace-mode-install.js.map +1 -0
- package/dist/orchestrator/viewpoint-pattern-catalog.d.ts +23 -0
- package/dist/orchestrator/viewpoint-pattern-catalog.d.ts.map +1 -0
- package/dist/orchestrator/viewpoint-pattern-catalog.js +48 -0
- package/dist/orchestrator/viewpoint-pattern-catalog.js.map +1 -0
- package/dist/orchestrator/viewpoint-scaffold.d.ts +3 -0
- package/dist/orchestrator/viewpoint-scaffold.d.ts.map +1 -0
- package/dist/orchestrator/viewpoint-scaffold.js +70 -0
- package/dist/orchestrator/viewpoint-scaffold.js.map +1 -0
- package/package.json +3 -3
- package/src/cli/commands/audit.ts +49 -10
- package/src/cli/commands/capability.ts +3 -1
- package/src/cli/commands/changed.ts +2 -4
- package/src/cli/commands/dashboard.ts +1 -26
- package/src/cli/commands/defect.ts +49 -0
- package/src/cli/commands/delivery.ts +70 -42
- package/src/cli/commands/results-merge.ts +7 -1
- package/src/cli/commands/select.ts +88 -0
- package/src/cli/commands/triage.ts +57 -0
- package/src/cli/commands/update.ts +3 -0
- package/src/cli/commands/viewpoint.ts +58 -0
- package/src/cli/index.ts +13 -2
- package/src/dashboard/templates/index.html +1 -1
- package/src/dashboard/types.ts +1 -0
- package/src/exporters/csv-exporter.ts +12 -11
- package/src/exporters/feature-parser.ts +12 -0
- package/src/exporters/json-exporter.ts +6 -1
- package/src/exporters/manual-hybrid-oracle.ts +112 -0
- package/src/exporters/matrix/build.ts +57 -11
- package/src/exporters/matrix/carry-manual-results.ts +70 -7
- package/src/exporters/matrix/export.ts +16 -2
- package/src/exporters/matrix/gates.ts +6 -0
- package/src/exporters/matrix/render-csv.ts +1 -1
- package/src/exporters/matrix/render-testcase-sheet.ts +10 -4
- package/src/exporters/matrix/render-xlsx.ts +3 -0
- package/src/exporters/matrix/types.ts +13 -1
- package/src/exporters/matrix/wording.ts +10 -1
- package/src/exporters/playwright-report-parser.ts +1 -0
- package/src/exporters/run-environment.ts +89 -0
- package/src/exporters/scenario-merger.ts +51 -5
- package/src/exporters/test-data-resolver.ts +0 -6
- package/src/exporters/types.ts +5 -0
- package/src/generators/gherkin-parser/index.ts +50 -18
- package/src/generators/test-generator/adapters/adapter-interface.ts +4 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/click-select-action.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/select-action.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/pick-option.hbs +13 -0
- package/src/generators/test-generator/adapters/playwright/templates/test-file.hbs +16 -0
- package/src/generators/test-generator/code-generator.ts +23 -2
- package/src/generators/test-generator/diagnostics.ts +3 -1
- package/src/harness/audit.ts +52 -17
- package/src/harness/counted-assertions.ts +33 -0
- package/src/harness/data-driven-lint.ts +82 -24
- package/src/harness/defect-draft.ts +115 -0
- package/src/harness/defects.ts +94 -0
- package/src/harness/manifest.ts +2 -2
- package/src/harness/next-step.ts +47 -5
- package/src/harness/parse.ts +151 -27
- package/src/harness/quality-gates.ts +6 -4
- package/src/harness/read-text.ts +25 -0
- package/src/harness/script-check.ts +7 -1
- package/src/harness/select-by-ids.ts +83 -0
- package/src/harness/sensors.ts +91 -21
- package/src/harness/spec-branches.ts +1 -1
- package/src/harness/triage-signals.ts +46 -0
- package/src/harness/triage-unit.ts +113 -0
- package/src/harness/triage.ts +127 -0
- package/src/harness/ui-inventory.ts +164 -0
- package/src/harness/viewpoint-baseline.ts +7 -1
- package/src/harness/viewpoint-draft-header.ts +55 -0
- package/src/harness/viewpoint-ledger.ts +111 -24
- package/src/harness/viewpoint-mode.ts +49 -0
- package/src/harness/viewpoint-priority.ts +38 -0
- package/src/harness/viewpoint-review-open.ts +51 -0
- package/src/harness/viewpoint-scan-refs.ts +154 -0
- package/src/harness/viewpoint-scan.ts +111 -0
- package/src/orchestrator/flow-manager.ts +3 -53
- package/src/orchestrator/screen-manager.ts +7 -61
- package/src/orchestrator/templates/ai-src/agents/discovery.md +11 -3
- package/src/orchestrator/templates/ai-src/agents/reviewer.md +8 -0
- package/src/orchestrator/templates/ai-src/commands/create-test.md +32 -24
- package/src/orchestrator/templates/ai-src/commands/review.md +2 -2
- package/src/orchestrator/templates/ai-src/commands/run-test.md +78 -15
- package/src/orchestrator/templates/ai-src/skills/sungen-capture/mode-local.md +2 -2
- package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +22 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +18 -9
- package/src/orchestrator/templates/ai-src/skills/sungen-harness-audit/SKILL.md +5 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +58 -14
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +42 -2
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +7 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-viewpoint/SKILL.md +18 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-viewpoint/review-checklist.md +63 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-viewpoint/viewpoint-template.md +158 -0
- package/src/orchestrator/templates/playwright.config.ts +4 -2
- package/src/orchestrator/templates/specs-url-assert.ts +23 -2
- package/src/orchestrator/trace-mode-install.ts +94 -0
- package/src/orchestrator/viewpoint-pattern-catalog.ts +61 -0
- package/src/orchestrator/viewpoint-scaffold.ts +36 -0
|
@@ -6,9 +6,8 @@
|
|
|
6
6
|
import { Command } from 'commander';
|
|
7
7
|
import * as path from 'path';
|
|
8
8
|
import * as fs from 'fs';
|
|
9
|
-
import { execSync } from 'child_process';
|
|
10
9
|
import { parse as parseYaml } from 'yaml';
|
|
11
|
-
import { parseFeatureMetadata } from '../../exporters/feature-parser';
|
|
10
|
+
import { isHookScenario, parseFeatureMetadata } from '../../exporters/feature-parser';
|
|
12
11
|
import { parseSpecFile } from '../../exporters/spec-parser';
|
|
13
12
|
import { loadTestData } from '../../exporters/test-data-resolver';
|
|
14
13
|
import { loadSelectorKeyMap } from '../../exporters/selector-key-resolver';
|
|
@@ -21,10 +20,10 @@ import {
|
|
|
21
20
|
renderCsv,
|
|
22
21
|
writeCsv,
|
|
23
22
|
} from '../../exporters/csv-exporter';
|
|
24
|
-
import { renderXlsxMultiSheet, writeXlsx, buildApiDetailRows, addApiDetailSheet } from '../../exporters/xlsx-exporter';
|
|
23
|
+
import { renderXlsxMultiSheet, writeXlsx, buildApiDetailRows, addApiDetailSheet, deliverableXlsxPath } from '../../exporters/xlsx-exporter';
|
|
25
24
|
import { loadApiCatalog } from '../../exporters/api-catalog-loader';
|
|
26
25
|
import { resolveApiEnvironment } from '../../exporters/api-environment';
|
|
27
|
-
import {
|
|
26
|
+
import { getEnvironment, platformOfResultFile, RunEnvironment } from '../../exporters/run-environment';
|
|
28
27
|
import { resolvePlatformAppId } from '../../exporters/mobile-app-id';
|
|
29
28
|
import { scanTestDataSecrets, formatSecretScan, type SecretScanResult } from '../../harness/secret-scan';
|
|
30
29
|
import { EnvironmentInfo, PreflightCheck, ScreenSummary, TestCaseRow, ApiCatalogEntry } from '../../exporters/types';
|
|
@@ -39,7 +38,7 @@ import {
|
|
|
39
38
|
hasReview,
|
|
40
39
|
} from '../../exporters/matrix/export';
|
|
41
40
|
import { MatrixFinding } from '../../exporters/matrix/types';
|
|
42
|
-
import { ManualCarryReport } from '../../exporters/matrix/carry-manual-results';
|
|
41
|
+
import { applyManualCarry, ManualCarryReport, PriorManualResults, readPriorManualResults } from '../../exporters/matrix/carry-manual-results';
|
|
43
42
|
import { formatTestOutcome, resultTotals } from '../../exporters/matrix/result-totals';
|
|
44
43
|
import { readCapabilities } from '../../harness/capability';
|
|
45
44
|
|
|
@@ -86,6 +85,12 @@ export interface DeliveryTarget {
|
|
|
86
85
|
isFlow: boolean;
|
|
87
86
|
}
|
|
88
87
|
|
|
88
|
+
/** The command that regenerates a target's unit — flag by kind, the unit (not feature) name. */
|
|
89
|
+
function generateCommandFor(target: DeliveryTarget): string {
|
|
90
|
+
const flag = target.kind === 'flow' ? '--flow' : target.kind === 'api' ? '--api' : '--screen';
|
|
91
|
+
return `sungen generate ${flag} ${target.screen}`;
|
|
92
|
+
}
|
|
93
|
+
|
|
89
94
|
function makeTarget(screen: string, featureBaseName: string, kind: UnitKind): DeliveryTarget {
|
|
90
95
|
return { screen, featureBaseName, name: featureBaseName, kind, isFlow: kind === 'flow' };
|
|
91
96
|
}
|
|
@@ -342,34 +347,27 @@ export function hasBlockingMissing(p: PreflightCheck): boolean {
|
|
|
342
347
|
// Environment
|
|
343
348
|
// ----------------------------------------------------------------------------
|
|
344
349
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
if (fs.existsSync(configPath)) {
|
|
350
|
-
const c = fs.readFileSync(configPath, 'utf-8');
|
|
351
|
-
// Both config shapes: the legacy literal `baseURL: 'x'` and the env-aware
|
|
352
|
-
// `baseURL: resolveBaseUrl('x')` (the quoted value is the static fallback).
|
|
353
|
-
const baseMatch = c.match(/baseURL:\s*(?:resolveBaseUrl\(\s*)?['"]([^'"]+)['"]/);
|
|
354
|
-
if (baseMatch) baseURL = baseMatch[1];
|
|
355
|
-
const projMatch = c.match(/name:\s*['"]([^'"]+)['"]/);
|
|
356
|
-
if (projMatch) projectName = projMatch[1];
|
|
357
|
-
}
|
|
358
|
-
// SUNGEN_BASE_URL (shell > .env.qa.<SUNGEN_ENV> > .env.qa) overrides the static
|
|
359
|
-
// value — the Test Environment column must show the host the run actually hit.
|
|
360
|
-
baseURL = resolveWebBaseUrl(cwd, baseURL);
|
|
361
|
-
|
|
362
|
-
let executor = process.env.CI_USER || process.env.USER || '';
|
|
363
|
-
try {
|
|
364
|
-
const gitUser = execSync('git config user.name', { cwd, encoding: 'utf-8' }).trim();
|
|
365
|
-
if (gitUser) executor = gitUser;
|
|
366
|
-
} catch {
|
|
367
|
-
// ignore
|
|
368
|
-
}
|
|
350
|
+
/** The unit's path for the Environment column — a flow records it in its page selector. */
|
|
351
|
+
function targetFeaturePath(target: DeliveryTarget, declared: string | undefined, selectorsFile: string): string | undefined {
|
|
352
|
+
return declared || (target.isFlow ? readFlowPagePath(selectorsFile) : undefined);
|
|
353
|
+
}
|
|
369
354
|
|
|
370
|
-
|
|
355
|
+
/**
|
|
356
|
+
* Executor + environment for the matrix's run-result rows (#850) — resolved exactly as the
|
|
357
|
+
* legacy workbook resolves them: the API datasource host for an api unit, and on mobile the
|
|
358
|
+
* app id of the OS the result file came from (`<f>-test-result.<os>[.<env>].json`).
|
|
359
|
+
*/
|
|
360
|
+
function matrixRunEnv(cwd: string, target: DeliveryTarget, env: EnvironmentInfo, paths: MatrixTargetPaths): RunEnvironment {
|
|
361
|
+
const base = qaDir(cwd, target);
|
|
362
|
+
let declared: string | undefined;
|
|
363
|
+
try { declared = parseFeatureMetadata(paths.featureFile).featurePath; } catch { /* loadMatrixModel reports it */ }
|
|
364
|
+
const featurePath = targetFeaturePath(target, declared, path.join(base, 'selectors', `${target.featureBaseName}.yaml`));
|
|
365
|
+
const apiEnv = target.kind === 'api' ? resolveApiEnvironment(cwd, loadApiCatalog(cwd, target.kind, target.screen)) : null;
|
|
366
|
+
const os = platformOfResultFile(paths.resultsPath) ?? process.env.MOBILE_PLATFORM ?? process.env.SUNGEN_ENV;
|
|
367
|
+
return { ...env, ...(apiEnv ?? {}), featurePath: resolvePlatformAppId(featurePath, os) };
|
|
371
368
|
}
|
|
372
369
|
|
|
370
|
+
|
|
373
371
|
/**
|
|
374
372
|
* True when this is a mobile project (`platform: mobile` in qa/capabilities.yaml).
|
|
375
373
|
* Mobile results are written PER-OS (`<feature>-test-result.android|ios.json`) —
|
|
@@ -405,6 +403,18 @@ function removeBaseDeliverables(cwd: string, featureBaseName: string): void {
|
|
|
405
403
|
// Per-screen export
|
|
406
404
|
// ----------------------------------------------------------------------------
|
|
407
405
|
|
|
406
|
+
/**
|
|
407
|
+
* #810 — a manual oracle that lives only in unlabelled `Tester verifies:` prose is exported
|
|
408
|
+
* verbatim into Expected (never dropped), and the author is told to structure it.
|
|
409
|
+
*/
|
|
410
|
+
function warnUnlabeledManualOracles(label: string, merged: ReturnType<typeof mergeFeatureAndSpec>): void {
|
|
411
|
+
const hits = merged.filter((m) => m.unlabeledManualOracle);
|
|
412
|
+
if (!hits.length) return;
|
|
413
|
+
log(`${COLOR.yellow}⚠ ${label}: manual oracle found only in an unlabeled comment (${hits.length})${COLOR.reset} — exported verbatim into Expected; write it as numbered "N. Observable: …" lines:`);
|
|
414
|
+
for (const m of hits.slice(0, 10)) log(` ${m.feature.name.slice(0, 90)}`);
|
|
415
|
+
if (hits.length > 10) log(` … and ${hits.length - 10} more`);
|
|
416
|
+
}
|
|
417
|
+
|
|
408
418
|
async function exportTarget(
|
|
409
419
|
cwd: string,
|
|
410
420
|
target: DeliveryTarget,
|
|
@@ -423,14 +433,12 @@ async function exportTarget(
|
|
|
423
433
|
const spec = parseSpecFile(specFile);
|
|
424
434
|
|
|
425
435
|
const merged = mergeFeatureAndSpec(feature, spec);
|
|
436
|
+
warnUnlabeledManualOracles(label, merged);
|
|
426
437
|
// Screens get their URL path from the .feature file directly. Flows record
|
|
427
438
|
// it in selectors/<feature>.yaml under the page-selector block — fall back
|
|
428
439
|
// to that so the Environment column shows a full URL for flows too.
|
|
429
|
-
let featurePath = feature.featurePath;
|
|
430
440
|
const selectorsFile = path.join(base, 'selectors', `${target.featureBaseName}.yaml`);
|
|
431
|
-
|
|
432
|
-
featurePath = readFlowPagePath(selectorsFile);
|
|
433
|
-
}
|
|
441
|
+
const featurePath = targetFeaturePath(target, feature.featurePath, selectorsFile);
|
|
434
442
|
const selectorKeyMap = loadSelectorKeyMap(selectorsFile);
|
|
435
443
|
|
|
436
444
|
const specLink = fs.existsSync(specMdFile) ? path.relative(cwd, specMdFile) : '';
|
|
@@ -462,6 +470,7 @@ async function exportTarget(
|
|
|
462
470
|
if (isMobileProject(cwd)) removeBaseDeliverables(cwd, target.featureBaseName);
|
|
463
471
|
const rows = buildTestCaseRows({
|
|
464
472
|
screen: label,
|
|
473
|
+
generateCommand: generateCommandFor(target),
|
|
465
474
|
featureName: feature.featureName,
|
|
466
475
|
// OS-precise env column: a mobile dual-id Path resolves to just the
|
|
467
476
|
// android package / ios bundle id for SUNGEN_ENV=android|ios.
|
|
@@ -520,6 +529,7 @@ async function exportTarget(
|
|
|
520
529
|
for (const variant of osVariants) {
|
|
521
530
|
const variantRows = buildTestCaseRows({
|
|
522
531
|
screen: label,
|
|
532
|
+
generateCommand: generateCommandFor(target),
|
|
523
533
|
featureName: feature.featureName,
|
|
524
534
|
featurePath: resolvePlatformAppId(featurePath, variant.locale), // OS-precise app id
|
|
525
535
|
merged,
|
|
@@ -572,6 +582,7 @@ async function exportTarget(
|
|
|
572
582
|
const variantResults = variant.resultsPath ? loadPlaywrightReport(variant.resultsPath) : null;
|
|
573
583
|
const variantRows = buildTestCaseRows({
|
|
574
584
|
screen: label,
|
|
585
|
+
generateCommand: generateCommandFor(target),
|
|
575
586
|
featureName: feature.featureName,
|
|
576
587
|
// No-op for web (URL Path); for a mobile fall-through (a no-suffix result
|
|
577
588
|
// json) this collapses the dual-id to the android package so the combined
|
|
@@ -673,17 +684,21 @@ async function exportMatrixTarget(
|
|
|
673
684
|
cwd: string,
|
|
674
685
|
target: DeliveryTarget,
|
|
675
686
|
opts: { check?: boolean; preview?: boolean; format?: MatrixFormat },
|
|
687
|
+
env: EnvironmentInfo,
|
|
676
688
|
): Promise<MatrixRunSummary | 'skipped' | null> {
|
|
677
689
|
const paths = matrixPathsFor(cwd, target);
|
|
690
|
+
paths.runEnv = matrixRunEnv(cwd, target, env, paths);
|
|
678
691
|
|
|
679
692
|
// A feature whose scenarios are ALL @exploration probes has nothing to deliver —
|
|
680
693
|
// skip it quietly (no map required) instead of demanding a reviewed grouping for
|
|
681
694
|
// probes that are, by contract, outside the official suite.
|
|
682
695
|
{
|
|
683
696
|
const featureMeta = parseFeatureMetadata(paths.featureFile);
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
697
|
+
// Hooks are setup/teardown, never test cases — they neither need nor make a deliverable (#880).
|
|
698
|
+
const scenarios = featureMeta.scenarios.filter((s) => !isHookScenario(s));
|
|
699
|
+
const deliverable = scenarios.filter((s) => !s.tags.some((t) => /^@exploration\b/i.test(t)));
|
|
700
|
+
if (scenarios.length > 0 && deliverable.length === 0) {
|
|
701
|
+
log(`${COLOR.gray}${paths.label}: nothing to deliver — all ${scenarios.length} scenario(s) are @exploration probes (outside the official suite).${COLOR.reset}`);
|
|
687
702
|
return 'skipped';
|
|
688
703
|
}
|
|
689
704
|
}
|
|
@@ -703,6 +718,17 @@ async function exportMatrixTarget(
|
|
|
703
718
|
return null;
|
|
704
719
|
}
|
|
705
720
|
|
|
721
|
+
// The tester's recorded Manual results are part of the outcome: read them BEFORE anything is
|
|
722
|
+
// counted, so this line, the workbook's totals and the summary table all agree (#891). A file
|
|
723
|
+
// that cannot be read is reported where it matters — when it would be overwritten.
|
|
724
|
+
const format = opts.format ?? 'xlsx';
|
|
725
|
+
let prior: PriorManualResults | null = null;
|
|
726
|
+
let priorError: Error | null = null;
|
|
727
|
+
try { prior = await readPriorManualResults(deliverableXlsxPath(cwd, paths.unit)); }
|
|
728
|
+
// A CSV-only export overwrites no workbook, so an unreadable one only means "nothing to carry".
|
|
729
|
+
catch (e) { if (format !== 'csv') priorError = e as Error; }
|
|
730
|
+
applyManualCarry(model, prior);
|
|
731
|
+
|
|
706
732
|
log(`${COLOR.bold}${paths.label}${COLOR.reset} — ${model.manifest.itemCount} delivery item(s), ${model.manifest.variantCount} coverage variant(s), review: ${model.manifest.approvalState} (design), test outcome: ${formatTestOutcome(resultTotals(model.items))}`);
|
|
707
733
|
{
|
|
708
734
|
const mocked = model.items.flatMap((it) => it.variants.filter((v) => v.mocked));
|
|
@@ -737,7 +763,8 @@ async function exportMatrixTarget(
|
|
|
737
763
|
|
|
738
764
|
let deliverables: Awaited<ReturnType<typeof writeMatrixDeliverables>>;
|
|
739
765
|
try {
|
|
740
|
-
|
|
766
|
+
if (priorError) throw priorError;
|
|
767
|
+
deliverables = await writeMatrixDeliverables(paths, model, format, prior);
|
|
741
768
|
} catch (e) {
|
|
742
769
|
// The existing workbook could not be read, so writing would destroy its Manual results.
|
|
743
770
|
log(` ${COLOR.red}not written:${COLOR.reset} ${(e as Error).message}`);
|
|
@@ -977,9 +1004,9 @@ export function registerDeliveryCommand(program: Command): void {
|
|
|
977
1004
|
// Same contract as the export path: an all-@exploration feature has no
|
|
978
1005
|
// deliverable variants, so there is no map to approve — skip, not an error.
|
|
979
1006
|
{
|
|
980
|
-
const
|
|
981
|
-
if (
|
|
982
|
-
&&
|
|
1007
|
+
const scenarios = parseFeatureMetadata(paths.featureFile).scenarios.filter((s) => !isHookScenario(s));
|
|
1008
|
+
if (scenarios.length > 0
|
|
1009
|
+
&& scenarios.every((s) => s.tags.some((t) => /^@exploration\b/i.test(t)))) {
|
|
983
1010
|
log(`${COLOR.gray}${paths.label}: nothing to approve — all scenario(s) are @exploration probes.${COLOR.reset}`);
|
|
984
1011
|
continue;
|
|
985
1012
|
}
|
|
@@ -1004,10 +1031,11 @@ export function registerDeliveryCommand(program: Command): void {
|
|
|
1004
1031
|
|
|
1005
1032
|
const matrixSummaries: MatrixRunSummary[] = [];
|
|
1006
1033
|
const notExported: string[] = [];
|
|
1034
|
+
const env = getEnvironment(cwd);
|
|
1007
1035
|
for (const target of toExport) {
|
|
1008
1036
|
const s = await exportMatrixTarget(cwd, target, {
|
|
1009
1037
|
check: options.check, preview: options.preview, format: format as MatrixFormat,
|
|
1010
|
-
});
|
|
1038
|
+
}, env);
|
|
1011
1039
|
if (s === 'skipped') continue; // all-@exploration feature — neither exported nor a failure
|
|
1012
1040
|
if (s) matrixSummaries.push(s);
|
|
1013
1041
|
else notExported.push(target.isFlow ? `flow/${target.featureBaseName}` : target.featureBaseName);
|
|
@@ -39,7 +39,7 @@ function readReport(file: string, label: string): any {
|
|
|
39
39
|
* (flows → screens → api → api flows); the same basename found in more than one
|
|
40
40
|
* place is an ambiguity error, never a silent pick.
|
|
41
41
|
*/
|
|
42
|
-
function resolveFeatureDir(cwd: string, name: string): string {
|
|
42
|
+
export function resolveFeatureDir(cwd: string, name: string): string {
|
|
43
43
|
const roots = [
|
|
44
44
|
path.join(cwd, 'specs', 'generated', 'flows'),
|
|
45
45
|
path.join(cwd, 'specs', 'generated'),
|
|
@@ -89,6 +89,12 @@ export function runResultsMerge(baseArg: string | undefined, newArg: string | un
|
|
|
89
89
|
if (!baseFile || path.dirname(baseFile) !== dir) {
|
|
90
90
|
throw new Error(`results merge: no per-target result file for "${opts.screen}" in ${path.relative(cwd, dir)} — nothing to merge into (a first full run writes it directly).`);
|
|
91
91
|
}
|
|
92
|
+
// Under SUNGEN_ENV the candidate order falls back to the base-locale file when the env file
|
|
93
|
+
// does not exist yet; merging a `vi` subset there would overwrite `ja`/base results in silence.
|
|
94
|
+
const env = process.env.SUNGEN_ENV;
|
|
95
|
+
if (env && !path.basename(baseFile).endsWith(`.${env}.json`)) {
|
|
96
|
+
throw new Error(`results merge: SUNGEN_ENV=${env} but "${opts.screen}" has no ${env} result file yet (found ${path.basename(baseFile)}) — run the full ${env} suite first; it writes the file directly.`);
|
|
97
|
+
}
|
|
92
98
|
// Ghost/foreign-title checks need the CURRENT spec titles.
|
|
93
99
|
const specFile = path.join(dir, `${opts.screen}.spec.ts`);
|
|
94
100
|
try { specTitles = parseSpecFile(specFile).tests.map((t) => t.testTitle); } catch { specTitles = null; }
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `sungen select` — which tests does a list of test-case IDs name? (#835)
|
|
3
|
+
*
|
|
4
|
+
* Backs `run-test --ids`. Like `sungen changed` for `--change-only`, the selection is computed here
|
|
5
|
+
* so it is deterministic and testable; the workflow only runs it and merges the subset result into
|
|
6
|
+
* the existing per-feature result file.
|
|
7
|
+
*
|
|
8
|
+
* Fails loud: an ID that selects nothing runnable exits non-zero, because a 0-test run reports green.
|
|
9
|
+
*/
|
|
10
|
+
import { Command } from 'commander';
|
|
11
|
+
import * as path from 'path';
|
|
12
|
+
import { findUnitDir } from '../resolve-unit';
|
|
13
|
+
import { loadUnitScenarios } from '../../harness/parse';
|
|
14
|
+
import { parseIdList, selectByIds } from '../../harness/select-by-ids';
|
|
15
|
+
import { resolveFeatureDir } from './results-merge';
|
|
16
|
+
import { readCapabilities } from '../../harness/capability';
|
|
17
|
+
import { projectRootFromScreenDir } from '../../harness/intent';
|
|
18
|
+
|
|
19
|
+
export function registerSelectCommand(program: Command): void {
|
|
20
|
+
program
|
|
21
|
+
.command('select')
|
|
22
|
+
.description('List the tests a set of test-case IDs selects, grouped per feature (backs run-test --ids)')
|
|
23
|
+
.option('-s, --screen <name>', 'Screen or flow name')
|
|
24
|
+
.option('--api <name>', 'API-first area or api flow (qa/api/<name>)')
|
|
25
|
+
.option('--area <name>', 'Alias of --api')
|
|
26
|
+
.requiredOption('--ids <list>', 'Test-case IDs, comma- or space-separated (e.g. VP-UI-001,VP-UI-002)')
|
|
27
|
+
.option('--json', 'Output the selection as JSON (per feature: spec, ids, titles, grep)')
|
|
28
|
+
.option('--grep', 'Output ONLY the regex for -g / --mochaOpts.grep (selection must lie in one feature)')
|
|
29
|
+
.action((options) => {
|
|
30
|
+
try {
|
|
31
|
+
const name = options.screen || options.api || options.area;
|
|
32
|
+
if (!name) throw new Error('Provide --screen <name> (or --api <area>)');
|
|
33
|
+
const dir = findUnitDir(name);
|
|
34
|
+
if (!dir) throw new Error(`Not found: qa/screens|flows|api/${name}`);
|
|
35
|
+
const ids = parseIdList(options.ids);
|
|
36
|
+
if (ids.length === 0) throw new Error('--ids is empty — pass at least one test-case ID');
|
|
37
|
+
|
|
38
|
+
const cwd = process.cwd();
|
|
39
|
+
// Same capability set `generate` compiles against: a @requires:<cap> scenario is a skip without it.
|
|
40
|
+
const enabledCaps = new Set(readCapabilities(projectRootFromScreenDir(path.resolve(dir))).enabled.map((c) => c.toLowerCase()));
|
|
41
|
+
const selection = selectByIds(loadUnitScenarios(dir, name), ids, name, enabledCaps);
|
|
42
|
+
// Resolve each feature's compiled spec: a test cannot run before `sungen generate`.
|
|
43
|
+
const features = selection.features.map((g) => {
|
|
44
|
+
let spec: string | null = null;
|
|
45
|
+
try { spec = path.relative(cwd, path.join(resolveFeatureDir(cwd, g.feature), `${g.feature}.spec.ts`)); } catch { /* reported below */ }
|
|
46
|
+
return { ...g, spec };
|
|
47
|
+
});
|
|
48
|
+
const notCompiled = features.filter((f) => !f.spec).map((f) => f.feature);
|
|
49
|
+
const failed = selection.unknown.length > 0 || selection.notRunnable.length > 0 || notCompiled.length > 0 || features.length === 0;
|
|
50
|
+
|
|
51
|
+
if (options.json) {
|
|
52
|
+
console.log(JSON.stringify({ unit: name, features, unknown: selection.unknown, notRunnable: selection.notRunnable, notCompiled }, null, 2));
|
|
53
|
+
if (failed) process.exitCode = 1;
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (options.grep) {
|
|
57
|
+
// Only a clean single-feature selection prints a regex; anything else must not be run
|
|
58
|
+
// as if it were complete, so it prints nothing and exits non-zero.
|
|
59
|
+
if (failed || features.length !== 1) {
|
|
60
|
+
console.error(features.length > 1
|
|
61
|
+
? `select: the IDs span ${features.length} features (${features.map((f) => f.feature).join(', ')}) — use --json and run each feature on its own.`
|
|
62
|
+
: 'select: nothing runnable for these IDs — run without --grep to see why.');
|
|
63
|
+
process.exitCode = 1;
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
console.log(features[0].grep);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const selected = features.reduce((n, f) => n + f.titles.length, 0);
|
|
71
|
+
// A @cases scenario expands to one test per dataset row, so this counts scenarios.
|
|
72
|
+
console.log(`\n ${selected} scenario(s) selected for ${ids.length} ID(s) in ${name}:\n`);
|
|
73
|
+
for (const f of features) {
|
|
74
|
+
console.log(` ${f.feature} → ${f.spec ?? '(not compiled — run sungen generate first)'}`);
|
|
75
|
+
for (const t of f.titles) console.log(` ▶ ${t}`);
|
|
76
|
+
}
|
|
77
|
+
for (const u of selection.unknown) {
|
|
78
|
+
console.log(` ✗ ${u.id} — no scenario has this ID${u.didYouMean.length ? ` (did you mean ${u.didYouMean.join(', ')}?)` : ''}`);
|
|
79
|
+
}
|
|
80
|
+
for (const n of selection.notRunnable) console.log(` ○ ${n.id} — ${n.reason}`);
|
|
81
|
+
console.log('');
|
|
82
|
+
if (failed) process.exitCode = 1;
|
|
83
|
+
} catch (err) {
|
|
84
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
85
|
+
process.exitCode = 1;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { resolveUnit } from '../resolve-unit';
|
|
4
|
+
import { triageUnit, writeTriageReport } from '../../harness/triage-unit';
|
|
5
|
+
import type { TriageClass } from '../../harness/triage';
|
|
6
|
+
|
|
7
|
+
/** Display order: what needs a decision first, green last. */
|
|
8
|
+
const ORDER: TriageClass[] = ['product-candidate', 'unclassified', 'test-defect', 'environment', 'blocked-capability', 'known-defect', 'flaky'];
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* `sungen triage` — classify every red or flaky scenario of the latest run (see harness/triage.ts).
|
|
12
|
+
* Reports, never gates: always exits 0 once the unit is found.
|
|
13
|
+
*/
|
|
14
|
+
export function registerTriageCommand(program: Command): void {
|
|
15
|
+
program
|
|
16
|
+
.command('triage')
|
|
17
|
+
.description('Classify each red or flaky scenario of the latest run: known-defect, blocked, environment, test-defect, product-candidate, unclassified')
|
|
18
|
+
.option('-s, --screen <name>', 'Screen or flow name')
|
|
19
|
+
.option('--api <name>', 'API-first area or api flow (qa/api/<name>)')
|
|
20
|
+
.option('--area <name>', 'Alias of --api')
|
|
21
|
+
.option('--json', 'Output the raw JSON report')
|
|
22
|
+
.action((o: { screen?: string; api?: string; area?: string; json?: boolean }) => {
|
|
23
|
+
try {
|
|
24
|
+
const name = o.screen || o.api || o.area;
|
|
25
|
+
if (!name) throw new Error('Provide --screen <name> (or --api <area>)');
|
|
26
|
+
const cwd = process.cwd();
|
|
27
|
+
const unit = resolveUnit(name, cwd);
|
|
28
|
+
if (!unit) throw new Error(`Not found: qa/screens/${name}, qa/flows/${name}, or qa/api/${name}`);
|
|
29
|
+
|
|
30
|
+
const report = triageUnit(cwd, unit);
|
|
31
|
+
if (report.resultFiles.length === 0) {
|
|
32
|
+
if (o.json) console.log(JSON.stringify(report, null, 2));
|
|
33
|
+
else console.log(`\n ${name}: no results — run the tests first.\n`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const saved = writeTriageReport(cwd, report);
|
|
37
|
+
if (o.json) { console.log(JSON.stringify(report, null, 2)); return; }
|
|
38
|
+
|
|
39
|
+
const L = console.log;
|
|
40
|
+
L(`\n━━━ Triage: ${name} ━━━`);
|
|
41
|
+
if (report.rows.length === 0) L(' ✓ every scenario passed on its first attempt');
|
|
42
|
+
for (const cls of ORDER) {
|
|
43
|
+
const rows = report.rows.filter((r) => r.class === cls);
|
|
44
|
+
if (rows.length === 0) continue;
|
|
45
|
+
L(`\n ${cls} (${rows.length})`);
|
|
46
|
+
for (const r of rows) {
|
|
47
|
+
L(` • ${r.scenario}${r.variant ? ` [${r.variant}]` : ''}${r.defectId ? ` → ${r.defectId}` : ''}`);
|
|
48
|
+
L(` ${r.ruleId}: ${r.evidence}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
L(`\n Report: ${path.relative(cwd, saved)}\n`);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error('Error:', error instanceof Error ? error.message : error);
|
|
54
|
+
process.exitCode = 1;
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
@@ -103,6 +103,9 @@ export function registerUpdateCommand(program: Command): void {
|
|
|
103
103
|
// playwright.config.ts is written once at init and never re-synced — patch the reporter in.
|
|
104
104
|
const { installReportSanitizeReporter } = require('../../orchestrator/report-sanitize-reporter-install');
|
|
105
105
|
installReportSanitizeReporter(process.cwd(), options.dryRun ?? false);
|
|
106
|
+
// …and move the old `trace: 'on-first-retry'` default, which never records a trace off CI.
|
|
107
|
+
const { installTraceMode } = require('../../orchestrator/trace-mode-install');
|
|
108
|
+
installTraceMode(process.cwd(), options.dryRun ?? false);
|
|
106
109
|
} catch (error) {
|
|
107
110
|
console.error('❌ Update failed:', error);
|
|
108
111
|
process.exit(1);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { resolveUnit } from '../resolve-unit';
|
|
3
|
+
import { scanViewpointUnit, ViewpointScan } from '../../harness/viewpoint-scan';
|
|
4
|
+
|
|
5
|
+
const SHOWN = 10;
|
|
6
|
+
|
|
7
|
+
/** `a, b, c … +N more` — a list long enough to scroll past is a count plus a sample. */
|
|
8
|
+
const sample = (xs: string[]): string => (xs.length <= SHOWN ? xs.join(', ') : `${xs.slice(0, SHOWN).join(', ')} … +${xs.length - SHOWN} more`);
|
|
9
|
+
|
|
10
|
+
function printScan(name: string, s: ViewpointScan): void {
|
|
11
|
+
const L = console.log;
|
|
12
|
+
L(`\n━━━ Viewpoint scan: ${name} ━━━`);
|
|
13
|
+
L(` mode ${s.mode} (${s.modeReason}) — ${s.mode === 2 ? 'the AI drafts / revises its own draft' : 'review only, the file is never edited'}`);
|
|
14
|
+
L(` ${s.vpRows.length} rows (${s.distinctIds} distinct ids) without Kỳ vọng: ${s.rowsWithoutExpected.length} manual: ${s.manualRows.length}`);
|
|
15
|
+
if (s.note) L(` ⚠ ${s.note}`);
|
|
16
|
+
if (s.rowsWithoutExpected.length) L(` no Kỳ vọng: ${sample(s.rowsWithoutExpected)}`);
|
|
17
|
+
if (s.manualRows.length) L(` manual: ${sample(s.manualRows)}`);
|
|
18
|
+
if (s.itemSource === 'none') L(' item source: none (the viewpoint cites no spec id, spec item or UI-NNN)');
|
|
19
|
+
else L(` item source: ${s.itemSource} uncited items: ${s.uncitedItems.length}`);
|
|
20
|
+
if (s.uncitedItems.length) L(` ${sample(s.uncitedItems.map((i) => (i.name ? `${i.ref} (${i.name})` : i.ref)))}`);
|
|
21
|
+
if (s.withoutScenario === null) L(' scenarios: no feature file yet');
|
|
22
|
+
else L(` VP ids with no scenario: ${s.withoutScenario.length}${s.withoutScenario.length ? `\n ${sample(s.withoutScenario)}` : ''}`);
|
|
23
|
+
L('');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* `sungen viewpoint scan` — ids and item refs only. Read-only: no audit finding, no score, no
|
|
28
|
+
* baseline effect; the file is never edited. What the rows MEAN is the review skill's job.
|
|
29
|
+
*/
|
|
30
|
+
export function registerViewpointCommand(program: Command): void {
|
|
31
|
+
const viewpoint = program
|
|
32
|
+
.command('viewpoint')
|
|
33
|
+
.description('Read a unit\'s requirements/test-viewpoint.md');
|
|
34
|
+
|
|
35
|
+
viewpoint
|
|
36
|
+
.command('scan')
|
|
37
|
+
.description('List VP rows (full-id first cell), the item numbers they cite, uncited spec items and VP ids no scenario carries — read-only')
|
|
38
|
+
.option('-s, --screen <name>', 'Screen (or flow) name')
|
|
39
|
+
.option('--flow <name>', 'Flow name (qa/flows/<name>)')
|
|
40
|
+
.option('--json', 'Output the scan as JSON')
|
|
41
|
+
.action((o: { screen?: string; flow?: string; json?: boolean }) => {
|
|
42
|
+
try {
|
|
43
|
+
if (o.screen && o.flow) throw new Error('Provide --screen <name> or --flow <name>, not both');
|
|
44
|
+
const name = o.screen || o.flow;
|
|
45
|
+
if (!name) throw new Error('Provide --screen <name> or --flow <name>');
|
|
46
|
+
const unit = resolveUnit(name);
|
|
47
|
+
if (!unit) throw new Error(`Not found: qa/screens/${name} or qa/flows/${name}`);
|
|
48
|
+
if (o.flow && unit.kind !== 'flow') throw new Error(`Not a flow: ${name} resolves to qa/${unit.unitId} (use --screen)`);
|
|
49
|
+
|
|
50
|
+
const scan = scanViewpointUnit(unit.dir, name);
|
|
51
|
+
if (o.json) console.log(JSON.stringify(scan, null, 2));
|
|
52
|
+
else printScan(name, scan);
|
|
53
|
+
} catch (e) {
|
|
54
|
+
console.error('Error:', e instanceof Error ? e.message : e);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
package/src/cli/index.ts
CHANGED
|
@@ -24,6 +24,7 @@ import { registerIngestCommand } from './commands/ingest';
|
|
|
24
24
|
import { registerEvalCommand } from './commands/eval';
|
|
25
25
|
import { registerManifestCommand } from './commands/manifest';
|
|
26
26
|
import { registerChangedCommand } from './commands/changed';
|
|
27
|
+
import { registerSelectCommand } from './commands/select';
|
|
27
28
|
import { registerLedgerCommand } from './commands/ledger';
|
|
28
29
|
import { registerFeedbackCommand } from './commands/feedback';
|
|
29
30
|
import { registerQaFeedbackCommand } from './commands/qa-feedback';
|
|
@@ -36,8 +37,11 @@ import { registerCapabilityCommand } from './commands/capability';
|
|
|
36
37
|
import { registerFlowCheckCommand } from './commands/flow-check';
|
|
37
38
|
import { registerContextCommand } from './commands/context';
|
|
38
39
|
import { registerRepairCommand } from './commands/repair';
|
|
40
|
+
import { registerTriageCommand } from './commands/triage';
|
|
41
|
+
import { registerDefectCommand } from './commands/defect';
|
|
39
42
|
import { registerResultsCommand } from './commands/results-merge';
|
|
40
43
|
import { registerLoginCommand } from './commands/login';
|
|
44
|
+
import { registerViewpointCommand } from './commands/viewpoint';
|
|
41
45
|
import { capabilityRegistry } from '../capabilities/registry';
|
|
42
46
|
import { discoverAndRegisterCapabilities } from '../capabilities/discover';
|
|
43
47
|
import { warnAssetsDrift } from '../orchestrator/assets-drift';
|
|
@@ -75,6 +79,7 @@ registerSetupMobileEnvCommand(program);
|
|
|
75
79
|
registerGateCommand(program);
|
|
76
80
|
registerManifestCommand(program);
|
|
77
81
|
registerChangedCommand(program);
|
|
82
|
+
registerSelectCommand(program);
|
|
78
83
|
registerLedgerCommand(program);
|
|
79
84
|
registerFeedbackCommand(program);
|
|
80
85
|
registerQaFeedbackCommand(program);
|
|
@@ -87,10 +92,13 @@ registerSetupMobileEnvCommand(program);
|
|
|
87
92
|
registerFlowCheckCommand(program);
|
|
88
93
|
registerContextCommand(program);
|
|
89
94
|
registerRepairCommand(program);
|
|
95
|
+
registerTriageCommand(program);
|
|
96
|
+
registerDefectCommand(program);
|
|
90
97
|
registerResultsCommand(program);
|
|
91
98
|
registerIngestCommand(program);
|
|
92
99
|
registerEvalCommand(program);
|
|
93
100
|
registerLoginCommand(program);
|
|
101
|
+
registerViewpointCommand(program);
|
|
94
102
|
|
|
95
103
|
// Capability-contributed CLI commands (Capability SPI): drivers own their authoring commands
|
|
96
104
|
// (e.g. @sungen/driver-api → `sungen api import`). Discover, then register each.
|
|
@@ -123,12 +131,15 @@ registerSetupMobileEnvCommand(program);
|
|
|
123
131
|
const opts = actionCommand.opts() as Record<string, unknown>;
|
|
124
132
|
const unit = [opts.screen, opts.flow, opts.area].find((v) => typeof v === 'string' && v)
|
|
125
133
|
?? (typeof actionCommand.args?.[0] === 'string' ? actionCommand.args[0] : undefined);
|
|
126
|
-
|
|
134
|
+
// `--grep` is machine output too: callers capture it with `$(…)`, and a footer on stdout
|
|
135
|
+
// became part of the regex — the last alternative of `changed --grep` silently matched
|
|
136
|
+
// nothing, so `--change-only` skipped its last scenario (#836).
|
|
137
|
+
handBack = { unit: unit as string | undefined, json: opts.json === true || opts.grep === true };
|
|
127
138
|
});
|
|
128
139
|
process.on('exit', (code) => {
|
|
129
140
|
if (!handBack || code === 1) return;
|
|
130
141
|
const line = `\nNext: sungen next${handBack.unit ? ` ${handBack.unit}` : ''}`;
|
|
131
|
-
// stderr under --json so the hand-back never lands in the
|
|
142
|
+
// stderr under --json / --grep so the hand-back never lands in the captured payload.
|
|
132
143
|
if (handBack.json) process.stderr.write(`${line}\n`);
|
|
133
144
|
else process.stdout.write(`${line}\n`);
|
|
134
145
|
});
|