codeceptjs 3.6.10-beta.1 → 3.7.0-beta.1
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/README.md +81 -110
- package/bin/codecept.js +2 -2
- package/docs/webapi/clearCookie.mustache +1 -1
- package/lib/actor.js +46 -36
- package/lib/assert/empty.js +3 -5
- package/lib/assert/equal.js +4 -7
- package/lib/assert/include.js +4 -6
- package/lib/assert/throws.js +2 -4
- package/lib/assert/truth.js +2 -2
- package/lib/codecept.js +87 -83
- package/lib/command/configMigrate.js +2 -4
- package/lib/command/definitions.js +5 -25
- package/lib/command/generate.js +10 -14
- package/lib/command/gherkin/snippets.js +10 -8
- package/lib/command/gherkin/steps.js +1 -1
- package/lib/command/info.js +1 -3
- package/lib/command/init.js +8 -12
- package/lib/command/interactive.js +1 -1
- package/lib/command/list.js +1 -1
- package/lib/command/run-multiple.js +12 -35
- package/lib/command/run-workers.js +10 -10
- package/lib/command/utils.js +5 -6
- package/lib/command/workers/runTests.js +14 -17
- package/lib/container.js +327 -237
- package/lib/data/context.js +10 -13
- package/lib/data/dataScenarioConfig.js +8 -8
- package/lib/data/dataTableArgument.js +6 -6
- package/lib/data/table.js +5 -11
- package/lib/els.js +177 -0
- package/lib/event.js +1 -0
- package/lib/heal.js +78 -80
- package/lib/helper/ApiDataFactory.js +3 -6
- package/lib/helper/Appium.js +15 -30
- package/lib/helper/FileSystem.js +3 -3
- package/lib/helper/GraphQLDataFactory.js +3 -3
- package/lib/helper/JSONResponse.js +57 -37
- package/lib/helper/Nightmare.js +35 -53
- package/lib/helper/Playwright.js +189 -251
- package/lib/helper/Protractor.js +54 -77
- package/lib/helper/Puppeteer.js +134 -232
- package/lib/helper/REST.js +5 -17
- package/lib/helper/TestCafe.js +21 -44
- package/lib/helper/WebDriver.js +103 -162
- package/lib/helper/testcafe/testcafe-utils.js +26 -27
- package/lib/listener/artifacts.js +2 -2
- package/lib/listener/emptyRun.js +58 -0
- package/lib/listener/exit.js +4 -4
- package/lib/listener/{retry.js → globalRetry.js} +5 -5
- package/lib/listener/{timeout.js → globalTimeout.js} +8 -8
- package/lib/listener/helpers.js +15 -15
- package/lib/listener/mocha.js +1 -1
- package/lib/listener/steps.js +17 -12
- package/lib/listener/store.js +12 -0
- package/lib/mocha/asyncWrapper.js +204 -0
- package/lib/{interfaces → mocha}/bdd.js +3 -3
- package/lib/mocha/cli.js +257 -0
- package/lib/mocha/factory.js +104 -0
- package/lib/{interfaces → mocha}/featureConfig.js +11 -12
- package/lib/{interfaces → mocha}/gherkin.js +26 -28
- package/lib/mocha/hooks.js +83 -0
- package/lib/mocha/index.js +12 -0
- package/lib/mocha/inject.js +24 -0
- package/lib/{interfaces → mocha}/scenarioConfig.js +10 -6
- package/lib/mocha/suite.js +55 -0
- package/lib/mocha/test.js +60 -0
- package/lib/mocha/types.d.ts +31 -0
- package/lib/mocha/ui.js +219 -0
- package/lib/output.js +28 -10
- package/lib/pause.js +159 -135
- package/lib/plugin/autoDelay.js +4 -4
- package/lib/plugin/autoLogin.js +6 -7
- package/lib/plugin/commentStep.js +1 -1
- package/lib/plugin/coverage.js +10 -19
- package/lib/plugin/customLocator.js +3 -3
- package/lib/plugin/debugErrors.js +2 -2
- package/lib/plugin/eachElement.js +1 -1
- package/lib/plugin/fakerTransform.js +1 -1
- package/lib/plugin/heal.js +6 -9
- package/lib/plugin/retryFailedStep.js +4 -4
- package/lib/plugin/retryTo.js +2 -2
- package/lib/plugin/screenshotOnFail.js +9 -36
- package/lib/plugin/selenoid.js +15 -35
- package/lib/plugin/stepByStepReport.js +51 -13
- package/lib/plugin/stepTimeout.js +4 -11
- package/lib/plugin/subtitles.js +4 -4
- package/lib/plugin/tryTo.js +1 -1
- package/lib/plugin/wdio.js +8 -10
- package/lib/recorder.js +142 -121
- package/lib/secret.js +1 -1
- package/lib/step.js +160 -144
- package/lib/store.js +6 -2
- package/lib/template/heal.js +2 -11
- package/lib/utils.js +224 -216
- package/lib/within.js +73 -55
- package/lib/workers.js +265 -261
- package/package.json +45 -46
- package/typings/index.d.ts +172 -184
- package/typings/promiseBasedTypes.d.ts +53 -516
- package/typings/types.d.ts +127 -587
- package/lib/cli.js +0 -256
- package/lib/helper/ExpectHelper.js +0 -391
- package/lib/helper/SoftExpectHelper.js +0 -381
- package/lib/mochaFactory.js +0 -113
- package/lib/scenario.js +0 -224
- package/lib/ui.js +0 -236
|
@@ -76,14 +76,14 @@ const defaultConfig = {
|
|
|
76
76
|
* ```
|
|
77
77
|
*
|
|
78
78
|
*/
|
|
79
|
-
module.exports =
|
|
79
|
+
module.exports = config => {
|
|
80
80
|
config = Object.assign(defaultConfig, config)
|
|
81
81
|
config.ignoredSteps = config.ignoredSteps.concat(config.defaultIgnoredSteps)
|
|
82
82
|
const customWhen = config.when
|
|
83
83
|
|
|
84
84
|
let enableRetry = false
|
|
85
85
|
|
|
86
|
-
const when =
|
|
86
|
+
const when = err => {
|
|
87
87
|
if (!enableRetry) return
|
|
88
88
|
const store = require('../store')
|
|
89
89
|
if (store.debugMode) return false
|
|
@@ -92,7 +92,7 @@ module.exports = (config) => {
|
|
|
92
92
|
}
|
|
93
93
|
config.when = when
|
|
94
94
|
|
|
95
|
-
event.dispatcher.on(event.step.started,
|
|
95
|
+
event.dispatcher.on(event.step.started, step => {
|
|
96
96
|
if (process.env.TRY_TO === 'true') {
|
|
97
97
|
log('Info: RetryFailedStep plugin is disabled inside tryTo block')
|
|
98
98
|
return
|
|
@@ -112,7 +112,7 @@ module.exports = (config) => {
|
|
|
112
112
|
enableRetry = false
|
|
113
113
|
})
|
|
114
114
|
|
|
115
|
-
event.dispatcher.on(event.test.before,
|
|
115
|
+
event.dispatcher.on(event.test.before, test => {
|
|
116
116
|
if (test && test.disableRetryFailedStep) return // disable retry when a test is not active
|
|
117
117
|
// this env var is used to set the retries inside _before() block of helpers
|
|
118
118
|
process.env.FAILED_STEP_RETRIES = config.retries
|
package/lib/plugin/retryTo.js
CHANGED
|
@@ -100,7 +100,7 @@ module.exports = function (config) {
|
|
|
100
100
|
})
|
|
101
101
|
|
|
102
102
|
// Catch errors and retry
|
|
103
|
-
recorder.session.catch(
|
|
103
|
+
recorder.session.catch(err => {
|
|
104
104
|
recorder.session.restore(`retryTo ${tries}`)
|
|
105
105
|
if (tries <= maxTries) {
|
|
106
106
|
debug(`Error ${err}... Retrying`)
|
|
@@ -112,7 +112,7 @@ module.exports = function (config) {
|
|
|
112
112
|
})
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
recorder.add('retryTo', tryBlock).catch(
|
|
115
|
+
recorder.add('retryTo', tryBlock).catch(err => {
|
|
116
116
|
console.error('An error occurred:', err)
|
|
117
117
|
done(null)
|
|
118
118
|
})
|
|
@@ -63,9 +63,7 @@ module.exports = function (config) {
|
|
|
63
63
|
}
|
|
64
64
|
|
|
65
65
|
if (Codeceptjs.container.mocha()) {
|
|
66
|
-
options.reportDir =
|
|
67
|
-
Codeceptjs.container.mocha().options.reporterOptions &&
|
|
68
|
-
Codeceptjs.container.mocha().options.reporterOptions.reportDir
|
|
66
|
+
options.reportDir = Codeceptjs.container.mocha().options.reporterOptions && Codeceptjs.container.mocha().options.reporterOptions.reportDir
|
|
69
67
|
}
|
|
70
68
|
|
|
71
69
|
if (options.disableScreenshots) {
|
|
@@ -73,12 +71,9 @@ module.exports = function (config) {
|
|
|
73
71
|
return
|
|
74
72
|
}
|
|
75
73
|
|
|
76
|
-
event.dispatcher.on(event.test.failed,
|
|
74
|
+
event.dispatcher.on(event.test.failed, test => {
|
|
77
75
|
if (test.ctx?._runnable.title.includes('hook: ')) {
|
|
78
|
-
output.plugin(
|
|
79
|
-
'screenshotOnFail',
|
|
80
|
-
'BeforeSuite/AfterSuite do not have any access to the browser, hence it could not take screenshot.',
|
|
81
|
-
)
|
|
76
|
+
output.plugin('screenshotOnFail', 'BeforeSuite/AfterSuite do not have any access to the browser, hence it could not take screenshot.')
|
|
82
77
|
return
|
|
83
78
|
}
|
|
84
79
|
recorder.add(
|
|
@@ -90,8 +85,7 @@ module.exports = function (config) {
|
|
|
90
85
|
if (fileName.indexOf('{') !== -1) {
|
|
91
86
|
fileName = fileName.substr(0, fileName.indexOf('{') - 3).trim()
|
|
92
87
|
}
|
|
93
|
-
if (test.ctx && test.ctx.test && test.ctx.test.type === 'hook')
|
|
94
|
-
fileName = clearString(`${test.title}_${test.ctx.test.title}`)
|
|
88
|
+
if (test.ctx && test.ctx.test && test.ctx.test.type === 'hook') fileName = clearString(`${test.title}_${test.ctx.test.title}`)
|
|
95
89
|
if (options.uniqueScreenshotNames && test) {
|
|
96
90
|
const uuid = _getUUID(test)
|
|
97
91
|
fileName = `${fileName.substring(0, 10)}_${uuid}.failed.png`
|
|
@@ -112,34 +106,20 @@ module.exports = function (config) {
|
|
|
112
106
|
|
|
113
107
|
if (!test.artifacts) test.artifacts = {}
|
|
114
108
|
test.artifacts.screenshot = path.join(global.output_dir, fileName)
|
|
115
|
-
if (
|
|
116
|
-
Container.mocha().options.reporterOptions['mocha-junit-reporter'] &&
|
|
117
|
-
Container.mocha().options.reporterOptions['mocha-junit-reporter'].options.attachments
|
|
118
|
-
) {
|
|
109
|
+
if (Container.mocha().options.reporterOptions['mocha-junit-reporter'] && Container.mocha().options.reporterOptions['mocha-junit-reporter'].options.attachments) {
|
|
119
110
|
test.attachments = [path.join(global.output_dir, fileName)]
|
|
120
111
|
}
|
|
121
112
|
|
|
122
113
|
const allureReporter = Container.plugins('allure')
|
|
123
114
|
if (allureReporter) {
|
|
124
|
-
allureReporter.addAttachment(
|
|
125
|
-
'Main session - Last Seen Screenshot',
|
|
126
|
-
fs.readFileSync(path.join(global.output_dir, fileName)),
|
|
127
|
-
dataType,
|
|
128
|
-
)
|
|
115
|
+
allureReporter.addAttachment('Main session - Last Seen Screenshot', fs.readFileSync(path.join(global.output_dir, fileName)), dataType)
|
|
129
116
|
|
|
130
117
|
if (helper.activeSessionName) {
|
|
131
118
|
const sessions = helper.sessionPages || helper.sessionWindows
|
|
132
119
|
for (const sessionName in sessions) {
|
|
133
120
|
const screenshotFileName = `${sessionName}_${fileName}`
|
|
134
|
-
test.artifacts[`${sessionName.replace(/ /g, '_')}_screenshot`] = path.join(
|
|
135
|
-
|
|
136
|
-
screenshotFileName,
|
|
137
|
-
)
|
|
138
|
-
allureReporter.addAttachment(
|
|
139
|
-
`${sessionName} - Last Seen Screenshot`,
|
|
140
|
-
fs.readFileSync(path.join(global.output_dir, screenshotFileName)),
|
|
141
|
-
dataType,
|
|
142
|
-
)
|
|
121
|
+
test.artifacts[`${sessionName.replace(/ /g, '_')}_screenshot`] = path.join(global.output_dir, screenshotFileName)
|
|
122
|
+
allureReporter.addAttachment(`${sessionName} - Last Seen Screenshot`, fs.readFileSync(path.join(global.output_dir, screenshotFileName)), dataType)
|
|
143
123
|
}
|
|
144
124
|
}
|
|
145
125
|
}
|
|
@@ -150,14 +130,7 @@ module.exports = function (config) {
|
|
|
150
130
|
}
|
|
151
131
|
} catch (err) {
|
|
152
132
|
output.plugin(err)
|
|
153
|
-
if (
|
|
154
|
-
err &&
|
|
155
|
-
err.type &&
|
|
156
|
-
err.type === 'RuntimeError' &&
|
|
157
|
-
err.message &&
|
|
158
|
-
(err.message.indexOf('was terminated due to') > -1 ||
|
|
159
|
-
err.message.indexOf('no such window: target window already closed') > -1)
|
|
160
|
-
) {
|
|
133
|
+
if (err && err.type && err.type === 'RuntimeError' && err.message && (err.message.indexOf('was terminated due to') > -1 || err.message.indexOf('no such window: target window already closed') > -1)) {
|
|
161
134
|
output.log(`Can't make screenshot, ${err}`)
|
|
162
135
|
helper.isRunning = false
|
|
163
136
|
}
|
package/lib/plugin/selenoid.js
CHANGED
|
@@ -41,12 +41,7 @@ const dockerCreateScriptArr = [
|
|
|
41
41
|
'aerokube/selenoid:latest-release -log-output-dir /opt/selenoid/logs',
|
|
42
42
|
]
|
|
43
43
|
|
|
44
|
-
const dockerImageCheckScript = [
|
|
45
|
-
'docker images',
|
|
46
|
-
"--filter reference='selenoid/video-recorder'",
|
|
47
|
-
"--filter reference='selenoid/chrome:latest'",
|
|
48
|
-
"--filter reference='selenoid/firefox:latest'",
|
|
49
|
-
].join(' ')
|
|
44
|
+
const dockerImageCheckScript = ['docker images', "--filter reference='selenoid/video-recorder'", "--filter reference='selenoid/chrome:latest'", "--filter reference='selenoid/firefox:latest'"].join(' ')
|
|
50
45
|
|
|
51
46
|
let dockerCreateScript = dockerCreateScriptArr.join(' ')
|
|
52
47
|
let dockerStartScript = 'docker start $name$'
|
|
@@ -56,8 +51,8 @@ let seleniumUrl = 'http://localhost:$port$'
|
|
|
56
51
|
const supportedHelpers = ['WebDriver']
|
|
57
52
|
const SELENOID_START_TIMEOUT = 2000
|
|
58
53
|
const SELENOID_STOP_TIMEOUT = 10000
|
|
59
|
-
const wait =
|
|
60
|
-
new Promise(
|
|
54
|
+
const wait = time =>
|
|
55
|
+
new Promise(res => {
|
|
61
56
|
setTimeout(() => {
|
|
62
57
|
// @ts-ignore
|
|
63
58
|
res()
|
|
@@ -179,7 +174,7 @@ const wait = (time) =>
|
|
|
179
174
|
*
|
|
180
175
|
*/
|
|
181
176
|
|
|
182
|
-
const selenoid =
|
|
177
|
+
const selenoid = config => {
|
|
183
178
|
const helpers = container.helpers()
|
|
184
179
|
let helperName
|
|
185
180
|
|
|
@@ -194,14 +189,7 @@ const selenoid = (config) => {
|
|
|
194
189
|
return // no helpers for Selenoid
|
|
195
190
|
}
|
|
196
191
|
|
|
197
|
-
const {
|
|
198
|
-
autoStart,
|
|
199
|
-
name = 'selenoid',
|
|
200
|
-
deletePassed = true,
|
|
201
|
-
additionalParams = '',
|
|
202
|
-
autoCreate = true,
|
|
203
|
-
port = 4444,
|
|
204
|
-
} = config
|
|
192
|
+
const { autoStart, name = 'selenoid', deletePassed = true, additionalParams = '', autoCreate = true, port = 4444 } = config
|
|
205
193
|
const passedTests = []
|
|
206
194
|
|
|
207
195
|
recorder.startUnlessRunning()
|
|
@@ -213,7 +201,7 @@ const selenoid = (config) => {
|
|
|
213
201
|
output.debug('Staring Selenoid... ')
|
|
214
202
|
return createAndStart(autoCreate)
|
|
215
203
|
.then(() => output.debug('Selenoid started'))
|
|
216
|
-
.catch(
|
|
204
|
+
.catch(err => {
|
|
217
205
|
throw new Error(err.stack)
|
|
218
206
|
})
|
|
219
207
|
})
|
|
@@ -226,7 +214,7 @@ const selenoid = (config) => {
|
|
|
226
214
|
.then(() => deletePassedTests(passedTests))
|
|
227
215
|
.then(stopSelenoid)
|
|
228
216
|
.then(() => output.debug('Selenoid stopped'))
|
|
229
|
-
.catch(
|
|
217
|
+
.catch(err => {
|
|
230
218
|
throw new Error(err.stack)
|
|
231
219
|
})
|
|
232
220
|
})
|
|
@@ -241,7 +229,7 @@ const selenoid = (config) => {
|
|
|
241
229
|
}
|
|
242
230
|
})
|
|
243
231
|
|
|
244
|
-
event.dispatcher.on(event.test.before,
|
|
232
|
+
event.dispatcher.on(event.test.before, test => {
|
|
245
233
|
switch (helperName) {
|
|
246
234
|
case 'WebDriver':
|
|
247
235
|
setTestConfigForWebdriver(test)
|
|
@@ -250,7 +238,7 @@ const selenoid = (config) => {
|
|
|
250
238
|
})
|
|
251
239
|
|
|
252
240
|
if (config.enableVideo) {
|
|
253
|
-
event.dispatcher.on(event.test.passed,
|
|
241
|
+
event.dispatcher.on(event.test.passed, test => {
|
|
254
242
|
if (deletePassed) {
|
|
255
243
|
passedTests.push(test.title)
|
|
256
244
|
} else {
|
|
@@ -258,7 +246,7 @@ const selenoid = (config) => {
|
|
|
258
246
|
}
|
|
259
247
|
})
|
|
260
248
|
|
|
261
|
-
event.dispatcher.on(event.test.failed,
|
|
249
|
+
event.dispatcher.on(event.test.failed, test => {
|
|
262
250
|
test.artifacts.video = videoSaved(test)
|
|
263
251
|
})
|
|
264
252
|
}
|
|
@@ -312,16 +300,12 @@ const pullImage = async () => {
|
|
|
312
300
|
console.time('Pulled containers')
|
|
313
301
|
if (!stdout.includes('selenoid/video-recorder')) {
|
|
314
302
|
output.debug('Pulling selenoid/video-recorder...')
|
|
315
|
-
resultPromise = exec('docker pull selenoid/video-recorder:latest-release').then(() =>
|
|
316
|
-
output.debug('Pulled in selenoid/video-recorder'),
|
|
317
|
-
)
|
|
303
|
+
resultPromise = exec('docker pull selenoid/video-recorder:latest-release').then(() => output.debug('Pulled in selenoid/video-recorder'))
|
|
318
304
|
pulls.push(resultPromise)
|
|
319
305
|
}
|
|
320
306
|
if (!stdout.includes('selenoid/chrome')) {
|
|
321
307
|
output.debug('Pulling selenoid/chrome...')
|
|
322
|
-
resultPromise = exec('docker pull selenoid/chrome:latest').then(() =>
|
|
323
|
-
output.debug('Pulled in selenoid/video-recorder'),
|
|
324
|
-
)
|
|
308
|
+
resultPromise = exec('docker pull selenoid/chrome:latest').then(() => output.debug('Pulled in selenoid/video-recorder'))
|
|
325
309
|
pulls.push(resultPromise)
|
|
326
310
|
}
|
|
327
311
|
if (!stdout.includes('selenoid/firefox')) {
|
|
@@ -341,16 +325,12 @@ function createAndStart(autoCreate) {
|
|
|
341
325
|
}
|
|
342
326
|
|
|
343
327
|
function deletePassedTests(passedTests) {
|
|
344
|
-
const deleteVideoPromiseList = passedTests
|
|
345
|
-
|
|
346
|
-
.map((test) => axios.delete(`${seleniumUrl}/video/${test}.mp4`))
|
|
347
|
-
const deleteLogPromiseList = passedTests
|
|
348
|
-
.map(clearString)
|
|
349
|
-
.map((test) => axios.delete(`${seleniumUrl}/logs/${test}.log`))
|
|
328
|
+
const deleteVideoPromiseList = passedTests.map(clearString).map(test => axios.delete(`${seleniumUrl}/video/${test}.mp4`))
|
|
329
|
+
const deleteLogPromiseList = passedTests.map(clearString).map(test => axios.delete(`${seleniumUrl}/logs/${test}.log`))
|
|
350
330
|
|
|
351
331
|
return Promise.all(deleteVideoPromiseList.concat(deleteLogPromiseList))
|
|
352
332
|
.then(() => output.debug('Deleted videos and logs for all passed tests'))
|
|
353
|
-
.catch(
|
|
333
|
+
.catch(err => output.error(`Error while deleting video and log files ${err.stack}`))
|
|
354
334
|
}
|
|
355
335
|
|
|
356
336
|
function setOptionsForWebdriver(config) {
|
|
@@ -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')
|
|
@@ -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,
|
|
92
|
+
event.dispatcher.on(event.suite.before, suite => {
|
|
92
93
|
stepNum = -1
|
|
93
94
|
})
|
|
94
95
|
|
|
95
|
-
event.dispatcher.on(event.test.before,
|
|
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,15 +107,15 @@ module.exports = function (config) {
|
|
|
106
107
|
currentTest = test
|
|
107
108
|
})
|
|
108
109
|
|
|
109
|
-
event.dispatcher.on(event.step.failed,
|
|
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,
|
|
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,
|
|
118
|
+
event.dispatcher.on(event.test.passed, test => {
|
|
118
119
|
if (!config.deleteSuccessful) return persist(test)
|
|
119
120
|
// cleanup
|
|
120
121
|
deleteDir(dir)
|
|
@@ -122,10 +123,7 @@ module.exports = function (config) {
|
|
|
122
123
|
|
|
123
124
|
event.dispatcher.on(event.test.failed, (test, err) => {
|
|
124
125
|
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
|
-
)
|
|
126
|
+
output.plugin('stepByStepReport', 'BeforeSuite/AfterSuite do not have any access to the browser, hence it could not take screenshot.')
|
|
129
127
|
return
|
|
130
128
|
}
|
|
131
129
|
persist(test, err)
|
|
@@ -133,7 +131,49 @@ module.exports = function (config) {
|
|
|
133
131
|
|
|
134
132
|
event.dispatcher.on(event.all.result, () => {
|
|
135
133
|
if (Object.keys(recordedTests).length === 0 || !Object.keys(slides).length) return
|
|
134
|
+
generateRecordsHtml(recordedTests)
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
event.dispatcher.on(event.workers.result, async () => {
|
|
138
|
+
await recorder.add(() => {
|
|
139
|
+
const recordedTests = getRecordFoldersWithDetails(reportDir)
|
|
140
|
+
generateRecordsHtml(recordedTests)
|
|
141
|
+
})
|
|
142
|
+
})
|
|
136
143
|
|
|
144
|
+
function getRecordFoldersWithDetails(dirPath) {
|
|
145
|
+
let results = {}
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
const items = fs.readdirSync(dirPath, { withFileTypes: true })
|
|
149
|
+
|
|
150
|
+
items.forEach(item => {
|
|
151
|
+
if (item.isDirectory() && item.name.startsWith('record_')) {
|
|
152
|
+
const recordFolderPath = path.join(dirPath, item.name)
|
|
153
|
+
const indexPath = path.join(recordFolderPath, 'index.html')
|
|
154
|
+
|
|
155
|
+
let name = ''
|
|
156
|
+
if (fs.existsSync(indexPath)) {
|
|
157
|
+
try {
|
|
158
|
+
const htmlContent = fs.readFileSync(indexPath, 'utf-8')
|
|
159
|
+
const $ = cheerio.load(htmlContent)
|
|
160
|
+
name = $('.navbar-brand').text().trim()
|
|
161
|
+
} catch (err) {
|
|
162
|
+
console.error(`Error reading index.html in ${recordFolderPath}:`, err.message)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
results[name || 'Unkown'] = `${item.name}/index.html`
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
} catch (err) {
|
|
170
|
+
console.error(`Error reading directory ${dirPath}:`, err.message)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return results
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function generateRecordsHtml(recordedTests) {
|
|
137
177
|
let links = ''
|
|
138
178
|
|
|
139
179
|
for (const link in recordedTests) {
|
|
@@ -147,10 +187,8 @@ module.exports = function (config) {
|
|
|
147
187
|
|
|
148
188
|
fs.writeFileSync(path.join(reportDir, 'records.html'), indexHTML)
|
|
149
189
|
|
|
150
|
-
output.print(
|
|
151
|
-
|
|
152
|
-
)
|
|
153
|
-
})
|
|
190
|
+
output.print(`${figures.circleFilled} Step-by-step preview: ${colors.white.bold(`file://${reportDir}/records.html`)}`)
|
|
191
|
+
}
|
|
154
192
|
|
|
155
193
|
async function persistStep(step) {
|
|
156
194
|
if (stepNum === -1) return // Ignore steps from BeforeSuite function
|
|
@@ -61,12 +61,12 @@ const defaultConfig = {
|
|
|
61
61
|
* ```
|
|
62
62
|
*
|
|
63
63
|
*/
|
|
64
|
-
module.exports =
|
|
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,
|
|
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
|
}
|
package/lib/plugin/subtitles.js
CHANGED
|
@@ -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,
|
|
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,
|
|
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
|
|
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) => {
|
package/lib/plugin/tryTo.js
CHANGED
|
@@ -92,7 +92,7 @@ function tryTo(callback) {
|
|
|
92
92
|
recorder.session.restore('tryTo')
|
|
93
93
|
return result
|
|
94
94
|
})
|
|
95
|
-
recorder.session.catch(
|
|
95
|
+
recorder.session.catch(err => {
|
|
96
96
|
result = false
|
|
97
97
|
const msg = err.inspect ? err.inspect() : err.toString()
|
|
98
98
|
debug(`Unsuccessful try > ${msg}`)
|
package/lib/plugin/wdio.js
CHANGED
|
@@ -81,7 +81,7 @@ let restartsSession
|
|
|
81
81
|
* * ... - additional configuration passed into services.
|
|
82
82
|
*
|
|
83
83
|
*/
|
|
84
|
-
module.exports =
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
|
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
|
|
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,
|
|
207
|
+
event.dispatcher.on(event.all.result, result => service.after(result))
|
|
210
208
|
}
|
|
211
209
|
}
|
|
212
210
|
|