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 🙏

© 2024 – Pkg Stats / Ryan Hefner

cucumber-jest

v0.3.1

Published

a jest transformer for executing cucumber tests in jest

Downloads

981

Readme

cucumber-jest

Build Passing Build Passing Branches Functions Lines Statements

A transformer for executing cucumber tests in jest

npm i cucumber-jest -D

Table of Contents

Gherkin Features

| Supported | Feature | Notes | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------- | | :white_check_mark: | And | | | :white_check_mark: | Background | | | :white_check_mark: | But | | | :white_check_mark: | Comments | | | :white_check_mark: | Data Table | | | :white_check_mark: | DocString | if it finds the docString is JSON, it will parse it for you | | | Rule | haven't seen examples of this; not sure if it's worth it | | :white_check_mark: | Scenario | | | :white_check_mark: | Scenario Outline | |

Cucumber Features

| Supported | Feature | Notes | | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | | :white_check_mark: | After | called after each scenario in a feature file | | :white_check_mark: | AfterAll | called after the feature file is completed; unlike Cucumber, you will have access to "this" context here. | | | Attachments | use a reporter like jest-html-reporters and its attach utility instead | | :white_check_mark: | Before | called before each scenario per feature file | | :white_check_mark: | BeforeAll | called before the feature file is started; unlike Cucumber, you will have access to "this" context here. | | :white_check_mark: | expressions | | | :white_check_mark: | Given | | | | setDefaultTimeout | use jest.setTimeout or set the timeout property in your jest config | | :white_check_mark: | setDefinitionFunctionWrapper | | | :white_check_mark: | setWorldConstructor | | | :white_check_mark: | Tags | | | :white_check_mark: | Then | | | :white_check_mark: | When | |

Getting Started

Jest Config

You'll need to add the following to your jest config:

{
    "moduleFileExtensions": [
        "feature",
        // <--- *1
        "js",
        "json",
        "ts",
        "tsx"
    ],
    "setupFilesAfterEnv": [
        "<rootDir>/node_modules/cucumber-jest/dist/init.js",
        // <--- *2
        "<rootDir>/path/to/your/world.ts",
        "<rootDir>/path/to/your/hooks.tsx",
        "<rootDir>/path/to/your/steps.ts"
    ],
    "transform": {
        "^.+\\.(js|jsx|ts|tsx)$": "babel-jest",
        "^.+\\.(feature)$": "cucumber-jest"
        // <--- *3
    },
    "testMatch": [
        "<rootDir>/path/to/your/*.feature"
        // <--- *4
    ]
}
  1. Add feature to moduleFileExtensions
  2. Add "<rootDir>/node_modules/cucumber-jest/dist/init.js" to setupFilesAfterEnv
    • this file calls cucumber's supportCodeLibraryBuilder.reset which sets up cucumber to start capturing registered hooks / steps
    • it's important to note that this file must be list first in the setupFilesAfterEnv list of files; before your world, hooks, or step files
  3. Add "^.+\\.(feature)$": "cucumber-jest" as a transformer
  4. Add "<rootDir>/path/to/your/*.feature" as a testMatch pattern

Example

The code-snippets below are taken from the example project

World

setWorldConstructor allows you to set the context of "this" for your steps/hooks definitions.

This can be helpful when you want to maintain state, access globals, or assign component testing classes. The values are accessible within all Hooks and Steps.

import { setWorldConstructor } from '@cucumber/cucumber';

import Element from './element';
import { $server, $spy } from './mocks';

/**
 *  Element class is a helper provided in the example project
 *  for more details, please see the example project
 */

export class TestWorld {
    $server = $server; // reference to mws (mock-server-worker) setupServer
    $spy = $spy;       // jest.fn()

    email = new Element({name: 'email'});
    extraEmails = new Element({name: 'extraEmails'});
    firstName = new Element({name: 'firstName'});
    lastName = new Element({name: 'lastName'});
    password = new Element({name: 'password'});
    reset = new Element({dataId: 'reset'});
    submit = new Element({dataId: 'submit'});
    successAlert = new Element({dataId: 'successAlert'});
    showExtraEmailsAlert = new Element({dataId: 'showExtraEmailsAlert'});
}

setWorldConstructor(
    TestWorld
);

Hooks

*Unlike cucumber.js, "this" is accessible inside BeforeAll and AfterAll hooks, and they receive two additional parameters: Spec and the fileName

If you are using these two params with typescript, you'll notice that it shows a typing error. The library does not currently augment cucumber's hook typings to add the two additional params; if anyone has a solution, please contribute.

import { After, AfterAll, Before, BeforeAll } from '@cucumber/cucumber';
import { Spec } from 'cucumber-jest';
import { advanceTo, clear } from 'jest-date-mock';
import React from 'react';
import ReactDOM from 'react-dom';
import { act } from 'react-dom/test-utils';

import { SignUp } from '../src/signUp';
import { TestWorld } from './world';

const $root = document.createElement('div');

document.body.appendChild($root);

BeforeAll(async function (this: TestWorld, spec: Spec, fileName: string) {
    console.log('spec::', spec);
    console.log('fileName::', fileName);

    this.$server.listen();

    advanceTo(new Date('2019-12-01T15:00:00.000Z'));

    act(() => {
        ReactDOM.render(<SignUp / >, $root);
    });
});

Before(function (this: TestWorld) {
    // do something before each spec
});

After(function (this: TestWorld) {
    this.$spy.mockClear();
    this.$server.resetHandlers();
});

AfterAll(async function (this: TestWorld) {
    clear();

    act(() => {
        ReactDOM.unmountComponentAtNode($root);
    });

    this.$server.close();
});

Steps

import { Given, Then, When } from '@cucumber/cucumber';

import type { TestWorld } from './world';

Given(/^the (\S+) component rendered$/,
    async function (this: TestWorld, name) {
        await this[name].click();
    }
);

When('the {word} button is clicked',
    async function (this: TestWorld, name) {
        await this[name].click();

        await this[name].waitForEnabled();
    }
);

When(
    /^the (\S+) text input value is (.*)$/,
    async function (this: TestWorld, name, value) {
        await this[name].setValue(value);
    }
);

When(
    /^the (\S+) checkbox input is (checked|not checked)$/,
    async function (this: TestWorld, name, state) {
        const currentValue = this[name].getAttribute('checked');

        if (currentValue !== (state === 'checked')) {
            await this[name].click();
        }
    }
);

Then(
    /^(GET|PUT|POST|DELETE) (.*) is called with the (request body|params):$/,
    function (this: TestWorld, method, url, type, value) {
        const hasBody = type === 'request body';

        expect(this.$spy).toHaveBeenCalledWith({
            url,
            method,
            ...(hasBody ? {data: value} : {params: value})
        });
    }
);

Then(
    /^the (\S+) is (visible|not visible)$/,
    async function (this: TestWorld, name, state) {
        await this[name][
            state === 'visible' ? 'waitForInDom' : 'waitForNotInDom'
            ]();

        expect(this[name].isInDom()).toEqual(state === 'visible');
    }
);

Then(
    /^the (\S+) inner text is "(.*)"$/,
    function (this: TestWorld, name, innerText) {
        expect(this[name].innerText()).toEqual(innerText);
    }
);

Feature

Feature: Sign Up

  Scenario: Without Extra Emails
    Given the firstName text input value is James
    And the lastName text input value is Dean
    And the email text input value is [email protected]
    And the password text input value is itsASecretShh...
    When the submit button is clicked
    Then POST /api/sign-up is called with the request body:
      """
       {
           "firstName": "James",
           "lastName": "Dean",
           "email": "[email protected]",
           "password": "itsASecretShh...",
           "extraEmails": false,
           "date": "2019-12-01T15:00:00.000Z"
       }
      """
    And the successAlert is visible
    And the showExtraEmailsAlert is not visible

  Scenario: With Extra Emails
    Given the firstName text input value is James
    And the lastName text input value is Dean
    And the email text input value is [email protected]
    And the password text input value is itsASecretShh...
    And the extraEmails checkbox input is checked
    When the submit button is clicked
    Then POST /api/sign-up is called with the request body:
      """
       {
           "firstName": "James",
           "lastName": "Dean",
           "email": "[email protected]",
           "password": "itsASecretShh...",
           "extraEmails": true,
           "date": "2019-12-01T15:00:00.000Z"
       }
      """
    And the successAlert is visible
    And the showExtraEmailsAlert is visible

Output

Below is an example output from running tests against the example project

  PASS  test/features/scenario.feature (110 MB heap size)
  Feature: Sign Up
    Scenario: Without Extra Emails
      ✓ Given the firstName text input value is James (21 ms)
      ✓ And the lastName text input value is Dean (8 ms)
      ✓ And the email text input value is [email protected] (8 ms)
      ✓ And the password text input value is itsASecretShh... (10 ms)
      ✓ When the submit button is clicked (200 ms)
      ✓ Then POST /api/sign-up is called with the request body:
       {
           "firstName": "James",
           "lastName": "Dean",
           "email": "[email protected]",
           "password": "itsASecretShh...",
           "extraEmails": false,
           "date": "2019-12-01T15:00:00.000Z"
       } (2 ms)
      ✓ And the successAlert is visible (26 ms)
      ✓ And the showExtraEmailsAlert is not visible (12 ms)
    Scenario: With Extra Emails
      ✓ Given the firstName text input value is James (16 ms)
      ✓ And the lastName text input value is Dean (9 ms)
      ✓ And the email text input value is [email protected] (11 ms)
      ✓ And the password text input value is itsASecretShh... (10 ms)
      ✓ And the extraEmails checkbox input is checked (45 ms)
      ✓ When the submit button is clicked (84 ms)
      ✓ Then POST /api/sign-up is called with the request body:
       {
           "firstName": "James",
           "lastName": "Dean",
           "email": "[email protected]",
           "password": "itsASecretShh...",
           "extraEmails": true,
           "date": "2019-12-01T15:00:00.000Z"
       } (1 ms)
      ✓ And the successAlert is visible (2 ms)
      ✓ And the showExtraEmailsAlert is visible (1 ms)

Test Suites: 1 passed, 1 total
Tests:       17 passed, 17 total
Snapshots:   0 total
Time:        5.066 s

Tags [Experimental]

Built-In

There are two tags that come built in:

| tag | description | | ------ | ---------------------------------------------- | | @skip | skips the scenario | | @debug | only execute that scenario in the feature file |

@debug Example

Feature: Sign Up W/ Background & Tags Example

  Background:
    Given the firstName text input value is James
    And the lastName text input value is Dean
    And the email text input value is [email protected]
    And the password text input value is itsASecretShh...

  @debug
  Scenario: Without Extra Emails
    When the submit button is clicked
    Then POST /api/sign-up is called with the request body:
      """
       {
           "firstName": "James",
           "lastName": "Dean",
           "email": "[email protected]",
           "password": "itsASecretShh...",
           "extraEmails": false,
           "date": "2019-12-01T15:00:00.000Z"
       }
      """
    And the successAlert is visible
    And the showExtraEmailsAlert is not visible

  Scenario: With Extra Emails
    When the extraEmails checkbox input is checked
    And the submit button is clicked
    Then POST /api/sign-up is called with the request body:
      """
       {
           "firstName": "James",
           "lastName": "Dean",
           "email": "[email protected]",
           "password": "itsASecretShh...",
           "extraEmails": true,
           "date": "2019-12-01T15:00:00.000Z"
       }
      """
    And the successAlert is visible
    And the showExtraEmailsAlert is visible

@skip Example

Feature: Sign Up W/ Background & Tags Example

  Background:
    Given the firstName text input value is James
    And the lastName text input value is Dean
    And the email text input value is [email protected]
    And the password text input value is itsASecretShh...

  @skip
  Scenario: Without Extra Emails
    When the submit button is clicked
    Then POST /api/sign-up is called with the request body:
      """
       {
           "firstName": "James",
           "lastName": "Dean",
           "email": "[email protected]",
           "password": "itsASecretShh...",
           "extraEmails": false,
           "date": "2019-12-01T15:00:00.000Z"
       }
      """
    And the successAlert is visible
    And the showExtraEmailsAlert is not visible

  Scenario: With Extra Emails
    When the extraEmails checkbox input is checked
    And the submit button is clicked
    Then POST /api/sign-up is called with the request body:
      """
       {
           "firstName": "James",
           "lastName": "Dean",
           "email": "[email protected]",
           "password": "itsASecretShh...",
           "extraEmails": true,
           "date": "2019-12-01T15:00:00.000Z"
       }
      """
    And the successAlert is visible
    And the showExtraEmailsAlert is visible

Custom

Tags are supported by using the environment variable TAGS with a comma delimited list of strings. For best results, use the tags only at the scenario level. Support for feature level will be added later. Unfortunately, jest cli does not support custom commands and will throw an error if you try to use them. For this reason, we need to use environment variables to pass these.

Inclusive Example:

TAGS=foo,bar jest

TAGS=foo, bar jest # space is trimmed

Exclusive Example:

TAGS="not foo" jest

TAGS="not foo, not bar" jest

Exclusive & Inclusive Example:

TAGS="bar, not foo" jest

Variables

You can inject values into your feature files using variables.

When variable files are found and injected into a feature file has variables injected, a temporary feature file with the same name is written to your os's temporary folder; eg. for mac users, /var/folders/sb/2wr3vgcd1sxg4tgv1mr8dtq80000gn/T/cucumber-jest

If you need to figure out the temporary folder path, run the jest with this environment variable CUCUMBER_JEST_SHOW_TEMP_PATH=true.

Use Without ENV

Set up a file where the name matches the file you're targeting and include .vars in the name, eg.

| Feature File | Variable File | | ----------------------- | ----------------------------- | | scenarioOutline.feature | scenarioOutline.vars.js | | scenarioOutline.feature | scenarioOutline.vars.json | | scenarioOutline.feature | scenarioOutline.vars.ts |

Use With ENV

Alternatively, you can have variables target an environment by setting the ENV environment variable and using it in the variables file name.

Below are multiple examples with different ENV values and extensions:

| Environment Variable | Feature File | Variable File | | -------------------- | ----------------------- | --------------------------------- | | ENV=dev | scenarioOutline.feature | scenarioOutline.vars.dev.js | | ENV=dev | scenarioOutline.feature | scenarioOutline.vars.dev.json | | ENV=dev | scenarioOutline.feature | scenarioOutline.vars.dev.ts | | ENV=qa | scenarioOutline.feature | scenarioOutline.vars.qa.js | | ENV=qa | scenarioOutline.feature | scenarioOutline.vars.qa.json | | ENV=qa | scenarioOutline.feature | scenarioOutline.vars.qa.ts |

Example Variable Use

Properties in your variable files can be used in your feature file by prefixing the json path with $, eg.

| Property Name In Feature File | Property Name In Variable File | | ----------------------------- | ------------------------------ | | $firstName | firstName |

** nested structures are also supported, please see examples below.

Example With ENV & Flat Variables Structure

Testing Command:

ENV=dev $(npm bin)/jest

Variables File: scenarioOutline.vars.dev.ts

export default {
    email: '[email protected]',
    firstName: 'James',
    lastName: 'Dean',
    password: 'itsASecretShh...'
};

Feature File: scenarioOutline.feature

Feature: Sign Up

  Scenario Outline: Submitting <prefix> Extra Emails
    Given the firstName text input value is $firstName
    And the lastName text input value is $lastName
    And the email text input value is $email
    And the password text input value is $password
    And the extraEmails checkbox input is <extraEmails>
    When the submit button is clicked
    Then POST /api/sign-up is called with the request body:
      """
       {
           "firstName": "$firstName",
           "lastName": "$lastName",
           "email": "$email",
           "password": "$password",
           "extraEmails": <extraEmailsValue>,
           "date": "2019-12-01T15:00:00.000Z"
       }
      """
    And the successAlert is <successAlert>
    And the showExtraEmailsAlert is <showExtraEmailsAlert>

    Examples:
      | prefix  | extraEmails | extraEmailsValue | successAlert | showExtraEmailsAlert |
      | With    | not checked | false            | visible      | not visible          |
      | Without | checked     | true             | visible      | visible              |

Example With ENV & Nested Variables Structure

Testing Command:

ENV=qa $(npm bin)/jest

Variables File: scenarioOutline.vars.qa.ts

export default {
    user: {
        email: '[email protected]',
        firstName: 'James',
        lastName: 'Dean',
        password: 'itsASecretShh...'
    }
};

Feature File: scenarioOutline.feature

Feature: Sign Up - Scenario Outline [Nested]

  Scenario Outline: Submitting <prefix> Extra Emails
    Given the firstName text input value is $user.firstName
    And the lastName text input value is $user.lastName
    And the email text input value is $user.email
    And the password text input value is $user.password
    And the extraEmails checkbox input is <extraEmails>
    When the submit button is clicked
    Then POST /api/sign-up is called with the request body:
      """
       {
           "firstName": "$user.firstName",
           "lastName": "$user.lastName",
           "email": "$user.email",
           "password": "$user.password",
           "extraEmails": <extraEmailsValue>,
           "date": "2019-12-01T15:00:00.000Z"
       }
      """
    And the successAlert is <successAlert>
    And the showExtraEmailsAlert is <showExtraEmailsAlert>

    Examples:
      | prefix  | extraEmails | extraEmailsValue | successAlert | showExtraEmailsAlert |
      | With    | not checked | false            | visible      | not visible          |
      | Without | checked     | true             | visible      | visible              |

** to accessing properties, please use standard javascript property pathing.

Examples

| Type | Feature File | Variable File | | -------------------------------- | ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | | Global Variables Without Env | scenarioOutlineNestedGlobal | global.vars.ts | | Variables Without Env | scenarioOutline | scenarioOutline.vars.ts | | Global Variables With Env | scenarioOutlineNestedGlobalEnv | global.vars.dev.ts | | Variables With (dev) | scenarioOutlineNested | scenarioOutlineNested.vars.dev.ts |

Variable File Rules

  • must be located within your project
  • uses an extension defined in your jest configuration: moduleFileExtensions
  • can be parsed into a javascript object, eg. .js, .json, .ts
  • global files, as the name implies, can be used in any feature file
  • when providing an env, it will prioritize files with an env over those that do not. eg, ENV=dev = feature.vars.dev > feature.vars
  • global and feature specific variable files will get merged together into a single object.
  • when global and feature specific variable files have the same variable paths, the feature specific values will be prioritized.

Cucumber Docs

Here are some useful links to the cucumber-js docs: