Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change in _owner causes issues with shallow rendered tests #5292

Closed
hatched opened this issue Oct 26, 2015 · 7 comments
Closed

Change in _owner causes issues with shallow rendered tests #5292

hatched opened this issue Oct 26, 2015 · 7 comments

Comments

@hatched
Copy link

hatched commented Oct 26, 2015

There appears to have been a change in how _owner is generated between 0.13 and 0.14 which is causing a number of our shallow rendered tests to fail when upgrading because the deepEquals fails on the _owner property. Here is a simple example component which exhibits the issue.

'use strict';

fdescribe('ComponentTest', function() {

  it('passes in 0.13 but fails in 0.14', function() {
    var components = {};
    components.tester = React.createClass({

      getInitialState: function() {
        return this.generateState();
      },

      generateState: function() {
        var state = {};
        state.body = (
          <ul className="foo">
            <li><div>Item 1</div></li>
            <li>Item 2</li>
          </ul>);
        return state;
      },

      render: function() {
        return (
          <div className="foo">
            {this.state.body}
          </div>);
      }
    });

    var shallowRenderer = testUtils.createRenderer();
    shallowRenderer.render(<components.tester />);
    var output = shallowRenderer.getRenderOutput();

    assert.deepEqual(output,
      <div className="foo">
        <ul className="foo">
          <li><div>Item 1</div></li>
          <li>Item 2</li>
        </ul>
      </div>);
  });
});
    AssertionError: expected { '$typeof': 60103,
      type: 'div',
      key: null,
      ref: null,
      props: 
       { className: 'foo',
         children: 
          { '$typeof': 60103,
            type: 'ul',
            key: null,
            ref: null,
            props: [Object],
            _owner: [Object],
            _store: {} } },
      _owner: null,
      _store: {} } to deeply equal { '$typeof': 60103,
      type: 'div',
      key: null,
      ref: null,
      props: 
       { className: 'foo',
         children: 
          { '$typeof': 60103,
            type: 'ul',
            key: null,
            ref: null,
            props: [Object],
            _owner: null,
            _store: {} } },
      _owner: null,
      _store: {} }

@iredelmeier
Copy link

I've been seeing some similar issues with shallow rendering. My current (messy - sorry!) workaround is this:

// in ReactTestUtils.js

var NoopInternalComponent = function (element) {
  var type = element.type.name || element.type;
  var children = NoopInternalChildren(element.props.children);
  var props = assign({}, element.props, { children: children })

  this._renderedOutput = assign({}, element, { type, props });
  this._currentElement = element;
};

var NoopInternalChildren = function (children) {
  if (Array.isArray(children)) {
    return children.map(NoopInternalChild);
  } else if (children === Object(children)) {
    return NoopInternalChild(children);
  }
  return children;
};

var NoopInternalChild = function (child) {
  var props = child.props && child.props.children ? assign({}, child.props, {
    children: NoopInternalChildren(child.props.children)
  }) : child.props;
  return assign({}, child, { _owner: null }, { props });
};

Let me know if that helps!

My version currently breaks some of the Jest tests, but I'm happy to work on this some more and submit a PR.

@hatched
Copy link
Author

hatched commented Oct 27, 2015

Thank you very much for this, it's been a tremendous help!

While this solved the problem for my test case above it still failed on a large number of our more complex tests. I've been iterating on it and once I have all of our tests passing I'll add the updated version.

@hatched
Copy link
Author

hatched commented Oct 27, 2015

Here is the updated workaround, it works for our 238 tests of varying complexity but I'd be interested to see if it passes all of your tests as well.

var NoopInternalComponent = function (element) {
  var type = element.type.name || element.type;
  var props = {};
  if (element.props.children) {
    props.children = NoopInternalChildren(element.props.children);
  }
  var props = assign({}, element.props, props);

  this._renderedOutput = assign({}, element, { type: type, props: props });
  this._currentElement = element;
};

var NoopInternalChildren = function (children) {
  if (Array.isArray(children)) {
    return children.map(NoopInternalChild);
  } else if (children === Object(children)) {
    return NoopInternalChild(children);
  }
  return children;
};

var NoopInternalChild = function (child) {
  if (child === null || child === undefined) { return; }
  if (typeof child === 'string') { return child; }
  if (Array.isArray(child)) {
    return child.map(NoopInternalChild);
  }
  var props = child.props && child.props.children ? assign({}, child.props, {
    children: NoopInternalChildren(child.props.children)
  }) : child.props;
  return assign({}, child, { _owner: null }, { props: props });
};

@iredelmeier
Copy link

Awesome!

If you don't mind trying, what happens if you do

// NoopInternalChildren
if (Array.isArray(children) {
  return children.map(NoopInternalChildren);
}

and then use the earlier NoopInternalChild?

@hatched
Copy link
Author

hatched commented Oct 27, 2015

Much cleaner thanks, I guess I was in my head too much :)

var NoopInternalComponent = function (element) {
  var type = element.type.name || element.type;
  var props = {};
  if (element.props.children) {
    props.children = NoopInternalChildren(element.props.children);
  }
  var props = assign({}, element.props, props);

  this._renderedOutput = assign({}, element, { type: type, props: props });
  this._currentElement = element;
};

var NoopInternalChildren = function (children) {
  if (Array.isArray(children)) {
    return children.map(NoopInternalChildren);
  } else if (children === Object(children)) {
    return NoopInternalChild(children);
  }
  return children;
};

var NoopInternalChild = function (child) {
  var props = child.props && child.props.children ? assign({}, child.props, {
    children: NoopInternalChildren(child.props.children)
  }) : child.props;
  return assign({}, child, { _owner: null }, { props: props });
};

@vfil
Copy link

vfil commented Apr 8, 2016

I prefer applying a monkey patch, until we have this fixed in react by wrapping TestUtils in my own module:

function render(component) {
    const renderer = TestUtils.createRenderer()
    renderer.render(component)
    return patch(renderer.getRenderOutput())
}

function fixOwner(obj) {
    return obj._owner ? Object.assign({}, obj, {_owner: null}) : Object.assign({}, obj)
}

function patch(obj) {
    if (Array.isArray(obj)) {
        return obj.map(comp => {
            return fixChildren(comp)
        })
    } else {
        obj = fixOwner(obj)
        if (obj.props && typeof obj.props.children === 'object') {
            obj.props = fixOwner(obj.props)
            obj.props.children = fixChildren(obj.props.children)
        }
        return obj
    }
}

https://gist.github.com/vfil/81049b1590aa3b9e439188a49a5d67bc

@gaearon
Copy link
Collaborator

gaearon commented Apr 14, 2016

This should be fixed by #6362.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants