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
@@ -4,6 +4,7 @@ const figures = require('figures')
4
4
  const fs = require('fs')
5
5
  const mkdirp = require('mkdirp')
6
6
  const path = require('path')
7
+ const cheerio = require('cheerio')
7
8
 
8
9
  const Container = require('../container')
9
10
  const recorder = require('../recorder')
@@ -11,7 +12,7 @@ const event = require('../event')
11
12
  const output = require('../output')
12
13
  const { template, deleteDir } = require('../utils')
13
14
 
14
- const supportedHelpers = require('./standardActingHelpers')
15
+ const supportedHelpers = Container.STANDARD_ACTING_HELPERS
15
16
 
16
17
  const defaultConfig = {
17
18
  deleteSuccessful: true,
@@ -88,11 +89,11 @@ module.exports = function (config) {
88
89
  const pad = '0000'
89
90
  const reportDir = config.output ? path.resolve(global.codecept_dir, config.output) : defaultConfig.output
90
91
 
91
- event.dispatcher.on(event.suite.before, (suite) => {
92
+ event.dispatcher.on(event.suite.before, suite => {
92
93
  stepNum = -1
93
94
  })
94
95
 
95
- event.dispatcher.on(event.test.before, (test) => {
96
+ event.dispatcher.on(event.test.before, test => {
96
97
  const sha256hash = crypto
97
98
  .createHash('sha256')
98
99
  .update(test.file + test.title)
@@ -106,34 +107,74 @@ module.exports = function (config) {
106
107
  currentTest = test
107
108
  })
108
109
 
109
- event.dispatcher.on(event.step.failed, (step) => {
110
+ event.dispatcher.on(event.step.failed, step => {
110
111
  recorder.add('screenshot of failed test', async () => persistStep(step), true)
111
112
  })
112
113
 
113
- event.dispatcher.on(event.step.after, (step) => {
114
+ event.dispatcher.on(event.step.after, step => {
114
115
  recorder.add('screenshot of step of test', async () => persistStep(step), true)
115
116
  })
116
117
 
117
- event.dispatcher.on(event.test.passed, (test) => {
118
+ event.dispatcher.on(event.test.passed, test => {
118
119
  if (!config.deleteSuccessful) return persist(test)
119
120
  // cleanup
120
121
  deleteDir(dir)
121
122
  })
122
123
 
123
- event.dispatcher.on(event.test.failed, (test, err) => {
124
- if (test.ctx._runnable.title.includes('hook: ')) {
125
- output.plugin(
126
- 'stepByStepReport',
127
- 'BeforeSuite/AfterSuite do not have any access to the browser, hence it could not take screenshot.',
128
- )
124
+ event.dispatcher.on(event.test.failed, (test, _err, hookName) => {
125
+ if (hookName === 'BeforeSuite' || hookName === 'AfterSuite') {
126
+ // no browser here
129
127
  return
130
128
  }
131
- persist(test, err)
129
+
130
+ persist(test)
132
131
  })
133
132
 
134
133
  event.dispatcher.on(event.all.result, () => {
135
134
  if (Object.keys(recordedTests).length === 0 || !Object.keys(slides).length) return
135
+ generateRecordsHtml(recordedTests)
136
+ })
137
+
138
+ event.dispatcher.on(event.workers.result, async () => {
139
+ await recorder.add(() => {
140
+ const recordedTests = getRecordFoldersWithDetails(reportDir)
141
+ generateRecordsHtml(recordedTests)
142
+ })
143
+ })
144
+
145
+ function getRecordFoldersWithDetails(dirPath) {
146
+ let results = {}
147
+
148
+ try {
149
+ const items = fs.readdirSync(dirPath, { withFileTypes: true })
150
+
151
+ items.forEach(item => {
152
+ if (item.isDirectory() && item.name.startsWith('record_')) {
153
+ const recordFolderPath = path.join(dirPath, item.name)
154
+ const indexPath = path.join(recordFolderPath, 'index.html')
155
+
156
+ let name = ''
157
+ if (fs.existsSync(indexPath)) {
158
+ try {
159
+ const htmlContent = fs.readFileSync(indexPath, 'utf-8')
160
+ const $ = cheerio.load(htmlContent)
161
+ name = $('.navbar-brand').text().trim()
162
+ } catch (err) {
163
+ console.error(`Error reading index.html in ${recordFolderPath}:`, err.message)
164
+ }
165
+ }
166
+
167
+ results[name || 'Unkown'] = `${item.name}/index.html`
168
+ }
169
+ })
170
+ } catch (err) {
171
+ console.error(`Error reading directory ${dirPath}:`, err.message)
172
+ }
136
173
 
174
+ return results
175
+ }
176
+
177
+ function generateRecordsHtml(recordedTests) {
137
178
  let links = ''
138
179
 
139
180
  for (const link in recordedTests) {
@@ -147,10 +188,8 @@ module.exports = function (config) {
147
188
 
148
189
  fs.writeFileSync(path.join(reportDir, 'records.html'), indexHTML)
149
190
 
150
- output.print(
151
- `${figures.circleFilled} Step-by-step preview: ${colors.white.bold(`file://${reportDir}/records.html`)}`,
152
- )
153
- })
191
+ output.print(`${figures.circleFilled} Step-by-step preview: ${colors.white.bold(`file://${reportDir}/records.html`)}`)
192
+ }
154
193
 
155
194
  async function persistStep(step) {
156
195
  if (stepNum === -1) return // Ignore steps from BeforeSuite function
@@ -1,5 +1,5 @@
1
1
  const event = require('../event')
2
- const TIMEOUT_ORDER = require('../step').TIMEOUT_ORDER
2
+ const { TIMEOUT_ORDER } = require('../timeout')
3
3
 
4
4
  const defaultConfig = {
5
5
  timeout: 150,
@@ -61,12 +61,12 @@ const defaultConfig = {
61
61
  * ```
62
62
  *
63
63
  */
64
- module.exports = (config) => {
64
+ module.exports = config => {
65
65
  config = Object.assign(defaultConfig, config)
66
66
  // below override rule makes sure customTimeoutSteps go first but then they override noTimeoutSteps in case of exact pattern match
67
67
  config.customTimeoutSteps = config.customTimeoutSteps.concat(config.noTimeoutSteps).concat(config.customTimeoutSteps)
68
68
 
69
- event.dispatcher.on(event.step.before, (step) => {
69
+ event.dispatcher.on(event.step.before, step => {
70
70
  let stepTimeout
71
71
  for (let stepRule of config.customTimeoutSteps) {
72
72
  let customTimeout = 0
@@ -74,19 +74,12 @@ module.exports = (config) => {
74
74
  if (stepRule.length > 1) customTimeout = stepRule[1]
75
75
  stepRule = stepRule[0]
76
76
  }
77
- if (
78
- stepRule instanceof RegExp
79
- ? step.name.match(stepRule)
80
- : step.name === stepRule || (stepRule.indexOf('*') && step.name.startsWith(stepRule.slice(0, -1)))
81
- ) {
77
+ if (stepRule instanceof RegExp ? step.name.match(stepRule) : step.name === stepRule || (stepRule.indexOf('*') && step.name.startsWith(stepRule.slice(0, -1)))) {
82
78
  stepTimeout = customTimeout
83
79
  break
84
80
  }
85
81
  }
86
82
  stepTimeout = stepTimeout === undefined ? config.timeout : stepTimeout
87
- step.setTimeout(
88
- stepTimeout * 1000,
89
- config.overrideStepLimits ? TIMEOUT_ORDER.stepTimeoutHard : TIMEOUT_ORDER.stepTimeoutSoft,
90
- )
83
+ step.setTimeout(stepTimeout * 1000, config.overrideStepLimits ? TIMEOUT_ORDER.stepTimeoutHard : TIMEOUT_ORDER.stepTimeoutSoft)
91
84
  })
92
85
  }
@@ -29,12 +29,12 @@ let testStartedAt
29
29
  * ```
30
30
  */
31
31
  module.exports = function () {
32
- event.dispatcher.on(event.test.before, (_) => {
32
+ event.dispatcher.on(event.test.before, _ => {
33
33
  testStartedAt = Date.now()
34
34
  steps = {}
35
35
  })
36
36
 
37
- event.dispatcher.on(event.step.started, (step) => {
37
+ event.dispatcher.on(event.step.started, step => {
38
38
  const stepStartedAt = Date.now()
39
39
  step.id = uuidv4()
40
40
 
@@ -50,13 +50,13 @@ module.exports = function () {
50
50
  }
51
51
  })
52
52
 
53
- event.dispatcher.on(event.step.finished, (step) => {
53
+ event.dispatcher.on(event.step.finished, step => {
54
54
  if (step && step.id && steps[step.id]) {
55
55
  steps[step.id].end = formatTimestamp(Date.now() - testStartedAt)
56
56
  }
57
57
  })
58
58
 
59
- event.dispatcher.on(event.test.after, async (test) => {
59
+ event.dispatcher.on(event.test.after, async test => {
60
60
  if (test && test.artifacts && test.artifacts.video) {
61
61
  const stepsSortedByStartTime = Object.values(steps)
62
62
  stepsSortedByStartTime.sort((stepA, stepB) => {
@@ -1,115 +1,16 @@
1
- const recorder = require('../recorder')
2
- const { debug } = require('../output')
1
+ const { tryTo } = require('../effects')
3
2
 
4
3
  const defaultConfig = {
5
4
  registerGlobal: true,
6
5
  }
7
6
 
8
- /**
9
- *
10
- *
11
- * Adds global `tryTo` function in which all failed steps won't fail a test but will return true/false.
12
- *
13
- * Enable this plugin in `codecept.conf.js` (enabled by default for new setups):
14
- *
15
- * ```js
16
- * plugins: {
17
- * tryTo: {
18
- * enabled: true
19
- * }
20
- * }
21
- * ```
22
- * Use it in your tests:
23
- *
24
- * ```js
25
- * const result = await tryTo(() => I.see('Welcome'));
26
- *
27
- * // if text "Welcome" is on page, result => true
28
- * // if text "Welcome" is not on page, result => false
29
- * ```
30
- *
31
- * Disables retryFailedStep plugin for steps inside a block;
32
- *
33
- * Use this plugin if:
34
- *
35
- * * you need to perform multiple assertions inside a test
36
- * * there is A/B testing on a website you test
37
- * * there is "Accept Cookie" banner which may surprisingly appear on a page.
38
- *
39
- * #### Usage
40
- *
41
- * #### Multiple Conditional Assertions
42
- *
43
- * ```js
44
- *
45
- * Add assert requires first:
46
- * ```js
47
- * const assert = require('assert');
48
- * ```
49
- * Then use the assertion:
50
- * const result1 = await tryTo(() => I.see('Hello, user'));
51
- * const result2 = await tryTo(() => I.seeElement('.welcome'));
52
- * assert.ok(result1 && result2, 'Assertions were not succesful');
53
- * ```
54
- *
55
- * ##### Optional click
56
- *
57
- * ```js
58
- * I.amOnPage('/');
59
- * tryTo(() => I.click('Agree', '.cookies'));
60
- * ```
61
- *
62
- * #### Configuration
63
- *
64
- * * `registerGlobal` - to register `tryTo` function globally, true by default
65
- *
66
- * If `registerGlobal` is false you can use tryTo from the plugin:
67
- *
68
- * ```js
69
- * const tryTo = codeceptjs.container.plugins('tryTo');
70
- * ```
71
- *
72
- */
73
7
  module.exports = function (config) {
74
8
  config = Object.assign(defaultConfig, config)
9
+ console.log(`Deprecation Warning: 'tryTo' has been moved to the 'codeceptjs/effects' module. Disable tryTo plugin to remove this warning.`)
75
10
 
76
11
  if (config.registerGlobal) {
77
12
  global.tryTo = tryTo
78
13
  }
79
- return tryTo
80
- }
81
14
 
82
- function tryTo(callback) {
83
- let result = false
84
- return recorder.add(
85
- 'tryTo',
86
- () => {
87
- recorder.session.start('tryTo')
88
- process.env.TRY_TO = 'true'
89
- callback()
90
- recorder.add(() => {
91
- result = true
92
- recorder.session.restore('tryTo')
93
- return result
94
- })
95
- recorder.session.catch((err) => {
96
- result = false
97
- const msg = err.inspect ? err.inspect() : err.toString()
98
- debug(`Unsuccessful try > ${msg}`)
99
- recorder.session.restore('tryTo')
100
- return result
101
- })
102
- return recorder.add(
103
- 'result',
104
- () => {
105
- process.env.TRY_TO = undefined
106
- return result
107
- },
108
- true,
109
- false,
110
- )
111
- },
112
- false,
113
- false,
114
- )
15
+ return tryTo
115
16
  }
@@ -81,7 +81,7 @@ let restartsSession
81
81
  * * ... - additional configuration passed into services.
82
82
  *
83
83
  */
84
- module.exports = (config) => {
84
+ module.exports = config => {
85
85
  // Keep initial configs to pass as options to wdio services
86
86
  const wdioOptions = { ...config }
87
87
  const webDriver = container.helpers('WebDriver')
@@ -116,9 +116,7 @@ module.exports = (config) => {
116
116
  continue
117
117
  }
118
118
 
119
- throw new Error(
120
- `Couldn't initialize service ${name} from wdio plugin config.\nIt should be available either in '@wdio/${name.toLowerCase()}-service' package`,
121
- )
119
+ throw new Error(`Couldn't initialize service ${name} from wdio plugin config.\nIt should be available either in '@wdio/${name.toLowerCase()}-service' package`)
122
120
  }
123
121
 
124
122
  debug(`services ${services}, launchers ${launchers}`)
@@ -144,7 +142,7 @@ module.exports = (config) => {
144
142
  }
145
143
 
146
144
  if (service.afterSession) {
147
- event.dispatcher.on(event.all.result, (result) => {
145
+ event.dispatcher.on(event.all.result, result => {
148
146
  recorder.add(`service ${name} all.after`, async () => {
149
147
  await service.afterSession(result)
150
148
  })
@@ -152,21 +150,21 @@ module.exports = (config) => {
152
150
  }
153
151
 
154
152
  if (service.beforeSuite) {
155
- event.dispatcher.on(event.suite.before, (suite) => {
153
+ event.dispatcher.on(event.suite.before, suite => {
156
154
  debug(`suite started: ${suite.title}`)
157
155
  recorder.add(`service ${name} suite.before`, () => service.beforeSuite(suite))
158
156
  })
159
157
  }
160
158
 
161
159
  if (service.afterSuite) {
162
- event.dispatcher.on(event.suite.after, (suite) => {
160
+ event.dispatcher.on(event.suite.after, suite => {
163
161
  debug(`suite finished: ${suite.title}`)
164
162
  recorder.add(`service ${name} suite.after`, () => service.afterSuite(suite))
165
163
  })
166
164
  }
167
165
 
168
166
  if (service.beforeTest) {
169
- event.dispatcher.on(event.test.started, async (test) => {
167
+ event.dispatcher.on(event.test.started, async test => {
170
168
  if (test.parent) {
171
169
  test.parent.toString = () => test.parent.title
172
170
  }
@@ -181,7 +179,7 @@ module.exports = (config) => {
181
179
  }
182
180
 
183
181
  if (service.afterTest) {
184
- event.dispatcher.on(event.test.finished, async (test) => {
182
+ event.dispatcher.on(event.test.finished, async test => {
185
183
  debug(`test finished: ${test.title}`)
186
184
  await service.afterTest(test)
187
185
  })
@@ -206,7 +204,7 @@ module.exports = (config) => {
206
204
  }
207
205
 
208
206
  if (!restartsSession && service.after) {
209
- event.dispatcher.on(event.all.result, (result) => service.after(result))
207
+ event.dispatcher.on(event.all.result, result => service.after(result))
210
208
  }
211
209
  }
212
210