codeceptjs 4.0.0-beta.3 → 4.0.0-beta.5

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 (155) hide show
  1. package/README.md +134 -119
  2. package/bin/codecept.js +12 -2
  3. package/bin/test-server.js +53 -0
  4. package/docs/webapi/clearCookie.mustache +1 -1
  5. package/lib/actor.js +66 -102
  6. package/lib/ai.js +130 -121
  7. package/lib/assert/empty.js +3 -5
  8. package/lib/assert/equal.js +4 -7
  9. package/lib/assert/include.js +4 -6
  10. package/lib/assert/throws.js +2 -4
  11. package/lib/assert/truth.js +2 -2
  12. package/lib/codecept.js +141 -86
  13. package/lib/command/check.js +201 -0
  14. package/lib/command/configMigrate.js +2 -4
  15. package/lib/command/definitions.js +8 -26
  16. package/lib/command/dryRun.js +30 -35
  17. package/lib/command/generate.js +10 -14
  18. package/lib/command/gherkin/snippets.js +75 -73
  19. package/lib/command/gherkin/steps.js +1 -1
  20. package/lib/command/info.js +42 -8
  21. package/lib/command/init.js +13 -12
  22. package/lib/command/interactive.js +10 -2
  23. package/lib/command/list.js +1 -1
  24. package/lib/command/run-multiple/chunk.js +48 -45
  25. package/lib/command/run-multiple.js +12 -35
  26. package/lib/command/run-workers.js +21 -58
  27. package/lib/command/utils.js +5 -6
  28. package/lib/command/workers/runTests.js +263 -222
  29. package/lib/container.js +386 -238
  30. package/lib/data/context.js +10 -13
  31. package/lib/data/dataScenarioConfig.js +8 -8
  32. package/lib/data/dataTableArgument.js +6 -6
  33. package/lib/data/table.js +5 -11
  34. package/lib/effects.js +223 -0
  35. package/lib/element/WebElement.js +327 -0
  36. package/lib/els.js +158 -0
  37. package/lib/event.js +21 -17
  38. package/lib/heal.js +88 -80
  39. package/lib/helper/AI.js +2 -1
  40. package/lib/helper/ApiDataFactory.js +4 -7
  41. package/lib/helper/Appium.js +50 -57
  42. package/lib/helper/FileSystem.js +3 -3
  43. package/lib/helper/GraphQLDataFactory.js +4 -4
  44. package/lib/helper/JSONResponse.js +75 -37
  45. package/lib/helper/Mochawesome.js +31 -9
  46. package/lib/helper/Nightmare.js +37 -58
  47. package/lib/helper/Playwright.js +267 -272
  48. package/lib/helper/Protractor.js +56 -87
  49. package/lib/helper/Puppeteer.js +247 -264
  50. package/lib/helper/REST.js +29 -17
  51. package/lib/helper/TestCafe.js +22 -47
  52. package/lib/helper/WebDriver.js +157 -368
  53. package/lib/helper/extras/PlaywrightPropEngine.js +2 -2
  54. package/lib/helper/extras/Popup.js +22 -22
  55. package/lib/helper/network/utils.js +1 -1
  56. package/lib/helper/testcafe/testcafe-utils.js +27 -28
  57. package/lib/listener/emptyRun.js +55 -0
  58. package/lib/listener/exit.js +7 -10
  59. package/lib/listener/{retry.js → globalRetry.js} +5 -5
  60. package/lib/listener/globalTimeout.js +165 -0
  61. package/lib/listener/helpers.js +15 -15
  62. package/lib/listener/mocha.js +1 -1
  63. package/lib/listener/result.js +12 -0
  64. package/lib/listener/retryEnhancer.js +85 -0
  65. package/lib/listener/steps.js +32 -18
  66. package/lib/listener/store.js +20 -0
  67. package/lib/locator.js +1 -1
  68. package/lib/mocha/asyncWrapper.js +231 -0
  69. package/lib/{interfaces → mocha}/bdd.js +3 -3
  70. package/lib/mocha/cli.js +308 -0
  71. package/lib/mocha/factory.js +104 -0
  72. package/lib/{interfaces → mocha}/featureConfig.js +32 -12
  73. package/lib/{interfaces → mocha}/gherkin.js +26 -28
  74. package/lib/mocha/hooks.js +112 -0
  75. package/lib/mocha/index.js +12 -0
  76. package/lib/mocha/inject.js +29 -0
  77. package/lib/{interfaces → mocha}/scenarioConfig.js +31 -7
  78. package/lib/mocha/suite.js +82 -0
  79. package/lib/mocha/test.js +181 -0
  80. package/lib/mocha/types.d.ts +42 -0
  81. package/lib/mocha/ui.js +232 -0
  82. package/lib/output.js +93 -65
  83. package/lib/pause.js +160 -138
  84. package/lib/plugin/analyze.js +396 -0
  85. package/lib/plugin/auth.js +435 -0
  86. package/lib/plugin/autoDelay.js +8 -8
  87. package/lib/plugin/autoLogin.js +3 -338
  88. package/lib/plugin/commentStep.js +6 -1
  89. package/lib/plugin/coverage.js +10 -22
  90. package/lib/plugin/customLocator.js +3 -3
  91. package/lib/plugin/customReporter.js +52 -0
  92. package/lib/plugin/eachElement.js +1 -1
  93. package/lib/plugin/fakerTransform.js +1 -1
  94. package/lib/plugin/heal.js +36 -9
  95. package/lib/plugin/htmlReporter.js +1947 -0
  96. package/lib/plugin/pageInfo.js +140 -0
  97. package/lib/plugin/retryFailedStep.js +17 -18
  98. package/lib/plugin/retryTo.js +2 -113
  99. package/lib/plugin/screenshotOnFail.js +17 -58
  100. package/lib/plugin/selenoid.js +15 -35
  101. package/lib/plugin/standardActingHelpers.js +4 -1
  102. package/lib/plugin/stepByStepReport.js +56 -17
  103. package/lib/plugin/stepTimeout.js +5 -12
  104. package/lib/plugin/subtitles.js +4 -4
  105. package/lib/plugin/tryTo.js +3 -102
  106. package/lib/plugin/wdio.js +8 -10
  107. package/lib/recorder.js +155 -124
  108. package/lib/rerun.js +43 -42
  109. package/lib/result.js +161 -0
  110. package/lib/secret.js +1 -2
  111. package/lib/step/base.js +239 -0
  112. package/lib/step/comment.js +10 -0
  113. package/lib/step/config.js +50 -0
  114. package/lib/step/func.js +46 -0
  115. package/lib/step/helper.js +50 -0
  116. package/lib/step/meta.js +99 -0
  117. package/lib/step/record.js +74 -0
  118. package/lib/step/retry.js +11 -0
  119. package/lib/step/section.js +55 -0
  120. package/lib/step.js +21 -332
  121. package/lib/steps.js +50 -0
  122. package/lib/store.js +37 -5
  123. package/lib/template/heal.js +2 -11
  124. package/lib/test-server.js +323 -0
  125. package/lib/timeout.js +66 -0
  126. package/lib/utils.js +351 -218
  127. package/lib/within.js +75 -55
  128. package/lib/workerStorage.js +2 -1
  129. package/lib/workers.js +386 -277
  130. package/package.json +81 -75
  131. package/translations/de-DE.js +5 -3
  132. package/translations/fr-FR.js +5 -4
  133. package/translations/index.js +1 -0
  134. package/translations/it-IT.js +4 -3
  135. package/translations/ja-JP.js +4 -3
  136. package/translations/nl-NL.js +76 -0
  137. package/translations/pl-PL.js +4 -3
  138. package/translations/pt-BR.js +4 -3
  139. package/translations/ru-RU.js +4 -3
  140. package/translations/utils.js +9 -0
  141. package/translations/zh-CN.js +4 -3
  142. package/translations/zh-TW.js +4 -3
  143. package/typings/index.d.ts +197 -187
  144. package/typings/promiseBasedTypes.d.ts +53 -903
  145. package/typings/types.d.ts +372 -1042
  146. package/lib/cli.js +0 -257
  147. package/lib/helper/ExpectHelper.js +0 -391
  148. package/lib/helper/MockServer.js +0 -221
  149. package/lib/helper/SoftExpectHelper.js +0 -381
  150. package/lib/listener/artifacts.js +0 -19
  151. package/lib/listener/timeout.js +0 -109
  152. package/lib/mochaFactory.js +0 -113
  153. package/lib/plugin/debugErrors.js +0 -67
  154. package/lib/scenario.js +0 -224
  155. package/lib/ui.js +0 -236
@@ -1,340 +1,5 @@
1
- const fs = require('fs')
2
- const path = require('path')
3
- const { fileExists } = require('../utils')
4
- const container = require('../container')
5
- const store = require('../store')
6
- const recorder = require('../recorder')
7
- const { debug } = require('../output')
8
- const isAsyncFunction = require('../utils').isAsyncFunction
1
+ const auth = require('./auth')
9
2
 
10
- const defaultUser = {
11
- fetch: (I) => I.grabCookie(),
12
- check: () => {},
13
- restore: (I, cookies) => {
14
- I.amOnPage('/') // open a page
15
- I.setCookie(cookies)
16
- },
17
- }
3
+ console.log('autoLogin plugin was renamed to auth plugin. Please update your config.')
18
4
 
19
- const defaultConfig = {
20
- saveToFile: false,
21
- inject: 'login',
22
- }
23
-
24
- /**
25
- * Logs user in for the first test and reuses session for next tests.
26
- * Works by saving cookies into memory or file.
27
- * If a session expires automatically logs in again.
28
- *
29
- * > For better development experience cookies can be saved into file, so a session can be reused while writing tests.
30
- *
31
- * #### Usage
32
- *
33
- * 1. Enable this plugin and configure as described below
34
- * 2. Define user session names (example: `user`, `editor`, `admin`, etc).
35
- * 3. Define how users are logged in and how to check that user is logged in
36
- * 4. Use `login` object inside your tests to log in:
37
- *
38
- * ```js
39
- * // inside a test file
40
- * // use login to inject auto-login function
41
- * Feature('Login');
42
- *
43
- * Before(({ login }) => {
44
- * login('user'); // login using user session
45
- * });
46
- *
47
- * // Alternatively log in for one scenario.
48
- * Scenario('log me in', ( { I, login } ) => {
49
- * login('admin');
50
- * I.see('I am logged in');
51
- * });
52
- * ```
53
- *
54
- * #### Configuration
55
- *
56
- * * `saveToFile` (default: false) - save cookies to file. Allows to reuse session between execution.
57
- * * `inject` (default: `login`) - name of the login function to use
58
- * * `users` - an array containing different session names and functions to:
59
- * * `login` - sign in into the system
60
- * * `check` - check that user is logged in
61
- * * `fetch` - to get current cookies (by default `I.grabCookie()`)
62
- * * `restore` - to set cookies (by default `I.amOnPage('/'); I.setCookie(cookie)`)
63
- *
64
- * #### How It Works
65
- *
66
- * 1. `restore` method is executed. It should open a page and set credentials.
67
- * 2. `check` method is executed. It should reload a page (so cookies are applied) and check that this page belongs to logged-in user. When you pass the second args `session`, you could perform the validation using passed session.
68
- * 3. If `restore` and `check` were not successful, `login` is executed
69
- * 4. `login` should fill in login form
70
- * 5. After successful login, `fetch` is executed to save cookies into memory or file.
71
- *
72
- * #### Example: Simple login
73
- *
74
- * ```js
75
- * autoLogin: {
76
- * enabled: true,
77
- * saveToFile: true,
78
- * inject: 'login',
79
- * users: {
80
- * admin: {
81
- * // loginAdmin function is defined in `steps_file.js`
82
- * login: (I) => I.loginAdmin(),
83
- * // if we see `Admin` on page, we assume we are logged in
84
- * check: (I) => {
85
- * I.amOnPage('/');
86
- * I.see('Admin');
87
- * }
88
- * }
89
- * }
90
- * }
91
- * ```
92
- *
93
- * #### Example: Multiple users
94
- *
95
- * ```js
96
- * autoLogin: {
97
- * enabled: true,
98
- * saveToFile: true,
99
- * inject: 'loginAs', // use `loginAs` instead of login
100
- * users: {
101
- * user: {
102
- * login: (I) => {
103
- * I.amOnPage('/login');
104
- * I.fillField('email', 'user@site.com');
105
- * I.fillField('password', '123456');
106
- * I.click('Login');
107
- * },
108
- * check: (I) => {
109
- * I.amOnPage('/');
110
- * I.see('User', '.navbar');
111
- * },
112
- * },
113
- * admin: {
114
- * login: (I) => {
115
- * I.amOnPage('/login');
116
- * I.fillField('email', 'admin@site.com');
117
- * I.fillField('password', '123456');
118
- * I.click('Login');
119
- * },
120
- * check: (I) => {
121
- * I.amOnPage('/');
122
- * I.see('Admin', '.navbar');
123
- * },
124
- * },
125
- * }
126
- * }
127
- * ```
128
- *
129
- * #### Example: Keep cookies between tests
130
- *
131
- * If you decide to keep cookies between tests you don't need to save/retrieve cookies between tests.
132
- * But you need to login once work until session expires.
133
- * For this case, disable `fetch` and `restore` methods.
134
- *
135
- * ```js
136
- * helpers: {
137
- * WebDriver: {
138
- * // config goes here
139
- * keepCookies: true; // keep cookies for all tests
140
- * }
141
- * },
142
- * plugins: {
143
- * autoLogin: {
144
- * users: {
145
- * admin: {
146
- * login: (I) => {
147
- * I.amOnPage('/login');
148
- * I.fillField('email', 'admin@site.com');
149
- * I.fillField('password', '123456');
150
- * I.click('Login');
151
- * },
152
- * check: (I) => {
153
- * I.amOnPage('/dashboard');
154
- * I.see('Admin', '.navbar');
155
- * },
156
- * fetch: () => {}, // empty function
157
- * restore: () => {}, // empty funciton
158
- * }
159
- * }
160
- * }
161
- * }
162
- * ```
163
- *
164
- * #### Example: Getting sessions from local storage
165
- *
166
- * If your session is stored in local storage instead of cookies you still can obtain sessions.
167
- *
168
- * ```js
169
- * plugins: {
170
- * autoLogin: {
171
- * admin: {
172
- * login: (I) => I.loginAsAdmin(),
173
- * check: (I) => I.see('Admin', '.navbar'),
174
- * fetch: (I) => {
175
- * return I.executeScript(() => localStorage.getItem('session_id'));
176
- * },
177
- * restore: (I, session) => {
178
- * I.amOnPage('/');
179
- * I.executeScript((session) => localStorage.setItem('session_id', session), session);
180
- * },
181
- * }
182
- * }
183
- * }
184
- * ```
185
- *
186
- * #### Tips: Using async function in the autoLogin
187
- *
188
- * If you use async functions in the autoLogin plugin, login function should be used with `await` keyword.
189
- *
190
- * ```js
191
- * autoLogin: {
192
- * enabled: true,
193
- * saveToFile: true,
194
- * inject: 'login',
195
- * users: {
196
- * admin: {
197
- * login: async (I) => { // If you use async function in the autoLogin plugin
198
- * const phrase = await I.grabTextFrom('#phrase')
199
- * I.fillField('username', 'admin'),
200
- * I.fillField('password', 'password')
201
- * I.fillField('phrase', phrase)
202
- * },
203
- * check: (I) => {
204
- * I.amOnPage('/');
205
- * I.see('Admin');
206
- * },
207
- * }
208
- * }
209
- * }
210
- * ```
211
- *
212
- * ```js
213
- * Scenario('login', async ( {I, login} ) => {
214
- * await login('admin') // you should use `await`
215
- * })
216
- * ```
217
- *
218
- * #### Tips: Using session to validate user
219
- *
220
- * Instead of asserting on page elements for the current user in `check`, you can use the `session` you saved in `fetch`
221
- *
222
- * ```js
223
- * autoLogin: {
224
- * enabled: true,
225
- * saveToFile: true,
226
- * inject: 'login',
227
- * users: {
228
- * admin: {
229
- * login: async (I) => { // If you use async function in the autoLogin plugin
230
- * const phrase = await I.grabTextFrom('#phrase')
231
- * I.fillField('username', 'admin'),
232
- * I.fillField('password', 'password')
233
- * I.fillField('phrase', phrase)
234
- * },
235
- * check: (I, session) => {
236
- * // Throwing an error in `check` will make CodeceptJS perform the login step for the user
237
- * if (session.profile.email !== the.email.you.expect@some-mail.com) {
238
- * throw new Error ('Wrong user signed in');
239
- * }
240
- * },
241
- * }
242
- * }
243
- * }
244
- * ```
245
- *
246
- * ```js
247
- * Scenario('login', async ( {I, login} ) => {
248
- * await login('admin') // you should use `await`
249
- * })
250
- *
251
- *
252
- */
253
- module.exports = function (config) {
254
- config = Object.assign(defaultConfig, config)
255
- Object.keys(config.users).map(
256
- (u) =>
257
- (config.users[u] = {
258
- ...defaultUser,
259
- ...config.users[u],
260
- }),
261
- )
262
-
263
- if (config.saveToFile) {
264
- // loading from file
265
- for (const name in config.users) {
266
- const fileName = path.join(global.output_dir, `${name}_session.json`)
267
- if (!fileExists(fileName)) continue
268
- const data = fs.readFileSync(fileName).toString()
269
- try {
270
- store[`${name}_session`] = JSON.parse(data)
271
- } catch (err) {
272
- throw new Error(`Could not load session from ${fileName}\n${err}`)
273
- }
274
- debug(`Loaded user session for ${name}`)
275
- }
276
- }
277
-
278
- const loginFunction = async (name) => {
279
- const userSession = config.users[name]
280
- const I = container.support('I')
281
- const cookies = store[`${name}_session`]
282
- const shouldAwait =
283
- isAsyncFunction(userSession.login) || isAsyncFunction(userSession.restore) || isAsyncFunction(userSession.check)
284
-
285
- const loginAndSave = async () => {
286
- if (shouldAwait) {
287
- await userSession.login(I)
288
- } else {
289
- userSession.login(I)
290
- }
291
-
292
- const cookies = await userSession.fetch(I)
293
- if (!cookies) {
294
- debug("Cannot save user session with empty cookies from auto login's fetch method")
295
- return
296
- }
297
- if (config.saveToFile) {
298
- debug(`Saved user session into file for ${name}`)
299
- fs.writeFileSync(path.join(global.output_dir, `${name}_session.json`), JSON.stringify(cookies))
300
- }
301
- store[`${name}_session`] = cookies
302
- }
303
-
304
- if (!cookies) return loginAndSave()
305
-
306
- recorder.session.start('check login')
307
- if (shouldAwait) {
308
- await userSession.restore(I, cookies)
309
- await userSession.check(I, cookies)
310
- } else {
311
- userSession.restore(I, cookies)
312
- userSession.check(I, cookies)
313
- }
314
- recorder.session.catch((err) => {
315
- debug(`Failed auto login for ${name} due to ${err}`)
316
- debug('Logging in again')
317
- recorder.session.start('auto login')
318
- return loginAndSave()
319
- .then(() => {
320
- recorder.add(() => recorder.session.restore('auto login'))
321
- recorder.catch(() => debug('continue'))
322
- })
323
- .catch((err) => {
324
- recorder.session.restore('auto login')
325
- recorder.session.restore('check login')
326
- recorder.throw(err)
327
- })
328
- })
329
- recorder.add(() => {
330
- recorder.session.restore('check login')
331
- })
332
-
333
- return recorder.promise()
334
- }
335
-
336
- // adding this to DI container
337
- const support = {}
338
- support[config.inject] = loginFunction
339
- container.append({ support })
340
- }
5
+ module.exports = auth
@@ -7,6 +7,8 @@ let currentCommentStep
7
7
  const defaultGlobalName = '__'
8
8
 
9
9
  /**
10
+ * This plugin is **deprecated**, use `Section` instead.
11
+ *
10
12
  * Add descriptive nested steps for your tests:
11
13
  *
12
14
  * ```js
@@ -100,11 +102,14 @@ const defaultGlobalName = '__'
100
102
  * ```
101
103
  */
102
104
  module.exports = function (config) {
105
+ console.log('commentStep is deprecated, disable it and use Section instead')
106
+ console.log('const { Section: __ } = require("codeceptjs/steps")')
107
+
103
108
  event.dispatcher.on(event.test.started, () => {
104
109
  currentCommentStep = null
105
110
  })
106
111
 
107
- event.dispatcher.on(event.step.started, (step) => {
112
+ event.dispatcher.on(event.step.started, step => {
108
113
  if (currentCommentStep) {
109
114
  const metaStep = getRootMetaStep(step)
110
115
 
@@ -15,7 +15,7 @@ const supportedHelpers = ['Puppeteer', 'Playwright', 'WebDriver']
15
15
 
16
16
  const v8CoverageHelpers = {
17
17
  Playwright: {
18
- startCoverage: async (page) => {
18
+ startCoverage: async page => {
19
19
  await Promise.all([
20
20
  page.coverage.startJSCoverage({
21
21
  resetOnNavigation: false,
@@ -26,16 +26,13 @@ const v8CoverageHelpers = {
26
26
  ])
27
27
  },
28
28
  takeCoverage: async (page, coverageReport) => {
29
- const [jsCoverage, cssCoverage] = await Promise.all([
30
- page.coverage.stopJSCoverage(),
31
- page.coverage.stopCSSCoverage(),
32
- ])
29
+ const [jsCoverage, cssCoverage] = await Promise.all([page.coverage.stopJSCoverage(), page.coverage.stopCSSCoverage()])
33
30
  const coverageList = [...jsCoverage, ...cssCoverage]
34
31
  await coverageReport.add(coverageList)
35
32
  },
36
33
  },
37
34
  Puppeteer: {
38
- startCoverage: async (page) => {
35
+ startCoverage: async page => {
39
36
  await Promise.all([
40
37
  page.coverage.startJSCoverage({
41
38
  resetOnNavigation: false,
@@ -47,13 +44,10 @@ const v8CoverageHelpers = {
47
44
  ])
48
45
  },
49
46
  takeCoverage: async (page, coverageReport) => {
50
- const [jsCoverage, cssCoverage] = await Promise.all([
51
- page.coverage.stopJSCoverage(),
52
- page.coverage.stopCSSCoverage(),
53
- ])
47
+ const [jsCoverage, cssCoverage] = await Promise.all([page.coverage.stopJSCoverage(), page.coverage.stopCSSCoverage()])
54
48
  // to raw V8 script coverage
55
49
  const coverageList = [
56
- ...jsCoverage.map((it) => {
50
+ ...jsCoverage.map(it => {
57
51
  return {
58
52
  source: it.text,
59
53
  ...it.rawScriptCoverage,
@@ -65,7 +59,7 @@ const v8CoverageHelpers = {
65
59
  },
66
60
  },
67
61
  WebDriver: {
68
- startCoverage: async (page) => {
62
+ startCoverage: async page => {
69
63
  await Promise.all([
70
64
  page.coverage.startJSCoverage({
71
65
  resetOnNavigation: false,
@@ -77,13 +71,10 @@ const v8CoverageHelpers = {
77
71
  ])
78
72
  },
79
73
  takeCoverage: async (page, coverageReport) => {
80
- const [jsCoverage, cssCoverage] = await Promise.all([
81
- page.coverage.stopJSCoverage(),
82
- page.coverage.stopCSSCoverage(),
83
- ])
74
+ const [jsCoverage, cssCoverage] = await Promise.all([page.coverage.stopJSCoverage(), page.coverage.stopCSSCoverage()])
84
75
  // to raw V8 script coverage
85
76
  const coverageList = [
86
- ...jsCoverage.map((it) => {
77
+ ...jsCoverage.map(it => {
87
78
  return {
88
79
  source: it.text,
89
80
  ...it.rawScriptCoverage,
@@ -131,7 +122,7 @@ module.exports = function (config) {
131
122
  let coverageRunning = false
132
123
 
133
124
  const v8Names = Object.keys(v8CoverageHelpers)
134
- const helperName = Object.keys(helpers).find((it) => v8Names.includes(it))
125
+ const helperName = Object.keys(helpers).find(it => v8Names.includes(it))
135
126
  if (!helperName) {
136
127
  console.error(`Coverage is only supported in ${supportedHelpers.join(' or ')}`)
137
128
  // no helpers for screenshot
@@ -143,9 +134,6 @@ module.exports = function (config) {
143
134
 
144
135
  const helper = helpers[helperName]
145
136
 
146
- if (helperName === 'WebDriver' && !helper.config.devtoolsProtocol)
147
- throw Error('Coverage is currently supporting the WebDriver running with Devtools protocol.')
148
-
149
137
  const v8Helper = v8CoverageHelpers[helperName]
150
138
 
151
139
  const coverageOptions = {
@@ -183,7 +171,7 @@ module.exports = function (config) {
183
171
  })
184
172
 
185
173
  // Save coverage data after every test run
186
- event.dispatcher.on(event.test.after, (test) => {
174
+ event.dispatcher.on(event.test.after, test => {
187
175
  recorder.add(
188
176
  'take coverage',
189
177
  async () => {
@@ -111,7 +111,7 @@ const defaultConfig = {
111
111
  * I.click('=sign-up'); // matches => [data-qa=sign-up],[data-test=sign-up]
112
112
  * ```
113
113
  */
114
- module.exports = (config) => {
114
+ module.exports = config => {
115
115
  config = { ...defaultConfig, ...config }
116
116
 
117
117
  Locator.addFilter((value, locatorObj) => {
@@ -125,7 +125,7 @@ module.exports = (config) => {
125
125
  if (config.strategy.toLowerCase() === 'xpath') {
126
126
  locatorObj.value = `.//*[${[]
127
127
  .concat(config.attribute)
128
- .map((attr) => `@${attr}=${xpathLocator.literal(val)}`)
128
+ .map(attr => `@${attr}=${xpathLocator.literal(val)}`)
129
129
  .join(' or ')}]`
130
130
  locatorObj.type = 'xpath'
131
131
  }
@@ -133,7 +133,7 @@ module.exports = (config) => {
133
133
  if (config.strategy.toLowerCase() === 'css') {
134
134
  locatorObj.value = []
135
135
  .concat(config.attribute)
136
- .map((attr) => `[${attr}=${val}]`)
136
+ .map(attr => `[${attr}=${val}]`)
137
137
  .join(',')
138
138
  locatorObj.type = 'css'
139
139
  }
@@ -0,0 +1,52 @@
1
+ const event = require('../event')
2
+
3
+ /**
4
+ * Sample custom reporter for CodeceptJS.
5
+ */
6
+ module.exports = function (config) {
7
+ event.dispatcher.on(event.hook.finished, hook => {
8
+ if (config.onHookFinished) {
9
+ config.onHookFinished(hook)
10
+ }
11
+ })
12
+
13
+ event.dispatcher.on(event.test.before, test => {
14
+ if (config.onTestBefore) {
15
+ config.onTestBefore(test)
16
+ }
17
+ })
18
+
19
+ event.dispatcher.on(event.test.failed, (test, err) => {
20
+ if (config.onTestFailed) {
21
+ config.onTestFailed(test, err)
22
+ }
23
+ })
24
+
25
+ event.dispatcher.on(event.test.passed, test => {
26
+ if (config.onTestPassed) {
27
+ config.onTestPassed(test)
28
+ }
29
+ })
30
+
31
+ event.dispatcher.on(event.test.skipped, test => {
32
+ if (config.onTestSkipped) {
33
+ config.onTestSkipped(test)
34
+ }
35
+ })
36
+
37
+ event.dispatcher.on(event.test.finished, test => {
38
+ if (config.onTestFinished) {
39
+ config.onTestFinished(test)
40
+ }
41
+ })
42
+
43
+ event.dispatcher.on(event.all.result, result => {
44
+ if (config.onResult) {
45
+ config.onResult(result)
46
+ }
47
+
48
+ if (config.save) {
49
+ result.save()
50
+ }
51
+ })
52
+ }
@@ -72,7 +72,7 @@ function eachElement(purpose, locator, fn) {
72
72
  if (store.dryRun) return
73
73
  const helpers = Object.values(container.helpers())
74
74
 
75
- const helper = helpers.filter((h) => !!h._locate)[0]
75
+ const helper = helpers.filter(h => !!h._locate)[0]
76
76
 
77
77
  if (!helper) {
78
78
  throw new Error('No helper enabled with _locate method with returns a list of elements.')
@@ -40,7 +40,7 @@ const transform = require('../transform')
40
40
  *
41
41
  */
42
42
  module.exports = function (config) {
43
- transform.addTransformerBeforeAll('gherkin.examples', (value) => {
43
+ transform.addTransformerBeforeAll('gherkin.examples', value => {
44
44
  if (typeof value === 'string' && value.length > 0) {
45
45
  return faker.helpers.fake(value)
46
46
  }
@@ -5,6 +5,7 @@ const event = require('../event')
5
5
  const output = require('../output')
6
6
  const heal = require('../heal')
7
7
  const store = require('../store')
8
+ const container = require('../container')
8
9
 
9
10
  const defaultConfig = {
10
11
  healLimit: 2,
@@ -31,10 +32,7 @@ const defaultConfig = {
31
32
  module.exports = function (config = {}) {
32
33
  if (store.debugMode && !process.env.DEBUG) {
33
34
  event.dispatcher.on(event.test.failed, () => {
34
- output.plugin(
35
- 'heal',
36
- 'Healing is disabled in --debug mode, use DEBUG="codeceptjs:heal" to enable it in debug mode',
37
- )
35
+ output.plugin('heal', 'Healing is disabled in --debug mode, use DEBUG="codeceptjs:heal" to enable it in debug mode')
38
36
  })
39
37
  return
40
38
  }
@@ -48,21 +46,21 @@ module.exports = function (config = {}) {
48
46
 
49
47
  config = Object.assign(defaultConfig, config)
50
48
 
51
- event.dispatcher.on(event.test.before, (test) => {
49
+ event.dispatcher.on(event.test.before, test => {
52
50
  currentTest = test
53
51
  healedSteps = 0
54
52
  caughtError = null
55
53
  })
56
54
 
57
- event.dispatcher.on(event.step.started, (step) => (currentStep = step))
55
+ event.dispatcher.on(event.step.started, step => (currentStep = step))
58
56
 
59
- event.dispatcher.on(event.step.after, (step) => {
57
+ event.dispatcher.on(event.step.after, step => {
60
58
  if (isHealing) return
61
59
  if (healTries >= config.healLimit) return // out of limit
62
60
 
63
61
  if (!heal.hasCorrespondingRecipes(step)) return
64
62
 
65
- recorder.catchWithoutStop(async (err) => {
63
+ recorder.catchWithoutStop(async err => {
66
64
  isHealing = true
67
65
  if (caughtError === err) throw err // avoid double handling
68
66
  caughtError = err
@@ -99,7 +97,7 @@ module.exports = function (config = {}) {
99
97
 
100
98
  print(`${colors.bold(heal.fixes.length)} ${heal.fixes.length === 1 ? 'step was' : 'steps were'} healed`)
101
99
 
102
- const suggestions = heal.fixes.filter((fix) => fix.recipe && heal.recipes[fix.recipe].suggest)
100
+ const suggestions = heal.fixes.filter(fix => fix.recipe && heal.recipes[fix.recipe].suggest)
103
101
 
104
102
  if (!suggestions.length) return
105
103
 
@@ -118,4 +116,33 @@ module.exports = function (config = {}) {
118
116
  i++
119
117
  }
120
118
  })
119
+
120
+ event.dispatcher.on(event.workers.result, result => {
121
+ const { print } = output
122
+
123
+ const healedTests = Object.values(result.tests)
124
+ .flat()
125
+ .filter(test => test.notes.some(note => note.type === 'heal'))
126
+ if (!healedTests.length) return
127
+
128
+ setTimeout(() => {
129
+ print('')
130
+ print('===================')
131
+ print(colors.bold.green('Self-Healing Report:'))
132
+
133
+ print('')
134
+ print('Suggested changes:')
135
+ print('')
136
+
137
+ healedTests.forEach(test => {
138
+ print(`${colors.bold.magenta(test.title)}`)
139
+ test.notes
140
+ .filter(note => note.type === 'heal')
141
+ .forEach(note => {
142
+ print(note.text)
143
+ print('')
144
+ })
145
+ })
146
+ }, 0)
147
+ })
121
148
  }