-
Notifications
You must be signed in to change notification settings - Fork 469
Upgrade wait-for-expect package #342
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Upgrade wait-for-expect package #342
Conversation
The new version of this package includes some important fixes.
Will this be a breaking change? If so, I'll need to understand the justification for bumping the major version here and in all other consuming libraries for this change. |
It's a breaking change for those who are using the lib and are setting jest's timers to However, as we discussed in TheBrainFamily/wait-for-expect#20, it doesn't really make sense to use Regarding the benefits, upgrading the package will allow to have |
Hmmm... I wonder if our own |
Sure. I will take a look at it tomorrow |
Indeed, the |
I'd like to hear what other maintainers have to say about this breaking change. To help clarify things, @brapifra, could you please give us a small example of a test that works today, but wont work with these changes, as well as one which does not work today and will work with these changes and why that is desirable? |
Right... I think I expect this part of the code not to care about Jest or fake timers at all. |
@alexkrolick That part of the code does take care about Jest timers because is using the globals @kentcdodds This is an example that doesn't work today (when it should): import { waitForElement } from "@testing-library/dom";
// The timers have been defaulted to fake in package.json/jest.config.json
describe("Suite", () => {
it("should not timeout", async () => {
jest.useRealTimers();
const container = document.createElement("div");
await waitForElement(() => container.querySelector("div"));
}); // throws timeout error
}); It doesn't work because the dom-testing-library doesn't get all these global timeout variables lazily. Therefore, the unit test times out as it's using fake timers when it should use the real ones that have been set inside the On the other hand, this is working now (although it shouldn't) and it will start failing if you accept my changes: import { waitForElement } from "@testing-library/dom";
// The timers are real by default if you don't change them in package.json/jest.config.json
describe("Suite", () => {
it("should timeout", async () => {
jest.useFakeTimers();
const container = document.createElement("div");
await waitForElement(() => container.querySelector("div"));
}); // it passes
}); Due to the same reason, it's using the default real timers instead of the fake ones set inside the |
Thank you for those examples. They're very helpful. I think I've never run into this problem because I don't enable fake timers by default. Furthermore I also don't mix fake timers and the wait utilities in a single test (which I think we should recommend in the docs if we don't already). So what we're doing here is changing the default I guess. When we do something like that, we need to consider if we accept and release this pull request:
Here's my analysis, I'd like to hear from others: The negative impact and the likelihood of that impact: people who are mixing fake timers and wait utilities will have their tests break. I can't tell how many people this is, but it's probably a nontrivial amount of people, so I would say the likelihood of that impact is pretty high. In addition, the likelihood that people will continue to be impacted over time as they bump into this is nontrivial as well and the cost of teaching people to avoid this problem is nontrivial as well. The positive impact and the likelihood of that impact: people who are using fake timers by default and using the wait utilities will be impacted. The number of people in that category today is 0. We know this because doing that is currently impossible. There may be a handful of people who want to do that, but I think that is actually pretty rare because the default configuration in Jest is to use real timers and the vast majority of people do not change the default because they either don't know it's possible, or don't know why they would want to. So the positive impact will be for people who know that they can use fake timers by default and configure jest to do so. I'd say that the impact is relatively low, but the likelihood of a positive impact for that smaller group is high. Ultimately, mixing fake timers and our wait utilities is pretty problematic. I definitely would like it if we could make enabling fake timers by default work, but I don't want to force people to have to call The fact is that async stuff (which is tested by our wait utilities) happens in an app way more often than people needing fake timers. So here's what I want: Can we think of a way to make this work: import { waitForElement } from "@testing-library/dom";
// The timers have been defaulted to fake in package.json/jest.config.json
describe("Suite", () => {
it("should not timeout", async () => {
// jest.useRealTimers(); // <-- this is not needed
const container = document.createElement("div");
await waitForElement(() => container.querySelector("div"));
}); // it passes
}); Because I don't think that I want to make this change for what is a relatively small group of people and incur the negative impact on what is probably a relatively larger group of people (today and in the future) who will run into issues if we merge this PR. |
@kentcdodds that's what I meant, too - can we make |
I agree with you that ideally this lib and others like It's basically the same thing as having a library that is run in the browser: If someone else modifies any of the global variables that your library is using, it will probably stop working as expected. And unfortunately, I'm one of the few that has a big project with fake timers by default 😞 EDIT: One option I thought about is to create a separate jsdom environment and use the window object from that environment: import { JSDOM } from 'jsdom';
const globalObj = new JSDOM().window; There would still be a point of failure (the EDIT2: Another solution would be to check if the timers have been set to fake by jest. In that case we could temporarily set them back to real in order to get the proper timeout functions: const usingJestFakeTimers = window.setTimeout._isMockFunction;
// Disable jest's fake timers temporarily
if (usingJestFakeTimers) {
jest.useRealTimers();
}
const globalObj = typeof window === 'undefined' ? global : window
...
const clearTimeoutFn = globalObj.clearTimeout
// istanbul ignore next
const setImmediateFn = globalObj.setImmediate || setImmediatePolyfill
const setTimeoutFn = globalObj.setTimeout
// Restore jest's timers
if (usingJestFakeTimers) {
jest.useFakeTimers();
} I like this solution more, although it would only fix the problem in jest. There would still be the case where the library is used with some other test runner that mocks the timers in a different way. |
Any thoughts about my previous comment? @kentcdodds @alexkrolick |
Whoa, I didn't get notified of your edit. Sorry about that. Will read it over soon. |
I'm in favor of your last solution of enabling fake timers. Let's solve for the jest case, and if others crop up we can deal with those. Just make sure that the solution code runs in other environments. I love this. Thanks for putting some thought into it! Could you make that happen? |
I can, and I will... tomorrow 😄 I agree with you, the last solution is probably the best one. |
Unfortunately, because the |
I was planning to apply the same logic to |
Oh? I thought that all we needed to do was your suggestion above and that would result in no breaking change correct? |
Oopss... yeah, that's what I was trying to say 😅 By adding my suggestion, all the wait* functions will work as expected without any breaking change. |
Sweet! Thanks for talking us through this! I'll look forward to your updates when you get the chance :) |
There you go @kentcdodds. I have added a few more tests to make sure that the library restores the jest timers after getting the time functions. EDIT: I'm working on improving the tests to reach 100% code coverage |
Ok, I think everything is properly changed and tested now. I've ended up creating a |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! This looks good to me 👍 Just a few comments.
Hmm... the nodev8 build job failed. That's weird 🤔 I've just tried to run the tests with a couple of nodev8 versions that I had in my laptop and they passed 😕 |
It could be a race condition issue. That sometimes happens with these lower-level async tests 😬 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me. I'd love to get a 👍 from another maintainer on this though.
Could you take a look at this? @alexkrolick |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is fine. Let's get this merged 👍
@all-contributors please add @brapifra for code and tests |
I've put up a pull request to add @brapifra! 🎉 |
Once |
🎉 This PR is included in version 6.3.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
Hey guys, wait-for-expect got bumped to 3.0.0 to accommodate the changes in line with the work/discussion here. @kentcdodds feel free to take a look and decide if you want to upgrade. |
Related to TheBrainFamily/wait-for-expect#20