Skip to content
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

Merged

Conversation

udayanshevade
Copy link
Contributor

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):
Screen Shot 2020-10-08 at 7 02 46 PM

Tag (updated):
Screen Shot 2020-10-08 at 11 28 12 PM


Reviewing this pull request? Check out our Code Review Practices guide if you haven't already!

@@ -37,7 +37,7 @@ export function InputSwitch({
setKey(Utils.randomString());
};

const onKeyPress = (e: SyntheticKeyboardEvent<HTMLInputElement>) => {
Copy link
Contributor Author

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,
Copy link
Contributor Author

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) => {
Copy link
Contributor Author

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.

Copy link
Member

@julianguyen julianguyen left a 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);
Copy link
Member

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.

Copy link
Contributor Author

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):

Screen Shot 2020-10-09 at 10 50 59 AM

The visibility gets toggled by the class accordionClose in the parent, rather than removing the input.

So with queryByLabelText I get:

Screen Shot 2020-10-09 at 10 30 21 AM

(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:

Screen Shot 2020-10-09 at 11 06 48 AM

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!

Copy link
Member

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();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary?

Copy link
Contributor Author

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

Copy link
Contributor Author

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"]');
Copy link
Member

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);
Copy link
Member

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?

Copy link
Contributor Author

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();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary?

Copy link
Contributor Author

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
Copy link
Member

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';
Copy link
Member

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();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants