Skip to content

Commit

Permalink
Simplified bootstrap (#2385)
Browse files Browse the repository at this point in the history
* updated missing dependency

* bootstrap simplified

* fixed tests

* added documentation

* updated api documentation

* updated tests

* codestyle improv

* Apply suggestions from code review

Co-authored-by: Paul <pbeigang@gmail.com>

Co-authored-by: Mykhailo Bodnarchuk <mykhailo.bodnarchuk@Mykhailos-MacBook-Pro.local>
Co-authored-by: Paul <pbeigang@gmail.com>
  • Loading branch information
3 people committed May 14, 2020
1 parent 5f3572e commit 03c5c91
Show file tree
Hide file tree
Showing 34 changed files with 710 additions and 608 deletions.
38 changes: 0 additions & 38 deletions bin/codecept.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,44 +119,6 @@ program.command('run [test]')
.option('--child <string>', 'option for child processes')

.action(require('../lib/command/run'));

program.command('run-rerun [test]')
.description('Executes tests in more than one test suite run')

// codecept-only options
.option('--steps', 'show step-by-step execution')
.option('--debug', 'output additional information')
.option('--verbose', 'output internal logging information')
.option('-o, --override [value]', 'override current config options')
.option('--profile [value]', 'configuration profile to be used')
.option('-c, --config [file]', 'configuration file to be used')
.option('--features', 'run only *.feature files and skip tests')
.option('--tests', 'run only JS test files and skip features')
.option('-p, --plugins <k=v,k2=v2,...>', 'enable plugins, comma-separated')

// mocha options
.option('--colors', 'force enabling of colors')
.option('--no-colors', 'force disabling of colors')
.option('-G, --growl', 'enable growl notification support')
.option('-O, --reporter-options <k=v,k2=v2,...>', 'reporter-specific options')
.option('-R, --reporter <name>', 'specify the reporter to use')
.option('-S, --sort', 'sort test files')
.option('-b, --bail', 'bail after first test failure')
.option('-d, --debug', "enable node's debugger, synonym for node --debug")
.option('-g, --grep <pattern>', 'only run tests matching <pattern>')
.option('-f, --fgrep <string>', 'only run tests containing <string>')
.option('-i, --invert', 'inverts --grep and --fgrep matches')
.option('--full-trace', 'display the full stack trace')
.option('--compilers <ext>:<module>,...', 'use the given module(s) to compile files')
.option('--debug-brk', "enable node's debugger breaking on the first line")
.option('--inline-diffs', 'display actual/expected differences inline within each string')
.option('--no-exit', 'require a clean shutdown of the event loop: mocha will not call process.exit')
.option('--recursive', 'include sub directories')
.option('--trace', 'trace function calls')
.option('--child <string>', 'option for child processes')

.action(require('../lib/command/run-rerun'));

program.command('run-workers <workers>')
.description('Executes tests in workers')
.option('-c, --config [file]', 'configuration file to be used')
Expand Down
265 changes: 265 additions & 0 deletions docs/api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,265 @@
---
permalink: /internal-api
title: Internal API
---

## Concepts

In this guide we will overview the internal API of CodeceptJS.
This knowledge is required for customization, writing plugins, etc.

CodeceptJS provides an API which can be loaded via `require('codeceptjs')` when CodeceptJS is installed locally. Otherwise, you can load codeceptjs API via global `codeceptjs` object:

```js
// via module
const { recorder, event, output } = require('codeceptjs');
// or using global object
const { recorder, event, output } = codeceptjs;
```

These internal objects are available:

* [`codecept`](https://github.com/Codeception/CodeceptJS/blob/master/lib/codecept.js): test runner class
* [`config`](https://github.com/Codeception/CodeceptJS/blob/master/lib/config.js): current codecept config
* [`event`](https://github.com/Codeception/CodeceptJS/blob/master/lib/event.js): event listener
* [`recorder`](https://github.com/Codeception/CodeceptJS/blob/master/lib/recorder.js): global promise chain
* [`output`](https://github.com/Codeception/CodeceptJS/blob/master/lib/output.js): internal printer
* [`container`](https://github.com/Codeception/CodeceptJS/blob/master/lib/container.js): dependency injection container for tests, includes current helpers and support objects
* [`helper`](https://github.com/Codeception/CodeceptJS/blob/master/lib/helper.js): basic helper class
* [`actor`](https://github.com/Codeception/CodeceptJS/blob/master/lib/actor.js): basic actor (I) class

[API reference](https://github.com/Codeception/CodeceptJS/tree/master/docs/api) is available on GitHub.
Also please check the source code of corresponding modules.

### Container

CodeceptJS has a dependency injection container with helpers and support objects.
They can be retrieved from the container:

```js
const { container } = require('codeceptjs');

// get object with all helpers
const helpers = container.helpers();

// get helper by name
const { WebDriver } = container.helpers();

// get support objects
const supportObjects = container.support();

// get support object by name
const { UserPage } = container.support();

// get all registered plugins
const plugins = container.plugins();
```

New objects can also be added to container in runtime:

```js
const { container } = require('codeceptjs');

container.append({
helpers: { // add helper
MyHelper: new MyHelper({ config1: 'val1' });
},
support: { // add page object
UserPage: require('./pages/user');
}
})
```

> Use this trick to define custom objects inside `boostrap` script
The container also contains the current Mocha instance:

```js
const mocha = container.mocha();
```

### Event Listeners

CodeceptJS provides a module with an [event dispatcher and set of predefined events](https://github.com/Codeception/CodeceptJS/blob/master/lib/event.js).

It can be required from codeceptjs package if it is installed locally.

```js
const { event } = require('codeceptjs');

module.exports = function() {

event.dispatcher.on(event.test.before, function (test) {

console.log('--- I am before test --');

});
}
```

Available events:

* `event.test.before(test)` - *async* when `Before` hooks 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.
* `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 like `I.say`.
* `event.all.before` - before running tests
* `event.all.after` - after running tests
* `event.all.result` - when results are printed
* `event.workers.before` - before spawning workers in parallel run
* `event.workers.after` - after workers finished in parallel run


> *sync* - means that event is fired in the moment of the action happening.
*async* - means that event is fired when an action is scheduled. Use `recorder` to schedule your actions.

For further reference look for [currently available listeners](https://github.com/Codeception/CodeceptJS/tree/master/lib/listener) using the event system.


### 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](https://github.com/Codeception/CodeceptJS/blob/master/lib/recorder.js) represents a global promises chain.

Provide a function in the first parameter, a function must be async or must return a promise:

```js
const { event, recorder } = require('codeceptjs');

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();
}
});
}
});
}
```
### Config
CodeceptJS config can be accessed from `require('codeceptjs').config.get()`:
```js
const { config } = require('codeceptjs');

// config object has access to all values of the current config file

if (config.get().myKey == 'value') {
// run something
}
```
### Output
Output module provides four verbosity levels. Depending on the mode you can have different information printed using corresponding functions.
* `default`: prints basic information using `output.print`
* `steps`: toggled by `--steps` option, prints step execution
* `debug`: toggled by `--debug` option, prints steps, and debug information with `output.debug`
* `verbose`: toggled by `--verbose` prints debug information and internal logs with `output.log`
It is recommended to avoid `console.log` and use output.* methods for printing.
```js
const output = require('codeceptjs').output;

output.print('This is basic information');
output.debug('This is debug information');
output.log('This is verbose logging information');
```
#### Test Object
The test events are providing a test object with following properties:
* `title` title of the test
* `body` test function as a string
* `opts` additional test options like retries, and others
* `pending` true if test is scheduled for execution and false if a test has finished
* `tags` array of tags for this test
* `artifacts` list of files attached to this test. Screenshots, videos and other files can be saved here and shared accross different reporters
* `file` path to a file with a test
* `steps` array of executed steps (available only in `test.passed`, `test.failed`, `test.finished` event)
* `skipInfo` additional test options when test skipped
* * `message` string with reason for skip
* * `description` string with test body
and others
#### Step Object
Step events provide step objects with following fields:
* `name` name of a step, like 'see', 'click', and others
* `actor` current actor, in most cases it is `I`
* `helper` current helper instance used to execute this step
* `helperMethod` corresponding helper method, in most cases is the same as `name`
* `status` status of a step (passed or failed)
* `prefix` if a step is executed inside `within` block contain within text, like: 'Within .js-signup-form'.
* `args` passed arguments
Whenever you execute tests with `--verbose` option you will see registered events and promises executed by a recorder.
## Custom Runner
You can run CodeceptJS tests from your script.
```js
const { codecept: Codecept } = require('codeceptjs');

// define main config
const config = {
helpers: {
WebDriver: {
browser: 'chrome',
url: 'http://localhost'
}
}
};

const opts = { steps: true };

// run CodeceptJS inside async function
(async () => {
const codecept = new Codecept(config, options);
codecept.init(__dirname);

try {
await codecept.bootstrap();
codecept.loadTests('**_test.js');
// run tests
await codecept.run(test);
} catch (err) {
printError(err);
process.exitCode = 1;
} finally {
await codecept.teardown();
}
})();
```
> Also, you can run tests inside workers in a custom scripts. Please refer to the [parallel execution](/parallel) guide for more details.

0 comments on commit 03c5c91

Please sign in to comment.