Unofficial Enzyme adapter specifically designed for React 17 applications, enabling developers to use Enzyme's testing utilities with React 17's updated architecture.
An unofficial Enzyme adapter specifically designed for React 17 applications, enabling developers to use Enzyme's testing utilities with React 17's updated architecture. This adapter acts as a bridge between Enzyme's testing framework and React 17's fiber reconciler, supporting shallow rendering, full DOM rendering, and static rendering of React components.
npm install --save-dev @wojtekmaj/enzyme-adapter-react-17import Adapter from '@wojtekmaj/enzyme-adapter-react-17';CommonJS:
const Adapter = require('@wojtekmaj/enzyme-adapter-react-17');TypeScript:
import * as Adapter from '@wojtekmaj/enzyme-adapter-react-17';
// or
import Adapter = require('@wojtekmaj/enzyme-adapter-react-17');import Enzyme from 'enzyme';
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
// Configure Enzyme to use the React 17 adapter
Enzyme.configure({ adapter: new Adapter() });
// Now use Enzyme's standard API
import { shallow, mount, render } from 'enzyme';
import MyComponent from './MyComponent';
// Shallow rendering
const wrapper = shallow(<MyComponent prop="value" />);
// Full DOM rendering
const wrapper = mount(<MyComponent prop="value" />);
// Static rendering
const wrapper = render(<MyComponent prop="value" />);The adapter extends Enzyme's EnzymeAdapter class and provides React 17-specific implementations for:
The main adapter class that extends EnzymeAdapter and provides React 17 compatibility.
/**
* React 17 Enzyme Adapter class
* Extends EnzymeAdapter to provide React 17 compatibility
*/
class ReactSeventeenAdapter extends EnzymeAdapter {
constructor();
// Renderer factory methods
createMountRenderer(options: MountRendererOptions): MountRenderer;
createShallowRenderer(options: ShallowRendererOptions): ShallowRenderer;
createStringRenderer(options: StringRendererOptions): StringRenderer;
createRenderer(options: RendererOptions): Renderer;
// Element and node manipulation
wrap(element: ReactElement): ReactWrapper;
nodeToElement(node: EnzymeNode): ReactElement | null;
elementToNode(element: ReactElement): EnzymeNode;
nodeToHostNode(node: EnzymeNode, supportsArray?: boolean): Node | Node[];
// Type checking and validation
matchesElementType(node: EnzymeNode, matchingType: ComponentType): boolean;
isValidElement(element: any): boolean;
isValidElementType(object: any): boolean;
isFragment(fragment: any): boolean;
isCustomComponent(type: ComponentType): boolean;
isContextConsumer(type: ComponentType): boolean;
isCustomComponentElement(inst: any): boolean;
// Utility methods
displayNameOfNode(node: EnzymeNode): string | null;
getProviderFromConsumer(Consumer: ComponentType): ComponentType | null;
createElement(...args: any[]): ReactElement;
wrapWithWrappingComponent(node: EnzymeNode, options: WrappingComponentOptions): EnzymeNode;
}Full DOM rendering capabilities for integration and end-to-end testing.
interface MountRenderer {
/**
* Renders a React element to the DOM
* @param el - React element to render
* @param context - Optional context for the element
* @param callback - Optional callback after rendering
*/
render(el: ReactElement, context?: any, callback?: () => void): void;
/**
* Unmounts the rendered component from the DOM
*/
unmount(): void;
/**
* Gets the rendered component tree as EnzymeNode
* @returns The root node of the rendered tree
*/
getNode(): EnzymeNode;
/**
* Simulates an error in the component tree
* @param nodeHierarchy - Array of nodes representing the error hierarchy
* @param rootNode - Root node of the component tree
* @param error - Error object to simulate
*/
simulateError(nodeHierarchy: EnzymeNode[], rootNode: EnzymeNode, error: Error): void;
/**
* Simulates a DOM event on a component node
* @param node - Target node for the event
* @param event - Event name (e.g., 'click', 'change')
* @param mock - Mock event object
*/
simulateEvent(node: EnzymeNode, event: string, mock: any): void;
/**
* Batches React updates for optimal performance
* @param fn - Function containing React updates
*/
batchedUpdates(fn: () => void): void;
/**
* Gets renderer for wrapping components
* @returns Renderer for wrapping components
*/
getWrappingComponentRenderer(): any;
}
interface MountRendererOptions {
mode?: 'mount';
attachTo?: Element;
hydrateIn?: Element;
wrappingComponentProps?: any;
// Note: suspenseFallback is NOT supported by mount renderer
[key: string]: any;
}Shallow rendering for unit testing individual components in isolation.
interface ShallowRenderer {
/**
* Renders a React element shallowly (one level deep)
* @param el - React element to render
* @param unmaskedContext - Context object for the element
* @param options - Additional rendering options
*/
render(el: ReactElement, unmaskedContext?: any, options?: ShallowRenderOptions): void;
/**
* Unmounts the shallow rendered component
*/
unmount(): void;
/**
* Gets the shallow rendered component tree
* @returns The root node of the shallow rendered tree
*/
getNode(): EnzymeNode;
/**
* Simulates an error in the shallow component tree
* @param nodeHierarchy - Array of nodes representing the error hierarchy
* @param rootNode - Root node of the component tree
* @param error - Error object to simulate
*/
simulateError(nodeHierarchy: EnzymeNode[], rootNode: EnzymeNode, error: Error): void;
/**
* Simulates events on shallow rendered components
* @param node - Target node for the event
* @param event - Event name
* @param args - Event arguments
*/
simulateEvent(node: EnzymeNode, event: string, ...args: any[]): void;
/**
* Batches updates for shallow rendering
* @param fn - Function containing updates
*/
batchedUpdates(fn: () => void): void;
/**
* Validates component prop types
* @param typeSpecs - PropTypes specification object
* @param values - Actual prop values
* @param location - Location string for error reporting
* @param hierarchy - Component hierarchy for stack traces
*/
checkPropTypes(typeSpecs: any, values: any, location: string, hierarchy: EnzymeNode[]): void;
}
interface ShallowRendererOptions {
mode?: 'shallow';
suspenseFallback?: boolean; // Must be boolean or undefined
[key: string]: any;
}
interface ShallowRenderOptions {
providerValues?: Map<any, any>;
[key: string]: any;
}Server-side rendering capabilities for static HTML generation.
interface StringRenderer {
/**
* Renders a React element to a static HTML string
* @param el - React element to render
* @param context - Optional context for server-side rendering
* @returns HTML string representation
*/
render(el: ReactElement, context?: any): string;
}
interface StringRendererOptions {
mode?: 'string';
// Note: suspenseFallback is NOT supported by string renderer
[key: string]: any;
}Options that can be passed to the adapter and its renderers.
interface RendererOptions {
/**
* Rendering mode: 'mount', 'shallow', or 'string'
*/
mode: 'mount' | 'shallow' | 'string';
/**
* Whether to use fallback content for Suspense components
* @default false
*/
suspenseFallback?: boolean;
/**
* Additional context for rendering
*/
context?: any;
/**
* Provider values for context testing
*/
providerValues?: Map<any, any>;
/**
* Additional options specific to each renderer
*/
[key: string]: any;
}
interface WrappingComponentOptions {
/**
* Wrapping component for enhanced testing scenarios
*/
wrappingComponent?: ComponentType;
/**
* Props to pass to the wrapping component
*/
wrappingComponentProps?: any;
[key: string]: any;
}Core types used throughout the adapter API.
/**
* React element type (imported from React)
*/
type ReactElement = import('react').ReactElement;
/**
* React component type
*/
type ComponentType = import('react').ComponentType;
/**
* Enzyme's internal node representation
*/
interface EnzymeNode {
nodeType: string;
type: ComponentType | string;
props: any;
key: string | null;
ref: any;
instance: any;
rendered: EnzymeNode | EnzymeNode[] | null;
}
/**
* React wrapper for mount rendering
*/
interface ReactWrapper {
find(selector: string): ReactWrapper;
props(): any;
state(): any;
simulate(event: string, ...args: any[]): ReactWrapper;
setProps(props: any): ReactWrapper;
setState(state: any): ReactWrapper;
unmount(): ReactWrapper;
update(): ReactWrapper;
[key: string]: any;
}The adapter handles various error scenarios gracefully:
// Error boundary testing
const wrapper = mount(
<ErrorBoundary>
<ComponentThatThrows />
</ErrorBoundary>
);
// Simulate errors for testing error boundaries
wrapper.find('ComponentThatThrows').simulateError(new Error('Test error'));import { mount } from 'enzyme';
import { useState } from 'react';
function HookComponent() {
const [count, setCount] = useState(0);
return (
<div>
<span>{count}</span>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
const wrapper = mount(<HookComponent />);
expect(wrapper.find('span').text()).toBe('0');
wrapper.find('button').simulate('click');
expect(wrapper.find('span').text()).toBe('1');import { mount } from 'enzyme';
import { createContext, useContext } from 'react';
const ThemeContext = createContext('light');
function ThemedComponent() {
const theme = useContext(ThemeContext);
return <div className={theme}>Themed content</div>;
}
const wrapper = mount(
<ThemeContext.Provider value="dark">
<ThemedComponent />
</ThemeContext.Provider>
);
expect(wrapper.find('div').hasClass('dark')).toBe(true);import { mount } from 'enzyme';
import { Suspense } from 'react';
const LazyComponent = React.lazy(() => import('./LazyComponent'));
const wrapper = mount(
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>,
{ suspenseFallback: true }
);
// Test fallback content
expect(wrapper.find('div').text()).toBe('Loading...');The adapter requires these peer dependencies to be installed:
enzyme: ^3.0.0 - The main Enzyme testing libraryreact: ^17.0.0-0 - React 17.xreact-dom: ^17.0.0-0 - React DOM 17.xInstallation example:
npm install --save-dev enzyme @wojtekmaj/enzyme-adapter-react-17 react@^17 react-dom@^17Install with Tessl CLI
npx tessl i tessl/npm-wojtekmaj--enzyme-adapter-react-17