@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
|
@@ -154,6 +154,7 @@ two content-filtered queries can hit different rows if the table re-renders in b
|
|
|
154
154
|
# dropdown | select | combobox → tag probed at runtime (toHaveValue native, toHaveText custom trigger)
|
|
155
155
|
# 5. State: User see [T] button is disabled | checkbox is checked | dialog with {{v}} is hidden
|
|
156
156
|
# 6. Attribute (toHaveAttribute): User see [T] image | link with {{v}}
|
|
157
|
+
# WHICH attribute comes from the entry's `attribute:` — any name, not just href/src
|
|
157
158
|
# 7. Count: User see [T] row with {{count}}
|
|
158
159
|
# 8. Page: User see [T] page | page with {{url}} ← `with` also asserts the query string
|
|
159
160
|
# NEVER a state: `page is hidden` / `page is empty` … are NOT step forms (see Pattern 8 below)
|
|
@@ -291,7 +292,22 @@ job table:
|
|
|
291
292
|
value: 'div.job-list'
|
|
292
293
|
rows: 'div.job-row' # what EVERY row matches — NOT a row label's entry
|
|
293
294
|
```
|
|
294
|
-
Two different keys, two meanings: `rows:` on the table = every row (the shape); a `type: locator` entry on a **row label** = ONE positioned row (`tbody tr:first-child`). A shape put on a row label fails at run time with a `toHaveCount(1)` message that points back here. A
|
|
295
|
+
Two different keys, two meanings: `rows:` on the table = every row (the shape); a `type: locator` entry on a **row label** = ONE positioned row (`tbody tr:first-child`). A shape put on a row label fails at run time with a `toHaveCount(1)` message that points back here. A table located by CSS — or by a role that holds no rows, such as `role: list` or `role: region` — with no `rows:` gets `SG-W016`. On a shaped table `table row {{n}}` counts from the first data row (no header offset).
|
|
296
|
+
|
|
297
|
+
**A shaped row has no CELLS either, so the two cell-reading steps need one more declaration.** `see [Col] column with {{v}}` reaches the cell through `getByRole('cell')`, and `table match data:` reads its header row from `thead tr` and addresses cells as `td, th` — a `<div>` row has none of those. Name the cell on the column:
|
|
298
|
+
```yaml
|
|
299
|
+
job table:
|
|
300
|
+
type: locator
|
|
301
|
+
value: 'div.job-list'
|
|
302
|
+
rows: 'div.job-row'
|
|
303
|
+
columns:
|
|
304
|
+
title:
|
|
305
|
+
index: 0
|
|
306
|
+
header: 'Title'
|
|
307
|
+
elements:
|
|
308
|
+
cell: { type: locator, value: '.job-title' } # relative to ONE row
|
|
309
|
+
```
|
|
310
|
+
With it, `see [Title] column with {{v}}` compiles to `tableRow.locator('.job-title')` and `table match data:` stops reading `thead` altogether — the header-to-column mapping is then known at compile time, so there is nothing left to race with. Without it on a shaped table, `see [Col] column` warns (`SG-W034`) and `table match data:` is refused outright (`SG-E035`) rather than waiting out every timeout against a header row that will never exist.
|
|
295
311
|
|
|
296
312
|
### Browser storage `[web]`
|
|
297
313
|
|
|
@@ -556,6 +572,7 @@ award:
|
|
|
556
572
|
| `page` | relative URL | — | navigation |
|
|
557
573
|
| `upload` | — | — | file inputs |
|
|
558
574
|
| `frame` | iframe selector | — | iframes |
|
|
575
|
+
| `id` | `#the-id` (a bare `the-id` also works) | — | an element whose only stable handle is its DOM id |
|
|
559
576
|
|
|
560
577
|
Options: `nth` `exact` `scope` `match` `variant` `frame` `contenteditable` `columns` `checked` `selected`
|
|
561
578
|
|
|
@@ -569,6 +586,39 @@ when the state differs, then assert it; `is checked` / `is unchecked` / `is sele
|
|
|
569
586
|
the same entry (`is focused`, …) keep their native matchers. The value must be ONE plain class token or
|
|
570
587
|
attribute name — whitespace, a quote or a backslash is refused at compile time, naming the entry.
|
|
571
588
|
|
|
589
|
+
`attribute:` names the attribute an `image`/`link` assertion reads, and it takes ANY attribute name
|
|
590
|
+
straight from the YAML — nothing is hardcoded to `href`/`src`. To assert several attributes of ONE
|
|
591
|
+
element, declare several keys with the SAME locator and different `attribute:`; each key is then a
|
|
592
|
+
separate reference in the Gherkin:
|
|
593
|
+
```yaml
|
|
594
|
+
job link: # the link's TEXT
|
|
595
|
+
type: locator
|
|
596
|
+
value: 'a.job-link'
|
|
597
|
+
job link href: # the same element, its href
|
|
598
|
+
type: locator
|
|
599
|
+
value: 'a.job-link'
|
|
600
|
+
attribute: href
|
|
601
|
+
job link target:
|
|
602
|
+
type: locator
|
|
603
|
+
value: 'a.job-link'
|
|
604
|
+
attribute: target
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
**A `page` value that is an ABSOLUTE URL now anchors the HOST as well as the path.** `value:
|
|
608
|
+
'https://admin.example.com/jobs'` means "that path on that host": `see [X] page`, `is on [X] page`
|
|
609
|
+
and `should remain on` all fail on `https://admin-stg.example.com/jobs`. Before, only `wait for [X]
|
|
610
|
+
page` checked the host and the assertions matched the path on any host at all — so an assertion
|
|
611
|
+
naming one environment silently passed in another. If the page is the same in every environment,
|
|
612
|
+
declare it **relative** (`value: '/jobs'`) and let `baseURL` supply the host; keep the absolute form
|
|
613
|
+
for a destination that really is on another host.
|
|
614
|
+
|
|
615
|
+
`anchor:` is a `page` entry's rendered-content proof: `Then User see [X] page` asserts the URL AND
|
|
616
|
+
that the anchor element is present, so a route guard that leaves the URL alone cannot pass it. Three
|
|
617
|
+
things it does NOT do, each verified rather than assumed: it is read only by the `Then`-side page
|
|
618
|
+
assertion, so `Given User is on [X] page` ignores it; the `should see route` / `should remain on`
|
|
619
|
+
forms ignore it too — they compile through the route assertion, which is never handed an anchor; and
|
|
620
|
+
nothing warns when a page entry declares one and no step asserts that page.
|
|
621
|
+
|
|
572
622
|
`scope` (e.g. `scope: dialog`) is `[web]`-effective only — no Appium template reads `inDialog`, so
|
|
573
623
|
on mobile a dialog-scoped ref still resolves against the whole screen (`SG-W021`).
|
|
574
624
|
|
|
@@ -580,6 +630,7 @@ on mobile a dialog-scoped ref still resolves against the whole screen (`SG-W021`
|
|
|
580
630
|
|---|---|
|
|
581
631
|
| `@spec:<id>` | **Traceability**: this scenario proves requirement `<id>` from `requirements/spec.md` (`@spec:FR-003`). Repeatable — a scenario may prove several. Read by `sungen audit` (requirement coverage) and by delivery (the Coverage sheet's requirement table). Without it the link is only inferred from wording and cannot survive an edit. |
|
|
582
632
|
| `@manual` | Skip in generation |
|
|
633
|
+
| `@known-defect` | The scenario is EXPECTED to be red — it asserts a live defect. It still runs and still reports as failed (never skipped, never "fixed"), and it stays a pass-through tag so `--grep @known-defect` selects it. **`[mobile]`** Its failure does NOT escalate the next scenario's `@reset:` tier: a red that was designed in is not evidence of leftover state |
|
|
583
634
|
| `@auth:role` | Use auth storage state for role |
|
|
584
635
|
| `@no-auth` | Disable inherited auth |
|
|
585
636
|
| `@steps:name` | Define reusable step block (base scenario) |
|
|
@@ -645,6 +696,14 @@ Rules that matter when generating:
|
|
|
645
696
|
relaunch does not undo a half-written local DB. One rung, one scenario, and never from `none`.
|
|
646
697
|
A **retry** of the failed scenario is NOT escalated — it re-runs at its declared tier, so a
|
|
647
698
|
state-dependent failure fails both attempts and is reported honestly instead of passing on attempt 2.
|
|
699
|
+
A scenario tagged **`@known-defect`** escalates nothing either: its red was designed in, so making
|
|
700
|
+
the next scenario pay a harder reset for it buys nothing, on every run, forever.
|
|
701
|
+
- **Escalation never picks a tier this session cannot perform.** Reinstalling needs the app's build
|
|
702
|
+
file, so when the app is attached by package the ladder caps at `data` (Android) / `soft` (iOS) and
|
|
703
|
+
says it capped, instead of throwing. Nothing asked for the escalated tier, and the throw would land
|
|
704
|
+
in `beforeEach` — a hook failure, which skips every remaining scenario in the FILE. The cap never
|
|
705
|
+
goes BELOW the declared tier: a tier you asked for that the session cannot do still throws by name,
|
|
706
|
+
because that one is an unmet contract rather than a recovery the runner chose.
|
|
648
707
|
- **`SUNGEN_RESET_TIER=data` raises the floor for a whole run** without regenerating specs — CI can run
|
|
649
708
|
stricter than local dev. It never lowers a declared tier.
|
|
650
709
|
- **`data`/`full` on iOS reinstall the app**, because `mobile: clearApp` is Android-only (UiAutomator2).
|
|
@@ -724,7 +783,9 @@ npx playwright test --grep "@smoke|@high" # smoke OR high
|
|
|
724
783
|
| Serial (default) | `test.describe.serial()` | Shared | `beforeAll` (1 goto) | Skip remaining |
|
|
725
784
|
| `@parallel` | `test.describe()` | Fresh per test | `beforeEach` (N goto) | Continue |
|
|
726
785
|
|
|
727
|
-
|
|
786
|
+
**Multiple auth groups need the default isolation.** A feature mixing `@auth:user` with `@no-auth` compiles as-is — features are parallel by default, and `@parallel` only names that default. What breaks it is `@serial`: one shared browser context cannot carry two different sessions, so the compiler errors and asks you to drop `@serial` or split the feature.
|
|
787
|
+
|
|
788
|
+
`@no-auth` is a demand for an **empty** session, not merely the absence of `@auth:`. Its scenarios compile into their own `test.describe('no-auth')` with an inline empty `storageState`, so they stay signed out even when `playwright.config.ts` sets a global one. An untagged scenario is different again: it inherits whatever that config says.
|
|
728
789
|
|
|
729
790
|
### `@flow` tag (E2E cross-screen testing)
|
|
730
791
|
|
|
@@ -806,6 +867,7 @@ dashboard:
|
|
|
806
867
|
| Assertion clause the pattern drops | `Then User see [Code] text matches "<format>"` compiling to a bare `toBeVisible()` | Use the exact supported phrasing for the clause (see the `matches` / `contains` / `has text` forms above). A clause that gets swallowed leaves an assertion that passes whatever the element says, so the step is rejected with `SG-E023` instead. If the step still compiles bare, you are on an older global sungen — run `npx sungen` |
|
|
807
868
|
| Mobile lifecycle step with no app id | `When User restart the app` in a feature whose `Path:` carries only a route | Declare the app id on the Feature: `Path: com.example.app/.MainActivity \| com.example.app`. The lifecycle steps resolve the app from `Path`; without it the whole spec would die at runtime, so the step is rejected with `SG-E024`. For a mobile unit `--path` carries the APP ID, `--reach` carries the navigation recipe |
|
|
808
869
|
| Missing target type | `fill [email] with {{v}}` | `fill [email] field with {{v}}` |
|
|
870
|
+
| Asserting a cell with the row filter | `Then User see [Status] text in [Job Table] table with {{v}}` | The single `with` is read as the EXPECTED VALUE, not as a row filter, so the assertion runs against the WHOLE table and can pass on a row the sentence never named. Use the two-`with` form — `Then User see [Status] text with <expected> in [Job Row] row with {{v}}` — pointing the second `[…]` at a ROW entry, not at the table entry (a filter applied to the table narrows nothing). Warned with `SG-W032`. A `fill` is NOT flagged: a write needs a value, so its `with {{v}}` is that value and the step is an ordinary container-scoped write |
|
|
809
871
|
| Background with scope | `Background: ... And User is on [X] dialog` | Use `@steps` + `@extend` for scope-dependent flows |
|
|
810
872
|
| `is on` after When | `When ... And User is on [X] dialog` | `And User see [X] dialog` or separate Given |
|
|
811
873
|
| Literal URL navigate | `User navigate to "/dashboard"` | `User is on [Dashboard] page` (add page selector in `selectors.yaml`) |
|
|
@@ -128,7 +128,10 @@ back to the feature's own path (or `/<ref>/`) and warns `SG-W013`: a test that r
|
|
|
128
128
|
|
|
129
129
|
If the label is used by only ONE type, the base key (no suffix) is fine — suffix only on collision.
|
|
130
130
|
|
|
131
|
-
Type aliases (normalized automatically)
|
|
131
|
+
Type aliases (normalized automatically). The suffix and the step may use **different spellings of
|
|
132
|
+
the same type** — `confirm--modal` resolves for `[Confirm] dialog` and `confirm--dialog` resolves
|
|
133
|
+
for `[Confirm] modal`. Prefer the normalized suffix for consistency; either is found.
|
|
134
|
+
|
|
132
135
|
| Gherkin type | Normalized suffix |
|
|
133
136
|
|---|---|
|
|
134
137
|
| title, label, caption, message | `text` |
|
|
@@ -143,6 +146,15 @@ Type aliases (normalized automatically):
|
|
|
143
146
|
| column | `columnheader` |
|
|
144
147
|
| list-item | `listitem` |
|
|
145
148
|
|
|
149
|
+
When the step names a type from this table, only a key whose suffix is a spelling of that type is
|
|
150
|
+
used — `[Confirm] dialog` will not fall back to an unrelated `confirm--popup` entry. It reports the
|
|
151
|
+
keys it looked for, so the fix is to add one of them.
|
|
152
|
+
|
|
153
|
+
A bare `[Confirm]` keeps the older, looser fallback and matches any `confirm--*` entry, because with
|
|
154
|
+
no type there is nothing to match a suffix against. So does a `[Ref]` followed by a word that is not
|
|
155
|
+
an element type at all — `Then User see [Quick Panel] is hidden` reads `is` as that word. This is
|
|
156
|
+
what keeps state-clause steps working on a label whose keys are all suffixed.
|
|
157
|
+
|
|
146
158
|
## Nth-Suffixed Keys
|
|
147
159
|
|
|
148
160
|
When targeting a specific occurrence by index, add `--N` suffix:
|
|
@@ -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
|
+
}
|