@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
|
@@ -1,18 +1,49 @@
|
|
|
1
1
|
import { ParsedStep } from '../gherkin-parser';
|
|
2
|
+
import { PARENT_CONTAINER_TYPES } from '../gherkin-parser/parent-scope-clause';
|
|
2
3
|
import { TemplateEngine } from './template-engine';
|
|
3
4
|
import { adapterRegistry } from './adapters';
|
|
4
5
|
import { PatternRegistry, PatternContext, Platform } from './patterns';
|
|
5
6
|
import { SelectorResolver } from './utils/selector-resolver';
|
|
6
7
|
import { DataResolver } from './utils/data-resolver';
|
|
8
|
+
import { locatorIdentityKey } from './utils/dialog-scope-identity';
|
|
7
9
|
import { DiagnosticsCollector } from './diagnostics';
|
|
8
10
|
import { unsupportedStepCode } from './patterns/unsupported-step-code';
|
|
11
|
+
import { maskRefsForMatching } from './utils/mask-refs-for-matching';
|
|
12
|
+
import { isKnownSelectorType } from './utils/selector-type-validation';
|
|
9
13
|
import { loadAppProfile, ResolvedAppProfile } from './app-profile';
|
|
10
14
|
import { capabilityRegistry } from '../../capabilities/registry';
|
|
11
15
|
|
|
16
|
+
/**
|
|
17
|
+
* The correction SG-W001 owes a project that gated a platform driver away.
|
|
18
|
+
*
|
|
19
|
+
* Gating (plan `260903-1654` phase 11) keeps a mobile-only pattern out of a web project's registry.
|
|
20
|
+
* Right isolation — but `platformMiss` then has nothing to catch, so a mobile-only step falls
|
|
21
|
+
* through to SG-W001, whose sentence "sungen has no step form for it" is FALSE: the form exists in
|
|
22
|
+
* a vocabulary this project declined. Ungated, the author would have seen SG-W020 naming the
|
|
23
|
+
* alternative.
|
|
24
|
+
*
|
|
25
|
+
* It states only what discovery knows. Which STEP the skipped driver owned is unknowable here — the
|
|
26
|
+
* patterns were never loaded — and a static list of off-platform step texts would rot on the next
|
|
27
|
+
* pattern anyone adds, putting a second wrong sentence where the first one was.
|
|
28
|
+
*
|
|
29
|
+
* Returns '' when nothing was gated, so every project that predates the platform profile keeps the
|
|
30
|
+
* message it has today, word for word.
|
|
31
|
+
*/
|
|
32
|
+
function offPlatformHint(): string {
|
|
33
|
+
const gated = capabilityRegistry.gatedDrivers();
|
|
34
|
+
if (!gated.length) return '';
|
|
35
|
+
const platform = capabilityRegistry.gatedForPlatform();
|
|
36
|
+
const names = gated.map(([pkg, serves]) => `${pkg} (${serves})`).join(', ');
|
|
37
|
+
return `this project declares platform: ${platform}, so ${names} was not loaded; a ${gated[0][1]}-only step form exists but is out of scope here. Per-platform vocabulary: docs/spec/step-support-matrix.md.`;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
12
41
|
export interface MappedStep {
|
|
13
42
|
code: string; // Generated Playwright code
|
|
14
43
|
comment?: string; // Optional comment
|
|
15
44
|
imports?: string[]; // Required imports
|
|
45
|
+
pattern?: string; // Name of the step form that produced the code (post-map guards read it)
|
|
46
|
+
templateData?: Record<string, any>; // What the resolver handed the template (e.g. a recorded refusal)
|
|
16
47
|
}
|
|
17
48
|
|
|
18
49
|
/**
|
|
@@ -28,6 +59,8 @@ export class StepMapper {
|
|
|
28
59
|
private dataResolver: DataResolver;
|
|
29
60
|
private baseURL: string;
|
|
30
61
|
private featureName?: string;
|
|
62
|
+
/** The feature's declared `@platform:` OS, for the compile-time iOS gate (SG-E025). */
|
|
63
|
+
private featurePlatform?: 'mobile' | 'android' | 'ios';
|
|
31
64
|
private screenName?: string;
|
|
32
65
|
private featurePath?: string;
|
|
33
66
|
private currentScenarioSteps?: ParsedStep[];
|
|
@@ -74,7 +107,9 @@ export class StepMapper {
|
|
|
74
107
|
this.platform = adapter.frameworkName === 'appium' ? 'mobile' : 'web';
|
|
75
108
|
this.templateEngine = new TemplateEngine(templatesDir);
|
|
76
109
|
this.patternRegistry = new PatternRegistry();
|
|
77
|
-
|
|
110
|
+
// `platform` is set just above from the ADAPTER — the resolver needs it to decline the
|
|
111
|
+
// list-member auto-infer that has no mobile equivalent (#665).
|
|
112
|
+
this.selectorResolver = new SelectorResolver(undefined, options.screenName, this.platform);
|
|
78
113
|
this.dataResolver = new DataResolver(undefined, options.screenName, options.runtimeData);
|
|
79
114
|
|
|
80
115
|
// App profile (qa/app.yaml) — loaded after PatternRegistry so capability discovery has
|
|
@@ -100,8 +135,9 @@ export class StepMapper {
|
|
|
100
135
|
/**
|
|
101
136
|
* Set feature context for data and selector resolution
|
|
102
137
|
*/
|
|
103
|
-
setFeatureContext(featureName: string, featurePath?: string): void {
|
|
138
|
+
setFeatureContext(featureName: string, featurePath?: string, featurePlatform?: 'mobile' | 'android' | 'ios'): void {
|
|
104
139
|
this.featureName = featureName;
|
|
140
|
+
this.featurePlatform = featurePlatform;
|
|
105
141
|
this.featurePath = featurePath;
|
|
106
142
|
this.selectorResolver.setFeatureContext(featureName);
|
|
107
143
|
// One StepMapper serves every feature in a run, so the step counter that names the
|
|
@@ -173,6 +209,19 @@ export class StepMapper {
|
|
|
173
209
|
this.dataResolver.registerCaptured(name);
|
|
174
210
|
}
|
|
175
211
|
|
|
212
|
+
/**
|
|
213
|
+
* Register the head of a variable bound by the feature's `@beforeAll` hook. Feature-scoped: it
|
|
214
|
+
* survives setScenarioContext() so every scenario can read `{{head.*}}` as a runtime get().
|
|
215
|
+
*/
|
|
216
|
+
registerFeatureCaptured(head: string): void {
|
|
217
|
+
this.dataResolver.registerFeatureCaptured(head);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** Drop feature-scoped captured vars — call once per feature, before its hooks and scenarios map. */
|
|
221
|
+
clearFeatureCaptured(): void {
|
|
222
|
+
this.dataResolver.clearFeatureCaptured();
|
|
223
|
+
}
|
|
224
|
+
|
|
176
225
|
/**
|
|
177
226
|
* Map a Gherkin step to Playwright code
|
|
178
227
|
* Uses pattern registry first, falls back to AI if enabled
|
|
@@ -186,6 +235,8 @@ export class StepMapper {
|
|
|
186
235
|
this.lastPrimaryKeyword = keyword;
|
|
187
236
|
}
|
|
188
237
|
|
|
238
|
+
this.warnScopeClauseDiscarded(step);
|
|
239
|
+
|
|
189
240
|
// SG-W015 — `see [X] card with {{n}}` where n is a bare number compiles to a TEXT
|
|
190
241
|
// assertion (toHaveText('3')), not a count. An author reaching for "3 cards are shown"
|
|
191
242
|
// gets an assertion that can only pass if the card's TEXT is the digit — a wrong test
|
|
@@ -265,20 +316,25 @@ export class StepMapper {
|
|
|
265
316
|
this.templateEngine.setBaseContext({ inFrame: true, frameSelector });
|
|
266
317
|
// C1 — routed through the adapter instead of building `page.frameLocator(...)` inline:
|
|
267
318
|
// the Appium adapter's own template performs its webview-context switch instead.
|
|
268
|
-
|
|
319
|
+
// `frameRef` carries the name as WRITTEN in the Gherkin: the Appium twin can fail (#705),
|
|
320
|
+
// and its error has to quote the step the author would go and look at, not the resolved
|
|
321
|
+
// xpath they never typed.
|
|
322
|
+
const code = this.templateEngine.renderStep('frame-enter-action', { frameSelector, frameRef: selectorRef });
|
|
269
323
|
return { code, comment: `Switch to ${selectorRef} frame` };
|
|
270
324
|
}
|
|
271
325
|
|
|
272
326
|
// Dialog scope directives — intercept before pattern matching
|
|
273
327
|
if (/\buse dialog\b/i.test(step.text)) {
|
|
274
328
|
this.inDialogScope = true;
|
|
275
|
-
|
|
329
|
+
// setBaseContext MERGES — an earlier scope's root keys in the same scenario would
|
|
330
|
+
// otherwise leak into this unnamed one.
|
|
331
|
+
this.templateEngine.setBaseContext({ inDialog: true, dialogScopeKey: undefined });
|
|
276
332
|
this.warnDialogScopeIgnoredOnMobile(step);
|
|
277
333
|
return { code: '', comment: 'Enter dialog scope' };
|
|
278
334
|
}
|
|
279
335
|
if (/\b(?:close|dismiss|exit)\s+dialog\b/i.test(step.text)) {
|
|
280
336
|
this.inDialogScope = false;
|
|
281
|
-
this.templateEngine.setBaseContext({ inDialog: false });
|
|
337
|
+
this.templateEngine.setBaseContext({ inDialog: false, dialogScopeKey: undefined });
|
|
282
338
|
return { code: '', comment: 'Exit dialog scope' };
|
|
283
339
|
}
|
|
284
340
|
// `is on [X] <type>` supports exactly two shapes: `page` (URL from the page-type selector
|
|
@@ -314,6 +370,7 @@ export class StepMapper {
|
|
|
314
370
|
dialogRootExpr: undefined,
|
|
315
371
|
dialogRoleName: undefined,
|
|
316
372
|
dialogFilterText: undefined,
|
|
373
|
+
dialogScopeKey: undefined,
|
|
317
374
|
};
|
|
318
375
|
|
|
319
376
|
if (step.selectorRef && step.dataRef) {
|
|
@@ -342,6 +399,16 @@ export class StepMapper {
|
|
|
342
399
|
}
|
|
343
400
|
// Case 1: no selectorRef → plain page.getByRole('dialog')
|
|
344
401
|
|
|
402
|
+
// Record what this root IS, so a later step naming the same element is recognized as the
|
|
403
|
+
// root rather than nested inside it (issue #692). The declared entry when there is one;
|
|
404
|
+
// otherwise the named-dialog fallback the element itself will resolve to.
|
|
405
|
+
if (step.selectorRef) {
|
|
406
|
+
const entry = this.resolveDialogEntry(step.selectorRef);
|
|
407
|
+
contextVars.dialogScopeKey = entry
|
|
408
|
+
? locatorIdentityKey(entry)
|
|
409
|
+
: locatorIdentityKey({ strategy: 'role', role: 'dialog', name: step.selectorRef });
|
|
410
|
+
}
|
|
411
|
+
|
|
345
412
|
this.templateEngine.setBaseContext(contextVars);
|
|
346
413
|
return { code: '', comment: `Enter dialog scope${step.selectorRef ? ': ' + step.selectorRef : ''}` };
|
|
347
414
|
}
|
|
@@ -396,10 +463,15 @@ export class StepMapper {
|
|
|
396
463
|
this.checkDataRefConsumed(step, mappedStep);
|
|
397
464
|
this.checkPageAssertionNoOpOnMobile(step, mappedStep);
|
|
398
465
|
this.checkSetupNoOpOnMobile(step, mappedStep);
|
|
399
|
-
this.
|
|
466
|
+
this.checkNavigationNoOpOnMobile(step, mappedStep);
|
|
467
|
+
this.checkPageStepFallback(step, mappedStep);
|
|
400
468
|
this.checkDeclaredUrlHasNoQuery(step, mappedStep);
|
|
469
|
+
const positionDropped = this.checkClickPositionDropped(step, mappedStep);
|
|
470
|
+
if (positionDropped) return positionDropped;
|
|
401
471
|
const swallowed = this.checkAssertionClauseSwallowed(step, mappedStep);
|
|
402
472
|
if (swallowed) return swallowed;
|
|
473
|
+
const badType = this.checkUnknownSelectorType(step, mappedStep);
|
|
474
|
+
if (badType) return badType;
|
|
403
475
|
return mappedStep;
|
|
404
476
|
}
|
|
405
477
|
|
|
@@ -422,7 +494,13 @@ export class StepMapper {
|
|
|
422
494
|
step: step.text,
|
|
423
495
|
});
|
|
424
496
|
} else {
|
|
425
|
-
|
|
497
|
+
// Two shapes, because the ORIGINAL CLAUSE is what was wrong. Appending a correction to
|
|
498
|
+
// "sungen has no step form for it" leaves the reader a sentence that contradicts itself one
|
|
499
|
+
// clause later. When the gate dropped a vocabulary, that clause is simply not written.
|
|
500
|
+
const hint = offPlatformHint();
|
|
501
|
+
reason = hint
|
|
502
|
+
? `step "${step.text}" matched no pattern here — ${hint} If that is not what you meant, rephrase using a supported step form (see the sungen-gherkin-syntax reference), or tag the scenario @manual if the check cannot be automated.`
|
|
503
|
+
: `step "${step.text}" matched no pattern — sungen has no step form for it. Rephrase using a supported step form (see the sungen-gherkin-syntax reference), or tag the scenario @manual if the check cannot be automated.`;
|
|
426
504
|
this.diagnostics?.add('SG-W001', reason, {
|
|
427
505
|
feature: this.featureName,
|
|
428
506
|
step: step.text,
|
|
@@ -450,6 +528,27 @@ export class StepMapper {
|
|
|
450
528
|
* falls back to the legacy named-dialog root, so behavior is unchanged for those entries.
|
|
451
529
|
*/
|
|
452
530
|
private renderDialogRoot(selectorRef: string, opts: { nonRoleOnly?: boolean } = {}): string | undefined {
|
|
531
|
+
const resolved = this.resolveDialogEntry(selectorRef);
|
|
532
|
+
if (!resolved) return undefined;
|
|
533
|
+
if (!StepMapper.DIALOG_ROOT_STRATEGIES.includes(resolved.strategy)) return undefined;
|
|
534
|
+
if (opts.nonRoleOnly && resolved.strategy === 'role') return undefined;
|
|
535
|
+
|
|
536
|
+
// Render the root PAGE-rooted: leaving inDialog set (or `scope: dialog` on the entry
|
|
537
|
+
// itself) would send the partial back through dialog-root and nest the dialog in itself.
|
|
538
|
+
const expr = this.templateEngine.renderStep('locator', {
|
|
539
|
+
...resolved,
|
|
540
|
+
scope: resolved.scope === 'dialog' ? undefined : resolved.scope,
|
|
541
|
+
inDialog: false,
|
|
542
|
+
}).trim();
|
|
543
|
+
return expr || undefined;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/**
|
|
547
|
+
* The selectors.yaml entry behind a dialog reference, normalized the way the `locator` partial
|
|
548
|
+
* needs it. Shared by `renderDialogRoot` (which turns it into the root expression) and the
|
|
549
|
+
* scope-entry path (which keys off it to recognize the root again later — issue #692).
|
|
550
|
+
*/
|
|
551
|
+
private resolveDialogEntry(selectorRef: string): any | undefined {
|
|
453
552
|
let resolved: any;
|
|
454
553
|
try {
|
|
455
554
|
resolved = this.selectorResolver.resolveSelector(selectorRef, this.featureName, 'dialog', 0);
|
|
@@ -463,34 +562,27 @@ export class StepMapper {
|
|
|
463
562
|
if (resolved.strategy === 'css' && resolved.locator) {
|
|
464
563
|
resolved = { ...resolved, strategy: 'locator', value: resolved.locator };
|
|
465
564
|
}
|
|
466
|
-
|
|
467
|
-
if (opts.nonRoleOnly && resolved.strategy === 'role') return undefined;
|
|
468
|
-
|
|
469
|
-
// Render the root PAGE-rooted: leaving inDialog set (or `scope: dialog` on the entry
|
|
470
|
-
// itself) would send the partial back through dialog-root and nest the dialog in itself.
|
|
471
|
-
const expr = this.templateEngine.renderStep('locator', {
|
|
472
|
-
...resolved,
|
|
473
|
-
scope: resolved.scope === 'dialog' ? undefined : resolved.scope,
|
|
474
|
-
inDialog: false,
|
|
475
|
-
}).trim();
|
|
476
|
-
return expr || undefined;
|
|
565
|
+
return resolved;
|
|
477
566
|
}
|
|
478
567
|
|
|
479
568
|
/**
|
|
480
|
-
* SG-W013 — a page
|
|
481
|
-
* selector entry.
|
|
482
|
-
*
|
|
483
|
-
* anchored that produced a test which always PASSED; anchored, it produces
|
|
484
|
-
* pass
|
|
569
|
+
* SG-W013 — a page STEP whose path came from a fallback rather than from a `type: page`
|
|
570
|
+
* selector entry. Every resolver degrades quietly when `[Ref]` does not resolve: `see [X] page`
|
|
571
|
+
* and `wait for [X] page` fall back to the feature's own path, `is on [X] page` to `/<ref>/`.
|
|
572
|
+
* Before the match was anchored that produced a test which always PASSED; anchored, it produces
|
|
573
|
+
* one that can never pass — an assertion that always fails, or, since #677 anchored the wait
|
|
574
|
+
* too, a wait that can only time out. None is the step the author wrote, and nothing said so.
|
|
485
575
|
*
|
|
486
|
-
* Detected here rather than in the resolver on purpose:
|
|
487
|
-
*
|
|
488
|
-
*
|
|
576
|
+
* Detected here rather than in the resolver on purpose: the resolvers live in the driver packages
|
|
577
|
+
* while the collector lives in core (the mapped step now also names its pattern and carries the
|
|
578
|
+
* resolver's data, but the fallback itself is only visible by re-resolving the selector). Re-asking the SAME resolver the step-mapper already owns costs three lines and
|
|
489
579
|
* duplicates no logic — the alternative is widening the adapter contract for every driver.
|
|
490
580
|
*/
|
|
491
|
-
private
|
|
581
|
+
private checkPageStepFallback(step: ParsedStep, mapped: MappedStep): void {
|
|
492
582
|
if (!this.diagnostics || !step.selectorRef || step.elementType !== 'page') return;
|
|
493
|
-
|
|
583
|
+
// every shape that compiles a page path into an ANCHORED match: the two assertions (#662)
|
|
584
|
+
// and the wait (#677). A step that emitted none of them has no path to get wrong.
|
|
585
|
+
if (!/\b(?:urlMatches|expectPage|waitForPage)\(/.test(mapped.code || '')) return;
|
|
494
586
|
let resolvedAsPage = false;
|
|
495
587
|
try {
|
|
496
588
|
const resolved = this.selectorResolver.resolveSelector(
|
|
@@ -502,7 +594,7 @@ export class StepMapper {
|
|
|
502
594
|
}
|
|
503
595
|
if (resolvedAsPage) return;
|
|
504
596
|
this.diagnostics.add('SG-W013',
|
|
505
|
-
`page
|
|
597
|
+
`page step "[${step.selectorRef}]" has no "type: page" selector entry, so it targets a FALLBACK path — the anchored match can never reach the real URL (an assertion always fails; a wait can only time out). Declare the page, e.g. "${step.selectorRef.toLowerCase()}:\\n type: page\\n value: '/your/path'"`,
|
|
506
598
|
{ feature: this.featureName, step: step.text });
|
|
507
599
|
}
|
|
508
600
|
|
|
@@ -551,6 +643,43 @@ export class StepMapper {
|
|
|
551
643
|
* Deliberately narrow — it fires only on the bare-visibility shape, so a pattern that genuinely
|
|
552
644
|
* consumed the clause (`is-hidden`, `contains`, `matches-format`, the state family) is untouched.
|
|
553
645
|
*/
|
|
646
|
+
/**
|
|
647
|
+
* SG-E027 (#711) — the step names an entry whose `type:` is not a selector type, so the resolver
|
|
648
|
+
* fell to its placeholder default and this step compiled to `getByPlaceholder('<the value>')`:
|
|
649
|
+
* a locator that cannot match, which would fail much later as a timeout naming placeholder text
|
|
650
|
+
* the author never wrote. Compile it to a `throw` instead — the same reasoning as SG-E023 above,
|
|
651
|
+
* that a step which reads as a check and can only ever fail obscurely is worse than one that
|
|
652
|
+
* fails loudly and says why.
|
|
653
|
+
*
|
|
654
|
+
* The DIAGNOSTIC is not added here. `SelectorResolver.loadNewSelectorFile` already reported this
|
|
655
|
+
* entry once for the whole file; re-reporting per step would print the same line twenty times for
|
|
656
|
+
* one typo. This method only replaces the code, so the compile-time message and the runtime
|
|
657
|
+
* failure stay one fact reported once.
|
|
658
|
+
*
|
|
659
|
+
* A resolve throw means the key is missing entirely — a different defect that the resolver's own
|
|
660
|
+
* error already owns — and an auto-inferred selector stamps no `selectorType`, so both skip.
|
|
661
|
+
*/
|
|
662
|
+
private checkUnknownSelectorType(step: ParsedStep, mapped: MappedStep): MappedStep | null {
|
|
663
|
+
if (!step.selectorRef) return null;
|
|
664
|
+
let selectorType: unknown;
|
|
665
|
+
try {
|
|
666
|
+
selectorType = this.selectorResolver.resolveSelector(
|
|
667
|
+
step.selectorRef, this.featureName, step.elementType, step.nth,
|
|
668
|
+
).selectorType;
|
|
669
|
+
} catch {
|
|
670
|
+
return null; // missing key — the resolver's own error owns that
|
|
671
|
+
}
|
|
672
|
+
if (selectorType === undefined || isKnownSelectorType(selectorType)) return null;
|
|
673
|
+
const reason = `step "${step.text}" names selector "${step.selectorRef}", whose selectors.yaml `
|
|
674
|
+
+ `entry declares type: "${String(selectorType)}" — not a selector type, so the entry `
|
|
675
|
+
+ `resolved to a placeholder locator that cannot match. See SG-E027 for the valid types and `
|
|
676
|
+
+ `the nearest spelling to what was written.`;
|
|
677
|
+
return {
|
|
678
|
+
code: unsupportedStepCode(reason),
|
|
679
|
+
comment: `Unknown selector type "${String(selectorType)}" — see SG-E027: ${step.text}`,
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
|
|
554
683
|
private checkAssertionClauseSwallowed(step: ParsedStep, mapped: MappedStep): MappedStep | null {
|
|
555
684
|
const code = mapped.code || '';
|
|
556
685
|
// Bare visibility: nothing but a visible/displayed assertion on one locator.
|
|
@@ -593,6 +722,42 @@ export class StepMapper {
|
|
|
593
722
|
};
|
|
594
723
|
}
|
|
595
724
|
|
|
725
|
+
/**
|
|
726
|
+
* A click step that names a POSITION (`at position "top-left"`, `at offset x=…, y=…`) must compile
|
|
727
|
+
* through the positioned-click form with a position it understood. Anything else — a typo in the
|
|
728
|
+
* keyword, a value the grammar rejects, or a combination another click form claims first (`try to
|
|
729
|
+
* click … at position`, `… at offset … and accept [OK] alert`, `… with {{v}} … at position`) —
|
|
730
|
+
* would otherwise click the element's CENTRE and pass, which for a backdrop-dismiss test is the
|
|
731
|
+
* one outcome that proves nothing. Owned by the compiler rather than the driver so it holds no
|
|
732
|
+
* matter which pattern wins.
|
|
733
|
+
*/
|
|
734
|
+
private static readonly CLICK_POSITION_HINT =
|
|
735
|
+
/\bat\s+(?:position|offset)\b|\bat\s+\w+\s+(?:"(?:top|bottom)-(?:left|right)"|x\s*=)|\b(?:top|bottom)[- ](?:left|right)\b|\bat\s+x\s*=/i;
|
|
736
|
+
|
|
737
|
+
private checkClickPositionDropped(step: ParsedStep, mapped: MappedStep): MappedStep | null {
|
|
738
|
+
// Refs are masked; so is every quoted VALUE except a preset name — `with "Engineer at position 3"`
|
|
739
|
+
// is a text filter, not a position, while a mistyped `at postion "top-left"` must stay visible.
|
|
740
|
+
const masked = maskRefsForMatching(step.text)
|
|
741
|
+
.replace(/"([^"]*)"/g, (quoted, inner: string) => (/^(?:top|bottom)-(?:left|right)$/i.test(inner.trim()) ? quoted : '"~"'));
|
|
742
|
+
if (!/\bclick/i.test(masked) || !StepMapper.CLICK_POSITION_HINT.test(masked)) return null;
|
|
743
|
+
// Already refused upstream (off-platform SG-W020, a dropped state clause, a missing template…):
|
|
744
|
+
// that refusal names the real cause; this guard only exists to stop a SILENT centre click.
|
|
745
|
+
if (/^throw new Error\("\[sungen\]/.test(mapped.code.trim())) return null;
|
|
746
|
+
const refused = mapped.templateData?.positionError as string | undefined;
|
|
747
|
+
if (mapped.pattern === 'click-element-at-position' && !refused) return null;
|
|
748
|
+
const reason = refused
|
|
749
|
+
? `step "${step.text}": ${refused}`
|
|
750
|
+
: `step "${step.text}" asks for a click position but compiled through the "${mapped.pattern ?? 'unknown'}" `
|
|
751
|
+
+ 'step form, which clicks the element\'s centre — the position would be silently dropped. Write '
|
|
752
|
+
+ 'it as `click [X] <type> at position "<preset>"` or `… at offset x=<n>px|%, y=<n>px|%` on its own: '
|
|
753
|
+
+ '`try to click`, a `with {{v}}` text filter and `… and accept [OK] alert` do not take a position.';
|
|
754
|
+
this.diagnostics?.add('SG-E026', reason, { feature: this.featureName, step: step.text });
|
|
755
|
+
return {
|
|
756
|
+
code: unsupportedStepCode(reason),
|
|
757
|
+
comment: `Click position dropped — see SG-E026: ${step.text}`,
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
|
|
596
761
|
private checkDataRefConsumed(step: ParsedStep, mapped: MappedStep): void {
|
|
597
762
|
if (!step.dataRef || !this.diagnostics) return;
|
|
598
763
|
const code = mapped.code || '';
|
|
@@ -665,6 +830,70 @@ export class StepMapper {
|
|
|
665
830
|
/**
|
|
666
831
|
* Create pattern context for generators
|
|
667
832
|
*/
|
|
833
|
+
/**
|
|
834
|
+
* Whether a mapped step emits anything that actually RUNS.
|
|
835
|
+
*
|
|
836
|
+
* Comments are stripped — block comments as well as line ones, because a template written with
|
|
837
|
+
* `/* … *\/` would otherwise read as executable code and silence the no-op diagnostics exactly
|
|
838
|
+
* when they were needed. Keyed on the emitted CODE rather than on a template name (MappedStep
|
|
839
|
+
* does not carry one), so any future template that degrades to a comment is caught without being
|
|
840
|
+
* enumerated anywhere.
|
|
841
|
+
*
|
|
842
|
+
* Shared by the SG-W022 / SG-W023 / SG-W026 checks — the three of them ask the same question, and
|
|
843
|
+
* three copies of this would drift.
|
|
844
|
+
*/
|
|
845
|
+
private emitsExecutableCode(mapped: MappedStep): boolean {
|
|
846
|
+
return Boolean(
|
|
847
|
+
(mapped.code || '')
|
|
848
|
+
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
849
|
+
.split('\n')
|
|
850
|
+
.map((l) => l.replace(/\/\/.*$/, '').trim())
|
|
851
|
+
.filter(Boolean)
|
|
852
|
+
.join(''),
|
|
853
|
+
);
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
/**
|
|
857
|
+
* SG-W026 — a mobile ACTIVE-NAVIGATION step that emitted no executable code, so a promised screen
|
|
858
|
+
* change never happens.
|
|
859
|
+
*
|
|
860
|
+
* `When User open [Settings] page` compiles to the one-line `navigation` template: a comment
|
|
861
|
+
* noting the app was already launched by the wdio capabilities. The step reads as "go to Settings"
|
|
862
|
+
* and goes nowhere. Pair it with `Then User see [X] page` — itself a no-op, reported as SG-W022 —
|
|
863
|
+
* and the whole scenario passes without ever touching the app.
|
|
864
|
+
*
|
|
865
|
+
* This completes a class that was handled in halves: `route-assertion` already throws loudly and
|
|
866
|
+
* `page-assertion` already reports W022; `navigation` alone was silent.
|
|
867
|
+
*
|
|
868
|
+
* Scoped to the ACTIVE forms. `is on [X] page` and its mobile `screen` twin are mobile's
|
|
869
|
+
* app-LAUNCH anchor, whose empty output is correct and documented as such where IS_ON_TYPES is
|
|
870
|
+
* defined — warning there would cry wolf on every valid mobile feature, and a diagnostic that
|
|
871
|
+
* cries wolf gets filtered out, taking the real ones with it. The exclusion is matched on
|
|
872
|
+
* `is on` FIRST, before the open/navigate test, because "the user is on the page they opened"
|
|
873
|
+
* phrasings contain both.
|
|
874
|
+
*
|
|
875
|
+
* Deliberately NOT a `throw`, for the same reason as SG-W023: an unfulfilled navigation weakens
|
|
876
|
+
* the scenario rather than inverting an assertion, and throwing would break every mobile suite
|
|
877
|
+
* already carrying one of these steps. `generate --strict` is the opt-in that makes it fail.
|
|
878
|
+
*/
|
|
879
|
+
private checkNavigationNoOpOnMobile(step: ParsedStep, mapped: MappedStep): void {
|
|
880
|
+
if (this.platform !== 'mobile' || !this.diagnostics) return;
|
|
881
|
+
|
|
882
|
+
const text = step.text.toLowerCase();
|
|
883
|
+
// The anchor is checked first and wins: it is legitimately empty on mobile.
|
|
884
|
+
if (/\bis\s+on\b/.test(text)) return;
|
|
885
|
+
if (!/\bopens?\b|\bnavigates?\s+to\b|\bgo(es)?\s+to\b/.test(text)) return;
|
|
886
|
+
// Only the navigation TARGET forms — `open [X] page|screen`. `open [X] dialog`/`menu` is a tap
|
|
887
|
+
// on a control, which compiles to real code and never reaches here anyway.
|
|
888
|
+
if (step.elementType && !['page', 'screen'].includes(step.elementType)) return;
|
|
889
|
+
|
|
890
|
+
if (this.emitsExecutableCode(mapped)) return;
|
|
891
|
+
|
|
892
|
+
this.diagnostics.add('SG-W026',
|
|
893
|
+
'this navigation step compiled to a COMMENT — a native app is already launched by the wdio capabilities, so nothing navigates and the following steps run against whatever screen the app happens to be on. Change screens the way a user does, with a tap/gesture on the control that opens it (`When User tap [Settings] button`), and assert a marker on the new screen; keep `is on [X] screen` only as the feature\'s opening anchor',
|
|
894
|
+
{ feature: this.featureName, step: step.text });
|
|
895
|
+
}
|
|
896
|
+
|
|
668
897
|
/**
|
|
669
898
|
* SG-W023 — a mobile SETUP step that emitted no executable code, so a declared precondition is
|
|
670
899
|
* never actually established.
|
|
@@ -699,13 +928,7 @@ export class StepMapper {
|
|
|
699
928
|
// Strip comments; whatever remains is what actually runs. Block comments too — a template written
|
|
700
929
|
// with `/* … */` would otherwise read as executable code and silence this check exactly when it
|
|
701
930
|
// was needed.
|
|
702
|
-
|
|
703
|
-
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
704
|
-
.split('\n')
|
|
705
|
-
.map((l) => l.replace(/\/\/.*$/, '').trim())
|
|
706
|
-
.filter(Boolean)
|
|
707
|
-
.join('');
|
|
708
|
-
if (executable) return;
|
|
931
|
+
if (this.emitsExecutableCode(mapped)) return;
|
|
709
932
|
|
|
710
933
|
const remedy = isBackendState
|
|
711
934
|
? 'backend state is not app state — reset it through a test API/script, or bind a `@query:` precondition, the same way the web adapter has to'
|
|
@@ -738,18 +961,28 @@ export class StepMapper {
|
|
|
738
961
|
// Strip comments; whatever remains is what actually runs. Block comments are handled as well as
|
|
739
962
|
// line ones: a template written with `/* … */` would otherwise read as executable code and
|
|
740
963
|
// silently defeat this check — the diagnostic would go quiet exactly when it was needed.
|
|
741
|
-
|
|
742
|
-
.replace(/\/\*[\s\S]*?\*\//g, '')
|
|
743
|
-
.split('\n')
|
|
744
|
-
.map((l) => l.replace(/\/\/.*$/, '').trim())
|
|
745
|
-
.filter(Boolean)
|
|
746
|
-
.join('');
|
|
747
|
-
if (executable) return;
|
|
964
|
+
if (this.emitsExecutableCode(mapped)) return;
|
|
748
965
|
this.diagnostics.add('SG-W022',
|
|
749
966
|
'a native app has no URL, so this page assertion compiled to a COMMENT — it asserts nothing and the scenario passes regardless. Assert a marker that is actually on the screen instead (`Then User see [Some Element] ...`), or tag the scenario @manual',
|
|
750
967
|
{ feature: this.featureName, step: step.text });
|
|
751
968
|
}
|
|
752
969
|
|
|
970
|
+
/**
|
|
971
|
+
* SG-W028 — a trailing `in [X] <word>` scope clause whose `<word>` is not a container type.
|
|
972
|
+
*
|
|
973
|
+
* The clause is dropped, and everything downstream takes the FIRST `[ref]` and the FIRST
|
|
974
|
+
* `{{data}}` it finds, so the sentence compiles clean while the assertion silently widens to the
|
|
975
|
+
* whole screen (#667). Unlike an unrecognized STEP, which at least raises SG-W001, a partially
|
|
976
|
+
* understood step used to say nothing at all — and a check that runs against more than it names
|
|
977
|
+
* passes for the wrong reason in exactly the list-of-identical-cards case it was written for.
|
|
978
|
+
*/
|
|
979
|
+
private warnScopeClauseDiscarded(step: ParsedStep): void {
|
|
980
|
+
if (!step.unsupportedScopeType) return;
|
|
981
|
+
this.diagnostics?.add('SG-W028',
|
|
982
|
+
`\`${step.unsupportedScopeType}\` is not a container type, so the scope clause was DROPPED and this step runs against the whole page, not the named container. Supported: ${PARENT_CONTAINER_TYPES.join(', ')}`,
|
|
983
|
+
{ feature: this.featureName, step: step.text });
|
|
984
|
+
}
|
|
985
|
+
|
|
753
986
|
/**
|
|
754
987
|
* SG-W021 — dialog scope is a no-op on the Appium adapter.
|
|
755
988
|
*
|
|
@@ -779,6 +1012,7 @@ export class StepMapper {
|
|
|
779
1012
|
renderLocator: (resolved) => this.templateEngine.renderStep('locator', resolved),
|
|
780
1013
|
appProfile: this.appProfile,
|
|
781
1014
|
platform: this.platform,
|
|
1015
|
+
featurePlatform: this.featurePlatform,
|
|
782
1016
|
adapterName: this.adapterName,
|
|
783
1017
|
diagnostics: this.diagnostics,
|
|
784
1018
|
baseURL: this.baseURL,
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import Handlebars from 'handlebars';
|
|
2
|
+
import { escapeSingleQuoted } from './utils/escape-js-string';
|
|
2
3
|
import fs from 'fs';
|
|
3
4
|
import path from 'path';
|
|
4
|
-
import type { RenderImportsOptions } from './adapters/adapter-interface';
|
|
5
|
+
import type { RenderImportsOptions, HookRenderData } from './adapters/adapter-interface';
|
|
6
|
+
import { locatorIdentityKey } from './utils/dialog-scope-identity';
|
|
5
7
|
|
|
6
8
|
export class TemplateEngine {
|
|
7
9
|
private templates: Map<string, HandlebarsTemplateDelegate> = new Map();
|
|
@@ -28,16 +30,9 @@ export class TemplateEngine {
|
|
|
28
30
|
// the result in '…' (selector values, attribute values, scope), so a value containing a newline,
|
|
29
31
|
// backslash, or quote must be escaped or the generated spec is a syntax error (e.g. a Flutter
|
|
30
32
|
// composite content-desc like "Quick Convert\nSelect File\n…" used as an accessibility-id).
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return String(text)
|
|
35
|
-
.replace(/\\/g, '\\\\')
|
|
36
|
-
.replace(/'/g, "\\'")
|
|
37
|
-
.replace(/\n/g, '\\n')
|
|
38
|
-
.replace(/\r/g, '\\r')
|
|
39
|
-
.replace(/\t/g, '\\t');
|
|
40
|
-
});
|
|
33
|
+
// The rule itself lives in utils/escape-js-string so the pattern layer, which builds some
|
|
34
|
+
// locator strings by hand, cannot drift from what the templates do.
|
|
35
|
+
Handlebars.registerHelper('escapeQuotes', (text: string) => escapeSingleQuoted(text));
|
|
41
36
|
|
|
42
37
|
// Escape a raw string for embedding inside a REGEX LITERAL (/…/). Metacharacters first —
|
|
43
38
|
// that class doubles a backslash, so the control-char passes below cannot be mis-read as
|
|
@@ -152,6 +147,17 @@ export class TemplateEngine {
|
|
|
152
147
|
// element is dialog-rooted even if its role is portal-exempt (issue #465).
|
|
153
148
|
const explicitDialog = this.scope === 'dialog';
|
|
154
149
|
if (!this.inDialog && !explicitDialog) return 'page';
|
|
150
|
+
// The element being resolved IS the active scope's own dialog root (issue #692). Prefixing
|
|
151
|
+
// it with that root asks for a dialog nested inside itself —
|
|
152
|
+
// getByRole('dialog',{name:X}).getByRole('dialog',{name:X}) — which can never resolve, yet
|
|
153
|
+
// compiles clean and fails as a plain timeout reading "the dialog never opened". So the
|
|
154
|
+
// root is idempotent: re-asserting the dialog you are scoped inside means what it reads as.
|
|
155
|
+
// Skipped for a `with {{v}}` root, whose .filter({ hasText }) form is not expressible as
|
|
156
|
+
// the element's own locator — identity says nothing about it there.
|
|
157
|
+
if (this.inDialog && !this.dialogFilterText && this.dialogScopeKey &&
|
|
158
|
+
locatorIdentityKey(this) === this.dialogScopeKey) {
|
|
159
|
+
return 'page';
|
|
160
|
+
}
|
|
155
161
|
// Portal-rendered roles (Radix/MUI/Headless UI/antd render option/menu/tooltip
|
|
156
162
|
// layers onto document.body) live OUTSIDE the dialog's DOM subtree — wrapping
|
|
157
163
|
// them in the dialog root guarantees a timeout. List comes from the app profile
|
|
@@ -297,7 +303,7 @@ export class TemplateEngine {
|
|
|
297
303
|
}
|
|
298
304
|
|
|
299
305
|
renderImports(options?: RenderImportsOptions): string {
|
|
300
|
-
return this.render('imports', { runtimeData: options?.runtimeData, basePath: options?.basePath || '..', isParallel: options?.isParallel, needsCleanupImport: options?.needsCleanupImport, needsDb: options?.needsDb, needsApi: options?.needsApi, needsMock: options?.needsMock, urlAssertImports: options?.urlAssertImports, regexEscapeImport: options?.regexEscapeImport });
|
|
306
|
+
return this.render('imports', { runtimeData: options?.runtimeData, basePath: options?.basePath || '..', isParallel: options?.isParallel, needsCleanupImport: options?.needsCleanupImport, needsDb: options?.needsDb, needsApi: options?.needsApi, needsMock: options?.needsMock, urlAssertImports: options?.urlAssertImports, regexEscapeImport: options?.regexEscapeImport, checkedStateImports: options?.checkedStateImports });
|
|
301
307
|
}
|
|
302
308
|
|
|
303
309
|
renderTestFile(data: {
|
|
@@ -332,21 +338,15 @@ export class TemplateEngine {
|
|
|
332
338
|
return this.render('before-each', data);
|
|
333
339
|
}
|
|
334
340
|
|
|
335
|
-
renderBeforeAll(data: {
|
|
336
|
-
steps: Array<{ comment?: string; code: string }>;
|
|
337
|
-
}): string {
|
|
341
|
+
renderBeforeAll(data: HookRenderData): string {
|
|
338
342
|
return this.render('before-all', data);
|
|
339
343
|
}
|
|
340
344
|
|
|
341
|
-
renderAfterEach(data: {
|
|
342
|
-
steps: Array<{ comment?: string; code: string }>;
|
|
343
|
-
}): string {
|
|
345
|
+
renderAfterEach(data: HookRenderData): string {
|
|
344
346
|
return this.render('after-each', data);
|
|
345
347
|
}
|
|
346
348
|
|
|
347
|
-
renderAfterAll(data: {
|
|
348
|
-
steps: Array<{ comment?: string; code: string }>;
|
|
349
|
-
}): string {
|
|
349
|
+
renderAfterAll(data: HookRenderData): string {
|
|
350
350
|
return this.render('after-all', data);
|
|
351
351
|
}
|
|
352
352
|
|
|
@@ -29,6 +29,27 @@ export class DataResolver {
|
|
|
29
29
|
this.capturedVars.clear();
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
// Vars bound by a `@beforeAll` hook's `@api:`/`@query:` preconditions. The hook runs before every
|
|
33
|
+
// test of the feature, so its bindings are readable from EVERY scenario — they survive the
|
|
34
|
+
// per-scenario clearCaptured(). Stored by head (`login`) and matched against the ref's head
|
|
35
|
+
// (`login.body.token` → `login`), since a scenario may read any sub-path of the bound response.
|
|
36
|
+
private featureCaptured = new Set<string>();
|
|
37
|
+
|
|
38
|
+
/** Register the head of a variable bound by the feature's `@beforeAll` hook (feature-scoped). */
|
|
39
|
+
registerFeatureCaptured(head: string): void {
|
|
40
|
+
this.featureCaptured.add(head);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Clear feature-scoped captured vars — call at the start of each feature. */
|
|
44
|
+
clearFeatureCaptured(): void {
|
|
45
|
+
this.featureCaptured.clear();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Runtime-only ref (scenario-captured, or bound by the feature's `@beforeAll` hook)? */
|
|
49
|
+
private isCaptured(dataRef: string): boolean {
|
|
50
|
+
return this.capturedVars.has(dataRef) || this.featureCaptured.has(dataRef.split(/[.[]/)[0]);
|
|
51
|
+
}
|
|
52
|
+
|
|
32
53
|
constructor(testDataDir?: string, screenName?: string, runtimeMode: boolean = false) {
|
|
33
54
|
this.testDataDir = testDataDir || path.join(process.cwd(), 'qa', 'test-data');
|
|
34
55
|
this.screenName = screenName;
|
|
@@ -52,7 +73,7 @@ export class DataResolver {
|
|
|
52
73
|
resolveData(dataRef: string, featureName?: string): string {
|
|
53
74
|
// Captured vars exist only at runtime → emit a marker (→ testData.get) and
|
|
54
75
|
// skip YAML validation. Requires runtime mode (capture writes testData.set).
|
|
55
|
-
if (this.
|
|
76
|
+
if (this.isCaptured(dataRef)) {
|
|
56
77
|
return DataResolver.encodeMarker(dataRef);
|
|
57
78
|
}
|
|
58
79
|
|
|
@@ -75,7 +96,7 @@ export class DataResolver {
|
|
|
75
96
|
* anything said about it belongs in a warning, never a gate.
|
|
76
97
|
*/
|
|
77
98
|
peekBaseValue(dataRef: string, featureName?: string): string | undefined {
|
|
78
|
-
if (this.
|
|
99
|
+
if (this.isCaptured(dataRef)) return undefined;
|
|
79
100
|
try {
|
|
80
101
|
return this.resolveValue(dataRef, featureName);
|
|
81
102
|
} catch {
|