I created a sandbox to outline the main points of interest: https://codesandbox.io/s/restless-dawn-nwy0l
Please ignore the formatting as this is just a MWE I put together.
When I run the following test in the sandbox above
import React from "react";
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import "@testing-library/jest-dom";
import Statistics from "../src/components/Block/Statistics";
import { IAction, IState } from "../src/typings/AppTypes";
import { AppReducer } from "../src/reducers/AppReducer";
import { AppContext } from "../src/context/AppContext";
import * as MineUtil from "../src/utils/mine";
import * as ConversionUtil from "../src/utils/conversion";
const state: IState = {
verifiedTrans: [
{
to: "A",
from: "B",
amount: 1.23,
message: "First Transaction",
signature: "AB1.23"
},
{
to: "A",
from: "C",
amount: 456.78,
message: "Second Transaction",
signature: "AC456.78"
},
{
to: "A",
from: "D",
amount: 999.99,
message: "Third Transaction",
signature: "AD999.99"
},
{
to: "A",
from: "E",
amount: 987.65,
message: "Forth Transaction",
signature: "AE987.65"
},
{
to: "A",
from: "F",
amount: 1.01,
message: "Fifth Transaction",
signature: "AF1.01"
}
],
selectedTrans: [
{
to: "A",
from: "C",
amount: 456.78,
message: "Second Transaction",
signature: "AC456.78"
}
],
chain: [
{
index: 0,
prevHash: "",
currHash: new Array(64).fill("0").join(""),
transactions: [],
timestamp: Date.parse("04/31/2021"),
merkleRoot: "",
valid: true
},
{
index: 1,
prevHash: new Array(64).fill("0").join(""),
currHash: new Array(64).fill("A").join(""),
transactions: [
{
to: "A",
from: "E",
amount: 987.65,
message: "Forth Transaction",
signature: "AE987.65"
}
],
timestamp: Date.parse("05/01/2021"),
merkleRoot: "987.65EForthTransactionAE987.65A",
valid: true
}
],
preview: {
index: 2,
timestamp: Date.parse("05/02/2021"),
prevHash: new Array(64).fill("A").join(""),
currHash: "",
transactions: [],
merkleRoot: "",
valid: false
}
};
const dispatch = (action: IAction) => AppReducer(state, action);
it("keeps mining button disabled after mining due to valid solution", async () => {
const solution =
"000a4fda363405b2796986a63e8cedde080e1f29ed774f5f93bd97c42b9a96fc0";
const target =
"000b4fda363405b2796986a63e8cedde080e1f29ed774f5f93bd97c42b9a96fc0";
jest.spyOn(MineUtil, "createTarget").mockReturnValue(Promise.resolve(target));
jest
.spyOn(ConversionUtil, "digestMessage")
.mockReturnValue(Promise.resolve(solution));
render(
<AppContext.Provider value={{ state, dispatch }}>
<Statistics chain={false} />
</AppContext.Provider>
);
expect(screen.getByRole("button", { name: /Block Mine/i })).toBeEnabled();
// need to await state changes
fireEvent.click(screen.getByRole("button", { name: /Block Mine/i }));
await waitFor(() => {
expect(screen.getByRole("button", { name: /Block Mine/i })).toBeDisabled();
});
await waitFor(() => {
expect(
screen.getByRole("textbox", { name: /Block Solution/i })
).toHaveClass("valid-solution");
});
});
I get:
The test simply checks that if you press the mine button once there are selected transactions, the button becomes disabled, and once a valid solution is mined it's class changes and it thus becomes green text (for this component). The button should also remain disabled as the solution is valid.
Additionally the preview block will turn green but this is outside the scope of this unit test.
However, it seems like the state preview block (which determines the class on the solution input textbox) does not update properly in the test environment. During development this is not the case and everything works as expected.
Any suggestions/hints?
In your test, you setup a mock version of value, dispatch
which does not trigger update when you click on Block mine
button.
But in actual code, in App.tsx
you use useReducer
hook (deep down it trigger re-render and new props is passed to Statistics
via Context).
To fix it, just simulate your test with useReducer
const Wrapper = () => {
const [state, dispatch] = useReducer(AppReducer, inititalValues);
return (
<AppContext.Provider value={{ state, dispatch }}>
<Statistics chain={false} />
</AppContext.Provider>
);
};
render(<Wrapper />);
inititalValues
is the variable state
on line 12
Working sandbox here