Skip to content

Commit

Permalink
add HookWrapper class + add updateProps function to Wrapper classes
Browse files Browse the repository at this point in the history
  • Loading branch information
Raice Hannay committed Dec 16, 2024
1 parent 76b747c commit bae1d1e
Show file tree
Hide file tree
Showing 19 changed files with 2,084 additions and 1,691 deletions.
6 changes: 0 additions & 6 deletions .eslintrc

This file was deleted.

30 changes: 22 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ between tests.

## Example
```typescript jsx
import { Wrapper } from "react-test-wrapper";
import { screen, Wrapper } from "react-test-wrapper";

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

describe("when testing a scenario", () => {
const { getByText } = component
.withProps({
prop1: "Scenario value 1"
})
.render();
let result: ReturnType<typeof component.render>;

it("renders the component", () => {
result = component
.withProps({
prop1: "Scenario value 1"
})
.render();
});

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

it("uses the default value for prop2", () => {
expect(getByText("Default value 2")).toBeDefined();
expect(screen.getByText("Default value 2")).toBeDefined();
});

it("updates the props", () => {
result.updateProps({
prop1: "New scenario value 1"
});
});

it("renders the new prop value", () => {
expect(screen.getByText("New scenario value 1")).toBeDefined();
});
});
```
Expand Down
85 changes: 85 additions & 0 deletions docs/react-testing-library/HookWrapper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
`HookWrapper`
=============

This abstract class has a `wrapper` property defined which wraps the component in a Redux `Provider`,
passing in the return value from the `createStore` method.

To use this class, you have to extend it and set the `wrapper` property to an instance of your application's
pre-configured `Wrapper` class. This is to keep things scalable and able to support any type of `Wrapper`
being used.


How to extend
-------------

To extend this class to implement your own setup functionality, you can do so as shown in the
example below.

```typescript jsx
import { THook, HookWrapper as BaseHookWrapper } from "react-test-wrapper";

// This must be your pre-configured `Wrapper` class, to ensure that all of the methods and types are present.
import { Wrapper } from "./Wrapper.js";

export class HookWrapper<
H extends THook,
W extends Wrapper<React.ComponentType<any>>,
> extends BaseHookWrapper<H, W> {
constructor(hook: H) {
super(hook);

this.wrapper = new Wrapper(this.WrappingComponent) as W;
}
}
```


How to use it in your tests
---------------------------

```typescript jsx
const hook = new HookWrapper(useMyStuff);

hook.wrapper.withDefaultReduxState({
test: {
valueA: "Default value A",
valueB: "Default value B",
},
});

describe("useMyStuff", () => {
describe("when using scenario-specific state", () => {
it("mounts the hook", () => {
hook.wrapper.withReduxState({
test: {
valueA: "Scenario value A",
},
});

hook.mount("A", 2);
});

it("returns the expected value", () => {
expect(hook.returnValue).toEqual({
selectedValueA: "Scenario value A",
selectedValueB: "Default value B",
valueA: "A",
valueB: 2,
});
});

it("updates the parameters", () => {
hook.update("B", 3);
});

it("updates the return value", () => {
expect(hook.returnValue).toEqual({
selectedValueA: "Scenario value A",
selectedValueB: "Default value B",
valueA: "B",
valueB: 3,
});
});
});
});
```
4 changes: 4 additions & 0 deletions docs/react-testing-library/Wrapper.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ Sets the scenario-specific props to be used - cleared after `render` is called.
Mounts the component with the `react-testing-library` `render` function, using the currently-set data.
Returns the `RenderResult` returned by the `render` function.

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


How to extend
-------------
Expand Down
28 changes: 18 additions & 10 deletions docs/react-testing-library/WrapperWithRedux.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,24 +15,28 @@ For example:
const component = new WrapperWithRedux(SomeComponent);

describe("when testing a scenario", () => {
const wrapper = component
.withReduxState({
test: {
value: "Scenario value 1"
}
})
.render();
let result: ReturnType<typeof component.render>;

it("renders the component", () => {
result = component
.withReduxState({
test: {
value: "Scenario value 1"
}
})
.render();
});

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

it("dispatches actions.setValue", () => {
wrapper.store.dispatch(actions.setValue("New value"));
result.store.dispatch(actions.setValue("New value"));
});

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

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


How to extend for use in your project
-------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion jest.setup.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import "@testing-library/react/dont-cleanup-after-each";

import "@testing-library/jest-dom/jest-globals";
import { configure } from "@testing-library/react";

configure({
Expand Down
59 changes: 30 additions & 29 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"author": "Raice Hannay <voodoocreation@gmail.com>",
"description": "A set of classes to make setting up React components for unit tests easy.",
"license": "ISC",
"version": "4.0.0",
"version": "4.1.0",
"keywords": [
"component",
"react-testing-library",
Expand Down Expand Up @@ -55,11 +55,11 @@
},
"homepage": "https://github.com/voodoocreation/react-test-wrapper#readme",
"peerDependencies": {
"@testing-library/react": ">= 14.1.2",
"@types/react": "^18.2.48",
"react": "^18.2.0",
"react-intl": ">= 6.1.1",
"react-redux": ">= 9.1.0",
"@testing-library/react": ">= 16.1.0",
"@types/react": "^18.3.16",
"react": "^18.3.1",
"react-intl": ">= 7.0.4",
"react-redux": ">= 9.2.0",
"redux": ">= 5.0.1"
},
"peerDependenciesMeta": {
Expand All @@ -74,37 +74,38 @@
}
},
"devDependencies": {
"@reduxjs/toolkit": "^2.0.1",
"@testing-library/react": "^14.1.2",
"@types/jest": "^29.0.3",
"@types/react": "^18.2.48",
"@types/react-redux": "^7.1.24",
"@typescript-eslint/eslint-plugin": "^6.19.1",
"@typescript-eslint/parser": "^6.19.1",
"@reduxjs/toolkit": "^2.5.0",
"@testing-library/dom": "^10.4.0",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.1.0",
"@testing-library/user-event": "^14.5.2",
"@types/jest": "^29.5.14",
"@types/react": "^18.3.16",
"@types/react-redux": "^7.1.34",
"cross-env": "^7.0.3",
"eslint": "^8.24.0",
"eslint-config-voodoocreation": "^5.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jest": "^27.0.4",
"eslint-plugin-jsx-a11y": "^6.6.1",
"eslint": "^9.17.0",
"eslint-config-voodoocreation": "^7.0.1",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jest": "^28.9.0",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-prefer-arrow": "^1.2.3",
"eslint-plugin-react": "^7.31.8",
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-hooks": "^5.1.0",
"jest": "^29.0.3",
"jest-environment-jsdom": "^29.0.3",
"npm-run-all": "^4.1.5",
"prettier": "^3.2.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-intl": "^6.1.1",
"react-redux": "^9.1.0",
"prettier": "^3.4.2",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-intl": "^7.0.4",
"react-redux": "^9.2.0",
"redux": "^5.0.1",
"rimraf": "^5.0.5",
"ts-jest": "^29.0.2",
"typescript": "^5.3.3"
"rimraf": "^6.0.1",
"ts-jest": "^29.2.5",
"typescript": "^5.7.2"
},
"dependencies": {
"ts-deepmerge": "^7.0.0",
"ts-deepmerge": "^7.0.2",
"utility-types": "^3.11.0"
},
"resolutions": {
Expand Down
70 changes: 70 additions & 0 deletions src/react-testing-library/HookWrapper.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { TestHookWrapper } from "../../test/react-testing-library/TestHookWrapper.js";
import { useTestHook } from "../../test/useTestHook.js";

const hook = new TestHookWrapper(useTestHook);

hook.wrapper.withDefaultReduxState({
test: {
value: "Redux value",
},
});

describe("HookWrapper", () => {
describe("when using default state", () => {
it("mounts the hook", () => {
hook.mount("A", 2);
});

it("returns the expected value", () => {
expect(hook.returnValue).toEqual({
selectedValue: "Redux value",
valueA: "A",
valueB: 2,
});
});

it("updates the parameters", () => {
hook.update("B", 3);
});

it("updates the return value", () => {
expect(hook.returnValue).toEqual({
selectedValue: "Redux value",
valueA: "B",
valueB: 3,
});
});
});

describe("when using scenario-specific state", () => {
it("mounts the hook", () => {
hook.wrapper.withReduxState({
test: {
value: "New redux value",
},
});

hook.mount("A", 2);
});

it("returns the expected value", () => {
expect(hook.returnValue).toEqual({
selectedValue: "New redux value",
valueA: "A",
valueB: 2,
});
});

it("updates the parameters", () => {
hook.update("B", 3);
});

it("updates the return value", () => {
expect(hook.returnValue).toEqual({
selectedValue: "New redux value",
valueA: "B",
valueB: 3,
});
});
});
});
Loading

0 comments on commit bae1d1e

Please sign in to comment.