codeceptjs 4.0.0-beta.5 → 4.0.0-beta.7.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 +0 -45
- package/bin/codecept.js +46 -57
- package/lib/actor.js +15 -11
- package/lib/ai.js +6 -5
- package/lib/assert/empty.js +9 -8
- package/lib/assert/equal.js +15 -17
- package/lib/assert/error.js +2 -2
- package/lib/assert/include.js +9 -11
- package/lib/assert/throws.js +1 -1
- package/lib/assert/truth.js +8 -5
- package/lib/assert.js +18 -18
- package/lib/codecept.js +66 -107
- package/lib/colorUtils.js +48 -50
- package/lib/command/check.js +32 -27
- package/lib/command/configMigrate.js +11 -10
- package/lib/command/definitions.js +16 -10
- package/lib/command/dryRun.js +16 -16
- package/lib/command/generate.js +29 -26
- package/lib/command/gherkin/init.js +36 -38
- package/lib/command/gherkin/snippets.js +14 -14
- package/lib/command/gherkin/steps.js +21 -18
- package/lib/command/info.js +8 -8
- package/lib/command/init.js +34 -31
- package/lib/command/interactive.js +11 -10
- package/lib/command/list.js +10 -9
- package/lib/command/run-multiple/chunk.js +5 -5
- package/lib/command/run-multiple/collection.js +5 -5
- package/lib/command/run-multiple/run.js +3 -3
- package/lib/command/run-multiple.js +16 -13
- package/lib/command/run-rerun.js +6 -7
- package/lib/command/run-workers.js +10 -24
- package/lib/command/run.js +8 -8
- package/lib/command/utils.js +20 -18
- package/lib/command/workers/runTests.js +117 -269
- package/lib/config.js +111 -49
- package/lib/container.js +299 -102
- package/lib/data/context.js +6 -5
- package/lib/data/dataScenarioConfig.js +1 -1
- package/lib/data/dataTableArgument.js +1 -1
- package/lib/data/table.js +1 -1
- package/lib/effects.js +94 -10
- package/lib/els.js +11 -9
- package/lib/event.js +11 -10
- package/lib/globals.js +141 -0
- package/lib/heal.js +12 -12
- package/lib/helper/AI.js +1 -1
- package/lib/helper/ApiDataFactory.js +16 -13
- package/lib/helper/FileSystem.js +32 -12
- package/lib/helper/GraphQL.js +1 -1
- package/lib/helper/GraphQLDataFactory.js +1 -1
- package/lib/helper/JSONResponse.js +19 -30
- package/lib/helper/Mochawesome.js +9 -28
- package/lib/helper/Playwright.js +668 -265
- package/lib/helper/Puppeteer.js +284 -169
- package/lib/helper/REST.js +29 -12
- package/lib/helper/WebDriver.js +192 -71
- 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 +1 -1
- 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 +6 -7
- package/lib/listener/exit.js +4 -3
- package/lib/listener/globalRetry.js +5 -5
- package/lib/listener/globalTimeout.js +11 -10
- package/lib/listener/helpers.js +33 -14
- package/lib/listener/mocha.js +3 -4
- package/lib/listener/result.js +4 -5
- package/lib/listener/steps.js +7 -18
- package/lib/listener/store.js +3 -3
- package/lib/locator.js +213 -192
- package/lib/mocha/asyncWrapper.js +108 -75
- package/lib/mocha/bdd.js +99 -13
- package/lib/mocha/cli.js +60 -27
- package/lib/mocha/factory.js +75 -19
- package/lib/mocha/featureConfig.js +1 -1
- package/lib/mocha/gherkin.js +57 -25
- package/lib/mocha/hooks.js +12 -3
- package/lib/mocha/index.js +13 -4
- package/lib/mocha/inject.js +22 -5
- package/lib/mocha/scenarioConfig.js +2 -2
- package/lib/mocha/suite.js +9 -2
- package/lib/mocha/test.js +10 -13
- package/lib/mocha/ui.js +28 -31
- package/lib/output.js +11 -9
- package/lib/parser.js +44 -44
- package/lib/pause.js +15 -16
- package/lib/plugin/analyze.js +19 -12
- package/lib/plugin/auth.js +20 -21
- package/lib/plugin/autoDelay.js +12 -8
- package/lib/plugin/coverage.js +12 -8
- package/lib/plugin/customLocator.js +3 -3
- package/lib/plugin/customReporter.js +3 -2
- package/lib/plugin/heal.js +14 -9
- package/lib/plugin/pageInfo.js +10 -10
- package/lib/plugin/pauseOnFail.js +4 -3
- package/lib/plugin/retryFailedStep.js +47 -5
- package/lib/plugin/screenshotOnFail.js +75 -37
- package/lib/plugin/stepByStepReport.js +14 -14
- package/lib/plugin/stepTimeout.js +4 -3
- package/lib/plugin/subtitles.js +6 -5
- package/lib/recorder.js +33 -23
- package/lib/rerun.js +69 -26
- package/lib/result.js +4 -4
- package/lib/secret.js +18 -17
- package/lib/session.js +95 -89
- package/lib/step/base.js +6 -6
- package/lib/step/config.js +1 -1
- package/lib/step/func.js +3 -3
- package/lib/step/helper.js +3 -3
- package/lib/step/meta.js +4 -4
- package/lib/step/record.js +11 -11
- package/lib/step/retry.js +3 -3
- package/lib/step/section.js +3 -3
- package/lib/step.js +7 -10
- package/lib/steps.js +9 -5
- package/lib/store.js +1 -1
- package/lib/timeout.js +1 -7
- package/lib/transform.js +8 -8
- package/lib/translation.js +32 -18
- package/lib/utils.js +68 -97
- package/lib/workerStorage.js +16 -17
- package/lib/workers.js +145 -171
- package/package.json +58 -55
- package/translations/de-DE.js +2 -2
- package/translations/fr-FR.js +2 -2
- package/translations/index.js +23 -10
- package/translations/it-IT.js +2 -2
- package/translations/ja-JP.js +2 -2
- package/translations/nl-NL.js +2 -2
- package/translations/pl-PL.js +2 -2
- package/translations/pt-BR.js +2 -2
- package/translations/ru-RU.js +2 -2
- package/translations/utils.js +4 -3
- package/translations/zh-CN.js +2 -2
- package/translations/zh-TW.js +2 -2
- package/typings/index.d.ts +7 -18
- package/typings/promiseBasedTypes.d.ts +3769 -5450
- package/typings/types.d.ts +3953 -5778
- package/bin/test-server.js +0 -53
- package/lib/element/WebElement.js +0 -327
- package/lib/helper/Nightmare.js +0 -1486
- package/lib/helper/Protractor.js +0 -1840
- package/lib/helper/TestCafe.js +0 -1391
- 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 -61
- package/lib/listener/retryEnhancer.js +0 -85
- package/lib/plugin/allure.js +0 -15
- package/lib/plugin/autoLogin.js +0 -5
- package/lib/plugin/commentStep.js +0 -141
- package/lib/plugin/eachElement.js +0 -127
- package/lib/plugin/fakerTransform.js +0 -49
- package/lib/plugin/htmlReporter.js +0 -1947
- package/lib/plugin/retryTo.js +0 -16
- package/lib/plugin/selenoid.js +0 -364
- package/lib/plugin/standardActingHelpers.js +0 -6
- package/lib/plugin/tryTo.js +0 -16
- package/lib/plugin/wdio.js +0 -247
- package/lib/test-server.js +0 -323
- package/lib/within.js +0 -90
package/lib/plugin/analyze.js
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const
|
|
1
|
+
import debugFactory from 'debug'
|
|
2
|
+
const debug = debugFactory('codeceptjs:analyze')
|
|
3
|
+
import { isMainThread } from 'node:worker_threads'
|
|
4
|
+
import figures from 'figures'
|
|
5
|
+
const { arrowRight } = figures
|
|
6
|
+
import Container from '../container.js'
|
|
7
|
+
// Container already imported correctly above
|
|
8
|
+
import store from '../store.js'
|
|
9
|
+
|
|
10
|
+
import aiModule from '../ai.js'
|
|
11
|
+
const ai = aiModule.default || aiModule
|
|
12
|
+
import colors from 'chalk'
|
|
13
|
+
import ora from 'ora'
|
|
14
|
+
import event from '../event.js'
|
|
15
|
+
|
|
16
|
+
import output from '../output.js'
|
|
17
|
+
|
|
18
|
+
import { ansiRegExp, base64EncodeFile, markdownToAnsi } from '../utils.js'
|
|
12
19
|
|
|
13
20
|
const MAX_DATA_LENGTH = 5000
|
|
14
21
|
|
|
@@ -212,7 +219,7 @@ const defaultConfig = {
|
|
|
212
219
|
* @param {Object} config - Plugin configuration
|
|
213
220
|
* @returns {void}
|
|
214
221
|
*/
|
|
215
|
-
|
|
222
|
+
export default function (config = {}) {
|
|
216
223
|
config = Object.assign(defaultConfig, config)
|
|
217
224
|
|
|
218
225
|
event.dispatcher.on(event.workers.before, () => {
|
package/lib/plugin/auth.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const { isAsyncFunction } = require('../utils')
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import { fileExists, isAsyncFunction } from '../utils.js'
|
|
4
|
+
import CommentStep from '../step/comment.js'
|
|
5
|
+
import Section from '../step/section.js'
|
|
6
|
+
import container from '../container.js'
|
|
7
|
+
import store from '../store.js'
|
|
8
|
+
import event from '../event.js'
|
|
9
|
+
import recorder from '../recorder.js'
|
|
10
|
+
import output from '../output.js'
|
|
12
11
|
|
|
13
12
|
const defaultUser = {
|
|
14
13
|
fetch: I => I.grabCookie(),
|
|
@@ -253,7 +252,7 @@ const defaultConfig = {
|
|
|
253
252
|
*
|
|
254
253
|
*
|
|
255
254
|
*/
|
|
256
|
-
|
|
255
|
+
export default function (config) {
|
|
257
256
|
config = Object.assign(defaultConfig, config)
|
|
258
257
|
Object.keys(config.users).map(
|
|
259
258
|
u =>
|
|
@@ -292,12 +291,12 @@ module.exports = function (config) {
|
|
|
292
291
|
|
|
293
292
|
if (isPlaywrightSession() && test?.opts?.cookies) {
|
|
294
293
|
if (test.opts.user == name) {
|
|
295
|
-
debug(`Cookies already loaded for ${name}`)
|
|
294
|
+
output.debug(`Cookies already loaded for ${name}`)
|
|
296
295
|
|
|
297
296
|
alreadyLoggedIn(name)
|
|
298
297
|
return
|
|
299
298
|
} else {
|
|
300
|
-
debug(`Cookies already loaded for ${test.opts.user}, but not for ${name}`)
|
|
299
|
+
output.debug(`Cookies already loaded for ${test.opts.user}, but not for ${name}`)
|
|
301
300
|
await I.deleteCookie()
|
|
302
301
|
}
|
|
303
302
|
}
|
|
@@ -317,11 +316,11 @@ module.exports = function (config) {
|
|
|
317
316
|
section.end()
|
|
318
317
|
const cookies = await userSession.fetch(I)
|
|
319
318
|
if (!cookies) {
|
|
320
|
-
debug("Cannot save user session with empty cookies from auto login's fetch method")
|
|
319
|
+
output.debug("Cannot save user session with empty cookies from auto login's fetch method")
|
|
321
320
|
return
|
|
322
321
|
}
|
|
323
322
|
if (config.saveToFile) {
|
|
324
|
-
debug(`Saved user session into file for ${name}`)
|
|
323
|
+
output.debug(`Saved user session into file for ${name}`)
|
|
325
324
|
fs.writeFileSync(path.join(global.output_dir, `${name}_session.json`), JSON.stringify(cookies))
|
|
326
325
|
}
|
|
327
326
|
store[`${name}_session`] = cookies
|
|
@@ -339,13 +338,13 @@ module.exports = function (config) {
|
|
|
339
338
|
}
|
|
340
339
|
section.end()
|
|
341
340
|
recorder.session.catch(err => {
|
|
342
|
-
debug(`Failed auto login for ${name} due to ${err}`)
|
|
343
|
-
debug('Logging in again')
|
|
341
|
+
output.debug(`Failed auto login for ${name} due to ${err}`)
|
|
342
|
+
output.debug('Logging in again')
|
|
344
343
|
recorder.session.start('auto login')
|
|
345
344
|
return loginAndSave()
|
|
346
345
|
.then(() => {
|
|
347
346
|
recorder.add(() => recorder.session.restore('auto login'))
|
|
348
|
-
recorder.catch(() => debug('continue'))
|
|
347
|
+
recorder.catch(() => output.debug('continue'))
|
|
349
348
|
})
|
|
350
349
|
.catch(err => {
|
|
351
350
|
recorder.session.restore('auto login')
|
|
@@ -365,7 +364,7 @@ module.exports = function (config) {
|
|
|
365
364
|
const suite = store.currentSuite
|
|
366
365
|
if (!suite) return
|
|
367
366
|
|
|
368
|
-
debug(`enabling auth as ${name} for each test of suite ${suite.title}`)
|
|
367
|
+
output.debug(`enabling auth as ${name} for each test of suite ${suite.title}`)
|
|
369
368
|
|
|
370
369
|
// we are setting test opts so they can be picked up by Playwright if it starts browser for this test
|
|
371
370
|
suite.eachTest(test => {
|
|
@@ -420,7 +419,7 @@ function loadCookiesFromFile(config) {
|
|
|
420
419
|
} catch (err) {
|
|
421
420
|
throw new Error(`Could not load session from ${fileName}\n${err}`)
|
|
422
421
|
}
|
|
423
|
-
debug(`Loaded user session for ${name}`)
|
|
422
|
+
output.debug(`Loaded user session for ${name}`)
|
|
424
423
|
}
|
|
425
424
|
}
|
|
426
425
|
|
package/lib/plugin/autoDelay.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import Container from '../container.js'
|
|
2
|
+
|
|
3
|
+
import store from '../store.js'
|
|
4
|
+
|
|
5
|
+
import recorder from '../recorder.js'
|
|
6
|
+
|
|
7
|
+
import event from '../event.js'
|
|
8
|
+
|
|
9
|
+
import output from '../output.js'
|
|
6
10
|
const standardActingHelpers = Container.STANDARD_ACTING_HELPERS
|
|
7
11
|
|
|
8
12
|
const methodsToDelay = ['click', 'fillField', 'checkOption', 'pressKey', 'doubleClick', 'rightClick']
|
|
@@ -51,7 +55,7 @@ const defaultConfig = {
|
|
|
51
55
|
* * `delayAfter`: put a delay after a command. 200ms by default
|
|
52
56
|
*
|
|
53
57
|
*/
|
|
54
|
-
|
|
58
|
+
export default function (config) {
|
|
55
59
|
const affectedHelpers = [...standardActingHelpers, 'REST']
|
|
56
60
|
const helpers = Container.helpers()
|
|
57
61
|
let helper
|
|
@@ -71,7 +75,7 @@ module.exports = function (config) {
|
|
|
71
75
|
|
|
72
76
|
recorder.add('auto-delay', async () => {
|
|
73
77
|
if (store.debugMode) return // no need to delay in debug
|
|
74
|
-
log(`Delaying for ${config.delayBefore}ms`)
|
|
78
|
+
output.log(`Delaying for ${config.delayBefore}ms`)
|
|
75
79
|
return new Promise(resolve => {
|
|
76
80
|
setTimeout(resolve, config.delayBefore)
|
|
77
81
|
})
|
|
@@ -83,7 +87,7 @@ module.exports = function (config) {
|
|
|
83
87
|
|
|
84
88
|
recorder.add('auto-delay', async () => {
|
|
85
89
|
if (store.debugMode) return // no need to delay in debug
|
|
86
|
-
log(`Delaying for ${config.delayAfter}ms`)
|
|
90
|
+
output.log(`Delaying for ${config.delayAfter}ms`)
|
|
87
91
|
return new Promise(resolve => {
|
|
88
92
|
setTimeout(resolve, config.delayAfter)
|
|
89
93
|
})
|
package/lib/plugin/coverage.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import debugModule from 'debug'
|
|
2
|
+
import { CoverageReport } from 'monocart-coverage-reports'
|
|
3
|
+
import Container from '../container.js'
|
|
4
|
+
|
|
5
|
+
import recorder from '../recorder.js'
|
|
6
|
+
|
|
7
|
+
import event from '../event.js'
|
|
8
|
+
|
|
9
|
+
import output from '../output.js'
|
|
10
|
+
|
|
11
|
+
import { deepMerge } from '../utils.js'
|
|
8
12
|
|
|
9
13
|
const defaultConfig = {
|
|
10
14
|
name: 'CodeceptJS Coverage Report',
|
|
@@ -113,7 +117,7 @@ const v8CoverageHelpers = {
|
|
|
113
117
|
* * `sourcePath`: option to resolve a custom path.
|
|
114
118
|
*
|
|
115
119
|
*/
|
|
116
|
-
|
|
120
|
+
export default function (config) {
|
|
117
121
|
config = deepMerge(defaultConfig, config)
|
|
118
122
|
|
|
119
123
|
if (config.debug) config.logging = 'debug'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import Locator from '../locator.js'
|
|
2
|
+
import { xpathLocator } from '../utils.js'
|
|
3
3
|
|
|
4
4
|
const defaultConfig = {
|
|
5
5
|
prefix: '$',
|
|
@@ -111,7 +111,7 @@ const defaultConfig = {
|
|
|
111
111
|
* I.click('=sign-up'); // matches => [data-qa=sign-up],[data-test=sign-up]
|
|
112
112
|
* ```
|
|
113
113
|
*/
|
|
114
|
-
|
|
114
|
+
export default function (config) {
|
|
115
115
|
config = { ...defaultConfig, ...config }
|
|
116
116
|
|
|
117
117
|
Locator.addFilter((value, locatorObj) => {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
|
|
1
|
+
import event from '../event.js'
|
|
2
|
+
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Sample custom reporter for CodeceptJS.
|
|
5
6
|
*/
|
|
6
|
-
|
|
7
|
+
export default function (config) {
|
|
7
8
|
event.dispatcher.on(event.hook.finished, hook => {
|
|
8
9
|
if (config.onHookFinished) {
|
|
9
10
|
config.onHookFinished(hook)
|
package/lib/plugin/heal.js
CHANGED
|
@@ -1,11 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import debugFactory from 'debug'
|
|
2
|
+
const debug = debugFactory('codeceptjs:heal')
|
|
3
|
+
import colors from 'chalk'
|
|
4
|
+
import recorder from '../recorder.js'
|
|
5
|
+
|
|
6
|
+
import event from '../event.js'
|
|
7
|
+
|
|
8
|
+
import output from '../output.js'
|
|
9
|
+
|
|
10
|
+
import healModule from '../heal.js'
|
|
11
|
+
const heal = healModule.default || healModule
|
|
12
|
+
import store from '../store.js'
|
|
13
|
+
|
|
9
14
|
|
|
10
15
|
const defaultConfig = {
|
|
11
16
|
healLimit: 2,
|
|
@@ -29,7 +34,7 @@ const defaultConfig = {
|
|
|
29
34
|
* * `healLimit` - how many steps can be healed in a single test (default: 2)
|
|
30
35
|
*
|
|
31
36
|
*/
|
|
32
|
-
|
|
37
|
+
export default function (config = {}) {
|
|
33
38
|
if (store.debugMode && !process.env.DEBUG) {
|
|
34
39
|
event.dispatcher.on(event.test.failed, () => {
|
|
35
40
|
output.plugin('heal', 'Healing is disabled in --debug mode, use DEBUG="codeceptjs:heal" to enable it in debug mode')
|
package/lib/plugin/pageInfo.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const recorder = require('../recorder')
|
|
5
|
-
const event = require('../event')
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import fs from 'fs'
|
|
3
|
+
import Container from '../container.js'
|
|
6
4
|
const supportedHelpers = Container.STANDARD_ACTING_HELPERS
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
import recorder from '../recorder.js'
|
|
6
|
+
import event from '../event.js'
|
|
7
|
+
import { scanForErrorMessages } from '../html.js'
|
|
8
|
+
import { output } from '../index.js'
|
|
9
|
+
import { humanizeString, ucfirst } from '../utils.js'
|
|
10
|
+
import { testToFileName } from '../mocha/test.js'
|
|
11
11
|
const defaultConfig = {
|
|
12
12
|
errorClasses: ['error', 'warning', 'alert', 'danger'],
|
|
13
13
|
browserLogs: ['error'],
|
|
@@ -35,7 +35,7 @@ const defaultConfig = {
|
|
|
35
35
|
* * `browserLogs` - list of types of errors to search for in browser logs (default: `['error']`)
|
|
36
36
|
*
|
|
37
37
|
*/
|
|
38
|
-
|
|
38
|
+
export default function (config = {}) {
|
|
39
39
|
const helpers = Container.helpers()
|
|
40
40
|
let helper
|
|
41
41
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import event from '../event.js'
|
|
2
|
+
|
|
3
|
+
import pause from '../pause.js'
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Automatically launches [interactive pause](/basics/#pause) when a test fails.
|
|
@@ -21,7 +22,7 @@ const pause = require('../pause')
|
|
|
21
22
|
* ```
|
|
22
23
|
*
|
|
23
24
|
*/
|
|
24
|
-
|
|
25
|
+
export default function() {
|
|
25
26
|
let failed = false
|
|
26
27
|
|
|
27
28
|
event.dispatcher.on(event.test.started, () => {
|
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import event from '../event.js'
|
|
2
|
+
|
|
3
|
+
import recorder from '../recorder.js'
|
|
4
|
+
|
|
5
|
+
import store from '../store.js'
|
|
6
|
+
|
|
4
7
|
const defaultConfig = {
|
|
5
8
|
retries: 3,
|
|
6
9
|
defaultIgnoredSteps: ['amOnPage', 'wait*', 'send*', 'execute*', 'run*', 'have*'],
|
|
@@ -74,7 +77,7 @@ const defaultConfig = {
|
|
|
74
77
|
* ```
|
|
75
78
|
*
|
|
76
79
|
*/
|
|
77
|
-
|
|
80
|
+
export default function (config) {
|
|
78
81
|
config = Object.assign(defaultConfig, config)
|
|
79
82
|
config.ignoredSteps = config.ignoredSteps.concat(config.defaultIgnoredSteps)
|
|
80
83
|
const customWhen = config.when
|
|
@@ -85,11 +88,20 @@ module.exports = config => {
|
|
|
85
88
|
if (!enableRetry) return
|
|
86
89
|
if (store.debugMode) return false
|
|
87
90
|
if (!store.autoRetries) return false
|
|
91
|
+
// Don't retry terminal errors (e.g., frame detachment errors)
|
|
92
|
+
if (err && err.isTerminal) return false
|
|
93
|
+
// Don't retry navigation errors that are known to be terminal
|
|
94
|
+
if (err && err.message && (err.message.includes('ERR_ABORTED') || err.message.includes('frame was detached') || err.message.includes('Target page, context or browser has been closed'))) return false
|
|
88
95
|
if (customWhen) return customWhen(err)
|
|
89
96
|
return true
|
|
90
97
|
}
|
|
91
98
|
config.when = when
|
|
92
99
|
|
|
100
|
+
// Ensure retry options are available before any steps run
|
|
101
|
+
if (!recorder.retries.find(r => r === config)) {
|
|
102
|
+
recorder.retries.push(config)
|
|
103
|
+
}
|
|
104
|
+
|
|
93
105
|
event.dispatcher.on(event.step.started, step => {
|
|
94
106
|
// if a step is ignored - return
|
|
95
107
|
for (const ignored of config.ignoredSteps) {
|
|
@@ -101,7 +113,8 @@ module.exports = config => {
|
|
|
101
113
|
enableRetry = true // enable retry for a step
|
|
102
114
|
})
|
|
103
115
|
|
|
104
|
-
|
|
116
|
+
// Disable retry only after a successful step; keep it enabled for failure so retry logic can act
|
|
117
|
+
event.dispatcher.on(event.step.passed, () => {
|
|
105
118
|
enableRetry = false
|
|
106
119
|
})
|
|
107
120
|
|
|
@@ -112,9 +125,38 @@ module.exports = config => {
|
|
|
112
125
|
store.autoRetries = false
|
|
113
126
|
return // disable retry when a test is not active
|
|
114
127
|
}
|
|
128
|
+
|
|
129
|
+
// Don't apply plugin retry logic if there are already manual retries configured
|
|
130
|
+
// Check if any retry configs exist that aren't from this plugin
|
|
131
|
+
const hasManualRetries = recorder.retries.some(retry => retry !== config)
|
|
132
|
+
if (hasManualRetries) {
|
|
133
|
+
store.autoRetries = false
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
|
|
115
137
|
// this option is used to set the retries inside _before() block of helpers
|
|
116
138
|
store.autoRetries = true
|
|
117
139
|
test.opts.conditionalRetries = config.retries
|
|
140
|
+
// debug: record applied retries value for tests
|
|
141
|
+
if (process.env.DEBUG_RETRY_PLUGIN) {
|
|
142
|
+
// eslint-disable-next-line no-console
|
|
143
|
+
console.log('[retryFailedStep] applying retries =', config.retries, 'for test', test.title)
|
|
144
|
+
}
|
|
118
145
|
recorder.retry(config)
|
|
119
146
|
})
|
|
147
|
+
|
|
148
|
+
// Fallback for environments where event.test.before wasn't emitted (runner scenarios)
|
|
149
|
+
event.dispatcher.on(event.test.started, test => {
|
|
150
|
+
if (test.opts?.disableRetryFailedStep || test.disableRetryFailedStep) return
|
|
151
|
+
|
|
152
|
+
// Don't apply plugin retry logic if there are already manual retries configured
|
|
153
|
+
// Check if any retry configs exist that aren't from this plugin
|
|
154
|
+
const hasManualRetries = recorder.retries.some(retry => retry !== config)
|
|
155
|
+
if (hasManualRetries) return
|
|
156
|
+
|
|
157
|
+
if (!store.autoRetries) {
|
|
158
|
+
store.autoRetries = true
|
|
159
|
+
test.opts.conditionalRetries = test.opts.conditionalRetries || config.retries
|
|
160
|
+
}
|
|
161
|
+
})
|
|
120
162
|
}
|
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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'
|
|
11
15
|
|
|
12
16
|
const defaultConfig = {
|
|
13
17
|
uniqueScreenshotNames: false,
|
|
@@ -43,7 +47,7 @@ const supportedHelpers = Container.STANDARD_ACTING_HELPERS
|
|
|
43
47
|
*
|
|
44
48
|
*
|
|
45
49
|
*/
|
|
46
|
-
|
|
50
|
+
export default function (config) {
|
|
47
51
|
const helpers = Container.helpers()
|
|
48
52
|
let helper
|
|
49
53
|
|
|
@@ -86,11 +90,28 @@ module.exports = function (config) {
|
|
|
86
90
|
let fileName
|
|
87
91
|
|
|
88
92
|
if (options.uniqueScreenshotNames && test) {
|
|
89
|
-
fileName = `${testToFileName(test, { unique: true })}.failed.png`
|
|
93
|
+
fileName = `${testToFileName(test, { suffix: '', unique: true })}.failed.png`
|
|
90
94
|
} else {
|
|
91
|
-
fileName = `${testToFileName(test)}.failed.png`
|
|
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
|
|
92
114
|
}
|
|
93
|
-
output.plugin('screenshotOnFail', 'Test failed, try to save a screenshot')
|
|
94
115
|
|
|
95
116
|
try {
|
|
96
117
|
if (options.reportDir) {
|
|
@@ -100,36 +121,53 @@ module.exports = function (config) {
|
|
|
100
121
|
fs.mkdirSync(mochaReportDir)
|
|
101
122
|
}
|
|
102
123
|
}
|
|
103
|
-
await helper.saveScreenshot(fileName, options.fullPageScreenshots)
|
|
104
124
|
|
|
105
|
-
if
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
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')
|
|
109
128
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (allureReporter) {
|
|
113
|
-
allureReporter.addAttachment('Main session - Last Seen Screenshot', fs.readFileSync(path.join(global.output_dir, fileName)), dataType)
|
|
114
|
-
|
|
115
|
-
if (helper.activeSessionName) {
|
|
116
|
-
const sessions = helper.sessionPages || helper.sessionWindows
|
|
117
|
-
for (const sessionName in sessions) {
|
|
118
|
-
const screenshotFileName = `${sessionName}_${fileName}`
|
|
119
|
-
test.artifacts[`${sessionName.replace(/ /g, '_')}_screenshot`] = path.join(global.output_dir, screenshotFileName)
|
|
120
|
-
allureReporter.addAttachment(`${sessionName} - Last Seen Screenshot`, fs.readFileSync(path.join(global.output_dir, screenshotFileName)), dataType)
|
|
121
|
-
}
|
|
122
|
-
}
|
|
129
|
+
if (helper.browser && helper.browser.isConnected && !helper.browser.isConnected()) {
|
|
130
|
+
throw new Error('Browser has been disconnected')
|
|
123
131
|
}
|
|
124
132
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
|
128
153
|
}
|
|
129
154
|
} catch (err) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
155
|
+
if (!quietMode) {
|
|
156
|
+
output.plugin('screenshotOnFail', `Failed to save screenshot: ${err.message}`)
|
|
157
|
+
}
|
|
158
|
+
// Enhanced error handling for browser closed scenarios
|
|
159
|
+
if (
|
|
160
|
+
err &&
|
|
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'))
|
|
169
|
+
) {
|
|
170
|
+
output.log(`Can't make screenshot, ${err.message}`)
|
|
133
171
|
helper.isRunning = false
|
|
134
172
|
}
|
|
135
173
|
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
14
|
|
|
15
15
|
const supportedHelpers = Container.STANDARD_ACTING_HELPERS
|
|
16
16
|
|
|
@@ -63,7 +63,7 @@ const templates = {}
|
|
|
63
63
|
* @param {*} config
|
|
64
64
|
*/
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
export default function (config) {
|
|
67
67
|
const helpers = Container.helpers()
|
|
68
68
|
let helper
|
|
69
69
|
|
|
@@ -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,7 +62,7 @@ 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)
|
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,7 +29,7 @@ let testStartedAt
|
|
|
28
29
|
* }
|
|
29
30
|
* ```
|
|
30
31
|
*/
|
|
31
|
-
|
|
32
|
+
export default function () {
|
|
32
33
|
event.dispatcher.on(event.test.before, _ => {
|
|
33
34
|
testStartedAt = Date.now()
|
|
34
35
|
steps = {}
|