codeceptjs 4.0.0-beta.4 → 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 (150) 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 +139 -87
  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/generate.js +10 -14
  17. package/lib/command/gherkin/snippets.js +75 -73
  18. package/lib/command/gherkin/steps.js +1 -1
  19. package/lib/command/info.js +42 -8
  20. package/lib/command/init.js +13 -12
  21. package/lib/command/interactive.js +10 -2
  22. package/lib/command/list.js +1 -1
  23. package/lib/command/run-multiple/chunk.js +48 -45
  24. package/lib/command/run-multiple.js +12 -35
  25. package/lib/command/run-workers.js +21 -58
  26. package/lib/command/utils.js +5 -6
  27. package/lib/command/workers/runTests.js +262 -220
  28. package/lib/container.js +386 -238
  29. package/lib/data/context.js +10 -13
  30. package/lib/data/dataScenarioConfig.js +8 -8
  31. package/lib/data/dataTableArgument.js +6 -6
  32. package/lib/data/table.js +5 -11
  33. package/lib/effects.js +223 -0
  34. package/lib/element/WebElement.js +327 -0
  35. package/lib/els.js +158 -0
  36. package/lib/event.js +21 -17
  37. package/lib/heal.js +88 -80
  38. package/lib/helper/AI.js +2 -1
  39. package/lib/helper/ApiDataFactory.js +3 -6
  40. package/lib/helper/Appium.js +47 -51
  41. package/lib/helper/FileSystem.js +3 -3
  42. package/lib/helper/GraphQLDataFactory.js +3 -3
  43. package/lib/helper/JSONResponse.js +75 -37
  44. package/lib/helper/Mochawesome.js +31 -9
  45. package/lib/helper/Nightmare.js +35 -53
  46. package/lib/helper/Playwright.js +262 -267
  47. package/lib/helper/Protractor.js +54 -77
  48. package/lib/helper/Puppeteer.js +246 -260
  49. package/lib/helper/REST.js +5 -17
  50. package/lib/helper/TestCafe.js +21 -44
  51. package/lib/helper/WebDriver.js +151 -170
  52. package/lib/helper/extras/Popup.js +22 -22
  53. package/lib/helper/testcafe/testcafe-utils.js +26 -27
  54. package/lib/listener/emptyRun.js +55 -0
  55. package/lib/listener/exit.js +7 -10
  56. package/lib/listener/{retry.js → globalRetry.js} +5 -5
  57. package/lib/listener/globalTimeout.js +165 -0
  58. package/lib/listener/helpers.js +15 -15
  59. package/lib/listener/mocha.js +1 -1
  60. package/lib/listener/result.js +12 -0
  61. package/lib/listener/retryEnhancer.js +85 -0
  62. package/lib/listener/steps.js +32 -18
  63. package/lib/listener/store.js +20 -0
  64. package/lib/mocha/asyncWrapper.js +231 -0
  65. package/lib/{interfaces → mocha}/bdd.js +3 -3
  66. package/lib/mocha/cli.js +308 -0
  67. package/lib/mocha/factory.js +104 -0
  68. package/lib/{interfaces → mocha}/featureConfig.js +32 -12
  69. package/lib/{interfaces → mocha}/gherkin.js +26 -28
  70. package/lib/mocha/hooks.js +112 -0
  71. package/lib/mocha/index.js +12 -0
  72. package/lib/mocha/inject.js +29 -0
  73. package/lib/{interfaces → mocha}/scenarioConfig.js +31 -7
  74. package/lib/mocha/suite.js +82 -0
  75. package/lib/mocha/test.js +181 -0
  76. package/lib/mocha/types.d.ts +42 -0
  77. package/lib/mocha/ui.js +232 -0
  78. package/lib/output.js +82 -62
  79. package/lib/pause.js +160 -138
  80. package/lib/plugin/analyze.js +396 -0
  81. package/lib/plugin/auth.js +435 -0
  82. package/lib/plugin/autoDelay.js +8 -8
  83. package/lib/plugin/autoLogin.js +3 -338
  84. package/lib/plugin/commentStep.js +6 -1
  85. package/lib/plugin/coverage.js +10 -19
  86. package/lib/plugin/customLocator.js +3 -3
  87. package/lib/plugin/customReporter.js +52 -0
  88. package/lib/plugin/eachElement.js +1 -1
  89. package/lib/plugin/fakerTransform.js +1 -1
  90. package/lib/plugin/heal.js +36 -9
  91. package/lib/plugin/htmlReporter.js +1947 -0
  92. package/lib/plugin/pageInfo.js +140 -0
  93. package/lib/plugin/retryFailedStep.js +17 -18
  94. package/lib/plugin/retryTo.js +2 -113
  95. package/lib/plugin/screenshotOnFail.js +17 -58
  96. package/lib/plugin/selenoid.js +15 -35
  97. package/lib/plugin/standardActingHelpers.js +4 -1
  98. package/lib/plugin/stepByStepReport.js +56 -17
  99. package/lib/plugin/stepTimeout.js +5 -12
  100. package/lib/plugin/subtitles.js +4 -4
  101. package/lib/plugin/tryTo.js +3 -102
  102. package/lib/plugin/wdio.js +8 -10
  103. package/lib/recorder.js +155 -124
  104. package/lib/rerun.js +43 -42
  105. package/lib/result.js +161 -0
  106. package/lib/secret.js +1 -1
  107. package/lib/step/base.js +239 -0
  108. package/lib/step/comment.js +10 -0
  109. package/lib/step/config.js +50 -0
  110. package/lib/step/func.js +46 -0
  111. package/lib/step/helper.js +50 -0
  112. package/lib/step/meta.js +99 -0
  113. package/lib/step/record.js +74 -0
  114. package/lib/step/retry.js +11 -0
  115. package/lib/step/section.js +55 -0
  116. package/lib/step.js +21 -332
  117. package/lib/steps.js +50 -0
  118. package/lib/store.js +37 -5
  119. package/lib/template/heal.js +2 -11
  120. package/lib/test-server.js +323 -0
  121. package/lib/timeout.js +66 -0
  122. package/lib/utils.js +351 -218
  123. package/lib/within.js +75 -55
  124. package/lib/workerStorage.js +2 -1
  125. package/lib/workers.js +386 -276
  126. package/package.json +76 -70
  127. package/translations/de-DE.js +4 -3
  128. package/translations/fr-FR.js +4 -3
  129. package/translations/index.js +1 -0
  130. package/translations/it-IT.js +4 -3
  131. package/translations/ja-JP.js +4 -3
  132. package/translations/nl-NL.js +76 -0
  133. package/translations/pl-PL.js +4 -3
  134. package/translations/pt-BR.js +4 -3
  135. package/translations/ru-RU.js +4 -3
  136. package/translations/utils.js +9 -0
  137. package/translations/zh-CN.js +4 -3
  138. package/translations/zh-TW.js +4 -3
  139. package/typings/index.d.ts +188 -186
  140. package/typings/promiseBasedTypes.d.ts +18 -705
  141. package/typings/types.d.ts +301 -804
  142. package/lib/cli.js +0 -256
  143. package/lib/helper/ExpectHelper.js +0 -391
  144. package/lib/helper/SoftExpectHelper.js +0 -381
  145. package/lib/listener/artifacts.js +0 -19
  146. package/lib/listener/timeout.js +0 -109
  147. package/lib/mochaFactory.js +0 -113
  148. package/lib/plugin/debugErrors.js +0 -67
  149. package/lib/scenario.js +0 -224
  150. package/lib/ui.js +0 -236
@@ -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)
package/lib/codecept.js CHANGED
@@ -1,14 +1,15 @@
1
- const { existsSync, readFileSync } = require('fs');
2
- const glob = require('glob');
3
- const fsPath = require('path');
4
- const { resolve } = require('path');
5
-
6
- const container = require('./container');
7
- const Config = require('./config');
8
- const event = require('./event');
9
- const runHook = require('./hooks');
10
- const output = require('./output');
11
- const { emptyFolder } = require('./utils');
1
+ const { existsSync, readFileSync } = require('fs')
2
+ const { globSync } = require('glob')
3
+ const shuffle = require('lodash.shuffle')
4
+ const fsPath = require('path')
5
+ const { resolve } = require('path')
6
+
7
+ const container = require('./container')
8
+ const Config = require('./config')
9
+ const event = require('./event')
10
+ const runHook = require('./hooks')
11
+ const output = require('./output')
12
+ const { emptyFolder } = require('./utils')
12
13
 
13
14
  /**
14
15
  * CodeceptJS runner
@@ -22,10 +23,10 @@ class Codecept {
22
23
  * @param {*} opts
23
24
  */
24
25
  constructor(config, opts) {
25
- this.config = Config.create(config);
26
- this.opts = opts;
27
- this.testFiles = new Array(0);
28
- this.requireModules(config.require);
26
+ this.config = Config.create(config)
27
+ this.opts = opts
28
+ this.testFiles = new Array(0)
29
+ this.requireModules(config.require)
29
30
  }
30
31
 
31
32
  /**
@@ -35,13 +36,13 @@ class Codecept {
35
36
  */
36
37
  requireModules(requiringModules) {
37
38
  if (requiringModules) {
38
- requiringModules.forEach((requiredModule) => {
39
- const isLocalFile = existsSync(requiredModule) || existsSync(`${requiredModule}.js`);
39
+ requiringModules.forEach(requiredModule => {
40
+ const isLocalFile = existsSync(requiredModule) || existsSync(`${requiredModule}.js`)
40
41
  if (isLocalFile) {
41
- requiredModule = resolve(requiredModule);
42
+ requiredModule = resolve(requiredModule)
42
43
  }
43
- require(requiredModule);
44
- });
44
+ require(requiredModule)
45
+ })
45
46
  }
46
47
  }
47
48
 
@@ -52,10 +53,10 @@ class Codecept {
52
53
  * @param {string} dir
53
54
  */
54
55
  init(dir) {
55
- this.initGlobals(dir);
56
+ this.initGlobals(dir)
56
57
  // initializing listeners
57
- container.create(this.config, this.opts);
58
- this.runHooks();
58
+ container.create(this.config, this.opts)
59
+ this.runHooks()
59
60
  }
60
61
 
61
62
  /**
@@ -64,37 +65,37 @@ class Codecept {
64
65
  * @param {string} dir
65
66
  */
66
67
  initGlobals(dir) {
67
- global.codecept_dir = dir;
68
- global.output_dir = fsPath.resolve(dir, this.config.output);
68
+ global.codecept_dir = dir
69
+ global.output_dir = fsPath.resolve(dir, this.config.output)
69
70
 
70
- if (this.config.emptyOutputFolder) emptyFolder(global.output_dir);
71
+ if (this.config.emptyOutputFolder) emptyFolder(global.output_dir)
71
72
 
72
73
  if (!this.config.noGlobals) {
73
- global.Helper = global.codecept_helper = require('@codeceptjs/helper');
74
- global.actor = global.codecept_actor = require('./actor');
75
- global.pause = require('./pause');
76
- global.within = require('./within');
77
- global.session = require('./session');
78
- global.DataTable = require('./data/table');
79
- global.locate = locator => require('./locator').build(locator);
80
- global.inject = container.support;
81
- global.share = container.share;
82
- global.secret = require('./secret').secret;
83
- global.codecept_debug = output.debug;
84
- global.codeceptjs = require('./index'); // load all objects
74
+ global.Helper = global.codecept_helper = require('@codeceptjs/helper')
75
+ global.actor = global.codecept_actor = require('./actor')
76
+ global.pause = require('./pause')
77
+ global.within = require('./within')
78
+ global.session = require('./session')
79
+ global.DataTable = require('./data/table')
80
+ global.locate = locator => require('./locator').build(locator)
81
+ global.inject = container.support
82
+ global.share = container.share
83
+ global.secret = require('./secret').secret
84
+ global.codecept_debug = output.debug
85
+ global.codeceptjs = require('./index') // load all objects
85
86
 
86
87
  // BDD
87
- const stepDefinitions = require('./interfaces/bdd');
88
- global.Given = stepDefinitions.Given;
89
- global.When = stepDefinitions.When;
90
- global.Then = stepDefinitions.Then;
91
- global.DefineParameterType = stepDefinitions.defineParameterType;
88
+ const stepDefinitions = require('./mocha/bdd')
89
+ global.Given = stepDefinitions.Given
90
+ global.When = stepDefinitions.When
91
+ global.Then = stepDefinitions.Then
92
+ global.DefineParameterType = stepDefinitions.defineParameterType
92
93
 
93
94
  // debug mode
94
- global.debugMode = false;
95
+ global.debugMode = false
95
96
 
96
97
  // mask sensitive data
97
- global.maskSensitiveData = this.config.maskSensitiveData || false;
98
+ global.maskSensitiveData = this.config.maskSensitiveData || false
98
99
  }
99
100
  }
100
101
 
@@ -103,16 +104,19 @@ class Codecept {
103
104
  */
104
105
  runHooks() {
105
106
  // default hooks
106
- runHook(require('./listener/steps'));
107
- runHook(require('./listener/artifacts'));
108
- runHook(require('./listener/config'));
109
- runHook(require('./listener/helpers'));
110
- runHook(require('./listener/retry'));
111
- runHook(require('./listener/timeout'));
112
- runHook(require('./listener/exit'));
107
+ runHook(require('./listener/store'))
108
+ runHook(require('./listener/steps'))
109
+ runHook(require('./listener/config'))
110
+ runHook(require('./listener/result'))
111
+ runHook(require('./listener/helpers'))
112
+ runHook(require('./listener/globalTimeout'))
113
+ runHook(require('./listener/globalRetry'))
114
+ runHook(require('./listener/retryEnhancer'))
115
+ runHook(require('./listener/exit'))
116
+ runHook(require('./listener/emptyRun'))
113
117
 
114
118
  // custom hooks (previous iteration of plugins)
115
- this.config.hooks.forEach(hook => runHook(hook));
119
+ this.config.hooks.forEach(hook => runHook(hook))
116
120
  }
117
121
 
118
122
  /**
@@ -120,7 +124,7 @@ class Codecept {
120
124
  *
121
125
  */
122
126
  async bootstrap() {
123
- return runHook(this.config.bootstrap, 'bootstrap');
127
+ return runHook(this.config.bootstrap, 'bootstrap')
124
128
  }
125
129
 
126
130
  /**
@@ -128,7 +132,7 @@ class Codecept {
128
132
 
129
133
  */
130
134
  async teardown() {
131
- return runHook(this.config.teardown, 'teardown');
135
+ return runHook(this.config.teardown, 'teardown')
132
136
  }
133
137
 
134
138
  /**
@@ -139,43 +143,89 @@ class Codecept {
139
143
  loadTests(pattern) {
140
144
  const options = {
141
145
  cwd: global.codecept_dir,
142
- };
146
+ }
143
147
 
144
- let patterns = [pattern];
148
+ let patterns = [pattern]
145
149
  if (!pattern) {
146
- patterns = [];
150
+ patterns = []
147
151
 
148
152
  // If the user wants to test a specific set of test files as an array or string.
149
153
  if (this.config.tests && !this.opts.features) {
150
154
  if (Array.isArray(this.config.tests)) {
151
- patterns.push(...this.config.tests);
155
+ patterns.push(...this.config.tests)
152
156
  } else {
153
- patterns.push(this.config.tests);
157
+ patterns.push(this.config.tests)
154
158
  }
155
159
  }
156
160
 
157
161
  if (this.config.gherkin.features && !this.opts.tests) {
158
162
  if (Array.isArray(this.config.gherkin.features)) {
159
163
  this.config.gherkin.features.forEach(feature => {
160
- patterns.push(feature);
161
- });
164
+ patterns.push(feature)
165
+ })
162
166
  } else {
163
- patterns.push(this.config.gherkin.features);
167
+ patterns.push(this.config.gherkin.features)
164
168
  }
165
169
  }
166
170
  }
167
171
 
168
172
  for (pattern of patterns) {
169
- glob.sync(pattern, options).forEach((file) => {
170
- if (file.includes('node_modules')) return;
171
- if (!fsPath.isAbsolute(file)) {
172
- file = fsPath.join(global.codecept_dir, file);
173
- }
174
- if (!this.testFiles.includes(fsPath.resolve(file))) {
175
- this.testFiles.push(fsPath.resolve(file));
176
- }
177
- });
173
+ if (pattern) {
174
+ globSync(pattern, options).forEach(file => {
175
+ if (file.includes('node_modules')) return
176
+ if (!fsPath.isAbsolute(file)) {
177
+ file = fsPath.join(global.codecept_dir, file)
178
+ }
179
+ if (!this.testFiles.includes(fsPath.resolve(file))) {
180
+ this.testFiles.push(fsPath.resolve(file))
181
+ }
182
+ })
183
+ }
184
+ }
185
+
186
+ if (this.opts.shuffle) {
187
+ this.testFiles = shuffle(this.testFiles)
188
+ }
189
+
190
+ if (this.opts.shard) {
191
+ this.testFiles = this._applySharding(this.testFiles, this.opts.shard)
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Apply sharding to test files based on shard configuration
197
+ *
198
+ * @param {Array<string>} testFiles - Array of test file paths
199
+ * @param {string} shardConfig - Shard configuration in format "index/total" (e.g., "1/4")
200
+ * @returns {Array<string>} - Filtered array of test files for this shard
201
+ */
202
+ _applySharding(testFiles, shardConfig) {
203
+ const shardMatch = shardConfig.match(/^(\d+)\/(\d+)$/)
204
+ if (!shardMatch) {
205
+ throw new Error('Invalid shard format. Expected format: "index/total" (e.g., "1/4")')
206
+ }
207
+
208
+ const shardIndex = parseInt(shardMatch[1], 10)
209
+ const shardTotal = parseInt(shardMatch[2], 10)
210
+
211
+ if (shardTotal < 1) {
212
+ throw new Error('Shard total must be at least 1')
213
+ }
214
+
215
+ if (shardIndex < 1 || shardIndex > shardTotal) {
216
+ throw new Error(`Shard index ${shardIndex} must be between 1 and ${shardTotal}`)
178
217
  }
218
+
219
+ if (testFiles.length === 0) {
220
+ return testFiles
221
+ }
222
+
223
+ // Calculate which tests belong to this shard
224
+ const shardSize = Math.ceil(testFiles.length / shardTotal)
225
+ const startIndex = (shardIndex - 1) * shardSize
226
+ const endIndex = Math.min(startIndex + shardSize, testFiles.length)
227
+
228
+ return testFiles.slice(startIndex, endIndex)
179
229
  }
180
230
 
181
231
  /**
@@ -185,34 +235,36 @@ class Codecept {
185
235
  * @returns {Promise<void>}
186
236
  */
187
237
  async run(test) {
238
+ await container.started()
239
+
188
240
  return new Promise((resolve, reject) => {
189
- const mocha = container.mocha();
190
- mocha.files = this.testFiles;
241
+ const mocha = container.mocha()
242
+ mocha.files = this.testFiles
191
243
  if (test) {
192
244
  if (!fsPath.isAbsolute(test)) {
193
- test = fsPath.join(global.codecept_dir, test);
245
+ test = fsPath.join(global.codecept_dir, test)
194
246
  }
195
- mocha.files = mocha.files.filter(t => fsPath.basename(t, '.js') === test || t === test);
247
+ mocha.files = mocha.files.filter(t => fsPath.basename(t, '.js') === test || t === test)
196
248
  }
197
249
  const done = () => {
198
- event.emit(event.all.result, this);
199
- event.emit(event.all.after, this);
200
- resolve();
201
- };
250
+ event.emit(event.all.result, container.result())
251
+ event.emit(event.all.after, this)
252
+ resolve()
253
+ }
202
254
 
203
255
  try {
204
- event.emit(event.all.before, this);
205
- mocha.run(() => done());
256
+ event.emit(event.all.before, this)
257
+ mocha.run(() => done())
206
258
  } catch (e) {
207
- output.error(e.stack);
208
- reject(e);
259
+ output.error(e.stack)
260
+ reject(e)
209
261
  }
210
- });
262
+ })
211
263
  }
212
264
 
213
265
  static version() {
214
- return JSON.parse(readFileSync(`${__dirname}/../package.json`, 'utf8')).version;
266
+ return JSON.parse(readFileSync(`${__dirname}/../package.json`, 'utf8')).version
215
267
  }
216
268
  }
217
269
 
218
- module.exports = Codecept;
270
+ module.exports = Codecept
@@ -0,0 +1,201 @@
1
+ const { getConfig, getTestRoot } = require('./utils')
2
+ const Codecept = require('../codecept')
3
+ const output = require('../output')
4
+ const store = require('../store')
5
+ const container = require('../container')
6
+ const figures = require('figures')
7
+ const chalk = require('chalk')
8
+ const { createTest } = require('../mocha/test')
9
+ const { getMachineInfo } = require('./info')
10
+ const definitions = require('./definitions')
11
+
12
+ module.exports = async function (options) {
13
+ const configFile = options.config
14
+
15
+ setTimeout(() => {
16
+ output.error("Something went wrong. Checks didn't pass and timed out. Please check your config and helpers.")
17
+ process.exit(1)
18
+ }, options.timeout || 50000)
19
+
20
+ const checks = {
21
+ config: false,
22
+ container: false,
23
+ pageObjects: false,
24
+ plugins: false,
25
+ ai: true, // we don't need to check AI
26
+ helpers: false,
27
+ setup: false,
28
+ teardown: false,
29
+ tests: false,
30
+ def: false,
31
+ }
32
+
33
+ const testRoot = getTestRoot(configFile)
34
+ let config = getConfig(configFile)
35
+
36
+ try {
37
+ config = getConfig(configFile)
38
+ checks['config'] = true
39
+ } catch (err) {
40
+ checks['config'] = err
41
+ }
42
+
43
+ printCheck('config', checks['config'], config.name)
44
+
45
+ let codecept
46
+ try {
47
+ codecept = new Codecept(config, options)
48
+ codecept.init(testRoot)
49
+ await container.started()
50
+ checks.container = true
51
+ } catch (err) {
52
+ checks.container = err
53
+ }
54
+
55
+ const standardActingHelpers = container.STANDARD_ACTING_HELPERS
56
+
57
+ printCheck('container', checks['container'])
58
+
59
+ if (codecept) {
60
+ try {
61
+ if (options.bootstrap) await codecept.bootstrap()
62
+ checks.bootstrap = true
63
+ } catch (err) {
64
+ checks.bootstrap = err
65
+ }
66
+ printCheck('bootstrap', checks['bootstrap'], options.bootstrap ? 'Bootstrap was executed' : 'No bootstrap command')
67
+ }
68
+
69
+ let numTests = 0
70
+ if (codecept) {
71
+ try {
72
+ codecept.loadTests()
73
+ const mocha = container.mocha()
74
+ mocha.files = codecept.testFiles
75
+ mocha.loadFiles()
76
+ mocha.suite.suites.forEach(suite => {
77
+ numTests += suite.tests.length
78
+ })
79
+ if (numTests > 0) {
80
+ checks.tests = true
81
+ } else {
82
+ throw new Error('No tests found')
83
+ }
84
+ } catch (err) {
85
+ checks.tests = err
86
+ }
87
+ }
88
+
89
+ if (config?.ai?.request) {
90
+ checks.ai = true
91
+ printCheck('ai', checks['ai'], 'Configuration is enabled, request function is set')
92
+ } else {
93
+ printCheck('ai', checks['ai'], 'Disabled')
94
+ }
95
+
96
+ printCheck('tests', checks['tests'], `Total: ${numTests} tests`)
97
+
98
+ store.dryRun = true
99
+
100
+ const helpers = container.helpers()
101
+
102
+ try {
103
+ if (!Object.keys(helpers).length) throw new Error('No helpers found')
104
+ // load helpers
105
+ for (const helper of Object.values(helpers)) {
106
+ if (helper._init) helper._init()
107
+ }
108
+ checks.helpers = true
109
+ } catch (err) {
110
+ checks.helpers = err
111
+ }
112
+
113
+ printCheck('helpers', checks['helpers'], `${Object.keys(helpers).join(', ')}`)
114
+
115
+ const pageObjects = container.support()
116
+
117
+ try {
118
+ if (Object.keys(pageObjects).length) {
119
+ for (const pageObject of Object.values(pageObjects)) {
120
+ pageObject.name
121
+ }
122
+ }
123
+ checks.pageObjects = true
124
+ } catch (err) {
125
+ checks.pageObjects = err
126
+ }
127
+ printCheck('page objects', checks['pageObjects'], `Total: ${Object.keys(pageObjects).length} support objects`)
128
+
129
+ checks.plugins = true // how to check plugins?
130
+ printCheck('plugins', checks['plugins'], Object.keys(container.plugins()).join(', '))
131
+
132
+ if (Object.keys(helpers).length) {
133
+ const suite = container.mocha().suite
134
+ const test = createTest('test', () => {})
135
+ checks.setup = true
136
+ for (const helper of Object.values(helpers)) {
137
+ try {
138
+ if (helper._beforeSuite) await helper._beforeSuite(suite)
139
+ if (helper._before) await helper._before(test)
140
+ } catch (err) {
141
+ err.message = `${helper.constructor.name} helper: ${err.message}`
142
+ if (checks.setup instanceof Error) err.message = `${err.message}\n\n${checks.setup?.message || ''}`.trim()
143
+ checks.setup = err
144
+ }
145
+ }
146
+
147
+ printCheck('Helpers Before', checks['setup'], standardActingHelpers.some(h => Object.keys(helpers).includes(h)) ? 'Initializing browser' : '')
148
+
149
+ checks.teardown = true
150
+ for (const helper of Object.values(helpers).reverse()) {
151
+ try {
152
+ if (helper._passed) await helper._passed(test)
153
+ if (helper._after) await helper._after(test)
154
+ if (helper._finishTest) await helper._finishTest(suite)
155
+ if (helper._afterSuite) await helper._afterSuite(suite)
156
+ } catch (err) {
157
+ err.message = `${helper.constructor.name} helper: ${err.message}`
158
+ if (checks.teardown instanceof Error) err.message = `${err.message}\n\n${checks.teardown?.message || ''}`.trim()
159
+ checks.teardown = err
160
+ }
161
+ }
162
+
163
+ printCheck('Helpers After', checks['teardown'], standardActingHelpers.some(h => Object.keys(helpers).includes(h)) ? 'Closing browser' : '')
164
+ }
165
+
166
+ try {
167
+ definitions(configFile, { dryRun: true })
168
+ checks.def = true
169
+ } catch (err) {
170
+ checks.def = err
171
+ }
172
+
173
+ printCheck('TypeScript Definitions', checks['def'])
174
+
175
+ output.print('')
176
+
177
+ if (!Object.values(checks).every(check => check === true)) {
178
+ output.error("Something went wrong. Checks didn't pass.")
179
+ output.print()
180
+ await getMachineInfo()
181
+ process.exit(1)
182
+ }
183
+
184
+ output.print(output.styles.success('All checks passed'.toUpperCase()), 'Ready to run your tests 🚀')
185
+ process.exit(0)
186
+ }
187
+
188
+ function printCheck(name, value, comment = '') {
189
+ let status = ''
190
+ if (value == true) {
191
+ status += chalk.bold.green(figures.tick)
192
+ } else {
193
+ status += chalk.bold.red(figures.cross)
194
+ }
195
+
196
+ if (value instanceof Error) {
197
+ comment = `${comment} ${chalk.red(value.message)}`.trim()
198
+ }
199
+
200
+ output.print(status, name.toUpperCase(), chalk.dim(comment))
201
+ }
@@ -14,9 +14,7 @@ module.exports = function (initPath) {
14
14
 
15
15
  print()
16
16
  print(` Welcome to ${colors.magenta.bold('CodeceptJS')} configuration migration tool`)
17
- print(
18
- ` It will help you switch from ${colors.cyan.bold('.json')} to ${colors.magenta.bold('.js')} config format at ease`,
19
- )
17
+ print(` It will help you switch from ${colors.cyan.bold('.json')} to ${colors.magenta.bold('.js')} config format at ease`)
20
18
  print()
21
19
 
22
20
  if (!path) {
@@ -53,7 +51,7 @@ module.exports = function (initPath) {
53
51
  default: true,
54
52
  },
55
53
  ])
56
- .then((result) => {
54
+ .then(result => {
57
55
  if (result.configFile) {
58
56
  const jsonConfigFile = path.join(testsPath, 'codecept.js')
59
57
  const config = JSON.parse(fs.readFileSync(jsonConfigFile, 'utf8'))