Repository: Codeception/CodeceptJS
Pull request #2289 (2020-03-29 11:21:13)

Release 2.6
Bringing all changes + adding documentation

DavertMik: 
![Codacy](https://app.codacy.com/assets/images/favicon.png) Here is an overview of what got changed by this pull request:


```diff

Issues
======
- Added 1
           

Clones added
============
- test/runner/comment_step_test.js  1
         
```


See the complete overview on [Codacy](https://app.codacy.com/manual/DavertMik/CodeceptJS/pullRequest?prid=5264126&bid=17245758)
        
github-actions[bot]: 🌀 Tests overview by [Testomatio](https://testomat.io)


Found **957** mocha tests in 74 files 
#### ✔️ Added 3 tests


```diff

+ Playwright - Emulation: should open page as iPhone 
+ CodeceptJS commentStep plugin: should print nested steps when global var comments used
+ CodeceptJS commentStep plugin: should print nested steps when local var comments used
```



<details>
  <summary>⚠️ List all skipped tests (1)</summary>

* [~~should resize window to maximum screen dimensions~~](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/helper/WebDriver_test.js#L786)

</details>



<details>
  <summary>📎 List all suites (62)</summary>


* **GraphQL (9 tests)** [test/graphql/GraphQL_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/graphql/GraphQL_test.js)
* **GraphQLDataFactory (6 tests)** [test/graphql/GraphQLDataFactory_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/graphql/GraphQLDataFactory_test.js)
* **Appium (73 tests)** [test/helper/Appium_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/helper/Appium_test.js)
* **Appium Web (16 tests)** [test/helper/AppiumWeb_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/helper/AppiumWeb_test.js)
* **Nightmare (17 tests)** [test/helper/Nightmare_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/helper/Nightmare_test.js)
* **Playwright (90 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/helper/Playwright_test.js)
* **#_startBrowser (2 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/helper/Playwright_test.js)
* **Playwright - BasicAuth (1 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/helper/Playwright_test.js)
* **Playwright - Emulation (1 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/helper/Playwright_test.js)
* **Protractor (114 tests)** [test/helper/Protractor_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/helper/Protractor_test.js)
* **Protractor-NonAngular (47 tests)** [test/helper/ProtractorWeb_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/helper/ProtractorWeb_test.js)
* **Puppeteer - BasicAuth (2 tests)** [test/helper/Puppeteer_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/helper/Puppeteer_test.js)
* **Puppeteer (96 tests)** [test/helper/Puppeteer_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/helper/Puppeteer_test.js)
* **Puppeteer (remote browser) (2 tests)** [test/helper/Puppeteer_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/helper/Puppeteer_test.js)
* **TestCafe (5 tests)** [test/helper/TestCafe_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/helper/TestCafe_test.js)
* **WebDriver (202 tests)** [test/helper/WebDriver_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/helper/WebDriver_test.js)
* **WebDriver - Basic Authentication (1 tests)** [test/helper/WebDriver_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/helper/WebDriver_test.js)
* **WebDriverIO (94 tests)** [test/helper/WebDriverIO_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/helper/WebDriverIO_test.js)
* **ApiDataFactory (10 tests)** [test/rest/ApiDataFactory_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/rest/ApiDataFactory_test.js)
* **REST (17 tests)** [test/rest/REST_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/rest/REST_test.js)
* **CodeceptJS Allure Plugin (4 tests)** [test/runner/allure_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/runner/allure_test.js)
* **BDD Gherkin (16 tests)** [test/runner/bdd_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/runner/bdd_test.js)
* **CodeceptJS Bootstrap and Teardown (18 tests)** [test/runner/bootstrap_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/runner/bootstrap_test.js)
* **CodeceptJS Runner (24 tests)** [test/runner/codecept_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/runner/codecept_test.js)
* **Codeceptjs Events (2 tests)** [test/runner/codecept_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/runner/codecept_test.js)
* **CodeceptJS commentStep plugin (2 tests)** [test/runner/comment_step_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/runner/comment_step_test.js)
* **Definitions (9 tests)** [test/runner/definitions_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/runner/definitions_test.js)
* **dry-run command (11 tests)** [test/runner/dry_run_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/runner/dry_run_test.js)
* **CodeceptJS Interface (16 tests)** [test/runner/interface_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/runner/interface_test.js)
* **list commands (1 tests)** [test/runner/list_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/runner/list_test.js)
* **CodeceptJS Interface (16 tests)** [test/runner/pageobject_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/runner/pageobject_test.js)
* **CodeceptJS Multiple Runner (20 tests)** [test/runner/run_multiple_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/runner/run_multiple_test.js)
* **run-rerun command (5 tests)** [test/runner/run_rerun_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/runner/run_rerun_test.js)
* **CodeceptJS Workers Runner (8 tests)** [test/runner/run_workers_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/runner/run_workers_test.js)
* **CodeceptJS session (3 tests)** [test/runner/session_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/runner/session_test.js)
* **Todo (3 tests)** [test/runner/todo_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/runner/todo_test.js)
* **Translation (1 tests)** [test/runner/translation_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/runner/translation_test.js)
* **CodeceptJS within (3 tests)** [test/runner/within_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/runner/within_test.js)
* **Actor (6 tests)** [test/unit/actor_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/unit/actor_test.js)
* **Assertion (2 tests)** [test/unit/assert_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/unit/assert_test.js)
* **empty assertion (4 tests)** [test/unit/assert/empty_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/unit/assert/empty_test.js)
* **equal assertion (8 tests)** [test/unit/assert/equal_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/unit/assert/equal_test.js)
* **equal assertion (8 tests)** [test/unit/assert/include_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/unit/assert/include_test.js)
* **BDD (28 tests)** [test/unit/bdd_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/unit/bdd_test.js)
* **Config (4 tests)** [test/unit/config_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/unit/config_test.js)
* **Container (20 tests)** [test/unit/container_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/unit/container_test.js)
* **DataTableArgument (3 tests)** [test/unit/data/dataTableArgument_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/unit/data/dataTableArgument_test.js)
* **DataTable (10 tests)** [test/unit/data/table_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/unit/data/table_test.js)
* **ui (22 tests)** [test/unit/data/ui_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/unit/data/ui_test.js)
* **ElementNotFound error (5 tests)** [test/unit/helper/element_not_found_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/unit/helper/element_not_found_test.js)
* **FileSystem (4 tests)** [test/unit/helper/FileSystem_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/unit/helper/FileSystem_test.js)
* **Locator (22 tests)** [test/unit/locator_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/unit/locator_test.js)
* **Output (3 tests)** [test/unit/output_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/unit/output_test.js)
* **parser (2 tests)** [test/unit/parser_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/unit/parser_test.js)
* **customLocator (4 tests)** [test/unit/plugin/customLocator_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/unit/plugin/customLocator_test.js)
* **retryFailedStep (6 tests)** [test/unit/plugin/retryFailedStep_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/unit/plugin/retryFailedStep_test.js)
* **screenshotOnFail (3 tests)** [test/unit/plugin/screenshotOnFail_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/unit/plugin/screenshotOnFail_test.js)
* **Recorder (7 tests)** [test/unit/recorder_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/unit/recorder_test.js)
* **Scenario (5 tests)** [test/unit/scenario_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/unit/scenario_test.js)
* **Step (6 tests)** [test/unit/steps_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/unit/steps_test.js)
* **ui (22 tests)** [test/unit/ui_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/unit/ui_test.js)
* **utils (22 tests)** [test/unit/utils_test.js](https://github.com/Codeception/CodeceptJS/tree/f26852d4a0e2d2463bf827c337551ff5f852b9da/test/unit/utils_test.js)

</details>


DavertMik: 
![Codacy](https://app.codacy.com/assets/images/favicon.png) Here is an overview of what got changed by this pull request:


```diff

Clones added
============
- test/helper/Playwright_test.js  3
- test/runner/comment_step_test.js  1
- lib/helper/WebDriver.js  22
         
```


See the complete overview on [Codacy](https://app.codacy.com/manual/DavertMik/CodeceptJS/pullRequest?prid=5264126&bid=17245758)
        
github-actions[bot]: 🌀 Tests overview by [Testomatio](https://testomat.io)


Found **957** mocha tests in 74 files 
#### ✔️ Added 3 tests


```diff

+ Playwright - Emulation: should open page as iPhone 
+ CodeceptJS commentStep plugin: should print nested steps when global var comments used
+ CodeceptJS commentStep plugin: should print nested steps when local var comments used
```



<details>
  <summary>⚠️ List all skipped tests (1)</summary>

* [~~should resize window to maximum screen dimensions~~](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/helper/WebDriver_test.js#L786)

</details>



<details>
  <summary>📎 List all suites (62)</summary>


* **GraphQL (9 tests)** [test/graphql/GraphQL_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/graphql/GraphQL_test.js)
* **GraphQLDataFactory (6 tests)** [test/graphql/GraphQLDataFactory_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/graphql/GraphQLDataFactory_test.js)
* **Appium (73 tests)** [test/helper/Appium_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/helper/Appium_test.js)
* **Appium Web (16 tests)** [test/helper/AppiumWeb_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/helper/AppiumWeb_test.js)
* **Nightmare (17 tests)** [test/helper/Nightmare_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/helper/Nightmare_test.js)
* **Playwright (90 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/helper/Playwright_test.js)
* **#_startBrowser (2 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/helper/Playwright_test.js)
* **Playwright - BasicAuth (1 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/helper/Playwright_test.js)
* **Playwright - Emulation (1 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/helper/Playwright_test.js)
* **Protractor (114 tests)** [test/helper/Protractor_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/helper/Protractor_test.js)
* **Protractor-NonAngular (47 tests)** [test/helper/ProtractorWeb_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/helper/ProtractorWeb_test.js)
* **Puppeteer - BasicAuth (2 tests)** [test/helper/Puppeteer_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/helper/Puppeteer_test.js)
* **Puppeteer (96 tests)** [test/helper/Puppeteer_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/helper/Puppeteer_test.js)
* **Puppeteer (remote browser) (2 tests)** [test/helper/Puppeteer_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/helper/Puppeteer_test.js)
* **TestCafe (5 tests)** [test/helper/TestCafe_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/helper/TestCafe_test.js)
* **WebDriver (202 tests)** [test/helper/WebDriver_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/helper/WebDriver_test.js)
* **WebDriver - Basic Authentication (1 tests)** [test/helper/WebDriver_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/helper/WebDriver_test.js)
* **WebDriverIO (94 tests)** [test/helper/WebDriverIO_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/helper/WebDriverIO_test.js)
* **ApiDataFactory (10 tests)** [test/rest/ApiDataFactory_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/rest/ApiDataFactory_test.js)
* **REST (17 tests)** [test/rest/REST_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/rest/REST_test.js)
* **CodeceptJS Allure Plugin (4 tests)** [test/runner/allure_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/runner/allure_test.js)
* **BDD Gherkin (16 tests)** [test/runner/bdd_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/runner/bdd_test.js)
* **CodeceptJS Bootstrap and Teardown (18 tests)** [test/runner/bootstrap_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/runner/bootstrap_test.js)
* **CodeceptJS Runner (24 tests)** [test/runner/codecept_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/runner/codecept_test.js)
* **Codeceptjs Events (2 tests)** [test/runner/codecept_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/runner/codecept_test.js)
* **CodeceptJS commentStep plugin (2 tests)** [test/runner/comment_step_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/runner/comment_step_test.js)
* **Definitions (9 tests)** [test/runner/definitions_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/runner/definitions_test.js)
* **dry-run command (11 tests)** [test/runner/dry_run_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/runner/dry_run_test.js)
* **CodeceptJS Interface (16 tests)** [test/runner/interface_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/runner/interface_test.js)
* **list commands (1 tests)** [test/runner/list_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/runner/list_test.js)
* **CodeceptJS Interface (16 tests)** [test/runner/pageobject_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/runner/pageobject_test.js)
* **CodeceptJS Multiple Runner (20 tests)** [test/runner/run_multiple_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/runner/run_multiple_test.js)
* **run-rerun command (5 tests)** [test/runner/run_rerun_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/runner/run_rerun_test.js)
* **CodeceptJS Workers Runner (8 tests)** [test/runner/run_workers_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/runner/run_workers_test.js)
* **CodeceptJS session (3 tests)** [test/runner/session_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/runner/session_test.js)
* **Todo (3 tests)** [test/runner/todo_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/runner/todo_test.js)
* **Translation (1 tests)** [test/runner/translation_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/runner/translation_test.js)
* **CodeceptJS within (3 tests)** [test/runner/within_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/runner/within_test.js)
* **Actor (6 tests)** [test/unit/actor_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/unit/actor_test.js)
* **Assertion (2 tests)** [test/unit/assert_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/unit/assert_test.js)
* **empty assertion (4 tests)** [test/unit/assert/empty_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/unit/assert/empty_test.js)
* **equal assertion (8 tests)** [test/unit/assert/equal_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/unit/assert/equal_test.js)
* **equal assertion (8 tests)** [test/unit/assert/include_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/unit/assert/include_test.js)
* **BDD (28 tests)** [test/unit/bdd_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/unit/bdd_test.js)
* **Config (4 tests)** [test/unit/config_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/unit/config_test.js)
* **Container (20 tests)** [test/unit/container_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/unit/container_test.js)
* **DataTableArgument (3 tests)** [test/unit/data/dataTableArgument_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/unit/data/dataTableArgument_test.js)
* **DataTable (10 tests)** [test/unit/data/table_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/unit/data/table_test.js)
* **ui (22 tests)** [test/unit/data/ui_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/unit/data/ui_test.js)
* **ElementNotFound error (5 tests)** [test/unit/helper/element_not_found_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/unit/helper/element_not_found_test.js)
* **FileSystem (4 tests)** [test/unit/helper/FileSystem_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/unit/helper/FileSystem_test.js)
* **Locator (22 tests)** [test/unit/locator_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/unit/locator_test.js)
* **Output (3 tests)** [test/unit/output_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/unit/output_test.js)
* **parser (2 tests)** [test/unit/parser_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/unit/parser_test.js)
* **customLocator (4 tests)** [test/unit/plugin/customLocator_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/unit/plugin/customLocator_test.js)
* **retryFailedStep (6 tests)** [test/unit/plugin/retryFailedStep_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/unit/plugin/retryFailedStep_test.js)
* **screenshotOnFail (3 tests)** [test/unit/plugin/screenshotOnFail_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/unit/plugin/screenshotOnFail_test.js)
* **Recorder (7 tests)** [test/unit/recorder_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/unit/recorder_test.js)
* **Scenario (5 tests)** [test/unit/scenario_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/unit/scenario_test.js)
* **Step (6 tests)** [test/unit/steps_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/unit/steps_test.js)
* **ui (22 tests)** [test/unit/ui_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/unit/ui_test.js)
* **utils (22 tests)** [test/unit/utils_test.js](https://github.com/Codeception/CodeceptJS/tree/482d211eed165c323a670f28b2063748b5975cce/test/unit/utils_test.js)

</details>


DavertMik: 
![Codacy](https://app.codacy.com/assets/images/favicon.png) Here is an overview of what got changed by this pull request:


```diff

Clones added
============
- test/helper/Playwright_test.js  3
- lib/helper/WebDriver.js  22
- test/runner/comment_step_test.js  1
         
```


See the complete overview on [Codacy](https://app.codacy.com/manual/DavertMik/CodeceptJS/pullRequest?prid=5264126&bid=17245758)
        
github-actions[bot]: 🌀 Tests overview by [Testomatio](https://testomat.io)


Found **956** mocha tests in 74 files 
#### ✔️ Added 3 tests


```diff

+ Playwright - Emulation: should open page as iPhone 
+ CodeceptJS commentStep plugin: should print nested steps when global var comments used
+ CodeceptJS commentStep plugin: should print nested steps when local var comments used
```


#### 🗑️ Removed 1 tests


```diff

- Playwright: open page : #amOnPage: should be unauthenticated 
```



<details>
  <summary>⚠️ List all skipped tests (1)</summary>

* [~~should resize window to maximum screen dimensions~~](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/helper/WebDriver_test.js#L786)

</details>



<details>
  <summary>📎 List all suites (62)</summary>


* **GraphQL (9 tests)** [test/graphql/GraphQL_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/graphql/GraphQL_test.js)
* **GraphQLDataFactory (6 tests)** [test/graphql/GraphQLDataFactory_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/graphql/GraphQLDataFactory_test.js)
* **Appium (73 tests)** [test/helper/Appium_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/helper/Appium_test.js)
* **Appium Web (16 tests)** [test/helper/AppiumWeb_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/helper/AppiumWeb_test.js)
* **Nightmare (17 tests)** [test/helper/Nightmare_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/helper/Nightmare_test.js)
* **Playwright (89 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/helper/Playwright_test.js)
* **#_startBrowser (2 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/helper/Playwright_test.js)
* **Playwright - BasicAuth (1 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/helper/Playwright_test.js)
* **Playwright - Emulation (1 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/helper/Playwright_test.js)
* **Protractor (114 tests)** [test/helper/Protractor_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/helper/Protractor_test.js)
* **Protractor-NonAngular (47 tests)** [test/helper/ProtractorWeb_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/helper/ProtractorWeb_test.js)
* **Puppeteer - BasicAuth (2 tests)** [test/helper/Puppeteer_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/helper/Puppeteer_test.js)
* **Puppeteer (96 tests)** [test/helper/Puppeteer_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/helper/Puppeteer_test.js)
* **Puppeteer (remote browser) (2 tests)** [test/helper/Puppeteer_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/helper/Puppeteer_test.js)
* **TestCafe (5 tests)** [test/helper/TestCafe_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/helper/TestCafe_test.js)
* **WebDriver (202 tests)** [test/helper/WebDriver_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/helper/WebDriver_test.js)
* **WebDriver - Basic Authentication (1 tests)** [test/helper/WebDriver_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/helper/WebDriver_test.js)
* **WebDriverIO (94 tests)** [test/helper/WebDriverIO_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/helper/WebDriverIO_test.js)
* **ApiDataFactory (10 tests)** [test/rest/ApiDataFactory_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/rest/ApiDataFactory_test.js)
* **REST (17 tests)** [test/rest/REST_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/rest/REST_test.js)
* **CodeceptJS Allure Plugin (4 tests)** [test/runner/allure_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/runner/allure_test.js)
* **BDD Gherkin (16 tests)** [test/runner/bdd_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/runner/bdd_test.js)
* **CodeceptJS Bootstrap and Teardown (18 tests)** [test/runner/bootstrap_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/runner/bootstrap_test.js)
* **CodeceptJS Runner (24 tests)** [test/runner/codecept_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/runner/codecept_test.js)
* **Codeceptjs Events (2 tests)** [test/runner/codecept_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/runner/codecept_test.js)
* **CodeceptJS commentStep plugin (2 tests)** [test/runner/comment_step_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/runner/comment_step_test.js)
* **Definitions (9 tests)** [test/runner/definitions_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/runner/definitions_test.js)
* **dry-run command (11 tests)** [test/runner/dry_run_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/runner/dry_run_test.js)
* **CodeceptJS Interface (16 tests)** [test/runner/interface_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/runner/interface_test.js)
* **list commands (1 tests)** [test/runner/list_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/runner/list_test.js)
* **CodeceptJS Interface (16 tests)** [test/runner/pageobject_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/runner/pageobject_test.js)
* **CodeceptJS Multiple Runner (20 tests)** [test/runner/run_multiple_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/runner/run_multiple_test.js)
* **run-rerun command (5 tests)** [test/runner/run_rerun_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/runner/run_rerun_test.js)
* **CodeceptJS Workers Runner (8 tests)** [test/runner/run_workers_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/runner/run_workers_test.js)
* **CodeceptJS session (3 tests)** [test/runner/session_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/runner/session_test.js)
* **Todo (3 tests)** [test/runner/todo_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/runner/todo_test.js)
* **Translation (1 tests)** [test/runner/translation_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/runner/translation_test.js)
* **CodeceptJS within (3 tests)** [test/runner/within_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/runner/within_test.js)
* **Actor (6 tests)** [test/unit/actor_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/unit/actor_test.js)
* **Assertion (2 tests)** [test/unit/assert_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/unit/assert_test.js)
* **empty assertion (4 tests)** [test/unit/assert/empty_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/unit/assert/empty_test.js)
* **equal assertion (8 tests)** [test/unit/assert/equal_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/unit/assert/equal_test.js)
* **equal assertion (8 tests)** [test/unit/assert/include_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/unit/assert/include_test.js)
* **BDD (28 tests)** [test/unit/bdd_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/unit/bdd_test.js)
* **Config (4 tests)** [test/unit/config_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/unit/config_test.js)
* **Container (20 tests)** [test/unit/container_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/unit/container_test.js)
* **DataTableArgument (3 tests)** [test/unit/data/dataTableArgument_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/unit/data/dataTableArgument_test.js)
* **DataTable (10 tests)** [test/unit/data/table_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/unit/data/table_test.js)
* **ui (22 tests)** [test/unit/data/ui_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/unit/data/ui_test.js)
* **ElementNotFound error (5 tests)** [test/unit/helper/element_not_found_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/unit/helper/element_not_found_test.js)
* **FileSystem (4 tests)** [test/unit/helper/FileSystem_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/unit/helper/FileSystem_test.js)
* **Locator (22 tests)** [test/unit/locator_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/unit/locator_test.js)
* **Output (3 tests)** [test/unit/output_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/unit/output_test.js)
* **parser (2 tests)** [test/unit/parser_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/unit/parser_test.js)
* **customLocator (4 tests)** [test/unit/plugin/customLocator_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/unit/plugin/customLocator_test.js)
* **retryFailedStep (6 tests)** [test/unit/plugin/retryFailedStep_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/unit/plugin/retryFailedStep_test.js)
* **screenshotOnFail (3 tests)** [test/unit/plugin/screenshotOnFail_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/unit/plugin/screenshotOnFail_test.js)
* **Recorder (7 tests)** [test/unit/recorder_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/unit/recorder_test.js)
* **Scenario (5 tests)** [test/unit/scenario_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/unit/scenario_test.js)
* **Step (6 tests)** [test/unit/steps_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/unit/steps_test.js)
* **ui (22 tests)** [test/unit/ui_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/unit/ui_test.js)
* **utils (22 tests)** [test/unit/utils_test.js](https://github.com/Codeception/CodeceptJS/tree/3e1953f5aa9119513d7ecfaeefe03599f72909f0/test/unit/utils_test.js)

</details>


DavertMik: 
![Codacy](https://app.codacy.com/assets/images/favicon.png) Here is an overview of what got changed by this pull request:


```diff

Clones added
============
- test/runner/comment_step_test.js  1
- lib/helper/WebDriver.js  22
- test/helper/Playwright_test.js  3
         
```


See the complete overview on [Codacy](https://app.codacy.com/manual/DavertMik/CodeceptJS/pullRequest?prid=5264126&bid=17245758)
        
github-actions[bot]: 🌀 Tests overview by [Testomatio](https://testomat.io)


Found **947** mocha tests in 74 files 
#### ✔️ Added 3 tests


```diff

+ Playwright - Emulation: should open page as iPhone 
+ CodeceptJS commentStep plugin: should print nested steps when global var comments used
+ CodeceptJS commentStep plugin: should print nested steps when local var comments used
```


#### 🗑️ Removed 10 tests


```diff

- Playwright: open page : #amOnPage: should be unauthenticated 
- Playwright: #waitForClickable: should wait for clickable
- Playwright: #waitForClickable: should wait for clickable by XPath
- Playwright: #waitForClickable: should fail for disabled element
- Playwright: #waitForClickable: should fail for disabled element by XPath
- Playwright: #waitForClickable: should fail for element not in viewport by top
- Playwright: #waitForClickable: should fail for element not in viewport by bottom
- Playwright: #waitForClickable: should fail for element not in viewport by left
- Playwright: #waitForClickable: should fail for element not in viewport by right
- Playwright: #waitForClickable: should fail for overlapping element
```



<details>
  <summary>⚠️ List all skipped tests (1)</summary>

* [~~should resize window to maximum screen dimensions~~](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/helper/WebDriver_test.js#L786)

</details>



<details>
  <summary>📎 List all suites (62)</summary>


* **GraphQL (9 tests)** [test/graphql/GraphQL_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/graphql/GraphQL_test.js)
* **GraphQLDataFactory (6 tests)** [test/graphql/GraphQLDataFactory_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/graphql/GraphQLDataFactory_test.js)
* **Appium (73 tests)** [test/helper/Appium_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/helper/Appium_test.js)
* **Appium Web (16 tests)** [test/helper/AppiumWeb_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/helper/AppiumWeb_test.js)
* **Nightmare (17 tests)** [test/helper/Nightmare_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/helper/Nightmare_test.js)
* **Playwright (80 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/helper/Playwright_test.js)
* **#_startBrowser (2 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/helper/Playwright_test.js)
* **Playwright - BasicAuth (1 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/helper/Playwright_test.js)
* **Playwright - Emulation (1 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/helper/Playwright_test.js)
* **Protractor (114 tests)** [test/helper/Protractor_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/helper/Protractor_test.js)
* **Protractor-NonAngular (47 tests)** [test/helper/ProtractorWeb_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/helper/ProtractorWeb_test.js)
* **Puppeteer - BasicAuth (2 tests)** [test/helper/Puppeteer_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/helper/Puppeteer_test.js)
* **Puppeteer (96 tests)** [test/helper/Puppeteer_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/helper/Puppeteer_test.js)
* **Puppeteer (remote browser) (2 tests)** [test/helper/Puppeteer_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/helper/Puppeteer_test.js)
* **TestCafe (5 tests)** [test/helper/TestCafe_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/helper/TestCafe_test.js)
* **WebDriver (202 tests)** [test/helper/WebDriver_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/helper/WebDriver_test.js)
* **WebDriver - Basic Authentication (1 tests)** [test/helper/WebDriver_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/helper/WebDriver_test.js)
* **WebDriverIO (94 tests)** [test/helper/WebDriverIO_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/helper/WebDriverIO_test.js)
* **ApiDataFactory (10 tests)** [test/rest/ApiDataFactory_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/rest/ApiDataFactory_test.js)
* **REST (17 tests)** [test/rest/REST_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/rest/REST_test.js)
* **CodeceptJS Allure Plugin (4 tests)** [test/runner/allure_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/runner/allure_test.js)
* **BDD Gherkin (16 tests)** [test/runner/bdd_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/runner/bdd_test.js)
* **CodeceptJS Bootstrap and Teardown (18 tests)** [test/runner/bootstrap_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/runner/bootstrap_test.js)
* **CodeceptJS Runner (24 tests)** [test/runner/codecept_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/runner/codecept_test.js)
* **Codeceptjs Events (2 tests)** [test/runner/codecept_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/runner/codecept_test.js)
* **CodeceptJS commentStep plugin (2 tests)** [test/runner/comment_step_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/runner/comment_step_test.js)
* **Definitions (9 tests)** [test/runner/definitions_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/runner/definitions_test.js)
* **dry-run command (11 tests)** [test/runner/dry_run_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/runner/dry_run_test.js)
* **CodeceptJS Interface (16 tests)** [test/runner/interface_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/runner/interface_test.js)
* **list commands (1 tests)** [test/runner/list_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/runner/list_test.js)
* **CodeceptJS Interface (16 tests)** [test/runner/pageobject_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/runner/pageobject_test.js)
* **CodeceptJS Multiple Runner (20 tests)** [test/runner/run_multiple_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/runner/run_multiple_test.js)
* **run-rerun command (5 tests)** [test/runner/run_rerun_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/runner/run_rerun_test.js)
* **CodeceptJS Workers Runner (8 tests)** [test/runner/run_workers_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/runner/run_workers_test.js)
* **CodeceptJS session (3 tests)** [test/runner/session_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/runner/session_test.js)
* **Todo (3 tests)** [test/runner/todo_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/runner/todo_test.js)
* **Translation (1 tests)** [test/runner/translation_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/runner/translation_test.js)
* **CodeceptJS within (3 tests)** [test/runner/within_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/runner/within_test.js)
* **Actor (6 tests)** [test/unit/actor_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/unit/actor_test.js)
* **Assertion (2 tests)** [test/unit/assert_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/unit/assert_test.js)
* **empty assertion (4 tests)** [test/unit/assert/empty_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/unit/assert/empty_test.js)
* **equal assertion (8 tests)** [test/unit/assert/equal_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/unit/assert/equal_test.js)
* **equal assertion (8 tests)** [test/unit/assert/include_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/unit/assert/include_test.js)
* **BDD (28 tests)** [test/unit/bdd_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/unit/bdd_test.js)
* **Config (4 tests)** [test/unit/config_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/unit/config_test.js)
* **Container (20 tests)** [test/unit/container_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/unit/container_test.js)
* **DataTableArgument (3 tests)** [test/unit/data/dataTableArgument_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/unit/data/dataTableArgument_test.js)
* **DataTable (10 tests)** [test/unit/data/table_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/unit/data/table_test.js)
* **ui (22 tests)** [test/unit/data/ui_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/unit/data/ui_test.js)
* **ElementNotFound error (5 tests)** [test/unit/helper/element_not_found_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/unit/helper/element_not_found_test.js)
* **FileSystem (4 tests)** [test/unit/helper/FileSystem_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/unit/helper/FileSystem_test.js)
* **Locator (22 tests)** [test/unit/locator_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/unit/locator_test.js)
* **Output (3 tests)** [test/unit/output_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/unit/output_test.js)
* **parser (2 tests)** [test/unit/parser_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/unit/parser_test.js)
* **customLocator (4 tests)** [test/unit/plugin/customLocator_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/unit/plugin/customLocator_test.js)
* **retryFailedStep (6 tests)** [test/unit/plugin/retryFailedStep_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/unit/plugin/retryFailedStep_test.js)
* **screenshotOnFail (3 tests)** [test/unit/plugin/screenshotOnFail_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/unit/plugin/screenshotOnFail_test.js)
* **Recorder (7 tests)** [test/unit/recorder_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/unit/recorder_test.js)
* **Scenario (5 tests)** [test/unit/scenario_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/unit/scenario_test.js)
* **Step (6 tests)** [test/unit/steps_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/unit/steps_test.js)
* **ui (22 tests)** [test/unit/ui_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/unit/ui_test.js)
* **utils (22 tests)** [test/unit/utils_test.js](https://github.com/Codeception/CodeceptJS/tree/54a0d16aabd31a739d80fad05e5415193eecc892/test/unit/utils_test.js)

</details>


gkushang: @DavertMik should the below PR (shadow dom support) go into this release too?

https://github.com/Codeception/CodeceptJS/pull/2276
github-actions[bot]: 🌀 Tests overview by [Testomatio](https://testomat.io)


Found **947** mocha tests in 74 files 
#### ✔️ Added 3 tests


```diff

+ Playwright - Emulation: should open page as iPhone 
+ CodeceptJS commentStep plugin: should print nested steps when global var comments used
+ CodeceptJS commentStep plugin: should print nested steps when local var comments used
```


#### 🗑️ Removed 10 tests


```diff

- Playwright: open page : #amOnPage: should be unauthenticated 
- Playwright: #waitForClickable: should wait for clickable
- Playwright: #waitForClickable: should wait for clickable by XPath
- Playwright: #waitForClickable: should fail for disabled element
- Playwright: #waitForClickable: should fail for disabled element by XPath
- Playwright: #waitForClickable: should fail for element not in viewport by top
- Playwright: #waitForClickable: should fail for element not in viewport by bottom
- Playwright: #waitForClickable: should fail for element not in viewport by left
- Playwright: #waitForClickable: should fail for element not in viewport by right
- Playwright: #waitForClickable: should fail for overlapping element
```



<details>
  <summary>⚠️ List all skipped tests (1)</summary>

* [~~should resize window to maximum screen dimensions~~](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/helper/WebDriver_test.js#L786)

</details>



<details>
  <summary>📎 List all suites (62)</summary>


* **GraphQL (9 tests)** [test/graphql/GraphQL_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/graphql/GraphQL_test.js)
* **GraphQLDataFactory (6 tests)** [test/graphql/GraphQLDataFactory_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/graphql/GraphQLDataFactory_test.js)
* **Appium (73 tests)** [test/helper/Appium_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/helper/Appium_test.js)
* **Appium Web (16 tests)** [test/helper/AppiumWeb_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/helper/AppiumWeb_test.js)
* **Nightmare (17 tests)** [test/helper/Nightmare_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/helper/Nightmare_test.js)
* **Playwright (80 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/helper/Playwright_test.js)
* **#_startBrowser (2 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/helper/Playwright_test.js)
* **Playwright - BasicAuth (1 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/helper/Playwright_test.js)
* **Playwright - Emulation (1 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/helper/Playwright_test.js)
* **Protractor (114 tests)** [test/helper/Protractor_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/helper/Protractor_test.js)
* **Protractor-NonAngular (47 tests)** [test/helper/ProtractorWeb_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/helper/ProtractorWeb_test.js)
* **Puppeteer - BasicAuth (2 tests)** [test/helper/Puppeteer_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/helper/Puppeteer_test.js)
* **Puppeteer (96 tests)** [test/helper/Puppeteer_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/helper/Puppeteer_test.js)
* **Puppeteer (remote browser) (2 tests)** [test/helper/Puppeteer_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/helper/Puppeteer_test.js)
* **TestCafe (5 tests)** [test/helper/TestCafe_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/helper/TestCafe_test.js)
* **WebDriver (202 tests)** [test/helper/WebDriver_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/helper/WebDriver_test.js)
* **WebDriver - Basic Authentication (1 tests)** [test/helper/WebDriver_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/helper/WebDriver_test.js)
* **WebDriverIO (94 tests)** [test/helper/WebDriverIO_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/helper/WebDriverIO_test.js)
* **ApiDataFactory (10 tests)** [test/rest/ApiDataFactory_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/rest/ApiDataFactory_test.js)
* **REST (17 tests)** [test/rest/REST_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/rest/REST_test.js)
* **CodeceptJS Allure Plugin (4 tests)** [test/runner/allure_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/runner/allure_test.js)
* **BDD Gherkin (16 tests)** [test/runner/bdd_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/runner/bdd_test.js)
* **CodeceptJS Bootstrap and Teardown (18 tests)** [test/runner/bootstrap_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/runner/bootstrap_test.js)
* **CodeceptJS Runner (24 tests)** [test/runner/codecept_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/runner/codecept_test.js)
* **Codeceptjs Events (2 tests)** [test/runner/codecept_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/runner/codecept_test.js)
* **CodeceptJS commentStep plugin (2 tests)** [test/runner/comment_step_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/runner/comment_step_test.js)
* **Definitions (9 tests)** [test/runner/definitions_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/runner/definitions_test.js)
* **dry-run command (11 tests)** [test/runner/dry_run_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/runner/dry_run_test.js)
* **CodeceptJS Interface (16 tests)** [test/runner/interface_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/runner/interface_test.js)
* **list commands (1 tests)** [test/runner/list_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/runner/list_test.js)
* **CodeceptJS Interface (16 tests)** [test/runner/pageobject_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/runner/pageobject_test.js)
* **CodeceptJS Multiple Runner (20 tests)** [test/runner/run_multiple_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/runner/run_multiple_test.js)
* **run-rerun command (5 tests)** [test/runner/run_rerun_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/runner/run_rerun_test.js)
* **CodeceptJS Workers Runner (8 tests)** [test/runner/run_workers_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/runner/run_workers_test.js)
* **CodeceptJS session (3 tests)** [test/runner/session_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/runner/session_test.js)
* **Todo (3 tests)** [test/runner/todo_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/runner/todo_test.js)
* **Translation (1 tests)** [test/runner/translation_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/runner/translation_test.js)
* **CodeceptJS within (3 tests)** [test/runner/within_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/runner/within_test.js)
* **Actor (6 tests)** [test/unit/actor_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/unit/actor_test.js)
* **Assertion (2 tests)** [test/unit/assert_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/unit/assert_test.js)
* **empty assertion (4 tests)** [test/unit/assert/empty_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/unit/assert/empty_test.js)
* **equal assertion (8 tests)** [test/unit/assert/equal_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/unit/assert/equal_test.js)
* **equal assertion (8 tests)** [test/unit/assert/include_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/unit/assert/include_test.js)
* **BDD (28 tests)** [test/unit/bdd_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/unit/bdd_test.js)
* **Config (4 tests)** [test/unit/config_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/unit/config_test.js)
* **Container (20 tests)** [test/unit/container_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/unit/container_test.js)
* **DataTableArgument (3 tests)** [test/unit/data/dataTableArgument_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/unit/data/dataTableArgument_test.js)
* **DataTable (10 tests)** [test/unit/data/table_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/unit/data/table_test.js)
* **ui (22 tests)** [test/unit/data/ui_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/unit/data/ui_test.js)
* **ElementNotFound error (5 tests)** [test/unit/helper/element_not_found_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/unit/helper/element_not_found_test.js)
* **FileSystem (4 tests)** [test/unit/helper/FileSystem_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/unit/helper/FileSystem_test.js)
* **Locator (22 tests)** [test/unit/locator_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/unit/locator_test.js)
* **Output (3 tests)** [test/unit/output_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/unit/output_test.js)
* **parser (2 tests)** [test/unit/parser_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/unit/parser_test.js)
* **customLocator (4 tests)** [test/unit/plugin/customLocator_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/unit/plugin/customLocator_test.js)
* **retryFailedStep (6 tests)** [test/unit/plugin/retryFailedStep_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/unit/plugin/retryFailedStep_test.js)
* **screenshotOnFail (3 tests)** [test/unit/plugin/screenshotOnFail_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/unit/plugin/screenshotOnFail_test.js)
* **Recorder (7 tests)** [test/unit/recorder_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/unit/recorder_test.js)
* **Scenario (5 tests)** [test/unit/scenario_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/unit/scenario_test.js)
* **Step (6 tests)** [test/unit/steps_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/unit/steps_test.js)
* **ui (22 tests)** [test/unit/ui_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/unit/ui_test.js)
* **utils (22 tests)** [test/unit/utils_test.js](https://github.com/Codeception/CodeceptJS/tree/027447c949abd60c5aeeac92848c9e6331822cd4/test/unit/utils_test.js)

</details>


DavertMik: 
![Codacy](https://app.codacy.com/assets/images/favicon.png) Here is an overview of what got changed by this pull request:


```diff

Clones added
============
- test/runner/comment_step_test.js  1
- test/helper/Playwright_test.js  2
- lib/helper/WebDriver.js  22
         
```


See the complete overview on [Codacy](https://app.codacy.com/manual/DavertMik/CodeceptJS/pullRequest?prid=5264126&bid=17245758)
        
DavertMik: @gkushang Yes! I am writing release notes for shadow dom support right now
github-actions[bot]: 🌀 Tests overview by [Testomatio](https://testomat.io)


Found **947** mocha tests in 74 files 
#### ✔️ Added 3 tests


```diff

+ Playwright - Emulation: should open page as iPhone 
+ CodeceptJS commentStep plugin: should print nested steps when global var comments used
+ CodeceptJS commentStep plugin: should print nested steps when local var comments used
```


#### 🗑️ Removed 10 tests


```diff

- Playwright: open page : #amOnPage: should be unauthenticated 
- Playwright: #waitForClickable: should wait for clickable
- Playwright: #waitForClickable: should wait for clickable by XPath
- Playwright: #waitForClickable: should fail for disabled element
- Playwright: #waitForClickable: should fail for disabled element by XPath
- Playwright: #waitForClickable: should fail for element not in viewport by top
- Playwright: #waitForClickable: should fail for element not in viewport by bottom
- Playwright: #waitForClickable: should fail for element not in viewport by left
- Playwright: #waitForClickable: should fail for element not in viewport by right
- Playwright: #waitForClickable: should fail for overlapping element
```



<details>
  <summary>⚠️ List all skipped tests (1)</summary>

* [~~should resize window to maximum screen dimensions~~](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/helper/WebDriver_test.js#L786)

</details>



<details>
  <summary>📎 List all suites (62)</summary>


* **GraphQL (9 tests)** [test/graphql/GraphQL_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/graphql/GraphQL_test.js)
* **GraphQLDataFactory (6 tests)** [test/graphql/GraphQLDataFactory_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/graphql/GraphQLDataFactory_test.js)
* **Appium (73 tests)** [test/helper/Appium_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/helper/Appium_test.js)
* **Appium Web (16 tests)** [test/helper/AppiumWeb_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/helper/AppiumWeb_test.js)
* **Nightmare (17 tests)** [test/helper/Nightmare_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/helper/Nightmare_test.js)
* **Playwright (80 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/helper/Playwright_test.js)
* **#_startBrowser (2 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/helper/Playwright_test.js)
* **Playwright - BasicAuth (1 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/helper/Playwright_test.js)
* **Playwright - Emulation (1 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/helper/Playwright_test.js)
* **Protractor (114 tests)** [test/helper/Protractor_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/helper/Protractor_test.js)
* **Protractor-NonAngular (47 tests)** [test/helper/ProtractorWeb_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/helper/ProtractorWeb_test.js)
* **Puppeteer - BasicAuth (2 tests)** [test/helper/Puppeteer_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/helper/Puppeteer_test.js)
* **Puppeteer (96 tests)** [test/helper/Puppeteer_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/helper/Puppeteer_test.js)
* **Puppeteer (remote browser) (2 tests)** [test/helper/Puppeteer_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/helper/Puppeteer_test.js)
* **TestCafe (5 tests)** [test/helper/TestCafe_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/helper/TestCafe_test.js)
* **WebDriver (202 tests)** [test/helper/WebDriver_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/helper/WebDriver_test.js)
* **WebDriver - Basic Authentication (1 tests)** [test/helper/WebDriver_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/helper/WebDriver_test.js)
* **WebDriverIO (94 tests)** [test/helper/WebDriverIO_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/helper/WebDriverIO_test.js)
* **ApiDataFactory (10 tests)** [test/rest/ApiDataFactory_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/rest/ApiDataFactory_test.js)
* **REST (17 tests)** [test/rest/REST_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/rest/REST_test.js)
* **CodeceptJS Allure Plugin (4 tests)** [test/runner/allure_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/runner/allure_test.js)
* **BDD Gherkin (16 tests)** [test/runner/bdd_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/runner/bdd_test.js)
* **CodeceptJS Bootstrap and Teardown (18 tests)** [test/runner/bootstrap_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/runner/bootstrap_test.js)
* **CodeceptJS Runner (24 tests)** [test/runner/codecept_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/runner/codecept_test.js)
* **Codeceptjs Events (2 tests)** [test/runner/codecept_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/runner/codecept_test.js)
* **CodeceptJS commentStep plugin (2 tests)** [test/runner/comment_step_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/runner/comment_step_test.js)
* **Definitions (9 tests)** [test/runner/definitions_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/runner/definitions_test.js)
* **dry-run command (11 tests)** [test/runner/dry_run_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/runner/dry_run_test.js)
* **CodeceptJS Interface (16 tests)** [test/runner/interface_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/runner/interface_test.js)
* **list commands (1 tests)** [test/runner/list_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/runner/list_test.js)
* **CodeceptJS Interface (16 tests)** [test/runner/pageobject_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/runner/pageobject_test.js)
* **CodeceptJS Multiple Runner (20 tests)** [test/runner/run_multiple_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/runner/run_multiple_test.js)
* **run-rerun command (5 tests)** [test/runner/run_rerun_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/runner/run_rerun_test.js)
* **CodeceptJS Workers Runner (8 tests)** [test/runner/run_workers_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/runner/run_workers_test.js)
* **CodeceptJS session (3 tests)** [test/runner/session_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/runner/session_test.js)
* **Todo (3 tests)** [test/runner/todo_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/runner/todo_test.js)
* **Translation (1 tests)** [test/runner/translation_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/runner/translation_test.js)
* **CodeceptJS within (3 tests)** [test/runner/within_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/runner/within_test.js)
* **Actor (6 tests)** [test/unit/actor_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/unit/actor_test.js)
* **Assertion (2 tests)** [test/unit/assert_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/unit/assert_test.js)
* **empty assertion (4 tests)** [test/unit/assert/empty_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/unit/assert/empty_test.js)
* **equal assertion (8 tests)** [test/unit/assert/equal_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/unit/assert/equal_test.js)
* **equal assertion (8 tests)** [test/unit/assert/include_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/unit/assert/include_test.js)
* **BDD (28 tests)** [test/unit/bdd_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/unit/bdd_test.js)
* **Config (4 tests)** [test/unit/config_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/unit/config_test.js)
* **Container (20 tests)** [test/unit/container_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/unit/container_test.js)
* **DataTableArgument (3 tests)** [test/unit/data/dataTableArgument_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/unit/data/dataTableArgument_test.js)
* **DataTable (10 tests)** [test/unit/data/table_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/unit/data/table_test.js)
* **ui (22 tests)** [test/unit/data/ui_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/unit/data/ui_test.js)
* **ElementNotFound error (5 tests)** [test/unit/helper/element_not_found_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/unit/helper/element_not_found_test.js)
* **FileSystem (4 tests)** [test/unit/helper/FileSystem_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/unit/helper/FileSystem_test.js)
* **Locator (22 tests)** [test/unit/locator_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/unit/locator_test.js)
* **Output (3 tests)** [test/unit/output_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/unit/output_test.js)
* **parser (2 tests)** [test/unit/parser_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/unit/parser_test.js)
* **customLocator (4 tests)** [test/unit/plugin/customLocator_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/unit/plugin/customLocator_test.js)
* **retryFailedStep (6 tests)** [test/unit/plugin/retryFailedStep_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/unit/plugin/retryFailedStep_test.js)
* **screenshotOnFail (3 tests)** [test/unit/plugin/screenshotOnFail_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/unit/plugin/screenshotOnFail_test.js)
* **Recorder (7 tests)** [test/unit/recorder_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/unit/recorder_test.js)
* **Scenario (5 tests)** [test/unit/scenario_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/unit/scenario_test.js)
* **Step (6 tests)** [test/unit/steps_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/unit/steps_test.js)
* **ui (22 tests)** [test/unit/ui_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/unit/ui_test.js)
* **utils (22 tests)** [test/unit/utils_test.js](https://github.com/Codeception/CodeceptJS/tree/7eefb25e4aa01a0be60af68a5a5dd3ffaced839d/test/unit/utils_test.js)

</details>


DavertMik: 
![Codacy](https://app.codacy.com/assets/images/favicon.png) Here is an overview of what got changed by this pull request:


```diff

Clones added
============
- test/helper/Playwright_test.js  2
- lib/helper/WebDriver.js  22
- test/runner/comment_step_test.js  1
         
```


See the complete overview on [Codacy](https://app.codacy.com/manual/DavertMik/CodeceptJS/pullRequest?prid=5264126&bid=17245758)
        
github-actions[bot]: 🌀 Tests overview by [Testomatio](https://testomat.io)


Found **947** mocha tests in 74 files 
#### ✔️ Added 3 tests


```diff

+ Playwright - Emulation: should open page as iPhone 
+ CodeceptJS commentStep plugin: should print nested steps when global var comments used
+ CodeceptJS commentStep plugin: should print nested steps when local var comments used
```


#### 🗑️ Removed 10 tests


```diff

- Playwright: open page : #amOnPage: should be unauthenticated 
- Playwright: #waitForClickable: should wait for clickable
- Playwright: #waitForClickable: should wait for clickable by XPath
- Playwright: #waitForClickable: should fail for disabled element
- Playwright: #waitForClickable: should fail for disabled element by XPath
- Playwright: #waitForClickable: should fail for element not in viewport by top
- Playwright: #waitForClickable: should fail for element not in viewport by bottom
- Playwright: #waitForClickable: should fail for element not in viewport by left
- Playwright: #waitForClickable: should fail for element not in viewport by right
- Playwright: #waitForClickable: should fail for overlapping element
```



<details>
  <summary>⚠️ List all skipped tests (1)</summary>

* [~~should resize window to maximum screen dimensions~~](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/helper/WebDriver_test.js#L786)

</details>



<details>
  <summary>📎 List all suites (62)</summary>


* **GraphQL (9 tests)** [test/graphql/GraphQL_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/graphql/GraphQL_test.js)
* **GraphQLDataFactory (6 tests)** [test/graphql/GraphQLDataFactory_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/graphql/GraphQLDataFactory_test.js)
* **Appium (73 tests)** [test/helper/Appium_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/helper/Appium_test.js)
* **Appium Web (16 tests)** [test/helper/AppiumWeb_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/helper/AppiumWeb_test.js)
* **Nightmare (17 tests)** [test/helper/Nightmare_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/helper/Nightmare_test.js)
* **Playwright (80 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/helper/Playwright_test.js)
* **#_startBrowser (2 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/helper/Playwright_test.js)
* **Playwright - BasicAuth (1 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/helper/Playwright_test.js)
* **Playwright - Emulation (1 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/helper/Playwright_test.js)
* **Protractor (114 tests)** [test/helper/Protractor_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/helper/Protractor_test.js)
* **Protractor-NonAngular (47 tests)** [test/helper/ProtractorWeb_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/helper/ProtractorWeb_test.js)
* **Puppeteer - BasicAuth (2 tests)** [test/helper/Puppeteer_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/helper/Puppeteer_test.js)
* **Puppeteer (96 tests)** [test/helper/Puppeteer_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/helper/Puppeteer_test.js)
* **Puppeteer (remote browser) (2 tests)** [test/helper/Puppeteer_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/helper/Puppeteer_test.js)
* **TestCafe (5 tests)** [test/helper/TestCafe_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/helper/TestCafe_test.js)
* **WebDriver (202 tests)** [test/helper/WebDriver_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/helper/WebDriver_test.js)
* **WebDriver - Basic Authentication (1 tests)** [test/helper/WebDriver_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/helper/WebDriver_test.js)
* **WebDriverIO (94 tests)** [test/helper/WebDriverIO_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/helper/WebDriverIO_test.js)
* **ApiDataFactory (10 tests)** [test/rest/ApiDataFactory_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/rest/ApiDataFactory_test.js)
* **REST (17 tests)** [test/rest/REST_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/rest/REST_test.js)
* **CodeceptJS Allure Plugin (4 tests)** [test/runner/allure_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/runner/allure_test.js)
* **BDD Gherkin (16 tests)** [test/runner/bdd_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/runner/bdd_test.js)
* **CodeceptJS Bootstrap and Teardown (18 tests)** [test/runner/bootstrap_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/runner/bootstrap_test.js)
* **CodeceptJS Runner (24 tests)** [test/runner/codecept_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/runner/codecept_test.js)
* **Codeceptjs Events (2 tests)** [test/runner/codecept_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/runner/codecept_test.js)
* **CodeceptJS commentStep plugin (2 tests)** [test/runner/comment_step_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/runner/comment_step_test.js)
* **Definitions (9 tests)** [test/runner/definitions_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/runner/definitions_test.js)
* **dry-run command (11 tests)** [test/runner/dry_run_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/runner/dry_run_test.js)
* **CodeceptJS Interface (16 tests)** [test/runner/interface_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/runner/interface_test.js)
* **list commands (1 tests)** [test/runner/list_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/runner/list_test.js)
* **CodeceptJS Interface (16 tests)** [test/runner/pageobject_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/runner/pageobject_test.js)
* **CodeceptJS Multiple Runner (20 tests)** [test/runner/run_multiple_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/runner/run_multiple_test.js)
* **run-rerun command (5 tests)** [test/runner/run_rerun_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/runner/run_rerun_test.js)
* **CodeceptJS Workers Runner (8 tests)** [test/runner/run_workers_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/runner/run_workers_test.js)
* **CodeceptJS session (3 tests)** [test/runner/session_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/runner/session_test.js)
* **Todo (3 tests)** [test/runner/todo_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/runner/todo_test.js)
* **Translation (1 tests)** [test/runner/translation_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/runner/translation_test.js)
* **CodeceptJS within (3 tests)** [test/runner/within_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/runner/within_test.js)
* **Actor (6 tests)** [test/unit/actor_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/unit/actor_test.js)
* **Assertion (2 tests)** [test/unit/assert_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/unit/assert_test.js)
* **empty assertion (4 tests)** [test/unit/assert/empty_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/unit/assert/empty_test.js)
* **equal assertion (8 tests)** [test/unit/assert/equal_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/unit/assert/equal_test.js)
* **equal assertion (8 tests)** [test/unit/assert/include_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/unit/assert/include_test.js)
* **BDD (28 tests)** [test/unit/bdd_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/unit/bdd_test.js)
* **Config (4 tests)** [test/unit/config_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/unit/config_test.js)
* **Container (20 tests)** [test/unit/container_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/unit/container_test.js)
* **DataTableArgument (3 tests)** [test/unit/data/dataTableArgument_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/unit/data/dataTableArgument_test.js)
* **DataTable (10 tests)** [test/unit/data/table_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/unit/data/table_test.js)
* **ui (22 tests)** [test/unit/data/ui_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/unit/data/ui_test.js)
* **ElementNotFound error (5 tests)** [test/unit/helper/element_not_found_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/unit/helper/element_not_found_test.js)
* **FileSystem (4 tests)** [test/unit/helper/FileSystem_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/unit/helper/FileSystem_test.js)
* **Locator (22 tests)** [test/unit/locator_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/unit/locator_test.js)
* **Output (3 tests)** [test/unit/output_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/unit/output_test.js)
* **parser (2 tests)** [test/unit/parser_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/unit/parser_test.js)
* **customLocator (4 tests)** [test/unit/plugin/customLocator_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/unit/plugin/customLocator_test.js)
* **retryFailedStep (6 tests)** [test/unit/plugin/retryFailedStep_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/unit/plugin/retryFailedStep_test.js)
* **screenshotOnFail (3 tests)** [test/unit/plugin/screenshotOnFail_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/unit/plugin/screenshotOnFail_test.js)
* **Recorder (7 tests)** [test/unit/recorder_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/unit/recorder_test.js)
* **Scenario (5 tests)** [test/unit/scenario_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/unit/scenario_test.js)
* **Step (6 tests)** [test/unit/steps_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/unit/steps_test.js)
* **ui (22 tests)** [test/unit/ui_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/unit/ui_test.js)
* **utils (22 tests)** [test/unit/utils_test.js](https://github.com/Codeception/CodeceptJS/tree/3b041e5615776158731aafcc63d55e9154a93777/test/unit/utils_test.js)

</details>


DavertMik: 
![Codacy](https://app.codacy.com/assets/images/favicon.png) Here is an overview of what got changed by this pull request:


```diff

Clones added
============
- lib/helper/WebDriver.js  22
- test/helper/Playwright_test.js  2
- test/runner/comment_step_test.js  1
         
```


See the complete overview on [Codacy](https://app.codacy.com/manual/DavertMik/CodeceptJS/pullRequest?prid=5264126&bid=17245758)
        
github-actions[bot]: 🌀 Tests overview by [Testomatio](https://testomat.io)


Found **947** mocha tests in 74 files 
#### ✔️ Added 3 tests


```diff

+ Playwright - Emulation: should open page as iPhone 
+ CodeceptJS commentStep plugin: should print nested steps when global var comments used
+ CodeceptJS commentStep plugin: should print nested steps when local var comments used
```


#### 🗑️ Removed 10 tests


```diff

- Playwright: open page : #amOnPage: should be unauthenticated 
- Playwright: #waitForClickable: should wait for clickable
- Playwright: #waitForClickable: should wait for clickable by XPath
- Playwright: #waitForClickable: should fail for disabled element
- Playwright: #waitForClickable: should fail for disabled element by XPath
- Playwright: #waitForClickable: should fail for element not in viewport by top
- Playwright: #waitForClickable: should fail for element not in viewport by bottom
- Playwright: #waitForClickable: should fail for element not in viewport by left
- Playwright: #waitForClickable: should fail for element not in viewport by right
- Playwright: #waitForClickable: should fail for overlapping element
```



<details>
  <summary>⚠️ List all skipped tests (1)</summary>

* [~~should resize window to maximum screen dimensions~~](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/helper/WebDriver_test.js#L786)

</details>



<details>
  <summary>📎 List all suites (62)</summary>


* **GraphQL (9 tests)** [test/graphql/GraphQL_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/graphql/GraphQL_test.js)
* **GraphQLDataFactory (6 tests)** [test/graphql/GraphQLDataFactory_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/graphql/GraphQLDataFactory_test.js)
* **Appium (73 tests)** [test/helper/Appium_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/helper/Appium_test.js)
* **Appium Web (16 tests)** [test/helper/AppiumWeb_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/helper/AppiumWeb_test.js)
* **Nightmare (17 tests)** [test/helper/Nightmare_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/helper/Nightmare_test.js)
* **Playwright (80 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/helper/Playwright_test.js)
* **#_startBrowser (2 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/helper/Playwright_test.js)
* **Playwright - BasicAuth (1 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/helper/Playwright_test.js)
* **Playwright - Emulation (1 tests)** [test/helper/Playwright_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/helper/Playwright_test.js)
* **Protractor (114 tests)** [test/helper/Protractor_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/helper/Protractor_test.js)
* **Protractor-NonAngular (47 tests)** [test/helper/ProtractorWeb_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/helper/ProtractorWeb_test.js)
* **Puppeteer - BasicAuth (2 tests)** [test/helper/Puppeteer_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/helper/Puppeteer_test.js)
* **Puppeteer (96 tests)** [test/helper/Puppeteer_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/helper/Puppeteer_test.js)
* **Puppeteer (remote browser) (2 tests)** [test/helper/Puppeteer_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/helper/Puppeteer_test.js)
* **TestCafe (5 tests)** [test/helper/TestCafe_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/helper/TestCafe_test.js)
* **WebDriver (202 tests)** [test/helper/WebDriver_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/helper/WebDriver_test.js)
* **WebDriver - Basic Authentication (1 tests)** [test/helper/WebDriver_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/helper/WebDriver_test.js)
* **WebDriverIO (94 tests)** [test/helper/WebDriverIO_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/helper/WebDriverIO_test.js)
* **ApiDataFactory (10 tests)** [test/rest/ApiDataFactory_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/rest/ApiDataFactory_test.js)
* **REST (17 tests)** [test/rest/REST_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/rest/REST_test.js)
* **CodeceptJS Allure Plugin (4 tests)** [test/runner/allure_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/runner/allure_test.js)
* **BDD Gherkin (16 tests)** [test/runner/bdd_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/runner/bdd_test.js)
* **CodeceptJS Bootstrap and Teardown (18 tests)** [test/runner/bootstrap_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/runner/bootstrap_test.js)
* **CodeceptJS Runner (24 tests)** [test/runner/codecept_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/runner/codecept_test.js)
* **Codeceptjs Events (2 tests)** [test/runner/codecept_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/runner/codecept_test.js)
* **CodeceptJS commentStep plugin (2 tests)** [test/runner/comment_step_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/runner/comment_step_test.js)
* **Definitions (9 tests)** [test/runner/definitions_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/runner/definitions_test.js)
* **dry-run command (11 tests)** [test/runner/dry_run_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/runner/dry_run_test.js)
* **CodeceptJS Interface (16 tests)** [test/runner/interface_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/runner/interface_test.js)
* **list commands (1 tests)** [test/runner/list_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/runner/list_test.js)
* **CodeceptJS Interface (16 tests)** [test/runner/pageobject_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/runner/pageobject_test.js)
* **CodeceptJS Multiple Runner (20 tests)** [test/runner/run_multiple_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/runner/run_multiple_test.js)
* **run-rerun command (5 tests)** [test/runner/run_rerun_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/runner/run_rerun_test.js)
* **CodeceptJS Workers Runner (8 tests)** [test/runner/run_workers_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/runner/run_workers_test.js)
* **CodeceptJS session (3 tests)** [test/runner/session_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/runner/session_test.js)
* **Todo (3 tests)** [test/runner/todo_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/runner/todo_test.js)
* **Translation (1 tests)** [test/runner/translation_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/runner/translation_test.js)
* **CodeceptJS within (3 tests)** [test/runner/within_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/runner/within_test.js)
* **Actor (6 tests)** [test/unit/actor_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/unit/actor_test.js)
* **Assertion (2 tests)** [test/unit/assert_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/unit/assert_test.js)
* **empty assertion (4 tests)** [test/unit/assert/empty_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/unit/assert/empty_test.js)
* **equal assertion (8 tests)** [test/unit/assert/equal_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/unit/assert/equal_test.js)
* **equal assertion (8 tests)** [test/unit/assert/include_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/unit/assert/include_test.js)
* **BDD (28 tests)** [test/unit/bdd_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/unit/bdd_test.js)
* **Config (4 tests)** [test/unit/config_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/unit/config_test.js)
* **Container (20 tests)** [test/unit/container_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/unit/container_test.js)
* **DataTableArgument (3 tests)** [test/unit/data/dataTableArgument_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/unit/data/dataTableArgument_test.js)
* **DataTable (10 tests)** [test/unit/data/table_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/unit/data/table_test.js)
* **ui (22 tests)** [test/unit/data/ui_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/unit/data/ui_test.js)
* **ElementNotFound error (5 tests)** [test/unit/helper/element_not_found_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/unit/helper/element_not_found_test.js)
* **FileSystem (4 tests)** [test/unit/helper/FileSystem_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/unit/helper/FileSystem_test.js)
* **Locator (22 tests)** [test/unit/locator_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/unit/locator_test.js)
* **Output (3 tests)** [test/unit/output_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/unit/output_test.js)
* **parser (2 tests)** [test/unit/parser_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/unit/parser_test.js)
* **customLocator (4 tests)** [test/unit/plugin/customLocator_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/unit/plugin/customLocator_test.js)
* **retryFailedStep (6 tests)** [test/unit/plugin/retryFailedStep_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/unit/plugin/retryFailedStep_test.js)
* **screenshotOnFail (3 tests)** [test/unit/plugin/screenshotOnFail_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/unit/plugin/screenshotOnFail_test.js)
* **Recorder (7 tests)** [test/unit/recorder_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/unit/recorder_test.js)
* **Scenario (5 tests)** [test/unit/scenario_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/unit/scenario_test.js)
* **Step (6 tests)** [test/unit/steps_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/unit/steps_test.js)
* **ui (22 tests)** [test/unit/ui_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/unit/ui_test.js)
* **utils (22 tests)** [test/unit/utils_test.js](https://github.com/Codeception/CodeceptJS/tree/3c129231695ac1b490606cf8129bd98243c5a479/test/unit/utils_test.js)

</details>


DavertMik: 
![Codacy](https://app.codacy.com/assets/images/favicon.png) Here is an overview of what got changed by this pull request:


```diff

Clones added
============
- test/runner/comment_step_test.js  1
- test/helper/Playwright_test.js  2
- lib/helper/WebDriver.js  22
         
```


See the complete overview on [Codacy](https://app.codacy.com/manual/DavertMik/CodeceptJS/pullRequest?prid=5264126&bid=17245758)
        

Patch:
diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml
index 99a4eeeb..ebec574b 100644
--- a/.github/workflows/playwright.yml
+++ b/.github/workflows/playwright.yml
@@ -8,6 +8,11 @@ on:
     branches:
       - master
 
+env:
+  CI: true
+  # Force terminal colors. @see https://www.npmjs.com/package/colors
+  FORCE_COLOR: 1
+
 jobs:
   build:
 
@@ -26,8 +31,7 @@ jobs:
     - name: install required packages
       run: |
         sudo apt-get update
-        sudo apt-get install libgbm-dev libwoff1 libopus0 libwebp6 libwebpdemux2 libenchant1c2a libgudev-1.0-0 libsecret-1-0 libhyphen0 libgdk-pixbuf2.0-0 libegl1 libgles2 libevent-2.1-6 libnotify4 libxslt1.1
-        sudo apt-get install xvfb
+        sudo apt-get install libgbm1 libgbm-dev libwoff1 libopus0 libwebp6 libwebpdemux2 libenchant1c2a libgudev-1.0-0 libsecret-1-0 libhyphen0 libgdk-pixbuf2.0-0 libegl1 libgles2 libevent-2.1-6 libnotify4 libxslt1.1
         sudo apt-get install php
     - name: npm install
       run: |
@@ -35,8 +39,8 @@ jobs:
     - name: start a server
       run: "php -S 127.0.0.1:8000 -t test/data/app &"
     - name: run chromium tests
-      run: " ./bin/codecept.js run -c test/acceptance/codecept.Playwright.js --grep @Playwright --debug"
+      run: "./bin/codecept.js run -c test/acceptance/codecept.Playwright.js --grep @Playwright --debug"
     - name: run firefox tests
-      run: "BROWSER=firefox ./bin/codecept.js run -c test/acceptance/codecept.Playwright.js --grep @Playwright --debug"
+      run: "BROWSER=firefox node ./bin/codecept.js run -c test/acceptance/codecept.Playwright.js --grep @Playwright --debug"
     - name: run webkit tests
-      run: "BROWSER=webkit ./bin/codecept.js run -c test/acceptance/codecept.Playwright.js --grep @Playwright --debug"
+      run: "BROWSER=webkit node ./bin/codecept.js run -c test/acceptance/codecept.Playwright.js --grep @Playwright --debug"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f12d69ee..92ac8aa0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,80 @@
+## 2.6.0
+
+* **[Playwright] Updated to Playwright 0.12** by @Georgegriff.
+
+Upgrade playwright to ^0.12:
+
+```
+npm i playwright@^0.12 --save
+```
+
+[Notable changes](https://github.com/microsoft/playwright/releases/tag/v0.12.0):
+  * Fixed opening two browsers on start
+  * `executeScript` - passed function now accepts only one argument. Pass in objects or arrays if you need multtple arguments:
+```js
+// Old style, does not work anymore:
+I.executeScript((x, y) => x + y, x, y);
+// New style, passing an object:
+I.executeScript(({x, y}) => x + y, {x, y});
+```
+  * `click` - automatically waits for element to become clickable (visible, not animated) and waits for navigation.
+  * `clickLink` - deprecated
+  * `waitForClickable` - deprecated
+  * Added support for custom locators. See #2277
+  * Introduced [device emulation](/playwright/#device-emulation):
+    * globally via `emulate` config option
+    * per session
+
+**[WebDriver] Updated to webdriverio v6** by @PeterNgTr.
+
+Read [release notes](https://webdriver.io/blog/2020/03/26/webdriverio-v6-released.html), then
+upgrade webdriverio to ^6.0:
+
+```
+npm i webdriverio@^6.0 --save
+```
+*(webdriverio v5 support is deprecated and will be removed in CodeceptJS 3.0)*
+
+[WebDriver] Introduced [Shadow DOM support](/shadow) by @gkushang
+
+```js
+I.click({ shadow: ['my-app', 'recipe-hello', 'button'] });
+```
+
+* **Fixed parallel execution of `run-workers` for Gherkin** scenarios by @koushikmohan1996
+* [MockRequest] Updated and **moved to [standalone package](https://github.com/codecept-js/mock-request)**:
+  * full support for record/replay mode for Puppeteer
+  * added `mockServer` method to use flexible PollyJS API to define mocks
+  * fixed stale browser screen in record mode.
+* [Playwright] Added support on for `screenshotOnFail` plugin by @amonkc
+* Gherkin improvement: setting different tags per examples. See #2208 by @acuper
+* [TestCafe] Updated `click` to take first visible element. Fixes #2226 by @theTainted
+* [Puppeteer][WebDriver] Updated `waitForClickable` method to check for element overlapping. See #2261 by @PiQx
+* [Puppeteer] Dropped `puppeteer-firefox` support, as Puppeteer supports Firefox natively.
+* [REST] Rrespect Content-Type header. See #2262 by @pmarshall-legacy
+* [allure plugin] Fixes BeforeSuite failures in allure reports. See #2248 by @Georgegriff
+* [WebDriver][Puppeteer][Playwright] A screenshot of for an active session is saved in multi-session mode. See #2253 by @ChexWarrior
+* Fixed `--profile` option by @pablopaul. Profile value to be passed into `run-multiple` and `run-workers`:
+
+```
+npx codecept run-workers 2 --profile firefox
+```
+
+Value is available at `process.env.profile` (previously `process.profile`). See #2302. Fixes #1968 #1315
+
+* [commentStep Plugin introduced](/plugins#commentstep). Allows to annotate logical parts of a test:
+
+```js
+__`Given`;
+I.amOnPage('/profile')
+
+__`When`;
+I.click('Logout');
+
+__`Then`;
+I.see('You are logged out');
+```
+
 ## 2.5.0
 
 * **Experimental: [Playwright](/playwright) helper introduced**.
diff --git a/docs/basics.md b/docs/basics.md
index d3cb25dc..9e02721f 100644
--- a/docs/basics.md
+++ b/docs/basics.md
@@ -622,7 +622,7 @@ within('.js-signup-form', () => {
 I.see('There were problems creating your account.');
 ```
 
-> ⚠ `within` can cause problems when used incorrectly. If you see a weired behavior of a test try to refactor it to not use `within`. It is recommended to keep within for simplest cases when possible.
+> ⚠ `within` can cause problems when used incorrectly. If you see a weird behavior of a test try to refactor it to not use `within`. It is recommended to keep within for simplest cases when possible.
 
 `within` can also work with IFrames. A special `frame` locator is required to locate the iframe and get into its context.
 
diff --git a/docs/changelog.md b/docs/changelog.md
index 8d680008..b040ec2c 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -7,6 +7,25 @@ layout: Section
 
 # Releases
 
+## 2.5.0
+
+* **Experimental: [Playwright](/playwright) helper introduced**.
+
+> [Playwright](https://github.com/microsoft/playwright/) is an alternative to Puppeteer which works very similarly to it but adds cross-browser support with Firefox and Webkit. Until v1.0 Playwright API is not stable but we introduce it to CodeceptJS so you could try it.
+
+* **[Puppeteer]** Fixed basic auth support when running in multiple sessions. See [#2178](https://github.com/Codeception/CodeceptJS/issues/2178) by **[ian-bartholomew](https://github.com/ian-bartholomew)**
+* **[Puppeteer]** Fixed `waitForText` when there is no `body` element on page (redirect). See [#2181](https://github.com/Codeception/CodeceptJS/issues/2181) by **[Vorobeyko](https://github.com/Vorobeyko)**
+* [Selenoid plugin] Fixed overriding current capabilities by adding deepMerge. Fixes [#2183](https://github.com/Codeception/CodeceptJS/issues/2183) by **[koushikmohan1996](https://github.com/koushikmohan1996)**
+* Added types for `Scenario.todo` by **[Vorobeyko](https://github.com/Vorobeyko)**
+* Added types for Mocha by **[Vorobeyko](https://github.com/Vorobeyko)**. Fixed typing conflicts with Jest
+* **[FileSystem]** Added methods by **[nitschSB](https://github.com/nitschSB)**
+  * `waitForFile`
+  * `seeFileContentsEqualReferenceFile`
+* Added `--colors` option to `run` and `run-multiple` so you force colored output in dockerized environment. See [#2189](https://github.com/Codeception/CodeceptJS/issues/2189) by **[mirao](https://github.com/mirao)**
+* **[WebDriver]** Added `type` command to enter value without focusing on a field. See [#2198](https://github.com/Codeception/CodeceptJS/issues/2198) by **[xMutaGenx](https://github.com/xMutaGenx)**
+* Fixed `codeceptjs gt` command to respect config pattern for tests. See [#2200](https://github.com/Codeception/CodeceptJS/issues/2200) and [#2204](https://github.com/Codeception/CodeceptJS/issues/2204) by **[matheo](https://github.com/matheo)**
+
+
 ## 2.4.3
 
 * Hotfix for interactive pause
diff --git a/docs/configuration.md b/docs/configuration.md
index f8edcaa5..17940caf 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -118,7 +118,7 @@ exports.config = {
 
 ## Profile
 
-Using values from `process.profile` you can change the config dynamically.
+Using `process.env.profile` you can change the config dynamically.
 It provides value of `--profile` option passed to runner.
 Use its value to change config value on the fly.
 
@@ -134,7 +134,7 @@ exports.config = {
     WebDriver: {
       url: 'http://localhost:3000',
       // load value from `profile`
-      browser: process.profile || 'firefox'
+      browser: process.env.profile || 'firefox'
 
     }
   }
diff --git a/docs/custom-helpers.md b/docs/custom-helpers.md
index 430215cd..00e9dc88 100644
--- a/docs/custom-helpers.md
+++ b/docs/custom-helpers.md
@@ -3,6 +3,7 @@ permalink: /helpers
 title: Custom Helpers
 ---
 
+# Extending CodeceptJS with Custom Helopers
 
 Helpers is a core concept of CodeceptJS. Helper is a wrapper on top of various libraries providing unified interface around them.
 
@@ -168,6 +169,50 @@ class MyHelper extends Helper {
 module.exports = MyHelper;
 ```
 
+## Accessing Elements
+
+WebDriver, Puppeteer, Playwright, and Protractor drivers provide API for web elements.
+However, CodeceptJS do not expose them to tests by design, keeping test to be action focused.
+If you need to get access to web elements, it is recommended to implement operations for web elements in a custom helper.
+
+To get access for elements, connect to a corresponding helper and use `_locate` function to match web elements by CSS or XPath, like you usually do:
+
+### Acessing Elements in WebDriver
+
+```js
+// inside a custom helper
+async clickOnEveryElement(locator) {
+  const { WebDriver } = this.helpers;
+  const els = await WebDriver._locate(locator);
+
+  for (let el of els) {
+    await el.click();
+  }
+}
+```
+In this case an an instance of webdriverio element is used.
+To get a [complete API of an element](https://webdriver.io/docs/api/) refer to webdriverio docs.
+
+### Accessing Elements in Playwright & Puppeteer
+
+Similar method can be implemented for Playwright & Puppeteer:
+
+```js
+// inside a custom helper
+async clickOnEveryElement(locator) {
+  const { Playwright } = this.helpers;
+  const els = await Playwright._locate(locator);
+
+  for (let el of els) {
+    await el.click();
+  }
+}
+```
+
+In this case `el` will be an instance of [ElementHandle](https://github.com/microsoft/playwright/blob/v0.12.1/docs/api.md#class-elementhandle) which is similar for Playwright & [Puppeteer](https://pptr.dev/#?product=Puppeteer&version=v2.1.1&show=api-class-elementhandle).
+
+> ℹ There are more `_locate*` methods in each helper. Take a look on documentation of a helper you use to see which exact method it exposes.
+
 ## Configuration
 
 Helpers should be enabled inside `codecept.json` or `codecept.conf.js` files. Command `generate helper`
@@ -175,13 +220,13 @@ does that for you, however you can enable them manually by placing helper to `he
 You can also pass additional config options to your helper from a config - **(please note, this example contains comments, while JSON format doesn't support them)**:
 
 ```js
-"helpers": {
+helpers: {
   // here goes standard helpers:
   // WebDriver, Protractor, Nightmare, etc...
   // and their configuration
-  "MyHelper": {
-    "require": "./my_helper.js", // path to module
-    "defaultHost": "http://mysite.com" // custom config param
+  MyHelper: {
+    require: "./my_helper.js", // path to module
+    defaultHost: "http://mysite.com" // custom config param
   }
 
 }
diff --git a/docs/helpers/Appium.md b/docs/helpers/Appium.md
index 965a9cec..82e78c61 100644
--- a/docs/helpers/Appium.md
+++ b/docs/helpers/Appium.md
@@ -1107,6 +1107,30 @@ I.waitForText('Thank you, form has been submitted', 5, '#modal');
 -   `sec` **[number][8]** (optional, `1` by default) time in seconds to wait (optional, default `1`)
 -   `context` **([string][4] \| [object][6])?** (optional) element located by CSS|XPath|strict locator. (optional, default `null`)
 
+### \_isShadowLocator
+
+Check if locator is type of "Shadow"
+
+#### Parameters
+
+-   `locator` **[object][6]** 
+
+### \_locateShadow
+
+Locate Element within the Shadow Dom
+
+#### Parameters
+
+-   `locator` **[object][6]** 
+
+### \_smartWait
+
+Smart Wait to locate an element
+
+#### Parameters
+
+-   `locator` **[object][6]** 
+
 ### \_locate
 
 Get elements by different locator types, including strict locator.
@@ -1477,6 +1501,8 @@ I.moveCursorTo('#submit', 5,5);
 #### Parameters
 
 -   `locator` **([string][4] \| [object][6])** located by CSS|XPath|strict locator.
+-   `xOffset`  
+-   `yOffset`  
 -   `offsetX` **[number][8]** (optional, `0` by default) X-axis offset. (optional, default `0`)
 -   `offsetY` **[number][8]** (optional, `0` by default) Y-axis offset. (optional, default `0`)
 
diff --git a/docs/helpers/Nightmare.md b/docs/helpers/Nightmare.md
index 7829cda4..dcc74f48 100644
--- a/docs/helpers/Nightmare.md
+++ b/docs/helpers/Nightmare.md
@@ -876,15 +876,23 @@ I.selectOption('Which OS do you use?', ['Android', 'iOS']);
 
 ### setCookie
 
-Sets a cookie.
+Sets cookie(s).
+
+Can be a single cookie object or an array of cookies:
 
 ```js
 I.setCookie({name: 'auth', value: true});
+
+// as array
+I.setCookie([
+  {name: 'auth', value: true},
+  {name: 'agree', value: true}
+]);
 ```
 
 #### Parameters
 
--   `cookie` **[object][4]** a cookie object.Wrapper for `.cookies.set(cookie)`.
+-   `cookie` **([object][4] | [array][11])** a cookie object or array of cookie objects.Wrapper for `.cookies.set(cookie)`.
     [See more][14]
 
 ### triggerMouseEvent
diff --git a/docs/helpers/Playwright.md b/docs/helpers/Playwright.md
index cdf451da..500337d5 100644
--- a/docs/helpers/Playwright.md
+++ b/docs/helpers/Playwright.md
@@ -32,6 +32,7 @@ This helper should be configured in codecept.json or codecept.conf.js
 -   `show`:  - show browser window.
 -   `restart`:  - restart browser between tests.
 -   `disableScreenshots`:   - don't save screenshot on failure.
+-   `emulate`:  launch browser in device emulation mode.
 -   `fullPageScreenshots`  - make full page screenshots on failure.
 -   `uniqueScreenshotNames`:   - option to prevent screenshot override if you have scenarios with the same name in different suites.
 -   `keepBrowserState`:  - keep browser state between tests when `restart` is set to false.
@@ -126,6 +127,21 @@ This helper should be configured in codecept.json or codecept.conf.js
 }
 ```
 
+#### Example #6: Lunach tests emulating iPhone 6
+
+```js
+const { devices } = require('playwright');
+
+{
+ helpers: {
+   Playwright: {
+     url: "http://localhost",
+     emulate: devices['iPhone 6'],
+   }
+ }
+}
+```
+
 Note: When connecting to remote browser `show` and specific `chrome` options (e.g. `headless` or `devtools`) are ignored.
 
 ## Access From Helpers
@@ -376,16 +392,12 @@ I.click({css: 'nav a.login'});
 
 ### clickLink
 
-Performs a click on a link and waits for navigation before moving on.
-
-```js
-I.clickLink('Logout', '#nav');
-```
+Clicks link and waits for navigation (deprecated)
 
 #### Parameters
 
--   `locator` **([string][7] | [object][5])** clickable link or button located by text, or any element located by CSS|XPath|strict locator
--   `context` **([string][7]? | [object][5])** (optional, `null` by default) element to search in CSS|XPath|Strict locator 
+-   `locator`  
+-   `context`   
 
 ### closeCurrentTab
 
@@ -575,68 +587,33 @@ I.dragSlider('#slider', -70);
 -   `locator` **([string][7] | [object][5])** located by label|name|CSS|XPath|strict locator.
 -   `offsetX` **[number][8]** position to drag. 
 
-### executeAsyncScript
-
-Executes async script on page.
-Provided function should execute a passed callback (as first argument) to signal it is finished.
+### executeScript
 
-Example: In Vue.js to make components completely rendered we are waiting for [nextTick][9].
+Executes a script on the page:
 
 ```js
-I.executeAsyncScript(function(done) {
-  Vue.nextTick(done); // waiting for next tick
-});
+I.executeScript(() => window.alert('Hello world'));
 ```
 
-By passing value to `done()` function you can return values.
-Additional arguments can be passed as well, while `done` function is always last parameter in arguments list.
+Additional parameters of the function can be passed as an object argument:
 
 ```js
-let val = await I.executeAsyncScript(function(url, done) {
-  // in browser context
-  $.ajax(url, { success: (data) => done(data); }
-}, 'http://ajax.callback.url/');
+I.executeScript(({x, y}) => x + y, {x, y});
 ```
 
-#### Parameters
-
--   `fn` **([string][7] | [function][10])** function to be executed in browser context.
--   `args` **...any** to be passed to function.
-
-Returns **[Promise][11]&lt;any>** Asynchronous scripts can also be executed with `executeScript` if a function returns a Promise.
-
-### executeScript
-
-Executes sync script on a page.
-Pass arguments to function as additional parameters.
-Will return execution result to a test.
-In this case you should use async function and await to receive results.
-
-Example with jQuery DatePicker:
+You can pass only one parameter into a function
+but you can pass in array or object.
 
 ```js
-// change date of jQuery DatePicker
-I.executeScript(function() {
-  // now we are inside browser context
-  $('date').datetimepicker('setDate', new Date());
-});
+I.executeScript(([x, y]) => x + y, [x, y]);
 ```
 
-Can return values. Don't forget to use `await` to get them.
-
-```js
-let date = await I.executeScript(function(el) {
-  // only basic types can be returned
-  return $(el).datetimepicker('getDate').toString();
-}, '#date'); // passing jquery selector
-```
+If a function returns a Promise it will wait for its resolution.
 
 #### Parameters
 
--   `fn` **([string][7] | [function][10])** function to be executed in browser context.
--   `args` **...any** to be passed to function.
-
-Returns **[Promise][11]&lt;any>** If a function returns a Promise It will wait for it resolution.
+-   `fn`  
+-   `arg`  
 
 ### fillField
 
@@ -659,6 +636,20 @@ I.fillField({css: 'form#login input[name=username]'}, 'John');
 -   `field` **([string][7] | [object][5])** located by label|name|CSS|XPath|strict locator.
 -   `value` **[string][7]** text value to fill.
 
+### forceClick
+
+Force clicks an element without waiting for it to become visible and not animating.
+
+```js
+I.forceClick('#hiddenButton');
+I.forceClick('Click me', '#hidden');
+```
+
+#### Parameters
+
+-   `locator`  
+-   `context`   
+
 ### grabAttributeFrom
 
 Retrieves an attribute from an element located by CSS or XPath and returns it to test.
@@ -674,7 +665,7 @@ let hint = await I.grabAttributeFrom('#tooltip', 'title');
 -   `locator` **([string][7] | [object][5])** element located by CSS|XPath|strict locator.
 -   `attr` **[string][7]** attribute name.
 
-Returns **[Promise][11]&lt;[string][7]>** attribute value
+Returns **[Promise][9]&lt;[string][7]>** attribute value
 
 ### grabBrowserLogs
 
@@ -685,7 +676,7 @@ let logs = await I.grabBrowserLogs();
 console.log(JSON.stringify(logs))
 ```
 
-Returns **[Promise][11]&lt;[Array][12]&lt;any>>** 
+Returns **[Promise][9]&lt;[Array][10]&lt;any>>** 
 
 ### grabCookie
 
@@ -702,7 +693,7 @@ assert(cookie.value, '123456');
 
 -   `name` **[string][7]?** cookie name. 
 
-Returns **[Promise][11]&lt;[string][7]>** attribute valueReturns cookie in JSON format. If name not passed returns all cookies for this domain.
+Returns **[Promise][9]&lt;[string][7]>** attribute valueReturns cookie in JSON format. If name not passed returns all cookies for this domain.
 
 ### grabCssPropertyFrom
 
@@ -718,7 +709,7 @@ const value = await I.grabCssPropertyFrom('h3', 'font-weight');
 -   `locator` **([string][7] | [object][5])** element located by CSS|XPath|strict locator.
 -   `cssProperty` **[string][7]** CSS property name.
 
-Returns **[Promise][11]&lt;[string][7]>** CSS value
+Returns **[Promise][9]&lt;[string][7]>** CSS value
 
 ### grabCurrentUrl
 
@@ -730,7 +721,7 @@ let url = await I.grabCurrentUrl();
 console.log(`Current URL is [${url}]`);
 ```
 
-Returns **[Promise][11]&lt;[string][7]>** current URL
+Returns **[Promise][9]&lt;[string][7]>** current URL
 
 ### grabDataFromPerformanceTiming
 
@@ -798,7 +789,7 @@ let postHTML = await I.grabHTMLFrom('#post');
 -   `locator`  
 -   `element` **([string][7] | [object][5])** located by CSS|XPath|strict locator.
 
-Returns **[Promise][11]&lt;[string][7]>** HTML code for an element
+Returns **[Promise][9]&lt;[string][7]>** HTML code for an element
 
 ### grabNumberOfOpenTabs
 
@@ -808,7 +799,7 @@ Grab number of open tabs.
 let tabs = await I.grabNumberOfOpenTabs();
 ```
 
-Returns **[Promise][11]&lt;[number][8]>** number of open tabs
+Returns **[Promise][9]&lt;[number][8]>** number of open tabs
 
 ### grabNumberOfVisibleElements
 
@@ -822,7 +813,7 @@ let numOfElements = await I.grabNumberOfVisibleElements('p');
 
 -   `locator` **([string][7] | [object][5])** located by CSS|XPath|strict locator.
 
-Returns **[Promise][11]&lt;[number][8]>** number of visible elements
+Returns **[Promise][9]&lt;[number][8]>** number of visible elements
 
 ### grabPageScrollPosition
 
@@ -833,7 +824,7 @@ Resumes test execution, so **should be used inside an async function with `await
 let { x, y } = await I.grabPageScrollPosition();
 ```
 
-Returns **[Promise][11]&lt;[Object][5]&lt;[string][7], any>>** scroll position
+Returns **[Promise][9]&lt;[Object][5]&lt;[string][7], any>>** scroll position
 
 ### grabPopupText
 
@@ -843,7 +834,7 @@ Grab the text within the popup. If no popup is visible then it will return null
 await I.grabPopupText();
 ```
 
-Returns **[Promise][11]&lt;([string][7] | null)>** 
+Returns **[Promise][9]&lt;([string][7] | null)>** 
 
 ### grabSource
 
@@ -854,7 +845,7 @@ Resumes test execution, so should be used inside an async function.
 let pageSource = await I.grabSource();
 ```
 
-Returns **[Promise][11]&lt;[string][7]>** source code
+Returns **[Promise][9]&lt;[string][7]>** source code
 
 ### grabTextFrom
 
@@ -871,7 +862,7 @@ If multiple elements found returns an array of texts.
 
 -   `locator` **([string][7] | [object][5])** element located by CSS|XPath|strict locator.
 
-Returns **[Promise][11]&lt;([string][7] | [Array][12]&lt;[string][7]>)>** attribute value
+Returns **[Promise][9]&lt;([string][7] | [Array][10]&lt;[string][7]>)>** attribute value
 
 ### grabTitle
 
@@ -882,7 +873,7 @@ Resumes test execution, so **should be used inside async with `await`** operator
 let title = await I.grabTitle();
 ```
 
-Returns **[Promise][11]&lt;[string][7]>** title
+Returns **[Promise][9]&lt;[string][7]>** title
 
 ### grabValueFrom
 
@@ -897,7 +888,7 @@ let email = await I.grabValueFrom('input[name=email]');
 
 -   `locator` **([string][7] | [object][5])** field located by label|name|CSS|XPath|strict locator.
 
-Returns **[Promise][11]&lt;[string][7]>** attribute value
+Returns **[Promise][9]&lt;[string][7]>** attribute value
 
 ### haveRequestHeaders
 
@@ -937,11 +928,22 @@ Open new tab and switch to it
 I.openNewTab();
 ```
 
+You can pass in [page options][11] to emulate device on this page
+
+```js
+// enable mobile
+I.openNewTab({ isMobile: true });
+```
+
+#### Parameters
+
+-   `options`  
+
 ### pressKey
 
 Presses a key in the browser (on a focused element).
 
-_Hint:_ For populating text field or textarea, it is recommended to use [`fillField`][13].
+_Hint:_ For populating text field or textarea, it is recommended to use [`fillField`][12].
 
 ```js
 I.pressKey('Backspace');
@@ -1000,13 +1002,13 @@ Some of the supported key names are:
 
 #### Parameters
 
--   `key` **([string][7] | [Array][12]&lt;[string][7]>)** key or array of keys to press._Note:_ Shortcuts like `'Meta'` + `'A'` do not work on macOS ([GoogleChrome/Playwright#1313][14]).
+-   `key` **([string][7] | [Array][10]&lt;[string][7]>)** key or array of keys to press._Note:_ Shortcuts like `'Meta'` + `'A'` do not work on macOS ([GoogleChrome/Playwright#1313][13]).
 
 ### pressKeyDown
 
 Presses a key in the browser and leaves it in a down state.
 
-To make combinations with modifier key and user operation (e.g. `'Control'` + [`click`][15]).
+To make combinations with modifier key and user operation (e.g. `'Control'` + [`click`][14]).
 
 ```js
 I.pressKeyDown('Control');
@@ -1022,7 +1024,7 @@ I.pressKeyUp('Control');
 
 Releases a key in the browser which was previously set to a down state.
 
-To make combinations with modifier key and user operation (e.g. `'Control'` + [`click`][15]).
+To make combinations with modifier key and user operation (e.g. `'Control'` + [`click`][14]).
 
 ```js
 I.pressKeyDown('Control');
@@ -1090,7 +1092,7 @@ I.saveScreenshot('debug.png', true) //resizes to available scrollHeight and scro
 #### Parameters
 
 -   `fileName` **[string][7]** file name to save.
--   `fullPage` **[boolean][16]** (optional, `false` by default) flag to enable fullscreen screenshot mode. 
+-   `fullPage` **[boolean][15]** (optional, `false` by default) flag to enable fullscreen screenshot mode. 
 
 ### scrollPageToBottom
 
@@ -1376,19 +1378,27 @@ I.selectOption('Which OS do you use?', ['Android', 'iOS']);
 #### Parameters
 
 -   `select` **([string][7] | [object][5])** field located by label|name|CSS|XPath|strict locator.
--   `option` **([string][7] | [Array][12]&lt;any>)** visible text or value of option.
+-   `option` **([string][7] | [Array][10]&lt;any>)** visible text or value of option.
 
 ### setCookie
 
-Sets a cookie.
+Sets cookie(s).
+
+Can be a single cookie object or an array of cookies:
 
 ```js
 I.setCookie({name: 'auth', value: true});
+
+// as array
+I.setCookie([
+  {name: 'auth', value: true},
+  {name: 'agree', value: true}
+]);
 ```
 
 #### Parameters
 
--   `cookie` **[object][5]** a cookie object.
+-   `cookie` **([object][5] | [array][10])** a cookie object or array of cookie objects.
 
 ### switchTo
 
@@ -1531,8 +1541,8 @@ I.waitForFunction((count) => window.requests == count, [3], 5) // pass args and
 
 #### Parameters
 
--   `fn` **([string][7] | [function][10])** to be executed in browser context.
--   `argsOrSec` **([Array][12]&lt;any> | [number][8])?** (optional, `1` by default) arguments for function or seconds. 
+-   `fn` **([string][7] | [function][16])** to be executed in browser context.
+-   `argsOrSec` **([Array][10]&lt;any> | [number][8])?** (optional, `1` by default) arguments for function or seconds. 
 -   `sec` **[number][8]?** (optional, `1` by default) time in seconds to wait 
 
 ### waitForInvisible
@@ -1570,7 +1580,7 @@ I.waitForRequest(request => request.url() === 'http://example.com' && request.me
 
 #### Parameters
 
--   `urlOrPredicate` **([string][7] | [function][10])** 
+-   `urlOrPredicate` **([string][7] | [function][16])** 
 -   `sec` **[number][8]?** seconds to wait 
 
 ### waitForResponse
@@ -1584,7 +1594,7 @@ I.waitForResponse(request => request.url() === 'http://example.com' && request.m
 
 #### Parameters
 
--   `urlOrPredicate` **([string][7] | [function][10])** 
+-   `urlOrPredicate` **([string][7] | [function][16])** 
 -   `sec` **[number][8]?** number of seconds to wait 
 
 ### waitForText
@@ -1684,7 +1694,7 @@ I.waitUntil(() => window.requests == 0, 5);
 
 #### Parameters
 
--   `fn` **([function][10] | [string][7])** function which is executed in browser context.
+-   `fn` **([function][16] | [string][7])** function which is executed in browser context.
 -   `sec` **[number][8]** (optional, `1` by default) time in seconds to wait 
 -   `timeoutMsg` **[string][7]** message to show in case of timeout fail. 
 -   `interval` **[number][8]?**  
@@ -1719,20 +1729,20 @@ I.waitUrlEquals('http://127.0.0.1:8000/info');
 
 [8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
 
-[9]: https://vuejs.org/v2/api/#Vue-nextTick
+[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
 
-[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function
+[10]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
 
-[11]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise
+[11]: https://github.com/microsoft/playwright/blob/v0.12.1/docs/api.md#browsernewpageoptions
 
-[12]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
+[12]: #fillfield
 
-[13]: #fillfield
+[13]: https://github.com/GoogleChrome/Playwright/issues/1313
 
-[14]: https://github.com/GoogleChrome/Playwright/issues/1313
+[14]: #click
 
-[15]: #click
+[15]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
 
-[16]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
+[16]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Statements/function
 
 [17]: https://codecept.io/react
diff --git a/docs/helpers/Protractor.md b/docs/helpers/Protractor.md
index fe0bb354..3e0ec644 100644
--- a/docs/helpers/Protractor.md
+++ b/docs/helpers/Protractor.md
@@ -1207,15 +1207,23 @@ I.selectOption('Which OS do you use?', ['Android', 'iOS']);
 
 ### setCookie
 
-Sets a cookie.
+Sets cookie(s).
+
+Can be a single cookie object or an array of cookies:
 
 ```js
 I.setCookie({name: 'auth', value: true});
+
+// as array
+I.setCookie([
+  {name: 'auth', value: true},
+  {name: 'agree', value: true}
+]);
 ```
 
 #### Parameters
 
--   `cookie` **[object][10]** a cookie object.
+-   `cookie` **([object][10] | [array][14])** a cookie object or array of cookie objects.
 
 ### switchTo
 
diff --git a/docs/helpers/Puppeteer.md b/docs/helpers/Puppeteer.md
index c0d60459..9a3092ab 100644
--- a/docs/helpers/Puppeteer.md
+++ b/docs/helpers/Puppeteer.md
@@ -287,7 +287,7 @@ I.attachFile('form input[name=avatar]', 'data/avatar.jpg');
 #### Parameters
 
 -   `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.
+-   `pathToFile` **[string][8]** local file path relative to codecept.json config file.> ⚠ There is an [issue with file upload in Puppeteer 2.1.0 & 2.1.1][9], downgrade to 2.0.0 if you face it.
 
 ### cancelPopup
 
@@ -601,7 +601,7 @@ I.dragSlider('#slider', -70);
 #### Parameters
 
 -   `locator` **([string][8] | [object][6])** located by label|name|CSS|XPath|strict locator.
--   `offsetX` **[number][9]** position to drag.
+-   `offsetX` **[number][10]** position to drag.
     
 
 
@@ -613,7 +613,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][10].
+Example: In Vue.js to make components completely rendered we are waiting for [nextTick][11].
 
 ```js
 I.executeAsyncScript(function(done) {
@@ -633,10 +633,10 @@ let val = await I.executeAsyncScript(function(url, done) {
 
 #### Parameters
 
--   `fn` **([string][8] | [function][11])** function to be executed in browser context.
+-   `fn` **([string][8] | [function][12])** function to be executed in browser context.
 -   `args` **...any** to be passed to function.
 
-Returns **[Promise][12]&lt;any>** Asynchronous scripts can also be executed with `executeScript` if a function returns a Promise.
+Returns **[Promise][13]&lt;any>** Asynchronous scripts can also be executed with `executeScript` if a function returns a Promise.
 
 ### executeScript
 
@@ -666,10 +666,10 @@ let date = await I.executeScript(function(el) {
 
 #### Parameters
 
--   `fn` **([string][8] | [function][11])** function to be executed in browser context.
+-   `fn` **([string][8] | [function][12])** function to be executed in browser context.
 -   `args` **...any** to be passed to function.
 
-Returns **[Promise][12]&lt;any>** If a function returns a Promise It will wait for it resolution.
+Returns **[Promise][13]&lt;any>** If a function returns a Promise It will wait for it resolution.
 
 ### fillField
 
@@ -711,7 +711,7 @@ let hint = await I.grabAttributeFrom('#tooltip', 'title');
 -   `locator` **([string][8] | [object][6])** element located by CSS|XPath|strict locator.
 -   `attr` **[string][8]** attribute name.
 
-Returns **[Promise][12]&lt;[string][8]>** attribute value
+Returns **[Promise][13]&lt;[string][8]>** attribute value
 
 
 
@@ -727,7 +727,7 @@ let logs = await I.grabBrowserLogs();
 console.log(JSON.stringify(logs))
 ```
 
-Returns **[Promise][12]&lt;[Array][13]&lt;any>>** 
+Returns **[Promise][13]&lt;[Array][14]&lt;any>>** 
 
 ### grabCookie
 
@@ -744,7 +744,7 @@ assert(cookie.value, '123456');
 
 -   `name` **[string][8]?** cookie name. 
 
-Returns **[Promise][12]&lt;[string][8]>** attribute valueReturns cookie in JSON format. If name not passed returns all cookies for this domain.
+Returns **[Promise][13]&lt;[string][8]>** attribute valueReturns cookie in JSON format. If name not passed returns all cookies for this domain.
 
 ### grabCssPropertyFrom
 
@@ -760,7 +760,7 @@ const value = await I.grabCssPropertyFrom('h3', 'font-weight');
 -   `locator` **([string][8] | [object][6])** element located by CSS|XPath|strict locator.
 -   `cssProperty` **[string][8]** CSS property name.
 
-Returns **[Promise][12]&lt;[string][8]>** CSS value
+Returns **[Promise][13]&lt;[string][8]>** CSS value
 
 
 
@@ -777,7 +777,7 @@ let url = await I.grabCurrentUrl();
 console.log(`Current URL is [${url}]`);
 ```
 
-Returns **[Promise][12]&lt;[string][8]>** current URL
+Returns **[Promise][13]&lt;[string][8]>** current URL
 
 ### grabDataFromPerformanceTiming
 
@@ -845,7 +845,7 @@ let postHTML = await I.grabHTMLFrom('#post');
 -   `locator`  
 -   `element` **([string][8] | [object][6])** located by CSS|XPath|strict locator.
 
-Returns **[Promise][12]&lt;[string][8]>** HTML code for an element
+Returns **[Promise][13]&lt;[string][8]>** HTML code for an element
 
 ### grabNumberOfOpenTabs
 
@@ -855,7 +855,7 @@ Grab number of open tabs.
 let tabs = await I.grabNumberOfOpenTabs();
 ```
 
-Returns **[Promise][12]&lt;[number][9]>** number of open tabs
+Returns **[Promise][13]&lt;[number][10]>** number of open tabs
 
 ### grabNumberOfVisibleElements
 
@@ -869,7 +869,7 @@ let numOfElements = await I.grabNumberOfVisibleElements('p');
 
 -   `locator` **([string][8] | [object][6])** located by CSS|XPath|strict locator.
 
-Returns **[Promise][12]&lt;[number][9]>** number of visible elements
+Returns **[Promise][13]&lt;[number][10]>** number of visible elements
 
 
 
@@ -885,7 +885,7 @@ Resumes test execution, so **should be used inside an async function with `await
 let { x, y } = await I.grabPageScrollPosition();
 ```
 
-Returns **[Promise][12]&lt;[Object][6]&lt;[string][8], any>>** scroll position
+Returns **[Promise][13]&lt;[Object][6]&lt;[string][8], any>>** scroll position
 
 ### grabPopupText
 
@@ -895,7 +895,7 @@ Grab the text within the popup. If no popup is visible then it will return null
 await I.grabPopupText();
 ```
 
-Returns **[Promise][12]&lt;([string][8] | null)>** 
+Returns **[Promise][13]&lt;([string][8] | null)>** 
 
 ### grabSource
 
@@ -906,7 +906,7 @@ Resumes test execution, so should be used inside an async function.
 let pageSource = await I.grabSource();
 ```
 
-Returns **[Promise][12]&lt;[string][8]>** source code
+Returns **[Promise][13]&lt;[string][8]>** source code
 
 ### grabTextFrom
 
@@ -923,7 +923,7 @@ If multiple elements found returns an array of texts.
 
 -   `locator` **([string][8] | [object][6])** element located by CSS|XPath|strict locator.
 
-Returns **[Promise][12]&lt;([string][8] | [Array][13]&lt;[string][8]>)>** attribute value
+Returns **[Promise][13]&lt;([string][8] | [Array][14]&lt;[string][8]>)>** attribute value
 
 
 
@@ -939,7 +939,7 @@ Resumes test execution, so **should be used inside async with `await`** operator
 let title = await I.grabTitle();
 ```
 
-Returns **[Promise][12]&lt;[string][8]>** title
+Returns **[Promise][13]&lt;[string][8]>** title
 
 ### grabValueFrom
 
@@ -954,12 +954,12 @@ let email = await I.grabValueFrom('input[name=email]');
 
 -   `locator` **([string][8] | [object][6])** field located by label|name|CSS|XPath|strict locator.
 
-Returns **[Promise][12]&lt;[string][8]>** attribute value
+Returns **[Promise][13]&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][15] 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.
@@ -1002,8 +1002,8 @@ I.moveCursorTo('#submit', 5,5);
 #### Parameters
 
 -   `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.
+-   `offsetX` **[number][10]** (optional, `0` by default) X-axis offset. 
+-   `offsetY` **[number][10]** (optional, `0` by default) Y-axis offset.
 
 
 This action supports [React locators](https://codecept.io/react#locators)
@@ -1021,7 +1021,7 @@ I.openNewTab();
 
 Presses a key in the browser (on a focused element).
 
-_Hint:_ For populating text field or textarea, it is recommended to use [`fillField`][15].
+_Hint:_ For populating text field or textarea, it is recommended to use [`fillField`][16].
 
 ```js
 I.pressKey('Backspace');
@@ -1080,13 +1080,13 @@ Some of the supported key names are:
 
 #### Parameters
 
--   `key` **([string][8] | [Array][13]&lt;[string][8]>)** key or array of keys to press._Note:_ Shortcuts like `'Meta'` + `'A'` do not work on macOS ([GoogleChrome/puppeteer#1313][16]).
+-   `key` **([string][8] | [Array][14]&lt;[string][8]>)** key or array of keys to press._Note:_ Shortcuts like `'Meta'` + `'A'` do not work on macOS ([GoogleChrome/puppeteer#1313][17]).
 
 ### pressKeyDown
 
 Presses a key in the browser and leaves it in a down state.
 
-To make combinations with modifier key and user operation (e.g. `'Control'` + [`click`][17]).
+To make combinations with modifier key and user operation (e.g. `'Control'` + [`click`][18]).
 
 ```js
 I.pressKeyDown('Control');
@@ -1102,7 +1102,7 @@ I.pressKeyUp('Control');
 
 Releases a key in the browser which was previously set to a down state.
 
-To make combinations with modifier key and user operation (e.g. `'Control'` + [`click`][17]).
+To make combinations with modifier key and user operation (e.g. `'Control'` + [`click`][18]).
 
 ```js
 I.pressKeyDown('Control');
@@ -1129,8 +1129,8 @@ First parameter can be set to `maximize`.
 
 #### Parameters
 
--   `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!
+-   `width` **[number][10]** width in pixels or `maximize`.
+-   `height` **[number][10]** 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.
     It also can't maximize a window.
 
@@ -1170,7 +1170,7 @@ I.saveScreenshot('debug.png', true) //resizes to available scrollHeight and scro
 #### Parameters
 
 -   `fileName` **[string][8]** file name to save.
--   `fullPage` **[boolean][18]** (optional, `false` by default) flag to enable fullscreen screenshot mode. 
+-   `fullPage` **[boolean][19]** (optional, `false` by default) flag to enable fullscreen screenshot mode. 
 
 ### scrollPageToBottom
 
@@ -1201,8 +1201,8 @@ I.scrollTo('#submit', 5, 5);
 #### Parameters
 
 -   `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. 
+-   `offsetX` **[number][10]** (optional, `0` by default) X-axis offset. 
+-   `offsetY` **[number][10]** (optional, `0` by default) Y-axis offset. 
 
 ### see
 
@@ -1410,7 +1410,7 @@ I.seeNumberOfElements('#submitBtn', 1);
 #### Parameters
 
 -   `locator` **([string][8] | [object][6])** element located by CSS|XPath|strict locator.
--   `num` **[number][9]** number of elements.
+-   `num` **[number][10]** number of elements.
 
 
 This action supports [React locators](https://codecept.io/react#locators)
@@ -1428,7 +1428,7 @@ I.seeNumberOfVisibleElements('.buttons', 3);
 #### Parameters
 
 -   `locator` **([string][8] | [object][6])** element located by CSS|XPath|strict locator.
--   `num` **[number][9]** number of elements.
+-   `num` **[number][10]** number of elements.
 
 
 This action supports [React locators](https://codecept.io/react#locators)
@@ -1483,19 +1483,27 @@ I.selectOption('Which OS do you use?', ['Android', 'iOS']);
 #### Parameters
 
 -   `select` **([string][8] | [object][6])** field located by label|name|CSS|XPath|strict locator.
--   `option` **([string][8] | [Array][13]&lt;any>)** visible text or value of option.
+-   `option` **([string][8] | [Array][14]&lt;any>)** visible text or value of option.
 
 ### setCookie
 
-Sets a cookie.
+Sets cookie(s).
+
+Can be a single cookie object or an array of cookies:
 
 ```js
 I.setCookie({name: 'auth', value: true});
+
+// as array
+I.setCookie([
+  {name: 'auth', value: true},
+  {name: 'agree', value: true}
+]);
 ```
 
 #### Parameters
 
--   `cookie` **[object][6]** a cookie object.
+-   `cookie` **([object][6] | [array][14])** a cookie object or array of cookie objects.
 
 ### switchTo
 
@@ -1521,7 +1529,7 @@ I.switchToNextTab(2);
 
 #### Parameters
 
--   `num` **[number][9]**  
+-   `num` **[number][10]**  
 
 ### switchToPreviousTab
 
@@ -1534,7 +1542,7 @@ I.switchToPreviousTab(2);
 
 #### Parameters
 
--   `num` **[number][9]**  
+-   `num` **[number][10]**  
 
 ### uncheckOption
 
@@ -1564,7 +1572,7 @@ I.wait(2); // wait 2 secs
 
 #### Parameters
 
--   `sec` **[number][9]** number of second to wait.
+-   `sec` **[number][10]** number of second to wait.
 
 ### waitForClickable
 
@@ -1580,7 +1588,7 @@ I.waitForClickable('.btn.continue', 5); // wait for 5 secs
 
 -   `locator` **([string][8] | [object][6])** element located by CSS|XPath|strict locator.
 -   `waitTimeout`  
--   `sec` **[number][9]?** (optional, `1` by default) time in seconds to wait
+-   `sec` **[number][10]?** (optional, `1` by default) time in seconds to wait
 
 ### waitForDetached
 
@@ -1594,7 +1602,7 @@ I.waitForDetached('#popup');
 #### Parameters
 
 -   `locator` **([string][8] | [object][6])** element located by CSS|XPath|strict locator.
--   `sec` **[number][9]** (optional, `1` by default) time in seconds to wait 
+-   `sec` **[number][10]** (optional, `1` by default) time in seconds to wait 
 
 ### waitForElement
 
@@ -1609,7 +1617,7 @@ I.waitForElement('.btn.continue', 5); // wait for 5 secs
 #### Parameters
 
 -   `locator` **([string][8] | [object][6])** element located by CSS|XPath|strict locator.
--   `sec` **[number][9]?** (optional, `1` by default) time in seconds to wait
+-   `sec` **[number][10]?** (optional, `1` by default) time in seconds to wait
     
 
 
@@ -1624,7 +1632,7 @@ Element can be located by CSS or XPath.
 #### Parameters
 
 -   `locator` **([string][8] | [object][6])** element located by CSS|XPath|strict locator.
--   `sec` **[number][9]** (optional) time in seconds to wait, 1 by default. 
+-   `sec` **[number][10]** (optional) time in seconds to wait, 1 by default. 
 
 ### waitForFunction
 
@@ -1643,9 +1651,9 @@ I.waitForFunction((count) => window.requests == count, [3], 5) // pass args and
 
 #### Parameters
 
--   `fn` **([string][8] | [function][11])** to be executed in browser context.
--   `argsOrSec` **([Array][13]&lt;any> | [number][9])?** (optional, `1` by default) arguments for function or seconds. 
--   `sec` **[number][9]?** (optional, `1` by default) time in seconds to wait 
+-   `fn` **([string][8] | [function][12])** to be executed in browser context.
+-   `argsOrSec` **([Array][14]&lt;any> | [number][10])?** (optional, `1` by default) arguments for function or seconds. 
+-   `sec` **[number][10]?** (optional, `1` by default) time in seconds to wait 
 
 ### waitForInvisible
 
@@ -1659,7 +1667,7 @@ I.waitForInvisible('#popup');
 #### Parameters
 
 -   `locator` **([string][8] | [object][6])** element located by CSS|XPath|strict locator.
--   `sec` **[number][9]** (optional, `1` by default) time in seconds to wait 
+-   `sec` **[number][10]** (optional, `1` by default) time in seconds to wait 
 
 ### waitForNavigation
 
@@ -1682,8 +1690,8 @@ I.waitForRequest(request => request.url() === 'http://example.com' && request.me
 
 #### Parameters
 
--   `urlOrPredicate` **([string][8] | [function][11])** 
--   `sec` **[number][9]?** seconds to wait 
+-   `urlOrPredicate` **([string][8] | [function][12])** 
+-   `sec` **[number][10]?** seconds to wait 
 
 ### waitForResponse
 
@@ -1696,8 +1704,8 @@ I.waitForResponse(request => request.url() === 'http://example.com' && request.m
 
 #### Parameters
 
--   `urlOrPredicate` **([string][8] | [function][11])** 
--   `sec` **[number][9]?** number of seconds to wait 
+-   `urlOrPredicate` **([string][8] | [function][12])** 
+-   `sec` **[number][10]?** number of seconds to wait 
 
 ### waitForText
 
@@ -1713,7 +1721,7 @@ I.waitForText('Thank you, form has been submitted', 5, '#modal');
 #### Parameters
 
 -   `text` **[string][8]** to wait for.
--   `sec` **[number][9]** (optional, `1` by default) time in seconds to wait 
+-   `sec` **[number][10]** (optional, `1` by default) time in seconds to wait 
 -   `context` **([string][8] | [object][6])?** (optional) element located by CSS|XPath|strict locator. 
 
 ### waitForValue
@@ -1728,7 +1736,7 @@ I.waitForValue('//input', "GoodValue");
 
 -   `field` **([string][8] | [object][6])** input field.
 -   `value` **[string][8]** expected value.
--   `sec` **[number][9]** (optional, `1` by default) time in seconds to wait 
+-   `sec` **[number][10]** (optional, `1` by default) time in seconds to wait 
 
 ### waitForVisible
 
@@ -1742,7 +1750,7 @@ I.waitForVisible('#popup');
 #### Parameters
 
 -   `locator` **([string][8] | [object][6])** element located by CSS|XPath|strict locator.
--   `sec` **[number][9]** (optional, `1` by default) time in seconds to waitThis method accepts [React selectors][19]. 
+-   `sec` **[number][10]** (optional, `1` by default) time in seconds to waitThis method accepts [React selectors][20]. 
 
 ### waitInUrl
 
@@ -1755,7 +1763,7 @@ I.waitInUrl('/info', 2);
 #### Parameters
 
 -   `urlPart` **[string][8]** value to check.
--   `sec` **[number][9]** (optional, `1` by default) time in seconds to wait 
+-   `sec` **[number][10]** (optional, `1` by default) time in seconds to wait 
 
 ### waitNumberOfVisibleElements
 
@@ -1768,8 +1776,8 @@ I.waitNumberOfVisibleElements('a', 3);
 #### Parameters
 
 -   `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
+-   `num` **[number][10]** number of elements.
+-   `sec` **[number][10]** (optional, `1` by default) time in seconds to wait
     
 
 
@@ -1788,7 +1796,7 @@ I.waitToHide('#popup');
 #### Parameters
 
 -   `locator` **([string][8] | [object][6])** element located by CSS|XPath|strict locator.
--   `sec` **[number][9]** (optional, `1` by default) time in seconds to wait 
+-   `sec` **[number][10]** (optional, `1` by default) time in seconds to wait 
 
 ### waitUntil
 
@@ -1801,10 +1809,10 @@ I.waitUntil(() => window.requests == 0, 5);
 
 #### Parameters
 
--   `fn` **([function][11] | [string][8])** function which is executed in browser context.
--   `sec` **[number][9]** (optional, `1` by default) time in seconds to wait 
+-   `fn` **([function][12] | [string][8])** function which is executed in browser context.
+-   `sec` **[number][10]** (optional, `1` by default) time in seconds to wait 
 -   `timeoutMsg` **[string][8]** message to show in case of timeout fail. 
--   `interval` **[number][9]?**  
+-   `interval` **[number][10]?**  
 
 ### waitUrlEquals
 
@@ -1818,7 +1826,7 @@ I.waitUrlEquals('http://127.0.0.1:8000/info');
 #### Parameters
 
 -   `urlPart` **[string][8]** value to check.
--   `sec` **[number][9]** (optional, `1` by default) time in seconds to wait 
+-   `sec` **[number][10]** (optional, `1` by default) time in seconds to wait 
 
 [1]: https://github.com/GoogleChrome/puppeteer
 
@@ -1836,24 +1844,26 @@ I.waitUrlEquals('http://127.0.0.1:8000/info');
 
 [8]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
 
-[9]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
+[9]: https://github.com/puppeteer/puppeteer/issues/5420
+
+[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://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
+[14]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
 
-[14]: https://codecept.io/helpers/FileSystem
+[15]: https://codecept.io/helpers/FileSystem
 
-[15]: #fillfield
+[16]: #fillfield
 
-[16]: https://github.com/GoogleChrome/puppeteer/issues/1313
+[17]: https://github.com/GoogleChrome/puppeteer/issues/1313
 
-[17]: #click
+[18]: #click
 
-[18]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
+[19]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
 
-[19]: https://codecept.io/react
+[20]: https://codecept.io/react
diff --git a/docs/helpers/WebDriver.md b/docs/helpers/WebDriver.md
index 23a36e7e..60f0f3c9 100644
--- a/docs/helpers/WebDriver.md
+++ b/docs/helpers/WebDriver.md
@@ -352,6 +352,14 @@ const browser = WebDriver.browser
 
 -   `config`  
 
+### _isShadowLocator
+
+Check if locator is type of "Shadow"
+
+#### Parameters
+
+-   `locator` **[object][18]** 
+
 ### _locate
 
 Get elements by different locator types, including strict locator.
@@ -363,7 +371,7 @@ this.helpers['WebDriver']._locate({name: 'password'}).then //...
 
 #### Parameters
 
--   `locator` **([string][18] | [object][19])** element located by CSS|XPath|strict locator.
+-   `locator` **([string][19] | [object][18])** element located by CSS|XPath|strict locator.
 -   `smartWait`   
 
 ### _locateCheckable
@@ -376,7 +384,7 @@ this.helpers['WebDriver']._locateCheckable('I agree with terms and conditions').
 
 #### Parameters
 
--   `locator` **([string][18] | [object][19])** element located by CSS|XPath|strict locator.
+-   `locator` **([string][19] | [object][18])** element located by CSS|XPath|strict locator.
 
 ### _locateClickable
 
@@ -388,7 +396,7 @@ this.helpers['WebDriver']._locateClickable('Next page').then // ...
 
 #### Parameters
 
--   `locator` **([string][18] | [object][19])** element located by CSS|XPath|strict locator.
+-   `locator` **([string][19] | [object][18])** element located by CSS|XPath|strict locator.
 
 ### _locateFields
 
@@ -400,7 +408,23 @@ this.helpers['WebDriver']._locateFields('Your email').then // ...
 
 #### Parameters
 
--   `locator` **([string][18] | [object][19])** element located by CSS|XPath|strict locator.
+-   `locator` **([string][19] | [object][18])** element located by CSS|XPath|strict locator.
+
+### _locateShadow
+
+Locate Element within the Shadow Dom
+
+#### Parameters
+
+-   `locator` **[object][18]** 
+
+### _smartWait
+
+Smart Wait to locate an element
+
+#### Parameters
+
+-   `locator` **[object][18]** 
 
 ### acceptPopup
 
@@ -421,7 +445,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.
 
 ### appendField
 
@@ -434,8 +458,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][18])** located by label|name|CSS|XPath|strict locator
+-   `value` **[string][19]** text value to append.
     
 
 
@@ -455,8 +479,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][18])** field located by label|name|CSS|XPath|strict locator.
+-   `pathToFile` **[string][19]** local file path relative to codecept.json config file.
     Appium: not tested
 
 ### cancelPopup
@@ -478,8 +502,8 @@ I.checkOption('agree', '//form');
 
 #### Parameters
 
--   `field` **([string][18] | [object][19])** checkbox located by label | name | CSS | XPath | strict locator.
--   `context` **([string][18]? | [object][19])** (optional, `null` by default) element located by CSS | XPath | strict locator.
+-   `field` **([string][19] | [object][18])** checkbox located by label | name | CSS | XPath | strict locator.
+-   `context` **([string][19]? | [object][18])** (optional, `null` by default) element located by CSS | XPath | strict locator.
     Appium: not tested 
 
 ### clearCookie
@@ -494,7 +518,7 @@ I.clearCookie('test');
 
 #### Parameters
 
--   `cookie` **[string][18]?** (optional, `null` by default) cookie name 
+-   `cookie` **[string][19]?** (optional, `null` by default) cookie name 
 
 ### clearField
 
@@ -509,7 +533,7 @@ I.clearField('#email');
 #### Parameters
 
 -   `field`  
--   `editable` **([string][18] | [object][19])** field located by label|name|CSS|XPath|strict locator.
+-   `editable` **([string][19] | [object][18])** field located by label|name|CSS|XPath|strict locator.
 
 ### click
 
@@ -537,8 +561,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][18])** clickable link or button located by text, or any element located by CSS|XPath|strict locator.
+-   `context` **([string][19]? | [object][18])** (optional, `null` by default) element to search in CSS|XPath|Strict locator.
 
 
 This action supports [React locators](https://codecept.io/react#locators)
@@ -587,8 +611,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][18])?** (optional) element located by CSS|XPath|strict locator in which to perfrom search.
 
 
 This action supports [React locators](https://codecept.io/react#locators)
@@ -606,7 +630,7 @@ I.dontSeeCheckboxIsChecked('agree'); // located by name
 
 #### Parameters
 
--   `field` **([string][18] | [object][19])** located by label|name|CSS|XPath|strict locator.Appium: not tested
+-   `field` **([string][19] | [object][18])** located by label|name|CSS|XPath|strict locator.Appium: not tested
 
 ### dontSeeCookie
 
@@ -618,7 +642,7 @@ I.dontSeeCookie('auth'); // no auth cookie
 
 #### Parameters
 
--   `name` **[string][18]** cookie name.
+-   `name` **[string][19]** cookie name.
 
 ### dontSeeCurrentUrlEquals
 
@@ -632,7 +656,7 @@ I.dontSeeCurrentUrlEquals('http://mysite.com/login'); // absolute urls are also
 
 #### Parameters
 
--   `url` **[string][18]** value to check.
+-   `url` **[string][19]** value to check.
 
 ### dontSeeElement
 
@@ -644,7 +668,7 @@ I.dontSeeElement('.modal'); // modal is not shown
 
 #### Parameters
 
--   `locator` **([string][18] | [object][19])** located by CSS|XPath|Strict locator.
+-   `locator` **([string][19] | [object][18])** located by CSS|XPath|Strict locator.
     
 
 
@@ -661,7 +685,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][18])** located by CSS|XPath|Strict locator.
 
 ### dontSeeInCurrentUrl
 
@@ -669,7 +693,7 @@ Checks that current url does not contain a provided fragment.
 
 #### Parameters
 
--   `url` **[string][18]** value to check.
+-   `url` **[string][19]** value to check.
 
 ### dontSeeInField
 
@@ -683,8 +707,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][18])** located by label|name|CSS|XPath|strict locator.
+-   `value` **[string][19]** value to check.
 
 ### dontSeeInSource
 
@@ -697,7 +721,7 @@ I.dontSeeInSource('<!--'); // no comments in source
 #### Parameters
 
 -   `text`  
--   `value` **[string][18]** to check.
+-   `value` **[string][19]** to check.
 
 ### dontSeeInTitle
 
@@ -709,7 +733,7 @@ I.dontSeeInTitle('Error');
 
 #### Parameters
 
--   `text` **[string][18]** value to check.
+-   `text` **[string][19]** value to check.
 
 ### doubleClick
 
@@ -725,8 +749,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][18])** clickable link or button located by text, or any element located by CSS|XPath|strict locator.
+-   `context` **([string][19]? | [object][18])** (optional, `null` by default) element to search in CSS|XPath|Strict locator.
 
 
 This action supports [React locators](https://codecept.io/react#locators)
@@ -742,8 +766,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][18])** located by CSS|XPath|strict locator.
+-   `destElement` **([string][19] | [object][18])** located by CSS|XPath|strict locator.
     Appium: not tested
 
 ### dragSlider
@@ -758,7 +782,7 @@ I.dragSlider('#slider', -70);
 
 #### Parameters
 
--   `locator` **([string][18] | [object][19])** located by label|name|CSS|XPath|strict locator.
+-   `locator` **([string][19] | [object][18])** located by label|name|CSS|XPath|strict locator.
 -   `offsetX` **[number][22]** position to drag. 
 
 ### executeAsyncScript
@@ -786,7 +810,7 @@ let val = await I.executeAsyncScript(function(url, done) {
 
 #### Parameters
 
--   `fn` **([string][18] | [function][24])** function to be executed in browser context.
+-   `fn` **([string][19] | [function][24])** function to be executed in browser context.
 -   `args` **...any** to be passed to function.
 
 Returns **[Promise][25]&lt;any>** 
@@ -819,7 +843,7 @@ let date = await I.executeScript(function(el) {
 
 #### Parameters
 
--   `fn` **([string][18] | [function][24])** function to be executed in browser context.
+-   `fn` **([string][19] | [function][24])** function to be executed in browser context.
 -   `args` **...any** to be passed to function.
 
 Returns **[Promise][25]&lt;any>** Wraps [execute][26] command.
@@ -842,8 +866,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][18])** located by label|name|CSS|XPath|strict locator.
+-   `value` **[string][19]** text value to fill.
 
 
 This action supports [React locators](https://codecept.io/react#locators)
@@ -870,10 +894,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][18])** element located by CSS|XPath|strict locator.
+-   `attr` **[string][19]** attribute name.
 
-Returns **[Promise][25]&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")
 
 ### grabBrowserLogs
@@ -885,7 +909,7 @@ let logs = await I.grabBrowserLogs();
 console.log(JSON.stringify(logs))
 ```
 
-Returns **[Promise][25]&lt;([string][18] | [undefined][27])>** 
+Returns **[Promise][25]&lt;([string][19] | [undefined][27])>** 
 
 ### grabCookie
 
@@ -900,9 +924,9 @@ assert(cookie.value, '123456');
 
 #### Parameters
 
--   `name` **[string][18]?** cookie name. 
+-   `name` **[string][19]?** cookie name. 
 
-Returns **[Promise][25]&lt;[string][18]>** attribute value
+Returns **[Promise][25]&lt;[string][19]>** attribute value
 
 ### grabCssPropertyFrom
 
@@ -915,10 +939,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][18])** element located by CSS|XPath|strict locator.
+-   `cssProperty` **[string][19]** CSS property name.
 
-Returns **[Promise][25]&lt;[string][18]>** CSS value
+Returns **[Promise][25]&lt;[string][19]>** CSS value
 
 ### grabCurrentUrl
 
@@ -930,7 +954,7 @@ let url = await I.grabCurrentUrl();
 console.log(`Current URL is [${url}]`);
 ```
 
-Returns **[Promise][25]&lt;[string][18]>** current URL
+Returns **[Promise][25]&lt;[string][19]>** current URL
 
 ### grabCurrentWindowHandle
 
@@ -963,11 +987,11 @@ const width = await I.grabElementBoundingRect('h3', 'width');
 
 #### Parameters
 
--   `locator` **([string][18] | [object][19])** element located by CSS|XPath|strict locator.
+-   `locator` **([string][19] | [object][18])** element located by CSS|XPath|strict locator.
 -   `prop`  
--   `elementSize` **[string][18]** x, y, width or height of the given element.
+-   `elementSize` **[string][19]** x, y, width or height of the given element.
 
-Returns **[object][19]** Element bounding rectangle
+Returns **[object][18]** Element bounding rectangle
 
 ### grabGeoLocation
 
@@ -990,9 +1014,9 @@ let postHTML = await I.grabHTMLFrom('#post');
 #### Parameters
 
 -   `locator`  
--   `element` **([string][18] | [object][19])** located by CSS|XPath|strict locator.
+-   `element` **([string][19] | [object][18])** located by CSS|XPath|strict locator.
 
-Returns **[Promise][25]&lt;[string][18]>** HTML code for an element
+Returns **[Promise][25]&lt;[string][19]>** HTML code for an element
 
 ### grabNumberOfOpenTabs
 
@@ -1014,7 +1038,7 @@ let numOfElements = await I.grabNumberOfVisibleElements('p');
 
 #### Parameters
 
--   `locator` **([string][18] | [object][19])** located by CSS|XPath|strict locator.
+-   `locator` **([string][19] | [object][18])** located by CSS|XPath|strict locator.
 
 Returns **[Promise][25]&lt;[number][22]>** number of visible elements
 
@@ -1027,7 +1051,7 @@ Resumes test execution, so **should be used inside an async function with `await
 let { x, y } = await I.grabPageScrollPosition();
 ```
 
-Returns **[Promise][25]&lt;[Object][19]&lt;[string][18], any>>** scroll position
+Returns **[Promise][25]&lt;[Object][18]&lt;[string][19], any>>** scroll position
 
 ### grabPopupText
 
@@ -1046,7 +1070,7 @@ Resumes test execution, so should be used inside an async function.
 let pageSource = await I.grabSource();
 ```
 
-Returns **[Promise][25]&lt;[string][18]>** source code
+Returns **[Promise][25]&lt;[string][19]>** source code
 
 ### grabTextFrom
 
@@ -1061,9 +1085,9 @@ If multiple elements found returns an array of texts.
 
 #### Parameters
 
--   `locator` **([string][18] | [object][19])** element located by CSS|XPath|strict locator.
+-   `locator` **([string][19] | [object][18])** element located by CSS|XPath|strict locator.
 
-Returns **[Promise][25]&lt;([string][18] | [Array][28]&lt;[string][18]>)>** attribute value
+Returns **[Promise][25]&lt;([string][19] | [Array][28]&lt;[string][19]>)>** attribute value
 
 ### grabTitle
 
@@ -1074,7 +1098,7 @@ Resumes test execution, so **should be used inside async with `await`** operator
 let title = await I.grabTitle();
 ```
 
-Returns **[Promise][25]&lt;[string][18]>** title
+Returns **[Promise][25]&lt;[string][19]>** title
 
 ### grabValueFrom
 
@@ -1087,14 +1111,14 @@ 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][18])** field located by label|name|CSS|XPath|strict locator.
 
-Returns **[Promise][25]&lt;[string][18]>** attribute value
+Returns **[Promise][25]&lt;[string][19]>** attribute value
 
 ### moveCursorTo
 
 Moves cursor to element matched by locator.
-Extra shift can be set with xOffset and yOffset options.
+Extra shift can be set with offsetX and offsetY options.
 
 ```js
 I.moveCursorTo('.tooltip');
@@ -1103,9 +1127,11 @@ I.moveCursorTo('#submit', 5,5);
 
 #### Parameters
 
--   `locator` **([string][18] | [object][19])** located by CSS|XPath|strict locator.
--   `offsetX` **[number][22]** (optional, `undefined` by default) X-axis offset. 
--   `offsetY` **[number][22]** (optional, `undefined` by default) Y-axis offset. 
+-   `locator` **([string][19] | [object][18])** located by CSS|XPath|strict locator.
+-   `xOffset`  
+-   `yOffset`  
+-   `offsetX` **[number][22]** (optional, `0` by default) X-axis offset. 
+-   `offsetY` **[number][22]** (optional, `0` by default) Y-axis offset. 
 
 ### openNewTab
 
@@ -1183,7 +1209,7 @@ Some of the supported key names are:
 
 #### Parameters
 
--   `key` **([string][18] | [Array][28]&lt;[string][18]>)** key or array of keys to press._Note:_ In case a text field or textarea is focused be aware that some browsers do not respect active modifier when combining modifier keys with other keys.
+-   `key` **([string][19] | [Array][28]&lt;[string][19]>)** key or array of keys to press._Note:_ In case a text field or textarea is focused be aware that some browsers do not respect active modifier when combining modifier keys with other keys.
 
 ### pressKeyDown
 
@@ -1199,7 +1225,7 @@ I.pressKeyUp('Control');
 
 #### Parameters
 
--   `key` **[string][18]** name of key to press down.
+-   `key` **[string][19]** name of key to press down.
 
 ### pressKeyUp
 
@@ -1215,7 +1241,7 @@ I.pressKeyUp('Control');
 
 #### Parameters
 
--   `key` **[string][18]** name of key to release.
+-   `key` **[string][19]** name of key to release.
 
 ### refreshPage
 
@@ -1251,8 +1277,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][18])** clickable element located by CSS|XPath|strict locator.
+-   `context` **([string][19]? | [object][18])** (optional, `null` by default) element located by CSS|XPath|strict locator.
 
 
 This action supports [React locators](https://codecept.io/react#locators)
@@ -1297,7 +1323,7 @@ I.saveScreenshot('debug.png', true) //resizes to available scrollHeight and scro
 
 #### Parameters
 
--   `fileName` **[string][18]** file name to save.
+-   `fileName` **[string][19]** file name to save.
 -   `fullPage` **[boolean][31]** (optional, `false` by default) flag to enable fullscreen screenshot mode. 
 
 ### scrollIntoView
@@ -1312,9 +1338,9 @@ I.scrollIntoView('#submit', { behavior: "smooth", block: "center", inline: "cent
 
 #### Parameters
 
--   `locator` **([string][18] | [object][19])** located by CSS|XPath|strict locator.
+-   `locator` **([string][19] | [object][18])** located by CSS|XPath|strict locator.
 -   `scrollIntoViewOptions`  
--   `alignToTop` **([boolean][31] | [object][19])** (optional) or scrollIntoViewOptions (optional), see [https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView][32].
+-   `alignToTop` **([boolean][31] | [object][18])** (optional) or scrollIntoViewOptions (optional), see [https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView][32].
 
 ### scrollPageToBottom
 
@@ -1344,7 +1370,7 @@ I.scrollTo('#submit', 5, 5);
 
 #### Parameters
 
--   `locator` **([string][18] | [object][19])** located by CSS|XPath|strict locator.
+-   `locator` **([string][19] | [object][18])** 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. 
 
@@ -1361,8 +1387,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][18])** (optional, `null` by default) element located by CSS|Xpath|strict locator in which to search for text.
 
 
 This action supports [React locators](https://codecept.io/react#locators)
@@ -1378,8 +1404,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][18])** located by CSS|XPath|strict locator.
+-   `attributes` **[object][18]** attributes and their values to check.
 
 ### seeCheckboxIsChecked
 
@@ -1393,7 +1419,7 @@ I.seeCheckboxIsChecked({css: '#signup_form input[type=checkbox]'});
 
 #### Parameters
 
--   `field` **([string][18] | [object][19])** located by label|name|CSS|XPath|strict locator.Appium: not tested
+-   `field` **([string][19] | [object][18])** located by label|name|CSS|XPath|strict locator.Appium: not tested
 
 ### seeCookie
 
@@ -1405,7 +1431,7 @@ I.seeCookie('Auth');
 
 #### Parameters
 
--   `name` **[string][18]** cookie name.
+-   `name` **[string][19]** cookie name.
 
 ### seeCssPropertiesOnElements
 
@@ -1417,8 +1443,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][18])** located by CSS|XPath|strict locator.
+-   `cssProperties` **[object][18]** object with CSS properties and their values to check.
 
 ### seeCurrentUrlEquals
 
@@ -1433,7 +1459,7 @@ I.seeCurrentUrlEquals('http://my.site.com/register');
 
 #### Parameters
 
--   `url` **[string][18]** value to check.
+-   `url` **[string][19]** value to check.
 
 ### seeElement
 
@@ -1446,7 +1472,7 @@ I.seeElement('#modal');
 
 #### Parameters
 
--   `locator` **([string][18] | [object][19])** located by CSS|XPath|strict locator.
+-   `locator` **([string][19] | [object][18])** located by CSS|XPath|strict locator.
     
 
 
@@ -1464,7 +1490,7 @@ I.seeElementInDOM('#modal');
 
 #### Parameters
 
--   `locator` **([string][18] | [object][19])** element located by CSS|XPath|strict locator.
+-   `locator` **([string][19] | [object][18])** element located by CSS|XPath|strict locator.
 
 ### seeInCurrentUrl
 
@@ -1476,7 +1502,7 @@ I.seeInCurrentUrl('/register'); // we are on registration page
 
 #### Parameters
 
--   `url` **[string][18]** a fragment to check
+-   `url` **[string][19]** a fragment to check
 
 ### seeInField
 
@@ -1492,8 +1518,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][18])** located by label|name|CSS|XPath|strict locator.
+-   `value` **[string][19]** value to check.
 
 ### seeInPopup
 
@@ -1502,7 +1528,7 @@ given string.
 
 #### Parameters
 
--   `text` **[string][18]** value to check.
+-   `text` **[string][19]** value to check.
 
 ### seeInSource
 
@@ -1514,7 +1540,7 @@ I.seeInSource('<h1>Green eggs &amp; ham</h1>');
 
 #### Parameters
 
--   `text` **[string][18]** value to check.
+-   `text` **[string][19]** value to check.
 
 ### seeInTitle
 
@@ -1526,7 +1552,7 @@ I.seeInTitle('Home Page');
 
 #### Parameters
 
--   `text` **[string][18]** text value to check.
+-   `text` **[string][19]** text value to check.
 
 ### seeNumberOfElements
 
@@ -1539,7 +1565,7 @@ I.seeNumberOfElements('#submitBtn', 1);
 
 #### Parameters
 
--   `locator` **([string][18] | [object][19])** element located by CSS|XPath|strict locator.
+-   `locator` **([string][19] | [object][18])** element located by CSS|XPath|strict locator.
 -   `num` **[number][22]** number of elements.
 
 
@@ -1557,7 +1583,7 @@ I.seeNumberOfVisibleElements('.buttons', 3);
 
 #### Parameters
 
--   `locator` **([string][18] | [object][19])** element located by CSS|XPath|strict locator.
+-   `locator` **([string][19] | [object][18])** element located by CSS|XPath|strict locator.
 -   `num` **[number][22]** number of elements.
 
 
@@ -1574,8 +1600,8 @@ I.seeTextEquals('text', 'h1');
 
 #### Parameters
 
--   `text` **[string][18]** element value to check.
--   `context` **([string][18] | [object][19]?)** element located by CSS|XPath|strict locator. 
+-   `text` **[string][19]** element value to check.
+-   `context` **([string][19] | [object][18]?)** element located by CSS|XPath|strict locator. 
 
 ### seeTitleEquals
 
@@ -1587,7 +1613,7 @@ I.seeTitleEquals('Test title.');
 
 #### Parameters
 
--   `text` **[string][18]** value to check.
+-   `text` **[string][19]** value to check.
 
 ### selectOption
 
@@ -1612,20 +1638,28 @@ 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][28]&lt;any>)** visible text or value of option.
+-   `select` **([string][19] | [object][18])** field located by label|name|CSS|XPath|strict locator.
+-   `option` **([string][19] | [Array][28]&lt;any>)** visible text or value of option.
 
 ### setCookie
 
-Sets a cookie.
+Sets cookie(s).
+
+Can be a single cookie object or an array of cookies:
 
 ```js
 I.setCookie({name: 'auth', value: true});
+
+// as array
+I.setCookie([
+  {name: 'auth', value: true},
+  {name: 'agree', value: true}
+]);
 ```
 
 #### Parameters
 
--   `cookie` **[object][19]** a cookie object.Uses Selenium's JSON [cookie
+-   `cookie` **([object][18] | [array][28])** a cookie object or array of cookie objects.Uses Selenium's JSON [cookie
     format][33].
 
 ### setGeoLocation
@@ -1654,7 +1688,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][18])** (optional, `null` by default) element located by CSS|XPath|strict locator. 
 
 ### switchToNextTab
 
@@ -1717,7 +1751,7 @@ I.type(['T', 'E', 'X', 'T']);
 #### Parameters
 
 -   `keys`  
--   `key` **([string][18] | [Array][28]&lt;[string][18]>)** or array of keys to type.
+-   `key` **([string][19] | [Array][28]&lt;[string][19]>)** or array of keys to type.
     Type out given array of keys or a string of text
 
 ### uncheckOption
@@ -1735,8 +1769,8 @@ I.uncheckOption('agree', '//form');
 
 #### Parameters
 
--   `field` **([string][18] | [object][19])** checkbox located by label | name | CSS | XPath | strict locator.
--   `context` **([string][18]? | [object][19])** (optional, `null` by default) element located by CSS | XPath | strict locator.
+-   `field` **([string][19] | [object][18])** checkbox located by label | name | CSS | XPath | strict locator.
+-   `context` **([string][19]? | [object][18])** (optional, `null` by default) element located by CSS | XPath | strict locator.
     Appium: not tested 
 
 ### wait
@@ -1763,7 +1797,7 @@ I.waitForClickable('.btn.continue', 5); // wait for 5 secs
 
 #### Parameters
 
--   `locator` **([string][18] | [object][19])** element located by CSS|XPath|strict locator.
+-   `locator` **([string][19] | [object][18])** element located by CSS|XPath|strict locator.
 -   `waitTimeout`  
 -   `sec` **[number][22]?** (optional, `1` by default) time in seconds to wait
 
@@ -1778,7 +1812,7 @@ I.waitForDetached('#popup');
 
 #### Parameters
 
--   `locator` **([string][18] | [object][19])** element located by CSS|XPath|strict locator.
+-   `locator` **([string][19] | [object][18])** element located by CSS|XPath|strict locator.
 -   `sec` **[number][22]** (optional, `1` by default) time in seconds to wait 
 
 ### waitForElement
@@ -1793,7 +1827,7 @@ I.waitForElement('.btn.continue', 5); // wait for 5 secs
 
 #### Parameters
 
--   `locator` **([string][18] | [object][19])** element located by CSS|XPath|strict locator.
+-   `locator` **([string][19] | [object][18])** element located by CSS|XPath|strict locator.
 -   `sec` **[number][22]?** (optional, `1` by default) time in seconds to wait 
 
 ### waitForEnabled
@@ -1803,7 +1837,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][18])** element located by CSS|XPath|strict locator.
 -   `sec` **[number][22]** (optional) time in seconds to wait, 1 by default. 
 
 ### waitForFunction
@@ -1823,7 +1857,7 @@ I.waitForFunction((count) => window.requests == count, [3], 5) // pass args and
 
 #### Parameters
 
--   `fn` **([string][18] | [function][24])** to be executed in browser context.
+-   `fn` **([string][19] | [function][24])** to be executed in browser context.
 -   `argsOrSec` **([Array][28]&lt;any> | [number][22])?** (optional, `1` by default) arguments for function or seconds. 
 -   `sec` **[number][22]?** (optional, `1` by default) time in seconds to wait 
 
@@ -1838,7 +1872,7 @@ I.waitForInvisible('#popup');
 
 #### Parameters
 
--   `locator` **([string][18] | [object][19])** element located by CSS|XPath|strict locator.
+-   `locator` **([string][19] | [object][18])** element located by CSS|XPath|strict locator.
 -   `sec` **[number][22]** (optional, `1` by default) time in seconds to wait 
 
 ### waitForText
@@ -1854,9 +1888,9 @@ I.waitForText('Thank you, form has been submitted', 5, '#modal');
 
 #### Parameters
 
--   `text` **[string][18]** to wait for.
+-   `text` **[string][19]** to wait for.
 -   `sec` **[number][22]** (optional, `1` by default) time in seconds to wait 
--   `context` **([string][18] | [object][19])?** (optional) element located by CSS|XPath|strict locator. 
+-   `context` **([string][19] | [object][18])?** (optional) element located by CSS|XPath|strict locator. 
 
 ### waitForValue
 
@@ -1868,8 +1902,8 @@ I.waitForValue('//input', "GoodValue");
 
 #### Parameters
 
--   `field` **([string][18] | [object][19])** input field.
--   `value` **[string][18]** expected value.
+-   `field` **([string][19] | [object][18])** input field.
+-   `value` **[string][19]** expected value.
 -   `sec` **[number][22]** (optional, `1` by default) time in seconds to wait 
 
 ### waitForVisible
@@ -1883,7 +1917,7 @@ I.waitForVisible('#popup');
 
 #### Parameters
 
--   `locator` **([string][18] | [object][19])** element located by CSS|XPath|strict locator.
+-   `locator` **([string][19] | [object][18])** element located by CSS|XPath|strict locator.
 -   `sec` **[number][22]** (optional, `1` by default) time in seconds to wait 
 
 ### waitInUrl
@@ -1896,7 +1930,7 @@ I.waitInUrl('/info', 2);
 
 #### Parameters
 
--   `urlPart` **[string][18]** value to check.
+-   `urlPart` **[string][19]** value to check.
 -   `sec` **[number][22]** (optional, `1` by default) time in seconds to wait 
 
 ### waitNumberOfVisibleElements
@@ -1909,7 +1943,7 @@ I.waitNumberOfVisibleElements('a', 3);
 
 #### Parameters
 
--   `locator` **([string][18] | [object][19])** element located by CSS|XPath|strict locator.
+-   `locator` **([string][19] | [object][18])** 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 
 
@@ -1924,7 +1958,7 @@ I.waitToHide('#popup');
 
 #### Parameters
 
--   `locator` **([string][18] | [object][19])** element located by CSS|XPath|strict locator.
+-   `locator` **([string][19] | [object][18])** element located by CSS|XPath|strict locator.
 -   `sec` **[number][22]** (optional, `1` by default) time in seconds to wait 
 
 ### waitUntil
@@ -1938,9 +1972,9 @@ I.waitUntil(() => window.requests == 0, 5);
 
 #### Parameters
 
--   `fn` **([function][24] | [string][18])** function which is executed in browser context.
+-   `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][18]** message to show in case of timeout fail. 
+-   `timeoutMsg` **[string][19]** message to show in case of timeout fail. 
 -   `interval` **[number][22]?**  
 
 ### waitUrlEquals
@@ -1954,7 +1988,7 @@ I.waitUrlEquals('http://127.0.0.1:8000/info');
 
 #### Parameters
 
--   `urlPart` **[string][18]** value to check.
+-   `urlPart` **[string][19]** value to check.
 -   `sec` **[number][22]** (optional, `1` by default) time in seconds to wait 
 
 [1]: http://webdriver.io/
@@ -1991,9 +2025,9 @@ I.waitUrlEquals('http://127.0.0.1:8000/info');
 
 [17]: http://webdriver.io/guide/usage/multiremote.html
 
-[18]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
+[18]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
 
-[19]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
+[19]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
 
 [20]: http://jster.net/category/windows-modals-popups
 
diff --git a/docs/locators.md b/docs/locators.md
index 923c6293..e53f8eea 100644
--- a/docs/locators.md
+++ b/docs/locators.md
@@ -12,6 +12,8 @@ CodeceptJS provides flexible strategies for locating elements:
 * [Locator Builder](#locator-builder)
 * [ID locators](#id-locators): by CSS id or by accessibility id
 * [Custom Locator Strategies](#custom-locators): by data attributes or whatever you prefer.
+* [Shadow DOM](/shadow): to access shadow dom elements
+* [React](/react): to access React elements by component names and props
 
 Most methods in CodeceptJS use locators which can be either a string or an object.
 
diff --git a/docs/playwright.md b/docs/playwright.md
index 4a7416e3..0de3b4d0 100644
--- a/docs/playwright.md
+++ b/docs/playwright.md
@@ -201,6 +201,65 @@ I.see('0 items left', '.todo-count');
 
 CodeceptJS allows you to implement custom actions like `I.createTodo` or use **PageObjects**. Learn how to improve your tests in [PageObjects](http://codecept.io/pageobjects/) guide.
 
+## Multi Session Testing
+
+TO launch additional browser context (or incognito window) use `session` command.
+
+```js
+Scenario('I try to open this site as anonymous user', () => {
+  I.amOnPage('/');
+  I.dontSee('Agree to cookies');
+  session('anonymous user', () => {
+    I.amOnPage('/');
+    I.see('Agree to cookies');
+  });
+})
+```
+
+> ℹ Learn more about [multi-session testing](/basics/#multiple-sessions)
+
+## Device Emulation
+
+Playwright can emulate browsers of mobile devices. Instead of paying for expensive devices for mobile tests you can adjust Playwright settings so it could emulate mobile browsers on iPhone, Samsung Galaxy, etc.
+
+Device emulation can be enabled in CodeceptJS globally in a config or per session.
+
+Playwright contains a [list of predefined devices](https://github.com/Microsoft/playwright/blob/master/src/deviceDescriptors.ts) to emulate, for instance this is how you can enable iPhone 6 emulation for all tests:
+
+```js
+const { devices } = require('playwright');
+
+helpers: {
+  Playwright: {
+    // regular config goes here
+    emulate: devices['iPhone 6'],
+  }
+}
+```
+To adjust browser settings you can pass [custom options](https://github.com/microsoft/playwright/blob/v0.12.1/docs/api.md#browsernewcontextoptions)
+
+```js
+helpers: {
+  Playwright: {
+    // regular config goes here
+    // put on mobile device
+    emulate: { isMobile: true, deviceScaleFactor: 2 }
+  }
+}
+```
+
+To enable device emulation for a specific test, create an additional browser session and pass in config as a second parameter:
+
+```js
+const { devices } = require('playwright');
+
+Scenario('website looks nice on iPhone', () => {
+  session('mobile user', devices['iPhone 6'], () => {
+    I.amOnPage('/');
+    I.see('Hello, iPhone user!')
+  })
+});
+```
 
 ## Extending
 
@@ -239,6 +298,7 @@ async setPermissions() {
   const context = browser.defaultContext()
   return context.setPermissions('https://html5demos.com', ['geolocation']);
 }
+```
 
 > [▶ Learn more about BrowserContext](https://github.com/microsoft/playwright/blob/v0.12.1/docs/api.md#class-browsercontext)
 
diff --git a/docs/plugins.md b/docs/plugins.md
index 8d708480..b065af54 100644
--- a/docs/plugins.md
+++ b/docs/plugins.md
@@ -305,6 +305,109 @@ Scenario('login', async (I, login) => {
 
 -   `config`  
 
+## commentStep
+
+Add descriptive nested steps for your tests:
+
+```js
+Scenario('project update test', async (I) => {
+  __`Given`;
+  const projectId = await I.have('project');
+
+  __`When`;
+  projectPage.update(projectId, { title: 'new title' });
+
+  __`Then`;
+  projectPage.open(projectId);
+  I.see('new title', 'h1');
+})
+```
+
+Steps prefixed with `__` will be printed as nested steps in `--steps` output:
+
+      Given
+        I have "project"
+      When
+        projectPage update
+      Then
+        projectPage open
+        I see "new title", "h1"
+
+Also those steps will be exported to allure reports.
+
+This plugin can be used
+
+### Config
+
+-   `enabled` - (default: false) enable a plugin
+-   `regusterGlobal` - (default: false) register `__` template literal function globally. You can override function global name by providing a name as a value.
+
+### Examples
+
+Registering `__` globally:
+
+```js
+plugins: {
+  commentStep: {
+    enabled: true,
+    registerGlobal: true
+  }
+}
+```
+
+Registering `Step` globally:
+
+```js
+plugins: {
+  commentStep: {
+    enabled: true,
+    registerGlobal: 'Step'
+  }
+}
+```
+
+Using only local function names:
+
+```js
+plugins: {
+  commentStep: {
+    enabled: true
+  }
+}
+```
+
+Then inside a test import a comment function from a plugin.
+For instance, you can prepare Given/When/Then functions to use them inside tests:
+
+```js
+// inside a test
+const step = codeceptjs.container.plugins('commentStep');
+
+const Given = () => step`Given`;
+const When = () => step`When`;
+const Then = () => step`Then`;
+```
+
+Scenario('project update test', async (I) => {
+  Given();
+  const projectId = await I.have('project');
+
+  When();
+  projectPage.update(projectId, { title: 'new title' });
+
+  Then();
+  projectPage.open(projectId);
+  I.see('new title', 'h1');
+});
+
+```
+
+```
+
+### Parameters
+
+-   `config`  
+
 ## customLocator
 
 Creates a [custom locator][3] by using special attributes in HTML.
diff --git a/docs/shadow.md b/docs/shadow.md
index 9955a3cc..ad4aa056 100644
--- a/docs/shadow.md
+++ b/docs/shadow.md
@@ -5,6 +5,8 @@ title: Locating Shadow Dom Elements
 
 # Locating Shadow Dom Elements
 
+> ℹ Shadow DOM locators is supported only in WebDriver helper
+
 Shadow DOM is one of the key browser features that make up web components. Web components are a really great way to build reusable elements, and are able to scale all the way up to complete web applications. Style encapsulation, the feature that gives shadow DOM it's power, has been a bit of a pain when it comes to E2E or UI testing. Things just got a little easier though, as CodeceptJS introduced built-in support for shadow DOM via locators of type `shadow`. Let's dig into what they're all about.
 
 Generated HTML code may often look like this (ref: [Salesforce's Lighting Web Components](https://github.com/salesforce/lwc)):
@@ -31,11 +33,11 @@ This uses custom elements, `my-app`, `recipe-hello`, `recipe-hello-binding` and
 For Web Components or [Salesforce's Lighting Web Components](https://github.com/salesforce/lwc) with Shadow DOM's, a special `shadow` locator is available. It allows to select an element by its shadow dom sequences and sequences are defined as an Array of `elements`. Elements defined in the array of `elements` must be in the ordered the shadow elements appear in the DOM.
 
 ```js
-{ shadow: ['my-app', 'recipe-hello', 'button'] } 
+{ shadow: ['my-app', 'recipe-hello', 'button'] }
 { shadow: ['my-app', 'recipe-hello-binding', 'ui-input', 'input.input'] }
 ```
 
-In WebDriver, you can use shadow locators in any method where locator is required. 
+In WebDriver, you can use shadow locators in any method where locator is required.
 
 For example, to fill value in `input` field or to click the `Click Me!` button, in above HTML code:
 
diff --git a/docs/webapi/setCookie.mustache b/docs/webapi/setCookie.mustache
index 1165a965..c792d8c1 100644
--- a/docs/webapi/setCookie.mustache
+++ b/docs/webapi/setCookie.mustache
@@ -1,7 +1,15 @@
-Sets a cookie.
+Sets cookie(s).
+
+Can be a single cookie object or an array of cookies:
 
 ```js
 I.setCookie({name: 'auth', value: true});
+
+// as array
+I.setCookie([
+  {name: 'auth', value: true},
+  {name: 'agree', value: true}
+]);
 ```
 
-@param {object} cookie a cookie object.
\ No newline at end of file
+@param {object|array} cookie a cookie object or array of cookie objects.
\ No newline at end of file
diff --git a/docs/webdriver.md b/docs/webdriver.md
index 519b632c..5487d490 100644
--- a/docs/webdriver.md
+++ b/docs/webdriver.md
@@ -204,7 +204,7 @@ Scenario('login test', (I) => {
 > ▶ 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](/basics#writing-tests).
 
 
-An empty test case can be created with `codeceptjs gt` command.
+An empty test case can be created with `npx codeceptjs gt` command.
 
 ```
 npx codeceptjs gt
@@ -223,7 +223,7 @@ Scenario('open my website', (I) => {
 
 This is just enough to run a test, open a browser, and think what to do next to write a test case.
 
-When you execute such test with `codeceptjs run` command you may see the browser is started
+When you execute such test with `npx codeceptjs run` command you may see the browser is started
 
 ```
 npx codeceptjs run --steps
@@ -264,6 +264,8 @@ Scenario('create todo item', (I) => {
 
 > [▶ Working example of CodeceptJS WebDriver tests](https://github.com/DavertMik/codeceptjs-webdriver-example) for TodoMVC application.
 
+WebDriver helper supports standard [CSS/XPath and text locators](/locators) as well as non-trivial [React locators](/react) and [Shadow DOM](/shadow).
+
 ## 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.
diff --git a/examples/codecept.conf.example.js b/examples/codecept.conf.example.js
index 8f5d6351..da0bc889 100644
--- a/examples/codecept.conf.example.js
+++ b/examples/codecept.conf.example.js
@@ -1,6 +1,6 @@
 console.log('Use JS config file');
 
-console.log(process.profile);
+console.log(process.env.profile);
 
 exports.config = {
   tests: './*_test.js',
@@ -9,7 +9,7 @@ exports.config = {
   helpers: {
     WebDriverIO: {
       url: 'http://localhost',
-      browser: process.profile || 'firefox',
+      browser: process.env.profile || 'firefox',
       restart: true,
     },
   },
diff --git a/lib/command/interactive.js b/lib/command/interactive.js
index f4954248..a19245c6 100644
--- a/lib/command/interactive.js
+++ b/lib/command/interactive.js
@@ -6,7 +6,9 @@ const event = require('../event');
 const output = require('../output');
 
 module.exports = function (path, options) {
+  // Backward compatibility for --profile
   process.profile = options.profile;
+  process.env.profile = options.profile;
 
   const testsPath = getTestRoot(path);
   const config = getConfig(testsPath);
diff --git a/lib/command/run-multiple.js b/lib/command/run-multiple.js
index 8bf77b4e..9265ae04 100644
--- a/lib/command/run-multiple.js
+++ b/lib/command/run-multiple.js
@@ -29,7 +29,7 @@ let processesDone;
 
 module.exports = function (selectedRuns, options) {
   // registering options globally to use in config
-  process.profile = options.profile;
+  process.env.profile = options.profile;
   const configFile = options.config;
   let codecept;
 
diff --git a/lib/command/run-rerun.js b/lib/command/run-rerun.js
index eee1f2b6..3b5e28df 100644
--- a/lib/command/run-rerun.js
+++ b/lib/command/run-rerun.js
@@ -7,7 +7,9 @@ const Codecept = require('../rerun');
 
 module.exports = function (test, options) {
   // registering options globally to use in config
+  // Backward compatibility for --profile
   process.profile = options.profile;
+  process.env.profile = options.profile;
   const configFile = options.config;
   let codecept;
 
diff --git a/lib/command/run-workers.js b/lib/command/run-workers.js
index cb836113..1a1a471e 100644
--- a/lib/command/run-workers.js
+++ b/lib/command/run-workers.js
@@ -32,7 +32,7 @@ module.exports = function (workers, options) {
     'Required minimum Node version of 11.7.0 to work with "run-workers"',
   );
 
-  process.profile = options.profile;
+  process.env.profile = options.profile;
 
   const { Worker } = require('worker_threads');
 
@@ -232,16 +232,11 @@ function simplifyObject(object) {
 const updateFinishedTests = (test, maxReruns) => {
   const { id } = test;
   if (finishedTests[id]) {
-    if (finishedTests[id].runs > maxReruns) {
-      const stats = {
-        passes: 0,
-        failures: -1,
-        tests: 0,
-      };
-      appendStats(stats);
-    } else {
+    const stats = { passes: 0, failures: -1, tests: 0 };
+    if (finishedTests[id].runs <= maxReruns) {
       finishedTests[id].runs++;
     }
+    appendStats(stats);
   } else {
     finishedTests[id] = test;
     finishedTests[id].runs = 1;
diff --git a/lib/command/run.js b/lib/command/run.js
index f9a65ee2..33e56be9 100644
--- a/lib/command/run.js
+++ b/lib/command/run.js
@@ -10,7 +10,9 @@ const Codecept = require('../codecept');
 
 module.exports = function (test, options) {
   // registering options globally to use in config
+  // Backward compatibility for --profile
   process.profile = options.profile;
+  process.env.profile = options.profile;
   const configFile = options.config;
   let codecept;
 
diff --git a/lib/helper/MockRequest.js b/lib/helper/MockRequest.js
index 7a5a252a..6bc32edd 100644
--- a/lib/helper/MockRequest.js
+++ b/lib/helper/MockRequest.js
@@ -57,6 +57,9 @@ let PollyJS;
  */
 class MockRequest extends Helper {
   constructor(config) {
+    console.log('DEPRECATION NOTICE:');
+    console.log('MockRequest helper was moved to a standalone package: https://github.com/codecept-js/mock-request');
+    console.log('Disable MockRequest in config & install @codeceptjs/mock-request package\n');
     super(config);
     this._setConfig(config);
     PollyJS = requireg('@pollyjs/core').Polly;
diff --git a/lib/helper/Playwright.js b/lib/helper/Playwright.js
index 49681640..d3683df9 100644
--- a/lib/helper/Playwright.js
+++ b/lib/helper/Playwright.js
@@ -32,12 +32,13 @@ const Console = require('./extras/Console');
 
 let playwright;
 let perfTiming;
+let defaultSelectorEnginesInitialized = false;
 
 const popupStore = new Popup();
 const consoleLogStore = new Console();
 const availableBrowsers = ['chromium', 'webkit', 'firefox'];
 
-const { createValueEngine } = require('./extras/PlaywrightPropEngine');
+const { createValueEngine, createDisabledEngine } = require('./extras/PlaywrightPropEngine');
 /**
  * Uses [Playwright](https://github.com/microsoft/playwright) library to run tests inside:
  *
@@ -62,6 +63,7 @@ const { createValueEngine } = require('./extras/PlaywrightPropEngine');
  * * `show`: (optional, default: false) - show browser window.
  * * `restart`: (optional, default: true) - restart browser between tests.
  * * `disableScreenshots`: (optional, default: false)  - don't save screenshot on failure.
+ * * `emulate`: (optional, default: {}) launch browser in device emulation mode.
  * * `fullPageScreenshots` (optional, default: false) - make full page screenshots on failure.
  * * `uniqueScreenshotNames`: (optional, default: false)  - option to prevent screenshot override if you have scenarios with the same name in different suites.
  * * `keepBrowserState`: (optional, default: false) - keep browser state between tests when `restart` is set to false.
@@ -156,6 +158,23 @@ const { createValueEngine } = require('./extras/PlaywrightPropEngine');
  * }
  * ```
  *
+ * #### Example #6: Lunach tests emulating iPhone 6
+ *
+ *
+ *
+ * ```js
+ * const { devices } = require('playwright');
+ *
+ * {
+ *  helpers: {
+ *    Playwright: {
+ *      url: "http://localhost",
+ *      emulate: devices['iPhone 6'],
+ *    }
+ *  }
+ * }
+ * ```
+ *
  * Note: When connecting to remote browser `show` and specific `chrome` options (e.g. `headless` or `devtools`) are ignored.
  *
  * ## Access From Helpers
@@ -195,6 +214,9 @@ class Playwright extends Helper {
 
   _validateConfig(config) {
     const defaults = {
+      // options to emulate context
+      emulate: {},
+
       browser: 'chromium',
       waitForAction: 100,
       waitForTimeout: 1000,
@@ -259,8 +281,11 @@ class Playwright extends Helper {
 
   async _init() {
     // register an internal selector engine for reading value property of elements in a selector
+    if (defaultSelectorEnginesInitialized) return;
+    defaultSelectorEnginesInitialized = true;
     try {
       await playwright.selectors.register('__value', createValueEngine);
+      await playwright.selectors.register('__disabled', createDisabledEngine);
     } catch (e) {
       console.warn(e);
     }
@@ -331,11 +356,11 @@ class Playwright extends Helper {
   _session() {
     const defaultContext = this.browserContext;
     return {
-      start: async (sessionName = '') => {
-        this.debugSection('Incognito Tab', 'opened');
+      start: async (sessionName = '', config) => {
+        this.debugSection('New Context', config ? JSON.stringify(config) : 'opened');
         this.activeSessionName = sessionName;
 
-        const bc = await this.browser.newContext();
+        const bc = await this.browser.newContext(config);
         const page = await bc.newPage();
         targetCreatedHandler.call(this, page);
         this._setPage(page);
@@ -357,12 +382,11 @@ class Playwright extends Helper {
 
         if (!session) {
           this.activeSessionName = '';
-          const existingPages = await this.browserContext.pages();
-          await this._setPage(existingPages[0]);
         } else {
           this.activeSessionName = session;
-          await this._setPage(this.sessionPages[this.activeSessionName]);
         }
+        const existingPages = await this.browserContext.pages();
+        await this._setPage(existingPages[0]);
 
         return this._waitForAction();
       },
@@ -509,7 +533,7 @@ class Playwright extends Helper {
     this.browser.on('targetchanged', (target) => {
       this.debugSection('Url', target.url());
     });
-    this.browserContext = await this.browser.newContext();
+    this.browserContext = await this.browser.newContext(this.options.emulate);
 
     const existingPages = await this.browserContext.pages();
 
@@ -719,9 +743,9 @@ class Playwright extends Helper {
       assertElementExists(els, locator, 'Element');
       await els[0].scrollIntoViewIfNeeded();
       const elementCoordinates = await els[0]._clickablePoint();
-      await this.executeScript((x, y) => window.scrollBy(x, y), elementCoordinates.x + offsetX, elementCoordinates.y + offsetY);
+      await this.executeScript((offsetX, offsetY) => window.scrollBy(offsetX, offsetY), { offsetX: elementCoordinates.x + offsetX, offsetY: elementCoordinates.y + offsetY });
     } else {
-      await this.executeScript((x, y) => window.scrollTo(x, y), offsetX, offsetY);
+      await this.executeScript(({ offsetX, offsetY }) => window.scrollTo(offsetX, offsetY), { offsetX, offsetY });
     }
     return this._waitForAction();
   }
@@ -912,9 +936,16 @@ class Playwright extends Helper {
    * ```js
    * I.openNewTab();
    * ```
+   *
+   * You can pass in [page options](https://github.com/microsoft/playwright/blob/v0.12.1/docs/api.md#browsernewpageoptions) to emulate device on this page
+   *
+   * ```js
+   * // enable mobile
+   * I.openNewTab({ isMobile: true });
+   * ```
    */
-  async openNewTab() {
-    await this._setPage(await this.browserContext.newPage());
+  async openNewTab(options) {
+    await this._setPage(await this.browserContext.newPage(options));
     return this._waitForAction();
   }
 
@@ -972,12 +1003,26 @@ class Playwright extends Helper {
   }
 
   /**
-   * {{> clickLink }}
+   * Clicks link and waits for navigation (deprecated)
+   */
+  async clickLink(locator, context = null) {
+    console.log('clickLink deprecated: Playwright automatically waits for navigation to happen.');
+    console.log('Replace I.clickLink with I.click');
+    return this.click(locator, context);
+  }
+
+  /**
    *
+   * Force clicks an element without waiting for it to become visible and not animating.
+   *
+   * ```js
+   * I.forceClick('#hiddenButton');
+   * I.forceClick('Click me', '#hidden');
+   * ```
    *
    */
-  async clickLink(locator, context = null) {
-    return proceedClick.call(this, locator, context, { waitForNavigation: true });
+  async forceClick(locator, context = null) {
+    return proceedClick.call(this, locator, context, { force: true });
   }
 
 
@@ -1326,9 +1371,9 @@ class Playwright extends Helper {
    */
   async setCookie(cookie) {
     if (Array.isArray(cookie)) {
-      return this.browserContext.setCookies(...cookie);
+      return this.browserContext.addCookies(...cookie);
     }
-    return this.browserContext.setCookies([cookie]);
+    return this.browserContext.addCookies([cookie]);
   }
 
   /**
@@ -1370,39 +1415,33 @@ class Playwright extends Helper {
   }
 
   /**
-   * {{> executeScript }}
+   * Executes a script on the page:
+   *
+   * ```js
+   * I.executeScript(() => window.alert('Hello world'));
+   * ```
    *
-   * If a function returns a Promise It will wait for it resolution.
+   * Additional parameters of the function can be passed as an object argument:
+   *
+   * ```js
+   * I.executeScript(({x, y}) => x + y, {x, y});
+   * ```
+   * You can pass only one parameter into a function
+   * but you can pass in array or object.
+   *
+   * ```js
+   * I.executeScript(([x, y]) => x + y, [x, y]);
+   * ```
+   * If a function returns a Promise it will wait for its resolution.
    */
-  async executeScript(fn) {
+  async executeScript(fn, arg) {
     let context = this.page;
     if (this.context && this.context.constructor.name === 'Frame') {
       context = this.context; // switching to iframe context
     }
-    return context.evaluate.apply(context, arguments);
-  }
-
-  /**
-   * {{> executeAsyncScript }}
-   *
-   * Asynchronous scripts can also be executed with `executeScript` if a function returns a Promise.
-   */
-  async executeAsyncScript(fn) {
-    const args = Array.from(arguments);
-    const asyncFn = function () {
-      const args = Array.from(arguments);
-      const fn = eval(`(${args.shift()})`); // eslint-disable-line no-eval
-      return new Promise((done) => {
-        args.push(done);
-        fn.apply(null, args);
-      });
-    };
-    args[0] = args[0].toString();
-    args.unshift(asyncFn);
-    return this.page.evaluate.apply(this.page, args);
+    return context.evaluate.apply(context, [fn, arg]);
   }
 
-
   /**
    * {{> grabTextFrom }}
    *
@@ -1609,7 +1648,7 @@ class Playwright extends Helper {
     const context = await this._getContext();
     if (!locator.isXPath()) {
       // playwright combined selectors
-      waiter = context.waitForSelector(`${locator.isCustom() ? `${locator.type}=${locator.value}` : locator.simplify()} >> :not([disabled])`, { timeout: waitTimeout });
+      waiter = context.waitForSelector(`${locator.isCustom() ? `${locator.type}=${locator.value}` : locator.simplify()} >> __disabled=false`, { timeout: waitTimeout });
     } else {
       const enabledFn = function ([locator, $XPath]) {
         eval($XPath); // eslint-disable-line no-eval
@@ -1682,16 +1721,8 @@ class Playwright extends Helper {
    * {{> waitForClickable }}
    */
   async waitForClickable(locator, waitTimeout) {
-    const els = await this._locate(locator);
-    assertElementExists(els, locator);
-
-    return this.waitForFunction(isElementClickable, [els[0]], waitTimeout).catch(async (e) => {
-      if (/failed: timeout/i.test(e.message)) {
-        throw new Error(`element ${new Locator(locator).toString()} still not clickable after ${waitTimeout || this.options.waitForTimeout / 1000} sec`);
-      } else {
-        throw e;
-      }
-    });
+    console.log('I.waitForClickable is DEPRECATED: This is no longer needed, Playwright automatically waits for element to be clikable');
+    console.log('Remove usage of this function');
   }
 
   /**
diff --git a/lib/helper/Puppeteer.js b/lib/helper/Puppeteer.js
index d838bb43..51a7c668 100644
--- a/lib/helper/Puppeteer.js
+++ b/lib/helper/Puppeteer.js
@@ -222,6 +222,9 @@ class Puppeteer extends Helper {
       {
         name: 'show', message: 'Show browser window', default: true, type: 'confirm',
       },
+      {
+        name: 'windowSize', message: 'Browser viewport size', default: '1200x900',
+      },
     ];
   }
 
@@ -245,6 +248,7 @@ class Puppeteer extends Helper {
 
 
   async _before() {
+    this.sessionPages = {};
     recorder.retry({
       retries: 5,
       when: err => err.message.indexOf('context') > -1, // ignore context errors
@@ -300,12 +304,12 @@ class Puppeteer extends Helper {
 
   _session() {
     return {
-      start: async (name = '', config = {}) => {
+      start: async (name = '', config) => {
         this.debugSection('Incognito Tab', 'opened');
         this.activeSessionName = name;
 
         const bc = await this.browser.createIncognitoBrowserContext();
-        await bc.newPage();
+        const page = await bc.newPage();
 
         // Create a new page inside context.
         return bc;
@@ -323,15 +327,12 @@ class Puppeteer extends Helper {
 
         if (!session) {
           this.activeSessionName = '';
-
-          // Find original page on default context
-          const defaultCtx = this.browser.defaultBrowserContext();
-          const existingPages = defaultCtx.targets().filter(t => t.type() === 'page');
-          await this._setPage(await existingPages[0].page());
         } else {
           this.activeSessionName = session;
-          await this._setPage(this.sessionPages[this.activeSessionName]);
         }
+        const defaultCtx = this.browser.defaultBrowserContext();
+        const existingPages = defaultCtx.targets().filter(t => t.type() === 'page');
+        await this._setPage(await existingPages[0].page());
 
         return this._waitForAction();
       },
@@ -1208,6 +1209,8 @@ class Puppeteer extends Helper {
 
   /**
    * {{> attachFile }}
+   *
+   * > ⚠ There is an [issue with file upload in Puppeteer 2.1.0 & 2.1.1](https://github.com/puppeteer/puppeteer/issues/5420), downgrade to 2.0.0 if you face it.
    */
   async attachFile(locator, pathToFile) {
     const file = path.join(global.codecept_dir, pathToFile);
diff --git a/lib/helper/WebDriver.js b/lib/helper/WebDriver.js
index f13845d5..a8e6b673 100644
--- a/lib/helper/WebDriver.js
+++ b/lib/helper/WebDriver.js
@@ -2,6 +2,7 @@ let webdriverio;
 
 const assert = require('assert');
 const path = require('path');
+const fs = require('fs');
 const requireg = require('requireg');
 
 const Helper = require('../helper');
@@ -31,6 +32,7 @@ const Locator = require('../locator');
 const SHADOW = 'shadow';
 const webRoot = 'body';
 
+let version;
 /**
  * WebDriver helper which wraps [webdriverio](http://webdriver.io/) library to
  * manipulate browser using Selenium WebDriver or PhantomJS.
@@ -381,10 +383,22 @@ class WebDriver extends Helper {
     if (webdriverio.VERSION && webdriverio.VERSION.indexOf('4') === 0) {
       throw new Error(`This helper is compatible with "webdriverio@5". Current version: ${webdriverio.VERSION}. Please upgrade webdriverio to v5+ or use WebDriverIO helper instead`);
     }
+    try {
+      version = JSON.parse(fs.readFileSync(path.join(requireg.resolve('webdriverio'), '/../../', 'package.json')).toString()).version;
+    } catch (err) {
+      this.debug('Can\'t detect webdriverio version, assuming webdriverio v6 is used');
+    }
+
+    if (isWebDriver5()) {
+      console.log('DEPRECATION NOTICE:');
+      console.log('You are using webdriverio v5. It is recommended to update to webdriverio@6.\nSupport of webdriverio v5 is deprecated and will be removed in CodeceptJS 3.0\n');
+    }
     // set defaults
     this.root = webRoot;
     this.isWeb = true;
     this.isRunning = false;
+    this.sessionWindows = {};
+    this.activeSessionName = '';
 
     this._setConfig(config);
 
@@ -565,12 +579,12 @@ class WebDriver extends Helper {
   _session() {
     const defaultSession = this.browser;
     return {
-      start: async (opts) => {
+      start: async (sessionName, opts) => {
         // opts.disableScreenshots = true; // screenshots cant be saved as session will be already closed
         opts = this._validateConfig(Object.assign(this.options, opts));
         this.debugSection('New Browser', JSON.stringify(opts));
         const browser = await webdriverio.remote(opts);
-
+        this.activeSessionName = sessionName;
         if (opts.timeouts && this.isWeb) {
           await this._defineBrowserTimeout(browser, opts.timeouts);
         }
@@ -586,8 +600,12 @@ class WebDriver extends Helper {
         if (this.context !== this.root) throw new Error('Can\'t start session inside within block');
         this.browser = browser;
         this.$$ = this.browser.$$.bind(this.browser);
+        this.sessionWindows[this.activeSessionName] = browser;
       },
-      restoreVars: async () => {
+      restoreVars: async (session) => {
+        if (!session) {
+          this.activeSessionName = '';
+        }
         this.browser = defaultSession;
         this.$$ = this.browser.$$.bind(this.browser);
       },
@@ -1456,6 +1474,7 @@ class WebDriver extends Helper {
     const res = await this._locate(withStrictLocator(locator), true);
     assertElementExists(res, locator);
     const elem = usingFirstElement(res);
+    if (isWebDriver5()) return elem.moveTo(xOffset, yOffset);
     return elem.moveTo({ xOffset, yOffset });
   }
 
@@ -1466,6 +1485,15 @@ class WebDriver extends Helper {
   async saveScreenshot(fileName, fullPage = false) {
     const outputFile = screenshotOutputFolder(fileName);
 
+    if (this.activeSessionName) {
+      const browser = this.sessionWindows[this.activeSessionName];
+
+      if (browser) {
+        this.debug(`Screenshot of ${this.activeSessionName} session has been saved to ${outputFile}`);
+        return browser.saveScreenshot(outputFile);
+      }
+    }
+
     if (!fullPage) {
       this.debug(`Screenshot has been saved to ${outputFile}`);
       return this.browser.saveScreenshot(outputFile);
@@ -1884,6 +1912,20 @@ class WebDriver extends Helper {
     const client = this.browser;
     const aSec = sec || this.options.waitForTimeout;
     let currUrl = '';
+    if (isWebDriver5()) {
+      return client
+        .waitUntil(function () {
+          return this.getUrl().then((res) => {
+            currUrl = decodeUrl(res);
+            return currUrl.indexOf(urlPart) > -1;
+          });
+        }, aSec * 1000).catch((e) => {
+          if (e.message.indexOf('timeout')) {
+            throw new Error(`expected url to include ${urlPart}, but found ${currUrl}`);
+          }
+          throw e;
+        });
+    }
     return client
       .waitUntil(function () {
         return this.getUrl().then((res) => {
@@ -1928,6 +1970,21 @@ class WebDriver extends Helper {
   async waitForText(text, sec = null, context = null) {
     const aSec = sec || this.options.waitForTimeout;
     const _context = context || this.root;
+    if (isWebDriver5()) {
+      return this.browser.waitUntil(
+        async () => {
+          const res = await this.$$(withStrictLocator.call(this, _context));
+          if (!res || res.length === 0) return false;
+          const selected = await forEachAsync(res, async el => this.browser.getElementText(getElementId(el)));
+          if (Array.isArray(selected)) {
+            return selected.filter(part => part.indexOf(text) >= 0).length > 0;
+          }
+          return selected.indexOf(text) >= 0;
+        }, aSec * 1000,
+        `element (${_context}) is not in DOM or there is no element(${_context}) with text "${text}" after ${aSec} sec`,
+      );
+    }
+
     return this.browser.waitUntil(
       async () => {
         const res = await this.$$(withStrictLocator.call(this, _context));
@@ -1950,6 +2007,20 @@ class WebDriver extends Helper {
   async waitForValue(field, value, sec = null) {
     const client = this.browser;
     const aSec = sec || this.options.waitForTimeout;
+    if (isWebDriver5()) {
+      return client.waitUntil(
+        async () => {
+          const res = await findFields.call(this, field);
+          if (!res || res.length === 0) return false;
+          const selected = await forEachAsync(res, async el => el.getValue());
+          if (Array.isArray(selected)) {
+            return selected.filter(part => part.indexOf(value) >= 0).length > 0;
+          }
+          return selected.indexOf(value) >= 0;
+        }, aSec * 1000,
+        `element (${field}) is not in DOM or there is no element(${field}) with value "${value}" after ${aSec} sec`,
+      );
+    }
     return client.waitUntil(
       async () => {
         const res = await findFields.call(this, field);
@@ -1972,6 +2043,17 @@ class WebDriver extends Helper {
    */
   async waitForVisible(locator, sec = null) {
     const aSec = sec || this.options.waitForTimeout;
+    if (isWebDriver5()) {
+      return this.browser.waitUntil(async () => {
+        const res = await this.$$(withStrictLocator(locator));
+        if (!res || res.length === 0) return false;
+        const selected = await forEachAsync(res, async el => el.isDisplayed());
+        if (Array.isArray(selected)) {
+          return selected.filter(val => val === true).length > 0;
+        }
+        return selected;
+      }, aSec * 1000, `element (${new Locator(locator)}) still not visible after ${aSec} sec`);
+    }
     return this.browser.waitUntil(async () => {
       const res = await this.$$(withStrictLocator(locator));
       if (!res || res.length === 0) return false;
@@ -1988,6 +2070,16 @@ class WebDriver extends Helper {
    */
   async waitNumberOfVisibleElements(locator, num, sec = null) {
     const aSec = sec || this.options.waitForTimeout;
+    if (isWebDriver5()) {
+      return this.browser.waitUntil(async () => {
+        const res = await this.$$(withStrictLocator(locator));
+        if (!res || res.length === 0) return false;
+        let selected = await forEachAsync(res, async el => el.isDisplayed());
+
+        if (!Array.isArray(selected)) selected = [selected];
+        return selected.length === num;
+      }, aSec * 1000, `The number of elements (${new Locator(locator)}) is not ${num} after ${aSec} sec`);
+    }
     return this.browser.waitUntil(async () => {
       const res = await this.$$(withStrictLocator(locator));
       if (!res || res.length === 0) return false;
@@ -2004,6 +2096,14 @@ class WebDriver extends Helper {
    */
   async waitForInvisible(locator, sec = null) {
     const aSec = sec || this.options.waitForTimeout;
+    if (isWebDriver5()) {
+      return this.browser.waitUntil(async () => {
+        const res = await this.$$(withStrictLocator(locator));
+        if (!res || res.length === 0) return true;
+        const selected = await forEachAsync(res, async el => el.isDisplayed());
+        return !selected.length;
+      }, aSec * 1000, `element (${new Locator(locator)}) still visible after ${aSec} sec`);
+    }
     return this.browser.waitUntil(async () => {
       const res = await this.$$(withStrictLocator(locator));
       if (!res || res.length === 0) return true;
@@ -2031,6 +2131,15 @@ class WebDriver extends Helper {
    */
   async waitForDetached(locator, sec = null) {
     const aSec = sec || this.options.waitForTimeout;
+    if (isWebDriver5()) {
+      return this.browser.waitUntil(async () => {
+        const res = await this.$$(withStrictLocator(locator));
+        if (!res || res.length === 0) {
+          return true;
+        }
+        return false;
+      }, aSec * 1000, `element (${new Locator(locator)}) still on page after ${aSec} sec`);
+    }
     return this.browser.waitUntil(async () => {
       const res = await this.$$(withStrictLocator(locator));
       if (!res || res.length === 0) {
@@ -2055,6 +2164,9 @@ class WebDriver extends Helper {
     }
 
     const aSec = sec || this.options.waitForTimeout;
+    if (isWebDriver5()) {
+      return this.browser.waitUntil(async () => this.browser.execute(fn, ...args), aSec * 1000, '');
+    }
     return this.browser.waitUntil(async () => this.browser.execute(fn, ...args), { timeout: aSec * 1000, timeoutMsg: '' });
   }
 
@@ -2065,7 +2177,10 @@ class WebDriver extends Helper {
   async waitUntil(fn, sec = null, timeoutMsg = null, interval = null) {
     const aSec = sec || this.options.waitForTimeout;
     const _interval = typeof interval === 'number' ? interval * 1000 : null;
-    return this.browser.waitUntil(fn, aSec * 1000, timeoutMsg, _interval);
+    if (isWebDriver5()) {
+      return this.browser.waitUntil(fn, aSec * 1000, timeoutMsg, _interval);
+    }
+    return this.browser.waitUntil(fn, { timeout: aSec * 1000, timeoutMsg, interval: _interval });
   }
 
   /**
@@ -2102,6 +2217,19 @@ class WebDriver extends Helper {
     const aSec = sec || this.options.waitForTimeout;
     let target;
     const current = await this.browser.getWindowHandle();
+
+    if (isWebDriver5()) {
+      await this.browser.waitUntil(async () => {
+        await this.browser.getWindowHandles().then((handles) => {
+          if (handles.indexOf(current) + num + 1 <= handles.length) {
+            target = handles[handles.indexOf(current) + num];
+          }
+        });
+        return target;
+      }, aSec * 1000, `There is no ability to switch to next tab with offset ${num}`);
+      return this.browser.switchToWindow(target);
+    }
+
     await this.browser.waitUntil(async () => {
       await this.browser.getWindowHandles().then((handles) => {
         if (handles.indexOf(current) + num + 1 <= handles.length) {
@@ -2128,6 +2256,19 @@ class WebDriver extends Helper {
     const aSec = sec || this.options.waitForTimeout;
     const current = await this.browser.getWindowHandle();
     let target;
+
+    if (isWebDriver5()) {
+      await this.browser.waitUntil(async () => {
+        await this.browser.getWindowHandles().then((handles) => {
+          if (handles.indexOf(current) - num > -1) {
+            target = handles[handles.indexOf(current) - num];
+          }
+        });
+        return target;
+      }, aSec * 1000, `There is no ability to switch to previous tab with offset ${num}`);
+      return this.browser.switchToWindow(target);
+    }
+
     await this.browser.waitUntil(async () => {
       await this.browser.getWindowHandles().then((handles) => {
         if (handles.indexOf(current) - num > -1) {
@@ -2708,4 +2849,8 @@ function prepareLocateFn(context) {
   };
 }
 
+function isWebDriver5() {
+  return version && version.indexOf('5') === 0;
+}
+
 module.exports = WebDriver;
diff --git a/lib/helper/extras/PlaywrightPropEngine.js b/lib/helper/extras/PlaywrightPropEngine.js
index 28966ef9..8da86260 100644
--- a/lib/helper/extras/PlaywrightPropEngine.js
+++ b/lib/helper/extras/PlaywrightPropEngine.js
@@ -11,7 +11,7 @@ module.exports.createValueEngine = () => {
       if (!root) {
         return null;
       }
-      return `${root.value}` === selector;
+      return `${root.value}`.includes(selector) ? root : null;
     },
 
     // Returns all elements matching given selector in the root's subtree.
@@ -19,7 +19,35 @@ module.exports.createValueEngine = () => {
       if (!root) {
         return null;
       }
-      return `${root.value}` === selector;
+      return `${root.value}`.includes(selector) ? root : null;
+    },
+  };
+};
+
+module.exports.createDisabledEngine = () => {
+  return {
+    // Creates a selector that matches given target when queried at the root.
+    // Can return undefined if unable to create one.
+    create(root, target) {
+      return null;
+    },
+
+    // Returns the first element matching given selector in the root's subtree.
+    query(root, value) {
+      const bool = value === 'true';
+      if (!root) {
+        return null;
+      }
+      return root.disabled === bool ? root : null;
+    },
+
+    // Returns all elements matching given selector in the root's subtree.
+    queryAll(root, value) {
+      const bool = value === 'true';
+      if (!root) {
+        return null;
+      }
+      return root.disabled === bool ? root : null;
     },
   };
 };
diff --git a/lib/mochaFactory.js b/lib/mochaFactory.js
index 92b2234b..064f7992 100644
--- a/lib/mochaFactory.js
+++ b/lib/mochaFactory.js
@@ -38,19 +38,21 @@ class MochaFactory {
     };
 
     mocha.loadFiles = (fn) => {
-      // load features
-      mocha.files
-        .filter(file => file.match(/\.feature$/))
-        .map(file => fs.readFileSync(file, 'utf8'))
-        .forEach(content => mocha.suite.addSuite(gherkinParser(content)));
+      if (mocha.suite.suites.length === 0) {
+        // load features
+        mocha.files
+          .filter(file => file.match(/\.feature$/))
+          .map(file => fs.readFileSync(file, 'utf8'))
+          .forEach(content => mocha.suite.addSuite(gherkinParser(content)));
 
-      // remove feature files
-      mocha.files = mocha.files.filter(file => !file.match(/\.feature$/));
+        // remove feature files
+        mocha.files = mocha.files.filter(file => !file.match(/\.feature$/));
 
-      Mocha.prototype.loadFiles.call(mocha, fn);
+        Mocha.prototype.loadFiles.call(mocha, fn);
 
-      // add ids for each test
-      mocha.suite.eachTest(test => test.id = genTestId(test));
+        // add ids for each test
+        mocha.suite.eachTest(test => test.id = genTestId(test));
+      }
     };
 
     // use standard reporter
diff --git a/lib/plugin/commentStep.js b/lib/plugin/commentStep.js
new file mode 100644
index 00000000..0077e91d
--- /dev/null
+++ b/lib/plugin/commentStep.js
@@ -0,0 +1,133 @@
+const event = require('../event');
+const recorder = require('../recorder');
+const { MetaStep } = require('../step');
+
+let currentCommentStep;
+
+const defaultGlobalName = '__';
+
+/**
+ * Add descriptive nested steps for your tests:
+ *
+ * ```js
+ * Scenario('project update test', async (I) => {
+ *   __`Given`;
+ *   const projectId = await I.have('project');
+ *
+ *   __`When`;
+ *   projectPage.update(projectId, { title: 'new title' });
+ *
+ *   __`Then`;
+ *   projectPage.open(projectId);
+ *   I.see('new title', 'h1');
+ * })
+ * ```
+ * Steps prefixed with `__` will be printed as nested steps in `--steps` output:
+ *
+ * ```
+ *   Given
+ *     I have "project"
+ *   When
+ *     projectPage update
+ *   Then
+ *     projectPage open
+ *     I see "new title", "h1"
+ * ```
+ *
+ * Also those steps will be exported to allure reports.
+ *
+ * This plugin can be used
+ *
+ * ### Config
+ *
+ * * `enabled` - (default: false) enable a plugin
+ * * `regusterGlobal` - (default: false) register `__` template literal function globally. You can override function global name by providing a name as a value.
+ *
+ * ### Examples
+ *
+ * Registering `__` globally:
+ *
+ * ```js
+ * plugins: {
+ *   commentStep: {
+ *     enabled: true,
+ *     registerGlobal: true
+ *   }
+ * }
+ * ```
+ *
+ * Registering `Step` globally:
+ * ```js
+ * plugins: {
+ *   commentStep: {
+ *     enabled: true,
+ *     registerGlobal: 'Step'
+ *   }
+ * }
+ * ```
+ *
+ * Using only local function names:
+ * ```js
+ * plugins: {
+ *   commentStep: {
+ *     enabled: true
+ *   }
+ * }
+ * ```
+ * Then inside a test import a comment function from a plugin.
+ * For instance, you can prepare Given/When/Then functions to use them inside tests:
+ *
+ * ```js
+ * // inside a test
+ * const step = codeceptjs.container.plugins('commentStep');
+ *
+ * const Given = () => step`Given`;
+ * const When = () => step`When`;
+ * const Then = () => step`Then`;
+ * ```
+ *
+ * Scenario('project update test', async (I) => {
+ *   Given();
+ *   const projectId = await I.have('project');
+ *
+ *   When();
+ *   projectPage.update(projectId, { title: 'new title' });
+ *
+ *   Then();
+ *   projectPage.open(projectId);
+ *   I.see('new title', 'h1');
+ * });
+ * ```
+ */
+module.exports = function (config) {
+  event.dispatcher.on(event.test.started, (test) => {
+    currentCommentStep = null;
+  });
+
+  event.dispatcher.on(event.step.started, (step) => {
+    if (currentCommentStep) {
+      const metaStep = getRootMetaStep(step);
+      metaStep.metaStep = currentCommentStep;
+    }
+  });
+
+  if (config.registerGlobal) {
+    if (config.registerGlobal === true) {
+      config.registerGlobal = defaultGlobalName;
+    }
+    global[config.registerGlobal] = setCommentString;
+  }
+
+  return setCommentString;
+};
+
+function getRootMetaStep(step) {
+  if (step.metaStep) return getRootMetaStep(step.metaStep);
+  return step;
+}
+
+function setCommentString(string) {
+  recorder.add('set comment metastep', () => {
+    currentCommentStep = new MetaStep(String.raw(string), '');
+  });
+}
diff --git a/lib/session.js b/lib/session.js
index a7229ce3..d3d7abe0 100644
--- a/lib/session.js
+++ b/lib/session.js
@@ -65,7 +65,7 @@ function session(sessionName, config, fn) {
     }
 
     recorder.add('save vars', async () => {
-      savedSessions[sessionName].vars = await savedSessions[sessionName].start(sessionName);
+      savedSessions[sessionName].vars = await savedSessions[sessionName].start(sessionName, config);
     });
   }
 
diff --git a/package.json b/package.json
index 6cd187f7..1497eac4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "codeceptjs",
-  "version": "2.5.0",
+  "version": "2.6.0",
   "description": "Supercharged End 2 End Testing Framework for NodeJS",
   "keywords": [
     "acceptance",
@@ -109,7 +109,7 @@
     "nodemon": "^1.19.4",
     "playwright": "^0.12.1",
     "protractor": "^5.4.1",
-    "puppeteer": "^2.1.1",
+    "puppeteer": "^1.20.0",
     "qrcode-terminal": "^0.12.0",
     "rosie": "^1.6.0",
     "runio.js": "^1.0.20",
diff --git a/runio.js b/runio.js
index 68ba6b8a..d766aa7f 100755
--- a/runio.js
+++ b/runio.js
@@ -305,14 +305,11 @@ title: ${name}
     await this.wiki();
 
     const dir = 'website';
-    await git((fn) => {
-      if (!fs.existsSync(dir)) {
-        fn.clone('git@github.com:codecept-js/website.git', dir);
-      } else {
-        fn.pull();
-      }
-    });
+    if (fs.existsSync(dir)) {
+      await exec(`rm -rf ${dir}`);
+    }
 
+    await git((fn) => fn.clone('git@github.com:codecept-js/website.git', dir));
     await copy('docs', 'website/docs');
 
     await chdir(dir, async () => {
@@ -320,7 +317,8 @@ title: ${name}
       await git((fn) => {
         fn.add('-A');
         fn.commit('-m "synchronized with docs"');
-        fn.push('--no-verify');
+        fn.pull();
+        fn.push();
       });
       stopOnFail(true);
 


Test patch:
diff --git a/docs/helpers/TestCafe.md b/docs/helpers/TestCafe.md
index 89e78867..686fcddb 100644
--- a/docs/helpers/TestCafe.md
+++ b/docs/helpers/TestCafe.md
@@ -852,15 +852,23 @@ I.selectOption('Which OS do you use?', ['Android', 'iOS']);
 
 ### setCookie
 
-Sets a cookie.
+Sets cookie(s).
+
+Can be a single cookie object or an array of cookies:
 
 ```js
 I.setCookie({name: 'auth', value: true});
+
+// as array
+I.setCookie([
+  {name: 'auth', value: true},
+  {name: 'agree', value: true}
+]);
 ```
 
 #### Parameters
 
--   `cookie` **[object][5]** a cookie object.
+-   `cookie` **([object][5] | [array][9])** a cookie object or array of cookie objects.
 
 ### switchTo
 
diff --git a/settings_override.xml b/settings_override.xml
new file mode 100644
index 00000000..e69de29b
diff --git a/test/acceptance/codecept.WebDriver.js b/test/acceptance/codecept.WebDriver.js
index 574ea9b5..b5ee5480 100644
--- a/test/acceptance/codecept.WebDriver.js
+++ b/test/acceptance/codecept.WebDriver.js
@@ -18,6 +18,10 @@ module.exports.config = {
       // },
     },
     MockRequest: {},
+    ScreenshotSessionHelper: {
+      require: '../support/ScreenshotSessionHelper.js',
+      outputPath: './output',
+    },
   },
   include: {},
   bootstrap: done => setTimeout(done, 5000), // let's wait for selenium
diff --git a/test/acceptance/config_test.js b/test/acceptance/config_test.js
index ed0547a3..01ba02ec 100644
--- a/test/acceptance/config_test.js
+++ b/test/acceptance/config_test.js
@@ -24,6 +24,7 @@ Scenario('change config 4 @WebDriverIO @Puppeteer @Playwright @Protractor @Night
   return { url: 'https://codecept.io/', capabilities: { 'moz:title': test.title } };
 });
 
+
 Scenario('change config 5 @WebDriverIO @Puppeteer @Playwright @Protractor @Nightmare', (I) => {
   I.amOnPage('/');
   I.dontSeeInCurrentUrl('github.com');
diff --git a/test/acceptance/session_test.js b/test/acceptance/session_test.js
index 5b8ad9d0..294ed82e 100644
--- a/test/acceptance/session_test.js
+++ b/test/acceptance/session_test.js
@@ -1,4 +1,5 @@
 const assert = require('assert');
+const path = require('path');
 
 Feature('Session');
 
@@ -13,9 +14,7 @@ Scenario('simple session @WebDriverIO @Protractor @Puppeteer @Playwright', (I) =
   I.seeInCurrentUrl('/info');
 });
 
-Scenario('screenshots reflect the current page of current session @Puppeteer @Playwright', async (I) => {
-  const outputPath = await I.getOutputPath();
-
+Scenario('screenshots reflect the current page of current session @Puppeteer @Playwright @WebDriver', async (I) => {
   I.amOnPage('/');
   I.saveScreenshot('session_default_1.png');
 
@@ -31,10 +30,10 @@ Scenario('screenshots reflect the current page of current session @Puppeteer @Pl
   });
 
   const [default1Digest, default2Digest, john1Digest, john2Digest] = await I.getMD5Digests([
-    `${outputPath}/session_default_1.png`,
-    `${outputPath}/session_default_2.png`,
-    `${outputPath}/session_john_1.png`,
-    `${outputPath}/session_john_2.png`,
+    `${output_dir}/session_default_1.png`,
+    `${output_dir}/session_default_2.png`,
+    `${output_dir}/session_john_1.png`,
+    `${output_dir}/session_john_2.png`,
   ]);
 
   // Assert that screenshots of same page in same session are equal
@@ -131,6 +130,31 @@ Scenario('should work with within @WebDriverIO @Protractor @Puppeteer @Playwrigh
   });
 });
 
+Scenario('change page emulation @Playwright', async (I) => {
+  const assert = require('assert');
+  I.amOnPage('/');
+  session('mobile user', {
+    viewport: { width: 300, height: 500 },
+  }, async () => {
+    I.amOnPage('/');
+    const width = await I.executeScript('window.innerWidth');
+    assert.equal(width, 300);
+  });
+});
+
+
+Scenario('emulate iPhone @Playwright', async (I) => {
+  const { devices } = require('playwright');
+  if (process.env.BROWSER === 'firefox') return;
+  const assert = require('assert');
+  I.amOnPage('/');
+  session('mobile user', devices['iPhone 6'], async () => {
+    I.amOnPage('/');
+    const width = await I.executeScript('window.innerWidth');
+    assert.ok(width > 950 && width < 1000);
+  });
+});
+
 xScenario('should use different base URL @Protractor @Puppeteer @Playwright', (I) => { // nah, that's broken
   I.amOnPage('/');
   I.see('Welcome to test app');
diff --git a/test/data/sandbox/config.js b/test/data/sandbox/config.js
index 460dea48..7244343c 100644
--- a/test/data/sandbox/config.js
+++ b/test/data/sandbox/config.js
@@ -1,4 +1,4 @@
-const profile = process.profile;
+const profile = process.env.profile || process.profile;
 
 exports.config = {
   tests: './*_test.js',
diff --git a/test/data/sandbox/configs/commentStep/codecept.conf.js b/test/data/sandbox/configs/commentStep/codecept.conf.js
new file mode 100644
index 00000000..df9bec82
--- /dev/null
+++ b/test/data/sandbox/configs/commentStep/codecept.conf.js
@@ -0,0 +1,16 @@
+exports.config = {
+  tests: './*_test.js',
+  output: './output',
+  helpers: {
+    CustomHelper: {
+      require: './customHelper.js',
+    },
+  },
+  plugins: {
+    commentStep: {
+      enabled: true,
+      registerGlobal: true,
+    },
+  },
+  name: 'pageobject-as-class',
+};
diff --git a/test/data/sandbox/configs/commentStep/customHelper.js b/test/data/sandbox/configs/commentStep/customHelper.js
new file mode 100644
index 00000000..84fb5354
--- /dev/null
+++ b/test/data/sandbox/configs/commentStep/customHelper.js
@@ -0,0 +1,10 @@
+// const Helper = require('../../lib/helper');
+
+class CustomHelper extends Helper {
+  print(s) {
+    // this.debug('Print message from CustomHelper');
+    // console.log(s);
+  }
+}
+
+module.exports = CustomHelper;
diff --git a/test/data/sandbox/configs/commentStep/first_test.js b/test/data/sandbox/configs/commentStep/first_test.js
new file mode 100644
index 00000000..1f3a5467
--- /dev/null
+++ b/test/data/sandbox/configs/commentStep/first_test.js
@@ -0,0 +1,42 @@
+const given = when = then = global.codeceptjs.container.plugins('commentStep');
+const { I } = inject();
+
+Feature('CommentStep');
+
+const pageObject = {
+  metaPrint: (data) => {
+    I.print('meta value');
+    I.print(data);
+  },
+};
+
+Scenario('global var', (I) => {
+  __`Prepare user base`;
+  I.print('other thins');
+
+  __`Update data`;
+  I.print('do some things');
+
+  __`Check the result`;
+  I.print('see everything works');
+});
+
+Scenario('local vars', (I) => {
+  given`Prepare project`;
+  I.print('other thins');
+
+  when`Update project`;
+  I.print('do some things');
+
+  then`Check project`;
+  I.print('see everything works');
+});
+
+Scenario('from page object', (I) => {
+  __`Prepare project`;
+  I.print('other thins');
+  pageObject.metaPrint('login user');
+
+  __`Update project`;
+  I.print('do some things');
+});
diff --git a/test/helper/Playwright_test.js b/test/helper/Playwright_test.js
index e61c6a5b..8e99cb00 100644
--- a/test/helper/Playwright_test.js
+++ b/test/helper/Playwright_test.js
@@ -27,7 +27,7 @@ describe('Playwright', function () {
     I = new Playwright({
       url: siteUrl,
       windowSize: '500x700',
-      show: true,
+      show: false,
       waitForTimeout: 5000,
       waitForAction: 500,
       restart: true,
@@ -54,19 +54,6 @@ describe('Playwright', function () {
     return I._after();
   });
 
-  xdescribe('Session', () => {
-    it('should not fail for localStorage.clear() on about:blank', async () => {
-      I.options.restart = false;
-      return I.page.goto('about:blank')
-        .then(() => I._after())
-        .then(() => { I.options.restart = true; })
-        .catch((e) => {
-          I.options.restart = true;
-          throw new Error(e);
-        });
-    });
-  });
-
   describe('open page : #amOnPage', () => {
     it('should open main page of configured site', async () => {
       await I.amOnPage('/');
@@ -84,16 +71,6 @@ describe('Playwright', function () {
       const url = await page.url();
       return url.should.eql(`${siteUrl}/`);
     });
-
-    it('should be unauthenticated ', async () => {
-      let err = true;
-      try {
-        await I.amOnPage('/basic_auth');
-      } catch (e) {
-        err = false;
-      }
-      if (err) throw new Error('Should fail at auth page');
-    });
   });
 
   describe('grabDataFromPerformanceTiming', () => {
@@ -781,82 +758,6 @@ describe('Playwright', function () {
       await FS.waitForFile('downloads/avatar.jpg', 5);
     });
   });
-
-  describe('#waitForClickable', () => {
-    it('should wait for clickable', async () => {
-      await I.amOnPage('/form/wait_for_clickable');
-      await I.waitForClickable({ css: 'input#text' });
-    });
-
-    it('should wait for clickable by XPath', async () => {
-      await I.amOnPage('/form/wait_for_clickable');
-      await I.waitForClickable({ xpath: './/input[@id="text"]' });
-    });
-
-    it('should fail for disabled element', async () => {
-      await I.amOnPage('/form/wait_for_clickable');
-      await I.waitForClickable({ css: '#button' }, 0.1).then((isClickable) => {
-        if (isClickable) throw new Error('Element is clickable, but must be unclickable');
-      }).catch((e) => {
-        e.message.should.include('element {css: #button} still not clickable after 0.1 sec');
-      });
-    });
-
-    it('should fail for disabled element by XPath', async () => {
-      await I.amOnPage('/form/wait_for_clickable');
-      await I.waitForClickable({ xpath: './/button[@id="button"]' }, 0.1).then((isClickable) => {
-        if (isClickable) throw new Error('Element is clickable, but must be unclickable');
-      }).catch((e) => {
-        e.message.should.include('element {xpath: .//button[@id="button"]} still not clickable after 0.1 sec');
-      });
-    });
-
-    it('should fail for element not in viewport by top', async () => {
-      await I.amOnPage('/form/wait_for_clickable');
-      await I.waitForClickable({ css: '#notInViewportTop' }, 0.1).then((isClickable) => {
-        if (isClickable) throw new Error('Element is clickable, but must be unclickable');
-      }).catch((e) => {
-        e.message.should.include('element {css: #notInViewportTop} still not clickable after 0.1 sec');
-      });
-    });
-
-    it('should fail for element not in viewport by bottom', async () => {
-      await I.amOnPage('/form/wait_for_clickable');
-      await I.waitForClickable({ css: '#notInViewportBottom' }, 0.1).then((isClickable) => {
-        if (isClickable) throw new Error('Element is clickable, but must be unclickable');
-      }).catch((e) => {
-        e.message.should.include('element {css: #notInViewportBottom} still not clickable after 0.1 sec');
-      });
-    });
-
-    it('should fail for element not in viewport by left', async () => {
-      await I.amOnPage('/form/wait_for_clickable');
-      await I.waitForClickable({ css: '#notInViewportLeft' }, 0.1).then((isClickable) => {
-        if (isClickable) throw new Error('Element is clickable, but must be unclickable');
-      }).catch((e) => {
-        e.message.should.include('element {css: #notInViewportLeft} still not clickable after 0.1 sec');
-      });
-    });
-
-    it('should fail for element not in viewport by right', async () => {
-      await I.amOnPage('/form/wait_for_clickable');
-      await I.waitForClickable({ css: '#notInViewportRight' }, 0.1).then((isClickable) => {
-        if (isClickable) throw new Error('Element is clickable, but must be unclickable');
-      }).catch((e) => {
-        e.message.should.include('element {css: #notInViewportRight} still not clickable after 0.1 sec');
-      });
-    });
-
-    it('should fail for overlapping element', async () => {
-      await I.amOnPage('/form/wait_for_clickable');
-      await I.waitForClickable({ css: '#div2_button' }, 0.1);
-      await I.waitForClickable({ css: '#div1_button' }, 0.1).then((isClickable) => {
-        if (isClickable) throw new Error('Element is clickable, but must be unclickable');
-      }).catch((e) => {
-        e.message.should.include('element {css: #div1_button} still not clickable after 0.1 sec');
-      });
-    });
-  });
 });
 
 let remoteBrowser;
@@ -997,3 +898,43 @@ describe('Playwright - BasicAuth', () => {
     });
   });
 });
+
+describe('Playwright - Emulation', () => {
+  before(() => {
+    const { devices } = require('playwright');
+    global.codecept_dir = path.join(__dirname, '/../data');
+
+    I = new Playwright({
+      url: 'http://localhost:8000',
+      browser: 'chromium',
+      windowSize: '500x700',
+      emulate: devices['iPhone 6'],
+      show: false,
+      restart: true,
+      waitForTimeout: 5000,
+      waitForAction: 500,
+      chrome: {
+        args: ['--no-sandbox', '--disable-setuid-sandbox'],
+      },
+    });
+    I._init();
+    return I._beforeSuite();
+  });
+
+  beforeEach(() => {
+    return I._before().then(() => {
+      page = I.page;
+      browser = I.browser;
+    });
+  });
+
+  afterEach(() => {
+    return I._after();
+  });
+
+  it('should open page as iPhone ', async () => {
+    await I.amOnPage('/');
+    const width = await I.executeScript('window.innerWidth');
+    assert.equal(width, 980);
+  });
+});
diff --git a/test/helper/webapi.js b/test/helper/webapi.js
index 4bcc2f0c..b2164789 100644
--- a/test/helper/webapi.js
+++ b/test/helper/webapi.js
@@ -400,6 +400,7 @@ module.exports.tests = function () {
 
     it('should execute async script', async function () {
       if (isHelper('TestCafe')) this.skip(); // TODO Not yet implemented
+      if (isHelper('Playwright')) return; // It won't be implemented
 
       await I.amOnPage('/');
       const val = await I.executeAsyncScript((val, done) => {
diff --git a/test/runner/allure_test.js b/test/runner/allure_test.js
index 929ba813..501443e3 100644
--- a/test/runner/allure_test.js
+++ b/test/runner/allure_test.js
@@ -2,6 +2,7 @@ const assert = require('assert');
 const path = require('path');
 const { exec } = require('child_process');
 const fs = require('fs');
+const { satisfyNodeVersion } = require('../../lib/command/utils');
 const { deleteDir } = require('../../lib/utils');
 
 const runner = path.join(__dirname, '/../../bin/codecept.js');
@@ -58,9 +59,13 @@ describe('CodeceptJS Allure Plugin', () => {
     });
   });
 
-  it('should report BeforeSuite errors when executing via run-workers command', (done) => {
+  it('should report BeforeSuite errors when executing via run-workers command', function (done) {
+    if (parseInt(process.version.match(/\d+/), 10) < 12) {
+      this.skip();
+    }
+
     exec(codecept_workers_config('before_suite_test_failed.conf.js'), (err, stdout) => {
-      stdout.should.include('FAIL  | 0 passed, 1 failed');
+      stdout.should.include('FAIL  | 0 passed');
 
       const files = fs.readdirSync(path.join(codecept_dir, 'output/failed'));
       const testResultPath = files[0];
diff --git a/test/runner/comment_step_test.js b/test/runner/comment_step_test.js
new file mode 100644
index 00000000..17a9d41e
--- /dev/null
+++ b/test/runner/comment_step_test.js
@@ -0,0 +1,47 @@
+const assert = require('assert');
+const path = require('path');
+const exec = require('child_process').exec;
+
+const runner = path.join(__dirname, '/../../bin/codecept.js');
+const codecept_dir = path.join(
+  __dirname,
+  '/../data/sandbox/configs/commentStep',
+);
+const codecept_run = `${runner} run`;
+const config_run_config = (config, grep) => `${codecept_run} --config ${codecept_dir}/${config} ${
+  grep ? `--grep "${grep}"` : ''
+}`;
+
+describe('CodeceptJS commentStep plugin', function () {
+  this.timeout(3000);
+
+  before(() => {
+    process.chdir(codecept_dir);
+  });
+
+  it('should print nested steps when global var comments used', done => {
+    exec(
+      `${config_run_config('codecept.conf.js', 'global var')} --debug`,
+      (err, stdout) => {
+        stdout.should.include('    Prepare user base  \n      I print "other thins"');
+        stdout.should.include('    Update data  \n      I print "do some things"');
+        stdout.should.include('    Check the result  \n      I print "see everything works"');
+        assert(!err);
+        done();
+      },
+    );
+  });
+
+  it('should print nested steps when local var comments used', done => {
+    exec(
+      `${config_run_config('codecept.conf.js', 'local var')} --debug`,
+      (err, stdout) => {
+        stdout.should.include('    Prepare project  \n      I print "other thins"');
+        stdout.should.include('    Update project  \n      I print "do some things"');
+        stdout.should.include('    Check project  \n      I print "see everything works"');
+        assert(!err);
+        done();
+      },
+    );
+  });
+});
diff --git a/test/support/ScreenshotSessionHelper.js b/test/support/ScreenshotSessionHelper.js
index 49d82d2d..7f4c0809 100644
--- a/test/support/ScreenshotSessionHelper.js
+++ b/test/support/ScreenshotSessionHelper.js
@@ -14,7 +14,7 @@ class ScreenshotSessionHelper extends Helper {
 
   constructor(config) {
     super(config);
-    this.outputPath = config.outputPath;
+    this.outputPath = output_dir;
   }
 
   getMD5Digests(files = []) {