codeceptjs 3.6.4 → 3.6.5-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 +84 -63
- package/lib/assert/empty.js +19 -19
- package/lib/assert/equal.js +32 -30
- package/lib/assert/error.js +14 -14
- package/lib/assert/include.js +42 -42
- package/lib/assert/throws.js +13 -11
- package/lib/assert/truth.js +17 -18
- package/lib/command/configMigrate.js +57 -52
- package/lib/command/definitions.js +88 -88
- package/lib/command/dryRun.js +65 -63
- package/lib/command/generate.js +191 -181
- package/lib/command/info.js +39 -37
- package/lib/command/init.js +289 -286
- package/lib/command/interactive.js +32 -32
- package/lib/command/list.js +26 -26
- package/lib/command/run-multiple.js +113 -93
- package/lib/command/run-rerun.js +22 -22
- package/lib/command/run-workers.js +63 -63
- package/lib/command/run.js +24 -26
- package/lib/command/utils.js +64 -63
- package/lib/data/context.js +60 -60
- package/lib/data/dataScenarioConfig.js +47 -47
- package/lib/data/dataTableArgument.js +29 -29
- package/lib/data/table.js +26 -20
- package/lib/helper/AI.js +67 -65
- package/lib/helper/ApiDataFactory.js +72 -69
- package/lib/helper/Appium.js +409 -379
- package/lib/helper/ExpectHelper.js +214 -248
- package/lib/helper/FileSystem.js +77 -78
- package/lib/helper/GraphQL.js +44 -43
- package/lib/helper/GraphQLDataFactory.js +49 -50
- package/lib/helper/JSONResponse.js +64 -62
- package/lib/helper/Mochawesome.js +28 -28
- package/lib/helper/MockServer.js +12 -12
- package/lib/helper/Nightmare.js +664 -572
- package/lib/helper/Playwright.js +1320 -1211
- package/lib/helper/Protractor.js +663 -629
- package/lib/helper/Puppeteer.js +1232 -1124
- package/lib/helper/REST.js +87 -72
- package/lib/helper/TestCafe.js +490 -491
- package/lib/helper/WebDriver.js +1294 -1156
- package/lib/interfaces/bdd.js +38 -51
- package/lib/interfaces/featureConfig.js +19 -19
- package/lib/interfaces/gherkin.js +122 -111
- package/lib/interfaces/scenarioConfig.js +29 -29
- package/lib/listener/artifacts.js +9 -9
- package/lib/listener/config.js +24 -23
- package/lib/listener/exit.js +12 -12
- package/lib/listener/helpers.js +42 -42
- package/lib/listener/mocha.js +11 -11
- package/lib/listener/retry.js +32 -30
- package/lib/listener/steps.js +50 -51
- package/lib/listener/timeout.js +53 -53
- package/lib/plugin/allure.js +14 -14
- package/lib/plugin/autoDelay.js +29 -36
- package/lib/plugin/autoLogin.js +70 -66
- package/lib/plugin/commentStep.js +18 -18
- package/lib/plugin/coverage.js +92 -77
- package/lib/plugin/customLocator.js +20 -19
- package/lib/plugin/debugErrors.js +24 -24
- package/lib/plugin/eachElement.js +37 -37
- package/lib/plugin/fakerTransform.js +6 -6
- package/lib/plugin/heal.js +66 -63
- package/lib/plugin/pauseOnFail.js +10 -10
- package/lib/plugin/retryFailedStep.js +31 -38
- package/lib/plugin/retryTo.js +28 -28
- package/lib/plugin/screenshotOnFail.js +107 -86
- package/lib/plugin/selenoid.js +131 -117
- package/lib/plugin/standardActingHelpers.js +2 -8
- package/lib/plugin/stepByStepReport.js +102 -92
- package/lib/plugin/stepTimeout.js +23 -22
- package/lib/plugin/subtitles.js +34 -34
- package/lib/plugin/tryTo.js +39 -29
- package/lib/plugin/wdio.js +77 -72
- package/lib/template/heal.js +11 -14
- package/package.json +4 -2
- package/translations/de-DE.js +1 -1
- package/translations/fr-FR.js +1 -1
- package/translations/index.js +9 -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/promiseBasedTypes.d.ts +238 -0
- package/typings/types.d.ts +32 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
const Locator = require('../locator')
|
|
2
|
-
const { xpathLocator } = require('../utils')
|
|
1
|
+
const Locator = require('../locator')
|
|
2
|
+
const { xpathLocator } = require('../utils')
|
|
3
3
|
|
|
4
4
|
const defaultConfig = {
|
|
5
5
|
prefix: '$',
|
|
6
6
|
attribute: 'data-test-id',
|
|
7
7
|
strategy: 'xpath',
|
|
8
8
|
showActual: false,
|
|
9
|
-
}
|
|
9
|
+
}
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* Creates a [custom locator](https://codecept.io/locators#custom-locators) by using special attributes in HTML.
|
|
@@ -112,33 +112,34 @@ const defaultConfig = {
|
|
|
112
112
|
* ```
|
|
113
113
|
*/
|
|
114
114
|
module.exports = (config) => {
|
|
115
|
-
config = { ...defaultConfig, ...config }
|
|
115
|
+
config = { ...defaultConfig, ...config }
|
|
116
116
|
|
|
117
117
|
Locator.addFilter((value, locatorObj) => {
|
|
118
|
-
if (typeof value !== 'string') return
|
|
119
|
-
if (!value.startsWith(config.prefix)) return
|
|
118
|
+
if (typeof value !== 'string') return
|
|
119
|
+
if (!value.startsWith(config.prefix)) return
|
|
120
120
|
|
|
121
|
-
if (!['String', 'Array'].includes(config.attribute.constructor.name)) return
|
|
121
|
+
if (!['String', 'Array'].includes(config.attribute.constructor.name)) return
|
|
122
122
|
|
|
123
|
-
const val = value.substr(config.prefix.length)
|
|
123
|
+
const val = value.substr(config.prefix.length)
|
|
124
124
|
|
|
125
125
|
if (config.strategy.toLowerCase() === 'xpath') {
|
|
126
|
-
locatorObj.value = `.//*[${
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
locatorObj.type = 'xpath'
|
|
126
|
+
locatorObj.value = `.//*[${[]
|
|
127
|
+
.concat(config.attribute)
|
|
128
|
+
.map((attr) => `@${attr}=${xpathLocator.literal(val)}`)
|
|
129
|
+
.join(' or ')}]`
|
|
130
|
+
locatorObj.type = 'xpath'
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
if (config.strategy.toLowerCase() === 'css') {
|
|
134
|
-
locatorObj.value = []
|
|
134
|
+
locatorObj.value = []
|
|
135
|
+
.concat(config.attribute)
|
|
135
136
|
.map((attr) => `[${attr}=${val}]`)
|
|
136
|
-
.join(',')
|
|
137
|
-
locatorObj.type = 'css'
|
|
137
|
+
.join(',')
|
|
138
|
+
locatorObj.type = 'css'
|
|
138
139
|
}
|
|
139
140
|
|
|
140
141
|
if (config.showActual) {
|
|
141
|
-
locatorObj.output = locatorObj.value
|
|
142
|
+
locatorObj.output = locatorObj.value
|
|
142
143
|
}
|
|
143
|
-
})
|
|
144
|
-
}
|
|
144
|
+
})
|
|
145
|
+
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
const Container = require('../container')
|
|
2
|
-
const recorder = require('../recorder')
|
|
3
|
-
const event = require('../event')
|
|
4
|
-
const supportedHelpers = require('./standardActingHelpers')
|
|
5
|
-
const { scanForErrorMessages } = require('../html')
|
|
6
|
-
const { output } = require('..')
|
|
1
|
+
const Container = require('../container')
|
|
2
|
+
const recorder = require('../recorder')
|
|
3
|
+
const event = require('../event')
|
|
4
|
+
const supportedHelpers = require('./standardActingHelpers')
|
|
5
|
+
const { scanForErrorMessages } = require('../html')
|
|
6
|
+
const { output } = require('..')
|
|
7
7
|
|
|
8
8
|
const defaultConfig = {
|
|
9
9
|
errorClasses: ['error', 'warning', 'alert', 'danger'],
|
|
10
|
-
}
|
|
10
|
+
}
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Prints errors found in HTML code after each failed test.
|
|
@@ -30,38 +30,38 @@ const defaultConfig = {
|
|
|
30
30
|
*
|
|
31
31
|
*/
|
|
32
32
|
module.exports = function (config = {}) {
|
|
33
|
-
const helpers = Container.helpers()
|
|
34
|
-
let helper
|
|
33
|
+
const helpers = Container.helpers()
|
|
34
|
+
let helper
|
|
35
35
|
|
|
36
|
-
config = Object.assign(defaultConfig, config)
|
|
36
|
+
config = Object.assign(defaultConfig, config)
|
|
37
37
|
|
|
38
38
|
for (const helperName of supportedHelpers) {
|
|
39
39
|
if (Object.keys(helpers).indexOf(helperName) > -1) {
|
|
40
|
-
helper = helpers[helperName]
|
|
40
|
+
helper = helpers[helperName]
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
if (!helper) return
|
|
44
|
+
if (!helper) return // no helpers for screenshot
|
|
45
45
|
|
|
46
46
|
event.dispatcher.on(event.test.failed, (test) => {
|
|
47
47
|
recorder.add('HTML snapshot failed test', async () => {
|
|
48
48
|
try {
|
|
49
|
-
const currentOutputLevel = output.level()
|
|
50
|
-
output.level(0)
|
|
51
|
-
const html = await helper.grabHTMLFrom('body')
|
|
52
|
-
output.level(currentOutputLevel)
|
|
49
|
+
const currentOutputLevel = output.level()
|
|
50
|
+
output.level(0)
|
|
51
|
+
const html = await helper.grabHTMLFrom('body')
|
|
52
|
+
output.level(currentOutputLevel)
|
|
53
53
|
|
|
54
|
-
if (!html) return
|
|
54
|
+
if (!html) return
|
|
55
55
|
|
|
56
|
-
const errors = scanForErrorMessages(html, config.errorClasses)
|
|
56
|
+
const errors = scanForErrorMessages(html, config.errorClasses)
|
|
57
57
|
if (errors.length) {
|
|
58
|
-
output.debug('Detected errors in HTML code')
|
|
59
|
-
errors.forEach((error) => output.debug(error))
|
|
60
|
-
test.artifacts.errors = errors
|
|
58
|
+
output.debug('Detected errors in HTML code')
|
|
59
|
+
errors.forEach((error) => output.debug(error))
|
|
60
|
+
test.artifacts.errors = errors
|
|
61
61
|
}
|
|
62
62
|
} catch (err) {
|
|
63
63
|
// not really needed
|
|
64
64
|
}
|
|
65
|
-
})
|
|
66
|
-
})
|
|
67
|
-
}
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
const output = require('../output')
|
|
2
|
-
const store = require('../store')
|
|
3
|
-
const recorder = require('../recorder')
|
|
4
|
-
const container = require('../container')
|
|
5
|
-
const event = require('../event')
|
|
6
|
-
const Step = require('../step')
|
|
7
|
-
const { isAsyncFunction } = require('../utils')
|
|
1
|
+
const output = require('../output')
|
|
2
|
+
const store = require('../store')
|
|
3
|
+
const recorder = require('../recorder')
|
|
4
|
+
const container = require('../container')
|
|
5
|
+
const event = require('../event')
|
|
6
|
+
const Step = require('../step')
|
|
7
|
+
const { isAsyncFunction } = require('../utils')
|
|
8
8
|
|
|
9
9
|
const defaultConfig = {
|
|
10
10
|
registerGlobal: true,
|
|
11
|
-
}
|
|
11
|
+
}
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Provides `eachElement` global function to iterate over found elements to perform actions on them.
|
|
@@ -69,59 +69,59 @@ const defaultConfig = {
|
|
|
69
69
|
* @return {Promise<*> | undefined}
|
|
70
70
|
*/
|
|
71
71
|
function eachElement(purpose, locator, fn) {
|
|
72
|
-
if (store.dryRun) return
|
|
73
|
-
const helpers = Object.values(container.helpers())
|
|
72
|
+
if (store.dryRun) return
|
|
73
|
+
const helpers = Object.values(container.helpers())
|
|
74
74
|
|
|
75
|
-
const helper = helpers.filter(h => !!h._locate)[0]
|
|
75
|
+
const helper = helpers.filter((h) => !!h._locate)[0]
|
|
76
76
|
|
|
77
77
|
if (!helper) {
|
|
78
|
-
throw new Error('No helper enabled with _locate method with returns a list of elements.')
|
|
78
|
+
throw new Error('No helper enabled with _locate method with returns a list of elements.')
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
if (!isAsyncFunction(fn)) {
|
|
82
|
-
throw new Error('Async function should be passed into each element')
|
|
82
|
+
throw new Error('Async function should be passed into each element')
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
-
const step = new Step(helper, `${purpose || 'each element'} within "${locator}"`)
|
|
86
|
-
step.helperMethod = '_locate'
|
|
85
|
+
const step = new Step(helper, `${purpose || 'each element'} within "${locator}"`)
|
|
86
|
+
step.helperMethod = '_locate'
|
|
87
87
|
// eachElement('select all users', 'locator', async (el) => {
|
|
88
|
-
event.dispatcher.emit(event.step.before, step)
|
|
88
|
+
event.dispatcher.emit(event.step.before, step)
|
|
89
89
|
|
|
90
90
|
return recorder.add('register each element wrapper', async () => {
|
|
91
|
-
event.emit(event.step.started, step)
|
|
92
|
-
const els = await helper._locate(locator)
|
|
93
|
-
output.debug(`Found ${els.length} elements for each elements to iterate`)
|
|
91
|
+
event.emit(event.step.started, step)
|
|
92
|
+
const els = await helper._locate(locator)
|
|
93
|
+
output.debug(`Found ${els.length} elements for each elements to iterate`)
|
|
94
94
|
|
|
95
|
-
const errs = []
|
|
96
|
-
let i = 0
|
|
95
|
+
const errs = []
|
|
96
|
+
let i = 0
|
|
97
97
|
for (const el of els) {
|
|
98
98
|
try {
|
|
99
|
-
await fn(el, i)
|
|
99
|
+
await fn(el, i)
|
|
100
100
|
} catch (err) {
|
|
101
|
-
output.error(`eachElement: failed operation on element #${i} ${el}`)
|
|
102
|
-
errs.push(err)
|
|
101
|
+
output.error(`eachElement: failed operation on element #${i} ${el}`)
|
|
102
|
+
errs.push(err)
|
|
103
103
|
}
|
|
104
|
-
i
|
|
104
|
+
i++
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
if (errs.length) {
|
|
108
|
-
event.dispatcher.emit(event.step.after, step)
|
|
109
|
-
event.emit(event.step.failed, step, errs[0])
|
|
110
|
-
event.emit(event.step.finished, step)
|
|
111
|
-
throw errs[0]
|
|
108
|
+
event.dispatcher.emit(event.step.after, step)
|
|
109
|
+
event.emit(event.step.failed, step, errs[0])
|
|
110
|
+
event.emit(event.step.finished, step)
|
|
111
|
+
throw errs[0]
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
event.dispatcher.emit(event.step.after, step)
|
|
115
|
-
event.emit(event.step.passed, step, null)
|
|
116
|
-
event.emit(event.step.finished, step)
|
|
117
|
-
})
|
|
114
|
+
event.dispatcher.emit(event.step.after, step)
|
|
115
|
+
event.emit(event.step.passed, step, null)
|
|
116
|
+
event.emit(event.step.finished, step)
|
|
117
|
+
})
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
module.exports = function (config) {
|
|
121
|
-
config = Object.assign(defaultConfig, config)
|
|
121
|
+
config = Object.assign(defaultConfig, config)
|
|
122
122
|
|
|
123
123
|
if (config.registerGlobal) {
|
|
124
|
-
global.eachElement = eachElement
|
|
124
|
+
global.eachElement = eachElement
|
|
125
125
|
}
|
|
126
|
-
return eachElement
|
|
127
|
-
}
|
|
126
|
+
return eachElement
|
|
127
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const { faker } = require('@faker-js/faker')
|
|
2
|
-
const transform = require('../transform')
|
|
1
|
+
const { faker } = require('@faker-js/faker')
|
|
2
|
+
const transform = require('../transform')
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Use the `@faker-js/faker` package to generate fake data inside examples on your gherkin tests
|
|
@@ -42,8 +42,8 @@ const transform = require('../transform');
|
|
|
42
42
|
module.exports = function (config) {
|
|
43
43
|
transform.addTransformerBeforeAll('gherkin.examples', (value) => {
|
|
44
44
|
if (typeof value === 'string' && value.length > 0) {
|
|
45
|
-
return faker.helpers.fake(value)
|
|
45
|
+
return faker.helpers.fake(value)
|
|
46
46
|
}
|
|
47
|
-
return value
|
|
48
|
-
})
|
|
49
|
-
}
|
|
47
|
+
return value
|
|
48
|
+
})
|
|
49
|
+
}
|
package/lib/plugin/heal.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
const debug = require('debug')('codeceptjs:heal')
|
|
2
|
-
const colors = require('chalk')
|
|
3
|
-
const recorder = require('../recorder')
|
|
4
|
-
const event = require('../event')
|
|
5
|
-
const output = require('../output')
|
|
6
|
-
const heal = require('../heal')
|
|
7
|
-
const store = require('../store')
|
|
1
|
+
const debug = require('debug')('codeceptjs:heal')
|
|
2
|
+
const colors = require('chalk')
|
|
3
|
+
const recorder = require('../recorder')
|
|
4
|
+
const event = require('../event')
|
|
5
|
+
const output = require('../output')
|
|
6
|
+
const heal = require('../heal')
|
|
7
|
+
const store = require('../store')
|
|
8
8
|
|
|
9
9
|
const defaultConfig = {
|
|
10
10
|
healLimit: 2,
|
|
11
|
-
}
|
|
11
|
+
}
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Self-healing tests with AI.
|
|
@@ -31,88 +31,91 @@ const defaultConfig = {
|
|
|
31
31
|
module.exports = function (config = {}) {
|
|
32
32
|
if (store.debugMode && !process.env.DEBUG) {
|
|
33
33
|
event.dispatcher.on(event.test.failed, () => {
|
|
34
|
-
output.plugin(
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
output.plugin(
|
|
35
|
+
'heal',
|
|
36
|
+
'Healing is disabled in --debug mode, use DEBUG="codeceptjs:heal" to enable it in debug mode',
|
|
37
|
+
)
|
|
38
|
+
})
|
|
39
|
+
return
|
|
37
40
|
}
|
|
38
41
|
|
|
39
|
-
let currentTest = null
|
|
40
|
-
let currentStep = null
|
|
41
|
-
let healedSteps = 0
|
|
42
|
-
let caughtError
|
|
43
|
-
let healTries = 0
|
|
44
|
-
let isHealing = false
|
|
42
|
+
let currentTest = null
|
|
43
|
+
let currentStep = null
|
|
44
|
+
let healedSteps = 0
|
|
45
|
+
let caughtError
|
|
46
|
+
let healTries = 0
|
|
47
|
+
let isHealing = false
|
|
45
48
|
|
|
46
|
-
config = Object.assign(defaultConfig, config)
|
|
49
|
+
config = Object.assign(defaultConfig, config)
|
|
47
50
|
|
|
48
51
|
event.dispatcher.on(event.test.before, (test) => {
|
|
49
|
-
currentTest = test
|
|
50
|
-
healedSteps = 0
|
|
51
|
-
caughtError = null
|
|
52
|
-
})
|
|
52
|
+
currentTest = test
|
|
53
|
+
healedSteps = 0
|
|
54
|
+
caughtError = null
|
|
55
|
+
})
|
|
53
56
|
|
|
54
|
-
event.dispatcher.on(event.step.started, step => currentStep = step)
|
|
57
|
+
event.dispatcher.on(event.step.started, (step) => (currentStep = step))
|
|
55
58
|
|
|
56
59
|
event.dispatcher.on(event.step.after, (step) => {
|
|
57
|
-
if (isHealing) return
|
|
58
|
-
if (healTries >= config.healLimit) return
|
|
60
|
+
if (isHealing) return
|
|
61
|
+
if (healTries >= config.healLimit) return // out of limit
|
|
59
62
|
|
|
60
|
-
if (!heal.hasCorrespondingRecipes(step)) return
|
|
63
|
+
if (!heal.hasCorrespondingRecipes(step)) return
|
|
61
64
|
|
|
62
65
|
recorder.catchWithoutStop(async (err) => {
|
|
63
|
-
isHealing = true
|
|
64
|
-
if (caughtError === err) throw err
|
|
65
|
-
caughtError = err
|
|
66
|
+
isHealing = true
|
|
67
|
+
if (caughtError === err) throw err // avoid double handling
|
|
68
|
+
caughtError = err
|
|
66
69
|
|
|
67
|
-
const test = currentTest
|
|
70
|
+
const test = currentTest
|
|
68
71
|
|
|
69
|
-
recorder.session.start('heal')
|
|
72
|
+
recorder.session.start('heal')
|
|
70
73
|
|
|
71
|
-
debug('Self-healing started', step.toCode())
|
|
74
|
+
debug('Self-healing started', step.toCode())
|
|
72
75
|
|
|
73
|
-
await heal.healStep(step, err, { test })
|
|
76
|
+
await heal.healStep(step, err, { test })
|
|
74
77
|
|
|
75
|
-
healTries
|
|
78
|
+
healTries++
|
|
76
79
|
|
|
77
80
|
recorder.add('close healing session', () => {
|
|
78
|
-
recorder.reset()
|
|
79
|
-
recorder.session.restore('heal')
|
|
80
|
-
recorder.ignoreErr(err)
|
|
81
|
-
})
|
|
82
|
-
await recorder.promise()
|
|
81
|
+
recorder.reset()
|
|
82
|
+
recorder.session.restore('heal')
|
|
83
|
+
recorder.ignoreErr(err)
|
|
84
|
+
})
|
|
85
|
+
await recorder.promise()
|
|
83
86
|
|
|
84
|
-
isHealing = false
|
|
85
|
-
})
|
|
86
|
-
})
|
|
87
|
+
isHealing = false
|
|
88
|
+
})
|
|
89
|
+
})
|
|
87
90
|
|
|
88
91
|
event.dispatcher.on(event.all.result, () => {
|
|
89
|
-
if (!heal.fixes?.length) return
|
|
92
|
+
if (!heal.fixes?.length) return
|
|
90
93
|
|
|
91
|
-
const { print } = output
|
|
94
|
+
const { print } = output
|
|
92
95
|
|
|
93
|
-
print('')
|
|
94
|
-
print('===================')
|
|
95
|
-
print(colors.bold.green('Self-Healing Report:'))
|
|
96
|
+
print('')
|
|
97
|
+
print('===================')
|
|
98
|
+
print(colors.bold.green('Self-Healing Report:'))
|
|
96
99
|
|
|
97
|
-
print(`${colors.bold(heal.fixes.length)} ${heal.fixes.length === 1 ? 'step was' : 'steps were'} healed`)
|
|
100
|
+
print(`${colors.bold(heal.fixes.length)} ${heal.fixes.length === 1 ? 'step was' : 'steps were'} healed`)
|
|
98
101
|
|
|
99
|
-
const suggestions = heal.fixes.filter(fix => fix.recipe && heal.recipes[fix.recipe].suggest)
|
|
102
|
+
const suggestions = heal.fixes.filter((fix) => fix.recipe && heal.recipes[fix.recipe].suggest)
|
|
100
103
|
|
|
101
|
-
if (!suggestions.length) return
|
|
104
|
+
if (!suggestions.length) return
|
|
102
105
|
|
|
103
|
-
let i = 1
|
|
104
|
-
print('')
|
|
105
|
-
print('Suggested changes:')
|
|
106
|
-
print('')
|
|
106
|
+
let i = 1
|
|
107
|
+
print('')
|
|
108
|
+
print('Suggested changes:')
|
|
109
|
+
print('')
|
|
107
110
|
|
|
108
111
|
for (const suggestion of suggestions) {
|
|
109
|
-
print(`${i}. To fix ${colors.bold.magenta(suggestion.test?.title)}`)
|
|
110
|
-
print(' Replace the failed code:', colors.gray(`(suggested by ${colors.bold(suggestion.recipe)})`))
|
|
111
|
-
print(colors.red(`- ${suggestion.step.toCode()}`))
|
|
112
|
-
print(colors.green(`+ ${suggestion.snippet}`))
|
|
113
|
-
print(suggestion.step.line())
|
|
114
|
-
print('')
|
|
115
|
-
i
|
|
112
|
+
print(`${i}. To fix ${colors.bold.magenta(suggestion.test?.title)}`)
|
|
113
|
+
print(' Replace the failed code:', colors.gray(`(suggested by ${colors.bold(suggestion.recipe)})`))
|
|
114
|
+
print(colors.red(`- ${suggestion.step.toCode()}`))
|
|
115
|
+
print(colors.green(`+ ${suggestion.snippet}`))
|
|
116
|
+
print(suggestion.step.line())
|
|
117
|
+
print('')
|
|
118
|
+
i++
|
|
116
119
|
}
|
|
117
|
-
})
|
|
118
|
-
}
|
|
120
|
+
})
|
|
121
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const event = require('../event')
|
|
2
|
-
const pause = require('../pause')
|
|
1
|
+
const event = require('../event')
|
|
2
|
+
const pause = require('../pause')
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Automatically launches [interactive pause](/basics/#pause) when a test fails.
|
|
@@ -22,17 +22,17 @@ const pause = require('../pause');
|
|
|
22
22
|
*
|
|
23
23
|
*/
|
|
24
24
|
module.exports = () => {
|
|
25
|
-
let failed = false
|
|
25
|
+
let failed = false
|
|
26
26
|
|
|
27
27
|
event.dispatcher.on(event.test.started, () => {
|
|
28
|
-
failed = false
|
|
29
|
-
})
|
|
28
|
+
failed = false
|
|
29
|
+
})
|
|
30
30
|
|
|
31
31
|
event.dispatcher.on(event.step.failed, () => {
|
|
32
|
-
failed = true
|
|
33
|
-
})
|
|
32
|
+
failed = true
|
|
33
|
+
})
|
|
34
34
|
|
|
35
35
|
event.dispatcher.on(event.test.after, () => {
|
|
36
|
-
if (failed) pause()
|
|
37
|
-
})
|
|
38
|
-
}
|
|
36
|
+
if (failed) pause()
|
|
37
|
+
})
|
|
38
|
+
}
|
|
@@ -1,21 +1,14 @@
|
|
|
1
|
-
const event = require('../event')
|
|
2
|
-
const recorder = require('../recorder')
|
|
3
|
-
const container = require('../container')
|
|
4
|
-
const { log } = require('../output')
|
|
1
|
+
const event = require('../event')
|
|
2
|
+
const recorder = require('../recorder')
|
|
3
|
+
const container = require('../container')
|
|
4
|
+
const { log } = require('../output')
|
|
5
5
|
|
|
6
6
|
const defaultConfig = {
|
|
7
7
|
retries: 3,
|
|
8
|
-
defaultIgnoredSteps: [
|
|
9
|
-
'amOnPage',
|
|
10
|
-
'wait*',
|
|
11
|
-
'send*',
|
|
12
|
-
'execute*',
|
|
13
|
-
'run*',
|
|
14
|
-
'have*',
|
|
15
|
-
],
|
|
8
|
+
defaultIgnoredSteps: ['amOnPage', 'wait*', 'send*', 'execute*', 'run*', 'have*'],
|
|
16
9
|
factor: 1.5,
|
|
17
10
|
ignoredSteps: [],
|
|
18
|
-
}
|
|
11
|
+
}
|
|
19
12
|
|
|
20
13
|
/**
|
|
21
14
|
* Retries each failed step in a test.
|
|
@@ -84,45 +77,45 @@ const defaultConfig = {
|
|
|
84
77
|
*
|
|
85
78
|
*/
|
|
86
79
|
module.exports = (config) => {
|
|
87
|
-
config = Object.assign(defaultConfig, config)
|
|
88
|
-
config.ignoredSteps = config.ignoredSteps.concat(config.defaultIgnoredSteps)
|
|
89
|
-
const customWhen = config.when
|
|
80
|
+
config = Object.assign(defaultConfig, config)
|
|
81
|
+
config.ignoredSteps = config.ignoredSteps.concat(config.defaultIgnoredSteps)
|
|
82
|
+
const customWhen = config.when
|
|
90
83
|
|
|
91
|
-
let enableRetry = false
|
|
84
|
+
let enableRetry = false
|
|
92
85
|
|
|
93
86
|
const when = (err) => {
|
|
94
|
-
if (!enableRetry) return
|
|
95
|
-
const store = require('../store')
|
|
96
|
-
if (store.debugMode) return false
|
|
97
|
-
if (customWhen) return customWhen(err)
|
|
98
|
-
return true
|
|
99
|
-
}
|
|
100
|
-
config.when = when
|
|
87
|
+
if (!enableRetry) return
|
|
88
|
+
const store = require('../store')
|
|
89
|
+
if (store.debugMode) return false
|
|
90
|
+
if (customWhen) return customWhen(err)
|
|
91
|
+
return true
|
|
92
|
+
}
|
|
93
|
+
config.when = when
|
|
101
94
|
|
|
102
95
|
event.dispatcher.on(event.step.started, (step) => {
|
|
103
96
|
if (process.env.TRY_TO === 'true') {
|
|
104
|
-
log('Info: RetryFailedStep plugin is disabled inside tryTo block')
|
|
105
|
-
return
|
|
97
|
+
log('Info: RetryFailedStep plugin is disabled inside tryTo block')
|
|
98
|
+
return
|
|
106
99
|
}
|
|
107
100
|
|
|
108
101
|
// if a step is ignored - return
|
|
109
102
|
for (const ignored of config.ignoredSteps) {
|
|
110
|
-
if (step.name === ignored) return
|
|
103
|
+
if (step.name === ignored) return
|
|
111
104
|
if (ignored instanceof RegExp) {
|
|
112
|
-
if (step.name.match(ignored)) return
|
|
113
|
-
} else if (ignored.indexOf('*') && step.name.startsWith(ignored.slice(0, -1))) return
|
|
105
|
+
if (step.name.match(ignored)) return
|
|
106
|
+
} else if (ignored.indexOf('*') && step.name.startsWith(ignored.slice(0, -1))) return
|
|
114
107
|
}
|
|
115
|
-
enableRetry = true
|
|
116
|
-
})
|
|
108
|
+
enableRetry = true // enable retry for a step
|
|
109
|
+
})
|
|
117
110
|
|
|
118
111
|
event.dispatcher.on(event.step.finished, () => {
|
|
119
|
-
enableRetry = false
|
|
120
|
-
})
|
|
112
|
+
enableRetry = false
|
|
113
|
+
})
|
|
121
114
|
|
|
122
115
|
event.dispatcher.on(event.test.before, (test) => {
|
|
123
|
-
if (test && test.disableRetryFailedStep) return
|
|
116
|
+
if (test && test.disableRetryFailedStep) return // disable retry when a test is not active
|
|
124
117
|
// this env var is used to set the retries inside _before() block of helpers
|
|
125
|
-
process.env.FAILED_STEP_RETRIES = config.retries
|
|
126
|
-
recorder.retry(config)
|
|
127
|
-
})
|
|
128
|
-
}
|
|
118
|
+
process.env.FAILED_STEP_RETRIES = config.retries
|
|
119
|
+
recorder.retry(config)
|
|
120
|
+
})
|
|
121
|
+
}
|
package/lib/plugin/retryTo.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
const recorder = require('../recorder')
|
|
2
|
-
const { debug } = require('../output')
|
|
1
|
+
const recorder = require('../recorder')
|
|
2
|
+
const { debug } = require('../output')
|
|
3
3
|
|
|
4
4
|
const defaultConfig = {
|
|
5
5
|
registerGlobal: true,
|
|
6
6
|
pollInterval: 200,
|
|
7
|
-
}
|
|
7
|
+
}
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
*
|
|
@@ -74,54 +74,54 @@ const defaultConfig = {
|
|
|
74
74
|
*
|
|
75
75
|
*/
|
|
76
76
|
module.exports = function (config) {
|
|
77
|
-
config = Object.assign(defaultConfig, config)
|
|
77
|
+
config = Object.assign(defaultConfig, config)
|
|
78
78
|
function retryTo(callback, maxTries, pollInterval = config.pollInterval) {
|
|
79
79
|
return new Promise((done, reject) => {
|
|
80
|
-
let tries = 1
|
|
80
|
+
let tries = 1
|
|
81
81
|
|
|
82
82
|
function handleRetryException(err) {
|
|
83
|
-
recorder.throw(err)
|
|
84
|
-
reject(err)
|
|
83
|
+
recorder.throw(err)
|
|
84
|
+
reject(err)
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
const tryBlock = async () => {
|
|
88
|
-
tries
|
|
89
|
-
recorder.session.start(`retryTo ${tries}`)
|
|
88
|
+
tries++
|
|
89
|
+
recorder.session.start(`retryTo ${tries}`)
|
|
90
90
|
try {
|
|
91
|
-
await callback(tries)
|
|
91
|
+
await callback(tries)
|
|
92
92
|
} catch (err) {
|
|
93
|
-
handleRetryException(err)
|
|
93
|
+
handleRetryException(err)
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
// Call done if no errors
|
|
97
97
|
recorder.add(() => {
|
|
98
|
-
recorder.session.restore(`retryTo ${tries}`)
|
|
99
|
-
done(null)
|
|
100
|
-
})
|
|
98
|
+
recorder.session.restore(`retryTo ${tries}`)
|
|
99
|
+
done(null)
|
|
100
|
+
})
|
|
101
101
|
|
|
102
102
|
// Catch errors and retry
|
|
103
103
|
recorder.session.catch((err) => {
|
|
104
|
-
recorder.session.restore(`retryTo ${tries}`)
|
|
104
|
+
recorder.session.restore(`retryTo ${tries}`)
|
|
105
105
|
if (tries <= maxTries) {
|
|
106
|
-
debug(`Error ${err}... Retrying`)
|
|
107
|
-
recorder.add(`retryTo ${tries}`, () => setTimeout(tryBlock, pollInterval))
|
|
106
|
+
debug(`Error ${err}... Retrying`)
|
|
107
|
+
recorder.add(`retryTo ${tries}`, () => setTimeout(tryBlock, pollInterval))
|
|
108
108
|
} else {
|
|
109
109
|
// if maxTries reached
|
|
110
|
-
handleRetryException(err)
|
|
110
|
+
handleRetryException(err)
|
|
111
111
|
}
|
|
112
|
-
})
|
|
113
|
-
}
|
|
112
|
+
})
|
|
113
|
+
}
|
|
114
114
|
|
|
115
|
-
recorder.add('retryTo', tryBlock).catch(err => {
|
|
116
|
-
console.error('An error occurred:', err)
|
|
117
|
-
done(null)
|
|
118
|
-
})
|
|
119
|
-
})
|
|
115
|
+
recorder.add('retryTo', tryBlock).catch((err) => {
|
|
116
|
+
console.error('An error occurred:', err)
|
|
117
|
+
done(null)
|
|
118
|
+
})
|
|
119
|
+
})
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
if (config.registerGlobal) {
|
|
123
|
-
global.retryTo = retryTo
|
|
123
|
+
global.retryTo = retryTo
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
-
return retryTo
|
|
127
|
-
}
|
|
126
|
+
return retryTo
|
|
127
|
+
}
|