-
Notifications
You must be signed in to change notification settings - Fork 3k
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
[No QA] Simplify test workflow using Jest --shard flag #13943
Conversation
# Conflicts: # .github/scripts/verifyPodfile.sh
Okay, that test is consistently passing when run in isolation, but I'm able to reproduce the failure (and timeout issue) when running the full |
…rsonalDetails Onyx data
# Conflicts: # tests/ui/UnreadIndicatorsTest.js
@@ -19,8 +19,6 @@ import * as SequentialQueue from '../../src/libs/Network/SequentialQueue'; | |||
import * as MainQueue from '../../src/libs/Network/MainQueue'; | |||
import * as Request from '../../src/libs/Request'; | |||
|
|||
jest.useFakeTimers(); |
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 was redundant
@@ -11,10 +11,6 @@ import ONYXKEYS from '../../src/ONYXKEYS'; | |||
|
|||
jest.mock('../../src/libs/getPlatform'); | |||
|
|||
// Using fake timers is causing problems with promises getting timed out | |||
// This seems related: https://github.com/facebook/jest/issues/11876 | |||
jest.useRealTimers(); |
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 seems to be resolved now, maybe because of the new fake timers module or because we have doNotFake: ['nextTick]
@thesahindia @aldo-expensify One of you needs to copy/paste the Reviewer Checklist from here into a new comment on this PR and complete it. If you have the K2 extension, you can simply click: [this button] |
Removing C+ review here because there's nothing for them to test beyond what we can see in the CI. |
# Conflicts: # src/libs/ReportUtils.js
@@ -276,6 +276,7 @@ describe('actions/Report', () => { | |||
expect(ReportUtils.isUnread(report)).toBe(true); | |||
|
|||
// When the user visits the report | |||
jest.advanceTimersByTime(10); | |||
currentTime = DateUtils.getDBTime(); |
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.
NAB: The DateUtils.getDBTime()
will get a frozen time that only moves forward if you do jest.advanceTimersByTime
, did I get that correctly?
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.
Yes
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.
But that's kind of a good thing because the "fake" timestamps are consistent and predictable.
Reviewer Checklist
Screenshots: N/A |
✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release. |
Performance Comparison Report 📊Significant Changes To DurationThere are no entries Meaningless Changes To DurationShow entries
Show details
|
🚀 Deployed to staging by https://github.com/roryabraham in version: 1.2.68-0 🚀
|
🚀 Deployed to production by https://github.com/mountiny in version: 1.2.68-0 🚀
|
Details
This PR started out as a simplification of the test workflow, but grew with some (valuable) scope-creep. Upgrading Jest was a huge pain, so I'm proud of this PR that gets it done!
The initial changes
config
job fromtest.yml
, instead define the matrix inlineUpgrading Jest
--shard
feature of jest to split up the tests between multiple runners, we needed to upgrade to at least Jest 28: https://jestjs.io/blog/2022/04/25/jest-28#sharding-of-test-run, so I went for the latest version (Jest 29)Fake Timers - why?
One of the biggest changes with Jest 28 was that it changed the "fake timers" module.
I think the idea behind fake timers is to make tests faster and less flaky. Rather than waiting for actual CPU cycles, timers, JavaScript macrotasks, etc... (all things I don't really understand very well), everything is "frozen" by default with fake timers.
Then you get fine-grained control when you want to wait for promises to resolve, when you want to advance the timers and by how much. This speeds things up because you generally never wait for real timeouts, and when the clock needs to be advanced that happens as close to instantly as possible.
So in general using real timers in tests is bad (supposedly), so we enable them globally. The only timer we don't fake globally is
nextTick
, and I think it has to do with how Onyx intentionally updates subscribers only on the next tick (example). As stated in the code comment there we don't necessarily need to do this anymore and it in my opinion it would be very valuable to clean that up in the future. I think that would also mean we could remove many if instances ofwaitForPromisesToResolve
throughout our tests.Fixing flaky UnreadActionsTest
The UnreadActionsTest is slow and was pretty flaky (very consistent on my M2 mac, but very inconsistent on the GitHub Actions runners). So I did a few things to make it better:
Moved
jest.setTimeout
to the correct location. Not sure if something changed in jest or if it just wasn't doing anything before, but according to the jest docs:So I think that the file root is the correct place (and it resolved timeout errors I was seeing on this PR).
Before we were using
jest.advanceTimersByTime(100);
to wait for animations to complete. This was flaky even on my computer, and to get it working consistently on the runner I had to increase the timeout to like500
. Maybe something to do with the new timers? Anyways, rather than relying on this sort of hacky/slow/flaky method of waiting for the animation to finish, I decided to use the approach recommended by@testing-library/react-native
– usingwaitFor
.However,
waitFor
had a gotcha – for now, we need to use the@testing-library/react-native
jest preset in order for it to work properly (fixed upstream in React Native 0.71.2, issue created here, TODO left in the code)In order to use the
@testing-library/react-native
plugin, I had to upgrade@testing-library/react-native
. (More on this below)Upgrading @testing-library/react-native
render
call to usingscreen
instead. This wasn't strictly necessary, but is recommended by the library maintainers. While it did make the diff of this PR a bit nastier, it's ultimately a simplification because it doesn't require you to maintain and pass around a reference to the render result. I went back and forth on whether this was worth it, but it was just a series of a few simple find-and-replace queries in three files, so I included it.SessionTest
I'm not sure why this was passing before and not after the Jest upgrade, but it seems like it was a false positive before.
Session.signOut
alone is not enough to makePushNotification.deregister
be called. You need to call signOutAndRedirectToSignIn instead, which in turn clears Onyx, which in turn causesPushNotification.deregister
to be called.Other
config/jest.config.js
orjest/config.js
, so I just put in the project root because that's the standard place for it.FAIL tests/
in the GitHub Actions logs). Now we get a concise list of all the tests that failed.Future Work
jsdom
, but GitHub Actions tests run in node, as so should be tested in a node environment rather thanjsdom
. This can be accomplished with a comment in the first lines of the file, but I think a better solution would be to move those totests/githubActions
and configure jest to use thenode
runner for tests in that directory, andjsdom
for the others. This PR is big enough already so I'm not doing that today.waitFor
instead of advancing timers or usingwaitForPromisesToResolve
, and it might make the tests more readable and intuitiveFixed Issues
$ #14088
Tests (already done)
Purposely break a Jest test, then push code to this PR. The
test
workflow should fail. This happened plenty of times during development, as you can see below.Offline tests
None.
QA Steps
None.
PR Author Checklist
### Fixed Issues
section aboveTests
sectionOffline steps
sectionQA steps
sectiontoggleReport
and notonIconClick
)src/languages/*
files and using the translation methodWaiting for Copy
label for a copy review on the original GH to get the correct copy.STYLE.md
) were followedAvatar
, I verified the components usingAvatar
are working as expected)/** comment above it */
this
properly so there are no scoping issues (i.e. foronClick={this.submit}
the methodthis.submit
should be bound tothis
in the constructor)this
are necessary to be bound (i.e. avoidthis.submit = this.submit.bind(this);
ifthis.submit
is never passed to a component event handler likeonClick
)StyleUtils.getBackgroundAndBorderStyle(themeColors.componentBG
)Avatar
is modified, I verified thatAvatar
is working as expected in all cases)ScrollView
component to make it scrollable when more elements are added to the page.Screenshots/Videos
Screenshots are not included, since only test code has been updated and those tests have run in the CI for this PR. For good measure I've included a screenshot of all tests passing locally (both in my terminal and my IDE).