Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Allow random order of globs #11293

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion packages/jest-util/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
"devDependencies": {
"@types/graceful-fs": "^4.1.2",
"@types/is-ci": "^2.0.0",
"@types/micromatch": "^4.0.1",
"@types/picomatch": "^2.2.2"
},
"engines": {
Expand Down
90 changes: 35 additions & 55 deletions packages/jest-util/src/__tests__/globsToMatcher.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,94 +5,74 @@
* LICENSE file in the root directory of this source tree.
*/

import micromatch = require('micromatch');
import globsToMatcher from '../globsToMatcher';

it('works like micromatch with only positive globs', () => {
it('works with only positive globs', () => {
const globs = ['**/*.test.js', '**/*.test.jsx'];
const matcher = globsToMatcher(globs);

expect(matcher('some-module.js')).toBe(
micromatch(['some-module.js'], globs).length > 0,
);

expect(matcher('some-module.test.js')).toBe(
micromatch(['some-module.test.js'], globs).length > 0,
);
expect(matcher('some-module.js')).toBe(false);
expect(matcher('some-module.test.js')).toBe(true);
});

it('works like micromatch with a mix of overlapping positive and negative globs', () => {
it('works with a mix of overlapping positive and negative globs', () => {
const globs = ['**/*.js', '!**/*.test.js', '**/*.test.js'];
const matcher = globsToMatcher(globs);

expect(matcher('some-module.js')).toBe(
micromatch(['some-module.js'], globs).length > 0,
);

expect(matcher('some-module.test.js')).toBe(
micromatch(['some-module.test.js'], globs).length > 0,
);
expect(matcher('some-module.js')).toBe(true);
expect(matcher('some-module.test.js')).toBe(false);

const globs2 = ['**/*.js', '!**/*.test.js', '**/*.test.js', '!**/*.test.js'];
const matcher2 = globsToMatcher(globs2);

expect(matcher2('some-module.js')).toBe(
micromatch(['some-module.js'], globs2).length > 0,
);

expect(matcher2('some-module.test.js')).toBe(
micromatch(['some-module.test.js'], globs2).length > 0,
);
expect(matcher2('some-module.js')).toBe(true);
expect(matcher2('some-module.test.js')).toBe(false);
});

it('works like micromatch with only negative globs', () => {
it('works with only negative globs', () => {
const globs = ['!**/*.test.js', '!**/*.test.jsx'];
const matcher = globsToMatcher(globs);

expect(matcher('some-module.js')).toBe(
micromatch(['some-module.js'], globs).length > 0,
);

expect(matcher('some-module.test.js')).toBe(
micromatch(['some-module.test.js'], globs).length > 0,
);
expect(matcher('some-module.js')).toBe(true);
expect(matcher('some-module.test.js')).toBe(false);
});

it('works like micromatch with empty globs', () => {
it('works with empty globs', () => {
const globs = [];
const matcher = globsToMatcher(globs);

expect(matcher('some-module.js')).toBe(
micromatch(['some-module.js'], globs).length > 0,
);

expect(matcher('some-module.test.js')).toBe(
micromatch(['some-module.test.js'], globs).length > 0,
);
expect(matcher('some-module.js')).toBe(false);
expect(matcher('some-module.test.js')).toBe(false);
});

it('works like micromatch with pure negated extglobs', () => {
it('works with pure negated extglobs', () => {
const globs = ['**/*.js', '!(some-module.test.js)'];
const matcher = globsToMatcher(globs);

expect(matcher('some-module.js')).toBe(
micromatch(['some-module.js'], globs).length > 0,
);

expect(matcher('some-module.test.js')).toBe(
micromatch(['some-module.test.js'], globs).length > 0,
);
expect(matcher('some-module.js')).toBe(true);
expect(matcher('some-module.test.js')).toBe(false);
});

it('works like micromatch with negated extglobs', () => {
it('works with negated extglobs', () => {
const globs = ['**/*.js', '!(tests|coverage)/*.js'];
const matcher = globsToMatcher(globs);

expect(matcher('some-module.js')).toBe(
micromatch(['some-module.js'], globs).length > 0,
);
expect(matcher('some-module.js')).toBe(true);
expect(matcher('tests/some-module.test.js')).toBe(false);
});

it('works with negated extglobs in reverse order', () => {
const globs = ['!(tests|coverage)/*.js', '**/*.js'];
const matcher = globsToMatcher(globs);

expect(matcher('some-module.js')).toBe(true);
expect(matcher('tests/some-module.test.js')).toBe(false);
});

it('works with negated glob in reverse order', () => {
const globs = ['!tests/*.js', '**/*.js'];
const matcher = globsToMatcher(globs);

expect(matcher('tests/some-module.test.js')).toBe(
micromatch(['tests/some-module.test.js'], globs).length > 0,
);
expect(matcher('some-module.js')).toBe(true);
expect(matcher('tests/some-module.test.js')).toBe(false);
});
35 changes: 23 additions & 12 deletions packages/jest-util/src/globsToMatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ const picomatchOptions = {dot: true};
* matchers ahead of time and then have an optimized path for determining
* whether an individual path matches.
*
* This function is intended to match the behavior of `micromatch()`.
* This function is based on the behavior of `micromatch()` version 3.
* micromatch version 4 does respect order which does not make sense here, so
* globs can appear in any order and will always yield the same result.
*
* @example
* const isMatch = globsToMatcher(['*.js', '!*.test.js']);
Expand All @@ -44,13 +46,17 @@ export default function globsToMatcher(globs: Array<Config.Glob>): Matcher {

const matchers = globs.map(glob => {
if (!globsToMatchersMap.has(glob)) {
const isMatch = picomatch(glob, picomatchOptions, true);
const negated = glob.startsWith('!');
const isMatch = picomatch(
negated ? glob.slice(1) : glob,
Copy link
Contributor Author

@danez danez Apr 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This glob.slice(1) is the fix for the "wrong" negation behavior mentioned in the main PR text.
By removing the exclamation mark we do match the ignored path but later know because of the negated flag that a match means ignore this file.

picomatchOptions,
);

const matcher = {
isMatch,
// Matchers that are negated have different behavior than matchers that
// are not negated, so we need to store this information ahead of time.
negated: isMatch.state.negated || !!isMatch.state.negatedExtglob,
negated,
};

globsToMatchersMap.set(glob, matcher);
Expand All @@ -69,20 +75,25 @@ export default function globsToMatcher(globs: Array<Config.Glob>): Matcher {

if (negated) {
negatives++;
} else if (kept !== undefined) {
// The current pattern is not negated and we already have `kept` set to
// either true or false, so we do not need to do anything, because if
// kept===true we don't need to check if it matches anything more and if
// kept===false the file is already ignored and we do not want to
// overwrite that.
continue;
}

const matched = isMatch(replacedPath);

if (!matched && negated) {
// The path was not matched, and the matcher is a negated matcher, so we
// want to omit the path. This means that the negative matcher is
// filtering the path out.
kept = false;
} else if (matched && !negated) {
// The path was matched, and the matcher is not a negated matcher, so we
// want to keep the path.
kept = true;
if (!matched) {
continue;
}

// The path was matched, and if the matcher is a negated matcher, we
// want to omit the path. If the matcher is not a negated matcher we
// want to keep the path.
kept = !negated;
}

// If all of the globs were negative globs, then we want to include the path
Expand Down
1 change: 0 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -14015,7 +14015,6 @@ fsevents@^1.2.7:
"@jest/types": ^27.0.0-next.8
"@types/graceful-fs": ^4.1.2
"@types/is-ci": ^2.0.0
"@types/micromatch": ^4.0.1
"@types/node": "*"
"@types/picomatch": ^2.2.2
chalk: ^4.0.0
Expand Down