-
Notifications
You must be signed in to change notification settings - Fork 577
Shift mocked store enzyme test to React Testing Library #3909
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
base: main
Are you sure you want to change the base?
Shift mocked store enzyme test to React Testing Library #3909
Conversation
|
Thanks for your contribution, @nishitmistry! Is this ready for a review or do you still plan to make changes? |
|
Hey @mathjazz, I am done with the migrations of store related tests, you can go ahead with the review. |
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.
Nice work!
Added some high-level questions and comments. Please also rebase against main.
Thanks for your contributions to Pontoon!
|
|
||
| import { FailedChecks } from './FailedChecks'; | ||
| import { vi } from 'vitest'; | ||
| import { expect } from 'vitest'; |
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.
Why not put everything in one line?
import { vi, expect } from 'vitest';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.
fixed
| }); | ||
|
|
||
| expect(wrapper.find('ul > *')).toHaveLength(3); | ||
| expect(container.querySelectorAll('ul > *')).toHaveLength(3); |
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.
Could you please help me understand what's the motivation for this change (and many other similar changes below)? Is find() coming from enzyme?
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, find() is returned by the mount() method of enzyme, which locates a node based on any selector. There's no such method in React Testing Library to select a node with html selector, so we use the container's querySelector() to select the nodes.
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.
Using querySelector() is a bit of a code smell with testing-library, tbh. It's ok to do so initially here, but it would be good to leave // TODO code comments in the vicinity of each such test to replace them with getBy* and similar testing-library-ish approaches.
|
|
||
| return ( | ||
| <li className={cn} onClick={handleSelectEntity}> | ||
| <li className={cn} onClick={handleSelectEntity} data-testid='entity'> |
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.
Not a big fan of adding data-* attributes just for the sake of testing. Is it really 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.
We can't query based on the component as we do with Enzyme ( wrapper.find(Entity); is not possible in React Testing Library). So if we wish to know whether a particular component exists or not, adding data-testid='entity' and then querying it would be the way afaik.
React Testing Library recommends an accessibility-based testing approach RTL queries priority guide, but the task is limited to just the migration.
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.
The testid attributes do have their place, but as the page you link to itself documents, they should only be used when no other option works.
579e89f to
8ba0e27
Compare
28d5063 to
fd687b4
Compare
eemeli
left a comment
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.
Looks pretty decent. A few too many test ids and on occasion a bit too verbose, but decent. More comments inline.
| @@ -30,7 +30,9 @@ describe('<FailedChecks>', () => { | |||
| it('does not render if no errors or warnings present', () => { | |||
| const wrapper = mountFailedChecks({ errors: [], warnings: [] }); | |||
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.
Let's go with the destructuring style you use elsewhere in this PR.
| const wrapper = mountFailedChecks({ errors: [], warnings: [] }); | |
| const { container } = mountFailedChecks({ errors: [], warnings: [] }); |
This same suggestion also applies to other places where the render() result is used.
| }); | ||
|
|
||
| expect(wrapper.find('ul > *')).toHaveLength(3); | ||
| expect(container.querySelectorAll('ul > *')).toHaveLength(3); |
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.
Using querySelector() is a bit of a code smell with testing-library, tbh. It's ok to do so initially here, but it would be good to leave // TODO code comments in the vicinity of each such test to replace them with getBy* and similar testing-library-ish approaches.
| <section className='history'> | ||
| <section | ||
| className='history' | ||
| data-testid='history-History--no-translations' |
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.
You've taken this testid from the localization id. Rather than doing so, this (and potentially other similar cases) seem like a perfect use case for getByText().
| @@ -52,7 +53,7 @@ describe('<EntitiesList>', () => { | |||
| hasMore: true, | |||
| }); | |||
| const wrapper = mountComponentWithStore(EntitiesList, store); | |||
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.
Breaking this suggestion in two to deal with GitHub's limitations; similar also applies to other tests -- the getBy* functions should be preferred where they're an option, and they don't need an expect() wrapper for an N=1 expected result.
| const wrapper = mountComponentWithStore(EntitiesList, store); | |
| const { getByTestId } = mountComponentWithStore(EntitiesList, store); |
| }); | ||
| const wrapper = mountComponentWithStore(EntitiesList, store); | ||
| expect(wrapper.find('SkeletonLoader')).toHaveLength(1); | ||
| expect(wrapper.queryByTestId('skeleton-loader')).toBeInTheDocument(); |
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.
| expect(wrapper.queryByTestId('skeleton-loader')).toBeInTheDocument(); | |
| getByTestId('skeleton-loader'); |
| expect(wrapper.container).toHaveTextContent('Klingon'); | ||
| expect(wrapper.container).toHaveTextContent('kg'); |
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.
All of the tests (not just in this file, but also elsewhere) that use .toHaveTextContent() and other replacements for Enzyme's .text() might be best replaced with getByText(), which also accepts a regexp argument.
|
|
||
| it('renders a plain translation correctly', () => { | ||
| const wrapper = createTranslation('', PLAIN_TRANSLATION); | ||
| createTranslation('', PLAIN_TRANSLATION); |
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.
| createTranslation('', PLAIN_TRANSLATION); | |
| const { getByText } = createTranslation('', PLAIN_TRANSLATION); |
|
|
||
| const gt = wrapper.find('GenericTranslation'); | ||
| expect(gt.props().content).toMatch(/^Un cheval/); | ||
| expect(screen.getByText(/^Un cheval/)).toBeInTheDocument(); |
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.
| expect(screen.getByText(/^Un cheval/)).toBeInTheDocument(); | |
| getByText(/^Un cheval/); |
| const views = Array.from( | ||
| wrapper.find('.translationform').instance().querySelectorAll('.cm-content'), | ||
| ).map((el) => el.cmView.view); | ||
| const form = wrapper.container.querySelector('.translationform'); |
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.
Wouldn't getByRole('form') work here?
| expect(container.querySelectorAll('.accesskeys button')).toHaveLength(8); | ||
| expect( | ||
| container.querySelectorAll('.accesskeys button')[0], | ||
| ).toHaveTextContent('C'); | ||
| expect( | ||
| container.querySelectorAll('.accesskeys button')[1], | ||
| ).toHaveTextContent('a'); | ||
| expect( | ||
| container.querySelectorAll('.accesskeys button')[2], | ||
| ).toHaveTextContent('n'); | ||
| expect( | ||
| container.querySelectorAll('.accesskeys button')[3], | ||
| ).toHaveTextContent('d'); | ||
| expect( | ||
| container.querySelectorAll('.accesskeys button')[4], | ||
| ).toHaveTextContent('i'); | ||
| expect( | ||
| container.querySelectorAll('.accesskeys button')[5], | ||
| ).toHaveTextContent('t'); | ||
| expect( | ||
| container.querySelectorAll('.accesskeys button')[6], | ||
| ).toHaveTextContent('e'); | ||
| expect( | ||
| container.querySelectorAll('.accesskeys button')[7], | ||
| ).toHaveTextContent('s'); |
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.
Something like this would be more legible:
| expect(container.querySelectorAll('.accesskeys button')).toHaveLength(8); | |
| expect( | |
| container.querySelectorAll('.accesskeys button')[0], | |
| ).toHaveTextContent('C'); | |
| expect( | |
| container.querySelectorAll('.accesskeys button')[1], | |
| ).toHaveTextContent('a'); | |
| expect( | |
| container.querySelectorAll('.accesskeys button')[2], | |
| ).toHaveTextContent('n'); | |
| expect( | |
| container.querySelectorAll('.accesskeys button')[3], | |
| ).toHaveTextContent('d'); | |
| expect( | |
| container.querySelectorAll('.accesskeys button')[4], | |
| ).toHaveTextContent('i'); | |
| expect( | |
| container.querySelectorAll('.accesskeys button')[5], | |
| ).toHaveTextContent('t'); | |
| expect( | |
| container.querySelectorAll('.accesskeys button')[6], | |
| ).toHaveTextContent('e'); | |
| expect( | |
| container.querySelectorAll('.accesskeys button')[7], | |
| ).toHaveTextContent('s'); | |
| const buttonTexts = Array.from( | |
| container.querySelectorAll('.accesskeys button'), | |
| button => button.textContent | |
| ); | |
| expect(buttonTexts).toMatchObject(['C', 'a', 'n', 'd', 'i', 't', 'e', 's']); |
Uh oh!
There was an error while loading. Please reload this page.