Extending CodeceptJS
Extending
Section titled “Extending”CodeceptJS provides API to run custom code before and after the test and inject custom listeners into the event system.
Plugins
Section titled “Plugins”Plugins allow to use CodeceptJS internal API to extend functionality. Use internal event dispatcher, container, output, promise recorder, to create your own reporters, test listeners, etc.
CodeceptJS includes built-in plugins which extend basic functionality and can be turned on and off on purpose. Taking them as examples you can develop your custom plugins.
A plugin is a basic JS module returning a function. Plugins can have individual configs which are passed into this function:
const defaultConfig = { someDefaultOption: true}
module.exports = function(config) { config = Object.assign(defaultConfig, config); // do stuff}Plugin can register event listeners or hook into promise chain with recorder. See API reference.
To enable your custom plugin in config add it to plugins section. Specify path to node module using require.
"plugins": { "myPlugin": { "require": "./path/to/my/module", "enabled": true }}require- specifies relative path to a plugin file. Path is relative to config file.enabled- to enable this plugin.
If a plugin is disabled (enabled is not set or false) this plugin can be enabled from command line:
npx codeceptjs run --plugin myPluginSeveral plugins can be enabled as well:
npx codeceptjs run --plugin myPlugin,allureExample: Execute code for a specific group of tests
Section titled “Example: Execute code for a specific group of tests”If you need to execute some code before a group of tests, you can mark these tests with a same tag. Then to listen for tests where this tag is included (see test object api).
Let’s say we need to populate database for a group of tests.
// populate database for slow testsconst event = require('codeceptjs').event;
module.exports = function() {
event.dispatcher.on(event.test.before, function (test) {
if (test.tags.indexOf('@populate') >= 0) { recorder.add('populate database', async () => { // populate database for this test }) } });}Example: Check URL before running a test
Section titled “Example: Check URL before running a test”If you want to share bootstrap script or run multiple bootstraps, it’s a good idea to wrap that script into a plugin. Plugin can also execute JS before tests but you need to use internal APIs to synchronize promises.
const { recorder } = require('codeceptjs');
module.exports = function(options) {
event.dispatcher.on(event.all.before, function () { recorder.startUnlessRunning(); // start recording promises recorder.add('do some async stuff', async () => { // your code }); });}Use local CodeceptJS installation to get access to codeceptjs module
CodeceptJS provides an API which can be loaded via require('codeceptjs') when CodeceptJS is installed locally.
These internal objects are available:
codecept: test runner classconfig: current codecept configevent: event listenerrecorder: global promise chainoutput: internal printercontainer: dependency injection container for tests, includes current helpers and support objectshelper: basic helper classactor: basic actor (I) class
API reference is available on GitHub. Also please check the source code of corresponding modules.
Event Listeners
Section titled “Event Listeners”CodeceptJS provides a module with event dispatcher and set of predefined events.
It can be required from codeceptjs package if it is installed locally.
const event = require('codeceptjs').event;
module.exports = function() {
event.dispatcher.on(event.test.before, function (test) {
console.log('--- I am before test --');
});}Available events:
-
event.test.before(test)- async whenBeforehooks from helpers and from test is executed -
event.test.after(test)- async after each test -
event.test.started(test)- sync at the very beginning of a test. Passes a current test object. -
event.test.passed(test)- sync when test passed -
event.test.failed(test, error)- sync when test failed -
event.test.finished(test)- sync when test finished -
event.suite.before(suite)- async before a suite -
event.suite.after(suite)- async after a suite -
event.step.before(step)- async when the step is scheduled for execution -
event.step.after(step)- async after a step -
event.step.started(step)- sync when step starts. -
event.step.passed(step)- sync when step passed. -
event.step.failed(step, err)- sync when step failed. -
event.step.finished(step)- sync when step finishes. -
event.step.comment(step)- sync fired for comments likeI.say. -
event.bddStep.before(bddStep)- async when the gherkin step is scheduled for execution -
event.bddStep.after(bddStep)- async after a gherkin step -
event.all.before- before running tests -
event.all.after- after running tests -
event.all.result- when results are printed -
sync - means that event is fired in the moment of action happens.
-
async - means that event is fired when an actions is scheduled. Use
recorderto schedule your actions.
For further reference look for currently available listeners using event system.
Test Object
Section titled “Test Object”Test events provide a test object with following fields:
titletitle of a testbodytest function as a stringoptsadditional test options like retries, and otherspendingtrue if test is scheduled for execution and false if a test has finishedtagsarray of tags for this testfilepath to a file with a test.stepsarray of executed steps (available only intest.passed,test.failed,test.finishedevent)skipInfoadditional test options when test skipped-
messagestring with reason for skip
-
descriptionstring with test body and others
Step Object
Section titled “Step Object”Step events provide step objects with following fields:
namename of a step, like ‘see’, ‘click’, and othersactorcurrent actor, in most cases itIhelpercurrent helper instance used to execute this stephelperMethodcorresponding helper method, in most cases is the same asnamestatusstatus of a step (passed or failed)prefixif a step is executed insidewithinblock contain within text, like: ‘Within .js-signup-form’.argspassed arguments
Recorder
Section titled “Recorder”To inject asynchronous functions in a test or before/after a test you can subscribe to corresponding event and register a function inside a recorder object. Recorder represents a global promises chain.
Provide a function description as a first parameter, function should return a promise:
const event = require('codeceptjs').event;const recorder = require('codeceptjs').recorder;module.exports = function() {
event.dispatcher.on(event.test.before, function (test) {
const request = require('request');
recorder.add('create fixture data via API', function() { return new Promise((doneFn, errFn) => { request({ baseUrl: 'http://api.site.com/', method: 'POST', url: '/users', json: { name: 'john', email: 'john@john.com' } }), (err, httpResponse, body) => { if (err) return errFn(err); doneFn(); } }); } });}Whenever you execute tests with --verbose option you will see registered events and promises executed by a recorder.
Output
Section titled “Output”Output module provides 4 verbosity levels. Depending on the mode you can have different information printed using corresponding functions.
default: prints basic information usingoutput.printsteps: toggled by--stepsoption, prints step executiondebug: toggled by--debugoption, prints steps, and debug information withoutput.debugverbose: toggled by--verboseprints debug information and internal logs withoutput.log
It is recommended to avoid console.log and use output.* methods for printing.
const output = require('codeceptjs').output;
output.print('This is basic information');output.debug('This is debug information');output.log('This is verbose logging information');Container
Section titled “Container”CodeceptJS has a dependency injection container with Helpers and Support objects. They can be retrieved from the container:
let container = require('codeceptjs').container;
// get object with all helperslet helpers = container.helpers();
// get helper by namelet WebDriver = container.helpers('WebDriver');
// get support objectslet support = container.support();
// get support object by namelet UserPage = container.support('UserPage');
// get all registered pluginslet plugins = container.plugins();New objects can also be added to container in runtime:
let container = require('codeceptjs').container;
container.append({ helpers: { // add helper MyHelper: new MyHelper({ config1: 'val1' }); }, support: { // add page object UserPage: require('./pages/user'); }})Container also contains current Mocha instance:
let mocha = container.mocha();Config
Section titled “Config”CodeceptJS config can be accessed from require('codeceptjs').config.get():
let config = require('codeceptjs').config.get();
if (config.myKey == 'value') { // run hook}Custom Runner
Section titled “Custom Runner”📺 Watch this material on YouTube
CodeceptJS can be imported and used in custom runners. To initialize Codecept you need to create Config and Container objects.
const { codecept: Codecept } = require('codeceptjs');
const config = { helpers: { WebDriver: { browser: 'chrome', url: 'http://localhost' } } };const opts = { steps: true };
const codecept = new Codecept(config, options);codecept.init(testRoot);
// run teststry { await codecept.bootstrap(); codecept.loadTests('*_test.js'); await codecept.run(test);} catch (err) { printError(err); process.exitCode = 1;} finally { await codecept.teardown();}In this way Codecept runner class can be extended.