Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ pnpm-lock.yaml
Playwright/test-results/
Playwright/playwright-report/
Playwright/log/
WebdriverIO/log/
WebdriverIO/logs/
WebdriverIO/.wdio-*

# Environment variables
.env
Expand Down
141 changes: 141 additions & 0 deletions WebdriverIO/README.md
Original file line number Diff line number Diff line change
@@ -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=<your-username>
export BROWSERSTACK_ACCESS_KEY=<your-access-key>
npm test
```

Or inline:

```bash
BROWSERSTACK_USERNAME=<your-username> BROWSERSTACK_ACCESS_KEY=<your-access-key> npm test
```

### Windows (PowerShell)

```powershell
$env:BROWSERSTACK_USERNAME="<your-username>"
$env:BROWSERSTACK_ACCESS_KEY="<your-access-key>"
npm test
```

### Windows (Command Prompt)

```cmd
set BROWSERSTACK_USERNAME=<your-username>
set BROWSERSTACK_ACCESS_KEY=<your-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 |
17 changes: 17 additions & 0 deletions WebdriverIO/package.json
Original file line number Diff line number Diff line change
@@ -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"
}
}
74 changes: 74 additions & 0 deletions WebdriverIO/tests/e2e-login-add-transaction.spec.js
Original file line number Diff line number Diff line change
@@ -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 });
});
});
116 changes: 116 additions & 0 deletions WebdriverIO/wdio.conf.js
Original file line number Diff line number Diff line change
@@ -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']);
});

21 changes: 21 additions & 0 deletions WebdriverIO/wdio.local.conf.js
Original file line number Diff line number Diff line change
@@ -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: [],
};
Loading