codeceptjs 3.5.0 → 3.5.1-2.beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. package/README.md +24 -25
  2. package/lib/actor.js +6 -3
  3. package/lib/ai.js +12 -3
  4. package/lib/cli.js +12 -2
  5. package/lib/codecept.js +4 -0
  6. package/lib/colorUtils.js +10 -0
  7. package/lib/command/definitions.js +2 -7
  8. package/lib/command/dryRun.js +2 -1
  9. package/lib/command/info.js +24 -0
  10. package/lib/command/init.js +51 -5
  11. package/lib/command/run-multiple/collection.js +17 -5
  12. package/lib/command/run-multiple.js +4 -2
  13. package/lib/command/run-workers.js +66 -4
  14. package/lib/command/run.js +7 -0
  15. package/lib/command/workers/runTests.js +39 -0
  16. package/lib/data/context.js +14 -6
  17. package/lib/event.js +4 -0
  18. package/lib/helper/ApiDataFactory.js +2 -1
  19. package/lib/helper/Appium.js +73 -24
  20. package/lib/helper/Expect.js +422 -0
  21. package/lib/helper/FileSystem.js +1 -1
  22. package/lib/helper/GraphQL.js +25 -0
  23. package/lib/helper/Nightmare.js +9 -4
  24. package/lib/helper/OpenAI.js +14 -10
  25. package/lib/helper/Playwright.js +1205 -288
  26. package/lib/helper/Protractor.js +11 -6
  27. package/lib/helper/Puppeteer.js +173 -61
  28. package/lib/helper/TestCafe.js +44 -9
  29. package/lib/helper/WebDriver.js +231 -82
  30. package/lib/helper/errors/ElementNotFound.js +2 -1
  31. package/lib/helper/extras/PlaywrightReactVueLocator.js +38 -0
  32. package/lib/helper/scripts/blurElement.js +17 -0
  33. package/lib/helper/scripts/focusElement.js +17 -0
  34. package/lib/helper/scripts/highlightElement.js +2 -2
  35. package/lib/html.js +3 -3
  36. package/lib/interfaces/bdd.js +1 -1
  37. package/lib/interfaces/gherkin.js +37 -3
  38. package/lib/interfaces/scenarioConfig.js +1 -0
  39. package/lib/locator.js +17 -4
  40. package/lib/mochaFactory.js +2 -1
  41. package/lib/output.js +1 -1
  42. package/lib/pause.js +12 -9
  43. package/lib/plugin/autoLogin.js +45 -10
  44. package/lib/plugin/heal.js +47 -17
  45. package/lib/plugin/retryFailedStep.js +10 -1
  46. package/lib/plugin/retryTo.js +2 -4
  47. package/lib/plugin/selenoid.js +6 -1
  48. package/lib/plugin/standardActingHelpers.js +0 -2
  49. package/lib/plugin/stepByStepReport.js +2 -2
  50. package/lib/plugin/tryTo.js +5 -7
  51. package/lib/plugin/wdio.js +0 -1
  52. package/lib/recorder.js +20 -9
  53. package/lib/session.js +1 -1
  54. package/lib/step.js +30 -11
  55. package/lib/ui.js +1 -0
  56. package/lib/utils.js +18 -1
  57. package/lib/workers.js +28 -3
  58. package/package.json +108 -98
  59. package/translations/de-DE.js +5 -0
  60. package/translations/fr-FR.js +14 -1
  61. package/translations/it-IT.js +1 -0
  62. package/translations/ja-JP.js +5 -0
  63. package/translations/pl-PL.js +5 -0
  64. package/translations/pt-BR.js +1 -0
  65. package/translations/ru-RU.js +1 -0
  66. package/translations/zh-CN.js +5 -0
  67. package/translations/zh-TW.js +5 -0
  68. package/typings/index.d.ts +8 -6
  69. package/typings/promiseBasedTypes.d.ts +784 -822
  70. package/typings/types.d.ts +1214 -727
  71. package/CHANGELOG.md +0 -2492
  72. package/docs/advanced.md +0 -351
  73. package/docs/ai.md +0 -246
  74. package/docs/api.md +0 -323
  75. package/docs/basics.md +0 -980
  76. package/docs/bdd.md +0 -535
  77. package/docs/best.md +0 -237
  78. package/docs/books.md +0 -37
  79. package/docs/bootstrap.md +0 -135
  80. package/docs/build/ApiDataFactory.js +0 -409
  81. package/docs/build/Appium.js +0 -1978
  82. package/docs/build/FileSystem.js +0 -228
  83. package/docs/build/GraphQL.js +0 -204
  84. package/docs/build/GraphQLDataFactory.js +0 -309
  85. package/docs/build/JSONResponse.js +0 -338
  86. package/docs/build/Mochawesome.js +0 -71
  87. package/docs/build/Nightmare.js +0 -2147
  88. package/docs/build/OpenAI.js +0 -122
  89. package/docs/build/Playwright.js +0 -4134
  90. package/docs/build/Polly.js +0 -42
  91. package/docs/build/Protractor.js +0 -2701
  92. package/docs/build/Puppeteer.js +0 -3743
  93. package/docs/build/REST.js +0 -344
  94. package/docs/build/SeleniumWebdriver.js +0 -76
  95. package/docs/build/TestCafe.js +0 -2059
  96. package/docs/build/WebDriver.js +0 -4042
  97. package/docs/changelog.md +0 -2501
  98. package/docs/commands.md +0 -254
  99. package/docs/community-helpers.md +0 -58
  100. package/docs/configuration.md +0 -157
  101. package/docs/continuous-integration.md +0 -22
  102. package/docs/custom-helpers.md +0 -306
  103. package/docs/data.md +0 -375
  104. package/docs/detox.md +0 -235
  105. package/docs/docker.md +0 -137
  106. package/docs/email.md +0 -183
  107. package/docs/examples.md +0 -149
  108. package/docs/helpers/ApiDataFactory.md +0 -266
  109. package/docs/helpers/Appium.md +0 -1317
  110. package/docs/helpers/Detox.md +0 -586
  111. package/docs/helpers/FileSystem.md +0 -152
  112. package/docs/helpers/GraphQL.md +0 -130
  113. package/docs/helpers/GraphQLDataFactory.md +0 -226
  114. package/docs/helpers/JSONResponse.md +0 -254
  115. package/docs/helpers/Mochawesome.md +0 -8
  116. package/docs/helpers/MockRequest.md +0 -377
  117. package/docs/helpers/Nightmare.md +0 -1258
  118. package/docs/helpers/OpenAI.md +0 -70
  119. package/docs/helpers/Playwright.md +0 -2250
  120. package/docs/helpers/Polly.md +0 -44
  121. package/docs/helpers/Puppeteer-firefox.md +0 -86
  122. package/docs/helpers/Puppeteer.md +0 -2147
  123. package/docs/helpers/REST.md +0 -218
  124. package/docs/helpers/TestCafe.md +0 -1224
  125. package/docs/helpers/WebDriver.md +0 -2325
  126. package/docs/hooks.md +0 -340
  127. package/docs/index.md +0 -111
  128. package/docs/installation.md +0 -75
  129. package/docs/internal-api.md +0 -265
  130. package/docs/locators.md +0 -331
  131. package/docs/mobile-react-native-locators.md +0 -67
  132. package/docs/mobile.md +0 -344
  133. package/docs/nightmare.md +0 -223
  134. package/docs/pageobjects.md +0 -291
  135. package/docs/parallel.md +0 -288
  136. package/docs/playwright.md +0 -609
  137. package/docs/plugins.md +0 -1225
  138. package/docs/puppeteer.md +0 -316
  139. package/docs/quickstart.md +0 -163
  140. package/docs/react.md +0 -69
  141. package/docs/reports.md +0 -392
  142. package/docs/secrets.md +0 -36
  143. package/docs/shadow.md +0 -68
  144. package/docs/shared/keys.mustache +0 -31
  145. package/docs/shared/react.mustache +0 -1
  146. package/docs/testcafe.md +0 -174
  147. package/docs/translation.md +0 -247
  148. package/docs/tutorial.md +0 -271
  149. package/docs/typescript.md +0 -180
  150. package/docs/ui.md +0 -59
  151. package/docs/videos.md +0 -28
  152. package/docs/visual.md +0 -202
  153. package/docs/vue.md +0 -121
  154. package/docs/webapi/amOnPage.mustache +0 -11
  155. package/docs/webapi/appendField.mustache +0 -11
  156. package/docs/webapi/attachFile.mustache +0 -12
  157. package/docs/webapi/checkOption.mustache +0 -13
  158. package/docs/webapi/clearCookie.mustache +0 -10
  159. package/docs/webapi/clearField.mustache +0 -9
  160. package/docs/webapi/click.mustache +0 -25
  161. package/docs/webapi/clickLink.mustache +0 -8
  162. package/docs/webapi/closeCurrentTab.mustache +0 -7
  163. package/docs/webapi/closeOtherTabs.mustache +0 -8
  164. package/docs/webapi/dontSee.mustache +0 -11
  165. package/docs/webapi/dontSeeCheckboxIsChecked.mustache +0 -10
  166. package/docs/webapi/dontSeeCookie.mustache +0 -8
  167. package/docs/webapi/dontSeeCurrentUrlEquals.mustache +0 -10
  168. package/docs/webapi/dontSeeElement.mustache +0 -8
  169. package/docs/webapi/dontSeeElementInDOM.mustache +0 -8
  170. package/docs/webapi/dontSeeInCurrentUrl.mustache +0 -4
  171. package/docs/webapi/dontSeeInField.mustache +0 -11
  172. package/docs/webapi/dontSeeInSource.mustache +0 -8
  173. package/docs/webapi/dontSeeInTitle.mustache +0 -8
  174. package/docs/webapi/doubleClick.mustache +0 -13
  175. package/docs/webapi/downloadFile.mustache +0 -12
  176. package/docs/webapi/dragAndDrop.mustache +0 -9
  177. package/docs/webapi/dragSlider.mustache +0 -11
  178. package/docs/webapi/executeAsyncScript.mustache +0 -24
  179. package/docs/webapi/executeScript.mustache +0 -26
  180. package/docs/webapi/fillField.mustache +0 -16
  181. package/docs/webapi/forceClick.mustache +0 -28
  182. package/docs/webapi/forceRightClick.mustache +0 -18
  183. package/docs/webapi/grabAllWindowHandles.mustache +0 -7
  184. package/docs/webapi/grabAttributeFrom.mustache +0 -10
  185. package/docs/webapi/grabAttributeFromAll.mustache +0 -9
  186. package/docs/webapi/grabBrowserLogs.mustache +0 -9
  187. package/docs/webapi/grabCookie.mustache +0 -11
  188. package/docs/webapi/grabCssPropertyFrom.mustache +0 -11
  189. package/docs/webapi/grabCssPropertyFromAll.mustache +0 -10
  190. package/docs/webapi/grabCurrentUrl.mustache +0 -9
  191. package/docs/webapi/grabCurrentWindowHandle.mustache +0 -6
  192. package/docs/webapi/grabDataFromPerformanceTiming.mustache +0 -20
  193. package/docs/webapi/grabElementBoundingRect.mustache +0 -20
  194. package/docs/webapi/grabGeoLocation.mustache +0 -8
  195. package/docs/webapi/grabHTMLFrom.mustache +0 -10
  196. package/docs/webapi/grabHTMLFromAll.mustache +0 -9
  197. package/docs/webapi/grabNumberOfOpenTabs.mustache +0 -8
  198. package/docs/webapi/grabNumberOfVisibleElements.mustache +0 -9
  199. package/docs/webapi/grabPageScrollPosition.mustache +0 -8
  200. package/docs/webapi/grabPopupText.mustache +0 -5
  201. package/docs/webapi/grabSource.mustache +0 -8
  202. package/docs/webapi/grabTextFrom.mustache +0 -10
  203. package/docs/webapi/grabTextFromAll.mustache +0 -9
  204. package/docs/webapi/grabTitle.mustache +0 -8
  205. package/docs/webapi/grabValueFrom.mustache +0 -9
  206. package/docs/webapi/grabValueFromAll.mustache +0 -8
  207. package/docs/webapi/moveCursorTo.mustache +0 -12
  208. package/docs/webapi/openNewTab.mustache +0 -7
  209. package/docs/webapi/pressKey.mustache +0 -12
  210. package/docs/webapi/pressKeyDown.mustache +0 -12
  211. package/docs/webapi/pressKeyUp.mustache +0 -12
  212. package/docs/webapi/pressKeyWithKeyNormalization.mustache +0 -60
  213. package/docs/webapi/refreshPage.mustache +0 -6
  214. package/docs/webapi/resizeWindow.mustache +0 -6
  215. package/docs/webapi/rightClick.mustache +0 -14
  216. package/docs/webapi/saveElementScreenshot.mustache +0 -10
  217. package/docs/webapi/saveScreenshot.mustache +0 -12
  218. package/docs/webapi/say.mustache +0 -10
  219. package/docs/webapi/scrollIntoView.mustache +0 -11
  220. package/docs/webapi/scrollPageToBottom.mustache +0 -6
  221. package/docs/webapi/scrollPageToTop.mustache +0 -6
  222. package/docs/webapi/scrollTo.mustache +0 -12
  223. package/docs/webapi/see.mustache +0 -11
  224. package/docs/webapi/seeAttributesOnElements.mustache +0 -9
  225. package/docs/webapi/seeCheckboxIsChecked.mustache +0 -10
  226. package/docs/webapi/seeCookie.mustache +0 -8
  227. package/docs/webapi/seeCssPropertiesOnElements.mustache +0 -9
  228. package/docs/webapi/seeCurrentUrlEquals.mustache +0 -11
  229. package/docs/webapi/seeElement.mustache +0 -8
  230. package/docs/webapi/seeElementInDOM.mustache +0 -8
  231. package/docs/webapi/seeInCurrentUrl.mustache +0 -8
  232. package/docs/webapi/seeInField.mustache +0 -12
  233. package/docs/webapi/seeInPopup.mustache +0 -8
  234. package/docs/webapi/seeInSource.mustache +0 -7
  235. package/docs/webapi/seeInTitle.mustache +0 -8
  236. package/docs/webapi/seeNumberOfElements.mustache +0 -11
  237. package/docs/webapi/seeNumberOfVisibleElements.mustache +0 -10
  238. package/docs/webapi/seeTextEquals.mustache +0 -9
  239. package/docs/webapi/seeTitleEquals.mustache +0 -8
  240. package/docs/webapi/selectOption.mustache +0 -21
  241. package/docs/webapi/setCookie.mustache +0 -16
  242. package/docs/webapi/setGeoLocation.mustache +0 -12
  243. package/docs/webapi/switchTo.mustache +0 -9
  244. package/docs/webapi/switchToNextTab.mustache +0 -10
  245. package/docs/webapi/switchToPreviousTab.mustache +0 -10
  246. package/docs/webapi/type.mustache +0 -21
  247. package/docs/webapi/uncheckOption.mustache +0 -13
  248. package/docs/webapi/wait.mustache +0 -8
  249. package/docs/webapi/waitForClickable.mustache +0 -11
  250. package/docs/webapi/waitForDetached.mustache +0 -10
  251. package/docs/webapi/waitForElement.mustache +0 -11
  252. package/docs/webapi/waitForEnabled.mustache +0 -6
  253. package/docs/webapi/waitForFunction.mustache +0 -17
  254. package/docs/webapi/waitForInvisible.mustache +0 -10
  255. package/docs/webapi/waitForText.mustache +0 -13
  256. package/docs/webapi/waitForValue.mustache +0 -10
  257. package/docs/webapi/waitForVisible.mustache +0 -10
  258. package/docs/webapi/waitInUrl.mustache +0 -9
  259. package/docs/webapi/waitNumberOfVisibleElements.mustache +0 -10
  260. package/docs/webapi/waitToHide.mustache +0 -10
  261. package/docs/webapi/waitUrlEquals.mustache +0 -10
  262. package/docs/webdriver.md +0 -657
  263. package/docs/wiki/Books-&-Posts.md +0 -27
  264. package/docs/wiki/Community-Helpers-&-Plugins.md +0 -49
  265. package/docs/wiki/Converting-Playwright-to-Istanbul-Coverage.md +0 -29
  266. package/docs/wiki/Examples.md +0 -139
  267. package/docs/wiki/Google-Summer-of-Code-(GSoC)-2020.md +0 -68
  268. package/docs/wiki/Home.md +0 -16
  269. package/docs/wiki/Release-Process.md +0 -24
  270. package/docs/wiki/Roadmap.md +0 -23
  271. package/docs/wiki/Tests.md +0 -1393
  272. package/docs/wiki/Upgrading-to-CodeceptJS-3.md +0 -153
  273. package/docs/wiki/Videos.md +0 -19
package/README.md CHANGED
@@ -7,10 +7,14 @@
7
7
 
8
8
  Build Status:
9
9
 
10
+ Appium Helper:
11
+ [![Appium V2 Tests - Android](https://github.com/codeceptjs/CodeceptJS/actions/workflows/appiumV2_Android.yml/badge.svg)](https://github.com/codeceptjs/CodeceptJS/actions/workflows/appiumV2_Android.yml)
12
+ [![Appium V2 Tests - iOS](https://github.com/codeceptjs/CodeceptJS/actions/workflows/appiumV2_iOS.yml/badge.svg)](https://github.com/codeceptjs/CodeceptJS/actions/workflows/appiumV2_iOS.yml)
13
+
14
+ Web Helper:
10
15
  [![Playwright Tests](https://github.com/codeceptjs/CodeceptJS/actions/workflows/playwright.yml/badge.svg)](https://github.com/codeceptjs/CodeceptJS/actions/workflows/playwright.yml)
11
16
  [![Puppeteer Tests](https://github.com/codeceptjs/CodeceptJS/actions/workflows/puppeteer.yml/badge.svg)](https://github.com/codeceptjs/CodeceptJS/actions/workflows/puppeteer.yml)
12
17
  [![WebDriver Tests](https://github.com/codeceptjs/CodeceptJS/actions/workflows/webdriver.yml/badge.svg)](https://github.com/codeceptjs/CodeceptJS/actions/workflows/webdriver.yml)
13
- [![Appium Tests](https://github.com/codeceptjs/CodeceptJS/actions/workflows/appium.yml/badge.svg)](https://github.com/codeceptjs/CodeceptJS/actions/workflows/appium.yml)
14
18
  [![TestCafe Tests](https://github.com/codeceptjs/CodeceptJS/actions/workflows/testcafe.yml/badge.svg)](https://github.com/codeceptjs/CodeceptJS/actions/workflows/testcafe.yml)
15
19
 
16
20
  # CodeceptJS [![Made in Ukraine](https://img.shields.io/badge/made_in-ukraine-ffd700.svg?labelColor=0057b7)](https://stand-with-ukraine.pp.ua)
@@ -20,7 +24,7 @@ Reference: [Helpers API](https://github.com/codeceptjs/CodeceptJS/tree/master/do
20
24
  ## Supercharged E2E Testing
21
25
 
22
26
  CodeceptJS is a new testing framework for end-to-end testing with WebDriver (or others).
23
- It abstracts browser interaction to simple steps that are written from a user perspective.
27
+ It abstracts browser interaction to simple steps that are written from a user's perspective.
24
28
  A simple test that verifies the "Welcome" text is present on a main page of a site will look like:
25
29
 
26
30
  ```js
@@ -36,7 +40,7 @@ CodeceptJS tests are:
36
40
 
37
41
  * **Synchronous**. You don't need to care about callbacks or promises or test scenarios which are linear. But, your tests should be linear.
38
42
  * Written from **user's perspective**. Every action is a method of `I`. That makes test easy to read, write and maintain even for non-tech persons.
39
- * Backend **API agnostic**. We don't know which WebDriver implementation is running this test. We can easily switch from WebDriverIO to Protractor or PhantomJS.
43
+ * Backend **API agnostic**. We don't know which WebDriver implementation is running this test.
40
44
 
41
45
  CodeceptJS uses **Helper** modules to provide actions to `I` object. Currently, CodeceptJS has these helpers:
42
46
 
@@ -44,7 +48,6 @@ CodeceptJS uses **Helper** modules to provide actions to `I` object. Currently,
44
48
  * [**Puppeteer**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/Puppeteer.md) - uses Google Chrome's Puppeteer for fast headless testing.
45
49
  * [**WebDriver**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/WebDriver.md) - uses [webdriverio](http://webdriver.io/) to run tests via WebDriver protocol.
46
50
  * [**TestCafe**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/TestCafe.md) - cheap and fast cross-browser test automation.
47
- * [**Nightmare**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/Nightmare.md) - uses Electron and NightmareJS to run tests.
48
51
  * [**Appium**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/Appium.md) - for **mobile testing** with Appium
49
52
  * [**Detox**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/Detox.md) - This is a wrapper on top of Detox library, aimed to unify testing experience for CodeceptJS framework. Detox provides a grey box testing for mobile applications, playing especially well for React Native apps.
50
53
 
@@ -54,27 +57,27 @@ And more to come...
54
57
 
55
58
  CodeceptJS is a successor of [Codeception](http://codeception.com), a popular full-stack testing framework for PHP.
56
59
  With CodeceptJS your scenario-driven functional and acceptance tests will be as simple and clean as they can be.
57
- You don't need to worry about asynchronous nature of NodeJS or about various APIs of Selenium, Puppeteer, Protractor, TestCafe, etc. as CodeceptJS unifies them and makes them work as they are synchronous.
60
+ You don't need to worry about asynchronous nature of NodeJS or about various APIs of Playwright, Selenium, Puppeteer, TestCafe, etc. as CodeceptJS unifies them and makes them work as they are synchronous.
58
61
 
59
62
 
60
63
  ## Features
61
64
 
62
- * 🪄 **AI-powered** with GPT features to assist and heal failing tests
63
- * Based on [Mocha](https://mochajs.org/) testing framework.
64
- * Designed for scenario driven acceptance testing in BDD-style
65
- * Uses ES6 natively without transpiler.
65
+ * 🪄 **AI-powered** with GPT features to assist and heal failing tests.
66
+ * Based on [Mocha](https://mochajs.org/) testing framework.
67
+ * 💼 Designed for scenario driven acceptance testing in BDD-style.
68
+ * 💻 Uses ES6 natively without transpiler.
66
69
  * Also plays nice with TypeScript.
67
- * Smart locators: use names, labels, matching text, CSS or XPath to locate elements.
68
- * Interactive debugging shell: pause test at any point and try different commands in a browser.
70
+ * </> Smart locators: use names, labels, matching text, CSS or XPath to locate elements.
71
+ * 🌐 Interactive debugging shell: pause test at any point and try different commands in a browser.
69
72
  * Easily create tests, pageobjects, stepobjects with CLI generators.
70
73
 
71
- ## Install
74
+ ## Installation
72
75
 
73
76
  ```sh
74
77
  npm i codeceptjs --save
75
78
  ```
76
79
 
77
- Move to directory where you'd like to have your tests (and codeceptjs config) stored, and execute
80
+ Move to directory where you'd like to have your tests (and CodeceptJS config) stored, and execute:
78
81
 
79
82
  ```sh
80
83
  npx codeceptjs init
@@ -128,8 +131,8 @@ Scenario('test some forms', ({ I }) => {
128
131
  });
129
132
  ```
130
133
 
131
- All actions are performed by I object; assertions functions start with `see` function.
132
- In this examples all methods of `I` are taken from WebDriver helper, see [reference](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/WebDriver.md) to learn how to use them.
134
+ All actions are performed by `I` object; assertions functions start with `see` function.
135
+ In these examples all methods of `I` are taken from WebDriver helper, see [reference](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/WebDriver.md) to learn how to use them.
133
136
 
134
137
  Let's execute this test with `run` command. Additional option `--steps` will show us the running process. We recommend use `--steps` or `--debug` during development.
135
138
 
@@ -195,17 +198,15 @@ In this case 'User is valid' string will be searched only inside elements locate
195
198
  ### Grabbers
196
199
 
197
200
  In case you need to return a value from a webpage and use it directly in test, you should use methods with `grab` prefix.
198
- They are expected to be used inside async/await functions, and their results will be available in test:
201
+ They are expected to be used inside `async/await` functions, and their results will be available in test:
199
202
 
200
203
  ```js
201
- const assert = require('assert');
202
-
203
204
  Feature('CodeceptJS Demonstration');
204
205
 
205
206
  Scenario('test page title', async ({ I }) => {
206
207
  I.amOnPage('http://simple-form-bootstrap.plataformatec.com.br/documentation');
207
208
  const title = await I.grabTitle();
208
- assert.equal(title, 'Example application with SimpleForm and Twitter Bootstrap');
209
+ I.expectEqual(title, 'Example application with SimpleForm and Twitter Bootstrap'); // Avaiable with Expect helper. -> https://codecept.io/helpers/Expect/
209
210
  });
210
211
  ```
211
212
 
@@ -303,8 +304,8 @@ Thanks all to those who are and will have contributing to this awesome project!
303
304
  <a href="https://github.com/APshenkin"><img src="https://avatars.githubusercontent.com/u/14344430?v=4" title="APshenkin" width="80" height="80"></a>
304
305
  <a href="https://github.com/fabioel"><img src="https://avatars.githubusercontent.com/u/9824235?v=4" title="fabioel" width="80" height="80"></a>
305
306
  <a href="https://github.com/pablopaul"><img src="https://avatars.githubusercontent.com/u/635526?v=4" title="pablopaul" width="80" height="80"></a>
306
- <a href="https://github.com/Georgegriff"><img src="https://avatars.githubusercontent.com/u/9056958?v=4" title="Georgegriff" width="80" height="80"></a>
307
307
  <a href="https://github.com/mirao"><img src="https://avatars.githubusercontent.com/u/12584138?v=4" title="mirao" width="80" height="80"></a>
308
+ <a href="https://github.com/Georgegriff"><img src="https://avatars.githubusercontent.com/u/9056958?v=4" title="Georgegriff" width="80" height="80"></a>
308
309
  <a href="https://github.com/KMKoushik"><img src="https://avatars.githubusercontent.com/u/24666922?v=4" title="KMKoushik" width="80" height="80"></a>
309
310
  <a href="https://github.com/nikocanvacom"><img src="https://avatars.githubusercontent.com/u/83254493?v=4" title="nikocanvacom" width="80" height="80"></a>
310
311
  <a href="https://github.com/elukoyanov"><img src="https://avatars.githubusercontent.com/u/11647141?v=4" title="elukoyanov" width="80" height="80"></a>
@@ -314,17 +315,15 @@ Thanks all to those who are and will have contributing to this awesome project!
314
315
  <a href="https://github.com/VikalpP"><img src="https://avatars.githubusercontent.com/u/11846339?v=4" title="VikalpP" width="80" height="80"></a>
315
316
  <a href="https://github.com/BorisOsipov"><img src="https://avatars.githubusercontent.com/u/6514276?v=4" title="BorisOsipov" width="80" height="80"></a>
316
317
  <a href="https://github.com/elaichenkov"><img src="https://avatars.githubusercontent.com/u/29764053?v=4" title="elaichenkov" width="80" height="80"></a>
317
- <a href="https://github.com/apps/dependabot"><img src="https://avatars.githubusercontent.com/in/29110?v=4" title="dependabot[bot]" width="80" height="80"></a>
318
318
  <a href="https://github.com/nitschSB"><img src="https://avatars.githubusercontent.com/u/39341455?v=4" title="nitschSB" width="80" height="80"></a>
319
319
  <a href="https://github.com/hubidu"><img src="https://avatars.githubusercontent.com/u/13134082?v=4" title="hubidu" width="80" height="80"></a>
320
320
  <a href="https://github.com/jploskonka"><img src="https://avatars.githubusercontent.com/u/669483?v=4" title="jploskonka" width="80" height="80"></a>
321
+ <a href="https://github.com/ngraf"><img src="https://avatars.githubusercontent.com/u/7094389?v=4" title="ngraf" width="80" height="80"></a>
321
322
  <a href="https://github.com/maojunxyz"><img src="https://avatars.githubusercontent.com/u/28778042?v=4" title="maojunxyz" width="80" height="80"></a>
322
323
  <a href="https://github.com/abhimanyupandian"><img src="https://avatars.githubusercontent.com/u/36107381?v=4" title="abhimanyupandian" width="80" height="80"></a>
323
324
  <a href="https://github.com/martomo"><img src="https://avatars.githubusercontent.com/u/1850135?v=4" title="martomo" width="80" height="80"></a>
324
- <a href="https://github.com/denis-sokolov"><img src="https://avatars.githubusercontent.com/u/113721?v=4" title="denis-sokolov" width="80" height="80"></a>
325
- <a href="https://github.com/lennym"><img src="https://avatars.githubusercontent.com/u/117398?v=4" title="lennym" width="80" height="80"></a>
326
- <a href="https://github.com/petehouston"><img src="https://avatars.githubusercontent.com/u/9006720?v=4" title="petehouston" width="80" height="80"></a>
327
- <a href="https://github.com/jancorvus"><img src="https://avatars.githubusercontent.com/u/67001310?v=4" title="jancorvus" width="80" height="80"></a>
325
+ <a href="https://github.com/hatufacci"><img src="https://avatars.githubusercontent.com/u/4963181?v=4" title="hatufacci" width="80" height="80"></a>
326
+ <a href="https://github.com/johnyb"><img src="https://avatars.githubusercontent.com/u/86358?v=4" title="johnyb" width="80" height="80"></a>
328
327
 
329
328
  [//]: contributor-faces
330
329
 
package/lib/actor.js CHANGED
@@ -13,15 +13,18 @@ const output = require('./output');
13
13
  */
14
14
  class Actor {
15
15
  /**
16
- * add print comment method`
16
+ * Print the comment on log. Also, adding a step in the `Test.steps` object
17
17
  * @param {string} msg
18
18
  * @param {string} color
19
19
  * @inner
20
20
  *
21
21
  * ⚠️ returns a promise which is synchronized internally by recorder
22
22
  */
23
- say(msg, color = 'cyan') {
24
- return recorder.add(`say ${msg}`, () => {
23
+ async say(msg, color = 'cyan') {
24
+ const step = new Step('say', 'say');
25
+ step.status = 'passed';
26
+ return recordStep(step, [msg]).then(() => {
27
+ // this is backward compatibility as this event may be used somewhere
25
28
  event.emit(event.step.comment, msg);
26
29
  output.say(msg, `${color}`);
27
30
  });
package/lib/ai.js CHANGED
@@ -16,6 +16,8 @@ const htmlConfig = {
16
16
  html: {},
17
17
  };
18
18
 
19
+ const aiInstance = null;
20
+
19
21
  class AiAssistant {
20
22
  constructor() {
21
23
  this.config = config.get('ai', defaultConfig);
@@ -26,7 +28,10 @@ class AiAssistant {
26
28
 
27
29
  this.isEnabled = !!process.env.OPENAI_API_KEY;
28
30
 
29
- if (!this.isEnabled) return;
31
+ if (!this.isEnabled) {
32
+ debug('No OpenAI API key provided. AI assistant is disabled.');
33
+ return;
34
+ }
30
35
 
31
36
  const configuration = new Configuration({
32
37
  apiKey: process.env.OPENAI_API_KEY,
@@ -35,13 +40,17 @@ class AiAssistant {
35
40
  this.openai = new OpenAIApi(configuration);
36
41
  }
37
42
 
38
- setHtmlContext(html) {
43
+ static getInstance() {
44
+ return aiInstance || new AiAssistant();
45
+ }
46
+
47
+ async setHtmlContext(html) {
39
48
  let processedHTML = html;
40
49
 
41
50
  if (this.htmlConfig.simplify) {
42
51
  processedHTML = removeNonInteractiveElements(processedHTML, this.htmlConfig);
43
52
  }
44
- if (this.htmlConfig.minify) processedHTML = minifyHtml(processedHTML);
53
+ if (this.htmlConfig.minify) processedHTML = await minifyHtml(processedHTML);
45
54
  if (this.htmlConfig.maxLength) processedHTML = splitByChunks(processedHTML, this.htmlConfig.maxLength)[0];
46
55
 
47
56
  debug(processedHTML);
package/lib/cli.js CHANGED
@@ -81,6 +81,11 @@ class Cli extends Base {
81
81
  if (!codeceptjsEventDispatchersRegistered) {
82
82
  codeceptjsEventDispatchersRegistered = true;
83
83
 
84
+ event.dispatcher.on(event.bddStep.started, (step) => {
85
+ output.stepShift = 2;
86
+ output.step(step);
87
+ });
88
+
84
89
  event.dispatcher.on(event.step.started, (step) => {
85
90
  let processingStep = step;
86
91
  const metaSteps = [];
@@ -93,12 +98,17 @@ class Cli extends Base {
93
98
  for (let i = 0; i < Math.max(currentMetaStep.length, metaSteps.length); i++) {
94
99
  if (currentMetaStep[i] !== metaSteps[i]) {
95
100
  output.stepShift = 3 + 2 * i;
96
- if (metaSteps[i]) output.step(metaSteps[i]);
101
+ if (!metaSteps[i]) continue;
102
+ // bdd steps are handled by bddStep.started
103
+ if (metaSteps[i].isBDD()) continue;
104
+ output.step(metaSteps[i]);
97
105
  }
98
106
  }
99
107
  currentMetaStep = metaSteps;
100
108
  output.stepShift = 3 + 2 * shift;
101
- output.step(step);
109
+ if (step.helper.constructor.name !== 'ExpectHelper') {
110
+ output.step(step);
111
+ }
102
112
  });
103
113
 
104
114
  event.dispatcher.on(event.step.finished, () => {
package/lib/codecept.js CHANGED
@@ -89,6 +89,9 @@ class Codecept {
89
89
  global.When = stepDefinitions.When;
90
90
  global.Then = stepDefinitions.Then;
91
91
  global.DefineParameterType = stepDefinitions.defineParameterType;
92
+
93
+ // debug mode
94
+ global.debugMode = false;
92
95
  }
93
96
  }
94
97
 
@@ -176,6 +179,7 @@ class Codecept {
176
179
  * Run a specific test or all loaded tests.
177
180
  *
178
181
  * @param {string} [test]
182
+ * @returns {Promise<void>}
179
183
  */
180
184
  async run(test) {
181
185
  return new Promise((resolve, reject) => {
package/lib/colorUtils.js CHANGED
@@ -226,15 +226,25 @@ function isColorProperty(prop) {
226
226
  'color',
227
227
  'background',
228
228
  'backgroundColor',
229
+ 'background-color',
229
230
  'borderColor',
231
+ 'border-color',
230
232
  'borderBottomColor',
233
+ 'border-bottom-color',
231
234
  'borderLeftColor',
235
+ 'border-left-color',
232
236
  'borderRightColor',
233
237
  'borderTopColor',
234
238
  'caretColor',
235
239
  'columnRuleColor',
236
240
  'outlineColor',
237
241
  'textDecorationColor',
242
+ 'border-right-color',
243
+ 'border-top-color',
244
+ 'caret-color',
245
+ 'column-rule-color',
246
+ 'outline-color',
247
+ 'text-decoration-color',
238
248
  ].indexOf(prop) > -1;
239
249
  }
240
250
 
@@ -17,7 +17,6 @@ const actingHelpers = [...require('../plugin/standardActingHelpers'), 'REST'];
17
17
  * @param {Map} params.supportObject
18
18
  * @param {Array<string>} params.helperNames
19
19
  * @param {Array<string>} params.importPaths
20
- * @param {Array<string>} params.customHelpers
21
20
  * @param params.translations
22
21
  *
23
22
  * @returns {string}
@@ -29,15 +28,13 @@ const getDefinitionsFileContent = ({
29
28
  supportObject,
30
29
  importPaths,
31
30
  translations,
32
- customHelpers,
33
31
  }) => {
34
32
  const getHelperListFragment = ({
35
33
  hasCustomHelper,
36
34
  hasCustomStepsFile,
37
- customHelpers,
38
35
  }) => {
39
36
  if (hasCustomHelper && hasCustomStepsFile) {
40
- return `${['ReturnType<steps_file>', ...customHelpers].join(', ')}`;
37
+ return `${['ReturnType<steps_file>', 'WithTranslation<Methods>'].join(', ')}`;
41
38
  }
42
39
 
43
40
  if (hasCustomStepsFile) {
@@ -50,7 +47,6 @@ const getDefinitionsFileContent = ({
50
47
  const helpersListFragment = getHelperListFragment({
51
48
  hasCustomHelper,
52
49
  hasCustomStepsFile,
53
- customHelpers,
54
50
  });
55
51
 
56
52
  const importPathsFragment = importPaths.join('\n');
@@ -143,7 +139,7 @@ module.exports = function (genPath, options) {
143
139
  }
144
140
 
145
141
  if (!actingHelpers.includes(name)) {
146
- customHelpers.push(`WithTranslation<${name}>`);
142
+ customHelpers.push(name);
147
143
  }
148
144
  }
149
145
 
@@ -186,7 +182,6 @@ module.exports = function (genPath, options) {
186
182
  translations,
187
183
  hasCustomStepsFile,
188
184
  hasCustomHelper,
189
- customHelpers,
190
185
  });
191
186
 
192
187
  // add aliases for translations
@@ -20,7 +20,8 @@ module.exports = async function (test, options) {
20
20
  if (config.plugins) {
21
21
  // disable all plugins by default, they can be enabled with -p option
22
22
  for (const plugin in config.plugins) {
23
- config.plugins[plugin].enabled = false;
23
+ // if `-p all` is passed, then enabling all plugins, otherwise plugins could be enabled by `-p customLocator,commentStep,tryTo`
24
+ config.plugins[plugin].enabled = options.plugins === 'all';
24
25
  }
25
26
  }
26
27
 
@@ -37,3 +37,27 @@ module.exports = async function (path) {
37
37
  output.print('Please copy environment info when you report issues on GitHub: https://github.com/Codeception/CodeceptJS/issues');
38
38
  output.print('***************************************');
39
39
  };
40
+
41
+ module.exports.getMachineInfo = async () => {
42
+ const info = {
43
+ nodeInfo: await envinfo.helpers.getNodeInfo(),
44
+ osInfo: await envinfo.helpers.getOSInfo(),
45
+ cpuInfo: await envinfo.helpers.getCPUInfo(),
46
+ chromeInfo: await envinfo.helpers.getChromeInfo(),
47
+ edgeInfo: await envinfo.helpers.getEdgeInfo(),
48
+ firefoxInfo: await envinfo.helpers.getFirefoxInfo(),
49
+ safariInfo: await envinfo.helpers.getSafariInfo(),
50
+ };
51
+
52
+ output.print('***************************************');
53
+ for (const [key, value] of Object.entries(info)) {
54
+ if (Array.isArray(value)) {
55
+ output.print(`${key}: ${value[1]}`);
56
+ } else {
57
+ output.print(`${key}: ${JSON.stringify(value, null, ' ')}`);
58
+ }
59
+ }
60
+ output.print('If you need more detailed info, just run this: npx codeceptjs info');
61
+ output.print('***************************************');
62
+ return info;
63
+ };
@@ -20,7 +20,7 @@ const defaultConfig = {
20
20
  include: {},
21
21
  };
22
22
 
23
- const helpers = ['Playwright', 'WebDriver', 'Puppeteer', 'REST', 'GraphQL', 'Appium', 'TestCafe', 'Nightmare'];
23
+ const helpers = ['Playwright', 'WebDriver', 'Puppeteer', 'REST', 'GraphQL', 'Appium', 'TestCafe'];
24
24
  const translations = Object.keys(require('../../translations'));
25
25
 
26
26
  const noTranslation = 'English (no localization)';
@@ -201,7 +201,13 @@ module.exports = function (initPath) {
201
201
  // no extra step file for typescript (as it doesn't match TS conventions)
202
202
  const stepFile = `./steps_file.${extension}`;
203
203
  fs.writeFileSync(path.join(testsPath, stepFile), extension === 'ts' ? defaultActorTs : defaultActor);
204
- config.include.I = isTypeScript === true ? './steps_file' : stepFile;
204
+
205
+ if (isTypeScript) {
206
+ config.include = _actorTranslation('./steps_file', config.translation);
207
+ } else {
208
+ config.include = _actorTranslation(stepFile, config.translation);
209
+ }
210
+
205
211
  print(`Steps file created at ${stepFile}`);
206
212
 
207
213
  let configSource;
@@ -316,7 +322,17 @@ module.exports = function (initPath) {
316
322
  };
317
323
 
318
324
  print('Configure helpers...');
319
- inquirer.prompt(helperConfigs).then((helperResult) => {
325
+ inquirer.prompt(helperConfigs).then(async (helperResult) => {
326
+ if (helperResult.Playwright_browser === 'electron') {
327
+ delete helperResult.Playwright_url;
328
+ delete helperResult.Playwright_show;
329
+
330
+ helperResult.Playwright_electron = {
331
+ executablePath: '// require("electron") or require("electron-forge")',
332
+ args: ['path/to/your/main.js'],
333
+ };
334
+ }
335
+
320
336
  Object.keys(helperResult).forEach((key) => {
321
337
  const parts = key.split('_');
322
338
  const helperName = parts[0];
@@ -326,12 +342,12 @@ module.exports = function (initPath) {
326
342
  });
327
343
 
328
344
  print('');
329
- finish();
345
+ await finish();
330
346
  });
331
347
  });
332
348
  };
333
349
 
334
- function install(dependencies, verbose) {
350
+ function install(dependencies) {
335
351
  let command;
336
352
  let args;
337
353
 
@@ -364,9 +380,39 @@ function install(dependencies, verbose) {
364
380
  ].concat(dependencies);
365
381
  }
366
382
 
383
+ if (process.env._INIT_DRY_RUN_INSTALL) {
384
+ args.push('--dry-run');
385
+ }
386
+
367
387
  const { status } = spawn.sync(command, args, { stdio: 'inherit' });
368
388
  if (status !== 0) {
369
389
  throw new Error(`${command} ${args.join(' ')} failed`);
370
390
  }
371
391
  return true;
372
392
  }
393
+
394
+ function _actorTranslation(stepFile, translationSelected) {
395
+ let actor;
396
+
397
+ for (const translationAvailable of translations) {
398
+ if (actor) {
399
+ break;
400
+ }
401
+
402
+ if (translationSelected === translationAvailable) {
403
+ const nameOfActor = require('../../translations')[translationAvailable].I;
404
+
405
+ actor = {
406
+ [nameOfActor]: stepFile,
407
+ };
408
+ }
409
+ }
410
+
411
+ if (!actor) {
412
+ actor = {
413
+ I: stepFile,
414
+ };
415
+ }
416
+
417
+ return actor;
418
+ }
@@ -47,14 +47,26 @@ class Collection {
47
47
  */
48
48
  prepareRuns(selectedRuns, config) {
49
49
  selectedRuns.forEach((selectedRun) => {
50
- const [runName] = selectedRun.split(':');
51
- const runConfig = config.multiple[runName];
50
+ const runConfig = [];
51
+ const runName = [];
52
52
 
53
- if (!runConfig) {
54
- throw new Error(`run ${runName} was not configured in "multiple" section of config`);
53
+ if (selectedRun === 'all') {
54
+ Object.keys(config.multiple).forEach(name => {
55
+ runName.push(name);
56
+ runConfig.push(config.multiple[name]);
57
+ });
58
+ } else {
59
+ runName.push(selectedRun.split(':')[0]);
60
+ runConfig.push(config.multiple[runName[0]]);
55
61
  }
56
62
 
57
- this.addRun(createRun(runName, runConfig));
63
+ for (let i = 0; i < runConfig.length; i++) {
64
+ if (!runConfig[i]) {
65
+ throw new Error(`run ${runName[i]} was not configured in "multiple" section of config`);
66
+ }
67
+
68
+ this.addRun(createRun(runName[i], runConfig[i]));
69
+ }
58
70
  });
59
71
 
60
72
  return this;
@@ -28,7 +28,9 @@ let processesDone;
28
28
 
29
29
  module.exports = async function (selectedRuns, options) {
30
30
  // registering options globally to use in config
31
- process.env.profile = options.profile;
31
+ if (options.profile) {
32
+ process.env.profile = options.profile;
33
+ }
32
34
  const configFile = options.config;
33
35
 
34
36
  const testRoot = getTestRoot(configFile);
@@ -124,7 +126,7 @@ function executeRun(runName, runConfig) {
124
126
  if (browserConfig.outputName) {
125
127
  outputDir += typeof browserConfig.outputName === 'function' ? browserConfig.outputName() : browserConfig.outputName;
126
128
  } else {
127
- const hash = crypto.createHash('md5');
129
+ const hash = crypto.createHash('sha256');
128
130
  hash.update(JSON.stringify(browserConfig));
129
131
  outputDir += hash.digest('hex');
130
132
  }
@@ -7,6 +7,12 @@ const Workers = require('../workers');
7
7
  module.exports = async function (workerCount, selectedRuns, options) {
8
8
  process.env.profile = options.profile;
9
9
 
10
+ const suiteArr = [];
11
+ const passedTestArr = [];
12
+ const failedTestArr = [];
13
+ const skippedTestArr = [];
14
+ const stepArr = [];
15
+
10
16
  const { config: testConfig, override = '' } = options;
11
17
  const overrideConfigs = tryOrDefault(() => JSON.parse(override), {});
12
18
  const by = options.suites ? 'suite' : 'test';
@@ -26,19 +32,75 @@ module.exports = async function (workerCount, selectedRuns, options) {
26
32
 
27
33
  const workers = new Workers(numberOfWorkers, config);
28
34
  workers.overrideConfig(overrideConfigs);
29
- workers.on(event.test.failed, (failedTest) => {
30
- output.test.failed(failedTest);
35
+
36
+ workers.on(event.suite.before, (suite) => {
37
+ suiteArr.push(suite);
38
+ });
39
+
40
+ workers.on(event.step.passed, (step) => {
41
+ stepArr.push(step);
31
42
  });
32
43
 
33
- workers.on(event.test.passed, (successTest) => {
34
- output.test.passed(successTest);
44
+ workers.on(event.step.failed, (step) => {
45
+ stepArr.push(step);
46
+ });
47
+
48
+ workers.on(event.test.failed, (test) => {
49
+ failedTestArr.push(test);
50
+ output.test.failed(test);
51
+ });
52
+
53
+ workers.on(event.test.passed, (test) => {
54
+ passedTestArr.push(test);
55
+ output.test.passed(test);
56
+ });
57
+
58
+ workers.on(event.test.skipped, (test) => {
59
+ skippedTestArr.push(test);
60
+ output.test.skipped(test);
35
61
  });
36
62
 
37
63
  workers.on(event.all.result, () => {
64
+ // expose test stats after all workers finished their execution
65
+ function addStepsToTest(test, stepArr) {
66
+ stepArr.test.steps.forEach(step => {
67
+ if (test.steps.length === 0) {
68
+ test.steps.push(step);
69
+ }
70
+ });
71
+ }
72
+
73
+ stepArr.forEach(step => {
74
+ passedTestArr.forEach(test => {
75
+ if (step.test.title === test.title) {
76
+ addStepsToTest(test, step);
77
+ }
78
+ });
79
+
80
+ failedTestArr.forEach(test => {
81
+ if (step.test.title === test.title) {
82
+ addStepsToTest(test, step);
83
+ }
84
+ });
85
+ });
86
+
87
+ event.dispatcher.emit(event.workers.result, {
88
+ suites: suiteArr,
89
+ tests: {
90
+ passed: passedTestArr,
91
+ failed: failedTestArr,
92
+ skipped: skippedTestArr,
93
+ },
94
+ });
38
95
  workers.printResults();
39
96
  });
40
97
 
41
98
  try {
99
+ if (options.verbose) {
100
+ global.debugMode = true;
101
+ const { getMachineInfo } = require('./info');
102
+ await getMachineInfo();
103
+ }
42
104
  await workers.bootstrapAll();
43
105
  await workers.run();
44
106
  } catch (err) {
@@ -28,6 +28,13 @@ module.exports = async function (test, options) {
28
28
  codecept.init(testRoot);
29
29
  await codecept.bootstrap();
30
30
  codecept.loadTests(test);
31
+
32
+ if (options.verbose) {
33
+ global.debugMode = true;
34
+ const { getMachineInfo } = require('./info');
35
+ await getMachineInfo();
36
+ }
37
+
31
38
  await codecept.run();
32
39
  } catch (err) {
33
40
  printError(err);