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(betterer ✨): allow negation of filters #831

Merged
merged 1 commit into from
Aug 23, 2021
Merged
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
3 changes: 2 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testRegex: '.*\\.spec\\.ts$',
watchPathIgnorePatterns: ['<rootDir>/fixtures', '<rootDir>/packages/[^/]+/src']
watchPathIgnorePatterns: ['<rootDir>/fixtures', '<rootDir>/packages/[^/]+/src'],
modulePathIgnorePatterns: ['<rootDir>/packages/extension']
};
34 changes: 32 additions & 2 deletions packages/betterer/src/suite/suite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { Defer, defer } from '../utils';
import { BettererSuiteSummaryΩ } from './suite-summary';
import { BettererSuite } from './types';

const NEGATIVE_FILTER_TOKEN = '!';

export class BettererSuiteΩ implements BettererSuite {
private _reporter: BettererReporterΩ;

Expand Down Expand Up @@ -60,9 +62,37 @@ export class BettererSuiteΩ implements BettererSuite {
runsΩ.map(async (runΩ, index) => {
const lifecycle = runLifecycles[index];

const isFiltered = filters.length && !filters.some((filter) => filter.test(runΩ.name));
// This is all a bit backwards because "filters" is named badly.
const hasFilters = !!filters.length;

// And this is some madness which applies filters and negative filters in
// the order they are read:
//
// ["foo"] => [/foo/] => ["foo"]
// ["foo"] => [/bar/] => []
// ["foo"] => [/!foo/] => []
// ["foo"] => [/!bar/] => ["foo"]
// ["foo"] => [/foo/, /!foo/] => []
// ["foo"] => [/!foo/, /foo/] => ["foo"]
const isSelected = filters.reduce((selected, filter) => {
const isNegated = filter.source.startsWith(NEGATIVE_FILTER_TOKEN);
if (selected) {
if (isNegated) {
const negativeFilter = new RegExp(filter.source.substr(1), filter.flags);
return !negativeFilter.test(runΩ.name);
}
return selected;
} else {
if (isNegated) {
const negativeFilter = new RegExp(filter.source.substr(1), filter.flags);
return !negativeFilter.test(runΩ.name);
}
return filter.test(runΩ.name);
}
}, false);

const isOtherTestOnly = hasOnly && !runΩ.testMeta.isOnly;
const isSkipped = isFiltered || isOtherTestOnly || runΩ.testMeta.isSkipped;
const isSkipped = (hasFilters && !isSelected) || isOtherTestOnly || runΩ.testMeta.isSkipped;

// Don't await here! A custom reporter could be awaiting
// the lifecycle promise which is unresolved right now!
Expand Down
6 changes: 5 additions & 1 deletion packages/cli/src/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,11 @@ function tsconfigPathOption(): void {
}

function filtersOption(): void {
commander.option('-f, --filter [value]', 'RegExp filter for tests to run. Takes multiple values', argsToArray);
commander.option(
'-f, --filter [value]',
'RegExp filter for tests to run. Add "!" at the start to negate. Takes multiple values',
argsToArray
);
}

function excludesOption(): void {
Expand Down
137 changes: 137 additions & 0 deletions test/cli/__snapshots__/filter-negative.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`betterer cli should filter tests by name with negation 1`] = `
Array [
"
/ | / _ _ _
'-.ooo.-' | |__ ___| |_| |_ ___ _ __ ___ _ __
---ooooo--- | '_ // _ / __| __/ _ / '__/ _ / '__|
.-'ooo'-. | |_)| __/ |_| || __/ | | __/ |
/ | / |_.__//___|/__|/__/___|_| /___|_|
",
"
/ | / _ _ _
'-.ooo.-' | |__ ___| |_| |_ ___ _ __ ___ _ __
---ooooo--- | '_ // _ / __| __/ _ / '__/ _ / '__|
.-'ooo'-. | |_)| __/ |_| || __/ | | __/ |
/ | / |_.__//___|/__|/__/___|_| /___|_|

🌟 Betterer (0ms):
",
"
/ | / _ _ _
'-.ooo.-' | |__ ___| |_| |_ ___ _ __ ___ _ __
---ooooo--- | '_ // _ / __| __/ _ / '__/ _ / '__|
.-'ooo'-. | |_)| __/ |_| || __/ | | __/ |
/ | / |_.__//___|/__|/__/___|_| /___|_|

🎉 Betterer (0ms): 3 tests done!
✅ test 1: \\"test 1\\" got checked for the first time! 🎉
✅ test 2: \\"test 2\\" got checked for the first time! 🎉
✅ test 3: \\"test 3\\" got checked for the first time! 🎉
",
"
/ | / _ _ _
'-.ooo.-' | |__ ___| |_| |_ ___ _ __ ___ _ __
---ooooo--- | '_ // _ / __| __/ _ / '__/ _ / '__|
.-'ooo'-. | |_)| __/ |_| || __/ | | __/ |
/ | / |_.__//___|/__|/__/___|_| /___|_|

🎉 Betterer (0ms): 3 tests done!
✅ test 1: \\"test 1\\" got checked for the first time! 🎉
✅ test 2: \\"test 2\\" got checked for the first time! 🎉
✅ test 3: \\"test 3\\" got checked for the first time! 🎉

3 tests got checked. 🤔
3 tests got checked for the first time! 🎉
",
"
/ | / _ _ _
'-.ooo.-' | |__ ___| |_| |_ ___ _ __ ___ _ __
---ooooo--- | '_ // _ / __| __/ _ / '__/ _ / '__|
.-'ooo'-. | |_)| __/ |_| || __/ | | __/ |
/ | / |_.__//___|/__|/__/___|_| /___|_|
",
"
/ | / _ _ _
'-.ooo.-' | |__ ___| |_| |_ ___ _ __ ___ _ __
---ooooo--- | '_ // _ / __| __/ _ / '__/ _ / '__|
.-'ooo'-. | |_)| __/ |_| || __/ | | __/ |
/ | / |_.__//___|/__|/__/___|_| /___|_|

🌟 Betterer (0ms):
",
"
/ | / _ _ _
'-.ooo.-' | |__ ___| |_| |_ ___ _ __ ___ _ __
---ooooo--- | '_ // _ / __| __/ _ / '__/ _ / '__|
.-'ooo'-. | |_)| __/ |_| || __/ | | __/ |
/ | / |_.__//___|/__|/__/___|_| /___|_|

🎉 Betterer (0ms): 3 tests done!
✅ test 1: \\"test 1\\" got skipped. 🚫
✅ test 2: \\"test 2\\" stayed the same. 😐
✅ test 3: \\"test 3\\" stayed the same. 😐
",
"
/ | / _ _ _
'-.ooo.-' | |__ ___| |_| |_ ___ _ __ ___ _ __
---ooooo--- | '_ // _ / __| __/ _ / '__/ _ / '__|
.-'ooo'-. | |_)| __/ |_| || __/ | | __/ |
/ | / |_.__//___|/__|/__/___|_| /___|_|

🎉 Betterer (0ms): 3 tests done!
✅ test 1: \\"test 1\\" got skipped. 🚫
✅ test 2: \\"test 2\\" stayed the same. 😐
✅ test 3: \\"test 3\\" stayed the same. 😐

2 tests got checked. 🤔
2 tests stayed the same. 😐
1 test got skipped. 🚫
",
"
/ | / _ _ _
'-.ooo.-' | |__ ___| |_| |_ ___ _ __ ___ _ __
---ooooo--- | '_ // _ / __| __/ _ / '__/ _ / '__|
.-'ooo'-. | |_)| __/ |_| || __/ | | __/ |
/ | / |_.__//___|/__|/__/___|_| /___|_|
",
"
/ | / _ _ _
'-.ooo.-' | |__ ___| |_| |_ ___ _ __ ___ _ __
---ooooo--- | '_ // _ / __| __/ _ / '__/ _ / '__|
.-'ooo'-. | |_)| __/ |_| || __/ | | __/ |
/ | / |_.__//___|/__|/__/___|_| /___|_|

🌟 Betterer (0ms):
",
"
/ | / _ _ _
'-.ooo.-' | |__ ___| |_| |_ ___ _ __ ___ _ __
---ooooo--- | '_ // _ / __| __/ _ / '__/ _ / '__|
.-'ooo'-. | |_)| __/ |_| || __/ | | __/ |
/ | / |_.__//___|/__|/__/___|_| /___|_|

🎉 Betterer (0ms): 3 tests done!
✅ test 1: \\"test 1\\" stayed the same. 😐
✅ test 2: \\"test 2\\" got skipped. 🚫
✅ test 3: \\"test 3\\" got skipped. 🚫
",
"
/ | / _ _ _
'-.ooo.-' | |__ ___| |_| |_ ___ _ __ ___ _ __
---ooooo--- | '_ // _ / __| __/ _ / '__/ _ / '__|
.-'ooo'-. | |_)| __/ |_| || __/ | | __/ |
/ | / |_.__//___|/__|/__/___|_| /___|_|

🎉 Betterer (0ms): 3 tests done!
✅ test 1: \\"test 1\\" stayed the same. 😐
✅ test 2: \\"test 2\\" got skipped. 🚫
✅ test 3: \\"test 3\\" got skipped. 🚫

1 test got checked. 🤔
1 test stayed the same. 😐
2 tests got skipped. 🚫
",
]
`;
55 changes: 55 additions & 0 deletions test/cli/filter-negative.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { startΔ } from '@betterer/cli';

import { createFixture } from '../fixture';

const ARGV = ['node', './bin/betterer'];

describe('betterer cli', () => {
it('should filter tests by name with negation', async () => {
const { logs, paths, cleanup, runNames } = await createFixture(
'filter-negative',
{
'.betterer.js': `
const { BettererTest } = require('@betterer/betterer');
const { bigger } = require('@betterer/constraints');

module.exports = {
'test 1': () => new BettererTest({
test: () => 0,
constraint: bigger
}),
'test 2': () => new BettererTest({
test: () => 0,
constraint: bigger
}),
'test 3': () => new BettererTest({
test: () => 0,
constraint: bigger
})
};
`
},
{
logFilters: [/: running /, /running.../]
}
);

const fixturePath = paths.cwd;

const firstRun = await startΔ(fixturePath, ARGV, false);

expect(runNames(firstRun.ran)).toEqual(['test 1', 'test 2', 'test 3']);

const secondRun = await startΔ(fixturePath, [...ARGV, '--filter', '!1'], false);

expect(runNames(secondRun.ran)).toEqual(['test 2', 'test 3']);

const thirdRun = await startΔ(fixturePath, [...ARGV, '--filter', 'test', '--filter', '![2|3]'], false);

expect(runNames(thirdRun.ran)).toEqual(['test 1']);

expect(logs).toMatchSnapshot();

await cleanup();
});
});