npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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

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

  1. Overview
  2. Architecture
  3. Prerequisites
  4. Installation
  5. Project Structure
  6. Configuration
  7. Writing Tests
  8. Reusable Functions Reference
  9. Running Tests
  10. Reporting
  11. CI/CD Integration
  12. Mobile Testing (Appium)
  13. Environment Variables
  14. 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 -y

2. Install the framework

npm install cjp-automation

On install, the package automatically copies template files into your project root:

  • Report.js — HTML report generator
  • Report_Junit.js — JUnit XML report generator
  • README.md — This documentation
  • .gitlab-ci.yml — CI/CD pipeline template
  • Test-Data/Test.js — Sample test data file
  • Features/Step-Definitions/Common.js — Sample step definitions
  • Features/Test.feature — Sample feature file
  • Report/Report.json — Report output placeholder

3. Install ChromeDriver

npm install chromedriver

Make 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.json

Configuration

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=secret

The 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.json

Execute a specific feature file

npx cucumber-js Features/Test.feature --format json:Report/Report.json

Execute 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.json

Run 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.js

This 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.js

This creates Report/Report.xml in standard JUnit format.

Full test + report workflow

npx cucumber-js --format json:Report/Report.json && node Report.js

CI/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:
    - automation2

Jenkins

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

  1. Install Appium globally:

    npm install -g appium
  2. Start Appium server on port 8000:

    appium -p 8000
  3. Connect 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

  1. Run with non-headless mode to visually see what's happening:

    await reusableFunctions.openBrowser("No", "No");
  2. Add waits between steps to slow down execution:

    await reusableFunctions.wait(3000);
  3. Take screenshots at failure points:

    await reusableFunctions.screenshot();
    // Screenshot is stored in reusableFunctions.screen (base64)
  4. Use extractText to 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 chromedriver

2. 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.js

Author

Justin Chetty

Version

1.0.55

License

Private / Internal Use