@sun-asterisk/sungen 3.2.32 → 3.2.34
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 +5 -0
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/audit.js +26 -4
- 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 +35 -17
- 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/delivery.d.ts.map +1 -1
- package/dist/cli/commands/delivery.js +79 -53
- package/dist/cli/commands/delivery.js.map +1 -1
- package/dist/cli/commands/generate.d.ts.map +1 -1
- package/dist/cli/commands/generate.js +14 -7
- package/dist/cli/commands/generate.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 +9 -12
- package/dist/cli/commands/results-merge.js.map +1 -1
- package/dist/cli/commands/results-sanitize.d.ts.map +1 -1
- package/dist/cli/commands/results-sanitize.js +2 -13
- package/dist/cli/commands/results-sanitize.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/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/index.js +7 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/dashboard/snapshot-builder.d.ts.map +1 -1
- package/dist/dashboard/snapshot-builder.js +6 -10
- package/dist/dashboard/snapshot-builder.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.map +1 -1
- package/dist/exporters/csv-exporter.js +11 -10
- package/dist/exporters/csv-exporter.js.map +1 -1
- package/dist/exporters/feature-parser.d.ts +21 -0
- package/dist/exporters/feature-parser.d.ts.map +1 -1
- package/dist/exporters/feature-parser.js +32 -1
- 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/locale-variants.d.ts +7 -1
- package/dist/exporters/locale-variants.d.ts.map +1 -1
- package/dist/exporters/locale-variants.js +23 -16
- package/dist/exporters/locale-variants.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 +19 -9
- package/dist/exporters/matrix/build.js.map +1 -1
- package/dist/exporters/matrix/carry-manual-results.d.ts +70 -0
- package/dist/exporters/matrix/carry-manual-results.d.ts.map +1 -0
- package/dist/exporters/matrix/carry-manual-results.js +232 -0
- package/dist/exporters/matrix/carry-manual-results.js.map +1 -0
- package/dist/exporters/matrix/export.d.ts +5 -0
- package/dist/exporters/matrix/export.d.ts.map +1 -1
- package/dist/exporters/matrix/export.js +8 -1
- 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-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/types.d.ts +8 -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 +8 -1
- package/dist/exporters/matrix/wording.d.ts.map +1 -1
- package/dist/exporters/matrix/wording.js +37 -2
- package/dist/exporters/matrix/wording.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 +47 -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/xlsx-exporter.d.ts +2 -0
- package/dist/exporters/xlsx-exporter.d.ts.map +1 -1
- package/dist/exporters/xlsx-exporter.js +7 -2
- package/dist/exporters/xlsx-exporter.js.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/appium/templates/steps/actions/reinstall-app-action.hbs +2 -2
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/label-value-assertion.hbs +8 -2
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/visible-with-role-variable-assertion.hbs +7 -3
- package/dist/generators/test-generator/adapters/appium/templates/test-file.hbs +68 -4
- 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/assertions/label-value-assertion.hbs +7 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +8 -4
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/pick-option.hbs +13 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/url-assertion.hbs +5 -1
- package/dist/generators/test-generator/adapters/playwright/templates/test-file.hbs +16 -0
- package/dist/generators/test-generator/code-generator.d.ts +2 -1
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +90 -5
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/generators/test-generator/diagnostics.d.ts +36 -5
- package/dist/generators/test-generator/diagnostics.d.ts.map +1 -1
- package/dist/generators/test-generator/diagnostics.js +35 -4
- package/dist/generators/test-generator/diagnostics.js.map +1 -1
- package/dist/generators/test-generator/patterns/index.d.ts +12 -2
- package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/index.js +15 -9
- package/dist/generators/test-generator/patterns/index.js.map +1 -1
- package/dist/generators/test-generator/patterns/types.d.ts +15 -0
- package/dist/generators/test-generator/patterns/types.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/with-parent-scope.d.ts +5 -0
- package/dist/generators/test-generator/patterns/with-parent-scope.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/with-parent-scope.js +6 -0
- package/dist/generators/test-generator/patterns/with-parent-scope.js.map +1 -1
- package/dist/generators/test-generator/step-mapper.d.ts +46 -0
- package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.js +84 -9
- package/dist/generators/test-generator/step-mapper.js.map +1 -1
- package/dist/generators/test-generator/utils/data-resolver.d.ts +25 -0
- package/dist/generators/test-generator/utils/data-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/data-resolver.js +39 -8
- package/dist/generators/test-generator/utils/data-resolver.js.map +1 -1
- package/dist/generators/test-generator/utils/selector-entry-fields.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/selector-entry-fields.js +1 -0
- package/dist/generators/test-generator/utils/selector-entry-fields.js.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.d.ts +37 -4
- package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.js +212 -79
- package/dist/generators/test-generator/utils/selector-resolver.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 +29 -12
- package/dist/harness/audit.js.map +1 -1
- package/dist/harness/capability.d.ts +7 -1
- package/dist/harness/capability.d.ts.map +1 -1
- package/dist/harness/capability.js +15 -12
- package/dist/harness/capability.js.map +1 -1
- package/dist/harness/catalog/universal-viewpoints.yaml +5 -5
- package/dist/harness/config-file.d.ts +17 -0
- package/dist/harness/config-file.d.ts.map +1 -0
- package/dist/harness/config-file.js +100 -0
- package/dist/harness/config-file.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/depth-lint.d.ts.map +1 -1
- package/dist/harness/depth-lint.js +2 -1
- package/dist/harness/depth-lint.js.map +1 -1
- package/dist/harness/flow-check.d.ts.map +1 -1
- package/dist/harness/flow-check.js +6 -2
- package/dist/harness/flow-check.js.map +1 -1
- package/dist/harness/keyword-match.d.ts +59 -0
- package/dist/harness/keyword-match.d.ts.map +1 -0
- package/dist/harness/keyword-match.js +86 -0
- package/dist/harness/keyword-match.js.map +1 -0
- package/dist/harness/next-step.d.ts.map +1 -1
- package/dist/harness/next-step.js +1 -0
- package/dist/harness/next-step.js.map +1 -1
- package/dist/harness/parse.d.ts +24 -0
- package/dist/harness/parse.d.ts.map +1 -1
- package/dist/harness/parse.js +117 -13
- package/dist/harness/parse.js.map +1 -1
- package/dist/harness/perf.d.ts +7 -1
- package/dist/harness/perf.d.ts.map +1 -1
- package/dist/harness/perf.js +13 -10
- package/dist/harness/perf.js.map +1 -1
- package/dist/harness/quality-gates.d.ts +9 -2
- package/dist/harness/quality-gates.d.ts.map +1 -1
- package/dist/harness/quality-gates.js +22 -8
- package/dist/harness/quality-gates.js.map +1 -1
- package/dist/harness/query-catalog.js +0 -0
- package/dist/harness/query-catalog.js.map +1 -1
- package/dist/harness/read-text.d.ts +21 -0
- package/dist/harness/read-text.d.ts.map +1 -1
- package/dist/harness/read-text.js +32 -1
- package/dist/harness/read-text.js.map +1 -1
- package/dist/harness/secret-scan.d.ts +27 -1
- package/dist/harness/secret-scan.d.ts.map +1 -1
- package/dist/harness/secret-scan.js +57 -4
- package/dist/harness/secret-scan.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 +20 -1
- package/dist/harness/sensors.d.ts.map +1 -1
- package/dist/harness/sensors.js +102 -27
- package/dist/harness/sensors.js.map +1 -1
- package/dist/harness/spec-branches.d.ts +0 -18
- package/dist/harness/spec-branches.d.ts.map +1 -1
- package/dist/harness/spec-branches.js +24 -3
- package/dist/harness/spec-branches.js.map +1 -1
- package/dist/harness/spec-coverage.d.ts.map +1 -1
- package/dist/harness/spec-coverage.js +14 -4
- package/dist/harness/spec-coverage.js.map +1 -1
- 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-ledger.d.ts.map +1 -1
- package/dist/harness/viewpoint-ledger.js +66 -10
- package/dist/harness/viewpoint-ledger.js.map +1 -1
- package/dist/harness/write-atomic.d.ts +2 -0
- package/dist/harness/write-atomic.d.ts.map +1 -0
- package/dist/harness/write-atomic.js +62 -0
- package/dist/harness/write-atomic.js.map +1 -0
- package/dist/orchestrator/ai-skills/writer.d.ts.map +1 -1
- package/dist/orchestrator/ai-skills/writer.js +15 -7
- package/dist/orchestrator/ai-skills/writer.js.map +1 -1
- package/dist/orchestrator/assets-drift.d.ts.map +1 -1
- package/dist/orchestrator/assets-drift.js +11 -6
- package/dist/orchestrator/assets-drift.js.map +1 -1
- package/dist/orchestrator/project-deps-sync.d.ts.map +1 -1
- package/dist/orchestrator/project-deps-sync.js +22 -1
- package/dist/orchestrator/project-deps-sync.js.map +1 -1
- package/dist/orchestrator/project-initializer.d.ts +8 -0
- package/dist/orchestrator/project-initializer.d.ts.map +1 -1
- package/dist/orchestrator/project-initializer.js +49 -7
- package/dist/orchestrator/project-initializer.js.map +1 -1
- package/dist/orchestrator/screen-manager.d.ts.map +1 -1
- package/dist/orchestrator/screen-manager.js +3 -11
- package/dist/orchestrator/screen-manager.js.map +1 -1
- package/dist/orchestrator/security-overrides-catalog.d.ts +39 -0
- package/dist/orchestrator/security-overrides-catalog.d.ts.map +1 -0
- package/dist/orchestrator/security-overrides-catalog.js +63 -0
- package/dist/orchestrator/security-overrides-catalog.js.map +1 -0
- package/dist/orchestrator/security-overrides.d.ts +51 -0
- package/dist/orchestrator/security-overrides.d.ts.map +1 -0
- package/dist/orchestrator/security-overrides.js +165 -0
- package/dist/orchestrator/security-overrides.js.map +1 -0
- 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 +2 -2
- package/dist/orchestrator/templates/ai-src/commands/review.md +2 -2
- package/dist/orchestrator/templates/ai-src/commands/run-test.md +70 -13
- package/dist/orchestrator/templates/ai-src/skills/sungen-capture/mode-local.md +2 -2
- package/dist/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +14 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +23 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +26 -8
- package/dist/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +58 -14
- package/dist/orchestrator/templates/ai-src/skills/sungen-selector-keys/SKILL.md +13 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +41 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +7 -0
- package/dist/orchestrator/templates/playwright.config.ts +4 -2
- package/dist/orchestrator/templates/specs-locale-fixture.ts +21 -9
- package/dist/orchestrator/templates/specs-pw-shape-reporter.ts +0 -0
- package/dist/orchestrator/templates/specs-url-assert.d.ts +17 -1
- package/dist/orchestrator/templates/specs-url-assert.d.ts.map +1 -1
- package/dist/orchestrator/templates/specs-url-assert.js +38 -4
- package/dist/orchestrator/templates/specs-url-assert.js.map +1 -1
- package/dist/orchestrator/templates/specs-url-assert.ts +47 -4
- package/dist/orchestrator/templates/wdio.conf.ts +24 -1
- 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 +20 -0
- package/dist/orchestrator/viewpoint-pattern-catalog.d.ts.map +1 -0
- package/dist/orchestrator/viewpoint-pattern-catalog.js +45 -0
- package/dist/orchestrator/viewpoint-pattern-catalog.js.map +1 -0
- package/package.json +3 -3
- package/src/cli/commands/audit.ts +24 -5
- package/src/cli/commands/capability.ts +51 -18
- package/src/cli/commands/changed.ts +2 -4
- package/src/cli/commands/dashboard.ts +1 -26
- package/src/cli/commands/delivery.ts +75 -48
- package/src/cli/commands/generate.ts +15 -7
- package/src/cli/commands/results-merge.ts +9 -9
- package/src/cli/commands/results-sanitize.ts +2 -9
- package/src/cli/commands/select.ts +88 -0
- package/src/cli/commands/update.ts +3 -0
- package/src/cli/index.ts +7 -2
- package/src/dashboard/snapshot-builder.ts +6 -8
- package/src/dashboard/templates/index.html +1 -1
- package/src/dashboard/types.ts +1 -0
- package/src/exporters/csv-exporter.ts +7 -10
- package/src/exporters/feature-parser.ts +32 -2
- package/src/exporters/json-exporter.ts +6 -1
- package/src/exporters/locale-variants.ts +23 -17
- package/src/exporters/manual-hybrid-oracle.ts +112 -0
- package/src/exporters/matrix/build.ts +20 -10
- package/src/exporters/matrix/carry-manual-results.ts +229 -0
- package/src/exporters/matrix/export.ts +14 -4
- package/src/exporters/matrix/gates.ts +6 -0
- package/src/exporters/matrix/render-testcase-sheet.ts +10 -4
- package/src/exporters/matrix/types.ts +8 -1
- package/src/exporters/matrix/wording.ts +36 -2
- package/src/exporters/run-environment.ts +89 -0
- package/src/exporters/scenario-merger.ts +49 -4
- package/src/exporters/test-data-resolver.ts +0 -6
- package/src/exporters/xlsx-exporter.ts +7 -2
- package/src/generators/gherkin-parser/index.ts +50 -18
- package/src/generators/test-generator/adapters/adapter-interface.ts +6 -2
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/reinstall-app-action.hbs +2 -2
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/label-value-assertion.hbs +8 -2
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/visible-with-role-variable-assertion.hbs +7 -3
- package/src/generators/test-generator/adapters/appium/templates/test-file.hbs +68 -4
- 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/assertions/label-value-assertion.hbs +7 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +8 -4
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/pick-option.hbs +13 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/url-assertion.hbs +5 -1
- package/src/generators/test-generator/adapters/playwright/templates/test-file.hbs +16 -0
- package/src/generators/test-generator/code-generator.ts +77 -5
- package/src/generators/test-generator/diagnostics.ts +36 -5
- package/src/generators/test-generator/patterns/index.ts +15 -8
- package/src/generators/test-generator/patterns/types.ts +15 -0
- package/src/generators/test-generator/patterns/with-parent-scope.ts +6 -0
- package/src/generators/test-generator/step-mapper.ts +90 -8
- package/src/generators/test-generator/utils/data-resolver.ts +41 -12
- package/src/generators/test-generator/utils/selector-entry-fields.ts +1 -0
- package/src/generators/test-generator/utils/selector-resolver.ts +238 -82
- package/src/harness/audit.ts +37 -16
- package/src/harness/capability.ts +23 -12
- package/src/harness/catalog/universal-viewpoints.yaml +5 -5
- package/src/harness/config-file.ts +66 -0
- package/src/harness/data-driven-lint.ts +82 -24
- package/src/harness/depth-lint.ts +2 -1
- package/src/harness/flow-check.ts +6 -2
- package/src/harness/keyword-match.ts +85 -0
- package/src/harness/next-step.ts +1 -0
- package/src/harness/parse.ts +123 -16
- package/src/harness/perf.ts +15 -7
- package/src/harness/quality-gates.ts +22 -8
- package/src/harness/query-catalog.ts +0 -0
- package/src/harness/read-text.ts +32 -1
- package/src/harness/secret-scan.ts +60 -4
- package/src/harness/select-by-ids.ts +83 -0
- package/src/harness/sensors.ts +109 -28
- package/src/harness/spec-branches.ts +6 -3
- package/src/harness/spec-coverage.ts +14 -4
- package/src/harness/ui-inventory.ts +164 -0
- package/src/harness/viewpoint-ledger.ts +59 -12
- package/src/harness/write-atomic.ts +22 -0
- package/src/orchestrator/ai-skills/writer.ts +18 -3
- package/src/orchestrator/assets-drift.ts +10 -5
- package/src/orchestrator/project-deps-sync.ts +23 -1
- package/src/orchestrator/project-initializer.ts +54 -6
- package/src/orchestrator/screen-manager.ts +3 -11
- package/src/orchestrator/security-overrides-catalog.ts +69 -0
- package/src/orchestrator/security-overrides.ts +149 -0
- 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 +2 -2
- package/src/orchestrator/templates/ai-src/commands/review.md +2 -2
- package/src/orchestrator/templates/ai-src/commands/run-test.md +70 -13
- package/src/orchestrator/templates/ai-src/skills/sungen-capture/mode-local.md +2 -2
- package/src/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +14 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +23 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +26 -8
- package/src/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +58 -14
- package/src/orchestrator/templates/ai-src/skills/sungen-selector-keys/SKILL.md +13 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +41 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +7 -0
- package/src/orchestrator/templates/playwright.config.ts +4 -2
- package/src/orchestrator/templates/specs-locale-fixture.ts +21 -9
- package/src/orchestrator/templates/specs-pw-shape-reporter.ts +0 -0
- package/src/orchestrator/templates/specs-url-assert.ts +47 -4
- package/src/orchestrator/templates/wdio.conf.ts +24 -1
- package/src/orchestrator/trace-mode-install.ts +94 -0
- package/src/orchestrator/viewpoint-pattern-catalog.ts +58 -0
|
@@ -46,6 +46,16 @@ export interface UrlExpectation {
|
|
|
46
46
|
declared?: string;
|
|
47
47
|
/** The test-data key behind `declared`, so a failure can name what an author must go fix. */
|
|
48
48
|
dataRef?: string;
|
|
49
|
+
/**
|
|
50
|
+
* The page selector declared `query: any` — the query string is not part of this page's identity,
|
|
51
|
+
* so SILENCE from the step stops meaning "there must be no query" (#748).
|
|
52
|
+
*
|
|
53
|
+
* Emitted only for a page whose entry says so. The default stays strict: a bare page means a bare
|
|
54
|
+
* page, which is the #464 assertion and the reason a filter regression cannot read as green.
|
|
55
|
+
* It is the no-`dataRef` branch alone that this relaxes — a step that declares `with {{v}}` still
|
|
56
|
+
* asserts every param it names, on this page as on any other.
|
|
57
|
+
*/
|
|
58
|
+
ignoreQuery?: boolean;
|
|
49
59
|
}
|
|
50
60
|
|
|
51
61
|
/**
|
|
@@ -113,8 +123,14 @@ function locationMatches(pathname: RegExp, hostname: RegExp | undefined, u: URL)
|
|
|
113
123
|
* tracking/UTM param the test never cared about cannot turn it red.
|
|
114
124
|
* A declared fragment is asserted too; a declared value carrying no query
|
|
115
125
|
* at all constrains the path only (the compiler warns: SG-W014).
|
|
126
|
+
*
|
|
127
|
+
* `query: any` on the page's selector entry overrides the FIRST rule only (#748): silence then
|
|
128
|
+
* means "the query is not mine to judge" instead of "there must be none". It is the escape hatch
|
|
129
|
+
* for a page whose query is generated at run time — `/admins/detail?id=<the row just clicked>` —
|
|
130
|
+
* which the strict rule makes unassertable and `with {{v}}` cannot express, since that asserts a
|
|
131
|
+
* literal. The second rule is untouched: declare params and they are still all checked.
|
|
116
132
|
*/
|
|
117
|
-
export function urlMatches({ pathname, hostname, declared, dataRef }: UrlExpectation): (u: URL) => boolean {
|
|
133
|
+
export function urlMatches({ pathname, hostname, declared, dataRef, ignoreQuery }: UrlExpectation): (u: URL) => boolean {
|
|
118
134
|
return (u: URL): boolean => {
|
|
119
135
|
if (!locationMatches(pathname, hostname, u)) return false;
|
|
120
136
|
|
|
@@ -123,7 +139,13 @@ export function urlMatches({ pathname, hostname, declared, dataRef }: UrlExpecta
|
|
|
123
139
|
// undefined, and they mean opposite things: the first is a real assertion (a bare page must be
|
|
124
140
|
// bare), the second is broken test-data. Only the generator knows which, and it says so by
|
|
125
141
|
// emitting `dataRef` exactly when the step carried a `with {{v}}`.
|
|
126
|
-
|
|
142
|
+
// `query: any` on the page entry (#748): the query is not part of this page's identity, so
|
|
143
|
+
// silence from the step is not an assertion about it. Without this there is NO way to express
|
|
144
|
+
// "this path, whatever the query" — `with {{v}}` asserts a LITERAL value, and a detail page
|
|
145
|
+
// keyed by `?id=<the row just clicked>` has no literal to declare. Checked before the bare-page
|
|
146
|
+
// rule, never instead of the declared-value rule below: a step that names params still asserts
|
|
147
|
+
// them here exactly as anywhere else.
|
|
148
|
+
if (!dataRef) return ignoreQuery ? true : u.search === '';
|
|
127
149
|
|
|
128
150
|
// Silence is the whole bug class this assertion exists to kill, so an unusable declared value
|
|
129
151
|
// is an ERROR, not a quiet verdict. Returning false would read as "the app is wrong"; these
|
|
@@ -183,6 +205,26 @@ interface SettleOpts { settleMs?: number; networkIdleMs?: number }
|
|
|
183
205
|
* 3000) bounds step 1. Both are runtime knobs, so a slow environment is a config change, not a
|
|
184
206
|
* regenerate.
|
|
185
207
|
*/
|
|
208
|
+
/**
|
|
209
|
+
* A millisecond budget from the environment. `Number()` alone turned `"1s"` / `"abc"` into NaN and
|
|
210
|
+
* `""` into 0 (`??` keeps an empty string): the hold loop then never ran, so a late redirect passed
|
|
211
|
+
* unseen, and a 0 / NaN networkidle timeout means "no timeout" to Playwright — the step waited out
|
|
212
|
+
* the whole test (#856). Unset or empty means the default; a malformed value falls back to it and
|
|
213
|
+
* says so, once per variable. `allowZero` for a knob where 0 is a real choice (no hold).
|
|
214
|
+
*/
|
|
215
|
+
const warnedEnv = new Set<string>();
|
|
216
|
+
function msEnv(name: string, fallback: number, allowZero = false): number {
|
|
217
|
+
const raw = process.env[name]?.trim();
|
|
218
|
+
if (!raw) return fallback;
|
|
219
|
+
const value = Number(raw);
|
|
220
|
+
if (Number.isFinite(value) && (value > 0 || (allowZero && value === 0))) return value;
|
|
221
|
+
if (!warnedEnv.has(name)) {
|
|
222
|
+
warnedEnv.add(name);
|
|
223
|
+
console.warn(`[sungen] ${name}=${JSON.stringify(raw)} is not a ${allowZero ? 'non-negative' : 'positive'} number of milliseconds — using the default ${fallback}.`);
|
|
224
|
+
}
|
|
225
|
+
return fallback;
|
|
226
|
+
}
|
|
227
|
+
|
|
186
228
|
async function settleAndHold(
|
|
187
229
|
page: SettleablePage,
|
|
188
230
|
matches: (u: URL) => boolean,
|
|
@@ -190,8 +232,9 @@ async function settleAndHold(
|
|
|
190
232
|
label: string,
|
|
191
233
|
consequence: string,
|
|
192
234
|
): Promise<void> {
|
|
193
|
-
|
|
194
|
-
const
|
|
235
|
+
// 0 holds nothing (the operator's call); 0 for networkidle would mean "no timeout", so it is refused.
|
|
236
|
+
const settleMs = opts.settleMs ?? msEnv('SUNGEN_URL_SETTLE_MS', 1000, true);
|
|
237
|
+
const networkIdleMs = opts.networkIdleMs ?? msEnv('SUNGEN_URL_NETWORKIDLE_MS', 3000);
|
|
195
238
|
const arrivedAt = page.url();
|
|
196
239
|
|
|
197
240
|
await page.waitForLoadState('networkidle', { timeout: networkIdleMs }).catch(() => undefined);
|
|
@@ -48,6 +48,14 @@ function evidenceSlug(s: string): string {
|
|
|
48
48
|
.slice(0, 80) || 'unnamed';
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* True only in the launcher process. `@wdio/runner` re-parses this very config inside EVERY worker
|
|
53
|
+
* it spawns, so anything logged at module scope prints once per spec unless it is gated. WDIO's own
|
|
54
|
+
* "am I a worker" test is this env var (`@wdio/local-runner` sets it when spawning; `@wdio/utils`
|
|
55
|
+
* reads it) — reuse it rather than inventing a second signal.
|
|
56
|
+
*/
|
|
57
|
+
const IS_LAUNCHER = !process.env.WDIO_WORKER_ID;
|
|
58
|
+
|
|
51
59
|
/**
|
|
52
60
|
* Auto-select MOBILE specs and classify each by its target OS (dual-target routing).
|
|
53
61
|
*
|
|
@@ -82,7 +90,12 @@ function classifyMobileSpecs(root: string): Array<{ file: string; platform: stri
|
|
|
82
90
|
.filter((full) => {
|
|
83
91
|
try {
|
|
84
92
|
return fs.readFileSync(full, 'utf-8').includes('@wdio/globals');
|
|
85
|
-
} catch {
|
|
93
|
+
} catch (e) {
|
|
94
|
+
// A spec listed by readdir that cannot be read cannot be classified, so it is excluded.
|
|
95
|
+
// Excluding it SILENTLY is the fault: WDIO only fails the run when the spec list is
|
|
96
|
+
// EMPTY ("No specs found to run"), so dropping one spec out of ten leaves a green suite
|
|
97
|
+
// containing a test that never executed. Name it, and let the count below show the shortfall.
|
|
98
|
+
if (IS_LAUNCHER) console.warn(`[sungen] could not read ${full} — EXCLUDED from this run: ${(e as Error).message}`);
|
|
86
99
|
return false;
|
|
87
100
|
}
|
|
88
101
|
})
|
|
@@ -98,6 +111,16 @@ const androidSpecs = specEntries.filter((e) => e.platform !== 'ios').map((e) =>
|
|
|
98
111
|
const iosSpecs = specEntries.filter((e) => e.platform !== 'android').map((e) => e.file);
|
|
99
112
|
const mobileSpecs = specEntries.map((e) => e.file); // union — top-level fallback
|
|
100
113
|
|
|
114
|
+
// What DISCOVERY found — a mobile suite is only trustworthy if you can see how many specs it
|
|
115
|
+
// resolved: WDIO fails loudly on an EMPTY list but says nothing when the list is merely short,
|
|
116
|
+
// which is exactly what a dropped spec looks like.
|
|
117
|
+
//
|
|
118
|
+
// Two things this number is not. It is not an error on 0: WDIO already exits 1 there
|
|
119
|
+
// ("No specs found to run"), and a `--shard` that legitimately receives none must still return 0.
|
|
120
|
+
// And under `--shard` it is the PRE-SHARD total, because ConfigParser slices `specs` down to the
|
|
121
|
+
// shard's portion only after this file has finished evaluating.
|
|
122
|
+
if (IS_LAUNCHER) console.log(`[sungen] specs: ${specEntries.length} mobile spec(s) — android ${androidSpecs.length}, ios ${iosSpecs.length}`);
|
|
123
|
+
|
|
101
124
|
// App under test — env overrides the init-configured defaults.
|
|
102
125
|
const APP_PACKAGE = process.env.APP_PACKAGE || '__APP_PACKAGE__';
|
|
103
126
|
const APP_ACTIVITY = process.env.APP_ACTIVITY || '__APP_ACTIVITY__';
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** The template's trace value: cheap on CI (retries exist there), a trace for every local failure. */
|
|
2
|
+
export declare const TRACE_MODE_EXPRESSION = "process.env.CI ? 'on-first-retry' : 'retain-on-failure'";
|
|
3
|
+
/**
|
|
4
|
+
* 'patched' — the old default was rewritten now
|
|
5
|
+
* 'present' — already the template expression (idempotent)
|
|
6
|
+
* 'custom' — another value, no trace line, more than one old-default line, or one inside
|
|
7
|
+
* `projects: [...]`; nothing written
|
|
8
|
+
* 'absent' — no playwright.config.ts (a mobile-only project, or not a sungen project)
|
|
9
|
+
*/
|
|
10
|
+
export type TraceModeOutcome = 'patched' | 'present' | 'custom' | 'absent';
|
|
11
|
+
export declare function patchTraceMode(cwd: string, dryRun?: boolean): TraceModeOutcome;
|
|
12
|
+
/** `sungen update` step: patch the trace mode and say so when it changed. */
|
|
13
|
+
export declare function installTraceMode(cwd: string, dryRun?: boolean): void;
|
|
14
|
+
//# sourceMappingURL=trace-mode-install.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace-mode-install.d.ts","sourceRoot":"","sources":["../../src/orchestrator/trace-mode-install.ts"],"names":[],"mappings":"AAgBA,sGAAsG;AACtG,eAAO,MAAM,qBAAqB,4DAA4D,CAAC;AAE/F;;;;;;GAMG;AACH,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAC;AA6C3E,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,OAAe,GAAG,gBAAgB,CAgBrF;AAED,6EAA6E;AAC7E,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,OAAe,GAAG,IAAI,CAG3E"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.TRACE_MODE_EXPRESSION = void 0;
|
|
37
|
+
exports.patchTraceMode = patchTraceMode;
|
|
38
|
+
exports.installTraceMode = installTraceMode;
|
|
39
|
+
/**
|
|
40
|
+
* Move an existing project's `use.trace` off the old default so local runs keep a trace.
|
|
41
|
+
*
|
|
42
|
+
* The old template paired `trace: 'on-first-retry'` with `retries: process.env.CI ? 2 : 0`: off CI
|
|
43
|
+
* there is no retry, so there is never a "first retry" and a QA fixing a test on their machine never
|
|
44
|
+
* got a trace. The template now records `on-first-retry` on CI and `retain-on-failure` locally.
|
|
45
|
+
*
|
|
46
|
+
* `playwright.config.ts` is written once at `sungen init` and never re-synced (see
|
|
47
|
+
* report-sanitize-reporter-install.ts, the same problem for the sanitize reporter), so `sungen update`
|
|
48
|
+
* calls this. It rewrites the trace line ONLY while it is still the old default — any other value is
|
|
49
|
+
* a choice the user made, and a wrong edit to a user's config is worse than a missing trace. The same
|
|
50
|
+
* goes for a `trace` inside `projects: [...]`: that is a per-project override, not the template default.
|
|
51
|
+
*/
|
|
52
|
+
const fs = __importStar(require("fs"));
|
|
53
|
+
const path = __importStar(require("path"));
|
|
54
|
+
/** The template's trace value: cheap on CI (retries exist there), a trace for every local failure. */
|
|
55
|
+
exports.TRACE_MODE_EXPRESSION = "process.env.CI ? 'on-first-retry' : 'retain-on-failure'";
|
|
56
|
+
const escapeRegExp = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
57
|
+
// Both are anchored to the start of a line, so a commented-out copy (`// trace: …`) never counts.
|
|
58
|
+
// OLD_DEFAULT keeps the indentation in $1 and leaves the trailing comma, comment and EOL alone.
|
|
59
|
+
const OLD_DEFAULT = /^([ \t]*trace:[ \t]*)(['"])on-first-retry\2/gm;
|
|
60
|
+
const PRESENT = new RegExp(`^[ \\t]*trace:[ \\t]*${escapeRegExp(exports.TRACE_MODE_EXPRESSION)}`, 'm');
|
|
61
|
+
// The old template's one-line comment, only when it sits directly above the (patched) trace line.
|
|
62
|
+
// A comment the user wrote is theirs and stays.
|
|
63
|
+
const OLD_COMMENT = new RegExp(`^([ \\t]*)/\\* Collect trace when retrying the failed test\\. See https://playwright\\.dev/docs/trace-viewer \\*/(\\r?\\n)(?=[ \\t]*trace:[ \\t]*${escapeRegExp(exports.TRACE_MODE_EXPRESSION)})`, 'm');
|
|
64
|
+
const newComment = (indent, eol) => `${indent}/* Trace for a failed test. CI: recorded on the first retry (CI retries). Local: no retries, so${eol}` +
|
|
65
|
+
`${indent} 'on-first-retry' would never record one — keep the trace of every failed test instead.${eol}` +
|
|
66
|
+
`${indent} See https://playwright.dev/docs/trace-viewer */${eol}`;
|
|
67
|
+
/**
|
|
68
|
+
* [start, end] offsets of each live `projects: [...]` array, matched by bracket depth. Brackets inside
|
|
69
|
+
* a `//` or `/* *\/` comment or a quoted string are skipped: one stray `]` in a note would otherwise
|
|
70
|
+
* close the span early and expose a per-project override to the patch.
|
|
71
|
+
*/
|
|
72
|
+
function projectsSpans(src) {
|
|
73
|
+
const spans = [];
|
|
74
|
+
for (const m of src.matchAll(/^[ \t]*projects\s*:\s*\[/gm)) {
|
|
75
|
+
const open = m.index + m[0].length - 1;
|
|
76
|
+
let depth = 0;
|
|
77
|
+
for (let i = open; i < src.length; i++) {
|
|
78
|
+
const ch = src[i];
|
|
79
|
+
if (ch === '/' && src[i + 1] === '/') {
|
|
80
|
+
const nl = src.indexOf('\n', i);
|
|
81
|
+
i = nl < 0 ? src.length : nl;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (ch === '/' && src[i + 1] === '*') {
|
|
85
|
+
const end = src.indexOf('*/', i + 2);
|
|
86
|
+
i = end < 0 ? src.length : end + 1;
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (ch === "'" || ch === '"' || ch === '`') {
|
|
90
|
+
for (i++; i < src.length && src[i] !== ch; i++)
|
|
91
|
+
if (src[i] === '\\')
|
|
92
|
+
i++;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (ch === '[')
|
|
96
|
+
depth++;
|
|
97
|
+
else if (ch === ']' && --depth === 0) {
|
|
98
|
+
spans.push([open, i]);
|
|
99
|
+
break;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return spans;
|
|
104
|
+
}
|
|
105
|
+
function patchTraceMode(cwd, dryRun = false) {
|
|
106
|
+
const configPath = path.join(cwd, 'playwright.config.ts');
|
|
107
|
+
if (!fs.existsSync(configPath))
|
|
108
|
+
return 'absent';
|
|
109
|
+
const src = fs.readFileSync(configPath, 'utf8');
|
|
110
|
+
if (PRESENT.test(src))
|
|
111
|
+
return 'present';
|
|
112
|
+
const hits = [...src.matchAll(OLD_DEFAULT)];
|
|
113
|
+
if (hits.length !== 1)
|
|
114
|
+
return 'custom'; // none: the user's own value · several: ambiguous, don't guess
|
|
115
|
+
const at = hits[0].index;
|
|
116
|
+
if (projectsSpans(src).some(([s, e]) => at > s && at < e))
|
|
117
|
+
return 'custom'; // a per-project override
|
|
118
|
+
if (!dryRun) {
|
|
119
|
+
const patched = src
|
|
120
|
+
.replace(OLD_DEFAULT, `$1${exports.TRACE_MODE_EXPRESSION}`)
|
|
121
|
+
.replace(OLD_COMMENT, (_m, indent, eol) => newComment(indent, eol));
|
|
122
|
+
fs.writeFileSync(configPath, patched);
|
|
123
|
+
}
|
|
124
|
+
return 'patched';
|
|
125
|
+
}
|
|
126
|
+
/** `sungen update` step: patch the trace mode and say so when it changed. */
|
|
127
|
+
function installTraceMode(cwd, dryRun = false) {
|
|
128
|
+
if (patchTraceMode(cwd, dryRun) !== 'patched')
|
|
129
|
+
return;
|
|
130
|
+
console.log(`${dryRun ? '📋 would update' : '✓ updated'} playwright.config.ts: local runs now keep a trace for every failed test (trace: ${exports.TRACE_MODE_EXPRESSION})`);
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=trace-mode-install.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trace-mode-install.js","sourceRoot":"","sources":["../../src/orchestrator/trace-mode-install.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The sungen-viewpoint pattern catalog by number — the `## N. <Name>` headings of
|
|
3
|
+
* `templates/ai-src/skills/sungen-viewpoint/group-*.md` — for the "UI Patterns Identified" table
|
|
4
|
+
* `sungen add` scaffolds into `test-viewpoint.md` (#842).
|
|
5
|
+
*
|
|
6
|
+
* The scaffold used to hard-code its own ten rows: eight of them numbered differently from the
|
|
7
|
+
* catalog (Data Table 2 vs 7, Modal 9 vs 12…), plus a "Pagination" pattern the catalog does not
|
|
8
|
+
* have, so authors copied the wrong numbers next to a skill that cites the right ones.
|
|
9
|
+
* `tests/harness/viewpoint-comments-and-patterns.run.ts` pins this list to the catalog headings.
|
|
10
|
+
*/
|
|
11
|
+
export interface ViewpointPattern {
|
|
12
|
+
n: number;
|
|
13
|
+
name: string;
|
|
14
|
+
/** Group F — native-app patterns, read IN ADDITION to 1–17 for a mobile screen. */
|
|
15
|
+
mobile?: boolean;
|
|
16
|
+
}
|
|
17
|
+
export declare const VIEWPOINT_PATTERNS: readonly ViewpointPattern[];
|
|
18
|
+
/** The scaffold's pattern-table rows: 1–17 for every screen, plus Group F on a mobile project. */
|
|
19
|
+
export declare function uiPatternRows(isMobile: boolean): string[];
|
|
20
|
+
//# sourceMappingURL=viewpoint-pattern-catalog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewpoint-pattern-catalog.d.ts","sourceRoot":"","sources":["../../src/orchestrator/viewpoint-pattern-catalog.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,WAAW,gBAAgB;IAC/B,CAAC,EAAE,MAAM,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,eAAO,MAAM,kBAAkB,EAAE,SAAS,gBAAgB,EAiCzD,CAAC;AAEF,kGAAkG;AAClG,wBAAgB,aAAa,CAAC,QAAQ,EAAE,OAAO,GAAG,MAAM,EAAE,CAIzD"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VIEWPOINT_PATTERNS = void 0;
|
|
4
|
+
exports.uiPatternRows = uiPatternRows;
|
|
5
|
+
exports.VIEWPOINT_PATTERNS = [
|
|
6
|
+
{ n: 1, name: 'Form & Inputs' },
|
|
7
|
+
{ n: 2, name: 'File Upload' },
|
|
8
|
+
{ n: 3, name: 'Import / Export' },
|
|
9
|
+
{ n: 4, name: 'Create / Add' },
|
|
10
|
+
{ n: 5, name: 'Update / Edit' },
|
|
11
|
+
{ n: 6, name: 'Delete' },
|
|
12
|
+
{ n: 7, name: 'Data Table' },
|
|
13
|
+
{ n: 8, name: 'Search' },
|
|
14
|
+
{ n: 9, name: 'Filter' },
|
|
15
|
+
{ n: 10, name: 'List / Card View' },
|
|
16
|
+
{ n: 11, name: 'Chart / Analytics' },
|
|
17
|
+
{ n: 12, name: 'Modal / Dialog' },
|
|
18
|
+
{ n: 13, name: 'Navigation' },
|
|
19
|
+
{ n: 14, name: 'Notification / Toast / Alert' },
|
|
20
|
+
{ n: 15, name: 'Login / Logout' },
|
|
21
|
+
{ n: 16, name: 'Register' },
|
|
22
|
+
{ n: 17, name: 'Password Management' },
|
|
23
|
+
{ n: 18, name: 'Bottom Sheet', mobile: true },
|
|
24
|
+
{ n: 19, name: 'Tab Bar / Bottom Navigation', mobile: true },
|
|
25
|
+
{ n: 20, name: 'Swipe-to-Delete Row', mobile: true },
|
|
26
|
+
{ n: 21, name: 'Native Picker', mobile: true },
|
|
27
|
+
{ n: 22, name: 'Permission Dialog', mobile: true },
|
|
28
|
+
{ n: 23, name: 'Biometric Prompt', mobile: true },
|
|
29
|
+
{ n: 24, name: 'Camera / Gallery Picker', mobile: true },
|
|
30
|
+
{ n: 25, name: 'Push Notification', mobile: true },
|
|
31
|
+
{ n: 26, name: 'Deep-link Entry', mobile: true },
|
|
32
|
+
{ n: 27, name: 'Offline Banner', mobile: true },
|
|
33
|
+
{ n: 28, name: 'Onboarding Carousel', mobile: true },
|
|
34
|
+
{ n: 29, name: 'Pull-to-Refresh List', mobile: true },
|
|
35
|
+
{ n: 30, name: 'Soft-Keyboard Overlap', mobile: true },
|
|
36
|
+
{ n: 31, name: 'Settings Toggle List', mobile: true },
|
|
37
|
+
{ n: 32, name: 'App Lifecycle & Orientation', mobile: true },
|
|
38
|
+
];
|
|
39
|
+
/** The scaffold's pattern-table rows: 1–17 for every screen, plus Group F on a mobile project. */
|
|
40
|
+
function uiPatternRows(isMobile) {
|
|
41
|
+
return exports.VIEWPOINT_PATTERNS
|
|
42
|
+
.filter((p) => isMobile || !p.mobile)
|
|
43
|
+
.map((p) => `| ${p.n} | ${p.name} | ☐ | |`);
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=viewpoint-pattern-catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"viewpoint-pattern-catalog.js","sourceRoot":"","sources":["../../src/orchestrator/viewpoint-pattern-catalog.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sun-asterisk/sungen",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.34",
|
|
4
4
|
"description": "Deterministic E2E Test Compiler - Gherkin + Selectors → Playwright tests",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
"@babel/types": "^7.28.5",
|
|
41
41
|
"@cucumber/gherkin": "^37.0.0",
|
|
42
42
|
"@cucumber/messages": "^31.0.0",
|
|
43
|
-
"@sungen/driver-data-factory": "3.2.
|
|
44
|
-
"@sungen/driver-ui": "3.2.
|
|
43
|
+
"@sungen/driver-data-factory": "3.2.34",
|
|
44
|
+
"@sungen/driver-ui": "3.2.34",
|
|
45
45
|
"chalk": "^5.6.2",
|
|
46
46
|
"commander": "^14.0.2",
|
|
47
47
|
"dotenv": "^17.2.3",
|
|
@@ -7,11 +7,22 @@ import { acceptViewpointBaseline } from '../../harness/viewpoint-baseline';
|
|
|
7
7
|
import { findUnitDir } from '../resolve-unit';
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
function bar(ratio: number, width = 20): string {
|
|
11
|
-
|
|
10
|
+
export function bar(ratio: number, width = 20): string {
|
|
11
|
+
// A display helper must not be able to take the CLI down whatever the model hands it: an
|
|
12
|
+
// out-of-range ratio made `String.repeat` throw `Invalid count value: -5`, which killed the
|
|
13
|
+
// run mid-scorecard so the gate, the findings and the next-step hint never printed (#762).
|
|
14
|
+
const n = Math.max(0, Math.min(width, Math.round((Number.isFinite(ratio) ? ratio : 0) * width)));
|
|
12
15
|
return '█'.repeat(n) + '░'.repeat(width - n);
|
|
13
16
|
}
|
|
14
17
|
|
|
18
|
+
/** The ⑥ headline. No viewpoint ID read → nothing was matched, so `0%` would read as a measured
|
|
19
|
+
* failure; say `n/a` and why instead (#832). Same with no scenario yet (#852). */
|
|
20
|
+
export function traceabilityHeadline(trace: AuditReport['trace']): string {
|
|
21
|
+
if (trace.overviewIds === 0) return ' ⑥ Traceability — n/a (no viewpoint ID found in test-viewpoint.md)';
|
|
22
|
+
if (trace.total === 0) return ' ⑥ Traceability — n/a (no scenarios yet)';
|
|
23
|
+
return ` ⑥ Traceability — ${(trace.mappedRatio * 100).toFixed(0)}% scenarios linked to viewpoint-overview`;
|
|
24
|
+
}
|
|
25
|
+
|
|
15
26
|
function render(r: AuditReport): void {
|
|
16
27
|
const L = console.log;
|
|
17
28
|
L('');
|
|
@@ -47,6 +58,11 @@ function render(r: AuditReport): void {
|
|
|
47
58
|
L(` ① Viewpoint Gate — page-type: ${r.gate.pageType ?? 'unknown'} — themes ${r.gate.themesCovered}/${r.gate.themesTotal} covered`);
|
|
48
59
|
if (r.gate.gaps.length) for (const g of r.gate.gaps) L(` ✗ ${g.status === 'shallow' ? 'SHALLOW' : 'MISSING'}: ${g.theme}`);
|
|
49
60
|
else L(' ✓ all critical themes covered (with data assertions)');
|
|
61
|
+
// Name the scenario behind each covered theme (#776). A bare count gives a reviewer nothing to
|
|
62
|
+
// check, so a keyword matching something absurd stays invisible; the evidence line makes it
|
|
63
|
+
// obvious. Second line of defence — it holds even if the matcher is wrong again.
|
|
64
|
+
for (const c of r.gate.coveredBy ?? [])
|
|
65
|
+
L(` ✓ ${c.theme} ← [${c.keyword}] ${c.by.length > 88 ? `${c.by.slice(0, 88)}…` : c.by}`);
|
|
50
66
|
L('');
|
|
51
67
|
if (r.flowQuality?.hasContract) {
|
|
52
68
|
const fq = r.flowQuality;
|
|
@@ -74,7 +90,7 @@ function render(r: AuditReport): void {
|
|
|
74
90
|
if (r.depth.shallowBusinessCritical.length > 8) L(` … +${r.depth.shallowBusinessCritical.length - 8} more`);
|
|
75
91
|
L('');
|
|
76
92
|
L(` ③ Claim-Proof — ${r.claim.proven}/${r.claim.withClaims} titled claims proven by steps [${r.claim.verdict.toUpperCase()}]`);
|
|
77
|
-
if (r.claim.unproven.length === 0) L(' ✓ every claim (all/only/single/correct/changes/hidden) is backed by an assertion');
|
|
93
|
+
if (r.claim.unproven.length === 0) L(' ✓ every claim (all/only/single/correct/changes/hidden/order/limit) is backed by an assertion');
|
|
78
94
|
for (const u of r.claim.unproven.slice(0, 8)) L(` ${u.severity === 'fail' ? '✗' : '⚠'} [${u.claim}] ${u.name} → needs ${u.need}`);
|
|
79
95
|
if (r.claim.unproven.length > 8) L(` … +${r.claim.unproven.length - 8} more`);
|
|
80
96
|
L('');
|
|
@@ -102,7 +118,7 @@ function render(r: AuditReport): void {
|
|
|
102
118
|
}
|
|
103
119
|
}
|
|
104
120
|
L('');
|
|
105
|
-
L(
|
|
121
|
+
L(traceabilityHeadline(r.trace));
|
|
106
122
|
L(` ${r.trace.note}`);
|
|
107
123
|
L('');
|
|
108
124
|
if (r.spec.hasSpec && (r.spec.frTotal > 0 || r.spec.triggerGaps.length > 0 || r.spec.verdict !== 'pass')) {
|
|
@@ -190,7 +206,10 @@ export function registerAuditCommand(program: Command): void {
|
|
|
190
206
|
console.log(' Re-run the audit to score atomicLedger + traceability against it.\n');
|
|
191
207
|
}
|
|
192
208
|
} else if (options.acceptViewpoint && !options.json) {
|
|
193
|
-
|
|
209
|
+
// `absent` covers a file with no readable viewpoint id too — say which (#832).
|
|
210
|
+
console.log(vb.status === 'absent'
|
|
211
|
+
? `\n ○ nothing to accept for ${name} — no viewpoint ID found in requirements/test-viewpoint.md (missing file, or no section declares one).\n`
|
|
212
|
+
: `\n ○ nothing to accept for ${name} — the viewpoint baseline is ${vb.status}.\n`);
|
|
194
213
|
}
|
|
195
214
|
|
|
196
215
|
// Persist report under .sungen/reports/ (flat slug: api flow `flows/x` → `flows-x-audit.json`).
|
|
@@ -4,7 +4,8 @@ import { createRequire } from 'module';
|
|
|
4
4
|
import * as fs from 'fs';
|
|
5
5
|
import * as path from 'path';
|
|
6
6
|
import {
|
|
7
|
-
readCapabilities, writeCapabilities, loadDriverCatalog, driverMeta,
|
|
7
|
+
readCapabilities, writeCapabilities, loadDriverCatalog, driverMeta, capabilitiesPath,
|
|
8
|
+
type CapabilityProfile,
|
|
8
9
|
} from '../../harness/capability';
|
|
9
10
|
import { buildPlan, MANUAL_REASONS } from '../../harness/capability-plan';
|
|
10
11
|
import { adapterRegistry } from '../../generators/test-generator/adapters';
|
|
@@ -12,26 +13,37 @@ import { emitMobileRuntime, installMobileDriver } from '../../orchestrator/mobil
|
|
|
12
13
|
import { emitAppiumEnvironment, runAppiumEnvironmentSetup } from '../../orchestrator/appium-environment-setup';
|
|
13
14
|
import { coreVersion, resolveDriverVersion } from '../../orchestrator/lockstep-version';
|
|
14
15
|
import { findUnitDir, resolveUnit } from '../resolve-unit';
|
|
16
|
+
import { readTextFile } from '../../harness/read-text';
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
|
-
* Add `test:mobile` to the project's package.json scripts (if absent).
|
|
18
|
-
*
|
|
19
|
-
*
|
|
19
|
+
* Add `test:mobile` to the project's package.json scripts (if absent). Idempotent.
|
|
20
|
+
*
|
|
21
|
+
* Four outcomes, not two. The old boolean collapsed "already present", "no package.json" and
|
|
22
|
+
* "could not parse it" into a single `false`, and the caller rendered that as
|
|
23
|
+
* `– skipped package.json scripts[test:mobile] (already exists)` — which, on a malformed
|
|
24
|
+
* package.json, states the exact opposite of the truth: the script is absent, the command promised
|
|
25
|
+
* it, and the next thing the user sees is `npm error Missing script: "test:mobile"`.
|
|
20
26
|
*/
|
|
21
|
-
|
|
27
|
+
type MobileScriptOutcome =
|
|
28
|
+
| { state: 'added' }
|
|
29
|
+
| { state: 'already-present' }
|
|
30
|
+
| { state: 'no-package-json' }
|
|
31
|
+
| { state: 'unreadable'; error: string };
|
|
32
|
+
|
|
33
|
+
function addMobileScript(cwd: string): MobileScriptOutcome {
|
|
22
34
|
const pkgPath = path.join(cwd, 'package.json');
|
|
23
|
-
if (!fs.existsSync(pkgPath)) return
|
|
35
|
+
if (!fs.existsSync(pkgPath)) return { state: 'no-package-json' };
|
|
24
36
|
let pkg: Record<string, any>;
|
|
25
37
|
try {
|
|
26
|
-
pkg = JSON.parse(
|
|
27
|
-
} catch {
|
|
28
|
-
return
|
|
38
|
+
pkg = JSON.parse(readTextFile(pkgPath)); // BOM-tolerant: npm reads such a file fine, so must we
|
|
39
|
+
} catch (e) {
|
|
40
|
+
return { state: 'unreadable', error: (e as Error).message.split('\n')[0] };
|
|
29
41
|
}
|
|
30
42
|
if (!pkg.scripts || typeof pkg.scripts !== 'object') pkg.scripts = {};
|
|
31
|
-
if (pkg.scripts['test:mobile']) return
|
|
43
|
+
if (pkg.scripts['test:mobile']) return { state: 'already-present' };
|
|
32
44
|
pkg.scripts['test:mobile'] = 'wdio run wdio.conf.ts';
|
|
33
45
|
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
|
|
34
|
-
return
|
|
46
|
+
return { state: 'added' };
|
|
35
47
|
}
|
|
36
48
|
|
|
37
49
|
/**
|
|
@@ -221,9 +233,13 @@ export function registerCapabilityCommand(program: Command): void {
|
|
|
221
233
|
}
|
|
222
234
|
|
|
223
235
|
const profile = readCapabilities(cwd);
|
|
236
|
+
assertProfileWritable(profile, cwd);
|
|
224
237
|
const enabled = Array.from(new Set([...profile.enabled, driver]));
|
|
225
238
|
const platform = meta.kind === 'platform' ? driver : profile.platform;
|
|
226
|
-
|
|
239
|
+
// Carry `verification` through. It is the recorded TEST TYPE, not a derived value: dropping
|
|
240
|
+
// it here silently re-derives the scope from `enabled`, handing an E2E-only project the
|
|
241
|
+
// @api/@query verification it had explicitly narrowed away.
|
|
242
|
+
writeCapabilities(cwd, { platform, enabled, verification: profile.verification });
|
|
227
243
|
console.log(`✓ capability "${driver}" enabled${meta.kind === 'platform' ? ` (platform=${driver})` : ''}.`);
|
|
228
244
|
|
|
229
245
|
// Emit the mobile runtime harness when the mobile platform driver is added.
|
|
@@ -236,11 +252,11 @@ export function registerCapabilityCommand(program: Command): void {
|
|
|
236
252
|
for (const f of result.skipped) console.log(` – skipped ${f} (already exists)`);
|
|
237
253
|
|
|
238
254
|
// Add test:mobile script to package.json (idempotent)
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}
|
|
255
|
+
const script = addMobileScript(cwd);
|
|
256
|
+
if (script.state === 'added') console.log(' ✓ created package.json scripts[test:mobile]');
|
|
257
|
+
else if (script.state === 'already-present') console.log(' – skipped package.json scripts[test:mobile] (already exists)');
|
|
258
|
+
else if (script.state === 'no-package-json') console.log(' ✗ NOT added package.json scripts[test:mobile] — no package.json here; run `npm init -y` first, then add: "test:mobile": "wdio run wdio.conf.ts"');
|
|
259
|
+
else console.log(` ✗ NOT added package.json scripts[test:mobile] — package.json is not valid JSON (${script.error}); fix it, then add: "test:mobile": "wdio run wdio.conf.ts"`);
|
|
244
260
|
|
|
245
261
|
// appium-environment/ provisioning bundle (idempotent per file) — emitted before the driver
|
|
246
262
|
// install, run after it so its Appium step finds the project-local CLI.
|
|
@@ -262,6 +278,22 @@ export function registerCapabilityCommand(program: Command): void {
|
|
|
262
278
|
}
|
|
263
279
|
});
|
|
264
280
|
|
|
281
|
+
/**
|
|
282
|
+
* Every path that WRITES qa/capabilities.yaml first reads it, edits one field, and writes the whole
|
|
283
|
+
* file back. That round-trip is only safe when the read succeeded: an unreadable profile reads as
|
|
284
|
+
* "no platform, no drivers", so the write-back would replace the user's declaration with just the
|
|
285
|
+
* one driver being added — and `remove` would replace it with nothing at all. Refuse instead.
|
|
286
|
+
*/
|
|
287
|
+
function assertProfileWritable(profile: CapabilityProfile, cwd: string): void {
|
|
288
|
+
if (profile.source !== 'unreadable') return;
|
|
289
|
+
console.error(
|
|
290
|
+
`✗ ${path.relative(cwd, capabilitiesPath(cwd))} exists but could not be parsed.\n` +
|
|
291
|
+
` Refusing to rewrite it: this command replaces the whole file, so it would discard the\n` +
|
|
292
|
+
` platform, drivers and verification scope you declared. Fix the YAML and re-run.`,
|
|
293
|
+
);
|
|
294
|
+
process.exit(1);
|
|
295
|
+
}
|
|
296
|
+
|
|
265
297
|
// ── remove ─────────────────────────────────────────────────────────────
|
|
266
298
|
cap
|
|
267
299
|
.command('remove <driver>')
|
|
@@ -271,9 +303,10 @@ export function registerCapabilityCommand(program: Command): void {
|
|
|
271
303
|
try {
|
|
272
304
|
const cwd = process.cwd();
|
|
273
305
|
const profile = readCapabilities(cwd);
|
|
306
|
+
assertProfileWritable(profile, cwd);
|
|
274
307
|
const enabled = profile.enabled.filter((d) => d !== driver);
|
|
275
308
|
const platform = profile.platform === driver ? undefined : profile.platform;
|
|
276
|
-
writeCapabilities(cwd, { platform, enabled });
|
|
309
|
+
writeCapabilities(cwd, { platform, enabled, verification: profile.verification });
|
|
277
310
|
const meta = driverMeta(driver);
|
|
278
311
|
if (o.uninstall && meta && !adapterRegistry.hasAdapter(meta.adapter || meta.id)) {
|
|
279
312
|
spawnSync('npm', ['uninstall', meta.package], { stdio: 'inherit', shell: true });
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import { Command } from 'commander';
|
|
13
13
|
import { buildRunManifest, readRunManifest, writeRunManifest, selectChanged } from '../../harness/run-manifest';
|
|
14
14
|
import { findUnitDir } from '../resolve-unit';
|
|
15
|
+
import { titlesToGrep } from '../../harness/select-by-ids';
|
|
15
16
|
import { execFileSync } from 'child_process';
|
|
16
17
|
import * as path from 'path';
|
|
17
18
|
|
|
@@ -40,9 +41,6 @@ function gitChangedFiles(unitDir: string): string[] {
|
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
/** Escape a scenario title for a `-g` / `--mochaOpts.grep` regex. */
|
|
44
|
-
const toGrep = (titles: string[]): string =>
|
|
45
|
-
titles.map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|');
|
|
46
44
|
|
|
47
45
|
export function registerChangedCommand(program: Command): void {
|
|
48
46
|
program
|
|
@@ -75,7 +73,7 @@ export function registerChangedCommand(program: Command): void {
|
|
|
75
73
|
if (options.grep) {
|
|
76
74
|
// Empty output is meaningful: nothing to run. A caller must treat "" as "skip the run",
|
|
77
75
|
// never as "match everything" — an empty grep would run the whole suite.
|
|
78
|
-
console.log(
|
|
76
|
+
console.log(titlesToGrep(selection.scenarios));
|
|
79
77
|
return;
|
|
80
78
|
}
|
|
81
79
|
if (options.json) { console.log(JSON.stringify(selection, null, 2)); return; }
|