enzyme-adapter-preact-pure
Version:
Enzyme adapter for Preact
87 lines (86 loc) • 3.35 kB
JavaScript
import { isValidElement } from 'preact';
import { propFromEvent } from '../../src/util.js';
import PreactShallowDiff from './compat-shallow-renderer/PreactShallowRenderer.js';
import { flushRenders, installHook as installDebounceHook, } from '../../src/debounce-render-hook.js';
import { nodeTypeFromType, rstNodeFromElement, } from '../../src/preact10-rst.js';
function isErrorBoundary({ instance, type }) {
if (typeof type === 'function' &&
type.getDerivedStateFromError) {
return true;
}
return instance && instance.componentDidCatch;
}
/**
* A shallow renderer that natively shallow renders Preact components by not
* relying on a document and overriding children to return null. It relies on a
* copy of Preact's diff algorithm, modified to not descend and diff children of
* the given element.
*/
export default class CompatShallowRenderer {
constructor() {
this._renderer = PreactShallowDiff.createRenderer();
this._cachedNode = null;
installDebounceHook();
}
render(el, context, options) {
this._cachedNode = el;
if (typeof el.type !== 'string') {
return this._renderer.render(el, context);
}
}
simulateError(nodeHierarchy, rootNode, error) {
nodeHierarchy = nodeHierarchy.concat(rstNodeFromElement(this._cachedNode));
const { instance: catchingInstance, type: catchingType } = nodeHierarchy.find(isErrorBoundary) || {};
const componentDidCatch = catchingInstance?.componentDidCatch;
const getDerivedStateFromError = catchingType
?.getDerivedStateFromError;
if (!catchingInstance ||
!catchingType ||
(!componentDidCatch && !getDerivedStateFromError)) {
throw error;
}
if (getDerivedStateFromError) {
const stateUpdate = getDerivedStateFromError.call(catchingType, error);
catchingInstance.setState(stateUpdate);
}
if (componentDidCatch) {
componentDidCatch.call(catchingInstance, error, {
componentStack: 'Test component stack',
});
}
}
simulateEvent(node, eventName, args) {
const handler = node.props[propFromEvent(eventName)];
if (handler) {
handler(args);
}
}
unmount() {
this._renderer.unmount();
}
getNode() {
if (this._cachedNode == null || !isValidElement(this._cachedNode)) {
return null;
}
flushRenders();
// The output of DOM elements is props.children whereas for components its
// from the renderer
const output = typeof this._cachedNode.type === 'string'
? this._cachedNode.props.children
: this._renderer.getRenderOutput();
return {
nodeType: nodeTypeFromType(this._cachedNode.type),
type: this._cachedNode.type,
props: this._cachedNode.props,
key: this._cachedNode.key ?? undefined,
ref: this._cachedNode.ref,
instance: this._renderer.getMountedInstance(),
rendered: Array.isArray(output)
? output.flat().map(el => rstNodeFromElement(el, true))
: [rstNodeFromElement(output, true)],
};
}
batchedUpdates(fn) {
fn();
}
}