Skip to content

Commit eac5141

Browse files
committed
(wip) feat: fuzzy search
1 parent 8585fcd commit eac5141

File tree

6 files changed

+185
-81
lines changed

6 files changed

+185
-81
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"dependencies": {
3535
"ansi-escapes": "^4.3.1",
3636
"chalk": "^4.0.0",
37+
"fuzzysearch": "^1.0.3",
3738
"jest-regex-util": "^27.0.0",
3839
"jest-watcher": "^27.0.0",
3940
"slash": "^4.0.0",

src/file_name_plugin/__tests__/__snapshots__/plugin.test.ts.snap

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,84 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`can create a pattern corresponding to the fuzzy search 1`] = `
4+
5+
[MOCK - cursorLeft]
6+
7+
pattern › f
8+
[MOCK - cursorSavePosition]
9+
[MOCK - cursorLeft]
10+
11+
12+
Pattern matches 3 files
13+
14+
› src/foo.js
15+
16+
› src/file-1.js
17+
18+
› src/file-2.js
19+
[MOCK - cursorTo(12, 5)]
20+
[MOCK - cursorRestorePosition]
21+
22+
[MOCK - cursorLeft]
23+
24+
pattern › f1
25+
[MOCK - cursorSavePosition]
26+
[MOCK - cursorLeft]
27+
28+
29+
Pattern matches 1 file
30+
31+
› src/file-1.js
32+
[MOCK - cursorTo(13, 5)]
33+
[MOCK - cursorRestorePosition]
34+
`;
35+
36+
exports[`can fuzzy search 1`] = `
37+
38+
[MOCK - cursorLeft]
39+
40+
pattern › f
41+
[MOCK - cursorSavePosition]
42+
[MOCK - cursorLeft]
43+
44+
45+
Pattern matches 3 files
46+
47+
› src/foo.js
48+
49+
› src/file-1.js
50+
51+
› src/file-2.js
52+
[MOCK - cursorTo(12, 5)]
53+
[MOCK - cursorRestorePosition]
54+
55+
[MOCK - cursorLeft]
56+
57+
pattern › f1
58+
[MOCK - cursorSavePosition]
59+
[MOCK - cursorLeft]
60+
61+
62+
Pattern matches 1 file
63+
64+
› src/file-1.js
65+
[MOCK - cursorTo(13, 5)]
66+
[MOCK - cursorRestorePosition]
67+
68+
[MOCK - cursorLeft]
69+
70+
pattern › f1
71+
[MOCK - cursorSavePosition]
72+
[MOCK - cursorLeft]
73+
74+
75+
Pattern matches 1 file
76+
77+
› src/file-1.js
78+
[MOCK - cursorTo(13, 5)]
79+
[MOCK - cursorRestorePosition]
80+
`;
81+
382
exports[`can select a pattern that matches multiple files 1`] = `
483
584
[MOCK - cursorLeft]

src/file_name_plugin/__tests__/plugin.test.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ it('can select a pattern that matches multiple files', async () => {
119119

120120
expect(updateConfigAndRun).toHaveBeenCalledWith({
121121
mode: 'watch',
122-
testPathPattern: 'fi',
122+
testPathPattern: 'f.*i',
123123
});
124124
});
125125

@@ -179,3 +179,21 @@ it("selected file doesn't include trimming dots", async () => {
179179
testPathPattern: 'ong_name_gonna_need_trimming\\.js',
180180
});
181181
});
182+
183+
it('can fuzzy search', async () => {
184+
const { stdout, hookEmitter, updateConfigAndRun, plugin, type } =
185+
pluginTester(FileNamePlugin);
186+
187+
hookEmitter.onFileChange({ projects });
188+
const runPromise = plugin.run({}, updateConfigAndRun);
189+
stdout.write.mockReset();
190+
type('f', '1', KEYS.ARROW_DOWN, KEYS.ENTER);
191+
expect(stdout.write.mock.calls.join('\n')).toMatchSnapshot();
192+
193+
await runPromise;
194+
195+
expect(updateConfigAndRun).toHaveBeenCalledWith({
196+
mode: 'watch',
197+
testPathPattern: 'src/file-1\\.js',
198+
});
199+
});

src/file_name_plugin/prompt.ts

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
printRestoredPatternCaret,
99
} from 'jest-watcher';
1010
import { escapeStrForRegex } from 'jest-regex-util';
11+
import fuzzysearch from 'fuzzysearch';
1112
import type { Config } from '@jest/types';
1213
import {
1314
highlight,
@@ -91,14 +92,6 @@ export default class FileNamePatternPrompt extends PatternPrompt {
9192
path: string;
9293
context: { config: Config.ProjectConfig };
9394
}> {
94-
let regex: RegExp;
95-
96-
try {
97-
regex = new RegExp(pattern, 'i');
98-
} catch (e) {
99-
return [];
100-
}
101-
10295
return this._searchSources.reduce<
10396
Array<{
10497
path: string;
@@ -107,7 +100,9 @@ export default class FileNamePatternPrompt extends PatternPrompt {
107100
>((tests, { testPaths, config }) => {
108101
return tests.concat(
109102
testPaths
110-
.filter((testPath) => regex.test(testPath))
103+
.filter((testPath) =>
104+
fuzzysearch(pattern.toLowerCase(), testPath.toLowerCase()),
105+
)
111106
.map((path) => ({
112107
path,
113108
context: { config },
@@ -130,7 +125,7 @@ export default class FileNamePatternPrompt extends PatternPrompt {
130125
super.run(
131126
(value) => {
132127
onSuccess(
133-
removeTrimmingDots(value).split('/').map(escapeStrForRegex).join('/'),
128+
removeTrimmingDots(value).split('').map(escapeStrForRegex).join('.*'),
134129
);
135130
},
136131
onCancel,

src/fuzzysearch.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/** Declaration file generated by dts-gen */
2+
declare module 'fuzzysearch' {
3+
function fuzzysearch(needle: string, haystack: string): boolean;
4+
5+
export = fuzzysearch;
6+
}

yarn.lock

Lines changed: 75 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -3310,6 +3310,11 @@ functional-red-black-tree@^1.0.1:
33103310
resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
33113311
integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
33123312

3313+
fuzzysearch@^1.0.3:
3314+
version "1.0.3"
3315+
resolved "https://registry.yarnpkg.com/fuzzysearch/-/fuzzysearch-1.0.3.tgz#dffc80f6d6b04223f2226aa79dd194231096d008"
3316+
integrity sha1-3/yA9tawQiPyImqnndGUIxCW0Ag=
3317+
33133318
gauge@^4.0.0:
33143319
version "4.0.3"
33153320
resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.3.tgz#286cf105c1962c659f0963058fb05116c1b82d3f"
@@ -5164,76 +5169,76 @@ npm@^7.0.0:
51645169
resolved "https://registry.yarnpkg.com/npm/-/npm-7.24.2.tgz#861117af8241bea592289f22407230e5300e59ca"
51655170
integrity sha512-120p116CE8VMMZ+hk8IAb1inCPk4Dj3VZw29/n2g6UI77urJKVYb7FZUDW8hY+EBnfsjI/2yrobBgFyzo7YpVQ==
51665171
dependencies:
5167-
"@isaacs/string-locale-compare" "*"
5168-
"@npmcli/arborist" "*"
5169-
"@npmcli/ci-detect" "*"
5170-
"@npmcli/config" "*"
5171-
"@npmcli/map-workspaces" "*"
5172-
"@npmcli/package-json" "*"
5173-
"@npmcli/run-script" "*"
5174-
abbrev "*"
5175-
ansicolors "*"
5176-
ansistyles "*"
5177-
archy "*"
5178-
cacache "*"
5179-
chalk "*"
5180-
chownr "*"
5181-
cli-columns "*"
5182-
cli-table3 "*"
5183-
columnify "*"
5184-
fastest-levenshtein "*"
5185-
glob "*"
5186-
graceful-fs "*"
5187-
hosted-git-info "*"
5188-
ini "*"
5189-
init-package-json "*"
5190-
is-cidr "*"
5191-
json-parse-even-better-errors "*"
5192-
libnpmaccess "*"
5193-
libnpmdiff "*"
5194-
libnpmexec "*"
5195-
libnpmfund "*"
5196-
libnpmhook "*"
5197-
libnpmorg "*"
5198-
libnpmpack "*"
5199-
libnpmpublish "*"
5200-
libnpmsearch "*"
5201-
libnpmteam "*"
5202-
libnpmversion "*"
5203-
make-fetch-happen "*"
5204-
minipass "*"
5205-
minipass-pipeline "*"
5206-
mkdirp "*"
5207-
mkdirp-infer-owner "*"
5208-
ms "*"
5209-
node-gyp "*"
5210-
nopt "*"
5211-
npm-audit-report "*"
5212-
npm-install-checks "*"
5213-
npm-package-arg "*"
5214-
npm-pick-manifest "*"
5215-
npm-profile "*"
5216-
npm-registry-fetch "*"
5217-
npm-user-validate "*"
5218-
npmlog "*"
5219-
opener "*"
5220-
pacote "*"
5221-
parse-conflict-json "*"
5222-
qrcode-terminal "*"
5223-
read "*"
5224-
read-package-json "*"
5225-
read-package-json-fast "*"
5226-
readdir-scoped-modules "*"
5227-
rimraf "*"
5228-
semver "*"
5229-
ssri "*"
5230-
tar "*"
5231-
text-table "*"
5232-
tiny-relative-date "*"
5233-
treeverse "*"
5234-
validate-npm-package-name "*"
5235-
which "*"
5236-
write-file-atomic "*"
5172+
"@isaacs/string-locale-compare" "^1.1.0"
5173+
"@npmcli/arborist" "^2.9.0"
5174+
"@npmcli/ci-detect" "^1.2.0"
5175+
"@npmcli/config" "^2.3.0"
5176+
"@npmcli/map-workspaces" "^1.0.4"
5177+
"@npmcli/package-json" "^1.0.1"
5178+
"@npmcli/run-script" "^1.8.6"
5179+
abbrev "~1.1.1"
5180+
ansicolors "~0.3.2"
5181+
ansistyles "~0.1.3"
5182+
archy "~1.0.0"
5183+
cacache "^15.3.0"
5184+
chalk "^4.1.2"
5185+
chownr "^2.0.0"
5186+
cli-columns "^3.1.2"
5187+
cli-table3 "^0.6.0"
5188+
columnify "~1.5.4"
5189+
fastest-levenshtein "^1.0.12"
5190+
glob "^7.2.0"
5191+
graceful-fs "^4.2.8"
5192+
hosted-git-info "^4.0.2"
5193+
ini "^2.0.0"
5194+
init-package-json "^2.0.5"
5195+
is-cidr "^4.0.2"
5196+
json-parse-even-better-errors "^2.3.1"
5197+
libnpmaccess "^4.0.2"
5198+
libnpmdiff "^2.0.4"
5199+
libnpmexec "^2.0.1"
5200+
libnpmfund "^1.1.0"
5201+
libnpmhook "^6.0.2"
5202+
libnpmorg "^2.0.2"
5203+
libnpmpack "^2.0.1"
5204+
libnpmpublish "^4.0.1"
5205+
libnpmsearch "^3.1.1"
5206+
libnpmteam "^2.0.3"
5207+
libnpmversion "^1.2.1"
5208+
make-fetch-happen "^9.1.0"
5209+
minipass "^3.1.3"
5210+
minipass-pipeline "^1.2.4"
5211+
mkdirp "^1.0.4"
5212+
mkdirp-infer-owner "^2.0.0"
5213+
ms "^2.1.2"
5214+
node-gyp "^7.1.2"
5215+
nopt "^5.0.0"
5216+
npm-audit-report "^2.1.5"
5217+
npm-install-checks "^4.0.0"
5218+
npm-package-arg "^8.1.5"
5219+
npm-pick-manifest "^6.1.1"
5220+
npm-profile "^5.0.3"
5221+
npm-registry-fetch "^11.0.0"
5222+
npm-user-validate "^1.0.1"
5223+
npmlog "^5.0.1"
5224+
opener "^1.5.2"
5225+
pacote "^11.3.5"
5226+
parse-conflict-json "^1.1.1"
5227+
qrcode-terminal "^0.12.0"
5228+
read "~1.0.7"
5229+
read-package-json "^4.1.1"
5230+
read-package-json-fast "^2.0.3"
5231+
readdir-scoped-modules "^1.1.0"
5232+
rimraf "^3.0.2"
5233+
semver "^7.3.5"
5234+
ssri "^8.0.1"
5235+
tar "^6.1.11"
5236+
text-table "~0.2.0"
5237+
tiny-relative-date "^1.3.0"
5238+
treeverse "^1.0.4"
5239+
validate-npm-package-name "~3.0.0"
5240+
which "^2.0.2"
5241+
write-file-atomic "^3.0.3"
52375242

52385243
npmlog@*, npmlog@^6.0.0, npmlog@^6.0.1:
52395244
version "6.0.1"

0 commit comments

Comments
 (0)