React 组件在浏览器中正确呈现,但 Jest 在呈现时测试错误:"Only a ReactOwner can have refs"
React components render correctly in browser, but Jest test errors when rendering: "Only a ReactOwner can have refs"
我在 React 中有两个组件可以很好地呈现并在浏览器中产生预期的行为,但是当 运行 通过 Jest 进行测试时似乎无法呈现。
descriptions.js
var React = require('react/addons');
var $ = require('jquery');
var Description = require('./description.js');
var Descriptions = React.createClass({
getInitialState: function () { //container always starts with at least one description field that is empty, or whatever is contained in props
var descriptions = [];
if (this.props.info == null) {
descriptions.push({num: 0, data: ''});
} else {
$.each(this.props.info, function (i, string) {
descriptions.push({num: i, data: string});
});
if (descriptions.length == 0) { //we want at least one description field at all times
descriptions.push({num: 0, data: ''});
}
}
return {descriptions: descriptions, focus: -1};
},
componentWillReceiveProps: function (nextProps) { //props are updated
var descriptions = []; //we don't care about previous values, so we will make a new list
$.each(nextProps.info, function (i, string) {
descriptions.push({num: i, data: string});
});
if (descriptions.length == 0) { //we want at least one description field at all times
descriptions.push({num: 0, data: ''});
}
this.setState({descriptions: descriptions});
},
addDescription: function (pos) { //adds a new description underneath the last one in the container
var descriptions = this.state.descriptions;
var max = 0;
$.each(descriptions, function (i, item) {
if (item.num > max) max = item.num;
});
descriptions.splice(pos + 1, 0, {num: max + 1, data: ''});
this.setState({descriptions: descriptions, focus: pos + 1}); //focus the new description
},
removeDescription: function (pos) { //remove a description from the array given its array position
var descriptions = this.state.descriptions;
if (descriptions.length != 1) {
descriptions.splice(pos, 1);
this.setState({descriptions: descriptions, focus: pos == 0 ? 0 : pos - 1});
}
},
render: function () {
var items = this.state.descriptions.map(function (item, i) { //add one Description for every item in the array
return (
<Description key={item.num} addDescription={this.addDescription}
removeDescription={this.removeDescription}
descriptionNum={i} focus={i == this.state.focus} data={item.data}/>
);
}.bind(this));
return (
<div className="descriptions">
{items}
</div>
);
}
});
module.exports = Descriptions;
本质上,该组件是一个或多个子“Description”组件的容器,需要渲染的“Description”组件的数量取决于传递的info
prop,它持有一个数组字符串。
description.js
var React = require('react/addons');
var Description = React.createClass({
mixins: [React.addons.LinkedStateMixin],
componentDidMount: function () { //focus the input if it was added after page load
if (this.props.focus) {
this.refs.descInput.getDOMNode().focus();
}
},
componentWillReceiveProps: function (nextProps) {
if (nextProps.focus) {
this.refs.descInput.getDOMNode().focus();
}
this.setState({description: nextProps.data});
},
getInitialState: function () {
return({description: this.props.data});
},
handleKeyDown: function (e) {
var key = e.keyCode;
if (key == 13) { //enter is pressed, we need to add a new line underneath this one
e.preventDefault();
this.props.addDescription(this.props.descriptionNum);
} else if (key == 8) { //backspace was pressed, check to see if line is empty and remove if so
var value = this.refs.descInput.getDOMNode().value;
if (value == null || value == '') {
e.preventDefault();
this.props.removeDescription(this.props.descriptionNum);
}
}
},
render: function () {
return (
<div className="description">
<input type="text" onKeyDown={this.handleKeyDown} valueLink={this.linkState('description')} ref="descInput"/>
</div>
)
}
});
module.exports = Description;
此组件接收一个字符串(或什么都没有),将其状态设置为包含该字符串,并在输入值发生变化时使用 LinkedStateMixin
更新状态,反之亦然。
我以为这些组件没有问题,但是下面的 Jest 测试...
描述-test.js
jest.dontMock('../js/descriptions.js');
var React = require('react/addons');
var TestUtils = React.addons.TestUtils;
describe('Descriptions', function () {
it('creates exactly two Description components when given a string array of length 2', function() {
jest.dontMock('../js/description.js');
var Description = require('../js/description.js');
var info = ['foo','bar'];
var Descriptions = require('../js/descriptions.js');
var descriptions = TestUtils.renderIntoDocument(<Descriptions info={info}/>);
var array = TestUtils.scryRenderedComponentsWithType(descriptions, Description);
expect(array.length).toEqual(2);
});
});
...失败并出现以下错误:
● Descriptions › it mocks Description exactly twice when given info array of length 2
- Error: Invariant Violation: addComponentAsRefTo(...): Only a ReactOwner can have refs. This usually means that you're trying to add a ref to a component that doesn't have an owner (that is, was not created inside of another component's `render` method). Try rendering this component inside of a new top-level component which will hold the ref.
...在这一行:
var descriptions = TestUtils.renderIntoDocument(<Descriptions info={info}/>);
这对我来说毫无意义,因为组件在浏览器中呈现良好,没有任何问题。它似乎只有在 React 的 TestUtils 尝试这样做时才会中断。
这是我的依赖项:
package.json
"dependencies": {
"jquery": "^2.1.4",
"react": "^0.13.3",
"react-tools": "^0.13.3"
},
"devDependencies": {
"browserify": "^10.2.1",
"gulp": "^3.8.11",
"gulp-react": "^3.0.1",
"gulp-shell": "^0.4.1",
"gulp-streamify": "0.0.5",
"gulp-uglify": "~1.1.0",
"jest-cli": "^0.4.5",
"node-libs-browser": "^0.5.2",
"reactify": "^1.1.1",
"vinyl-source-stream": "^1.1.0",
"watchify": "^3.2.1",
"webpack": "^1.9.10"
}
有谁知道可能导致此错误的原因吗?
将 require
移出测试函数。
jest.dontMock('../js/descriptions.js');
var React = require('react/addons');
var Description = require('../js/description.js');
var Descriptions = require('../js/descriptions.js');
describe('Descriptions', function () {
it('creates exactly two Description components when given a string array of length 2', function() {
var TestUtils = React.addons.TestUtils;
var info = ['foo','bar'];
var descriptions = TestUtils.renderIntoDocument(<Descriptions info={info}/>);
var array = TestUtils.scryRenderedComponentsWithType(descriptions, Description);
expect(array.length).toEqual(2);
});
});
我在 React 中有两个组件可以很好地呈现并在浏览器中产生预期的行为,但是当 运行 通过 Jest 进行测试时似乎无法呈现。
descriptions.js
var React = require('react/addons');
var $ = require('jquery');
var Description = require('./description.js');
var Descriptions = React.createClass({
getInitialState: function () { //container always starts with at least one description field that is empty, or whatever is contained in props
var descriptions = [];
if (this.props.info == null) {
descriptions.push({num: 0, data: ''});
} else {
$.each(this.props.info, function (i, string) {
descriptions.push({num: i, data: string});
});
if (descriptions.length == 0) { //we want at least one description field at all times
descriptions.push({num: 0, data: ''});
}
}
return {descriptions: descriptions, focus: -1};
},
componentWillReceiveProps: function (nextProps) { //props are updated
var descriptions = []; //we don't care about previous values, so we will make a new list
$.each(nextProps.info, function (i, string) {
descriptions.push({num: i, data: string});
});
if (descriptions.length == 0) { //we want at least one description field at all times
descriptions.push({num: 0, data: ''});
}
this.setState({descriptions: descriptions});
},
addDescription: function (pos) { //adds a new description underneath the last one in the container
var descriptions = this.state.descriptions;
var max = 0;
$.each(descriptions, function (i, item) {
if (item.num > max) max = item.num;
});
descriptions.splice(pos + 1, 0, {num: max + 1, data: ''});
this.setState({descriptions: descriptions, focus: pos + 1}); //focus the new description
},
removeDescription: function (pos) { //remove a description from the array given its array position
var descriptions = this.state.descriptions;
if (descriptions.length != 1) {
descriptions.splice(pos, 1);
this.setState({descriptions: descriptions, focus: pos == 0 ? 0 : pos - 1});
}
},
render: function () {
var items = this.state.descriptions.map(function (item, i) { //add one Description for every item in the array
return (
<Description key={item.num} addDescription={this.addDescription}
removeDescription={this.removeDescription}
descriptionNum={i} focus={i == this.state.focus} data={item.data}/>
);
}.bind(this));
return (
<div className="descriptions">
{items}
</div>
);
}
});
module.exports = Descriptions;
本质上,该组件是一个或多个子“Description”组件的容器,需要渲染的“Description”组件的数量取决于传递的info
prop,它持有一个数组字符串。
description.js
var React = require('react/addons');
var Description = React.createClass({
mixins: [React.addons.LinkedStateMixin],
componentDidMount: function () { //focus the input if it was added after page load
if (this.props.focus) {
this.refs.descInput.getDOMNode().focus();
}
},
componentWillReceiveProps: function (nextProps) {
if (nextProps.focus) {
this.refs.descInput.getDOMNode().focus();
}
this.setState({description: nextProps.data});
},
getInitialState: function () {
return({description: this.props.data});
},
handleKeyDown: function (e) {
var key = e.keyCode;
if (key == 13) { //enter is pressed, we need to add a new line underneath this one
e.preventDefault();
this.props.addDescription(this.props.descriptionNum);
} else if (key == 8) { //backspace was pressed, check to see if line is empty and remove if so
var value = this.refs.descInput.getDOMNode().value;
if (value == null || value == '') {
e.preventDefault();
this.props.removeDescription(this.props.descriptionNum);
}
}
},
render: function () {
return (
<div className="description">
<input type="text" onKeyDown={this.handleKeyDown} valueLink={this.linkState('description')} ref="descInput"/>
</div>
)
}
});
module.exports = Description;
此组件接收一个字符串(或什么都没有),将其状态设置为包含该字符串,并在输入值发生变化时使用 LinkedStateMixin
更新状态,反之亦然。
我以为这些组件没有问题,但是下面的 Jest 测试...
描述-test.js
jest.dontMock('../js/descriptions.js');
var React = require('react/addons');
var TestUtils = React.addons.TestUtils;
describe('Descriptions', function () {
it('creates exactly two Description components when given a string array of length 2', function() {
jest.dontMock('../js/description.js');
var Description = require('../js/description.js');
var info = ['foo','bar'];
var Descriptions = require('../js/descriptions.js');
var descriptions = TestUtils.renderIntoDocument(<Descriptions info={info}/>);
var array = TestUtils.scryRenderedComponentsWithType(descriptions, Description);
expect(array.length).toEqual(2);
});
});
...失败并出现以下错误:
● Descriptions › it mocks Description exactly twice when given info array of length 2
- Error: Invariant Violation: addComponentAsRefTo(...): Only a ReactOwner can have refs. This usually means that you're trying to add a ref to a component that doesn't have an owner (that is, was not created inside of another component's `render` method). Try rendering this component inside of a new top-level component which will hold the ref.
...在这一行:
var descriptions = TestUtils.renderIntoDocument(<Descriptions info={info}/>);
这对我来说毫无意义,因为组件在浏览器中呈现良好,没有任何问题。它似乎只有在 React 的 TestUtils 尝试这样做时才会中断。
这是我的依赖项:
package.json
"dependencies": {
"jquery": "^2.1.4",
"react": "^0.13.3",
"react-tools": "^0.13.3"
},
"devDependencies": {
"browserify": "^10.2.1",
"gulp": "^3.8.11",
"gulp-react": "^3.0.1",
"gulp-shell": "^0.4.1",
"gulp-streamify": "0.0.5",
"gulp-uglify": "~1.1.0",
"jest-cli": "^0.4.5",
"node-libs-browser": "^0.5.2",
"reactify": "^1.1.1",
"vinyl-source-stream": "^1.1.0",
"watchify": "^3.2.1",
"webpack": "^1.9.10"
}
有谁知道可能导致此错误的原因吗?
将 require
移出测试函数。
jest.dontMock('../js/descriptions.js');
var React = require('react/addons');
var Description = require('../js/description.js');
var Descriptions = require('../js/descriptions.js');
describe('Descriptions', function () {
it('creates exactly two Description components when given a string array of length 2', function() {
var TestUtils = React.addons.TestUtils;
var info = ['foo','bar'];
var descriptions = TestUtils.renderIntoDocument(<Descriptions info={info}/>);
var array = TestUtils.scryRenderedComponentsWithType(descriptions, Description);
expect(array.length).toEqual(2);
});
});