Skip to content

Commit 653b520

Browse files
committed
test: add in app core test
1 parent 154403b commit 653b520

File tree

2 files changed

+340
-6
lines changed

2 files changed

+340
-6
lines changed
Lines changed: 334 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,334 @@
1+
import { beforeEach, describe, expect, it, vi } from "vitest";
2+
import { baseSepolia } from "../../../../chains/chain-definitions/base-sepolia.js";
3+
import { createThirdwebClient } from "../../../../client/client.js";
4+
import { getEcosystemInfo } from "../../../ecosystem/get-ecosystem-wallet-auth-options.js";
5+
import type { Account } from "../../../interfaces/wallet.js";
6+
import type { InAppConnector } from "../interfaces/connector.js";
7+
import { createInAppWallet } from "./in-app-core.js";
8+
import { autoConnectInAppWallet, connectInAppWallet } from "./index.js";
9+
10+
vi.mock("../../../../analytics/track/connect.js", () => ({
11+
trackConnect: vi.fn(),
12+
}));
13+
14+
vi.mock("./index.js", () => ({
15+
autoConnectInAppWallet: vi.fn(),
16+
connectInAppWallet: vi.fn(),
17+
}));
18+
19+
vi.mock("../../../ecosystem/get-ecosystem-wallet-auth-options.js", () => ({
20+
getEcosystemInfo: vi.fn(),
21+
}));
22+
23+
describe("createInAppWallet", () => {
24+
const mockClient = createThirdwebClient({
25+
clientId: "test-client",
26+
});
27+
const mockChain = baseSepolia;
28+
const mockAccount = { address: "0x123" } as Account;
29+
30+
const mockConnectorFactory = vi.fn(() =>
31+
Promise.resolve({
32+
connect: vi.fn(),
33+
logout: vi.fn(() => Promise.resolve({ success: true })),
34+
authenticate: vi.fn(),
35+
getAccounts: vi.fn(),
36+
getAccount: vi.fn(),
37+
getProfiles: vi.fn(),
38+
getUser: vi.fn(),
39+
linkProfile: vi.fn(),
40+
preAuthenticate: vi.fn(),
41+
} as InAppConnector),
42+
);
43+
44+
beforeEach(() => {
45+
vi.clearAllMocks();
46+
});
47+
48+
it("should connect successfully", async () => {
49+
vi.mocked(connectInAppWallet).mockResolvedValue([mockAccount, mockChain]);
50+
51+
const wallet = createInAppWallet({
52+
connectorFactory: mockConnectorFactory,
53+
});
54+
55+
const result = await wallet.connect({
56+
client: mockClient,
57+
chain: mockChain,
58+
strategy: "email",
59+
email: "",
60+
verificationCode: "",
61+
});
62+
63+
expect(result).toBe(mockAccount);
64+
expect(connectInAppWallet).toHaveBeenCalledWith(
65+
expect.objectContaining({
66+
client: mockClient,
67+
chain: mockChain,
68+
}),
69+
undefined,
70+
expect.any(Object),
71+
);
72+
});
73+
74+
it("should auto connect successfully", async () => {
75+
vi.mocked(autoConnectInAppWallet).mockResolvedValue([
76+
mockAccount,
77+
mockChain,
78+
]);
79+
80+
const wallet = createInAppWallet({
81+
connectorFactory: mockConnectorFactory,
82+
});
83+
84+
const result = await wallet.autoConnect({
85+
client: mockClient,
86+
chain: mockChain,
87+
});
88+
89+
expect(result).toBe(mockAccount);
90+
expect(autoConnectInAppWallet).toHaveBeenCalledWith(
91+
expect.objectContaining({
92+
client: mockClient,
93+
chain: mockChain,
94+
}),
95+
undefined,
96+
expect.any(Object),
97+
);
98+
});
99+
100+
it("should handle ecosystem wallet connection with smart account settings", async () => {
101+
vi.mocked(getEcosystemInfo).mockResolvedValue({
102+
smartAccountOptions: {
103+
chainIds: [mockChain.id],
104+
sponsorGas: true,
105+
accountFactoryAddress: "0x456",
106+
},
107+
authOptions: [],
108+
name: "hello world",
109+
slug: "test-ecosystem",
110+
});
111+
112+
vi.mocked(connectInAppWallet).mockResolvedValue([mockAccount, mockChain]);
113+
114+
const wallet = createInAppWallet({
115+
connectorFactory: mockConnectorFactory,
116+
ecosystem: { id: "ecosystem.test-ecosystem" },
117+
});
118+
119+
const result = await wallet.connect({
120+
client: mockClient,
121+
chain: mockChain,
122+
strategy: "email",
123+
email: "",
124+
verificationCode: "",
125+
});
126+
127+
expect(result).toBe(mockAccount);
128+
expect(connectInAppWallet).toHaveBeenCalledWith(
129+
expect.objectContaining({
130+
client: mockClient,
131+
chain: mockChain,
132+
}),
133+
expect.objectContaining({
134+
smartAccount: expect.objectContaining({
135+
chain: mockChain,
136+
sponsorGas: true,
137+
factoryAddress: "0x456",
138+
}),
139+
}),
140+
expect.any(Object),
141+
);
142+
});
143+
it("should handle ecosystem wallet connection with smart account settings even when no chain is set", async () => {
144+
vi.mocked(getEcosystemInfo).mockResolvedValue({
145+
smartAccountOptions: {
146+
chainIds: [mockChain.id],
147+
sponsorGas: true,
148+
accountFactoryAddress: "0x456",
149+
},
150+
authOptions: [],
151+
name: "hello world",
152+
slug: "test-ecosystem",
153+
});
154+
155+
vi.mocked(connectInAppWallet).mockResolvedValue([mockAccount, mockChain]);
156+
157+
const wallet = createInAppWallet({
158+
connectorFactory: mockConnectorFactory,
159+
ecosystem: { id: "ecosystem.test-ecosystem" },
160+
});
161+
162+
const result = await wallet.connect({
163+
client: mockClient,
164+
strategy: "email",
165+
email: "",
166+
verificationCode: "",
167+
});
168+
169+
expect(result).toBe(mockAccount);
170+
expect(connectInAppWallet).toHaveBeenCalledWith(
171+
expect.objectContaining({
172+
client: mockClient,
173+
}),
174+
expect.objectContaining({
175+
smartAccount: expect.objectContaining({
176+
chain: mockChain,
177+
sponsorGas: true,
178+
factoryAddress: "0x456",
179+
}),
180+
}),
181+
expect.any(Object),
182+
);
183+
});
184+
185+
it("should handle ecosystem wallet auto connection with smart account settings", async () => {
186+
vi.mocked(getEcosystemInfo).mockResolvedValue({
187+
smartAccountOptions: {
188+
chainIds: [mockChain.id],
189+
sponsorGas: true,
190+
accountFactoryAddress: "0x456",
191+
},
192+
authOptions: [],
193+
name: "hello world",
194+
slug: "test-ecosystem",
195+
});
196+
197+
vi.mocked(autoConnectInAppWallet).mockResolvedValue([
198+
mockAccount,
199+
mockChain,
200+
]);
201+
202+
const wallet = createInAppWallet({
203+
connectorFactory: mockConnectorFactory,
204+
ecosystem: { id: "ecosystem.test-ecosystem" },
205+
});
206+
207+
const result = await wallet.autoConnect({
208+
client: mockClient,
209+
chain: mockChain,
210+
});
211+
212+
expect(result).toBe(mockAccount);
213+
expect(autoConnectInAppWallet).toHaveBeenCalledWith(
214+
expect.objectContaining({
215+
client: mockClient,
216+
chain: mockChain,
217+
}),
218+
expect.objectContaining({
219+
smartAccount: expect.objectContaining({
220+
chain: mockChain,
221+
sponsorGas: true,
222+
factoryAddress: "0x456",
223+
}),
224+
}),
225+
expect.any(Object),
226+
);
227+
});
228+
229+
it("should handle ecosystem wallet auto connection with smart account settings even when no chain is set", async () => {
230+
vi.mocked(getEcosystemInfo).mockResolvedValue({
231+
smartAccountOptions: {
232+
chainIds: [mockChain.id],
233+
sponsorGas: true,
234+
accountFactoryAddress: "0x456",
235+
},
236+
authOptions: [],
237+
name: "hello world",
238+
slug: "test-ecosystem",
239+
});
240+
241+
vi.mocked(autoConnectInAppWallet).mockResolvedValue([
242+
mockAccount,
243+
mockChain,
244+
]);
245+
246+
const wallet = createInAppWallet({
247+
connectorFactory: mockConnectorFactory,
248+
ecosystem: { id: "ecosystem.test-ecosystem" },
249+
});
250+
251+
const result = await wallet.autoConnect({
252+
client: mockClient,
253+
});
254+
255+
expect(result).toBe(mockAccount);
256+
expect(autoConnectInAppWallet).toHaveBeenCalledWith(
257+
expect.objectContaining({
258+
client: mockClient,
259+
}),
260+
expect.objectContaining({
261+
smartAccount: expect.objectContaining({
262+
chain: mockChain,
263+
sponsorGas: true,
264+
factoryAddress: "0x456",
265+
}),
266+
}),
267+
expect.any(Object),
268+
);
269+
});
270+
271+
it("should throw error for unsupported chain in ecosystem", async () => {
272+
const { getEcosystemInfo } = await import(
273+
"../../../ecosystem/get-ecosystem-wallet-auth-options.js"
274+
);
275+
276+
vi.mocked(getEcosystemInfo).mockResolvedValue({
277+
smartAccountOptions: {
278+
chainIds: [1],
279+
sponsorGas: true,
280+
accountFactoryAddress: "0x456",
281+
},
282+
authOptions: [],
283+
name: "hello world",
284+
slug: "test-ecosystem",
285+
});
286+
287+
const wallet = createInAppWallet({
288+
connectorFactory: mockConnectorFactory,
289+
ecosystem: { id: "ecosystem.test-ecosystem" },
290+
});
291+
292+
await expect(
293+
wallet.connect({
294+
client: mockClient,
295+
chain: mockChain,
296+
strategy: "email",
297+
email: "",
298+
verificationCode: "",
299+
}),
300+
).rejects.toThrow(
301+
`Chain ID ${mockChain.id} is not supported for ecosystem smart accounts.`,
302+
);
303+
});
304+
it("should throw error for unsupported chain in ecosystem when auto-connecting", async () => {
305+
const { getEcosystemInfo } = await import(
306+
"../../../ecosystem/get-ecosystem-wallet-auth-options.js"
307+
);
308+
309+
vi.mocked(getEcosystemInfo).mockResolvedValue({
310+
smartAccountOptions: {
311+
chainIds: [1],
312+
sponsorGas: true,
313+
accountFactoryAddress: "0x456",
314+
},
315+
authOptions: [],
316+
name: "hello world",
317+
slug: "test-ecosystem",
318+
});
319+
320+
const wallet = createInAppWallet({
321+
connectorFactory: mockConnectorFactory,
322+
ecosystem: { id: "ecosystem.test-ecosystem" },
323+
});
324+
325+
await expect(
326+
wallet.autoConnect({
327+
client: mockClient,
328+
chain: mockChain,
329+
}),
330+
).rejects.toThrow(
331+
`Chain ID ${mockChain.id} is not supported for ecosystem smart accounts.`,
332+
);
333+
});
334+
});

packages/thirdweb/src/wallets/in-app/core/wallet/in-app-core.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ export function createInAppWallet(args: {
7878
const smartAccountOptions = ecosystemOptions?.smartAccountOptions;
7979
if (smartAccountOptions) {
8080
const defaultChainId =
81-
ecosystemOptions.smartAccountOptions.chainIds[0];
81+
ecosystemOptions.smartAccountOptions.chainIds?.[0];
8282
if (!defaultChainId) {
8383
throw new Error(
8484
"No default chain ID found for ecosystem smart accounts. Please reach out to the ecosystem owner.",
@@ -88,12 +88,12 @@ export function createInAppWallet(args: {
8888
options.chain ?? getCachedChain(defaultChainId);
8989

9090
if (
91-
!ecosystemOptions.smartAccountOptions.chainIds.includes(
91+
!ecosystemOptions.smartAccountOptions.chainIds?.includes(
9292
preferredChain?.id,
9393
)
9494
) {
9595
throw new Error(
96-
`Chain ID ${preferredChain.id} is not supported for ecosystem smart accounts, pass it via connect() or via UI components`,
96+
`Chain ID ${preferredChain.id} is not supported for ecosystem smart accounts.`,
9797
);
9898
}
9999

@@ -141,7 +141,7 @@ export function createInAppWallet(args: {
141141
const smartAccountOptions = ecosystemOptions?.smartAccountOptions;
142142
if (smartAccountOptions) {
143143
const defaultChainId =
144-
ecosystemOptions.smartAccountOptions.chainIds[0];
144+
ecosystemOptions.smartAccountOptions.chainIds?.[0];
145145
if (!defaultChainId) {
146146
throw new Error(
147147
"No default chain ID found for ecosystem smart accounts. Please reach out to the ecosystem owner.",
@@ -151,12 +151,12 @@ export function createInAppWallet(args: {
151151
options.chain ?? getCachedChain(defaultChainId);
152152

153153
if (
154-
!ecosystemOptions.smartAccountOptions.chainIds.includes(
154+
!ecosystemOptions.smartAccountOptions.chainIds?.includes(
155155
preferredChain?.id,
156156
)
157157
) {
158158
throw new Error(
159-
`Chain ID ${preferredChain.id} is not supported for ecosystem smart accounts, pass it via connect() or via UI components`,
159+
`Chain ID ${preferredChain.id} is not supported for ecosystem smart accounts.`,
160160
);
161161
}
162162

0 commit comments

Comments
 (0)