@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,6 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
const table = {{> locator}};
|
|
3
|
-
|
|
3
|
+
{{!-- Row universe: `rows:` on the table entry (what EVERY row matches — a <div> list has no
|
|
4
|
+
role=row, so the ARIA chain below is 0 elements and this assertion would be vacuous, issue #688)
|
|
5
|
+
wins; otherwise the historical ARIA chain, byte-identical. --}}
|
|
6
|
+
const rows = table{{#if rows}}.locator('{{escapeQuotes rows}}'){{else}}.locator('tbody').getByRole('row', { includeHidden: true }){{/if}}.filter({ visible: true });
|
|
4
7
|
{{#each assertions}}
|
|
5
8
|
{{{this}}}
|
|
6
9
|
{{/each~}}
|
|
@@ -1 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
{{!-- Row universe: `rows:` on the table entry (what EVERY row matches — a <div> list has no
|
|
2
|
+
role=row, so the ARIA chain below is 0 elements and this assertion would be vacuous, issue #688)
|
|
3
|
+
wins; otherwise the historical ARIA chain, byte-identical. --}}
|
|
4
|
+
await expect({{> locator}}{{#if rows}}.locator('{{escapeQuotes rows}}'){{else}}.locator('tbody').getByRole('row', { includeHidden: true }){{/if}}.filter({ visible: true })).toHaveCount({{expectedCount}});
|
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
{{~#if rowSelector}}
|
|
2
|
+
{{!-- Declared row LABEL = one positioned row. The toHaveCount(1) guard is what turns "I used the
|
|
3
|
+
row label as the row SHAPE" (div.job-row, issue #688) into a named error instead of a
|
|
4
|
+
strict-mode message pointing at the wrong line. --}}
|
|
2
5
|
{{~#if isGiven}}
|
|
3
|
-
const tableRow = {{> locator}}.locator('{{rowSelector}}');
|
|
6
|
+
const tableRow = {{> locator}}.locator('{{escapeQuotes rowSelector}}');
|
|
7
|
+
await expect(tableRow, 'row label "[{{escapeQuotes rowLabel}}]" must position exactly ONE row (its selectors.yaml entry is relative to the table) — if this selector describes what EVERY row looks like, declare it as `rows:` on the TABLE entry instead').toHaveCount(1);
|
|
4
8
|
await tableRow.filter({ hasText: '{{escapeQuotes filterValue}}' }).waitFor();
|
|
5
9
|
{{~else}}
|
|
6
|
-
const tableRow = {{> locator}}.locator('{{rowSelector}}');
|
|
10
|
+
const tableRow = {{> locator}}.locator('{{escapeQuotes rowSelector}}');
|
|
11
|
+
await expect(tableRow, 'row label "[{{escapeQuotes rowLabel}}]" must position exactly ONE row (its selectors.yaml entry is relative to the table) — if this selector describes what EVERY row looks like, declare it as `rows:` on the TABLE entry instead').toHaveCount(1);
|
|
7
12
|
await expect(tableRow).toContainText('{{escapeQuotes filterValue}}');
|
|
8
13
|
{{~/if}}
|
|
9
14
|
{{~else}}
|
|
15
|
+
{{!-- Row universe: `rows:` on the table entry (what EVERY row matches — a <div> list has no
|
|
16
|
+
role=row, so the ARIA chain below is 0 elements and this assertion would be vacuous, issue #688)
|
|
17
|
+
wins; otherwise the historical ARIA chain, byte-identical. --}}
|
|
10
18
|
{{~#if isGiven}}
|
|
11
|
-
const tableRow = {{> locator}}.getByRole('row', { includeHidden: true }).filter({ hasText: '{{escapeQuotes filterValue}}' }).filter({ visible: true });
|
|
19
|
+
const tableRow = {{> locator}}{{#if rows}}.locator('{{escapeQuotes rows}}'){{else}}.getByRole('row', { includeHidden: true }){{/if}}.filter({ hasText: '{{escapeQuotes filterValue}}' }).filter({ visible: true });
|
|
12
20
|
await tableRow.waitFor();
|
|
13
21
|
{{~else}}
|
|
14
|
-
const tableRow = {{> locator}}.getByRole('row', { includeHidden: true }).filter({ hasText: '{{escapeQuotes filterValue}}' }).filter({ visible: true });
|
|
22
|
+
const tableRow = {{> locator}}{{#if rows}}.locator('{{escapeQuotes rows}}'){{else}}.getByRole('row', { includeHidden: true }){{/if}}.filter({ hasText: '{{escapeQuotes filterValue}}' }).filter({ visible: true });
|
|
15
23
|
await expect(tableRow).toBeVisible();
|
|
16
24
|
{{~/if}}
|
|
17
25
|
{{~/if}}
|
|
@@ -1,5 +1,17 @@
|
|
|
1
|
+
{{~#if rowSelector}}
|
|
2
|
+
{{!-- Declared row LABEL = one positioned row (same contract as the exists twin). The row must not
|
|
3
|
+
hold the value. Block-scoped: this step does not enter row scope, so it must not redeclare
|
|
4
|
+
the function-level tableRow the exists step binds. --}}
|
|
5
|
+
{ const tableRow = {{> locator}}.locator('{{escapeQuotes rowSelector}}');
|
|
6
|
+
await expect(tableRow, 'row label "[{{escapeQuotes rowLabel}}]" must position exactly ONE row (its selectors.yaml entry is relative to the table) — if this selector describes what EVERY row looks like, declare it as `rows:` on the TABLE entry instead').toHaveCount(1);
|
|
7
|
+
await expect(tableRow).not.toContainText('{{escapeQuotes filterValue}}'); }
|
|
8
|
+
{{~else}}
|
|
9
|
+
{{!-- Row universe: `rows:` on the table entry (what EVERY row matches — a <div> list has no
|
|
10
|
+
role=row, so the ARIA chain below is 0 elements and this assertion would be vacuous, issue #688)
|
|
11
|
+
wins; otherwise the historical ARIA chain, byte-identical. --}}
|
|
1
12
|
{{~#if isGiven}}
|
|
2
|
-
await {{> locator}}.getByRole('row', { includeHidden: true }).filter({ hasText: '{{escapeQuotes filterValue}}' }).filter({ visible: true }).waitFor({ state: 'hidden' });
|
|
13
|
+
await {{> locator}}{{#if rows}}.locator('{{escapeQuotes rows}}'){{else}}.getByRole('row', { includeHidden: true }){{/if}}.filter({ hasText: '{{escapeQuotes filterValue}}' }).filter({ visible: true }).waitFor({ state: 'hidden' });
|
|
3
14
|
{{~else}}
|
|
4
|
-
await expect({{> locator}}.getByRole('row', { includeHidden: true }).filter({ hasText: '{{escapeQuotes filterValue}}' }).filter({ visible: true })).toHaveCount(0);
|
|
15
|
+
await expect({{> locator}}{{#if rows}}.locator('{{escapeQuotes rows}}'){{else}}.getByRole('row', { includeHidden: true }){{/if}}.filter({ hasText: '{{escapeQuotes filterValue}}' }).filter({ visible: true })).toHaveCount(0);
|
|
16
|
+
{{~/if}}
|
|
5
17
|
{{~/if}}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{{#if name}}
|
|
2
2
|
{{#if dataValue}}
|
|
3
|
-
await expect(page.getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }).filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{
|
|
3
|
+
await expect(page.getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }).filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{> locator-nth}}).toBeVisible();
|
|
4
4
|
{{else}}
|
|
5
|
-
await expect(page.getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }){{
|
|
5
|
+
await expect(page.getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }){{> locator-nth}}).toBeVisible();
|
|
6
6
|
{{/if}}
|
|
7
7
|
{{else}}
|
|
8
|
-
await expect(page.getByRole('{{role}}'{{#if exact}}, { exact: true }{{/if}}).filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{
|
|
8
|
+
await expect(page.getByRole('{{role}}'{{#if exact}}, { exact: true }{{/if}}).filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{> locator-nth}}).toBeVisible();
|
|
9
9
|
{{/if}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
await expect(page.getByText('{{escapeQuotes value}}'{{#if exact}}, { exact: true }{{/if}}){{
|
|
1
|
+
await expect(page.getByText('{{escapeQuotes value}}'{{#if exact}}, { exact: true }{{/if}}){{> locator-nth}}).toBeVisible();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
await expect(page.getByText('{{escapeQuotes selectorValue}}').filter({ hasText: /.*{{escapeRegex dataValue}}$/ }){{
|
|
1
|
+
await expect(page.getByText('{{escapeQuotes selectorValue}}').filter({ hasText: /.*{{escapeRegex dataValue}}$/ }){{> locator-nth}}).toBeVisible();
|
package/src/generators/test-generator/adapters/playwright/templates/steps/partials/checked-opts.hbs
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{{!-- The `checked:`/`selected:` signal from selectors.yaml, appended as the options argument of
|
|
2
|
+
setCheckedState/expectCheckedState (issue #730). One partial so all nine call sites carry it
|
|
3
|
+
identically, and so a site that declares nothing stays BYTE-IDENTICAL to before — the whole
|
|
4
|
+
argument disappears rather than becoming an empty object. Takes `cls`/`attr` so a caller can
|
|
5
|
+
pass a signal resolved from a DIFFERENT entry than the one it spread (table-action-in-row
|
|
6
|
+
spreads the TABLE but acts on the row's own control). A selector declares one or the other;
|
|
7
|
+
the resolver refuses both, so the two branches never collide here. --~}}
|
|
8
|
+
{{~#if cls}}, { checkedClass: '{{escapeQuotes cls}}' }{{else if attr}}, { checkedAttribute: '{{escapeQuotes attr}}' }{{/if~}}
|
package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
{{#if parentLocator}}{{parentLocator}}.{{#switch strategy~}}
|
|
2
|
-
{{~#case 'testid'}}getByTestId('{{value}}'){{/case~}}
|
|
2
|
+
{{~#case 'testid'}}getByTestId('{{escapeQuotes value}}'){{/case~}}
|
|
3
3
|
{{~#case 'role'}}{{#if name}}getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }){{else}}getByRole('{{role}}'{{#if exact}}, { exact: true }{{/if}}){{/if}}{{/case~}}
|
|
4
4
|
{{~#case 'placeholder'}}getByPlaceholder('{{escapeQuotes value}}'{{#if exact}}, { exact: true }{{/if}}){{/case~}}
|
|
5
5
|
{{~#case 'label'}}getByLabel('{{escapeQuotes value}}'{{#if exact}}, { exact: true }{{/if}}){{/case~}}
|
|
6
6
|
{{~#case 'text'}}getByText('{{escapeQuotes value}}'{{#if exact}}, { exact: true }{{/if}}){{/case~}}
|
|
7
|
-
{{~#case 'locator'}}locator('{{value}}'){{/case~}}
|
|
8
|
-
{{~#case 'id'}}locator('{{value}}'){{/case~}}
|
|
9
|
-
{{~#default}}locator('{{value}}'){{/default~}}
|
|
7
|
+
{{~#case 'locator'}}locator('{{escapeQuotes value}}'){{/case~}}
|
|
8
|
+
{{~#case 'id'}}locator('{{escapeQuotes value}}'){{/case~}}
|
|
9
|
+
{{~#default}}locator('{{escapeQuotes value}}'){{/default~}}
|
|
10
10
|
{{~/switch}}{{else~}}
|
|
11
11
|
{{#if scope}}{{#if (eq scope "dialog")}}{{pageRoot}}.{{else}}{{pageRoot}}.getByLabel('{{escapeQuotes scope}}').{{/if}}{{#switch strategy~}}
|
|
12
|
-
{{~#case 'testid'}}getByTestId('{{value}}'){{/case~}}
|
|
12
|
+
{{~#case 'testid'}}getByTestId('{{escapeQuotes value}}'){{/case~}}
|
|
13
13
|
{{~#case 'role'}}{{#if name}}getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }){{else}}getByRole('{{role}}'{{#if exact}}, { exact: true }{{/if}}){{/if}}{{/case~}}
|
|
14
14
|
{{~#case 'placeholder'}}getByPlaceholder('{{escapeQuotes value}}'{{#if exact}}, { exact: true }{{/if}}){{/case~}}
|
|
15
15
|
{{~#case 'label'}}getByLabel('{{escapeQuotes value}}'{{#if exact}}, { exact: true }{{/if}}){{/case~}}
|
|
16
16
|
{{~#case 'text'}}getByText('{{escapeQuotes value}}'{{#if exact}}, { exact: true }{{/if}}){{/case~}}
|
|
17
|
-
{{~#case 'locator'}}locator('{{value}}'){{/case~}}
|
|
18
|
-
{{~#case 'id'}}locator('{{value}}'){{/case~}}
|
|
19
|
-
{{~#default}}locator('{{value}}'){{/default~}}
|
|
17
|
+
{{~#case 'locator'}}locator('{{escapeQuotes value}}'){{/case~}}
|
|
18
|
+
{{~#case 'id'}}locator('{{escapeQuotes value}}'){{/case~}}
|
|
19
|
+
{{~#default}}locator('{{escapeQuotes value}}'){{/default~}}
|
|
20
20
|
{{~/switch}}{{else}}{{#switch strategy~}}
|
|
21
21
|
{{~#case 'testid'}}{{> locator-strategies/testid}}{{/case~}}
|
|
22
22
|
{{~#case 'role'}}{{> locator-strategies/role}}{{/case~}}
|
package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-nth.hbs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{{#if (gte nth 0)}}.nth({{nth}}){{/if}}
|
|
1
|
+
{{#if (gte nth 0)}}.nth({{nth}}){{else if (eq nth -1)}}.last(){{/if}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
page.locator('{{selector}}')
|
|
1
|
+
page.locator('{{escapeQuotes selector}}')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{{pageRoot}}.getByTestId('{{value}}')
|
|
1
|
+
{{pageRoot}}.getByTestId('{{escapeQuotes value}}')
|
package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
{{#if parentLocator}}{{parentLocator}}.{{#switch strategy~}}
|
|
2
|
-
{{~#case 'testid'}}getByTestId('{{value}}'){{/case~}}
|
|
2
|
+
{{~#case 'testid'}}getByTestId('{{escapeQuotes value}}'){{/case~}}
|
|
3
3
|
{{~#case 'role'}}{{#if name}}getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }){{else}}getByRole('{{role}}'{{#if exact}}, { exact: true }{{/if}}){{/if}}{{/case~}}
|
|
4
4
|
{{~#case 'placeholder'}}getByPlaceholder('{{escapeQuotes value}}'{{#if exact}}, { exact: true }{{/if}}){{/case~}}
|
|
5
5
|
{{~#case 'label'}}getByLabel('{{escapeQuotes value}}'{{#if exact}}, { exact: true }{{/if}}){{/case~}}
|
|
6
6
|
{{~#case 'text'}}getByText('{{escapeQuotes value}}'{{#if exact}}, { exact: true }{{/if}}){{/case~}}
|
|
7
|
-
{{~#case 'locator'}}locator('{{value}}'){{/case~}}
|
|
8
|
-
{{~#case 'id'}}locator('{{value}}'){{/case~}}
|
|
9
|
-
{{~#default}}locator('{{value}}'){{/default~}}
|
|
7
|
+
{{~#case 'locator'}}locator('{{escapeQuotes value}}'){{/case~}}
|
|
8
|
+
{{~#case 'id'}}locator('{{escapeQuotes value}}'){{/case~}}
|
|
9
|
+
{{~#default}}locator('{{escapeQuotes value}}'){{/default~}}
|
|
10
10
|
{{~/switch}}{{> locator-nth}}{{else~}}
|
|
11
11
|
{{#if scope}}{{#if (eq scope "dialog")}}{{pageRoot}}.{{else}}{{pageRoot}}.getByLabel('{{escapeQuotes scope}}').{{/if}}{{#switch strategy~}}
|
|
12
|
-
{{~#case 'testid'}}getByTestId('{{value}}'){{/case~}}
|
|
12
|
+
{{~#case 'testid'}}getByTestId('{{escapeQuotes value}}'){{/case~}}
|
|
13
13
|
{{~#case 'role'}}{{#if name}}getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }){{else}}getByRole('{{role}}'{{#if exact}}, { exact: true }{{/if}}){{/if}}{{/case~}}
|
|
14
14
|
{{~#case 'placeholder'}}getByPlaceholder('{{escapeQuotes value}}'{{#if exact}}, { exact: true }{{/if}}){{/case~}}
|
|
15
15
|
{{~#case 'label'}}getByLabel('{{escapeQuotes value}}'{{#if exact}}, { exact: true }{{/if}}){{/case~}}
|
|
16
16
|
{{~#case 'text'}}getByText('{{escapeQuotes value}}'{{#if exact}}, { exact: true }{{/if}}){{/case~}}
|
|
17
|
-
{{~#case 'locator'}}locator('{{value}}'){{/case~}}
|
|
18
|
-
{{~#case 'id'}}locator('{{value}}'){{/case~}}
|
|
19
|
-
{{~#default}}locator('{{value}}'){{/default~}}
|
|
17
|
+
{{~#case 'locator'}}locator('{{escapeQuotes value}}'){{/case~}}
|
|
18
|
+
{{~#case 'id'}}locator('{{escapeQuotes value}}'){{/case~}}
|
|
19
|
+
{{~#default}}locator('{{escapeQuotes value}}'){{/default~}}
|
|
20
20
|
{{~/switch}}{{> locator-nth}}{{else}}{{#switch strategy~}}
|
|
21
21
|
{{~#case 'testid'}}{{> locator-strategies/testid}}{{/case~}}
|
|
22
22
|
{{~#case 'role'}}{{> locator-strategies/role}}{{/case~}}
|
|
@@ -98,6 +98,16 @@ test.describe.serial('{{featureName}}', () => {
|
|
|
98
98
|
});
|
|
99
99
|
{{/if}}
|
|
100
100
|
|
|
101
|
+
{{#if beforeAll}}
|
|
102
|
+
{{beforeAll}}
|
|
103
|
+
|
|
104
|
+
{{/if}}
|
|
105
|
+
{{#if afterAll}}
|
|
106
|
+
// User @afterAll runs BEFORE the built-in close below (Playwright runs afterAll hooks in registration
|
|
107
|
+
// order), so the shared page is still open for its cleanup steps.
|
|
108
|
+
{{afterAll}}
|
|
109
|
+
|
|
110
|
+
{{/if}}
|
|
101
111
|
test.afterAll(async () => {
|
|
102
112
|
await page.close();
|
|
103
113
|
await context.close();
|
|
@@ -112,18 +122,10 @@ test.describe.serial('{{featureName}}', () => {
|
|
|
112
122
|
{{#if screenshotOnFailure}}
|
|
113
123
|
test.use({ screenshotOnFailure: true });
|
|
114
124
|
|
|
115
|
-
{{/if}}
|
|
116
|
-
{{#if beforeAll}}
|
|
117
|
-
{{beforeAll}}
|
|
118
|
-
|
|
119
125
|
{{/if}}
|
|
120
126
|
{{#if afterEach}}
|
|
121
127
|
{{afterEach}}
|
|
122
128
|
|
|
123
|
-
{{/if}}
|
|
124
|
-
{{#if afterAll}}
|
|
125
|
-
{{afterAll}}
|
|
126
|
-
|
|
127
129
|
{{/if}}
|
|
128
130
|
{{#if authGroups}}
|
|
129
131
|
{{#each authGroups}}
|
|
@@ -2,7 +2,7 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { ParsedFeature, ParsedScenario, ParsedStep } from '../gherkin-parser';
|
|
4
4
|
import { StepMapper } from './step-mapper';
|
|
5
|
-
import { TestGeneratorAdapter, adapterRegistry } from './adapters';
|
|
5
|
+
import { TestGeneratorAdapter, adapterRegistry, HookStep } from './adapters';
|
|
6
6
|
import { transformToRuntimeData } from './utils/runtime-data-transformer';
|
|
7
7
|
import { DiagnosticsCollector } from './diagnostics';
|
|
8
8
|
import { parseResetTier, DEFAULT_RESET_TIER } from './reset-tier';
|
|
@@ -12,6 +12,9 @@ import { readCapabilities } from '../../harness/capability';
|
|
|
12
12
|
import { parseManualComments } from '../../exporters/scenario-merger';
|
|
13
13
|
import { findSyntaxError } from './utils/spec-syntax-check';
|
|
14
14
|
|
|
15
|
+
/** One capability-injected precondition statement (`@api:`/`@query:` → `testData.bind(name, …)`). */
|
|
16
|
+
type CapabilityPrecondition = { comment?: string; code: string; boundVars?: string[] };
|
|
17
|
+
|
|
15
18
|
/**
|
|
16
19
|
* Filter base scenario steps for @extend: only keep Given→When steps.
|
|
17
20
|
* Skips Then and any And/But that follow Then (since And/But inherit
|
|
@@ -346,7 +349,7 @@ export class CodeGenerator {
|
|
|
346
349
|
// helper into every UI spec — including the many that touch no URL at all. Hence the body is
|
|
347
350
|
// rendered BEFORE the imports. Importing exactly the names used keeps the import honest; the
|
|
348
351
|
// helper file itself is synced only when something needs it (an api-only unit carries none).
|
|
349
|
-
const urlAssertImports = ['urlMatches', 'joinPath', 'expectPage'].filter((n) => testCode.includes(`${n}(`)).join(', ');
|
|
352
|
+
const urlAssertImports = ['urlMatches', 'joinPath', 'expectPage', 'waitForPage'].filter((n) => testCode.includes(`${n}(`)).join(', ');
|
|
350
353
|
if (urlAssertImports) this.syncGeneratedHelper(outputDir, 'url-assert.ts', 'specs-url-assert.ts');
|
|
351
354
|
|
|
352
355
|
// Same body-first rule for the runtime regex-escape helper: only a spec whose
|
|
@@ -354,7 +357,16 @@ export class CodeGenerator {
|
|
|
354
357
|
const regexEscapeImport = /\bescapeRegExp\(/.test(testCode);
|
|
355
358
|
if (regexEscapeImport) this.syncGeneratedHelper(outputDir, 'regex-escape.ts', 'specs-regex-escape.ts');
|
|
356
359
|
|
|
357
|
-
|
|
360
|
+
// Same body-first rule for the checked-state helper (#675): only a spec that actually compiled a
|
|
361
|
+
// `check`/`uncheck` step imports (and syncs) it, so the many specs that touch no toggle carry
|
|
362
|
+
// neither the import nor the file.
|
|
363
|
+
// Import exactly the names the body used, like urlAssertImports above — the action half and the
|
|
364
|
+
// assertion half of #675 are independent, and most specs need only one of them.
|
|
365
|
+
const checkedStateImports = ['setCheckedState', 'expectCheckedState']
|
|
366
|
+
.filter((n) => testCode.includes(`${n}(`)).join(', ');
|
|
367
|
+
if (checkedStateImports) this.syncGeneratedHelper(outputDir, 'checked-state.ts', 'specs-checked-state.ts');
|
|
368
|
+
|
|
369
|
+
const imports = this.adapter.renderImports({ runtimeData: this.options.runtimeData, basePath, needsCleanupImport, needsDb, needsApi, needsMock, urlAssertImports, regexEscapeImport, checkedStateImports });
|
|
358
370
|
|
|
359
371
|
// Combine and collapse any runs of 3+ newlines down to 2 (one blank line max)
|
|
360
372
|
const raw = `${imports}\n\n${testCode}`;
|
|
@@ -482,10 +494,10 @@ export class CodeGenerator {
|
|
|
482
494
|
* Each capability owns its annotation codegen via the SPI (`preconditionCodegen`); the compiler
|
|
483
495
|
* just composes + indents the returned statements (R4).
|
|
484
496
|
*/
|
|
485
|
-
private capabilityPreconditions(scenario: ParsedScenario):
|
|
497
|
+
private capabilityPreconditions(scenario: ParsedScenario): CapabilityPrecondition[] {
|
|
486
498
|
discoverAndRegisterCapabilities();
|
|
487
499
|
const tags = scenario.tags || [];
|
|
488
|
-
const out:
|
|
500
|
+
const out: CapabilityPrecondition[] = [];
|
|
489
501
|
for (const cap of capabilityRegistry.all()) {
|
|
490
502
|
if (!cap.preconditionCodegen) continue;
|
|
491
503
|
out.push(...cap.preconditionCodegen({ tags, screenName: this.queryScreenName, cwd: process.cwd() }));
|
|
@@ -527,6 +539,11 @@ export class CodeGenerator {
|
|
|
527
539
|
if (this.options.runtimeData) {
|
|
528
540
|
this.syncGeneratedHelper(outputDir, 'test-data.ts', 'specs-test-data.ts');
|
|
529
541
|
}
|
|
542
|
+
// The Playwright reporter that keeps machine paths out of the committed JSON result — refreshed
|
|
543
|
+
// like base.ts so an existing project picks it up on its next generate (mobile has no Playwright).
|
|
544
|
+
if (this.adapter.frameworkName !== 'appium') {
|
|
545
|
+
this.syncGeneratedHelper(outputDir, 'report-path-sanitize-reporter.ts', 'specs-report-path-sanitize-reporter.ts');
|
|
546
|
+
}
|
|
530
547
|
}
|
|
531
548
|
|
|
532
549
|
private async generateTestCode(feature: ParsedFeature): Promise<string> {
|
|
@@ -562,7 +579,11 @@ export class CodeGenerator {
|
|
|
562
579
|
this.stepMapper.setFlowMode(isFlowFeature);
|
|
563
580
|
|
|
564
581
|
// Set feature context for data resolution and navigation
|
|
565
|
-
|
|
582
|
+
// The feature's declared OS rides along so the pattern layer can refuse an Android-only step
|
|
583
|
+
// in a feature that says it runs on iOS (SG-E025). Read from the tag, not from the adapter.
|
|
584
|
+
const declaredOs = (feature.tags || [])
|
|
585
|
+
.find((tg) => tg.startsWith('@platform:'))?.split(':')[1] as 'mobile' | 'android' | 'ios' | undefined;
|
|
586
|
+
this.stepMapper.setFeatureContext(featureName, feature.path, declaredOs);
|
|
566
587
|
|
|
567
588
|
// Warn if multiple auth tags at feature level
|
|
568
589
|
if (feature.tags && feature.tags.some(tag => tag.startsWith('@auth:'))) {
|
|
@@ -596,6 +617,26 @@ export class CodeGenerator {
|
|
|
596
617
|
// never skip-cascades). `@serial` opts into the shared-page serial suite for a stateful sequence.
|
|
597
618
|
const isParallel = !(feature.tags || []).includes('@serial');
|
|
598
619
|
|
|
620
|
+
// A `@beforeAll` hook runs before every test of the describe, so the `{{name}}` its `@api:`/`@query:`
|
|
621
|
+
// preconditions bind are readable from EVERY scenario of the feature. Register the bound heads
|
|
622
|
+
// before the scenarios map: the per-scenario captured set is cleared at each scenario, and without
|
|
623
|
+
// this a scenario's `{{login.body.token}}` compiles to a dead literal. `@afterEach`/`@afterAll` run
|
|
624
|
+
// after the tests — their bindings stay hook-local. The set is per feature: clear it FIRST, before
|
|
625
|
+
// the Background maps, or the previous feature's bindings would leak into this Background.
|
|
626
|
+
// Whether the Background itself may read them depends on where it runs: per-scenario (parallel)
|
|
627
|
+
// Background is a beforeEach and runs after the user beforeAll → register before it; @serial
|
|
628
|
+
// Background is inlined into the built-in beforeAll that runs BEFORE the user hook → register
|
|
629
|
+
// only after it has mapped, so a serial Background ref stays an unresolved literal, as it should.
|
|
630
|
+
this.stepMapper.clearFeatureCaptured();
|
|
631
|
+
const registerBeforeAllBindings = (): void => {
|
|
632
|
+
const beforeAllHook = hookScenarios.get('beforeAll');
|
|
633
|
+
if (!beforeAllHook) return;
|
|
634
|
+
for (const p of this.capabilityPreconditions(beforeAllHook)) {
|
|
635
|
+
for (const head of p.boundVars || []) this.stepMapper.registerFeatureCaptured(head);
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
if (isParallel) registerBeforeAllBindings();
|
|
639
|
+
|
|
599
640
|
// Generate background if exists
|
|
600
641
|
let background: string | undefined;
|
|
601
642
|
let backgroundSteps: Array<{ comment?: string; code: string }> | undefined;
|
|
@@ -638,6 +679,8 @@ export class CodeGenerator {
|
|
|
638
679
|
}
|
|
639
680
|
}
|
|
640
681
|
|
|
682
|
+
if (!isParallel) registerBeforeAllBindings();
|
|
683
|
+
|
|
641
684
|
// Generate hook blocks
|
|
642
685
|
const beforeAll = hookScenarios.has('beforeAll')
|
|
643
686
|
? await this.generateHook(hookScenarios.get('beforeAll')!, 'beforeAll')
|
|
@@ -854,6 +897,21 @@ export class CodeGenerator {
|
|
|
854
897
|
}
|
|
855
898
|
if (iosPart) iosBundleId = iosPart;
|
|
856
899
|
}
|
|
900
|
+
// SG-W029 — the appium adapter reached codegen with NO usable app package. Both ways in are
|
|
901
|
+
// silent: a feature with no `Path:` line at all, and a web-style route `Path: /applied-jobs`
|
|
902
|
+
// that the guard above deliberately rejects. Before this code that combination quietly emitted
|
|
903
|
+
// Mocha's `before()` — once per FILE — so Background ran once and every scenario shared one
|
|
904
|
+
// continuous app session, leaking tab/scroll/login state forward; the `@reset:` tier ladder was
|
|
905
|
+
// dropped along with it, making the tag inert. The generated spec now falls back to the session
|
|
906
|
+
// capability instead, but the feature file is still missing something it should declare, and
|
|
907
|
+
// the run throws if wdio.conf cannot supply it either — so it is reported, not assumed.
|
|
908
|
+
// A warning rather than an error on SG-W004's reasoning: the run usually still works (wdio.conf
|
|
909
|
+
// carries the id), and `--strict` already turns the channel non-zero for anyone who wants a gate.
|
|
910
|
+
if (this.adapter.frameworkName === 'appium' && !appPackage && !iosBundleId) {
|
|
911
|
+
this.diagnostics.add('SG-W029',
|
|
912
|
+
`this mobile feature declares no app-package \`Path:\`${feature.path ? ` (\`${feature.path}\` is a web route, not a package)` : ''}, so the compiler cannot name the app to reset between scenarios. The spec now resolves the app id from the session capability (APP_PACKAGE / IOS_BUNDLE_ID in wdio.conf) and THROWS at run time if that is absent too. Declare it on the feature instead: \`Path: com.example.app/.MainActivity\` (or \`<androidPkg>/<activity> | <iosBundleId>\` for a dual-id feature).`,
|
|
913
|
+
{ feature: feature.name });
|
|
914
|
+
}
|
|
857
915
|
|
|
858
916
|
// Use adapter to render the complete test file structure
|
|
859
917
|
return this.adapter.renderTestFile({
|
|
@@ -916,23 +974,66 @@ export class CodeGenerator {
|
|
|
916
974
|
});
|
|
917
975
|
}
|
|
918
976
|
|
|
977
|
+
/**
|
|
978
|
+
* Hook block (`@beforeAll` / `@afterEach` / `@afterAll`). Shares prepare/finalize with ordinary
|
|
979
|
+
* scenarios, so an `@api:` / `@query:` precondition on the hook runs INSIDE it (bind {{name}}) and
|
|
980
|
+
* the hook's own `{{name.*}}` refs resolve at runtime — a hook used to map only its steps, so
|
|
981
|
+
* `@beforeAll @api:login` compiled to a beforeAll with no api.call and a testData.get() of nothing.
|
|
982
|
+
* Not applied to hooks: `@cases` (a hook has no dataset rows) and `@extend` (no base scenario) —
|
|
983
|
+
* warned and ignored; `@auth:` grouping and the mock epilogue are scenario-only. `@concurrent:N`
|
|
984
|
+
* IS valid: the api capability reads it off the same tags.
|
|
985
|
+
*
|
|
986
|
+
* Failure collection: a failing hook skips every test in its describe, so one run must report
|
|
987
|
+
* EVERY failed check, not just the first. The hook template wraps each mapped step in try/catch:
|
|
988
|
+
* an assertion step (effective keyword Then) records its failure and lets the next step run; an
|
|
989
|
+
* action step (Given/When) records it and fails the hook right there, since the steps after it
|
|
990
|
+
* would run on state the failed action never produced. The block ends with a guard that fails once
|
|
991
|
+
* with the full list. `expect.soft` is not an option here — a soft failure marks the owning test,
|
|
992
|
+
* and a hook has none.
|
|
993
|
+
*/
|
|
919
994
|
private async generateHook(
|
|
920
995
|
scenario: ParsedScenario,
|
|
921
996
|
hookType: 'beforeAll' | 'afterEach' | 'afterAll'
|
|
922
997
|
): Promise<string> {
|
|
923
|
-
const
|
|
924
|
-
for (const
|
|
998
|
+
const unsupported = scenario.tags.filter((t) => t.startsWith('@cases:') || t.startsWith('@extend:'));
|
|
999
|
+
for (const tag of unsupported) {
|
|
1000
|
+
console.warn(`⚠ @${hookType} hook "${scenario.name}": ${tag} is not supported on hooks — ignored`);
|
|
1001
|
+
}
|
|
1002
|
+
const hookScenario: ParsedScenario = {
|
|
1003
|
+
...scenario,
|
|
1004
|
+
tags: scenario.tags.filter((t) => !unsupported.includes(t)),
|
|
1005
|
+
extendsName: undefined,
|
|
1006
|
+
};
|
|
1007
|
+
const { preconditions } = this.prepareScenarioBody(hookScenario, hookScenario.steps);
|
|
1008
|
+
|
|
1009
|
+
const steps: HookStep[] = [];
|
|
1010
|
+
let effectiveKeyword = 'Given'; // And/But inherit the last Given/When/Then
|
|
1011
|
+
for (const step of hookScenario.steps) {
|
|
1012
|
+
const keyword = step.keyword.trim();
|
|
1013
|
+
if (keyword === 'Given' || keyword === 'When' || keyword === 'Then') effectiveKeyword = keyword;
|
|
925
1014
|
const mapped = await Promise.resolve(this.stepMapper.mapStep(step));
|
|
1015
|
+
// A step whose code declares a top-level binding (`const frame = …`, `let tableRow = …`) is read
|
|
1016
|
+
// by the steps AFTER it — a try/catch would scope the binding away from them. Such a step runs
|
|
1017
|
+
// unwrapped and throws on failure exactly as before.
|
|
1018
|
+
if (/^(const|let|var)\s/m.test(mapped.code)) {
|
|
1019
|
+
steps.push({ comment: mapped.comment, code: this.indentCode(mapped.code, 4) });
|
|
1020
|
+
continue;
|
|
1021
|
+
}
|
|
926
1022
|
steps.push({
|
|
927
1023
|
comment: mapped.comment,
|
|
928
|
-
code: this.indentCode(mapped.code,
|
|
1024
|
+
code: this.indentCode(mapped.code, 6),
|
|
1025
|
+
wrap: true,
|
|
1026
|
+
stopOnFailure: effectiveKeyword !== 'Then',
|
|
1027
|
+
label: JSON.stringify(mapped.comment || step.text || 'hook step'),
|
|
929
1028
|
});
|
|
930
1029
|
}
|
|
1030
|
+
this.finalizeScenarioBody(steps, preconditions);
|
|
1031
|
+
const collect = steps.some((s) => s.wrap);
|
|
931
1032
|
|
|
932
1033
|
const renderMap = {
|
|
933
|
-
beforeAll: () => this.adapter.renderBeforeAll({ steps }),
|
|
934
|
-
afterEach: () => this.adapter.renderAfterEach({ steps }),
|
|
935
|
-
afterAll: () => this.adapter.renderAfterAll({ steps }),
|
|
1034
|
+
beforeAll: () => this.adapter.renderBeforeAll({ steps, collect }),
|
|
1035
|
+
afterEach: () => this.adapter.renderAfterEach({ steps, collect }),
|
|
1036
|
+
afterAll: () => this.adapter.renderAfterAll({ steps, collect }),
|
|
936
1037
|
};
|
|
937
1038
|
return renderMap[hookType]();
|
|
938
1039
|
}
|
|
@@ -952,6 +1053,63 @@ export class CodeGenerator {
|
|
|
952
1053
|
].join('\n');
|
|
953
1054
|
}
|
|
954
1055
|
|
|
1056
|
+
/**
|
|
1057
|
+
* Scenario-body preparation shared by ordinary scenarios and hook blocks — runs BEFORE the steps
|
|
1058
|
+
* are mapped (the step mapper reads what it sets): scenario context, `@cases` row-column capture,
|
|
1059
|
+
* and the capability preconditions (`@api:<name>` / `@query:<name>` → bind {{name}}) whose bound
|
|
1060
|
+
* `{{name.*}}` refs exist only at runtime, so they are registered as captured and compile to a
|
|
1061
|
+
* runtime get() instead of a compile-time YAML lookup that would fail. Pair with
|
|
1062
|
+
* finalizeScenarioBody(), which prepends the preconditions once the steps are mapped.
|
|
1063
|
+
*/
|
|
1064
|
+
private prepareScenarioBody(
|
|
1065
|
+
scenario: ParsedScenario,
|
|
1066
|
+
stepsToMap: ParsedStep[],
|
|
1067
|
+
): { preconditions: CapabilityPrecondition[]; casesDataset?: string } {
|
|
1068
|
+
this.stepMapper.setScenarioContext(stepsToMap);
|
|
1069
|
+
|
|
1070
|
+
// Data-driven (@cases): the scenario's {{col}} refs are dataset *row columns* that
|
|
1071
|
+
// exist only at runtime — register them as captured so they resolve to a runtime
|
|
1072
|
+
// get() (→ rowData.get) instead of compile-time YAML lookup.
|
|
1073
|
+
const casesTag = scenario.tags.find((t) => t.startsWith('@cases:'));
|
|
1074
|
+
const casesDataset = casesTag ? casesTag.slice('@cases:'.length).trim() : undefined;
|
|
1075
|
+
if (casesDataset) {
|
|
1076
|
+
const refs = new Set<string>();
|
|
1077
|
+
for (const st of stepsToMap) {
|
|
1078
|
+
for (const m of (st.text || '').matchAll(/\{\{\s*([\w.]+)\s*\}\}/g)) refs.add(m[1]);
|
|
1079
|
+
}
|
|
1080
|
+
for (const r of refs) this.stepMapper.registerCaptured(r);
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
const preconditions = this.capabilityPreconditions(scenario);
|
|
1084
|
+
const boundVars = preconditions.flatMap((p) => p.boundVars || []);
|
|
1085
|
+
if (boundVars.length) {
|
|
1086
|
+
for (const st of stepsToMap) {
|
|
1087
|
+
for (const mt of (st.text || '').matchAll(/\{\{\s*([^}]+?)\s*\}\}/g)) {
|
|
1088
|
+
const head = mt[1].split(/[.[]/)[0];
|
|
1089
|
+
if (boundVars.includes(head)) this.stepMapper.registerCaptured(mt[1]);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
return { preconditions, casesDataset };
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
/**
|
|
1097
|
+
* Capability preconditions run BEFORE the body's own steps — prepend them (indented) once the
|
|
1098
|
+
* steps are mapped. Second half of the prepareScenarioBody() pair.
|
|
1099
|
+
*/
|
|
1100
|
+
private finalizeScenarioBody(steps: Array<{ comment?: string; code: string }>, preconditions: CapabilityPrecondition[]): void {
|
|
1101
|
+
if (preconditions.length) {
|
|
1102
|
+
steps.unshift(...preconditions.map((p) => ({ comment: p.comment, code: this.indentCode(p.code, 4) })));
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
/**
|
|
1107
|
+
* Step-boundary trace (#673): scenario name → the ordered step-comment texts emitted for it.
|
|
1108
|
+
* Read by script-check to attribute emitted code to steps structurally; see the populating
|
|
1109
|
+
* site in generateScenario for why comment adjacency cannot do the job.
|
|
1110
|
+
*/
|
|
1111
|
+
readonly stepTrace = new Map<string, string[]>();
|
|
1112
|
+
|
|
955
1113
|
private async generateScenario(
|
|
956
1114
|
scenario: ParsedScenario,
|
|
957
1115
|
hasBackground: boolean,
|
|
@@ -983,35 +1141,7 @@ export class CodeGenerator {
|
|
|
983
1141
|
warnMultipleAuthTags(scenario.tags, `scenario '${scenario.name}'`);
|
|
984
1142
|
}
|
|
985
1143
|
|
|
986
|
-
|
|
987
|
-
this.stepMapper.setScenarioContext(stepsToMap);
|
|
988
|
-
|
|
989
|
-
// Data-driven (@cases): the scenario's {{col}} refs are dataset *row columns* that
|
|
990
|
-
// exist only at runtime — register them as captured so they resolve to a runtime
|
|
991
|
-
// get() (→ rowData.get) instead of compile-time YAML lookup.
|
|
992
|
-
const casesTag = scenario.tags.find((t) => t.startsWith('@cases:'));
|
|
993
|
-
const casesDataset = casesTag ? casesTag.slice('@cases:'.length).trim() : undefined;
|
|
994
|
-
if (casesDataset) {
|
|
995
|
-
const refs = new Set<string>();
|
|
996
|
-
for (const st of stepsToMap) {
|
|
997
|
-
for (const m of (st.text || '').matchAll(/\{\{\s*([\w.]+)\s*\}\}/g)) refs.add(m[1]);
|
|
998
|
-
}
|
|
999
|
-
for (const r of refs) this.stepMapper.registerCaptured(r);
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
// Capability preconditions (db `@query:<name>` → bind {{name}}) are owned by the capability via
|
|
1003
|
-
// the SPI. Their bound `{{name.*}}` vars exist only at runtime → register them as captured so
|
|
1004
|
-
// they resolve to a runtime get() instead of a compile-time YAML lookup that would fail.
|
|
1005
|
-
const preconditions = this.capabilityPreconditions(scenario);
|
|
1006
|
-
const boundVars = preconditions.flatMap((p) => p.boundVars || []);
|
|
1007
|
-
if (boundVars.length) {
|
|
1008
|
-
for (const st of stepsToMap) {
|
|
1009
|
-
for (const mt of (st.text || '').matchAll(/\{\{\s*([^}]+?)\s*\}\}/g)) {
|
|
1010
|
-
const head = mt[1].split(/[.[]/)[0];
|
|
1011
|
-
if (boundVars.includes(head)) this.stepMapper.registerCaptured(mt[1]);
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1144
|
+
const { preconditions, casesDataset } = this.prepareScenarioBody(scenario, stepsToMap);
|
|
1015
1145
|
|
|
1016
1146
|
const steps: Array<{ comment?: string; code: string }> = [];
|
|
1017
1147
|
|
|
@@ -1042,11 +1172,7 @@ export class CodeGenerator {
|
|
|
1042
1172
|
}
|
|
1043
1173
|
}
|
|
1044
1174
|
|
|
1045
|
-
|
|
1046
|
-
// above) run BEFORE the scenario's own steps — prepend them, indenting the capability statements.
|
|
1047
|
-
if (preconditions.length) {
|
|
1048
|
-
steps.unshift(...preconditions.map((p) => ({ comment: p.comment, code: this.indentCode(p.code, 4) })));
|
|
1049
|
-
}
|
|
1175
|
+
this.finalizeScenarioBody(steps, preconditions);
|
|
1050
1176
|
|
|
1051
1177
|
// Mock scenario epilogue. (a) EVERY path: the fail-on-unmocked verdict — the guard
|
|
1052
1178
|
// records+aborts unmatched requests (it must not throw inside a route handler:
|
|
@@ -1076,6 +1202,15 @@ export class CodeGenerator {
|
|
|
1076
1202
|
const tags = formatPassThroughTags(extractPassThroughTags(scenario.tags, featureTags), this.adapter.frameworkName);
|
|
1077
1203
|
|
|
1078
1204
|
// Use adapter to render scenario
|
|
1205
|
+
// #673 — record which `//` lines of this scenario are STEP markers, in order. A generated
|
|
1206
|
+
// spec has no textual way to tell a step comment from a template body comment (both land at
|
|
1207
|
+
// the same indent), so script-check attributed code to steps by comment ADJACENCY: any two
|
|
1208
|
+
// adjacent comments gave the first one an empty region → a false hollow BYPASS. Three shipped
|
|
1209
|
+
// templates emit consecutive `//` lines (user-login-todo 6, clear-database 4,
|
|
1210
|
+
// alert-text-assertion 2), as do `// [from @steps:…]` headers, so the check fired on sungen's
|
|
1211
|
+
// own output. The trace makes the attribution structural instead of positional.
|
|
1212
|
+
this.stepTrace.set(scenario.name, steps.map((s) => s.comment).filter((c): c is string => !!c));
|
|
1213
|
+
|
|
1079
1214
|
const rendered = this.adapter.renderScenario({
|
|
1080
1215
|
scenarioName: scenario.name,
|
|
1081
1216
|
steps,
|