Has anyone experience with setting up tests with @testing-library/react
and @testing-library/user-event
?
I am struggling to setup the thing correctly with mocha
and puppeteer
.
PS: Actually managed to get it to work with user-event
v13. Could not get v14-beta to work well in server mode. And running client tests separately from server tests with v13 (by placing the file under /client
) led to strange bugs. Maybe I need to use --full-app
for these integration tests I am trying to put in place. Will see.
After some fiddling about it became apparent I had some fundamental misunderstanding on how the testing environment works. My confusion was partly reinforced by the inability to import user-event
v14-beta on the server while v13 can be imported just fine.
What I get from my attempts so far is:
- You can go very far by writing
--full-app
tests without the need of external libraries. For instance there is no real need for me to use @testing-library/react
, the entire app can be spawned for tests without that. Though @testing-library/user-event
is useful to simulate real user interaction with the app.
- Although you write tests under
Meteor.isServer/isClient
conditionals, hoisted import statement get executed in both environments if your tests are isomorphic. If those import statements break when for instance global.window
does not exist, it will break in the server environment.
- In normal mode (without
--full-app
) NOTHING will be setup aside from the MongoDB process and connection. You get exactly what you import. However, tests are not isolated so your stuff could be working only because some other test file imports modules that cause side-effects you rely on.
- There is no supported way to run isolated tests in parallel in a real environment.
- Even if you configure
mainModule
the testing file eager loading feature will fail to load files having a client
ancestor on their path in server mode and vice versa (which kind of makes sense, would probably work by setting testModule
but then you lose eager loading).
1 Like
To conclude this thread here is how simple your client --full-app
tests can look like with @testing-library/dom@8.11.2
and @testing-library/user-event@14.0.0-beta.7
.
import {within, waitForElementToBeRemoved} from '@testing-library/dom';
import userEvent from '@testing-library/user-event';
const setupApp = () => {
return {
user: userEvent.setup(),
...within(document.body),
};
};
describe(__filename, () => {
it('should allow to register a new user', async () => {
const username = 'test';
const password = 'test;
const {getByRole, findByRole, getByLabelText, user} = setupApp();
await user.click(await findByRole('button', {name: 'Sign in'}));
await user.click(await findByRole('button', {name: 'Create account?'}));
await findByRole('button', {name: 'Register'});
await user.type(getByLabelText('Username'), username);
await user.type(getByLabelText('Password'), password);
await user.click(getByRole('button', {name: 'Register'}));
await waitForElementToBeRemoved(() => {
return getByRole('button', {name: 'Register'});
});
await user.click(
await findByRole('button', {name: `Logged in as ${username}`}),
);
});
});
Of course you need to add some magic to reset the App and database after each test if you plan on writing more than one test.
If you really need to put this in an isomorphic test file, some adjustments are necessary:
import {within, waitForElementToBeRemoved} from '@testing-library/dom';
if (Meteor.isClient) {
const setupApp = async () => {
const {default: userEvent} = await import('@testing-library/user-event');
return {
user: userEvent.setup(),
...within(document.body),
};
};
describe(__filename, () => {
it('should allow to register a new user', async () => {
...
const {getByRole, findByRole, getByLabelText, user} = await setupApp();
...
});
});
}