@frak-labs/core-sdk 0.1.0 → 0.1.1-beta.4dfea079

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.
Files changed (130) hide show
  1. package/README.md +58 -0
  2. package/cdn/bundle.js +3 -8
  3. package/dist/actions.cjs +1 -1
  4. package/dist/actions.d.cts +3 -1400
  5. package/dist/actions.d.ts +3 -1400
  6. package/dist/actions.js +1 -1
  7. package/dist/bundle.cjs +1 -13
  8. package/dist/bundle.d.cts +4 -1927
  9. package/dist/bundle.d.ts +4 -1927
  10. package/dist/bundle.js +1 -13
  11. package/dist/computeLegacyProductId-CscYhyUi.d.cts +525 -0
  12. package/dist/computeLegacyProductId-WbD1gXV9.d.ts +525 -0
  13. package/dist/index.cjs +1 -13
  14. package/dist/index.d.cts +3 -1269
  15. package/dist/index.d.ts +3 -1269
  16. package/dist/index.js +1 -13
  17. package/dist/openSso-CC1-loUk.d.cts +1019 -0
  18. package/dist/openSso-tkqaDQLV.d.ts +1019 -0
  19. package/dist/setupClient-BjIbK6XJ.cjs +13 -0
  20. package/dist/setupClient-D_HId3e2.js +13 -0
  21. package/dist/siweAuthenticate-B_Z2OZmj.cjs +1 -0
  22. package/dist/siweAuthenticate-CQ4OfPuA.js +1 -0
  23. package/dist/siweAuthenticate-CR4Dpji6.d.cts +467 -0
  24. package/dist/siweAuthenticate-udoruuy9.d.ts +467 -0
  25. package/dist/trackEvent-CGIryq5h.cjs +1 -0
  26. package/dist/trackEvent-YfUh4jrx.js +1 -0
  27. package/package.json +24 -30
  28. package/src/actions/displayEmbeddedWallet.test.ts +194 -0
  29. package/src/actions/displayEmbeddedWallet.ts +20 -0
  30. package/src/actions/displayModal.test.ts +388 -0
  31. package/src/actions/displayModal.ts +120 -0
  32. package/src/actions/getMerchantInformation.test.ts +116 -0
  33. package/src/actions/getMerchantInformation.ts +9 -0
  34. package/src/actions/index.ts +29 -0
  35. package/src/actions/openSso.ts +116 -0
  36. package/src/actions/prepareSso.test.ts +223 -0
  37. package/src/actions/prepareSso.ts +48 -0
  38. package/src/actions/referral/processReferral.test.ts +248 -0
  39. package/src/actions/referral/processReferral.ts +232 -0
  40. package/src/actions/referral/referralInteraction.test.ts +147 -0
  41. package/src/actions/referral/referralInteraction.ts +52 -0
  42. package/src/actions/sendInteraction.ts +24 -0
  43. package/src/actions/trackPurchaseStatus.test.ts +287 -0
  44. package/src/actions/trackPurchaseStatus.ts +56 -0
  45. package/src/actions/watchWalletStatus.test.ts +372 -0
  46. package/src/actions/watchWalletStatus.ts +93 -0
  47. package/src/actions/wrapper/modalBuilder.test.ts +239 -0
  48. package/src/actions/wrapper/modalBuilder.ts +203 -0
  49. package/src/actions/wrapper/sendTransaction.test.ts +164 -0
  50. package/src/actions/wrapper/sendTransaction.ts +62 -0
  51. package/src/actions/wrapper/siweAuthenticate.test.ts +290 -0
  52. package/src/actions/wrapper/siweAuthenticate.ts +94 -0
  53. package/src/bundle.ts +2 -0
  54. package/src/clients/DebugInfo.test.ts +418 -0
  55. package/src/clients/DebugInfo.ts +182 -0
  56. package/src/clients/createIFrameFrakClient.ts +289 -0
  57. package/src/clients/index.ts +3 -0
  58. package/src/clients/setupClient.test.ts +343 -0
  59. package/src/clients/setupClient.ts +73 -0
  60. package/src/clients/transports/iframeLifecycleManager.test.ts +558 -0
  61. package/src/clients/transports/iframeLifecycleManager.ts +174 -0
  62. package/src/constants/interactionTypes.ts +15 -0
  63. package/src/constants/locales.ts +14 -0
  64. package/src/index.ts +110 -0
  65. package/src/types/client.ts +14 -0
  66. package/src/types/compression.ts +22 -0
  67. package/src/types/config.ts +117 -0
  68. package/src/types/context.ts +13 -0
  69. package/src/types/index.ts +75 -0
  70. package/src/types/lifecycle/client.ts +69 -0
  71. package/src/types/lifecycle/iframe.ts +41 -0
  72. package/src/types/lifecycle/index.ts +2 -0
  73. package/src/types/rpc/displayModal.ts +82 -0
  74. package/src/types/rpc/embedded/index.ts +68 -0
  75. package/src/types/rpc/embedded/loggedIn.ts +55 -0
  76. package/src/types/rpc/embedded/loggedOut.ts +28 -0
  77. package/src/types/rpc/interaction.ts +30 -0
  78. package/src/types/rpc/merchantInformation.ts +77 -0
  79. package/src/types/rpc/modal/final.ts +46 -0
  80. package/src/types/rpc/modal/generic.ts +46 -0
  81. package/src/types/rpc/modal/index.ts +16 -0
  82. package/src/types/rpc/modal/login.ts +36 -0
  83. package/src/types/rpc/modal/siweAuthenticate.ts +37 -0
  84. package/src/types/rpc/modal/transaction.ts +33 -0
  85. package/src/types/rpc/sso.ts +80 -0
  86. package/src/types/rpc/walletStatus.ts +29 -0
  87. package/src/types/rpc.ts +146 -0
  88. package/src/types/tracking.ts +60 -0
  89. package/src/types/transport.ts +34 -0
  90. package/src/utils/FrakContext.test.ts +407 -0
  91. package/src/utils/FrakContext.ts +158 -0
  92. package/src/utils/backendUrl.test.ts +83 -0
  93. package/src/utils/backendUrl.ts +62 -0
  94. package/src/utils/clientId.test.ts +41 -0
  95. package/src/utils/clientId.ts +40 -0
  96. package/src/utils/compression/b64.test.ts +181 -0
  97. package/src/utils/compression/b64.ts +29 -0
  98. package/src/utils/compression/compress.test.ts +123 -0
  99. package/src/utils/compression/compress.ts +11 -0
  100. package/src/utils/compression/decompress.test.ts +149 -0
  101. package/src/utils/compression/decompress.ts +11 -0
  102. package/src/utils/compression/index.ts +3 -0
  103. package/src/utils/computeLegacyProductId.ts +11 -0
  104. package/src/utils/constants.test.ts +23 -0
  105. package/src/utils/constants.ts +14 -0
  106. package/src/utils/deepLinkWithFallback.test.ts +243 -0
  107. package/src/utils/deepLinkWithFallback.ts +97 -0
  108. package/src/utils/formatAmount.test.ts +113 -0
  109. package/src/utils/formatAmount.ts +18 -0
  110. package/src/utils/getCurrencyAmountKey.test.ts +44 -0
  111. package/src/utils/getCurrencyAmountKey.ts +15 -0
  112. package/src/utils/getSupportedCurrency.test.ts +51 -0
  113. package/src/utils/getSupportedCurrency.ts +14 -0
  114. package/src/utils/getSupportedLocale.test.ts +64 -0
  115. package/src/utils/getSupportedLocale.ts +16 -0
  116. package/src/utils/iframeHelper.test.ts +450 -0
  117. package/src/utils/iframeHelper.ts +147 -0
  118. package/src/utils/index.ts +36 -0
  119. package/src/utils/merchantId.test.ts +564 -0
  120. package/src/utils/merchantId.ts +122 -0
  121. package/src/utils/sso.ts +126 -0
  122. package/src/utils/ssoUrlListener.test.ts +252 -0
  123. package/src/utils/ssoUrlListener.ts +60 -0
  124. package/src/utils/trackEvent.test.ts +180 -0
  125. package/src/utils/trackEvent.ts +31 -0
  126. package/cdn/bundle.js.LICENSE.txt +0 -10
  127. package/dist/interactions.cjs +0 -1
  128. package/dist/interactions.d.cts +0 -182
  129. package/dist/interactions.d.ts +0 -182
  130. package/dist/interactions.js +0 -1
@@ -0,0 +1,372 @@
1
+ /**
2
+ * Tests for watchWalletStatus action
3
+ * Tests wallet status watching and side effects
4
+ */
5
+
6
+ import { vi } from "vitest";
7
+
8
+ // Mock Deferred before imports
9
+ vi.mock("@frak-labs/frame-connector", () => ({
10
+ Deferred: class {
11
+ promise: Promise<any>;
12
+ resolve: (value: any) => void;
13
+
14
+ constructor() {
15
+ let resolveFunc: (value: any) => void;
16
+ this.promise = new Promise((resolve) => {
17
+ resolveFunc = resolve;
18
+ });
19
+ this.resolve = resolveFunc!;
20
+ }
21
+ },
22
+ }));
23
+
24
+ import type { Address } from "viem";
25
+ import {
26
+ afterEach,
27
+ beforeEach,
28
+ describe,
29
+ expect,
30
+ it,
31
+ } from "../../tests/vitest-fixtures";
32
+ import type { FrakClient, WalletStatusReturnType } from "../types";
33
+ import { watchWalletStatus } from "./watchWalletStatus";
34
+
35
+ describe("watchWalletStatus", () => {
36
+ let mockSessionStorage: {
37
+ getItem: ReturnType<typeof vi.fn>;
38
+ setItem: ReturnType<typeof vi.fn>;
39
+ removeItem: ReturnType<typeof vi.fn>;
40
+ };
41
+
42
+ beforeEach(() => {
43
+ // Mock sessionStorage
44
+ mockSessionStorage = {
45
+ getItem: vi.fn(),
46
+ setItem: vi.fn(),
47
+ removeItem: vi.fn(),
48
+ };
49
+ Object.defineProperty(window, "sessionStorage", {
50
+ value: mockSessionStorage,
51
+ writable: true,
52
+ configurable: true,
53
+ });
54
+ });
55
+
56
+ afterEach(() => {
57
+ vi.clearAllMocks();
58
+ });
59
+
60
+ describe("without callback", () => {
61
+ it("should make one-shot request", async () => {
62
+ const mockStatus: WalletStatusReturnType = {
63
+ key: "connected",
64
+ wallet: "0x1234567890123456789012345678901234567890" as Address,
65
+ interactionToken: "token-123",
66
+ };
67
+
68
+ const mockClient = {
69
+ request: vi.fn().mockResolvedValue(mockStatus),
70
+ openPanel: {
71
+ setGlobalProperties: vi.fn(),
72
+ },
73
+ } as unknown as FrakClient;
74
+
75
+ const result = await watchWalletStatus(mockClient);
76
+
77
+ expect(mockClient.request).toHaveBeenCalledWith({
78
+ method: "frak_listenToWalletStatus",
79
+ });
80
+ expect(result).toEqual(mockStatus);
81
+ });
82
+
83
+ it("should save interaction token to sessionStorage", async () => {
84
+ const mockStatus: WalletStatusReturnType = {
85
+ key: "connected",
86
+ wallet: "0x1234567890123456789012345678901234567890" as Address,
87
+ interactionToken: "token-abc",
88
+ };
89
+
90
+ const mockClient = {
91
+ request: vi.fn().mockResolvedValue(mockStatus),
92
+ openPanel: {
93
+ setGlobalProperties: vi.fn(),
94
+ },
95
+ } as unknown as FrakClient;
96
+
97
+ await watchWalletStatus(mockClient);
98
+
99
+ expect(mockSessionStorage.setItem).toHaveBeenCalledWith(
100
+ "frak-wallet-interaction-token",
101
+ "token-abc"
102
+ );
103
+ });
104
+
105
+ it("should remove interaction token when not present", async () => {
106
+ const mockStatus: WalletStatusReturnType = {
107
+ key: "not-connected",
108
+ };
109
+
110
+ const mockClient = {
111
+ request: vi.fn().mockResolvedValue(mockStatus),
112
+ openPanel: {
113
+ setGlobalProperties: vi.fn(),
114
+ },
115
+ } as unknown as FrakClient;
116
+
117
+ await watchWalletStatus(mockClient);
118
+
119
+ expect(mockSessionStorage.removeItem).toHaveBeenCalledWith(
120
+ "frak-wallet-interaction-token"
121
+ );
122
+ });
123
+
124
+ it("should update OpenPanel global properties", async () => {
125
+ const mockStatus: WalletStatusReturnType = {
126
+ key: "connected",
127
+ wallet: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd" as Address,
128
+ };
129
+
130
+ const mockClient = {
131
+ request: vi.fn().mockResolvedValue(mockStatus),
132
+ openPanel: {
133
+ setGlobalProperties: vi.fn(),
134
+ },
135
+ } as unknown as FrakClient;
136
+
137
+ await watchWalletStatus(mockClient);
138
+
139
+ expect(
140
+ mockClient.openPanel?.setGlobalProperties
141
+ ).toHaveBeenCalledWith({
142
+ wallet: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
143
+ });
144
+ });
145
+
146
+ it("should set wallet to null when not connected", async () => {
147
+ const mockStatus: WalletStatusReturnType = {
148
+ key: "not-connected",
149
+ };
150
+
151
+ const mockClient = {
152
+ request: vi.fn().mockResolvedValue(mockStatus),
153
+ openPanel: {
154
+ setGlobalProperties: vi.fn(),
155
+ },
156
+ } as unknown as FrakClient;
157
+
158
+ await watchWalletStatus(mockClient);
159
+
160
+ expect(
161
+ mockClient.openPanel?.setGlobalProperties
162
+ ).toHaveBeenCalledWith({
163
+ wallet: null,
164
+ });
165
+ });
166
+
167
+ it("should work without openPanel", async () => {
168
+ const mockStatus: WalletStatusReturnType = {
169
+ key: "connected",
170
+ wallet: "0x1234567890123456789012345678901234567890" as Address,
171
+ };
172
+
173
+ const mockClient = {
174
+ request: vi.fn().mockResolvedValue(mockStatus),
175
+ } as unknown as FrakClient;
176
+
177
+ const result = await watchWalletStatus(mockClient);
178
+
179
+ expect(result).toEqual(mockStatus);
180
+ });
181
+ });
182
+
183
+ describe("with callback", () => {
184
+ it("should setup listener request", async () => {
185
+ const mockStatus: WalletStatusReturnType = {
186
+ key: "connected",
187
+ wallet: "0x1234567890123456789012345678901234567890" as Address,
188
+ };
189
+
190
+ const mockCallback = vi.fn();
191
+ const mockClient = {
192
+ listenerRequest: vi.fn((_params, callback) => {
193
+ // Simulate status update
194
+ setTimeout(() => callback(mockStatus), 0);
195
+ }),
196
+ openPanel: {
197
+ setGlobalProperties: vi.fn(),
198
+ },
199
+ } as unknown as FrakClient;
200
+
201
+ const resultPromise = watchWalletStatus(mockClient, mockCallback);
202
+
203
+ expect(mockClient.listenerRequest).toHaveBeenCalled();
204
+
205
+ await resultPromise;
206
+ });
207
+
208
+ it("should call callback with status updates", async () => {
209
+ const mockStatus: WalletStatusReturnType = {
210
+ key: "connected",
211
+ wallet: "0x1234567890123456789012345678901234567890" as Address,
212
+ interactionToken: "token-123",
213
+ };
214
+
215
+ const mockCallback = vi.fn();
216
+ const mockClient = {
217
+ listenerRequest: vi.fn((_params, callback) => {
218
+ setTimeout(() => callback(mockStatus), 0);
219
+ }),
220
+ openPanel: {
221
+ setGlobalProperties: vi.fn(),
222
+ },
223
+ } as unknown as FrakClient;
224
+
225
+ await watchWalletStatus(mockClient, mockCallback);
226
+
227
+ expect(mockCallback).toHaveBeenCalledWith(mockStatus);
228
+ });
229
+
230
+ it("should resolve promise with first status", async () => {
231
+ const firstStatus: WalletStatusReturnType = {
232
+ key: "connected",
233
+ wallet: "0x1234567890123456789012345678901234567890" as Address,
234
+ };
235
+
236
+ const secondStatus: WalletStatusReturnType = {
237
+ key: "connected",
238
+ wallet: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd" as Address,
239
+ };
240
+
241
+ const mockCallback = vi.fn();
242
+ const mockClient = {
243
+ listenerRequest: vi.fn((_params, callback) => {
244
+ setTimeout(() => {
245
+ callback(firstStatus);
246
+ callback(secondStatus);
247
+ }, 0);
248
+ }),
249
+ openPanel: {
250
+ setGlobalProperties: vi.fn(),
251
+ },
252
+ } as unknown as FrakClient;
253
+
254
+ const result = await watchWalletStatus(mockClient, mockCallback);
255
+
256
+ expect(result).toEqual(firstStatus);
257
+ expect(mockCallback).toHaveBeenCalledTimes(2);
258
+ });
259
+
260
+ it("should handle side effects for each status update", async () => {
261
+ const firstStatus: WalletStatusReturnType = {
262
+ key: "connected",
263
+ wallet: "0x1234567890123456789012345678901234567890" as Address,
264
+ interactionToken: "token-1",
265
+ };
266
+
267
+ const secondStatus: WalletStatusReturnType = {
268
+ key: "connected",
269
+ wallet: "0x1234567890123456789012345678901234567890" as Address,
270
+ interactionToken: "token-2",
271
+ };
272
+
273
+ const mockCallback = vi.fn();
274
+ const mockClient = {
275
+ listenerRequest: vi.fn((_params, callback) => {
276
+ setTimeout(() => {
277
+ callback(firstStatus);
278
+ callback(secondStatus);
279
+ }, 0);
280
+ }),
281
+ openPanel: {
282
+ setGlobalProperties: vi.fn(),
283
+ },
284
+ } as unknown as FrakClient;
285
+
286
+ await watchWalletStatus(mockClient, mockCallback);
287
+
288
+ // Both tokens should be saved
289
+ expect(mockSessionStorage.setItem).toHaveBeenCalledWith(
290
+ "frak-wallet-interaction-token",
291
+ "token-1"
292
+ );
293
+ expect(mockSessionStorage.setItem).toHaveBeenCalledWith(
294
+ "frak-wallet-interaction-token",
295
+ "token-2"
296
+ );
297
+ });
298
+
299
+ it("should save and remove interaction token based on status", async () => {
300
+ const connectedStatus: WalletStatusReturnType = {
301
+ key: "connected",
302
+ wallet: "0x1234567890123456789012345678901234567890" as Address,
303
+ interactionToken: "token-123",
304
+ };
305
+
306
+ const disconnectedStatus: WalletStatusReturnType = {
307
+ key: "not-connected",
308
+ };
309
+
310
+ const mockCallback = vi.fn();
311
+ const mockClient = {
312
+ listenerRequest: vi.fn((_params, callback) => {
313
+ setTimeout(() => {
314
+ callback(connectedStatus);
315
+ callback(disconnectedStatus);
316
+ }, 0);
317
+ }),
318
+ openPanel: {
319
+ setGlobalProperties: vi.fn(),
320
+ },
321
+ } as unknown as FrakClient;
322
+
323
+ await watchWalletStatus(mockClient, mockCallback);
324
+
325
+ expect(mockSessionStorage.setItem).toHaveBeenCalledWith(
326
+ "frak-wallet-interaction-token",
327
+ "token-123"
328
+ );
329
+ expect(mockSessionStorage.removeItem).toHaveBeenCalledWith(
330
+ "frak-wallet-interaction-token"
331
+ );
332
+ });
333
+
334
+ it("should update OpenPanel properties with each status", async () => {
335
+ const firstStatus: WalletStatusReturnType = {
336
+ key: "connected",
337
+ wallet: "0x1111111111111111111111111111111111111111" as Address,
338
+ };
339
+
340
+ const secondStatus: WalletStatusReturnType = {
341
+ key: "connected",
342
+ wallet: "0x2222222222222222222222222222222222222222" as Address,
343
+ };
344
+
345
+ const mockCallback = vi.fn();
346
+ const mockClient = {
347
+ listenerRequest: vi.fn((_params, callback) => {
348
+ setTimeout(() => {
349
+ callback(firstStatus);
350
+ callback(secondStatus);
351
+ }, 0);
352
+ }),
353
+ openPanel: {
354
+ setGlobalProperties: vi.fn(),
355
+ },
356
+ } as unknown as FrakClient;
357
+
358
+ await watchWalletStatus(mockClient, mockCallback);
359
+
360
+ expect(
361
+ mockClient.openPanel?.setGlobalProperties
362
+ ).toHaveBeenCalledWith({
363
+ wallet: "0x1111111111111111111111111111111111111111",
364
+ });
365
+ expect(
366
+ mockClient.openPanel?.setGlobalProperties
367
+ ).toHaveBeenCalledWith({
368
+ wallet: "0x2222222222222222222222222222222222222222",
369
+ });
370
+ });
371
+ });
372
+ });
@@ -0,0 +1,93 @@
1
+ import { Deferred } from "@frak-labs/frame-connector";
2
+ import type { FrakClient } from "../types/client";
3
+ import type { WalletStatusReturnType } from "../types/rpc/walletStatus";
4
+
5
+ /**
6
+ * Function used to watch the current frak wallet status
7
+ * @param client - The current Frak Client
8
+ * @param callback - The callback that will receive any wallet status change
9
+ * @returns A promise resolving with the initial wallet status
10
+ *
11
+ * @description This function will return the current wallet status, and will listen to any change in the wallet status.
12
+ *
13
+ * @example
14
+ * await watchWalletStatus(frakConfig, (status: WalletStatusReturnType) => {
15
+ * if (status.key === "connected") {
16
+ * console.log("Wallet connected:", status.wallet);
17
+ * } else {
18
+ * console.log("Wallet not connected");
19
+ * }
20
+ * });
21
+ */
22
+ export function watchWalletStatus(
23
+ client: FrakClient,
24
+ callback?: (status: WalletStatusReturnType) => void
25
+ ): Promise<WalletStatusReturnType> {
26
+ // If no callback is provided, just do a request with deferred result
27
+ if (!callback) {
28
+ return client
29
+ .request({ method: "frak_listenToWalletStatus" })
30
+ .then((result) => {
31
+ // Handle side effects of this request
32
+ walletStatusSideEffect(client, result);
33
+
34
+ // Return the result
35
+ return result;
36
+ });
37
+ }
38
+
39
+ // Otherwise, listen to the wallet status and return the first one received
40
+ const firstResult = new Deferred<WalletStatusReturnType>();
41
+ let hasResolved = false;
42
+
43
+ // Start the listening request, and return the first result
44
+ client.listenerRequest(
45
+ {
46
+ method: "frak_listenToWalletStatus",
47
+ },
48
+ (status) => {
49
+ // Handle side effects of this request
50
+ walletStatusSideEffect(client, status);
51
+
52
+ // Transmit the status to the callback
53
+ callback(status);
54
+
55
+ // If the promise hasn't resolved yet, resolve it
56
+ if (!hasResolved) {
57
+ firstResult.resolve(status);
58
+ hasResolved = true;
59
+ }
60
+ }
61
+ );
62
+
63
+ return firstResult.promise;
64
+ }
65
+
66
+ /**
67
+ * Helper to save a potential interaction token
68
+ * @param interactionToken
69
+ */
70
+ function walletStatusSideEffect(
71
+ client: FrakClient,
72
+ status: WalletStatusReturnType
73
+ ) {
74
+ if (typeof window === "undefined") {
75
+ return;
76
+ }
77
+
78
+ // Update the global properties
79
+ client.openPanel?.setGlobalProperties({
80
+ wallet: status.wallet ?? null,
81
+ });
82
+
83
+ if (status.interactionToken) {
84
+ // If we got an interaction token, save it
85
+ window.sessionStorage.setItem(
86
+ "frak-wallet-interaction-token",
87
+ status.interactionToken
88
+ );
89
+ } else {
90
+ // Otherwise, remove it
91
+ window.sessionStorage.removeItem("frak-wallet-interaction-token");
92
+ }
93
+ }
@@ -0,0 +1,239 @@
1
+ import { vi } from "vitest";
2
+
3
+ vi.mock("../displayModal", () => ({
4
+ displayModal: vi.fn(),
5
+ }));
6
+
7
+ import type { Address } from "viem";
8
+ import { describe, expect, it } from "../../../tests/vitest-fixtures";
9
+ import type { FrakClient } from "../../types";
10
+ import { modalBuilder } from "./modalBuilder";
11
+
12
+ describe("modalBuilder", () => {
13
+ const mockClient = {
14
+ config: {
15
+ metadata: {
16
+ name: "Test App",
17
+ },
18
+ },
19
+ request: vi.fn(),
20
+ } as unknown as FrakClient;
21
+
22
+ describe("initialization", () => {
23
+ it("should create builder with base params", () => {
24
+ const builder = modalBuilder(mockClient, {});
25
+
26
+ expect(builder.params).toBeDefined();
27
+ expect(builder.params.steps.login).toEqual({});
28
+ });
29
+
30
+ it("should create builder with custom login params", () => {
31
+ const builder = modalBuilder(mockClient, {
32
+ login: { allowSso: true },
33
+ });
34
+
35
+ expect(builder.params.steps.login).toEqual({ allowSso: true });
36
+ });
37
+
38
+ it("should create builder with metadata", () => {
39
+ const builder = modalBuilder(mockClient, {
40
+ metadata: {
41
+ header: { title: "Test Title" },
42
+ },
43
+ });
44
+
45
+ expect(builder.params.metadata).toEqual({
46
+ header: { title: "Test Title" },
47
+ });
48
+ });
49
+ });
50
+
51
+ describe("sendTx step", () => {
52
+ it("should add sendTransaction step", () => {
53
+ const builder = modalBuilder(mockClient, {});
54
+ const withTx = builder.sendTx({
55
+ tx: {
56
+ to: "0x1234567890123456789012345678901234567890" as Address,
57
+ data: "0xdata" as `0x${string}`,
58
+ },
59
+ });
60
+
61
+ expect(withTx.params.steps.sendTransaction).toEqual({
62
+ tx: {
63
+ to: "0x1234567890123456789012345678901234567890",
64
+ data: "0xdata",
65
+ },
66
+ });
67
+ });
68
+
69
+ it("should preserve previous steps when adding sendTx", () => {
70
+ const builder = modalBuilder(mockClient, {
71
+ login: { allowSso: true },
72
+ });
73
+ const withTx = builder.sendTx({
74
+ tx: {
75
+ to: "0x1234567890123456789012345678901234567890" as Address,
76
+ },
77
+ });
78
+
79
+ expect(withTx.params.steps.login).toEqual({ allowSso: true });
80
+ expect(withTx.params.steps.sendTransaction).toBeDefined();
81
+ });
82
+ });
83
+
84
+ describe("reward step", () => {
85
+ it("should add reward final step", () => {
86
+ const builder = modalBuilder(mockClient, {});
87
+ const withReward = builder.reward();
88
+
89
+ expect(withReward.params.steps.final).toEqual({
90
+ action: { key: "reward" },
91
+ });
92
+ });
93
+
94
+ it("should add reward step with options", () => {
95
+ const builder = modalBuilder(mockClient, {});
96
+ const withReward = builder.reward({
97
+ autoSkip: true,
98
+ });
99
+
100
+ expect(withReward.params.steps.final).toEqual({
101
+ autoSkip: true,
102
+ action: { key: "reward" },
103
+ });
104
+ });
105
+ });
106
+
107
+ describe("sharing step", () => {
108
+ it("should add sharing final step", () => {
109
+ const builder = modalBuilder(mockClient, {});
110
+ const withSharing = builder.sharing({
111
+ popupTitle: "Share!",
112
+ text: "Check this out",
113
+ link: "https://example.com",
114
+ });
115
+
116
+ expect(withSharing.params.steps.final).toEqual({
117
+ action: {
118
+ key: "sharing",
119
+ options: {
120
+ popupTitle: "Share!",
121
+ text: "Check this out",
122
+ link: "https://example.com",
123
+ },
124
+ },
125
+ });
126
+ });
127
+
128
+ it("should add sharing step with additional options", () => {
129
+ const builder = modalBuilder(mockClient, {});
130
+ const withSharing = builder.sharing(
131
+ { text: "Share text", link: "https://example.com" },
132
+ { autoSkip: false }
133
+ );
134
+
135
+ expect(withSharing.params.steps.final).toEqual({
136
+ autoSkip: false,
137
+ action: {
138
+ key: "sharing",
139
+ options: {
140
+ text: "Share text",
141
+ link: "https://example.com",
142
+ },
143
+ },
144
+ });
145
+ });
146
+ });
147
+
148
+ describe("chaining", () => {
149
+ it("should chain sendTx and reward", () => {
150
+ const builder = modalBuilder(mockClient, {});
151
+ const chained = builder
152
+ .sendTx({
153
+ tx: {
154
+ to: "0x1234567890123456789012345678901234567890" as Address,
155
+ },
156
+ })
157
+ .reward();
158
+
159
+ expect(chained.params.steps.sendTransaction).toBeDefined();
160
+ expect(chained.params.steps.final).toEqual({
161
+ action: { key: "reward" },
162
+ });
163
+ });
164
+
165
+ it("should chain sendTx and sharing", () => {
166
+ const builder = modalBuilder(mockClient, {});
167
+ const chained = builder
168
+ .sendTx({
169
+ tx: {
170
+ to: "0x1234567890123456789012345678901234567890" as Address,
171
+ },
172
+ })
173
+ .sharing({ link: "https://example.com" });
174
+
175
+ expect(chained.params.steps.sendTransaction).toBeDefined();
176
+ expect(chained.params.steps.final?.action).toEqual({
177
+ key: "sharing",
178
+ options: { link: "https://example.com" },
179
+ });
180
+ });
181
+ });
182
+
183
+ describe("display", () => {
184
+ it("should call displayModal when display is invoked", async () => {
185
+ const { displayModal } = await import("../displayModal");
186
+
187
+ const mockResponse = {
188
+ login: {
189
+ wallet: "0x1234567890123456789012345678901234567890" as Address,
190
+ },
191
+ };
192
+ vi.mocked(displayModal).mockResolvedValue(mockResponse as any);
193
+
194
+ const builder = modalBuilder(mockClient, {});
195
+ await builder.display();
196
+
197
+ expect(displayModal).toHaveBeenCalledWith(
198
+ mockClient,
199
+ builder.params
200
+ );
201
+ });
202
+
203
+ it("should apply metadata override when provided", async () => {
204
+ const { displayModal } = await import("../displayModal");
205
+
206
+ vi.mocked(displayModal).mockResolvedValue({} as any);
207
+
208
+ const builder = modalBuilder(mockClient, {
209
+ metadata: { header: { title: "Original" } },
210
+ });
211
+ await builder.display(() => ({
212
+ header: { title: "Overridden" },
213
+ }));
214
+
215
+ expect(displayModal).toHaveBeenCalledWith(
216
+ mockClient,
217
+ expect.objectContaining({
218
+ metadata: { header: { title: "Overridden" } },
219
+ })
220
+ );
221
+ });
222
+
223
+ it("should return displayModal result", async () => {
224
+ const { displayModal } = await import("../displayModal");
225
+
226
+ const mockResponse = {
227
+ login: {
228
+ wallet: "0x1234567890123456789012345678901234567890" as Address,
229
+ },
230
+ };
231
+ vi.mocked(displayModal).mockResolvedValue(mockResponse as any);
232
+
233
+ const builder = modalBuilder(mockClient, {});
234
+ const result = await builder.display();
235
+
236
+ expect(result).toEqual(mockResponse);
237
+ });
238
+ });
239
+ });