Skip to content

Upgrading to CodeceptJS 3

Michael Bodnarchuk edited this page Sep 30, 2020 · 20 revisions

🚀 CodeceptJS 3 is out now. Install it:

npm i codeceptjs

After installing a new version, run codecept3-upgrade which is described below:


CodeceptJS 3.0 is a new version of CodeceptJS with some breaking changes included. You should update your project following this guide to ensure that everything works correctly.

1️⃣ Syntax Change

CodeceptJS 3.0 introduced a new syntax for declaring tests, instead of:

Scenario('title', (I, loginPage) => {})

we use a new way of passing arguments into a test, via destruction:

Scenario('title', ({ I, loginPage }) => {})

This works similarly to inject() command. This change was done to unify accessing dependency injection container, and to provide better TypeScript support.

If you use BDD-style project with Gherkin, no changes needed for this step.

To upgrade your project, you don't need to change manually all your tests.

💪 Use codecept3-upgrade tool to perform the migration. Use it carefully, as it updates your current code.

To upgrade your codebase, commit all your changes, and switch to a new branch. Run upgrade script (even without installing) for a directory where you have your tests:

npx codecept3-upgrade tests

Review the changes in Git Diff and try to execute tests using CodeceptJS 3.0

2️⃣ Grabbers signature change

There were confusion and inconsistency across grab* methods. What they will return if multiple elements are found? A single element or an array? This was the problem as the format was dependent on a page content.

In 3.0 we decided to make all current grab* methods to return a single value only. While we add new methods grab**FromAll that return an array.

For instance, grabTextFrom will always return a single text value, while grabTextFromAll will return an array of values:

await I.grabTextFrom('.username'); => 'john'
await I.grabTextFromAll('.username'); => ['john', 'claudia', 'bill']

Please update parts in your project where you rely on grab* methods to return an array.

Single Value Multiple Values
grabTextFrom 🙌 grabTextFromAll
grabValueFrom 🙌 grabValueFromAll
grabAttributeFrom 🙌grabAttributeFromAll
grabHTMLFrom 🙌 grabHTMLFromAll
grabCssPropertyFrom 🙌 grabCssPropertyFromAll

Single-value grab*From will throw error when no data was matched, while grab*FromAll will return array.

3️⃣ Bootstrap / Teardown Changed

async/await paradigm changed the way we write asynchronous code in NodeJS. However, bootstrap functions were created to use old-style methods of passing done callback inside.

In 3.0 we decided to completely change the way async bootstrap is performed and replace all it with async/await functions:

// before
bootstrap: (done) => {
  server.start().then(done);
},

// after
bootstrap: async () => {
  await server.start();
}

Passing a string as bootstrap function (to require a function from external file) is also not supported:

// before
bootstrap: './server_start.js',

// after
bootstrap: require('./server_start.js'),

The same rules are applied to teardown, bootstrapAll, teardownAll.

4️⃣ Locator Detection Heuristic Change

In 3.0 we added a new rule to auto-detect CSS locator. If a locator starts with [ parser will use it as a CSS locator, without trying to match value by text.

  • Previous behavior: I.click('[user]') - will try to search for a link with [user] text, if no found - take [user] as CSS locator
  • Current behavior: I.click('[user]') - will check only for CSS locator [user]

5️⃣ Improved Parallel Execution with Workers

CodeceptJS has two parallel execution modes:

  • classical run-multiple which spawns independent CodeceptJS processes on system level.
  • threaded run-workers which uses NodeJS workers instead of subprocesses.

Workers are faster, they can communicate with parent thread. Unfortunately, run-workers was not as efficient as run-multiple because there was no way of declaring sophisticated configs for parallel execution. For instance, running tests in 4 threads in 2 browsers could not be set.

We updated workers API to make them as flexible as possible. You don't even need to put complex logic into config! You can create your own parallel runner and customize it to match your expectations:

// don't initialize workers in constructor
const workers = new Workers(null, workerConfig);
// split tests by suites in 2 groups
const testGroups = workers.createGroupsOfSuites(2);

const browsers = ['firefox', 'chrome'];

const configs = browsers.map(browser => {
  return helpers: {
    WebDriver: { browser }
  }
});

for (const config of configs) {
  for (group of groupOfTests) {
    const worker = workers.spawn();
    worker.addTests(group);
    worker.addConfig(config);
  }
}

workers.run();

Learn more about how you can create a parallel runner.

run-multiple will still work but it is considered deprecated and won't be supported.

WebDriverIO helper removed

WebDriverIO helper supported webdriverio v4 library, which is not maintained anymore. It should be easy to switch to WebDriver helper which supports webdriverio v6.