[Solved] Mysterious errors with @testing-library/user-event@v14-beta.7

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();
		...
	});
});

}