Skip to content

Commit 65ae60f

Browse files
Copilotkobenguyent
andcommitted
Implement comprehensive stepByStep reporter fix for all command scenarios
Co-authored-by: kobenguyent <7845001+kobenguyent@users.noreply.github.com>
1 parent ffd4eb9 commit 65ae60f

File tree

3 files changed

+216
-20
lines changed

3 files changed

+216
-20
lines changed

lib/plugin/stepByStepReport.js

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@ const templates = {}
4646
* npx codeceptjs run-workers 2 --plugins stepByStepReport
4747
* ```
4848
*
49+
* Run tests with multiple configurations:
50+
*
51+
* ```
52+
* npx codeceptjs run-multiple --all --plugins stepByStepReport
53+
* ```
54+
*
4955
* #### Configuration
5056
*
5157
* ```js
@@ -66,10 +72,17 @@ const templates = {}
6672
* * `screenshotsForAllureReport`: If Allure plugin is enabled this plugin attaches each saved screenshot to allure report. Default: false.
6773
* * `disableScreenshotOnFail : Disables the capturing of screeshots after the failed step. Default: true.
6874
*
69-
* #### Worker Support
75+
* #### Worker and Multiple Run Support
7076
*
71-
* When using `run-workers`, screenshots from all workers are automatically consolidated into a shared directory
72-
* to ensure the final step-by-step report contains all screenshots from all workers.
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.
80+
*
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
7386
*
7487
* @param {*} config
7588
*/
@@ -99,19 +112,39 @@ module.exports = function (config) {
99112
const recordedTests = {}
100113
const pad = '0000'
101114

102-
// When running with workers, use the shared output directory instead of worker-specific directories
103-
// This ensures that all workers save their screenshots to the same location so the final consolidated
104-
// report can find all screenshots in one place
115+
// Determine if we're running in a multi-process scenario (workers or run-multiple)
116+
// and need to consolidate screenshots from multiple directories
105117
let reportDir
106-
if (process.env.RUNS_WITH_WORKERS === 'true' && global.codecept_dir) {
107-
// Extract the base output directory from the worker-specific path
108-
// Worker paths are typically like: /project/output/worker_name
109-
// We want to extract: /project/output and create: /project/output/stepByStepReport
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
110125
const currentOutputDir = config.output ? path.resolve(global.codecept_dir, config.output) : defaultConfig.output
111-
const workerDirPattern = /[/\\][^/\\]+$/ // Match the last directory segment (worker name)
112-
const baseOutputDir = currentOutputDir.replace(workerDirPattern, '')
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
113145
reportDir = path.join(baseOutputDir, 'stepByStepReport')
114146
} else {
147+
// Regular run command: use the output directory as-is
115148
reportDir = config.output ? path.resolve(global.codecept_dir, config.output) : defaultConfig.output
116149
}
117150

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
const path = require('path')
2+
const fs = require('fs')
3+
const { exec } = require('child_process')
4+
const { expect } = require('expect')
5+
6+
const runner = path.join(__dirname, '../../bin/codecept.js')
7+
const codecept_dir = path.join(__dirname, '../data/sandbox')
8+
9+
describe('stepByStepReport plugin with different run commands', function () {
10+
this.timeout(60000)
11+
12+
const outputDir = path.join(codecept_dir, 'output')
13+
const stepByStepDir = path.join(outputDir, 'stepByStepReport')
14+
15+
beforeEach(() => {
16+
// Clean up output directory before each test
17+
if (fs.existsSync(outputDir)) {
18+
fs.rmSync(outputDir, { recursive: true, force: true })
19+
}
20+
})
21+
22+
it('should create stepByStepReport directory when using run-workers', function (done) {
23+
const config = `
24+
exports.config = {
25+
tests: './*_test.js',
26+
timeout: 10000,
27+
output: './output',
28+
helpers: {
29+
FakeDriver: {
30+
require: '../fake_driver',
31+
browser: 'dummy',
32+
},
33+
},
34+
plugins: {
35+
stepByStepReport: {
36+
enabled: true,
37+
deleteSuccessful: false,
38+
},
39+
},
40+
include: {},
41+
bootstrap: false,
42+
mocha: {},
43+
name: 'stepByStepTest',
44+
}
45+
`
46+
47+
const configPath = path.join(codecept_dir, 'codecept.stepbystep.js')
48+
fs.writeFileSync(configPath, config)
49+
50+
const command = `${runner} run-workers 2 --config ${configPath} --grep "@stepbystep"`
51+
52+
exec(command, (err, stdout, stderr) => {
53+
console.log('STDOUT:', stdout)
54+
console.log('STDERR:', stderr)
55+
56+
// The step by step directory should exist
57+
const exists = fs.existsSync(stepByStepDir)
58+
expect(exists).toBe(true)
59+
60+
// Clean up
61+
fs.unlinkSync(configPath)
62+
63+
done()
64+
})
65+
})
66+
67+
it('should consolidate screenshots from run-multiple', function (done) {
68+
const config = `
69+
exports.config = {
70+
tests: './*_test.multiple.js',
71+
timeout: 10000,
72+
output: './output',
73+
helpers: {
74+
FakeDriver: {
75+
require: '../fake_driver',
76+
browser: 'dummy',
77+
},
78+
},
79+
plugins: {
80+
stepByStepReport: {
81+
enabled: true,
82+
deleteSuccessful: false,
83+
},
84+
},
85+
multiple: {
86+
test1: {
87+
browsers: ['chrome'],
88+
},
89+
test2: {
90+
browsers: ['firefox'],
91+
},
92+
},
93+
include: {},
94+
bootstrap: false,
95+
mocha: {},
96+
name: 'stepByStepMultipleTest',
97+
}
98+
`
99+
100+
const configPath = path.join(codecept_dir, 'codecept.stepbystep.multiple.js')
101+
fs.writeFileSync(configPath, config)
102+
103+
const command = `${runner} run-multiple --config ${configPath} test1`
104+
105+
exec(command, (err, stdout, stderr) => {
106+
console.log('STDOUT:', stdout)
107+
console.log('STDERR:', stderr)
108+
109+
// The step by step directory should exist
110+
const exists = fs.existsSync(stepByStepDir)
111+
expect(exists).toBe(true)
112+
113+
// Clean up
114+
fs.unlinkSync(configPath)
115+
116+
done()
117+
})
118+
})
119+
})

test/unit/worker_test.js

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -269,29 +269,73 @@ describe('Workers', function () {
269269
const path = require('path')
270270

271271
// Mock the stepByStep directory resolution logic
272-
function getStepByStepReportDir(config, isWorker, globalCodeceptDir) {
272+
function getStepByStepReportDir(config, isRunningWithWorkers, isRunMultipleChild, globalCodeceptDir) {
273+
const needsConsolidation = isRunningWithWorkers || isRunMultipleChild
273274
let reportDir
274-
if (isWorker && globalCodeceptDir) {
275+
276+
if (needsConsolidation && globalCodeceptDir) {
275277
const currentOutputDir = config.output ? path.resolve(globalCodeceptDir, config.output) : '/default-output'
276-
const workerDirPattern = /[/\\][^/\\]+$/
277-
const baseOutputDir = currentOutputDir.replace(workerDirPattern, '')
278+
279+
let baseOutputDir = currentOutputDir
280+
281+
// For mixed scenario (run-multiple + workers), we need to strip both worker and run directory segments
282+
// For run-workers only, strip worker directory segment
283+
// For run-multiple only, strip run directory segment
284+
if (isRunningWithWorkers) {
285+
// Strip worker directory: /output/smoke_chrome_hash_1/worker1 -> /output/smoke_chrome_hash_1 or /output/worker1 -> /output
286+
const workerDirPattern = /[/\\][^/\\]+$/ // Match the last directory segment (worker name)
287+
baseOutputDir = baseOutputDir.replace(workerDirPattern, '')
288+
}
289+
290+
if (isRunMultipleChild) {
291+
// Strip run directory: /output/smoke_chrome_hash_1 -> /output
292+
const runDirPattern = /[/\\][^/\\]+$/ // Match the last directory segment (run name)
293+
baseOutputDir = baseOutputDir.replace(runDirPattern, '')
294+
}
295+
278296
reportDir = path.join(baseOutputDir, 'stepByStepReport')
279297
} else {
280298
reportDir = config.output ? path.resolve(globalCodeceptDir, config.output) : '/default-output'
281299
}
300+
282301
return reportDir
283302
}
284303

285304
const globalCodeceptDir = '/tmp/test'
286305

287-
// Test regular (non-worker) mode
306+
// Test regular (non-worker) mode with default directory
288307
const regularConfig = { output: './output' }
289-
const regularDir = getStepByStepReportDir(regularConfig, false, globalCodeceptDir)
308+
const regularDir = getStepByStepReportDir(regularConfig, false, false, globalCodeceptDir)
290309
expect(regularDir).equal('/tmp/test/output')
291310

292-
// Test worker mode
311+
// Test regular (non-worker) mode with custom directory
312+
const customConfig = { output: './custom-output' }
313+
const customDir = getStepByStepReportDir(customConfig, false, false, globalCodeceptDir)
314+
expect(customDir).equal('/tmp/test/custom-output')
315+
316+
// Test run-workers mode with default directory
293317
const workerConfig = { output: './output/worker1' }
294-
const workerDir = getStepByStepReportDir(workerConfig, true, globalCodeceptDir)
318+
const workerDir = getStepByStepReportDir(workerConfig, true, false, globalCodeceptDir)
295319
expect(workerDir).equal('/tmp/test/output/stepByStepReport')
320+
321+
// Test run-workers mode with custom directory
322+
const workerCustomConfig = { output: './custom-output/worker2' }
323+
const workerCustomDir = getStepByStepReportDir(workerCustomConfig, true, false, globalCodeceptDir)
324+
expect(workerCustomDir).equal('/tmp/test/custom-output/stepByStepReport')
325+
326+
// Test run-multiple mode with default directory
327+
const multipleConfig = { output: './output/smoke_chrome_hash_1' }
328+
const multipleDir = getStepByStepReportDir(multipleConfig, false, true, globalCodeceptDir)
329+
expect(multipleDir).equal('/tmp/test/output/stepByStepReport')
330+
331+
// Test run-multiple mode with custom directory
332+
const multipleCustomConfig = { output: './custom-output/regression_firefox_hash_2' }
333+
const multipleCustomDir = getStepByStepReportDir(multipleCustomConfig, false, true, globalCodeceptDir)
334+
expect(multipleCustomDir).equal('/tmp/test/custom-output/stepByStepReport')
335+
336+
// Test mixed run-multiple + workers mode
337+
const mixedConfig = { output: './output/smoke_chrome_hash_1/worker1' }
338+
const mixedDir = getStepByStepReportDir(mixedConfig, true, true, globalCodeceptDir)
339+
expect(mixedDir).equal('/tmp/test/output/stepByStepReport')
296340
})
297341
})

0 commit comments

Comments
 (0)