mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-11-05 14:57:42 +00:00
ci: search the test directory from the chat list in the smoke test
The app is built with DIRECTORY_SERVICE_LINK set to the test directory's short address (sed in the workflow). The smoke test types "ci seed", taps "Search in Directory", accepts the first-search consent alert with "Search", and expects the Directory results with #ci_group (Join group) and #ci_channel (Join channel), retrying once if the search fails. The steps now use scripts/ci/ios-sim/ui, which wraps AXe (tap by label or value, type, wait, screenshot, compact accessibility tree) for scripts and for an agent on the runner. Later steps get SIM_UDID and UI_OUT. This run tests: the onboarding and typing steps on the new helper, the consent alert, and the directory results in the app.
This commit is contained in:
1 parent
e348855a20
commit
2ebad19b21
3 files changed
+145
-79
No files matched your search
@@ -204,6 +204,13 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
run: scripts/ci/ios-sim/backend.sh start ~/Downloads/${{ env.BACKEND_PKG }} "$RUNNER_TEMP/backend"
|
||||
|
||||
- name: Point the app at the test directory
|
||||
run: |
|
||||
link=$(cat "$RUNNER_TEMP/backend/directory-link")
|
||||
file=apps/ios/Shared/Model/DirectorySearch.swift
|
||||
sed -i '' "s|^let DIRECTORY_SERVICE_LINK = \".*\"$|let DIRECTORY_SERVICE_LINK = \"$link\"|" "$file"
|
||||
grep -F "let DIRECTORY_SERVICE_LINK = \"$link\"" "$file"
|
||||
|
||||
- name: Build app
|
||||
run: |
|
||||
xcodebuild build -quiet \
|
||||
|
||||
@@ -1,70 +1,45 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Installs the app in a new iPhone simulator and drives it with AXe: onboarding, chat list, typing in the search field.
|
||||
# Installs the app in a new iPhone simulator and runs the scripted UI cases with the ui helper:
|
||||
# onboarding to the chat list, typing in the search field, and searching the test directory.
|
||||
# Usage: scripts/ci/ios-sim/smoke-test.sh <path to SimpleX.app> <output dir>
|
||||
# Each step saves a screenshot and an accessibility tree to the output dir and prints the tree to the log.
|
||||
# Later steps of the job get SIM_UDID and UI_OUT, with the simulator still running.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
app=$1
|
||||
out=$2
|
||||
export UI_OUT=$2
|
||||
bundle_id=chat.simplex.app
|
||||
search_text="ci smoke"
|
||||
mkdir -p "$out"
|
||||
search_text="ci seed"
|
||||
ui=$(dirname "$0")/ui
|
||||
mkdir -p "$UI_OUT"
|
||||
|
||||
axe --version
|
||||
runtime=com.apple.CoreSimulator.SimRuntime.iOS-$(xcrun --sdk iphonesimulator --show-sdk-version | tr . -)
|
||||
udid=$(xcrun simctl create ci-iphone com.apple.CoreSimulator.SimDeviceType.iPhone-17 "$runtime")
|
||||
echo "simulator $udid, runtime $runtime"
|
||||
xcrun simctl boot "$udid"
|
||||
xcrun simctl bootstatus "$udid" -b
|
||||
open -a Simulator --args -CurrentDeviceUDID "$udid"
|
||||
SIM_UDID=$(xcrun simctl create ci-iphone com.apple.CoreSimulator.SimDeviceType.iPhone-17 "$runtime")
|
||||
export SIM_UDID
|
||||
echo "simulator $SIM_UDID, runtime $runtime"
|
||||
if [ -n "${GITHUB_ENV:-}" ]; then
|
||||
echo "SIM_UDID=$SIM_UDID" >> "$GITHUB_ENV"
|
||||
echo "UI_OUT=$UI_OUT" >> "$GITHUB_ENV"
|
||||
fi
|
||||
xcrun simctl boot "$SIM_UDID"
|
||||
xcrun simctl bootstatus "$SIM_UDID" -b
|
||||
open -a Simulator --args -CurrentDeviceUDID "$SIM_UDID"
|
||||
|
||||
xcrun simctl spawn "$udid" log stream --level debug --style compact --predicate 'process == "SimpleX"' > "$out/app.log" 2>&1 &
|
||||
xcrun simctl spawn "$SIM_UDID" log stream --level debug --style compact --predicate 'process == "SimpleX"' > "$UI_OUT/app.log" 2>&1 &
|
||||
log_pid=$!
|
||||
|
||||
step=0
|
||||
snap() {
|
||||
step=$((step + 1))
|
||||
local f
|
||||
f=$(printf '%s/%02d-%s' "$out" "$step" "$1")
|
||||
xcrun simctl io "$udid" screenshot "$f.png" > /dev/null
|
||||
axe describe-ui --udid "$udid" > "$f.json"
|
||||
echo "::group::accessibility tree: $1"
|
||||
cat "$f.json"
|
||||
echo "::endgroup::"
|
||||
}
|
||||
|
||||
wait_for() {
|
||||
local deadline=$((SECONDS + $2))
|
||||
local tree="$out/wait.json"
|
||||
until axe describe-ui --udid "$udid" > "$tree" && grep -qF "$1" "$tree"; do
|
||||
if [ "$SECONDS" -ge "$deadline" ]; then
|
||||
echo "error: \"$1\" not shown after $2 s"
|
||||
return 1
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
}
|
||||
|
||||
tap_label() {
|
||||
axe tap --label "$1" --wait-timeout "$2" --post-delay 2 --udid "$udid"
|
||||
}
|
||||
|
||||
# SwiftUI text fields have no accessibility label; an empty field exposes its placeholder as the value
|
||||
tap_field() {
|
||||
axe tap --value "$1" --wait-timeout "$2" --post-delay 2 --udid "$udid"
|
||||
}
|
||||
|
||||
finish() {
|
||||
local status=$?
|
||||
snap final || true
|
||||
"$ui" shot final || true
|
||||
kill "$log_pid" || true
|
||||
echo "::group::core start in the app log"
|
||||
grep -E 'chatMigrateInit|startChat' "$out/app.log" || true
|
||||
grep -E 'chatMigrateInit|startChat' "$UI_OUT/app.log" || true
|
||||
echo "::endgroup::"
|
||||
echo "::group::app log, last 300 lines"
|
||||
tail -n 300 "$out/app.log"
|
||||
tail -n 300 "$UI_OUT/app.log"
|
||||
echo "::endgroup::"
|
||||
find ~/Library/Logs/DiagnosticReports -name 'SimpleX*' -newer "$app" 2>/dev/null | while read -r report; do
|
||||
echo "::group::crash report $report"
|
||||
@@ -75,46 +50,68 @@ finish() {
|
||||
}
|
||||
trap finish EXIT
|
||||
|
||||
xcrun simctl install "$udid" "$app"
|
||||
xcrun simctl launch "$udid" "$bundle_id"
|
||||
expect_in_tree() {
|
||||
local tree
|
||||
tree=$("$ui" tree)
|
||||
if ! grep -q "$@" <<< "$tree"; then
|
||||
echo "error: ${*: -1} is not in the accessibility tree"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
wait_for "Get started" 180
|
||||
snap first-screen
|
||||
tap_label "Get started" 10
|
||||
xcrun simctl install "$SIM_UDID" "$app"
|
||||
xcrun simctl launch "$SIM_UDID" "$bundle_id"
|
||||
|
||||
wait_for "Create profile" 30
|
||||
tap_field "Enter profile name..." 10
|
||||
axe type 'citester' --udid "$udid"
|
||||
snap profile-name
|
||||
tap_label "Create profile" 10
|
||||
# onboarding to the chat list
|
||||
"$ui" wait "Get started" 180
|
||||
"$ui" shot first-screen
|
||||
"$ui" tap "Get started"
|
||||
|
||||
wait_for "Continue" 60
|
||||
snap your-network
|
||||
tap_label "Continue" 10
|
||||
"$ui" wait "Create profile" 30
|
||||
"$ui" tap-value "Enter profile name..."
|
||||
"$ui" type citester
|
||||
"$ui" shot profile-name
|
||||
"$ui" tap "Create profile"
|
||||
|
||||
wait_for "Accept" 30
|
||||
snap network-commitments
|
||||
tap_label "Accept" 10
|
||||
"$ui" wait "Continue" 60
|
||||
"$ui" shot your-network
|
||||
"$ui" tap "Continue"
|
||||
|
||||
"$ui" wait "Accept" 30
|
||||
"$ui" shot network-commitments
|
||||
"$ui" tap "Accept"
|
||||
|
||||
sleep 5
|
||||
snap after-onboarding
|
||||
tap_label "Allow" 20 || echo "no notifications alert to allow"
|
||||
"$ui" shot after-onboarding
|
||||
"$ui" tap "Allow" 20 || echo "no notifications alert to allow"
|
||||
|
||||
wait_for "Search or paste SimpleX link" 60
|
||||
snap chat-list
|
||||
tap_field "Search or paste SimpleX link" 10
|
||||
axe type "$search_text" --udid "$udid"
|
||||
"$ui" wait "Search or paste SimpleX link" 60
|
||||
"$ui" shot chat-list
|
||||
|
||||
# typed text that is not a SimpleX name offers the directory
|
||||
"$ui" tap-value "Search or paste SimpleX link"
|
||||
"$ui" type "$search_text"
|
||||
sleep 2
|
||||
snap search-typed
|
||||
|
||||
"$ui" shot search-typed
|
||||
# the keyboard capitalizes the first letter
|
||||
tree=$(ls "$out"/*-search-typed.json)
|
||||
if ! grep -qiF "$search_text" "$tree"; then
|
||||
echo "error: \"$search_text\" is not in the accessibility tree after typing"
|
||||
exit 1
|
||||
expect_in_tree -iF "\"$search_text\""
|
||||
expect_in_tree -F '"Search in Directory"'
|
||||
|
||||
# the directory finds the seeded group and channel, after the first-search consent
|
||||
"$ui" tap "Search in Directory"
|
||||
"$ui" wait "Search in Directory?" 10
|
||||
"$ui" shot search-consent
|
||||
"$ui" tap "Search"
|
||||
if ! "$ui" wait "Join channel" 60; then
|
||||
"$ui" shot search-failed
|
||||
"$ui" tap "Search failed, tap to retry"
|
||||
"$ui" wait "Join channel" 60
|
||||
fi
|
||||
if ! grep -qF "Search in Directory" "$tree"; then
|
||||
echo "error: no \"Search in Directory\" row after typing"
|
||||
exit 1
|
||||
fi
|
||||
echo "smoke test passed: the chat list search field shows \"$search_text\""
|
||||
"$ui" shot directory-results
|
||||
# the header alone is matched exactly: "Search in Directory" stays shown above the field
|
||||
expect_in_tree -F '"Directory"'
|
||||
expect_in_tree -F '#ci_group'
|
||||
expect_in_tree -F 'Join group'
|
||||
expect_in_tree -F '#ci_channel'
|
||||
expect_in_tree -F 'Join channel'
|
||||
echo "smoke test passed: the directory search for \"$search_text\" shows #ci_group (Join group) and #ci_channel (Join channel)"
|
||||
Executable
+62
@@ -0,0 +1,62 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Drives the app in the simulator SIM_UDID with AXe, for the scripted cases and for an agent on the runner.
|
||||
# Screenshots and accessibility trees go to UI_OUT.
|
||||
#
|
||||
# ui tap <label> [secs] tap the element with this exact accessibility label, waiting up to secs (10) for it
|
||||
# ui tap-value <text> [secs] tap the element with this exact value: text fields have no label, only their placeholder or text
|
||||
# ui type <text> type into the focused field (US keyboard characters only)
|
||||
# ui wait <text> [secs] wait up to secs (30) until a label or value contains the text (case-sensitive)
|
||||
# ui shot <name> save UI_OUT/NN-<name>.png and .json, and print the tree
|
||||
# ui tree print the tree, one element per line: type, label, value, top-left corner and size in points
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
: "${SIM_UDID:?SIM_UDID must be the simulator UDID}"
|
||||
|
||||
compact_tree() {
|
||||
jq -r '.. | objects | select(.AXLabel or .AXValue)
|
||||
| "\(.type) \(.AXLabel | tojson) value=\(.AXValue | tojson) at \(.frame.x // 0 | round),\(.frame.y // 0 | round) size \(.frame.width // 0 | round)x\(.frame.height // 0 | round)"'
|
||||
}
|
||||
|
||||
tree() {
|
||||
axe describe-ui --udid "$SIM_UDID" | compact_tree
|
||||
}
|
||||
|
||||
wait_for() {
|
||||
local text=$1 secs=${2:-30}
|
||||
local deadline=$((SECONDS + secs)) t
|
||||
until t=$(tree 2> /dev/null) && grep -qF -- "$text" <<< "$t"; do
|
||||
if [ "$SECONDS" -ge "$deadline" ]; then
|
||||
echo "error: \"$text\" not shown after $secs s"
|
||||
return 1
|
||||
fi
|
||||
sleep 2
|
||||
done
|
||||
}
|
||||
|
||||
shot() {
|
||||
: "${UI_OUT:?UI_OUT must be the output directory}"
|
||||
mkdir -p "$UI_OUT"
|
||||
local n f
|
||||
n=$(find "$UI_OUT" -maxdepth 1 -name '*.png' | wc -l)
|
||||
f=$(printf '%s/%02d-%s' "$UI_OUT" $((n + 1)) "$1")
|
||||
xcrun simctl io "$SIM_UDID" screenshot "$f.png" > /dev/null
|
||||
axe describe-ui --udid "$SIM_UDID" > "$f.json"
|
||||
echo "::group::accessibility tree: $1"
|
||||
compact_tree < "$f.json"
|
||||
echo "::endgroup::"
|
||||
}
|
||||
|
||||
case "${1:-}" in
|
||||
tap) axe tap --label "$2" --wait-timeout "${3:-10}" --post-delay 2 --udid "$SIM_UDID" ;;
|
||||
tap-value) axe tap --value "$2" --wait-timeout "${3:-10}" --post-delay 2 --udid "$SIM_UDID" ;;
|
||||
type) axe type "$2" --udid "$SIM_UDID" ;;
|
||||
wait) wait_for "$2" "${3:-30}" ;;
|
||||
shot) shot "$2" ;;
|
||||
tree) tree ;;
|
||||
*)
|
||||
sed -n '3,12s/^# \{0,1\}//p' "$0"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
Reference in new issue
Block a user