# Enzyme Shallow Rendering + useContext
## Problem
We sometimes consume global state from components.
Global state is generally accepted to be problematic in software engineering, but it's common practice in Web Applications due to the nature of browsers and legacy JavaScript practices.
## Case
We need to hide a button in `ParentComponent` if the application is being rendered in a WebView.
Given an `isWebview(): boolean` function, this is as simple as adding a `{!isWebview() && (` render condition:
```typescript=
{!isWebview() && (
<Button/>
)}
```
The problem arises when we want to test this case. We're forced to mock the implementation of the function globally:
```typescript=
import * as WebViewModule from '~/webview';
const mockWebView = (value: boolean) =>
jest.spyOn(WebViewModule, 'isWebview').mockImplementationOnce(() => value);
```
This does indeed work:
```typescript=
describe('Buttons', () => {
const testWebView = (isWebview: boolean, expectedCount: number) => {
mockWebView(isWebview);
const parentComponent = shallow(
<ParentComponent {...props} />,
);
const button = parentComponent.find(Button);
expect(button.length).toBe(expectedCount);
}
it('should show if not in a webview', () => {
testWebView(false, 1);
});
it('should not show if in a webview', () => {
testWebView(true, 0);
});
});
```
The problem with this approach is that we are globally altering the `isWebview` function, which other tests in the test suit may be using. This may not be a problem if we run our tests sequentially, but could lead to non-determinism if ran in parallel.
## Solution using React Context
```typescript=
import { createContext } from 'react';
interface WebViewContext {
readonly isWebview: () => boolean;
}
export const WebViewContext = createContext<WebViewContext>(null);
```
```typescript=
<WebViewContext.Provider value={{ isWebview }}>
...
</WebViewContext>
```
```typescript=
const { isWebview } = useContext(WebViewContext);
```
### Naive Approach
Now that our component uses `useContext`, all we need to do is to wrap the component being tested with a "mock" context provider in the test:
```typescript=
describe('Buttons in WebView', () => {
it('should show if not in a webview', () => {
const parent = shallow(
<WebViewContext.Provider value={{ isWebview: () => false }}>
<ParentComponent {...defaultProps} />,
</WebViewContext.Provider>
);
const button = parent.find(Button);
expect(button.length).toBe(1);
});
});
```
Yet this doesn't work because the shallow renderer only renders the root `WebViewContext`. `ParentComponent` itself is mocked and the child we're looking for, `Button`, does not exist in the shallow-rendered tree.
Enzyme provides two escape-hatches for this problem.
### Let's Dive In
The first one would be to use the `.dive` method. We could, for example:
```typescript=
const parentWithContext = shallow(
<WebViewContext.Provider value={{ isWebview: () => false }}>
<ParentComponent {...props} />,
</WebViewContext.Provider>
);
const parentComponent = parentWithContext.find(ParentComponent).dive();
const button = parentComponent.find(Button);
expect(button.length).toBe(1);
```
Yet running this code leads to an error being thrown: cannot destructure `isWebview` of `undefined.` No matter what we do, the `useContext(WebViewContext)` line in ParentComponent always returns `undefined`.
This happens because `dive()` internally creates a new `ShallowRenderWrapper` wrapping the node, so the new dived-into element is isolated from the Context.Provider.
In practice, it's almost equivalent to this:
```typescript=
const parentWithContext = shallow(
<WebViewContext.Provider value={{ isWebview: () => false }}>
<ParentComponent {...props} />,
</WebViewContext.Provider>
);
const parentComponent = shallow(
<ParentComponent {...props} />,
);
```
### Wrapping Up
Finally, there is one solution offered by Enzyme: `wrappingComponent`.
When calling `shallow`, an options arguments can be passed, which accepts a `wrappingComponent`.
From the docs:
> A component that will render as a parent of the node. **It can be used to provide context to the node**, among other things.
Sounds like it was specifically designed for our problem!
Let's give it a try:
```typescript=
const WebviewContextTrue = ({ children }) => (
<WebviewContext.Provider value={{ isWebview: () => true }}>
{ children }
</WebviewContext.Provider>
);
const parentComponentWithContext = shallow(
<ParentComponent {...defaultProps} />,
{ wrappingComponent: WebviewContextTrue },
);
```
Unfortunately, this does not work either. Running this test will lead to the same error being thrown.
## Why Doesn't It Work?
Unfortunately, `useContext` simply does not work with shallow rendering. The `wrappingComponent` approach was created for the legacy Context API. Compatibility with the `useContext` hook was never implemented.
This is a known issue and has been reported to Enzyme already: [useContext hook not working with shallow](https://github.com/enzymejs/enzyme/issues/2176).
### Some Context...
Historically, the Shallow Renderer was provided by React itself. Enzyme built its ShallowWrapper and `shallow()` on top of it.
Over time, the Facebook teams found themselves using the shallow renderer less and less, and thus progress on it basically stopped.
This led to Dan Abramov of the React team opening a GitHub issue, [[Shallow Renderer] Plan forward](https://github.com/facebook/react/issues/17321), to discuss what to do with it, which led to Enzyme taking over the project, moving it to [their GitHub account](https://github.com/enzymejs/react-shallow-renderer), and publishing it to NPM at [react-shallow-renderer](https://www.npmjs.com/package/react-shallow-renderer).
## Shallow Rendering Fix Attempt
ReactSixteenThreeAdapter.js `createShallowRenderer`
react-shallow-renderer
react-test-renderer/react-test-renderer-shallow.development.js `_createDispatcher`
## Other Literature
- [ReactJS Shallow Renderer Docs](https://reactjs.org/docs/shallow-renderer.html)
- [Testing ‘useContext’ React hook with Enzyme shallow](https://medium.com/7shifts-engineering-blog/testing-usecontext-react-hook-with-enzyme-shallow-da062140fc83)
- [Shallow rendering revisited: Better unit tests for React/Preact components](https://robertknight.me.uk/posts/shallow-rendering-revisited/)