Skip to content

Commit bae1d1e

Browse files
author
Raice Hannay
committed
add HookWrapper class + add updateProps function to Wrapper classes
1 parent 76b747c commit bae1d1e

19 files changed

+2084
-1691
lines changed

Diff for: .eslintrc

-6
This file was deleted.

Diff for: README.md

+22-8
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ between tests.
2020

2121
## Example
2222
```typescript jsx
23-
import { Wrapper } from "react-test-wrapper";
23+
import { screen, Wrapper } from "react-test-wrapper";
2424

2525
const component = new Wrapper(SomeComponent)
2626
.withDefaultChildren(<div className="Child" />)
@@ -30,18 +30,32 @@ const component = new Wrapper(SomeComponent)
3030
});
3131

3232
describe("when testing a scenario", () => {
33-
const { getByText } = component
34-
.withProps({
35-
prop1: "Scenario value 1"
36-
})
37-
.render();
33+
let result: ReturnType<typeof component.render>;
34+
35+
it("renders the component", () => {
36+
result = component
37+
.withProps({
38+
prop1: "Scenario value 1"
39+
})
40+
.render();
41+
});
3842

3943
it("uses the scenario-specific value for prop1", () => {
40-
expect(getByText("Scenario value 1")).toBeDefined();
44+
expect(screen.getByText("Scenario value 1")).toBeDefined();
4145
});
4246

4347
it("uses the default value for prop2", () => {
44-
expect(getByText("Default value 2")).toBeDefined();
48+
expect(screen.getByText("Default value 2")).toBeDefined();
49+
});
50+
51+
it("updates the props", () => {
52+
result.updateProps({
53+
prop1: "New scenario value 1"
54+
});
55+
});
56+
57+
it("renders the new prop value", () => {
58+
expect(screen.getByText("New scenario value 1")).toBeDefined();
4559
});
4660
});
4761
```

Diff for: docs/react-testing-library/HookWrapper.md

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
`HookWrapper`
2+
=============
3+
4+
This abstract class has a `wrapper` property 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 set the `wrapper` property to an instance of your application's
8+
pre-configured `Wrapper` class. This is to keep things scalable and able to support any type of `Wrapper`
9+
being used.
10+
11+
12+
How to extend
13+
-------------
14+
15+
To extend this class to implement your own setup functionality, you can do so as shown in the
16+
example below.
17+
18+
```typescript jsx
19+
import { THook, HookWrapper as BaseHookWrapper } from "react-test-wrapper";
20+
21+
// This must be your pre-configured `Wrapper` class, to ensure that all of the methods and types are present.
22+
import { Wrapper } from "./Wrapper.js";
23+
24+
export class HookWrapper<
25+
H extends THook,
26+
W extends Wrapper<React.ComponentType<any>>,
27+
> extends BaseHookWrapper<H, W> {
28+
constructor(hook: H) {
29+
super(hook);
30+
31+
this.wrapper = new Wrapper(this.WrappingComponent) as W;
32+
}
33+
}
34+
```
35+
36+
37+
How to use it in your tests
38+
---------------------------
39+
40+
```typescript jsx
41+
const hook = new HookWrapper(useMyStuff);
42+
43+
hook.wrapper.withDefaultReduxState({
44+
test: {
45+
valueA: "Default value A",
46+
valueB: "Default value B",
47+
},
48+
});
49+
50+
describe("useMyStuff", () => {
51+
describe("when using scenario-specific state", () => {
52+
it("mounts the hook", () => {
53+
hook.wrapper.withReduxState({
54+
test: {
55+
valueA: "Scenario value A",
56+
},
57+
});
58+
59+
hook.mount("A", 2);
60+
});
61+
62+
it("returns the expected value", () => {
63+
expect(hook.returnValue).toEqual({
64+
selectedValueA: "Scenario value A",
65+
selectedValueB: "Default value B",
66+
valueA: "A",
67+
valueB: 2,
68+
});
69+
});
70+
71+
it("updates the parameters", () => {
72+
hook.update("B", 3);
73+
});
74+
75+
it("updates the return value", () => {
76+
expect(hook.returnValue).toEqual({
77+
selectedValueA: "Scenario value A",
78+
selectedValueB: "Default value B",
79+
valueA: "B",
80+
valueB: 3,
81+
});
82+
});
83+
});
84+
});
85+
```

Diff for: docs/react-testing-library/Wrapper.md

+4
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ Sets the scenario-specific props to be used - cleared after `render` is called.
4343
Mounts the component with the `react-testing-library` `render` function, using the currently-set data.
4444
Returns the `RenderResult` returned by the `render` function.
4545

46+
In addition to the `RenderResult` values, an `updateProps` function is also returned, which wraps
47+
a call to the RTL `rerender` method in an `act`, as a convenience method to update props within the
48+
component lifecycle.
49+
4650

4751
How to extend
4852
-------------

Diff for: docs/react-testing-library/WrapperWithRedux.md

+18-10
Original file line numberDiff line numberDiff line change
@@ -15,24 +15,28 @@ For example:
1515
const component = new WrapperWithRedux(SomeComponent);
1616

1717
describe("when testing a scenario", () => {
18-
const wrapper = component
19-
.withReduxState({
20-
test: {
21-
value: "Scenario value 1"
22-
}
23-
})
24-
.render();
18+
let result: ReturnType<typeof component.render>;
19+
20+
it("renders the component", () => {
21+
result = component
22+
.withReduxState({
23+
test: {
24+
value: "Scenario value 1"
25+
}
26+
})
27+
.render();
28+
});
2529

2630
it("renders the initial value", () => {
27-
expect(wrapper.find(".SomeComponent--value").text()).toBe("Initial value");
31+
expect(screen.getByText("Initial value")).toBeDefined();
2832
});
2933

3034
it("dispatches actions.setValue", () => {
31-
wrapper.store.dispatch(actions.setValue("New value"));
35+
result.store.dispatch(actions.setValue("New value"));
3236
});
3337

3438
it("renders the updated value", () => {
35-
expect(wrapper.find(".SomeComponent--value").text()).toBe("New value");
39+
expect(screen.getByText("New value")).toBeDefined();
3640
});
3741
});
3842
```
@@ -65,6 +69,10 @@ Toggles whether arrays get merged or not in Redux state.
6569
Mounts the component with the React Testing Library `render` function, using the currently-set data.
6670
Returns a `RenderResult` instance, which also includes a `store` property.
6771

72+
In addition to the `RenderResult` values, an `updateProps` function is also returned, which wraps
73+
a call to the RTL `rerender` method in an `act`, as a convenience method to update props within the
74+
component lifecycle.
75+
6876

6977
How to extend for use in your project
7078
-------------------------------------

Diff for: jest.setup.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import "@testing-library/react/dont-cleanup-after-each";
2-
2+
import "@testing-library/jest-dom/jest-globals";
33
import { configure } from "@testing-library/react";
44

55
configure({

Diff for: package.json

+30-29
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"author": "Raice Hannay <[email protected]>",
44
"description": "A set of classes to make setting up React components for unit tests easy.",
55
"license": "ISC",
6-
"version": "4.0.0",
6+
"version": "4.1.0",
77
"keywords": [
88
"component",
99
"react-testing-library",
@@ -55,11 +55,11 @@
5555
},
5656
"homepage": "https://github.com/voodoocreation/react-test-wrapper#readme",
5757
"peerDependencies": {
58-
"@testing-library/react": ">= 14.1.2",
59-
"@types/react": "^18.2.48",
60-
"react": "^18.2.0",
61-
"react-intl": ">= 6.1.1",
62-
"react-redux": ">= 9.1.0",
58+
"@testing-library/react": ">= 16.1.0",
59+
"@types/react": "^18.3.16",
60+
"react": "^18.3.1",
61+
"react-intl": ">= 7.0.4",
62+
"react-redux": ">= 9.2.0",
6363
"redux": ">= 5.0.1"
6464
},
6565
"peerDependenciesMeta": {
@@ -74,37 +74,38 @@
7474
}
7575
},
7676
"devDependencies": {
77-
"@reduxjs/toolkit": "^2.0.1",
78-
"@testing-library/react": "^14.1.2",
79-
"@types/jest": "^29.0.3",
80-
"@types/react": "^18.2.48",
81-
"@types/react-redux": "^7.1.24",
82-
"@typescript-eslint/eslint-plugin": "^6.19.1",
83-
"@typescript-eslint/parser": "^6.19.1",
77+
"@reduxjs/toolkit": "^2.5.0",
78+
"@testing-library/dom": "^10.4.0",
79+
"@testing-library/jest-dom": "^6.6.3",
80+
"@testing-library/react": "^16.1.0",
81+
"@testing-library/user-event": "^14.5.2",
82+
"@types/jest": "^29.5.14",
83+
"@types/react": "^18.3.16",
84+
"@types/react-redux": "^7.1.34",
8485
"cross-env": "^7.0.3",
85-
"eslint": "^8.24.0",
86-
"eslint-config-voodoocreation": "^5.0.0",
87-
"eslint-plugin-import": "^2.26.0",
88-
"eslint-plugin-jest": "^27.0.4",
89-
"eslint-plugin-jsx-a11y": "^6.6.1",
86+
"eslint": "^9.17.0",
87+
"eslint-config-voodoocreation": "^7.0.1",
88+
"eslint-plugin-import": "^2.31.0",
89+
"eslint-plugin-jest": "^28.9.0",
90+
"eslint-plugin-jsx-a11y": "^6.10.2",
9091
"eslint-plugin-prefer-arrow": "^1.2.3",
91-
"eslint-plugin-react": "^7.31.8",
92-
"eslint-plugin-react-hooks": "^4.6.0",
92+
"eslint-plugin-react": "^7.37.2",
93+
"eslint-plugin-react-hooks": "^5.1.0",
9394
"jest": "^29.0.3",
9495
"jest-environment-jsdom": "^29.0.3",
9596
"npm-run-all": "^4.1.5",
96-
"prettier": "^3.2.4",
97-
"react": "^18.2.0",
98-
"react-dom": "^18.2.0",
99-
"react-intl": "^6.1.1",
100-
"react-redux": "^9.1.0",
97+
"prettier": "^3.4.2",
98+
"react": "^18.3.1",
99+
"react-dom": "^18.3.1",
100+
"react-intl": "^7.0.4",
101+
"react-redux": "^9.2.0",
101102
"redux": "^5.0.1",
102-
"rimraf": "^5.0.5",
103-
"ts-jest": "^29.0.2",
104-
"typescript": "^5.3.3"
103+
"rimraf": "^6.0.1",
104+
"ts-jest": "^29.2.5",
105+
"typescript": "^5.7.2"
105106
},
106107
"dependencies": {
107-
"ts-deepmerge": "^7.0.0",
108+
"ts-deepmerge": "^7.0.2",
108109
"utility-types": "^3.11.0"
109110
},
110111
"resolutions": {

Diff for: src/react-testing-library/HookWrapper.test.tsx

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { TestHookWrapper } from "../../test/react-testing-library/TestHookWrapper.js";
2+
import { useTestHook } from "../../test/useTestHook.js";
3+
4+
const hook = new TestHookWrapper(useTestHook);
5+
6+
hook.wrapper.withDefaultReduxState({
7+
test: {
8+
value: "Redux value",
9+
},
10+
});
11+
12+
describe("HookWrapper", () => {
13+
describe("when using default state", () => {
14+
it("mounts the hook", () => {
15+
hook.mount("A", 2);
16+
});
17+
18+
it("returns the expected value", () => {
19+
expect(hook.returnValue).toEqual({
20+
selectedValue: "Redux value",
21+
valueA: "A",
22+
valueB: 2,
23+
});
24+
});
25+
26+
it("updates the parameters", () => {
27+
hook.update("B", 3);
28+
});
29+
30+
it("updates the return value", () => {
31+
expect(hook.returnValue).toEqual({
32+
selectedValue: "Redux value",
33+
valueA: "B",
34+
valueB: 3,
35+
});
36+
});
37+
});
38+
39+
describe("when using scenario-specific state", () => {
40+
it("mounts the hook", () => {
41+
hook.wrapper.withReduxState({
42+
test: {
43+
value: "New redux value",
44+
},
45+
});
46+
47+
hook.mount("A", 2);
48+
});
49+
50+
it("returns the expected value", () => {
51+
expect(hook.returnValue).toEqual({
52+
selectedValue: "New redux value",
53+
valueA: "A",
54+
valueB: 2,
55+
});
56+
});
57+
58+
it("updates the parameters", () => {
59+
hook.update("B", 3);
60+
});
61+
62+
it("updates the return value", () => {
63+
expect(hook.returnValue).toEqual({
64+
selectedValue: "New redux value",
65+
valueA: "B",
66+
valueB: 3,
67+
});
68+
});
69+
});
70+
});

0 commit comments

Comments
 (0)