@tomo-inc/inject-providers 0.0.16 → 0.0.18

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.
@@ -0,0 +1,288 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { DogecoinProvider } from "../dogecoin/dogecoin";
3
+ import { IProductInfo, IConnectors } from "../types";
4
+ import * as utils from "../utils/index";
5
+
6
+ describe("DogecoinProvider", () => {
7
+ let provider: DogecoinProvider;
8
+ let mockSendRequest: ReturnType<typeof vi.fn>;
9
+ let mockOnResponse: ReturnType<typeof vi.fn>;
10
+ let productInfo: IProductInfo;
11
+ let connectors: IConnectors;
12
+
13
+ beforeEach(() => {
14
+ mockSendRequest = vi.fn();
15
+ mockOnResponse = vi.fn().mockResolvedValue({ data: null });
16
+
17
+ productInfo = {
18
+ name: "Test Wallet",
19
+ rdns: "com.test.wallet",
20
+ icon: "test-icon.png",
21
+ };
22
+
23
+ connectors = {
24
+ sendRequest: mockSendRequest,
25
+ onResponse: mockOnResponse,
26
+ };
27
+
28
+ // Mock utils
29
+ vi.spyOn(utils, "getDappInfo").mockResolvedValue({
30
+ origin: "https://test.com",
31
+ title: "Test",
32
+ desc: "Test desc",
33
+ favicon: "",
34
+ });
35
+ vi.spyOn(utils, "domReadyCall").mockImplementation((callback) => {
36
+ callback();
37
+ });
38
+
39
+ vi.spyOn(document, "addEventListener").mockImplementation(() => {});
40
+ vi.useFakeTimers();
41
+ });
42
+
43
+ afterEach(() => {
44
+ vi.restoreAllMocks();
45
+ vi.useRealTimers();
46
+ });
47
+
48
+ describe("constructor", () => {
49
+ it("should create DogecoinProvider instance", () => {
50
+ provider = new DogecoinProvider(productInfo, connectors);
51
+ expect(provider).toBeInstanceOf(DogecoinProvider);
52
+ expect(provider.name).toBe("Test Wallet");
53
+ expect(provider.rdns).toBe("com.test.wallet");
54
+ });
55
+
56
+ it("should call _request on visibilitychange when visible", async () => {
57
+ let visibilityHandler: (() => void) | null = null;
58
+ vi.mocked(document.addEventListener).mockImplementation((event: string, handler: any) => {
59
+ if (event === "visibilitychange") visibilityHandler = handler;
60
+ });
61
+ provider = new DogecoinProvider(productInfo, connectors);
62
+ mockOnResponse.mockResolvedValue({});
63
+ Object.defineProperty(document, "visibilityState", {
64
+ value: "visible",
65
+ writable: true,
66
+ configurable: true,
67
+ });
68
+ if (visibilityHandler) await visibilityHandler();
69
+ expect(mockSendRequest).toHaveBeenCalledWith(
70
+ "dogecoin",
71
+ expect.objectContaining({ method: "wallet_sendDomainMetadata" }),
72
+ );
73
+ });
74
+ });
75
+
76
+ describe("request", () => {
77
+ beforeEach(() => {
78
+ provider = new DogecoinProvider(productInfo, connectors);
79
+ });
80
+
81
+ it("should throw error if method is not provided", async () => {
82
+ await expect(provider.request({ method: "", params: {} })).rejects.toThrow();
83
+ });
84
+
85
+ it("should return response data", async () => {
86
+ const expectedData = ["0x123"];
87
+ mockOnResponse.mockImplementation(() => Promise.resolve({ data: expectedData }));
88
+ const result = await provider.request({ method: "getAccounts", params: {} });
89
+
90
+ expect(result).toEqual(expectedData);
91
+ });
92
+ });
93
+
94
+ describe("_request with adapter", () => {
95
+ beforeEach(() => {
96
+ provider = new DogecoinProvider(productInfo, connectors);
97
+ });
98
+
99
+ it("should use adapter when provided", async () => {
100
+ mockOnResponse.mockResolvedValue({ data: { balance: 100 } });
101
+ const adapter = vi.fn((d: any) => ({ adapted: d }));
102
+ const result = await (provider as any)._request({ method: "getBalance" }, adapter);
103
+ expect(adapter).toHaveBeenCalledWith({ balance: 100 });
104
+ expect(result).toEqual({ adapted: { balance: 100 } });
105
+ });
106
+
107
+ it("should return data when response is null (res || {} branch)", async () => {
108
+ mockOnResponse.mockResolvedValue(null);
109
+ const result = await (provider as any)._request({ method: "getBalance" });
110
+ expect(result).toBeUndefined();
111
+ });
112
+ });
113
+
114
+ describe("connect", () => {
115
+ beforeEach(() => {
116
+ provider = new DogecoinProvider(productInfo, connectors);
117
+ });
118
+
119
+ it("should call request with connect method", async () => {
120
+ mockOnResponse.mockImplementation(() => Promise.resolve({ data: { address: "0x123" } }));
121
+ await provider.connect();
122
+
123
+ expect(mockSendRequest).toHaveBeenCalled();
124
+ });
125
+ });
126
+
127
+ describe("disconnect", () => {
128
+ beforeEach(() => {
129
+ provider = new DogecoinProvider(productInfo, connectors);
130
+ });
131
+
132
+ it("should call request with disconnect method", async () => {
133
+ mockOnResponse.mockImplementation(() => Promise.resolve({ data: { disconnected: true } }));
134
+ await provider.disconnect();
135
+
136
+ expect(mockSendRequest).toHaveBeenCalled();
137
+ });
138
+ });
139
+
140
+ describe("requestAccounts", () => {
141
+ beforeEach(() => {
142
+ provider = new DogecoinProvider(productInfo, connectors);
143
+ });
144
+
145
+ it("should return accounts array", async () => {
146
+ const accounts = ["0x123"];
147
+ mockOnResponse.mockImplementation(() => Promise.resolve({ data: accounts }));
148
+ const result = await provider.requestAccounts();
149
+
150
+ expect(result).toEqual(accounts);
151
+ });
152
+ });
153
+
154
+ describe("signMessage", () => {
155
+ beforeEach(() => {
156
+ provider = new DogecoinProvider(productInfo, connectors);
157
+ });
158
+
159
+ it("should sign message and return signed message", async () => {
160
+ const signedMessage = "signed-message";
161
+ mockOnResponse.mockImplementation(() => Promise.resolve({ data: { signedMessage } }));
162
+ const result = await provider.signMessage("test message");
163
+
164
+ expect(result).toBe(signedMessage);
165
+ });
166
+ });
167
+
168
+ describe("getBalance", () => {
169
+ beforeEach(() => {
170
+ provider = new DogecoinProvider(productInfo, connectors);
171
+ });
172
+
173
+ it("should return balance object", async () => {
174
+ const balance = { confirmed: 1000, unconfirmed: 100, total: 1100 };
175
+ mockOnResponse.mockImplementation(() => Promise.resolve({ data: balance }));
176
+ const result = await provider.getBalance();
177
+
178
+ expect(result).toEqual(balance);
179
+ });
180
+ });
181
+
182
+ describe("sendDogecoin", () => {
183
+ beforeEach(() => {
184
+ provider = new DogecoinProvider(productInfo, connectors);
185
+ });
186
+
187
+ it("should send dogecoin with amount", async () => {
188
+ const txId = "0xtxid";
189
+ mockOnResponse.mockImplementation(() => Promise.resolve({ data: txId }));
190
+ const result = await provider.sendDogecoin("0xrecipient", 1000);
191
+
192
+ expect(result).toBe(txId);
193
+ });
194
+
195
+ it("should send dogecoin with options", async () => {
196
+ const txId = "0xtxid";
197
+ mockOnResponse.mockImplementation(() => Promise.resolve({ data: txId }));
198
+ const result = await provider.sendDogecoin("0xrecipient", 1000, { feeRate: 10 });
199
+
200
+ expect(result).toBe(txId);
201
+ });
202
+ });
203
+
204
+ describe("additional API methods", () => {
205
+ beforeEach(() => {
206
+ provider = new DogecoinProvider(productInfo, connectors);
207
+ mockOnResponse.mockImplementation(({ method }: { method: string }) =>
208
+ Promise.resolve({ data: `res-${method}` }),
209
+ );
210
+ });
211
+
212
+ it("getPublicKey", async () => {
213
+ const r = await provider.getPublicKey();
214
+ expect(r).toBe("res-getPublicKey");
215
+ });
216
+ it("getTransactionStatus", async () => {
217
+ const r = await provider.getTransactionStatus({ txId: "tx1" });
218
+ expect(r).toBe("res-getTransactionStatus");
219
+ });
220
+ it("requestSignedMessage with type", async () => {
221
+ const r = await provider.requestSignedMessage({ message: "hi", type: "ecdsa" });
222
+ expect(r).toBe("res-requestSignedMessage");
223
+ });
224
+ it("requestDecryptedMessage", async () => {
225
+ const r = await provider.requestDecryptedMessage({ message: "enc" });
226
+ expect(r).toBe("res-requestDecryptedMessage");
227
+ });
228
+ it("requestPsbt", async () => {
229
+ const r = await provider.requestPsbt({ rawTx: "hex" });
230
+ expect(r).toBe("res-requestPsbt");
231
+ });
232
+ it("signPsbt", async () => {
233
+ const r = await provider.signPsbt("hex", { autoFinalized: true });
234
+ expect(r).toBe("res-signPsbt");
235
+ });
236
+ it("signPsbts", async () => {
237
+ const r = await provider.signPsbts(["a", "b"], {});
238
+ expect(r).toBe("res-signPsbts");
239
+ });
240
+ it("requestTransaction", async () => {
241
+ const r = await provider.requestTransaction({ recipientAddress: "addr", dogeAmount: 100 });
242
+ expect(r).toBe("res-requestTransaction");
243
+ });
244
+ it("send", async () => {
245
+ const r = await provider.send("addr", 1000, { feeRate: 5 });
246
+ expect(r).toBe("res-send");
247
+ });
248
+ it("getConnectionStatus", async () => {
249
+ const r = await provider.getConnectionStatus();
250
+ expect(r).toBe("res-getConnectionStatus");
251
+ });
252
+ it("getDRC20Balances", async () => {
253
+ const r = await provider.getDRC20Balances("addr", "ticker");
254
+ expect(r).toBe("res-getDRC20Balances");
255
+ });
256
+ it("getDRC20Balance", async () => {
257
+ const r = await provider.getDRC20Balance({ ticker: "t" });
258
+ expect(r).toBe("res-getDRC20Balance");
259
+ });
260
+ it("getTransferableDRC20", async () => {
261
+ const r = await provider.getTransferableDRC20({ ticker: "t" });
262
+ expect(r).toBe("res-getTransferableDRC20");
263
+ });
264
+ it("requestAvailableDRC20Transaction", async () => {
265
+ const r = await provider.requestAvailableDRC20Transaction({ ticker: "t", amount: 1 });
266
+ expect(r).toBe("res-requestAvailableDRC20Transaction");
267
+ });
268
+ it("requestInscriptionTransaction", async () => {
269
+ const r = await provider.requestInscriptionTransaction({
270
+ location: "loc",
271
+ recipientAddress: "addr",
272
+ });
273
+ expect(r).toBe("res-requestInscriptionTransaction");
274
+ });
275
+ it("getDunesBalance", async () => {
276
+ const r = await provider.getDunesBalance({ ticker: "t" });
277
+ expect(r).toBe("res-getDunesBalance");
278
+ });
279
+ it("requestDunesTransaction", async () => {
280
+ const r = await provider.requestDunesTransaction({
281
+ ticker: "t",
282
+ amount: 1,
283
+ recipientAddress: "addr",
284
+ });
285
+ expect(r).toBe("res-requestDunesTransaction");
286
+ });
287
+ });
288
+ });
@@ -0,0 +1,146 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { domReadyCall, checkLoaded } from "../utils/dom";
3
+
4
+ describe("dom", () => {
5
+ let originalReadyState: DocumentReadyState;
6
+ let originalAddEventListener: typeof document.addEventListener;
7
+ let originalRemoveEventListener: typeof document.removeEventListener;
8
+
9
+ beforeEach(() => {
10
+ originalReadyState = document.readyState;
11
+ originalAddEventListener = document.addEventListener;
12
+ originalRemoveEventListener = document.removeEventListener;
13
+ });
14
+
15
+ afterEach(() => {
16
+ Object.defineProperty(document, "readyState", {
17
+ value: originalReadyState,
18
+ writable: true,
19
+ configurable: true,
20
+ });
21
+ document.addEventListener = originalAddEventListener;
22
+ document.removeEventListener = originalRemoveEventListener;
23
+ });
24
+
25
+ describe("domReadyCall", () => {
26
+ it("should call callback immediately if document is already complete", () => {
27
+ Object.defineProperty(document, "readyState", {
28
+ value: "complete",
29
+ writable: true,
30
+ configurable: true,
31
+ });
32
+
33
+ const callback = vi.fn();
34
+ domReadyCall(callback);
35
+
36
+ expect(callback).toHaveBeenCalledTimes(1);
37
+ });
38
+
39
+ it("should add event listener if document is not complete", () => {
40
+ Object.defineProperty(document, "readyState", {
41
+ value: "loading",
42
+ writable: true,
43
+ configurable: true,
44
+ });
45
+
46
+ const callback = vi.fn();
47
+ const addEventListenerSpy = vi.fn();
48
+ document.addEventListener = addEventListenerSpy;
49
+
50
+ domReadyCall(callback);
51
+
52
+ expect(addEventListenerSpy).toHaveBeenCalledWith("DOMContentLoaded", expect.any(Function));
53
+ expect(callback).not.toHaveBeenCalled();
54
+ });
55
+
56
+ it("should call callback and remove listener when DOMContentLoaded fires", () => {
57
+ Object.defineProperty(document, "readyState", {
58
+ value: "loading",
59
+ writable: true,
60
+ configurable: true,
61
+ });
62
+
63
+ const callback = vi.fn();
64
+ let eventHandler: ((e: Event) => void) | null = null;
65
+ const removeEventListenerSpy = vi.fn();
66
+
67
+ document.addEventListener = vi.fn((event: string, handler: EventListener) => {
68
+ if (event === "DOMContentLoaded") {
69
+ eventHandler = handler as (e: Event) => void;
70
+ }
71
+ });
72
+ document.removeEventListener = removeEventListenerSpy;
73
+
74
+ domReadyCall(callback);
75
+
76
+ // Simulate DOMContentLoaded event
77
+ if (eventHandler) {
78
+ eventHandler(new Event("DOMContentLoaded"));
79
+ }
80
+
81
+ expect(callback).toHaveBeenCalledTimes(1);
82
+ expect(removeEventListenerSpy).toHaveBeenCalledWith("DOMContentLoaded", eventHandler);
83
+ });
84
+
85
+ it("checkLoaded should call callback when document.readyState is complete", () => {
86
+ Object.defineProperty(document, "readyState", {
87
+ value: "complete",
88
+ writable: true,
89
+ configurable: true,
90
+ });
91
+ const callback = vi.fn();
92
+ const result = checkLoaded(callback);
93
+ expect(callback).toHaveBeenCalledTimes(1);
94
+ expect(result).toBe(true);
95
+ });
96
+
97
+ it("checkLoaded should schedule itself when document is not complete", () => {
98
+ Object.defineProperty(document, "readyState", {
99
+ value: "loading",
100
+ writable: true,
101
+ configurable: true,
102
+ });
103
+ const callback = vi.fn();
104
+ const setTimeoutSpy = vi.spyOn(global, "setTimeout");
105
+ checkLoaded(callback);
106
+ expect(callback).not.toHaveBeenCalled();
107
+ expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), 100);
108
+ const scheduled = setTimeoutSpy.mock.calls[0][0];
109
+ Object.defineProperty(document, "readyState", {
110
+ value: "complete",
111
+ writable: true,
112
+ configurable: true,
113
+ });
114
+ scheduled();
115
+ expect(callback).toHaveBeenCalled();
116
+ setTimeoutSpy.mockRestore();
117
+ });
118
+
119
+ it("checkLoaded should return without calling callback when tryCount exceeds 600", () => {
120
+ Object.defineProperty(document, "readyState", {
121
+ value: "loading",
122
+ writable: true,
123
+ configurable: true,
124
+ });
125
+ const callback = vi.fn();
126
+ let scheduled: (() => void) | null = null;
127
+ vi.spyOn(global, "setTimeout").mockImplementation((fn: () => void) => {
128
+ scheduled = fn;
129
+ return 0 as unknown as ReturnType<typeof setTimeout>;
130
+ });
131
+ checkLoaded(callback);
132
+ for (let i = 0; i < 599; i++) {
133
+ if (scheduled) {
134
+ scheduled();
135
+ const lastCall = (global.setTimeout as ReturnType<typeof vi.fn>).mock.calls[
136
+ (global.setTimeout as ReturnType<typeof vi.fn>).mock.calls.length - 1
137
+ ];
138
+ scheduled = lastCall?.[0] ?? null;
139
+ }
140
+ }
141
+ checkLoaded(callback);
142
+ expect(callback).not.toHaveBeenCalled();
143
+ vi.mocked(global.setTimeout).mockRestore();
144
+ });
145
+ });
146
+ });