codeceptjs 3.4.1 → 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 (281) hide show
  1. package/README.md +31 -30
  2. package/bin/codecept.js +1 -1
  3. package/lib/actor.js +6 -3
  4. package/lib/ai.js +180 -0
  5. package/lib/cli.js +13 -3
  6. package/lib/codecept.js +8 -0
  7. package/lib/colorUtils.js +10 -0
  8. package/lib/command/definitions.js +2 -7
  9. package/lib/command/dryRun.js +11 -2
  10. package/lib/command/generate.js +46 -3
  11. package/lib/command/info.js +24 -0
  12. package/lib/command/init.js +64 -6
  13. package/lib/command/interactive.js +15 -1
  14. package/lib/command/run-multiple/collection.js +17 -5
  15. package/lib/command/run-multiple.js +4 -2
  16. package/lib/command/run-workers.js +68 -5
  17. package/lib/command/run.js +7 -0
  18. package/lib/command/workers/runTests.js +39 -0
  19. package/lib/container.js +13 -3
  20. package/lib/data/context.js +14 -6
  21. package/lib/event.js +4 -0
  22. package/lib/helper/ApiDataFactory.js +2 -1
  23. package/lib/helper/Appium.js +116 -29
  24. package/lib/helper/Expect.js +422 -0
  25. package/lib/helper/FileSystem.js +1 -1
  26. package/lib/helper/GraphQL.js +25 -0
  27. package/lib/helper/JSONResponse.js +4 -4
  28. package/lib/helper/Nightmare.js +10 -5
  29. package/lib/helper/OpenAI.js +126 -0
  30. package/lib/helper/Playwright.js +1298 -229
  31. package/lib/helper/Protractor.js +12 -7
  32. package/lib/helper/Puppeteer.js +204 -64
  33. package/lib/helper/REST.js +15 -5
  34. package/lib/helper/TestCafe.js +45 -10
  35. package/lib/helper/WebDriver.js +252 -83
  36. package/lib/helper/errors/ElementNotFound.js +2 -1
  37. package/lib/helper/extras/PlaywrightReactVueLocator.js +38 -0
  38. package/lib/helper/scripts/blurElement.js +17 -0
  39. package/lib/helper/scripts/focusElement.js +17 -0
  40. package/lib/helper/scripts/highlightElement.js +20 -0
  41. package/lib/html.js +258 -0
  42. package/lib/interfaces/bdd.js +1 -1
  43. package/lib/interfaces/gherkin.js +37 -3
  44. package/lib/interfaces/scenarioConfig.js +1 -0
  45. package/lib/listener/retry.js +2 -1
  46. package/lib/locator.js +17 -4
  47. package/lib/mochaFactory.js +2 -1
  48. package/lib/output.js +1 -1
  49. package/lib/pause.js +78 -19
  50. package/lib/plugin/autoLogin.js +45 -10
  51. package/lib/plugin/debugErrors.js +67 -0
  52. package/lib/plugin/fakerTransform.js +4 -6
  53. package/lib/plugin/heal.js +209 -0
  54. package/lib/plugin/retryFailedStep.js +10 -1
  55. package/lib/plugin/retryTo.js +2 -4
  56. package/lib/plugin/screenshotOnFail.js +11 -2
  57. package/lib/plugin/selenoid.js +6 -1
  58. package/lib/plugin/standardActingHelpers.js +0 -2
  59. package/lib/plugin/stepByStepReport.js +2 -2
  60. package/lib/plugin/tryTo.js +5 -7
  61. package/lib/plugin/wdio.js +0 -1
  62. package/lib/recorder.js +22 -11
  63. package/lib/secret.js +5 -4
  64. package/lib/session.js +1 -1
  65. package/lib/step.js +36 -12
  66. package/lib/ui.js +5 -3
  67. package/lib/utils.js +22 -1
  68. package/lib/workers.js +83 -10
  69. package/package.json +117 -95
  70. package/translations/de-DE.js +5 -0
  71. package/translations/fr-FR.js +14 -1
  72. package/translations/it-IT.js +1 -0
  73. package/translations/ja-JP.js +14 -9
  74. package/translations/pl-PL.js +5 -0
  75. package/translations/pt-BR.js +1 -0
  76. package/translations/ru-RU.js +1 -0
  77. package/translations/zh-CN.js +5 -0
  78. package/translations/zh-TW.js +5 -0
  79. package/typings/index.d.ts +51 -15
  80. package/typings/promiseBasedTypes.d.ts +864 -802
  81. package/typings/types.d.ts +1339 -744
  82. package/CHANGELOG.md +0 -2427
  83. package/docs/advanced.md +0 -351
  84. package/docs/api.md +0 -323
  85. package/docs/basics.md +0 -980
  86. package/docs/bdd.md +0 -535
  87. package/docs/best.md +0 -237
  88. package/docs/books.md +0 -37
  89. package/docs/bootstrap.md +0 -135
  90. package/docs/build/ApiDataFactory.js +0 -409
  91. package/docs/build/Appium.js +0 -1938
  92. package/docs/build/FileSystem.js +0 -228
  93. package/docs/build/GraphQL.js +0 -204
  94. package/docs/build/GraphQLDataFactory.js +0 -309
  95. package/docs/build/JSONResponse.js +0 -338
  96. package/docs/build/Mochawesome.js +0 -71
  97. package/docs/build/Nightmare.js +0 -2145
  98. package/docs/build/Playwright.js +0 -3986
  99. package/docs/build/Polly.js +0 -42
  100. package/docs/build/Protractor.js +0 -2699
  101. package/docs/build/Puppeteer.js +0 -3710
  102. package/docs/build/REST.js +0 -334
  103. package/docs/build/SeleniumWebdriver.js +0 -76
  104. package/docs/build/TestCafe.js +0 -2057
  105. package/docs/build/WebDriver.js +0 -4017
  106. package/docs/changelog.md +0 -2436
  107. package/docs/commands.md +0 -254
  108. package/docs/community-helpers.md +0 -58
  109. package/docs/configuration.md +0 -157
  110. package/docs/continuous-integration.md +0 -22
  111. package/docs/custom-helpers.md +0 -306
  112. package/docs/data.md +0 -375
  113. package/docs/detox.md +0 -235
  114. package/docs/docker.md +0 -137
  115. package/docs/email.md +0 -183
  116. package/docs/examples.md +0 -149
  117. package/docs/helpers/ApiDataFactory.md +0 -266
  118. package/docs/helpers/Appium.md +0 -1312
  119. package/docs/helpers/Detox.md +0 -586
  120. package/docs/helpers/FileSystem.md +0 -152
  121. package/docs/helpers/GraphQL.md +0 -130
  122. package/docs/helpers/GraphQLDataFactory.md +0 -226
  123. package/docs/helpers/JSONResponse.md +0 -254
  124. package/docs/helpers/Mochawesome.md +0 -8
  125. package/docs/helpers/MockRequest.md +0 -377
  126. package/docs/helpers/Nightmare.md +0 -1256
  127. package/docs/helpers/Playwright.md +0 -2208
  128. package/docs/helpers/Polly.md +0 -44
  129. package/docs/helpers/Puppeteer-firefox.md +0 -86
  130. package/docs/helpers/Puppeteer.md +0 -2141
  131. package/docs/helpers/REST.md +0 -217
  132. package/docs/helpers/TestCafe.md +0 -1222
  133. package/docs/helpers/WebDriver.md +0 -2319
  134. package/docs/hooks.md +0 -340
  135. package/docs/index.md +0 -111
  136. package/docs/installation.md +0 -75
  137. package/docs/internal-api.md +0 -265
  138. package/docs/locators.md +0 -331
  139. package/docs/mobile-react-native-locators.md +0 -67
  140. package/docs/mobile.md +0 -297
  141. package/docs/nightmare.md +0 -223
  142. package/docs/pageobjects.md +0 -291
  143. package/docs/parallel.md +0 -232
  144. package/docs/playwright.md +0 -609
  145. package/docs/plugins.md +0 -1171
  146. package/docs/puppeteer.md +0 -316
  147. package/docs/quickstart.md +0 -163
  148. package/docs/react.md +0 -69
  149. package/docs/reports.md +0 -392
  150. package/docs/secrets.md +0 -30
  151. package/docs/shadow.md +0 -68
  152. package/docs/shared/keys.mustache +0 -31
  153. package/docs/shared/react.mustache +0 -1
  154. package/docs/testcafe.md +0 -174
  155. package/docs/translation.md +0 -247
  156. package/docs/tutorial.md +0 -271
  157. package/docs/typescript.md +0 -180
  158. package/docs/ui.md +0 -59
  159. package/docs/videos.md +0 -28
  160. package/docs/visual.md +0 -202
  161. package/docs/vue.md +0 -121
  162. package/docs/webapi/amOnPage.mustache +0 -11
  163. package/docs/webapi/appendField.mustache +0 -9
  164. package/docs/webapi/attachFile.mustache +0 -12
  165. package/docs/webapi/checkOption.mustache +0 -13
  166. package/docs/webapi/clearCookie.mustache +0 -10
  167. package/docs/webapi/clearField.mustache +0 -9
  168. package/docs/webapi/click.mustache +0 -25
  169. package/docs/webapi/clickLink.mustache +0 -8
  170. package/docs/webapi/closeCurrentTab.mustache +0 -7
  171. package/docs/webapi/closeOtherTabs.mustache +0 -8
  172. package/docs/webapi/dontSee.mustache +0 -11
  173. package/docs/webapi/dontSeeCheckboxIsChecked.mustache +0 -10
  174. package/docs/webapi/dontSeeCookie.mustache +0 -8
  175. package/docs/webapi/dontSeeCurrentUrlEquals.mustache +0 -10
  176. package/docs/webapi/dontSeeElement.mustache +0 -8
  177. package/docs/webapi/dontSeeElementInDOM.mustache +0 -8
  178. package/docs/webapi/dontSeeInCurrentUrl.mustache +0 -4
  179. package/docs/webapi/dontSeeInField.mustache +0 -11
  180. package/docs/webapi/dontSeeInSource.mustache +0 -8
  181. package/docs/webapi/dontSeeInTitle.mustache +0 -8
  182. package/docs/webapi/doubleClick.mustache +0 -13
  183. package/docs/webapi/downloadFile.mustache +0 -12
  184. package/docs/webapi/dragAndDrop.mustache +0 -9
  185. package/docs/webapi/dragSlider.mustache +0 -11
  186. package/docs/webapi/executeAsyncScript.mustache +0 -24
  187. package/docs/webapi/executeScript.mustache +0 -26
  188. package/docs/webapi/fillField.mustache +0 -16
  189. package/docs/webapi/forceClick.mustache +0 -28
  190. package/docs/webapi/forceRightClick.mustache +0 -18
  191. package/docs/webapi/grabAllWindowHandles.mustache +0 -7
  192. package/docs/webapi/grabAttributeFrom.mustache +0 -10
  193. package/docs/webapi/grabAttributeFromAll.mustache +0 -9
  194. package/docs/webapi/grabBrowserLogs.mustache +0 -9
  195. package/docs/webapi/grabCookie.mustache +0 -11
  196. package/docs/webapi/grabCssPropertyFrom.mustache +0 -11
  197. package/docs/webapi/grabCssPropertyFromAll.mustache +0 -10
  198. package/docs/webapi/grabCurrentUrl.mustache +0 -9
  199. package/docs/webapi/grabCurrentWindowHandle.mustache +0 -6
  200. package/docs/webapi/grabDataFromPerformanceTiming.mustache +0 -20
  201. package/docs/webapi/grabElementBoundingRect.mustache +0 -20
  202. package/docs/webapi/grabGeoLocation.mustache +0 -8
  203. package/docs/webapi/grabHTMLFrom.mustache +0 -10
  204. package/docs/webapi/grabHTMLFromAll.mustache +0 -9
  205. package/docs/webapi/grabNumberOfOpenTabs.mustache +0 -8
  206. package/docs/webapi/grabNumberOfVisibleElements.mustache +0 -9
  207. package/docs/webapi/grabPageScrollPosition.mustache +0 -8
  208. package/docs/webapi/grabPopupText.mustache +0 -5
  209. package/docs/webapi/grabSource.mustache +0 -8
  210. package/docs/webapi/grabTextFrom.mustache +0 -10
  211. package/docs/webapi/grabTextFromAll.mustache +0 -9
  212. package/docs/webapi/grabTitle.mustache +0 -8
  213. package/docs/webapi/grabValueFrom.mustache +0 -9
  214. package/docs/webapi/grabValueFromAll.mustache +0 -8
  215. package/docs/webapi/moveCursorTo.mustache +0 -12
  216. package/docs/webapi/openNewTab.mustache +0 -7
  217. package/docs/webapi/pressKey.mustache +0 -12
  218. package/docs/webapi/pressKeyDown.mustache +0 -12
  219. package/docs/webapi/pressKeyUp.mustache +0 -12
  220. package/docs/webapi/pressKeyWithKeyNormalization.mustache +0 -60
  221. package/docs/webapi/refreshPage.mustache +0 -6
  222. package/docs/webapi/resizeWindow.mustache +0 -6
  223. package/docs/webapi/rightClick.mustache +0 -14
  224. package/docs/webapi/saveElementScreenshot.mustache +0 -10
  225. package/docs/webapi/saveScreenshot.mustache +0 -12
  226. package/docs/webapi/say.mustache +0 -10
  227. package/docs/webapi/scrollIntoView.mustache +0 -11
  228. package/docs/webapi/scrollPageToBottom.mustache +0 -6
  229. package/docs/webapi/scrollPageToTop.mustache +0 -6
  230. package/docs/webapi/scrollTo.mustache +0 -12
  231. package/docs/webapi/see.mustache +0 -11
  232. package/docs/webapi/seeAttributesOnElements.mustache +0 -9
  233. package/docs/webapi/seeCheckboxIsChecked.mustache +0 -10
  234. package/docs/webapi/seeCookie.mustache +0 -8
  235. package/docs/webapi/seeCssPropertiesOnElements.mustache +0 -9
  236. package/docs/webapi/seeCurrentUrlEquals.mustache +0 -11
  237. package/docs/webapi/seeElement.mustache +0 -8
  238. package/docs/webapi/seeElementInDOM.mustache +0 -8
  239. package/docs/webapi/seeInCurrentUrl.mustache +0 -8
  240. package/docs/webapi/seeInField.mustache +0 -12
  241. package/docs/webapi/seeInPopup.mustache +0 -8
  242. package/docs/webapi/seeInSource.mustache +0 -7
  243. package/docs/webapi/seeInTitle.mustache +0 -8
  244. package/docs/webapi/seeNumberOfElements.mustache +0 -11
  245. package/docs/webapi/seeNumberOfVisibleElements.mustache +0 -10
  246. package/docs/webapi/seeTextEquals.mustache +0 -9
  247. package/docs/webapi/seeTitleEquals.mustache +0 -8
  248. package/docs/webapi/selectOption.mustache +0 -21
  249. package/docs/webapi/setCookie.mustache +0 -16
  250. package/docs/webapi/setGeoLocation.mustache +0 -12
  251. package/docs/webapi/switchTo.mustache +0 -9
  252. package/docs/webapi/switchToNextTab.mustache +0 -10
  253. package/docs/webapi/switchToPreviousTab.mustache +0 -10
  254. package/docs/webapi/type.mustache +0 -18
  255. package/docs/webapi/uncheckOption.mustache +0 -13
  256. package/docs/webapi/wait.mustache +0 -8
  257. package/docs/webapi/waitForClickable.mustache +0 -11
  258. package/docs/webapi/waitForDetached.mustache +0 -10
  259. package/docs/webapi/waitForElement.mustache +0 -11
  260. package/docs/webapi/waitForEnabled.mustache +0 -6
  261. package/docs/webapi/waitForFunction.mustache +0 -17
  262. package/docs/webapi/waitForInvisible.mustache +0 -10
  263. package/docs/webapi/waitForText.mustache +0 -13
  264. package/docs/webapi/waitForValue.mustache +0 -10
  265. package/docs/webapi/waitForVisible.mustache +0 -10
  266. package/docs/webapi/waitInUrl.mustache +0 -9
  267. package/docs/webapi/waitNumberOfVisibleElements.mustache +0 -10
  268. package/docs/webapi/waitToHide.mustache +0 -10
  269. package/docs/webapi/waitUrlEquals.mustache +0 -10
  270. package/docs/webdriver.md +0 -657
  271. package/docs/wiki/Books-&-Posts.md +0 -27
  272. package/docs/wiki/Community-Helpers-&-Plugins.md +0 -49
  273. package/docs/wiki/Converting-Playwright-to-Istanbul-Coverage.md +0 -29
  274. package/docs/wiki/Examples.md +0 -139
  275. package/docs/wiki/Google-Summer-of-Code-(GSoC)-2020.md +0 -68
  276. package/docs/wiki/Home.md +0 -16
  277. package/docs/wiki/Release-Process.md +0 -24
  278. package/docs/wiki/Roadmap.md +0 -23
  279. package/docs/wiki/Tests.md +0 -1393
  280. package/docs/wiki/Upgrading-to-CodeceptJS-3.md +0 -153
  281. package/docs/wiki/Videos.md +0 -19
@@ -4,8 +4,6 @@ const standardActingHelpers = [
4
4
  'Puppeteer',
5
5
  'Appium',
6
6
  'TestCafe',
7
- 'Protractor',
8
- 'Nightmare',
9
7
  ];
10
8
 
11
9
  module.exports = standardActingHelpers;
@@ -89,8 +89,8 @@ module.exports = function (config) {
89
89
  const reportDir = config.output ? path.resolve(global.codecept_dir, config.output) : defaultConfig.output;
90
90
 
91
91
  event.dispatcher.on(event.test.before, (test) => {
92
- const md5hash = crypto.createHash('md5').update(test.file + test.title).digest('hex');
93
- dir = path.join(reportDir, `record_${md5hash}`);
92
+ const sha256hash = crypto.createHash('sha256').update(test.file + test.title).digest('hex');
93
+ dir = path.join(reportDir, `record_${sha256hash}`);
94
94
  mkdirp.sync(dir);
95
95
  stepNum = 0;
96
96
  error = null;
@@ -1,5 +1,4 @@
1
1
  const recorder = require('../recorder');
2
- const store = require('../store');
3
2
  const { debug } = require('../output');
4
3
 
5
4
  const defaultConfig = {
@@ -9,7 +8,7 @@ const defaultConfig = {
9
8
  /**
10
9
  *
11
10
  *
12
- * Adds global `tryTo` function inside of which all failed steps won't fail a test but will return true/false.
11
+ * Adds global `tryTo` function in which all failed steps won't fail a test but will return true/false.
13
12
  *
14
13
  * Enable this plugin in `codecept.conf.js` (enabled by default for new setups):
15
14
  *
@@ -47,7 +46,7 @@ const defaultConfig = {
47
46
  * ```js
48
47
  * const assert = require('assert');
49
48
  * ```
50
- * Then use the assert:
49
+ * Then use the assertion:
51
50
  * const result1 = await tryTo(() => I.see('Hello, user'));
52
51
  * const result2 = await tryTo(() => I.seeElement('.welcome'));
53
52
  * assert.ok(result1 && result2, 'Assertions were not succesful');
@@ -70,7 +69,7 @@ const defaultConfig = {
70
69
  * const tryTo = codeceptjs.container.plugins('tryTo');
71
70
  * ```
72
71
  *
73
- */
72
+ */
74
73
  module.exports = function (config) {
75
74
  config = Object.assign(defaultConfig, config);
76
75
 
@@ -81,11 +80,10 @@ module.exports = function (config) {
81
80
  };
82
81
 
83
82
  function tryTo(callback) {
84
- const mode = store.debugMode;
85
83
  let result = false;
86
84
  return recorder.add('tryTo', () => {
87
- store.debugMode = true;
88
85
  recorder.session.start('tryTo');
86
+ process.env.TRY_TO = 'true';
89
87
  callback();
90
88
  recorder.add(() => {
91
89
  result = true;
@@ -100,7 +98,7 @@ function tryTo(callback) {
100
98
  return result;
101
99
  });
102
100
  return recorder.add('result', () => {
103
- store.debugMode = mode;
101
+ process.env.TRY_TO = undefined;
104
102
  return result;
105
103
  }, true, false);
106
104
  }, false, false);
@@ -51,7 +51,6 @@ let restartsSession;
51
51
  * }
52
52
  * ```
53
53
  *
54
- * Please note, this service can be used with Protractor helper as well!
55
54
  *
56
55
  * #### Sauce Service
57
56
  *
package/lib/recorder.js CHANGED
@@ -1,6 +1,6 @@
1
1
  const debug = require('debug')('codeceptjs:recorder');
2
2
  const promiseRetry = require('promise-retry');
3
-
3
+ const { printObjectProperties } = require('./utils');
4
4
  const { log } = require('./output');
5
5
 
6
6
  const MAX_TASKS = 100;
@@ -11,6 +11,7 @@ let errFn;
11
11
  let queueId = 0;
12
12
  let sessionId = null;
13
13
  let asyncErr = null;
14
+ let ignoredErrs = [];
14
15
 
15
16
  let tasks = [];
16
17
  let oldPromises = [];
@@ -41,6 +42,7 @@ module.exports = {
41
42
  * @inner
42
43
  */
43
44
  start() {
45
+ debug('Starting recording promises');
44
46
  running = true;
45
47
  asyncErr = null;
46
48
  errFn = null;
@@ -92,8 +94,10 @@ module.exports = {
92
94
  promise = Promise.resolve();
93
95
  oldPromises = [];
94
96
  tasks = [];
97
+ ignoredErrs = [];
95
98
  this.session.running = false;
96
- this.retries = [];
99
+ // reset this retries makes the retryFailedStep plugin won't work if there is Before/BeforeSuit block due to retries is undefined on Scenario
100
+ // this.retries = [];
97
101
  },
98
102
 
99
103
  /**
@@ -161,7 +165,7 @@ module.exports = {
161
165
  * true: it will retries if `retryOpts` set.
162
166
  * false: ignore `retryOpts` and won't retry.
163
167
  * @param {number} [timeout]
164
- * @return {Promise<*> | undefined}
168
+ * @return {Promise<*>}
165
169
  * @inner
166
170
  */
167
171
  add(taskName, fn = undefined, force = false, retry = undefined, timeout = undefined) {
@@ -175,7 +179,7 @@ module.exports = {
175
179
  return;
176
180
  }
177
181
  tasks.push(taskName);
178
- if (process.env.DEBUG) debug(`${currentQueue()}Queued | ${taskName}`);
182
+ debug(`${currentQueue()}Queued | ${taskName}`);
179
183
 
180
184
  return promise = Promise.resolve(promise).then((res) => {
181
185
  // prefer options for non-conditional retries
@@ -224,10 +228,12 @@ module.exports = {
224
228
  * @inner
225
229
  */
226
230
  catch(customErrFn) {
231
+ const fnDescription = customErrFn?.toString()?.replace(/\s{2,}/g, ' ').replace(/\n/g, ' ')?.slice(0, 50);
232
+ debug(`${currentQueue()}Queued | catch with error handler ${fnDescription || ''}`);
227
233
  return promise = promise.catch((err) => {
228
- log(`${currentQueue()}Error | ${err}`);
234
+ log(`${currentQueue()}Error | ${err} ${fnDescription}...`);
229
235
  if (!(err instanceof Error)) { // strange things may happen
230
- err = new Error(`[Wrapped Error] ${JSON.stringify(err)}`); // we should be prepared for them
236
+ err = new Error(`[Wrapped Error] ${printObjectProperties(err)}`); // we should be prepared for them
231
237
  }
232
238
  if (customErrFn) {
233
239
  customErrFn(err);
@@ -244,15 +250,15 @@ module.exports = {
244
250
  * @inner
245
251
  */
246
252
  catchWithoutStop(customErrFn) {
253
+ const fnDescription = customErrFn?.toString()?.replace(/\s{2,}/g, ' ').replace(/\n/g, ' ')?.slice(0, 50);
247
254
  return promise = promise.catch((err) => {
248
- log(`${currentQueue()}Error | ${err}`);
255
+ if (ignoredErrs.includes(err)) return; // already caught
256
+ log(`${currentQueue()}Error (Non-Terminated) | ${err} | ${fnDescription || ''}...`);
249
257
  if (!(err instanceof Error)) { // strange things may happen
250
258
  err = new Error(`[Wrapped Error] ${JSON.stringify(err)}`); // we should be prepared for them
251
259
  }
252
260
  if (customErrFn) {
253
- customErrFn(err);
254
- } else if (errFn) {
255
- errFn(err);
261
+ return customErrFn(err);
256
262
  }
257
263
  });
258
264
  },
@@ -264,12 +270,17 @@ module.exports = {
264
270
  * @param {*} err
265
271
  * @inner
266
272
  */
273
+
267
274
  throw(err) {
268
- return this.add(`throw error ${err}`, () => {
275
+ return this.add(`throw error: ${err.message}`, () => {
269
276
  throw err;
270
277
  });
271
278
  },
272
279
 
280
+ ignoreErr(err) {
281
+ ignoredErrs.push(err);
282
+ },
283
+
273
284
  /**
274
285
  * @param {*} err
275
286
  * @inner
package/lib/secret.js CHANGED
@@ -1,6 +1,8 @@
1
1
  /* eslint-disable max-classes-per-file */
2
2
  const { deepClone } = require('./utils');
3
3
 
4
+ const maskedString = '*****';
5
+
4
6
  /** @param {string} secret */
5
7
  class Secret {
6
8
  constructor(secret) {
@@ -13,7 +15,7 @@ class Secret {
13
15
  }
14
16
 
15
17
  getMasked() {
16
- return '*****';
18
+ return maskedString;
17
19
  }
18
20
 
19
21
  /**
@@ -36,12 +38,11 @@ function secretObject(obj, fieldsToHide = []) {
36
38
  if (prop === 'toString') {
37
39
  return function () {
38
40
  const maskedObject = deepClone(obj);
39
- fieldsToHide.forEach(f => maskedObject[f] = '****');
41
+ fieldsToHide.forEach(f => maskedObject[f] = maskedString);
40
42
  return JSON.stringify(maskedObject);
41
43
  };
42
44
  }
43
-
44
- return obj[prop];
45
+ return fieldsToHide.includes(prop) ? new Secret(obj[prop]) : obj[prop];
45
46
  },
46
47
  };
47
48
 
package/lib/session.js CHANGED
@@ -19,7 +19,7 @@ const savedSessions = {};
19
19
  * @param {CodeceptJS.LocatorOrString} sessionName
20
20
  * @param {Function | Object<string, *>} config
21
21
  * @param {Function} [fn]
22
- * @return {Promise<*> | undefined}
22
+ * @return {any}
23
23
  */
24
24
  function session(sessionName, config, fn) {
25
25
  if (typeof config === 'function') {
package/lib/step.js CHANGED
@@ -119,7 +119,9 @@ class Step {
119
119
  }
120
120
  let result;
121
121
  try {
122
- result = this.helper[this.helperMethod].apply(this.helper, this.args);
122
+ if (this.helperMethod !== 'say') {
123
+ result = this.helper[this.helperMethod].apply(this.helper, this.args);
124
+ }
123
125
  this.setStatus('success');
124
126
  } catch (err) {
125
127
  this.setStatus('failed');
@@ -138,13 +140,7 @@ class Step {
138
140
 
139
141
  /** @return {string} */
140
142
  humanize() {
141
- return this.name
142
- // insert a space before all caps
143
- .replace(/([A-Z])/g, ' $1')
144
- // _ chars to spaces
145
- .replace('_', ' ')
146
- // uppercase the first character
147
- .replace(/^(.)|\s(.)/g, $1 => $1.toLowerCase());
143
+ return humanizeString(this.name);
148
144
  }
149
145
 
150
146
  /** @return {string} */
@@ -172,7 +168,12 @@ class Step {
172
168
  } else if (arg.toString && arg.toString() !== '[object Object]') {
173
169
  return arg.toString();
174
170
  } else if (typeof arg === 'object') {
175
- return JSON.stringify(arg);
171
+ const returnedArg = {};
172
+ for (const [key, value] of Object.entries(arg)) {
173
+ returnedArg[key] = value;
174
+ if (value instanceof Secret) returnedArg[key] = value.getMasked();
175
+ }
176
+ return JSON.stringify(returnedArg);
176
177
  }
177
178
  return arg;
178
179
  }).join(', ');
@@ -242,12 +243,21 @@ class MetaStep extends Step {
242
243
  }
243
244
 
244
245
  toString() {
245
- const actorText = !this.isBDD() && !this.isWithin() ? `${this.actor}:` : this.actor;
246
- return `${this.prefix}${actorText} ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`;
246
+ const actorText = this.actor;
247
+
248
+ if (this.isBDD() || this.isWithin()) {
249
+ return `${this.prefix}${actorText} ${this.name} "${this.humanizeArgs()}${this.suffix}"`;
250
+ }
251
+
252
+ if (actorText === 'I') {
253
+ return `${this.prefix}${actorText} ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`;
254
+ }
255
+
256
+ return `On ${this.prefix}${actorText}: ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`;
247
257
  }
248
258
 
249
259
  humanize() {
250
- return this.name;
260
+ return humanizeString(this.name);
251
261
  }
252
262
 
253
263
  setTrace() {
@@ -311,3 +321,17 @@ function dryRunResolver() {
311
321
  },
312
322
  };
313
323
  }
324
+
325
+ function humanizeString(string) {
326
+ // split strings by words, then make them all lowercase
327
+ const _result = string.replace(/([a-z](?=[A-Z]))/g, '$1 ')
328
+ .split(' ')
329
+ .map(word => word.toLowerCase());
330
+
331
+ _result[0] = _result[0] === 'i' ? capitalizeFLetter(_result[0]) : _result[0];
332
+ return _result.join(' ').trim();
333
+ }
334
+
335
+ function capitalizeFLetter(string) {
336
+ return (string[0].toUpperCase() + string.slice(1));
337
+ }
package/lib/ui.js CHANGED
@@ -23,10 +23,11 @@ const setContextTranslation = (context) => {
23
23
  /**
24
24
  * Codecept-style interface:
25
25
  *
26
- * Feature('login')
27
- * Scenario('login as regular user', (I) {
26
+ * Feature('login');
27
+ *
28
+ * Scenario('login as regular user', ({I}) {
28
29
  * I.fillField();
29
- * I.click()
30
+ * I.click();
30
31
  * I.see('Hello, '+data.login);
31
32
  * });
32
33
  *
@@ -167,6 +168,7 @@ module.exports = function (suite) {
167
168
  context.Scenario.only = function (title, opts, fn) {
168
169
  const reString = `^${escapeRe(`${suites[0].title}: ${title}`.replace(/( \| {.+})?$/g, ''))}`;
169
170
  mocha.grep(new RegExp(reString));
171
+ process.env.SCENARIO_ONLY = true;
170
172
  return addScenario(title, opts, fn);
171
173
  };
172
174
 
package/lib/utils.js CHANGED
@@ -11,7 +11,7 @@ function deepMerge(target, source) {
11
11
  }
12
12
 
13
13
  module.exports.genTestId = (test) => {
14
- return require('crypto').createHash('md5').update(test.fullTitle()).digest('base64')
14
+ return require('crypto').createHash('sha256').update(test.fullTitle()).digest('base64')
15
15
  .slice(0, -2);
16
16
  };
17
17
 
@@ -455,3 +455,24 @@ module.exports.isNotSet = function (obj) {
455
455
  if (obj === undefined) return true;
456
456
  return false;
457
457
  };
458
+
459
+ module.exports.emptyFolder = async (directoryPath) => {
460
+ require('child_process').execSync(`rm -rf ${directoryPath}/*`);
461
+ };
462
+
463
+ module.exports.printObjectProperties = (obj) => {
464
+ if (typeof obj !== 'object' || obj === null) {
465
+ return obj;
466
+ }
467
+
468
+ let result = '';
469
+ for (const [key, value] of Object.entries(obj)) {
470
+ result += `${key}: "${value}"; `;
471
+ }
472
+
473
+ return `{${result}}`;
474
+ };
475
+
476
+ module.exports.normalizeSpacesInString = (string) => {
477
+ return string.replace(/\s+/g, ' ');
478
+ };
package/lib/workers.js CHANGED
@@ -10,12 +10,14 @@ const MochaFactory = require('./mochaFactory');
10
10
  const Container = require('./container');
11
11
  const { getTestRoot } = require('./command/utils');
12
12
  const { isFunction, fileExists } = require('./utils');
13
+ const { replaceValueDeep, deepClone } = require('./utils');
13
14
  const mainConfig = require('./config');
14
15
  const output = require('./output');
15
16
  const event = require('./event');
16
17
  const recorder = require('./recorder');
17
18
  const runHook = require('./hooks');
18
19
  const WorkerStorage = require('./workerStorage');
20
+ const collection = require('./command/run-multiple/collection');
19
21
 
20
22
  const pathToWorker = path.join(__dirname, 'command', 'workers', 'runTests.js');
21
23
 
@@ -79,15 +81,60 @@ const repackTest = (test) => {
79
81
  return test;
80
82
  };
81
83
 
82
- const createWorkerObjects = (testGroups, config, testRoot, options) => {
83
- return testGroups.map((tests, index) => {
84
- const workerObj = new WorkerObject(index);
85
- workerObj.addConfig(config);
86
- workerObj.addTests(tests);
87
- workerObj.setTestRoot(testRoot);
88
- workerObj.addOptions(options);
89
- return workerObj;
84
+ const createWorkerObjects = (testGroups, config, testRoot, options, selectedRuns) => {
85
+ selectedRuns = options && options.all && config.multiple ? Object.keys(config.multiple) : selectedRuns;
86
+ if (selectedRuns === undefined || !selectedRuns.length || config.multiple === undefined) {
87
+ return testGroups.map((tests, index) => {
88
+ const workerObj = new WorkerObject(index);
89
+ workerObj.addConfig(config);
90
+ workerObj.addTests(tests);
91
+ workerObj.setTestRoot(testRoot);
92
+ workerObj.addOptions(options);
93
+ return workerObj;
94
+ });
95
+ }
96
+ const workersToExecute = [];
97
+
98
+ const currentOutputFolder = config.output;
99
+ let currentMochawesomeReportDir;
100
+ let currentMochaJunitReporterFile;
101
+
102
+ if (config.mocha && config.mocha.reporterOptions) {
103
+ currentMochawesomeReportDir = config.mocha.reporterOptions?.mochawesome.options.reportDir;
104
+ currentMochaJunitReporterFile = config.mocha.reporterOptions['mocha-junit-reporter'].options.mochaFile;
105
+ }
106
+
107
+ collection.createRuns(selectedRuns, config).forEach((worker) => {
108
+ const separator = path.sep;
109
+ const _config = { ...config };
110
+ let workerName = worker.name.replace(':', '_');
111
+ _config.output = `${currentOutputFolder}${separator}${workerName}`;
112
+ if (config.mocha && config.mocha.reporterOptions) {
113
+ _config.mocha.reporterOptions.mochawesome.options.reportDir = `${currentMochawesomeReportDir}${separator}${workerName}`;
114
+
115
+ const _tempArray = currentMochaJunitReporterFile.split(separator);
116
+ _tempArray.splice(_tempArray.findIndex(item => item.includes('.xml')), 0, workerName);
117
+ _config.mocha.reporterOptions['mocha-junit-reporter'].options.mochaFile = _tempArray.join(separator);
118
+ }
119
+ workerName = worker.getOriginalName() || worker.getName();
120
+ const workerConfig = worker.getConfig();
121
+ workersToExecute.push(getOverridenConfig(workerName, workerConfig, _config));
90
122
  });
123
+ const workers = [];
124
+ let index = 0;
125
+ testGroups.forEach((tests) => {
126
+ const testWorkerArray = [];
127
+ workersToExecute.forEach((finalConfig) => {
128
+ const workerObj = new WorkerObject(index++);
129
+ workerObj.addConfig(finalConfig);
130
+ workerObj.addTests(tests);
131
+ workerObj.setTestRoot(testRoot);
132
+ workerObj.addOptions(options);
133
+ testWorkerArray.push(workerObj);
134
+ });
135
+ workers.push(...testWorkerArray);
136
+ });
137
+ return workers;
91
138
  };
92
139
 
93
140
  const indexOfSmallestElement = (groups) => {
@@ -115,6 +162,28 @@ const convertToMochaTests = (testGroup) => {
115
162
  return group;
116
163
  };
117
164
 
165
+ const getOverridenConfig = (workerName, workerConfig, config) => {
166
+ // clone config
167
+ const overriddenConfig = deepClone(config);
168
+
169
+ // get configuration
170
+ const browserConfig = workerConfig.browser;
171
+
172
+ for (const key in browserConfig) {
173
+ overriddenConfig.helpers = replaceValueDeep(overriddenConfig.helpers, key, browserConfig[key]);
174
+ }
175
+
176
+ // override tests configuration
177
+ if (overriddenConfig.tests) {
178
+ overriddenConfig.tests = workerConfig.tests;
179
+ }
180
+
181
+ if (overriddenConfig.gherkin && workerConfig.gherkin && workerConfig.gherkin.features) {
182
+ overriddenConfig.gherkin.features = workerConfig.gherkin.features;
183
+ }
184
+ return overriddenConfig;
185
+ };
186
+
118
187
  class WorkerObject {
119
188
  /**
120
189
  * @param {Number} workerIndex - Unique ID for worker
@@ -183,7 +252,7 @@ class Workers extends EventEmitter {
183
252
 
184
253
  _initWorkers(numberOfWorkers, config) {
185
254
  this.splitTestsByGroups(numberOfWorkers, config);
186
- this.workers = createWorkerObjects(this.testGroups, this.codecept.config, config.testConfig, config.options);
255
+ this.workers = createWorkerObjects(this.testGroups, this.codecept.config, config.testConfig, config.options, config.selectedRuns);
187
256
  this.numberOfWorkers = this.workers.length;
188
257
  }
189
258
 
@@ -290,13 +359,16 @@ class Workers extends EventEmitter {
290
359
  this.stats.start = new Date();
291
360
  recorder.startUnlessRunning();
292
361
  event.dispatcher.emit(event.workers.before);
362
+ process.env.RUNS_WITH_WORKERS = 'true';
293
363
  recorder.add('starting workers', () => {
294
364
  for (const worker of this.workers) {
295
365
  const workerThread = createWorker(worker);
296
366
  this._listenWorkerEvents(workerThread);
297
367
  }
298
368
  });
299
- return new Promise(resolve => this.on('end', resolve));
369
+ return new Promise(resolve => {
370
+ this.on('end', resolve);
371
+ });
300
372
  }
301
373
 
302
374
  /**
@@ -421,6 +493,7 @@ class Workers extends EventEmitter {
421
493
  }
422
494
 
423
495
  output.result(this.stats.passes, this.stats.failures, this.stats.pending, ms(this.stats.duration));
496
+ process.env.RUNS_WITH_WORKERS = 'false';
424
497
  }
425
498
  }
426
499