0
# @wojtekmaj/enzyme-adapter-react-17
1
2
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.
3
4
## Package Information
5
6
- **Package Name**: @wojtekmaj/enzyme-adapter-react-17
7
- **Package Type**: npm
8
- **Language**: JavaScript (with TypeScript definitions)
9
- **Installation**: `npm install --save-dev @wojtekmaj/enzyme-adapter-react-17`
10
11
## Core Imports
12
13
```javascript
14
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
15
```
16
17
CommonJS:
18
19
```javascript
20
const Adapter = require('@wojtekmaj/enzyme-adapter-react-17');
21
```
22
23
TypeScript:
24
25
```typescript
26
import * as Adapter from '@wojtekmaj/enzyme-adapter-react-17';
27
// or
28
import Adapter = require('@wojtekmaj/enzyme-adapter-react-17');
29
```
30
31
## Basic Usage
32
33
```javascript
34
import Enzyme from 'enzyme';
35
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
36
37
// Configure Enzyme to use the React 17 adapter
38
Enzyme.configure({ adapter: new Adapter() });
39
40
// Now use Enzyme's standard API
41
import { shallow, mount, render } from 'enzyme';
42
import MyComponent from './MyComponent';
43
44
// Shallow rendering
45
const wrapper = shallow(<MyComponent prop="value" />);
46
47
// Full DOM rendering
48
const wrapper = mount(<MyComponent prop="value" />);
49
50
// Static rendering
51
const wrapper = render(<MyComponent prop="value" />);
52
```
53
54
## Architecture
55
56
The adapter extends Enzyme's `EnzymeAdapter` class and provides React 17-specific implementations for:
57
58
- **Fiber Architecture Support**: Handles React 17's internal fiber structure changes
59
- **Renderer Implementations**: Provides mount, shallow, and string renderers
60
- **Event Simulation**: Supports React 17's event system for testing
61
- **Context Handling**: Works with React 17's updated context API
62
- **Concurrent Features**: Compatible with React 17's concurrent rendering
63
64
## Capabilities
65
66
### ReactSeventeenAdapter Class
67
68
The main adapter class that extends EnzymeAdapter and provides React 17 compatibility.
69
70
```javascript { .api }
71
/**
72
* React 17 Enzyme Adapter class
73
* Extends EnzymeAdapter to provide React 17 compatibility
74
*/
75
class ReactSeventeenAdapter extends EnzymeAdapter {
76
constructor();
77
78
// Renderer factory methods
79
createMountRenderer(options: MountRendererOptions): MountRenderer;
80
createShallowRenderer(options: ShallowRendererOptions): ShallowRenderer;
81
createStringRenderer(options: StringRendererOptions): StringRenderer;
82
createRenderer(options: RendererOptions): Renderer;
83
84
// Element and node manipulation
85
wrap(element: ReactElement): ReactWrapper;
86
nodeToElement(node: EnzymeNode): ReactElement | null;
87
elementToNode(element: ReactElement): EnzymeNode;
88
nodeToHostNode(node: EnzymeNode, supportsArray?: boolean): Node | Node[];
89
90
// Type checking and validation
91
matchesElementType(node: EnzymeNode, matchingType: ComponentType): boolean;
92
isValidElement(element: any): boolean;
93
isValidElementType(object: any): boolean;
94
isFragment(fragment: any): boolean;
95
isCustomComponent(type: ComponentType): boolean;
96
isContextConsumer(type: ComponentType): boolean;
97
isCustomComponentElement(inst: any): boolean;
98
99
// Utility methods
100
displayNameOfNode(node: EnzymeNode): string | null;
101
getProviderFromConsumer(Consumer: ComponentType): ComponentType | null;
102
createElement(...args: any[]): ReactElement;
103
wrapWithWrappingComponent(node: EnzymeNode, options: WrappingComponentOptions): EnzymeNode;
104
}
105
```
106
107
### Mount Renderer
108
109
Full DOM rendering capabilities for integration and end-to-end testing.
110
111
```javascript { .api }
112
interface MountRenderer {
113
/**
114
* Renders a React element to the DOM
115
* @param el - React element to render
116
* @param context - Optional context for the element
117
* @param callback - Optional callback after rendering
118
*/
119
render(el: ReactElement, context?: any, callback?: () => void): void;
120
121
/**
122
* Unmounts the rendered component from the DOM
123
*/
124
unmount(): void;
125
126
/**
127
* Gets the rendered component tree as EnzymeNode
128
* @returns The root node of the rendered tree
129
*/
130
getNode(): EnzymeNode;
131
132
/**
133
* Simulates an error in the component tree
134
* @param nodeHierarchy - Array of nodes representing the error hierarchy
135
* @param rootNode - Root node of the component tree
136
* @param error - Error object to simulate
137
*/
138
simulateError(nodeHierarchy: EnzymeNode[], rootNode: EnzymeNode, error: Error): void;
139
140
/**
141
* Simulates a DOM event on a component node
142
* @param node - Target node for the event
143
* @param event - Event name (e.g., 'click', 'change')
144
* @param mock - Mock event object
145
*/
146
simulateEvent(node: EnzymeNode, event: string, mock: any): void;
147
148
/**
149
* Batches React updates for optimal performance
150
* @param fn - Function containing React updates
151
*/
152
batchedUpdates(fn: () => void): void;
153
154
/**
155
* Gets renderer for wrapping components
156
* @returns Renderer for wrapping components
157
*/
158
getWrappingComponentRenderer(): any;
159
}
160
161
interface MountRendererOptions {
162
mode?: 'mount';
163
attachTo?: Element;
164
hydrateIn?: Element;
165
wrappingComponentProps?: any;
166
// Note: suspenseFallback is NOT supported by mount renderer
167
[key: string]: any;
168
}
169
```
170
171
### Shallow Renderer
172
173
Shallow rendering for unit testing individual components in isolation.
174
175
```javascript { .api }
176
interface ShallowRenderer {
177
/**
178
* Renders a React element shallowly (one level deep)
179
* @param el - React element to render
180
* @param unmaskedContext - Context object for the element
181
* @param options - Additional rendering options
182
*/
183
render(el: ReactElement, unmaskedContext?: any, options?: ShallowRenderOptions): void;
184
185
/**
186
* Unmounts the shallow rendered component
187
*/
188
unmount(): void;
189
190
/**
191
* Gets the shallow rendered component tree
192
* @returns The root node of the shallow rendered tree
193
*/
194
getNode(): EnzymeNode;
195
196
/**
197
* Simulates an error in the shallow component tree
198
* @param nodeHierarchy - Array of nodes representing the error hierarchy
199
* @param rootNode - Root node of the component tree
200
* @param error - Error object to simulate
201
*/
202
simulateError(nodeHierarchy: EnzymeNode[], rootNode: EnzymeNode, error: Error): void;
203
204
/**
205
* Simulates events on shallow rendered components
206
* @param node - Target node for the event
207
* @param event - Event name
208
* @param args - Event arguments
209
*/
210
simulateEvent(node: EnzymeNode, event: string, ...args: any[]): void;
211
212
/**
213
* Batches updates for shallow rendering
214
* @param fn - Function containing updates
215
*/
216
batchedUpdates(fn: () => void): void;
217
218
/**
219
* Validates component prop types
220
* @param typeSpecs - PropTypes specification object
221
* @param values - Actual prop values
222
* @param location - Location string for error reporting
223
* @param hierarchy - Component hierarchy for stack traces
224
*/
225
checkPropTypes(typeSpecs: any, values: any, location: string, hierarchy: EnzymeNode[]): void;
226
}
227
228
interface ShallowRendererOptions {
229
mode?: 'shallow';
230
suspenseFallback?: boolean; // Must be boolean or undefined
231
[key: string]: any;
232
}
233
234
interface ShallowRenderOptions {
235
providerValues?: Map<any, any>;
236
[key: string]: any;
237
}
238
```
239
240
### String Renderer
241
242
Server-side rendering capabilities for static HTML generation.
243
244
```javascript { .api }
245
interface StringRenderer {
246
/**
247
* Renders a React element to a static HTML string
248
* @param el - React element to render
249
* @param context - Optional context for server-side rendering
250
* @returns HTML string representation
251
*/
252
render(el: ReactElement, context?: any): string;
253
}
254
255
interface StringRendererOptions {
256
mode?: 'string';
257
// Note: suspenseFallback is NOT supported by string renderer
258
[key: string]: any;
259
}
260
```
261
262
### Configuration Options
263
264
Options that can be passed to the adapter and its renderers.
265
266
```javascript { .api }
267
interface RendererOptions {
268
/**
269
* Rendering mode: 'mount', 'shallow', or 'string'
270
*/
271
mode: 'mount' | 'shallow' | 'string';
272
273
/**
274
* Whether to use fallback content for Suspense components
275
* @default false
276
*/
277
suspenseFallback?: boolean;
278
279
/**
280
* Additional context for rendering
281
*/
282
context?: any;
283
284
/**
285
* Provider values for context testing
286
*/
287
providerValues?: Map<any, any>;
288
289
/**
290
* Additional options specific to each renderer
291
*/
292
[key: string]: any;
293
}
294
295
interface WrappingComponentOptions {
296
/**
297
* Wrapping component for enhanced testing scenarios
298
*/
299
wrappingComponent?: ComponentType;
300
301
/**
302
* Props to pass to the wrapping component
303
*/
304
wrappingComponentProps?: any;
305
306
[key: string]: any;
307
}
308
```
309
310
## Types
311
312
Core types used throughout the adapter API.
313
314
```javascript { .api }
315
/**
316
* React element type (imported from React)
317
*/
318
type ReactElement = import('react').ReactElement;
319
320
/**
321
* React component type
322
*/
323
type ComponentType = import('react').ComponentType;
324
325
/**
326
* Enzyme's internal node representation
327
*/
328
interface EnzymeNode {
329
nodeType: string;
330
type: ComponentType | string;
331
props: any;
332
key: string | null;
333
ref: any;
334
instance: any;
335
rendered: EnzymeNode | EnzymeNode[] | null;
336
}
337
338
/**
339
* React wrapper for mount rendering
340
*/
341
interface ReactWrapper {
342
find(selector: string): ReactWrapper;
343
props(): any;
344
state(): any;
345
simulate(event: string, ...args: any[]): ReactWrapper;
346
setProps(props: any): ReactWrapper;
347
setState(state: any): ReactWrapper;
348
unmount(): ReactWrapper;
349
update(): ReactWrapper;
350
[key: string]: any;
351
}
352
```
353
354
## React 17 Features Support
355
356
### Concurrent Features
357
- Compatible with React 17's concurrent rendering
358
- Supports Suspense boundaries and fallback content
359
- Handles concurrent mode components correctly
360
361
### Modern React Patterns
362
- **React.memo()**: Full support for memoized components
363
- **React.lazy()**: Code splitting and dynamic imports
364
- **React.Suspense**: Loading states and error boundaries
365
- **Context API**: Provider/Consumer pattern testing
366
- **Hooks**: All React hooks work with the adapter
367
- **Forward Refs**: Reference forwarding in components
368
369
### Event System
370
- Updated event handling for React 17's event delegation changes
371
- Proper event simulation with React's SyntheticEvent system
372
- Support for both legacy and modern event patterns
373
374
## Error Handling
375
376
The adapter handles various error scenarios gracefully:
377
378
```javascript
379
// Error boundary testing
380
const wrapper = mount(
381
<ErrorBoundary>
382
<ComponentThatThrows />
383
</ErrorBoundary>
384
);
385
386
// Simulate errors for testing error boundaries
387
wrapper.find('ComponentThatThrows').simulateError(new Error('Test error'));
388
```
389
390
## Common Usage Patterns
391
392
### Testing Hooks with mount()
393
```javascript
394
import { mount } from 'enzyme';
395
import { useState } from 'react';
396
397
function HookComponent() {
398
const [count, setCount] = useState(0);
399
return (
400
<div>
401
<span>{count}</span>
402
<button onClick={() => setCount(count + 1)}>Increment</button>
403
</div>
404
);
405
}
406
407
const wrapper = mount(<HookComponent />);
408
expect(wrapper.find('span').text()).toBe('0');
409
wrapper.find('button').simulate('click');
410
expect(wrapper.find('span').text()).toBe('1');
411
```
412
413
### Testing Context Providers
414
```javascript
415
import { mount } from 'enzyme';
416
import { createContext, useContext } from 'react';
417
418
const ThemeContext = createContext('light');
419
420
function ThemedComponent() {
421
const theme = useContext(ThemeContext);
422
return <div className={theme}>Themed content</div>;
423
}
424
425
const wrapper = mount(
426
<ThemeContext.Provider value="dark">
427
<ThemedComponent />
428
</ThemeContext.Provider>
429
);
430
expect(wrapper.find('div').hasClass('dark')).toBe(true);
431
```
432
433
### Testing Suspense Components
434
```javascript
435
import { mount } from 'enzyme';
436
import { Suspense } from 'react';
437
438
const LazyComponent = React.lazy(() => import('./LazyComponent'));
439
440
const wrapper = mount(
441
<Suspense fallback={<div>Loading...</div>}>
442
<LazyComponent />
443
</Suspense>,
444
{ suspenseFallback: true }
445
);
446
447
// Test fallback content
448
expect(wrapper.find('div').text()).toBe('Loading...');
449
```
450
451
## Peer Dependencies
452
453
The adapter requires these peer dependencies to be installed:
454
455
- `enzyme: ^3.0.0` - The main Enzyme testing library
456
- `react: ^17.0.0-0` - React 17.x
457
- `react-dom: ^17.0.0-0` - React DOM 17.x
458
459
Installation example:
460
```bash
461
npm install --save-dev enzyme @wojtekmaj/enzyme-adapter-react-17 react@^17 react-dom@^17
462
```