From 094680b1e4b387504c3a747b3b5d10226a5be225 Mon Sep 17 00:00:00 2001 From: gololdf1sh Date: Fri, 28 Aug 2026 12:14:52 +0300 Subject: [PATCH] fix: preserve test artifacts on retry --- lib/listener/retryEnhancer.js | 3 ++- lib/plugin/screencast.js | 3 ++- test/unit/mocha/test_clone_test.js | 30 +++++++++++++++++++++++++++++ test/unit/plugin/screencast_test.js | 21 ++++++++++++++++++++ 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/lib/listener/retryEnhancer.js b/lib/listener/retryEnhancer.js index 8220fe1d1..3514698be 100644 --- a/lib/listener/retryEnhancer.js +++ b/lib/listener/retryEnhancer.js @@ -44,7 +44,8 @@ function copyCodeceptJSProperties(originalTest, retriedTest) { } if (originalTest.artifacts !== undefined) { - retriedTest.artifacts = originalTest.artifacts ? [...originalTest.artifacts] : [] + const artifacts = originalTest.artifacts + retriedTest.artifacts = artifacts ? (Array.isArray(artifacts) ? Object.assign([], artifacts) : { ...artifacts }) : [] } if (originalTest.steps !== undefined) { diff --git a/lib/plugin/screencast.js b/lib/plugin/screencast.js index 7d225ea95..46712b786 100644 --- a/lib/plugin/screencast.js +++ b/lib/plugin/screencast.js @@ -276,7 +276,8 @@ function buildSrt(steps) { } function ensureArtifactsObject(test) { - if (!test.artifacts || Array.isArray(test.artifacts)) test.artifacts = {} + if (!test.artifacts) test.artifacts = {} + else if (Array.isArray(test.artifacts)) test.artifacts = Object.assign({}, test.artifacts) } function attachJUnitArtifact(test, filePath) { diff --git a/test/unit/mocha/test_clone_test.js b/test/unit/mocha/test_clone_test.js index e16da8ad7..0e5a324b8 100644 --- a/test/unit/mocha/test_clone_test.js +++ b/test/unit/mocha/test_clone_test.js @@ -137,4 +137,34 @@ describe('Test cloning for retries', function () { expect(retriedTest.applyOptions).to.be.a('function') expect(retriedTest.simplify).to.be.a('function') }) + + it('should copy object-shaped artifacts on retry without throwing', function () { + retryEnhancer() + + const originalTest = createTest('Test with object artifacts', () => {}) + + originalTest.artifacts = { screenshot: 'failed.png', screencast: 'failed.webm' } + + const retriedTest = Test.prototype.clone.call(originalTest) + event.emit(event.test.before, retriedTest) + + expect(retriedTest.artifacts).to.deep.equal({ screenshot: 'failed.png', screencast: 'failed.webm' }) + expect(retriedTest.simplify).to.be.a('function') + }) + + it('should keep named keys written onto array-shaped artifacts', function () { + retryEnhancer() + + const originalTest = createTest('Test with mixed artifacts', () => {}) + + const artifacts = ['trace.zip'] + artifacts.screenshot = 'failed.png' + originalTest.artifacts = artifacts + + const retriedTest = Test.prototype.clone.call(originalTest) + event.emit(event.test.before, retriedTest) + + expect([...retriedTest.artifacts]).to.deep.equal(['trace.zip']) + expect(retriedTest.artifacts.screenshot).to.equal('failed.png') + }) }) diff --git a/test/unit/plugin/screencast_test.js b/test/unit/plugin/screencast_test.js index 8dbe9d451..736b53378 100644 --- a/test/unit/plugin/screencast_test.js +++ b/test/unit/plugin/screencast_test.js @@ -128,6 +128,27 @@ describe('screencast', () => { expect(test.artifacts.screencast).to.match(/keep-on-fail.*\.webm$/) }) + it('on=fail keeps a screenshot already written onto array-shaped artifacts', async () => { + const sc = makeFakeScreencast() + container.clear({ Playwright: { options: {}, page: { screencast: sc } } }) + + screencast({ on: 'fail' }) + const test = createTest('keep-screenshot') + test.artifacts.screenshot = 'failed.png' + + event.dispatcher.emit(event.test.before, test) + event.dispatcher.emit(event.test.started, test) + event.dispatcher.emit(event.step.started, aStep()) + await recorder.promise() + + event.dispatcher.emit(event.test.failed, test, new Error('boom')) + event.dispatcher.emit(event.test.after, test) + await recorder.promise() + + expect(test.artifacts.screenshot).to.equal('failed.png') + expect(test.artifacts.screencast).to.match(/keep-screenshot.*\.webm$/) + }) + it('captions=true triggers showActions; captions=false does not', async () => { const sc = makeFakeScreencast() container.clear({ Playwright: { options: {}, page: { screencast: sc } } })