codeceptjs 4.0.0-beta.4 → 4.0.0-beta.6.esm-aria

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 (188) hide show
  1. package/README.md +89 -119
  2. package/bin/codecept.js +53 -54
  3. package/docs/webapi/clearCookie.mustache +1 -1
  4. package/lib/actor.js +70 -102
  5. package/lib/ai.js +131 -121
  6. package/lib/assert/empty.js +11 -12
  7. package/lib/assert/equal.js +16 -21
  8. package/lib/assert/error.js +2 -2
  9. package/lib/assert/include.js +11 -15
  10. package/lib/assert/throws.js +3 -5
  11. package/lib/assert/truth.js +10 -7
  12. package/lib/assert.js +18 -18
  13. package/lib/codecept.js +112 -101
  14. package/lib/colorUtils.js +48 -50
  15. package/lib/command/check.js +206 -0
  16. package/lib/command/configMigrate.js +13 -14
  17. package/lib/command/definitions.js +24 -36
  18. package/lib/command/dryRun.js +16 -16
  19. package/lib/command/generate.js +38 -39
  20. package/lib/command/gherkin/init.js +36 -38
  21. package/lib/command/gherkin/snippets.js +76 -74
  22. package/lib/command/gherkin/steps.js +21 -18
  23. package/lib/command/info.js +49 -15
  24. package/lib/command/init.js +41 -37
  25. package/lib/command/interactive.js +22 -13
  26. package/lib/command/list.js +11 -10
  27. package/lib/command/run-multiple/chunk.js +50 -47
  28. package/lib/command/run-multiple/collection.js +5 -5
  29. package/lib/command/run-multiple/run.js +3 -3
  30. package/lib/command/run-multiple.js +27 -47
  31. package/lib/command/run-rerun.js +6 -7
  32. package/lib/command/run-workers.js +15 -66
  33. package/lib/command/run.js +8 -8
  34. package/lib/command/utils.js +22 -21
  35. package/lib/command/workers/runTests.js +131 -241
  36. package/lib/config.js +111 -49
  37. package/lib/container.js +589 -244
  38. package/lib/data/context.js +16 -18
  39. package/lib/data/dataScenarioConfig.js +9 -9
  40. package/lib/data/dataTableArgument.js +7 -7
  41. package/lib/data/table.js +6 -12
  42. package/lib/effects.js +307 -0
  43. package/lib/els.js +160 -0
  44. package/lib/event.js +24 -19
  45. package/lib/globals.js +141 -0
  46. package/lib/heal.js +89 -81
  47. package/lib/helper/AI.js +3 -2
  48. package/lib/helper/ApiDataFactory.js +19 -19
  49. package/lib/helper/Appium.js +47 -51
  50. package/lib/helper/FileSystem.js +35 -15
  51. package/lib/helper/GraphQL.js +1 -1
  52. package/lib/helper/GraphQLDataFactory.js +4 -4
  53. package/lib/helper/JSONResponse.js +72 -45
  54. package/lib/helper/Mochawesome.js +14 -11
  55. package/lib/helper/Playwright.js +832 -434
  56. package/lib/helper/Puppeteer.js +393 -292
  57. package/lib/helper/REST.js +32 -27
  58. package/lib/helper/WebDriver.js +320 -219
  59. package/lib/helper/errors/ConnectionRefused.js +6 -6
  60. package/lib/helper/errors/ElementAssertion.js +11 -16
  61. package/lib/helper/errors/ElementNotFound.js +5 -9
  62. package/lib/helper/errors/RemoteBrowserConnectionRefused.js +5 -5
  63. package/lib/helper/extras/Console.js +11 -11
  64. package/lib/helper/extras/PlaywrightLocator.js +110 -0
  65. package/lib/helper/extras/PlaywrightPropEngine.js +18 -18
  66. package/lib/helper/extras/PlaywrightRestartOpts.js +23 -23
  67. package/lib/helper/extras/Popup.js +22 -22
  68. package/lib/helper/extras/React.js +29 -30
  69. package/lib/helper/network/actions.js +33 -48
  70. package/lib/helper/network/utils.js +76 -83
  71. package/lib/helper/scripts/blurElement.js +6 -6
  72. package/lib/helper/scripts/focusElement.js +6 -6
  73. package/lib/helper/scripts/highlightElement.js +9 -9
  74. package/lib/helper/scripts/isElementClickable.js +34 -34
  75. package/lib/helper.js +2 -1
  76. package/lib/history.js +23 -20
  77. package/lib/hooks.js +10 -10
  78. package/lib/html.js +90 -100
  79. package/lib/index.js +48 -21
  80. package/lib/listener/config.js +8 -9
  81. package/lib/listener/emptyRun.js +54 -0
  82. package/lib/listener/exit.js +10 -12
  83. package/lib/listener/{retry.js → globalRetry.js} +10 -10
  84. package/lib/listener/globalTimeout.js +166 -0
  85. package/lib/listener/helpers.js +43 -24
  86. package/lib/listener/mocha.js +4 -5
  87. package/lib/listener/result.js +11 -0
  88. package/lib/listener/steps.js +26 -23
  89. package/lib/listener/store.js +20 -0
  90. package/lib/locator.js +213 -192
  91. package/lib/mocha/asyncWrapper.js +264 -0
  92. package/lib/mocha/bdd.js +167 -0
  93. package/lib/mocha/cli.js +341 -0
  94. package/lib/mocha/factory.js +160 -0
  95. package/lib/{interfaces → mocha}/featureConfig.js +33 -13
  96. package/lib/{interfaces → mocha}/gherkin.js +75 -45
  97. package/lib/mocha/hooks.js +121 -0
  98. package/lib/mocha/index.js +21 -0
  99. package/lib/mocha/inject.js +46 -0
  100. package/lib/{interfaces → mocha}/scenarioConfig.js +32 -8
  101. package/lib/mocha/suite.js +89 -0
  102. package/lib/mocha/test.js +178 -0
  103. package/lib/mocha/types.d.ts +42 -0
  104. package/lib/mocha/ui.js +229 -0
  105. package/lib/output.js +86 -64
  106. package/lib/parser.js +44 -44
  107. package/lib/pause.js +160 -139
  108. package/lib/plugin/analyze.js +403 -0
  109. package/lib/plugin/{autoLogin.js → auth.js} +137 -43
  110. package/lib/plugin/autoDelay.js +19 -15
  111. package/lib/plugin/coverage.js +22 -27
  112. package/lib/plugin/customLocator.js +5 -5
  113. package/lib/plugin/customReporter.js +53 -0
  114. package/lib/plugin/heal.js +49 -17
  115. package/lib/plugin/pageInfo.js +140 -0
  116. package/lib/plugin/pauseOnFail.js +4 -3
  117. package/lib/plugin/retryFailedStep.js +60 -19
  118. package/lib/plugin/screenshotOnFail.js +80 -83
  119. package/lib/plugin/stepByStepReport.js +70 -31
  120. package/lib/plugin/stepTimeout.js +7 -13
  121. package/lib/plugin/subtitles.js +10 -9
  122. package/lib/recorder.js +167 -126
  123. package/lib/rerun.js +94 -50
  124. package/lib/result.js +161 -0
  125. package/lib/secret.js +18 -17
  126. package/lib/session.js +95 -89
  127. package/lib/step/base.js +239 -0
  128. package/lib/step/comment.js +10 -0
  129. package/lib/step/config.js +50 -0
  130. package/lib/step/func.js +46 -0
  131. package/lib/step/helper.js +50 -0
  132. package/lib/step/meta.js +99 -0
  133. package/lib/step/record.js +74 -0
  134. package/lib/step/retry.js +11 -0
  135. package/lib/step/section.js +55 -0
  136. package/lib/step.js +18 -332
  137. package/lib/steps.js +54 -0
  138. package/lib/store.js +37 -5
  139. package/lib/template/heal.js +2 -11
  140. package/lib/timeout.js +60 -0
  141. package/lib/transform.js +8 -8
  142. package/lib/translation.js +32 -18
  143. package/lib/utils.js +354 -250
  144. package/lib/workerStorage.js +16 -16
  145. package/lib/workers.js +366 -282
  146. package/package.json +107 -95
  147. package/translations/de-DE.js +5 -4
  148. package/translations/fr-FR.js +5 -4
  149. package/translations/index.js +23 -9
  150. package/translations/it-IT.js +5 -4
  151. package/translations/ja-JP.js +5 -4
  152. package/translations/nl-NL.js +76 -0
  153. package/translations/pl-PL.js +5 -4
  154. package/translations/pt-BR.js +5 -4
  155. package/translations/ru-RU.js +5 -4
  156. package/translations/utils.js +18 -0
  157. package/translations/zh-CN.js +5 -4
  158. package/translations/zh-TW.js +5 -4
  159. package/typings/index.d.ts +177 -186
  160. package/typings/promiseBasedTypes.d.ts +3573 -5941
  161. package/typings/types.d.ts +4042 -6370
  162. package/lib/cli.js +0 -256
  163. package/lib/helper/ExpectHelper.js +0 -391
  164. package/lib/helper/Nightmare.js +0 -1504
  165. package/lib/helper/Protractor.js +0 -1863
  166. package/lib/helper/SoftExpectHelper.js +0 -381
  167. package/lib/helper/TestCafe.js +0 -1414
  168. package/lib/helper/clientscripts/nightmare.js +0 -213
  169. package/lib/helper/extras/PlaywrightReactVueLocator.js +0 -43
  170. package/lib/helper/testcafe/testControllerHolder.js +0 -42
  171. package/lib/helper/testcafe/testcafe-utils.js +0 -62
  172. package/lib/interfaces/bdd.js +0 -81
  173. package/lib/listener/artifacts.js +0 -19
  174. package/lib/listener/timeout.js +0 -109
  175. package/lib/mochaFactory.js +0 -113
  176. package/lib/plugin/allure.js +0 -15
  177. package/lib/plugin/commentStep.js +0 -136
  178. package/lib/plugin/debugErrors.js +0 -67
  179. package/lib/plugin/eachElement.js +0 -127
  180. package/lib/plugin/fakerTransform.js +0 -49
  181. package/lib/plugin/retryTo.js +0 -127
  182. package/lib/plugin/selenoid.js +0 -384
  183. package/lib/plugin/standardActingHelpers.js +0 -3
  184. package/lib/plugin/tryTo.js +0 -115
  185. package/lib/plugin/wdio.js +0 -249
  186. package/lib/scenario.js +0 -224
  187. package/lib/ui.js +0 -236
  188. package/lib/within.js +0 -70
package/lib/result.js ADDED
@@ -0,0 +1,161 @@
1
+ import fs from 'fs'
2
+ import path from 'path'
3
+ import { serializeTest } from './mocha/test.js'
4
+
5
+ /**
6
+ * Result of the test run
7
+ *
8
+ * @typedef {Object} Stats
9
+ * @property {number} passes
10
+ * @property {number} failures
11
+ * @property {number} tests
12
+ * @property {number} pending
13
+ * @property {number} failedHooks
14
+ * @property {Date} start
15
+ * @property {Date} end
16
+ * @property {number} duration
17
+ */
18
+ class Result {
19
+ /**
20
+ * Create Result of the test run
21
+ */
22
+ constructor() {
23
+ this._startTime = new Date()
24
+ this._endTime = null
25
+
26
+ this.reset()
27
+ this.start()
28
+ }
29
+
30
+ reset() {
31
+ this._stats = {
32
+ passes: 0,
33
+ failures: 0,
34
+ tests: 0,
35
+ pending: 0,
36
+ failedHooks: 0,
37
+ start: null,
38
+ end: null,
39
+ duration: 0,
40
+ }
41
+
42
+ /** @type {CodeceptJS.Test[]} */
43
+ this._tests = []
44
+
45
+ /** @type {String[]} */
46
+ this._failures = []
47
+ }
48
+
49
+ start() {
50
+ this._startTime = new Date()
51
+ }
52
+
53
+ finish() {
54
+ this._endTime = new Date()
55
+ }
56
+
57
+ get hasFailed() {
58
+ return this._stats.failures > 0
59
+ }
60
+
61
+ get tests() {
62
+ return this._tests
63
+ }
64
+
65
+ get failures() {
66
+ return this._failures.filter(f => f && (!Array.isArray(f) || f.length > 0))
67
+ }
68
+
69
+ get stats() {
70
+ return this._stats
71
+ }
72
+
73
+ get startTime() {
74
+ return this._startTime
75
+ }
76
+
77
+ /**
78
+ * Add test to result
79
+ *
80
+ * @param {CodeceptJS.Test} test
81
+ */
82
+ addTest(test) {
83
+ const existingTestIndex = this._tests.findIndex(t => !!t.uid && t.uid === test.uid)
84
+ if (existingTestIndex >= 0) {
85
+ this._tests[existingTestIndex] = test
86
+ return
87
+ }
88
+
89
+ this._tests.push(test)
90
+ }
91
+
92
+ /**
93
+ * Add failures to result
94
+ *
95
+ * @param {String[]} newFailures
96
+ */
97
+ addFailures(newFailures) {
98
+ this._failures.push(...newFailures)
99
+ }
100
+
101
+ get hasFailures() {
102
+ return this.stats.failures > 0
103
+ }
104
+
105
+ get duration() {
106
+ return this._endTime ? +this._endTime - +this._startTime : 0
107
+ }
108
+
109
+ get failedTests() {
110
+ return this._tests.filter(test => test.state === 'failed')
111
+ }
112
+
113
+ get passedTests() {
114
+ return this._tests.filter(test => test.state === 'passed')
115
+ }
116
+
117
+ get skippedTests() {
118
+ return this._tests.filter(test => test.state === 'skipped' || test.state === 'pending')
119
+ }
120
+
121
+ simplify() {
122
+ return {
123
+ hasFailed: this.hasFailed,
124
+ stats: this.stats,
125
+ duration: this.duration,
126
+ tests: this._tests.map(test => serializeTest(test)),
127
+ failures: this._failures,
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Save result to json file
133
+ *
134
+ * @param {string} fileName
135
+ */
136
+ save(fileName) {
137
+ if (!fileName) fileName = 'result.json'
138
+ fs.writeFileSync(path.join(global.output_dir, fileName), JSON.stringify(this.simplify(), null, 2))
139
+ }
140
+
141
+ /**
142
+ * Add stats to result
143
+ *
144
+ * @param {object} newStats
145
+ */
146
+ addStats(newStats = {}) {
147
+ this._stats.passes += newStats.passes || 0
148
+ this._stats.failures += newStats.failures || 0
149
+ this._stats.tests += newStats.tests || 0
150
+ this._stats.pending += newStats.pending || 0
151
+ this._stats.failedHooks += newStats.failedHooks || 0
152
+
153
+ // do not override start time
154
+ this._stats.start = this._stats.start || newStats.start
155
+
156
+ this._stats.end = newStats.end || this._stats.end
157
+ this._stats.duration = newStats.duration
158
+ }
159
+ }
160
+
161
+ export default Result
package/lib/secret.js CHANGED
@@ -1,20 +1,20 @@
1
- const { deepClone } = require('./utils');
1
+ import { deepClone } from './utils.js'
2
2
 
3
- const maskedString = '*****';
3
+ const maskedString = '*****'
4
4
 
5
5
  /** @param {string} secret */
6
6
  class Secret {
7
7
  constructor(secret) {
8
- this._secret = secret;
8
+ this._secret = secret
9
9
  }
10
10
 
11
11
  /** @returns {string} */
12
12
  toString() {
13
- return this._secret;
13
+ return this._secret
14
14
  }
15
15
 
16
16
  getMasked() {
17
- return maskedString;
17
+ return maskedString
18
18
  }
19
19
 
20
20
  /**
@@ -23,11 +23,11 @@ class Secret {
23
23
  */
24
24
  static secret(secret) {
25
25
  if (typeof secret === 'object') {
26
- const fields = Array.from(arguments);
27
- fields.shift();
28
- return secretObject(secret, fields);
26
+ const fields = Array.from(arguments)
27
+ fields.shift()
28
+ return secretObject(secret, fields)
29
29
  }
30
- return new Secret(secret);
30
+ return new Secret(secret)
31
31
  }
32
32
  }
33
33
 
@@ -36,16 +36,17 @@ function secretObject(obj, fieldsToHide = []) {
36
36
  get(obj, prop) {
37
37
  if (prop === 'toString') {
38
38
  return function () {
39
- const maskedObject = deepClone(obj);
40
- fieldsToHide.forEach(f => maskedObject[f] = maskedString);
41
- return JSON.stringify(maskedObject);
42
- };
39
+ const maskedObject = deepClone(obj)
40
+ fieldsToHide.forEach(f => (maskedObject[f] = maskedString))
41
+ return JSON.stringify(maskedObject)
42
+ }
43
43
  }
44
- return fieldsToHide.includes(prop) ? new Secret(obj[prop]) : obj[prop];
44
+ return fieldsToHide.includes(prop) ? new Secret(obj[prop]) : obj[prop]
45
45
  },
46
- };
46
+ }
47
47
 
48
- return new Proxy(obj, handler);
48
+ return new Proxy(obj, handler)
49
49
  }
50
50
 
51
- module.exports = Secret;
51
+ export default Secret
52
+ export const secret = Secret.secret
package/lib/session.js CHANGED
@@ -1,19 +1,13 @@
1
- const recorder = require('./recorder');
2
- const container = require('./container');
3
- const event = require('./event');
4
- const output = require('./output');
5
- const store = require('./store');
6
- const { isAsyncFunction } = require('./utils');
7
-
8
- const sessionColors = [
9
- 'cyan',
10
- 'blue',
11
- 'red',
12
- 'magenta',
13
- 'yellow',
14
- ];
15
-
16
- const savedSessions = {};
1
+ import recorder from './recorder.js'
2
+ import container from './container.js'
3
+ import event from './event.js'
4
+ import output from './output.js'
5
+ import store from './store.js'
6
+ import { isAsyncFunction } from './utils.js'
7
+
8
+ const sessionColors = ['cyan', 'blue', 'red', 'magenta', 'yellow']
9
+
10
+ const savedSessions = {}
17
11
 
18
12
  /**
19
13
  * @param {CodeceptJS.LocatorOrString} sessionName
@@ -24,109 +18,121 @@ const savedSessions = {};
24
18
  function session(sessionName, config, fn) {
25
19
  if (typeof config === 'function') {
26
20
  if (typeof fn === 'function') {
27
- config = config();
21
+ config = config()
28
22
  } else {
29
23
  // no config but with function
30
- fn = config;
31
- config = {};
24
+ fn = config
25
+ config = {}
32
26
  }
33
27
  }
34
28
  // session helpers won't use beforeSuite and afterSuite hooks...
35
29
  // restart: false options are not allowed as well
36
30
  // but those helpers should be already started so inside listener/helpers.js the `_init` method should already be called
37
31
 
38
- const helpers = container.helpers();
32
+ const helpers = container.helpers()
39
33
 
40
34
  if (!savedSessions[sessionName]) {
41
35
  for (const helper in helpers) {
42
- if (!helpers[helper]._session) continue;
36
+ if (!helpers[helper]._session) continue
43
37
  savedSessions[sessionName] = {
44
38
  start: () => null,
45
39
  stop: () => null,
46
40
  loadVars: () => null,
47
41
  restoreVars: () => null,
48
42
  ...(store.dryRun ? {} : helpers[helper]._session()),
49
- };
50
- break;
43
+ }
44
+ break
51
45
  }
52
46
 
53
47
  const closeBrowsers = () => {
54
- const session = savedSessions[sessionName];
55
- if (!session) return;
56
- session.stop(session.vars);
57
- delete savedSessions[sessionName];
58
- };
48
+ const session = savedSessions[sessionName]
49
+ if (!session) return
50
+ session.stop(session.vars)
51
+ delete savedSessions[sessionName]
52
+ }
59
53
 
60
54
  event.dispatcher.once(event.test.after, () => {
61
- recorder.add('close session browsers', closeBrowsers);
62
- });
55
+ recorder.add('close session browsers', closeBrowsers)
56
+ })
63
57
 
64
58
  if (!savedSessions[sessionName]) {
65
- throw new Error('Configured helpers do not support starting sessions. Please use a helper with session support.');
59
+ throw new Error('Configured helpers do not support starting sessions. Please use a helper with session support.')
66
60
  }
67
61
 
68
62
  recorder.add('save vars', async () => {
69
- savedSessions[sessionName].vars = await savedSessions[sessionName].start(sessionName, config);
70
- });
63
+ savedSessions[sessionName].vars = await savedSessions[sessionName].start(sessionName, config)
64
+ })
71
65
  }
72
66
 
73
67
  // pick random color
74
- const color = sessionColors[Object.keys(savedSessions).indexOf(sessionName) % sessionColors.length];
75
-
76
- const addContextToStep = (step) => {
77
- step.actor = `${output.colors[color](sessionName)}: I`;
78
- };
79
-
80
- if (!fn) return; // no current session steps
81
-
82
- return recorder.add('register session wrapper', async () => {
83
- const session = savedSessions[sessionName];
84
- recorder.session.start(`session:${sessionName}`);
85
- event.dispatcher.on(event.step.after, addContextToStep);
86
- recorder.add('switch to browser', () => {
87
- const session = savedSessions[sessionName];
88
- return session.loadVars(session.vars);
89
- });
90
-
91
- const finalize = () => {
92
- recorder.add('Finalize session', async () => {
93
- output.stepShift = 0;
94
- event.dispatcher.removeListener(event.step.after, addContextToStep);
95
- await session.restoreVars();
96
- recorder.session.restore(`session:${sessionName}`);
97
- });
98
- };
99
-
100
- // Indicate when executing this function that we are in a session
101
- if (isAsyncFunction(fn)) {
102
- return fn.apply(null).then((res) => {
103
- finalize();
104
- return recorder.promise().then(() => res);
105
- }).catch((e) => {
106
- output.stepShift = 0;
107
- session.restoreVars(sessionName);
108
- event.dispatcher.removeListener(event.step.after, addContextToStep);
109
- recorder.throw(e);
110
- return recorder.promise();
111
- });
112
- }
68
+ const color = sessionColors[Object.keys(savedSessions).indexOf(sessionName) % sessionColors.length]
113
69
 
114
- let res;
115
- try {
116
- res = fn.apply(null);
117
- } catch (err) {
118
- recorder.throw(err);
119
- } finally {
120
- recorder.catch((e) => {
121
- session.restoreVars(sessionName);
122
- output.stepShift = 0;
123
- event.dispatcher.removeListener(event.step.after, addContextToStep);
124
- throw e;
125
- });
126
- }
127
- finalize();
128
- return recorder.promise().then(() => res);
129
- }, false, false);
70
+ const addContextToStep = step => {
71
+ step.actor = `${output.colors[color](sessionName)}: I`
72
+ }
73
+
74
+ if (!fn) return // no current session steps
75
+
76
+ return recorder.add(
77
+ 'register session wrapper',
78
+ async () => {
79
+ const session = savedSessions[sessionName]
80
+ if (!session) {
81
+ throw new Error(`Session "${sessionName}" not found. It may have been closed already.`)
82
+ }
83
+ recorder.session.start(`session:${sessionName}`)
84
+ event.dispatcher.on(event.step.after, addContextToStep)
85
+ recorder.add('switch to browser', () => {
86
+ const session = savedSessions[sessionName]
87
+ return session.loadVars(session.vars)
88
+ })
89
+
90
+ const finalize = () => {
91
+ recorder.add('Finalize session', async () => {
92
+ output.stepShift = 0
93
+ event.dispatcher.removeListener(event.step.after, addContextToStep)
94
+ await session.restoreVars()
95
+ recorder.session.restore(`session:${sessionName}`)
96
+ })
97
+ }
98
+
99
+ // Indicate when executing this function that we are in a session
100
+ if (isAsyncFunction(fn)) {
101
+ return fn
102
+ .apply(null)
103
+ .then(async res => {
104
+ finalize()
105
+ await recorder.promise()
106
+ return res
107
+ })
108
+ .catch(e => {
109
+ output.stepShift = 0
110
+ session.restoreVars(sessionName)
111
+ event.dispatcher.removeListener(event.step.after, addContextToStep)
112
+ recorder.throw(e)
113
+ return recorder.promise()
114
+ })
115
+ }
116
+
117
+ let res
118
+ try {
119
+ res = fn.apply(null)
120
+ } catch (err) {
121
+ recorder.throw(err)
122
+ } finally {
123
+ recorder.catch(e => {
124
+ session.restoreVars(sessionName)
125
+ output.stepShift = 0
126
+ event.dispatcher.removeListener(event.step.after, addContextToStep)
127
+ throw e
128
+ })
129
+ }
130
+ finalize()
131
+ return recorder.promise().then(() => res)
132
+ },
133
+ false,
134
+ false,
135
+ )
130
136
  }
131
137
 
132
- module.exports = session;
138
+ export default session
@@ -0,0 +1,239 @@
1
+ import color from 'chalk'
2
+ import Secret from '../secret.js'
3
+ import { getCurrentTimeout } from '../timeout.js'
4
+ import { ucfirst, humanizeString, serializeError } from '../utils.js'
5
+ import recordStep from './record.js'
6
+
7
+ const STACK_LINE = 5
8
+
9
+ /**
10
+ * Each command in test executed through `I.` object is wrapped in Step.
11
+ * Step allows logging executed commands and triggers hook before and after step execution.
12
+ * @param {string} name
13
+ */
14
+ class Step {
15
+ constructor(name) {
16
+ /** @member {string} */
17
+ this.name = name
18
+ /** @member {Map<number, number>} */
19
+ this.timeouts = new Map()
20
+
21
+ /** @member {Array<*>} */
22
+ this.args = []
23
+
24
+ /** @member {Record<string,any>} */
25
+ this.opts = {}
26
+ /** @member {string} */
27
+ this.actor = 'I' // I = actor
28
+
29
+ /** @member {string} */
30
+ this.status = 'pending'
31
+ /** @member {string} */
32
+ this.prefix = this.suffix = ''
33
+ /** @member {string} */
34
+ this.comment = ''
35
+ /** @member {any} */
36
+ this.metaStep = undefined
37
+ /** @member {string} */
38
+ this.stack = ''
39
+
40
+ // These are part of HelperStep class
41
+ // but left here for types compatibility
42
+ /** @member {any} */
43
+ this.helper = null
44
+ /** @member {string} */
45
+ this.helperMethod = name
46
+
47
+ this.startTime = 0
48
+ this.endTime = 0
49
+
50
+ this.setTrace()
51
+ }
52
+
53
+ setMetaStep(metaStep) {
54
+ this.metaStep = metaStep
55
+ }
56
+
57
+ run() {
58
+ throw new Error('Not implemented')
59
+ }
60
+
61
+ addToRecorder(args) {
62
+ return recordStep(this, args)
63
+ }
64
+
65
+ /**
66
+ * @returns {number|undefined}
67
+ */
68
+ get timeout() {
69
+ return getCurrentTimeout(this.timeouts)
70
+ }
71
+
72
+ /**
73
+ * @param {number} timeout - timeout in milliseconds or 0 if no timeout
74
+ * @param {number} order - order defines the priority of timeout, timeouts set with lower order override those set with higher order.
75
+ * When order below 0 value of timeout only override if new value is lower
76
+ */
77
+ setTimeout(timeout, order) {
78
+ this.timeouts.set(order, timeout)
79
+ }
80
+
81
+ /** @function */
82
+ setTrace() {
83
+ Error.captureStackTrace(this)
84
+ }
85
+
86
+ /** @param {Array<*>} args */
87
+ setArguments(args) {
88
+ this.args = args
89
+ }
90
+
91
+ setActor(actor) {
92
+ this.actor = actor || ''
93
+ }
94
+
95
+ /** @param {string} status */
96
+ setStatus(status) {
97
+ this.status = status
98
+ if (this.metaStep) {
99
+ this.metaStep.setStatus(status)
100
+ }
101
+ }
102
+
103
+ /** @return {string} */
104
+ humanize() {
105
+ return humanizeString(this.name)
106
+ }
107
+
108
+ /** @return {string} */
109
+ humanizeArgs() {
110
+ return this.args
111
+ .map(arg => {
112
+ if (!arg) {
113
+ return ''
114
+ }
115
+ if (typeof arg === 'string') {
116
+ return `"${arg}"`
117
+ }
118
+ if (Array.isArray(arg)) {
119
+ try {
120
+ const res = JSON.stringify(arg)
121
+ return res
122
+ } catch (err) {
123
+ return `[${arg.toString()}]`
124
+ }
125
+ } else if (typeof arg === 'function') {
126
+ return arg.toString()
127
+ } else if (typeof arg === 'undefined') {
128
+ return `${arg}`
129
+ } else if (arg instanceof Secret) {
130
+ return arg.getMasked()
131
+ } else if (arg.toString && arg.toString() !== '[object Object]') {
132
+ return arg.toString()
133
+ } else if (typeof arg === 'object') {
134
+ const returnedArg = {}
135
+ for (const [key, value] of Object.entries(arg)) {
136
+ returnedArg[key] = value
137
+ if (value instanceof Secret) returnedArg[key] = value.getMasked()
138
+ }
139
+ return JSON.stringify(returnedArg)
140
+ }
141
+ return arg
142
+ })
143
+ .join(', ')
144
+ }
145
+
146
+ /** @return {string} */
147
+ line() {
148
+ const lines = this.stack.split('\n')
149
+ if (lines[STACK_LINE]) {
150
+ return lines[STACK_LINE].trim()
151
+ .replace(global.codecept_dir || '', '.')
152
+ .trim()
153
+ }
154
+ return ''
155
+ }
156
+
157
+ /** @return {string} */
158
+ toString() {
159
+ return ucfirst(`${this.prefix}${this.actor} ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`).trim()
160
+ }
161
+
162
+ /** @return {string} */
163
+ toCliStyled() {
164
+ return `${this.prefix}${this.actor} ${color.italic(this.humanize())} ${color.yellow(this.humanizeArgs())}${this.suffix}`
165
+ }
166
+
167
+ /** @return {string} */
168
+ toCode() {
169
+ return `${this.prefix}${this.actor}.${this.name}(${this.humanizeArgs()})${this.suffix}`
170
+ }
171
+
172
+ isMetaStep() {
173
+ return this.constructor.name === 'MetaStep'
174
+ }
175
+
176
+ get duration() {
177
+ if (!this.startTime || !this.endTime) return 0
178
+ return this.endTime - this.startTime
179
+ }
180
+
181
+ simplify() {
182
+ const step = this
183
+
184
+ const parent = {}
185
+ if (step.metaStep) {
186
+ parent.title = step.metaStep.actor
187
+ }
188
+
189
+ if (step.opts) {
190
+ Object.keys(step.opts).forEach(k => {
191
+ if (typeof step.opts[k] === 'object') delete step.opts[k]
192
+ if (typeof step.opts[k] === 'function') delete step.opts[k]
193
+ })
194
+ }
195
+
196
+ const args = []
197
+ if (step.args) {
198
+ for (const arg of step.args) {
199
+ // check if arg is a JOI object
200
+ if (arg && typeof arg === 'function') {
201
+ args.push(arg.name)
202
+ } else if (typeof arg == 'string') {
203
+ args.push(arg)
204
+ } else if (arg) {
205
+ args.push((JSON.stringify(arg) || '').slice(0, 300))
206
+ }
207
+ }
208
+ }
209
+
210
+ return {
211
+ opts: step.opts || {},
212
+ title: step.name,
213
+ args: args,
214
+ status: step.status,
215
+ startTime: step.startTime,
216
+ endTime: step.endTime,
217
+ parent,
218
+ }
219
+ }
220
+
221
+ /** @return {boolean} */
222
+ hasBDDAncestor() {
223
+ let hasBDD = false
224
+ let processingStep
225
+ processingStep = this
226
+
227
+ while (processingStep.metaStep) {
228
+ if (processingStep.metaStep.actor?.match(/^(Given|When|Then|And)/)) {
229
+ hasBDD = true
230
+ break
231
+ } else {
232
+ processingStep = processingStep.metaStep
233
+ }
234
+ }
235
+ return hasBDD
236
+ }
237
+ }
238
+
239
+ export default Step
@@ -0,0 +1,10 @@
1
+ const FuncStep = require('./func')
2
+
3
+ class CommentStep extends FuncStep {
4
+ constructor(name, comment) {
5
+ super(name)
6
+ this.fn = () => {}
7
+ }
8
+ }
9
+
10
+ module.exports = CommentStep