我有一个问题,我想测试一些转发的参考。我能够让这些转发的引用在我的浏览器中正常工作,但在测试环境中我看到了不同的行为。
您可以在这里浏览我的完整复制应用程序https://github.com/jasonfb/jest-playground-2
快速概览:
// 应用程序.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,有一些依赖项
// 包.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 中的代码通过使用对 DefThing 的 ref 获取其位置,并在其上获取 '.getBoundingClientRect()`,如下所示:
this.def_thing_ref.current.getBoundingClientRect()
(参见 abc_container.js #componentDidMount)
在我的浏览器中,这可以正常工作,并且current
转发的 ref 已填充
但是,在运行规范时我无法让它工作。这是我的规格
// 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}
表明 ref 工作正常
似乎在测试设置中让 Forwarded Refs 工作缺少一些东西
2019 年 12 月 13 日更新:
我在这方面取得了一点进展。我想我的测试文件中没有包含 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()
})
通过修改测试文件,我成功地使 Forwarded ref显示为填充:
但是,当我调用 getBoundingRectRect()
请拉 repo 并运行规范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********************")
}
但是,如果我将其包装在 asetTimeout
中,则 refs 似乎仍然是 refs,但它们的输出为{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 }