Skip to content

Commit 52b8cd3

Browse files
author
William Gorge
committed
fix: step by step reporter for run-workers (taken from codeceptjs#5083)
1 parent a483106 commit 52b8cd3

File tree

1 file changed

+115
-3
lines changed

1 file changed

+115
-3
lines changed

lib/plugin/stepByStepReport.js

Lines changed: 115 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,18 @@ const templates = {}
4040
* npx codeceptjs run --plugins stepByStepReport
4141
* ```
4242
*
43+
* Run tests with workers:
44+
*
45+
* ```
46+
* npx codeceptjs run-workers 2 --plugins stepByStepReport
47+
* ```
48+
*
49+
* Run tests with multiple configurations:
50+
*
51+
* ```
52+
* npx codeceptjs run-multiple --all --plugins stepByStepReport
53+
* ```
54+
*
4355
* #### Configuration
4456
*
4557
* ```js
@@ -60,6 +72,20 @@ const templates = {}
6072
* * `screenshotsForAllureReport`: If Allure plugin is enabled this plugin attaches each saved screenshot to allure report. Default: false.
6173
* * `disableScreenshotOnFail : Disables the capturing of screeshots after the failed step. Default: true.
6274
*
75+
* #### Worker and Multiple Run Support
76+
*
77+
* When using `run-workers`, `run-multiple`, or combinations thereof, the plugin automatically
78+
* detects all worker and run processes and creates a consolidated step-by-step report that
79+
* includes screenshots from all processes while keeping them in their original directories.
80+
*
81+
* Screenshots remain in their respective process directories for traceability:
82+
* - **run-workers**: Screenshots saved in `/output/worker1/`, `/output/worker2/`, etc.
83+
* - **run-multiple**: Screenshots saved in `/output/config_name_hash/`, etc.
84+
* - **Mixed scenarios**: Screenshots saved in `/output/config_name_hash/worker1/`, etc.
85+
*
86+
* The final consolidated report links to all screenshots while preserving their original locations
87+
* and indicating which process or worker they came from.
88+
*
6389
* @param {*} config
6490
*/
6591

@@ -87,8 +113,12 @@ module.exports = function (config) {
87113

88114
const recordedTests = {}
89115
const pad = '0000'
116+
90117
const reportDir = config.output ? path.resolve(global.codecept_dir, config.output) : defaultConfig.output
91118

119+
// Ensure the report directory exists
120+
mkdirp.sync(reportDir)
121+
92122
event.dispatcher.on(event.suite.before, suite => {
93123
stepNum = -1
94124
})
@@ -137,11 +167,70 @@ module.exports = function (config) {
137167

138168
event.dispatcher.on(event.workers.result, async () => {
139169
await recorder.add(() => {
140-
const recordedTests = getRecordFoldersWithDetails(reportDir)
170+
// For workers and run-multiple scenarios, we need to search across multiple directories
171+
// to find all screenshot folders from different processes
172+
const recordedTests = getRecordFoldersFromAllDirectories()
141173
generateRecordsHtml(recordedTests)
142174
})
143175
})
144176

177+
function getRecordFoldersFromAllDirectories() {
178+
let results = {}
179+
180+
// Determine the base output directory to search from
181+
const baseOutputDir = config.output ? path.resolve(global.codecept_dir, config.output) : defaultConfig.output
182+
183+
// Function to recursively search for record folders in a directory
184+
function searchForRecordFolders(searchDir, basePath = '') {
185+
try {
186+
if (!fs.existsSync(searchDir)) return
187+
188+
const items = fs.readdirSync(searchDir, { withFileTypes: true })
189+
190+
items.forEach(item => {
191+
if (item.isDirectory()) {
192+
const itemPath = path.join(searchDir, item.name)
193+
const relativePath = basePath ? path.join(basePath, item.name) : item.name
194+
195+
// If this is a record folder, process it
196+
if (item.name.startsWith('record_')) {
197+
const indexPath = path.join(itemPath, 'index.html')
198+
199+
let name = ''
200+
if (fs.existsSync(indexPath)) {
201+
try {
202+
const htmlContent = fs.readFileSync(indexPath, 'utf-8')
203+
const $ = cheerio.load(htmlContent)
204+
name = $('.navbar-brand').text().trim()
205+
} catch (err) {
206+
console.error(`Error reading index.html in ${itemPath}:`, err.message)
207+
}
208+
}
209+
210+
// Include the relative path to show which process/worker this came from
211+
const displayName = basePath ? `${name} (${basePath})` : name
212+
results[displayName || 'Unknown'] = path.join(relativePath, 'index.html')
213+
} else {
214+
// Continue searching in subdirectories (worker folders, run-multiple folders)
215+
searchForRecordFolders(itemPath, relativePath)
216+
}
217+
}
218+
})
219+
} catch (err) {
220+
console.error(`Error searching directory ${searchDir}:`, err.message)
221+
}
222+
}
223+
224+
// Start the search from the base output directory
225+
searchForRecordFolders(baseOutputDir)
226+
227+
// Also check the current reportDir for backwards compatibility
228+
const currentDirResults = getRecordFoldersWithDetails(reportDir)
229+
Object.assign(results, currentDirResults)
230+
231+
return results
232+
}
233+
145234
function getRecordFoldersWithDetails(dirPath) {
146235
let results = {}
147236

@@ -186,9 +275,32 @@ module.exports = function (config) {
186275
records: links,
187276
})
188277

189-
fs.writeFileSync(path.join(reportDir, 'records.html'), indexHTML)
278+
// Determine where to write the main records.html file
279+
// For worker/run-multiple scenarios, we want to write to the base output directory
280+
let recordsHtmlDir = reportDir
281+
282+
if (global.codecept_dir && (process.env.RUNS_WITH_WORKERS === 'true' || process.argv.some(arg => arg === '--child'))) {
283+
// Extract base output directory by removing worker/run-specific segments
284+
const baseOutputDir = config.output ? path.resolve(global.codecept_dir, config.output) : defaultConfig.output
285+
let actualBaseDir = baseOutputDir
286+
287+
// For workers: strip worker directory segment
288+
if (process.env.RUNS_WITH_WORKERS === 'true') {
289+
actualBaseDir = actualBaseDir.replace(/[/\\][^/\\]+$/, '')
290+
}
291+
292+
// For run-multiple: strip run directory segment
293+
if (process.argv.some(arg => arg === '--child')) {
294+
actualBaseDir = actualBaseDir.replace(/[/\\][^/\\]+$/, '')
295+
}
296+
297+
recordsHtmlDir = actualBaseDir
298+
mkdirp.sync(recordsHtmlDir)
299+
}
300+
301+
fs.writeFileSync(path.join(recordsHtmlDir, 'records.html'), indexHTML)
190302

191-
output.print(`${figures.circleFilled} Step-by-step preview: ${colors.white.bold(`file://${reportDir}/records.html`)}`)
303+
output.print(`${figures.circleFilled} Step-by-step preview: ${colors.white.bold(`file://${recordsHtmlDir}/records.html`)}`)
192304
}
193305

194306
async function persistStep(step) {

0 commit comments

Comments
 (0)