fix(test-utils): Duplicated subscribers re insertion (#13798)

* fix

* Create pink-boats-type.md
This commit is contained in:
Adrien de Peretti
2025-10-21 16:00:48 +02:00
committed by GitHub
parent a34fcfab35
commit 2d1d51a2af
3 changed files with 131 additions and 3 deletions

View File

@@ -0,0 +1,5 @@
---
"@medusajs/test-utils": patch
---
fix(test-utils): Event subscribers duplicated durin tests

View File

@@ -307,4 +307,120 @@ describe("waitSubscribersExecution", () => {
clearTimeoutSpy.mockRestore()
})
})
describe("nested wrapper handling", () => {
it("should handle multiple consecutive waitSubscribersExecution calls on the same event", async () => {
const originalListener = jest.fn().mockResolvedValue("result")
eventBus.eventEmitter_.on(TEST_EVENT, originalListener)
// First wait
const wait1 = waitSubscribersExecution(TEST_EVENT, eventBus as any)
eventBus.emit(TEST_EVENT, "data1")
await wait1
expect(originalListener).toHaveBeenCalledWith("data1")
expect(originalListener).toHaveBeenCalledTimes(1)
// Second wait on the same event - should still restore properly
const wait2 = waitSubscribersExecution(TEST_EVENT, eventBus as any)
eventBus.emit(TEST_EVENT, "data2")
await wait2
expect(originalListener).toHaveBeenCalledWith("data2")
expect(originalListener).toHaveBeenCalledTimes(2)
// Original listener should still be intact and work normally
eventBus.emit(TEST_EVENT, "data3")
expect(originalListener).toHaveBeenCalledWith("data3")
expect(originalListener).toHaveBeenCalledTimes(3)
// Verify we still have the correct number of listeners
const listeners = eventBus.eventEmitter_.listeners(TEST_EVENT)
expect(listeners).toHaveLength(1)
expect(listeners[0]).toBe(originalListener)
})
it("should handle three consecutive waitSubscribersExecution calls", async () => {
const originalListener = jest.fn().mockResolvedValue("result")
eventBus.eventEmitter_.on(TEST_EVENT, originalListener)
// First wait
const wait1 = waitSubscribersExecution(TEST_EVENT, eventBus as any)
eventBus.emit(TEST_EVENT, "data1")
await wait1
// Second wait
const wait2 = waitSubscribersExecution(TEST_EVENT, eventBus as any)
eventBus.emit(TEST_EVENT, "data2")
await wait2
// Third wait
const wait3 = waitSubscribersExecution(TEST_EVENT, eventBus as any)
eventBus.emit(TEST_EVENT, "data3")
await wait3
// Verify the original listener is still properly attached
const listeners = eventBus.eventEmitter_.listeners(TEST_EVENT)
expect(listeners).toHaveLength(1)
expect(listeners[0]).toBe(originalListener)
// Verify it still works
eventBus.emit(TEST_EVENT, "data4")
expect(originalListener).toHaveBeenCalledWith("data4")
expect(originalListener).toHaveBeenCalledTimes(4)
})
it("should handle multiple listeners with consecutive waits", async () => {
const listener1 = jest.fn().mockResolvedValue("result1")
const listener2 = jest.fn().mockResolvedValue("result2")
eventBus.eventEmitter_.on(TEST_EVENT, listener1)
eventBus.eventEmitter_.on(TEST_EVENT, listener2)
// First wait
const wait1 = waitSubscribersExecution(TEST_EVENT, eventBus as any)
eventBus.emit(TEST_EVENT, "data1")
await wait1
expect(listener1).toHaveBeenCalledWith("data1")
expect(listener2).toHaveBeenCalledWith("data1")
// Second wait
const wait2 = waitSubscribersExecution(TEST_EVENT, eventBus as any)
eventBus.emit(TEST_EVENT, "data2")
await wait2
expect(listener1).toHaveBeenCalledWith("data2")
expect(listener2).toHaveBeenCalledWith("data2")
// Verify both original listeners are still properly attached
const listeners = eventBus.eventEmitter_.listeners(TEST_EVENT)
expect(listeners).toHaveLength(2)
expect(listeners[0]).toBe(listener1)
expect(listeners[1]).toBe(listener2)
// Verify they still work normally
eventBus.emit(TEST_EVENT, "data3")
expect(listener1).toHaveBeenCalledWith("data3")
expect(listener2).toHaveBeenCalledWith("data3")
})
it("should not add undefined listener when originalListener chain ends", async () => {
// This tests the edge case where listener.originalListener might be undefined
const originalListener = jest.fn().mockResolvedValue("result")
eventBus.eventEmitter_.on(TEST_EVENT, originalListener)
const wait1 = waitSubscribersExecution(TEST_EVENT, eventBus as any)
eventBus.emit(TEST_EVENT, "data1")
await wait1
// Verify we have exactly one listener (the original)
const listeners = eventBus.eventEmitter_.listeners(TEST_EVENT)
expect(listeners).toHaveLength(1)
// Verify it's not undefined
expect(listeners[0]).toBeDefined()
expect(listeners[0]).toBe(originalListener)
})
})
})

View File

@@ -99,6 +99,7 @@ const doWaitSubscribersExecution = (
})
subscriberPromises.push(promise)
let res: any[] = []
let cleanupDone = false
const newListener = async (...args2: any[]) => {
try {
@@ -121,14 +122,20 @@ const doWaitSubscribersExecution = (
nok(error)
}
if (currentCount >= triggerCount) {
if (currentCount >= triggerCount && !cleanupDone) {
// As soon as the subscriber is executed the required number of times, we restore the original listener
cleanupDone = true
eventEmitter.removeListener(eventName, newListener)
// Unwrap to find the true original listener
let listenerToAdd = listener
while (listenerToAdd.originalListener) {
while (listenerToAdd?.originalListener) {
listenerToAdd = listenerToAdd.originalListener
}
eventEmitter.on(eventName, listenerToAdd)
if (listenerToAdd) {
eventEmitter.on(eventName, listenerToAdd)
}
}
}