Repository: Codeception/CodeceptJS
Pull request #1919 (2019-10-01 16:13:59)

update readme


Patch:
diff --git a/README.md b/README.md
index eef5ef2a..d6656830 100644
--- a/README.md
+++ b/README.md
@@ -3,11 +3,11 @@
 
 Reference: [Helpers API](https://github.com/Codeception/CodeceptJS/blob/master/docs) | [Demo](https://github.com/Codeception/codeceptjs-demo)
 
-## Modern Era Acceptance Testing Framework for NodeJS
+## Supercharged End 2 End Testing
 
 CodeceptJS is a new testing framework for end-to-end testing with WebDriver (or others).
 It abstracts browser interaction to simple steps which is written from a user perspective.
-A simple test that verifies that "Welcome" text is present on a main page of a site will look like:
+A simple test that verifies the "Welcome" text is present on a main page of a site will look like:
 
 ```js
 Feature('CodeceptJS demo');
@@ -32,6 +32,7 @@ CodeceptJS uses **Helper** modules to provide actions to `I` object. Currently C
 * [**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
+* [**Detox**](https://github.com/Codeception/CodeceptJS/blob/master/docs/helpers/Detox.md) - This is a wrapper on top of Detox library, aimied to unify testing experience for CodeceptJS framework. Detox provides a grey box testing for mobile applications, playing especially good for React Native apps.
 
 And more to come...
 
@@ -39,8 +40,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, Puppeteer, Protractor, TestCafe etc,
-as CodeceptJS unifies them and makes them work as they were synchronous.
+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 are synchronous.
 
 ## Features
 
@@ -118,7 +118,7 @@ In this examples all methods of `I` are taken from WebDriver helper, see [refere
 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.
 
 ```sh
-codeceptjs run --steps
+npx codeceptjs run --steps
 ```
 
 This will produce an output:
@@ -144,7 +144,7 @@ Just add `pause()` call at any place in a test and run it.
 Interactive shell can be started outside test context by running:
 
 ```sh
-codeceptjs shell
+npx codeceptjs shell
 ```
 
 ### Actions
@@ -182,13 +182,13 @@ In case you need to return a value from a webpage and use it directly in test, y
 They are expected to be used inside async/await functions, and their results will be available in test:
 
 ```js
-var assert = require('assert');
+let assert = require('assert');
 
 Feature('CodeceptJS Demonstration');
 
 Scenario('test page title', async (I) => {
   I.amOnPage('http://simple-form-bootstrap.plataformatec.com.br/documentation');
-  var title = await I.grabTitle();
+  let title = await I.grabTitle();
   assert.equal(title, 'Example application with SimpleForm and Twitter Bootstrap');
 });
 ```
diff --git a/docs/angular.md b/docs/angular.md
index 702a11ac..d933744f 100644
--- a/docs/angular.md
+++ b/docs/angular.md
@@ -7,22 +7,22 @@ title: Testing with Protractor
 
 ## Introduction
 
-CodeceptJS is an acceptance testing framework. In diversified world of JavaScript testing libraries it aims to create a unified high level API for end-to-end testing, powered by different backends.
-CodeceptJS allows you to write a test and switch in config execution drivers: will it be *wedriverio*, *puppeteer*, or *protractor* depends on you.
-This way you aren't be bound to implementation, and your acceptance tests will work no matter of framework running them.
+CodeceptJS is an acceptance testing framework. In the diversified world of JavaScript testing libraries, it aims to create a unified high-level API for end-to-end testing, powered by a variety of backends.
+CodeceptJS allows you to write a test and switch the execution driver via config: whether it's *wedriverio*, *puppeteer*, or *protractor* depends on you.
+This way you aren't bound to a specific implementation, and your acceptance tests will work no matter what framework is running them.
 
-As you know, [Protractor](http://www.protractortest.org/#/) is an official tool for testing AngularJS applications.
-CodeceptJS should not be considered as alternative to Protractor but a testing framework utilizing this powerful library.
+[Protractor](http://www.protractortest.org/#/) is an official tool for testing AngularJS applications.
+CodeceptJS should not be considered as alternative to Protractor, but rather a testing framework that leverages this powerful library.
 
 ![angular-protractor](https://codecept.io/img/angular-protractor.png)
 
-So there is no magic in testing of AngularJS application in CodeceptJS.
-You just execute regular Protractor commands, packed in a simple high-level API.
+There is no magic in testing of AngularJS application in CodeceptJS.
+You just execute regular Protractor commands, packaged into a simple, high-level API.
 
 ![todo-mvc](https://codecept.io/img/todo.png)
 
-As an example we will use popular [TodoMVC application](http://todomvc.com/examples/angularjs/#/).
-How would we test creating a new todo item in CodeceptJS?
+As an example, we will use the popular [TodoMVC application](http://todomvc.com/examples/angularjs/#/).
+How would we test creating a new todo item using CodeceptJS?
 
 ```js
 Scenario('create todo item', (I) => {
@@ -35,7 +35,7 @@ Scenario('create todo item', (I) => {
 });
 ```
 
-The similar test written in native syntax of Protractor (inherited from selenium-webdriver) would look like this:
+A similar test written using Protractor's native syntax (inherited from selenium-webdriver) would look like this:
 
 ```js
 it('should create todo item', (I) => {
@@ -50,16 +50,15 @@ it('should create todo item', (I) => {
 });
 ```
 
-Comparing to the API proposed by CodeceptJS, this code looks a bit more complicated.
-But what the more important, it's really really hard to read and follow its logic.
-Readability is the most crucial part in acceptance testing.
-You should easily change tests when changes specification or design.
-Probably, only a person who writes Protractor tests in your company,
-could do those changes, while CodeceptJS allows anyone to work with tests.
-Contrary, CodeceptJS provides CodeceptJS provides scenario-driven approach, so test is just a step-by-step representation of real user actions.
-This way you can easily read, and follow test scenario, and edit it when you need it to be changed.
+Compared to the API proposed by CodeceptJS, the Protractor code looks more complicated.
+Even more important, it's harder to read and follow the logic of the Protractor test.
+Readability is a crucial part of acceptance testing.
+Tests should be easy to modify when there are changes in the specification or design.
+If the test is written in Protractor, it would likely require someone familiar with Protractor to make the change, whereas CodeceptJS allows anyone to understand and modify the test.
+CodeceptJS provides scenario-driven approach, so a test is just a step-by-step representation of real user actions.
+This means you can easily read and understand the steps in a test scenario, and edit the steps when the test needs to be changed.
 
-In this way CodeceptJS is more similar to Cucumber, so if you run a test with `--steps` option you will see this output:
+In this way, CodeceptJS is similar to Cucumber. If you run a test with `--steps` option you will see this output:
 
 ```bash
 TodoMvc --
@@ -73,24 +72,24 @@ TodoMvc --
  ✓ OK in 968ms
 ```
 
-Unlike Cucumber, CodeceptJS is not about writing test scenarios above for business rules.
-To say it again, its **goal is to provide standard action steps you can use for testing applications**.
-Surely, it can't cover 100% of cases but it aims for 90%, for others you can write your own steps inside a [custom Helper](http://codecept.io/helpers/) using API of Protractor
+Unlike Cucumber, CodeceptJS is not about writing test scenarios to satisfy business rules or requirements.
+Instead, its **goal is to provide standard action steps you can use for testing applications**.
+Although it can't cover 100% of use cases, CodeceptJS aims for 90%. For the remainder, you can write your own steps inside a [custom Helper](http://codecept.io/helpers/) using Protractor's API.
 
 ### Setting up CodeceptJS with Protractor
 
-To start using CodeceptJS you will need to install it via NPM and initialize it in directory with tests.
+To start using CodeceptJS you will need to install it via NPM and initialize it in a directory with tests.
 
 ```bash
 npm install codeceptjs --save
 npx codeceptjs init
 ```
 
-You will be asked questions about initial configuration, make sure you select Protractor helper.
-If you didn't have Protractor library it **will be installed**.
-Please agree to extend steps, and use `http://todomvc.com/examples/angularjs/` as a url for Protractor helper.
+You will be asked questions about the initial configuration, make sure you select the Protractor helper.
+If your project didn't already have the Protractor library, it **will be installed** as part of this process.
+Please agree to extend steps, and use `http://todomvc.com/examples/angularjs/` as the url for Protractor helper.
 
-For TodoMVC application you will have following config created in `codecept.conf.js` file:
+For TodoMVC application, you will have following config created in the `codecept.conf.js` file:
 
 ```js
 exports.config = { tests: './*_test.js',
@@ -109,15 +108,15 @@ exports.config = { tests: './*_test.js',
 }
 ```
 
-First test can be generated with `gt` command:
+Your first test can be generated with the `gt` command:
 
 ```bash
 npx codeceptjs gt
 ```
 
-After that you can start writing your first CodeceptJS/Angular tests.
-Please look into the reference of [Protractor helper](http://codecept.io/helpers/Protractor/) for all available actions.
-You can also run `list` command to see methods of I:
+After that, you can start writing your first CodeceptJS/Angular tests.
+Please refer to the [Protractor helper](http://codecept.io/helpers/Protractor/) documentation for a list of all available actions.
+You can also run the `list` command to see methods of I:
 
 ```bash
 npx codeceptjs list
@@ -131,12 +130,12 @@ Protractor requires Selenium Server to be started and running. To start and stop
 npm i @wdio/selenium-standalone-service --save
 ```
 
-Enable it in config inside plugins section:
+Enable it in the `codecept.conf.js` file, inside the plugins section:
 
 ```js
 exports.config = {
   // ...
-  // inside condecept.conf.js
+  // inside codecept.conf.js
   plugins: {
     wdio: {
         enabled: true,
@@ -148,7 +147,7 @@ exports.config = {
 
 ## Testing non-Angular Applications
 
-Sure, Protractor can be used to test applications built without AngularJS. In this case you need to disable angular synchronization feature in config:
+Protractor can also be used to test applications built without AngularJS. In this case, you need to disable the angular synchronization feature inside the config:
 
 ```js
 helpers: {
@@ -161,12 +160,11 @@ helpers: {
 }
 ```
 
-## Writing First Test
+## Writing Your First Test
 
-Test scenario should always use `I` object to execute commands.
-This is important as all methods of `I` are running in global promise chain, this way CodeceptJS makes sure everything is executed in right order.
-At first a page should be opened to proceed, we use `amOnPage` command for that. As we already specified full URL to TodoMVC app,
-we can pass relative path into it instead of absolute url:
+Your test scenario should always use the `I` object to execute commands.
+This is important, as all methods of `I` are running in the global promise chain. This way, CodeceptJS makes sure everything is executed in right order.
+To start with opening a webpage, use the `amOnPage` command for. Since we already specified the full URL to the TodoMVC app, we can pass the relative path for our url, instead of the absolute url:
 
 ```js
 Feature('Todo MVC');
@@ -176,7 +174,7 @@ Scenario('create todo item', (I) => {
 });
 ```
 
-All scenarios should describe actions on site and assertions taken in the end. In CodeceptJS assertion commands have `see` or `dontSee` prefix:
+All scenarios should describe actions on the site, with assertions at the end. In CodeceptJS, assertion commands have the `see` or `dontSee` prefix:
 
 ```js
 Feature('Todo MVC');
@@ -187,7 +185,7 @@ Scenario('create todo item', (I) => {
 });
 ```
 
-A test can be executed with `run` command, we recommend to use `--steps` options to follow step-by-step execution:
+A test can be executed with the `run` command, we recommend using the `--steps` option to print out the step-by-step execution:
 
 ```sh
 npx codeceptjs run --steps
@@ -205,11 +203,10 @@ TodoMvc --
 
 ## Running Several Scenarios
 
-By writing a test in similar manner we will have a test shown in the beginning of this guide. Probably we would like not to finish with one test,
-but have more, for testing editing of todo items, checking todo items, and more.
+By now, you should have a test similar to the one shown in the beginning of this guide. We probably want to have multiple tests though, like testing the editing of todo items, checking todo items, etc.
 
-Let's prepare our test for multiple scenarios. All test scenarios will need to open main page of application, so `amOnPage` can be moved into the `Before` hook:
-Scenarios will probably deal with created todo items, so we can move logic of crating new todo into a function.
+Let's prepare our test to contain multiple scenarios. All of our test scenarios will need to to start with with the main page of application open, so `amOnPage` can be moved into the `Before` hook:
+Our scenarios will also probably deal with created todo items, so we can move the logic of creating a new todo into a function.
 
 ```js
 Feature('TodoMvc');
@@ -231,7 +228,7 @@ Scenario('create todo item', (I) => {
 });
 ```
 
-and so we can add even more tests!
+and now we can add even more tests!
 
 ```js
 Scenario('edit todo', (I) => {
@@ -257,9 +254,8 @@ Scenario('check todo item', (I) => {
 
 ## Locators
 
-Like you may have noticed, CodeceptJS doesn't use `by.*` locators similar to Protractor or Selenium Webdriver.
-Instead most of methods expect you to pass valid CSS or XPath. In case you don't want CodeceptJS to guess the type of locator,
-you can specify them using so-called strict locators. This is an absolute analogy of `by`, so you can use angular specific locators (like models, repeaters, bindings, etc) in it:
+You may have noticed that CodeceptJS doesn't use `by.*` locators which are common in Protractor or Selenium Webdriver.
+Instead, most methods expect you to pass valid CSS selectors or XPath. If you don't want CodeceptJS to guess the locator type, then you can specify the type using *strict locators*. This is the CodeceptJS version of `by`, so you can also reuse your angular specific locators (like models, repeaters, bindings, etc):
 
 ```sh
 {css: 'button'}
@@ -267,17 +263,17 @@ you can specify them using so-called strict locators. This is an absolute analog
 {binding: 'latest'}
 ```
 
-When we deal with clicks, CodeceptJS can take a text and search a web page for a valid clickable element with that text.
-So links and buttons can be searched by their text.
+When dealing with clicks, we can specify a text value. CodeceptJS will use that value to search the web page for a valid clickable element containing our specified text.
+This enables us to search for links and buttons by their text.
 
-Same thing happens for form fields: they are searched by field names, labels, and so on.
+The same is true for form fields: they can be searched by field name, label, and so on.
 
-Using such smart locators makes tests easy to write, however, searching an element by text is slower than using CSS|XPath and much slower than using strict locators.
+Using smart locators makes tests easier to write, however searching an element by text is slower than searching via CSS|XPath, and is much slower than using strict locators.
 
 ## Refactoring
 
-In previous examples, we've moved actions into `createTodo` function. Is there a more elegant way of refactoring?
-Can we have something like `I.createTodo()` to be used in code? Sure, we can do so by editing `steps_file.js` created by init command.
+In the previous examples, we moved actions into the `createTodo` function. Is there a more elegant way of refactoring?
+Can we instead write a function like `I.createTodo()` which we can reuse? In fact, we can do so by editing the `steps_file.js` file created by the init command.
 
 ```js
 // in this file you can append custom step methods to 'I' object
@@ -292,7 +288,7 @@ module.exports = function() {
 }
 ```
 
-And that's all, method is available to use as `I.createTodo(title)`:
+That's it, our method is now available to use as `I.createTodo(title)`:
 
 ```js
 Scenario('create todo item', (I) => {
@@ -308,11 +304,10 @@ To learn more about refactoring options in CodeceptJS read [PageObjects guide](h
 
 ## Extending
 
-What if CodeceptJS doesn't provide some of Protractor functionality you actually need? Sure its API is to general,
-and this case is possible. If you don't know how to do something with CodeceptJS - revert back to Protractor syntax!
+What if CodeceptJS doesn't provide some specific Protractor functionality you need? If you don't know how to do something with CodeceptJS, you can simply revert back to using Protractor syntax!
 
-Create custom helper, define methods for it, and use it inside the I object. Your Helper can access `browser` from Protractor
-by accessing Protractor helper:
+Create a custom helper, define methods for it, and use it inside the I object. Your Helper can access `browser` from Protractor
+by accessing the Protractor helper:
 
 ```js
 let browser = this.helpers['Protractor'].browser;
@@ -324,7 +319,7 @@ or use global `element` and `by` variables to locate elements:
 element.all(by.repeater('result in memory'));
 ```
 
-This way we recommend to implement all custom logic using low-level Protractor syntax and using it inside scenario tests.
-Please see an [example of such helper](http://codecept.io/helpers/#protractor-example).
+This is the recommended way to implement all custom logic using low-level Protractor syntax in order to reuse it inside of test scenarios.
+For more information, see an [example of such a helper](http://codecept.io/helpers/#protractor-example).
 
 
diff --git a/docs/commands.md b/docs/commands.md
index e674a880..88ae0015 100644
--- a/docs/commands.md
+++ b/docs/commands.md
@@ -110,7 +110,7 @@ npx codeceptjs dry-run --steps -p allure
 To enable bootstrap script in dry-run mode, pass in `--bootstrap` option when running with `--steps` or `--debug`
 
 ```
-nox codeceptjs dry-run --steps --bootstrap
+npx codeceptjs dry-run --steps --bootstrap
 ```
 
 ## Run Multiple
diff --git a/docs/configuration.md b/docs/configuration.md
index 7ec0bf07..2f962dc5 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -17,7 +17,7 @@ Here is an overview of available options with their defaults:
 * **output**: `"./output"` - where to store failure screenshots, etc
 * **helpers**: `{}` - list of enabled helpers
 * **mocha**: `{}` - mocha options, [reporters](http://codecept.io/reports/) can be configured here
-* **multiple**: `{}` - multiple options, see [Multiple Execution](http://codecept.io/advanced/#multiple-browsers-execution)
+* **multiple**: `{}` - multiple options, see [Multiple Execution](http://codecept.io/parallel#multiple-browsers-execution)
 * **bootstrap**: `"./bootstrap.js"` - an option to run code _before_ tests are run. See [Hooks](http://codecept.io/hooks/#bootstrap-teardown)).
 * **bootstrapAll**: `"./bootstrap.js"` - an option to run code _before_ all test suites are run when using the run-multiple mode. See [Hooks](http://codecept.io/hooks/#bootstrap-teardown)).
 * **teardown**: - an option to run code _after_  all test suites are run when using the run-multiple mode. See [Hooks](http://codecept.io/hooks/#bootstrap-teardown).
diff --git a/docs/hooks.md b/docs/hooks.md
index 2fdc8c9e..4c9099c0 100644
--- a/docs/hooks.md
+++ b/docs/hooks.md
@@ -11,7 +11,7 @@ CodeceptJS provides API to run custom code before and after the test and inject
 In case you need to execute arbitrary code before or after the tests,
 you can use `bootstrap` and `teardown` config. Use it to start and stop webserver, Selenium, etc.
 
-When using the [Multiple Execution](http://codecept.io/advanced/#multiple-execution) mode, there are two additional hooks available; `bootstrapAll` and `teardownAll`. See [BootstrapAll & TeardownAll](#bootstrapall--teardownall) for more information.
+When using the [Multiple Execution](http://codecept.io/advanced/#multiple-execution) mode, there are two additional hooks available; `bootstrapAll` and `teardownAll`. See [BootstrapAll & TeardownAll](#bootstrapall-teardownall) for more information.
 
 There are different ways to define bootstrap and teardown functions:
 
diff --git a/docs/visual.md b/docs/visual.md
index 8fa71fec..f9934497 100644
--- a/docs/visual.md
+++ b/docs/visual.md
@@ -183,3 +183,91 @@ Scenario('Compare CodeceptIO Home Page @visual-test', async (I, adminPage) => {
 Depending of your configuration this test will fail if no baseline exists and log the link to the image to accept or automatically accept the first run as baseline.
 > You can accept the first image as baseline automatically via ```autoBaseline: true``` _default is false_
 
+## Using Applitools
+
+Applitools helps Test Automation engineers, DevOps, and FrontEnd Developers continuously test and validate visually perfect mobile, web, and native apps. Now it can be used with CodeceptJS.
+
+### Setup
+
+Create an account at [Applitools](https://applitools.com/users/register) and install the npm packages
+
+```
+npm i codeceptjs-applitoolshelper --save
+npm i webdriverio@5 --save
+```
+
+### Configuring
+
+```js
+...
+  helpers: {
+    WebDriver: {
+      url: 'https://applitools.com/helloworld',
+      browser: 'chrome',
+      desiredCapabilities: {
+        chromeOptions: {
+          args: [ '--headless', '--disable-extensions', '--disable-gpu', '--no-sandbox', '--disable-dev-shm-usage']
+        }
+      },
+      windowSize: '1920x600',
+      smartWait: 5000,
+      timeouts: {
+        'script': 60000,
+        'page load': 10000
+      },
+    },
+    ApplitoolsHelper: {
+      require: 'codeceptjs-applitoolshelper',
+      applitoolsKey: 'YOUR_API_KEY'
+    }
+  },
+...
+```
+
+#### To use this helper you need to provide the following info:
+
+- applitoolsKey (Required): You can find your API key under the user menu located at the right hand side of the test manager toolbar
+- windowSize (Optional): the windows size as for instance 1440x700, if not provided, the default 1920x600 will be used. The windowSize will follow this precedence: ApplitoolsHelper, Webdriver.
+- appName (Optional): you can either provide your desired application name, if not provided, the default 'Application Under Test' will be used.
+
+### Usage
+
+```javascript
+/**
+ * @param pageToCheck {string} Name of page you want to check
+ */
+I.eyeCheck(pageToCheck);
+
+```
+
+The first time you run this test a new baseline will be created, and subsequent test runs will be compared to this baseline. If any screenshot mismatch its baseline image in a perceptible way, there will be a `DiffsFoundException` which includes a URL that points to a detailed report where you can see the detected differences and take appropriate actions such as reporting bugs, updating the baseline and more.
+
+```js
+-- FAILURES:
+
+  1) Applitools functionality
+       Check home page @test:
+     Test 'Applitools functionality' of 'Application Under Test' detected differences!. See details at: https://eyes.applitools.com/app/batches/00000251831777088983/00000251831777088717?accountId=KO-Oh9tXI0e8VF8Ha_GLVA~~
+```
+
+> You can find the latest documentation here [Applitools Docs](https://applitools.com/tutorials/webdriverio5.html#run-your-first-test)
+
+### Example
+
+Lets consider visual testing for [CodeceptJS Home](http://codecept.io).
+You can also find example repo here: https://github.com/PeterNgTr/codeceptjs-applitoolshelper
+
+```js
+const { I } = inject();
+
+Feature('Applitools functionality');
+
+Before(() => {
+    I.amOnPage('https://applitools.com/helloworld');
+});
+
+Scenario('Check home page @test', async () => {
+    await I.eyeCheck('Homepage');
+});
+```
+
diff --git a/examples/codecept.conf.js b/examples/codecept.conf.js
index 6443b875..c8c324d1 100644
--- a/examples/codecept.conf.js
+++ b/examples/codecept.conf.js
@@ -1,9 +1,3 @@
-const { useHeadlessWhen, useSharedCookies } = require('@codeceptjs/config-hooks');
-
-useHeadlessWhen(process.env.headless);
-useSharedCookies();
-useWindowSize(1600, 1000);
-
 exports.config = {
   output: './output',
   helpers: {
@@ -12,7 +6,7 @@ exports.config = {
       browser: 'chrome',
       restart: false,
       windowSize: '1600x1200',
-      show: false,
+      show: true,
     },
     REST: {},
   },
diff --git a/lib/config.js b/lib/config.js
index c333ed10..4ca0b712 100644
--- a/lib/config.js
+++ b/lib/config.js
@@ -1,8 +1,11 @@
 const fs = require('fs');
 const path = require('path');
-const isFile = require('./utils').isFile;
-const fileExists = require('./utils').fileExists;
-const deepMerge = require('./utils').deepMerge;
+const {
+  fileExists,
+  isFile,
+  deepMerge,
+  deepClone,
+} = require('./utils');
 
 const defaultConfig = {
   output: './_output',
@@ -33,7 +36,7 @@ class Config {
    * @param {*} newConfig
    */
   static create(newConfig) {
-    config = deepMerge(defaultConfig, newConfig);
+    config = deepMerge(deepClone(defaultConfig), newConfig);
     hooks.forEach(f => f(config));
     return config;
   }
@@ -104,7 +107,7 @@ class Config {
    */
   static reset() {
     hooks = [];
-    return config = defaultConfig;
+    return config = Object.assign({}, defaultConfig);
   }
 }
 
diff --git a/lib/helper/WebDriver.js b/lib/helper/WebDriver.js
index d2b57c64..4326fc26 100644
--- a/lib/helper/WebDriver.js
+++ b/lib/helper/WebDriver.js
@@ -2190,7 +2190,10 @@ async function findFields(locator) {
   if (!locator.isFuzzy()) return this._locate(locator, true);
 
   const literal = xpathLocator.literal(locator.value);
-  let els = await this._locate(Locator.field.byText(literal));
+  let els = await this._locate(Locator.field.labelEquals(literal));
+  if (els.length) return els;
+
+  els = await this._locate(Locator.field.labelContains(literal));
   if (els.length) return els;
 
   els = await this._locate(Locator.field.byName(literal));
diff --git a/lib/locator.js b/lib/locator.js
index c2cbc64b..b6109495 100644
--- a/lib/locator.js
+++ b/lib/locator.js
@@ -209,7 +209,7 @@ Locator.clickable = {
     `.//button[./@name = ${literal}]`,
     `.//*[@aria-label = ${literal}]`,
     `.//*[@title = ${literal}]`,
-    `.//*[@aria-labelledby = //*[normalize-space(string(.)) = ${literal}]/@id ]`,
+    `.//*[@aria-labelledby = //*[@id][normalize-space(string(.)) = ${literal}]/@id ]`,
   ]),
 
   self: literal => `./self::*[contains(normalize-space(string(.)), ${literal}) or contains(normalize-space(@value), ${literal})]`,
@@ -217,19 +217,19 @@ Locator.clickable = {
 
 Locator.field = {
   labelEquals: literal => xpathLocator.combine([
-    `.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')][((./@name = ${literal}) or ./@id = //label[normalize-space(string(.)) = ${literal}]/@for or ./@placeholder = ${literal})]`,
+    `.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')][((./@name = ${literal}) or ./@id = //label[@for][normalize-space(string(.)) = ${literal}]/@for or ./@placeholder = ${literal})]`,
     `.//label[normalize-space(string(.)) = ${literal}]//.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]`,
   ]),
   labelContains: literal => xpathLocator.combine([
-    `.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')][(((./@name = ${literal}) or ./@id = //label[contains(normalize-space(string(.)), ${literal})]/@for) or ./@placeholder = ${literal})]`,
+    `.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')][(((./@name = ${literal}) or ./@id = //label[@for][contains(normalize-space(string(.)), ${literal})]/@for) or ./@placeholder = ${literal})]`,
     `.//label[contains(normalize-space(string(.)), ${literal})]//.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]`,
     `.//*[@aria-label = ${literal}]`,
     `.//*[@title = ${literal}]`,
-    `.//*[@aria-labelledby = //*[normalize-space(string(.)) = ${literal}]/@id ]`,
+    `.//*[@aria-labelledby = //*[@id][normalize-space(string(.)) = ${literal}]/@id ]`,
   ]),
   byName: literal => `.//*[self::input | self::textarea | self::select][@name = ${literal}]`,
   byText: literal => xpathLocator.combine([
-    `.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')][(((./@name = ${literal}) or ./@id = //label[contains(normalize-space(string(.)), ${literal})]/@for) or ./@placeholder = ${literal})]`,
+    `.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')][(((./@name = ${literal}) or ./@id = //label[@for][contains(normalize-space(string(.)), ${literal})]/@for) or ./@placeholder = ${literal})]`,
     `.//label[contains(normalize-space(string(.)), ${literal})]//.//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]`,
   ]),
 
@@ -237,7 +237,7 @@ Locator.field = {
 
 Locator.checkable = {
   byText: literal => xpathLocator.combine([
-    `.//input[@type = 'checkbox' or @type = 'radio'][(@id = //label[contains(normalize-space(string(.)), ${literal})]/@for) or @placeholder = ${literal}]`,
+    `.//input[@type = 'checkbox' or @type = 'radio'][(@id = //label[@for][contains(normalize-space(string(.)), ${literal})]/@for) or @placeholder = ${literal}]`,
     `.//label[contains(normalize-space(string(.)), ${literal})]//input[@type = 'radio' or @type = 'checkbox']`,
   ]),
 
diff --git a/lib/plugin/puppeteerCoverage.js b/lib/plugin/puppeteerCoverage.js
index 083288e4..87569be8 100644
--- a/lib/plugin/puppeteerCoverage.js
+++ b/lib/plugin/puppeteerCoverage.js
@@ -56,7 +56,7 @@ function buildFileName(test, uniqueFileName) {
  *
  * Possible config options:
  *
- * * `outputDir`: directory to dump coverage files
+ * * `coverageDir`: directory to dump coverage files
  * * `uniqueFileName`: generate a unique filename by adding uuid
  *
  *  First of all, your mileage may vary!
diff --git a/package.json b/package.json
index 838d3d36..72f9310a 100644
--- a/package.json
+++ b/package.json
@@ -39,6 +39,7 @@
     "dev:graphql": "nodemon test/data/graphql/index.js"
   },
   "dependencies": {
+    "@codeceptjs/configure": "^0.3.0",
     "allure-js-commons": "^1.3.2",
     "axios": "^0.19.0",
     "chalk": "^1.1.3",
@@ -73,10 +74,10 @@
     "@types/inquirer": "^0.0.35",
     "@types/mocha": "^5.2.7",
     "@types/node": "^8.10.54",
-    "@wdio/sauce-service": "^5.12.5",
-    "@wdio/selenium-standalone-service": "^5.12.1",
-    "@wdio/utils": "^5.13.0",
-    "apollo-server-express": "^2.9.3",
+    "@wdio/sauce-service": "^5.13.2",
+    "@wdio/selenium-standalone-service": "^5.13.2",
+    "@wdio/utils": "^5.13.2",
+    "apollo-server-express": "^2.9.4",
     "chai": "^3.4.1",
     "chai-as-promised": "^5.2.0",
     "documentation": "^8.1.2",
@@ -86,7 +87,7 @@
     "eslint-plugin-mocha": "^5.3.0",
     "express": "^4.17.1",
     "faker": "^4.1.0",
-    "graphql": "^14.5.7",
+    "graphql": "^14.5.8",
     "husky": "^1.2.1",
     "json-server": "^0.10.1",
     "nightmare": "^3.0.2",
@@ -99,7 +100,7 @@
     "testcafe": "^1.5.0",
     "typescript": "^2.9.2",
     "wdio-docker-service": "^1.5.0",
-    "webdriverio": "^5.13.1",
+    "webdriverio": "^5.13.2",
     "xmldom": "^0.1.27",
     "xpath": "0.0.27"
   },
diff --git a/website/static/css/custom.css b/website/static/css/custom.css
index 06e6aeb4..54db3d74 100755
--- a/website/static/css/custom.css
+++ b/website/static/css/custom.css
@@ -340,6 +340,11 @@ background: #2ecc71;
   color: #333;
 }
 
+.navSearchWrapper:before {
+  -webkit-box-sizing: content-box;
+  box-sizing: content-box;
+}
+
 
 @media only screen and (min-device-width: 360px) and (max-device-width: 736px) {
 }


Test patch:
diff --git a/lib/helper/TestCafe.js b/lib/helper/TestCafe.js
index e001ed6f..acf959d9 100644
--- a/lib/helper/TestCafe.js
+++ b/lib/helper/TestCafe.js
@@ -86,7 +86,6 @@ class TestCafe extends Helper {
   constructor(config) {
     super(config);
 
-    this.iteration = 1;
     this.testcafe = undefined; // testcafe instance
     this.t = undefined; // testcafe test controller
     this.dummyTestcafeFile; // generated testcafe test file
@@ -136,7 +135,7 @@ class TestCafe extends Helper {
 
     this.iteration += 2; // Use different ports for each test run
     // @ts-ignore
-    this.testcafe = await createTestCafe('localhost', 1338 + this.iteration, 1339 + this.iteration);
+    this.testcafe = await createTestCafe('localhost', null, null);
 
     this.debugSection('_before', 'Starting testcafe browser...');
 
diff --git a/test/data/app/view/form/aria.php b/test/data/app/view/form/aria.php
new file mode 100644
index 00000000..3a9a5649
--- /dev/null
+++ b/test/data/app/view/form/aria.php
@@ -0,0 +1,136 @@
+<!doctype html>
+<html>
+    <head>
+      <title>Accessibility</title>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+      <style></style>
+    </head>
+    <body>
+    <form action="/form/complex" method="POST">
+    <div id="myBillingId">Billing</div>
+
+<div>
+    <div id="myNameId">Name</div>
+    <input type="text" name="my-form-name" aria-labelledby="myBillingId myNameId"/>
+</div>
+<div>
+    <div id="myAddressId">Address</div>
+    <input type="text" aria-label="My Address" name="my-form-address" aria-labelledby="myBillingId myAddressId"/>
+</div>
+
+<div id="myPhoneId">Phone</div>
+    <input type="text" name="my-form-phone" aria-labelledby="myPhoneId"/>
+    <input type="submit" value="Submit" />
+    </form>
+
+<div>
+    <div aria-label="get info" onclick="window.location.href='/info'">&raquo;</div>
+</div>
+
+    </body>
+</html>
diff --git a/test/helper/webapi.js b/test/helper/webapi.js
index ff0c2698..a7855296 100644
--- a/test/helper/webapi.js
+++ b/test/helper/webapi.js
@@ -213,6 +213,12 @@ module.exports.tests = function () {
       assert.ok(err);
     });
 
+    it('should should click by aria-label', async () => {
+      await I.amOnPage('/form/aria');
+      await I.click('get info');
+      await I.seeInCurrentUrl('/info');
+    });
+
     it('should click link with inner span', async () => {
       await I.amOnPage('/form/example7');
       await I.click('Buy Chocolate Bar');
@@ -455,6 +461,15 @@ module.exports.tests = function () {
       assert.equal(formContents('description'), 'Nothing special');
     });
 
+    it('should fill input by aria-label and aria-labelledby', async () => {
+      await I.amOnPage('/form/aria');
+      await I.fillField('My Address', 'Home Sweet Home');
+      await I.fillField('Phone', '123456');
+      await I.click('Submit');
+      assert.equal(formContents('my-form-phone'), '123456');
+      assert.equal(formContents('my-form-address'), 'Home Sweet Home');
+    });
+
     it('should fill textarea by overwritting the existing value', async () => {
       await I.amOnPage('/form/textarea');
       await I.fillField('Description', 'Nothing special');
diff --git a/test/unit/config_test.js b/test/unit/config_test.js
new file mode 100644
index 00000000..30ce1c15
--- /dev/null
+++ b/test/unit/config_test.js
@@ -0,0 +1,64 @@
+const config = require('../../lib/config');
+const { expect } = require('chai');
+
+describe('Config', () => {
+  beforeEach(() => config.reset());
+
+  it('should be created', () => {
+    const cfg = config.create({
+      output: './report',
+    });
+    expect(cfg).to.contain.keys(['helpers', 'plugins', 'include']);
+    expect(config.get()).to.eql(cfg);
+    expect(cfg.output).to.eql('./report');
+    expect(config.get('output')).to.eql('./report');
+    expect(config.get('output', './other')).to.eql('./report');
+    expect(config.get('tests', '**_test.js')).to.eql('**_test.js');
+  });
+
+
+  it('should be completely reset', () => {
+    config.addHook((cfg) => {
+      cfg.helpers.Puppeteer.show = true;
+    });
+    config.create({
+      tests: '**tests',
+      helpers: {
+        Puppeteer: {},
+      },
+    });
+    config.append({
+      output: './other',
+    });
+    expect(config.get('helpers').Puppeteer.show).to.eql(true);
+    config.reset();
+    expect(config.get().output).to.not.eql('./other');
+    expect(config.get()).to.not.contain.key('tests');
+    expect(config.get('helpers')).to.not.contain.key('Puppeteer');
+    config.create({
+      helpers: {
+        Puppeteer: {},
+      },
+    });
+    expect(config.get('helpers').Puppeteer.show).to.not.eql(true);
+  });
+
+  it('can be updated', () => {
+    config.create();
+    config.append({
+      output: './other',
+    });
+    expect(config.get('output')).to.eql('./other');
+  });
+
+  it('should use config hooks to enhance configs', () => {
+    config.addHook((cfg) => {
+      cfg.additionalValue = true;
+    });
+    const cfg = config.create({
+      additionalValue: false,
+    });
+    expect(cfg).to.contain.key('additionalValue');
+    expect(cfg.additionalValue).to.eql(true);
+  });
+});