@sun-asterisk/sungen 3.2.30 → 3.2.32

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.
Files changed (177) hide show
  1. package/dist/cli/commands/audit.d.ts.map +1 -1
  2. package/dist/cli/commands/audit.js +3 -1
  3. package/dist/cli/commands/audit.js.map +1 -1
  4. package/dist/cli/commands/capability.d.ts.map +1 -1
  5. package/dist/cli/commands/capability.js +11 -0
  6. package/dist/cli/commands/capability.js.map +1 -1
  7. package/dist/cli/commands/init.d.ts.map +1 -1
  8. package/dist/cli/commands/init.js +2 -0
  9. package/dist/cli/commands/init.js.map +1 -1
  10. package/dist/cli/commands/setup-mobile-env.d.ts +15 -0
  11. package/dist/cli/commands/setup-mobile-env.d.ts.map +1 -0
  12. package/dist/cli/commands/setup-mobile-env.js +65 -0
  13. package/dist/cli/commands/setup-mobile-env.js.map +1 -0
  14. package/dist/cli/index.js +2 -0
  15. package/dist/cli/index.js.map +1 -1
  16. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/all-contain-assertion.hbs +1 -2
  17. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/all-contain-element.hbs +8 -5
  18. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/count-assertion.hbs +5 -1
  19. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/disabled-with-filter-assertion.hbs +1 -1
  20. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +1 -1
  21. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/hidden-with-filter-assertion.hbs +1 -1
  22. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +1 -1
  23. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/list-item-count-assertion.hbs +5 -2
  24. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/state-with-filter-assertion.hbs +1 -1
  25. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/visible-filtered-assertion.hbs +1 -1
  26. package/dist/generators/test-generator/adapters/appium/templates/steps/partials/await-elements.hbs +12 -0
  27. package/dist/generators/test-generator/adapters/appium/templates/test-file.hbs +26 -0
  28. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/capture-variable.hbs +7 -1
  29. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/row-scoped-column-assertion.hbs +23 -1
  30. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +4 -4
  31. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/default.hbs +1 -1
  32. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/id.hbs +1 -1
  33. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +4 -4
  34. package/dist/generators/test-generator/adapters/playwright/templates/steps/partials/url-assertion.hbs +13 -2
  35. package/dist/generators/test-generator/adapters/playwright/templates/test-file.hbs +14 -0
  36. package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
  37. package/dist/generators/test-generator/code-generator.js +56 -13
  38. package/dist/generators/test-generator/code-generator.js.map +1 -1
  39. package/dist/generators/test-generator/diagnostics.d.ts +39 -2
  40. package/dist/generators/test-generator/diagnostics.d.ts.map +1 -1
  41. package/dist/generators/test-generator/diagnostics.js +38 -1
  42. package/dist/generators/test-generator/diagnostics.js.map +1 -1
  43. package/dist/generators/test-generator/patterns/index.d.ts +1 -1
  44. package/dist/generators/test-generator/patterns/index.js +1 -1
  45. package/dist/generators/test-generator/step-mapper.d.ts +35 -0
  46. package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
  47. package/dist/generators/test-generator/step-mapper.js +103 -4
  48. package/dist/generators/test-generator/step-mapper.js.map +1 -1
  49. package/dist/generators/test-generator/template-engine.d.ts.map +1 -1
  50. package/dist/generators/test-generator/template-engine.js +18 -0
  51. package/dist/generators/test-generator/template-engine.js.map +1 -1
  52. package/dist/generators/test-generator/utils/selector-resolver.d.ts +47 -0
  53. package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
  54. package/dist/generators/test-generator/utils/selector-resolver.js +17 -3
  55. package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
  56. package/dist/harness/audit.d.ts.map +1 -1
  57. package/dist/harness/audit.js +23 -4
  58. package/dist/harness/audit.js.map +1 -1
  59. package/dist/harness/next-step.d.ts.map +1 -1
  60. package/dist/harness/next-step.js +2 -1
  61. package/dist/harness/next-step.js.map +1 -1
  62. package/dist/harness/parse.d.ts.map +1 -1
  63. package/dist/harness/parse.js +42 -7
  64. package/dist/harness/parse.js.map +1 -1
  65. package/dist/harness/spec-branches.d.ts +7 -2
  66. package/dist/harness/spec-branches.d.ts.map +1 -1
  67. package/dist/harness/spec-branches.js +62 -11
  68. package/dist/harness/spec-branches.js.map +1 -1
  69. package/dist/harness/spec-coverage.d.ts +16 -0
  70. package/dist/harness/spec-coverage.d.ts.map +1 -1
  71. package/dist/harness/spec-coverage.js +119 -10
  72. package/dist/harness/spec-coverage.js.map +1 -1
  73. package/dist/orchestrator/appium-environment-setup.d.ts +24 -0
  74. package/dist/orchestrator/appium-environment-setup.d.ts.map +1 -0
  75. package/dist/orchestrator/appium-environment-setup.js +157 -0
  76. package/dist/orchestrator/appium-environment-setup.js.map +1 -0
  77. package/dist/orchestrator/mobile-runtime-scaffolder.d.ts +2 -0
  78. package/dist/orchestrator/mobile-runtime-scaffolder.d.ts.map +1 -1
  79. package/dist/orchestrator/mobile-runtime-scaffolder.js.map +1 -1
  80. package/dist/orchestrator/project-initializer.d.ts +1 -0
  81. package/dist/orchestrator/project-initializer.d.ts.map +1 -1
  82. package/dist/orchestrator/project-initializer.js +14 -0
  83. package/dist/orchestrator/project-initializer.js.map +1 -1
  84. package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +5 -1
  85. package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +55 -2
  86. package/dist/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +20 -5
  87. package/dist/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +3 -3
  88. package/dist/orchestrator/templates/appium-environment/README.md +83 -0
  89. package/dist/orchestrator/templates/appium-environment/scripts/common/install-appium.ps1 +44 -0
  90. package/dist/orchestrator/templates/appium-environment/scripts/common/install-appium.sh +50 -0
  91. package/dist/orchestrator/templates/appium-environment/scripts/common/lib.ps1 +108 -0
  92. package/dist/orchestrator/templates/appium-environment/scripts/common/lib.sh +141 -0
  93. package/dist/orchestrator/templates/appium-environment/scripts/common/verify-appium.ps1 +39 -0
  94. package/dist/orchestrator/templates/appium-environment/scripts/common/verify-appium.sh +43 -0
  95. package/dist/orchestrator/templates/appium-environment/scripts/common/verify-node.ps1 +13 -0
  96. package/dist/orchestrator/templates/appium-environment/scripts/common/verify-node.sh +16 -0
  97. package/dist/orchestrator/templates/appium-environment/scripts/macos/install-android.sh +110 -0
  98. package/dist/orchestrator/templates/appium-environment/scripts/macos/install-ios.sh +32 -0
  99. package/dist/orchestrator/templates/appium-environment/scripts/macos/verify-android.sh +32 -0
  100. package/dist/orchestrator/templates/appium-environment/scripts/macos/verify-ios.sh +15 -0
  101. package/dist/orchestrator/templates/appium-environment/scripts/windows/install-android.ps1 +153 -0
  102. package/dist/orchestrator/templates/appium-environment/scripts/windows/verify-android.ps1 +26 -0
  103. package/dist/orchestrator/templates/appium-environment/setup.ps1 +58 -0
  104. package/dist/orchestrator/templates/appium-environment/setup.sh +88 -0
  105. package/dist/orchestrator/templates/specs-checked-state.d.ts +1 -1
  106. package/dist/orchestrator/templates/specs-checked-state.d.ts.map +1 -1
  107. package/dist/orchestrator/templates/specs-checked-state.js +26 -3
  108. package/dist/orchestrator/templates/specs-checked-state.js.map +1 -1
  109. package/dist/orchestrator/templates/specs-checked-state.ts +31 -6
  110. package/dist/orchestrator/templates/specs-mock.d.ts +2 -1
  111. package/dist/orchestrator/templates/specs-mock.d.ts.map +1 -1
  112. package/dist/orchestrator/templates/specs-mock.js +10 -3
  113. package/dist/orchestrator/templates/specs-mock.js.map +1 -1
  114. package/dist/orchestrator/templates/specs-mock.ts +12 -4
  115. package/package.json +3 -3
  116. package/src/cli/commands/audit.ts +2 -1
  117. package/src/cli/commands/capability.ts +12 -1
  118. package/src/cli/commands/init.ts +3 -0
  119. package/src/cli/commands/setup-mobile-env.ts +64 -0
  120. package/src/cli/index.ts +2 -0
  121. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/all-contain-assertion.hbs +1 -2
  122. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/all-contain-element.hbs +8 -5
  123. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/count-assertion.hbs +5 -1
  124. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/disabled-with-filter-assertion.hbs +1 -1
  125. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/disabled-with-role-variable-assertion.hbs +1 -1
  126. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/hidden-with-filter-assertion.hbs +1 -1
  127. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/hidden-with-role-variable-assertion.hbs +1 -1
  128. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/list-item-count-assertion.hbs +5 -2
  129. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/state-with-filter-assertion.hbs +1 -1
  130. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/visible-filtered-assertion.hbs +1 -1
  131. package/src/generators/test-generator/adapters/appium/templates/steps/partials/await-elements.hbs +12 -0
  132. package/src/generators/test-generator/adapters/appium/templates/test-file.hbs +26 -0
  133. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/capture-variable.hbs +7 -1
  134. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/row-scoped-column-assertion.hbs +23 -1
  135. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-base.hbs +4 -4
  136. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/default.hbs +1 -1
  137. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator-strategies/id.hbs +1 -1
  138. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/locator.hbs +4 -4
  139. package/src/generators/test-generator/adapters/playwright/templates/steps/partials/url-assertion.hbs +13 -2
  140. package/src/generators/test-generator/adapters/playwright/templates/test-file.hbs +14 -0
  141. package/src/generators/test-generator/code-generator.ts +66 -17
  142. package/src/generators/test-generator/diagnostics.ts +39 -2
  143. package/src/generators/test-generator/patterns/index.ts +1 -1
  144. package/src/generators/test-generator/step-mapper.ts +106 -5
  145. package/src/generators/test-generator/template-engine.ts +18 -0
  146. package/src/generators/test-generator/utils/selector-resolver.ts +18 -3
  147. package/src/harness/audit.ts +24 -5
  148. package/src/harness/next-step.ts +2 -1
  149. package/src/harness/parse.ts +42 -7
  150. package/src/harness/spec-branches.ts +67 -12
  151. package/src/harness/spec-coverage.ts +121 -11
  152. package/src/orchestrator/appium-environment-setup.ts +127 -0
  153. package/src/orchestrator/mobile-runtime-scaffolder.ts +2 -0
  154. package/src/orchestrator/project-initializer.ts +15 -1
  155. package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +5 -1
  156. package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +55 -2
  157. package/src/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +20 -5
  158. package/src/orchestrator/templates/ai-src/skills/sungen-tc-review/SKILL.md +3 -3
  159. package/src/orchestrator/templates/appium-environment/README.md +83 -0
  160. package/src/orchestrator/templates/appium-environment/scripts/common/install-appium.ps1 +44 -0
  161. package/src/orchestrator/templates/appium-environment/scripts/common/install-appium.sh +50 -0
  162. package/src/orchestrator/templates/appium-environment/scripts/common/lib.ps1 +108 -0
  163. package/src/orchestrator/templates/appium-environment/scripts/common/lib.sh +141 -0
  164. package/src/orchestrator/templates/appium-environment/scripts/common/verify-appium.ps1 +39 -0
  165. package/src/orchestrator/templates/appium-environment/scripts/common/verify-appium.sh +43 -0
  166. package/src/orchestrator/templates/appium-environment/scripts/common/verify-node.ps1 +13 -0
  167. package/src/orchestrator/templates/appium-environment/scripts/common/verify-node.sh +16 -0
  168. package/src/orchestrator/templates/appium-environment/scripts/macos/install-android.sh +110 -0
  169. package/src/orchestrator/templates/appium-environment/scripts/macos/install-ios.sh +32 -0
  170. package/src/orchestrator/templates/appium-environment/scripts/macos/verify-android.sh +32 -0
  171. package/src/orchestrator/templates/appium-environment/scripts/macos/verify-ios.sh +15 -0
  172. package/src/orchestrator/templates/appium-environment/scripts/windows/install-android.ps1 +153 -0
  173. package/src/orchestrator/templates/appium-environment/scripts/windows/verify-android.ps1 +26 -0
  174. package/src/orchestrator/templates/appium-environment/setup.ps1 +58 -0
  175. package/src/orchestrator/templates/appium-environment/setup.sh +88 -0
  176. package/src/orchestrator/templates/specs-checked-state.ts +31 -6
  177. package/src/orchestrator/templates/specs-mock.ts +12 -4
@@ -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)"
@@ -0,0 +1,110 @@
1
+ #!/usr/bin/env bash
2
+ # install-android.sh — macOS: JDK ≥ 17, Android SDK root, platform-tools + emulator, shell profile block.
3
+ # Headless via Homebrew (decision D2): `openjdk@21` + the `android-commandlinetools` cask + sdkmanager.
4
+ # Android Studio is optional (only for an on-screen emulator) and is never installed here.
5
+ # Sourced by setup.sh (shares the runner + counters); runnable standalone for debugging.
6
+ [ -n "${SUNGEN_ENV_LIB_LOADED:-}" ] || . "$(cd "$(dirname "${BASH_SOURCE[0]}")/../common" && pwd)/lib.sh"
7
+
8
+ JDK_MIN=17
9
+ BREW_SDK_ROOT=$( { have brew && printf '%s/share/android-commandlinetools' "$(brew --prefix)"; } 2>/dev/null || true)
10
+
11
+ # ── JDK ────────────────────────────────────────────────────────────────────────────────────────
12
+ # Any working JDK ≥ 17 is accepted (Temurin, Corretto, Oracle, asdf…) — we never replace a good one.
13
+ # resolve_* run in THIS shell (not a subshell) so their exports survive; *_check run captured.
14
+ resolve_java_home() {
15
+ local major home
16
+ if [ -n "${JAVA_HOME:-}" ] && [ -x "$JAVA_HOME/bin/java" ]; then
17
+ major=$(java_major_of "$JAVA_HOME/bin/java"); [ -n "$major" ] && [ "$major" -ge "$JDK_MIN" ] && return 0
18
+ fi
19
+ if have java; then
20
+ major=$(java_major_of java)
21
+ if [ -n "$major" ] && [ "$major" -ge "$JDK_MIN" ]; then
22
+ home=$(java -XshowSettings:properties -version 2>&1 | awk -F' = ' '/java\.home/{print $2}')
23
+ [ -d "$home" ] && { export JAVA_HOME=$home; return 0; }
24
+ fi
25
+ fi
26
+ # Homebrew openjdk@21 present but not on PATH (brew keeps it keg-only).
27
+ if have brew && [ -x "$(brew --prefix openjdk@21 2>/dev/null)/bin/java" ]; then export JAVA_HOME; JAVA_HOME=$(brew --prefix openjdk@21); return 0; fi
28
+ return 1
29
+ }
30
+ jdk_check() {
31
+ local major
32
+ [ -n "${JAVA_HOME:-}" ] && [ -x "$JAVA_HOME/bin/java" ] || return 1
33
+ major=$(java_major_of "$JAVA_HOME/bin/java"); [ -n "$major" ] && [ "$major" -ge "$JDK_MIN" ] || return 1
34
+ printf 'java %s · JAVA_HOME=%s' "$major" "$JAVA_HOME"
35
+ }
36
+ jdk_install() { brew install openjdk@21 && { export JAVA_HOME; JAVA_HOME=$(brew --prefix openjdk@21); }; }
37
+ jdk_verify() { jdk_check || { printf 'no JDK ≥ %s — brew install openjdk@21 && export JAVA_HOME=$(brew --prefix openjdk@21)' "$JDK_MIN"; return 1; }; }
38
+ resolve_java_home || true
39
+ run_step "JDK ≥ $JDK_MIN" jdk_check jdk_install jdk_verify
40
+
41
+ # ── Android SDK root ───────────────────────────────────────────────────────────────────────────
42
+ # find_sdkmanager <sdk root> → path of sdkmanager (cmdline-tools/latest, any cmdline-tools/*, legacy tools/bin)
43
+ find_sdkmanager() {
44
+ local root=$1 c
45
+ [ -n "$root" ] || return 1
46
+ for c in "$root/cmdline-tools/latest/bin/sdkmanager" "$root"/cmdline-tools/*/bin/sdkmanager "$root/tools/bin/sdkmanager"; do
47
+ [ -x "$c" ] && { printf '%s' "$c"; return 0; }
48
+ done
49
+ return 1
50
+ }
51
+ sdk_root_usable() { [ -n "$1" ] && [ -d "$1" ] && { [ -x "$1/platform-tools/adb" ] || find_sdkmanager "$1" >/dev/null; }; }
52
+ # Precedence: what the user already exports → Android Studio's default → the Homebrew cask root.
53
+ resolve_android_home() {
54
+ local c
55
+ for c in "${ANDROID_HOME:-}" "${ANDROID_SDK_ROOT:-}" "$HOME/Library/Android/sdk" "$BREW_SDK_ROOT"; do
56
+ if sdk_root_usable "$c"; then export ANDROID_HOME=$c ANDROID_SDK_ROOT=$c; return 0; fi
57
+ done
58
+ return 1
59
+ }
60
+ sdk_root_check() { sdk_root_usable "${ANDROID_HOME:-}" || return 1; printf 'ANDROID_HOME=%s' "$ANDROID_HOME"; }
61
+ sdk_root_install() { brew install --cask android-commandlinetools && export ANDROID_HOME=$BREW_SDK_ROOT ANDROID_SDK_ROOT=$BREW_SDK_ROOT; }
62
+ sdk_root_verify() {
63
+ find_sdkmanager "${ANDROID_HOME:-}" >/dev/null || { printf 'sdkmanager not found under %s — brew install --cask android-commandlinetools' "${ANDROID_HOME:-<unset>}"; return 1; }
64
+ printf 'ANDROID_HOME=%s' "$ANDROID_HOME"
65
+ }
66
+ resolve_android_home || true
67
+ run_step "Android SDK root (cmdline-tools)" sdk_root_check sdk_root_install sdk_root_verify
68
+
69
+ # ── platform-tools (adb) + emulator ────────────────────────────────────────────────────────────
70
+ sdk_pkgs_check() {
71
+ local v
72
+ [ -x "${ANDROID_HOME:-}/platform-tools/adb" ] && [ -x "$ANDROID_HOME/emulator/emulator" ] || return 1
73
+ v=$("$ANDROID_HOME/platform-tools/adb" version 2>/dev/null) || v='adb'
74
+ printf 'platform-tools + emulator · %s' "$(first_line "$v")"
75
+ }
76
+ sdk_pkgs_install() {
77
+ local sm
78
+ sm=$(find_sdkmanager "${ANDROID_HOME:-}") || { printf 'no sdkmanager — the "Android SDK root" step must succeed first\n' >&2; return 1; }
79
+ # sdkmanager needs a JDK; --sdk_root is mandatory for the cask layout. Licenses are accepted
80
+ # non-interactively (they are Google's SDK terms — the same prompt Android Studio shows).
81
+ yes 2>/dev/null | JAVA_HOME=${JAVA_HOME:-} "$sm" --sdk_root="$ANDROID_HOME" --licenses >/dev/null 2>&1 || true
82
+ JAVA_HOME=${JAVA_HOME:-} "$sm" --sdk_root="$ANDROID_HOME" "platform-tools" "emulator"
83
+ }
84
+ sdk_pkgs_verify() { sdk_pkgs_check || { printf 'adb / emulator still missing — sdkmanager --sdk_root="%s" platform-tools emulator' "${ANDROID_HOME:-}"; return 1; }; }
85
+ run_step "Android platform-tools (adb) + emulator" sdk_pkgs_check sdk_pkgs_install sdk_pkgs_verify
86
+
87
+ # Make the tools reachable for the rest of THIS run (the profile block covers new terminals).
88
+ if [ -n "${ANDROID_HOME:-}" ]; then
89
+ export PATH="$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator:$ANDROID_HOME/cmdline-tools/latest/bin:$PATH"
90
+ fi
91
+
92
+ # ── shell profile block (decision D3) ─────────────────────────────────────────────────────────
93
+ PROFILE_TITLE="Shell profile (ANDROID_HOME, JAVA_HOME, PATH)"
94
+ if [ -z "${ANDROID_HOME:-}" ] || [ -z "${JAVA_HOME:-}" ]; then
95
+ log_warn "$PROFILE_TITLE" "skipped — fix the JDK / SDK steps above first, then re-run"
96
+ elif [ "$SUNGEN_ENV_INITIAL_ANDROID_HOME" = "$ANDROID_HOME" ] && [ "$SUNGEN_ENV_INITIAL_JAVA_HOME" = "$JAVA_HOME" ] \
97
+ && [ -n "$SUNGEN_ENV_INITIAL_ADB" ] && grep -qs 'ANDROID_HOME' "$(profile_file)"; then
98
+ # The user's own profile already exports the right values (and adb was on PATH before we touched it) —
99
+ # nothing to add. A value exported only in this terminal session does not count: the profile must name it.
100
+ log_skip "$PROFILE_TITLE" "already exported by your shell profile (ANDROID_HOME, JAVA_HOME, adb on PATH) — no block written"
101
+ else
102
+ profile_step "$PROFILE_TITLE" "$(printf 'export ANDROID_HOME="%s"\nexport ANDROID_SDK_ROOT="$ANDROID_HOME"\nexport JAVA_HOME="%s"\nexport PATH="$ANDROID_HOME/platform-tools:$ANDROID_HOME/emulator:$ANDROID_HOME/cmdline-tools/latest/bin:$PATH"' "$ANDROID_HOME" "$JAVA_HOME")"
103
+ fi
104
+
105
+ # ── Android Studio — optional, GUI, never installed here ───────────────────────────────────────
106
+ if [ -d "/Applications/Android Studio.app" ]; then
107
+ log_skip "Android Studio (optional)" "/Applications/Android Studio.app"
108
+ else
109
+ log_skip "Android Studio (optional)" "not installed — only needed for the AVD manager / an on-screen emulator: https://developer.android.com/studio"
110
+ fi
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env bash
2
+ # install-ios.sh — macOS: Xcode Command Line Tools, Xcode.app (checked + guided), license, applesimutils.
3
+ # Xcode.app comes from the App Store and is never installed here (decision D2); xcuitest needs it
4
+ # because WebDriverAgent is built by Xcode at run time.
5
+ [ -n "${SUNGEN_ENV_LIB_LOADED:-}" ] || . "$(cd "$(dirname "${BASH_SOURCE[0]}")/../common" && pwd)/lib.sh"
6
+
7
+ # ── Command Line Tools ─────────────────────────────────────────────────────────────────────────
8
+ clt_check() { xcode-select -p 2>/dev/null; }
9
+ # `xcode-select --install` opens Apple's dialog and returns immediately — the install finishes
10
+ # outside this script, so report "needs a manual step" (exit 2) rather than success or failure.
11
+ clt_install() { xcode-select --install >/dev/null 2>&1; return 2; }
12
+ clt_verify() { clt_check || { printf 'finish the "Install Command Line Developer Tools" dialog, then re-run setup.sh'; return 1; }; }
13
+ run_step "Xcode Command Line Tools" clt_check clt_install clt_verify
14
+
15
+ # ── Xcode.app (guided) ─────────────────────────────────────────────────────────────────────────
16
+ # Only a full Xcode answers `xcodebuild -version`; a CLT-only machine errors out here.
17
+ xcode_check() { local v; v=$(xcodebuild -version 2>/dev/null) || return 1; first_line "$v"; }
18
+ run_guided "Xcode.app" xcode_check "not installed — required for iOS (Xcode builds WebDriverAgent). Install Xcode from the App Store, open it once, then re-run setup.sh"
19
+
20
+ # ── license — only meaningful once Xcode.app is there ──────────────────────────────────────────
21
+ if xcode_check >/dev/null 2>&1; then
22
+ license_check() { xcodebuild -license check >/dev/null 2>&1 && printf 'accepted'; }
23
+ license_install() { sudo xcodebuild -license accept; }
24
+ license_verify() { license_check || { printf 'run: sudo xcodebuild -license accept'; return 1; }; }
25
+ run_step "Xcode license" license_check license_install license_verify
26
+ fi
27
+
28
+ # ── applesimutils — Simulator permission steps (`grant permission …`) ──────────────────────────
29
+ asu_check() { have applesimutils && printf 'applesimutils at %s' "$(command -v applesimutils)"; }
30
+ asu_install() { brew tap wix/brew && brew install applesimutils; }
31
+ asu_verify() { asu_check || { printf 'brew tap wix/brew && brew install applesimutils'; return 1; }; }
32
+ run_step "applesimutils (Simulator permission steps)" asu_check asu_install asu_verify
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env bash
2
+ # verify-android.sh — macOS: is the Android side usable right now? (adb, env vars, connected devices)
3
+ [ -n "${SUNGEN_ENV_LIB_LOADED:-}" ] || . "$(cd "$(dirname "${BASH_SOURCE[0]}")/../common" && pwd)/lib.sh"
4
+
5
+ # Prefer the adb under ANDROID_HOME (this run may have just installed it) over a stale one on PATH.
6
+ android_adb() {
7
+ if [ -x "${ANDROID_HOME:-}/platform-tools/adb" ]; then printf '%s/platform-tools/adb' "$ANDROID_HOME"
8
+ else command -v adb 2>/dev/null; fi
9
+ }
10
+
11
+ adb_check() {
12
+ local adb v
13
+ adb=$(android_adb) && [ -x "$adb" ] || return 1
14
+ v=$("$adb" version 2>/dev/null) || return 1
15
+ printf '%s' "$(first_line "$v")"
16
+ }
17
+ run_check "adb" adb_check "adb not found — re-run without --skip-android, or open a NEW terminal if the profile block was just written"
18
+
19
+ env_check() {
20
+ [ -n "${ANDROID_HOME:-}" ] && [ -n "${JAVA_HOME:-}" ] || return 1
21
+ printf 'ANDROID_HOME=%s · JAVA_HOME=%s' "$ANDROID_HOME" "$JAVA_HOME"
22
+ }
23
+ run_check "ANDROID_HOME / JAVA_HOME" env_check "not set in this shell — open a NEW terminal (profile block) or export them"
24
+
25
+ devices_check() {
26
+ local adb n
27
+ adb=$(android_adb) && [ -x "$adb" ] || return 1
28
+ n=$("$adb" devices 2>/dev/null | awk 'NR>1 && $2=="device"{n++} END{print n+0}')
29
+ if [ "$n" -gt 0 ]; then printf '%s device(s) connected' "$n"
30
+ else printf 'no device connected — plug in a phone (USB debugging on) or start an emulator before /sungen:run-test'; fi
31
+ }
32
+ run_check "Android devices" devices_check "adb is not usable yet" warn
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env bash
2
+ # verify-ios.sh — macOS: is the iOS side usable right now? (Xcode selected, Simulator runtimes visible)
3
+ [ -n "${SUNGEN_ENV_LIB_LOADED:-}" ] || . "$(cd "$(dirname "${BASH_SOURCE[0]}")/../common" && pwd)/lib.sh"
4
+
5
+ xcode_version_check() { local v; v=$(xcodebuild -version 2>/dev/null) || return 1; printf '%s · %s' "$(first_line "$v")" "$(xcode-select -p 2>/dev/null)"; }
6
+ run_check "Xcode" xcode_version_check "Xcode.app not installed or not selected — App Store, then: sudo xcode-select -s /Applications/Xcode.app" warn
7
+
8
+ simulators_check() {
9
+ local json n
10
+ json=$(xcrun simctl list devices available -j 2>/dev/null) || return 1
11
+ n=$(printf '%s' "$json" | grep -c '"udid"' || true)
12
+ if [ "${n:-0}" -gt 0 ]; then printf '%s simulator(s) available (xcrun simctl list devices available)' "$n"
13
+ else printf 'no Simulator runtime — Xcode → Settings → Components, add an iOS Simulator runtime'; return 1; fi
14
+ }
15
+ run_check "iOS Simulators" simulators_check "xcrun simctl is not usable — needs Xcode.app (real-iPhone runs need it too: WebDriverAgent signing)" warn
@@ -0,0 +1,153 @@
1
+ # install-android.ps1 - Windows: JDK >= 17, Android SDK root, platform-tools + emulator, User environment.
2
+ # Headless via winget (decision D2): Microsoft OpenJDK 21 + Google's command-line tools zip + sdkmanager.
3
+ # Android Studio is optional (only for an on-screen emulator) and is never installed here.
4
+ # Dot-sourced by setup.ps1 (shares the runner + counters); runnable standalone for debugging.
5
+ if (-not $script:SungenEnvLibLoaded) { . (Join-Path $PSScriptRoot '..\common\lib.ps1') }
6
+
7
+ $JdkMin = 17
8
+ # Google rotates the command-line tools zip name; bump this ONE value when the download 404s.
9
+ # Current builds: https://developer.android.com/studio#command-line-tools-only
10
+ $CmdlineToolsBuild = '13114758'
11
+ $DefaultSdkRoot = Join-Path $env:LOCALAPPDATA 'Android\Sdk'
12
+
13
+ # --- JDK ------------------------------------------------------------------------------------------
14
+ # Any working JDK >= 17 is accepted (Microsoft, Temurin, Oracle, Corretto...) - we never replace a good one.
15
+ function Get-JavaMajor([string]$JavaExe) {
16
+ try { $out = (& $JavaExe -version 2>&1) | Out-String; if ($out -match '"(\d+)(\.\d+)*') { return [int]$Matches[1] } } catch {}
17
+ return 0
18
+ }
19
+ function Get-JavaHomeFromExe([string]$JavaExe) {
20
+ $out = (& $JavaExe -XshowSettings:properties -version 2>&1) | Out-String
21
+ if ($out -match 'java\.home = (.+)') { return $Matches[1].Trim() }
22
+ return $null
23
+ }
24
+ function Test-JdkHome([string]$JdkHome) {
25
+ if (-not $JdkHome) { return $false }
26
+ $exe = Join-Path $JdkHome 'bin\java.exe'
27
+ return ((Test-Path $exe) -and ((Get-JavaMajor $exe) -ge $JdkMin))
28
+ }
29
+ # Resolve-JavaHome - sets $env:JAVA_HOME when a usable JDK is found (env -> PATH -> vendor default folders).
30
+ function Resolve-JavaHome {
31
+ if (Test-JdkHome $env:JAVA_HOME) { return $true }
32
+ $java = Get-Command java -ErrorAction SilentlyContinue
33
+ if ($java -and ((Get-JavaMajor $java.Source) -ge $JdkMin)) {
34
+ $home = Get-JavaHomeFromExe $java.Source
35
+ if ($home -and (Test-Path $home)) { $env:JAVA_HOME = $home; return $true }
36
+ }
37
+ foreach ($root in @((Join-Path $env:ProgramFiles 'Microsoft'), (Join-Path $env:ProgramFiles 'Eclipse Adoptium'), (Join-Path $env:ProgramFiles 'Java'))) {
38
+ if (-not (Test-Path $root)) { continue }
39
+ $cand = Get-ChildItem $root -Directory -Filter 'jdk-*' -ErrorAction SilentlyContinue | Sort-Object Name -Descending |
40
+ Where-Object { Test-JdkHome $_.FullName } | Select-Object -First 1
41
+ if ($cand) { $env:JAVA_HOME = $cand.FullName; return $true }
42
+ }
43
+ return $false
44
+ }
45
+ function Get-JdkDetail { if (Resolve-JavaHome) { "java $(Get-JavaMajor (Join-Path $env:JAVA_HOME 'bin\java.exe')), JAVA_HOME=$env:JAVA_HOME" } }
46
+
47
+ Invoke-Step -Title "JDK >= $JdkMin" -Check { Get-JdkDetail } -Install {
48
+ Invoke-Native winget @('install', '--id', 'Microsoft.OpenJDK.21', '-e', '--silent', '--accept-source-agreements', '--accept-package-agreements')
49
+ Update-SessionPath
50
+ } -Verify { Get-JdkDetail } -Hint 'winget install --id Microsoft.OpenJDK.21 -e (then open a NEW terminal and re-run)'
51
+
52
+ # --- Android SDK root -----------------------------------------------------------------------------
53
+ function Find-SdkManager([string]$Root) {
54
+ if (-not $Root) { return $null }
55
+ foreach ($c in @((Join-Path $Root 'cmdline-tools\latest\bin\sdkmanager.bat'), (Join-Path $Root 'tools\bin\sdkmanager.bat'))) { if (Test-Path $c) { return $c } }
56
+ $any = Get-ChildItem (Join-Path $Root 'cmdline-tools') -Directory -ErrorAction SilentlyContinue |
57
+ ForEach-Object { Join-Path $_.FullName 'bin\sdkmanager.bat' } | Where-Object { Test-Path $_ } | Select-Object -First 1
58
+ return $any
59
+ }
60
+ function Test-SdkRoot([string]$Root) {
61
+ if (-not $Root -or -not (Test-Path $Root)) { return $false }
62
+ return ((Test-Path (Join-Path $Root 'platform-tools\adb.exe')) -or [bool](Find-SdkManager $Root))
63
+ }
64
+ # Precedence: what the user already set -> Android Studio's default (%LOCALAPPDATA%\Android\Sdk).
65
+ function Resolve-AndroidHome {
66
+ foreach ($c in @($env:ANDROID_HOME, $env:ANDROID_SDK_ROOT, $DefaultSdkRoot)) {
67
+ if (Test-SdkRoot $c) { $env:ANDROID_HOME = $c; $env:ANDROID_SDK_ROOT = $c; return $true }
68
+ }
69
+ return $false
70
+ }
71
+ function Install-CmdlineTools {
72
+ # Cmdlet errors (download 404 when Google rotates the zip, unpack failures) must become terminating so
73
+ # Invoke-Step's try/catch reports the real cause instead of only the generic hint.
74
+ $ErrorActionPreference = 'Stop'
75
+ $root = $DefaultSdkRoot
76
+ $url = "https://dl.google.com/android/repository/commandlinetools-win-${CmdlineToolsBuild}_latest.zip"
77
+ $zip = Join-Path $env:TEMP "commandlinetools-win-$CmdlineToolsBuild.zip"
78
+ $staging = Join-Path $env:TEMP "sungen-cmdline-tools-$CmdlineToolsBuild"
79
+ New-Item -ItemType Directory -Force -Path (Join-Path $root 'cmdline-tools') | Out-Null
80
+ Write-Host " downloading $url" -ForegroundColor DarkGray
81
+ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
82
+ Invoke-WebRequest -Uri $url -OutFile $zip -UseBasicParsing
83
+ if (Test-Path $staging) { Remove-Item $staging -Recurse -Force }
84
+ Expand-Archive -Path $zip -DestinationPath $staging -Force
85
+ # The zip unpacks as cmdline-tools\ but sdkmanager only works from cmdline-tools\latest\.
86
+ $latest = Join-Path $root 'cmdline-tools\latest'
87
+ if (Test-Path $latest) { Remove-Item $latest -Recurse -Force }
88
+ Move-Item (Join-Path $staging 'cmdline-tools') $latest
89
+ Remove-Item $zip -Force -ErrorAction SilentlyContinue
90
+ Remove-Item $staging -Recurse -Force -ErrorAction SilentlyContinue
91
+ $env:ANDROID_HOME = $root; $env:ANDROID_SDK_ROOT = $root
92
+ }
93
+ [void](Resolve-AndroidHome)
94
+ Invoke-Step -Title 'Android SDK root (cmdline-tools)' -Check {
95
+ if (Test-SdkRoot $env:ANDROID_HOME) { "ANDROID_HOME=$env:ANDROID_HOME" }
96
+ } -Install { Install-CmdlineTools } -Verify {
97
+ if (Find-SdkManager $env:ANDROID_HOME) { "ANDROID_HOME=$env:ANDROID_HOME" }
98
+ } -Hint "download the command-line tools zip from https://developer.android.com/studio#command-line-tools-only and unpack it to $DefaultSdkRoot\cmdline-tools\latest"
99
+
100
+ # --- platform-tools (adb) + emulator --------------------------------------------------------------
101
+ function Get-SdkPackagesDetail {
102
+ if (-not $env:ANDROID_HOME) { return $null }
103
+ $adb = Join-Path $env:ANDROID_HOME 'platform-tools\adb.exe'; $emu = Join-Path $env:ANDROID_HOME 'emulator\emulator.exe'
104
+ if ((Test-Path $adb) -and (Test-Path $emu)) { $v = (& $adb version) 2>$null | Select-Object -First 1; "platform-tools + emulator, $v" }
105
+ }
106
+ Invoke-Step -Title 'Android platform-tools (adb) + emulator' -Check { Get-SdkPackagesDetail } -Install {
107
+ $sm = Find-SdkManager $env:ANDROID_HOME
108
+ if (-not $sm) { throw 'no sdkmanager - the "Android SDK root" step must succeed first' }
109
+ # sdkmanager needs JAVA_HOME (set above). Licenses are Google's SDK terms - the same prompt Android
110
+ # Studio shows - accepted non-interactively here.
111
+ $yes = ('y' + "`n") * 40
112
+ $yes | & $sm "--sdk_root=$env:ANDROID_HOME" --licenses 2>&1 | Out-Null
113
+ Invoke-Native $sm @("--sdk_root=$env:ANDROID_HOME", 'platform-tools', 'emulator')
114
+ } -Verify { Get-SdkPackagesDetail } -Hint "sdkmanager --sdk_root=$env:ANDROID_HOME platform-tools emulator"
115
+
116
+ # Make the tools reachable for the rest of THIS run (the User environment covers new consoles).
117
+ if ($env:ANDROID_HOME) {
118
+ foreach ($d in @('platform-tools', 'emulator', 'cmdline-tools\latest\bin')) {
119
+ $p = Join-Path $env:ANDROID_HOME $d
120
+ if (-not (($env:Path -split ';') -contains $p)) { $env:Path = "$env:Path;$p" }
121
+ }
122
+ }
123
+
124
+ # --- User environment (decision D3) ---------------------------------------------------------------
125
+ $EnvTitle = 'User environment (ANDROID_HOME, ANDROID_SDK_ROOT, JAVA_HOME, Path)'
126
+ $PathDirs = @('platform-tools', 'emulator', 'cmdline-tools\latest\bin')
127
+ if (-not $env:ANDROID_HOME -or -not $env:JAVA_HOME) {
128
+ Write-Warn $EnvTitle 'skipped - fix the JDK / SDK steps above first, then re-run'
129
+ } elseif ($script:SungenEnvNoProfile) {
130
+ Write-Skip $EnvTitle '-NoProfile'
131
+ } elseif ($script:SungenEnvCheckOnly) {
132
+ $missing = @()
133
+ foreach ($pair in @(@('ANDROID_HOME', $env:ANDROID_HOME), @('ANDROID_SDK_ROOT', $env:ANDROID_HOME), @('JAVA_HOME', $env:JAVA_HOME))) {
134
+ if ([Environment]::GetEnvironmentVariable($pair[0], 'User') -ne $pair[1]) { $missing += $pair[0] }
135
+ }
136
+ $userPath = @(([Environment]::GetEnvironmentVariable('Path', 'User')) -split ';')
137
+ foreach ($d in $PathDirs) { if (-not ($userPath -contains (Join-Path $env:ANDROID_HOME $d))) { $missing += "Path+=$d" } }
138
+ if ($missing.Count -gt 0) { Write-Fail $EnvTitle "not set for your user: $($missing -join ', ') - run without -Check to write them" }
139
+ else { Write-Skip $EnvTitle 'already set for your user' }
140
+ } else {
141
+ $changed = @()
142
+ if (Set-UserEnv 'ANDROID_HOME' $env:ANDROID_HOME) { $changed += 'ANDROID_HOME' }
143
+ if (Set-UserEnv 'ANDROID_SDK_ROOT' $env:ANDROID_HOME) { $changed += 'ANDROID_SDK_ROOT' }
144
+ if (Set-UserEnv 'JAVA_HOME' $env:JAVA_HOME) { $changed += 'JAVA_HOME' }
145
+ foreach ($d in $PathDirs) { if (Add-UserPath (Join-Path $env:ANDROID_HOME $d)) { $changed += "Path+=$d" } }
146
+ if ($changed.Count -gt 0) { Write-Ok $EnvTitle "set for your user: $($changed -join ', ') - open a NEW terminal to apply" }
147
+ else { Write-Skip $EnvTitle 'already set for your user' }
148
+ }
149
+
150
+ # --- Android Studio - optional, GUI, never installed here ------------------------------------------
151
+ $studio = @((Join-Path $env:LOCALAPPDATA 'Programs\Android Studio'), (Join-Path $env:ProgramFiles 'Android\Android Studio')) | Where-Object { Test-Path $_ } | Select-Object -First 1
152
+ if ($studio) { Write-Skip 'Android Studio (optional)' $studio }
153
+ else { Write-Skip 'Android Studio (optional)' 'not installed - only needed for the AVD manager / an on-screen emulator: https://developer.android.com/studio' }
@@ -0,0 +1,26 @@
1
+ # verify-android.ps1 - Windows: is the Android side usable right now? (adb, env vars, connected devices)
2
+ if (-not $script:SungenEnvLibLoaded) { . (Join-Path $PSScriptRoot '..\common\lib.ps1') }
3
+
4
+ # Prefer the adb under ANDROID_HOME (this run may have just installed it) over a stale one on PATH.
5
+ function Get-AndroidAdb {
6
+ if ($env:ANDROID_HOME) { $p = Join-Path $env:ANDROID_HOME 'platform-tools\adb.exe'; if (Test-Path $p) { return $p } }
7
+ $cmd = Get-Command adb -ErrorAction SilentlyContinue
8
+ if ($cmd) { return $cmd.Source }
9
+ return $null
10
+ }
11
+
12
+ Invoke-Check -Title 'adb' -Check {
13
+ $adb = Get-AndroidAdb; if (-not $adb) { return $null }
14
+ (& $adb version) 2>$null | Select-Object -First 1
15
+ } -Hint 'adb not found - re-run without -SkipAndroid, or open a NEW terminal if the environment was just written'
16
+
17
+ Invoke-Check -Title 'ANDROID_HOME / JAVA_HOME' -Check {
18
+ if ($env:ANDROID_HOME -and $env:JAVA_HOME) { "ANDROID_HOME=$env:ANDROID_HOME, JAVA_HOME=$env:JAVA_HOME" }
19
+ } -Hint 'not set in this console - open a NEW terminal (User environment) or set them'
20
+
21
+ Invoke-Check -Title 'Android devices' -Check {
22
+ $adb = Get-AndroidAdb; if (-not $adb) { return $null }
23
+ $lines = @((& $adb devices) 2>$null | Select-Object -Skip 1 | Where-Object { $_ -match '\sdevice$' })
24
+ if ($lines.Count -gt 0) { "$($lines.Count) device(s) connected" }
25
+ else { 'no device connected - plug in a phone (USB debugging on) or start an emulator before /sungen:run-test' }
26
+ } -Hint 'adb is not usable yet' -Warn
@@ -0,0 +1,58 @@
1
+ <#
2
+ .SYNOPSIS
3
+ setup.ps1 - Windows entry point of the sungen Appium environment bundle.
4
+ .DESCRIPTION
5
+ .\appium-environment\setup.ps1 [-Check] [-SkipAndroid] [-NoProfile]
6
+
7
+ Emitted by `sungen init --mobile` / `sungen capability add mobile`, which also run it once.
8
+ Re-run it any time: every step is check -> install -> verify and a satisfied step is skipped.
9
+ -Check report what is missing, install nothing (side-effect free)
10
+ -SkipAndroid skip JDK / Android SDK / uiautomator2
11
+ -NoProfile do not write ANDROID_HOME / JAVA_HOME / Path to your User environment
12
+ iOS (xcuitest) needs Xcode and therefore a Mac - there is no iOS step on Windows.
13
+ Exit: 0 ready (or only warnings) - 1 something failed - 2 unsupported host
14
+ Run as: powershell -NoProfile -ExecutionPolicy Bypass -File .\appium-environment\setup.ps1
15
+ #>
16
+ [CmdletBinding()]
17
+ param([switch]$Check, [switch]$SkipAndroid, [switch]$NoProfile)
18
+
19
+ $ErrorActionPreference = 'Continue'
20
+ if ($PSVersionTable.PSVersion.Major -lt 5) { Write-Host 'Windows PowerShell 5.1 or newer is required.' -ForegroundColor Red; exit 2 }
21
+ if ($env:OS -ne 'Windows_NT') { Write-Host 'setup.ps1 is for Windows - on macOS run ./appium-environment/setup.sh' -ForegroundColor Red; exit 2 }
22
+
23
+ $script:SungenEnvCheckOnly = [bool]$Check
24
+ $script:SungenEnvSkipAndroid = [bool]$SkipAndroid
25
+ $script:SungenEnvNoProfile = [bool]$NoProfile
26
+ $script:ProjectDir = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
27
+ . (Join-Path $PSScriptRoot 'scripts\common\lib.ps1')
28
+
29
+ Set-Location $script:ProjectDir
30
+ if ($script:SungenEnvCheckOnly) { Write-Section 'sungen - Appium environment - check only (Windows)' }
31
+ else { Write-Section 'sungen - Appium environment setup (Windows)' }
32
+ Write-Host "project: $($script:ProjectDir)" -ForegroundColor DarkGray
33
+
34
+ # --- 1/4 prerequisites -----------------------------------------------------------------------------
35
+ Write-Section '1/4 Prerequisites'
36
+ . (Join-Path $PSScriptRoot 'scripts\common\verify-node.ps1')
37
+ Invoke-Check -Title 'winget (Windows Package Manager)' -Check {
38
+ if (Get-Command winget -ErrorAction SilentlyContinue) { $v = (& winget --version) 2>$null | Select-Object -First 1; "winget $v" }
39
+ } -Hint 'winget missing - install "App Installer" from the Microsoft Store (Windows 10 1809+ / 11), then re-run'
40
+
41
+ # --- 2/4 Android -----------------------------------------------------------------------------------
42
+ if (-not $script:SungenEnvSkipAndroid) {
43
+ Write-Section '2/4 Android (JDK, SDK, adb, emulator)'
44
+ . (Join-Path $PSScriptRoot 'scripts\windows\install-android.ps1')
45
+ } else {
46
+ Write-Section '2/4 Android'; Write-Skip 'Android toolchain' '-SkipAndroid'
47
+ }
48
+
49
+ # --- 3/4 Appium ------------------------------------------------------------------------------------
50
+ Write-Section '3/4 Appium (project-local) + platform driver'
51
+ . (Join-Path $PSScriptRoot 'scripts\common\install-appium.ps1')
52
+
53
+ # --- 4/4 health ------------------------------------------------------------------------------------
54
+ Write-Section '4/4 Health check'
55
+ if (-not $script:SungenEnvSkipAndroid) { . (Join-Path $PSScriptRoot 'scripts\windows\verify-android.ps1') }
56
+ . (Join-Path $PSScriptRoot 'scripts\common\verify-appium.ps1')
57
+
58
+ exit (Write-Summary)