reblend-testing-library
Version:
Simple and complete Reblendjs testing utilities that encourage good testing practices.
145 lines (141 loc) • 4.46 kB
JavaScript
import { getQueriesForElement, prettyDOM } from "@testing-library/dom";
import { fireEvent } from "./fire-event";
import { getConfig, configure } from "./config";
import Reblend, { ConfigUtil, detach, useRef } from "reblendjs";
// Ideally we'd just use a WeakMap where containers are keys and roots are values.
// We use two variables so that we can bail out in constant time when we render with a new container (most common use case)
const mountedContainers = new Set();
function wrapUiIfNeeded(innerElement, wrapperComponent) {
return wrapperComponent ? Reblend.construct(wrapperComponent, {}, innerElement) : innerElement;
}
async function renderRoot(ui, {
baseElement,
container,
queries,
wrapper: WrapperComponent
} = {}) {
if (!container) return undefined;
ConfigUtil.getInstance().update({
noDefering: true
});
await Reblend.mountOn(container, await wrapUiIfNeeded(ui, WrapperComponent));
return {
container,
baseElement,
debug: (el = baseElement, maxLength, options) => Array.isArray(el) ?
// eslint-disable-next-line no-console
el.forEach(e => console.log(prettyDOM(e, maxLength, options))) :
// eslint-disable-next-line no-console,
console.log(prettyDOM(el, maxLength, options)),
unmount: async () => {
mountedContainers.delete(container);
await detach(container);
},
rerender: async rerenderUi => {
const rerenderchildren = await wrapUiIfNeeded(rerenderUi, WrapperComponent);
await Reblend.mountOn(container, rerenderchildren);
// Intentionally do not return anything to avoid unnecessarily complicating the API.
// folks can use all the same utilities we return in the first place that are bound to the container
},
asFragment: () => {
/* istanbul ignore else (old jsdom limitation) */
if (typeof document.createRange === "function") {
return document.createRange().createContextualFragment(container?.innerHTML);
} else {
const template = document.createElement("template");
template.innerHTML = container?.innerHTML;
return template.content;
}
},
...getQueriesForElement(baseElement, queries)
};
}
async function render(ui, {
baseElement,
container,
queries,
wrapper
} = {}) {
if (!baseElement) {
// default to document.body instead of documentElement to avoid output of potentially-large
// head elements (such as JSS style blocks) in debug output
baseElement = document.body;
}
if (!container) {
container = baseElement.appendChild(document.createElement("div"));
}
mountedContainers.add(container);
return await renderRoot(ui, {
container,
baseElement,
queries,
wrapper
});
}
async function cleanup() {
for (const container of mountedContainers) {
await detach(container);
}
mountedContainers.clear();
}
async function renderHook(useRenderCallback, options = {}) {
const {
initialProps,
...renderOptions
} = options;
let result = useRef();
//@reblendComponent
class TestComponent extends Reblend {
static ELEMENT_NAME = "TestComponent";
constructor() {
super();
}
async initState() {
const pendingResult = await useRenderCallback.bind(this)(this.props.initialProps);
this.state.pendingResult = pendingResult;
result.current = this.state.pendingResult;
}
async initProps({
initialProps
}) {
this.props = {};
this.props.initialProps = initialProps;
}
async html() {
return null;
}
}
/* @Reblend: Transformed from function to class */
const {
rerender: baseRerender,
unmount
} = await render(
//@ts-ignore
Reblend.construct.bind(this)(TestComponent, {
initialProps: initialProps
}), renderOptions);
async function rerender(rerenderCallbackProps) {
return await baseRerender(
//@ts-ignore
Reblend.construct.bind(this)(TestComponent, {
initialProps: rerenderCallbackProps
}));
}
return {
result,
rerender,
unmount
};
}
async function act(callback) {
if (typeof callback !== "function") {
throw new Error("You should pass a function to act.");
}
await callback();
// Flush microtasks
await Promise.resolve();
}
// just re-export everything from dom-testing-library
export * from "@testing-library/dom";
export { act, render, renderHook, cleanup, fireEvent, getConfig, configure };
/* eslint func-name-matching:0 */