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