diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..f91f646 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,12 @@ +# +# https://help.github.com/articles/dealing-with-line-endings/ +# +# Linux start script should use lf +/gradlew text eol=lf + +# These are Windows script files and should use crlf +*.bat text eol=crlf + +# Binary files should be left untouched +*.jar binary + diff --git a/.github/workflows/android-emulator-appium.yml b/.github/workflows/android-emulator-appium.yml new file mode 100644 index 0000000..5e005a8 --- /dev/null +++ b/.github/workflows/android-emulator-appium.yml @@ -0,0 +1,46 @@ +name: Android Instrumented UI Tests + +on: + push: + branches: [ '*' ] + pull_request: + branches: [ '*' ] + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: 17 + + - name: Set up Android SDK + uses: android-actions/setup-android@v3 + + - name: Cache Gradle dependencies + uses: actions/cache@v4 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} + restore-keys: | + gradle-${{ runner.os }}- + + - name: Grant execute permission for gradlew + run: chmod +x ./gradlew + + - name: Run instrumented tests on emulator + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: 30 + arch: x86_64 + disable-animations: true + emulator-options: -no-snapshot -no-window -no-audio + script: ./gradlew connectedAndroidTest \ No newline at end of file diff --git a/.gitignore b/.gitignore index 716b41b..24496a7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,63 +1,36 @@ -# Dependencies -node_modules/ -npm-debug.log* -yarn-debug.log* -yarn-error.log* -package-lock.json -yarn.lock - -# Build outputs -dist/ +# Ignore Gradle project-specific cache directory +.gradle/ + +# Ignore Gradle build output directory build/ -out/ +app/build/ -# Test outputs -test-results/ +# Ignore Allure results (test artifacts) allure-results/ -allure-report/ -screenshots/ -videos/ -logs/ +**/allure-results/ -# IDE files -.vscode/ +# Ignore JUnit and test reports +**/build/reports/ +**/test-results/ + +# Ignore IDE files .idea/ -*.swp -*.swo -*~ +.vscode/ +*.iml -# OS files +# Ignore OS generated files .DS_Store -.DS_Store? -._* -.Spotlight-V100 -.Trashes -ehthumbs.db Thumbs.db -# Temporary files -tmp/ -temp/ +# Logs and temp +*.log *.tmp -*.temp - -# Environment files -.env -.env.local -.env.development.local -.env.test.local -.env.production.local -# Android specific +# Android/Emulator *.apk -*.aab -local.properties - -# iOS specific -*.ipa -*.dSYM.zip -*.dSYM +*.ap_ +*.keystore -# Appium specific -appium.log -*.log \ No newline at end of file +# Local environment +local.properties +.env diff --git a/README.md b/README.md index d9afa9b..7a47268 100644 --- a/README.md +++ b/README.md @@ -1,236 +1,25 @@ -# Appium Mobile Automation Test Setup +# Appium Autotest -This repository provides a complete setup guide and scripts for preparing a mobile automation workspace using Appium. +A Java-based automation testing framework using Appium. -## 🎯 Goal -Prepare a mobile automation workspace with all necessary tools and dependencies. +## Requirements +- Java 17 or higher +- Gradle 9.0.0 or higher -## πŸ“‹ Requirements -- JDK 17 -- Gradle/Maven -- Android Studio + SDK + AVD -- Node.js -- Appium 2 -- Appium Inspector - -## βœ… Definition of Done -- appium-doctor passes all checks -- Android emulator runs successfully -- Appium server starts successfully - -## πŸš€ Quick Start - -**New to Appium?** Check out our [Quick Start Guide](docs/quick-start.md) for a streamlined setup process. - -### Automated Setup - -#### For macOS/Linux: -```bash -./scripts/setup-macos-linux.sh -``` - -#### For Windows: -```powershell -.\scripts\setup-windows.ps1 -``` - -### Install Project Dependencies -After running the setup script, install the project dependencies: -```bash -npm install -``` - -### Verification -Run the verification script to ensure all tools are properly installed: -```bash -npm run verify-setup -``` - -## πŸ“– Manual Installation Guide - -### 1. Install JDK 17 -#### macOS: -```bash -brew install openjdk@17 -echo 'export JAVA_HOME=/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home' >> ~/.zshrc -``` - -#### Linux: -```bash -sudo apt update -sudo apt install openjdk-17-jdk -export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 -``` - -#### Windows: -1. Download JDK 17 from [Oracle](https://www.oracle.com/java/technologies/downloads/#java17) or [OpenJDK](https://openjdk.org/projects/jdk/17/) -2. Install and set JAVA_HOME environment variable - -### 2. Install Gradle -```bash -# macOS -brew install gradle - -# Linux -sudo apt install gradle - -# Windows (using Chocolatey) -choco install gradle -``` - -### 3. Install Android Studio and SDK -1. Download [Android Studio](https://developer.android.com/studio) -2. Install Android SDK through Android Studio -3. Set ANDROID_HOME environment variable: - - macOS/Linux: `export ANDROID_HOME=$HOME/Library/Android/sdk` - - Windows: `set ANDROID_HOME=%USERPROFILE%\AppData\Local\Android\Sdk` - -### 4. Install Node.js -```bash -# macOS -brew install node - -# Linux -curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - -sudo apt-get install -y nodejs - -# Windows -# Download from https://nodejs.org/ -``` - -### 5. Install Appium 2 -```bash -npm install -g appium@next -``` - -### 6. Install Appium Inspector -```bash -npm install -g appium-inspector -``` - -## πŸ”§ Configuration - -### Android Virtual Device (AVD) Setup -Use our AVD management script: -```bash -# Create default test AVD -npm run avd:create - -# List available AVDs -npm run avd:list - -# Start an AVD -npm run avd:start - -# Stop all emulators -npm run avd:stop -``` - -Or manually through Android Studio: -1. Open Android Studio -2. Go to AVD Manager -3. Create a new virtual device -4. Choose a device definition (e.g., Pixel 4) -5. Select a system image (API level 29 or higher recommended) -6. Configure AVD settings and finish - -### Environment Variables -Ensure the following environment variables are set: - -```bash -export JAVA_HOME=/path/to/jdk17 -export ANDROID_HOME=/path/to/android/sdk -export PATH=$PATH:$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools -``` - -## πŸ§ͺ Testing Your Setup - -**Important:** First install project dependencies if you haven't already: +## Build & Test ```bash -npm install +./gradlew build # Build the project +./gradlew test # Run all tests ``` -### 1. Run Appium Doctor -```bash -npm run doctor -``` +## Reports +- JUnit: `app/build/reports/tests/test/index.html` +- Allure: `./gradlew allureReport` β†’ `app/build/reports/allure-report/` -### 2. Start Android Emulator -```bash -npm run avd:start +## Project Structure ``` - -### 3. Start Appium Server -```bash -npm run start-appium +app/ + src/main/java/ # Application code + src/test/java/ # Test code + build.gradle # Build config ``` - -### 4. Run Sample Test -```bash -npm run test:sample -``` - -## πŸ“‹ Available NPM Scripts - -### Setup and Verification -- `npm run verify-setup` - Comprehensive setup verification -- `npm run doctor` - Run appium-doctor diagnostic - -### Appium Server -- `npm run start-appium` - Start Appium server - -### Android Virtual Device Management -- `npm run avd` - Show AVD management help -- `npm run avd:list` - List available AVDs -- `npm run avd:create` - Create default test AVD -- `npm run avd:start` - Start an AVD -- `npm run avd:stop` - Stop all running emulators - -### Testing -- `npm run test:sample` - Run sample Appium test - -## πŸ› οΈ Troubleshooting - -For common issues and solutions, check our [Troubleshooting Guide](docs/troubleshooting.md). - -### Quick Fixes -```bash -# Kill all processes and restart -pkill -f appium -npm run avd:stop -adb kill-server && adb start-server - -# Check system status -npm run verify-setup -adb devices -``` - -## πŸ“ Project Structure -``` -appium-autotest/ -β”œβ”€β”€ scripts/ -β”‚ β”œβ”€β”€ setup-macos-linux.sh # macOS/Linux setup script -β”‚ β”œβ”€β”€ setup-windows.ps1 # Windows setup script -β”‚ β”œβ”€β”€ verify-setup.js # Setup verification script -β”‚ └── manage-avd.sh # AVD management utility -β”œβ”€β”€ config/ -β”‚ β”œβ”€β”€ appium.config.js # Appium server configuration -β”‚ └── android.config.js # Android-specific configuration -β”œβ”€β”€ tests/ -β”‚ └── sample-test.js # Sample Appium test -β”œβ”€β”€ docs/ -β”‚ β”œβ”€β”€ quick-start.md # Quick start guide -β”‚ └── troubleshooting.md # Troubleshooting guide -β”œβ”€β”€ package.json # Node.js dependencies and scripts -└── README.md # This file -``` - -## 🀝 Contributing -1. Fork the repository -2. Create a feature branch -3. Make your changes -4. Test your setup thoroughly -5. Submit a pull request - -## πŸ“„ License -This project is licensed under the MIT License. \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..0492ba0 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,52 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This generated file contains a sample Java application project to get you started. + * For more details on building Java & JVM projects, please refer to https://docs.gradle.org/9.0.0/userguide/building_java_projects.html in the Gradle documentation. + */ + +plugins { + // Apply the application plugin to add support for building a CLI application in Java. + id 'application' +} + +repositories { + // Use Maven Central for resolving dependencies. + mavenCentral() +} + +dependencies { + // Use JUnit Jupiter for testing. + testImplementation libs.junit.jupiter + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + + // Appium Java Client for mobile automation + testImplementation libs.appium.java.client + + // SLF4J for logging + implementation libs.slf4j.api + implementation libs.slf4j.simple + + // Allure for test reporting + testImplementation libs.allure.junit5 + + // This dependency is used by the application. + implementation libs.guava +} + +// Apply a specific Java toolchain to ease working on different environments. +java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +application { + // Define the main class for the application. + mainClass = 'org.example.App' +} + +tasks.named('test') { + // Use JUnit Platform for unit tests. + useJUnitPlatform() +} diff --git a/app/src/main/java/org/example/App.java b/app/src/main/java/org/example/App.java new file mode 100644 index 0000000..e7e1af9 --- /dev/null +++ b/app/src/main/java/org/example/App.java @@ -0,0 +1,14 @@ +/* + * This source file was generated by the Gradle 'init' task + */ +package org.example; + +public class App { + public String getGreeting() { + return "Hello World!"; + } + + public static void main(String[] args) { + System.out.println(new App().getGreeting()); + } +} diff --git a/app/src/test/java/org/example/AndroidEmulatorFunctionalTest.java b/app/src/test/java/org/example/AndroidEmulatorFunctionalTest.java new file mode 100644 index 0000000..893c50f --- /dev/null +++ b/app/src/test/java/org/example/AndroidEmulatorFunctionalTest.java @@ -0,0 +1,47 @@ +package org.example; + +import io.appium.java_client.android.AndroidDriver; +import io.appium.java_client.android.options.UiAutomator2Options; +import org.junit.jupiter.api.*; +import org.openqa.selenium.By; +import org.openqa.selenium.WebElement; +import java.net.MalformedURLException; +import java.net.URL; + +import static org.junit.jupiter.api.Assertions.*; + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +class AndroidEmulatorFunctionalTest { + private static AndroidDriver driver; + + @BeforeAll + static void setUp() throws MalformedURLException { + UiAutomator2Options options = new UiAutomator2Options() + .setDeviceName("emulator-5554") // Default emulator name + .setPlatformName("Android") + .setAppPackage("com.android.calculator2") + .setAppActivity("com.android.calculator2.Calculator"); + driver = new AndroidDriver(new URL("http://localhost:4723/wd/hub"), options); + } + + @Test + @Order(1) + @DisplayName("Simple addition in Calculator app") + void testCalculatorAddition() { + // 2 + 3 = 5 + driver.findElement(By.id("digit_2")).click(); + driver.findElement(By.id("op_add")).click(); + driver.findElement(By.id("digit_3")).click(); + driver.findElement(By.id("eq")).click(); + WebElement result = driver.findElement(By.id("result")); + assertTrue(result.getText().contains("5"), "Result should contain 5"); + } + + @AfterAll + static void tearDown() { + if (driver != null) { + driver.quit(); + } + } +} + diff --git a/config/android.config.js b/config/android.config.js deleted file mode 100644 index 4750763..0000000 --- a/config/android.config.js +++ /dev/null @@ -1,100 +0,0 @@ -// Android-specific configuration for Appium tests - -module.exports = { - // Android Virtual Device (AVD) configurations - avdConfigs: { - default: { - avdName: 'Test_AVD_API33', - deviceName: 'Pixel_6', - systemImage: 'system-images;android-33;google_apis;x86_64', - sdcardSize: '512M', - hardwareProfile: 'pixel_6' - }, - - tablet: { - avdName: 'Tablet_AVD_API33', - deviceName: 'Nexus_10', - systemImage: 'system-images;android-33;google_apis;x86_64', - sdcardSize: '1024M', - hardwareProfile: 'Nexus 10' - } - }, - - // Android SDK paths - androidSdk: { - // These will be automatically detected from ANDROID_HOME - platformTools: process.env.ANDROID_HOME ? `${process.env.ANDROID_HOME}/platform-tools` : null, - buildTools: process.env.ANDROID_HOME ? `${process.env.ANDROID_HOME}/build-tools` : null, - emulator: process.env.ANDROID_HOME ? `${process.env.ANDROID_HOME}/emulator` : null, - avdManager: process.env.ANDROID_HOME ? `${process.env.ANDROID_HOME}/cmdline-tools/latest/bin/avdmanager` : null, - sdkManager: process.env.ANDROID_HOME ? `${process.env.ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager` : null - }, - - // Common Android capabilities - androidCapabilities: { - platformName: 'Android', - automationName: 'UiAutomator2', - platformVersion: '16.0', - deviceName: 'Android Emulator', - - // Performance optimizations - skipDeviceInitialization: false, - skipServerInstallation: false, - ignoreHiddenApiPolicyError: true, - disableWindowAnimation: true, - - // Timeouts - newCommandTimeout: 300, - commandTimeouts: { - 'default': 60000 - }, - - // System settings - autoGrantPermissions: true, - noReset: false, - fullReset: false, - - // Logging - enableLogcatCapture: true, - logcatFilterSpecs: ['*:W'] - }, - - // Test application configurations - testApps: { - settings: { - appPackage: 'com.android.settings', - appActivity: '.Settings' - }, - - calculator: { - appPackage: 'com.google.android.calculator', - appActivity: 'com.android.calculator2.Calculator' - }, - - chrome: { - appPackage: 'com.android.chrome', - appActivity: 'com.google.android.apps.chrome.Main' - } - }, - - // Emulator commands - emulatorCommands: { - // Start emulator - start: (avdName) => `emulator -avd ${avdName} -no-snapshot-save -no-audio -no-window`, - - // List AVDs - listAvds: 'emulator -list-avds', - - // Kill all emulators - killAll: 'adb devices | grep emulator | cut -f1 | while read line; do adb -s $line emu kill; done' - }, - - // AVD creation script - createAvdScript: { - createDefault: ` - echo "Creating default Android AVD..." - avdmanager create avd -n Test_AVD_API33 -k "system-images;android-33;google_apis;x86_64" -d "pixel_6" --force - echo "AVD created successfully!" - ` - } -}; \ No newline at end of file diff --git a/config/appium.config.js b/config/appium.config.js deleted file mode 100644 index e6a480f..0000000 --- a/config/appium.config.js +++ /dev/null @@ -1,60 +0,0 @@ -// Appium Configuration File -// This file contains the default configuration for Appium server - -module.exports = { - server: { - // Server configuration - host: '0.0.0.0', - port: 4723, - keepAliveTimeout: 600, - newCommandTimeout: 300, - sessionOverride: true, - - // Logging - logLevel: 'info', - logTimestamp: true, - logNoColors: false, - - // Security - relaxedSecurityEnabled: true, - allowInsecure: ['chromedriver_autodownload'], - denyInsecure: [] - }, - - capabilities: { - // Common capabilities - platformName: 'Android', - automationName: 'UiAutomator2', - deviceName: 'Android Emulator', - platformVersion: '16.0', - - // App configuration - appPackage: 'com.android.settings', - appActivity: '.Settings', - - // Timeouts - newCommandTimeout: 300, - implicitWaitTimeout: 10000, - - // Performance - skipDeviceInitialization: false, - skipServerInstallation: false, - skipLogcatCapture: false - }, - - // Driver configuration - drivers: { - uiautomator2: { - automationName: 'UiAutomator2', - systemPort: 8200, - skipServerInstallation: false, - disableWindowAnimation: true - }, - xcuitest: { - automationName: 'XCUITest', - useNewWDA: true, - usePrebuiltWDA: true, - shouldTerminateApp: true - } - } -}; \ No newline at end of file diff --git a/docs/development-notes.md b/docs/development-notes.md deleted file mode 100644 index b84b0f2..0000000 --- a/docs/development-notes.md +++ /dev/null @@ -1,61 +0,0 @@ -# Development Notes - -## CI Environment Limitations - -When running in CI environments (like GitHub Actions), some tools may not be fully available: - -- Android emulator may not start due to virtualization limitations -- Hardware acceleration (Intel HAXM/Hyper-V) may not be available -- Some system-level tools may be restricted - -## Testing in CI - -The verification script (`npm run verify-setup`) is designed to work in CI environments and will: -- Show warnings for missing tools that aren't critical for basic verification -- Test installation paths and environment variables -- Validate configuration files -- Check tool availability where possible - -## Local Development - -For full functionality, run the setup on a local development machine where: -- Virtualization is available for Android emulators -- All system permissions are available -- Interactive installation is possible - -## Script Features - -### Verification Script (`scripts/verify-setup.js`) -- Comprehensive tool checking -- Environment variable validation -- Appium Doctor integration -- Emulator availability testing -- Appium server startup testing - -### Setup Scripts -- **macOS/Linux**: `scripts/setup-macos-linux.sh` -- **Windows**: `scripts/setup-windows.ps1` -- Automated tool installation -- Environment configuration -- Platform-specific optimizations - -### AVD Management (`scripts/manage-avd.sh`) -- Create default testing AVDs -- List available virtual devices -- Start/stop emulator management -- Performance optimization - -## Usage Patterns - -1. **First Time Setup**: Run platform-specific setup script -2. **Verification**: Use `npm run verify-setup` to check installation -3. **Development**: Use NPM scripts for common tasks -4. **Troubleshooting**: Reference `docs/troubleshooting.md` - -## Best Practices - -- Always verify setup after running installation scripts -- Use AVD management scripts for consistent emulator configuration -- Check environment variables are properly set -- Use the troubleshooting guide for common issues -- Keep tools updated regularly \ No newline at end of file diff --git a/docs/quick-start.md b/docs/quick-start.md deleted file mode 100644 index f5c75be..0000000 --- a/docs/quick-start.md +++ /dev/null @@ -1,117 +0,0 @@ -# Quick Start Guide - -Get up and running with Appium mobile automation in minutes! - -## πŸš€ Quick Setup - -### 1. Run Setup Script -Choose your platform: - -**macOS/Linux:** -```bash -./scripts/setup-macos-linux.sh -``` - -**Windows:** -```powershell -.\scripts\setup-windows.ps1 -``` - -### 2. Verify Installation -```bash -npm run verify-setup -``` - -### 3. Create Android Virtual Device -```bash -npm run avd:create -``` - -### 4. Start Emulator -```bash -npm run avd:start -``` - -### 5. Start Appium Server -```bash -npm run start-appium -``` - -### 6. Run Sample Test -```bash -npm run test:sample -``` - -## πŸ“‹ Available Commands - -### Setup and Verification -- `npm run verify-setup` - Check if all tools are properly installed -- `npm run doctor` - Run appium-doctor diagnostic - -### Appium Server -- `npm run start-appium` - Start Appium server - -### Android Virtual Device Management -- `npm run avd` - Show AVD management help -- `npm run avd:list` - List available AVDs -- `npm run avd:create` - Create default test AVD -- `npm run avd:start` - Start an AVD -- `npm run avd:stop` - Stop all running emulators - -### Testing -- `npm run test:sample` - Run sample Appium test - -## 🎯 Quick Checklist - -After running the setup script, verify these items: - -- [ ] βœ… Java JDK 17 installed and JAVA_HOME set -- [ ] βœ… Node.js and npm installed -- [ ] βœ… Gradle installed -- [ ] βœ… Android SDK installed and ANDROID_HOME set -- [ ] βœ… Appium 2 installed globally -- [ ] βœ… Appium Doctor passes all checks -- [ ] βœ… Android Virtual Device created -- [ ] βœ… Emulator can start successfully -- [ ] βœ… Appium server starts without errors -- [ ] βœ… Sample test runs successfully - -## πŸ†˜ Quick Troubleshooting - -### Common Issues -1. **Path not found errors**: Restart terminal after setup -2. **Permission errors**: Run with appropriate permissions -3. **Port conflicts**: Kill existing processes or use different ports -4. **Emulator won't start**: Check virtualization is enabled in BIOS - -### Quick Fixes -```bash -# Kill all appium processes -pkill -f appium - -# Kill all emulator processes -npm run avd:stop - -# Restart adb -adb kill-server -adb start-server - -# Check what's running -adb devices -``` - -## πŸ“– Next Steps - -1. **Explore the sample test** in `tests/sample-test.js` -2. **Read the full documentation** in `README.md` -3. **Check troubleshooting guide** in `docs/troubleshooting.md` -4. **Customize configuration** in `config/` directory - -## πŸ’‘ Pro Tips - -- Use Appium Inspector to find element selectors -- Keep emulator running between tests for faster execution -- Use `noReset: true` capability to skip app reinstall -- Set up parallel execution for multiple devices - -Happy testing! πŸŽ‰ \ No newline at end of file diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md deleted file mode 100644 index a6f388a..0000000 --- a/docs/troubleshooting.md +++ /dev/null @@ -1,382 +0,0 @@ -# Troubleshooting Guide - -This guide helps you resolve common issues when setting up and using the Appium mobile automation environment. - -## πŸ”§ Installation Issues - -### Java Issues - -#### Issue: `java: command not found` -**Solution:** -```bash -# macOS -brew install openjdk@17 -echo 'export JAVA_HOME=/opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk/Contents/Home' >> ~/.zshrc -source ~/.zshrc - -# Linux -sudo apt install openjdk-17-jdk -echo 'export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64' >> ~/.bashrc -source ~/.bashrc - -# Windows -# Download and install JDK 17 from Oracle or OpenJDK -# Set JAVA_HOME in System Environment Variables -``` - -#### Issue: Wrong Java version -**Check current version:** -```bash -java -version -javac -version -``` - -**Solution:** Ensure JAVA_HOME points to JDK 17: -```bash -export JAVA_HOME=/path/to/jdk17 -export PATH=$JAVA_HOME/bin:$PATH -``` - -### Node.js Issues - -#### Issue: `node: command not found` -**Solution:** -```bash -# macOS -brew install node - -# Linux -curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash - -sudo apt-get install -y nodejs - -# Windows -# Download from https://nodejs.org/ -``` - -#### Issue: npm permission errors on macOS/Linux -**Solution:** -```bash -# Use nvm (recommended) -curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash -nvm install node -nvm use node - -# Or fix npm permissions -sudo chown -R $(whoami) ~/.npm -``` - -### Android SDK Issues - -#### Issue: `ANDROID_HOME not set` -**Solution:** -```bash -# Find your Android SDK location (usually): -# macOS: ~/Library/Android/sdk -# Linux: ~/Android/Sdk -# Windows: %USERPROFILE%\AppData\Local\Android\Sdk - -export ANDROID_HOME=/path/to/android/sdk -export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools -``` - -#### Issue: Android SDK tools not found -**Solution:** -```bash -# Download command line tools -cd $ANDROID_HOME -# Download from: https://developer.android.com/studio/index.html#command-tools - -# Install required packages -sdkmanager "platform-tools" "platforms;android-33" "build-tools;33.0.2" -``` - -#### Issue: `adb: command not found` -**Solution:** -```bash -# Add platform-tools to PATH -export PATH=$PATH:$ANDROID_HOME/platform-tools - -# Or install standalone adb -# macOS -brew install android-platform-tools - -# Linux -sudo apt install android-tools-adb -``` - -## πŸ”§ Appium Issues - -### Appium Doctor Issues - -#### Issue: "βœ– ANDROID_HOME is NOT set!" -**Solution:** -```bash -export ANDROID_HOME=/path/to/android/sdk -echo 'export ANDROID_HOME=/path/to/android/sdk' >> ~/.bashrc # or ~/.zshrc -``` - -#### Issue: "βœ– JAVA_HOME is NOT set!" -**Solution:** -```bash -export JAVA_HOME=/path/to/jdk17 -echo 'export JAVA_HOME=/path/to/jdk17' >> ~/.bashrc # or ~/.zshrc -``` - -#### Issue: "βœ– adb could not be found" -**Solution:** -```bash -export PATH=$PATH:$ANDROID_HOME/platform-tools -# Restart terminal and run: adb version -``` - -#### Issue: "βœ– android could not be found" -**This is a known warning and can usually be ignored. The `android` command was deprecated.** - -### Appium Server Issues - -#### Issue: `appium: command not found` -**Solution:** -```bash -npm install -g appium@next -npm install -g appium-doctor -``` - -#### Issue: "Error: listen EADDRINUSE :::4723" -**Solution:** -```bash -# Kill existing Appium processes -pkill -f appium - -# Or use different port -appium -p 4724 -``` - -#### Issue: "No drivers have been installed" -**Solution:** -```bash -appium driver install uiautomator2 -appium driver install xcuitest # for iOS -appium driver list -``` - -#### Issue: Appium server crashes on startup -**Check logs and try:** -```bash -# Update Appium -npm update -g appium - -# Reinstall drivers -appium driver uninstall uiautomator2 -appium driver install uiautomator2 -``` - -## πŸ”§ Emulator Issues - -### Android Emulator Issues - -#### Issue: "No emulators found" -**Solution:** -```bash -# List available AVDs -emulator -list-avds - -# Create new AVD -avdmanager create avd -n TestAVD -k "system-images;android-33;google_apis;x86_64" -``` - -#### Issue: Emulator won't start -**Solution:** -```bash -# Check if virtualization is enabled -# Intel: VT-x must be enabled in BIOS -# AMD: AMD-V must be enabled in BIOS - -# For Linux, install KVM -sudo apt install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils - -# Add user to groups -sudo usermod -aG libvirt $USER -sudo usermod -aG kvm $USER -``` - -#### Issue: Emulator is slow -**Solutions:** -- Use x86_64 system images instead of ARM -- Increase RAM allocation in AVD settings -- Enable hardware acceleration (Intel HAXM/Hyper-V) -- Close unnecessary applications - -#### Issue: "Intel HAXM installation failed" -**Solution for macOS:** -```bash -# Enable virtualization in BIOS -# Install Intel HAXM -brew install --cask intel-haxm -``` - -**Solution for Windows:** -```bash -# Disable Hyper-V if using Intel HAXM -dism.exe /Online /Disable-Feature:Microsoft-Hyper-V - -# Or use Hyper-V instead of HAXM -# Enable Windows Hypervisor Platform -dism.exe /Online /Enable-Feature /FeatureName:HypervisorPlatform /All -``` - -### Connection Issues - -#### Issue: "Could not find a connected Android device" -**Solution:** -```bash -# Check connected devices -adb devices - -# Start emulator -emulator -avd YOUR_AVD_NAME - -# Wait for emulator to fully boot -adb wait-for-device -``` - -#### Issue: Device unauthorized -**Solution:** -```bash -# Kill adb server -adb kill-server -adb start-server - -# Accept RSA key fingerprint on device/emulator -# Check 'Always allow from this computer' checkbox -``` - -## πŸ”§ Test Execution Issues - -### WebDriverIO Issues - -#### Issue: "Cannot resolve module 'webdriverio'" -**Solution:** -```bash -npm install webdriverio -``` - -#### Issue: Session creation failed -**Check these points:** -1. Appium server is running -2. Emulator/device is connected -3. Capabilities match your device -4. App package/activity is correct - -#### Issue: Element not found -**Solutions:** -- Use Appium Inspector to find correct selectors -- Add explicit waits -- Check if element is in a different context (web view) - -### Permission Issues - -#### Issue: App permissions not granted -**Solution:** -Add to capabilities: -```javascript -{ - autoGrantPermissions: true, - // or - permissions: ['android.permission.CAMERA', 'android.permission.WRITE_EXTERNAL_STORAGE'] -} -``` - -## πŸ”§ Platform-Specific Issues - -### macOS Issues - -#### Issue: Xcode Command Line Tools not found -**Solution:** -```bash -xcode-select --install -``` - -#### Issue: Homebrew permission issues -**Solution:** -```bash -sudo chown -R $(whoami) /opt/homebrew -``` - -### Windows Issues - -#### Issue: PowerShell execution policy -**Solution:** -```powershell -Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser -``` - -#### Issue: Long path names -**Solution:** -Enable long paths in Windows: -```powershell -# Run as Administrator -New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\FileSystem" -Name "LongPathsEnabled" -Value 1 -PropertyType DWORD -Force -``` - -### Linux Issues - -#### Issue: Permission denied for /dev/kvm -**Solution:** -```bash -sudo usermod -aG kvm $USER -# Logout and login again -``` - -#### Issue: Missing 32-bit libraries -**Solution:** -```bash -sudo apt install libc6:i386 libncurses5:i386 libstdc++6:i386 lib32z1 libbz2-1.0:i386 -``` - -## πŸ”§ Performance Optimization - -### Speed up tests -1. Use `skipDeviceInitialization: true` for faster startup -2. Use `noReset: true` to skip app reinstallation -3. Disable animations: `disableWindowAnimation: true` -4. Use parallel execution for multiple tests - -### Reduce flakiness -1. Add proper waits instead of hard sleeps -2. Use stable locators (ID > accessibility ID > XPath) -3. Handle dynamic content properly -4. Implement retry mechanisms - -## πŸ†˜ Getting Help - -### Log Analysis -Always check logs when troubleshooting: -```bash -# Appium server logs -appium --log-level debug - -# ADB logs -adb logcat - -# Emulator logs -emulator -avd YOUR_AVD -verbose -``` - -### Useful Commands -```bash -# System info -appium-doctor -adb devices -emulator -list-avds - -# Reset everything -adb kill-server -pkill -f appium -pkill -f emulator -``` - -### Community Resources -- [Appium Documentation](https://appium.io/docs/) -- [Appium GitHub Issues](https://github.com/appium/appium/issues) -- [Stack Overflow Appium Tag](https://stackoverflow.com/questions/tagged/appium) -- [Appium Slack Community](https://appium.slack.com/) \ No newline at end of file diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..377538c --- /dev/null +++ b/gradle.properties @@ -0,0 +1,5 @@ +# This file was generated by the Gradle 'init' task. +# https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties + +org.gradle.configuration-cache=true + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..7f0a1b4 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,17 @@ +# This file was generated by the Gradle 'init' task. +# https://docs.gradle.org/current/userguide/platforms.html#sub::toml-dependencies-format + +[versions] +guava = "33.4.6-jre" +junit-jupiter = "5.12.1" +appium = "9.3.0" +slf4j = "2.0.13" +allure-junit5 = "2.29.0" + +[libraries] +guava = { module = "com.google.guava:guava", version.ref = "guava" } +junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit-jupiter" } +appium-java-client = { module = "io.appium:java-client", version.ref = "appium" } +slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } +slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" } +allure-junit5 = { module = "io.qameta.allure:allure-junit5", version.ref = "allure-junit5" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..8bdaf60 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..2a84e18 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..ef07e01 --- /dev/null +++ b/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright Β© 2015 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions Β«$varΒ», Β«${var}Β», Β«${var:-default}Β», Β«${var+SET}Β», +# Β«${var#prefix}Β», Β«${var%suffix}Β», and Β«$( cmd )Β»; +# * compound commands having a testable exit status, especially Β«caseΒ»; +# * various built-in commands including Β«commandΒ», Β«setΒ», and Β«ulimitΒ». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH="\\\"\\\"" + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..db3a6ac --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH= + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/package.json b/package.json deleted file mode 100644 index f43863b..0000000 --- a/package.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "appium-autotest", - "version": "1.0.0", - "description": "Mobile automation testing setup with Appium", - "main": "index.js", - "scripts": { - "verify-setup": "node scripts/verify-setup.js", - "doctor": "npx appium-doctor", - "start-appium": "appium", - "start-emulator": "emulator -list-avds && echo 'Use: emulator -avd '", - "avd": "./scripts/manage-avd.sh", - "avd:list": "./scripts/manage-avd.sh list", - "avd:create": "./scripts/manage-avd.sh create", - "avd:start": "./scripts/manage-avd.sh start", - "avd:stop": "./scripts/manage-avd.sh stop", - "test": "echo \"No tests specified yet\" && exit 0", - "test:sample": "node tests/sample-test.js" - }, - "keywords": [ - "appium", - "mobile", - "automation", - "testing", - "android", - "ios" - ], - "author": "", - "license": "MIT", - "devDependencies": { - "appium": "^2.0.0", - "appium-doctor": "^1.16.2", - "appium-uiautomator2-driver": "^4.2.9" - }, - "dependencies": { - "appium-inspector": "^2023.2.1", - "webdriverio": "^8.0.0" - } -} diff --git a/scripts/verify-setup.js b/scripts/verify-setup.js deleted file mode 100755 index 146af2f..0000000 --- a/scripts/verify-setup.js +++ /dev/null @@ -1,296 +0,0 @@ -#!/usr/bin/env node - -/** - * Verification script for Appium Mobile Automation Setup - * This script checks if all required tools are properly installed and configured - */ - -const { execSync, spawn } = require('child_process'); -const fs = require('fs'); -const path = require('path'); - -class SetupVerifier { - constructor() { - this.results = []; - this.errors = []; - this.warnings = []; - } - - log(message, type = 'info') { - const timestamp = new Date().toISOString(); - const prefix = { - 'info': 'πŸ“‹', - 'success': 'βœ…', - 'error': '❌', - 'warning': '⚠️' - }[type] || 'ℹ️'; - - console.log(`${prefix} ${message}`); - - if (type === 'error') { - this.errors.push(message); - } else if (type === 'warning') { - this.warnings.push(message); - } - } - - async checkCommand(command, expectedVersion = null, description = '') { - try { - const output = execSync(command, { encoding: 'utf-8', stdio: 'pipe' }); - - if (expectedVersion) { - if (output.includes(expectedVersion)) { - this.log(`${description} - Version check passed`, 'success'); - return true; - } else { - this.log(`${description} - Version mismatch. Expected: ${expectedVersion}`, 'warning'); - this.log(`Actual output: ${output.trim()}`, 'info'); - return false; - } - } else { - this.log(`${description} - Found`, 'success'); - return true; - } - } catch (error) { - this.log(`${description} - Not found or failed: ${error.message}`, 'error'); - return false; - } - } - - async checkEnvironmentVariable(varName, description = '') { - const value = process.env[varName]; - if (value) { - this.log(`${description} (${varName}): ${value}`, 'success'); - return true; - } else { - this.log(`${description} (${varName}) - Not set`, 'error'); - return false; - } - } - - async checkDirectory(dirPath, description = '') { - if (fs.existsSync(dirPath)) { - this.log(`${description}: ${dirPath}`, 'success'); - return true; - } else { - this.log(`${description}: ${dirPath} - Not found`, 'error'); - return false; - } - } - - async checkJava() { - this.log('πŸ” Checking Java installation...', 'info'); - - const javaCheck = await this.checkCommand('java -version', '17', 'Java JDK'); - const javacCheck = await this.checkCommand('javac -version', '17', 'Java Compiler'); - const javaHomeCheck = await this.checkEnvironmentVariable('JAVA_HOME', 'Java Home'); - - return javaCheck && javacCheck && javaHomeCheck; - } - - async checkNodeJs() { - this.log('πŸ” Checking Node.js installation...', 'info'); - - const nodeCheck = await this.checkCommand('node --version', null, 'Node.js'); - const npmCheck = await this.checkCommand('npm --version', null, 'NPM'); - - return nodeCheck && npmCheck; - } - - async checkGradle() { - this.log('πŸ” Checking Gradle installation...', 'info'); - return await this.checkCommand('gradle --version', null, 'Gradle'); - } - - async checkAndroidSDK() { - this.log('πŸ” Checking Android SDK installation...', 'info'); - - const androidHomeCheck = await this.checkEnvironmentVariable('ANDROID_HOME', 'Android SDK Home'); - - if (androidHomeCheck) { - const androidHome = process.env.ANDROID_HOME; - const platformToolsCheck = await this.checkDirectory(path.join(androidHome, 'platform-tools'), 'Platform Tools'); - const buildToolsCheck = await this.checkDirectory(path.join(androidHome, 'build-tools'), 'Build Tools'); - const adbCheck = await this.checkCommand('adb version', null, 'ADB (Android Debug Bridge)'); - - return platformToolsCheck && buildToolsCheck && adbCheck; - } - - return false; - } - - async checkAppium() { - this.log('πŸ” Checking Appium installation...', 'info'); - - const appiumCheck = await this.checkCommand('appium --version', null, 'Appium Server'); - - if (appiumCheck) { - try { - const drivers = execSync('appium driver list', { encoding: 'utf-8', stdio: 'pipe' }); - if (drivers.includes('uiautomator2')) { - this.log('UIAutomator2 driver - Installed', 'success'); - } else { - this.log('UIAutomator2 driver - Not installed', 'warning'); - } - } catch (error) { - this.log('Failed to check Appium drivers', 'warning'); - } - } - - return appiumCheck; - } - - async checkAppiumDoctor() { - this.log('πŸ” Running Appium Doctor...', 'info'); - - try { - const output = execSync('appium-doctor', { encoding: 'utf-8', stdio: 'pipe' }); - - if (output.includes('βœ”') || output.includes('All checks passed')) { - this.log('Appium Doctor - All checks passed', 'success'); - return true; - } else { - this.log('Appium Doctor - Some checks failed', 'warning'); - this.log('Full Appium Doctor output:', 'info'); - console.log(output); - return false; - } - } catch (error) { - this.log(`Appium Doctor failed: ${error.message}`, 'error'); - return false; - } - } - - async checkEmulator() { - this.log('πŸ” Checking Android Emulator...', 'info'); - - try { - const output = execSync('emulator -list-avds', { encoding: 'utf-8', stdio: 'pipe' }); - const avds = output.trim().split('\n').filter(line => line.trim()); - - if (avds.length > 0) { - this.log(`Found ${avds.length} AVD(s): ${avds.join(', ')}`, 'success'); - return true; - } else { - this.log('No Android Virtual Devices found', 'warning'); - this.log('Create an AVD in Android Studio or using avdmanager', 'info'); - return false; - } - } catch (error) { - this.log(`Emulator check failed: ${error.message}`, 'error'); - return false; - } - } - - async testAppiumServer() { - this.log('πŸ” Testing Appium Server startup...', 'info'); - - return new Promise((resolve) => { - const appiumProcess = spawn('appium', ['--session-override'], { - stdio: 'pipe' - }); - - let serverStarted = false; - const timeout = setTimeout(() => { - if (!serverStarted) { - appiumProcess.kill(); - this.log('Appium Server - Startup timeout', 'error'); - resolve(false); - } - }, 10000); - - appiumProcess.stdout.on('data', (data) => { - const output = data.toString(); - if (output.includes('Appium REST http interface listener started')) { - serverStarted = true; - clearTimeout(timeout); - appiumProcess.kill(); - this.log('Appium Server - Started successfully', 'success'); - resolve(true); - } - }); - - appiumProcess.stderr.on('data', (data) => { - const output = data.toString(); - if (output.includes('Error') || output.includes('EADDRINUSE')) { - clearTimeout(timeout); - appiumProcess.kill(); - this.log(`Appium Server - Error: ${output}`, 'error'); - resolve(false); - } - }); - - appiumProcess.on('error', (error) => { - clearTimeout(timeout); - this.log(`Appium Server - Failed to start: ${error.message}`, 'error'); - resolve(false); - }); - }); - } - - async runVerification() { - console.log('πŸš€ Starting Appium Mobile Automation Setup Verification...\n'); - - const checks = [ - { name: 'Java JDK 17', fn: () => this.checkJava() }, - { name: 'Node.js', fn: () => this.checkNodeJs() }, - { name: 'Gradle', fn: () => this.checkGradle() }, - { name: 'Android SDK', fn: () => this.checkAndroidSDK() }, - { name: 'Appium', fn: () => this.checkAppium() }, - { name: 'Android Emulator', fn: () => this.checkEmulator() }, - { name: 'Appium Doctor', fn: () => this.checkAppiumDoctor() }, - { name: 'Appium Server Test', fn: () => this.testAppiumServer() } - ]; - - for (const check of checks) { - try { - const result = await check.fn(); - this.results.push({ name: check.name, passed: result }); - console.log(''); // Add spacing between checks - } catch (error) { - this.log(`${check.name} - Unexpected error: ${error.message}`, 'error'); - this.results.push({ name: check.name, passed: false }); - } - } - - this.printSummary(); - } - - printSummary() { - console.log('\nπŸ“Š VERIFICATION SUMMARY'); - console.log('========================'); - - let passedCount = 0; - - this.results.forEach(result => { - const status = result.passed ? 'βœ… PASS' : '❌ FAIL'; - console.log(`${status} ${result.name}`); - if (result.passed) passedCount++; - }); - - console.log(`\nπŸ“ˆ Overall: ${passedCount}/${this.results.length} checks passed`); - - if (this.errors.length > 0) { - console.log('\n❌ ERRORS TO FIX:'); - this.errors.forEach(error => console.log(` β€’ ${error}`)); - } - - if (this.warnings.length > 0) { - console.log('\n⚠️ WARNINGS:'); - this.warnings.forEach(warning => console.log(` β€’ ${warning}`)); - } - - if (passedCount === this.results.length) { - console.log('\nπŸŽ‰ All checks passed! Your mobile automation setup is ready!'); - } else { - console.log('\nπŸ”§ Some issues found. Please address the errors and warnings above.'); - } - } -} - -// Run verification -const verifier = new SetupVerifier(); -verifier.runVerification().catch(error => { - console.error('❌ Verification failed:', error); - process.exit(1); -}); \ No newline at end of file diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..15bb8b5 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,14 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * For more detailed information on multi-project builds, please refer to https://docs.gradle.org/9.0.0/userguide/multi_project_builds.html in the Gradle documentation. + */ + +plugins { + // Apply the foojay-resolver plugin to allow automatic download of JDKs + id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' +} + +rootProject.name = 'appium-autotest' +include('app') diff --git a/tests/sample-test.js b/tests/sample-test.js deleted file mode 100644 index 748c854..0000000 --- a/tests/sample-test.js +++ /dev/null @@ -1,104 +0,0 @@ -// Sample Appium test to verify the setup is working (Appium 2.x + WebdriverIO v8) - -const { remote } = require('webdriverio'); - -// βœ… Capabilities updated for Appium 2.x -const capabilities = { - platformName: 'Android', - 'appium:automationName': 'UiAutomator2', - 'appium:deviceName': 'Android Emulator', - 'appium:platformVersion': '16.0', // Adjust based on your emulator - 'appium:appPackage': 'com.android.settings', - 'appium:appActivity': '.Settings', - 'appium:newCommandTimeout': 300, - 'appium:autoGrantPermissions': true -}; - -// WebDriverIO options -const wdOpts = { - protocol: 'http', // Added explicitly - hostname: '127.0.0.1', - port: 4723, - path: '/', // Needed for Appium 2.x if using default server - logLevel: 'info', - capabilities -}; - -async function runSampleTest() { - console.log('πŸš€ Starting Appium sample test...'); - - let driver; - - try { - // Create Appium session - console.log('πŸ“± Connecting to Appium server...'); - driver = await remote(wdOpts); - console.log('βœ… Connected to Appium server successfully'); - - // Wait for the app to load - console.log('⏳ Waiting for Settings app to load...'); - await driver.pause(3000); - - // Get current app package & activity - const currentPackage = await driver.getCurrentPackage(); - const currentActivity = await driver.getCurrentActivity(); - console.log(`πŸ“¦ Current package: ${currentPackage}`); - console.log(`🎯 Current activity: ${currentActivity}`); - - // Validate the app launched - if (currentPackage === 'com.android.settings') { - console.log('βœ… Settings app launched successfully'); - } else { - console.warn('⚠️ Unexpected app package detected'); - } - - // Find and click the search button (if exists) - console.log('πŸ” Looking for Settings elements...'); - try { - const searchElement = await driver.$('//android.widget.ImageButton[@content-desc="Search settings"]'); - if (await searchElement.isDisplayed()) { - console.log('βœ… Found search button'); - await searchElement.click(); - console.log('πŸ” Clicked search button'); - await driver.pause(1000); - } - } catch { - console.log('ℹ️ Search button not found, skipping...'); - } - - // Validate page source contains Settings text - console.log('πŸ“„ Getting page source...'); - const pageSource = await driver.getPageSource(); - if (pageSource.includes('Settings') || pageSource.includes('settings')) { - console.log('βœ… Page source contains expected content'); - } else { - console.warn('⚠️ Page source verification failed'); - } - - console.log('πŸŽ‰ Sample test completed successfully!'); - } catch (error) { - console.error('❌ Test failed:', error.message); - throw error; - } finally { - if (driver) { - console.log('πŸ”š Closing Appium session...'); - await driver.deleteSession(); - console.log('βœ… Session closed'); - } - } -} - -// Run test when executed directly -if (require.main === module) { - runSampleTest() - .then(() => { - console.log('βœ… All tests passed!'); - process.exit(0); - }) - .catch((error) => { - console.error('❌ Test suite failed:', error); - process.exit(1); - }); -} - -module.exports = { runSampleTest }; \ No newline at end of file