@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,603 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
+ import { getDappInfo } from "../utils/dapp-info";
3
+
4
+ describe("dapp-info", () => {
5
+ let originalQuerySelector: typeof document.querySelector;
6
+ let originalCreateElement: typeof document.createElement;
7
+ let originalWindowTop: Window | null;
8
+
9
+ beforeEach(() => {
10
+ originalQuerySelector = document.querySelector;
11
+ originalCreateElement = document.createElement;
12
+ originalWindowTop = window.top;
13
+
14
+ // Mock window.top
15
+ Object.defineProperty(window, "top", {
16
+ value: {
17
+ location: {
18
+ origin: "https://example.com",
19
+ },
20
+ },
21
+ writable: true,
22
+ configurable: true,
23
+ });
24
+ });
25
+
26
+ afterEach(() => {
27
+ document.querySelector = originalQuerySelector;
28
+ document.createElement = originalCreateElement;
29
+ Object.defineProperty(window, "top", {
30
+ value: originalWindowTop,
31
+ writable: true,
32
+ configurable: true,
33
+ });
34
+ });
35
+
36
+ describe("getDappInfo", () => {
37
+ it("should return dapp info with origin", async () => {
38
+ // Mock document.querySelector to return null (no meta tags)
39
+ document.querySelector = vi.fn().mockReturnValue(null);
40
+ document.title = "Test Page";
41
+
42
+ // Mock createElement for img
43
+ const mockImg = {
44
+ onload: null as any,
45
+ onerror: null as any,
46
+ src: "",
47
+ };
48
+ document.createElement = vi.fn((tagName: string) => {
49
+ if (tagName === "img") {
50
+ return mockImg as any;
51
+ }
52
+ return originalCreateElement.call(document, tagName);
53
+ });
54
+
55
+ // Simulate image load success
56
+ setTimeout(() => {
57
+ if (mockImg.onload) {
58
+ mockImg.onload();
59
+ }
60
+ }, 10);
61
+
62
+ const info = await getDappInfo();
63
+
64
+ expect(info.origin).toBe("https://example.com");
65
+ expect(info.title).toBe("Test Page");
66
+ });
67
+
68
+ it("should extract site name from og:site_name meta tag", async () => {
69
+ const mockMeta = {
70
+ content: "Test Site Name",
71
+ } as HTMLMetaElement;
72
+
73
+ document.querySelector = vi.fn((selector: string) => {
74
+ if (selector === 'head > meta[property="og:site_name"]') {
75
+ return mockMeta;
76
+ }
77
+ return null;
78
+ });
79
+
80
+ const mockImg = {
81
+ onload: null as any,
82
+ onerror: null as any,
83
+ src: "",
84
+ };
85
+ document.createElement = vi.fn((tagName: string) => {
86
+ if (tagName === "img") {
87
+ return mockImg as any;
88
+ }
89
+ return originalCreateElement.call(document, tagName);
90
+ });
91
+
92
+ setTimeout(() => {
93
+ if (mockImg.onload) {
94
+ mockImg.onload();
95
+ }
96
+ }, 10);
97
+
98
+ const info = await getDappInfo();
99
+ expect(info.title).toBe("Test Site Name");
100
+ });
101
+
102
+ it("should extract description from og:description meta tag", async () => {
103
+ const mockMeta = {
104
+ content: "Test Description",
105
+ } as HTMLMetaElement;
106
+
107
+ document.querySelector = vi.fn((selector: string) => {
108
+ if (selector === 'head > meta[property="og:description"]') {
109
+ return mockMeta;
110
+ }
111
+ return null;
112
+ });
113
+
114
+ const mockImg = {
115
+ onload: null as any,
116
+ onerror: null as any,
117
+ src: "",
118
+ };
119
+ document.createElement = vi.fn((tagName: string) => {
120
+ if (tagName === "img") {
121
+ return mockImg as any;
122
+ }
123
+ return originalCreateElement.call(document, tagName);
124
+ });
125
+
126
+ setTimeout(() => {
127
+ if (mockImg.onload) {
128
+ mockImg.onload();
129
+ }
130
+ }, 10);
131
+
132
+ const info = await getDappInfo();
133
+ expect(info.desc).toBe("Test Description");
134
+ });
135
+
136
+ it("should handle image load error", async () => {
137
+ document.querySelector = vi.fn((selector: string) => {
138
+ if (selector === 'head > link[rel~="icon"]') {
139
+ return {
140
+ href: "https://example.com/favicon.ico",
141
+ } as HTMLLinkElement;
142
+ }
143
+ return null;
144
+ });
145
+
146
+ const mockImg = {
147
+ onload: null as any,
148
+ onerror: null as any,
149
+ src: "",
150
+ };
151
+ document.createElement = vi.fn((tagName: string) => {
152
+ if (tagName === "img") {
153
+ return mockImg as any;
154
+ }
155
+ return originalCreateElement.call(document, tagName);
156
+ });
157
+
158
+ // Simulate image load error
159
+ setTimeout(() => {
160
+ if (mockImg.onerror) {
161
+ mockImg.onerror();
162
+ }
163
+ }, 10);
164
+
165
+ const info = await getDappInfo();
166
+ expect(info.favicon).toBe("");
167
+ });
168
+
169
+ it("should extract site name from meta[name='title'] if og:site_name is not available", async () => {
170
+ const mockMeta = {
171
+ content: "Meta Title",
172
+ } as HTMLMetaElement;
173
+
174
+ document.querySelector = vi.fn((selector: string) => {
175
+ if (selector === 'head > meta[name="title"]') {
176
+ return mockMeta;
177
+ }
178
+ return null;
179
+ });
180
+
181
+ const mockImg = {
182
+ onload: null as any,
183
+ onerror: null as any,
184
+ src: "",
185
+ };
186
+ document.createElement = vi.fn((tagName: string) => {
187
+ if (tagName === "img") {
188
+ return mockImg as any;
189
+ }
190
+ return originalCreateElement.call(document, tagName);
191
+ });
192
+
193
+ setTimeout(() => {
194
+ if (mockImg.onload) {
195
+ mockImg.onload();
196
+ }
197
+ }, 10);
198
+
199
+ const info = await getDappInfo();
200
+ expect(info.title).toBe("Meta Title");
201
+ });
202
+
203
+ it("should use document.title if no meta tags are available", async () => {
204
+ document.querySelector = vi.fn().mockReturnValue(null);
205
+ document.title = "Document Title";
206
+
207
+ const mockImg = {
208
+ onload: null as any,
209
+ onerror: null as any,
210
+ src: "",
211
+ };
212
+ document.createElement = vi.fn((tagName: string) => {
213
+ if (tagName === "img") {
214
+ return mockImg as any;
215
+ }
216
+ return originalCreateElement.call(document, tagName);
217
+ });
218
+
219
+ setTimeout(() => {
220
+ if (mockImg.onload) {
221
+ mockImg.onload();
222
+ }
223
+ }, 10);
224
+
225
+ const info = await getDappInfo();
226
+ expect(info.title).toBe("Document Title");
227
+ });
228
+
229
+ it("should use hostname if document.title is empty", async () => {
230
+ document.querySelector = vi.fn().mockReturnValue(null);
231
+ document.title = "";
232
+ // Mock document.body to have innerText
233
+ Object.defineProperty(document, "body", {
234
+ value: {
235
+ innerText: "",
236
+ },
237
+ writable: true,
238
+ configurable: true,
239
+ });
240
+ // Mock location.hostname without triggering navigation
241
+ Object.defineProperty(window, "location", {
242
+ value: {
243
+ ...window.location,
244
+ hostname: "example.com",
245
+ },
246
+ writable: true,
247
+ configurable: true,
248
+ });
249
+
250
+ const mockImg = {
251
+ onload: null as any,
252
+ onerror: null as any,
253
+ src: "",
254
+ };
255
+ document.createElement = vi.fn((tagName: string) => {
256
+ if (tagName === "img") {
257
+ return mockImg as any;
258
+ }
259
+ return originalCreateElement.call(document, tagName);
260
+ });
261
+
262
+ setTimeout(() => {
263
+ if (mockImg.onload) {
264
+ mockImg.onload();
265
+ }
266
+ }, 10);
267
+
268
+ const info = await getDappInfo();
269
+ expect(info.title).toBe("example.com");
270
+ });
271
+
272
+ it("should extract description from twitter:description meta tag", async () => {
273
+ const mockMeta = {
274
+ content: "Twitter Description",
275
+ } as HTMLMetaElement;
276
+
277
+ document.querySelector = vi.fn((selector: string) => {
278
+ if (selector === 'head > meta[property="twitter:description"]') {
279
+ return mockMeta;
280
+ }
281
+ return null;
282
+ });
283
+
284
+ const mockImg = {
285
+ onload: null as any,
286
+ onerror: null as any,
287
+ src: "",
288
+ };
289
+ document.createElement = vi.fn((tagName: string) => {
290
+ if (tagName === "img") {
291
+ return mockImg as any;
292
+ }
293
+ return originalCreateElement.call(document, tagName);
294
+ });
295
+
296
+ setTimeout(() => {
297
+ if (mockImg.onload) {
298
+ mockImg.onload();
299
+ }
300
+ }, 10);
301
+
302
+ const info = await getDappInfo();
303
+ expect(info.desc).toBe("Twitter Description");
304
+ });
305
+
306
+ it("should extract description from meta[name='description']", async () => {
307
+ const mockMeta = {
308
+ content: "Meta Description",
309
+ } as HTMLMetaElement;
310
+
311
+ document.querySelector = vi.fn((selector: string) => {
312
+ if (selector === 'head > meta[name="description"]') {
313
+ return mockMeta;
314
+ }
315
+ return null;
316
+ });
317
+
318
+ const mockImg = {
319
+ onload: null as any,
320
+ onerror: null as any,
321
+ src: "",
322
+ };
323
+ document.createElement = vi.fn((tagName: string) => {
324
+ if (tagName === "img") {
325
+ return mockImg as any;
326
+ }
327
+ return originalCreateElement.call(document, tagName);
328
+ });
329
+
330
+ setTimeout(() => {
331
+ if (mockImg.onload) {
332
+ mockImg.onload();
333
+ }
334
+ }, 10);
335
+
336
+ const info = await getDappInfo();
337
+ expect(info.desc).toBe("Meta Description");
338
+ });
339
+
340
+ it("should use document.title for description if no description meta tags", async () => {
341
+ document.querySelector = vi.fn().mockReturnValue(null);
342
+ document.title = "Page Title";
343
+
344
+ const mockImg = {
345
+ onload: null as any,
346
+ onerror: null as any,
347
+ src: "",
348
+ };
349
+ document.createElement = vi.fn((tagName: string) => {
350
+ if (tagName === "img") {
351
+ return mockImg as any;
352
+ }
353
+ return originalCreateElement.call(document, tagName);
354
+ });
355
+
356
+ setTimeout(() => {
357
+ if (mockImg.onload) {
358
+ mockImg.onload();
359
+ }
360
+ }, 10);
361
+
362
+ const info = await getDappInfo();
363
+ expect(info.desc).toBe("Page Title");
364
+ });
365
+
366
+ it("should use body innerText first line if no description available", async () => {
367
+ document.querySelector = vi.fn().mockReturnValue(null);
368
+ document.title = "";
369
+ // Use Object.defineProperty to set body.innerText
370
+ const bodyElement = document.createElement("body");
371
+ Object.defineProperty(bodyElement, "innerText", {
372
+ value: "First line\nSecond line",
373
+ writable: true,
374
+ configurable: true,
375
+ });
376
+ Object.defineProperty(document, "body", {
377
+ value: bodyElement,
378
+ writable: true,
379
+ configurable: true,
380
+ });
381
+
382
+ const mockImg = {
383
+ onload: null as any,
384
+ onerror: null as any,
385
+ src: "",
386
+ };
387
+ document.createElement = vi.fn((tagName: string) => {
388
+ if (tagName === "img") {
389
+ return mockImg as any;
390
+ }
391
+ return originalCreateElement.call(document, tagName);
392
+ });
393
+
394
+ setTimeout(() => {
395
+ if (mockImg.onload) {
396
+ mockImg.onload();
397
+ }
398
+ }, 10);
399
+
400
+ const info = await getDappInfo();
401
+ expect(info.desc).toBe("First line");
402
+ });
403
+
404
+ it("should return empty string for description if body innerText is empty", async () => {
405
+ document.querySelector = vi.fn().mockReturnValue(null);
406
+ document.title = "";
407
+ const bodyElement = document.createElement("body");
408
+ Object.defineProperty(bodyElement, "innerText", {
409
+ value: "",
410
+ writable: true,
411
+ configurable: true,
412
+ });
413
+ Object.defineProperty(document, "body", {
414
+ value: bodyElement,
415
+ writable: true,
416
+ configurable: true,
417
+ });
418
+
419
+ const mockImg = {
420
+ onload: null as any,
421
+ onerror: null as any,
422
+ src: "",
423
+ };
424
+ document.createElement = vi.fn((tagName: string) => {
425
+ if (tagName === "img") {
426
+ return mockImg as any;
427
+ }
428
+ return originalCreateElement.call(document, tagName);
429
+ });
430
+
431
+ setTimeout(() => {
432
+ if (mockImg.onload) {
433
+ mockImg.onload();
434
+ }
435
+ }, 10);
436
+
437
+ const info = await getDappInfo();
438
+ expect(info.desc).toBe("");
439
+ });
440
+
441
+ it("should extract icon from link[rel~='icon'] when image loads successfully", async () => {
442
+ document.querySelector = vi.fn((selector: string) => {
443
+ if (selector === 'head > link[rel~="icon"]') {
444
+ return { href: "https://example.com/favicon.ico" } as HTMLLinkElement;
445
+ }
446
+ return null;
447
+ });
448
+
449
+ const bodyElement = document.createElement("body");
450
+ Object.defineProperty(bodyElement, "innerText", { value: "", writable: true, configurable: true });
451
+ Object.defineProperty(document, "body", { value: bodyElement, writable: true, configurable: true });
452
+
453
+ document.createElement = vi.fn((tagName: string) => {
454
+ if (tagName === "img") {
455
+ const el = originalCreateElement.call(document, tagName) as HTMLImageElement;
456
+ let onloadFn: (() => void) | null = null;
457
+ Object.defineProperty(el, "onload", {
458
+ get: () => onloadFn,
459
+ set: (fn: () => void) => {
460
+ onloadFn = fn;
461
+ },
462
+ configurable: true,
463
+ });
464
+ Object.defineProperty(el, "src", {
465
+ get: () => (el as any)._src,
466
+ set: (url: string) => {
467
+ (el as any)._src = url;
468
+ queueMicrotask(() => {
469
+ if (onloadFn) onloadFn();
470
+ });
471
+ },
472
+ configurable: true,
473
+ });
474
+ return el;
475
+ }
476
+ return originalCreateElement.call(document, tagName);
477
+ });
478
+
479
+ const info = await getDappInfo();
480
+ expect(info.favicon).toBe("https://example.com/favicon.ico");
481
+ });
482
+
483
+ it("should extract icon from meta[itemprop='image'] when image loads successfully", async () => {
484
+ document.querySelector = vi.fn((selector: string) => {
485
+ if (selector === 'head > link[rel~="icon"]') return null;
486
+ if (selector === 'head > meta[itemprop="image"]') {
487
+ return { content: "https://example.com/image.png" } as HTMLMetaElement;
488
+ }
489
+ return null;
490
+ });
491
+
492
+ const bodyElement = document.createElement("body");
493
+ Object.defineProperty(bodyElement, "innerText", { value: "", writable: true, configurable: true });
494
+ Object.defineProperty(document, "body", { value: bodyElement, writable: true, configurable: true });
495
+
496
+ document.createElement = vi.fn((tagName: string) => {
497
+ if (tagName === "img") {
498
+ const el = originalCreateElement.call(document, tagName) as HTMLImageElement;
499
+ let onloadFn: (() => void) | null = null;
500
+ Object.defineProperty(el, "onload", {
501
+ get: () => onloadFn,
502
+ set: (fn: () => void) => { onloadFn = fn; },
503
+ configurable: true,
504
+ });
505
+ Object.defineProperty(el, "src", {
506
+ get: () => (el as any)._src,
507
+ set: (url: string) => {
508
+ (el as any)._src = url;
509
+ queueMicrotask(() => { if (onloadFn) onloadFn(); });
510
+ },
511
+ configurable: true,
512
+ });
513
+ return el;
514
+ }
515
+ return originalCreateElement.call(document, tagName);
516
+ });
517
+
518
+ const info = await getDappInfo();
519
+ expect(info.favicon).toBe("https://example.com/image.png");
520
+ });
521
+
522
+ it("should handle image load error and return empty string", async () => {
523
+ document.querySelector = vi.fn((selector: string) => {
524
+ if (selector === 'head > link[rel~="icon"]') {
525
+ return {
526
+ href: "https://example.com/invalid.ico",
527
+ } as HTMLLinkElement;
528
+ }
529
+ return null;
530
+ });
531
+
532
+ // Mock body to prevent innerText error
533
+ const bodyElement = document.createElement("body");
534
+ Object.defineProperty(bodyElement, "innerText", {
535
+ value: "",
536
+ writable: true,
537
+ configurable: true,
538
+ });
539
+ Object.defineProperty(document, "body", {
540
+ value: bodyElement,
541
+ writable: true,
542
+ configurable: true,
543
+ });
544
+
545
+ const mockImg = {
546
+ onload: null as any,
547
+ onerror: null as any,
548
+ src: "",
549
+ };
550
+ document.createElement = vi.fn((tagName: string) => {
551
+ if (tagName === "img") {
552
+ return mockImg as any;
553
+ }
554
+ return originalCreateElement.call(document, tagName);
555
+ });
556
+
557
+ setTimeout(() => {
558
+ if (mockImg.onerror) {
559
+ mockImg.onerror();
560
+ }
561
+ }, 10);
562
+
563
+ const info = await getDappInfo();
564
+ expect(info.favicon).toBe("");
565
+ });
566
+
567
+ it("should handle exception in imgExists", async () => {
568
+ document.querySelector = vi.fn((selector: string) => {
569
+ if (selector === 'head > link[rel~="icon"]') {
570
+ return {
571
+ href: "https://example.com/favicon.ico",
572
+ } as HTMLLinkElement;
573
+ }
574
+ return null;
575
+ });
576
+
577
+ // Mock body to prevent innerText error
578
+ const bodyElement = document.createElement("body");
579
+ Object.defineProperty(bodyElement, "innerText", {
580
+ value: "",
581
+ writable: true,
582
+ configurable: true,
583
+ });
584
+ Object.defineProperty(document, "body", {
585
+ value: bodyElement,
586
+ writable: true,
587
+ configurable: true,
588
+ });
589
+
590
+ const originalCreateElement = document.createElement;
591
+ document.createElement = vi.fn((tagName: string) => {
592
+ if (tagName === "img") {
593
+ throw new Error("Cannot create element");
594
+ }
595
+ return originalCreateElement.call(document, tagName);
596
+ });
597
+
598
+ // The error will be caught in imgExists and return false, not throw
599
+ const info = await getDappInfo();
600
+ expect(info.favicon).toBe("");
601
+ });
602
+ });
603
+ });