@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,14 @@
|
|
|
1
1
|
{{#if platform}}
|
|
2
2
|
// sungen:platform={{platform}}
|
|
3
3
|
{{/if}}
|
|
4
|
+
{{#if screenshotOnFailure}}
|
|
5
|
+
// @screenshot:on-failure — used to be web-only and silently dropped on mobile.
|
|
6
|
+
// wdio.conf.ts's afterTest hook already captures screenshot + page source + device log for EVERY
|
|
7
|
+
// mobile failure, so the tag is not what turns evidence on. What it declares is that this feature's
|
|
8
|
+
// evidence is ESSENTIAL: it survives SUNGEN_MOBILE_EVIDENCE=off, which otherwise suppresses capture
|
|
9
|
+
// for the run. Set at module load, so it covers every scenario in this spec.
|
|
10
|
+
(globalThis as Record<string, unknown>).__sungenForceEvidence = true;
|
|
11
|
+
{{/if}}
|
|
4
12
|
{{#if runtimeData}}
|
|
5
13
|
|
|
6
14
|
const testData = TestDataLoader.load('{{screenName}}', '{{featureFileName}}');
|
|
@@ -202,6 +210,36 @@ async function __assertCheckedState(el: WebdriverIO.Element, expected: boolean,
|
|
|
202
210
|
}
|
|
203
211
|
throw new Error(`Expect ${label} to be ${want} — read [${signals.map(s => `${s.source} → ${s.value}`).join(', ')}]`);
|
|
204
212
|
}
|
|
213
|
+
|
|
214
|
+
// Entering a "frame" on a native driver means switching into the WebView CONTEXT — there is no
|
|
215
|
+
// iframe to locate. That context does not exist until the WebView runtime has created it, and the
|
|
216
|
+
// tap that kicks off the navigation returns long before that, so a ONE-SHOT getContexts() reads a
|
|
217
|
+
// list that holds only NATIVE_APP. Skipping the switch on that reading is the worst possible
|
|
218
|
+
// answer: every later step then searches the native tree, where a hybrid WebView is a SINGLE
|
|
219
|
+
// opaque leaf with no children — the target can never appear there, so no downstream wait can
|
|
220
|
+
// recover it and the run dies with a misleading "element still not displayed" (#705). Poll for the
|
|
221
|
+
// context instead, and throw when it never arrives: a step that reads "switch to X frame" must
|
|
222
|
+
// never quietly not switch.
|
|
223
|
+
async function __enterWebContext(label: string): Promise<void> {
|
|
224
|
+
const budget = Number((driver as any)?.options?.waitforTimeout) || 30000;
|
|
225
|
+
const deadline = Date.now() + budget;
|
|
226
|
+
let seen: any[] = [];
|
|
227
|
+
for (;;) {
|
|
228
|
+
seen = await driver.getContexts();
|
|
229
|
+
const web = seen.find(c => /WEBVIEW|CHROMIUM/i.test(typeof c === 'string' ? c : c.id));
|
|
230
|
+
if (web) { await driver.switchContext(typeof web === 'string' ? web : web.id); return; }
|
|
231
|
+
if (Date.now() >= deadline) break;
|
|
232
|
+
await driver.pause(500);
|
|
233
|
+
}
|
|
234
|
+
throw new Error(
|
|
235
|
+
`[sungen] "switch to ${label} frame" — no WEBVIEW/CHROMIUM context appeared within ${budget}ms ` +
|
|
236
|
+
`(contexts seen: ${JSON.stringify(seen)}). The driver is still in NATIVE_APP, where a hybrid WebView ` +
|
|
237
|
+
`is one opaque node with no children, so every later step would fail as "element not displayed" ` +
|
|
238
|
+
`while the real cause is this switch. If the transition is genuinely slower than ${budget}ms, raise ` +
|
|
239
|
+
`waitforTimeout in wdio.conf.ts; if the screen has no WebView, drop the frame step and assert the ` +
|
|
240
|
+
`native markers instead.`,
|
|
241
|
+
);
|
|
242
|
+
}
|
|
205
243
|
{{#if singleAuthRole}}
|
|
206
244
|
import * as fs from 'node:fs';
|
|
207
245
|
import * as path from 'node:path';
|
|
@@ -332,23 +370,25 @@ describe('{{featureName}}', () => {
|
|
|
332
370
|
// @auth:{{singleAuthRole}} — authenticate ONCE before any scenario; the session persists across
|
|
333
371
|
// the per-scenario relaunch below (noReset:true). Throws (blocks the spec) if login can't be done.
|
|
334
372
|
before(async () => {
|
|
335
|
-
{{#if appPackage}}
|
|
336
373
|
// Relaunch to a known launch screen first — the session may attach to the app in any state.
|
|
337
374
|
// Platform-aware id (NOT the Android literal): on iOS a @platform:mobile feature's app is the
|
|
338
375
|
// dual-id bundle id — a literal appPackage here would throw and block the whole spec.
|
|
339
|
-
// (__resolveAppId is a hoisted function declaration in this describe scope.
|
|
376
|
+
// (__resolveAppId is a hoisted function declaration in this describe scope. It is no longer
|
|
377
|
+
// gated on a `Path:`-derived package (#669): it resolves the session capability too, and throws
|
|
378
|
+
// a named error when nothing supplies an id at all.)
|
|
340
379
|
const __AUTH_APP__ = __resolveAppId();
|
|
341
380
|
try { await driver.terminateApp(__AUTH_APP__); } catch { /* not running */ }
|
|
342
381
|
await driver.activateApp(__AUTH_APP__);
|
|
343
|
-
{{/if}}
|
|
344
382
|
await __ensureAuth('{{singleAuthRole}}');
|
|
345
383
|
});
|
|
346
384
|
|
|
347
385
|
{{/if}}
|
|
348
|
-
{{#if appPackage}}
|
|
349
386
|
// Multi-app: this feature's app (from the `Path:` line). Reset to its launch screen before each
|
|
350
387
|
// scenario so one `wdio run` can host screens from different apps — each spec launches its own.
|
|
351
388
|
// The `Path:` holds the ANDROID package (or, for an @platform:ios feature, the iOS bundle id).
|
|
389
|
+
// EMPTY when the feature declared no usable `Path:` (#669) — the session capability then supplies
|
|
390
|
+
// the id (see __resolveAppId). Emitted unconditionally so that a feature without a `Path:` still
|
|
391
|
+
// gets per-scenario reset + Background instead of silently dropping to a once-per-file before().
|
|
352
392
|
const __APP_PKG_DEFAULT__ = '{{appPackage}}';
|
|
353
393
|
{{#if iosBundleId}}
|
|
354
394
|
// Per-feature iOS bundle id from the dual-id `Path: <androidPkg>/<activity> | <iosBundleId>` —
|
|
@@ -358,13 +398,26 @@ describe('{{featureName}}', () => {
|
|
|
358
398
|
{{/if}}
|
|
359
399
|
// Platform-aware app id: an @platform:mobile spec runs on BOTH OSes. On iOS prefer the feature's own
|
|
360
400
|
// bundle id (dual-id Path), then the session cap (IOS_BUNDLE_ID in wdio.conf), then the Path value;
|
|
361
|
-
// on Android (or any non-iOS session)
|
|
401
|
+
// on Android (or any non-iOS session) prefer the Path package, then the session cap.
|
|
402
|
+
// The session-capability fallback (#669) is what lets a feature with no app-package `Path:` keep a
|
|
403
|
+
// real per-scenario reset: wdio.conf sets `appium:appPackage` for the run, so the id the compiler
|
|
404
|
+
// could not read off the feature file is almost always present here. Throwing when NOTHING supplies
|
|
405
|
+
// it is deliberate — the alternative was the silent once-per-file before() this issue is about,
|
|
406
|
+
// where scenarios leak tab/scroll/login state into each other and the suite lies about why it
|
|
407
|
+
// failed. SG-W029 already named this at compile time, so this throw is never the first warning.
|
|
362
408
|
function __resolveAppId(): string {
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
409
|
+
const caps = (typeof driver !== 'undefined' ? (driver.requestedCapabilities || {}) : {}) as Record<string, string>;
|
|
410
|
+
const resolved = (typeof driver !== 'undefined' && driver.isIOS)
|
|
411
|
+
? ({{#if iosBundleId}}__IOS_BUNDLE__ || {{/if}}caps['appium:bundleId'] || caps['bundleId'] || __APP_PKG_DEFAULT__ || caps['appium:appPackage'] || caps['appPackage'])
|
|
412
|
+
: (__APP_PKG_DEFAULT__ || caps['appium:appPackage'] || caps['appPackage'] || caps['appium:bundleId'] || caps['bundleId']);
|
|
413
|
+
if (!resolved) {
|
|
414
|
+
throw new Error(
|
|
415
|
+
`[sungen] cannot resolve this app's id, so no per-scenario reset is possible (SG-W029). ` +
|
|
416
|
+
`Add an app-package \`Path:\` to the feature — \`Path: com.example.app/.MainActivity\` — ` +
|
|
417
|
+
`or set APP_PACKAGE (Android) / IOS_BUNDLE_ID (iOS) so wdio.conf puts it on the session.`,
|
|
418
|
+
);
|
|
366
419
|
}
|
|
367
|
-
return
|
|
420
|
+
return resolved;
|
|
368
421
|
}
|
|
369
422
|
// ── Reset tier ────────────────────────────────────────────────────────────────────────────────
|
|
370
423
|
// How much state each scenario starts from (`@reset:` on the Feature; default `soft`):
|
|
@@ -575,20 +628,6 @@ describe('{{featureName}}', () => {
|
|
|
575
628
|
{{/if}}
|
|
576
629
|
});
|
|
577
630
|
|
|
578
|
-
{{/if}}
|
|
579
|
-
{{#unless appPackage}}
|
|
580
|
-
{{#if backgroundSteps}}
|
|
581
|
-
before(async () => {
|
|
582
|
-
{{#each backgroundSteps}}
|
|
583
|
-
{{#if comment}}
|
|
584
|
-
// {{comment}}
|
|
585
|
-
{{/if}}
|
|
586
|
-
{{code}}
|
|
587
|
-
{{/each}}
|
|
588
|
-
});
|
|
589
|
-
|
|
590
|
-
{{/if}}
|
|
591
|
-
{{/unless}}
|
|
592
631
|
{{#if beforeAll}}
|
|
593
632
|
{{beforeAll}}
|
|
594
633
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { TestGeneratorAdapter, TestFileData, ScenarioData, StepTemplateData, LocatorExpression } from './adapter-interface';
|
|
1
|
+
export { TestGeneratorAdapter, TestFileData, ScenarioData, StepTemplateData, LocatorExpression, HookStep, HookRenderData } from './adapter-interface';
|
|
2
2
|
export { AdapterRegistry, adapterRegistry, DriverNotInstalledError } from './adapter-registry';
|
|
3
3
|
export { PlaywrightAdapter } from './playwright/playwright-adapter';
|
|
4
4
|
export { AppiumAdapter } from './appium/appium-adapter';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
-
import { TestGeneratorAdapter, TestFileData, ScenarioData, RenderImportsOptions } from '../adapter-interface';
|
|
2
|
+
import { TestGeneratorAdapter, TestFileData, ScenarioData, RenderImportsOptions, HookRenderData } from '../adapter-interface';
|
|
3
3
|
import { TemplateEngine } from '../../template-engine';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -34,15 +34,15 @@ export class PlaywrightAdapter implements TestGeneratorAdapter {
|
|
|
34
34
|
return this.templateEngine.renderBeforeEach(data);
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
renderBeforeAll(data:
|
|
37
|
+
renderBeforeAll(data: HookRenderData): string {
|
|
38
38
|
return this.templateEngine.renderBeforeAll(data);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
renderAfterEach(data:
|
|
41
|
+
renderAfterEach(data: HookRenderData): string {
|
|
42
42
|
return this.templateEngine.renderAfterEach(data);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
renderAfterAll(data:
|
|
45
|
+
renderAfterAll(data: HookRenderData): string {
|
|
46
46
|
return this.templateEngine.renderAfterAll(data);
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -1,8 +1,26 @@
|
|
|
1
|
-
test.afterAll(async (
|
|
1
|
+
test.afterAll(async () => {
|
|
2
|
+
{{#if collect}}
|
|
3
|
+
// Hook checks are collected: every failed step is recorded and the hook fails ONCE with the full list
|
|
4
|
+
const __hookFailures: string[] = [];
|
|
5
|
+
{{/if}}
|
|
2
6
|
{{#each steps}}
|
|
3
7
|
{{#if comment}}
|
|
4
8
|
// {{comment}}
|
|
5
9
|
{{/if}}
|
|
10
|
+
{{#if wrap}}
|
|
11
|
+
try {
|
|
12
|
+
{{code}}
|
|
13
|
+
} catch (e) {
|
|
14
|
+
__hookFailures.push({{label}} + ': ' + String((e as Error).message).split('\n')[0]);
|
|
15
|
+
{{#if stopOnFailure}}
|
|
16
|
+
expect(__hookFailures, 'Hook preconditions not met').toEqual([]);
|
|
17
|
+
{{/if}}
|
|
18
|
+
}
|
|
19
|
+
{{else}}
|
|
6
20
|
{{code}}
|
|
21
|
+
{{/if}}
|
|
7
22
|
{{/each}}
|
|
23
|
+
{{#if collect}}
|
|
24
|
+
expect(__hookFailures, 'Hook preconditions not met').toEqual([]);
|
|
25
|
+
{{/if}}
|
|
8
26
|
});
|
|
@@ -1,8 +1,26 @@
|
|
|
1
1
|
test.afterEach(async ({ page }) => {
|
|
2
|
+
{{#if collect}}
|
|
3
|
+
// Hook checks are collected: every failed step is recorded and the hook fails ONCE with the full list
|
|
4
|
+
const __hookFailures: string[] = [];
|
|
5
|
+
{{/if}}
|
|
2
6
|
{{#each steps}}
|
|
3
7
|
{{#if comment}}
|
|
4
8
|
// {{comment}}
|
|
5
9
|
{{/if}}
|
|
10
|
+
{{#if wrap}}
|
|
11
|
+
try {
|
|
12
|
+
{{code}}
|
|
13
|
+
} catch (e) {
|
|
14
|
+
__hookFailures.push({{label}} + ': ' + String((e as Error).message).split('\n')[0]);
|
|
15
|
+
{{#if stopOnFailure}}
|
|
16
|
+
expect(__hookFailures, 'Hook preconditions not met').toEqual([]);
|
|
17
|
+
{{/if}}
|
|
18
|
+
}
|
|
19
|
+
{{else}}
|
|
6
20
|
{{code}}
|
|
21
|
+
{{/if}}
|
|
7
22
|
{{/each}}
|
|
23
|
+
{{#if collect}}
|
|
24
|
+
expect(__hookFailures, 'Hook preconditions not met').toEqual([]);
|
|
25
|
+
{{/if}}
|
|
8
26
|
});
|
|
@@ -1,8 +1,26 @@
|
|
|
1
|
-
test.beforeAll(async (
|
|
1
|
+
test.beforeAll(async () => {
|
|
2
|
+
{{#if collect}}
|
|
3
|
+
// Hook checks are collected: every failed step is recorded and the hook fails ONCE with the full list
|
|
4
|
+
const __hookFailures: string[] = [];
|
|
5
|
+
{{/if}}
|
|
2
6
|
{{#each steps}}
|
|
3
7
|
{{#if comment}}
|
|
4
8
|
// {{comment}}
|
|
5
9
|
{{/if}}
|
|
10
|
+
{{#if wrap}}
|
|
11
|
+
try {
|
|
12
|
+
{{code}}
|
|
13
|
+
} catch (e) {
|
|
14
|
+
__hookFailures.push({{label}} + ': ' + String((e as Error).message).split('\n')[0]);
|
|
15
|
+
{{#if stopOnFailure}}
|
|
16
|
+
expect(__hookFailures, 'Hook preconditions not met').toEqual([]);
|
|
17
|
+
{{/if}}
|
|
18
|
+
}
|
|
19
|
+
{{else}}
|
|
6
20
|
{{code}}
|
|
21
|
+
{{/if}}
|
|
7
22
|
{{/each}}
|
|
23
|
+
{{#if collect}}
|
|
24
|
+
expect(__hookFailures, 'Hook preconditions not met').toEqual([]);
|
|
25
|
+
{{/if}}
|
|
8
26
|
});
|
|
@@ -18,6 +18,9 @@ import { {{urlAssertImports}} } from '{{basePath}}/url-assert';
|
|
|
18
18
|
{{#if regexEscapeImport}}
|
|
19
19
|
import { escapeRegExp } from '{{basePath}}/regex-escape';
|
|
20
20
|
{{/if}}
|
|
21
|
+
{{#if checkedStateImports}}
|
|
22
|
+
import { {{checkedStateImports}} } from '{{basePath}}/checked-state';
|
|
23
|
+
{{/if}}
|
|
21
24
|
|
|
22
25
|
// This file is auto-generated from Gherkin feature files
|
|
23
26
|
// DO NOT EDIT MANUALLY - changes will be overwritten
|
package/src/generators/test-generator/adapters/playwright/templates/steps/actions/check-action.hbs
CHANGED
|
@@ -1 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
{{!-- Playwright's .check() accepts ONLY native checkbox/radio and role=checkbox|radio|switch —
|
|
2
|
+
a `<button aria-pressed>` toggle threw "Not a checkbox or radio button" (#675). The branch
|
|
3
|
+
cannot be decided here: a testid/CSS selector carries no role, so the element is inspected at
|
|
4
|
+
run time by setCheckedState (specs/checked-state.ts), which also keeps the step IDEMPOTENT —
|
|
5
|
+
the reason `check` exists rather than `click`. --}}
|
|
6
|
+
await setCheckedState({{> locator}}, true, '{{escapeQuotes selectorRef}}'{{> checked-opts cls=checkedClass attr=checkedAttribute}});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{{#if clickPosition}}
|
|
2
|
+
{{!-- Positioned click: coordinates are relative to the element's PADDING box (clientWidth/Height),
|
|
3
|
+
the same box `click({ position })` measures from — boundingBox() would be the border box and
|
|
4
|
+
drift by the border width. Read once, checked, then clicked, so an offset past the real size
|
|
5
|
+
fails naming that size instead of landing on a neighbour. --}}
|
|
6
|
+
{ const el = {{> locator}};
|
|
7
|
+
const __label = {{{label}}};
|
|
8
|
+
await el.waitFor({ state: 'visible' }); // the box is read once — not before the element has laid out
|
|
9
|
+
{{!-- padding box = bounding rect minus borders: clientWidth/Height would read 0 on an inline element (a link). --}}
|
|
10
|
+
const box = await el.evaluate((n) => {
|
|
11
|
+
const r = n.getBoundingClientRect(); const cs = getComputedStyle(n); const b = (p: string) => parseFloat(cs.getPropertyValue(p)) || 0;
|
|
12
|
+
return { w: r.width - b('border-left-width') - b('border-right-width'), h: r.height - b('border-top-width') - b('border-bottom-width') };
|
|
13
|
+
});
|
|
14
|
+
if (box.w <= 0 || box.h <= 0) throw new Error('[sungen] ' + __label + ': element has no layout box (hidden, detached or zero-sized)');
|
|
15
|
+
const x = {{#if (eq clickPosition.xUnit '%')}}box.w * {{clickPosition.xRatio}}{{else}}{{clickPosition.x}}{{/if}};
|
|
16
|
+
const y = {{#if (eq clickPosition.yUnit '%')}}box.h * {{clickPosition.yRatio}}{{else}}{{clickPosition.y}}{{/if}};
|
|
17
|
+
if (x >= box.w || y >= box.h) throw new Error('[sungen] ' + __label + ': click position (' + Math.round(x) + ', ' + Math.round(y) + ') lies outside the element box ' + Math.round(box.w) + 'x' + Math.round(box.h));
|
|
18
|
+
await el.click({ position: { x, y } });
|
|
19
|
+
}
|
|
20
|
+
{{else}}
|
|
21
|
+
{{!-- Unreachable through the compiler (SG-E026 replaces the step) — kept so a stray render fails loud. --}}
|
|
22
|
+
throw new Error('[sungen] click position could not be parsed');
|
|
23
|
+
{{/if}}
|
package/src/generators/test-generator/adapters/playwright/templates/steps/actions/expand-action.hbs
CHANGED
|
@@ -5,20 +5,20 @@ await el.click();
|
|
|
5
5
|
{{else if expanded.class}}
|
|
6
6
|
{{!-- State signalled by a class on a descendant (e.g. an accordion chevron icon). --}}
|
|
7
7
|
{{#if (eq direction 'expand')}}
|
|
8
|
-
if (!(await el.locator('.{{expanded.class}}').count())) { await el.click(); }
|
|
9
|
-
await expect(el.locator('.{{expanded.class}}')).toBeVisible();
|
|
8
|
+
if (!(await el.locator('.{{escapeQuotes expanded.class}}').count())) { await el.click(); }
|
|
9
|
+
await expect(el.locator('.{{escapeQuotes expanded.class}}')).toBeVisible();
|
|
10
10
|
{{else}}
|
|
11
|
-
if (await el.locator('.{{expanded.class}}').count()) { await el.click(); }
|
|
12
|
-
await expect(el.locator('.{{expanded.class}}')).toHaveCount(0);
|
|
11
|
+
if (await el.locator('.{{escapeQuotes expanded.class}}').count()) { await el.click(); }
|
|
12
|
+
await expect(el.locator('.{{escapeQuotes expanded.class}}')).toHaveCount(0);
|
|
13
13
|
{{/if}}
|
|
14
14
|
{{else if expanded.attribute}}
|
|
15
15
|
{{!-- Explicit boolean attribute ('true'/'false'), custom or aria-expanded. --}}
|
|
16
16
|
{{#if (eq direction 'expand')}}
|
|
17
|
-
if (await el.getAttribute('{{expanded.attribute}}') !== 'true') { await el.click(); }
|
|
18
|
-
await expect(el).toHaveAttribute('{{expanded.attribute}}', 'true');
|
|
17
|
+
if (await el.getAttribute('{{escapeQuotes expanded.attribute}}') !== 'true') { await el.click(); }
|
|
18
|
+
await expect(el).toHaveAttribute('{{escapeQuotes expanded.attribute}}', 'true');
|
|
19
19
|
{{else}}
|
|
20
|
-
if (await el.getAttribute('{{expanded.attribute}}') !== 'false') { await el.click(); }
|
|
21
|
-
await expect(el).toHaveAttribute('{{expanded.attribute}}', 'false');
|
|
20
|
+
if (await el.getAttribute('{{escapeQuotes expanded.attribute}}') !== 'false') { await el.click(); }
|
|
21
|
+
await expect(el).toHaveAttribute('{{escapeQuotes expanded.attribute}}', 'false');
|
|
22
22
|
{{/if}}
|
|
23
23
|
{{else}}
|
|
24
24
|
{{!-- Default (zero-config): drive by aria-expanded, but assert it ONLY when the element exposes it. --}}
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
-
const editorLocator
|
|
1
|
+
{{!-- Block-scoped, like the appium counterpart: `const editorLocator` at statement level made a
|
|
2
|
+
SECOND editor fill in the same scenario a duplicate declaration, and Playwright fails the
|
|
3
|
+
WHOLE file with one SyntaxError — every scenario in it never runs. Unreachable while
|
|
4
|
+
`contenteditable:` was dead and nothing set `inputMethod` (#712); a form with two rich-text
|
|
5
|
+
fields hits it immediately once the field works. SG-E022 is the gate that caught it. --}}
|
|
6
|
+
{ const editorLocator = {{> locator}};
|
|
2
7
|
await editorLocator.click();
|
|
3
|
-
await editorLocator.pressSequentially('{{escapeQuotes fillValue}}');
|
|
8
|
+
await editorLocator.pressSequentially('{{escapeQuotes fillValue}}'); }
|
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
{
|
|
2
|
-
|
|
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
|
+
{ const tableRow = {{> locator}}{{#if rows}}.locator('{{escapeQuotes rows}}'){{else}}.getByRole('row'){{/if}}.nth({{{rowIndexExpr}}});
|
|
5
|
+
{{!-- `check`/`uncheck` must not emit a bare .check() here either (#675) — the row control is just
|
|
6
|
+
as likely to be an aria-pressed toggle as a standalone one. `click` stays a plain click. --}}
|
|
7
|
+
{{#if (eq action 'click')}}await {{{actionLocator}}}.click();{{else}}await setCheckedState({{{actionLocator}}}, {{#if (eq action 'check')}}true{{else}}false{{/if}}, '{{escapeQuotes elementName}}'{{> checked-opts cls=actionCheckedClass attr=actionCheckedAttribute}});{{/if}} }
|
|
@@ -1,2 +1,7 @@
|
|
|
1
|
-
{
|
|
2
|
-
|
|
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
|
+
{ const tableRow = {{> locator}}{{#if rows}}.locator('{{escapeQuotes rows}}'){{else}}.getByRole('row'){{/if}}.filter({ hasText: '{{escapeQuotes filterValue}}' });
|
|
5
|
+
{{!-- `check`/`uncheck` must not emit a bare .check() here either (#675) — the row control is just
|
|
6
|
+
as likely to be an aria-pressed toggle as a standalone one. `click` stays a plain click. --}}
|
|
7
|
+
{{#if (eq action 'click')}}await {{{actionLocator}}}.click();{{else}}await setCheckedState({{{actionLocator}}}, {{#if (eq action 'check')}}true{{else}}false{{/if}}, '{{escapeQuotes elementName}}'{{> checked-opts cls=actionCheckedClass attr=actionCheckedAttribute}});{{/if}} }
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
{{!-- The paginator is located from `page`, NOT from inside the table locator: antd/MUI/Radix all
|
|
2
|
+
render the control as a SIBLING of the <table>, so scoping it to the table would resolve 0
|
|
3
|
+
elements (issue #712). No implicit wait — the round-trip belongs to
|
|
4
|
+
`User wait for [X] table to refresh`. A disabled control fails loud: click waits for enabled. --}}
|
|
5
|
+
await page.locator('{{escapeQuotes paginationSelector}}').click();
|
package/src/generators/test-generator/adapters/playwright/templates/steps/actions/uncheck-action.hbs
CHANGED
|
@@ -1 +1,3 @@
|
|
|
1
|
-
|
|
1
|
+
{{!-- Inverse of check-action.hbs — see there and specs/checked-state.ts for why the control is
|
|
2
|
+
inspected at run time rather than driven by a bare .uncheck() (#675). --}}
|
|
3
|
+
await setCheckedState({{> locator}}, false, '{{escapeQuotes selectorRef}}'{{> checked-opts cls=checkedClass attr=checkedAttribute}});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
await page.getByText('{{escapeQuotes selectorValue}}').filter({ hasText: {{> has-text-value dataValue=dataValue exact=exact legacyTail=(eq selectorValue "")}} }){{
|
|
1
|
+
await page.getByText('{{escapeQuotes selectorValue}}').filter({ hasText: {{> has-text-value dataValue=dataValue exact=exact legacyTail=(eq selectorValue "")}} }){{> locator-nth}}.{{action}}();
|
package/src/generators/test-generator/adapters/playwright/templates/steps/actions/wait-for-page.hbs
CHANGED
|
@@ -1 +1,18 @@
|
|
|
1
|
-
|
|
1
|
+
{{!-- `wait for [T] page` is a WAIT, not an oracle — but a bare `page.waitForURL(/…/)` resolves on
|
|
2
|
+
the FIRST URL sample that matches, which for a client-side router is the URL it pushed before
|
|
3
|
+
the route guard had answered. Every step after the wait then ran against a page mid-redirect,
|
|
4
|
+
so their failures pointed at the wrong thing (#677). The unanchored regex also matched a
|
|
5
|
+
neighbour: `/vi/search` resolved on `/vi/search-results`.
|
|
6
|
+
|
|
7
|
+
`waitForPage` is the third caller of the settle-and-hold core in specs/url-assert.ts, after
|
|
8
|
+
the `see [T] page` / `is on [T] page` pair #662 routed there (partials/url-assertion.hbs):
|
|
9
|
+
arrive, let the page settle, HOLD the URL, and fail naming where the user actually ended up.
|
|
10
|
+
|
|
11
|
+
Where it deliberately DIFFERS from the Then-side `expectPage`: the query is not constrained.
|
|
12
|
+
A bare page ASSERTION means a bare page; a wait for a results page legitimately lands on
|
|
13
|
+
`?q=…`, and reddening there would assert something the step never said. The helper's docstring
|
|
14
|
+
carries the full argument.
|
|
15
|
+
|
|
16
|
+
`hostname` rides separately and only for an absolute page value: it is read off `u.hostname`,
|
|
17
|
+
which never appears in `u.pathname`, so one concatenated regex cannot carry both. --}}
|
|
18
|
+
await waitForPage(page, { pathname: /^{{pathRegex}}\/?$/{{#if hostRegex}}, hostname: /^{{hostRegex}}$/{{/if}} });
|
|
@@ -1,7 +1,35 @@
|
|
|
1
|
-
{
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
{{!-- All-contain: the assertion has to SURVIVE whatever async action precedes it.
|
|
2
|
+
|
|
3
|
+
`locator.allInnerTexts()` is a one-shot DOM read — no auto-wait, no retry — and wrapping its
|
|
4
|
+
RESULT in expect() adds no waiting either, because by then it is a plain array of strings.
|
|
5
|
+
Playwright's auto-waiting only applies when the LOCATOR is handed to expect(). Placed after a
|
|
6
|
+
search click or a filter change, the old shape asserted against the PRE-action DOM and failed
|
|
7
|
+
deterministically, naming real-looking values that read as a product defect rather than as a
|
|
8
|
+
generator bug (#672). A `toBeGreaterThan(0)` guard does not help: the stale list is non-empty,
|
|
9
|
+
so the length check passes and the CONTENT check is what races.
|
|
10
|
+
|
|
11
|
+
Playwright has no locator-level "every match contains X" — `filter({ hasNotText })` matches
|
|
12
|
+
case-insensitively on normalized whitespace, which is not the `toContain` semantics this step
|
|
13
|
+
promises — so the wait is expressed with expect.poll, which retries for `expect.timeout` from
|
|
14
|
+
playwright.config.ts. Two details carry the fix:
|
|
15
|
+
|
|
16
|
+
- The loop asserts on the snapshot the poll SETTLED on. Re-reading the locator afterwards
|
|
17
|
+
reopens the very race being closed — a late render pass between the poll settling and the
|
|
18
|
+
re-read swapped the rows out again, which is how the reporter's first patch passed in
|
|
19
|
+
isolation and failed in the full suite.
|
|
20
|
+
- The poll's own rejection is swallowed: on its own it can only report
|
|
21
|
+
`toBe(true) received false`. The per-item expect() below names expected vs received, which
|
|
22
|
+
is the message a QA has to act on. --~}}{
|
|
23
|
+
const __expected_{{stepCounter}} = '{{escapeQuotes expectedText}}';
|
|
24
|
+
let __items_{{stepCounter}}: string[] = [];
|
|
25
|
+
try {
|
|
26
|
+
await expect.poll(async () => {
|
|
27
|
+
__items_{{stepCounter}} = await {{> locator}}.allInnerTexts();
|
|
28
|
+
return __items_{{stepCounter}}.length > 0 && __items_{{stepCounter}}.every((__s) => __s.includes(__expected_{{stepCounter}}));
|
|
29
|
+
}).toBe(true);
|
|
30
|
+
} catch { /* the assertions below report expected-vs-received on the settled snapshot */ }
|
|
31
|
+
expect(__items_{{stepCounter}}.length, 'Expected at least one [{{escapeQuotes selectorRef}}]').toBeGreaterThan(0);
|
|
4
32
|
for (const __t_{{stepCounter}} of __items_{{stepCounter}}) {
|
|
5
|
-
expect(__t_{{stepCounter}}).toContain(
|
|
33
|
+
expect(__t_{{stepCounter}}).toContain(__expected_{{stepCounter}});
|
|
6
34
|
}
|
|
7
35
|
}
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
-
{
|
|
1
|
+
{{!-- The count() below is a one-shot read, same class as #672: after a search or filter click the
|
|
2
|
+
container list may not have rendered yet, and a bare `toBeGreaterThan(0)` on a stale 0 fails
|
|
3
|
+
instantly with no retry. `not.toHaveCount(0)` IS locator-based, so it auto-waits for the list
|
|
4
|
+
to appear; count() then reads a list that is already known to exist. --~}}{
|
|
2
5
|
const {{containerVar}} = {{> locator}};
|
|
6
|
+
await expect({{containerVar}}, 'Expected at least one [{{escapeQuotes selectorRef}}]').not.toHaveCount(0);
|
|
3
7
|
const __n_{{stepCounter}} = await {{containerVar}}.count();
|
|
4
|
-
expect(__n_{{stepCounter}}, 'Expected at least one [{{escapeQuotes selectorRef}}]').toBeGreaterThan(0);
|
|
5
8
|
for (let {{containerIndexVar}} = 0; {{containerIndexVar}} < __n_{{stepCounter}}; {{containerIndexVar}}++) {
|
|
6
9
|
await expect({{#with child}}{{> locator parentLocator=(add (add (add ../containerVar ".nth(") ../containerIndexVar) ")")}}{{/with}}, 'Every [{{escapeQuotes selectorRef}}] must contain [{{escapeQuotes childRef}}]').toBeVisible();
|
|
7
10
|
}
|
|
@@ -1 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
{{!-- `expect(locator).toBeChecked()` throws the same "Not a checkbox or radio button" (#675) on a
|
|
2
|
+
`<button aria-pressed>` as the action side did, so driving a toggle worked while ASSERTING it
|
|
3
|
+
did not. expectCheckedState reads state the same way setCheckedState drives it — an action and
|
|
4
|
+
the assertion after it can never disagree about what the control reports. --}}
|
|
5
|
+
await expectCheckedState({{> locator}}, true, '{{escapeQuotes selectorRef}}'{{> checked-opts cls=checkedClass attr=checkedAttribute}});
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
{{!-- Rooted at `page`, like the two lines below it. This line used to render `{{> locator}}` from
|
|
2
|
+
step data that carries no strategy/value at all, so it emitted `page.locator('')` — an empty
|
|
3
|
+
CSS selector, which Playwright refuses to parse. The step could not run.
|
|
4
|
+
|
|
5
|
+
Block-scoped: the column-index const is named after the [Reference], so two steps naming the
|
|
6
|
+
same column — or two non-ASCII references, which sanitize down to the same identifier — would
|
|
7
|
+
redeclare it in one scenario and the spec would not compile. The braces make each step's
|
|
8
|
+
binding its own.
|
|
9
|
+
|
|
10
|
+
Not dialog/frame scoped: this step form carries no selector entry, so there is nothing for
|
|
11
|
+
`pageRoot` to read. A column assertion inside a modal needs the row-scoped form. --~}}{ await expect(page.getByRole('columnheader', { name: '{{columnName}}', includeHidden: true })).toBeVisible();
|
|
2
12
|
const {{columnIndexVar}} = (await page.getByRole('columnheader', { includeHidden: true }).filter({ visible: true }).allTextContents()).findIndex(h => h.includes('{{columnName}}'));
|
|
3
|
-
|
|
13
|
+
expect({{columnIndexVar}}, 'Column "{{escapeQuotes columnName}}" is not among the visible headers').toBeGreaterThanOrEqual(0);
|
|
14
|
+
await expect(page.getByRole('row', { includeHidden: true }).filter({ visible: true }).nth({{rowNth}}).getByRole('cell', { includeHidden: true }).filter({ visible: true }).nth({{columnIndexVar}})).toHaveText('{{dataValue}}'); }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
await expect({{> locator}}).toContainText('{{expectedText}}');
|
|
1
|
+
await expect({{> locator}}).toContainText('{{escapeQuotes expectedText}}');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{{#if name}}
|
|
2
|
-
await expect(page.getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }).filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{
|
|
2
|
+
await expect(page.getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }).filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{> locator-nth}}).toBeDisabled();
|
|
3
3
|
{{else}}
|
|
4
|
-
await expect(page.getByRole('{{role}}'{{#if exact}}, { exact: true }{{/if}}).filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{
|
|
4
|
+
await expect(page.getByRole('{{role}}'{{#if exact}}, { exact: true }{{/if}}).filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{> locator-nth}}).toBeDisabled();
|
|
5
5
|
{{/if}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
await expect(page.getByText('{{escapeQuotes selectorRef}}').filter({ hasText: {{#if (eq selectorValue "")}}/.*{{escapeRegex dataValue}}$/{{else}}'{{escapeQuotes dataValue}}'{{/if}} }){{
|
|
1
|
+
await expect(page.getByText('{{escapeQuotes selectorRef}}').filter({ hasText: {{#if (eq selectorValue "")}}/.*{{escapeRegex dataValue}}$/{{else}}'{{escapeQuotes dataValue}}'{{/if}} }){{> locator-nth}}).toBeDisabled();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
await expect({{> locator}}).toHaveText('{{expectedText}}');
|
|
1
|
+
await expect({{> locator}}).toHaveText('{{escapeQuotes expectedText}}');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{{#if name}}
|
|
2
|
-
await expect(page.getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }).filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{
|
|
2
|
+
await expect(page.getByRole('{{role}}', { name: '{{escapeQuotes name}}'{{#if exact}}, exact: true{{/if}} }).filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{> locator-nth}}).toBeHidden();
|
|
3
3
|
{{else}}
|
|
4
|
-
await expect(page.getByRole('{{role}}'{{#if exact}}, { exact: true }{{/if}}).filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{
|
|
4
|
+
await expect(page.getByRole('{{role}}'{{#if exact}}, { exact: true }{{/if}}).filter({ hasText: /^{{escapeRegex dataValue}}$/ }){{> locator-nth}}).toBeHidden();
|
|
5
5
|
{{/if}}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
await expect(page.getByText('{{escapeQuotes selectorRef}}').filter({ hasText: {{#if (eq selectorValue "")}}/.*{{escapeRegex dataValue}}$/{{else}}'{{escapeQuotes dataValue}}'{{/if}} }){{
|
|
1
|
+
await expect(page.getByText('{{escapeQuotes selectorRef}}').filter({ hasText: {{#if (eq selectorValue "")}}/.*{{escapeRegex dataValue}}$/{{else}}'{{escapeQuotes dataValue}}'{{/if}} }){{> locator-nth}}).toBeHidden();
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
{{!-- Inverse of checked-assertion.hbs — see there (#675). --}}
|
|
2
|
+
await expectCheckedState({{> locator}}, false, '{{escapeQuotes selectorRef}}'{{> checked-opts cls=checkedClass attr=checkedAttribute}});
|
|
@@ -1,7 +1,16 @@
|
|
|
1
|
-
{{#if (or
|
|
1
|
+
{{#if (or checkedClass checkedAttribute)}}
|
|
2
|
+
{{!-- A DECLARED signal (issue #730) short-circuits the whole ARIA probe below: the author
|
|
3
|
+
reached for `selected:`/`checked:` precisely because this component publishes no aria-selected,
|
|
4
|
+
no aria-current and no checked state — a Tailwind card/tab picker. Probing for them first would
|
|
5
|
+
just spend the budget confirming they are absent. --}}
|
|
6
|
+
await expectCheckedState({{> locator}}, true, '{{escapeQuotes selectorRef}}'{{> checked-opts cls=checkedClass attr=checkedAttribute}});
|
|
7
|
+
{{else if (or (eq role 'checkbox') (eq role 'radio') (eq role 'switch'))}}
|
|
2
8
|
{{!-- Form control: "selected" is the checked state, never aria-selected. A native checkbox/radio
|
|
3
9
|
exposes no aria-selected attribute — its state lives in the `checked` property. --}}
|
|
4
|
-
|
|
10
|
+
{{!-- "selected" on a form control IS the checked state — routed through expectCheckedState so an
|
|
11
|
+
aria-pressed toggle does not throw "Not a checkbox or radio button" (#675), and so the selector
|
|
12
|
+
that declares role: switch while the element publishes aria-pressed still reads correctly. --}}
|
|
13
|
+
await expectCheckedState({{> locator}}, true, '{{escapeQuotes selectorRef}}');
|
|
5
14
|
{{else}}
|
|
6
15
|
{{!-- Other/unknown role (tab, option, or a text/testid/css selector): probe at runtime. An ARIA
|
|
7
16
|
single-select widget exposes aria-selected → assert it; anything without it falls back to the
|
|
@@ -9,8 +18,17 @@ await expect({{> locator}}).toBeChecked();
|
|
|
9
18
|
{ const el = {{> locator}};
|
|
10
19
|
if ((await el.getAttribute('aria-selected')) !== null) {
|
|
11
20
|
await expect(el).toHaveAttribute('aria-selected', 'true');
|
|
21
|
+
} else if ((await el.getAttribute('aria-current')) !== null) {
|
|
22
|
+
{{!-- aria-current marks the CURRENT item of a navigation set (WAI-ARIA 1.1): a Next.js tab strip
|
|
23
|
+
of plain <a> links carries aria-current="page" and has neither aria-selected nor a checked
|
|
24
|
+
state, so toBeChecked() threw "Not a checkbox or radio button" on a correct step. Any value
|
|
25
|
+
but "false" ("page", "step", "true", …) means current. Probed AFTER aria-selected: a real
|
|
26
|
+
role=tab owns the stronger signal. --}}
|
|
27
|
+
await expect(el).toHaveAttribute('aria-current', /^(?!false$)./);
|
|
12
28
|
} else {
|
|
13
|
-
|
|
29
|
+
{{!-- Last resort, same as the -with-filter twin: neither aria-selected nor aria-current, so read the
|
|
30
|
+
checked/pressed state through the #675-safe path rather than toBeChecked(). --}}
|
|
31
|
+
await expectCheckedState(el, true, '{{escapeQuotes selectorRef}}');
|
|
14
32
|
}
|
|
15
33
|
}
|
|
16
34
|
{{/if}}
|
|
@@ -1,13 +1,31 @@
|
|
|
1
1
|
{{!-- Filter contract shared with the visible/hidden/contains siblings (has-text-value):
|
|
2
2
|
substring by default, `exact: true` in the selector YAML anchors. --}}
|
|
3
3
|
{{#if selectedProbe}}
|
|
4
|
+
{{#if (or checkedClass checkedAttribute)}}
|
|
5
|
+
{{!-- Declared class signal (issue #730) — short-circuits the ARIA probe, exactly as the unfiltered
|
|
6
|
+
twin selected-assertion.hbs does. Without this branch the filtered form stayed broken while the
|
|
7
|
+
unfiltered one worked, which is the inconsistency #730's second field report ran into. --}}
|
|
8
|
+
await expectCheckedState({{> locator-base}}.filter({ hasText: {{> has-text-value dataValue=dataValue exact=exact}} }){{> locator-nth}}, true, '{{escapeQuotes selectorRef}}'{{> checked-opts cls=checkedClass attr=checkedAttribute}});
|
|
9
|
+
{{else}}
|
|
4
10
|
{ const el = {{> locator-base}}.filter({ hasText: {{> has-text-value dataValue=dataValue exact=exact}} }){{> locator-nth}};
|
|
5
11
|
if ((await el.getAttribute('aria-selected')) !== null) {
|
|
6
12
|
await expect(el).toHaveAttribute('aria-selected', 'true');
|
|
13
|
+
} else if ((await el.getAttribute('aria-current')) !== null) {
|
|
14
|
+
{{!-- aria-current marks the CURRENT item of a navigation set (WAI-ARIA 1.1): a Next.js tab strip
|
|
15
|
+
of plain <a> links carries aria-current="page" and has neither aria-selected nor a checked
|
|
16
|
+
state, so toBeChecked() threw "Not a checkbox or radio button" on a correct step. Any value
|
|
17
|
+
but "false" ("page", "step", "true", …) means current. Probed AFTER aria-selected: a real
|
|
18
|
+
role=tab owns the stronger signal. --}}
|
|
19
|
+
await expect(el).toHaveAttribute('aria-current', /^(?!false$)./);
|
|
7
20
|
} else {
|
|
8
|
-
|
|
21
|
+
{{!-- Last resort: the element publishes neither aria-selected nor aria-current. toBeChecked() used
|
|
22
|
+
to sit here and threw "Not a checkbox or radio button" on an aria-pressed toggle (#675). --}}
|
|
23
|
+
await expectCheckedState(el, true, '{{escapeQuotes selectorRef}}');
|
|
9
24
|
}
|
|
10
25
|
}
|
|
26
|
+
{{/if}}
|
|
27
|
+
{{else}}{{#if isCheckedAssertion}}
|
|
28
|
+
await expectCheckedState({{> locator-base}}.filter({ hasText: {{> has-text-value dataValue=dataValue exact=exact}} }){{> locator-nth}}, {{checkedWant}}, '{{escapeQuotes selectorRef}}'{{> checked-opts cls=checkedClass attr=checkedAttribute}});
|
|
11
29
|
{{else}}
|
|
12
30
|
await expect({{> locator-base}}.filter({ hasText: {{> has-text-value dataValue=dataValue exact=exact}} }){{> locator-nth}}).{{{stateAssertion}}};
|
|
13
|
-
{{/if}}
|
|
31
|
+
{{/if}}{{/if}}
|
package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/table-empty.hbs
CHANGED
|
@@ -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(0);
|