From 4b8d857e7bc4c6bb230c4ba1577b945d055a5a95 Mon Sep 17 00:00:00 2001 From: Samiran Saha Date: Wed, 12 Nov 2025 13:57:04 +0530 Subject: [PATCH 01/17] restructured logic with further logic improvements --- mac/common-utils.sh | 377 +++++++++ mac/device-machine-allocation.sh | 161 ++++ mac/env-prequisite-checks.sh | 157 ++++ mac/env-setup-run.sh | 493 ++++++++++++ mac/logging-utils.sh | 31 + mac/proxy-check.sh | 56 -- mac/run.sh | 1286 ++---------------------------- mac/user-interaction.sh | 111 +++ 8 files changed, 1384 insertions(+), 1288 deletions(-) create mode 100644 mac/common-utils.sh create mode 100644 mac/device-machine-allocation.sh create mode 100755 mac/env-prequisite-checks.sh create mode 100644 mac/env-setup-run.sh create mode 100644 mac/logging-utils.sh delete mode 100755 mac/proxy-check.sh create mode 100644 mac/user-interaction.sh diff --git a/mac/common-utils.sh b/mac/common-utils.sh new file mode 100644 index 0000000..40a2d95 --- /dev/null +++ b/mac/common-utils.sh @@ -0,0 +1,377 @@ +#!/bin/bash + +source "$(dirname "$0")/device-machine-allocation.sh" + +# # ===== Global Variables ===== +WORKSPACE_DIR="$HOME/.browserstack" +PROJECT_FOLDER="NOW" + +# URL handling +DEFAULT_TEST_URL="https://bstackdemo.com" + +# ===== Log files (per-run) ===== +LOG_DIR="$WORKSPACE_DIR/$PROJECT_FOLDER/logs" +NOW_RUN_LOG_FILE="" + +# ===== Global Variables ===== +USERNAME="" +ACCESS_KEY="" +TEST_TYPE="" # Web / App / Both +TECH_STACK="" # Java / Python / JS +CX_TEST_URL="$DEFAULT_TEST_URL" + +WEB_PLAN_FETCHED=false +MOBILE_PLAN_FETCHED=false +TEAM_PARALLELS_MAX_ALLOWED_WEB=0 +TEAM_PARALLELS_MAX_ALLOWED_MOBILE=0 + +# App specific globals +APP_URL="" +APP_PLATFORM="" # ios | android | all + + +# ===== Logging Functions ===== +log_msg_to() { + local message="$1" + local dest_file=$NOW_RUN_LOG_FILE + local ts + ts="$(date +"%Y-%m-%d %H:%M:%S")" + local line="[$ts] $message" + + # print to console + if [[ "$RUN_MODE" == *"--debug"* ]]; then + echo "$line" + fi + + # write to dest file if provided + if [ -n "$dest_file" ]; then + mkdir -p "$(dirname "$dest_file")" + echo "$line" >> $NOW_RUN_LOG_FILE + fi +} + +# Spinner function for long-running processes +show_spinner() { + local pid=$1 + local spin='|/-\' + local i=0 + local ts + ts="$(date +"%Y-%m-%d %H:%M:%S")" + while kill -0 "$pid" 2>/dev/null; do + i=$(( (i+1) %4 )) + printf "\r⏳ Processing... ${spin:$i:1}" + sleep 0.1 + done + echo "" + log_info "Run Test command completed." + sleep 5 + #log_msg_to "✅ Done!" +} + +# ===== Workspace Management ===== +setup_workspace() { + log_section "⚙️ Environment & Credentials" + local full_path="$WORKSPACE_DIR/$PROJECT_FOLDER" + if [ ! -d "$full_path" ]; then + mkdir -p "$full_path" + log_info "Created onboarding workspace: $full_path" + else + log_success "Onboarding workspace found at: $full_path" + fi +} + + +# ===== App Upload Management ===== +handle_app_upload() { + local app_platform="" + if [[ "$RUN_MODE" == *"--silent"* || "$RUN_MODE" == *"--debug"* ]]; then + upload_sample_app + app_platform="android" + export APP_PLATFORM="$app_platform" + log_msg_to "Exported APP_PLATFORM=$APP_PLATFORM" + else + local choice + choice=$(osascript -e ' + display dialog "How would you like to select your app?" ¬ + with title "BrowserStack App Upload" ¬ + with icon note ¬ + buttons {"Use Sample App", "Upload my App (.apk/.ipa)", "Cancel"} ¬ + default button "Upload my App (.apk/.ipa)" + ' 2>/dev/null) + + if [[ "$choice" == *"Use Sample App"* ]]; then + upload_sample_app + app_platform="android" + export APP_PLATFORM="$app_platform" + log_msg_to "Exported APP_PLATFORM=$APP_PLATFORM" + elif [[ "$choice" == *"Upload my App"* ]]; then + upload_custom_app + else + return 1 + fi + fi +} + +upload_sample_app() { + log_msg_to "⬆️ Uploading sample app to BrowserStack..." + local upload_response + upload_response=$(curl -s -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" \ + -X POST "https://api-cloud.browserstack.com/app-automate/upload" \ + -F "url=https://www.browserstack.com/app-automate/sample-apps/android/WikipediaSample.apk") + + app_url=$(echo "$upload_response" | grep -o '"app_url":"[^"]*' | cut -d'"' -f4) + export BROWSERSTACK_APP=$app_url + log_msg_to "Exported BROWSERSTACK_APP=$BROWSERSTACK_APP" + + if [ -z "$app_url" ]; then + log_msg_to "❌ Upload failed. Response: $UPLOAD_RESPONSE" + return 1 + fi + + log_msg_to "✅ App uploaded successfully: $app_url" + return 0 +} + +upload_custom_app() { + local -n app_url=$1 + local -n platform=$2 + local app_platform="" + local file_path + file_path=$(osascript -e 'choose file with prompt "Select your .apk or .ipa file:" of type {"apk", "ipa"}' 2>/dev/null) + + if [ -z "$file_path" ]; then + log_msg_to "❌ No file selected" + return 1 + fi + + # Determine platform from file extension + if [[ "$file_path" == *.ipa ]]; then + platform="ios" + app_platform="ios" + elif [[ "$file_path" == *.apk ]]; then + platform="android" + app_platform="android" + else + log_msg_to "❌ Invalid file type. Must be .apk or .ipa" + return 1 + fi + + log_msg_to "⬆️ Uploading app to BrowserStack..." + local upload_response + upload_response=$(curl -s -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" \ + -X POST "https://api-cloud.browserstack.com/app-automate/upload" \ + -F "file=@$file_path") + + app_url=$(echo "$upload_response" | grep -o '"app_url":"[^"]*' | cut -d'"' -f4) + if [ -z "$app_url" ]; then + log_msg_to "❌ Failed to upload app" + return 1 + fi + + export BROWSERSTACK_APP=$app_url + log_msg_to "✅ App uploaded successfully" + log_msg_to "Exported BROWSERSTACK_APP=$BROWSERSTACK_APP" + + export APP_PLATFORM="$app_platform" + log_msg_to "Exported APP_PLATFORM=$APP_PLATFORM" + return 0 +} + +# ===== Dynamic config generators ===== +generate_web_platforms() { + local max_total_parallels=$1 + local platformsListContentFormat=$2 + local platform="web" + export NOW_PLATFORM="$platform" + local platformsList=$(pick_terminal_devices "$NOW_PLATFORM" $max_total_parallels "$platformsListContentFormat") + echo "$platformsList" +} + +generate_mobile_platforms() { + local max_total_parallels=$1 + local platformsListContentFormat=$2 + local app_platform="$APP_PLATFORM" + local platformsList=$(pick_terminal_devices "$app_platform" $max_total_parallels, "$platformsListContentFormat") + echo "$platformsList" +} + + +# ===== Fetch plan details (writes to GLOBAL) ===== +fetch_plan_details() { + local test_type=$1 + + log_section "☁️ Account & Plan Details" + log_info "Fetching BrowserStack plan for $test_type" + local web_unauthorized=false + local mobile_unauthorized=false + + if [[ "$test_type" == "web" || "$test_type" == "both" ]]; then + RESPONSE_WEB=$(curl -s -w "\n%{http_code}" -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" https://api.browserstack.com/automate/plan.json) + HTTP_CODE_WEB=$(echo "$RESPONSE_WEB" | tail -n1) + RESPONSE_WEB_BODY=$(echo "$RESPONSE_WEB" | sed '$d') + if [ "$HTTP_CODE_WEB" == "200" ]; then + WEB_PLAN_FETCHED=true + TEAM_PARALLELS_MAX_ALLOWED_WEB=$(echo "$RESPONSE_WEB_BODY" | grep -o '"parallel_sessions_max_allowed":[0-9]*' | grep -o '[0-9]*') + export TEAM_PARALLELS_MAX_ALLOWED_WEB="$TEAM_PARALLELS_MAX_ALLOWED_WEB" + log_msg_to "✅ Web Testing Plan fetched: Team max parallel sessions = $TEAM_PARALLELS_MAX_ALLOWED_WEB" + else + log_msg_to "❌ Web Testing Plan fetch failed ($HTTP_CODE_WEB)" + [ "$HTTP_CODE_WEB" == "401" ] && web_unauthorized=true + fi + fi + + if [[ "$test_type" == "app" || "$test_type" == "both" ]]; then + RESPONSE_MOBILE=$(curl -s -w "\n%{http_code}" -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" https://api-cloud.browserstack.com/app-automate/plan.json) + HTTP_CODE_MOBILE=$(echo "$RESPONSE_MOBILE" | tail -n1) + RESPONSE_MOBILE_BODY=$(echo "$RESPONSE_MOBILE" | sed '$d') + if [ "$HTTP_CODE_MOBILE" == "200" ]; then + MOBILE_PLAN_FETCHED=true + TEAM_PARALLELS_MAX_ALLOWED_MOBILE=$(echo "$RESPONSE_MOBILE_BODY" | grep -o '"parallel_sessions_max_allowed":[0-9]*' | grep -o '[0-9]*') + export TEAM_PARALLELS_MAX_ALLOWED_MOBILE="$TEAM_PARALLELS_MAX_ALLOWED_MOBILE" + log_msg_to "✅ Mobile App Testing Plan fetched: Team max parallel sessions = $TEAM_PARALLELS_MAX_ALLOWED_MOBILE" + else + log_msg_to "❌ Mobile App Testing Plan fetch failed ($HTTP_CODE_MOBILE)" + [ "$HTTP_CODE_MOBILE" == "401" ] && mobile_unauthorized=true + fi + fi + + log_info "Plan summary: Web $WEB_PLAN_FETCHED ($TEAM_PARALLELS_MAX_ALLOWED_WEB max), Mobile $MOBILE_PLAN_FETCHED ($TEAM_PARALLELS_MAX_ALLOWED_MOBILE max)" + + if [[ "$test_type" == "web" && "$web_unauthorized" == true ]] || \ + [[ "$test_type" == "app" && "$mobile_unauthorized" == true ]] || \ + [[ "$test_type" == "both" && "$web_unauthorized" == true && "$mobile_unauthorized" == true ]]; then + log_msg_to "❌ Unauthorized to fetch required plan(s). Exiting." + exit 1 + fi +} + +# Function to check if IP is private +is_private_ip() { + case $1 in + 10.* | 192.168.* | 172.16.* | 172.17.* | 172.18.* | 172.19.* | \ + 172.20.* | 172.21.* | 172.22.* | 172.23.* | 172.24.* | 172.25.* | \ + 172.26.* | 172.27.* | 172.28.* | 172.29.* | 172.30.* | 172.31.* | "") + return 0 ;; # Private + *) + return 1 ;; # Public + esac +} + +is_domain_private() { + domain=${CX_TEST_URL#*://} # remove protocol + domain=${domain%%/*} # remove everything after first "/" + log_msg_to "Website domain: $domain" + export NOW_WEB_DOMAIN="$CX_TEST_URL" + + # Resolve domain using Cloudflare DNS + IP_ADDRESS=$(dig +short "$domain" @1.1.1.1 | head -n1) + + # Determine if domain is private + if is_private_ip "$IP_ADDRESS"; then + is_cx_domain_private=0 + else + is_cx_domain_private=-1 + fi + + log_msg_to "Resolved IPs: $IP_ADDRESS" + + return $is_cx_domain_private +} + + +identify_run_status_java() { + local log_file=$1 + log_section "✅ Results" + + # Extract the test summary line + local line=$(grep -m 2 -E "[INFO|ERROR].*Tests run" < "$log_file") + # If not found, fail + if [[ -z "$line" ]]; then + log_warn "❌ No test summary line found." + return 1 + fi + + # Extract numbers using regex + tests_run=$(echo "$line" | grep -m 1 -oE "Tests run: [0-9]+" | awk '{print $3}') + failures=$(echo "$line" | grep -m 1 -oE "Failures: [0-9]+" | awk '{print $2}') + errors=$(echo "$line" | grep -m 1 -oE "Errors: [0-9]+" | awk '{print $2}') + skipped=$(echo "$line" | grep -m 1 -oE "Skipped: [0-9]+" | awk '{print $2}') + + # Calculate passed tests + passed=$(( $tests_run - ($failures + $errors + $skipped) )) + + # Check condition + if (( passed > 0 )); then + log_success "Success: $passed test(s) passed." + return 0 + else + log_error "Error: No tests passed (Tests run: $tests_run, Failures: $failures, Errors: $errors, Skipped: $skipped)" + return 1 + fi + + return 1 +} + + +identify_run_status_nodejs() { + + local log_file=$1 + log_info "Identifying run status" + local line=$(grep -m 1 -E "Spec Files:.*passed.*total" < "$log_file") + # If not found, fail + if [[ -z "$line" ]]; then + log_warn "❌ No test summary line found." + return 1 + fi + + # Extract numbers using regex + passed=$(echo "$line" | grep -oE '[0-9]+ passed' | awk '{print $1}') + # Check condition + if (( passed > 0 )); then + log_success "Success: $passed test(s) passed" + return 0 + else + log_error "❌ Error: No tests passed" + return 1 + fi + + return 1 +} + + +identify_run_status_python() { + + local log_file=$1 + log_info "Identifying run status" + + # Extract numbers and sum them + passed_sum=$(grep -oE '[0-9]+ passed' "$log_file" | awk '{sum += $1} END {print sum+0}') + + echo "✅ Total Passed: $passed_sum" + + local completed_test_count=passed_sum+warning_sum + + # If not found, fail + if [[ -z "$passed_sum" ]]; then + log_warn "❌ No test summary line found." + return 1 + fi + + # Check condition + if (( passed_sum > 0 )); then + log_success "Success: $passed_sum test(s) completed" + return 0 + else + log_error "❌ Error: No tests completed" + return 1 + fi + + return 1 +} + +clear_old_logs() { + mkdir -p "$LOG_DIR" + : > "$NOW_RUN_LOG_FILE" + + log_success "Logs cleared and fresh run initiated." +} diff --git a/mac/device-machine-allocation.sh b/mac/device-machine-allocation.sh new file mode 100644 index 0000000..0338994 --- /dev/null +++ b/mac/device-machine-allocation.sh @@ -0,0 +1,161 @@ +#!/bin/bash + +# --------------------------- +# Usage: ./pick_devices.sh +# Example: ./pick_devices.sh android 3 +# --------------------------- + + +# Define array of devices +MOBILE_ALL=( + # Tier 1 + "ios|iPhone 1[234567]*" + "android|Samsung Galaxy S*" + + # Tier 2 + "ios|iPad Air*" + "android|Samsung Galaxy Tab*" + "android|Samsung Galaxy M*" + "android|Google Pixel [56789]*" + "android|Vivo Y*" + "android|Oppo*" + + # Tier 4 + "ios|iPad Pro*" + "android|Samsung Galaxy A*" + "android|Google Pixel 10*" + "android|OnePlus *" + "android|Vivo V*" + "android|Xiaomi *" + "android|Huawei *" +) + +WEB_ALL=( + "Windows|Chrome" + "Windows|Firefox" + "Windows|Edge" + "Windows|Chrome" + "Windows|Chrome" + "OS X|Chrome" + "OS X|Safari" + "OS X|Chrome" + "OS X|Safari" + "OS X|Firefox" + "OS X|Safari" + # Tier 1 + "ios|iPhone 1[234567]*" + "android|Samsung Galaxy S*" + + # Tier 2 + "ios|iPad Air*" + "android|Samsung Galaxy Tab*" + "android|Samsung Galaxy M*" + "android|Google Pixel [56789]*" + "android|Vivo Y*" + "android|Oppo*" + + # Tier 4 + "ios|iPhone SE*" + "ios|iPad Pro*" + "android|Samsung Galaxy A*" + "android|Google Pixel 10*" + "android|OnePlus *" + "android|Vivo V*" + "android|Xiaomi *" + "android|Huawei *" +) + + + +pick_terminal_devices() { + local platformName="$1" + local count=$2 + count="${count%,}" # remove trailing comma if present + local platformsListContentFormat="$3" + + # --------------------------- + # Check for valid input + # --------------------------- + if [[ -z "$platformName" || -z "$count" ]]; then + log_msg_to "Platform name for parallel count is invalid: $0 $platformName $count" + return 1 + fi + + # Validate count is a number + if ! [[ "$count" =~ ^[0-9]+$ ]]; then + log_msg_to "Error: count must be a number." + return 1 + fi + + # --------------------------- + # Filter and store matching entries + # --------------------------- + matching_devices=() + + if [[ "$platformName" == "android" || "$platformName" == "ios" ]]; then + for entry in "${MOBILE_ALL[@]}"; do + prefix="${entry%%|*}" # text before '|' + if [[ "$prefix" == "$platformName" ]]; then + matching_devices+=("$entry") + fi + done + else + for entry in "${WEB_ALL[@]}"; do + matching_devices+=("$entry") + done + fi + + # --------------------------- + # Loop as many times as 'count' + # --------------------------- + local yaml="" + local json="[" + + for ((i = 1; i <= count; i++)); do + index=$(( (i - 1) % ${#matching_devices[@]} )) + entry="${matching_devices[$index]}" + suffixEntry="${entry#*|}" + prefixEntry="${entry%%|*}" + + if [[ "$platformsListContentFormat" == "yaml" ]]; then + if [[ "$prefixEntry" == "android" || "$prefixEntry" == "ios" ]]; then + yaml+=" - platformName: $prefixEntry + deviceName: $suffixEntry +" + else + yaml+=" - osVersion: $prefixEntry + browserName: $suffixEntry + browserVersion: latest +" + fi + + # Add comma-like separator logic here only if needed + if [[ $i -lt $count ]]; then + yaml+=$'\n' + fi + + elif [[ "$platformsListContentFormat" == "json" ]]; then + # JSON mode + if [[ "$prefixEntry" == "android" || "$prefixEntry" == "ios" ]]; then + json+=$'{"platformName": "'"$prefixEntry"'","bstack:options":{"deviceName": "'"$suffixEntry"'"}},' + else + json+=$'{"bstack:options":{ "os": "'"$prefixEntry"'"},"browserName": "'"$suffixEntry"'","browserVersion": "latest"},' + fi + + # Stop if max reached + if [[ -n "$max_total" && $i -ge $max_total ]]; then + break + fi + fi + done + + # Close JSON array + json="${json%,}]" + + # Output based on requested format + if [[ "$platformsListContentFormat" == "yaml" ]]; then + echo "$yaml" + else + echo "$json" + fi +} \ No newline at end of file diff --git a/mac/env-prequisite-checks.sh b/mac/env-prequisite-checks.sh new file mode 100755 index 0000000..0923392 --- /dev/null +++ b/mac/env-prequisite-checks.sh @@ -0,0 +1,157 @@ +#!/bin/bash + +# URL to test +PROXY_TEST_URL="https://www.browserstack.com/automate/browsers.json" + +# Detect proxy from env (case insensitive) +PROXY="${http_proxy:-${HTTP_PROXY:-${https_proxy:-${HTTPS_PROXY}}}}" + +# Reset output variables +export PROXY_HOST="" +export PROXY_PORT="" + +# Function: parse proxy url to host + port +parse_proxy() { + p="$1" + # strip protocol e.g. http://, https:// + p="${p#http://}" + p="${p#https://}" + # strip credentials if any user:pass@ + p="${p#*[@]}" + + # extract host and port + export PROXY_HOST="${p%%:*}" + export PROXY_PORT="${p##*:}" +} + +set_proxy_in_env() { + log_section "🌐 Network & Proxy Validation" + base64_encoded_creds=$(printf "%s" $BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY | base64 | tr -d '\n') + + + # If no proxy configured, exit early + if [ -z "$PROXY" ]; then + log_warn "No proxy found. Using direct connection." + export PROXY_HOST="" + export PROXY_PORT="" + return 0 2>/dev/null || exit 0 + fi + + log_msg_to "Proxy detected: $PROXY" + parse_proxy "$PROXY" + + log_msg_to "Testing reachability via proxy..." + + + STATUS_CODE=$(curl -sS -o /dev/null -H "Authorization: Basic ${base64_encoded_creds}" -w "%{http_code}" --proxy "$PROXY" "$PROXY_TEST_URL" 2>/dev/null) + + if [ "${STATUS_CODE#2}" != "$STATUS_CODE" ]; then + log_msg_to "✅ Reachable. HTTP $STATUS_CODE" + log_msg_to "Exporting PROXY_HOST=$PROXY_HOST" + log_msg_to "Exporting PROXY_PORT=$PROXY_PORT" + export PROXY_HOST + export PROXY_PORT + log_success "Connected to BrowserStack from proxy: $PROXY_HOST:$PROXY_PORT" + else + log_warn "⚠️ Could not connect to BrowserStack using proxy. Using direct connection." + log_msg_to "❌ Not reachable (HTTP $STATUS_CODE). Clearing variables." + export PROXY_HOST="" + export PROXY_PORT="" + fi +} + + +# ===== Tech Stack Validation Functions ===== +check_java_installation() { + log_msg_to "🔍 Checking if 'java' command exists..." + if ! command -v java >/dev/null 2>&1; then + log_msg_to "❌ Java command not found in PATH." + return 1 + fi + + log_msg_to "🔍 Checking if Java runs correctly..." + if ! JAVA_VERSION_OUTPUT=$(java -version 2>&1); then + log_msg_to "❌ Java exists but failed to run." + return 1 + fi + + log_success "Java installed and functional\n$JAVA_VERSION_OUTPUT" + #log_msg_to "$JAVA_VERSION_OUTPUT" | while read -r l; do log_msg_to " $l" ; done + return 0 +} + +check_python_installation() { + log_msg_to "🔍 Checking if 'python3' command exists..." + if ! command -v python3 >/dev/null 2>&1; then + log_msg_to "❌ Python3 command not found in PATH." + return 1 + fi + + log_msg_to "🔍 Checking if Python3 runs correctly..." + if ! PYTHON_VERSION_OUTPUT=$(python3 --version 2>&1); then + log_msg_to "❌ Python3 exists but failed to run." + return 1 + fi + + log_success "Python3 default installation: $PYTHON_VERSION_OUTPUT" + return 0 +} + +check_nodejs_installation() { + log_msg_to "🔍 Checking if 'node' command exists..." + if ! command -v node >/dev/null 2>&1; then + log_msg_to "❌ Node.js command not found in PATH." + return 1 + fi + + log_msg_to "🔍 Checking if 'npm' command exists..." + if ! command -v npm >/dev/null 2>&1; then + log_msg_to "❌ npm command not found in PATH." + return 1 + fi + + log_msg_to "🔍 Checking if Node.js runs correctly..." + if ! NODE_VERSION_OUTPUT=$(node -v 2>&1); then + log_msg_to "❌ Node.js exists but failed to run." + return 1 + fi + + log_msg_to "🔍 Checking if npm runs correctly..." + if ! NPM_VERSION_OUTPUT=$(npm -v 2>&1); then + log_msg_to "❌ npm exists but failed to run." + return 1 + fi + + log_success "Node.js installed: $NODE_VERSION_OUTPUT" + log_success "npm installed: $NPM_VERSION_OUTPUT" + return 0 +} + +validate_tech_stack_installed() { + local tech_stack=$1 + + log_section "🧩 System Prerequisites Check" + log_info "Checking prerequisites for $tech_stack" + + case "$tech_stack" in + java) + check_java_installation + ;; + python) + check_python_installation + ;; + nodejs) + check_nodejs_installation + ;; + *) + log_msg_to "❌ Unknown tech stack selected: $tech_stack" + return 1 + ;; + esac + + log_msg_to "✅ Prerequisites validated for $tech_stack" + return 0 +} + + + diff --git a/mac/env-setup-run.sh b/mac/env-setup-run.sh new file mode 100644 index 0000000..c26c3f8 --- /dev/null +++ b/mac/env-setup-run.sh @@ -0,0 +1,493 @@ +# Common setup function for both web and mobile +setup_environment() { + local setup_type=$1 # "web" or "mobile" + local tech_stack=$2 + local max_parallels + local setup_function + + log_section "📦 Project Setup" + + # Set variables based on setup type + if [ "$setup_type" = "web" ]; then + log_msg_to "Team max parallels for web: $TEAM_PARALLELS_MAX_ALLOWED_WEB" "$NOW_RUN_LOG_FILE" + max_parallels=$TEAM_PARALLELS_MAX_ALLOWED_WEB + else + log_msg_to "Team max parallels for mobile: $TEAM_PARALLELS_MAX_ALLOWED_MOBILE" "$NOW_RUN_LOG_FILE" + max_parallels=$TEAM_PARALLELS_MAX_ALLOWED_MOBILE + fi + + log_msg_to "Starting ${setup_type} setup for $TECH_STACK" "$NOW_RUN_LOG_FILE" + + local local_flag=false + local attempt=1 + local run_status=1 + + # Calculate parallels + local total_parallels + total_parallels=$(echo "$max_parallels" | bc | cut -d'.' -f1) + [ -z "$total_parallels" ] && total_parallels=1 + local parallels_per_platform=$total_parallels + + log_msg_to "[${setup_type} Setup]" "$NOW_RUN_LOG_FILE" + log_msg_to "Total parallels allocated: $total_parallels" "$NOW_RUN_LOG_FILE" + + + case "$TECH_STACK" in + java) + "setup_${setup_type}_java" "$local_flag" "$parallels_per_platform" "$NOW_RUN_LOG_FILE" + log_section "✅ Results" + run_status=$(identify_run_status_java "$NOW_RUN_LOG_FILE") + log_info "Fetching results..." + check_return_value $? "$NOW_RUN_LOG_FILE" "${setup_type} setup succeeded." "❌ ${setup_type} setup failed. Check $log_file for details" + ;; + python) + "setup_${setup_type}_python" "$local_flag" "$parallels_per_platform" "$NOW_RUN_LOG_FILE" + log_section "✅ Results" + run_status=$(identify_run_status_python "$NOW_RUN_LOG_FILE") + log_info "Fetching results..." + check_return_value $? "$NOW_RUN_LOG_FILE" "${setup_type} setup succeeded." "❌ ${setup_type} setup failed. Check $log_file for details" + ;; + nodejs) + "setup_${setup_type}_nodejs" "$local_flag" "$parallels_per_platform" "$NOW_RUN_LOG_FILE" + log_section "✅ Results" + run_status=$(identify_run_status_nodejs "$NOW_RUN_LOG_FILE") + log_info "Fetching results..." + check_return_value $? "$NOW_RUN_LOG_FILE" "${setup_type} setup succeeded." "❌ ${setup_type} setup failed. Check $log_file for details" + ;; + *) + log_warn "Unknown TECH_STACK: $TECH_STACK" "$NOW_RUN_LOG_FILE" + return 1 + ;; + esac +} + +setup_web_java() { + local local_flag=$1 + local parallels=$2 + + REPO="now-testng-browserstack" + TARGET_DIR="$WORKSPACE_DIR/$PROJECT_FOLDER/$REPO" + + mkdir -p "$WORKSPACE_DIR/$PROJECT_FOLDER" + + clone_repository $REPO $TARGET_DIR + + cd "$TARGET_DIR"|| return 1 + + log_info "Target website: $CX_TEST_URL" + + if is_domain_private; then + local_flag=true + fi + + export BROWSERSTACK_LOCAL=$local_flag + + report_bstack_local_status "$local_flag" + + # === 5️⃣ YAML Setup === + log_msg_to "🧩 Generating YAML config (bstack.yml)" + + + # YAML config path + export BROWSERSTACK_CONFIG_FILE="./browserstack.yml" + platform_yaml=$(generate_web_platforms "$TEAM_PARALLELS_MAX_ALLOWED_WEB", "yaml") + + cat >> "$BROWSERSTACK_CONFIG_FILE" <> $NOW_RUN_LOG_FILE 2>&1 || return 1 + log_success "Dependencies installed" + + print_tests_running_log_section "mvn test -P sample-test" + log_msg_to "🚀 Running 'mvn test -P sample-test'. This could take a few minutes. Follow the Automaton build here: https://automation.browserstack.com/" + mvn test -P sample-test >> $NOW_RUN_LOG_FILE 2>&1 & + cmd_pid=$!|| return 1 + + show_spinner "$cmd_pid" + wait "$cmd_pid" + + cd "$WORKSPACE_DIR/$PROJECT_FOLDER" + return 0 +} + +setup_app_java() { + local local_flag=$1 + local parallels=$2 + local log_file=$3 + + REPO="now-testng-appium-app-browserstack" + TARGET_DIR="$WORKSPACE_DIR/$PROJECT_FOLDER/$REPO" + local app_url=$BROWSERSTACK_APP + log_msg_to "APP_PLATFORM: $APP_PLATFORM" >> "$NOW_RUN_LOG_FILE" 2>&1 + + clone_repository $REPO $TARGET_DIR + + if [[ "$APP_PLATFORM" == "all" || "$APP_PLATFORM" == "android" ]]; then + cd android/testng-examples + else + cd ios/testng-examples + fi + + + # YAML config path + export BROWSERSTACK_CONFIG_FILE="./browserstack.yml" + platform_yaml=$(generate_mobile_platforms $TEAM_PARALLELS_MAX_ALLOWED_MOBILE "yaml") + + cat >> "$BROWSERSTACK_CONFIG_FILE" <> $NOW_RUN_LOG_FILE 2>&1; then + log_msg_to "❌ 'mvn clean' FAILED. See $log_file for details." + return 1 # Fail the function if clean fails + fi + log_success "Dependencies installed" + + log_msg_to "🚀 Running 'mvn test -P sample-test'. This could take a few minutes. Follow the Automaton build here: https://automation.browserstack.com/" + print_tests_running_log_section "mvn test -P sample-test" + mvn test -P sample-test >> $NOW_RUN_LOG_FILE 2>&1 & + cmd_pid=$!|| return 1 + + show_spinner "$cmd_pid" + wait "$cmd_pid" + + cd "$WORKSPACE_DIR/$PROJECT_FOLDER" + return 0 +} + +setup_web_python() { + local local_flag=$1 + local parallels=$2 + local log_file=$3 + + REPO="browserstack-examples-pytest" + TARGET_DIR="$WORKSPACE_DIR/$PROJECT_FOLDER/$REPO" + + clone_repository $REPO $TARGET_DIR + + detect_setup_python_env + + pip3 install -r requirements.txt >> $NOW_RUN_LOG_FILE 2>&1 + log_success "Dependencies installed" + + # Update YAML at root level (browserstack.yml) + export BROWSERSTACK_CONFIG_FILE="./src/conf/browserstack_parallel.yml" + platform_yaml=$(generate_web_platforms "$TEAM_PARALLELS_MAX_ALLOWED_WEB" "yaml") + + cat >> "$BROWSERSTACK_CONFIG_FILE" <> $NOW_RUN_LOG_FILE 2>&1 + # & + # cmd_pid=$!|| return 1 + + # show_spinner "$cmd_pid" + # wait "$cmd_pid" + + cd "$WORKSPACE_DIR/$PROJECT_FOLDER" + return 0 +} + +setup_app_python() { + local local_flag=$1 + local parallels=$2 + local log_file=$3 + + REPO="now-pytest-appium-app-browserstack" + TARGET_DIR="$WORKSPACE_DIR/$PROJECT_FOLDER/$REPO" + + clone_repository $REPO $TARGET_DIR + + detect_setup_python_env + + # Install dependencies + pip install -r requirements.txt >> "$NOW_RUN_LOG_FILE" 2>&1 + log_success "Dependencies installed" + + + # Prepare platform-specific YAMLs in android/ and ios/ + local original_platform="$APP_PLATFORM" + + local app_url=$BROWSERSTACK_APP + local platform_yaml + + export BSTACK_PARALLELS=1 + export BROWSERSTACK_CONFIG_FILE="./android/browserstack.yml" + platform_yaml=$(generate_mobile_platforms $TEAM_PARALLELS_MAX_ALLOWED_MOBILE "yaml") + + cat >> "$BROWSERSTACK_CONFIG_FILE" <> "$NOW_RUN_LOG_FILE" 2>&1 || return 1 + ) + + deactivate + cd "$WORKSPACE_DIR/$PROJECT_FOLDER" + return 0 +} + +setup_web_nodejs() { + local local_flag=$1 + local parallels=$2 + + REPO="now-webdriverio-browserstack" + TARGET_DIR="$WORKSPACE_DIR/$PROJECT_FOLDER/$REPO" + mkdir -p "$WORKSPACE_DIR/$PROJECT_FOLDER" + + clone_repository $REPO $TARGET_DIR + + + # === 2️⃣ Install Dependencies === + log_msg_to "⚙️ Running 'npm install'" + log_info "Installing dependencies" + npm install >> $NOW_RUN_LOG_FILE 2>&1 || return 1 + log_success "Dependencies installed" + + local caps_json="" + # === 4️⃣ Generate Capabilities JSON === + caps_json=$(generate_web_platforms "$parallels" "json") + export BSTACK_CAPS_JSON=$caps_json + export BSTACK_PARALLELS=$parallels + + if is_domain_private; then + local_flag=true + fi + + export BROWSERSTACK_LOCAL=$local_flag + + report_bstack_local_status "$local_flag" + + + + # === 8️⃣ Run Tests === + log_msg_to "🚀 Running 'npm run test'. This could take a few minutes. Follow the Automaton build here: https://automation.browserstack.com/" + print_tests_running_log_section "npm run test" + npm run test >> $NOW_RUN_LOG_FILE 2>&1 || return 1 & + cmd_pid=$!|| return 1 + + show_spinner "$cmd_pid" + wait "$cmd_pid" + + cd "$WORKSPACE_DIR/$PROJECT_FOLDER" + return 0 +} + +setup_app_nodejs() { + local local_flag=$1 + local parallels=$2 + local log_file=$3 + local caps_json="" + + log_msg_to "Starting Mobile NodeJS setup with parallels: $parallels" >> "$NOW_RUN_LOG_FILE" 2>&1 + mkdir -p "$WORKSPACE_DIR/$PROJECT_FOLDER" + REPO="now-webdriverio-appium-app-browserstack" + TARGET_DIR="$WORKSPACE_DIR/$PROJECT_FOLDER/$REPO" + $TEST_FOLDER="/test" + + clone_repository $REPO $TARGET_DIR $TEST_FOLDER + + # === 2️⃣ Install Dependencies === + log_info "Installing dependencies" + log_msg_to "⚙️ Running 'npm install'" + npm install >> $NOW_RUN_LOG_FILE 2>&1 || return 1 + log_success "Dependencies installed" + + caps_json=$(generate_mobile_platforms $TEAM_PARALLELS_MAX_ALLOWED_MOBILE "json") + + + export BSTACK_CAPS_JSON=$caps_json + + local app_url=$BROWSERSTACK_APP + + export BSTACK_PARALLELS=$parallels + export BROWSERSTACK_LOCAL=true + export BROWSERSTACK_APP=$app_url + + # === 8️⃣ Run Tests === + log_msg_to "🚀 Running 'npm run test'. This could take a few minutes. Follow the Automaton build here: https://automation.browserstack.com/" + print_tests_running_log_section "npm run test" + npm run test >> $NOW_RUN_LOG_FILE 2>&1 || return 1 & + cmd_pid=$!|| return 1 + + show_spinner "$cmd_pid" + wait "$cmd_pid" + + # === 9️⃣ Wrap Up === + log_msg_to "✅ Mobile JS setup and test execution completed successfully." + + cd "$WORKSPACE_DIR/$PROJECT_FOLDER" + return 0 +} + +clone_repository() { + repo_git=$1 + install_folder=$2 + test_folder=$3 + + rm -rf $install_folder + log_msg_to "📦 Cloning repo $repo_git into $install_folder" + log_info "Cloning repository: $repo_git" + git clone https://github.com/BrowserStackCE/$repo_git.git "$install_folder" >> $NOW_RUN_LOG_FILE 2>&1 || return 1 + log_msg_to "✅ Cloned repository: $repo_git into $install_folder" + cd "$install_folder/$test_folder" || return 1 +} + +# ===== Orchestration: decide what to run based on TEST_TYPE and plan fetch ===== +run_setup_wrapper() { + local test_type=$1 + local tech_stack=$2 + log_msg_to "Orchestration: TEST_TYPE=$test_type, WEB_PLAN_FETCHED=$WEB_PLAN_FETCHED, MOBILE_PLAN_FETCHED=$MOBILE_PLAN_FETCHED" + + case "$test_type" in + Web) + if [ "$WEB_PLAN_FETCHED" == true ]; then + run_setup $test_type $tech_stack + else + log_msg_to "⚠️ Skipping Web setup — Web plan not fetched" + fi + ;; + App) + if [ "$MOBILE_PLAN_FETCHED" == true ]; then + run_setup $test_type $tech_stack + else + log_msg_to "⚠️ Skipping Mobile setup — Mobile plan not fetched" + fi + ;; + Both) + local ran_any=false + if [ "$WEB_PLAN_FETCHED" == true ]; then + run_setup $test_type $tech_stack + ran_any=true + else + log_msg_to "⚠️ Skipping Web setup — Web plan not fetched" + fi + if [ "$MOBILE_PLAN_FETCHED" == true ]; then + run_setup $test_type $tech_stack + ran_any=true + else + log_msg_to "⚠️ Skipping Mobile setup — Mobile plan not fetched" + fi + if [ "$ran_any" == false ]; then + log_msg_to "❌ Both Web and Mobile setup were skipped. Exiting." + exit 1 + fi + ;; + *) + log_msg_to "❌ Invalid TEST_TYPE: $TEST_TYPE" + exit 1 + ;; + esac +} + +check_return_value() { + local return_value=$1 + local log_file=$2 + local success_message=$3 + local failure_message=$4 + + if [ "$return_value" -eq 0 ]; then + log_success "$success_message" "$NOW_RUN_LOG_FILE" + exit 0 + else + log_error "$failure_message" "$NOW_RUN_LOG_FILE" + exit 1 + fi +} + + +report_bstack_local_status() { + if [ "$local_flag" = "true" ]; then + log_msg_to "✅ BrowserStack Local is ENABLED for this run." + log_success "Target website is behind firewall. BrowserStack Local enabled for this run." + else + log_msg_to "✅ BrowserStack Local is DISABLED for this run." + log_success "Target website is publicly resolvable. BrowserStack Local disabled for this run." + fi +} + +print_tests_running_log_section() { + log_section "🚀 Running Tests: $1" + log_info "Executing: Test run command. This could take a few minutes..." + log_info "You can monitor test progress here: 🔗 https://automation.browserstack.com/" +} + + +detect_setup_python_env() { + log_info "Detecting latest Python environment" + + latest_python=$( + { ls -1 /usr/local/bin/python3.[0-9]* /usr/bin/python3.[0-9]* 2>/dev/null || true; } \ + | grep -E 'python3\.[0-9]+$' \ + | sort -V \ + | tail -n 1 + ) + + if [[ -z "$latest_python" ]]; then + log_warn "No specific Python3.x version found. Falling back to system python3." + latest_python=$(command -v python3) + fi + + if [[ -z "$latest_python" ]]; then + log_error "Python3 not found on this system." + exit 1 + fi + + echo "🐍 Switching to: $latest_python" + log_info "Using Python interpreter: $latest_python" + + "$latest_python" -m venv .venv || { + log_error "Failed to create virtual environment." + exit 1 + } + + source .venv/bin/activate + log_success "Virtual environment created and activated." +} diff --git a/mac/logging-utils.sh b/mac/logging-utils.sh new file mode 100644 index 0000000..2ac5b2b --- /dev/null +++ b/mac/logging-utils.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +#set -e + +# ============================================== +# 🎨 COLOR & STYLE DEFINITIONS +# ============================================== +BOLD="\033[1m" +RESET="\033[0m" +GREEN="\033[32m" +YELLOW="\033[33m" +BLUE="\033[34m" +CYAN="\033[36m" +RED="\033[31m" +GRAY="\033[90m" +LIGHT_GRAY='\033[0;37m' + +# ============================================== +# 🪄 LOGGING HELPERS +# ============================================== +log_section() { + echo "" + echo -e "${BOLD}${CYAN}───────────────────────────────────────────────${RESET}" + echo -e "${BOLD}$1${RESET}" + echo -e "${BOLD}${CYAN}───────────────────────────────────────────────${RESET}" +} + +log_info() { echo -e "${LIGHT_GRAY}ℹ️ $1${RESET}"; } +log_success() { echo -e "${GREEN}✅ $1${RESET}"; } +log_warn() { echo -e "${YELLOW}⚠️ $1${RESET}"; } +log_error() { echo -e "${RED}❌ $1${RESET}"; } + diff --git a/mac/proxy-check.sh b/mac/proxy-check.sh deleted file mode 100755 index 4e6ba4b..0000000 --- a/mac/proxy-check.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env sh - -# URL to test -TEST_URL="https://www.browserstack.com/automate/browsers.json" - -# Detect proxy from env (case insensitive) -PROXY="${http_proxy:-${HTTP_PROXY:-${https_proxy:-${HTTPS_PROXY}}}}" - -# Reset output variables -export PROXY_HOST="" -export PROXY_PORT="" - -# Function: parse proxy url to host + port -parse_proxy() { - p="$1" - # strip protocol e.g. http://, https:// - p="${p#http://}" - p="${p#https://}" - # strip credentials if any user:pass@ - p="${p#*[@]}" - - # extract host and port - export PROXY_HOST="${p%%:*}" - export PROXY_PORT="${p##*:}" -} - -base64_encoded_creds=$(printf "%s" $BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY | base64 | tr -d '\n') - - -# If no proxy configured, exit early -if [ -z "$PROXY" ]; then - echo "No proxy found in environment. Clearing proxy host and port variables." - export PROXY_HOST="" - export PROXY_PORT="" - return 0 2>/dev/null || exit 0 -fi - -echo "Proxy detected: $PROXY" -parse_proxy "$PROXY" - -echo "Testing reachability via proxy..." - - -STATUS_CODE=$(curl -sS -o /dev/null -H "Authorization: Basic ${base64_encoded_creds}" -w "%{http_code}" --proxy "$PROXY" "$TEST_URL" 2>/dev/null) - -if [ "${STATUS_CODE#2}" != "$STATUS_CODE" ]; then - echo "✅ Reachable. HTTP $STATUS_CODE" - echo "Exporting PROXY_HOST=$PROXY_HOST" - echo "Exporting PROXY_PORT=$PROXY_PORT" - export PROXY_HOST - export PROXY_PORT -else - echo "❌ Not reachable (HTTP $STATUS_CODE). Clearing variables." - export PROXY_HOST="" - export PROXY_PORT="" -fi diff --git a/mac/run.sh b/mac/run.sh index 8142103..cb0b89c 100755 --- a/mac/run.sh +++ b/mac/run.sh @@ -1,1253 +1,75 @@ #!/bin/bash set -o pipefail -# ===== Global Variables ===== -WORKSPACE_DIR="$HOME/.browserstack" -PROJECT_FOLDER="NOW" - -BROWSERSTACK_USERNAME="" -BROWSERSTACK_ACCESS_KEY="" -TEST_TYPE="" # Web / App / Both -TECH_STACK="" # Java / Python / JS - -PARALLEL_PERCENTAGE=1.00 - -WEB_PLAN_FETCHED=false -MOBILE_PLAN_FETCHED=false -TEAM_PARALLELS_MAX_ALLOWED_WEB=0 -TEAM_PARALLELS_MAX_ALLOWED_MOBILE=0 - -# URL handling -DEFAULT_TEST_URL="https://bstackdemo.com" -CX_TEST_URL="$DEFAULT_TEST_URL" - -# Global vars -APP_URL="" -APP_PLATFORM="" # ios | android | all - - - -# ===== Example Platform Templates (replace with your full lists if available) ===== -WEB_PLATFORM_TEMPLATES=( - "Windows|10|Chrome" - "Windows|10|Firefox" - "Windows|11|Edge" - "Windows|11|Chrome" - "Windows|8|Chrome" - "OS X|Monterey|Chrome" - "OS X|Ventura|Chrome" - "OS X|Catalina|Firefox" -) - -MOBILE_ALL=( - # Tier 1 - "ios|iPhone 15|17" - "ios|iPhone 15 Pro|17" - "ios|iPhone 16|18" - "android|Samsung Galaxy S25|15" - "android|Samsung Galaxy S24|14" - - # Tier 2 - "ios|iPhone 14 Pro|16" - "ios|iPhone 14|16" - "ios|iPad Air 13 2025|18" - "android|Samsung Galaxy S23|13" - "android|Samsung Galaxy S22|12" - "android|Samsung Galaxy S21|11" - "android|Samsung Galaxy Tab S10 Plus|15" - - # Tier 3 - "ios|iPhone 13 Pro Max|15" - "ios|iPhone 13|15" - "ios|iPhone 12 Pro|14" - "ios|iPhone 12 Pro|17" - "ios|iPhone 12|17" - "ios|iPhone 12|14" - "ios|iPhone 12 Pro Max|16" - "ios|iPhone 13 Pro|15" - "ios|iPhone 13 Mini|15" - "ios|iPhone 16 Pro|18" - "ios|iPad 9th|15" - "ios|iPad Pro 12.9 2020|14" - "ios|iPad Pro 12.9 2020|16" - "ios|iPad 8th|16" - "android|Samsung Galaxy S22 Ultra|12" - "android|Samsung Galaxy S21|12" - "android|Samsung Galaxy S21 Ultra|11" - "android|Samsung Galaxy S20|10" - "android|Samsung Galaxy M32|11" - "android|Samsung Galaxy S10|9" - "android|Samsung Galaxy Tab S8|12" - "android|Google Pixel 9|15" - "android|Google Pixel 6 Pro|13" - "android|Google Pixel 8|14" - "android|Google Pixel 7|13" - "android|Google Pixel 6|12" - "android|Vivo Y21|11" - "android|Vivo Y50|10" - "android|Oppo Reno 6|11" - - # Tier 4 - "ios|iPhone 15 Pro Max|17" - "ios|iPhone 15 Pro Max|26" - "ios|iPhone 15|26" - "ios|iPhone 15 Plus|17" - "ios|iPhone 14 Pro|26" - "ios|iPhone 14|18" - "ios|iPhone 14|26" - "ios|iPhone 13 Pro Max|18" - "ios|iPhone 13|16" - "ios|iPhone 13|17" - "ios|iPhone 13|18" - "ios|iPhone 12 Pro|18" - "ios|iPhone 14 Pro Max|16" - "ios|iPhone 14 Plus|16" - "ios|iPhone 11|13" - "ios|iPhone 8|11" - "ios|iPhone 7|10" - "ios|iPhone 17 Pro Max|26" - "ios|iPhone 17 Pro|26" - "ios|iPhone 17 Air|26" - "ios|iPhone 17|26" - "ios|iPhone 16e|18" - "ios|iPhone 16 Pro Max|18" - "ios|iPhone 16 Plus|18" - "ios|iPhone SE 2020|16" - "ios|iPhone SE 2022|15" - "ios|iPad Air 4|14" - "ios|iPad 9th|18" - "ios|iPad Air 5|26" - "ios|iPad Pro 11 2021|18" - "ios|iPad Pro 13 2024|17" - "ios|iPad Pro 12.9 2021|14" - "ios|iPad Pro 12.9 2021|17" - "ios|iPad Pro 11 2024|17" - "ios|iPad Air 6|17" - "ios|iPad Pro 12.9 2022|16" - "ios|iPad Pro 11 2022|16" - "ios|iPad 10th|16" - "ios|iPad Air 13 2025|26" - "ios|iPad Pro 11 2020|13" - "ios|iPad Pro 11 2020|16" - "ios|iPad 8th|14" - "ios|iPad Mini 2021|15" - "ios|iPad Pro 12.9 2018|12" - "ios|iPad 6th|11" - "android|Samsung Galaxy S23 Ultra|13" - "android|Samsung Galaxy S22 Plus|12" - "android|Samsung Galaxy S21 Plus|11" - "android|Samsung Galaxy S20 Ultra|10" - "android|Samsung Galaxy S25 Ultra|15" - "android|Samsung Galaxy S24 Ultra|14" - "android|Samsung Galaxy M52|11" - "android|Samsung Galaxy A52|11" - "android|Samsung Galaxy A51|10" - "android|Samsung Galaxy A11|10" - "android|Samsung Galaxy A10|9" - "android|Samsung Galaxy Tab A9 Plus|14" - "android|Samsung Galaxy Tab S9|13" - "android|Samsung Galaxy Tab S7|10" - "android|Samsung Galaxy Tab S7|11" - "android|Samsung Galaxy Tab S6|9" - "android|Google Pixel 9|16" - "android|Google Pixel 10 Pro XL|16" - "android|Google Pixel 10 Pro|16" - "android|Google Pixel 10|16" - "android|Google Pixel 9 Pro XL|15" - "android|Google Pixel 9 Pro|15" - "android|Google Pixel 6 Pro|12" - "android|Google Pixel 6 Pro|15" - "android|Google Pixel 8 Pro|14" - "android|Google Pixel 7 Pro|13" - "android|Google Pixel 5|11" - "android|OnePlus 13R|15" - "android|OnePlus 12R|14" - "android|OnePlus 11R|13" - "android|OnePlus 9|11" - "android|OnePlus 8|10" - "android|Motorola Moto G71 5G|11" - "android|Motorola Moto G9 Play|10" - "android|Vivo V21|11" - "android|Oppo A96|11" - "android|Oppo Reno 3 Pro|10" - "android|Xiaomi Redmi Note 11|11" - "android|Xiaomi Redmi Note 9|10" - "android|Huawei P30|9" -) - - -APP_URL="" -APP_PLATFORM="" # ios | android | all - - -# ===== Log files (runtime only; created on first write) ===== -# ===== Log files (per-run) ===== -LOG_DIR="$WORKSPACE_DIR/$PROJECT_FOLDER/logs" -GLOBAL="$LOG_DIR/global.log" -WEB_LOG_FILE="$LOG_DIR/web_run_result.log" -MOBILE_LOG_FILE="$LOG_DIR/mobile_run_result.log" - -# Ensure log directory exists -mkdir -p "$LOG_DIR" - -# Clear old logs to start fresh -: > "$GLOBAL_LOG_FILE" -: > "$WEB_LOG_FILE" -: > "$MOBILE_LOG_FILE" - - -# ===== Logging helper (runtime timestamped logging) ===== -# Usage: log_msg_to "message" "$DEST_FILE" (DEST_FILE optional; prints to console always) -log_msg_to() { - local message="$1" - local dest_file="$2" # optional - local ts - ts="$(date +"%Y-%m-%d %H:%M:%S")" - local line="[$ts] $message" - - # print to console - echo "$line" - - # write to dest file if provided - if [ -n "$dest_file" ]; then - mkdir -p "$(dirname "$dest_file")" - echo "$line" >> "$dest_file" - fi -} - -# Spinner function -show_spinner() { - local pid=$1 - local spin='|/-\' - local i=0 - local ts - ts="$(date +"%Y-%m-%d %H:%M:%S")" - while kill -0 "$pid" 2>/dev/null; do - i=$(( (i+1) %4 )) - printf "\r[$ts] ⏳ Processing... ${spin:$i:1}" - sleep 0.1 - done - log_msg_to "✅ Done!" -} - -# ===== Functions: baseline interactions ===== -setup_workspace() { - local full_path="$WORKSPACE_DIR/$PROJECT_FOLDER" - if [ ! -d "$full_path" ]; then - mkdir -p "$full_path" - log_msg_to "✅ Created Onboarding workspace: $full_path" "$GLOBAL_LOG_FILE" - else - log_msg_to "ℹ️ Onboarding Workspace already exists: $full_path" "$GLOBAL_LOG_FILE" - fi -} - -ask_browserstack_credentials() { - # Prompt username - BROWSERSTACK_USERNAME=$(osascript -e 'Tell application "System Events" to display dialog "Please enter your BrowserStack Username.\n\nNote: Locate it in your BrowserStack account profile page.\nhttps://www.browserstack.com/accounts/profile/details" default answer "" with title "BrowserStack Setup" buttons {"OK"} default button "OK"' \ - -e 'text returned of result') - if [ -z "$BROWSERSTACK_USERNAME" ]; then - log_msg_to "❌ Username empty" "$GLOBAL_LOG_FILE" - exit 1 - fi - - # Prompt access key (hidden) - BROWSERSTACK_ACCESS_KEY=$(osascript -e 'Tell application "System Events" to display dialog "Please enter your BrowserStack Access Key.\n\nNote: Locate it in your BrowserStack account page.\nhttps://www.browserstack.com/accounts/profile/details" default answer "" with hidden answer with title "BrowserStack Setup" buttons {"OK"} default button "OK"' \ - -e 'text returned of result') - if [ -z "$BROWSERSTACK_ACCESS_KEY" ]; then - log_msg_to "❌ Access Key empty" "$GLOBAL_LOG_FILE" - exit 1 - fi - - log_msg_to "✅ BrowserStack credentials captured (access key hidden)" "$GLOBAL_LOG_FILE" -} - - -ask_tech_stack() { - TECH_STACK=$(osascript -e 'Tell application "System Events" to display dialog "Select installed tech stack:" buttons {"Java", "Python", "NodeJS"} default button "Java" with title "Testing Framework Technology Stack"' \ - -e 'button returned of result') - log_msg_to "✅ Selected Tech Stack: $TECH_STACK" "$GLOBAL_LOG_FILE" -} - -validate_tech_stack_installed() { - log_msg_to "ℹ️ Checking prerequisites for $TECH_STACK" "$GLOBAL_LOG_FILE" - - case "$TECH_STACK" in - Java) - log_msg_to "🔍 Checking if 'java' command exists..." "$GLOBAL_LOG_FILE" - if ! command -v java >/dev/null 2>&1; then - log_msg_to "❌ Java command not found in PATH." "$GLOBAL_LOG_FILE" - exit 1 - fi - - log_msg_to "🔍 Checking if Java runs correctly..." "$GLOBAL_LOG_FILE" - if ! JAVA_VERSION_OUTPUT=$(java -version 2>&1); then - log_msg_to "❌ Java exists but failed to run." "$GLOBAL_LOG_FILE" - exit 1 - fi - - log_msg_to "✅ Java is installed. Version details:" "$GLOBAL_LOG_FILE" - echo "$JAVA_VERSION_OUTPUT" | while read -r l; do log_msg_to " $l" "$GLOBAL_LOG_FILE"; done - ;; - Python) - log_msg_to "🔍 Checking if 'python3' command exists..." "$GLOBAL_LOG_FILE" - if ! command -v python3 >/dev/null 2>&1; then - log_msg_to "❌ Python3 command not found in PATH." "$GLOBAL_LOG_FILE" - exit 1 - fi - - log_msg_to "🔍 Checking if Python3 runs correctly..." "$GLOBAL_LOG_FILE" - if ! PYTHON_VERSION_OUTPUT=$(python3 --version 2>&1); then - log_msg_to "❌ Python3 exists but failed to run." "$GLOBAL_LOG_FILE" - exit 1 - fi - - log_msg_to "✅ Python3 is installed: $PYTHON_VERSION_OUTPUT" "$GLOBAL_LOG_FILE" - ;; - NodeJS) - log_msg_to "🔍 Checking if 'node' command exists..." "$GLOBAL_LOG_FILE" - if ! command -v node >/dev/null 2>&1; then - log_msg_to "❌ Node.js command not found in PATH." "$GLOBAL_LOG_FILE" - exit 1 - fi - log_msg_to "🔍 Checking if 'npm' command exists..." "$GLOBAL_LOG_FILE" - if ! command -v npm >/dev/null 2>&1; then - log_msg_to "❌ npm command not found in PATH." "$GLOBAL_LOG_FILE" - exit 1 - fi - - log_msg_to "🔍 Checking if Node.js runs correctly..." "$GLOBAL_LOG_FILE" - if ! NODE_VERSION_OUTPUT=$(node -v 2>&1); then - log_msg_to "❌ Node.js exists but failed to run." "$GLOBAL_LOG_FILE" - exit 1 - fi - - log_msg_to "🔍 Checking if npm runs correctly..." "$GLOBAL_LOG_FILE" - if ! NPM_VERSION_OUTPUT=$(npm -v 2>&1); then - log_msg_to "❌ npm exists but failed to run." "$GLOBAL_LOG_FILE" - exit 1 - fi - - log_msg_to "✅ Node.js is installed: $NODE_VERSION_OUTPUT" "$GLOBAL_LOG_FILE" - log_msg_to "✅ npm is installed: $NPM_VERSION_OUTPUT" "$GLOBAL_LOG_FILE" - ;; - *) - log_msg_to "❌ Unknown tech stack selected: $TECH_STACK" "$GLOBAL_LOG_FILE" - exit 1 - ;; - esac - - log_msg_to "✅ Prerequisites validated for $TECH_STACK" "$GLOBAL_LOG_FILE" -} - -# ===== Ask user for test URL via UI prompt ===== -ask_user_for_test_url() { - CX_TEST_URL=$(osascript -e 'Tell application "System Events" to display dialog "Enter the URL you want to test with BrowserStack:\n(Leave blank for default: '"$DEFAULT_TEST_URL"')" default answer "" with title "Test URL Setup" buttons {"OK"} default button "OK"' \ - -e 'text returned of result') - - if [ -n "$CX_TEST_URL" ]; then - log_msg_to "🌐 Using custom test URL: $CX_TEST_URL" "$GLOBAL_LOG_FILE" - else - CX_TEST_URL="$DEFAULT_TEST_URL" - log_msg_to "⚠️ No URL entered. Falling back to default: $CX_TEST_URL" "$GLOBAL_LOG_FILE" - fi -} - -ask_and_upload_app() { - - CHOICE_RESPONSE=$(osascript -e ' - display dialog "How would you like to select your app?" ¬ - with title "BrowserStack App Upload" ¬ - with icon note ¬ - buttons {"Use Sample App", "Upload my App (.apk/.ipa)", "Cancel"} ¬ - default button "Upload my App (.apk/.ipa)" - ' 2>/dev/null) - - if [[ "$CHOICE_RESPONSE" == *"Use Sample App"* ]]; then - log_msg_to "⬆️ Uploading sample app to BrowserStack..." "$GLOBAL_LOG_FILE" - UPLOAD_RESPONSE=$(curl -s -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" \ - -X POST "https://api-cloud.browserstack.com/app-automate/upload" \ - -F "url=https://www.browserstack.com/app-automate/sample-apps/android/WikipediaSample.apk") - - APP_URL=$(echo "$UPLOAD_RESPONSE" | grep -o '"app_url":"[^"]*' | cut -d'"' -f4) - export BROWSERSTACK_APP=$APP_URL - log_msg_to "Exported BROWSERSTACK_APP=$BROWSERSTACK_APP" "$GLOBAL_LOG_FILE" - - if [ -z "$APP_URL" ]; then - log_msg_to "❌ Upload failed. Response: $UPLOAD_RESPONSE" "$GLOBAL_LOG_FILE" - return 1 - fi - - log_msg_to "✅ App uploaded successfully: $APP_URL" "$GLOBAL_LOG_FILE" - APP_PLATFORM="android" - return 0 - - elif [[ "$CHOICE_RESPONSE" == *"Upload my App"* ]]; then - APP_FILE_PATH=$(osascript -e 'POSIX path of (choose file with prompt "📱 Please select your .apk or .ipa app file")' 2>/dev/null) - - if [ -z "$APP_FILE_PATH" ]; then - log_msg_to "⚠️ File selection canceled. Aborting." "$GLOBAL_LOG_FILE" - return 1 - fi - - if [[ "$APP_FILE_PATH" == *.apk ]]; then - APP_PLATFORM="android" - elif [[ "$APP_FILE_PATH" == *.ipa ]]; then - APP_PLATFORM="ios" - else - log_msg_to "❌ Unsupported file type. Only .apk or .ipa allowed." "$GLOBAL_LOG_FILE" - return 1 - fi - - log_msg_to "⬆️ Uploading $APP_FILE_PATH to BrowserStack..." "$GLOBAL_LOG_FILE" - UPLOAD_RESPONSE=$(curl -s -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" \ - -X POST "https://api-cloud.browserstack.com/app-automate/upload" \ - -F "file=@$APP_FILE_PATH") - - APP_URL=$(echo "$UPLOAD_RESPONSE" | grep -o '"app_url":"[^"]*' | cut -d'"' -f4) - export BROWSERSTACK_APP=$APP_URL - - if [ -z "$APP_URL" ]; then - log_msg_to "❌ Upload failed. Response: $UPLOAD_RESPONSE" "$GLOBAL_LOG_FILE" - return 1 - fi - - log_msg_to "✅ App uploaded successfully: $APP_URL" "$GLOBAL_LOG_FILE" - return 0 - - else - log_msg_to "🚫 Operation canceled by user." "$GLOBAL_LOG_FILE" - return 1 - fi -} - -ask_test_type() { - TEST_TYPE=$(osascript -e 'Tell application "System Events" to display dialog "Select testing type:" buttons {"Web", "App", "Both"} default button "Web" with title "Testing Type"' \ - -e 'button returned of result') - log_msg_to "✅ Selected Testing Type: $TEST_TYPE" "$GLOBAL_LOG_FILE" - - case "$TEST_TYPE" in - "Web") - ask_user_for_test_url - ;; - "App") - ask_and_upload_app - ;; - "Both") - ask_user_for_test_url - ask_and_upload_app - ;; - esac -} -# ===== Dynamic config generators ===== -generate_web_platforms_yaml() { - local max_total_parallels=$1 - local max - max=$(echo "$max_total_parallels * $PARALLEL_PERCENTAGE" | bc | cut -d'.' -f1) - [ -z "$max" ] && max=0 - local yaml="" - local count=0 - - for template in "${WEB_PLATFORM_TEMPLATES[@]}"; do - IFS="|" read -r os osVersion browserName <<< "$template" - for version in latest latest-1 latest-2; do - yaml+=" - os: $os - osVersion: $osVersion - browserName: $browserName - browserVersion: $version -" - count=$((count + 1)) - if [ "$count" -ge "$max" ]; then - echo "$yaml" - return - fi - done - done - - echo "$yaml" -} - -generate_mobile_platforms_yaml() { - local max_total_parallels=$1 - local max=$(echo "$max_total_parallels * $PARALLEL_PERCENTAGE" | bc | cut -d'.' -f1) - - # fallback if bc result is empty or zero - if [ -z "$max" ] || [ "$max" -lt 1 ]; then - max=1 - fi - - local yaml="" - local count=0 - - for template in "${MOBILE_ALL[@]}"; do - IFS="|" read -r platformName deviceName platformVersion <<< "$template" - - if [ -n "$APP_PLATFORM" ]; then - if [[ "$APP_PLATFORM" == "ios" && "$platformName" != "ios" ]]; then - continue - fi - if [[ "$APP_PLATFORM" == "android" && "$platformName" != "android" ]]; then - continue - fi - if [[ "$APP_PLATFORM" == "all" && "$platformName" != "android" ]]; then - continue - fi - fi - - yaml+=" - platformName: $platformName - deviceName: $deviceName - platformVersion: '${platformVersion}.0' -" - count=$((count + 1)) - if [ "$count" -ge "$max" ]; then - echo "$yaml" - return - fi - done - - echo "$yaml" - log_msg_to "Generated mobile platforms YAML:" "$MOBILE_LOG_FILE" -} - - -generate_web_caps_json() { - local max_total_parallels=$1 - local max - max=$(echo "$max_total_parallels * $PARALLEL_PERCENTAGE" | bc | cut -d'.' -f1) - [ "$max" -lt 1 ] && max=1 # fallback to minimum 1 - - local json="" - local count=0 - - for template in "${WEB_PLATFORM_TEMPLATES[@]}"; do - IFS="|" read -r os osVersion browserName <<< "$template" - for version in latest latest-1 latest-2; do - json+="{ - \"browserName\": \"$browserName\", - \"browserVersion\": \"$version\", - \"bstack:options\": { - \"os\": \"$os\", - \"osVersion\": \"$osVersion\" - } - }," - count=$((count + 1)) - if [ "$count" -ge "$max" ]; then - json="${json%,}" # strip trailing comma - echo "$json" - return - fi - done - done - - # Fallback in case not enough combinations - json="${json%,}" - echo "$json" -} - -generate_mobile_caps_json() { - local max_total=$1 - local output_file="$WORKSPACE_DIR/$PROJECT_FOLDER/usage_file.json" - : > "$output_file" # Clear the output file - - local count=0 - - local json="[" - - for template in "${MOBILE_ALL[@]}"; do - IFS="|" read -r platformName deviceName baseVersion <<< "$template" - if [ "$APP_PLATFORM" == "ios" ] && [ "$platformName" != "ios" ]; then - continue - elif [ "$APP_PLATFORM" == "android" ] || [ "$APP_PLATFORM" == "all" ] && [ "$platformName" != "android" ]; then - continue - fi - - json="${json}{ - \"bstack:options\": { - \"deviceName\": \"${deviceName}\", - \"osVersion\": \"${baseVersion}.0\" - } - }," - - count=$((count + 1)) - if [ "$count" -ge "$max_total" ]; then - break - fi - done # End of the loop - json="${json%,}]" - echo "$json" > "$output_file" +# Import utilities +source "$(dirname "$0")/common-utils.sh" +source "$(dirname "$0")/logging-utils.sh" +source "$(dirname "$0")/env-setup-run.sh" +source "$(dirname "$0")/user-interaction.sh" +source "$(dirname "$0")/env-prequisite-checks.sh" + +# ===== Web wrapper with retry logic (writes runtime logs to $NOW_RUN_LOG_FILE) ===== +# Wrapper functions using the common setup_environment function +run_setup() { + local test_type=$1 + local tech_stack=$2 + setup_environment $test_type $tech_stack } -# ===== Fetch plan details (writes to GLOBAL) ===== -fetch_plan_details() { - log_msg_to "ℹ️ Fetching BrowserStack Plan Details..." "$GLOBAL_LOG_FILE" - local web_unauthorized=false - local mobile_unauthorized=false - - if [[ "$TEST_TYPE" == "Web" || "$TEST_TYPE" == "Both" ]]; then - RESPONSE_WEB=$(curl -s -w "\n%{http_code}" -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" https://api.browserstack.com/automate/plan.json) - HTTP_CODE_WEB=$(echo "$RESPONSE_WEB" | tail -n1) - RESPONSE_WEB_BODY=$(echo "$RESPONSE_WEB" | sed '$d') - if [ "$HTTP_CODE_WEB" == "200" ]; then - WEB_PLAN_FETCHED=true - TEAM_PARALLELS_MAX_ALLOWED_WEB=$(echo "$RESPONSE_WEB_BODY" | grep -o '"parallel_sessions_max_allowed":[0-9]*' | grep -o '[0-9]*') - log_msg_to "✅ Web Testing Plan fetched: Team max parallel sessions = $TEAM_PARALLELS_MAX_ALLOWED_WEB" "$GLOBAL_LOG_FILE" - else - log_msg_to "❌ Web Testing Plan fetch failed ($HTTP_CODE_WEB)" "$GLOBAL_LOG_FILE" - [ "$HTTP_CODE_WEB" == "401" ] && web_unauthorized=true - fi - fi - if [[ "$TEST_TYPE" == "App" || "$TEST_TYPE" == "Both" ]]; then - RESPONSE_MOBILE=$(curl -s -w "\n%{http_code}" -u "$BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY" https://api-cloud.browserstack.com/app-automate/plan.json) - HTTP_CODE_MOBILE=$(echo "$RESPONSE_MOBILE" | tail -n1) - RESPONSE_MOBILE_BODY=$(echo "$RESPONSE_MOBILE" | sed '$d') - if [ "$HTTP_CODE_MOBILE" == "200" ]; then - MOBILE_PLAN_FETCHED=true - TEAM_PARALLELS_MAX_ALLOWED_MOBILE=$(echo "$RESPONSE_MOBILE_BODY" | grep -o '"parallel_sessions_max_allowed":[0-9]*' | grep -o '[0-9]*') - log_msg_to "✅ Mobile App Testing Plan fetched: Team max parallel sessions = $TEAM_PARALLELS_MAX_ALLOWED_MOBILE" "$GLOBAL_LOG_FILE" - else - log_msg_to "❌ Mobile App Testing Plan fetch failed ($HTTP_CODE_MOBILE)" "$GLOBAL_LOG_FILE" - [ "$HTTP_CODE_MOBILE" == "401" ] && mobile_unauthorized=true - fi - fi - - if [[ "$TEST_TYPE" == "Web" && "$web_unauthorized" == true ]] || \ - [[ "$TEST_TYPE" == "App" && "$mobile_unauthorized" == true ]] || \ - [[ "$TEST_TYPE" == "Both" && "$web_unauthorized" == true && "$mobile_unauthorized" == true ]]; then - log_msg_to "❌ Unauthorized to fetch required plan(s). Exiting." "$GLOBAL_LOG_FILE" - exit 1 - fi -} +# ===== Main flow (baseline steps then run) ===== -# Function to check if IP is private -is_private_ip() { - case $1 in - 10.* | 192.168.* | 172.16.* | 172.17.* | 172.18.* | 172.19.* | \ - 172.20.* | 172.21.* | 172.22.* | 172.23.* | 172.24.* | 172.25.* | \ - 172.26.* | 172.27.* | 172.28.* | 172.29.* | 172.30.* | 172.31.* | "") - return 0 ;; # Private - *) - return 1 ;; # Public - esac -} +RUN_MODE=$1 # --interactive or --silent / --debug +TT=$2 # Testing Type from env (for silent mode) +TSTACK=$3 # Tech Stack from env (for silent mode) -is_domain_private() { - domain=${CX_TEST_URL#*://} # remove protocol - domain=${domain%%/*} # remove everything after first "/" - log_msg_to "Website domain: $domain" - export NOW_WEB_DOMAIN="$CX_TEST_URL" +log_section "🧭 Setup Summary – BrowserStack NOW" +log_info "Timestamp: $(date '+%Y-%m-%d %H:%M:%S')" -# Resolve domain using Cloudflare DNS -IP_ADDRESS=$(dig +short "$domain" @1.1.1.1 | head -n1) -# Determine if domain is private -if is_private_ip "$IP_ADDRESS"; then - is_cx_domain_private=0 +log_file="" +if [[ "$RUN_MODE" == *"--silent"* || "$RUN_MODE" == *"--debug"* ]]; then + TEST_TYPE=$TT + TECH_STACK=$TSTACK + log_file="$LOG_DIR/${TEST_TYPE:unknown}_run_result.log" + log_info "Run Mode: ${RUN_MODE:-default}" else - is_cx_domain_private=-1 + get_test_type $RUN_MODE + get_tech_stack $RUN_MODE + log_file="$LOG_DIR/${TEST_TYPE:unknown}_run_result.log" + perform_next_steps_based_on_test_type $TEST_TYPE fi -log_msg_to "Resolved IPs: $IP_ADDRESS" - -return $is_cx_domain_private -} - - -setup_web_java() { - local local_flag=$1 - local parallels=$2 - - REPO="now-testng-browserstack" - TARGET_DIR="$WORKSPACE_DIR/$PROJECT_FOLDER/$REPO" - - mkdir -p "$WORKSPACE_DIR/$PROJECT_FOLDER" - rm -rf $TARGET_DIR - # === 1️⃣ Clone Repo === - log_msg_to "📦 Cloning repo $REPO into $TARGET_DIR" "$GLOBAL_LOG_FILE" "$WEB_LOG_FILE" - git clone https://github.com/browserstackCE/$REPO "$TARGET_DIR" >> "$WEB_LOG_FILE" 2>&1 || true - - - cd "$TARGET_DIR"|| return 1 - - - if is_domain_private; then - local_flag=true - fi - - # === 4️⃣ Local Flag === - if [ "$local_flag" = "true" ]; then - log_msg_to "✅ BrowserStack Local is ENABLED for this run." "$GLOBAL_LOG_FILE" "$WEB_LOG_FILE" - else - log_msg_to "✅ BrowserStack Local is DISABLED for this run." "$GLOBAL_LOG_FILE" "$WEB_LOG_FILE" - fi - - # === 5️⃣ YAML Setup === - log_msg_to "🧩 Generating YAML config (bstack.yml)" "$GLOBAL_LOG_FILE" "$WEB_LOG_FILE" - - - # YAML config path - export BROWSERSTACK_CONFIG_FILE="./browserstack.yml" - platform_yaml=$(generate_web_platforms_yaml "$TEAM_PARALLELS_MAX_ALLOWED_WEB") - - cat >> "$BROWSERSTACK_CONFIG_FILE" <> "$WEB_LOG_FILE" 2>&1 || true - - log_msg_to "🚀 Running 'mvn test -P sample-test'. This could take a few minutes. Follow the Automaton build here: https://automation.browserstack.com/" "$GLOBAL_LOG_FILE" "$WEB_LOG_FILE" - mvn test -P sample-test >> "$WEB_LOG_FILE" 2>&1 & - cmd_pid=$!|| true - - show_spinner "$cmd_pid" - wait "$cmd_pid" - - cd "$WORKSPACE_DIR/$PROJECT_FOLDER" - return 0 -} - -setup_web_python() { - local local_flag=$1 - local parallels=$2 - local log_file=$3 - - REPO="now-pytest-browserstack" - TARGET_DIR="$WORKSPACE_DIR/$PROJECT_FOLDER/$REPO" - - rm -rf $TARGET_DIR - - git clone https://github.com/browserstackCE/$REPO.git "$TARGET_DIR" >> "$WEB_LOG_FILE" 2>&1 || true - log_msg_to "✅ Cloned repository: $REPO into $TARGET_DIR" "$GLOBAL_LOG_FILE" - - - cd "$TARGET_DIR" || return 1 - - - # Setup Python venv - if [ ! -d "venv" ]; then - python3 -m venv venv - log_msg_to "✅ Created Python virtual environment" - fi - - # shellcheck disable=SC1091 - source venv/bin/activate - pip3 install -r requirements.txt >> "$WEB_LOG_FILE" 2>&1 - - # Export credentials for pytest to use - export BROWSERSTACK_USERNAME="$BROWSERSTACK_USERNAME" - export BROWSERSTACK_ACCESS_KEY="$BROWSERSTACK_ACCESS_KEY" - - # Update YAML at root level (browserstack.yml) - export BROWSERSTACK_CONFIG_FILE="browserstack.yml" - platform_yaml=$(generate_web_platforms_yaml "$TEAM_PARALLELS_MAX_ALLOWED_WEB") - - if is_domain_private; then - local_flag=true - fi - - # === 4️⃣ Local Flag === - if [ "$local_flag" = "true" ]; then - log_msg_to "✅ BrowserStack Local is ENABLED for this run." "$GLOBAL_LOG_FILE" "$WEB_LOG_FILE" - else - log_msg_to "✅ BrowserStack Local is DISABLED for this run." "$GLOBAL_LOG_FILE" "$WEB_LOG_FILE" - fi - - - cat > browserstack.yml <> "$WEB_LOG_FILE" 2>&1 & - cmd_pid=$!|| true - - show_spinner "$cmd_pid" - wait "$cmd_pid" - - cd "$WORKSPACE_DIR/$PROJECT_FOLDER" - return 0 -} - - -setup_web_nodejs() { - local local_flag=$1 - local parallels=$2 - - REPO="now-webdriverio-browserstack" - TARGET_DIR="$WORKSPACE_DIR/$PROJECT_FOLDER/$REPO" - - rm -rf $TARGET_DIR - - mkdir -p "$WORKSPACE_DIR/$PROJECT_FOLDER" - - # === 1️⃣ Clone Repo === - log_msg_to "📦 Cloning repo $REPO into $TARGET_DIR" "$GLOBAL_LOG_FILE" "$WEB_LOG_FILE" - git clone https://github.com/browserstackCE/$REPO.git "$TARGET_DIR" >> "$WEB_LOG_FILE" 2>&1 || true - - cd "$TARGET_DIR" || return 1 - - # === 2️⃣ Install Dependencies === - log_msg_to "⚙️ Running 'npm install'" "$WEB_LOG_FILE" - npm install >> "$WEB_LOG_FILE" 2>&1 || true - - - # === 4️⃣ Generate Capabilities JSON === - log_msg_to "🧩 Generating browser/OS capabilities" "$WEB_LOG_FILE" - local caps_json - caps_json=$(generate_web_caps_json "$parallels") - - export BSTACK_PARALLELS=$parallels - - export BSTACK_CAPS_JSON=$caps_json - - if is_domain_private; then - local_flag=true - fi - - #log_msg_to local flag status - if [ "$local_flag" = "true" ]; then - log_msg_to "✅ BrowserStack Local is ENABLED for this run." "$WEB_LOG_FILE" - else - log_msg_to "✅ BrowserStack Local is DISABLED for this run." "$WEB_LOG_FILE" - fi - - # === 7️⃣ Export BrowserStack Credentials === - export BROWSERSTACK_USERNAME="$BROWSERSTACK_USERNAME" - export BROWSERSTACK_ACCESS_KEY="$BROWSERSTACK_ACCESS_KEY" - export BROWSERSTACK_LOCAL=$local_flag - - # === 8️⃣ Run Tests === - log_msg_to "🚀 Running 'npm run test'. This could take a few minutes. Follow the Automaton build here: https://automation.browserstack.com/" "$WEB_LOG_FILE" - npm run test >> "$WEB_LOG_FILE" 2>&1 || true & - cmd_pid=$!|| true - - show_spinner "$cmd_pid" - wait "$cmd_pid" - - # === 9️⃣ Wrap Up === - log_msg_to "✅ Web JS setup and test execution completed successfully." "$WEB_LOG_FILE" - - cd "$WORKSPACE_DIR/$PROJECT_FOLDER" - return 0 -} - - -# ===== Web wrapper with retry logic (writes runtime logs to WEB_LOG_FILE) ===== -setup_web() { - log_msg_to "Starting Web setup for $TECH_STACK" "$WEB_LOG_FILE" - - local local_flag=false - local attempt=1 - local success=true - local log_file=$WEB_LOG_FILE - # don't pre-create; file will be created on first write by log_msg_to or command output redirection - - local total_parallels - total_parallels=$(echo "$TEAM_PARALLELS_MAX_ALLOWED_WEB * $PARALLEL_PERCENTAGE" | bc | cut -d'.' -f1) - [ -z "$total_parallels" ] && total_parallels=1 - local parallels_per_platform - parallels_per_platform=$total_parallels - - while [ "$attempt" -le 1 ]; do - log_msg_to "[Web Setup]" "$WEB_LOG_FILE" - case "$TECH_STACK" in - Java) - setup_web_java "$local_flag" "$parallels_per_platform" "$WEB_LOG_FILE" - if (grep -qiE "BUILD FAILURE" "$WEB_LOG_FILE"); then - success=false - fi - ;; - Python) - setup_web_python "$local_flag" "$parallels_per_platform" "$WEB_LOG_FILE" - if (grep -qiE "BUILD FAILURE" "$WEB_LOG_FILE"); then ## needs to change based on pytest output - success=false - fi - ;; - +log_info "Log file path: $log_file" +export NOW_RUN_LOG_FILE="$log_file" +setup_workspace +get_browserstack_credentials $RUN_MODE - NodeJS) setup_web_nodejs "$local_flag" "$parallels_per_platform" "$WEB_LOG_FILE" - if (grep -qiE "([1-9][0-9]*) passed, ([1-9]*) failed" "$WEB_LOG_FILE"); then - success=false - fi - ;; +log_section "⚙️ Platform & Tech Stack" +log_info "Platform: ${TEST_TYPE:-N/A}" +log_info "Tech Stack: ${TECH_STACK:-N/A}" - *) log_msg_to "Unknown TECH_STACK: $TECH_STACK" "$WEB_LOG_FILE"; return 1 ;; - esac +validate_tech_stack_installed $TECH_STACK +fetch_plan_details $TEST_TYPE - if [ "$success" = true ]; then - log_msg_to "✅ Web setup succeeded." "$WEB_LOG_FILE" - break - else - log_msg_to "❌ Web setup failed. Check $WEB_LOG_FILE for details" "$WEB_LOG_FILE" - break +if [[ "$RUN_MODE" == *"--silent"* || "$RUN_MODE" == *"--debug"* ]]; then + if [[ $TEST_TYPE == "app" ]]; then + get_upload_app + log_success "Sample App uploaded successfully" fi - done -} - - -setup_mobile_python() { - local local_flag=$1 - local parallels=$2 - local log_file=$3 - - REPO="pytest-appium-app-browserstack" - TARGET_DIR="$WORKSPACE_DIR/$PROJECT_FOLDER/$REPO" - - rm -rf $TARGET_DIR - - # Clone repo if not present - git clone https://github.com/browserstack/$REPO.git "$TARGET_DIR" - log_msg_to "✅ Cloned repository: $REPO into $TARGET_DIR" "$GLOBAL_LOG_FILE" - - cd "$TARGET_DIR" || return 1 - - # Create & activate venv (if not exists) - if [ ! -d "venv" ]; then - python3 -m venv venv - fi - # shellcheck disable=SC1091 - source venv/bin/activate - - # Install dependencies - pip install -r requirements.txt >> "$log_file" 2>&1 - - # Export credentials - export BROWSERSTACK_USERNAME="$BROWSERSTACK_USERNAME" - export BROWSERSTACK_ACCESS_KEY="$BROWSERSTACK_ACCESS_KEY" - - # Prepare platform-specific YAMLs in android/ and ios/ - local original_platform="$APP_PLATFORM" - - APP_PLATFORM="android" - local platform_yaml_android - platform_yaml_android=$(generate_mobile_platforms_yaml "$TEAM_PARALLELS_MAX_ALLOWED_MOBILE") - cat > android/browserstack.yml < ios/browserstack.yml < android/bstack_sample.py <<'PYEOF' -import pytest - - -@pytest.mark.usefixtures('setWebdriver') -class TestUniversalAppCheck: - - def test_app_health_check(self): - - # 1. Get initial app and device state (no locators) - initial_package = self.driver.current_package - initial_activity = self.driver.current_activity - initial_orientation = self.driver.orientation - - # 2. Log the captured data to BrowserStack using 'annotate' - log_data = f"Initial State: Package='{initial_package}', Activity='{initial_activity}', Orientation='{initial_orientation}'" - self.driver.execute_script( - 'browserstack_executor: {"action": "annotate", "arguments": {"data": "' + log_data + '", "level": "info"}}' - ) - - # 3. Perform a locator-free action: change device orientation - self.driver.orientation = 'LANDSCAPE' - - # 4. Perform locator-free assertions - assert self.driver.orientation == 'LANDSCAPE' - - # 5. Log the successful state change - self.driver.execute_script( - 'browserstack_executor: {"action": "annotate", "arguments": {"data": "Successfully changed orientation to LANDSCAPE", "level": "info"}}' - ) - - # 6. Set the final session status to 'passed' - self.driver.execute_script( - 'browserstack_executor: {"action": "setSessionStatus", "arguments": {"status": "passed", "reason": "App state verified and orientation changed!"}}' - ) -PYEOF - - cp android/bstack_sample.py ios/bstack_sample.py - - # Decide which directory to run based on APP_PLATFORM (default to android) - local run_dir="android" - if [ "$APP_PLATFORM" = "ios" ]; then - run_dir="ios" - fi - - if is_domain_private; then - local_flag=true - fi - - # Log local flag status - if [ "$local_flag" = "true" ]; then - log_msg_to "✅ BrowserStack Local is ENABLED for this run." - else - log_msg_to "✅ BrowserStack Local is DISABLED for this run." - fi - - # Run pytest with BrowserStack SDK from the chosen platform directory - log_msg_to "🚀 Running 'cd $run_dir && browserstack-sdk pytest -s bstack_sample.py'" - ( - cd "$run_dir" && browserstack-sdk pytest -s bstack_sample.py >> "$log_file" 2>&1 || true - ) - - deactivate - cd "$WORKSPACE_DIR/$PROJECT_FOLDER" - return 0 -} - -setup_mobile_java() { - local local_flag=$1 - local parallels=$2 - local log_file=$3 - - REPO="now-testng-appium-app-browserstack" - TARGET_DIR="$WORKSPACE_DIR/$PROJECT_FOLDER/$REPO" +fi - rm -rf $TARGET_DIR +log_msg_to "Plan summary: WEB_PLAN_FETCHED=$WEB_PLAN_FETCHED (team max=$TEAM_PARALLELS_MAX_ALLOWED_WEB), MOBILE_PLAN_FETCHED=$MOBILE_PLAN_FETCHED (team max=$TEAM_PARALLELS_MAX_ALLOWED_MOBILE)" +log_msg_to "Checking proxy in environment" +set_proxy_in_env - git clone https://github.com/BrowserStackCE/$REPO.git "$TARGET_DIR" >> "$WEB_LOG_FILE" 2>&1 || true - log_msg_to "✅ Cloned repository: $REPO into $TARGET_DIR" "$GLOBAL_LOG_FILE" "$MOBILE_LOG_FILE" - cd "$TARGET_DIR" || return 1 +log_section "🧹 Getting Ready" +log_info "Clearing old logs..." -if [[ "$APP_PLATFORM" == "all" || "$APP_PLATFORM" == "android" ]]; then - cd android/testng-examples +clear_old_logs +log_info "Starting $TEST_TYPE setup for $TECH_STACK" +run_setup $TEST_TYPE $TECH_STACK +if [ $? -eq 0 ]; then + log_section "🎯 IN RUN.sh success!${RESET}" else - cd ios/testng-examples + log_section "🎯 IN RUN.sh failed ${RESET}" fi - - - export BROWSERSTACK_USERNAME="$BROWSERSTACK_USERNAME" - export BROWSERSTACK_ACCESS_KEY="$BROWSERSTACK_ACCESS_KEY" - - # YAML config path - export BROWSERSTACK_CONFIG_FILE="./browserstack.yml" - platform_yaml=$(generate_mobile_platforms_yaml "$TEAM_PARALLELS_MAX_ALLOWED_MOBILE") - - cat >> "$BROWSERSTACK_CONFIG_FILE" <> "$MOBILE_LOG_FILE" 2>&1; then - log_msg_to "❌ 'mvn clean' FAILED. See $log_file for details." "$GLOBAL_LOG_FILE" "$MOBILE_LOG_FILE" - return 1 # Fail the function if clean fails - fi - - log_msg_to "🚀 Running 'mvn test -P sample-test'. This could take a few minutes. Follow the Automaton build here: https://automation.browserstack.com/" "$GLOBAL_LOG_FILE" "$WEB_LOG_FILE" - mvn test -P sample-test >> "$MOBILE_LOG_FILE" 2>&1 & - cmd_pid=$!|| true - - show_spinner "$cmd_pid" - wait "$cmd_pid" - - cd "$WORKSPACE_DIR/$PROJECT_FOLDER" - return 0 -} - - -setup_mobile_nodejs() { - local local_flag=$1 - local parallels=$2 - local log_file=$3 - - echo "Starting Mobile NodeJS setup with parallels: $parallels" >> "$log_file" 2>&1 - - # cd $WORKSPACE_DIR/$PROJECT_FOLDER || return 1 - - REPO="now-webdriverio-appium-app-browserstack" - TARGET_DIR="$WORKSPACE_DIR/$PROJECT_FOLDER/$REPO" - - rm -rf $TARGET_DIR - git clone https://github.com/BrowserStackCE/$REPO.git "$TARGET_DIR" >> "$WEB_LOG_FILE" 2>&1 || true - log_msg_to "✅ Cloned repository: $REPO into $TARGET_DIR" "$GLOBAL_LOG_FILE" "$MOBILE_LOG_FILE" - cd "$TARGET_DIR"/test || return 1 - - # === 2️⃣ Install Dependencies === - log_msg_to "⚙️ Running 'npm install'" "$MOBILE_LOG_FILE" - npm install >> "$MOBILE_LOG_FILE" 2>&1 || true - - generate_mobile_caps_json "$parallels" - - export BROWSERSTACK_USERNAME="$BROWSERSTACK_USERNAME" - export BROWSERSTACK_ACCESS_KEY="$BROWSERSTACK_ACCESS_KEY" - export BSTACK_PARALLELS=$parallels - - echo "Parallels for NODEJS Repo: " $BSTACK_PARALLELS - -# === 8️⃣ Run Tests === - log_msg_to "🚀 Running 'npm run test'. This could take a few minutes. Follow the Automaton build here: https://automation.browserstack.com/" "$WEB_LOG_FILE" - npm run test >> "$MOBILE_LOG_FILE" 2>&1 || true & - cmd_pid=$!|| true - - show_spinner "$cmd_pid" - wait "$cmd_pid" - - # === 9️⃣ Wrap Up === - log_msg_to "✅ Mobile JS setup and test execution completed successfully." "$MOBILE_LOG_FILE" - - cd "$WORKSPACE_DIR/$PROJECT_FOLDER" - return 0 -} - -# ===== Mobile wrapper with retry logic (writes runtime logs to MOBILE_LOG_FILE) ===== -setup_mobile() { - log_msg_to "Starting Mobile setup for $TECH_STACK" "$WEB_LOG_FILE" - - local local_flag=false - local attempt=1 - local success=true - local log_file=$WEB_LOG_FILE - # don't pre-create; file will be created on first write by log_msg_to or command output redirection - - local total_parallels - total_parallels=$(echo "$TEAM_PARALLELS_MAX_ALLOWED_MOBILE * $PARALLEL_PERCENTAGE" | bc | cut -d'.' -f1) - [ -z "$total_parallels" ] && total_parallels=1 - local parallels_per_platform - parallels_per_platform=$total_parallels - - - while [ "$attempt" -le 1 ]; do - log_msg_to "[Mobile Setup]" "$MOBILE_LOG_FILE" - case "$TECH_STACK" in - Java) - setup_mobile_java "$local_flag" "$parallels_per_platform" "$MOBILE_LOG_FILE" - if (grep -qiE "BUILD FAILURE" "$MOBILE_LOG_FILE"); then - success=false - fi - ;; - Python) - setup_mobile_python "$local_flag" "$parallels_per_platform" "$MOBILE_LOG_FILE" - if (grep -qiE "BUILD FAILURE" "$MOBILE_LOG_FILE"); then ## needs to change based on pytest output - success=false - fi - ;; - - - NodeJS) setup_mobile_nodejs "$local_flag" "$parallels_per_platform" "$MOBILE_LOG_FILE" - if (grep -qiE "([1-9][0-9]*) passed, ([1-9]*) failed" "$MOBILE_LOG_FILE"); then - success=false - fi - ;; - - *) log_msg_to "Unknown TECH_STACK: $TECH_STACK" "$MOBILE_LOG_FILE"; return 1 ;; - esac - - if [ "$success" = true ]; then - log_msg_to "✅ Mobile setup succeeded." "$MOBILE_LOG_FILE" - break - else - log_msg_to "❌ Mobile setup failed. Check $MOBILE_LOG_FILE for details" "$MOBILE_LOG_FILE" - break - fi - done -} - -# ===== Orchestration: decide what to run based on TEST_TYPE and plan fetch ===== -run_setup() { - log_msg_to "Orchestration: TEST_TYPE=$TEST_TYPE, WEB_PLAN_FETCHED=$WEB_PLAN_FETCHED, MOBILE_PLAN_FETCHED=$MOBILE_PLAN_FETCHED" "$GLOBAL_LOG_FILE" - - case "$TEST_TYPE" in - Web) - if [ "$WEB_PLAN_FETCHED" == true ]; then - setup_web - else - log_msg_to "⚠️ Skipping Web setup — Web plan not fetched" "$GLOBAL_LOG_FILE" - fi - ;; - App) - if [ "$MOBILE_PLAN_FETCHED" == true ]; then - setup_mobile - else - log_msg_to "⚠️ Skipping Mobile setup — Mobile plan not fetched" "$GLOBAL_LOG_FILE" - fi - ;; - Both) - local ran_any=false - if [ "$WEB_PLAN_FETCHED" == true ]; then - setup_web - ran_any=true - else - log_msg_to "⚠️ Skipping Web setup — Web plan not fetched" "$GLOBAL_LOG_FILE" - fi - if [ "$MOBILE_PLAN_FETCHED" == true ]; then - setup_mobile - ran_any=true - else - log_msg_to "⚠️ Skipping Mobile setup — Mobile plan not fetched" "$GLOBAL_LOG_FILE" - fi - if [ "$ran_any" == false ]; then - log_msg_to "❌ Both Web and Mobile setup were skipped. Exiting." "$GLOBAL_LOG_FILE" - exit 1 - fi - ;; - *) - log_msg_to "❌ Invalid TEST_TYPE: $TEST_TYPE" "$GLOBAL_LOG_FILE" - exit 1 - ;; - esac -} - -# ===== Main flow (baseline steps then run) ===== -setup_workspace -ask_browserstack_credentials -ask_test_type -ask_tech_stack -validate_tech_stack_installed -fetch_plan_details - -log_msg_to "Plan summary: WEB_PLAN_FETCHED=$WEB_PLAN_FETCHED (team max=$TEAM_PARALLELS_MAX_ALLOWED_WEB), MOBILE_PLAN_FETCHED=$MOBILE_PLAN_FETCHED (team max=$TEAM_PARALLELS_MAX_ALLOWED_MOBILE)" "$GLOBAL_LOG_FILE" -log_msg_to "Checking proxy in environment" "$GLOBAL_LOG_FILE" -chmod +x ./mac/proxy-check.sh -./mac/proxy-check.sh -log_msg_to "Starting setup run..." "$GLOBAL_LOG_FILE" -run_setup -log_msg_to "Setup run finished." "$GLOBAL_LOG_FILE" diff --git a/mac/user-interaction.sh b/mac/user-interaction.sh new file mode 100644 index 0000000..7e2fe68 --- /dev/null +++ b/mac/user-interaction.sh @@ -0,0 +1,111 @@ +#!/bin/bash + +# ===== Credential Management ===== +get_browserstack_credentials() { + local run_mode=$1 + if [[ "$RUN_MODE" == *"--silent"* || "$RUN_MODE" == *"--debug"* ]]; then + username="$BROWSERSTACK_USERNAME" + access_key="$BROWSERSTACK_ACCESS_KEY" + log_info "BrowserStack credentials loaded from environment variables for user: $username" + else + username=$(osascript -e 'Tell application "System Events" to display dialog "Please enter your BrowserStack Username.\n\nNote: Locate it in your BrowserStack account profile page.\nhttps://www.browserstack.com/accounts/profile/details" default answer "" with title "BrowserStack Setup" buttons {"OK"} default button "OK"' \ + -e 'text returned of result') + + if [ -z "$username" ]; then + log_msg_to "❌ Username empty" + return 1 + fi + + access_key=$(osascript -e 'Tell application "System Events" to display dialog "Please enter your BrowserStack Access Key.\n\nNote: Locate it in your BrowserStack account page.\nhttps://www.browserstack.com/accounts/profile/details" default answer "" with hidden answer with title "BrowserStack Setup" buttons {"OK"} default button "OK"' \ + -e 'text returned of result') + if [ -z "$access_key" ]; then + log_msg_to "❌ Access Key empty" + return 1 + fi + + export BROWSERSTACK_USERNAME=$username + export BROWSERSTACK_ACCESS_KEY=$access_key + log_info "BrowserStack credentials captured from user: $username" + fi + + + return 0 +} + +# ===== Tech Stack Management ===== +get_tech_stack() { + local run_mode=$1 + local tech_stack="" + if [[ "$RUN_MODE" == *"--silent"* || "$RUN_MODE" == *"--debug"* ]]; then + tech_stack="$TSTACK" + log_msg_to "✅ Selected Tech Stack from environment: $tech_stack" + else + tech_stack=$(osascript -e 'Tell application "System Events" to display dialog "Select installed tech stack:" buttons {"java", "python", "nodejs"} default button "java" with title "Testing Framework Technology Stack"' \ + -e 'button returned of result') + fi + log_msg_to "✅ Selected Tech Stack: $tech_stack" + log_info "Tech Stack: $tech_stack" + + export TECH_STACK="$tech_stack" + log_msg_to "Exported TECH_STACK=$TECH_STACK" +} + + +# ===== URL Management ===== +get_test_url() { + local test_url=$DEFAULT_TEST_URL + + test_url=$(osascript -e 'Tell application "System Events" to display dialog "Enter the URL you want to test with BrowserStack:\n(Leave blank for default: '"$DEFAULT_TEST_URL"')" default answer "" with title "Test URL Setup" buttons {"OK"} default button "OK"' \ + -e 'text returned of result') + + if [ -n "$test_url" ]; then + log_msg_to "🌐 Using custom test URL: $test_url" + log_info "🌐 Using custom test URL: $test_url" + else + test_url="$DEFAULT_TEST_URL" + log_msg_to "⚠️ No URL entered. Falling back to default: $test_url" + log_info "No URL entered. Falling back to default: $test_url" + fi + + export CX_TEST_URL="$test_url" + log_msg_to "Exported TEST_URL=$TEST_URL" +} + + +get_test_type() { + local run_mode=$1 + local test_type="" + if [[ "$RUN_MODE" == *"--silent"* || "$RUN_MODE" == *"--debug"* ]]; then + test_type=$TT + log_msg_to "✅ Selected Testing Type from environment: $TEST_TYPE" + else + test_type=$(osascript -e 'Tell application "System Events" to display dialog "Select testing type:" buttons {"web", "app", "both"} default button "web" with title "Testing Type"' \ + -e 'button returned of result') + log_msg_to "✅ Selected Testing Type: $TEST_TYPE" + RUN_MODE=$test_type + log_info "Run Mode: ${RUN_MODE:-default}" + fi + export TEST_TYPE="$test_type" + log_msg_to "Exported TEST_TYPE=$TEST_TYPE" +} + +perform_next_steps_based_on_test_type() { + local test_type=$1 + case "$test_type" in + "web") + get_test_url + ;; + "app") + get_upload_app + ;; + "both") + get_test_url + get_upload_app + ;; + esac +} + +get_upload_app() { + log_msg_to "Determining app upload steps." + handle_app_upload +} \ No newline at end of file From a5e6a1d40ad08553b99e3a7889f2a38385a30125 Mon Sep 17 00:00:00 2001 From: Samiran Saha Date: Sat, 15 Nov 2025 11:29:04 +0530 Subject: [PATCH 02/17] fixes + github actions --- .github/workflows/test-scripts.yml | 409 +++++++++++++++++++++++++++++ mac/env-setup-run.sh | 54 +++- mac/user-interaction.sh | 3 +- 3 files changed, 458 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/test-scripts.yml diff --git a/.github/workflows/test-scripts.yml b/.github/workflows/test-scripts.yml new file mode 100644 index 0000000..ad3020f --- /dev/null +++ b/.github/workflows/test-scripts.yml @@ -0,0 +1,409 @@ +name: Test Scripts - Mac and Windows + +on: + schedule: + # Run daily at 2 AM UTC + - cron: '0 2 * * *' + pull_request: + branches: + - main + workflow_dispatch: + +jobs: + test-mac: + name: Test mac/run.sh on macOS + runs-on: macos-latest + timeout-minutes: 30 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Bash + run: | + echo "Bash version:" + bash --version + + - name: Validate shell script syntax + run: | + echo "Validating mac/run.sh syntax..." + bash -n mac/run.sh + echo "✅ mac/run.sh syntax is valid" + + - name: Validate supporting scripts syntax + run: | + echo "Validating supporting scripts..." + bash -n mac/common-utils.sh + bash -n mac/logging-utils.sh + bash -n mac/env-setup-run.sh + bash -n mac/user-interaction.sh + bash -n mac/env-prequisite-checks.sh + echo "✅ All supporting scripts are valid" + + - name: Check if scripts are executable + run: | + chmod +x mac/run.sh + chmod +x mac/common-utils.sh + chmod +x mac/logging-utils.sh + chmod +x mac/env-setup-run.sh + chmod +x mac/user-interaction.sh + chmod +x mac/env-prequisite-checks.sh + echo "✅ All scripts are executable" + + - name: Run ShellCheck on mac scripts + run: | + brew install shellcheck + echo "Running ShellCheck on mac scripts..." + shellcheck -x mac/run.sh || true + shellcheck -x mac/common-utils.sh || true + shellcheck -x mac/logging-utils.sh || true + shellcheck -x mac/env-setup-run.sh || true + shellcheck -x mac/user-interaction.sh || true + shellcheck -x mac/env-prequisite-checks.sh || true + echo "✅ ShellCheck analysis complete" + + - name: Verify required dependencies + run: | + echo "Checking required dependencies..." + command -v bash && echo "✅ bash found" + command -v curl && echo "✅ curl found" + command -v git && echo "✅ git found" + command -v bc && echo "✅ bc found" + echo "All required dependencies are available" + + - name: Test script sourcing (dry run) + run: | + set -e + echo "Testing script sourcing..." + bash -c "source mac/common-utils.sh && echo '✅ common-utils.sh sourced successfully'" + echo "✅ Script sourcing successful" + + - name: Integration Test - Silent Mode Execution + if: success() + env: + BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} + BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} + TURL: https://bstackdemo.com + run: | + echo "Running integration tests in silent mode..." + + # Set default values if secrets are not provided + BROWSERSTACK_USERNAME="${BROWSERSTACK_USERNAME:-test_user}" + BROWSERSTACK_ACCESS_KEY="${BROWSERSTACK_ACCESS_KEY:-test_key}" + + export BROWSERSTACK_USERNAME + export BROWSERSTACK_ACCESS_KEY + export TURL + + # Test configurations + test_configs=( + "web java" + "app java" + "web python" + "app python" + "web nodejs" + "app nodejs" + ) + + for config in "${test_configs[@]}"; do + read -r test_type tech_stack <<< "$config" + echo "================================" + echo "Testing: mac/run.sh --silent $test_type $tech_stack" + echo "================================" + + # Run with timeout and capture exit code (non-blocking) + timeout 60 bash mac/run.sh --silent "$test_type" "$tech_stack" >> "/tmp/run_test_${test_type}_${tech_stack}.log" 2>&1 & + job_pid=$! + wait "$job_pid" || exit_code=$? + + if [ -z "$exit_code" ] || [ "$exit_code" -eq 0 ] || [ "$exit_code" -eq 124 ]; then + echo "✅ mac/run.sh --silent $test_type $tech_stack completed (exit code: ${exit_code:-0})" + else + echo "⚠️ mac/run.sh --silent $test_type $tech_stack exited with code: $exit_code" + if [ -f "/tmp/run_test_${test_type}_${tech_stack}.log" ]; then + echo "Log output (last 20 lines):" + tail -n 20 "/tmp/run_test_${test_type}_${tech_stack}.log" + fi + fi + unset exit_code + done + + echo "✅ All integration tests completed" + + test-windows: + name: Test win/run.ps1 on Windows + runs-on: windows-latest + timeout-minutes: 30 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Check PowerShell version + run: | + $PSVersionTable.PSVersion + Write-Host "✅ PowerShell version check complete" + + - name: Validate PowerShell script syntax + run: | + Write-Host "Validating win/run.ps1 syntax..." + $ScriptPath = "win/run.ps1" + $null = [System.Management.Automation.PSParser]::Tokenize((Get-Content $ScriptPath), [ref]$null) + Write-Host "✅ win/run.ps1 syntax is valid" + + - name: Validate supporting PowerShell scripts syntax + run: | + Write-Host "Validating supporting PowerShell scripts..." + $Scripts = @("win/proxy-check.ps1") + foreach ($Script in $Scripts) { + $null = [System.Management.Automation.PSParser]::Tokenize((Get-Content $Script), [ref]$null) + Write-Host "✅ $Script syntax is valid" + } + + - name: Run PSScriptAnalyzer + run: | + Write-Host "Installing PSScriptAnalyzer..." + Install-Module -Name PSScriptAnalyzer -Force -SkipPublisherCheck -ErrorAction SilentlyContinue + Write-Host "Running PSScriptAnalyzer..." + Invoke-ScriptAnalyzer -Path "win/run.ps1" -Recurse -ReportSummary || $true + Write-Host "✅ PSScriptAnalyzer analysis complete" + + - name: Check script file encoding + run: | + Write-Host "Checking PowerShell script encoding..." + $ScriptPath = "win/run.ps1" + $Encoding = (Get-Item $ScriptPath).EncodingInfo + Write-Host "File encoding: $Encoding" + Write-Host "✅ Encoding check complete" + + - name: Verify required dependencies + run: | + Write-Host "Checking required dependencies..." + if (Get-Command curl.exe -ErrorAction SilentlyContinue) { Write-Host "✅ curl found" } + if (Get-Command git.exe -ErrorAction SilentlyContinue) { Write-Host "✅ git found" } + Write-Host "✅ PowerShell dependencies verified" + + - name: Integration Test - Silent Mode Execution + if: success() + env: + BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} + BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} + TURL: https://bstackdemo.com + run: | + Write-Host "Running integration tests in silent mode..." + + # Set default values if secrets are not provided + $BrowserStackUsername = if ($env:BROWSERSTACK_USERNAME) { $env:BROWSERSTACK_USERNAME } else { "test_user" } + $BrowserStackAccessKey = if ($env:BROWSERSTACK_ACCESS_KEY) { $env:BROWSERSTACK_ACCESS_KEY } else { "test_key" } + $TestUrl = $env:TURL + + # Export environment variables + $env:BROWSERSTACK_USERNAME = $BrowserStackUsername + $env:BROWSERSTACK_ACCESS_KEY = $BrowserStackAccessKey + $env:TURL = $TestUrl + + # Test configurations + $testConfigs = @( + @("web", "java"), + @("app", "java"), + @("web", "python"), + @("app", "python"), + @("web", "nodejs"), + @("app", "nodejs") + ) + + foreach ($config in $testConfigs) { + $testType = $config[0] + $techStack = $config[1] + + Write-Host "================================" + Write-Host "Testing: .\win\run.ps1 --silent $testType $techStack" + Write-Host "================================" + + # Create log file path + $logPath = "C:\Temp\run_test_${testType}_${techStack}.log" + New-Item -ItemType Directory -Path "C:\Temp" -Force -ErrorAction SilentlyContinue | Out-Null + + # Run with timeout (using job for timeout capability) + $job = Start-Job -ScriptBlock { + param($path, $testType, $techStack, $logPath) + & $path --silent $testType $techStack 2>&1 | Tee-Object -FilePath $logPath -Append + } -ArgumentList ".\win\run.ps1", $testType, $techStack, $logPath + + # Wait for job with 60 second timeout + $timeout = New-TimeSpan -Seconds 60 + $completed = Wait-Job -Job $job -Timeout 60 + + if ($completed) { + $result = Receive-Job -Job $job + if ($job.State -eq "Completed") { + Write-Host "✅ .\win\run.ps1 --silent $testType $techStack completed successfully" + } else { + Write-Host "⚠️ .\win\run.ps1 --silent $testType $techStack exited with state: $($job.State)" + if (Test-Path $logPath) { + Write-Host "Log output (last 20 lines):" + Get-Content -Path $logPath -Tail 20 + } + } + } else { + Write-Host "⚠️ .\win\run.ps1 --silent $testType $techStack timed out after 60 seconds" + Stop-Job -Job $job + if (Test-Path $logPath) { + Write-Host "Log output (last 20 lines):" + Get-Content -Path $logPath -Tail 20 + } + } + + Remove-Job -Job $job -Force + } + + Write-Host "✅ All integration tests completed" + + test-linux: + name: Test mac/run.sh on Linux + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Bash + run: | + echo "Bash version:" + bash --version + + - name: Validate shell script syntax + run: | + echo "Validating mac/run.sh syntax..." + bash -n mac/run.sh + echo "✅ mac/run.sh syntax is valid" + + - name: Validate supporting scripts syntax + run: | + echo "Validating supporting scripts..." + bash -n mac/common-utils.sh + bash -n mac/logging-utils.sh + bash -n mac/env-setup-run.sh + bash -n mac/user-interaction.sh + bash -n mac/env-prequisite-checks.sh + echo "✅ All supporting scripts are valid" + + - name: Check if scripts are executable + run: | + chmod +x mac/run.sh + chmod +x mac/common-utils.sh + chmod +x mac/logging-utils.sh + chmod +x mac/env-setup-run.sh + chmod +x mac/user-interaction.sh + chmod +x mac/env-prequisite-checks.sh + echo "✅ All scripts are executable" + + - name: Install ShellCheck + run: | + echo "Installing ShellCheck..." + sudo apt-get update + sudo apt-get install -y shellcheck + shellcheck --version + + - name: Run ShellCheck on mac scripts + run: | + echo "Running ShellCheck on mac scripts..." + shellcheck -x mac/run.sh || true + shellcheck -x mac/common-utils.sh || true + shellcheck -x mac/logging-utils.sh || true + shellcheck -x mac/env-setup-run.sh || true + shellcheck -x mac/user-interaction.sh || true + shellcheck -x mac/env-prequisite-checks.sh || true + echo "✅ ShellCheck analysis complete" + + - name: Verify required dependencies + run: | + echo "Checking required dependencies..." + command -v bash && echo "✅ bash found" + command -v curl && echo "✅ curl found" + command -v git && echo "✅ git found" + command -v bc && echo "✅ bc found" + echo "All required dependencies are available" + + - name: Test script sourcing (dry run) + run: | + set -e + echo "Testing script sourcing..." + bash -c "source mac/common-utils.sh && echo '✅ common-utils.sh sourced successfully'" + echo "✅ Script sourcing successful" + + - name: Integration Test - Silent Mode Execution + if: success() + env: + BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} + BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} + TURL: https://bstackdemo.com + run: | + echo "Running integration tests in silent mode..." + + # Set default values if secrets are not provided + BROWSERSTACK_USERNAME="${BROWSERSTACK_USERNAME:-test_user}" + BROWSERSTACK_ACCESS_KEY="${BROWSERSTACK_ACCESS_KEY:-test_key}" + + export BROWSERSTACK_USERNAME + export BROWSERSTACK_ACCESS_KEY + export TURL + + # Test configurations + test_configs=( + "web java" + "app java" + "web python" + "app python" + "web nodejs" + "app nodejs" + ) + + for config in "${test_configs[@]}"; do + read -r test_type tech_stack <<< "$config" + echo "================================" + echo "Testing: mac/run.sh --silent $test_type $tech_stack" + echo "================================" + + # Run with timeout and capture exit code (non-blocking) + timeout 60 bash mac/run.sh --silent "$test_type" "$tech_stack" >> "/tmp/run_test_${test_type}_${tech_stack}.log" 2>&1 & + job_pid=$! + wait "$job_pid" || exit_code=$? + + if [ -z "$exit_code" ] || [ "$exit_code" -eq 0 ] || [ "$exit_code" -eq 124 ]; then + echo "✅ mac/run.sh --silent $test_type $tech_stack completed (exit code: ${exit_code:-0})" + else + echo "⚠️ mac/run.sh --silent $test_type $tech_stack exited with code: $exit_code" + if [ -f "/tmp/run_test_${test_type}_${tech_stack}.log" ]; then + echo "Log output (last 20 lines):" + tail -n 20 "/tmp/run_test_${test_type}_${tech_stack}.log" + fi + fi + unset exit_code + done + + echo "✅ All integration tests completed" + + test-summary: + name: Test Summary + runs-on: ubuntu-latest + needs: [test-mac, test-windows, test-linux] + if: always() + steps: + - name: Check test results + run: | + echo "=== Test Results Summary ===" + echo "macOS Tests: ${{ needs.test-mac.result }}" + echo "Windows Tests: ${{ needs.test-windows.result }}" + echo "Linux Tests: ${{ needs.test-linux.result }}" + + if [ "${{ needs.test-mac.result }}" = "failure" ] || [ "${{ needs.test-windows.result }}" = "failure" ] || [ "${{ needs.test-linux.result }}" = "failure" ]; then + echo "❌ Some tests failed" + exit 1 + fi + echo "✅ All tests passed!" + + - name: Notify success + if: success() + run: | + echo "✅ All script validations passed successfully!" + echo "- mac/run.sh and supporting scripts validated on macOS and Linux" + echo "- win/run.ps1 and supporting scripts validated on Windows" diff --git a/mac/env-setup-run.sh b/mac/env-setup-run.sh index c26c3f8..7482905 100644 --- a/mac/env-setup-run.sh +++ b/mac/env-setup-run.sh @@ -37,21 +37,18 @@ setup_environment() { "setup_${setup_type}_java" "$local_flag" "$parallels_per_platform" "$NOW_RUN_LOG_FILE" log_section "✅ Results" run_status=$(identify_run_status_java "$NOW_RUN_LOG_FILE") - log_info "Fetching results..." check_return_value $? "$NOW_RUN_LOG_FILE" "${setup_type} setup succeeded." "❌ ${setup_type} setup failed. Check $log_file for details" ;; python) "setup_${setup_type}_python" "$local_flag" "$parallels_per_platform" "$NOW_RUN_LOG_FILE" log_section "✅ Results" run_status=$(identify_run_status_python "$NOW_RUN_LOG_FILE") - log_info "Fetching results..." check_return_value $? "$NOW_RUN_LOG_FILE" "${setup_type} setup succeeded." "❌ ${setup_type} setup failed. Check $log_file for details" ;; nodejs) "setup_${setup_type}_nodejs" "$local_flag" "$parallels_per_platform" "$NOW_RUN_LOG_FILE" log_section "✅ Results" run_status=$(identify_run_status_nodejs "$NOW_RUN_LOG_FILE") - log_info "Fetching results..." check_return_value $? "$NOW_RUN_LOG_FILE" "${setup_type} setup succeeded." "❌ ${setup_type} setup failed. Check $log_file for details" ;; *) @@ -100,11 +97,21 @@ EOF export BSTACK_PARALLELS=$parallels # to be picked up from ENV in repo export BSTACK_PLATFORMS=$platform_yaml export BROWSERSTACK_LOCAL=$local_flag # to be picked up from ENV in repo + # === 6️⃣ Build and Run === log_msg_to "⚙️ Running 'mvn install -DskipTests'" log_info "Installing dependencies" mvn install -DskipTests >> $NOW_RUN_LOG_FILE 2>&1 || return 1 log_success "Dependencies installed" + + + log_section "Validate Environment Variables" + log_info "BrowserStack Username: $BROWSERSTACK_USERNAME" + log_info "Web Application Endpoint: $CX_TEST_URL" + log_info "BrowserStack Local Flag: $BROWSERSTACK_LOCAL" + log_info "Parallels per platform: $BSTACK_PARALLELS" + log_info "Platforms: \n$BSTACK_PLATFORMS" + print_tests_running_log_section "mvn test -P sample-test" log_msg_to "🚀 Running 'mvn test -P sample-test'. This could take a few minutes. Follow the Automaton build here: https://automation.browserstack.com/" @@ -159,6 +166,13 @@ EOF return 1 # Fail the function if clean fails fi log_success "Dependencies installed" + + log_section "Validate Environment Variables" + log_info "BrowserStack Username: $BROWSERSTACK_USERNAME" + log_info "Native App Endpoint: $BROWSERSTACK_APP" + log_info "BrowserStack Local Flag: $BROWSERSTACK_LOCAL" + log_info "Parallels per platform: $BSTACK_PARALLELS" + log_info "Platforms: \n$BSTACK_PLATFORMS" log_msg_to "🚀 Running 'mvn test -P sample-test'. This could take a few minutes. Follow the Automaton build here: https://automation.browserstack.com/" print_tests_running_log_section "mvn test -P sample-test" @@ -205,6 +219,13 @@ EOF export BROWSERSTACK_LOCAL=$local_flag report_bstack_local_status "$local_flag" + + log_section "Validate Environment Variables" + log_info "BrowserStack Username: $BROWSERSTACK_USERNAME" + log_info "Web Application Endpoint: $CX_TEST_URL" + log_info "BrowserStack Local Flag: $BROWSERSTACK_LOCAL" + log_info "Parallels per platform: $BSTACK_PARALLELS" + log_info "Platforms: \n$BSTACK_PLATFORMS" print_tests_running_log_section "browserstack-sdk pytest -s src/test/suites/*.py --browserstack.config ./src/conf/browserstack_parallel.yml" @@ -261,6 +282,13 @@ EOF fi export BROWSERSTACK_LOCAL=true + + log_section "Validate Environment Variables" + log_info "BrowserStack Username: $BROWSERSTACK_USERNAME" + log_info "Native App Endpoint: $BROWSERSTACK_APP" + log_info "BrowserStack Local Flag: $BROWSERSTACK_LOCAL" + log_info "Parallels per platform: $BSTACK_PARALLELS" + log_info "Platforms: \n$BSTACK_PLATFORMS" print_tests_running_log_section "browserstack-sdk pytest -s bstack-sample.py" # Run pytest with BrowserStack SDK from the chosen platform directory @@ -282,7 +310,7 @@ setup_web_nodejs() { TARGET_DIR="$WORKSPACE_DIR/$PROJECT_FOLDER/$REPO" mkdir -p "$WORKSPACE_DIR/$PROJECT_FOLDER" - clone_repository $REPO $TARGET_DIR + clone_repository $REPO $TARGET_DIR "test" # === 2️⃣ Install Dependencies === @@ -304,8 +332,13 @@ setup_web_nodejs() { export BROWSERSTACK_LOCAL=$local_flag report_bstack_local_status "$local_flag" - - + + log_section "Validate Environment Variables" + log_info "BrowserStack Username: $BROWSERSTACK_USERNAME" + log_info "Web Application Endpoint: $CX_TEST_URL" + log_info "BrowserStack Local Flag: $BROWSERSTACK_LOCAL" + log_info "Parallels per platform: $BSTACK_PARALLELS" + log_info "Platforms: \n$BSTACK_CAPS_JSON" # === 8️⃣ Run Tests === log_msg_to "🚀 Running 'npm run test'. This could take a few minutes. Follow the Automaton build here: https://automation.browserstack.com/" @@ -330,7 +363,7 @@ setup_app_nodejs() { mkdir -p "$WORKSPACE_DIR/$PROJECT_FOLDER" REPO="now-webdriverio-appium-app-browserstack" TARGET_DIR="$WORKSPACE_DIR/$PROJECT_FOLDER/$REPO" - $TEST_FOLDER="/test" + TEST_FOLDER="/test" clone_repository $REPO $TARGET_DIR $TEST_FOLDER @@ -350,6 +383,13 @@ setup_app_nodejs() { export BSTACK_PARALLELS=$parallels export BROWSERSTACK_LOCAL=true export BROWSERSTACK_APP=$app_url + + log_section "Validate Environment Variables" + log_info "BrowserStack Username: $BROWSERSTACK_USERNAME" + log_info "Native App Endpoint: $BROWSERSTACK_APP" + log_info "BrowserStack Local Flag: $BROWSERSTACK_LOCAL" + log_info "Parallels per platform: $BSTACK_PARALLELS" + log_info "Platforms: \n$BSTACK_CAPS_JSON" # === 8️⃣ Run Tests === log_msg_to "🚀 Running 'npm run test'. This could take a few minutes. Follow the Automaton build here: https://automation.browserstack.com/" diff --git a/mac/user-interaction.sh b/mac/user-interaction.sh index 7e2fe68..9ddf908 100644 --- a/mac/user-interaction.sh +++ b/mac/user-interaction.sh @@ -3,6 +3,8 @@ # ===== Credential Management ===== get_browserstack_credentials() { local run_mode=$1 + local username="" + local access_key="" if [[ "$RUN_MODE" == *"--silent"* || "$RUN_MODE" == *"--debug"* ]]; then username="$BROWSERSTACK_USERNAME" access_key="$BROWSERSTACK_ACCESS_KEY" @@ -28,7 +30,6 @@ get_browserstack_credentials() { log_info "BrowserStack credentials captured from user: $username" fi - return 0 } From cdca21f46440bbf66b80269312598ce6c6b28c3d Mon Sep 17 00:00:00 2001 From: Samiran Saha Date: Sat, 15 Nov 2025 11:32:09 +0530 Subject: [PATCH 03/17] github actions permission flag --- .github/workflows/test-scripts.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test-scripts.yml b/.github/workflows/test-scripts.yml index ad3020f..7230aa2 100644 --- a/.github/workflows/test-scripts.yml +++ b/.github/workflows/test-scripts.yml @@ -1,4 +1,6 @@ -name: Test Scripts - Mac and Windows +name: Test Scripts - Mac, Linux and Windows +permissions: + contents: read on: schedule: From 1c0f087cfe105ec6c19b64cf1b76f884ff92304b Mon Sep 17 00:00:00 2001 From: Samiran Saha Date: Sat, 15 Nov 2025 11:53:54 +0530 Subject: [PATCH 04/17] fix for timeout error --- .github/workflows/test-scripts.yml | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/.github/workflows/test-scripts.yml b/.github/workflows/test-scripts.yml index 7230aa2..d53bed7 100644 --- a/.github/workflows/test-scripts.yml +++ b/.github/workflows/test-scripts.yml @@ -112,10 +112,26 @@ jobs: echo "Testing: mac/run.sh --silent $test_type $tech_stack" echo "================================" - # Run with timeout and capture exit code (non-blocking) - timeout 60 bash mac/run.sh --silent "$test_type" "$tech_stack" >> "/tmp/run_test_${test_type}_${tech_stack}.log" 2>&1 & + # Run with timeout and capture exit code (macOS compatible) + bash mac/run.sh --silent "$test_type" "$tech_stack" >> "/tmp/run_test_${test_type}_${tech_stack}.log" 2>&1 & job_pid=$! - wait "$job_pid" || exit_code=$? + + # Wait with 60 second timeout using sleep loop (macOS compatible) + count=0 + max_wait=60 + while kill -0 "$job_pid" 2>/dev/null && [ $count -lt $max_wait ]; do + sleep 1 + count=$((count + 1)) + done + + # Check if process is still running after timeout + if kill -0 "$job_pid" 2>/dev/null; then + echo "⚠️ mac/run.sh --silent $test_type $tech_stack timed out after 60 seconds" + kill -9 "$job_pid" 2>/dev/null || true + exit_code=124 + else + wait "$job_pid" || exit_code=$? + fi if [ -z "$exit_code" ] || [ "$exit_code" -eq 0 ] || [ "$exit_code" -eq 124 ]; then echo "✅ mac/run.sh --silent $test_type $tech_stack completed (exit code: ${exit_code:-0})" @@ -134,7 +150,6 @@ jobs: test-windows: name: Test win/run.ps1 on Windows runs-on: windows-latest - timeout-minutes: 30 steps: - name: Checkout code uses: actions/checkout@v4 @@ -365,7 +380,7 @@ jobs: echo "Testing: mac/run.sh --silent $test_type $tech_stack" echo "================================" - # Run with timeout and capture exit code (non-blocking) + # Run with timeout and capture exit code (using timeout command on Linux) timeout 60 bash mac/run.sh --silent "$test_type" "$tech_stack" >> "/tmp/run_test_${test_type}_${tech_stack}.log" 2>&1 & job_pid=$! wait "$job_pid" || exit_code=$? From cc41163f5c6f5cf9eccf07688fe415ffc46735db Mon Sep 17 00:00:00 2001 From: Samiran Saha Date: Sat, 15 Nov 2025 12:02:01 +0530 Subject: [PATCH 05/17] define env: BrowserStack --- .github/workflows/test-scripts.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/test-scripts.yml b/.github/workflows/test-scripts.yml index d53bed7..48cd9cb 100644 --- a/.github/workflows/test-scripts.yml +++ b/.github/workflows/test-scripts.yml @@ -16,6 +16,7 @@ jobs: name: Test mac/run.sh on macOS runs-on: macos-latest timeout-minutes: 30 + environment: BrowserStack steps: - name: Checkout code uses: actions/checkout@v4 @@ -150,6 +151,7 @@ jobs: test-windows: name: Test win/run.ps1 on Windows runs-on: windows-latest + environment: BrowserStack steps: - name: Checkout code uses: actions/checkout@v4 @@ -278,6 +280,7 @@ jobs: name: Test mac/run.sh on Linux runs-on: ubuntu-latest timeout-minutes: 30 + environment: BrowserStack steps: - name: Checkout code uses: actions/checkout@v4 From 98cc0f3fd04f963883d93e56f20d3dfb0af4e04e Mon Sep 17 00:00:00 2001 From: Samiran Saha Date: Sat, 15 Nov 2025 12:10:23 +0530 Subject: [PATCH 06/17] increase timeout: 600 secs --- .github/workflows/test-scripts.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/test-scripts.yml b/.github/workflows/test-scripts.yml index 48cd9cb..01cfad9 100644 --- a/.github/workflows/test-scripts.yml +++ b/.github/workflows/test-scripts.yml @@ -15,7 +15,7 @@ jobs: test-mac: name: Test mac/run.sh on macOS runs-on: macos-latest - timeout-minutes: 30 + timeout-minutes: 60 environment: BrowserStack steps: - name: Checkout code @@ -117,9 +117,9 @@ jobs: bash mac/run.sh --silent "$test_type" "$tech_stack" >> "/tmp/run_test_${test_type}_${tech_stack}.log" 2>&1 & job_pid=$! - # Wait with 60 second timeout using sleep loop (macOS compatible) + # Wait with 600 second timeout using sleep loop (macOS compatible) count=0 - max_wait=60 + max_wait=600 while kill -0 "$job_pid" 2>/dev/null && [ $count -lt $max_wait ]; do sleep 1 count=$((count + 1)) @@ -127,7 +127,7 @@ jobs: # Check if process is still running after timeout if kill -0 "$job_pid" 2>/dev/null; then - echo "⚠️ mac/run.sh --silent $test_type $tech_stack timed out after 60 seconds" + echo "⚠️ mac/run.sh --silent $test_type $tech_stack timed out after 600 seconds" kill -9 "$job_pid" 2>/dev/null || true exit_code=124 else @@ -247,9 +247,9 @@ jobs: & $path --silent $testType $techStack 2>&1 | Tee-Object -FilePath $logPath -Append } -ArgumentList ".\win\run.ps1", $testType, $techStack, $logPath - # Wait for job with 60 second timeout - $timeout = New-TimeSpan -Seconds 60 - $completed = Wait-Job -Job $job -Timeout 60 + # Wait for job with 600 second timeout + $timeout = New-TimeSpan -Seconds 600 + $completed = Wait-Job -Job $job -Timeout 600 if ($completed) { $result = Receive-Job -Job $job @@ -263,7 +263,7 @@ jobs: } } } else { - Write-Host "⚠️ .\win\run.ps1 --silent $testType $techStack timed out after 60 seconds" + Write-Host "⚠️ .\win\run.ps1 --silent $testType $techStack timed out after 600 seconds" Stop-Job -Job $job if (Test-Path $logPath) { Write-Host "Log output (last 20 lines):" @@ -279,7 +279,7 @@ jobs: test-linux: name: Test mac/run.sh on Linux runs-on: ubuntu-latest - timeout-minutes: 30 + timeout-minutes: 60 environment: BrowserStack steps: - name: Checkout code @@ -384,7 +384,7 @@ jobs: echo "================================" # Run with timeout and capture exit code (using timeout command on Linux) - timeout 60 bash mac/run.sh --silent "$test_type" "$tech_stack" >> "/tmp/run_test_${test_type}_${tech_stack}.log" 2>&1 & + timeout 600 bash mac/run.sh --silent "$test_type" "$tech_stack" >> "/tmp/run_test_${test_type}_${tech_stack}.log" 2>&1 & job_pid=$! wait "$job_pid" || exit_code=$? From ef37bd59638e519a10a148ad1a64bbe52cecda9e Mon Sep 17 00:00:00 2001 From: Samiran Saha Date: Sat, 15 Nov 2025 12:47:35 +0530 Subject: [PATCH 07/17] web nodejs fix + upload artifacts --- .github/workflows/test-scripts.yml | 37 ++++++++++++++++++++++++++++-- mac/env-setup-run.sh | 2 +- 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test-scripts.yml b/.github/workflows/test-scripts.yml index 01cfad9..c6d6062 100644 --- a/.github/workflows/test-scripts.yml +++ b/.github/workflows/test-scripts.yml @@ -15,7 +15,7 @@ jobs: test-mac: name: Test mac/run.sh on macOS runs-on: macos-latest - timeout-minutes: 60 + timeout-minutes: 15 environment: BrowserStack steps: - name: Checkout code @@ -148,9 +148,20 @@ jobs: echo "✅ All integration tests completed" + - name: Upload BrowserStack Logs as Artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: browserstack-logs-macos + path: | + ~/.browserstack/NOW/logs/ + /tmp/run_test_*.log + retention-days: 30 + test-windows: name: Test win/run.ps1 on Windows runs-on: windows-latest + timeout-minutes: 15 environment: BrowserStack steps: - name: Checkout code @@ -276,10 +287,21 @@ jobs: Write-Host "✅ All integration tests completed" + - name: Upload BrowserStack Logs as Artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: browserstack-logs-windows + path: | + C:\Users\runneradmin\.browserstack\NOW\logs\ + C:\Temp\run_test_*.log + retention-days: 30 + if-no-files-found: ignore + test-linux: name: Test mac/run.sh on Linux runs-on: ubuntu-latest - timeout-minutes: 60 + timeout-minutes: 15 environment: BrowserStack steps: - name: Checkout code @@ -402,6 +424,17 @@ jobs: echo "✅ All integration tests completed" + - name: Upload BrowserStack Logs as Artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: browserstack-logs-linux + path: | + ~/.browserstack/NOW/logs/ + /tmp/run_test_*.log + retention-days: 30 + if-no-files-found: ignore + test-summary: name: Test Summary runs-on: ubuntu-latest diff --git a/mac/env-setup-run.sh b/mac/env-setup-run.sh index 7482905..361f7d2 100644 --- a/mac/env-setup-run.sh +++ b/mac/env-setup-run.sh @@ -310,7 +310,7 @@ setup_web_nodejs() { TARGET_DIR="$WORKSPACE_DIR/$PROJECT_FOLDER/$REPO" mkdir -p "$WORKSPACE_DIR/$PROJECT_FOLDER" - clone_repository $REPO $TARGET_DIR "test" + clone_repository $REPO $TARGET_DIR # === 2️⃣ Install Dependencies === From e9cecadd88afd5ebae05c0137d3c0694d288eebc Mon Sep 17 00:00:00 2001 From: Samiran Saha Date: Sat, 15 Nov 2025 13:17:33 +0530 Subject: [PATCH 08/17] log files corrections --- .github/workflows/test-scripts.yml | 17 +++++++++++++---- mac/env-setup-run.sh | 9 ++++++--- mac/run.sh | 4 ++-- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test-scripts.yml b/.github/workflows/test-scripts.yml index c6d6062..0310612 100644 --- a/.github/workflows/test-scripts.yml +++ b/.github/workflows/test-scripts.yml @@ -20,7 +20,10 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 - + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: '3.13' - name: Set up Bash run: | echo "Bash version:" @@ -166,7 +169,10 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 - + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: '3.13' - name: Check PowerShell version run: | $PSVersionTable.PSVersion @@ -306,7 +312,10 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 - + - name: Set up Python 3.13 + uses: actions/setup-python@v5 + with: + python-version: '3.13' - name: Set up Bash run: | echo "Bash version:" @@ -430,7 +439,7 @@ jobs: with: name: browserstack-logs-linux path: | - ~/.browserstack/NOW/logs/ + $HOME/.browserstack/NOW/logs/ /tmp/run_test_*.log retention-days: 30 if-no-files-found: ignore diff --git a/mac/env-setup-run.sh b/mac/env-setup-run.sh index 361f7d2..401c8ba 100644 --- a/mac/env-setup-run.sh +++ b/mac/env-setup-run.sh @@ -94,9 +94,9 @@ platforms: $platform_yaml EOF - export BSTACK_PARALLELS=$parallels # to be picked up from ENV in repo + export BSTACK_PARALLELS=$parallels export BSTACK_PLATFORMS=$platform_yaml - export BROWSERSTACK_LOCAL=$local_flag # to be picked up from ENV in repo + export BROWSERSTACK_LOCAL=$local_flag # === 6️⃣ Build and Run === log_msg_to "⚙️ Running 'mvn install -DskipTests'" @@ -156,6 +156,7 @@ EOF export BSTACK_PARALLELS=$parallels export BROWSERSTACK_LOCAL=true + export BSTACK_PLATFORMS=$platform_yaml # Run Maven install first @@ -204,7 +205,7 @@ setup_web_python() { # Update YAML at root level (browserstack.yml) export BROWSERSTACK_CONFIG_FILE="./src/conf/browserstack_parallel.yml" platform_yaml=$(generate_web_platforms "$TEAM_PARALLELS_MAX_ALLOWED_WEB" "yaml") - + export BSTACK_PLATFORMS=$platform_yaml cat >> "$BROWSERSTACK_CONFIG_FILE" < Date: Sat, 15 Nov 2025 13:33:42 +0530 Subject: [PATCH 09/17] log files corrections --- mac/common-utils.sh | 25 ++++--------- mac/env-prequisite-checks.sh | 2 +- mac/env-setup-run.sh | 69 +++++++++++++----------------------- mac/logging-utils.sh | 2 -- mac/run.sh | 24 ++++++------- 5 files changed, 45 insertions(+), 77 deletions(-) diff --git a/mac/common-utils.sh b/mac/common-utils.sh index 40a2d95..981e1e0 100644 --- a/mac/common-utils.sh +++ b/mac/common-utils.sh @@ -14,10 +14,6 @@ LOG_DIR="$WORKSPACE_DIR/$PROJECT_FOLDER/logs" NOW_RUN_LOG_FILE="" # ===== Global Variables ===== -USERNAME="" -ACCESS_KEY="" -TEST_TYPE="" # Web / App / Both -TECH_STACK="" # Java / Python / JS CX_TEST_URL="$DEFAULT_TEST_URL" WEB_PLAN_FETCHED=false @@ -26,7 +22,6 @@ TEAM_PARALLELS_MAX_ALLOWED_WEB=0 TEAM_PARALLELS_MAX_ALLOWED_MOBILE=0 # App specific globals -APP_URL="" APP_PLATFORM="" # ios | android | all @@ -133,8 +128,6 @@ upload_sample_app() { } upload_custom_app() { - local -n app_url=$1 - local -n platform=$2 local app_platform="" local file_path file_path=$(osascript -e 'choose file with prompt "Select your .apk or .ipa file:" of type {"apk", "ipa"}' 2>/dev/null) @@ -146,10 +139,8 @@ upload_custom_app() { # Determine platform from file extension if [[ "$file_path" == *.ipa ]]; then - platform="ios" app_platform="ios" elif [[ "$file_path" == *.apk ]]; then - platform="android" app_platform="android" else log_msg_to "❌ Invalid file type. Must be .apk or .ipa" @@ -162,6 +153,7 @@ upload_custom_app() { -X POST "https://api-cloud.browserstack.com/app-automate/upload" \ -F "file=@$file_path") + local app_url app_url=$(echo "$upload_response" | grep -o '"app_url":"[^"]*' | cut -d'"' -f4) if [ -z "$app_url" ]; then log_msg_to "❌ Failed to upload app" @@ -182,8 +174,9 @@ generate_web_platforms() { local max_total_parallels=$1 local platformsListContentFormat=$2 local platform="web" + local platformsList="" export NOW_PLATFORM="$platform" - local platformsList=$(pick_terminal_devices "$NOW_PLATFORM" $max_total_parallels "$platformsListContentFormat") + platformsList=$(pick_terminal_devices "$NOW_PLATFORM" "$max_total_parallels" "$platformsListContentFormat") echo "$platformsList" } @@ -191,7 +184,8 @@ generate_mobile_platforms() { local max_total_parallels=$1 local platformsListContentFormat=$2 local app_platform="$APP_PLATFORM" - local platformsList=$(pick_terminal_devices "$app_platform" $max_total_parallels, "$platformsListContentFormat") + local platformsList="" + platformsList=$(pick_terminal_devices "$app_platform" "$max_total_parallels", "$platformsListContentFormat") echo "$platformsList" } @@ -317,7 +311,8 @@ identify_run_status_nodejs() { local log_file=$1 log_info "Identifying run status" - local line=$(grep -m 1 -E "Spec Files:.*passed.*total" < "$log_file") + local line="" + line=$(grep -m 1 -E "Spec Files:.*passed.*total" < "$log_file") # If not found, fail if [[ -z "$line" ]]; then log_warn "❌ No test summary line found." @@ -334,8 +329,6 @@ identify_run_status_nodejs() { log_error "❌ Error: No tests passed" return 1 fi - - return 1 } @@ -349,8 +342,6 @@ identify_run_status_python() { echo "✅ Total Passed: $passed_sum" - local completed_test_count=passed_sum+warning_sum - # If not found, fail if [[ -z "$passed_sum" ]]; then log_warn "❌ No test summary line found." @@ -365,8 +356,6 @@ identify_run_status_python() { log_error "❌ Error: No tests completed" return 1 fi - - return 1 } clear_old_logs() { diff --git a/mac/env-prequisite-checks.sh b/mac/env-prequisite-checks.sh index 0923392..3e3ef11 100755 --- a/mac/env-prequisite-checks.sh +++ b/mac/env-prequisite-checks.sh @@ -17,7 +17,7 @@ parse_proxy() { p="${p#http://}" p="${p#https://}" # strip credentials if any user:pass@ - p="${p#*[@]}" + p="${p#*@}" # extract host and port export PROXY_HOST="${p%%:*}" diff --git a/mac/env-setup-run.sh b/mac/env-setup-run.sh index 401c8ba..159583f 100644 --- a/mac/env-setup-run.sh +++ b/mac/env-setup-run.sh @@ -1,9 +1,10 @@ -# Common setup function for both web and mobile +#!/usr/bin/env bash +# shellcheck shell=bash + setup_environment() { local setup_type=$1 # "web" or "mobile" local tech_stack=$2 local max_parallels - local setup_function log_section "📦 Project Setup" @@ -19,8 +20,6 @@ setup_environment() { log_msg_to "Starting ${setup_type} setup for $TECH_STACK" "$NOW_RUN_LOG_FILE" local local_flag=false - local attempt=1 - local run_status=1 # Calculate parallels local total_parallels @@ -36,19 +35,19 @@ setup_environment() { java) "setup_${setup_type}_java" "$local_flag" "$parallels_per_platform" "$NOW_RUN_LOG_FILE" log_section "✅ Results" - run_status=$(identify_run_status_java "$NOW_RUN_LOG_FILE") + identify_run_status_java "$NOW_RUN_LOG_FILE" check_return_value $? "$NOW_RUN_LOG_FILE" "${setup_type} setup succeeded." "❌ ${setup_type} setup failed. Check $log_file for details" ;; python) "setup_${setup_type}_python" "$local_flag" "$parallels_per_platform" "$NOW_RUN_LOG_FILE" log_section "✅ Results" - run_status=$(identify_run_status_python "$NOW_RUN_LOG_FILE") + identify_run_status_python "$NOW_RUN_LOG_FILE" check_return_value $? "$NOW_RUN_LOG_FILE" "${setup_type} setup succeeded." "❌ ${setup_type} setup failed. Check $log_file for details" ;; nodejs) "setup_${setup_type}_nodejs" "$local_flag" "$parallels_per_platform" "$NOW_RUN_LOG_FILE" log_section "✅ Results" - run_status=$(identify_run_status_nodejs "$NOW_RUN_LOG_FILE") + identify_run_status_nodejs "$NOW_RUN_LOG_FILE" check_return_value $? "$NOW_RUN_LOG_FILE" "${setup_type} setup succeeded." "❌ ${setup_type} setup failed. Check $log_file for details" ;; *) @@ -67,7 +66,7 @@ setup_web_java() { mkdir -p "$WORKSPACE_DIR/$PROJECT_FOLDER" - clone_repository $REPO $TARGET_DIR + clone_repository "$REPO" "$TARGET_DIR" cd "$TARGET_DIR"|| return 1 @@ -101,7 +100,7 @@ EOF # === 6️⃣ Build and Run === log_msg_to "⚙️ Running 'mvn install -DskipTests'" log_info "Installing dependencies" - mvn install -DskipTests >> $NOW_RUN_LOG_FILE 2>&1 || return 1 + mvn install -DskipTests >> "$NOW_RUN_LOG_FILE" 2>&1 || return 1 log_success "Dependencies installed" @@ -115,13 +114,13 @@ EOF print_tests_running_log_section "mvn test -P sample-test" log_msg_to "🚀 Running 'mvn test -P sample-test'. This could take a few minutes. Follow the Automaton build here: https://automation.browserstack.com/" - mvn test -P sample-test >> $NOW_RUN_LOG_FILE 2>&1 & + mvn test -P sample-test >> "$NOW_RUN_LOG_FILE" 2>&1 & cmd_pid=$!|| return 1 show_spinner "$cmd_pid" wait "$cmd_pid" - cd "$WORKSPACE_DIR/$PROJECT_FOLDER" + cd "$WORKSPACE_DIR/$PROJECT_FOLDER" || return 1 return 0 } @@ -138,9 +137,9 @@ setup_app_java() { clone_repository $REPO $TARGET_DIR if [[ "$APP_PLATFORM" == "all" || "$APP_PLATFORM" == "android" ]]; then - cd android/testng-examples + cd "android/testng-examples" || return 1 else - cd ios/testng-examples + cd ios/testng-examples || return 1 fi @@ -162,7 +161,7 @@ EOF # Run Maven install first log_msg_to "⚙️ Running 'mvn clean'" log_info "Installing dependencies" - if ! mvn clean >> $NOW_RUN_LOG_FILE 2>&1; then + if ! mvn clean >> "$NOW_RUN_LOG_FILE" 2>&1; then log_msg_to "❌ 'mvn clean' FAILED. See $log_file for details." return 1 # Fail the function if clean fails fi @@ -177,7 +176,7 @@ EOF log_msg_to "🚀 Running 'mvn test -P sample-test'. This could take a few minutes. Follow the Automaton build here: https://automation.browserstack.com/" print_tests_running_log_section "mvn test -P sample-test" - mvn test -P sample-test >> $NOW_RUN_LOG_FILE 2>&1 & + mvn test -P sample-test >> "$NOW_RUN_LOG_FILE" 2>&1 & cmd_pid=$!|| return 1 show_spinner "$cmd_pid" @@ -195,11 +194,11 @@ setup_web_python() { REPO="browserstack-examples-pytest" TARGET_DIR="$WORKSPACE_DIR/$PROJECT_FOLDER/$REPO" - clone_repository $REPO $TARGET_DIR + clone_repository "$REPO" "$TARGET_DIR" detect_setup_python_env - pip3 install -r requirements.txt >> $NOW_RUN_LOG_FILE 2>&1 + pip3 install -r requirements.txt >> "$NOW_RUN_LOG_FILE" 2>&1 log_success "Dependencies installed" # Update YAML at root level (browserstack.yml) @@ -301,7 +300,7 @@ EOF ) deactivate - cd "$WORKSPACE_DIR/$PROJECT_FOLDER" + cd "$WORKSPACE_DIR/$PROJECT_FOLDER" || return 1 return 0 } @@ -352,7 +351,7 @@ setup_web_nodejs() { show_spinner "$cmd_pid" wait "$cmd_pid" - cd "$WORKSPACE_DIR/$PROJECT_FOLDER" + cd "$WORKSPACE_DIR/$PROJECT_FOLDER" || return 1 return 0 } @@ -397,7 +396,7 @@ setup_app_nodejs() { # === 8️⃣ Run Tests === log_msg_to "🚀 Running 'npm run test'. This could take a few minutes. Follow the Automaton build here: https://automation.browserstack.com/" print_tests_running_log_section "npm run test" - npm run test >> $NOW_RUN_LOG_FILE 2>&1 || return 1 & + npm run test >> "$NOW_RUN_LOG_FILE" 2>&1 || return 1 & cmd_pid=$!|| return 1 show_spinner "$cmd_pid" @@ -406,7 +405,7 @@ setup_app_nodejs() { # === 9️⃣ Wrap Up === log_msg_to "✅ Mobile JS setup and test execution completed successfully." - cd "$WORKSPACE_DIR/$PROJECT_FOLDER" + cd "$WORKSPACE_DIR/$PROJECT_FOLDER" || return 1 return 0 } @@ -415,10 +414,10 @@ clone_repository() { install_folder=$2 test_folder=$3 - rm -rf $install_folder + rm -rf "$install_folder" log_msg_to "📦 Cloning repo $repo_git into $install_folder" log_info "Cloning repository: $repo_git" - git clone https://github.com/BrowserStackCE/$repo_git.git "$install_folder" >> $NOW_RUN_LOG_FILE 2>&1 || return 1 + git clone https://github.com/BrowserStackCE/$repo_git.git "$install_folder" >> "$NOW_RUN_LOG_FILE" 2>&1 || return 1 log_msg_to "✅ Cloned repository: $repo_git into $install_folder" cd "$install_folder/$test_folder" || return 1 } @@ -432,36 +431,17 @@ run_setup_wrapper() { case "$test_type" in Web) if [ "$WEB_PLAN_FETCHED" == true ]; then - run_setup $test_type $tech_stack + run_setup "$test_type" "$tech_stack" else log_msg_to "⚠️ Skipping Web setup — Web plan not fetched" fi ;; App) if [ "$MOBILE_PLAN_FETCHED" == true ]; then - run_setup $test_type $tech_stack - else - log_msg_to "⚠️ Skipping Mobile setup — Mobile plan not fetched" - fi - ;; - Both) - local ran_any=false - if [ "$WEB_PLAN_FETCHED" == true ]; then - run_setup $test_type $tech_stack - ran_any=true - else - log_msg_to "⚠️ Skipping Web setup — Web plan not fetched" - fi - if [ "$MOBILE_PLAN_FETCHED" == true ]; then - run_setup $test_type $tech_stack - ran_any=true + run_setup "$test_type" "$tech_stack" else log_msg_to "⚠️ Skipping Mobile setup — Mobile plan not fetched" fi - if [ "$ran_any" == false ]; then - log_msg_to "❌ Both Web and Mobile setup were skipped. Exiting." - exit 1 - fi ;; *) log_msg_to "❌ Invalid TEST_TYPE: $TEST_TYPE" @@ -531,6 +511,7 @@ detect_setup_python_env() { exit 1 } + # shellcheck source=/dev/null source .venv/bin/activate log_success "Virtual environment created and activated." } diff --git a/mac/logging-utils.sh b/mac/logging-utils.sh index 2ac5b2b..2358b31 100644 --- a/mac/logging-utils.sh +++ b/mac/logging-utils.sh @@ -8,10 +8,8 @@ BOLD="\033[1m" RESET="\033[0m" GREEN="\033[32m" YELLOW="\033[33m" -BLUE="\033[34m" CYAN="\033[36m" RED="\033[31m" -GRAY="\033[90m" LIGHT_GRAY='\033[0;37m' # ============================================== diff --git a/mac/run.sh b/mac/run.sh index 06b237f..609d7fd 100755 --- a/mac/run.sh +++ b/mac/run.sh @@ -2,10 +2,15 @@ set -o pipefail # Import utilities +# shellcheck source=/dev/null source "$(dirname "$0")/common-utils.sh" +# shellcheck source=/dev/null source "$(dirname "$0")/logging-utils.sh" +# shellcheck source=/dev/null source "$(dirname "$0")/env-setup-run.sh" +# shellcheck source=/dev/null source "$(dirname "$0")/user-interaction.sh" +# shellcheck source=/dev/null source "$(dirname "$0")/env-prequisite-checks.sh" # ===== Web wrapper with retry logic (writes runtime logs to $NOW_RUN_LOG_FILE) ===== @@ -33,23 +38,23 @@ if [[ "$RUN_MODE" == *"--silent"* || "$RUN_MODE" == *"--debug"* ]]; then log_file="$LOG_DIR/${TEST_TYPE:unknown}_${TECH_STACK:unknown}_run_result.log" log_info "Run Mode: ${RUN_MODE:-default}" else - get_test_type $RUN_MODE - get_tech_stack $RUN_MODE + get_test_type "$RUN_MODE" + get_tech_stack "$RUN_MODE" log_file="$LOG_DIR/${TEST_TYPE:unknown}_${TECH_STACK:unknown}_run_result.log" - perform_next_steps_based_on_test_type $TEST_TYPE + perform_next_steps_based_on_test_type "$TEST_TYPE" fi log_info "Log file path: $log_file" export NOW_RUN_LOG_FILE="$log_file" setup_workspace -get_browserstack_credentials $RUN_MODE +get_browserstack_credentials "$RUN_MODE" log_section "⚙️ Platform & Tech Stack" log_info "Platform: ${TEST_TYPE:-N/A}" log_info "Tech Stack: ${TECH_STACK:-N/A}" -validate_tech_stack_installed $TECH_STACK -fetch_plan_details $TEST_TYPE +validate_tech_stack_installed "$TECH_STACK" +fetch_plan_details "$TEST_TYPE" if [[ "$RUN_MODE" == *"--silent"* || "$RUN_MODE" == *"--debug"* ]]; then if [[ $TEST_TYPE == "app" ]]; then @@ -67,9 +72,4 @@ log_info "Clearing old logs..." clear_old_logs log_info "Starting $TEST_TYPE setup for $TECH_STACK" -run_setup $TEST_TYPE $TECH_STACK -if [ $? -eq 0 ]; then - log_section "🎯 IN RUN.sh success!${RESET}" -else - log_section "🎯 IN RUN.sh failed ${RESET}" -fi +run_setup "$TEST_TYPE" "$TECH_STACK" From 76ba173efbc5b39111c5d6f7bde1e40e13700eba Mon Sep 17 00:00:00 2001 From: Samiran Saha Date: Sat, 15 Nov 2025 14:09:50 +0530 Subject: [PATCH 10/17] log files path --- .github/workflows/test-scripts.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-scripts.yml b/.github/workflows/test-scripts.yml index 0310612..a8d2632 100644 --- a/.github/workflows/test-scripts.yml +++ b/.github/workflows/test-scripts.yml @@ -157,7 +157,7 @@ jobs: with: name: browserstack-logs-macos path: | - ~/.browserstack/NOW/logs/ + /Users/runner/.browserstack/NOW/logs/ /tmp/run_test_*.log retention-days: 30 @@ -439,7 +439,7 @@ jobs: with: name: browserstack-logs-linux path: | - $HOME/.browserstack/NOW/logs/ + /home/runner/.browserstack/NOW/logs/ /tmp/run_test_*.log retention-days: 30 if-no-files-found: ignore From f8c8696909390b34eddba3e6dce0dc75d9110132 Mon Sep 17 00:00:00 2001 From: Samiran Saha Date: Sat, 15 Nov 2025 14:23:42 +0530 Subject: [PATCH 11/17] linter checks --- mac/common-utils.sh | 12 ++++++------ mac/env-prequisite-checks.sh | 4 ++-- mac/env-setup-run.sh | 36 ++++++++++++++++-------------------- mac/run.sh | 2 +- 4 files changed, 25 insertions(+), 29 deletions(-) diff --git a/mac/common-utils.sh b/mac/common-utils.sh index 981e1e0..2fb14df 100644 --- a/mac/common-utils.sh +++ b/mac/common-utils.sh @@ -1,5 +1,6 @@ #!/bin/bash +# shellcheck source=/dev/null source "$(dirname "$0")/device-machine-allocation.sh" # # ===== Global Variables ===== @@ -41,20 +42,21 @@ log_msg_to() { # write to dest file if provided if [ -n "$dest_file" ]; then mkdir -p "$(dirname "$dest_file")" - echo "$line" >> $NOW_RUN_LOG_FILE + echo "$line" >> "$NOW_RUN_LOG_FILE" fi } # Spinner function for long-running processes show_spinner() { local pid=$1 + # shellcheck disable=SC1003 local spin='|/-\' local i=0 local ts ts="$(date +"%Y-%m-%d %H:%M:%S")" while kill -0 "$pid" 2>/dev/null; do i=$(( (i+1) %4 )) - printf "\r⏳ Processing... ${spin:$i:1}" + printf "\r⏳ Processing... %s" "${spin:$i:1}" sleep 0.1 done echo "" @@ -119,7 +121,7 @@ upload_sample_app() { log_msg_to "Exported BROWSERSTACK_APP=$BROWSERSTACK_APP" if [ -z "$app_url" ]; then - log_msg_to "❌ Upload failed. Response: $UPLOAD_RESPONSE" + log_msg_to "❌ Upload failed. Response: $upload_response" return 1 fi @@ -292,7 +294,7 @@ identify_run_status_java() { skipped=$(echo "$line" | grep -m 1 -oE "Skipped: [0-9]+" | awk '{print $2}') # Calculate passed tests - passed=$(( $tests_run - ($failures + $errors + $skipped) )) + passed=$(( tests_run-(failures+errors+skipped) )) # Check condition if (( passed > 0 )); then @@ -302,8 +304,6 @@ identify_run_status_java() { log_error "Error: No tests passed (Tests run: $tests_run, Failures: $failures, Errors: $errors, Skipped: $skipped)" return 1 fi - - return 1 } diff --git a/mac/env-prequisite-checks.sh b/mac/env-prequisite-checks.sh index 3e3ef11..51aa5c2 100755 --- a/mac/env-prequisite-checks.sh +++ b/mac/env-prequisite-checks.sh @@ -26,7 +26,7 @@ parse_proxy() { set_proxy_in_env() { log_section "🌐 Network & Proxy Validation" - base64_encoded_creds=$(printf "%s" $BROWSERSTACK_USERNAME:$BROWSERSTACK_ACCESS_KEY | base64 | tr -d '\n') + base64_encoded_creds=$(printf "%s" "$BROWSERSTACK_USERNAME":"$BROWSERSTACK_ACCESS_KEY" | base64 | tr -d '\n') # If no proxy configured, exit early @@ -34,7 +34,7 @@ set_proxy_in_env() { log_warn "No proxy found. Using direct connection." export PROXY_HOST="" export PROXY_PORT="" - return 0 2>/dev/null || exit 0 + return 0 2>/dev/null fi log_msg_to "Proxy detected: $PROXY" diff --git a/mac/env-setup-run.sh b/mac/env-setup-run.sh index 159583f..f2aea2e 100644 --- a/mac/env-setup-run.sh +++ b/mac/env-setup-run.sh @@ -2,7 +2,7 @@ # shellcheck shell=bash setup_environment() { - local setup_type=$1 # "web" or "mobile" + local setup_type=$1 local tech_stack=$2 local max_parallels @@ -17,7 +17,7 @@ setup_environment() { max_parallels=$TEAM_PARALLELS_MAX_ALLOWED_MOBILE fi - log_msg_to "Starting ${setup_type} setup for $TECH_STACK" "$NOW_RUN_LOG_FILE" + log_msg_to "Starting ${setup_type} setup for " "$tech_stack" "$NOW_RUN_LOG_FILE" local local_flag=false @@ -134,7 +134,7 @@ setup_app_java() { local app_url=$BROWSERSTACK_APP log_msg_to "APP_PLATFORM: $APP_PLATFORM" >> "$NOW_RUN_LOG_FILE" 2>&1 - clone_repository $REPO $TARGET_DIR + clone_repository "$REPO" "$TARGET_DIR" if [[ "$APP_PLATFORM" == "all" || "$APP_PLATFORM" == "android" ]]; then cd "android/testng-examples" || return 1 @@ -145,7 +145,7 @@ setup_app_java() { # YAML config path export BROWSERSTACK_CONFIG_FILE="./browserstack.yml" - platform_yaml=$(generate_mobile_platforms $TEAM_PARALLELS_MAX_ALLOWED_MOBILE "yaml") + platform_yaml=$(generate_mobile_platforms "$TEAM_PARALLELS_MAX_ALLOWED_MOBILE" "yaml") cat >> "$BROWSERSTACK_CONFIG_FILE" <> $NOW_RUN_LOG_FILE 2>&1 + browserstack-sdk pytest -s src/test/suites/*.py --browserstack.config ./src/conf/browserstack_parallel.yml >> "$NOW_RUN_LOG_FILE" 2>&1 # & # cmd_pid=$!|| return 1 # show_spinner "$cmd_pid" # wait "$cmd_pid" - cd "$WORKSPACE_DIR/$PROJECT_FOLDER" + cd "$WORKSPACE_DIR/$PROJECT_FOLDER" || return 1 return 0 } @@ -251,7 +251,7 @@ setup_app_python() { REPO="now-pytest-appium-app-browserstack" TARGET_DIR="$WORKSPACE_DIR/$PROJECT_FOLDER/$REPO" - clone_repository $REPO $TARGET_DIR + clone_repository "$REPO" "$TARGET_DIR" detect_setup_python_env @@ -259,16 +259,12 @@ setup_app_python() { pip install -r requirements.txt >> "$NOW_RUN_LOG_FILE" 2>&1 log_success "Dependencies installed" - - # Prepare platform-specific YAMLs in android/ and ios/ - local original_platform="$APP_PLATFORM" - local app_url=$BROWSERSTACK_APP local platform_yaml export BSTACK_PARALLELS=1 export BROWSERSTACK_CONFIG_FILE="./android/browserstack.yml" - platform_yaml=$(generate_mobile_platforms $TEAM_PARALLELS_MAX_ALLOWED_MOBILE "yaml") + platform_yaml=$(generate_mobile_platforms "$TEAM_PARALLELS_MAX_ALLOWED_MOBILE" "yaml") cat >> "$BROWSERSTACK_CONFIG_FILE" <> $NOW_RUN_LOG_FILE 2>&1 || return 1 + npm install >> "$NOW_RUN_LOG_FILE" 2>&1 || return 1 log_success "Dependencies installed" local caps_json="" @@ -345,7 +341,7 @@ setup_web_nodejs() { # === 8️⃣ Run Tests === log_msg_to "🚀 Running 'npm run test'. This could take a few minutes. Follow the Automaton build here: https://automation.browserstack.com/" print_tests_running_log_section "npm run test" - npm run test >> $NOW_RUN_LOG_FILE 2>&1 || return 1 & + npm run test >> "$NOW_RUN_LOG_FILE" 2>&1 || return 1 & cmd_pid=$!|| return 1 show_spinner "$cmd_pid" @@ -367,15 +363,15 @@ setup_app_nodejs() { TARGET_DIR="$WORKSPACE_DIR/$PROJECT_FOLDER/$REPO" TEST_FOLDER="/test" - clone_repository $REPO $TARGET_DIR $TEST_FOLDER + clone_repository $REPO "$TARGET_DIR" "$TEST_FOLDER" # === 2️⃣ Install Dependencies === log_info "Installing dependencies" log_msg_to "⚙️ Running 'npm install'" - npm install >> $NOW_RUN_LOG_FILE 2>&1 || return 1 + npm install >> "$NOW_RUN_LOG_FILE" 2>&1 || return 1 log_success "Dependencies installed" - caps_json=$(generate_mobile_platforms $TEAM_PARALLELS_MAX_ALLOWED_MOBILE "json") + caps_json=$(generate_mobile_platforms "$TEAM_PARALLELS_MAX_ALLOWED_MOBILE" "json") export BSTACK_CAPS_JSON=$caps_json @@ -444,7 +440,7 @@ run_setup_wrapper() { fi ;; *) - log_msg_to "❌ Invalid TEST_TYPE: $TEST_TYPE" + log_msg_to "❌ Invalid TEST_TYPE: $test_type" exit 1 ;; esac diff --git a/mac/run.sh b/mac/run.sh index 609d7fd..25226ef 100755 --- a/mac/run.sh +++ b/mac/run.sh @@ -18,7 +18,7 @@ source "$(dirname "$0")/env-prequisite-checks.sh" run_setup() { local test_type=$1 local tech_stack=$2 - setup_environment $test_type $tech_stack + setup_environment "$test_type" "$tech_stack" } # ===== Main flow (baseline steps then run) ===== From ceae214e998867ceb833dcf80c8a3ae845d38b14 Mon Sep 17 00:00:00 2001 From: Samiran Saha Date: Sat, 15 Nov 2025 14:24:14 +0530 Subject: [PATCH 12/17] linter checks --- mac/common-utils.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mac/common-utils.sh b/mac/common-utils.sh index 2fb14df..2c91758 100644 --- a/mac/common-utils.sh +++ b/mac/common-utils.sh @@ -278,9 +278,9 @@ is_domain_private() { identify_run_status_java() { local log_file=$1 log_section "✅ Results" - + local line="" # Extract the test summary line - local line=$(grep -m 2 -E "[INFO|ERROR].*Tests run" < "$log_file") + line=$(grep -m 2 -E "[INFO|ERROR].*Tests run" < "$log_file") # If not found, fail if [[ -z "$line" ]]; then log_warn "❌ No test summary line found." From 9e5c581275b3d38359f18d914d27bc46e664686e Mon Sep 17 00:00:00 2001 From: Samiran Saha Date: Sat, 15 Nov 2025 15:14:10 +0530 Subject: [PATCH 13/17] GHW path --- .github/workflows/test-scripts.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-scripts.yml b/.github/workflows/test-scripts.yml index a8d2632..496afa3 100644 --- a/.github/workflows/test-scripts.yml +++ b/.github/workflows/test-scripts.yml @@ -157,7 +157,7 @@ jobs: with: name: browserstack-logs-macos path: | - /Users/runner/.browserstack/NOW/logs/ + ${{ github.workspace }}/.browserstack/NOW/logs/ /tmp/run_test_*.log retention-days: 30 @@ -439,7 +439,7 @@ jobs: with: name: browserstack-logs-linux path: | - /home/runner/.browserstack/NOW/logs/ + ${{ github.workspace }}/.browserstack/NOW/logs/ /tmp/run_test_*.log retention-days: 30 if-no-files-found: ignore From 63a2b77618b19c0e9ff6333b39a6f15c7a2e547e Mon Sep 17 00:00:00 2001 From: Samiran Saha Date: Sun, 16 Nov 2025 13:15:28 +0530 Subject: [PATCH 14/17] env vars + detect os --- .github/workflows/test-scripts.yml | 291 ++++++++++++++--------------- mac/common-utils.sh | 29 +++ mac/env-setup-run.sh | 22 ++- 3 files changed, 193 insertions(+), 149 deletions(-) diff --git a/.github/workflows/test-scripts.yml b/.github/workflows/test-scripts.yml index 496afa3..3f8f551 100644 --- a/.github/workflows/test-scripts.yml +++ b/.github/workflows/test-scripts.yml @@ -161,148 +161,148 @@ jobs: /tmp/run_test_*.log retention-days: 30 - test-windows: - name: Test win/run.ps1 on Windows - runs-on: windows-latest - timeout-minutes: 15 - environment: BrowserStack - steps: - - name: Checkout code - uses: actions/checkout@v4 - - name: Set up Python 3.13 - uses: actions/setup-python@v5 - with: - python-version: '3.13' - - name: Check PowerShell version - run: | - $PSVersionTable.PSVersion - Write-Host "✅ PowerShell version check complete" - - - name: Validate PowerShell script syntax - run: | - Write-Host "Validating win/run.ps1 syntax..." - $ScriptPath = "win/run.ps1" - $null = [System.Management.Automation.PSParser]::Tokenize((Get-Content $ScriptPath), [ref]$null) - Write-Host "✅ win/run.ps1 syntax is valid" - - - name: Validate supporting PowerShell scripts syntax - run: | - Write-Host "Validating supporting PowerShell scripts..." - $Scripts = @("win/proxy-check.ps1") - foreach ($Script in $Scripts) { - $null = [System.Management.Automation.PSParser]::Tokenize((Get-Content $Script), [ref]$null) - Write-Host "✅ $Script syntax is valid" - } - - - name: Run PSScriptAnalyzer - run: | - Write-Host "Installing PSScriptAnalyzer..." - Install-Module -Name PSScriptAnalyzer -Force -SkipPublisherCheck -ErrorAction SilentlyContinue - Write-Host "Running PSScriptAnalyzer..." - Invoke-ScriptAnalyzer -Path "win/run.ps1" -Recurse -ReportSummary || $true - Write-Host "✅ PSScriptAnalyzer analysis complete" - - - name: Check script file encoding - run: | - Write-Host "Checking PowerShell script encoding..." - $ScriptPath = "win/run.ps1" - $Encoding = (Get-Item $ScriptPath).EncodingInfo - Write-Host "File encoding: $Encoding" - Write-Host "✅ Encoding check complete" - - - name: Verify required dependencies - run: | - Write-Host "Checking required dependencies..." - if (Get-Command curl.exe -ErrorAction SilentlyContinue) { Write-Host "✅ curl found" } - if (Get-Command git.exe -ErrorAction SilentlyContinue) { Write-Host "✅ git found" } - Write-Host "✅ PowerShell dependencies verified" - - - name: Integration Test - Silent Mode Execution - if: success() - env: - BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} - BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} - TURL: https://bstackdemo.com - run: | - Write-Host "Running integration tests in silent mode..." - - # Set default values if secrets are not provided - $BrowserStackUsername = if ($env:BROWSERSTACK_USERNAME) { $env:BROWSERSTACK_USERNAME } else { "test_user" } - $BrowserStackAccessKey = if ($env:BROWSERSTACK_ACCESS_KEY) { $env:BROWSERSTACK_ACCESS_KEY } else { "test_key" } - $TestUrl = $env:TURL - - # Export environment variables - $env:BROWSERSTACK_USERNAME = $BrowserStackUsername - $env:BROWSERSTACK_ACCESS_KEY = $BrowserStackAccessKey - $env:TURL = $TestUrl - - # Test configurations - $testConfigs = @( - @("web", "java"), - @("app", "java"), - @("web", "python"), - @("app", "python"), - @("web", "nodejs"), - @("app", "nodejs") - ) - - foreach ($config in $testConfigs) { - $testType = $config[0] - $techStack = $config[1] - - Write-Host "================================" - Write-Host "Testing: .\win\run.ps1 --silent $testType $techStack" - Write-Host "================================" - - # Create log file path - $logPath = "C:\Temp\run_test_${testType}_${techStack}.log" - New-Item -ItemType Directory -Path "C:\Temp" -Force -ErrorAction SilentlyContinue | Out-Null - - # Run with timeout (using job for timeout capability) - $job = Start-Job -ScriptBlock { - param($path, $testType, $techStack, $logPath) - & $path --silent $testType $techStack 2>&1 | Tee-Object -FilePath $logPath -Append - } -ArgumentList ".\win\run.ps1", $testType, $techStack, $logPath - - # Wait for job with 600 second timeout - $timeout = New-TimeSpan -Seconds 600 - $completed = Wait-Job -Job $job -Timeout 600 - - if ($completed) { - $result = Receive-Job -Job $job - if ($job.State -eq "Completed") { - Write-Host "✅ .\win\run.ps1 --silent $testType $techStack completed successfully" - } else { - Write-Host "⚠️ .\win\run.ps1 --silent $testType $techStack exited with state: $($job.State)" - if (Test-Path $logPath) { - Write-Host "Log output (last 20 lines):" - Get-Content -Path $logPath -Tail 20 - } - } - } else { - Write-Host "⚠️ .\win\run.ps1 --silent $testType $techStack timed out after 600 seconds" - Stop-Job -Job $job - if (Test-Path $logPath) { - Write-Host "Log output (last 20 lines):" - Get-Content -Path $logPath -Tail 20 - } - } - - Remove-Job -Job $job -Force - } - - Write-Host "✅ All integration tests completed" - - - name: Upload BrowserStack Logs as Artifacts - if: always() - uses: actions/upload-artifact@v4 - with: - name: browserstack-logs-windows - path: | - C:\Users\runneradmin\.browserstack\NOW\logs\ - C:\Temp\run_test_*.log - retention-days: 30 - if-no-files-found: ignore + # test-windows: + # name: Test win/run.ps1 on Windows + # runs-on: windows-latest + # timeout-minutes: 15 + # environment: BrowserStack + # steps: + # - name: Checkout code + # uses: actions/checkout@v4 + # - name: Set up Python 3.13 + # uses: actions/setup-python@v5 + # with: + # python-version: '3.13' + # - name: Check PowerShell version + # run: | + # $PSVersionTable.PSVersion + # Write-Host "✅ PowerShell version check complete" + # + # - name: Validate PowerShell script syntax + # run: | + # Write-Host "Validating win/run.ps1 syntax..." + # $ScriptPath = "win/run.ps1" + # $null = [System.Management.Automation.PSParser]::Tokenize((Get-Content $ScriptPath), [ref]$null) + # Write-Host "✅ win/run.ps1 syntax is valid" + # + # - name: Validate supporting PowerShell scripts syntax + # run: | + # Write-Host "Validating supporting PowerShell scripts..." + # $Scripts = @("win/proxy-check.ps1") + # foreach ($Script in $Scripts) { + # $null = [System.Management.Automation.PSParser]::Tokenize((Get-Content $Script), [ref]$null) + # Write-Host "✅ $Script syntax is valid" + # } + # + # - name: Run PSScriptAnalyzer + # run: | + # Write-Host "Installing PSScriptAnalyzer..." + # Install-Module -Name PSScriptAnalyzer -Force -SkipPublisherCheck -ErrorAction SilentlyContinue + # Write-Host "Running PSScriptAnalyzer..." + # Invoke-ScriptAnalyzer -Path "win/run.ps1" -Recurse -ReportSummary || $true + # Write-Host "✅ PSScriptAnalyzer analysis complete" + # + # - name: Check script file encoding + # run: | + # Write-Host "Checking PowerShell script encoding..." + # $ScriptPath = "win/run.ps1" + # $Encoding = (Get-Item $ScriptPath).EncodingInfo + # Write-Host "File encoding: $Encoding" + # Write-Host "✅ Encoding check complete" + # + # - name: Verify required dependencies + # run: | + # Write-Host "Checking required dependencies..." + # if (Get-Command curl.exe -ErrorAction SilentlyContinue) { Write-Host "✅ curl found" } + # if (Get-Command git.exe -ErrorAction SilentlyContinue) { Write-Host "✅ git found" } + # Write-Host "✅ PowerShell dependencies verified" + # + # - name: Integration Test - Silent Mode Execution + # if: success() + # env: + # BROWSERSTACK_USERNAME: ${{ secrets.BROWSERSTACK_USERNAME }} + # BROWSERSTACK_ACCESS_KEY: ${{ secrets.BROWSERSTACK_ACCESS_KEY }} + # TURL: https://bstackdemo.com + # run: | + # Write-Host "Running integration tests in silent mode..." + # + # # Set default values if secrets are not provided + # $BrowserStackUsername = if ($env:BROWSERSTACK_USERNAME) { $env:BROWSERSTACK_USERNAME } else { "test_user" } + # $BrowserStackAccessKey = if ($env:BROWSERSTACK_ACCESS_KEY) { $env:BROWSERSTACK_ACCESS_KEY } else { "test_key" } + # $TestUrl = $env:TURL + # + # # Export environment variables + # $env:BROWSERSTACK_USERNAME = $BrowserStackUsername + # $env:BROWSERSTACK_ACCESS_KEY = $BrowserStackAccessKey + # $env:TURL = $TestUrl + # + # # Test configurations + # $testConfigs = @( + # @("web", "java"), + # @("app", "java"), + # @("web", "python"), + # @("app", "python"), + # @("web", "nodejs"), + # @("app", "nodejs") + # ) + # + # foreach ($config in $testConfigs) { + # $testType = $config[0] + # $techStack = $config[1] + # + # Write-Host "================================" + # Write-Host "Testing: .\win\run.ps1 --silent $testType $techStack" + # Write-Host "================================" + # + # # Create log file path + # $logPath = "C:\Temp\run_test_${testType}_${techStack}.log" + # New-Item -ItemType Directory -Path "C:\Temp" -Force -ErrorAction SilentlyContinue | Out-Null + # + # # Run with timeout (using job for timeout capability) + # $job = Start-Job -ScriptBlock { + # param($path, $testType, $techStack, $logPath) + # & $path --silent $testType $techStack 2>&1 | Tee-Object -FilePath $logPath -Append + # } -ArgumentList ".\win\run.ps1", $testType, $techStack, $logPath + # + # # Wait for job with 600 second timeout + # $timeout = New-TimeSpan -Seconds 600 + # $completed = Wait-Job -Job $job -Timeout 600 + # + # if ($completed) { + # $result = Receive-Job -Job $job + # if ($job.State -eq "Completed") { + # Write-Host "✅ .\win\run.ps1 --silent $testType $techStack completed successfully" + # } else { + # Write-Host "⚠️ .\win\run.ps1 --silent $testType $techStack exited with state: $($job.State)" + # if (Test-Path $logPath) { + # Write-Host "Log output (last 20 lines):" + # Get-Content -Path $logPath -Tail 20 + # } + # } + # } else { + # Write-Host "⚠️ .\win\run.ps1 --silent $testType $techStack timed out after 600 seconds" + # Stop-Job -Job $job + # if (Test-Path $logPath) { + # Write-Host "Log output (last 20 lines):" + # Get-Content -Path $logPath -Tail 20 + # } + # } + # + # Remove-Job -Job $job -Force + # } + # + # Write-Host "✅ All integration tests completed" + # + # - name: Upload BrowserStack Logs as Artifacts + # if: always() + # uses: actions/upload-artifact@v4 + # with: + # name: browserstack-logs-windows + # path: | + # C:\Users\runneradmin\.browserstack\NOW\logs\ + # C:\Temp\run_test_*.log + # retention-days: 30 + # if-no-files-found: ignore test-linux: name: Test mac/run.sh on Linux @@ -447,17 +447,16 @@ jobs: test-summary: name: Test Summary runs-on: ubuntu-latest - needs: [test-mac, test-windows, test-linux] + needs: [test-mac, test-linux] if: always() steps: - name: Check test results run: | echo "=== Test Results Summary ===" echo "macOS Tests: ${{ needs.test-mac.result }}" - echo "Windows Tests: ${{ needs.test-windows.result }}" echo "Linux Tests: ${{ needs.test-linux.result }}" - if [ "${{ needs.test-mac.result }}" = "failure" ] || [ "${{ needs.test-windows.result }}" = "failure" ] || [ "${{ needs.test-linux.result }}" = "failure" ]; then + if [ "${{ needs.test-mac.result }}" = "failure" ] || [ "${{ needs.test-linux.result }}" = "failure" ]; then echo "❌ Some tests failed" exit 1 fi @@ -468,4 +467,4 @@ jobs: run: | echo "✅ All script validations passed successfully!" echo "- mac/run.sh and supporting scripts validated on macOS and Linux" - echo "- win/run.ps1 and supporting scripts validated on Windows" + echo "- win/run.ps1 and supporting scripts validated on Windows (temporarily disabled)" diff --git a/mac/common-utils.sh b/mac/common-utils.sh index 2c91758..9d1e82a 100644 --- a/mac/common-utils.sh +++ b/mac/common-utils.sh @@ -364,3 +364,32 @@ clear_old_logs() { log_success "Logs cleared and fresh run initiated." } + + +detect_os() { + local unameOut="" + unameOut="$(uname -s 2>/dev/null | tr '[:upper:]' '[:lower:]')" + local response="" + case "$unameOut" in + linux*) + # Detect WSL vs normal Linux + if grep -qi "microsoft" /proc/version 2>/dev/null; then + response="wsl" + else + response="linux" + fi + ;; + darwin*) + response="macos" + ;; + msys*|mingw*|cygwin*) + response="windows" + ;; + *) + response="unknown" + ;; + esac + + export NOW_OS=$response +} + diff --git a/mac/env-setup-run.sh b/mac/env-setup-run.sh index f2aea2e..590ca0d 100644 --- a/mac/env-setup-run.sh +++ b/mac/env-setup-run.sh @@ -31,7 +31,7 @@ setup_environment() { log_msg_to "Total parallels allocated: $total_parallels" "$NOW_RUN_LOG_FILE" - case "$TECH_STACK" in + case "$tech_stack" in java) "setup_${setup_type}_java" "$local_flag" "$parallels_per_platform" "$NOW_RUN_LOG_FILE" log_section "✅ Results" @@ -76,8 +76,6 @@ setup_web_java() { local_flag=true fi - export BROWSERSTACK_LOCAL=$local_flag - report_bstack_local_status "$local_flag" # === 5️⃣ YAML Setup === @@ -96,6 +94,8 @@ EOF export BSTACK_PARALLELS=$parallels export BSTACK_PLATFORMS=$platform_yaml export BROWSERSTACK_LOCAL=$local_flag + export BROWSERSTACK_BUILD_NAME="now-$NOW_OS-web-java-testng" + export BROWSERSTACK_PROJECT_NAME="now-$NOW_OS-app" # === 6️⃣ Build and Run === log_msg_to "⚙️ Running 'mvn install -DskipTests'" @@ -106,6 +106,7 @@ EOF log_section "Validate Environment Variables" log_info "BrowserStack Username: $BROWSERSTACK_USERNAME" + log_info "BrowserStack Build: $BROWSERSTACK_BUILD_NAME" log_info "Web Application Endpoint: $CX_TEST_URL" log_info "BrowserStack Local Flag: $BROWSERSTACK_LOCAL" log_info "Parallels per platform: $BSTACK_PARALLELS" @@ -156,6 +157,8 @@ EOF export BSTACK_PARALLELS=$parallels export BROWSERSTACK_LOCAL=true export BSTACK_PLATFORMS=$platform_yaml + export BROWSERSTACK_BUILD_NAME="now-$NOW_OS-app-java-testng" + export BROWSERSTACK_PROJECT_NAME="now-$NOW_OS-app" # Run Maven install first @@ -169,6 +172,7 @@ EOF log_section "Validate Environment Variables" log_info "BrowserStack Username: $BROWSERSTACK_USERNAME" + log_info "BrowserStack Build: $BROWSERSTACK_BUILD_NAME" log_info "Native App Endpoint: $BROWSERSTACK_APP" log_info "BrowserStack Local Flag: $BROWSERSTACK_LOCAL" log_info "Parallels per platform: $BSTACK_PARALLELS" @@ -218,11 +222,14 @@ EOF export BSTACK_PARALLELS=1 export BROWSERSTACK_LOCAL=$local_flag export BSTACK_PLATFORMS=$platform_yaml + export BROWSERSTACK_BUILD_NAME="now-$NOW_OS-web-python-pytest" + export BROWSERSTACK_PROJECT_NAME="now-$NOW_OS-web" report_bstack_local_status "$local_flag" log_section "Validate Environment Variables" log_info "BrowserStack Username: $BROWSERSTACK_USERNAME" + log_info "BrowserStack Build: $BROWSERSTACK_BUILD_NAME" log_info "Web Application Endpoint: $CX_TEST_URL" log_info "BrowserStack Local Flag: $BROWSERSTACK_LOCAL" log_info "Parallels per platform: $BSTACK_PARALLELS" @@ -280,9 +287,12 @@ EOF export BSTACK_PLATFORMS=$platform_yaml export BROWSERSTACK_LOCAL=true + export BROWSERSTACK_BUILD_NAME="now-$NOW_OS-app-python-pytest" + export BROWSERSTACK_PROJECT_NAME="now-$NOW_OS-app" log_section "Validate Environment Variables" log_info "BrowserStack Username: $BROWSERSTACK_USERNAME" + log_info "BrowserStack Build: $BROWSERSTACK_BUILD_NAME" log_info "Native App Endpoint: $BROWSERSTACK_APP" log_info "BrowserStack Local Flag: $BROWSERSTACK_LOCAL" log_info "Parallels per platform: $BSTACK_PARALLELS" @@ -328,11 +338,14 @@ setup_web_nodejs() { fi export BROWSERSTACK_LOCAL=$local_flag + export BROWSERSTACK_BUILD_NAME="now-$NOW_OS-web-nodejs-wdio" + export BROWSERSTACK_PROJECT_NAME="now-$NOW_OS-web" report_bstack_local_status "$local_flag" log_section "Validate Environment Variables" log_info "BrowserStack Username: $BROWSERSTACK_USERNAME" + log_info "BrowserStack Build: $BROWSERSTACK_BUILD_NAME" log_info "Web Application Endpoint: $CX_TEST_URL" log_info "BrowserStack Local Flag: $BROWSERSTACK_LOCAL" log_info "Parallels per platform: $BSTACK_PARALLELS" @@ -381,9 +394,12 @@ setup_app_nodejs() { export BSTACK_PARALLELS=$parallels export BROWSERSTACK_LOCAL=true export BROWSERSTACK_APP=$app_url + export BROWSERSTACK_BUILD_NAME="now-$NOW_OS-app-nodejs-wdio" + export BROWSERSTACK_PROJECT_NAME="now-$NOW_OS-app" log_section "Validate Environment Variables" log_info "BrowserStack Username: $BROWSERSTACK_USERNAME" + log_info "BrowserStack Build: $BROWSERSTACK_BUILD_NAME" log_info "Native App Endpoint: $BROWSERSTACK_APP" log_info "BrowserStack Local Flag: $BROWSERSTACK_LOCAL" log_info "Parallels per platform: $BSTACK_PARALLELS" From e59035249a242bb71148209fb4f8c93bc8658bb2 Mon Sep 17 00:00:00 2001 From: Samiran Saha Date: Sun, 16 Nov 2025 20:46:03 +0530 Subject: [PATCH 15/17] now utility cleanup for github actions - mac + linux --- mac/common-utils.sh | 50 ++++++++++----------- mac/detect-os.sh | 31 +++++++++++++ mac/device-machine-allocation.sh | 12 ++++- mac/env-setup-run.sh | 77 ++++++++++++++++++-------------- mac/run.sh | 5 ++- mac/user-interaction.sh | 5 +-- 6 files changed, 115 insertions(+), 65 deletions(-) create mode 100644 mac/detect-os.sh diff --git a/mac/common-utils.sh b/mac/common-utils.sh index 9d1e82a..dae53f9 100644 --- a/mac/common-utils.sh +++ b/mac/common-utils.sh @@ -258,6 +258,7 @@ is_domain_private() { domain=${domain%%/*} # remove everything after first "/" log_msg_to "Website domain: $domain" export NOW_WEB_DOMAIN="$CX_TEST_URL" + export CX_TEST_URL="$CX_TEST_URL" # Resolve domain using Cloudflare DNS IP_ADDRESS=$(dig +short "$domain" @1.1.1.1 | head -n1) @@ -277,7 +278,6 @@ is_domain_private() { identify_run_status_java() { local log_file=$1 - log_section "✅ Results" local line="" # Extract the test summary line line=$(grep -m 2 -E "[INFO|ERROR].*Tests run" < "$log_file") @@ -367,29 +367,29 @@ clear_old_logs() { detect_os() { - local unameOut="" - unameOut="$(uname -s 2>/dev/null | tr '[:upper:]' '[:lower:]')" - local response="" - case "$unameOut" in - linux*) - # Detect WSL vs normal Linux - if grep -qi "microsoft" /proc/version 2>/dev/null; then - response="wsl" - else - response="linux" - fi - ;; - darwin*) - response="macos" - ;; - msys*|mingw*|cygwin*) - response="windows" - ;; - *) - response="unknown" - ;; - esac - - export NOW_OS=$response + local unameOut="" + unameOut="$(uname -s 2>/dev/null | tr '[:upper:]' '[:lower:]')" + local response="" + case "$unameOut" in + linux*) + # Detect WSL vs normal Linux + if grep -qi "microsoft" /proc/version 2>/dev/null; then + response="wsl" + else + response="linux" + fi + ;; + darwin*) + response="macos" + ;; + msys*|mingw*|cygwin*) + response="windows" + ;; + *) + response="unknown" + ;; + esac + + export NOW_OS=$response } diff --git a/mac/detect-os.sh b/mac/detect-os.sh new file mode 100644 index 0000000..30ccd34 --- /dev/null +++ b/mac/detect-os.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +detect_os() { + local unameOut="" + unameOut="$(uname -s 2>/dev/null | tr '[:upper:]' '[:lower:]')" + local response="" + case "$unameOut" in + linux*) + # Detect WSL vs normal Linux + if grep -qi "microsoft" /proc/version 2>/dev/null; then + response="wsl" + else + response="linux" + fi + ;; + darwin*) + response="macos" + ;; + msys*|mingw*|cygwin*) + response="windows" + ;; + *) + response="unknown" + ;; + esac + echo "OS is: $response" + export NOW_OS=$response +} + +detect_os + diff --git a/mac/device-machine-allocation.sh b/mac/device-machine-allocation.sh index 0338994..fbbe280 100644 --- a/mac/device-machine-allocation.sh +++ b/mac/device-machine-allocation.sh @@ -116,7 +116,15 @@ pick_terminal_devices() { entry="${matching_devices[$index]}" suffixEntry="${entry#*|}" prefixEntry="${entry%%|*}" + bVersionLiteral="" + mod=$(( i % 4 )) + if [ $((i % 4)) -ne 0 ]; then + bVersionLiteral="-$mod" + else + bVersionLiteral="" + fi + bVersion="latest$bVersionLiteral" if [[ "$platformsListContentFormat" == "yaml" ]]; then if [[ "$prefixEntry" == "android" || "$prefixEntry" == "ios" ]]; then yaml+=" - platformName: $prefixEntry @@ -125,7 +133,7 @@ pick_terminal_devices() { else yaml+=" - osVersion: $prefixEntry browserName: $suffixEntry - browserVersion: latest + browserVersion: $bVersion " fi @@ -139,7 +147,7 @@ pick_terminal_devices() { if [[ "$prefixEntry" == "android" || "$prefixEntry" == "ios" ]]; then json+=$'{"platformName": "'"$prefixEntry"'","bstack:options":{"deviceName": "'"$suffixEntry"'"}},' else - json+=$'{"bstack:options":{ "os": "'"$prefixEntry"'"},"browserName": "'"$suffixEntry"'","browserVersion": "latest"},' + json+=$'{"bstack:options":{ "os": "'"$prefixEntry"'"},"browserName": "'"$suffixEntry"'","browserVersion": "'"$bVersion"'"},' fi # Stop if max reached diff --git a/mac/env-setup-run.sh b/mac/env-setup-run.sh index 590ca0d..fdbf0a4 100644 --- a/mac/env-setup-run.sh +++ b/mac/env-setup-run.sh @@ -2,7 +2,7 @@ # shellcheck shell=bash setup_environment() { - local setup_type=$1 + local setup_type=$1 local tech_stack=$2 local max_parallels @@ -51,7 +51,7 @@ setup_environment() { check_return_value $? "$NOW_RUN_LOG_FILE" "${setup_type} setup succeeded." "❌ ${setup_type} setup failed. Check $log_file for details" ;; *) - log_warn "Unknown TECH_STACK: $TECH_STACK" "$NOW_RUN_LOG_FILE" + log_warn "Unknown TECH_STACK: $tech_stack" "$NOW_RUN_LOG_FILE" return 1 ;; esac @@ -65,7 +65,7 @@ setup_web_java() { TARGET_DIR="$WORKSPACE_DIR/$PROJECT_FOLDER/$REPO" mkdir -p "$WORKSPACE_DIR/$PROJECT_FOLDER" - + clone_repository "$REPO" "$TARGET_DIR" cd "$TARGET_DIR"|| return 1 @@ -91,19 +91,19 @@ platforms: $platform_yaml EOF - export BSTACK_PARALLELS=$parallels + export BSTACK_PARALLELS=$parallels export BSTACK_PLATFORMS=$platform_yaml - export BROWSERSTACK_LOCAL=$local_flag + export BROWSERSTACK_LOCAL=$local_flag export BROWSERSTACK_BUILD_NAME="now-$NOW_OS-web-java-testng" - export BROWSERSTACK_PROJECT_NAME="now-$NOW_OS-app" - + export BROWSERSTACK_PROJECT_NAME="now-$NOW_OS-web" + # === 6️⃣ Build and Run === log_msg_to "⚙️ Running 'mvn install -DskipTests'" log_info "Installing dependencies" mvn install -DskipTests >> "$NOW_RUN_LOG_FILE" 2>&1 || return 1 log_success "Dependencies installed" - - + + log_section "Validate Environment Variables" log_info "BrowserStack Username: $BROWSERSTACK_USERNAME" log_info "BrowserStack Build: $BROWSERSTACK_BUILD_NAME" @@ -134,7 +134,7 @@ setup_app_java() { TARGET_DIR="$WORKSPACE_DIR/$PROJECT_FOLDER/$REPO" local app_url=$BROWSERSTACK_APP log_msg_to "APP_PLATFORM: $APP_PLATFORM" >> "$NOW_RUN_LOG_FILE" 2>&1 - + clone_repository "$REPO" "$TARGET_DIR" if [[ "$APP_PLATFORM" == "all" || "$APP_PLATFORM" == "android" ]]; then @@ -169,7 +169,7 @@ EOF return 1 # Fail the function if clean fails fi log_success "Dependencies installed" - + log_section "Validate Environment Variables" log_info "BrowserStack Username: $BROWSERSTACK_USERNAME" log_info "BrowserStack Build: $BROWSERSTACK_BUILD_NAME" @@ -195,10 +195,10 @@ setup_web_python() { local parallels=$2 local log_file=$3 - REPO="browserstack-examples-pytest" + REPO="now-pytest-browserstack" TARGET_DIR="$WORKSPACE_DIR/$PROJECT_FOLDER/$REPO" - clone_repository "$REPO" "$TARGET_DIR" + clone_repository "$REPO" "$TARGET_DIR" "" detect_setup_python_env @@ -206,19 +206,21 @@ setup_web_python() { log_success "Dependencies installed" # Update YAML at root level (browserstack.yml) - export BROWSERSTACK_CONFIG_FILE="./src/conf/browserstack_parallel.yml" + export BROWSERSTACK_CONFIG_FILE="./browserstack.yml" platform_yaml=$(generate_web_platforms "$TEAM_PARALLELS_MAX_ALLOWED_WEB" "yaml") export BSTACK_PLATFORMS=$platform_yaml cat >> "$BROWSERSTACK_CONFIG_FILE" <> "$NOW_RUN_LOG_FILE" 2>&1 - # & - # cmd_pid=$!|| return 1 - - # show_spinner "$cmd_pid" - # wait "$cmd_pid" + browserstack-sdk pytest -s tests/*.py >> "$NOW_RUN_LOG_FILE" 2>&1 & cmd_pid=$!|| return 1 + show_spinner "$cmd_pid" + wait "$cmd_pid" cd "$WORKSPACE_DIR/$PROJECT_FOLDER" || return 1 return 0 @@ -289,7 +288,7 @@ EOF export BROWSERSTACK_LOCAL=true export BROWSERSTACK_BUILD_NAME="now-$NOW_OS-app-python-pytest" export BROWSERSTACK_PROJECT_NAME="now-$NOW_OS-app" - + log_section "Validate Environment Variables" log_info "BrowserStack Username: $BROWSERSTACK_USERNAME" log_info "BrowserStack Build: $BROWSERSTACK_BUILD_NAME" @@ -319,7 +318,7 @@ setup_web_nodejs() { mkdir -p "$WORKSPACE_DIR/$PROJECT_FOLDER" clone_repository "$REPO" "$TARGET_DIR" - + # === 2️⃣ Install Dependencies === log_msg_to "⚙️ Running 'npm install'" @@ -342,14 +341,14 @@ setup_web_nodejs() { export BROWSERSTACK_PROJECT_NAME="now-$NOW_OS-web" report_bstack_local_status "$local_flag" - + log_section "Validate Environment Variables" log_info "BrowserStack Username: $BROWSERSTACK_USERNAME" log_info "BrowserStack Build: $BROWSERSTACK_BUILD_NAME" log_info "Web Application Endpoint: $CX_TEST_URL" log_info "BrowserStack Local Flag: $BROWSERSTACK_LOCAL" log_info "Parallels per platform: $BSTACK_PARALLELS" - log_info "Platforms: \n$BSTACK_CAPS_JSON" + log_info "Platforms: \n$BSTACK_CAPS_JSON" # === 8️⃣ Run Tests === log_msg_to "🚀 Running 'npm run test'. This could take a few minutes. Follow the Automaton build here: https://automation.browserstack.com/" @@ -396,7 +395,7 @@ setup_app_nodejs() { export BROWSERSTACK_APP=$app_url export BROWSERSTACK_BUILD_NAME="now-$NOW_OS-app-nodejs-wdio" export BROWSERSTACK_PROJECT_NAME="now-$NOW_OS-app" - + log_section "Validate Environment Variables" log_info "BrowserStack Username: $BROWSERSTACK_USERNAME" log_info "BrowserStack Build: $BROWSERSTACK_BUILD_NAME" @@ -422,14 +421,24 @@ setup_app_nodejs() { } clone_repository() { - repo_git=$1 - install_folder=$2 - test_folder=$3 - + local repo_git=$1 + local install_folder=$2 + local test_folder=$3 + local git_branch=$4 + rm -rf "$install_folder" log_msg_to "📦 Cloning repo $repo_git into $install_folder" log_info "Cloning repository: $repo_git" - git clone https://github.com/BrowserStackCE/$repo_git.git "$install_folder" >> "$NOW_RUN_LOG_FILE" 2>&1 || return 1 + # git clone https://github.com/BrowserStackCE/"$repo_git".git "$install_folder" >> "$NOW_RUN_LOG_FILE" 2>&1 || return 1 + if [ -z "$git_branch" ]; then + # git_branch is null or empty + git clone "https://github.com/BrowserStackCE/$repo_git.git" \ + "$install_folder" >> "$NOW_RUN_LOG_FILE" 2>&1 || return 1 + else + # git_branch has a value + git clone -b "$git_branch" "https://github.com/BrowserStackCE/$repo_git.git" \ + "$install_folder" >> "$NOW_RUN_LOG_FILE" 2>&1 || return 1 + fi log_msg_to "✅ Cloned repository: $repo_git into $install_folder" cd "$install_folder/$test_folder" || return 1 } diff --git a/mac/run.sh b/mac/run.sh index 25226ef..a6a9c9b 100755 --- a/mac/run.sh +++ b/mac/run.sh @@ -68,8 +68,11 @@ log_msg_to "Checking proxy in environment" set_proxy_in_env log_section "🧹 Getting Ready" -log_info "Clearing old logs..." +detect_os +log_info "Detected Operating system: $NOW_OS" +log_info "Clearing old logs fron NOW Home Directory inside .browserstack" clear_old_logs + log_info "Starting $TEST_TYPE setup for $TECH_STACK" run_setup "$TEST_TYPE" "$TECH_STACK" diff --git a/mac/user-interaction.sh b/mac/user-interaction.sh index 9ddf908..ecba65d 100644 --- a/mac/user-interaction.sh +++ b/mac/user-interaction.sh @@ -69,18 +69,17 @@ get_test_url() { fi export CX_TEST_URL="$test_url" - log_msg_to "Exported TEST_URL=$TEST_URL" + log_msg_to "Exported TEST_URL $CX_TEST_URL" } get_test_type() { - local run_mode=$1 local test_type="" if [[ "$RUN_MODE" == *"--silent"* || "$RUN_MODE" == *"--debug"* ]]; then test_type=$TT log_msg_to "✅ Selected Testing Type from environment: $TEST_TYPE" else - test_type=$(osascript -e 'Tell application "System Events" to display dialog "Select testing type:" buttons {"web", "app", "both"} default button "web" with title "Testing Type"' \ + test_type=$(osascript -e 'Tell application "System Events" to display dialog "Select testing type:" buttons {"web", "app"} default button "web" with title "Testing Type"' \ -e 'button returned of result') log_msg_to "✅ Selected Testing Type: $TEST_TYPE" RUN_MODE=$test_type From f408d07d8ced9a1d4986d3e4d8d17e81ef9cf8ad Mon Sep 17 00:00:00 2001 From: Samiran Saha Date: Sun, 16 Nov 2025 21:10:20 +0530 Subject: [PATCH 16/17] gha should preserve logs --- .github/workflows/test-scripts.yml | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-scripts.yml b/.github/workflows/test-scripts.yml index 3f8f551..7e88c61 100644 --- a/.github/workflows/test-scripts.yml +++ b/.github/workflows/test-scripts.yml @@ -150,16 +150,26 @@ jobs: done echo "✅ All integration tests completed" - + + - name: Sync BrowserStack logs to workspace + if: always() + run: | + mkdir -p ${{ github.workspace }}/bs-logs + if [ -d ~/.browserstack/NOW/logs ]; then + cp -R ~/.browserstack/NOW/logs/* ${{ github.workspace }}/bs-logs/ || true + else + echo "No logs found in ~/.browserstack/NOW/logs" + fi - name: Upload BrowserStack Logs as Artifacts if: always() uses: actions/upload-artifact@v4 with: name: browserstack-logs-macos path: | - ${{ github.workspace }}/.browserstack/NOW/logs/ - /tmp/run_test_*.log + ${{ github.workspace }}/bs-logs + /tmp/run_test_*.log retention-days: 30 + if-no-files-found: ignore # test-windows: # name: Test win/run.ps1 on Windows @@ -433,13 +443,23 @@ jobs: echo "✅ All integration tests completed" + - name: Sync BrowserStack logs to workspace + if: always() + run: | + mkdir -p ${{ github.workspace }}/bs-logs + if [ -d ~/.browserstack/NOW/logs ]; then + cp -R ~/.browserstack/NOW/logs/* ${{ github.workspace }}/bs-logs/ || true + else + echo "No logs found in ~/.browserstack/NOW/logs" + fi + - name: Upload BrowserStack Logs as Artifacts if: always() uses: actions/upload-artifact@v4 with: name: browserstack-logs-linux path: | - ${{ github.workspace }}/.browserstack/NOW/logs/ + ${{ github.workspace }}/bs-logs /tmp/run_test_*.log retention-days: 30 if-no-files-found: ignore From 2aab1e5bbe337097577aec4505bd27eabdb38e1f Mon Sep 17 00:00:00 2001 From: Samiran Saha Date: Mon, 17 Nov 2025 13:24:18 +0530 Subject: [PATCH 17/17] semgrep changes + pytest cd --- .github/workflows/Semgrep.yml | 2 +- mac/env-setup-run.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/Semgrep.yml b/.github/workflows/Semgrep.yml index 5398af9..b54bbdf 100644 --- a/.github/workflows/Semgrep.yml +++ b/.github/workflows/Semgrep.yml @@ -36,7 +36,7 @@ jobs: # Fetch project source with GitHub Actions Checkout. - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3.5.3 # Run the "semgrep ci" command on the command line of the docker image. - - run: semgrep ci --sarif --output=semgrep.sarif + - run: semgrep ci --config auto --sarif --output=semgrep.sarif env: # Add the rules that Semgrep uses by setting the SEMGREP_RULES environment variable. SEMGREP_RULES: p/default # more at semgrep.dev/explore diff --git a/mac/env-setup-run.sh b/mac/env-setup-run.sh index fdbf0a4..34f7915 100644 --- a/mac/env-setup-run.sh +++ b/mac/env-setup-run.sh @@ -297,7 +297,7 @@ EOF log_info "Parallels per platform: $BSTACK_PARALLELS" log_info "Platforms: \n$BSTACK_PLATFORMS" - print_tests_running_log_section "browserstack-sdk pytest -s bstack-sample.py" + print_tests_running_log_section "cd $run_dir && browserstack-sdk pytest -s bstack-sample.py" # Run pytest with BrowserStack SDK from the chosen platform directory log_msg_to "🚀 Running 'cd $run_dir && browserstack-sdk pytest -s bstack_sample.py'" (