Repository: Codeception/CodeceptJS
Pull request #1790 (2019-07-19 21:15:14)

Fix the --path option on gherkin:snippets
PR https://github.com/Codeception/CodeceptJS/pull/1688 added support to direct new snippets to a specific file, but forgot to add the `--path` option to the command line parser.  Without this fix, `codeceptjs gherkin:snippets --path=foo` results in an error "error: unknown option `--path'"


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 917becfc..4e9f2c60 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,7 +1,7 @@
 ## 2.2.0
 
 * **EXPERIMENTAL** [**TestCafe** helper](https://codecept.io/helpers/TestCafe) introduced. TestCafe allows to run cross-browser tests it its own very fast engine. Supports all browsers including mobile. Thanks to @hubidu for implementation! Please test it and send us feedback.
-* [Puppeteer] Mocking requests enabled by introducing [Polly.js helper](https://codecept.io/helpers/Polly)
+* [Puppeteer] Mocking requests enabled by introducing [Polly.js helper](https://codecept.io/helpers/Polly). Thanks @VikalpP
 
 ```js
 // use Polly & Puppeteer helpers
diff --git a/README.md b/README.md
index e1ddf8ac..eef5ef2a 100644
--- a/README.md
+++ b/README.md
@@ -18,19 +18,20 @@ Scenario('check Welcome page on site', (I) => {
 });
 ```
 
-Codeception tests are:
+CodeceptJS tests are:
 
 * **Synchronous**. You don't need to care about callbacks, or promises, test scenarios are linear, your test should be too.
 * Written from **user's perspective**. Every action is a method of `I`. That makes test easy to read, write and maintain even for non-tech persons.
 * Backend **API agnostic**. We don't know which WebDriver implementation is running this test. We can easily switch from WebDriverIO to Protractor or PhantomJS.
 
-Codeception uses **Helper** modules to provide actions to `I` object. Currently CodeceptJS has these helpers:
+CodeceptJS uses **Helper** modules to provide actions to `I` object. Currently CodeceptJS has these helpers:
 
-* [**WebDriverIO**](https://github.com/Codeception/CodeceptJS/blob/master/docs/helpers/WebDriverIO.md) - wrapper on top of Selenium bindings library [WebDriverIO](http://webdriver.io/)
-* [**Protractor**](https://github.com/Codeception/CodeceptJS/blob/master/docs/helpers/Protractor.md) - helper empowered by [Protractor](http://protractortest.org/) framework for AngularJS testing
-* [**Nightmare**](https://github.com/Codeception/CodeceptJS/blob/master/docs/helpers/Nightmare.md) - helper which for testing web applications indi Electron  using NightmareJS.
-* [**Appium**](https://github.com/Codeception/CodeceptJS/blob/master/docs/helpers/Appium.md) - for **mobile testing** with Appium
 * [**Puppeteer**](https://github.com/Codeception/CodeceptJS/blob/master/docs/helpers/Puppeteer.md) - uses Google Chrome's Puppeteer for fast headless testing.
+* [**WebDriver**](https://github.com/Codeception/CodeceptJS/blob/master/docs/helpers/WebDriver.md) - uses [webdriverio](http://webdriver.io/) to run tests via WebDriver protocol.
+* [**Protractor**](https://github.com/Codeception/CodeceptJS/blob/master/docs/helpers/Protractor.md) - helper empowered by [Protractor](http://protractortest.org/) to run tests via WebDriver protocol.
+* [**TestCafe**](https://github.com/Codeception/CodeceptJS/blob/master/docs/helpers/TestCafe.md) - cheap and fast cross-browser test automation.
+* [**Nightmare**](https://github.com/Codeception/CodeceptJS/blob/master/docs/helpers/Nightmare.md) - uses Electron and NightmareJS to run tests.
+* [**Appium**](https://github.com/Codeception/CodeceptJS/blob/master/docs/helpers/Appium.md) - for **mobile testing** with Appium
 
 And more to come...
 
@@ -38,7 +39,7 @@ And more to come...
 
 CodeceptJS is a successor of [Codeception](http://codeception.com), a popular full-stack testing framework for PHP.
 With CodeceptJS your scenario-driven functional and acceptance tests will be as simple and clean as they can be.
-You don't need to worry about asynchronous nature of NodeJS or about various APIs of Selenium, PhantomJS, Protractor, etc,
+You don't need to worry about asynchronous nature of NodeJS or about various APIs of Selenium, Puppeteer, Protractor, TestCafe etc,
 as CodeceptJS unifies them and makes them work as they were synchronous.
 
 ## Features
@@ -47,7 +48,6 @@ as CodeceptJS unifies them and makes them work as they were synchronous.
 * Designed for scenario driven acceptance testing in BDD-style
 * Uses ES6 natively without transpiler.
 * Also plays nice with TypeScript.
-* Selenium WebDriver integration using [webdriverio](http://webdriver.io).
 * Smart locators: use names, labels, matching text, CSS or XPath to locate elements.
 * Interactive debugging shell: pause test at any point and try different commands in a browser.
 * Easily create tests, pageobjects, stepobjects with CLI generators.
@@ -55,13 +55,13 @@ as CodeceptJS unifies them and makes them work as they were synchronous.
 ## Install
 
 ```sh
-npm install -g codeceptjs
+npm install codeceptjs --save
 ```
 
 Move to directory where you'd like to have your tests (and codeceptjs config) stored, and run
 
 ```sh
-codeceptjs init
+npx codeceptjs init
 ```
 
 to create and configure test environment. It is recommended to select WebDriverIO from the list of helpers,
@@ -70,19 +70,19 @@ if you need to write Selenium WebDriver tests.
 After that create your first test by executing:
 
 ```sh
-codeceptjs generate:test
+npx codeceptjs generate:test
 ```
 
 Now test is created and can be executed with
 
 ```sh
-codeceptjs run
+npx codeceptjs run
 ```
 
 If you want to write your tests using TypeScript just generate standard Type Definitions by executing:
 
 ```sh
-codeceptjs def .
+npx codeceptjs def .
 ```
 
 Later you can even automagically update Type Definitions to include your own custom [helpers methods](docs/helpers.md).
@@ -113,7 +113,7 @@ Scenario('test some forms', (I) => {
 ```
 
 All actions are performed by I object; assertions functions start with `see` function.
-In this examples all methods of `I` are taken from WebDriverIO helper, see [reference](https://github.com/Codeception/CodeceptJS/blob/master/docs/helpers/WebDriverIO.md) to learn how to use them.
+In this examples all methods of `I` are taken from WebDriver helper, see [reference](https://github.com/Codeception/CodeceptJS/blob/master/docs/helpers/WebDriver.md) to learn how to use them.
 
 Let's execute this test with `run` command. Additional option `--steps` will show us the running process. We recommend use `--steps` or `--debug` during development.
 
@@ -230,16 +230,9 @@ It will create a page object file for you and add it to config.
 Let's assume we created one named `docsPage`:
 
 ```js
-'use strict';
-
-let I;
+const { I } = inject();
 
 module.exports = {
-
-  _init() {
-    I = actor();
-  },
-
   fields: {
     email: '#user_basic_email',
     password: '#user_basic_password'
diff --git a/RoboFile.php b/RoboFile.php
index 8160e07a..5f0df1e3 100644
--- a/RoboFile.php
+++ b/RoboFile.php
@@ -71,6 +71,25 @@ class RoboFile extends \Robo\Tasks
                 ->textFromFile("docs/helpers/{$info['filename']}.md")
                 ->run();
         }
+
+        $this->docsExternalHelpers();
+    }
+
+
+    function docsExternalHelpers()
+    {
+
+        $this->say('Building @codecepjs/detox helper docs');
+        $helper = 'Detox';
+        $this->_exec("npx documentation build node_modules/@codeceptjs/detox-helper/$helper.js -o docs/helpers/$helper.md -f md --shallow --markdown-toc=false --sort-order=alpha ");
+        $this->taskWriteToFile("docs/helpers/$helper.md")
+            ->line('---')
+            ->line("id: $helper")
+            ->line("title: $helper")
+            ->line('---')
+            ->line('')
+            ->textFromFile("docs/helpers/$helper.md")
+            ->run();
     }
 
     function publishSite()
@@ -159,7 +178,7 @@ class RoboFile extends \Robo\Tasks
         //issue
         $changelog = preg_replace(
             '~#(\d+)~',
-            '[#$1](https://github.com/Codeception/Codeception/issues/$1)',
+            '[#$1](https://github.com/Codeception/CodeceptJS/issues/$1)',
             $changelog
         );
 
@@ -203,4 +222,4 @@ class RoboFile extends \Robo\Tasks
         $this->_exec('npm publish');
         $this->yell('It is released!');
     }
-}
\ No newline at end of file
+}
diff --git a/bin/codecept.js b/bin/codecept.js
index 63df5619..e9878354 100755
--- a/bin/codecept.js
+++ b/bin/codecept.js
@@ -56,6 +56,7 @@ program.command('gherkin:snippets [path]')
   .description('Generate step defintions from steps.')
   .option('--dry-run', "don't save snippets to file")
   .option('-c, --config [file]', 'configuration file to be used')
+  .option('--path [file]', 'file in which to place the new snippets')
   .action(require('../lib/command/gherkin/snippets'));
 
 
diff --git a/docs/advanced.md b/docs/advanced.md
index c89d6500..9ffc9943 100644
--- a/docs/advanced.md
+++ b/docs/advanced.md
@@ -367,7 +367,7 @@ Scenario('should create data via v2 version of API', (I) => {
 ```
 
 Config can also be set by a function, in this case you can get a test object and specify config values based on it.
-This is very useful when running tests against cloud providers, like BrowserStack.
+This is very useful when running tests against cloud providers, like BrowserStack. This function can also be asynchronous.
 
 ```js
 Scenario('should report to BrowserStack', (I) => {
diff --git a/docs/angular.md b/docs/angular.md
index 05f77f2b..f5620e88 100644
--- a/docs/angular.md
+++ b/docs/angular.md
@@ -150,12 +150,12 @@ exports.config = {
 Sure, Protractor can be used to test applications built without AngularJS. In this case you need to disable angular synchronization feature in config:
 
 ```js
-"helpers": {
-  "Protractor": {
-    "url": "http://todomvc.com/examples/angularjs/",
-    "driver": "hosted",
-    "browser": "firefox",
-    "angular": false
+helpers: {
+  Protractor: {
+    url: "http://todomvc.com/examples/angularjs/",
+    driver: "hosted",
+    browser: "firefox",
+    angular: false
   }
 }
 ```
@@ -304,9 +304,6 @@ Scenario('create todo item', (I) => {
 
 To learn more about refactoring options in CodeceptJS read [PageObjects guide](http://codecept.io/pageobjects/).
 
-## Multiple Sessions
-
-To run several browsers in one test see [multiple session](https://codecept.io/acceptance/#multiple-sessions) chapter.
 
 ## Extending
 
diff --git a/docs/basics.md b/docs/basics.md
index c4d9cebe..e234d0f8 100644
--- a/docs/basics.md
+++ b/docs/basics.md
@@ -30,20 +30,23 @@ Here is the diagram of CodeceptJS architecture
 All helpers share the same API so it's easy to migrate tests from one backend to other.
 However, because of difference in backends and their limitations, they are not guaranteed to be compatible with each other. For instance, you can't set request headers in WebDriver or Protractor, but you can do so in Puppteer or Nightmare.
 
-Please note, you can't run tests by different helpers at once. You can't use some APIs from WebDriver and some from Nightmare. You should **pick one helper, as it defines how tests are executed.** If requirements change it's easy to migrate to another, but don't use few helpers at once. It's just not possible.
+**Pick one helper, as it defines how tests are executed.** If requirements change it's easy to migrate to another, but don't use few helpers of same kind at once.
 
-A helper should be enabled in main config. Configuration (like base url) should be provided as well:
+---
 
-```json
-  "helpers": {
-    "WebDriver": {
-      "url": "http://localhost",
-      "browser": "chrome"
-    }
-  }
-```
+Refer to following guides to more information on:
+
+* [▶ WebDriver](https://codecept.io/webdriver)
+* [▶ Protractor](https://codecept.io/angular)
+* [▶ Puppeteer](https://codecept.io/puppeteer)
+* [▶ Nightmare](https://codecept.io/nightmare)
+* TestCafe
+
+> ℹ Depending on a helper selected a list of available actions may change.
+
+To list all available commands for current configuration run `codeceptjs list`
+or enable [auto-completion by generating TypeScript definitions](#intellisense).
 
-In this config config all methods of `I` will be taken from `WebDriver` helper.
 
 ## Writing Tests
 
@@ -56,11 +59,221 @@ I.see('Please Login', 'h1');
 // ...
 ```
 
-To list all available commands for current configuration run `codeceptjs list`
-or enable [auto-completion by generating TypeScript definitions](#intellisense).
+### Opening a Page
+
+A test should usually start with navigating browser to the website.
+
+Start a test by opening a page. Use `I.amOnPage()` command for this:
+
+```js
+// When "http://site.com" is url in config
+I.amOnPage('/'); // -> opens http://site.com/
+I.amOnPage('/about'); // -> opens http://site.com/about
+I.amOnPage('https://google.com'); // -> https://google.com
+```
+
+When URL doesn't start with a protocol (http:// or https://) it is considered to be a relative URL and appended to URL which was initially set in the config.
+
+> It is recommended to use relative URLs and keep base URL in config file, so you could easily switch between development, staging, and production environments.
+
+
+### Locating Element
+
+Element can be found by CSS or XPath locators.
+
+```js
+I.seeElement('.user'); // element with CSS class user
+I.seeElement('//button[contains(., "press me")]'); // button
+```
+
+By default CodeceptJS tries to guess the locator type.
+In order to specify exact locator type you can pass an object called **strict locator**.
+
+```js
+I.seeElement({css: 'div.user'});
+I.seeElement({xpath: '//div[@class=user]'});
+```
+
+Strict locators allow to specify additional locator types:
+
+```js
+// locate form element by name
+I.seeElement({name: 'password'});
+// locate element by id
+I.seeElement({id: 'users'});
+// locate element by React component and props
+I.seeElement({react: 'user-profile', props: {name: 'davert'}});
+```
+
+In [mobile testing](http://codecept.io/mobile/#locating-elements) you can use `~` to specify accessibility id to locate an element. In web application you can locate element by their `aria-label` value.
+
+```js
+// locate element by [aria-label] attribute in web
+// or by accessibility id in mobile
+I.seeElement('~username');
+```
+
+> [▶ Learn more about using locators in CodeceptJS](https://codecept.io/locators).
+
+### Clicking
+
+CodeceptJS provides a flexible syntax to specify an element to click.
+
+By default CodeceptJS tries to find button or link with exact text on it
+
+```js
+// search for link or button
+I.click('Login');
+```
+
+If none found, CodeceptJS tries to find link or button containing that text. In case an image is clickable its `alt` attribute will be checked for text inclusion. Form buttons will also be searched by name.
+
+To narrow down the results you can specify a context in second parameter.
+
+```js
+I.click('Login', '.nav'); // search only in .nav
+I.click('Login', {css: 'footer'}); // search only in footer
+```
+
+> To skip guessing locator type pass in a strict locator. A locator starting with '#' or '.' is considered to be CSS. Locator starting with '//' or './/' is considered to be XPath.
+
+You are not limited to buttons and links. Any element can be found by passing in valid CSS or XPath:
+
+```js
+// click element by CSS
+I.click('#signup');
+// click element located by special test-id attribute
+I.click('//dev[@test-id="myid"]');
+```
+
+### Filling Fields
+
+Clicking the links is not what takes the most time during testing a web site. If your site consists only of links you can skip test automation. The most routine waste of time goes into the testing of forms. CodeceptJS provides several ways of doing that.
+
+Let's submit this sample form for a test:
+
+```html
+<form method="post" action="/update" id="update_form">
+     <label for="user_name">Name</label>
+     <input type="text" name="user[name]" id="user_name" />
+     <label for="user_email">Email</label>
+     <input type="text" name="user[email]" id="user_email" />
+     <label for="user_gender">Gender</label>
+     <select id="user_gender" name="user[gender]">
+          <option value="m">Male</option>
+          <option value="f">Female</option>
+     </select>
+     <input type="submit" name="submitButton" value="Update" />
+</form>
+```
+
+We need to fill in all those fields and click "Update" button. CodeceptJS matches form elements by their label, name, or by CSS or XPath locators.
+
+```js
+// we are using label to match user_name field
+I.fillField('Name', 'Miles');
+// we can use input name
+I.fillField('user[email]','miles@davis.com');
+// select element by label, choose option by text
+I.selectOption('Gender','Male');
+// click 'Update' button, found by text
+I.click('Update');
+```
+
+Alternative scenario:
+
+```js
+// we are using CSS
+I.fillField('#user_name', 'Miles');
+I.fillField('#user_email','miles@davis.com');
+// select element by label, option by value
+I.selectOption('#user_gender','m');
+// click 'Update' button, found by name
+I.click('submitButton', '#update_form');
+```
+
+To fill in sensitive data use `secret` function:
+
+```js
+I.fillField('password', secret('123456'));
+```
 
-> For most helpers basic actions like `amOnPage`, `fillField`, `click` are the same.
-Proceed to [Acceptance Testing Chapter](https://codecept.io/acceptance/) to learn how to use them.
+### Assertions
+
+In order to verify the expected behavior of a web application, a content should be checked.
+CodeceptJS provides built-in assertions for that. They start with `see` (or `dontSee`) prefix.
+
+The most general and common assertion is `see`, which checks visilibility of a text on a page:
+
+```js
+// Just a visible text on a page
+I.see('Hello');
+// text inside .msg element
+I.see('Hello', '.msg');
+// opposite
+I.dontSee('Bye');
+```
+
+You should provide a text as first argument and optionally a locator to search for a text in a context.
+
+You can check that specific element exists (or not) on a page, as it was described in [Locating Element](#locating-element) section.
+
+```js
+I.seeElement('.notice');
+I.dontSeeElement('.error');
+```
+
+Additional assertions:
+
+```js
+I.seeInCurrentUrl('/user/miles');
+I.seeInField('user[name]', 'Miles');
+I.seeInTitle('My Website');
+```
+
+To see all possible assertions see the helper's reference.
+
+### Grabbing
+
+Sometimes you need to retrieve a data from a page to use it in next steps of a scenario.
+Imagine, application generates a password and you want to ensure that user can login using this password.
+
+```js
+Scenario('login with generated password', async (I) => {
+  I.fillField('email', 'miles@davis.com');
+  I.click('Generate Password');
+  const password = await I.grabTextFrom('#password');
+  I.click('Login');
+  I.fillField('email', 'miles@davis.com');
+  I.fillField('password', password);
+  I.click('Log in!');
+  I.see('Hello, Miles');
+});
+```
+
+`grabTextFrom` action is used here to retrieve text from an element. All actions starting with `grab` prefix are expected to return data. In order to synchronize this step with a scenario you should pause test execution with `await` keyword of ES6. To make it work your test should be written inside a async function (notice `async` in its definition).
+
+```js
+Scenario('use page title', async (I) => {
+  // ...
+  const password = await I.grabTextFrom('#password');
+  I.fillField('password', password);
+});
+```
+
+### Waiting
+
+In modern web applications rendering is happen on client side.
+Sometimes that may cause delays. A test may fail while trying to click an element which has not appeared on a page yet.
+To handle this cases `wait*` methods introduced.
+
+```js
+I.waitForElement('#agree_button', 30); // secs
+// clicks a button only when it is visible
+I.click('#agree_button');
+```
+
+> ℹ See [helpers reference](https://codecept.io/reference) for a complete list of all available commands for a helper you use.
 
 ## How It Works
 
@@ -197,6 +410,76 @@ This can be configured in [screenshotOnFail Plugin](https://codecept.io/plugins/
 To see how the test was executed, use [stepByStepReport Plugin](https://codecept.io/plugins/#stepbystepreport). It saves a screenshot of each passed step and shows them in a nice slideshow.
 
 
+## Retries
+
+### 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:
+
+```js
+I.retry().see('Welcome');
+```
+
+If you'd like to retry step more than once pass the amount as parameter:
+
+```js
+I.retry(3).see('Welcome');
+```
+
+Additional options can be provided to retry so you can set the additional options (defined in [promise-retry](https://www.npmjs.com/package/promise-retry) library).
+
+
+```js
+// 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
+I.retry({
+  retries: 2,
+  when: err => err.message === 'Node not visible'
+}).seeElement('#user');
+```
+
+Pass a function to `when` option to retry only when error matches the expected one.
+
+### Auto Retry
+
+You can auto-retry a failed step by enabling [retryFailedStep Plugin](https://codecept.io/plugins/#retryfailedstep).
+
+### 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](https://mochajs.org#retry-tests);
+You can set number of a retries for a feature:
+
+```js
+Scenario('Really complex', (I) => {
+  // test goes here
+}).retry(2);
+
+// 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:
+
+```js
+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.
+
+
 ## Before
 
 Common preparation steps like opening a web page, logging in a user, can be placed in `Before` or `Background` hook:
@@ -258,7 +541,23 @@ within('.js-signup-form', () => {
 I.see('There were problems creating your account.');
 ```
 
-`within` can also work with [iframes](/acceptance/#iframes)
+`within` can also work with IFrames. Special `frame` locator is required to locate the iframe and get into its context.
+
+See example:
+
+```js
+within({frame: "#editor"}, () => {
+  I.see('Page');
+});
+```
+
+Nested IFrames can be set by passing array *(WebDriver, Nightmare & Puppeteer only)*:
+
+```js
+within({frame: [".content", "#editor"]}, () => {
+  I.see('Page');
+});
+```
 
 When running steps inside a within block will be shown with a shift:
 
@@ -293,102 +592,105 @@ I.say('This is blue', 'blue'); //blue is used
 I.say('This is by default'); //cyan is used
 ```
 
-## IntelliSense
+## Multiple Sessions
 
-If you are using Visual Studio Code or other IDE that supports TypeScript Definitions,
-you can generate step definitions with
-
-```sh
-codeceptjs def
-```
+CodeceptJS allows to run several browser sessions inside a test. This can be useful for testing communication between users inside a system, for instance in chats. To open another browser use `session()` function as shown in example:
 
-Now you should create `tsconfig.json` in your project root directory.
-
-```tsconfig.json
-{
-  "compilerOptions": {
-    "allowJs": true,
-  }
-}
+```js
+Scenario('test app', (I) => {
+  I.amOnPage('/chat');
+  I.fillField('name', 'davert');
+  I.click('Sign In');
+  I.see('Hello, davert');
+  session('john', () => {
+    // another session started
+    I.amOnPage('/chat');
+    I.fillField('name', 'john');
+    I.click('Sign In');
+    I.see('Hello, john');
+  });
+  // switching back to default session
+  I.fillField('message', 'Hi, john');
+  // there is a message from current user
+  I.see('me: Hi, john', '.messages');
+  session('john', () => {
+    // let's check if john received it
+    I.see('davert: Hi, john', '.messages');
+  });
+});
 ```
-but in usually case, this file has already generated when you execute `codeceptjs init`.
-
-Alternatively, you can include `/// <reference path="./steps.d.ts" />` into your test files 
-to get method autocompletion while writing tests.
-
-## Skipping
-
-Like in Mocha you can use `x` and `only` to skip tests or making a single test to run.
-
-* `xScenario` - skips current test
-* `Scenario.only` - executes only the current test
-
 
-## Retries
-
-### Retry Step
+`session` function expects a first parameter to be a name of a session. You can switch back to session by using the same name.
 
-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:
+You can override config for session by passing second parameter:
 
 ```js
-I.retry().see('Welcome');
+session('john', { browser: 'firefox' } , () => {
+  // run this steps in firefox
+  I.amOnPage('/');
+});
 ```
 
-If you'd like to retry step more than once pass the amount as parameter:
+or just start session without switching to it. Call `session` passing only its name:
 
 ```js
-I.retry(3).see('Welcome');
-```
+Scenario('test', (I) => {
+  // opens 3 additional browsers
+  session('john');
+  session('mary');
+  session('jane');
 
-Additional options can be provided to retry so you can set the additional options (defined in [promise-retry](https://www.npmjs.com/package/promise-retry) library).
+  I.amOnPage('/');
 
+  // switch to session by its name
+  session('mary', () => {
+    I.amOnPage('/login');
+  });
+}
+```
+`session` can return value which can be used in scenario:
 
 ```js
-// 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
-I.retry({
-  retries: 2,
-  when: err => err.message === 'Node not visible'
-}).seeElement('#user');
+// inside async function
+const val = await session('john', () => {
+  I.amOnPage('/info');
+  return I.grabTextFrom({ css: 'h1' });
+});
+I.fillField('Description', val);
 ```
 
-Pass a function to `when` option to retry only when error matches the expected one.
+Function passed into session can use `I`, page objects, and any objects declared for the scenario.
+This function can also be declared as async (but doesn't work as generator).
 
-### Auto Retry
+Also, you can use `within` inside a session but you can't call session from inside `within`.
 
-You can auto-retry a failed step by enabling [retryFailedStep Plugin](https://codecept.io/plugins/#retryfailedstep).
 
-### Retry Scenario
+## IntelliSense
 
-When you need to rerun scenarios few times just add `retries` option added to `Scenario` declaration.
+If you are using Visual Studio Code or other IDE that supports TypeScript Definitions,
+you can generate step definitions with
 
-CodeceptJS implements retries the same way [Mocha do](https://mochajs.org#retry-tests);
-You can set number of a retries for a feature:
+```sh
+codeceptjs def
+```
 
-```js
-Scenario('Really complex', (I) => {
-  // test goes here
-}).retry(2);
+Now you should create `jsconfig.json` in your project root directory.
 
-// alternative
-Scenario('Really complex', { retries: 2 }, (I) => {});
+```jsconfig.json
+{
+  "compilerOptions": {
+    "allowJs": true,
+  }
+}
 ```
+but in usually case, this file has already generated when you execute `codeceptjs init`.
 
-This scenario will be restarted two times on a failure.
-
-### Retry Feature
+Alternatively, you can include `/// <reference path="./steps.d.ts" />` into your test files
+to get method autocompletion while writing tests.
 
-To set this option for all scenarios in a file, add retry to a feature:
+## Skipping
 
-```js
-Feature('Complex JS Stuff').retry(3);
-```
+Like in Mocha you can use `x` and `only` to skip tests or making a single test to run.
 
-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.
+* `xScenario` - skips current test
+* `Scenario.only` - executes only the current test
diff --git a/docs/changelog.md b/docs/changelog.md
index b50e67c6..c1c16089 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -6,7 +6,7 @@ title: Releases
 ## 2.2.0
 
 * **EXPERIMENTAL** [**TestCafe** helper](https://codecept.io/helpers/TestCafe) introduced. TestCafe allows to run cross-browser tests it its own very fast engine. Supports all browsers including mobile. Thanks to **[hubidu](https://github.com/hubidu)** for implementation! Please test it and send us feedback.
-* **[Puppeteer]** Mocking requests enabled by introducing [Polly.js helper](https://codecept.io/helpers/Polly)
+* **[Puppeteer]** Mocking requests enabled by introducing [Polly.js helper](https://codecept.io/helpers/Polly). Thanks **[VikalpP](https://github.com/VikalpP)**
 
 ```js
 // use Polly & Puppeteer helpers
@@ -14,24 +14,24 @@ I.mockRequest('GET', '/api/users', 200);
 I.mockRequest('POST', '/users', { user: { name: 'fake' }});
 ```
 
-* **EXPERIMENTAL** **[Puppeteer]** [Firefox support](https://codecept.io/helpers/Puppeteer-firefox) introduced by **[ngadiyak](https://github.com/ngadiyak)**, see [#1740](https://github.com/Codeception/Codeception/issues/1740)
-* **[stepByStepReportPlugin]** use md5 hash to generate reports into unique folder. Fix [#1744](https://github.com/Codeception/Codeception/issues/1744) by **[chimurai](https://github.com/chimurai)**
+* **EXPERIMENTAL** **[Puppeteer]** [Firefox support](https://codecept.io/helpers/Puppeteer-firefox) introduced by **[ngadiyak](https://github.com/ngadiyak)**, see [#1740](https://github.com/Codeception/CodeceptJS/issues/1740)
+* **[stepByStepReportPlugin]** use md5 hash to generate reports into unique folder. Fix [#1744](https://github.com/Codeception/CodeceptJS/issues/1744) by **[chimurai](https://github.com/chimurai)**
 * Interactive pause improvements:
   * print result of `grab` commands
   * print message for successful assertions
 * `run-multiple` (parallel execution) improvements:
-  * `bootstrapAll` must be called before creating chunks. [#1741](https://github.com/Codeception/Codeception/issues/1741) by **[Vorobeyko](https://github.com/Vorobeyko)**
-  * Bugfix: If value in config has falsy value then multiple config does not overwrite original value. [#1756](https://github.com/Codeception/Codeception/issues/1756) by **[LukoyanovE](https://github.com/LukoyanovE)**
+  * `bootstrapAll` must be called before creating chunks. [#1741](https://github.com/Codeception/CodeceptJS/issues/1741) by **[Vorobeyko](https://github.com/Vorobeyko)**
+  * Bugfix: If value in config has falsy value then multiple config does not overwrite original value. [#1756](https://github.com/Codeception/CodeceptJS/issues/1756) by **[LukoyanovE](https://github.com/LukoyanovE)**
 * Fixed hooks broken in 2.1.5 by **[Vorobeyko](https://github.com/Vorobeyko)**
-* Fix references to support objects when using Dependency Injection. Fix by **[johnyb](https://github.com/johnyb)**. See [#1701](https://github.com/Codeception/Codeception/issues/1701)
-* Fix dynamic config applied for multiple helpers by **[VikalpP](https://github.com/VikalpP)** [#1743](https://github.com/Codeception/Codeception/issues/1743)
+* Fix references to support objects when using Dependency Injection. Fix by **[johnyb](https://github.com/johnyb)**. See [#1701](https://github.com/Codeception/CodeceptJS/issues/1701)
+* Fix dynamic config applied for multiple helpers by **[VikalpP](https://github.com/VikalpP)** [#1743](https://github.com/Codeception/CodeceptJS/issues/1743)
 
 
 ## 2.1.5
 
 * **EXPERIMENTAL** [Wix Detox support](https://github.com/Codeception/detox-helper) introduced as standalone helper. Provides a faster alternative to Appium for mobile testing.
 * Saving successful commands inside interactive pause into `_output/cli-history` file. By **[hubidu](https://github.com/hubidu)**
-* Fixed hanging error handler inside scenario. See [#1721](https://github.com/Codeception/Codeception/issues/1721) by **[haily-lgc](https://github.com/haily-lgc)**.
+* Fixed hanging error handler inside scenario. See [#1721](https://github.com/Codeception/CodeceptJS/issues/1721) by **[haily-lgc](https://github.com/haily-lgc)**.
 * Fixed by **[Vorobeyko](https://github.com/Vorobeyko)**: tests did not fail when an exception was raised in async bootstrap.
 * **[WebDriver]** Added window control methods by **[emmonspired](https://github.com/emmonspired)**
   * `grabAllWindowHandles` returns all window handles
@@ -193,7 +193,7 @@ Use it with `FileSystem` helper to test availability of a file:
 * [allure plugin] Add skipped tests to allure reports by **[Vorobeyko](https://github.com/Vorobeyko)**
 * Fixed `Logged Test name | [object Object]` when used Data().Scenario(). By **[Vorobeyko](https://github.com/Vorobeyko)**
 * Fixed Data().only.Scenario() to run for all datasets. By **[Vorobeyko](https://github.com/Vorobeyko)**
-* **[WebDriver]** `attachFile` to work with hidden elements. Fixed in [#1460](https://github.com/Codeception/Codeception/issues/1460) by **[tsuemura](https://github.com/tsuemura)**
+* **[WebDriver]** `attachFile` to work with hidden elements. Fixed in [#1460](https://github.com/Codeception/CodeceptJS/issues/1460) by **[tsuemura](https://github.com/tsuemura)**
 
 
 
@@ -262,7 +262,7 @@ Use it with `FileSystem` helper to test availability of a file:
 * **Using `codecept.conf.js` as default configuration format**
 * Fixed "enametoolong" error when saving screenshots for data driven tests by **[PeterNgTr](https://github.com/PeterNgTr)**
 * Updated NodeJS to 10 in Docker image
-* **[Pupeteer]** Add support to use WSEndpoint. Allows to execute tests remotely. [See [#1350](https://github.com/Codeception/Codeception/issues/1350)] by **[gabrielcaires](https://github.com/gabrielcaires)** (https://github.com/Codeception/CodeceptJS/pull/1350)
+* **[Pupeteer]** Add support to use WSEndpoint. Allows to execute tests remotely. [See [#1350](https://github.com/Codeception/CodeceptJS/issues/1350)] by **[gabrielcaires](https://github.com/gabrielcaires)** (https://github.com/Codeception/CodeceptJS/pull/1350)
 * In interactive shell **[Enter]** goes to next step. Improvement by **[PeterNgTr](https://github.com/PeterNgTr)**.
 * `I.say` accepts second parameter as color to print colorful comments. Improvement by **[PeterNgTr](https://github.com/PeterNgTr)**.
 
@@ -278,12 +278,12 @@ I.say('This is by default'); //cyan is used
 
 * **[Puppeteer]** `dragSlider` action added by **[PeterNgTr](https://github.com/PeterNgTr)**
 * **[Puppeteer]** Fixed opening browser in shell mode by **[allenhwkim](https://github.com/allenhwkim)**
-* **[Puppeteer]** Fixed making screenshot on additional sessions by **[PeterNgTr](https://github.com/PeterNgTr)**. Fixes [#1266](https://github.com/Codeception/Codeception/issues/1266)
+* **[Puppeteer]** Fixed making screenshot on additional sessions by **[PeterNgTr](https://github.com/PeterNgTr)**. Fixes [#1266](https://github.com/Codeception/CodeceptJS/issues/1266)
 * Added `--invert` option to `run-multiple` command by **[LukoyanovE](https://github.com/LukoyanovE)**
 * Fixed steps in Allure reports by **[PeterNgTr](https://github.com/PeterNgTr)**
 * Add option `output` to customize output directory in [stepByStepReport plugin](https://codecept.io/plugins/#stepbystepreport). By **[fpsthirty](https://github.com/fpsthirty)**
 * Changed type definition of PageObjects to get auto completion by **[rhicu](https://github.com/rhicu)**
-* Fixed steps output for async/arrow functions in CLI by **[LukoyanovE](https://github.com/LukoyanovE)**. See [#1329](https://github.com/Codeception/Codeception/issues/1329)
+* Fixed steps output for async/arrow functions in CLI by **[LukoyanovE](https://github.com/LukoyanovE)**. See [#1329](https://github.com/Codeception/CodeceptJS/issues/1329)
 
 ## 1.4.5
 
@@ -296,7 +296,7 @@ I.say('This is by default'); //cyan is used
 ```
 
 * **[WebDriverIO]** Fix timeouts definition to be compatible with W3C drivers. By **[LukoyanovE](https://github.com/LukoyanovE)**
-* Fixed: exception in Before block w/ Mocha causes test not to report failure. See [#1292](https://github.com/Codeception/Codeception/issues/1292) by **[PeterNgTr](https://github.com/PeterNgTr)**
+* Fixed: exception in Before block w/ Mocha causes test not to report failure. See [#1292](https://github.com/Codeception/CodeceptJS/issues/1292) by **[PeterNgTr](https://github.com/PeterNgTr)**
 * Command `run-parallel` now accepts `--override` flag. Thanks to **[ClemCB](https://github.com/ClemCB)**
 * Fixed Allure report with Before/BeforeSuite/After/AfterSuite steps. By **[PeterNgTr](https://github.com/PeterNgTr)**
 * Added `RUN_MULTIPLE` env variable to [Docker config](https://codecept.io/docker/). Allows to run tests in parallel inside a container. Thanks to **[PeterNgTr](https://github.com/PeterNgTr)**
@@ -327,7 +327,7 @@ Scenario('update user profile', () => {
 
 * Fixed attaching Allure screenshot on exception. Fix by **[DevinWatson](https://github.com/DevinWatson)**
 * Improved type definitions for custom steps. By **[Akxe](https://github.com/Akxe)**
-* Fixed setting `multiple.parallel.chunks` as environment variable in config. See [#1238](https://github.com/Codeception/Codeception/issues/1238) by **[ngadiyak](https://github.com/ngadiyak)**
+* Fixed setting `multiple.parallel.chunks` as environment variable in config. See [#1238](https://github.com/Codeception/CodeceptJS/issues/1238) by **[ngadiyak](https://github.com/ngadiyak)**
 
 ## 1.4.2
 
@@ -497,9 +497,9 @@ locate('//table')
 
 * [Dynamic configuration](https://codecept.io/advanced/#dynamic-configuration) to update helpers config per test or per suite.
 * Added `event.test.finished` which fires synchronously for both failed and passed tests.
-* [WebDriverIO][Protractor][Nightmare][Puppeteer] Full page screenshots on failure disabled by default. See [issue[#1600](https://github.com/Codeception/Codeception/issues/1600). You can enabled them with `fullPageScreenshots: true`, however they may work unstable in Selenium.
+* [WebDriverIO][Protractor][Nightmare][Puppeteer] Full page screenshots on failure disabled by default. See [issue[#1600](https://github.com/Codeception/CodeceptJS/issues/1600). You can enabled them with `fullPageScreenshots: true`, however they may work unstable in Selenium.
 * `within` blocks can return values. See [updated documentation](https://codecept.io/basics/#within).
-* Removed doublt call to `_init` in helpers. Fixes issue [#1036](https://github.com/Codeception/Codeception/issues/1036)
+* Removed doublt call to `_init` in helpers. Fixes issue [#1036](https://github.com/Codeception/CodeceptJS/issues/1036)
 * Added scenario and feature configuration via fluent API:
 
 ```js
@@ -763,7 +763,7 @@ I.dontSee('Email Address');
 ↑ This element can be located with `~foobar` in WebDriverIO and Appium helpers. Thanks to **[flyskywhy](https://github.com/flyskywhy)**
 
 * Allow providing arbitrary objects in config includes by **[rlewan](https://github.com/rlewan)**
-* **[REST]** Prevent from mutating default headers by **[alexashley](https://github.com/alexashley)**. See [#789](https://github.com/Codeception/Codeception/issues/789)
+* **[REST]** Prevent from mutating default headers by **[alexashley](https://github.com/alexashley)**. See [#789](https://github.com/Codeception/CodeceptJS/issues/789)
 * **[REST]** Fixed sending empty helpers with `haveRequestHeaders` in `sendPostRequest`. By **[petrisorionel](https://github.com/petrisorionel)**
 * Fixed displaying undefined args in output by **[APshenkin](https://github.com/APshenkin)**
 * Fixed NaN instead of seconds in output by **[APshenkin](https://github.com/APshenkin)**
@@ -775,7 +775,7 @@ I.dontSee('Email Address');
 ## 1.0.3
 
 * [WebDriverIO][Protractor][Nightmare] method `waitUntilExists` implemented by **[sabau](https://github.com/sabau)**
-* Absolute path can be set for `output` dir by **[APshenkin](https://github.com/APshenkin)**. Fix [#571](https://github.com/Codeception/Codeception/issues/571)* Data table rows can be ignored by using `xadd`. By **[APhenkin](https://github.com/APhenkin)**
+* Absolute path can be set for `output` dir by **[APshenkin](https://github.com/APshenkin)**. Fix [#571](https://github.com/Codeception/CodeceptJS/issues/571)* Data table rows can be ignored by using `xadd`. By **[APhenkin](https://github.com/APhenkin)**
 * Added `Data(table).only.Scenario` to give ability to launch only Data tests. By **[APhenkin](https://github.com/APhenkin)**
 * Implemented `ElementNotFound` error by **[BorisOsipov](https://github.com/BorisOsipov)**.
 * Added TypeScript compiler / configs to check the JavaScript by **[KennyRules](https://github.com/KennyRules)**
@@ -788,11 +788,11 @@ I.dontSee('Email Address');
 
 * Introduced generators support in scenario hooks for `BeforeSuite`/`Before`/`AfterSuite`/`After`
 * **[ApiDataFactory]** Fixed loading helper; `requireg` package included.
-* Fix [#485](https://github.com/Codeception/Codeception/issues/485)`run-multiple`: the first browser-resolution combination was be used in all configurations
+* Fix [#485](https://github.com/Codeception/CodeceptJS/issues/485)`run-multiple`: the first browser-resolution combination was be used in all configurations
 * Fixed unique test names:
-  * Fixed [#447](https://github.com/Codeception/Codeception/issues/447) tests failed silently if they have the same name as other tests.
+  * Fixed [#447](https://github.com/Codeception/CodeceptJS/issues/447) tests failed silently if they have the same name as other tests.
   * Use uuid in screenshot names when `uniqueScreenshotNames: true`
-* **[Protractor]** Fixed testing non-angular application. `amOutsideAngularApp` is executed before each step. Fixes [#458](https://github.com/Codeception/Codeception/issues/458)* Added output for steps in hooks when they fail
+* **[Protractor]** Fixed testing non-angular application. `amOutsideAngularApp` is executed before each step. Fixes [#458](https://github.com/Codeception/CodeceptJS/issues/458)* Added output for steps in hooks when they fail
 
 ## 1.0.1
 
@@ -801,7 +801,7 @@ I.dontSee('Email Address');
   * Added [Mochawesome](http://codecept.io/helpers/Mochawesome/) helper
   * `addMochawesomeContext` method to add custom data to mochawesome reports
   * Fixed Mochawesome context for failed screenshots.
-* **[WebDriverIO]** improved click on context to match clickable element with a text inside. Fixes [#647](https://github.com/Codeception/Codeception/issues/647)* **[Nightmare]** Added `refresh` function by **[awhanks](https://github.com/awhanks)**
+* **[WebDriverIO]** improved click on context to match clickable element with a text inside. Fixes [#647](https://github.com/Codeception/CodeceptJS/issues/647)* **[Nightmare]** Added `refresh` function by **[awhanks](https://github.com/awhanks)**
 * fixed `Unhandled promise rejection (rejection id: 1): Error: Unknown wait type: pageLoad`
 * support for tests with retries in html report
 * be sure that change window size and timeouts completes before test
@@ -866,16 +866,16 @@ Next notable feature is **[SmartWait](http://codecept.io/acceptance/#smartwait)*
 * Minimal NodeJS version is 6.11.1 LTS
 * Use `within` command with generators.
 * [Data Driven Tests](http://codecept.io/advanced/#data-driven-tests) introduced.
-* Print execution time per step in `--debug` mode. [#591](https://github.com/Codeception/Codeception/issues/591) by **[APshenkin](https://github.com/APshenkin)**
+* Print execution time per step in `--debug` mode. [#591](https://github.com/Codeception/CodeceptJS/issues/591) by **[APshenkin](https://github.com/APshenkin)**
 * [WebDriverIO][Protractor][Nightmare] Added `disableScreenshots` option to disable screenshots on fail by **[Apshenkin](https://github.com/Apshenkin)**
 * [WebDriverIO][Protractor][Nightmare] Added `uniqueScreenshotNames` option to generate unique names for screenshots on failure by **[Apshenkin](https://github.com/Apshenkin)**
 * [WebDriverIO][Nightmare] Fixed click on context; `click('text', '#el')` will throw exception if text is not found inside `#el`.
 * [WebDriverIO][Protractor][SeleniumWebdriver] [SmartWait introduced](http://codecept.io/acceptance/#smartwait).
-* [WebDriverIO][Protractor][Nightmare]Fixed `saveScreenshot` for PhantomJS, `fullPageScreenshots` option introduced by **[HughZurname](https://github.com/HughZurname)** [#549](https://github.com/Codeception/Codeception/issues/549)
+* [WebDriverIO][Protractor][Nightmare]Fixed `saveScreenshot` for PhantomJS, `fullPageScreenshots` option introduced by **[HughZurname](https://github.com/HughZurname)** [#549](https://github.com/Codeception/CodeceptJS/issues/549)
 * **[Appium]** helper introduced by **[APshenkin](https://github.com/APshenkin)**
-* **[REST]** helper introduced by **[atrevino](https://github.com/atrevino)** in [#504](https://github.com/Codeception/Codeception/issues/504)
-* [WebDriverIO][SeleniumWebdriver] Fixed "windowSize": "maximize" for Chrome 59+ version [#560](https://github.com/Codeception/Codeception/issues/560) by **[APshenkin](https://github.com/APshenkin)**
-* **[Nightmare]** Fixed restarting by **[APshenkin](https://github.com/APshenkin)** [#581](https://github.com/Codeception/Codeception/issues/581)
+* **[REST]** helper introduced by **[atrevino](https://github.com/atrevino)** in [#504](https://github.com/Codeception/CodeceptJS/issues/504)
+* [WebDriverIO][SeleniumWebdriver] Fixed "windowSize": "maximize" for Chrome 59+ version [#560](https://github.com/Codeception/CodeceptJS/issues/560) by **[APshenkin](https://github.com/APshenkin)**
+* **[Nightmare]** Fixed restarting by **[APshenkin](https://github.com/APshenkin)** [#581](https://github.com/Codeception/CodeceptJS/issues/581)
 * **[WebDriverIO]** Methods added by **[APshenkin](https://github.com/APshenkin)**:
     * [grabCssPropertyFrom](http://codecept.io/helpers/WebDriverIO/#grabcsspropertyfrom)
     * [seeTitleEquals](http://codecept.io/helpers/WebDriverIO/#seetitleequals)
@@ -895,10 +895,10 @@ Next notable feature is **[SmartWait](http://codecept.io/acceptance/#smartwait)*
     * [scrollPageToBottom](http://codecept.io/helpers/WebDriverIO/#scrollpagetobottom)
     * [scrollPageToTop](http://codecept.io/helpers/WebDriverIO/#scrollpagetotop)
     * [grabBrowserLogs](http://codecept.io/helpers/WebDriverIO/#grabbrowserlogs)
-* Use mkdirp to create output directory. [#592](https://github.com/Codeception/Codeception/issues/592) by **[vkramskikh](https://github.com/vkramskikh)**
-* **[WebDriverIO]** Fixed `seeNumberOfVisibleElements` by **[BorisOsipov](https://github.com/BorisOsipov)** [#574](https://github.com/Codeception/Codeception/issues/574)
-* Lots of fixes for promise chain by **[APshenkin](https://github.com/APshenkin)** [#568](https://github.com/Codeception/Codeception/issues/568)
-    * Fix [#543](https://github.com/Codeception/Codeception/issues/543)- After block not properly executed if Scenario fails
+* Use mkdirp to create output directory. [#592](https://github.com/Codeception/CodeceptJS/issues/592) by **[vkramskikh](https://github.com/vkramskikh)**
+* **[WebDriverIO]** Fixed `seeNumberOfVisibleElements` by **[BorisOsipov](https://github.com/BorisOsipov)** [#574](https://github.com/Codeception/CodeceptJS/issues/574)
+* Lots of fixes for promise chain by **[APshenkin](https://github.com/APshenkin)** [#568](https://github.com/Codeception/CodeceptJS/issues/568)
+    * Fix [#543](https://github.com/Codeception/CodeceptJS/issues/543)- After block not properly executed if Scenario fails
     * Expected behavior in promise chains: `_beforeSuite` hooks from helpers -> `BeforeSuite` from test -> `_before` hooks from helpers -> `Before` from test - > Test steps -> `_failed` hooks from helpers (if test failed) -> `After` from test -> `_after` hooks from helpers -> `AfterSuite` from test -> `_afterSuite` hook from helpers.
     * if during test we got errors from any hook (in test or in helper) - stop complete this suite and go to another
     * if during test we got error from Selenium server - stop complete this suite and go to another
@@ -906,7 +906,7 @@ Next notable feature is **[SmartWait](http://codecept.io/acceptance/#smartwait)*
     * Complete `_after`, `_afterSuite` hooks even After/AfterSuite from test was failed
     * Don't close browser between suites, when `restart` option is false. We should start browser only one time and close it only after all tests.
     * Close tabs and clear local storage, if `keepCookies` flag is enabled
-* Fix TypeError when using babel-node or ts-node on node.js 7+ [#586](https://github.com/Codeception/Codeception/issues/586) by **[vkramskikh](https://github.com/vkramskikh)**
+* Fix TypeError when using babel-node or ts-node on node.js 7+ [#586](https://github.com/Codeception/CodeceptJS/issues/586) by **[vkramskikh](https://github.com/vkramskikh)**
 * **[Nightmare]** fixed usage of `_locate`
 
 Special thanks to **Andrey Pshenkin** for his work on this release and the major improvements.
@@ -922,9 +922,9 @@ Scenario('My scenario', { build_id: 123, type: 'slow' }, function (I)
 those options can be accessed as `opts` property inside a `test` object. Can be used in custom listeners.
 
 * Added `docs` directory to a package.
-* [WebDriverIO][Protractor][SeleniumWebdriver] Bugfix: cleaning session when `restart: false` by **[tfiwm](https://github.com/tfiwm)** [#519](https://github.com/Codeception/Codeception/issues/519)
+* [WebDriverIO][Protractor][SeleniumWebdriver] Bugfix: cleaning session when `restart: false` by **[tfiwm](https://github.com/tfiwm)** [#519](https://github.com/Codeception/CodeceptJS/issues/519)
 * [WebDriverIO][Protractor][Nightmare] Added second parameter to `saveScreenshot` to allow a full page screenshot. By **[HughZurname](https://github.com/HughZurname)**
-* Added suite object to `suite.before` and `suite.after` events by **[implico](https://github.com/implico)**. [#496](https://github.com/Codeception/Codeception/issues/496)
+* Added suite object to `suite.before` and `suite.after` events by **[implico](https://github.com/implico)**. [#496](https://github.com/Codeception/CodeceptJS/issues/496)
 
 ## 0.6.2
 
@@ -993,8 +993,8 @@ codeceptjs run users_test.js -c tests
 ## 0.5.0
 
 * Protractor ^5.0.0 support (while keeping ^4.0.9 compatibility)
-* Fix 'fullTitle() is not a function' in exit.js by **[hubidu](https://github.com/hubidu)**. See [#388](https://github.com/Codeception/Codeception/issues/388).
-* **[Nightmare]** Fix for `waitTimeout` by **[HughZurname](https://github.com/HughZurname)**. See [#391](https://github.com/Codeception/Codeception/issues/391). Resolves [#236](https://github.com/Codeception/Codeception/issues/236)* Dockerized CodeceptJS setup by **[artiomnist](https://github.com/artiomnist)**. [See reference](https://github.com/Codeception/CodeceptJS/blob/master/docker/README.md)
+* Fix 'fullTitle() is not a function' in exit.js by **[hubidu](https://github.com/hubidu)**. See [#388](https://github.com/Codeception/CodeceptJS/issues/388).
+* **[Nightmare]** Fix for `waitTimeout` by **[HughZurname](https://github.com/HughZurname)**. See [#391](https://github.com/Codeception/CodeceptJS/issues/391). Resolves [#236](https://github.com/Codeception/CodeceptJS/issues/236)* Dockerized CodeceptJS setup by **[artiomnist](https://github.com/artiomnist)**. [See reference](https://github.com/Codeception/CodeceptJS/blob/master/docker/README.md)
 
 ## 0.4.16
 
@@ -1076,7 +1076,7 @@ Scenario('Not that complex', {timeout: 1000}, (I) => {
 });
 ```
 
-* **[WebDriverIO]** Added `uniqueScreenshotNames` option to set unique screenshot names for failed tests. By **[APshenkin](https://github.com/APshenkin)**. See [#299](https://github.com/Codeception/Codeception/issues/299)
+* **[WebDriverIO]** Added `uniqueScreenshotNames` option to set unique screenshot names for failed tests. By **[APshenkin](https://github.com/APshenkin)**. See [#299](https://github.com/Codeception/CodeceptJS/issues/299)
 * **[WebDriverIO]** `clearField` method improved to accept name/label locators and throw errors.
 * [Nightmare][SeleniumWebdriver][Protractor] `clearField` method added.
 * **[Nightmare]** Fixed `waitForElement`, and `waitForVisible` methods.
@@ -1098,7 +1098,7 @@ codeceptjs run -o '{ "helpers": {"WebDriverIO": {"browser": "chrome"}}}'
 ## 0.4.11
 
 * Fixed regression in 0.4.10
-* Added `bootstrap`/`teardown` config options to accept functions as parameters by **[pscanf](https://github.com/pscanf)**. See updated [config reference](http://codecept.io/configuration/) [#319](https://github.com/Codeception/Codeception/issues/319)
+* Added `bootstrap`/`teardown` config options to accept functions as parameters by **[pscanf](https://github.com/pscanf)**. See updated [config reference](http://codecept.io/configuration/) [#319](https://github.com/Codeception/CodeceptJS/issues/319)
 
 ## 0.4.10
 
@@ -1113,26 +1113,26 @@ module.exports = function(done) {
 }
 ```
 
-* Changed 'pending' to 'skipped' in reports by **[timja-kainos](https://github.com/timja-kainos)**. See [#315](https://github.com/Codeception/Codeception/issues/315)
+* Changed 'pending' to 'skipped' in reports by **[timja-kainos](https://github.com/timja-kainos)**. See [#315](https://github.com/Codeception/CodeceptJS/issues/315)
 
 ## 0.4.9
 
 * [SeleniumWebdriver][Protractor][WebDriverIO][Nightmare] fixed `executeScript`, `executeAsyncScript` to work and return values.
 * [Protractor][SeleniumWebdriver][WebDriverIO] Added `waitForInvisible` and `waitForStalenessOf` methods by **[Nighthawk14](https://github.com/Nighthawk14)**.
 * Added `--config` option to `codeceptjs run` to manually specify config file by **[cnworks](https://github.com/cnworks)**
-* **[Protractor]** Simplified behavior of `amOutsideAngularApp` by using `ignoreSynchronization`. Fixes [#278](https://github.com/Codeception/Codeception/issues/278)
-* Set exit code to 1 when test fails at `Before`/`After` hooks. Fixes [#279](https://github.com/Codeception/Codeception/issues/279)
+* **[Protractor]** Simplified behavior of `amOutsideAngularApp` by using `ignoreSynchronization`. Fixes [#278](https://github.com/Codeception/CodeceptJS/issues/278)
+* Set exit code to 1 when test fails at `Before`/`After` hooks. Fixes [#279](https://github.com/Codeception/CodeceptJS/issues/279)
 
 
 ## 0.4.8
 
 * [Protractor][SeleniumWebdriver][Nightmare] added `moveCursorTo` method.
-* [Protractor][SeleniumWebdriver][WebDriverIO] Added `manualStart` option to start browser manually in the beginning of test. By **[cnworks](https://github.com/cnworks)**. [PR[#250](https://github.com/Codeception/Codeception/issues/250)
+* [Protractor][SeleniumWebdriver][WebDriverIO] Added `manualStart` option to start browser manually in the beginning of test. By **[cnworks](https://github.com/cnworks)**. [PR[#250](https://github.com/Codeception/CodeceptJS/issues/250)
 * Fixed `codeceptjs init` to work with nested directories and file masks.
 * Fixed `codeceptjs gt` to generate test with proper file name suffix. By **[Zougi](https://github.com/Zougi)**.
 * **[Nightmare]** Fixed: Error is thrown when clicking on element which can't be locate. By **[davetmik](https://github.com/davetmik)**
 * **[WebDriverIO]** Fixed `attachFile` for file upload. By **[giuband](https://github.com/giuband)** and **[davetmik](https://github.com/davetmik)**
-* **[WebDriverIO]** Add support for timeouts in config and with `defineTimeouts` method. By **[easternbloc](https://github.com/easternbloc)** [#258](https://github.com/Codeception/Codeception/issues/258) and [#267](https://github.com/Codeception/Codeception/issues/267) by **[davetmik](https://github.com/davetmik)**
+* **[WebDriverIO]** Add support for timeouts in config and with `defineTimeouts` method. By **[easternbloc](https://github.com/easternbloc)** [#258](https://github.com/Codeception/CodeceptJS/issues/258) and [#267](https://github.com/Codeception/CodeceptJS/issues/267) by **[davetmik](https://github.com/davetmik)**
 * Fixed hanging of CodeceptJS when error is thrown by event dispatcher. Fix by **[Zougi](https://github.com/Zougi)** and **[davetmik](https://github.com/davetmik)**
 
 
@@ -1149,7 +1149,7 @@ module.exports = function(done) {
 ## 0.4.5
 
 * Fixed running `codecept def` command by **[jankaspar](https://github.com/jankaspar)**
-* [Protractor][SeleniumWebdriver] Added support for special keys in `pressKey` method. Fixes [#216](https://github.com/Codeception/Codeception/issues/216)
+* [Protractor][SeleniumWebdriver] Added support for special keys in `pressKey` method. Fixes [#216](https://github.com/Codeception/CodeceptJS/issues/216)
 
 ## 0.4.4
 
@@ -1176,7 +1176,7 @@ exports.config = {
 
 ## 0.4.2
 
-* Added ability to localize tests with translation [#189](https://github.com/Codeception/Codeception/issues/189). Thanks to **[abner](https://github.com/abner)**
+* Added ability to localize tests with translation [#189](https://github.com/Codeception/CodeceptJS/issues/189). Thanks to **[abner](https://github.com/abner)**
   * **[Translation]** ru-RU translation added.
   * **[Translation]** pt-BR translation added.
 * **[Protractor]** Protractor 4.0.4 compatibility.
@@ -1187,8 +1187,8 @@ exports.config = {
 
 ## 0.4.1
 
-* Added custom steps to step definition list. See [#174](https://github.com/Codeception/Codeception/issues/174) by **[jayS-de](https://github.com/jayS-de)**
-* **[WebDriverIO]** Fixed using `waitForTimeout` option by **[stephane-ruhlmann](https://github.com/stephane-ruhlmann)**. See [#178](https://github.com/Codeception/Codeception/issues/178)
+* Added custom steps to step definition list. See [#174](https://github.com/Codeception/CodeceptJS/issues/174) by **[jayS-de](https://github.com/jayS-de)**
+* **[WebDriverIO]** Fixed using `waitForTimeout` option by **[stephane-ruhlmann](https://github.com/stephane-ruhlmann)**. See [#178](https://github.com/Codeception/CodeceptJS/issues/178)
 
 ## 0.4.0
 
@@ -1228,7 +1228,7 @@ Depending on installation type additional modules (webdriverio, protractor, ...)
 * [Protractor][SeleniumWebdriver] fixed closing browser instances
 * [Protractor][SeleniumWebdriver] `doubleClick` method added
 * [WebDriverIO][Protractor][SeleniumWebdriver] `doubleClick` method to locate clickable elements by text, `context` option added.
-* Fixed using assert in generator without yields [#89](https://github.com/Codeception/Codeception/issues/89)
+* Fixed using assert in generator without yields [#89](https://github.com/Codeception/CodeceptJS/issues/89)
 
 ## 0.3.1
 
@@ -1248,8 +1248,8 @@ Starting from 0.3.0 webdriverio is not the only backend for running selenium tes
 * **[WebDriverIO]** fixed `windowSize: maximize` option in config
 * **[WebDriverIO]** `seeElement` and `dontSeeElement` check element for visibility by **[fabioel](https://github.com/fabioel)** and **[davertmik](https://github.com/davertmik)**
 * **[WebDriverIO]** `seeElementInDOM`, `dontSeeElementInDOM` added to check element exists on page.
-* **[WebDriverIO]** fixed saving screenshots on failure. Fixes [#70](https://github.com/Codeception/Codeception/issues/70)
-* fixed `within` block doesn't end in output not [#79](https://github.com/Codeception/Codeception/issues/79)
+* **[WebDriverIO]** fixed saving screenshots on failure. Fixes [#70](https://github.com/Codeception/CodeceptJS/issues/70)
+* fixed `within` block doesn't end in output not [#79](https://github.com/Codeception/CodeceptJS/issues/79)
 
 
 ## 0.2.8
@@ -1258,20 +1258,20 @@ Starting from 0.3.0 webdriverio is not the only backend for running selenium tes
 
 ## 0.2.7
 
-* process ends with exit code 1 on error or failure [#49](https://github.com/Codeception/Codeception/issues/49)
-* fixed registereing global Helper [#57](https://github.com/Codeception/Codeception/issues/57)
-* fixed handling error in within block [#50](https://github.com/Codeception/Codeception/issues/50)
+* process ends with exit code 1 on error or failure [#49](https://github.com/Codeception/CodeceptJS/issues/49)
+* fixed registereing global Helper [#57](https://github.com/Codeception/CodeceptJS/issues/57)
+* fixed handling error in within block [#50](https://github.com/Codeception/CodeceptJS/issues/50)
 
 ## 0.2.6
 
 * Fixed `done() was called multiple times`
 * **[WebDriverIO]** added `waitToHide` method by **[fabioel](https://github.com/fabioel)**
-* Added global `Helper` (alias `codecept_helper)`, object use for writing custom Helpers. Generator updated. Changes to [#48](https://github.com/Codeception/Codeception/issues/48)
+* Added global `Helper` (alias `codecept_helper)`, object use for writing custom Helpers. Generator updated. Changes to [#48](https://github.com/Codeception/CodeceptJS/issues/48)
 
 ## 0.2.5
 
-* Fixed issues with using yield inside a test [#45](https://github.com/Codeception/Codeception/issues/45) [#47](https://github.com/Codeception/Codeception/issues/47) [#43](https://github.com/Codeception/Codeception/issues/43)
-* Fixed generating a custom helper. Helper class is now accessible with `codecept_helper` var. Fixes [#48](https://github.com/Codeception/Codeception/issues/48)
+* Fixed issues with using yield inside a test [#45](https://github.com/Codeception/CodeceptJS/issues/45) [#47](https://github.com/Codeception/CodeceptJS/issues/47) [#43](https://github.com/Codeception/CodeceptJS/issues/43)
+* Fixed generating a custom helper. Helper class is now accessible with `codecept_helper` var. Fixes [#48](https://github.com/Codeception/CodeceptJS/issues/48)
 
 ## 0.2.4
 
@@ -1304,7 +1304,7 @@ Whenever you need to create `I` object (in page objects, custom steps, but not i
   - mouse: `rightClick`
 * tests added
 * **[WebDriverIO]** proxy configuration added by **[petehouston](https://github.com/petehouston)**
-* **[WebDriverIO]** fixed `waitForText` method by **[roadhump](https://github.com/roadhump)**. Fixes [#11](https://github.com/Codeception/Codeception/issues/11)
+* **[WebDriverIO]** fixed `waitForText` method by **[roadhump](https://github.com/roadhump)**. Fixes [#11](https://github.com/Codeception/CodeceptJS/issues/11)
 * Fixed creating output dir when it already exists on init by **[alfirin](https://github.com/alfirin)**
 * Fixed loading of custom helpers
 
diff --git a/docs/community-helpers.md b/docs/community-helpers.md
index fead78e7..e55c53c3 100644
--- a/docs/community-helpers.md
+++ b/docs/community-helpers.md
@@ -34,5 +34,7 @@ Please **add your own** by editing this page.
 ## Reporters
 * [codeceptjs-rphelper](https://www.npmjs.com/package/codeceptjs-rphelper) is a CodeceptJS helper which can publish tests results on ReportPortal after execution.
 
+## Page Object Code Generator
+* [codeceptjs-CodeGenerator](https://github.com/senthillkumar/CodeCeptJS-PageObject) is a CodeceptJS custom wrapper which can create page class with action methods from the page object file(JSON) and project setup(Folder Structure).
 ## Other
 
diff --git a/docs/detox.md b/docs/detox.md
new file mode 100644
index 00000000..93e5936e
--- /dev/null
+++ b/docs/detox.md
@@ -0,0 +1,235 @@
+---
+id: detox
+title: Testing React Native with Detox
+---
+
+> Warning! Detox support in CodeceptJS is experimental. Please try it and help us to test it and improve it. [See Detox helper repository](https://github.com/Codeception/detox-helper).
+
+Automated mobile testing can be slow, hard, and ineffective. The price of it goes high, if we take into account fragility of applications, slowness of emulators, and the complexity of debug. [Appium](https://codecept.io/mobile) helps writing mobile tests but not all apps can be tested effectively with it. That's why you should consider using an alternative approach.
+
+Meet [Detox](https://github.com/wix/Detox) - grey-box testing solution for mobile testing by Wix.
+
+Unlike, Appium, Detox requires to update mobile application to include test instrumentation, so an application could receive commands from a test, and act accordingly. Let's see pros and cons of such approach:
+
+
+**Pros**:
+
+* faster tests
+* synchronization with application
+* plays nicely with React Native
+
+**Cons**:
+
+* application should be built in a special way
+* no hybrid applications
+* native Android apps not supported (except React Native)
+* no cloud testing with SauceLabs or BrowserStack
+
+CodeceptJS allows you to try different options and choose the one which works best for you. Both Appium and Detox helpers share the same syntax for testing mobile applications, interactive pause, automatic retries, and other useful features.
+
+CodeceptJS provides next features over standard Detox:
+
+* **Unified API**. The same test can be executed in Appium or Detox. Unified API helps different teams to use the same syntax and easy port tests from one engine to another.
+* [Interactive pause](https://codecept.io/basics#pause). When starting/stopping an application takes a long time, debugging and writing tests can be hard. CodeceptJS solves this by pausing an execution and letting you try different commands and locators. With this feature a test can be writtern during one running session.
+* [Auto-retries](https://codecept.io/basics#retries) using `retryFailedStepPlugin` and `I.retry()`
+* **Cross-Platform testing** - one test can be executed on different engines. When needed, platform-specific actions and locators can be easily applied.
+
+## A Test
+
+Compare a test written for Detox using its native syntax:
+
+```js
+await expect(element(by.text('Welcome'))).toBeVisible();
+await expect(element(by.id('createdAndVisibleText'))).toNotExist();
+await element(by.id('GoButton')).tap();
+await waitFor(element(by.id('createdAndVisibleText'))).toExist().withTimeout(20000);
+await expect(element(by.id('createdAndVisibleText'))).toExist();
+```
+
+with the same test written using CodeceptJS syntax:
+
+```js
+I.see('Welcome');
+I.dontSeeElement('#createdAndVisibleText');
+I.click('#GoButton');
+I.waitForElement('#createdAndVisibleText', 20);
+I.seeElement('#createdAndVisibleText');
+```
+
+As you see, CodeceptJS test is shorter and easier to follow. By simplifying the code and reducing visual noise we make tests easier to follow. But before writing a test we need to prepare an application to be testable with Detox.
+
+## Setup
+
+It is important to follow [Detox guide](https://github.com/wix/Detox/blob/master/docs/Introduction.GettingStarted.md) to build an application with Detox test instrouments included.
+
+After you install Detox, create configuration and build an application using `detox build` command, you are ready to integrate Detox with CodeceptJS.
+
+You need to install Detox CodeceptJS helper:
+
+```
+npm i @codeceptjs/detox-helper --save-dev
+```
+
+Then enable this helper in `codecept.conf.js`:
+
+```js
+helpers: {
+   Detox: {
+     require: '@codeceptjs/detox-helper',
+     configuration: '<detox app configuration name>',
+     reloadReactNative: true,
+   }
+}
+```
+
+Enable `reloadReactNative: true` if you test React Native application.
+
+## Actions
+
+Create test as usual, by running command:
+
+```
+npx codeceptjs gt
+```
+
+A test starts when emulator starts and loads an application. So you can begin testing an application.
+
+```js
+// inside a created test
+Scenario('test React Native app', (I) => {
+  I.see('Welcome');
+  I.tap('Start');
+  I.see('Started!');
+});
+```
+
+The most common actions are:
+
+* `tap` (or `click`)
+* `multiTap` - perform multiple taps on element
+* `longPress` - longer press
+* `fillField` - fill in value of text field
+* `clearField` - clear value in text field
+* `appendField` - append value in a field
+* `swipeUp`, `swipeDown`, `swipeLeft`, `swipeRigth`
+
+There are also common assertions:
+
+* `see` - to check visibility of text
+* `seeElement` - to check visibility of element
+* `seeElementExists` - to check that element exists
+
+> For more details on actions refer to the [API reference of Detox helper](https://github.com/Codeception/detox-helper#api).
+
+## Locators
+
+To write a test you need to learn about available locators in Detox.
+Unlike, Appium there are no XPath locators. Possible locators limited to `text`, `id`, `accessibility id`, and element `type`. Thus, again, an application should be prepared for testing, to ensure that all active elements are accessible.
+
+Let's see how they can be used:
+
+* For **ID locators** use `#` prefix (same as in CSS). Example:
+
+```js
+I.seeElement('#WelcomeScreen')
+```
+* For **accessibility ID** use `~` prefix (as in Appium helper). Example:
+
+```js
+I.seeElement('~welcome')
+```
+
+* Locating elements **by text** requires no prefix, but can be applied only for actions accepting semantic locators.
+
+```js
+I.tap('Start')
+I.fillField('Username', 'davert')
+I.clearField('Username')
+I.see('Welcome, davert')
+```
+
+* Locating elements **by type** also use no prefix but can be used only where for elements.
+
+```js
+I.seeElement('Button')
+```
+
+Text locators can be combined with others, as methods `tap`, `click` and `see` can receive a second paramater, which defines a context where to perfrom a search.
+
+```js
+// tap "Start" inside "#Layout"
+I.tap('Start', '#Layout');
+// see text "Welcome" inside "~msg"
+I.see('Welcome', '~msg');
+```
+
+Alternatively, you can use specify locator by using *strict locator*, passing an object as a locator:
+
+```js
+I.click({ type: 'Button' });
+I.seeElement({ label: 'welcome' });
+```
+
+> If you are familiar with Detox API, this is how locators are actually translated: `{label: 'welcome'}` => `by.label('welcome')`.
+
+### Cross Platform Testing
+
+If element differs on on iOS and Android you can use **cross platform locators**.
+
+```js
+// locate element by text on Android
+// locate element by accessibility id on iOS
+I.click({ android: 'Start', ios: '~start' });
+```
+
+When application behavior differs on Android and iOS use platform-specific actions:
+
+```js
+I.runOnIOS(() => {
+  // this will be executed only on IOS
+  I.see('Hello iOS');
+});
+I.runOnAndroid(() => {
+  // this will be executed only on Android
+  I.see('Hello Android');
+});
+```
+
+### Sample Test
+
+Finally, you can get a test looking like this
+
+```js
+Feature('My Detox App');
+
+Scenario('save in application', (I) => {
+  I.setLandscapeOrientation();
+  I.fillField('#text', 'a new text');
+  I.see('a new text', '#textValue');
+  I.dontSeeElement('#createdAndVisibleText');
+  I.click({ ios: '#GoButton', android: 'Button' });
+  I.waitForElement('#createdAndVisibleText', 20);
+  I.seeElement('#createdAndVisibleText');
+  I.runOnAndroid(() => {
+    I.click('Save');
+    I.see('Text Saved', '#message');
+  });
+  I.runOnIOS(() => {
+    I.click('SAVE');
+    I.see('SAVED!');
+  });
+});
+```
+
+To execute it use `codeceptjs run` command
+
+```
+npx codeceptjs run
+```
+If you want to use detox configuration other than is set in `codecept.conf.js` use `--configuration` argument:
+
+```
+npx codeceptjs run --configuration android.test.ci
+```
+
+You can also pass all [other arguments that Detox CLI supports](https://github.com/wix/Detox/blob/master/docs/APIRef.DetoxCLI.md#test).
\ No newline at end of file
diff --git a/docs/helpers/Detox.md b/docs/helpers/Detox.md
new file mode 100644
index 00000000..420bd350
--- /dev/null
+++ b/docs/helpers/Detox.md
@@ -0,0 +1,6 @@
+---
+id: Detox
+title: Detox
+---
+
+<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
diff --git a/docs/helpers/Puppeteer.md b/docs/helpers/Puppeteer.md
index f3528232..8158996b 100644
--- a/docs/helpers/Puppeteer.md
+++ b/docs/helpers/Puppeteer.md
@@ -36,8 +36,8 @@ This helper should be configured in codecept.json or codecept.conf.js
 -   `windowSize`: (optional) default window size. Set a dimension like `640x480`.
 -   `userAgent`: (optional) user-agent string.
 -   `manualStart`:  - do not start browser before a test, start it manually inside a helper with `this.helpers["Puppeteer"]._startBrowser()`.
--   `browser`:  - can be changed to `firefox` when using [puppeteer-firefox][4].
--   `chrome`: (optional) pass additional [Puppeteer run options][5].
+-   `browser`:  - can be changed to `firefox` when using [puppeteer-firefox][2].
+-   `chrome`: (optional) pass additional [Puppeteer run options][4].
 
 #### Example #1: Wait for 0 network connections.
 
@@ -82,7 +82,7 @@ This helper should be configured in codecept.json or codecept.conf.js
 }
 ```
 
-#### Example #4: Connect to remote browser by specifying [websocket endpoint][6]
+#### Example #4: Connect to remote browser by specifying [websocket endpoint][5]
 
 ```json
 {
@@ -192,13 +192,13 @@ Set current page
 
 #### Parameters
 
--   `page` [object][7] page to set
+-   `page` [object][6] page to set
 
 ### acceptPopup
 
 Accepts the active JavaScript native popup window, as created by window.alert|window.confirm|window.prompt.
 Don't confuse popups with modal windows, as created by [various
-libraries][8].
+libraries][7].
 
 ### amAcceptingPopups
 
@@ -236,7 +236,7 @@ I.amOnPage('/login'); // opens a login page
 
 #### Parameters
 
--   `url` [string][9] url path or global url.
+-   `url` [string][8] url path or global url.
     
 
 
@@ -251,8 +251,8 @@ I.appendField('#myTextField', 'appended');
 
 #### Parameters
 
--   `field` ([string][9] \| [object][7]) located by label|name|CSS|XPath|strict locator
--   `value` [string][9] text value to append.
+-   `field` ([string][8] \| [object][6]) located by label|name|CSS|XPath|strict locator
+-   `value` [string][8] text value to append.
     
 
 
@@ -273,8 +273,8 @@ I.attachFile('form input[name=avatar]', 'data/avatar.jpg');
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) field located by label|name|CSS|XPath|strict locator.
--   `pathToFile` [string][9] local file path relative to codecept.json config file.
+-   `locator` ([string][8] \| [object][6]) field located by label|name|CSS|XPath|strict locator.
+-   `pathToFile` [string][8] local file path relative to codecept.json config file.
     
 
 
@@ -297,8 +297,8 @@ I.checkOption('agree', '//form');
 
 #### Parameters
 
--   `field` ([string][9] \| [object][7]) checkbox located by label | name | CSS | XPath | strict locator.
--   `context` [string][9] (optional, `null` by default) element located by CSS | XPath | strict locator.
+-   `field` ([string][8] \| [object][6]) checkbox located by label | name | CSS | XPath | strict locator.
+-   `context` [string][8] (optional, `null` by default) element located by CSS | XPath | strict locator.
     
  
 
@@ -315,7 +315,7 @@ I.clearCookie('test');
 #### Parameters
 
 -   `name`  
--   `cookie` [string][9] (optional, `null` by default) cookie name
+-   `cookie` [string][8] (optional, `null` by default) cookie name
     
 
 
@@ -332,7 +332,7 @@ I.clearField('#email');
 #### Parameters
 
 -   `field`  
--   `editable` ([string][9] \| [object][7]) field located by label|name|CSS|XPath|strict locator.
+-   `editable` ([string][8] \| [object][6]) field located by label|name|CSS|XPath|strict locator.
     
 
 
@@ -362,8 +362,8 @@ I.click({css: 'nav a.login'});
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) clickable link or button located by text, or any element located by CSS|XPath|strict locator.
--   `context` ([string][9] \| [object][7]) (optional, `null` by default) element to search in CSS|XPath|Strict locator.
+-   `locator` ([string][8] \| [object][6]) clickable link or button located by text, or any element located by CSS|XPath|strict locator.
+-   `context` ([string][8] \| [object][6]) (optional, `null` by default) element to search in CSS|XPath|Strict locator.
     
 
 
@@ -381,8 +381,8 @@ I.clickLink('Logout', '#nav');
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) clickable link or button located by text, or any element located by CSS|XPath|strict locator
--   `context` ([string][9] \| [object][7]) (optional, `null` by default) element to search in CSS|XPath|Strict locator
+-   `locator` ([string][8] \| [object][6]) clickable link or button located by text, or any element located by CSS|XPath|strict locator
+-   `context` ([string][8] \| [object][6]) (optional, `null` by default) element to search in CSS|XPath|Strict locator
     
 
 
@@ -418,8 +418,8 @@ I.dontSee('Login', '.nav'); // no login inside .nav element
 
 #### Parameters
 
--   `text` [string][9] which is not present.
--   `context` ([string][9] \| [object][7]) (optional) element located by CSS|XPath|strict locator in which to perfrom search.
+-   `text` [string][8] which is not present.
+-   `context` ([string][8] \| [object][6]) (optional) element located by CSS|XPath|strict locator in which to perfrom search.
 
 
 
@@ -438,7 +438,7 @@ I.dontSeeeCheckboxIsChedcked('agree'); // located by name
 
 #### Parameters
 
--   `field` ([string][9] \| [object][7]) located by label|name|CSS|XPath|strict locator.
+-   `field` ([string][8] \| [object][6]) located by label|name|CSS|XPath|strict locator.
     
 
 
@@ -452,7 +452,7 @@ I.dontSeeCookie('auth'); // no auth cookie
 
 #### Parameters
 
--   `name` [string][9] cookie name.
+-   `name` [string][8] cookie name.
     
 
 
@@ -468,7 +468,7 @@ I.dontSeeCurrentUrlEquals('http://mysite.com/login'); // absolute urls are also
 
 #### Parameters
 
--   `url` [string][9] value to check.
+-   `url` [string][8] value to check.
     
 
 
@@ -482,7 +482,7 @@ I.dontSeeElement('.modal'); // modal is not shown
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) located by CSS|XPath|Strict locator.
+-   `locator` ([string][8] \| [object][6]) located by CSS|XPath|Strict locator.
     
 
     
@@ -501,7 +501,7 @@ I.dontSeeElementInDOM('.nav'); // checks that element is not on page visible or
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) located by CSS|XPath|Strict locator.
+-   `locator` ([string][8] \| [object][6]) located by CSS|XPath|Strict locator.
     
 
 
@@ -511,7 +511,7 @@ Checks that current url does not contain a provided fragment.
 
 #### Parameters
 
--   `url` [string][9] value to check.
+-   `url` [string][8] value to check.
     
 
 
@@ -527,8 +527,8 @@ I.dontSeeInField({ css: 'form input.email' }, 'user@user.com'); // field by CSS
 
 #### Parameters
 
--   `field` ([string][9] \| [object][7]) located by label|name|CSS|XPath|strict locator.
--   `value` [string][9] value to check.
+-   `field` ([string][8] \| [object][6]) located by label|name|CSS|XPath|strict locator.
+-   `value` [string][8] value to check.
     
 
 
@@ -543,7 +543,7 @@ I.dontSeeInSource('<!--'); // no comments in source
 #### Parameters
 
 -   `text`  
--   `value` [string][9] to check.
+-   `value` [string][8] to check.
     
 
 
@@ -557,7 +557,7 @@ I.dontSeeInTitle('Error');
 
 #### Parameters
 
--   `text` [string][9] value to check.
+-   `text` [string][8] value to check.
     
 
 
@@ -575,8 +575,8 @@ I.doubleClick('.btn.edit');
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) clickable link or button located by text, or any element located by CSS|XPath|strict locator.
--   `context` ([string][9] \| [object][7]) (optional, `null` by default) element to search in CSS|XPath|Strict locator.
+-   `locator` ([string][8] \| [object][6]) clickable link or button located by text, or any element located by CSS|XPath|strict locator.
+-   `context` ([string][8] \| [object][6]) (optional, `null` by default) element to search in CSS|XPath|Strict locator.
     
 
 
@@ -605,8 +605,8 @@ I.dragAndDrop('#dragHandle', '#container');
 
 #### Parameters
 
--   `srcElement` ([string][9] \| [object][7]) located by CSS|XPath|strict locator.
--   `destElement` ([string][9] \| [object][7]) located by CSS|XPath|strict locator.
+-   `srcElement` ([string][8] \| [object][6]) located by CSS|XPath|strict locator.
+-   `destElement` ([string][8] \| [object][6]) located by CSS|XPath|strict locator.
     
 
 
@@ -622,8 +622,8 @@ I.dragSlider('#slider', -70);
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) located by label|name|CSS|XPath|strict locator.
--   `offsetX` [number][10] position to drag.
+-   `locator` ([string][8] \| [object][6]) located by label|name|CSS|XPath|strict locator.
+-   `offsetX` [number][9] position to drag.
     
 
     
@@ -637,7 +637,7 @@ This action supports [React locators](https://codecept.io/react#locators)
 Executes async script on page.
 Provided function should execute a passed callback (as first argument) to signal it is finished.
 
-Example: In Vue.js to make components completely rendered we are waiting for [nextTick][11].
+Example: In Vue.js to make components completely rendered we are waiting for [nextTick][10].
 
 ```js
 I.executeAsyncScript(function(done) {
@@ -657,7 +657,7 @@ let val = await I.executeAsyncScript(function(url, done) {
 
 #### Parameters
 
--   `fn` ([string][9] \| [function][12]) function to be executed in browser context.
+-   `fn` ([string][8] \| [function][11]) function to be executed in browser context.
 
 ### executeScript
 
@@ -687,7 +687,7 @@ let date = await I.executeScript(function(el) {
 
 #### Parameters
 
--   `fn` ([string][9] \| [function][12]) function to be executed in browser context.
+-   `fn` ([string][8] \| [function][11]) function to be executed in browser context.
 
 ### fillField
 
@@ -707,8 +707,8 @@ I.fillField({css: 'form#login input[name=username]'}, 'John');
 
 #### Parameters
 
--   `field` ([string][9] \| [object][7]) located by label|name|CSS|XPath|strict locator.
--   `value` [string][9] text value to fill.
+-   `field` ([string][8] \| [object][6]) located by label|name|CSS|XPath|strict locator.
+-   `value` [string][8] text value to fill.
     
 
     
@@ -729,10 +729,10 @@ let hint = await I.grabAttributeFrom('#tooltip', 'title');
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) element located by CSS|XPath|strict locator.
--   `attr` [string][9] attribute name.
+-   `locator` ([string][8] \| [object][6]) element located by CSS|XPath|strict locator.
+-   `attr` [string][8] attribute name.
 
-Returns [Promise][13]&lt;[string][9]> attribute value
+Returns [Promise][12]&lt;[string][8]> attribute value
 
 
 
@@ -766,7 +766,7 @@ assert(cookie.value, '123456');
 
 -   `name`  cookie name. 
 
-Returns [Promise][13]&lt;[string][9]> attribute value
+Returns [Promise][12]&lt;[string][8]> attribute value
 
 Returns cookie in JSON format. If name not passed returns all cookies for this domain.
 
@@ -781,10 +781,10 @@ const value = await I.grabCssPropertyFrom('h3', 'font-weight');
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) element located by CSS|XPath|strict locator.
--   `cssProperty` [string][9] CSS property name.
+-   `locator` ([string][8] \| [object][6]) element located by CSS|XPath|strict locator.
+-   `cssProperty` [string][8] CSS property name.
 
-Returns [Promise][13]&lt;[string][9]> CSS value
+Returns [Promise][12]&lt;[string][8]> CSS value
 
 
 
@@ -803,7 +803,7 @@ let url = await I.grabCurrentUrl();
 console.log(`Current URL is [${url}]`);
 ```
 
-Returns [Promise][13]&lt;[string][9]> current URL
+Returns [Promise][12]&lt;[string][8]> current URL
 
 
 
@@ -847,7 +847,7 @@ let postHTML = await I.grabHTMLFrom('#post');
 
 -   `locator`  element located by CSS|XPath|strict locator.
 
-Returns [Promise][13]&lt;[string][9]> HTML code for an element
+Returns [Promise][12]&lt;[string][8]> HTML code for an element
 
 
 
@@ -859,7 +859,7 @@ Grab number of open tabs.
 let tabs = await I.grabNumberOfOpenTabs();
 ```
 
-Returns [Promise][13]&lt;[number][10]> number of open tabs
+Returns [Promise][12]&lt;[number][9]> number of open tabs
 
 
 
@@ -873,9 +873,9 @@ let numOfElements = await I.grabNumberOfVisibleElements('p');
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) located by CSS|XPath|strict locator.
+-   `locator` ([string][8] \| [object][6]) located by CSS|XPath|strict locator.
 
-Returns [Promise][13]&lt;[number][10]> number of visible elements
+Returns [Promise][12]&lt;[number][9]> number of visible elements
 
 
 
@@ -893,7 +893,7 @@ Resumes test execution, so should be used inside an async function with `await`
 let { x, y } = await I.grabPageScrollPosition();
 ```
 
-Returns [Promise][13]&lt;[object][7]> scroll position
+Returns [Promise][12]&lt;[object][6]> scroll position
 
 
 
@@ -914,7 +914,7 @@ Resumes test execution, so should be used inside an async function.
 let pageSource = await I.grabSource();
 ```
 
-Returns [Promise][13]&lt;[string][9]> source code
+Returns [Promise][12]&lt;[string][8]> source code
 
 
 
@@ -933,7 +933,7 @@ If multiple elements found returns an array of texts.
 
 -   `locator`  element located by CSS|XPath|strict locator.
 
-Returns [Promise][13]&lt;[string][9]> attribute value
+Returns [Promise][12]&lt;[string][8]> attribute value
 
 
 
@@ -951,7 +951,7 @@ Resumes test execution, so should be used inside async with `await` operator.
 let title = await I.grabTitle();
 ```
 
-Returns [Promise][13]&lt;[string][9]> title
+Returns [Promise][12]&lt;[string][8]> title
 
 
 
@@ -966,16 +966,16 @@ let email = await I.grabValueFrom('input[name=email]');
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) field located by label|name|CSS|XPath|strict locator.
+-   `locator` ([string][8] \| [object][6]) field located by label|name|CSS|XPath|strict locator.
 
-Returns [Promise][13]&lt;[string][9]> attribute value
+Returns [Promise][12]&lt;[string][8]> attribute value
 
 
 
 ### handleDownloads
 
 Sets a directory to where save files. Allows to test file downloads.
-Should be used with [FileSystem helper][14] to check that file were downloaded correctly.
+Should be used with [FileSystem helper][13] to check that file were downloaded correctly.
 
 By default files are saved to `output/downloads`.
 This directory is cleaned on every `handleDownloads` call, to ensure no old files are kept.
@@ -989,7 +989,7 @@ I.seeFile('avatar.jpg');
 
 #### Parameters
 
--   `downloadPath` [string][9] change this parameter to set another directory for saving 
+-   `downloadPath` [string][8] change this parameter to set another directory for saving 
 
 ### haveRequestHeaders
 
@@ -1003,7 +1003,7 @@ I.haveRequestHeaders({
 
 #### Parameters
 
--   `customHeaders` [object][7] headers to set
+-   `customHeaders` [object][6] headers to set
 
 ### moveCursorTo
 
@@ -1017,9 +1017,9 @@ I.moveCursorTo('#submit', 5,5);
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) located by CSS|XPath|strict locator.
--   `offsetX` [number][10] (optional, `0` by default) X-axis offset. 
--   `offsetY` [number][10] (optional, `0` by default) Y-axis offset.
+-   `locator` ([string][8] \| [object][6]) located by CSS|XPath|strict locator.
+-   `offsetX` [number][9] (optional, `0` by default) X-axis offset. 
+-   `offsetY` [number][9] (optional, `0` by default) Y-axis offset.
     
 
     
@@ -1039,7 +1039,7 @@ I.openNewTab();
 ### pressKey
 
 Presses a key on a focused element.
-Special keys like 'Enter', 'Control', [etc][15]
+Special keys like 'Enter', 'Control', [etc][14]
 will be replaced with corresponding unicode.
 If modifier key is used (Control, Command, Alt, Shift) in array, it will be released afterwards.
 
@@ -1050,7 +1050,7 @@ I.pressKey(['Control','a']);
 
 #### Parameters
 
--   `key` ([string][9] \| [array][16]) key or array of keys to press.
+-   `key` ([string][8] \| [array][15]) key or array of keys to press.
     
 
 
@@ -1105,8 +1105,8 @@ First parameter can be set to `maximize`.
 
 #### Parameters
 
--   `width` [number][10] width in pixels or `maximize`.
--   `height` [number][10] height in pixels.
+-   `width` [number][9] width in pixels or `maximize`.
+-   `height` [number][9] height in pixels.
     
 Unlike other drivers Puppeteer changes the size of a viewport, not the window!
     Puppeteer does not control the window of a browser so it can't adjust its real size.
@@ -1127,8 +1127,8 @@ I.rightClick('Click me', '.context');
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) clickable element located by CSS|XPath|strict locator.
--   `context` ([string][9] \| [object][7]) (optional, `null` by default) element located by CSS|XPath|strict locator.
+-   `locator` ([string][8] \| [object][6]) clickable element located by CSS|XPath|strict locator.
+-   `context` ([string][8] \| [object][6]) (optional, `null` by default) element located by CSS|XPath|strict locator.
     
 
 
@@ -1149,8 +1149,8 @@ I.saveScreenshot('debug.png', true) //resizes to available scrollHeight and scro
 
 #### Parameters
 
--   `fileName` [string][9] file name to save.
--   `fullPage` [boolean][17] (optional, `false` by default) flag to enable fullscreen screenshot mode.
+-   `fileName` [string][8] file name to save.
+-   `fullPage` [boolean][16] (optional, `false` by default) flag to enable fullscreen screenshot mode.
     
 
 
@@ -1188,9 +1188,9 @@ I.scrollTo('#submit', 5, 5);
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) located by CSS|XPath|strict locator.
--   `offsetX` [number][10] (optional, `0` by default) X-axis offset. 
--   `offsetY` [number][10] (optional, `0` by default) Y-axis offset.
+-   `locator` ([string][8] \| [object][6]) located by CSS|XPath|strict locator.
+-   `offsetX` [number][9] (optional, `0` by default) X-axis offset. 
+-   `offsetY` [number][9] (optional, `0` by default) Y-axis offset.
     
  
 
@@ -1207,8 +1207,8 @@ I.see('Register', {css: 'form.register'}); // use strict locator
 
 #### Parameters
 
--   `text` [string][9] expected on page.
--   `context` ([string][9] \| [object][7]) (optional, `null` by default) element located by CSS|Xpath|strict locator in which to search for text.
+-   `text` [string][8] expected on page.
+-   `context` ([string][8] \| [object][6]) (optional, `null` by default) element located by CSS|Xpath|strict locator in which to search for text.
     
 
 
@@ -1226,8 +1226,8 @@ I.seeAttributesOnElements('//form', { method: "post"});
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) located by CSS|XPath|strict locator.
--   `attributes` [object][7] attributes and their values to check.
+-   `locator` ([string][8] \| [object][6]) located by CSS|XPath|strict locator.
+-   `attributes` [object][6] attributes and their values to check.
     
 
     
@@ -1248,7 +1248,7 @@ I.seeCheckboxIsChecked({css: '#signup_form input[type=checkbox]'});
 
 #### Parameters
 
--   `field` ([string][9] \| [object][7]) located by label|name|CSS|XPath|strict locator.
+-   `field` ([string][8] \| [object][6]) located by label|name|CSS|XPath|strict locator.
     
 
 
@@ -1262,7 +1262,7 @@ I.seeCookie('Auth');
 
 #### Parameters
 
--   `name` [string][9] cookie name.
+-   `name` [string][8] cookie name.
     
 
 
@@ -1276,8 +1276,8 @@ I.seeCssPropertiesOnElements('h3', { 'font-weight': "bold"});
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) located by CSS|XPath|strict locator.
--   `cssProperties` [object][7] object with CSS properties and their values to check.
+-   `locator` ([string][8] \| [object][6]) located by CSS|XPath|strict locator.
+-   `cssProperties` [object][6] object with CSS properties and their values to check.
     
 
     
@@ -1299,7 +1299,7 @@ I.seeCurrentUrlEquals('http://my.site.com/register');
 
 #### Parameters
 
--   `url` [string][9] value to check.
+-   `url` [string][8] value to check.
     
 
 
@@ -1314,7 +1314,7 @@ I.seeElement('#modal');
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) located by CSS|XPath|strict locator.
+-   `locator` ([string][8] \| [object][6]) located by CSS|XPath|strict locator.
     
 
     
@@ -1334,7 +1334,7 @@ I.seeElementInDOM('#modal');
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) element located by CSS|XPath|strict locator.
+-   `locator` ([string][8] \| [object][6]) element located by CSS|XPath|strict locator.
     
 
 
@@ -1348,7 +1348,7 @@ I.seeInCurrentUrl('/register'); // we are on registration page
 
 #### Parameters
 
--   `url` [string][9] a fragment to check
+-   `url` [string][8] a fragment to check
     
 
 
@@ -1366,8 +1366,8 @@ I.seeInField('#searchform input','Search');
 
 #### Parameters
 
--   `field` ([string][9] \| [object][7]) located by label|name|CSS|XPath|strict locator.
--   `value` [string][9] value to check.
+-   `field` ([string][8] \| [object][6]) located by label|name|CSS|XPath|strict locator.
+-   `value` [string][8] value to check.
     
 
 
@@ -1390,7 +1390,7 @@ I.seeInSource('<h1>Green eggs &amp; ham</h1>');
 
 #### Parameters
 
--   `text` [string][9] value to check.
+-   `text` [string][8] value to check.
     
 
 
@@ -1404,7 +1404,7 @@ I.seeInTitle('Home Page');
 
 #### Parameters
 
--   `text` [string][9] text value to check.
+-   `text` [string][8] text value to check.
     
 
 
@@ -1419,8 +1419,8 @@ I.seeNumberOfElements('#submitBtn', 1);
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) element located by CSS|XPath|strict locator.
--   `num` [number][10] number of elements.
+-   `locator` ([string][8] \| [object][6]) element located by CSS|XPath|strict locator.
+-   `num` [number][9] number of elements.
     
 
 
@@ -1439,8 +1439,8 @@ I.seeNumberOfVisibleElements('.buttons', 3);
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) element located by CSS|XPath|strict locator.
--   `num` [number][10] number of elements.
+-   `locator` ([string][8] \| [object][6]) element located by CSS|XPath|strict locator.
+-   `num` [number][9] number of elements.
     
 
 
@@ -1496,8 +1496,8 @@ I.selectOption('Which OS do you use?', ['Android', 'iOS']);
 
 #### Parameters
 
--   `select` ([string][9] \| [object][7]) field located by label|name|CSS|XPath|strict locator.
--   `option` ([string][9] \| [array][16]) visible text or value of option.
+-   `select` ([string][8] \| [object][6]) field located by label|name|CSS|XPath|strict locator.
+-   `option` ([string][8] \| [array][15]) visible text or value of option.
     
 
 
@@ -1511,7 +1511,7 @@ I.setCookie({name: 'auth', value: true});
 
 #### Parameters
 
--   `cookie` [object][7] a cookie object.
+-   `cookie` [object][6] a cookie object.
     
 
 
@@ -1526,7 +1526,7 @@ I.switchTo(); // switch back to main page
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) (optional, `null` by default) element located by CSS|XPath|strict locator.
+-   `locator` ([string][8] \| [object][6]) (optional, `null` by default) element located by CSS|XPath|strict locator.
     
 
 
@@ -1541,7 +1541,7 @@ I.switchToNextTab(2);
 
 #### Parameters
 
--   `num` [number][10]  
+-   `num` [number][9]  
 
 ### switchToPreviousTab
 
@@ -1554,7 +1554,7 @@ I.switchToPreviousTab(2);
 
 #### Parameters
 
--   `num` [number][10]  
+-   `num` [number][9]  
 
 ### uncheckOption
 
@@ -1571,8 +1571,8 @@ I.uncheckOption('agree', '//form');
 
 #### Parameters
 
--   `field` ([string][9] \| [object][7]) checkbox located by label | name | CSS | XPath | strict locator.
--   `context` [string][9] (optional, `null` by default) element located by CSS | XPath | strict locator.
+-   `field` ([string][8] \| [object][6]) checkbox located by label | name | CSS | XPath | strict locator.
+-   `context` [string][8] (optional, `null` by default) element located by CSS | XPath | strict locator.
     
  
 
@@ -1586,7 +1586,7 @@ I.wait(2); // wait 2 secs
 
 #### Parameters
 
--   `sec` [number][10] number of second to wait.
+-   `sec` [number][9] number of second to wait.
     
 
 
@@ -1601,8 +1601,8 @@ I.waitForDetached('#popup');
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) element located by CSS|XPath|strict locator.
--   `sec` [number][10] (optional, `1` by default) time in seconds to wait
+-   `locator` ([string][8] \| [object][6]) element located by CSS|XPath|strict locator.
+-   `sec` [number][9] (optional, `1` by default) time in seconds to wait
     
 
 
@@ -1618,8 +1618,8 @@ I.waitForElement('.btn.continue', 5); // wait for 5 secs
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) element located by CSS|XPath|strict locator.
--   `sec` [number][10] (optional, `1` by default) time in seconds to wait
+-   `locator` ([string][8] \| [object][6]) element located by CSS|XPath|strict locator.
+-   `sec` [number][9] (optional, `1` by default) time in seconds to wait
     
 
     
@@ -1635,7 +1635,7 @@ Element can be located by CSS or XPath.
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) element located by CSS|XPath|strict locator.
+-   `locator` ([string][8] \| [object][6]) element located by CSS|XPath|strict locator.
 -   `sec`  (optional) time in seconds to wait, 1 by default.
     
 
@@ -1657,9 +1657,9 @@ I.waitForFunction((count) => window.requests == count, [3], 5) // pass args and
 
 #### Parameters
 
--   `fn` ([string][9] \| [function][12]) to be executed in browser context.
--   `argsOrSec` ([array][16] \| [number][10]) (optional, `1` by default) arguments for function or seconds. 
--   `sec` [number][10] (optional, `1` by default) time in seconds to wait
+-   `fn` ([string][8] \| [function][11]) to be executed in browser context.
+-   `argsOrSec` ([array][15] \| [number][9]) (optional, `1` by default) arguments for function or seconds. 
+-   `sec` [number][9] (optional, `1` by default) time in seconds to wait
     
  
 
@@ -1674,8 +1674,8 @@ I.waitForInvisible('#popup');
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) element located by CSS|XPath|strict locator.
--   `sec` [number][10] (optional, `1` by default) time in seconds to wait
+-   `locator` ([string][8] \| [object][6]) element located by CSS|XPath|strict locator.
+-   `sec` [number][9] (optional, `1` by default) time in seconds to wait
     
 
 
@@ -1700,8 +1700,8 @@ I.waitForRequest(request => request.url() === 'http://example.com' && request.me
 
 #### Parameters
 
--   `urlOrPredicate` ([string][9] \| [function][12]) 
--   `sec` [number][10]? seconds to wait 
+-   `urlOrPredicate` ([string][8] \| [function][11]) 
+-   `sec` [number][9]? seconds to wait 
 
 ### waitForResponse
 
@@ -1714,8 +1714,8 @@ I.waitForResponse(request => request.url() === 'http://example.com' && request.m
 
 #### Parameters
 
--   `urlOrPredicate` ([string][9] \| [function][12]) 
--   `sec` [number][10]? number of seconds to wait 
+-   `urlOrPredicate` ([string][8] \| [function][11]) 
+-   `sec` [number][9]? number of seconds to wait 
 
 ### waitForText
 
@@ -1730,9 +1730,9 @@ I.waitForText('Thank you, form has been submitted', 5, '#modal');
 
 #### Parameters
 
--   `text` [string][9] to wait for.
--   `sec` [number][10] (optional, `1` by default) time in seconds to wait 
--   `context` ([string][9] \| [object][7]) (optional) element located by CSS|XPath|strict locator.
+-   `text` [string][8] to wait for.
+-   `sec` [number][9] (optional, `1` by default) time in seconds to wait 
+-   `context` ([string][8] \| [object][6]) (optional) element located by CSS|XPath|strict locator.
     
  
 
@@ -1746,9 +1746,9 @@ I.waitForValue('//input', "GoodValue");
 
 #### Parameters
 
--   `field` ([string][9] \| [object][7]) input field.
--   `value` [string][9] expected value.
--   `sec` [number][10] (optional, `1` by default) time in seconds to wait
+-   `field` ([string][8] \| [object][6]) input field.
+-   `value` [string][8] expected value.
+-   `sec` [number][9] (optional, `1` by default) time in seconds to wait
     
 
 
@@ -1763,10 +1763,10 @@ I.waitForVisible('#popup');
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) element located by CSS|XPath|strict locator.
--   `sec` [number][10] (optional, `1` by default) time in seconds to wait
+-   `locator` ([string][8] \| [object][6]) element located by CSS|XPath|strict locator.
+-   `sec` [number][9] (optional, `1` by default) time in seconds to wait
     
-This method accepts [React selectors][18].
+This method accepts [React selectors][17].
 
 ### waitInUrl
 
@@ -1778,8 +1778,8 @@ I.waitInUrl('/info', 2);
 
 #### Parameters
 
--   `urlPart` [string][9] value to check.
--   `sec` [number][10] (optional, `1` by default) time in seconds to wait
+-   `urlPart` [string][8] value to check.
+-   `sec` [number][9] (optional, `1` by default) time in seconds to wait
     
  
 
@@ -1793,9 +1793,9 @@ I.waitNumberOfVisibleElements('a', 3);
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) element located by CSS|XPath|strict locator.
--   `num` [number][10] number of elements.
--   `sec` [number][10] (optional, `1` by default) time in seconds to wait
+-   `locator` ([string][8] \| [object][6]) element located by CSS|XPath|strict locator.
+-   `num` [number][9] number of elements.
+-   `sec` [number][9] (optional, `1` by default) time in seconds to wait
     
 
     
@@ -1815,8 +1815,8 @@ I.waitToHide('#popup');
 
 #### Parameters
 
--   `locator` ([string][9] \| [object][7]) element located by CSS|XPath|strict locator.
--   `sec` [number][10] (optional, `1` by default) time in seconds to wait
+-   `locator` ([string][8] \| [object][6]) element located by CSS|XPath|strict locator.
+-   `sec` [number][9] (optional, `1` by default) time in seconds to wait
     
 
 
@@ -1831,9 +1831,9 @@ I.waitUntil(() => window.requests == 0, 5);
 
 #### Parameters
 
--   `fn` ([function][12] \| [string][9]) function which is executed in browser context.
--   `sec` [number][10] (optional, `1` by default) time in seconds to wait 
--   `timeoutMsg` [string][9] message to show in case of timeout fail.
+-   `fn` ([function][11] \| [string][8]) function which is executed in browser context.
+-   `sec` [number][9] (optional, `1` by default) time in seconds to wait 
+-   `timeoutMsg` [string][8] message to show in case of timeout fail.
     
  
 
@@ -1848,8 +1848,8 @@ I.waitUrlEquals('http://127.0.0.1:8000/info');
 
 #### Parameters
 
--   `urlPart` [string][9] value to check.
--   `sec` [number][10] (optional, `1` by default) time in seconds to wait
+-   `urlPart` [string][8] value to check.
+-   `sec` [number][9] (optional, `1` by default) time in seconds to wait
     
  
 
@@ -1859,32 +1859,30 @@ I.waitUrlEquals('http://127.0.0.1:8000/info');
 
 [3]: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagewaitfornavigationoptions
 
-[4]: (https://codecept.io/helpers/Puppeteer-firefox)
+[4]: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions
 
-[5]: https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions
+[5]: https://chromedevtools.github.io/devtools-protocol/#how-do-i-access-the-browser-target
 
-[6]: https://chromedevtools.github.io/devtools-protocol/#how-do-i-access-the-browser-target
+[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
 
-[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
+[7]: http://jster.net/category/windows-modals-popups
 
-[8]: http://jster.net/category/windows-modals-popups
+[8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
 
-[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
+[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
 
-[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
+[10]: https://vuejs.org/v2/api/#Vue-nextTick
 
-[11]: https://vuejs.org/v2/api/#Vue-nextTick
+[11]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function
 
-[12]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function
+[12]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
 
-[13]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
+[13]: https://codecept.io/helpers/FileSystem
 
-[14]: https://codecept.io/helpers/FileSystem
+[14]: https://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/value
 
-[15]: https://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/value
+[15]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
 
-[16]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
+[16]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
 
-[17]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
-
-[18]: https://codecept.io/react
+[17]: https://codecept.io/react
diff --git a/docs/helpers/WebDriver.md b/docs/helpers/WebDriver.md
index d25c9072..8e25bb92 100644
--- a/docs/helpers/WebDriver.md
+++ b/docs/helpers/WebDriver.md
@@ -42,15 +42,15 @@ This helper should be configured in codecept.json or codecept.conf.js
 
 Example:
 
-```json
+```js
 {
-   "helpers": {
-     "WebDriver" : {
-       "smartWait": 5000,
-       "browser": "chrome",
-       "restart": false,
-       "windowSize": "maximize",
-       "timeouts": {
+   helpers: {
+     WebDriver : {
+       smartWait: 5000,
+       browser: "chrome",
+       restart: false,
+       windowSize: "maximize",
+       timeouts: {
          "script": 60000,
          "page load": 10000
        }
@@ -64,15 +64,15 @@ website][6].
 
 ### Headless Chrome
 
-```json
+```js
 {
-   "helpers": {
-     "WebDriver" : {
-       "url": "http://localhost",
-       "browser": "chrome",
-       "desiredCapabilities": {
-         "chromeOptions": {
-           "args": [ "--headless", "--disable-gpu", "--window-size=800,600" ]
+   helpers: {
+     WebDriver : {
+       url: "http://localhost",
+       browser: "chrome",
+       desiredCapabilities: {
+         chromeOptions: {
+           args: [ "--headless", "--disable-gpu", "--window-size=800,600" ]
          }
        }
      }
@@ -84,14 +84,14 @@ website][6].
 
 Additional configuration params can be used from [IE options][7]
 
-```json
+```js
 {
-   "helpers": {
-     "WebDriver" : {
-       "url": "http://localhost",
-       "browser": "internet explorer",
-       "desiredCapabilities": {
-         "ieOptions": {
+   helpers: {
+     WebDriver : {
+       url: "http://localhost",
+       browser: "internet explorer",
+       desiredCapabilities: {
+         ieOptions: {
            "ie.browserCommandLineSwitches": "-private",
            "ie.usePerProcessProxy": true,
            "ie.ensureCleanSession": true,
@@ -104,15 +104,18 @@ Additional configuration params can be used from [IE options][7]
 
 ### Selenoid Options
 
-```json
+[Selenoid][8] is a modern way to run Selenium inside Docker containers.
+Selenoid is easy to set up and provides more features than original Selenium Server. Use `selenoidOptions` to set Selenoid capabilities
+
+```js
 {
-   "helpers": {
-     "WebDriver" : {
-       "url": "http://localhost",
-       "browser": "chrome",
-       "desiredCapabilities": {
-         "selenoidOptions": {
-           "enableVNC": true,
+   helpers: {
+     WebDriver : {
+       url: "http://localhost",
+       browser: "chrome",
+       desiredCapabilities: {
+         selenoidOptions: {
+           enableVNC: true,
          }
        }
      }
@@ -120,17 +123,17 @@ Additional configuration params can be used from [IE options][7]
 }
 ```
 
-### Connect through proxy
+### Connect Through proxy
 
 CodeceptJS also provides flexible options when you want to execute tests to Selenium servers through proxy. You will
 need to update the `helpers.WebDriver.capabilities.proxy` key.
 
 ```js
 {
-    "helpers": {
-        "WebDriver": {
-            "capabilities": {
-                "proxy": {
+    helpers: {
+        WebDriver: {
+            capabilities: {
+                proxy: {
                     "proxyType": "manual|pac",
                     "proxyAutoconfigUrl": "URL TO PAC FILE",
                     "httpProxy": "PROXY SERVER",
@@ -151,10 +154,10 @@ For example,
 
 ```js
 {
-    "helpers": {
-        "WebDriver": {
-            "capabilities": {
-                "proxy": {
+    helpers: {
+        WebDriver: {
+            capabilities: {
+                proxy: {
                     "proxyType": "manual",
                     "httpProxy": "http://corporate.proxy:8080",
                     "socksUsername": "codeceptjs",
@@ -173,7 +176,7 @@ information.
 ### Cloud Providers
 
 WebDriver makes it possible to execute tests against services like `Sauce Labs` `BrowserStack` `TestingBot`
-Check out their documentation on [available parameters][8]
+Check out their documentation on [available parameters][9]
 
 Connecting to `BrowserStack` and `Sauce Labs` is simple. All you need to do
 is set the `user` and `key` parameters. WebDriver automatically know which
@@ -181,12 +184,12 @@ service provider to connect to.
 
 ```js
 {
-    "helpers":{
-        "WebDriver": {
-            "url": "YOUR_DESIRED_HOST",
-            "user": "YOUR_BROWSERSTACK_USER",
-            "key": "YOUR_BROWSERSTACK_KEY",
-            "capabilities": {
+    helpers:{
+        WebDriver: {
+            url: "YOUR_DESIRED_HOST",
+            user: "YOUR_BROWSERSTACK_USER",
+            key: "YOUR_BROWSERSTACK_KEY",
+            capabilities: {
                 "browserName": "chrome",
 
                 // only set this if you're using BrowserStackLocal to test a local domain
@@ -222,9 +225,9 @@ plugins: {
 }
 ```
 
-See [complete reference on webdriver.io][9].
+See [complete reference on webdriver.io][10].
 
-> Alternatively, use [codeceptjs-saucehelper][10] for better reporting.
+> Alternatively, use [codeceptjs-saucehelper][11] for better reporting.
 
 #### BrowserStack
 
@@ -248,13 +251,13 @@ plugins: {
 }
 ```
 
-See [complete reference on webdriver.io][11].
+See [complete reference on webdriver.io][12].
 
-> Alternatively, use [codeceptjs-bshelper][12] for better reporting.
+> Alternatively, use [codeceptjs-bshelper][13] for better reporting.
 
 #### TestingBot
 
-> Recommended: use official [TestingBot Helper][13].
+> Recommended: use official [TestingBot Helper][14].
 
 Alternatively, TestingBot can be configured via wdio service, which should be installed additionally:
 
@@ -276,25 +279,25 @@ plugins: {
 }
 ```
 
-See [complete reference on webdriver.io][14].
+See [complete reference on webdriver.io][15].
 
 #### Applitools
 
 Visual testing via Applitools service
 
-> Use [CodeceptJS Applitools Helper][15] with Applitools wdio service.
+> Use [CodeceptJS Applitools Helper][16] with Applitools wdio service.
 
 ### Multiremote Capabilities
 
 This is a work in progress but you can control two browsers at a time right out of the box.
 Individual control is something that is planned for a later version.
 
-Here is the [webdriverio docs][16] on the subject
+Here is the [webdriverio docs][17] on the subject
 
 ```js
 {
-    "helpers": {
-        "WebDriver": {
+    helpers: {
+        WebDriver: {
             "multiremote": {
                 "MyChrome": {
                     "desiredCapabilities": {
@@ -381,7 +384,7 @@ this.helpers['WebDriver']._locateFields('Your email').then // ...
 
 Accepts the active JavaScript native popup window, as created by window.alert|window.confirm|window.prompt.
 Don't confuse popups with modal windows, as created by [various
-libraries][17].
+libraries][18].
 
 ### amOnPage
 
@@ -396,7 +399,7 @@ I.amOnPage('/login'); // opens a login page
 
 #### Parameters
 
--   `url` [string][18] url path or global url.
+-   `url` [string][19] url path or global url.
     
 
 
@@ -411,8 +414,8 @@ I.appendField('#myTextField', 'appended');
 
 #### Parameters
 
--   `field` ([string][18] \| [object][19]) located by label|name|CSS|XPath|strict locator
--   `value` [string][18] text value to append.
+-   `field` ([string][19] \| [object][20]) located by label|name|CSS|XPath|strict locator
+-   `value` [string][19] text value to append.
     
 
     
@@ -434,8 +437,8 @@ I.attachFile('form input[name=avatar]', 'data/avatar.jpg');
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) field located by label|name|CSS|XPath|strict locator.
--   `pathToFile` [string][18] local file path relative to codecept.json config file.
+-   `locator` ([string][19] \| [object][20]) field located by label|name|CSS|XPath|strict locator.
+-   `pathToFile` [string][19] local file path relative to codecept.json config file.
     
 
     Appium: not tested
@@ -459,8 +462,8 @@ I.checkOption('agree', '//form');
 
 #### Parameters
 
--   `field` ([string][18] \| [object][19]) checkbox located by label | name | CSS | XPath | strict locator.
--   `context` [string][18] (optional, `null` by default) element located by CSS | XPath | strict locator.
+-   `field` ([string][19] \| [object][20]) checkbox located by label | name | CSS | XPath | strict locator.
+-   `context` [string][19] (optional, `null` by default) element located by CSS | XPath | strict locator.
     
 
     Appium: not tested 
@@ -477,7 +480,7 @@ I.clearCookie('test');
 
 #### Parameters
 
--   `cookie` [string][18] (optional, `null` by default) cookie name
+-   `cookie` [string][19] (optional, `null` by default) cookie name
     
 
 
@@ -494,7 +497,7 @@ I.clearField('#email');
 #### Parameters
 
 -   `field`  
--   `editable` ([string][18] \| [object][19]) field located by label|name|CSS|XPath|strict locator.
+-   `editable` ([string][19] \| [object][20]) field located by label|name|CSS|XPath|strict locator.
     
 
 
@@ -524,8 +527,8 @@ I.click({css: 'nav a.login'});
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) clickable link or button located by text, or any element located by CSS|XPath|strict locator.
--   `context` ([string][18] \| [object][19]) (optional, `null` by default) element to search in CSS|XPath|Strict locator.
+-   `locator` ([string][19] \| [object][20]) clickable link or button located by text, or any element located by CSS|XPath|strict locator.
+-   `context` ([string][19] \| [object][20]) (optional, `null` by default) element to search in CSS|XPath|Strict locator.
     
 
 
@@ -551,7 +554,7 @@ I.closeOtherTabs();
 
 ### defineTimeout
 
-Set [WebDriver timeouts][20] in realtime.
+Set [WebDriver timeouts][21] in realtime.
 
 Timeouts are expected to be passed as object:
 
@@ -576,8 +579,8 @@ I.dontSee('Login', '.nav'); // no login inside .nav element
 
 #### Parameters
 
--   `text` [string][18] which is not present.
--   `context` ([string][18] \| [object][19]) (optional) element located by CSS|XPath|strict locator in which to perfrom search.
+-   `text` [string][19] which is not present.
+-   `context` ([string][19] \| [object][20]) (optional) element located by CSS|XPath|strict locator in which to perfrom search.
 
 
 
@@ -596,7 +599,7 @@ I.dontSeeeCheckboxIsChedcked('agree'); // located by name
 
 #### Parameters
 
--   `field` ([string][18] \| [object][19]) located by label|name|CSS|XPath|strict locator.
+-   `field` ([string][19] \| [object][20]) located by label|name|CSS|XPath|strict locator.
     
 
     Appium: not tested
@@ -611,7 +614,7 @@ I.dontSeeCookie('auth'); // no auth cookie
 
 #### Parameters
 
--   `name` [string][18] cookie name.
+-   `name` [string][19] cookie name.
     
 
 
@@ -627,7 +630,7 @@ I.dontSeeCurrentUrlEquals('http://mysite.com/login'); // absolute urls are also
 
 #### Parameters
 
--   `url` [string][18] value to check.
+-   `url` [string][19] value to check.
     
 
 
@@ -641,7 +644,7 @@ I.dontSeeElement('.modal'); // modal is not shown
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) located by CSS|XPath|Strict locator.
+-   `locator` ([string][19] \| [object][20]) located by CSS|XPath|Strict locator.
     
 
     
@@ -660,7 +663,7 @@ I.dontSeeElementInDOM('.nav'); // checks that element is not on page visible or
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) located by CSS|XPath|Strict locator.
+-   `locator` ([string][19] \| [object][20]) located by CSS|XPath|Strict locator.
     
 
 
@@ -670,7 +673,7 @@ Checks that current url does not contain a provided fragment.
 
 #### Parameters
 
--   `url` [string][18] value to check.
+-   `url` [string][19] value to check.
     
 
 
@@ -686,8 +689,8 @@ I.dontSeeInField({ css: 'form input.email' }, 'user@user.com'); // field by CSS
 
 #### Parameters
 
--   `field` ([string][18] \| [object][19]) located by label|name|CSS|XPath|strict locator.
--   `value` [string][18] value to check.
+-   `field` ([string][19] \| [object][20]) located by label|name|CSS|XPath|strict locator.
+-   `value` [string][19] value to check.
     
 
 
@@ -702,7 +705,7 @@ I.dontSeeInSource('<!--'); // no comments in source
 #### Parameters
 
 -   `text`  
--   `value` [string][18] to check.
+-   `value` [string][19] to check.
     
 
 
@@ -716,7 +719,7 @@ I.dontSeeInTitle('Error');
 
 #### Parameters
 
--   `text` [string][18] value to check.
+-   `text` [string][19] value to check.
     
 
 
@@ -734,8 +737,8 @@ I.doubleClick('.btn.edit');
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) clickable link or button located by text, or any element located by CSS|XPath|strict locator.
--   `context` ([string][18] \| [object][19]) (optional, `null` by default) element to search in CSS|XPath|Strict locator.
+-   `locator` ([string][19] \| [object][20]) clickable link or button located by text, or any element located by CSS|XPath|strict locator.
+-   `context` ([string][19] \| [object][20]) (optional, `null` by default) element to search in CSS|XPath|Strict locator.
     
 
 
@@ -753,8 +756,8 @@ I.dragAndDrop('#dragHandle', '#container');
 
 #### Parameters
 
--   `srcElement` ([string][18] \| [object][19]) located by CSS|XPath|strict locator.
--   `destElement` ([string][18] \| [object][19]) located by CSS|XPath|strict locator.
+-   `srcElement` ([string][19] \| [object][20]) located by CSS|XPath|strict locator.
+-   `destElement` ([string][19] \| [object][20]) located by CSS|XPath|strict locator.
     
 
     Appium: not tested
@@ -771,8 +774,8 @@ I.dragSlider('#slider', -70);
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) located by label|name|CSS|XPath|strict locator.
--   `offsetX` [number][21] position to drag.
+-   `locator` ([string][19] \| [object][20]) located by label|name|CSS|XPath|strict locator.
+-   `offsetX` [number][22] position to drag.
     
  
 
@@ -781,7 +784,7 @@ I.dragSlider('#slider', -70);
 Executes async script on page.
 Provided function should execute a passed callback (as first argument) to signal it is finished.
 
-Example: In Vue.js to make components completely rendered we are waiting for [nextTick][22].
+Example: In Vue.js to make components completely rendered we are waiting for [nextTick][23].
 
 ```js
 I.executeAsyncScript(function(done) {
@@ -801,7 +804,7 @@ let val = await I.executeAsyncScript(function(url, done) {
 
 #### Parameters
 
--   `fn` ([string][18] \| [function][23]) function to be executed in browser context.
+-   `fn` ([string][19] \| [function][24]) function to be executed in browser context.
 
 ### executeScript
 
@@ -831,7 +834,7 @@ let date = await I.executeScript(function(el) {
 
 #### Parameters
 
--   `fn` ([string][18] \| [function][23]) function to be executed in browser context.
+-   `fn` ([string][19] \| [function][24]) function to be executed in browser context.
 
 ### fillField
 
@@ -851,8 +854,8 @@ I.fillField({css: 'form#login input[name=username]'}, 'John');
 
 #### Parameters
 
--   `field` ([string][18] \| [object][19]) located by label|name|CSS|XPath|strict locator.
--   `value` [string][18] text value to fill.
+-   `field` ([string][19] \| [object][20]) located by label|name|CSS|XPath|strict locator.
+-   `value` [string][19] text value to fill.
     
 
     
@@ -882,10 +885,10 @@ let hint = await I.grabAttributeFrom('#tooltip', 'title');
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) element located by CSS|XPath|strict locator.
--   `attr` [string][18] attribute name.
+-   `locator` ([string][19] \| [object][20]) element located by CSS|XPath|strict locator.
+-   `attr` [string][19] attribute name.
 
-Returns [Promise][24]&lt;[string][18]> attribute value
+Returns [Promise][25]&lt;[string][19]> attribute value
 
 
 Appium: can be used for apps only with several values ("contentDescription", "text", "className", "resourceId")
@@ -915,7 +918,7 @@ assert(cookie.value, '123456');
 
 -   `name`  cookie name. 
 
-Returns [Promise][24]&lt;[string][18]> attribute value
+Returns [Promise][25]&lt;[string][19]> attribute value
 
 
 
@@ -930,10 +933,10 @@ const value = await I.grabCssPropertyFrom('h3', 'font-weight');
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) element located by CSS|XPath|strict locator.
--   `cssProperty` [string][18] CSS property name.
+-   `locator` ([string][19] \| [object][20]) element located by CSS|XPath|strict locator.
+-   `cssProperty` [string][19] CSS property name.
 
-Returns [Promise][24]&lt;[string][18]> CSS value
+Returns [Promise][25]&lt;[string][19]> CSS value
 
 
 
@@ -947,7 +950,7 @@ let url = await I.grabCurrentUrl();
 console.log(`Current URL is [${url}]`);
 ```
 
-Returns [Promise][24]&lt;[string][18]> current URL
+Returns [Promise][25]&lt;[string][19]> current URL
 
 
 
@@ -974,7 +977,7 @@ let postHTML = await I.grabHTMLFrom('#post');
 
 -   `locator`  element located by CSS|XPath|strict locator.
 
-Returns [Promise][24]&lt;[string][18]> HTML code for an element
+Returns [Promise][25]&lt;[string][19]> HTML code for an element
 
 
 
@@ -986,7 +989,7 @@ Grab number of open tabs.
 let tabs = await I.grabNumberOfOpenTabs();
 ```
 
-Returns [Promise][24]&lt;[number][21]> number of open tabs
+Returns [Promise][25]&lt;[number][22]> number of open tabs
 
 
 
@@ -1000,9 +1003,9 @@ let numOfElements = await I.grabNumberOfVisibleElements('p');
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) located by CSS|XPath|strict locator.
+-   `locator` ([string][19] \| [object][20]) located by CSS|XPath|strict locator.
 
-Returns [Promise][24]&lt;[number][21]> number of visible elements
+Returns [Promise][25]&lt;[number][22]> number of visible elements
 
 
 
@@ -1015,7 +1018,7 @@ Resumes test execution, so should be used inside an async function with `await`
 let { x, y } = await I.grabPageScrollPosition();
 ```
 
-Returns [Promise][24]&lt;[object][19]> scroll position
+Returns [Promise][25]&lt;[object][20]> scroll position
 
 
 
@@ -1036,7 +1039,7 @@ Resumes test execution, so should be used inside an async function.
 let pageSource = await I.grabSource();
 ```
 
-Returns [Promise][24]&lt;[string][18]> source code
+Returns [Promise][25]&lt;[string][19]> source code
 
 
 
@@ -1055,7 +1058,7 @@ If multiple elements found returns an array of texts.
 
 -   `locator`  element located by CSS|XPath|strict locator.
 
-Returns [Promise][24]&lt;[string][18]> attribute value
+Returns [Promise][25]&lt;[string][19]> attribute value
 
 
 
@@ -1068,7 +1071,7 @@ Resumes test execution, so should be used inside async with `await` operator.
 let title = await I.grabTitle();
 ```
 
-Returns [Promise][24]&lt;[string][18]> title
+Returns [Promise][25]&lt;[string][19]> title
 
 
 
@@ -1083,9 +1086,9 @@ let email = await I.grabValueFrom('input[name=email]');
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) field located by label|name|CSS|XPath|strict locator.
+-   `locator` ([string][19] \| [object][20]) field located by label|name|CSS|XPath|strict locator.
 
-Returns [Promise][24]&lt;[string][18]> attribute value
+Returns [Promise][25]&lt;[string][19]> attribute value
 
 
 
@@ -1101,9 +1104,9 @@ I.moveCursorTo('#submit', 5,5);
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) located by CSS|XPath|strict locator.
--   `offsetX` [number][21] (optional, `0` by default) X-axis offset. 
--   `offsetY` [number][21] (optional, `0` by default) Y-axis offset.
+-   `locator` ([string][19] \| [object][20]) located by CSS|XPath|strict locator.
+-   `offsetX` [number][22] (optional, `0` by default) X-axis offset. 
+-   `offsetY` [number][22] (optional, `0` by default) Y-axis offset.
     
  
 
@@ -1118,7 +1121,7 @@ I.openNewTab();
 ### pressKey
 
 Presses a key on a focused element.
-Special keys like 'Enter', 'Control', [etc][25]
+Special keys like 'Enter', 'Control', [etc][26]
 will be replaced with corresponding unicode.
 If modifier key is used (Control, Command, Alt, Shift) in array, it will be released afterwards.
 
@@ -1129,7 +1132,7 @@ I.pressKey(['Control','a']);
 
 #### Parameters
 
--   `key` ([string][18] \| [array][26]) key or array of keys to press.
+-   `key` ([string][19] \| [array][27]) key or array of keys to press.
     
 
     
@@ -1189,8 +1192,8 @@ First parameter can be set to `maximize`.
 
 #### Parameters
 
--   `width` [number][21] width in pixels or `maximize`.
--   `height` [number][21] height in pixels.
+-   `width` [number][22] width in pixels or `maximize`.
+-   `height` [number][22] height in pixels.
     
 
     Appium: not tested in web, in apps doesn't work
@@ -1210,8 +1213,8 @@ I.rightClick('Click me', '.context');
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) clickable element located by CSS|XPath|strict locator.
--   `context` ([string][18] \| [object][19]) (optional, `null` by default) element located by CSS|XPath|strict locator.
+-   `locator` ([string][19] \| [object][20]) clickable element located by CSS|XPath|strict locator.
+-   `context` ([string][19] \| [object][20]) (optional, `null` by default) element located by CSS|XPath|strict locator.
     
 
 
@@ -1258,8 +1261,8 @@ I.saveScreenshot('debug.png', true) //resizes to available scrollHeight and scro
 
 #### Parameters
 
--   `fileName` [string][18] file name to save.
--   `fullPage` [boolean][27] (optional, `false` by default) flag to enable fullscreen screenshot mode.
+-   `fileName` [string][19] file name to save.
+-   `fullPage` [boolean][28] (optional, `false` by default) flag to enable fullscreen screenshot mode.
     
  
 
@@ -1297,9 +1300,9 @@ I.scrollTo('#submit', 5, 5);
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) located by CSS|XPath|strict locator.
--   `offsetX` [number][21] (optional, `0` by default) X-axis offset. 
--   `offsetY` [number][21] (optional, `0` by default) Y-axis offset.
+-   `locator` ([string][19] \| [object][20]) located by CSS|XPath|strict locator.
+-   `offsetX` [number][22] (optional, `0` by default) X-axis offset. 
+-   `offsetY` [number][22] (optional, `0` by default) Y-axis offset.
     
  
 
@@ -1316,8 +1319,8 @@ I.see('Register', {css: 'form.register'}); // use strict locator
 
 #### Parameters
 
--   `text` [string][18] expected on page.
--   `context` ([string][18] \| [object][19]) (optional, `null` by default) element located by CSS|Xpath|strict locator in which to search for text.
+-   `text` [string][19] expected on page.
+-   `context` ([string][19] \| [object][20]) (optional, `null` by default) element located by CSS|Xpath|strict locator in which to search for text.
     
 
 
@@ -1335,8 +1338,8 @@ I.seeAttributesOnElements('//form', { method: "post"});
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) located by CSS|XPath|strict locator.
--   `attributes` [object][19] attributes and their values to check.
+-   `locator` ([string][19] \| [object][20]) located by CSS|XPath|strict locator.
+-   `attributes` [object][20] attributes and their values to check.
     
 
 
@@ -1352,7 +1355,7 @@ I.seeCheckboxIsChecked({css: '#signup_form input[type=checkbox]'});
 
 #### Parameters
 
--   `field` ([string][18] \| [object][19]) located by label|name|CSS|XPath|strict locator.
+-   `field` ([string][19] \| [object][20]) located by label|name|CSS|XPath|strict locator.
     
 
     Appium: not tested
@@ -1367,7 +1370,7 @@ I.seeCookie('Auth');
 
 #### Parameters
 
--   `name` [string][18] cookie name.
+-   `name` [string][19] cookie name.
     
 
 
@@ -1381,8 +1384,8 @@ I.seeCssPropertiesOnElements('h3', { 'font-weight': "bold"});
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) located by CSS|XPath|strict locator.
--   `cssProperties` [object][19] object with CSS properties and their values to check.
+-   `locator` ([string][19] \| [object][20]) located by CSS|XPath|strict locator.
+-   `cssProperties` [object][20] object with CSS properties and their values to check.
     
 
 
@@ -1399,7 +1402,7 @@ I.seeCurrentUrlEquals('http://my.site.com/register');
 
 #### Parameters
 
--   `url` [string][18] value to check.
+-   `url` [string][19] value to check.
     
 
 
@@ -1414,7 +1417,7 @@ I.seeElement('#modal');
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) located by CSS|XPath|strict locator.
+-   `locator` ([string][19] \| [object][20]) located by CSS|XPath|strict locator.
     
 
     
@@ -1434,7 +1437,7 @@ I.seeElementInDOM('#modal');
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) element located by CSS|XPath|strict locator.
+-   `locator` ([string][19] \| [object][20]) element located by CSS|XPath|strict locator.
     
 
 
@@ -1448,7 +1451,7 @@ I.seeInCurrentUrl('/register'); // we are on registration page
 
 #### Parameters
 
--   `url` [string][18] a fragment to check
+-   `url` [string][19] a fragment to check
     
 
 
@@ -1466,8 +1469,8 @@ I.seeInField('#searchform input','Search');
 
 #### Parameters
 
--   `field` ([string][18] \| [object][19]) located by label|name|CSS|XPath|strict locator.
--   `value` [string][18] value to check.
+-   `field` ([string][19] \| [object][20]) located by label|name|CSS|XPath|strict locator.
+-   `value` [string][19] value to check.
     
 
 
@@ -1490,7 +1493,7 @@ I.seeInSource('<h1>Green eggs &amp; ham</h1>');
 
 #### Parameters
 
--   `text` [string][18] value to check.
+-   `text` [string][19] value to check.
     
 
 
@@ -1504,7 +1507,7 @@ I.seeInTitle('Home Page');
 
 #### Parameters
 
--   `text` [string][18] text value to check.
+-   `text` [string][19] text value to check.
     
 
 
@@ -1519,8 +1522,8 @@ I.seeNumberOfElements('#submitBtn', 1);
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) element located by CSS|XPath|strict locator.
--   `num` [number][21] number of elements.
+-   `locator` ([string][19] \| [object][20]) element located by CSS|XPath|strict locator.
+-   `num` [number][22] number of elements.
     
 
     
@@ -1540,8 +1543,8 @@ I.seeNumberOfVisibleElements('.buttons', 3);
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) element located by CSS|XPath|strict locator.
--   `num` [number][21] number of elements.
+-   `locator` ([string][19] \| [object][20]) element located by CSS|XPath|strict locator.
+-   `num` [number][22] number of elements.
     
 
     
@@ -1598,8 +1601,8 @@ I.selectOption('Which OS do you use?', ['Android', 'iOS']);
 
 #### Parameters
 
--   `select` ([string][18] \| [object][19]) field located by label|name|CSS|XPath|strict locator.
--   `option` ([string][18] \| [array][26]) visible text or value of option.
+-   `select` ([string][19] \| [object][20]) field located by label|name|CSS|XPath|strict locator.
+-   `option` ([string][19] \| [array][27]) visible text or value of option.
     
 
 
@@ -1613,10 +1616,10 @@ I.setCookie({name: 'auth', value: true});
 
 #### Parameters
 
--   `cookie` [object][19] a cookie object.
+-   `cookie` [object][20] a cookie object.
     
 Uses Selenium's JSON [cookie
-    format][28].
+    format][29].
 
 ### switchTo
 
@@ -1629,7 +1632,7 @@ I.switchTo(); // switch back to main page
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) (optional, `null` by default) element located by CSS|XPath|strict locator.
+-   `locator` ([string][19] \| [object][20]) (optional, `null` by default) element located by CSS|XPath|strict locator.
     
 
 
@@ -1694,8 +1697,8 @@ I.uncheckOption('agree', '//form');
 
 #### Parameters
 
--   `field` ([string][18] \| [object][19]) checkbox located by label | name | CSS | XPath | strict locator.
--   `context` [string][18] (optional, `null` by default) element located by CSS | XPath | strict locator.
+-   `field` ([string][19] \| [object][20]) checkbox located by label | name | CSS | XPath | strict locator.
+-   `context` [string][19] (optional, `null` by default) element located by CSS | XPath | strict locator.
     
 
     Appium: not tested 
@@ -1710,7 +1713,7 @@ I.wait(2); // wait 2 secs
 
 #### Parameters
 
--   `sec` [number][21] number of second to wait.
+-   `sec` [number][22] number of second to wait.
     
 
 
@@ -1725,8 +1728,8 @@ I.waitForDetached('#popup');
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) element located by CSS|XPath|strict locator.
--   `sec` [number][21] (optional, `1` by default) time in seconds to wait
+-   `locator` ([string][19] \| [object][20]) element located by CSS|XPath|strict locator.
+-   `sec` [number][22] (optional, `1` by default) time in seconds to wait
     
  
 
@@ -1742,8 +1745,8 @@ I.waitForElement('.btn.continue', 5); // wait for 5 secs
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) element located by CSS|XPath|strict locator.
--   `sec` [number][21] (optional, `1` by default) time in seconds to wait
+-   `locator` ([string][19] \| [object][20]) element located by CSS|XPath|strict locator.
+-   `sec` [number][22] (optional, `1` by default) time in seconds to wait
     
  
 
@@ -1754,7 +1757,7 @@ Element can be located by CSS or XPath.
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) element located by CSS|XPath|strict locator.
+-   `locator` ([string][19] \| [object][20]) element located by CSS|XPath|strict locator.
 -   `sec`  (optional) time in seconds to wait, 1 by default.
     
 
@@ -1776,9 +1779,9 @@ I.waitForFunction((count) => window.requests == count, [3], 5) // pass args and
 
 #### Parameters
 
--   `fn` ([string][18] \| [function][23]) to be executed in browser context.
--   `argsOrSec` ([array][26] \| [number][21]) (optional, `1` by default) arguments for function or seconds. 
--   `sec` [number][21] (optional, `1` by default) time in seconds to wait
+-   `fn` ([string][19] \| [function][24]) to be executed in browser context.
+-   `argsOrSec` ([array][27] \| [number][22]) (optional, `1` by default) arguments for function or seconds. 
+-   `sec` [number][22] (optional, `1` by default) time in seconds to wait
     
  
 
@@ -1793,8 +1796,8 @@ I.waitForInvisible('#popup');
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) element located by CSS|XPath|strict locator.
--   `sec` [number][21] (optional, `1` by default) time in seconds to wait
+-   `locator` ([string][19] \| [object][20]) element located by CSS|XPath|strict locator.
+-   `sec` [number][22] (optional, `1` by default) time in seconds to wait
     
  
 
@@ -1811,9 +1814,9 @@ I.waitForText('Thank you, form has been submitted', 5, '#modal');
 
 #### Parameters
 
--   `text` [string][18] to wait for.
--   `sec` [number][21] (optional, `1` by default) time in seconds to wait 
--   `context` ([string][18] \| [object][19]) (optional) element located by CSS|XPath|strict locator.
+-   `text` [string][19] to wait for.
+-   `sec` [number][22] (optional, `1` by default) time in seconds to wait 
+-   `context` ([string][19] \| [object][20]) (optional) element located by CSS|XPath|strict locator.
     
  
 
@@ -1827,9 +1830,9 @@ I.waitForValue('//input', "GoodValue");
 
 #### Parameters
 
--   `field` ([string][18] \| [object][19]) input field.
--   `value` [string][18] expected value.
--   `sec` [number][21] (optional, `1` by default) time in seconds to wait
+-   `field` ([string][19] \| [object][20]) input field.
+-   `value` [string][19] expected value.
+-   `sec` [number][22] (optional, `1` by default) time in seconds to wait
     
  
 
@@ -1844,8 +1847,8 @@ I.waitForVisible('#popup');
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) element located by CSS|XPath|strict locator.
--   `sec` [number][21] (optional, `1` by default) time in seconds to wait
+-   `locator` ([string][19] \| [object][20]) element located by CSS|XPath|strict locator.
+-   `sec` [number][22] (optional, `1` by default) time in seconds to wait
     
  
 
@@ -1859,8 +1862,8 @@ I.waitInUrl('/info', 2);
 
 #### Parameters
 
--   `urlPart` [string][18] value to check.
--   `sec` [number][21] (optional, `1` by default) time in seconds to wait
+-   `urlPart` [string][19] value to check.
+-   `sec` [number][22] (optional, `1` by default) time in seconds to wait
     
  
 
@@ -1874,9 +1877,9 @@ I.waitNumberOfVisibleElements('a', 3);
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) element located by CSS|XPath|strict locator.
--   `num` [number][21] number of elements.
--   `sec` [number][21] (optional, `1` by default) time in seconds to wait
+-   `locator` ([string][19] \| [object][20]) element located by CSS|XPath|strict locator.
+-   `num` [number][22] number of elements.
+-   `sec` [number][22] (optional, `1` by default) time in seconds to wait
     
  
 
@@ -1891,8 +1894,8 @@ I.waitToHide('#popup');
 
 #### Parameters
 
--   `locator` ([string][18] \| [object][19]) element located by CSS|XPath|strict locator.
--   `sec` [number][21] (optional, `1` by default) time in seconds to wait
+-   `locator` ([string][19] \| [object][20]) element located by CSS|XPath|strict locator.
+-   `sec` [number][22] (optional, `1` by default) time in seconds to wait
     
  
 
@@ -1907,9 +1910,9 @@ I.waitUntil(() => window.requests == 0, 5);
 
 #### Parameters
 
--   `fn` ([function][23] \| [string][18]) function which is executed in browser context.
--   `sec` [number][21] (optional, `1` by default) time in seconds to wait 
--   `timeoutMsg` [string][18] message to show in case of timeout fail.
+-   `fn` ([function][24] \| [string][19]) function which is executed in browser context.
+-   `sec` [number][22] (optional, `1` by default) time in seconds to wait 
+-   `timeoutMsg` [string][19] message to show in case of timeout fail.
     
  
 -   `interval`   
@@ -1925,8 +1928,8 @@ I.waitUrlEquals('http://127.0.0.1:8000/info');
 
 #### Parameters
 
--   `urlPart` [string][18] value to check.
--   `sec` [number][21] (optional, `1` by default) time in seconds to wait
+-   `urlPart` [string][19] value to check.
+-   `sec` [number][22] (optional, `1` by default) time in seconds to wait
     
  
 
@@ -1944,44 +1947,46 @@ I.waitUrlEquals('http://127.0.0.1:8000/info');
 
 [7]: https://seleniumhq.github.io/selenium/docs/api/rb/Selenium/WebDriver/IE/Options.html
 
-[8]: http://webdriver.io/guide/usage/cloudservices.html
+[8]: https://aerokube.com/selenoid/latest/
+
+[9]: http://webdriver.io/guide/usage/cloudservices.html
 
-[9]: https://webdriver.io/docs/sauce-service.html
+[10]: https://webdriver.io/docs/sauce-service.html
 
-[10]: https://github.com/puneet0191/codeceptjs-saucehelper/
+[11]: https://github.com/puneet0191/codeceptjs-saucehelper/
 
-[11]: https://webdriver.io/docs/browserstack-service.html
+[12]: https://webdriver.io/docs/browserstack-service.html
 
-[12]: https://github.com/PeterNgTr/codeceptjs-bshelper
+[13]: https://github.com/PeterNgTr/codeceptjs-bshelper
 
-[13]: https://github.com/testingbot/codeceptjs-tbhelper
+[14]: https://github.com/testingbot/codeceptjs-tbhelper
 
-[14]: https://webdriver.io/docs/testingbot-service.html
+[15]: https://webdriver.io/docs/testingbot-service.html
 
-[15]: https://github.com/PeterNgTr/codeceptjs-applitoolshelper
+[16]: https://github.com/PeterNgTr/codeceptjs-applitoolshelper
 
-[16]: http://webdriver.io/guide/usage/multiremote.html
+[17]: http://webdriver.io/guide/usage/multiremote.html
 
-[17]: http://jster.net/category/windows-modals-popups
+[18]: http://jster.net/category/windows-modals-popups
 
-[18]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
+[19]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
 
-[19]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
+[20]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
 
-[20]: https://webdriver.io/docs/timeouts.html
+[21]: https://webdriver.io/docs/timeouts.html
 
-[21]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
+[22]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
 
-[22]: https://vuejs.org/v2/api/#Vue-nextTick
+[23]: https://vuejs.org/v2/api/#Vue-nextTick
 
-[23]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function
+[24]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function
 
-[24]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
+[25]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
 
-[25]: https://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/value
+[26]: https://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/element/:id/value
 
-[26]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
+[27]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
 
-[27]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
+[28]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
 
-[28]: https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object
+[29]: https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object
diff --git a/docs/mobile.md b/docs/mobile.md
index af2769ef..ede8b53f 100644
--- a/docs/mobile.md
+++ b/docs/mobile.md
@@ -173,6 +173,8 @@ In both Android and iPhone elements are defined in XML format and can be searche
 I.seeElement('//android.widget.ScrollView/android.widget.LinearLayout')'
 ```
 
+> Despite showing XPath in this guide we **do not recommend using XPath for testing iOS native apps. XPath runs very slow on iOS. Consider using ID or Accessibility ID locators instead.
+
 CSS locators are not supported in native mobile apps, you need to switch to web context to use them.
 
 Elements can also be located by their accessability id, available both at Android and iOS.
@@ -181,7 +183,9 @@ Accessibility id is recommended to use for locating element, as it rarely change
 * iOS uses [UIAccessibilityIdentification](https://developer.apple.com/documentation/uikit/uiaccessibilityidentification)
 * Android `accessibility id` matches the content-description
 * Web view uses `[aria-label]` attribute as accessibility id
-* ReactNative for Android has some caveats you could find more [here](mobile-react-native-locators.md)
+* For [React Native for Android see our special guide](mobile-react-native-locators.md).
+
+> If you test React Native application, consider using [Detox helper](/detox) for faster tests.
 
 Add `~` prefix to search for element by its accessibility id:
 
diff --git a/docs/puppeteer.md b/docs/puppeteer.md
index b89ec492..961bae45 100644
--- a/docs/puppeteer.md
+++ b/docs/puppeteer.md
@@ -36,7 +36,7 @@ Or see [alternative installation options](http://codecept.io/installation/)
 And a basic project initialized
 
 ```sh
-codeceptjs init
+npx codeceptjs init
 ```
 
 You will be asked for a Helper to use, you should select Puppeteer and provide url of a website you are testing.
@@ -84,7 +84,7 @@ It will wait for a small amount of time (100ms) by default after each user actio
 CodeceptJS test should be created with `gt` command:
 
 ```sh
-codeceptjs gt
+npx codeceptjs gt
 ```
 
 As an example we will use `ToDoMvc` app for testing.
@@ -102,7 +102,7 @@ Tests consist with a scenario of user's action taken on a page. The most widely
 * `see`, `dontSee` - to check for a text on a page
 * `seeElement`, `dontSeeElement` - to check for elements on a page
 
-*All actions are listed in [helper reference](http://codecept.io/helpers/Puppeteer/).*
+> ℹ  All actions are listed in [Puppeteer helper reference](http://codecept.io/helpers/Puppeteer/).*
 
 All actions which interact with elements **support CSS and XPath locators**. Actions like `click` or `fillField` by locate elements by their name or value on a page:
 
@@ -184,7 +184,7 @@ Puppeteer has a very [rich and flexible API](https://github.com/GoogleChrome/pup
 Start with creating an `MyPuppeteer` helper using `generate:helper` or `gh` command:
 
 ```sh
-codeceptjs gh
+npx codeceptjs gh
 ```
 
 Then inside a Helper you can access `Puppeteer` helper of CodeceptJS.
diff --git a/docs/quickstart.md b/docs/quickstart.md
index da51720d..431036fc 100644
--- a/docs/quickstart.md
+++ b/docs/quickstart.md
@@ -4,18 +4,42 @@ title: Quickstart
 ---
 
 **NodeJS v 8.9** and higher required to start.
-CodeceptJS is multi-backend testing framework. It can execute tests using different libraries like webdriverio, Puppeteer, Protractor, etc.
+CodeceptJS is an end 2 end testing framework which supports multiple browser drivers like WebDriver, Puppeteer, Protractor, TestCafe etc.
 
-* In this guide we will use [Google Chrome **Puppeteer**](https://github.com/GoogleChrome/puppeteer) as a driver for browsers. This allows us to start in a minutes with no extra tools installed.
-* If you are familiar with Selenium, you can choose classical [**Selenium WebDriver** setup](#using-selenium-webdriver).
-* Also, look at [complete installation reference](https://codecept.io/installation/).
+> **⬇️ TLDR [Fastest and simplest setup with Puppeteer and CodeceptJS](#using-puppeteer) ⬇️**
 
+How to choose the right driver for your web application?
+Here is a brief comparison of all tools you can use with CodeceptJS.
+
+
+| Driver  | Cross-Browser | Limitations | Headless | Selenuim | Speed |
+|---|---|---|--|--|--|
+| WebDriver  | ✔️ | headers, downloads | ⁉ | ✔️ | normal |
+| Puppeteer  | chrome | cross-browser support | ✔️ | | fast |
+| Protractor | ✔️ | headers, downloads | ⁉ | ✔️ | normal |
+| TestCafe | ✔️  | multiple tabs | ✔️ | | fast |
+| Nightmare | electron (chromium) | multiple tabs, cross-browser  | ✔️ | | fast |
+
+⁉ - headless mode requires additional tools and configuration.
+
+#### How to choose browser driver
+
+* **[Choose Puppeteer](#Using-Puppeteer)** for simplest setup, fast tests, full browser control. Limited to Chrome and Firefox only. Cloud browsers via browserless.io.
+* **[Choose WebDriver](#Using-WebDriver)** or Protractor for classical Selenium. Rich ecosystem and cross browser support with cloud browsers via Sauce Labs, BrowserStack, TestingBot. **Selenium server requried** for local start.
+* **Choose TestCafe** for cheap and fast cross-browser tests. Has stability and feature limitation comparing to WebDriver.
+
+Each driver has its own pros and cons which can't be described in this paragraph. However, in CodeceptJS it is easy to switch between them. In most cases you just need to update a config to run tests differently.
 
 ## Using Puppeteer
 
 
 <video onclick="this.paused ? this.play() : this.pause();" src="/img/codeceptjs-install.mp4" style="width: 100%" controls></video>
 
+0) If you start an empty project, initialize npm first:
+
+```
+npm init -y
+```
 
 1) Install CodeceptJS with Puppeteer
 
@@ -102,17 +126,24 @@ Puppeteer starts a browser without showing its window. To see the browser, edit
 
 Rerun the test to see the browser.
 
-> Next: [CodeceptJS with Puppeteer >>>](https://codecept.io/puppeteer/)
+> [▶ Next: CodeceptJS Basics](https://codecept.io/basics/)
+
+> [▶ Next: CodeceptJS with Puppeteer](https://codecept.io/puppeteer/)
 
-> Next: [CodeceptJS Basics >>>](https://codecept.io/basics/)
 
-> Next: [Demo Project](https://github.com/DavertMik/codeceptjs-todomvc-puppeteer)
 
 
 ---
 
 ## Using Selenium WebDriver
 
+0) If you start an empty project, initialize npm first:
+
+```
+npm init -y
+```
+
+
 1) Install CodeceptJS with webdriverio library
 
 ```
@@ -209,19 +240,19 @@ My First Test --
 ```
 
 
-> Next: [CodeceptJS Basics >>>](https://codecept.io/basics/)
+> [▶ Next: CodeceptJS Basics](https://codecept.io/basics/)
 
-> Next: [Acceptance Testing in CodeceptJS >>>](https://codecept.io/acceptance/)
+> [▶ Next: WebDriver Testing](https://codecept.io/webdriver/)
 
 
 ## Using Protractor
 
-> [**Follow corresponding guide >>**](https://codecept.io/angular/)
+> [▶ Follow corresponding guide](https://codecept.io/angular/)
 
 ## Using Appium
 
-> [**Follow corresponding guide >>**](https://codecept.io/mobile/)
+> [▶ Follow corresponding guide](https://codecept.io/mobile/)
 
 ## Using NightmareJS
 
-> [**Follow corresponding guide >>**](https://codecept.io/nightmare/)
+> [▶ Follow corresponding guide](https://codecept.io/nightmare/)
diff --git a/docs/webdriver.md b/docs/webdriver.md
new file mode 100644
index 00000000..6f5ae8e1
--- /dev/null
+++ b/docs/webdriver.md
@@ -0,0 +1,428 @@
+---
+id: webdriver
+title: Testing with WebDriver
+---
+
+How does your client, manager, or tester, or any other non-technical person, know your web application is working? By opening the browser, accessing a site, clicking on links, filling in the forms, and actually seeing the content on a web page.
+
+End to End tests can cover standard but complex scenarios from a user's perspective. With e2e tests you can be confident that users, following all defined scenarios, won't get errors. We check **functionality of application and a user interface** (UI) as well.
+
+## What is Selenium WebDriver
+
+The standard and proved way to run browser test automation over years is Selenium WebDriver. Over years this technology was standartized and works over all popular browsers and operating systems. There are cloud services like SauceLabs or BrowserStack which allow executing such browsers in the clooud. The superset of WebDriver protocol is also used to test [native and hybrid mobile applications](https://codecept.io/mobile).
+
+Let's clarify the terms:
+
+* Selenium - is a toolset for browser test automation
+* WebDriver - a standard protocol for communicating between test framework and browsers
+* JSON Wire - an older version of such protocol
+
+We use [webdriverio](https://webdriver.io) library to run tests over WebDriver.
+
+> Popular tool [Protractor](https://codecept.io/angular) also uses WebDriver for running end 2 end tests.
+
+To proceed you need to have [CodeceptJS installed](https://codecept.io/quickstart#using-selenium-webdriver) and `WebDriver` helper selected.
+
+Selenium WebDriver may be complicated from start, as it requires following tools to be installed and started.
+
+1. Selenium Server - to execute and send commands to browser
+2. ChromeDriver or GeckoDriver - to allow browsers to run in automated mode.
+
+> Those tools can be easily installed via NPM. Use [selenium-standalone](https://www.npmjs.com/package/selenium-standalone) to automatically install them.
+
+You can also use `@wdio/selenium-standalone-service` package, to install and start Selenium Server for your tests automatically.
+
+```
+npm i @wdio/selenium-standalone-service --save-dev
+```
+
+Enable it in config inside plugins section:
+
+```js
+exports.config = {
+  // ...
+  // inside condecept.conf.js
+  plugins: {
+    wdio: {
+      enabled: true,
+      services: ['selenium-standalone']
+    }
+  }
+}
+```
+
+## Configuring WebDriver
+
+WebDriver can be configured to run browser tests in window, headlessly, on a remote server or in a cloud.
+
+> By default CodeceptJS is already configured to run WebDriver tests locally with Chrome or Firefox. If you just need to start running tests - proceed to the next chapter.
+
+Configuration for WebDriver should be provided inside `codecept.conf.js` file under `helpers: WebDriver` section:
+
+```js
+  helpers: {
+    WebDriver: {
+      url: 'https://myapp.com',
+      browser: 'chrome',
+      host: '127.0.0.1',
+      port: 4444,
+      restart: false,
+      windowSize: '1920x1680',
+      desiredCapabilities: {
+        chromeOptions: {
+          args: [ /*"--headless",*/ "--disable-gpu", "--window-size=1200,1000", "--no-sandbox" ]
+        }
+      }
+    },
+  }
+```
+
+WebDriver protocol works over HTTP, so you need to have a Selenium Server to be running or other service that will launch a browser for you. That's why you may need to specify `host`, `port`, `protocol`, and `path` parameters.
+
+By default, those parameters are set to connect to local Selenium Server but they should be changed if you want to run tests via [Cloud Services](https://codecept.io/helpers/WebDriver#cloud-providers) (Sauce Labs, etc) or inside Docker container.
+
+If you want to run tests using raw ChromeDriver (which also supports WebDriver protocol) avoiding Selenium Server, you should provide following configuration:
+
+```js
+port: 9515,
+browser: 'chrome',
+path: '/',
+```
+
+> If you face issues connecting to WebDriver, please check that corresponding server is running on a specified port. If host is other than `localhost` or port is other than `4444`, update the configuration.
+
+Additional parameters for a specific browser can be set via `desiredCapabilities` options. For instance, this is how we can set to **run headless Chrome**:
+
+```js
+desiredCapabilities: {
+  chromeOptions: {
+    args: [ "--headless", "--disable-gpu", "--window-size=1200,1000", "--no-sandbox" ]
+  }
+}
+```
+
+Next popular use case for capabilities is configuring what to do with unhandled alert popups.
+
+```js
+desiredCapabilities: {
+  // close all unexpected popups
+  unexpectedAlertBehaviour: 'dismiss',
+}
+```
+
+There are also [browser and platform specific capabilities](https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities). Services like SauceLabs, BrowserStack or browser vendors can provide their own specific capabilities for more tuning.
+
+There are also options specific to CodeceptJS. By default CodeceptJS runs tests in the same browser window but clears cookies and local storage after each test. This behavior can be changed with these options:
+
+```js
+// change to true to restart browser between tests
+restart: false,
+// don't change browser state and not clear cookies between tests
+keepBrowserState: true,
+keepCookies: true,
+```
+
+> ▶ More config options available on [WebDriver helper reference](https://codecept.io/helpers/WebDriver#configuration)
+
+### Configuring WebDriver for CI
+
+To develop tests it's fine to use local Selenium Server and window mode. Setting up WebDriver on remote CI (Continous Integration) server is different. If there is no desktop and no window mode on CI.
+
+There are following options available:
+
+* Use headless Chrome or Firefox (see configuration above).
+* Use [Selenoid](https://codecept.io/helpers/WebDriver#selenoid-options) to run browsers inside Docker containers.
+* Use paid [cloud services (SauceLabs, BrowserStack, TestingBot)](https://codecept.io/helpers/WebDriver#cloud-providers).
+
+## Writing Tests
+
+CodeceptJS provides high-level API on top of WebDriver protocol. While most standard implementations focus on dealing with WebElements on page, CodeceptJS is about user scenarios and interactions. That's why you don't have a direct access to web elements inside a test, but it is proved that in majority of cases you don't need it. Tests written from user's perspective are simpler to write, understand, log and debug.
+
+> If you come from Java, Python or Ruby don't be afraid of a new syntax. It is more flexible than you think!
+
+A typical test case may look like this:
+
+```js
+Feature('login');
+
+Scenario('login test', (I) => {
+  I.amOnPage('/login');
+  I.fillField('Username', 'john');
+  I.fillField('Password', '123456');
+  I.click('Login');
+  I.see('Welcome, John');
+});
+```
+> ▶ Actions like `amOnPage`, `click`, `fillField` are not limited to WebDriver only. They work similarly for all available helpers. [Go to Basics guide to learn them](https://codecept.io/basics#writing-tests).
+
+
+An empty test case can be created with `codeceptjs gt` command.
+
+```
+npx codeceptjs gt
+```
+
+It's easy to start writing a test if you use [interactive pause](https://codecept.io/basics#debug). Just open a web page and pause execution.
+
+```js
+Feature('Sample Test');
+
+Scenario('open my website', (I) => {
+  I.amOnPage('/');
+  pause();
+});
+```
+
+This is just enough to run a test, open a browser, and think what to do next to write a test case.
+
+Let's execute a test.
+
+When you execute such test with `codeceptjs run` command you may see the browser is started
+
+```
+npx codeceptjs run --steps
+```
+
+After a page is opened a full control of a browser is given to a terminal. Type in different commands such as `click`, `see`, `fillField` to write the test. A successful commands will be saved to `./output/cli-history` file and can be copied into a test.
+
+> ℹ  All actions are listed in [WebDriver helper reference](https://codecept.io/helpers/WebDriver).
+
+An interactive shell output may look like this:
+
+```
+ Interactive shell started
+ Use JavaScript syntax to try steps in action
+ - Press ENTER to run the next step
+ - Press TAB twice to see all available commands
+ - Type exit + Enter to exit the interactive shell
+ I.fillField('.new-todo', 'Write a test')
+ I.pressKey('Enter')
+ I.
+ Commands have been saved to /home/davert/demos/codeceptjs/output/cli-history
+```
+After typing in successful commands you can copy them into a test.
+
+Here is a test checking basic [todo application](http://todomvc.com/).
+
+```js
+Feature('TodoMVC');
+
+Scenario('create todo item', (I) => {
+  I.amOnPage('/examples/vue/');
+  I.waitForElement('.new-todo');
+  I.fillField('.new-todo', 'Write a test')
+  I.pressKey('Enter');
+  I.see('1 item left', '.todo-count');
+});
+```
+
+> [▶ Working example of CodeceptJS WebDriver tests](https://github.com/DavertMik/codeceptjs-webdriver-example) for TodoMVC application.
+
+## Waiting
+
+Web applications do not always respond instantly. That's why WebDriver protocol has methods to wait for changes on a page. CodeceptJS provides such commands prefixed with `wait*` so you could explicitly define what effects we wait for.
+
+Most popular "waiters" are:
+
+* `waitForText` - wait for text to appear on a page
+* `waitForElement` - wait for element to appear on a page
+* `waitForInvisible` - wait element to become invisible.
+
+By default, they will wait for 1 second. This number can be changed in WebDriver configuration:
+
+```js
+// inside codecept.conf.js
+exports.config = {
+  helpers: {
+    WebDriver: {
+      // WebDriver config goes here
+      // wait for 5 seconds
+      waitForTimeout: 5000
+    }
+  }
+}
+```
+
+## SmartWait
+
+It is possible to wait for elements pragmatically. If a test uses element which is not on a page yet, CodeceptJS will wait for few extra seconds before failing. This feature is based on [Implicit Wait](http://www.seleniumhq.org/docs/04_webdriver_advanced.jsp#implicit-waits) of Selenium. CodeceptJS enables implicit wait only when searching for a specific element and disables in all other cases. Thus, the performance of a test is not affected.
+
+SmartWait can be enabled by setting wait option in WebDriver config.
+Add `smartWait: 5000` to wait for additional 5s.
+
+```js
+// inside codecept.conf.js
+exports.config = {
+  helpers: {
+    WebDriver: {
+      // WebDriver config goes here
+      // smart wait for 5 seconds
+      smartWait: 5000
+    }
+  }
+}
+```
+
+SmartWait works with a CSS/XPath locators in `click`, `seeElement` and other methods. See where it is enabled and where is not:
+
+```js
+I.click('Login'); // DISABLED, not a locator
+I.fillField('user', 'davert'); // DISABLED, not a specific locator
+I.fillField({name: 'password'}, '123456'); // ENABLED, strict locator
+I.click('#login'); // ENABLED, locator is CSS ID
+I.see('Hello, Davert'); // DISABLED, Not a locator
+I.seeElement('#userbar'); // ENABLED
+I.dontSeeElement('#login'); // DISABLED, can't wait for element to hide
+I.seeNumberOfElements('button.link', 5); // DISABLED, can wait only for one element
+
+```
+
+SmartWait doesn't check element for visibility, so tests may fail even element is on a page.
+
+Usage example:
+
+```js
+// we use smartWait: 5000 instead of
+// I.waitForElement('#click-me', 5);
+// to wait for element on page
+I.click('#click-me');
+```
+
+If it's hard to define what to wait, it is recommended to use [retries](https://codecept.io/basics/#retries) to rerun flaky steps.
+
+## Auto Login
+
+To share the same user session across different tests CodeceptJS provides [autoLogin plugin](https://codecept.io/plugins#autologin). It simplifies login management and reduces time consuming login operations. Instead of filling in login form before each test it saves the cookies of a valid user session and reuses it for next tests. If a session expires or doesn't exist, logs in a user again.
+
+This plugin requires some configuration but is very simple in use:
+
+```js
+Scenario('do something with logged in user', (I, login)) => {
+  login('user');
+  I.see('Dashboard','h1');
+});
+```
+
+With `autoLogin` plugin you can save cookies into a file and reuse same session on different runs.
+
+> [▶ How to set up autoLogin plugin](https://codecept.io/plugins#autologin)
+
+
+## Multiple Windows
+
+CodeceptJS allows to use several browser windows inside a test. Sometimes we are testing the functionality of websites that we cannot control, such as a closed-source managed package, and there are popups that either remain open for configuring data on the screen, or close as a result of clicking a window. We can use these functions in order to gain more control over which page is being tested with Codecept at any given time. For example:
+
+```js
+const assert = require('assert');
+
+Scenario('should open main page of configured site, open a popup, switch to main page, then switch to popup, close popup, and go back to main page', async (I) => {
+    I.amOnPage('/');
+    const handleBeforePopup = await I.grabCurrentWindowHandle();
+    const urlBeforePopup = await I.grabCurrentUrl();
+    const allHandlesBeforePopup = await I.grabAllWindowHandles();
+    assert.equal(allHandlesBeforePopup.length, 1, 'Single Window');
+
+    await I.executeScript(() => {
+        window.open('https://www.w3schools.com/', 'new window', 'toolbar=yes,scrollbars=yes,resizable=yes,width=400,height=400');
+    });
+
+    const allHandlesAfterPopup = await I.grabAllWindowHandles();
+    assert.equal(allHandlesAfterPopup.length, 2, 'Two Windows');
+
+    await I.switchToWindow(allHandlesAfterPopup[1]);
+    const urlAfterPopup = await I.grabCurrentUrl();
+    assert.equal(urlAfterPopup, 'https://www.w3schools.com/', 'Expected URL: Popup');
+
+    assert.equal(handleBeforePopup, allHandlesAfterPopup[0], 'Expected Window: Main Window');
+    await I.switchToWindow(handleBeforePopup);
+    const currentURL = await I.grabCurrentUrl();
+    assert.equal(currentURL, urlBeforePopup, 'Expected URL: Main URL');
+
+    await I.switchToWindow(allHandlesAfterPopup[1]);
+    const urlAfterSwitchBack = await I.grabCurrentUrl();
+    assert.equal(urlAfterSwitchBack, 'https://www.w3schools.com/', 'Expected URL: Popup');
+    await I.closeCurrentTab();
+
+    const allHandlesAfterPopupClosed = await I.grabAllWindowHandles();
+    assert.equal(allHandlesAfterPopupClosed.length, 1, 'Single Window');
+    const currentWindowHandle = await I.grabCurrentWindowHandle();
+    assert.equal(currentWindowHandle, allHandlesAfterPopup[0], 'Expected Window: Main Window');
+
+});
+```
+
+## Extending WebDriver
+
+CodeceptJS doesn't aim to embrace all possible functionality of WebDriver. At some points you may find that some actions do not exist, however it is easy to add one. You will need to use WebDriver API from [webdriver.io](https://webdriver.io) library.
+
+To create new actions which will be added into `I.` object you need to create a new helper. This can be done with `codeceptjs gh` command.
+
+```
+npx codeceptjs gh
+```
+
+Name a new helper "Web". Now each method of a created class can be added to I object. Be sure to enable this helper in config:
+
+```js
+exports.config = {
+  helpers: {
+    WebDriver: { /* WebDriver config goes here */ },
+    WebHelper: {
+      // load custom helper
+      require: './web_helper.js'
+    }
+  }
+}
+```
+
+> ℹ  See [Custom Helper](https://codecept.io/helpers) guide to see more examples.
+
+While implementing custom actions using WebDriver API please note that, there is two versions of protocol: WebDriver and JSON Wire. Depending on a browser version one of those protocols can be used. We can't know for sure which protocol is going to used, so we will need to implement an action using both APIs.
+
+```js
+const Helper = codeceptjs.helper;
+
+class Web extends Helper {
+
+  // method to drag an item to coordinates
+  async dragToPoint(el, x, y) {
+    // access browser object from WebDriver
+    const browser = this.helpers.WebDriver.browser;
+    await this.helpers.WebDriver.moveCursorTo(el);
+
+    if (browser.isW3C) {
+      // we use WebDriver protocol
+      return browser.performActions([
+        {"type": "pointerDown", "button": 0},
+        {"type": "pointerMove", "origin": "pointer", "duration": 1000, x, y },
+        {"type": "pointerUp", "button": 0}
+      ]);
+    }
+
+    // we use JSON Wire protocol
+    await browser.buttonDown(0);
+    await browser.moveToElement(null, x, y);
+    await browser.buttonUp(0);
+  }
+
+  // method which restarts browser
+  async restartBrowser() {
+    const browser = this.helpers.WebDriver.browser;
+    await browser.reloadSession();
+    await browser.maximizeWindow();
+  }
+
+  // method which goes to previous page
+  async backToPreviousPage() {
+    const browser = this.helpers.WebDriver.browser;
+    await browser.back();
+  }
+}
+```
+
+When a helper is created, regenerate your step definitions, so you could see those actions when using [intellisense](https://codecept.io/basics#intellisense):
+
+```
+npx codeceptjs def
+```
+
diff --git a/lib/command/definitions.js b/lib/command/definitions.js
index e519a216..72db5e58 100644
--- a/lib/command/definitions.js
+++ b/lib/command/definitions.js
@@ -9,7 +9,7 @@ const fs = require('fs');
 const path = require('path');
 
 const template = `
-type ICodeceptCallback = (i: CodeceptJS.{{I}}{{callbackParams}}) => void;
+type ICodeceptCallback = (i?: CodeceptJS.{{I}}, current?:any{{callbackParams}}) => void;
 
 declare class FeatureConfig {
   retry(times: number): FeatureConfig
@@ -41,6 +41,134 @@ interface ILocator {
 
 type LocatorOrString = string | ILocator | Locator;
 
+declare class Container {
+  create(config: Object, opts: Object): void
+  plugins(name?: string): Object
+  support(name?: string): Object
+  helpers(name?: string): Object
+  translation(): Object
+  mocha(): Object
+  append(newContainer: Object): void
+  clear(newHelpers: Object, newSupport: Object, newPlugins: Object): void
+}
+
+declare class RecorderSession {
+  running: boolean
+    start(name: string): void
+    restore(name: string): void
+    catch(fn: CallableFunction): void
+}
+
+declare class Recorder {
+  retries: Object[]
+  start(): void
+  isRunning(): boolean
+  startUnlessRunning(): void
+  errHandler(fn: CallableFunction): void
+  reset(): void
+  session: RecorderSession
+  add(taskName, fn?: CallableFunction, force?: boolean, retry?: boolean): Promise<any>
+  retry(opts: Object): Promise<any>
+  catch(customErrFn: CallableFunction): Promise<any>
+  catchWithoutStop(customErrFn: CallableFunction ): Promise<any>
+  throw(err: Error): Promise<any>
+  saveFirstAsyncError(err: Error): void
+  getAsyncErr(): Promise<Error>
+  cleanAsyncErr(): void
+  stop():void
+  promise(): Promise<any>
+  scheduled(): string[]
+  toString(): string
+  add(hookName: string, fn: CallableFunction, force?: boolean): void
+  catch(customErrFn: CallableFunction)
+}
+
+declare class CodeceptJSEvent {
+  dispatcher: EventEmitter
+  test: {
+    started: string
+    before: string
+    after: string
+    passed: string
+    failed: string
+    finished: string
+  }
+  suite: {
+    before: string,
+    after: string,
+  }
+  hook: {
+    started: string,
+    passed: string,
+  }
+  step: {
+    before: string,
+    after: string,
+    started: string,
+    passed: string,
+    failed: string,
+    finished: string,
+  }
+  all: {
+    before: string,
+    after: string,
+    result: string,
+  }
+  multiple: {
+    before: string,
+    after: string,
+  }
+  emit(event: string, param: string)
+  cleanDispatcher(): void
+}
+
+declare class Output {
+  colors: any
+  styles: {
+    error: any,
+    success: any,
+    scenario: any,
+    basic: any,
+    debug: any,
+    log: any,
+  }
+
+  print(msg: string): void
+  stepShift: number
+  level(level: number): number
+  process(process: string): string
+  debug(msg: string): void
+  log(msg: string): void
+  error(msg: string): void
+  success(msg: string): void
+  plugin(name: string, msg: string): void
+  step(step: any): void
+  suite: {
+    started: Function
+  }
+  test: {
+    started(test: string): void
+    passed(test: string): void
+    failed(test: string): void
+    skipped(test: string): void
+  }
+  scenario: {
+    started(test: string): void
+    passed(test: string): void
+    failed(test: string): void
+  }
+  say(message: string, color?: string): void
+  result(passed: number, failed: number, skipped: number, duration: string): void
+}
+
+declare class Config {
+  create(newConfig: Object): Object
+  load(configFile: string): Config
+  get(key: string, val: any): any
+  append(additionalConfig: Object): Object
+  reset(): Object
+}
+
 declare class Helper {
   /** Abstract method to provide required config options */
   static _config(): any;
@@ -129,7 +257,9 @@ declare function BeforeSuite(callback: ICodeceptCallback): void;
 declare function After(callback: ICodeceptCallback): void;
 declare function AfterSuite(callback: ICodeceptCallback): void;
 
-declare function inject(): { [key: string]: any };
+declare function inject(): {
+{{injectPageObjects}}
+};
 declare function locate(selector: LocatorOrString): Locator;
 declare function within(selector: LocatorOrString, callback: Function): Promise<any>;
 declare function session(selector: LocatorOrString, callback: Function): Promise<any>;
@@ -140,6 +270,12 @@ declare function secret(secret: any): string;
 declare const codeceptjs: any;
 
 declare namespace CodeceptJS {
+  export const container: Container
+  export const recorder: Recorder
+  export const event: CodeceptJSEvent
+  export const output: Output
+  export const config: Config
+
   export interface {{I}} {
 {{methods}}
   }
@@ -151,6 +287,8 @@ declare module "codeceptjs" {
 }
 `;
 
+const injectSupportTemplate = '  {{name}}: CodeceptJS.{{name}}';
+
 const pageObjectTemplate = `
   export interface {{name}} {
 {{methods}}
@@ -181,19 +319,26 @@ module.exports = function (genPath, options) {
 
   const supports = container.support(); // return all support objects
   const exportPageObjects = [];
+  const injectPageObjects = [];
   const callbackParams = [];
-  for (const name in supports) {
-    if (name === 'I') continue;
-    callbackParams.push(`${name}:CodeceptJS.${name}`);
+  // See #1795 and #1799, there's no obvious way to use for-in with Proxies
+  Reflect.ownKeys(supports).forEach((name) => {
+    if (name === 'I') {
+      injectPageObjects.push(injectSupportTemplate.replace(/{{name}}/g, String(name)));
+      return;
+    }
+    callbackParams.push(`${String(name)}?:CodeceptJS.${String(name)}`);
     const pageObject = supports[name];
     const pageMethods = addAllMethodsInObject(pageObject, {}, []);
     let pageObjectExport = pageObjectTemplate.replace('{{methods}}', pageMethods.join(''));
-    pageObjectExport = pageObjectExport.replace('{{name}}', name);
+    pageObjectExport = pageObjectExport.replace('{{name}}', String(name));
+    injectPageObjects.push(injectSupportTemplate.replace(/{{name}}/g, String(name)));
     exportPageObjects.push(pageObjectExport);
-  }
+  });
 
   let definitionsTemplate = template.replace('{{methods}}', methods.join(''));
   definitionsTemplate = definitionsTemplate.replace('{{exportPageObjects}}', exportPageObjects.join('\n'));
+  definitionsTemplate = definitionsTemplate.replace('{{injectPageObjects}}', injectPageObjects.join('\n'));
   definitionsTemplate = definitionsTemplate.replace('{{callbackParams}}', `, ${[...callbackParams, '...args: any'].join(', ')}`);
   if (translations) {
     definitionsTemplate = definitionsTemplate.replace(/\{\{I\}\}/g, translations.I);
diff --git a/lib/command/gherkin/snippets.js b/lib/command/gherkin/snippets.js
index 1520c86e..91a58543 100644
--- a/lib/command/gherkin/snippets.js
+++ b/lib/command/gherkin/snippets.js
@@ -8,6 +8,7 @@ const { Parser } = require('gherkin');
 const glob = require('glob');
 const fsPath = require('path');
 const fs = require('fs');
+const escapeStringRegexp = require('escape-string-regexp');
 
 const parser = new Parser();
 parser.stopAtFirstError = false;
@@ -62,11 +63,21 @@ module.exports = function (genPath, options) {
       try {
         matchStep(step.text);
       } catch (err) {
-        let stepLine = step.text
-          .replace(/\"(.*?)\"/g, '{string}')
-          .replace(/(\d+\.\d+)/, '{float}')
-          .replace(/ (\d+) /, ' {int} ');
-        stepLine = Object.assign(stepLine, { type: step.keyword.trim(), location: step.location });
+        let stepLine;
+        if (/[{}()/]/.test(step.text)) {
+          stepLine = escapeStringRegexp(step.text)
+            .replace(/\//g, '\\/')
+            .replace(/\"(.*?)\"/g, '"(.*?)"')
+            .replace(/(\d+\\\.\d+)/, '(\\d+\\.\\d+)')
+            .replace(/ (\d+) /, ' (\\d+) ');
+          stepLine = Object.assign(stepLine, { type: step.keyword.trim(), location: step.location, regexp: true });
+        } else {
+          stepLine = step.text
+            .replace(/\"(.*?)\"/g, '{string}')
+            .replace(/(\d+\.\d+)/, '{float}')
+            .replace(/ (\d+) /, ' {int} ');
+          stepLine = Object.assign(stepLine, { type: step.keyword.trim(), location: step.location, regexp: false });
+        }
         newSteps.push(stepLine);
       }
     }
@@ -99,7 +110,7 @@ module.exports = function (genPath, options) {
     .filter((value, index, self) => self.indexOf(value) === index)
     .map((step) => {
       return `
-${step.type}('${step}', () => {
+${step.type}(${step.regexp ? '/^' : "'"}${step}${step.regexp ? '$/' : "'"}, () => {
   // From "${step.file}" ${JSON.stringify(step.location)}
   throw new Error('Not implemented yet');
 });`;
diff --git a/lib/command/init.js b/lib/command/init.js
index e1579eae..4029bf7d 100644
--- a/lib/command/init.js
+++ b/lib/command/init.js
@@ -4,7 +4,8 @@ const fs = require('fs');
 const path = require('path');
 const { fileExists, beautify } = require('../utils');
 const inquirer = require('inquirer');
-const getTestRoot = require('./utils').getTestRoot;
+const { getTestRoot } = require('./utils');
+const generateDefinitions = require('./definitions');
 const isLocal = require('../utils').installedLocally();
 const mkdirp = require('mkdirp');
 const { inspect } = require('util');
@@ -158,42 +159,46 @@ module.exports = function (initPath) {
         }
         fs.writeFileSync(stepFile, defaultActor);
         config.include.I = result.steps_file;
-        success(`Steps file created at ${stepFile}`);
-      }
-
-      const tsconfig = {
-        compilerOption: {
-          allowJs: true,
-        },
-      };
-      const tsconfigJson = beautify(JSON.stringify(tsconfig));
-      const tsconfigFile = path.join(testsPath, 'tsconfig.json');
-      if (fileExists(tsconfigFile)) {
-        print(`tsconfig.json has already exists at ${tsconfigFile}`);
-      } else {
-        fs.writeFileSync(tsconfigFile, tsconfigJson);
-        success(`TypeScript project configuration file created at ${tsconfigFile}`);
+        print(`Steps file created at ${stepFile}`);
       }
 
       fs.writeFileSync(configFile, beautify(`exports.config = ${inspect(config, false, 4, false)}`), 'utf-8');
-      success(`Config created at ${configFile}`);
+      print(`Config created at ${configFile}`);
 
       if (config.output) {
         if (!fileExists(config.output)) {
           mkdirp.sync(path.join(testsPath, config.output));
-          success(`Directory for temporary output files created at '${config.output}'`);
+          print(`Directory for temporary output files created at '${config.output}'`);
         } else {
           print(`Directory for temporary output files is already created at '${config.output}'`);
         }
       }
+
+
+      const jsconfig = {
+        compilerOption: {
+          allowJs: true,
+        },
+      };
+      const jsconfigJson = beautify(JSON.stringify(jsconfig));
+      const jsconfigFile = path.join(testsPath, 'jsconfig.json');
+      if (fileExists(jsconfigFile)) {
+        print(`jsconfig.json has already exists at ${jsconfigFile}`);
+      } else {
+        fs.writeFileSync(jsconfigFile, jsconfigJson);
+        print(`Intellisense enabled in ${jsconfigFile}`);
+      }
+
+      generateDefinitions(testsPath, {});
+
       success('Almost done! Create your first test by executing `npx codeceptjs gt` (generate test) command');
 
       if (packages) {
         print('\n--');
         if (isLocal) {
-          print(`Please install dependent packages locally: ${colors.bold(`npm install --save-dev ${packages.join(' ')}`)}`);
+          success(`Please install dependent packages locally: ${colors.bold(`npm install --save-dev ${packages.join(' ')}`)}`);
         } else {
-          print(`Please install dependent packages globally: [sudo] ${colors.bold(`npm install -g ${packages.join(' ')}`)}`);
+          success(`Please install dependent packages globally: [sudo] ${colors.bold(`npm install -g ${packages.join(' ')}`)}`);
         }
       }
     };
diff --git a/lib/command/utils.js b/lib/command/utils.js
index c9cc65fa..ae488772 100644
--- a/lib/command/utils.js
+++ b/lib/command/utils.js
@@ -11,7 +11,7 @@ module.exports.getConfig = function (configFile) {
   try {
     return require('../config').load(configFile);
   } catch (err) {
-    fail(err.message);
+    fail(err.stack);
   }
 };
 
diff --git a/lib/container.js b/lib/container.js
index a0f85cb6..3473a379 100644
--- a/lib/container.js
+++ b/lib/container.js
@@ -162,6 +162,12 @@ function createHelpers(config) {
 
 function createSupportObjects(config) {
   const objects = container.support = new Proxy({}, {
+    has(target, key) {
+      return key in config;
+    },
+    ownKeys() {
+      return Reflect.ownKeys(config);
+    },
     get(target, key) {
       // configured but not in support object, yet: load the module
       if (key in config && !(key in target)) target[key] = lazyLoad(key);
diff --git a/lib/data/dataScenarioConfig.js b/lib/data/dataScenarioConfig.js
index 2ecf335a..9cb9173f 100644
--- a/lib/data/dataScenarioConfig.js
+++ b/lib/data/dataScenarioConfig.js
@@ -61,6 +61,24 @@ class DataScenarioConfig {
     this.scenarios.forEach(scenario => scenario.tag(tagName));
     return this;
   }
+
+  /**
+     * Pass in additional objects to inject into test
+     * @param {*} obj
+     */
+  inject(obj) {
+    this.scenarios.forEach(scenario => scenario.inject(obj));
+    return this;
+  }
+
+  /**
+     * Dynamically injects dependencies, see https://codecept.io/pageobjects/#dynamic-injection
+     * @param {*} dependencies
+     */
+  injectDependencies(dependencies) {
+    this.scenarios.forEach(scenario => scenario.injectDependencies(dependencies));
+    return this;
+  }
 }
 
 module.exports = DataScenarioConfig;
diff --git a/lib/helper/Polly.js b/lib/helper/Polly.js
index 859f2f8c..fca77809 100644
--- a/lib/helper/Polly.js
+++ b/lib/helper/Polly.js
@@ -58,7 +58,7 @@ class Polly extends Helper {
 
   // Start mocking network requests/responses
   async _startMocking(title = 'Test') {
-    if (!this.helpers && !this.helpers.Puppeteer) {
+    if (!(this.helpers && this.helpers.Puppeteer)) {
       throw new Error('Puppeteer is the only supported helper right now');
     }
     await this._connectPuppeteer(title);
@@ -67,20 +67,21 @@ class Polly extends Helper {
   // Connect Puppeteer helper to mock future requests.
   async _connectPuppeteer(title) {
     const adapter = require('@pollyjs/adapter-puppeteer');
-
     PollyJS.register(adapter);
+
     const { page } = this.helpers.Puppeteer;
+    if (!page) {
+      throw new Error('Looks like, there is no open tab');
+    }
     await page.setRequestInterception(true);
 
     this.polly = new PollyJS(title, {
+      mode: 'passthrough',
       adapters: ['puppeteer'],
       adapterOptions: {
         puppeteer: { page },
       },
     });
-
-    // By default let pass through all network requests
-    if (this.polly) this.polly.server.any().passthrough();
   }
 
   /**
@@ -100,10 +101,12 @@ class Polly extends Helper {
    */
   async mockRequest(method, oneOrMoreUrls, dataOrStatusCode) {
     await this._checkAndStartMocking();
+    const puppeteerConfigUrl = this.helpers.Puppeteer && this.helpers.Puppeteer.options.url;
+
     const handler = this._getRouteHandler(
       method,
       oneOrMoreUrls,
-      this.options.url,
+      this.options.url || puppeteerConfigUrl,
     );
 
     if (typeof dataOrStatusCode === 'number') {
@@ -148,16 +151,18 @@ class Polly extends Helper {
    */
   async stopMocking() {
     if (!this._checkIfMockingStarted()) return;
-
     await this._disconnectPuppeteer();
-    await this.polly.flush();
-    await this.polly.stop();
-    this.polly = undefined;
+
+    const { polly } = this;
+    if (!polly) return;
+    await polly.flush();
+    await polly.stop();
+    delete this.polly;
   }
 
   async _disconnectPuppeteer() {
     const { page } = this.helpers.Puppeteer;
-    await page.setRequestInterception(false);
+    if (page) await page.setRequestInterception(false);
   }
 }
 
diff --git a/lib/helper/Puppeteer.js b/lib/helper/Puppeteer.js
index b6436ea8..a2d3e529 100644
--- a/lib/helper/Puppeteer.js
+++ b/lib/helper/Puppeteer.js
@@ -57,12 +57,13 @@ const consoleLogStore = new Console();
  * * `keepCookies`: (optional, default: false) - keep cookies between tests when `restart` is set to false.
  * * `waitForAction`: (optional) how long to wait after click, doubleClick or PressKey actions in ms. Default: 100.
  * * `waitForNavigation`: (optional, default: 'load'). When to consider navigation succeeded. Possible options: `load`, `domcontentloaded`, `networkidle0`, `networkidle2`. See [Puppeteer API](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagewaitfornavigationoptions). Array values are accepted as well.
+ * * `pressKeyDelay`: (optional, default: '10'). Delay between key presses in ms. Used when calling Puppeteers page.type(...) in fillField/appendField
  * * `getPageTimeout` (optional, default: '0') config option to set maximum navigation time in milliseconds.
  * * `waitForTimeout`: (optional) default wait* timeout in ms. Default: 1000.
  * * `windowSize`: (optional) default window size. Set a dimension like `640x480`.
  * * `userAgent`: (optional) user-agent string.
  * * `manualStart`: (optional, default: false) - do not start browser before a test, start it manually inside a helper with `this.helpers["Puppeteer"]._startBrowser()`.
- * * `browser`: (optional, default: chrome) - can be changed to `firefox` when using [puppeteer-firefox]((https://codecept.io/helpers/Puppeteer-firefox)).
+ * * `browser`: (optional, default: chrome) - can be changed to `firefox` when using [puppeteer-firefox](https://codecept.io/helpers/Puppeteer-firefox).
  * * `chrome`: (optional) pass additional [Puppeteer run options](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions).
  *
  *
@@ -159,6 +160,7 @@ class Puppeteer extends Helper {
       browser: 'chrome',
       waitForAction: 100,
       waitForTimeout: 1000,
+      pressKeyDelay: 10,
       fullPageScreenshots: false,
       disableScreenshots: false,
       uniqueScreenshotNames: false,
@@ -599,7 +601,7 @@ class Puppeteer extends Helper {
    * {{> scrollPageToTop }}
    */
   scrollPageToTop() {
-    return this.page.evaluate(() => {
+    return this.executeScript(() => {
       window.scrollTo(0, 0);
     });
   }
@@ -608,7 +610,7 @@ class Puppeteer extends Helper {
    * {{> scrollPageToBottom }}
    */
   scrollPageToBottom() {
-    return this.page.evaluate(() => {
+    return this.executeScript(() => {
       const body = document.body;
       const html = document.documentElement;
       window.scrollTo(0, Math.max(
@@ -627,20 +629,16 @@ class Puppeteer extends Helper {
       offsetX = locator;
       locator = null;
     }
-    let x = 0;
-    let y = 0;
+
     if (locator) {
       const els = await this._locate(locator);
       assertElementExists(els, locator, 'Element');
       await els[0]._scrollIntoViewIfNeeded();
       const elementCoordinates = await els[0]._clickablePoint();
-      x = elementCoordinates.x;
-      y = elementCoordinates.y;
+      await this.executeScript((x, y) => window.scrollBy(x, y), elementCoordinates.x + offsetX, elementCoordinates.y + offsetY);
+    } else {
+      await this.executeScript((x, y) => window.scrollTo(x, y), offsetX, offsetY);
     }
-
-    await this.page.evaluate((x, y) => {
-      window.scrollTo(x, y);
-    }, x + offsetX, y + offsetY);
     return this._waitForAction();
   }
 
@@ -1086,7 +1084,7 @@ class Puppeteer extends Helper {
     } else if (editable) {
       await this._evaluateHandeInContext(el => el.innerHTML = '', el);
     }
-    await el.type(value.toString(), { delay: 10 });
+    await el.type(value.toString(), { delay: this.options.pressKeyDelay });
     return this._waitForAction();
   }
 
@@ -1107,7 +1105,7 @@ class Puppeteer extends Helper {
     const els = await findFields.call(this, field);
     assertElementExists(els, field, 'Field');
     await els[0].press('End');
-    await els[0].type(value, { delay: 10 });
+    await els[0].type(value, { delay: this.options.pressKeyDelay });
     return this._waitForAction();
   }
 
diff --git a/lib/helper/WebDriver.js b/lib/helper/WebDriver.js
index 90c2d317..6413c069 100644
--- a/lib/helper/WebDriver.js
+++ b/lib/helper/WebDriver.js
@@ -15,7 +15,6 @@ const {
   chunkArray,
   convertCssPropertiesToCamelCase,
   screenshotOutputFolder,
-  fileToBase64Zip,
 } = require('../utils');
 const {
   isColorProperty,
@@ -61,15 +60,15 @@ const webRoot = 'body';
  *
  * Example:
  *
- * ```json
+ * ```js
  * {
- *    "helpers": {
- *      "WebDriver" : {
- *        "smartWait": 5000,
- *        "browser": "chrome",
- *        "restart": false,
- *        "windowSize": "maximize",
- *        "timeouts": {
+ *    helpers: {
+ *      WebDriver : {
+ *        smartWait: 5000,
+ *        browser: "chrome",
+ *        restart: false,
+ *        windowSize: "maximize",
+ *        timeouts: {
  *          "script": 60000,
  *          "page load": 10000
  *        }
@@ -83,15 +82,15 @@ const webRoot = 'body';
  *
  * ### Headless Chrome
  *
- * ```json
+ * ```js
  * {
- *    "helpers": {
- *      "WebDriver" : {
- *        "url": "http://localhost",
- *        "browser": "chrome",
- *        "desiredCapabilities": {
- *          "chromeOptions": {
- *            "args": [ "--headless", "--disable-gpu", "--window-size=800,600" ]
+ *    helpers: {
+ *      WebDriver : {
+ *        url: "http://localhost",
+ *        browser: "chrome",
+ *        desiredCapabilities: {
+ *          chromeOptions: {
+ *            args: [ "--headless", "--disable-gpu", "--window-size=800,600" ]
  *          }
  *        }
  *      }
@@ -103,14 +102,14 @@ const webRoot = 'body';
  *
  * Additional configuration params can be used from [IE options](https://seleniumhq.github.io/selenium/docs/api/rb/Selenium/WebDriver/IE/Options.html)
  *
- * ```json
+ * ```js
  * {
- *    "helpers": {
- *      "WebDriver" : {
- *        "url": "http://localhost",
- *        "browser": "internet explorer",
- *        "desiredCapabilities": {
- *          "ieOptions": {
+ *    helpers: {
+ *      WebDriver : {
+ *        url: "http://localhost",
+ *        browser: "internet explorer",
+ *        desiredCapabilities: {
+ *          ieOptions: {
  *            "ie.browserCommandLineSwitches": "-private",
  *            "ie.usePerProcessProxy": true,
  *            "ie.ensureCleanSession": true,
@@ -123,15 +122,18 @@ const webRoot = 'body';
  *
  * ### Selenoid Options
  *
- * ```json
+ * [Selenoid](https://aerokube.com/selenoid/latest/) is a modern way to run Selenium inside Docker containers.
+ * Selenoid is easy to set up and provides more features than original Selenium Server. Use `selenoidOptions` to set Selenoid capabilities
+ *
+ * ```js
  * {
- *    "helpers": {
- *      "WebDriver" : {
- *        "url": "http://localhost",
- *        "browser": "chrome",
- *        "desiredCapabilities": {
- *          "selenoidOptions": {
- *            "enableVNC": true,
+ *    helpers: {
+ *      WebDriver : {
+ *        url: "http://localhost",
+ *        browser: "chrome",
+ *        desiredCapabilities: {
+ *          selenoidOptions: {
+ *            enableVNC: true,
  *          }
  *        }
  *      }
@@ -139,17 +141,17 @@ const webRoot = 'body';
  * }
  * ```
  *
- * ### Connect through proxy
+ * ### Connect Through proxy
  *
  * CodeceptJS also provides flexible options when you want to execute tests to Selenium servers through proxy. You will
  * need to update the `helpers.WebDriver.capabilities.proxy` key.
  *
  * ```js
  * {
- *     "helpers": {
- *         "WebDriver": {
- *             "capabilities": {
- *                 "proxy": {
+ *     helpers: {
+ *         WebDriver: {
+ *             capabilities: {
+ *                 proxy: {
  *                     "proxyType": "manual|pac",
  *                     "proxyAutoconfigUrl": "URL TO PAC FILE",
  *                     "httpProxy": "PROXY SERVER",
@@ -169,10 +171,10 @@ const webRoot = 'body';
  *
  * ```js
  * {
- *     "helpers": {
- *         "WebDriver": {
- *             "capabilities": {
- *                 "proxy": {
+ *     helpers: {
+ *         WebDriver: {
+ *             capabilities: {
+ *                 proxy: {
  *                     "proxyType": "manual",
  *                     "httpProxy": "http://corporate.proxy:8080",
  *                     "socksUsername": "codeceptjs",
@@ -199,12 +201,12 @@ const webRoot = 'body';
  *
  * ```js
  * {
- *     "helpers":{
- *         "WebDriver": {
- *             "url": "YOUR_DESIRED_HOST",
- *             "user": "YOUR_BROWSERSTACK_USER",
- *             "key": "YOUR_BROWSERSTACK_KEY",
- *             "capabilities": {
+ *     helpers:{
+ *         WebDriver: {
+ *             url: "YOUR_DESIRED_HOST",
+ *             user: "YOUR_BROWSERSTACK_USER",
+ *             key: "YOUR_BROWSERSTACK_KEY",
+ *             capabilities: {
  *                 "browserName": "chrome",
  *
  *                 // only set this if you're using BrowserStackLocal to test a local domain
@@ -318,8 +320,8 @@ const webRoot = 'body';
  *
  * ```js
  * {
- *     "helpers": {
- *         "WebDriver": {
+ *     helpers: {
+ *         WebDriver: {
  *             "multiremote": {
  *                 "MyChrome": {
  *                     "desiredCapabilities": {
@@ -852,12 +854,11 @@ class WebDriver extends Helper {
     assertElementExists(res, locator, 'File field');
     const el = usingFirstElement(res);
 
-    // Remote Uplaod (when running Selenium Server)
+    // Remote Upload (when running Selenium Server)
     if (this.options.remoteFileUpload) {
-      const fileCompressed = await fileToBase64Zip(file);
       try {
         this.debugSection('File', 'Uploading file to remote server');
-        file = await this.browser.uploadFile(fileCompressed);
+        file = await this.browser.uploadFile(file);
       } catch (err) {
         throw new Error(`File can't be transferred to remote server. Set \`remoteFileUpload: false\` in config to upload file locally.\n${err.message}`);
       }
diff --git a/lib/interfaces/scenarioConfig.js b/lib/interfaces/scenarioConfig.js
index 568ab7d6..05cbf8cd 100644
--- a/lib/interfaces/scenarioConfig.js
+++ b/lib/interfaces/scenarioConfig.js
@@ -58,13 +58,13 @@ class ScenarioConfig {
    * Configures a helper.
    * Helper name can be omitted and values will be applied to first helper.
    */
-  config(helper, obj) {
+  async config(helper, obj) {
     if (!obj) {
       obj = helper;
       helper = 0;
     }
     if (typeof obj === 'function') {
-      obj = obj(this.test);
+      obj = await obj(this.test);
     }
     if (!this.test.config) {
       this.test.config = {};
diff --git a/lib/step.js b/lib/step.js
index 1e770157..877f33aa 100644
--- a/lib/step.js
+++ b/lib/step.js
@@ -154,8 +154,11 @@ function detectMetaStep(stack) {
     if (isTest(line) || isBDD(line)) break;
     const fnName = line.match(/^at (\w+)\.(\w+)\s\(/);
     if (!fnName) continue;
-    if (fnName[1] === 'Generator') return; // don't track meta steps inside generators
-    if (fnName[1] === 'recorder') return; // don't track meta steps inside generators
+    if (fnName[1] === 'Generator'
+      || fnName[1] === 'recorder'
+      || fnName[1] === 'Runner'
+    ) { return; } // don't track meta steps inside generators
+
     if (fnName[1] === 'Object') {
       // detect PO name from includes
       for (const name in support) {
diff --git a/lib/ui.js b/lib/ui.js
index 9ebcbcfa..1a6a2f80 100644
--- a/lib/ui.js
+++ b/lib/ui.js
@@ -138,7 +138,7 @@ module.exports = function (suite) {
      * Pending test case.
      */
     context.xScenario = context.Scenario.skip = function (title) {
-      context.Scenario(title, {});
+      return context.Scenario(title, {});
     };
 
     addDataContext(context);
diff --git a/lib/utils.js b/lib/utils.js
index a0b6efd5..4b7e507b 100644
--- a/lib/utils.js
+++ b/lib/utils.js
@@ -281,26 +281,6 @@ module.exports.screenshotOutputFolder = function (fileName) {
   return path.join(global.codecept_dir, fileName);
 };
 
-module.exports.fileToBase64Zip = async function (localPath) {
-  const archiver = require('archiver');
-
-  const zipData = [];
-  const source = fs.createReadStream(localPath);
-
-  return new Promise((resolve, reject) => {
-    archiver('zip')
-      .on('error', (e) => { throw new Error(e); })
-      .on('data', data => zipData.push(data))
-      .on('end', () => resolve(Buffer.concat(zipData).toString('base64')))
-      .append(source, { name: path.basename(localPath) })
-      .finalize((err) => {
-        if (err) {
-          reject(err);
-        }
-      });
-  });
-};
-
 module.exports.beautify = function (code) {
   const format = require('js-beautify').js;
   return format(code, { indent_size: 2, space_in_empty_paren: true });
@@ -322,7 +302,7 @@ function joinUrl(baseUrl, url) {
   return shouldAppendBaseUrl(url) ? `${baseUrl}/${trimUrl(url)}` : url;
 }
 
-module.exports.appendBaseUrl = function (baseUrl, oneOrMoreUrls) {
+module.exports.appendBaseUrl = function (baseUrl = '', oneOrMoreUrls) {
   // Remove '/' if it's at the end of baseUrl
   const lastChar = baseUrl.substr(-1);
   if (lastChar === '/') {
diff --git a/package.json b/package.json
index 39d52c11..3b09c4de 100644
--- a/package.json
+++ b/package.json
@@ -37,25 +37,26 @@
     "test": "mocha test/unit --recursive && mocha test/runner --recursive"
   },
   "dependencies": {
+    "@codeceptjs/detox-helper": "^1.0.1",
     "allure-js-commons": "^1.3.2",
     "archiver": "^3.0.0",
     "axios": "^0.19.0",
     "chalk": "^1.1.3",
     "commander": "^2.20.0",
     "css-to-xpath": "^0.1.0",
-    "cucumber-expressions": "^6.0.1",
+    "cucumber-expressions": "^6.6.2",
     "escape-string-regexp": "^1.0.3",
     "figures": "^2.0.0",
     "fn-args": "^4.0.0",
     "fs-extra": "^8.0.1",
     "gherkin": "^5.1.0",
     "glob": "^6.0.1",
-    "inquirer": "^6.3.1",
-    "js-beautify": "^1.9.1",
+    "inquirer": "^6.4.1",
+    "js-beautify": "^1.10.0",
     "lodash.merge": "^4.6.1",
     "mkdirp": "^0.5.1",
     "mocha": "^4.1.0",
-    "mocha-junit-reporter": "^1.22.0",
+    "mocha-junit-reporter": "^1.23.0",
     "parse-function": "^5.2.10",
     "promise-retry": "^1.1.1",
     "requireg": "^0.1.8",
@@ -66,31 +67,31 @@
     "@pollyjs/adapter-puppeteer": "^2.5.0",
     "@pollyjs/core": "^2.5.0",
     "@types/inquirer": "^0.0.35",
-    "@types/node": "^8.10.48",
-    "@wdio/sauce-service": "^5.8.0",
-    "@wdio/selenium-standalone-service": "^5.8.0",
-    "@wdio/utils": "^5.8.0",
+    "@types/node": "^8.10.49",
+    "@wdio/sauce-service": "^5.10.8",
+    "@wdio/selenium-standalone-service": "^5.9.3",
+    "@wdio/utils": "^5.9.3",
     "chai": "^3.4.1",
     "chai-as-promised": "^5.2.0",
     "co-mocha": "^1.2",
     "documentation": "^8.1.2",
     "eslint": "^4.17.0",
     "eslint-config-airbnb-base": "^12.1.0",
-    "eslint-plugin-import": "^2.17.2",
+    "eslint-plugin-import": "^2.18.0",
     "eslint-plugin-mocha": "^5.3.0",
     "faker": "^4.1.0",
     "husky": "^1.2.1",
     "json-server": "^0.10.1",
     "nightmare": "^3.0.2",
     "protractor": "^5.4.1",
-    "puppeteer": "^1.15.0",
+    "puppeteer": "^1.18.1",
     "rosie": "^1.6.0",
     "sinon": "^1.17.2",
     "sinon-chai": "^2.14.0",
     "testcafe": "^1.2.1",
     "typescript": "^2.9.2",
     "wdio-docker-service": "^1.5.0",
-    "webdriverio": "^5.8.0",
+    "webdriverio": "^5.10.9",
     "xmldom": "^0.1.27",
     "xpath": "0.0.27"
   },
diff --git a/website/i18n/en.json b/website/i18n/en.json
index 086edd90..11ab9bf6 100644
--- a/website/i18n/en.json
+++ b/website/i18n/en.json
@@ -45,7 +45,7 @@
         "title": "Books & Posts"
       },
       "changelog": {
-        "title": "changelog"
+        "title": "Releases"
       },
       "commands": {
         "title": "Commands"
@@ -59,6 +59,9 @@
       "data": {
         "title": "Data Management"
       },
+      "detox": {
+        "title": "Testing React Native with Detox"
+      },
       "docker": {
         "title": "Docker"
       },
@@ -74,6 +77,9 @@
       "helpers/Appium": {
         "title": "Appium"
       },
+      "helpers/Detox": {
+        "title": "Detox"
+      },
       "helpers/FileSystem": {
         "title": "FileSystem"
       },
@@ -83,9 +89,15 @@
       "helpers/Nightmare": {
         "title": "Nightmare"
       },
+      "helpers/Polly": {
+        "title": "Polly"
+      },
       "helpers/Protractor": {
         "title": "Protractor"
       },
+      "helpers/Puppeteer-firefox": {
+        "title": "helpers/Puppeteer-firefox"
+      },
       "helpers/Puppeteer": {
         "title": "Puppeteer"
       },
@@ -95,6 +107,9 @@
       "helpers/SeleniumWebdriver": {
         "title": "SeleniumWebdriver"
       },
+      "helpers/TestCafe": {
+        "title": "TestCafe"
+      },
       "helpers/WebDriver": {
         "title": "WebDriver"
       },
@@ -148,6 +163,9 @@
       },
       "visual": {
         "title": "Visual Testing"
+      },
+      "webdriver": {
+        "title": "Testing with WebDriver"
       }
     },
     "links": {
diff --git a/website/pages/en/index.js b/website/pages/en/index.js
index e2f84b4d..84b09c15 100644
--- a/website/pages/en/index.js
+++ b/website/pages/en/index.js
@@ -101,7 +101,8 @@ class Index extends React.Component {
     <div className="col-md-4">
       <img src="/img/Prism-3.svg" style={{filter: 'hue-rotate(90deg)', transform: 'rotate(180deg)'}} alt="" />
       <h5>Web & Mobile Testing</h5>
-      Write your tests for web and and mobile applications using same API.
+      Test native mobile apps using <b>Appium</b> or <b>Detox</b>.
+      <a href="/mobile" className="btn btn-light btn-sm">Learn More</a>
     </div>
   </div>
 
diff --git a/website/sidebars.json b/website/sidebars.json
index fd7a4fc0..7a997cb4 100755
--- a/website/sidebars.json
+++ b/website/sidebars.json
@@ -10,7 +10,7 @@
     ],
     "Web Testing": [
       "basics",
-      "acceptance",
+      "webdriver",
       "puppeteer",
       "angular",
       "nightmare",
@@ -18,7 +18,8 @@
       "react"
     ],
     "Mobile Testing": [
-      "mobile"
+      "mobile",
+      "detox"
     ],
     "Organizing Tests": [
       "best",
@@ -48,7 +49,8 @@
       "helpers/Protractor",
       "helpers/TestCafe",
       "helpers/Nightmare",
-      "helpers/Appium"
+      "helpers/Appium",
+      "helpers/Detox"
     ],
     "Additional Helpers": [
       "helpers/REST",


Test patch:
diff --git a/docs/helpers/TestCafe.md b/docs/helpers/TestCafe.md
index b4214f96..1b49779c 100644
--- a/docs/helpers/TestCafe.md
+++ b/docs/helpers/TestCafe.md
@@ -491,6 +491,19 @@ Returns [Promise][6]&lt;[number][7]> number of visible elements
 
 
 
+### grabPageScrollPosition
+
+Retrieves a page scroll position and returns it to test.
+Resumes test execution, so should be used inside an async function with `await` operator.
+
+```js
+let { x, y } = await I.grabPageScrollPosition();
+```
+
+Returns [Promise][6]&lt;[object][4]> scroll position
+
+
+
 ### grabSource
 
 Retrieves page source and returns it to test.
@@ -670,6 +683,46 @@ I.saveScreenshot('debug.png', true) //resizes to available scrollHeight and scro
     
 
 
+### scrollPageToBottom
+
+Scroll page to the bottom.
+
+```js
+I.scrollPageToBottom();
+```
+
+
+
+
+### scrollPageToTop
+
+Scroll page to the top.
+
+```js
+I.scrollPageToTop();
+```
+
+
+
+
+### scrollTo
+
+Scrolls to element matched by locator.
+Extra shift can be set with offsetX and offsetY options.
+
+```js
+I.scrollTo('footer');
+I.scrollTo('#submit', 5, 5);
+```
+
+#### Parameters
+
+-   `locator` ([string][3] \| [object][4]) located by CSS|XPath|strict locator.
+-   `offsetX` [number][7] (optional, `0` by default) X-axis offset. 
+-   `offsetY` [number][7] (optional, `0` by default) Y-axis offset.
+    
+ 
+
 ### see
 
 Checks that a page contains a visible text.
diff --git a/lib/helper/TestCafe.js b/lib/helper/TestCafe.js
index 266d839e..2c40a86c 100644
--- a/lib/helper/TestCafe.js
+++ b/lib/helper/TestCafe.js
@@ -124,6 +124,7 @@ class TestCafe extends Helper {
   static _config() {
     return [
       { name: 'url', message: 'Base url of site to be tested', default: 'http://localhost' },
+      { name: 'browser', message: 'Browser to be used', default: 'chrome' },
     ];
   }
 
@@ -780,10 +781,76 @@ class TestCafe extends Helper {
     return ClientFunction(() => document.location.href).with({ boundTestRun: this.t })();
   }
 
+  /**
+   * {{> grabPageScrollPosition }}
+   */
+  async grabPageScrollPosition() {
+    return ClientFunction(() => ({ x: window.pageXOffset, y: window.pageYOffset })).with({ boundTestRun: this.t })();
+  }
+
+  /**
+   * {{> scrollPageToTop }}
+   */
+  scrollPageToTop() {
+    return ClientFunction(() => window.scrollTo(0, 0)).with({ boundTestRun: this.t })();
+  }
+
+  /**
+   * {{> scrollPageToBottom }}
+   */
+  scrollPageToBottom() {
+    return ClientFunction(() => {
+      const body = document.body;
+      const html = document.documentElement;
+      window.scrollTo(0, Math.max(
+        body.scrollHeight, body.offsetHeight,
+        html.clientHeight, html.scrollHeight, html.offsetHeight,
+      ));
+    }).with({ boundTestRun: this.t })();
+  }
+
+  /**
+   * {{> scrollTo }}
+   */
+  async scrollTo(locator, offsetX = 0, offsetY = 0) {
+    if (typeof locator === 'number' && typeof offsetX === 'number') {
+      offsetY = offsetX;
+      offsetX = locator;
+      locator = null;
+    }
+
+    const scrollBy = ClientFunction((offset) => {
+      if (window && window.scrollBy && offset) {
+        window.scrollBy(offset.x, offset.y);
+      }
+    }).with({ boundTestRun: this.t });
+
+    if (locator) {
+      const els = await this._locate(locator);
+      assertElementExists(els, locator, 'Element');
+      const el = await els.nth(0);
+      const x = (await el.offsetLeft) + offsetX;
+      const y = (await el.offsetTop) + offsetY;
+
+      return scrollBy({ x, y });
+    }
+
+    const x = offsetX;
+    const y = offsetY;
+    return scrollBy({ x, y });
+  }
+
   /**
    * {{> switchTo }}
    */
   async switchTo(locator) {
+    if (Number.isInteger(locator)) {
+      throw new Error('Not supported switching to iframe by number');
+    }
+
+    if (!locator) {
+      return this.t.switchToMainWindow();
+    }
     return this.t.switchToIframe(findElements.call(this, this.context, locator));
   }
 
diff --git a/lib/helper/testcafe/testcafe-utils.js b/lib/helper/testcafe/testcafe-utils.js
index 65963c7d..3e32f5d1 100644
--- a/lib/helper/testcafe/testcafe-utils.js
+++ b/lib/helper/testcafe/testcafe-utils.js
@@ -9,7 +9,7 @@ const createTestFile = () => {
   assert(global.output_dir, 'global.output_dir must be set');
 
   const testFile = path.join(global.output_dir, `${Date.now()}_test.js`);
-  const testControllerHolderDir = __dirname;
+  const testControllerHolderDir = __dirname.replace(/\\/g, '/');
 
   fs.writeFileSync(
     testFile,
diff --git a/test/acceptance/codecept.Puppeteer.js b/test/acceptance/codecept.Puppeteer.js
index a699546a..83890d6a 100644
--- a/test/acceptance/codecept.Puppeteer.js
+++ b/test/acceptance/codecept.Puppeteer.js
@@ -15,9 +15,7 @@ module.exports.config = {
         ],
       },
     },
-    Polly: {
-      url: TestHelper.siteUrl(),
-    },
+    Polly: {},
   },
   include: {},
   bootstrap: false,
diff --git a/test/acceptance/config_test.js b/test/acceptance/config_test.js
index 38128868..09067119 100644
--- a/test/acceptance/config_test.js
+++ b/test/acceptance/config_test.js
@@ -29,3 +29,11 @@ Scenario('change config 5 @WebDriverIO @Puppeteer @Protractor @Nightmare', (I) =
   I.dontSeeInCurrentUrl('github.com');
   I.seeInCurrentUrl('google.com');
 });
+
+Scenario('change config 6 @WebDriverIO @Puppeteer @Protractor @Nightmare', (I) => {
+  I.amOnPage('/');
+  I.seeInCurrentUrl('github.com');
+}).config(async (test) => {
+  await new Promise(r => setTimeout(r, 50));
+  return { url: 'https://github.com' };
+});
diff --git a/test/acceptance/mock_test.js b/test/acceptance/mock_test.js
index d6111c07..3bd8e1fd 100644
--- a/test/acceptance/mock_test.js
+++ b/test/acceptance/mock_test.js
@@ -8,30 +8,24 @@ const fetchUsers = response => response.url() === 'https://jsonplaceholder.typic
 
 Scenario('change statusCode @Puppeteer', (I) => {
   I.amOnPage('/form/fetch_call');
-  I.click('GET POSTS');
-  I.waitForResponse(fetchPost, 3);
   I.mockRequest('GET', 'https://jsonplaceholder.typicode.com/*', 404);
   I.click('GET POSTS');
-  I.see('Can not load data!');
+  I.waitForText('Can not load data!', 1, '#data');
   I.stopMocking();
 });
 
 Scenario('change response data @Puppeteer', (I) => {
   I.amOnPage('/form/fetch_call');
-  I.click('GET COMMENTS');
-  I.waitForResponse(fetchComments, 3);
   I.mockRequest('GET', 'https://jsonplaceholder.typicode.com/*', {
     modified: 'This is modified from mocking',
   });
   I.click('GET COMMENTS');
-  I.see('This is modified from mocking', '#data');
+  I.waitForText('This is modified from mocking', 1, '#data');
   I.stopMocking();
 });
 
 Scenario('change response data for multiple requests @Puppeteer', (I) => {
   I.amOnPage('/form/fetch_call');
-  I.click('GET USERS');
-  I.waitForResponse(fetchUsers, 3);
   I.mockRequest(
     'GET',
     [
@@ -44,11 +38,11 @@ Scenario('change response data for multiple requests @Puppeteer', (I) => {
     },
   );
   I.click('GET POSTS');
-  I.see('MY CUSTOM DATA', '#data');
+  I.waitForText('MY CUSTOM DATA', 1, '#data');
   I.click('GET COMMENTS');
-  I.see('MY CUSTOM DATA', '#data');
+  I.waitForText('MY CUSTOM DATA', 1, '#data');
   I.click('GET USERS');
-  I.see('MY CUSTOM DATA', '#data');
+  I.waitForText('MY CUSTOM DATA', 1, '#data');
   I.stopMocking();
 });
 
@@ -56,15 +50,15 @@ Scenario(
   'should request for original data after mocking stopped @Puppeteer',
   (I) => {
     I.amOnPage('/form/fetch_call');
-    I.click('GET COMMENTS');
     I.mockRequest('GET', 'https://jsonplaceholder.typicode.com/*', {
-      comment: 'CUSTOM',
+      comment: 'CUSTOM _uniqueId_u4805sd23',
     });
     I.click('GET COMMENTS');
-    I.see('CUSTOM', '#data');
+    I.waitForText('_uniqueId_u4805sd23', 1, '#data');
     I.stopMocking();
 
     I.click('GET COMMENTS');
-    I.dontSee('CUSTOM', '#data');
+    I.wait(2);
+    I.dontSee('_uniqueId_u4805sd23', '#data');
   },
 );
diff --git a/test/data/sandbox/codecept.inject.po.json b/test/data/sandbox/codecept.inject.po.json
index a914e7f3..5dde0213 100644
--- a/test/data/sandbox/codecept.inject.po.json
+++ b/test/data/sandbox/codecept.inject.po.json
@@ -7,7 +7,8 @@
   },
   "include": {
     "I": "./support/custom_steps.js",
-    "MyPage": "./support/my_page.js"
+    "MyPage": "./support/my_page.js",
+    "SecondPage": "./support/second_page.js"
   },
   "bootstrap": false,
   "mocha": {},
diff --git a/test/data/sandbox/support/dummy.feature b/test/data/sandbox/support/dummy.feature
index f4786582..8a99984d 100644
--- a/test/data/sandbox/support/dummy.feature
+++ b/test/data/sandbox/support/dummy.feature
@@ -5,4 +5,12 @@ Feature: Login
     When I click login button at 1.2
     And I enter username "davert" and password "wow"
     And I submit 1 form
-    Then I should log in
\ No newline at end of file
+    Then I should log in
+    When I define a step with an opening paren ( only
+    And I define a step with a closing paren ) only
+    And I define a step with a opening brace { only
+    And I define a step with a closing brace } only
+    And I define a step with a slash http://example.com/foo
+    And I define a step with a ( paren and an 32 int
+    And I define a step with a ( paren and a 32.2 float
+    And I define a step with a ( paren and a "foo" string
diff --git a/test/data/sandbox/support/second_page.js b/test/data/sandbox/support/second_page.js
new file mode 100644
index 00000000..adfd44c9
--- /dev/null
+++ b/test/data/sandbox/support/second_page.js
@@ -0,0 +1,5 @@
+module.exports = {
+  secondPageMethod() {
+    console.log('secondPageMethod');
+  },
+};
diff --git a/test/helper/webapi.js b/test/helper/webapi.js
index 399925b9..a7c0bbf1 100644
--- a/test/helper/webapi.js
+++ b/test/helper/webapi.js
@@ -980,7 +980,6 @@ module.exports.tests = function () {
     });
 
     it('should scroll to an element', async () => {
-      if (isHelper('TestCafe')) return;
       await I.amOnPage('/form/scroll');
       await I.resizeWindow(500, 700);
       const { x, y } = await I.grabPageScrollPosition();
@@ -990,7 +989,6 @@ module.exports.tests = function () {
     });
 
     it('should scroll to coordinates', async () => {
-      if (isHelper('TestCafe')) return;
       await I.amOnPage('/form/scroll');
       await I.resizeWindow(500, 700);
       const { x, y } = await I.grabPageScrollPosition();
@@ -1001,7 +999,6 @@ module.exports.tests = function () {
     });
 
     it('should scroll to bottom of page', async () => {
-      if (isHelper('TestCafe')) return;
       await I.amOnPage('/form/scroll');
       await I.resizeWindow(500, 700);
       const { y } = await I.grabPageScrollPosition();
@@ -1012,7 +1009,6 @@ module.exports.tests = function () {
     });
 
     it('should scroll to top of page', async () => {
-      if (isHelper('TestCafe')) return;
       await I.amOnPage('/form/scroll');
       await I.resizeWindow(500, 700);
       await I.scrollPageToBottom();
diff --git a/test/runner/bdd_test.js b/test/runner/bdd_test.js
index 94552462..a8c65903 100644
--- a/test/runner/bdd_test.js
+++ b/test/runner/bdd_test.js
@@ -220,6 +220,46 @@ When('I submit {int} form', () => {
 Then('I should log in', () => {
   // From "support/dummy.feature" {"line":8,"column":5}
   throw new Error('Not implemented yet');
+});
+
+When(/^I define a step with an opening paren \\( only$/, () => {
+  // From "support/dummy.feature" {"line":9,"column":5}
+  throw new Error('Not implemented yet');
+});
+
+When(/^I define a step with a closing paren \\) only$/, () => {
+  // From "support/dummy.feature" {"line":10,"column":5}
+  throw new Error('Not implemented yet');
+});
+
+When(/^I define a step with a opening brace \\{ only$/, () => {
+  // From "support/dummy.feature" {"line":11,"column":5}
+  throw new Error('Not implemented yet');
+});
+
+When(/^I define a step with a closing brace \\} only$/, () => {
+  // From "support/dummy.feature" {"line":12,"column":5}
+  throw new Error('Not implemented yet');
+});
+
+When(/^I define a step with a slash http:\\/\\/example\\.com\\/foo$/, () => {
+  // From "support/dummy.feature" {"line":13,"column":5}
+  throw new Error('Not implemented yet');
+});
+
+When(/^I define a step with a \\( paren and an (\\d+) int$/, () => {
+  // From "support/dummy.feature" {"line":14,"column":5}
+  throw new Error('Not implemented yet');
+});
+
+When(/^I define a step with a \\( paren and a (\\d+\\.\\d+) float$/, () => {
+  // From "support/dummy.feature" {"line":15,"column":5}
+  throw new Error('Not implemented yet');
+});
+
+When(/^I define a step with a \\( paren and a "(.*?)" string$/, () => {
+  // From "support/dummy.feature" {"line":16,"column":5}
+  throw new Error('Not implemented yet');
 });`);
       assert(!err);
       done();
diff --git a/test/runner/list_test.js b/test/runner/list_test.js
index 816204dc..825c26e0 100644
--- a/test/runner/list_test.js
+++ b/test/runner/list_test.js
@@ -47,4 +47,78 @@ describe('list/def commands', () => {
       done();
     });
   });
+
+  it('def should create definition file with support object', (done) => {
+    try {
+      fs.unlinkSync(`${codecept_dir}/steps.d.ts`);
+    } catch (e) {
+      // continue regardless of error
+    }
+    exec(`${runner} def --config ${codecept_dir}/codecept.inject.po.json`, () => {
+      const content = fs.readFileSync(`${codecept_dir}/steps.d.ts`).toString();
+      content.should.include('    openDir() : void');
+      content.should.include('  export interface MyPage {');
+      content.should.include('    hasFile() : void');
+      done();
+    });
+  });
+
+  it('def should create definition file with internal object', (done) => {
+    try {
+      fs.unlinkSync(`${codecept_dir}/steps.d.ts`);
+    } catch (e) {
+      // continue regardless of error
+    }
+    exec(`${runner} def --config ${codecept_dir}/codecept.inject.po.json`, () => {
+      const content = fs.readFileSync(`${codecept_dir}/steps.d.ts`).toString();
+      content.should.include('  export const recorder: Recorder');
+      content.should.include('  export const event: CodeceptJSEvent');
+      content.should.include('  export const output: Output');
+      content.should.include('  export const config: Config');
+      content.should.include('  export const container: Container');
+      done();
+    });
+  });
+
+  it('def should create definition file with inject which contains support objects', (done) => {
+    try {
+      fs.unlinkSync(`${codecept_dir}/steps.d.ts`);
+    } catch (e) {
+      // continue regardless of error
+    }
+    exec(`${runner} def --config ${codecept_dir}/codecept.inject.po.json`, () => {
+      const content = fs.readFileSync(`${codecept_dir}/steps.d.ts`).toString();
+      content.should.include('declare function inject(): {');
+      content.should.include(' MyPage: CodeceptJS.MyPage');
+      content.should.include(' SecondPage: CodeceptJS.SecondPage');
+      done();
+    });
+  });
+
+  it('def should create definition file with inject which contains I object', (done) => {
+    try {
+      fs.unlinkSync(`${codecept_dir}/steps.d.ts`);
+    } catch (e) {
+      // continue regardless of error
+    }
+    exec(`${runner} def --config ${codecept_dir}/codecept.inject.po.json`, () => {
+      const content = fs.readFileSync(`${codecept_dir}/steps.d.ts`).toString();
+      content.should.include('declare function inject(): {');
+      content.should.include(' I: CodeceptJS.I');
+      done();
+    });
+  });
+
+  it('def should create definition file with callback params', (done) => {
+    try {
+      fs.unlinkSync(`${codecept_dir}/steps.d.ts`);
+    } catch (e) {
+      // continue regardless of error
+    }
+    exec(`${runner} def --config ${codecept_dir}/codecept.inject.po.json`, () => {
+      const content = fs.readFileSync(`${codecept_dir}/steps.d.ts`).toString();
+      content.should.include('type ICodeceptCallback = (i?: CodeceptJS.I, current?:any, MyPage?:CodeceptJS.MyPage, SecondPage?:CodeceptJS.SecondPage, ...args: any) => void;');
+      done();
+    });
+  });
 });