codeceptjs 4.0.0-beta.1 → 4.0.0-beta.10.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 (207) hide show
  1. package/README.md +133 -120
  2. package/bin/codecept.js +107 -96
  3. package/bin/test-server.js +64 -0
  4. package/docs/webapi/clearCookie.mustache +1 -1
  5. package/docs/webapi/click.mustache +5 -1
  6. package/lib/actor.js +71 -103
  7. package/lib/ai.js +159 -188
  8. package/lib/assert/empty.js +22 -24
  9. package/lib/assert/equal.js +30 -37
  10. package/lib/assert/error.js +14 -14
  11. package/lib/assert/include.js +43 -48
  12. package/lib/assert/throws.js +11 -11
  13. package/lib/assert/truth.js +22 -22
  14. package/lib/assert.js +20 -18
  15. package/lib/codecept.js +238 -162
  16. package/lib/colorUtils.js +50 -52
  17. package/lib/command/check.js +206 -0
  18. package/lib/command/configMigrate.js +56 -51
  19. package/lib/command/definitions.js +96 -109
  20. package/lib/command/dryRun.js +77 -79
  21. package/lib/command/generate.js +234 -194
  22. package/lib/command/gherkin/init.js +42 -33
  23. package/lib/command/gherkin/snippets.js +76 -74
  24. package/lib/command/gherkin/steps.js +20 -17
  25. package/lib/command/info.js +74 -38
  26. package/lib/command/init.js +300 -290
  27. package/lib/command/interactive.js +41 -32
  28. package/lib/command/list.js +28 -27
  29. package/lib/command/run-multiple/chunk.js +51 -48
  30. package/lib/command/run-multiple/collection.js +5 -5
  31. package/lib/command/run-multiple/run.js +5 -1
  32. package/lib/command/run-multiple.js +97 -97
  33. package/lib/command/run-rerun.js +19 -25
  34. package/lib/command/run-workers.js +68 -92
  35. package/lib/command/run.js +39 -27
  36. package/lib/command/utils.js +80 -64
  37. package/lib/command/workers/runTests.js +388 -226
  38. package/lib/config.js +124 -50
  39. package/lib/container.js +751 -260
  40. package/lib/data/context.js +60 -61
  41. package/lib/data/dataScenarioConfig.js +47 -47
  42. package/lib/data/dataTableArgument.js +32 -32
  43. package/lib/data/table.js +22 -22
  44. package/lib/effects.js +307 -0
  45. package/lib/element/WebElement.js +327 -0
  46. package/lib/els.js +160 -0
  47. package/lib/event.js +173 -163
  48. package/lib/globals.js +141 -0
  49. package/lib/heal.js +89 -85
  50. package/lib/helper/AI.js +131 -41
  51. package/lib/helper/ApiDataFactory.js +107 -75
  52. package/lib/helper/Appium.js +542 -404
  53. package/lib/helper/FileSystem.js +100 -79
  54. package/lib/helper/GraphQL.js +44 -43
  55. package/lib/helper/GraphQLDataFactory.js +52 -52
  56. package/lib/helper/JSONResponse.js +126 -88
  57. package/lib/helper/Mochawesome.js +54 -29
  58. package/lib/helper/Playwright.js +2547 -1316
  59. package/lib/helper/Puppeteer.js +1578 -1181
  60. package/lib/helper/REST.js +209 -68
  61. package/lib/helper/WebDriver.js +1482 -1342
  62. package/lib/helper/errors/ConnectionRefused.js +6 -6
  63. package/lib/helper/errors/ElementAssertion.js +11 -16
  64. package/lib/helper/errors/ElementNotFound.js +5 -9
  65. package/lib/helper/errors/RemoteBrowserConnectionRefused.js +5 -5
  66. package/lib/helper/extras/Console.js +11 -11
  67. package/lib/helper/extras/PlaywrightLocator.js +110 -0
  68. package/lib/helper/extras/PlaywrightPropEngine.js +18 -18
  69. package/lib/helper/extras/PlaywrightReactVueLocator.js +17 -8
  70. package/lib/helper/extras/PlaywrightRestartOpts.js +25 -11
  71. package/lib/helper/extras/Popup.js +22 -22
  72. package/lib/helper/extras/React.js +27 -28
  73. package/lib/helper/network/actions.js +36 -42
  74. package/lib/helper/network/utils.js +78 -84
  75. package/lib/helper/scripts/blurElement.js +5 -5
  76. package/lib/helper/scripts/focusElement.js +5 -5
  77. package/lib/helper/scripts/highlightElement.js +8 -8
  78. package/lib/helper/scripts/isElementClickable.js +34 -34
  79. package/lib/helper.js +2 -3
  80. package/lib/history.js +23 -19
  81. package/lib/hooks.js +8 -8
  82. package/lib/html.js +94 -104
  83. package/lib/index.js +38 -27
  84. package/lib/listener/config.js +30 -23
  85. package/lib/listener/emptyRun.js +54 -0
  86. package/lib/listener/enhancedGlobalRetry.js +110 -0
  87. package/lib/listener/exit.js +16 -18
  88. package/lib/listener/globalRetry.js +70 -0
  89. package/lib/listener/globalTimeout.js +181 -0
  90. package/lib/listener/helpers.js +76 -51
  91. package/lib/listener/mocha.js +10 -11
  92. package/lib/listener/result.js +11 -0
  93. package/lib/listener/retryEnhancer.js +85 -0
  94. package/lib/listener/steps.js +71 -59
  95. package/lib/listener/store.js +20 -0
  96. package/lib/locator.js +214 -197
  97. package/lib/mocha/asyncWrapper.js +274 -0
  98. package/lib/mocha/bdd.js +167 -0
  99. package/lib/mocha/cli.js +341 -0
  100. package/lib/mocha/factory.js +163 -0
  101. package/lib/mocha/featureConfig.js +89 -0
  102. package/lib/mocha/gherkin.js +231 -0
  103. package/lib/mocha/hooks.js +121 -0
  104. package/lib/mocha/index.js +21 -0
  105. package/lib/mocha/inject.js +46 -0
  106. package/lib/{interfaces → mocha}/scenarioConfig.js +58 -34
  107. package/lib/mocha/suite.js +89 -0
  108. package/lib/mocha/test.js +184 -0
  109. package/lib/mocha/types.d.ts +42 -0
  110. package/lib/mocha/ui.js +242 -0
  111. package/lib/output.js +141 -71
  112. package/lib/parser.js +47 -44
  113. package/lib/pause.js +173 -145
  114. package/lib/plugin/analyze.js +403 -0
  115. package/lib/plugin/{autoLogin.js → auth.js} +178 -79
  116. package/lib/plugin/autoDelay.js +36 -40
  117. package/lib/plugin/coverage.js +131 -78
  118. package/lib/plugin/customLocator.js +22 -21
  119. package/lib/plugin/customReporter.js +53 -0
  120. package/lib/plugin/enhancedRetryFailedStep.js +99 -0
  121. package/lib/plugin/heal.js +101 -110
  122. package/lib/plugin/htmlReporter.js +3648 -0
  123. package/lib/plugin/pageInfo.js +140 -0
  124. package/lib/plugin/pauseOnFail.js +12 -11
  125. package/lib/plugin/retryFailedStep.js +82 -47
  126. package/lib/plugin/screenshotOnFail.js +111 -92
  127. package/lib/plugin/stepByStepReport.js +159 -101
  128. package/lib/plugin/stepTimeout.js +20 -25
  129. package/lib/plugin/subtitles.js +38 -38
  130. package/lib/recorder.js +193 -130
  131. package/lib/rerun.js +94 -49
  132. package/lib/result.js +238 -0
  133. package/lib/retryCoordinator.js +207 -0
  134. package/lib/secret.js +20 -18
  135. package/lib/session.js +95 -89
  136. package/lib/step/base.js +239 -0
  137. package/lib/step/comment.js +10 -0
  138. package/lib/step/config.js +50 -0
  139. package/lib/step/func.js +46 -0
  140. package/lib/step/helper.js +50 -0
  141. package/lib/step/meta.js +99 -0
  142. package/lib/step/record.js +74 -0
  143. package/lib/step/retry.js +11 -0
  144. package/lib/step/section.js +55 -0
  145. package/lib/step.js +18 -329
  146. package/lib/steps.js +54 -0
  147. package/lib/store.js +38 -7
  148. package/lib/template/heal.js +3 -12
  149. package/lib/template/prompts/generatePageObject.js +31 -0
  150. package/lib/template/prompts/healStep.js +13 -0
  151. package/lib/template/prompts/writeStep.js +9 -0
  152. package/lib/test-server.js +334 -0
  153. package/lib/timeout.js +60 -0
  154. package/lib/transform.js +8 -8
  155. package/lib/translation.js +34 -21
  156. package/lib/utils/mask_data.js +47 -0
  157. package/lib/utils.js +411 -228
  158. package/lib/workerStorage.js +37 -34
  159. package/lib/workers.js +532 -296
  160. package/package.json +115 -95
  161. package/translations/de-DE.js +5 -3
  162. package/translations/fr-FR.js +5 -4
  163. package/translations/index.js +22 -12
  164. package/translations/it-IT.js +4 -3
  165. package/translations/ja-JP.js +4 -3
  166. package/translations/nl-NL.js +76 -0
  167. package/translations/pl-PL.js +4 -3
  168. package/translations/pt-BR.js +4 -3
  169. package/translations/ru-RU.js +4 -3
  170. package/translations/utils.js +10 -0
  171. package/translations/zh-CN.js +4 -3
  172. package/translations/zh-TW.js +4 -3
  173. package/typings/index.d.ts +546 -185
  174. package/typings/promiseBasedTypes.d.ts +150 -879
  175. package/typings/types.d.ts +547 -996
  176. package/lib/cli.js +0 -249
  177. package/lib/dirname.js +0 -5
  178. package/lib/helper/Expect.js +0 -425
  179. package/lib/helper/ExpectHelper.js +0 -399
  180. package/lib/helper/MockServer.js +0 -223
  181. package/lib/helper/Nightmare.js +0 -1411
  182. package/lib/helper/Protractor.js +0 -1835
  183. package/lib/helper/SoftExpectHelper.js +0 -381
  184. package/lib/helper/TestCafe.js +0 -1410
  185. package/lib/helper/clientscripts/nightmare.js +0 -213
  186. package/lib/helper/testcafe/testControllerHolder.js +0 -42
  187. package/lib/helper/testcafe/testcafe-utils.js +0 -63
  188. package/lib/interfaces/bdd.js +0 -98
  189. package/lib/interfaces/featureConfig.js +0 -69
  190. package/lib/interfaces/gherkin.js +0 -195
  191. package/lib/listener/artifacts.js +0 -19
  192. package/lib/listener/retry.js +0 -68
  193. package/lib/listener/timeout.js +0 -109
  194. package/lib/mochaFactory.js +0 -110
  195. package/lib/plugin/allure.js +0 -15
  196. package/lib/plugin/commentStep.js +0 -136
  197. package/lib/plugin/debugErrors.js +0 -67
  198. package/lib/plugin/eachElement.js +0 -127
  199. package/lib/plugin/fakerTransform.js +0 -49
  200. package/lib/plugin/retryTo.js +0 -121
  201. package/lib/plugin/selenoid.js +0 -371
  202. package/lib/plugin/standardActingHelpers.js +0 -9
  203. package/lib/plugin/tryTo.js +0 -105
  204. package/lib/plugin/wdio.js +0 -246
  205. package/lib/scenario.js +0 -222
  206. package/lib/ui.js +0 -238
  207. package/lib/within.js +0 -70
package/lib/session.js CHANGED
@@ -1,19 +1,13 @@
1
- import recorder from './recorder.js';
2
- import container from './container.js';
3
- import * as event from './event.js';
4
- import * as output from './output.js';
5
- import { store } from './store.js';
6
- import { isAsyncFunction } from './utils.js';
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.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.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.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
- export default 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|But)/)) {
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
+ import FuncStep from './func.js'
2
+
3
+ class CommentStep extends FuncStep {
4
+ constructor(name, comment) {
5
+ super(name)
6
+ this.fn = () => {}
7
+ }
8
+ }
9
+
10
+ export default CommentStep
@@ -0,0 +1,50 @@
1
+ /**
2
+ * StepConfig is a configuration object for a step.
3
+ * It is used to create a new step that is a combination of other steps.
4
+ */
5
+ class StepConfig {
6
+ constructor(opts = {}) {
7
+ /** @member {{ opts: Record<string, any>, timeout: number|undefined, retry: number|undefined }} */
8
+ this.config = {
9
+ opts,
10
+ timeout: undefined,
11
+ retry: undefined,
12
+ }
13
+ }
14
+
15
+ /**
16
+ * Set the options for the step.
17
+ * @param {object} opts - The options for the step.
18
+ * @returns {StepConfig} - The step configuration object.
19
+ */
20
+ opts(opts) {
21
+ this.config.opts = opts
22
+ return this
23
+ }
24
+
25
+ /**
26
+ * Set the timeout for the step.
27
+ * @param {number} timeout - The timeout for the step.
28
+ * @returns {StepConfig} - The step configuration object.
29
+ */
30
+ timeout(timeout) {
31
+ this.config.timeout = timeout
32
+ return this
33
+ }
34
+
35
+ /**
36
+ * Set the retry for the step.
37
+ * @param {number} retry - The retry for the step.
38
+ * @returns {StepConfig} - The step configuration object.
39
+ */
40
+ retry(retry) {
41
+ this.config.retry = retry
42
+ return this
43
+ }
44
+
45
+ getConfig() {
46
+ return this.config
47
+ }
48
+ }
49
+
50
+ export default StepConfig
@@ -0,0 +1,46 @@
1
+ import BaseStep from './base.js'
2
+ import store from '../store.js'
3
+
4
+ /**
5
+ * Function executed as a step
6
+ */
7
+ class FuncStep extends BaseStep {
8
+ // this is actual function that should be executed within step
9
+ setCallable(fn) {
10
+ this.fn = fn
11
+ }
12
+
13
+ // helper is optional, if we need to allow step to access helper methods
14
+ setHelper(helper) {
15
+ this.helper = helper
16
+ }
17
+
18
+ run() {
19
+ if (!this.fn) throw new Error('Function is not set')
20
+
21
+ // we wrap that function to track time and status
22
+ // and disable it in dry run mode
23
+ this.args = Array.prototype.slice.call(arguments)
24
+ this.startTime = +Date.now()
25
+
26
+ if (store.dryRun) {
27
+ this.setStatus('success')
28
+ // should we add Proxy and dry run resolver here?
29
+ return Promise.resolve(true)
30
+ }
31
+
32
+ let result
33
+ try {
34
+ result = this.fn.apply(this.helper, this.args)
35
+ this.setStatus('success')
36
+ this.endTime = +Date.now()
37
+ } catch (err) {
38
+ this.endTime = +Date.now()
39
+ this.setStatus('failed')
40
+ throw err
41
+ }
42
+ return result
43
+ }
44
+ }
45
+
46
+ export default FuncStep
@@ -0,0 +1,50 @@
1
+ import Step from './base.js'
2
+ import store from '../store.js'
3
+
4
+ class HelperStep extends Step {
5
+ constructor(helper, name) {
6
+ super(name)
7
+ /** @member {CodeceptJS.Helper} helper corresponding helper */
8
+ this.helper = helper
9
+ /** @member {string} helperMethod name of method to be executed */
10
+ this.helperMethod = name
11
+ }
12
+
13
+ /**
14
+ * @param {...any} args
15
+ * @return {*}
16
+ */
17
+ run() {
18
+ this.args = Array.prototype.slice.call(arguments)
19
+ this.startTime = +Date.now()
20
+
21
+ if (store.dryRun) {
22
+ this.setStatus('success')
23
+ return Promise.resolve(new Proxy({}, dryRunResolver()))
24
+ }
25
+ let result
26
+ try {
27
+ if (this.helperMethod !== 'say') {
28
+ result = this.helper[this.helperMethod].apply(this.helper, this.args)
29
+ }
30
+ this.setStatus('success')
31
+ this.endTime = +Date.now()
32
+ } catch (err) {
33
+ this.endTime = +Date.now()
34
+ this.setStatus('failed')
35
+ throw err
36
+ }
37
+ return result
38
+ }
39
+ }
40
+
41
+ export default HelperStep
42
+
43
+ function dryRunResolver() {
44
+ return {
45
+ get(target, prop) {
46
+ if (prop === 'toString') return () => '<VALUE>'
47
+ return new Proxy({}, dryRunResolver())
48
+ },
49
+ }
50
+ }