codeceptjs 3.7.5-beta.15 → 3.7.5-beta.17
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 +0 -23
- package/lib/helper/Playwright.js +3 -0
- package/lib/helper/Puppeteer.js +5 -1
- package/lib/helper/network/actions.js +4 -2
- package/lib/listener/enhancedGlobalRetry.js +110 -0
- package/lib/mocha/test.js +0 -1
- package/lib/plugin/enhancedRetryFailedStep.js +99 -0
- package/lib/recorder.js +19 -3
- package/lib/retryCoordinator.js +207 -0
- package/lib/utils.js +48 -2
- package/lib/workers.js +3 -20
- package/package.json +23 -20
- package/typings/promiseBasedTypes.d.ts +8 -0
- package/typings/types.d.ts +8 -0
- package/lib/command/run-failed-tests.js +0 -277
- package/lib/plugin/failedTestsTracker.js +0 -413
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codeceptjs",
|
|
3
|
-
"version": "3.7.5-beta.
|
|
3
|
+
"version": "3.7.5-beta.17",
|
|
4
4
|
"description": "Supercharged End 2 End Testing Framework for NodeJS",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"acceptance",
|
|
@@ -78,12 +78,12 @@
|
|
|
78
78
|
"@codeceptjs/configure": "1.0.6",
|
|
79
79
|
"@codeceptjs/helper": "2.0.4",
|
|
80
80
|
"@cucumber/cucumber-expressions": "18",
|
|
81
|
-
"@cucumber/gherkin": "
|
|
82
|
-
"@cucumber/messages": "
|
|
81
|
+
"@cucumber/gherkin": "35.0.0",
|
|
82
|
+
"@cucumber/messages": "29.0.1",
|
|
83
83
|
"@xmldom/xmldom": "0.9.8",
|
|
84
|
-
"acorn": "8.
|
|
84
|
+
"acorn": "8.15.0",
|
|
85
85
|
"arrify": "3.0.0",
|
|
86
|
-
"axios": "1.
|
|
86
|
+
"axios": "1.12.2",
|
|
87
87
|
"chalk": "4.1.2",
|
|
88
88
|
"cheerio": "^1.0.0",
|
|
89
89
|
"chokidar": "^4.0.3",
|
|
@@ -95,20 +95,20 @@
|
|
|
95
95
|
"escape-string-regexp": "4.0.0",
|
|
96
96
|
"figures": "3.2.0",
|
|
97
97
|
"fn-args": "4.0.0",
|
|
98
|
-
"fs-extra": "11.3.
|
|
98
|
+
"fs-extra": "11.3.2",
|
|
99
99
|
"fuse.js": "^7.0.0",
|
|
100
100
|
"glob": ">=9.0.0 <12",
|
|
101
101
|
"html-minifier-terser": "7.2.0",
|
|
102
102
|
"inquirer": "^8.2.7",
|
|
103
103
|
"invisi-data": "^1.0.0",
|
|
104
|
-
"joi": "
|
|
104
|
+
"joi": "18.0.1",
|
|
105
105
|
"js-beautify": "1.15.4",
|
|
106
106
|
"lodash.clonedeep": "4.5.0",
|
|
107
107
|
"lodash.merge": "4.6.2",
|
|
108
108
|
"lodash.shuffle": "4.2.0",
|
|
109
109
|
"mkdirp": "3.0.1",
|
|
110
|
-
"mocha": "11.
|
|
111
|
-
"monocart-coverage-reports": "2.12.
|
|
110
|
+
"mocha": "11.7.2",
|
|
111
|
+
"monocart-coverage-reports": "2.12.9",
|
|
112
112
|
"ms": "2.1.3",
|
|
113
113
|
"multer": "^2.0.2",
|
|
114
114
|
"ora-classic": "5.4.2",
|
|
@@ -120,20 +120,20 @@
|
|
|
120
120
|
"uuid": "11.1.0"
|
|
121
121
|
},
|
|
122
122
|
"optionalDependencies": {
|
|
123
|
-
"@codeceptjs/detox-helper": "1.1.
|
|
123
|
+
"@codeceptjs/detox-helper": "1.1.12"
|
|
124
124
|
},
|
|
125
125
|
"devDependencies": {
|
|
126
|
-
"@apollo/server": "^
|
|
126
|
+
"@apollo/server": "^5",
|
|
127
127
|
"@codeceptjs/expect-helper": "^1.0.2",
|
|
128
128
|
"@codeceptjs/mock-request": "0.3.1",
|
|
129
129
|
"@eslint/eslintrc": "3.3.1",
|
|
130
|
-
"@eslint/js": "9.
|
|
130
|
+
"@eslint/js": "9.35.0",
|
|
131
131
|
"@faker-js/faker": "9.8.0",
|
|
132
132
|
"@pollyjs/adapter-puppeteer": "6.0.6",
|
|
133
133
|
"@pollyjs/core": "6.0.6",
|
|
134
134
|
"@types/chai": "5.2.2",
|
|
135
|
-
"@types/inquirer": "9.0.
|
|
136
|
-
"@types/node": "24.0
|
|
135
|
+
"@types/inquirer": "9.0.9",
|
|
136
|
+
"@types/node": "24.5.0",
|
|
137
137
|
"@wdio/sauce-service": "9.12.5",
|
|
138
138
|
"@wdio/selenium-standalone-service": "8.15.0",
|
|
139
139
|
"@wdio/utils": "9.15.0",
|
|
@@ -142,13 +142,13 @@
|
|
|
142
142
|
"chai-as-promised": "7.1.2",
|
|
143
143
|
"chai-subset": "1.6.0",
|
|
144
144
|
"documentation": "14.0.3",
|
|
145
|
-
"electron": "
|
|
146
|
-
"eslint": "^9.
|
|
145
|
+
"electron": "38.1.0",
|
|
146
|
+
"eslint": "^9.35.0",
|
|
147
147
|
"eslint-plugin-import": "2.32.0",
|
|
148
148
|
"eslint-plugin-mocha": "11.1.0",
|
|
149
|
-
"expect": "30.
|
|
149
|
+
"expect": "30.1.2",
|
|
150
150
|
"express": "^5.1.0",
|
|
151
|
-
"globals": "16.
|
|
151
|
+
"globals": "16.4.0",
|
|
152
152
|
"graphql": "16.11.0",
|
|
153
153
|
"graphql-tag": "^2.12.6",
|
|
154
154
|
"husky": "9.1.7",
|
|
@@ -157,7 +157,7 @@
|
|
|
157
157
|
"jsdoc-typeof-plugin": "1.0.0",
|
|
158
158
|
"json-server": "0.17.4",
|
|
159
159
|
"mochawesome": "^7.1.3",
|
|
160
|
-
"playwright": "1.
|
|
160
|
+
"playwright": "1.55.0",
|
|
161
161
|
"prettier": "^3.3.2",
|
|
162
162
|
"puppeteer": "24.15.0",
|
|
163
163
|
"qrcode-terminal": "0.12.0",
|
|
@@ -171,7 +171,7 @@
|
|
|
171
171
|
"ts-node": "10.9.2",
|
|
172
172
|
"tsd": "^0.33.0",
|
|
173
173
|
"tsd-jsdoc": "2.5.0",
|
|
174
|
-
"typedoc": "0.28.
|
|
174
|
+
"typedoc": "0.28.13",
|
|
175
175
|
"typedoc-plugin-markdown": "4.8.1",
|
|
176
176
|
"typescript": "5.8.3",
|
|
177
177
|
"wdio-docker-service": "3.2.1",
|
|
@@ -189,5 +189,8 @@
|
|
|
189
189
|
"compilerOptions": {
|
|
190
190
|
"strict": false
|
|
191
191
|
}
|
|
192
|
+
},
|
|
193
|
+
"overrides": {
|
|
194
|
+
"tmp": "0.2.5"
|
|
192
195
|
}
|
|
193
196
|
}
|
|
@@ -2738,6 +2738,8 @@ declare namespace CodeceptJS {
|
|
|
2738
2738
|
// @ts-ignore
|
|
2739
2739
|
// @ts-ignore
|
|
2740
2740
|
// @ts-ignore
|
|
2741
|
+
// @ts-ignore
|
|
2742
|
+
// @ts-ignore
|
|
2741
2743
|
type PlaywrightConfig = {
|
|
2742
2744
|
url?: string;
|
|
2743
2745
|
browser?: 'chromium' | 'firefox' | 'webkit' | 'electron';
|
|
@@ -6118,6 +6120,8 @@ declare namespace CodeceptJS {
|
|
|
6118
6120
|
// @ts-ignore
|
|
6119
6121
|
// @ts-ignore
|
|
6120
6122
|
// @ts-ignore
|
|
6123
|
+
// @ts-ignore
|
|
6124
|
+
// @ts-ignore
|
|
6121
6125
|
type PuppeteerConfig = {
|
|
6122
6126
|
url: string;
|
|
6123
6127
|
basicAuth?: any;
|
|
@@ -7927,6 +7931,8 @@ declare namespace CodeceptJS {
|
|
|
7927
7931
|
// @ts-ignore
|
|
7928
7932
|
// @ts-ignore
|
|
7929
7933
|
// @ts-ignore
|
|
7934
|
+
// @ts-ignore
|
|
7935
|
+
// @ts-ignore
|
|
7930
7936
|
type RESTConfig = {
|
|
7931
7937
|
endpoint?: string;
|
|
7932
7938
|
prettyPrintJson?: boolean;
|
|
@@ -9075,6 +9081,8 @@ declare namespace CodeceptJS {
|
|
|
9075
9081
|
// @ts-ignore
|
|
9076
9082
|
// @ts-ignore
|
|
9077
9083
|
// @ts-ignore
|
|
9084
|
+
// @ts-ignore
|
|
9085
|
+
// @ts-ignore
|
|
9078
9086
|
type WebDriverConfig = {
|
|
9079
9087
|
url: string;
|
|
9080
9088
|
browser: string;
|
package/typings/types.d.ts
CHANGED
|
@@ -2828,6 +2828,8 @@ declare namespace CodeceptJS {
|
|
|
2828
2828
|
// @ts-ignore
|
|
2829
2829
|
// @ts-ignore
|
|
2830
2830
|
// @ts-ignore
|
|
2831
|
+
// @ts-ignore
|
|
2832
|
+
// @ts-ignore
|
|
2831
2833
|
type PlaywrightConfig = {
|
|
2832
2834
|
url?: string;
|
|
2833
2835
|
browser?: 'chromium' | 'firefox' | 'webkit' | 'electron';
|
|
@@ -6359,6 +6361,8 @@ declare namespace CodeceptJS {
|
|
|
6359
6361
|
// @ts-ignore
|
|
6360
6362
|
// @ts-ignore
|
|
6361
6363
|
// @ts-ignore
|
|
6364
|
+
// @ts-ignore
|
|
6365
|
+
// @ts-ignore
|
|
6362
6366
|
type PuppeteerConfig = {
|
|
6363
6367
|
url: string;
|
|
6364
6368
|
basicAuth?: any;
|
|
@@ -8304,6 +8308,8 @@ declare namespace CodeceptJS {
|
|
|
8304
8308
|
// @ts-ignore
|
|
8305
8309
|
// @ts-ignore
|
|
8306
8310
|
// @ts-ignore
|
|
8311
|
+
// @ts-ignore
|
|
8312
|
+
// @ts-ignore
|
|
8307
8313
|
type RESTConfig = {
|
|
8308
8314
|
endpoint?: string;
|
|
8309
8315
|
prettyPrintJson?: boolean;
|
|
@@ -9512,6 +9518,8 @@ declare namespace CodeceptJS {
|
|
|
9512
9518
|
// @ts-ignore
|
|
9513
9519
|
// @ts-ignore
|
|
9514
9520
|
// @ts-ignore
|
|
9521
|
+
// @ts-ignore
|
|
9522
|
+
// @ts-ignore
|
|
9515
9523
|
type WebDriverConfig = {
|
|
9516
9524
|
url: string;
|
|
9517
9525
|
browser: string;
|
|
@@ -1,277 +0,0 @@
|
|
|
1
|
-
const fs = require('fs')
|
|
2
|
-
const path = require('path')
|
|
3
|
-
const { getConfig, printError, getTestRoot, createOutputDir } = require('./utils')
|
|
4
|
-
const Config = require('../config')
|
|
5
|
-
const store = require('../store')
|
|
6
|
-
const Codecept = require('../codecept')
|
|
7
|
-
const output = require('../output')
|
|
8
|
-
const Workers = require('../workers')
|
|
9
|
-
const { tryOrDefault } = require('../utils')
|
|
10
|
-
|
|
11
|
-
module.exports = async function (options) {
|
|
12
|
-
// registering options globally to use in config
|
|
13
|
-
if (options.profile) {
|
|
14
|
-
process.env.profile = options.profile
|
|
15
|
-
}
|
|
16
|
-
if (options.verbose || options.debug) store.debugMode = true
|
|
17
|
-
|
|
18
|
-
const configFile = options.config
|
|
19
|
-
let config = getConfig(configFile)
|
|
20
|
-
|
|
21
|
-
if (options.override) {
|
|
22
|
-
config = Config.append(JSON.parse(options.override))
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const testRoot = getTestRoot(configFile)
|
|
26
|
-
createOutputDir(config, testRoot)
|
|
27
|
-
|
|
28
|
-
// Determine failed tests file path - respect CodeceptJS output directory
|
|
29
|
-
const failedTestsFile = options.file || 'failed-tests.json'
|
|
30
|
-
const failedTestsPath = path.isAbsolute(failedTestsFile)
|
|
31
|
-
? failedTestsFile
|
|
32
|
-
: path.resolve(global.output_dir || './output', failedTestsFile)
|
|
33
|
-
|
|
34
|
-
// Check if failed tests file exists
|
|
35
|
-
if (!fs.existsSync(failedTestsPath)) {
|
|
36
|
-
output.error(`Failed tests file not found: ${failedTestsPath}`)
|
|
37
|
-
output.print('Run tests first to generate a failed tests file, or specify a different file with --file option')
|
|
38
|
-
process.exitCode = 1
|
|
39
|
-
return
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
let failedTestsData
|
|
43
|
-
try {
|
|
44
|
-
const fileContent = fs.readFileSync(failedTestsPath, 'utf8')
|
|
45
|
-
failedTestsData = JSON.parse(fileContent)
|
|
46
|
-
} catch (error) {
|
|
47
|
-
output.error(`Failed to read or parse failed tests file: ${error.message}`)
|
|
48
|
-
process.exitCode = 1
|
|
49
|
-
return
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (!failedTestsData.tests || failedTestsData.tests.length === 0) {
|
|
53
|
-
output.print('No failed tests found in the file')
|
|
54
|
-
return
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
output.print(`Found ${failedTestsData.tests.length} failed tests from ${failedTestsData.timestamp}`)
|
|
58
|
-
|
|
59
|
-
// Debug: Show what's in the failed tests data
|
|
60
|
-
if (options.verbose) {
|
|
61
|
-
output.print('\nFailed tests data structure:')
|
|
62
|
-
failedTestsData.tests.forEach((test, index) => {
|
|
63
|
-
output.print(` ${index + 1}. Title: "${test.title}", UID: "${test.uid}", File: "${test.file}"`)
|
|
64
|
-
})
|
|
65
|
-
output.print('')
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
// Build test patterns from failed tests
|
|
69
|
-
const testPatterns = []
|
|
70
|
-
const testsByFile = new Map()
|
|
71
|
-
|
|
72
|
-
// Group tests by file for more efficient execution
|
|
73
|
-
failedTestsData.tests.forEach(test => {
|
|
74
|
-
if (test.file) {
|
|
75
|
-
if (!testsByFile.has(test.file)) {
|
|
76
|
-
testsByFile.set(test.file, [])
|
|
77
|
-
}
|
|
78
|
-
testsByFile.get(test.file).push(test)
|
|
79
|
-
}
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
// Build precise test selection from failed tests
|
|
83
|
-
if (testsByFile.size > 0) {
|
|
84
|
-
// Use file paths for loading tests
|
|
85
|
-
for (const [file, tests] of testsByFile) {
|
|
86
|
-
testPatterns.push(file)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Create a map of exact test titles to target for filtering
|
|
90
|
-
const failedTestTitles = new Set(failedTestsData.tests.map(test => test.title).filter(Boolean))
|
|
91
|
-
|
|
92
|
-
if (failedTestTitles.size > 0) {
|
|
93
|
-
output.print(`Targeting ${failedTestTitles.size} specific failed tests by exact title matching`)
|
|
94
|
-
options.exactTestTitles = Array.from(failedTestTitles)
|
|
95
|
-
} else {
|
|
96
|
-
output.print('No specific test titles found, running all tests in files')
|
|
97
|
-
}
|
|
98
|
-
} else {
|
|
99
|
-
// Fallback: use test titles with exact match grep
|
|
100
|
-
const testTitles = failedTestsData.tests.map(test => test.title).filter(Boolean)
|
|
101
|
-
if (testTitles.length > 0) {
|
|
102
|
-
const grepPattern = testTitles.map(title => `^${title.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`).join('|')
|
|
103
|
-
options.grep = grepPattern
|
|
104
|
-
output.print(`Targeting failed tests by title pattern (no file info available)`)
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Check if user wants to run with workers
|
|
109
|
-
if (options.workers) {
|
|
110
|
-
await runWithWorkers(config, options, testPatterns, failedTestsData)
|
|
111
|
-
} else {
|
|
112
|
-
await runWithoutWorkers(config, options, testPatterns, failedTestsData, testRoot)
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
async function runWithWorkers(config, options, testPatterns, failedTestsData) {
|
|
117
|
-
const numberOfWorkers = parseInt(options.workers, 10)
|
|
118
|
-
const overrideConfigs = tryOrDefault(() => JSON.parse(options.override || '{}'), {})
|
|
119
|
-
|
|
120
|
-
// Determine test split strategy
|
|
121
|
-
let by = 'test' // default for failed tests
|
|
122
|
-
if (options.by) {
|
|
123
|
-
by = options.by
|
|
124
|
-
} else if (options.suites) {
|
|
125
|
-
by = 'suite'
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Validate the by option
|
|
129
|
-
const validStrategies = ['test', 'suite', 'pool']
|
|
130
|
-
if (!validStrategies.includes(by)) {
|
|
131
|
-
throw new Error(`Invalid --by strategy: ${by}. Valid options are: ${validStrategies.join(', ')}`)
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
const workerConfig = {
|
|
135
|
-
by,
|
|
136
|
-
testConfig: options.config,
|
|
137
|
-
options,
|
|
138
|
-
selectedRuns: undefined,
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// If we have specific test UIDs, override the worker test selection
|
|
142
|
-
if (options.tests && options.tests.length > 0) {
|
|
143
|
-
workerConfig.by = 'test' // Force test-level distribution for precise targeting
|
|
144
|
-
output.print(`Using precise test UID targeting for ${options.tests.length} failed tests`)
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
output.print(`CodeceptJS v${require('../codecept').version()}`)
|
|
148
|
-
output.print(`Re-running ${failedTestsData.tests.length} failed tests in ${output.styles.bold(numberOfWorkers)} workers...`)
|
|
149
|
-
output.print()
|
|
150
|
-
store.hasWorkers = true
|
|
151
|
-
|
|
152
|
-
const workers = new Workers(numberOfWorkers, workerConfig)
|
|
153
|
-
workers.overrideConfig(overrideConfigs)
|
|
154
|
-
|
|
155
|
-
// Set up event listeners for worker output
|
|
156
|
-
workers.on('test.failed', test => {
|
|
157
|
-
output.test.failed(test)
|
|
158
|
-
})
|
|
159
|
-
|
|
160
|
-
workers.on('test.passed', test => {
|
|
161
|
-
output.test.passed(test)
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
workers.on('test.skipped', test => {
|
|
165
|
-
output.test.skipped(test)
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
workers.on('all.result', result => {
|
|
169
|
-
workers.printResults()
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
try {
|
|
173
|
-
if (options.verbose || options.debug) store.debugMode = true
|
|
174
|
-
|
|
175
|
-
if (options.verbose) {
|
|
176
|
-
output.print('\nFailed tests to re-run with workers:')
|
|
177
|
-
failedTestsData.tests.forEach((test, index) => {
|
|
178
|
-
output.print(` ${index + 1}. ${test.fullTitle || test.title} (${test.file || 'unknown file'})`)
|
|
179
|
-
if (test.error && test.error.message) {
|
|
180
|
-
output.print(` Error: ${test.error.message}`)
|
|
181
|
-
}
|
|
182
|
-
})
|
|
183
|
-
output.print('')
|
|
184
|
-
|
|
185
|
-
const { getMachineInfo } = require('./info')
|
|
186
|
-
await getMachineInfo()
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
await workers.bootstrapAll()
|
|
190
|
-
await workers.run()
|
|
191
|
-
} catch (err) {
|
|
192
|
-
printError(err)
|
|
193
|
-
process.exitCode = 1
|
|
194
|
-
} finally {
|
|
195
|
-
await workers.teardownAll()
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
async function runWithoutWorkers(config, options, testPatterns, failedTestsData, testRoot) {
|
|
200
|
-
const codecept = new Codecept(config, options)
|
|
201
|
-
|
|
202
|
-
try {
|
|
203
|
-
codecept.init(testRoot)
|
|
204
|
-
await codecept.bootstrap()
|
|
205
|
-
|
|
206
|
-
// Load tests - if we have specific patterns, use them, otherwise load all and filter with grep
|
|
207
|
-
if (testPatterns.length > 0) {
|
|
208
|
-
codecept.loadTests(testPatterns.join(' '))
|
|
209
|
-
} else {
|
|
210
|
-
codecept.loadTests()
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// If we have specific test titles, use exact matching
|
|
214
|
-
if (options.exactTestTitles && options.exactTestTitles.length > 0) {
|
|
215
|
-
// Store the exact failed tests globally for runtime verification
|
|
216
|
-
global.__EXACT_FAILED_TESTS__ = failedTestsData.tests.map(test => ({
|
|
217
|
-
fullTitle: test.fullTitle,
|
|
218
|
-
stableId: test.stableId,
|
|
219
|
-
title: test.title,
|
|
220
|
-
file: test.file
|
|
221
|
-
}))
|
|
222
|
-
|
|
223
|
-
// Override the test loading mechanism to filter tests after loading
|
|
224
|
-
const Container = require('../container')
|
|
225
|
-
const mocha = Container.mocha()
|
|
226
|
-
|
|
227
|
-
// Override Mocha's loadFiles to filter tests after loading
|
|
228
|
-
const originalLoadFiles = mocha.loadFiles.bind(mocha)
|
|
229
|
-
mocha.loadFiles = function() {
|
|
230
|
-
const result = originalLoadFiles()
|
|
231
|
-
|
|
232
|
-
// Filter the suite after files are loaded to ensure only exact matches run
|
|
233
|
-
const filterSuite = (suite) => {
|
|
234
|
-
// Filter direct tests in this suite
|
|
235
|
-
suite.tests = suite.tests.filter(test => {
|
|
236
|
-
const testFullTitle = test.fullTitle()
|
|
237
|
-
return global.__EXACT_FAILED_TESTS__.some(failedTest =>
|
|
238
|
-
failedTest.fullTitle === testFullTitle
|
|
239
|
-
)
|
|
240
|
-
})
|
|
241
|
-
|
|
242
|
-
// Recursively filter child suites
|
|
243
|
-
suite.suites.forEach(childSuite => filterSuite(childSuite))
|
|
244
|
-
|
|
245
|
-
// Remove empty child suites
|
|
246
|
-
suite.suites = suite.suites.filter(childSuite =>
|
|
247
|
-
childSuite.tests.length > 0 || childSuite.suites.length > 0
|
|
248
|
-
)
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
filterSuite(this.suite)
|
|
252
|
-
return result
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
output.print(`Filtered to ${failedTestsData.tests.length} specific failed tests using exact matching`)
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// Display information about what we're running
|
|
259
|
-
if (options.verbose) {
|
|
260
|
-
output.print('\nFailed tests to re-run:')
|
|
261
|
-
failedTestsData.tests.forEach((test, index) => {
|
|
262
|
-
output.print(` ${index + 1}. ${test.fullTitle || test.title} (${test.file || 'unknown file'})`)
|
|
263
|
-
if (test.error && test.error.message) {
|
|
264
|
-
output.print(` Error: ${test.error.message}`)
|
|
265
|
-
}
|
|
266
|
-
})
|
|
267
|
-
output.print('')
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
await codecept.run()
|
|
271
|
-
} catch (err) {
|
|
272
|
-
printError(err)
|
|
273
|
-
process.exitCode = 1
|
|
274
|
-
} finally {
|
|
275
|
-
await codecept.teardown()
|
|
276
|
-
}
|
|
277
|
-
}
|