A production-ready mobile test automation framework with support for both Android and iOS platforms, featuring comprehensive logging, reporting, and app-to-web navigation capabilities.
- Prerequisites
- Setup
- Configuration
- Running Tests
- Features
- Project Structure
- Usage Examples
- Known Issues
- Node.js (v20+)
- Java JDK (v11+)
- Android SDK (for Android testing)
- iOS Simulator/Device + Xcode (for iOS testing)
- Android Emulator or Physical Device
- nvm (Node Version Manager)
- Switch to Node.js v20:
nvm use 20- Install dependencies:
npm install- Install Appium drivers:
# For Android
px appium driver install uiautomator2
# For iOS
px appium driver install xcuitest- Place your app files (optional):
- Android APK:
./app/android/app.apk - iOS IPA:
./app/ios/app.ipa - Currently using pre-installed Settings app for both platforms
- Android APK:
Update device.config.ts to configure your test device:
For Android:
export const deviceConfig = androidConfig; // Switch to thisFor iOS:
export const deviceConfig = iosConfig; // Switch to thisUpdate device details:
export const androidConfig: DeviceConfig = {
platformName: 'Android',
deviceName: 'sdk_gphone64_arm64', // Your device name
platformVersion: '13.0', // Your Android version
automationName: 'UiAutomator2',
appPackage: 'com.android.settings',
appActivity: '.Settings',
newCommandTimeout: 240
};Update env.config.ts for different environments:
export const env = {
baseUrl: process.env.BASE_URL || 'https://api.example.com',
timeout: parseInt(process.env.TIMEOUT || '10000'),
environment: process.env.ENV || 'dev'
};For Android:
~/Library/Android/sdk/platform-tools/adb devices -l
~/Library/Android/sdk/platform-tools/adb shell getprop ro.product.model
~/Library/Android/sdk/platform-tools/adb shell getprop ro.build.version.releaseFor iOS:
# List available simulators
xcrun simctl list devices
# Get device info
instruments -s devicesnvm use 20
npm testnpm test -- --spec=src/features/sample.feature# Run app-to-web tests
npm run test:app-to-web
# Run smoke tests
npm run test:smoke
# Run all tests except app-to-web
npm run test:regression
# Custom tag expression
npm test -- --cucumberOpts.tagExpression='@smoke and not @app-to-web'npm run report- Winston logger for detailed test execution logs
- Logs saved to
logs/test.log - Console and file output
- Timestamped log entries
- Allure reports with rich test details
- Automatic screenshot capture on test failure
- Test execution history
- Trend analysis
- Each scenario runs with a fresh app state
- Automatic app termination and relaunch between scenarios
- Configurable reset behavior (
noReset,fullReset)
- Easy switching between Android and iOS
- Platform-specific configurations
- Shared test code across platforms
- Clean and maintainable test structure
- Reusable page objects
- Separation of test logic and page interactions
Reusable gesture utilities:
- Scroll up/down
- Swipe left/right
- Long press
- Hide keyboard
- Automatically retry failed scenarios (configurable)
- Reduces flaky test failures
- Configurable retry count
- Support for multiple environments (dev, staging, prod)
- Environment-specific configurations
- Environment variables support
- Seamless context switching between native app and webview
- Support for external browser navigation (Chrome/Safari)
- Automatic platform detection for browser switching
- WebView interaction utilities
├── src/
│ ├── features/ # Cucumber feature files
│ │ ├── sample.feature
│ │ └── app-to-web.feature
│ ├── steps/ # Step definitions
│ │ ├── sample.steps.ts
│ │ └── app-to-web.steps.ts
│ ├── pages/ # Page objects
│ │ ├── BasePage.ts
│ │ ├── SettingsPage.ts
│ │ └── WebViewPage.ts
│ └── utils/ # Utilities
│ ├── logger.ts # Winston logger
│ ├── MobileGestures.ts # Gesture utilities
│ └── ContextSwitcher.ts # App/Web context switching
├── app/
│ ├── android/ # Android APK files
│ └── ios/ # iOS IPA files
├── logs/ # Test execution logs
├── allure-results/ # Allure test results
├── device.config.ts # Device configurations (Android/iOS)
├── env.config.ts # Environment configurations
├── wdio.conf.ts # WebdriverIO configuration
├── tsconfig.json # TypeScript configuration
└── package.json # Dependencies and scripts
import { MobileGestures } from '../utils/MobileGestures';
// Scroll down
await MobileGestures.scrollDown();
// Swipe left
await MobileGestures.swipeLeft();
// Long press on element
const element = await $('~myElement');
await MobileGestures.longPress(element, 3000);
// Hide keyboard
await MobileGestures.hideKeyboard();import { ContextSwitcher } from '../utils/ContextSwitcher';
import WebViewPage from '../pages/WebViewPage';
// Switch to browser (auto-detects platform)
await ContextSwitcher.switchToBrowser();
// Navigate to URL
await WebViewPage.navigateToUrl('https://example.com');
// Interact with web elements
await WebViewPage.fillInput('#username', 'testuser');
await WebViewPage.clickElement('#login');
// Switch back to app
await ContextSwitcher.switchBackToApp(appPackage, bundleId);
// Switch to webview (in-app)
await ContextSwitcher.switchToWebView();
// Switch to native app
await ContextSwitcher.switchToNativeApp();import logger from '../utils/logger';
logger.info('Test step started');
logger.error('Test failed with error');
logger.warn('Warning message');import { BasePage } from './BasePage';
class LoginPage extends BasePage {
get usernameInput() {
return $('~username');
}
async login(username: string, password: string) {
await this.tap('~username');
await this.usernameInput.setValue(username);
await this.tap('~password');
await $('~password').setValue(password);
await this.tap('~loginButton');
}
}
export default new LoginPage();External Browser Context Issue: When opening Chrome/Safari as an external app from the Settings app, Appium cannot access the browser's webview context. This is a limitation of Appium's architecture.
Workaround: The app-to-web feature works best when:
- Your app has an embedded WebView (hybrid app)
- You're testing a web app directly (not launching from native app)
- Your app opens URLs in an in-app browser (not external Chrome/Safari)
For external browser testing:
- Use native app automation to open the browser
- Switch to the browser app context
- Use UI Automator selectors to interact with browser elements (limited functionality)
Example for in-app WebView:
// This works - in-app webview
await ContextSwitcher.switchToWebView();
await driver.url('https://example.com');
// This doesn't work - external Chrome browser
await ContextSwitcher.switchToChrome(); // Opens Chrome but can't access webviewTo test the app-to-web feature, you need an app with embedded WebView support.
The expect-webdriverio v5.6.2 package has a structure issue where the entry point is missing. This is automatically fixed by the postinstall script that creates a symlink:
node_modules/expect-webdriverio/lib/index.js → src/index.jsWhat's the problem?
- WebdriverIO's
@wdio/runnertries to import:expect-webdriverio/lib/index.js - But the actual compiled file is located at:
expect-webdriverio/lib/src/index.js - The package's
package.jsonexports field points to/lib/index.js, but this file doesn't exist - This appears to be a build/packaging issue in the
expect-webdriveriov5.6.2 release
How is it fixed?
- The
postinstallscript inpackage.jsonautomatically creates a symbolic link - This link makes
lib/index.jspoint to the actual file atlib/src/index.js - The symlink is recreated automatically every time you run
npm install
Error you might see without the fix:
Error [ERR_MODULE_NOT_FOUND]: Cannot find module
'/path/to/node_modules/expect-webdriverio/lib/index.js'
If you encounter module not found errors after npm install, the postinstall script will handle it automatically.
├── src/
│ ├── features/ # Cucumber feature files
│ ├── steps/ # Step definitions
│ ├── pages/ # Page objects
│ └── utils/ # Utilities (logger)
├── app/
│ ├── android/ # Android APK files
│ └── ios/ # iOS IPA files
├── logs/ # Test execution logs
├── allure-results/ # Allure test results
├── device.config.ts # Device configurations (Android/iOS)
├── wdio.conf.ts # WebdriverIO configuration
├── tsconfig.json # TypeScript configuration
└── package.json # Dependencies and scripts
- ✅ Logging: Winston logger for detailed test execution logs
- ✅ Reporting: Allure reports with screenshots on failure
- ✅ Fresh App State: Each scenario runs with a fresh app state
- ✅ Multi-Platform: Easy switching between Android and iOS
- ✅ Page Object Model: Clean and maintainable test structure
- ✅ Mobile Gestures: Reusable gesture utilities (scroll, swipe, long press)
- ✅ Retry Mechanism: Automatically retry failed scenarios
- ✅ Environment Config: Manage different test environments
- ✅ App-to-Web Navigation: Seamless context switching between native app and webview/browser