codeceptjs 4.0.0-rc.10 → 4.0.0-rc.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/mcp-server.js +32 -5
- package/lib/ai.js +3 -2
- package/lib/assertions.js +18 -0
- package/lib/codecept.js +6 -6
- package/lib/command/check.js +2 -0
- package/lib/command/dryRun.js +2 -3
- package/lib/command/generate.js +2 -0
- package/lib/command/gherkin/snippets.js +5 -4
- package/lib/command/init.js +1 -0
- package/lib/command/run-multiple.js +2 -0
- package/lib/command/run-workers.js +1 -0
- package/lib/command/run.js +1 -1
- package/lib/command/workers/runTests.js +10 -10
- package/lib/container.js +63 -13
- package/lib/effects.js +17 -0
- package/lib/element/WebElement.js +128 -0
- package/lib/globals.js +22 -10
- package/lib/heal.js +4 -3
- package/lib/helper/ApiDataFactory.js +2 -1
- package/lib/helper/FileSystem.js +3 -2
- package/lib/helper/GraphQLDataFactory.js +2 -1
- package/lib/helper/Playwright.js +22 -17
- package/lib/helper/Puppeteer.js +20 -6
- package/lib/helper/WebDriver.js +12 -2
- package/lib/helper/errors/NonFocusedType.js +8 -0
- package/lib/helper/extras/Download.js +45 -0
- package/lib/helper/extras/focusCheck.js +43 -0
- package/lib/helper/extras/richTextEditor.js +115 -0
- package/lib/history.js +3 -2
- package/lib/listener/config.js +6 -4
- package/lib/listener/emptyRun.js +2 -1
- package/lib/listener/helpers.js +4 -1
- package/lib/listener/mocha.js +2 -1
- package/lib/listener/pageobjects.js +43 -0
- package/lib/listener/result.js +3 -2
- package/lib/locator.js +112 -0
- package/lib/mocha/cli.js +4 -2
- package/lib/mocha/factory.js +2 -1
- package/lib/mocha/scenarioConfig.js +2 -1
- package/lib/mocha/ui.js +5 -6
- package/lib/plugin/aiTrace.js +4 -3
- package/lib/plugin/analyze.js +1 -1
- package/lib/plugin/auth.js +3 -3
- package/lib/plugin/pageInfo.js +2 -1
- package/lib/plugin/pauseOn.js +167 -0
- package/lib/plugin/screenshotOnFail.js +3 -4
- package/lib/plugin/stepByStepReport.js +5 -4
- package/lib/rerun.js +2 -1
- package/lib/result.js +2 -1
- package/lib/step/base.js +3 -2
- package/lib/step/record.js +1 -1
- package/lib/store.js +72 -3
- package/lib/translation.js +2 -1
- package/lib/utils/mask_data.js +2 -1
- package/lib/utils.js +4 -3
- package/lib/workers.js +2 -0
- package/package.json +5 -3
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import event from '../event.js'
|
|
2
|
+
import pause from '../pause.js'
|
|
3
|
+
import recorder from '../recorder.js'
|
|
4
|
+
import Container from '../container.js'
|
|
5
|
+
import output from '../output.js'
|
|
6
|
+
|
|
7
|
+
const supportedHelpers = Container.STANDARD_ACTING_HELPERS
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Pauses test execution in different modes. Unlike `pauseOnFail`, this plugin supports
|
|
11
|
+
* multiple triggers for pausing and is controlled via CLI arguments.
|
|
12
|
+
*
|
|
13
|
+
* Enable it via `-p` option with a mode:
|
|
14
|
+
*
|
|
15
|
+
* ```
|
|
16
|
+
* npx codeceptjs run -p pauseOn:fail
|
|
17
|
+
* npx codeceptjs run -p pauseOn:step
|
|
18
|
+
* npx codeceptjs run -p pauseOn:file:tests/login_test.js
|
|
19
|
+
* npx codeceptjs run -p pauseOn:file:tests/login_test.js:43
|
|
20
|
+
* npx codeceptjs run -p pauseOn:url:/users/*
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* #### Modes
|
|
24
|
+
*
|
|
25
|
+
* * **fail** — pause when a step fails (same as `pauseOnFail` plugin)
|
|
26
|
+
* * **step** — pause before first step, use `next` to advance step-by-step
|
|
27
|
+
* * **file** — pause when execution reaches a specific file (and optionally line)
|
|
28
|
+
* * **url** — pause when the browser URL matches a pattern (supports `*` wildcards)
|
|
29
|
+
*
|
|
30
|
+
*/
|
|
31
|
+
export default function (config = {}) {
|
|
32
|
+
const args = config._args || []
|
|
33
|
+
const mode = args[0] || 'fail'
|
|
34
|
+
|
|
35
|
+
switch (mode) {
|
|
36
|
+
case 'fail':
|
|
37
|
+
return initFailMode()
|
|
38
|
+
case 'step':
|
|
39
|
+
return initStepMode()
|
|
40
|
+
case 'file':
|
|
41
|
+
return initFileMode(args.slice(1))
|
|
42
|
+
case 'url':
|
|
43
|
+
return initUrlMode(args.slice(1))
|
|
44
|
+
default:
|
|
45
|
+
output.error(`pauseOn: unknown mode "${mode}". Available: fail, step, file, url`)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function initFailMode() {
|
|
50
|
+
let failed = false
|
|
51
|
+
|
|
52
|
+
event.dispatcher.on(event.test.started, () => {
|
|
53
|
+
failed = false
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
event.dispatcher.on(event.step.failed, () => {
|
|
57
|
+
failed = true
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
event.dispatcher.on(event.test.after, () => {
|
|
61
|
+
if (failed) pause()
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function initStepMode() {
|
|
66
|
+
let activated = false
|
|
67
|
+
|
|
68
|
+
event.dispatcher.on(event.test.before, () => {
|
|
69
|
+
if (activated) return
|
|
70
|
+
activated = true
|
|
71
|
+
recorder.add('pauseOn:step', () => pause())
|
|
72
|
+
})
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function initFileMode(fileArgs) {
|
|
76
|
+
if (fileArgs.length === 0) {
|
|
77
|
+
output.error('pauseOn:file requires a path. Usage: -p pauseOn:file:<path>[:<line>]')
|
|
78
|
+
return
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const targetFile = fileArgs[0]
|
|
82
|
+
const targetLine = fileArgs[1] ? parseInt(fileArgs[1], 10) : null
|
|
83
|
+
let paused = false
|
|
84
|
+
|
|
85
|
+
event.dispatcher.on(event.step.before, (step) => {
|
|
86
|
+
if (paused) return
|
|
87
|
+
|
|
88
|
+
const stepLine = step.line()
|
|
89
|
+
if (!stepLine) return
|
|
90
|
+
|
|
91
|
+
const match = parseStepLine(stepLine)
|
|
92
|
+
if (!match) return
|
|
93
|
+
|
|
94
|
+
const fileMatches = match.file.includes(targetFile) || match.file.endsWith(targetFile)
|
|
95
|
+
if (!fileMatches) return
|
|
96
|
+
|
|
97
|
+
if (targetLine !== null && match.line !== targetLine) return
|
|
98
|
+
|
|
99
|
+
paused = true
|
|
100
|
+
recorder.add('pauseOn:file', () => pause())
|
|
101
|
+
})
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function initUrlMode(urlArgs) {
|
|
105
|
+
if (urlArgs.length === 0) {
|
|
106
|
+
output.error('pauseOn:url requires a pattern. Usage: -p pauseOn:url:<pattern>')
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const urlPattern = urlArgs.join(':')
|
|
111
|
+
|
|
112
|
+
const helpers = Container.helpers()
|
|
113
|
+
let helper = null
|
|
114
|
+
for (const helperName of supportedHelpers) {
|
|
115
|
+
if (Object.keys(helpers).indexOf(helperName) > -1) {
|
|
116
|
+
helper = helpers[helperName]
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (!helper) {
|
|
121
|
+
output.error('pauseOn:url requires a browser helper (Playwright, WebDriver, Puppeteer, Appium)')
|
|
122
|
+
return
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const regex = patternToRegex(urlPattern)
|
|
126
|
+
let paused = false
|
|
127
|
+
|
|
128
|
+
event.dispatcher.on(event.step.after, () => {
|
|
129
|
+
if (paused) return
|
|
130
|
+
|
|
131
|
+
recorder.add('pauseOn:url check', async () => {
|
|
132
|
+
if (paused) return
|
|
133
|
+
try {
|
|
134
|
+
const currentUrl = await helper.grabCurrentUrl()
|
|
135
|
+
if (regex.test(currentUrl)) {
|
|
136
|
+
paused = true
|
|
137
|
+
return pause()
|
|
138
|
+
}
|
|
139
|
+
} catch (err) {
|
|
140
|
+
// page may not be loaded yet
|
|
141
|
+
}
|
|
142
|
+
})
|
|
143
|
+
})
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function parseStepLine(stepLine) {
|
|
147
|
+
let line = stepLine.trim()
|
|
148
|
+
if (line.startsWith('at ')) line = line.substring(3).trim()
|
|
149
|
+
|
|
150
|
+
const lastColon = line.lastIndexOf(':')
|
|
151
|
+
if (lastColon < 0) return null
|
|
152
|
+
const secondLastColon = line.lastIndexOf(':', lastColon - 1)
|
|
153
|
+
if (secondLastColon < 0) return null
|
|
154
|
+
|
|
155
|
+
const file = line.substring(0, secondLastColon)
|
|
156
|
+
const lineNum = parseInt(line.substring(secondLastColon + 1, lastColon), 10)
|
|
157
|
+
|
|
158
|
+
if (isNaN(lineNum)) return null
|
|
159
|
+
|
|
160
|
+
return { file, line: lineNum }
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function patternToRegex(pattern) {
|
|
164
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&')
|
|
165
|
+
const regexStr = escaped.replace(/\*/g, '.*')
|
|
166
|
+
return new RegExp(regexStr)
|
|
167
|
+
}
|
|
@@ -8,6 +8,7 @@ import recorder from '../recorder.js'
|
|
|
8
8
|
import event from '../event.js'
|
|
9
9
|
|
|
10
10
|
import output from '../output.js'
|
|
11
|
+
import store from '../store.js'
|
|
11
12
|
|
|
12
13
|
import { fileExists } from '../utils.js'
|
|
13
14
|
import Codeceptjs from '../index.js'
|
|
@@ -94,7 +95,7 @@ export default function (config) {
|
|
|
94
95
|
} else {
|
|
95
96
|
fileName = `${testToFileName(test, { suffix: '', unique: false })}.failed.png`
|
|
96
97
|
}
|
|
97
|
-
const quietMode = !
|
|
98
|
+
const quietMode = !store.outputDir
|
|
98
99
|
if (!quietMode) {
|
|
99
100
|
output.plugin('screenshotOnFail', 'Test failed, try to save a screenshot')
|
|
100
101
|
}
|
|
@@ -139,9 +140,7 @@ export default function (config) {
|
|
|
139
140
|
await Promise.race([screenshotPromise, timeoutPromise])
|
|
140
141
|
|
|
141
142
|
if (!test.artifacts) test.artifacts = {}
|
|
142
|
-
|
|
143
|
-
// Detect output directory safely (may not be initialized in narrow unit tests)
|
|
144
|
-
const baseOutputDir = 'output_dir' in global && typeof global.output_dir === 'string' && global.output_dir ? global.output_dir : null
|
|
143
|
+
const baseOutputDir = store.outputDir || null
|
|
145
144
|
if (baseOutputDir) {
|
|
146
145
|
test.artifacts.screenshot = path.join(baseOutputDir, fileName)
|
|
147
146
|
if (Container.mocha().options.reporterOptions['mocha-junit-reporter'] && Container.mocha().options.reporterOptions['mocha-junit-reporter'].options.attachments) {
|
|
@@ -4,8 +4,9 @@ import figures from 'figures'
|
|
|
4
4
|
import fs from 'fs'
|
|
5
5
|
import { mkdirp } from 'mkdirp'
|
|
6
6
|
import path from 'path'
|
|
7
|
-
import cheerio from 'cheerio'
|
|
7
|
+
import * as cheerio from 'cheerio'
|
|
8
8
|
|
|
9
|
+
import store from '../store.js'
|
|
9
10
|
import Container from '../container.js'
|
|
10
11
|
import recorder from '../recorder.js'
|
|
11
12
|
import event from '../event.js'
|
|
@@ -19,7 +20,7 @@ const defaultConfig = {
|
|
|
19
20
|
animateSlides: true,
|
|
20
21
|
ignoreSteps: [],
|
|
21
22
|
fullPageScreenshots: false,
|
|
22
|
-
output:
|
|
23
|
+
output: store.outputDir,
|
|
23
24
|
screenshotsForAllureReport: false,
|
|
24
25
|
disableScreenshotOnFail: true,
|
|
25
26
|
}
|
|
@@ -87,7 +88,7 @@ export default function (config) {
|
|
|
87
88
|
|
|
88
89
|
const recordedTests = {}
|
|
89
90
|
const pad = '0000'
|
|
90
|
-
const reportDir = config.output ? path.resolve(
|
|
91
|
+
const reportDir = config.output ? path.resolve(store.codeceptDir, config.output) : defaultConfig.output
|
|
91
92
|
|
|
92
93
|
event.dispatcher.on(event.suite.before, suite => {
|
|
93
94
|
stepNum = -1
|
|
@@ -198,7 +199,7 @@ export default function (config) {
|
|
|
198
199
|
// Ignore steps from BeforeSuite function
|
|
199
200
|
if (scenarioFailed && config.disableScreenshotOnFail) return
|
|
200
201
|
if (step.metaStep && step.metaStep.name === 'BeforeSuite') return
|
|
201
|
-
if (!
|
|
202
|
+
if (!currentTest) return // Ignore steps from AfterSuite
|
|
202
203
|
|
|
203
204
|
const fileName = `${pad.substring(0, pad.length - stepNum.toString().length) + stepNum.toString()}.png`
|
|
204
205
|
if (step.status === 'failed') {
|
package/lib/rerun.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fsPath from 'path'
|
|
2
|
+
import store from './store.js'
|
|
2
3
|
import container from './container.js'
|
|
3
4
|
import event from './event.js'
|
|
4
5
|
import BaseCodecept from './codecept.js'
|
|
@@ -29,7 +30,7 @@ class CodeceptRerunner extends BaseCodecept {
|
|
|
29
30
|
let filesToRun = this.testFiles
|
|
30
31
|
if (test) {
|
|
31
32
|
if (!fsPath.isAbsolute(test)) {
|
|
32
|
-
test = fsPath.join(
|
|
33
|
+
test = fsPath.join(store.codeceptDir, test)
|
|
33
34
|
}
|
|
34
35
|
filesToRun = this.testFiles.filter(t => fsPath.basename(t, '.js') === test || t === test)
|
|
35
36
|
}
|
package/lib/result.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'fs'
|
|
2
2
|
import path from 'path'
|
|
3
3
|
import { serializeTest } from './mocha/test.js'
|
|
4
|
+
import store from './store.js'
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* @typedef {Object} Stats Statistics for a test result.
|
|
@@ -212,7 +213,7 @@ class Result {
|
|
|
212
213
|
*/
|
|
213
214
|
save(fileName) {
|
|
214
215
|
if (!fileName) fileName = 'result.json'
|
|
215
|
-
fs.writeFileSync(path.join(
|
|
216
|
+
fs.writeFileSync(path.join(store.outputDir, fileName), JSON.stringify(this.simplify(), null, 2))
|
|
216
217
|
}
|
|
217
218
|
|
|
218
219
|
/**
|
package/lib/step/base.js
CHANGED
|
@@ -3,6 +3,7 @@ import Secret from '../secret.js'
|
|
|
3
3
|
import { getCurrentTimeout } from '../timeout.js'
|
|
4
4
|
import { ucfirst, humanizeString, serializeError } from '../utils.js'
|
|
5
5
|
import recordStep from './record.js'
|
|
6
|
+
import store from '../store.js'
|
|
6
7
|
|
|
7
8
|
const STACK_LINE = 5
|
|
8
9
|
|
|
@@ -148,11 +149,11 @@ class Step {
|
|
|
148
149
|
const lines = this.stack.split('\n')
|
|
149
150
|
if (lines[STACK_LINE]) {
|
|
150
151
|
let line = lines[STACK_LINE].trim()
|
|
151
|
-
.replace(
|
|
152
|
+
.replace(store.codeceptDir || '', '.')
|
|
152
153
|
.trim()
|
|
153
154
|
|
|
154
155
|
// Map .temp.mjs back to original .ts files using container's tsFileMapping
|
|
155
|
-
const fileMapping =
|
|
156
|
+
const fileMapping = store.tsFileMapping
|
|
156
157
|
if (line.includes('.temp.mjs') && fileMapping) {
|
|
157
158
|
for (const [tsFile, mjsFile] of fileMapping.entries()) {
|
|
158
159
|
if (line.includes(mjsFile)) {
|
package/lib/step/record.js
CHANGED
|
@@ -63,7 +63,7 @@ function recordStep(step, args) {
|
|
|
63
63
|
step.endTime = +Date.now()
|
|
64
64
|
|
|
65
65
|
// Fix error stack to point to original .ts files (lazy import to avoid circular dependency)
|
|
66
|
-
const fileMapping =
|
|
66
|
+
const fileMapping = store.tsFileMapping
|
|
67
67
|
if (fileMapping) {
|
|
68
68
|
fixErrorStack(err, fileMapping)
|
|
69
69
|
}
|
package/lib/store.js
CHANGED
|
@@ -1,8 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Global store for current session
|
|
3
3
|
* @namespace
|
|
4
4
|
*/
|
|
5
5
|
const store = {
|
|
6
|
+
// --- Required (set once via initialize(), immutable after) ---
|
|
7
|
+
|
|
8
|
+
/** @type {string | null} */
|
|
9
|
+
_codeceptDir: null,
|
|
10
|
+
/** @type {string | null} */
|
|
11
|
+
_outputDir: null,
|
|
12
|
+
|
|
13
|
+
get codeceptDir() {
|
|
14
|
+
return this._codeceptDir || global.codecept_dir || null
|
|
15
|
+
},
|
|
16
|
+
set codeceptDir(val) {
|
|
17
|
+
this._codeceptDir = val
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
get outputDir() {
|
|
21
|
+
return this._outputDir || global.output_dir || null
|
|
22
|
+
},
|
|
23
|
+
set outputDir(val) {
|
|
24
|
+
this._outputDir = val
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
/** @type {boolean} */
|
|
28
|
+
workerMode: false,
|
|
29
|
+
|
|
30
|
+
// --- Session config (per-session, mutable, set at session start) ---
|
|
31
|
+
|
|
6
32
|
/**
|
|
7
33
|
* If we are in --debug mode
|
|
8
34
|
* @type {boolean}
|
|
@@ -27,20 +53,63 @@ const store = {
|
|
|
27
53
|
* @type {boolean}
|
|
28
54
|
*/
|
|
29
55
|
dryRun: false,
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Feature.only() was used
|
|
59
|
+
* @type {boolean}
|
|
60
|
+
*/
|
|
61
|
+
featureOnly: false,
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Scenario.only() was used
|
|
65
|
+
* @type {boolean}
|
|
66
|
+
*/
|
|
67
|
+
scenarioOnly: false,
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Mask sensitive data config
|
|
71
|
+
* @type {boolean|object}
|
|
72
|
+
*/
|
|
73
|
+
maskSensitiveData: false,
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* noGlobals mode — user imports everything
|
|
77
|
+
* @type {boolean}
|
|
78
|
+
*/
|
|
79
|
+
noGlobals: false,
|
|
80
|
+
|
|
81
|
+
// --- State (tracks current execution, changes constantly) ---
|
|
82
|
+
|
|
30
83
|
/**
|
|
31
84
|
* If we are in pause mode
|
|
32
85
|
* @type {boolean}
|
|
33
86
|
*/
|
|
34
87
|
onPause: false,
|
|
35
88
|
|
|
36
|
-
// current object states
|
|
37
|
-
|
|
38
89
|
/** @type {CodeceptJS.Test | null} */
|
|
39
90
|
currentTest: null,
|
|
40
91
|
/** @type {CodeceptJS.Step | null} */
|
|
41
92
|
currentStep: null,
|
|
42
93
|
/** @type {CodeceptJS.Suite | null} */
|
|
43
94
|
currentSuite: null,
|
|
95
|
+
|
|
96
|
+
/** @type {Map<string, string> | null} */
|
|
97
|
+
tsFileMapping: null,
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Initialize required store fields.
|
|
101
|
+
* These values cannot be overwritten after initialization.
|
|
102
|
+
* @param {object} opts
|
|
103
|
+
* @param {string} opts.codeceptDir - root directory of tests
|
|
104
|
+
* @param {string} opts.outputDir - resolved output directory
|
|
105
|
+
*/
|
|
106
|
+
initialize(opts) {
|
|
107
|
+
if (!opts.codeceptDir) throw new Error('codeceptDir is required')
|
|
108
|
+
if (!opts.outputDir) throw new Error('outputDir is required')
|
|
109
|
+
|
|
110
|
+
this._codeceptDir = opts.codeceptDir
|
|
111
|
+
this._outputDir = opts.outputDir
|
|
112
|
+
},
|
|
44
113
|
}
|
|
45
114
|
|
|
46
115
|
export default store
|
package/lib/translation.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import merge from 'lodash.merge'
|
|
2
2
|
import path from 'path'
|
|
3
3
|
import { createRequire } from 'module'
|
|
4
|
+
import store from './store.js'
|
|
4
5
|
|
|
5
6
|
const defaultVocabulary = {
|
|
6
7
|
I: 'I',
|
|
@@ -15,7 +16,7 @@ class Translation {
|
|
|
15
16
|
|
|
16
17
|
loadVocabulary(vocabularyFile) {
|
|
17
18
|
if (!vocabularyFile) return
|
|
18
|
-
const filePath = path.join(
|
|
19
|
+
const filePath = path.join(store.codeceptDir, vocabularyFile)
|
|
19
20
|
|
|
20
21
|
try {
|
|
21
22
|
const require = createRequire(import.meta.url)
|
package/lib/utils/mask_data.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { maskSensitiveData } from 'invisi-data'
|
|
2
|
+
import store from '../store.js'
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Mask sensitive data utility for CodeceptJS
|
|
@@ -33,7 +34,7 @@ export function maskData(input, config) {
|
|
|
33
34
|
* @returns {boolean|object} - Current masking configuration
|
|
34
35
|
*/
|
|
35
36
|
export function getMaskConfig() {
|
|
36
|
-
return global.maskSensitiveData || false
|
|
37
|
+
return store.maskSensitiveData || global.maskSensitiveData || false
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
/**
|
package/lib/utils.js
CHANGED
|
@@ -7,6 +7,7 @@ import getFunctionArguments from 'fn-args'
|
|
|
7
7
|
import deepClone from 'lodash.clonedeep'
|
|
8
8
|
import merge from 'lodash.merge'
|
|
9
9
|
import { convertColorToRGBA, isColorProperty } from './colorUtils.js'
|
|
10
|
+
import store from './store.js'
|
|
10
11
|
import Fuse from 'fuse.js'
|
|
11
12
|
import crypto from 'crypto'
|
|
12
13
|
import jsBeautify from 'js-beautify'
|
|
@@ -335,13 +336,13 @@ export const screenshotOutputFolder = function (fileName) {
|
|
|
335
336
|
const fileSep = path.sep
|
|
336
337
|
|
|
337
338
|
if (!fileName.includes(fileSep) || fileName.includes('record_')) {
|
|
338
|
-
return path.resolve(
|
|
339
|
+
return path.resolve(store.outputDir, fileName)
|
|
339
340
|
}
|
|
340
|
-
return path.resolve(
|
|
341
|
+
return path.resolve(store.codeceptDir, fileName)
|
|
341
342
|
}
|
|
342
343
|
|
|
343
344
|
export const relativeDir = function (fileName) {
|
|
344
|
-
return fileName.replace(
|
|
345
|
+
return fileName.replace(store.codeceptDir, '').replace(/^\//, '')
|
|
345
346
|
}
|
|
346
347
|
|
|
347
348
|
export const beautify = function (code) {
|
package/lib/workers.js
CHANGED
|
@@ -20,6 +20,7 @@ import event from './event.js'
|
|
|
20
20
|
import { deserializeTest } from './mocha/test.js'
|
|
21
21
|
import { deserializeSuite } from './mocha/suite.js'
|
|
22
22
|
import recorder from './recorder.js'
|
|
23
|
+
import store from './store.js'
|
|
23
24
|
import runHook from './hooks.js'
|
|
24
25
|
import WorkerStorage from './workerStorage.js'
|
|
25
26
|
import { createRuns } from './command/run-multiple/collection.js'
|
|
@@ -504,6 +505,7 @@ class Workers extends EventEmitter {
|
|
|
504
505
|
await this._ensureInitialized()
|
|
505
506
|
recorder.startUnlessRunning()
|
|
506
507
|
event.dispatcher.emit(event.workers.before)
|
|
508
|
+
store.workerMode = true
|
|
507
509
|
process.env.RUNS_WITH_WORKERS = 'true'
|
|
508
510
|
|
|
509
511
|
// Create workers and set up message handlers immediately (not in recorder queue)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeceptjs",
|
|
3
|
-
"version": "4.0.0-rc.
|
|
3
|
+
"version": "4.0.0-rc.15",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Supercharged End 2 End Testing Framework for NodeJS",
|
|
6
6
|
"keywords": [
|
|
@@ -41,7 +41,8 @@
|
|
|
41
41
|
"./els": "./lib/els.js",
|
|
42
42
|
"./effects": "./lib/effects.js",
|
|
43
43
|
"./steps": "./lib/steps.js",
|
|
44
|
-
"./store": "./lib/store.js"
|
|
44
|
+
"./store": "./lib/store.js",
|
|
45
|
+
"./assertions": "./lib/assertions.js"
|
|
45
46
|
},
|
|
46
47
|
"bin": {
|
|
47
48
|
"codeceptjs": "./bin/codecept.js",
|
|
@@ -214,6 +215,7 @@
|
|
|
214
215
|
}
|
|
215
216
|
},
|
|
216
217
|
"overrides": {
|
|
217
|
-
"tmp": "0.2.5"
|
|
218
|
+
"tmp": "0.2.5",
|
|
219
|
+
"js-yaml": "^4.1.1"
|
|
218
220
|
}
|
|
219
221
|
}
|