codeceptjs 3.6.10 → 3.7.0-beta.1

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 (105) hide show
  1. package/README.md +81 -110
  2. package/bin/codecept.js +2 -2
  3. package/docs/webapi/clearCookie.mustache +1 -1
  4. package/lib/actor.js +46 -36
  5. package/lib/assert/empty.js +3 -5
  6. package/lib/assert/equal.js +4 -7
  7. package/lib/assert/include.js +4 -6
  8. package/lib/assert/throws.js +2 -4
  9. package/lib/assert/truth.js +2 -2
  10. package/lib/codecept.js +87 -83
  11. package/lib/command/configMigrate.js +2 -4
  12. package/lib/command/definitions.js +5 -25
  13. package/lib/command/generate.js +10 -14
  14. package/lib/command/gherkin/snippets.js +10 -8
  15. package/lib/command/gherkin/steps.js +1 -1
  16. package/lib/command/info.js +1 -3
  17. package/lib/command/init.js +8 -12
  18. package/lib/command/interactive.js +1 -1
  19. package/lib/command/list.js +1 -1
  20. package/lib/command/run-multiple.js +12 -35
  21. package/lib/command/run-workers.js +10 -10
  22. package/lib/command/utils.js +5 -6
  23. package/lib/command/workers/runTests.js +14 -17
  24. package/lib/container.js +327 -237
  25. package/lib/data/context.js +10 -13
  26. package/lib/data/dataScenarioConfig.js +8 -8
  27. package/lib/data/dataTableArgument.js +6 -6
  28. package/lib/data/table.js +5 -11
  29. package/lib/els.js +177 -0
  30. package/lib/event.js +1 -0
  31. package/lib/heal.js +78 -80
  32. package/lib/helper/ApiDataFactory.js +3 -6
  33. package/lib/helper/Appium.js +15 -30
  34. package/lib/helper/FileSystem.js +3 -3
  35. package/lib/helper/GraphQLDataFactory.js +3 -3
  36. package/lib/helper/JSONResponse.js +57 -37
  37. package/lib/helper/Nightmare.js +35 -53
  38. package/lib/helper/Playwright.js +189 -251
  39. package/lib/helper/Protractor.js +54 -77
  40. package/lib/helper/Puppeteer.js +134 -232
  41. package/lib/helper/REST.js +5 -17
  42. package/lib/helper/TestCafe.js +21 -44
  43. package/lib/helper/WebDriver.js +103 -162
  44. package/lib/helper/testcafe/testcafe-utils.js +26 -27
  45. package/lib/listener/artifacts.js +2 -2
  46. package/lib/listener/emptyRun.js +58 -0
  47. package/lib/listener/exit.js +4 -4
  48. package/lib/listener/{retry.js → globalRetry.js} +5 -5
  49. package/lib/listener/{timeout.js → globalTimeout.js} +8 -8
  50. package/lib/listener/helpers.js +15 -15
  51. package/lib/listener/mocha.js +1 -1
  52. package/lib/listener/steps.js +17 -12
  53. package/lib/listener/store.js +12 -0
  54. package/lib/mocha/asyncWrapper.js +204 -0
  55. package/lib/{interfaces → mocha}/bdd.js +3 -3
  56. package/lib/mocha/cli.js +257 -0
  57. package/lib/mocha/factory.js +104 -0
  58. package/lib/{interfaces → mocha}/featureConfig.js +11 -12
  59. package/lib/{interfaces → mocha}/gherkin.js +26 -28
  60. package/lib/mocha/hooks.js +83 -0
  61. package/lib/mocha/index.js +12 -0
  62. package/lib/mocha/inject.js +24 -0
  63. package/lib/{interfaces → mocha}/scenarioConfig.js +10 -6
  64. package/lib/mocha/suite.js +55 -0
  65. package/lib/mocha/test.js +60 -0
  66. package/lib/mocha/types.d.ts +31 -0
  67. package/lib/mocha/ui.js +219 -0
  68. package/lib/output.js +28 -10
  69. package/lib/pause.js +159 -135
  70. package/lib/plugin/autoDelay.js +4 -4
  71. package/lib/plugin/autoLogin.js +6 -7
  72. package/lib/plugin/commentStep.js +1 -1
  73. package/lib/plugin/coverage.js +10 -19
  74. package/lib/plugin/customLocator.js +3 -3
  75. package/lib/plugin/debugErrors.js +2 -2
  76. package/lib/plugin/eachElement.js +1 -1
  77. package/lib/plugin/fakerTransform.js +1 -1
  78. package/lib/plugin/heal.js +6 -9
  79. package/lib/plugin/retryFailedStep.js +4 -4
  80. package/lib/plugin/retryTo.js +2 -2
  81. package/lib/plugin/screenshotOnFail.js +9 -36
  82. package/lib/plugin/selenoid.js +15 -35
  83. package/lib/plugin/stepByStepReport.js +51 -13
  84. package/lib/plugin/stepTimeout.js +4 -11
  85. package/lib/plugin/subtitles.js +4 -4
  86. package/lib/plugin/tryTo.js +1 -1
  87. package/lib/plugin/wdio.js +8 -10
  88. package/lib/recorder.js +142 -121
  89. package/lib/secret.js +1 -1
  90. package/lib/step.js +160 -144
  91. package/lib/store.js +6 -2
  92. package/lib/template/heal.js +2 -11
  93. package/lib/utils.js +224 -216
  94. package/lib/within.js +73 -55
  95. package/lib/workers.js +265 -261
  96. package/package.json +46 -47
  97. package/typings/index.d.ts +172 -184
  98. package/typings/promiseBasedTypes.d.ts +53 -516
  99. package/typings/types.d.ts +127 -587
  100. package/lib/cli.js +0 -256
  101. package/lib/helper/ExpectHelper.js +0 -391
  102. package/lib/helper/SoftExpectHelper.js +0 -381
  103. package/lib/mochaFactory.js +0 -113
  104. package/lib/scenario.js +0 -224
  105. package/lib/ui.js +0 -236
package/README.md CHANGED
@@ -1,9 +1,7 @@
1
1
  [![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/banner-direct-single.svg)](https://stand-with-ukraine.pp.ua)
2
2
 
3
-
4
-
5
3
  [<img src="https://img.shields.io/badge/slack-@codeceptjs-purple.svg?logo=slack">](https://join.slack.com/t/codeceptjs/shared_invite/enQtMzA5OTM4NDM2MzA4LWE4MThhN2NmYTgxNTU5MTc4YzAyYWMwY2JkMmZlYWI5MWQ2MDM5MmRmYzZmYmNiNmY5NTAzM2EwMGIwOTNhOGQ) [<img src="https://img.shields.io/badge/discourse-codeceptjs-purple">](https://codecept.discourse.group) [![NPM version][npm-image]][npm-url] [<img src="https://img.shields.io/badge/dockerhub-images-blue.svg?logo=codeceptjs">](https://hub.docker.com/r/codeceptjs/codeceptjs)
6
- [![AI features](https://img.shields.io/badge/AI-features?logo=openai&logoColor=white)](https://github.com/codeceptjs/CodeceptJS/edit/3.x/docs/ai.md) [![StandWithUkraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md)
4
+ [![AI features](https://img.shields.io/badge/AI-features?logo=openai&logoColor=white)](https://github.com/codeceptjs/CodeceptJS/edit/3.x/docs/ai.md) [![StandWithUkraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://github.com/vshymanskyy/StandWithUkraine/blob/main/docs/README.md)
7
5
 
8
6
  Build Status:
9
7
 
@@ -27,28 +25,28 @@ It abstracts browser interaction to simple steps that are written from a user's
27
25
  A simple test that verifies the "Welcome" text is present on a main page of a site will look like:
28
26
 
29
27
  ```js
30
- Feature('CodeceptJS demo');
28
+ Feature('CodeceptJS demo')
31
29
 
32
30
  Scenario('check Welcome page on site', ({ I }) => {
33
- I.amOnPage('/');
34
- I.see('Welcome');
35
- });
31
+ I.amOnPage('/')
32
+ I.see('Welcome')
33
+ })
36
34
  ```
37
35
 
38
36
  CodeceptJS tests are:
39
37
 
40
- * **Synchronous**. You don't need to care about callbacks or promises or test scenarios which are linear. But, your tests should be linear.
41
- * 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.
42
- * Backend **API agnostic**. We don't know which WebDriver implementation is running this test.
38
+ - **Synchronous**. You don't need to care about callbacks or promises or test scenarios which are linear. But, your tests should be linear.
39
+ - 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.
40
+ - Backend **API agnostic**. We don't know which WebDriver implementation is running this test.
43
41
 
44
42
  CodeceptJS uses **Helper** modules to provide actions to `I` object. Currently, CodeceptJS has these helpers:
45
43
 
46
- * [**Playwright**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/Playwright.md) - is a Node library to automate the Chromium, WebKit and Firefox browsers with a single API.
47
- * [**Puppeteer**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/Puppeteer.md) - uses Google Chrome's Puppeteer for fast headless testing.
48
- * [**WebDriver**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/WebDriver.md) - uses [webdriverio](http://webdriver.io/) to run tests via WebDriver or Devtools protocol.
49
- * [**TestCafe**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/TestCafe.md) - cheap and fast cross-browser test automation.
50
- * [**Appium**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/Appium.md) - for **mobile testing** with Appium
51
- * [**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.
44
+ - [**Playwright**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/Playwright.md) - is a Node library to automate the Chromium, WebKit and Firefox browsers with a single API.
45
+ - [**Puppeteer**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/Puppeteer.md) - uses Google Chrome's Puppeteer for fast headless testing.
46
+ - [**WebDriver**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/WebDriver.md) - uses [webdriverio](http://webdriver.io/) to run tests via WebDriver or Devtools protocol.
47
+ - [**TestCafe**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/TestCafe.md) - cheap and fast cross-browser test automation.
48
+ - [**Appium**](https://github.com/codeceptjs/CodeceptJS/blob/master/docs/helpers/Appium.md) - for **mobile testing** with Appium
49
+ - [**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.
52
50
 
53
51
  And more to come...
54
52
 
@@ -58,17 +56,16 @@ CodeceptJS is a successor of [Codeception](http://codeception.com), a popular fu
58
56
  With CodeceptJS your scenario-driven functional and acceptance tests will be as simple and clean as they can be.
59
57
  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.
60
58
 
61
-
62
59
  ## Features
63
-
64
- * 🪄 **AI-powered** with GPT features to assist and heal failing tests.
65
- * ☕ Based on [Mocha](https://mochajs.org/) testing framework.
66
- * 💼 Designed for scenario driven acceptance testing in BDD-style.
67
- * 💻 Uses ES6 natively without transpiler.
68
- * Also plays nice with TypeScript.
69
- * </> Smart locators: use names, labels, matching text, CSS or XPath to locate elements.
70
- * 🌐 Interactive debugging shell: pause test at any point and try different commands in a browser.
71
- * Easily create tests, pageobjects, stepobjects with CLI generators.
60
+
61
+ - 🪄 **AI-powered** with GPT features to assist and heal failing tests.
62
+ - ☕ Based on [Mocha](https://mochajs.org/) testing framework.
63
+ - 💼 Designed for scenario driven acceptance testing in BDD-style.
64
+ - 💻 Uses ES6 natively without transpiler.
65
+ - Also plays nice with TypeScript.
66
+ - </> Smart locators: use names, labels, matching text, CSS or XPath to locate elements.
67
+ - 🌐 Interactive debugging shell: pause test at any point and try different commands in a browser.
68
+ - Easily create tests, pageobjects, stepobjects with CLI generators.
72
69
 
73
70
  ## Installation
74
71
 
@@ -105,7 +102,8 @@ npx codeceptjs def .
105
102
  Later you can even automagically update Type Definitions to include your own custom [helpers methods](docs/helpers.md).
106
103
 
107
104
  Note:
108
- - CodeceptJS requires Node.js version `12+` or later.
105
+
106
+ - CodeceptJS requires Node.js version `12+` or later.
109
107
 
110
108
  ## Usage
111
109
 
@@ -116,18 +114,18 @@ Learn CodeceptJS by examples. Let's assume we have CodeceptJS installed and WebD
116
114
  Let's see how we can handle basic form testing:
117
115
 
118
116
  ```js
119
- Feature('CodeceptJS Demonstration');
117
+ Feature('CodeceptJS Demonstration')
120
118
 
121
119
  Scenario('test some forms', ({ I }) => {
122
- I.amOnPage('http://simple-form-bootstrap.plataformatec.com.br/documentation');
123
- I.fillField('Email', 'hello@world.com');
124
- I.fillField('Password', secret('123456'));
125
- I.checkOption('Active');
126
- I.checkOption('Male');
127
- I.click('Create User');
128
- I.see('User is valid');
129
- I.dontSeeInCurrentUrl('/documentation');
130
- });
120
+ I.amOnPage('http://simple-form-bootstrap.plataformatec.com.br/documentation')
121
+ I.fillField('Email', 'hello@world.com')
122
+ I.fillField('Password', secret('123456'))
123
+ I.checkOption('Active')
124
+ I.checkOption('Male')
125
+ I.click('Create User')
126
+ I.see('User is valid')
127
+ I.dontSeeInCurrentUrl('/documentation')
128
+ })
131
129
  ```
132
130
 
133
131
  All actions are performed by `I` object; assertions functions start with `see` function.
@@ -172,11 +170,11 @@ The same way you can locate element by name, `CSS` or `XPath` locators in tests:
172
170
 
173
171
  ```js
174
172
  // by name
175
- I.fillField('user_basic[email]', 'hello@world.com');
173
+ I.fillField('user_basic[email]', 'hello@world.com')
176
174
  // by CSS
177
- I.fillField('#user_basic_email', 'hello@world.com');
175
+ I.fillField('#user_basic_email', 'hello@world.com')
178
176
  // don't make us guess locator type, specify it
179
- I.fillField({css: '#user_basic_email'}, 'hello@world.com');
177
+ I.fillField({ css: '#user_basic_email' }, 'hello@world.com')
180
178
  ```
181
179
 
182
180
  Other methods like `checkOption`, and `click` work in a similar manner. They can take labels or CSS or XPath locators to find elements to interact.
@@ -187,9 +185,9 @@ Assertions start with `see` or `dontSee` prefix. In our case we are asserting th
187
185
  However, we can narrow the search to particular element by providing a second parameter:
188
186
 
189
187
  ```js
190
- I.see('User is valid');
188
+ I.see('User is valid')
191
189
  // better to specify context:
192
- I.see('User is valid', '.alert-success');
190
+ I.see('User is valid', '.alert-success')
193
191
  ```
194
192
 
195
193
  In this case 'User is valid' string will be searched only inside elements located by CSS `.alert-success`.
@@ -200,13 +198,13 @@ In case you need to return a value from a webpage and use it directly in test, y
200
198
  They are expected to be used inside `async/await` functions, and their results will be available in test:
201
199
 
202
200
  ```js
203
- Feature('CodeceptJS Demonstration');
201
+ Feature('CodeceptJS Demonstration')
204
202
 
205
203
  Scenario('test page title', async ({ I }) => {
206
- I.amOnPage('http://simple-form-bootstrap.plataformatec.com.br/documentation');
207
- const title = await I.grabTitle();
208
- I.expectEqual(title, 'Example application with SimpleForm and Twitter Bootstrap'); // Avaiable with Expect helper. -> https://codecept.io/helpers/Expect/
209
- });
204
+ I.amOnPage('http://simple-form-bootstrap.plataformatec.com.br/documentation')
205
+ const title = await I.grabTitle()
206
+ I.expectEqual(title, 'Example application with SimpleForm and Twitter Bootstrap') // Avaiable with Expect helper. -> https://codecept.io/helpers/Expect/
207
+ })
210
208
  ```
211
209
 
212
210
  The same way you can grab text, attributes, or form values and use them in next test steps.
@@ -216,23 +214,24 @@ The same way you can grab text, attributes, or form values and use them in next
216
214
  Common preparation steps like opening a web page, logging in a user, can be placed in `Before` or `Background`:
217
215
 
218
216
  ```js
219
- const { I } = inject();
217
+ const { I } = inject()
220
218
 
221
- Feature('CodeceptJS Demonstration');
219
+ Feature('CodeceptJS Demonstration')
222
220
 
223
- Before(() => { // or Background
224
- I.amOnPage('http://simple-form-bootstrap.plataformatec.com.br/documentation');
225
- });
221
+ Before(() => {
222
+ // or Background
223
+ I.amOnPage('http://simple-form-bootstrap.plataformatec.com.br/documentation')
224
+ })
226
225
 
227
226
  Scenario('test some forms', () => {
228
- I.click('Create User');
229
- I.see('User is valid');
230
- I.dontSeeInCurrentUrl('/documentation');
231
- });
227
+ I.click('Create User')
228
+ I.see('User is valid')
229
+ I.dontSeeInCurrentUrl('/documentation')
230
+ })
232
231
 
233
232
  Scenario('test title', () => {
234
- I.seeInTitle('Example application');
235
- });
233
+ I.seeInTitle('Example application')
234
+ })
236
235
  ```
237
236
 
238
237
  ## PageObjects
@@ -248,83 +247,55 @@ It will create a page object file for you and add it to the config.
248
247
  Let's assume we created one named `docsPage`:
249
248
 
250
249
  ```js
251
- const { I } = inject();
250
+ const { I } = inject()
252
251
 
253
252
  module.exports = {
254
253
  fields: {
255
254
  email: '#user_basic_email',
256
- password: '#user_basic_password'
255
+ password: '#user_basic_password',
257
256
  },
258
- submitButton: {css: '#new_user_basic input[type=submit]'},
257
+ submitButton: { css: '#new_user_basic input[type=submit]' },
259
258
 
260
259
  sendForm(email, password) {
261
- I.fillField(this.fields.email, email);
262
- I.fillField(this.fields.password, password);
263
- I.click(this.submitButton);
264
- }
260
+ I.fillField(this.fields.email, email)
261
+ I.fillField(this.fields.password, password)
262
+ I.click(this.submitButton)
263
+ },
265
264
  }
266
265
  ```
267
266
 
268
267
  You can easily inject it to test by providing its name in test arguments:
269
268
 
270
269
  ```js
271
- Feature('CodeceptJS Demonstration');
270
+ Feature('CodeceptJS Demonstration')
272
271
 
273
- Before(({ I }) => { // or Background
274
- I.amOnPage('http://simple-form-bootstrap.plataformatec.com.br/documentation');
275
- });
272
+ Before(({ I }) => {
273
+ // or Background
274
+ I.amOnPage('http://simple-form-bootstrap.plataformatec.com.br/documentation')
275
+ })
276
276
 
277
277
  Scenario('test some forms', ({ I, docsPage }) => {
278
- docsPage.sendForm('hello@world.com','123456');
279
- I.see('User is valid');
280
- I.dontSeeInCurrentUrl('/documentation');
281
- });
278
+ docsPage.sendForm('hello@world.com', '123456')
279
+ I.see('User is valid')
280
+ I.dontSeeInCurrentUrl('/documentation')
281
+ })
282
282
  ```
283
283
 
284
284
  When using Typescript, replace `module.exports` with `export` for autocompletion.
285
285
 
286
-
287
286
  ## Contributing
288
287
 
289
- - ### [Contributing Guide](https://github.com/codeceptjs/CodeceptJS/blob/master/.github/CONTRIBUTING.md)
290
- - ### [Code of conduct](https://github.com/codeceptjs/CodeceptJS/blob/master/.github/CODE_OF_CONDUCT.md)
291
-
288
+ - ### [Contributing Guide](https://github.com/codeceptjs/CodeceptJS/blob/master/.github/CONTRIBUTING.md)
289
+ - ### [Code of conduct](https://github.com/codeceptjs/CodeceptJS/blob/master/.github/CODE_OF_CONDUCT.md)
292
290
 
293
291
  ## Contributors
294
292
 
295
- Thanks all to those who are and will have contributing to this awesome project!
296
-
297
- [//]: contributor-faces
298
- <a href="https://github.com/DavertMik"><img src="https://avatars.githubusercontent.com/u/220264?v=4" title="DavertMik" width="80" height="80"></a>
299
- <a href="https://github.com/kobenguyent"><img src="https://avatars.githubusercontent.com/u/7845001?v=4" title="kobenguyent" width="80" height="80"></a>
300
- <a href="https://github.com/Vorobeyko"><img src="https://avatars.githubusercontent.com/u/11293201?v=4" title="Vorobeyko" width="80" height="80"></a>
301
- <a href="https://github.com/reubenmiller"><img src="https://avatars.githubusercontent.com/u/3029781?v=4" title="reubenmiller" width="80" height="80"></a>
302
- <a href="https://github.com/Arhell"><img src="https://avatars.githubusercontent.com/u/26163841?v=4" title="Arhell" width="80" height="80"></a>
303
- <a href="https://github.com/APshenkin"><img src="https://avatars.githubusercontent.com/u/14344430?v=4" title="APshenkin" width="80" height="80"></a>
304
- <a href="https://github.com/fabioel"><img src="https://avatars.githubusercontent.com/u/9824235?v=4" title="fabioel" width="80" height="80"></a>
305
- <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/mirao"><img src="https://avatars.githubusercontent.com/u/12584138?v=4" title="mirao" width="80" height="80"></a>
307
- <a href="https://github.com/Georgegriff"><img src="https://avatars.githubusercontent.com/u/9056958?v=4" title="Georgegriff" width="80" height="80"></a>
308
- <a href="https://github.com/KMKoushik"><img src="https://avatars.githubusercontent.com/u/24666922?v=4" title="KMKoushik" width="80" height="80"></a>
309
- <a href="https://github.com/nikocanvacom"><img src="https://avatars.githubusercontent.com/u/83254493?v=4" title="nikocanvacom" width="80" height="80"></a>
310
- <a href="https://github.com/elukoyanov"><img src="https://avatars.githubusercontent.com/u/11647141?v=4" title="elukoyanov" width="80" height="80"></a>
311
- <a href="https://github.com/gkushang"><img src="https://avatars.githubusercontent.com/u/3663389?v=4" title="gkushang" width="80" height="80"></a>
312
- <a href="https://github.com/tsuemura"><img src="https://avatars.githubusercontent.com/u/17092259?v=4" title="tsuemura" width="80" height="80"></a>
313
- <a href="https://github.com/EgorBodnar"><img src="https://avatars.githubusercontent.com/u/63167966?v=4" title="EgorBodnar" width="80" height="80"></a>
314
- <a href="https://github.com/VikalpP"><img src="https://avatars.githubusercontent.com/u/11846339?v=4" title="VikalpP" width="80" height="80"></a>
315
- <a href="https://github.com/thomashohn"><img src="https://avatars.githubusercontent.com/u/3414869?v=4" title="thomashohn" width="80" height="80"></a>
316
- <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/BorisOsipov"><img src="https://avatars.githubusercontent.com/u/6514276?v=4" title="BorisOsipov" width="80" height="80"></a>
318
- <a href="https://github.com/ngraf"><img src="https://avatars.githubusercontent.com/u/7094389?v=4" title="ngraf" width="80" height="80"></a>
319
- <a href="https://github.com/nitschSB"><img src="https://avatars.githubusercontent.com/u/39341455?v=4" title="nitschSB" width="80" height="80"></a>
320
- <a href="https://github.com/hubidu"><img src="https://avatars.githubusercontent.com/u/13134082?v=4" title="hubidu" width="80" height="80"></a>
321
- <a href="https://github.com/jploskonka"><img src="https://avatars.githubusercontent.com/u/669483?v=4" title="jploskonka" width="80" height="80"></a>
322
- <a href="https://github.com/maojunxyz"><img src="https://avatars.githubusercontent.com/u/28778042?v=4" title="maojunxyz" width="80" height="80"></a>
323
- <a href="https://github.com/abhimanyupandian"><img src="https://avatars.githubusercontent.com/u/36107381?v=4" title="abhimanyupandian" width="80" height="80"></a>
324
- <a href="https://github.com/martomo"><img src="https://avatars.githubusercontent.com/u/1850135?v=4" title="martomo" 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
-
327
- [//]: contributor-faces
293
+ Thanks to our awesome contributors! 🎉
294
+ <a href="https://github.com/codeceptjs/codeceptjs/graphs/contributors">
295
+ <img src="https://contrib.rocks/image?repo=codeceptjs/codeceptjs" />
296
+ </a>
297
+
298
+ Made with [contrib.rocks](https://contrib.rocks).
328
299
 
329
300
  ## License
330
301
 
package/bin/codecept.js CHANGED
@@ -32,7 +32,7 @@ const commandFlags = {
32
32
  }
33
33
 
34
34
  const errorHandler =
35
- (fn) =>
35
+ fn =>
36
36
  async (...args) => {
37
37
  try {
38
38
  await fn(...args)
@@ -286,7 +286,7 @@ program
286
286
 
287
287
  .action(require('../lib/command/run-rerun'))
288
288
 
289
- program.on('command:*', (cmd) => {
289
+ program.on('command:*', cmd => {
290
290
  console.log(`\nUnknown command ${cmd}\n`)
291
291
  program.outputHelp()
292
292
  })
@@ -3,7 +3,7 @@ if none provided clears all cookies.
3
3
 
4
4
  ```js
5
5
  I.clearCookie();
6
- I.clearCookie('test'); // Playwright currently doesn't support clear a particular cookie name
6
+ I.clearCookie('test');
7
7
  ```
8
8
 
9
9
  @param {?string} [cookie=null] (optional, `null` by default) cookie name
package/lib/actor.js CHANGED
@@ -40,7 +40,7 @@ class Actor {
40
40
  limitTime(timeout) {
41
41
  if (!store.timeouts) return this;
42
42
 
43
- event.dispatcher.prependOnceListener(event.step.before, (step) => {
43
+ event.dispatcher.prependOnceListener(event.step.before, step => {
44
44
  output.log(`Timeout to ${step}: ${timeout}s`);
45
45
  step.setTimeout(timeout * 1000, Step.TIMEOUT_ORDER.codeLimitTime);
46
46
  });
@@ -70,37 +70,18 @@ class Actor {
70
70
  * @ignore
71
71
  */
72
72
  module.exports = function (obj = {}) {
73
- if (!store.actor) {
74
- store.actor = new Actor();
75
- }
76
- const actor = store.actor;
77
-
78
- const translation = container.translation();
79
-
80
- if (Object.keys(obj).length > 0) {
81
- Object.keys(obj)
82
- .forEach(action => {
83
- const actionAlias = translation.actionAliasFor(action);
84
-
85
- const currentMethod = obj[action];
86
- const ms = new MetaStep('I', action);
87
- if (translation.loaded) {
88
- ms.name = actionAlias;
89
- ms.actor = translation.I;
90
- }
91
- ms.setContext(actor);
92
- actor[action] = actor[actionAlias] = ms.run.bind(ms, currentMethod);
93
- });
94
- }
73
+ const actor = container.actor() || new Actor();
95
74
 
96
- const helpers = container.helpers();
75
+ // load all helpers once container initialized
76
+ container.started(() => {
77
+ const translation = container.translation();
78
+ const helpers = container.helpers();
97
79
 
98
- // add methods from enabled helpers
99
- Object.values(helpers)
100
- .forEach((helper) => {
80
+ // add methods from enabled helpers
81
+ Object.values(helpers).forEach(helper => {
101
82
  methodsOfObject(helper, 'Helper')
102
83
  .filter(method => method !== 'constructor' && method[0] !== '_')
103
- .forEach((action) => {
84
+ .forEach(action => {
104
85
  const actionAlias = translation.actionAliasFor(action);
105
86
  if (!actor[action]) {
106
87
  actor[action] = actor[actionAlias] = function () {
@@ -116,6 +97,28 @@ module.exports = function (obj = {}) {
116
97
  });
117
98
  });
118
99
 
100
+ // add translated custom steps from actor
101
+ Object.keys(obj).forEach(key => {
102
+ const actionAlias = translation.actionAliasFor(key);
103
+ if (!actor[actionAlias]) {
104
+ actor[actionAlias] = actor[key];
105
+ }
106
+ });
107
+
108
+ container.append({
109
+ support: {
110
+ I: actor,
111
+ },
112
+ });
113
+ });
114
+ // store.actor = actor;
115
+ // add custom steps from actor
116
+ Object.keys(obj).forEach(key => {
117
+ const ms = new MetaStep('I', key);
118
+ ms.setContext(actor);
119
+ actor[key] = ms.run.bind(ms, obj[key]);
120
+ });
121
+
119
122
  return actor;
120
123
  };
121
124
 
@@ -130,13 +133,20 @@ function recordStep(step, args) {
130
133
  let val;
131
134
 
132
135
  // run step inside promise
133
- recorder.add(task, () => {
134
- if (!step.startTime) { // step can be retries
135
- event.emit(event.step.started, step);
136
- step.startTime = Date.now();
137
- }
138
- return val = step.run(...args);
139
- }, false, undefined, step.getTimeout());
136
+ recorder.add(
137
+ task,
138
+ () => {
139
+ if (!step.startTime) {
140
+ // step can be retries
141
+ event.emit(event.step.started, step);
142
+ step.startTime = Date.now();
143
+ }
144
+ return (val = step.run(...args));
145
+ },
146
+ false,
147
+ undefined,
148
+ step.getTimeout(),
149
+ );
140
150
 
141
151
  event.emit(event.step.after, step);
142
152
 
@@ -146,7 +156,7 @@ function recordStep(step, args) {
146
156
  event.emit(event.step.finished, step);
147
157
  });
148
158
 
149
- recorder.catchWithoutStop((err) => {
159
+ recorder.catchWithoutStop(err => {
150
160
  step.status = 'failed';
151
161
  step.endTime = Date.now();
152
162
  event.emit(event.step.failed, step);
@@ -5,7 +5,7 @@ const output = require('../output')
5
5
 
6
6
  class EmptinessAssertion extends Assertion {
7
7
  constructor(params) {
8
- super((value) => {
8
+ super(value => {
9
9
  if (Array.isArray(value)) {
10
10
  return value.length === 0
11
11
  }
@@ -22,9 +22,7 @@ class EmptinessAssertion extends Assertion {
22
22
  const err = new AssertionFailedError(this.params, "{{customMessage}}expected {{subject}} '{{value}}' {{type}}")
23
23
 
24
24
  err.cliMessage = () => {
25
- const msg = err.template
26
- .replace('{{value}}', output.colors.bold('{{value}}'))
27
- .replace('{{subject}}', output.colors.bold('{{subject}}'))
25
+ const msg = err.template.replace('{{value}}', output.colors.bold('{{value}}')).replace('{{subject}}', output.colors.bold('{{subject}}'))
28
26
  return template(msg, this.params)
29
27
  }
30
28
  return err
@@ -39,5 +37,5 @@ class EmptinessAssertion extends Assertion {
39
37
 
40
38
  module.exports = {
41
39
  Assertion: EmptinessAssertion,
42
- empty: (subject) => new EmptinessAssertion({ subject }),
40
+ empty: subject => new EmptinessAssertion({ subject }),
43
41
  }
@@ -18,10 +18,7 @@ class EqualityAssertion extends Assertion {
18
18
  getException() {
19
19
  const params = this.params
20
20
  params.jar = template(params.jar, params)
21
- const err = new AssertionFailedError(
22
- params,
23
- '{{customMessage}}expected {{jar}} "{{expected}}" {{type}} "{{actual}}"',
24
- )
21
+ const err = new AssertionFailedError(params, '{{customMessage}}expected {{jar}} "{{expected}}" {{type}} "{{actual}}"')
25
22
  err.showDiff = false
26
23
  if (typeof err.cliMessage === 'function') {
27
24
  err.message = err.cliMessage()
@@ -42,8 +39,8 @@ class EqualityAssertion extends Assertion {
42
39
 
43
40
  module.exports = {
44
41
  Assertion: EqualityAssertion,
45
- equals: (jar) => new EqualityAssertion({ jar }),
46
- urlEquals: (baseUrl) => {
42
+ equals: jar => new EqualityAssertion({ jar }),
43
+ urlEquals: baseUrl => {
47
44
  const assert = new EqualityAssertion({ jar: 'url of current page' })
48
45
  assert.comparator = function (expected, actual) {
49
46
  if (expected.indexOf('http') !== 0) {
@@ -53,5 +50,5 @@ module.exports = {
53
50
  }
54
51
  return assert
55
52
  },
56
- fileEquals: (file) => new EqualityAssertion({ file, jar: 'contents of {{file}}' }),
53
+ fileEquals: file => new EqualityAssertion({ file, jar: 'contents of {{file}}' }),
57
54
  }
@@ -10,7 +10,7 @@ class InclusionAssertion extends Assertion {
10
10
  params.jar = params.jar || 'string'
11
11
  const comparator = function (needle, haystack) {
12
12
  if (Array.isArray(haystack)) {
13
- return haystack.filter((part) => part.indexOf(needle) >= 0).length > 0
13
+ return haystack.filter(part => part.indexOf(needle) >= 0).length > 0
14
14
  }
15
15
  return haystack.indexOf(needle) >= 0
16
16
  }
@@ -28,9 +28,7 @@ class InclusionAssertion extends Assertion {
28
28
  this.params.haystack = this.params.haystack.join('\n___(next element)___\n')
29
29
  }
30
30
  err.cliMessage = function () {
31
- const msg = this.template
32
- .replace('{{jar}}', output.colors.bold('{{jar}}'))
33
- .replace('{{needle}}', output.colors.bold('{{needle}}'))
31
+ const msg = this.template.replace('{{jar}}', output.colors.bold('{{jar}}')).replace('{{needle}}', output.colors.bold('{{needle}}'))
34
32
  return template(msg, this.params)
35
33
  }
36
34
  return err
@@ -66,11 +64,11 @@ class InclusionAssertion extends Assertion {
66
64
 
67
65
  module.exports = {
68
66
  Assertion: InclusionAssertion,
69
- includes: (needleType) => {
67
+ includes: needleType => {
70
68
  needleType = needleType || 'string'
71
69
  return new InclusionAssertion({ jar: needleType })
72
70
  },
73
- fileIncludes: (file) => new InclusionAssertion({ file, jar: 'file {{file}}' }),
71
+ fileIncludes: file => new InclusionAssertion({ file, jar: 'file {{file}}' }),
74
72
  }
75
73
 
76
74
  function escapeRegExp(str) {
@@ -11,10 +11,8 @@ function errorThrown(actual, expected) {
11
11
  throw new Error(`Expected error to be thrown with message ${expected} while '${msg}' caught`)
12
12
  }
13
13
  if (typeof expected === 'object') {
14
- if (actual.constructor.name !== expected.constructor.name)
15
- throw new Error(`Expected ${expected} error to be thrown but ${actual} was caught`)
16
- if (expected.message && expected.message !== msg)
17
- throw new Error(`Expected error to be thrown with message ${expected.message} while '${msg}' caught`)
14
+ if (actual.constructor.name !== expected.constructor.name) throw new Error(`Expected ${expected} error to be thrown but ${actual} was caught`)
15
+ if (expected.message && expected.message !== msg) throw new Error(`Expected error to be thrown with message ${expected.message} while '${msg}' caught`)
18
16
  }
19
17
  return null
20
18
  }
@@ -5,9 +5,9 @@ const output = require('../output')
5
5
 
6
6
  class TruthAssertion extends Assertion {
7
7
  constructor(params) {
8
- super((value) => {
8
+ super(value => {
9
9
  if (Array.isArray(value)) {
10
- return value.filter((val) => !!val).length > 0
10
+ return value.filter(val => !!val).length > 0
11
11
  }
12
12
  return !!value
13
13
  }, params)