codeceptjs 3.6.4 → 3.6.5-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/bin/codecept.js +84 -63
- package/lib/assert/empty.js +19 -19
- package/lib/assert/equal.js +32 -30
- package/lib/assert/error.js +14 -14
- package/lib/assert/include.js +42 -42
- package/lib/assert/throws.js +13 -11
- package/lib/assert/truth.js +17 -18
- package/lib/command/configMigrate.js +57 -52
- package/lib/command/definitions.js +88 -88
- package/lib/command/dryRun.js +65 -63
- package/lib/command/generate.js +191 -181
- package/lib/command/info.js +39 -37
- package/lib/command/init.js +289 -286
- package/lib/command/interactive.js +32 -32
- package/lib/command/list.js +26 -26
- package/lib/command/run-multiple.js +113 -93
- package/lib/command/run-rerun.js +22 -22
- package/lib/command/run-workers.js +63 -63
- package/lib/command/run.js +24 -26
- package/lib/command/utils.js +64 -63
- package/lib/data/context.js +60 -60
- package/lib/data/dataScenarioConfig.js +47 -47
- package/lib/data/dataTableArgument.js +29 -29
- package/lib/data/table.js +26 -20
- package/lib/helper/AI.js +67 -65
- package/lib/helper/ApiDataFactory.js +72 -69
- package/lib/helper/Appium.js +409 -379
- package/lib/helper/ExpectHelper.js +214 -248
- package/lib/helper/FileSystem.js +77 -78
- package/lib/helper/GraphQL.js +44 -43
- package/lib/helper/GraphQLDataFactory.js +49 -50
- package/lib/helper/JSONResponse.js +64 -62
- package/lib/helper/Mochawesome.js +28 -28
- package/lib/helper/MockServer.js +12 -12
- package/lib/helper/Nightmare.js +664 -572
- package/lib/helper/Playwright.js +1320 -1211
- package/lib/helper/Protractor.js +663 -629
- package/lib/helper/Puppeteer.js +1232 -1124
- package/lib/helper/REST.js +87 -72
- package/lib/helper/TestCafe.js +490 -491
- package/lib/helper/WebDriver.js +1294 -1156
- package/lib/interfaces/bdd.js +38 -51
- package/lib/interfaces/featureConfig.js +19 -19
- package/lib/interfaces/gherkin.js +122 -111
- package/lib/interfaces/scenarioConfig.js +29 -29
- package/lib/listener/artifacts.js +9 -9
- package/lib/listener/config.js +24 -23
- package/lib/listener/exit.js +12 -12
- package/lib/listener/helpers.js +42 -42
- package/lib/listener/mocha.js +11 -11
- package/lib/listener/retry.js +32 -30
- package/lib/listener/steps.js +50 -51
- package/lib/listener/timeout.js +53 -53
- package/lib/plugin/allure.js +14 -14
- package/lib/plugin/autoDelay.js +29 -36
- package/lib/plugin/autoLogin.js +70 -66
- package/lib/plugin/commentStep.js +18 -18
- package/lib/plugin/coverage.js +92 -77
- package/lib/plugin/customLocator.js +20 -19
- package/lib/plugin/debugErrors.js +24 -24
- package/lib/plugin/eachElement.js +37 -37
- package/lib/plugin/fakerTransform.js +6 -6
- package/lib/plugin/heal.js +66 -63
- package/lib/plugin/pauseOnFail.js +10 -10
- package/lib/plugin/retryFailedStep.js +31 -38
- package/lib/plugin/retryTo.js +28 -28
- package/lib/plugin/screenshotOnFail.js +107 -86
- package/lib/plugin/selenoid.js +131 -117
- package/lib/plugin/standardActingHelpers.js +2 -8
- package/lib/plugin/stepByStepReport.js +102 -92
- package/lib/plugin/stepTimeout.js +23 -22
- package/lib/plugin/subtitles.js +34 -34
- package/lib/plugin/tryTo.js +39 -29
- package/lib/plugin/wdio.js +77 -72
- package/lib/template/heal.js +11 -14
- package/package.json +4 -2
- package/translations/de-DE.js +1 -1
- package/translations/fr-FR.js +1 -1
- package/translations/index.js +9 -9
- package/translations/it-IT.js +1 -1
- package/translations/ja-JP.js +1 -1
- package/translations/pl-PL.js +1 -1
- package/translations/pt-BR.js +1 -1
- package/translations/ru-RU.js +1 -1
- package/translations/zh-CN.js +1 -1
- package/translations/zh-TW.js +1 -1
- package/typings/promiseBasedTypes.d.ts +238 -0
- package/typings/types.d.ts +32 -0
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
const colors = require('chalk')
|
|
2
|
-
const crypto = require('crypto')
|
|
3
|
-
const figures = require('figures')
|
|
4
|
-
const fs = require('fs')
|
|
5
|
-
const mkdirp = require('mkdirp')
|
|
6
|
-
const path = require('path')
|
|
1
|
+
const colors = require('chalk')
|
|
2
|
+
const crypto = require('crypto')
|
|
3
|
+
const figures = require('figures')
|
|
4
|
+
const fs = require('fs')
|
|
5
|
+
const mkdirp = require('mkdirp')
|
|
6
|
+
const path = require('path')
|
|
7
7
|
|
|
8
|
-
const Container = require('../container')
|
|
9
|
-
const recorder = require('../recorder')
|
|
10
|
-
const event = require('../event')
|
|
11
|
-
const output = require('../output')
|
|
12
|
-
const { template, deleteDir } = require('../utils')
|
|
8
|
+
const Container = require('../container')
|
|
9
|
+
const recorder = require('../recorder')
|
|
10
|
+
const event = require('../event')
|
|
11
|
+
const output = require('../output')
|
|
12
|
+
const { template, deleteDir } = require('../utils')
|
|
13
13
|
|
|
14
|
-
const supportedHelpers = require('./standardActingHelpers')
|
|
14
|
+
const supportedHelpers = require('./standardActingHelpers')
|
|
15
15
|
|
|
16
16
|
const defaultConfig = {
|
|
17
17
|
deleteSuccessful: true,
|
|
@@ -21,9 +21,9 @@ const defaultConfig = {
|
|
|
21
21
|
output: global.output_dir,
|
|
22
22
|
screenshotsForAllureReport: false,
|
|
23
23
|
disableScreenshotOnFail: true,
|
|
24
|
-
}
|
|
24
|
+
}
|
|
25
25
|
|
|
26
|
-
const templates = {}
|
|
26
|
+
const templates = {}
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* 
|
|
@@ -63,135 +63,145 @@ const templates = {};
|
|
|
63
63
|
*/
|
|
64
64
|
|
|
65
65
|
module.exports = function (config) {
|
|
66
|
-
const helpers = Container.helpers()
|
|
67
|
-
let helper
|
|
66
|
+
const helpers = Container.helpers()
|
|
67
|
+
let helper
|
|
68
68
|
|
|
69
|
-
config = Object.assign(defaultConfig, config)
|
|
69
|
+
config = Object.assign(defaultConfig, config)
|
|
70
70
|
|
|
71
71
|
for (const helperName of supportedHelpers) {
|
|
72
72
|
if (Object.keys(helpers).indexOf(helperName) > -1) {
|
|
73
|
-
helper = helpers[helperName]
|
|
73
|
+
helper = helpers[helperName]
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
if (!helper) return
|
|
77
|
+
if (!helper) return // no helpers for screenshot
|
|
78
78
|
|
|
79
|
-
let dir
|
|
80
|
-
let stepNum
|
|
81
|
-
let slides = {}
|
|
82
|
-
let error
|
|
83
|
-
let savedStep = null
|
|
84
|
-
let currentTest = null
|
|
85
|
-
let scenarioFailed = false
|
|
79
|
+
let dir
|
|
80
|
+
let stepNum
|
|
81
|
+
let slides = {}
|
|
82
|
+
let error
|
|
83
|
+
let savedStep = null
|
|
84
|
+
let currentTest = null
|
|
85
|
+
let scenarioFailed = false
|
|
86
86
|
|
|
87
|
-
const recordedTests = {}
|
|
88
|
-
const pad = '0000'
|
|
89
|
-
const reportDir = config.output ? path.resolve(global.codecept_dir, config.output) : defaultConfig.output
|
|
87
|
+
const recordedTests = {}
|
|
88
|
+
const pad = '0000'
|
|
89
|
+
const reportDir = config.output ? path.resolve(global.codecept_dir, config.output) : defaultConfig.output
|
|
90
90
|
|
|
91
91
|
event.dispatcher.on(event.test.before, (test) => {
|
|
92
|
-
const sha256hash = crypto
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
92
|
+
const sha256hash = crypto
|
|
93
|
+
.createHash('sha256')
|
|
94
|
+
.update(test.file + test.title)
|
|
95
|
+
.digest('hex')
|
|
96
|
+
dir = path.join(reportDir, `record_${sha256hash}`)
|
|
97
|
+
mkdirp.sync(dir)
|
|
98
|
+
stepNum = 0
|
|
99
|
+
error = null
|
|
100
|
+
slides = {}
|
|
101
|
+
savedStep = null
|
|
102
|
+
currentTest = test
|
|
103
|
+
})
|
|
101
104
|
|
|
102
105
|
event.dispatcher.on(event.step.failed, (step) => {
|
|
103
|
-
recorder.add('screenshot of failed test', async () => persistStep(step), true)
|
|
104
|
-
})
|
|
106
|
+
recorder.add('screenshot of failed test', async () => persistStep(step), true)
|
|
107
|
+
})
|
|
105
108
|
|
|
106
|
-
event.dispatcher.on(event.step.
|
|
109
|
+
event.dispatcher.on(event.step.failed, (step) => {
|
|
110
|
+
recorder.add('screenshot of failed test', async () => persistStep(step), true)
|
|
111
|
+
})
|
|
107
112
|
|
|
108
113
|
event.dispatcher.on(event.test.passed, (test) => {
|
|
109
|
-
if (!config.deleteSuccessful) return persist(test)
|
|
114
|
+
if (!config.deleteSuccessful) return persist(test)
|
|
110
115
|
// cleanup
|
|
111
|
-
deleteDir(dir)
|
|
112
|
-
})
|
|
116
|
+
deleteDir(dir)
|
|
117
|
+
})
|
|
113
118
|
|
|
114
119
|
event.dispatcher.on(event.test.failed, (test, err) => {
|
|
115
120
|
if (test.ctx._runnable.title.includes('hook: ')) {
|
|
116
|
-
output.plugin(
|
|
117
|
-
|
|
121
|
+
output.plugin(
|
|
122
|
+
'stepByStepReport',
|
|
123
|
+
'BeforeSuite/AfterSuite do not have any access to the browser, hence it could not take screenshot.',
|
|
124
|
+
)
|
|
125
|
+
return
|
|
118
126
|
}
|
|
119
|
-
persist(test, err)
|
|
120
|
-
})
|
|
127
|
+
persist(test, err)
|
|
128
|
+
})
|
|
121
129
|
|
|
122
130
|
event.dispatcher.on(event.all.result, () => {
|
|
123
|
-
if (Object.keys(recordedTests).length === 0 || !Object.keys(slides).length) return
|
|
131
|
+
if (Object.keys(recordedTests).length === 0 || !Object.keys(slides).length) return
|
|
124
132
|
|
|
125
|
-
let links = ''
|
|
133
|
+
let links = ''
|
|
126
134
|
|
|
127
135
|
for (const link in recordedTests) {
|
|
128
|
-
links += `<li><a href="${recordedTests[link]}">${link}</a></li>\n
|
|
136
|
+
links += `<li><a href="${recordedTests[link]}">${link}</a></li>\n`
|
|
129
137
|
}
|
|
130
138
|
|
|
131
139
|
const indexHTML = template(templates.index, {
|
|
132
140
|
time: Date().toString(),
|
|
133
141
|
records: links,
|
|
134
|
-
})
|
|
142
|
+
})
|
|
135
143
|
|
|
136
|
-
fs.writeFileSync(path.join(reportDir, 'records.html'), indexHTML)
|
|
144
|
+
fs.writeFileSync(path.join(reportDir, 'records.html'), indexHTML)
|
|
137
145
|
|
|
138
|
-
output.print(
|
|
139
|
-
|
|
146
|
+
output.print(
|
|
147
|
+
`${figures.circleFilled} Step-by-step preview: ${colors.white.bold(`file://${reportDir}/records.html`)}`,
|
|
148
|
+
)
|
|
149
|
+
})
|
|
140
150
|
|
|
141
151
|
async function persistStep(step) {
|
|
142
|
-
if (isStepIgnored(step)) return
|
|
143
|
-
if (savedStep === step) return
|
|
152
|
+
if (isStepIgnored(step)) return
|
|
153
|
+
if (savedStep === step) return // already saved
|
|
144
154
|
// Ignore steps from BeforeSuite function
|
|
145
|
-
if (scenarioFailed && config.disableScreenshotOnFail) return
|
|
146
|
-
if (step.metaStep && step.metaStep.name === 'BeforeSuite') return
|
|
155
|
+
if (scenarioFailed && config.disableScreenshotOnFail) return
|
|
156
|
+
if (step.metaStep && step.metaStep.name === 'BeforeSuite') return
|
|
147
157
|
|
|
148
|
-
const fileName = `${pad.substring(0, pad.length - stepNum.toString().length) + stepNum.toString()}.png
|
|
158
|
+
const fileName = `${pad.substring(0, pad.length - stepNum.toString().length) + stepNum.toString()}.png`
|
|
149
159
|
if (step.status === 'failed') {
|
|
150
|
-
scenarioFailed = true
|
|
160
|
+
scenarioFailed = true
|
|
151
161
|
}
|
|
152
|
-
stepNum
|
|
153
|
-
slides[fileName] = step
|
|
162
|
+
stepNum++
|
|
163
|
+
slides[fileName] = step
|
|
154
164
|
try {
|
|
155
|
-
await helper.saveScreenshot(path.join(dir, fileName), config.fullPageScreenshots)
|
|
165
|
+
await helper.saveScreenshot(path.join(dir, fileName), config.fullPageScreenshots)
|
|
156
166
|
} catch (err) {
|
|
157
|
-
output.plugin(`Can't save step screenshot: ${err}`)
|
|
158
|
-
error = err
|
|
159
|
-
return
|
|
167
|
+
output.plugin(`Can't save step screenshot: ${err}`)
|
|
168
|
+
error = err
|
|
169
|
+
return
|
|
160
170
|
} finally {
|
|
161
|
-
savedStep = step
|
|
171
|
+
savedStep = step
|
|
162
172
|
}
|
|
163
173
|
|
|
164
|
-
if (!currentTest.artifacts.screenshots) currentTest.artifacts.screenshots = []
|
|
174
|
+
if (!currentTest.artifacts.screenshots) currentTest.artifacts.screenshots = []
|
|
165
175
|
// added attachments to test
|
|
166
|
-
currentTest.artifacts.screenshots.push(path.join(dir, fileName))
|
|
176
|
+
currentTest.artifacts.screenshots.push(path.join(dir, fileName))
|
|
167
177
|
|
|
168
|
-
const allureReporter = Container.plugins('allure')
|
|
178
|
+
const allureReporter = Container.plugins('allure')
|
|
169
179
|
if (allureReporter && config.screenshotsForAllureReport) {
|
|
170
|
-
output.plugin('stepByStepReport', 'Adding screenshot to Allure')
|
|
171
|
-
allureReporter.addAttachment(`Screenshot of step ${step}`, fs.readFileSync(path.join(dir, fileName)), 'image/png')
|
|
180
|
+
output.plugin('stepByStepReport', 'Adding screenshot to Allure')
|
|
181
|
+
allureReporter.addAttachment(`Screenshot of step ${step}`, fs.readFileSync(path.join(dir, fileName)), 'image/png')
|
|
172
182
|
}
|
|
173
183
|
}
|
|
174
184
|
|
|
175
185
|
function persist(test) {
|
|
176
|
-
if (error) return
|
|
186
|
+
if (error) return
|
|
177
187
|
|
|
178
|
-
let indicatorHtml = ''
|
|
179
|
-
let slideHtml = ''
|
|
188
|
+
let indicatorHtml = ''
|
|
189
|
+
let slideHtml = ''
|
|
180
190
|
|
|
181
191
|
for (const i in slides) {
|
|
182
|
-
const step = slides[i]
|
|
183
|
-
const stepNum = parseInt(i, 10)
|
|
192
|
+
const step = slides[i]
|
|
193
|
+
const stepNum = parseInt(i, 10)
|
|
184
194
|
indicatorHtml += template(templates.indicator, {
|
|
185
195
|
step: stepNum,
|
|
186
196
|
isActive: stepNum ? '' : 'class="active"',
|
|
187
|
-
})
|
|
197
|
+
})
|
|
188
198
|
|
|
189
199
|
slideHtml += template(templates.slides, {
|
|
190
200
|
image: i,
|
|
191
201
|
caption: step.toString().replace(/\[\d{2}m/g, ''), // remove ANSI escape sequence
|
|
192
202
|
isActive: stepNum ? '' : 'active',
|
|
193
203
|
isError: step.status === 'failed' ? 'error' : '',
|
|
194
|
-
})
|
|
204
|
+
})
|
|
195
205
|
}
|
|
196
206
|
|
|
197
207
|
const html = template(templates.global, {
|
|
@@ -200,21 +210,21 @@ module.exports = function (config) {
|
|
|
200
210
|
feature: test.parent && test.parent.title,
|
|
201
211
|
test: test.title,
|
|
202
212
|
carousel_class: config.animateSlides ? ' slide' : '',
|
|
203
|
-
})
|
|
213
|
+
})
|
|
204
214
|
|
|
205
|
-
const index = path.join(dir, 'index.html')
|
|
206
|
-
fs.writeFileSync(index, html)
|
|
207
|
-
recordedTests[`${test.parent.title}: ${test.title}`] = path.relative(reportDir, index)
|
|
215
|
+
const index = path.join(dir, 'index.html')
|
|
216
|
+
fs.writeFileSync(index, html)
|
|
217
|
+
recordedTests[`${test.parent.title}: ${test.title}`] = path.relative(reportDir, index)
|
|
208
218
|
}
|
|
209
219
|
|
|
210
220
|
function isStepIgnored(step) {
|
|
211
|
-
if (!config.ignoreSteps) return
|
|
221
|
+
if (!config.ignoreSteps) return
|
|
212
222
|
for (const pattern of config.ignoreSteps || []) {
|
|
213
|
-
if (step.name.match(pattern)) return true
|
|
223
|
+
if (step.name.match(pattern)) return true
|
|
214
224
|
}
|
|
215
|
-
return false
|
|
225
|
+
return false
|
|
216
226
|
}
|
|
217
|
-
}
|
|
227
|
+
}
|
|
218
228
|
|
|
219
229
|
templates.slides = `
|
|
220
230
|
<div class="item {{isActive}}">
|
|
@@ -226,11 +236,11 @@ templates.slides = `
|
|
|
226
236
|
<small>scroll up and down to see the full page</small>
|
|
227
237
|
</div>
|
|
228
238
|
</div>
|
|
229
|
-
|
|
239
|
+
`
|
|
230
240
|
|
|
231
241
|
templates.indicator = `
|
|
232
242
|
<li data-target="#steps" data-slide-to="{{step}}" {{isActive}}></li>
|
|
233
|
-
|
|
243
|
+
`
|
|
234
244
|
|
|
235
245
|
templates.index = `
|
|
236
246
|
<!DOCTYPE html>
|
|
@@ -259,7 +269,7 @@ templates.index = `
|
|
|
259
269
|
|
|
260
270
|
</body>
|
|
261
271
|
</html>
|
|
262
|
-
|
|
272
|
+
`
|
|
263
273
|
|
|
264
274
|
templates.global = `
|
|
265
275
|
<!DOCTYPE html>
|
|
@@ -369,4 +379,4 @@ templates.global = `
|
|
|
369
379
|
</body>
|
|
370
380
|
|
|
371
381
|
</html>
|
|
372
|
-
|
|
382
|
+
`
|
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
const event = require('../event')
|
|
2
|
-
const TIMEOUT_ORDER = require('../step').TIMEOUT_ORDER
|
|
1
|
+
const event = require('../event')
|
|
2
|
+
const TIMEOUT_ORDER = require('../step').TIMEOUT_ORDER
|
|
3
3
|
|
|
4
4
|
const defaultConfig = {
|
|
5
5
|
timeout: 150,
|
|
6
6
|
overrideStepLimits: false,
|
|
7
|
-
noTimeoutSteps: [
|
|
8
|
-
'amOnPage',
|
|
9
|
-
'wait*',
|
|
10
|
-
],
|
|
7
|
+
noTimeoutSteps: ['amOnPage', 'wait*'],
|
|
11
8
|
customTimeoutSteps: [],
|
|
12
|
-
}
|
|
9
|
+
}
|
|
13
10
|
|
|
14
11
|
/**
|
|
15
12
|
* Set timeout for test steps globally.
|
|
@@ -65,27 +62,31 @@ const defaultConfig = {
|
|
|
65
62
|
*
|
|
66
63
|
*/
|
|
67
64
|
module.exports = (config) => {
|
|
68
|
-
config = Object.assign(defaultConfig, config)
|
|
65
|
+
config = Object.assign(defaultConfig, config)
|
|
69
66
|
// below override rule makes sure customTimeoutSteps go first but then they override noTimeoutSteps in case of exact pattern match
|
|
70
|
-
config.customTimeoutSteps = config.customTimeoutSteps.concat(config.noTimeoutSteps).concat(config.customTimeoutSteps)
|
|
67
|
+
config.customTimeoutSteps = config.customTimeoutSteps.concat(config.noTimeoutSteps).concat(config.customTimeoutSteps)
|
|
71
68
|
|
|
72
69
|
event.dispatcher.on(event.step.before, (step) => {
|
|
73
|
-
let stepTimeout
|
|
70
|
+
let stepTimeout
|
|
74
71
|
for (let stepRule of config.customTimeoutSteps) {
|
|
75
|
-
let customTimeout = 0
|
|
72
|
+
let customTimeout = 0
|
|
76
73
|
if (Array.isArray(stepRule)) {
|
|
77
|
-
if (stepRule.length > 1) customTimeout = stepRule[1]
|
|
78
|
-
stepRule = stepRule[0]
|
|
74
|
+
if (stepRule.length > 1) customTimeout = stepRule[1]
|
|
75
|
+
stepRule = stepRule[0]
|
|
79
76
|
}
|
|
80
|
-
if (
|
|
81
|
-
|
|
82
|
-
|
|
77
|
+
if (
|
|
78
|
+
stepRule instanceof RegExp
|
|
79
|
+
? step.name.match(stepRule)
|
|
80
|
+
: step.name === stepRule || (stepRule.indexOf('*') && step.name.startsWith(stepRule.slice(0, -1)))
|
|
83
81
|
) {
|
|
84
|
-
stepTimeout = customTimeout
|
|
85
|
-
break
|
|
82
|
+
stepTimeout = customTimeout
|
|
83
|
+
break
|
|
86
84
|
}
|
|
87
85
|
}
|
|
88
|
-
stepTimeout = stepTimeout === undefined ? config.timeout : stepTimeout
|
|
89
|
-
step.setTimeout(
|
|
90
|
-
|
|
91
|
-
|
|
86
|
+
stepTimeout = stepTimeout === undefined ? config.timeout : stepTimeout
|
|
87
|
+
step.setTimeout(
|
|
88
|
+
stepTimeout * 1000,
|
|
89
|
+
config.overrideStepLimits ? TIMEOUT_ORDER.stepTimeoutHard : TIMEOUT_ORDER.stepTimeoutSoft,
|
|
90
|
+
)
|
|
91
|
+
})
|
|
92
|
+
}
|
package/lib/plugin/subtitles.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
const { v4: uuidv4 } = require('uuid')
|
|
2
|
-
const fsPromise = require('fs').promises
|
|
3
|
-
const path = require('path')
|
|
4
|
-
const event = require('../event')
|
|
1
|
+
const { v4: uuidv4 } = require('uuid')
|
|
2
|
+
const fsPromise = require('fs').promises
|
|
3
|
+
const path = require('path')
|
|
4
|
+
const event = require('../event')
|
|
5
5
|
|
|
6
6
|
// This will convert a given timestamp in milliseconds to
|
|
7
7
|
// an SRT recognized timestamp, ie HH:mm:ss,SSS
|
|
8
8
|
function formatTimestamp(timestampInMs) {
|
|
9
|
-
const date = new Date(0, 0, 0, 0, 0, 0, timestampInMs)
|
|
10
|
-
const hours = date.getHours()
|
|
11
|
-
const minutes = date.getMinutes()
|
|
12
|
-
const seconds = date.getSeconds()
|
|
13
|
-
const ms = timestampInMs - (hours * 3600000 + minutes * 60000 + seconds * 1000)
|
|
14
|
-
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')},${ms.toString().padStart(3, '0')}
|
|
9
|
+
const date = new Date(0, 0, 0, 0, 0, 0, timestampInMs)
|
|
10
|
+
const hours = date.getHours()
|
|
11
|
+
const minutes = date.getMinutes()
|
|
12
|
+
const seconds = date.getSeconds()
|
|
13
|
+
const ms = timestampInMs - (hours * 3600000 + minutes * 60000 + seconds * 1000)
|
|
14
|
+
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')},${ms.toString().padStart(3, '0')}`
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
let steps = {}
|
|
18
|
-
let testStartedAt
|
|
17
|
+
let steps = {}
|
|
18
|
+
let testStartedAt
|
|
19
19
|
/**
|
|
20
20
|
* Automatically captures steps as subtitle, and saves it as an artifact when a video is found for a failed test
|
|
21
21
|
*
|
|
@@ -30,40 +30,40 @@ let testStartedAt;
|
|
|
30
30
|
*/
|
|
31
31
|
module.exports = function () {
|
|
32
32
|
event.dispatcher.on(event.test.before, (_) => {
|
|
33
|
-
testStartedAt = Date.now()
|
|
34
|
-
steps = {}
|
|
35
|
-
})
|
|
33
|
+
testStartedAt = Date.now()
|
|
34
|
+
steps = {}
|
|
35
|
+
})
|
|
36
36
|
|
|
37
37
|
event.dispatcher.on(event.step.started, (step) => {
|
|
38
|
-
const stepStartedAt = Date.now()
|
|
39
|
-
step.id = uuidv4()
|
|
38
|
+
const stepStartedAt = Date.now()
|
|
39
|
+
step.id = uuidv4()
|
|
40
40
|
|
|
41
|
-
let title = `${step.actor}.${step.name}(${step.args ? step.args.join(',') : ''})
|
|
41
|
+
let title = `${step.actor}.${step.name}(${step.args ? step.args.join(',') : ''})`
|
|
42
42
|
if (title.length > 100) {
|
|
43
|
-
title = `${title.substring(0, 100)}
|
|
43
|
+
title = `${title.substring(0, 100)}...`
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
steps[step.id] = {
|
|
47
47
|
start: formatTimestamp(stepStartedAt - testStartedAt),
|
|
48
48
|
startedAt: stepStartedAt,
|
|
49
49
|
title,
|
|
50
|
-
}
|
|
51
|
-
})
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
52
|
|
|
53
53
|
event.dispatcher.on(event.step.finished, (step) => {
|
|
54
54
|
if (step && step.id && steps[step.id]) {
|
|
55
|
-
steps[step.id].end = formatTimestamp(Date.now() - testStartedAt)
|
|
55
|
+
steps[step.id].end = formatTimestamp(Date.now() - testStartedAt)
|
|
56
56
|
}
|
|
57
|
-
})
|
|
57
|
+
})
|
|
58
58
|
|
|
59
59
|
event.dispatcher.on(event.test.after, async (test) => {
|
|
60
60
|
if (test && test.artifacts && test.artifacts.video) {
|
|
61
|
-
const stepsSortedByStartTime = Object.values(steps)
|
|
61
|
+
const stepsSortedByStartTime = Object.values(steps)
|
|
62
62
|
stepsSortedByStartTime.sort((stepA, stepB) => {
|
|
63
|
-
return stepA.startedAt - stepB.startedAt
|
|
64
|
-
})
|
|
63
|
+
return stepA.startedAt - stepB.startedAt
|
|
64
|
+
})
|
|
65
65
|
|
|
66
|
-
let subtitle = ''
|
|
66
|
+
let subtitle = ''
|
|
67
67
|
|
|
68
68
|
// For an SRT file, every subtitle has to be in the format as mentioned below:
|
|
69
69
|
//
|
|
@@ -76,13 +76,13 @@ module.exports = function () {
|
|
|
76
76
|
${step.start} --> ${step.end}
|
|
77
77
|
${step.title}
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
`
|
|
80
80
|
}
|
|
81
|
-
})
|
|
81
|
+
})
|
|
82
82
|
|
|
83
|
-
const { dir: artifactsDirectory, name: fileName } = path.parse(test.artifacts.video)
|
|
84
|
-
await fsPromise.writeFile(`${artifactsDirectory}/${fileName}.srt`, subtitle)
|
|
85
|
-
test.artifacts.subtitle = `${artifactsDirectory}/${fileName}.srt
|
|
83
|
+
const { dir: artifactsDirectory, name: fileName } = path.parse(test.artifacts.video)
|
|
84
|
+
await fsPromise.writeFile(`${artifactsDirectory}/${fileName}.srt`, subtitle)
|
|
85
|
+
test.artifacts.subtitle = `${artifactsDirectory}/${fileName}.srt`
|
|
86
86
|
}
|
|
87
|
-
})
|
|
88
|
-
}
|
|
87
|
+
})
|
|
88
|
+
}
|
package/lib/plugin/tryTo.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
const recorder = require('../recorder')
|
|
2
|
-
const { debug } = require('../output')
|
|
1
|
+
const recorder = require('../recorder')
|
|
2
|
+
const { debug } = require('../output')
|
|
3
3
|
|
|
4
4
|
const defaultConfig = {
|
|
5
5
|
registerGlobal: true,
|
|
6
|
-
}
|
|
6
|
+
}
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
*
|
|
@@ -71,35 +71,45 @@ const defaultConfig = {
|
|
|
71
71
|
*
|
|
72
72
|
*/
|
|
73
73
|
module.exports = function (config) {
|
|
74
|
-
config = Object.assign(defaultConfig, config)
|
|
74
|
+
config = Object.assign(defaultConfig, config)
|
|
75
75
|
|
|
76
76
|
if (config.registerGlobal) {
|
|
77
|
-
global.tryTo = tryTo
|
|
77
|
+
global.tryTo = tryTo
|
|
78
78
|
}
|
|
79
|
-
return tryTo
|
|
80
|
-
}
|
|
79
|
+
return tryTo
|
|
80
|
+
}
|
|
81
81
|
|
|
82
82
|
function tryTo(callback) {
|
|
83
|
-
let result = false
|
|
84
|
-
return recorder.add(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
recorder.
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
return
|
|
103
|
-
|
|
104
|
-
|
|
83
|
+
let result = false
|
|
84
|
+
return recorder.add(
|
|
85
|
+
'tryTo',
|
|
86
|
+
() => {
|
|
87
|
+
recorder.session.start('tryTo')
|
|
88
|
+
process.env.TRY_TO = 'true'
|
|
89
|
+
callback()
|
|
90
|
+
recorder.add(() => {
|
|
91
|
+
result = true
|
|
92
|
+
recorder.session.restore('tryTo')
|
|
93
|
+
return result
|
|
94
|
+
})
|
|
95
|
+
recorder.session.catch((err) => {
|
|
96
|
+
result = false
|
|
97
|
+
const msg = err.inspect ? err.inspect() : err.toString()
|
|
98
|
+
debug(`Unsuccessful try > ${msg}`)
|
|
99
|
+
recorder.session.restore('tryTo')
|
|
100
|
+
return result
|
|
101
|
+
})
|
|
102
|
+
return recorder.add(
|
|
103
|
+
'result',
|
|
104
|
+
() => {
|
|
105
|
+
process.env.TRY_TO = undefined
|
|
106
|
+
return result
|
|
107
|
+
},
|
|
108
|
+
true,
|
|
109
|
+
false,
|
|
110
|
+
)
|
|
111
|
+
},
|
|
112
|
+
false,
|
|
113
|
+
false,
|
|
114
|
+
)
|
|
105
115
|
}
|