mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import {RichText} from '@atproto/api'
2
3import {parseEmbedPlayerFromUrl} from 'lib/strings/embed-player'
4import {
5 createStarterPackGooglePlayUri,
6 createStarterPackLinkFromAndroidReferrer,
7 parseStarterPackUri,
8} from 'lib/strings/starter-pack'
9import {cleanError} from '../../src/lib/strings/errors'
10import {createFullHandle, makeValidHandle} from '../../src/lib/strings/handles'
11import {enforceLen} from '../../src/lib/strings/helpers'
12import {detectLinkables} from '../../src/lib/strings/rich-text-detection'
13import {shortenLinks} from '../../src/lib/strings/rich-text-manip'
14import {
15 makeRecordUri,
16 toNiceDomain,
17 toShareUrl,
18 toShortUrl,
19} from '../../src/lib/strings/url-helpers'
20
21describe('detectLinkables', () => {
22 const inputs = [
23 'no linkable',
24 '@start middle end',
25 'start @middle end',
26 'start middle @end',
27 '@start @middle @end',
28 '@full123.test-of-chars',
29 'not@right',
30 '@bad!@#$chars',
31 '@newline1\n@newline2',
32 'parenthetical (@handle)',
33 'start https://middle.com end',
34 'start https://middle.com/foo/bar end',
35 'start https://middle.com/foo/bar?baz=bux end',
36 'start https://middle.com/foo/bar?baz=bux#hash end',
37 'https://start.com/foo/bar?baz=bux#hash middle end',
38 'start middle https://end.com/foo/bar?baz=bux#hash',
39 'https://newline1.com\nhttps://newline2.com',
40 'start middle.com end',
41 'start middle.com/foo/bar end',
42 'start middle.com/foo/bar?baz=bux end',
43 'start middle.com/foo/bar?baz=bux#hash end',
44 'start.com/foo/bar?baz=bux#hash middle end',
45 'start middle end.com/foo/bar?baz=bux#hash',
46 'newline1.com\nnewline2.com',
47 'not.. a..url ..here',
48 'e.g.',
49 'e.g. real.com fake.notreal',
50 'something-cool.jpg',
51 'website.com.jpg',
52 'e.g./foo',
53 'website.com.jpg/foo',
54 'Classic article https://socket3.wordpress.com/2018/02/03/designing-windows-95s-user-interface/',
55 'Classic article https://socket3.wordpress.com/2018/02/03/designing-windows-95s-user-interface/ ',
56 'https://foo.com https://bar.com/whatever https://baz.com',
57 'punctuation https://foo.com, https://bar.com/whatever; https://baz.com.',
58 'parenthetical (https://foo.com)',
59 'except for https://foo.com/thing_(cool)',
60 ]
61 const outputs = [
62 ['no linkable'],
63 [{link: '@start'}, ' middle end'],
64 ['start ', {link: '@middle'}, ' end'],
65 ['start middle ', {link: '@end'}],
66 [{link: '@start'}, ' ', {link: '@middle'}, ' ', {link: '@end'}],
67 [{link: '@full123.test-of-chars'}],
68 ['not@right'],
69 [{link: '@bad'}, '!@#$chars'],
70 [{link: '@newline1'}, '\n', {link: '@newline2'}],
71 ['parenthetical (', {link: '@handle'}, ')'],
72 ['start ', {link: 'https://middle.com'}, ' end'],
73 ['start ', {link: 'https://middle.com/foo/bar'}, ' end'],
74 ['start ', {link: 'https://middle.com/foo/bar?baz=bux'}, ' end'],
75 ['start ', {link: 'https://middle.com/foo/bar?baz=bux#hash'}, ' end'],
76 [{link: 'https://start.com/foo/bar?baz=bux#hash'}, ' middle end'],
77 ['start middle ', {link: 'https://end.com/foo/bar?baz=bux#hash'}],
78 [{link: 'https://newline1.com'}, '\n', {link: 'https://newline2.com'}],
79 ['start ', {link: 'middle.com'}, ' end'],
80 ['start ', {link: 'middle.com/foo/bar'}, ' end'],
81 ['start ', {link: 'middle.com/foo/bar?baz=bux'}, ' end'],
82 ['start ', {link: 'middle.com/foo/bar?baz=bux#hash'}, ' end'],
83 [{link: 'start.com/foo/bar?baz=bux#hash'}, ' middle end'],
84 ['start middle ', {link: 'end.com/foo/bar?baz=bux#hash'}],
85 [{link: 'newline1.com'}, '\n', {link: 'newline2.com'}],
86 ['not.. a..url ..here'],
87 ['e.g.'],
88 ['e.g. ', {link: 'real.com'}, ' fake.notreal'],
89 ['something-cool.jpg'],
90 ['website.com.jpg'],
91 ['e.g./foo'],
92 ['website.com.jpg/foo'],
93 [
94 'Classic article ',
95 {
96 link: 'https://socket3.wordpress.com/2018/02/03/designing-windows-95s-user-interface/',
97 },
98 ],
99 [
100 'Classic article ',
101 {
102 link: 'https://socket3.wordpress.com/2018/02/03/designing-windows-95s-user-interface/',
103 },
104 ' ',
105 ],
106 [
107 {link: 'https://foo.com'},
108 ' ',
109 {link: 'https://bar.com/whatever'},
110 ' ',
111 {link: 'https://baz.com'},
112 ],
113 [
114 'punctuation ',
115 {link: 'https://foo.com'},
116 ', ',
117 {link: 'https://bar.com/whatever'},
118 '; ',
119 {link: 'https://baz.com'},
120 '.',
121 ],
122 ['parenthetical (', {link: 'https://foo.com'}, ')'],
123 ['except for ', {link: 'https://foo.com/thing_(cool)'}],
124 ]
125 it('correctly handles a set of text inputs', () => {
126 for (let i = 0; i < inputs.length; i++) {
127 const input = inputs[i]
128 const output = detectLinkables(input)
129 expect(output).toEqual(outputs[i])
130 }
131 })
132})
133
134describe('makeRecordUri', () => {
135 const inputs: [string, string, string][] = [
136 ['alice.test', 'app.bsky.feed.post', '3jk7x4irgv52r'],
137 ]
138 const outputs = ['at://alice.test/app.bsky.feed.post/3jk7x4irgv52r']
139
140 it('correctly builds a record URI', () => {
141 for (let i = 0; i < inputs.length; i++) {
142 const input = inputs[i]
143 const result = makeRecordUri(...input)
144 expect(result).toEqual(outputs[i])
145 }
146 })
147})
148
149describe('makeValidHandle', () => {
150 const inputs = [
151 'test-handle-123',
152 'test!"#$%&/()=?_',
153 'this-handle-should-be-too-big',
154 ]
155 const outputs = ['test-handle-123', 'test', 'this-handle-should-b']
156
157 it('correctly parses and corrects handles', () => {
158 for (let i = 0; i < inputs.length; i++) {
159 const result = makeValidHandle(inputs[i])
160 expect(result).toEqual(outputs[i])
161 }
162 })
163})
164
165describe('createFullHandle', () => {
166 const inputs: [string, string][] = [
167 ['test-handle-123', 'test'],
168 ['.test.handle', 'test.test.'],
169 ['test.handle.', '.test.test'],
170 ]
171 const outputs = [
172 'test-handle-123.test',
173 '.test.handle.test.test.',
174 'test.handle.test.test',
175 ]
176
177 it('correctly parses and corrects handles', () => {
178 for (let i = 0; i < inputs.length; i++) {
179 const input = inputs[i]
180 const result = createFullHandle(...input)
181 expect(result).toEqual(outputs[i])
182 }
183 })
184})
185
186describe('enforceLen', () => {
187 const inputs: [string, number][] = [
188 ['Hello World!', 5],
189 ['Hello World!', 20],
190 ['', 5],
191 ]
192 const outputs = ['Hello', 'Hello World!', '']
193
194 it('correctly enforces defined length on a given string', () => {
195 for (let i = 0; i < inputs.length; i++) {
196 const input = inputs[i]
197 const result = enforceLen(...input)
198 expect(result).toEqual(outputs[i])
199 }
200 })
201})
202
203describe('cleanError', () => {
204 const inputs = [
205 'TypeError: Network request failed',
206 'Error: Aborted',
207 'Error: TypeError "x" is not a function',
208 'Error: SyntaxError unexpected token "export"',
209 'Some other error',
210 ]
211 const outputs = [
212 'Unable to connect. Please check your internet connection and try again.',
213 'Unable to connect. Please check your internet connection and try again.',
214 'TypeError "x" is not a function',
215 'SyntaxError unexpected token "export"',
216 'Some other error',
217 ]
218
219 it('removes extra content from error message', () => {
220 for (let i = 0; i < inputs.length; i++) {
221 const result = cleanError(inputs[i])
222 expect(result).toEqual(outputs[i])
223 }
224 })
225})
226
227describe('toNiceDomain', () => {
228 const inputs = [
229 'https://example.com/index.html',
230 'https://bsky.app',
231 'https://bsky.social',
232 '#123123123',
233 ]
234 const outputs = ['example.com', 'bsky.app', 'Bluesky Social', '#123123123']
235
236 it("displays the url's host in a easily readable manner", () => {
237 for (let i = 0; i < inputs.length; i++) {
238 const result = toNiceDomain(inputs[i])
239 expect(result).toEqual(outputs[i])
240 }
241 })
242})
243
244describe('toShortUrl', () => {
245 const inputs = [
246 'https://bsky.app',
247 'https://bsky.app/3jk7x4irgv52r',
248 'https://bsky.app/3jk7x4irgv52r2313y182h9',
249 'https://very-long-domain-name.com/foo',
250 'https://very-long-domain-name.com/foo?bar=baz#andsomemore',
251 ]
252 const outputs = [
253 'bsky.app',
254 'bsky.app/3jk7x4irgv52r',
255 'bsky.app/3jk7x4irgv52...',
256 'very-long-domain-name.com/foo',
257 'very-long-domain-name.com/foo?bar=baz#...',
258 ]
259
260 it('shortens the url', () => {
261 for (let i = 0; i < inputs.length; i++) {
262 const result = toShortUrl(inputs[i])
263 expect(result).toEqual(outputs[i])
264 }
265 })
266})
267
268describe('toShareUrl', () => {
269 const inputs = ['https://bsky.app', '/3jk7x4irgv52r', 'item/test/123']
270 const outputs = [
271 'https://bsky.app',
272 'https://bsky.app/3jk7x4irgv52r',
273 'https://bsky.app/item/test/123',
274 ]
275
276 it('appends https, when not present', () => {
277 for (let i = 0; i < inputs.length; i++) {
278 const result = toShareUrl(inputs[i])
279 expect(result).toEqual(outputs[i])
280 }
281 })
282})
283
284describe('shortenLinks', () => {
285 const inputs = [
286 'start https://middle.com/foo/bar?baz=bux#hash end',
287 'https://start.com/foo/bar?baz=bux#hash middle end',
288 'start middle https://end.com/foo/bar?baz=bux#hash',
289 'https://newline1.com/very/long/url/here\nhttps://newline2.com/very/long/url/here',
290 'Classic article https://socket3.wordpress.com/2018/02/03/designing-windows-95s-user-interface/',
291 ]
292 const outputs = [
293 [
294 'start middle.com/foo/bar?baz=... end',
295 ['https://middle.com/foo/bar?baz=bux#hash'],
296 ],
297 [
298 'start.com/foo/bar?baz=... middle end',
299 ['https://start.com/foo/bar?baz=bux#hash'],
300 ],
301 [
302 'start middle end.com/foo/bar?baz=...',
303 ['https://end.com/foo/bar?baz=bux#hash'],
304 ],
305 [
306 'newline1.com/very/long/ur...\nnewline2.com/very/long/ur...',
307 [
308 'https://newline1.com/very/long/url/here',
309 'https://newline2.com/very/long/url/here',
310 ],
311 ],
312 [
313 'Classic article socket3.wordpress.com/2018/02/03/d...',
314 [
315 'https://socket3.wordpress.com/2018/02/03/designing-windows-95s-user-interface/',
316 ],
317 ],
318 ]
319
320 it('correctly shortens rich text while preserving facet URIs', () => {
321 for (let i = 0; i < inputs.length; i++) {
322 const input = inputs[i]
323 const inputRT = new RichText({text: input})
324 inputRT.detectFacetsWithoutResolution()
325 const outputRT = shortenLinks(inputRT)
326 expect(outputRT.text).toEqual(outputs[i][0])
327 expect(outputRT.facets?.length).toEqual(outputs[i][1].length)
328 for (let j = 0; j < outputs[i][1].length; j++) {
329 expect(outputRT.facets![j].features[0].uri).toEqual(outputs[i][1][j])
330 }
331 }
332 })
333})
334
335describe('parseEmbedPlayerFromUrl', () => {
336 const inputs = [
337 'https://youtu.be/videoId',
338 'https://youtu.be/videoId?t=1s',
339 'https://www.youtube.com/watch?v=videoId',
340 'https://www.youtube.com/watch?v=videoId&feature=share',
341 'https://www.youtube.com/watch?v=videoId&t=1s',
342 'https://youtube.com/watch?v=videoId',
343 'https://youtube.com/watch?v=videoId&feature=share',
344 'https://youtube.com/shorts/videoId',
345 'https://youtube.com/live/videoId',
346 'https://m.youtube.com/watch?v=videoId',
347 'https://music.youtube.com/watch?v=videoId',
348
349 'https://youtube.com/shorts/',
350 'https://youtube.com/',
351 'https://youtube.com/random',
352 'https://youtube.com/live/',
353
354 'https://twitch.tv/channelName',
355 'https://www.twitch.tv/channelName',
356 'https://m.twitch.tv/channelName',
357
358 'https://twitch.tv/channelName/clip/clipId',
359 'https://twitch.tv/videos/videoId',
360
361 'https://open.spotify.com/playlist/playlistId',
362 'https://open.spotify.com/playlist/playlistId?param=value',
363 'https://open.spotify.com/locale/playlist/playlistId',
364
365 'https://open.spotify.com/track/songId',
366 'https://open.spotify.com/track/songId?param=value',
367 'https://open.spotify.com/locale/track/songId',
368
369 'https://open.spotify.com/album/albumId',
370 'https://open.spotify.com/album/albumId?param=value',
371 'https://open.spotify.com/locale/album/albumId',
372
373 'https://soundcloud.com/user/track',
374 'https://soundcloud.com/user/sets/set',
375 'https://soundcloud.com/user/',
376
377 'https://music.apple.com/us/playlist/playlistName/playlistId',
378 'https://music.apple.com/us/album/albumName/albumId',
379 'https://music.apple.com/us/album/albumName/albumId?i=songId',
380
381 'https://vimeo.com/videoId',
382 'https://vimeo.com/videoId?autoplay=0',
383
384 'https://giphy.com/gifs/some-random-gif-name-gifId',
385 'https://giphy.com/gif/some-random-gif-name-gifId',
386 'https://giphy.com/gifs/',
387
388 'https://giphy.com/gifs/39248209509382934029?hh=100&ww=100',
389
390 'https://media.giphy.com/media/gifId/giphy.webp',
391 'https://media0.giphy.com/media/gifId/giphy.webp',
392 'https://media1.giphy.com/media/gifId/giphy.gif',
393 'https://media2.giphy.com/media/gifId/giphy.webp',
394 'https://media3.giphy.com/media/gifId/giphy.mp4',
395 'https://media4.giphy.com/media/gifId/giphy.webp',
396 'https://media5.giphy.com/media/gifId/giphy.mp4',
397 'https://media0.giphy.com/media/gifId/giphy.mp3',
398 'https://media1.google.com/media/gifId/giphy.webp',
399
400 'https://media.giphy.com/media/trackingId/gifId/giphy.webp',
401
402 'https://i.giphy.com/media/gifId/giphy.webp',
403 'https://i.giphy.com/media/gifId/giphy.webp',
404 'https://i.giphy.com/gifId.gif',
405 'https://i.giphy.com/gifId.gif',
406
407 'https://tenor.com/view/gifId',
408 'https://tenor.com/notView/gifId',
409 'https://tenor.com/view',
410 'https://tenor.com/view/gifId.gif',
411 'https://tenor.com/intl/view/gifId.gif',
412
413 'https://media.tenor.com/someID_AAAAC/someName.gif?hh=100&ww=100',
414 'https://media.tenor.com/someID_AAAAC/someName.gif',
415 'https://media.tenor.com/someID/someName.gif',
416 'https://media.tenor.com/someID',
417 'https://media.tenor.com',
418
419 'https://www.flickr.com/photos/username/albums/72177720308493661',
420 'https://flickr.com/photos/username/albums/72177720308493661',
421 'https://flickr.com/photos/username/albums/72177720308493661/',
422 'https://flickr.com/photos/username/albums/72177720308493661//',
423 'https://flic.kr/s/aHBqjAES3i',
424
425 'https://flickr.com/foetoes/username/albums/3903',
426 'https://flickr.com/albums/3903',
427 'https://flic.kr/s/OolI',
428 'https://flic.kr/t/aHBqjAES3i',
429
430 'https://www.flickr.com/groups/898944@N23/pool',
431 'https://flickr.com/groups/898944@N23/pool',
432 'https://flickr.com/groups/898944@N23/pool/',
433 'https://flickr.com/groups/898944@N23/pool//',
434 'https://flic.kr/go/8WJtR',
435
436 'https://www.flickr.com/groups/898944@N23/',
437 'https://www.flickr.com/groups',
438 ]
439
440 const outputs = [
441 {
442 type: 'youtube_video',
443 source: 'youtube',
444 playerUri: 'https://bsky.app/iframe/youtube.html?videoId=videoId&start=0',
445 },
446 {
447 type: 'youtube_video',
448 source: 'youtube',
449 playerUri: 'https://bsky.app/iframe/youtube.html?videoId=videoId&start=1',
450 },
451 {
452 type: 'youtube_video',
453 source: 'youtube',
454 playerUri: 'https://bsky.app/iframe/youtube.html?videoId=videoId&start=0',
455 },
456 {
457 type: 'youtube_video',
458 source: 'youtube',
459 playerUri: 'https://bsky.app/iframe/youtube.html?videoId=videoId&start=0',
460 },
461 {
462 type: 'youtube_video',
463 source: 'youtube',
464 playerUri: 'https://bsky.app/iframe/youtube.html?videoId=videoId&start=1',
465 },
466 {
467 type: 'youtube_video',
468 source: 'youtube',
469 playerUri: 'https://bsky.app/iframe/youtube.html?videoId=videoId&start=0',
470 },
471 {
472 type: 'youtube_video',
473 source: 'youtube',
474 playerUri: 'https://bsky.app/iframe/youtube.html?videoId=videoId&start=0',
475 },
476 {
477 type: 'youtube_short',
478 source: 'youtubeShorts',
479 hideDetails: true,
480 playerUri: 'https://bsky.app/iframe/youtube.html?videoId=videoId&start=0',
481 },
482 {
483 type: 'youtube_video',
484 source: 'youtube',
485 playerUri: 'https://bsky.app/iframe/youtube.html?videoId=videoId&start=0',
486 },
487 {
488 type: 'youtube_video',
489 source: 'youtube',
490 playerUri: 'https://bsky.app/iframe/youtube.html?videoId=videoId&start=0',
491 },
492 {
493 type: 'youtube_video',
494 source: 'youtube',
495 playerUri: 'https://bsky.app/iframe/youtube.html?videoId=videoId&start=0',
496 },
497
498 undefined,
499 undefined,
500 undefined,
501 undefined,
502
503 {
504 type: 'twitch_video',
505 source: 'twitch',
506 playerUri: `https://player.twitch.tv/?volume=0.5&!muted&autoplay&channel=channelName&parent=localhost`,
507 },
508 {
509 type: 'twitch_video',
510 source: 'twitch',
511 playerUri: `https://player.twitch.tv/?volume=0.5&!muted&autoplay&channel=channelName&parent=localhost`,
512 },
513 {
514 type: 'twitch_video',
515 source: 'twitch',
516 playerUri: `https://player.twitch.tv/?volume=0.5&!muted&autoplay&channel=channelName&parent=localhost`,
517 },
518 {
519 type: 'twitch_video',
520 source: 'twitch',
521 playerUri: `https://clips.twitch.tv/embed?volume=0.5&autoplay=true&clip=clipId&parent=localhost`,
522 },
523 {
524 type: 'twitch_video',
525 source: 'twitch',
526 playerUri: `https://player.twitch.tv/?volume=0.5&!muted&autoplay&video=videoId&parent=localhost`,
527 },
528
529 {
530 type: 'spotify_playlist',
531 source: 'spotify',
532 playerUri: `https://open.spotify.com/embed/playlist/playlistId`,
533 },
534 {
535 type: 'spotify_playlist',
536 source: 'spotify',
537 playerUri: `https://open.spotify.com/embed/playlist/playlistId`,
538 },
539 {
540 type: 'spotify_playlist',
541 source: 'spotify',
542 playerUri: `https://open.spotify.com/embed/playlist/playlistId`,
543 },
544
545 {
546 type: 'spotify_song',
547 source: 'spotify',
548 playerUri: `https://open.spotify.com/embed/track/songId`,
549 },
550 {
551 type: 'spotify_song',
552 source: 'spotify',
553 playerUri: `https://open.spotify.com/embed/track/songId`,
554 },
555 {
556 type: 'spotify_song',
557 source: 'spotify',
558 playerUri: `https://open.spotify.com/embed/track/songId`,
559 },
560
561 {
562 type: 'spotify_album',
563 source: 'spotify',
564 playerUri: `https://open.spotify.com/embed/album/albumId`,
565 },
566 {
567 type: 'spotify_album',
568 source: 'spotify',
569 playerUri: `https://open.spotify.com/embed/album/albumId`,
570 },
571 {
572 type: 'spotify_album',
573 source: 'spotify',
574 playerUri: `https://open.spotify.com/embed/album/albumId`,
575 },
576
577 {
578 type: 'soundcloud_track',
579 source: 'soundcloud',
580 playerUri: `https://w.soundcloud.com/player/?url=https://soundcloud.com/user/track&auto_play=true&visual=false&hide_related=true`,
581 },
582 {
583 type: 'soundcloud_set',
584 source: 'soundcloud',
585 playerUri: `https://w.soundcloud.com/player/?url=https://soundcloud.com/user/sets/set&auto_play=true&visual=false&hide_related=true`,
586 },
587 undefined,
588
589 {
590 type: 'apple_music_playlist',
591 source: 'appleMusic',
592 playerUri:
593 'https://embed.music.apple.com/us/playlist/playlistName/playlistId',
594 },
595 {
596 type: 'apple_music_album',
597 source: 'appleMusic',
598 playerUri: 'https://embed.music.apple.com/us/album/albumName/albumId',
599 },
600 {
601 type: 'apple_music_song',
602 source: 'appleMusic',
603 playerUri:
604 'https://embed.music.apple.com/us/album/albumName/albumId?i=songId',
605 },
606
607 {
608 type: 'vimeo_video',
609 source: 'vimeo',
610 playerUri: 'https://player.vimeo.com/video/videoId?autoplay=1',
611 },
612 {
613 type: 'vimeo_video',
614 source: 'vimeo',
615 playerUri: 'https://player.vimeo.com/video/videoId?autoplay=1',
616 },
617
618 {
619 type: 'giphy_gif',
620 source: 'giphy',
621 isGif: true,
622 hideDetails: true,
623 metaUri: 'https://giphy.com/gifs/gifId',
624 playerUri: 'https://i.giphy.com/media/gifId/200.webp',
625 },
626 undefined,
627 undefined,
628 {
629 type: 'giphy_gif',
630 source: 'giphy',
631 isGif: true,
632 hideDetails: true,
633 metaUri: 'https://giphy.com/gifs/39248209509382934029',
634 playerUri: 'https://i.giphy.com/media/39248209509382934029/200.webp',
635 },
636 {
637 type: 'giphy_gif',
638 source: 'giphy',
639 isGif: true,
640 hideDetails: true,
641 metaUri: 'https://giphy.com/gifs/gifId',
642 playerUri: 'https://i.giphy.com/media/gifId/200.webp',
643 },
644 {
645 type: 'giphy_gif',
646 source: 'giphy',
647 isGif: true,
648 hideDetails: true,
649 metaUri: 'https://giphy.com/gifs/gifId',
650 playerUri: 'https://i.giphy.com/media/gifId/200.webp',
651 },
652 {
653 type: 'giphy_gif',
654 source: 'giphy',
655 isGif: true,
656 hideDetails: true,
657 metaUri: 'https://giphy.com/gifs/gifId',
658 playerUri: 'https://i.giphy.com/media/gifId/200.webp',
659 },
660 {
661 type: 'giphy_gif',
662 source: 'giphy',
663 isGif: true,
664 hideDetails: true,
665 metaUri: 'https://giphy.com/gifs/gifId',
666 playerUri: 'https://i.giphy.com/media/gifId/200.webp',
667 },
668 {
669 type: 'giphy_gif',
670 source: 'giphy',
671 isGif: true,
672 hideDetails: true,
673 metaUri: 'https://giphy.com/gifs/gifId',
674 playerUri: 'https://i.giphy.com/media/gifId/200.webp',
675 },
676 {
677 type: 'giphy_gif',
678 source: 'giphy',
679 isGif: true,
680 hideDetails: true,
681 metaUri: 'https://giphy.com/gifs/gifId',
682 playerUri: 'https://i.giphy.com/media/gifId/200.webp',
683 },
684 undefined,
685 undefined,
686 undefined,
687
688 {
689 type: 'giphy_gif',
690 source: 'giphy',
691 isGif: true,
692 hideDetails: true,
693 metaUri: 'https://giphy.com/gifs/gifId',
694 playerUri: 'https://i.giphy.com/media/gifId/200.webp',
695 },
696
697 {
698 type: 'giphy_gif',
699 source: 'giphy',
700 isGif: true,
701 hideDetails: true,
702 metaUri: 'https://giphy.com/gifs/gifId',
703 playerUri: 'https://i.giphy.com/media/gifId/200.webp',
704 },
705 {
706 type: 'giphy_gif',
707 source: 'giphy',
708 isGif: true,
709 hideDetails: true,
710 metaUri: 'https://giphy.com/gifs/gifId',
711 playerUri: 'https://i.giphy.com/media/gifId/200.webp',
712 },
713 {
714 type: 'giphy_gif',
715 source: 'giphy',
716 isGif: true,
717 hideDetails: true,
718 metaUri: 'https://giphy.com/gifs/gifId',
719 playerUri: 'https://i.giphy.com/media/gifId/200.webp',
720 },
721 {
722 type: 'giphy_gif',
723 source: 'giphy',
724 isGif: true,
725 hideDetails: true,
726 metaUri: 'https://giphy.com/gifs/gifId',
727 playerUri: 'https://i.giphy.com/media/gifId/200.webp',
728 },
729
730 undefined,
731 undefined,
732 undefined,
733 undefined,
734 undefined,
735
736 {
737 type: 'tenor_gif',
738 source: 'tenor',
739 isGif: true,
740 hideDetails: true,
741 playerUri: 'https://t.gifs.bsky.app/someID_AAAAM/someName.gif',
742 dimensions: {
743 width: 100,
744 height: 100,
745 },
746 },
747 undefined,
748 undefined,
749 undefined,
750 undefined,
751
752 {
753 type: 'flickr_album',
754 source: 'flickr',
755 playerUri: 'https://embedr.flickr.com/photosets/72177720308493661',
756 },
757 {
758 type: 'flickr_album',
759 source: 'flickr',
760 playerUri: 'https://embedr.flickr.com/photosets/72177720308493661',
761 },
762 {
763 type: 'flickr_album',
764 source: 'flickr',
765 playerUri: 'https://embedr.flickr.com/photosets/72177720308493661',
766 },
767 {
768 type: 'flickr_album',
769 source: 'flickr',
770 playerUri: 'https://embedr.flickr.com/photosets/72177720308493661',
771 },
772 {
773 type: 'flickr_album',
774 source: 'flickr',
775 playerUri: 'https://embedr.flickr.com/photosets/72177720308493661',
776 },
777
778 undefined,
779 undefined,
780 undefined,
781 undefined,
782
783 {
784 type: 'flickr_album',
785 source: 'flickr',
786 playerUri: 'https://embedr.flickr.com/groups/898944@N23',
787 },
788 {
789 type: 'flickr_album',
790 source: 'flickr',
791 playerUri: 'https://embedr.flickr.com/groups/898944@N23',
792 },
793 {
794 type: 'flickr_album',
795 source: 'flickr',
796 playerUri: 'https://embedr.flickr.com/groups/898944@N23',
797 },
798 {
799 type: 'flickr_album',
800 source: 'flickr',
801 playerUri: 'https://embedr.flickr.com/groups/898944@N23',
802 },
803 {
804 type: 'flickr_album',
805 source: 'flickr',
806 playerUri: 'https://embedr.flickr.com/groups/898944@N23',
807 },
808
809 undefined,
810 undefined,
811 ]
812
813 it('correctly grabs the correct id from uri', () => {
814 for (let i = 0; i < inputs.length; i++) {
815 const input = inputs[i]
816 const output = outputs[i]
817
818 const res = parseEmbedPlayerFromUrl(input)
819
820 expect(res).toEqual(output)
821 }
822 })
823})
824
825describe('createStarterPackLinkFromAndroidReferrer', () => {
826 const validOutput = 'at://haileyok.com/app.bsky.graph.starterpack/rkey'
827
828 it('returns a link when input contains utm_source and utm_content', () => {
829 expect(
830 createStarterPackLinkFromAndroidReferrer(
831 'utm_source=bluesky&utm_content=starterpack_haileyok.com_rkey',
832 ),
833 ).toEqual(validOutput)
834
835 expect(
836 createStarterPackLinkFromAndroidReferrer(
837 'utm_source=bluesky&utm_content=starterpack_test-lover-9000.com_rkey',
838 ),
839 ).toEqual('at://test-lover-9000.com/app.bsky.graph.starterpack/rkey')
840 })
841
842 it('returns a link when input contains utm_source and utm_content in different order', () => {
843 expect(
844 createStarterPackLinkFromAndroidReferrer(
845 'utm_content=starterpack_haileyok.com_rkey&utm_source=bluesky',
846 ),
847 ).toEqual(validOutput)
848 })
849
850 it('returns a link when input contains other parameters as well', () => {
851 expect(
852 createStarterPackLinkFromAndroidReferrer(
853 'utm_source=bluesky&utm_medium=starterpack&utm_content=starterpack_haileyok.com_rkey',
854 ),
855 ).toEqual(validOutput)
856 })
857
858 it('returns null when utm_source is not present', () => {
859 expect(
860 createStarterPackLinkFromAndroidReferrer(
861 'utm_content=starterpack_haileyok.com_rkey',
862 ),
863 ).toEqual(null)
864 })
865
866 it('returns null when utm_content is not present', () => {
867 expect(
868 createStarterPackLinkFromAndroidReferrer('utm_source=bluesky'),
869 ).toEqual(null)
870 })
871
872 it('returns null when utm_content is malformed', () => {
873 expect(
874 createStarterPackLinkFromAndroidReferrer(
875 'utm_content=starterpack_haileyok.com',
876 ),
877 ).toEqual(null)
878
879 expect(
880 createStarterPackLinkFromAndroidReferrer('utm_content=starterpack'),
881 ).toEqual(null)
882
883 expect(
884 createStarterPackLinkFromAndroidReferrer(
885 'utm_content=starterpack_haileyok.com_rkey_more',
886 ),
887 ).toEqual(null)
888
889 expect(
890 createStarterPackLinkFromAndroidReferrer(
891 'utm_content=notastarterpack_haileyok.com_rkey',
892 ),
893 ).toEqual(null)
894 })
895})
896
897describe('parseStarterPackHttpUri', () => {
898 const baseUri = 'https://bsky.app/start'
899
900 it('returns a valid at uri when http uri is valid', () => {
901 const validHttpUri = `${baseUri}/haileyok.com/rkey`
902 expect(parseStarterPackUri(validHttpUri)).toEqual({
903 name: 'haileyok.com',
904 rkey: 'rkey',
905 })
906
907 const validHttpUri2 = `${baseUri}/haileyok.com/ilovetesting`
908 expect(parseStarterPackUri(validHttpUri2)).toEqual({
909 name: 'haileyok.com',
910 rkey: 'ilovetesting',
911 })
912
913 const validHttpUri3 = `${baseUri}/testlover9000.com/rkey`
914 expect(parseStarterPackUri(validHttpUri3)).toEqual({
915 name: 'testlover9000.com',
916 rkey: 'rkey',
917 })
918 })
919
920 it('returns null when there is no rkey', () => {
921 const validHttpUri = `${baseUri}/haileyok.com`
922 expect(parseStarterPackUri(validHttpUri)).toEqual(null)
923 })
924
925 it('returns null when there is an extra path', () => {
926 const validHttpUri = `${baseUri}/haileyok.com/rkey/other`
927 expect(parseStarterPackUri(validHttpUri)).toEqual(null)
928 })
929
930 it('returns null when there is no handle or rkey', () => {
931 const validHttpUri = `${baseUri}`
932 expect(parseStarterPackUri(validHttpUri)).toEqual(null)
933 })
934
935 it('returns null when the route is not /start or /starter-pack', () => {
936 const validHttpUri = 'https://bsky.app/start/haileyok.com/rkey'
937 expect(parseStarterPackUri(validHttpUri)).toEqual({
938 name: 'haileyok.com',
939 rkey: 'rkey',
940 })
941
942 const validHttpUri2 = 'https://bsky.app/starter-pack/haileyok.com/rkey'
943 expect(parseStarterPackUri(validHttpUri2)).toEqual({
944 name: 'haileyok.com',
945 rkey: 'rkey',
946 })
947
948 const invalidHttpUri = 'https://bsky.app/profile/haileyok.com/rkey'
949 expect(parseStarterPackUri(invalidHttpUri)).toEqual(null)
950 })
951
952 it('returns the at uri when the input is a valid starterpack at uri', () => {
953 const validAtUri = 'at://did:123/app.bsky.graph.starterpack/rkey'
954 expect(parseStarterPackUri(validAtUri)).toEqual({
955 name: 'did:123',
956 rkey: 'rkey',
957 })
958 })
959
960 it('returns null when the at uri has no rkey', () => {
961 const validAtUri = 'at://did:123/app.bsky.graph.starterpack'
962 expect(parseStarterPackUri(validAtUri)).toEqual(null)
963 })
964
965 it('returns null when the collection is not app.bsky.graph.starterpack', () => {
966 const validAtUri = 'at://did:123/app.bsky.graph.list/rkey'
967 expect(parseStarterPackUri(validAtUri)).toEqual(null)
968 })
969
970 it('returns null when the input is undefined', () => {
971 expect(parseStarterPackUri(undefined)).toEqual(null)
972 })
973})
974
975describe('createStarterPackGooglePlayUri', () => {
976 const base =
977 'https://play.google.com/store/apps/details?id=xyz.blueskyweb.app&referrer=utm_source%3Dbluesky%26utm_medium%3Dstarterpack%26utm_content%3Dstarterpack_'
978
979 it('returns valid google play uri when input is valid', () => {
980 expect(createStarterPackGooglePlayUri('name', 'rkey')).toEqual(
981 `${base}name_rkey`,
982 )
983 })
984
985 it('returns null when no rkey is supplied', () => {
986 // @ts-expect-error test
987 expect(createStarterPackGooglePlayUri('name', undefined)).toEqual(null)
988 })
989
990 it('returns null when no name or rkey are supplied', () => {
991 // @ts-expect-error test
992 expect(createStarterPackGooglePlayUri(undefined, undefined)).toEqual(null)
993 })
994
995 it('returns null when rkey is supplied but no name', () => {
996 // @ts-expect-error test
997 expect(createStarterPackGooglePlayUri(undefined, 'rkey')).toEqual(null)
998 })
999})