Skip to content

Commit 70e9f29

Browse files
DavertMikclaude
andcommitted
fix(retryFailedStep): avoid mutating shared defaultConfig
Each call to retryFailedStep mutated the module-level defaultConfig via Object.assign(defaultConfig, config), so config.when from a prior call leaked into the next as customWhen and chained recursively. In tests this made when() return undefined for closures that no longer had a live step.started listener (e.g. after event.cleanDispatcher), causing the new regression test to fail in the full unit suite even though it passed in isolation. Use Object.assign({}, defaultConfig, config) so each registration gets an independent config object. Rewrites the regression test to assert via retryConfig.when() directly, which is now sound. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent e9f4836 commit 70e9f29

2 files changed

Lines changed: 9 additions & 22 deletions

File tree

lib/plugin/retryFailedStep.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ const RETRY_PRIORITIES = {
8989
*
9090
*/
9191
export default function (config) {
92-
config = Object.assign(defaultConfig, config)
92+
config = Object.assign({}, defaultConfig, config)
9393
config.ignoredSteps = config.ignoredSteps.concat(config.defaultIgnoredSteps)
9494
const customWhen = config.when
9595

test/unit/plugin/retryFailedStep_test.js

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -157,34 +157,21 @@ describe('retryFailedStep', () => {
157157
// expects to retry only once
158158
})
159159

160-
it('should not treat exact-name ignoredSteps entries as wildcard prefixes', async () => {
160+
it('should not treat exact-name ignoredSteps entries as wildcard prefixes', () => {
161161
// Regression: ignored.indexOf('*') was used as truthy check.
162162
// -1 is truthy, so entries without '*' were matched via startsWith(slice(0, -1)).
163163
// ignoredSteps: ['see'] would silently ignore seeElement, seeInField, selectOption, etc.
164164
retryFailedStep({ retries: 2, minTimeout: 1, ignoredSteps: ['see'] })
165-
event.dispatcher.emit(event.test.before, createTest('test'))
165+
store.autoRetries = true
166+
const retryConfig = recorder.retries[recorder.retries.length - 1]
166167

167-
let counter = 0
168168
event.dispatcher.emit(event.step.started, { title: 'seeElement' })
169-
try {
170-
await recorder.add(
171-
() => {
172-
counter++
173-
if (counter < 3) {
174-
throw new Error()
175-
}
176-
},
177-
undefined,
178-
undefined,
179-
true,
180-
)
181-
await recorder.promise()
182-
} catch (e) {
183-
await recorder.catchWithoutStop(err => err)
184-
}
169+
expect(retryConfig.when(new Error()), "'seeElement' must not be ignored when only 'see' is configured").to.equal(true)
170+
171+
event.dispatcher.emit(event.step.passed, {})
185172

186-
// seeElement is NOT in ignoredSteps (only 'see' is) — should be retried.
187-
expect(counter).to.be.greaterThan(1)
173+
event.dispatcher.emit(event.step.started, { title: 'see' })
174+
expect(retryConfig.when(new Error()), "exact match 'see' must still be ignored").to.not.equal(true)
188175
})
189176

190177
it('should add custom regexp steps to ignore', async () => {

0 commit comments

Comments
 (0)