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/codecept.js
CHANGED
|
@@ -1,228 +1,328 @@
|
|
|
1
|
-
import { existsSync, readFileSync } from 'fs'
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
import
|
|
16
|
-
import
|
|
17
|
-
import
|
|
18
|
-
import
|
|
19
|
-
import
|
|
20
|
-
import
|
|
21
|
-
import
|
|
22
|
-
import
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
import
|
|
26
|
-
import
|
|
27
|
-
import
|
|
28
|
-
import
|
|
29
|
-
import
|
|
30
|
-
import
|
|
31
|
-
import
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const require = createRequire(import.meta.url);
|
|
1
|
+
import { existsSync, readFileSync } from 'fs'
|
|
2
|
+
import { globSync } from 'glob'
|
|
3
|
+
import shuffle from 'lodash.shuffle'
|
|
4
|
+
import fsPath from 'path'
|
|
5
|
+
import { resolve } from 'path'
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from 'url'
|
|
7
|
+
import { dirname } from 'path'
|
|
8
|
+
import { createRequire } from 'module'
|
|
9
|
+
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url)
|
|
11
|
+
const __dirname = dirname(__filename)
|
|
12
|
+
|
|
13
|
+
import Helper from '@codeceptjs/helper'
|
|
14
|
+
import container from './container.js'
|
|
15
|
+
import Config from './config.js'
|
|
16
|
+
import event from './event.js'
|
|
17
|
+
import runHook from './hooks.js'
|
|
18
|
+
import ActorFactory from './actor.js'
|
|
19
|
+
import output from './output.js'
|
|
20
|
+
import { emptyFolder } from './utils.js'
|
|
21
|
+
import { initCodeceptGlobals } from './globals.js'
|
|
22
|
+
import { validateTypeScriptSetup } from './utils/loaderCheck.js'
|
|
23
|
+
import recorder from './recorder.js'
|
|
24
|
+
|
|
25
|
+
import storeListener from './listener/store.js'
|
|
26
|
+
import stepsListener from './listener/steps.js'
|
|
27
|
+
import configListener from './listener/config.js'
|
|
28
|
+
import resultListener from './listener/result.js'
|
|
29
|
+
import helpersListener from './listener/helpers.js'
|
|
30
|
+
import globalTimeoutListener from './listener/globalTimeout.js'
|
|
31
|
+
import globalRetryListener from './listener/globalRetry.js'
|
|
32
|
+
import exitListener from './listener/exit.js'
|
|
33
|
+
import emptyRunListener from './listener/emptyRun.js'
|
|
35
34
|
|
|
36
35
|
/**
|
|
37
|
-
* CodeceptJS runner
|
|
36
|
+
* CodeceptJS runner
|
|
38
37
|
*/
|
|
39
|
-
|
|
38
|
+
class Codecept {
|
|
40
39
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
40
|
+
* Create CodeceptJS runner.
|
|
41
|
+
* Config and options should be passed
|
|
42
|
+
*
|
|
43
|
+
* @param {*} config
|
|
44
|
+
* @param {*} opts
|
|
44
45
|
*/
|
|
45
46
|
constructor(config, opts) {
|
|
46
|
-
this.config = Config.create(config)
|
|
47
|
-
this.opts = opts
|
|
48
|
-
this.testFiles =
|
|
49
|
-
this.
|
|
47
|
+
this.config = Config.create(config)
|
|
48
|
+
this.opts = opts
|
|
49
|
+
this.testFiles = new Array(0)
|
|
50
|
+
this.requiringModules = config.require
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
54
|
+
* Require modules before codeceptjs running
|
|
55
|
+
*
|
|
56
|
+
* @param {string[]} requiringModules
|
|
55
57
|
*/
|
|
56
|
-
requireModules(requiringModules) {
|
|
57
|
-
requiringModules
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
async requireModules(requiringModules) {
|
|
59
|
+
if (requiringModules) {
|
|
60
|
+
for (const requiredModule of requiringModules) {
|
|
61
|
+
let modulePath = requiredModule
|
|
62
|
+
const isLocalFile = existsSync(modulePath) || existsSync(`${modulePath}.js`)
|
|
63
|
+
if (isLocalFile) {
|
|
64
|
+
modulePath = resolve(modulePath)
|
|
65
|
+
// For ESM, ensure .js extension for local files
|
|
66
|
+
if (!modulePath.endsWith('.js') && !modulePath.endsWith('.mjs') && !modulePath.endsWith('.cjs')) {
|
|
67
|
+
if (existsSync(`${modulePath}.js`)) {
|
|
68
|
+
modulePath = `${modulePath}.js`
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
} else {
|
|
72
|
+
// For npm packages, resolve from the user's directory
|
|
73
|
+
// This ensures packages like tsx are found in user's node_modules
|
|
74
|
+
const userDir = global.codecept_dir || process.cwd()
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
// Use createRequire to resolve from user's directory
|
|
78
|
+
const userRequire = createRequire(pathToFileURL(resolve(userDir, 'package.json')).href)
|
|
79
|
+
const resolvedPath = userRequire.resolve(requiredModule)
|
|
80
|
+
modulePath = pathToFileURL(resolvedPath).href
|
|
81
|
+
} catch (resolveError) {
|
|
82
|
+
// If resolution fails, try direct import (will check from CodeceptJS node_modules)
|
|
83
|
+
// This is the fallback for globally installed packages
|
|
84
|
+
modulePath = requiredModule
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Use dynamic import for ESM
|
|
88
|
+
await import(modulePath)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
62
91
|
}
|
|
63
92
|
|
|
64
93
|
/**
|
|
65
|
-
*
|
|
66
|
-
*
|
|
94
|
+
* Initialize CodeceptJS at specific dir.
|
|
95
|
+
* Loads config, requires factory methods
|
|
96
|
+
*
|
|
97
|
+
* @param {string} dir
|
|
67
98
|
*/
|
|
68
|
-
init(dir) {
|
|
69
|
-
this.initGlobals(dir)
|
|
70
|
-
|
|
71
|
-
this.
|
|
99
|
+
async init(dir) {
|
|
100
|
+
await this.initGlobals(dir)
|
|
101
|
+
// Require modules before initializing
|
|
102
|
+
await this.requireModules(this.requiringModules)
|
|
103
|
+
// initializing listeners
|
|
104
|
+
await container.create(this.config, this.opts)
|
|
105
|
+
// Store container globally for easy access
|
|
106
|
+
global.container = container
|
|
107
|
+
await this.runHooks()
|
|
72
108
|
}
|
|
73
109
|
|
|
74
110
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
111
|
+
* Creates global variables
|
|
112
|
+
*
|
|
113
|
+
* @param {string} dir
|
|
77
114
|
*/
|
|
78
|
-
initGlobals(dir) {
|
|
79
|
-
|
|
80
|
-
global.output_dir = resolve(dir, this.config.output);
|
|
81
|
-
|
|
82
|
-
if (this.config.emptyOutputFolder) emptyFolder(global.output_dir);
|
|
83
|
-
|
|
84
|
-
if (!this.config.noGlobals) {
|
|
85
|
-
this.initGlobalHelpers();
|
|
86
|
-
}
|
|
115
|
+
async initGlobals(dir) {
|
|
116
|
+
await initCodeceptGlobals(dir, this.config, container)
|
|
87
117
|
}
|
|
88
118
|
|
|
89
119
|
/**
|
|
90
|
-
*
|
|
120
|
+
* Executes hooks.
|
|
91
121
|
*/
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
// BDD
|
|
107
|
-
global.Given = stepDefinitions.Given;
|
|
108
|
-
global.When = stepDefinitions.When;
|
|
109
|
-
global.Then = stepDefinitions.Then;
|
|
110
|
-
global.DefineParameterType = stepDefinitions.defineParameterType;
|
|
111
|
-
global.debugMode = false;
|
|
112
|
-
}
|
|
122
|
+
async runHooks() {
|
|
123
|
+
// default hooks - dynamic imports for ESM
|
|
124
|
+
const listenerModules = [
|
|
125
|
+
'./listener/store.js',
|
|
126
|
+
'./listener/steps.js',
|
|
127
|
+
'./listener/config.js',
|
|
128
|
+
'./listener/result.js',
|
|
129
|
+
'./listener/helpers.js',
|
|
130
|
+
'./listener/globalTimeout.js',
|
|
131
|
+
'./listener/globalRetry.js',
|
|
132
|
+
'./listener/retryEnhancer.js',
|
|
133
|
+
'./listener/exit.js',
|
|
134
|
+
'./listener/emptyRun.js',
|
|
135
|
+
]
|
|
113
136
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
listenerConfig,
|
|
122
|
-
listenerHelpers,
|
|
123
|
-
listenerRetry,
|
|
124
|
-
listenerTimeout,
|
|
125
|
-
listenerExit,
|
|
126
|
-
];
|
|
127
|
-
|
|
128
|
-
listeners.forEach(runHook);
|
|
129
|
-
|
|
130
|
-
// Run custom hooks
|
|
131
|
-
this.config.hooks.forEach(runHook);
|
|
137
|
+
for (const modulePath of listenerModules) {
|
|
138
|
+
const module = await import(modulePath)
|
|
139
|
+
runHook(module.default || module)
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// custom hooks (previous iteration of plugins)
|
|
143
|
+
this.config.hooks.forEach(hook => runHook(hook))
|
|
132
144
|
}
|
|
133
145
|
|
|
134
146
|
/**
|
|
135
|
-
* Executes
|
|
147
|
+
* Executes bootstrap.
|
|
148
|
+
*
|
|
149
|
+
* @returns {Promise<void>}
|
|
136
150
|
*/
|
|
137
151
|
async bootstrap() {
|
|
138
|
-
return runHook(this.config.bootstrap, 'bootstrap')
|
|
152
|
+
return runHook(this.config.bootstrap, 'bootstrap')
|
|
139
153
|
}
|
|
140
154
|
|
|
141
155
|
/**
|
|
142
|
-
* Executes
|
|
156
|
+
* Executes teardown.
|
|
157
|
+
*
|
|
158
|
+
* @returns {Promise<void>}
|
|
143
159
|
*/
|
|
144
160
|
async teardown() {
|
|
145
|
-
return runHook(this.config.teardown, 'teardown')
|
|
161
|
+
return runHook(this.config.teardown, 'teardown')
|
|
146
162
|
}
|
|
147
163
|
|
|
148
164
|
/**
|
|
149
|
-
* Loads
|
|
150
|
-
*
|
|
165
|
+
* Loads tests by pattern or by config.tests
|
|
166
|
+
*
|
|
167
|
+
* @param {string} [pattern]
|
|
151
168
|
*/
|
|
152
169
|
loadTests(pattern) {
|
|
153
|
-
const
|
|
154
|
-
|
|
170
|
+
const options = {
|
|
171
|
+
cwd: global.codecept_dir,
|
|
172
|
+
}
|
|
155
173
|
|
|
156
|
-
patterns
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const fullPath = isAbsolute(file) ? file : join(global.codecept_dir, file);
|
|
160
|
-
const resolvedFile = resolve(fullPath);
|
|
174
|
+
let patterns = [pattern]
|
|
175
|
+
if (!pattern) {
|
|
176
|
+
patterns = []
|
|
161
177
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
178
|
+
// If the user wants to test a specific set of test files as an array or string.
|
|
179
|
+
if (this.config.tests && !this.opts.features) {
|
|
180
|
+
if (Array.isArray(this.config.tests)) {
|
|
181
|
+
patterns.push(...this.config.tests)
|
|
182
|
+
} else {
|
|
183
|
+
patterns.push(this.config.tests)
|
|
165
184
|
}
|
|
166
|
-
}
|
|
167
|
-
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (this.config.gherkin && this.config.gherkin.features && !this.opts.tests) {
|
|
188
|
+
if (Array.isArray(this.config.gherkin.features)) {
|
|
189
|
+
this.config.gherkin.features.forEach(feature => {
|
|
190
|
+
patterns.push(feature)
|
|
191
|
+
})
|
|
192
|
+
} else {
|
|
193
|
+
patterns.push(this.config.gherkin.features)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
for (pattern of patterns) {
|
|
199
|
+
if (pattern) {
|
|
200
|
+
globSync(pattern, options).forEach(file => {
|
|
201
|
+
if (file.includes('node_modules')) return
|
|
202
|
+
if (!fsPath.isAbsolute(file)) {
|
|
203
|
+
file = fsPath.join(global.codecept_dir, file)
|
|
204
|
+
}
|
|
205
|
+
if (!this.testFiles.includes(fsPath.resolve(file))) {
|
|
206
|
+
this.testFiles.push(fsPath.resolve(file))
|
|
207
|
+
}
|
|
208
|
+
})
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (this.opts.shuffle) {
|
|
213
|
+
this.testFiles = shuffle(this.testFiles)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (this.opts.shard) {
|
|
217
|
+
this.testFiles = this._applySharding(this.testFiles, this.opts.shard)
|
|
218
|
+
}
|
|
168
219
|
}
|
|
169
220
|
|
|
170
221
|
/**
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
* @
|
|
222
|
+
* Apply sharding to test files based on shard configuration
|
|
223
|
+
*
|
|
224
|
+
* @param {Array<string>} testFiles - Array of test file paths
|
|
225
|
+
* @param {string} shardConfig - Shard configuration in format "index/total" (e.g., "1/4")
|
|
226
|
+
* @returns {Array<string>} - Filtered array of test files for this shard
|
|
174
227
|
*/
|
|
175
|
-
|
|
176
|
-
|
|
228
|
+
_applySharding(testFiles, shardConfig) {
|
|
229
|
+
const shardMatch = shardConfig.match(/^(\d+)\/(\d+)$/)
|
|
230
|
+
if (!shardMatch) {
|
|
231
|
+
throw new Error('Invalid shard format. Expected format: "index/total" (e.g., "1/4")')
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const shardIndex = parseInt(shardMatch[1], 10)
|
|
235
|
+
const shardTotal = parseInt(shardMatch[2], 10)
|
|
177
236
|
|
|
178
|
-
|
|
179
|
-
|
|
237
|
+
if (shardTotal < 1) {
|
|
238
|
+
throw new Error('Shard total must be at least 1')
|
|
239
|
+
}
|
|
180
240
|
|
|
181
|
-
if (
|
|
182
|
-
|
|
241
|
+
if (shardIndex < 1 || shardIndex > shardTotal) {
|
|
242
|
+
throw new Error(`Shard index ${shardIndex} must be between 1 and ${shardTotal}`)
|
|
183
243
|
}
|
|
184
244
|
|
|
185
|
-
if (
|
|
186
|
-
|
|
245
|
+
if (testFiles.length === 0) {
|
|
246
|
+
return testFiles
|
|
187
247
|
}
|
|
188
248
|
|
|
189
|
-
|
|
249
|
+
// Calculate which tests belong to this shard
|
|
250
|
+
const shardSize = Math.ceil(testFiles.length / shardTotal)
|
|
251
|
+
const startIndex = (shardIndex - 1) * shardSize
|
|
252
|
+
const endIndex = Math.min(startIndex + shardSize, testFiles.length)
|
|
253
|
+
|
|
254
|
+
return testFiles.slice(startIndex, endIndex)
|
|
190
255
|
}
|
|
191
256
|
|
|
192
257
|
/**
|
|
193
|
-
*
|
|
194
|
-
*
|
|
258
|
+
* Run a specific test or all loaded tests.
|
|
259
|
+
*
|
|
260
|
+
* @param {string} [test]
|
|
195
261
|
* @returns {Promise<void>}
|
|
196
262
|
*/
|
|
197
263
|
async run(test) {
|
|
198
|
-
|
|
199
|
-
mocha.files = this.testFiles;
|
|
264
|
+
await container.started()
|
|
200
265
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
266
|
+
// Check TypeScript loader configuration before running tests
|
|
267
|
+
const tsValidation = validateTypeScriptSetup(this.testFiles, this.requiringModules || [])
|
|
268
|
+
if (tsValidation.hasError) {
|
|
269
|
+
output.error(tsValidation.message)
|
|
270
|
+
process.exit(1)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Ensure translations are loaded for Gherkin features
|
|
274
|
+
try {
|
|
275
|
+
const { loadTranslations } = await import('./mocha/gherkin.js')
|
|
276
|
+
await loadTranslations()
|
|
277
|
+
} catch (e) {
|
|
278
|
+
// Ignore if gherkin module not available
|
|
204
279
|
}
|
|
205
280
|
|
|
206
281
|
return new Promise((resolve, reject) => {
|
|
282
|
+
const mocha = container.mocha()
|
|
283
|
+
mocha.files = this.testFiles
|
|
284
|
+
|
|
285
|
+
if (test) {
|
|
286
|
+
if (!fsPath.isAbsolute(test)) {
|
|
287
|
+
test = fsPath.join(global.codecept_dir, test)
|
|
288
|
+
}
|
|
289
|
+
const testBasename = fsPath.basename(test, '.js')
|
|
290
|
+
const testFeatureBasename = fsPath.basename(test, '.feature')
|
|
291
|
+
mocha.files = mocha.files.filter(t => {
|
|
292
|
+
return fsPath.basename(t, '.js') === testBasename || fsPath.basename(t, '.feature') === testFeatureBasename || t === test
|
|
293
|
+
})
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const done = async (failures) => {
|
|
297
|
+
event.emit(event.all.result, container.result())
|
|
298
|
+
event.emit(event.all.after, this)
|
|
299
|
+
// Wait for any recorder tasks added by event.all.after handlers
|
|
300
|
+
await recorder.promise()
|
|
301
|
+
// Set exit code based on test failures
|
|
302
|
+
if (failures) {
|
|
303
|
+
process.exitCode = 1
|
|
304
|
+
}
|
|
305
|
+
resolve()
|
|
306
|
+
}
|
|
307
|
+
|
|
207
308
|
try {
|
|
208
|
-
event.emit(event.all.before, this)
|
|
209
|
-
mocha.run(() =>
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
});
|
|
214
|
-
} catch (error) {
|
|
215
|
-
output.output.error(error.stack);
|
|
216
|
-
reject(error);
|
|
309
|
+
event.emit(event.all.before, this)
|
|
310
|
+
mocha.run(async (failures) => await done(failures))
|
|
311
|
+
} catch (e) {
|
|
312
|
+
output.error(e.stack)
|
|
313
|
+
reject(e)
|
|
217
314
|
}
|
|
218
|
-
})
|
|
315
|
+
})
|
|
219
316
|
}
|
|
220
|
-
}
|
|
221
317
|
|
|
222
|
-
/**
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
318
|
+
/**
|
|
319
|
+
* Returns the version string of CodeceptJS.
|
|
320
|
+
*
|
|
321
|
+
* @returns {string} The version string.
|
|
322
|
+
*/
|
|
323
|
+
static version() {
|
|
324
|
+
return JSON.parse(readFileSync(`${__dirname}/../package.json`, 'utf8')).version
|
|
325
|
+
}
|
|
228
326
|
}
|
|
327
|
+
|
|
328
|
+
export default Codecept
|
package/lib/colorUtils.js
CHANGED
|
@@ -144,14 +144,14 @@ function convertColorNameToHex(color) {
|
|
|
144
144
|
whitesmoke: '#f5f5f5',
|
|
145
145
|
yellow: '#ffff00',
|
|
146
146
|
yellowgreen: '#9acd32',
|
|
147
|
-
}
|
|
147
|
+
}
|
|
148
148
|
|
|
149
|
-
const cColor = `${color}`.toLowerCase()
|
|
149
|
+
const cColor = `${color}`.toLowerCase()
|
|
150
150
|
if (typeof colors[cColor] !== 'undefined') {
|
|
151
|
-
return colors[cColor]
|
|
151
|
+
return colors[cColor]
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
return color
|
|
154
|
+
return color
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
/**
|
|
@@ -160,23 +160,23 @@ function convertColorNameToHex(color) {
|
|
|
160
160
|
*/
|
|
161
161
|
function convertHexColorToRgba(hex) {
|
|
162
162
|
// Expand shorthand form (e.g. "#03F") to full form (e.g. "#0033FF")
|
|
163
|
-
const shorthandRegex = /^#([a-f\d])([a-f\d])([a-f\d])$/i
|
|
163
|
+
const shorthandRegex = /^#([a-f\d])([a-f\d])([a-f\d])$/i
|
|
164
164
|
const hexFull = `${hex}`.replace(shorthandRegex, (m, r, g, b) => {
|
|
165
|
-
return r + r + g + g + b + b
|
|
166
|
-
})
|
|
165
|
+
return r + r + g + g + b + b
|
|
166
|
+
})
|
|
167
167
|
|
|
168
|
-
const result = /^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hexFull)
|
|
168
|
+
const result = /^#([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hexFull)
|
|
169
169
|
if (!result) {
|
|
170
170
|
// Return untouched if not a hex code
|
|
171
|
-
return hex
|
|
171
|
+
return hex
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
const r = parseInt(result[1], 16)
|
|
175
|
-
const g = parseInt(result[2], 16)
|
|
176
|
-
const b = parseInt(result[3], 16)
|
|
177
|
-
const a = 1
|
|
174
|
+
const r = parseInt(result[1], 16)
|
|
175
|
+
const g = parseInt(result[2], 16)
|
|
176
|
+
const b = parseInt(result[3], 16)
|
|
177
|
+
const a = 1
|
|
178
178
|
|
|
179
|
-
return `rgba(${r}, ${g}, ${b}, ${a})
|
|
179
|
+
return `rgba(${r}, ${g}, ${b}, ${a})`
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
/**
|
|
@@ -189,30 +189,30 @@ function convertHexColorToRgba(hex) {
|
|
|
189
189
|
*
|
|
190
190
|
* @param {string} color Color as a string, i.e. rgb(85,0,0)
|
|
191
191
|
*/
|
|
192
|
-
|
|
193
|
-
const cstr = `${color}`.toLowerCase().trim() || ''
|
|
192
|
+
function convertColorToRGBA(color) {
|
|
193
|
+
const cstr = `${color}`.toLowerCase().trim() || ''
|
|
194
194
|
|
|
195
195
|
if (!/^rgba?\(.+?\)$/.test(cstr)) {
|
|
196
196
|
// Convert both color names and hex colors to rgba
|
|
197
|
-
const hexColor = convertColorNameToHex(color)
|
|
198
|
-
return convertHexColorToRgba(hexColor)
|
|
197
|
+
const hexColor = convertColorNameToHex(color)
|
|
198
|
+
return convertHexColorToRgba(hexColor)
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
// Convert rgb to rgba
|
|
202
|
-
const channels = cstr.match(/([\-0-9.]+)/g) || []
|
|
202
|
+
const channels = cstr.match(/([\-0-9.]+)/g) || []
|
|
203
203
|
|
|
204
204
|
switch (channels.length) {
|
|
205
205
|
case 3:
|
|
206
206
|
// Convert rgb to rgba
|
|
207
|
-
return `rgba(${channels.join(', ')}, 1)
|
|
207
|
+
return `rgba(${channels.join(', ')}, 1)`
|
|
208
208
|
|
|
209
209
|
case 4:
|
|
210
210
|
// Format rgba
|
|
211
|
-
return `rgba(${channels.join(', ')})
|
|
211
|
+
return `rgba(${channels.join(', ')})`
|
|
212
212
|
|
|
213
213
|
default:
|
|
214
214
|
// Unexpected color format. Leave it untouched (let the user handle it)
|
|
215
|
-
return color
|
|
215
|
+
return color
|
|
216
216
|
}
|
|
217
217
|
}
|
|
218
218
|
|
|
@@ -221,35 +221,33 @@ export function convertColorToRGBA(color) {
|
|
|
221
221
|
*
|
|
222
222
|
* @param {string} prop CSS Property name
|
|
223
223
|
*/
|
|
224
|
-
|
|
225
|
-
return
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
224
|
+
function isColorProperty(prop) {
|
|
225
|
+
return (
|
|
226
|
+
[
|
|
227
|
+
'color',
|
|
228
|
+
'background',
|
|
229
|
+
'backgroundColor',
|
|
230
|
+
'background-color',
|
|
231
|
+
'borderColor',
|
|
232
|
+
'border-color',
|
|
233
|
+
'borderBottomColor',
|
|
234
|
+
'border-bottom-color',
|
|
235
|
+
'borderLeftColor',
|
|
236
|
+
'border-left-color',
|
|
237
|
+
'borderRightColor',
|
|
238
|
+
'borderTopColor',
|
|
239
|
+
'caretColor',
|
|
240
|
+
'columnRuleColor',
|
|
241
|
+
'outlineColor',
|
|
242
|
+
'textDecorationColor',
|
|
243
|
+
'border-right-color',
|
|
244
|
+
'border-top-color',
|
|
245
|
+
'caret-color',
|
|
246
|
+
'column-rule-color',
|
|
247
|
+
'outline-color',
|
|
248
|
+
'text-decoration-color',
|
|
249
|
+
].indexOf(prop) > -1
|
|
250
|
+
)
|
|
249
251
|
}
|
|
250
252
|
|
|
251
|
-
export
|
|
252
|
-
isColorProperty,
|
|
253
|
-
convertColorToRGBA,
|
|
254
|
-
convertColorNameToHex,
|
|
255
|
-
};
|
|
253
|
+
export { isColorProperty, convertColorToRGBA, convertColorNameToHex }
|