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
@@ -1,60 +1,60 @@
1
- const glob = require('glob');
2
- const path = require('path');
3
- const fs = require('fs');
1
+ const { globSync } = require('glob')
2
+ const path = require('path')
3
+ const fs = require('fs')
4
4
 
5
5
  /**
6
6
  * Splits a list to (n) parts, defined via the size argument.
7
7
  */
8
8
  const splitFiles = (list, size) => {
9
- const sets = [];
10
- const chunks = list.length / size;
11
- let i = 0;
9
+ const sets = []
10
+ const chunks = list.length / size
11
+ let i = 0
12
12
 
13
13
  while (i < chunks) {
14
- sets[i] = list.splice(0, size);
15
- i++;
14
+ sets[i] = list.splice(0, size)
15
+ i++
16
16
  }
17
17
 
18
- return sets;
19
- };
18
+ return sets
19
+ }
20
20
 
21
21
  /**
22
22
  * Executes a glob pattern and pushes the results to a list.
23
23
  */
24
- const findFiles = (pattern) => {
25
- const files = [];
24
+ const findFiles = pattern => {
25
+ const files = []
26
26
 
27
- glob.sync(pattern).forEach((file) => {
28
- files.push(path.resolve(file));
29
- });
27
+ globSync(pattern).forEach(file => {
28
+ files.push(path.resolve(file))
29
+ })
30
30
 
31
- return files;
32
- };
31
+ return files
32
+ }
33
33
 
34
34
  /**
35
35
  * Joins a list of files to a valid glob pattern
36
36
  */
37
- const flattenFiles = (list) => {
38
- const pattern = list.join(',');
39
- return pattern.indexOf(',') > -1 ? `{${pattern}}` : pattern;
40
- };
37
+ const flattenFiles = list => {
38
+ const pattern = list.join(',')
39
+ return pattern.indexOf(',') > -1 ? `{${pattern}}` : pattern
40
+ }
41
41
 
42
42
  /**
43
43
  * Greps a file by its content, checks if Scenario or Feature text'
44
44
  * matches the grep text.
45
45
  */
46
46
  const grepFile = (file, grep) => {
47
- const contents = fs.readFileSync(file, 'utf8');
48
- const pattern = new RegExp(`((Scenario|Feature)\(.*${grep}.*\))`, 'g'); // <- How future proof/solid is this?
49
- return !!pattern.exec(contents);
50
- };
47
+ const contents = fs.readFileSync(file, 'utf8')
48
+ const pattern = new RegExp(`((Scenario|Feature)\(.*${grep}.*\))`, 'g') // <- How future proof/solid is this?
49
+ return !!pattern.exec(contents)
50
+ }
51
51
 
52
- const mapFileFormats = (files) => {
52
+ const mapFileFormats = files => {
53
53
  return {
54
54
  gherkin: files.filter(file => file.match(/\.feature$/)),
55
55
  js: files.filter(file => file.match(/\.t|js$/)),
56
- };
57
- };
56
+ }
57
+ }
58
58
 
59
59
  /**
60
60
  * Creates a list of chunks incl. configuration by either dividing a list of scenario
@@ -62,30 +62,33 @@ const mapFileFormats = (files) => {
62
62
  * the splitting.
63
63
  */
64
64
  const createChunks = (config, patterns = []) => {
65
- const files = patterns.filter(pattern => !!pattern).map((pattern) => {
66
- return findFiles(pattern).filter((file) => {
67
- return config.grep ? grepFile(file, config.grep) : true;
68
- });
69
- }).reduce((acc, val) => acc.concat(val), []);
65
+ const files = patterns
66
+ .filter(pattern => !!pattern)
67
+ .map(pattern => {
68
+ return findFiles(pattern).filter(file => {
69
+ return config.grep ? grepFile(file, config.grep) : true
70
+ })
71
+ })
72
+ .reduce((acc, val) => acc.concat(val), [])
70
73
 
71
- let chunks = [];
74
+ let chunks = []
72
75
  if (typeof config.chunks === 'function') {
73
- chunks = config.chunks.call(this, files);
76
+ chunks = config.chunks.call(this, files)
74
77
  } else if (typeof config.chunks === 'number' || typeof config.chunks === 'string') {
75
- chunks = splitFiles(files, Math.ceil(files.length / config.chunks));
78
+ chunks = splitFiles(files, Math.ceil(files.length / config.chunks))
76
79
  } else {
77
- throw new Error('chunks is neither a finite number or a valid function');
80
+ throw new Error('chunks is neither a finite number or a valid function')
78
81
  }
79
82
 
80
- const chunkConfig = { ...config };
81
- delete chunkConfig.chunks;
83
+ const chunkConfig = { ...config }
84
+ delete chunkConfig.chunks
82
85
 
83
- return chunks.map((chunkFiles) => {
84
- const { js, gherkin } = mapFileFormats(chunkFiles);
85
- return { ...chunkConfig, tests: flattenFiles(js), gherkin: { features: flattenFiles(gherkin) } };
86
- });
87
- };
86
+ return chunks.map(chunkFiles => {
87
+ const { js, gherkin } = mapFileFormats(chunkFiles)
88
+ return { ...chunkConfig, tests: flattenFiles(js), gherkin: { features: flattenFiles(gherkin) } }
89
+ })
90
+ }
88
91
 
89
92
  module.exports = {
90
93
  createChunks,
91
- };
94
+ }
@@ -11,20 +11,7 @@ const { getConfig, getTestRoot, fail } = require('./utils')
11
11
  const runner = path.join(__dirname, '/../../bin/codecept')
12
12
  let config
13
13
  const childOpts = {}
14
- const copyOptions = [
15
- 'override',
16
- 'steps',
17
- 'reporter',
18
- 'verbose',
19
- 'config',
20
- 'reporter-options',
21
- 'grep',
22
- 'fgrep',
23
- 'invert',
24
- 'debug',
25
- 'plugins',
26
- 'colors',
27
- ]
14
+ const copyOptions = ['override', 'steps', 'reporter', 'verbose', 'config', 'reporter-options', 'grep', 'fgrep', 'invert', 'debug', 'plugins', 'colors']
28
15
  let overrides = {}
29
16
 
30
17
  // codeceptjs run-multiple smoke:chrome regression:firefox - will launch smoke run in chrome and regression in firefox
@@ -49,8 +36,8 @@ module.exports = async function (selectedRuns, options) {
49
36
 
50
37
  // copy opts to run
51
38
  Object.keys(options)
52
- .filter((key) => copyOptions.indexOf(key) > -1)
53
- .forEach((key) => {
39
+ .filter(key => copyOptions.indexOf(key) > -1)
40
+ .forEach(key => {
54
41
  childOpts[key] = options[key]
55
42
  })
56
43
 
@@ -96,12 +83,12 @@ module.exports = async function (selectedRuns, options) {
96
83
  config.gherkin.features = ''
97
84
  }
98
85
 
99
- const childProcessesPromise = new Promise((resolve) => {
86
+ const childProcessesPromise = new Promise(resolve => {
100
87
  processesDone = resolve
101
88
  })
102
89
 
103
90
  const runsToExecute = []
104
- collection.createRuns(selectedRuns, config).forEach((run) => {
91
+ collection.createRuns(selectedRuns, config).forEach(run => {
105
92
  const runName = run.getOriginalName() || run.getName()
106
93
  const runConfig = run.getConfig()
107
94
  runsToExecute.push(executeRun(runName, runConfig))
@@ -113,7 +100,7 @@ module.exports = async function (selectedRuns, options) {
113
100
 
114
101
  // Execute all forks
115
102
  totalSubprocessCount = runsToExecute.length
116
- runsToExecute.forEach((runToExecute) => runToExecute.call(this))
103
+ runsToExecute.forEach(runToExecute => runToExecute.call(this))
117
104
 
118
105
  return childProcessesPromise.then(async () => {
119
106
  // fire hook
@@ -149,11 +136,7 @@ function executeRun(runName, runConfig) {
149
136
  // tweaking default output directories and for mochawesome
150
137
  overriddenConfig = replaceValueDeep(overriddenConfig, 'output', path.join(config.output, outputDir))
151
138
  overriddenConfig = replaceValueDeep(overriddenConfig, 'reportDir', path.join(config.output, outputDir))
152
- overriddenConfig = replaceValueDeep(
153
- overriddenConfig,
154
- 'mochaFile',
155
- path.join(config.output, outputDir, `${browserName}_report.xml`),
156
- )
139
+ overriddenConfig = replaceValueDeep(overriddenConfig, 'mochaFile', path.join(config.output, outputDir, `${browserName}_report.xml`))
157
140
 
158
141
  // override tests configuration
159
142
  if (overriddenConfig.tests) {
@@ -165,15 +148,9 @@ function executeRun(runName, runConfig) {
165
148
  }
166
149
 
167
150
  // override grep param and collect all params
168
- const params = [
169
- 'run',
170
- '--child',
171
- `${runId++}.${runName}:${browserName}`,
172
- '--override',
173
- JSON.stringify(overriddenConfig),
174
- ]
175
-
176
- Object.keys(childOpts).forEach((key) => {
151
+ const params = ['run', '--child', `${runId++}.${runName}:${browserName}`, '--override', JSON.stringify(overriddenConfig)]
152
+
153
+ Object.keys(childOpts).forEach(key => {
177
154
  params.push(`--${key}`)
178
155
  if (childOpts[key] !== true) params.push(childOpts[key])
179
156
  })
@@ -183,7 +160,7 @@ function executeRun(runName, runConfig) {
183
160
  params.push(runConfig.grep)
184
161
  }
185
162
 
186
- const onProcessEnd = (errorCode) => {
163
+ const onProcessEnd = errorCode => {
187
164
  if (errorCode !== 0) {
188
165
  process.exitCode = errorCode
189
166
  }
@@ -197,7 +174,7 @@ function executeRun(runName, runConfig) {
197
174
  // Return function of fork for later execution
198
175
  return () =>
199
176
  fork(runner, params, { stdio: [0, 1, 2, 'ipc'] })
200
- .on('exit', (code) => {
177
+ .on('exit', code => {
201
178
  return onProcessEnd(code)
202
179
  })
203
180
  .on('error', () => {
@@ -8,15 +8,24 @@ const Workers = require('../workers')
8
8
  module.exports = async function (workerCount, selectedRuns, options) {
9
9
  process.env.profile = options.profile
10
10
 
11
- const suiteArr = []
12
- const passedTestArr = []
13
- const failedTestArr = []
14
- const skippedTestArr = []
15
- const stepArr = []
16
-
17
11
  const { config: testConfig, override = '' } = options
18
12
  const overrideConfigs = tryOrDefault(() => JSON.parse(override), {})
19
- const by = options.suites ? 'suite' : 'test'
13
+
14
+ // Determine test split strategy
15
+ let by = 'test' // default
16
+ if (options.by) {
17
+ // Explicit --by option takes precedence
18
+ by = options.by
19
+ } else if (options.suites) {
20
+ // Legacy --suites option
21
+ by = 'suite'
22
+ }
23
+
24
+ // Validate the by option
25
+ const validStrategies = ['test', 'suite', 'pool']
26
+ if (!validStrategies.includes(by)) {
27
+ throw new Error(`Invalid --by strategy: ${by}. Valid options are: ${validStrategies.join(', ')}`)
28
+ }
20
29
  delete options.parent
21
30
  const config = {
22
31
  by,
@@ -30,69 +39,24 @@ module.exports = async function (workerCount, selectedRuns, options) {
30
39
  output.print(`CodeceptJS v${require('../codecept').version()} ${output.standWithUkraine()}`)
31
40
  output.print(`Running tests in ${output.styles.bold(numberOfWorkers)} workers...`)
32
41
  output.print()
42
+ store.hasWorkers = true
33
43
 
34
44
  const workers = new Workers(numberOfWorkers, config)
35
45
  workers.overrideConfig(overrideConfigs)
36
46
 
37
- workers.on(event.suite.before, (suite) => {
38
- suiteArr.push(suite)
39
- })
40
-
41
- workers.on(event.step.passed, (step) => {
42
- stepArr.push(step)
43
- })
44
-
45
- workers.on(event.step.failed, (step) => {
46
- stepArr.push(step)
47
- })
48
-
49
- workers.on(event.test.failed, (test) => {
50
- failedTestArr.push(test)
47
+ workers.on(event.test.failed, test => {
51
48
  output.test.failed(test)
52
49
  })
53
50
 
54
- workers.on(event.test.passed, (test) => {
55
- passedTestArr.push(test)
51
+ workers.on(event.test.passed, test => {
56
52
  output.test.passed(test)
57
53
  })
58
54
 
59
- workers.on(event.test.skipped, (test) => {
60
- skippedTestArr.push(test)
55
+ workers.on(event.test.skipped, test => {
61
56
  output.test.skipped(test)
62
57
  })
63
58
 
64
- workers.on(event.all.result, () => {
65
- // expose test stats after all workers finished their execution
66
- function addStepsToTest(test, stepArr) {
67
- stepArr.test.steps.forEach((step) => {
68
- if (test.steps.length === 0) {
69
- test.steps.push(step)
70
- }
71
- })
72
- }
73
-
74
- stepArr.forEach((step) => {
75
- passedTestArr.forEach((test) => {
76
- if (step.test.title === test.title) {
77
- addStepsToTest(test, step)
78
- }
79
- })
80
-
81
- failedTestArr.forEach((test) => {
82
- if (step.test.title === test.title) {
83
- addStepsToTest(test, step)
84
- }
85
- })
86
- })
87
-
88
- event.dispatcher.emit(event.workers.result, {
89
- suites: suiteArr,
90
- tests: {
91
- passed: passedTestArr,
92
- failed: failedTestArr,
93
- skipped: skippedTestArr,
94
- },
95
- })
59
+ workers.on(event.all.result, result => {
96
60
  workers.printResults()
97
61
  })
98
62
 
@@ -100,7 +64,6 @@ module.exports = async function (workerCount, selectedRuns, options) {
100
64
  if (options.verbose || options.debug) store.debugMode = true
101
65
 
102
66
  if (options.verbose) {
103
- global.debugMode = true
104
67
  const { getMachineInfo } = require('./info')
105
68
  await getMachineInfo()
106
69
  }
@@ -29,8 +29,7 @@ module.exports.readConfig = function (configFile) {
29
29
  function getTestRoot(currentPath) {
30
30
  if (!currentPath) currentPath = '.'
31
31
  if (!path.isAbsolute(currentPath)) currentPath = path.join(process.cwd(), currentPath)
32
- currentPath =
33
- fs.lstatSync(currentPath).isDirectory() || !path.extname(currentPath) ? currentPath : path.dirname(currentPath)
32
+ currentPath = fs.lstatSync(currentPath).isDirectory() || !path.extname(currentPath) ? currentPath : path.dirname(currentPath)
34
33
  return currentPath
35
34
  }
36
35
  module.exports.getTestRoot = getTestRoot
@@ -71,7 +70,7 @@ function safeFileWrite(file, contents) {
71
70
 
72
71
  module.exports.safeFileWrite = safeFileWrite
73
72
 
74
- module.exports.captureStream = (stream) => {
73
+ module.exports.captureStream = stream => {
75
74
  let oldStream
76
75
  let buffer = ''
77
76
 
@@ -79,7 +78,7 @@ module.exports.captureStream = (stream) => {
79
78
  startCapture() {
80
79
  buffer = ''
81
80
  oldStream = stream.write.bind(stream)
82
- stream.write = (chunk) => (buffer += chunk)
81
+ stream.write = chunk => (buffer += chunk)
83
82
  },
84
83
  stopCapture() {
85
84
  if (oldStream !== undefined) stream.write = oldStream
@@ -88,7 +87,7 @@ module.exports.captureStream = (stream) => {
88
87
  }
89
88
  }
90
89
 
91
- module.exports.printError = (err) => {
90
+ module.exports.printError = err => {
92
91
  output.print('')
93
92
  output.error(err.message)
94
93
  output.print('')
@@ -106,7 +105,7 @@ module.exports.createOutputDir = (config, testRoot) => {
106
105
  }
107
106
  }
108
107
 
109
- module.exports.findConfigFile = (testsPath) => {
108
+ module.exports.findConfigFile = testsPath => {
110
109
  const extensions = ['js', 'ts']
111
110
  for (const ext of extensions) {
112
111
  const configFile = path.join(testsPath, `codecept.conf.${ext}`)