Sifa professional network API (Fastify, AT Protocol, Jetstream)
sifa.id/
1import { describe, expect, it } from 'vitest';
2import { normalizeUrl, extractPlatformUsername } from '../../src/lib/url-utils.js';
3
4describe('url-utils', () => {
5 describe('normalizeUrl', () => {
6 it('strips https protocol', () => {
7 expect(normalizeUrl('https://github.com/gxjansen')).toBe('github.com/gxjansen');
8 });
9
10 it('strips http protocol', () => {
11 expect(normalizeUrl('http://github.com/gxjansen')).toBe('github.com/gxjansen');
12 });
13
14 it('strips trailing slash', () => {
15 expect(normalizeUrl('https://github.com/gxjansen/')).toBe('github.com/gxjansen');
16 });
17
18 it('strips www prefix', () => {
19 expect(normalizeUrl('https://www.linkedin.com/in/gxjansen')).toBe('linkedin.com/in/gxjansen');
20 });
21
22 it('lowercases the URL', () => {
23 expect(normalizeUrl('https://GitHub.COM/GxJansen')).toBe('github.com/gxjansen');
24 });
25
26 it('handles multiple normalizations together', () => {
27 expect(normalizeUrl('HTTP://WWW.GitHub.Com/GxJansen/')).toBe('github.com/gxjansen');
28 });
29
30 it('handles URLs without protocol', () => {
31 expect(normalizeUrl('github.com/gxjansen')).toBe('github.com/gxjansen');
32 });
33
34 it('handles empty string', () => {
35 expect(normalizeUrl('')).toBe('');
36 });
37
38 it('strips multiple trailing slashes', () => {
39 expect(normalizeUrl('https://example.com///')).toBe('example.com');
40 });
41 });
42
43 describe('extractPlatformUsername', () => {
44 it('extracts GitHub username', () => {
45 expect(extractPlatformUsername('github', 'https://github.com/gxjansen')).toBe('gxjansen');
46 });
47
48 it('extracts GitHub username with trailing slash', () => {
49 expect(extractPlatformUsername('github', 'https://github.com/gxjansen/')).toBe('gxjansen');
50 });
51
52 it('extracts LinkedIn username', () => {
53 expect(extractPlatformUsername('linkedin', 'https://www.linkedin.com/in/gxjansen')).toBe(
54 'gxjansen',
55 );
56 });
57
58 it('extracts LinkedIn username with trailing slash', () => {
59 expect(extractPlatformUsername('linkedin', 'https://linkedin.com/in/gxjansen/')).toBe(
60 'gxjansen',
61 );
62 });
63
64 it('extracts Twitter/X username from twitter.com', () => {
65 expect(extractPlatformUsername('twitter', 'https://twitter.com/guido')).toBe('guido');
66 });
67
68 it('extracts Twitter/X username from x.com', () => {
69 expect(extractPlatformUsername('twitter', 'https://x.com/guido')).toBe('guido');
70 });
71
72 it('extracts Instagram username', () => {
73 expect(extractPlatformUsername('instagram', 'https://instagram.com/guido')).toBe('guido');
74 });
75
76 it('extracts YouTube channel ID', () => {
77 expect(extractPlatformUsername('youtube', 'https://youtube.com/channel/UC1234abc')).toBe(
78 'UC1234abc',
79 );
80 });
81
82 it('extracts YouTube custom handle', () => {
83 expect(extractPlatformUsername('youtube', 'https://youtube.com/@guido')).toBe('@guido');
84 });
85
86 it('extracts Fediverse username from URL', () => {
87 expect(extractPlatformUsername('fediverse', 'https://mastodon.social/@guido')).toBe(
88 '@guido@mastodon.social',
89 );
90 });
91
92 it('returns null for unknown platform', () => {
93 expect(extractPlatformUsername('other', 'https://example.com/user')).toBeNull();
94 });
95
96 it('returns null for malformed URL', () => {
97 expect(extractPlatformUsername('github', 'not-a-url')).toBeNull();
98 });
99
100 it('returns null for GitHub URL without username path', () => {
101 expect(extractPlatformUsername('github', 'https://github.com')).toBeNull();
102 });
103
104 it('is case-insensitive for platform matching', () => {
105 expect(extractPlatformUsername('GitHub', 'https://github.com/gxjansen')).toBe('gxjansen');
106 });
107 });
108});