@sun-asterisk/sungen 3.2.31 → 3.2.33
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/cli/commands/audit.d.ts +1 -0
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/audit.js +13 -2
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/capability.d.ts.map +1 -1
- package/dist/cli/commands/capability.js +46 -17
- 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 +39 -18
- 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 +14 -7
- package/dist/cli/commands/generate.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +2 -0
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/results-merge.d.ts.map +1 -1
- package/dist/cli/commands/results-merge.js +2 -12
- package/dist/cli/commands/results-merge.js.map +1 -1
- package/dist/cli/commands/results-sanitize.d.ts.map +1 -1
- package/dist/cli/commands/results-sanitize.js +2 -13
- package/dist/cli/commands/results-sanitize.js.map +1 -1
- package/dist/cli/commands/setup-mobile-env.d.ts +15 -0
- package/dist/cli/commands/setup-mobile-env.d.ts.map +1 -0
- package/dist/cli/commands/setup-mobile-env.js +65 -0
- package/dist/cli/commands/setup-mobile-env.js.map +1 -0
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/dashboard/snapshot-builder.d.ts.map +1 -1
- package/dist/dashboard/snapshot-builder.js +6 -10
- package/dist/dashboard/snapshot-builder.js.map +1 -1
- package/dist/exporters/feature-parser.d.ts +21 -0
- package/dist/exporters/feature-parser.d.ts.map +1 -1
- package/dist/exporters/feature-parser.js +32 -1
- package/dist/exporters/feature-parser.js.map +1 -1
- package/dist/exporters/locale-variants.d.ts +7 -1
- package/dist/exporters/locale-variants.d.ts.map +1 -1
- package/dist/exporters/locale-variants.js +23 -16
- package/dist/exporters/locale-variants.js.map +1 -1
- package/dist/exporters/matrix/build.d.ts.map +1 -1
- package/dist/exporters/matrix/build.js +13 -7
- package/dist/exporters/matrix/build.js.map +1 -1
- package/dist/exporters/matrix/carry-manual-results.d.ts +70 -0
- package/dist/exporters/matrix/carry-manual-results.d.ts.map +1 -0
- package/dist/exporters/matrix/carry-manual-results.js +232 -0
- package/dist/exporters/matrix/carry-manual-results.js.map +1 -0
- package/dist/exporters/matrix/export.d.ts +2 -0
- package/dist/exporters/matrix/export.d.ts.map +1 -1
- package/dist/exporters/matrix/export.js +7 -1
- package/dist/exporters/matrix/export.js.map +1 -1
- package/dist/exporters/matrix/wording.d.ts +7 -0
- package/dist/exporters/matrix/wording.d.ts.map +1 -1
- package/dist/exporters/matrix/wording.js +27 -1
- package/dist/exporters/matrix/wording.js.map +1 -1
- package/dist/exporters/xlsx-exporter.d.ts +2 -0
- package/dist/exporters/xlsx-exporter.d.ts.map +1 -1
- package/dist/exporters/xlsx-exporter.js +7 -2
- package/dist/exporters/xlsx-exporter.js.map +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/actions/reinstall-app-action.hbs +2 -2
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/all-contain-assertion.hbs +1 -2
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/all-contain-element.hbs +8 -5
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/count-assertion.hbs +5 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/disabled-with-filter-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/hidden-with-filter-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/label-value-assertion.hbs +8 -2
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/list-item-count-assertion.hbs +5 -2
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/state-with-filter-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/visible-filtered-assertion.hbs +1 -1
- package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/visible-with-role-variable-assertion.hbs +7 -3
- package/dist/generators/test-generator/adapters/appium/templates/steps/partials/await-elements.hbs +12 -0
- package/dist/generators/test-generator/adapters/appium/templates/test-file.hbs +94 -4
- package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/capture-variable.hbs +7 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/label-value-assertion.hbs +7 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/row-scoped-column-assertion.hbs +23 -1
- package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +8 -4
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +4 -4
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/default.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.hbs +4 -4
- package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/url-assertion.hbs +17 -2
- package/dist/generators/test-generator/adapters/playwright/templates/test-file.hbs +14 -0
- package/dist/generators/test-generator/code-generator.d.ts +2 -1
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +122 -15
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/generators/test-generator/diagnostics.d.ts +70 -4
- package/dist/generators/test-generator/diagnostics.d.ts.map +1 -1
- package/dist/generators/test-generator/diagnostics.js +69 -3
- package/dist/generators/test-generator/diagnostics.js.map +1 -1
- package/dist/generators/test-generator/patterns/index.d.ts +13 -3
- package/dist/generators/test-generator/patterns/index.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/index.js +16 -10
- package/dist/generators/test-generator/patterns/index.js.map +1 -1
- package/dist/generators/test-generator/patterns/types.d.ts +15 -0
- package/dist/generators/test-generator/patterns/types.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/with-parent-scope.d.ts +5 -0
- package/dist/generators/test-generator/patterns/with-parent-scope.d.ts.map +1 -1
- package/dist/generators/test-generator/patterns/with-parent-scope.js +6 -0
- package/dist/generators/test-generator/patterns/with-parent-scope.js.map +1 -1
- package/dist/generators/test-generator/step-mapper.d.ts +81 -0
- package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.js +187 -13
- 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 +18 -0
- package/dist/generators/test-generator/template-engine.js.map +1 -1
- package/dist/generators/test-generator/utils/data-resolver.d.ts +25 -0
- package/dist/generators/test-generator/utils/data-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/data-resolver.js +39 -8
- package/dist/generators/test-generator/utils/data-resolver.js.map +1 -1
- package/dist/generators/test-generator/utils/selector-entry-fields.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/selector-entry-fields.js +1 -0
- package/dist/generators/test-generator/utils/selector-entry-fields.js.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.d.ts +84 -4
- package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.js +229 -82
- package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
- package/dist/harness/audit.d.ts.map +1 -1
- package/dist/harness/audit.js +35 -8
- package/dist/harness/audit.js.map +1 -1
- package/dist/harness/capability.d.ts +7 -1
- package/dist/harness/capability.d.ts.map +1 -1
- package/dist/harness/capability.js +15 -12
- package/dist/harness/capability.js.map +1 -1
- package/dist/harness/catalog/universal-viewpoints.yaml +5 -5
- package/dist/harness/config-file.d.ts +17 -0
- package/dist/harness/config-file.d.ts.map +1 -0
- package/dist/harness/config-file.js +100 -0
- package/dist/harness/config-file.js.map +1 -0
- package/dist/harness/depth-lint.d.ts.map +1 -1
- package/dist/harness/depth-lint.js +2 -1
- package/dist/harness/depth-lint.js.map +1 -1
- package/dist/harness/flow-check.d.ts.map +1 -1
- package/dist/harness/flow-check.js +6 -2
- package/dist/harness/flow-check.js.map +1 -1
- package/dist/harness/keyword-match.d.ts +59 -0
- package/dist/harness/keyword-match.d.ts.map +1 -0
- package/dist/harness/keyword-match.js +86 -0
- package/dist/harness/keyword-match.js.map +1 -0
- package/dist/harness/next-step.d.ts.map +1 -1
- package/dist/harness/next-step.js +2 -1
- package/dist/harness/next-step.js.map +1 -1
- package/dist/harness/parse.d.ts.map +1 -1
- package/dist/harness/parse.js +42 -7
- package/dist/harness/parse.js.map +1 -1
- package/dist/harness/perf.d.ts +7 -1
- package/dist/harness/perf.d.ts.map +1 -1
- package/dist/harness/perf.js +13 -10
- package/dist/harness/perf.js.map +1 -1
- package/dist/harness/quality-gates.d.ts +9 -2
- package/dist/harness/quality-gates.d.ts.map +1 -1
- package/dist/harness/quality-gates.js +16 -4
- package/dist/harness/quality-gates.js.map +1 -1
- package/dist/harness/query-catalog.js +0 -0
- package/dist/harness/query-catalog.js.map +1 -1
- package/dist/harness/read-text.d.ts +6 -0
- package/dist/harness/read-text.d.ts.map +1 -1
- package/dist/harness/read-text.js +7 -1
- package/dist/harness/read-text.js.map +1 -1
- package/dist/harness/secret-scan.d.ts +27 -1
- package/dist/harness/secret-scan.d.ts.map +1 -1
- package/dist/harness/secret-scan.js +57 -4
- package/dist/harness/secret-scan.js.map +1 -1
- package/dist/harness/sensors.d.ts +12 -0
- package/dist/harness/sensors.d.ts.map +1 -1
- package/dist/harness/sensors.js +41 -18
- package/dist/harness/sensors.js.map +1 -1
- package/dist/harness/spec-branches.d.ts +7 -20
- package/dist/harness/spec-branches.d.ts.map +1 -1
- package/dist/harness/spec-branches.js +85 -13
- package/dist/harness/spec-branches.js.map +1 -1
- package/dist/harness/spec-coverage.d.ts +16 -0
- package/dist/harness/spec-coverage.d.ts.map +1 -1
- package/dist/harness/spec-coverage.js +131 -12
- package/dist/harness/spec-coverage.js.map +1 -1
- package/dist/harness/viewpoint-ledger.d.ts.map +1 -1
- package/dist/harness/viewpoint-ledger.js +2 -1
- package/dist/harness/viewpoint-ledger.js.map +1 -1
- package/dist/harness/write-atomic.d.ts +2 -0
- package/dist/harness/write-atomic.d.ts.map +1 -0
- package/dist/harness/write-atomic.js +62 -0
- package/dist/harness/write-atomic.js.map +1 -0
- package/dist/orchestrator/ai-skills/writer.d.ts.map +1 -1
- package/dist/orchestrator/ai-skills/writer.js +15 -7
- package/dist/orchestrator/ai-skills/writer.js.map +1 -1
- package/dist/orchestrator/appium-environment-setup.d.ts +24 -0
- package/dist/orchestrator/appium-environment-setup.d.ts.map +1 -0
- package/dist/orchestrator/appium-environment-setup.js +157 -0
- package/dist/orchestrator/appium-environment-setup.js.map +1 -0
- package/dist/orchestrator/assets-drift.d.ts.map +1 -1
- package/dist/orchestrator/assets-drift.js +11 -6
- package/dist/orchestrator/assets-drift.js.map +1 -1
- package/dist/orchestrator/mobile-runtime-scaffolder.d.ts +2 -0
- package/dist/orchestrator/mobile-runtime-scaffolder.d.ts.map +1 -1
- package/dist/orchestrator/mobile-runtime-scaffolder.js.map +1 -1
- package/dist/orchestrator/project-deps-sync.d.ts.map +1 -1
- package/dist/orchestrator/project-deps-sync.js +22 -1
- package/dist/orchestrator/project-deps-sync.js.map +1 -1
- package/dist/orchestrator/project-initializer.d.ts +9 -0
- package/dist/orchestrator/project-initializer.d.ts.map +1 -1
- package/dist/orchestrator/project-initializer.js +63 -7
- package/dist/orchestrator/project-initializer.js.map +1 -1
- package/dist/orchestrator/security-overrides-catalog.d.ts +39 -0
- package/dist/orchestrator/security-overrides-catalog.d.ts.map +1 -0
- package/dist/orchestrator/security-overrides-catalog.js +63 -0
- package/dist/orchestrator/security-overrides-catalog.js.map +1 -0
- package/dist/orchestrator/security-overrides.d.ts +51 -0
- package/dist/orchestrator/security-overrides.d.ts.map +1 -0
- package/dist/orchestrator/security-overrides.js +165 -0
- package/dist/orchestrator/security-overrides.js.map +1 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +14 -0
- package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +8 -2
- package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +64 -2
- package/dist/orchestrator/templates/ai-src/skills/sungen-selector-keys/SKILL.md +13 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +20 -5
- package/dist/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +3 -3
- package/dist/orchestrator/templates/appium-environment/README.md +83 -0
- package/dist/orchestrator/templates/appium-environment/scripts/common/install-appium.ps1 +44 -0
- package/dist/orchestrator/templates/appium-environment/scripts/common/install-appium.sh +50 -0
- package/dist/orchestrator/templates/appium-environment/scripts/common/lib.ps1 +108 -0
- package/dist/orchestrator/templates/appium-environment/scripts/common/lib.sh +141 -0
- package/dist/orchestrator/templates/appium-environment/scripts/common/verify-appium.ps1 +39 -0
- package/dist/orchestrator/templates/appium-environment/scripts/common/verify-appium.sh +43 -0
- package/dist/orchestrator/templates/appium-environment/scripts/common/verify-node.ps1 +13 -0
- package/dist/orchestrator/templates/appium-environment/scripts/common/verify-node.sh +16 -0
- package/dist/orchestrator/templates/appium-environment/scripts/macos/install-android.sh +110 -0
- package/dist/orchestrator/templates/appium-environment/scripts/macos/install-ios.sh +32 -0
- package/dist/orchestrator/templates/appium-environment/scripts/macos/verify-android.sh +32 -0
- package/dist/orchestrator/templates/appium-environment/scripts/macos/verify-ios.sh +15 -0
- package/dist/orchestrator/templates/appium-environment/scripts/windows/install-android.ps1 +153 -0
- package/dist/orchestrator/templates/appium-environment/scripts/windows/verify-android.ps1 +26 -0
- package/dist/orchestrator/templates/appium-environment/setup.ps1 +58 -0
- package/dist/orchestrator/templates/appium-environment/setup.sh +88 -0
- package/dist/orchestrator/templates/specs-checked-state.d.ts +1 -1
- package/dist/orchestrator/templates/specs-checked-state.d.ts.map +1 -1
- package/dist/orchestrator/templates/specs-checked-state.js +26 -3
- package/dist/orchestrator/templates/specs-checked-state.js.map +1 -1
- package/dist/orchestrator/templates/specs-checked-state.ts +31 -6
- package/dist/orchestrator/templates/specs-locale-fixture.ts +21 -9
- package/dist/orchestrator/templates/specs-mock.d.ts +2 -1
- package/dist/orchestrator/templates/specs-mock.d.ts.map +1 -1
- package/dist/orchestrator/templates/specs-mock.js +10 -3
- package/dist/orchestrator/templates/specs-mock.js.map +1 -1
- package/dist/orchestrator/templates/specs-mock.ts +12 -4
- package/dist/orchestrator/templates/specs-pw-shape-reporter.ts +0 -0
- package/dist/orchestrator/templates/specs-url-assert.d.ts +17 -1
- package/dist/orchestrator/templates/specs-url-assert.d.ts.map +1 -1
- package/dist/orchestrator/templates/specs-url-assert.js +14 -2
- package/dist/orchestrator/templates/specs-url-assert.js.map +1 -1
- package/dist/orchestrator/templates/specs-url-assert.ts +24 -2
- package/dist/orchestrator/templates/wdio.conf.ts +24 -1
- package/package.json +3 -3
- package/src/cli/commands/audit.ts +12 -3
- package/src/cli/commands/capability.ts +63 -19
- package/src/cli/commands/delivery.ts +38 -16
- package/src/cli/commands/generate.ts +15 -7
- package/src/cli/commands/init.ts +3 -0
- package/src/cli/commands/results-merge.ts +2 -8
- package/src/cli/commands/results-sanitize.ts +2 -9
- package/src/cli/commands/setup-mobile-env.ts +64 -0
- package/src/cli/index.ts +2 -0
- package/src/dashboard/snapshot-builder.ts +6 -8
- package/src/exporters/feature-parser.ts +32 -2
- package/src/exporters/locale-variants.ts +23 -17
- package/src/exporters/matrix/build.ts +14 -8
- package/src/exporters/matrix/carry-manual-results.ts +229 -0
- package/src/exporters/matrix/export.ts +10 -4
- package/src/exporters/matrix/wording.ts +26 -1
- package/src/exporters/xlsx-exporter.ts +7 -2
- package/src/generators/test-generator/adapters/adapter-interface.ts +2 -2
- package/src/generators/test-generator/adapters/appium/templates/steps/actions/reinstall-app-action.hbs +2 -2
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/all-contain-assertion.hbs +1 -2
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/all-contain-element.hbs +8 -5
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/count-assertion.hbs +5 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/disabled-with-filter-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/hidden-with-filter-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/label-value-assertion.hbs +8 -2
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/list-item-count-assertion.hbs +5 -2
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/state-with-filter-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/visible-filtered-assertion.hbs +1 -1
- package/src/generators/test-generator/adapters/appium/templates/steps/assertions/visible-with-role-variable-assertion.hbs +7 -3
- package/src/generators/test-generator/adapters/appium/templates/steps/partials/await-elements.hbs +12 -0
- package/src/generators/test-generator/adapters/appium/templates/test-file.hbs +94 -4
- package/src/generators/test-generator/adapters/playwright/templates/steps/actions/capture-variable.hbs +7 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/label-value-assertion.hbs +7 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/row-scoped-column-assertion.hbs +23 -1
- package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/visible-with-role-variable-assertion.hbs +8 -4
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +4 -4
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/default.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.hbs +4 -4
- package/src/generators/test-generator/adapters/playwright/templates/steps/partials/url-assertion.hbs +17 -2
- package/src/generators/test-generator/adapters/playwright/templates/test-file.hbs +14 -0
- package/src/generators/test-generator/code-generator.ts +119 -19
- package/src/generators/test-generator/diagnostics.ts +70 -4
- package/src/generators/test-generator/patterns/index.ts +16 -9
- package/src/generators/test-generator/patterns/types.ts +15 -0
- package/src/generators/test-generator/patterns/with-parent-scope.ts +6 -0
- package/src/generators/test-generator/step-mapper.ts +196 -13
- package/src/generators/test-generator/template-engine.ts +18 -0
- package/src/generators/test-generator/utils/data-resolver.ts +41 -12
- package/src/generators/test-generator/utils/selector-entry-fields.ts +1 -0
- package/src/generators/test-generator/utils/selector-resolver.ts +256 -85
- package/src/harness/audit.ts +41 -12
- package/src/harness/capability.ts +23 -12
- package/src/harness/catalog/universal-viewpoints.yaml +5 -5
- package/src/harness/config-file.ts +66 -0
- package/src/harness/depth-lint.ts +2 -1
- package/src/harness/flow-check.ts +6 -2
- package/src/harness/keyword-match.ts +85 -0
- package/src/harness/next-step.ts +2 -1
- package/src/harness/parse.ts +42 -7
- package/src/harness/perf.ts +15 -7
- package/src/harness/quality-gates.ts +16 -4
- package/src/harness/query-catalog.ts +0 -0
- package/src/harness/read-text.ts +7 -1
- package/src/harness/secret-scan.ts +60 -4
- package/src/harness/sensors.ts +50 -19
- package/src/harness/spec-branches.ts +72 -14
- package/src/harness/spec-coverage.ts +133 -13
- package/src/harness/viewpoint-ledger.ts +2 -1
- package/src/harness/write-atomic.ts +22 -0
- package/src/orchestrator/ai-skills/writer.ts +18 -3
- package/src/orchestrator/appium-environment-setup.ts +127 -0
- package/src/orchestrator/assets-drift.ts +10 -5
- package/src/orchestrator/mobile-runtime-scaffolder.ts +2 -0
- package/src/orchestrator/project-deps-sync.ts +23 -1
- package/src/orchestrator/project-initializer.ts +69 -7
- package/src/orchestrator/security-overrides-catalog.ts +69 -0
- package/src/orchestrator/security-overrides.ts +149 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +14 -0
- package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +8 -2
- package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +64 -2
- package/src/orchestrator/templates/ai-src/skills/sungen-selector-keys/SKILL.md +13 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +20 -5
- package/src/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +3 -3
- package/src/orchestrator/templates/appium-environment/README.md +83 -0
- package/src/orchestrator/templates/appium-environment/scripts/common/install-appium.ps1 +44 -0
- package/src/orchestrator/templates/appium-environment/scripts/common/install-appium.sh +50 -0
- package/src/orchestrator/templates/appium-environment/scripts/common/lib.ps1 +108 -0
- package/src/orchestrator/templates/appium-environment/scripts/common/lib.sh +141 -0
- package/src/orchestrator/templates/appium-environment/scripts/common/verify-appium.ps1 +39 -0
- package/src/orchestrator/templates/appium-environment/scripts/common/verify-appium.sh +43 -0
- package/src/orchestrator/templates/appium-environment/scripts/common/verify-node.ps1 +13 -0
- package/src/orchestrator/templates/appium-environment/scripts/common/verify-node.sh +16 -0
- package/src/orchestrator/templates/appium-environment/scripts/macos/install-android.sh +110 -0
- package/src/orchestrator/templates/appium-environment/scripts/macos/install-ios.sh +32 -0
- package/src/orchestrator/templates/appium-environment/scripts/macos/verify-android.sh +32 -0
- package/src/orchestrator/templates/appium-environment/scripts/macos/verify-ios.sh +15 -0
- package/src/orchestrator/templates/appium-environment/scripts/windows/install-android.ps1 +153 -0
- package/src/orchestrator/templates/appium-environment/scripts/windows/verify-android.ps1 +26 -0
- package/src/orchestrator/templates/appium-environment/setup.ps1 +58 -0
- package/src/orchestrator/templates/appium-environment/setup.sh +88 -0
- package/src/orchestrator/templates/specs-checked-state.ts +31 -6
- package/src/orchestrator/templates/specs-locale-fixture.ts +21 -9
- package/src/orchestrator/templates/specs-mock.ts +12 -4
- package/src/orchestrator/templates/specs-pw-shape-reporter.ts +0 -0
- package/src/orchestrator/templates/specs-url-assert.ts +24 -2
- package/src/orchestrator/templates/wdio.conf.ts +24 -1
|
@@ -56,7 +56,7 @@ Scenario: VP-VAL-002 An email in an invalid format shows the format error
|
|
|
56
56
|
2. **ENV-BOUND** (must exist in the test environment; harmless if leaked): seeded ids, registered emails, valid tokens → keep in yaml with a `# ENV-BOUND: <what to seed>` marker; per-environment values go in `<unit>.<env>.yaml`.
|
|
57
57
|
3. **SECRET** (real login / harmful if leaked): NEVER write the real value — reference `${QA_<NAME>}` (uppercase, `QA_` prefix mandatory; only that shape resolves from `.env.qa` at runtime, any other `${...}` stays literal). Whenever you emit a new `${QA_*}` ref, ALSO append the name to `.env.qa.example`. Note: "password" is not automatically SECRET — a made-up password probing validation is INVENTED; only a real account's password is SECRET (and `@auth:role` via `sungen makeauth` usually removes the need entirely).
|
|
58
58
|
|
|
59
|
-
-
|
|
59
|
+
- Mixing `@auth:X` + `@no-auth` in one feature is fine by default; just never tag that feature `@serial`
|
|
60
60
|
→ Playwright shares browser context per worker; auth state leaks across scenarios without isolation.
|
|
61
61
|
|
|
62
62
|
- XSS ≠ SQL injection — generate as **2 separate scenarios**, never merge
|
|
@@ -568,7 +568,7 @@ and a real oracle, not a shrug: write the steps a human follows and the value th
|
|
|
568
568
|
|
|
569
569
|
Add cleanup tags per the `sungen-gherkin-syntax` Cleanup table. Key rules:
|
|
570
570
|
- **Always `@cleanup:overlay`** if ANY section opens a dialog; **always `@cleanup:forms`** if the screen has inline search, filter, or editable forms.
|
|
571
|
-
- **`@parallel`**
|
|
571
|
+
- **`@parallel`** names the default isolation, so mixing auth groups (`@auth:X` + `@no-auth`) needs no tag at all — only `@serial` breaks it, because one shared context cannot hold two sessions. Still worth stating explicitly on validation-heavy features that want a clean form state per scenario.
|
|
572
572
|
- **`@afterEach`** hook only when `@cleanup:*` tags aren't enough (feature-specific reset logic).
|
|
573
573
|
|
|
574
574
|
---
|
|
@@ -838,9 +838,24 @@ is the common failure. Every navigation target the spec names needs a click-thro
|
|
|
838
838
|
(`NAV-TARGET-ABSENT`), even when this flow does not consume the destination — a context param
|
|
839
839
|
handed to another screen is that screen's input, and the handoff is where it goes wrong.
|
|
840
840
|
|
|
841
|
-
**A
|
|
842
|
-
when the content is hidden for everyone. Pair it with "permission granted → content
|
|
843
|
-
way a boundary needs `min-1 / min / max / max+1` (`PERMISSION-PAIR-MISSING`).
|
|
841
|
+
**A hidden case needs its shown case.** "permission denied → content hidden" passes just as
|
|
842
|
+
happily when the content is hidden for everyone. Pair it with "permission granted → content
|
|
843
|
+
shown", the way a boundary needs `min-1 / min / max / max+1` (`PERMISSION-PAIR-MISSING`).
|
|
844
|
+
|
|
845
|
+
The same holds for ANY condition, not just permission: an element you assert `is hidden` in one
|
|
846
|
+
state must be asserted PRESENT in some other state, or nothing distinguishes a working rule from a
|
|
847
|
+
feature that is broken for everyone — and a mistyped selector matches nothing, so `is hidden`
|
|
848
|
+
passes forever on its own (`VISIBILITY-PAIR-MISSING`). Conditional buttons, empty-vs-populated
|
|
849
|
+
lists, the active tab, a radio state: "show A or B, never both" is a very common implementation
|
|
850
|
+
defect, and only the pair catches it.
|
|
851
|
+
|
|
852
|
+
**A requirement that states both directions is TWO requirements.** "`X` MUST be shown when no
|
|
853
|
+
score is set; MUST NOT be shown once a score exists" is one line and two mutually-exclusive states,
|
|
854
|
+
so one scenario cannot prove it — tagging `@spec:FR-013` on the positive case closes the ID, not
|
|
855
|
+
the rule. Write one case per direction; `sungen audit` counts them as two coverage units and
|
|
856
|
+
reports the unproved half as `SPEC-CLAUSE-UNCOVERED`. The gate only presses a requirement that
|
|
857
|
+
NAMES its element and speaks about visibility or enabledness — but the habit is worth keeping for
|
|
858
|
+
every opposing rule, gated or not.
|
|
844
859
|
|
|
845
860
|
**State that ARRIVES with the data is a different case from state the test creates.** A
|
|
846
861
|
discriminator field you can only observe at load (an item already favourited before the page
|
|
@@ -105,7 +105,7 @@ Hard-fail gate. Any ✗ is a syntax error — must be fixed; do not score furthe
|
|
|
105
105
|
|
|
106
106
|
- [ ] Exactly **one** priority tag per scenario (`@high`/`@normal`/`@low`…)
|
|
107
107
|
- [ ] `@manual`/`@auth:role`/`@no-auth`/`@cleanup:*`/`@parallel`/`@flow` used in the right context
|
|
108
|
-
- [ ]
|
|
108
|
+
- [ ] A feature mixing auth groups (`@auth:user` + `@no-auth`) is NOT `@serial` — parallel is the default and one shared context cannot hold two sessions
|
|
109
109
|
- [ ] `@flow`: `[Screen:Element]` namespace is consistent; YAML keys quoted with the colon (`"login:submit":`)
|
|
110
110
|
|
|
111
111
|
### A8. Mobile (`@platform:mobile/android/ios` units only)
|
|
@@ -321,7 +321,7 @@ Do NOT deduct points when a `.feature` lacks the following viewpoints (Playwrigh
|
|
|
321
321
|
12. **Orphan key in test-data** (the advisory lint after `sungen generate` says `defined but never referenced`; a `@query`/`@api` param bound by name, a `@manual` comment ref or a `_*` metadata key is not one) → delete it or bind it into a scenario. **Unbound `{{x}}` in a step** (the same lint says `is not a test-data key and nothing binds it`) → add the key or fix the ref — left alone it runs as the literal text `${x}`.
|
|
322
322
|
13. **Unnecessary CSS/locator selector** → switch to role+name/label/text; keys use spaces.
|
|
323
323
|
14. **Scope crammed into `Background`** → split into `@steps`/`@extend`.
|
|
324
|
-
15. **Mixed auth groups
|
|
324
|
+
15. **Mixed auth groups on a `@serial` feature** → remove `@serial`, or split one feature per auth role.
|
|
325
325
|
16. **Missing secondary behaviors** — spec defines tiebreaker, fallback rule, or secondary sort but no scenario tests it? Add 1 `@high` TC per rule.
|
|
326
326
|
17. **Missing concurrency scenarios** — spec or test-viewpoint mentions multi-tab, multi-user, or simultaneous actions but no `@manual` scenario exists? Add 1 `@manual` TC per risk (`@normal` by default; `@high` if data integrity at risk).
|
|
327
327
|
18. **Mobile: `see [X] page` oracle** → replace with a marker element on the destination screen + the origin's marker `is hidden` (SG-W022 — the original passes having proven nothing).
|
|
@@ -363,7 +363,7 @@ If `selectors/<name>.yaml` exists, count lines matching `@needs-live-verify` (pr
|
|
|
363
363
|
- [ ] Assertions follow the 8 patterns, no redundant `is visible`
|
|
364
364
|
- [ ] Every {{var}} exists in test-data.yaml and no orphan keys — both reported by the advisory data-driven lint at `sungen generate` (not `audit`); an unbound {{var}} compiles to a literal placeholder, never a compile error
|
|
365
365
|
- [ ] Selector keys follow sungen-selector-keys; locator only as last resort
|
|
366
|
-
- [ ] Tags valid;
|
|
366
|
+
- [ ] Tags valid; no `@serial` on a feature that mixes auth groups
|
|
367
367
|
- Syntax errors found: <n>
|
|
368
368
|
|
|
369
369
|
### Coverage Matrix (Layer B)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# appium-environment/ — one command to a working Appium machine
|
|
2
|
+
|
|
3
|
+
Emitted by `sungen init --mobile` (or `sungen capability add mobile`), which also runs it once.
|
|
4
|
+
It provisions everything **below** the Appium layer — the part QA used to do by hand — and ends with
|
|
5
|
+
`appium driver doctor`. Every step is **check → install → verify**: a satisfied step is skipped, so the
|
|
6
|
+
bundle is safe to re-run any time (a new machine, a teammate's clone, after an OS update).
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
sungen setup-mobile-env # any OS — emits this folder if missing, then runs it
|
|
10
|
+
./appium-environment/setup.sh # macOS, directly
|
|
11
|
+
powershell -NoProfile -ExecutionPolicy Bypass -File .\appium-environment\setup.ps1 # Windows, directly
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
| Flag (macOS) | Flag (Windows) | Effect |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| `--check` | `-Check` | report what is missing, install nothing (side-effect free) |
|
|
17
|
+
| `--skip-android` | `-SkipAndroid` | skip JDK / Android SDK / uiautomator2 |
|
|
18
|
+
| `--skip-ios` | — | skip Xcode CLT / applesimutils / xcuitest (iOS needs a Mac) |
|
|
19
|
+
| `--no-profile` | `-NoProfile` | do not write env vars to your shell profile / User environment |
|
|
20
|
+
|
|
21
|
+
Exit code: `0` ready (or only warnings) · `1` something failed · `2` unsupported OS / bad flag.
|
|
22
|
+
`sungen init` treats a non-zero exit as a warning — the project scaffold is already complete.
|
|
23
|
+
|
|
24
|
+
## What it installs (and what it only checks)
|
|
25
|
+
|
|
26
|
+
| Step | macOS (`setup.sh`) | Windows (`setup.ps1`) |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| Node.js ≥ 18 | check only (sungen needs it already) | check only |
|
|
29
|
+
| Package manager | Homebrew — installed if missing | winget — check only (App Installer, Microsoft Store) |
|
|
30
|
+
| JDK ≥ 17 | `brew install openjdk@21` (an existing JDK ≥ 17 is kept) | `winget install Microsoft.OpenJDK.21` (existing JDK ≥ 17 kept) |
|
|
31
|
+
| Android SDK root | existing `ANDROID_HOME` / Android Studio SDK / `brew install --cask android-commandlinetools` | existing / `%LOCALAPPDATA%\Android\Sdk` / Google's command-line tools zip |
|
|
32
|
+
| `platform-tools` (adb) + `emulator` | `sdkmanager` | `sdkmanager` |
|
|
33
|
+
| Env vars | marked block in `~/.zshrc` (or `~/.bash_profile`) | User environment (`ANDROID_HOME`, `ANDROID_SDK_ROOT`, `JAVA_HOME`, `Path`) |
|
|
34
|
+
| Android Studio | **check + hint only** (optional — AVD manager / on-screen emulator) | same |
|
|
35
|
+
| Xcode Command Line Tools | `xcode-select --install` (Apple's dialog — finish it, re-run) | — |
|
|
36
|
+
| Xcode.app | **check + hint only** (App Store; required for iOS) | — |
|
|
37
|
+
| Xcode license | `sudo xcodebuild -license accept` | — |
|
|
38
|
+
| `applesimutils` | `brew tap wix/brew && brew install applesimutils` | — |
|
|
39
|
+
| Appium | **project-local** (`node_modules/.bin/appium`, from `@sungen/driver-mobile`) — never global | same |
|
|
40
|
+
| Appium drivers | `uiautomator2` + `xcuitest` into `~/.appium` | `uiautomator2` |
|
|
41
|
+
| Health | `adb`, env vars, devices, Simulators, `appium driver doctor` | `adb`, env vars, devices, `appium driver doctor` |
|
|
42
|
+
|
|
43
|
+
Nothing is installed globally with npm. GUI apps (Xcode.app, Android Studio) are never installed for you —
|
|
44
|
+
the step tells you exactly where to get them and re-running the script picks up from there.
|
|
45
|
+
|
|
46
|
+
## The env-var block
|
|
47
|
+
|
|
48
|
+
macOS: the lines live between `# >>> sungen mobile env >>>` and `# <<< sungen mobile env <<<` in your
|
|
49
|
+
shell profile; re-running rewrites only that block, and it is skipped entirely when your shell already
|
|
50
|
+
exports matching values. To undo, delete the block. Windows: plain User-scope variables — undo in
|
|
51
|
+
*System Properties → Environment Variables*.
|
|
52
|
+
|
|
53
|
+
**Open a new terminal after the first run** so `PATH` / `JAVA_HOME` / `ANDROID_HOME` apply.
|
|
54
|
+
|
|
55
|
+
## Known limitations
|
|
56
|
+
|
|
57
|
+
- **Linux** is not provisioned (the entry point prints a notice and exits 0). Android on Linux: install a
|
|
58
|
+
JDK ≥ 17 and the Android command-line tools with your package manager, export the same variables, then
|
|
59
|
+
`sungen capability add mobile` for the project-local Appium + `uiautomator2`.
|
|
60
|
+
- **`sungen update` does not refresh this folder.** To pick up a newer bundle, delete `appium-environment/`
|
|
61
|
+
and run `sungen capability add mobile` again (all other files are skipped because they already exist).
|
|
62
|
+
- Windows downloads Google's command-line tools by a **pinned build number** (`$CmdlineToolsBuild` at the
|
|
63
|
+
top of `scripts/windows/install-android.ps1`). If the download 404s, bump it from
|
|
64
|
+
https://developer.android.com/studio#command-line-tools-only.
|
|
65
|
+
- No AVD / Simulator is created and no phone is paired — plug in a device (USB debugging on) or start an
|
|
66
|
+
emulator before `/sungen:run-test`.
|
|
67
|
+
|
|
68
|
+
## Manual fallback (the steps this bundle automates)
|
|
69
|
+
|
|
70
|
+
- Android: https://sungen.sun-asterisk.vn/docs/mobile/appium-setup-android
|
|
71
|
+
- iOS: https://sungen.sun-asterisk.vn/docs/mobile/appium-setup-ios
|
|
72
|
+
- Overview + troubleshooting: https://sungen.sun-asterisk.vn/docs/mobile/appium-setup ·
|
|
73
|
+
https://sungen.sun-asterisk.vn/docs/mobile/mobile-troubleshooting
|
|
74
|
+
|
|
75
|
+
## Layout
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
appium-environment/
|
|
79
|
+
├── setup.sh · setup.ps1 entry points (OS check, flags, step order, summary)
|
|
80
|
+
├── scripts/common/ lib.{sh,ps1} step runner · verify-node · install-appium · verify-appium
|
|
81
|
+
├── scripts/macos/ install-android · verify-android · install-ios · verify-ios
|
|
82
|
+
└── scripts/windows/ install-android · verify-android
|
|
83
|
+
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# install-appium.ps1 - Appium stays PROJECT-LOCAL (a devDependency pulled in by @sungen/driver-mobile),
|
|
2
|
+
# never `npm i -g appium`; the platform driver (uiautomator2) lives in ~\.appium and is installed
|
|
3
|
+
# through that local CLI. Mirrors what `sungen init --mobile` does in TypeScript, so this script also
|
|
4
|
+
# works on a fresh clone where a teammate ran init.
|
|
5
|
+
if (-not $script:SungenEnvLibLoaded) { . (Join-Path $PSScriptRoot 'lib.ps1') }
|
|
6
|
+
|
|
7
|
+
$AppiumCmd = $script:SungenEnvAppiumCmd
|
|
8
|
+
|
|
9
|
+
function Get-AppiumVersion { if (Test-Path $AppiumCmd) { ((& $AppiumCmd -v) 2>$null | Select-Object -First 1) } }
|
|
10
|
+
Invoke-Step -Title 'Appium (project-local)' -Check {
|
|
11
|
+
if (Test-Path $AppiumCmd) { "appium $(Get-AppiumVersion) (project-local: node_modules\.bin\appium)" }
|
|
12
|
+
} -Install {
|
|
13
|
+
# Only re-install when the project already declares the mobile driver - otherwise the fix is
|
|
14
|
+
# `sungen capability add mobile`, a consent-gated sungen command, not this script's call.
|
|
15
|
+
$pkg = Join-Path $script:ProjectDir 'package.json'
|
|
16
|
+
if ((Test-Path $pkg) -and ((Get-Content $pkg -Raw) -match '"@sungen/driver-mobile"')) {
|
|
17
|
+
Push-Location $script:ProjectDir; try { Invoke-Native npm @('install') } finally { Pop-Location }
|
|
18
|
+
} else { return 'needs-user' }
|
|
19
|
+
} -Verify {
|
|
20
|
+
if (Test-Path $AppiumCmd) { "appium $(Get-AppiumVersion)" }
|
|
21
|
+
} -Hint "Appium is project-local (never global). Add the mobile driver + runtime with:`n sungen capability add mobile"
|
|
22
|
+
|
|
23
|
+
# --- platform driver -------------------------------------------------------------------------------
|
|
24
|
+
function Get-InstalledDrivers {
|
|
25
|
+
$raw = (& $AppiumCmd driver list --installed --json) 2>$null | Out-String
|
|
26
|
+
if (-not $raw.Trim()) { return $null }
|
|
27
|
+
try { return ($raw | ConvertFrom-Json) } catch { return $null }
|
|
28
|
+
}
|
|
29
|
+
function Get-DriverDetail([string]$Name) {
|
|
30
|
+
$j = Get-InstalledDrivers
|
|
31
|
+
if ($j -and ($j.PSObject.Properties.Name -contains $Name)) { "$Name $($j.$Name.version) in ~\.appium" }
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (Test-Path $AppiumCmd) {
|
|
35
|
+
if (-not $script:SungenEnvSkipAndroid) {
|
|
36
|
+
$script:CurrentDriver = 'uiautomator2'
|
|
37
|
+
Invoke-Step -Title "Appium driver: $($script:CurrentDriver)" -Check { Get-DriverDetail $script:CurrentDriver } `
|
|
38
|
+
-Install { Invoke-Native $AppiumCmd @('driver', 'install', $script:CurrentDriver) } `
|
|
39
|
+
-Verify { Get-DriverDetail $script:CurrentDriver } `
|
|
40
|
+
-Hint "retry: npx appium driver install $($script:CurrentDriver)"
|
|
41
|
+
}
|
|
42
|
+
} else {
|
|
43
|
+
Write-Warn 'Appium platform driver (uiautomator2)' 'skipped - Appium is not installed in this project yet (see the step above)'
|
|
44
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# install-appium.sh — Appium stays PROJECT-LOCAL (a devDependency pulled in by @sungen/driver-mobile),
|
|
3
|
+
# never `npm i -g appium`; the platform drivers (uiautomator2 / xcuitest) live in ~/.appium and are
|
|
4
|
+
# installed through that local CLI. Mirrors what `sungen init --mobile` does in TypeScript, so this
|
|
5
|
+
# script also works on a fresh clone where a teammate ran init.
|
|
6
|
+
[ -n "${SUNGEN_ENV_LIB_LOADED:-}" ] || . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib.sh"
|
|
7
|
+
|
|
8
|
+
APPIUM_BIN=$SUNGEN_ENV_APPIUM_BIN
|
|
9
|
+
|
|
10
|
+
appium_check() {
|
|
11
|
+
[ -x "$APPIUM_BIN" ] || return 1
|
|
12
|
+
printf 'appium %s (project-local: node_modules/.bin/appium)' "$("$APPIUM_BIN" -v 2>/dev/null)"
|
|
13
|
+
}
|
|
14
|
+
appium_install() {
|
|
15
|
+
# Only re-install when the project already declares the mobile driver — otherwise the fix is
|
|
16
|
+
# `sungen capability add mobile`, which is a consent-gated sungen command, not this script's call.
|
|
17
|
+
if grep -q '"@sungen/driver-mobile"' "$PROJECT_DIR/package.json" 2>/dev/null; then
|
|
18
|
+
(cd "$PROJECT_DIR" && npm install)
|
|
19
|
+
else
|
|
20
|
+
return 2
|
|
21
|
+
fi
|
|
22
|
+
}
|
|
23
|
+
appium_verify() {
|
|
24
|
+
appium_check || { printf 'Appium is project-local (never global). Add the mobile driver + runtime with:\n sungen capability add mobile'; return 1; }
|
|
25
|
+
}
|
|
26
|
+
run_step "Appium (project-local)" appium_check appium_install appium_verify
|
|
27
|
+
|
|
28
|
+
# ── platform drivers — one run_step per driver, CURRENT_DRIVER carries the name (bash 3.2: no closures)
|
|
29
|
+
installed_drivers_json() { "$APPIUM_BIN" driver list --installed --json 2>/dev/null; }
|
|
30
|
+
drv_check() {
|
|
31
|
+
local json ver
|
|
32
|
+
json=$(installed_drivers_json) || return 1
|
|
33
|
+
printf '%s' "$json" | grep -q "\"$CURRENT_DRIVER\"" || return 1
|
|
34
|
+
ver=$(printf '%s' "$json" | node -e "let s='';process.stdin.on('data',d=>s+=d).on('end',()=>{try{const j=JSON.parse(s);process.stdout.write((j['$CURRENT_DRIVER']||{}).version||'')}catch{}})" 2>/dev/null)
|
|
35
|
+
printf '%s %s in ~/.appium' "$CURRENT_DRIVER" "${ver:-}"
|
|
36
|
+
}
|
|
37
|
+
drv_install() { "$APPIUM_BIN" driver install "$CURRENT_DRIVER"; }
|
|
38
|
+
drv_verify() { drv_check || { printf 'retry: npx appium driver install %s' "$CURRENT_DRIVER"; return 1; }; }
|
|
39
|
+
|
|
40
|
+
if [ -x "$APPIUM_BIN" ]; then
|
|
41
|
+
APPIUM_DRIVERS="uiautomator2"
|
|
42
|
+
[ "$SUNGEN_ENV_SKIP_ANDROID" = 1 ] && APPIUM_DRIVERS=""
|
|
43
|
+
# xcuitest builds WebDriverAgent with Xcode at run time → only meaningful on a Mac.
|
|
44
|
+
if [ "$(uname -s)" = Darwin ] && [ "$SUNGEN_ENV_SKIP_IOS" != 1 ]; then APPIUM_DRIVERS="$APPIUM_DRIVERS xcuitest"; fi
|
|
45
|
+
for CURRENT_DRIVER in $APPIUM_DRIVERS; do
|
|
46
|
+
run_step "Appium driver: $CURRENT_DRIVER" drv_check drv_install drv_verify
|
|
47
|
+
done
|
|
48
|
+
else
|
|
49
|
+
log_warn "Appium platform drivers (uiautomator2 / xcuitest)" "skipped — Appium is not installed in this project yet (see the step above)"
|
|
50
|
+
fi
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# lib.ps1 - shared step runner for the sungen appium-environment bundle (Windows side).
|
|
2
|
+
# Dot-sourced by setup.ps1 and by every scripts\**\*.ps1; never run directly.
|
|
3
|
+
# Windows PowerShell 5.1 compatible: no ternary, no ??, ASCII only (5.1 reads BOM-less files as ANSI).
|
|
4
|
+
#
|
|
5
|
+
# Step model (see docs: Mobile -> Appium setup -> "One command"):
|
|
6
|
+
# Check -> returns a detail string when already satisfied, $null otherwise (side-effect free)
|
|
7
|
+
# Install -> idempotent; output streams to the console; returns 'needs-user' when the user must finish it
|
|
8
|
+
# Verify -> returns a detail string when satisfied, $null otherwise; -Hint is shown on failure
|
|
9
|
+
|
|
10
|
+
if ($script:SungenEnvLibLoaded) { return }
|
|
11
|
+
$script:SungenEnvLibLoaded = $true
|
|
12
|
+
$ErrorActionPreference = 'Continue'
|
|
13
|
+
|
|
14
|
+
# Flags - set by setup.ps1's parameters, read by every step.
|
|
15
|
+
if ($null -eq $script:SungenEnvCheckOnly) { $script:SungenEnvCheckOnly = $false }
|
|
16
|
+
if ($null -eq $script:SungenEnvSkipAndroid) { $script:SungenEnvSkipAndroid = $false }
|
|
17
|
+
if ($null -eq $script:SungenEnvNoProfile) { $script:SungenEnvNoProfile = $false }
|
|
18
|
+
# Flipped to $true when the User environment actually changed.
|
|
19
|
+
$script:SungenEnvProfileChanged = $false
|
|
20
|
+
|
|
21
|
+
# Paths: <project>\appium-environment\scripts\common\lib.ps1 -> bundle = ..\.., project = bundle\..
|
|
22
|
+
$script:SungenEnvBundleDir = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
|
|
23
|
+
if (-not $script:ProjectDir) { $script:ProjectDir = (Resolve-Path (Join-Path $script:SungenEnvBundleDir '..')).Path }
|
|
24
|
+
# Appium is project-local by design (never `npm i -g appium`) - see install-appium.ps1.
|
|
25
|
+
$script:SungenEnvAppiumCmd = Join-Path $script:ProjectDir 'node_modules\.bin\appium.cmd'
|
|
26
|
+
|
|
27
|
+
$script:CountOk = 0; $script:CountSkip = 0; $script:CountWarn = 0; $script:CountFail = 0
|
|
28
|
+
|
|
29
|
+
function Write-Section([string]$Title) { Write-Host ''; Write-Host $Title -ForegroundColor White }
|
|
30
|
+
function Write-Detail([string]$Text) {
|
|
31
|
+
if (-not $Text) { return }
|
|
32
|
+
foreach ($line in ($Text -split "`r?`n")) { if ($line) { Write-Host " $line" -ForegroundColor DarkGray } }
|
|
33
|
+
}
|
|
34
|
+
function Write-Ok([string]$Title, [string]$Detail) { $script:CountOk++; Write-Host "[ok] $Title" -ForegroundColor Green; Write-Detail $Detail }
|
|
35
|
+
function Write-Skip([string]$Title, [string]$Detail) { $script:CountSkip++; Write-Host "[skip] $Title" -ForegroundColor DarkGray; Write-Detail $Detail }
|
|
36
|
+
function Write-Warn([string]$Title, [string]$Detail) { $script:CountWarn++; Write-Host "[warn] $Title" -ForegroundColor Yellow; Write-Detail $Detail }
|
|
37
|
+
function Write-Fail([string]$Title, [string]$Detail) { $script:CountFail++; Write-Host "[fail] $Title" -ForegroundColor Red; Write-Detail $Detail }
|
|
38
|
+
function Write-Install([string]$Title) { Write-Host "[install] $Title" -ForegroundColor Cyan }
|
|
39
|
+
|
|
40
|
+
# Invoke-Native <exe> <args...> - run a native command, throw on a non-zero exit code.
|
|
41
|
+
function Invoke-Native([string]$File, [string[]]$Arguments) {
|
|
42
|
+
& $File @Arguments
|
|
43
|
+
if ($LASTEXITCODE -ne 0) { throw "$File exited with code $LASTEXITCODE" }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
# Invoke-Step - an install-type step.
|
|
47
|
+
function Invoke-Step {
|
|
48
|
+
param([string]$Title, [scriptblock]$Check, [scriptblock]$Install, [scriptblock]$Verify, [string]$Hint)
|
|
49
|
+
$found = $null; try { $found = & $Check } catch { $found = $null }
|
|
50
|
+
if ($found) { Write-Skip $Title "$found"; return }
|
|
51
|
+
if ($script:SungenEnvCheckOnly) { Write-Fail $Title 'not installed - run without -Check to install'; return }
|
|
52
|
+
Write-Install $Title
|
|
53
|
+
$needsUser = $false; $err = $null
|
|
54
|
+
try { $r = & $Install; if ("$r" -eq 'needs-user') { $needsUser = $true } } catch { $err = $_.Exception.Message }
|
|
55
|
+
$ok = $null; try { $ok = & $Verify } catch { $ok = $null }
|
|
56
|
+
if ($ok) { Write-Ok $Title "$ok"; return }
|
|
57
|
+
$msg = $Hint; if ($err) { $msg = "$err`n$Hint" }
|
|
58
|
+
if ($needsUser) { Write-Warn $Title $msg } else { Write-Fail $Title $msg }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
# Invoke-Check - a verification-only step (ok / fail, or -Warn for informational checks).
|
|
62
|
+
function Invoke-Check {
|
|
63
|
+
param([string]$Title, [scriptblock]$Check, [string]$Hint, [switch]$Warn)
|
|
64
|
+
$out = $null; try { $out = & $Check } catch { $out = $null }
|
|
65
|
+
if ($out) { Write-Ok $Title "$out"; return }
|
|
66
|
+
if ($Warn) { Write-Warn $Title $Hint } else { Write-Fail $Title $Hint }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# Invoke-Guided - GUI installs this bundle never performs itself: skip when present, else warn with the next action.
|
|
70
|
+
function Invoke-Guided {
|
|
71
|
+
param([string]$Title, [scriptblock]$Check, [string]$Guidance)
|
|
72
|
+
$found = $null; try { $found = & $Check } catch { $found = $null }
|
|
73
|
+
if ($found) { Write-Skip $Title "$found" } else { Write-Warn $Title $Guidance }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
# --- user environment (the Windows counterpart of the shell-profile block) ------------------------
|
|
77
|
+
# Update-SessionPath - pick up PATH changes made by installers (winget) without a new console.
|
|
78
|
+
function Update-SessionPath {
|
|
79
|
+
$env:Path = [Environment]::GetEnvironmentVariable('Path', 'Machine') + ';' + [Environment]::GetEnvironmentVariable('Path', 'User')
|
|
80
|
+
}
|
|
81
|
+
# Set-UserEnv <name> <value> - user-scope variable (what `setx` does, without its 1024-char truncation). $true when changed.
|
|
82
|
+
function Set-UserEnv([string]$Name, [string]$Value) {
|
|
83
|
+
if ([Environment]::GetEnvironmentVariable($Name, 'User') -eq $Value) { Set-Item -Path "env:$Name" -Value $Value; return $false }
|
|
84
|
+
[Environment]::SetEnvironmentVariable($Name, $Value, 'User')
|
|
85
|
+
Set-Item -Path "env:$Name" -Value $Value
|
|
86
|
+
$script:SungenEnvProfileChanged = $true
|
|
87
|
+
return $true
|
|
88
|
+
}
|
|
89
|
+
# Add-UserPath <dir> - append to the user-scope Path once; also to this session. $true when changed.
|
|
90
|
+
function Add-UserPath([string]$Dir) {
|
|
91
|
+
$current = [Environment]::GetEnvironmentVariable('Path', 'User')
|
|
92
|
+
$parts = @(); if ($current) { $parts = @($current -split ';' | Where-Object { $_ }) }
|
|
93
|
+
if (-not (($env:Path -split ';') -contains $Dir)) { $env:Path = "$env:Path;$Dir" }
|
|
94
|
+
if ($parts -contains $Dir) { return $false }
|
|
95
|
+
[Environment]::SetEnvironmentVariable('Path', (($parts + $Dir) -join ';'), 'User')
|
|
96
|
+
$script:SungenEnvProfileChanged = $true
|
|
97
|
+
return $true
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
# Write-Summary - tallies + the "new terminal" notice. Returns 1 when anything failed, else 0.
|
|
101
|
+
function Write-Summary {
|
|
102
|
+
Write-Host ''
|
|
103
|
+
Write-Host "$($script:CountOk) ok, $($script:CountSkip) skipped, $($script:CountWarn) warned, $($script:CountFail) failed" -ForegroundColor White
|
|
104
|
+
if ($script:SungenEnvProfileChanged) { Write-Host '-> Open a NEW terminal so Path / JAVA_HOME / ANDROID_HOME apply.' -ForegroundColor Yellow }
|
|
105
|
+
if ($script:CountWarn -gt 0) { Write-Host ' warnings need a manual step - do it, then re-run this script.' }
|
|
106
|
+
if ($script:CountFail -gt 0) { return 1 }
|
|
107
|
+
return 0
|
|
108
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# lib.sh — shared step runner for the sungen appium-environment bundle (macOS / Linux side).
|
|
3
|
+
# Sourced by setup.sh and by every scripts/**/*.sh; never run directly.
|
|
4
|
+
# Bash 3.2 compatible (the /bin/bash that ships with macOS): no associative arrays, no ${var,,}.
|
|
5
|
+
#
|
|
6
|
+
# Step model (see docs: Mobile → Appium setup → "One command"):
|
|
7
|
+
# check → exit 0 when already satisfied (side-effect free — this is all `--check` runs)
|
|
8
|
+
# install → idempotent; output streams to the terminal; exit 2 = started but needs a user action
|
|
9
|
+
# verify → exit 0 when satisfied; stdout is the detail (ok) or the hint (fail)
|
|
10
|
+
|
|
11
|
+
[ -n "${SUNGEN_ENV_LIB_LOADED:-}" ] && return 0
|
|
12
|
+
SUNGEN_ENV_LIB_LOADED=1
|
|
13
|
+
|
|
14
|
+
# Flags — set by setup.sh's argument parser, read by every step.
|
|
15
|
+
SUNGEN_ENV_CHECK_ONLY=${SUNGEN_ENV_CHECK_ONLY:-0}
|
|
16
|
+
SUNGEN_ENV_SKIP_ANDROID=${SUNGEN_ENV_SKIP_ANDROID:-0}
|
|
17
|
+
SUNGEN_ENV_SKIP_IOS=${SUNGEN_ENV_SKIP_IOS:-0}
|
|
18
|
+
SUNGEN_ENV_NO_PROFILE=${SUNGEN_ENV_NO_PROFILE:-0}
|
|
19
|
+
# Flipped to 1 by write_profile_block when the shell profile actually changed.
|
|
20
|
+
SUNGEN_ENV_PROFILE_CHANGED=0
|
|
21
|
+
|
|
22
|
+
# Paths: <project>/appium-environment/scripts/common/lib.sh → bundle = ../.., project = bundle/..
|
|
23
|
+
SUNGEN_ENV_BUNDLE_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)
|
|
24
|
+
PROJECT_DIR=${PROJECT_DIR:-$(cd "$SUNGEN_ENV_BUNDLE_DIR/.." && pwd)}
|
|
25
|
+
# Appium is project-local by design (never `npm i -g appium`) — see install-appium.sh.
|
|
26
|
+
SUNGEN_ENV_APPIUM_BIN="$PROJECT_DIR/node_modules/.bin/appium"
|
|
27
|
+
|
|
28
|
+
# Environment as it was BEFORE this run — lets the profile step recognise a machine whose own
|
|
29
|
+
# shell profile already exports everything (then no block is written).
|
|
30
|
+
SUNGEN_ENV_INITIAL_ANDROID_HOME=${ANDROID_HOME:-}
|
|
31
|
+
SUNGEN_ENV_INITIAL_JAVA_HOME=${JAVA_HOME:-}
|
|
32
|
+
SUNGEN_ENV_INITIAL_ADB=$(command -v adb 2>/dev/null || true)
|
|
33
|
+
|
|
34
|
+
COUNT_OK=0; COUNT_SKIP=0; COUNT_WARN=0; COUNT_FAIL=0
|
|
35
|
+
|
|
36
|
+
if [ -t 1 ]; then
|
|
37
|
+
C_OK=$'\033[32m'; C_WARN=$'\033[33m'; C_FAIL=$'\033[31m'; C_DIM=$'\033[2m'; C_BOLD=$'\033[1m'; C_RESET=$'\033[0m'
|
|
38
|
+
else
|
|
39
|
+
C_OK=''; C_WARN=''; C_FAIL=''; C_DIM=''; C_BOLD=''; C_RESET=''
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
have() { command -v "$1" >/dev/null 2>&1; }
|
|
43
|
+
# version_major "v22.17.1" → 22
|
|
44
|
+
version_major() { printf '%s' "$1" | sed -E 's/^[^0-9]*([0-9]+).*/\1/'; }
|
|
45
|
+
# java_major_of <java binary> → 21 (empty when not runnable). "1.8.0_x" → 1, so JDK 8 fails a ≥17 gate.
|
|
46
|
+
java_major_of() { "$1" -version 2>&1 | head -n1 | sed -E 's/^[^"]*"([0-9]+)(\.[0-9]+)*.*$/\1/' | grep -E '^[0-9]+$'; }
|
|
47
|
+
# first_line <multi-line string>
|
|
48
|
+
first_line() { printf '%s' "${1%%$'\n'*}"; }
|
|
49
|
+
|
|
50
|
+
section() { printf '\n%s%s%s\n' "$C_BOLD" "$1" "$C_RESET"; }
|
|
51
|
+
_detail() { [ -n "$1" ] && printf ' %s%s%s\n' "$C_DIM" "$1" "$C_RESET"; return 0; }
|
|
52
|
+
_detail_lines() { printf '%s\n' "$1" | while IFS= read -r line; do [ -n "$line" ] && _detail "$line"; done; return 0; }
|
|
53
|
+
|
|
54
|
+
log_ok() { COUNT_OK=$((COUNT_OK+1)); printf '%s✓ ok%s %s\n' "$C_OK" "$C_RESET" "$1"; _detail_lines "${2:-}"; }
|
|
55
|
+
log_skip() { COUNT_SKIP=$((COUNT_SKIP+1)); printf '%s– skip%s %s\n' "$C_DIM" "$C_RESET" "$1"; _detail_lines "${2:-}"; }
|
|
56
|
+
log_warn() { COUNT_WARN=$((COUNT_WARN+1)); printf '%s⚠ warn%s %s\n' "$C_WARN" "$C_RESET" "$1"; _detail_lines "${2:-}"; }
|
|
57
|
+
log_fail() { COUNT_FAIL=$((COUNT_FAIL+1)); printf '%s✗ fail%s %s\n' "$C_FAIL" "$C_RESET" "$1"; _detail_lines "${2:-}"; }
|
|
58
|
+
log_install() { printf '📦 install %s\n' "$1"; }
|
|
59
|
+
|
|
60
|
+
# run_step <title> <check_fn> <install_fn> <verify_fn> — an install-type step.
|
|
61
|
+
run_step() {
|
|
62
|
+
local title=$1 check_fn=$2 install_fn=$3 verify_fn=$4 found out rc
|
|
63
|
+
if found=$("$check_fn" 2>/dev/null); then log_skip "$title" "${found:-already installed}"; return 0; fi
|
|
64
|
+
if [ "$SUNGEN_ENV_CHECK_ONLY" = 1 ]; then log_fail "$title" "not installed — run without --check to install"; return 0; fi
|
|
65
|
+
log_install "$title"
|
|
66
|
+
"$install_fn"; rc=$?
|
|
67
|
+
if out=$("$verify_fn" 2>&1); then log_ok "$title" "$out"; return 0; fi
|
|
68
|
+
if [ "$rc" -eq 2 ]; then log_warn "$title" "${out:-needs a manual step — re-run setup.sh afterwards}"
|
|
69
|
+
else log_fail "$title" "${out:-install did not complete}"; fi
|
|
70
|
+
return 0
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# run_check <title> <check_fn> <hint> [warn] — a verification-only step (ok / fail, or warn for informational checks).
|
|
74
|
+
run_check() {
|
|
75
|
+
local title=$1 check_fn=$2 hint=$3 level=${4:-fail} out msg
|
|
76
|
+
if out=$("$check_fn" 2>&1); then log_ok "$title" "$out"; return 0; fi
|
|
77
|
+
msg=$hint; [ -n "$out" ] && msg="$out"$'\n'"$hint"
|
|
78
|
+
if [ "$level" = warn ]; then log_warn "$title" "$msg"; else log_fail "$title" "$msg"; fi
|
|
79
|
+
return 0
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
# run_guided <title> <check_fn> <guidance> — GUI / App Store installs this bundle never performs itself.
|
|
83
|
+
run_guided() {
|
|
84
|
+
local title=$1 check_fn=$2 guidance=$3 found
|
|
85
|
+
if found=$("$check_fn" 2>/dev/null); then log_skip "$title" "${found:-already installed}"; return 0; fi
|
|
86
|
+
log_warn "$title" "$guidance"
|
|
87
|
+
return 0
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# ── shell profile block ────────────────────────────────────────────────────────────────────────
|
|
91
|
+
PROFILE_BEGIN='# >>> sungen mobile env >>>'
|
|
92
|
+
PROFILE_END='# <<< sungen mobile env <<<'
|
|
93
|
+
# zsh → ~/.zshrc, bash → ~/.bash_profile, anything else → ~/.profile
|
|
94
|
+
profile_file() {
|
|
95
|
+
case "${SHELL:-}" in
|
|
96
|
+
*zsh) printf '%s' "$HOME/.zshrc" ;;
|
|
97
|
+
*bash) printf '%s' "$HOME/.bash_profile" ;;
|
|
98
|
+
*) printf '%s' "$HOME/.profile" ;;
|
|
99
|
+
esac
|
|
100
|
+
}
|
|
101
|
+
profile_block_content() {
|
|
102
|
+
local file; file=$(profile_file); [ -f "$file" ] || return 0
|
|
103
|
+
awk -v b="$PROFILE_BEGIN" -v e="$PROFILE_END" '$0==b{f=1;next} $0==e{f=0} f' "$file"
|
|
104
|
+
}
|
|
105
|
+
# write_profile_block <content> — append the marked block, replace it when different, keep it when identical.
|
|
106
|
+
# Returns 0 = unchanged, 3 = written. Sets PROFILE_MSG. Must NOT be called in a subshell (it sets globals).
|
|
107
|
+
write_profile_block() {
|
|
108
|
+
local content=$1 file tmp
|
|
109
|
+
file=$(profile_file); [ -f "$file" ] || : > "$file"
|
|
110
|
+
if [ "$(profile_block_content)" = "$content" ]; then PROFILE_MSG="block already up to date in $file"; return 0; fi
|
|
111
|
+
tmp=$(mktemp)
|
|
112
|
+
awk -v b="$PROFILE_BEGIN" -v e="$PROFILE_END" '$0==b{f=1;next} $0==e{f=0;next} !f' "$file" > "$tmp"
|
|
113
|
+
[ -s "$tmp" ] && [ -n "$(tail -c1 "$tmp")" ] && printf '\n' >> "$tmp"
|
|
114
|
+
{ cat "$tmp"; printf '%s\n%s\n%s\n' "$PROFILE_BEGIN" "$content" "$PROFILE_END"; } > "$file"
|
|
115
|
+
rm -f "$tmp"
|
|
116
|
+
SUNGEN_ENV_PROFILE_CHANGED=1
|
|
117
|
+
PROFILE_MSG="written to $file — open a new terminal to apply"
|
|
118
|
+
return 3
|
|
119
|
+
}
|
|
120
|
+
# profile_step <title> <content> — the profile block as a step (honours --no-profile and --check).
|
|
121
|
+
profile_step() {
|
|
122
|
+
local title=$1 content=$2
|
|
123
|
+
if [ "$SUNGEN_ENV_NO_PROFILE" = 1 ]; then log_skip "$title" "--no-profile"; return 0; fi
|
|
124
|
+
if [ "$SUNGEN_ENV_CHECK_ONLY" = 1 ]; then
|
|
125
|
+
if [ "$(profile_block_content)" = "$content" ]; then log_skip "$title" "block already up to date in $(profile_file)"
|
|
126
|
+
else log_fail "$title" "block missing or outdated in $(profile_file) — run without --check to write it"; fi
|
|
127
|
+
return 0
|
|
128
|
+
fi
|
|
129
|
+
write_profile_block "$content"
|
|
130
|
+
case $? in 3) log_ok "$title" "$PROFILE_MSG" ;; *) log_skip "$title" "$PROFILE_MSG" ;; esac
|
|
131
|
+
return 0
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
# print_summary — tallies + the "new terminal" notice. Exit 1 when anything failed.
|
|
135
|
+
print_summary() {
|
|
136
|
+
printf '\n%s%d ok · %d skipped · %d warned · %d failed%s\n' "$C_BOLD" "$COUNT_OK" "$COUNT_SKIP" "$COUNT_WARN" "$COUNT_FAIL" "$C_RESET"
|
|
137
|
+
[ "$SUNGEN_ENV_PROFILE_CHANGED" = 1 ] && printf '%s→ Open a NEW terminal so PATH / JAVA_HOME / ANDROID_HOME apply.%s\n' "$C_WARN" "$C_RESET"
|
|
138
|
+
[ "$COUNT_WARN" -gt 0 ] && printf ' warnings need a manual step — do it, then re-run this script.\n'
|
|
139
|
+
[ "$COUNT_FAIL" -gt 0 ] && return 1
|
|
140
|
+
return 0
|
|
141
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# verify-appium.ps1 - the final gate: `appium driver doctor uiautomator2` checks the whole chain the
|
|
2
|
+
# driver needs at run time (JAVA_HOME, ANDROID_HOME, adb). Doctor problems are reported as warnings
|
|
3
|
+
# with the doctor's own output - it names the missing piece.
|
|
4
|
+
if (-not $script:SungenEnvLibLoaded) { . (Join-Path $PSScriptRoot 'lib.ps1') }
|
|
5
|
+
|
|
6
|
+
$AppiumCmd = $script:SungenEnvAppiumCmd
|
|
7
|
+
# Doctor prints a required failure as a heavy-ballot-x line; the character is referenced by code point
|
|
8
|
+
# so this file stays ASCII (Windows PowerShell 5.1 reads BOM-less scripts as ANSI).
|
|
9
|
+
$RequiredFailMark = [string][char]0x2716
|
|
10
|
+
|
|
11
|
+
Invoke-Check -Title 'Appium CLI' -Check {
|
|
12
|
+
if (Test-Path $AppiumCmd) { "appium $((& $AppiumCmd -v) 2>$null | Select-Object -First 1)" }
|
|
13
|
+
} -Hint 'not installed in this project - sungen capability add mobile'
|
|
14
|
+
|
|
15
|
+
if ((Test-Path $AppiumCmd) -and -not $script:SungenEnvSkipAndroid) {
|
|
16
|
+
$installed = (& $AppiumCmd driver list --installed --json) 2>$null | Out-String
|
|
17
|
+
if ($installed -match '"uiautomator2"') {
|
|
18
|
+
# Inline (not Invoke-Check) so the failure detail prints AFTER the step title, like every other step.
|
|
19
|
+
$title = 'appium driver doctor uiautomator2'
|
|
20
|
+
$hint = 'fix the required items above (open a NEW terminal first if the environment was just written), then re-run'
|
|
21
|
+
$out = (& $AppiumCmd driver doctor uiautomator2 2>&1) | Out-String
|
|
22
|
+
$rc = $LASTEXITCODE
|
|
23
|
+
# Doctor marks BOTH required and optional problems with the same mark, so read its summary line:
|
|
24
|
+
# "### Diagnostic completed, N required fixes needed, M optional fixes possible. ###"
|
|
25
|
+
$req = $null; if ($out -match 'Diagnostic completed, (\d+) required fix') { $req = [int]$Matches[1] }
|
|
26
|
+
$opt = 0; if ($out -match ', (\d+) optional fix') { $opt = [int]$Matches[1] }
|
|
27
|
+
if ($null -eq $req) {
|
|
28
|
+
# No summary line (older driver) - fall back to the exit code.
|
|
29
|
+
if ($rc -eq 0) { Write-Ok $title 'doctor exited 0' } else { Write-Warn $title "$out`n$hint" }
|
|
30
|
+
} elseif ($req -gt 0) {
|
|
31
|
+
$marks = @(($out -split "`r?`n") | Where-Object { $_.Contains($RequiredFailMark) } | ForEach-Object { $_ -replace '^.*Doctor\s+', '' })
|
|
32
|
+
Write-Warn $title ("$req required fix(es) needed:`n" + ($marks -join "`n") + "`nfull report: npx appium driver doctor uiautomator2`n$hint")
|
|
33
|
+
} else {
|
|
34
|
+
Write-Ok $title "0 required fixes, $opt optional (extras like bundletool / gstreamer - see: npx appium driver doctor uiautomator2)"
|
|
35
|
+
}
|
|
36
|
+
} else {
|
|
37
|
+
Write-Warn 'appium driver doctor uiautomator2' 'driver not installed - see the Appium step above'
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# verify-appium.sh — the final gate: `appium driver doctor <driver>` checks the whole chain the
|
|
3
|
+
# driver needs at run time (JAVA_HOME, ANDROID_HOME, adb; Xcode + simctl for xcuitest).
|
|
4
|
+
# Doctor problems are reported as warnings with the doctor's own output — it names the missing piece.
|
|
5
|
+
[ -n "${SUNGEN_ENV_LIB_LOADED:-}" ] || . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib.sh"
|
|
6
|
+
|
|
7
|
+
APPIUM_BIN=$SUNGEN_ENV_APPIUM_BIN
|
|
8
|
+
|
|
9
|
+
appium_cli_check() { [ -x "$APPIUM_BIN" ] || return 1; printf 'appium %s' "$("$APPIUM_BIN" -v 2>/dev/null)"; }
|
|
10
|
+
run_check "Appium CLI" appium_cli_check "not installed in this project — sungen capability add mobile"
|
|
11
|
+
|
|
12
|
+
doctor_check() {
|
|
13
|
+
local out rc req opt
|
|
14
|
+
out=$("$APPIUM_BIN" driver doctor "$CURRENT_DRIVER" 2>&1); rc=$?
|
|
15
|
+
# Doctor marks BOTH required and optional problems with "✖", so read its own summary line instead:
|
|
16
|
+
# "### Diagnostic completed, N required fixes needed, M optional fixes possible. ###"
|
|
17
|
+
req=$(printf '%s\n' "$out" | sed -n -E 's/.*Diagnostic completed, ([0-9]+) required fix.*/\1/p' | tail -n1)
|
|
18
|
+
opt=$(printf '%s\n' "$out" | sed -n -E 's/.*, ([0-9]+) optional fix.*/\1/p' | tail -n1)
|
|
19
|
+
if [ -z "$req" ]; then
|
|
20
|
+
# No summary line (older driver) — fall back to the exit code.
|
|
21
|
+
[ "$rc" -eq 0 ] && { printf 'doctor exited 0'; return 0; }
|
|
22
|
+
printf '%s' "$out"; return 1
|
|
23
|
+
fi
|
|
24
|
+
if [ "$req" -gt 0 ]; then
|
|
25
|
+
printf '%s required fix(es) needed:\n%s\nfull report: npx appium driver doctor %s' "$req" \
|
|
26
|
+
"$(printf '%s\n' "$out" | grep '✖' | sed -E 's/^.*Doctor +//')" "$CURRENT_DRIVER"
|
|
27
|
+
return 1
|
|
28
|
+
fi
|
|
29
|
+
printf '0 required fixes · %s optional (extras like bundletool / gstreamer — see: npx appium driver doctor %s)' "${opt:-0}" "$CURRENT_DRIVER"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if [ -x "$APPIUM_BIN" ]; then
|
|
33
|
+
DOCTOR_DRIVERS=""
|
|
34
|
+
[ "$SUNGEN_ENV_SKIP_ANDROID" != 1 ] && DOCTOR_DRIVERS="uiautomator2"
|
|
35
|
+
if [ "$(uname -s)" = Darwin ] && [ "$SUNGEN_ENV_SKIP_IOS" != 1 ]; then DOCTOR_DRIVERS="$DOCTOR_DRIVERS xcuitest"; fi
|
|
36
|
+
for CURRENT_DRIVER in $DOCTOR_DRIVERS; do
|
|
37
|
+
if printf '%s' "$("$APPIUM_BIN" driver list --installed --json 2>/dev/null)" | grep -q "\"$CURRENT_DRIVER\""; then
|
|
38
|
+
run_check "appium driver doctor $CURRENT_DRIVER" doctor_check "fix the required items above (open a NEW terminal first if the profile block was just written), then re-run" warn
|
|
39
|
+
else
|
|
40
|
+
log_warn "appium driver doctor $CURRENT_DRIVER" "driver not installed — see the Appium step above"
|
|
41
|
+
fi
|
|
42
|
+
done
|
|
43
|
+
fi
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# verify-node.ps1 - Node.js >= 18 (sungen's own engine floor). Never installs Node: sungen is already
|
|
2
|
+
# running on it when this bundle is invoked from `sungen init`; a standalone run gets the guide.
|
|
3
|
+
if (-not $script:SungenEnvLibLoaded) { . (Join-Path $PSScriptRoot 'lib.ps1') }
|
|
4
|
+
|
|
5
|
+
$NodeMin = 18
|
|
6
|
+
Invoke-Check -Title "Node.js >= $NodeMin" -Check {
|
|
7
|
+
if (-not (Get-Command node -ErrorAction SilentlyContinue)) { return $null }
|
|
8
|
+
$v = (& node -v) 2>$null
|
|
9
|
+
$major = 0; if ("$v" -match '(\d+)') { $major = [int]$Matches[1] }
|
|
10
|
+
if ($major -lt $NodeMin) { return $null }
|
|
11
|
+
$npm = (& npm -v) 2>$null
|
|
12
|
+
"node $v, npm $npm"
|
|
13
|
+
} -Hint 'install Node LTS: https://nodejs.org (or: winget install OpenJS.NodeJS.LTS), then open a new terminal'
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# verify-node.sh — Node.js ≥ 18 (sungen's own engine floor). Never installs Node: sungen is already
|
|
3
|
+
# running on it when this bundle is invoked from `sungen init`; a standalone run gets the guide.
|
|
4
|
+
[ -n "${SUNGEN_ENV_LIB_LOADED:-}" ] || . "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib.sh"
|
|
5
|
+
|
|
6
|
+
NODE_MIN=18
|
|
7
|
+
|
|
8
|
+
node_check() {
|
|
9
|
+
local v major
|
|
10
|
+
have node || return 1
|
|
11
|
+
v=$(node -v 2>/dev/null) || return 1
|
|
12
|
+
major=$(version_major "$v")
|
|
13
|
+
[ "$major" -ge "$NODE_MIN" ] || { printf 'node %s is below %s' "$v" "$NODE_MIN"; return 1; }
|
|
14
|
+
printf 'node %s · npm %s' "$v" "$(npm -v 2>/dev/null || printf '?')"
|
|
15
|
+
}
|
|
16
|
+
run_check "Node.js ≥ $NODE_MIN" node_check "install Node LTS: https://nodejs.org (or: brew install node · nvm install --lts)"
|