@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
|
@@ -13,7 +13,8 @@ user-invocable: false
|
|
|
13
13
|
- **Actor**: Always `User`, always active voice.
|
|
14
14
|
- **Value**: `with {{snake_case}}` — never hardcode static data.
|
|
15
15
|
- **State**: `is <keyword>` — never use `{{}}` for states.
|
|
16
|
-
- **Parent scope**: `in [Parent] parentType` — optional, only when page has 2+ similar blocks needing disambiguation.
|
|
16
|
+
- **Parent scope**: `in [Parent] parentType` — optional, only when page has 2+ similar blocks needing disambiguation. Container types: `table` `list` `list-item` `section` `dialog` `modal` `drawer` `overlay` `form` `card` `item` `row` `group` `panel` `region` `container` `tab` `menu`. Anything else is not a container, and the clause is DROPPED with `SG-W028` rather than silently widening the step to the whole page.
|
|
17
|
+
- **Parent scope BY CONTENT** (web only): `… with {{v}} in [Parent] parentType with {{v2}}` — the SECOND `with` picks the one container whose content is `{{v2}}`, for a list of look-alike cards/rows. One `with` after the clause still belongs to the TARGET (`see [first] row in [apply list] table with {{job_name}}`), so the by-content form always carries two. On the mobile adapter it reports `SG-W028` — an Appium parent chain has no content filter.
|
|
17
18
|
|
|
18
19
|
## Keyword → Action Rules
|
|
19
20
|
|
|
@@ -35,7 +36,7 @@ AND → inherits from preceding keyword
|
|
|
35
36
|
### Setup / Form / Interaction
|
|
36
37
|
|
|
37
38
|
```
|
|
38
|
-
User is on [T] page | page with {{v}} | dialog
|
|
39
|
+
User is on [T] page | page with {{v}} | dialog # {{v}} = query/fragment ("?id=200", "#tab") APPENDED to the page value — a bare "200" onto "/x?id=199" gives "?id=199200"
|
|
39
40
|
User fill [T] field | textarea | search | slider | date-picker with {{v}} # date-picker: web only unless qa/app.yaml declares widgets.datepicker — appium refuses it
|
|
40
41
|
User fill [T] uploader with {{f}} [web]
|
|
41
42
|
User clear [T] field
|
|
@@ -45,6 +46,8 @@ User select [T] dropdown with {{v}}
|
|
|
45
46
|
User click [T] button | tab | column | breadcrumb
|
|
46
47
|
User click [T] row with {{v}}
|
|
47
48
|
User try to click [T] button | link # DISABLED element only — see rule below (v3.3) [web]
|
|
49
|
+
User click [T] overlay at position "top-left" # corner preset: top-left | top-right | bottom-left | bottom-right [web]
|
|
50
|
+
User click [T] overlay at offset x=24px, y=10% # px or % per axis, from the element's top-left [web]
|
|
48
51
|
User double click [T] element
|
|
49
52
|
User hover [T] icon | row # no-op on mobile (see Platform Support)
|
|
50
53
|
User drag [T] to [T2]
|
|
@@ -57,7 +60,17 @@ User expand | collapse [T] row
|
|
|
57
60
|
scenario asserts nothing happens (cooldown re-send, submit before the form is valid). `click` waits for
|
|
58
61
|
`enabled` → it can only time out there; `try to click` compiles to `.click({ force: true })`. Always
|
|
59
62
|
pair it with the proof: `Then User see [T] TYPE is disabled` + an assertion that the effect did NOT occur.
|
|
60
|
-
NEVER use it for a click that is supposed to work — it deletes the actionability wait.
|
|
63
|
+
NEVER use it for a click that is supposed to work — it deletes the actionability wait. (`force: true` is
|
|
64
|
+
reachable ONLY through `try to click` — there is no separate `force` keyword.)
|
|
65
|
+
|
|
66
|
+
**click at position rule** (web only): a plain `click` lands on the element's CENTRE. To click a
|
|
67
|
+
modal backdrop where the dialog is not, or any control whose centre is covered, append
|
|
68
|
+
`at position "<corner>"` (5% in from that corner) or `at offset x=<n>px|%, y=<n>px|%`. The position
|
|
69
|
+
is checked against the element's real box at run time (an offset past it fails naming the size). The
|
|
70
|
+
suffix is refused at compile time (`SG-E026`, the step throws instead of clicking the centre) when it
|
|
71
|
+
is misspelt or unquoted, when a value is negative / above 100% / missing an axis, when anything
|
|
72
|
+
follows it, and when it is combined with `try to click`, a `with {{v}}` text filter, `… and accept [OK]
|
|
73
|
+
alert`, or `double click` — those forms take no position.
|
|
61
74
|
|
|
62
75
|
### Alert / Keyboard / Wait / Scroll
|
|
63
76
|
|
|
@@ -68,12 +81,11 @@ User click [OK | Cancel] alert # two-step form: must come BEF
|
|
|
68
81
|
User fill [T] alert with {{v}} # no-op on mobile — native prompt fill is app-specific
|
|
69
82
|
User see [message text] alert
|
|
70
83
|
User press Escape key | [Enter] key | Tab key 5 times | Enter on [T] field
|
|
71
|
-
User wait for N seconds | [T] page # [T] page: web waits for the URL; mobile pauses (settle) — see Platform Support
|
|
84
|
+
User wait for N seconds | [T] page # [T] page: web waits for the URL, then HOLDS it (see Pattern 8); mobile pauses (settle) — see Platform Support
|
|
72
85
|
User wait for [T] TYPE is visible | hidden | enabled | disabled # ANY reference (v3.3)
|
|
73
86
|
User wait for [T] TYPE with {{v}} # until it shows the value
|
|
74
87
|
User wait for [T] table to refresh # filter/search/pagination round-trip (v3.3)
|
|
75
88
|
User scroll to [T] section
|
|
76
|
-
User switch to [T] frame | [main] frame # web: iframe; mobile: hybrid-app WebView context (no-op if the screen has no WebView)
|
|
77
89
|
```
|
|
78
90
|
|
|
79
91
|
> **Browser alerts (native `window.confirm/alert/prompt` only):** prefer the compound form —
|
|
@@ -86,6 +98,36 @@ User switch to [T] frame | [main] frame # web: iframe; mobile: hybrid-
|
|
|
86
98
|
> `wait for N seconds` stays a last resort. `table to refresh` watches the app's loading
|
|
87
99
|
> indicator (`qa/app.yaml` `feedback.loading.indicator`, default `[aria-busy="true"]`).
|
|
88
100
|
|
|
101
|
+
### Scope control
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
User is on [T] dialog # ENTER dialog scope, rooted at [T]'s selectors.yaml entry
|
|
105
|
+
User use dialog # ENTER dialog scope, unnamed — plain page.getByRole('dialog')
|
|
106
|
+
User close dialog | dismiss dialog | exit dialog # EXIT dialog scope (three spellings, identical)
|
|
107
|
+
User switch to [T] frame # ENTER frame scope — web: iframe; mobile: hybrid-app WebView context
|
|
108
|
+
User switch to [main] frame # EXIT frame scope (no-op if the screen has no WebView)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
> **Dialog scope is STICKY — it ends only where you end it.** Once a scenario enters dialog scope,
|
|
112
|
+
> EVERY later locator in that scenario is prefixed with the dialog root, **including the steps that
|
|
113
|
+
> run after the dialog has closed**. Nothing implicit drops it: not a click on the dialog's own
|
|
114
|
+
> Close/OK button, and **not** `wait for [T] dialog is hidden` — that step asserts the dialog went
|
|
115
|
+
> away and then keeps resolving the following steps INSIDE it, which can never succeed. Only
|
|
116
|
+
> `close`/`dismiss`/`exit dialog`, or the start of the next scenario, drops the scope. The failure
|
|
117
|
+
> has a recognisable shape: a step after the dialog closed times out reading "element not found"
|
|
118
|
+
> while the element is plainly on screen — you are not missing a selector, you are still scoped
|
|
119
|
+
> inside a dead dialog.
|
|
120
|
+
>
|
|
121
|
+
> ```gherkin
|
|
122
|
+
> Given User is on [Confirm Delete] dialog # scope enters here — implicitly, but it still needs an EXPLICIT exit
|
|
123
|
+
> When User click [OK] button # scoped — correct, the button IS in the dialog
|
|
124
|
+
> And User exit dialog # ← REQUIRED: the click closed the dialog, the scope did not
|
|
125
|
+
> Then User see [Deleted] toast # page-rooted again
|
|
126
|
+
> ```
|
|
127
|
+
>
|
|
128
|
+
> Dialog scope is `[web]`-effective only — no Appium template reads `inDialog`, so on mobile the
|
|
129
|
+
> following steps resolve against the whole screen (`SG-W021`). Frame scope works on both.
|
|
130
|
+
|
|
89
131
|
### Positional table rows (v3.3) `[web]`
|
|
90
132
|
|
|
91
133
|
```
|
|
@@ -103,6 +145,9 @@ two content-filtered queries can hit different rows if the table re-renders in b
|
|
|
103
145
|
```
|
|
104
146
|
# 1. Visibility: User see [T] type (NEVER add "is visible") | is hidden
|
|
105
147
|
# 2. Text (toHaveText): User see [T] message | header | label with {{v}}
|
|
148
|
+
# `label` forks on the entry: declared by IDENTITY (xpath/accessibility-id/testid/id/css)
|
|
149
|
+
# → asserts THAT element contains {{v}} (add `exact: true` for a strict match); no entry,
|
|
150
|
+
# or `type: text` → matches the caption+value text pair anywhere on screen
|
|
106
151
|
# 3. Partial (toContainText): User see [T] text contains {{v}}
|
|
107
152
|
# 3b. Format (shape, not value): User see [T] label matches "yyyy/mm/dd ~ yyyy/mm/dd"
|
|
108
153
|
# 4. Input (toHaveValue): User see [T] field | date-picker | search | slider with {{v}}
|
|
@@ -139,6 +184,20 @@ detail:
|
|
|
139
184
|
anchor: { type: 'role', value: 'heading', name: 'Item Detail' } # the rendered-content proof
|
|
140
185
|
```
|
|
141
186
|
|
|
187
|
+
**`wait for [T] page` shares that hold, and only that.** Since 3.2.30 the wait compiles to
|
|
188
|
+
`waitForPage(page, {…})` on the same helper: it arrives, settles, holds the URL, and fails naming
|
|
189
|
+
where the user went — so the steps AFTER the wait no longer run against a page mid-redirect, and
|
|
190
|
+
`/vi/search` no longer resolves on `/vi/search-results`. Two differences from the `Then` side:
|
|
191
|
+
|
|
192
|
+
- **A wait does not constrain the query.** `see [Results] page` with no `with {{v}}` demands a bare
|
|
193
|
+
URL; `wait for [Results] page` after submitting a search tolerates the `?q=…` it lands on. Prove
|
|
194
|
+
the query in the `Then` that follows.
|
|
195
|
+
- **A wait is not an oracle.** It never counts toward a scenario's assertions — `ORACLE-URL-ONLY`
|
|
196
|
+
and the assertionless-test check both ignore it. Always follow it with a real `Then`.
|
|
197
|
+
|
|
198
|
+
An absolute page `value` (`https://app.example.com/reception/:id`) compiles its host into a
|
|
199
|
+
separate anchored `hostname:` — the same path on another host no longer satisfies the wait.
|
|
200
|
+
|
|
142
201
|
**Pattern 8 — the page assertion judges pathname AND query together.** Both `see [T] page` and
|
|
143
202
|
`is on [T] page` (Then-side) compile to ONE web-first check — `expectPage(page, {…})`, built on
|
|
144
203
|
`toHaveURL(urlMatches({…}))` and retrying like any other assertion — comparing the **exact** pathname
|
|
@@ -153,6 +212,8 @@ detail:
|
|
|
153
212
|
| `… page with {{v}}`, `v` = `/vi/search` (no query) | **path only**, and the compiler warns (`SG-W014`): the step asserts less than it reads as |
|
|
154
213
|
| `… page with {{v}}`, `v` empty or key missing at run time | **throws, naming the key** — an unusable value is never a silent pass |
|
|
155
214
|
|
|
215
|
+
**Navigating** with data (`Given User is on [T] page with {{v}}`) appends `{{v}}` to the page selector's value **verbatim** — no merging, by design (the primary use is injecting malformed query input, which the compiler must not sanitize). So the page value carries the PATH only and `{{v}}` carries the whole query: `value: '/job/detail'` + `job_query: "?id=200"`. A page value that already holds `?id=199` plus `{{v}}` = `"200"` navigates to `?id=199200`. A rooted `{{v}}` (`/other/path`) replaces the page's path; an absolute URL replaces everything.
|
|
216
|
+
|
|
156
217
|
So pass `with {{v}}` whenever the URL carries state worth proving, and leave it off when the page
|
|
157
218
|
must be reached clean. A dynamic segment in the selector value (`/users/:id/profile`) wildcards to
|
|
158
219
|
`[^/]+` on its own, still anchored — `/users/42/profile/edit` does not match. Params compare
|
|
@@ -206,6 +267,15 @@ User see [Table] table match data:
|
|
|
206
267
|
|
|
207
268
|
Row scope: `see [Ref] row in [Table] table with {{v}}` enters scope. Subsequent `see [Col] column with {{v}}` checks cell in that row. Use `table match data:` for multi-row verification.
|
|
208
269
|
|
|
270
|
+
**[Col] does not have to be the DOM header text.** `see [Col] column in [T] table` looks for a header whose accessible name is the reference, which cannot match a page in another language. Keep the reference readable — it is what the delivered test case shows — and declare the real header on the table entry (or on a `<col>--columnheader` entry's `name:`, which also takes `{{variables}}` for per-locale text):
|
|
271
|
+
```yaml
|
|
272
|
+
article list:
|
|
273
|
+
type: role
|
|
274
|
+
value: table
|
|
275
|
+
columns:
|
|
276
|
+
category: { index: 2, header: '記事分類' }
|
|
277
|
+
```
|
|
278
|
+
|
|
209
279
|
**[Ref] of a row step is a display label UNLESS declared in selectors.yaml.** Without an entry the step checks the value exists in ANY row — a name like `[First Contact Row]` does NOT position the row, and an ordering rule would pass even when the row moved (SG-W010 warns on positional-looking undeclared labels). To assert a POSITION, declare the row (a `type: locator` value RELATIVE to the table):
|
|
210
280
|
```yaml
|
|
211
281
|
first contact row:
|
|
@@ -214,6 +284,15 @@ first contact row:
|
|
|
214
284
|
```
|
|
215
285
|
→ compiles to `expect(table.locator('tbody tr:first-child')).toContainText(v)` — the exact row must hold the value — and still enters row scope for `[Col] column` checks.
|
|
216
286
|
|
|
287
|
+
**A list built from `<div>`s is a table only if you tell sungen what a row is.** Every table step finds rows by `getByRole('row')`; a `<div>` list has none, so `is hidden` and `is empty` would pass on 0 rows (asserting nothing) and counts/clicks would time out. Declare the row SHAPE on the **table** entry — `rows:` is relative to the table and drives all seven table steps:
|
|
288
|
+
```yaml
|
|
289
|
+
job table:
|
|
290
|
+
type: locator
|
|
291
|
+
value: 'div.job-list'
|
|
292
|
+
rows: 'div.job-row' # what EVERY row matches — NOT a row label's entry
|
|
293
|
+
```
|
|
294
|
+
Two different keys, two meanings: `rows:` on the table = every row (the shape); a `type: locator` entry on a **row label** = ONE positioned row (`tbody tr:first-child`). A shape put on a row label fails at run time with a `toHaveCount(1)` message that points back here. A CSS-located table with no `rows:` gets `SG-W016`. On a shaped table `table row {{n}}` counts from the first data row (no header offset).
|
|
295
|
+
|
|
217
296
|
### Browser storage `[web]`
|
|
218
297
|
|
|
219
298
|
```
|
|
@@ -374,7 +453,7 @@ entirely (a *pattern* gap, not a template gap), which is what the catalogue belo
|
|
|
374
453
|
|
|
375
454
|
**Mobile-only `[mobile]`** — the gesture catalog (swipe, long-press, pinch-zoom, pull-to-refresh,
|
|
376
455
|
rotate, background/foreground, notifications, grant-permission, clipboard set, set-geolocation,
|
|
377
|
-
hide-keyboard, tap-top-of) has no web counterpart, and neither do the lifecycle / device-state /
|
|
456
|
+
hide-keyboard, tap-top-of, `go back`) has no web counterpart, and neither do the lifecycle / device-state /
|
|
378
457
|
timing steps (`restart the app`, `reinstall the app`, `turn airplane mode|wifi|mobile data on|off`,
|
|
379
458
|
`turn device location on|off` — the latter two Android-only, throw on iOS — the window
|
|
380
459
|
assertions `see [X] disappear within N seconds` / `still visible after N seconds`, and the
|
|
@@ -387,8 +466,9 @@ Full syntax → `sungen-mobile-gestures`.
|
|
|
387
466
|
| Step | Web | Mobile |
|
|
388
467
|
|---|---|---|
|
|
389
468
|
| `see [T] page` \| `page with {{v}}` | asserts path+query | **silent no-op** — asserts nothing; the scenario passes regardless. Assert a screen-marker element instead |
|
|
390
|
-
| `is on [T] page` \| `open [T] page` (Given/When) | navigates via URL | no-op —
|
|
391
|
-
| `wait for [T] page` | waits for the URL | fixed `driver.pause(500)` settle — not a real wait condition |
|
|
469
|
+
| `is on [T] page` \| `open [T] page` (Given/When) | navigates via URL | `is on` = the app-launch anchor, correctly a no-op. `open`/`navigate to` = a no-op that **reports `SG-W026`** — it promises a screen change and performs none; use tap/gesture steps instead |
|
|
470
|
+
| `wait for [T] page` | waits for the URL, then settles and HOLDS it (`waitForPage`) | fixed `driver.pause(500)` settle — not a real wait condition |
|
|
471
|
+
| `go back` (OS/hardware back) | **not a web step** — reports `SG-W020`; re-enter with `is on [X] page` or tag `@manual` | Android: `KEYCODE_BACK`, the real hardware key. iOS: WDA **navigation**-back, *not* the user's edge swipe-back (iOS has no hardware back). Distinct from `tap [Back] button`: system back skips the screen's own handler unless the app wires it |
|
|
392
472
|
| `hover [T] icon \| row` | real hover | no-op — hover-revealed content is normally already visible on mobile; use `tap` |
|
|
393
473
|
| `fill [T] alert with {{v}}` | fills native `prompt()` | no-op (comment only) — app-specific, handle manually |
|
|
394
474
|
| `switch to [T] frame` | enters an `<iframe>` | switches a hybrid app's WebView context; no-op on a pure-native screen. **Needs `APPIUM_CHROMEDRIVER_AUTODOWNLOAD=1`** (.env.appium) or the switch fails with "No Chromedriver found". INSIDE the webview, selectors are **css/xpath only** — UiSelector-based types and the text-value asserts (`label with {{v}}`) don't apply there; assert element presence via css/xpath refs, then `switch to [main] frame` back |
|
|
@@ -477,7 +557,17 @@ award:
|
|
|
477
557
|
| `upload` | — | — | file inputs |
|
|
478
558
|
| `frame` | iframe selector | — | iframes |
|
|
479
559
|
|
|
480
|
-
Options: `nth` `exact` `scope` `match` `variant` `frame` `contenteditable` `columns`
|
|
560
|
+
Options: `nth` `exact` `scope` `match` `variant` `frame` `contenteditable` `columns` `checked` `selected`
|
|
561
|
+
|
|
562
|
+
`checked: { class: <cls> }` or `checked: { attribute: <attr> }` — how a checkbox-like control that is
|
|
563
|
+
NOT a real checkbox (a `<div>` tile with no `role`/`aria-checked`; `.check()` throws "Not a checkbox or
|
|
564
|
+
radio button") shows its state. `selected:` is an ALIAS of the same signal, for a card/tab picker where
|
|
565
|
+
`checked:` reads wrong; declare ONE spelling and ONE of class/attribute. `check`/`uncheck` click only
|
|
566
|
+
when the state differs, then assert it; `is checked` / `is unchecked` / `is selected` (plain or
|
|
567
|
+
`with {{v}}`) assert it. The class is read on the located element OR on a descendant (a checkmark
|
|
568
|
+
`<svg>` that only mounts when on); the attribute is read on the located element itself. Other states on
|
|
569
|
+
the same entry (`is focused`, …) keep their native matchers. The value must be ONE plain class token or
|
|
570
|
+
attribute name — whitespace, a quote or a backslash is refused at compile time, naming the entry.
|
|
481
571
|
|
|
482
572
|
`scope` (e.g. `scope: dialog`) is `[web]`-effective only — no Appium template reads `inDialog`, so
|
|
483
573
|
on mobile a dialog-scoped ref still resolves against the whole screen (`SG-W021`).
|
|
@@ -794,7 +884,32 @@ Only when `@cleanup:*` tags aren't enough — feature-specific logic.
|
|
|
794
884
|
|
|
795
885
|
### Layer 3: `@beforeAll` / `@afterAll` (optional)
|
|
796
886
|
|
|
797
|
-
For one-time setup/teardown.
|
|
887
|
+
For one-time setup/teardown. A hook scenario takes `@api:` / `@query:` preconditions exactly like an
|
|
888
|
+
ordinary scenario (override args and `@concurrent:N` included) — they run inside the hook and bind
|
|
889
|
+
`{{name}}`:
|
|
890
|
+
|
|
891
|
+
```gherkin
|
|
892
|
+
@beforeAll @api:login(email={{email}},password={{pw}}) @api:list_items(token={{login.body.token}})
|
|
893
|
+
Scenario: seed the session once before any scenario
|
|
894
|
+
Then expect {{login.status}} is 200
|
|
895
|
+
And expect {{list_items.status}} is 200
|
|
896
|
+
|
|
897
|
+
@afterAll @api:cleanup
|
|
898
|
+
Scenario: drop the seeded data after the last scenario
|
|
899
|
+
Then expect {{cleanup.status}} is 204
|
|
900
|
+
```
|
|
901
|
+
|
|
902
|
+
- A variable bound in `@beforeAll` (`{{login.body.token}}`) is readable from EVERY scenario of the
|
|
903
|
+
feature (it runs before each of them). `@afterEach`/`@afterAll` bindings stay hook-local.
|
|
904
|
+
- A failing hook skips every test in the file, so hook steps are COLLECTED: each `Then` that fails is
|
|
905
|
+
recorded and the next step still runs; a failing action step (`Given`/`When`) stops the hook there.
|
|
906
|
+
The hook fails ONCE with the full list (`Hook preconditions not met`), not with the first failure.
|
|
907
|
+
- `@cases` and `@extend` are not supported on a hook — they are ignored with a compile warning.
|
|
908
|
+
- A hook has no `page` fixture on a per-scenario (non-`@serial`) feature — keep its steps to `@api:`/
|
|
909
|
+
`@query:` preconditions and `expect` assertions there.
|
|
798
910
|
|
|
799
911
|
**Rendering order in `.spec.ts`:**
|
|
800
|
-
`test.describe` → `test.use(storageState)` → `test.use(autoCleanup)` → `test.beforeAll` → `test.beforeEach` → `test.afterEach` → `test.afterAll` → `test()` blocks
|
|
912
|
+
`test.describe` → `test.use(storageState)` → `test.use(autoCleanup)` → `test.beforeAll` → `test.beforeEach` → `test.afterEach` → `test.afterAll` → `test()` blocks.
|
|
913
|
+
On a `@serial` feature the user `@beforeAll`/`@afterAll` blocks are registered right after the built-in
|
|
914
|
+
`beforeAll` (page creation) and BEFORE the built-in `afterAll` (page close): Playwright runs `afterAll`
|
|
915
|
+
hooks in registration order, so the shared `page` is still open for the user's teardown steps.
|
|
@@ -17,6 +17,21 @@ patterns (`click`, `hover`, `fill`) don't cover.
|
|
|
17
17
|
> - **`swipe <dir> on [X]`** → `mobile: swipeGesture`.
|
|
18
18
|
> - **`long-press [X] [for N seconds]`** → `mobile: longClickGesture`.
|
|
19
19
|
> - **`rotate to landscape|portrait`** → `driver.setOrientation(...)`.
|
|
20
|
+
> - **`go back`** (also `goes back` · `press the back button` · `hardware|system|device back` ·
|
|
21
|
+
> `back gesture` · `swipe back`) → Android `mobile: pressKey` with keycode 4 (KEYCODE_BACK — the
|
|
22
|
+
> real hardware key); iOS `driver.back()`, which is WebDriverAgent's **navigation** back and *not*
|
|
23
|
+
> the user's edge swipe-back gesture — iOS has no hardware back key. **This is NOT `tap [Back]`:**
|
|
24
|
+
> Android's system back does not run the screen's own handler unless the app wires it
|
|
25
|
+
> (`onBackPressed` / `BackHandler`), so a defect on the OS path is invisible to an in-app tap —
|
|
26
|
+
> which is why the viewpoint ledger refuses the in-app button as an answer to its "OS/hardware
|
|
27
|
+
> back gesture" theme. The step takes **no `[Ref]`**; that absence is what selects it. A phrasing
|
|
28
|
+
> that names a target (`go back to [List] screen`) is refused rather than reinterpreted, so pick
|
|
29
|
+
> one form: `go back`, or a tap.
|
|
30
|
+
> **`swipe back` is only a spelling of this step, not a swipe.** It compiles to the same
|
|
31
|
+
> `KEYCODE_BACK` / `driver.back()` as every other phrasing above, so on iOS — where the edge
|
|
32
|
+
> swipe-back *is* what a real user does — a scenario named for the gesture never performs it. If
|
|
33
|
+
> the edge swipe itself is under test, that needs a left-edge `mobile: swipe`, which no step
|
|
34
|
+
> currently emits: tag it `@manual` rather than letting the wording imply coverage it does not have.
|
|
20
35
|
> - **`pull-to-refresh on [X]`** → fast `mobile: swipeGesture` (direction down).
|
|
21
36
|
> - **`pinch-zoom in|out on [X]`** → `mobile: pinchOpenGesture` / `pinchCloseGesture`.
|
|
22
37
|
> - **`send app to background for N seconds`** → `driver.background(N)`.
|
|
@@ -108,7 +123,7 @@ from `appium_find_element`; screen gestures pass `direction` or coordinates.
|
|
|
108
123
|
| Scroll a list | `User scroll down on [Feed]` | `action=scroll, direction=down` |
|
|
109
124
|
| Scroll until visible | `User scroll to [Footer]` | `action=scroll_to_element, strategy, selector, direction` |
|
|
110
125
|
| Pinch zoom in/out | `User pinch-zoom in on [Map]` | `action=pinch_zoom, elementUUID, scale` (>1 in, <1 out) |
|
|
111
|
-
| System back | `User go back` | `action=back` |
|
|
126
|
+
| System back | `User go back` | `action=back` — **compiles** (see codegen status; per-OS) |
|
|
112
127
|
| Drag & drop | `User drag [A] onto [B]` | `appium_drag_and_drop` (separate tool) |
|
|
113
128
|
|
|
114
129
|
Other device-level actions (separate MCP tools for exploration; Gherkin now compiles — see codegen
|
|
@@ -96,6 +96,7 @@ When running Phase 0 for a **flow** (`qa/flows/<name>/`), check existing screen
|
|
|
96
96
|
- Toast/alert named from content (no aria-label) → `feedback.toast: { role: status, named_by: content }` + `name: null` on the selector entries.
|
|
97
97
|
- Loading indicator for `wait for [T] table to refresh` → `feedback.loading.indicator` (skip if the app sets `aria-busy`).
|
|
98
98
|
- **Every qa/app.yaml edit MUST carry provenance** in `detection:`: who/when (`detected_by`) and the observed evidence per key (`evidence:`). A profile without evidence is a guess a human cannot review.
|
|
99
|
+
7c. **Checkbox-like control that is not a checkbox** (`.check()` failed with `Not a checkbox or radio button`; the snapshot shows a `<div>`/`<button>` tile with no `role="checkbox"` and no `aria-checked`): keep the locator that finds the tile and add the state signal to the SAME entry — `checked: { class: '<class present when on>' }` (a class on that element itself, e.g. `bg-selected`) or `checked: { attribute: '<attr>' }` (a `'true'`/`'false'` attribute such as `data-checked`); on a card/tab picker write `selected:` instead — same signal, different spelling. This is what makes `check`/`uncheck`/`is checked`/`is selected` compile for the tile. ONE plain class token or attribute name only — whitespace or a quoted fragment is refused at compile time, naming the entry. Same shape as `expanded:` for accordions; `checked.class` matches on the located element OR a descendant carrying it (the checkmark icon), while `checked.attribute` is read on the located element itself.
|
|
99
100
|
8. **Merge, don't overwrite**: preserve the page selector and any user-authored entries in `selectors.yaml`. Only add missing keys.
|
|
100
101
|
- **Type-collision check** (see `sungen-selector-keys` § Collision rule): before writing a base key, check whether it already exists with a *different* `type`. If so, give **both** entries a `--type` suffix instead of clobbering or sharing the base key — a base-key entry of the wrong type gets picked up by any step referencing that label. Watch `page` especially: a `[X] page` step reads `value` as a URL, so if `X` already holds a button (`value: 'button'`) the compiler won't use the button's value as a URL, but it can't invent the real one either — it falls back to the feature's own path and warns `SG-W013`, asserting a page that isn't `X`'s. Write `X--page` (URL from the live page / feature `Path:`) alongside `X--button`.
|
|
101
102
|
9. **Show summary + confirm**: list the keys that will be added, ask the user to approve, then write the file.
|
|
@@ -428,6 +429,17 @@ users:
|
|
|
428
429
|
|
|
429
430
|
**How to build `columns`**: count column headers in snapshot (left to right, 0-indexed). Map each `[Col] column` reference from feature file to its index.
|
|
430
431
|
|
|
432
|
+
**A list built from `<div>`s (no `<table>`, no `role="row"` in the snapshot)** — the table steps still work, but you must declare the row SHAPE on the table entry, or every row step resolves 0 rows (`is hidden`/`is empty` pass vacuously — `SG-W016` warns about exactly this):
|
|
433
|
+
|
|
434
|
+
```yaml
|
|
435
|
+
job list:
|
|
436
|
+
type: 'locator'
|
|
437
|
+
value: 'div.job-list'
|
|
438
|
+
rows: 'div.job-row' # RELATIVE to the table: what every row matches
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
Pick `rows` from the snapshot as the repeating child that holds one record (a class like `.job-row`, `[data-testid="job-item"]`, or `> div`). Do **not** put it on a row label's entry — that key positions ONE row (`div.job-row:first-child`) and strict-mode-fails when it matches many.
|
|
442
|
+
|
|
431
443
|
---
|
|
432
444
|
|
|
433
445
|
## Detail Screens with Dynamic IDs
|
|
@@ -219,35 +219,49 @@ If no YAML key exists, the resolver infers from the Gherkin element type:
|
|
|
219
219
|
| `[X] button` | `getByRole('button', { name: 'X' })` | Button's accessible name = X |
|
|
220
220
|
| `[X] link` | `getByRole('link', { name: 'X' })` | Link text = X |
|
|
221
221
|
| `[X] heading` / `header` | `getByRole('heading', { name: 'X' })` | Heading text = X |
|
|
222
|
-
| `[X] checkbox` | `getByRole('checkbox', { name: 'X' })` | Checkbox label = X |
|
|
222
|
+
| `[X] checkbox` | `getByRole('checkbox', { name: 'X' })` | Checkbox label = X — a checkbox-LIKE tile with no `role`/`aria-checked` needs an explicit entry plus `checked: { class: <cls> }` or `checked: { attribute: <attr> }` (read on the located element itself; `expanded.class` reads a descendant) |
|
|
223
223
|
| `[X] radio` | `getByRole('radio', { name: 'X' })` | Radio label = X |
|
|
224
224
|
| `[X] field` | `getByPlaceholder('X')` | Placeholder text = X AND field has a placeholder |
|
|
225
225
|
| `[X] text` / `message` / `label` | `getByText('X')` | Visible text = X (partial match) |
|
|
226
226
|
| `[X] logo/image/icon` | `getByRole('img', { name: 'X' })` | Image alt = X |
|
|
227
227
|
| `[X] search` | `getByRole('searchbox', { name: 'X' })` | Searchbox label = X |
|
|
228
|
-
| `[X] option` | `getByRole('option'
|
|
228
|
+
| `[X] option` | `getByRole('option')` — **no name** | web only (see the list-member note below) |
|
|
229
229
|
| `[X] slider` | `getByRole('slider', { name: 'X' })` | Slider label = X |
|
|
230
230
|
| `[X] toggle` | `getByRole('switch', { name: 'X' })` | Toggle label = X |
|
|
231
231
|
| `[X] tab` | `getByRole('tab', { name: 'X' })` | Tab text = X |
|
|
232
232
|
| `[X] table` | `getByRole('table', { name: 'X' })` | Table aria-label = X |
|
|
233
233
|
| `[X] list` | `getByRole('list', { name: 'X' })` | List aria-label = X |
|
|
234
|
-
| `[X] column` | `getByRole('columnheader', { name: 'X' })` | Column header text = X |
|
|
234
|
+
| `[X] column` | `getByRole('columnheader', { name: 'X' })` | Column header text = X — **overridable**, see the column-header note below |
|
|
235
235
|
| `[X] dialog` / `modal` / `drawer` | `getByRole('dialog', { name: 'X' })` | Dialog aria-label/heading = X |
|
|
236
236
|
| `[X] dropdown` / `select` | `getByRole('combobox', { name: 'X' })` | Combobox label = X |
|
|
237
237
|
| `[X] menuitem` | `getByRole('menuitem', { name: 'X' })` | Menu item text = X |
|
|
238
238
|
| `[X] progressbar` | `getByRole('progressbar', { name: 'X' })` | Progressbar label = X |
|
|
239
239
|
| `[X] section` | `getByRole('region', { name: 'X' })` | Section aria-label = X |
|
|
240
240
|
| `[X] card` | `getByRole('article', { name: 'X' })` | Card aria-label = X |
|
|
241
|
-
| `[X] item` | `getByRole('listitem'
|
|
242
|
-
| `[X] cell` | `getByRole('cell'
|
|
241
|
+
| `[X] item` | `getByRole('listitem')` — **no name** | web only (see the list-member note below) |
|
|
242
|
+
| `[X] cell` | `getByRole('cell')` — **no name** | web only (see the list-member note below) |
|
|
243
243
|
| `[X] spinner` | `getByRole('status', { name: 'X' })` | Spinner aria-label = X |
|
|
244
244
|
| `[X] breadcrumb` | `getByRole('navigation', { name: 'X' })` | Navigation aria-label = X |
|
|
245
245
|
| `[X] badge` / `tooltip` / `tag` | `getByText('X')` | Visible text = X |
|
|
246
246
|
|
|
247
|
+
**Column headers: the `[Reference]` is not required to be the DOM text.** `[X] column` defaults its accessible name to the reference, which is unmatchable when the page is not in the reference's language (a `[Category]` column whose header reads 記事分類). Declare the real header instead of renaming the reference — the reference is what ships in the delivered test case. Most specific first: the table entry's `columns.<col>.header`, then a `<col>--columnheader` entry's `name:`, then the reference text. A `header` may hold `{{variables}}` for per-locale text, like any `name:`. A same-named entry that is NOT a column header is ignored and cannot hijack the lookup.
|
|
248
|
+
|
|
249
|
+
**Special note on the list members `[X] item` / `[X] option` / `[X] cell`** — the only rows above that behave differently per platform:
|
|
250
|
+
|
|
251
|
+
- **Web**: they infer the role with **no `name`**, on purpose. A list member takes its accessible name from its own CONTENT, so a name defaulted from the `[Reference]` text matches zero elements — and `toBeHidden()` PASSES on an empty locator, so the scenario would go green having proved nothing. The role alone is the whole locator; `nth` or a count assertion narrows it. An explicit `name:` you write yourself is always honoured.
|
|
252
|
+
- **Mobile**: they do **not** auto-infer at all. A native a11y tree has no role concept, so with no name there is nothing to build an accessibility-id from — the old behaviour emitted the role token itself (`~listitem`), which no tree ever contains. `sungen generate` now fails naming the key to add, and selector generation (Phase 0) scans the live page for it instead of skipping. **Write an explicit entry**, e.g. `job list--item: { type: accessibility-id, value: 'job-row' }`.
|
|
253
|
+
|
|
254
|
+
**On mobile, `type: role` needs a `name:`.** The rule above is not about these three types — it is about a role with **no accessible name**, whichever way it got that way. On mobile such a role compiles to the role token (`~status`) and can never match, so it is refused at compile time. That includes `name: null`, which is the web opt-out for a content-named role: it has no mobile meaning, and `SG-W003` (which recommends it) does not fire on mobile for that reason. On a native app, prefer `type: accessibility-id`, `type: xpath` or `type: id` over `type: role` — the device exposes content-desc / accessibilityIdentifier, not ARIA roles.
|
|
255
|
+
|
|
247
256
|
**Special note on `[X] field`**: `getByPlaceholder('X')` only works when (1) the field has a placeholder attribute AND (2) the placeholder text equals X. For fields without placeholders (floating labels, aria-label), write explicit YAML: `type: label, value: "Actual label text"`.
|
|
248
257
|
|
|
249
258
|
**Only add a YAML entry when** auto-infer cannot work: DOM name differs from Gherkin label, need `testid`, need `nth`, need `exact: true`, need `name: null`, or the field type requires explicit config.
|
|
250
259
|
|
|
260
|
+
**`nth:` takes an integer `>= -1`** — `0`, `1`, `2`… are zero-based indexes and `-1` is the LAST
|
|
261
|
+
match (`.last()` on web, `.at(-1)` on mobile). Nothing else is an index: `nth: -2`, `nth: 'last'`
|
|
262
|
+
and `nth: 1.5` are ignored, the selector stops filtering by position, and the compiler reports
|
|
263
|
+
`SG-W018`. Reach for `nth` only when `exact: true` or `scope` cannot disambiguate.
|
|
264
|
+
|
|
251
265
|
### `name: null` — name-from-content roles (toast, alert, spinner)
|
|
252
266
|
|
|
253
267
|
For a `type: role` entry **without** `name:`, the compiler defaults the accessible-name filter to the Gherkin `[Reference]` text. That is right for buttons/links — and **always wrong** for roles whose accessible name derives from their CONTENT (`status`, `alert`, `log`, `marquee`, `timer`): a toast's name is its message text, never the string "Success Toast". The compiler warns (`SG-W003`). Opt out explicitly:
|
|
@@ -309,7 +309,7 @@ Security: [S1 – admin only]
|
|
|
309
309
|
**Depth is a GATE dimension (harness-roadmap P1) — self-raise, never silently go shallow:**
|
|
310
310
|
- For every data-correctness theme the catalog marks `depth.requires: data-assertion`, emit its `depth.template` shape by **default** — don't wait for the repair loop. `sungen audit` measures `businessDepth` (ratio of these scenarios that assert data) against an intent threshold (functional ≥ 0.70); below it the **gate FAILs**.
|
|
311
311
|
- **Verify depth deterministically before the gate:** run `sungen depth-lint --screen <name>`. It classifies every shallow business-critical scenario into **deepen-in-place** (add the theme's value assertion — the printed `template` is a hint, fit it to the actual claim) vs **cross-screen** (route to a flow / `@manual:Mx`). Clear the `deepen` list first — this is the mechanical way to hit `businessDepth` on the first pass instead of churning repair rounds. Never fake a value assertion onto a visibility/behavior scenario the lint over-counts; leave it and note the over-count.
|
|
312
|
-
- `depth.cross_screen: true` (cart / detail / filter / brand correctness) → write the deep capture/compare shape as an **automated flow scenario** (in the flow — do NOT leave a full-step `@manual` duplicate on the screen). `@manual` is **only** for genuine judgment (M6 visual/UX · M8 not-worth · M9 human) or a missing capability (M1–M5/M7), and it **must** carry a reason code (`@manual:Mx`, or a reason comment the planner can infer). A `@manual` scenario that still has full automatable steps (a data assertion, no visual/mock/a11y judgment) is now flagged by `sungen audit` as `MANUAL-AUTOMATABLE`, and business-critical scenarios you defer to `@manual` are reported as `DEPTH-DEFERRED` (they do NOT silently inflate `businessDepth`). Deferring automatable work to `@manual` lowers quality — automate it in the flow instead.
|
|
312
|
+
- `depth.cross_screen: true` (cart / detail / filter / brand correctness) → write the deep capture/compare shape as an **automated flow scenario** (in the flow — do NOT leave a full-step `@manual` duplicate on the screen). `@manual` is **only** for genuine judgment (M6 visual/UX · M8 not-worth · M9 human), a device/OS limit on mobile (M10 OS capability · M11 real-device only), or a missing capability (M1–M5/M7), and it **must** carry a reason code (`@manual:Mx`, or a reason comment the planner can infer). A `@manual` scenario that still has full automatable steps (a data assertion, no visual/mock/a11y judgment) is now flagged by `sungen audit` as `MANUAL-AUTOMATABLE`, and business-critical scenarios you defer to `@manual` are reported as `DEPTH-DEFERRED` (they do NOT silently inflate `businessDepth`). Deferring automatable work to `@manual` lowers quality — automate it in the flow instead.
|
|
313
313
|
- **Pick the right `@manual:Mx` code — it decides which driver can later automate the case** (`sungen audit` flags a code↔reason mismatch). Tag the code that matches the **oracle the reason describes**:
|
|
314
314
|
|
|
315
315
|
| The reason needs… | Code | Unblocked by |
|
|
@@ -321,9 +321,11 @@ Security: [S1 – admin only]
|
|
|
321
321
|
| an external dependency (email, payment gateway, download) | `M5` | mail-file / contract |
|
|
322
322
|
| visual / UX / responsive / a11y judgment | `M6` | — (keep manual) |
|
|
323
323
|
| not worth automating · true human judgment | `M8` / `M9` | — (keep manual) |
|
|
324
|
+
| **mobile** — what the OS/driver will not expose (notification shade, app switcher/recents, permission variance by OS version, call interruption) | `M10` | — (keep manual) |
|
|
325
|
+
| **mobile** — what an emulator/simulator cannot be (biometrics, real camera, NFC, a real push token) | `M11` | — (keep manual) |
|
|
324
326
|
|
|
325
327
|
e.g. "submit a payload then check the subscribers **table**" is an API+DB oracle → `@manual:M2` (NOT `M1`); "seed a DB with zero products" is a data state → `M1`; "throttle the network" → `M3`.
|
|
326
|
-
- **Prefer automation-ready `@requires:<cap>` over prose `@manual`.** When you *can* write the steps for a capability-manual case (an API/DB oracle, a seeded state), write it **automation-ready** — the real `@api`/`@query`/… steps tagged `@requires:<cap>` (e.g. `@requires:db @query:subscriber_row`) — instead of a prose `@manual:M2`. It compiles to a skipped-with-reason stub until `sungen capability add <cap>`, then runs as a real test with **no rewrite**. Reserve prose `@manual:Mx` for cases whose steps genuinely can't be expressed (M6/M8/M9 judgment, or a capability with no driver). `sungen audit` reports these as `AUTOMATION-READY-PENDING` (not a gap, not manual).
|
|
328
|
+
- **Prefer automation-ready `@requires:<cap>` over prose `@manual`.** When you *can* write the steps for a capability-manual case (an API/DB oracle, a seeded state), write it **automation-ready** — the real `@api`/`@query`/… steps tagged `@requires:<cap>` (e.g. `@requires:db @query:subscriber_row`) — instead of a prose `@manual:M2`. It compiles to a skipped-with-reason stub until `sungen capability add <cap>`, then runs as a real test with **no rewrite**. Reserve prose `@manual:Mx` for cases whose steps genuinely can't be expressed (M6/M8/M9 judgment, M10/M11 device-or-OS limits on mobile, or a capability with no driver). `sungen audit` reports these as `AUTOMATION-READY-PENDING` (not a gap, not manual).
|
|
327
329
|
- **If the spec lacks the concrete value** a deep assertion needs (exact message, price, count): still write the deep shape with a `{{var}}` placeholder and leave a `# SPEC-GAP: <field> value not in spec` comment — do **not** downgrade to `see [X] section`. A visible gap is better than a silent shallow pass.
|
|
328
330
|
- **Blind-Spot Memory:** before finishing, run `sungen blindspot list --prompt` (Bash) and make sure the suite satisfies each recorded pattern (e.g. "for any Add/Create action: check success + resulting data state + duplicate/double-submit"). These are gaps QA hit before — don't repeat them.
|
|
329
331
|
|
|
@@ -478,6 +480,90 @@ And User wait for [Page Title] heading is visible
|
|
|
478
480
|
|
|
479
481
|
---
|
|
480
482
|
|
|
483
|
+
## Mobile — the two axes web has no concept of
|
|
484
|
+
|
|
485
|
+
Applies to a `@platform:mobile/android/ios` unit. Read `sungen-viewpoint` →
|
|
486
|
+
`group-f-mobile-native.md` for *what to check* per native pattern (it carries the theme↔catalog
|
|
487
|
+
map the audit scores against); this section is the two **cross-cutting** axes that multiply every
|
|
488
|
+
other scenario on a device, and that a web-shaped coverage map has no row for.
|
|
489
|
+
|
|
490
|
+
### Axis 1 — the app lifecycle state machine
|
|
491
|
+
|
|
492
|
+
A browser tab is either open or closed. An app has five states, and a screen can be wrong in any of
|
|
493
|
+
them. Generate lifecycle scenarios from the transitions, not from the states.
|
|
494
|
+
|
|
495
|
+
```
|
|
496
|
+
launch (cold)
|
|
497
|
+
│
|
|
498
|
+
▼
|
|
499
|
+
┌──────────────► FOREGROUND ──────────────┐
|
|
500
|
+
│ │ │
|
|
501
|
+
│ background N seconds restart (kill)
|
|
502
|
+
│ ▼ ▼
|
|
503
|
+
│ BACKGROUNDED RELAUNCHED ── reinstall ──► FIRST-RUN
|
|
504
|
+
│ │ (install state cleared)
|
|
505
|
+
└──── resume ─────────┤
|
|
506
|
+
▼
|
|
507
|
+
PROCESS DEATH (the OS reclaims a backgrounded app)
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
| Transition | Step | Availability |
|
|
511
|
+
|---|---|---|
|
|
512
|
+
| foreground → backgrounded → resumed | `User background the app for 5 seconds` | both OSes |
|
|
513
|
+
| foreground → killed → relaunched | `User restart the app` | both OSes — **the** persistence oracle |
|
|
514
|
+
| → first-run | `User reinstall the app` | both OSes; needs the session to have installed via `APP_APK` / `IOS_APP` |
|
|
515
|
+
| a clean state *between* scenarios | `@reset:none\|soft\|data\|full` on the Feature | a tag, never a step — a step that tries reports **SG-W023** |
|
|
516
|
+
| backgrounded → process death → resumed | *no step* | `@manual:M10` (OS capability) |
|
|
517
|
+
|
|
518
|
+
**Every lifecycle scenario needs a DATA oracle.** "The screen is still there" passes on an empty
|
|
519
|
+
shell and on a silently re-logged-in session. Assert the value that must have survived:
|
|
520
|
+
|
|
521
|
+
```gherkin
|
|
522
|
+
@high @platform:mobile
|
|
523
|
+
Scenario: VP-LOGIC-001 The session survives a cold restart
|
|
524
|
+
Given User is on [Account] screen
|
|
525
|
+
When User restart the app
|
|
526
|
+
Then User see [Account Name] text contains {{user_name}}
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
Minimum for any mobile unit: **one resume scenario** (the screen owes its own data back) and, when
|
|
530
|
+
the screen persists anything — a session, a setting, a dismissal, a show-once card — **one restart
|
|
531
|
+
scenario per persisted thing**. Rotation belongs here too: `User rotate to landscape` must not lose
|
|
532
|
+
entered values or fire the request twice.
|
|
533
|
+
|
|
534
|
+
### Axis 2 — permission × network decision table
|
|
535
|
+
|
|
536
|
+
These two multiply: a permission-gated feature behaves differently offline, and the honest expected
|
|
537
|
+
result is often "which message wins". Build the table, then **test only the rows with distinct
|
|
538
|
+
outcomes** — the usual decision-table discipline, not all 12 combinations.
|
|
539
|
+
|
|
540
|
+
| # | Permission | Network | Expected | How |
|
|
541
|
+
|---|---|---|---|---|
|
|
542
|
+
| 1 | granted | online | the happy path, feature content asserted | `User grant camera permission` — both OSes (iOS Simulator needs `applesimutils`) |
|
|
543
|
+
| 2 | granted | offline | the spec's cached/queued behaviour + a clear message; entered data never silently lost | `User turn wifi off` → `@platform:android` (XCUITest cannot toggle connectivity); iOS is `@manual:M10` |
|
|
544
|
+
| 3 | granted | off → on | recovers and refreshes with no manual reload | `@platform:android` |
|
|
545
|
+
| 4 | denied | online | an in-app explanation with a route to Settings — never a crash or a dead button | `@manual:M10` — no step denies a permission |
|
|
546
|
+
| 5 | denied | offline | one message wins per spec; do NOT generate this row unless the spec says which | `@manual:M10` |
|
|
547
|
+
| 6 | never-ask-again | online | no request loop; the Settings route is shown | `@manual:M10` |
|
|
548
|
+
| 7 | revoked while running | online | the app re-checks instead of trusting a stale grant | `@manual:M10` |
|
|
549
|
+
|
|
550
|
+
Row 1 is `@high` and automated. Rows 2–3 are `@high` and automated **on Android only** — tag them,
|
|
551
|
+
do not write them platform-neutral and hope. Rows 4–7 carry `@manual:M10`, which is a reason code
|
|
552
|
+
and a real oracle, not a shrug: write the steps a human follows and the value they must see.
|
|
553
|
+
|
|
554
|
+
### Two mobile traps worth naming while generating
|
|
555
|
+
|
|
556
|
+
- **A `page` assertion proves nothing on mobile.** A native app has no URL, so `Then User see [Login]
|
|
557
|
+
page` compiles to an empty comment (**SG-W022**) and the scenario passes without looking. The
|
|
558
|
+
`screen` spelling is different and is what you want: it treats the target as a plain element and
|
|
559
|
+
emits a real visibility assertion — so name a marker only that screen has (`Then User see [Login
|
|
560
|
+
Submit] button`, or `Then User see [Login] screen` against a screen-root selector), and pair it
|
|
561
|
+
with the previous screen's marker going `is hidden`.
|
|
562
|
+
- **An `open [X] page` / `navigate to` step on mobile navigates nowhere** (**SG-W026**) — the app was
|
|
563
|
+
already launched by the capabilities. Change screens with a real tap or gesture.
|
|
564
|
+
|
|
565
|
+
---
|
|
566
|
+
|
|
481
567
|
## Cleanup & Hooks
|
|
482
568
|
|
|
483
569
|
Add cleanup tags per the `sungen-gherkin-syntax` Cleanup table. Key rules:
|