codeceptjs 4.0.0-beta.9.esm-aria → 4.0.0-rc.10
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 +39 -27
- package/bin/codecept.js +2 -2
- package/bin/mcp-server.js +610 -0
- package/docs/webapi/appendField.mustache +5 -0
- package/docs/webapi/attachFile.mustache +12 -0
- package/docs/webapi/checkOption.mustache +1 -1
- package/docs/webapi/clearField.mustache +5 -0
- package/docs/webapi/dontSeeCurrentPathEquals.mustache +10 -0
- package/docs/webapi/dontSeeElement.mustache +4 -0
- package/docs/webapi/dontSeeInField.mustache +5 -0
- package/docs/webapi/fillField.mustache +5 -0
- package/docs/webapi/moveCursorTo.mustache +5 -1
- package/docs/webapi/seeCurrentPathEquals.mustache +10 -0
- package/docs/webapi/seeElement.mustache +4 -0
- package/docs/webapi/seeInField.mustache +5 -0
- package/docs/webapi/selectOption.mustache +5 -0
- package/docs/webapi/uncheckOption.mustache +1 -1
- package/lib/actor.js +12 -8
- package/lib/codecept.js +51 -18
- package/lib/command/definitions.js +14 -7
- package/lib/command/init.js +2 -4
- package/lib/command/run-workers.js +13 -2
- package/lib/command/workers/runTests.js +121 -9
- package/lib/config.js +24 -33
- package/lib/container.js +177 -28
- package/lib/element/WebElement.js +81 -2
- package/lib/els.js +12 -6
- package/lib/helper/Appium.js +8 -8
- package/lib/helper/GraphQL.js +6 -4
- package/lib/helper/JSONResponse.js +3 -4
- package/lib/helper/Playwright.js +339 -505
- package/lib/helper/Puppeteer.js +324 -89
- package/lib/helper/REST.js +15 -9
- package/lib/helper/WebDriver.js +311 -81
- package/lib/helper/errors/ElementNotFound.js +5 -2
- package/lib/helper/errors/MultipleElementsFound.js +52 -0
- package/lib/helper/extras/elementSelection.js +58 -0
- package/lib/helper/scripts/dropFile.js +11 -0
- package/lib/html.js +14 -1
- package/lib/listener/config.js +11 -3
- package/lib/listener/globalRetry.js +32 -6
- package/lib/listener/helpers.js +2 -14
- package/lib/locator.js +32 -0
- package/lib/mocha/cli.js +16 -0
- package/lib/mocha/factory.js +7 -27
- package/lib/mocha/gherkin.js +4 -4
- package/lib/mocha/test.js +4 -2
- package/lib/output.js +2 -2
- package/lib/plugin/aiTrace.js +464 -0
- package/lib/plugin/auth.js +2 -1
- package/lib/plugin/retryFailedStep.js +28 -19
- package/lib/plugin/stepByStepReport.js +5 -1
- package/lib/step/base.js +14 -1
- package/lib/step/config.js +15 -2
- package/lib/step/meta.js +18 -1
- package/lib/step/record.js +9 -1
- package/lib/utils/loaderCheck.js +162 -0
- package/lib/utils/typescript.js +449 -0
- package/lib/utils.js +48 -0
- package/lib/workers.js +163 -54
- package/package.json +43 -32
- package/typings/index.d.ts +120 -4
- package/lib/helper/extras/PlaywrightLocator.js +0 -110
- package/lib/listener/enhancedGlobalRetry.js +0 -110
- package/lib/plugin/enhancedRetryFailedStep.js +0 -99
- package/lib/plugin/htmlReporter.js +0 -3648
- package/lib/retryCoordinator.js +0 -207
- package/typings/promiseBasedTypes.d.ts +0 -11011
- package/typings/types.d.ts +0 -13073
package/typings/index.d.ts
CHANGED
|
@@ -519,12 +519,12 @@ declare namespace CodeceptJS {
|
|
|
519
519
|
retry(retries?: number): HookConfig
|
|
520
520
|
}
|
|
521
521
|
|
|
522
|
-
function addStep(step: string, fn: Function): Promise<void>
|
|
522
|
+
function addStep(step: string | RegExp, fn: Function): Promise<void>
|
|
523
523
|
}
|
|
524
524
|
|
|
525
|
-
type TryTo =
|
|
526
|
-
type HopeThat =
|
|
527
|
-
type RetryTo =
|
|
525
|
+
type TryTo = (fn: () => void) => Promise<boolean>
|
|
526
|
+
type HopeThat = (fn: () => void) => Promise<boolean>
|
|
527
|
+
type RetryTo = (fn: (tries: number) => Promise<void> | void, maxTries: number, pollInterval?: number) => Promise<boolean>
|
|
528
528
|
|
|
529
529
|
// Globals
|
|
530
530
|
declare const codecept_dir: string
|
|
@@ -635,8 +635,105 @@ declare namespace Mocha {
|
|
|
635
635
|
}
|
|
636
636
|
}
|
|
637
637
|
|
|
638
|
+
// Internal API types
|
|
638
639
|
declare module 'codeceptjs' {
|
|
639
640
|
export default codeceptjs
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Dependency Injection Container
|
|
644
|
+
* Provides access to helpers, support objects, plugins, and translation
|
|
645
|
+
*/
|
|
646
|
+
export const container: typeof CodeceptJS.Container
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Test runner class
|
|
650
|
+
*/
|
|
651
|
+
export const codecept: typeof CodeceptJS.Codecept
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* Output module for printing messages
|
|
655
|
+
*/
|
|
656
|
+
export const output: typeof CodeceptJS.output
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* Event dispatcher for listening to CodeceptJS events
|
|
660
|
+
*/
|
|
661
|
+
export const event: typeof CodeceptJS.event
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* Global promise chain recorder
|
|
665
|
+
*/
|
|
666
|
+
export const recorder: CodeceptJS.recorder
|
|
667
|
+
|
|
668
|
+
/**
|
|
669
|
+
* Configuration module
|
|
670
|
+
*/
|
|
671
|
+
export const config: typeof CodeceptJS.Config
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Actor (I) constructor
|
|
675
|
+
*/
|
|
676
|
+
export const actor: CodeceptJS.actor
|
|
677
|
+
|
|
678
|
+
/**
|
|
679
|
+
* Base Helper class
|
|
680
|
+
*/
|
|
681
|
+
export const helper: typeof CodeceptJS.Helper
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Pause execution until user input
|
|
685
|
+
*/
|
|
686
|
+
export const pause: typeof CodeceptJS.pause
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* Execute steps within specific context
|
|
690
|
+
*/
|
|
691
|
+
export const within: typeof CodeceptJS.within
|
|
692
|
+
|
|
693
|
+
/**
|
|
694
|
+
* Create data tables for data-driven tests
|
|
695
|
+
*/
|
|
696
|
+
export const dataTable: typeof CodeceptJS.DataTable
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* Create data table arguments
|
|
700
|
+
*/
|
|
701
|
+
export const dataTableArgument: typeof CodeceptJS.DataTableArgument
|
|
702
|
+
|
|
703
|
+
/**
|
|
704
|
+
* Shared store for test data
|
|
705
|
+
*/
|
|
706
|
+
export const store: typeof CodeceptJS.store
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* Locator builder
|
|
710
|
+
*/
|
|
711
|
+
export const locator: typeof CodeceptJS.Locator
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* Auto-healing module
|
|
715
|
+
*/
|
|
716
|
+
export const heal: any
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* AI assistant module
|
|
720
|
+
*/
|
|
721
|
+
export const ai: any
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* Workers for parallel execution
|
|
725
|
+
*/
|
|
726
|
+
export const Workers: any
|
|
727
|
+
|
|
728
|
+
/**
|
|
729
|
+
* Secret value type for sensitive data
|
|
730
|
+
*/
|
|
731
|
+
export const Secret: typeof CodeceptJS.Secret
|
|
732
|
+
|
|
733
|
+
/**
|
|
734
|
+
* Create a secret value
|
|
735
|
+
*/
|
|
736
|
+
export const secret: typeof CodeceptJS.Secret.secret
|
|
640
737
|
}
|
|
641
738
|
|
|
642
739
|
declare module '@codeceptjs/helper' {
|
|
@@ -648,3 +745,22 @@ declare module 'codeceptjs/effects' {
|
|
|
648
745
|
export const retryTo: RetryTo
|
|
649
746
|
export const hopeThat: HopeThat
|
|
650
747
|
}
|
|
748
|
+
|
|
749
|
+
declare module 'codeceptjs/steps' {
|
|
750
|
+
const step: {
|
|
751
|
+
opts(opts: CodeceptJS.StepOptions): CodeceptJS.StepConfig;
|
|
752
|
+
timeout(timeout: number): CodeceptJS.StepConfig;
|
|
753
|
+
retry(retry: number): CodeceptJS.StepConfig;
|
|
754
|
+
stepOpts(opts: CodeceptJS.StepOptions): CodeceptJS.StepConfig;
|
|
755
|
+
stepTimeout(timeout: number): CodeceptJS.StepConfig;
|
|
756
|
+
stepRetry(retry: number): CodeceptJS.StepConfig;
|
|
757
|
+
section(name: string): any;
|
|
758
|
+
endSection(): any;
|
|
759
|
+
Section(name: string): any;
|
|
760
|
+
EndSection(): any;
|
|
761
|
+
Given(): any;
|
|
762
|
+
When(): any;
|
|
763
|
+
Then(): any;
|
|
764
|
+
}
|
|
765
|
+
export default step
|
|
766
|
+
}
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import Locator from '../../locator.js'
|
|
2
|
-
|
|
3
|
-
function buildLocatorString(locator) {
|
|
4
|
-
if (locator.isCustom()) {
|
|
5
|
-
return `${locator.type}=${locator.value}`
|
|
6
|
-
}
|
|
7
|
-
if (locator.isXPath()) {
|
|
8
|
-
return `xpath=${locator.value}`
|
|
9
|
-
}
|
|
10
|
-
return locator.simplify()
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
async function findElements(matcher, locator) {
|
|
14
|
-
const matchedLocator = new Locator(locator, 'css')
|
|
15
|
-
|
|
16
|
-
if (matchedLocator.type === 'react') return findReact(matcher, matchedLocator)
|
|
17
|
-
if (matchedLocator.type === 'vue') return findVue(matcher, matchedLocator)
|
|
18
|
-
if (matchedLocator.type === 'pw') return findByPlaywrightLocator(matcher, matchedLocator)
|
|
19
|
-
if (matchedLocator.isRole()) return findByRole(matcher, matchedLocator)
|
|
20
|
-
|
|
21
|
-
return matcher.locator(buildLocatorString(matchedLocator)).all()
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
async function findElement(matcher, locator) {
|
|
25
|
-
const matchedLocator = new Locator(locator, 'css')
|
|
26
|
-
|
|
27
|
-
if (matchedLocator.type === 'react') return findReact(matcher, matchedLocator)
|
|
28
|
-
if (matchedLocator.type === 'vue') return findVue(matcher, matchedLocator)
|
|
29
|
-
if (matchedLocator.type === 'pw') return findByPlaywrightLocator(matcher, matchedLocator, { first: true })
|
|
30
|
-
if (matchedLocator.isRole()) return findByRole(matcher, matchedLocator, { first: true })
|
|
31
|
-
|
|
32
|
-
return matcher.locator(buildLocatorString(matchedLocator)).first()
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async function getVisibleElements(elements) {
|
|
36
|
-
const visibleElements = []
|
|
37
|
-
for (const element of elements) {
|
|
38
|
-
if (await element.isVisible()) {
|
|
39
|
-
visibleElements.push(element)
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
if (visibleElements.length === 0) {
|
|
43
|
-
return elements
|
|
44
|
-
}
|
|
45
|
-
return visibleElements
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async function findReact(matcher, locator) {
|
|
49
|
-
const details = locator.locator ?? { react: locator.value }
|
|
50
|
-
let locatorString = `_react=${details.react}`
|
|
51
|
-
|
|
52
|
-
if (details.props) {
|
|
53
|
-
locatorString += propBuilder(details.props)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return matcher.locator(locatorString).all()
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
async function findVue(matcher, locator) {
|
|
60
|
-
const details = locator.locator ?? { vue: locator.value }
|
|
61
|
-
let locatorString = `_vue=${details.vue}`
|
|
62
|
-
|
|
63
|
-
if (details.props) {
|
|
64
|
-
locatorString += propBuilder(details.props)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
return matcher.locator(locatorString).all()
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async function findByPlaywrightLocator(matcher, locator, { first = false } = {}) {
|
|
71
|
-
const details = locator.locator ?? { pw: locator.value }
|
|
72
|
-
const locatorValue = details.pw
|
|
73
|
-
|
|
74
|
-
const handle = matcher.locator(locatorValue)
|
|
75
|
-
return first ? handle.first() : handle.all()
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
async function findByRole(matcher, locator, { first = false } = {}) {
|
|
79
|
-
const details = locator.locator ?? { role: locator.value }
|
|
80
|
-
const { role, text, name, exact, includeHidden, ...rest } = details
|
|
81
|
-
const options = { ...rest }
|
|
82
|
-
|
|
83
|
-
if (includeHidden !== undefined) options.includeHidden = includeHidden
|
|
84
|
-
|
|
85
|
-
const accessibleName = name ?? text
|
|
86
|
-
if (accessibleName !== undefined) {
|
|
87
|
-
options.name = accessibleName
|
|
88
|
-
if (exact === true) options.exact = true
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const roleLocator = matcher.getByRole(role, options)
|
|
92
|
-
return first ? roleLocator.first() : roleLocator.all()
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function propBuilder(props) {
|
|
96
|
-
let _props = ''
|
|
97
|
-
|
|
98
|
-
for (const [key, value] of Object.entries(props)) {
|
|
99
|
-
if (typeof value === 'object') {
|
|
100
|
-
for (const [k, v] of Object.entries(value)) {
|
|
101
|
-
_props += `[${key}.${k} = "${v}"]`
|
|
102
|
-
}
|
|
103
|
-
} else {
|
|
104
|
-
_props += `[${key} = "${value}"]`
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
return _props
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export { buildLocatorString, findElements, findElement, getVisibleElements, findReact, findVue, findByPlaywrightLocator, findByRole }
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
import event from '../event.js'
|
|
2
|
-
import output from '../output.js'
|
|
3
|
-
import Config from '../config.js'
|
|
4
|
-
import { isNotSet } from '../utils.js'
|
|
5
|
-
|
|
6
|
-
const hooks = ['Before', 'After', 'BeforeSuite', 'AfterSuite']
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Priority levels for retry mechanisms (higher number = higher priority)
|
|
10
|
-
* This ensures consistent behavior when multiple retry mechanisms are active
|
|
11
|
-
*/
|
|
12
|
-
const RETRY_PRIORITIES = {
|
|
13
|
-
MANUAL_STEP: 100, // I.retry() or step.retry() - highest priority
|
|
14
|
-
STEP_PLUGIN: 50, // retryFailedStep plugin
|
|
15
|
-
SCENARIO_CONFIG: 30, // Global scenario retry config
|
|
16
|
-
FEATURE_CONFIG: 20, // Global feature retry config
|
|
17
|
-
HOOK_CONFIG: 10, // Hook retry config - lowest priority
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Enhanced global retry mechanism that coordinates with other retry types
|
|
22
|
-
*/
|
|
23
|
-
export default function () {
|
|
24
|
-
event.dispatcher.on(event.suite.before, suite => {
|
|
25
|
-
let retryConfig = Config.get('retry')
|
|
26
|
-
if (!retryConfig) return
|
|
27
|
-
|
|
28
|
-
if (Number.isInteger(+retryConfig)) {
|
|
29
|
-
// is number - apply as feature-level retry
|
|
30
|
-
const retryNum = +retryConfig
|
|
31
|
-
output.log(`[Global Retry] Feature retries: ${retryNum}`)
|
|
32
|
-
|
|
33
|
-
// Only set if not already set by higher priority mechanism
|
|
34
|
-
if (isNotSet(suite.retries())) {
|
|
35
|
-
suite.retries(retryNum)
|
|
36
|
-
suite.opts.retryPriority = RETRY_PRIORITIES.FEATURE_CONFIG
|
|
37
|
-
}
|
|
38
|
-
return
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (!Array.isArray(retryConfig)) {
|
|
42
|
-
retryConfig = [retryConfig]
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
for (const config of retryConfig) {
|
|
46
|
-
if (config.grep) {
|
|
47
|
-
if (!suite.title.includes(config.grep)) continue
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Handle hook retries with priority awareness
|
|
51
|
-
hooks
|
|
52
|
-
.filter(hook => !!config[hook])
|
|
53
|
-
.forEach(hook => {
|
|
54
|
-
const retryKey = `retry${hook}`
|
|
55
|
-
if (isNotSet(suite.opts[retryKey])) {
|
|
56
|
-
suite.opts[retryKey] = config[hook]
|
|
57
|
-
suite.opts[`${retryKey}Priority`] = RETRY_PRIORITIES.HOOK_CONFIG
|
|
58
|
-
}
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
// Handle feature-level retries
|
|
62
|
-
if (config.Feature) {
|
|
63
|
-
if (isNotSet(suite.retries()) || (suite.opts.retryPriority || 0) <= RETRY_PRIORITIES.FEATURE_CONFIG) {
|
|
64
|
-
suite.retries(config.Feature)
|
|
65
|
-
suite.opts.retryPriority = RETRY_PRIORITIES.FEATURE_CONFIG
|
|
66
|
-
output.log(`[Global Retry] Feature retries: ${config.Feature}`)
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
event.dispatcher.on(event.test.before, test => {
|
|
73
|
-
let retryConfig = Config.get('retry')
|
|
74
|
-
if (!retryConfig) return
|
|
75
|
-
|
|
76
|
-
if (Number.isInteger(+retryConfig)) {
|
|
77
|
-
// Only set if not already set by higher priority mechanism
|
|
78
|
-
if (test.retries() === -1) {
|
|
79
|
-
test.retries(retryConfig)
|
|
80
|
-
test.opts.retryPriority = RETRY_PRIORITIES.SCENARIO_CONFIG
|
|
81
|
-
output.log(`[Global Retry] Scenario retries: ${retryConfig}`)
|
|
82
|
-
}
|
|
83
|
-
return
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (!Array.isArray(retryConfig)) {
|
|
87
|
-
retryConfig = [retryConfig]
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
retryConfig = retryConfig.filter(config => !!config.Scenario)
|
|
91
|
-
|
|
92
|
-
for (const config of retryConfig) {
|
|
93
|
-
if (config.grep) {
|
|
94
|
-
if (!test.fullTitle().includes(config.grep)) continue
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
if (config.Scenario) {
|
|
98
|
-
// Respect priority system
|
|
99
|
-
if (test.retries() === -1 || (test.opts.retryPriority || 0) <= RETRY_PRIORITIES.SCENARIO_CONFIG) {
|
|
100
|
-
test.retries(config.Scenario)
|
|
101
|
-
test.opts.retryPriority = RETRY_PRIORITIES.SCENARIO_CONFIG
|
|
102
|
-
output.log(`[Global Retry] Scenario retries: ${config.Scenario}`)
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
})
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Export priority constants for use by other retry mechanisms
|
|
110
|
-
export { RETRY_PRIORITIES }
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import event from '../event.js'
|
|
2
|
-
import recorder from '../recorder.js'
|
|
3
|
-
import store from '../store.js'
|
|
4
|
-
import output from '../output.js'
|
|
5
|
-
import { RETRY_PRIORITIES } from '../retryCoordinator.js'
|
|
6
|
-
|
|
7
|
-
const defaultConfig = {
|
|
8
|
-
retries: 3,
|
|
9
|
-
defaultIgnoredSteps: ['amOnPage', 'wait*', 'send*', 'execute*', 'run*', 'have*'],
|
|
10
|
-
factor: 1.5,
|
|
11
|
-
ignoredSteps: [],
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Enhanced retryFailedStep plugin that coordinates with other retry mechanisms
|
|
16
|
-
*
|
|
17
|
-
* This plugin provides step-level retries and coordinates with global retry settings
|
|
18
|
-
* to avoid conflicts and provide predictable behavior.
|
|
19
|
-
*/
|
|
20
|
-
export default config => {
|
|
21
|
-
config = Object.assign({}, defaultConfig, config)
|
|
22
|
-
config.ignoredSteps = config.ignoredSteps.concat(config.defaultIgnoredSteps)
|
|
23
|
-
const customWhen = config.when
|
|
24
|
-
|
|
25
|
-
let enableRetry = false
|
|
26
|
-
|
|
27
|
-
const when = err => {
|
|
28
|
-
if (!enableRetry) return false
|
|
29
|
-
if (store.debugMode) return false
|
|
30
|
-
if (!store.autoRetries) return false
|
|
31
|
-
if (customWhen) return customWhen(err)
|
|
32
|
-
return true
|
|
33
|
-
}
|
|
34
|
-
config.when = when
|
|
35
|
-
|
|
36
|
-
event.dispatcher.on(event.step.started, step => {
|
|
37
|
-
// if a step is ignored - return
|
|
38
|
-
for (const ignored of config.ignoredSteps) {
|
|
39
|
-
if (step.name === ignored) return
|
|
40
|
-
if (ignored instanceof RegExp) {
|
|
41
|
-
if (step.name.match(ignored)) return
|
|
42
|
-
} else if (ignored.indexOf('*') && step.name.startsWith(ignored.slice(0, -1))) return
|
|
43
|
-
}
|
|
44
|
-
enableRetry = true // enable retry for a step
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
event.dispatcher.on(event.step.finished, () => {
|
|
48
|
-
enableRetry = false
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
event.dispatcher.on(event.test.before, test => {
|
|
52
|
-
// pass disableRetryFailedStep is a preferred way to disable retries
|
|
53
|
-
// test.disableRetryFailedStep is used for backward compatibility
|
|
54
|
-
if (test.opts.disableRetryFailedStep || test.disableRetryFailedStep) {
|
|
55
|
-
store.autoRetries = false
|
|
56
|
-
output.log(`[Step Retry] Disabled for test: ${test.title}`)
|
|
57
|
-
return // disable retry when a test is not active
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Check if step retries should be disabled due to higher priority scenario retries
|
|
61
|
-
const scenarioRetries = test.retries()
|
|
62
|
-
const stepRetryPriority = RETRY_PRIORITIES.STEP_PLUGIN
|
|
63
|
-
const scenarioPriority = test.opts.retryPriority || 0
|
|
64
|
-
|
|
65
|
-
if (scenarioRetries > 0 && config.deferToScenarioRetries !== false) {
|
|
66
|
-
// Scenario retries are configured with higher or equal priority
|
|
67
|
-
// Option 1: Disable step retries (conservative approach)
|
|
68
|
-
store.autoRetries = false
|
|
69
|
-
output.log(`[Step Retry] Deferred to scenario retries (${scenarioRetries} retries)`)
|
|
70
|
-
return
|
|
71
|
-
|
|
72
|
-
// Option 2: Reduce step retries to avoid excessive total retries
|
|
73
|
-
// const reducedStepRetries = Math.max(1, Math.floor(config.retries / scenarioRetries))
|
|
74
|
-
// config.retries = reducedStepRetries
|
|
75
|
-
// output.log(`[Step Retry] Reduced to ${reducedStepRetries} retries due to scenario retries (${scenarioRetries})`)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// this option is used to set the retries inside _before() block of helpers
|
|
79
|
-
store.autoRetries = true
|
|
80
|
-
test.opts.conditionalRetries = config.retries
|
|
81
|
-
test.opts.stepRetryPriority = stepRetryPriority
|
|
82
|
-
|
|
83
|
-
recorder.retry(config)
|
|
84
|
-
|
|
85
|
-
output.log(`[Step Retry] Enabled with ${config.retries} retries for test: ${test.title}`)
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
// Add coordination info for debugging
|
|
89
|
-
event.dispatcher.on(event.test.finished, test => {
|
|
90
|
-
if (test.state === 'passed' && test.opts.conditionalRetries && store.autoRetries) {
|
|
91
|
-
const stepRetries = test.opts.conditionalRetries || 0
|
|
92
|
-
const scenarioRetries = test.retries() || 0
|
|
93
|
-
|
|
94
|
-
if (stepRetries > 0 && scenarioRetries > 0) {
|
|
95
|
-
output.log(`[Retry Coordination] Test used both step retries (${stepRetries}) and scenario retries (${scenarioRetries})`)
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
})
|
|
99
|
-
}
|