I am following the docs for react-testing-library to find if the element with data-testid attribute is rendered or not.
The react-testing-library is not able to find the element even though it exists.
TEST
test('Renders step based on the active step', () => {
render(<RenderStep />, { initialState: { implOnboard: initialState } });
});
expect(screen.getByTestId('step-1')).toBeDefined(); // 👉 THROWS ERROR ❌
}
COMPONENT
// redux
const { activeStep } = useSelector((state) => state.implOnboard);
const renderStep = () => {
switch (activeStep) {
case 1:
console.log('Rendering this 🔥'); // 👈 THIS IS GETTING LOGGED TOO!
return (
<div data-testid="step-1">
<StepOne />
</div>
);
case 2:
return (
<div data-testid="step-2">
<StepTwo />
</div>
);
}
};
return <React.Fragment>{renderStep()}</React.Fragment>;
ERROR
TestingLibraryElementError: Unable to find an element by: [data-testid="step-1"]
Please use the queryByTestId instead getByTestId for the case. It will work.
The answer from Elena is wrong and just masks the error. Using queryByTestId will return null if the element is not found, instead of an error when using getByTestId, and the assertion will actually be:
expect(null).toBeDefined();
and this WILL pass. This is not testing anything. toBeDefined() fails only if the element is equal to undefined and it passes for everything else.
If OP wants to actually check if that element is NOT present they should do:
expect(screen.queryByTestId('step-1')).not.toBeInTheDocument();
The original error from OP is probably happening because the expect is outside the test block.
Adding to Elena's response, we need to use queryByTestId because, queryByTestId returns null value if element is not found instead of throwing error unlike getByTestId. Here's the image that explains when to use different methods.
Sometimes you get an error even when using this queryBy, so please check other code before this statement.
Reference - https://www.youtube.com/watch?v=Yghw9FkNGsc&list=PL4cUxeGkcC9gm4_-5UsNmLqMosM-dzuvQ&index=5&ab_channel=TheNetNinja
For those using React Native Web, you will need to use testID which will compile down to data-testid on the element. It needs to be on a react native element not your custom one (pass it through as a prop and put it on a View, etc.).
Related
I am using react jest with react testing library to test my component.
I am facing a weird issue. I am usng debug return by render from testing-library.
test('component should work', async () => {
const { findByText, debug } = render(<MyComponent />);
const myElement = await findByText(/someText/i);
debug();
});
As you can see in the screenshot there are incomplete dev and closing tags for parents are missing.
You need to change the value of DEBUG_PRINT_LIMIT env variable (default is 7000).
For example, run your tests with: DEBUG_PRINT_LIMIT=10000 yarn test
Source: https://github.com/testing-library/dom-testing-library/blob/master/src/pretty-dom.js#L33
I am using this version: "#testing-library/react": "^11.0.4"
able to do just like below, we can change 300000 as the max length of output.
debug(undefined, 300000)
Another option
screen.debug(undefined, Infinity);
The second argument of the debug() function can be used to set maxLengthToPrint.
E.g. to print everything in myElement using the recommended screen approach:
import {render, screen} from '#testing-library/react'
render(<MyComponent />);
const myElement = await screen.findByText(/someText/i);
const maxLengthToPrint = 100000
screen.debug(myElement, maxLengthToPrint);
See:
Api docs for debug() in render results
Api docs for screen.debug()
You can use Jest Preview (https://github.com/nvh95/jest-preview) to view your app UI when testing in a browser, instead of HTML in the terminal, just by 2 lines of code:
import { debug } from 'jest-preview';
describe('App', () => {
it('should work as expected', () => {
render(<App />);
debug();
});
});
It works great with jest and react-testing-library.
Introduction: https://www.jest-preview.com/docs/getting-started/intro
Installation guide: https://www.jest-preview.com/docs/getting-started/installation
If none of the other answers work for you, make sure it's not your terminal limit. For example VS Code only keeps 5000 lines in buffer. Try Mac OS terminal.
This worked for me:
debug(undefined, 300000);
It will give you the markeup of whatever you passed into render() as:
import {render, screen} from '#testing-library/react'
render(<MyComponent />);
You can read about more ways to help you with printing out the results, including prettifying the resulting markup at:
API doc for debug
This worked for me
const history = createMemoryHistory()
const { debug } = renderWithRedux(
<Router history={history}>
<SideBar />
</Router>
, state);
screen.debug(debug(), 20000);
Since the DOM size can get really large, you can set the limit of DOM content to be printed via environment variable DEBUG_PRINT_LIMIT. The default value is 7000. You will see ... in the console, when the DOM content is stripped off, because of the length you have set or due to default size limit. Here's how you might increase this limit when running tests:
DEBUG_PRINT_LIMIT=10000 npm test
Here more about debuggin on documentation
Adding on top of answer by #Haryono
Also make sure you don't have silent flag set in scripts
"test": "jest --config jest.config.js --silent";
Removing silent flag should work.
Note: I think silent flag supresses warnings and debug outputs
Also be sure your terminal allows you to scroll back that far. In iTerm2, I had my "Scrollback lines" set to 1000. Changed it to "Unlimited scrollback" and how life is good iTerm2:
By default RTL doesn't show comments, <script /> and <style /> tags. In my case I needed to test for a commented node in the DOM.
If you want your tests to include all the nodes, you can use prettyDOM like this:
// render DOM with a commented node
const html = {__html: '<!--this is a commented DOM element-->'};
const element = <div dangerouslySetInnerHTML={html} />;
const { container } = render(element);
// This is what tells RLT to print all nodes!
const prettyfiedDOM = prettyDOM(container, undefined, { filterNode: () => true}) || '';
expect(prettyfiedDOM.includes('<!--this is a commented DOM element-->')).toBeTruthy();
Notice that the filterNode function always returns true, which tells RTL to print all DOM nodes, and hence you can test comments, styles, tags, etc. You can read more on prettyDOM source code and configure your desired behavior of DOM filtering.
View the live demo here
Hope that helps!
I am trying to test a component whose output depends on a property, ie: props.editable == 'true' : render input otherwise render div
I have tried doing:
expect(wrapper.first().type()).toBe('div')
but instead I get [Function: ComponentName]
Here is my code:
ComponentFile.js
function ComponentName(props) {
if (props.editable) return <input />
else return <div />
}
ComponentFile.test.js
it('should render a div if not editable', () => {
const wrapper = mount(<ComponentName editable={false} />
expect(wrapper.first().type()).toBe('div')
})
The above test fails because it gets [Function: ComponentName]
I have also tried wrapper.getElement().type but it is also a funcion.
I was able to temporarily get the results I want by doing:
expect(wrapper.html().substr(0, 4)).toBe('<div')
but I was hoping there was a more idiomatic way
EDIT:
Solved. Turns out it works if you use shallow instead of mount.
I think you just miss typed there. You should use .to.equal instead of toBe:
expect(wrapper.first().type()).toEqual('div')
Reference:
https://airbnb.io/enzyme/docs/api/ShallowWrapper/type.html
P.S.:
This will work only if you use shallow instead of mount, to render your component. Not sure why...
Update:
Matt comments above have a good answer with an example. Posting here for visibility:
https://codesandbox.io/s/function-testing-6sxt5
I have a use-case where a React ref makes sense.
I've tried a few different ways of implementing them, and in this case integrating them with hammerjs.
I'm mostly going off of this question:
adding hammerjs to a react js component properly
My return method in my render is as such:
return (
<div className={"App card-row card-color " + this.props.className} ref={
(el) => this._slider = el
}>
{this.state.bubblemsg ? (
<NotifBubble message={this.state.bubblemsg} merchant={this.props.merchant.merchant}/>
) : (
null
)}
<ScrollMenu
data={this.state.list}
inertiaScrolling={true}
transition={.1}
inertiaScrollingSlowdown={.000001}
/>
</div>
);
Which I would think would attach my div element as a reference.
In my componentDidMount() method, I am then attaching it to hammer:
componentDidMount() {
this.hammer = Hammer(this._slider)
this.hammer.on('swipeleft', console.log("swipe left"));
this.hammer.on('swiperight', console.log("swipe right"));
}
However, I am getting the error:
TypeError: Cannot read property 'addEventListener' of undefined
And this is directly related to Hammer, and thus the reference I assume.
So what am I doing wrong with my references? I don't totally understand how they're supposed to work and the React tutorial explanation wasn't super clear, so a thorough explanation would be useful.
I think the problem is that in the listening of the hammer you have to pass a function to be called, try inserting an arrow function to log
componentDidMount() {
this.hammer = Hammer(this._slider)
this.hammer.on('swipeleft', () => console.log("swipe left"));
this.hammer.on('swiperight', () => console.log("swipe right"));
}
I have a component library that I'm writing unit tests for using Jest and react-testing-library. Based on certain props or events I want to verify that certain elements aren't being rendered.
getByText, getByTestId, etc throw and error in react-testing-library if the element isn't found causing the test to fail before the expect function fires.
How do you test for something not existing in jest using react-testing-library?
From DOM Testing-library Docs - Appearance and Disappearance
Asserting elements are not present
The standard getBy methods throw an error when they can't find an element, so
if you want to make an assertion that an element is not present in the DOM,
you can use queryBy APIs instead:
const submitButton = screen.queryByText('submit')
expect(submitButton).toBeNull() // it doesn't exist
The queryAll APIs version return an array of matching nodes. The length of the
array can be useful for assertions after elements are added or removed from the
DOM.
const submitButtons = screen.queryAllByText('submit')
expect(submitButtons).toHaveLength(2) // expect 2 elements
not.toBeInTheDocument
The jest-dom utility library provides the
.toBeInTheDocument() matcher, which can be used to assert that an element is
in the body of the document, or not. This can be more meaningful than asserting
a query result is null.
import '#testing-library/jest-dom/extend-expect'
// use `queryBy` to avoid throwing an error with `getBy`
const submitButton = screen.queryByText('submit')
expect(submitButton).not.toBeInTheDocument()
Use queryBy / queryAllBy.
As you say, getBy* and getAllBy* throw an error if nothing is found.
However, the equivalent methods queryBy* and queryAllBy* instead return null or []:
queryBy
queryBy* queries return the first matching node for a query, and return null if no elements match. This is useful for asserting an element that is not present. This throws if more than one match is found (use queryAllBy instead).
queryAllBy
queryAllBy* queries return an array of all matching nodes for a query, and return an empty array ([]) if no elements match.
https://testing-library.com/docs/dom-testing-library/api-queries#queryby
So for the specific two you mentioned, you'd instead use queryByText and queryByTestId, but these work for all queries, not just those two.
getBy* throws an error when not finding an elements, so you can check for that
expect(() => getByText('your text')).toThrow('Unable to find an element');
const submitButton = screen.queryByText('submit')
expect(submitButton).toBeNull() // it doesn't exist
expect(submitButton).not.toBeNull() // it exist
You have to use queryByTestId instead of getByTestId.
Here a code example where i want to test if the component with "car" id isn't existing.
describe('And there is no car', () => {
it('Should not display car mark', () => {
const props = {
...defaultProps,
base: null,
}
const { queryByTestId } = render(
<IntlProvider locale="fr" messages={fr}>
<CarContainer{...props} />
</IntlProvider>,
);
expect(queryByTestId(/car/)).toBeNull();
});
});
Worked out for me (if you want to use getByTestId):
expect(() => getByTestId('time-label')).toThrow()
Hope this will be helpfull
this table shows why/when function errors
which functions are asynchronous
what is return statement for function
Another solution: you could also use a try/catch block
expect.assertions(1)
try {
// if the element is found, the following expect will fail the test
expect(getByTestId('your-test-id')).not.toBeVisible();
} catch (error) {
// otherwise, the expect will throw, and the following expect will pass the test
expect(true).toBeTruthy();
}
You can use react-native-testing-library "getAllByType" and then check to see if the component is null. Has the advantage of not having to set TestID, also should work with third party components
it('should contain Customer component', () => {
const component = render(<Details/>);
const customerComponent = component.getAllByType(Customer);
expect(customerComponent).not.toBeNull();
});
// check if modal can be open
const openModalBtn = await screen.findByTestId("open-modal-btn");
fireEvent.click(openModalBtn);
expect(
await screen.findByTestId(`title-modal`)
).toBeInTheDocument();
// check if modal can be close
const closeModalBtn = await screen.findByTestId(
"close-modal-btn"
);
fireEvent.click(closeModalBtn);
const sleep = (ms: number) => {
return new Promise((resolve) => setTimeout(resolve, ms));
};
await sleep(500);
expect(screen.queryByTestId("title-modal")).toBeNull();
I recently wrote a method to check visibility of element for a jest cucumber project.
Hope it is useful.
public async checknotVisibility(page:Page,location:string) :Promise<void>
{
const element = await page.waitForSelector(location);
expect(element).not.toBe(location);
}
don't want to bury the lead, so here's the right solution ✅
waitFor(() => queryByTestId(/car/) === null)
There are issues with all of the answers here so far...
don't use getByTestId, that'll have to wait 😴 for the timeout because it's expecting the element to eventually be there. Then it'll throw and you'll have to catch that, which is a less readable test. Finally you could have a RACE CONDITION 🚫 where getByTestId is evaluated before the element disappears and our test will flake.
Just using queryByTestId without waitFor is a problem if the page is changing at all and the element has not disappeared yet. RACE CONDITION 🚫
deleteCarButton.click();
expect(queryByTestId(/car/)).toBeNull(); //
if expect() gets evaluated before the click handler and render completes we'll have a bad time.
The default behavior of queryByRole is to find exactly one element. If not, it throws an error. So if you catch an error, this means the current query finds 0 element
expect(
()=>screen.getByRole('button')
).toThrow()
getByRole returns 'null', if it does not find anthing
expect(screen.queryByRole('button')).toEqual((null))
findByRole runs asynchronously, so it returns a Promise. If it does not find an element, it rejects the promise. If you are using this, you need to run async callback
test("testing", async () => {
let nonExist = false;
try {
await screen.findByRole("button");
} catch (error) {
nonExist = true;
}
expect(nonExist).toEqual(true);
});
I'm using elements with custom tag names in React and getting a wall of these errors. There's a GitHub issue on the subject (https://github.com/hyperfuse/react-anime/issues/33) in which someone states:
This is a new warning message in React 16. Has nothing to do with anime/react-anime and this warning can safely be ignored.
It's nice that it can be safely ignored, but it's still not going to pass scrutiny when my code is filling the console with useless error messages.
How can I suppress these warnings?
I found a potential fix for this issue - if you are using a plugin (and potentially in other circumstances) you can use the is attribute.
Found here when working with X3d - simply writing <scene is="x3d" .../> works
Update:
see the answer from #triple with the correct solution:
https://stackoverflow.com/a/55537927/1483006
Orignal:
I'm not saying this a correct thing you should really do, but you could hook console.error and filter this message by putting this somewhere before react-anime is loaded:
const realError = console.error;
console.error = (...x) => {
// debugger;
if (x[0] === 'Warning: The tag <g> is unrecognized in this browser. If you meant to render a React component, start its name with an uppercase letter.') {
return;
}
realError(...x);
};
It seemed to work on the sample that was posted in the GitHub issue you linked at least. :3
My solution was to create envelope component which renders <div> with desired classes:
import React, {Component, DetailedHTMLFactory, HTMLAttributes} from "react";
import classNames from "classnames";
export default class SimpleTagComponent extends Component<SimplePropTypes>{
baseClassName = 'simpleComponent'
render() {
return React.createElement(
'div',
{
...this.props,
className: classNames(this.baseClassName, this.props.className),
},
this.props.children
);
}
}
type SimplePropTypes = HTMLAttributes<HTMLDivElement>
export class MyTag extends SimpleTagComponent {
baseClassName = 'my'
}
I don't believe there's a built in way to suppress the error message.
The warning message is logged right here in react-dom. You could fork react-dom and simply remove the error message. For a change as small as this, perhaps using something like patch-package would be useful, so you don't have to maintain a fork.
React 16 gives warnings with x3dom components .
including is="x3d" in component suppresses these warnings.
I had the same error. My problem was the new file for js when I use sfc I first letter of the name (tagname) has to be capital letter. I am just new so, I didn't notice it. But I am writing this just in case
I wrapped my HTML in the <svg> tag.
https://github.com/facebook/react/issues/16135:
I think you're probably rendering them outside of <svg> tags.
This is what got rid of the Web Browser Warnings for me:
BAD:
const SocialMedia = (props) => {
<socialmedia>
return (
Good:
const SocialMedia = (props) => {
return (