From 81519adff84b1883247025920db782dc83a3b27a Mon Sep 17 00:00:00 2001 From: "grigas.petraitis" Date: Mon, 17 Sep 2018 10:56:12 +0300 Subject: [PATCH 1/3] fix(waitForElement): ensure waitForElement disconnects MutationObserver at the right time --- src/__tests__/wait-for-element.js | 45 +++++++++++++++++++++++++++++++ src/wait-for-element.js | 2 +- 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/src/__tests__/wait-for-element.js b/src/__tests__/wait-for-element.js index 6c655855..1feaa619 100644 --- a/src/__tests__/wait-for-element.js +++ b/src/__tests__/wait-for-element.js @@ -312,3 +312,48 @@ test('it returns immediately if the callback returns the value before any mutati return promise }) + +test('does not get into infinite setTimeout loop after MutationObserver notification', async () => { + const {container} = render(`
`) + + let didMakeMutation = false + const callback = jest.fn(() => didMakeMutation).mockName('callback') + const successHandler = jest.fn().mockName('successHandler') + const errorHandler = jest.fn().mockName('errorHandler') + jest.useFakeTimers() + + const promise = waitForElement(callback, { + container, + timeout: 70, + mutationObserverOptions: {attributes: true}, + }).then(successHandler, errorHandler) + + // Expect 2 timeouts to be scheduled: + // - waitForElement timeout + // - MutationObserver timeout + expect(setTimeout).toHaveBeenCalledTimes(2) + + // One synchronous `callback` call is expected. + expect(callback).toHaveBeenCalledTimes(1) + expect(successHandler).toHaveBeenCalledTimes(0) + expect(errorHandler).toHaveBeenCalledTimes(0) + + // Make a mutation so MutationObserver calls out callback + container.setAttribute('data-test-attribute', 'something changed') + didMakeMutation = true + + // Advance timer to expire MutationObserver timeout + jest.advanceTimersByTime(50) + jest.runAllImmediates() + await promise + expect(setTimeout).toHaveBeenCalledTimes(3) + + // Expect callback and successHandler to be called + expect(callback).toHaveBeenCalledTimes(2) + expect(successHandler).toHaveBeenCalledTimes(1) + expect(errorHandler).toHaveBeenCalledTimes(0) + + // Expect no more setTimeout calls + jest.advanceTimersByTime(100) + expect(setTimeout).toHaveBeenCalledTimes(3) +}) diff --git a/src/wait-for-element.js b/src/wait-for-element.js index 0ae3ba41..1bed5511 100644 --- a/src/wait-for-element.js +++ b/src/wait-for-element.js @@ -18,7 +18,7 @@ function waitForElement( let lastError, observer, timer // eslint-disable-line prefer-const function onDone(error, result) { clearTimeout(timer) - observer.disconnect() + setImmediate(() => observer.disconnect()) if (error) { reject(error) } else { From 00b018d6d0acbee2a7cc258da2b1f32fee3d8a11 Mon Sep 17 00:00:00 2001 From: "Kent C. Dodds" Date: Mon, 17 Sep 2018 11:38:21 -0600 Subject: [PATCH 2/3] test to make sure the test fails without changes --- src/wait-for-element.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wait-for-element.js b/src/wait-for-element.js index 1bed5511..0ae3ba41 100644 --- a/src/wait-for-element.js +++ b/src/wait-for-element.js @@ -18,7 +18,7 @@ function waitForElement( let lastError, observer, timer // eslint-disable-line prefer-const function onDone(error, result) { clearTimeout(timer) - setImmediate(() => observer.disconnect()) + observer.disconnect() if (error) { reject(error) } else { From 898450144f18ff42f835eab38be1159341b9954f Mon Sep 17 00:00:00 2001 From: "Kent C. Dodds" Date: Mon, 17 Sep 2018 11:41:23 -0600 Subject: [PATCH 3/3] restore original solution --- src/wait-for-element.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wait-for-element.js b/src/wait-for-element.js index 0ae3ba41..1bed5511 100644 --- a/src/wait-for-element.js +++ b/src/wait-for-element.js @@ -18,7 +18,7 @@ function waitForElement( let lastError, observer, timer // eslint-disable-line prefer-const function onDone(error, result) { clearTimeout(timer) - observer.disconnect() + setImmediate(() => observer.disconnect()) if (error) { reject(error) } else {