codeceptjs 4.0.0-rc.11 → 4.0.0-rc.16

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.
Files changed (54) hide show
  1. package/lib/ai.js +3 -2
  2. package/lib/assertions.js +18 -0
  3. package/lib/codecept.js +6 -6
  4. package/lib/command/check.js +2 -1
  5. package/lib/command/dryRun.js +2 -3
  6. package/lib/command/generate.js +2 -0
  7. package/lib/command/gherkin/snippets.js +5 -4
  8. package/lib/command/init.js +1 -0
  9. package/lib/command/run-multiple.js +2 -0
  10. package/lib/command/run-workers.js +1 -0
  11. package/lib/command/run.js +1 -1
  12. package/lib/command/workers/runTests.js +10 -10
  13. package/lib/container.js +63 -13
  14. package/lib/effects.js +17 -0
  15. package/lib/element/WebElement.js +128 -0
  16. package/lib/globals.js +22 -10
  17. package/lib/heal.js +4 -3
  18. package/lib/helper/ApiDataFactory.js +2 -1
  19. package/lib/helper/FileSystem.js +3 -2
  20. package/lib/helper/GraphQLDataFactory.js +2 -1
  21. package/lib/helper/Playwright.js +17 -16
  22. package/lib/helper/Puppeteer.js +16 -6
  23. package/lib/helper/WebDriver.js +8 -2
  24. package/lib/helper/extras/Download.js +45 -0
  25. package/lib/helper/extras/richTextEditor.js +178 -0
  26. package/lib/history.js +3 -2
  27. package/lib/listener/config.js +6 -4
  28. package/lib/listener/emptyRun.js +2 -1
  29. package/lib/listener/helpers.js +4 -1
  30. package/lib/listener/mocha.js +2 -1
  31. package/lib/listener/pageobjects.js +43 -0
  32. package/lib/listener/result.js +3 -2
  33. package/lib/locator.js +112 -0
  34. package/lib/mocha/cli.js +4 -2
  35. package/lib/mocha/factory.js +2 -1
  36. package/lib/mocha/scenarioConfig.js +2 -1
  37. package/lib/mocha/ui.js +5 -6
  38. package/lib/plugin/aiTrace.js +4 -3
  39. package/lib/plugin/analyze.js +1 -1
  40. package/lib/plugin/auth.js +3 -3
  41. package/lib/plugin/pageInfo.js +2 -1
  42. package/lib/plugin/pauseOn.js +167 -0
  43. package/lib/plugin/screenshotOnFail.js +3 -4
  44. package/lib/plugin/stepByStepReport.js +5 -4
  45. package/lib/rerun.js +2 -1
  46. package/lib/result.js +2 -1
  47. package/lib/step/base.js +3 -2
  48. package/lib/step/record.js +1 -1
  49. package/lib/store.js +72 -3
  50. package/lib/translation.js +2 -1
  51. package/lib/utils/mask_data.js +2 -1
  52. package/lib/utils.js +4 -3
  53. package/lib/workers.js +2 -0
  54. package/package.json +5 -3
@@ -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: global.output_dir,
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(global.codecept_dir, config.output) : defaultConfig.output
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 (!step.test) return // Ignore steps from AfterSuite
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(global.codecept_dir, test)
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(global.output_dir, fileName), JSON.stringify(this.simplify(), null, 2))
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(global.codecept_dir || '', '.')
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 = global.container?.tsFileMapping?.()
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)) {
@@ -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 = global.container?.tsFileMapping?.()
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
- * global values for current session
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
@@ -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(global.codecept_dir, vocabularyFile)
19
+ const filePath = path.join(store.codeceptDir, vocabularyFile)
19
20
 
20
21
  try {
21
22
  const require = createRequire(import.meta.url)
@@ -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(global.output_dir, fileName)
339
+ return path.resolve(store.outputDir, fileName)
339
340
  }
340
- return path.resolve(global.codecept_dir, fileName)
341
+ return path.resolve(store.codeceptDir, fileName)
341
342
  }
342
343
 
343
344
  export const relativeDir = function (fileName) {
344
- return fileName.replace(global.codecept_dir, '').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.11",
3
+ "version": "4.0.0-rc.16",
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
  }