From 9e7f25ab878c0fc40ddc1c53a76c7a1c62768e99 Mon Sep 17 00:00:00 2001 From: Tim Deschryver <28659384+timdeschryver@users.noreply.github.com> Date: Tue, 22 Mar 2022 19:26:38 +0100 Subject: [PATCH] fix: prevent state on regexp with global flag --- src/__tests__/matches.js | 16 ++++++++++++++++ src/matches.ts | 15 +++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/__tests__/matches.js b/src/__tests__/matches.js index 3d512e65..629c283b 100644 --- a/src/__tests__/matches.js +++ b/src/__tests__/matches.js @@ -15,6 +15,22 @@ test('matchers accept regex', () => { expect(fuzzyMatches('ABC', node, /ABC/, normalizer)).toBe(true) }) +// https://stackoverflow.com/questions/1520800/why-does-a-regexp-with-global-flag-give-wrong-results +test('a regex with the global flag consistently (re-)finds a match', () => { + const regex = /ABC/g + const spy = jest.spyOn(console, 'warn').mockImplementation() + + expect(matches('ABC', node, regex, normalizer)).toBe(true) + expect(fuzzyMatches('ABC', node, regex, normalizer)).toBe(true) + + expect(spy).toBeCalledTimes(2) + expect(spy).toHaveBeenCalledWith( + `To match all elements we had to reset the lastIndex of the RegExp because the global flag is enabled. We encourage to remove the global flag from the RegExp.`, + ) + + console.warn.mockClear() +}) + test('matchers accept functions', () => { expect(matches('ABC', node, text => text === 'ABC', normalizer)).toBe(true) expect(fuzzyMatches('ABC', node, text => text === 'ABC', normalizer)).toBe( diff --git a/src/matches.ts b/src/matches.ts index 31670d1d..c49c7cca 100644 --- a/src/matches.ts +++ b/src/matches.ts @@ -36,7 +36,7 @@ function fuzzyMatches( } else if (typeof matcher === 'function') { return matcher(normalizedText, node) } else { - return matcher.test(normalizedText) + return matchRegExp(matcher, normalizedText) } } @@ -56,7 +56,7 @@ function matches( if (matcher instanceof Function) { return matcher(normalizedText, node) } else if (matcher instanceof RegExp) { - return matcher.test(normalizedText) + return matchRegExp(matcher, normalizedText) } else { return normalizedText === String(matcher) } @@ -111,4 +111,15 @@ function makeNormalizer({ return normalizer } +function matchRegExp(matcher: RegExp, text: string) { + const match = matcher.test(text) + if (matcher.global && matcher.lastIndex !== 0) { + console.warn( + `To match all elements we had to reset the lastIndex of the RegExp because the global flag is enabled. We encourage to remove the global flag from the RegExp.`, + ) + matcher.lastIndex = 0 + } + return match +} + export {fuzzyMatches, matches, getDefaultNormalizer, makeNormalizer}