@sun-asterisk/sungen 3.2.28 → 3.2.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/capabilities/discover.d.ts +29 -0
- package/dist/capabilities/discover.d.ts.map +1 -1
- package/dist/capabilities/discover.js +144 -10
- package/dist/capabilities/discover.js.map +1 -1
- package/dist/capabilities/registry.d.ts +18 -0
- package/dist/capabilities/registry.d.ts.map +1 -1
- package/dist/capabilities/registry.js +25 -0
- package/dist/capabilities/registry.js.map +1 -1
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/audit.js +9 -0
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/capability.js +1 -1
- package/dist/cli/commands/capability.js.map +1 -1
- package/dist/cli/commands/delivery.d.ts.map +1 -1
- package/dist/cli/commands/delivery.js +2 -1
- 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 +6 -2
- package/dist/cli/commands/generate.js.map +1 -1
- package/dist/cli/commands/inspect.js +1 -1
- package/dist/cli/commands/inspect.js.map +1 -1
- package/dist/cli/commands/results-merge.d.ts.map +1 -1
- package/dist/cli/commands/results-merge.js +9 -2
- package/dist/cli/commands/results-merge.js.map +1 -1
- package/dist/cli/commands/results-sanitize.d.ts +24 -0
- package/dist/cli/commands/results-sanitize.d.ts.map +1 -0
- package/dist/cli/commands/results-sanitize.js +126 -0
- package/dist/cli/commands/results-sanitize.js.map +1 -0
- package/dist/cli/commands/script-check.d.ts.map +1 -1
- package/dist/cli/commands/script-check.js +3 -1
- package/dist/cli/commands/script-check.js.map +1 -1
- 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/dashboard/templates/index.html +1 -1
- package/dist/exporters/api-testcase-formatter.d.ts +3 -6
- package/dist/exporters/api-testcase-formatter.d.ts.map +1 -1
- package/dist/exporters/api-testcase-formatter.js +7 -10
- package/dist/exporters/api-testcase-formatter.js.map +1 -1
- package/dist/exporters/feature-parser.d.ts +1 -1
- package/dist/exporters/feature-parser.js +1 -1
- package/dist/exporters/matrix/build.d.ts.map +1 -1
- package/dist/exporters/matrix/build.js +18 -3
- package/dist/exporters/matrix/build.js.map +1 -1
- package/dist/exporters/matrix/item-labels.d.ts +35 -0
- package/dist/exporters/matrix/item-labels.d.ts.map +1 -0
- package/dist/exporters/matrix/item-labels.js +90 -0
- package/dist/exporters/matrix/item-labels.js.map +1 -0
- package/dist/exporters/matrix/render-coverage-sheet.d.ts +14 -0
- package/dist/exporters/matrix/render-coverage-sheet.d.ts.map +1 -0
- package/dist/exporters/matrix/render-coverage-sheet.js +162 -0
- package/dist/exporters/matrix/render-coverage-sheet.js.map +1 -0
- package/dist/exporters/matrix/render-csv.d.ts.map +1 -1
- package/dist/exporters/matrix/render-csv.js +3 -1
- package/dist/exporters/matrix/render-csv.js.map +1 -1
- package/dist/exporters/matrix/render-overview-sheet.d.ts +20 -0
- package/dist/exporters/matrix/render-overview-sheet.d.ts.map +1 -0
- package/dist/exporters/matrix/render-overview-sheet.js +32 -0
- package/dist/exporters/matrix/render-overview-sheet.js.map +1 -0
- package/dist/exporters/matrix/render-testcase-sheet.d.ts +30 -0
- package/dist/exporters/matrix/render-testcase-sheet.d.ts.map +1 -0
- package/dist/exporters/matrix/render-testcase-sheet.js +156 -0
- package/dist/exporters/matrix/render-testcase-sheet.js.map +1 -0
- package/dist/exporters/matrix/render-xlsx.d.ts +41 -31
- package/dist/exporters/matrix/render-xlsx.d.ts.map +1 -1
- package/dist/exporters/matrix/render-xlsx.js +72 -425
- package/dist/exporters/matrix/render-xlsx.js.map +1 -1
- package/dist/exporters/matrix/result-totals.d.ts +23 -0
- package/dist/exporters/matrix/result-totals.d.ts.map +1 -0
- package/dist/exporters/matrix/result-totals.js +21 -0
- package/dist/exporters/matrix/result-totals.js.map +1 -0
- package/dist/exporters/matrix/testcase-summary-band.d.ts +30 -0
- package/dist/exporters/matrix/testcase-summary-band.d.ts.map +1 -0
- package/dist/exporters/matrix/testcase-summary-band.js +71 -0
- package/dist/exporters/matrix/testcase-summary-band.js.map +1 -0
- package/dist/exporters/matrix/wording.d.ts.map +1 -1
- package/dist/exporters/matrix/wording.js +13 -0
- package/dist/exporters/matrix/wording.js.map +1 -1
- package/dist/exporters/matrix/xlsx-cells.d.ts +38 -0
- package/dist/exporters/matrix/xlsx-cells.d.ts.map +1 -0
- package/dist/exporters/matrix/xlsx-cells.js +52 -0
- package/dist/exporters/matrix/xlsx-cells.js.map +1 -0
- package/dist/exporters/qa-env-secret-denylist.js +2 -2
- package/dist/exporters/qa-env-secret-denylist.js.map +1 -1
- package/dist/exporters/report-path-sanitizer.d.ts +38 -0
- package/dist/exporters/report-path-sanitizer.d.ts.map +1 -0
- package/dist/exporters/report-path-sanitizer.js +137 -0
- package/dist/exporters/report-path-sanitizer.js.map +1 -0
- package/dist/exporters/result-variants.d.ts.map +1 -1
- package/dist/exporters/result-variants.js +2 -1
- package/dist/exporters/result-variants.js.map +1 -1
- package/dist/exporters/sensitive-key.d.ts +31 -0
- package/dist/exporters/sensitive-key.d.ts.map +1 -0
- package/dist/exporters/sensitive-key.js +50 -0
- package/dist/exporters/sensitive-key.js.map +1 -0
- package/dist/exporters/test-data-resolver.d.ts +4 -1
- package/dist/exporters/test-data-resolver.d.ts.map +1 -1
- package/dist/exporters/test-data-resolver.js +12 -2
- package/dist/exporters/test-data-resolver.js.map +1 -1
- package/dist/exporters/xlsx-report-builder.d.ts +12 -1
- package/dist/exporters/xlsx-report-builder.d.ts.map +1 -1
- package/dist/exporters/xlsx-report-builder.js +10 -2
- package/dist/exporters/xlsx-report-builder.js.map +1 -1
- package/dist/generators/gherkin-parser/index.d.ts +10 -0
- package/dist/generators/gherkin-parser/index.d.ts.map +1 -1
- package/dist/generators/gherkin-parser/index.js +20 -12
- package/dist/generators/gherkin-parser/index.js.map +1 -1
- package/dist/generators/gherkin-parser/parent-scope-clause.d.ts +54 -0
- package/dist/generators/gherkin-parser/parent-scope-clause.d.ts.map +1 -0
- package/dist/generators/gherkin-parser/parent-scope-clause.js +109 -0
- package/dist/generators/gherkin-parser/parent-scope-clause.js.map +1 -0
- package/dist/generators/test-generator/adapters/adapter-interface.d.ts +23 -18
- package/dist/generators/test-generator/adapters/adapter-interface.d.ts.map +1 -1
- package/dist/generators/test-generator/adapters/appium/appium-adapter.d.ts +4 -19
- package/dist/generators/test-generator/adapters/appium/appium-adapter.d.ts.map +1 -1
- package/dist/generators/test-generator/adapters/appium/appium-adapter.js.map +1 -1
- package/dist/generators/test-generator/adapters/appium/os-support.d.ts +53 -0
- package/dist/generators/test-generator/adapters/appium/os-support.d.ts.map +1 -0
- package/dist/generators/test-generator/adapters/appium/os-support.js +200 -0
- package/dist/generators/test-generator/adapters/appium/os-support.js.map +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/after-all.hbs +18 -0
- package/dist/generators/test-generator/adapters/appium/templates/after-each.hbs +18 -0
- package/dist/generators/test-generator/adapters/appium/templates/before-all.hbs +18 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/alert-fill-action.hbs +9 -2
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/capture-variable.hbs +24 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/click-at-position-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/click-element-with-text.hbs +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/double-click-action.hbs +7 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/frame-enter-action.hbs +3 -6
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/hover-action.hbs +6 -2
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/hover-element-with-text.hbs +4 -3
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/table-paginate-action.hbs +3 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/unknown-element-action.hbs +9 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/have-value-assertion.hbs +34 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/label-value-assertion.hbs +14 -6
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/visible-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/go-back-action.hbs +21 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/long-press-action.hbs +4 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/pinch-zoom-action.hbs +4 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/set-geolocation-action.hbs +16 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/partials/locator.hbs +9 -1
- package/dist/generators/test-generator/adapters/appium/templates/test-file.hbs +62 -23
- package/dist/generators/test-generator/adapters/index.d.ts +1 -1
- package/dist/generators/test-generator/adapters/index.d.ts.map +1 -1
- package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts +4 -19
- package/dist/generators/test-generator/adapters/playwright/playwright-adapter.d.ts.map +1 -1
- package/dist/generators/test-generator/adapters/playwright/playwright-adapter.js.map +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/after-all.hbs +19 -1
- package/dist/generators/test-generator/adapters/playwright/templates/after-each.hbs +18 -0
- package/dist/generators/test-generator/adapters/playwright/templates/before-all.hbs +19 -1
- package/dist/generators/test-generator/adapters/playwright/templates/imports.hbs +3 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/check-action.hbs +6 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/click-at-position-action.hbs +23 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/expand-action.hbs +8 -8
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/fill-editor-action.hbs +7 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row-nth.hbs +7 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row.hbs +7 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/table-paginate-action.hbs +5 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/uncheck-action.hbs +3 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/unknown-element-action.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/wait-for-page.hbs +18 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/all-contain-assertion.hbs +32 -4
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/all-contain-element.hbs +5 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/checked-assertion.hbs +5 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/column-cell-assertion.hbs +13 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +2 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-variable-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/have-text-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +2 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-variable-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/not-checked-assertion.hbs +2 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/selected-assertion.hbs +21 -3
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/state-with-filter-assertion.hbs +20 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-empty.hbs +4 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-match-data.hbs +4 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-count.hbs +4 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-exists.hbs +12 -4
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-not-exists.hbs +14 -2
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +3 -3
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-variable-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/checked-opts.hbs +8 -0
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +8 -8
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-nth.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/id.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/testid.hbs +1 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +8 -8
- package/dist/generators/test-generator/adapters/playwright/templates/test-file.hbs +10 -8
- package/dist/generators/test-generator/code-generator.d.ts +37 -0
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +165 -40
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/generators/test-generator/diagnostics.d.ts +116 -6
- package/dist/generators/test-generator/diagnostics.d.ts.map +1 -1
- package/dist/generators/test-generator/diagnostics.js +115 -5
- package/dist/generators/test-generator/diagnostics.js.map +1 -1
- package/dist/generators/test-generator/patterns/index.d.ts +57 -11
- package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/index.js +163 -21
- package/dist/generators/test-generator/patterns/index.js.map +1 -1
- package/dist/generators/test-generator/patterns/types.d.ts +8 -0
- package/dist/generators/test-generator/patterns/types.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/with-parent-scope.d.ts +18 -0
- package/dist/generators/test-generator/patterns/with-parent-scope.d.ts.map +1 -0
- package/dist/generators/test-generator/patterns/with-parent-scope.js +40 -0
- package/dist/generators/test-generator/patterns/with-parent-scope.js.map +1 -0
- package/dist/generators/test-generator/step-mapper.d.ts +103 -10
- package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.js +266 -45
- package/dist/generators/test-generator/step-mapper.js.map +1 -1
- package/dist/generators/test-generator/template-engine.d.ts +4 -19
- package/dist/generators/test-generator/template-engine.d.ts.map +1 -1
- package/dist/generators/test-generator/template-engine.js +17 -12
- package/dist/generators/test-generator/template-engine.js.map +1 -1
- package/dist/generators/test-generator/utils/data-resolver.d.ts +7 -0
- package/dist/generators/test-generator/utils/data-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/data-resolver.js +19 -2
- package/dist/generators/test-generator/utils/data-resolver.js.map +1 -1
- package/dist/generators/test-generator/utils/dialog-scope-identity.d.ts +41 -0
- package/dist/generators/test-generator/utils/dialog-scope-identity.d.ts.map +1 -0
- package/dist/generators/test-generator/utils/dialog-scope-identity.js +61 -0
- package/dist/generators/test-generator/utils/dialog-scope-identity.js.map +1 -0
- package/dist/generators/test-generator/utils/escape-js-string.d.ts +17 -0
- package/dist/generators/test-generator/utils/escape-js-string.d.ts.map +1 -0
- package/dist/generators/test-generator/utils/escape-js-string.js +29 -0
- package/dist/generators/test-generator/utils/escape-js-string.js.map +1 -0
- package/dist/generators/test-generator/utils/nearest-match.d.ts +24 -0
- package/dist/generators/test-generator/utils/nearest-match.d.ts.map +1 -0
- package/dist/generators/test-generator/utils/nearest-match.js +56 -0
- package/dist/generators/test-generator/utils/nearest-match.js.map +1 -0
- package/dist/generators/test-generator/utils/selector-entry-fields.d.ts +58 -0
- package/dist/generators/test-generator/utils/selector-entry-fields.d.ts.map +1 -0
- package/dist/generators/test-generator/utils/selector-entry-fields.js +119 -0
- package/dist/generators/test-generator/utils/selector-entry-fields.js.map +1 -0
- package/dist/generators/test-generator/utils/selector-entry-walk.d.ts +25 -0
- package/dist/generators/test-generator/utils/selector-entry-walk.d.ts.map +1 -0
- package/dist/generators/test-generator/utils/selector-entry-walk.js +54 -0
- package/dist/generators/test-generator/utils/selector-entry-walk.js.map +1 -0
- package/dist/generators/test-generator/utils/selector-nth-validation.d.ts +59 -0
- package/dist/generators/test-generator/utils/selector-nth-validation.d.ts.map +1 -0
- package/dist/generators/test-generator/utils/selector-nth-validation.js +91 -0
- package/dist/generators/test-generator/utils/selector-nth-validation.js.map +1 -0
- package/dist/generators/test-generator/utils/selector-resolver.d.ts +53 -2
- package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.js +273 -27
- package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
- package/dist/generators/test-generator/utils/selector-type-validation.d.ts +51 -0
- package/dist/generators/test-generator/utils/selector-type-validation.d.ts.map +1 -0
- package/dist/generators/test-generator/utils/selector-type-validation.js +77 -0
- package/dist/generators/test-generator/utils/selector-type-validation.js.map +1 -0
- package/dist/harness/audit.d.ts +3 -1
- package/dist/harness/audit.d.ts.map +1 -1
- package/dist/harness/audit.js +51 -28
- package/dist/harness/audit.js.map +1 -1
- package/dist/harness/capability-plan.d.ts +4 -0
- package/dist/harness/capability-plan.d.ts.map +1 -1
- package/dist/harness/capability-plan.js +32 -2
- package/dist/harness/capability-plan.js.map +1 -1
- package/dist/harness/catalog/drivers.yaml +1 -1
- package/dist/harness/catalog/universal-viewpoints.yaml +145 -0
- package/dist/harness/data-driven-lint.d.ts.map +1 -1
- package/dist/harness/data-driven-lint.js +217 -33
- package/dist/harness/data-driven-lint.js.map +1 -1
- package/dist/harness/parse.d.ts +6 -0
- package/dist/harness/parse.d.ts.map +1 -1
- package/dist/harness/parse.js +1 -1
- package/dist/harness/parse.js.map +1 -1
- package/dist/harness/repair.d.ts.map +1 -1
- package/dist/harness/repair.js +8 -1
- package/dist/harness/repair.js.map +1 -1
- package/dist/harness/script-check.d.ts +2 -2
- package/dist/harness/script-check.d.ts.map +1 -1
- package/dist/harness/script-check.js +186 -25
- package/dist/harness/script-check.js.map +1 -1
- package/dist/harness/sensors.d.ts +48 -0
- package/dist/harness/sensors.d.ts.map +1 -1
- package/dist/harness/sensors.js +26 -1
- package/dist/harness/sensors.js.map +1 -1
- package/dist/harness/viewpoint-ledger.d.ts +10 -0
- package/dist/harness/viewpoint-ledger.d.ts.map +1 -1
- package/dist/harness/viewpoint-ledger.js +23 -2
- package/dist/harness/viewpoint-ledger.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -1
- package/dist/index.js.map +1 -1
- package/dist/orchestrator/project-initializer.d.ts +6 -0
- package/dist/orchestrator/project-initializer.d.ts.map +1 -1
- package/dist/orchestrator/project-initializer.js +16 -0
- package/dist/orchestrator/project-initializer.js.map +1 -1
- package/dist/orchestrator/report-sanitize-reporter-install.d.ts +24 -0
- package/dist/orchestrator/report-sanitize-reporter-install.d.ts.map +1 -0
- package/dist/orchestrator/report-sanitize-reporter-install.js +161 -0
- package/dist/orchestrator/report-sanitize-reporter-install.js.map +1 -0
- package/dist/orchestrator/templates/ai-src/commands/delivery.md +4 -3
- package/dist/orchestrator/templates/ai-src/commands/run-test.md +9 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-api-coverage-model/SKILL.md +10 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +14 -5
- package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +62 -3
- package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +126 -11
- package/dist/orchestrator/templates/ai-src/skills/sungen-mobile-gestures/SKILL.md +16 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +12 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-selector-keys/SKILL.md +19 -5
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +88 -2
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +62 -5
- package/dist/orchestrator/templates/ai-src/skills/sungen-test-design-techniques/SKILL.md +13 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-viewpoint/SKILL.md +59 -10
- package/dist/orchestrator/templates/ai-src/skills/sungen-viewpoint/group-f-mobile-native.md +419 -0
- package/dist/orchestrator/templates/playwright.config.ts +7 -0
- package/dist/orchestrator/templates/readme.md +1 -1
- package/dist/orchestrator/templates/specs-checked-state.d.ts +83 -0
- package/dist/orchestrator/templates/specs-checked-state.d.ts.map +1 -0
- package/dist/orchestrator/templates/specs-checked-state.js +261 -0
- package/dist/orchestrator/templates/specs-checked-state.js.map +1 -0
- package/dist/orchestrator/templates/specs-checked-state.ts +324 -0
- package/dist/orchestrator/templates/specs-pw-shape-reporter.ts +0 -0
- package/dist/orchestrator/templates/specs-report-path-sanitize-reporter.d.ts +36 -0
- package/dist/orchestrator/templates/specs-report-path-sanitize-reporter.d.ts.map +1 -0
- package/dist/orchestrator/templates/specs-report-path-sanitize-reporter.js +155 -0
- package/dist/orchestrator/templates/specs-report-path-sanitize-reporter.js.map +1 -0
- package/dist/orchestrator/templates/specs-report-path-sanitize-reporter.ts +142 -0
- package/dist/orchestrator/templates/specs-url-assert.d.ts +50 -25
- package/dist/orchestrator/templates/specs-url-assert.d.ts.map +1 -1
- package/dist/orchestrator/templates/specs-url-assert.js +65 -24
- package/dist/orchestrator/templates/specs-url-assert.js.map +1 -1
- package/dist/orchestrator/templates/specs-url-assert.ts +101 -28
- package/dist/orchestrator/templates/wdio.conf.ts +105 -1
- package/dist/utils/selector-types.d.ts +43 -1
- package/dist/utils/selector-types.d.ts.map +1 -1
- package/dist/utils/selector-types.js +51 -7
- package/dist/utils/selector-types.js.map +1 -1
- package/package.json +3 -3
- package/src/capabilities/discover.ts +146 -10
- package/src/capabilities/registry.ts +30 -0
- package/src/cli/commands/audit.ts +9 -0
- package/src/cli/commands/capability.ts +1 -1
- package/src/cli/commands/delivery.ts +2 -1
- package/src/cli/commands/generate.ts +5 -2
- package/src/cli/commands/inspect.ts +1 -1
- package/src/cli/commands/results-merge.ts +9 -2
- package/src/cli/commands/results-sanitize.ts +98 -0
- package/src/cli/commands/script-check.ts +3 -1
- package/src/cli/commands/update.ts +3 -0
- package/src/dashboard/templates/index.html +1 -1
- package/src/exporters/api-testcase-formatter.ts +5 -7
- package/src/exporters/feature-parser.ts +1 -1
- package/src/exporters/matrix/build.ts +17 -3
- package/src/exporters/matrix/item-labels.ts +78 -0
- package/src/exporters/matrix/render-coverage-sheet.ts +170 -0
- package/src/exporters/matrix/render-csv.ts +3 -1
- package/src/exporters/matrix/render-overview-sheet.ts +51 -0
- package/src/exporters/matrix/render-testcase-sheet.ts +178 -0
- package/src/exporters/matrix/render-xlsx.ts +67 -448
- package/src/exporters/matrix/result-totals.ts +37 -0
- package/src/exporters/matrix/testcase-summary-band.ts +74 -0
- package/src/exporters/matrix/wording.ts +13 -0
- package/src/exporters/matrix/xlsx-cells.ts +56 -0
- package/src/exporters/qa-env-secret-denylist.ts +1 -1
- package/src/exporters/report-path-sanitizer.ts +144 -0
- package/src/exporters/result-variants.ts +2 -1
- package/src/exporters/sensitive-key.ts +48 -0
- package/src/exporters/test-data-resolver.ts +12 -2
- package/src/exporters/xlsx-report-builder.ts +16 -2
- package/src/generators/gherkin-parser/index.ts +22 -11
- package/src/generators/gherkin-parser/parent-scope-clause.ts +130 -0
- package/src/generators/test-generator/adapters/adapter-interface.ts +25 -3
- package/src/generators/test-generator/adapters/appium/appium-adapter.ts +4 -4
- package/src/generators/test-generator/adapters/appium/os-support.ts +176 -0
- package/src/generators/test-generator/adapters/appium/templates/after-all.hbs +18 -0
- package/src/generators/test-generator/adapters/appium/templates/after-each.hbs +18 -0
- package/src/generators/test-generator/adapters/appium/templates/before-all.hbs +18 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/alert-fill-action.hbs +9 -2
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/capture-variable.hbs +24 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/click-at-position-action.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/click-element-with-text.hbs +1 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/double-click-action.hbs +7 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/frame-enter-action.hbs +3 -6
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/hover-action.hbs +6 -2
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/hover-element-with-text.hbs +4 -3
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/table-paginate-action.hbs +3 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/unknown-element-action.hbs +9 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/have-value-assertion.hbs +34 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/label-value-assertion.hbs +14 -6
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/visible-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/gestures/go-back-action.hbs +21 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/gestures/long-press-action.hbs +4 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/gestures/pinch-zoom-action.hbs +4 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/gestures/set-geolocation-action.hbs +16 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/partials/locator.hbs +9 -1
- package/src/generators/test-generator/adapters/appium/templates/test-file.hbs +62 -23
- package/src/generators/test-generator/adapters/index.ts +1 -1
- package/src/generators/test-generator/adapters/playwright/playwright-adapter.ts +4 -4
- package/src/generators/test-generator/adapters/playwright/templates/after-all.hbs +19 -1
- package/src/generators/test-generator/adapters/playwright/templates/after-each.hbs +18 -0
- package/src/generators/test-generator/adapters/playwright/templates/before-all.hbs +19 -1
- package/src/generators/test-generator/adapters/playwright/templates/imports.hbs +3 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/check-action.hbs +6 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/click-at-position-action.hbs +23 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/expand-action.hbs +8 -8
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/fill-editor-action.hbs +7 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row-nth.hbs +7 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/table-action-in-row.hbs +7 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/table-paginate-action.hbs +5 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/uncheck-action.hbs +3 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/unknown-element-action.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/wait-for-page.hbs +18 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/all-contain-assertion.hbs +32 -4
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/all-contain-element.hbs +5 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/checked-assertion.hbs +5 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/column-cell-assertion.hbs +13 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +2 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/disabled-with-variable-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/have-text-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +2 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/hidden-with-variable-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/not-checked-assertion.hbs +2 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/selected-assertion.hbs +21 -3
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/state-with-filter-assertion.hbs +20 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-empty.hbs +4 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-match-data.hbs +4 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-count.hbs +4 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-exists.hbs +12 -4
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-row-not-exists.hbs +14 -2
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +3 -3
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-value-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-variable-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/checked-opts.hbs +8 -0
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +8 -8
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-nth.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/id.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/testid.hbs +1 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +8 -8
- package/src/generators/test-generator/adapters/playwright/templates/test-file.hbs +10 -8
- package/src/generators/test-generator/code-generator.ts +181 -46
- package/src/generators/test-generator/diagnostics.ts +117 -6
- package/src/generators/test-generator/patterns/index.ts +175 -24
- package/src/generators/test-generator/patterns/types.ts +8 -0
- package/src/generators/test-generator/patterns/with-parent-scope.ts +41 -0
- package/src/generators/test-generator/step-mapper.ts +277 -43
- package/src/generators/test-generator/template-engine.ts +21 -21
- package/src/generators/test-generator/utils/data-resolver.ts +23 -2
- package/src/generators/test-generator/utils/dialog-scope-identity.ts +71 -0
- package/src/generators/test-generator/utils/escape-js-string.ts +24 -0
- package/src/generators/test-generator/utils/nearest-match.ts +55 -0
- package/src/generators/test-generator/utils/selector-entry-fields.ts +131 -0
- package/src/generators/test-generator/utils/selector-entry-walk.ts +58 -0
- package/src/generators/test-generator/utils/selector-nth-validation.ts +99 -0
- package/src/generators/test-generator/utils/selector-resolver.ts +333 -28
- package/src/generators/test-generator/utils/selector-type-validation.ts +87 -0
- package/src/harness/audit.ts +55 -30
- package/src/harness/capability-plan.ts +33 -1
- package/src/harness/catalog/drivers.yaml +1 -1
- package/src/harness/catalog/universal-viewpoints.yaml +145 -0
- package/src/harness/data-driven-lint.ts +192 -32
- package/src/harness/parse.ts +7 -1
- package/src/harness/repair.ts +8 -1
- package/src/harness/script-check.ts +186 -21
- package/src/harness/sensors.ts +75 -1
- package/src/harness/viewpoint-ledger.ts +26 -5
- package/src/index.ts +7 -0
- package/src/orchestrator/project-initializer.ts +17 -0
- package/src/orchestrator/report-sanitize-reporter-install.ts +109 -0
- package/src/orchestrator/templates/ai-src/commands/delivery.md +4 -3
- package/src/orchestrator/templates/ai-src/commands/run-test.md +9 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-api-coverage-model/SKILL.md +10 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +14 -5
- package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +62 -3
- package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +126 -11
- package/src/orchestrator/templates/ai-src/skills/sungen-mobile-gestures/SKILL.md +16 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-selector-fix/SKILL.md +12 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-selector-keys/SKILL.md +19 -5
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +88 -2
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +62 -5
- package/src/orchestrator/templates/ai-src/skills/sungen-test-design-techniques/SKILL.md +13 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-viewpoint/SKILL.md +59 -10
- package/src/orchestrator/templates/ai-src/skills/sungen-viewpoint/group-f-mobile-native.md +419 -0
- package/src/orchestrator/templates/playwright.config.ts +7 -0
- package/src/orchestrator/templates/readme.md +1 -1
- package/src/orchestrator/templates/specs-checked-state.ts +324 -0
- package/src/orchestrator/templates/specs-pw-shape-reporter.ts +0 -0
- package/src/orchestrator/templates/specs-report-path-sanitize-reporter.ts +142 -0
- package/src/orchestrator/templates/specs-url-assert.ts +101 -28
- package/src/orchestrator/templates/wdio.conf.ts +105 -1
- package/src/utils/selector-types.ts +54 -5
package/src/harness/sensors.ts
CHANGED
|
@@ -76,6 +76,9 @@ export interface CatalogTheme { theme: string; keywords: string[]; depth?: Theme
|
|
|
76
76
|
export interface Catalog {
|
|
77
77
|
page_types: Record<string, { detect_keywords: string[]; must_cover: CatalogTheme[] }>;
|
|
78
78
|
universal: CatalogTheme[];
|
|
79
|
+
/** Reminder themes offered to MOBILE targets only — `universal` also applies to web, where
|
|
80
|
+
* orientation and connectivity have no meaning. Optional so an older catalog still parses. */
|
|
81
|
+
mobile_universal?: CatalogTheme[];
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
export function loadCatalog(): Catalog {
|
|
@@ -181,7 +184,10 @@ export function viewpointGate(scenarios: ScenarioInfo[], viewpoints: ViewpointEn
|
|
|
181
184
|
}
|
|
182
185
|
}
|
|
183
186
|
|
|
184
|
-
|
|
187
|
+
// Mobile targets are also reminded of the native-only themes. Same weight as `universal`:
|
|
188
|
+
// these surface as narrative, not as score — see the note in the catalog.
|
|
189
|
+
const reminderThemes = [...catalog.universal, ...(isMobile ? catalog.mobile_universal ?? [] : [])];
|
|
190
|
+
const universalGaps = reminderThemes
|
|
185
191
|
.filter((t) => !t.keywords.some((k) => has(scenarios.map((s) => s.haystack), k)))
|
|
186
192
|
.map((t) => t.theme);
|
|
187
193
|
|
|
@@ -521,6 +527,74 @@ export function duplicateClusters(scenarios: ScenarioInfo[]): DuplicateResult {
|
|
|
521
527
|
};
|
|
522
528
|
}
|
|
523
529
|
|
|
530
|
+
// ---------- Sensor 4b: Reused scenario IDs ----------
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Two DIFFERENT scenarios wearing the same `VP-<CAT>-<NNN>` id (#694).
|
|
534
|
+
*
|
|
535
|
+
* Sensor 4 above is content-shaped — it clusters identical STEP SKELETONS — so two scenarios that
|
|
536
|
+
* merely share an id sail past it, and every other axis stays green. `VIEWPOINT-ID-REUSED` reads
|
|
537
|
+
* ids out of `test-viewpoint.md` only; the `.feature`'s own `Scenario:` lines were never compared
|
|
538
|
+
* against each other. The reported symptom was a ledger item showing UNCOVERED — pointing at the
|
|
539
|
+
* wrong cause, since the scenario exists and is just wearing the wrong number.
|
|
540
|
+
*
|
|
541
|
+
* This is BLOCKING (it joins the gate conjunction) where its three declaration-integrity siblings
|
|
542
|
+
* are advisory, because a reused id does not stop at the audit — it reaches the client:
|
|
543
|
+
*
|
|
544
|
+
* - `generateTcId()` builds the delivered testcase id straight out of the vpId
|
|
545
|
+
* (`${SCREEN}-${vpCore}`), so two scenarios under one id ship as two rows with ONE id in the
|
|
546
|
+
* CSV/XLSX the customer reviews.
|
|
547
|
+
* - It has already shipped a false green. `scenario-merger.ts` records the field report: a
|
|
548
|
+
* feature carried `VP-NAV-001` on 14 scenarios and the result-merge fallback attached an
|
|
549
|
+
* automated sibling's result to every @manual sibling — 7 testcases nobody ran were delivered
|
|
550
|
+
* as "Passed". The guard added there degrades to "Pending"; it never closed the cause.
|
|
551
|
+
* - `matrix/gates.ts` already grades the same condition `severity: 'error'` — but at delivery,
|
|
552
|
+
* after generation and review. This moves that judgment to where it can still be acted on
|
|
553
|
+
* cheaply: renumber and regenerate.
|
|
554
|
+
*
|
|
555
|
+
* Scoped PER FILE, matching the issue's wording. A unit may hold several `.feature`s
|
|
556
|
+
* (`add --feature`) and ids colliding ACROSS two of them still collapse into one `generateTcId`,
|
|
557
|
+
* because that id keys on the screen and not on the file — deliberately out of scope here.
|
|
558
|
+
*
|
|
559
|
+
* `@cases:<dataset>` is safe: a data-driven scenario is ONE `ScenarioInfo` that expands to N row
|
|
560
|
+
* tests at runtime, so it can never look like a collision to this count. `Scenario Outline` is not
|
|
561
|
+
* a vector either — this DSL does not support it (`@cases` is the data-driven form).
|
|
562
|
+
*
|
|
563
|
+
* One CONVENTION dependency, stated rather than hidden: `vpId` is whatever leading title token
|
|
564
|
+
* `isIdLike` accepts (uppercase-first, alphanumeric, carries a digit), not specifically `VP-`/`FL-`.
|
|
565
|
+
* Two scenarios both titled `iOS15 …` would therefore read as a collision. Every generator path
|
|
566
|
+
* and skill doc mandates the `VP-<CAT>-<NNN>` scheme, so this has no real surface today — but it is
|
|
567
|
+
* a convention holding it up, not the parser.
|
|
568
|
+
*/
|
|
569
|
+
export interface ReusedScenarioId {
|
|
570
|
+
id: string;
|
|
571
|
+
count: number;
|
|
572
|
+
titles: string[];
|
|
573
|
+
/** Basename of the `.feature` holding the collision; '' for hand-built scenarios with no source. */
|
|
574
|
+
file: string;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
export function scenarioIdReuse(scenarios: ScenarioInfo[]): ReusedScenarioId[] {
|
|
578
|
+
// Keyed by file THEN id: the check is per-file, and a bare id key would silently widen it to
|
|
579
|
+
// the whole unit. NUL-joined because a feature path may legitimately contain a space.
|
|
580
|
+
const byFileAndId = new Map<string, ScenarioInfo[]>();
|
|
581
|
+
for (const s of scenarios) {
|
|
582
|
+
if (!s.vpId) continue; // an id-less scenario is a different finding
|
|
583
|
+
const key = `${s.sourceFile ?? ''}\u0000${s.vpId.toUpperCase()}`;
|
|
584
|
+
byFileAndId.set(key, [...(byFileAndId.get(key) ?? []), s]);
|
|
585
|
+
}
|
|
586
|
+
// Read the id and the file off the GROUP, never by splitting the key back apart.
|
|
587
|
+
return [...byFileAndId.values()]
|
|
588
|
+
.filter((group) => group.length > 1)
|
|
589
|
+
.map((group) => ({
|
|
590
|
+
id: group[0].vpId!.toUpperCase(),
|
|
591
|
+
count: group.length,
|
|
592
|
+
titles: group.map((s) => s.name),
|
|
593
|
+
file: group[0].sourceFile ? path.basename(group[0].sourceFile) : '',
|
|
594
|
+
}))
|
|
595
|
+
.sort((a, b) => b.count - a.count || a.id.localeCompare(b.id));
|
|
596
|
+
}
|
|
597
|
+
|
|
524
598
|
// ---------- Sensor 5: Traceability ----------
|
|
525
599
|
|
|
526
600
|
export interface TraceResult {
|
|
@@ -222,7 +222,14 @@ export function viewpointLedger(viewpointPath: string, scenarios: ScenarioInfo[]
|
|
|
222
222
|
* The language has no reload/back step at all (#593), so the honest outcome is a `@manual`
|
|
223
223
|
* deferral naming the gesture — not a silent swap.
|
|
224
224
|
*/
|
|
225
|
-
|
|
225
|
+
/**
|
|
226
|
+
* `stepForm` is the step that ANSWERS the gesture, when one exists. The web three have none
|
|
227
|
+
* (#593), so their honest reply is still `@manual` naming the gesture. The mobile three all
|
|
228
|
+
* compile now — `go back` closed the last of them (#626) — and the finding must say so, or it
|
|
229
|
+
* keeps telling the author to defer work the compiler can already do. Data, not a sentence in
|
|
230
|
+
* the finding: when a reload/back step lands for web, filling this field is the whole change.
|
|
231
|
+
*/
|
|
232
|
+
const BROWSER_GESTURES: Array<{ gesture: string; re: RegExp; appSubstitute: RegExp; platform?: string; stepForm?: string }> = [
|
|
226
233
|
{ gesture: 'browser back', re: /\bbrowser(?:'s)?[ -]?(?:back|history)\b|\bhistory[ -]back\b/i,
|
|
227
234
|
appSubstitute: /\bclick \[[^\]]*back[^\]]*\]/i, platform: 'web' },
|
|
228
235
|
{ gesture: 'browser reload/refresh', re: /\b(?:page |browser )?(?:reload|refresh)(?:ing|ed|es)?\b/i,
|
|
@@ -236,21 +243,35 @@ const BROWSER_GESTURES: Array<{ gesture: string; re: RegExp; appSubstitute: RegE
|
|
|
236
243
|
// an in-app navigation never touches — so a defect that only appears on the OS path lives
|
|
237
244
|
// exactly in the substitution (#626). The check was web-only, so mobile flows were silent about
|
|
238
245
|
// the very risk it exists to catch.
|
|
239
|
-
{ gesture: 'the OS/hardware back gesture', platform: 'mobile',
|
|
246
|
+
{ gesture: 'the OS/hardware back gesture', platform: 'mobile', stepForm: 'When User go back',
|
|
240
247
|
re: /\b(?:hardware|system|os|device|android|physical)[ -]?back\b|\bback (?:gesture|button|swipe)\b|\bswipe[ -]back\b/i,
|
|
241
248
|
appSubstitute: /\b(?:tap|click) \[[^\]]*back[^\]]*\]/i },
|
|
242
249
|
{ gesture: 'backgrounding and resuming the app', platform: 'mobile',
|
|
250
|
+
stepForm: 'When User background the app for 5 seconds',
|
|
243
251
|
re: /\b(?:background(?:ing|ed)?|foreground(?:ing|ed)?|app switcher|recent apps|minimi[sz]|resum(?:e|ing)|kill(?:ed|ing)? the app|force[ -]stop|process death)\b/i,
|
|
244
252
|
appSubstitute: /\b(?:tap|click) \[[^\]]+\]|is on \[[^\]]+\] screen\b/i },
|
|
245
253
|
{ gesture: 'rotating the device', platform: 'mobile',
|
|
254
|
+
stepForm: 'When User rotate the device to landscape',
|
|
246
255
|
re: /\b(?:rotat(?:e|ing|ion)|landscape|portrait|orientation change)\b/i,
|
|
247
256
|
appSubstitute: /\bis on \[[^\]]+\] screen\b/i },
|
|
248
257
|
];
|
|
249
258
|
|
|
259
|
+
/**
|
|
260
|
+
* The remedy sentence for one substitution. Lives beside the gesture table rather than in the
|
|
261
|
+
* audit finding: the table is what knows whether a step exists, and a ternary buried in
|
|
262
|
+
* `audit.ts` would be one branch nothing tests. Both branches are pinned in
|
|
263
|
+
* `tests/codegen/os-back-step.run.ts`.
|
|
264
|
+
*/
|
|
265
|
+
export function substitutionRemedy(sub: { stepForm?: string }): string {
|
|
266
|
+
return sub.stepForm
|
|
267
|
+
? `That gesture DOES have a step: write \`${sub.stepForm}\` instead of the in-app control.`
|
|
268
|
+
: 'The step vocabulary has no reload/back step (#593), so defer it with `@manual` naming the gesture rather than substituting silently.';
|
|
269
|
+
}
|
|
270
|
+
|
|
250
271
|
export function browserGestureSubstitutions(
|
|
251
272
|
viewpointPath: string, scenarios: ScenarioInfo[], platform?: string,
|
|
252
|
-
): Array<{ gesture: string; item: string }> {
|
|
253
|
-
const out: Array<{ gesture: string; item: string }> = [];
|
|
273
|
+
): Array<{ gesture: string; item: string; stepForm?: string }> {
|
|
274
|
+
const out: Array<{ gesture: string; item: string; stepForm?: string }> = [];
|
|
254
275
|
// A gesture only applies where its platform does: "reload" means nothing on a native app, and
|
|
255
276
|
// "backgrounding" means nothing in a browser tab. An unknown platform checks everything, which
|
|
256
277
|
// is how the web-only set behaved before.
|
|
@@ -266,7 +287,7 @@ export function browserGestureSubstitutions(
|
|
|
266
287
|
if (!g.re.test(item.text)) continue;
|
|
267
288
|
// A @manual scenario whose own subject IS the gesture is the honest answer.
|
|
268
289
|
if (deferred.some((h) => g.re.test(h))) break;
|
|
269
|
-
if (automated.some((h) => g.appSubstitute.test(h))) out.push({ gesture: g.gesture, item: item.text });
|
|
290
|
+
if (automated.some((h) => g.appSubstitute.test(h))) out.push({ gesture: g.gesture, item: item.text, stepForm: g.stepForm });
|
|
270
291
|
break;
|
|
271
292
|
}
|
|
272
293
|
}
|
package/src/index.ts
CHANGED
|
@@ -18,10 +18,17 @@ export { expandRecipe } from './generators/test-generator/recipe-expander';
|
|
|
18
18
|
export type { RecipeExpandContext, RecipeNode } from './generators/test-generator/recipe-expander';
|
|
19
19
|
export type { ResolvedAppProfile } from './generators/test-generator/app-profile';
|
|
20
20
|
export type { MappedStep } from './generators/test-generator/step-mapper';
|
|
21
|
+
// The shared "nothing honest to compile" step body. Exported for the driver SPI: a pattern that
|
|
22
|
+
// must refuse (SG-W017 and siblings) emits THIS rather than shipping a one-sided refusal template,
|
|
23
|
+
// which would fail the adapter-parity gate and push playwright off its refusal-only floor of 0.
|
|
24
|
+
export { unsupportedStepCode } from './generators/test-generator/patterns/unsupported-step-code';
|
|
21
25
|
export type { ParsedStep } from './generators/gherkin-parser';
|
|
22
26
|
export { getPathCode, inferPath, resolvePathVariables } from './generators/test-generator/utils/path-inference';
|
|
23
27
|
export { formatToRegexSource, formatToRegexLiteral, hasFormatToken } from './generators/test-generator/utils/format-pattern';
|
|
24
28
|
export { LIST_MEMBER_ROLES } from './generators/test-generator/utils/selector-resolver';
|
|
29
|
+
// The compiler's selector-key rule. A driver needs it to look up anything keyed the way a
|
|
30
|
+
// `[Reference]` is keyed — e.g. a table entry's `columns:` map (issue #674).
|
|
31
|
+
export { normalizeRefLabel } from './utils/ref-key';
|
|
25
32
|
|
|
26
33
|
// --- Precondition-annotation override grammar (shared by the @query / @api driver codegen) ---
|
|
27
34
|
export { parseQueryOverrides } from './harness/annotation-overrides';
|
|
@@ -155,6 +155,7 @@ export class ProjectInitializer {
|
|
|
155
155
|
if (!this.mobileOnly) {
|
|
156
156
|
this.createPlaywrightConfig();
|
|
157
157
|
this.createSpecsBase();
|
|
158
|
+
this.createSpecsReportSanitizeReporter();
|
|
158
159
|
}
|
|
159
160
|
|
|
160
161
|
// Create tsconfig.json if doesn't exist (needed by both web + mobile)
|
|
@@ -594,6 +595,22 @@ export class ProjectInitializer {
|
|
|
594
595
|
this.createdItems.push('specs/generated/base.ts');
|
|
595
596
|
}
|
|
596
597
|
|
|
598
|
+
/**
|
|
599
|
+
* Create specs/generated/report-path-sanitize-reporter.ts — the Playwright reporter the config
|
|
600
|
+
* registers. Written at init (the config would otherwise reference a file only `generate` writes,
|
|
601
|
+
* and Playwright refuses to load a config whose reporter module is missing).
|
|
602
|
+
*/
|
|
603
|
+
private createSpecsReportSanitizeReporter(): void {
|
|
604
|
+
const reporterPath = path.join(this.cwd, 'specs', 'generated', 'report-path-sanitize-reporter.ts');
|
|
605
|
+
if (fs.existsSync(reporterPath)) {
|
|
606
|
+
this.skippedItems.push('specs/generated/report-path-sanitize-reporter.ts');
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
fs.mkdirSync(path.dirname(reporterPath), { recursive: true });
|
|
610
|
+
fs.writeFileSync(reporterPath, this.readTemplate('specs-report-path-sanitize-reporter.ts'), 'utf-8');
|
|
611
|
+
this.createdItems.push('specs/generated/report-path-sanitize-reporter.ts');
|
|
612
|
+
}
|
|
613
|
+
|
|
597
614
|
/**
|
|
598
615
|
* Create specs/test-data.ts for runtime YAML loading
|
|
599
616
|
*/
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install the report-path-sanitize reporter into a project — the file under `specs/generated/` and
|
|
3
|
+
* its entry in `playwright.config.ts`.
|
|
4
|
+
*
|
|
5
|
+
* A NEW project gets both from the templates at `sungen init`. An EXISTING project has a
|
|
6
|
+
* `playwright.config.ts` that sungen wrote once and never touches again, so the reporter would never
|
|
7
|
+
* reach it: `sungen update` calls this to add the reporter file (the same refresh `sungen generate`
|
|
8
|
+
* applies to `specs/generated/base.ts`) and to insert the reporter line after the JSON reporter,
|
|
9
|
+
* once. A config that has been customised beyond recognition (no `['json'` reporter line) is left
|
|
10
|
+
* alone with a printed instruction — a wrong edit to a user's config is worse than a missing reporter,
|
|
11
|
+
* and `sungen results sanitize` (run by /sungen:run-test) covers the gap.
|
|
12
|
+
*/
|
|
13
|
+
import * as fs from 'fs';
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
|
|
16
|
+
export const REPORTER_FILE = path.join('specs', 'generated', 'report-path-sanitize-reporter.ts');
|
|
17
|
+
export const REPORTER_CONFIG_ENTRY = "['./specs/generated/report-path-sanitize-reporter.ts']";
|
|
18
|
+
const TEMPLATE = 'specs-report-path-sanitize-reporter.ts';
|
|
19
|
+
|
|
20
|
+
export type ConfigPatchOutcome = 'patched' | 'present' | 'absent' | 'manual';
|
|
21
|
+
|
|
22
|
+
/** Write (or refresh) the reporter file from the bundled template. Returns true when the file changed. */
|
|
23
|
+
export function ensureReporterFile(cwd: string, dryRun: boolean = false): boolean {
|
|
24
|
+
const templatePath = path.join(__dirname, 'templates', TEMPLATE);
|
|
25
|
+
if (!fs.existsSync(templatePath)) return false;
|
|
26
|
+
const next = fs.readFileSync(templatePath, 'utf8');
|
|
27
|
+
const target = path.join(cwd, REPORTER_FILE);
|
|
28
|
+
if (fs.existsSync(target) && fs.readFileSync(target, 'utf8') === next) return false;
|
|
29
|
+
if (!dryRun) {
|
|
30
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
31
|
+
fs.writeFileSync(target, next);
|
|
32
|
+
}
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Add the reporter entry to `playwright.config.ts` right after the JSON reporter line.
|
|
38
|
+
* 'patched' — inserted now
|
|
39
|
+
* 'present' — already there (idempotent)
|
|
40
|
+
* 'absent' — no playwright.config.ts (a mobile-only project, or not a sungen project)
|
|
41
|
+
* 'manual' — the config has no recognisable `['json', …]` reporter line; nothing written
|
|
42
|
+
*/
|
|
43
|
+
export function patchPlaywrightConfig(cwd: string, dryRun: boolean = false): ConfigPatchOutcome {
|
|
44
|
+
const configPath = path.join(cwd, 'playwright.config.ts');
|
|
45
|
+
if (!fs.existsSync(configPath)) return 'absent';
|
|
46
|
+
const src = fs.readFileSync(configPath, 'utf8');
|
|
47
|
+
if (src.includes('report-path-sanitize-reporter')) return 'present';
|
|
48
|
+
const patched = insertAfterJsonReporter(src);
|
|
49
|
+
if (patched === null) return 'manual';
|
|
50
|
+
if (!dryRun) fs.writeFileSync(configPath, patched);
|
|
51
|
+
return 'patched';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** `sungen update` step: install file + config entry, report what happened. */
|
|
55
|
+
export function installReportSanitizeReporter(cwd: string, dryRun: boolean = false): void {
|
|
56
|
+
if (!fs.existsSync(path.join(cwd, 'playwright.config.ts'))) return; // nothing to run Playwright with — nothing to sanitize
|
|
57
|
+
// The file FIRST: a config that names a reporter module Playwright cannot find refuses to load at all.
|
|
58
|
+
const wroteFile = ensureReporterFile(cwd, dryRun);
|
|
59
|
+
if (wroteFile) console.log(`${dryRun ? '📋 would write' : '✓ wrote'} ${REPORTER_FILE}`);
|
|
60
|
+
if (!dryRun && !fs.existsSync(path.join(cwd, REPORTER_FILE))) return; // bundled template missing — leave the config alone
|
|
61
|
+
const outcome = patchPlaywrightConfig(cwd, dryRun);
|
|
62
|
+
if (outcome === 'patched') console.log(`${dryRun ? '📋 would add' : '✓ added'} the report-path-sanitize reporter to playwright.config.ts`);
|
|
63
|
+
if (outcome === 'manual') {
|
|
64
|
+
console.log(`⚠️ playwright.config.ts has no recognisable ['json', …] reporter line — add ${REPORTER_CONFIG_ENTRY} to its reporter array by hand (until then, /sungen:run-test's \`sungen results sanitize\` step keeps result files free of machine paths).`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Insert the reporter entry right after the `['json', …]` entry, whichever shape the config uses:
|
|
70
|
+
* - one entry per line (the current template): a new line with the same indentation
|
|
71
|
+
* - the whole array on one line (older templates: `reporter: [['list'], ['json', {…}]],`): inline
|
|
72
|
+
* The entry's end is found by bracket depth, so an `outputFile` expression may carry any braces or
|
|
73
|
+
* quotes. `null` when there is no `['json'` entry at all.
|
|
74
|
+
*/
|
|
75
|
+
export function insertAfterJsonReporter(src: string): string | null {
|
|
76
|
+
const eol = src.includes('\r\n') ? '\r\n' : '\n';
|
|
77
|
+
// the first `['json'` that is code, not a commented-out line
|
|
78
|
+
let start = -1;
|
|
79
|
+
for (const m of src.matchAll(/\[\s*['"]json['"]/g)) {
|
|
80
|
+
const lineStart = src.lastIndexOf('\n', m.index!) + 1;
|
|
81
|
+
if (/^\s*(\/\/|\*)/.test(src.slice(lineStart, m.index!))) continue;
|
|
82
|
+
start = m.index!; break;
|
|
83
|
+
}
|
|
84
|
+
if (start < 0) return null;
|
|
85
|
+
let depth = 0;
|
|
86
|
+
let end = -1;
|
|
87
|
+
for (let i = start; i < src.length; i++) {
|
|
88
|
+
const ch = src[i];
|
|
89
|
+
if (ch === '[' || ch === '{' || ch === '(') depth++;
|
|
90
|
+
else if (ch === ']' || ch === '}' || ch === ')') {
|
|
91
|
+
depth--;
|
|
92
|
+
if (depth === 0) { end = i; break; }
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (end < 0) return null;
|
|
96
|
+
const after = end + 1;
|
|
97
|
+
const restOfLine = src.slice(after, src.indexOf('\n', after) < 0 ? src.length : src.indexOf('\n', after));
|
|
98
|
+
if (/^\s*,?\s*$/.test(restOfLine)) {
|
|
99
|
+
// the entry ends its line → add a line below it, same indentation; exactly one comma after the
|
|
100
|
+
// json entry whether or not the source had one; the file's own line ending
|
|
101
|
+
const lineStart = src.lastIndexOf('\n', start) + 1;
|
|
102
|
+
const indent = /^[ \t]*/.exec(src.slice(lineStart))![0];
|
|
103
|
+
const lineEnd = src.indexOf('\n', after);
|
|
104
|
+
const rest = lineEnd < 0 ? '' : src.slice(lineEnd + 1);
|
|
105
|
+
return `${src.slice(0, after)},${eol}${indent}${REPORTER_CONFIG_ENTRY},${lineEnd < 0 ? '' : eol}${rest}`;
|
|
106
|
+
}
|
|
107
|
+
// inline array: `…, ['json', {…}]], use: …` → `…, ['json', {…}], [reporter]], use: …`
|
|
108
|
+
return `${src.slice(0, after)}, ${REPORTER_CONFIG_ENTRY}${src.slice(after)}`;
|
|
109
|
+
}
|
|
@@ -201,8 +201,9 @@ re-asking.
|
|
|
201
201
|
Forward the CLI summary verbatim (Items = review rows; **Variants = execution units — progress is
|
|
202
202
|
counted in variants, never items**). Then `AskUserQuestion`:
|
|
203
203
|
|
|
204
|
-
- **Open the workbook** — inspect `qa/deliverables/<unit>-testcases.xlsx` (
|
|
205
|
-
collapse outline level 1 for the customer view; Coverage
|
|
204
|
+
- **Open the workbook** — inspect `qa/deliverables/<unit>-testcases.xlsx` (Overview: live roll-up;
|
|
205
|
+
Auto / Manual: collapse outline level 1 for the customer view; Coverage: target × category
|
|
206
|
+
grid + gaps).
|
|
206
207
|
- **Run tests to refresh results** — `/sungen:run-test <unit>`, then re-run delivery.
|
|
207
208
|
- **Also export CSV** — `sungen delivery <unit> --format csv` (flat `item`/`variant` rows for pipelines).
|
|
208
209
|
- **Export the legacy workbook too** — `sungen delivery <unit> --legacy`.
|
|
@@ -231,6 +232,6 @@ sungen delivery [names...] # matrix (default; needs the map) → XLSX onl
|
|
|
231
232
|
--skip-preflight | --continue-on-missing | --env <env> # as before
|
|
232
233
|
```
|
|
233
234
|
|
|
234
|
-
Outputs: `qa/deliverables/<unit>-testcases.xlsx` (
|
|
235
|
+
Outputs: `qa/deliverables/<unit>-testcases.xlsx` (Overview + Auto + Manual + Coverage sheets) by default;
|
|
235
236
|
`--format csv` writes `<unit>-testcases.csv` instead (flat, `Level` column `item|variant`),
|
|
236
237
|
`--format both` writes both. Legacy mode writes the classic files instead.
|
|
@@ -68,7 +68,7 @@ When `platform: mobile`:
|
|
|
68
68
|
SUNGEN_SPECS='./specs/generated/<name>/<name>.spec.ts' MOBILE_PLATFORM=both npm run test:mobile
|
|
69
69
|
```
|
|
70
70
|
`wdio.conf.ts` auto-selects `@wdio/globals` specs and auto-starts Appium. iOS needs a booted Simulator and `IOS_BUNDLE_ID`/`IOS_UDID` env vars.
|
|
71
|
-
- **Results**: the wdio reporter writes `<name>-test-result.<android|ios>[.<env>].json` — one file per capability, so a both-OS run keeps both outcomes.
|
|
71
|
+
- **Results**: the wdio reporter writes `<name>-test-result.<android|ios>[.<env>].json` — one file per capability, so a both-OS run keeps both outcomes. Then run `npx sungen results sanitize` — wdio error stacks quote absolute machine paths, and the result file is committed next to the spec.
|
|
72
72
|
- **Skip** the "Playwright command guidelines" section below — it applies to web only.
|
|
73
73
|
- The phased waves (smoke → priority → full → regression), attempt budgets, and next-steps mirror the web flow — follow the `sungen-selector-fix-mobile` skill for the mobile equivalents.
|
|
74
74
|
|
|
@@ -305,6 +305,14 @@ PLAYWRIGHT_JSON_OUTPUT_NAME=specs/generated/flows/<name>/<name>-test-result.json
|
|
|
305
305
|
|
|
306
306
|
**DO NOT** pass `--reporter=...` flag — it overrides the reporters from `playwright.config.ts` and disables the JSON reporter that `sungen delivery` depends on.
|
|
307
307
|
|
|
308
|
+
**Sanitize the result file paths right after the run** — the JSON report records the operator's absolute machine paths (`/Users/<name>/…`, `C:\Users\…`) in `config`, error locations, attachments and stack traces, and the file is committed next to the spec:
|
|
309
|
+
|
|
310
|
+
```bash
|
|
311
|
+
npx sungen results sanitize # every *-test-result*.json under specs/generated + .sungen/reports/*.json → project-relative paths
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
Projects created with a recent sungen (or refreshed by `sungen update`) run the same rewrite automatically through the `report-path-sanitize-reporter` in `playwright.config.ts`; this command is the safety net for every other project and is idempotent — an already-clean file is left untouched.
|
|
315
|
+
|
|
308
316
|
**Re-running a SUBSET (fix-and-retry, or a partially edited spec) when the per-target result file already exists:** the JSON reporter would OVERWRITE it and lose every result you didn't re-run. Write the subset's report to a temp file and MERGE it instead — same title wins, everything else is kept:
|
|
309
317
|
|
|
310
318
|
```bash
|
|
@@ -67,6 +67,16 @@ Never emit a status-only assertion for a success case. Three tiers — see also
|
|
|
67
67
|
|
|
68
68
|
Anti-pattern to avoid: re-asserting the value you just sent (`expect {{x.body.username}} is {{username}}` on the thing you created proves little) — assert a **server-derived** field (generated id, timestamp, computed status) or read it back independently.
|
|
69
69
|
|
|
70
|
+
## Alias entries (binding one endpoint twice)
|
|
71
|
+
|
|
72
|
+
To bind two responses of the SAME endpoint in one scenario (a control call next to the call under
|
|
73
|
+
test), clone the catalog entry under a second name (`create_order_control`) — identical to the
|
|
74
|
+
original except `name`/`description`. The audit gate counts identical entries as ONE endpoint: its
|
|
75
|
+
must-cover viewpoints (`contract`, `error`) are satisfied when ANY alias asserts them, so the clone
|
|
76
|
+
needs no error scenario of its own. An entry on the same route that differs in any request or
|
|
77
|
+
response detail (`encoding`, `body`, `headers`, `expect`, `fields`, schema) is a distinct endpoint
|
|
78
|
+
and keeps its own viewpoints. An omitted `datasource` resolves to the project default before comparing.
|
|
79
|
+
|
|
70
80
|
## Output discipline
|
|
71
81
|
- Enumerate the **full Tier-1 list per endpoint first**, then write incrementally (≈10–15 scenarios per Write call) — never the whole suite in one response (output-token cap).
|
|
72
82
|
- Name scenarios `VP-API-NNN <what it proves>`; keep the endpoint + technique legible.
|
|
@@ -63,7 +63,16 @@ instead; `--format both` writes both.
|
|
|
63
63
|
every unit it did not export with the reason, and exits 0 as long as it wrote something — a
|
|
64
64
|
scaffolded unit never blocks the ready ones.
|
|
65
65
|
|
|
66
|
-
**Workbook
|
|
66
|
+
**Workbook** (matrix mode): four sheets — `Overview` (live roll-up over the two below), `Auto`,
|
|
67
|
+
`Manual`, `Coverage`. Auto and Manual split **per variant**, so a **mixed item's parent row
|
|
68
|
+
appears on both**, each side carrying only its own variants and rolling up only what is on that
|
|
69
|
+
sheet — expect the same ID twice in the workbook, with two different numbers. Both detail sheets
|
|
70
|
+
always exist even when one is empty. Layout is BM-2-901-13: band on rows 6/7/8, `*: Mandatory`
|
|
71
|
+
legend on row 12, the 16 headers `A..P` on row 13, data from row 14, frozen at `A14`.
|
|
72
|
+
`Testcase type` (`K`) is variant-level (`Auto` · `Auto · Mocked` · `Manual (M6)`) and blank on a
|
|
73
|
+
parent row. Full detail: `docs/spec/delivery-coverage-matrix-spec.md` §8.
|
|
74
|
+
|
|
75
|
+
`Auto` / `Manual` — parent rows + outline-level-1 variant sub-rows for **every**
|
|
67
76
|
item (single-variant included: the sub-row carries the source VP-id, resolved data, and the
|
|
68
77
|
result/evidence entry). Sub-rows are **delta-only**: knowledge common to the whole item
|
|
69
78
|
(precondition, trigger) is written ONCE on the parent; a child repeats only what distinguishes it
|
|
@@ -80,9 +89,9 @@ never a concatenation of variant titles.
|
|
|
80
89
|
Variant Result cells hold exactly one of `Passed`/`Failed`/`Blocked`/`Pending`/`N/A` (dropdown) —
|
|
81
90
|
that vocabulary is the contract: the parent Result and the summary band are **live Excel formulas**
|
|
82
91
|
counting those words, so a parent label is always a composed `n/m Passed …` string (`All N/A` when
|
|
83
|
-
every variant is N/A) and never a bare state word. **`N/A` leaves the denominator.**
|
|
84
|
-
|
|
85
|
-
header rows.
|
|
92
|
+
every variant is N/A) and never a bare state word. **`N/A` leaves the denominator.** Note, Defect ID
|
|
93
|
+
and actual result share one column `P`; the header row is frozen; ISO dates; landscape print with
|
|
94
|
+
repeated header rows.
|
|
86
95
|
|
|
87
96
|
`Coverage` sheet — requirement coverage table (every FR/TR/NFR id with an explicit status),
|
|
88
97
|
target × category grid with explicit `—` gaps, dispositions, manifest. CSV mirrors the same model
|
|
@@ -201,7 +210,7 @@ The base sheet label comes from `base_locale:` (default **EN**). Undeclared env
|
|
|
201
210
|
|
|
202
211
|
## API delivery — extra worksheet
|
|
203
212
|
|
|
204
|
-
For **api-kind units** (`qa/api/<area>/`), the `.xlsx` gains a final worksheet **`API detail`** (appended after Overview/Auto/Manual). The main BM-2-901-13
|
|
213
|
+
For **api-kind units** (`qa/api/<area>/`), the `.xlsx` gains a final worksheet **`API detail`** (appended after Overview/Auto/Manual). The main BM-2-901-13 testcase-sheet layout is unchanged. The CSV is unchanged (16-column, no extra sheet).
|
|
205
214
|
|
|
206
215
|
### Required sources (API detail sheet only)
|
|
207
216
|
|
|
@@ -82,6 +82,7 @@ needs any of these, it is a **finding for QA** — surface it in the run summary
|
|
|
82
82
|
| Element is not an input | Wrong element type targeted | Change `type` or `value` to match actual element |
|
|
83
83
|
| not a select | Custom dropdown, not native `<select>` | Set the widget variant: `variant: radix-select`/`antd-select` (built-in recipes), a custom recipe name from `qa/app.yaml`, or project-wide `widgets.select` in `qa/app.yaml` |
|
|
84
84
|
| Frame not found | iframe selector wrong or doesn't exist | Fix `frame` value, verify iframe in snapshot |
|
|
85
|
+
| Timeout / not found on a step AFTER a dialog closed, and the element is plainly there in the snapshot | **Stale dialog scope** — the scenario entered dialog scope (`Given User is on [X] dialog` or `User use dialog`) and never left. Scope is sticky: it survives the click that closed the dialog, and `wait for [X] dialog is hidden` does NOT end it. Every following locator is still prefixed with the dead dialog root | **Not a selectors.yaml fix — the locator is correct.** The `.feature` needs a `close`/`dismiss`/`exit dialog` step after the dialog closes, and the `.feature` is FROZEN during run-test → **report it as a Gherkin gap for QA** and let the test fail |
|
|
85
86
|
|
|
86
87
|
### Compile diagnostics (`sungen generate` output) → fix BEFORE running
|
|
87
88
|
|
|
@@ -90,6 +91,7 @@ needs any of these, it is a **finding for QA** — surface it in the run summary
|
|
|
90
91
|
| `SG-W001` | Step matched no pattern — sungen has no step form for the sentence. Compiles to `throw new Error("[sungen] …")` naming the step (it emitted a no-op `// TODO` before the unsupported-step gate, which let the scenario PASS asserting nothing) | Rephrase to a documented pattern (`sungen-gherkin-syntax`) or tag `@manual` with a reason. There is no silent fallback — the test goes red until one of those two is done |
|
|
91
92
|
| `SG-W002` | A step's `{{value}}` was parsed but never used in the generated code | The step form isn't the one you think — check the pattern's exact syntax |
|
|
92
93
|
| `SG-W003` | `{ name }` filter defaulted from the Gherkin reference on a name-from-content role (toast/alert/status) | Add explicit `name:` or opt out with `name: null` |
|
|
94
|
+
| `SG-W004` | A selectors.yaml entry declares a **field no consumer reads**, so writing it changes nothing in the generated test. Reserved for this in the original diagnostics spec (`exact: true` on an entry with no construct it applies to) and implemented for the wider class by #712, where `pagination:` and `contenteditable:` were public, documented and plumbed — yet read by nothing, discoverable only by grepping. The field partition lives in `utils/selector-entry-fields.ts`, each entry naming WHO reads it. A warning, not an error: an unknown key is inert, so refusing to compile would break a project over a stray comment-turned-key while teaching nothing | Delete the field, or fix its spelling — the message names the nearest valid key. If the field genuinely should do something, that is a compiler gap: report it rather than leaving the declaration in place believing it works |
|
|
93
95
|
| `SG-W005` | `qa/app.yaml` unknown key / unknown preset / unparseable | Fix the key, or `extends: native\|radix\|antd` |
|
|
94
96
|
| `SG-W006` | `qa/app.<env>.yaml` found — unsupported by design | Describe the app once in `qa/app.yaml`; env differences belong in test-data overlays |
|
|
95
97
|
| `SG-W007` | Browser-alert handler step written AFTER an action — listener registers too late, the dialog auto-dismisses silently | Use the compound form `click [X] button and accept [OK] alert` (preferred), or move the alert step before the trigger |
|
|
@@ -98,21 +100,32 @@ needs any of these, it is a **finding for QA** — surface it in the run summary
|
|
|
98
100
|
| `SG-W010` | A row step's `[Ref]` looks POSITIONAL (`First…`/`Last…`/`Top…`/`nth`) but has no selectors.yaml entry — the label is display-only, so the step checks existence ANYWHERE in the table; an ordering/sort-stability rule would pass even when the row moved | Declare the row in selectors.yaml (a `type: locator` value RELATIVE to the table, e.g. `first contact row:` → `value: 'tbody tr:first-child'`) — the step then asserts `toContainText` on that exact row. This is a selectors.yaml-only fix — allowed inside the run-test auto-fix loop |
|
|
99
101
|
| `SG-W011` | A storage assertion written as `expect … in local/session storage` — `expect` steps only read `{{response}}` refs, so this matches no pattern. Compiles to a `throw` (was a silent no-op) | Rewrite using the supported grammar: `User see [KEY] in local storage exists/…` |
|
|
100
102
|
| `SG-W012` | A mock-install step written AFTER a navigation step in the same block — `page.route()` registered after `goto()` misses every request fired during page load | Move the mock-install step before the navigation, or into `Background` |
|
|
101
|
-
| `SG-W013` | A page
|
|
103
|
+
| `SG-W013` | A page step (`see [X] page` / `is on [X] page` / `wait for [X] page`) whose `[Ref]` has no `type: page` selector entry (or collides with a non-page entry) — the step falls back to the feature's own path (or `/<ref>/`) instead of `X`'s real URL, so the anchored match can never reach it: the assertion always fails, the wait can only time out | Declare a `type: page` entry for `[Ref]` with its real URL; if the key collides with another type, disambiguate with a `--type` suffix (`sungen-selector-keys` § Collision rule) |
|
|
102
104
|
| `SG-W014` | `[X] page with {{v}}` where `{{v}}`'s base test-data value carries no query and no fragment — the step checks the PATH only, asserting less than it reads as | Informational — pass a value like `?q=…` if you meant to assert a query, or drop `with {{v}}` for a bare page |
|
|
103
105
|
| `SG-W015` | `see [X] card/item with {{n}}` where `{{n}}`'s test-data value is a **bare number** — this compiles to a TEXT assertion (`toHaveText('3')`), NOT a count. An author reaching for "3 cards are shown" gets an assertion that can only pass if the card's TEXT is literally the digit: a wrong test with no error. Scoped to container-ish element types (`card`/`item`/`option` + plurals); a text element asserting a numeric readout (`[Row Quantity] text with 4`) stays silent | For a COUNT use a form that has count semantics: `see [Ref] row with {{n}}` / `see [T] table with {{n}}`, or a list `should have count`. If the number really is the element's text, assert it on a **text/label** element instead |
|
|
106
|
+
| `SG-W016` | A table step on a table located by CSS/testid whose value does not mention `table` and that declares no `rows:` — the table templates find rows by `getByRole('row')`; on a list built from `<div>`s that is 0 rows, so `is hidden`/`is empty` PASS without checking anything and counts/clicks time out (issue #688). A heuristic — the compiler cannot see the DOM — so it warns once per table | Declare the row shape on the TABLE entry (`rows: 'div.job-row'`, relative to the table) — it drives all seven table steps — or locate the table by `role: table` if it really is one. Do not put the shape on a row label's entry: that key positions ONE row |
|
|
107
|
+
| `SG-W017` | A step needs a **selector-entry field the entry does not declare**, so there is nothing honest to compile — today, a table paginate step whose table entry declares no `pagination.next`/`.prev` (#712). The mirror of `SG-W004`: that one reports a field written that nothing reads, this one a field a consumer needs that nobody wrote. The step compiles to a loud runtime `throw` rather than guessing a locator, which is the #690 failure mode (a locator no page has, failing far from its cause) | Add the missing field to the entry in selectors.yaml — for pagination: `pagination:` with `next:`/`prev:` locators. This is a selectors.yaml-only fix, allowed inside the run-test auto-fix loop |
|
|
108
|
+
| `SG-W018` | A selectors.yaml entry declares an **`nth:` outside the contract** — `nth: -2`, `nth: 'last'`, `nth: 1.5`. `nth:` was the one entry field whose VALUE nothing checked (issue #720), so the web adapter silently DROPPED the filter (identical to deleting the line — the step then hits `strict mode violation: resolved to N elements`, which names the DOM and never the YAML line) while the mobile adapter emitted `$$('~x')[-1]` / `[last]` — `undefined` indexing and a bare identifier. The value is now ignored and reported once per entry | Write an integer `>= 0` for a zero-based index, or `-1` for the last match (`.last()` on web, `.at(-1)` on mobile). There is no keyword form: `'last'` is a string, not an index. This is a selectors.yaml-only fix — allowed inside the run-test auto-fix loop |
|
|
104
109
|
| `SG-E020` | A step matched a pattern, but the **active adapter ships no template** for it (e.g. a web-only step compiled under `platform: mobile`). The feature file still generates — that one step compiles to `throw new Error("[sungen] …")` naming the step, feature, pattern, template and adapter, so the failure is loud and traceable rather than a crashed build | Rephrase to a step the target adapter actually ships (see `sungen-gherkin-syntax` Platform Support section / `sungen-mobile-gestures`), or tag the scenario `@manual` with the platform reason |
|
|
105
110
|
| `SG-E021` | The step carries a trailing **state clause** (`… is hidden`) that the pattern which matched it does not assert — the clause would be SILENTLY DROPPED. Worst shape in this table: it does not weaken an assertion, it can INVERT one. `see [X] page is hidden` matched `page-assertion` (priority 13) ahead of `is-hidden` (11), so the step compiled to `toHaveURL(...)` — asserting the page IS displayed, and passing. Also fires on `wait for [X] is checked` (a wait honours only `hidden`/`visible`/`disabled`/`enabled`; everything else silently fell back to `visible`) and on `row`/`column in [T] table … is <state>` for any state but `hidden` | Move the state onto the element that has it: `Then User see [X] checkbox is checked`. For a page, assert the page you expect (`Then User see [Other] page`) or a marker element (`Then User see [Banner] is hidden`). If sungen cannot express the check, tag the scenario `@manual`. The diagnostic names the pattern that swallowed the clause — that name tells you which form you actually hit |
|
|
106
|
-
| `SG-W020` | The matched pattern **declares `platforms`**
|
|
111
|
+
| `SG-W020` | The matched pattern **declares `platforms`** and the active platform isn't among them. Caught before template lookup, so the diagnostic names the alternative for the platform you are on. This is now the MAIN off-platform channel, not a corner case: the whole web-only set (`table-*`, `storage-*`, `route-assertion`, `tab-order`, `sorted`, `upload`, `alert-fill`) and the whole mobile-only set (gestures, device state, app lifecycle, device oracles) declare it, so the refusal arrives at compile time with a concrete substitute instead of at run time from inside a template. It still compiles to a `throw` — moving the message earlier never turns a refusal into a silent pass | Do what the message says: it carries the pattern's OWN `platformAlternative`, e.g. a table assertion on mobile → per-row accessibility-id assertions; `local storage` on mobile → assert the state via a UI element or `@api`/`@query`; a swipe on web → drive the control the swipe reveals. `@mock` on a mobile unit is the same shape (there is no Mock Driver on Appium) |
|
|
107
112
|
| `SG-W021` | `use dialog` / `User is on [X] dialog` scope under the **mobile** adapter — the scope is recorded but no Appium template reads `inDialog`, so every following step resolves against the whole screen, not just the dialog | Don't rely on `scope: dialog`/`use dialog` for disambiguation on mobile — give the element inside the dialog its own unique accessibility-id/testid instead |
|
|
108
113
|
| `SG-W022` | `Then User see [X] page` under the **mobile** adapter — a native app has no URL, so the step compiles to a bare COMMENT: it reads as an assertion, checks nothing, and the scenario passes whatever is on screen. Worse than a hard failure, because nothing ever goes red. (Its `is on [X] page` twin throws via `route-assertion`; `Given User is on [X] page` is the app-LAUNCH directive and correctly emits nothing) | Assert something actually on the screen — `Then User see [Some Header] text` / a screen-marker accessibility-id — instead of a page/URL check, or tag the scenario `@manual` |
|
|
109
114
|
| `SG-W023` | A mobile **setup** step that emitted NO executable code, so a declared PRECONDITION is never established. `Given the user is logged out` matches `user-not-logged-in` → the `clear-auth` template, which on Appium is a bare COMMENT: mobile auth lives in the keychain/prefs, not in cookies, so no per-step action can clear it. The scenario then runs against whatever the PREVIOUS scenario left signed in and passes regardless — the same false-pass class as `SG-W022`, arriving through setup instead of assertion. Deliberately NOT a `throw` (an unestablished precondition weakens setup, it does not INVERT an assertion) | Declare a feature-level reset tier — `@reset:data` on the Feature — so each scenario starts from a wiped data layer; app state is reset in the `beforeEach` hook, not mid-scenario by a step. For BACKEND state (`database is empty`) the remedy is different: reset through a test API/script, or bind a `@query:` precondition, the same way the web adapter has to |
|
|
110
115
|
| `SG-W024` | Two or more scenarios in one feature **share a name** and at least ONE carries a `@reset:` tag. The generated per-scenario tier lookup is keyed on the scenario NAME (a `@cases` title is built at runtime, so no compile-time key can match the rendered title), so every same-named scenario resolves to the SAME tier — including one that declared none, which then silently runs at its namesake's tier: a `@reset:none` chain broken, or a reinstall nobody asked for. Counted over EVERY scenario rather than the tagged subset, because the untagged twin is the one that gets the wrong tier and has no tag to be counted by | **Rename** the scenarios so each title is unique. Reported rather than resolved by precedence on purpose — two tests genuinely sharing one title cannot be told apart by a name-keyed lookup, so a rename is the only honest fix (silently picking a winner would be the same silent-discard shape as `SG-W021`) |
|
|
116
|
+
| `SG-W026` | A mobile **active-navigation** step that emitted NO executable code, so a promised screen change never happens. `When User open [Settings] page` compiles to the one-line `navigation` template — a COMMENT noting the app was already launched by the wdio capabilities — so the step reads as "go to Settings" and goes nowhere; pair it with a `page` assertion (itself a no-op, `SG-W022`) and the scenario passes without ever touching the app. Scoped to the ACTIVE forms only: `Given User is on [X] page|screen` is the app-LAUNCH anchor and correctly emits nothing, so it is never reported. Deliberately NOT a `throw` — an unfulfilled navigation weakens the scenario rather than INVERTING an assertion, and throwing would break every mobile suite already carrying one | Change screens the way a user does — `When User tap [Settings] button` — then assert a marker on the new screen (`Then User see [Settings Header] text`). Keep `is on [X] screen` only as the feature's opening anchor. Note `generate --strict` fails on any diagnostic, so this code is blocking there from the day it lands |
|
|
117
|
+
| `SG-W028` | A trailing **scope clause was DROPPED**, so the step runs against more of the page than its sentence names. Two causes, one code: (1) the word after `in [X]` is not a container type — `in [Job Card] widget`, or a leaf element used as a parent — so the clause matched nothing and the first-`[ref]`/first-`{{data}}` extractors took only the head of the sentence; (2) the by-content form `in [X] <type> with {{v}}` on the **mobile** adapter, where a parent is chained with `$()` and has no content filter. Before this code, both compiled clean and silently widened the assertion to the WHOLE screen — in a list of look-alike cards the test then passes for the wrong reason, findable only by diffing the generated `.spec.ts` against the Gherkin (#667) | For (1) use a real container type: `table` `list` `list-item` `section` `dialog` `modal` `drawer` `overlay` `form` `card` `item` `row` `group` `panel` `region` `container` `tab` `menu`. For (2) give the container an accessibility-id that identifies it and scope by that, or assert on the element the value belongs to |
|
|
118
|
+
| `SG-W029` | A **mobile feature with no usable app-package `Path:`** — the line is missing, or it holds a web-style route (`Path: /applied-jobs`). The compiler then cannot name the app to reset between scenarios, and before this code that was SILENT: the per-scenario `beforeEach` was not emitted at all, so Background fell to Mocha's `before()` — once per FILE. Every scenario shared one continuous app session, so a tab switch or a scroll in one stranded elements off-screen for every later one, and the `@reset:` tier ladder was dropped with it, leaving the tag inert (#669). The spec now always emits the reset machinery and resolves the app id from the session capability instead, throwing a named error at run time if nothing supplies one | Add the package to the feature: `Path: com.example.app/.MainActivity`, or `Path: <androidPkg>/<activity> | <iosBundleId>` for a dual-id feature. A `Path:` starting with `/` is a web route and will not be read as a package. Failing that, set `APP_PACKAGE` (Android) / `IOS_BUNDLE_ID` (iOS) so `wdio.conf` puts the id on the session |
|
|
111
119
|
| `SG-E022` | The **generated spec file itself** is not parseable JS/TS, so NOTHING in it runs — Playwright reports one `SyntaxError` and every scenario in the unit is lost. A compiler self-check (`@babel/parser`) on the emitted code, added after a field report where the runtime-data marker post-processor mis-paired quote delimiters across two literals and the user only found out at `npx playwright test`. Reported, never thrown — the file is still written so you keep the artifact to inspect. **Unlike every other code in this table this one blames the COMPILER, not your `.feature`** | Nothing to fix in the Gherkin — this should be impossible. **Report it** with the `.feature` (and the step) the file was generated from, attaching the unparseable `.spec.ts`. Check first that you are running the project-local compiler (`npx sungen`), not an older global one |
|
|
112
120
|
| `SG-E023` | The step carries an **assertion clause** (`matches "…"`, `contains`, `has text`) but the generated code is a BARE VISIBILITY check — the clause was swallowed by a catch-all and the test would pass whatever the element says. The sibling of `SG-E021`, for the clauses that are not states: `SG-E021` closed the state case declaratively, but a dropped `matches`/`contains`/`has text` still slid through (reproduced live: a `matches "<format>"` step compiled by an older sungen lost the assertion entirely, emitted no warning, and left a `toBeVisible()` that passed whatever the label said). Four separate defects arrived this way, so the step now compiles to a runtime `throw`. Deliberately narrow — fires only on the bare-visibility shape, so a pattern that genuinely consumed the clause (`is-hidden`, `contains`, `matches-format`, the state family, or a `filter({ hasText })` on a list member) is untouched | Either the phrasing is not the supported one for that clause — check the exact form in `sungen-gherkin-syntax` — or this sungen predates the step: verify you are running the project-local compiler (`npx sungen`), not an older global one |
|
|
113
121
|
| `SG-E024` | A mobile **app-lifecycle** step (`User restart the app` / `User reinstall the app`) in a feature that declares no `Path:` app id. The lifecycle templates resolve the target app through `__resolveAppId()`, a helper the appium test-file template emits ONLY when the id is present, so the spec used to compile clean and then die at runtime with `ReferenceError: __resolveAppId is not defined` — taking the WHOLE spec down, not the one step, because the failure lands in a before hook. (Field case: a mobile unit scaffolded with the nav recipe in `--path` instead of `--reach`, so `Path` never carried an app id.) Now refused at compile time as a `throw` naming the missing line | Add the app id under the `Feature` line: `Path: com.example.app/.MainActivity \| com.example.app`. For a mobile unit `--path` carries the **APP ID** and `--reach` carries the navigation recipe — if the nav recipe ended up in `--path`, that is the mistake |
|
|
122
|
+
| `SG-E025` | A feature tagged `@platform:ios` uses a step the Appium adapter does **not** serve on iOS — the template either throws deep inside a device run (`set-connectivity`, `location-services`, `open-notifications`) or silently does nothing (`hide-keyboard`, a guarded `if (!driver.isIOS …)`). Derived from the template body via the per-OS axis of the step-support matrix, so it cannot disagree with the adapter. Only an explicit `@platform:ios` is gated (`@platform:mobile` picks its OS at run time). Compiles to a loud `throw` | Use a step the matrix marks ✅ for iOS (see `docs/spec/step-support-matrix.md`), tag the feature `@platform:mobile` if it should run on either OS, or tag the scenario `@manual` |
|
|
123
|
+
| `SG-E026` | A **click step names a position** (`at position "top-left"`, `at offset x=…, y=…`) that would not be honoured: the keyword is misspelt or unquoted, a value is outside the grammar (not a number, negative, above 100%, a missing axis, an unknown preset), text follows the position, or another click form claimed the step first (`try to click`, a `with {{v}}` text filter, `… and accept [OK] alert`, `double click`). Each of those would compile to a click on the element's CENTRE and pass — for a backdrop-dismiss overlay that is the one outcome that proves nothing — so the step compiles to a loud `throw` | Write `click [X] <type> at position "top-left\|top-right\|bottom-left\|bottom-right"` or `… at offset x=<n>px\|%, y=<n>px\|%` as the LAST thing in the step, on a plain `click` (no `try to`, no `with {{v}}`, no alert clause). Web only — on mobile tap the control the position targets |
|
|
124
|
+
| `SG-E027` | A selectors.yaml entry declares a **`type:` that is not a selector type** — `type: 'testId'` (capital I), `'xpth'`, `'rol'`, `'test-id'`. YAML is untyped at run time, so this used to compile clean and resolve through the strategy switch's `default:` as `getByPlaceholder('<the value>')`: a locator that cannot match, surfacing much later as a timeout naming placeholder text nobody wrote. Reported once per entry, naming the entry key and the nearest valid spelling; every step using that entry now compiles to a `throw` | Fix the spelling in selectors.yaml. Valid types: `placeholder`, `role`, `testid`, `label`, `text`, `locator`, `id`, `upload`, `page`, `column`, `frame`, `table`, and the mobile strategies `accessibility-id`, `xpath`, `android-uiautomator`, `ios-predicate`, `css`. This is a selectors.yaml-only fix — allowed inside the run-test auto-fix loop |
|
|
125
|
+
| `SG-W030` | A selectors.yaml **`expanded.class`/`expanded.attribute`** value carries a character a class or attribute name cannot — whitespace, a quote, a backslash, `$`, `{`, `}`. The value is pasted into the generated spec (string literal, regex, `classList` argument) and selectors.yaml is often generated from the page under test, so it is refused and the field dropped: the step falls back to driving and asserting `aria-expanded`. Reported once per entry and field. The checked/selected signal is refused outright instead of warned about, because dropping it leaves the step with no state to read at all | Use the plain class or attribute name (Tailwind arbitrary values such as `bg-[#eff6ff]` or `!mt-0` are accepted); never paste a selector fragment or quoted text into the field |
|
|
126
|
+
| `SG-E031` | A selectors.yaml **`checked:`/`selected:` declaration the compiler refuses**: the two spellings disagree (`checked.class` and `selected.class` with different values), a class token or attribute name carries whitespace / a quote / a backslash, or one entry declares both a `class` and an `attribute`. Every one of them is silent at run time — the generated matcher tests a single token, so the step would simply time out blaming your component — and an entry declaring two signals lets an action drive one while the assertion after it reads the other. Refused, not dropped: with no signal the step is back on the *"has no on/off state to set"* failure the declaration exists to remove | Declare ONE spelling and ONE of `class` / `attribute`, as a single plain token: `checked: { class: 'is-checked' }`, `selected: { class: 'border-link' }`, or `checked: { attribute: 'data-checked' }`. This is a selectors.yaml-only fix, allowed inside the run-test auto-fix loop |
|
|
114
127
|
|
|
115
|
-
A diagnostic never changes generation's own exit code — `sungen generate --strict` turns any collected diagnostic into a non-zero exit for CI gates. They are not all inert, though: **`SG-W001`, `SG-W011`, `SG-E020`, `SG-E021`, `SG-W020`, `SG-E023` and `SG-
|
|
128
|
+
A diagnostic never changes generation's own exit code — `sungen generate --strict` turns any collected diagnostic into a non-zero exit for CI gates. They are not all inert, though: **`SG-W001`, `SG-W011`, `SG-E020`, `SG-E021`, `SG-W020`, `SG-E023`, `SG-E024`, `SG-E025`, `SG-E026` and `SG-E027` additionally compile the offending step to a runtime `throw`, and `SG-E031` refuses the whole file** — a step that reads as a check and silently checks nothing is worse than one that fails loudly. Generation always COMPLETES: the rest of the scenario and the rest of the file still compile. `SG-E022` is the odd one out — it blames the compiler, not your `.feature`, and throws nothing (the file is written so you can inspect it).
|
|
116
129
|
|
|
117
130
|
### Runtime error → `Test data "<key>" references ${QA_*} but the environment variable is not set`
|
|
118
131
|
|
|
@@ -202,6 +215,52 @@ If `toHaveText` fails on an input → the Gherkin step has the wrong target type
|
|
|
202
215
|
|
|
203
216
|
---
|
|
204
217
|
|
|
218
|
+
## Mobile Runtime Errors (Appium / WebdriverIO)
|
|
219
|
+
|
|
220
|
+
**The fixes are NOT written here.** They live once, as data, in `driver-mobile/src/mobile-repair.ts`,
|
|
221
|
+
and `sungen repair --screen <unit>` prints the matching one for the failure you actually got:
|
|
222
|
+
|
|
223
|
+
```bash
|
|
224
|
+
sungen audit --screen <unit> # findings
|
|
225
|
+
npx playwright test # (or the wdio run) — leaves the result JSON
|
|
226
|
+
sungen repair --screen <unit> # → the concrete fix for each finding + each failure
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
This table is the INDEX into that catalog — symptom → rule id — so you can recognise a failure class
|
|
230
|
+
by eye and know a rule already covers it. Two copies of a fix drift; one copy plus an index does not.
|
|
231
|
+
`tests/ai-skills/error-mapping-repair-parity.run.ts` fails if this table and the catalog disagree in
|
|
232
|
+
either direction.
|
|
233
|
+
|
|
234
|
+
| What you see | Rule id | Class |
|
|
235
|
+
|---|---|---|
|
|
236
|
+
| `Element not found: ~key` / `no such element` | `mobile-element-absent` | selector |
|
|
237
|
+
| `iOS geometry oracle: rect={…0x0…}` | `mobile-ios-offscreen` | selector |
|
|
238
|
+
| `element click intercepted` / `is not clickable at point` | `mobile-tap-intercepted` | selector |
|
|
239
|
+
| `Default scrollable element '//android.widget.ScrollView' not found` | `mobile-scroll-no-scrollable` | selector |
|
|
240
|
+
| `A new session could not be created` | `mobile-session-not-created` | session |
|
|
241
|
+
| `Could not proxy command to the remote server` / `socket hang up` | `mobile-driver-proxy-lost` | session |
|
|
242
|
+
| `The instrumentation process cannot be initialized` | `mobile-instrumentation-failed` | session |
|
|
243
|
+
| `No Chromedriver found that can automate Chrome '…'` | `mobile-chromedriver-mismatch` | session |
|
|
244
|
+
| `[sungen] "switch to X frame" — no WEBVIEW/CHROMIUM context appeared within …ms` | `mobile-webview-context-absent` | session |
|
|
245
|
+
| `@auth:<role> login did not complete` / `could not decide the login state` | `mobile-auth-session` | session |
|
|
246
|
+
| `device log (logcat) did not contain "…"` | `mobile-device-log-silent` | oracle |
|
|
247
|
+
| `[sungen] … web-only` / `not supported on the appium adapter` | `mobile-web-only-step` | compile (SG-W020/E020) |
|
|
248
|
+
| `[sungen] step "…" matched no pattern` | `mobile-step-unmatched` | compile (SG-W001) |
|
|
249
|
+
| `… needs the app id: the mobile lifecycle steps resolve …` | `mobile-lifecycle-no-app-id` | compile (SG-E024) |
|
|
250
|
+
| `… This feature declares @platform:ios` | `mobile-ios-unsupported-step` | compile (SG-E025) |
|
|
251
|
+
| `UNIVERSAL: missing theme(s): …` | `mobile-universal-theme` | audit |
|
|
252
|
+
| `GATE: critical theme "…" has NO covering scenario` | `mobile-gate-theme-uncovered` | audit |
|
|
253
|
+
| `GATE: critical theme "…" covered only by SHALLOW` | `mobile-gate-theme-shallow` | audit |
|
|
254
|
+
| `VIEWPOINT-GESTURE-SUBSTITUTED: …` | `mobile-gesture-substituted` | audit |
|
|
255
|
+
| `MANUAL-CODE-MISSING: …` | `mobile-manual-uncoded` | audit |
|
|
256
|
+
| `PAGE-TYPE-UNDETERMINED: …` | `mobile-page-type-undetermined` | audit |
|
|
257
|
+
|
|
258
|
+
A failure with no rule id is reported by `sungen repair` under **"No known rule (review manually)"**.
|
|
259
|
+
That list is deliberately not empty-by-construction: a rule broad enough to match everything would
|
|
260
|
+
hide the findings nobody has looked at yet.
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
205
264
|
## Performance & Infrastructure Errors → Fix in `specs/base.ts`
|
|
206
265
|
|
|
207
266
|
All generated `.spec.ts` import from `specs/base.ts` — shared context caching, navigation, overlay cleanup. AI **can and should** tune `base.ts` to match the project.
|