fix: Stop calling waitFor callback after timeout#1271
fix: Stop calling waitFor callback after timeout#1271eps1lon merged 7 commits intotesting-library:mainfrom
waitFor callback after timeout#1271Conversation
|
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit 10a1ac7:
|
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## alpha #1271 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 24 24
Lines 1038 1041 +3
Branches 346 347 +1
=========================================
+ Hits 1038 1041 +3
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
eps1lon
left a comment
There was a problem hiding this comment.
Thanks for working on the fix. However, we should rather make sure that callback is not being invoked again. The proposed fix just hides the issue.
Also: Can you provide a minimal repro against which we can test this fix? Ideally we could extract a unit test from it.
73bbd53 to
a0bb005
Compare
|
@eps1lon I changed the solution implementation. You can find a reproduction example on this repository. Output example: It's been a while since I have used Don't hesitate if you have any other questions 🙇 |
|
Uh oh! @KevinBon, the image you shared is missing helpful alt text. Check #1271 (comment). Alt text is an invisible description that helps screen readers describe images to blind or low-vision users. If you are using markdown to display images, add your alt text inside the brackets of the markdown image. Learn more about alt text at Basic writing and formatting syntax: images on GitHub Docs. |
|
I created a branch to show-case that applying this patch will fix this behavior: KevinBon/jasmine-tl-waitfor-leak#1 |
src/wait-for.js
Outdated
| } | ||
|
|
||
| function handleTimeout() { | ||
| if (finished) { |
There was a problem hiding this comment.
Shouldn't we cancel the timeout instead when we finish?
There was a problem hiding this comment.
When we finish, onDone should have been called with clearTimeout (src), therefore I don't really think this one is needed, but it's more for extra-safety.
src/wait-for.js
Outdated
|
|
||
| function checkCallback() { | ||
| if (promiseStatus === 'pending') return | ||
| if (finished || promiseStatus === 'pending') return |
There was a problem hiding this comment.
Can we cancel whatever is calling checkCallback instead?
There was a problem hiding this comment.
I would love to, but I'm afraid it might introduce breaking changes.
We'll need to move the if(finished) before checkCallback here: https://github.com/testing-library/dom-testing-library/pull/1271/files#diff-68584107d5f50eb95702a47b23bb6e0f6c2d78f9bab7456a1a4150c79229db78R84-R92
But the following comment is daunting:
dom-testing-library/src/wait-for.js
Lines 84 to 87 in a0bb005
There was a problem hiding this comment.
Actually https://github.com/testing-library/dom-testing-library/pull/1271/files#diff-68584107d5f50eb95702a47b23bb6e0f6c2d78f9bab7456a1a4150c79229db78R84-R92 is un-related, so I should move the if(finished) before checkCallback 👍
| }) | ||
|
|
||
| // Could have timed-out | ||
| if (finished) { |
There was a problem hiding this comment.
With this factoring, I wonder if we could simplify further by moving checkCallback before the advanceTimers. That way the loop condition makes sure we exit as soon as we're finished. And we can remove another checkCallback on L54.
At this point I would remove the "It's really important that checkCallback [...]" comment. We might have fixed the issue along the way. And since I'd rather ship this with the next major, we have a good opportunity to resurface the bug so that we can at least link a repro. It doesn't have to be a Jest test but somehow the reordering fixed "something". That "something" needs to be linked a t least. Doesn't need to be an automated test but it needs to be something. Otherwise we're chasing ghosts.
There was a problem hiding this comment.
If we move checkCallback before the advanceTimers, the waitFor callback will be called twice before we advance the clock, instead of once right now.
Which will break this fix: https://github.com/testing-library/dom-testing-library/pull/1073/files
There was a problem hiding this comment.
the waitFor callback will be called twice before we advance the clock, instead of once right now.
but not if we remove the checkCallback call earlier from L54, no?
There was a problem hiding this comment.
There will be a change of behavior in
, for atimeout smaller than the interval, it will instead be rejected with [Error: Timed out in waitFor.]
| test('does not work after it resolves', async () => { | ||
| jest.useFakeTimers('modern') | ||
| let context = 'initial' | ||
| const contextStack = [] |
There was a problem hiding this comment.
Or
| const contextStack = [] | |
| const contextCallStack = [] |
| }) | ||
|
|
||
| expect(context).toEqual('initial') | ||
| test(`when fake timer is installed, on waitFor timeout, it doesn't call the callback afterward`, async () => { |
There was a problem hiding this comment.
This test ensures that the fix is working
| @@ -292,12 +292,11 @@ test('the real timers => fake timers error shows the original stack trace when c | |||
|
|
|||
| test('does not work after it resolves', async () => { | |||
There was a problem hiding this comment.
I made some changes on this test to help understand the outcome: everything that starts needs to end
waitfor callback leakwaitFor callback leak on waitFor timeout
|
@eps1lon let me know if you need additional information or are unhappy with my changes 🙇 |
|
@eps1lon bumping, let me know if you need anything from me. Thank you! |
|
Thanks for the patience. Checking this out now so that we can land it ASAP. |
waitFor callback leak on waitFor timeoutwaitFor callback leak on waitFor timeout
waitFor callback leak on waitFor timeoutwaitFor callback after timeout
eps1lon
left a comment
There was a problem hiding this comment.
Looks great, thank you. I verified the tests are actually failing on main
eps1lon
left a comment
There was a problem hiding this comment.
Looks great, thank you. I verified the tests are actually failing on main
|
@all-contributors add @KevinBon for code, bugs |
|
I've put up a pull request to add @KevinBon! 🎉 |

What:
Prevent
waitForcallback from being invoked even after it resolved, onwaitFortimeout.Why:
More context can be found in this issue, but with that context in mind, it's preventing test side-effects:
afterEachshould be enough to "clean" the previous test from any side-effect. However, because of the callback leak issue,window.variablewill remain to be123even after being set back to1for a short period of time.How:
When the clock is mocked, only call
checkCallbackwhenfinishedisfalseChecklist:
docs site: N/A
resolves #1270