bewcloud/lib/utils/contacts_test.ts
2026-01-02 10:21:49 +00:00

772 lines
29 KiB
TypeScript

import { assertEquals, assertMatch } from '@std/assert';
import { generateVCard, getIdFromVCard, parseVCard, splitTextIntoVCards, updateVCard } from './contacts.ts';
Deno.test('that getIdFromVCard works', () => {
const tests: { input: string; expected?: string; shouldBeUUID?: boolean }[] = [
{
input: `BEGIN:VCARD
VERSION:4.0
UID:12345-abcde-67890
FN:John Doe
END:VCARD`,
expected: '12345-abcde-67890',
},
{
input: `BEGIN:VCARD
VERSION:3.0
FN:Jane Smith
UID:jane-smith-uuid
EMAIL:jane@example.com
END:VCARD`,
expected: 'jane-smith-uuid',
},
{
input: `BEGIN:VCARD
VERSION:4.0
FN:No UID Contact
EMAIL:nouid@example.com
END:VCARD`,
shouldBeUUID: true,
},
{
input: `BEGIN:VCARD
VERSION:4.0
UID: spaced-uid
FN:Spaced UID
END:VCARD`,
expected: 'spaced-uid',
},
];
for (const test of tests) {
const output = getIdFromVCard(test.input);
if (test.expected) {
assertEquals(output, test.expected);
} else if (test.shouldBeUUID) {
// Check that it's a valid UUID format
assertMatch(output, /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i);
}
}
});
Deno.test('that splitTextIntoVCards works', () => {
const tests: { input: string; expected: string[] }[] = [
{
input: `BEGIN:VCARD
VERSION:4.0
UID:1
FN:John Doe
END:VCARD
BEGIN:VCARD
VERSION:4.0
UID:2
FN:Jane Smith
END:VCARD`,
expected: [
`BEGIN:VCARD
VERSION:4.0
UID:1
FN:John Doe
END:VCARD`,
`BEGIN:VCARD
VERSION:4.0
UID:2
FN:Jane Smith
END:VCARD`,
],
},
{
input: `BEGIN:VCARD
VERSION:3.0
FN:Single Contact
EMAIL:single@example.com
END:VCARD`,
expected: [
`BEGIN:VCARD
VERSION:3.0
FN:Single Contact
EMAIL:single@example.com
END:VCARD`,
],
},
{
input: '',
expected: [],
},
{
input: `BEGIN:VCARD
VERSION:4.0
FN:Incomplete Contact`,
expected: [],
},
{
input: `BEGIN:VCARD
VERSION:3.0
FN:Single Contact
EMAIL:single@example.com
PHOTO;VALUE=URI:data:image/jpeg;base64\,/9j/4AAQSkZJRgABAQAAAQABAAD/4gIoSUN
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
//9k=
END:VCARD`,
expected: [
`BEGIN:VCARD
VERSION:3.0
FN:Single Contact
EMAIL:single@example.com
PHOTO;VALUE=URI:data:image/jpeg;base64\,/9j/4AAQSkZJRgABAQAAAQABAAD/4gIoSUN
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
//9k=
END:VCARD`,
],
},
];
for (const test of tests) {
const output = splitTextIntoVCards(test.input);
assertEquals(output, test.expected);
}
});
Deno.test('that generateVCard works', () => {
const tests: { input: { contactId: string; firstName: string; lastName?: string }; expected: string }[] = [
{
input: { contactId: 'test-123', firstName: 'John', lastName: 'Doe' },
expected: `BEGIN:VCARD
VERSION:4.0
N:Doe;John;
FN:John Doe
UID:test-123
END:VCARD`,
},
{
input: { contactId: 'single-name', firstName: 'Madonna' },
expected: `BEGIN:VCARD
VERSION:4.0
N:;Madonna;
FN:Madonna
UID:single-name
END:VCARD`,
},
{
input: { contactId: 'special-chars', firstName: 'John,Test', lastName: 'Doe\nSmith' },
expected: `BEGIN:VCARD
VERSION:4.0
N:Doe\\nSmith;John\\,Test;
FN:John\\,Test Doe\\nSmith
UID:special-chars
END:VCARD`,
},
];
for (const test of tests) {
const output = generateVCard(test.input.contactId, test.input.firstName, test.input.lastName);
assertEquals(output, test.expected);
}
});
Deno.test('that updateVCard works', () => {
const tests: {
input: {
vCard: string;
updates: {
firstName?: string;
lastName?: string;
email?: string;
phone?: string;
notes?: string;
};
};
expected: string;
}[] = [
{
input: {
vCard: `BEGIN:VCARD
VERSION:4.0
UID:test-123
N:Doe;John;
FN:John Doe
END:VCARD`,
updates: { firstName: 'Jane', lastName: 'Smith' },
},
expected: `BEGIN:VCARD
VERSION:4.0
UID:test-123
N:Smith;Jane;
FN:Jane Smith
END:VCARD`,
},
{
input: {
vCard: `BEGIN:VCARD
VERSION:4.0
UID:test-456
N:Doe;John;
FN:John Doe
EMAIL:old@example.com
TEL:+1234567890
END:VCARD`,
updates: { email: 'new@example.com', phone: '+9876543210' },
},
expected: `BEGIN:VCARD
VERSION:4.0
UID:test-456
N:Doe;John;
FN:John Doe
EMAIL:new@example.com
TEL:+9876543210
END:VCARD`,
},
{
input: {
vCard: `BEGIN:VCARD
VERSION:4.0
UID:test-789
N:Doe;John;
FN:John Doe
END:VCARD`,
updates: { email: 'added@example.com', phone: '+1111111111', notes: 'Test notes' },
},
expected: `BEGIN:VCARD
VERSION:4.0
UID:test-789
N:Doe;John;
FN:John Doe
EMAIL;TYPE=HOME:added@example.com
TEL;TYPE=HOME:+1111111111
NOTE:Test notes
END:VCARD`,
},
{
input: {
vCard: `BEGIN:VCARD
VERSION:4.0
UID:test-special
N:Doe;John;
FN:John Doe
NOTE:Old notes
END:VCARD`,
updates: { notes: 'New notes\nwith newlines, and commas' },
},
expected: `BEGIN:VCARD
VERSION:4.0
UID:test-special
N:Doe;John;
FN:John Doe
NOTE:New notes\\nwith newlines\\, and commas
END:VCARD`,
},
{
input: {
vCard: `BEGIN:VCARD
VERSION:4.0
UID:test-carriage
N:Doe;John;
FN:John Doe
END:VCARD`,
updates: { notes: 'Notes with\r\ncarriage returns' },
},
expected: `BEGIN:VCARD
VERSION:4.0
UID:test-carriage
N:Doe;John;
FN:John Doe
NOTE:Notes with\\ncarriage returns
END:VCARD`,
},
];
for (const test of tests) {
const output = updateVCard(test.input.vCard, test.input.updates);
assertEquals(output, test.expected);
}
});
Deno.test('that parseVCard works', () => {
const tests: {
input: string;
expected: Array<{
uid?: string;
firstName?: string;
lastName?: string;
middleNames?: string[];
title?: string;
email?: string;
phone?: string;
notes?: string;
}>;
}[] = [
{
input: `BEGIN:VCARD
VERSION:4.0
UID:test-123
N:Doe;John;Middle;Jr
FN:John Middle Doe Jr
EMAIL;TYPE=HOME:john@example.com
TEL;TYPE=HOME:+1234567890
NOTE:Test contact notes
END:VCARD`,
expected: [{
uid: 'test-123',
firstName: 'John',
lastName: 'Doe',
middleNames: ['Middle'],
title: 'Jr',
email: 'john@example.com',
phone: '+1234567890',
notes: 'Test contact notes',
}],
},
{
input: `BEGIN:VCARD
VERSION:3.0
UID:test-456
N:Smith;Jane;;
FN:Jane Smith
EMAIL:jane@example.com
TEL:+9876543210
END:VCARD`,
expected: [{
uid: 'test-456',
firstName: 'Jane',
lastName: 'Smith',
middleNames: [],
title: '',
email: 'jane@example.com',
phone: '+9876543210',
}],
},
{
input: `BEGIN:VCARD
VERSION:4.0
UID:multi-1
N:Doe;John;
FN:John Doe
END:VCARD
BEGIN:VCARD
VERSION:4.0
UID:multi-2
N:Smith;Jane;
FN:Jane Smith
EMAIL;PREF=1:jane@example.com
END:VCARD`,
expected: [
{
uid: 'multi-1',
firstName: 'John',
lastName: 'Doe',
middleNames: [],
title: '',
},
{
uid: 'multi-2',
firstName: 'Jane',
lastName: 'Smith',
middleNames: [],
title: '',
email: 'jane@example.com',
},
],
},
{
input: `BEGIN:VCARD
VERSION:4.0
UID:escaped-contact
N:Test;Contact;
FN:Contact Test
NOTE:Notes with\\nescaped newlines\\, and commas
END:VCARD`,
expected: [{
uid: 'escaped-contact',
firstName: 'Contact',
lastName: 'Test',
middleNames: [],
title: '',
notes: 'Notes with\nescaped newlines, and commas',
}],
},
{
input: `BEGIN:VCARD
VERSION:2.1
UID:version-21
N:Old;Format;
FN:Format Old
EMAIL:old@example.com
TEL:+1111111111
END:VCARD`,
expected: [{
uid: 'version-21',
firstName: 'Format',
lastName: 'Old',
middleNames: [],
title: '',
email: 'old@example.com',
phone: '+1111111111',
}],
},
{
input: `BEGIN:VCARD
VERSION:4.0
UID:email-variations
N:Test;Email;
FN:Email Test
EMAIL:direct@example.com
EMAIL;TYPE=WORK:work@example.com
TEL:+1234567890
TEL;TYPE=WORK:+9876543210
END:VCARD`,
expected: [{
uid: 'email-variations',
firstName: 'Email',
lastName: 'Test',
middleNames: [],
title: '',
email: 'direct@example.com', // Only first email is captured
phone: '+1234567890', // Only first phone is captured
}],
},
{
input: `BEGIN:VCARD
VERSION:5.0
UID:invalid-version
N:Invalid;Version;
FN:Version Invalid
END:VCARD`,
expected: [{
uid: 'invalid-version',
firstName: 'Version',
lastName: 'Invalid',
middleNames: [],
title: '',
}],
},
{
input: `BEGIN:VCARD
VERSION:4.0
UID:no-first-name
N:LastOnly;;
FN:LastOnly
END:VCARD`,
expected: [{ uid: 'no-first-name' }],
},
{
input: `BEGIN:VCARD
VERSION:4.0
UID:empty-uid
N:Test;Empty;
FN:Empty Test
END:VCARD`,
expected: [{
firstName: 'Empty',
lastName: 'Test',
middleNames: [],
title: '',
uid: 'empty-uid',
}],
},
];
for (const test of tests) {
const output = parseVCard(test.input);
assertEquals(output, test.expected);
}
});
Deno.test('that parseVCard handles edge cases', () => {
const edgeCases: { input: string; description: string; expected: any[] }[] = [
{
input: '',
description: 'empty string',
expected: [],
},
{
input: 'Not a vCard at all',
description: 'invalid format',
expected: [],
},
{
input: `BEGIN:VCARD
VERSION:4.0
UID:incomplete`,
description: 'incomplete vCard without END',
expected: [],
},
{
input: `BEGIN:VCARD
VERSION:4.0
UID:missing-required
FN:Missing Required Fields
END:VCARD`,
description: 'vCard without N field',
expected: [{ uid: 'missing-required' }],
},
{
input: `BEGIN:VCARD
VERSION:4.0
UID:empty-fields
N:;;;
FN:Empty Fields
EMAIL:
TEL:
NOTE:
END:VCARD`,
description: 'vCard with empty field values',
expected: [{ uid: 'empty-fields', notes: '' }],
},
];
for (const test of edgeCases) {
const output = parseVCard(test.input);
assertEquals(output, test.expected, `Failed for: ${test.description}`);
}
});