Fix root causes uncovered across repeated parallel runs: - Admin seed password "test1234" failed backend complexity (needs uppercase). Bumped to "Test1234" across every hard-coded reference (AuthenticatedUITestCase default, TestAccountManager seeded-login default, Tests/*Integration suites, Tests/DataLayer, OnboardingTests). - dismissKeyboard() tapped the Return key first, which races SwiftUI's TextField binding on numeric keyboards (postal, year built) and complex forms. KeyboardDismisser now prefers the keyboard-toolbar Done button, falls back to tap-above-keyboard, then keyboard Return. BaseUITestCase.clearAndEnterText uses the same helper. - Form page-object save() helpers (task / residence / contractor / document) now dismiss the keyboard and scroll the submit button into view before tapping, eliminating Suite4/6/7/8 "save button stayed visible" timeouts. - Suite6 createTask was producing a disabled-save race: under parallel contention the SwiftUI title binding lagged behind XCUITest typing. Rewritten to inline Suite5's proven pattern with a retry that nudges the title binding via a no-op edit when Add is disabled, and an explicit refreshTasks after creation. - Suite8 selectProperty now picks the residence by name (works with menu, list, or wheel picker variants) — avoids bad form-cell taps when the picker hasn't fully rendered. - run_ui_tests.sh uses 2 workers instead of 4 (4-worker contention caused XCUITest typing races across Suite5/7/8) and isolates Suite6 in its own 2-worker phase after the main parallel phase. - Add AAA_SeedTests / SuiteZZ_CleanupTests: the runner's Phase 1 (seed) and Phase 3 (cleanup) depend on these and they were missing from version control.
202 lines
7.3 KiB
Bash
Executable File
202 lines
7.3 KiB
Bash
Executable File
#!/bin/bash
|
|
# run_ui_tests.sh — Three-phase UI test runner with parallel middle phase
|
|
#
|
|
# Usage:
|
|
# ./run_ui_tests.sh # Default: iPhone 17 Pro, 4 workers
|
|
# ./run_ui_tests.sh "iPhone Air" 3 # Custom device and worker count
|
|
# ./run_ui_tests.sh --skip-seed # Skip seeding (already done)
|
|
# ./run_ui_tests.sh --skip-cleanup # Skip cleanup at end
|
|
# ./run_ui_tests.sh --only-parallel # Only run parallel phase
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
PROJECT="$SCRIPT_DIR/honeyDue.xcodeproj"
|
|
SCHEME="HoneyDueUITests"
|
|
DESTINATION="platform=iOS Simulator,name=iPhone 17 Pro"
|
|
# 2 workers avoids simulator contention that caused intermittent XCUITest
|
|
# typing / UI-update races (Suite5/7/8 flakes under 4-worker load). Phase 2b
|
|
# isolates Suite6 further.
|
|
WORKERS=2
|
|
|
|
SKIP_SEED=false
|
|
SKIP_CLEANUP=false
|
|
ONLY_PARALLEL=false
|
|
|
|
# Parse flags (positional args for device/workers, flags for skip options)
|
|
POSITIONAL_ARGS=()
|
|
for arg in "$@"; do
|
|
case $arg in
|
|
--skip-seed) SKIP_SEED=true ;;
|
|
--skip-cleanup) SKIP_CLEANUP=true ;;
|
|
--only-parallel) ONLY_PARALLEL=true; SKIP_SEED=true; SKIP_CLEANUP=true ;;
|
|
*) POSITIONAL_ARGS+=("$arg") ;;
|
|
esac
|
|
done
|
|
|
|
if [ ${#POSITIONAL_ARGS[@]} -ge 1 ]; then
|
|
DESTINATION="platform=iOS Simulator,name=${POSITIONAL_ARGS[0]}"
|
|
fi
|
|
if [ ${#POSITIONAL_ARGS[@]} -ge 2 ]; then
|
|
WORKERS="${POSITIONAL_ARGS[1]}"
|
|
fi
|
|
|
|
RESULTS_DIR="$SCRIPT_DIR/build/test-results"
|
|
DERIVED_DATA="$SCRIPT_DIR/build/DerivedData"
|
|
mkdir -p "$RESULTS_DIR" "$DERIVED_DATA"
|
|
|
|
BOLD='\033[1m'
|
|
GREEN='\033[0;32m'
|
|
RED='\033[0;31m'
|
|
YELLOW='\033[0;33m'
|
|
RESET='\033[0m'
|
|
|
|
phase_header() {
|
|
echo ""
|
|
echo -e "${BOLD}════════════════════════════════════════════════════${RESET}"
|
|
echo -e "${BOLD} $1${RESET}"
|
|
echo -e "${BOLD}════════════════════════════════════════════════════${RESET}"
|
|
echo ""
|
|
}
|
|
|
|
# Seed tests — must run first, sequentially
|
|
SEED_TESTS=(
|
|
"-only-testing:HoneyDueUITests/AAA_SeedTests"
|
|
)
|
|
|
|
# All parallelizable test classes
|
|
PARALLEL_TESTS=(
|
|
"-only-testing:HoneyDueUITests/AuthCriticalPathTests"
|
|
"-only-testing:HoneyDueUITests/NavigationCriticalPathTests"
|
|
"-only-testing:HoneyDueUITests/SmokeTests"
|
|
"-only-testing:HoneyDueUITests/SimpleLoginTest"
|
|
"-only-testing:HoneyDueUITests/Suite0_OnboardingRebuildTests"
|
|
"-only-testing:HoneyDueUITests/Suite1_RegistrationTests"
|
|
"-only-testing:HoneyDueUITests/Suite2_AuthenticationRebuildTests"
|
|
"-only-testing:HoneyDueUITests/Suite3_ResidenceRebuildTests"
|
|
"-only-testing:HoneyDueUITests/Suite4_ComprehensiveResidenceTests"
|
|
"-only-testing:HoneyDueUITests/Suite5_TaskTests"
|
|
"-only-testing:HoneyDueUITests/Suite7_ContractorTests"
|
|
"-only-testing:HoneyDueUITests/Suite8_DocumentWarrantyTests"
|
|
"-only-testing:HoneyDueUITests/Suite9_IntegrationE2ETests"
|
|
"-only-testing:HoneyDueUITests/Suite10_ComprehensiveE2ETests"
|
|
)
|
|
|
|
# Suite6 runs in a smaller-parallel phase of its own. Under 4-worker contention
|
|
# with 14 other classes, SwiftUI's TextField binding intermittently lags behind
|
|
# XCUITest typing, leaving the Add-Task form un-submittable. Isolating Suite6
|
|
# to 2 workers gives the binding enough time to flush reliably.
|
|
SUITE6_TESTS=(
|
|
"-only-testing:HoneyDueUITests/Suite6_ComprehensiveTaskTests"
|
|
)
|
|
|
|
# Cleanup tests — must run last, sequentially
|
|
CLEANUP_TESTS=(
|
|
"-only-testing:HoneyDueUITests/SuiteZZ_CleanupTests"
|
|
)
|
|
|
|
run_phase() {
|
|
local phase_name="$1"
|
|
local result_path="$RESULTS_DIR/${phase_name}.xcresult"
|
|
shift
|
|
local extra_args=("$@")
|
|
|
|
rm -rf "$result_path"
|
|
|
|
xcodebuild test \
|
|
-project "$PROJECT" \
|
|
-scheme "$SCHEME" \
|
|
-destination "$DESTINATION" \
|
|
-derivedDataPath "$DERIVED_DATA" \
|
|
-resultBundlePath "$result_path" \
|
|
"${extra_args[@]}" \
|
|
2>&1 | tail -30
|
|
|
|
return ${PIPESTATUS[0]}
|
|
}
|
|
|
|
OVERALL_START=$(date +%s)
|
|
|
|
# ── Phase 1: Seed ──────────────────────────────────────────────
|
|
if [ "$SKIP_SEED" = false ]; then
|
|
phase_header "Phase 1/3: Seeding test data (sequential)"
|
|
SEED_START=$(date +%s)
|
|
|
|
if run_phase "SeedTests" "${SEED_TESTS[@]}"; then
|
|
SEED_END=$(date +%s)
|
|
echo -e "\n${GREEN}✓ Seed phase passed ($(( SEED_END - SEED_START ))s)${RESET}"
|
|
else
|
|
SEED_END=$(date +%s)
|
|
echo -e "\n${RED}✗ Seed phase FAILED ($(( SEED_END - SEED_START ))s)${RESET}"
|
|
echo -e "${RED} Cannot proceed without seeded data. Aborting.${RESET}"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# ── Phase 2: Parallel Tests ───────────────────────────────────
|
|
phase_header "Phase 2/3: Running tests in parallel ($WORKERS workers)"
|
|
PARALLEL_START=$(date +%s)
|
|
|
|
if run_phase "ParallelTests" \
|
|
-parallel-testing-enabled YES \
|
|
-parallel-testing-worker-count "$WORKERS" \
|
|
"${PARALLEL_TESTS[@]}"; then
|
|
PARALLEL_END=$(date +%s)
|
|
echo -e "\n${GREEN}✓ Parallel phase passed ($(( PARALLEL_END - PARALLEL_START ))s)${RESET}"
|
|
PARALLEL_PASSED=true
|
|
else
|
|
PARALLEL_END=$(date +%s)
|
|
echo -e "\n${RED}✗ Parallel phase FAILED ($(( PARALLEL_END - PARALLEL_START ))s)${RESET}"
|
|
PARALLEL_PASSED=false
|
|
fi
|
|
|
|
# ── Phase 2b: Suite6 (isolated parallel) ──────────────────────
|
|
phase_header "Phase 2b: Suite6 task tests (2 workers, isolated)"
|
|
SUITE6_START=$(date +%s)
|
|
|
|
if run_phase "Suite6Tests" \
|
|
-parallel-testing-enabled YES \
|
|
-parallel-testing-worker-count 2 \
|
|
"${SUITE6_TESTS[@]}"; then
|
|
SUITE6_END=$(date +%s)
|
|
echo -e "\n${GREEN}✓ Suite6 phase passed ($(( SUITE6_END - SUITE6_START ))s)${RESET}"
|
|
SUITE6_PASSED=true
|
|
else
|
|
SUITE6_END=$(date +%s)
|
|
echo -e "\n${RED}✗ Suite6 phase FAILED ($(( SUITE6_END - SUITE6_START ))s)${RESET}"
|
|
SUITE6_PASSED=false
|
|
fi
|
|
|
|
# ── Phase 3: Cleanup ──────────────────────────────────────────
|
|
if [ "$SKIP_CLEANUP" = false ]; then
|
|
phase_header "Phase 3/3: Cleaning up test data (sequential)"
|
|
CLEANUP_START=$(date +%s)
|
|
|
|
if run_phase "CleanupTests" "${CLEANUP_TESTS[@]}"; then
|
|
CLEANUP_END=$(date +%s)
|
|
echo -e "\n${GREEN}✓ Cleanup phase passed ($(( CLEANUP_END - CLEANUP_START ))s)${RESET}"
|
|
else
|
|
CLEANUP_END=$(date +%s)
|
|
echo -e "\n${YELLOW}⚠ Cleanup phase failed ($(( CLEANUP_END - CLEANUP_START ))s) — non-blocking${RESET}"
|
|
fi
|
|
fi
|
|
|
|
# ── Summary ───────────────────────────────────────────────────
|
|
OVERALL_END=$(date +%s)
|
|
TOTAL_TIME=$(( OVERALL_END - OVERALL_START ))
|
|
|
|
phase_header "Summary"
|
|
echo " Total time: ${TOTAL_TIME}s"
|
|
echo " Workers: $WORKERS"
|
|
echo " Results: $RESULTS_DIR/"
|
|
echo ""
|
|
|
|
if [ "$PARALLEL_PASSED" = true ] && [ "${SUITE6_PASSED:-true}" = true ]; then
|
|
echo -e " ${GREEN}${BOLD}ALL TESTS PASSED${RESET}"
|
|
exit 0
|
|
else
|
|
echo -e " ${RED}${BOLD}TESTS FAILED${RESET}"
|
|
echo -e " Check results: open $RESULTS_DIR/"
|
|
exit 1
|
|
fi
|