UNPKG

enzyme-adapter-preact-pure

Version:

Enzyme adapter for Preact

176 lines (175 loc) 6.27 kB
"use strict"; /** * Functions for rendering components using Preact v10 and converting the result * to a React Standard Tree (RST) format defined by Enzyme. * * Preact 10 stores details of the rendered elements on internal fields of * the VNodes. A reference to the vnode is stored in the root DOM element. * The rendered result is converted to RST by traversing these vnode references. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.getNode = exports.rstNodeFromElement = exports.nodeTypeFromType = void 0; const preact_1 = require("preact"); const compat_js_1 = require("./compat.js"); const preact10_internals_js_1 = require("./preact10-internals.js"); const shallow_render_utils_js_1 = require("./shallow-render-utils.js"); function stripSpecialProps(props, preserveChildrenProp = false) { if (preserveChildrenProp) { const { key, ref, ...otherProps } = props; return otherProps; } else { const { children, key, ref, ...otherProps } = props; return otherProps; } } function convertDOMProps(props, preserveChildrenProp = false) { const srcProps = stripSpecialProps(props, preserveChildrenProp); const converted = {}; Object.keys(srcProps).forEach(srcProp => { const destProp = srcProp === 'class' ? 'className' : srcProp; converted[destProp] = props[srcProp]; }); return converted; } /** * Convert the rendered output of a vnode to RST nodes. */ function rstNodesFromChildren(nodes) { if (!nodes) { return []; } return nodes.flatMap(node => { if (node === null) { // The array of rendered children may have `null` entries as a result of // eg. conditionally rendered children where the condition was false. // // These are omitted from the rendered tree that Enzyme works with. return []; } const rst = rstNodeFromVNode(node); return Array.isArray(rst) ? rst : [rst]; }); } function rstNodeFromVNode(node) { if (node == null) { return null; } // Preact 10 represents text nodes as VNodes with `node.type == null` and // `node.props` equal to the string content. if (typeof node.props === 'string' || typeof node.props === 'number' || typeof node.props === 'bigint') { return String(node.props); } if (node.type === preact_1.Fragment) { return rstNodesFromChildren((0, preact10_internals_js_1.getChildren)(node)); } const component = (0, preact10_internals_js_1.getComponent)(node); if (component) { return rstNodeFromComponent(node, component); } if (!(0, preact10_internals_js_1.getDOMNode)(node)) { throw new Error(`Expected VDOM node to be a DOM node but got ${node.type}`); } return { nodeType: 'host', type: node.type, props: convertDOMProps(node.props), key: node.key || null, ref: node.ref || null, instance: (0, preact10_internals_js_1.getDOMNode)(node), rendered: rstNodesFromChildren((0, preact10_internals_js_1.getChildren)(node)), }; } function nodeTypeFromType(type) { if (typeof type === 'string') { return 'host'; } else if (type.prototype && typeof type.prototype.render === 'function') { return 'class'; } else if (typeof type === 'function') { return 'function'; } else { throw new Error(`Unknown node type: ${type}`); } } exports.nodeTypeFromType = nodeTypeFromType; /** * Convert a JSX element tree returned by Preact's `h` function into an RST * node. */ function rstNodeFromElement(node, preserveChildrenProp = false) { if (!(0, preact_1.isValidElement)(node)) { return node; } const children = (0, compat_js_1.childElements)(node) .flat() .map(child => rstNodeFromElement(child, preserveChildrenProp)); const nodeType = nodeTypeFromType(node.type); let props = {}; if (typeof node.props === 'object' && node.props) { props = nodeType === 'host' ? convertDOMProps(node.props, preserveChildrenProp) : stripSpecialProps(node.props, preserveChildrenProp); } const ref = node.ref || null; return { nodeType, type: node.type, props, key: node.key || null, ref, instance: (0, preact10_internals_js_1.getComponent)(node), rendered: children, }; } exports.rstNodeFromElement = rstNodeFromElement; /** * Return a React Standard Tree (RST) node from a Preact `Component` instance. */ function rstNodeFromComponent(vnode, component) { const nodeType = nodeTypeFromType(component.constructor); const rendered = rstNodesFromChildren((0, preact10_internals_js_1.getLastRenderOutput)(component)); // If this was a shallow-rendered component, set the RST node's type to the // real component function/class. const shallowRenderedType = (0, shallow_render_utils_js_1.getRealType)(component); const type = shallowRenderedType ? shallowRenderedType : component.constructor; return { nodeType, type, props: { children: [], ...component.props }, key: vnode.key || null, ref: vnode.ref || null, instance: component, rendered, }; } /** * Convert the Preact components rendered into `container` into an RST node. */ function getNode(container) { const rendered = (0, preact10_internals_js_1.getLastVNodeRenderedIntoContainer)(container); const rstNode = rstNodeFromVNode(rendered); // There is currently a requirement that the root element produces a single // RST node. Fragments do not appear in the RST tree, so it is fine if the // root node is a fragment, provided that it renders only a single child. In // fact Preact itself wraps the root element in a single-child fragment. if (Array.isArray(rstNode)) { if (rstNode.length === 1) { return rstNode[0]; } else { throw new Error('Root element must not be a fragment with multiple children'); } } else { return rstNode; } } exports.getNode = getNode;