codeceptjs 4.0.0-beta.1 → 4.0.0-beta.3
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 -81
- package/lib/actor.js +13 -13
- package/lib/ai.js +10 -13
- package/lib/assert/empty.js +20 -21
- package/lib/assert/equal.js +37 -39
- package/lib/assert/error.js +14 -14
- package/lib/assert/include.js +46 -47
- package/lib/assert/throws.js +13 -11
- package/lib/assert/truth.js +19 -22
- package/lib/assert.js +4 -2
- package/lib/cli.js +57 -49
- package/lib/codecept.js +142 -155
- package/lib/colorUtils.js +3 -3
- package/lib/command/configMigrate.js +58 -52
- package/lib/command/definitions.js +88 -89
- package/lib/command/dryRun.js +71 -68
- package/lib/command/generate.js +197 -188
- package/lib/command/gherkin/init.js +27 -16
- package/lib/command/gherkin/snippets.js +20 -20
- package/lib/command/gherkin/steps.js +8 -8
- package/lib/command/info.js +40 -38
- package/lib/command/init.js +290 -288
- package/lib/command/interactive.js +32 -32
- package/lib/command/list.js +26 -26
- package/lib/command/run-multiple/chunk.js +5 -5
- package/lib/command/run-multiple/collection.js +3 -3
- package/lib/command/run-multiple/run.js +6 -2
- package/lib/command/run-multiple.js +113 -93
- package/lib/command/run-rerun.js +20 -25
- package/lib/command/run-workers.js +64 -66
- package/lib/command/run.js +26 -29
- package/lib/command/utils.js +80 -65
- package/lib/command/workers/runTests.js +10 -10
- package/lib/config.js +10 -9
- package/lib/container.js +40 -48
- package/lib/data/context.js +60 -59
- package/lib/data/dataScenarioConfig.js +47 -47
- package/lib/data/dataTableArgument.js +29 -29
- package/lib/data/table.js +26 -20
- package/lib/event.js +163 -167
- package/lib/heal.js +13 -17
- package/lib/helper/AI.js +130 -41
- package/lib/helper/ApiDataFactory.js +73 -69
- package/lib/helper/Appium.js +413 -382
- package/lib/helper/ExpectHelper.js +40 -48
- package/lib/helper/FileSystem.js +80 -79
- package/lib/helper/GraphQL.js +44 -43
- package/lib/helper/GraphQLDataFactory.js +50 -50
- package/lib/helper/JSONResponse.js +65 -62
- package/lib/helper/Mochawesome.js +28 -28
- package/lib/helper/MockServer.js +12 -14
- package/lib/helper/Nightmare.js +662 -566
- package/lib/helper/Playwright.js +1361 -1216
- package/lib/helper/Protractor.js +663 -627
- package/lib/helper/Puppeteer.js +1231 -1128
- package/lib/helper/REST.js +159 -68
- package/lib/helper/SoftExpectHelper.js +2 -2
- package/lib/helper/TestCafe.js +490 -484
- package/lib/helper/WebDriver.js +1297 -1156
- package/lib/helper/clientscripts/PollyWebDriverExt.js +1 -1
- package/lib/helper/errors/ConnectionRefused.js +1 -1
- package/lib/helper/errors/ElementAssertion.js +2 -2
- package/lib/helper/errors/ElementNotFound.js +2 -2
- package/lib/helper/errors/RemoteBrowserConnectionRefused.js +1 -1
- package/lib/helper/extras/Console.js +1 -1
- package/lib/helper/extras/PlaywrightPropEngine.js +2 -2
- package/lib/helper/extras/PlaywrightReactVueLocator.js +1 -1
- package/lib/helper/extras/PlaywrightRestartOpts.js +21 -18
- package/lib/helper/extras/Popup.js +1 -1
- package/lib/helper/extras/React.js +3 -3
- package/lib/helper/network/actions.js +14 -7
- package/lib/helper/network/utils.js +3 -2
- package/lib/helper/scripts/blurElement.js +1 -1
- package/lib/helper/scripts/focusElement.js +1 -1
- package/lib/helper/scripts/highlightElement.js +1 -1
- package/lib/helper/scripts/isElementClickable.js +1 -1
- package/lib/helper/testcafe/testControllerHolder.js +1 -1
- package/lib/helper/testcafe/testcafe-utils.js +6 -7
- package/lib/helper.js +1 -3
- package/lib/history.js +6 -5
- package/lib/hooks.js +6 -6
- package/lib/html.js +7 -7
- package/lib/index.js +25 -41
- package/lib/interfaces/bdd.js +47 -64
- package/lib/interfaces/featureConfig.js +19 -19
- package/lib/interfaces/gherkin.js +124 -118
- package/lib/interfaces/scenarioConfig.js +29 -29
- package/lib/listener/artifacts.js +9 -9
- package/lib/listener/config.js +24 -24
- 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 -53
- package/lib/listener/timeout.js +54 -54
- package/lib/locator.js +6 -10
- package/lib/mochaFactory.js +18 -15
- package/lib/output.js +6 -10
- package/lib/parser.js +15 -12
- package/lib/pause.js +40 -33
- package/lib/plugin/allure.js +15 -15
- package/lib/plugin/autoDelay.js +29 -37
- package/lib/plugin/autoLogin.js +70 -65
- package/lib/plugin/commentStep.js +18 -18
- package/lib/plugin/coverage.js +115 -67
- package/lib/plugin/customLocator.js +21 -20
- package/lib/plugin/debugErrors.js +24 -24
- package/lib/plugin/eachElement.js +38 -38
- package/lib/plugin/fakerTransform.js +6 -6
- package/lib/plugin/heal.js +67 -108
- package/lib/plugin/pauseOnFail.js +11 -11
- package/lib/plugin/retryFailedStep.js +32 -39
- package/lib/plugin/retryTo.js +46 -40
- package/lib/plugin/screenshotOnFail.js +109 -87
- package/lib/plugin/selenoid.js +131 -118
- package/lib/plugin/standardActingHelpers.js +2 -8
- package/lib/plugin/stepByStepReport.js +110 -91
- package/lib/plugin/stepTimeout.js +24 -23
- package/lib/plugin/subtitles.js +34 -35
- package/lib/plugin/tryTo.js +40 -30
- package/lib/plugin/wdio.js +78 -75
- package/lib/recorder.js +14 -17
- package/lib/rerun.js +11 -10
- package/lib/scenario.js +25 -23
- package/lib/secret.js +4 -2
- package/lib/session.js +10 -10
- package/lib/step.js +12 -9
- package/lib/store.js +2 -3
- package/lib/transform.js +1 -1
- package/lib/translation.js +7 -8
- package/lib/ui.js +12 -14
- package/lib/utils.js +70 -72
- package/lib/within.js +10 -10
- package/lib/workerStorage.js +27 -25
- package/lib/workers.js +29 -32
- package/package.json +56 -57
- package/translations/de-DE.js +1 -1
- package/translations/fr-FR.js +1 -1
- package/translations/index.js +9 -13
- 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/index.d.ts +415 -65
- package/typings/promiseBasedTypes.d.ts +32 -0
- package/typings/types.d.ts +32 -0
- package/lib/dirname.js +0 -5
- package/lib/helper/Expect.js +0 -425
package/lib/listener/helpers.js
CHANGED
|
@@ -1,79 +1,79 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const event = require('../event')
|
|
3
|
+
const container = require('../container')
|
|
4
|
+
const recorder = require('../recorder')
|
|
5
|
+
const store = require('../store')
|
|
6
|
+
const { error } = require('../output')
|
|
7
7
|
/**
|
|
8
8
|
* Enable Helpers to listen to test events
|
|
9
9
|
*/
|
|
10
|
-
|
|
11
|
-
const helpers = container.helpers()
|
|
10
|
+
module.exports = function () {
|
|
11
|
+
const helpers = container.helpers()
|
|
12
12
|
|
|
13
13
|
const runHelpersHook = (hook, param) => {
|
|
14
|
-
if (store.dryRun) return
|
|
14
|
+
if (store.dryRun) return
|
|
15
15
|
Object.values(helpers).forEach((helper) => {
|
|
16
16
|
if (helper[hook]) {
|
|
17
|
-
helper[hook](param)
|
|
17
|
+
helper[hook](param)
|
|
18
18
|
}
|
|
19
|
-
})
|
|
20
|
-
}
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
21
|
|
|
22
22
|
const runAsyncHelpersHook = (hook, param, force) => {
|
|
23
|
-
if (store.dryRun) return
|
|
23
|
+
if (store.dryRun) return
|
|
24
24
|
Object.keys(helpers).forEach((key) => {
|
|
25
|
-
if (!helpers[key][hook]) return
|
|
26
|
-
recorder.add(`hook ${key}.${hook}()`, () => helpers[key][hook](param), force, false)
|
|
27
|
-
})
|
|
28
|
-
}
|
|
25
|
+
if (!helpers[key][hook]) return
|
|
26
|
+
recorder.add(`hook ${key}.${hook}()`, () => helpers[key][hook](param), force, false)
|
|
27
|
+
})
|
|
28
|
+
}
|
|
29
29
|
|
|
30
30
|
event.dispatcher.on(event.suite.before, (suite) => {
|
|
31
31
|
// if (suite.parent) return; // only for root suite
|
|
32
|
-
runAsyncHelpersHook('_beforeSuite', suite, true)
|
|
33
|
-
})
|
|
32
|
+
runAsyncHelpersHook('_beforeSuite', suite, true)
|
|
33
|
+
})
|
|
34
34
|
|
|
35
35
|
event.dispatcher.on(event.suite.after, (suite) => {
|
|
36
36
|
// if (suite.parent) return; // only for root suite
|
|
37
|
-
runAsyncHelpersHook('_afterSuite', suite, true)
|
|
38
|
-
})
|
|
37
|
+
runAsyncHelpersHook('_afterSuite', suite, true)
|
|
38
|
+
})
|
|
39
39
|
|
|
40
40
|
event.dispatcher.on(event.test.started, (test) => {
|
|
41
|
-
runHelpersHook('_test', test)
|
|
42
|
-
recorder.catch(e =>
|
|
43
|
-
})
|
|
41
|
+
runHelpersHook('_test', test)
|
|
42
|
+
recorder.catch((e) => error(e))
|
|
43
|
+
})
|
|
44
44
|
|
|
45
45
|
event.dispatcher.on(event.test.before, (test) => {
|
|
46
46
|
// schedule config to revert changes
|
|
47
|
-
runAsyncHelpersHook('_before', test, true)
|
|
48
|
-
recorder.catchWithoutStop(e =>
|
|
49
|
-
})
|
|
47
|
+
runAsyncHelpersHook('_before', test, true)
|
|
48
|
+
recorder.catchWithoutStop((e) => error(e))
|
|
49
|
+
})
|
|
50
50
|
|
|
51
51
|
event.dispatcher.on(event.test.passed, (test) => {
|
|
52
|
-
runAsyncHelpersHook('_passed', test, true)
|
|
52
|
+
runAsyncHelpersHook('_passed', test, true)
|
|
53
53
|
// should not fail test execution, so errors should be caught
|
|
54
|
-
recorder.catchWithoutStop(e =>
|
|
55
|
-
})
|
|
54
|
+
recorder.catchWithoutStop((e) => error(e))
|
|
55
|
+
})
|
|
56
56
|
|
|
57
57
|
event.dispatcher.on(event.test.failed, (test) => {
|
|
58
|
-
runAsyncHelpersHook('_failed', test, true)
|
|
58
|
+
runAsyncHelpersHook('_failed', test, true)
|
|
59
59
|
// should not fail test execution, so errors should be caught
|
|
60
|
-
recorder.catchWithoutStop(e =>
|
|
61
|
-
})
|
|
60
|
+
recorder.catchWithoutStop((e) => error(e))
|
|
61
|
+
})
|
|
62
62
|
|
|
63
63
|
event.dispatcher.on(event.test.after, () => {
|
|
64
|
-
runAsyncHelpersHook('_after', {}, true)
|
|
65
|
-
recorder.catchWithoutStop(e =>
|
|
66
|
-
})
|
|
64
|
+
runAsyncHelpersHook('_after', {}, true)
|
|
65
|
+
recorder.catchWithoutStop((e) => error(e))
|
|
66
|
+
})
|
|
67
67
|
|
|
68
68
|
event.dispatcher.on(event.step.before, (step) => {
|
|
69
|
-
runAsyncHelpersHook('_beforeStep', step)
|
|
70
|
-
})
|
|
69
|
+
runAsyncHelpersHook('_beforeStep', step)
|
|
70
|
+
})
|
|
71
71
|
|
|
72
72
|
event.dispatcher.on(event.step.after, (step) => {
|
|
73
|
-
runAsyncHelpersHook('_afterStep', step)
|
|
74
|
-
})
|
|
73
|
+
runAsyncHelpersHook('_afterStep', step)
|
|
74
|
+
})
|
|
75
75
|
|
|
76
76
|
event.dispatcher.on(event.all.result, () => {
|
|
77
|
-
runAsyncHelpersHook('_finishTest', {}, true)
|
|
78
|
-
})
|
|
77
|
+
runAsyncHelpersHook('_finishTest', {}, true)
|
|
78
|
+
})
|
|
79
79
|
}
|
package/lib/listener/mocha.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
const event = require('../event')
|
|
2
|
+
const container = require('../container')
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
let mocha
|
|
4
|
+
module.exports = function () {
|
|
5
|
+
let mocha
|
|
6
6
|
|
|
7
7
|
event.dispatcher.on(event.all.before, () => {
|
|
8
|
-
mocha = container.mocha()
|
|
9
|
-
})
|
|
8
|
+
mocha = container.mocha()
|
|
9
|
+
})
|
|
10
10
|
|
|
11
11
|
event.dispatcher.on(event.test.passed, (test) => {
|
|
12
|
-
mocha.Runner.emit('pass', test)
|
|
13
|
-
})
|
|
12
|
+
mocha.Runner.emit('pass', test)
|
|
13
|
+
})
|
|
14
14
|
|
|
15
15
|
event.dispatcher.on(event.test.failed, (test, err) => {
|
|
16
|
-
test.state = 'failed'
|
|
17
|
-
mocha.Runner.emit('fail', test, err)
|
|
18
|
-
})
|
|
16
|
+
test.state = 'failed'
|
|
17
|
+
mocha.Runner.emit('fail', test, err)
|
|
18
|
+
})
|
|
19
19
|
}
|
package/lib/listener/retry.js
CHANGED
|
@@ -1,68 +1,70 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
const event = require('../event')
|
|
2
|
+
const output = require('../output')
|
|
3
|
+
const Config = require('../config')
|
|
4
|
+
const { isNotSet } = require('../utils')
|
|
5
5
|
|
|
6
|
-
const hooks = ['Before', 'After', 'BeforeSuite', 'AfterSuite']
|
|
6
|
+
const hooks = ['Before', 'After', 'BeforeSuite', 'AfterSuite']
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
module.exports = function () {
|
|
9
9
|
event.dispatcher.on(event.suite.before, (suite) => {
|
|
10
|
-
let retryConfig = Config.get('retry')
|
|
11
|
-
if (!retryConfig) return
|
|
10
|
+
let retryConfig = Config.get('retry')
|
|
11
|
+
if (!retryConfig) return
|
|
12
12
|
|
|
13
13
|
if (Number.isInteger(+retryConfig)) {
|
|
14
14
|
// is number
|
|
15
|
-
const retryNum = +retryConfig
|
|
16
|
-
output.
|
|
17
|
-
suite.retries(retryNum)
|
|
18
|
-
return
|
|
15
|
+
const retryNum = +retryConfig
|
|
16
|
+
output.log(`Retries: ${retryNum}`)
|
|
17
|
+
suite.retries(retryNum)
|
|
18
|
+
return
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
if (!Array.isArray(retryConfig)) {
|
|
22
|
-
retryConfig = [retryConfig]
|
|
22
|
+
retryConfig = [retryConfig]
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
for (const config of retryConfig) {
|
|
26
26
|
if (config.grep) {
|
|
27
|
-
if (!suite.title.includes(config.grep)) continue
|
|
27
|
+
if (!suite.title.includes(config.grep)) continue
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
hooks
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
hooks
|
|
31
|
+
.filter((hook) => !!config[hook])
|
|
32
|
+
.forEach((hook) => {
|
|
33
|
+
if (isNotSet(suite.opts[`retry${hook}`])) suite.opts[`retry${hook}`] = config[hook]
|
|
34
|
+
})
|
|
33
35
|
|
|
34
36
|
if (config.Feature) {
|
|
35
|
-
if (isNotSet(suite.retries())) suite.retries(config.Feature)
|
|
37
|
+
if (isNotSet(suite.retries())) suite.retries(config.Feature)
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
output.
|
|
40
|
+
output.log(`Retries: ${JSON.stringify(config)}`)
|
|
39
41
|
}
|
|
40
|
-
})
|
|
42
|
+
})
|
|
41
43
|
|
|
42
44
|
event.dispatcher.on(event.test.before, (test) => {
|
|
43
|
-
let retryConfig = Config.get('retry')
|
|
44
|
-
if (!retryConfig) return
|
|
45
|
+
let retryConfig = Config.get('retry')
|
|
46
|
+
if (!retryConfig) return
|
|
45
47
|
|
|
46
48
|
if (Number.isInteger(+retryConfig)) {
|
|
47
|
-
if (test.retries() === -1) test.retries(retryConfig)
|
|
48
|
-
return
|
|
49
|
+
if (test.retries() === -1) test.retries(retryConfig)
|
|
50
|
+
return
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
if (!Array.isArray(retryConfig)) {
|
|
52
|
-
retryConfig = [retryConfig]
|
|
54
|
+
retryConfig = [retryConfig]
|
|
53
55
|
}
|
|
54
56
|
|
|
55
|
-
retryConfig = retryConfig.filter(config => !!config.Scenario)
|
|
57
|
+
retryConfig = retryConfig.filter((config) => !!config.Scenario)
|
|
56
58
|
|
|
57
59
|
for (const config of retryConfig) {
|
|
58
60
|
if (config.grep) {
|
|
59
|
-
if (!test.fullTitle().includes(config.grep)) continue
|
|
61
|
+
if (!test.fullTitle().includes(config.grep)) continue
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
if (config.Scenario) {
|
|
63
|
-
if (test.retries() === -1) test.retries(config.Scenario)
|
|
64
|
-
output.
|
|
65
|
+
if (test.retries() === -1) test.retries(config.Scenario)
|
|
66
|
+
output.log(`Retries: ${config.Scenario}`)
|
|
65
67
|
}
|
|
66
68
|
}
|
|
67
|
-
})
|
|
69
|
+
})
|
|
68
70
|
}
|
package/lib/listener/steps.js
CHANGED
|
@@ -1,86 +1,83 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
const debug = require('debug')('codeceptjs:steps')
|
|
2
|
+
const event = require('../event')
|
|
3
|
+
const store = require('../store')
|
|
4
|
+
const output = require('../output')
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
let currentTest;
|
|
9
|
-
let currentHook;
|
|
6
|
+
let currentTest
|
|
7
|
+
let currentHook
|
|
10
8
|
|
|
11
9
|
/**
|
|
12
10
|
* Register steps inside tests
|
|
13
11
|
*/
|
|
14
|
-
|
|
12
|
+
module.exports = function () {
|
|
15
13
|
event.dispatcher.on(event.test.before, (test) => {
|
|
16
|
-
test.startedAt = +new Date()
|
|
17
|
-
test.artifacts = {}
|
|
18
|
-
})
|
|
14
|
+
test.startedAt = +new Date()
|
|
15
|
+
test.artifacts = {}
|
|
16
|
+
})
|
|
19
17
|
|
|
20
18
|
event.dispatcher.on(event.test.started, (test) => {
|
|
21
|
-
currentTest = test
|
|
22
|
-
currentTest.steps = []
|
|
23
|
-
if (!('retryNum' in currentTest)) currentTest.retryNum = 0
|
|
24
|
-
else currentTest.retryNum += 1
|
|
25
|
-
})
|
|
19
|
+
currentTest = test
|
|
20
|
+
currentTest.steps = []
|
|
21
|
+
if (!('retryNum' in currentTest)) currentTest.retryNum = 0
|
|
22
|
+
else currentTest.retryNum += 1
|
|
23
|
+
})
|
|
26
24
|
|
|
27
25
|
event.dispatcher.on(event.test.after, (test) => {
|
|
28
|
-
currentTest = null
|
|
29
|
-
})
|
|
26
|
+
currentTest = null
|
|
27
|
+
})
|
|
30
28
|
|
|
31
|
-
event.dispatcher.on(event.test.finished, (test) => {
|
|
32
|
-
});
|
|
29
|
+
event.dispatcher.on(event.test.finished, (test) => {})
|
|
33
30
|
|
|
34
31
|
event.dispatcher.on(event.hook.started, (suite) => {
|
|
35
|
-
currentHook = suite.ctx.test
|
|
36
|
-
currentHook.steps = []
|
|
32
|
+
currentHook = suite.ctx.test
|
|
33
|
+
currentHook.steps = []
|
|
37
34
|
|
|
38
|
-
if (suite.ctx && suite.ctx.test) output.
|
|
39
|
-
})
|
|
35
|
+
if (suite.ctx && suite.ctx.test) output.log(`--- STARTED ${suite.ctx.test.title} ---`)
|
|
36
|
+
})
|
|
40
37
|
|
|
41
38
|
event.dispatcher.on(event.hook.passed, (suite) => {
|
|
42
|
-
currentHook = null
|
|
43
|
-
if (suite.ctx && suite.ctx.test) output.
|
|
44
|
-
})
|
|
39
|
+
currentHook = null
|
|
40
|
+
if (suite.ctx && suite.ctx.test) output.log(`--- ENDED ${suite.ctx.test.title} ---`)
|
|
41
|
+
})
|
|
45
42
|
|
|
46
43
|
event.dispatcher.on(event.test.failed, () => {
|
|
47
44
|
const cutSteps = function (current) {
|
|
48
|
-
const failureIndex = current.steps.findIndex(el => el.status === 'failed')
|
|
45
|
+
const failureIndex = current.steps.findIndex((el) => el.status === 'failed')
|
|
49
46
|
// To be sure that failed test will be failed in report
|
|
50
|
-
current.state = 'failed'
|
|
51
|
-
current.steps.length = failureIndex + 1
|
|
52
|
-
return current
|
|
53
|
-
}
|
|
47
|
+
current.state = 'failed'
|
|
48
|
+
current.steps.length = failureIndex + 1
|
|
49
|
+
return current
|
|
50
|
+
}
|
|
54
51
|
if (currentHook && Array.isArray(currentHook.steps) && currentHook.steps.length) {
|
|
55
|
-
currentHook = cutSteps(currentHook)
|
|
56
|
-
return currentHook = null
|
|
52
|
+
currentHook = cutSteps(currentHook)
|
|
53
|
+
return (currentHook = null)
|
|
57
54
|
}
|
|
58
|
-
if (!currentTest) return
|
|
55
|
+
if (!currentTest) return
|
|
59
56
|
// last step is failing step
|
|
60
|
-
if (!currentTest.steps.length) return
|
|
61
|
-
return currentTest = cutSteps(currentTest)
|
|
62
|
-
})
|
|
57
|
+
if (!currentTest.steps.length) return
|
|
58
|
+
return (currentTest = cutSteps(currentTest))
|
|
59
|
+
})
|
|
63
60
|
|
|
64
61
|
event.dispatcher.on(event.test.passed, () => {
|
|
65
|
-
if (!currentTest) return
|
|
62
|
+
if (!currentTest) return
|
|
66
63
|
// To be sure that passed test will be passed in report
|
|
67
|
-
delete currentTest.err
|
|
68
|
-
currentTest.state = 'passed'
|
|
69
|
-
})
|
|
64
|
+
delete currentTest.err
|
|
65
|
+
currentTest.state = 'passed'
|
|
66
|
+
})
|
|
70
67
|
|
|
71
68
|
event.dispatcher.on(event.step.started, (step) => {
|
|
72
|
-
step.startedAt = +new Date()
|
|
73
|
-
step.test = currentTest
|
|
69
|
+
step.startedAt = +new Date()
|
|
70
|
+
step.test = currentTest
|
|
74
71
|
if (currentHook && Array.isArray(currentHook.steps)) {
|
|
75
|
-
return currentHook.steps.push(step)
|
|
72
|
+
return currentHook.steps.push(step)
|
|
76
73
|
}
|
|
77
|
-
if (!currentTest || !currentTest.steps) return
|
|
78
|
-
currentTest.steps.push(step)
|
|
79
|
-
})
|
|
74
|
+
if (!currentTest || !currentTest.steps) return
|
|
75
|
+
currentTest.steps.push(step)
|
|
76
|
+
})
|
|
80
77
|
|
|
81
78
|
event.dispatcher.on(event.step.finished, (step) => {
|
|
82
|
-
step.finishedAt = +new Date()
|
|
83
|
-
if (step.startedAt) step.duration = step.finishedAt - step.startedAt
|
|
84
|
-
debug(`Step '${step}' finished; Duration: ${step.duration || 0}ms`)
|
|
85
|
-
})
|
|
79
|
+
step.finishedAt = +new Date()
|
|
80
|
+
if (step.startedAt) step.duration = step.finishedAt - step.startedAt
|
|
81
|
+
debug(`Step '${step}' finished; Duration: ${step.duration || 0}ms`)
|
|
82
|
+
})
|
|
86
83
|
}
|
package/lib/listener/timeout.js
CHANGED
|
@@ -1,109 +1,109 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
let timeout
|
|
10
|
-
let suiteTimeout = []
|
|
11
|
-
let currentTest
|
|
12
|
-
let currentTimeout
|
|
1
|
+
const event = require('../event')
|
|
2
|
+
const output = require('../output')
|
|
3
|
+
const recorder = require('../recorder')
|
|
4
|
+
const Config = require('../config')
|
|
5
|
+
const { timeouts } = require('../store')
|
|
6
|
+
const TIMEOUT_ORDER = require('../step').TIMEOUT_ORDER
|
|
7
|
+
|
|
8
|
+
module.exports = function () {
|
|
9
|
+
let timeout
|
|
10
|
+
let suiteTimeout = []
|
|
11
|
+
let currentTest
|
|
12
|
+
let currentTimeout
|
|
13
13
|
|
|
14
14
|
if (!timeouts) {
|
|
15
|
-
console.log('Timeouts were disabled')
|
|
16
|
-
return
|
|
15
|
+
console.log('Timeouts were disabled')
|
|
16
|
+
return
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
event.dispatcher.on(event.suite.before, (suite) => {
|
|
20
|
-
suiteTimeout = []
|
|
21
|
-
let timeoutConfig = Config.get('timeout')
|
|
20
|
+
suiteTimeout = []
|
|
21
|
+
let timeoutConfig = Config.get('timeout')
|
|
22
22
|
|
|
23
23
|
if (timeoutConfig) {
|
|
24
24
|
if (!Number.isNaN(+timeoutConfig)) {
|
|
25
|
-
checkForSeconds(timeoutConfig)
|
|
26
|
-
suiteTimeout.push(timeoutConfig)
|
|
25
|
+
checkForSeconds(timeoutConfig)
|
|
26
|
+
suiteTimeout.push(timeoutConfig)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
if (!Array.isArray(timeoutConfig)) {
|
|
30
|
-
timeoutConfig = [timeoutConfig]
|
|
30
|
+
timeoutConfig = [timeoutConfig]
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
for (const config of timeoutConfig.filter(c => !!c.Feature)) {
|
|
33
|
+
for (const config of timeoutConfig.filter((c) => !!c.Feature)) {
|
|
34
34
|
if (config.grep) {
|
|
35
|
-
if (!suite.title.includes(config.grep)) continue
|
|
35
|
+
if (!suite.title.includes(config.grep)) continue
|
|
36
36
|
}
|
|
37
|
-
suiteTimeout.push(config.Feature)
|
|
37
|
+
suiteTimeout.push(config.Feature)
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
if (suite.totalTimeout) suiteTimeout.push(suite.totalTimeout)
|
|
42
|
-
output.
|
|
43
|
-
})
|
|
41
|
+
if (suite.totalTimeout) suiteTimeout.push(suite.totalTimeout)
|
|
42
|
+
output.log(`Timeouts: ${suiteTimeout}`)
|
|
43
|
+
})
|
|
44
44
|
|
|
45
45
|
event.dispatcher.on(event.test.before, (test) => {
|
|
46
|
-
currentTest = test
|
|
47
|
-
let testTimeout = null
|
|
46
|
+
currentTest = test
|
|
47
|
+
let testTimeout = null
|
|
48
48
|
|
|
49
|
-
let timeoutConfig = Config.get('timeout')
|
|
49
|
+
let timeoutConfig = Config.get('timeout')
|
|
50
50
|
|
|
51
51
|
if (typeof timeoutConfig === 'object' || Array.isArray(timeoutConfig)) {
|
|
52
52
|
if (!Array.isArray(timeoutConfig)) {
|
|
53
|
-
timeoutConfig = [timeoutConfig]
|
|
53
|
+
timeoutConfig = [timeoutConfig]
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
for (const config of timeoutConfig.filter(c => !!c.Scenario)) {
|
|
57
|
-
console.log('Test Timeout', config, test.title.includes(config.grep))
|
|
56
|
+
for (const config of timeoutConfig.filter((c) => !!c.Scenario)) {
|
|
57
|
+
console.log('Test Timeout', config, test.title.includes(config.grep))
|
|
58
58
|
if (config.grep) {
|
|
59
|
-
if (!test.title.includes(config.grep)) continue
|
|
59
|
+
if (!test.title.includes(config.grep)) continue
|
|
60
60
|
}
|
|
61
|
-
testTimeout = config.Scenario
|
|
61
|
+
testTimeout = config.Scenario
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
timeout = test.totalTimeout || testTimeout || suiteTimeout[suiteTimeout.length - 1]
|
|
66
|
-
if (!timeout) return
|
|
67
|
-
currentTimeout = timeout
|
|
68
|
-
output.
|
|
69
|
-
timeout *= 1000
|
|
70
|
-
})
|
|
65
|
+
timeout = test.totalTimeout || testTimeout || suiteTimeout[suiteTimeout.length - 1]
|
|
66
|
+
if (!timeout) return
|
|
67
|
+
currentTimeout = timeout
|
|
68
|
+
output.debug(`Test Timeout: ${timeout}s`)
|
|
69
|
+
timeout *= 1000
|
|
70
|
+
})
|
|
71
71
|
|
|
72
72
|
event.dispatcher.on(event.test.passed, (test) => {
|
|
73
|
-
currentTest = null
|
|
74
|
-
})
|
|
73
|
+
currentTest = null
|
|
74
|
+
})
|
|
75
75
|
|
|
76
76
|
event.dispatcher.on(event.test.failed, (test) => {
|
|
77
|
-
currentTest = null
|
|
78
|
-
})
|
|
77
|
+
currentTest = null
|
|
78
|
+
})
|
|
79
79
|
|
|
80
80
|
event.dispatcher.on(event.step.before, (step) => {
|
|
81
|
-
if (typeof timeout !== 'number') return
|
|
81
|
+
if (typeof timeout !== 'number') return
|
|
82
82
|
|
|
83
83
|
if (timeout < 0) {
|
|
84
|
-
step.setTimeout(0.01,
|
|
84
|
+
step.setTimeout(0.01, TIMEOUT_ORDER.testOrSuite)
|
|
85
85
|
} else {
|
|
86
|
-
step.setTimeout(timeout,
|
|
86
|
+
step.setTimeout(timeout, TIMEOUT_ORDER.testOrSuite)
|
|
87
87
|
}
|
|
88
|
-
})
|
|
88
|
+
})
|
|
89
89
|
|
|
90
90
|
event.dispatcher.on(event.step.finished, (step) => {
|
|
91
|
-
if (typeof timeout === 'number' && !Number.isNaN(timeout)) timeout -= step.duration
|
|
91
|
+
if (typeof timeout === 'number' && !Number.isNaN(timeout)) timeout -= step.duration
|
|
92
92
|
|
|
93
93
|
if (typeof timeout === 'number' && timeout <= 0 && recorder.isRunning()) {
|
|
94
94
|
if (currentTest && currentTest.callback) {
|
|
95
|
-
recorder.reset()
|
|
95
|
+
recorder.reset()
|
|
96
96
|
// replace mocha timeout with custom timeout
|
|
97
|
-
currentTest.timeout(0)
|
|
98
|
-
currentTest.callback(new Error(`Timeout ${currentTimeout}s exceeded (with Before hook)`))
|
|
99
|
-
currentTest.timedOut = true
|
|
97
|
+
currentTest.timeout(0)
|
|
98
|
+
currentTest.callback(new Error(`Timeout ${currentTimeout}s exceeded (with Before hook)`))
|
|
99
|
+
currentTest.timedOut = true
|
|
100
100
|
}
|
|
101
101
|
}
|
|
102
|
-
})
|
|
102
|
+
})
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
function checkForSeconds(timeout) {
|
|
106
106
|
if (timeout >= 1000) {
|
|
107
|
-
console.log(`Warning: Timeout was set to ${timeout}secs.\nGlobal timeout should be specified in seconds.`)
|
|
107
|
+
console.log(`Warning: Timeout was set to ${timeout}secs.\nGlobal timeout should be specified in seconds.`)
|
|
108
108
|
}
|
|
109
109
|
}
|
package/lib/locator.js
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import { sprintf } from 'sprintf-js';
|
|
2
|
-
|
|
3
|
-
import csstoxpath from 'csstoxpath';
|
|
4
|
-
|
|
5
|
-
import css_to_xpath from 'css-to-xpath';
|
|
6
|
-
import { xpathLocator } from './utils.js';
|
|
7
|
-
|
|
8
1
|
let cssToXPath;
|
|
2
|
+
const { sprintf } = require('sprintf-js');
|
|
3
|
+
|
|
4
|
+
const { xpathLocator } = require('./utils');
|
|
9
5
|
|
|
10
6
|
const locatorTypes = ['css', 'by', 'xpath', 'id', 'name', 'fuzzy', 'frame', 'shadow', 'pw'];
|
|
11
7
|
/** @class */
|
|
@@ -182,9 +178,9 @@ class Locator {
|
|
|
182
178
|
const limitation = [':nth-of-type', ':first-of-type', ':last-of-type', ':nth-last-child', ':nth-last-of-type', ':checked', ':disabled', ':enabled', ':required', ':lang', ':nth-child', ':has'];
|
|
183
179
|
|
|
184
180
|
if (limitation.some(item => locator.includes(item))) {
|
|
185
|
-
cssToXPath =
|
|
181
|
+
cssToXPath = require('css-to-xpath');
|
|
186
182
|
} else {
|
|
187
|
-
cssToXPath = csstoxpath;
|
|
183
|
+
cssToXPath = require('csstoxpath');
|
|
188
184
|
}
|
|
189
185
|
|
|
190
186
|
if (this.isXPath()) return this.value;
|
|
@@ -526,7 +522,7 @@ Locator.select = {
|
|
|
526
522
|
},
|
|
527
523
|
};
|
|
528
524
|
|
|
529
|
-
|
|
525
|
+
module.exports = Locator;
|
|
530
526
|
|
|
531
527
|
/**
|
|
532
528
|
* @private
|
package/lib/mochaFactory.js
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
const Mocha = require('mocha');
|
|
2
|
+
const fsPath = require('path');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const reporter = require('./cli');
|
|
5
|
+
const gherkinParser = require('./interfaces/gherkin');
|
|
6
|
+
const output = require('./output');
|
|
7
|
+
const { genTestId } = require('./utils');
|
|
8
|
+
const ConnectionRefused = require('./helper/errors/ConnectionRefused');
|
|
8
9
|
|
|
9
|
-
|
|
10
|
+
const scenarioUi = fsPath.join(__dirname, './ui.js');
|
|
10
11
|
|
|
11
12
|
let mocha;
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
class MochaFactory {
|
|
14
15
|
static create(config, opts) {
|
|
15
16
|
mocha = new Mocha(Object.assign(config, opts));
|
|
16
|
-
output.
|
|
17
|
+
output.process(opts.child);
|
|
17
18
|
mocha.ui(scenarioUi);
|
|
18
19
|
|
|
19
20
|
Mocha.Runner.prototype.uncaught = function (err) {
|
|
@@ -21,11 +22,11 @@ export class MochaFactory {
|
|
|
21
22
|
if (err.toString().indexOf('ECONNREFUSED') >= 0) {
|
|
22
23
|
err = new ConnectionRefused(err);
|
|
23
24
|
}
|
|
24
|
-
output.
|
|
25
|
-
output.
|
|
25
|
+
output.error(err);
|
|
26
|
+
output.print(err.stack);
|
|
26
27
|
process.exit(1);
|
|
27
28
|
}
|
|
28
|
-
output.
|
|
29
|
+
output.error('Uncaught undefined exception');
|
|
29
30
|
process.exit(1);
|
|
30
31
|
};
|
|
31
32
|
|
|
@@ -60,12 +61,12 @@ export class MochaFactory {
|
|
|
60
61
|
});
|
|
61
62
|
if (dupes.length) {
|
|
62
63
|
// ideally this should be no-op and throw (breaking change)...
|
|
63
|
-
output.
|
|
64
|
+
output.error(`Duplicate test names detected - Feature + Scenario name should be unique:\n${dupes.join('\n')}`);
|
|
64
65
|
}
|
|
65
66
|
|
|
66
67
|
if (missingFeatureInFile.length) {
|
|
67
68
|
missingFeatureInFile = [...new Set(missingFeatureInFile)];
|
|
68
|
-
output.
|
|
69
|
+
output.error(`Missing Feature section in:\n${missingFeatureInFile.join('\n')}`);
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
};
|
|
@@ -108,3 +109,5 @@ export class MochaFactory {
|
|
|
108
109
|
return mocha;
|
|
109
110
|
}
|
|
110
111
|
}
|
|
112
|
+
|
|
113
|
+
module.exports = MochaFactory;
|