cjp-automation
v1.0.56
Published
A Node.js-based test automation framework built on Cucumber.js (BDD) with Selenium WebDriver for web testing and Appium (via WD) for mobile testing. This package is designed to be installed as an npm dependency, providing reusable step definitions, global
Downloads
203
Keywords
Readme
CJP Automation Framework
A Node.js-based test automation framework built on Cucumber.js (BDD) with Selenium WebDriver for web testing and Appium (via WD) for mobile testing. This package is designed to be installed as an npm dependency, providing reusable step definitions, global functions, and reporting out of the box.
Table of Contents
- Overview
- Architecture
- Prerequisites
- Installation
- Project Structure
- Configuration
- Writing Tests
- Reusable Functions Reference
- Running Tests
- Reporting
- CI/CD Integration
- Mobile Testing (Appium)
- Environment Variables
- Troubleshooting
Overview
cjp-automation is a shared automation package that provides:
- Pre-built Cucumber step definitions for common web and mobile actions
- A library of reusable Selenium/Appium functions (click, type, wait, scroll, screenshot, OCR, etc.)
- HTML and JUnit XML report generation
- Support for both headless and headed Chrome browser testing
- Mobile device testing via Appium
- File reading, text extraction, image-to-text (OCR via Tesseract.js)
- Database connectivity support (MSSQL, MySQL, PostgreSQL)
Architecture
┌─────────────────────────────────────────────────────┐
│ Your Test Project │
├─────────────────────────────────────────────────────┤
│ Features/ (Gherkin .feature files) │
│ Features/Step-Definitions/ (Step definition JS) │
│ Test-Data/ (Test data & config) │
├─────────────────────────────────────────────────────┤
│ cjp-automation (this package) │
│ ┌───────────────────────────────────────────────┐ │
│ │ Global-Functions/Reusable-Functions.js │ │
│ │ (Selenium WebDriver + Appium + Utilities) │ │
│ └───────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────┤
│ Report.js → HTML Report Generation │
│ Report_Junit.js → JUnit XML Report Generation │
└─────────────────────────────────────────────────────┘Prerequisites
Before using this framework, ensure you have the following installed:
| Tool | Version | Purpose | |------|---------|---------| | Node.js | >= 14.x | Runtime environment | | npm | >= 6.x | Package manager | | Google Chrome | Latest | Browser for web tests | | ChromeDriver | Matching Chrome version | Browser automation driver | | Java JDK | >= 8 (for mobile) | Required by Appium | | Appium | Latest (for mobile) | Mobile device automation |
Optional (for database testing)
- Microsoft SQL Server client tools (for MSSQL tests)
- MySQL server access (for MySQL tests)
- PostgreSQL server access (for PostgreSQL tests)
Installation
1. Initialize your test project
mkdir my-automation-project
cd my-automation-project
npm init -y2. Install the framework
npm install cjp-automationOn install, the package automatically copies template files into your project root:
Report.js— HTML report generatorReport_Junit.js— JUnit XML report generatorREADME.md— This documentation.gitlab-ci.yml— CI/CD pipeline templateTest-Data/Test.js— Sample test data fileFeatures/Step-Definitions/Common.js— Sample step definitionsFeatures/Test.feature— Sample feature fileReport/Report.json— Report output placeholder
3. Install ChromeDriver
npm install chromedriverMake sure the ChromeDriver version matches your installed Chrome browser version.
Project Structure
After installation, your project should look like this:
my-automation-project/
├── Features/
│ ├── Step-Definitions/
│ │ └── Common.js # Step definitions (customize this)
│ └── Test.feature # Gherkin feature files (add your own)
├── Test-Data/
│ └── Test.js # Test data and variables
├── Report/
│ └── Report.json # Cucumber JSON output (auto-generated)
├── Report.js # HTML report generator script
├── Report_Junit.js # JUnit XML report generator script
├── .gitlab-ci.yml # CI/CD pipeline config
├── .env # Environment variables (create this)
├── node_modules/
│ └── cjp-automation/ # The framework package
├── package.json
└── package-lock.jsonConfiguration
Environment Variables (.env)
Create a .env file in your project root to store sensitive data and configuration:
# Example .env file
BASE_URL=https://your-app-url.com
USERNAME=testuser
PASSWORD=testpassword
DB_HOST=localhost
DB_USER=admin
DB_PASS=secretThe framework uses the dotenv package to load these variables. Access them in your test data or step definitions via process.env.VARIABLE_NAME.
Test Data (Test-Data/Test.js)
Store all test data, URLs, and reusable values here:
// Test-Data/Test.js
exports.link = "https://your-application-url.com";
exports.username = process.env.USERNAME || "defaultUser";
exports.password = process.env.PASSWORD || "defaultPass";
exports.searchTerm = "automation testing";Writing Tests
Feature Files (Gherkin Syntax)
Create .feature files in the Features/ directory using Gherkin syntax:
Feature: User Login
As a registered user
I want to log into the application
So that I can access my dashboard
Scenario Outline: Successful login with valid credentials
Given I go to <link>
When I enter text "//input[@id='username']" with <username>
And I enter text "//input[@id='password']" with <password>
And I click "//button[@id='login']"
Then I wait for "//h1[@class='dashboard-title']" to appear
And I compare text "//h1[@class='dashboard-title']" contains "Welcome"
Examples:
| link | username | password |
| "test.link" | "test.username" | "test.password" |Step Definitions
Step definitions map Gherkin steps to automation code. Edit Features/Step-Definitions/Common.js:
const { Before, Given, When, Then, After, setDefaultTimeout } = require("@cucumber/cucumber");
const { reusableFunctions } = require("cjp-automation");
const test = require("../../Test-Data/Test.js");
require('dotenv').config();
setDefaultTimeout(500000);
// --- Hooks ---
Before(async function() {
// Opens Chrome browser (non-headless, non-mobile)
// Parameters: headless ("Yes"/"No"), mobile emulation ("Yes"/"No")
await reusableFunctions.openBrowser("No", "No");
});
After(async function() {
await reusableFunctions.closePlatform();
});
// --- Navigation ---
Given("I go to {string}", async function(link) {
await reusableFunctions.openLink(eval(link));
});
// --- Input ---
When("I enter text {string} with {string}", async function(field, data) {
await reusableFunctions.enterText(field, eval(data));
});
When("I clear text {string}", async function(field) {
await reusableFunctions.clearText(field);
});
// --- Click ---
When("I click {string}", async function(field) {
await reusableFunctions.click(field);
});
// --- Wait ---
Then("I wait for {string} to appear", async function(field) {
await reusableFunctions.waitForField(field);
});
When("I wait {int} milliseconds", async function(time) {
await reusableFunctions.wait(time);
});
// --- Assertions ---
Then("I compare text {string} contains {string}", async function(field, value) {
await reusableFunctions.compareText(field, value);
});
Then("I check {string} does not exist", async function(field) {
await reusableFunctions.checkFieldDoesNotExist(field);
});
// --- Screenshots ---
Then("I take a screenshot", async function() {
await reusableFunctions.screenshot();
});
// --- Scrolling ---
When("I scroll to {string}", async function(field) {
await reusableFunctions.scrollToElement(field);
});
When("I scroll down", async function() {
await reusableFunctions.scrollDown();
});
// --- Tabs ---
When("I open a new tab", async function() {
await reusableFunctions.openNewTab();
});
When("I switch to tab {int}", async function(tabNumber) {
await reusableFunctions.switchTab(tabNumber);
});
// --- Dropdowns ---
When("I select dropdown {string} then {string}", async function(field, field1) {
await reusableFunctions.selectDropdown(field, field1);
});
// --- iFrames ---
When("I switch to frame {string}", async function(frame) {
await reusableFunctions.switchToFrame(frame);
});
When("I switch to default content", async function() {
await reusableFunctions.switchToDefaultContent();
});
// --- Text Extraction ---
When("I extract text from {string} as text {string}", async function(field, textNumber) {
await reusableFunctions.extractText(field, textNumber);
});
// --- File Upload ---
When("I upload file {string} to {string}", async function(filePath, xpath) {
await reusableFunctions.uploadFileOnline(filePath, xpath);
});Reusable Functions Reference
All functions are accessed via require("cjp-automation").reusableFunctions.
Browser Management
| Function | Parameters | Description |
|----------|-----------|-------------|
| openBrowser(headless, dotmobi) | headless: "Yes"/"No", dotmobi: "Yes"/"No" | Opens Chrome. Set headless for CI. Set dotmobi for mobile emulation (Galaxy S5). |
| closePlatform(platform) | platform: omit or "app" | Closes browser or mobile app session. |
| maximizeWindow() | — | Maximizes the browser window. |
Navigation
| Function | Parameters | Description |
|----------|-----------|-------------|
| openLink(link) | link: URL string | Navigates to the specified URL. |
| openNewTab() | — | Opens a new blank browser tab. |
| switchTab(tabNumber) | tabNumber: 0-based index | Switches to the specified tab. |
Element Interaction
| Function | Parameters | Description |
|----------|-----------|-------------|
| click(field, platform) | field: XPath, platform: omit or "app" | Clicks an element. |
| enterText(field, data, platform) | field: XPath, data: text, platform: omit or "app" | Clears field and types text. |
| clearText(field) | field: XPath | Clears an input field. |
| key(field, sendKeys) | field: XPath, sendKeys: Key constant (e.g., Key.ENTER) | Sends keyboard keys to an element. |
| selectDropdown(field, field1, platform) | field: trigger XPath, field1: option XPath | Clicks dropdown then selects option. |
| uploadFileOnline(filePath, xpath) | filePath: absolute path, xpath: upload input XPath | Uploads a file to a file input element. |
Waits
| Function | Parameters | Description |
|----------|-----------|-------------|
| wait(time, platform) | time: milliseconds, platform: omit or "app" | Static wait/sleep. |
| waitForField(field, platform, time) | field: XPath, platform: omit or "app", time: optional ms | Waits until element is located (default 200s timeout). |
| waitForPageLoading(field, platform) | field: XPath of loading indicator | Waits until a loading element disappears. |
Assertions
| Function | Parameters | Description |
|----------|-----------|-------------|
| compareText(field, value, platform) | field: XPath, value: expected text | Asserts element text contains the expected value. |
| checkFieldDoesNotExist(field, platform) | field: XPath | Asserts element is not present on page. |
| checkTextDoesNotExist(textValue, platform) | textValue: string | Asserts text is not present anywhere on page. |
| checkContain(value1, value2, value3) | value1: source, value2: XPath expression (or ""), value3: expected | Checks XML/text contains expected value. |
Scrolling
| Function | Parameters | Description |
|----------|-----------|-------------|
| scrollToElement(field) | field: XPath | Scrolls element into view. |
| scrollDown(platform) | platform: omit or "app" | Scrolls down the page/screen. |
| scrollRight() | — | Scrolls right (mobile only). |
Frames
| Function | Parameters | Description |
|----------|-----------|-------------|
| switchToFrame(frame) | frame: frame identifier | Switches context to an iframe. |
| switchToDefaultContent() | — | Switches back to main page content. |
Screenshots & OCR
| Function | Parameters | Description |
|----------|-----------|-------------|
| screenshot(platform) | platform: omit or "app" | Takes a screenshot. Stored in reusableFunctions.screen. |
| imageToText() | — | Converts last screenshot to text using Tesseract OCR. Result in reusableFunctions.imageText. |
Text Extraction
| Function | Parameters | Description |
|----------|-----------|-------------|
| extractText(field, textNumber, platform) | field: XPath, textNumber: "1"-"4" | Extracts text from element. Access via reusableFunctions.text1 through reusableFunctions.text4. |
File Operations
| Function | Parameters | Description |
|----------|-----------|-------------|
| readFile(filePath) | filePath: path to text file | Reads first two lines. Access via reusableFunctions.firstLine and reusableFunctions.secondLine. |
| removeFromFile(filePath, firstLine) | filePath: path, firstLine: text to remove | Removes specified text from a file. |
Mobile (Appium)
| Function | Parameters | Description |
|----------|-----------|-------------|
| openPhone(deviceName, app, otherApps) | deviceName: device, app: APK path, otherApps: helper APK path | Configures Appium desired capabilities. |
| startPhone() | — | Initializes the Appium session. |
| hideDeviceKeyboard() | — | Hides the on-screen keyboard. |
| delinkAccount() | — | Custom function to delink an account in a specific app. |
Running Tests
Execute all tests
npx cucumber-js --format json:Report/Report.jsonExecute a specific feature file
npx cucumber-js Features/Test.feature --format json:Report/Report.jsonExecute by tag
Add tags to your scenarios:
@smoke
Scenario: Quick login test
Given I go to "test.link"Then run:
npx cucumber-js --tags "@smoke" --format json:Report/Report.jsonRun in headless mode (for CI/CD)
Update your Before hook:
Before(async function() {
await reusableFunctions.openBrowser("Yes", "No");
});Run with mobile emulation
Before(async function() {
await reusableFunctions.openBrowser("No", "Yes");
});Reporting
Generate HTML Report
After test execution, generate a visual HTML report:
node Report.jsThis creates Report/Report.html and automatically opens it in your browser. The report uses the Bootstrap theme and includes:
- Test pass/fail status
- Scenario execution times
- Step-by-step results
- Platform metadata
Generate JUnit XML Report
For CI/CD integration (GitLab, Jenkins, etc.):
node Report_Junit.jsThis creates Report/Report.xml in standard JUnit format.
Full test + report workflow
npx cucumber-js --format json:Report/Report.json && node Report.jsCI/CD Integration
GitLab CI/CD
The included .gitlab-ci.yml provides a publish pipeline. For test execution, add a test stage:
stages:
- test
- publish
Test:
stage: test
script:
- npm install
- npx cucumber-js --format json:Report/Report.json
- node Report_Junit.js
artifacts:
when: always
paths:
- Report/
reports:
junit: Report/Report.xml
tags:
- automation2
Publish:
stage: publish
script:
- npm publish
rules:
- if: $CI_COMMIT_BRANCH == "main"
tags:
- automation2Jenkins
pipeline {
agent any
stages {
stage('Install') {
steps {
sh 'npm install'
}
}
stage('Test') {
steps {
sh 'npx cucumber-js --format json:Report/Report.json'
sh 'node Report_Junit.js'
}
}
}
post {
always {
junit 'Report/Report.xml'
archiveArtifacts artifacts: 'Report/**'
}
}
}Mobile Testing (Appium)
Setup
Install Appium globally:
npm install -g appiumStart Appium server on port 8000:
appium -p 8000Connect an Android device or start an emulator.
Writing Mobile Tests
const { reusableFunctions } = require("cjp-automation");
// Configure device
await reusableFunctions.openPhone(
"emulator-5554", // Device name
"/apps/MyApp.apk", // Main app path (relative to cwd)
"/apps/HelperApp.apk" // Other apps path (relative to cwd)
);
// Start session
await reusableFunctions.startPhone();
// Interact with elements (use "app" as platform parameter)
await reusableFunctions.click("//android.widget.Button[@text='Login']", "app");
await reusableFunctions.enterText("//android.widget.EditText[@resource-id='username']", "testuser", "app");
await reusableFunctions.wait(2000, "app");
await reusableFunctions.screenshot("app");
// Close
await reusableFunctions.closePlatform("app");Mobile Step Definitions Example
Before(async function() {
await reusableFunctions.openPhone("emulator-5554", "/apps/MyApp.apk", "/apps/Helper.apk");
await reusableFunctions.startPhone();
});
When("I tap {string} on app", async function(field) {
await reusableFunctions.click(field, "app");
});
When("I type {string} into {string} on app", async function(data, field) {
await reusableFunctions.enterText(field, data, "app");
});
After(async function() {
await reusableFunctions.closePlatform("app");
});Environment Variables
The framework supports .env files via the dotenv package. The .env file is gitignored by default.
Usage in Test Data
// Test-Data/Test.js
require('dotenv').config();
exports.link = process.env.BASE_URL || "https://default-url.com";
exports.username = process.env.TEST_USERNAME;
exports.password = process.env.TEST_PASSWORD;Usage in Step Definitions
require('dotenv').config();
Given("I login with environment credentials", async function() {
await reusableFunctions.enterText("//input[@id='user']", process.env.USERNAME);
await reusableFunctions.enterText("//input[@id='pass']", process.env.PASSWORD);
await reusableFunctions.click("//button[@type='submit']");
});Troubleshooting
Common Issues
| Issue | Solution |
|-------|----------|
| ChromeDriver version mismatch | Install ChromeDriver matching your Chrome version: npm install chromedriver@<version> |
| Element not found | Increase wait time or verify XPath. Use waitForField() before interacting. |
| Timeout exceeded | Increase setDefaultTimeout() in step definitions or pass custom time to waitForField(). |
| Cannot find module 'cjp-automation' | Run npm install again. Check package.json has the dependency. |
| ECONNREFUSED on port 8000 | Start Appium server: appium -p 8000 |
| .env variables undefined | Ensure require('dotenv').config() is called before accessing process.env. |
| Headless mode not working | Verify Chrome supports headless. Update Chrome and ChromeDriver. |
Debug Tips
Run with non-headless mode to visually see what's happening:
await reusableFunctions.openBrowser("No", "No");Add waits between steps to slow down execution:
await reusableFunctions.wait(3000);Take screenshots at failure points:
await reusableFunctions.screenshot(); // Screenshot is stored in reusableFunctions.screen (base64)Use
extractTextto debug element values:await reusableFunctions.extractText("//div[@class='error']", "1"); console.log(reusableFunctions.text1);
Quick Start Example
Here's a complete working example to get you started in under 5 minutes:
1. Setup
mkdir login-test && cd login-test
npm init -y
npm install cjp-automation chromedriver2. Edit Test-Data/Test.js
exports.link = "https://your-app.com/login";
exports.username = "[email protected]";
exports.password = "SecurePass123";3. Edit Features/Test.feature
Feature: Login
As a user
I want to log in
So that I can access the dashboard
Scenario Outline: Valid login
Given I go to <link>
When I enter text "//input[@name='email']" with <username>
And I enter text "//input[@name='password']" with <password>
And I click "//button[@type='submit']"
Then I wait for "//h1[contains(text(),'Dashboard')]" to appear
Examples:
| link | username | password |
| "test.link" | "test.username" | "test.password" |4. Run
npx cucumber-js --format json:Report/Report.json
node Report.jsAuthor
Justin Chetty
Version
1.0.55
License
Private / Internal Use
