xref: /aosp_15_r20/external/perfetto/ui/src/base/fuzzy_unittest.ts (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1// Copyright (C) 2023 The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15import {FuzzyFinder, fuzzyMatch} from './fuzzy';
16
17describe('FuzzyFinder', () => {
18  const items = ['aaa', 'aba', 'zzz', 'c z d z e', 'CAPS', 'ababc'];
19  const finder = new FuzzyFinder(items, (x) => x);
20
21  it('finds all for empty search term', () => {
22    const result = finder.find('');
23    // Expect all results are returned in original order.
24    expect(result).toEqual([
25      {item: 'aaa', segments: [{matching: false, value: 'aaa'}]},
26      {item: 'aba', segments: [{matching: false, value: 'aba'}]},
27      {item: 'zzz', segments: [{matching: false, value: 'zzz'}]},
28      {item: 'c z d z e', segments: [{matching: false, value: 'c z d z e'}]},
29      {item: 'CAPS', segments: [{matching: false, value: 'CAPS'}]},
30      {item: 'ababc', segments: [{matching: false, value: 'ababc'}]},
31    ]);
32  });
33
34  it('finds exact match', () => {
35    const result = finder.find('aaa');
36    expect(result).toEqual(
37      expect.arrayContaining([
38        {item: 'aaa', segments: [{matching: true, value: 'aaa'}]},
39      ]),
40    );
41  });
42
43  it('finds approx matches', () => {
44    const result = finder.find('aa');
45    // Allow finding results in any order.
46    expect(result).toEqual(
47      expect.arrayContaining([
48        {
49          item: 'aaa',
50          // Either |aa|a or a|aa| is valid.
51          segments: expect.arrayContaining([
52            {matching: true, value: 'aa'},
53            {matching: false, value: 'a'},
54          ]),
55        },
56        {
57          item: 'aba',
58          segments: [
59            {matching: true, value: 'a'},
60            {matching: false, value: 'b'},
61            {matching: true, value: 'a'},
62          ],
63        },
64      ]),
65    );
66  });
67
68  it('does not find completely unrelated items', () => {
69    // |zzz| looks nothing like |aa| and should not be returned.
70    const result = finder.find('aa');
71    expect(result).not.toEqual(
72      expect.arrayContaining([expect.objectContaining({item: 'zzz'})]),
73    );
74  });
75
76  it('finds non-consecutive matches', () => {
77    const result = finder.find('cde');
78    expect(result).toEqual(
79      expect.arrayContaining([
80        {
81          item: 'c z d z e',
82          segments: [
83            {matching: true, value: 'c'},
84            {matching: false, value: ' z '},
85            {matching: true, value: 'd'},
86            {matching: false, value: ' z '},
87            {matching: true, value: 'e'},
88          ],
89        },
90      ]),
91    );
92  });
93
94  it('finds caps match when search term is in lower case', () => {
95    const result = finder.find('caps');
96    expect(result).toEqual(
97      expect.arrayContaining([
98        {item: 'CAPS', segments: [{matching: true, value: 'CAPS'}]},
99      ]),
100    );
101  });
102
103  it('finds match with false start', () => {
104    const result = finder.find('abc');
105    expect(result).toEqual(
106      expect.arrayContaining([
107        {
108          item: 'ababc',
109          segments: [
110            {matching: true, value: 'ab'},
111            {matching: false, value: 'ab'},
112            {matching: true, value: 'c'},
113          ],
114        },
115      ]),
116    );
117  });
118});
119
120test('fuzzyMatch', () => {
121  expect(fuzzyMatch('foo bar baz', 'foo')).toEqual({
122    matches: true,
123    segments: [
124      {matching: true, value: 'foo'},
125      {matching: false, value: ' bar baz'},
126    ],
127  });
128
129  expect(fuzzyMatch('foo bar baz', 'qux')).toEqual({
130    matches: false,
131    segments: [],
132  });
133
134  expect(fuzzyMatch('bar baz', 'foo', 'bar')).toEqual({
135    matches: true,
136    segments: [
137      {matching: true, value: 'bar'},
138      {matching: false, value: ' baz'},
139    ],
140  });
141});
142