can-stache
Version:
Live binding handlebars templates
1,161 lines (969 loc) • 26.7 kB
JavaScript
var expression = require('../src/expression');
var QUnit = require('steal-qunit');
var Scope = require('can-view-scope');
var helpers = require('../helpers/core');
var canReflect = require("can-reflect");
var SimpleObservable = require('can-simple-observable');
var SimpleMap = require('can-simple-map');
var DefineList = require('can-define/list/list');
var assign = require("can-assign");
var testHelpers = require('can-test-helpers');
QUnit.module("can-stache/src/expression");
QUnit.test("expression.tokenize", function(assert) {
var literals = "'quote' \"QUOTE\" 1 undefined null true false 0.1";
var res = expression.tokenize(literals);
assert.deepEqual(res, literals.split(" "));
var keys = "key foo.bar foo@bar %foo *foo foo/bar";
res = expression.tokenize(keys);
assert.deepEqual(res, keys.split(" "));
var syntax = "( ) , ~ =";
res = expression.tokenize(syntax);
assert.deepEqual(res, syntax.split(" "));
var curly = "{{ }}";
res = expression.tokenize(curly);
assert.deepEqual(res, []);
var bracket = "[foo] bar [baz]";
res = expression.tokenize(bracket);
assert.deepEqual(res, ["[", "foo", "]", " ", "bar", " ", "[", "baz", "]", " "]);
});
QUnit.test("expression.ast - helper followed by hash", function(assert) {
var ast = expression.ast("print_hash prop=own_prop");
assert.deepEqual(ast, {
type: "Helper",
method: {
type: "Lookup",
key: "print_hash"
},
children: [
{
type: "Hashes",
children: [
{
type: "Hash",
prop: "prop",
children: [{type: "Lookup", key: "own_prop"}]
}
]
}
]
});
});
QUnit.test("expression.ast - root hash expressions work", function(assert) {
var ast = expression.ast("prop=own_prop");
assert.deepEqual(ast, {
type: "Hashes",
children: [
{
type: "Hash",
prop: "prop",
children: [{type: "Lookup", key: "own_prop"}]
}
]
});
});
QUnit.test("expression.ast - nested call expressions", function(assert) {
var ast = expression.ast("foo()()");
assert.deepEqual(ast, {
type: "Call",
method: {
type: "Call",
method: {type: "Lookup", key: "@foo"},
}
});
});
QUnit.test("expression.ast - everything", function(assert) {
var ast = expression.ast("helperA helperB(1, valueA, propA=~valueB propC=2, 1).zed() 'def' nested@prop outerPropA=helperC(2,valueB)");
var helperBCall = {
type: "Call",
method: {type: "Lookup", key: "@helperB"},
children: [
{type: "Literal", value: 1},
{type: "Lookup", key: "valueA"},
{
type: "Hashes",
children: [
{
type: "Hash",
prop: "propA",
children: [{type: "Arg", key: "~", children: [{type: "Lookup", key: "valueB"} ]}]
},
{
type: "Hash",
prop: "propC",
children: [{type: "Literal", value: 2}]
}
]
},
{type: "Literal", value: 1}
]
};
var helperCCall = {
type: "Call",
method: {type: "Lookup", key: "@helperC"},
children: [
{type: "Literal", value: 2},
{type: "Lookup", key: "valueB"}
]
};
assert.deepEqual(ast, {
type: "Helper",
method: {
type: "Lookup",
key: "helperA"
},
children: [
{
type: "Call",
method: {
type: "Lookup",
root: helperBCall,
key: "@zed"
}
},
{type: "Literal", value: 'def'},
{type: "Lookup", key: "nested@prop"},
{
type: "Hashes",
children: [
{
type: "Hash",
prop: "outerPropA",
children: [helperCCall]
}
]
}
]
});
});
QUnit.test("expression.parse - everything", function(assert) {
var exprData = expression.parse("helperA helperB(1, valueA, propA=~valueB propC=2, 1).zed 'def' nested.prop() outerPropA=helperC(2,valueB)");
var oneExpr = new expression.Literal(1),
twoExpr = new expression.Literal(2),
def = new expression.Literal('def'),
valueA = new expression.Lookup("valueA"),
valueB = new expression.Lookup("valueB"),
helperA = new expression.Lookup("helperA"),
helperB = new expression.Lookup("@helperB"),
helperC = new expression.Lookup("@helperC");
var helperBHashArg = new expression.Hashes({
propA: new expression.Arg(valueB, {compute: true}),
propC: twoExpr
});
var callHelperB = new expression.Call(
helperB,
[oneExpr, valueA, helperBHashArg, oneExpr]
);
var callHelperBdotZed = new expression.Lookup("zed", callHelperB);
var callNestedProp = new expression.Call(
new expression.Lookup("nested@prop"),
[],
{}
);
var callHelperC = new expression.Call(
helperC,
[twoExpr, valueB],
{}
);
var callHelperA = new expression.Helper(
helperA,
[callHelperBdotZed, def, callNestedProp],
{
outerPropA: callHelperC
}
);
assert.deepEqual(callHelperB, exprData.argExprs[0].rootExpr, "call helper b");
assert.deepEqual(callHelperC, exprData.hashExprs.outerPropA, "helperC call");
assert.deepEqual(callHelperBdotZed, exprData.argExprs[0], "call helper b.zed");
var expectedArgs = [callHelperBdotZed, def, callNestedProp];
canReflect.each(exprData.argExprs, function(arg, i){
assert.deepEqual(arg, expectedArgs[i], "helperA arg["+i+"]");
});
assert.deepEqual(exprData, callHelperA, "full thing");
});
QUnit.test("expression.parse(str, {lookupRule: 'method', methodRule: 'call'})",
function(assert) {
var exprData = expression.parse("withArgs content=content", {
lookupRule: "method",
methodRule: "call"
});
var valueContent = new expression.Lookup("content");
var hashArg = new expression.Arg(new expression.Hashes({
content: valueContent
}));
assert.equal(exprData.argExprs.length, 1, "there is one arg");
assert.deepEqual(exprData.argExprs[0], hashArg, "correct hashes");
});
QUnit.test("expression.parse nested Call expressions", function(assert) {
assert.expect(7);
assert.deepEqual(expression.parse("foo()()"),
new expression.Call(
new expression.Call(
new expression.Lookup('@foo'),
[],
{}
),
[],
{}
),
"Returned the correct expression"
);
var expr = new expression.Call(
new expression.Call(
new expression.Lookup('@bar'),
[ new expression.Literal(1) ],
{}
),
[ new expression.Literal(2) ],
{}
);
var compute = expr.value(
new Scope(
new SimpleMap({
bar: function(outter) {
assert.equal(outter, 1, "Outter called with correct value");
return function (inner) {
assert.equal(inner, 2, "Inner called with correct value");
return 'Inner!';
};
}
})
)
);
assert.equal(compute.get(), "Inner!", "Got the inner value");
expr = new expression.Call(
new expression.Call(
new expression.Lookup('@foobar'),
[ new expression.Lookup('fname') ],
{}
),
[ new expression.Lookup('lname') ],
{}
);
compute = expr.value(
new Scope(
new SimpleMap({
foobar: function(outter) {
assert.equal(outter, 'Matt', "Outter called with correct value");
return function (inner) {
assert.equal(inner, 'Chaffe', "Inner called with correct value");
return 'Inner!';
};
},
fname: 'Matt',
lname: 'Chaffe'
})
)
);
assert.equal(compute.get(), "Inner!", "Got the inner value");
});
QUnit.test("numeric expression.Literal", function(assert) {
var exprData = expression.parse("3");
var result = new expression.Literal(3);
assert.deepEqual( exprData, result);
});
QUnit.test("expression.Helper:value non-observable values", function(assert) {
// {{fullName 'marshall' 'thompson'}}
var scope = new Scope({
fullName: function(first, last){
return first+" "+last;
}
});
var callFullName = new expression.Helper(
new expression.Lookup("fullName"),
[new expression.Literal('marshall'), new expression.Literal('thompson')],
{}
);
var result = callFullName.value(scope, new Scope({}), {});
assert.equal(expression.toComputeOrValue(result)(), "marshall thompson");
});
QUnit.test("expression.Helper:value observable values", function(assert) {
// {{fullName first 'thompson'}}
var obj = {
fullName: function(first, last){
assert.equal(this, obj, "this is right");
return first()+" "+last;
},
first: new SimpleObservable("marshall")
};
var scope = new Scope(obj);
var callFullName = new expression.Helper(
new expression.Lookup("fullName"),
[new expression.Lookup("first"), new expression.Literal('thompson')],
{}
);
var result = callFullName.value(scope, new Scope({}) );
assert.equal(result(), "marshall thompson");
});
QUnit.test("methods can return values (#1887)", function(assert) {
var MyMap = SimpleMap.extend({
getSomething: function(arg){
return this.attr("foo") + arg();
}
});
var scope =
new Scope(new MyMap({foo: 2, bar: 3}));
var callGetSomething = new expression.Helper(
new expression.Lookup("getSomething"),
[new expression.Lookup("bar")],
{}
);
var result = callGetSomething.value(scope, new Scope({}), {asCompute: true});
assert.equal(result(), 5);
});
QUnit.test("methods don't update correctly (#1891)", function(assert) {
var map = new SimpleMap({
num: 1
});
assign(map,{
num2: function () {
return this.get('num') * 2;
},
runTest: function () {
this.attr('num', this.get('num') * 2);
}
});
var scope =
new Scope(map);
var num2Expression = new expression.Lookup("num2");
var num2 = num2Expression.value( scope, {asCompute: true} );
canReflect.onValue(num2,function(){});
map.runTest();
var func = canReflect.getValue( num2 );
assert.equal( func() , 4, "num2 updated correctly");
});
QUnit.test("call expressions called with different scopes give different results (#1791)", function(assert) {
var exprData = expression.parse("doSomething(number)");
var res = exprData.value(new Scope({
doSomething: function(num){
return num*2;
},
number: new SimpleObservable(2)
}));
assert.equal( res.get(), 4);
res = exprData.value(new Scope({
doSomething: function(num){
return num*3;
},
number: new SimpleObservable(4)
}));
assert.equal( res.get(), 12);
});
QUnit.test("call expressions called with different contexts (#616)", function(assert) {
assert.expect(1);
var exprData = expression.parse("this.foo.doSomething()");
var doSomething = function(){
return this.value;
};
var context = new SimpleMap({
foo: {
doSomething: doSomething,
value: "A"
}
});
var res = exprData.value( new Scope(context) );
canReflect.onValue(res, function(value){
assert.equal(value, "B");
});
context.set("foo",{
doSomething: doSomething,
value: "B"
});
});
QUnit.test("convertKeyToLookup", function(assert) {
assert.equal( expression.convertKeyToLookup("../foo"), "../@foo" );
assert.equal( expression.convertKeyToLookup("foo"), "@foo" );
assert.equal( expression.convertKeyToLookup(".foo"), "@foo" );
assert.equal( expression.convertKeyToLookup("./foo"), "./@foo" );
assert.equal( expression.convertKeyToLookup("foo.bar"), "foo@bar" );
});
QUnit.test("expression.ast - [] operator", function(assert) {
assert.deepEqual(expression.ast("['propName']"), {
type: "Bracket",
children: [{type: "Literal", value: "propName"}]
}, "['propName'] valid");
assert.deepEqual(expression.ast("[propName]"), {
type: "Bracket",
children: [{type: "Lookup", key: "propName"}]
}, "[propName] valid");
assert.deepEqual(expression.ast("foo['bar']"), {
type: "Bracket",
root: {type: "Lookup", key: "foo"},
children: [{type: "Literal", value: "bar"}]
}, "foo['bar'] valid");
assert.deepEqual(expression.ast("foo[bar]"), {
type: "Bracket",
root: {type: "Lookup", key: "foo"},
children: [{type: "Lookup", key: "bar"}]
}, "foo[bar] valid");
assert.deepEqual(expression.ast("foo[bar()]"), {
type: "Bracket",
root: {type: "Lookup", key: "foo"},
children: [{type: "Call", method: {key: "@bar", type: "Lookup" }}]
}, "foo[bar()] valid");
assert.deepEqual(expression.ast("foo()[bar]"), {
type: "Bracket",
root: {type: "Call", method: {key: "@foo", type: "Lookup" } },
children: [{type: "Lookup", key: "bar"}]
}, "foo()[bar] valid");
assert.deepEqual(expression.ast("foo [bar]"), {
type: "Helper",
method: {
type: "Lookup",
key: "foo"
},
children: [{
type: "Bracket",
children: [{type: "Lookup", key: "bar"}]
}]
}, "foo [bar] valid");
assert.deepEqual(expression.ast("eq foo['bar'] 'foo'"), {
type: "Helper",
method: {
type: "Lookup",
key: "eq"
},
children: [{
type: "Bracket",
root: {type: "Lookup", key: "foo"},
children: [{type: "Literal", value: "bar"}]
},
{
type: "Literal",
value: "foo"
}]
},
"eq foo['bar'] 'foo' valid"
);
assert.deepEqual(expression.ast("eq foo[bar] foo"), {
type: "Helper",
method: {
type: "Lookup",
key: "eq"
},
children: [{
type: "Bracket",
root: {type: "Lookup", key: "foo"},
children: [{type: "Lookup", key: "bar"}]
},
{
type: "Lookup",
key: "foo"
}]
}, "eq foo[bar] foo valid");
assert.deepEqual(expression.ast("foo[bar][baz]"), {
type: "Bracket",
root: {
type: "Bracket",
root: {type: "Lookup", key: "foo"},
children: [{type: "Lookup", key: "bar"}]
},
children: [{type: "Lookup", key: "baz"}]
}, "foo[bar][baz] valid");
assert.deepEqual(expression.ast("foo[bar].baz"), {
type: "Lookup",
key: "baz",
root: {
type: "Bracket",
root: {type: "Lookup", key: "foo"},
children: [{type: "Lookup", key: "bar"}]
}
}, "foo[bar].baz");
assert.deepEqual(expression.ast("eq foo[bar].baz xyz"), {
type: "Helper",
method: {
type: "Lookup",
key: "eq"
},
children: [{
type: "Lookup",
key: "baz",
root: {
type: "Bracket",
root: {type: "Lookup", key: "foo"},
children: [{type: "Lookup", key: "bar"}]
}
},
{
type: "Lookup",
key: "xyz"
}]
}, "eq foo[bar].baz xyz");
});
QUnit.test("expression.parse - [] operator", function(assert) {
assert.deepEqual(expression.parse("['propName']"),
new expression.Bracket(
new expression.Literal('propName')
),
"['propName']"
);
assert.deepEqual(expression.parse("[propName]"),
new expression.Bracket(
new expression.Lookup('propName')
),
"[propName]"
);
assert.deepEqual(expression.parse("foo['bar']"),
new expression.Bracket(
new expression.Literal('bar'),
new expression.Lookup('foo'),
'foo'
),
"foo['bar']"
);
assert.deepEqual(expression.parse("foo[bar]"),
new expression.Bracket(
new expression.Lookup('bar'),
new expression.Lookup('foo'),
'foo'
),
"foo[bar]"
);
assert.deepEqual(expression.parse("foo()[bar]"),
new expression.Bracket(
new expression.Lookup('bar'),
new expression.Call(
new expression.Lookup('@foo'),
[],
{}
)
),
"foo()[bar]"
);
var exprData = expression.parse("foo[bar()]");
assert.deepEqual(exprData,
new expression.Bracket(
new expression.Call(
new expression.Lookup('@bar'),
[],
{}
),
new expression.Lookup('foo'),
'foo'
)
);
exprData = expression.parse("foo()[bar()]");
assert.deepEqual(exprData,
new expression.Bracket(
new expression.Call(
new expression.Lookup("@bar"),
[],
{}
),
new expression.Call(
new expression.Lookup("@foo"),
[],
{}
)
)
);
exprData = expression.parse("equal(foo(), [bar])");
assert.equal(exprData.argExprs.length, 2, "there are two arguments");
assert.deepEqual(exprData,
new expression.Call(
new expression.Lookup("@equal"),
[
new expression.Arg(
new expression.Call(new expression.Lookup("@foo"), [], {})
),
new expression.Arg(
new expression.Bracket(new expression.Lookup("bar"))
)
],
{}
)
);
});
QUnit.test("Bracket expression", function(assert) {
// ["bar"]
var expr = new expression.Bracket(
new expression.Literal("bar")
);
var compute = expr.value(
new Scope(
new SimpleMap({bar: "name"})
)
);
assert.equal(compute.get(), "name");
// [bar]
expr = new expression.Bracket(
new expression.Lookup("bar")
);
compute = expr.value(
new Scope(
new SimpleMap({bar: "name", name: "Kevin"})
)
);
assert.equal(compute.get(), "Kevin");
// foo["bar"]
expr = new expression.Bracket(
new expression.Literal("bar"),
new expression.Lookup("foo")
);
compute = expr.value(
new Scope(
new SimpleMap({foo: {bar: "name"}})
)
);
assert.equal(compute.get(), "name");
// foo["bar.baz"]
expr = new expression.Bracket(
new expression.Literal("bar.baz"),
new expression.Lookup("foo")
);
compute = expr.value(
new Scope(
new SimpleMap({foo: {"bar.baz": "name"}})
)
);
assert.equal(compute.get(), "name",'foo["bar.baz"]');
// foo["bar.baz.quz"]
expr = new expression.Bracket(
new expression.Literal("bar.baz.quz"),
new expression.Lookup("foo")
);
compute = expr.value(
new Scope(
new SimpleMap({foo: {"bar.baz.quz": "name"}})
)
);
assert.equal(compute.get(), "name",'foo["bar.baz.quz"]');
// foo[bar]
expr = new expression.Bracket(
new expression.Lookup("bar"),
new expression.Lookup("foo")
);
var state = new SimpleMap({foo: new SimpleMap({name: "Kevin"}), bar: "name"});
compute = expr.value(
new Scope(
state
)
);
assert.equal(compute.get(), "Kevin", "foo[bar] get");
compute.set("Curtis");
assert.equal(state.get("foo").get("name"), "Curtis");
// foo()[bar]
expr = new expression.Bracket(
new expression.Lookup("bar"),
new expression.Call(
new expression.Lookup("@foo"),
[],
{}
)
);
compute = expr.value(
new Scope(
new SimpleMap({foo: function() { return {name: "Kevin"}; }, bar: "name"})
)
);
assert.equal(compute.get(), "Kevin");
// foo[bar()]
expr = new expression.Bracket(
new expression.Call(
new expression.Lookup('@bar'),
[],
{}
),
new expression.Lookup('foo')
);
compute = expr.value(
new Scope(
new SimpleMap({
foo: {name: "Kevin"},
bar: function () { return "name"; }
})
)
);
assert.equal(compute.get(), "Kevin");
// foo()[bar()]
expr = new expression.Bracket(
new expression.Call(
new expression.Lookup("@bar"),
[],
{}
),
new expression.Call(
new expression.Lookup("@foo"),
[],
{}
)
);
compute = expr.value(
new Scope(
new SimpleMap({
foo: function() { return {name: "Kevin"}; },
bar: function () { return "name"; }
})
)
);
assert.equal(compute.get(), "Kevin");
// foo([bar])
expr = new expression.Call(
new expression.Lookup('@foo'),
[
new expression.Bracket(
new expression.Lookup('bar')
)
],
{}
);
compute = expr.value(
new Scope(
new SimpleMap({
foo: function(val) { return val + '!'; },
bar: 'name',
name: 'Kevin'
})
)
);
assert.equal(compute.get(), "Kevin!");
});
QUnit.test("registerConverter helpers push and pull correct values", function(assert) {
helpers.registerConverter('numberToHex', {
get: function(valCompute) {
return valCompute().toString(16);
}, set: function(val, valCompute) {
return valCompute(parseInt("0x" + val));
}
});
var data = new SimpleMap({
observeVal: 255
});
var scope = new Scope( data );
var parentExpression = expression.parse("numberToHex(~observeVal)",{baseMethodType: "Call"});
var twoWayCompute = parentExpression.value(scope);
//twoWayCompute('34');
//var renderer = stache('<input type="text" bound-attr="numberToHex(~observeVal)" />');
assert.equal(twoWayCompute.get(), 'ff', 'Converter called');
twoWayCompute.set('7f');
assert.equal(data.get("observeVal"), 127, 'push converter called');
});
QUnit.test("registerConverter helpers push and pull multiple values", function(assert) {
helpers.registerConverter('isInList', {
get: function(valCompute, list) {
return !!~list.indexOf(valCompute());
}, set: function(newVal, valCompute, list) {
if(!~list.indexOf(newVal)) {
list.push(newVal);
}
}
});
var data = new SimpleMap({
observeVal: 4,
list: new DefineList([1,2,3])
});
var scope = new Scope( data );
var parentExpression = expression.parse("isInList(~observeVal, list)",{baseMethodType: "Call"});
var twoWayCompute = parentExpression.value(scope);
//twoWayCompute('34');
//var renderer = stache('<input type="text" bound-attr="numberToHex(~observeVal)" />');
assert.equal(twoWayCompute.get(), false, 'Converter called');
twoWayCompute.set(5);
assert.deepEqual(data.attr("list").attr(), [1,2,3,5], 'push converter called');
});
QUnit.test("registerConverter helpers are chainable", function(assert) {
helpers.registerConverter('numberToHex', {
get: function(valCompute) {
return valCompute().toString(16);
}, set: function(val, valCompute) {
return valCompute(parseInt("0x" + val));
}
});
helpers.registerConverter('upperCase', {
get: function(valCompute) {
return valCompute().toUpperCase();
}, set: function(val, valCompute) {
return valCompute(val.toLowerCase());
}
});
var data = new SimpleMap({
observeVal: 255
});
var scope = new Scope( data );
var parentExpression = expression.parse("upperCase(~numberToHex(~observeVal))",{baseMethodType: "Call"});
var twoWayCompute = parentExpression.value(scope);
//twoWayCompute('34');
//var renderer = stache('<input type="text" bound-attr="numberToHex(~observeVal)" />');
assert.equal(twoWayCompute.get(), 'FF', 'Converter called');
twoWayCompute.set('7F');
assert.equal(data.attr("observeVal"), 127, 'push converter called');
});
QUnit.test('foo().bar', function(assert) {
// expression.ast
var ast4 = expression.ast("foo().bar");
assert.deepEqual(ast4, {
type: "Lookup",
key: "bar",
root: {type: "Call", method: {key: "@foo", type: "Lookup" } }
});
// expression.parse
var exprData = expression.parse("foo().bar");
assert.deepEqual(exprData,
new expression.Lookup(
"bar",
new expression.Call( new expression.Lookup("@foo"), [], {} )
)
);
// expr.value
var expr = new expression.Lookup(
"bar",
new expression.Call( new expression.Lookup("@foo"), [], {} )
);
var compute = expr.value(
new Scope(
new SimpleMap({foo: function() { return {bar: "Kevin"}; }})
)
);
assert.equal(compute.get(), "Kevin");
});
QUnit.test("Helper with a ~ key operator (#112)", function(assert) {
var ast = expression.ast('each ~foo');
var expected = {
type: "Helper",
method: {type: "Lookup", key: "each"},
children: [{type: "Arg", key: "~", children: [{type: "Lookup", key: "foo"} ]}]
};
assert.deepEqual(ast, expected);
});
QUnit.test("ast with [double][brackets] or [bracket].prop (#207)", function(assert) {
var ast = expression.ast("test['foo'][0]");
var expected = {
type: "Bracket",
children: [{type: "Literal", value: 0}],
root: {
type: "Bracket",
children: [{type: "Literal", value: 'foo'}],
root: {type: "Lookup", key: "test"}
}
};
assert.deepEqual(ast, expected);
ast = expression.ast("test['foo'].zed");
expected = {
type: "Lookup",
key: "zed",
root: {
type: "Bracket",
children: [{type: "Literal", value: 'foo'}],
root: {type: "Lookup", key: "test"}
}
};
assert.deepEqual(ast, expected);
ast = expression.ast("test['foo'].zed['bar']");
expected = {
type: "Bracket",
children: [{type: "Literal", value: 'bar'}],
root: {
type: "Lookup",
key: "zed",
root: {
type: "Bracket",
children: [{type: "Literal", value: 'foo'}],
root: {type: "Lookup", key: "test"}
}
}
};
assert.deepEqual(ast, expected);
});
testHelpers.dev.devOnlyTest("All expression types have sourceText on prototype", function (assert){
["Arg", "Bracket", "Call", "Hashes", "Helper", "Literal"].forEach(function(name){
assert.ok(typeof expression[name].prototype.sourceText === "function", name);
});
});
testHelpers.dev.devOnlyTest("expression.sourceText - everything", function (assert){
var source = "helperA helperB(1,valueA,propA=~valueB propC=2,1).zed \"def\" nested.prop() outerPropA=helperC(2,valueB)";
var exprData = expression.parse(source);
assert.equal(exprData.sourceText(),source);
});
QUnit.test('Call Expressions can return functions instead of Observations', function(assert) {
var data = new SimpleMap({
name: "kevin",
foo: function() {
return this.get("name");
}
});
var expr = new expression.Call( new expression.Lookup("@foo"), [], {} );
var val = new SimpleObservable(
expr.value( new Scope( data ) )
);
assert.equal(canReflect.getValue(val.value), "kevin", "got correct initial value");
assert.ok(canReflect.isObservableLike(val.value), "value is observable by default");
canReflect.onValue(val.value, function(newVal) {
assert.equal(newVal, "mark", "got correct changed value");
});
data.set("name", "mark");
var nonBindingVal = new SimpleObservable(
expr.value( new Scope( data ), {
doNotWrapInObservation: true
})
);
assert.equal(canReflect.getValue(nonBindingVal.value), "mark", "got correct initial value");
assert.ok(!canReflect.isObservableLike(nonBindingVal.value), "value is not observable when doNotWrapInObservation is true");
});
QUnit.test("negative literals ast", function(assert) {
var ast = expression.ast("adjust(-10)");
var expected = {
children: [{ type: "Literal", value: -10 }],
method: { key: "@adjust", type: "Lookup" },
type: "Call"
};
assert.deepEqual(ast, expected);
});
testHelpers.dev.devOnlyTest("don't warn on perfectly fine function result reads", function (assert) {
var teardown = testHelpers.dev.willWarn(/Unable to find key/);
var exprData = expression.parse("method().toFixed(1)",{baseMethodType: "Call"});
var scope = new Scope({
method: function(){
return 1.111;
}
});
var result = exprData.value(scope);
assert.equal( result.get(), "1.1" , "got value");
assert.equal(teardown(), 0, 'got expected warning');
});
QUnit.test("let foo=bar,zed=ted", function(assert) {
//var helperAst = expression.ast("let foo=bar zed=ted");
var commaHelperAst = expression.ast("let foo=bar,zed=ted");
//assert.deepEqual( commaHelperAst, helperAst, "commas work in helpers");
assert.deepEqual(commaHelperAst, {
"type": "Helper",
"method": { "type": "Lookup", "key": "let" },
"children": [
{
"type": "Hashes",
"children": [
{
"type": "Hash",
"prop": "foo",
"children": [ { "type": "Lookup", "key": "bar" } ]
},
{
"type": "Hash",
"prop": "zed",
"children": [ { "type": "Lookup", "key": "ted" } ]
}
]
}
]
});
});
QUnit.test("double [] in a function", function(assert) {
var ast = expression.ast("log(thing['prop'][0])");
var logAst = {
type: "Call",
method: {type: "Lookup", key: "@log"},
children: [
{
"type":"Bracket",
"root": {
"type":"Bracket",
"root":{
"type":"Lookup",
"key":"thing"
},
"children":[{"type":"Literal","value":"prop"}]
},
"children":[{"type":"Literal","value":0}]
}
]
};
assert.deepEqual(ast, logAst);
});