codeceptjs 4.0.0-beta.2 → 4.0.0-beta.20
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 +262 -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 +301 -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 +109 -50
- package/lib/container.js +641 -261
- 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/loaderCheck.js +124 -0
- package/lib/utils/mask_data.js +47 -0
- package/lib/utils/typescript.js +237 -0
- package/lib/utils.js +411 -228
- package/lib/workerStorage.js +37 -34
- package/lib/workers.js +532 -296
- package/package.json +124 -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 -875
- package/typings/types.d.ts +547 -992
- 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
package/lib/step/meta.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import Step from './base.js'
|
|
2
|
+
import event from '../event.js'
|
|
3
|
+
import { humanizeString } from '../utils.js'
|
|
4
|
+
|
|
5
|
+
class MetaStep extends Step {
|
|
6
|
+
constructor(actor, method) {
|
|
7
|
+
if (!method) method = ''
|
|
8
|
+
super(method)
|
|
9
|
+
|
|
10
|
+
/** @member {boolean} collsapsed hide children steps from output */
|
|
11
|
+
this.collapsed = false
|
|
12
|
+
|
|
13
|
+
this.actor = actor
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** @return {boolean} */
|
|
17
|
+
isBDD() {
|
|
18
|
+
if (this.actor && this.actor.match && this.actor.match(/^(Given|When|Then|And|But)/)) {
|
|
19
|
+
return true
|
|
20
|
+
}
|
|
21
|
+
return false
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
toCliStyled() {
|
|
25
|
+
return this.toString()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
toString() {
|
|
29
|
+
const actorText = this.actor
|
|
30
|
+
|
|
31
|
+
if (this.isBDD()) {
|
|
32
|
+
return `${this.prefix}${actorText} ${this.name} "${this.humanizeArgs()}${this.suffix}"`
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (actorText === 'I') {
|
|
36
|
+
return `${this.prefix}${actorText} ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!this.actor) {
|
|
40
|
+
return `${this.name} ${this.humanizeArgs()}${this.suffix}`.trim()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return `On ${this.prefix}${actorText}: ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`.trim()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
humanize() {
|
|
47
|
+
return humanizeString(this.name)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
setTrace() {}
|
|
51
|
+
|
|
52
|
+
setContext(context) {
|
|
53
|
+
this.context = context
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** @return {*} */
|
|
57
|
+
run(fn) {
|
|
58
|
+
this.status = 'queued'
|
|
59
|
+
this.setArguments(Array.from(arguments).slice(1))
|
|
60
|
+
let result
|
|
61
|
+
|
|
62
|
+
const registerStep = step => {
|
|
63
|
+
this.setMetaStep(null)
|
|
64
|
+
step.setMetaStep(this)
|
|
65
|
+
}
|
|
66
|
+
event.dispatcher.prependListener(event.step.before, registerStep)
|
|
67
|
+
// Handle async and sync methods.
|
|
68
|
+
if (fn.constructor.name === 'AsyncFunction') {
|
|
69
|
+
result = fn
|
|
70
|
+
.apply(this.context, this.args)
|
|
71
|
+
.then(result => {
|
|
72
|
+
return result
|
|
73
|
+
})
|
|
74
|
+
.catch(error => {
|
|
75
|
+
this.setStatus('failed')
|
|
76
|
+
throw error
|
|
77
|
+
})
|
|
78
|
+
.finally(() => {
|
|
79
|
+
this.endTime = Date.now()
|
|
80
|
+
event.dispatcher.removeListener(event.step.before, registerStep)
|
|
81
|
+
})
|
|
82
|
+
} else {
|
|
83
|
+
try {
|
|
84
|
+
this.startTime = Date.now()
|
|
85
|
+
result = fn.apply(this.context, this.args)
|
|
86
|
+
} catch (error) {
|
|
87
|
+
this.setStatus('failed')
|
|
88
|
+
throw error
|
|
89
|
+
} finally {
|
|
90
|
+
this.endTime = Date.now()
|
|
91
|
+
event.dispatcher.removeListener(event.step.before, registerStep)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return result
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export default MetaStep
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import event from '../event.js'
|
|
2
|
+
import recorder from '../recorder.js'
|
|
3
|
+
import StepConfig from './config.js'
|
|
4
|
+
import output from '../output.js'
|
|
5
|
+
import store from '../store.js'
|
|
6
|
+
import { TIMEOUT_ORDER } from '../timeout.js'
|
|
7
|
+
import retryStep from './retry.js'
|
|
8
|
+
function recordStep(step, args) {
|
|
9
|
+
step.status = 'queued'
|
|
10
|
+
|
|
11
|
+
// apply step configuration
|
|
12
|
+
const lastArg = args[args.length - 1]
|
|
13
|
+
if (lastArg instanceof StepConfig) {
|
|
14
|
+
const stepConfig = args.pop()
|
|
15
|
+
const { opts, timeout, retry } = stepConfig.getConfig()
|
|
16
|
+
|
|
17
|
+
if (opts) {
|
|
18
|
+
output.debug(`Step ${step.name}: options applied ${JSON.stringify(opts)}`)
|
|
19
|
+
store.stepOptions = opts
|
|
20
|
+
step.opts = opts
|
|
21
|
+
}
|
|
22
|
+
if (timeout) {
|
|
23
|
+
output.debug(`Step ${step.name} timeout ${timeout}s`)
|
|
24
|
+
step.setTimeout(timeout * 1000, TIMEOUT_ORDER.codeLimitTime)
|
|
25
|
+
}
|
|
26
|
+
if (retry) retryStep(retry)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
step.setArguments(args)
|
|
30
|
+
// run async before step hooks
|
|
31
|
+
event.emit(event.step.before, step)
|
|
32
|
+
|
|
33
|
+
const task = `${step.name}: ${step.humanizeArgs()}`
|
|
34
|
+
let val
|
|
35
|
+
|
|
36
|
+
// run step inside promise
|
|
37
|
+
recorder.add(
|
|
38
|
+
task,
|
|
39
|
+
() => {
|
|
40
|
+
if (!step.startTime) {
|
|
41
|
+
// step can be retries
|
|
42
|
+
event.emit(event.step.started, step)
|
|
43
|
+
step.startTime = +Date.now()
|
|
44
|
+
}
|
|
45
|
+
return (val = step.run(...args))
|
|
46
|
+
},
|
|
47
|
+
false,
|
|
48
|
+
undefined,
|
|
49
|
+
step.timeout,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
event.emit(event.step.after, step)
|
|
53
|
+
|
|
54
|
+
recorder.add('step passed', () => {
|
|
55
|
+
step.endTime = +Date.now()
|
|
56
|
+
event.emit(event.step.passed, step, val)
|
|
57
|
+
event.emit(event.step.finished, step)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
recorder.catch(err => {
|
|
61
|
+
step.status = 'failed'
|
|
62
|
+
step.endTime = +Date.now()
|
|
63
|
+
event.emit(event.step.failed, step, err)
|
|
64
|
+
event.emit(event.step.finished, step)
|
|
65
|
+
throw err
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
recorder.add('return result', () => val)
|
|
69
|
+
// run async after step hooks
|
|
70
|
+
|
|
71
|
+
return recorder.promise()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export default recordStep
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import recorder from '../recorder.js'
|
|
2
|
+
import event from '../event.js'
|
|
3
|
+
|
|
4
|
+
function retryStep(opts) {
|
|
5
|
+
if (opts === undefined) opts = 1
|
|
6
|
+
recorder.retry(opts)
|
|
7
|
+
// remove retry once the step passed
|
|
8
|
+
recorder.add(() => event.dispatcher.once(event.step.finished, () => recorder.retries.pop()))
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default retryStep
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import MetaStep from './meta.js'
|
|
2
|
+
import event from '../event.js'
|
|
3
|
+
|
|
4
|
+
let currentSection
|
|
5
|
+
|
|
6
|
+
class Section {
|
|
7
|
+
constructor(name = '') {
|
|
8
|
+
this.name = name
|
|
9
|
+
|
|
10
|
+
this.metaStep = new MetaStep(null, name)
|
|
11
|
+
|
|
12
|
+
this.attachMetaStep = step => {
|
|
13
|
+
if (currentSection !== this) return
|
|
14
|
+
if (!step) return
|
|
15
|
+
const metaStep = getRootMetaStep(step)
|
|
16
|
+
|
|
17
|
+
if (metaStep !== this.metaStep) {
|
|
18
|
+
metaStep.metaStep = this.metaStep
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
hidden() {
|
|
24
|
+
this.metaStep.collapsed = true
|
|
25
|
+
return this
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
start() {
|
|
29
|
+
if (currentSection) currentSection.end()
|
|
30
|
+
currentSection = this
|
|
31
|
+
event.dispatcher.prependListener(event.step.before, this.attachMetaStep)
|
|
32
|
+
event.dispatcher.once(event.test.finished, () => this.end())
|
|
33
|
+
return this
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
end() {
|
|
37
|
+
currentSection = null
|
|
38
|
+
event.dispatcher.off(event.step.started, this.attachMetaStep)
|
|
39
|
+
return this
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @returns {Section}
|
|
44
|
+
*/
|
|
45
|
+
static current() {
|
|
46
|
+
return currentSection
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function getRootMetaStep(step) {
|
|
51
|
+
if (step.metaStep) return getRootMetaStep(step.metaStep)
|
|
52
|
+
return step
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export default Section
|
package/lib/step.js
CHANGED
|
@@ -1,334 +1,23 @@
|
|
|
1
|
-
//
|
|
2
|
-
/* eslint-disable max-classes-per-file */
|
|
3
|
-
import { store } from './store.js';
|
|
4
|
-
|
|
5
|
-
import Secret from './secret.js';
|
|
6
|
-
import * as event from './event.js';
|
|
7
|
-
|
|
8
|
-
const STACK_LINE = 4;
|
|
9
|
-
|
|
1
|
+
// refactored step class, moved to helper
|
|
10
2
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* @param {CodeceptJS.Helper} helper
|
|
14
|
-
* @param {string} name
|
|
3
|
+
* Step is wrapper around a helper method.
|
|
4
|
+
* It is used to create a new step that is a combination of other steps.
|
|
15
5
|
*/
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* timeouts set with order below zero only override timeouts of higher order if their value is smaller
|
|
21
|
-
*/
|
|
22
|
-
testOrSuite: -5,
|
|
23
|
-
/**
|
|
24
|
-
* 0-9 - designated for override of timeouts set from code, 5 is used by stepTimeout plugin when stepTimeout.config.overrideStepLimits=true
|
|
25
|
-
*/
|
|
26
|
-
stepTimeoutHard: 5,
|
|
27
|
-
/**
|
|
28
|
-
* 10-19 - designated for timeouts set from code, 15 is order of I.setTimeout(t) operation
|
|
29
|
-
*/
|
|
30
|
-
codeLimitTime: 15,
|
|
31
|
-
/**
|
|
32
|
-
* 20-29 - designated for timeout settings which could be overriden in tests code, 25 is used by stepTimeout plugin when stepTimeout.config.overrideStepLimits=false
|
|
33
|
-
*/
|
|
34
|
-
stepTimeoutSoft: 25,
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
constructor(helper, name) {
|
|
39
|
-
/** @member {string} */
|
|
40
|
-
this.actor = 'I'; // I = actor
|
|
41
|
-
/** @member {CodeceptJS.Helper} */
|
|
42
|
-
this.helper = helper; // corresponding helper
|
|
43
|
-
/** @member {string} */
|
|
44
|
-
this.name = name; // name of a step console
|
|
45
|
-
/** @member {string} */
|
|
46
|
-
this.helperMethod = name; // helper method
|
|
47
|
-
/** @member {string} */
|
|
48
|
-
this.status = 'pending';
|
|
49
|
-
/**
|
|
50
|
-
* @member {string} suffix
|
|
51
|
-
* @memberof CodeceptJS.Step#
|
|
52
|
-
*/
|
|
53
|
-
/** @member {string} */
|
|
54
|
-
this.prefix = this.suffix = '';
|
|
55
|
-
/** @member {string} */
|
|
56
|
-
this.comment = '';
|
|
57
|
-
/** @member {Array<*>} */
|
|
58
|
-
this.args = [];
|
|
59
|
-
/** @member {MetaStep} */
|
|
60
|
-
this.metaStep = undefined;
|
|
61
|
-
/** @member {string} */
|
|
62
|
-
this.stack = '';
|
|
63
|
-
|
|
64
|
-
const timeouts = new Map();
|
|
65
|
-
/**
|
|
66
|
-
* @method
|
|
67
|
-
* @returns {number|undefined}
|
|
68
|
-
*/
|
|
69
|
-
this.getTimeout = function () {
|
|
70
|
-
let totalTimeout;
|
|
71
|
-
// iterate over all timeouts starting from highest values of order
|
|
72
|
-
new Map([...timeouts.entries()].sort().reverse()).forEach((timeout, order) => {
|
|
73
|
-
if (timeout !== undefined && (
|
|
74
|
-
// when orders >= 0 - timeout value overrides those set with higher order elements
|
|
75
|
-
(// when `order < 0` - timeout overrides higher values of timeout or 'no timeout' (totalTimeout === 0) set by elements with higher order
|
|
76
|
-
(order >= 0
|
|
77
|
-
|
|
78
|
-
// when `order < 0 && totalTimeout === undefined` - timeout is used when nothing is set by elements with higher order
|
|
79
|
-
|| totalTimeout === undefined || timeout > 0 && (timeout < totalTimeout || totalTimeout === 0)))
|
|
80
|
-
)) {
|
|
81
|
-
totalTimeout = timeout;
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
return totalTimeout;
|
|
85
|
-
};
|
|
86
|
-
/**
|
|
87
|
-
* @method
|
|
88
|
-
* @param {number} timeout - timeout in milliseconds or 0 if no timeout
|
|
89
|
-
* @param {number} order - order defines the priority of timeout, timeouts set with lower order override those set with higher order.
|
|
90
|
-
* When order below 0 value of timeout only override if new value is lower
|
|
91
|
-
*/
|
|
92
|
-
this.setTimeout = function (timeout, order) {
|
|
93
|
-
timeouts.set(order, timeout);
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
this.setTrace();
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/** @function */
|
|
100
|
-
setTrace() {
|
|
101
|
-
Error.captureStackTrace(this);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/** @param {Array<*>} args */
|
|
105
|
-
setArguments(args) {
|
|
106
|
-
this.args = args;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* @param {...any} args
|
|
111
|
-
* @return {*}
|
|
112
|
-
*/
|
|
113
|
-
run() {
|
|
114
|
-
this.args = Array.prototype.slice.call(arguments);
|
|
115
|
-
if (store.dryRun) {
|
|
116
|
-
this.setStatus('success');
|
|
117
|
-
return Promise.resolve(new Proxy({}, dryRunResolver()));
|
|
118
|
-
}
|
|
119
|
-
let result;
|
|
120
|
-
try {
|
|
121
|
-
if (this.helperMethod !== 'say') {
|
|
122
|
-
result = this.helper[this.helperMethod].apply(this.helper, this.args);
|
|
123
|
-
}
|
|
124
|
-
this.setStatus('success');
|
|
125
|
-
} catch (err) {
|
|
126
|
-
this.setStatus('failed');
|
|
127
|
-
throw err;
|
|
128
|
-
}
|
|
129
|
-
return result;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/** @param {string} status */
|
|
133
|
-
setStatus(status) {
|
|
134
|
-
this.status = status;
|
|
135
|
-
if (this.metaStep) {
|
|
136
|
-
this.metaStep.setStatus(status);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/** @return {string} */
|
|
141
|
-
humanize() {
|
|
142
|
-
return humanizeString(this.name);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/** @return {string} */
|
|
146
|
-
humanizeArgs() {
|
|
147
|
-
return this.args.map((arg) => {
|
|
148
|
-
if (!arg) {
|
|
149
|
-
return '';
|
|
150
|
-
}
|
|
151
|
-
if (typeof arg === 'string') {
|
|
152
|
-
return `"${arg}"`;
|
|
153
|
-
}
|
|
154
|
-
if (Array.isArray(arg)) {
|
|
155
|
-
try {
|
|
156
|
-
const res = JSON.stringify(arg);
|
|
157
|
-
return res;
|
|
158
|
-
} catch (err) {
|
|
159
|
-
return `[${arg.toString()}]`;
|
|
160
|
-
}
|
|
161
|
-
} else if (typeof arg === 'function') {
|
|
162
|
-
return arg.toString();
|
|
163
|
-
} else if (typeof arg === 'undefined') {
|
|
164
|
-
return `${arg}`;
|
|
165
|
-
} else if (arg instanceof Secret) {
|
|
166
|
-
return arg.getMasked();
|
|
167
|
-
} else if (arg.toString && arg.toString() !== '[object Object]') {
|
|
168
|
-
return arg.toString();
|
|
169
|
-
} else if (typeof arg === 'object') {
|
|
170
|
-
const returnedArg = {};
|
|
171
|
-
for (const [key, value] of Object.entries(arg)) {
|
|
172
|
-
returnedArg[key] = value;
|
|
173
|
-
if (value instanceof Secret) returnedArg[key] = value.getMasked();
|
|
174
|
-
}
|
|
175
|
-
return JSON.stringify(returnedArg);
|
|
176
|
-
}
|
|
177
|
-
return arg;
|
|
178
|
-
}).join(', ');
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/** @return {string} */
|
|
182
|
-
line() {
|
|
183
|
-
const lines = this.stack.split('\n');
|
|
184
|
-
if (lines[STACK_LINE]) {
|
|
185
|
-
return lines[STACK_LINE].trim().replace(global.codecept_dir || '', '.').trim();
|
|
186
|
-
}
|
|
187
|
-
return '';
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/** @return {string} */
|
|
191
|
-
toString() {
|
|
192
|
-
return `${this.prefix}${this.actor} ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/** @return {string} */
|
|
196
|
-
toCode() {
|
|
197
|
-
return `${this.prefix}${this.actor}.${this.name}(${this.humanizeArgs()})${this.suffix}`;
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
isMetaStep() {
|
|
201
|
-
return this.constructor.name === 'MetaStep';
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
/** @return {boolean} */
|
|
205
|
-
hasBDDAncestor() {
|
|
206
|
-
let hasBDD = false;
|
|
207
|
-
let processingStep;
|
|
208
|
-
processingStep = this;
|
|
209
|
-
|
|
210
|
-
while (processingStep.metaStep) {
|
|
211
|
-
if (processingStep.metaStep.actor.match(/^(Given|When|Then|And)/)) {
|
|
212
|
-
hasBDD = true;
|
|
213
|
-
break;
|
|
214
|
-
} else {
|
|
215
|
-
processingStep = processingStep.metaStep;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
return hasBDD;
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/** @extends Step */
|
|
223
|
-
export class MetaStep extends Step {
|
|
224
|
-
constructor(obj, method) {
|
|
225
|
-
super(null, method);
|
|
226
|
-
this.actor = obj;
|
|
227
|
-
}
|
|
6
|
+
import BaseStep from './step/base.js'
|
|
7
|
+
import StepConfig from './step/config.js'
|
|
8
|
+
import Step from './step/helper.js'
|
|
228
9
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
isWithin() {
|
|
238
|
-
if (this.actor && this.actor.match && this.actor.match(/^(Within)/)) {
|
|
239
|
-
return true;
|
|
240
|
-
}
|
|
241
|
-
return false;
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
toString() {
|
|
245
|
-
const actorText = this.actor;
|
|
246
|
-
|
|
247
|
-
if (this.isBDD() || this.isWithin()) {
|
|
248
|
-
return `${this.prefix}${actorText} ${this.name} "${this.humanizeArgs()}${this.suffix}"`;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
if (actorText === 'I') {
|
|
252
|
-
return `${this.prefix}${actorText} ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`;
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
return `On ${this.prefix}${actorText}: ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
humanize() {
|
|
259
|
-
return humanizeString(this.name);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
setTrace() {
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
setContext(context) {
|
|
266
|
-
this.context = context;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/** @return {*} */
|
|
270
|
-
run(fn) {
|
|
271
|
-
this.status = 'queued';
|
|
272
|
-
this.setArguments(Array.from(arguments).slice(1));
|
|
273
|
-
let result;
|
|
274
|
-
|
|
275
|
-
const registerStep = (step) => {
|
|
276
|
-
this.metaStep = null;
|
|
277
|
-
step.metaStep = this;
|
|
278
|
-
};
|
|
279
|
-
event.dispatcher.prependListener(event.step.before, registerStep);
|
|
280
|
-
// Handle async and sync methods.
|
|
281
|
-
if (fn.constructor.name === 'AsyncFunction') {
|
|
282
|
-
result = fn.apply(this.context, this.args).then((result) => {
|
|
283
|
-
return result;
|
|
284
|
-
}).catch((error) => {
|
|
285
|
-
this.setStatus('failed');
|
|
286
|
-
throw error;
|
|
287
|
-
}).finally(() => {
|
|
288
|
-
this.endTime = Date.now();
|
|
289
|
-
event.dispatcher.removeListener(event.step.before, registerStep);
|
|
290
|
-
});
|
|
291
|
-
} else {
|
|
292
|
-
try {
|
|
293
|
-
this.startTime = Date.now();
|
|
294
|
-
result = fn.apply(this.context, this.args);
|
|
295
|
-
} catch (error) {
|
|
296
|
-
this.setStatus('failed');
|
|
297
|
-
throw error;
|
|
298
|
-
} finally {
|
|
299
|
-
this.endTime = Date.now();
|
|
300
|
-
event.dispatcher.removeListener(event.step.before, registerStep);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
return result;
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
Step.TIMEOUTS = {};
|
|
309
|
-
|
|
310
|
-
/** @type {Class<MetaStep>} */
|
|
311
|
-
Step.MetaStep = MetaStep;
|
|
312
|
-
|
|
313
|
-
function dryRunResolver() {
|
|
314
|
-
return {
|
|
315
|
-
get(target, prop) {
|
|
316
|
-
if (prop === 'toString') return () => '<VALUE>';
|
|
317
|
-
return new Proxy({}, dryRunResolver());
|
|
318
|
-
},
|
|
319
|
-
};
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
function humanizeString(string) {
|
|
323
|
-
// split strings by words, then make them all lowercase
|
|
324
|
-
const _result = string.replace(/([a-z](?=[A-Z]))/g, '$1 ')
|
|
325
|
-
.split(' ')
|
|
326
|
-
.map(word => word.toLowerCase());
|
|
10
|
+
/**
|
|
11
|
+
* MetaStep is a step that is used to wrap other steps.
|
|
12
|
+
* It is used to create a new step that is a combination of other steps.
|
|
13
|
+
* It is used to create a new step that is a combination of other steps.
|
|
14
|
+
*/
|
|
15
|
+
import MetaStep from './step/meta.js'
|
|
327
16
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Step used to execute a single function
|
|
19
|
+
*/
|
|
20
|
+
import FuncStep from './step/func.js'
|
|
331
21
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
}
|
|
22
|
+
export default Step
|
|
23
|
+
export { MetaStep, BaseStep, StepConfig, FuncStep }
|
package/lib/steps.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import StepConfig from './step/config.js'
|
|
2
|
+
import SectionClass from './step/section.js'
|
|
3
|
+
function stepOpts(opts = {}) {
|
|
4
|
+
return new StepConfig(opts)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function stepTimeout(timeout) {
|
|
8
|
+
return new StepConfig().timeout(timeout)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function stepRetry(retry) {
|
|
12
|
+
return new StepConfig().retry(retry)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function section(name) {
|
|
16
|
+
if (!name) return endSection()
|
|
17
|
+
return new SectionClass(name).start()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function endSection() {
|
|
21
|
+
return SectionClass.current().end()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Section function to be added here
|
|
25
|
+
|
|
26
|
+
const step = {
|
|
27
|
+
// steps.opts syntax
|
|
28
|
+
opts: stepOpts,
|
|
29
|
+
timeout: stepTimeout,
|
|
30
|
+
retry: stepRetry,
|
|
31
|
+
|
|
32
|
+
// one-function syntax
|
|
33
|
+
stepTimeout,
|
|
34
|
+
stepRetry,
|
|
35
|
+
stepOpts,
|
|
36
|
+
|
|
37
|
+
// sections
|
|
38
|
+
section,
|
|
39
|
+
endSection,
|
|
40
|
+
|
|
41
|
+
Section: section,
|
|
42
|
+
EndSection: endSection,
|
|
43
|
+
|
|
44
|
+
// shortcuts
|
|
45
|
+
Given: () => section('Given'),
|
|
46
|
+
When: () => section('When'),
|
|
47
|
+
Then: () => section('Then'),
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default step
|
|
51
|
+
|
|
52
|
+
// Named exports for ESM compatibility
|
|
53
|
+
export const Section = step.Section
|
|
54
|
+
export const EndSection = step.EndSection
|
package/lib/store.js
CHANGED
|
@@ -2,14 +2,45 @@
|
|
|
2
2
|
* global values for current session
|
|
3
3
|
* @namespace
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
5
|
+
const store = {
|
|
6
|
+
/**
|
|
7
|
+
* If we are in --debug mode
|
|
8
|
+
* @type {boolean}
|
|
9
|
+
*/
|
|
7
10
|
debugMode: false,
|
|
8
|
-
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Is timeouts enabled
|
|
14
|
+
* @type {boolean}
|
|
15
|
+
*/
|
|
9
16
|
timeouts: true,
|
|
10
|
-
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* If auto-retries are enabled by retryFailedStep plugin
|
|
20
|
+
* tryTo effect disables them
|
|
21
|
+
* @type {boolean}
|
|
22
|
+
*/
|
|
23
|
+
autoRetries: false,
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Tests are executed via dry-run
|
|
27
|
+
* @type {boolean}
|
|
28
|
+
*/
|
|
11
29
|
dryRun: false,
|
|
12
|
-
|
|
30
|
+
/**
|
|
31
|
+
* If we are in pause mode
|
|
32
|
+
* @type {boolean}
|
|
33
|
+
*/
|
|
34
|
+
onPause: false,
|
|
35
|
+
|
|
36
|
+
// current object states
|
|
37
|
+
|
|
38
|
+
/** @type {CodeceptJS.Test | null} */
|
|
39
|
+
currentTest: null,
|
|
40
|
+
/** @type {CodeceptJS.Step | null} */
|
|
41
|
+
currentStep: null,
|
|
42
|
+
/** @type {CodeceptJS.Suite | null} */
|
|
43
|
+
currentSuite: null,
|
|
44
|
+
}
|
|
13
45
|
|
|
14
|
-
|
|
15
|
-
export const timeouts = true;
|
|
46
|
+
export default store
|