UNPKG

reblend-testing-library

Version:

Simple and complete Reblendjs testing utilities that encourage good testing practices.

269 lines (263 loc) 7.81 kB
import { Portal, Reblend, useEffect, useReducer, useRef } from "reblendjs"; import { fireEvent, render, screen, configure } from "../"; describe("render API", () => { let originalConfig; beforeEach(() => { // Grab the existing configuration so we can restore // it at the end of the test configure(existingConfig => { originalConfig = existingConfig; // Don't change the existing config return {}; }); }); afterEach(() => { configure(originalConfig); }); test("renders div into document", async () => { const ref = useRef(); const { container } = await render(Reblend.construct.bind(this)("div", { ref: ref })); expect(container.firstChild).toBe(ref.current); }); test("works great with reblendjs portals", async () => { class MyPortal extends Reblend { initState(...args) { super.initState(...args); this.portalNode = document.createElement("div"); this.portalNode.dataset.testid = "my-portal"; } componentDidMount() { document.body.appendChild(this.portalNode); } componentWillUnmount() { this.portalNode.parentNode.removeChild(this.portalNode); } html() { return Reblend.construct.bind(this)(Portal, { portal: this.portalNode }, Reblend.construct.bind(this)(Greet, { greeting: "Hello", subject: "World" }), ","); } } class Greet extends Reblend { static ELEMENT_NAME = "Greet"; constructor() { super(); } async initState() {} async initProps({ greeting, subject }) { this.props = {}; this.props.greeting = greeting; this.props.subject = subject; } async html() { return Reblend.construct.bind(this)("div", null, Reblend.construct.bind(this)("strong", null, this.props.greeting, " ", this.props.subject)); } } /* @Reblend: Transformed from function to class */ const { unmount } = await render(Reblend.construct.bind(this)(MyPortal, null)); expect(screen.getByText("Hello World")).toBeInTheDocument(); const portalNode = screen.getByTestId("my-portal"); expect(portalNode).toBeInTheDocument(); await unmount(); expect(portalNode).not.toBeInTheDocument(); }); test("returns baseElement which defaults to document.body", async () => { const { baseElement } = await render(Reblend.construct.bind(this)("div", null)); expect(baseElement).toBe(document.body); }); test("supports fragments", async () => { class Test extends Reblend { html() { return Reblend.construct.bind(this)("div", null, Reblend.construct.bind(this)("code", null, "DocumentFragment"), " is pretty cool!"); } } const { asFragment } = await render(Reblend.construct.bind(this)(Test, null)); expect(asFragment()).toMatchSnapshot(); }); test("renders options.wrapper around node", async () => { //@reblendComponent class WrapperComponent extends Reblend { static ELEMENT_NAME = "WrapperComponent"; constructor() { super(); } async initState() {} async initProps({ children }) { this.props = {}; this.props.children = children; } async html() { return Reblend.construct.bind(this)("div", { "data-testid": "wrapper" }, this.props.children); } } /* @Reblend: Transformed from function to class */ const { container } = await render(Reblend.construct.bind(this)("div", { "data-testid": "inner" }), { wrapper: WrapperComponent }); expect(screen.getByTestId("wrapper")).toBeInTheDocument(); expect(container.firstChild).toMatchInlineSnapshot(` <div reblendcomponent="WrapperComponent" > <div data-testid="wrapper" > <div data-testid="inner" > </div> </div> </div> `); }); test("renders", async () => { const spy = jest.fn(); //@reblendComponent class Component extends Reblend { static ELEMENT_NAME = "Component"; constructor() { super(); } async initState() { this.props.spy(); } async initProps({ spy }) { this.props = {}; this.props.spy = spy; } async html() { return Reblend.construct.bind(this)(Reblend, null); } } /* @Reblend: Transformed from function to class */ await render(Reblend.construct.bind(this)(Component, { spy: spy })); expect(spy).toHaveBeenCalledTimes(1); }); test("flushes useEffect cleanup functions sync on unmount()", async () => { const spy = jest.fn(); //@reblendComponent class Component extends Reblend { static ELEMENT_NAME = "Component"; constructor() { super(); } async initState() { useEffect.bind(this)(() => this.props.spy, (() => []).bind(this)); } async initProps({ spy }) { this.props = {}; this.props.spy = spy; } async html() { return Reblend.construct.bind(this)(Reblend, null); } } /* @Reblend: Transformed from function to class */ const { unmount } = await render(Reblend.construct.bind(this)(Component, { spy: spy })); expect(spy).toHaveBeenCalledTimes(0); await unmount(); expect(spy).toHaveBeenCalledTimes(1); }); test("can be called multiple times on the same container", async () => { const container = document.createElement("div"); const { unmount } = await render(Reblend.construct.bind(this)("strong", null), { container }); expect(container).toContainHTML("<strong></strong>"); await render(Reblend.construct.bind(this)("em", null), { container }); expect(container).toContainHTML("<em></em>"); await unmount(); expect(container).toBeEmptyDOMElement(); }); /* test('The UI should be interactive', async () => { //@reblendComponent function App() { const [clicked, handleClick] = useReducer(n => n + 1, 0) return ( <button type="button" onClick={handleClick}> clicked:{clicked} </button> ) } const ui = <App /> const container = document.createElement('div') document.body.appendChild(container) container.innerHTML = await Reblend.renderToString(ui) expect(container).toHaveTextContent('clicked:0') await render(ui, {container}) fireEvent.click(container.querySelector('button')) expect(container).toHaveTextContent('clicked:1') }) */ test("Render with a wrapper", async () => { const wrapperComponentMountEffect = jest.fn(); const ui = Reblend.construct.bind(this)("div", null); //@reblendComponent class WrapperComponent extends Reblend { static ELEMENT_NAME = "WrapperComponent"; constructor() { super(); } async initState() { useEffect.bind(this)(() => { wrapperComponentMountEffect(); }); } async initProps({ children }) { this.props = {}; this.props.children = children; } async html() { return Reblend.construct.bind(this)(Reblend, null, this.props.children); } } /* @Reblend: Transformed from function to class */ const container = document.createElement("div"); document.body.appendChild(container); container.innerHTML = await Reblend.renderToString(ui); await render(ui, { container, wrapper: WrapperComponent }); expect(wrapperComponentMountEffect).toHaveBeenCalledTimes(1); }); });