codeceptjs 4.0.0-beta.1 → 4.0.0-beta.11.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 +133 -120
- package/bin/codecept.js +107 -96
- package/bin/test-server.js +64 -0
- package/docs/webapi/clearCookie.mustache +1 -1
- package/docs/webapi/click.mustache +5 -1
- package/lib/actor.js +71 -103
- package/lib/ai.js +159 -188
- package/lib/assert/empty.js +22 -24
- package/lib/assert/equal.js +30 -37
- package/lib/assert/error.js +14 -14
- package/lib/assert/include.js +43 -48
- package/lib/assert/throws.js +11 -11
- package/lib/assert/truth.js +22 -22
- package/lib/assert.js +20 -18
- package/lib/codecept.js +238 -162
- package/lib/colorUtils.js +50 -52
- package/lib/command/check.js +206 -0
- package/lib/command/configMigrate.js +56 -51
- package/lib/command/definitions.js +96 -109
- package/lib/command/dryRun.js +77 -79
- package/lib/command/generate.js +234 -194
- package/lib/command/gherkin/init.js +42 -33
- package/lib/command/gherkin/snippets.js +76 -74
- package/lib/command/gherkin/steps.js +20 -17
- package/lib/command/info.js +74 -38
- package/lib/command/init.js +300 -290
- package/lib/command/interactive.js +41 -32
- package/lib/command/list.js +28 -27
- package/lib/command/run-multiple/chunk.js +51 -48
- package/lib/command/run-multiple/collection.js +5 -5
- package/lib/command/run-multiple/run.js +5 -1
- package/lib/command/run-multiple.js +97 -97
- package/lib/command/run-rerun.js +19 -25
- package/lib/command/run-workers.js +68 -92
- package/lib/command/run.js +39 -27
- package/lib/command/utils.js +80 -64
- package/lib/command/workers/runTests.js +388 -226
- package/lib/config.js +124 -50
- package/lib/container.js +765 -260
- package/lib/data/context.js +60 -61
- package/lib/data/dataScenarioConfig.js +47 -47
- package/lib/data/dataTableArgument.js +32 -32
- package/lib/data/table.js +22 -22
- package/lib/effects.js +307 -0
- package/lib/element/WebElement.js +327 -0
- package/lib/els.js +160 -0
- package/lib/event.js +173 -163
- package/lib/globals.js +141 -0
- package/lib/heal.js +89 -85
- package/lib/helper/AI.js +131 -41
- package/lib/helper/ApiDataFactory.js +107 -75
- package/lib/helper/Appium.js +542 -404
- package/lib/helper/FileSystem.js +100 -79
- package/lib/helper/GraphQL.js +44 -43
- package/lib/helper/GraphQLDataFactory.js +52 -52
- package/lib/helper/JSONResponse.js +126 -88
- package/lib/helper/Mochawesome.js +54 -29
- package/lib/helper/Playwright.js +2547 -1316
- package/lib/helper/Puppeteer.js +1578 -1181
- package/lib/helper/REST.js +209 -68
- package/lib/helper/WebDriver.js +1482 -1342
- 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/PlaywrightReactVueLocator.js +17 -8
- package/lib/helper/extras/PlaywrightRestartOpts.js +25 -11
- package/lib/helper/extras/Popup.js +22 -22
- package/lib/helper/extras/React.js +27 -28
- package/lib/helper/network/actions.js +36 -42
- package/lib/helper/network/utils.js +78 -84
- package/lib/helper/scripts/blurElement.js +5 -5
- package/lib/helper/scripts/focusElement.js +5 -5
- package/lib/helper/scripts/highlightElement.js +8 -8
- package/lib/helper/scripts/isElementClickable.js +34 -34
- package/lib/helper.js +2 -3
- package/lib/history.js +23 -19
- package/lib/hooks.js +8 -8
- package/lib/html.js +94 -104
- package/lib/index.js +38 -27
- package/lib/listener/config.js +30 -23
- package/lib/listener/emptyRun.js +54 -0
- package/lib/listener/enhancedGlobalRetry.js +110 -0
- package/lib/listener/exit.js +16 -18
- package/lib/listener/globalRetry.js +70 -0
- package/lib/listener/globalTimeout.js +181 -0
- package/lib/listener/helpers.js +76 -51
- package/lib/listener/mocha.js +10 -11
- package/lib/listener/result.js +11 -0
- package/lib/listener/retryEnhancer.js +85 -0
- package/lib/listener/steps.js +71 -59
- package/lib/listener/store.js +20 -0
- package/lib/locator.js +214 -197
- package/lib/mocha/asyncWrapper.js +274 -0
- package/lib/mocha/bdd.js +167 -0
- package/lib/mocha/cli.js +341 -0
- package/lib/mocha/factory.js +163 -0
- package/lib/mocha/featureConfig.js +89 -0
- package/lib/mocha/gherkin.js +231 -0
- 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 +58 -34
- package/lib/mocha/suite.js +89 -0
- package/lib/mocha/test.js +184 -0
- package/lib/mocha/types.d.ts +42 -0
- package/lib/mocha/ui.js +242 -0
- package/lib/output.js +141 -71
- package/lib/parser.js +47 -44
- package/lib/pause.js +173 -145
- package/lib/plugin/analyze.js +403 -0
- package/lib/plugin/{autoLogin.js → auth.js} +178 -79
- package/lib/plugin/autoDelay.js +36 -40
- package/lib/plugin/coverage.js +131 -78
- package/lib/plugin/customLocator.js +22 -21
- package/lib/plugin/customReporter.js +53 -0
- package/lib/plugin/enhancedRetryFailedStep.js +99 -0
- package/lib/plugin/heal.js +101 -110
- package/lib/plugin/htmlReporter.js +3648 -0
- package/lib/plugin/pageInfo.js +140 -0
- package/lib/plugin/pauseOnFail.js +12 -11
- package/lib/plugin/retryFailedStep.js +82 -47
- package/lib/plugin/screenshotOnFail.js +111 -92
- package/lib/plugin/stepByStepReport.js +159 -101
- package/lib/plugin/stepTimeout.js +20 -25
- package/lib/plugin/subtitles.js +38 -38
- package/lib/recorder.js +193 -130
- package/lib/rerun.js +94 -49
- package/lib/result.js +238 -0
- package/lib/retryCoordinator.js +207 -0
- package/lib/secret.js +20 -18
- 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 -329
- package/lib/steps.js +54 -0
- package/lib/store.js +38 -7
- package/lib/template/heal.js +3 -12
- package/lib/template/prompts/generatePageObject.js +31 -0
- package/lib/template/prompts/healStep.js +13 -0
- package/lib/template/prompts/writeStep.js +9 -0
- package/lib/test-server.js +334 -0
- package/lib/timeout.js +60 -0
- package/lib/transform.js +8 -8
- package/lib/translation.js +34 -21
- package/lib/utils/mask_data.js +47 -0
- package/lib/utils.js +411 -228
- package/lib/workerStorage.js +37 -34
- package/lib/workers.js +532 -296
- package/package.json +115 -95
- package/translations/de-DE.js +5 -3
- package/translations/fr-FR.js +5 -4
- package/translations/index.js +22 -12
- package/translations/it-IT.js +4 -3
- package/translations/ja-JP.js +4 -3
- package/translations/nl-NL.js +76 -0
- package/translations/pl-PL.js +4 -3
- package/translations/pt-BR.js +4 -3
- package/translations/ru-RU.js +4 -3
- package/translations/utils.js +10 -0
- package/translations/zh-CN.js +4 -3
- package/translations/zh-TW.js +4 -3
- package/typings/index.d.ts +546 -185
- package/typings/promiseBasedTypes.d.ts +150 -879
- package/typings/types.d.ts +547 -996
- package/lib/cli.js +0 -249
- package/lib/dirname.js +0 -5
- package/lib/helper/Expect.js +0 -425
- package/lib/helper/ExpectHelper.js +0 -399
- package/lib/helper/MockServer.js +0 -223
- package/lib/helper/Nightmare.js +0 -1411
- package/lib/helper/Protractor.js +0 -1835
- package/lib/helper/SoftExpectHelper.js +0 -381
- package/lib/helper/TestCafe.js +0 -1410
- package/lib/helper/clientscripts/nightmare.js +0 -213
- package/lib/helper/testcafe/testControllerHolder.js +0 -42
- package/lib/helper/testcafe/testcafe-utils.js +0 -63
- package/lib/interfaces/bdd.js +0 -98
- package/lib/interfaces/featureConfig.js +0 -69
- package/lib/interfaces/gherkin.js +0 -195
- package/lib/listener/artifacts.js +0 -19
- package/lib/listener/retry.js +0 -68
- package/lib/listener/timeout.js +0 -109
- package/lib/mochaFactory.js +0 -110
- 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 -121
- package/lib/plugin/selenoid.js +0 -371
- package/lib/plugin/standardActingHelpers.js +0 -9
- package/lib/plugin/tryTo.js +0 -105
- package/lib/plugin/wdio.js +0 -246
- package/lib/scenario.js +0 -222
- package/lib/ui.js +0 -238
- package/lib/within.js +0 -70
|
@@ -0,0 +1,110 @@
|
|
|
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 }
|
package/lib/listener/exit.js
CHANGED
|
@@ -1,30 +1,28 @@
|
|
|
1
|
-
import
|
|
1
|
+
import event from '../event.js'
|
|
2
|
+
import debugModule from 'debug'
|
|
3
|
+
const debug = debugModule('codeceptjs:exit')
|
|
2
4
|
|
|
3
5
|
export default function () {
|
|
4
|
-
let failedTests = []
|
|
6
|
+
let failedTests = []
|
|
5
7
|
|
|
6
|
-
event.dispatcher.on(event.test.failed,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
failedTests.push(id);
|
|
11
|
-
});
|
|
8
|
+
event.dispatcher.on(event.test.failed, test => {
|
|
9
|
+
const id = test.uid || (test.ctx && test.ctx.test.uid) || 'empty'
|
|
10
|
+
failedTests.push(id)
|
|
11
|
+
})
|
|
12
12
|
|
|
13
13
|
// if test was successful after retries
|
|
14
|
-
event.dispatcher.on(event.test.passed,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
failedTests = failedTests.filter(failed => id !== failed);
|
|
19
|
-
});
|
|
14
|
+
event.dispatcher.on(event.test.passed, test => {
|
|
15
|
+
const id = test.uid || (test.ctx && test.ctx.test.uid) || 'empty'
|
|
16
|
+
failedTests = failedTests.filter(failed => id !== failed)
|
|
17
|
+
})
|
|
20
18
|
|
|
21
|
-
process.on('beforeExit',
|
|
19
|
+
process.on('beforeExit', code => {
|
|
22
20
|
if (failedTests.length) {
|
|
23
|
-
code = 1
|
|
21
|
+
code = 1
|
|
24
22
|
}
|
|
25
23
|
|
|
26
24
|
if (code) {
|
|
27
|
-
process.exit(code)
|
|
25
|
+
process.exit(code)
|
|
28
26
|
}
|
|
29
|
-
})
|
|
27
|
+
})
|
|
30
28
|
}
|
|
@@ -0,0 +1,70 @@
|
|
|
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
|
+
export default function () {
|
|
9
|
+
event.dispatcher.on(event.suite.before, suite => {
|
|
10
|
+
let retryConfig = Config.get('retry')
|
|
11
|
+
if (!retryConfig) return
|
|
12
|
+
|
|
13
|
+
if (Number.isInteger(+retryConfig)) {
|
|
14
|
+
// is number
|
|
15
|
+
const retryNum = +retryConfig
|
|
16
|
+
output.log(`Retries: ${retryNum}`)
|
|
17
|
+
suite.retries(retryNum)
|
|
18
|
+
return
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!Array.isArray(retryConfig)) {
|
|
22
|
+
retryConfig = [retryConfig]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
for (const config of retryConfig) {
|
|
26
|
+
if (config.grep) {
|
|
27
|
+
if (!suite.title.includes(config.grep)) continue
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
hooks
|
|
31
|
+
.filter(hook => !!config[hook])
|
|
32
|
+
.forEach(hook => {
|
|
33
|
+
if (isNotSet(suite.opts[`retry${hook}`])) suite.opts[`retry${hook}`] = config[hook]
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
if (config.Feature) {
|
|
37
|
+
if (isNotSet(suite.retries())) suite.retries(config.Feature)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
output.log(`Retries: ${JSON.stringify(config)}`)
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
event.dispatcher.on(event.test.before, test => {
|
|
45
|
+
let retryConfig = Config.get('retry')
|
|
46
|
+
if (!retryConfig) return
|
|
47
|
+
|
|
48
|
+
if (Number.isInteger(+retryConfig)) {
|
|
49
|
+
if (test.retries() === -1) test.retries(retryConfig)
|
|
50
|
+
return
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!Array.isArray(retryConfig)) {
|
|
54
|
+
retryConfig = [retryConfig]
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
retryConfig = retryConfig.filter(config => !!config.Scenario)
|
|
58
|
+
|
|
59
|
+
for (const config of retryConfig) {
|
|
60
|
+
if (config.grep) {
|
|
61
|
+
if (!test.fullTitle().includes(config.grep)) continue
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (config.Scenario) {
|
|
65
|
+
if (test.retries() === -1) test.retries(config.Scenario)
|
|
66
|
+
output.log(`Retries: ${config.Scenario}`)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import event from '../event.js'
|
|
2
|
+
import output from '../output.js'
|
|
3
|
+
import recorder from '../recorder.js'
|
|
4
|
+
import Config from '../config.js'
|
|
5
|
+
import store from '../store.js'
|
|
6
|
+
import debugModule from 'debug'
|
|
7
|
+
const debug = debugModule('codeceptjs:timeout')
|
|
8
|
+
import { TIMEOUT_ORDER, TimeoutError, TestTimeoutError, StepTimeoutError } from '../timeout.js'
|
|
9
|
+
import { BeforeSuiteHook, AfterSuiteHook } from '../mocha/hooks.js'
|
|
10
|
+
|
|
11
|
+
export default function () {
|
|
12
|
+
let timeout
|
|
13
|
+
let suiteTimeout = []
|
|
14
|
+
let currentTest
|
|
15
|
+
let currentTimeout
|
|
16
|
+
|
|
17
|
+
if (!store.timeouts) {
|
|
18
|
+
console.log('Timeouts were disabled')
|
|
19
|
+
return
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// disable timeout for BeforeSuite/AfterSuite hooks
|
|
23
|
+
// add separate configs to them?
|
|
24
|
+
// When a BeforeSuite/AfterSuite hook starts we want to disable the
|
|
25
|
+
// per-test timeout during that hook execution only. Previously the
|
|
26
|
+
// code cleared `suiteTimeout` permanently which caused the suite
|
|
27
|
+
// level timeout to be lost for subsequent tests. Save previous
|
|
28
|
+
// values and restore them when the hook finishes.
|
|
29
|
+
let __prevTimeout = undefined
|
|
30
|
+
let __prevSuiteTimeout = undefined
|
|
31
|
+
|
|
32
|
+
event.dispatcher.on(event.hook.started, hook => {
|
|
33
|
+
if (hook instanceof BeforeSuiteHook || hook instanceof AfterSuiteHook) {
|
|
34
|
+
__prevTimeout = timeout
|
|
35
|
+
// copy array to preserve original values
|
|
36
|
+
__prevSuiteTimeout = suiteTimeout.slice()
|
|
37
|
+
timeout = null
|
|
38
|
+
suiteTimeout = []
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
event.dispatcher.on(event.hook.finished, hook => {
|
|
43
|
+
if (hook instanceof BeforeSuiteHook || hook instanceof AfterSuiteHook) {
|
|
44
|
+
// restore previously stored values
|
|
45
|
+
timeout = __prevTimeout
|
|
46
|
+
suiteTimeout = __prevSuiteTimeout.slice()
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
event.dispatcher.on(event.suite.before, suite => {
|
|
51
|
+
suiteTimeout = []
|
|
52
|
+
let timeoutConfig = Config.get('timeout')
|
|
53
|
+
|
|
54
|
+
if (timeoutConfig) {
|
|
55
|
+
debug('config:', timeoutConfig)
|
|
56
|
+
if (!Number.isNaN(+timeoutConfig)) {
|
|
57
|
+
checkForSeconds(timeoutConfig)
|
|
58
|
+
suiteTimeout.push(timeoutConfig)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!Array.isArray(timeoutConfig)) {
|
|
62
|
+
timeoutConfig = [timeoutConfig]
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
for (const config of timeoutConfig.filter(c => !!c.Feature)) {
|
|
66
|
+
if (config.grep) {
|
|
67
|
+
if (!suite.title.includes(config.grep)) continue
|
|
68
|
+
}
|
|
69
|
+
suiteTimeout.push(config.Feature)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (suite.totalTimeout) suiteTimeout.push(suite.totalTimeout)
|
|
74
|
+
output.log(`Timeouts: ${suiteTimeout}`)
|
|
75
|
+
|
|
76
|
+
if (suiteTimeout.length > 0) debug(suite.title, 'timeout', suiteTimeout)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
event.dispatcher.on(event.test.before, test => {
|
|
80
|
+
currentTest = test
|
|
81
|
+
let testTimeout = null
|
|
82
|
+
|
|
83
|
+
let timeoutConfig = Config.get('timeout')
|
|
84
|
+
|
|
85
|
+
if (typeof timeoutConfig === 'object' || Array.isArray(timeoutConfig)) {
|
|
86
|
+
if (!Array.isArray(timeoutConfig)) {
|
|
87
|
+
timeoutConfig = [timeoutConfig]
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
for (const config of timeoutConfig.filter(c => !!c.Scenario)) {
|
|
91
|
+
console.log('Test Timeout', config, test.title.includes(config.grep))
|
|
92
|
+
if (config.grep) {
|
|
93
|
+
if (!test.title.includes(config.grep)) continue
|
|
94
|
+
}
|
|
95
|
+
testTimeout = config.Scenario
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
timeout = test.totalTimeout || testTimeout || suiteTimeout[suiteTimeout.length - 1]
|
|
100
|
+
if (!timeout) return
|
|
101
|
+
|
|
102
|
+
debug(test.title, 'timeout', {
|
|
103
|
+
'config from file': testTimeout,
|
|
104
|
+
'suite timeout': suiteTimeout,
|
|
105
|
+
'dynamic config': test.totalTimeout,
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
currentTimeout = timeout
|
|
109
|
+
output.debug(`Test Timeout: ${timeout}s`)
|
|
110
|
+
timeout *= 1000
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
event.dispatcher.on(event.test.passed, test => {
|
|
114
|
+
currentTest = null
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
event.dispatcher.on(event.test.failed, test => {
|
|
118
|
+
currentTest = null
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
event.dispatcher.on(event.step.before, step => {
|
|
122
|
+
if (typeof timeout !== 'number') return
|
|
123
|
+
|
|
124
|
+
if (!store.timeouts) {
|
|
125
|
+
debug('step', step.toCode().trim(), 'timeout disabled')
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (timeout < 0) {
|
|
130
|
+
debug('Previous steps timed out, setting timeout to 0.01s')
|
|
131
|
+
step.setTimeout(0.01, TIMEOUT_ORDER.testOrSuite)
|
|
132
|
+
} else {
|
|
133
|
+
debug(`Setting timeout ${timeout}ms for step ${step.toCode().trim()}`)
|
|
134
|
+
step.setTimeout(timeout, TIMEOUT_ORDER.testOrSuite)
|
|
135
|
+
}
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
event.dispatcher.on(event.step.after, step => {
|
|
139
|
+
if (typeof timeout !== 'number') return
|
|
140
|
+
if (!store.timeouts) return
|
|
141
|
+
|
|
142
|
+
recorder.catchWithoutStop(err => {
|
|
143
|
+
// we wrap timeout errors in a StepTimeoutError
|
|
144
|
+
// but only if global timeout is set
|
|
145
|
+
// should we wrap all timeout errors?
|
|
146
|
+
if (err instanceof TimeoutError) {
|
|
147
|
+
const testTimeoutExceeded = timeout && +Date.now() - step.startTime >= timeout
|
|
148
|
+
debug('Step failed due to global test or suite timeout')
|
|
149
|
+
if (testTimeoutExceeded) {
|
|
150
|
+
debug('Test failed due to global test or suite timeout')
|
|
151
|
+
throw new TestTimeoutError(currentTimeout)
|
|
152
|
+
}
|
|
153
|
+
throw new StepTimeoutError(currentTimeout, step)
|
|
154
|
+
}
|
|
155
|
+
throw err
|
|
156
|
+
})
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
event.dispatcher.on(event.step.finished, step => {
|
|
160
|
+
if (!store.timeouts) {
|
|
161
|
+
debug('step', step.toCode().trim(), 'timeout disabled')
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (typeof timeout === 'number') debug('Timeout', timeout)
|
|
166
|
+
|
|
167
|
+
debug(`step ${step.toCode().trim()}:${step.status} duration`, step.duration)
|
|
168
|
+
if (typeof timeout === 'number' && !Number.isNaN(timeout)) timeout -= step.duration
|
|
169
|
+
|
|
170
|
+
if (typeof timeout === 'number' && timeout <= 0 && recorder.isRunning()) {
|
|
171
|
+
debug(`step ${step.toCode().trim()} timed out`)
|
|
172
|
+
recorder.throw(new TestTimeoutError(currentTimeout))
|
|
173
|
+
}
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function checkForSeconds(timeout) {
|
|
178
|
+
if (timeout >= 1000) {
|
|
179
|
+
console.log(`Warning: Timeout was set to ${timeout}secs.\nGlobal timeout should be specified in seconds.`)
|
|
180
|
+
}
|
|
181
|
+
}
|
package/lib/listener/helpers.js
CHANGED
|
@@ -1,79 +1,104 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import recorder from '../recorder.js'
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
|
|
1
|
+
import path from 'path'
|
|
2
|
+
import event from '../event.js'
|
|
3
|
+
import recorder from '../recorder.js'
|
|
4
|
+
import store from '../store.js'
|
|
5
|
+
import output from '../output.js'
|
|
7
6
|
/**
|
|
8
7
|
* Enable Helpers to listen to test events
|
|
9
8
|
*/
|
|
10
9
|
export default function () {
|
|
11
|
-
const helpers = container.helpers()
|
|
10
|
+
const helpers = global.container.helpers()
|
|
12
11
|
|
|
13
12
|
const runHelpersHook = (hook, param) => {
|
|
14
|
-
if (store.dryRun) return
|
|
15
|
-
Object.values(helpers).forEach(
|
|
13
|
+
if (store.dryRun) return
|
|
14
|
+
Object.values(helpers).forEach(helper => {
|
|
16
15
|
if (helper[hook]) {
|
|
17
|
-
helper[hook](param)
|
|
16
|
+
helper[hook](param)
|
|
18
17
|
}
|
|
19
|
-
})
|
|
20
|
-
}
|
|
18
|
+
})
|
|
19
|
+
}
|
|
21
20
|
|
|
22
21
|
const runAsyncHelpersHook = (hook, param, force) => {
|
|
23
|
-
if (store.dryRun) return
|
|
24
|
-
Object.keys(helpers).forEach(
|
|
25
|
-
if (!helpers[key][hook]) return
|
|
26
|
-
recorder.add(`hook ${key}.${hook}()`, () => helpers[key][hook](param), force, false)
|
|
27
|
-
})
|
|
28
|
-
}
|
|
22
|
+
if (store.dryRun) return
|
|
23
|
+
Object.keys(helpers).forEach(key => {
|
|
24
|
+
if (!helpers[key][hook]) return
|
|
25
|
+
recorder.add(`hook ${key}.${hook}()`, () => helpers[key][hook](param), force, false)
|
|
26
|
+
})
|
|
27
|
+
}
|
|
29
28
|
|
|
30
|
-
event.dispatcher.on(event.suite.before,
|
|
29
|
+
event.dispatcher.on(event.suite.before, suite => {
|
|
31
30
|
// if (suite.parent) return; // only for root suite
|
|
32
|
-
runAsyncHelpersHook('_beforeSuite', suite, true)
|
|
33
|
-
})
|
|
31
|
+
runAsyncHelpersHook('_beforeSuite', suite, true)
|
|
32
|
+
})
|
|
34
33
|
|
|
35
|
-
event.dispatcher.on(event.suite.after,
|
|
34
|
+
event.dispatcher.on(event.suite.after, suite => {
|
|
36
35
|
// if (suite.parent) return; // only for root suite
|
|
37
|
-
runAsyncHelpersHook('_afterSuite', suite, true)
|
|
38
|
-
})
|
|
36
|
+
runAsyncHelpersHook('_afterSuite', suite, true)
|
|
37
|
+
})
|
|
39
38
|
|
|
40
|
-
event.dispatcher.on(event.test.started,
|
|
41
|
-
runHelpersHook('_test', test)
|
|
42
|
-
recorder.catch(e => output.
|
|
43
|
-
})
|
|
39
|
+
event.dispatcher.on(event.test.started, test => {
|
|
40
|
+
runHelpersHook('_test', test)
|
|
41
|
+
recorder.catch(e => output.error(e))
|
|
42
|
+
})
|
|
44
43
|
|
|
45
|
-
event.dispatcher.on(event.test.before,
|
|
44
|
+
event.dispatcher.on(event.test.before, test => {
|
|
46
45
|
// schedule config to revert changes
|
|
47
|
-
runAsyncHelpersHook('_before', test, true)
|
|
48
|
-
recorder.catchWithoutStop(e => output.
|
|
49
|
-
})
|
|
46
|
+
runAsyncHelpersHook('_before', test, true)
|
|
47
|
+
recorder.catchWithoutStop(e => output.error(e))
|
|
48
|
+
})
|
|
50
49
|
|
|
51
|
-
event.dispatcher.on(event.test.passed,
|
|
52
|
-
runAsyncHelpersHook('_passed', test, true)
|
|
50
|
+
event.dispatcher.on(event.test.passed, test => {
|
|
51
|
+
runAsyncHelpersHook('_passed', test, true)
|
|
53
52
|
// should not fail test execution, so errors should be caught
|
|
54
|
-
recorder.catchWithoutStop(e => output.
|
|
55
|
-
})
|
|
53
|
+
recorder.catchWithoutStop(e => output.error(e))
|
|
54
|
+
})
|
|
56
55
|
|
|
57
|
-
event.dispatcher.on(event.test.failed,
|
|
58
|
-
runAsyncHelpersHook('_failed', test, true)
|
|
56
|
+
event.dispatcher.on(event.test.failed, test => {
|
|
57
|
+
runAsyncHelpersHook('_failed', test, true)
|
|
59
58
|
// should not fail test execution, so errors should be caught
|
|
60
|
-
recorder.catchWithoutStop(e => output.
|
|
61
|
-
})
|
|
59
|
+
recorder.catchWithoutStop(e => output.error(e))
|
|
60
|
+
})
|
|
62
61
|
|
|
63
62
|
event.dispatcher.on(event.test.after, () => {
|
|
64
|
-
runAsyncHelpersHook('_after', {}, true)
|
|
65
|
-
recorder.catchWithoutStop(e => output.
|
|
66
|
-
})
|
|
63
|
+
runAsyncHelpersHook('_after', {}, true)
|
|
64
|
+
recorder.catchWithoutStop(e => output.error(e))
|
|
65
|
+
})
|
|
67
66
|
|
|
68
|
-
event.dispatcher.on(event.step.before,
|
|
69
|
-
runAsyncHelpersHook('_beforeStep', step)
|
|
70
|
-
})
|
|
67
|
+
event.dispatcher.on(event.step.before, step => {
|
|
68
|
+
runAsyncHelpersHook('_beforeStep', step)
|
|
69
|
+
})
|
|
71
70
|
|
|
72
|
-
event.dispatcher.on(event.step.after,
|
|
73
|
-
runAsyncHelpersHook('_afterStep', step)
|
|
74
|
-
})
|
|
71
|
+
event.dispatcher.on(event.step.after, step => {
|
|
72
|
+
runAsyncHelpersHook('_afterStep', step)
|
|
73
|
+
})
|
|
75
74
|
|
|
76
75
|
event.dispatcher.on(event.all.result, () => {
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
// Skip _finishTest for all helpers if any browser helper restarts to avoid double cleanup
|
|
77
|
+
const hasBrowserRestart = Object.values(helpers).some(helper =>
|
|
78
|
+
(helper.config && (helper.config.restart === 'browser' || helper.config.restart === 'context' || helper.config.restart === true)) ||
|
|
79
|
+
(helper.options && (helper.options.restart === 'browser' || helper.options.restart === 'context' || helper.options.restart === true))
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
Object.keys(helpers).forEach(key => {
|
|
83
|
+
const helper = helpers[key]
|
|
84
|
+
if (helper._finishTest && !hasBrowserRestart) {
|
|
85
|
+
recorder.add(`hook ${key}._finishTest()`, () => helper._finishTest(), true, false)
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
event.dispatcher.on(event.all.after, () => {
|
|
91
|
+
// Skip _cleanup for all helpers if any browser helper restarts to avoid double cleanup
|
|
92
|
+
const hasBrowserRestart = Object.values(helpers).some(helper =>
|
|
93
|
+
(helper.config && (helper.config.restart === 'browser' || helper.config.restart === 'context' || helper.config.restart === true)) ||
|
|
94
|
+
(helper.options && (helper.options.restart === 'browser' || helper.options.restart === 'context' || helper.options.restart === true))
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
Object.keys(helpers).forEach(key => {
|
|
98
|
+
const helper = helpers[key]
|
|
99
|
+
if (helper._cleanup && !hasBrowserRestart) {
|
|
100
|
+
recorder.add(`hook ${key}._cleanup()`, () => helper._cleanup(), true, false)
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
})
|
|
79
104
|
}
|
package/lib/listener/mocha.js
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
|
-
import
|
|
2
|
-
import container from '../container.js';
|
|
1
|
+
import event from '../event.js'
|
|
3
2
|
|
|
4
3
|
export default function () {
|
|
5
|
-
let mocha
|
|
4
|
+
let mocha
|
|
6
5
|
|
|
7
6
|
event.dispatcher.on(event.all.before, () => {
|
|
8
|
-
mocha = container.mocha()
|
|
9
|
-
})
|
|
7
|
+
mocha = global.container.mocha()
|
|
8
|
+
})
|
|
10
9
|
|
|
11
|
-
event.dispatcher.on(event.test.passed,
|
|
12
|
-
mocha.Runner.emit('pass', test)
|
|
13
|
-
})
|
|
10
|
+
event.dispatcher.on(event.test.passed, test => {
|
|
11
|
+
mocha.Runner.emit('pass', test)
|
|
12
|
+
})
|
|
14
13
|
|
|
15
14
|
event.dispatcher.on(event.test.failed, (test, err) => {
|
|
16
|
-
test.state = 'failed'
|
|
17
|
-
mocha.Runner.emit('fail', test, err)
|
|
18
|
-
})
|
|
15
|
+
test.state = 'failed'
|
|
16
|
+
mocha.Runner.emit('fail', test, err)
|
|
17
|
+
})
|
|
19
18
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import event from '../event.js'
|
|
2
|
+
|
|
3
|
+
export default function () {
|
|
4
|
+
event.dispatcher.on(event.hook.failed, err => {
|
|
5
|
+
global.container.result().addStats({ failedHooks: 1 })
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
event.dispatcher.on(event.test.before, test => {
|
|
9
|
+
global.container.result().addTest(test)
|
|
10
|
+
})
|
|
11
|
+
}
|