forked from mozilla/mozjexl
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add mode to throw on invalid property access, fixes mozilla#25
- Loading branch information
Showing
9 changed files
with
515 additions
and
453 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,206 @@ | ||
/* | ||
* Jexl | ||
* Copyright (c) 2015 TechnologyAdvice | ||
*/ | ||
|
||
var chai = require("chai"), | ||
chaiAsPromised = require("chai-as-promised"), | ||
Lexer = require("../../lib/Lexer"), | ||
Parser = require("../../lib/parser/Parser"), | ||
Evaluator = require("../../lib/evaluator/Evaluator"), | ||
grammar = require("../../lib/grammar").elements; | ||
|
||
chai.should(); | ||
chai.use(chaiAsPromised); | ||
|
||
var lexer = new Lexer(grammar); | ||
|
||
function toTree(exp) { | ||
var p = new Parser(grammar); | ||
p.addTokens(lexer.tokenize(exp)); | ||
return p.complete(); | ||
} | ||
|
||
describe("Evaluator in throwOnMissingProp mode", function() { | ||
it("should evaluate an arithmetic expression", function() { | ||
const e = new Evaluator(grammar, null, null, null, true); | ||
return e.eval(toTree("(2 + 3) * 4")).should.become(20); | ||
}); | ||
it("should evaluate a string concat", function() { | ||
var e = new Evaluator(grammar, null, null, null, true); | ||
return e | ||
.eval(toTree('"Hello" + (4+4) + "Wo\\"rld"')) | ||
.should.become('Hello8Wo"rld'); | ||
}); | ||
it("should evaluate a true comparison expression", function() { | ||
var e = new Evaluator(grammar, null, null, null, true); | ||
return e.eval(toTree("2 > 1")).should.become(true); | ||
}); | ||
it("should evaluate a false comparison expression", function() { | ||
var e = new Evaluator(grammar, null, null, null, true); | ||
return e.eval(toTree("2 <= 1")).should.become(false); | ||
}); | ||
it("should evaluate a complex expression", function() { | ||
var e = new Evaluator(grammar, null, null, null, true); | ||
return e | ||
.eval(toTree('"foo" && 6 >= 6 && 0 + 1 && true')) | ||
.should.become(true); | ||
}); | ||
it("should evaluate an identifier chain", function() { | ||
var context = { foo: { baz: { bar: "tek" } } }, | ||
e = new Evaluator(grammar, null, context, null, true); | ||
return e.eval(toTree("foo.baz.bar")).should.become(context.foo.baz.bar); | ||
}); | ||
it("should apply transforms", function() { | ||
var context = { foo: 10 }, | ||
half = function(val) { | ||
return val / 2; | ||
}, | ||
e = new Evaluator(grammar, { half: half }, context, null, true); | ||
return e.eval(toTree("foo|half + 3")).should.become(8); | ||
}); | ||
it("should filter arrays", function() { | ||
var context = { | ||
foo: { | ||
bar: [{ tek: "hello" }, { tek: "baz" }, { tok: "baz" }] | ||
} | ||
}, | ||
e = new Evaluator(grammar, null, context, null, true); | ||
return e | ||
.eval(toTree('foo.bar[.tek == "baz"]')) | ||
.should.eventually.deep.equal([{ tek: "baz" }]); | ||
}); | ||
it("should assume array index 0 when traversing", function() { | ||
var context = { | ||
foo: { | ||
bar: [{ tek: { hello: "world" } }, { tek: { hello: "universe" } }] | ||
} | ||
}, | ||
e = new Evaluator(grammar, null, context, null, true); | ||
return e.eval(toTree("foo.bar.tek.hello")).should.become("world"); | ||
}); | ||
it("should make array elements addressable by index", function() { | ||
var context = { | ||
foo: { | ||
bar: [{ tek: "tok" }, { tek: "baz" }, { tek: "foz" }] | ||
} | ||
}, | ||
e = new Evaluator(grammar, null, context, null, true); | ||
return e.eval(toTree("foo.bar[1].tek")).should.become("baz"); | ||
}); | ||
it("should allow filters to select object properties", function() { | ||
var context = { foo: { baz: { bar: "tek" } } }, | ||
e = new Evaluator(grammar, null, context, null, true); | ||
return e | ||
.eval(toTree('foo["ba" + "z"].bar')) | ||
.should.become(context.foo.baz.bar); | ||
}); | ||
it("should reject simple filters on undefined objects", function() { | ||
var context = { foo: {} }, | ||
e = new Evaluator(grammar, null, context, null, true); | ||
return e.eval(toTree('foo.bar["baz"].tok')).should.be.rejected; | ||
}); | ||
it("should reject complex filters on undefined objects", function() { | ||
var context = { foo: {} }, | ||
e = new Evaluator(grammar, null, context, null, true); | ||
return e.eval(toTree("foo.bar[.size > 1].baz")).should.be.rejected; | ||
}); | ||
it("should throw when transform does not exist", function() { | ||
var e = new Evaluator(grammar, null, null, null, true); | ||
return e.eval(toTree('"hello"|world')).should.be.rejected; | ||
}); | ||
it("should throw when top-level identifier doesn't exist", function() { | ||
var context = { foo: { baz: { bar: "dog" } } }, | ||
e = new Evaluator(grammar, null, context, null, true); | ||
|
||
return e.eval(toTree("monkey")).should.be.rejected; | ||
}); | ||
it("should throw when child identifier doesn't exist", function() { | ||
var context = { foo: { baz: { bar: "cat" } } }, | ||
e = new Evaluator(grammar, null, context, null, true); | ||
|
||
return e.eval(toTree("foo.baz.monkey")).should.be.rejected; | ||
}); | ||
it("should apply the DivFloor operator", function() { | ||
var e = new Evaluator(grammar, null, null, null, true); | ||
return e.eval(toTree("7 // 2")).should.become(3); | ||
}); | ||
it("should evaluate an object literal", function() { | ||
var e = new Evaluator(grammar, null, null, null, true); | ||
return e | ||
.eval(toTree('{foo: {bar: "tek"}}')) | ||
.should.eventually.deep.equal({ foo: { bar: "tek" } }); | ||
}); | ||
it("should evaluate an empty object literal", function() { | ||
var e = new Evaluator(grammar, null, null, null, true); | ||
return e.eval(toTree("{}")).should.eventually.deep.equal({}); | ||
}); | ||
it("should evaluate a transform with multiple args", function() { | ||
var e = new Evaluator( | ||
grammar, | ||
{ | ||
concat: function(val, a1, a2, a3) { | ||
return val + ": " + a1 + a2 + a3; | ||
} | ||
}, | ||
null, | ||
null, | ||
true | ||
); | ||
return e | ||
.eval(toTree('"foo"|concat("baz", "bar", "tek")')) | ||
.should.become("foo: bazbartek"); | ||
}); | ||
it("should evaluate dot notation for object literals", function() { | ||
var e = new Evaluator(grammar, null, null, null, true); | ||
return e.eval(toTree('{foo: "bar"}.foo')).should.become("bar"); | ||
}); | ||
it("should allow access to literal properties", function() { | ||
var e = new Evaluator(grammar, null, null, null, true); | ||
return e.eval(toTree('"foo".length')).should.become(3); | ||
}); | ||
it("should evaluate array literals", function() { | ||
var e = new Evaluator(grammar, null, null, null, true); | ||
return e | ||
.eval(toTree('["foo", 1+2]')) | ||
.should.eventually.deep.equal(["foo", 3]); | ||
}); | ||
it("should allow properties on empty arrays", function() { | ||
var context = { foo: {} }, | ||
e = new Evaluator(grammar, null, context, null, true); | ||
return e.eval(toTree("[].baz")).should.become(undefined); | ||
}); | ||
it('should apply the "in" operator to strings', function() { | ||
var e = new Evaluator(grammar, null, null, null, true); | ||
return Promise.all([ | ||
e.eval(toTree('"bar" in "foobartek"')).should.become(true), | ||
e.eval(toTree('"baz" in "foobartek"')).should.become(false) | ||
]); | ||
}); | ||
it('should apply the "in" operator to arrays', function() { | ||
var e = new Evaluator(grammar, null, null, null, true); | ||
return Promise.all([ | ||
e.eval(toTree('"bar" in ["foo","bar","tek"]')).should.become(true), | ||
e.eval(toTree('"baz" in ["foo","bar","tek"]')).should.become(false) | ||
]); | ||
}); | ||
it("should evaluate a conditional expression", function() { | ||
var e = new Evaluator(grammar, null, null, null, true); | ||
return Promise.all([ | ||
e.eval(toTree('"foo" ? 1 : 2')).should.become(1), | ||
e.eval(toTree('"" ? 1 : 2')).should.become(2) | ||
]); | ||
}); | ||
it("should allow missing consequent in ternary", function() { | ||
var e = new Evaluator(grammar, null, null, null, true); | ||
return e.eval(toTree('"foo" ?: "bar"')).should.become("foo"); | ||
}); | ||
it("does not treat falsey properties as undefined", function() { | ||
const e = new Evaluator(grammar, null, null, null, true); | ||
return e.eval(toTree('"".length')).should.become(0); | ||
}); | ||
it("should handle an expression with arbitrary whitespace", function() { | ||
var e = new Evaluator(grammar, null, null, null, true); | ||
return e.eval(toTree("(\t2\n+\n3) *\n4\n\r\n")).should.become(20); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.