codeceptjs 4.0.0-beta.1 → 4.0.0-beta.3
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 -81
- package/lib/actor.js +13 -13
- package/lib/ai.js +10 -13
- package/lib/assert/empty.js +20 -21
- package/lib/assert/equal.js +37 -39
- package/lib/assert/error.js +14 -14
- package/lib/assert/include.js +46 -47
- package/lib/assert/throws.js +13 -11
- package/lib/assert/truth.js +19 -22
- package/lib/assert.js +4 -2
- package/lib/cli.js +57 -49
- package/lib/codecept.js +142 -155
- package/lib/colorUtils.js +3 -3
- package/lib/command/configMigrate.js +58 -52
- package/lib/command/definitions.js +88 -89
- package/lib/command/dryRun.js +71 -68
- package/lib/command/generate.js +197 -188
- package/lib/command/gherkin/init.js +27 -16
- package/lib/command/gherkin/snippets.js +20 -20
- package/lib/command/gherkin/steps.js +8 -8
- package/lib/command/info.js +40 -38
- package/lib/command/init.js +290 -288
- package/lib/command/interactive.js +32 -32
- package/lib/command/list.js +26 -26
- package/lib/command/run-multiple/chunk.js +5 -5
- package/lib/command/run-multiple/collection.js +3 -3
- package/lib/command/run-multiple/run.js +6 -2
- package/lib/command/run-multiple.js +113 -93
- package/lib/command/run-rerun.js +20 -25
- package/lib/command/run-workers.js +64 -66
- package/lib/command/run.js +26 -29
- package/lib/command/utils.js +80 -65
- package/lib/command/workers/runTests.js +10 -10
- package/lib/config.js +10 -9
- package/lib/container.js +40 -48
- package/lib/data/context.js +60 -59
- package/lib/data/dataScenarioConfig.js +47 -47
- package/lib/data/dataTableArgument.js +29 -29
- package/lib/data/table.js +26 -20
- package/lib/event.js +163 -167
- package/lib/heal.js +13 -17
- package/lib/helper/AI.js +130 -41
- package/lib/helper/ApiDataFactory.js +73 -69
- package/lib/helper/Appium.js +413 -382
- package/lib/helper/ExpectHelper.js +40 -48
- package/lib/helper/FileSystem.js +80 -79
- package/lib/helper/GraphQL.js +44 -43
- package/lib/helper/GraphQLDataFactory.js +50 -50
- package/lib/helper/JSONResponse.js +65 -62
- package/lib/helper/Mochawesome.js +28 -28
- package/lib/helper/MockServer.js +12 -14
- package/lib/helper/Nightmare.js +662 -566
- package/lib/helper/Playwright.js +1361 -1216
- package/lib/helper/Protractor.js +663 -627
- package/lib/helper/Puppeteer.js +1231 -1128
- package/lib/helper/REST.js +159 -68
- package/lib/helper/SoftExpectHelper.js +2 -2
- package/lib/helper/TestCafe.js +490 -484
- package/lib/helper/WebDriver.js +1297 -1156
- package/lib/helper/clientscripts/PollyWebDriverExt.js +1 -1
- package/lib/helper/errors/ConnectionRefused.js +1 -1
- package/lib/helper/errors/ElementAssertion.js +2 -2
- package/lib/helper/errors/ElementNotFound.js +2 -2
- package/lib/helper/errors/RemoteBrowserConnectionRefused.js +1 -1
- package/lib/helper/extras/Console.js +1 -1
- package/lib/helper/extras/PlaywrightPropEngine.js +2 -2
- package/lib/helper/extras/PlaywrightReactVueLocator.js +1 -1
- package/lib/helper/extras/PlaywrightRestartOpts.js +21 -18
- package/lib/helper/extras/Popup.js +1 -1
- package/lib/helper/extras/React.js +3 -3
- package/lib/helper/network/actions.js +14 -7
- package/lib/helper/network/utils.js +3 -2
- package/lib/helper/scripts/blurElement.js +1 -1
- package/lib/helper/scripts/focusElement.js +1 -1
- package/lib/helper/scripts/highlightElement.js +1 -1
- package/lib/helper/scripts/isElementClickable.js +1 -1
- package/lib/helper/testcafe/testControllerHolder.js +1 -1
- package/lib/helper/testcafe/testcafe-utils.js +6 -7
- package/lib/helper.js +1 -3
- package/lib/history.js +6 -5
- package/lib/hooks.js +6 -6
- package/lib/html.js +7 -7
- package/lib/index.js +25 -41
- package/lib/interfaces/bdd.js +47 -64
- package/lib/interfaces/featureConfig.js +19 -19
- package/lib/interfaces/gherkin.js +124 -118
- package/lib/interfaces/scenarioConfig.js +29 -29
- package/lib/listener/artifacts.js +9 -9
- package/lib/listener/config.js +24 -24
- 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 -53
- package/lib/listener/timeout.js +54 -54
- package/lib/locator.js +6 -10
- package/lib/mochaFactory.js +18 -15
- package/lib/output.js +6 -10
- package/lib/parser.js +15 -12
- package/lib/pause.js +40 -33
- package/lib/plugin/allure.js +15 -15
- package/lib/plugin/autoDelay.js +29 -37
- package/lib/plugin/autoLogin.js +70 -65
- package/lib/plugin/commentStep.js +18 -18
- package/lib/plugin/coverage.js +115 -67
- package/lib/plugin/customLocator.js +21 -20
- package/lib/plugin/debugErrors.js +24 -24
- package/lib/plugin/eachElement.js +38 -38
- package/lib/plugin/fakerTransform.js +6 -6
- package/lib/plugin/heal.js +67 -108
- package/lib/plugin/pauseOnFail.js +11 -11
- package/lib/plugin/retryFailedStep.js +32 -39
- package/lib/plugin/retryTo.js +46 -40
- package/lib/plugin/screenshotOnFail.js +109 -87
- package/lib/plugin/selenoid.js +131 -118
- package/lib/plugin/standardActingHelpers.js +2 -8
- package/lib/plugin/stepByStepReport.js +110 -91
- package/lib/plugin/stepTimeout.js +24 -23
- package/lib/plugin/subtitles.js +34 -35
- package/lib/plugin/tryTo.js +40 -30
- package/lib/plugin/wdio.js +78 -75
- package/lib/recorder.js +14 -17
- package/lib/rerun.js +11 -10
- package/lib/scenario.js +25 -23
- package/lib/secret.js +4 -2
- package/lib/session.js +10 -10
- package/lib/step.js +12 -9
- package/lib/store.js +2 -3
- package/lib/transform.js +1 -1
- package/lib/translation.js +7 -8
- package/lib/ui.js +12 -14
- package/lib/utils.js +70 -72
- package/lib/within.js +10 -10
- package/lib/workerStorage.js +27 -25
- package/lib/workers.js +29 -32
- package/package.json +56 -57
- package/translations/de-DE.js +1 -1
- package/translations/fr-FR.js +1 -1
- package/translations/index.js +9 -13
- 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/index.d.ts +415 -65
- package/typings/promiseBasedTypes.d.ts +32 -0
- package/typings/types.d.ts +32 -0
- package/lib/dirname.js +0 -5
- package/lib/helper/Expect.js +0 -425
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
+
|
|
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
|
+
|
|
14
|
+
const supportedHelpers = require('./standardActingHelpers')
|
|
13
15
|
|
|
14
16
|
const defaultConfig = {
|
|
15
17
|
deleteSuccessful: true,
|
|
@@ -19,9 +21,9 @@ const defaultConfig = {
|
|
|
19
21
|
output: global.output_dir,
|
|
20
22
|
screenshotsForAllureReport: false,
|
|
21
23
|
disableScreenshotOnFail: true,
|
|
22
|
-
}
|
|
24
|
+
}
|
|
23
25
|
|
|
24
|
-
const templates = {}
|
|
26
|
+
const templates = {}
|
|
25
27
|
|
|
26
28
|
/**
|
|
27
29
|
* 
|
|
@@ -60,135 +62,152 @@ const templates = {};
|
|
|
60
62
|
* @param {*} config
|
|
61
63
|
*/
|
|
62
64
|
|
|
63
|
-
|
|
64
|
-
const helpers = Container.helpers()
|
|
65
|
-
let helper
|
|
65
|
+
module.exports = function (config) {
|
|
66
|
+
const helpers = Container.helpers()
|
|
67
|
+
let helper
|
|
66
68
|
|
|
67
|
-
config = Object.assign(defaultConfig, config)
|
|
69
|
+
config = Object.assign(defaultConfig, config)
|
|
68
70
|
|
|
69
71
|
for (const helperName of supportedHelpers) {
|
|
70
72
|
if (Object.keys(helpers).indexOf(helperName) > -1) {
|
|
71
|
-
helper = helpers[helperName]
|
|
73
|
+
helper = helpers[helperName]
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
|
|
75
|
-
if (!helper) return
|
|
77
|
+
if (!helper) return // no helpers for screenshot
|
|
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
|
|
76
86
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
let error;
|
|
81
|
-
let savedStep = null;
|
|
82
|
-
let currentTest = null;
|
|
83
|
-
let scenarioFailed = false;
|
|
87
|
+
const recordedTests = {}
|
|
88
|
+
const pad = '0000'
|
|
89
|
+
const reportDir = config.output ? path.resolve(global.codecept_dir, config.output) : defaultConfig.output
|
|
84
90
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
91
|
+
event.dispatcher.on(event.suite.before, (suite) => {
|
|
92
|
+
stepNum = -1
|
|
93
|
+
})
|
|
88
94
|
|
|
89
95
|
event.dispatcher.on(event.test.before, (test) => {
|
|
90
|
-
const sha256hash = crypto
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
const sha256hash = crypto
|
|
97
|
+
.createHash('sha256')
|
|
98
|
+
.update(test.file + test.title)
|
|
99
|
+
.digest('hex')
|
|
100
|
+
dir = path.join(reportDir, `record_${sha256hash}`)
|
|
101
|
+
mkdirp.sync(dir)
|
|
102
|
+
stepNum = 0
|
|
103
|
+
error = null
|
|
104
|
+
slides = {}
|
|
105
|
+
savedStep = null
|
|
106
|
+
currentTest = test
|
|
107
|
+
})
|
|
99
108
|
|
|
100
109
|
event.dispatcher.on(event.step.failed, (step) => {
|
|
101
|
-
recorder.add('screenshot of failed test', async () => persistStep(step), true)
|
|
102
|
-
})
|
|
110
|
+
recorder.add('screenshot of failed test', async () => persistStep(step), true)
|
|
111
|
+
})
|
|
103
112
|
|
|
104
|
-
event.dispatcher.on(event.step.after,
|
|
113
|
+
event.dispatcher.on(event.step.after, (step) => {
|
|
114
|
+
recorder.add('screenshot of step of test', async () => persistStep(step), true)
|
|
115
|
+
})
|
|
105
116
|
|
|
106
117
|
event.dispatcher.on(event.test.passed, (test) => {
|
|
107
|
-
if (!config.deleteSuccessful) return persist(test)
|
|
118
|
+
if (!config.deleteSuccessful) return persist(test)
|
|
108
119
|
// cleanup
|
|
109
|
-
deleteDir(dir)
|
|
110
|
-
})
|
|
120
|
+
deleteDir(dir)
|
|
121
|
+
})
|
|
111
122
|
|
|
112
123
|
event.dispatcher.on(event.test.failed, (test, err) => {
|
|
113
124
|
if (test.ctx._runnable.title.includes('hook: ')) {
|
|
114
|
-
output.
|
|
115
|
-
|
|
125
|
+
output.plugin(
|
|
126
|
+
'stepByStepReport',
|
|
127
|
+
'BeforeSuite/AfterSuite do not have any access to the browser, hence it could not take screenshot.',
|
|
128
|
+
)
|
|
129
|
+
return
|
|
116
130
|
}
|
|
117
|
-
|
|
131
|
+
persist(test, err)
|
|
132
|
+
})
|
|
118
133
|
|
|
119
134
|
event.dispatcher.on(event.all.result, () => {
|
|
120
|
-
if (!Object.keys(slides).length) return
|
|
135
|
+
if (Object.keys(recordedTests).length === 0 || !Object.keys(slides).length) return
|
|
121
136
|
|
|
122
|
-
let links = ''
|
|
137
|
+
let links = ''
|
|
123
138
|
|
|
124
139
|
for (const link in recordedTests) {
|
|
125
|
-
links += `<li><a href="${recordedTests[link]}">${link}</a></li>\n
|
|
140
|
+
links += `<li><a href="${recordedTests[link]}">${link}</a></li>\n`
|
|
126
141
|
}
|
|
127
142
|
|
|
128
143
|
const indexHTML = template(templates.index, {
|
|
129
144
|
time: Date().toString(),
|
|
130
145
|
records: links,
|
|
131
|
-
})
|
|
146
|
+
})
|
|
132
147
|
|
|
133
|
-
fs.writeFileSync(path.join(reportDir, 'records.html'), indexHTML)
|
|
148
|
+
fs.writeFileSync(path.join(reportDir, 'records.html'), indexHTML)
|
|
134
149
|
|
|
135
|
-
output.print(
|
|
136
|
-
|
|
150
|
+
output.print(
|
|
151
|
+
`${figures.circleFilled} Step-by-step preview: ${colors.white.bold(`file://${reportDir}/records.html`)}`,
|
|
152
|
+
)
|
|
153
|
+
})
|
|
137
154
|
|
|
138
155
|
async function persistStep(step) {
|
|
139
|
-
if (
|
|
140
|
-
if (
|
|
156
|
+
if (stepNum === -1) return // Ignore steps from BeforeSuite function
|
|
157
|
+
if (isStepIgnored(step)) return
|
|
158
|
+
if (savedStep === step) return // already saved
|
|
141
159
|
// Ignore steps from BeforeSuite function
|
|
142
|
-
if (scenarioFailed && config.disableScreenshotOnFail) return
|
|
143
|
-
if (step.metaStep && step.metaStep.name === 'BeforeSuite') return
|
|
160
|
+
if (scenarioFailed && config.disableScreenshotOnFail) return
|
|
161
|
+
if (step.metaStep && step.metaStep.name === 'BeforeSuite') return
|
|
162
|
+
if (!step.test) return // Ignore steps from AfterSuite
|
|
144
163
|
|
|
145
|
-
const fileName = `${pad.substring(0, pad.length - stepNum.toString().length) + stepNum.toString()}.png
|
|
164
|
+
const fileName = `${pad.substring(0, pad.length - stepNum.toString().length) + stepNum.toString()}.png`
|
|
146
165
|
if (step.status === 'failed') {
|
|
147
|
-
scenarioFailed = true
|
|
166
|
+
scenarioFailed = true
|
|
148
167
|
}
|
|
149
|
-
stepNum
|
|
150
|
-
slides[fileName] = step
|
|
168
|
+
stepNum++
|
|
169
|
+
slides[fileName] = step
|
|
151
170
|
try {
|
|
152
|
-
await helper.saveScreenshot(path.
|
|
171
|
+
await helper.saveScreenshot(path.join(dir, fileName), config.fullPageScreenshots)
|
|
153
172
|
} catch (err) {
|
|
154
|
-
output.
|
|
155
|
-
error = err
|
|
156
|
-
return
|
|
173
|
+
output.plugin(`Can't save step screenshot: ${err}`)
|
|
174
|
+
error = err
|
|
175
|
+
return
|
|
157
176
|
} finally {
|
|
158
|
-
savedStep = step
|
|
177
|
+
savedStep = step
|
|
159
178
|
}
|
|
160
179
|
|
|
161
|
-
if (!currentTest.artifacts.screenshots) currentTest.artifacts.screenshots = []
|
|
180
|
+
if (!currentTest.artifacts.screenshots) currentTest.artifacts.screenshots = []
|
|
162
181
|
// added attachments to test
|
|
163
|
-
currentTest.artifacts.screenshots.push(path.join(dir, fileName))
|
|
182
|
+
currentTest.artifacts.screenshots.push(path.join(dir, fileName))
|
|
164
183
|
|
|
165
|
-
const allureReporter = Container.plugins('allure')
|
|
184
|
+
const allureReporter = Container.plugins('allure')
|
|
166
185
|
if (allureReporter && config.screenshotsForAllureReport) {
|
|
167
|
-
output.
|
|
168
|
-
allureReporter.addAttachment(`Screenshot of step ${step}`, fs.readFileSync(path.join(dir, fileName)), 'image/png')
|
|
186
|
+
output.plugin('stepByStepReport', 'Adding screenshot to Allure')
|
|
187
|
+
allureReporter.addAttachment(`Screenshot of step ${step}`, fs.readFileSync(path.join(dir, fileName)), 'image/png')
|
|
169
188
|
}
|
|
170
189
|
}
|
|
171
190
|
|
|
172
191
|
function persist(test) {
|
|
173
|
-
if (error) return
|
|
192
|
+
if (error) return
|
|
174
193
|
|
|
175
|
-
let indicatorHtml = ''
|
|
176
|
-
let slideHtml = ''
|
|
194
|
+
let indicatorHtml = ''
|
|
195
|
+
let slideHtml = ''
|
|
177
196
|
|
|
178
197
|
for (const i in slides) {
|
|
179
|
-
const step = slides[i]
|
|
180
|
-
const stepNum = parseInt(i, 10)
|
|
198
|
+
const step = slides[i]
|
|
199
|
+
const stepNum = parseInt(i, 10)
|
|
181
200
|
indicatorHtml += template(templates.indicator, {
|
|
182
201
|
step: stepNum,
|
|
183
202
|
isActive: stepNum ? '' : 'class="active"',
|
|
184
|
-
})
|
|
203
|
+
})
|
|
185
204
|
|
|
186
205
|
slideHtml += template(templates.slides, {
|
|
187
206
|
image: i,
|
|
188
207
|
caption: step.toString().replace(/\[\d{2}m/g, ''), // remove ANSI escape sequence
|
|
189
208
|
isActive: stepNum ? '' : 'active',
|
|
190
209
|
isError: step.status === 'failed' ? 'error' : '',
|
|
191
|
-
})
|
|
210
|
+
})
|
|
192
211
|
}
|
|
193
212
|
|
|
194
213
|
const html = template(templates.global, {
|
|
@@ -197,19 +216,19 @@ export default function (config) {
|
|
|
197
216
|
feature: test.parent && test.parent.title,
|
|
198
217
|
test: test.title,
|
|
199
218
|
carousel_class: config.animateSlides ? ' slide' : '',
|
|
200
|
-
})
|
|
219
|
+
})
|
|
201
220
|
|
|
202
|
-
const index = path.join(dir, 'index.html')
|
|
203
|
-
fs.writeFileSync(index, html)
|
|
204
|
-
recordedTests[`${test.parent.title}: ${test.title}`] = path.relative(reportDir, index)
|
|
221
|
+
const index = path.join(dir, 'index.html')
|
|
222
|
+
fs.writeFileSync(index, html)
|
|
223
|
+
recordedTests[`${test.parent.title}: ${test.title}`] = path.relative(reportDir, index)
|
|
205
224
|
}
|
|
206
225
|
|
|
207
226
|
function isStepIgnored(step) {
|
|
208
|
-
if (!config.ignoreSteps) return
|
|
227
|
+
if (!config.ignoreSteps) return
|
|
209
228
|
for (const pattern of config.ignoreSteps || []) {
|
|
210
|
-
if (step.name.match(pattern)) return true
|
|
229
|
+
if (step.name.match(pattern)) return true
|
|
211
230
|
}
|
|
212
|
-
return false
|
|
231
|
+
return false
|
|
213
232
|
}
|
|
214
233
|
}
|
|
215
234
|
|
|
@@ -223,11 +242,11 @@ templates.slides = `
|
|
|
223
242
|
<small>scroll up and down to see the full page</small>
|
|
224
243
|
</div>
|
|
225
244
|
</div>
|
|
226
|
-
|
|
245
|
+
`
|
|
227
246
|
|
|
228
247
|
templates.indicator = `
|
|
229
248
|
<li data-target="#steps" data-slide-to="{{step}}" {{isActive}}></li>
|
|
230
|
-
|
|
249
|
+
`
|
|
231
250
|
|
|
232
251
|
templates.index = `
|
|
233
252
|
<!DOCTYPE html>
|
|
@@ -256,7 +275,7 @@ templates.index = `
|
|
|
256
275
|
|
|
257
276
|
</body>
|
|
258
277
|
</html>
|
|
259
|
-
|
|
278
|
+
`
|
|
260
279
|
|
|
261
280
|
templates.global = `
|
|
262
281
|
<!DOCTYPE html>
|
|
@@ -366,4 +385,4 @@ templates.global = `
|
|
|
366
385
|
</body>
|
|
367
386
|
|
|
368
387
|
</html>
|
|
369
|
-
|
|
388
|
+
`
|
|
@@ -1,15 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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.
|
|
@@ -64,28 +61,32 @@ const defaultConfig = {
|
|
|
64
61
|
* ```
|
|
65
62
|
*
|
|
66
63
|
*/
|
|
67
|
-
|
|
68
|
-
config = Object.assign(defaultConfig, config)
|
|
64
|
+
module.exports = (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,22 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
|
19
|
-
|
|
17
|
+
let steps = {}
|
|
18
|
+
let testStartedAt
|
|
20
19
|
/**
|
|
21
20
|
* Automatically captures steps as subtitle, and saves it as an artifact when a video is found for a failed test
|
|
22
21
|
*
|
|
@@ -29,42 +28,42 @@ let testStartedAt;
|
|
|
29
28
|
* }
|
|
30
29
|
* ```
|
|
31
30
|
*/
|
|
32
|
-
|
|
31
|
+
module.exports = function () {
|
|
33
32
|
event.dispatcher.on(event.test.before, (_) => {
|
|
34
|
-
testStartedAt = Date.now()
|
|
35
|
-
steps = {}
|
|
36
|
-
})
|
|
33
|
+
testStartedAt = Date.now()
|
|
34
|
+
steps = {}
|
|
35
|
+
})
|
|
37
36
|
|
|
38
37
|
event.dispatcher.on(event.step.started, (step) => {
|
|
39
|
-
const stepStartedAt = Date.now()
|
|
40
|
-
step.id = uuidv4()
|
|
38
|
+
const stepStartedAt = Date.now()
|
|
39
|
+
step.id = uuidv4()
|
|
41
40
|
|
|
42
|
-
let title = `${step.actor}.${step.name}(${step.args ? step.args.join(',') : ''})
|
|
41
|
+
let title = `${step.actor}.${step.name}(${step.args ? step.args.join(',') : ''})`
|
|
43
42
|
if (title.length > 100) {
|
|
44
|
-
title = `${title.substring(0, 100)}
|
|
43
|
+
title = `${title.substring(0, 100)}...`
|
|
45
44
|
}
|
|
46
45
|
|
|
47
46
|
steps[step.id] = {
|
|
48
47
|
start: formatTimestamp(stepStartedAt - testStartedAt),
|
|
49
48
|
startedAt: stepStartedAt,
|
|
50
49
|
title,
|
|
51
|
-
}
|
|
52
|
-
})
|
|
50
|
+
}
|
|
51
|
+
})
|
|
53
52
|
|
|
54
53
|
event.dispatcher.on(event.step.finished, (step) => {
|
|
55
54
|
if (step && step.id && steps[step.id]) {
|
|
56
|
-
steps[step.id].end = formatTimestamp(Date.now() - testStartedAt)
|
|
55
|
+
steps[step.id].end = formatTimestamp(Date.now() - testStartedAt)
|
|
57
56
|
}
|
|
58
|
-
})
|
|
57
|
+
})
|
|
59
58
|
|
|
60
59
|
event.dispatcher.on(event.test.after, async (test) => {
|
|
61
60
|
if (test && test.artifacts && test.artifacts.video) {
|
|
62
|
-
const stepsSortedByStartTime = Object.values(steps)
|
|
61
|
+
const stepsSortedByStartTime = Object.values(steps)
|
|
63
62
|
stepsSortedByStartTime.sort((stepA, stepB) => {
|
|
64
|
-
return stepA.startedAt - stepB.startedAt
|
|
65
|
-
})
|
|
63
|
+
return stepA.startedAt - stepB.startedAt
|
|
64
|
+
})
|
|
66
65
|
|
|
67
|
-
let subtitle = ''
|
|
66
|
+
let subtitle = ''
|
|
68
67
|
|
|
69
68
|
// For an SRT file, every subtitle has to be in the format as mentioned below:
|
|
70
69
|
//
|
|
@@ -77,13 +76,13 @@ export default function () {
|
|
|
77
76
|
${step.start} --> ${step.end}
|
|
78
77
|
${step.title}
|
|
79
78
|
|
|
80
|
-
|
|
79
|
+
`
|
|
81
80
|
}
|
|
82
|
-
})
|
|
81
|
+
})
|
|
83
82
|
|
|
84
|
-
const { dir: artifactsDirectory, name: fileName } = path.parse(test.artifacts.video)
|
|
85
|
-
await fsPromise.writeFile(`${artifactsDirectory}/${fileName}.srt`, subtitle)
|
|
86
|
-
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`
|
|
87
86
|
}
|
|
88
|
-
})
|
|
87
|
+
})
|
|
89
88
|
}
|
package/lib/plugin/tryTo.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
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
|
*
|
|
@@ -70,36 +70,46 @@ const defaultConfig = {
|
|
|
70
70
|
* ```
|
|
71
71
|
*
|
|
72
72
|
*/
|
|
73
|
-
|
|
74
|
-
config = Object.assign(defaultConfig, config)
|
|
73
|
+
module.exports = function (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
|
|
79
|
+
return tryTo
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
|
|
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
|
-
|
|
82
|
+
function tryTo(callback) {
|
|
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
|
}
|