From f043c845c273694f9ca909ac94cb11d5f546ce12 Mon Sep 17 00:00:00 2001 From: mrmlnc Date: Sat, 8 Feb 2020 12:42:13 +0300 Subject: [PATCH] fix(matchers): correctly handle multiple patterns --- src/providers/filters/deep.ts | 8 +---- src/providers/matchers/partial.spec.ts | 41 +++++++++++++------------ src/providers/matchers/partial.ts | 42 +++++++++++++------------- 3 files changed, 43 insertions(+), 48 deletions(-) diff --git a/src/providers/filters/deep.ts b/src/providers/filters/deep.ts index ff35d791..cc6c34e7 100644 --- a/src/providers/filters/deep.ts +++ b/src/providers/filters/deep.ts @@ -57,13 +57,7 @@ export default class DeepFilter { } private _isSkippedByPositivePatterns(entry: Entry, matcher: PartialMatcher): boolean { - const filepath = entry.path.replace(/^\.[/\\]/, ''); - - const parts = filepath.split('/'); - const level = parts.length - 1; - const part = parts[level]; - - return !this._settings.baseNameMatch && !matcher.match(level, part); + return !this._settings.baseNameMatch && !matcher.match(entry.path); } private _isSkippedByNegativePatterns(entry: Entry, negativeRe: PatternRe[]): boolean { diff --git a/src/providers/matchers/partial.spec.ts b/src/providers/matchers/partial.spec.ts index e8335aea..92772702 100644 --- a/src/providers/matchers/partial.spec.ts +++ b/src/providers/matchers/partial.spec.ts @@ -7,49 +7,50 @@ function getMatcher(patterns: Pattern[], options: MicromatchOptions = {}): Match return new Matcher(patterns, options); } -function assertMatch(patterns: Pattern[], level: number, part: string): void | never { +function assertMatch(patterns: Pattern[], filepath: string): void | never { const matcher = getMatcher(patterns); - assert.ok(matcher.match(level, part)); + assert.ok(matcher.match(filepath), `Path "${filepath}" should match: ${patterns}`); } -function assertNotMatch(patterns: Pattern[], level: number, part: string): void | never { +function assertNotMatch(patterns: Pattern[], filepath: string): void | never { const matcher = getMatcher(patterns); - assert.ok(!matcher.match(level, part)); + assert.ok(!matcher.match(filepath), `Path "${filepath}" should do not match: ${patterns}`); } describe('Providers → Matchers → Partial', () => { describe('.match', () => { it('should handle patterns with globstar', () => { - assertMatch(['**'], 0, 'a'); - assertMatch(['**'], 1, 'b'); - assertMatch(['**/a'], 0, 'a'); - assertMatch(['**/a'], 1, 'a'); - assertNotMatch(['a/**'], 0, 'b'); - assertMatch(['a/**'], 1, 'b'); + assertMatch(['**'], 'a'); + assertMatch(['**'], './a'); + assertMatch(['**/a'], 'a'); + assertMatch(['**/a'], 'b/a'); + assertMatch(['a/**'], 'a/b'); + assertNotMatch(['a/**'], 'b'); }); it('should do not match the latest segment', () => { - assertMatch(['b', 'b/*'], 0, 'b'); - assertNotMatch(['*'], 0, 'a'); - assertNotMatch(['a/*'], 1, 'b'); + assertMatch(['b/*'], 'b'); + assertNotMatch(['*'], 'a'); + assertNotMatch(['a/*'], 'a/b'); }); it('should trying to match all patterns', () => { - assertMatch(['a/*', 'b/*'], 0, 'b'); - assertMatch(['non-match', 'a/*/c'], 1, 'b'); + assertMatch(['a/*', 'b/*'], 'b'); + assertMatch(['non-match/b/c', 'a/*/c'], 'a/b'); + assertNotMatch(['non-match/d/c', 'a/b/c'], 'a/d'); }); it('should match a static segment', () => { - assertMatch(['a/b'], 0, 'a'); - assertNotMatch(['b/b'], 0, 'a'); + assertMatch(['a/b'], 'a'); + assertNotMatch(['b/b'], 'a'); }); it('should match a dynamic segment', () => { - assertMatch(['*/b'], 0, 'a'); - assertMatch(['{a,b}/*'], 0, 'a'); - assertNotMatch(['{a,b}/*'], 0, 'c'); + assertMatch(['*/b'], 'a'); + assertMatch(['{a,b}/*'], 'a'); + assertNotMatch(['{a,b}/*'], 'c'); }); }); }); diff --git a/src/providers/matchers/partial.ts b/src/providers/matchers/partial.ts index b6c8db4b..464825ac 100644 --- a/src/providers/matchers/partial.ts +++ b/src/providers/matchers/partial.ts @@ -1,9 +1,14 @@ import Matcher from './matcher'; export default class PartialMatcher extends Matcher { - public match(level: number, part: string): boolean { - for (const info of this._storage) { - const section = info.sections[0]; + public match(filepath: string): boolean { + const parts = filepath.split('/'); + const levels = parts.length; + + const patterns = this._storage.filter((info) => !info.complete || info.segments.length > levels); + + for (const pattern of patterns) { + const section = pattern.sections[0]; /** * In this case, the pattern has a globstar and we must read all directories unconditionally, @@ -12,30 +17,25 @@ export default class PartialMatcher extends Matcher { * fixtures/{a,b}/** * ^ true/false ^ always true */ - if (!info.complete && level >= section.length) { + if (!pattern.complete && levels > section.length) { return true; } - /** - * When size of the first group (minus the latest segment) greater or equals to `level`, - * we do not need reading the next directory, because in the next iteration, - * the path will have more levels than the pattern. - * - * But only if the pattern doesn't have a globstar (we must read all directories). - * - * In this cases we must trying to match other patterns. - */ - if (info.complete && level >= section.length - 1) { - continue; - } + const match = parts.every((part, index) => { + const segment = pattern.segments[index]; - const segment = section[level]; + if (segment.dynamic && segment.patternRe.test(part)) { + return true; + } - if (segment.dynamic && segment.patternRe.test(part)) { - return true; - } + if (!segment.dynamic && segment.pattern === part) { + return true; + } + + return false; + }); - if (!segment.dynamic && segment.pattern === part) { + if (match) { return true; } }