Skip to content

Commit 13a628b

Browse files
feat: Add Not operator (#695)
* feat: Add Not operator * Update changelog.rst * Fix code style
1 parent 3c3fe59 commit 13a628b

File tree

15 files changed

+456
-20
lines changed

15 files changed

+456
-20
lines changed

__tests__/unit/filters/not.test.js

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
const Not = require('../../../lib/filters/not')
2+
const Helper = require('../../../__fixtures__/unit/helper')
3+
4+
describe('Not Filter Unit Test', () => {
5+
let registry = { filters: new Map(), actions: new Map() }
6+
7+
beforeEach(() => {
8+
registry = { filters: new Map(), actions: new Map() }
9+
})
10+
11+
test('Should pass if subtasks fails', async () => {
12+
const not = new Not()
13+
const settings = {
14+
do: 'not',
15+
filter: [
16+
{
17+
do: 'repository',
18+
topics: {
19+
must_include: {
20+
regex: 'Topic 2'
21+
}
22+
}
23+
}
24+
]
25+
}
26+
const filter = await not.processFilter(createMockContext(['Topic 1']), settings, registry)
27+
expect(filter.status).toBe('pass')
28+
})
29+
30+
test('Should fail if subtasks passes', async () => {
31+
const not = new Not()
32+
const settings = {
33+
do: 'not',
34+
filter: [
35+
{
36+
do: 'repository',
37+
topics: {
38+
must_include: {
39+
regex: 'Topic 1'
40+
}
41+
}
42+
}
43+
]
44+
}
45+
const filter = await not.processFilter(createMockContext(['Topic 1']), settings, registry)
46+
expect(filter.status).toBe('fail')
47+
})
48+
49+
test('Error is returned when filter has more than one item', async () => {
50+
// TODO
51+
const not = new Not()
52+
const settings = {
53+
do: 'not'
54+
}
55+
const filter = await not.processFilter(createMockContext(['Topic 1']), settings, registry)
56+
expect(filter.status).toBe('error')
57+
})
58+
59+
test('Error is returned when filter is missing', async () => {
60+
const not = new Not()
61+
const settings = {
62+
do: 'not'
63+
}
64+
const filter = await not.processFilter(createMockContext(['Topic 1']), settings, registry)
65+
expect(filter.status).toBe('error')
66+
})
67+
68+
test('Error is returned when filter is not an array', async () => {
69+
const not = new Not()
70+
const settings = {
71+
do: 'not',
72+
filter: ''
73+
}
74+
const filter = await not.processFilter(createMockContext(['Topic 1']), settings, registry)
75+
expect(filter.status).toBe('error')
76+
})
77+
78+
test('Error is returned when filter is empty', async () => {
79+
const not = new Not()
80+
const settings = {
81+
do: 'not',
82+
filter: []
83+
}
84+
const filter = await not.processFilter(createMockContext(['Topic 1']), settings, registry)
85+
expect(filter.status).toBe('error')
86+
})
87+
88+
test('Error is returned when filter uses unsupported classes', async () => {
89+
const not = new Not()
90+
const settings = {
91+
do: 'not',
92+
filter: [
93+
{ do: 'missing' }
94+
]
95+
}
96+
const filter = await not.processFilter(createMockContext(['Topic 1']), settings, registry)
97+
expect(filter.status).toBe('error')
98+
})
99+
100+
test('Error if one of the sub filters errored', async () => {
101+
const not = new Not()
102+
const settings = {
103+
do: 'not',
104+
filter: [
105+
{
106+
do: 'not',
107+
filter: [
108+
{
109+
do: 'repository',
110+
topics: {
111+
// invalid syntax => error
112+
must_inclxude: {
113+
regex: 'Topic 1'
114+
}
115+
}
116+
},
117+
{
118+
do: 'repository',
119+
topics: {
120+
must_include: {
121+
regex: 'Topic 2'
122+
}
123+
}
124+
}
125+
]
126+
},
127+
{
128+
do: 'repository',
129+
topics: {
130+
must_include: {
131+
regex: 'Version 3'
132+
}
133+
}
134+
}
135+
]
136+
}
137+
138+
const filter = await not.processFilter(createMockContext(['Topic 2']), settings, registry)
139+
expect(filter.status).toBe('error')
140+
})
141+
})
142+
143+
const createMockContext = (repoTopics = []) => {
144+
const context = Helper.mockContext({ repoTopics: repoTopics })
145+
return context
146+
}

__tests__/unit/validators/not.test.js

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
const Not = require('../../../lib/validators/not')
2+
const Helper = require('../../../__fixtures__/unit/helper')
3+
4+
describe('Not Validator Unit Test', () => {
5+
let registry = { validators: new Map(), actions: new Map() }
6+
7+
beforeEach(() => {
8+
registry = { validators: new Map(), actions: new Map() }
9+
})
10+
11+
test('Should pass if subtask fails', async () => {
12+
const not = new Not()
13+
const settings = {
14+
do: 'not',
15+
validate: [
16+
{
17+
do: 'milestone',
18+
must_include: {
19+
regex: 'Version 2'
20+
}
21+
}
22+
]
23+
}
24+
const validation = await not.processValidate(createMockContext({ title: 'Version 1' }), settings, registry)
25+
expect(validation.status).toBe('pass')
26+
})
27+
28+
test('Should fail if subtask passes', async () => {
29+
const and = new Not()
30+
const settings = {
31+
do: 'not',
32+
validate: [
33+
{
34+
do: 'milestone',
35+
must_include: {
36+
regex: 'Version 2'
37+
}
38+
}
39+
]
40+
}
41+
const validation = await and.processValidate(createMockContext({ title: 'Version 2' }), settings, registry)
42+
expect(validation.status).toBe('fail')
43+
})
44+
45+
test('Error is returned when validate has more than one item', async () => {
46+
const and = new Not()
47+
const settings = {
48+
do: 'not',
49+
validate: [
50+
{
51+
do: 'milestone',
52+
must_include: {
53+
regex: 'Version 2'
54+
}
55+
},
56+
{
57+
do: 'milestone',
58+
must_include: {
59+
regex: 'Version 1'
60+
}
61+
}
62+
]
63+
}
64+
const validation = await and.processValidate(createMockContext({ title: 'Version 1' }), settings, registry)
65+
expect(validation.status).toBe('error')
66+
})
67+
68+
test('Error is returned when validate is missing', async () => {
69+
const and = new Not()
70+
const settings = {
71+
do: 'not'
72+
}
73+
const validation = await and.processValidate(createMockContext({ title: 'Version 1' }), settings, registry)
74+
expect(validation.status).toBe('error')
75+
})
76+
77+
test('Error is returned when validate is not an array', async () => {
78+
const and = new Not()
79+
const settings = {
80+
do: 'not',
81+
validate: ''
82+
}
83+
const validation = await and.processValidate(createMockContext({ title: 'Version 1' }), settings, registry)
84+
expect(validation.status).toBe('error')
85+
})
86+
87+
test('Error is returned when validate is empty', async () => {
88+
const and = new Not()
89+
const settings = {
90+
do: 'and',
91+
validate: []
92+
}
93+
const validation = await and.processValidate(createMockContext({ title: 'Version 1' }), settings, registry)
94+
expect(validation.status).toBe('error')
95+
})
96+
97+
test('Error is returned when validate uses unsupported classes', async () => {
98+
const and = new Not()
99+
const settings = {
100+
do: 'and',
101+
validate: [
102+
{ do: 'missing' }
103+
]
104+
}
105+
const validation = await and.processValidate(createMockContext({ title: 'Version 1' }), settings, registry)
106+
expect(validation.status).toBe('error')
107+
})
108+
109+
test('Error if the sub validator errored', async () => {
110+
const and = new Not()
111+
const settings = {
112+
do: 'and',
113+
validate: [
114+
{
115+
do: 'and',
116+
validate: [
117+
{
118+
do: 'milestone',
119+
// invalid syntax => error
120+
must_inclxude: {
121+
regex: 'Version 1'
122+
}
123+
},
124+
{
125+
do: 'milestone',
126+
must_include: {
127+
regex: 'Version 2'
128+
}
129+
}
130+
]
131+
},
132+
{
133+
do: 'milestone',
134+
must_include: {
135+
regex: 'Version 3'
136+
}
137+
}
138+
]
139+
}
140+
141+
const validation = await and.processValidate(createMockContext({ title: 'Version 2' }), settings, registry)
142+
expect(validation.status).toBe('error')
143+
})
144+
})
145+
146+
const createMockContext = (milestone, body, deepValidation) => {
147+
return Helper.mockContext({ milestone, body, deepValidation })
148+
}

docs/changelog.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
CHANGELOG
22
=====================================
3+
| February 3, 2023: feat: Add Not operator `#695 <https://github.com/mergeability/mergeable/pull/695>`_
34
| September 28, 2022: feat: Add last comment validator `#668 <https://github.com/mergeability/mergeable/pull/668>`_
45
| August 26, 2022: set fallback for `no_empty` options processor `#657 <https://github.com/mergeability/mergeable/pull/657>`_
56
| August 25, 2022: fix: set fallback value for `description` payload `#655 <https://github.com/mergeability/mergeable/pull/655>`_

docs/configuration.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,7 @@ Operator List
159159
.. toctree::
160160
operators/and.rst
161161
operators/or.rst
162+
operators/not.rst
162163

163164

164165
Actions

docs/operators/and.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
And
22
^^^^^^^^^^
33

4-
``And`` and ``Or`` can be used to create more complex validation/filter check
4+
``And``, ``Or``, and ``Not`` can be used to create more complex validation/filter check
55

66
::
77

@@ -20,7 +20,7 @@ And
2020
- do: label
2121
must_include: 'Ready to Merge'
2222

23-
you can also create nested ``And`` and ``Or``
23+
you can also create nested ``And``, ``Or``, and ``Not``
2424

2525
::
2626

docs/operators/not.rst

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
Not
2+
^^^^^^^^^^
3+
4+
``And``, ``Or``, and ``Not`` can be used to create more complex validation/filter check
5+
6+
::
7+
8+
filter:
9+
- do: not
10+
filter:
11+
- do: author
12+
must_include: 'user-1'
13+
- do: repository
14+
visibility: public
15+
validate:
16+
- do: not
17+
validate:
18+
- do: title
19+
begins_with: '[WIP]'
20+
- do: label
21+
must_include: 'Ready to Merge'
22+
23+
you can also create nested ``And``, ``Or``, and ``Not``
24+
25+
::
26+
27+
filter:
28+
- do: not
29+
filter:
30+
- do: or
31+
filter:
32+
- do: author
33+
must_include: 'user-1'
34+
- do: author
35+
must_include: 'user-2'
36+
validate:
37+
- do: and
38+
validate:
39+
- do: not
40+
validate:
41+
- do: title
42+
begins_with: 'feat:'
43+
- do: label
44+
must_include: 'feature'
45+
- do: label
46+
must_include: 'Ready to Merge'

docs/operators/or.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Or
22
^^^^^^^^^^
33

4-
``And`` and ``Or`` can be used to create more complex validation/filter check
4+
``And``, ``Or``, and ``Not`` can be used to create more complex validation/filter check
55

66
::
77

@@ -20,7 +20,7 @@ Or
2020
- do: label
2121
must_include: 'Ready to Merge'
2222

23-
you can also create nested ``And`` and ``Or``
23+
you can also create nested ``And``, ``Or``, and ``Not``
2424

2525
::
2626

0 commit comments

Comments
 (0)