UNPKG

enzyme-adapter-preact-pure

Version:

Enzyme adapter for Preact

143 lines (142 loc) 5.72 kB
import enzyme from 'enzyme'; import { Fragment, cloneElement, h } from 'preact'; import MountRenderer from './MountRenderer.js'; import ShallowRenderer from './ShallowRenderer.js'; import StringRenderer from './StringRenderer.js'; import { childElements } from './compat.js'; import { rstNodeFromElement } from './preact10-rst.js'; import RootFinder from './RootFinder.js'; import { isRSTNode, nodeToHostNode } from './util.js'; export const { EnzymeAdapter } = enzyme; export default class Adapter extends EnzymeAdapter { constructor(preactAdapterOptions = {}) { super(); // This function is only called during shallow rendering this.wrapWithWrappingComponent = (el, /** * Tip: * The use of `wrappingComponent` and `wrappingComponentProps` is discouraged. * Using those props complicates a potential future migration to a different testing library. * Instead, wrap a component like this: * ``` * shallow(<Wrapper><Component/></Wrapper>).dive() * ``` */ options = {}) => { const { wrappingComponent, wrappingComponentProps = {} } = options; if (!wrappingComponent) { return { RootFinder, node: el }; } let elementWithValidChildren; if (typeof el.props.children === 'string') { // This prevents an error when `.dive()` is used: // `TypeError: ShallowWrapper::dive() can only be called on components`. // --------------------------------------------------------------------- // VNode before: `{ type: Widget, props: { children: 'test' }, ... }` // VNode after: `{ type: Widget, props: { children: ['test'] }, ... }` elementWithValidChildren = cloneElement(el, el.props, childElements(el)); } else { elementWithValidChildren = el; } const wrappedElement = h(wrappingComponent, wrappingComponentProps, h(RootFinder, null, elementWithValidChildren)); return { RootFinder, node: wrappedElement, }; }; this.preactAdapterOptions = preactAdapterOptions; this.options = { // Prevent Enzyme's shallow renderer from manually invoking lifecycle // methods after a render. This manual invocation is needed for React // but not for the Preact adapter because we re-use the normal rendering // logic. lifecycles: { componentDidUpdate: { onSetState: false, }, }, }; // Work around a bug in Enzyme where `ShallowWrapper.getElements` calls // the `nodeToElement` method with undefined `this`. this.nodeToElement = this.nodeToElement.bind(this); if (this.preactAdapterOptions.ShallowRenderer) { this.isFragment = node => node?.type === Fragment; this.displayNameOfNode = (node) => { if (!node || !node.type) { return null; } if (this.isFragment?.(node)) { return 'Fragment'; } return typeof node.type === 'function' ? node.type.displayName || node.type.name || 'Component' : node.type; }; } } createRenderer(options) { switch (options.mode) { case 'mount': // The `attachTo` option is only supported for DOM rendering, for // consistency with React, even though the Preact adapter could easily // support it for shallow rendering. return new MountRenderer({ ...options, ...this.preactAdapterOptions, container: options.attachTo, }); case 'shallow': if (this.preactAdapterOptions.ShallowRenderer) { return new this.preactAdapterOptions.ShallowRenderer({ ...this.preactAdapterOptions, }); } else { return new ShallowRenderer({ ...this.preactAdapterOptions }); } case 'string': return new StringRenderer({ ...this.preactAdapterOptions }); default: throw new Error(`"${options.mode}" rendering is not supported`); } } nodeToElement(node) { if (!isRSTNode(node)) { return node; } const props = { ...node.props }; if (node.key) { props.key = node.key; } if (node.ref) { props.ref = node.ref; } const childElements = node.rendered.map(n => this.nodeToElement(n)); return h(node.type, props, ...childElements); } nodeToHostNode(node) { return nodeToHostNode(node); } isValidElement(el) { if (el == null || typeof el !== 'object') { return false; } if (typeof el.type !== 'string' && typeof el.type !== 'function' && el.type !== null) { return false; } if (typeof el.props !== 'object' || el.props == null) { return false; } return true; } createElement(type, props, ...children) { return h(type, props, ...children); } elementToNode(el) { return rstNodeFromElement(el, Boolean(this.preactAdapterOptions.ShallowRenderer) // preserveChildrenProp ); } }