nodebook-nbql
Version:
Filter query language for nodebook
437 lines (378 loc) • 15.6 kB
JavaScript
/* globals describe, it */
/* jshint unused:false */
var should = require('should'),
nbql = require('../lib/nbql');
describe('Parser', function () {
var parserError = /^Query Error: unexpected character in filter at char/;
describe('Operators', function () {
it('can parse standard equals', function () {
nbql.parse('count:5').should.eql({
statements: [
{prop: 'count', op: '=', value: 5}
]
});
nbql.parse('tag:getting-started').should.eql({
statements: [
{prop: 'tag', op: '=', value: 'getting-started'}
]
});
nbql.parse('author:\'Joe Bloggs\'').should.eql({
statements: [
{prop: 'author', op: '=', value: 'Joe Bloggs'}
]
});
nbql.parse('author:123-test').should.eql({
statements: [
{prop: 'author', op: '=', value: '123-test'}
]
});
});
it('can parse not equals', function () {
nbql.parse('count:-5').should.eql({
statements: [
{prop: 'count', op: '!=', value: 5}
]
});
nbql.parse('tag:-getting-started').should.eql({
statements: [
{prop: 'tag', op: '!=', value: 'getting-started'}
]
});
nbql.parse('author:-\'Joe Bloggs\'').should.eql({
statements: [
{prop: 'author', op: '!=', value: 'Joe Bloggs'}
]
});
});
it('can parse greater than', function () {
nbql.parse('count:>5').should.eql({
statements: [
{prop: 'count', op: '>', value: 5}
]
});
nbql.parse('tag:>getting-started').should.eql({
statements: [
{prop: 'tag', op: '>', value: 'getting-started'}
]
});
nbql.parse('author:>\'Joe Bloggs\'').should.eql({
statements: [
{prop: 'author', op: '>', value: 'Joe Bloggs'}
]
});
});
it('can parse less than', function () {
nbql.parse('count:<5').should.eql({
statements: [
{prop: 'count', op: '<', value: 5}
]
});
nbql.parse('tag:<getting-started').should.eql({
statements: [
{prop: 'tag', op: '<', value: 'getting-started'}
]
});
nbql.parse('author:<\'Joe Bloggs\'').should.eql({
statements: [
{prop: 'author', op: '<', value: 'Joe Bloggs'}
]
});
});
it('can parse greater than or equals', function () {
nbql.parse('count:>=5').should.eql({
statements: [
{prop: 'count', op: '>=', value: 5}
]
});
nbql.parse('tag:>=getting-started').should.eql({
statements: [
{prop: 'tag', op: '>=', value: 'getting-started'}
]
});
nbql.parse('author:>=\'Joe Bloggs\'').should.eql({
statements: [
{prop: 'author', op: '>=', value: 'Joe Bloggs'}
]
});
});
it('can parse less than or equals', function () {
nbql.parse('count:<=5').should.eql({
statements: [
{prop: 'count', op: '<=', value: 5}
]
});
nbql.parse('tag:<=getting-started').should.eql({
statements: [
{prop: 'tag', op: '<=', value: 'getting-started'}
]
});
nbql.parse('author:<=\'Joe Bloggs\'').should.eql({
statements: [
{prop: 'author', op: '<=', value: 'Joe Bloggs'}
]
});
});
it('can parse IN with single value', function () {
nbql.parse('count:[5]').should.eql({
statements: [
{prop: 'count', op: 'IN', value: [5]}
]
});
nbql.parse('tag:[getting-started]').should.eql({
statements: [
{prop: 'tag', op: 'IN', value: ['getting-started']}
]
});
nbql.parse('author:[\'Joe Bloggs\']').should.eql({
statements: [
{prop: 'author', op: 'IN', value: ['Joe Bloggs']}
]
});
});
it('can parse NOT IN with single value', function () {
nbql.parse('count:-[5]').should.eql({
statements: [
{prop: 'count', op: 'NOT IN', value: [5]}
]
});
nbql.parse('tag:-[getting-started]').should.eql({
statements: [
{prop: 'tag', op: 'NOT IN', value: ['getting-started']}
]
});
nbql.parse('author:-[\'Joe Bloggs\']').should.eql({
statements: [
{prop: 'author', op: 'NOT IN', value: ['Joe Bloggs']}
]
});
});
it('can parse IN with multiple values', function () {
nbql.parse('count:[5, 8, 12]').should.eql({
statements: [
{prop: 'count', op: 'IN', value: [5, 8, 12]}
]
});
nbql.parse('tag:[getting-started, ghost, really-long-1]').should.eql({
statements: [
{prop: 'tag', op: 'IN', value: ['getting-started', 'ghost', 'really-long-1']}
]
});
nbql.parse('author:[\'Joe Bloggs\', \'John O\\\'Nolan\', \'Hello World\']').should.eql({
statements: [
{prop: 'author', op: 'IN', value: ['Joe Bloggs', 'John O\'Nolan', 'Hello World']}
]
});
});
it('can parse NOT IN with single value', function () {
nbql.parse('count:-[5, 8, 12]').should.eql({
statements: [
{prop: 'count', op: 'NOT IN', value: [5, 8, 12]}
]
});
nbql.parse('tag:-[getting-started, ghost, really-long-1]').should.eql({
statements: [
{prop: 'tag', op: 'NOT IN', value: ['getting-started', 'ghost', 'really-long-1']}
]
});
nbql.parse('author:-[\'Joe Bloggs\', \'John O\\\'Nolan\', \'Hello World\']').should.eql({
statements: [
{prop: 'author', op: 'NOT IN', value: ['Joe Bloggs', 'John O\'Nolan', 'Hello World']}
]
});
});
});
describe('Values', function () {
it('can parse null', function () {
nbql.parse('image:null').should.eql({
statements: [
{prop: 'image', op: 'IS', value: null}
]
});
});
it('can parse NOT null', function () {
nbql.parse('image:-null').should.eql({
statements: [
{prop: 'image', op: 'IS NOT', value: null}
]
});
});
it('can parse true', function () {
nbql.parse('featured:true').should.eql({
statements: [
{prop: 'featured', op: '=', value: true}
]
});
});
it('can parse NOT true', function () {
nbql.parse('featured:-true').should.eql({
statements: [
{prop: 'featured', op: '!=', value: true}
]
});
});
it('can parse false', function () {
nbql.parse('featured:false').should.eql({
statements: [
{prop: 'featured', op: '=', value: false}
]
});
});
it('can parse NOT false', function () {
nbql.parse('featured:-false').should.eql({
statements: [
{prop: 'featured', op: '!=', value: false}
]
});
});
it('can parse a Number', function () {
nbql.parse('count:5').should.eql({
statements: [
{prop: 'count', op: '=', value: 5}
]
});
});
it('can parse NOT a Number', function () {
nbql.parse('count:-5').should.eql({
statements: [
{prop: 'count', op: '!=', value: 5}
]
});
});
});
describe('simple expressions', function () {
it('should parse simple id & value combos', function () {
nbql.parse('id:3').should.eql({
statements: [
{prop: 'id', op: '=', value: 3}
]
});
nbql.parse('slug:getting-started').should.eql({
statements: [
{prop: 'slug', op: '=', value: 'getting-started'}
]
});
});
});
describe('complex examples', function () {
it('many expressions', function () {
nbql.parse('tag:photo+featured:true,tag.count:>5').should.eql({
statements: [
{op: '=', value: 'photo', prop: 'tag'},
{op: '=', value: true, prop: 'featured', func: 'and'},
{op: '>', value: 5, prop: 'tag.count', func: 'or'}
]
});
nbql.parse('tag:photo+image:-null,tag.count:>5').should.eql({
statements: [
{op: '=', value: 'photo', prop: 'tag'},
{op: 'IS NOT', value: null, prop: 'image', func: 'and'},
{op: '>', value: 5, prop: 'tag.count', func: 'or'}
]
});
});
it('grouped expressions', function () {
nbql.parse('author:-joe+(tag:photo,image:-null,featured:true)').should.eql({
statements: [
{op: '!=', value: 'joe', prop: 'author'},
{
group: [
{op: '=', value: 'photo', prop: 'tag'},
{op: 'IS NOT', value: null, prop: 'image', func: 'or'},
{op: '=', value: true, prop: 'featured', func: 'or'}
], func: 'and'
}
]
});
nbql.parse('(tag:photo,image:-null,featured:true)+author:-joe').should.eql({
statements: [
{
group: [
{op: '=', value: 'photo', prop: 'tag'},
{op: 'IS NOT', value: null, prop: 'image', func: 'or'},
{op: '=', value: true, prop: 'featured', func: 'or'}
]
},
{op: '!=', value: 'joe', prop: 'author', func: 'and'}
]
});
nbql.parse('author:-joe,(tag:photo,image:-null,featured:true)').should.eql({
statements: [
{op: '!=', value: 'joe', prop: 'author'},
{
group: [
{op: '=', value: 'photo', prop: 'tag'},
{op: 'IS NOT', value: null, prop: 'image', func: 'or'},
{op: '=', value: true, prop: 'featured', func: 'or'}
], func: 'or'
}
]
});
nbql.parse('(tag:photo,image:-null,featured:false),author:-joe').should.eql({
statements: [
{
group: [
{op: '=', value: 'photo', prop: 'tag'},
{op: 'IS NOT', value: null, prop: 'image', func: 'or'},
{op: '=', value: false, prop: 'featured', func: 'or'}
]
},
{op: '!=', value: 'joe', prop: 'author', func: 'or'}
]
});
});
it('in expressions', function () {
nbql.parse('author:-joe+tag:[photo,video]').should.eql({
statements: [
{op: '!=', value: 'joe', prop: 'author'},
{op: 'IN', value: ['photo', 'video'], prop: 'tag', func: 'and'}
]
});
nbql.parse('author:-joe+tag:-[photo,video,audio]').should.eql({
statements: [
{op: '!=', value: 'joe', prop: 'author'},
{op: 'NOT IN', value: ['photo', 'video', 'audio'], prop: 'tag', func: 'and'}
]
});
nbql.parse('author:-joe+tag:[photo,video,magic,\'audio\']+post.count:>5+post.count:<100').should.eql({
statements: [
{op: '!=', value: 'joe', prop: 'author'},
{op: 'IN', value: ['photo', 'video', 'magic', 'audio'], prop: 'tag', func: 'and'},
{op: '>', value: 5, prop: 'post.count', func: 'and'},
{op: '<', value: 100, prop: 'post.count', func: 'and'}
]
});
});
});
describe('whitespace rules', function () {
it('will ignore whitespace in expressions', function () {
nbql.parse('count: -5').should.eql(nbql.parse('count:-5'));
nbql.parse('author: -joe + tag: [photo, video]').should.eql(nbql.parse('author:-joe+tag:[photo,video]'));
});
it('will not ignore whitespace in Strings', function () {
nbql.parse('author:\'Hello World\'').should.not.eql(nbql.parse('author:\'HelloWorld\''));
});
});
describe('invalid expressions', function () {
it('CANNOT parse characters outside of a STRING value', function () {
(function () { nbql.parse('tag:\'My Tag\'-');}).should.throw(parserError);
});
it('CANNOT parse property - operator - value in wrong order', function () {
(function () { nbql.parse('\'My Tag\':tag');}).should.throw(parserError);
(function () { nbql.parse('5>:tag');}).should.throw(parserError);
});
it('CANNOT parse combination without filter expression', function () {
(function () { nbql.parse('count:3+');}).should.throw(parserError);
(function () { nbql.parse(',count:3');}).should.throw(parserError);
});
it('CANNOT parse incomplete group', function () {
(function () { nbql.parse('id:5,(count:3');}).should.throw(parserError);
(function () { nbql.parse('count:3)');}).should.throw(parserError);
(function () { nbql.parse('id:5(count:3)');}).should.throw(parserError);
});
it('CANNOT parse invalid IN expression', function () {
(function () { nbql.parse('id:[test+ing]');}).should.throw(parserError);
(function () { nbql.parse('id:[test');}).should.throw(parserError);
(function () { nbql.parse('id:test,ing]');}).should.throw(parserError);
});
});
});