codeceptjs 4.0.0-beta.4 → 4.0.0-beta.6.esm-aria
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 +89 -119
- package/bin/codecept.js +53 -54
- package/docs/webapi/clearCookie.mustache +1 -1
- package/lib/actor.js +70 -102
- package/lib/ai.js +131 -121
- package/lib/assert/empty.js +11 -12
- package/lib/assert/equal.js +16 -21
- package/lib/assert/error.js +2 -2
- package/lib/assert/include.js +11 -15
- package/lib/assert/throws.js +3 -5
- package/lib/assert/truth.js +10 -7
- package/lib/assert.js +18 -18
- package/lib/codecept.js +112 -101
- package/lib/colorUtils.js +48 -50
- package/lib/command/check.js +206 -0
- package/lib/command/configMigrate.js +13 -14
- package/lib/command/definitions.js +24 -36
- package/lib/command/dryRun.js +16 -16
- package/lib/command/generate.js +38 -39
- package/lib/command/gherkin/init.js +36 -38
- package/lib/command/gherkin/snippets.js +76 -74
- package/lib/command/gherkin/steps.js +21 -18
- package/lib/command/info.js +49 -15
- package/lib/command/init.js +41 -37
- package/lib/command/interactive.js +22 -13
- package/lib/command/list.js +11 -10
- package/lib/command/run-multiple/chunk.js +50 -47
- package/lib/command/run-multiple/collection.js +5 -5
- package/lib/command/run-multiple/run.js +3 -3
- package/lib/command/run-multiple.js +27 -47
- package/lib/command/run-rerun.js +6 -7
- package/lib/command/run-workers.js +15 -66
- package/lib/command/run.js +8 -8
- package/lib/command/utils.js +22 -21
- package/lib/command/workers/runTests.js +131 -241
- package/lib/config.js +111 -49
- package/lib/container.js +589 -244
- package/lib/data/context.js +16 -18
- package/lib/data/dataScenarioConfig.js +9 -9
- package/lib/data/dataTableArgument.js +7 -7
- package/lib/data/table.js +6 -12
- package/lib/effects.js +307 -0
- package/lib/els.js +160 -0
- package/lib/event.js +24 -19
- package/lib/globals.js +141 -0
- package/lib/heal.js +89 -81
- package/lib/helper/AI.js +3 -2
- package/lib/helper/ApiDataFactory.js +19 -19
- package/lib/helper/Appium.js +47 -51
- package/lib/helper/FileSystem.js +35 -15
- package/lib/helper/GraphQL.js +1 -1
- package/lib/helper/GraphQLDataFactory.js +4 -4
- package/lib/helper/JSONResponse.js +72 -45
- package/lib/helper/Mochawesome.js +14 -11
- package/lib/helper/Playwright.js +832 -434
- package/lib/helper/Puppeteer.js +393 -292
- package/lib/helper/REST.js +32 -27
- package/lib/helper/WebDriver.js +320 -219
- package/lib/helper/errors/ConnectionRefused.js +6 -6
- package/lib/helper/errors/ElementAssertion.js +11 -16
- package/lib/helper/errors/ElementNotFound.js +5 -9
- package/lib/helper/errors/RemoteBrowserConnectionRefused.js +5 -5
- package/lib/helper/extras/Console.js +11 -11
- package/lib/helper/extras/PlaywrightLocator.js +110 -0
- package/lib/helper/extras/PlaywrightPropEngine.js +18 -18
- package/lib/helper/extras/PlaywrightRestartOpts.js +23 -23
- package/lib/helper/extras/Popup.js +22 -22
- package/lib/helper/extras/React.js +29 -30
- package/lib/helper/network/actions.js +33 -48
- package/lib/helper/network/utils.js +76 -83
- package/lib/helper/scripts/blurElement.js +6 -6
- package/lib/helper/scripts/focusElement.js +6 -6
- package/lib/helper/scripts/highlightElement.js +9 -9
- package/lib/helper/scripts/isElementClickable.js +34 -34
- package/lib/helper.js +2 -1
- package/lib/history.js +23 -20
- package/lib/hooks.js +10 -10
- package/lib/html.js +90 -100
- package/lib/index.js +48 -21
- package/lib/listener/config.js +8 -9
- package/lib/listener/emptyRun.js +54 -0
- package/lib/listener/exit.js +10 -12
- package/lib/listener/{retry.js → globalRetry.js} +10 -10
- package/lib/listener/globalTimeout.js +166 -0
- package/lib/listener/helpers.js +43 -24
- package/lib/listener/mocha.js +4 -5
- package/lib/listener/result.js +11 -0
- package/lib/listener/steps.js +26 -23
- package/lib/listener/store.js +20 -0
- package/lib/locator.js +213 -192
- package/lib/mocha/asyncWrapper.js +264 -0
- package/lib/mocha/bdd.js +167 -0
- package/lib/mocha/cli.js +341 -0
- package/lib/mocha/factory.js +160 -0
- package/lib/{interfaces → mocha}/featureConfig.js +33 -13
- package/lib/{interfaces → mocha}/gherkin.js +75 -45
- package/lib/mocha/hooks.js +121 -0
- package/lib/mocha/index.js +21 -0
- package/lib/mocha/inject.js +46 -0
- package/lib/{interfaces → mocha}/scenarioConfig.js +32 -8
- package/lib/mocha/suite.js +89 -0
- package/lib/mocha/test.js +178 -0
- package/lib/mocha/types.d.ts +42 -0
- package/lib/mocha/ui.js +229 -0
- package/lib/output.js +86 -64
- package/lib/parser.js +44 -44
- package/lib/pause.js +160 -139
- package/lib/plugin/analyze.js +403 -0
- package/lib/plugin/{autoLogin.js → auth.js} +137 -43
- package/lib/plugin/autoDelay.js +19 -15
- package/lib/plugin/coverage.js +22 -27
- package/lib/plugin/customLocator.js +5 -5
- package/lib/plugin/customReporter.js +53 -0
- package/lib/plugin/heal.js +49 -17
- package/lib/plugin/pageInfo.js +140 -0
- package/lib/plugin/pauseOnFail.js +4 -3
- package/lib/plugin/retryFailedStep.js +60 -19
- package/lib/plugin/screenshotOnFail.js +80 -83
- package/lib/plugin/stepByStepReport.js +70 -31
- package/lib/plugin/stepTimeout.js +7 -13
- package/lib/plugin/subtitles.js +10 -9
- package/lib/recorder.js +167 -126
- package/lib/rerun.js +94 -50
- package/lib/result.js +161 -0
- package/lib/secret.js +18 -17
- package/lib/session.js +95 -89
- package/lib/step/base.js +239 -0
- package/lib/step/comment.js +10 -0
- package/lib/step/config.js +50 -0
- package/lib/step/func.js +46 -0
- package/lib/step/helper.js +50 -0
- package/lib/step/meta.js +99 -0
- package/lib/step/record.js +74 -0
- package/lib/step/retry.js +11 -0
- package/lib/step/section.js +55 -0
- package/lib/step.js +18 -332
- package/lib/steps.js +54 -0
- package/lib/store.js +37 -5
- package/lib/template/heal.js +2 -11
- package/lib/timeout.js +60 -0
- package/lib/transform.js +8 -8
- package/lib/translation.js +32 -18
- package/lib/utils.js +354 -250
- package/lib/workerStorage.js +16 -16
- package/lib/workers.js +366 -282
- package/package.json +107 -95
- package/translations/de-DE.js +5 -4
- package/translations/fr-FR.js +5 -4
- package/translations/index.js +23 -9
- package/translations/it-IT.js +5 -4
- package/translations/ja-JP.js +5 -4
- package/translations/nl-NL.js +76 -0
- package/translations/pl-PL.js +5 -4
- package/translations/pt-BR.js +5 -4
- package/translations/ru-RU.js +5 -4
- package/translations/utils.js +18 -0
- package/translations/zh-CN.js +5 -4
- package/translations/zh-TW.js +5 -4
- package/typings/index.d.ts +177 -186
- package/typings/promiseBasedTypes.d.ts +3573 -5941
- package/typings/types.d.ts +4042 -6370
- package/lib/cli.js +0 -256
- package/lib/helper/ExpectHelper.js +0 -391
- package/lib/helper/Nightmare.js +0 -1504
- package/lib/helper/Protractor.js +0 -1863
- package/lib/helper/SoftExpectHelper.js +0 -381
- package/lib/helper/TestCafe.js +0 -1414
- package/lib/helper/clientscripts/nightmare.js +0 -213
- package/lib/helper/extras/PlaywrightReactVueLocator.js +0 -43
- package/lib/helper/testcafe/testControllerHolder.js +0 -42
- package/lib/helper/testcafe/testcafe-utils.js +0 -62
- package/lib/interfaces/bdd.js +0 -81
- package/lib/listener/artifacts.js +0 -19
- package/lib/listener/timeout.js +0 -109
- package/lib/mochaFactory.js +0 -113
- package/lib/plugin/allure.js +0 -15
- package/lib/plugin/commentStep.js +0 -136
- package/lib/plugin/debugErrors.js +0 -67
- package/lib/plugin/eachElement.js +0 -127
- package/lib/plugin/fakerTransform.js +0 -49
- package/lib/plugin/retryTo.js +0 -127
- package/lib/plugin/selenoid.js +0 -384
- package/lib/plugin/standardActingHelpers.js +0 -3
- package/lib/plugin/tryTo.js +0 -115
- package/lib/plugin/wdio.js +0 -249
- package/lib/scenario.js +0 -224
- package/lib/ui.js +0 -236
- package/lib/within.js +0 -70
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
import Container from '../container.js'
|
|
5
|
+
|
|
6
|
+
import recorder from '../recorder.js'
|
|
7
|
+
|
|
8
|
+
import event from '../event.js'
|
|
9
|
+
|
|
10
|
+
import output from '../output.js'
|
|
11
|
+
|
|
12
|
+
import { fileExists } from '../utils.js'
|
|
13
|
+
import Codeceptjs from '../index.js'
|
|
14
|
+
import { testToFileName } from '../mocha/test.js'
|
|
10
15
|
|
|
11
16
|
const defaultConfig = {
|
|
12
17
|
uniqueScreenshotNames: false,
|
|
@@ -14,7 +19,7 @@ const defaultConfig = {
|
|
|
14
19
|
fullPageScreenshots: false,
|
|
15
20
|
}
|
|
16
21
|
|
|
17
|
-
const supportedHelpers =
|
|
22
|
+
const supportedHelpers = Container.STANDARD_ACTING_HELPERS
|
|
18
23
|
|
|
19
24
|
/**
|
|
20
25
|
* Creates screenshot on failure. Screenshot is saved into `output` directory.
|
|
@@ -42,7 +47,7 @@ const supportedHelpers = require('./standardActingHelpers')
|
|
|
42
47
|
*
|
|
43
48
|
*
|
|
44
49
|
*/
|
|
45
|
-
|
|
50
|
+
export default function (config) {
|
|
46
51
|
const helpers = Container.helpers()
|
|
47
52
|
let helper
|
|
48
53
|
|
|
@@ -63,9 +68,7 @@ module.exports = function (config) {
|
|
|
63
68
|
}
|
|
64
69
|
|
|
65
70
|
if (Codeceptjs.container.mocha()) {
|
|
66
|
-
options.reportDir =
|
|
67
|
-
Codeceptjs.container.mocha().options.reporterOptions &&
|
|
68
|
-
Codeceptjs.container.mocha().options.reporterOptions.reportDir
|
|
71
|
+
options.reportDir = Codeceptjs.container.mocha()?.options?.reporterOptions && Codeceptjs.container.mocha()?.options?.reporterOptions?.reportDir
|
|
69
72
|
}
|
|
70
73
|
|
|
71
74
|
if (options.disableScreenshots) {
|
|
@@ -73,32 +76,42 @@ module.exports = function (config) {
|
|
|
73
76
|
return
|
|
74
77
|
}
|
|
75
78
|
|
|
76
|
-
event.dispatcher.on(event.test.failed, (test) => {
|
|
77
|
-
if (
|
|
78
|
-
|
|
79
|
-
'screenshotOnFail',
|
|
80
|
-
'BeforeSuite/AfterSuite do not have any access to the browser, hence it could not take screenshot.',
|
|
81
|
-
)
|
|
79
|
+
event.dispatcher.on(event.test.failed, (test, _err, hookName) => {
|
|
80
|
+
if (hookName === 'BeforeSuite' || hookName === 'AfterSuite') {
|
|
81
|
+
// no browser here
|
|
82
82
|
return
|
|
83
83
|
}
|
|
84
|
+
|
|
84
85
|
recorder.add(
|
|
85
86
|
'screenshot of failed test',
|
|
86
87
|
async () => {
|
|
87
|
-
let fileName = clearString(test.title)
|
|
88
88
|
const dataType = 'image/png'
|
|
89
89
|
// This prevents data driven to be included in the failed screenshot file name
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
if (test.ctx && test.ctx.test && test.ctx.test.type === 'hook')
|
|
94
|
-
fileName = clearString(`${test.title}_${test.ctx.test.title}`)
|
|
90
|
+
let fileName
|
|
91
|
+
|
|
95
92
|
if (options.uniqueScreenshotNames && test) {
|
|
96
|
-
|
|
97
|
-
fileName = `${fileName.substring(0, 10)}_${uuid}.failed.png`
|
|
93
|
+
fileName = `${testToFileName(test, { suffix: '', unique: true })}.failed.png`
|
|
98
94
|
} else {
|
|
99
|
-
fileName
|
|
95
|
+
fileName = `${testToFileName(test, { suffix: '', unique: false })}.failed.png`
|
|
96
|
+
}
|
|
97
|
+
const quietMode = !('output_dir' in global) || !global.output_dir
|
|
98
|
+
if (!quietMode) {
|
|
99
|
+
output.plugin('screenshotOnFail', 'Test failed, try to save a screenshot')
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Re-check helpers at runtime in case they weren't ready during plugin init
|
|
103
|
+
const runtimeHelpers = Container.helpers()
|
|
104
|
+
let runtimeHelper = null
|
|
105
|
+
for (const helperName of supportedHelpers) {
|
|
106
|
+
if (Object.keys(runtimeHelpers).indexOf(helperName) > -1) {
|
|
107
|
+
runtimeHelper = runtimeHelpers[helperName]
|
|
108
|
+
break
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (runtimeHelper && typeof runtimeHelper.saveScreenshot === 'function') {
|
|
113
|
+
helper = runtimeHelper
|
|
100
114
|
}
|
|
101
|
-
output.plugin('screenshotOnFail', 'Test failed, try to save a screenshot')
|
|
102
115
|
|
|
103
116
|
try {
|
|
104
117
|
if (options.reportDir) {
|
|
@@ -108,57 +121,53 @@ module.exports = function (config) {
|
|
|
108
121
|
fs.mkdirSync(mochaReportDir)
|
|
109
122
|
}
|
|
110
123
|
}
|
|
111
|
-
await helper.saveScreenshot(fileName, options.fullPageScreenshots)
|
|
112
124
|
|
|
113
|
-
if
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
Container.mocha().options.reporterOptions['mocha-junit-reporter'] &&
|
|
117
|
-
Container.mocha().options.reporterOptions['mocha-junit-reporter'].options.attachments
|
|
118
|
-
) {
|
|
119
|
-
test.attachments = [path.join(global.output_dir, fileName)]
|
|
125
|
+
// Check if browser/page is still available before attempting screenshot
|
|
126
|
+
if (helper.page && helper.page.isClosed && helper.page.isClosed()) {
|
|
127
|
+
throw new Error('Browser page has been closed')
|
|
120
128
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if (allureReporter) {
|
|
124
|
-
allureReporter.addAttachment(
|
|
125
|
-
'Main session - Last Seen Screenshot',
|
|
126
|
-
fs.readFileSync(path.join(global.output_dir, fileName)),
|
|
127
|
-
dataType,
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
if (helper.activeSessionName) {
|
|
131
|
-
const sessions = helper.sessionPages || helper.sessionWindows
|
|
132
|
-
for (const sessionName in sessions) {
|
|
133
|
-
const screenshotFileName = `${sessionName}_${fileName}`
|
|
134
|
-
test.artifacts[`${sessionName.replace(/ /g, '_')}_screenshot`] = path.join(
|
|
135
|
-
global.output_dir,
|
|
136
|
-
screenshotFileName,
|
|
137
|
-
)
|
|
138
|
-
allureReporter.addAttachment(
|
|
139
|
-
`${sessionName} - Last Seen Screenshot`,
|
|
140
|
-
fs.readFileSync(path.join(global.output_dir, screenshotFileName)),
|
|
141
|
-
dataType,
|
|
142
|
-
)
|
|
143
|
-
}
|
|
144
|
-
}
|
|
129
|
+
if (helper.browser && helper.browser.isConnected && !helper.browser.isConnected()) {
|
|
130
|
+
throw new Error('Browser has been disconnected')
|
|
145
131
|
}
|
|
146
132
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
133
|
+
// Add timeout wrapper to prevent hanging with shorter timeout for ESM
|
|
134
|
+
const screenshotPromise = helper.saveScreenshot(fileName, options.fullPageScreenshots)
|
|
135
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
136
|
+
setTimeout(() => reject(new Error('Screenshot timeout after 5 seconds')), 5000)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
await Promise.race([screenshotPromise, timeoutPromise])
|
|
140
|
+
|
|
141
|
+
if (!test.artifacts) test.artifacts = {}
|
|
142
|
+
// Some unit tests may not define global.output_dir; avoid throwing when it is undefined
|
|
143
|
+
// Detect output directory safely (may not be initialized in narrow unit tests)
|
|
144
|
+
const baseOutputDir = 'output_dir' in global && typeof global.output_dir === 'string' && global.output_dir ? global.output_dir : null
|
|
145
|
+
if (baseOutputDir) {
|
|
146
|
+
test.artifacts.screenshot = path.join(baseOutputDir, fileName)
|
|
147
|
+
if (Container.mocha().options.reporterOptions['mocha-junit-reporter'] && Container.mocha().options.reporterOptions['mocha-junit-reporter'].options.attachments) {
|
|
148
|
+
test.attachments = [path.join(baseOutputDir, fileName)]
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
// Fallback: just store the file name to keep tests stable without triggering path errors
|
|
152
|
+
test.artifacts.screenshot = fileName
|
|
150
153
|
}
|
|
151
154
|
} catch (err) {
|
|
152
|
-
|
|
155
|
+
if (!quietMode) {
|
|
156
|
+
output.plugin('screenshotOnFail', `Failed to save screenshot: ${err.message}`)
|
|
157
|
+
}
|
|
158
|
+
// Enhanced error handling for browser closed scenarios
|
|
153
159
|
if (
|
|
154
160
|
err &&
|
|
155
|
-
err.
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
161
|
+
((err.message &&
|
|
162
|
+
(err.message.includes('Target page, context or browser has been closed') ||
|
|
163
|
+
err.message.includes('Browser page has been closed') ||
|
|
164
|
+
err.message.includes('Browser has been disconnected') ||
|
|
165
|
+
err.message.includes('was terminated due to') ||
|
|
166
|
+
err.message.includes('no such window: target window already closed') ||
|
|
167
|
+
err.message.includes('Screenshot timeout after'))) ||
|
|
168
|
+
(err.type && err.type === 'RuntimeError'))
|
|
160
169
|
) {
|
|
161
|
-
output.log(`Can't make screenshot, ${err}`)
|
|
170
|
+
output.log(`Can't make screenshot, ${err.message}`)
|
|
162
171
|
helper.isRunning = false
|
|
163
172
|
}
|
|
164
173
|
}
|
|
@@ -166,16 +175,4 @@ module.exports = function (config) {
|
|
|
166
175
|
true,
|
|
167
176
|
)
|
|
168
177
|
})
|
|
169
|
-
|
|
170
|
-
function _getUUID(test) {
|
|
171
|
-
if (test.uuid) {
|
|
172
|
-
return test.uuid
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
if (test.ctx && test.ctx.test.uuid) {
|
|
176
|
-
return test.ctx.test.uuid
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return Math.floor(new Date().getTime() / 1000)
|
|
180
|
-
}
|
|
181
178
|
}
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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 cheerio from 'cheerio'
|
|
8
|
+
|
|
9
|
+
import Container from '../container.js'
|
|
10
|
+
import recorder from '../recorder.js'
|
|
11
|
+
import event from '../event.js'
|
|
12
|
+
import output from '../output.js'
|
|
13
|
+
import { template, deleteDir } from '../utils.js'
|
|
14
|
+
|
|
15
|
+
const supportedHelpers = Container.STANDARD_ACTING_HELPERS
|
|
15
16
|
|
|
16
17
|
const defaultConfig = {
|
|
17
18
|
deleteSuccessful: true,
|
|
@@ -62,7 +63,7 @@ const templates = {}
|
|
|
62
63
|
* @param {*} config
|
|
63
64
|
*/
|
|
64
65
|
|
|
65
|
-
|
|
66
|
+
export default function (config) {
|
|
66
67
|
const helpers = Container.helpers()
|
|
67
68
|
let helper
|
|
68
69
|
|
|
@@ -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,34 +107,74 @@ 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)
|
|
121
122
|
})
|
|
122
123
|
|
|
123
|
-
event.dispatcher.on(event.test.failed, (test,
|
|
124
|
-
if (
|
|
125
|
-
|
|
126
|
-
'stepByStepReport',
|
|
127
|
-
'BeforeSuite/AfterSuite do not have any access to the browser, hence it could not take screenshot.',
|
|
128
|
-
)
|
|
124
|
+
event.dispatcher.on(event.test.failed, (test, _err, hookName) => {
|
|
125
|
+
if (hookName === 'BeforeSuite' || hookName === 'AfterSuite') {
|
|
126
|
+
// no browser here
|
|
129
127
|
return
|
|
130
128
|
}
|
|
131
|
-
|
|
129
|
+
|
|
130
|
+
persist(test)
|
|
132
131
|
})
|
|
133
132
|
|
|
134
133
|
event.dispatcher.on(event.all.result, () => {
|
|
135
134
|
if (Object.keys(recordedTests).length === 0 || !Object.keys(slides).length) return
|
|
135
|
+
generateRecordsHtml(recordedTests)
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
event.dispatcher.on(event.workers.result, async () => {
|
|
139
|
+
await recorder.add(() => {
|
|
140
|
+
const recordedTests = getRecordFoldersWithDetails(reportDir)
|
|
141
|
+
generateRecordsHtml(recordedTests)
|
|
142
|
+
})
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
function getRecordFoldersWithDetails(dirPath) {
|
|
146
|
+
let results = {}
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
const items = fs.readdirSync(dirPath, { withFileTypes: true })
|
|
150
|
+
|
|
151
|
+
items.forEach(item => {
|
|
152
|
+
if (item.isDirectory() && item.name.startsWith('record_')) {
|
|
153
|
+
const recordFolderPath = path.join(dirPath, item.name)
|
|
154
|
+
const indexPath = path.join(recordFolderPath, 'index.html')
|
|
155
|
+
|
|
156
|
+
let name = ''
|
|
157
|
+
if (fs.existsSync(indexPath)) {
|
|
158
|
+
try {
|
|
159
|
+
const htmlContent = fs.readFileSync(indexPath, 'utf-8')
|
|
160
|
+
const $ = cheerio.load(htmlContent)
|
|
161
|
+
name = $('.navbar-brand').text().trim()
|
|
162
|
+
} catch (err) {
|
|
163
|
+
console.error(`Error reading index.html in ${recordFolderPath}:`, err.message)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
results[name || 'Unkown'] = `${item.name}/index.html`
|
|
168
|
+
}
|
|
169
|
+
})
|
|
170
|
+
} catch (err) {
|
|
171
|
+
console.error(`Error reading directory ${dirPath}:`, err.message)
|
|
172
|
+
}
|
|
136
173
|
|
|
174
|
+
return results
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function generateRecordsHtml(recordedTests) {
|
|
137
178
|
let links = ''
|
|
138
179
|
|
|
139
180
|
for (const link in recordedTests) {
|
|
@@ -147,10 +188,8 @@ module.exports = function (config) {
|
|
|
147
188
|
|
|
148
189
|
fs.writeFileSync(path.join(reportDir, 'records.html'), indexHTML)
|
|
149
190
|
|
|
150
|
-
output.print(
|
|
151
|
-
|
|
152
|
-
)
|
|
153
|
-
})
|
|
191
|
+
output.print(`${figures.circleFilled} Step-by-step preview: ${colors.white.bold(`file://${reportDir}/records.html`)}`)
|
|
192
|
+
}
|
|
154
193
|
|
|
155
194
|
async function persistStep(step) {
|
|
156
195
|
if (stepNum === -1) return // Ignore steps from BeforeSuite function
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import event from '../event.js'
|
|
2
|
+
|
|
3
|
+
import { TIMEOUT_ORDER } from '../timeout.js'
|
|
3
4
|
|
|
4
5
|
const defaultConfig = {
|
|
5
6
|
timeout: 150,
|
|
@@ -61,12 +62,12 @@ const defaultConfig = {
|
|
|
61
62
|
* ```
|
|
62
63
|
*
|
|
63
64
|
*/
|
|
64
|
-
|
|
65
|
+
export default function(config) {
|
|
65
66
|
config = Object.assign(defaultConfig, config)
|
|
66
67
|
// below override rule makes sure customTimeoutSteps go first but then they override noTimeoutSteps in case of exact pattern match
|
|
67
68
|
config.customTimeoutSteps = config.customTimeoutSteps.concat(config.noTimeoutSteps).concat(config.customTimeoutSteps)
|
|
68
69
|
|
|
69
|
-
event.dispatcher.on(event.step.before,
|
|
70
|
+
event.dispatcher.on(event.step.before, step => {
|
|
70
71
|
let stepTimeout
|
|
71
72
|
for (let stepRule of config.customTimeoutSteps) {
|
|
72
73
|
let customTimeout = 0
|
|
@@ -74,19 +75,12 @@ module.exports = (config) => {
|
|
|
74
75
|
if (stepRule.length > 1) customTimeout = stepRule[1]
|
|
75
76
|
stepRule = stepRule[0]
|
|
76
77
|
}
|
|
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
|
-
) {
|
|
78
|
+
if (stepRule instanceof RegExp ? step.name.match(stepRule) : step.name === stepRule || (stepRule.indexOf('*') && step.name.startsWith(stepRule.slice(0, -1)))) {
|
|
82
79
|
stepTimeout = customTimeout
|
|
83
80
|
break
|
|
84
81
|
}
|
|
85
82
|
}
|
|
86
83
|
stepTimeout = stepTimeout === undefined ? config.timeout : stepTimeout
|
|
87
|
-
step.setTimeout(
|
|
88
|
-
stepTimeout * 1000,
|
|
89
|
-
config.overrideStepLimits ? TIMEOUT_ORDER.stepTimeoutHard : TIMEOUT_ORDER.stepTimeoutSoft,
|
|
90
|
-
)
|
|
84
|
+
step.setTimeout(stepTimeout * 1000, config.overrideStepLimits ? TIMEOUT_ORDER.stepTimeoutHard : TIMEOUT_ORDER.stepTimeoutSoft)
|
|
91
85
|
})
|
|
92
86
|
}
|
package/lib/plugin/subtitles.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
|
|
1
|
+
import { v4 as uuidv4 } from 'uuid'
|
|
2
|
+
import fs from 'fs'
|
|
3
|
+
const fsPromise = fs.promises
|
|
4
|
+
import path from 'path'
|
|
5
|
+
import event from '../event.js'
|
|
5
6
|
|
|
6
7
|
// This will convert a given timestamp in milliseconds to
|
|
7
8
|
// an SRT recognized timestamp, ie HH:mm:ss,SSS
|
|
@@ -28,13 +29,13 @@ let testStartedAt
|
|
|
28
29
|
* }
|
|
29
30
|
* ```
|
|
30
31
|
*/
|
|
31
|
-
|
|
32
|
-
event.dispatcher.on(event.test.before,
|
|
32
|
+
export default function () {
|
|
33
|
+
event.dispatcher.on(event.test.before, _ => {
|
|
33
34
|
testStartedAt = Date.now()
|
|
34
35
|
steps = {}
|
|
35
36
|
})
|
|
36
37
|
|
|
37
|
-
event.dispatcher.on(event.step.started,
|
|
38
|
+
event.dispatcher.on(event.step.started, step => {
|
|
38
39
|
const stepStartedAt = Date.now()
|
|
39
40
|
step.id = uuidv4()
|
|
40
41
|
|
|
@@ -50,13 +51,13 @@ module.exports = function () {
|
|
|
50
51
|
}
|
|
51
52
|
})
|
|
52
53
|
|
|
53
|
-
event.dispatcher.on(event.step.finished,
|
|
54
|
+
event.dispatcher.on(event.step.finished, step => {
|
|
54
55
|
if (step && step.id && steps[step.id]) {
|
|
55
56
|
steps[step.id].end = formatTimestamp(Date.now() - testStartedAt)
|
|
56
57
|
}
|
|
57
58
|
})
|
|
58
59
|
|
|
59
|
-
event.dispatcher.on(event.test.after, async
|
|
60
|
+
event.dispatcher.on(event.test.after, async test => {
|
|
60
61
|
if (test && test.artifacts && test.artifacts.video) {
|
|
61
62
|
const stepsSortedByStartTime = Object.values(steps)
|
|
62
63
|
stepsSortedByStartTime.sort((stepA, stepB) => {
|