-
Notifications
You must be signed in to change notification settings - Fork 745
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
feat: #1792 Migrate 'components/Inputs/' tests to React Testing Library #1891
feat: #1792 Migrate 'components/Inputs/' tests to React Testing Library #1891
Conversation
@@ -37,7 +37,7 @@ export function InputSwitch({ | |||
setKey(Utils.randomString()); | |||
}; | |||
|
|||
const onKeyPress = (e: SyntheticKeyboardEvent<HTMLInputElement>) => { |
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.
Small update: replaces onKeyPress with onKeyDown
, should still work without issue
@@ -6,7 +6,7 @@ import type { Checkbox } from './utils'; | |||
export type Props = { | |||
checkboxes: Checkbox[], | |||
required?: boolean, // At least one checkbox must be checked | |||
hasError?: Function, | |||
hasError?: (errorPresent: boolean) => void, |
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.
Specified the type here to reflect the fix in the mock prop passed to the component in the test.
@@ -10,7 +10,8 @@ const label = 'Some Label'; | |||
const idTwo = 'some-other-id'; | |||
const nameTwo = 'some-other-name'; | |||
const labelTwo = 'Some Other Label'; | |||
const someEvent = () => { | |||
const someEvent = (hasError) => { |
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.
Minor fix: previously, window.alert
would be invoked below even if there was no error. The assertion on line 55 helped catch this issue.
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.
Fantastic work!! :D Thanks so much for taking this on!
I have some questions but overall it looks good to me!
|
||
expect(input).toBeInTheDocument(); | ||
|
||
expect(accordionContainer).toHaveClass(css.accordionClose); |
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.
We shouldn't need to test that the styles appear. Our assertions should be on the presence of elements. Can we test that the input
element is present instead?
It should be screen.queryByLabelText(label).toBeNull()
before toggling the accordion open and then screen.getByLabelText(label). toBeInTheDocument()
after.
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.
Absolutely, I agree. Initially I tried going the same route as well.
I found that the previous tests had been checking for the presence of a hierarchy in the DOM, rather than a target element:
wrapper.find('.accordionContent').find('input').exists()
It turns out the input is always present in the DOM (but '.accordionContent' in the parent gets swapped for css.accordionClose):
The visibility gets toggled by the class accordionClose
in the parent, rather than removing the input.
So with queryByLabelText
I get:
(Aside: I was wondering whether it would be a good idea to ask and revisit the implementation itself, but I felt it was probably better to stay in the scope of the issue.)
That's when I thought to use the more idiomatic toBeVisible
matcher from 'jest-dom', which ought to detect whether an element is viewable or accessible to a screen reader.
But there I ran into a possible external issue with styles not updating correctly (leaving a comment there for more visibility), even though the functionality itself seems to work:
So in the end I thought it might be better to revisit. I can definitely keep digging in that direction? Please LMK if I'm missing something!
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.
Ooo it looks like you're right, ok I think what you have in this PR is fine. Then in another PR you can revisit the implementation. Thanks for clarifying!
@@ -18,9 +19,13 @@ describe('InputCheckbox', () => { | |||
jest.spyOn(window, 'alert'); | |||
}); | |||
|
|||
afterEach(() => { | |||
jest.clearAllMocks(); |
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.
Is this necessary?
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.
Oh right! Let me remove those. The tests should work without them. At one point, I think I ran into an issue with a subsequent assertion passing even though it shouldn't (due to a previous call). But then I think I might have resolved it and forgotten to remove these. Should have noted it down
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.
So I went back and double-checked here just to make sure. I think maybe this reset in particular makes sense, as both blocks of tests use the same assertions.
It's possible for the functionality underlying the second block to break (somehow?) and its tests could still pass due to the previous calls from the first block of tests.
expect(screen.queryByRole('input')).not.toBeInTheDocument(); | ||
// Since '@testing-library/react' does not get hidden inputs, | ||
// it can be queried directly from the container for this test. | ||
const hiddenInput = container.querySelector('input[type="hidden"]'); |
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.
Good call to put a comment here :)
|
||
// toggle both checkboxes false | ||
userEvent.click(checkbox); | ||
userEvent.dblClick(otherCheckbox); |
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.
Can this just be a regular click?
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.
Oh right I think I should maybe just leave otherCheckbox
alone, since it has an initial value of false
anyways. This double click essentially just toggles it on and off again 😅 I think I transcribed the former test too literally, where it simulated the onChange
prop firing for a false
value. Let me change it so that the test just assumes otherCheckbox
is false to begin with.
Edit: so just for the record, I ended up adding assertions to ensure the checkboxes are both unchecked as prerequisites for validating whether or not the error handler is invoked.
@@ -18,9 +19,13 @@ describe('InputDefault', () => { | |||
jest.spyOn(window, 'alert'); | |||
}); | |||
|
|||
afterEach(() => { | |||
jest.clearAllMocks(); |
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.
Is this necessary?
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.
Similarly I think I added this reset as a way to future-proof any extended tests relying on window.alert
, which might accidentally pass when they shouldn't because of a separate call.
Should that situation be handled when the time comes? I'm worried someone might roll with a false positive. But that can be caught in code review, so I can still definitely remove this.
/** | ||
* TODO: Follow up on `userEvent.type(inputSwitch, '{enter}')` in v12.1.7. | ||
* Temporarily including `fireEvent` from RTL, for which the switch must have focus first: | ||
* https://github.com/testing-library/react-testing-library/issues/376#issuecomment-541242684 |
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 for documenting this!
@@ -0,0 +1,173 @@ | |||
// @flow | |||
import { render, screen } from '@testing-library/react'; |
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.
Thank you for writing tests for this 🏆
expect(pell.exec).toHaveBeenCalledWith(...expectedArgs); | ||
}); | ||
|
||
jest.restoreAllMocks(); |
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.
Is this necessary?
…ch, remove unused act; fix hasError test prop; add more detailed hasError prop flow type
…Each spy on window.alert
…fterEach to reset spy
…fterEach to reset spy
… spec to RTL, minor: remove unused type prop
…ate for RTL; comment InputSwitch keyDown test
be84440
to
030761f
Compare
Description
Updates the tests in ‘components/Input/’ to use React Testing Library instead of Enzyme.
More Details
(1) I added comments where I ran into a couple of issues using
userEvent
and jest-dom matchers. I updated the dependencies to see if it would help (Narrator: it didn’t). But that amounted to just a minor version bump in '@testing-library/react' so I left it in.(2) I also added some coverage for the Textarea component. I noticed a TODO there about stubbing the Pell editor methods. I can always revert the newly added tests, or make the necessary changes if it should be done a better way.
(3) Also, I was wondering whether to add more coverage to a couple of the Input components. I decided just to migrate the existing ones for starters, but I can definitely add more if time permits.
(4) Lastly, I updated minor details in a couple components themselves. They still work the same (including screenshots below).
Of course, if anything looks out of place (or missing), please LMK and I'll make the necessary changes.
Thanks! ☕
Corresponding Issue
#1792
Screenshots
Switch (updated):
Tag (updated):
Reviewing this pull request? Check out our Code Review Practices guide if you haven't already!