enzymejs / enzyme

JavaScript Testing utilities for React
https://enzymejs.github.io/enzyme/
MIT License
19.96k stars 2.01k forks source link

Mixed-depth shallow rendering mode #250

Open lelandrichardson opened 8 years ago

lelandrichardson commented 8 years ago

We could implement an option expand to shallow that allowed you to specify components that you'd like to "expand" the shallow render tree into, while all other components are considered leaf nodes.

function D() {
  return <div className="d" />
}
function C() {
  return <div className="c" />
}
function B(props) {
  return (
    <div className="b">
      <div>B</div>
      {props.children}
    </div>
  );
}
function A(props) {
  return (
    <B>
      <C />
      <D />
      {props.children}
    </B>
  );
}
const wrapper = shallow(<A>Root</A>, { expand: [B, C] });
wrapper.debug();

would output something like this:

<A>
  <B>
    <div className="b">
      <div>B</div>
      <C>
        <div className="c">A</div>
      </C>
      <D />
      Root
    </div>
  </B>
</A>
ljharb commented 8 years ago

Wouldn't this end up being more like mountUntil? ie, you'd render the entire tree and only stop when you found the expand elements - whereas mount only stops when it finds native HTML elements.

It seems closer to a partial mount to me, than a shallow render, but maybe I'm missing some of the semantic differences.

lelandrichardson commented 8 years ago

@ljharb mount indicates that it will actually mount onto a root DOM node. shallow only calls componentWillMount and render, getting the render tree, but not doing anything with it.

ljharb commented 8 years ago

hm, ok - so then mount is more like "shallow but only stop at native DOM elements" + "attach to the DOM"?

If so then this new thing maybe should be a new top-level thing - that mount calls into?

lelandrichardson commented 8 years ago

@ljharb i'm not sure that's a good way to describe mount. mount uses the same renderer that react-dom itself uses. It makes perfect sense to just use that directly (like we are doing right now).

Because this method is "opt in"... ie, you have to provide all of the components you want the renderer to expand, I think having this on shallow still makes the most sense...

sjdemartini commented 8 years ago

I like that this solution allows the user to specify which components should be shallow rendered in the subtree (compared to #249), which is useful for keeping the spec more isolate. However, #249 has the advantage that it will work for un-exported components in the root component's subtree, as are often used with stateless functional components. This will require that we export anything we want to expand, right?

lelandrichardson commented 8 years ago

@sjdemartini that is true. You could similarly provide a collapse option to shallow that would be the inverse, where you would manually specify the components that you would want it to stop at

sjdemartini commented 8 years ago

Nice, yeah, a collapse option with #249 sounds like an awesome idea.

lasekio commented 7 years ago

I've made PR to React to support similar functionality in Shallow Renderer itself:

https://github.com/facebook/react/pull/5513

But Facebook doesn't seem to be interested. Another problem is that original shallow renderer doesn't support functional components (primitives) because it doesnt have to - to test primitives call it and check output is enough.

The good idea would be build own shallow renderer outside react. I think it is possible but I React doesnt expose all internals (like ReactChildReconciler) and it would be hard to use this renderer outside node env, when we cannot reach internals like require('react/..../SomeInternal').

Package could contain react codebase which is not desirable. React should stay as peer dependency.

So... We'd need to build our own shallow renderer. It is possible but really hard to accomplish.

Any ideas? Maybe we can convince React team to merge my PR? Or provide another way to partial deep render?