Creating a plugin for enzyme-context
is relatively straightforward because an enzyme-context
plugin is just a function!
(node, options) => PluginReturns
node
(React.ReactElement
): The react element passed tomount
/shallow
options
(Object
): The options passed tomount
/shallow
(inclduing any custom options that your plugin wants to handle.)
-
An Object with the following attributes
-
node
(React.ReactElement
): the react element to mount -
controller
(any
): the object this plugin wants to attach to the Enzyme wrapper. For example:class Person { sayHello() { return 'Hello!'; } } const myPlugin = (node, options) => { const person = new Person(); return { node, options, controller: person, }; }; const mount = createMount({ p: myPlugin, }); const wrapper = mount(<MyComponent />); wrapper.p.sayHello();
-
options
(Object
): options to feed intomount()
/enzyme()
. This is how plugins provide context. For example:const myPlugin = (node, options) => { const person = new Person(); return { node, controller: person, options: { ...options, context: { ...options.context, person, }, }, }; }; const mount = createMount({ p: myPlugin }); function MyComponent(props, context) { return <button onClick={() => context.person.sayHello()}>Say Hello</button>; } MyComponent.childContextTypes = { person: PropTypes.instanceOf(Person) }; const wrapper = mount(<MyComponent />); wrapper.find('button').simulate('click');
-
updater
((wrapper: ReactWrapper | ShallowWrapper) => () => void
[optional]): This function will be called immediately after the enzyme wrapper is created. It can be used to setup listeners that update the wrapper after it has been created. For example, if our plugin was supplying context that contained a list of all thewindow.postMessage
events we've received, we could do something like this:const postMessagePlugin = (node, options) => { return { node, controller: null, options: { ...options, context: { ...options.context, messages: [], }, }, updater: wrapper => { const listener = event => { wrapper.setContext({ messages: wrapper.context('messages').concat([event.data]), }); }; // listen for message events and update the enzyme wrapper when they are // received. window.addEventListener('message', listener, false); // Return a function that will get called when the component is unmounted. return () => { // Clean up the listener on unmount to avoid a memory leak! window.removeEventListener('message', listener, false); }; }, }; };
-
Sometimes it is useful for your plugin to accept global (as opposed to passed to mount
/shallow
) configuration. This can be accomplished simply by exporting a factory for your plugin from your module:
Before:
const myPlugin = (node, options) => {
const controller = new Controller(/* how do we pass global config here?? */);
return {
node,
options,
controller,
};
};
const mount = createMount({ plugin: myPlugin });
After
const myPlugin = config => (node, options) => {
const controller = new Controller(config);
return {
node,
options,
controller,
};
};
const mount = createMount({ plugin: myPlugin({ some: 'config' }) });
I would even recommend that you export a factory for your plugin even if it doesn't accept any configuration. Then, if you need to accept configuration in the future, you can do so without introducing a breaking change to your API.
Enzyme Context has published a library called enzyme-context-utils with some helpful utilities for authoring enzyme-context plugins.