mzgoddard / preact-render-spy

Render preact components with access to the produced virtual dom for testing.

Home Page:https://www.npmjs.com/package/preact-render-spy

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

What should the API look like?

mzgoddard opened this issue · comments

Leaving #5 as a place to discuss where and how to announce this package. This Issue is for what the API currently is and how we want it changed to get it to an announcable state.

At this moment, the API is as following

module.exports(vdom) -> SpyWrapper
- shallow(vdom) -> SpyWrapper
- deep(vdom) -> SpyWrapper
- render(vdom) -> SpyWrapper

new FindWrapper(spyWrapper, vdoms, selector)
- at(index) -> FindWrapper
- find(selector) -> FindWrapper
- attr(name) -> Any
- attrs() -> Object
- text() -> String
- contains(vdom) -> Boolean
- simulate(event, ...args)
- output() -> vdom
- filter(selector) -> FindWrapper

new SpyWrapper() extends FindWrapper
- render(vdom, {depth}) -> SpyWrapper

Here is a current possible example

const {render} = require('preact-render-spy');

// ...

const spy = render(<Node />);
spy.find('div').simulate('click');
spy.find('div').text() === 'clicked';

The supported selector set is currently very small

  • 'div' or 'Node': Selects on a native dom element or a preact Component
  • '.class' or '#id': Selects nodes with a class or id attribute set the given class or id
  • '[onClick]': Selects nodes with an attribute of this name

The current selector can only be one of these options and cannot be mixed.

I figure this might the minimum we'd add before announcing?

--- current
+++ target
@@ -1,12 +1,18 @@
 renderSpy(vdom) -> SpyWrapper
+- shallow(vdom) -> SpyWrapper
+- render(vdom) -> SpyWrapper
 
 new SpyWrapper()
+- shallow(vdom) -> SpyWrapper
 - render(vdom) -> SpyWrapper
 - find(selector) -> FindWrapper
+- text() -> String
+- contains(vdom) -> Boolean
 
 new FindWrapper(spyWrapper, vdoms, selector)
 - at(index) -> FindWrapper
 - attr(name) -> Any
+- attrs(name) -> Any[]
 - text() -> String
 - contains(vdom) -> Boolean
 - simulate(event, ...args)
\ No newline at end of file

For selectors we should at least support joined selectors

Such as

  • .class.class2.class3
  • Node.anotherClass

And specified attribute values

  • [name=anchor]

Seems sensible to me.

How does attrs(name) work? I'd expect attrs() to return an object, such that

attr('foo') === attrs().foo

Also, what's the difference between renderSpy and shallow and render?

shallow would "stub" instead of "spy" on child components called for in the resulting JSX after "one level deep"

so shallow(<Node />) would render that Node, but if it rendered <SubNode /> it wouldn't actually create a SubNode component, just stub it.

whereas render(<Node />) would go deep?

@mzgoddard any chance you'd be willing to curate the "latest version" in the first comment in the issue as we make changes? so the initial text overviews the issue in case other people show up

Should the FindWrapper api include find ?

Also, with the wrapper itself, the top level element, without find... can you just call .attr() etc?

A .children() api might be useful too.

@mzgoddard any chance you'd be willing to curate the "latest version" in the first comment in the issue as we make changes? so the initial text overviews the issue in case other people show up

Sure!

Should the FindWrapper api include find ?

It's useful but I think its potentially confusing what the output would be. But I'm totally up for having it.

Also, with the wrapper itself, the top level element, without find... can you just call .attr() etc?

You could but you'd just get the attributes on the the node you passed into renderSpy.

A .children() api might be useful too.

How does that work since preact Components have shadow doms in regards to how this works? You can .find('Node') but it doesn't have children. It's rendered (shadow) dom may have children. Should it be dereferenced like that?

How does that work since preact Components have shadow doms in regards to how this works? You can .find('Node') but it doesn't have children. It's rendered (shadow) dom may have children. Should it be dereferenced like that?

Literal children of a find... I.E.

const items = [{}, {}, {}];
const context = renderSpy(<List items={items} />);
expect(context.find('ul').children().length).toEqual(items.length);

I would think .children() of a "functional" node I think would be it's attr('children')

I.E:

const SubNode = ({children}) => <div>{children}</div>;
const Node = () => (
  <SubNode>
    <span>1</span>
    <span>2</span>
  </SubNode>
);

expect(renderSpy().shallow(<Node />).find('SubNode').children())
  .toEqual(<span>1</span><span>2</span>);

It's the "children" of the node in vdom?

I would think .children() of a "functional" node I think would be it's attr('children')

er wait, it's true for everything isn't it...

Just .children() would return a FindWrapper of the .attr('children') instead of the actual children?

¯\(ツ)/¯ I'm gonna 🍌 myself on that idea and put it aside, it doesn't seem as useful to me anymore...

Yeah. It should be useful. The indirection of components makes it kind of weird. We could make it only children of already selected dom and not component nodes. But that could easily be misunderstood? It just sounds weird.

I'm seeing both of these in the above examples. What's the difference?

renderSpy(<Node />)
renderSpy().shallow(<Node />)

When would one use renderSpy(node) instead of renderSpy().shallow(node) or (presumably) renderSpy().render(node)?

Are shallow and render exported so they can be imported like so? Because I don't see why I'd use renderSpy.

import {shallow, render} from 'preact-render-spy';

It'd be nice to have one clear return from preact-render-spy and having shallow and render beside each other.

Should the returned objects from shallow and render be different @gnarf @cowboy. Should shallow return an object that can only be shallow rendered from then on? Should render return an object that can only be deeply rendered from then on?

@mzgoddard I don't see why it would matter... when are you re-rendering the component?

With how preact works I don't see a use in re-rendering. I figured originally it'd be like React which iirc will reuse dom with a top level render call. preact though does a completely fresh render. Which I think I learned after my last post.

But having said that you might use a re-render to test your componentWillUnmount stuff easily. As that lifecycle I'd imagine would still occur. Though if this is the only use, maybe it should be a teardown method intended for that purpose. Hmm.

#9 (comment) is up to date with the API we are likely shipping in version 1. If you'd like to comment further on the API please open a new Issue to discuss the change you are thinking about.