1 import { extractMergeable } from '@proton/shared/lib/contacts/helpers/merge';
2 import type { FormattedContact } from '@proton/shared/lib/interfaces/contacts/FormattedContact';
4 type TestContact = Pick<FormattedContact, 'Name' | 'emails'>;
7 * Check if two contacts match
8 * @dev For this test, for contact equality it's enough to check that the 'Name' and 'emails' properties match
10 const doContactsMatch = (contactLeft: TestContact, contactRight: TestContact) => {
11 const { Name: nameLeft, emails: emailsLeft } = contactLeft;
12 const { Name: nameRight, emails: emailsRight } = contactRight;
14 if (nameLeft !== nameRight) {
17 return emailsLeft.length === emailsRight.length && emailsLeft.every((email, i) => email === emailsRight[i]);
21 * Check if two arrays of contacts contain the same list of contacts, possibly in a different order
23 const doContactListsMatch = (contactsLeft: TestContact[], contactsRight: TestContact[]) => {
24 const differentContactsRight = [...contactsRight];
26 for (const contactLeft of contactsLeft) {
27 const index = differentContactsRight.findIndex((contact) => doContactsMatch(contact, contactLeft));
32 differentContactsRight.splice(index, 1);
35 return !differentContactsRight.length;
39 * Check if two arrays of contacts lists match, possibly in a different order
41 const doContactListArraysMatch = (contactListsLeft: TestContact[][], contactListsRight: TestContact[][]) => {
42 const differentContactListsRight = [...contactListsRight];
44 for (const contactListLeft of contactListsLeft) {
45 const index = differentContactListsRight.findIndex((contactList) =>
46 doContactListsMatch(contactList, contactListLeft)
52 differentContactListsRight.splice(index, 1);
55 return !differentContactListsRight.length;
58 describe('merge', () => {
59 describe('extractMergeable', () => {
60 it('should detect as mergeable multiple contacts with the same normalized name and same normalized email', () => {
61 // only names and emails are relevant for the logic of extractMergeable
63 { Name: 'TestName', emails: ['testname@pm.me', 'TestName@protonmail.com'] },
64 { Name: 'Someone else', emails: ['else@proton.me'] },
65 { Name: 'TESTNAME', emails: ['TESTNAME@proton.me'] },
66 { Name: 'testname', emails: ['testname@pm.me'] },
67 { Name: 'Party crasher', emails: ['party_crasher@proton.me'] },
68 { Name: 'TestEmail', emails: ['testemail@pm.me', 'TestEmail@protonmail.com'] },
69 { Name: 'Another one', emails: ['another@proton.me'] },
70 { Name: 'I am testing email', emails: ['TESTEMAIL@pm.me'] },
71 { Name: 'Party crasher friend 1', emails: ['party_crasher_friend_1@proton.me'] },
72 { Name: 'Party crasher friend 2', emails: ['party_crasher_friend_2@proton.me'] },
73 { Name: 'A final email test', emails: ['Testemail@protonmail.com', 'another@pm.me'] },
74 ] as FormattedContact[];
75 const mergeableContacts = extractMergeable(contacts);
76 const expectedMergeableContacts = [
78 [contacts[0], contacts[2], contacts[3]],
80 [contacts[5], contacts[7], contacts[10]],
83 expect(doContactListArraysMatch(mergeableContacts, expectedMergeableContacts)).toEqual(true);
86 it('should detect as mergeable two contacts with different names and emails, but which share a name and an email with a third one', () => {
87 // only names and emails are relevant for the logic of extractMergeable
89 { Name: 'First', emails: ['first@pm.me'] },
90 { Name: 'Second', emails: ['second@proton.me', 'first@pm.me'] },
91 { Name: 'second', emails: ['third@proton.me'] },
92 ] as FormattedContact[];
93 const mergeableContacts = extractMergeable(contacts);
95 expect(doContactListArraysMatch(mergeableContacts, [contacts])).toEqual(true);
98 it('should not detect as mergeable two contacts with unknown names added by Proton', () => {
99 // only names and emails are relevant for the logic of extractMergeable
101 { Name: 'Unknown', emails: ['first@pm.me'] },
102 { Name: 'Unknown', emails: ['second@proton.me', 'another@pm.me'] },
103 { Name: '<Unknown>', emails: ['third@proton.me'] },
104 { Name: '<Unknown>', emails: ['fourth@proton.me'] },
105 ] as FormattedContact[];
106 const mergeableContacts = extractMergeable(contacts);
108 expect(doContactListArraysMatch(mergeableContacts, [])).toEqual(true);