Skip to content

Commit

Permalink
[patch] fix pseudo regexp cases (#24)
Browse files Browse the repository at this point in the history
  • Loading branch information
mmisty authored Feb 19, 2024
1 parent 7dbe51e commit 25a5b97
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 35 deletions.
6 changes: 2 additions & 4 deletions README.pack.md
Original file line number Diff line number Diff line change
Expand Up @@ -280,10 +280,8 @@ Here are some examples of pseudo regexp:
- `GREP='@smoke&!@P2&!@P1'` - runs all tests with `smoke` and without `@P2` and without `@P1`
- `GREP='(@P1|@P2)&!@smoke'` or `GREP='(@P[12])&!@smoke'` - runs all tests with `@P2` or `@P1` and without `@smoke`

**Minor known issue with pseudo regexp**:

- line `@suite @test` will NOT match this expression: `GREP='(@test&@suite)|@tag'`(for `@test @suite` matches), as a workaround rewrite to `GREP='(@test|@tag)&(@suite|@tag)'`
- all other cases pass - you can check tested cases here if you are interested [tests/test-folder/regexp.test.ts](https://github.com/mmisty/cypress-grep/blob/main/tests/test-folder/regexp.test.ts#L5)
#### dev
- you can check tested cases here if you are interested [tests/test-folder/regexp.test.ts](https://github.com/mmisty/cypress-grep/blob/main/tests/test-folder/regexp.test.ts#L5)

### Select tests by regexp

Expand Down
84 changes: 65 additions & 19 deletions src/utils/regexp.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,83 @@
// when experession !(<expression>)
const transformReversed = (isInversed: boolean, s: string) => {
s = isInversed ? s.slice(2) : s;
s = isInversed ? s.slice(0, s.length - 2) : s;
type Replacement = { mapName: string; exp: string; inverse: boolean };

return s;
};
/**
* replace all parenthesis groups with placeholder
* @param input
* @param replacements
* @param num
*/
const replaceParenthesisGroups = (input: string, replacements: Replacement[], num = 1): string => {
let replaced = input;
const groupsNeg = input.match(/!\(([^()]*)\)/);
const groups = input.match(/\(([^()]*)\)/);

export const selectionTestGrep = (str: string): RegExp => {
if (str.startsWith('=')) {
// expressions like '=/hello$/i'
const beg = str.slice(1);
const endOfExpr = beg.lastIndexOf('/');
const flags = endOfExpr < beg.length - 1 ? beg.slice(endOfExpr + 1) : '';
const expr = beg.slice(beg.indexOf('/') + 1, endOfExpr);
if (!groupsNeg && !groups) {
return replaced;
}

return new RegExp(expr, flags);
const replaceExpression = (expression: string, group: string, inverse: boolean) => {
const mapName = `##R${num}##`;
replacements.push({ mapName, exp: group, inverse });
replaced = replaced.replace(expression, mapName);

return replaceParenthesisGroups(replaced, replacements, num + 1);
};

if (groupsNeg) {
return replaceExpression(groupsNeg[0], groupsNeg[1], true);
} else if (groups) {
return replaceExpression(groups[0], groups[1], false);
}

const isInverse = str.startsWith('!(');
const transformed = transformReversed(isInverse, str);
return replaced;
};

const reg = transformed
/**
* no parenthesis in the group
* @param exp
* @param inverse
*/
const convertOneGroup = (exp: string, inverse: boolean): string => {
const reg = exp
.split('/')
.map(t => (t.startsWith('!') ? t.replace(/^!(.*)/, '^(?!.*$1.*)') : t))
.map(t =>
t.indexOf('&') !== -1
? `${t
.split('&')
.map(nd => (nd.startsWith('!') ? nd.replace(/^!(.*)/, '^(?!.*$1)') : nd.replace(/^(.*)/, '(?=.*$1)')))
.map(nd => (nd.startsWith('!') ? nd.replace(/^!(.*)/, '^(?!.*$1.*)') : nd.replace(/^(.*)/, '(?=.*$1)')))
.join('+')}+`
: t,
)
.join('|');

return new RegExp(isInverse ? `^(?!.*${reg}).*` : `${reg}.*`, 'i');
const res = reg.indexOf('|') !== -1 ? `(${reg})` : reg;

return inverse ? `^(?!.*${res}.*)` : `${res}`;
};

export const selectionTestGrep = (str: string): RegExp => {
if (str.startsWith('=')) {
// expressions like '=/hello$/i'
const beg = str.slice(1);
const endOfExpr = beg.lastIndexOf('/');
const flags = endOfExpr < beg.length - 1 ? beg.slice(endOfExpr + 1) : '';
const expr = beg.slice(beg.indexOf('/') + 1, endOfExpr);

return new RegExp(expr, flags);
}

const replacements: Replacement[] = [];
const replacedString = replaceParenthesisGroups(str, replacements);
let convertedString = convertOneGroup(replacedString, false);
const groups = replacements.map(t => ({ ...t, reg: convertOneGroup(t.exp, t.inverse) }));

// last group should be converted first
groups
.sort((a, b) => (a.mapName > b.mapName ? -1 : 1))
.forEach(r => {
convertedString = convertedString.replace(r.mapName, r.reg);
});

return new RegExp(`${convertedString}.*`, 'i');
};
57 changes: 45 additions & 12 deletions tests/test-folder/regexp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,39 @@ describe('suite', () => {
{ expectMatch: false, testLine: 'no' },
],
},
{
desc: 'parenthesis',
GREP: '(my test)',
regExpected: /my test.*/i,
cases: [
{ expectMatch: true, testLine: 'hello my test' },
{ expectMatch: false, testLine: 'my tes hello ' },
],
},
{
desc: 'parenthesis several',
GREP: '!(my test)&!(his test)',
regExpected: /(?=.*^(?!.*my test.*))+(?=.*^(?!.*his test.*))+.*/i,
cases: [
{ expectMatch: true, testLine: 'her test' },
{ expectMatch: false, testLine: 'my test' },
{ expectMatch: false, testLine: 'his test' },
{ expectMatch: true, testLine: 'his tesPP' },
],
},
{
desc: 'parenthesis several complex',
GREP: '(((her)&!(my test|his test)))',
regExpected: /(?=.*her)+(?=.*^(?!.*(my test|his test).*))+.*/i,
cases: [
{ expectMatch: true, testLine: 'test her' },
{ expectMatch: true, testLine: 'her test' },
{ expectMatch: false, testLine: 'her his test' },
{ expectMatch: false, testLine: 'her my test' },
{ expectMatch: false, testLine: 'my test' },
{ expectMatch: false, testLine: 'his test' },
],
},
{
desc: 'tag with dot encoded',
GREP: '@test\\.1',
Expand Down Expand Up @@ -71,7 +104,7 @@ describe('suite', () => {
{
desc: 'or',
GREP: '@e2e|@regression',
regExpected: /@e2e|@regression.*/i,
regExpected: /(@e2e|@regression).*/i,
cases: [
{ expectMatch: true, testLine: 'sdd @e2e dsd' },
{ expectMatch: true, testLine: 'sd @regression' },
Expand All @@ -84,7 +117,7 @@ describe('suite', () => {
{
desc: 'or',
GREP: '@e2e/@regression',
regExpected: /@e2e|@regression.*/i,
regExpected: /(@e2e|@regression).*/i,
cases: [
{ expectMatch: true, testLine: 'sdd @e2e dsd' },
{ expectMatch: true, testLine: 'sd @regression' },
Expand Down Expand Up @@ -127,7 +160,7 @@ describe('suite', () => {
{
desc: 'tag and not',
GREP: '@test&!suite',
regExpected: /(?=.*@test)+^(?!.*suite)+.*/i,
regExpected: /(?=.*@test)+^(?!.*suite.*)+.*/i,
cases: [
{ expectMatch: true, testLine: 'abc @test my world' },
{ expectMatch: true, testLine: '@test' },
Expand All @@ -140,7 +173,7 @@ describe('suite', () => {
{
desc: 'not and not',
GREP: '!@test&!@suite',
regExpected: /(?=.*^(?!.*@test)+^(?!.*@suite.*))+.*/i,
regExpected: /(?=.*^(?!.*@test)+^(?!.*@suite.*).*)+.*/i,
cases: [
{ expectMatch: true, testLine: 'abc my world' },
{ expectMatch: false, testLine: '@test' },
Expand All @@ -154,9 +187,10 @@ describe('suite', () => {
{
desc: 'not and not Inversed',
GREP: '!(!@test&!@suite)',
regExpected: /^(?!.*(?=.*^(?!.*@test)+^(?!.*@suit.*))+).*/i,
regExpected: /^(?!.*(?=.*^(?!.*@test)+^(?!.*@suite.*).*)+.*).*/i,
cases: [
{ expectMatch: false, testLine: 'abc my world' },
{ expectMatch: false, testLine: '@suit abc my' },
{ expectMatch: true, testLine: '@test' },
{ expectMatch: true, testLine: '@TEST' },
{ expectMatch: true, testLine: 'abc @test my suite' },
Expand All @@ -182,7 +216,7 @@ describe('suite', () => {
{
desc: 'not and or combination different order',
GREP: '(@suite|@tag)&!@test',
regExpected: /(?=.*(@suite|@tag))+^(?!.*@test)+.*/i,
regExpected: /(?=.*(@suite|@tag))+^(?!.*@test.*)+.*/i,
cases: [
{ expectMatch: false, testLine: 'abc my @test world' },
{ expectMatch: false, testLine: 'abc @suite @tag my @test world' },
Expand All @@ -197,16 +231,15 @@ describe('suite', () => {
{
desc: 'and with parenthesis and or combination',
GREP: '(@test&@suite)|@tag',
regExpected: /(?=.*(@test)+(?=.*@suite)|@tag)+.*/i,
regExpected: /((?=.*@test)+(?=.*@suite)+|@tag).*/i,
cases: [
{ expectMatch: true, testLine: 'abc my @test @world @suite' },
{ expectMatch: false, testLine: 'abc my @suite' },
{ expectMatch: false, testLine: 'abc my @test' },
{ expectMatch: true, testLine: 'abc my @tag' },
{ expectMatch: true, testLine: 'abc my @test @tag' },
{ expectMatch: true, testLine: 'abc@suite my @test @tag' },
// this case does not work
{ expectMatch: true, testLine: '@suite @test', fixThis: true, actualResult: false },
{ expectMatch: true, testLine: '@suite @test' },
],
},
{
Expand All @@ -226,10 +259,11 @@ describe('suite', () => {
{
desc: 'not with parenthesis',
GREP: '!(@test&@suite)',
regExpected: /^(?!.*(?=.*@test)+(?=.*@suit)+).*/i,
regExpected: /^(?!.*(?=.*@test)+(?=.*@suite)+.*).*/i,
cases: [
{ expectMatch: false, testLine: '@test @suite' },
{ expectMatch: false, testLine: '@suite @test' },
{ expectMatch: true, testLine: '@suit @test' },
{ expectMatch: true, testLine: 'no suite test' },
{ expectMatch: true, testLine: 'only @test' },
{ expectMatch: true, testLine: 'only @TEST' },
Expand All @@ -239,7 +273,6 @@ describe('suite', () => {
])
.each(t => [{ desc: `: '${t.GREP}'` }])
.each(t => t.cases)

// .only(t => t.id === '1')
.run(t => {
const regActual = selectionTestGrep(t.GREP);
Expand All @@ -251,7 +284,7 @@ describe('suite', () => {
}

// remove actualResult when fixed
if (t.actualResult ?? t.expectMatch) {
if (t.expectMatch) {
expect(t.testLine).toMatch(regActual);
} else {
expect(t.testLine).not.toMatch(regActual);
Expand Down

0 comments on commit 25a5b97

Please sign in to comment.