@tomo-inc/embedded-wallet-providers 0.0.21 → 0.0.23
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 +4 -10
- package/dist/index.js +5 -11
- package/package.json +8 -3
- package/project.json +1 -1
- package/src/__tests__/embedded-wallet.test.ts +600 -0
- package/src/__tests__/hub.test.ts +152 -0
- package/src/__tests__/index.test.ts +26 -0
- package/src/__tests__/relay-route.test.ts +73 -0
- package/src/__tests__/setup.ts +60 -0
- package/src/__tests__/utils.test.ts +66 -0
- package/src/embedded-wallet.ts +16 -12
- package/src/hub.ts +1 -1
- package/vitest.config.ts +14 -0
package/dist/index.cjs
CHANGED
|
@@ -287,15 +287,7 @@ var EmbeddedWallet = class _EmbeddedWallet {
|
|
|
287
287
|
if (!config) {
|
|
288
288
|
throw new Error("config is not initialized");
|
|
289
289
|
}
|
|
290
|
-
|
|
291
|
-
if (oidcToken) {
|
|
292
|
-
walletUtils.cache.set(cacheKey, oidcToken, false);
|
|
293
|
-
}
|
|
294
|
-
this.oidcToken = oidcToken || walletUtils.cache.get(cacheKey) || "";
|
|
295
|
-
console.log("embedded wallet login with oidcToken:", config, this.oidcToken);
|
|
296
|
-
if (!this.oidcToken) {
|
|
297
|
-
console.warn("oidcToken is not found");
|
|
298
|
-
}
|
|
290
|
+
this.oidcToken = oidcToken || "";
|
|
299
291
|
return new Promise((resolve, reject) => {
|
|
300
292
|
const receiveResponse = (event) => {
|
|
301
293
|
var _a, _b;
|
|
@@ -345,7 +337,7 @@ var EmbeddedWallet = class _EmbeddedWallet {
|
|
|
345
337
|
dappOrigin: window.location.origin,
|
|
346
338
|
tomoStage: stage,
|
|
347
339
|
tomoClientId: clientId,
|
|
348
|
-
oidcToken: this.oidcToken
|
|
340
|
+
oidcToken: this.oidcToken,
|
|
349
341
|
logo: logo || "",
|
|
350
342
|
name: name || ""
|
|
351
343
|
});
|
|
@@ -388,6 +380,8 @@ var EmbeddedWallet = class _EmbeddedWallet {
|
|
|
388
380
|
async logout() {
|
|
389
381
|
if (this.walletIframe) {
|
|
390
382
|
this.walletIframe.src = `${this.walletOrigin}#logout=true`;
|
|
383
|
+
const config = this.config;
|
|
384
|
+
config && this.init(config);
|
|
391
385
|
}
|
|
392
386
|
window.removeEventListener("message", this.walletCloseHandler);
|
|
393
387
|
window.removeEventListener("message", this.logoutListener);
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TronProvider, SolanaProvider, DogecoinProvider, BitcoinProvider, EvmProvider } from '@tomo-inc/inject-providers';
|
|
2
|
-
import { ChainTypeEnum, ProviderProtocol, ProviderStandard
|
|
2
|
+
import { ChainTypeEnum, ProviderProtocol, ProviderStandard } from '@tomo-inc/wallet-utils';
|
|
3
3
|
import { OidcAuth } from '@tomo-inc/oidc-auth';
|
|
4
4
|
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
@@ -285,15 +285,7 @@ var EmbeddedWallet = class _EmbeddedWallet {
|
|
|
285
285
|
if (!config) {
|
|
286
286
|
throw new Error("config is not initialized");
|
|
287
287
|
}
|
|
288
|
-
|
|
289
|
-
if (oidcToken) {
|
|
290
|
-
cache.set(cacheKey, oidcToken, false);
|
|
291
|
-
}
|
|
292
|
-
this.oidcToken = oidcToken || cache.get(cacheKey) || "";
|
|
293
|
-
console.log("embedded wallet login with oidcToken:", config, this.oidcToken);
|
|
294
|
-
if (!this.oidcToken) {
|
|
295
|
-
console.warn("oidcToken is not found");
|
|
296
|
-
}
|
|
288
|
+
this.oidcToken = oidcToken || "";
|
|
297
289
|
return new Promise((resolve, reject) => {
|
|
298
290
|
const receiveResponse = (event) => {
|
|
299
291
|
var _a, _b;
|
|
@@ -343,7 +335,7 @@ var EmbeddedWallet = class _EmbeddedWallet {
|
|
|
343
335
|
dappOrigin: window.location.origin,
|
|
344
336
|
tomoStage: stage,
|
|
345
337
|
tomoClientId: clientId,
|
|
346
|
-
oidcToken: this.oidcToken
|
|
338
|
+
oidcToken: this.oidcToken,
|
|
347
339
|
logo: logo || "",
|
|
348
340
|
name: name || ""
|
|
349
341
|
});
|
|
@@ -386,6 +378,8 @@ var EmbeddedWallet = class _EmbeddedWallet {
|
|
|
386
378
|
async logout() {
|
|
387
379
|
if (this.walletIframe) {
|
|
388
380
|
this.walletIframe.src = `${this.walletOrigin}#logout=true`;
|
|
381
|
+
const config = this.config;
|
|
382
|
+
config && this.init(config);
|
|
389
383
|
}
|
|
390
384
|
window.removeEventListener("message", this.walletCloseHandler);
|
|
391
385
|
window.removeEventListener("message", this.logoutListener);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tomo-inc/embedded-wallet-providers",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.23",
|
|
4
4
|
"author": "tomo.inc",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"private": false,
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
}
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@tomo-inc/oidc-auth": "0.0.
|
|
20
|
-
"@tomo-inc/wallet-utils": "0.0.
|
|
19
|
+
"@tomo-inc/oidc-auth": "0.0.17",
|
|
20
|
+
"@tomo-inc/wallet-utils": "0.0.19",
|
|
21
21
|
"@tomo-inc/inject-providers": "0.0.16"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
@@ -28,7 +28,12 @@
|
|
|
28
28
|
"tsx": "^4.19.2",
|
|
29
29
|
"typescript": "^5.0.0",
|
|
30
30
|
"@vitest/browser": "^3.2.4",
|
|
31
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
31
32
|
"playwright": "^1.44.1",
|
|
32
33
|
"vitest": "^3.2.4"
|
|
34
|
+
},
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsup src/index.ts --format esm,cjs --dts --treeshake",
|
|
37
|
+
"test": "vitest run"
|
|
33
38
|
}
|
|
34
39
|
}
|
package/project.json
CHANGED
|
@@ -0,0 +1,600 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { EmbeddedWallet } from "../embedded-wallet";
|
|
3
|
+
|
|
4
|
+
// Mocks are defined in setup.ts to prevent @okxweb3/crypto-lib module initialization
|
|
5
|
+
|
|
6
|
+
describe("EmbeddedWallet", () => {
|
|
7
|
+
let embeddedWallet: EmbeddedWallet;
|
|
8
|
+
let mockIframe: HTMLIFrameElement;
|
|
9
|
+
let originalGetElementById: typeof document.getElementById;
|
|
10
|
+
let originalCreateElement: typeof document.createElement;
|
|
11
|
+
let originalAppendChild: typeof document.body.appendChild;
|
|
12
|
+
let messageHandlers: Array<(event: MessageEvent) => void>;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
embeddedWallet = EmbeddedWallet.getInstance();
|
|
16
|
+
vi.clearAllMocks();
|
|
17
|
+
|
|
18
|
+
// Mock iframe
|
|
19
|
+
mockIframe = {
|
|
20
|
+
id: "",
|
|
21
|
+
src: "",
|
|
22
|
+
style: {
|
|
23
|
+
cssText: "",
|
|
24
|
+
width: "",
|
|
25
|
+
height: "",
|
|
26
|
+
backgroundColor: "",
|
|
27
|
+
},
|
|
28
|
+
allow: "",
|
|
29
|
+
closed: false,
|
|
30
|
+
setAttribute: vi.fn(),
|
|
31
|
+
contentWindow: {
|
|
32
|
+
postMessage: vi.fn(),
|
|
33
|
+
} as any,
|
|
34
|
+
} as any;
|
|
35
|
+
|
|
36
|
+
// Mock document methods
|
|
37
|
+
originalGetElementById = document.getElementById;
|
|
38
|
+
originalCreateElement = document.createElement;
|
|
39
|
+
originalAppendChild = document.body.appendChild;
|
|
40
|
+
|
|
41
|
+
document.getElementById = vi.fn().mockReturnValue(null);
|
|
42
|
+
document.createElement = vi.fn((tagName: string) => {
|
|
43
|
+
if (tagName === "iframe") {
|
|
44
|
+
return mockIframe;
|
|
45
|
+
}
|
|
46
|
+
return originalCreateElement.call(document, tagName);
|
|
47
|
+
});
|
|
48
|
+
document.body.appendChild = vi.fn();
|
|
49
|
+
|
|
50
|
+
// Mock message event listeners
|
|
51
|
+
messageHandlers = [];
|
|
52
|
+
window.addEventListener = vi.fn((event: string, handler: any) => {
|
|
53
|
+
if (event === "message") {
|
|
54
|
+
messageHandlers.push(handler);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
window.removeEventListener = vi.fn();
|
|
58
|
+
|
|
59
|
+
// Mock window.location
|
|
60
|
+
Object.defineProperty(window, "location", {
|
|
61
|
+
value: {
|
|
62
|
+
origin: "https://test.com",
|
|
63
|
+
},
|
|
64
|
+
writable: true,
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
afterEach(() => {
|
|
69
|
+
document.getElementById = originalGetElementById;
|
|
70
|
+
document.createElement = originalCreateElement;
|
|
71
|
+
document.body.appendChild = originalAppendChild;
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe("getInstance", () => {
|
|
75
|
+
it("should return singleton instance", () => {
|
|
76
|
+
const instance1 = EmbeddedWallet.getInstance();
|
|
77
|
+
const instance2 = EmbeddedWallet.getInstance();
|
|
78
|
+
expect(instance1).toBe(instance2);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe("init", () => {
|
|
83
|
+
it("should initialize with valid config", async () => {
|
|
84
|
+
const config = {
|
|
85
|
+
clientId: "test-client-id",
|
|
86
|
+
walletBaseUrl: "https://wallet.tomo.inc",
|
|
87
|
+
name: "Test Wallet",
|
|
88
|
+
logo: "https://example.com/logo.png",
|
|
89
|
+
tomoStage: "dev" as const,
|
|
90
|
+
xClientId: "x-client-id",
|
|
91
|
+
googleClientId: "google-client-id",
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// Mock login response
|
|
95
|
+
setTimeout(() => {
|
|
96
|
+
const handler = messageHandlers[0];
|
|
97
|
+
if (handler) {
|
|
98
|
+
handler({
|
|
99
|
+
origin: "https://wallet.tomo.inc",
|
|
100
|
+
data: {
|
|
101
|
+
type: "wallet-ready",
|
|
102
|
+
data: {
|
|
103
|
+
isAvailable: true,
|
|
104
|
+
connectedInfo: {
|
|
105
|
+
evmProvider: {
|
|
106
|
+
connected: true,
|
|
107
|
+
address: ["0x123"],
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
} as MessageEvent);
|
|
113
|
+
}
|
|
114
|
+
}, 10);
|
|
115
|
+
|
|
116
|
+
const result = await embeddedWallet.init(config);
|
|
117
|
+
|
|
118
|
+
expect(result.isAvailable).toBe(true);
|
|
119
|
+
expect(embeddedWallet.config).toEqual(config);
|
|
120
|
+
expect(embeddedWallet.walletOrigin).toBe("https://wallet.tomo.inc");
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("should throw error if clientId is missing", async () => {
|
|
124
|
+
const config = {
|
|
125
|
+
walletBaseUrl: "https://wallet.tomo.inc",
|
|
126
|
+
name: "Test",
|
|
127
|
+
logo: "",
|
|
128
|
+
tomoStage: "dev" as const,
|
|
129
|
+
xClientId: "x",
|
|
130
|
+
googleClientId: "g",
|
|
131
|
+
} as any;
|
|
132
|
+
|
|
133
|
+
await expect(embeddedWallet.init(config)).rejects.toThrow("clientId is required.");
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it("should throw error if walletBaseUrl is missing", async () => {
|
|
137
|
+
const config = {
|
|
138
|
+
clientId: "test",
|
|
139
|
+
name: "Test",
|
|
140
|
+
logo: "",
|
|
141
|
+
tomoStage: "dev" as const,
|
|
142
|
+
xClientId: "x",
|
|
143
|
+
googleClientId: "g",
|
|
144
|
+
} as any;
|
|
145
|
+
|
|
146
|
+
await expect(embeddedWallet.init(config)).rejects.toThrow("walletBaseUrl is required.");
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it("should build rdns correctly when walletBaseUrl has port", async () => {
|
|
150
|
+
const config = {
|
|
151
|
+
clientId: "port-client",
|
|
152
|
+
walletBaseUrl: "https://wallet.example.com:8080",
|
|
153
|
+
name: "Test",
|
|
154
|
+
logo: "",
|
|
155
|
+
tomoStage: "dev" as const,
|
|
156
|
+
xClientId: "x",
|
|
157
|
+
googleClientId: "g",
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
setTimeout(() => {
|
|
161
|
+
const handler = messageHandlers[0];
|
|
162
|
+
if (handler) {
|
|
163
|
+
handler({
|
|
164
|
+
origin: "https://wallet.example.com:8080",
|
|
165
|
+
data: {
|
|
166
|
+
type: "wallet-ready",
|
|
167
|
+
data: { isAvailable: true, connectedInfo: null },
|
|
168
|
+
},
|
|
169
|
+
} as MessageEvent);
|
|
170
|
+
}
|
|
171
|
+
}, 10);
|
|
172
|
+
|
|
173
|
+
await embeddedWallet.init(config);
|
|
174
|
+
expect(embeddedWallet.walletOrigin).toBe("https://wallet.example.com:8080");
|
|
175
|
+
expect(embeddedWallet.info?.uuid).toContain("8080");
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
describe("setWalletZIndex", () => {
|
|
180
|
+
it("should set mask z-index", () => {
|
|
181
|
+
embeddedWallet.setWalletZIndex(9999);
|
|
182
|
+
expect(embeddedWallet.maskZIndex).toBe(9999);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
describe("popup", () => {
|
|
187
|
+
it("should show wallet iframe", () => {
|
|
188
|
+
embeddedWallet.walletIframe = mockIframe;
|
|
189
|
+
embeddedWallet.popup();
|
|
190
|
+
|
|
191
|
+
expect(mockIframe.style.cssText).toContain("position: fixed");
|
|
192
|
+
expect(mockIframe.style.cssText).toContain("z-index: 999");
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it("should do nothing if walletIframe is null", () => {
|
|
196
|
+
embeddedWallet.walletIframe = null;
|
|
197
|
+
expect(() => embeddedWallet.popup()).not.toThrow();
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
describe("close", () => {
|
|
202
|
+
it("should hide wallet iframe", () => {
|
|
203
|
+
embeddedWallet.walletIframe = mockIframe;
|
|
204
|
+
embeddedWallet.close();
|
|
205
|
+
|
|
206
|
+
expect(mockIframe.style.width).toBe("0");
|
|
207
|
+
expect(mockIframe.style.height).toBe("0");
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("should do nothing if walletIframe is null", () => {
|
|
211
|
+
embeddedWallet.walletIframe = null;
|
|
212
|
+
expect(() => embeddedWallet.close()).not.toThrow();
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
describe("request", () => {
|
|
217
|
+
it("should send request to wallet iframe", async () => {
|
|
218
|
+
embeddedWallet.walletIframe = mockIframe;
|
|
219
|
+
embeddedWallet.walletOrigin = "https://wallet.tomo.inc";
|
|
220
|
+
|
|
221
|
+
// Mock response
|
|
222
|
+
setTimeout(() => {
|
|
223
|
+
const handler = messageHandlers[0];
|
|
224
|
+
if (handler) {
|
|
225
|
+
handler({
|
|
226
|
+
origin: "https://wallet.tomo.inc",
|
|
227
|
+
data: {
|
|
228
|
+
type: "wallet-response",
|
|
229
|
+
data: { result: "success" },
|
|
230
|
+
},
|
|
231
|
+
} as MessageEvent);
|
|
232
|
+
}
|
|
233
|
+
}, 10);
|
|
234
|
+
|
|
235
|
+
const result = await embeddedWallet.request("token", { test: "params" });
|
|
236
|
+
|
|
237
|
+
expect(mockIframe.contentWindow?.postMessage).toHaveBeenCalled();
|
|
238
|
+
expect(result).toEqual({ result: "success" });
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
describe("open", () => {
|
|
243
|
+
it("should open wallet page and show popup", () => {
|
|
244
|
+
embeddedWallet.walletIframe = mockIframe;
|
|
245
|
+
embeddedWallet.walletOrigin = "https://wallet.tomo.inc";
|
|
246
|
+
|
|
247
|
+
embeddedWallet.open("wallet", { test: "params" });
|
|
248
|
+
|
|
249
|
+
expect(mockIframe.contentWindow?.postMessage).toHaveBeenCalled();
|
|
250
|
+
expect(mockIframe.style.cssText).toContain("position: fixed");
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
describe("onLoginStatusChanged", () => {
|
|
255
|
+
it("should set login status callback", () => {
|
|
256
|
+
const callback = vi.fn();
|
|
257
|
+
embeddedWallet.onLoginStatusChanged(callback);
|
|
258
|
+
|
|
259
|
+
expect(embeddedWallet.loginStatusCallback).toBe(callback);
|
|
260
|
+
expect(window.addEventListener).toHaveBeenCalled();
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it("should call loginStatusCallback with logout when logout message received", () => {
|
|
264
|
+
embeddedWallet.walletIframe = mockIframe;
|
|
265
|
+
embeddedWallet.walletOrigin = "https://wallet.tomo.inc";
|
|
266
|
+
const callback = vi.fn();
|
|
267
|
+
let capturedHandler: ((e: MessageEvent) => void) | null = null;
|
|
268
|
+
window.addEventListener = vi.fn((_event: string, handler: any) => {
|
|
269
|
+
messageHandlers.push(handler);
|
|
270
|
+
capturedHandler = handler;
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
embeddedWallet.onLoginStatusChanged(callback);
|
|
274
|
+
|
|
275
|
+
expect(capturedHandler).not.toBeNull();
|
|
276
|
+
capturedHandler!({
|
|
277
|
+
origin: "https://wallet.tomo.inc",
|
|
278
|
+
data: { type: "wallet-response", method: "logout" },
|
|
279
|
+
} as MessageEvent);
|
|
280
|
+
|
|
281
|
+
expect(callback).toHaveBeenCalledWith({ type: "logout" });
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
describe("themeChange", () => {
|
|
286
|
+
it("should persist theme and send theme-change request", async () => {
|
|
287
|
+
embeddedWallet.walletIframe = mockIframe;
|
|
288
|
+
embeddedWallet.walletOrigin = "https://wallet.tomo.inc";
|
|
289
|
+
|
|
290
|
+
embeddedWallet.themeChange({ mode: "dark" });
|
|
291
|
+
|
|
292
|
+
expect(embeddedWallet["themeConfig"]).toEqual({ mode: "dark" });
|
|
293
|
+
expect(mockIframe.contentWindow?.postMessage).toHaveBeenCalledWith(
|
|
294
|
+
expect.objectContaining({
|
|
295
|
+
type: "wallet-request",
|
|
296
|
+
data: { method: "theme-change", params: { mode: "dark" } },
|
|
297
|
+
}),
|
|
298
|
+
"https://wallet.tomo.inc",
|
|
299
|
+
);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it("should set themeConfig and call request with theme-change", () => {
|
|
303
|
+
embeddedWallet.walletIframe = mockIframe;
|
|
304
|
+
embeddedWallet.walletOrigin = "https://wallet.tomo.inc";
|
|
305
|
+
const requestSpy = vi.spyOn(embeddedWallet, "request").mockResolvedValue(undefined);
|
|
306
|
+
|
|
307
|
+
embeddedWallet.themeChange({ primary: "#000" });
|
|
308
|
+
|
|
309
|
+
expect(embeddedWallet["themeConfig"]).toEqual({ primary: "#000" });
|
|
310
|
+
expect(requestSpy).toHaveBeenCalledWith("theme-change", { primary: "#000" });
|
|
311
|
+
requestSpy.mockRestore();
|
|
312
|
+
});
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
describe("logout", () => {
|
|
316
|
+
it("should set iframe src to logout and remove listeners", async () => {
|
|
317
|
+
embeddedWallet.walletIframe = mockIframe;
|
|
318
|
+
embeddedWallet.walletOrigin = "https://wallet.tomo.inc";
|
|
319
|
+
|
|
320
|
+
const result = await embeddedWallet.logout();
|
|
321
|
+
|
|
322
|
+
expect(result).toBe(true);
|
|
323
|
+
expect(mockIframe.src).toBe("https://wallet.tomo.inc#logout=true");
|
|
324
|
+
expect(window.removeEventListener).toHaveBeenCalled();
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
describe("login", () => {
|
|
330
|
+
it("should throw when config is not initialized", async () => {
|
|
331
|
+
embeddedWallet.config = null;
|
|
332
|
+
await expect(embeddedWallet.login()).rejects.toThrow("config is not initialized");
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
it("should throw when walletBaseUrl or clientId is missing in config", async () => {
|
|
336
|
+
embeddedWallet.config = { clientId: "c", walletBaseUrl: "" } as any;
|
|
337
|
+
document.getElementById = vi.fn().mockReturnValue(null);
|
|
338
|
+
document.body.appendChild = vi.fn();
|
|
339
|
+
|
|
340
|
+
await expect(embeddedWallet.login()).rejects.toThrow("walletBaseUrl + clientId is required");
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it("should reject when appendChild throws", async () => {
|
|
344
|
+
embeddedWallet.config = {
|
|
345
|
+
clientId: "test-id",
|
|
346
|
+
walletBaseUrl: "https://wallet.tomo.inc",
|
|
347
|
+
name: "Test",
|
|
348
|
+
logo: "",
|
|
349
|
+
stage: "dev",
|
|
350
|
+
xClientId: "x",
|
|
351
|
+
googleClientId: "g",
|
|
352
|
+
} as any;
|
|
353
|
+
document.getElementById = vi.fn().mockReturnValue(null);
|
|
354
|
+
document.body.appendChild = vi.fn().mockImplementation(() => {
|
|
355
|
+
throw new Error("append failed");
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
await expect(embeddedWallet.login()).rejects.toThrow("append failed");
|
|
359
|
+
expect(window.removeEventListener).toHaveBeenCalled();
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
it("should call loginStatusCallback when wallet-ready received", async () => {
|
|
363
|
+
const config = {
|
|
364
|
+
clientId: "test-client-id",
|
|
365
|
+
walletBaseUrl: "https://wallet.tomo.inc",
|
|
366
|
+
name: "Test",
|
|
367
|
+
logo: "",
|
|
368
|
+
tomoStage: "dev" as const,
|
|
369
|
+
xClientId: "x",
|
|
370
|
+
googleClientId: "g",
|
|
371
|
+
};
|
|
372
|
+
embeddedWallet.loginStatusCallback = vi.fn();
|
|
373
|
+
|
|
374
|
+
setTimeout(() => {
|
|
375
|
+
const handler = messageHandlers[0];
|
|
376
|
+
if (handler) {
|
|
377
|
+
handler({
|
|
378
|
+
origin: "https://wallet.tomo.inc",
|
|
379
|
+
data: {
|
|
380
|
+
type: "wallet-ready",
|
|
381
|
+
data: { isAvailable: true, connectedInfo: null },
|
|
382
|
+
},
|
|
383
|
+
} as MessageEvent);
|
|
384
|
+
}
|
|
385
|
+
}, 10);
|
|
386
|
+
|
|
387
|
+
await embeddedWallet.init(config);
|
|
388
|
+
|
|
389
|
+
expect(embeddedWallet.loginStatusCallback).toHaveBeenCalledWith({ type: "login" });
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it("should cache oidcToken when login is called with token", async () => {
|
|
393
|
+
const config = {
|
|
394
|
+
clientId: "cache-client",
|
|
395
|
+
walletBaseUrl: "https://wallet.tomo.inc",
|
|
396
|
+
name: "Test",
|
|
397
|
+
logo: "",
|
|
398
|
+
tomoStage: "dev" as const,
|
|
399
|
+
xClientId: "x",
|
|
400
|
+
googleClientId: "g",
|
|
401
|
+
};
|
|
402
|
+
setTimeout(() => {
|
|
403
|
+
const handler = messageHandlers[0];
|
|
404
|
+
if (handler) {
|
|
405
|
+
handler({
|
|
406
|
+
origin: "https://wallet.tomo.inc",
|
|
407
|
+
data: {
|
|
408
|
+
type: "wallet-ready",
|
|
409
|
+
data: { isAvailable: true, connectedInfo: null },
|
|
410
|
+
},
|
|
411
|
+
} as MessageEvent);
|
|
412
|
+
}
|
|
413
|
+
}, 10);
|
|
414
|
+
await embeddedWallet.init(config);
|
|
415
|
+
|
|
416
|
+
const lenBefore = messageHandlers.length;
|
|
417
|
+
const loginPromise = embeddedWallet.login("cached-oidc-token");
|
|
418
|
+
expect(embeddedWallet["oidcToken"]).toBe("cached-oidc-token");
|
|
419
|
+
|
|
420
|
+
setTimeout(() => {
|
|
421
|
+
const handler = messageHandlers[lenBefore];
|
|
422
|
+
if (handler) {
|
|
423
|
+
handler({
|
|
424
|
+
origin: "https://wallet.tomo.inc",
|
|
425
|
+
data: {
|
|
426
|
+
type: "wallet-ready",
|
|
427
|
+
data: { isAvailable: true, connectedInfo: null },
|
|
428
|
+
},
|
|
429
|
+
} as MessageEvent);
|
|
430
|
+
}
|
|
431
|
+
}, 50);
|
|
432
|
+
await loginPromise;
|
|
433
|
+
}, 8000);
|
|
434
|
+
|
|
435
|
+
it("should remove existing iframe when login called with oidcToken", async () => {
|
|
436
|
+
const existingIframe = {
|
|
437
|
+
id: "existing-client",
|
|
438
|
+
remove: vi.fn(),
|
|
439
|
+
style: {},
|
|
440
|
+
setAttribute: vi.fn(),
|
|
441
|
+
allow: "",
|
|
442
|
+
contentWindow: { postMessage: vi.fn() },
|
|
443
|
+
} as any;
|
|
444
|
+
document.getElementById = vi.fn().mockReturnValue(existingIframe);
|
|
445
|
+
document.body.appendChild = vi.fn();
|
|
446
|
+
|
|
447
|
+
embeddedWallet.config = {
|
|
448
|
+
clientId: "existing-client",
|
|
449
|
+
walletBaseUrl: "https://wallet.tomo.inc",
|
|
450
|
+
name: "Test",
|
|
451
|
+
logo: "",
|
|
452
|
+
stage: "dev",
|
|
453
|
+
xClientId: "x",
|
|
454
|
+
googleClientId: "g",
|
|
455
|
+
} as any;
|
|
456
|
+
|
|
457
|
+
setTimeout(() => {
|
|
458
|
+
const handler = messageHandlers[0];
|
|
459
|
+
if (handler) {
|
|
460
|
+
handler({
|
|
461
|
+
origin: "https://wallet.tomo.inc",
|
|
462
|
+
data: {
|
|
463
|
+
type: "wallet-ready",
|
|
464
|
+
data: { isAvailable: true, connectedInfo: null },
|
|
465
|
+
},
|
|
466
|
+
} as MessageEvent);
|
|
467
|
+
}
|
|
468
|
+
}, 15);
|
|
469
|
+
|
|
470
|
+
await embeddedWallet.login("token-to-recreate-iframe");
|
|
471
|
+
|
|
472
|
+
expect(existingIframe.remove).toHaveBeenCalled();
|
|
473
|
+
}, 8000);
|
|
474
|
+
|
|
475
|
+
it("should close wallet when wallet-close message received during login", async () => {
|
|
476
|
+
const config = {
|
|
477
|
+
clientId: "close-test",
|
|
478
|
+
walletBaseUrl: "https://wallet.tomo.inc",
|
|
479
|
+
name: "Test",
|
|
480
|
+
logo: "",
|
|
481
|
+
tomoStage: "dev" as const,
|
|
482
|
+
xClientId: "x",
|
|
483
|
+
googleClientId: "g",
|
|
484
|
+
};
|
|
485
|
+
|
|
486
|
+
setTimeout(() => {
|
|
487
|
+
const handler = messageHandlers[0];
|
|
488
|
+
if (handler) {
|
|
489
|
+
handler({
|
|
490
|
+
origin: "https://wallet.tomo.inc",
|
|
491
|
+
data: { type: "wallet-close" },
|
|
492
|
+
} as MessageEvent);
|
|
493
|
+
}
|
|
494
|
+
}, 5);
|
|
495
|
+
setTimeout(() => {
|
|
496
|
+
const handler = messageHandlers[0];
|
|
497
|
+
if (handler) {
|
|
498
|
+
handler({
|
|
499
|
+
origin: "https://wallet.tomo.inc",
|
|
500
|
+
data: {
|
|
501
|
+
type: "wallet-ready",
|
|
502
|
+
data: { isAvailable: false, connectedInfo: null },
|
|
503
|
+
},
|
|
504
|
+
} as MessageEvent);
|
|
505
|
+
}
|
|
506
|
+
}, 15);
|
|
507
|
+
|
|
508
|
+
await embeddedWallet.init(config);
|
|
509
|
+
expect(embeddedWallet.walletIframe?.style.width).toBe("0");
|
|
510
|
+
}, 8000);
|
|
511
|
+
|
|
512
|
+
it("should close wallet when walletCloseHandler receives wallet-close message", async () => {
|
|
513
|
+
const config = {
|
|
514
|
+
clientId: "handler-close-test",
|
|
515
|
+
walletBaseUrl: "https://wallet.tomo.inc",
|
|
516
|
+
name: "Test",
|
|
517
|
+
logo: "",
|
|
518
|
+
tomoStage: "dev" as const,
|
|
519
|
+
xClientId: "x",
|
|
520
|
+
googleClientId: "g",
|
|
521
|
+
};
|
|
522
|
+
setTimeout(() => {
|
|
523
|
+
const handler = messageHandlers[0];
|
|
524
|
+
if (handler) {
|
|
525
|
+
handler({
|
|
526
|
+
origin: "https://wallet.tomo.inc",
|
|
527
|
+
data: {
|
|
528
|
+
type: "wallet-ready",
|
|
529
|
+
data: { isAvailable: true, connectedInfo: null },
|
|
530
|
+
},
|
|
531
|
+
} as MessageEvent);
|
|
532
|
+
}
|
|
533
|
+
}, 10);
|
|
534
|
+
await embeddedWallet.init(config);
|
|
535
|
+
|
|
536
|
+
embeddedWallet.walletIframe = mockIframe;
|
|
537
|
+
embeddedWallet.walletOrigin = "https://wallet.tomo.inc";
|
|
538
|
+
const walletCloseHandler = (embeddedWallet as any).walletCloseHandler as (ev: { origin: string; data: any }) => void;
|
|
539
|
+
walletCloseHandler({
|
|
540
|
+
origin: "https://wallet.tomo.inc",
|
|
541
|
+
data: { type: "wallet-close" },
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
expect(mockIframe.style.width).toBe("0");
|
|
545
|
+
}, 8000);
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
describe("loginByEmail", () => {
|
|
549
|
+
it("should request emailLogin and resolve with oidcToken", async () => {
|
|
550
|
+
embeddedWallet.walletIframe = mockIframe;
|
|
551
|
+
embeddedWallet.walletOrigin = "https://wallet.tomo.inc";
|
|
552
|
+
|
|
553
|
+
const loginPromise = embeddedWallet.loginByEmail({ email: "a@b.com" });
|
|
554
|
+
|
|
555
|
+
await new Promise((r) => setTimeout(r, 25));
|
|
556
|
+
const responseHandler = messageHandlers[1];
|
|
557
|
+
if (responseHandler) {
|
|
558
|
+
responseHandler({
|
|
559
|
+
origin: "https://wallet.tomo.inc",
|
|
560
|
+
data: { type: "wallet-response", data: { oidcToken: "email-oidc-token" } },
|
|
561
|
+
} as MessageEvent);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
const result = await loginPromise;
|
|
565
|
+
expect(result).toBe("email-oidc-token");
|
|
566
|
+
}, 8000);
|
|
567
|
+
|
|
568
|
+
it("should reject when request fails", async () => {
|
|
569
|
+
embeddedWallet.walletIframe = mockIframe;
|
|
570
|
+
embeddedWallet.walletOrigin = "https://wallet.tomo.inc";
|
|
571
|
+
vi.spyOn(embeddedWallet, "request").mockRejectedValueOnce(new Error("network error"));
|
|
572
|
+
|
|
573
|
+
await expect(embeddedWallet.loginByEmail({ email: "a@b.com" })).rejects.toThrow("network error");
|
|
574
|
+
});
|
|
575
|
+
|
|
576
|
+
it("should resolve with empty string when wallet-close received", async () => {
|
|
577
|
+
embeddedWallet.walletIframe = mockIframe;
|
|
578
|
+
embeddedWallet.walletOrigin = "https://wallet.tomo.inc";
|
|
579
|
+
|
|
580
|
+
const handlers: Array<(e: MessageEvent) => void> = [];
|
|
581
|
+
window.addEventListener = vi.fn((_event: string, handler: any) => {
|
|
582
|
+
handlers.push(handler);
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
const resultPromise = embeddedWallet.loginByEmail({ email: "a@b.com" });
|
|
586
|
+
|
|
587
|
+
await new Promise((r) => setTimeout(r, 25));
|
|
588
|
+
const closeHandler = handlers[0];
|
|
589
|
+
if (closeHandler) {
|
|
590
|
+
closeHandler({
|
|
591
|
+
origin: "https://wallet.tomo.inc",
|
|
592
|
+
data: { type: "wallet-close" },
|
|
593
|
+
} as MessageEvent);
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
const result = await resultPromise;
|
|
597
|
+
expect(result).toBe("");
|
|
598
|
+
}, 8000);
|
|
599
|
+
});
|
|
600
|
+
});
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
+
import { sendRequest, onResponse, notPorivderAPIs } from "../hub";
|
|
3
|
+
|
|
4
|
+
// Mock EmbeddedWallet
|
|
5
|
+
vi.mock("../embedded-wallet", () => ({
|
|
6
|
+
EmbeddedWallet: {
|
|
7
|
+
getInstance: vi.fn().mockReturnValue({
|
|
8
|
+
walletOrigin: "https://wallet.tomo.inc",
|
|
9
|
+
walletIframe: {
|
|
10
|
+
contentWindow: {
|
|
11
|
+
postMessage: vi.fn(),
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
popup: vi.fn(),
|
|
15
|
+
close: vi.fn(),
|
|
16
|
+
}),
|
|
17
|
+
},
|
|
18
|
+
}));
|
|
19
|
+
|
|
20
|
+
describe("hub", () => {
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
window.addEventListener = vi.fn();
|
|
23
|
+
window.removeEventListener = vi.fn();
|
|
24
|
+
});
|
|
25
|
+
describe("notPorivderAPIs", () => {
|
|
26
|
+
it("should have correct API flags", () => {
|
|
27
|
+
expect(notPorivderAPIs.keepAlive).toBe(true);
|
|
28
|
+
expect(notPorivderAPIs.wallet_getProviderState).toBe(true);
|
|
29
|
+
expect(notPorivderAPIs.wallet_sendDomainMetadata).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe("sendRequest", () => {
|
|
34
|
+
it("should throw error if chainType is empty", async () => {
|
|
35
|
+
await expect(sendRequest("", { method: "test" })).rejects.toThrow(
|
|
36
|
+
"chainType or method is not allowed to be empty",
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("should throw error if method is empty", async () => {
|
|
41
|
+
await expect(sendRequest("evm", { method: "" })).rejects.toThrow(
|
|
42
|
+
"chainType or method is not allowed to be empty",
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("should return early for notProviderAPIs", async () => {
|
|
47
|
+
const result = await sendRequest("evm", { method: "keepAlive" });
|
|
48
|
+
expect(result).toBeUndefined();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("should send request to wallet iframe", async () => {
|
|
52
|
+
const { EmbeddedWallet } = await import("../embedded-wallet");
|
|
53
|
+
const mockInstance = EmbeddedWallet.getInstance();
|
|
54
|
+
|
|
55
|
+
await sendRequest("evm", { method: "eth_requestAccounts", params: {} });
|
|
56
|
+
|
|
57
|
+
expect(mockInstance.walletIframe.contentWindow?.postMessage).toHaveBeenCalled();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("should return early when walletOrigin or walletIframe is not set", async () => {
|
|
61
|
+
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
62
|
+
const { EmbeddedWallet } = await import("../embedded-wallet");
|
|
63
|
+
vi.mocked(EmbeddedWallet.getInstance).mockReturnValueOnce({
|
|
64
|
+
walletOrigin: "",
|
|
65
|
+
walletIframe: null,
|
|
66
|
+
popup: vi.fn(),
|
|
67
|
+
close: vi.fn(),
|
|
68
|
+
} as any);
|
|
69
|
+
|
|
70
|
+
const result = await sendRequest("evm", { method: "eth_requestAccounts", params: {} });
|
|
71
|
+
|
|
72
|
+
expect(result).toBeUndefined();
|
|
73
|
+
expect(consoleSpy).toHaveBeenCalled();
|
|
74
|
+
consoleSpy.mockRestore();
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe("onResponse", () => {
|
|
79
|
+
it("should resolve with success response", async () => {
|
|
80
|
+
let messageHandler: ((e: { origin: string; data: any }) => void) | null = null;
|
|
81
|
+
window.addEventListener = vi.fn((event: string, handler: any) => {
|
|
82
|
+
if (event === "message") messageHandler = handler;
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const promise = onResponse({ method: "test" });
|
|
86
|
+
|
|
87
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
88
|
+
if (messageHandler) {
|
|
89
|
+
messageHandler({
|
|
90
|
+
origin: "https://wallet.tomo.inc",
|
|
91
|
+
data: {
|
|
92
|
+
type: "dapp-response",
|
|
93
|
+
method: "test",
|
|
94
|
+
success: true,
|
|
95
|
+
data: { result: "success" },
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const result = await promise;
|
|
101
|
+
expect(result).toBeDefined();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("should reject with error response", async () => {
|
|
105
|
+
let messageHandler: ((e: { origin: string; data: any }) => void) | null = null;
|
|
106
|
+
window.addEventListener = vi.fn((event: string, handler: any) => {
|
|
107
|
+
if (event === "message") messageHandler = handler;
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const promise = onResponse({ method: "test" });
|
|
111
|
+
|
|
112
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
113
|
+
if (messageHandler) {
|
|
114
|
+
messageHandler({
|
|
115
|
+
origin: "https://wallet.tomo.inc",
|
|
116
|
+
data: {
|
|
117
|
+
type: "dapp-response",
|
|
118
|
+
method: "test",
|
|
119
|
+
success: false,
|
|
120
|
+
data: { error: "test error" },
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
await expect(promise).rejects.toEqual({ error: "test error" });
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("should ignore non-dapp-response messages", async () => {
|
|
129
|
+
let messageHandler: ((e: { origin: string; data: any }) => void) | null = null;
|
|
130
|
+
window.addEventListener = vi.fn((event: string, handler: any) => {
|
|
131
|
+
if (event === "message") messageHandler = handler;
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const promise = onResponse({ method: "test" });
|
|
135
|
+
let resolved = false;
|
|
136
|
+
promise.then(() => {
|
|
137
|
+
resolved = true;
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
141
|
+
if (messageHandler) {
|
|
142
|
+
messageHandler({
|
|
143
|
+
origin: "https://wallet.tomo.inc",
|
|
144
|
+
data: { type: "other-message", method: "test" },
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
await new Promise((r) => setTimeout(r, 20));
|
|
149
|
+
expect(resolved).toBe(false);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
describe("embedded-wallet-providers", () => {
|
|
4
|
+
it("should export EmbeddedWallet", async () => {
|
|
5
|
+
const module = await import("../index");
|
|
6
|
+
expect(module.EmbeddedWallet).toBeDefined();
|
|
7
|
+
expect(typeof module.EmbeddedWallet.getInstance).toBe("function");
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it("should return same instance when getting EmbeddedWallet from index", async () => {
|
|
11
|
+
const { EmbeddedWallet } = await import("../index");
|
|
12
|
+
const a = EmbeddedWallet.getInstance();
|
|
13
|
+
const b = EmbeddedWallet.getInstance();
|
|
14
|
+
expect(a).toBe(b);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("should export InitConfig type", async () => {
|
|
18
|
+
const module = await import("../index");
|
|
19
|
+
expect(module).toBeDefined();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("should export EmailLoginResult type", async () => {
|
|
23
|
+
const module = await import("../index");
|
|
24
|
+
expect(module).toBeDefined();
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
dappPopups,
|
|
4
|
+
evmDappPopups,
|
|
5
|
+
dogeDappPopups,
|
|
6
|
+
solanaDappPopups,
|
|
7
|
+
tronDappPopups,
|
|
8
|
+
btcDappPopups,
|
|
9
|
+
shopDappPopups,
|
|
10
|
+
} from "../relay-route";
|
|
11
|
+
import { ChainTypeEnum } from "@tomo-inc/wallet-utils";
|
|
12
|
+
|
|
13
|
+
describe("relay-route", () => {
|
|
14
|
+
describe("dappPopups", () => {
|
|
15
|
+
it("should have popups for all chain types", () => {
|
|
16
|
+
expect(dappPopups[ChainTypeEnum.EVM]).toBeDefined();
|
|
17
|
+
expect(dappPopups[ChainTypeEnum.DOGECOIN]).toBeDefined();
|
|
18
|
+
expect(dappPopups[ChainTypeEnum.SOLANA]).toBeDefined();
|
|
19
|
+
expect(dappPopups[ChainTypeEnum.TRON]).toBeDefined();
|
|
20
|
+
expect(dappPopups[ChainTypeEnum.BITCOIN]).toBeDefined();
|
|
21
|
+
expect(dappPopups.shop).toBeDefined();
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe("evmDappPopups", () => {
|
|
26
|
+
it("should have correct EVM popup routes", () => {
|
|
27
|
+
expect(evmDappPopups.connect).toBe("/dapp/connect");
|
|
28
|
+
expect(evmDappPopups.eth_requestAccounts).toBe("/dapp/connect");
|
|
29
|
+
expect(evmDappPopups.wallet_addEthereumChain).toBe("/dapp-evm/network-add");
|
|
30
|
+
expect(evmDappPopups.personal_sign).toBe("/dapp-evm/message-sign");
|
|
31
|
+
expect(evmDappPopups.eth_sendTransaction).toBe("/dapp-evm/tx-send");
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
describe("dogeDappPopups", () => {
|
|
36
|
+
it("should have correct Dogecoin popup routes", () => {
|
|
37
|
+
expect(dogeDappPopups.connect).toBe("/dapp/connect");
|
|
38
|
+
expect(dogeDappPopups.signMessage).toBe("/dapp-doge/message-sign");
|
|
39
|
+
expect(dogeDappPopups.requestTransaction).toBe("/dapp-doge/tx-send");
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe("solanaDappPopups", () => {
|
|
44
|
+
it("should have correct Solana popup routes", () => {
|
|
45
|
+
expect(solanaDappPopups.connect).toBe("/dapp/connect");
|
|
46
|
+
expect(solanaDappPopups.signMessage).toBe("/dapp-solana/message-sign");
|
|
47
|
+
expect(solanaDappPopups.signTransaction).toBe("/dapp-solana/tx-send");
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe("tronDappPopups", () => {
|
|
52
|
+
it("should have correct Tron popup routes", () => {
|
|
53
|
+
expect(tronDappPopups.connect).toBe("/dapp/connect");
|
|
54
|
+
expect(tronDappPopups.signMessage).toBe("/dapp-tron/message-sign");
|
|
55
|
+
expect(tronDappPopups.sendTransaction).toBe("/dapp-tron/tx-send");
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
describe("btcDappPopups", () => {
|
|
60
|
+
it("should have correct Bitcoin popup routes", () => {
|
|
61
|
+
expect(btcDappPopups.connect).toBe("/dapp/connect");
|
|
62
|
+
expect(btcDappPopups.signMessage).toBe("/dapp-btc/message-sign");
|
|
63
|
+
expect(btcDappPopups.sendBitcoin).toBe("/dapp-btc/tx-send");
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe("shopDappPopups", () => {
|
|
68
|
+
it("should have correct shop popup routes", () => {
|
|
69
|
+
expect(shopDappPopups.connect).toBe("/dapp-shop/dialog");
|
|
70
|
+
expect(shopDappPopups.welcomeDialog).toBe("/dapp-shop/dialog");
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Setup file to mock problematic modules before any tests run
|
|
2
|
+
// ALL mocks must be here to prevent module-level code execution
|
|
3
|
+
import { vi } from "vitest";
|
|
4
|
+
|
|
5
|
+
// Mock @okxweb3/crypto-lib FIRST - this is critical to prevent module-level execution
|
|
6
|
+
vi.mock("@okxweb3/crypto-lib", () => ({}));
|
|
7
|
+
vi.mock("@okxweb3/crypto-lib/dist/signutil/schnorr/stark", () => ({}));
|
|
8
|
+
vi.mock("@okxweb3/crypto-lib/dist/signutil/schnorr", () => ({}));
|
|
9
|
+
vi.mock("@okxweb3/crypto-lib/dist/signutil", () => ({}));
|
|
10
|
+
|
|
11
|
+
// Mock @noble/hashes to prevent module-level initialization errors
|
|
12
|
+
vi.mock("@noble/hashes", () => ({
|
|
13
|
+
sha256: vi.fn(),
|
|
14
|
+
sha512: vi.fn(),
|
|
15
|
+
ripemd160: vi.fn(),
|
|
16
|
+
hmac: vi.fn(),
|
|
17
|
+
pbkdf2: vi.fn(),
|
|
18
|
+
scrypt: vi.fn(),
|
|
19
|
+
utils: {
|
|
20
|
+
bytesToHex: vi.fn(),
|
|
21
|
+
hexToBytes: vi.fn(),
|
|
22
|
+
toBytes: vi.fn((input: any) => {
|
|
23
|
+
if (input instanceof Uint8Array) return input;
|
|
24
|
+
if (typeof input === "string") return new TextEncoder().encode(input);
|
|
25
|
+
return new Uint8Array(0);
|
|
26
|
+
}),
|
|
27
|
+
},
|
|
28
|
+
default: {},
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
// Mock @tomo-inc/inject-providers to prevent crypto-lib initialization
|
|
32
|
+
vi.mock("@tomo-inc/inject-providers", () => ({
|
|
33
|
+
BitcoinProvider: class MockBitcoinProvider {
|
|
34
|
+
constructor() {}
|
|
35
|
+
},
|
|
36
|
+
BtcProvider: class MockBtcProvider {
|
|
37
|
+
constructor() {}
|
|
38
|
+
},
|
|
39
|
+
DogecoinProvider: class MockDogecoinProvider {
|
|
40
|
+
constructor() {}
|
|
41
|
+
},
|
|
42
|
+
EvmProvider: class MockEvmProvider {
|
|
43
|
+
setConnectedStatus = vi.fn();
|
|
44
|
+
constructor() {}
|
|
45
|
+
},
|
|
46
|
+
SolanaProvider: class MockSolanaProvider {
|
|
47
|
+
constructor() {}
|
|
48
|
+
},
|
|
49
|
+
TronProvider: class MockTronProvider {
|
|
50
|
+
constructor() {}
|
|
51
|
+
},
|
|
52
|
+
}));
|
|
53
|
+
|
|
54
|
+
// Mock @tomo-inc/oidc-auth
|
|
55
|
+
vi.mock("@tomo-inc/oidc-auth", () => ({
|
|
56
|
+
OidcAuth: vi.fn().mockReturnValue({
|
|
57
|
+
loginByGoogle: vi.fn().mockResolvedValue("google-token"),
|
|
58
|
+
loginByX: vi.fn().mockResolvedValue("x-token"),
|
|
59
|
+
}),
|
|
60
|
+
}));
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from "vitest";
|
|
2
|
+
import { isMobile, openWindow } from "../utils";
|
|
3
|
+
|
|
4
|
+
describe("utils", () => {
|
|
5
|
+
describe("isMobile", () => {
|
|
6
|
+
it("should detect mobile devices", () => {
|
|
7
|
+
Object.defineProperty(navigator, "userAgent", {
|
|
8
|
+
value: "Mozilla/5.0 (iPhone; CPU iPhone OS 14_0 like Mac OS X)",
|
|
9
|
+
writable: true,
|
|
10
|
+
configurable: true,
|
|
11
|
+
});
|
|
12
|
+
expect(isMobile()).toBe(true);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("should return false for desktop", () => {
|
|
16
|
+
Object.defineProperty(navigator, "userAgent", {
|
|
17
|
+
value: "Mozilla/5.0 (Windows NT 10.0; Win64; x64)",
|
|
18
|
+
writable: true,
|
|
19
|
+
configurable: true,
|
|
20
|
+
});
|
|
21
|
+
expect(isMobile()).toBe(false);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe("openWindow", () => {
|
|
26
|
+
it("should open window with default dimensions", () => {
|
|
27
|
+
const mockWindow = { closed: false } as Window;
|
|
28
|
+
window.open = vi.fn().mockReturnValue(mockWindow);
|
|
29
|
+
|
|
30
|
+
Object.defineProperty(window, "innerHeight", { value: 800, writable: true });
|
|
31
|
+
Object.defineProperty(window, "innerWidth", { value: 1200, writable: true });
|
|
32
|
+
Object.defineProperty(window, "screenY", { value: 0, writable: true });
|
|
33
|
+
Object.defineProperty(window, "screenX", { value: 0, writable: true });
|
|
34
|
+
|
|
35
|
+
const result = openWindow({ url: "https://example.com" });
|
|
36
|
+
expect(result).toBe(mockWindow);
|
|
37
|
+
expect(window.open).toHaveBeenCalled();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("should return null when window.open is blocked", () => {
|
|
41
|
+
window.open = vi.fn().mockReturnValue(null);
|
|
42
|
+
Object.defineProperty(window, "innerHeight", { value: 800, writable: true });
|
|
43
|
+
Object.defineProperty(window, "innerWidth", { value: 1200, writable: true });
|
|
44
|
+
Object.defineProperty(window, "screenY", { value: 0, writable: true });
|
|
45
|
+
Object.defineProperty(window, "screenX", { value: 0, writable: true });
|
|
46
|
+
|
|
47
|
+
const result = openWindow({ url: "https://example.com" });
|
|
48
|
+
expect(result).toBeNull();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("should return null when window.open throws", () => {
|
|
52
|
+
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
53
|
+
window.open = vi.fn().mockImplementation(() => {
|
|
54
|
+
throw new Error("popup blocked");
|
|
55
|
+
});
|
|
56
|
+
Object.defineProperty(window, "innerHeight", { value: 800, writable: true });
|
|
57
|
+
Object.defineProperty(window, "innerWidth", { value: 1200, writable: true });
|
|
58
|
+
Object.defineProperty(window, "screenY", { value: 0, writable: true });
|
|
59
|
+
Object.defineProperty(window, "screenX", { value: 0, writable: true });
|
|
60
|
+
|
|
61
|
+
const result = openWindow({ url: "https://example.com" });
|
|
62
|
+
expect(result).toBeNull();
|
|
63
|
+
consoleSpy.mockRestore();
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
});
|
package/src/embedded-wallet.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
import { onResponse, sendRequest } from "./hub";
|
|
9
9
|
|
|
10
10
|
import { OidcAuth } from "@tomo-inc/oidc-auth";
|
|
11
|
-
import { ChainTypeEnum, ProviderProtocol, ProviderStandard
|
|
11
|
+
import { ChainTypeEnum, ProviderProtocol, ProviderStandard } from "@tomo-inc/wallet-utils";
|
|
12
12
|
import {
|
|
13
13
|
EmbeddedWalletConfig,
|
|
14
14
|
EmbeddedWalletConnectors,
|
|
@@ -151,17 +151,18 @@ export class EmbeddedWallet {
|
|
|
151
151
|
if (!config) {
|
|
152
152
|
throw new Error("config is not initialized");
|
|
153
153
|
}
|
|
154
|
+
this.oidcToken = oidcToken || "";
|
|
154
155
|
|
|
155
156
|
// cache oidcToken for second login(2nd reason unknown)
|
|
156
|
-
const cacheKey = config.clientId + "-cube-oidc-token";
|
|
157
|
-
if (oidcToken) {
|
|
158
|
-
|
|
159
|
-
}
|
|
160
|
-
this.oidcToken = oidcToken || cache.get(cacheKey) || "";
|
|
161
|
-
console.log("embedded wallet login with oidcToken:", config, this.oidcToken);
|
|
162
|
-
if (!this.oidcToken) {
|
|
163
|
-
|
|
164
|
-
}
|
|
157
|
+
// const cacheKey = config.clientId + "-cube-oidc-token";
|
|
158
|
+
// if (oidcToken) {
|
|
159
|
+
// cache.set(cacheKey, oidcToken, false);
|
|
160
|
+
// }
|
|
161
|
+
// this.oidcToken = oidcToken || cache.get(cacheKey) || "";
|
|
162
|
+
// console.log("embedded wallet login with oidcToken:", config, this.oidcToken);
|
|
163
|
+
// if (!this.oidcToken) {
|
|
164
|
+
// console.warn("oidcToken is not found");
|
|
165
|
+
// }
|
|
165
166
|
|
|
166
167
|
return new Promise((resolve, reject) => {
|
|
167
168
|
const receiveResponse = (event: MessageEvent) => {
|
|
@@ -217,7 +218,7 @@ export class EmbeddedWallet {
|
|
|
217
218
|
dappOrigin: window.location.origin,
|
|
218
219
|
tomoStage: stage,
|
|
219
220
|
tomoClientId: clientId,
|
|
220
|
-
oidcToken: this.oidcToken
|
|
221
|
+
oidcToken: this.oidcToken,
|
|
221
222
|
logo: logo || "",
|
|
222
223
|
name: name || "",
|
|
223
224
|
});
|
|
@@ -252,7 +253,8 @@ export class EmbeddedWallet {
|
|
|
252
253
|
if (!this.walletIframe) {
|
|
253
254
|
return;
|
|
254
255
|
}
|
|
255
|
-
const baseCssText =
|
|
256
|
+
const baseCssText =
|
|
257
|
+
"position: fixed; top: 0; left: 0; border: none; width: 100%; height: 100%; background: transparent; background-color: transparent;";
|
|
256
258
|
const maskZIndex = this.maskZIndex;
|
|
257
259
|
this.walletIframe.style.cssText = `${baseCssText} z-index: ${maskZIndex};`;
|
|
258
260
|
this.walletIframe.setAttribute("allowTransparency", "true");
|
|
@@ -273,6 +275,8 @@ export class EmbeddedWallet {
|
|
|
273
275
|
public async logout(): Promise<boolean> {
|
|
274
276
|
if (this.walletIframe) {
|
|
275
277
|
this.walletIframe.src = `${this.walletOrigin}#logout=true`;
|
|
278
|
+
const config = this.config;
|
|
279
|
+
config && this.init(config);
|
|
276
280
|
}
|
|
277
281
|
window.removeEventListener("message", this.walletCloseHandler);
|
|
278
282
|
window.removeEventListener("message", this.logoutListener);
|
package/src/hub.ts
CHANGED
package/vitest.config.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineConfig } from "vitest/config";
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
test: {
|
|
5
|
+
globals: true,
|
|
6
|
+
environment: "jsdom",
|
|
7
|
+
setupFiles: ["./src/__tests__/setup.ts"],
|
|
8
|
+
coverage: {
|
|
9
|
+
provider: "v8",
|
|
10
|
+
reporter: ["text", "json", "html"],
|
|
11
|
+
exclude: ["node_modules/", "dist/", "**/*.config.*", "**/__tests__/**", "**/*.test.ts", "**/*.spec.ts"],
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
});
|