CodeceptJS is a modern end to end testing framework with a special BDD-style syntax. The test is written as a linear scenario of user's action on a site.

Feature('CodeceptJS demo');

Scenario('check Welcome page on site', (I) => {

Tests are expected to be written in ECMAScript 6. Each test is described inside a Scenario function with I object passed into it. I object is an actor, an abstraction for a testing user. I is a proxy object for currently enabled Helpers.

  "helpers": {
    "WebDriverIO": {
      "url": "http://localhost",
      "browser": "chrome"

For current config all methods of I will be taken from WebDriverIO helper. This is done to allow easy switching of running backends so you could replace WebDriverIO with Protractor or Nightmare helpers.

How It Works

Tests are written in synchronous way. Test scenarios should be linear, so tests by themselves should not include promises or callbacks as well. However, behind the scene all actions are wrapped in promises inside the I object. Global promise chain is initialized before each test and all I.* calls will be appended to it as well as setup and teardown.

If you want to get information from a running test you can use await inside async function and special methods of helpers started with grab prefix.

Scenario('try grabbers', async (I) => {
  var title = await I.grabTitle();

then you can use those variables in assertions:

var title = await I.grabTitle();
var assert = require('assert');
assert.equal(title, 'CodeceptJS');



Test execution can be paused in any place of a test with pause() call. This also launches interactive console where you can call actions of I object.


You can also use pause() to check the web application in a browser. Press ENTER to resume test execution.

To debug test step-by-step type next and press Enter. The next step will be executed and interactive shell will be shown again.

To see all available commands press TAB two times to see list of all actions included in I.

If a test is failing you can prevent browser from closing by putting pause() command into After() hook. This is very helpful to debug failing tests. This way you can keep the same session and try different actions on a page to get the idea what went wrong.


Interactive shell can be started outside the test context by running

codeceptjs shell

Screenshot on failure

By default CodeceptJS saves a screenshot of a failed test. This can be configured in screenshotOnFail Plugin

Step By Step Report

To see how the test was executed, use stepByStepReport Plugin. It saves a screenshot of each passed step and shows them in a nice slideshow.


Common preparation steps like opening a web page, logging in a user, can be placed in Before or Background hook:

Feature('CodeceptJS Demonstration');

Before((I) => { // or Background

Scenario('test some forms', (I) => {'Create User');
  I.see('User is valid');

Scenario('test title', (I) => {
  I.seeInTitle('Example application');

Same as Before you can use After to run teardown for each scenario.


If you need to run complex setup before all tests and teardown this afterwards you can use BeforeSuite and AfterSuite functions. BeforeSuite and AfterSuite have access to I object, but BeforeSuite/AfterSuite don't have an access to the browser because it's not running at this moment. You can use them to execute handlers that will setup your environment. BeforeSuite/AfterSuite will work only for a file where it was declared (so you can declare different setups for files)

BeforeSuite((I) => {

AfterSuite((I) => {

Here are some ideas where to use BeforeSuite hooks.


To specify the exact area on a page where actions can be performed you can use within function. Everything executed in its context will be narrowed to context specified by locator:

Usage: within('section', ()=>{})

within('.js-signup-form', () => {
  I.fillField('user[login]', 'User');
  I.fillField('user[email]', '[email protected]');
  I.fillField('user[password]', '[email protected]');'button');
I.see('There were problems creating your account.');

within can also work with iframes

When running steps inside a within block will be shown with a shift:


Within can return a value which can be used in a scenario:

// inside async function
const val = await within('#sidebar', () => {
  return I.grabTextFrom({ css: 'h1' });
I.fillField('Description', val);


There is a simple way to add additional comments to your test scenario. Use say command to print information to screen:

I.say('I am going to publish post');
I.say('I enter title and body');
I.say('I expect post is visible on site');


If you are using Visual Studio Code or other IDE that supports TypeScript Definitions, you can generate step definitions with

codeceptjs def

Now you should include /// <reference path="./steps.d.ts" /> into your test files to get method autocompletion while writing tests.


Like in Mocha you can use x and only to skip tests or making a single test to run.


Retry Step

If you have a step which often fails you can retry execution for this single step. Use retry() function before an action to ask CodeceptJS to retry this step on failure:


If you'd like to retry step more than once pass the amount as parameter:


Additional options can be provided to retry so you can set the additional options (defined in promise-retry library).

// retry action 3 times waiting for 0.1 second before next try
I.retry({ retries: 3, minTimeout: 100 }).see('Hello');

// retry action 3 times waiting no more than 3 seconds for last retry
I.retry({ retries: 3, maxTimeout: 3000 }).see('Hello');

// retry 2 times if error with message 'Node not visible' happens
  retries: 2,
  when: err => err.message === 'Node not visible'

Pass a function to when option to retry only when error matches the expected one.

Retry Scenario

When you need to rerun scenarios few times just add retries option added to Scenario declaration.

CodeceptJS implements retries the same way Mocha do; You can set number of a retries for a feature:

Scenario('Really complex', (I) => {
  // test goes here

// alternative
Scenario('Really complex', { retries: 2 }, (I) => {});

This scenario will be restarted two times on a failure.

Retry Feature

To set this option for all scenarios in a file, add retry to a feature:

Feature('Complex JS Stuff').retry(3);

Every Scenario inside this feature will be rerun 3 times. You can make an exception for a specific scenario by passing retries option to a Scenario.

Test Options

Features and Scenarios have their options that can be set by passing a hash after their names:

Feature('My feature', {key: val});

Scenario('My scenario', {key: val}, (I) => {});


By default there is no timeout for tests, however you can change this value for a specific suite:

Feature('Stop me').timeout(5000); // set timeout to 5s

or for the test:

// set timeout to 1s
Scenario("Stop me faster", (I) => {
  // test goes here

// alternative
Scenario("Stop me faster", {timeout: 1000}, (I) => {});

// disable timeout for this scenario
Scenario("Don't stop me", {timeout: 0}, (I) => {});