I have what I think is a general question about working with DOM elements in React Testing library.
Essentially, I have a couple of nested components, one of which creates a React ref that is forwarded down to some child components (code below).
When it does this, it then renders the child components and checks the nested Ref to call .getBoundingClientRect() a magical native Javascript function that gives me the x and y positioning, the width & height, and top, bottom, left & right bounds. I really need this information because I want to do some other things with it, without directly modifying the DOM (I am making all my DOM modifications using setState and letting React re-render the DOM)
In development (Chrome), when I do console.log(this.thing_one_ref.current.getBoundingClientRect()) I get this result:

(you'll note that thing_one_ref here is correctly populated once my app renders-- not before-- and looks like:)

I have a simple spec for this code, which happens to pass, but even though it passes, the console logged output when in the test environment looks like this:

Notice that all of the values are 0 instead of what I am expecting, which is the element to be rendered as-if it were on a real DOM
// src/App.test.js
import React from 'react';
import { render, wait } from '@testing-library/react';
import App from './App';
test('renders learn react link', async () => {
const { getByText, getByTestId } = render(<App />);
const linkElement = getByTestId("app")
await wait(() => expect(getByText(/A Test of refs/i, linkElement)).toBeInTheDocument());
});
// src/App.js
import React from 'react';
import './App.css';
import styled from 'styled-components'
import ThingOne from './thing_one'
import Container from './container'
const StyledApp = styled.div`
position: relative;
height: 100vh;
width: 100%;
`
function App() {
return (
<StyledApp data-testid="app" className="App" style={{position: 'relative'}}>
<Container />
</StyledApp>
);
}
export default App;
// src/container.js
import React from 'react'
import styled from 'styled-components'
import ThingOne from "./thing_one";
const StyledContainer = styled.div`
display: block;
`
class Container extends React.Component {
constructor(props) {
super(props)
this.thing_one_ref = React.createRef()
this.state = {
message: ""
}
}
componentDidMount() {
setTimeout(() => {
this.setState({message: "A test of refs"})
// here's the console.log
console.log(this.thing_one_ref.current.getBoundingClientRect())
}, 1000)
}
render() {
const {message} = this.state
return (
<StyledContainer>
{message}
<ThingOne ref={this.thing_one_ref}/>
</StyledContainer>
)
}
}
export default Container
// src/thing_one.js
import React from 'react'
import styled from 'styled-components'
const StyledThingOne = styled.div`
display: block;
width: 100px;
height: 100px;
position: relative;
top: 20%;
left: 20%;
border: solid 1px black;
margin: 20px;
`
const ThingOne = React.forwardRef((props, ref) => (
<StyledThingOne ref={ref}></StyledThingOne>
));
export default ThingOne
How do I get the test environment to behave as-if it is mounted in a real DOM, complete with a cartesian plane that I can use for trigonometry and animation? I have some working code that depends on the result of getBoundingClientRect() looking something like this result (as said above, work in browser)
DOMRect {x: 225.1875, y: 38, width: 102, height: 102, top: 38, …}
complete source code can be referenced here:
https://github.com/jasonfb/jest-playground-3
I am not sure if this goes beyond your current requirements, but you may consider running your test suites on an actual browser (real DOM environment), rather than jsdom, which is used by Jest to simulate a browser-like environment on Node.JS.
You may consider using test frameworks like Selenium and Cypress to carry out tests on supported browsers such as Chrome. This will allow you to carry out Integration testing (what you are trying to achieve), and in the future, End to End testing on the browser. This will eliminate the need to mock/stub/simulate DOM elements on Node.JS.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With