@sun-asterisk/sungen 3.2.0 → 3.2.2-beta.1
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/context-router.d.ts +11 -2
- package/dist/capabilities/context-router.d.ts.map +1 -1
- package/dist/capabilities/context-router.js +10 -3
- package/dist/capabilities/context-router.js.map +1 -1
- package/dist/capabilities/discover.js +1 -1
- package/dist/capabilities/discover.js.map +1 -1
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/audit.js +5 -3
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/delivery.d.ts.map +1 -1
- package/dist/cli/commands/delivery.js +31 -0
- package/dist/cli/commands/delivery.js.map +1 -1
- package/dist/exporters/feature-parser.d.ts +25 -0
- package/dist/exporters/feature-parser.d.ts.map +1 -1
- package/dist/exporters/feature-parser.js +59 -0
- package/dist/exporters/feature-parser.js.map +1 -1
- package/dist/exporters/types.d.ts +38 -0
- package/dist/exporters/types.d.ts.map +1 -1
- package/dist/exporters/xlsx-exporter.d.ts +31 -2
- package/dist/exporters/xlsx-exporter.d.ts.map +1 -1
- package/dist/exporters/xlsx-exporter.js +144 -1
- package/dist/exporters/xlsx-exporter.js.map +1 -1
- package/dist/generators/test-generator/adapters/appium/appium-adapter.d.ts +54 -0
- package/dist/generators/test-generator/adapters/appium/appium-adapter.d.ts.map +1 -0
- package/dist/generators/test-generator/adapters/appium/appium-adapter.js +52 -0
- package/dist/generators/test-generator/adapters/appium/appium-adapter.js.map +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/after-all.hbs +8 -0
- package/dist/generators/test-generator/adapters/appium/templates/after-each.hbs +8 -0
- package/dist/generators/test-generator/adapters/appium/templates/before-all.hbs +8 -0
- package/dist/generators/test-generator/adapters/appium/templates/before-each.hbs +8 -0
- package/dist/generators/test-generator/adapters/appium/templates/imports.hbs +8 -0
- package/dist/generators/test-generator/adapters/appium/templates/scenario.hbs +8 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/alert-accept-action.hbs +4 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/alert-dismiss-action.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/alert-fill-action.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/check-action.hbs +10 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/clear-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/click-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/click-element-with-text.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/click-select-action.hbs +4 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/dismiss-action.hbs +3 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/double-click-action.hbs +6 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/drag-action.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/expand-action.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/fill-action.hbs +14 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/fill-editor-action.hbs +12 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/frame-enter-action.hbs +6 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/frame-exit-action.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/hide-keyboard-action.hbs +4 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/hover-action.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/keyboard-global-action.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/press-action.hbs +4 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/radio-select-action.hbs +4 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/scroll-action.hbs +19 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/select-action.hbs +5 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/table-action-in-row.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/toggle-action.hbs +3 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/uncheck-action.hbs +8 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/unknown-element-action.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/upload-action.hbs +3 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/wait-for-page.hbs +3 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/wait-for-role-with-data.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/wait-for-role.hbs +3 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/alert-text-assertion.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/attribute-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/checked-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/clipboard-text-assertion.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/column-cell-assertion.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/contain-text-assertion.hbs +14 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/count-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/disabled-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/empty-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/enabled-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/focused-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/have-text-assertion.hbs +15 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/have-value-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/is-hidden-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/label-value-assertion.hbs +12 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/list-item-count-assertion.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/loading-assertion.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/not-checked-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/page-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/route-assertion.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/selected-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/sorted-assertion.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/table-column-exists.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/table-empty.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/table-match-data.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/table-row-count.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/table-row-exists.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/table-row-not-exists.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/visible-assertion.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/background-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/grant-permission-action.hbs +11 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/long-press-action.hbs +5 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/open-notifications-action.hbs +3 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/pinch-zoom-action.hbs +5 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/pull-to-refresh-action.hbs +5 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/rotate-action.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/set-clipboard-action.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/set-geolocation-action.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/swipe-action.hbs +5 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/gestures/tap-top-action.hbs +24 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/navigation/navigation.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/navigation/wait-for-element-with-text.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/navigation/wait-for-element.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/navigation/wait-timeout.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/partials/appium-selector-expr.hbs +8 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/partials/appium-selector.hbs +15 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/partials/locator.hbs +8 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/setup/application-running.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/setup/clear-auth.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/setup/clear-browser-state.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/setup/clear-database.hbs +1 -0
- package/dist/generators/test-generator/adapters/appium/templates/steps/setup/user-login-todo.hbs +2 -0
- package/dist/generators/test-generator/adapters/appium/templates/test-file.hbs +226 -0
- package/dist/generators/test-generator/adapters/index.d.ts +1 -0
- package/dist/generators/test-generator/adapters/index.d.ts.map +1 -1
- package/dist/generators/test-generator/adapters/index.js +9 -1
- package/dist/generators/test-generator/adapters/index.js.map +1 -1
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +3 -2
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/generators/test-generator/step-mapper.d.ts +1 -0
- package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.js +7 -37
- package/dist/generators/test-generator/step-mapper.js.map +1 -1
- package/dist/generators/test-generator/template-engine.d.ts.map +1 -1
- package/dist/generators/test-generator/template-engine.js +13 -1
- package/dist/generators/test-generator/template-engine.js.map +1 -1
- package/dist/harness/audit.d.ts +16 -2
- package/dist/harness/audit.d.ts.map +1 -1
- package/dist/harness/audit.js +74 -11
- package/dist/harness/audit.js.map +1 -1
- package/dist/harness/capability-plan.d.ts +2 -0
- package/dist/harness/capability-plan.d.ts.map +1 -1
- package/dist/harness/capability-plan.js +4 -1
- package/dist/harness/capability-plan.js.map +1 -1
- package/dist/harness/catalog/drivers.yaml +1 -1
- package/dist/harness/flow-check.d.ts.map +1 -1
- package/dist/harness/flow-check.js +13 -4
- package/dist/harness/flow-check.js.map +1 -1
- package/dist/harness/parse.d.ts +2 -0
- package/dist/harness/parse.d.ts.map +1 -1
- package/dist/harness/parse.js +13 -2
- package/dist/harness/parse.js.map +1 -1
- package/dist/harness/quality-gates.d.ts +6 -0
- package/dist/harness/quality-gates.d.ts.map +1 -1
- package/dist/harness/quality-gates.js +15 -1
- package/dist/harness/quality-gates.js.map +1 -1
- package/dist/harness/sensors.d.ts +27 -0
- package/dist/harness/sensors.d.ts.map +1 -1
- package/dist/harness/sensors.js +91 -21
- package/dist/harness/sensors.js.map +1 -1
- package/dist/orchestrator/ai-rules-updater.d.ts.map +1 -1
- package/dist/orchestrator/ai-rules-updater.js +9 -0
- package/dist/orchestrator/ai-rules-updater.js.map +1 -1
- package/dist/orchestrator/templates/ai-instructions/claude-agent-generator.md +44 -0
- package/dist/orchestrator/templates/ai-instructions/claude-cmd-create-test.md +18 -1
- package/dist/orchestrator/templates/ai-instructions/claude-skill-capture-mobile.md +184 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-delivery.md +27 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +2 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-mobile-gestures.md +109 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-selector-fix-mobile.md +316 -0
- package/dist/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +2 -0
- package/dist/orchestrator/templates/ai-instructions/copilot-cmd-create-test.md +2 -1
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mobile.md +184 -0
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-delivery.md +27 -0
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +2 -0
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-mobile-gestures.md +109 -0
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix-mobile.md +316 -0
- package/dist/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +2 -0
- package/dist/orchestrator/templates/env.appium.example +25 -0
- package/dist/orchestrator/templates/specs-api.d.ts +7 -0
- package/dist/orchestrator/templates/specs-api.d.ts.map +1 -1
- package/dist/orchestrator/templates/specs-api.js +13 -2
- package/dist/orchestrator/templates/specs-api.js.map +1 -1
- package/dist/orchestrator/templates/specs-api.ts +13 -2
- package/dist/orchestrator/templates/specs-pw-shape-reporter.ts +92 -0
- package/dist/orchestrator/templates/wdio.conf.ts +295 -0
- package/dist/utils/selector-types.d.ts +1 -1
- package/dist/utils/selector-types.d.ts.map +1 -1
- package/dist/utils/selector-types.js +5 -0
- package/dist/utils/selector-types.js.map +1 -1
- package/package.json +4 -4
- package/src/capabilities/context-router.ts +15 -3
- package/src/capabilities/discover.ts +1 -1
- package/src/cli/commands/audit.ts +5 -3
- package/src/cli/commands/delivery.ts +32 -2
- package/src/exporters/feature-parser.ts +57 -0
- package/src/exporters/types.ts +38 -0
- package/src/exporters/xlsx-exporter.ts +176 -2
- package/src/generators/test-generator/adapters/appium/appium-adapter.ts +57 -0
- package/src/generators/test-generator/adapters/appium/templates/after-all.hbs +8 -0
- package/src/generators/test-generator/adapters/appium/templates/after-each.hbs +8 -0
- package/src/generators/test-generator/adapters/appium/templates/before-all.hbs +8 -0
- package/src/generators/test-generator/adapters/appium/templates/before-each.hbs +8 -0
- package/src/generators/test-generator/adapters/appium/templates/imports.hbs +8 -0
- package/src/generators/test-generator/adapters/appium/templates/scenario.hbs +8 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/alert-accept-action.hbs +4 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/alert-dismiss-action.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/alert-fill-action.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/check-action.hbs +10 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/clear-action.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/click-action.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/click-element-with-text.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/click-select-action.hbs +4 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/dismiss-action.hbs +3 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/double-click-action.hbs +6 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/drag-action.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/expand-action.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/fill-action.hbs +14 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/fill-editor-action.hbs +12 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/frame-enter-action.hbs +6 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/frame-exit-action.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/hide-keyboard-action.hbs +4 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/hover-action.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/keyboard-global-action.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/press-action.hbs +4 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/radio-select-action.hbs +4 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/scroll-action.hbs +19 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/select-action.hbs +5 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/table-action-in-row.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/toggle-action.hbs +3 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/uncheck-action.hbs +8 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/unknown-element-action.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/upload-action.hbs +3 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/wait-for-page.hbs +3 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/wait-for-role-with-data.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/wait-for-role.hbs +3 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/alert-text-assertion.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/attribute-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/checked-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/clipboard-text-assertion.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/column-cell-assertion.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/contain-text-assertion.hbs +14 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/count-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/disabled-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/empty-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/enabled-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/focused-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/have-text-assertion.hbs +15 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/have-value-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/is-hidden-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/label-value-assertion.hbs +12 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/list-item-count-assertion.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/loading-assertion.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/not-checked-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/page-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/route-assertion.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/selected-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/sorted-assertion.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/table-column-exists.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/table-empty.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/table-match-data.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/table-row-count.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/table-row-exists.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/table-row-not-exists.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/visible-assertion.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/gestures/background-action.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/gestures/grant-permission-action.hbs +11 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/gestures/long-press-action.hbs +5 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/gestures/open-notifications-action.hbs +3 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/gestures/pinch-zoom-action.hbs +5 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/gestures/pull-to-refresh-action.hbs +5 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/gestures/rotate-action.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/gestures/set-clipboard-action.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/gestures/set-geolocation-action.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/gestures/swipe-action.hbs +5 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/gestures/tap-top-action.hbs +24 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/navigation/navigation.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/navigation/wait-for-element-with-text.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/navigation/wait-for-element.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/navigation/wait-timeout.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/partials/appium-selector-expr.hbs +8 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/partials/appium-selector.hbs +15 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/partials/locator.hbs +8 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/setup/application-running.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/setup/clear-auth.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/setup/clear-browser-state.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/setup/clear-database.hbs +1 -0
- package/src/generators/test-generator/adapters/appium/templates/steps/setup/user-login-todo.hbs +2 -0
- package/src/generators/test-generator/adapters/appium/templates/test-file.hbs +226 -0
- package/src/generators/test-generator/adapters/index.ts +7 -0
- package/src/generators/test-generator/code-generator.ts +3 -2
- package/src/generators/test-generator/step-mapper.ts +8 -5
- package/src/generators/test-generator/template-engine.ts +13 -1
- package/src/harness/audit.ts +84 -14
- package/src/harness/capability-plan.ts +5 -2
- package/src/harness/catalog/drivers.yaml +1 -1
- package/src/harness/flow-check.ts +13 -4
- package/src/harness/parse.ts +15 -2
- package/src/harness/quality-gates.ts +14 -1
- package/src/harness/sensors.ts +110 -22
- package/src/orchestrator/ai-rules-updater.ts +9 -0
- package/src/orchestrator/templates/ai-instructions/claude-agent-generator.md +44 -0
- package/src/orchestrator/templates/ai-instructions/claude-cmd-create-test.md +18 -1
- package/src/orchestrator/templates/ai-instructions/claude-skill-capture-mobile.md +184 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-delivery.md +27 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-gherkin-syntax.md +2 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-mobile-gestures.md +109 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-selector-fix-mobile.md +316 -0
- package/src/orchestrator/templates/ai-instructions/claude-skill-tc-generation.md +2 -0
- package/src/orchestrator/templates/ai-instructions/copilot-cmd-create-test.md +2 -1
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-capture-mobile.md +184 -0
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-delivery.md +27 -0
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-gherkin-syntax.md +2 -0
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-mobile-gestures.md +109 -0
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-selector-fix-mobile.md +316 -0
- package/src/orchestrator/templates/ai-instructions/github-skill-sungen-tc-generation.md +2 -0
- package/src/orchestrator/templates/env.appium.example +25 -0
- package/src/orchestrator/templates/specs-api.ts +13 -2
- package/src/orchestrator/templates/specs-pw-shape-reporter.ts +92 -0
- package/src/orchestrator/templates/wdio.conf.ts +295 -0
- package/src/utils/selector-types.ts +5 -0
|
@@ -164,6 +164,9 @@ export function splitVpAndName(scenarioName: string): { vpId?: string; category1
|
|
|
164
164
|
* Map VP prefix to Category 2.
|
|
165
165
|
* XSS/Injection security tests are input-validation tests → Function.
|
|
166
166
|
* All other VP-SEC tests (auth, RBAC, access control) → Accessing.
|
|
167
|
+
* API auth viewpoints (missing/invalid/insufficient credentials) are access-control
|
|
168
|
+
* tests → Accessing; every other API viewpoint (contract, error, idempotency, flow,
|
|
169
|
+
* async, and the numbered baseline ids) is functional → Function.
|
|
167
170
|
*/
|
|
168
171
|
export function mapVpToCategory2(vpId: string | undefined, scenarioName?: string): string {
|
|
169
172
|
if (!vpId) return 'Function';
|
|
@@ -171,12 +174,66 @@ export function mapVpToCategory2(vpId: string | undefined, scenarioName?: string
|
|
|
171
174
|
if (scenarioName && /xss|injection/i.test(scenarioName)) return 'Function';
|
|
172
175
|
return 'Accessing';
|
|
173
176
|
}
|
|
177
|
+
if (vpId.startsWith('VP-API-')) {
|
|
178
|
+
if (vpId.startsWith('VP-API-AUTH')) return 'Accessing';
|
|
179
|
+
return 'Function';
|
|
180
|
+
}
|
|
174
181
|
if (vpId.startsWith('VP-UI-')) return 'GUI';
|
|
175
182
|
if (vpId.startsWith('VP-VAL-')) return 'Function';
|
|
176
183
|
if (vpId.startsWith('VP-LOGIC-')) return 'Function';
|
|
177
184
|
return 'Function';
|
|
178
185
|
}
|
|
179
186
|
|
|
187
|
+
// ---------------------------------------------------------------------------
|
|
188
|
+
// API annotation helpers
|
|
189
|
+
// Used by the XLSX API-detail sheet only — no effect on CSV/BM-2-901-13 path.
|
|
190
|
+
// ---------------------------------------------------------------------------
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Extract ordered @api:<name> call sequence from feature-level or scenario tags.
|
|
194
|
+
* Tags may contain call arguments: @api:register(name={{x}},email={{y}}) — we strip
|
|
195
|
+
* the argument parens and keep only the endpoint name.
|
|
196
|
+
*
|
|
197
|
+
* Example: ["@api:register(name={{n}})", "@api:count_users(email={{e}})"]
|
|
198
|
+
* → ["register", "count_users"]
|
|
199
|
+
*/
|
|
200
|
+
export function extractApiCallOrder(tags: string[]): string[] {
|
|
201
|
+
return tags
|
|
202
|
+
.filter((t) => t.startsWith('@api:'))
|
|
203
|
+
.map((t) => {
|
|
204
|
+
const body = t.slice('@api:'.length);
|
|
205
|
+
const parenIdx = body.indexOf('(');
|
|
206
|
+
return parenIdx >= 0 ? body.slice(0, parenIdx) : body;
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Extract the @cases:<dataset> name from tags (returns the first one found, or null).
|
|
212
|
+
* Used to label the "cases" annotation so the detail sheet can note which scenarios
|
|
213
|
+
* exercise a given endpoint with a matrix of input/status pairs.
|
|
214
|
+
*/
|
|
215
|
+
export function extractCasesDataset(tags: string[]): string | null {
|
|
216
|
+
const tag = tags.find((t) => t.startsWith('@cases:'));
|
|
217
|
+
return tag ? tag.slice('@cases:'.length) : null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Extract the concurrency invariant text from @concurrent:<N> and @query:<oracle> tags.
|
|
222
|
+
* N is the number of parallel fires; the invariant the band proves is exactly-one success
|
|
223
|
+
* (regardless of N), cross-checked by the @query DB oracle. Returns e.g.
|
|
224
|
+
* "2× parallel → exactly-one; @query user_count", or '' when absent.
|
|
225
|
+
*/
|
|
226
|
+
export function extractConcurrencyInvariant(tags: string[]): string {
|
|
227
|
+
const concurrentTag = tags.find((t) => t.startsWith('@concurrent:'));
|
|
228
|
+
if (!concurrentTag) return '';
|
|
229
|
+
const n = concurrentTag.slice('@concurrent:'.length);
|
|
230
|
+
const queryTag = tags.find((t) => t.startsWith('@query:'));
|
|
231
|
+
const oracle = queryTag ? queryTag.slice('@query:'.length).split('(')[0] : '';
|
|
232
|
+
const parts = [`${n}× parallel → exactly-one`];
|
|
233
|
+
if (oracle) parts.push(`@query ${oracle}`);
|
|
234
|
+
return parts.join('; ');
|
|
235
|
+
}
|
|
236
|
+
|
|
180
237
|
/**
|
|
181
238
|
* Generate TC ID, namespaced by screen/flow so it is globally unique across the
|
|
182
239
|
* whole project. This matters because the dashboard tracks each test case by its
|
package/src/exporters/types.ts
CHANGED
|
@@ -128,3 +128,41 @@ export interface EnvironmentInfo {
|
|
|
128
128
|
projectName: string;
|
|
129
129
|
executor: string;
|
|
130
130
|
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* One row in the "API detail" worksheet — one row per catalog endpoint.
|
|
134
|
+
* Populated from the apis.yaml catalog + scenario annotations (@cases, @api, @concurrent).
|
|
135
|
+
* Only emitted for api-kind units; never touches the BM-2-901-13 Testcases sheet.
|
|
136
|
+
*/
|
|
137
|
+
export interface ApiDetailRow {
|
|
138
|
+
/** Endpoint path from catalog (e.g. /register, /users/count?email=:email) */
|
|
139
|
+
endpoint: string;
|
|
140
|
+
/** HTTP method (GET, POST, …) */
|
|
141
|
+
method: string;
|
|
142
|
+
/** Auth / datasource string composed from catalog datasource + any @auth tag */
|
|
143
|
+
authDatasource: string;
|
|
144
|
+
/** Request shape: body fields / params / encoding from the catalog entry */
|
|
145
|
+
requestShape: string;
|
|
146
|
+
/** Expected-status matrix: the catalog expect.status plus a pointer to any @cases dataset
|
|
147
|
+
* that drives this endpoint, e.g. "201; @cases:register_cases". */
|
|
148
|
+
expectedStatusMatrix: string;
|
|
149
|
+
/** Ordered @api:<name> call sequence for flow scenarios referencing this endpoint */
|
|
150
|
+
flowSteps: string;
|
|
151
|
+
/** Concurrency invariant for @concurrent scenarios, e.g. "2× parallel → exactly-one; @query <oracle>" */
|
|
152
|
+
concurrencyInvariant: string;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Catalog entry as parsed from apis.yaml.
|
|
157
|
+
* All fields are typed loosely (unknown) because the yaml structure may vary — callers
|
|
158
|
+
* must guard before use.
|
|
159
|
+
*/
|
|
160
|
+
export interface ApiCatalogEntry {
|
|
161
|
+
method?: string;
|
|
162
|
+
path?: string;
|
|
163
|
+
datasource?: string;
|
|
164
|
+
description?: string;
|
|
165
|
+
body?: unknown;
|
|
166
|
+
params?: unknown;
|
|
167
|
+
expect?: { status?: number | string };
|
|
168
|
+
}
|
|
@@ -13,10 +13,15 @@ import * as fs from 'fs';
|
|
|
13
13
|
import * as path from 'path';
|
|
14
14
|
import ExcelJS from 'exceljs';
|
|
15
15
|
import JSZip from 'jszip';
|
|
16
|
-
import { ScreenSummary, TestCaseRow } from './types';
|
|
16
|
+
import { ApiCatalogEntry, ApiDetailRow, ScreenSummary, TestCaseRow } from './types';
|
|
17
17
|
import { getPackageVersion } from './package-info';
|
|
18
18
|
import { SUN_LOGO_PNG_BASE64 } from './sun-logo';
|
|
19
19
|
import { deliverableBasename } from './csv-exporter';
|
|
20
|
+
import {
|
|
21
|
+
extractApiCallOrder,
|
|
22
|
+
extractCasesDataset,
|
|
23
|
+
extractConcurrencyInvariant,
|
|
24
|
+
} from './feature-parser';
|
|
20
25
|
|
|
21
26
|
const COL_COUNT = 16;
|
|
22
27
|
const HEADER_FILL = 'FFD9D2E9'; // lavender — matches the summary-header band on row 6
|
|
@@ -37,15 +42,31 @@ function applyBorder(cell: AnyCell): void {
|
|
|
37
42
|
};
|
|
38
43
|
}
|
|
39
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Optional context for the supplementary "API detail" worksheet.
|
|
47
|
+
* Passed only when the unit is kind:api. When omitted, only the standard
|
|
48
|
+
* Testcases sheet is emitted (non-api delivery stays byte-identical).
|
|
49
|
+
*/
|
|
50
|
+
export interface ApiDetailContext {
|
|
51
|
+
/** Parsed apis.yaml catalog keyed by endpoint name */
|
|
52
|
+
catalog: Record<string, ApiCatalogEntry>;
|
|
53
|
+
/** Pre-built detail rows (one per catalog endpoint) */
|
|
54
|
+
rows: ApiDetailRow[];
|
|
55
|
+
}
|
|
56
|
+
|
|
40
57
|
export function renderXlsx(
|
|
41
58
|
summary: ScreenSummary,
|
|
42
59
|
rows: TestCaseRow[],
|
|
43
|
-
specLink: string
|
|
60
|
+
specLink: string,
|
|
61
|
+
apiDetail?: ApiDetailContext,
|
|
44
62
|
): ExcelJS.Workbook {
|
|
45
63
|
const wb = new ExcelJS.Workbook();
|
|
46
64
|
wb.creator = 'sungen delivery';
|
|
47
65
|
wb.created = new Date();
|
|
48
66
|
addTestcaseSheet(wb, 'Testcases', summary, rows, specLink);
|
|
67
|
+
if (apiDetail) {
|
|
68
|
+
addApiDetailSheet(wb, apiDetail.rows);
|
|
69
|
+
}
|
|
49
70
|
return wb;
|
|
50
71
|
}
|
|
51
72
|
|
|
@@ -410,6 +431,159 @@ function addTestcaseSheet(
|
|
|
410
431
|
};
|
|
411
432
|
}
|
|
412
433
|
|
|
434
|
+
// ---------------------------------------------------------------------------
|
|
435
|
+
// API detail sheet (api-kind units only)
|
|
436
|
+
// Second worksheet appended after Testcases — never alters the Testcases sheet.
|
|
437
|
+
// ---------------------------------------------------------------------------
|
|
438
|
+
|
|
439
|
+
const API_DETAIL_HEADER_FILL = 'FF2E5984'; // dark blue header for differentiation
|
|
440
|
+
const API_DETAIL_HEADER_FONT = 'FFFFFFFF'; // white text on dark header
|
|
441
|
+
|
|
442
|
+
/**
|
|
443
|
+
* Build ApiDetailRow[] from the apis.yaml catalog + feature-level annotations.
|
|
444
|
+
* Called once per feature file for api-kind units in the delivery pipeline.
|
|
445
|
+
*
|
|
446
|
+
* @param catalog Parsed apis.yaml keyed by endpoint name
|
|
447
|
+
* @param scenarios Scenario-level tag arrays from parseFeatureMetadata().scenarios
|
|
448
|
+
*/
|
|
449
|
+
export function buildApiDetailRows(
|
|
450
|
+
catalog: Record<string, ApiCatalogEntry>,
|
|
451
|
+
scenarios: Array<{ tags: string[] }>,
|
|
452
|
+
): ApiDetailRow[] {
|
|
453
|
+
const rows: ApiDetailRow[] = [];
|
|
454
|
+
|
|
455
|
+
for (const [endpointName, entry] of Object.entries(catalog)) {
|
|
456
|
+
const method = (entry.method ?? '').toUpperCase();
|
|
457
|
+
const endpoint = entry.path ?? endpointName;
|
|
458
|
+
const datasource = entry.datasource ?? '';
|
|
459
|
+
|
|
460
|
+
// Auth: look for @auth: tag in any scenario that calls this endpoint.
|
|
461
|
+
const authTags = scenarios.flatMap((s) => {
|
|
462
|
+
const calls = extractApiCallOrder(s.tags);
|
|
463
|
+
if (!calls.includes(endpointName)) return [];
|
|
464
|
+
return s.tags.filter((t) => t.startsWith('@auth:'));
|
|
465
|
+
});
|
|
466
|
+
const uniqueAuth = [...new Set(authTags.map((t) => t.slice('@auth:'.length)))];
|
|
467
|
+
const authDatasource = [datasource, ...uniqueAuth].filter(Boolean).join('; ');
|
|
468
|
+
|
|
469
|
+
// Request shape: compose from body + params + encoding.
|
|
470
|
+
const bodyStr = entry.body
|
|
471
|
+
? `body: ${typeof entry.body === 'string' ? entry.body : JSON.stringify(entry.body)}`
|
|
472
|
+
: '';
|
|
473
|
+
const paramsArr = Array.isArray(entry.params) ? entry.params as string[] : [];
|
|
474
|
+
const paramsStr = paramsArr.length > 0 ? `params: [${paramsArr.join(', ')}]` : '';
|
|
475
|
+
const requestShape = [bodyStr, paramsStr].filter(Boolean).join('; ') || '—';
|
|
476
|
+
|
|
477
|
+
// Expected-status matrix: aggregate @cases dataset labels + expected status
|
|
478
|
+
// from scenarios that call this endpoint. Fall back to catalog expect.status.
|
|
479
|
+
const statusEntries: string[] = [];
|
|
480
|
+
for (const sc of scenarios) {
|
|
481
|
+
const calls = extractApiCallOrder(sc.tags);
|
|
482
|
+
if (!calls.includes(endpointName)) continue;
|
|
483
|
+
const dataset = extractCasesDataset(sc.tags);
|
|
484
|
+
if (dataset) {
|
|
485
|
+
// @cases dataset name as label — actual per-row statuses live in test-data.yaml
|
|
486
|
+
statusEntries.push(`@cases:${dataset}`);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
// Show the catalog baseline status plus a pointer to any @cases matrix dataset (the per-row
|
|
490
|
+
// statuses live in test-data; resolving them into this cell is a later enrichment).
|
|
491
|
+
const catalogStatus = entry.expect?.status != null ? String(entry.expect.status) : '';
|
|
492
|
+
const expectedStatusMatrix =
|
|
493
|
+
[catalogStatus, ...new Set(statusEntries)].filter(Boolean).join('; ') || '—';
|
|
494
|
+
|
|
495
|
+
// Flow steps: ordered @api names from flow-tagged scenarios referencing this endpoint.
|
|
496
|
+
const flowStepsSet = new Set<string>();
|
|
497
|
+
for (const sc of scenarios) {
|
|
498
|
+
const calls = extractApiCallOrder(sc.tags);
|
|
499
|
+
if (!calls.includes(endpointName)) continue;
|
|
500
|
+
// All scenarios show their call order; flow scenarios show multi-step chains.
|
|
501
|
+
if (calls.length > 1) {
|
|
502
|
+
flowStepsSet.add(calls.join(' → '));
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
const flowSteps = [...flowStepsSet].join('; ') || '—';
|
|
506
|
+
|
|
507
|
+
// Concurrency invariant: from @concurrent scenarios calling this endpoint.
|
|
508
|
+
const concurrencyParts: string[] = [];
|
|
509
|
+
for (const sc of scenarios) {
|
|
510
|
+
const calls = extractApiCallOrder(sc.tags);
|
|
511
|
+
if (!calls.includes(endpointName)) continue;
|
|
512
|
+
const inv = extractConcurrencyInvariant(sc.tags);
|
|
513
|
+
if (inv) concurrencyParts.push(inv);
|
|
514
|
+
}
|
|
515
|
+
const concurrencyInvariant = concurrencyParts.join('; ') || '—';
|
|
516
|
+
|
|
517
|
+
rows.push({
|
|
518
|
+
endpoint,
|
|
519
|
+
method,
|
|
520
|
+
authDatasource,
|
|
521
|
+
requestShape,
|
|
522
|
+
expectedStatusMatrix,
|
|
523
|
+
flowSteps,
|
|
524
|
+
concurrencyInvariant,
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
return rows;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Append a second "API detail" worksheet to the workbook.
|
|
533
|
+
* Called only for api-kind units; no effect on the Testcases sheet or other sheets.
|
|
534
|
+
*
|
|
535
|
+
* Columns: Endpoint · Method · Auth/Datasource · Request shape ·
|
|
536
|
+
* Expected-status matrix · Flow steps · Concurrency invariant
|
|
537
|
+
*/
|
|
538
|
+
export function addApiDetailSheet(wb: ExcelJS.Workbook, detailRows: ApiDetailRow[]): void {
|
|
539
|
+
const ws = wb.addWorksheet('API detail');
|
|
540
|
+
const ARIAL_FONT = 'Arial';
|
|
541
|
+
|
|
542
|
+
ws.columns = [
|
|
543
|
+
{ header: 'Endpoint', width: 35 },
|
|
544
|
+
{ header: 'Method', width: 10 },
|
|
545
|
+
{ header: 'Auth / Datasource', width: 22 },
|
|
546
|
+
{ header: 'Request shape', width: 40 },
|
|
547
|
+
{ header: 'Expected-status matrix', width: 30 },
|
|
548
|
+
{ header: 'Flow steps', width: 40 },
|
|
549
|
+
{ header: 'Concurrency invariant', width: 35 },
|
|
550
|
+
];
|
|
551
|
+
|
|
552
|
+
// Style the auto-generated header row (row 1).
|
|
553
|
+
const headerRow = ws.getRow(1);
|
|
554
|
+
headerRow.height = 30;
|
|
555
|
+
headerRow.eachCell((cell) => {
|
|
556
|
+
cell.font = { bold: true, color: { argb: API_DETAIL_HEADER_FONT }, name: ARIAL_FONT };
|
|
557
|
+
cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: API_DETAIL_HEADER_FILL } };
|
|
558
|
+
cell.alignment = { horizontal: 'center', vertical: 'middle', wrapText: true };
|
|
559
|
+
applyBorder(cell);
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
ws.views = [{ state: 'frozen', ySplit: 1 }];
|
|
563
|
+
|
|
564
|
+
for (const r of detailRows) {
|
|
565
|
+
const row = ws.addRow([
|
|
566
|
+
r.endpoint,
|
|
567
|
+
r.method,
|
|
568
|
+
r.authDatasource,
|
|
569
|
+
r.requestShape,
|
|
570
|
+
r.expectedStatusMatrix,
|
|
571
|
+
r.flowSteps,
|
|
572
|
+
r.concurrencyInvariant,
|
|
573
|
+
]);
|
|
574
|
+
row.alignment = { vertical: 'top', wrapText: true };
|
|
575
|
+
row.eachCell({ includeEmpty: true }, (cell) => {
|
|
576
|
+
applyBorder(cell);
|
|
577
|
+
cell.font = { name: ARIAL_FONT };
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
ws.autoFilter = {
|
|
582
|
+
from: { row: 1, column: 1 },
|
|
583
|
+
to: { row: ws.rowCount, column: 7 },
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
|
|
413
587
|
/**
|
|
414
588
|
* Write the workbook to `qa/deliverables/<screen>-testcases[.env].xlsx`.
|
|
415
589
|
* When `SUNGEN_ENV` is set, the env name is appended so locale exports don't
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { TestGeneratorAdapter, TestFileData, ScenarioData } from '../adapter-interface';
|
|
3
|
+
import { TemplateEngine } from '../../template-engine';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* AppiumAdapter
|
|
7
|
+
* Generates WebdriverIO + Appium (Mocha) test code using Handlebars templates.
|
|
8
|
+
*
|
|
9
|
+
* Mirrors PlaywrightAdapter: the adapter only renders the file skeleton (imports,
|
|
10
|
+
* describe/it, before/after hooks). Per-step code is produced by StepMapper +
|
|
11
|
+
* PatternRegistry driving this adapter's templates/ directory — the templates here
|
|
12
|
+
* carry the same names the patterns request (click-action, fill-action,
|
|
13
|
+
* visible-assertion, the `locator` partial, …) but emit WebdriverIO instead of Playwright.
|
|
14
|
+
*/
|
|
15
|
+
export class AppiumAdapter implements TestGeneratorAdapter {
|
|
16
|
+
readonly frameworkName = 'appium';
|
|
17
|
+
readonly fileExtension = '.spec.ts';
|
|
18
|
+
readonly templatesDir: string;
|
|
19
|
+
private templateEngine: TemplateEngine;
|
|
20
|
+
|
|
21
|
+
constructor() {
|
|
22
|
+
this.templatesDir = path.join(__dirname, 'templates');
|
|
23
|
+
this.templateEngine = new TemplateEngine(this.templatesDir);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
renderTestFile(data: TestFileData): string {
|
|
27
|
+
return this.templateEngine.renderTestFile(data);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
renderScenario(data: ScenarioData): string {
|
|
31
|
+
return this.templateEngine.renderScenario(data);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
renderImports(options?: { runtimeData?: boolean; basePath?: string; isParallel?: boolean; needsCleanupImport?: boolean; needsDb?: boolean; needsApi?: boolean }): string {
|
|
35
|
+
return this.templateEngine.renderImports(options);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
renderBeforeEach(data: { steps: Array<{ comment?: string; code: string }> }): string {
|
|
39
|
+
return this.templateEngine.renderBeforeEach(data);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
renderBeforeAll(data: { steps: Array<{ comment?: string; code: string }> }): string {
|
|
43
|
+
return this.templateEngine.renderBeforeAll(data);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
renderAfterEach(data: { steps: Array<{ comment?: string; code: string }> }): string {
|
|
47
|
+
return this.templateEngine.renderAfterEach(data);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
renderAfterAll(data: { steps: Array<{ comment?: string; code: string }> }): string {
|
|
51
|
+
return this.templateEngine.renderAfterAll(data);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
renderStep(templateName: string, data: any): string {
|
|
55
|
+
return this.templateEngine.renderStep(templateName, data);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { $, $$, expect, driver, browser } from '@wdio/globals';
|
|
2
|
+
{{#if runtimeData}}
|
|
3
|
+
import { TestDataLoader } from '{{basePath}}/test-data';
|
|
4
|
+
{{/if}}
|
|
5
|
+
|
|
6
|
+
// This file is auto-generated from Gherkin feature files
|
|
7
|
+
// DO NOT EDIT MANUALLY - changes will be overwritten
|
|
8
|
+
// To modify tests, edit the corresponding .feature file and regenerate
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
{{!-- Accept a native alert. Unlike web (handler set before the trigger), mobile alerts are handled
|
|
2
|
+
AFTER they appear; for launch-time permission dialogs prefer the `appium:autoAcceptAlerts` cap.
|
|
3
|
+
Best-effort: ignore if no alert is present. --}}
|
|
4
|
+
try { await driver.acceptAlert(); } catch { /* no native alert present */ }
|
package/src/generators/test-generator/adapters/appium/templates/steps/actions/check-action.hbs
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{{!-- WebdriverIO/Appium has no .check(). Read the current state and tap only if it's not already
|
|
2
|
+
on, so the step is idempotent (re-running a passing test won't flip it back off). Falls through
|
|
3
|
+
to a plain tap if the state can't be read. --}}
|
|
4
|
+
{
|
|
5
|
+
const __cb = await {{> locator}};
|
|
6
|
+
let __on = false;
|
|
7
|
+
try { __on = await __cb.isSelected(); }
|
|
8
|
+
catch { try { __on = (await __cb.getAttribute('checked')) === 'true'; } catch { /* state unreadable — tap anyway */ } }
|
|
9
|
+
if (!__on) await __cb.click();
|
|
10
|
+
}
|
package/src/generators/test-generator/adapters/appium/templates/steps/actions/clear-action.hbs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
await {{> locator}}.clearValue();
|
package/src/generators/test-generator/adapters/appium/templates/steps/actions/click-action.hbs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
await {{> locator}}.click();
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
{{!-- Tap an element by its visible text / content-desc (dynamic text match). --}}
|
|
2
|
+
{{#if nth}}await $$('//*[contains(@content-desc,"{{escapeQuotes dataValue}}") or contains(@text,"{{escapeQuotes dataValue}}")]')[{{nth}}].click();{{else}}await $('//*[contains(@content-desc,"{{escapeQuotes dataValue}}") or contains(@text,"{{escapeQuotes dataValue}}")]').click();{{/if}}
|
package/src/generators/test-generator/adapters/appium/templates/steps/actions/dismiss-action.hbs
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// Best-effort dismiss (launch interstitial / promo / ad): tap if it shows within a short grace,
|
|
2
|
+
// otherwise skip — never fails the step.
|
|
3
|
+
try { const __d = await {{> locator}}; if (await __d.waitForDisplayed({ timeout: 2500 }).then(() => true).catch(() => false)) await __d.click(); } catch { /* not present — skip */ }
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{{!-- Double-tap. Use the platform gesture (element.doubleClick is web-oriented). --}}
|
|
2
|
+
{
|
|
3
|
+
const __el = await {{> locator}};
|
|
4
|
+
if (driver.isIOS) await driver.execute('mobile: doubleTap', { elementId: __el.elementId });
|
|
5
|
+
else await driver.execute('mobile: doubleClickGesture', { elementId: __el.elementId });
|
|
6
|
+
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
{{!-- Drag the source element onto the target. WebdriverIO dragAndDrop drives W3C touch actions. --}}
|
|
2
|
+
await (await {{> locator}}).dragAndDrop(await $('{{#switch targetStrategy}}{{#case 'accessibility-id'}}~{{escapeQuotes targetValue}}{{/case}}{{#case 'testid'}}~{{escapeQuotes targetValue}}{{/case}}{{#case 'xpath'}}{{escapeQuotes targetValue}}{{/case}}{{#case 'android-uiautomator'}}android={{escapeQuotes targetValue}}{{/case}}{{#case 'id'}}id={{escapeQuotes targetValue}}{{/case}}{{#default}}~{{#if targetValue}}{{escapeQuotes targetValue}}{{else}}{{escapeQuotes targetName}}{{/if}}{{/default}}{{/switch}}'));
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{{!-- Robust mobile fill: Flutter often ignores a bare setValue (the field never registers, so a
|
|
2
|
+
dependent submit button stays disabled). Explicitly focus, clear, setValue, then if the field
|
|
3
|
+
still reads empty, retype with addValue. Best-effort read-back never fails the step. --}}
|
|
4
|
+
{
|
|
5
|
+
const __fill = await {{> locator}};
|
|
6
|
+
await __fill.click();
|
|
7
|
+
try { await __fill.clearValue(); } catch { /* not clearable */ }
|
|
8
|
+
await __fill.setValue('{{escapeQuotes fillValue}}');
|
|
9
|
+
try {
|
|
10
|
+
const __got = (await __fill.getText()) || (await __fill.getAttribute('text')) || '';
|
|
11
|
+
const __masked = /[•●*]/.test(__got);
|
|
12
|
+
if (!__masked && __got.trim() === '') await __fill.addValue('{{escapeQuotes fillValue}}');
|
|
13
|
+
} catch { /* read-back unsupported — trust setValue */ }
|
|
14
|
+
}
|
package/src/generators/test-generator/adapters/appium/templates/steps/actions/fill-editor-action.hbs
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{{!-- Rich-text/contenteditable editors don't exist as such on native mobile — treat as a normal field
|
|
2
|
+
with the robust fill (focus, clear, setValue, read-back, retry). --}}
|
|
3
|
+
{
|
|
4
|
+
const __fill = await {{> locator}};
|
|
5
|
+
await __fill.click();
|
|
6
|
+
try { await __fill.clearValue(); } catch { /* not clearable */ }
|
|
7
|
+
await __fill.setValue('{{escapeQuotes fillValue}}');
|
|
8
|
+
try {
|
|
9
|
+
const __got = (await __fill.getText()) || (await __fill.getAttribute('text')) || '';
|
|
10
|
+
if (!/[•●*]/.test(__got) && __got.trim() === '') await __fill.addValue('{{escapeQuotes fillValue}}');
|
|
11
|
+
} catch { /* read-back unsupported — trust setValue */ }
|
|
12
|
+
}
|
package/src/generators/test-generator/adapters/appium/templates/steps/actions/frame-enter-action.hbs
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{{!-- Hybrid app: switch into the webview context (mobile equivalent of entering an iframe). --}}
|
|
2
|
+
{
|
|
3
|
+
const __ctxs = await driver.getContexts();
|
|
4
|
+
const __web = __ctxs.find(c => /WEBVIEW|CHROMIUM/i.test(typeof c === 'string' ? c : c.id));
|
|
5
|
+
if (__web) await driver.switchContext(typeof __web === 'string' ? __web : __web.id);
|
|
6
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// Hide the soft keyboard (best-effort — never fails if no keyboard is shown).
|
|
2
|
+
// iOS XCUITest cannot dismiss the keyboard generically (WDA throws + retries, ~12s wasted),
|
|
3
|
+
// and XCUITest taps/asserts work with the keyboard up — so the dismiss is Android-only.
|
|
4
|
+
try { if (!driver.isIOS && await driver.isKeyboardShown()) await driver.hideKeyboard(); } catch { /* no keyboard / not supported */ }
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
{{!-- Mobile radios have no ARIA "radio" role to query by name, so tap the option by its
|
|
2
|
+
accessibility-id / content-desc (= the option value). If the visible label differs from the
|
|
3
|
+
element's accessibility-id, add an explicit selector for the option and reference it instead. --}}
|
|
4
|
+
await $('~{{escapeQuotes selectValue}}').click();
|
package/src/generators/test-generator/adapters/appium/templates/steps/actions/scroll-action.hbs
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
if (driver.isIOS) {
|
|
2
|
+
// iOS (XCUITest) has no UiScrollable. Swipe the screen up in bounded steps until the target is on
|
|
3
|
+
// screen (a below-fold element isn't in the XCUITest tree until scrolled, so getElementRect/tap on
|
|
4
|
+
// it would otherwise fail). Bounded so a missing target can't loop forever.
|
|
5
|
+
const __t = $({{> appium-selector-expr}});
|
|
6
|
+
for (let __i = 0; __i < 12 && !(await __t.isDisplayed().catch(() => false)); __i++) {
|
|
7
|
+
await driver.execute('mobile: swipe', { direction: 'up' });
|
|
8
|
+
}
|
|
9
|
+
} else {
|
|
10
|
+
{{#if (eq strategy 'accessibility-id')}}
|
|
11
|
+
// Android UiScrollable scrolls until the target's content-desc is on screen, then stops — no
|
|
12
|
+
// overshoot (which would drop a virtualized Flutter item from the a11y tree). Robust on Flutter/RN.
|
|
13
|
+
await $('android=new UiScrollable(new UiSelector().scrollable(true)).scrollIntoView(new UiSelector().descriptionContains("{{escapeQuotes value}}"))');
|
|
14
|
+
{{else}}
|
|
15
|
+
// Generic fallback: framework-agnostic moderate scroll gesture (wdio .scrollIntoView() can't always
|
|
16
|
+
// find a native ScrollView on Flutter/RN). Modest percent avoids over-scrolling a virtualized list.
|
|
17
|
+
await driver.execute('mobile: scrollGesture', { left: 60, top: 420, width: 960, height: 1320, direction: 'down', percent: 0.6 });
|
|
18
|
+
{{/if}}
|
|
19
|
+
}
|
package/src/generators/test-generator/adapters/appium/templates/steps/actions/select-action.hbs
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
{{!-- Native dropdown/picker: open the control, then tap the option by accessibility-id /
|
|
2
|
+
content-desc (= the option value). Two taps because there is no native <select>.selectOption
|
|
3
|
+
on mobile. If the option's accessibility-id differs from its label, add an explicit selector. --}}
|
|
4
|
+
await {{> locator}}.click();
|
|
5
|
+
await $('~{{escapeQuotes selectValue}}').click();
|
package/src/generators/test-generator/adapters/appium/templates/steps/actions/uncheck-action.hbs
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{{!-- WebdriverIO/Appium has no .uncheck(). Tap only if currently on, so the step is idempotent. --}}
|
|
2
|
+
{
|
|
3
|
+
const __cb = await {{> locator}};
|
|
4
|
+
let __on = false;
|
|
5
|
+
try { __on = await __cb.isSelected(); }
|
|
6
|
+
catch { try { __on = (await __cb.getAttribute('checked')) === 'true'; } catch { /* state unreadable — leave as-is */ } }
|
|
7
|
+
if (__on) await __cb.click();
|
|
8
|
+
}
|
package/src/generators/test-generator/adapters/appium/templates/steps/actions/upload-action.hbs
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
{{!-- File upload is not auto-generatable on native mobile (no <input type=file>): it needs app-specific
|
|
2
|
+
handling (driver.pushFile + the app's own picker, or a deep link). Fail loud so it isn't a silent skip. --}}
|
|
3
|
+
throw new Error("[sungen] upload step ('{{fileName}}') is not supported on the appium adapter — handle the file picker app-specifically (driver.pushFile + picker) or tag this scenario @manual.");
|