@sun-asterisk/sungen 3.2.31 → 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.
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/audit.js +3 -1
- 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 +11 -0
- package/dist/cli/commands/capability.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/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/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/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/partials/await-elements.hbs +12 -0
- package/dist/generators/test-generator/adapters/appium/templates/test-file.hbs +26 -0
- 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/row-scoped-column-assertion.hbs +23 -1
- 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 +13 -2
- package/dist/generators/test-generator/adapters/playwright/templates/test-file.hbs +14 -0
- package/dist/generators/test-generator/code-generator.d.ts.map +1 -1
- package/dist/generators/test-generator/code-generator.js +56 -13
- package/dist/generators/test-generator/code-generator.js.map +1 -1
- package/dist/generators/test-generator/diagnostics.d.ts +39 -2
- package/dist/generators/test-generator/diagnostics.d.ts.map +1 -1
- package/dist/generators/test-generator/diagnostics.js +38 -1
- package/dist/generators/test-generator/diagnostics.js.map +1 -1
- package/dist/generators/test-generator/patterns/index.d.ts +1 -1
- package/dist/generators/test-generator/patterns/index.js +1 -1
- package/dist/generators/test-generator/step-mapper.d.ts +35 -0
- package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
- package/dist/generators/test-generator/step-mapper.js +103 -4
- 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/selector-resolver.d.ts +47 -0
- package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
- package/dist/generators/test-generator/utils/selector-resolver.js +17 -3
- 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 +23 -4
- package/dist/harness/audit.js.map +1 -1
- 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/spec-branches.d.ts +7 -2
- package/dist/harness/spec-branches.d.ts.map +1 -1
- package/dist/harness/spec-branches.js +62 -11
- 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 +119 -10
- package/dist/harness/spec-coverage.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/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-initializer.d.ts +1 -0
- package/dist/orchestrator/project-initializer.d.ts.map +1 -1
- package/dist/orchestrator/project-initializer.js +14 -0
- package/dist/orchestrator/project-initializer.js.map +1 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +5 -1
- package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +55 -2
- 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-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/package.json +3 -3
- package/src/cli/commands/audit.ts +2 -1
- package/src/cli/commands/capability.ts +12 -1
- package/src/cli/commands/init.ts +3 -0
- package/src/cli/commands/setup-mobile-env.ts +64 -0
- package/src/cli/index.ts +2 -0
- 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/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/partials/await-elements.hbs +12 -0
- package/src/generators/test-generator/adapters/appium/templates/test-file.hbs +26 -0
- 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/row-scoped-column-assertion.hbs +23 -1
- 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 +13 -2
- package/src/generators/test-generator/adapters/playwright/templates/test-file.hbs +14 -0
- package/src/generators/test-generator/code-generator.ts +66 -17
- package/src/generators/test-generator/diagnostics.ts +39 -2
- package/src/generators/test-generator/patterns/index.ts +1 -1
- package/src/generators/test-generator/step-mapper.ts +106 -5
- package/src/generators/test-generator/template-engine.ts +18 -0
- package/src/generators/test-generator/utils/selector-resolver.ts +18 -3
- package/src/harness/audit.ts +24 -5
- package/src/harness/next-step.ts +2 -1
- package/src/harness/parse.ts +42 -7
- package/src/harness/spec-branches.ts +67 -12
- package/src/harness/spec-coverage.ts +121 -11
- package/src/orchestrator/appium-environment-setup.ts +127 -0
- package/src/orchestrator/mobile-runtime-scaffolder.ts +2 -0
- package/src/orchestrator/project-initializer.ts +15 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +5 -1
- package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +55 -2
- 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-mock.ts +12 -4
|
@@ -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)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# setup.sh — macOS entry point of the sungen Appium environment bundle.
|
|
3
|
+
#
|
|
4
|
+
# ./appium-environment/setup.sh [--check] [--skip-android] [--skip-ios] [--no-profile]
|
|
5
|
+
#
|
|
6
|
+
# Emitted by `sungen init --mobile` / `sungen capability add mobile`, which also run it once.
|
|
7
|
+
# Re-run it any time: every step is check → install → verify and a satisfied step is skipped.
|
|
8
|
+
# --check report what is missing, install nothing (side-effect free)
|
|
9
|
+
# --skip-android skip JDK / Android SDK / uiautomator2
|
|
10
|
+
# --skip-ios skip Xcode CLT / applesimutils / xcuitest
|
|
11
|
+
# --no-profile do not write the ANDROID_HOME / JAVA_HOME / PATH block to your shell profile
|
|
12
|
+
# Exit: 0 ready (or only warnings) · 1 something failed · 2 unsupported OS / bad flag
|
|
13
|
+
set -u
|
|
14
|
+
|
|
15
|
+
BUNDLE_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
16
|
+
PROJECT_DIR=$(cd "$BUNDLE_DIR/.." && pwd)
|
|
17
|
+
export PROJECT_DIR
|
|
18
|
+
. "$BUNDLE_DIR/scripts/common/lib.sh"
|
|
19
|
+
|
|
20
|
+
usage() { sed -n '4,13p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'; }
|
|
21
|
+
|
|
22
|
+
for arg in "$@"; do
|
|
23
|
+
case "$arg" in
|
|
24
|
+
--check) SUNGEN_ENV_CHECK_ONLY=1 ;;
|
|
25
|
+
--skip-android) SUNGEN_ENV_SKIP_ANDROID=1 ;;
|
|
26
|
+
--skip-ios) SUNGEN_ENV_SKIP_IOS=1 ;;
|
|
27
|
+
--no-profile) SUNGEN_ENV_NO_PROFILE=1 ;;
|
|
28
|
+
-h|--help) usage; exit 0 ;;
|
|
29
|
+
*) printf 'unknown flag: %s\n\n' "$arg" >&2; usage >&2; exit 2 ;;
|
|
30
|
+
esac
|
|
31
|
+
done
|
|
32
|
+
|
|
33
|
+
case "$(uname -s)" in
|
|
34
|
+
Darwin) ;;
|
|
35
|
+
Linux)
|
|
36
|
+
printf 'ℹ Linux detected — this bundle provisions macOS and Windows.\n'
|
|
37
|
+
printf ' Android on Linux: see README.md → "Manual fallback" (JDK + Android SDK via your package manager,\n'
|
|
38
|
+
printf ' then `sungen capability add mobile` for the project-local Appium + uiautomator2).\n'
|
|
39
|
+
exit 0 ;;
|
|
40
|
+
*) printf '✗ unsupported OS: %s (macOS: setup.sh · Windows: setup.ps1)\n' "$(uname -s)" >&2; exit 2 ;;
|
|
41
|
+
esac
|
|
42
|
+
|
|
43
|
+
cd "$PROJECT_DIR"
|
|
44
|
+
if [ "$SUNGEN_ENV_CHECK_ONLY" = 1 ]; then section "sungen · Appium environment — check only (macOS)"
|
|
45
|
+
else section "sungen · Appium environment setup (macOS)"; fi
|
|
46
|
+
printf '%sproject: %s%s\n' "$C_DIM" "$PROJECT_DIR" "$C_RESET"
|
|
47
|
+
|
|
48
|
+
# ── 1/5 prerequisites ──────────────────────────────────────────────────────────────────────────
|
|
49
|
+
section "1/5 Prerequisites"
|
|
50
|
+
. "$BUNDLE_DIR/scripts/common/verify-node.sh"
|
|
51
|
+
|
|
52
|
+
brew_check() { have brew && printf 'brew at %s' "$(command -v brew)"; }
|
|
53
|
+
brew_install() {
|
|
54
|
+
NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" || return 1
|
|
55
|
+
# The installer does not touch the running shell — pick brew up for the rest of this run.
|
|
56
|
+
if [ -x /opt/homebrew/bin/brew ]; then eval "$(/opt/homebrew/bin/brew shellenv)"
|
|
57
|
+
elif [ -x /usr/local/bin/brew ]; then eval "$(/usr/local/bin/brew shellenv)"; fi
|
|
58
|
+
}
|
|
59
|
+
brew_verify() { brew_check || { printf 'Homebrew not found after install — https://brew.sh, then re-run'; return 1; }; }
|
|
60
|
+
run_step "Homebrew" brew_check brew_install brew_verify
|
|
61
|
+
|
|
62
|
+
# ── 2/5 Android ────────────────────────────────────────────────────────────────────────────────
|
|
63
|
+
if [ "$SUNGEN_ENV_SKIP_ANDROID" != 1 ]; then
|
|
64
|
+
section "2/5 Android (JDK, SDK, adb, emulator)"
|
|
65
|
+
. "$BUNDLE_DIR/scripts/macos/install-android.sh"
|
|
66
|
+
else
|
|
67
|
+
section "2/5 Android"; log_skip "Android toolchain" "--skip-android"
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
# ── 3/5 iOS ────────────────────────────────────────────────────────────────────────────────────
|
|
71
|
+
if [ "$SUNGEN_ENV_SKIP_IOS" != 1 ]; then
|
|
72
|
+
section "3/5 iOS (Xcode, applesimutils)"
|
|
73
|
+
. "$BUNDLE_DIR/scripts/macos/install-ios.sh"
|
|
74
|
+
else
|
|
75
|
+
section "3/5 iOS"; log_skip "iOS toolchain" "--skip-ios"
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
# ── 4/5 Appium ─────────────────────────────────────────────────────────────────────────────────
|
|
79
|
+
section "4/5 Appium (project-local) + platform drivers"
|
|
80
|
+
. "$BUNDLE_DIR/scripts/common/install-appium.sh"
|
|
81
|
+
|
|
82
|
+
# ── 5/5 health ─────────────────────────────────────────────────────────────────────────────────
|
|
83
|
+
section "5/5 Health check"
|
|
84
|
+
[ "$SUNGEN_ENV_SKIP_ANDROID" != 1 ] && . "$BUNDLE_DIR/scripts/macos/verify-android.sh"
|
|
85
|
+
[ "$SUNGEN_ENV_SKIP_IOS" != 1 ] && . "$BUNDLE_DIR/scripts/macos/verify-ios.sh"
|
|
86
|
+
. "$BUNDLE_DIR/scripts/common/verify-appium.sh"
|
|
87
|
+
|
|
88
|
+
print_summary
|
|
@@ -26,7 +26,7 @@ export interface DeclaredSignal {
|
|
|
26
26
|
attribute?: string;
|
|
27
27
|
}
|
|
28
28
|
/** Where the control publishes its on/off state — decided in the page, from the real element. */
|
|
29
|
-
export type StateSource = 'native' | 'aria-pressed' | 'aria-checked' | 'class' | 'attribute' | 'none';
|
|
29
|
+
export type StateSource = 'native' | 'aria-pressed' | 'aria-checked' | 'aria-selected' | 'aria-current' | 'class' | 'attribute' | 'none';
|
|
30
30
|
export interface ControlProbe {
|
|
31
31
|
source: StateSource;
|
|
32
32
|
/** Current state, already normalised. Meaningless when `source` is 'native' (Playwright reads it). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"specs-checked-state.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-checked-state.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAchD,kGAAkG;AAClG,MAAM,WAAW,cAAc;IAAG,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE;AAEtE,iGAAiG;AACjG,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,cAAc,GAAG,cAAc,GAAG,OAAO,GAAG,WAAW,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"specs-checked-state.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/templates/specs-checked-state.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAchD,kGAAkG;AAClG,MAAM,WAAW,cAAc;IAAG,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE;AAEtE,iGAAiG;AACjG,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,cAAc,GAAG,cAAc,GAAG,eAAe,GAAG,cAAc,GAAG,OAAO,GAAG,WAAW,GAAG,MAAM,CAAC;AACzI,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,sGAAsG;IACtG,EAAE,EAAE,OAAO,CAAC;IACZ,mGAAmG;IACnG,UAAU,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,cAAc,GAAG,YAAY,CAoEnF;AAmCD;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAC7C,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/E,OAAO,CAAC,IAAI,CAAC,CA6Gf;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EACjD,OAAO,CAAC,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/E,OAAO,CAAC,IAAI,CAAC,CA4Cf"}
|
|
@@ -73,6 +73,24 @@ function probeControl(node, declared) {
|
|
|
73
73
|
// Role claims checkable but nothing is published — let Playwright raise its own, precise error.
|
|
74
74
|
if (ariaCheckableRole)
|
|
75
75
|
return { source: 'native', on: false, buttonLike };
|
|
76
|
+
// `is selected` on a tab, an option, a chip or a nav item. These publish `aria-selected` or
|
|
77
|
+
// `aria-current`, which nothing above reads: the probe fell straight through to 'none' and the
|
|
78
|
+
// step failed saying the control has no on/off state to set, on a control that publishes one in
|
|
79
|
+
// the standard place. Read here rather than in the templates so a suite already generated in the
|
|
80
|
+
// field picks the fix up without regenerating — the same reasoning the URL predicate is a runtime
|
|
81
|
+
// helper for.
|
|
82
|
+
//
|
|
83
|
+
// `aria-current` is not a boolean: the spec allows 'page', 'step', 'location', 'date', 'time' and
|
|
84
|
+
// 'true', and only the literal 'false' (or the attribute's absence) means off. Read that way
|
|
85
|
+
// round, so `aria-current="page"` on the active nav item reads as ON rather than as a stray
|
|
86
|
+
// non-'true' string.
|
|
87
|
+
if (el.hasAttribute('aria-selected')) {
|
|
88
|
+
return { source: 'aria-selected', on: String(el.getAttribute('aria-selected') ?? '').trim().toLowerCase() === 'true', buttonLike };
|
|
89
|
+
}
|
|
90
|
+
if (el.hasAttribute('aria-current')) {
|
|
91
|
+
const raw = String(el.getAttribute('aria-current') ?? '').trim().toLowerCase();
|
|
92
|
+
return { source: 'aria-current', on: raw !== '' && raw !== 'false', buttonLike };
|
|
93
|
+
}
|
|
76
94
|
return { source: 'none', on: false, buttonLike };
|
|
77
95
|
}
|
|
78
96
|
/** The declaration both helpers forward into the page. Undefined when nothing was declared, so a
|
|
@@ -91,8 +109,13 @@ const left = (deadline) => Math.max(250, deadline - Date.now());
|
|
|
91
109
|
/** Read the live state. A REMOVED attribute means off: `aria-pressed={on || undefined}` is a common
|
|
92
110
|
* React/Vue idiom, so requiring a literal "false" would fail a control that is correctly off. */
|
|
93
111
|
async function readOn(target, attr, deadline) {
|
|
94
|
-
const raw = await target.getAttribute(attr, { timeout: left(deadline) });
|
|
95
|
-
|
|
112
|
+
const raw = String(await target.getAttribute(attr, { timeout: left(deadline) }) ?? '').trim().toLowerCase();
|
|
113
|
+
// `aria-current` is the one non-boolean of the four: the spec allows 'page', 'step', 'location',
|
|
114
|
+
// 'date', 'time' and 'true', so only the literal 'false' (or absence) means off. Read the other
|
|
115
|
+
// way round it would report the active nav item as OFF.
|
|
116
|
+
if (attr === 'aria-current')
|
|
117
|
+
return raw !== '' && raw !== 'false';
|
|
118
|
+
return raw === 'true';
|
|
96
119
|
}
|
|
97
120
|
/**
|
|
98
121
|
* Drive `target` to the requested on/off state, idempotently.
|
|
@@ -184,7 +207,7 @@ async function setCheckedState(target, want, label, options) {
|
|
|
184
207
|
}
|
|
185
208
|
// Not pressable and publishes nothing — there is no reading of this that makes the step meaningful.
|
|
186
209
|
throw new Error(`Cannot ${want ? 'check' : 'uncheck'} [${label}] — it is not a checkbox/radio/switch, is not a `
|
|
187
|
-
+ `button, and publishes no aria-pressed/aria-checked, so it has no on/off state to set. Point the `
|
|
210
|
+
+ `button, and publishes no aria-pressed/aria-checked/aria-selected/aria-current, so it has no on/off state to set. Point the `
|
|
188
211
|
+ `selector at the control itself, or give it a real ARIA toggle role.`);
|
|
189
212
|
}
|
|
190
213
|
const attr = probe.source;
|