From 18d180d51b72c509d6791e78980489a80e6cd96a Mon Sep 17 00:00:00 2001 From: Abdul Qadir Khan Date: Tue, 8 Sep 2026 14:18:47 +0530 Subject: [PATCH] Add WebdriverIO onboarding setup with BrowserStack Automate integration - E2E test for Finstack login and add transaction flow using @wdio/browserstack-service - commonCapabilities pattern for shared bstack:options (networkLogs, debug, selfHeal, accessibility) - 10-platform matrix (5 desktop + 5 mobile real devices) matching Playwright/Selenium setups - Local Chrome config via wdio.local.conf.js - Updated .gitignore with WebdriverIO log/artifact entries --- .gitignore | 3 + WebdriverIO/README.md | 141 ++++++++++++++++++ WebdriverIO/package.json | 17 +++ .../tests/e2e-login-add-transaction.spec.js | 74 +++++++++ WebdriverIO/wdio.conf.js | 116 ++++++++++++++ WebdriverIO/wdio.local.conf.js | 21 +++ 6 files changed, 372 insertions(+) create mode 100644 WebdriverIO/README.md create mode 100644 WebdriverIO/package.json create mode 100644 WebdriverIO/tests/e2e-login-add-transaction.spec.js create mode 100644 WebdriverIO/wdio.conf.js create mode 100644 WebdriverIO/wdio.local.conf.js diff --git a/.gitignore b/.gitignore index 6a00483..87ef1c6 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,9 @@ pnpm-lock.yaml Playwright/test-results/ Playwright/playwright-report/ Playwright/log/ +WebdriverIO/log/ +WebdriverIO/logs/ +WebdriverIO/.wdio-* # Environment variables .env diff --git a/WebdriverIO/README.md b/WebdriverIO/README.md new file mode 100644 index 0000000..a7eee59 --- /dev/null +++ b/WebdriverIO/README.md @@ -0,0 +1,141 @@ +# WebdriverIO + BrowserStack Automate — Onboarding Demo + +End-to-end test for **Finstack** (login → add expense transaction) using **WebdriverIO v9** with **`@wdio/browserstack-service`**, in this repo. + +--- + +## Prerequisites + +| | Windows | macOS | +|---|---|---| +| Node.js | ≥ 18 — [nodejs.org](https://nodejs.org) | ≥ 18 — `brew install node` or [nodejs.org](https://nodejs.org) | +| npm | Bundled with Node.js | Bundled with Node.js | +| Chrome | For local runs | For local runs | + +--- + +## Setup + +### macOS + +```bash +cd path/to/Automate-Onboarding/WebdriverIO +npm install +``` + +### Windows + +```powershell +cd path\to\Automate-Onboarding\WebdriverIO +npm install +``` + +--- + +## Run locally (Chrome on your machine) + +### macOS + +```bash +npm run test:local +``` + +### Windows (PowerShell) + +```powershell +npm run test:local +``` + +Uses `wdio.local.conf.js` — connects directly to local Chrome, no BrowserStack credentials needed. + +--- + +## Run on BrowserStack Automate (10 platforms) + +### macOS + +```bash +export BROWSERSTACK_USERNAME= +export BROWSERSTACK_ACCESS_KEY= +npm test +``` + +Or inline: + +```bash +BROWSERSTACK_USERNAME= BROWSERSTACK_ACCESS_KEY= npm test +``` + +### Windows (PowerShell) + +```powershell +$env:BROWSERSTACK_USERNAME="" +$env:BROWSERSTACK_ACCESS_KEY="" +npm test +``` + +### Windows (Command Prompt) + +```cmd +set BROWSERSTACK_USERNAME= +set BROWSERSTACK_ACCESS_KEY= +npm test +``` + +> Credentials are at [automate.browserstack.com](https://automate.browserstack.com) → **Account → Settings**. + +--- + +## npm scripts + +| Script | Config | Where it runs | +|---|---|---| +| `npm run test:local` | `wdio.local.conf.js` | Local Chrome | +| `npm test` | `wdio.conf.js` | BrowserStack (10 platforms) | + +--- + +## Platform matrix (BrowserStack) + +| # | Platform | Browser | +|---|---|---| +| 1 | Windows 11 | Chrome (latest) | +| 2 | Windows 10 | Edge (latest) | +| 3 | Windows 11 | Firefox (latest) | +| 4 | macOS Ventura | Chrome (latest) | +| 5 | macOS Sonoma | Safari (latest) | +| 6 | iPhone 15 (iOS 17) | Safari | +| 7 | iPhone 14 (iOS 16) | Safari | +| 8 | Samsung Galaxy S24 (Android 14) | Chrome | +| 9 | Google Pixel 8 (Android 14) | Chrome | +| 10 | Samsung Galaxy Tab S11 (Android 16) | Chrome | + +--- + +## Project structure + +``` +WebdriverIO/ +├── tests/ +│ └── e2e-login-add-transaction.spec.js # E2E test spec +├── wdio.conf.js # BrowserStack config + capabilities +├── wdio.local.conf.js # Local Chrome config +├── package.json +└── README.md +``` + +--- + +## Test scenario + +| Step | Action | Expected | +|------|--------|----------| +| 1 | Navigate to `/login` | Sign-in button visible | +| 2–3 | Enter email + password | Fields populated | +| 4 | Click Sign In | Redirected to `/dashboard`, greeting visible | +| 5 | Click Add Transaction | Modal dialog opens | +| 6–7 | Enter amount `150.00` + description `Grocery Shopping` | Fields populated | +| 8–9 | Select Category → Food & Dining | Option selected | +| 10–11 | Select Account → Main Checking | Option selected | +| 12 | Submit transaction | — | +| 13 | Verify modal closes | Dashboard greeting still visible | diff --git a/WebdriverIO/package.json b/WebdriverIO/package.json new file mode 100644 index 0000000..763032f --- /dev/null +++ b/WebdriverIO/package.json @@ -0,0 +1,17 @@ +{ + "name": "wdio-browserstack-onboarding", + "version": "1.0.0", + "description": "WebdriverIO + BrowserStack Automate onboarding demo", + "scripts": { + "test": "wdio run wdio.conf.js", + "test:local": "wdio run wdio.local.conf.js" + }, + "devDependencies": { + "@wdio/browserstack-service": "^9.0.0", + "@wdio/cli": "^9.0.0", + "@wdio/local-runner": "^9.0.0", + "@wdio/mocha-framework": "^9.0.0", + "@wdio/spec-reporter": "^9.0.0", + "chromedriver": "latest" + } +} diff --git a/WebdriverIO/tests/e2e-login-add-transaction.spec.js b/WebdriverIO/tests/e2e-login-add-transaction.spec.js new file mode 100644 index 0000000..11d1c9a --- /dev/null +++ b/WebdriverIO/tests/e2e-login-add-transaction.spec.js @@ -0,0 +1,74 @@ +const BASE_URL = 'https://finstack-alpha.vercel.app'; + +describe('T001: E2E: Login and Add an Expense Transaction', () => { + it('should login and add a Grocery Shopping expense transaction', async () => { + // Step 1: Navigate to login page + await browser.url(`${BASE_URL}/login`); + await $('#sign_in').waitForDisplayed({ timeout: 30000 }); + + // Step 2: Enter email + await $('#email').setValue('testuser@bstackbank.com'); + + // Step 3: Enter password + await $('#password').setValue('Test@1234'); + + // Step 4: Click Sign In and wait for dashboard + await $('#sign_in').click(); + await browser.waitUntil( + async () => (await browser.getUrl()).includes('/dashboard'), + { timeout: 60000, timeoutMsg: 'Dashboard URL not reached within 60s' } + ); + const greeting = await $('h1*=Good morning'); + await greeting.waitForDisplayed({ timeout: 15000 }); + + // Step 5: Click '+ Add Transaction' button — scoped to header area, not dialog + const addTransactionBtn = await $( + '//div[contains(@class,"flex") and contains(@class,"gap-3")]//button[normalize-space()="Add Transaction"]' + ); + await addTransactionBtn.waitForClickable({ timeout: 15000 }); + await addTransactionBtn.click(); + const dialog = await $('[data-slot="dialog-content"]'); + await dialog.waitForDisplayed({ timeout: 15000 }); + + // Step 6: Enter amount + const amountField = await $('#amount'); + await amountField.waitForDisplayed({ timeout: 10000 }); + await amountField.clearValue(); + await amountField.setValue('150.00'); + + // Step 7: Enter description + await $('#description').setValue('Grocery Shopping'); + + // Step 8 & 9: Select Category → Food & Dining + const categoryBtn = await $( + '//div[contains(@class,"grid") and contains(@class,"gap-2") and .//*[contains(text(),"Category")]]//button[contains(@class,"border-input")]' + ); + await categoryBtn.waitForClickable({ timeout: 10000 }); + await categoryBtn.click(); + const foodOption = await $('div[role="option"]=Food & Dining'); + await foodOption.waitForClickable({ timeout: 10000 }); + await foodOption.click(); + + // Step 10 & 11: Select Account → Main Checking + const accountBtn = await $( + '//div[contains(@class,"grid") and contains(@class,"gap-2") and .//*[contains(text(),"Account")]]//button[contains(@class,"border-input")]' + ); + await accountBtn.waitForClickable({ timeout: 10000 }); + await accountBtn.click(); + const mainCheckingOption = await $('div[role="option"]=Main Checking'); + await mainCheckingOption.waitForClickable({ timeout: 10000 }); + await mainCheckingOption.click(); + + // Step 12: Submit the transaction (scoped inside dialog to avoid header button) + const submitBtn = await $( + '//*[@data-slot="dialog-content"]//button[normalize-space()="Add Transaction"]' + ); + await submitBtn.waitForClickable({ timeout: 10000 }); + await browser.execute((el) => el.scrollIntoView(true), submitBtn); + await submitBtn.click(); + + // Step 13: Verify modal closes and dashboard is shown + await dialog.waitForDisplayed({ timeout: 15000, reverse: true }); + await $('h1*=Good morning').waitForDisplayed({ timeout: 10000 }); + }); +}); diff --git a/WebdriverIO/wdio.conf.js b/WebdriverIO/wdio.conf.js new file mode 100644 index 0000000..891ce5b --- /dev/null +++ b/WebdriverIO/wdio.conf.js @@ -0,0 +1,116 @@ +exports.config = { + // ─── BrowserStack hub ──────────────────────────────────────────────────────── + user: process.env.BROWSERSTACK_USERNAME || '', + key: process.env.BROWSERSTACK_ACCESS_KEY || '', + + // ─── Test files ────────────────────────────────────────────────────────────── + specs: ['./tests/**/*.spec.js'], + exclude: [], + + // ─── Parallel instances ────────────────────────────────────────────────────── + maxInstances: 5, + + // ─── Common BrowserStack capabilities (merged into every capability) ───────── + commonCapabilities: { + 'bstack:options': { + debug: true, + networkLogs: true, + consoleLogs: 'info', + interactiveDebugging: true, + }, + }, + + // ─── Per-platform capabilities ─────────────────────────────────────────────── + capabilities: [ + // ── Desktop ── + { + browserName: 'chrome', + browserVersion: 'latest', + 'bstack:options': { os: 'Windows', osVersion: '11' }, + }, + { + browserName: 'edge', + browserVersion: 'latest', + 'bstack:options': { os: 'Windows', osVersion: '10' }, + }, + { + browserName: 'chrome', + browserVersion: 'latest', + 'bstack:options': { os: 'OS X', osVersion: 'Ventura' }, + }, + { + browserName: 'firefox', + browserVersion: 'latest', + 'bstack:options': { os: 'Windows', osVersion: '11' }, + }, + { + browserName: 'safari', + browserVersion: 'latest', + 'bstack:options': { os: 'OS X', osVersion: 'Sonoma' }, + }, + // ── Mobile Real Devices ── + { + browserName: 'safari', + 'bstack:options': { deviceName: 'iPhone 15', osVersion: '17' }, + }, + { + browserName: 'chrome', + 'bstack:options': { deviceName: 'Samsung Galaxy S24', osVersion: '14.0' }, + }, + { + browserName: 'chrome', + 'bstack:options': { deviceName: 'Google Pixel 8', osVersion: '14.0' }, + }, + { + browserName: 'safari', + 'bstack:options': { deviceName: 'iPhone 14', osVersion: '16' }, + }, + { + browserName: 'chrome', + 'bstack:options': { deviceName: 'Samsung Galaxy Tab S11', osVersion: '16.0' }, + }, + ], + + // ─── Framework ─────────────────────────────────────────────────────────────── + framework: 'mocha', + mochaOpts: { + ui: 'bdd', + timeout: 120000, + }, + + // ─── Services ──────────────────────────────────────────────────────────────── + services: [ + [ + 'browserstack', + { + browserstackLocal: true, + testObservability: true, + testObservabilityOptions: { + projectName: 'Automate_Onboarding', + buildName: 'Onboarding_Build', + buildTag: 'e2e' + }, + accessibility: true, + // Optional configuration options + accessibilityOptions: { + 'wcagVersion': 'wcag21aa', + 'includeIssueType': { + 'bestPractice': false, + 'needsReview': true + }, + }, + selfHeal: true, + }, + ], + ], + + // ─── Reporters ─────────────────────────────────────────────────────────────── + reporters: ['spec'], +}; + +// Merge commonCapabilities into every capability's bstack:options +exports.config.capabilities.forEach((cap) => { + const common = exports.config.commonCapabilities['bstack:options'] || {}; + cap['bstack:options'] = Object.assign({}, common, cap['bstack:options']); +}); + diff --git a/WebdriverIO/wdio.local.conf.js b/WebdriverIO/wdio.local.conf.js new file mode 100644 index 0000000..98e57b6 --- /dev/null +++ b/WebdriverIO/wdio.local.conf.js @@ -0,0 +1,21 @@ +const { config: bstackConfig } = require('./wdio.conf.js'); + +exports.config = { + ...bstackConfig, + + // ─── Override: run locally against Chrome ──────────────────────────────────── + user: undefined, + key: undefined, + hostname: undefined, + + maxInstances: 1, + + capabilities: [ + { + browserName: 'chrome', + }, + ], + + // Local runs use no BrowserStack service — TRA reporting is via Automate runs only. + services: [], +};