codeceptjs 4.0.0-beta.5 → 4.0.0-beta.6.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 +0 -45
- package/bin/codecept.js +46 -57
- package/lib/actor.js +15 -11
- package/lib/ai.js +6 -5
- package/lib/assert/empty.js +9 -8
- package/lib/assert/equal.js +15 -17
- package/lib/assert/error.js +2 -2
- package/lib/assert/include.js +9 -11
- package/lib/assert/throws.js +1 -1
- package/lib/assert/truth.js +8 -5
- package/lib/assert.js +18 -18
- package/lib/codecept.js +66 -107
- package/lib/colorUtils.js +48 -50
- package/lib/command/check.js +32 -27
- package/lib/command/configMigrate.js +11 -10
- package/lib/command/definitions.js +16 -10
- package/lib/command/dryRun.js +16 -16
- package/lib/command/generate.js +29 -26
- package/lib/command/gherkin/init.js +36 -38
- package/lib/command/gherkin/snippets.js +14 -14
- package/lib/command/gherkin/steps.js +21 -18
- package/lib/command/info.js +8 -8
- package/lib/command/init.js +34 -31
- package/lib/command/interactive.js +11 -10
- package/lib/command/list.js +10 -9
- package/lib/command/run-multiple/chunk.js +5 -5
- package/lib/command/run-multiple/collection.js +5 -5
- package/lib/command/run-multiple/run.js +3 -3
- package/lib/command/run-multiple.js +16 -13
- package/lib/command/run-rerun.js +6 -7
- package/lib/command/run-workers.js +10 -24
- package/lib/command/run.js +8 -8
- package/lib/command/utils.js +20 -18
- package/lib/command/workers/runTests.js +117 -269
- package/lib/config.js +111 -49
- package/lib/container.js +299 -102
- package/lib/data/context.js +6 -5
- package/lib/data/dataScenarioConfig.js +1 -1
- package/lib/data/dataTableArgument.js +1 -1
- package/lib/data/table.js +1 -1
- package/lib/effects.js +94 -10
- package/lib/els.js +11 -9
- package/lib/event.js +11 -10
- package/lib/globals.js +141 -0
- package/lib/heal.js +12 -12
- package/lib/helper/AI.js +1 -1
- package/lib/helper/ApiDataFactory.js +16 -13
- package/lib/helper/FileSystem.js +32 -12
- package/lib/helper/GraphQL.js +1 -1
- package/lib/helper/GraphQLDataFactory.js +1 -1
- package/lib/helper/JSONResponse.js +19 -30
- package/lib/helper/Mochawesome.js +9 -28
- package/lib/helper/Playwright.js +668 -265
- package/lib/helper/Puppeteer.js +284 -169
- package/lib/helper/REST.js +29 -12
- package/lib/helper/WebDriver.js +191 -71
- 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/PlaywrightRestartOpts.js +23 -23
- package/lib/helper/extras/Popup.js +1 -1
- package/lib/helper/extras/React.js +29 -30
- package/lib/helper/network/actions.js +33 -48
- package/lib/helper/network/utils.js +76 -83
- package/lib/helper/scripts/blurElement.js +6 -6
- package/lib/helper/scripts/focusElement.js +6 -6
- package/lib/helper/scripts/highlightElement.js +9 -9
- package/lib/helper/scripts/isElementClickable.js +34 -34
- package/lib/helper.js +2 -1
- package/lib/history.js +23 -20
- package/lib/hooks.js +10 -10
- package/lib/html.js +90 -100
- package/lib/index.js +48 -21
- package/lib/listener/config.js +8 -9
- package/lib/listener/emptyRun.js +6 -7
- package/lib/listener/exit.js +4 -3
- package/lib/listener/globalRetry.js +5 -5
- package/lib/listener/globalTimeout.js +11 -10
- package/lib/listener/helpers.js +33 -14
- package/lib/listener/mocha.js +3 -4
- package/lib/listener/result.js +4 -5
- package/lib/listener/steps.js +7 -18
- package/lib/listener/store.js +3 -3
- package/lib/locator.js +213 -192
- package/lib/mocha/asyncWrapper.js +108 -75
- package/lib/mocha/bdd.js +99 -13
- package/lib/mocha/cli.js +60 -27
- package/lib/mocha/factory.js +75 -19
- package/lib/mocha/featureConfig.js +1 -1
- package/lib/mocha/gherkin.js +57 -25
- package/lib/mocha/hooks.js +12 -3
- package/lib/mocha/index.js +13 -4
- package/lib/mocha/inject.js +22 -5
- package/lib/mocha/scenarioConfig.js +2 -2
- package/lib/mocha/suite.js +9 -2
- package/lib/mocha/test.js +10 -13
- package/lib/mocha/ui.js +28 -31
- package/lib/output.js +11 -9
- package/lib/parser.js +44 -44
- package/lib/pause.js +15 -16
- package/lib/plugin/analyze.js +19 -12
- package/lib/plugin/auth.js +20 -21
- package/lib/plugin/autoDelay.js +12 -8
- package/lib/plugin/coverage.js +12 -8
- package/lib/plugin/customLocator.js +3 -3
- package/lib/plugin/customReporter.js +3 -2
- package/lib/plugin/heal.js +14 -9
- package/lib/plugin/pageInfo.js +10 -10
- package/lib/plugin/pauseOnFail.js +4 -3
- package/lib/plugin/retryFailedStep.js +47 -5
- package/lib/plugin/screenshotOnFail.js +75 -37
- package/lib/plugin/stepByStepReport.js +14 -14
- package/lib/plugin/stepTimeout.js +4 -3
- package/lib/plugin/subtitles.js +6 -5
- package/lib/recorder.js +33 -23
- package/lib/rerun.js +69 -26
- package/lib/result.js +4 -4
- package/lib/secret.js +18 -17
- package/lib/session.js +95 -89
- package/lib/step/base.js +6 -6
- package/lib/step/config.js +1 -1
- package/lib/step/func.js +3 -3
- package/lib/step/helper.js +3 -3
- package/lib/step/meta.js +4 -4
- package/lib/step/record.js +11 -11
- package/lib/step/retry.js +3 -3
- package/lib/step/section.js +3 -3
- package/lib/step.js +7 -10
- package/lib/steps.js +9 -5
- package/lib/store.js +1 -1
- package/lib/timeout.js +1 -7
- package/lib/transform.js +8 -8
- package/lib/translation.js +32 -18
- package/lib/utils.js +68 -97
- package/lib/workerStorage.js +16 -17
- package/lib/workers.js +145 -171
- package/package.json +63 -57
- package/translations/de-DE.js +2 -2
- package/translations/fr-FR.js +2 -2
- package/translations/index.js +23 -10
- package/translations/it-IT.js +2 -2
- package/translations/ja-JP.js +2 -2
- package/translations/nl-NL.js +2 -2
- package/translations/pl-PL.js +2 -2
- package/translations/pt-BR.js +2 -2
- package/translations/ru-RU.js +2 -2
- package/translations/utils.js +11 -2
- package/translations/zh-CN.js +2 -2
- package/translations/zh-TW.js +2 -2
- package/typings/index.d.ts +7 -18
- package/typings/promiseBasedTypes.d.ts +3769 -5450
- package/typings/types.d.ts +3953 -5778
- package/bin/test-server.js +0 -53
- package/lib/element/WebElement.js +0 -327
- package/lib/helper/Nightmare.js +0 -1486
- package/lib/helper/Protractor.js +0 -1840
- package/lib/helper/TestCafe.js +0 -1391
- package/lib/helper/clientscripts/nightmare.js +0 -213
- package/lib/helper/extras/PlaywrightReactVueLocator.js +0 -43
- package/lib/helper/testcafe/testControllerHolder.js +0 -42
- package/lib/helper/testcafe/testcafe-utils.js +0 -61
- package/lib/listener/retryEnhancer.js +0 -85
- package/lib/plugin/allure.js +0 -15
- package/lib/plugin/autoLogin.js +0 -5
- package/lib/plugin/commentStep.js +0 -141
- package/lib/plugin/eachElement.js +0 -127
- package/lib/plugin/fakerTransform.js +0 -49
- package/lib/plugin/htmlReporter.js +0 -1947
- package/lib/plugin/retryTo.js +0 -16
- package/lib/plugin/selenoid.js +0 -364
- package/lib/plugin/standardActingHelpers.js +0 -6
- package/lib/plugin/tryTo.js +0 -16
- package/lib/plugin/wdio.js +0 -247
- package/lib/test-server.js +0 -323
- package/lib/within.js +0 -90
package/lib/recorder.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
const
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import debugModule from 'debug'
|
|
2
|
+
const debug = debugModule('codeceptjs:recorder')
|
|
3
|
+
import promiseRetry from 'promise-retry'
|
|
4
|
+
import chalk from 'chalk'
|
|
5
|
+
import { printObjectProperties } from './utils.js'
|
|
6
|
+
import output from './output.js'
|
|
7
|
+
import { TimeoutError } from './timeout.js'
|
|
7
8
|
const MAX_TASKS = 100
|
|
8
9
|
|
|
9
10
|
let promise
|
|
@@ -28,7 +29,7 @@ const defaultRetryOptions = {
|
|
|
28
29
|
* @alias recorder
|
|
29
30
|
* @interface
|
|
30
31
|
*/
|
|
31
|
-
|
|
32
|
+
export default {
|
|
32
33
|
/**
|
|
33
34
|
* @type {Array<Object<string, *>>}
|
|
34
35
|
* @inner
|
|
@@ -90,7 +91,7 @@ module.exports = {
|
|
|
90
91
|
queueId++
|
|
91
92
|
sessionId = null
|
|
92
93
|
asyncErr = null
|
|
93
|
-
log(`${currentQueue()} Starting recording promises`)
|
|
94
|
+
output.log(`${currentQueue()} Starting recording promises`)
|
|
94
95
|
promise = Promise.resolve()
|
|
95
96
|
oldPromises = []
|
|
96
97
|
tasks = []
|
|
@@ -201,7 +202,7 @@ module.exports = {
|
|
|
201
202
|
|
|
202
203
|
const retryRules = this.retries.slice().reverse()
|
|
203
204
|
return promiseRetry(Object.assign(defaultRetryOptions, retryOpts), (retry, number) => {
|
|
204
|
-
if (number > 1) log(`${currentQueue()}Retrying... Attempt #${number}`)
|
|
205
|
+
if (number > 1) output.log(`${currentQueue()}Retrying... Attempt #${number}`)
|
|
205
206
|
const [promise, timer] = getTimeoutPromise(timeout, taskName)
|
|
206
207
|
return Promise.race([promise, Promise.resolve(res).then(fn)])
|
|
207
208
|
.finally(() => clearTimeout(timer))
|
|
@@ -231,7 +232,9 @@ module.exports = {
|
|
|
231
232
|
if (Number.isInteger(opts)) {
|
|
232
233
|
opts = { retries: opts }
|
|
233
234
|
}
|
|
234
|
-
|
|
235
|
+
// Push retry options immediately so first step can see them
|
|
236
|
+
this.retries.push(opts)
|
|
237
|
+
return Promise.resolve()
|
|
235
238
|
},
|
|
236
239
|
|
|
237
240
|
/**
|
|
@@ -246,8 +249,9 @@ module.exports = {
|
|
|
246
249
|
.replace(/\n/g, ' ')
|
|
247
250
|
?.slice(0, 50)
|
|
248
251
|
debug(chalk.gray(`${currentQueue()} Queued | catch with error handler ${fnDescription || ''}`))
|
|
252
|
+
if (!promise) promise = Promise.resolve()
|
|
249
253
|
return (promise = promise.catch(err => {
|
|
250
|
-
log(`${currentQueue()}Error | ${err} ${fnDescription}...`)
|
|
254
|
+
output.log(`${currentQueue()}Error | ${err} ${fnDescription}...`)
|
|
251
255
|
if (!(err instanceof Error)) {
|
|
252
256
|
// strange things may happen
|
|
253
257
|
err = new Error(`[Wrapped Error] ${printObjectProperties(err)}`) // we should be prepared for them
|
|
@@ -272,15 +276,30 @@ module.exports = {
|
|
|
272
276
|
?.replace(/\s{2,}/g, ' ')
|
|
273
277
|
.replace(/\n/g, ' ')
|
|
274
278
|
?.slice(0, 50)
|
|
279
|
+
if (!promise) promise = Promise.resolve()
|
|
275
280
|
return (promise = promise.catch(err => {
|
|
276
281
|
if (ignoredErrs.includes(err)) return // already caught
|
|
277
|
-
|
|
282
|
+
|
|
283
|
+
// Handle terminal errors - don't continue, re-throw immediately
|
|
284
|
+
if (err && (err.isTerminal || (err.message && (err.message.includes('ERR_ABORTED') || err.message.includes('frame was detached') || err.message.includes('Target page, context or browser has been closed'))))) {
|
|
285
|
+
output.log(`${currentQueue()} Terminal Error | ${err} | ${fnDescription || ''}...`)
|
|
286
|
+
throw err // Re-throw terminal errors immediately
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
output.log(`${currentQueue()} Error (Non-Terminated) | ${err} | ${fnDescription || ''}...`)
|
|
278
290
|
if (!(err instanceof Error)) {
|
|
279
291
|
// strange things may happen
|
|
280
292
|
err = new Error(`[Wrapped Error] ${JSON.stringify(err)}`) // we should be prepared for them
|
|
281
293
|
}
|
|
282
294
|
if (customErrFn) {
|
|
283
|
-
|
|
295
|
+
try {
|
|
296
|
+
const result = customErrFn(err)
|
|
297
|
+
// If customErrFn returns a value (not throwing), treat it as handled
|
|
298
|
+
return result
|
|
299
|
+
} catch (thrownErr) {
|
|
300
|
+
// If customErrFn throws an error, propagate it up
|
|
301
|
+
throw thrownErr
|
|
302
|
+
}
|
|
284
303
|
}
|
|
285
304
|
}))
|
|
286
305
|
},
|
|
@@ -338,7 +357,7 @@ module.exports = {
|
|
|
338
357
|
*/
|
|
339
358
|
stop() {
|
|
340
359
|
debug(this.toString())
|
|
341
|
-
log(`${currentQueue()} Stopping recording promises`)
|
|
360
|
+
output.log(`${currentQueue()} Stopping recording promises`)
|
|
342
361
|
running = false
|
|
343
362
|
},
|
|
344
363
|
|
|
@@ -379,15 +398,6 @@ module.exports = {
|
|
|
379
398
|
toString() {
|
|
380
399
|
return `Queue: ${currentQueue()}\n\nTasks: ${this.scheduled()}`
|
|
381
400
|
},
|
|
382
|
-
|
|
383
|
-
/**
|
|
384
|
-
* Get current session ID
|
|
385
|
-
* @return {string|null}
|
|
386
|
-
* @inner
|
|
387
|
-
*/
|
|
388
|
-
getCurrentSessionId() {
|
|
389
|
-
return sessionId
|
|
390
|
-
},
|
|
391
401
|
}
|
|
392
402
|
|
|
393
403
|
function getTimeoutPromise(timeoutMs, taskName) {
|
package/lib/rerun.js
CHANGED
|
@@ -1,34 +1,77 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import fsPath from 'path'
|
|
2
|
+
import container from './container.js'
|
|
3
|
+
import event from './event.js'
|
|
4
|
+
import BaseCodecept from './codecept.js'
|
|
5
|
+
import output from './output.js'
|
|
6
|
+
import { createRequire } from 'module'
|
|
7
|
+
|
|
8
|
+
const require = createRequire(import.meta.url)
|
|
6
9
|
|
|
7
10
|
class CodeceptRerunner extends BaseCodecept {
|
|
8
|
-
runOnce(test) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
mocha.files = mocha.files.filter(t => fsPath.basename(t, '.js') === test || t === test)
|
|
22
|
-
}
|
|
11
|
+
async runOnce(test) {
|
|
12
|
+
await container.started()
|
|
13
|
+
|
|
14
|
+
// Ensure translations are loaded for Gherkin features
|
|
15
|
+
try {
|
|
16
|
+
const { loadTranslations } = await import('./mocha/gherkin.js')
|
|
17
|
+
await loadTranslations()
|
|
18
|
+
} catch (e) {
|
|
19
|
+
// Ignore if gherkin module not available
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return new Promise(async (resolve, reject) => {
|
|
23
23
|
try {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
// Create a fresh Mocha instance for each run
|
|
25
|
+
// @ts-ignore
|
|
26
|
+
container.createMocha()
|
|
27
|
+
const mocha = container.mocha()
|
|
28
|
+
|
|
29
|
+
let filesToRun = this.testFiles
|
|
30
|
+
if (test) {
|
|
31
|
+
if (!fsPath.isAbsolute(test)) {
|
|
32
|
+
test = fsPath.join(global.codecept_dir, test)
|
|
33
|
+
}
|
|
34
|
+
filesToRun = this.testFiles.filter(t => fsPath.basename(t, '.js') === test || t === test)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Clear any existing tests/suites
|
|
38
|
+
mocha.suite.suites = []
|
|
39
|
+
mocha.suite.tests = []
|
|
40
|
+
|
|
41
|
+
// Manually load each test file by importing it
|
|
42
|
+
for (const file of filesToRun) {
|
|
43
|
+
try {
|
|
44
|
+
// Clear CommonJS cache if available (for mixed environments)
|
|
45
|
+
try {
|
|
46
|
+
delete require.cache[file]
|
|
47
|
+
} catch (e) {
|
|
48
|
+
// ESM modules don't have require.cache, ignore
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Force reload the module by using a cache-busting query parameter
|
|
52
|
+
const fileUrl = `${fsPath.resolve(file)}?t=${Date.now()}`
|
|
53
|
+
await import(fileUrl)
|
|
54
|
+
} catch (e) {
|
|
55
|
+
console.error(`Error loading test file ${file}:`, e)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const done = () => {
|
|
60
|
+
event.emit(event.all.result, container.result())
|
|
61
|
+
event.emit(event.all.after, this)
|
|
62
|
+
|
|
63
|
+
// Check if there were any failures
|
|
64
|
+
if (container.result().hasFailed) {
|
|
65
|
+
reject(new Error('Test run failed'))
|
|
27
66
|
} else {
|
|
28
|
-
|
|
67
|
+
resolve(undefined)
|
|
29
68
|
}
|
|
30
|
-
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
event.emit(event.all.before, this)
|
|
72
|
+
mocha.run(() => done())
|
|
31
73
|
} catch (e) {
|
|
74
|
+
output.error(e.stack)
|
|
32
75
|
reject(e)
|
|
33
76
|
}
|
|
34
77
|
})
|
|
@@ -79,4 +122,4 @@ class CodeceptRerunner extends BaseCodecept {
|
|
|
79
122
|
}
|
|
80
123
|
}
|
|
81
124
|
|
|
82
|
-
|
|
125
|
+
export default CodeceptRerunner
|
package/lib/result.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import { serializeTest } from './mocha/test.js'
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Result of the test run
|
|
@@ -158,4 +158,4 @@ class Result {
|
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
-
|
|
161
|
+
export default Result
|
package/lib/secret.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
import { deepClone } from './utils.js'
|
|
2
2
|
|
|
3
|
-
const maskedString = '*****'
|
|
3
|
+
const maskedString = '*****'
|
|
4
4
|
|
|
5
5
|
/** @param {string} secret */
|
|
6
6
|
class Secret {
|
|
7
7
|
constructor(secret) {
|
|
8
|
-
this._secret = secret
|
|
8
|
+
this._secret = secret
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
/** @returns {string} */
|
|
12
12
|
toString() {
|
|
13
|
-
return this._secret
|
|
13
|
+
return this._secret
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
getMasked() {
|
|
17
|
-
return maskedString
|
|
17
|
+
return maskedString
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -23,11 +23,11 @@ class Secret {
|
|
|
23
23
|
*/
|
|
24
24
|
static secret(secret) {
|
|
25
25
|
if (typeof secret === 'object') {
|
|
26
|
-
const fields = Array.from(arguments)
|
|
27
|
-
fields.shift()
|
|
28
|
-
return secretObject(secret, fields)
|
|
26
|
+
const fields = Array.from(arguments)
|
|
27
|
+
fields.shift()
|
|
28
|
+
return secretObject(secret, fields)
|
|
29
29
|
}
|
|
30
|
-
return new Secret(secret)
|
|
30
|
+
return new Secret(secret)
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -36,16 +36,17 @@ function secretObject(obj, fieldsToHide = []) {
|
|
|
36
36
|
get(obj, prop) {
|
|
37
37
|
if (prop === 'toString') {
|
|
38
38
|
return function () {
|
|
39
|
-
const maskedObject = deepClone(obj)
|
|
40
|
-
fieldsToHide.forEach(f => (maskedObject[f] = maskedString))
|
|
41
|
-
return JSON.stringify(maskedObject)
|
|
42
|
-
}
|
|
39
|
+
const maskedObject = deepClone(obj)
|
|
40
|
+
fieldsToHide.forEach(f => (maskedObject[f] = maskedString))
|
|
41
|
+
return JSON.stringify(maskedObject)
|
|
42
|
+
}
|
|
43
43
|
}
|
|
44
|
-
return fieldsToHide.includes(prop) ? new Secret(obj[prop]) : obj[prop]
|
|
44
|
+
return fieldsToHide.includes(prop) ? new Secret(obj[prop]) : obj[prop]
|
|
45
45
|
},
|
|
46
|
-
}
|
|
46
|
+
}
|
|
47
47
|
|
|
48
|
-
return new Proxy(obj, handler)
|
|
48
|
+
return new Proxy(obj, handler)
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
export default Secret
|
|
52
|
+
export const secret = Secret.secret
|
package/lib/session.js
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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.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.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
|
-
|
|
138
|
+
export default session
|
package/lib/step/base.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
6
|
|
|
7
7
|
const STACK_LINE = 5
|
|
8
8
|
|
|
@@ -236,4 +236,4 @@ class Step {
|
|
|
236
236
|
}
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
-
|
|
239
|
+
export default Step
|
package/lib/step/config.js
CHANGED
package/lib/step/func.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import BaseStep from './base.js'
|
|
2
|
+
import store from '../store.js'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Function executed as a step
|
|
@@ -43,4 +43,4 @@ class FuncStep extends BaseStep {
|
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
export default FuncStep
|
package/lib/step/helper.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import Step from './base.js'
|
|
2
|
+
import store from '../store.js'
|
|
3
3
|
|
|
4
4
|
class HelperStep extends Step {
|
|
5
5
|
constructor(helper, name) {
|
|
@@ -38,7 +38,7 @@ class HelperStep extends Step {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
export default HelperStep
|
|
42
42
|
|
|
43
43
|
function dryRunResolver() {
|
|
44
44
|
return {
|
package/lib/step/meta.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import Step from './base.js'
|
|
2
|
+
import event from '../event.js'
|
|
3
|
+
import { humanizeString } from '../utils.js'
|
|
4
4
|
|
|
5
5
|
class MetaStep extends Step {
|
|
6
6
|
constructor(actor, method) {
|
|
@@ -96,4 +96,4 @@ class MetaStep extends Step {
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
export default MetaStep
|
package/lib/step/record.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
8
|
function recordStep(step, args) {
|
|
9
9
|
step.status = 'queued'
|
|
10
10
|
|
|
@@ -15,12 +15,12 @@ function recordStep(step, args) {
|
|
|
15
15
|
const { opts, timeout, retry } = stepConfig.getConfig()
|
|
16
16
|
|
|
17
17
|
if (opts) {
|
|
18
|
-
debug(`Step ${step.name}: options applied ${JSON.stringify(opts)}`)
|
|
18
|
+
output.debug(`Step ${step.name}: options applied ${JSON.stringify(opts)}`)
|
|
19
19
|
store.stepOptions = opts
|
|
20
20
|
step.opts = opts
|
|
21
21
|
}
|
|
22
22
|
if (timeout) {
|
|
23
|
-
debug(`Step ${step.name} timeout ${timeout}s`)
|
|
23
|
+
output.debug(`Step ${step.name} timeout ${timeout}s`)
|
|
24
24
|
step.setTimeout(timeout * 1000, TIMEOUT_ORDER.codeLimitTime)
|
|
25
25
|
}
|
|
26
26
|
if (retry) retryStep(retry)
|
|
@@ -57,7 +57,7 @@ function recordStep(step, args) {
|
|
|
57
57
|
event.emit(event.step.finished, step)
|
|
58
58
|
})
|
|
59
59
|
|
|
60
|
-
recorder.
|
|
60
|
+
recorder.catch(err => {
|
|
61
61
|
step.status = 'failed'
|
|
62
62
|
step.endTime = +Date.now()
|
|
63
63
|
event.emit(event.step.failed, step, err)
|
|
@@ -71,4 +71,4 @@ function recordStep(step, args) {
|
|
|
71
71
|
return recorder.promise()
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
export default recordStep
|