@frak-labs/core-sdk 0.0.19 → 0.1.0-beta.00226d62
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/cdn/bundle.iife.js +14 -0
- package/dist/actions-CEEObPYc.js +1 -0
- package/dist/actions-DbQhWYx8.cjs +1 -0
- package/dist/actions.cjs +1 -1
- package/dist/actions.d.cts +3 -1400
- package/dist/actions.d.ts +3 -1400
- package/dist/actions.js +1 -1
- package/dist/bundle.cjs +1 -13
- package/dist/bundle.d.cts +6 -2022
- package/dist/bundle.d.ts +6 -2022
- package/dist/bundle.js +1 -13
- package/dist/index-7OZ39x1U.d.ts +195 -0
- package/dist/index-C6FxkWPC.d.cts +511 -0
- package/dist/index-UFX7xCg3.d.ts +351 -0
- package/dist/index-d8xS4ryI.d.ts +511 -0
- package/dist/index-p4FqSp8z.d.cts +351 -0
- package/dist/index-zDq-VlKx.d.cts +195 -0
- package/dist/index.cjs +1 -13
- package/dist/index.d.cts +4 -1373
- package/dist/index.d.ts +4 -1373
- package/dist/index.js +1 -13
- package/dist/interaction-DMJ3ZfaF.d.cts +45 -0
- package/dist/interaction-KX1h9a7V.d.ts +45 -0
- package/dist/interactions-DnfM3oe0.js +1 -0
- package/dist/interactions-EIXhNLf6.cjs +1 -0
- package/dist/interactions.cjs +1 -1
- package/dist/interactions.d.cts +2 -182
- package/dist/interactions.d.ts +2 -182
- package/dist/interactions.js +1 -1
- package/dist/openSso-D--Airj6.d.cts +1018 -0
- package/dist/openSso-DsKJ4y0j.d.ts +1018 -0
- package/dist/productTypes-BUkXJKZ7.cjs +1 -0
- package/dist/productTypes-CGb1MmBF.js +1 -0
- package/dist/src-B_xO0AR6.cjs +13 -0
- package/dist/src-D2d52OZa.js +13 -0
- package/dist/trackEvent-CHnYa85W.js +1 -0
- package/dist/trackEvent-GuQm_1Nm.cjs +1 -0
- package/package.json +27 -18
- package/src/actions/displayEmbeddedWallet.test.ts +194 -0
- package/src/actions/displayEmbeddedWallet.ts +20 -0
- package/src/actions/displayModal.test.ts +387 -0
- package/src/actions/displayModal.ts +131 -0
- package/src/actions/getProductInformation.test.ts +133 -0
- package/src/actions/getProductInformation.ts +14 -0
- package/src/actions/index.ts +29 -0
- package/src/actions/openSso.test.ts +407 -0
- package/src/actions/openSso.ts +116 -0
- package/src/actions/prepareSso.test.ts +223 -0
- package/src/actions/prepareSso.ts +48 -0
- package/src/actions/referral/processReferral.test.ts +357 -0
- package/src/actions/referral/processReferral.ts +230 -0
- package/src/actions/referral/referralInteraction.test.ts +153 -0
- package/src/actions/referral/referralInteraction.ts +57 -0
- package/src/actions/sendInteraction.test.ts +219 -0
- package/src/actions/sendInteraction.ts +32 -0
- package/src/actions/trackPurchaseStatus.test.ts +287 -0
- package/src/actions/trackPurchaseStatus.ts +53 -0
- package/src/actions/watchWalletStatus.test.ts +372 -0
- package/src/actions/watchWalletStatus.ts +94 -0
- package/src/actions/wrapper/modalBuilder.test.ts +253 -0
- package/src/actions/wrapper/modalBuilder.ts +212 -0
- package/src/actions/wrapper/sendTransaction.test.ts +164 -0
- package/src/actions/wrapper/sendTransaction.ts +62 -0
- package/src/actions/wrapper/siweAuthenticate.test.ts +290 -0
- package/src/actions/wrapper/siweAuthenticate.ts +94 -0
- package/src/bundle.ts +3 -0
- package/src/clients/DebugInfo.test.ts +418 -0
- package/src/clients/DebugInfo.ts +182 -0
- package/src/clients/createIFrameFrakClient.ts +287 -0
- package/src/clients/index.ts +3 -0
- package/src/clients/setupClient.test.ts +343 -0
- package/src/clients/setupClient.ts +73 -0
- package/src/clients/transports/iframeLifecycleManager.test.ts +399 -0
- package/src/clients/transports/iframeLifecycleManager.ts +90 -0
- package/src/constants/interactionTypes.test.ts +128 -0
- package/src/constants/interactionTypes.ts +44 -0
- package/src/constants/locales.ts +14 -0
- package/src/constants/productTypes.test.ts +130 -0
- package/src/constants/productTypes.ts +33 -0
- package/src/index.ts +101 -0
- package/src/interactions/index.ts +5 -0
- package/src/interactions/pressEncoder.test.ts +215 -0
- package/src/interactions/pressEncoder.ts +53 -0
- package/src/interactions/purchaseEncoder.test.ts +291 -0
- package/src/interactions/purchaseEncoder.ts +99 -0
- package/src/interactions/referralEncoder.test.ts +170 -0
- package/src/interactions/referralEncoder.ts +47 -0
- package/src/interactions/retailEncoder.test.ts +107 -0
- package/src/interactions/retailEncoder.ts +37 -0
- package/src/interactions/webshopEncoder.test.ts +56 -0
- package/src/interactions/webshopEncoder.ts +30 -0
- package/src/types/client.ts +14 -0
- package/src/types/compression.ts +22 -0
- package/src/types/config.ts +111 -0
- package/src/types/context.ts +13 -0
- package/src/types/index.ts +71 -0
- package/src/types/lifecycle/client.ts +46 -0
- package/src/types/lifecycle/iframe.ts +35 -0
- package/src/types/lifecycle/index.ts +2 -0
- package/src/types/rpc/displayModal.ts +84 -0
- package/src/types/rpc/embedded/index.ts +68 -0
- package/src/types/rpc/embedded/loggedIn.ts +55 -0
- package/src/types/rpc/embedded/loggedOut.ts +28 -0
- package/src/types/rpc/interaction.ts +43 -0
- package/src/types/rpc/modal/final.ts +46 -0
- package/src/types/rpc/modal/generic.ts +46 -0
- package/src/types/rpc/modal/index.ts +20 -0
- package/src/types/rpc/modal/login.ts +32 -0
- package/src/types/rpc/modal/openSession.ts +25 -0
- package/src/types/rpc/modal/siweAuthenticate.ts +37 -0
- package/src/types/rpc/modal/transaction.ts +33 -0
- package/src/types/rpc/productInformation.ts +59 -0
- package/src/types/rpc/sso.ts +80 -0
- package/src/types/rpc/walletStatus.ts +35 -0
- package/src/types/rpc.ts +158 -0
- package/src/types/transport.ts +34 -0
- package/src/utils/FrakContext.test.ts +407 -0
- package/src/utils/FrakContext.ts +158 -0
- package/src/utils/compression/b64.test.ts +181 -0
- package/src/utils/compression/b64.ts +29 -0
- package/src/utils/compression/compress.test.ts +123 -0
- package/src/utils/compression/compress.ts +11 -0
- package/src/utils/compression/decompress.test.ts +145 -0
- package/src/utils/compression/decompress.ts +11 -0
- package/src/utils/compression/index.ts +3 -0
- package/src/utils/computeProductId.test.ts +80 -0
- package/src/utils/computeProductId.ts +11 -0
- package/src/utils/constants.test.ts +23 -0
- package/src/utils/constants.ts +4 -0
- package/src/utils/formatAmount.test.ts +113 -0
- package/src/utils/formatAmount.ts +18 -0
- package/src/utils/getCurrencyAmountKey.test.ts +44 -0
- package/src/utils/getCurrencyAmountKey.ts +15 -0
- package/src/utils/getSupportedCurrency.test.ts +51 -0
- package/src/utils/getSupportedCurrency.ts +14 -0
- package/src/utils/getSupportedLocale.test.ts +64 -0
- package/src/utils/getSupportedLocale.ts +16 -0
- package/src/utils/iframeHelper.test.ts +450 -0
- package/src/utils/iframeHelper.ts +143 -0
- package/src/utils/index.ts +21 -0
- package/src/utils/sso.test.ts +361 -0
- package/src/utils/sso.ts +119 -0
- package/src/utils/ssoUrlListener.test.ts +252 -0
- package/src/utils/ssoUrlListener.ts +60 -0
- package/src/utils/trackEvent.test.ts +162 -0
- package/src/utils/trackEvent.ts +26 -0
- package/cdn/bundle.js +0 -19
- package/cdn/bundle.js.LICENSE.txt +0 -10
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for DebugInfoGatherer class
|
|
3
|
+
* Tests debug information gathering and formatting for error reporting
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { FrakRpcError, RpcErrorCodes } from "@frak-labs/frame-connector";
|
|
7
|
+
import {
|
|
8
|
+
afterEach,
|
|
9
|
+
beforeEach,
|
|
10
|
+
describe,
|
|
11
|
+
expect,
|
|
12
|
+
it,
|
|
13
|
+
vi,
|
|
14
|
+
} from "../../tests/vitest-fixtures";
|
|
15
|
+
import type { FrakWalletSdkConfig } from "../types";
|
|
16
|
+
import { DebugInfoGatherer } from "./DebugInfo";
|
|
17
|
+
|
|
18
|
+
describe("DebugInfoGatherer", () => {
|
|
19
|
+
let mockConfig: FrakWalletSdkConfig;
|
|
20
|
+
let mockIframe: HTMLIFrameElement;
|
|
21
|
+
let consoleWarnSpy: ReturnType<typeof vi.spyOn>;
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
// Create mock config
|
|
25
|
+
mockConfig = {
|
|
26
|
+
metadata: {
|
|
27
|
+
name: "Test App",
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// Create mock iframe
|
|
32
|
+
mockIframe = document.createElement("iframe");
|
|
33
|
+
mockIframe.src = "https://wallet.frak.id";
|
|
34
|
+
mockIframe.setAttribute("loading", "lazy");
|
|
35
|
+
document.body.appendChild(mockIframe);
|
|
36
|
+
|
|
37
|
+
// Mock iframe contentDocument
|
|
38
|
+
Object.defineProperty(mockIframe, "contentDocument", {
|
|
39
|
+
value: {
|
|
40
|
+
readyState: "complete",
|
|
41
|
+
},
|
|
42
|
+
writable: true,
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// Mock iframe contentWindow
|
|
46
|
+
Object.defineProperty(mockIframe, "contentWindow", {
|
|
47
|
+
value: {},
|
|
48
|
+
writable: true,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// Spy on console.warn
|
|
52
|
+
consoleWarnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
afterEach(() => {
|
|
56
|
+
consoleWarnSpy.mockRestore();
|
|
57
|
+
document.body.removeChild(mockIframe);
|
|
58
|
+
vi.clearAllMocks();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe("constructor", () => {
|
|
62
|
+
it("should create instance with config and iframe", () => {
|
|
63
|
+
const gatherer = new DebugInfoGatherer(mockConfig, mockIframe);
|
|
64
|
+
|
|
65
|
+
expect(gatherer).toBeInstanceOf(DebugInfoGatherer);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("should create instance without config and iframe", () => {
|
|
69
|
+
const gatherer = new DebugInfoGatherer();
|
|
70
|
+
|
|
71
|
+
expect(gatherer).toBeInstanceOf(DebugInfoGatherer);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("should initialize with null lastRequest and lastResponse", () => {
|
|
75
|
+
const gatherer = new DebugInfoGatherer(mockConfig, mockIframe);
|
|
76
|
+
|
|
77
|
+
// Access private properties through formatDebugInfo output
|
|
78
|
+
const debugInfo = gatherer.formatDebugInfo("test error");
|
|
79
|
+
expect(debugInfo).toContain("No Frak request logged");
|
|
80
|
+
expect(debugInfo).toContain("No Frak response logged");
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe("static empty()", () => {
|
|
85
|
+
it("should create empty instance", () => {
|
|
86
|
+
const gatherer = DebugInfoGatherer.empty();
|
|
87
|
+
|
|
88
|
+
expect(gatherer).toBeInstanceOf(DebugInfoGatherer);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it("should create instance without config or iframe", () => {
|
|
92
|
+
const gatherer = DebugInfoGatherer.empty();
|
|
93
|
+
const debugInfo = gatherer.formatDebugInfo("test error");
|
|
94
|
+
|
|
95
|
+
expect(debugInfo).toContain("no-config");
|
|
96
|
+
expect(debugInfo).toContain("not-iframe");
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
describe("setLastRequest", () => {
|
|
101
|
+
it("should set last request with timestamp", () => {
|
|
102
|
+
const gatherer = new DebugInfoGatherer(mockConfig, mockIframe);
|
|
103
|
+
const mockMessage = {
|
|
104
|
+
id: 1,
|
|
105
|
+
method: "test_method",
|
|
106
|
+
params: [],
|
|
107
|
+
} as any;
|
|
108
|
+
|
|
109
|
+
gatherer.setLastRequest(mockMessage);
|
|
110
|
+
|
|
111
|
+
const debugInfo = gatherer.formatDebugInfo("test error");
|
|
112
|
+
expect(debugInfo).toContain("Last Request:");
|
|
113
|
+
expect(debugInfo).not.toContain("No Frak request logged");
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe("setLastResponse", () => {
|
|
118
|
+
it("should set last response with timestamp", () => {
|
|
119
|
+
const gatherer = new DebugInfoGatherer(mockConfig, mockIframe);
|
|
120
|
+
const mockMessage = {
|
|
121
|
+
id: 1,
|
|
122
|
+
method: "test_method",
|
|
123
|
+
} as any;
|
|
124
|
+
const mockResponse = {
|
|
125
|
+
id: 1,
|
|
126
|
+
result: "success",
|
|
127
|
+
} as any;
|
|
128
|
+
|
|
129
|
+
gatherer.setLastResponse(mockMessage, mockResponse);
|
|
130
|
+
|
|
131
|
+
const debugInfo = gatherer.formatDebugInfo("test error");
|
|
132
|
+
expect(debugInfo).toContain("Last Response:");
|
|
133
|
+
expect(debugInfo).not.toContain("No Frak response logged");
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe("updateSetupStatus", () => {
|
|
138
|
+
it("should update setup status to true", () => {
|
|
139
|
+
const gatherer = new DebugInfoGatherer(mockConfig, mockIframe);
|
|
140
|
+
|
|
141
|
+
gatherer.updateSetupStatus(true);
|
|
142
|
+
|
|
143
|
+
const debugInfo = gatherer.formatDebugInfo("test error");
|
|
144
|
+
expect(debugInfo).toContain("Client Status: setup");
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it("should update setup status to false", () => {
|
|
148
|
+
const gatherer = new DebugInfoGatherer(mockConfig, mockIframe);
|
|
149
|
+
|
|
150
|
+
gatherer.updateSetupStatus(false);
|
|
151
|
+
|
|
152
|
+
const debugInfo = gatherer.formatDebugInfo("test error");
|
|
153
|
+
expect(debugInfo).toContain("Client Status: not-setup");
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe("formatDebugInfo", () => {
|
|
158
|
+
it("should format debug info with all fields", () => {
|
|
159
|
+
const gatherer = new DebugInfoGatherer(mockConfig, mockIframe);
|
|
160
|
+
|
|
161
|
+
const debugInfo = gatherer.formatDebugInfo("test error");
|
|
162
|
+
|
|
163
|
+
expect(debugInfo).toContain("Debug Information:");
|
|
164
|
+
expect(debugInfo).toContain("Timestamp:");
|
|
165
|
+
expect(debugInfo).toContain("URL:");
|
|
166
|
+
expect(debugInfo).toContain("Config:");
|
|
167
|
+
expect(debugInfo).toContain("Navigator Info:");
|
|
168
|
+
expect(debugInfo).toContain("IFrame Status:");
|
|
169
|
+
expect(debugInfo).toContain("Last Request:");
|
|
170
|
+
expect(debugInfo).toContain("Last Response:");
|
|
171
|
+
expect(debugInfo).toContain("Client Status:");
|
|
172
|
+
expect(debugInfo).toContain("Error:");
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
it("should format FrakRpcError correctly", () => {
|
|
176
|
+
const gatherer = new DebugInfoGatherer(mockConfig, mockIframe);
|
|
177
|
+
const error = new FrakRpcError(
|
|
178
|
+
RpcErrorCodes.walletNotConnected,
|
|
179
|
+
"Wallet not connected"
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
const debugInfo = gatherer.formatDebugInfo(error);
|
|
183
|
+
|
|
184
|
+
expect(debugInfo).toContain(
|
|
185
|
+
`FrakRpcError: ${RpcErrorCodes.walletNotConnected} 'Wallet not connected'`
|
|
186
|
+
);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("should format regular Error correctly", () => {
|
|
190
|
+
const gatherer = new DebugInfoGatherer(mockConfig, mockIframe);
|
|
191
|
+
const error = new Error("Network timeout");
|
|
192
|
+
|
|
193
|
+
const debugInfo = gatherer.formatDebugInfo(error);
|
|
194
|
+
|
|
195
|
+
expect(debugInfo).toContain("Network timeout");
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it("should format string error correctly", () => {
|
|
199
|
+
const gatherer = new DebugInfoGatherer(mockConfig, mockIframe);
|
|
200
|
+
|
|
201
|
+
const debugInfo = gatherer.formatDebugInfo("String error message");
|
|
202
|
+
|
|
203
|
+
expect(debugInfo).toContain("String error message");
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it("should format unknown error as 'Unknown'", () => {
|
|
207
|
+
const gatherer = new DebugInfoGatherer(mockConfig, mockIframe);
|
|
208
|
+
|
|
209
|
+
const debugInfo = gatherer.formatDebugInfo(null);
|
|
210
|
+
|
|
211
|
+
expect(debugInfo).toContain("Error: Unknown");
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("should include encoded URL", () => {
|
|
215
|
+
const gatherer = new DebugInfoGatherer(mockConfig, mockIframe);
|
|
216
|
+
|
|
217
|
+
const debugInfo = gatherer.formatDebugInfo("test error");
|
|
218
|
+
|
|
219
|
+
// URL should be base64 encoded
|
|
220
|
+
expect(debugInfo).toContain("URL:");
|
|
221
|
+
const urlMatch = debugInfo.match(/URL: (.+)/);
|
|
222
|
+
expect(urlMatch).toBeTruthy();
|
|
223
|
+
if (urlMatch) {
|
|
224
|
+
// Should be base64 encoded (alphanumeric + =)
|
|
225
|
+
expect(urlMatch[1]).toMatch(/^[A-Za-z0-9+/=]+$/);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
it("should include encoded config when config is provided", () => {
|
|
230
|
+
const gatherer = new DebugInfoGatherer(mockConfig, mockIframe);
|
|
231
|
+
|
|
232
|
+
const debugInfo = gatherer.formatDebugInfo("test error");
|
|
233
|
+
|
|
234
|
+
expect(debugInfo).toContain("Config:");
|
|
235
|
+
expect(debugInfo).not.toContain("no-config");
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it("should show 'no-config' when config is not provided", () => {
|
|
239
|
+
const gatherer = new DebugInfoGatherer(undefined, mockIframe);
|
|
240
|
+
|
|
241
|
+
const debugInfo = gatherer.formatDebugInfo("test error");
|
|
242
|
+
|
|
243
|
+
expect(debugInfo).toContain("Config: no-config");
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
it("should include iframe status when iframe is provided", () => {
|
|
247
|
+
const gatherer = new DebugInfoGatherer(mockConfig, mockIframe);
|
|
248
|
+
|
|
249
|
+
const debugInfo = gatherer.formatDebugInfo("test error");
|
|
250
|
+
|
|
251
|
+
expect(debugInfo).toContain("IFrame Status:");
|
|
252
|
+
expect(debugInfo).not.toContain("not-iframe");
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("should show 'not-iframe' when iframe is not provided", () => {
|
|
256
|
+
const gatherer = new DebugInfoGatherer(mockConfig, undefined);
|
|
257
|
+
|
|
258
|
+
const debugInfo = gatherer.formatDebugInfo("test error");
|
|
259
|
+
|
|
260
|
+
expect(debugInfo).toContain("IFrame Status: not-iframe");
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it("should include navigator info", () => {
|
|
264
|
+
const gatherer = new DebugInfoGatherer(mockConfig, mockIframe);
|
|
265
|
+
|
|
266
|
+
const debugInfo = gatherer.formatDebugInfo("test error");
|
|
267
|
+
|
|
268
|
+
expect(debugInfo).toContain("Navigator Info:");
|
|
269
|
+
expect(debugInfo).not.toContain("no-navigator");
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it("should handle iframe without contentDocument", () => {
|
|
273
|
+
const iframeWithoutDoc = document.createElement("iframe");
|
|
274
|
+
Object.defineProperty(iframeWithoutDoc, "contentDocument", {
|
|
275
|
+
value: null,
|
|
276
|
+
writable: true,
|
|
277
|
+
});
|
|
278
|
+
Object.defineProperty(iframeWithoutDoc, "contentWindow", {
|
|
279
|
+
value: {},
|
|
280
|
+
writable: true,
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
const gatherer = new DebugInfoGatherer(
|
|
284
|
+
mockConfig,
|
|
285
|
+
iframeWithoutDoc
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
const debugInfo = gatherer.formatDebugInfo("test error");
|
|
289
|
+
|
|
290
|
+
expect(debugInfo).toContain("IFrame Status:");
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it("should handle iframe with incomplete readyState", () => {
|
|
294
|
+
const iframeIncomplete = document.createElement("iframe");
|
|
295
|
+
Object.defineProperty(iframeIncomplete, "contentDocument", {
|
|
296
|
+
value: {
|
|
297
|
+
readyState: "loading",
|
|
298
|
+
},
|
|
299
|
+
writable: true,
|
|
300
|
+
});
|
|
301
|
+
Object.defineProperty(iframeIncomplete, "contentWindow", {
|
|
302
|
+
value: {},
|
|
303
|
+
writable: true,
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
const gatherer = new DebugInfoGatherer(
|
|
307
|
+
mockConfig,
|
|
308
|
+
iframeIncomplete
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
const debugInfo = gatherer.formatDebugInfo("test error");
|
|
312
|
+
|
|
313
|
+
expect(debugInfo).toContain("IFrame Status:");
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it("should include last request when set", () => {
|
|
317
|
+
const gatherer = new DebugInfoGatherer(mockConfig, mockIframe);
|
|
318
|
+
const mockMessage = {
|
|
319
|
+
id: 1,
|
|
320
|
+
method: "test_method",
|
|
321
|
+
params: [],
|
|
322
|
+
} as any;
|
|
323
|
+
|
|
324
|
+
gatherer.setLastRequest(mockMessage);
|
|
325
|
+
const debugInfo = gatherer.formatDebugInfo("test error");
|
|
326
|
+
|
|
327
|
+
expect(debugInfo).toContain("Last Request:");
|
|
328
|
+
expect(debugInfo).not.toContain("No Frak request logged");
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
it("should include last response when set", () => {
|
|
332
|
+
const gatherer = new DebugInfoGatherer(mockConfig, mockIframe);
|
|
333
|
+
const mockMessage = {
|
|
334
|
+
id: 1,
|
|
335
|
+
method: "test_method",
|
|
336
|
+
} as any;
|
|
337
|
+
const mockResponse = {
|
|
338
|
+
id: 1,
|
|
339
|
+
result: "success",
|
|
340
|
+
} as any;
|
|
341
|
+
|
|
342
|
+
gatherer.setLastResponse(mockMessage, mockResponse);
|
|
343
|
+
const debugInfo = gatherer.formatDebugInfo("test error");
|
|
344
|
+
|
|
345
|
+
expect(debugInfo).toContain("Last Response:");
|
|
346
|
+
expect(debugInfo).not.toContain("No Frak response logged");
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it("should show 'No Frak request logged' when no request is set", () => {
|
|
350
|
+
const gatherer = new DebugInfoGatherer(mockConfig, mockIframe);
|
|
351
|
+
|
|
352
|
+
const debugInfo = gatherer.formatDebugInfo("test error");
|
|
353
|
+
|
|
354
|
+
expect(debugInfo).toContain("No Frak request logged");
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it("should show 'No Frak response logged' when no response is set", () => {
|
|
358
|
+
const gatherer = new DebugInfoGatherer(mockConfig, mockIframe);
|
|
359
|
+
|
|
360
|
+
const debugInfo = gatherer.formatDebugInfo("test error");
|
|
361
|
+
|
|
362
|
+
expect(debugInfo).toContain("No Frak response logged");
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
describe("base64 encoding error handling", () => {
|
|
367
|
+
it("should handle encoding errors gracefully", () => {
|
|
368
|
+
const gatherer = new DebugInfoGatherer(mockConfig, mockIframe);
|
|
369
|
+
|
|
370
|
+
// Create a circular reference that can't be JSON stringified
|
|
371
|
+
const circularObj: any = { prop: "value" };
|
|
372
|
+
circularObj.self = circularObj;
|
|
373
|
+
|
|
374
|
+
// Mock base64Encode to throw an error
|
|
375
|
+
// We can't directly test the private method, but we can test
|
|
376
|
+
// that formatDebugInfo doesn't throw when encoding fails
|
|
377
|
+
// by checking that it still produces output
|
|
378
|
+
|
|
379
|
+
const debugInfo = gatherer.formatDebugInfo("test error");
|
|
380
|
+
|
|
381
|
+
// Should still produce debug info even if encoding fails
|
|
382
|
+
expect(debugInfo).toContain("Debug Information:");
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
describe("integration", () => {
|
|
387
|
+
it("should format complete debug info with all data set", () => {
|
|
388
|
+
const gatherer = new DebugInfoGatherer(mockConfig, mockIframe);
|
|
389
|
+
const mockRequest = {
|
|
390
|
+
id: 1,
|
|
391
|
+
method: "frak_sendInteraction",
|
|
392
|
+
params: [],
|
|
393
|
+
} as any;
|
|
394
|
+
const mockResponse = {
|
|
395
|
+
id: 1,
|
|
396
|
+
result: { delegationId: "123" },
|
|
397
|
+
} as any;
|
|
398
|
+
const error = new FrakRpcError(
|
|
399
|
+
RpcErrorCodes.serverError,
|
|
400
|
+
"Server error"
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
gatherer.setLastRequest(mockRequest);
|
|
404
|
+
gatherer.setLastResponse(mockRequest, mockResponse);
|
|
405
|
+
gatherer.updateSetupStatus(true);
|
|
406
|
+
|
|
407
|
+
const debugInfo = gatherer.formatDebugInfo(error);
|
|
408
|
+
|
|
409
|
+
expect(debugInfo).toContain("Debug Information:");
|
|
410
|
+
expect(debugInfo).toContain("Client Status: setup");
|
|
411
|
+
expect(debugInfo).toContain(
|
|
412
|
+
`FrakRpcError: ${RpcErrorCodes.serverError} 'Server error'`
|
|
413
|
+
);
|
|
414
|
+
expect(debugInfo).not.toContain("No Frak request logged");
|
|
415
|
+
expect(debugInfo).not.toContain("No Frak response logged");
|
|
416
|
+
});
|
|
417
|
+
});
|
|
418
|
+
});
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import {
|
|
2
|
+
FrakRpcError,
|
|
3
|
+
type RpcMessage,
|
|
4
|
+
type RpcResponse,
|
|
5
|
+
} from "@frak-labs/frame-connector";
|
|
6
|
+
import type { FrakWalletSdkConfig } from "../types";
|
|
7
|
+
|
|
8
|
+
type IframeStatus = {
|
|
9
|
+
loading: boolean;
|
|
10
|
+
url: string | null;
|
|
11
|
+
readyState: number;
|
|
12
|
+
contentWindow: boolean;
|
|
13
|
+
isConnected: boolean;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
type DebugInfo = {
|
|
17
|
+
timestamp: string;
|
|
18
|
+
encodedUrl: string;
|
|
19
|
+
navigatorInfo: string;
|
|
20
|
+
encodedConfig: string;
|
|
21
|
+
iframeStatus: string;
|
|
22
|
+
lastRequest: string;
|
|
23
|
+
lastResponse: string;
|
|
24
|
+
clientStatus: string;
|
|
25
|
+
error: string;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
type NavigatorInfo = {
|
|
29
|
+
userAgent: string;
|
|
30
|
+
language: string;
|
|
31
|
+
onLine: boolean;
|
|
32
|
+
screenWidth: number;
|
|
33
|
+
screenHeight: number;
|
|
34
|
+
pixelRatio: number;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/** @ignore */
|
|
38
|
+
export class DebugInfoGatherer {
|
|
39
|
+
private config?: FrakWalletSdkConfig;
|
|
40
|
+
private iframe?: HTMLIFrameElement;
|
|
41
|
+
private isSetupDone = false;
|
|
42
|
+
private lastResponse: null | {
|
|
43
|
+
message: RpcMessage;
|
|
44
|
+
response: RpcResponse;
|
|
45
|
+
timestamp: number;
|
|
46
|
+
} = null;
|
|
47
|
+
private lastRequest: null | {
|
|
48
|
+
event: RpcMessage;
|
|
49
|
+
timestamp: number;
|
|
50
|
+
} = null;
|
|
51
|
+
|
|
52
|
+
constructor(config?: FrakWalletSdkConfig, iframe?: HTMLIFrameElement) {
|
|
53
|
+
this.config = config;
|
|
54
|
+
this.iframe = iframe;
|
|
55
|
+
this.lastRequest = null;
|
|
56
|
+
this.lastResponse = null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Update communication logs
|
|
60
|
+
public setLastResponse(message: RpcMessage, response: RpcResponse) {
|
|
61
|
+
this.lastResponse = {
|
|
62
|
+
message,
|
|
63
|
+
response,
|
|
64
|
+
timestamp: Date.now(),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
public setLastRequest(event: RpcMessage) {
|
|
68
|
+
this.lastRequest = { event, timestamp: Date.now() };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Update connection status
|
|
72
|
+
public updateSetupStatus(status: boolean) {
|
|
73
|
+
this.isSetupDone = status;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
private base64Encode(data: object): string {
|
|
77
|
+
try {
|
|
78
|
+
return btoa(JSON.stringify(data));
|
|
79
|
+
} catch (err) {
|
|
80
|
+
console.warn("Failed to encode debug data", err);
|
|
81
|
+
return btoa("Failed to encode data");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Extract information from the iframe status
|
|
87
|
+
*/
|
|
88
|
+
private getIframeStatus(): IframeStatus | null {
|
|
89
|
+
if (!this.iframe) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
loading: this.iframe.hasAttribute("loading"),
|
|
94
|
+
url: this.iframe.src,
|
|
95
|
+
readyState: this.iframe.contentDocument?.readyState
|
|
96
|
+
? this.iframe.contentDocument.readyState === "complete"
|
|
97
|
+
? 1
|
|
98
|
+
: 0
|
|
99
|
+
: -1,
|
|
100
|
+
contentWindow: !!this.iframe.contentWindow,
|
|
101
|
+
isConnected: this.iframe.isConnected,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private getNavigatorInfo(): NavigatorInfo | null {
|
|
106
|
+
if (!navigator) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
userAgent: navigator.userAgent,
|
|
111
|
+
language: navigator.language,
|
|
112
|
+
onLine: navigator.onLine,
|
|
113
|
+
screenWidth: window.screen.width,
|
|
114
|
+
screenHeight: window.screen.height,
|
|
115
|
+
pixelRatio: window.devicePixelRatio,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
private gatherDebugInfo(error: Error | unknown): DebugInfo {
|
|
120
|
+
const iframeStatus = this.getIframeStatus();
|
|
121
|
+
const navigatorInfo = this.getNavigatorInfo();
|
|
122
|
+
|
|
123
|
+
// Format the error in a readable format
|
|
124
|
+
let formattedError = "Unknown";
|
|
125
|
+
if (error instanceof FrakRpcError) {
|
|
126
|
+
formattedError = `FrakRpcError: ${error.code} '${error.message}'`;
|
|
127
|
+
} else if (error instanceof Error) {
|
|
128
|
+
formattedError = error.message;
|
|
129
|
+
} else if (typeof error === "string") {
|
|
130
|
+
formattedError = error;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Craft the debug info
|
|
134
|
+
const debugInfo: DebugInfo = {
|
|
135
|
+
timestamp: new Date().toISOString(),
|
|
136
|
+
encodedUrl: btoa(window.location.href),
|
|
137
|
+
encodedConfig: this.config
|
|
138
|
+
? this.base64Encode(this.config)
|
|
139
|
+
: "no-config",
|
|
140
|
+
navigatorInfo: navigatorInfo
|
|
141
|
+
? this.base64Encode(navigatorInfo)
|
|
142
|
+
: "no-navigator",
|
|
143
|
+
iframeStatus: iframeStatus
|
|
144
|
+
? this.base64Encode(iframeStatus)
|
|
145
|
+
: "not-iframe",
|
|
146
|
+
lastRequest: this.lastRequest
|
|
147
|
+
? this.base64Encode(this.lastRequest)
|
|
148
|
+
: "No Frak request logged",
|
|
149
|
+
lastResponse: this.lastResponse
|
|
150
|
+
? this.base64Encode(this.lastResponse)
|
|
151
|
+
: "No Frak response logged",
|
|
152
|
+
clientStatus: this.isSetupDone ? "setup" : "not-setup",
|
|
153
|
+
error: formattedError,
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
return debugInfo;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
public static empty(): DebugInfoGatherer {
|
|
160
|
+
return new DebugInfoGatherer();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Format Frak debug information
|
|
165
|
+
*/
|
|
166
|
+
public formatDebugInfo(error: Error | unknown | string): string {
|
|
167
|
+
const debugInfo = this.gatherDebugInfo(error);
|
|
168
|
+
return `
|
|
169
|
+
Debug Information:
|
|
170
|
+
-----------------
|
|
171
|
+
Timestamp: ${debugInfo.timestamp}
|
|
172
|
+
URL: ${debugInfo.encodedUrl}
|
|
173
|
+
Config: ${debugInfo.encodedConfig}
|
|
174
|
+
Navigator Info: ${debugInfo.navigatorInfo}
|
|
175
|
+
IFrame Status: ${debugInfo.iframeStatus}
|
|
176
|
+
Last Request: ${debugInfo.lastRequest}
|
|
177
|
+
Last Response: ${debugInfo.lastResponse}
|
|
178
|
+
Client Status: ${debugInfo.clientStatus}
|
|
179
|
+
Error: ${debugInfo.error}
|
|
180
|
+
`.trim();
|
|
181
|
+
}
|
|
182
|
+
}
|