@@ -74,15 +74,17 @@ const templates = {}
7474 *
7575 * #### Worker and Multiple Run Support
7676 *
77- * When using `run-workers`, `run-multiple`, or combinations thereof, screenshots from all workers and runs
78- * are automatically consolidated into a shared directory to ensure the final step-by-step report contains
79- * all screenshots from all processes.
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 .
8080 *
81- * The plugin automatically detects:
82- * - **run-workers**: Consolidates screenshots from multiple worker processes
83- * - **run-multiple**: Consolidates screenshots from multiple run configurations
84- * - **Mixed scenarios**: Handles combinations of both workers and multiple runs
85- * - **Custom output directories**: Works with both user-specified and default output directories
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.
8688 *
8789 * @param {* } config
8890 */
@@ -112,41 +114,7 @@ module.exports = function (config) {
112114 const recordedTests = { }
113115 const pad = '0000'
114116
115- // Determine if we're running in a multi-process scenario (workers or run-multiple)
116- // and need to consolidate screenshots from multiple directories
117- let reportDir
118-
119- const isRunningWithWorkers = process . env . RUNS_WITH_WORKERS === 'true'
120- const isRunMultipleChild = process . argv . some ( arg => arg === '--child' )
121- const needsConsolidation = isRunningWithWorkers || isRunMultipleChild
122-
123- if ( needsConsolidation && global . codecept_dir ) {
124- // Extract the base output directory and create a shared location for screenshots
125- const currentOutputDir = config . output ? path . resolve ( global . codecept_dir , config . output ) : defaultConfig . output
126-
127- let baseOutputDir = currentOutputDir
128-
129- // For mixed scenario (run-multiple + workers), we need to strip both worker and run directory segments
130- // For run-workers only, strip worker directory segment
131- // For run-multiple only, strip run directory segment
132- if ( isRunningWithWorkers ) {
133- // Strip worker directory: /output/smoke_chrome_hash_1/worker1 -> /output/smoke_chrome_hash_1 or /output/worker1 -> /output
134- const workerDirPattern = / [ / \\ ] [ ^ / \\ ] + $ / // Match the last directory segment (worker name)
135- baseOutputDir = baseOutputDir . replace ( workerDirPattern , '' )
136- }
137-
138- if ( isRunMultipleChild ) {
139- // Strip run directory: /output/smoke_chrome_hash_1 -> /output
140- const runDirPattern = / [ / \\ ] [ ^ / \\ ] + $ / // Match the last directory segment (run name)
141- baseOutputDir = baseOutputDir . replace ( runDirPattern , '' )
142- }
143-
144- // Create a shared directory for step-by-step screenshots
145- reportDir = path . join ( baseOutputDir , 'stepByStepReport' )
146- } else {
147- // Regular run command: use the output directory as-is
148- reportDir = config . output ? path . resolve ( global . codecept_dir , config . output ) : defaultConfig . output
149- }
117+ const reportDir = config . output ? path . resolve ( global . codecept_dir , config . output ) : defaultConfig . output
150118
151119 // Ensure the report directory exists
152120 mkdirp . sync ( reportDir )
@@ -199,11 +167,70 @@ module.exports = function (config) {
199167
200168 event . dispatcher . on ( event . workers . result , async ( ) => {
201169 await recorder . add ( ( ) => {
202- 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 ( )
203173 generateRecordsHtml ( recordedTests )
204174 } )
205175 } )
206176
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+
207234 function getRecordFoldersWithDetails ( dirPath ) {
208235 let results = { }
209236
@@ -248,9 +275,32 @@ module.exports = function (config) {
248275 records : links ,
249276 } )
250277
251- 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 )
252302
253- 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` ) } ` )
254304 }
255305
256306 async function persistStep ( step ) {
0 commit comments