codeceptjs 4.0.0-rc.21 → 4.0.0-rc.23
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.
- package/docs/advanced.md +1 -1
- package/docs/architecture.md +219 -0
- package/docs/configuration.md +82 -127
- package/docs/continuous-integration.md +113 -151
- package/docs/custom-helpers.md +1 -1
- package/docs/hooks.md +76 -277
- package/docs/installation.md +95 -40
- package/docs/parallel.md +98 -496
- package/docs/plugins.md +43 -0
- package/docs/reports.md +102 -401
- package/docs/retry.md +44 -37
- package/docs/typescript.md +54 -269
- package/lib/actor.js +1 -1
- package/lib/command/workers/runTests.js +1 -5
- package/lib/heal.js +2 -2
- package/lib/helper/Playwright.js +10 -4
- package/lib/plugin/aiTrace.js +4 -3
- package/lib/plugin/junitReporter.js +303 -0
- package/lib/plugin/retryFailedStep.js +4 -3
- package/lib/plugin/screencast.js +1 -1
- package/lib/plugin/screenshot.js +2 -2
- package/lib/plugin/stepTimeout.js +2 -1
- package/lib/step/base.js +7 -7
- package/lib/step/comment.js +2 -2
- package/lib/step/helper.js +4 -4
- package/lib/step/meta.js +3 -3
- package/lib/step/record.js +3 -3
- package/package.json +1 -1
- package/docs/internal-api.md +0 -265
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import os from 'os'
|
|
4
|
+
import { mkdirp } from 'mkdirp'
|
|
5
|
+
import { DOMImplementation, XMLSerializer } from '@xmldom/xmldom'
|
|
6
|
+
|
|
7
|
+
import event from '../event.js'
|
|
8
|
+
import store from '../store.js'
|
|
9
|
+
import output from '../output.js'
|
|
10
|
+
|
|
11
|
+
const defaultConfig = {
|
|
12
|
+
outputName: 'report.xml',
|
|
13
|
+
output: null,
|
|
14
|
+
testGroupName: 'CodeceptJS',
|
|
15
|
+
attachSteps: true,
|
|
16
|
+
attachMeta: true,
|
|
17
|
+
stepsInFailure: true,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const INVALID_XML_CHARS = new RegExp('[\\u0000-\\u0008\\u000B\\u000C\\u000E-\\u001F\\uFFFE\\uFFFF]', 'g')
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
*
|
|
24
|
+
* Generates a JUnit-compatible XML report after a test run.
|
|
25
|
+
*
|
|
26
|
+
* Unlike Mocha's `mocha-junit-reporter`, this plugin understands CodeceptJS steps and substeps.
|
|
27
|
+
* For every `<testcase>` it includes:
|
|
28
|
+
*
|
|
29
|
+
* * `<properties>` — the test's meta information: every `meta` key from `Scenario('...', { meta })`, plus its `tags` and `retries`
|
|
30
|
+
* * `<system-out>` — an indented step/substep log (substeps are nested under their meta step); only failed steps are marked
|
|
31
|
+
* * `<failure>` — for failed tests: the error message, type, stack trace and (optionally) the step trace
|
|
32
|
+
*
|
|
33
|
+
* The produced file is consumable by Jenkins, GitLab CI, CircleCI, GitHub Actions test reporters, etc.
|
|
34
|
+
*
|
|
35
|
+
* #### Configuration
|
|
36
|
+
*
|
|
37
|
+
* ```js
|
|
38
|
+
* "plugins": {
|
|
39
|
+
* "junitReporter": {
|
|
40
|
+
* "enabled": true
|
|
41
|
+
* }
|
|
42
|
+
* }
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* Possible config options:
|
|
46
|
+
*
|
|
47
|
+
* * `outputName`: file name for the report. Default: `report.xml`.
|
|
48
|
+
* * `output`: directory where the report is stored, relative to the project root. Default: the `output` directory.
|
|
49
|
+
* * `testGroupName`: value of the `name` attribute on the root `<testsuites>` element. Default: `CodeceptJS`.
|
|
50
|
+
* * `attachMeta`: add the test's meta information (`meta` keys, `tags`, `retries`) as `<properties>`. Default: true.
|
|
51
|
+
* * `attachSteps`: add the step/substep log as `<system-out>`. Default: true.
|
|
52
|
+
* * `stepsInFailure`: append the step trace to the `<failure>` body. Default: true.
|
|
53
|
+
*
|
|
54
|
+
* CLI examples:
|
|
55
|
+
*
|
|
56
|
+
* ```
|
|
57
|
+
* npx codeceptjs run -p junitReporter
|
|
58
|
+
* npx codeceptjs run -p junitReporter:outputName=junit.xml
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* > ℹ When running with `run-workers`, steps are serialized between processes and substep nesting is flattened.
|
|
62
|
+
*
|
|
63
|
+
* @param {*} config
|
|
64
|
+
*/
|
|
65
|
+
export default function (config = {}) {
|
|
66
|
+
config = Object.assign({}, defaultConfig, config)
|
|
67
|
+
|
|
68
|
+
let written = false
|
|
69
|
+
|
|
70
|
+
const writeReport = result => {
|
|
71
|
+
if (written) return
|
|
72
|
+
if (!result || !Array.isArray(result.tests)) return
|
|
73
|
+
written = true
|
|
74
|
+
|
|
75
|
+
const dir = config.output ? path.resolve(store.codeceptDir || process.cwd(), config.output) : store.outputDir || process.cwd()
|
|
76
|
+
mkdirp.sync(dir)
|
|
77
|
+
const file = path.join(dir, config.outputName)
|
|
78
|
+
|
|
79
|
+
fs.writeFileSync(file, buildXml(result, config))
|
|
80
|
+
output.plugin('junitReporter', `JUnit report saved to ${file}`)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
event.dispatcher.on(event.all.result, writeReport)
|
|
84
|
+
event.dispatcher.on(event.workers.result, writeReport)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function buildXml(result, config) {
|
|
88
|
+
const doc = new DOMImplementation().createDocument(null, null, null)
|
|
89
|
+
const suites = groupBySuite(result.tests)
|
|
90
|
+
|
|
91
|
+
const root = doc.createElement('testsuites')
|
|
92
|
+
setAttr(root, 'name', config.testGroupName)
|
|
93
|
+
setAttr(root, 'tests', result.tests.length)
|
|
94
|
+
setAttr(root, 'failures', countState(result.tests, 'failed'))
|
|
95
|
+
setAttr(root, 'skipped', countSkipped(result.tests))
|
|
96
|
+
setAttr(root, 'errors', 0)
|
|
97
|
+
setAttr(root, 'time', toSeconds(sumDuration(result.tests)))
|
|
98
|
+
setAttr(root, 'timestamp', toIso(result.stats && result.stats.start))
|
|
99
|
+
doc.appendChild(root)
|
|
100
|
+
|
|
101
|
+
suites.forEach((tests, index) => {
|
|
102
|
+
const suite = tests[0] && tests[0].parent
|
|
103
|
+
const suiteName = (suite && suite.title) || 'Tests'
|
|
104
|
+
const suiteFile = (suite && suite.file) || (tests[0] && tests[0].file) || ''
|
|
105
|
+
|
|
106
|
+
const suiteEl = doc.createElement('testsuite')
|
|
107
|
+
setAttr(suiteEl, 'name', suiteName)
|
|
108
|
+
setAttr(suiteEl, 'id', index)
|
|
109
|
+
setAttr(suiteEl, 'tests', tests.length)
|
|
110
|
+
setAttr(suiteEl, 'failures', countState(tests, 'failed'))
|
|
111
|
+
setAttr(suiteEl, 'skipped', countSkipped(tests))
|
|
112
|
+
setAttr(suiteEl, 'errors', 0)
|
|
113
|
+
setAttr(suiteEl, 'time', toSeconds(sumDuration(tests)))
|
|
114
|
+
setAttr(suiteEl, 'timestamp', toIso(suite && suite.startedAt))
|
|
115
|
+
setAttr(suiteEl, 'hostname', os.hostname())
|
|
116
|
+
if (suiteFile) setAttr(suiteEl, 'file', suiteFile)
|
|
117
|
+
root.appendChild(suiteEl)
|
|
118
|
+
|
|
119
|
+
for (const test of tests) {
|
|
120
|
+
suiteEl.appendChild(buildTestCase(doc, test, suiteName, config))
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
return '<?xml version="1.0" encoding="UTF-8"?>\n' + new XMLSerializer().serializeToString(doc) + '\n'
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function buildTestCase(doc, test, suiteName, config) {
|
|
128
|
+
const testEl = doc.createElement('testcase')
|
|
129
|
+
setAttr(testEl, 'name', test.title || '(no title)')
|
|
130
|
+
setAttr(testEl, 'classname', suiteName)
|
|
131
|
+
setAttr(testEl, 'time', toSeconds(test.duration || 0))
|
|
132
|
+
const file = test.file || (test.parent && test.parent.file)
|
|
133
|
+
if (file) setAttr(testEl, 'file', file)
|
|
134
|
+
|
|
135
|
+
if (config.attachMeta) {
|
|
136
|
+
const properties = metaProperties(test)
|
|
137
|
+
if (properties.length) {
|
|
138
|
+
const propertiesEl = doc.createElement('properties')
|
|
139
|
+
for (const [name, value] of properties) {
|
|
140
|
+
const prop = doc.createElement('property')
|
|
141
|
+
setAttr(prop, 'name', name)
|
|
142
|
+
setAttr(prop, 'value', value)
|
|
143
|
+
propertiesEl.appendChild(prop)
|
|
144
|
+
}
|
|
145
|
+
testEl.appendChild(propertiesEl)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const flat = flattenSteps(Array.isArray(test.steps) ? test.steps : [])
|
|
150
|
+
|
|
151
|
+
if (test.state === 'skipped' || test.state === 'pending') {
|
|
152
|
+
const skipped = doc.createElement('skipped')
|
|
153
|
+
const reason = skipReason(test)
|
|
154
|
+
if (reason) setAttr(skipped, 'message', reason)
|
|
155
|
+
testEl.appendChild(skipped)
|
|
156
|
+
} else if (test.state === 'failed') {
|
|
157
|
+
const err = test.err || {}
|
|
158
|
+
const failure = doc.createElement('failure')
|
|
159
|
+
setAttr(failure, 'message', err.message || 'Test failed')
|
|
160
|
+
setAttr(failure, 'type', err.name || 'Error')
|
|
161
|
+
let body = err.stack || err.message || 'Test failed'
|
|
162
|
+
if (config.stepsInFailure && flat.length) {
|
|
163
|
+
body += '\n\nSteps:\n' + flat.map(stepLogLine).join('\n')
|
|
164
|
+
}
|
|
165
|
+
failure.appendChild(doc.createTextNode(cleanText(body)))
|
|
166
|
+
testEl.appendChild(failure)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (config.attachSteps && flat.length) {
|
|
170
|
+
const out = doc.createElement('system-out')
|
|
171
|
+
out.appendChild(doc.createTextNode(cleanText(flat.map(stepLogLine).join('\n'))))
|
|
172
|
+
testEl.appendChild(out)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return testEl
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function metaProperties(test) {
|
|
179
|
+
const props = []
|
|
180
|
+
const meta = test.meta || {}
|
|
181
|
+
for (const key of Object.keys(meta)) {
|
|
182
|
+
if (meta[key] === undefined || meta[key] === null) continue
|
|
183
|
+
props.push([key, stringifyMeta(meta[key])])
|
|
184
|
+
}
|
|
185
|
+
if (Array.isArray(test.tags) && test.tags.length) {
|
|
186
|
+
props.push(['tags', test.tags.join(' ')])
|
|
187
|
+
}
|
|
188
|
+
if (test.retries > 0 || test.retryNum > 0) {
|
|
189
|
+
props.push(['retries', String(test.retryNum || test.retries)])
|
|
190
|
+
}
|
|
191
|
+
return props
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function stringifyMeta(value) {
|
|
195
|
+
if (typeof value === 'string') return value
|
|
196
|
+
if (typeof value === 'number' || typeof value === 'boolean') return String(value)
|
|
197
|
+
try {
|
|
198
|
+
return JSON.stringify(value)
|
|
199
|
+
} catch (err) {
|
|
200
|
+
return String(value)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function flattenSteps(steps) {
|
|
205
|
+
const out = []
|
|
206
|
+
let prevChain = []
|
|
207
|
+
|
|
208
|
+
for (const step of steps) {
|
|
209
|
+
const chain = metaChain(step)
|
|
210
|
+
|
|
211
|
+
let common = 0
|
|
212
|
+
while (common < chain.length && common < prevChain.length && chain[common].key === prevChain[common].key) common++
|
|
213
|
+
|
|
214
|
+
for (let d = common; d < chain.length; d++) {
|
|
215
|
+
out.push({ depth: d, step: chain[d].step })
|
|
216
|
+
}
|
|
217
|
+
out.push({ depth: chain.length, step })
|
|
218
|
+
prevChain = chain
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return out
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function metaChain(step) {
|
|
225
|
+
const chain = []
|
|
226
|
+
let meta = step && step.metaStep
|
|
227
|
+
while (meta) {
|
|
228
|
+
chain.unshift({ step: meta, key: meta })
|
|
229
|
+
meta = meta.metaStep
|
|
230
|
+
}
|
|
231
|
+
if (!chain.length && step && step.parent && step.parent.title) {
|
|
232
|
+
chain.push({ step: { title: step.parent.title, status: step.status }, key: `meta:${step.parent.title}` })
|
|
233
|
+
}
|
|
234
|
+
return chain
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function stepLogLine(entry) {
|
|
238
|
+
const indent = ' '.repeat(entry.depth)
|
|
239
|
+
const mark = entry.step && entry.step.status === 'failed' ? '[FAILED] ' : ''
|
|
240
|
+
return `${indent}${mark}${stepText(entry.step)} (${stepDuration(entry.step)}ms)`
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function stepText(step) {
|
|
244
|
+
if (step && typeof step.toString === 'function' && step.toString !== Object.prototype.toString) return step.toString()
|
|
245
|
+
return (step && step.title) || 'step'
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function stepDuration(step) {
|
|
249
|
+
if (!step) return 0
|
|
250
|
+
if (typeof step.duration === 'number' && step.duration >= 0) return step.duration
|
|
251
|
+
if (step.startTime && step.endTime) return Math.max(0, step.endTime - step.startTime)
|
|
252
|
+
return 0
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function groupBySuite(tests) {
|
|
256
|
+
const groups = []
|
|
257
|
+
const byKey = new Map()
|
|
258
|
+
for (const test of tests) {
|
|
259
|
+
const key = test.parent || test
|
|
260
|
+
if (!byKey.has(key)) {
|
|
261
|
+
const list = []
|
|
262
|
+
byKey.set(key, list)
|
|
263
|
+
groups.push(list)
|
|
264
|
+
}
|
|
265
|
+
byKey.get(key).push(test)
|
|
266
|
+
}
|
|
267
|
+
return groups
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function skipReason(test) {
|
|
271
|
+
if (test.opts && test.opts.skipInfo && test.opts.skipInfo.message) return test.opts.skipInfo.message
|
|
272
|
+
if (test.meta && test.meta.skipReason) return test.meta.skipReason
|
|
273
|
+
return ''
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function countState(tests, state) {
|
|
277
|
+
return tests.filter(t => t.state === state).length
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function countSkipped(tests) {
|
|
281
|
+
return tests.filter(t => t.state === 'skipped' || t.state === 'pending').length
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function sumDuration(tests) {
|
|
285
|
+
return tests.reduce((sum, t) => sum + (t.duration || 0), 0)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function toSeconds(ms) {
|
|
289
|
+
return (Math.max(0, ms) / 1000).toFixed(3)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function toIso(value) {
|
|
293
|
+
const date = value ? new Date(value) : new Date()
|
|
294
|
+
return Number.isNaN(date.getTime()) ? new Date().toISOString() : date.toISOString()
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
function cleanText(text) {
|
|
298
|
+
return String(text == null ? '' : text).replace(INVALID_XML_CHARS, '')
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function setAttr(el, name, value) {
|
|
302
|
+
el.setAttribute(name, cleanText(value))
|
|
303
|
+
}
|
|
@@ -111,11 +111,12 @@ export default function (config) {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
event.dispatcher.on(event.step.started, step => {
|
|
114
|
+
if (!step.title) return
|
|
114
115
|
for (const ignored of config.ignoredSteps) {
|
|
115
|
-
if (step.
|
|
116
|
+
if (step.title === ignored) return
|
|
116
117
|
if (ignored instanceof RegExp) {
|
|
117
|
-
if (step.
|
|
118
|
-
} else if (ignored.indexOf('*') && step.
|
|
118
|
+
if (step.title.match(ignored)) return
|
|
119
|
+
} else if (ignored.indexOf('*') && step.title.startsWith(ignored.slice(0, -1))) return
|
|
119
120
|
}
|
|
120
121
|
enableRetry = true
|
|
121
122
|
})
|
package/lib/plugin/screencast.js
CHANGED
|
@@ -258,7 +258,7 @@ function formatTimestamp(timestampInMs) {
|
|
|
258
258
|
}
|
|
259
259
|
|
|
260
260
|
function stepTitle(step) {
|
|
261
|
-
let title = `${step.actor}.${step.
|
|
261
|
+
let title = `${step.actor}.${step.title}(${step.args ? step.args.join(',') : ''})`
|
|
262
262
|
if (title.length > 100) title = `${title.substring(0, 100)}...`
|
|
263
263
|
return title
|
|
264
264
|
}
|
package/lib/plugin/screenshot.js
CHANGED
|
@@ -325,7 +325,7 @@ function wireSlides(options, trigger) {
|
|
|
325
325
|
if (stepNum === -1) return
|
|
326
326
|
if (savedStep === step) return
|
|
327
327
|
if (scenarioFailed) return
|
|
328
|
-
if (step.metaStep && step.metaStep.
|
|
328
|
+
if (step.metaStep && step.metaStep.title === 'BeforeSuite') return
|
|
329
329
|
if (!currentTest) return
|
|
330
330
|
if (!stepFilter(step)) return
|
|
331
331
|
if (isStepIgnored(step, options.ignoreSteps)) return
|
|
@@ -404,7 +404,7 @@ function makeStepFilter(trigger, options) {
|
|
|
404
404
|
function isStepIgnored(step, patterns) {
|
|
405
405
|
if (!patterns || !patterns.length) return false
|
|
406
406
|
for (const pattern of patterns) {
|
|
407
|
-
if (step.
|
|
407
|
+
if (step.title && step.title.match(pattern)) return true
|
|
408
408
|
}
|
|
409
409
|
return false
|
|
410
410
|
}
|
|
@@ -68,6 +68,7 @@ export default function(config) {
|
|
|
68
68
|
config.customTimeoutSteps = config.customTimeoutSteps.concat(config.noTimeoutSteps).concat(config.customTimeoutSteps)
|
|
69
69
|
|
|
70
70
|
event.dispatcher.on(event.step.before, step => {
|
|
71
|
+
if (!step.title) return
|
|
71
72
|
let stepTimeout
|
|
72
73
|
for (let stepRule of config.customTimeoutSteps) {
|
|
73
74
|
let customTimeout = 0
|
|
@@ -75,7 +76,7 @@ export default function(config) {
|
|
|
75
76
|
if (stepRule.length > 1) customTimeout = stepRule[1]
|
|
76
77
|
stepRule = stepRule[0]
|
|
77
78
|
}
|
|
78
|
-
if (stepRule instanceof RegExp ? step.
|
|
79
|
+
if (stepRule instanceof RegExp ? step.title.match(stepRule) : step.title === stepRule || (stepRule.indexOf('*') && step.title.startsWith(stepRule.slice(0, -1)))) {
|
|
79
80
|
stepTimeout = customTimeout
|
|
80
81
|
break
|
|
81
82
|
}
|
package/lib/step/base.js
CHANGED
|
@@ -10,12 +10,12 @@ const STACK_LINE = 5
|
|
|
10
10
|
/**
|
|
11
11
|
* Each command in test executed through `I.` object is wrapped in Step.
|
|
12
12
|
* Step allows logging executed commands and triggers hook before and after step execution.
|
|
13
|
-
* @param {string}
|
|
13
|
+
* @param {string} title
|
|
14
14
|
*/
|
|
15
15
|
class Step {
|
|
16
|
-
constructor(
|
|
16
|
+
constructor(title) {
|
|
17
17
|
/** @member {string} */
|
|
18
|
-
this.
|
|
18
|
+
this.title = title
|
|
19
19
|
/** @member {Map<number, number>} */
|
|
20
20
|
this.timeouts = new Map()
|
|
21
21
|
|
|
@@ -43,7 +43,7 @@ class Step {
|
|
|
43
43
|
/** @member {any} */
|
|
44
44
|
this.helper = null
|
|
45
45
|
/** @member {string} */
|
|
46
|
-
this.helperMethod =
|
|
46
|
+
this.helperMethod = title
|
|
47
47
|
|
|
48
48
|
this.startTime = 0
|
|
49
49
|
this.endTime = 0
|
|
@@ -103,7 +103,7 @@ class Step {
|
|
|
103
103
|
|
|
104
104
|
/** @return {string} */
|
|
105
105
|
humanize() {
|
|
106
|
-
return humanizeString(this.
|
|
106
|
+
return humanizeString(this.title)
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
/** @return {string} */
|
|
@@ -180,7 +180,7 @@ class Step {
|
|
|
180
180
|
|
|
181
181
|
/** @return {string} */
|
|
182
182
|
toCode() {
|
|
183
|
-
return `${this.prefix}${this.actor}.${this.
|
|
183
|
+
return `${this.prefix}${this.actor}.${this.title}(${this.humanizeArgs()})${this.suffix}`
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
isMetaStep() {
|
|
@@ -223,7 +223,7 @@ class Step {
|
|
|
223
223
|
|
|
224
224
|
return {
|
|
225
225
|
opts: step.opts || {},
|
|
226
|
-
title: step.
|
|
226
|
+
title: step.title,
|
|
227
227
|
args: args,
|
|
228
228
|
status: step.status,
|
|
229
229
|
startTime: step.startTime,
|
package/lib/step/comment.js
CHANGED
package/lib/step/helper.js
CHANGED
|
@@ -2,12 +2,12 @@ import Step from './base.js'
|
|
|
2
2
|
import store from '../store.js'
|
|
3
3
|
|
|
4
4
|
class HelperStep extends Step {
|
|
5
|
-
constructor(helper,
|
|
6
|
-
super(
|
|
5
|
+
constructor(helper, title) {
|
|
6
|
+
super(title)
|
|
7
7
|
/** @member {CodeceptJS.Helper} helper corresponding helper */
|
|
8
8
|
this.helper = helper
|
|
9
|
-
/** @member {string} helperMethod
|
|
10
|
-
this.helperMethod =
|
|
9
|
+
/** @member {string} helperMethod title of method to be executed */
|
|
10
|
+
this.helperMethod = title
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
/**
|
package/lib/step/meta.js
CHANGED
|
@@ -29,7 +29,7 @@ class MetaStep extends Step {
|
|
|
29
29
|
const actorText = this.actor
|
|
30
30
|
|
|
31
31
|
if (this.isBDD()) {
|
|
32
|
-
return `${this.prefix}${actorText} ${this.
|
|
32
|
+
return `${this.prefix}${actorText} ${this.title} "${this.humanizeArgs()}${this.suffix}"`
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
if (actorText === 'I') {
|
|
@@ -37,14 +37,14 @@ class MetaStep extends Step {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
if (!this.actor) {
|
|
40
|
-
return `${this.
|
|
40
|
+
return `${this.title} ${this.humanizeArgs()}${this.suffix}`.trim()
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
return `On ${this.prefix}${actorText}: ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`.trim()
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
humanize() {
|
|
47
|
-
return humanizeString(this.
|
|
47
|
+
return humanizeString(this.title)
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
setTrace() {}
|
package/lib/step/record.js
CHANGED
|
@@ -16,12 +16,12 @@ function recordStep(step, args) {
|
|
|
16
16
|
const { opts, timeout, retry } = stepConfig.getConfig()
|
|
17
17
|
|
|
18
18
|
if (opts) {
|
|
19
|
-
output.debug(`Step ${step.
|
|
19
|
+
output.debug(`Step ${step.title}: options applied ${JSON.stringify(opts)}`)
|
|
20
20
|
store.stepOptions = opts
|
|
21
21
|
step.opts = opts
|
|
22
22
|
}
|
|
23
23
|
if (timeout) {
|
|
24
|
-
output.debug(`Step ${step.
|
|
24
|
+
output.debug(`Step ${step.title} timeout ${timeout}s`)
|
|
25
25
|
step.setTimeout(timeout * 1000, TIMEOUT_ORDER.codeLimitTime)
|
|
26
26
|
}
|
|
27
27
|
if (retry) retryStep(retry)
|
|
@@ -31,7 +31,7 @@ function recordStep(step, args) {
|
|
|
31
31
|
// run async before step hooks
|
|
32
32
|
event.emit(event.step.before, step)
|
|
33
33
|
|
|
34
|
-
const task = `${step.
|
|
34
|
+
const task = `${step.title}: ${step.humanizeArgs()}`
|
|
35
35
|
let val
|
|
36
36
|
|
|
37
37
|
// run step inside promise
|