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
@@ -205,10 +205,7 @@ class REST extends Helper {
205
205
 
206
206
  if (request.data instanceof Secret) {
207
207
  _debugRequest.data = '*****'
208
- request.data =
209
- typeof request.data === 'object' && !(request.data instanceof Secret)
210
- ? { ...request.data.toString() }
211
- : request.data.toString()
208
+ request.data = typeof request.data === 'object' && !(request.data instanceof Secret) ? { ...request.data.toString() } : request.data.toString()
212
209
  }
213
210
 
214
211
  if (typeof request.data === 'string') {
@@ -221,9 +218,7 @@ class REST extends Helper {
221
218
  await this.config.onRequest(request)
222
219
  }
223
220
 
224
- this.options.prettyPrintJson
225
- ? this.debugSection('Request', beautify(JSON.stringify(_debugRequest)))
226
- : this.debugSection('Request', JSON.stringify(_debugRequest))
221
+ this.options.prettyPrintJson ? this.debugSection('Request', beautify(JSON.stringify(_debugRequest))) : this.debugSection('Request', JSON.stringify(_debugRequest))
227
222
 
228
223
  if (this.options.printCurl) {
229
224
  this.debugSection('CURL Request', curlize(request))
@@ -240,9 +235,7 @@ class REST extends Helper {
240
235
  if (this.config.onResponse) {
241
236
  await this.config.onResponse(response)
242
237
  }
243
- this.options.prettyPrintJson
244
- ? this.debugSection('Response', beautify(JSON.stringify(response.data)))
245
- : this.debugSection('Response', JSON.stringify(response.data))
238
+ this.options.prettyPrintJson ? this.debugSection('Response', beautify(JSON.stringify(response.data))) : this.debugSection('Response', JSON.stringify(response.data))
246
239
  return response
247
240
  }
248
241
 
@@ -408,18 +401,37 @@ class REST extends Helper {
408
401
 
409
402
  return this._executeRequest(request)
410
403
  }
404
+
405
+ /**
406
+ * Sends DELETE request to API with payload.
407
+ *
408
+ * ```js
409
+ * I.sendDeleteRequestWithPayload('/api/users/1', { author: 'john' });
410
+ * ```
411
+ *
412
+ * @param {*} url
413
+ * @param {*} [payload={}] - the payload to be sent. By default it is sent as an empty object
414
+ * @param {object} [headers={}] - the headers object to be sent. By default, it is sent as an empty object
415
+ *
416
+ * @returns {Promise<*>} response
417
+ */
418
+ async sendDeleteRequestWithPayload(url, payload = {}, headers = {}) {
419
+ const request = {
420
+ baseURL: this._url(url),
421
+ method: 'DELETE',
422
+ data: payload,
423
+ headers,
424
+ }
425
+
426
+ return this._executeRequest(request)
427
+ }
411
428
  }
412
429
 
413
430
  module.exports = REST
414
431
 
415
432
  function curlize(request) {
416
- if (request.data?.constructor.name.toLowerCase() === 'formdata')
417
- return 'cURL is not printed as the request body is not a JSON'
418
- let curl =
419
- `curl --location --request ${request.method ? request.method.toUpperCase() : 'GET'} ${request.baseURL} `.replace(
420
- "'",
421
- '',
422
- )
433
+ if (request.data?.constructor.name.toLowerCase() === 'formdata') return 'cURL is not printed as the request body is not a JSON'
434
+ let curl = `curl --location --request ${request.method ? request.method.toUpperCase() : 'GET'} ${request.baseURL} `.replace("'", '')
423
435
 
424
436
  if (request.headers) {
425
437
  Object.entries(request.headers).forEach(([key, value]) => {
@@ -21,9 +21,8 @@ const Locator = require('../locator')
21
21
  /**
22
22
  * Client Functions
23
23
  */
24
- const getPageUrl = (t) => ClientFunction(() => document.location.href).with({ boundTestRun: t })
25
- const getHtmlSource = (t) =>
26
- ClientFunction(() => document.getElementsByTagName('html')[0].innerHTML).with({ boundTestRun: t })
24
+ const getPageUrl = t => ClientFunction(() => document.location.href).with({ boundTestRun: t })
25
+ const getHtmlSource = t => ClientFunction(() => document.getElementsByTagName('html')[0].innerHTML).with({ boundTestRun: t })
27
26
 
28
27
  /**
29
28
  * Uses [TestCafe](https://github.com/DevExpress/testcafe) library to run cross-browser tests.
@@ -187,7 +186,7 @@ class TestCafe extends Helper {
187
186
  assertionTimeout: this.options.waitForTimeout,
188
187
  takeScreenshotsOnFails: true,
189
188
  })
190
- .catch((err) => {
189
+ .catch(err => {
191
190
  this.debugSection('_before', `Error ${err.toString()}`)
192
191
  this.isRunning = false
193
192
  this.testcafe.close()
@@ -208,7 +207,7 @@ class TestCafe extends Helper {
208
207
  skipJsErrors: true,
209
208
  skipUncaughtErrors: true,
210
209
  })
211
- .catch((err) => {
210
+ .catch(err => {
212
211
  this.debugSection('_before', `Error ${err.toString()}`)
213
212
  this.isRunning = false
214
213
  this.testcafe.close()
@@ -260,7 +259,7 @@ class TestCafe extends Helper {
260
259
  await this.clearCookie()
261
260
 
262
261
  // TODO IMHO that should only happen when
263
- await this.executeScript(() => localStorage.clear()).catch((err) => {
262
+ await this.executeScript(() => localStorage.clear()).catch(err => {
264
263
  if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err
265
264
  })
266
265
  }
@@ -383,7 +382,6 @@ class TestCafe extends Helper {
383
382
  * {{> refreshPage }}
384
383
  */
385
384
  async refreshPage() {
386
- // eslint-disable-next-line no-restricted-globals
387
385
  return this.t.eval(() => location.reload(true), { boundTestRun: this.t }).catch(mapError)
388
386
  }
389
387
 
@@ -394,9 +392,7 @@ class TestCafe extends Helper {
394
392
  async waitForVisible(locator, sec) {
395
393
  const timeout = sec ? sec * 1000 : undefined
396
394
 
397
- return (await findElements.call(this, this.context, locator))
398
- .with({ visibilityCheck: true, timeout })()
399
- .catch(mapError)
395
+ return (await findElements.call(this, this.context, locator)).with({ visibilityCheck: true, timeout })().catch(mapError)
400
396
  }
401
397
 
402
398
  /**
@@ -565,7 +561,6 @@ class TestCafe extends Helper {
565
561
  await this.t.click(optEl).catch(mapError)
566
562
  continue
567
563
  }
568
- // eslint-disable-next-line no-empty
569
564
  } catch (err) {}
570
565
 
571
566
  try {
@@ -574,7 +569,6 @@ class TestCafe extends Helper {
574
569
  if (await optEl.count) {
575
570
  await this.t.click(optEl).catch(mapError)
576
571
  }
577
- // eslint-disable-next-line no-empty
578
572
  } catch (err) {}
579
573
  }
580
574
  }
@@ -634,10 +628,7 @@ class TestCafe extends Helper {
634
628
  els = (await findElements.call(this, this.context, 'body')).withText(text)
635
629
  }
636
630
 
637
- return this.t
638
- .expect(els.filterVisible().count)
639
- .eql(0, `Element with text "${text}" can still be seen`)
640
- .catch(mapError)
631
+ return this.t.expect(els.filterVisible().count).eql(0, `Element with text "${text}" can still be seen`).catch(mapError)
641
632
  }
642
633
 
643
634
  /**
@@ -789,7 +780,7 @@ class TestCafe extends Helper {
789
780
  * {{> wait }}
790
781
  */
791
782
  async wait(sec) {
792
- return new Promise((done) => {
783
+ return new Promise(done => {
793
784
  setTimeout(done, sec * 1000)
794
785
  })
795
786
  }
@@ -938,10 +929,7 @@ class TestCafe extends Helper {
938
929
  return ClientFunction(() => {
939
930
  const body = document.body
940
931
  const html = document.documentElement
941
- window.scrollTo(
942
- 0,
943
- Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight),
944
- )
932
+ window.scrollTo(0, Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight))
945
933
  })
946
934
  .with({ boundTestRun: this.t })()
947
935
  .catch(mapError)
@@ -957,7 +945,7 @@ class TestCafe extends Helper {
957
945
  locator = null
958
946
  }
959
947
 
960
- const scrollBy = ClientFunction((offset) => {
948
+ const scrollBy = ClientFunction(offset => {
961
949
  if (window && window.scrollBy && offset) {
962
950
  window.scrollBy(offset.x, offset.y)
963
951
  }
@@ -1042,14 +1030,13 @@ class TestCafe extends Helper {
1042
1030
  async grabCookie(name) {
1043
1031
  if (!name) {
1044
1032
  const getCookie = ClientFunction(() => {
1045
- return document.cookie.split(';').map((c) => c.split('='))
1033
+ return document.cookie.split(';').map(c => c.split('='))
1046
1034
  }).with({ boundTestRun: this.t })
1047
1035
  const cookies = await getCookie()
1048
- return cookies.map((cookie) => ({ name: cookie[0].trim(), value: cookie[1] }))
1036
+ return cookies.map(cookie => ({ name: cookie[0].trim(), value: cookie[1] }))
1049
1037
  }
1050
1038
  const getCookie = ClientFunction(
1051
1039
  () => {
1052
- // eslint-disable-next-line prefer-template
1053
1040
  const v = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)')
1054
1041
  return v ? v[2] : null
1055
1042
  },
@@ -1089,7 +1076,7 @@ class TestCafe extends Helper {
1089
1076
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
1090
1077
 
1091
1078
  const clientFn = createClientFunction(
1092
- (urlPart) => {
1079
+ urlPart => {
1093
1080
  const currUrl = decodeURIComponent(decodeURIComponent(decodeURIComponent(window.location.href)))
1094
1081
  return currUrl.indexOf(urlPart) > -1
1095
1082
  },
@@ -1114,7 +1101,7 @@ class TestCafe extends Helper {
1114
1101
  }
1115
1102
 
1116
1103
  const clientFn = createClientFunction(
1117
- (urlPart) => {
1104
+ urlPart => {
1118
1105
  const currUrl = decodeURIComponent(decodeURIComponent(decodeURIComponent(window.location.href)))
1119
1106
  return currUrl === urlPart
1120
1107
  },
@@ -1175,9 +1162,7 @@ class TestCafe extends Helper {
1175
1162
  async waitToHide(locator, sec) {
1176
1163
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
1177
1164
 
1178
- return this.t
1179
- .expect(createSelector(locator).filterHidden().with({ boundTestRun: this.t }).exists)
1180
- .notOk({ timeout: waitTimeout })
1165
+ return this.t.expect(createSelector(locator).filterHidden().with({ boundTestRun: this.t }).exists).notOk({ timeout: waitTimeout })
1181
1166
  }
1182
1167
 
1183
1168
  /**
@@ -1186,9 +1171,7 @@ class TestCafe extends Helper {
1186
1171
  async waitForInvisible(locator, sec) {
1187
1172
  const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
1188
1173
 
1189
- return this.t
1190
- .expect(createSelector(locator).filterVisible().with({ boundTestRun: this.t }).exists)
1191
- .ok({ timeout: waitTimeout })
1174
+ return this.t.expect(createSelector(locator).filterVisible().with({ boundTestRun: this.t }).exists).ok({ timeout: waitTimeout })
1192
1175
  }
1193
1176
 
1194
1177
  /**
@@ -1215,17 +1198,16 @@ class TestCafe extends Helper {
1215
1198
 
1216
1199
  async function waitForFunction(browserFn, waitTimeout) {
1217
1200
  const pause = () =>
1218
- new Promise((done) => {
1201
+ new Promise(done => {
1219
1202
  setTimeout(done, 50)
1220
1203
  })
1221
1204
 
1222
1205
  const start = Date.now()
1223
- // eslint-disable-next-line no-constant-condition
1206
+
1224
1207
  while (true) {
1225
1208
  let result
1226
1209
  try {
1227
1210
  result = await browserFn()
1228
- // eslint-disable-next-line no-empty
1229
1211
  } catch (err) {
1230
1212
  throw new Error(`Error running function ${err.toString()}`)
1231
1213
  }
@@ -1240,13 +1222,13 @@ async function waitForFunction(browserFn, waitTimeout) {
1240
1222
  }
1241
1223
  }
1242
1224
 
1243
- const createSelector = (locator) => {
1225
+ const createSelector = locator => {
1244
1226
  locator = new Locator(locator, 'css')
1245
1227
  if (locator.isXPath()) return elementByXPath(locator.value)
1246
1228
  return Selector(locator.simplify())
1247
1229
  }
1248
1230
 
1249
- const elementByXPath = (xpath) => {
1231
+ const elementByXPath = xpath => {
1250
1232
  assert(xpath, 'xpath is required')
1251
1233
 
1252
1234
  return Selector(
@@ -1279,9 +1261,7 @@ async function findElements(matcher, locator) {
1279
1261
  locator = new Locator(locator, 'css')
1280
1262
 
1281
1263
  if (!locator.isXPath()) {
1282
- return matcher
1283
- ? matcher.find(locator.simplify())
1284
- : Selector(locator.simplify()).with({ timeout: 0, boundTestRun: this.t })
1264
+ return matcher ? matcher.find(locator.simplify()) : Selector(locator.simplify()).with({ timeout: 0, boundTestRun: this.t })
1285
1265
  }
1286
1266
 
1287
1267
  if (!matcher) return elementByXPath(locator.value).with({ timeout: 0, boundTestRun: this.t })
@@ -1310,12 +1290,7 @@ async function proceedClick(locator, context = null) {
1310
1290
 
1311
1291
  const els = await findClickable.call(this, matcher, locator)
1312
1292
  if (context) {
1313
- await assertElementExists(
1314
- els,
1315
- locator,
1316
- 'Clickable element',
1317
- `was not found inside element ${new Locator(context).toString()}`,
1318
- )
1293
+ await assertElementExists(els, locator, 'Clickable element', `was not found inside element ${new Locator(context).toString()}`)
1319
1294
  } else {
1320
1295
  await assertElementExists(els, locator, 'Clickable element')
1321
1296
  }