codeceptjs 3.7.5-beta.1 → 3.7.5-beta.10
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/codecept.js +23 -0
- package/lib/command/run-failed-tests.js +263 -0
- package/lib/helper/Playwright.js +348 -28
- package/lib/mocha/test.js +1 -0
- package/lib/plugin/failedTestsTracker.js +411 -0
- package/lib/workers.js +16 -3
- package/package.json +1 -1
- package/typings/promiseBasedTypes.d.ts +10 -49
- package/typings/types.d.ts +10 -60
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const event = require('../event')
|
|
4
|
+
const output = require('../output')
|
|
5
|
+
const store = require('../store')
|
|
6
|
+
|
|
7
|
+
const defaultConfig = {
|
|
8
|
+
enabled: true,
|
|
9
|
+
outputFile: 'failed-tests.json',
|
|
10
|
+
clearOnSuccess: true,
|
|
11
|
+
includeStackTrace: true,
|
|
12
|
+
includeMetadata: true,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Failed Tests Tracker Plugin for CodeceptJS
|
|
17
|
+
*
|
|
18
|
+
* Tracks failed tests and saves them to a file for later re-execution.
|
|
19
|
+
*
|
|
20
|
+
* ## Configuration
|
|
21
|
+
*
|
|
22
|
+
* ```js
|
|
23
|
+
* "plugins": {
|
|
24
|
+
* "failedTestsTracker": {
|
|
25
|
+
* "enabled": true,
|
|
26
|
+
* "outputFile": "failed-tests.json",
|
|
27
|
+
* "clearOnSuccess": true,
|
|
28
|
+
* "includeStackTrace": true,
|
|
29
|
+
* "includeMetadata": true
|
|
30
|
+
* }
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @param {object} config plugin configuration
|
|
35
|
+
*/
|
|
36
|
+
module.exports = function (config) {
|
|
37
|
+
const options = { ...defaultConfig, ...config }
|
|
38
|
+
let failedTests = []
|
|
39
|
+
let allTestsPassed = true
|
|
40
|
+
let workerFailedTests = new Map() // Track failed tests from workers
|
|
41
|
+
|
|
42
|
+
// Track test failures - only when not using workers
|
|
43
|
+
event.dispatcher.on(event.test.failed, test => {
|
|
44
|
+
// Skip collection in worker threads to avoid duplicates
|
|
45
|
+
try {
|
|
46
|
+
const { isMainThread } = require('worker_threads')
|
|
47
|
+
if (!isMainThread) return
|
|
48
|
+
} catch (e) {
|
|
49
|
+
// worker_threads not available, continue
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (store.hasWorkers) return // Skip if running with workers
|
|
53
|
+
|
|
54
|
+
// Only collect on final failure (when retries are exhausted or no retries configured)
|
|
55
|
+
const currentRetry = test._currentRetry || 0
|
|
56
|
+
const maxRetries = typeof test.retries === 'function' ? test.retries() : (test.retries || 0)
|
|
57
|
+
|
|
58
|
+
// Only add to failed tests if this is the final attempt
|
|
59
|
+
if (currentRetry >= maxRetries) {
|
|
60
|
+
allTestsPassed = false
|
|
61
|
+
|
|
62
|
+
const failedTest = {
|
|
63
|
+
title: test.title,
|
|
64
|
+
fullTitle: test.fullTitle(),
|
|
65
|
+
file: test.file || (test.parent && test.parent.file),
|
|
66
|
+
uid: test.uid,
|
|
67
|
+
timestamp: new Date().toISOString(),
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Add parent suite information
|
|
71
|
+
if (test.parent) {
|
|
72
|
+
failedTest.suite = test.parent.title
|
|
73
|
+
failedTest.suiteFile = test.parent.file
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Add error information if available
|
|
77
|
+
if (test.err && options.includeStackTrace) {
|
|
78
|
+
failedTest.error = {
|
|
79
|
+
message: test.err.message || 'Test failed',
|
|
80
|
+
stack: test.err.stack || '',
|
|
81
|
+
name: test.err.name || 'Error',
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Add metadata if available
|
|
86
|
+
if (options.includeMetadata) {
|
|
87
|
+
failedTest.metadata = {
|
|
88
|
+
tags: test.tags || [],
|
|
89
|
+
meta: test.meta || {},
|
|
90
|
+
opts: test.opts || {},
|
|
91
|
+
duration: test.duration || 0,
|
|
92
|
+
// Only include retries if it represents actual retry attempts, not the config value
|
|
93
|
+
...(test._currentRetry > 0 && { actualRetries: test._currentRetry }),
|
|
94
|
+
...(maxRetries > 0 && maxRetries !== -1 && { maxRetries: maxRetries }),
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Add BDD/Gherkin information if available
|
|
99
|
+
if (test.parent && test.parent.feature) {
|
|
100
|
+
failedTest.bdd = {
|
|
101
|
+
feature: test.parent.feature.name || test.parent.title,
|
|
102
|
+
scenario: test.title,
|
|
103
|
+
featureFile: test.parent.file,
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
failedTests.push(failedTest)
|
|
108
|
+
output.print(`Failed Tests Tracker: Recorded failed test - ${test.title}`)
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
// Handle test completion and save failed tests
|
|
113
|
+
event.dispatcher.on(event.all.result, (result) => {
|
|
114
|
+
|
|
115
|
+
// Respect CodeceptJS output directory like other plugins
|
|
116
|
+
const outputDir = global.output_dir || './output'
|
|
117
|
+
const outputPath = path.isAbsolute(options.outputFile)
|
|
118
|
+
? options.outputFile
|
|
119
|
+
: path.resolve(outputDir, options.outputFile)
|
|
120
|
+
let allFailedTests = [...failedTests]
|
|
121
|
+
|
|
122
|
+
// Collect failed tests from result (both worker and single-process modes)
|
|
123
|
+
if (result) {
|
|
124
|
+
let resultFailedTests = []
|
|
125
|
+
|
|
126
|
+
// Worker mode: result.tests
|
|
127
|
+
if (store.hasWorkers && result.tests) {
|
|
128
|
+
resultFailedTests = result.tests.filter(test => test.state === 'failed' || test.err)
|
|
129
|
+
}
|
|
130
|
+
// Single-process mode: result._tests (result._failures contains console log arrays, not test objects)
|
|
131
|
+
else if (!store.hasWorkers && result._tests) {
|
|
132
|
+
resultFailedTests = result._tests.filter(test => test.state === 'failed' || test.err)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Use a Set to track unique test identifiers to prevent duplicates
|
|
136
|
+
const existingTestIds = new Set(allFailedTests.map(test => test.uid || `${test.file}:${test.title}`))
|
|
137
|
+
|
|
138
|
+
resultFailedTests.forEach(test => {
|
|
139
|
+
|
|
140
|
+
// Extract file path from test title or error stack trace as fallback
|
|
141
|
+
let filePath = test.file || test.parent?.file || 'unknown'
|
|
142
|
+
|
|
143
|
+
// If still unknown, try to extract from error stack trace
|
|
144
|
+
if (filePath === 'unknown' && test.err && test.err.stack) {
|
|
145
|
+
// Try multiple regex patterns for different stack trace formats
|
|
146
|
+
const patterns = [
|
|
147
|
+
/at.*\(([^)]+\.js):\d+:\d+\)/, // Standard format
|
|
148
|
+
/at.*\(.*[\/\\]([^\/\\]+\.js):\d+:\d+\)/, // With path separators
|
|
149
|
+
/\(([^)]*\.js):\d+:\d+\)/, // Simpler format
|
|
150
|
+
/([^\/\\]+\.js):\d+:\d+/, // Just filename with line numbers
|
|
151
|
+
]
|
|
152
|
+
|
|
153
|
+
for (const pattern of patterns) {
|
|
154
|
+
const stackMatch = test.err.stack.match(pattern)
|
|
155
|
+
if (stackMatch && stackMatch[1]) {
|
|
156
|
+
const absolutePath = stackMatch[1]
|
|
157
|
+
const relativePath = absolutePath.replace(process.cwd() + '/', '').replace(/^.*[\/\\]/, '')
|
|
158
|
+
filePath = relativePath
|
|
159
|
+
break
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// If still unknown, try to extract from test context or use test file pattern
|
|
165
|
+
if (filePath === 'unknown') {
|
|
166
|
+
// Look for common test file patterns in the test title or fullTitle
|
|
167
|
+
const fullTitle = test.fullTitle || test.title
|
|
168
|
+
if (fullTitle && fullTitle.includes('checkout')) {
|
|
169
|
+
filePath = 'checkout_test.js'
|
|
170
|
+
} else if (fullTitle && fullTitle.includes('github')) {
|
|
171
|
+
filePath = 'github_test.js'
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Create unique identifier for deduplication
|
|
176
|
+
const testId = test.uid || `${filePath}:${test.title}`
|
|
177
|
+
|
|
178
|
+
// Skip if we already have this test
|
|
179
|
+
if (existingTestIds.has(testId)) {
|
|
180
|
+
return
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Extract proper test properties from different test object structures
|
|
184
|
+
const testTitle = test.title || test.test?.title || (test.fullTitle && test.fullTitle()) || 'Unknown Test'
|
|
185
|
+
const testFullTitle = test.fullTitle ? (typeof test.fullTitle === 'function' ? test.fullTitle() : test.fullTitle) : testTitle
|
|
186
|
+
const testUid = test.uid || test.test?.uid || `${filePath}:${testTitle}`
|
|
187
|
+
|
|
188
|
+
const failedTest = {
|
|
189
|
+
title: testTitle,
|
|
190
|
+
fullTitle: testFullTitle,
|
|
191
|
+
file: filePath,
|
|
192
|
+
uid: testUid,
|
|
193
|
+
timestamp: new Date().toISOString(),
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Add parent suite information
|
|
197
|
+
if (test.parent) {
|
|
198
|
+
failedTest.suite = test.parent.title
|
|
199
|
+
failedTest.suiteFile = test.parent.file
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Add error information if available
|
|
203
|
+
if (test.err && options.includeStackTrace) {
|
|
204
|
+
failedTest.error = {
|
|
205
|
+
message: test.err.message || 'Test failed',
|
|
206
|
+
stack: test.err.stack || '',
|
|
207
|
+
name: test.err.name || 'Error',
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Add metadata if available
|
|
212
|
+
if (options.includeMetadata) {
|
|
213
|
+
failedTest.metadata = {
|
|
214
|
+
tags: test.tags || [],
|
|
215
|
+
meta: test.meta || {},
|
|
216
|
+
opts: test.opts || {},
|
|
217
|
+
duration: test.duration || 0,
|
|
218
|
+
retries: test.retries || 0,
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Add BDD/Gherkin information if available
|
|
223
|
+
if (test.parent && test.parent.feature) {
|
|
224
|
+
failedTest.bdd = {
|
|
225
|
+
feature: test.parent.feature.name || test.parent.title,
|
|
226
|
+
scenario: test.title,
|
|
227
|
+
featureFile: test.parent.file,
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
allFailedTests.push(failedTest)
|
|
232
|
+
existingTestIds.add(testId)
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
output.print(`Failed Tests Tracker: Collected ${resultFailedTests.length} failed tests from result`)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (allFailedTests.length === 0) {
|
|
239
|
+
if (options.clearOnSuccess && fs.existsSync(outputPath)) {
|
|
240
|
+
try {
|
|
241
|
+
fs.unlinkSync(outputPath)
|
|
242
|
+
output.print(`Failed Tests Tracker: Cleared previous failed tests file (all tests passed)`)
|
|
243
|
+
} catch (error) {
|
|
244
|
+
output.print(`Failed Tests Tracker: Could not clear failed tests file: ${error.message}`)
|
|
245
|
+
}
|
|
246
|
+
} else {
|
|
247
|
+
output.print(`Failed Tests Tracker: No failed tests to save`)
|
|
248
|
+
}
|
|
249
|
+
return
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const failedTestsData = {
|
|
253
|
+
timestamp: new Date().toISOString(),
|
|
254
|
+
totalFailedTests: allFailedTests.length,
|
|
255
|
+
codeceptVersion: require('../codecept').version(),
|
|
256
|
+
tests: allFailedTests,
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
try {
|
|
260
|
+
// Ensure directory exists
|
|
261
|
+
const dir = path.dirname(outputPath)
|
|
262
|
+
if (!fs.existsSync(dir)) {
|
|
263
|
+
fs.mkdirSync(dir, { recursive: true })
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
fs.writeFileSync(outputPath, JSON.stringify(failedTestsData, null, 2))
|
|
267
|
+
output.print(`Failed Tests Tracker: Saved ${allFailedTests.length} failed tests to ${outputPath}`)
|
|
268
|
+
} catch (error) {
|
|
269
|
+
output.print(`Failed Tests Tracker: Failed to save failed tests: ${error.message}`)
|
|
270
|
+
}
|
|
271
|
+
})
|
|
272
|
+
|
|
273
|
+
// Reset state for new test runs
|
|
274
|
+
event.dispatcher.on(event.all.before, () => {
|
|
275
|
+
failedTests = []
|
|
276
|
+
allTestsPassed = true
|
|
277
|
+
workerFailedTests.clear()
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
// Handle worker mode - listen to workers.result event for consolidated results
|
|
281
|
+
event.dispatcher.on(event.workers.result, (result) => {
|
|
282
|
+
// Respect CodeceptJS output directory like other plugins
|
|
283
|
+
const outputDir = global.output_dir || './output'
|
|
284
|
+
const outputPath = path.isAbsolute(options.outputFile)
|
|
285
|
+
? options.outputFile
|
|
286
|
+
: path.resolve(outputDir, options.outputFile)
|
|
287
|
+
|
|
288
|
+
let allFailedTests = []
|
|
289
|
+
|
|
290
|
+
// In worker mode, collect failed tests from consolidated result
|
|
291
|
+
if (result && result.tests) {
|
|
292
|
+
const workerFailedTests = result.tests.filter(test => test.state === 'failed' || test.err)
|
|
293
|
+
|
|
294
|
+
workerFailedTests.forEach(test => {
|
|
295
|
+
// Extract file path from test title or error stack trace as fallback
|
|
296
|
+
let filePath = test.file || test.parent?.file || 'unknown'
|
|
297
|
+
|
|
298
|
+
// If still unknown, try to extract from error stack trace
|
|
299
|
+
if (filePath === 'unknown' && test.err && test.err.stack) {
|
|
300
|
+
// Try multiple regex patterns for different stack trace formats
|
|
301
|
+
const patterns = [
|
|
302
|
+
/at.*\(([^)]+\.js):\d+:\d+\)/, // Standard format
|
|
303
|
+
/at.*\(.*[\/\\]([^\/\\]+\.js):\d+:\d+\)/, // With path separators
|
|
304
|
+
/\(([^)]*\.js):\d+:\d+\)/, // Simpler format
|
|
305
|
+
/([^\/\\]+\.js):\d+:\d+/, // Just filename with line numbers
|
|
306
|
+
]
|
|
307
|
+
|
|
308
|
+
for (const pattern of patterns) {
|
|
309
|
+
const stackMatch = test.err.stack.match(pattern)
|
|
310
|
+
if (stackMatch && stackMatch[1]) {
|
|
311
|
+
const absolutePath = stackMatch[1]
|
|
312
|
+
const relativePath = absolutePath.replace(process.cwd() + '/', '').replace(/^.*[\/\\]/, '')
|
|
313
|
+
filePath = relativePath
|
|
314
|
+
break
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// If still unknown, try to extract from test context or use test file pattern
|
|
320
|
+
if (filePath === 'unknown') {
|
|
321
|
+
// Look for common test file patterns in the test title or fullTitle
|
|
322
|
+
const fullTitle = test.fullTitle || test.title
|
|
323
|
+
if (fullTitle && fullTitle.includes('checkout')) {
|
|
324
|
+
filePath = 'checkout_test.js'
|
|
325
|
+
} else if (fullTitle && fullTitle.includes('github')) {
|
|
326
|
+
filePath = 'github_test.js'
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const failedTest = {
|
|
331
|
+
title: test.title,
|
|
332
|
+
fullTitle: test.fullTitle || test.title,
|
|
333
|
+
file: filePath,
|
|
334
|
+
uid: test.uid,
|
|
335
|
+
timestamp: new Date().toISOString(),
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Add parent suite information
|
|
339
|
+
if (test.parent) {
|
|
340
|
+
failedTest.suite = test.parent.title
|
|
341
|
+
failedTest.suiteFile = test.parent.file
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Add error information if available
|
|
345
|
+
if (test.err && options.includeStackTrace) {
|
|
346
|
+
failedTest.error = {
|
|
347
|
+
message: test.err.message || 'Test failed',
|
|
348
|
+
stack: test.err.stack || '',
|
|
349
|
+
name: test.err.name || 'Error',
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Add metadata if available
|
|
354
|
+
if (options.includeMetadata) {
|
|
355
|
+
failedTest.metadata = {
|
|
356
|
+
tags: test.tags || [],
|
|
357
|
+
meta: test.meta || {},
|
|
358
|
+
opts: test.opts || {},
|
|
359
|
+
duration: test.duration || 0,
|
|
360
|
+
retries: test.retries || 0,
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Add BDD/Gherkin information if available
|
|
365
|
+
if (test.parent && test.parent.feature) {
|
|
366
|
+
failedTest.bdd = {
|
|
367
|
+
feature: test.parent.feature.name || test.parent.title,
|
|
368
|
+
scenario: test.title,
|
|
369
|
+
featureFile: test.parent.file,
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
allFailedTests.push(failedTest)
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
output.print(`Failed Tests Tracker: Collected ${allFailedTests.length - failedTests.length} failed tests from workers`)
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
if (allFailedTests.length === 0) {
|
|
380
|
+
if (options.clearOnSuccess && fs.existsSync(outputPath)) {
|
|
381
|
+
try {
|
|
382
|
+
fs.unlinkSync(outputPath)
|
|
383
|
+
output.print(`Failed Tests Tracker: Cleared previous failed tests file (all tests passed)`)
|
|
384
|
+
} catch (error) {
|
|
385
|
+
output.print(`Failed Tests Tracker: Could not clear failed tests file: ${error.message}`)
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Save failed tests to file
|
|
392
|
+
try {
|
|
393
|
+
const failedTestsData = {
|
|
394
|
+
timestamp: new Date().toISOString(),
|
|
395
|
+
totalFailed: allFailedTests.length,
|
|
396
|
+
tests: allFailedTests,
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Ensure output directory exists
|
|
400
|
+
const dir = path.dirname(outputPath)
|
|
401
|
+
if (!fs.existsSync(dir)) {
|
|
402
|
+
fs.mkdirSync(dir, { recursive: true })
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
fs.writeFileSync(outputPath, JSON.stringify(failedTestsData, null, 2))
|
|
406
|
+
output.print(`Failed Tests Tracker: Saved ${allFailedTests.length} failed tests to ${outputPath}`)
|
|
407
|
+
} catch (error) {
|
|
408
|
+
output.print(`Failed Tests Tracker: Failed to save failed tests: ${error.message}`)
|
|
409
|
+
}
|
|
410
|
+
})
|
|
411
|
+
}
|
package/lib/workers.js
CHANGED
|
@@ -310,11 +310,24 @@ class Workers extends EventEmitter {
|
|
|
310
310
|
const groups = populateGroups(numberOfWorkers)
|
|
311
311
|
let groupCounter = 0
|
|
312
312
|
|
|
313
|
+
// If specific tests are provided (e.g., from run-failed-tests), only include those
|
|
314
|
+
const targetTests = this.options && this.options.tests
|
|
315
|
+
|
|
313
316
|
mocha.suite.eachTest(test => {
|
|
314
|
-
const i = groupCounter % groups.length
|
|
315
317
|
if (test) {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
+
// If we have specific target tests, only include matching UIDs
|
|
319
|
+
if (targetTests && targetTests.length > 0) {
|
|
320
|
+
if (targetTests.includes(test.uid)) {
|
|
321
|
+
const i = groupCounter % groups.length
|
|
322
|
+
groups[i].push(test.uid)
|
|
323
|
+
groupCounter++
|
|
324
|
+
}
|
|
325
|
+
} else {
|
|
326
|
+
// Default behavior: include all tests
|
|
327
|
+
const i = groupCounter % groups.length
|
|
328
|
+
groups[i].push(test.uid)
|
|
329
|
+
groupCounter++
|
|
330
|
+
}
|
|
318
331
|
}
|
|
319
332
|
})
|
|
320
333
|
return groups
|
package/package.json
CHANGED
|
@@ -2733,8 +2733,11 @@ declare namespace CodeceptJS {
|
|
|
2733
2733
|
* @property [highlightElement] - highlight the interacting elements. Default: false. Note: only activate under verbose mode (--verbose).
|
|
2734
2734
|
* @property [recordHar] - record HAR and will be saved to `output/har`. See more of [HAR options](https://playwright.dev/docs/api/class-browser#browser-new-context-option-record-har).
|
|
2735
2735
|
* @property [testIdAttribute = data-testid] - locate elements based on the testIdAttribute. See more of [locate by test id](https://playwright.dev/docs/locators#locate-by-test-id).
|
|
2736
|
+
* @property [customLocatorStrategies] - custom locator strategies. An object with keys as strategy names and values as JavaScript functions. Example: `{ byRole: (selector, root) => { return root.querySelector(\`[role="\${selector}\"]\`) } }`
|
|
2736
2737
|
*/
|
|
2737
2738
|
// @ts-ignore
|
|
2739
|
+
// @ts-ignore
|
|
2740
|
+
// @ts-ignore
|
|
2738
2741
|
type PlaywrightConfig = {
|
|
2739
2742
|
url?: string;
|
|
2740
2743
|
browser?: 'chromium' | 'firefox' | 'webkit' | 'electron';
|
|
@@ -2772,6 +2775,7 @@ declare namespace CodeceptJS {
|
|
|
2772
2775
|
highlightElement?: boolean;
|
|
2773
2776
|
recordHar?: any;
|
|
2774
2777
|
testIdAttribute?: string;
|
|
2778
|
+
customLocatorStrategies?: any;
|
|
2775
2779
|
};
|
|
2776
2780
|
/**
|
|
2777
2781
|
* Uses [Playwright](https://github.com/microsoft/playwright) library to run tests inside:
|
|
@@ -6112,6 +6116,8 @@ declare namespace CodeceptJS {
|
|
|
6112
6116
|
* @property [highlightElement] - highlight the interacting elements. Default: false. Note: only activate under verbose mode (--verbose).
|
|
6113
6117
|
*/
|
|
6114
6118
|
// @ts-ignore
|
|
6119
|
+
// @ts-ignore
|
|
6120
|
+
// @ts-ignore
|
|
6115
6121
|
type PuppeteerConfig = {
|
|
6116
6122
|
url: string;
|
|
6117
6123
|
basicAuth?: any;
|
|
@@ -6535,17 +6541,6 @@ declare namespace CodeceptJS {
|
|
|
6535
6541
|
* {{ react }}
|
|
6536
6542
|
*/
|
|
6537
6543
|
_locate(): Promise<any>;
|
|
6538
|
-
/**
|
|
6539
|
-
* Get single element by different locator types, including strict locator
|
|
6540
|
-
* Should be used in custom helpers:
|
|
6541
|
-
*
|
|
6542
|
-
* ```js
|
|
6543
|
-
* const element = await this.helpers['Puppeteer']._locateElement({name: 'password'});
|
|
6544
|
-
* ```
|
|
6545
|
-
*
|
|
6546
|
-
* {{ react }}
|
|
6547
|
-
*/
|
|
6548
|
-
_locateElement(): Promise<any>;
|
|
6549
6544
|
/**
|
|
6550
6545
|
* Find a checkbox by providing human-readable text:
|
|
6551
6546
|
* NOTE: Assumes the checkable element exists
|
|
@@ -6582,17 +6577,6 @@ declare namespace CodeceptJS {
|
|
|
6582
6577
|
* @returns WebElement of being used Web helper
|
|
6583
6578
|
*/
|
|
6584
6579
|
grabWebElements(locator: CodeceptJS.LocatorOrString): Promise<any>;
|
|
6585
|
-
/**
|
|
6586
|
-
* Grab WebElement for given locator
|
|
6587
|
-
* Resumes test execution, so **should be used inside an async function with `await`** operator.
|
|
6588
|
-
*
|
|
6589
|
-
* ```js
|
|
6590
|
-
* const webElement = await I.grabWebElement('#button');
|
|
6591
|
-
* ```
|
|
6592
|
-
* @param locator - element located by CSS|XPath|strict locator.
|
|
6593
|
-
* @returns WebElement of being used Web helper
|
|
6594
|
-
*/
|
|
6595
|
-
grabWebElement(locator: CodeceptJS.LocatorOrString): Promise<any>;
|
|
6596
6580
|
/**
|
|
6597
6581
|
* Switch focus to a particular tab by its number. It waits tabs loading and then switch tab
|
|
6598
6582
|
*
|
|
@@ -7928,22 +7912,6 @@ declare namespace CodeceptJS {
|
|
|
7928
7912
|
*/
|
|
7929
7913
|
flushWebSocketMessages(): Promise<any>;
|
|
7930
7914
|
}
|
|
7931
|
-
/**
|
|
7932
|
-
* Find elements using Puppeteer's native element discovery methods
|
|
7933
|
-
* Note: Unlike Playwright, Puppeteer's Locator API doesn't have .all() method for multiple elements
|
|
7934
|
-
* @param matcher - Puppeteer context to search within
|
|
7935
|
-
* @param locator - Locator specification
|
|
7936
|
-
* @returns Array of ElementHandle objects
|
|
7937
|
-
*/
|
|
7938
|
-
function findElements(matcher: any, locator: any | string): Promise<any[]>;
|
|
7939
|
-
/**
|
|
7940
|
-
* Find a single element using Puppeteer's native element discovery methods
|
|
7941
|
-
* Note: Puppeteer Locator API doesn't have .first() method like Playwright
|
|
7942
|
-
* @param matcher - Puppeteer context to search within
|
|
7943
|
-
* @param locator - Locator specification
|
|
7944
|
-
* @returns Single ElementHandle object
|
|
7945
|
-
*/
|
|
7946
|
-
function findElement(matcher: any, locator: any | string): Promise<object>;
|
|
7947
7915
|
/**
|
|
7948
7916
|
* ## Configuration
|
|
7949
7917
|
* @property [endpoint] - API base URL
|
|
@@ -7957,6 +7925,8 @@ declare namespace CodeceptJS {
|
|
|
7957
7925
|
* @property [maxUploadFileSize] - set the max content file size in MB when performing api calls.
|
|
7958
7926
|
*/
|
|
7959
7927
|
// @ts-ignore
|
|
7928
|
+
// @ts-ignore
|
|
7929
|
+
// @ts-ignore
|
|
7960
7930
|
type RESTConfig = {
|
|
7961
7931
|
endpoint?: string;
|
|
7962
7932
|
prettyPrintJson?: boolean;
|
|
@@ -9103,6 +9073,8 @@ declare namespace CodeceptJS {
|
|
|
9103
9073
|
* @property [logLevel = silent] - level of logging verbosity. Default: silent. Options: trace | debug | info | warn | error | silent. More info: https://webdriver.io/docs/configuration/#loglevel
|
|
9104
9074
|
*/
|
|
9105
9075
|
// @ts-ignore
|
|
9076
|
+
// @ts-ignore
|
|
9077
|
+
// @ts-ignore
|
|
9106
9078
|
type WebDriverConfig = {
|
|
9107
9079
|
url: string;
|
|
9108
9080
|
browser: string;
|
|
@@ -9573,17 +9545,6 @@ declare namespace CodeceptJS {
|
|
|
9573
9545
|
* @returns WebElement of being used Web helper
|
|
9574
9546
|
*/
|
|
9575
9547
|
grabWebElements(locator: CodeceptJS.LocatorOrString): Promise<any>;
|
|
9576
|
-
/**
|
|
9577
|
-
* Grab WebElement for given locator
|
|
9578
|
-
* Resumes test execution, so **should be used inside an async function with `await`** operator.
|
|
9579
|
-
*
|
|
9580
|
-
* ```js
|
|
9581
|
-
* const webElement = await I.grabWebElement('#button');
|
|
9582
|
-
* ```
|
|
9583
|
-
* @param locator - element located by CSS|XPath|strict locator.
|
|
9584
|
-
* @returns WebElement of being used Web helper
|
|
9585
|
-
*/
|
|
9586
|
-
grabWebElement(locator: CodeceptJS.LocatorOrString): Promise<any>;
|
|
9587
9548
|
/**
|
|
9588
9549
|
* Set [WebDriver timeouts](https://webdriver.io/docs/timeouts.html) in realtime.
|
|
9589
9550
|
*
|