codeceptjs 4.0.0-beta.2 → 4.0.0-beta.21
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 +73 -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 +765 -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 +54 -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/session.js
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
|
-
import recorder from './recorder.js'
|
|
2
|
-
import container from './container.js'
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import { isAsyncFunction } from './utils.js'
|
|
7
|
-
|
|
8
|
-
const sessionColors = [
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
'red',
|
|
12
|
-
'magenta',
|
|
13
|
-
'yellow',
|
|
14
|
-
];
|
|
15
|
-
|
|
16
|
-
const savedSessions = {};
|
|
1
|
+
import recorder from './recorder.js'
|
|
2
|
+
import container from './container.js'
|
|
3
|
+
import event from './event.js'
|
|
4
|
+
import output from './output.js'
|
|
5
|
+
import store from './store.js'
|
|
6
|
+
import { isAsyncFunction } from './utils.js'
|
|
7
|
+
|
|
8
|
+
const sessionColors = ['cyan', 'blue', 'red', 'magenta', 'yellow']
|
|
9
|
+
|
|
10
|
+
const savedSessions = {}
|
|
17
11
|
|
|
18
12
|
/**
|
|
19
13
|
* @param {CodeceptJS.LocatorOrString} sessionName
|
|
@@ -24,109 +18,121 @@ const savedSessions = {};
|
|
|
24
18
|
function session(sessionName, config, fn) {
|
|
25
19
|
if (typeof config === 'function') {
|
|
26
20
|
if (typeof fn === 'function') {
|
|
27
|
-
config = config()
|
|
21
|
+
config = config()
|
|
28
22
|
} else {
|
|
29
23
|
// no config but with function
|
|
30
|
-
fn = config
|
|
31
|
-
config = {}
|
|
24
|
+
fn = config
|
|
25
|
+
config = {}
|
|
32
26
|
}
|
|
33
27
|
}
|
|
34
28
|
// session helpers won't use beforeSuite and afterSuite hooks...
|
|
35
29
|
// restart: false options are not allowed as well
|
|
36
30
|
// but those helpers should be already started so inside listener/helpers.js the `_init` method should already be called
|
|
37
31
|
|
|
38
|
-
const helpers = container.helpers()
|
|
32
|
+
const helpers = container.helpers()
|
|
39
33
|
|
|
40
34
|
if (!savedSessions[sessionName]) {
|
|
41
35
|
for (const helper in helpers) {
|
|
42
|
-
if (!helpers[helper]._session) continue
|
|
36
|
+
if (!helpers[helper]._session) continue
|
|
43
37
|
savedSessions[sessionName] = {
|
|
44
38
|
start: () => null,
|
|
45
39
|
stop: () => null,
|
|
46
40
|
loadVars: () => null,
|
|
47
41
|
restoreVars: () => null,
|
|
48
42
|
...(store.dryRun ? {} : helpers[helper]._session()),
|
|
49
|
-
}
|
|
50
|
-
break
|
|
43
|
+
}
|
|
44
|
+
break
|
|
51
45
|
}
|
|
52
46
|
|
|
53
47
|
const closeBrowsers = () => {
|
|
54
|
-
const session = savedSessions[sessionName]
|
|
55
|
-
if (!session) return
|
|
56
|
-
session.stop(session.vars)
|
|
57
|
-
delete savedSessions[sessionName]
|
|
58
|
-
}
|
|
48
|
+
const session = savedSessions[sessionName]
|
|
49
|
+
if (!session) return
|
|
50
|
+
session.stop(session.vars)
|
|
51
|
+
delete savedSessions[sessionName]
|
|
52
|
+
}
|
|
59
53
|
|
|
60
54
|
event.dispatcher.once(event.test.after, () => {
|
|
61
|
-
recorder.add('close session browsers', closeBrowsers)
|
|
62
|
-
})
|
|
55
|
+
recorder.add('close session browsers', closeBrowsers)
|
|
56
|
+
})
|
|
63
57
|
|
|
64
58
|
if (!savedSessions[sessionName]) {
|
|
65
|
-
throw new Error('Configured helpers do not support starting sessions. Please use a helper with session support.')
|
|
59
|
+
throw new Error('Configured helpers do not support starting sessions. Please use a helper with session support.')
|
|
66
60
|
}
|
|
67
61
|
|
|
68
62
|
recorder.add('save vars', async () => {
|
|
69
|
-
savedSessions[sessionName].vars = await savedSessions[sessionName].start(sessionName, config)
|
|
70
|
-
})
|
|
63
|
+
savedSessions[sessionName].vars = await savedSessions[sessionName].start(sessionName, config)
|
|
64
|
+
})
|
|
71
65
|
}
|
|
72
66
|
|
|
73
67
|
// pick random color
|
|
74
|
-
const color = sessionColors[Object.keys(savedSessions).indexOf(sessionName) % sessionColors.length]
|
|
75
|
-
|
|
76
|
-
const addContextToStep = (step) => {
|
|
77
|
-
step.actor = `${output.colors[color](sessionName)}: I`;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
if (!fn) return; // no current session steps
|
|
81
|
-
|
|
82
|
-
return recorder.add('register session wrapper', async () => {
|
|
83
|
-
const session = savedSessions[sessionName];
|
|
84
|
-
recorder.session.start(`session:${sessionName}`);
|
|
85
|
-
event.dispatcher.on(event.step.after, addContextToStep);
|
|
86
|
-
recorder.add('switch to browser', () => {
|
|
87
|
-
const session = savedSessions[sessionName];
|
|
88
|
-
return session.loadVars(session.vars);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
const finalize = () => {
|
|
92
|
-
recorder.add('Finalize session', async () => {
|
|
93
|
-
output.output.stepShift = 0;
|
|
94
|
-
event.dispatcher.removeListener(event.step.after, addContextToStep);
|
|
95
|
-
await session.restoreVars();
|
|
96
|
-
recorder.session.restore(`session:${sessionName}`);
|
|
97
|
-
});
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
// Indicate when executing this function that we are in a session
|
|
101
|
-
if (isAsyncFunction(fn)) {
|
|
102
|
-
return fn.apply(null).then((res) => {
|
|
103
|
-
finalize();
|
|
104
|
-
return recorder.promise().then(() => res);
|
|
105
|
-
}).catch((e) => {
|
|
106
|
-
output.output.stepShift = 0;
|
|
107
|
-
session.restoreVars(sessionName);
|
|
108
|
-
event.dispatcher.removeListener(event.step.after, addContextToStep);
|
|
109
|
-
recorder.throw(e);
|
|
110
|
-
return recorder.promise();
|
|
111
|
-
});
|
|
112
|
-
}
|
|
68
|
+
const color = sessionColors[Object.keys(savedSessions).indexOf(sessionName) % sessionColors.length]
|
|
113
69
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
70
|
+
const addContextToStep = step => {
|
|
71
|
+
step.actor = `${output.colors[color](sessionName)}: I`
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!fn) return // no current session steps
|
|
75
|
+
|
|
76
|
+
return recorder.add(
|
|
77
|
+
'register session wrapper',
|
|
78
|
+
async () => {
|
|
79
|
+
const session = savedSessions[sessionName]
|
|
80
|
+
if (!session) {
|
|
81
|
+
throw new Error(`Session "${sessionName}" not found. It may have been closed already.`)
|
|
82
|
+
}
|
|
83
|
+
recorder.session.start(`session:${sessionName}`)
|
|
84
|
+
event.dispatcher.on(event.step.after, addContextToStep)
|
|
85
|
+
recorder.add('switch to browser', () => {
|
|
86
|
+
const session = savedSessions[sessionName]
|
|
87
|
+
return session.loadVars(session.vars)
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
const finalize = () => {
|
|
91
|
+
recorder.add('Finalize session', async () => {
|
|
92
|
+
output.stepShift = 0
|
|
93
|
+
event.dispatcher.removeListener(event.step.after, addContextToStep)
|
|
94
|
+
await session.restoreVars()
|
|
95
|
+
recorder.session.restore(`session:${sessionName}`)
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Indicate when executing this function that we are in a session
|
|
100
|
+
if (isAsyncFunction(fn)) {
|
|
101
|
+
return fn
|
|
102
|
+
.apply(null)
|
|
103
|
+
.then(async res => {
|
|
104
|
+
finalize()
|
|
105
|
+
await recorder.promise()
|
|
106
|
+
return res
|
|
107
|
+
})
|
|
108
|
+
.catch(e => {
|
|
109
|
+
output.stepShift = 0
|
|
110
|
+
session.restoreVars(sessionName)
|
|
111
|
+
event.dispatcher.removeListener(event.step.after, addContextToStep)
|
|
112
|
+
recorder.throw(e)
|
|
113
|
+
return recorder.promise()
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
let res
|
|
118
|
+
try {
|
|
119
|
+
res = fn.apply(null)
|
|
120
|
+
} catch (err) {
|
|
121
|
+
recorder.throw(err)
|
|
122
|
+
} finally {
|
|
123
|
+
recorder.catch(e => {
|
|
124
|
+
session.restoreVars(sessionName)
|
|
125
|
+
output.stepShift = 0
|
|
126
|
+
event.dispatcher.removeListener(event.step.after, addContextToStep)
|
|
127
|
+
throw e
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
finalize()
|
|
131
|
+
return recorder.promise().then(() => res)
|
|
132
|
+
},
|
|
133
|
+
false,
|
|
134
|
+
false,
|
|
135
|
+
)
|
|
130
136
|
}
|
|
131
137
|
|
|
132
|
-
export default session
|
|
138
|
+
export default session
|
package/lib/step/base.js
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import color from 'chalk'
|
|
2
|
+
import Secret from '../secret.js'
|
|
3
|
+
import { getCurrentTimeout } from '../timeout.js'
|
|
4
|
+
import { ucfirst, humanizeString, serializeError } from '../utils.js'
|
|
5
|
+
import recordStep from './record.js'
|
|
6
|
+
|
|
7
|
+
const STACK_LINE = 5
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Each command in test executed through `I.` object is wrapped in Step.
|
|
11
|
+
* Step allows logging executed commands and triggers hook before and after step execution.
|
|
12
|
+
* @param {string} name
|
|
13
|
+
*/
|
|
14
|
+
class Step {
|
|
15
|
+
constructor(name) {
|
|
16
|
+
/** @member {string} */
|
|
17
|
+
this.name = name
|
|
18
|
+
/** @member {Map<number, number>} */
|
|
19
|
+
this.timeouts = new Map()
|
|
20
|
+
|
|
21
|
+
/** @member {Array<*>} */
|
|
22
|
+
this.args = []
|
|
23
|
+
|
|
24
|
+
/** @member {Record<string,any>} */
|
|
25
|
+
this.opts = {}
|
|
26
|
+
/** @member {string} */
|
|
27
|
+
this.actor = 'I' // I = actor
|
|
28
|
+
|
|
29
|
+
/** @member {string} */
|
|
30
|
+
this.status = 'pending'
|
|
31
|
+
/** @member {string} */
|
|
32
|
+
this.prefix = this.suffix = ''
|
|
33
|
+
/** @member {string} */
|
|
34
|
+
this.comment = ''
|
|
35
|
+
/** @member {any} */
|
|
36
|
+
this.metaStep = undefined
|
|
37
|
+
/** @member {string} */
|
|
38
|
+
this.stack = ''
|
|
39
|
+
|
|
40
|
+
// These are part of HelperStep class
|
|
41
|
+
// but left here for types compatibility
|
|
42
|
+
/** @member {any} */
|
|
43
|
+
this.helper = null
|
|
44
|
+
/** @member {string} */
|
|
45
|
+
this.helperMethod = name
|
|
46
|
+
|
|
47
|
+
this.startTime = 0
|
|
48
|
+
this.endTime = 0
|
|
49
|
+
|
|
50
|
+
this.setTrace()
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
setMetaStep(metaStep) {
|
|
54
|
+
this.metaStep = metaStep
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
run() {
|
|
58
|
+
throw new Error('Not implemented')
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
addToRecorder(args) {
|
|
62
|
+
return recordStep(this, args)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @returns {number|undefined}
|
|
67
|
+
*/
|
|
68
|
+
get timeout() {
|
|
69
|
+
return getCurrentTimeout(this.timeouts)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* @param {number} timeout - timeout in milliseconds or 0 if no timeout
|
|
74
|
+
* @param {number} order - order defines the priority of timeout, timeouts set with lower order override those set with higher order.
|
|
75
|
+
* When order below 0 value of timeout only override if new value is lower
|
|
76
|
+
*/
|
|
77
|
+
setTimeout(timeout, order) {
|
|
78
|
+
this.timeouts.set(order, timeout)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** @function */
|
|
82
|
+
setTrace() {
|
|
83
|
+
Error.captureStackTrace(this)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** @param {Array<*>} args */
|
|
87
|
+
setArguments(args) {
|
|
88
|
+
this.args = args
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
setActor(actor) {
|
|
92
|
+
this.actor = actor || ''
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** @param {string} status */
|
|
96
|
+
setStatus(status) {
|
|
97
|
+
this.status = status
|
|
98
|
+
if (this.metaStep) {
|
|
99
|
+
this.metaStep.setStatus(status)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** @return {string} */
|
|
104
|
+
humanize() {
|
|
105
|
+
return humanizeString(this.name)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** @return {string} */
|
|
109
|
+
humanizeArgs() {
|
|
110
|
+
return this.args
|
|
111
|
+
.map(arg => {
|
|
112
|
+
if (!arg) {
|
|
113
|
+
return ''
|
|
114
|
+
}
|
|
115
|
+
if (typeof arg === 'string') {
|
|
116
|
+
return `"${arg}"`
|
|
117
|
+
}
|
|
118
|
+
if (Array.isArray(arg)) {
|
|
119
|
+
try {
|
|
120
|
+
const res = JSON.stringify(arg)
|
|
121
|
+
return res
|
|
122
|
+
} catch (err) {
|
|
123
|
+
return `[${arg.toString()}]`
|
|
124
|
+
}
|
|
125
|
+
} else if (typeof arg === 'function') {
|
|
126
|
+
return arg.toString()
|
|
127
|
+
} else if (typeof arg === 'undefined') {
|
|
128
|
+
return `${arg}`
|
|
129
|
+
} else if (arg instanceof Secret) {
|
|
130
|
+
return arg.getMasked()
|
|
131
|
+
} else if (arg.toString && arg.toString() !== '[object Object]') {
|
|
132
|
+
return arg.toString()
|
|
133
|
+
} else if (typeof arg === 'object') {
|
|
134
|
+
const returnedArg = {}
|
|
135
|
+
for (const [key, value] of Object.entries(arg)) {
|
|
136
|
+
returnedArg[key] = value
|
|
137
|
+
if (value instanceof Secret) returnedArg[key] = value.getMasked()
|
|
138
|
+
}
|
|
139
|
+
return JSON.stringify(returnedArg)
|
|
140
|
+
}
|
|
141
|
+
return arg
|
|
142
|
+
})
|
|
143
|
+
.join(', ')
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/** @return {string} */
|
|
147
|
+
line() {
|
|
148
|
+
const lines = this.stack.split('\n')
|
|
149
|
+
if (lines[STACK_LINE]) {
|
|
150
|
+
return lines[STACK_LINE].trim()
|
|
151
|
+
.replace(global.codecept_dir || '', '.')
|
|
152
|
+
.trim()
|
|
153
|
+
}
|
|
154
|
+
return ''
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** @return {string} */
|
|
158
|
+
toString() {
|
|
159
|
+
return ucfirst(`${this.prefix}${this.actor} ${this.humanize()} ${this.humanizeArgs()}${this.suffix}`).trim()
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** @return {string} */
|
|
163
|
+
toCliStyled() {
|
|
164
|
+
return `${this.prefix}${this.actor} ${color.italic(this.humanize())} ${color.yellow(this.humanizeArgs())}${this.suffix}`
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** @return {string} */
|
|
168
|
+
toCode() {
|
|
169
|
+
return `${this.prefix}${this.actor}.${this.name}(${this.humanizeArgs()})${this.suffix}`
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
isMetaStep() {
|
|
173
|
+
return this.constructor.name === 'MetaStep'
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
get duration() {
|
|
177
|
+
if (!this.startTime || !this.endTime) return 0
|
|
178
|
+
return this.endTime - this.startTime
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
simplify() {
|
|
182
|
+
const step = this
|
|
183
|
+
|
|
184
|
+
const parent = {}
|
|
185
|
+
if (step.metaStep) {
|
|
186
|
+
parent.title = step.metaStep.actor
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (step.opts) {
|
|
190
|
+
Object.keys(step.opts).forEach(k => {
|
|
191
|
+
if (typeof step.opts[k] === 'object') delete step.opts[k]
|
|
192
|
+
if (typeof step.opts[k] === 'function') delete step.opts[k]
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const args = []
|
|
197
|
+
if (step.args) {
|
|
198
|
+
for (const arg of step.args) {
|
|
199
|
+
// check if arg is a JOI object
|
|
200
|
+
if (arg && typeof arg === 'function') {
|
|
201
|
+
args.push(arg.name)
|
|
202
|
+
} else if (typeof arg == 'string') {
|
|
203
|
+
args.push(arg)
|
|
204
|
+
} else if (arg) {
|
|
205
|
+
args.push((JSON.stringify(arg) || '').slice(0, 300))
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
opts: step.opts || {},
|
|
212
|
+
title: step.name,
|
|
213
|
+
args: args,
|
|
214
|
+
status: step.status,
|
|
215
|
+
startTime: step.startTime,
|
|
216
|
+
endTime: step.endTime,
|
|
217
|
+
parent,
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** @return {boolean} */
|
|
222
|
+
hasBDDAncestor() {
|
|
223
|
+
let hasBDD = false
|
|
224
|
+
let processingStep
|
|
225
|
+
processingStep = this
|
|
226
|
+
|
|
227
|
+
while (processingStep.metaStep) {
|
|
228
|
+
if (processingStep.metaStep.actor?.match(/^(Given|When|Then|And|But)/)) {
|
|
229
|
+
hasBDD = true
|
|
230
|
+
break
|
|
231
|
+
} else {
|
|
232
|
+
processingStep = processingStep.metaStep
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return hasBDD
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
export default Step
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StepConfig is a configuration object for a step.
|
|
3
|
+
* It is used to create a new step that is a combination of other steps.
|
|
4
|
+
*/
|
|
5
|
+
class StepConfig {
|
|
6
|
+
constructor(opts = {}) {
|
|
7
|
+
/** @member {{ opts: Record<string, any>, timeout: number|undefined, retry: number|undefined }} */
|
|
8
|
+
this.config = {
|
|
9
|
+
opts,
|
|
10
|
+
timeout: undefined,
|
|
11
|
+
retry: undefined,
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Set the options for the step.
|
|
17
|
+
* @param {object} opts - The options for the step.
|
|
18
|
+
* @returns {StepConfig} - The step configuration object.
|
|
19
|
+
*/
|
|
20
|
+
opts(opts) {
|
|
21
|
+
this.config.opts = opts
|
|
22
|
+
return this
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Set the timeout for the step.
|
|
27
|
+
* @param {number} timeout - The timeout for the step.
|
|
28
|
+
* @returns {StepConfig} - The step configuration object.
|
|
29
|
+
*/
|
|
30
|
+
timeout(timeout) {
|
|
31
|
+
this.config.timeout = timeout
|
|
32
|
+
return this
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Set the retry for the step.
|
|
37
|
+
* @param {number} retry - The retry for the step.
|
|
38
|
+
* @returns {StepConfig} - The step configuration object.
|
|
39
|
+
*/
|
|
40
|
+
retry(retry) {
|
|
41
|
+
this.config.retry = retry
|
|
42
|
+
return this
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
getConfig() {
|
|
46
|
+
return this.config
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default StepConfig
|
package/lib/step/func.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import BaseStep from './base.js'
|
|
2
|
+
import store from '../store.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Function executed as a step
|
|
6
|
+
*/
|
|
7
|
+
class FuncStep extends BaseStep {
|
|
8
|
+
// this is actual function that should be executed within step
|
|
9
|
+
setCallable(fn) {
|
|
10
|
+
this.fn = fn
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// helper is optional, if we need to allow step to access helper methods
|
|
14
|
+
setHelper(helper) {
|
|
15
|
+
this.helper = helper
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
run() {
|
|
19
|
+
if (!this.fn) throw new Error('Function is not set')
|
|
20
|
+
|
|
21
|
+
// we wrap that function to track time and status
|
|
22
|
+
// and disable it in dry run mode
|
|
23
|
+
this.args = Array.prototype.slice.call(arguments)
|
|
24
|
+
this.startTime = +Date.now()
|
|
25
|
+
|
|
26
|
+
if (store.dryRun) {
|
|
27
|
+
this.setStatus('success')
|
|
28
|
+
// should we add Proxy and dry run resolver here?
|
|
29
|
+
return Promise.resolve(true)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let result
|
|
33
|
+
try {
|
|
34
|
+
result = this.fn.apply(this.helper, this.args)
|
|
35
|
+
this.setStatus('success')
|
|
36
|
+
this.endTime = +Date.now()
|
|
37
|
+
} catch (err) {
|
|
38
|
+
this.endTime = +Date.now()
|
|
39
|
+
this.setStatus('failed')
|
|
40
|
+
throw err
|
|
41
|
+
}
|
|
42
|
+
return result
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export default FuncStep
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import Step from './base.js'
|
|
2
|
+
import store from '../store.js'
|
|
3
|
+
|
|
4
|
+
class HelperStep extends Step {
|
|
5
|
+
constructor(helper, name) {
|
|
6
|
+
super(name)
|
|
7
|
+
/** @member {CodeceptJS.Helper} helper corresponding helper */
|
|
8
|
+
this.helper = helper
|
|
9
|
+
/** @member {string} helperMethod name of method to be executed */
|
|
10
|
+
this.helperMethod = name
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {...any} args
|
|
15
|
+
* @return {*}
|
|
16
|
+
*/
|
|
17
|
+
run() {
|
|
18
|
+
this.args = Array.prototype.slice.call(arguments)
|
|
19
|
+
this.startTime = +Date.now()
|
|
20
|
+
|
|
21
|
+
if (store.dryRun) {
|
|
22
|
+
this.setStatus('success')
|
|
23
|
+
return Promise.resolve(new Proxy({}, dryRunResolver()))
|
|
24
|
+
}
|
|
25
|
+
let result
|
|
26
|
+
try {
|
|
27
|
+
if (this.helperMethod !== 'say') {
|
|
28
|
+
result = this.helper[this.helperMethod].apply(this.helper, this.args)
|
|
29
|
+
}
|
|
30
|
+
this.setStatus('success')
|
|
31
|
+
this.endTime = +Date.now()
|
|
32
|
+
} catch (err) {
|
|
33
|
+
this.endTime = +Date.now()
|
|
34
|
+
this.setStatus('failed')
|
|
35
|
+
throw err
|
|
36
|
+
}
|
|
37
|
+
return result
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export default HelperStep
|
|
42
|
+
|
|
43
|
+
function dryRunResolver() {
|
|
44
|
+
return {
|
|
45
|
+
get(target, prop) {
|
|
46
|
+
if (prop === 'toString') return () => '<VALUE>'
|
|
47
|
+
return new Proxy({}, dryRunResolver())
|
|
48
|
+
},
|
|
49
|
+
}
|
|
50
|
+
}
|