Skip to content

Commit 650bf6e

Browse files
author
Raice Hannay
committed
add docs + prepare for release
1 parent 489b467 commit 650bf6e

17 files changed

+423
-92
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ lib
1111
node_modules
1212
npm-debug.log
1313
yarn.error.l
14+
*.tgz
1415

1516
#IDEs
1617
.idea

.prettierignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
coverage/*
22
dist/*
3+
docs/*
34
package.json
45
yarn.lock
56
README.md

README.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
React Test Wrapper
2+
==================
3+
4+
The intention of this package is to provide a simple, clean way of setting up your React components
5+
for unit tests, to reduce test setup boilerplate code, whilst automatically inferring your component
6+
TypeScript definitions (props, instance methods etc) to avoid needing to manually import types and
7+
retype definitions in your tests and also providing better inline IDE autocomplete/validation support.
8+
9+
The classes provided are also able to be extended to add to the API that's available, if your
10+
project requires additional functionality as part of your component test setup.
11+
12+
The concept behind it is that you can create a single instance of the wrapper class at the top of
13+
your test file and define the defaults to use there, then in each test scenario you can reference
14+
the single instance and define the scenario-specific props/children etc. chaining the public methods,
15+
then finally calling the `mount`, `shallow` or `render` method to return the Enzyme wrapper and merged props.
16+
17+
The scenario-specific definitions are reset each time you call `mount`, `render` or `shallow`, which
18+
will ensure it reverts back to only the defaults set at the top and prevents scenario data from leaking
19+
between tests.
20+
21+
For example:
22+
```typescript jsx
23+
const component = new Wrapper(SomeComponent)
24+
.withDefaultChildren(<div className="Child" />)
25+
.withDefaultProps({
26+
prop1: "Default value 1",
27+
prop2: "Default value 2"
28+
});
29+
30+
describe("when testing a scenario", () => {
31+
const wrapper = component
32+
.withProps({
33+
prop1: "Scenario value 1"
34+
})
35+
.mount();
36+
37+
it("uses the scenario-specific value for prop1", () => {
38+
expect(wrapper.find(".SomeComponent--prop1").text()).toBe("Scenario value 1");
39+
});
40+
41+
it("uses the default value for prop2", () => {
42+
expect(wrapper.find(".SomeComponent--prop2").text()).toBe("Default value 1");
43+
});
44+
});
45+
```
46+
47+
48+
The classes
49+
-----------
50+
51+
- [`Wrapper`](./docs/Wrapper.md)
52+
- [`WrapperWithIntl`](./docs/WrapperWithIntl.md)
53+
- [`WrapperWithRedux`](./docs/WrapperWithRedux.md)

docs/Wrapper.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
`Wrapper`
2+
=========
3+
4+
This is the base class which is used for basic presentational components. The other classes provided
5+
extend this one, so all methods defined on this class are also available on the others.
6+
7+
As with all of these classes, you can extend it to implement your own additional functionality.
8+
9+
Public read-only properties
10+
---------------------------
11+
12+
### `props`
13+
The props the component was mounted with. Useful for avoiding needing to declare local variables
14+
for later assertion reference - especially mock function instances.
15+
16+
17+
Public methods
18+
--------------
19+
20+
### `withDefaultChildren`
21+
Sets the default children to be used for the wrapper instance.
22+
23+
### `withDefaultProps`
24+
Sets the default props to be used for the wrapper instance.
25+
26+
### `withChildren`
27+
Sets the scenario-specific children to be used - cleared after `mount`, `render` or `shallow` are called.
28+
29+
### `withProps`
30+
Sets the scenario-specific props to be used - cleared after `mount`, `render` or `shallow` are called.
31+
32+
### `mount`
33+
Mounts the component with the Enzyme `mount` function, using the currently-set data.
34+
Returns a `ReactWrapper` instance.
35+
36+
### `render`
37+
Mounts the component with the Enzyme `render` function, using the currently-set data.
38+
Returns a `ShallowWrapper` instance.
39+
40+
### `shallow`
41+
Mounts the component with the Enzyme `shallow` function, using the currently-set data.
42+
Returns a `Cheerio` instance.
43+
44+
How to extend
45+
-------------
46+
47+
To extend this class to implement your own setup functionality, you can do so as shown in the
48+
example below.
49+
50+
There's a `WrappingComponent` property on the `Wrapper` class that will automatically be used by
51+
`mount`, `render` and `shallow` when it's defined, to wrap the component being tested.
52+
This is very useful when testing components that require some form of context provider component to
53+
exist in the React tree.
54+
55+
```typescript jsx
56+
import * as React from "react";
57+
import { Wrapper as BaseWrapper } from "react-test-wrapper";
58+
59+
export class WrapperWithCustomStuff<
60+
C extends React.ComponentType<any>,
61+
P extends React.ComponentProps<C> = React.ComponentProps<C>
62+
> extends BaseWrapper<C, P> {
63+
protected WrappingComponent: React.FC = ({ children }) => (
64+
<SomeProviderComponent>
65+
{children}
66+
</SomeProviderComponent>
67+
);
68+
69+
// Add custom properties and methods here
70+
}
71+
```

docs/WrapperWithIntl.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
`WrapperWithIntl`
2+
=================
3+
4+
This abstract class has a `WrappingComponent` defined which wraps the component in an `IntlProvider`,
5+
passing in `intlProviderProps`.
6+
7+
To use this class, you have to extend it and define your own `intlProviderProps` to set your own
8+
`messages` etc.
9+
10+
11+
Public methods
12+
--------------
13+
The public methods are identical to the base [`Wrapper`](./Wrapper.md) class.
14+
15+
16+
How to extend for use in your project
17+
-------------------------------------
18+
19+
```typescript jsx
20+
import * as React from "react";
21+
import { IntlConfig } from "react-intl";
22+
import { WrapperWithIntl as BaseWrapper } from "react-test-wrapper";
23+
24+
import messages from "./locales/en-NZ";
25+
26+
export class WrapperWithIntl<
27+
C extends React.ComponentType<any>,
28+
P extends React.ComponentProps<C> = React.ComponentProps<C>
29+
> extends BaseWrapper<C, P> {
30+
protected intlProviderProps: Partial<IntlConfig> = {
31+
messages
32+
};
33+
}
34+
```

docs/WrapperWithRedux.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
`WrapperWithRedux`
2+
=================
3+
4+
This abstract class has a `WrappingComponent` defined which wraps the component in a Redux `Provider`,
5+
passing in the return value from the `createStore` method.
6+
7+
To use this class, you have to extend it and define your own `createStore` method to return an
8+
instance of your Redux store, using the `initialState` and `middlewares` that this class provides -
9+
it is important to ensure that your store uses these, because if it disregards them, none of the
10+
methods this class provides will function.
11+
12+
13+
Public read-only properties
14+
---------------------------
15+
16+
### `reduxHistory`
17+
An array of actions that have been dispatched, used when asserting that actions have been
18+
dispatched as expected during interactions or in the component lifecycle.
19+
20+
### `store`
21+
The instance of the Redux store used. However, this is only available after calling `mount`.
22+
If accessed prior to mounting your component, it will be `undefined`.
23+
24+
25+
Public methods
26+
--------------
27+
28+
In addition to the public methods provided by the base [`Wrapper`](./Wrapper.md) class, the following
29+
methods are available.
30+
31+
Note:
32+
While technically the `render` and `shallow` methods are accessible, they will throw an error
33+
due to issues with the integration of the Redux store provider.
34+
35+
`render` doesn't work due to its use of `useLayout`, which doesn't work in a test environment.
36+
`shallow` isn't useful due to it only mounting the Redux provider component.
37+
38+
### `withDefaultReduxState`
39+
Sets the default Redux store state to be used for the wrapper instance.
40+
41+
### `withReduxState`
42+
Sets the scenario-specific Redux store state to be used - cleared after `mount`, `render` or `shallow` are called.
43+
44+
45+
How to extend for use in your project
46+
-------------------------------------
47+
48+
```typescript jsx
49+
import * as React from "react";
50+
import { IntlConfig } from "react-intl";
51+
import { WrapperWithRedux as BaseWrapper } from "react-test-wrapper";
52+
53+
import { createStore } from "../store/createStore";
54+
55+
class Wrapper<
56+
C extends React.ComponentType<any>,
57+
S extends {} = TStoreState,
58+
P extends React.ComponentProps<C> = React.ComponentProps<C>
59+
> extends WrapperWithRedux<C, S, P> {
60+
protected createStore(
61+
initialState: DeepPartial<S>,
62+
middlewares: Middleware[]
63+
) {
64+
return createStore(initialState, middlewares);
65+
}
66+
}
67+
```

package.json

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,24 +21,26 @@
2121
"unit-testing"
2222
],
2323
"main": "dist/index.js",
24-
"module": "lib/index.js",
24+
"files": [
25+
"dist/**/*"
26+
],
2527
"scripts": {
26-
"build": "rollup -c && tsc",
2728
"format": "prettier --write \"**/*.{js,jsx,json,ts,tsx}\"",
2829
"lint": "tslint \"./{types,src,pages}/**/*.ts?(x)\"",
2930
"test": "cross-env NODE_ENV=test jest --no-cache --config ./jest.config.js",
3031
"test:all": "npm-run-all format typecheck lint test:coverage",
3132
"test:coverage": "cross-env NODE_ENV=test jest --no-cache --coverage --config ./jest.config.js",
3233
"typecheck": "tsc"
3334
},
34-
"prepublish": "build",
35+
"prepublish": "tsc",
3536
"repository": {
3637
"type": "git",
3738
"url": "[email protected]:voodoocreation/react-test-wrapper.git"
3839
},
3940
"bugs": {
4041
"url": "https://github.com/voodoocreation/react-test-wrapper/issues"
4142
},
43+
"homepage": "https://github.com/voodoocreation/react-test-wrapper#readme",
4244
"types": "dist/index.d.ts",
4345
"peerDependencies": {
4446
"enzyme": "^3.10.0",
@@ -77,9 +79,6 @@
7779
"react-intl": "^3.9.1",
7880
"react-redux": "^7.1.3",
7981
"redux": "^4.0.4",
80-
"rollup": "^1.27.9",
81-
"rollup-plugin-terser": "^5.1.2",
82-
"rollup-plugin-typescript2": "^0.25.3",
8382
"ts-jest": "^24.2.0",
8483
"tslint": "^5.20.0",
8584
"tslint-config-prettier": "^1.18.0",

rollup.config.js

Lines changed: 0 additions & 24 deletions
This file was deleted.

src/Wrapper.test.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ describe("Wrapper", () => {
1111
});
1212

1313
describe("when using 'shallow'", () => {
14-
const { wrapper } = component.shallow();
14+
const wrapper = component.shallow();
1515

1616
it("returns ShallowWrapper", () => {
1717
expect(wrapper).toBeInstanceOf(ShallowWrapper);
@@ -27,7 +27,7 @@ describe("Wrapper", () => {
2727
});
2828

2929
describe("when using 'mount'", () => {
30-
const { wrapper } = component.mount();
30+
const wrapper = component.mount();
3131

3232
it("returns ReactWrapper", () => {
3333
expect(wrapper).toBeInstanceOf(ReactWrapper);
@@ -43,7 +43,7 @@ describe("Wrapper", () => {
4343
});
4444

4545
describe("when using 'render'", () => {
46-
const { wrapper } = component.render();
46+
const wrapper = component.render();
4747

4848
it("returns Cheerio wrapper", () => {
4949
expect(wrapper.cheerio).toBe("[cheerio object]");
@@ -65,15 +65,15 @@ describe("Wrapper", () => {
6565
);
6666

6767
it("renders default children correctly", () => {
68-
const { wrapper } = component.render();
68+
const wrapper = component.render();
6969

7070
expect(wrapper.find(".Dummy--children").html()).toBe(
7171
"<div>Default children</div>"
7272
);
7373
});
7474

7575
it("renders test-specific children correctly", () => {
76-
const { wrapper } = component
76+
const wrapper = component
7777
.withChildren(<span>Test children</span>)
7878
.render();
7979

@@ -83,7 +83,7 @@ describe("Wrapper", () => {
8383
});
8484

8585
it("clears test-specific children after previous test and renders default children again", () => {
86-
const { wrapper } = component.render();
86+
const wrapper = component.render();
8787

8888
expect(wrapper.find(".Dummy--children").html()).toBe(
8989
"<div>Default children</div>"
@@ -97,24 +97,27 @@ describe("Wrapper", () => {
9797
});
9898

9999
it("renders with default props correctly", () => {
100-
const { wrapper } = component.render();
100+
const wrapper = component.render();
101101

102+
expect(component.props).toEqual({ value: "Default value" });
102103
expect(wrapper.find(".Dummy--value").text()).toBe("Default value");
103104
});
104105

105106
it("renders with test-specific props correctly", () => {
106-
const { wrapper } = component
107+
const wrapper = component
107108
.withProps({
108109
value: "Test value"
109110
})
110111
.render();
111112

113+
expect(component.props).toEqual({ value: "Test value" });
112114
expect(wrapper.find(".Dummy--value").text()).toBe("Test value");
113115
});
114116

115117
it("clears test-specific props after previous test and uses default props again", () => {
116-
const { wrapper } = component.render();
118+
const wrapper = component.render();
117119

120+
expect(component.props).toEqual({ value: "Default value" });
118121
expect(wrapper.find(".Dummy--value").text()).toBe("Default value");
119122
});
120123
});

0 commit comments

Comments
 (0)