Skip to content

Commit 3382d30

Browse files
authored
test(react, vue): useContractMutation hook and composable (#736)
1 parent 6cf1fd8 commit 3382d30

2 files changed

Lines changed: 240 additions & 0 deletions

File tree

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import { ChainProvider } from "../contexts/chain.js";
2+
import { ReactiveDotProvider } from "../contexts/provider.js";
3+
import { SignerProvider } from "../contexts/signer.js";
4+
import { useContractMutation } from "./use-contract-mutation.js";
5+
import {
6+
defineConfig,
7+
defineContract,
8+
idle,
9+
pending,
10+
} from "@reactive-dot/core";
11+
import { act, renderHook } from "@testing-library/react";
12+
import { atom } from "jotai";
13+
import type { PolkadotSigner, TxEvent } from "polkadot-api";
14+
import { from, of, throwError } from "rxjs";
15+
import { concatMap, delay } from "rxjs/operators";
16+
import { afterEach, beforeEach, expect, it, vi } from "vitest";
17+
18+
const mockSignSubmitAndWatch = vi.fn();
19+
20+
vi.mock("./use-typed-api.js", () => ({
21+
typedApiAtom: vi.fn(() => atom()),
22+
}));
23+
24+
vi.mock("./use-ink-client.js", () => ({
25+
inkClientAtom: vi.fn(() => atom()),
26+
}));
27+
28+
vi.mock(
29+
"@reactive-dot/core/internal/actions.js",
30+
async (getOriginalModule) => ({
31+
...(await getOriginalModule()),
32+
getInkContractTx: vi.fn(async () => ({
33+
signSubmitAndWatch: mockSignSubmitAndWatch,
34+
})),
35+
}),
36+
);
37+
38+
vi.useFakeTimers();
39+
40+
beforeEach(() =>
41+
mockSignSubmitAndWatch.mockReturnValue(
42+
from<Partial<TxEvent>[]>([
43+
{ type: "signed" },
44+
{ type: "broadcasted" },
45+
{ type: "txBestBlocksState" },
46+
{ type: "finalized" },
47+
]).pipe(concatMap((item) => of(item).pipe(delay(1000)))),
48+
),
49+
);
50+
51+
afterEach(() => {
52+
vi.restoreAllMocks();
53+
});
54+
55+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
56+
const testContract = defineContract({ descriptor: {} as any });
57+
58+
it("sign submit and watch", async () => {
59+
const { result } = await act(() =>
60+
renderHook(
61+
() =>
62+
useContractMutation((mutate) =>
63+
mutate(testContract, "0x", "test_message", {}),
64+
),
65+
{
66+
wrapper: ({ children }) => (
67+
<ReactiveDotProvider config={defineConfig({ chains: {} })}>
68+
<ChainProvider chainId="test_chain">
69+
<SignerProvider signer={{} as PolkadotSigner}>
70+
{children}
71+
</SignerProvider>
72+
</ChainProvider>
73+
</ReactiveDotProvider>
74+
),
75+
},
76+
),
77+
);
78+
79+
expect(result.current[0]).toBe(idle);
80+
81+
await act(() => result.current[1]());
82+
83+
expect(result.current[0]).toBe(pending);
84+
85+
await act(() => vi.advanceTimersByTime(1000));
86+
87+
expect(result.current[0]).toMatchObject({ type: "signed" });
88+
89+
await act(() => vi.advanceTimersByTime(1000));
90+
91+
expect(result.current[0]).toMatchObject({ type: "broadcasted" });
92+
93+
await act(() => vi.advanceTimersByTime(1000));
94+
95+
expect(result.current[0]).toMatchObject({ type: "txBestBlocksState" });
96+
97+
await act(() => vi.advanceTimersByTime(1000));
98+
99+
expect(result.current[0]).toMatchObject({ type: "finalized" });
100+
});
101+
102+
it("catches error", async () => {
103+
const { result } = await act(() =>
104+
renderHook(
105+
() =>
106+
useContractMutation((mutate) =>
107+
mutate(testContract, "0x", "test_message", {}),
108+
),
109+
{
110+
wrapper: ({ children }) => (
111+
<ReactiveDotProvider config={defineConfig({ chains: {} })}>
112+
<ChainProvider chainId="test_chain">
113+
<SignerProvider signer={{} as PolkadotSigner}>
114+
{children}
115+
</SignerProvider>
116+
</ChainProvider>
117+
</ReactiveDotProvider>
118+
),
119+
},
120+
),
121+
);
122+
123+
mockSignSubmitAndWatch.mockReturnValue(throwError(() => new Error("test")));
124+
125+
expect(result.current[0]).toBe(idle);
126+
127+
await act(() => result.current[1]());
128+
129+
expect(result.current[0]).toBeInstanceOf(Error);
130+
});
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { chainIdKey, configKey, signerKey } from "../keys.js";
2+
import { withSetup } from "../test-utils.js";
3+
import { useContractMutation } from "./use-contract-mutation.js";
4+
import { defineConfig, defineContract } from "@reactive-dot/core";
5+
import type { TxEvent } from "polkadot-api";
6+
import { from, of, throwError } from "rxjs";
7+
import { concatMap, delay } from "rxjs/operators";
8+
import { afterEach, beforeEach, expect, it, vi } from "vitest";
9+
10+
const mockSignSubmitAndWatch = vi.fn();
11+
12+
vi.mock("./use-typed-api.js", () => ({
13+
useTypedApiPromise: vi.fn(),
14+
}));
15+
16+
vi.mock("./use-ink-client.js", () => ({
17+
getInkClient: vi.fn(),
18+
}));
19+
20+
vi.mock("@reactive-dot/core/internal/actions.js", () => ({
21+
getInkContractTx: vi.fn(async () => ({
22+
signSubmitAndWatch: mockSignSubmitAndWatch,
23+
})),
24+
}));
25+
26+
vi.useFakeTimers();
27+
28+
beforeEach(() =>
29+
mockSignSubmitAndWatch.mockReturnValue(
30+
from<Partial<TxEvent>[]>([
31+
{ type: "signed" },
32+
{ type: "broadcasted" },
33+
{ type: "txBestBlocksState" },
34+
{ type: "finalized" },
35+
]).pipe(concatMap((item) => of(item).pipe(delay(1000)))),
36+
),
37+
);
38+
39+
afterEach(() => {
40+
vi.restoreAllMocks();
41+
});
42+
43+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
44+
const testContract = defineContract({ descriptor: {} as any });
45+
46+
it("sign submit and watch", async () => {
47+
const { result } = withSetup(
48+
() =>
49+
useContractMutation((mutate) =>
50+
mutate(testContract, "0x", "test_message", {}),
51+
),
52+
{
53+
[configKey]: defineConfig({ chains: {} }),
54+
[chainIdKey]: "test_chain",
55+
[signerKey]: {},
56+
},
57+
);
58+
59+
expect(result.status.value).toBe("idle");
60+
61+
result.execute();
62+
63+
expect(result.status.value).toBe("pending");
64+
65+
vi.advanceTimersByTime(1000);
66+
67+
await vi.waitUntil(() => result.status.value === "success", {
68+
timeout: 3000,
69+
});
70+
71+
expect(result.data.value).toMatchObject({ type: "signed" });
72+
73+
vi.advanceTimersByTime(1000);
74+
75+
expect(result.data.value).toMatchObject({ type: "broadcasted" });
76+
77+
vi.advanceTimersByTime(1000);
78+
79+
expect(result.data.value).toMatchObject({ type: "txBestBlocksState" });
80+
81+
vi.advanceTimersByTime(1000);
82+
83+
expect(result.data.value).toMatchObject({ type: "finalized" });
84+
});
85+
86+
it("catches error", async () => {
87+
const { result } = withSetup(
88+
() =>
89+
useContractMutation((mutate) =>
90+
mutate(testContract, "0x", "test_message", {}),
91+
),
92+
{
93+
[configKey]: defineConfig({ chains: {} }),
94+
[chainIdKey]: "test_chain",
95+
[signerKey]: {},
96+
},
97+
);
98+
99+
mockSignSubmitAndWatch.mockReturnValue(throwError(() => new Error("test")));
100+
101+
expect(result.status.value).toBe("idle");
102+
103+
result.execute();
104+
105+
await vi.waitUntil(() => result.status.value === "error", {
106+
timeout: 3000,
107+
});
108+
109+
expect(result.error.value).toBeInstanceOf(Error);
110+
});

0 commit comments

Comments
 (0)