@inseefr/lunatic 3.4.7 → 3.4.8

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.
Files changed (40) hide show
  1. package/esm/type.source.d.ts +25 -40
  2. package/esm/type.source.js +1 -0
  3. package/esm/type.source.js.map +1 -1
  4. package/esm/utils/search/SearchMiniSearch.spec.d.ts +1 -0
  5. package/esm/utils/search/SearchMiniSearch.spec.js +51 -0
  6. package/esm/utils/search/SearchMiniSearch.spec.js.map +1 -0
  7. package/esm/utils/search/melauto.js +1 -1
  8. package/esm/utils/search/melauto.spec.d.ts +1 -0
  9. package/esm/utils/search/melauto.spec.js +67 -0
  10. package/esm/utils/search/melauto.spec.js.map +1 -0
  11. package/esm/utils/search/tokenizer.d.ts +7 -2
  12. package/esm/utils/search/tokenizer.js +23 -8
  13. package/esm/utils/search/tokenizer.js.map +1 -1
  14. package/esm/utils/search/tokenizer.spec.d.ts +1 -0
  15. package/esm/utils/search/tokenizer.spec.js +160 -0
  16. package/esm/utils/search/tokenizer.spec.js.map +1 -0
  17. package/package.json +23 -1
  18. package/src/type.source.ts +93 -108
  19. package/src/utils/search/SearchMiniSearch.spec.ts +58 -0
  20. package/src/utils/search/melauto.spec.ts +75 -0
  21. package/src/utils/search/melauto.ts +1 -1
  22. package/src/utils/search/tokenizer.spec.ts +205 -0
  23. package/src/utils/search/tokenizer.ts +27 -8
  24. package/tsconfig.build.tsbuildinfo +1 -1
  25. package/type.source.d.ts +25 -40
  26. package/type.source.js +1 -0
  27. package/type.source.js.map +1 -1
  28. package/utils/search/SearchMiniSearch.spec.d.ts +1 -0
  29. package/utils/search/SearchMiniSearch.spec.js +51 -0
  30. package/utils/search/SearchMiniSearch.spec.js.map +1 -0
  31. package/utils/search/melauto.js +1 -1
  32. package/utils/search/melauto.spec.d.ts +1 -0
  33. package/utils/search/melauto.spec.js +69 -0
  34. package/utils/search/melauto.spec.js.map +1 -0
  35. package/utils/search/tokenizer.d.ts +7 -2
  36. package/utils/search/tokenizer.js +24 -8
  37. package/utils/search/tokenizer.js.map +1 -1
  38. package/utils/search/tokenizer.spec.d.ts +1 -0
  39. package/utils/search/tokenizer.spec.js +162 -0
  40. package/utils/search/tokenizer.spec.js.map +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inseefr/lunatic",
3
- "version": "3.4.7",
3
+ "version": "3.4.8",
4
4
  "description": "Library of questionnaire components",
5
5
  "repository": {
6
6
  "type": "git",
@@ -457,9 +457,12 @@
457
457
  "src/utils/number.ts",
458
458
  "src/utils/object.ts",
459
459
  "src/utils/search/SearchInterface.ts",
460
+ "src/utils/search/SearchMiniSearch.spec.ts",
460
461
  "src/utils/search/SearchMinisearch.ts",
461
462
  "src/utils/search/SuggestersDatabase.ts",
463
+ "src/utils/search/melauto.spec.ts",
462
464
  "src/utils/search/melauto.ts",
465
+ "src/utils/search/tokenizer.spec.ts",
463
466
  "src/utils/search/tokenizer.ts",
464
467
  "src/utils/to-number.ts",
465
468
  "src/utils/variables.spec.ts",
@@ -1619,6 +1622,9 @@
1619
1622
  "esm/utils/search/SearchInterface.d.ts",
1620
1623
  "esm/utils/search/SearchInterface.js",
1621
1624
  "esm/utils/search/SearchInterface.js.map",
1625
+ "esm/utils/search/SearchMiniSearch.spec.d.ts",
1626
+ "esm/utils/search/SearchMiniSearch.spec.js",
1627
+ "esm/utils/search/SearchMiniSearch.spec.js.map",
1622
1628
  "esm/utils/search/SearchMinisearch.d.ts",
1623
1629
  "esm/utils/search/SearchMinisearch.js",
1624
1630
  "esm/utils/search/SearchMinisearch.js.map",
@@ -1628,9 +1634,15 @@
1628
1634
  "esm/utils/search/melauto.d.ts",
1629
1635
  "esm/utils/search/melauto.js",
1630
1636
  "esm/utils/search/melauto.js.map",
1637
+ "esm/utils/search/melauto.spec.d.ts",
1638
+ "esm/utils/search/melauto.spec.js",
1639
+ "esm/utils/search/melauto.spec.js.map",
1631
1640
  "esm/utils/search/tokenizer.d.ts",
1632
1641
  "esm/utils/search/tokenizer.js",
1633
1642
  "esm/utils/search/tokenizer.js.map",
1643
+ "esm/utils/search/tokenizer.spec.d.ts",
1644
+ "esm/utils/search/tokenizer.spec.js",
1645
+ "esm/utils/search/tokenizer.spec.js.map",
1634
1646
  "esm/utils/to-number.d.ts",
1635
1647
  "esm/utils/to-number.js",
1636
1648
  "esm/utils/to-number.js.map",
@@ -1934,6 +1946,9 @@
1934
1946
  "utils/search/SearchInterface.d.ts",
1935
1947
  "utils/search/SearchInterface.js",
1936
1948
  "utils/search/SearchInterface.js.map",
1949
+ "utils/search/SearchMiniSearch.spec.d.ts",
1950
+ "utils/search/SearchMiniSearch.spec.js",
1951
+ "utils/search/SearchMiniSearch.spec.js.map",
1937
1952
  "utils/search/SearchMinisearch.d.ts",
1938
1953
  "utils/search/SearchMinisearch.js",
1939
1954
  "utils/search/SearchMinisearch.js.map",
@@ -1943,9 +1958,15 @@
1943
1958
  "utils/search/melauto.d.ts",
1944
1959
  "utils/search/melauto.js",
1945
1960
  "utils/search/melauto.js.map",
1961
+ "utils/search/melauto.spec.d.ts",
1962
+ "utils/search/melauto.spec.js",
1963
+ "utils/search/melauto.spec.js.map",
1946
1964
  "utils/search/tokenizer.d.ts",
1947
1965
  "utils/search/tokenizer.js",
1948
1966
  "utils/search/tokenizer.js.map",
1967
+ "utils/search/tokenizer.spec.d.ts",
1968
+ "utils/search/tokenizer.spec.js",
1969
+ "utils/search/tokenizer.spec.js.map",
1949
1970
  "utils/to-number.d.ts",
1950
1971
  "utils/to-number.js",
1951
1972
  "utils/to-number.js.map",
@@ -2007,6 +2028,7 @@
2007
2028
  "@types/react": "^18.3.3",
2008
2029
  "@types/react-dom": "^18.3.0",
2009
2030
  "@vitejs/plugin-react": "^4.3.1",
2031
+ "@vitest/coverage-v8": "2.1.2",
2010
2032
  "ajv": "^8.17.1",
2011
2033
  "concurrently": "^8.2.2",
2012
2034
  "eslint": "^9.9.0",
@@ -1,3 +1,4 @@
1
+ /* eslint-disable */
1
2
  /**
2
3
  * This file was automatically generated by json-schema-to-typescript.
3
4
  * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
@@ -34,20 +35,18 @@ export type ComponentDefinition =
34
35
  | ComponentSummaryDefinition
35
36
  | ComponentText
36
37
  | ComponentAccordion;
37
- export type ComponentInputDefinition = ComponentInputDefinition1 & {
38
+ export type ComponentInputDefinition = ComponentDefinitionBaseWithResponse & {
38
39
  componentType: 'Input' | 'Textarea';
39
40
  maxLength?: number;
40
41
  };
41
- export type ComponentInputDefinition1 = ComponentDefinitionBaseWithResponse;
42
42
  export type ComponentDefinitionBaseWithResponse = ComponentDefinitionBase & {
43
43
  response: ResponseDefinition;
44
44
  };
45
- export type ComponentSequenceDefinition = ComponentSequenceDefinition1 & {
45
+ export type ComponentSequenceDefinition = ComponentDefinitionBase & {
46
46
  componentType: 'Sequence' | 'Subsequence';
47
47
  goToPage?: string;
48
48
  };
49
- export type ComponentSequenceDefinition1 = ComponentDefinitionBase;
50
- export type ComponentRoundaboutDefinition = ComponentRoundaboutDefinition1 & {
49
+ export type ComponentRoundaboutDefinition = ComponentDefinitionBase & {
51
50
  componentType: 'Roundabout';
52
51
  iterations: VTLScalarExpression;
53
52
  locked: boolean;
@@ -59,25 +58,22 @@ export type ComponentRoundaboutDefinition = ComponentRoundaboutDefinition1 & {
59
58
  };
60
59
  components: ComponentDefinitionWithPage[];
61
60
  };
62
- export type ComponentRoundaboutDefinition1 = ComponentDefinitionBase;
63
61
  export type ComponentLoopDefinition = {
64
62
  componentType: 'Loop';
65
63
  loopDependencies?: string[];
66
64
  } & ComponentLoopDefinition1;
67
65
  export type ComponentLoopDefinition1 = PaginatedLoop | BlockLoop;
68
- export type PaginatedLoop = PaginatedLoop1 & {
66
+ export type PaginatedLoop = ComponentDefinitionBase & {
69
67
  components: ComponentDefinitionWithPage[];
70
68
  iterations: VTLScalarExpression;
71
69
  maxPage: string;
72
70
  paginatedLoop: true;
73
71
  };
74
- export type PaginatedLoop1 = ComponentDefinitionBase;
75
- export type BlockLoop = BlockLoop1 & {
72
+ export type BlockLoop = ComponentDefinitionBase & {
76
73
  paginatedLoop: false;
77
74
  components: ComponentDefinition[];
78
- } & BlockLoop2;
79
- export type BlockLoop1 = ComponentDefinitionBase;
80
- export type BlockLoop2 =
75
+ } & BlockLoop1;
76
+ export type BlockLoop1 =
81
77
  | {
82
78
  lines: {
83
79
  min: VTLExpression;
@@ -87,17 +83,15 @@ export type BlockLoop2 =
87
83
  | {
88
84
  iterations: VTLExpression;
89
85
  };
90
- export type ComponentRosterForLoopDefinition =
91
- ComponentRosterForLoopDefinition1 & {
92
- componentType: 'RosterForLoop';
93
- components: ComponentDefinition[];
94
- lines: {
95
- min: VTLScalarExpression;
96
- max: VTLScalarExpression;
97
- };
98
- header?: TableHeader;
86
+ export type ComponentRosterForLoopDefinition = ComponentDefinitionBase & {
87
+ componentType: 'RosterForLoop';
88
+ components: ComponentDefinition[];
89
+ lines: {
90
+ min: VTLScalarExpression;
91
+ max: VTLScalarExpression;
99
92
  };
100
- export type ComponentRosterForLoopDefinition1 = ComponentDefinitionBase;
93
+ header?: TableHeader;
94
+ };
101
95
  export type Options = {
102
96
  value: string | boolean;
103
97
  label: VTLExpression;
@@ -111,7 +105,7 @@ export type TableHeader = {
111
105
  rowspan?: number;
112
106
  options?: Options;
113
107
  }[];
114
- export type ComponentTableDefinition = ComponentTableDefinition1 & {
108
+ export type ComponentTableDefinition = ComponentDefinitionBase & {
115
109
  componentType: 'Table';
116
110
  header?: TableHeader;
117
111
  body: (
@@ -121,57 +115,49 @@ export type ComponentTableDefinition = ComponentTableDefinition1 & {
121
115
  }
122
116
  )[][];
123
117
  };
124
- export type ComponentTableDefinition1 = ComponentDefinitionBase;
125
- export type ComponentNumberDefinition = ComponentNumberDefinition1 & {
118
+ export type ComponentNumberDefinition = ComponentDefinitionBaseWithResponse & {
126
119
  componentType: 'InputNumber';
127
120
  unit?: string | VTLExpression;
128
121
  min?: number;
129
122
  max?: number;
130
123
  decimals?: number;
131
124
  };
132
- export type ComponentNumberDefinition1 = ComponentDefinitionBaseWithResponse;
133
- export type ComponentDurationDefinition = ComponentDurationDefinition1 & {
134
- componentType: 'Duration';
135
- format: 'PnYnM' | 'PTnHnM';
136
- };
137
- export type ComponentDurationDefinition1 = ComponentDefinitionBaseWithResponse;
138
- export type ComponentDatePickerDefinition = ComponentDatePickerDefinition1 & {
139
- componentType: 'Datepicker';
140
- dateFormat: 'YYYY-MM-DD' | 'YYYY' | 'YYYY-MM';
141
- min?: string;
142
- max?: string;
143
- };
144
- export type ComponentDatePickerDefinition1 =
145
- ComponentDefinitionBaseWithResponse;
146
- export type ComponentCheckboxGroupDefinition =
147
- ComponentCheckboxGroupDefinition1 & {
148
- componentType: 'CheckboxGroup';
149
- orientation?: 'horizontal' | 'vertical';
150
- responses: {
151
- label: VTLExpression;
152
- description?: VTLExpression;
153
- response: ResponseDefinition;
154
- conditionFilter?: VTLExpression;
155
- id: string;
156
- detail?: {
157
- label?: VTLExpression;
158
- response: ResponseDefinition;
159
- };
160
- }[];
125
+ export type ComponentDurationDefinition =
126
+ ComponentDefinitionBaseWithResponse & {
127
+ componentType: 'Duration';
128
+ format: 'PnYnM' | 'PTnHnM';
161
129
  };
162
- export type ComponentCheckboxGroupDefinition1 = ComponentDefinitionBase;
130
+ export type ComponentDatePickerDefinition =
131
+ ComponentDefinitionBaseWithResponse & {
132
+ componentType: 'Datepicker';
133
+ dateFormat: 'YYYY-MM-DD' | 'YYYY' | 'YYYY-MM';
134
+ min?: string;
135
+ max?: string;
136
+ };
137
+ export type ComponentCheckboxGroupDefinition = ComponentDefinitionBase & {
138
+ componentType: 'CheckboxGroup';
139
+ orientation?: 'horizontal' | 'vertical';
140
+ responses: {
141
+ label: VTLExpression;
142
+ description?: VTLExpression;
143
+ response: ResponseDefinition;
144
+ conditionFilter?: VTLExpression;
145
+ id: string;
146
+ detail?: {
147
+ label?: VTLExpression;
148
+ response: ResponseDefinition;
149
+ };
150
+ }[];
151
+ };
163
152
  export type ComponentCheckboxBooleanDefinition =
164
- ComponentCheckboxBooleanDefinition1 & {
153
+ ComponentDefinitionBaseWithResponse & {
165
154
  componentType: 'CheckboxBoolean';
166
155
  };
167
- export type ComponentCheckboxBooleanDefinition1 =
168
- ComponentDefinitionBaseWithResponse;
169
- export type ComponentRadioDefinition = ComponentRadioDefinition1 & {
156
+ export type ComponentRadioDefinition = ComponentDefinitionBaseWithResponse & {
170
157
  componentType: 'Radio';
171
158
  orientation?: 'horizontal' | 'vertical';
172
159
  options: OptionsWithDetail;
173
160
  };
174
- export type ComponentRadioDefinition1 = ComponentDefinitionBaseWithResponse;
175
161
  export type OptionsWithDetail = {
176
162
  value: string | boolean;
177
163
  label: VTLExpression;
@@ -183,63 +169,59 @@ export type OptionsWithDetail = {
183
169
  };
184
170
  };
185
171
  }[];
186
- export type ComponentDropdownDefinition = ComponentDropdownDefinition1 & {
187
- componentType: 'Dropdown';
188
- options: Options;
189
- };
190
- export type ComponentDropdownDefinition1 = ComponentDefinitionBaseWithResponse;
191
- export type ComponentQuestionDefinition = ComponentQuestionDefinition1 & {
172
+ export type ComponentDropdownDefinition =
173
+ ComponentDefinitionBaseWithResponse & {
174
+ componentType: 'Dropdown';
175
+ options: Options;
176
+ };
177
+ export type ComponentQuestionDefinition = ComponentDefinitionBase & {
192
178
  componentType: 'Question';
193
179
  components: ComponentDefinition[];
194
180
  };
195
- export type ComponentQuestionDefinition1 = ComponentDefinitionBase;
196
- export type ComponentCheckboxOneDefinition = ComponentCheckboxOneDefinition1 & {
197
- componentType: 'CheckboxOne';
198
- options: OptionsWithDetail;
199
- };
200
- export type ComponentCheckboxOneDefinition1 =
201
- ComponentDefinitionBaseWithResponse;
202
- export type ComponentSuggesterDefinition = ComponentSuggesterDefinition1 & {
203
- componentType: 'Suggester';
204
- /**
205
- * Nom / Index du référentiel à utiliser
206
- */
207
- storeName: string;
208
- /**
209
- * Permet l'entrée d'une valeur arbitraire (Autre)
210
- */
211
- arbitrary?: {
212
- response: ResponseDefinition;
181
+ export type ComponentCheckboxOneDefinition =
182
+ ComponentDefinitionBaseWithResponse & {
183
+ componentType: 'CheckboxOne';
184
+ options: OptionsWithDetail;
213
185
  };
214
- /**
215
- * Liste des attributs de la nomenclature à sauvegarder dans une variable
216
- */
217
- optionResponses?: {
186
+ export type ComponentSuggesterDefinition =
187
+ ComponentDefinitionBaseWithResponse & {
188
+ componentType: 'Suggester';
218
189
  /**
219
- * Nom de la variable
190
+ * Nom / Index du référentiel à utiliser
220
191
  */
221
- name: string;
192
+ storeName: string;
222
193
  /**
223
- * Nom de la propriété dans la nomenclature
194
+ * Permet l'entrée d'une valeur arbitraire (Autre)
224
195
  */
225
- attribute: string;
226
- }[];
227
- };
228
- export type ComponentSuggesterDefinition1 = ComponentDefinitionBaseWithResponse;
229
- export type ComponentPairWiseLinksDefinition =
230
- ComponentPairWiseLinksDefinition1 & {
231
- componentType: 'PairwiseLinks';
232
- xAxisIterations: VTLScalarExpression;
233
- yAxisIterations: VTLScalarExpression;
234
- symLinks: {
235
- [k: string]: {
236
- [k: string]: string | null;
237
- };
196
+ arbitrary?: {
197
+ response: ResponseDefinition;
198
+ };
199
+ /**
200
+ * Liste des attributs de la nomenclature à sauvegarder dans une variable
201
+ */
202
+ optionResponses?: {
203
+ /**
204
+ * Nom de la variable
205
+ */
206
+ name: string;
207
+ /**
208
+ * Nom de la propriété dans la nomenclature
209
+ */
210
+ attribute: string;
211
+ }[];
212
+ };
213
+ export type ComponentPairWiseLinksDefinition = ComponentDefinitionBase & {
214
+ componentType: 'PairwiseLinks';
215
+ xAxisIterations: VTLScalarExpression;
216
+ yAxisIterations: VTLScalarExpression;
217
+ symLinks: {
218
+ [k: string]: {
219
+ [k: string]: string | null;
238
220
  };
239
- components: ComponentDefinition[];
240
221
  };
241
- export type ComponentPairWiseLinksDefinition1 = ComponentDefinitionBase;
242
- export type ComponentSummaryDefinition = ComponentSummaryDefinition1 & {
222
+ components: ComponentDefinition[];
223
+ };
224
+ export type ComponentSummaryDefinition = ComponentDefinitionBase & {
243
225
  componentType: 'Summary';
244
226
  sections: {
245
227
  id: string;
@@ -252,7 +234,6 @@ export type ComponentSummaryDefinition = ComponentSummaryDefinition1 & {
252
234
  }[];
253
235
  }[];
254
236
  };
255
- export type ComponentSummaryDefinition1 = ComponentDefinitionBase;
256
237
  export type Variable =
257
238
  | {
258
239
  variableType: 'EXTERNAL';
@@ -465,4 +446,8 @@ export type SuggesterDefinition = {
465
446
  | {
466
447
  type: 'soft';
467
448
  };
449
+ /**
450
+ * list of words to exclude from the searching
451
+ */
452
+ stopWords?: string[];
468
453
  };
@@ -0,0 +1,58 @@
1
+ import { describe, it, expect, vi, beforeAll, afterEach } from 'vitest';
2
+ import { SearchMinisearch } from './SearchMinisearch';
3
+
4
+ vi.mock('minisearch', () => {
5
+ return {
6
+ default: vi.fn().mockImplementation(() => ({
7
+ addAll: vi.fn(),
8
+ search: vi.fn(),
9
+ })),
10
+ };
11
+ });
12
+
13
+ vi.mock('./melauto', () => ({
14
+ applyMelauto: vi.fn(),
15
+ }));
16
+
17
+ describe('SearchMinisearch', () => {
18
+ let searchInstance: SearchMinisearch<any>;
19
+ const mockData = [
20
+ { id: '1', label: 'First Item' },
21
+ { id: '2', label: 'Second Item' },
22
+ ];
23
+
24
+ beforeAll(() => {
25
+ searchInstance = new SearchMinisearch({
26
+ name: 'test-suggester',
27
+ fields: [{ name: 'id' }, { name: 'label' }],
28
+ queryParser: {
29
+ type: 'tokenized',
30
+ params: { language: 'English', pattern: '\\w+', min: 1 },
31
+ },
32
+ max: 10,
33
+ });
34
+ });
35
+
36
+ afterEach(() => {
37
+ const miniSearchMock = searchInstance.db as any;
38
+ miniSearchMock.addAll.mockClear();
39
+ });
40
+
41
+ it('should initialize and index data correctly', async () => {
42
+ await searchInstance.index(mockData);
43
+
44
+ // Check if MiniSearch instance was created and indexed
45
+ expect(searchInstance.db).not.toBeNull();
46
+ expect(searchInstance.isIndexed()).toBe(true);
47
+
48
+ // Check if addAll was called with the correct data
49
+ expect(searchInstance.db?.addAll).toHaveBeenCalledWith(mockData);
50
+ });
51
+
52
+ it('should not re-index if already indexed', async () => {
53
+ searchInstance.indexed = true;
54
+ await searchInstance.index(mockData);
55
+
56
+ expect(searchInstance.db?.addAll).not.toHaveBeenCalled();
57
+ });
58
+ });
@@ -0,0 +1,75 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { applyMelauto, melautoScore } from './melauto';
3
+
4
+ // Mock data to test the sorting and scoring
5
+ const data = [
6
+ { id: '1', label: 'Hello world' },
7
+ { id: '2', label: 'Bonjour le monde' },
8
+ { id: '3', label: 'Hello everyone' },
9
+ { id: '4', label: 'Greetings planet' },
10
+ ];
11
+
12
+ describe('applyMelauto', () => {
13
+ it('should sort data by relevance to the query', () => {
14
+ const sortedData = applyMelauto('hello', data);
15
+ const expectedSortedData = [
16
+ { id: '1', label: 'Hello world' },
17
+ { id: '3', label: 'Hello everyone' },
18
+ { id: '2', label: 'Bonjour le monde' },
19
+ { id: '4', label: 'Greetings planet' },
20
+ ];
21
+ expect(sortedData).toStrictEqual(expectedSortedData);
22
+ });
23
+
24
+ it('should return data in original order if query is empty', () => {
25
+ const sortedData = applyMelauto('', data);
26
+ expect(sortedData).toEqual(data);
27
+ });
28
+
29
+ it('should handle data without labels by using id instead', () => {
30
+ const noLabelData = [
31
+ { id: 'a' },
32
+ { id: 'b', label: 'Hello' },
33
+ { id: 'hello-w' },
34
+ ];
35
+ const sortedData = applyMelauto('hello', noLabelData);
36
+ const expectedSortedData = [
37
+ { id: 'b', label: 'Hello' },
38
+ { id: 'hello-w' },
39
+ { id: 'a' },
40
+ ];
41
+ expect(sortedData).toStrictEqual(expectedSortedData);
42
+ });
43
+ });
44
+
45
+ describe('melautoScore', () => {
46
+ it('should return a non-null score', () => {
47
+ const score = melautoScore('Hello world', 'hello');
48
+ const expectedScore = 1;
49
+ expect(score).toBeCloseTo(expectedScore, 2);
50
+ });
51
+
52
+ it('should give a higher score for a closer match', () => {
53
+ const score1 = melautoScore('Hello beautiful world', 'hello world');
54
+ const score2 = melautoScore('Hello beautiful world', 'hello');
55
+ expect(score1).toBeGreaterThan(score2);
56
+ });
57
+
58
+ it('should give the same score for a same query if the comparison string is longer', () => {
59
+ const score1 = melautoScore('Hello world', 'hello');
60
+ const score2 = melautoScore('Hello beautiful world', 'hello');
61
+ expect(score1).toBeCloseTo(score2, 2);
62
+ });
63
+
64
+ it('should calculate a null score', () => {
65
+ const score = melautoScore('Greetings planet', 'hello');
66
+ const expectedScore = 0;
67
+ expect(score).toBe(expectedScore);
68
+ });
69
+
70
+ it('should handle accent and special characters correctly', () => {
71
+ const score1 = melautoScore('Héllo wörld', 'hello world');
72
+ const score2 = melautoScore('Hello world', 'héllo-wOrld');
73
+ expect(score1).toBeCloseTo(score2, 2);
74
+ });
75
+ });
@@ -7,7 +7,7 @@ export function applyMelauto<T extends { id: string; label?: string }>(
7
7
  ): T[] {
8
8
  return data.sort(
9
9
  (a, b) =>
10
- melautoScore(b.label ?? a.id, query) -
10
+ melautoScore(b.label ?? b.id, query) -
11
11
  melautoScore(a.label ?? a.id, query)
12
12
  );
13
13
  }