react-testing-library version: 4.0.1react version: 16.3.2node version: 10.0npm (or yarn) version: yarn 1.7.0const { container } = render(
<SampleClass title="foo" />
);
const button = container.querySelector('button');
fireEvent.click(button);
I'm still relatively new to TypeScript but I believe strict null checks are causing issues with this library.
Typescript Error on the button passed to fireEvent.click
Error Message:
Argument of type 'HTMLButtonElement | null' is not assignable to parameter of type 'HTMLElement'. Type 'null' is not assignable to type 'HTMLElement'.
Extend Types to support null or specific HTML Elements
Hrm, the querySelector returned null (could not find button element). So it wouldn't be possible to fire the click event on it.
An option could be to test for the button exists first and then try to click it:
const { container } = render(
<SampleClass title="foo" />
);
const button = container.querySelector('button');
expect(button).not.toBe(null);
if (button !== null) {
fireEvent.click(button);
}
Here is an example of it in action with create-react-app-typescript:
https://codesandbox.io/s/ryl4pyrlyp
@smacpherson64 Thanks for chiming in. The SampleClass does have a button in the React render method and the actual test passes because the querySelector finds the button.
I can use expect(button).not.toBe(null); but the same typescript error still occurs here fireEvent.click(button); on the next line of your sample.
It looks like I can add a if check, TypeScript removes the error.:
if (button) {
fireEvent.click(button);
}
Is this the recommended approach or can we allow null so I don't have to wrap the simulated click in the if statement?
Hrm, I am not sure if it is the recommended approach. I will do my best to explain why it happens and you can decide what you think best.
querySelector returns an HTMLElement or null (if not found)fireEvent.click requires an HTMLElementquerySelector to fireEvent.click because the types don't match and querySelector could be nullif check validates, at compile time, that the querySelector response is not null.Hope this helps!
@ssylvia, by the way, I am not a maintainer, just a random person trying to help. If you still want to pursue the request to have the Types for fireEvent updated, it might be better to put the request in dom-testing-library as that's where the fireEvent comes from. https://github.com/kentcdodds/dom-testing-library/issues
Thank you for your help @smacpherson64!
@ssylvia, the best alternative would be to avoid querySelector and instead use one of the getBy* queries which always return an HTMLElement. Is there a reason you're not doing that?
@kentcdodds We're setting up a new app running CRA (TypeScript) and looking for the best approach for testing components.
I think there will times when we don't want to have data-testid in production and since we're not using babel we can't use babel-plugin-react-remove-properties.
The getBy* doesn't have a general getBySelector method. If I know a component always has a button, or a button with class, the selector seems like a better approach to test. That way we don't have to update the test if we change the button text.
Gotcha. I'm afraid that the data-testid is the least-optimal query that this library will provide, anything else causes tests to be too brittle. If none of the other queries work for you, then perhaps you could create your own getBySelector function and use your own utility in a test utility module for your project.
@ssylvia You can use non-null assertion operator in this case.
const button = container.querySelector('button');
fireEvent.click(button!);
I would avoid conditional statements in tests only to satisfy the typechecker. It may lead to tricky mistakes. E.g. in your example, when you forget the assertion expect(button).not.toBe(null);, then the test can silently pass for a non-existent button.
The only way I've been able to get container.querySelector to work with TypeScript is by using the JSX type casting as HTMLElement at the end of the line.
I don't think there's anything we'll do within the package to solve this and it looks like there are some workarounds, so I'm going to go ahead and close this issue. Thanks!
Most helpful comment
@ssylvia You can use non-null assertion operator in this case.
I would avoid conditional statements in tests only to satisfy the typechecker. It may lead to tricky mistakes. E.g. in your example, when you forget the assertion
expect(button).not.toBe(null);, then the test can silently pass for a non-existent button.