0

我有一个问题,我想测试一些转发的参考。我能够让这些转发的引用在我的浏览器中正常工作,但在测试环境中我看到了不同的行为。

您可以在这里浏览我的完整复制应用程序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 }

4

0 回答 0