我有一个要测试一些转发引用的问题。我可以使这些转发的引用在我的浏览器中正常工作,但是在测试环境中,我看到了不同的行为。
您可以在https://github.com/jasonfb/jest-playground-2
处浏览我完整的复制应用程序快速概述:
// App.js
import React from 'react';
import './App.css';
import AbcContainer from './abc_container'
function App() {
return (
<div className="App">
<AbcContainer />
</div>
);
}
export default App;
// abc_container.js
import React from 'react'
import DefThing from './def_thing'
import styled from 'styled-components'
const StyledAbcContainer = styled.div`
display: block;
width: 80%;
height: 80%;
margin-left: auto;
margin-right: auto;
border: solid 1px red;
padding: 15px;
`
class AbcContainer extends React.Component {
constructor(props) {
super()
this.def_thing_ref = React.createRef()
this.state = {
visible: false,
message: "wait for it..."
}
}
componentDidMount() {
setTimeout(() => {
console.log("this.def_thing_ref= ", this.def_thing_ref)
let rect = this.def_thing_ref.current.getBoundingClientRect()
console.log("the obejct's rect is ", rect)
this.setState({visible: true,
message: "the position of the box is " + rect.x + "," + rect.y +
"I know this because the ref for this.def_thing_ref.current is populated"
})
}, 2500)
}
render() {
const {visible, message} = this.state
return (
<StyledAbcContainer data-testid="abc-container">
StyledAbcContainer
<DefThing ref={this.def_thing_ref} visible={visible}/>
{message}
</StyledAbcContainer>
)
}
}
export default AbcContainer
// def_thing.js
import React, {Component} from 'react'
import styled from "styled-components";
const StyledDefThing = styled.div`
position: relative;
height: 110px;
width: 136px;
border: solid 1px green;
display: block;
`
class DefThing extends React.Component {
render () {
const {selfRef, visible} = this.props
return (
<StyledDefThing ref={selfRef} style={{'visibility': visible ? 'visible' : 'hidden'}}>
StyledDefThing
</StyledDefThing>
)
}
}
export default React.forwardRef((props, ref) => <DefThing {...props} selfRef={ref} />)
除此之外,这是基本的create-react-app,具有一些依赖关系
// package.json
{
"name": "jest-playground-2",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/dom": "^6.10.1",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.3.3",
"@testing-library/user-event": "^7.1.2",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-scripts": "3.3.0",
"styled-components": "^4.4.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"jest": {
"coverageReporters": [
"html",
"text"
]
}
}
您可以在https://github.com/jasonfb/jest-playground-2
处浏览我完整的复制应用程序在开发中,在我的浏览器中,转发的ref起作用。 2.5秒(任意)后,AbcContainer中的代码通过使用ref来获取DefThing的位置,并在其上获取'.getBoundingClientRect()`,如下所示:
this.def_thing_ref.current.getBoundingClientRect()
(请参阅abc_container.js #componentDidMount)
在我的浏览器中,它可以正常工作,并且已转发引用的current
已填充
但是,在运行规格时,我无法使其正常工作。这是我的规格
// abc_container.test.js
import React from 'react';
import { render, wait, waitForElement } from '@testing-library/react';
import App from './App';
import AbcContainer from "./abc_container";
test('it is able to render', () => {
const {getByTestId} = render(<AbcContainer/>)
const abcContainterElement = getByTestId('abc-container')
expect(abcContainterElement).toBeInTheDocument()
})
test('after 1 second, the ref is created', async () => {
const {getByText, getByTestId} = render(<AbcContainer/>)
const abcContainerElement = getByTestId('abc-container')
await wait(() => getByText(/the position of the box/i))
const words = getByText(/the position of the box/i)
expect(words).toBeInTheDocument()
})
// App.test.js
import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
test('renders learn react link', () => {
const { getByText } = render(<App />);
const styledDefThingElement = getByText(/StyledDefThing/i);
expect(styledDefThingElement).toBeInTheDocument();
});
// def_thing.test.js
import React from 'react';
import { render } from '@testing-library/react';
import App from './App';
import DefThing from './def_thing'
test('renders a box and expects a ref', () => {
const fakeRef = React.createRef()
const { getByText } = render(<DefThing ref={fakeRef}/>);
});
不幸的是,我在abc_container中遇到此失败,这证明了问题:
FAIL src/abc_container.test.js (7.19s)
● Console
console.log src/abc_container.js:32
this.def_thing_ref= { current: null }
console.error node_modules/@testing-library/react/dist/act-compat.js:52
Error: Uncaught [TypeError: Cannot read property 'getBoundingClientRect' of null]
at reportException (/Users/jason/Work/LEARNING/React_JS/jest-playground-2/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:66:24)
at Timeout.callback [as _onTimeout] (/Users/jason/Work/LEARNING/React_JS/jest-playground-2/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/browser/Window.js:680:7)
at listOnTimeout (timers.js:327:15)
at processTimers (timers.js:271:5) TypeError: Cannot read property 'getBoundingClientRect' of null
at setTimeout (/Users/jason/Work/LEARNING/React_JS/jest-playground-2/src/abc_container.js:33:45)
at Timeout.callback [as _onTimeout] (/Users/jason/Work/LEARNING/React_JS/jest-playground-2/node_modules/jest-environment-jsdom/node_modules/jsdom/lib/jsdom/browser/Window.js:678:19)
at listOnTimeout (timers.js:327:15)
at processTimers (timers.js:271:5)
● after 1 second, the ref is created
如您所见,this.def_thing_ref
返回的{ current: null }
不应返回。在实际的浏览器中,它返回{current: div.sc-bVVdaja.hwwsLL}
,指示引用正确无误
似乎在使“转发的引用”在测试设置中工作时缺少一些东西
更新12/13/2019:
我在这方面取得了一些进展。我认为我的测试文件中没有包含ReactDOM。我的新测试文件看起来像
// def_thing.test.js
import React from 'react';
import ReactDOM from 'react-dom';
import { render } from '@testing-library/react';
import { getByTestId, getByText } from '@testing-library/dom'
import App from './App';
import DefThing from './def_thing'
test('renders a box and expects a ref', () => {
const div = document.createElement('div');
const fakeRef = React.createRef()
const { getByText } = render(<DefThing ref={fakeRef}/>, div);
});
/// abc_container.test.js
import React from 'react';
import ReactDOM from 'react-dom';
import { render, wait, waitForElement } from '@testing-library/react';
import { getByTestId, getByText } from '@testing-library/dom'
import App from './App';
import AbcContainer from "./abc_container";
test('it is able to render', () => {
const div = document.createElement('div');
const {getByTestId} = render(<AbcContainer/>, div)
const abcContainterElement = getByTestId('abc-container')
expect(abcContainterElement).toBeInTheDocument()
})
test('after 1 second, the ref is created', async () => {
const div = document.createElement('div');
const {getByText, getByTestId} = render(<AbcContainer/>, div)
const abcContainerElement = getByTestId('abc-container')
await wait(() => getByText(/the position of the box is 169.5,134/i))
const words = getByText(/the position of the box is 169.5,134/i)
expect(words).toBeInTheDocument()
})
通过修改测试文件,我可以成功地将“转发的引用”显示为 :
但是,当我调用getBoundingRectRect()
请提取存储库并使用yarn test --watchAll --coverage
更新2019-12-13 @ 13-23EST:
基本上现在发生的是,我认为这已被归结为setTimeout的问题
由于某种原因,我看不到,当我从componentDidMount
中立即检查对象时,我看到正确填充了引用(现在是def_ref和abc_ref):
def_thing_ref:
{ current:
HTMLDivElement {
/// abc_container.js
componentDidMount() {
console.log("componentDidMount... this= ", this) // when examined this way, the def_thing_ref and abc_ref are coming out correctly populated
if (this.def_thing_ref.current) {
let rect = this.def_thing_ref.current.getBoundingClientRect()
console.log("the obejct's rect is ", rect)
this.setState({
visible: true,
message: "the position of the box is " + rect.x + "," + rect.y +
"....I know this because the ref for this.def_thing_ref.current is populated"
})
} else {
console.log(".....***** no current ref object********************")
}
但是,如果我将其包装在setTimeout
中,则参照似乎仍然是参照,但它们以{current: null}
的形式出现
当修改为位于setTimeout
之内时,代码如下:
// abc_container.js
componentDidMount() {
setTimeout(() => {
console.log("TIMEOUT componentDidMount... this= ", this)
if (this.def_thing_ref.current) {
let rect = this.def_thing_ref.current.getBoundingClientRect()
console.log("the obejct's rect is ", rect)
this.setState({
visible: true,
message: "the position of the box is " + rect.x + "," + rect.y +
"....I know this because the ref for this.def_thing_ref.current is populated"
})
} else {
console.log(".....***** no current ref object********************")
}
}, 2500)
}
在这里它产生:
TIMEOUT componentDidMount... this= AbcContainer {
//
def_thing_ref: { current: null },
abc_ref: { current: null },
如您所见,经过2.5秒的超时检查后,Refs对象似乎恢复(?)为{current:null}