From 0359fd2e5ca31ebe9b71595b985b4588b4382b0f Mon Sep 17 00:00:00 2001 From: Rafael de Oleza Date: Tue, 2 Aug 2016 16:40:47 -0700 Subject: [PATCH] [RFC] Proposal for ReactTestRenderer selector API --- src/renderers/testing/ReactTestMount.js | 12 ++ src/renderers/testing/ReactTestRenderer.js | 69 ++++++++++ .../__tests__/ReactTestRenderer-test.js | 130 ++++++++++++++++++ 3 files changed, 211 insertions(+) diff --git a/src/renderers/testing/ReactTestMount.js b/src/renderers/testing/ReactTestMount.js index a3f2e61aff7..89f964b7b2c 100644 --- a/src/renderers/testing/ReactTestMount.js +++ b/src/renderers/testing/ReactTestMount.js @@ -130,6 +130,18 @@ ReactTestInstance.prototype.toJSON = function() { var inst = getHostComponentFromComposite(this._component); return inst.toJSON(); }; +ReactTestInstance.prototype.find = function(selector) { + var inst = getHostComponentFromComposite(this._component); + return inst.find(selector, false); +}; +ReactTestInstance.prototype.findAll = function(selector) { + var inst = getHostComponentFromComposite(this._component); + return inst.findAll(selector); +}; +ReactTestInstance.prototype.findByProps = function(props) { + var inst = getHostComponentFromComposite(this._component); + return inst.findByProps(props); +}; /** * As soon as `ReactMount` is refactored to not rely on the DOM, we can share diff --git a/src/renderers/testing/ReactTestRenderer.js b/src/renderers/testing/ReactTestRenderer.js index b2d18464629..f82839d2c09 100644 --- a/src/renderers/testing/ReactTestRenderer.js +++ b/src/renderers/testing/ReactTestRenderer.js @@ -90,6 +90,66 @@ ReactTestComponent.prototype.toJSON = function() { }); return object; }; +ReactTestComponent.prototype.getType = function() { + return this._currentElement.type; +}; +ReactTestComponent.prototype.getProps = function() { + return this._currentElement.props; +}; +ReactTestComponent.prototype.getChildren = function() { + return Object.keys(this._renderedChildren || {}).map(function(key) { + return this._renderedChildren[key]; + }, this); +}; +ReactTestComponent.prototype.find = function(selector) { + if (this.getType() === selector) { + return this; + } + var children = this.getChildren(); + + for (var key in children) { + var inst = children[key]; + let result = inst.find(selector); + if (result) { + return result; + } + } + return null; +}; +ReactTestComponent.prototype.findAll = function(selector, props) { + var output = []; + + if (this.getType() === selector) { + output.push(this); + } + + return this.getChildren().reduce(function(current, inst) { + return current.concat(inst.findAll(selector)); + }, output); +}; +ReactTestComponent.prototype.findByProps = function(props) { + var propsEqual = true; + for (var key in props) { + if (this.getProps()[key] !== props[key]) { + propsEqual = false; + break; + } + } + if (propsEqual) { + return this; + } + + var children = this.getChildren(); + for (key in children) { + var inst = children[key]; + let result = inst.findByProps(props); + if (result) { + return result; + } + } + return null; +}; + Object.assign(ReactTestComponent.prototype, ReactMultiChild.Mixin); // ============================================================================= @@ -106,6 +166,15 @@ ReactTestTextComponent.prototype.unmountComponent = function() {}; ReactTestTextComponent.prototype.toJSON = function() { return this._currentElement; }; +ReactTestTextComponent.prototype.find = function() { + return null; +}; +ReactTestTextComponent.prototype.findAll = function() { + return []; +}; +ReactTestTextComponent.prototype.findByProps = function() { + return null; +}; // ============================================================================= diff --git a/src/renderers/testing/__tests__/ReactTestRenderer-test.js b/src/renderers/testing/__tests__/ReactTestRenderer-test.js index a20d442686d..4e700f028b0 100644 --- a/src/renderers/testing/__tests__/ReactTestRenderer-test.js +++ b/src/renderers/testing/__tests__/ReactTestRenderer-test.js @@ -198,4 +198,134 @@ describe('ReactTestRenderer', function() { expect(log).toEqual([null]); }); + describe('selectors API', function() { + it('finds one element by name', function() { + class Component extends React.Component { + render() { + return ( +
+
one element
+ + +
+ +
+
+
+ ); + } + } + + var renderer = ReactTestRenderer.create(); + + var myelement = renderer.find('myelement'); + expect(myelement.getProps().id).toEqual(1); + }); + + it('concatenates finders', function() { + class Component extends React.Component { + render() { + return ( +
+
one element
+ + +
+ +
+
+
+ ); + } + } + + var renderer = ReactTestRenderer.create(); + + var myelement = renderer.find('span').find('myelement'); + expect(myelement.getProps().id).toEqual(2); + }); + + it('finds all elements by name', function() { + class Component extends React.Component { + render() { + return ( +
+
one element
+ + +
+ +
+
+
+ ); + } + } + + var renderer = ReactTestRenderer.create(); + + var myelements = renderer.findAll('myelement'); + expect(myelements.length).toBe(2); + expect(myelements[0].getProps().id).toBe(1); + expect(myelements[1].getProps().id).toBe(2); + }); + + it('finds an element by props', function() { + class Component extends React.Component { + render() { + return ( +
+
one element
+ + +
+ +
+
+
+ ); + } + } + + var renderer = ReactTestRenderer.create(); + + var myelement = renderer.findByProps({ + className: 'found', + id: 2, + }); + expect(myelement.getProps()).toEqual({ + className: 'found', + id: 2, + another: true, + }); + }); + + it('can use finders to trigger events', function() { + class Component extends React.Component { + constructor() { + super(); + this.state = { clicked: 0 }; + } + + render() { + return ( +
+ {this.state.clicked} + +
+ ); + } + _onClick = () => { + this.setState({clicked: 1}); + } + } + + var renderer = ReactTestRenderer.create(); + + expect(renderer.find('span').getChildren()[0].toJSON()).toEqual(0); + + renderer.find('myelement').getProps().onClick(); + expect(renderer.find('span').getChildren()[0].toJSON()).toEqual(1); + }); + }); });