cozy-sharing 33.1.0 → 33.1.2

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.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,19 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [33.1.2](https://github.com/cozy/cozy-libs/compare/cozy-sharing@33.1.1...cozy-sharing@33.1.2) (2026-06-08)
7
+
8
+ ### Bug Fixes
9
+
10
+ - **cozy-sharing:** Avoid regex parsing in input matchers ([ecab525](https://github.com/cozy/cozy-libs/commit/ecab525b7559710d693787fb50c6c5fe8abd55ac))
11
+ - **cozy-sharing:** Make matching diacritic-insensitive ([e421a5a](https://github.com/cozy/cozy-libs/commit/e421a5ac5f97f64ef27340a3b87f9f15b3efebd9))
12
+
13
+ ## [33.1.1](https://github.com/cozy/cozy-libs/compare/cozy-sharing@33.1.0...cozy-sharing@33.1.1) (2026-06-04)
14
+
15
+ ### Bug Fixes
16
+
17
+ - **cozy-sharing:** Memoize ShareByEmail section in SharedDriveForm ([2ea5eb2](https://github.com/cozy/cozy-libs/commit/2ea5eb2a8d37eec582212b07b8617c36fd107930))
18
+
6
19
  # [33.1.0](https://github.com/cozy/cozy-libs/compare/cozy-sharing@33.0.2...cozy-sharing@33.1.0) (2026-06-04)
7
20
 
8
21
  ### Features
@@ -1,5 +1,5 @@
1
1
  import PropTypes from 'prop-types';
2
- import React from 'react';
2
+ import React, { memo } from 'react';
3
3
  import Button from 'cozy-ui/transpiled/react/Buttons';
4
4
  import TextField from 'cozy-ui/transpiled/react/TextField';
5
5
  import Typography from 'cozy-ui/transpiled/react/Typography';
@@ -9,9 +9,23 @@ import withLocales from '../../hoc/withLocales';
9
9
  import { default as DumbShareByEmail } from '../ShareByEmail';
10
10
  import SharedDriveHeader from './SharedDriveHeader';
11
11
  import { useSharedDrive } from './useSharedDrive';
12
- export var SharedDriveForm = withLocales(function (_ref) {
13
- var onSuccess = _ref.onSuccess,
14
- onCancel = _ref.onCancel;
12
+ var ShareByEmailSection = /*#__PURE__*/memo(function ShareByEmailSection(_ref) {
13
+ var pendingRecipients = _ref.pendingRecipients,
14
+ setPendingRecipients = _ref.setPendingRecipients,
15
+ selectedOption = _ref.selectedOption,
16
+ setSelectedOption = _ref.setSelectedOption;
17
+ return /*#__PURE__*/React.createElement(DumbShareByEmail, {
18
+ currentRecipients: [],
19
+ documentType: "Files",
20
+ pendingRecipients: pendingRecipients,
21
+ onPendingRecipientsChange: setPendingRecipients,
22
+ selectedOption: selectedOption,
23
+ onSelectedOptionChange: setSelectedOption
24
+ });
25
+ });
26
+ export var SharedDriveForm = withLocales(function (_ref2) {
27
+ var onSuccess = _ref2.onSuccess,
28
+ onCancel = _ref2.onCancel;
15
29
 
16
30
  var _useI18n = useI18n(),
17
31
  t = _useI18n.t;
@@ -44,13 +58,11 @@ export var SharedDriveForm = withLocales(function (_ref) {
44
58
  }), /*#__PURE__*/React.createElement(Typography, {
45
59
  variant: "h6",
46
60
  className: "u-mb-half"
47
- }, t('Share.contacts.addUsers')), /*#__PURE__*/React.createElement(DumbShareByEmail, {
48
- currentRecipients: [],
49
- documentType: "Files",
61
+ }, t('Share.contacts.addUsers')), /*#__PURE__*/React.createElement(ShareByEmailSection, {
50
62
  pendingRecipients: pendingRecipients,
51
- onPendingRecipientsChange: setPendingRecipients,
63
+ setPendingRecipients: setPendingRecipients,
52
64
  selectedOption: selectedOption,
53
- onSelectedOptionChange: setSelectedOption
65
+ setSelectedOption: setSelectedOption
54
66
  })), /*#__PURE__*/React.createElement("div", {
55
67
  className: "u-flex u-ph-2 u-pv-1"
56
68
  }, /*#__PURE__*/React.createElement(Button, {
@@ -1,43 +1,64 @@
1
- import escapeRegExp from 'lodash/escapeRegExp';
2
- import { Group } from 'cozy-doctypes'; // TODO: sadly we have different versions of contacts' doctype to handle...
1
+ import { Group } from 'cozy-doctypes';
2
+ var removeDiacritics;
3
+
4
+ try {
5
+ new RegExp('\\p{Diacritic}', 'gu');
6
+
7
+ removeDiacritics = function removeDiacritics(str) {
8
+ return str.replace(/(?:[\^`\xA8\xAF\xB4\xB7\xB8\u02B0-\u034E\u0350-\u0357\u035D-\u0362\u0374\u0375\u037A\u0384\u0385\u0483-\u0487\u0559\u0591-\u05A1\u05A3-\u05BD\u05BF\u05C1\u05C2\u05C4\u064B-\u0652\u0657\u0658\u06DF\u06E0\u06E5\u06E6\u06EA-\u06EC\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F5\u0818\u0819\u0898-\u089F\u08C9-\u08D2\u08E3-\u08FE\u093C\u094D\u0951-\u0954\u0971\u09BC\u09CD\u0A3C\u0A4D\u0ABC\u0ACD\u0AFD-\u0AFF\u0B3C\u0B4D\u0B55\u0BCD\u0C3C\u0C4D\u0CBC\u0CCD\u0D3B\u0D3C\u0D4D\u0DCA\u0E3A\u0E47-\u0E4C\u0E4E\u0EBA\u0EC8-\u0ECC\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F82-\u0F84\u0F86\u0F87\u0FC6\u1037\u1039\u103A\u1063\u1064\u1069-\u106D\u1087-\u108D\u108F\u109A\u109B\u135D-\u135F\u1714\u1715\u1734\u17C9-\u17D3\u17DD\u1939-\u193B\u1A60\u1A75-\u1A7C\u1A7F\u1AB0-\u1ABE\u1AC1-\u1ACB\u1B34\u1B44\u1B6B-\u1B73\u1BAA\u1BAB\u1BE6\u1BF2\u1BF3\u1C36\u1C37\u1C78-\u1C7D\u1CD0-\u1CE8\u1CED\u1CF4\u1CF7-\u1CF9\u1D2C-\u1D6A\u1DC4-\u1DCF\u1DF5-\u1DFF\u1FBD\u1FBF-\u1FC1\u1FCD-\u1FCF\u1FDD-\u1FDF\u1FED-\u1FEF\u1FFD\u1FFE\u2CEF-\u2CF1\u2E2F\u302A-\u302F\u3099-\u309C\u30FC\uA66F\uA67C\uA67D\uA67F\uA69C\uA69D\uA6F0\uA6F1\uA700-\uA721\uA788-\uA78A\uA7F8\uA7F9\uA806\uA82C\uA8C4\uA8E0-\uA8F1\uA92B-\uA92E\uA953\uA9B3\uA9C0\uA9E5\uAA7B-\uAA7D\uAABF-\uAAC2\uAAF6\uAB5B-\uAB5F\uAB69-\uAB6B\uABEC\uABED\uFB1E\uFE20-\uFE2F\uFF3E\uFF40\uFF70\uFF9E\uFF9F\uFFE3]|\uD800\uDEE0|\uD801[\uDF80-\uDF85\uDF87-\uDFB0\uDFB2-\uDFBA]|\uD802[\uDE38-\uDE3A\uDE3F\uDEE5\uDEE6]|\uD803[\uDD22-\uDD27\uDD4E\uDD69-\uDD6D\uDEFD-\uDEFF\uDF46-\uDF50\uDF82-\uDF85]|\uD804[\uDC46\uDC70\uDCB9\uDCBA\uDD33\uDD34\uDD73\uDDC0\uDDCA-\uDDCC\uDE35\uDE36\uDEE9\uDEEA\uDF3B\uDF3C\uDF4D\uDF66-\uDF6C\uDF70-\uDF74\uDFCE-\uDFD0\uDFD2\uDFD3\uDFE1\uDFE2]|\uD805[\uDC42\uDC46\uDCC2\uDCC3\uDDBF\uDDC0\uDE3F\uDEB6\uDEB7\uDF2B]|\uD806[\uDC39\uDC3A\uDD3D\uDD3E\uDD43\uDDE0\uDE34\uDE47\uDE99]|\uD807[\uDC3F\uDD42\uDD44\uDD45\uDD97\uDF41\uDF42\uDF5A]|\uD80D[\uDC47-\uDC55]|\uD818\uDD2F|\uD81A[\uDEF0-\uDEF4\uDF30-\uDF36]|\uD81B[\uDD6B\uDD6C\uDF8F-\uDF9F\uDFF0\uDFF1]|\uD82B[\uDFF0-\uDFF3\uDFF5-\uDFFB\uDFFD\uDFFE]|\uD833[\uDF00-\uDF2D\uDF30-\uDF46]|\uD834[\uDD67-\uDD69\uDD6D-\uDD72\uDD7B-\uDD82\uDD85-\uDD8B\uDDAA-\uDDAD]|\uD838[\uDC30-\uDC6D\uDD30-\uDD36\uDEAE\uDEEC-\uDEEF]|\uD839[\uDDEE\uDDEF]|\uD83A[\uDCD0-\uDCD6\uDD44-\uDD46\uDD48-\uDD4A])/g, '');
9
+ };
10
+ } catch (_unused) {
11
+ removeDiacritics = function removeDiacritics(str) {
12
+ return str.replace(/[̀-ͯ]/g, '');
13
+ };
14
+ }
15
+
16
+ var normalize = function normalize(str) {
17
+ return removeDiacritics(str.normalize('NFD'));
18
+ };
19
+
20
+ var normalizeLowercase = function normalizeLowercase(str) {
21
+ return normalize(str).toLowerCase();
22
+ }; // TODO: sadly we have different versions of contacts' doctype to handle...
3
23
  // A migration tool on the stack side is needed here
4
24
 
25
+
5
26
  var emailMatch = function emailMatch(input, contact) {
6
27
  if (!contact.email) return false;
7
- var emailInput = new RegExp(escapeRegExp(input), 'i');
28
+ var normalizedInput = normalizeLowercase(input);
8
29
 
9
30
  if (Array.isArray(contact.email)) {
10
31
  return contact.email.some(function (email) {
11
- return emailInput.test(email.address);
32
+ return normalizeLowercase(email.address).includes(normalizedInput);
12
33
  });
13
34
  }
14
35
 
15
- return emailInput.test(contact.email);
36
+ return normalizeLowercase(contact.email).includes(normalizedInput);
16
37
  };
17
38
 
18
39
  var cozyUrlMatch = function cozyUrlMatch(input, contact) {
19
40
  if (!contact.cozy && !contact.url) return false;
20
- var urlInput = new RegExp(escapeRegExp(input), 'i');
41
+ var normalizedInput = normalizeLowercase(input);
21
42
 
22
43
  if (contact.cozy && Array.isArray(contact.cozy)) {
23
44
  return contact.cozy.some(function (cozy) {
24
- return urlInput.test(cozy.url);
45
+ return normalizeLowercase(cozy.url).includes(normalizedInput);
25
46
  });
26
47
  }
27
48
 
28
- return urlInput.test(contact.url);
49
+ return normalizeLowercase(contact.url).includes(normalizedInput);
29
50
  };
30
51
 
31
52
  var groupNameMatch = function groupNameMatch(input, contactOrGroup) {
32
53
  if (contactOrGroup._type !== Group.doctype) return false;
33
- var nameInput = new RegExp(escapeRegExp(input), 'i');
34
- return nameInput.test(contactOrGroup.name);
54
+ var normalizedInput = normalizeLowercase(input);
55
+ return normalizeLowercase(contactOrGroup.name).includes(normalizedInput);
35
56
  };
36
57
 
37
58
  var fullnameMatch = function fullnameMatch(input, contact) {
38
59
  if (!contact.fullname) return false;
39
- var fullnameInput = new RegExp(escapeRegExp(input), 'i');
40
- return fullnameInput.test(contact.fullname);
60
+ var normalizedInput = normalizeLowercase(input);
61
+ return normalizeLowercase(contact.fullname).includes(normalizedInput);
41
62
  };
42
63
 
43
64
  var contactOrGroupMatch = function contactOrGroupMatch(input, contact) {
@@ -127,5 +127,108 @@ describe('Suggestion matchers', function () {
127
127
  expect(matchers.fullnameMatch('snow', contact)).toBeTruthy();
128
128
  expect(matchers.fullnameMatch('MARG', contact)).toBeTruthy();
129
129
  });
130
+ it('should match accented fullname with unaccented input', function () {
131
+ expect(matchers.fullnameMatch('zoe', {
132
+ fullname: 'Zoé Dupont'
133
+ })).toBeTruthy();
134
+ expect(matchers.fullnameMatch('elodie', {
135
+ fullname: 'Élodie Martin'
136
+ })).toBeTruthy();
137
+ expect(matchers.fullnameMatch('celine', {
138
+ fullname: 'Céline'
139
+ })).toBeTruthy();
140
+ });
141
+ it('should match unaccented fullname with accented input', function () {
142
+ expect(matchers.fullnameMatch('zoé', {
143
+ fullname: 'Zoe Dupont'
144
+ })).toBeTruthy();
145
+ });
146
+ it('should treat regex metacharacters as plain text', function () {
147
+ expect(function () {
148
+ return matchers.fullnameMatch('(', {
149
+ fullname: 'Jon Snow'
150
+ });
151
+ }).not.toThrow();
152
+ expect(matchers.fullnameMatch('(', {
153
+ fullname: 'Jon Snow'
154
+ })).toBeFalsy();
155
+ expect(matchers.fullnameMatch('.', {
156
+ fullname: 'Jon Snow'
157
+ })).toBeFalsy();
158
+ expect(matchers.fullnameMatch('.', {
159
+ fullname: 'Dr. House'
160
+ })).toBeTruthy();
161
+ });
162
+ });
163
+ describe('groupNameMatch', function () {
164
+ it('should match accented group name with unaccented input', function () {
165
+ var group = {
166
+ _type: 'io.cozy.contacts.groups',
167
+ name: 'Équipe'
168
+ };
169
+ expect(matchers.groupNameMatch('equipe', group)).toBeTruthy();
170
+ });
171
+ });
172
+ describe('Vietnamese diacritics support', function () {
173
+ var vietnameseContacts = [{
174
+ fullname: 'Nguyễn Văn Minh',
175
+ email: 'minh.nguyen@email.com'
176
+ }, {
177
+ fullname: 'Trần Thị Hương',
178
+ email: 'huong.tran@email.com'
179
+ }, {
180
+ fullname: 'Lê Hoàng Nam',
181
+ email: 'nam.hoang@email.com'
182
+ }, {
183
+ fullname: 'Phạm Đức Anh',
184
+ email: 'anh.pham@email.com',
185
+ cozy: [{
186
+ url: 'https://anh.mycozy.cloud'
187
+ }]
188
+ }];
189
+ it('should match Vietnamese accented name with unaccented input', function () {
190
+ expect(matchers.fullnameMatch('nguyen', vietnameseContacts[0])).toBeTruthy();
191
+ expect(matchers.fullnameMatch('van', vietnameseContacts[0])).toBeTruthy();
192
+ expect(matchers.fullnameMatch('minh', vietnameseContacts[0])).toBeTruthy();
193
+ expect(matchers.fullnameMatch('tran', vietnameseContacts[1])).toBeTruthy();
194
+ expect(matchers.fullnameMatch('huong', vietnameseContacts[1])).toBeTruthy();
195
+ expect(matchers.fullnameMatch('le', vietnameseContacts[2])).toBeTruthy();
196
+ expect(matchers.fullnameMatch('nam', vietnameseContacts[2])).toBeTruthy();
197
+ expect(matchers.fullnameMatch('pham', vietnameseContacts[3])).toBeTruthy();
198
+ expect(matchers.fullnameMatch('anh', vietnameseContacts[3])).toBeTruthy();
199
+ });
200
+ it('should match unaccented input with Vietnamese accented name', function () {
201
+ expect(matchers.fullnameMatch('nguyễn', vietnameseContacts[0])).toBeTruthy();
202
+ expect(matchers.fullnameMatch('văn', vietnameseContacts[0])).toBeTruthy();
203
+ expect(matchers.fullnameMatch('trần', vietnameseContacts[1])).toBeTruthy();
204
+ expect(matchers.fullnameMatch('hương', vietnameseContacts[1])).toBeTruthy();
205
+ expect(matchers.fullnameMatch('lê', vietnameseContacts[2])).toBeTruthy();
206
+ expect(matchers.fullnameMatch('phạm', vietnameseContacts[3])).toBeTruthy();
207
+ });
208
+ it('should match Vietnamese email with partial unaccented input', function () {
209
+ expect(matchers.emailMatch('minh', vietnameseContacts[0])).toBeTruthy();
210
+ expect(matchers.emailMatch('nguyen', vietnameseContacts[0])).toBeTruthy();
211
+ expect(matchers.emailMatch('huong', vietnameseContacts[1])).toBeTruthy();
212
+ expect(matchers.emailMatch('tran', vietnameseContacts[1])).toBeTruthy();
213
+ });
214
+ it('should match Vietnamese cozy URL with unaccented input', function () {
215
+ var contact = {
216
+ fullname: 'Đặng Minh Đức',
217
+ cozy: [{
218
+ url: 'https://duc.mycozy.cloud'
219
+ }]
220
+ };
221
+ expect(matchers.cozyUrlMatch('duc', contact)).toBeTruthy();
222
+ expect(matchers.cozyUrlMatch('mycozy', contact)).toBeTruthy();
223
+ });
224
+ it('should match Vietnamese group names with diacritics', function () {
225
+ var vietGroup = {
226
+ _type: 'io.cozy.contacts.groups',
227
+ name: 'Nhóm Việt'
228
+ };
229
+ expect(matchers.groupNameMatch('nhom', vietGroup)).toBeTruthy();
230
+ expect(matchers.groupNameMatch('viet', vietGroup)).toBeTruthy();
231
+ expect(matchers.groupNameMatch('nhóm', vietGroup)).toBeTruthy();
232
+ });
130
233
  });
131
234
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-sharing",
3
- "version": "33.1.0",
3
+ "version": "33.1.2",
4
4
  "description": "Provides sharing login for React applications.",
5
5
  "main": "dist/index.js",
6
6
  "author": "Cozy",
@@ -83,5 +83,5 @@
83
83
  "sideEffects": [
84
84
  "*.css"
85
85
  ],
86
- "gitHead": "9121c28a002700514566901dbab15401308bddbb"
86
+ "gitHead": "4f63c28ae3cd8cac22487e6640006de4f5c31cfc"
87
87
  }
@@ -1,5 +1,5 @@
1
1
  import PropTypes from 'prop-types'
2
- import React from 'react'
2
+ import React, { memo } from 'react'
3
3
 
4
4
  import Button from 'cozy-ui/transpiled/react/Buttons'
5
5
  import TextField from 'cozy-ui/transpiled/react/TextField'
@@ -12,6 +12,24 @@ import { default as DumbShareByEmail } from '../ShareByEmail'
12
12
  import SharedDriveHeader from './SharedDriveHeader'
13
13
  import { useSharedDrive } from './useSharedDrive'
14
14
 
15
+ const ShareByEmailSection = memo(function ShareByEmailSection({
16
+ pendingRecipients,
17
+ setPendingRecipients,
18
+ selectedOption,
19
+ setSelectedOption
20
+ }) {
21
+ return (
22
+ <DumbShareByEmail
23
+ currentRecipients={[]}
24
+ documentType="Files"
25
+ pendingRecipients={pendingRecipients}
26
+ onPendingRecipientsChange={setPendingRecipients}
27
+ selectedOption={selectedOption}
28
+ onSelectedOptionChange={setSelectedOption}
29
+ />
30
+ )
31
+ })
32
+
15
33
  export const SharedDriveForm = withLocales(({ onSuccess, onCancel }) => {
16
34
  const { t } = useI18n()
17
35
  const {
@@ -41,13 +59,11 @@ export const SharedDriveForm = withLocales(({ onSuccess, onCancel }) => {
41
59
  <Typography variant="h6" className="u-mb-half">
42
60
  {t('Share.contacts.addUsers')}
43
61
  </Typography>
44
- <DumbShareByEmail
45
- currentRecipients={[]}
46
- documentType="Files"
62
+ <ShareByEmailSection
47
63
  pendingRecipients={pendingRecipients}
48
- onPendingRecipientsChange={setPendingRecipients}
64
+ setPendingRecipients={setPendingRecipients}
49
65
  selectedOption={selectedOption}
50
- onSelectedOptionChange={setSelectedOption}
66
+ setSelectedOption={setSelectedOption}
51
67
  />
52
68
  </div>
53
69
  <div className="u-flex u-ph-2 u-pv-1">
@@ -1,37 +1,50 @@
1
- import escapeRegExp from 'lodash/escapeRegExp'
2
-
3
1
  import { Group } from 'cozy-doctypes'
4
2
 
3
+ let removeDiacritics
4
+ try {
5
+ new RegExp('\\p{Diacritic}', 'gu')
6
+ removeDiacritics = str => str.replace(/\p{Diacritic}/gu, '')
7
+ } catch {
8
+ removeDiacritics = str => str.replace(/[̀-ͯ]/g, '')
9
+ }
10
+
11
+ const normalize = str => removeDiacritics(str.normalize('NFD'))
12
+ const normalizeLowercase = str => normalize(str).toLowerCase()
13
+
5
14
  // TODO: sadly we have different versions of contacts' doctype to handle...
6
15
  // A migration tool on the stack side is needed here
7
16
  const emailMatch = (input, contact) => {
8
17
  if (!contact.email) return false
9
- const emailInput = new RegExp(escapeRegExp(input), 'i')
18
+ const normalizedInput = normalizeLowercase(input)
10
19
  if (Array.isArray(contact.email)) {
11
- return contact.email.some(email => emailInput.test(email.address))
20
+ return contact.email.some(email =>
21
+ normalizeLowercase(email.address).includes(normalizedInput)
22
+ )
12
23
  }
13
- return emailInput.test(contact.email)
24
+ return normalizeLowercase(contact.email).includes(normalizedInput)
14
25
  }
15
26
 
16
27
  const cozyUrlMatch = (input, contact) => {
17
28
  if (!contact.cozy && !contact.url) return false
18
- const urlInput = new RegExp(escapeRegExp(input), 'i')
29
+ const normalizedInput = normalizeLowercase(input)
19
30
  if (contact.cozy && Array.isArray(contact.cozy)) {
20
- return contact.cozy.some(cozy => urlInput.test(cozy.url))
31
+ return contact.cozy.some(cozy =>
32
+ normalizeLowercase(cozy.url).includes(normalizedInput)
33
+ )
21
34
  }
22
- return urlInput.test(contact.url)
35
+ return normalizeLowercase(contact.url).includes(normalizedInput)
23
36
  }
24
37
 
25
38
  const groupNameMatch = (input, contactOrGroup) => {
26
39
  if (contactOrGroup._type !== Group.doctype) return false
27
- const nameInput = new RegExp(escapeRegExp(input), 'i')
28
- return nameInput.test(contactOrGroup.name)
40
+ const normalizedInput = normalizeLowercase(input)
41
+ return normalizeLowercase(contactOrGroup.name).includes(normalizedInput)
29
42
  }
30
43
 
31
44
  const fullnameMatch = (input, contact) => {
32
45
  if (!contact.fullname) return false
33
- const fullnameInput = new RegExp(escapeRegExp(input), 'i')
34
- return fullnameInput.test(contact.fullname)
46
+ const normalizedInput = normalizeLowercase(input)
47
+ return normalizeLowercase(contact.fullname).includes(normalizedInput)
35
48
  }
36
49
 
37
50
  const contactOrGroupMatch = (input, contact) => {
@@ -146,5 +146,112 @@ describe('Suggestion matchers', () => {
146
146
  expect(matchers.fullnameMatch('snow', contact)).toBeTruthy()
147
147
  expect(matchers.fullnameMatch('MARG', contact)).toBeTruthy()
148
148
  })
149
+
150
+ it('should match accented fullname with unaccented input', () => {
151
+ expect(
152
+ matchers.fullnameMatch('zoe', { fullname: 'Zoé Dupont' })
153
+ ).toBeTruthy()
154
+ expect(
155
+ matchers.fullnameMatch('elodie', { fullname: 'Élodie Martin' })
156
+ ).toBeTruthy()
157
+ expect(
158
+ matchers.fullnameMatch('celine', { fullname: 'Céline' })
159
+ ).toBeTruthy()
160
+ })
161
+
162
+ it('should match unaccented fullname with accented input', () => {
163
+ expect(
164
+ matchers.fullnameMatch('zoé', { fullname: 'Zoe Dupont' })
165
+ ).toBeTruthy()
166
+ })
167
+
168
+ it('should treat regex metacharacters as plain text', () => {
169
+ expect(() =>
170
+ matchers.fullnameMatch('(', { fullname: 'Jon Snow' })
171
+ ).not.toThrow()
172
+ expect(matchers.fullnameMatch('(', { fullname: 'Jon Snow' })).toBeFalsy()
173
+ expect(matchers.fullnameMatch('.', { fullname: 'Jon Snow' })).toBeFalsy()
174
+ expect(
175
+ matchers.fullnameMatch('.', { fullname: 'Dr. House' })
176
+ ).toBeTruthy()
177
+ })
178
+ })
179
+
180
+ describe('groupNameMatch', () => {
181
+ it('should match accented group name with unaccented input', () => {
182
+ const group = {
183
+ _type: 'io.cozy.contacts.groups',
184
+ name: 'Équipe'
185
+ }
186
+ expect(matchers.groupNameMatch('equipe', group)).toBeTruthy()
187
+ })
188
+ })
189
+
190
+ describe('Vietnamese diacritics support', () => {
191
+ const vietnameseContacts = [
192
+ { fullname: 'Nguyễn Văn Minh', email: 'minh.nguyen@email.com' },
193
+ { fullname: 'Trần Thị Hương', email: 'huong.tran@email.com' },
194
+ { fullname: 'Lê Hoàng Nam', email: 'nam.hoang@email.com' },
195
+ {
196
+ fullname: 'Phạm Đức Anh',
197
+ email: 'anh.pham@email.com',
198
+ cozy: [{ url: 'https://anh.mycozy.cloud' }]
199
+ }
200
+ ]
201
+
202
+ it('should match Vietnamese accented name with unaccented input', () => {
203
+ expect(
204
+ matchers.fullnameMatch('nguyen', vietnameseContacts[0])
205
+ ).toBeTruthy()
206
+ expect(matchers.fullnameMatch('van', vietnameseContacts[0])).toBeTruthy()
207
+ expect(matchers.fullnameMatch('minh', vietnameseContacts[0])).toBeTruthy()
208
+ expect(matchers.fullnameMatch('tran', vietnameseContacts[1])).toBeTruthy()
209
+ expect(
210
+ matchers.fullnameMatch('huong', vietnameseContacts[1])
211
+ ).toBeTruthy()
212
+ expect(matchers.fullnameMatch('le', vietnameseContacts[2])).toBeTruthy()
213
+ expect(matchers.fullnameMatch('nam', vietnameseContacts[2])).toBeTruthy()
214
+ expect(matchers.fullnameMatch('pham', vietnameseContacts[3])).toBeTruthy()
215
+ expect(matchers.fullnameMatch('anh', vietnameseContacts[3])).toBeTruthy()
216
+ })
217
+
218
+ it('should match unaccented input with Vietnamese accented name', () => {
219
+ expect(
220
+ matchers.fullnameMatch('nguyễn', vietnameseContacts[0])
221
+ ).toBeTruthy()
222
+ expect(matchers.fullnameMatch('văn', vietnameseContacts[0])).toBeTruthy()
223
+ expect(matchers.fullnameMatch('trần', vietnameseContacts[1])).toBeTruthy()
224
+ expect(
225
+ matchers.fullnameMatch('hương', vietnameseContacts[1])
226
+ ).toBeTruthy()
227
+ expect(matchers.fullnameMatch('lê', vietnameseContacts[2])).toBeTruthy()
228
+ expect(matchers.fullnameMatch('phạm', vietnameseContacts[3])).toBeTruthy()
229
+ })
230
+
231
+ it('should match Vietnamese email with partial unaccented input', () => {
232
+ expect(matchers.emailMatch('minh', vietnameseContacts[0])).toBeTruthy()
233
+ expect(matchers.emailMatch('nguyen', vietnameseContacts[0])).toBeTruthy()
234
+ expect(matchers.emailMatch('huong', vietnameseContacts[1])).toBeTruthy()
235
+ expect(matchers.emailMatch('tran', vietnameseContacts[1])).toBeTruthy()
236
+ })
237
+
238
+ it('should match Vietnamese cozy URL with unaccented input', () => {
239
+ const contact = {
240
+ fullname: 'Đặng Minh Đức',
241
+ cozy: [{ url: 'https://duc.mycozy.cloud' }]
242
+ }
243
+ expect(matchers.cozyUrlMatch('duc', contact)).toBeTruthy()
244
+ expect(matchers.cozyUrlMatch('mycozy', contact)).toBeTruthy()
245
+ })
246
+
247
+ it('should match Vietnamese group names with diacritics', () => {
248
+ const vietGroup = {
249
+ _type: 'io.cozy.contacts.groups',
250
+ name: 'Nhóm Việt'
251
+ }
252
+ expect(matchers.groupNameMatch('nhom', vietGroup)).toBeTruthy()
253
+ expect(matchers.groupNameMatch('viet', vietGroup)).toBeTruthy()
254
+ expect(matchers.groupNameMatch('nhóm', vietGroup)).toBeTruthy()
255
+ })
149
256
  })
150
257
  })