simon360 / react-from-markup

Declare your React components with static HTML
MIT License
18 stars 4 forks source link

Testing utilities #23

Open simon360 opened 6 years ago

simon360 commented 6 years ago

Add some testing utilities.

In particular, a version of MarkupContainer that can be used by developers who are using the library in their project, would be handy.

Need to delineate clearly between development tools, and tools that are appropriate to send to production.

willtonkin commented 5 years ago

Having written a fair few tests for rehydrators now, some way of exporting the rehydrateChildren or having some mock function available would help a lot in the case of, for example:

  it("Should pass props through correctly on rehydration", async () => {
    const component = (
      <Component prop="my Value">
        <span>children</span>
      </Component>
    );
    const documentElement = document.createElement("div");

    documentElement.innerHTML = ReactDOMServer.renderToStaticMarkup(component);

    const rehydrateChildren = jest.fn(() => <span>rehydrated children</span>);
    const reactElement = await rehydrator(
      documentElement.childNodes[0],
      rehydrateChildren
    );

    expect(reactElement).toMatchSnapshot();
  });

In this usecase we'd prefer something more sophisticated than the jest.fn() at rehydrateChildren

simon360 commented 5 years ago

Hm, interesting. That's definitely worth a look. I can't think of a reason not to, at least as a testing utility.

simon360 commented 5 years ago

On a second look, rehydrateChildren is exported from react-from-markup. It's just not the rehydrateChildren you know (and love). It requires a couple of extra arguments, but they're easy to fill in.

The tl;dr: in your test above, you could...

import { rehydrateChildren } from "react-from-markup";

// ...

    const reactElement = await rehydrator(
      documentElement.childNodes[0],
      children => rehydrateChildren(children, [], {})
    );

and be on your merry way. You can provide some rehydrators or options if you wish, but in the vast majority of cases, I suspect this will do.

Why this works

When react-from-markup calls a rehydrator, it does this:

  return rehydrator(
    el,
    children => rehydrateChildren(children, rehydrators, options),
    options.extra
  );

That second argument takes a 3-argument version of rehydrateChildren, and binds it to the list of rehydrators and the options that are normally provided directly to rehydrate() when the page initializes. That way, your rehydrator gets a function that it can call with a single children argument, without escaping the system.

Under the hood, rehydrateChildren is where everything happens - rehydrate (the default export) just adds a markup container search into the mix.

The nice thing about the jest.fn() method you used, though, is that it avoids accidentally testing the internals of react-from-markup, when what you really want to do is test an independent rehydrator unit. There's definitely use cases where this is useful, but most of the time, it's best to treat children as something that will just work.