codeceptjs 3.7.5-beta.9 → 3.7.6-beta.1

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/lib/utils.js CHANGED
@@ -203,7 +203,7 @@ module.exports.test = {
203
203
  if (fs.existsSync(dataFile)) {
204
204
  break
205
205
  }
206
-
206
+
207
207
  // Use Node.js child_process.spawnSync with platform-specific sleep commands
208
208
  // This avoids busy waiting and allows other processes to run
209
209
  try {
@@ -221,7 +221,7 @@ module.exports.test = {
221
221
  // No-op loop - much lighter than previous approaches
222
222
  }
223
223
  }
224
-
224
+
225
225
  // Exponential backoff: gradually increase polling interval to reduce resource usage
226
226
  pollInterval = Math.min(pollInterval * 1.2, maxPollInterval)
227
227
  }
@@ -476,8 +476,12 @@ module.exports.isNotSet = function (obj) {
476
476
  return false
477
477
  }
478
478
 
479
- module.exports.emptyFolder = async directoryPath => {
480
- require('child_process').execSync(`rm -rf ${directoryPath}/*`)
479
+ module.exports.emptyFolder = directoryPath => {
480
+ // Do not throw on non-existent directory, since it may be created later
481
+ if (!fs.existsSync(directoryPath)) return
482
+ for (const file of fs.readdirSync(directoryPath)) {
483
+ fs.rmSync(path.join(directoryPath, file), { recursive: true, force: true })
484
+ }
481
485
  }
482
486
 
483
487
  module.exports.printObjectProperties = obj => {
@@ -576,6 +580,52 @@ module.exports.humanizeString = function (string) {
576
580
  return _result.join(' ').trim()
577
581
  }
578
582
 
583
+ /**
584
+ * Creates a circular-safe replacer function for JSON.stringify
585
+ * @param {string[]} keysToSkip - Keys to skip during serialization to break circular references
586
+ * @returns {Function} Replacer function for JSON.stringify
587
+ */
588
+ function createCircularSafeReplacer(keysToSkip = []) {
589
+ const seen = new WeakSet()
590
+ const defaultSkipKeys = ['parent', 'tests', 'suite', 'root', 'runner', 'ctx']
591
+ const skipKeys = new Set([...defaultSkipKeys, ...keysToSkip])
592
+
593
+ return function (key, value) {
594
+ // Skip specific keys that commonly cause circular references
595
+ if (key && skipKeys.has(key)) {
596
+ return undefined
597
+ }
598
+
599
+ if (value === null || typeof value !== 'object') {
600
+ return value
601
+ }
602
+
603
+ // Handle circular references
604
+ if (seen.has(value)) {
605
+ return `[Circular Reference to ${value.constructor?.name || 'Object'}]`
606
+ }
607
+
608
+ seen.add(value)
609
+ return value
610
+ }
611
+ }
612
+
613
+ /**
614
+ * Safely stringify an object, handling circular references
615
+ * @param {any} obj - Object to stringify
616
+ * @param {string[]} keysToSkip - Additional keys to skip during serialization
617
+ * @param {number} space - Number of spaces for indentation (default: 0)
618
+ * @returns {string} JSON string representation
619
+ */
620
+ module.exports.safeStringify = function (obj, keysToSkip = [], space = 0) {
621
+ try {
622
+ return JSON.stringify(obj, createCircularSafeReplacer(keysToSkip), space)
623
+ } catch (error) {
624
+ // Fallback for any remaining edge cases
625
+ return JSON.stringify({ error: `Failed to serialize: ${error.message}` }, null, space)
626
+ }
627
+ }
628
+
579
629
  module.exports.serializeError = function (error) {
580
630
  if (error) {
581
631
  const { stack, uncaught, message, actual, expected } = error
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codeceptjs",
3
- "version": "3.7.5-beta.9",
3
+ "version": "3.7.6-beta.1",
4
4
  "description": "Supercharged End 2 End Testing Framework for NodeJS",
5
5
  "keywords": [
6
6
  "acceptance",
@@ -29,14 +29,19 @@
29
29
  "docs/webapi/**"
30
30
  ],
31
31
  "main": "lib/index.js",
32
+ "module": "lib/index.js",
33
+ "types": "typings/index.d.ts",
32
34
  "exports": {
33
- ".": "./lib/index.js",
35
+ ".": {
36
+ "import": "./lib/index.js",
37
+ "require": "./lib/index.js",
38
+ "types": "./typings/index.d.ts"
39
+ },
34
40
  "./lib/*": "./lib/*.js",
35
41
  "./els": "./lib/els.js",
36
42
  "./effects": "./lib/effects.js",
37
43
  "./steps": "./lib/steps.js"
38
44
  },
39
- "types": "typings/index.d.ts",
40
45
  "bin": {
41
46
  "codeceptjs": "./bin/codecept.js"
42
47
  },
@@ -78,12 +83,12 @@
78
83
  "@codeceptjs/configure": "1.0.6",
79
84
  "@codeceptjs/helper": "2.0.4",
80
85
  "@cucumber/cucumber-expressions": "18",
81
- "@cucumber/gherkin": "32.1.2",
82
- "@cucumber/messages": "27.2.0",
86
+ "@cucumber/gherkin": "35.1.0",
87
+ "@cucumber/messages": "29.0.1",
83
88
  "@xmldom/xmldom": "0.9.8",
84
- "acorn": "8.14.1",
89
+ "acorn": "8.15.0",
85
90
  "arrify": "3.0.0",
86
- "axios": "1.11.0",
91
+ "axios": "1.12.2",
87
92
  "chalk": "4.1.2",
88
93
  "cheerio": "^1.0.0",
89
94
  "chokidar": "^4.0.3",
@@ -95,20 +100,20 @@
95
100
  "escape-string-regexp": "4.0.0",
96
101
  "figures": "3.2.0",
97
102
  "fn-args": "4.0.0",
98
- "fs-extra": "11.3.0",
103
+ "fs-extra": "11.3.2",
99
104
  "fuse.js": "^7.0.0",
100
105
  "glob": ">=9.0.0 <12",
101
106
  "html-minifier-terser": "7.2.0",
102
107
  "inquirer": "^8.2.7",
103
108
  "invisi-data": "^1.0.0",
104
- "joi": "17.13.3",
109
+ "joi": "18.0.1",
105
110
  "js-beautify": "1.15.4",
106
111
  "lodash.clonedeep": "4.5.0",
107
112
  "lodash.merge": "4.6.2",
108
113
  "lodash.shuffle": "4.2.0",
109
114
  "mkdirp": "3.0.1",
110
- "mocha": "11.6.0",
111
- "monocart-coverage-reports": "2.12.6",
115
+ "mocha": "11.7.2",
116
+ "monocart-coverage-reports": "2.12.9",
112
117
  "ms": "2.1.3",
113
118
  "multer": "^2.0.2",
114
119
  "ora-classic": "5.4.2",
@@ -120,35 +125,35 @@
120
125
  "uuid": "11.1.0"
121
126
  },
122
127
  "optionalDependencies": {
123
- "@codeceptjs/detox-helper": "1.1.8"
128
+ "@codeceptjs/detox-helper": "1.1.13"
124
129
  },
125
130
  "devDependencies": {
126
- "@apollo/server": "^4",
131
+ "@apollo/server": "^5",
127
132
  "@codeceptjs/expect-helper": "^1.0.2",
128
133
  "@codeceptjs/mock-request": "0.3.1",
129
134
  "@eslint/eslintrc": "3.3.1",
130
- "@eslint/js": "9.34.0",
135
+ "@eslint/js": "9.36.0",
131
136
  "@faker-js/faker": "9.8.0",
132
137
  "@pollyjs/adapter-puppeteer": "6.0.6",
133
138
  "@pollyjs/core": "6.0.6",
134
139
  "@types/chai": "5.2.2",
135
- "@types/inquirer": "9.0.7",
136
- "@types/node": "24.0.10",
140
+ "@types/inquirer": "9.0.9",
141
+ "@types/node": "24.5.2",
137
142
  "@wdio/sauce-service": "9.12.5",
138
143
  "@wdio/selenium-standalone-service": "8.15.0",
139
- "@wdio/utils": "9.15.0",
144
+ "@wdio/utils": "9.19.2",
140
145
  "@xmldom/xmldom": "0.9.8",
141
146
  "chai": "^4.0.0",
142
147
  "chai-as-promised": "7.1.2",
143
148
  "chai-subset": "1.6.0",
144
149
  "documentation": "14.0.3",
145
- "electron": "37.2.3",
146
- "eslint": "^9.24.0",
150
+ "electron": "38.1.2",
151
+ "eslint": "^9.36.0",
147
152
  "eslint-plugin-import": "2.32.0",
148
153
  "eslint-plugin-mocha": "11.1.0",
149
- "expect": "30.0.5",
154
+ "expect": "30.1.2",
150
155
  "express": "^5.1.0",
151
- "globals": "16.2.0",
156
+ "globals": "16.4.0",
152
157
  "graphql": "16.11.0",
153
158
  "graphql-tag": "^2.12.6",
154
159
  "husky": "9.1.7",
@@ -157,7 +162,7 @@
157
162
  "jsdoc-typeof-plugin": "1.0.0",
158
163
  "json-server": "0.17.4",
159
164
  "mochawesome": "^7.1.3",
160
- "playwright": "1.54.1",
165
+ "playwright": "1.55.1",
161
166
  "prettier": "^3.3.2",
162
167
  "puppeteer": "24.15.0",
163
168
  "qrcode-terminal": "0.12.0",
@@ -171,8 +176,8 @@
171
176
  "ts-node": "10.9.2",
172
177
  "tsd": "^0.33.0",
173
178
  "tsd-jsdoc": "2.5.0",
174
- "typedoc": "0.28.10",
175
- "typedoc-plugin-markdown": "4.8.1",
179
+ "typedoc": "0.28.13",
180
+ "typedoc-plugin-markdown": "4.9.0",
176
181
  "typescript": "5.8.3",
177
182
  "wdio-docker-service": "3.2.1",
178
183
  "webdriverio": "9.12.5",
@@ -189,5 +194,8 @@
189
194
  "compilerOptions": {
190
195
  "strict": false
191
196
  }
197
+ },
198
+ "overrides": {
199
+ "tmp": "0.2.5"
192
200
  }
193
201
  }
@@ -2737,7 +2737,6 @@ declare namespace CodeceptJS {
2737
2737
  */
2738
2738
  // @ts-ignore
2739
2739
  // @ts-ignore
2740
- // @ts-ignore
2741
2740
  type PlaywrightConfig = {
2742
2741
  url?: string;
2743
2742
  browser?: 'chromium' | 'firefox' | 'webkit' | 'electron';
@@ -6117,7 +6116,6 @@ declare namespace CodeceptJS {
6117
6116
  */
6118
6117
  // @ts-ignore
6119
6118
  // @ts-ignore
6120
- // @ts-ignore
6121
6119
  type PuppeteerConfig = {
6122
6120
  url: string;
6123
6121
  basicAuth?: any;
@@ -6541,6 +6539,17 @@ declare namespace CodeceptJS {
6541
6539
  * {{ react }}
6542
6540
  */
6543
6541
  _locate(): Promise<any>;
6542
+ /**
6543
+ * Get single element by different locator types, including strict locator
6544
+ * Should be used in custom helpers:
6545
+ *
6546
+ * ```js
6547
+ * const element = await this.helpers['Puppeteer']._locateElement({name: 'password'});
6548
+ * ```
6549
+ *
6550
+ * {{ react }}
6551
+ */
6552
+ _locateElement(): Promise<any>;
6544
6553
  /**
6545
6554
  * Find a checkbox by providing human-readable text:
6546
6555
  * NOTE: Assumes the checkable element exists
@@ -6577,6 +6586,17 @@ declare namespace CodeceptJS {
6577
6586
  * @returns WebElement of being used Web helper
6578
6587
  */
6579
6588
  grabWebElements(locator: CodeceptJS.LocatorOrString): Promise<any>;
6589
+ /**
6590
+ * Grab WebElement for given locator
6591
+ * Resumes test execution, so **should be used inside an async function with `await`** operator.
6592
+ *
6593
+ * ```js
6594
+ * const webElement = await I.grabWebElement('#button');
6595
+ * ```
6596
+ * @param locator - element located by CSS|XPath|strict locator.
6597
+ * @returns WebElement of being used Web helper
6598
+ */
6599
+ grabWebElement(locator: CodeceptJS.LocatorOrString): Promise<any>;
6580
6600
  /**
6581
6601
  * Switch focus to a particular tab by its number. It waits tabs loading and then switch tab
6582
6602
  *
@@ -7912,6 +7932,22 @@ declare namespace CodeceptJS {
7912
7932
  */
7913
7933
  flushWebSocketMessages(): Promise<any>;
7914
7934
  }
7935
+ /**
7936
+ * Find elements using Puppeteer's native element discovery methods
7937
+ * Note: Unlike Playwright, Puppeteer's Locator API doesn't have .all() method for multiple elements
7938
+ * @param matcher - Puppeteer context to search within
7939
+ * @param locator - Locator specification
7940
+ * @returns Array of ElementHandle objects
7941
+ */
7942
+ function findElements(matcher: any, locator: any | string): Promise<any[]>;
7943
+ /**
7944
+ * Find a single element using Puppeteer's native element discovery methods
7945
+ * Note: Puppeteer Locator API doesn't have .first() method like Playwright
7946
+ * @param matcher - Puppeteer context to search within
7947
+ * @param locator - Locator specification
7948
+ * @returns Single ElementHandle object
7949
+ */
7950
+ function findElement(matcher: any, locator: any | string): Promise<object>;
7915
7951
  /**
7916
7952
  * ## Configuration
7917
7953
  * @property [endpoint] - API base URL
@@ -7926,7 +7962,6 @@ declare namespace CodeceptJS {
7926
7962
  */
7927
7963
  // @ts-ignore
7928
7964
  // @ts-ignore
7929
- // @ts-ignore
7930
7965
  type RESTConfig = {
7931
7966
  endpoint?: string;
7932
7967
  prettyPrintJson?: boolean;
@@ -8052,6 +8087,16 @@ declare namespace CodeceptJS {
8052
8087
  * @returns response
8053
8088
  */
8054
8089
  sendGetRequest(url: any, headers?: any): Promise<any>;
8090
+ /**
8091
+ * Send HEAD request to REST API
8092
+ *
8093
+ * ```js
8094
+ * I.sendHeadRequest('/api/users.json');
8095
+ * ```
8096
+ * @param [headers = {}] - the headers object to be sent. By default, it is sent as an empty object
8097
+ * @returns response
8098
+ */
8099
+ sendHeadRequest(url: any, headers?: any): Promise<any>;
8055
8100
  /**
8056
8101
  * Sends POST request to API.
8057
8102
  *
@@ -9074,7 +9119,6 @@ declare namespace CodeceptJS {
9074
9119
  */
9075
9120
  // @ts-ignore
9076
9121
  // @ts-ignore
9077
- // @ts-ignore
9078
9122
  type WebDriverConfig = {
9079
9123
  url: string;
9080
9124
  browser: string;
@@ -9545,6 +9589,17 @@ declare namespace CodeceptJS {
9545
9589
  * @returns WebElement of being used Web helper
9546
9590
  */
9547
9591
  grabWebElements(locator: CodeceptJS.LocatorOrString): Promise<any>;
9592
+ /**
9593
+ * Grab WebElement for given locator
9594
+ * Resumes test execution, so **should be used inside an async function with `await`** operator.
9595
+ *
9596
+ * ```js
9597
+ * const webElement = await I.grabWebElement('#button');
9598
+ * ```
9599
+ * @param locator - element located by CSS|XPath|strict locator.
9600
+ * @returns WebElement of being used Web helper
9601
+ */
9602
+ grabWebElement(locator: CodeceptJS.LocatorOrString): Promise<any>;
9548
9603
  /**
9549
9604
  * Set [WebDriver timeouts](https://webdriver.io/docs/timeouts.html) in realtime.
9550
9605
  *
@@ -2827,7 +2827,6 @@ declare namespace CodeceptJS {
2827
2827
  */
2828
2828
  // @ts-ignore
2829
2829
  // @ts-ignore
2830
- // @ts-ignore
2831
2830
  type PlaywrightConfig = {
2832
2831
  url?: string;
2833
2832
  browser?: 'chromium' | 'firefox' | 'webkit' | 'electron';
@@ -6358,7 +6357,6 @@ declare namespace CodeceptJS {
6358
6357
  */
6359
6358
  // @ts-ignore
6360
6359
  // @ts-ignore
6361
- // @ts-ignore
6362
6360
  type PuppeteerConfig = {
6363
6361
  url: string;
6364
6362
  basicAuth?: any;
@@ -6798,6 +6796,17 @@ declare namespace CodeceptJS {
6798
6796
  * {{ react }}
6799
6797
  */
6800
6798
  _locate(): void;
6799
+ /**
6800
+ * Get single element by different locator types, including strict locator
6801
+ * Should be used in custom helpers:
6802
+ *
6803
+ * ```js
6804
+ * const element = await this.helpers['Puppeteer']._locateElement({name: 'password'});
6805
+ * ```
6806
+ *
6807
+ * {{ react }}
6808
+ */
6809
+ _locateElement(): void;
6801
6810
  /**
6802
6811
  * Find a checkbox by providing human-readable text:
6803
6812
  * NOTE: Assumes the checkable element exists
@@ -6834,6 +6843,17 @@ declare namespace CodeceptJS {
6834
6843
  * @returns WebElement of being used Web helper
6835
6844
  */
6836
6845
  grabWebElements(locator: CodeceptJS.LocatorOrString): Promise<any>;
6846
+ /**
6847
+ * Grab WebElement for given locator
6848
+ * Resumes test execution, so **should be used inside an async function with `await`** operator.
6849
+ *
6850
+ * ```js
6851
+ * const webElement = await I.grabWebElement('#button');
6852
+ * ```
6853
+ * @param locator - element located by CSS|XPath|strict locator.
6854
+ * @returns WebElement of being used Web helper
6855
+ */
6856
+ grabWebElement(locator: CodeceptJS.LocatorOrString): Promise<any>;
6837
6857
  /**
6838
6858
  * Switch focus to a particular tab by its number. It waits tabs loading and then switch tab
6839
6859
  *
@@ -8289,6 +8309,22 @@ declare namespace CodeceptJS {
8289
8309
  */
8290
8310
  flushWebSocketMessages(): void;
8291
8311
  }
8312
+ /**
8313
+ * Find elements using Puppeteer's native element discovery methods
8314
+ * Note: Unlike Playwright, Puppeteer's Locator API doesn't have .all() method for multiple elements
8315
+ * @param matcher - Puppeteer context to search within
8316
+ * @param locator - Locator specification
8317
+ * @returns Array of ElementHandle objects
8318
+ */
8319
+ function findElements(matcher: any, locator: any | string): Promise<any[]>;
8320
+ /**
8321
+ * Find a single element using Puppeteer's native element discovery methods
8322
+ * Note: Puppeteer Locator API doesn't have .first() method like Playwright
8323
+ * @param matcher - Puppeteer context to search within
8324
+ * @param locator - Locator specification
8325
+ * @returns Single ElementHandle object
8326
+ */
8327
+ function findElement(matcher: any, locator: any | string): Promise<object>;
8292
8328
  /**
8293
8329
  * ## Configuration
8294
8330
  * @property [endpoint] - API base URL
@@ -8303,7 +8339,6 @@ declare namespace CodeceptJS {
8303
8339
  */
8304
8340
  // @ts-ignore
8305
8341
  // @ts-ignore
8306
- // @ts-ignore
8307
8342
  type RESTConfig = {
8308
8343
  endpoint?: string;
8309
8344
  prettyPrintJson?: boolean;
@@ -8429,6 +8464,16 @@ declare namespace CodeceptJS {
8429
8464
  * @returns response
8430
8465
  */
8431
8466
  sendGetRequest(url: any, headers?: any): Promise<any>;
8467
+ /**
8468
+ * Send HEAD request to REST API
8469
+ *
8470
+ * ```js
8471
+ * I.sendHeadRequest('/api/users.json');
8472
+ * ```
8473
+ * @param [headers = {}] - the headers object to be sent. By default, it is sent as an empty object
8474
+ * @returns response
8475
+ */
8476
+ sendHeadRequest(url: any, headers?: any): Promise<any>;
8432
8477
  /**
8433
8478
  * Sends POST request to API.
8434
8479
  *
@@ -9511,7 +9556,6 @@ declare namespace CodeceptJS {
9511
9556
  */
9512
9557
  // @ts-ignore
9513
9558
  // @ts-ignore
9514
- // @ts-ignore
9515
9559
  type WebDriverConfig = {
9516
9560
  url: string;
9517
9561
  browser: string;
@@ -9982,6 +10026,17 @@ declare namespace CodeceptJS {
9982
10026
  * @returns WebElement of being used Web helper
9983
10027
  */
9984
10028
  grabWebElements(locator: CodeceptJS.LocatorOrString): Promise<any>;
10029
+ /**
10030
+ * Grab WebElement for given locator
10031
+ * Resumes test execution, so **should be used inside an async function with `await`** operator.
10032
+ *
10033
+ * ```js
10034
+ * const webElement = await I.grabWebElement('#button');
10035
+ * ```
10036
+ * @param locator - element located by CSS|XPath|strict locator.
10037
+ * @returns WebElement of being used Web helper
10038
+ */
10039
+ grabWebElement(locator: CodeceptJS.LocatorOrString): Promise<any>;
9985
10040
  /**
9986
10041
  * Set [WebDriver timeouts](https://webdriver.io/docs/timeouts.html) in realtime.
9987
10042
  *
@@ -11526,6 +11581,13 @@ declare namespace CodeceptJS {
11526
11581
  * Loads tests by pattern or by config.tests
11527
11582
  */
11528
11583
  loadTests(pattern?: string): void;
11584
+ /**
11585
+ * Apply sharding to test files based on shard configuration
11586
+ * @param testFiles - Array of test file paths
11587
+ * @param shardConfig - Shard configuration in format "index/total" (e.g., "1/4")
11588
+ * @returns - Filtered array of test files for this shard
11589
+ */
11590
+ _applySharding(testFiles: string[], shardConfig: string): string[];
11529
11591
  /**
11530
11592
  * Run a specific test or all loaded tests.
11531
11593
  */
@@ -11713,7 +11775,7 @@ declare namespace CodeceptJS {
11713
11775
  * - Logs errors and retries the callback until it either succeeds or the maximum number of attempts is reached.
11714
11776
  * - Restores the session state after each attempt, whether successful or not.
11715
11777
  * @example
11716
- * const { hopeThat } = require('codeceptjs/effects')
11778
+ * const { retryTo } = require('codeceptjs/effects')
11717
11779
  * await retryTo((tries) => {
11718
11780
  * if (tries < 3) {
11719
11781
  * I.see('Non-existent element'); // Simulates a failure
@@ -12019,6 +12081,10 @@ declare namespace CodeceptJS {
12019
12081
  * Get a state of current queue and tasks
12020
12082
  */
12021
12083
  toString(): string;
12084
+ /**
12085
+ * Get current session ID
12086
+ */
12087
+ getCurrentSessionId(): string | null;
12022
12088
  }
12023
12089
  interface RecorderSession {
12024
12090
  running: boolean;