- Master process events
- Worker process events
- Sharing data between master and worker processes
- Intercepting events
- Passing information between event handlers
- Parallel execution of plugin code
Event | Description |
---|---|
INIT |
Will be triggered before any job start (run or readTests ). If handler returns a promise then job will start only after the promise will be resolved. Emitted only once no matter how many times job will be performed. |
BEFORE_FILE_READ |
Will be triggered on test files parsing before reading the file. The handler accepts data object with file , testplane (helper which will be available in test file) and testParser (TestParserAPI object) fields. |
AFTER_FILE_READ |
Will be triggered on test files parsing right after reading the file. The handler accepts data object with file and testplane (helper which will be available in test file) fields. |
AFTER_TESTS_READ |
Will be triggered right after tests read via readTests or run methods with TestCollection object. |
RUNNER_START |
Will be triggered after worker farm initialization and before test execution. If a handler returns a promise, tests will be executed only after the promise is resolved. The handler accepts an instance of a runner as the first argument. You can use this instance to emit and subscribe to any other available events. |
RUNNER_END |
Will be triggered after test execution and before worker farm ends. If a handler returns a promise, worker farm will be ended only after the promise is resolved. The handler accepts an object with tests execution statistics. |
NEW_WORKER_PROCESS |
Will be triggered after new subprocess is spawned. The handler accepts a restricted process object with only send method. |
SESSION_START |
Will be triggered after browser session initialization. If a handler returns a promise, tests will be executed only after the promise is resolved. The handler accepts an instance of webdriverIO as the first argument and an object with a browser identifier as the second. |
SESSION_END |
Will be triggered after the browser session ends. If a handler returns a promise, tests will be executed only after the promise is resolved. The handler accepts an instance of webdriverIO as the first argument and an object with a browser identifier as the second. |
BEGIN |
Will be triggered before test execution, but after all the runners are initialized. |
END |
Will be triggered just before RUNNER_END event. The handler accepts a stats of tests execution. |
SUITE_BEGIN |
Test suite is about to execute. |
SUITE_END |
Test suite execution is finished. |
TEST_BEGIN |
Test is about to execute. |
TEST_END |
Test execution is finished. |
TEST_PASS |
Test passed. |
TEST_FAIL |
Test failed. |
TEST_PENDING |
Test is skipped. |
RETRY |
Test failed but went to retry. |
ERROR |
Generic (no tests) errors. |
INFO |
Reserved. |
WARNING |
Reserved. |
EXIT |
Will be triggered when SIGTERM is received (for example, Ctrl + C). The handler can return a promise. |
Event | Description |
---|---|
BEFORE_FILE_READ |
Will be triggered on test files parsing before reading the file. The handler accepts data object with file , testplane (helper which will be available in test file) and testParser (TestParserAPI object) fields. |
AFTER_FILE_READ |
Will be triggered on test files parsing right after reading the file. The handler accepts data object with file and testplane (helper which will be available in test file) fields. |
AFTER_TESTS_READ |
Will be triggered right after tests read each time some file is being reading during test run. |
NEW_BROWSER |
Will be triggered after new browser instance created. The handler accepts an instance of webdriverIO as the first argument and an object with a browser identifier and version as the second. |
UPDATE_REFERENCE |
Will be triggered after updating reference image. |
Events which are triggered in the main process and subprocesses can not share information between each other, for example:
module.exports = (testplane) => {
let flag = false;
testplane.on(testplane.events.RUNNER_START, () => {
flag = true;
});
testplane.on(testplane.events.NEW_BROWSER, () => {
// outputs `false`, because `NEW_BROWSER` event was triggered in a subprocess,
// but `RUNNER_START` was not
console.log(flag);
});
testplane.on(testplane.events.RUNNER_END, () => {
// outputs `true`
console.log(flag);
});
};
But you can solve such problem this way:
module.exports = (testplane, opts) => {
testplane.on(testplane.events.RUNNER_START, () => {
opts.flag = true;
});
testplane.on(testplane.events.NEW_BROWSER, () => {
// outputs `true`, because properties in a config (variable `opts` is a part of a config)
// which have raw data types are passed to subprocesses after `RUNNER_START` event
console.log(opts.flag);
});
};
You have the ability to intercept events in plugins and translate them to other events:
module.exports = (testplane) => {
testplane.intercept(testplane.events.TEST_FAIL, ({event, data: test}) => {
test.skip({reason: 'intercepted failure'});
return {event: testplane.events.TEST_PENDING, test};
});
testplane.on(testplane.events.TEST_FAIL, (test) => {
// this event handler will never be called
});
testplane.on(testplane.evenst.TEST_PENDING, (test) => {
// this event handler will always be called instead of 'TEST_FAIL' one
});
};
If for some reason interceptor should not translate passed event to another one you can return the same object or some falsey value:
module.exports = (testplane) => {
testplane.intercept(testplane.events.TEST_FAIL, ({event, data}) => {
return {event, data};
// return;
// return null;
// return false;
});
testplane.on(testplane.events.TEST_FAIL, (test) => {
// this event handler will be called as usual because interceptor does not change event
});
};
If for some reason interceptor should ignore passed event and do not translate it to any other listeners you can return an empty object:
module.exports = (testplane) => {
testplane.intercept(testplane.events.TEST_FAIL, ({event, data}) => {
return {};
});
testplane.on(testplane.events.TEST_FAIL, (test) => {
// this event handler will NEVER be called because interceptor ignores it
});
};
The above feature can be used to delay triggering of some events, for example:
module.exports = (testplane) => {
const intercepted = [];
testplane.intercept(testplane.events.TEST_FAIL, ({event, data}) => {
intercepted.push({event, data});
return {};
});
testplane.on(testplane.events.END, () => {
intercepted.forEach(({event, data}) => testplane.emit(event, data));
});
};
Event |
---|
SUITE_BEGIN |
SUITE_END |
TEST_BEGIN |
TEST_END |
TEST_PASS |
TEST_FAIL |
RETRY |
Events that are triggered in the master process and in Testplane workers cannot exchange information via global variables.
For example, this approach will not work:
module.exports = (testplane) => {
let flag = false;
testplane.on(testplane.events.RUNNER_START, () => {
flag = true;
});
testplane.on(testplane.events.NEW_BROWSER, () => {
// false will be displayed because the NEW_BROWSER event
// is triggered in the testplane worker, and RUNNER_START in the master process
console.info(flag);
});
testplane.on(testplane.events.RUNNER_END, () => {
// true will be output
console.info(flag);
});
};
But you can solve the issue like this:
module.exports = (testplane, opts) => {
testplane.on(testplane.events.RUNNER_START, () => {
opts.flag = true;
});
testplane.on(testplane.events.NEW_BROWSER, () => {
// true will be output because the properties in the config,
// which have a primitive type (and the "opts" variable is part of the config),
// are automatically passed to workers during the RUNNER_START event
console.info(opts.flag);
});
};
Or like this: see example from the description of the NEW_WORKER_PROCESS event.
Notice that upon transferring between master and workers objects go through serialization/deserialization (in particular, objects methods will be lost).
Also
The test runner has a method registerWorkers
, which registers the plugin code for parallel execution in Testplane workers. The method accepts the following parameters:
Parameter | Type | Description |
---|---|---|
workerFilepath |
String | Absolute path to the worker. |
exportedMethods |
String[] | List of exported methods. |
Returns an object containing asynchronous functions with names from the exported methods.
The file with the path workerFilepath
must export an object containing asynchronous functions with names from exportedMethods
.
Plugin code: plugin.js
let workers;
module.exports = (testplane) => {
testplane.on(testplane.events.RUNNER_START, async (runner) => {
const workerFilepath = require.resolve('./worker');
const exportedMethods = ['foo'];
workers = runner.registerWorkers(workerFilepath, exportedMethods);
// outputs FOO_RUNNER_START
console.info(await workers.foo('RUNNER_START'));
});
testplane.on(testplane.events.RUNNER_END, async () => {
// outputs FOO_RUNNER_END
console.info(await workers.foo('RUNNER_END'));
});
};
Worker code: worker.js
module.exports = {
foo: async function(event) {
return 'FOO_' + event;
}
};