npm package: @feminashajahan/nextest
Description: Cross Platform Mobile Test Framework (Android + iOS) | Appium + Playwright + Cucumber + TypeScript
Latest version: 1.0.1

# WTHR Mobile Automation Framework

Cross-platform mobile test automation for the **WTHR app** (Android + iOS), built with:

- **Appium 2** — native mobile automation (UiAutomator2 / XCUITest)
- **Playwright** — web / WebView automation (Safari, Chromium, WebKit)
- **Cucumber.js** — BDD with Gherkin feature files
- **TypeScript** — fully typed framework
- **WebdriverIO** — Appium client

---

## Project Structure

```
UnifiedUIAutomation/
├── cucumber.js                        # Cucumber runner config (parallel, tags, paths)
├── package.json                       # Dependencies & npm scripts
├── tsconfig.json                      # TypeScript compiler config
├── .env.example                       # All supported environment variables
│
├── src/
│   ├── config/
│   │   ├── appiumCapabilities.ts      # All Appium capability sets + smart selector
│   │   └── playwrightConfig.ts        # Playwright browser launch options
│   │
│   ├── core/                          # Reusable action + assertion libraries
│   │   ├── AppiumActions.ts           # tap, swipe, scroll, gestures, wait…
│   │   ├── PlaywrightActions.ts       # navigate, click, fill, scroll, screenshot…
│   │   └── Assertions.ts             # Unified assertions (Appium + Playwright + generic)
│   │
│   ├── drivers/
│   │   └── DriverManager.ts           # Singleton manager for Appium + Playwright sessions
│   │
│   ├── utils/
│   │   ├── PlatformManager.ts         # isAndroid() / isIOS() helpers
│   │   ├── ReportManager.ts           # Console logging + screenshot + JSON run log
│   │   └── ScreenRecordingUtil.ts     # Start/stop MP4 recording per scenario
│   │
│   └── test/                          # All test-specific code
│       ├── features/
│       │   └── wthr/
│       │       └── Wthr13.feature     # Gherkin scenarios
│       ├── hooks/
│       │   └── hooks.ts               # Before / After lifecycle hooks
│       ├── pages/
│       │   ├── BasePage.ts            # Base page with appium + pw + assert
│       │   └── wthr/
│       │       └── InitialPage.ts     # WTHR app page object
│       ├── steps/
│       │   └── wthr/
│       │       └── Wthr13Steps.ts     # Cucumber step definitions
│       └── world/
│           └── CustomWorld.ts         # Cucumber World (driver + playwrightPage)
│
└── scripts/
    └── generate-report.ts             # Merges worker JSONs → HTML report
```

---

## Prerequisites

| Tool | Version | Purpose |
|---|---|---|
| Node.js | ≥ 18 | Runtime |
| Appium | 2.x | Mobile automation server |
| Appium UiAutomator2 driver | latest | Android |
| Appium XCUITest driver | latest | iOS |
| Android SDK / ADB | latest | Emulator / device management |
| Xcode | ≥ 15 | iOS Simulator / real device |
| Java | ≥ 11 | Appium dependency |

### Install Appium and drivers

```bash
npm install -g appium
appium driver install uiautomator2
appium driver install xcuitest
```

---

## Setup

### 1. Install dependencies

```bash
npm install
```

### 2. Install Playwright browsers

```bash
npm run install:browsers
```

### 3. Configure environment

```bash
cp .env.example .env
# Edit .env with your device details, BrowserStack credentials, etc.
```

### 4. Start Appium server

```bash
appium --port 4723
```

---

## Environment Variables

| Variable | Default | Description |
|---|---|---|
| `PLATFORM` | `android` | `android` or `ios`. **Optional when using npm scripts** — already set inside each script. |
| `EXECUTION_TARGET` | `emulator` (Android) / `simulator` (iOS) | `emulator`, `simulator`, `local-device`, `browserstack` |
| `TAGS` | `@mobileApp` | Cucumber tag filter |
| `PARALLEL_WORKERS` | `1` | Number of concurrent workers |
| `UDID` | `emulator-5554` | Single device UDID (overrides auto-assign) |
| `DEVICE_POOL` | _(auto)_ | Comma-separated UDIDs for parallel runs |
| `APPIUM_HOST` | `127.0.0.1` | Appium server host |
| `APPIUM_PORT` | `4723` | Appium server port |
| `IOS_DEVICE_NAME` | `iPhone 17 Pro` | iOS simulator / device name |
| `IOS_VERSION` | _(auto)_ | iOS platform version — omit to let Appium auto-select |
| `DEVICE_NAME` | `Android Device` | Display name for physical Android device |
| `XCODE_ORG_ID` | _(required for real device)_ | Apple Team ID for code signing |
| `WDA_BUNDLE_ID` | _(optional)_ | Custom WDA bundle ID |
| `BROWSERSTACK_USERNAME` | — | BrowserStack credentials |
| `BROWSERSTACK_ACCESS_KEY` | — | BrowserStack credentials |
| `BS_APP_URL` | — | `bs://` app URL after uploading to BrowserStack |
| `BS_ANDROID_DEVICE` | `Samsung Galaxy S23` | BS Android device name |
| `BS_ANDROID_VERSION` | `13.0` | BS Android OS version |
| `BS_IOS_DEVICE` | `iPhone 15 Pro` | BS iOS device name |
| `BS_IOS_VERSION` | `17` | BS iOS version |
| `BS_PROJECT` | `WTHR Mobile Automation` | BrowserStack project name |
| `BS_BUILD` | _(auto-generated)_ | BrowserStack build name |

> **Note:** `PLATFORM` is baked into every `npm run test:android*` / `npm run test:ios*` script. You only need to set it manually when running `cucumber-js` directly or overriding the default (`android`).

---

## Running Tests

### Android Emulator

```bash
# All scenarios
npm run test:android

# Filter by tag
TAGS=@mobileApp  npm run test:android    # video playback full flow
TAGS=@mobileApp2 npm run test:android    # video icon + menu + suggest
TAGS=@mobileApp3 npm run test:android    # scroll to page end

# With HTML report generated after the run
TAGS=@mobileApp2 npm run test:android:report
```

### iOS Simulator

```bash
# All scenarios
npm run test:ios

# Filter by tag
TAGS=@mobileApp  npm run test:ios        # video playback full flow
TAGS=@mobileApp2 npm run test:ios        # video icon + menu + suggest
TAGS=@mobileApp3 npm run test:ios        # scroll to page end (Safari via Playwright)

# With HTML report
TAGS=@mobileApp3 npm run test:ios:report
```

### Physical Device

```bash
# Android physical device
EXECUTION_TARGET=local-device UDID=<device-udid> npm run test:android

# iOS physical device
EXECUTION_TARGET=local-device UDID=<device-udid> npm run test:ios
```

### With HTML report

```bash
npm run test:android:report
npm run test:ios:report
```

---

## Parallel Execution

Cucumber.js spawns each worker as a separate Node.js process.
Each worker gets its own Appium session, device, and report log.

### Android Emulators in parallel

> Start the required number of AVDs before running.
> UDIDs are auto-assigned: worker 0 → `emulator-5554`, worker 1 → `emulator-5556`, …

```bash
# Start 3 emulators (in separate terminals or via emulator CLI)
emulator -avd Pixel_7_API_34 -port 5554 &
emulator -avd Pixel_7_API_34 -port 5556 &
emulator -avd Pixel_7_API_34 -port 5558 &

# Run with 3 workers
npm run test:android:parallel
# equivalent: PLATFORM=android PARALLEL_WORKERS=3 cucumber-js
```

### iOS Simulators in parallel

> Boot simulators first, then supply their UDIDs via `DEVICE_POOL`.

```bash
xcrun simctl list devices | grep "iPhone 17 Pro"

EXECUTION_TARGET=simulator \
DEVICE_POOL=<udid1>,<udid2>,<udid3> \
PARALLEL_WORKERS=3 \
npm run test:ios:parallel
```

### Physical devices in parallel

```bash
EXECUTION_TARGET=local-device \
DEVICE_POOL=R3CN30XXXXX,R5CT10XXXXX \
PARALLEL_WORKERS=2 \
npm run test:android:parallel
```

### BrowserStack — cloud parallel

> No device management needed. BrowserStack handles concurrency.

```bash
# Android — 5 parallel cloud sessions
npm run test:browserstack:android

# iOS — 5 parallel cloud sessions
npm run test:browserstack:ios

# Custom device + OS version
EXECUTION_TARGET=browserstack \
BS_ANDROID_DEVICE="Google Pixel 8" \
BS_ANDROID_VERSION="14.0" \
PARALLEL_WORKERS=3 \
npm run test:android:parallel
```

---

## Execution Targets Summary

| `EXECUTION_TARGET` | Platform | Server | UDID required |
|---|---|---|---|
| `emulator` | Android | Local Appium | Auto-assigned |
| `simulator` | iOS | Local Appium | Optional (via `DEVICE_POOL`) |
| `local-device` | Android / iOS | Local Appium | Required (`UDID` or `DEVICE_POOL`) |
| `browserstack` | Android / iOS | BS Hub | Not needed |

---

## Scenarios

### @mobileApp — Video Playback Full Flow
Launches WTHR app → dismisses sign-up popup → verifies Top Stories → opens "Center Grove" story → plays video in fullscreen → toggles mute/captions → changes speed to 2x → exits fullscreen.

### @mobileApp2 — Video Icon + Menu + Suggest
Launches WTHR app → navigates to video via bottom nav → opens Menu → changes playback speed to 2x → closes sheet → uses Suggest / Suggest More.

### @mobileApp3 — Scroll to Page End
Launches WTHR app → opens "AES Indiana" story → scrolls page until end or 10 attempts maximum.

> On iOS this scenario uses **Playwright** (WebKit) for Safari/WebView scrolling.

---

## Scroll Behaviour

`scrollUntilEndOrAttempts()` automatically picks the right strategy based on the active driver and context:

| Context | Platform | Driver | Strategy |
|---|---|---|---|
| Native app | Android | Appium | `mobile: scrollGesture` — returns `false` at bottom edge for reliable end detection |
| Native app | iOS | Appium | W3C touch swipe up × maxScrolls |
| WebView (Appium context) | Android / iOS | Appium | JS `window.scrollTo` using `Math.max(body.scrollHeight, documentElement.scrollHeight)` — handles iOS Safari's non-body scroll root |
| Browser / WebView (Playwright) | Android / iOS | Playwright | Same JS strategy via `page.evaluate` |

No manual context switching or platform checks needed in test code — the framework detects the active context at runtime via `driver.getContext()`.

---

## Generating Reports

```bash
# HTML report from last run
npm run report

# Allure report (requires allure CLI)
npm run report:allure
```

Reports are written to:

```
reports/
├── cucumber-report.html         # Cucumber HTML report
├── cucumber-report.json         # Cucumber JSON (used by generate-report.ts)
├── run-log-worker-0.json        # Per-worker run log (merged into HTML report)
├── run-log-worker-1.json        # (one file per parallel worker)
├── screenshots/
│   └── screenshot_w0_<ts>.png   # Failure screenshots (w<workerId>_<timestamp>)
└── screen-recordings/
    └── <scenario-name>.mp4      # Screen recordings per scenario
```

---

## Framework Architecture

```
Feature File (.feature)
        │
        ▼
  Step Definitions  ──►  Page Object (InitialPage)
  (Wthr13Steps.ts)              │
        │               ┌───────┴────────┐
        │               ▼                ▼
  CustomWorld      AppiumActions   PlaywrightActions
  (driver +        (native + WebView) (browser / WebView
  playwrightPage)   via Appium)        via Playwright)
        │
        ▼
  DriverManager  ──►  Appium Session  (emulator / simulator / device / BS)
                  ──►  Playwright Page (WebKit / Chromium / Firefox)
```

### Core layer (`src/core/`)

| Class | Responsibility |
|---|---|
| `AppiumActions` | All reusable native mobile interactions: tap, swipe, scroll (native + WebView auto-detection), type, wait, gestures, device actions |
| `PlaywrightActions` | All reusable web interactions: navigate, click, fill, scroll (cross-browser scrollHeight fix), wait, screenshot |
| `Assertions` | Unified assertion library for both Appium elements and Playwright locators |

Page objects extend `BasePage` and use **only** `this.appium.*`, `this.pw.*`, and `this.assert.*` — no raw driver calls in page objects.

---

## Tech Stack

| Package | Version | Role |
|---|---|---|
| `@cucumber/cucumber` | ^10.8.0 | BDD test runner |
| `webdriverio` | ^8.39.0 | Appium WebDriver client |
| `@playwright/test` | ^1.45.0 | Web / WebView automation |
| `typescript` | ^5.5.0 | Language |
| `ts-node` | ^10.9.2 | TypeScript execution |
| `multiple-cucumber-html-reporter` | ^3.7.0 | HTML report generation |
| `allure-cucumberjs` | ^2.15.1 | Allure report integration |
| `winston` | ^3.13.0 | Structured logging |