UNPKG

enzyme-adapter-preact-pure

Version:

Enzyme adapter for Preact

135 lines (134 loc) 4.03 kB
import { Fragment, options } from 'preact'; import { childElements } from './compat.js'; /** * Map of component function to replacement stub function used when shallow * rendering. */ const shallowRenderComponents = new Map(); /** * Global flag indicating whether shallow rendering is active. */ let shallowRenderActive = false; let shallowRenderHookInstalled = false; function getDisplayName(type) { return type.displayName || type.name; } /** * Return the real component type of a component instance created by * shallow rendering. */ export function getRealType(component) { const c = component; const ctor = c.constructor; if (ctor.originalType) { return ctor.originalType; } else { return null; } } /** * Return true if a value is something that can be returned from a render * function. */ function isRenderable(value) { return (value === null || Array.isArray(value) || typeof value == 'number' || typeof value === 'string' || (typeof value === 'object' && value.type !== undefined)); } /** * Create a dummy component to replace an existing component in rendered output. * * The dummy renders nothing but has the same display name as the original. * This is used to implement shallow rendering by replacing the real component * during shallow renders. */ function makeShallowRenderComponent(type) { function ShallowRenderStub({ children }) { // Preact can render fragments, so we can return the children directly. // // There is an exception for `children` values which are not directly // renderable but need to be processed by the component being stubbed. // For example, a function used as part of the "render prop" pattern // (https://reactjs.org/docs/render-props.html). return isRenderable(children) ? children : null; } ShallowRenderStub.originalType = type; ShallowRenderStub.displayName = getDisplayName(type); return ShallowRenderStub; } /** * Preact `options.vnode` hook that causes a component not to be rendered when * shallow rendering is enabled. */ function shallowRenderVNode(vnode) { if (typeof vnode.type === 'string' || vnode.type == null || (typeof Fragment !== 'undefined' && vnode.type === Fragment)) { return; } let stub = shallowRenderComponents.get(vnode.type); if (!stub) { stub = makeShallowRenderComponent(vnode.type); shallowRenderComponents.set(vnode.type, stub); } vnode.type = stub; } function installShallowRenderHook() { if (shallowRenderHookInstalled) { return; } const prevHook = options.vnode; options.vnode = vnode => { if (shallowRenderActive) { shallowRenderVNode(vnode); } if (prevHook) { prevHook(vnode); } }; shallowRenderHookInstalled = true; } function isVNode(obj) { return Object(obj) === obj && typeof obj.type !== 'undefined'; } /** * Return true if a VNode has been modified to shallow-render. */ export function isShallowRendered(vnode) { if (vnode.type == null || typeof vnode.type === 'string') { return false; } const type = vnode.type; return typeof type.originalType === 'function'; } /** * Convert components in a VNode tree to shallow-render. */ export function shallowRenderVNodeTree(vnode) { shallowRenderVNode(vnode); childElements(vnode).forEach(c => { if (isVNode(c)) { shallowRenderVNodeTree(c); } }); } /** * Invoke `fn` with shallow rendering enabled in Preact. * * During the execution of `fn`, any function or class component elements * created by Preact's `h` function will be modified to only render a placeholder * instead of the real component. */ export function withShallowRendering(fn) { installShallowRenderHook(); try { shallowRenderActive = true; fn(); } finally { shallowRenderActive = false; } }