Merge pull request #54 from bkeepers/not

Not
This commit is contained in:
Brandon Keepers 2016-11-03 21:14:34 -05:00 committed by GitHub
commit a26d96ba27
7 changed files with 95 additions and 16 deletions

View File

@ -81,14 +81,17 @@ if @issue.body contains "- [ ]"
#### Operations
Operator | Description | Example
------------|-------------------------------|-----------------------------------
`is` | equal | `@sender.login is "hubot"`
`is not` | not equal | `@sender.login is not "hubot"`
`contains` | string contains a substring | `@issue.body contains "- [ ]"`
`matches` | matches a regular expression | `@issue.title matches "^\[?WIP\]?"`
`and` | logical and | `labeled(bug) and @sender.login is "hubot"`
`or` | logical or | `labeled(bug) or labeled(defect)`
Operator | Description | Example
--------------------|-------------------------------------|-----------------------------------
`is` | equal | `@sender.login is "hubot"`
`is not` | not equal | `@sender.login is not "hubot"`
`contains` | string contains a substring | `@issue.body contains "- [ ]"`
`does not contain` | string does not contain a substring | `@issue.body does not contain "- [x]"`
`matches` | matches a regular expression | `@issue.title matches "^\[?WIP\]?"`
`does not match` | does not match a regular expression | `@issue.title does not match "v\d+\.\d+"`
`and` | logical and | `labeled(bug) and @sender.login is "hubot"`
`or` | logical or | `labeled(bug) or labeled(defect)`
`not` | negate a condition | `not labeled(bug)`
#### Functions

View File

@ -1,10 +1,12 @@
const operators = {
'is': (left, right) => left === right,
'is not': (left, right) => left !== right,
'or': (left, right) => left || right,
'and': (left, right) => left && right,
'contains': (left, right) => left.includes(right),
'matches': (left, right) => left.match(right)
'is': (left, right) => left === right,
'is not': (left, right) => left !== right,
'contains': (left, right) => left.includes(right),
'does not contain': (left, right) => !left.includes(right),
'matches': (left, right) => left.match(right),
'does not match': (left, right) => !left.match(right),
'or': (left, right) => left || right,
'and': (left, right) => left && right
};
function resolve(value, context) {

View File

@ -71,7 +71,13 @@ RelationalExpression
= head:LogicalOrExpression tail:(ws RelationalOperator ws LogicalOrExpression)* {
return logicalExpression(head, tail);
}
RelationalOperator = "is not" / "is" / "contains" / "matches"
RelationalOperator
= "is not"
/ "is"
/ "does not contain"
/ "contains"
/ "does not match"
/ "matches"
LogicalOrExpression
= head:LogicalAndExpression tail:(ws or ws LogicalAndExpression)* {
@ -79,10 +85,21 @@ LogicalOrExpression
}
LogicalAndExpression
= head:operand tail:(ws and ws operand)* {
= head:UnaryExpression tail:(ws and ws UnaryExpression)* {
return logicalExpression(head, tail);
}
UnaryExpression
= operand
/ operator:UnaryOperator ws argument:UnaryExpression {
return {
type: 'UnaryExpression',
operator: operator,
argument: argument
}
}
UnaryOperator = "not"
operand = condition / attribute / string / boolean
condition

View File

@ -62,4 +62,10 @@ module.exports = class Transformer {
return logicalExpression(context, node.left, node.operator, node.right);
};
}
UnaryExpression(node) {
return context => {
return !node.argument(context);
};
}
};

View File

@ -43,6 +43,16 @@ describe('conditions', () => {
});
});
describe('does not contain', () => {
it('fails when operand contains substring', () => {
expect(test('if @issue.title does not contain "bug"')).toBeFalsy();
});
it('passes when operand does not contain substring', () => {
expect(test('if @issue.title does not contain "nope"')).toBeTruthy();
});
});
describe('matches', () => {
it('passes when operand matches regexp', () => {
expect(test('if @sender.login matches "ke+"')).toBeTruthy();
@ -53,6 +63,16 @@ describe('conditions', () => {
});
});
describe('does not match', () => {
it('fails when operand matches regexp', () => {
expect(test('if @sender.login does not match "ke+"')).toBeFalsy();
});
it('passes when operand does not match regexp', () => {
expect(test('if @issue.title does not match "nope"')).toBeTruthy();
});
});
describe('or', () => {
it('passes if either operand is truthy', () => {
expect(test('if labeled(bug) or labeled(feature)')).toBeTruthy();
@ -72,4 +92,14 @@ describe('conditions', () => {
expect(test('if labeled(bug) and labeled(nope)')).toBeFalsy();
});
});
describe('not', () => {
it('passes if condition is not truthy', () => {
expect(test('if not labeled(something)')).toBeTruthy();
});
it('fails if condition is truthy', () => {
expect(test('if not labeled(bug)')).toBeFalsy();
});
});
});

View File

@ -16,3 +16,14 @@ then comment("No bots allowed!") and close;
on issues.opened if @sender.login is "bkeepers" then close;
on issues.opened if @sender.login is not "bkeepers" then close;
on issues.opened if @issue.body contains "- [ ]" then label(wip);
on issues.opened
if not labeled(bug)
then close;
on issues.opened
if @issue.body does not match "### Prerequisites.*### Description.*### Steps to Reproduce.*### Versions"
or @issue.body.body contains "- [ ]"
then
label("insufficient-info")
and close;

View File

@ -127,6 +127,16 @@ describe('parser', () => {
});
});
describe('not', () => {
it('negates conditions', () => {
expect(parse('if not labeled(bug)')).toEqual({
type: 'UnaryExpression',
operator: 'not',
argument: {type: 'condition', name: 'labeled', value: 'bug'}
});
});
});
describe('precedence', () => {
it('orders "and" over "or"', () => {
expect(parse('if true and false or true')).toEqual({