@crossmint/client-sdk-react-ui 1.3.13 → 1.3.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +45 -20
- package/dist/index.d.ts +45 -20
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +13 -9
- package/src/components/auth/AuthModal.tsx +196 -0
- package/src/consts/version.ts +1 -1
- package/src/hooks/index.ts +1 -1
- package/src/hooks/useAuth.ts +11 -0
- package/src/hooks/useCrossmint.tsx +2 -1
- package/src/hooks/useWallet.ts +2 -1
- package/src/icons/x.tsx +19 -0
- package/src/providers/CrossmintAuthProvider.test.tsx +198 -0
- package/src/providers/CrossmintAuthProvider.tsx +126 -11
- package/src/providers/CrossmintWalletProvider.test.tsx +206 -0
- package/src/providers/CrossmintWalletProvider.tsx +59 -56
- package/src/testUtils.ts +5 -0
- package/src/utils/constants.ts +1 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/jwt.ts +9 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { fireEvent, render } from "@testing-library/react";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
import { beforeEach, describe, expect, vi } from "vitest";
|
|
4
|
+
import { mock } from "vitest-mock-extended";
|
|
5
|
+
|
|
6
|
+
import { EVMSmartWallet, SmartWalletSDK } from "@crossmint/client-sdk-smart-wallet";
|
|
7
|
+
import { createCrossmint } from "@crossmint/common-sdk-base";
|
|
8
|
+
|
|
9
|
+
import { useAuth, useWallet } from "../hooks";
|
|
10
|
+
import { CrossmintProvider, useCrossmint } from "../hooks/useCrossmint";
|
|
11
|
+
import { MOCK_API_KEY, waitForSettledState } from "../testUtils";
|
|
12
|
+
import { CrossmintAuthProvider, CrossmintAuthWalletConfig } from "./CrossmintAuthProvider";
|
|
13
|
+
|
|
14
|
+
vi.mock("@crossmint/client-sdk-smart-wallet", async () => {
|
|
15
|
+
const actual = await vi.importActual("@crossmint/client-sdk-smart-wallet");
|
|
16
|
+
return {
|
|
17
|
+
...actual,
|
|
18
|
+
SmartWalletSDK: {
|
|
19
|
+
init: vi.fn(),
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
vi.mock("@crossmint/common-sdk-base", async () => {
|
|
25
|
+
const actual = await vi.importActual("@crossmint/common-sdk-base");
|
|
26
|
+
return {
|
|
27
|
+
...actual,
|
|
28
|
+
createCrossmint: vi.fn(),
|
|
29
|
+
validateApiKeyAndGetCrossmintBaseUrl: vi.fn(),
|
|
30
|
+
};
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
function renderAuthProvider({
|
|
34
|
+
children,
|
|
35
|
+
embeddedWallets,
|
|
36
|
+
}: {
|
|
37
|
+
children: ReactNode;
|
|
38
|
+
embeddedWallets: CrossmintAuthWalletConfig;
|
|
39
|
+
}) {
|
|
40
|
+
return render(
|
|
41
|
+
<CrossmintProvider apiKey={MOCK_API_KEY}>
|
|
42
|
+
<CrossmintAuthProvider embeddedWallets={embeddedWallets}>{children}</CrossmintAuthProvider>
|
|
43
|
+
</CrossmintProvider>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function TestComponent() {
|
|
48
|
+
const { setJwt } = useCrossmint();
|
|
49
|
+
const { wallet, status: walletStatus, error } = useWallet();
|
|
50
|
+
const { status: authStatus } = useAuth();
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<div>
|
|
54
|
+
<div data-testid="error">{error?.message ?? "No Error"}</div>
|
|
55
|
+
<div data-testid="wallet-status">{walletStatus}</div>
|
|
56
|
+
<div data-testid="auth-status">{authStatus}</div>
|
|
57
|
+
<div data-testid="wallet">{wallet ? "Wallet Loaded" : "No Wallet"}</div>
|
|
58
|
+
<button data-testid="jwt-input" onClick={() => setJwt("mock-jwt")}>
|
|
59
|
+
Set JWT
|
|
60
|
+
</button>
|
|
61
|
+
|
|
62
|
+
<button data-testid="clear-jwt-button" onClick={() => setJwt(undefined)}>
|
|
63
|
+
Clear JWT
|
|
64
|
+
</button>
|
|
65
|
+
</div>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
describe("CrossmintAuthProvider", () => {
|
|
70
|
+
let mockSDK: SmartWalletSDK;
|
|
71
|
+
let mockWallet: EVMSmartWallet;
|
|
72
|
+
let embeddedWallets: CrossmintAuthWalletConfig;
|
|
73
|
+
|
|
74
|
+
beforeEach(() => {
|
|
75
|
+
vi.resetAllMocks();
|
|
76
|
+
vi.mocked(createCrossmint).mockImplementation(() => ({
|
|
77
|
+
apiKey: MOCK_API_KEY,
|
|
78
|
+
jwt: "mock-jwt",
|
|
79
|
+
}));
|
|
80
|
+
|
|
81
|
+
mockSDK = mock<SmartWalletSDK>();
|
|
82
|
+
mockWallet = mock<EVMSmartWallet>();
|
|
83
|
+
vi.mocked(SmartWalletSDK.init).mockReturnValue(mockSDK);
|
|
84
|
+
vi.mocked(mockSDK.getOrCreateWallet).mockResolvedValue(mockWallet);
|
|
85
|
+
|
|
86
|
+
embeddedWallets = {
|
|
87
|
+
defaultChain: "polygon",
|
|
88
|
+
createOnLogin: "all-users",
|
|
89
|
+
type: "evm-smart-wallet",
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("Happy path", async () => {
|
|
94
|
+
const { getByTestId } = renderAuthProvider({
|
|
95
|
+
children: <TestComponent />,
|
|
96
|
+
embeddedWallets,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
expect(getByTestId("wallet-status").textContent).toBe("in-progress");
|
|
100
|
+
expect(getByTestId("auth-status").textContent).toBe("logged-in");
|
|
101
|
+
expect(getByTestId("wallet").textContent).toBe("No Wallet");
|
|
102
|
+
expect(getByTestId("error").textContent).toBe("No Error");
|
|
103
|
+
|
|
104
|
+
await waitForSettledState(() => {
|
|
105
|
+
expect(getByTestId("wallet-status").textContent).toBe("loaded");
|
|
106
|
+
expect(getByTestId("auth-status").textContent).toBe("logged-in");
|
|
107
|
+
expect(getByTestId("wallet").textContent).toBe("Wallet Loaded");
|
|
108
|
+
expect(getByTestId("error").textContent).toBe("No Error");
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
expect(vi.mocked(mockSDK.getOrCreateWallet)).toHaveBeenCalledOnce();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test(`When "createOnLogin" is "false", wallet is not loaded`, async () => {
|
|
115
|
+
const { getByTestId } = renderAuthProvider({
|
|
116
|
+
children: <TestComponent />,
|
|
117
|
+
embeddedWallets: {
|
|
118
|
+
defaultChain: "polygon",
|
|
119
|
+
createOnLogin: "off",
|
|
120
|
+
type: "evm-smart-wallet",
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
await waitForSettledState(() => {
|
|
125
|
+
expect(getByTestId("wallet").textContent).toBe("No Wallet");
|
|
126
|
+
expect(getByTestId("wallet-status").textContent).toBe("not-loaded");
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
expect(vi.mocked(mockSDK.getOrCreateWallet)).not.toHaveBeenCalled();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test(`When the jwt from crossmint provider is not defined, wallet is not loaded`, async () => {
|
|
133
|
+
vi.mocked(createCrossmint).mockImplementation(() => ({
|
|
134
|
+
apiKey: MOCK_API_KEY,
|
|
135
|
+
jwt: undefined,
|
|
136
|
+
}));
|
|
137
|
+
|
|
138
|
+
const { getByTestId } = renderAuthProvider({
|
|
139
|
+
children: <TestComponent />,
|
|
140
|
+
embeddedWallets,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
await waitForSettledState(() => {
|
|
144
|
+
expect(getByTestId("wallet-status").textContent).toBe("not-loaded");
|
|
145
|
+
expect(getByTestId("auth-status").textContent).toBe("logged-out");
|
|
146
|
+
expect(getByTestId("wallet").textContent).toBe("No Wallet");
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
expect(vi.mocked(mockSDK.getOrCreateWallet)).not.toHaveBeenCalled();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
test("When the jwt is cleared, so is the wallet", async () => {
|
|
153
|
+
const { getByTestId } = renderAuthProvider({
|
|
154
|
+
children: <TestComponent />,
|
|
155
|
+
embeddedWallets,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
await waitForSettledState(() => {
|
|
159
|
+
expect(getByTestId("wallet-status").textContent).toBe("loaded");
|
|
160
|
+
expect(getByTestId("auth-status").textContent).toBe("logged-in");
|
|
161
|
+
expect(getByTestId("wallet").textContent).toBe("Wallet Loaded");
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
fireEvent.click(getByTestId("clear-jwt-button"));
|
|
165
|
+
|
|
166
|
+
await waitForSettledState(() => {
|
|
167
|
+
expect(getByTestId("wallet-status").textContent).toBe("not-loaded");
|
|
168
|
+
expect(getByTestId("auth-status").textContent).toBe("logged-out");
|
|
169
|
+
expect(getByTestId("wallet").textContent).toBe("No Wallet");
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
expect(vi.mocked(mockSDK.getOrCreateWallet)).toHaveBeenCalledOnce();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test(`Logging in and asserting the auth status`, async () => {
|
|
176
|
+
vi.mocked(createCrossmint).mockImplementation(() => ({
|
|
177
|
+
apiKey: MOCK_API_KEY,
|
|
178
|
+
jwt: undefined,
|
|
179
|
+
}));
|
|
180
|
+
|
|
181
|
+
const { getByTestId } = renderAuthProvider({
|
|
182
|
+
children: <TestComponent />,
|
|
183
|
+
embeddedWallets,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
await waitForSettledState(() => {
|
|
187
|
+
expect(getByTestId("auth-status").textContent).toBe("logged-out");
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
fireEvent.click(getByTestId("jwt-input"));
|
|
191
|
+
|
|
192
|
+
// We can't assert the status: "in-progress" because the jwt state is set instantly
|
|
193
|
+
|
|
194
|
+
await waitForSettledState(() => {
|
|
195
|
+
expect(getByTestId("auth-status").textContent).toBe("logged-in");
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
});
|
|
@@ -1,23 +1,138 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { type ReactNode, createContext, useEffect, useState } from "react";
|
|
2
|
+
import { createPortal } from "react-dom";
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
import { UIConfig } from "@crossmint/common-sdk-base";
|
|
4
|
+
import type { EVMSmartWalletChain } from "@crossmint/client-sdk-smart-wallet";
|
|
5
|
+
import { type UIConfig, validateApiKeyAndGetCrossmintBaseUrl } from "@crossmint/common-sdk-base";
|
|
6
6
|
|
|
7
|
-
import
|
|
7
|
+
import AuthModal from "../components/auth/AuthModal";
|
|
8
|
+
import { useCrossmint, useWallet } from "../hooks";
|
|
9
|
+
import { SESSION_PREFIX } from "../utils";
|
|
10
|
+
import { CrossmintWalletProvider } from "./CrossmintWalletProvider";
|
|
8
11
|
|
|
9
|
-
type
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
export type CrossmintAuthWalletConfig = {
|
|
13
|
+
defaultChain: EVMSmartWalletChain;
|
|
14
|
+
createOnLogin: "all-users" | "off";
|
|
15
|
+
type: "evm-smart-wallet";
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type CrossmintAuthProviderProps = {
|
|
19
|
+
embeddedWallets: CrossmintAuthWalletConfig;
|
|
12
20
|
appearance?: UIConfig;
|
|
21
|
+
children: ReactNode;
|
|
13
22
|
};
|
|
14
23
|
|
|
24
|
+
type AuthStatus = "logged-in" | "logged-out" | "in-progress";
|
|
25
|
+
|
|
26
|
+
type AuthContextType = {
|
|
27
|
+
login: () => void;
|
|
28
|
+
logout: () => void;
|
|
29
|
+
jwt?: string;
|
|
30
|
+
status: AuthStatus;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const AuthContext = createContext<AuthContextType>({
|
|
34
|
+
login: () => {},
|
|
35
|
+
logout: () => {},
|
|
36
|
+
status: "logged-out",
|
|
37
|
+
});
|
|
38
|
+
|
|
15
39
|
export function CrossmintAuthProvider({ embeddedWallets, children, appearance }: CrossmintAuthProviderProps) {
|
|
16
40
|
const { crossmint, setJwt } = useCrossmint("CrossmintAuthProvider must be used within CrossmintProvider");
|
|
41
|
+
const crossmintBaseUrl = validateApiKeyAndGetCrossmintBaseUrl(crossmint.apiKey);
|
|
42
|
+
const [modalOpen, setModalOpen] = useState(false);
|
|
43
|
+
|
|
44
|
+
const login = () => {
|
|
45
|
+
if (crossmint.jwt != null) {
|
|
46
|
+
console.log("User already logged in");
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
setModalOpen(true);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const logout = () => {
|
|
54
|
+
document.cookie = `${SESSION_PREFIX}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
|
|
55
|
+
setJwt(undefined);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (crossmint.jwt == null) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
setModalOpen(false);
|
|
64
|
+
}, [crossmint.jwt]);
|
|
65
|
+
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (crossmint.jwt) {
|
|
68
|
+
document.cookie = `${SESSION_PREFIX}=${crossmint.jwt}; path=/;SameSite=Lax;`;
|
|
69
|
+
}
|
|
70
|
+
}, [crossmint.jwt]);
|
|
71
|
+
|
|
72
|
+
const getAuthStatus = (): AuthStatus => {
|
|
73
|
+
if (crossmint.jwt != null) {
|
|
74
|
+
return "logged-in";
|
|
75
|
+
}
|
|
76
|
+
if (modalOpen) {
|
|
77
|
+
return "in-progress";
|
|
78
|
+
}
|
|
79
|
+
return "logged-out";
|
|
80
|
+
};
|
|
17
81
|
|
|
18
82
|
return (
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
83
|
+
<AuthContext.Provider
|
|
84
|
+
value={{
|
|
85
|
+
login,
|
|
86
|
+
logout,
|
|
87
|
+
jwt: crossmint.jwt,
|
|
88
|
+
status: getAuthStatus(),
|
|
89
|
+
}}
|
|
90
|
+
>
|
|
91
|
+
<CrossmintWalletProvider defaultChain={embeddedWallets.defaultChain}>
|
|
92
|
+
<WalletManager embeddedWallets={embeddedWallets} accessToken={crossmint.jwt}>
|
|
93
|
+
{children}
|
|
94
|
+
</WalletManager>
|
|
95
|
+
{modalOpen
|
|
96
|
+
? createPortal(
|
|
97
|
+
<AuthModal
|
|
98
|
+
baseUrl={crossmintBaseUrl}
|
|
99
|
+
setModalOpen={setModalOpen}
|
|
100
|
+
setJwtToken={setJwt}
|
|
101
|
+
apiKey={crossmint.apiKey}
|
|
102
|
+
appearance={appearance}
|
|
103
|
+
/>,
|
|
104
|
+
|
|
105
|
+
document.body
|
|
106
|
+
)
|
|
107
|
+
: null}
|
|
108
|
+
</CrossmintWalletProvider>
|
|
109
|
+
</AuthContext.Provider>
|
|
22
110
|
);
|
|
23
111
|
}
|
|
112
|
+
|
|
113
|
+
function WalletManager({
|
|
114
|
+
embeddedWallets,
|
|
115
|
+
children,
|
|
116
|
+
accessToken,
|
|
117
|
+
}: {
|
|
118
|
+
embeddedWallets: CrossmintAuthWalletConfig;
|
|
119
|
+
children: ReactNode;
|
|
120
|
+
accessToken: string | undefined;
|
|
121
|
+
}) {
|
|
122
|
+
const { getOrCreateWallet, clearWallet, status } = useWallet();
|
|
123
|
+
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
if (embeddedWallets.createOnLogin === "all-users" && status === "not-loaded" && accessToken != null) {
|
|
126
|
+
getOrCreateWallet({
|
|
127
|
+
type: embeddedWallets.type,
|
|
128
|
+
signer: { type: "PASSKEY" },
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (status === "loaded" && accessToken == null) {
|
|
133
|
+
clearWallet();
|
|
134
|
+
}
|
|
135
|
+
}, [accessToken, status]);
|
|
136
|
+
|
|
137
|
+
return <>{children}</>;
|
|
138
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { fireEvent, render, waitFor } from "@testing-library/react";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { mock } from "vitest-mock-extended";
|
|
5
|
+
|
|
6
|
+
import { EVMSmartWallet, SmartWalletError, SmartWalletSDK } from "@crossmint/client-sdk-smart-wallet";
|
|
7
|
+
import { createCrossmint } from "@crossmint/common-sdk-base";
|
|
8
|
+
|
|
9
|
+
import { CrossmintProvider, useCrossmint } from "../hooks/useCrossmint";
|
|
10
|
+
import { useWallet } from "../hooks/useWallet";
|
|
11
|
+
import { MOCK_API_KEY, waitForSettledState } from "../testUtils";
|
|
12
|
+
import { CrossmintWalletProvider } from "./CrossmintWalletProvider";
|
|
13
|
+
|
|
14
|
+
vi.mock("@crossmint/client-sdk-smart-wallet", async () => {
|
|
15
|
+
const actual = await vi.importActual("@crossmint/client-sdk-smart-wallet");
|
|
16
|
+
return {
|
|
17
|
+
...actual,
|
|
18
|
+
SmartWalletSDK: {
|
|
19
|
+
init: vi.fn(),
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
vi.mock("@crossmint/common-sdk-base", async () => {
|
|
25
|
+
const actual = await vi.importActual("@crossmint/common-sdk-base");
|
|
26
|
+
return {
|
|
27
|
+
...actual,
|
|
28
|
+
createCrossmint: vi.fn(),
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
function renderWalletProvider({ children }: { children: ReactNode }) {
|
|
33
|
+
return render(
|
|
34
|
+
<CrossmintProvider apiKey={MOCK_API_KEY}>
|
|
35
|
+
<CrossmintWalletProvider defaultChain="polygon-amoy">{children}</CrossmintWalletProvider>
|
|
36
|
+
</CrossmintProvider>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function TestComponent() {
|
|
41
|
+
const { status, wallet, error, getOrCreateWallet, clearWallet } = useWallet();
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<div>
|
|
45
|
+
<div data-testid="error">{error?.message ?? "No Error"}</div>
|
|
46
|
+
<div data-testid="status">{status}</div>
|
|
47
|
+
<div data-testid="wallet">{wallet ? "Wallet Loaded" : "No Wallet"}</div>
|
|
48
|
+
<button data-testid="create-wallet-button" onClick={() => getOrCreateWallet()}>
|
|
49
|
+
Create Wallet
|
|
50
|
+
</button>
|
|
51
|
+
<button data-testid="clear-wallet-button" onClick={() => clearWallet()}>
|
|
52
|
+
Clear Wallet
|
|
53
|
+
</button>
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
describe("CrossmintWalletProvider", () => {
|
|
59
|
+
let mockSDK: SmartWalletSDK;
|
|
60
|
+
let mockWallet: EVMSmartWallet;
|
|
61
|
+
|
|
62
|
+
beforeEach(() => {
|
|
63
|
+
vi.resetAllMocks();
|
|
64
|
+
|
|
65
|
+
vi.mocked(createCrossmint).mockImplementation(() => ({
|
|
66
|
+
apiKey: MOCK_API_KEY,
|
|
67
|
+
jwt: "mock-jwt",
|
|
68
|
+
}));
|
|
69
|
+
|
|
70
|
+
mockSDK = mock<SmartWalletSDK>();
|
|
71
|
+
mockWallet = mock<EVMSmartWallet>();
|
|
72
|
+
vi.mocked(SmartWalletSDK.init).mockReturnValue(mockSDK);
|
|
73
|
+
vi.mocked(mockSDK.getOrCreateWallet).mockResolvedValue(mockWallet);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe("getOrCreateWallet", () => {
|
|
77
|
+
test("happy path ", async () => {
|
|
78
|
+
const { getByTestId } = renderWalletProvider({
|
|
79
|
+
children: <TestComponent />,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
expect(getByTestId("status").textContent).toBe("not-loaded");
|
|
83
|
+
expect(getByTestId("wallet").textContent).toBe("No Wallet");
|
|
84
|
+
expect(getByTestId("error").textContent).toBe("No Error");
|
|
85
|
+
|
|
86
|
+
fireEvent.click(getByTestId("create-wallet-button"));
|
|
87
|
+
|
|
88
|
+
await waitFor(() => {
|
|
89
|
+
expect(getByTestId("status").textContent).toBe("in-progress");
|
|
90
|
+
expect(getByTestId("wallet").textContent).toBe("No Wallet");
|
|
91
|
+
expect(getByTestId("error").textContent).toBe("No Error");
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
await waitForSettledState(() => {
|
|
95
|
+
expect(getByTestId("status").textContent).toBe("loaded");
|
|
96
|
+
expect(getByTestId("wallet").textContent).toBe("Wallet Loaded");
|
|
97
|
+
expect(getByTestId("error").textContent).toBe("No Error");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
expect(vi.mocked(mockSDK.getOrCreateWallet)).toHaveBeenCalledOnce();
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe(`When jwt is not set in "CrossmintProvider"`, () => {
|
|
104
|
+
beforeEach(() => {
|
|
105
|
+
vi.mocked(createCrossmint).mockImplementation(() => ({
|
|
106
|
+
apiKey: MOCK_API_KEY,
|
|
107
|
+
jwt: undefined,
|
|
108
|
+
}));
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("does not create a wallet", async () => {
|
|
112
|
+
const { getByTestId } = renderWalletProvider({
|
|
113
|
+
children: <TestComponent />,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
fireEvent.click(getByTestId("create-wallet-button"));
|
|
117
|
+
|
|
118
|
+
await waitForSettledState(() => {
|
|
119
|
+
expect(getByTestId("status").textContent).toBe("not-loaded");
|
|
120
|
+
expect(getByTestId("wallet").textContent).toBe("No Wallet");
|
|
121
|
+
expect(getByTestId("error").textContent).toBe("No Error");
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
expect(vi.mocked(mockSDK.getOrCreateWallet)).not.toHaveBeenCalled();
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe("When getOrCreateWallet throws a known error", () => {
|
|
129
|
+
beforeEach(() => {
|
|
130
|
+
vi.mocked(mockSDK.getOrCreateWallet).mockRejectedValue(new SmartWalletError("Wallet creation failed"));
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("should set error directly with the thrown error", async () => {
|
|
134
|
+
const { getByTestId } = renderWalletProvider({
|
|
135
|
+
children: <TestComponent />,
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
fireEvent.click(getByTestId("create-wallet-button"));
|
|
139
|
+
|
|
140
|
+
await waitFor(() => {
|
|
141
|
+
expect(getByTestId("status").textContent).toBe("in-progress");
|
|
142
|
+
expect(getByTestId("wallet").textContent).toBe("No Wallet");
|
|
143
|
+
expect(getByTestId("error").textContent).toBe("No Error");
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
await waitForSettledState(() => {
|
|
147
|
+
expect(getByTestId("status").textContent).toBe("loading-error");
|
|
148
|
+
expect(getByTestId("wallet").textContent).toBe("No Wallet");
|
|
149
|
+
expect(getByTestId("error").textContent).toBe("Wallet creation failed");
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
expect(vi.mocked(mockSDK.getOrCreateWallet)).toHaveBeenCalledOnce();
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
describe("When getOrCreateWallet throws an unknown error", () => {
|
|
157
|
+
beforeEach(() => {
|
|
158
|
+
vi.mocked(mockSDK.getOrCreateWallet).mockRejectedValue(new Error("Wallet creation failed"));
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("should set the error with the thrown error wrapped with a SmartWalletError", async () => {
|
|
162
|
+
const { getByTestId } = renderWalletProvider({
|
|
163
|
+
children: <TestComponent />,
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
fireEvent.click(getByTestId("create-wallet-button"));
|
|
167
|
+
|
|
168
|
+
await waitFor(() => {
|
|
169
|
+
expect(getByTestId("status").textContent).toBe("in-progress");
|
|
170
|
+
expect(getByTestId("wallet").textContent).toBe("No Wallet");
|
|
171
|
+
expect(getByTestId("error").textContent).toBe("No Error");
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
await waitForSettledState(() => {
|
|
175
|
+
expect(getByTestId("status").textContent).toBe("loading-error");
|
|
176
|
+
expect(getByTestId("wallet").textContent).toBe("No Wallet");
|
|
177
|
+
expect(getByTestId("error").textContent).toBe("Unknown Wallet Error: Wallet creation failed");
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
expect(vi.mocked(mockSDK.getOrCreateWallet)).toHaveBeenCalledOnce();
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test("clearWallet happy path", async () => {
|
|
186
|
+
const { getByTestId } = renderWalletProvider({
|
|
187
|
+
children: <TestComponent />,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
fireEvent.click(getByTestId("create-wallet-button"));
|
|
191
|
+
|
|
192
|
+
await waitForSettledState(() => {
|
|
193
|
+
expect(getByTestId("status").textContent).toBe("loaded");
|
|
194
|
+
expect(getByTestId("wallet").textContent).toBe("Wallet Loaded");
|
|
195
|
+
expect(getByTestId("error").textContent).toBe("No Error");
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
fireEvent.click(getByTestId("clear-wallet-button"));
|
|
199
|
+
|
|
200
|
+
await waitForSettledState(() => {
|
|
201
|
+
expect(getByTestId("status").textContent).toBe("not-loaded");
|
|
202
|
+
expect(getByTestId("wallet").textContent).toBe("No Wallet");
|
|
203
|
+
expect(getByTestId("error").textContent).toBe("No Error");
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
});
|