can
Version:
MIT-licensed, client-side, JavaScript framework that makes building rich web applications easy.
1,359 lines (1,101 loc) • 28.5 kB
JavaScript
/* jshint asi: false */
steal("can/map/define", "can/route", "can/test", "steal-qunit", function () {
QUnit.module('can/map/define');
// remove, type, default
test('basics set', function () {
var Defined = can.Map.extend({
define: {
prop: {
set: function (newVal) {
return "foo" + newVal;
}
}
}
});
var def = new Defined();
def.attr("prop", "bar");
equal(def.attr("prop"), "foobar", "setter works");
Defined = can.Map.extend({
define: {
prop: {
set: function (newVal, setter) {
setter("foo" + newVal);
}
}
}
});
def = new Defined();
def.attr("prop", "bar");
equal(def.attr("prop"), "foobar", "setter callback works");
});
test("basics remove", function () {
var ViewModel = can.Map.extend({
define: {
makeId: {
remove: function () {
this.removeAttr("models");
}
},
models: {
remove: function () {
this.removeAttr("modelId");
}
},
modelId: {
remove: function () {
this.removeAttr("years");
}
},
years: {
remove: function () {
this.removeAttr("year");
}
}
}
});
var mmy = new ViewModel({
makes: [
{id: 1}
],
makeId: 1,
models: [
{id: 2}
],
modelId: 2,
years: [2010],
year: 2010
});
var events = ["year", "years", "modelId", "models", "makeId"],
eventCount = 0,
batchNum;
mmy.bind("change", function (ev, attr) {
if (batchNum === undefined) {
batchNum = ev.batchNum;
}
equal(attr, events[eventCount++], "got correct attribute");
ok(ev.batchNum && ev.batchNum === batchNum, "batched");
});
mmy.removeAttr("makeId");
});
test("basics get", function () {
var Person = can.Map.extend({
define: {
fullName: {
get: function () {
return this.attr("first") + " " + this.attr("last");
}
}
}
});
var p = new Person({first: "Justin", last: "Meyer"});
equal(p.attr("fullName"), "Justin Meyer", "sync getter works");
var Adder = can.Map.extend({
define: {
more: {
get: function (curVal, setVal) {
var num = this.attr("num");
setTimeout(function () {
setVal(num + 1);
}, 10);
}
}
}
});
var a = new Adder({num: 1}),
callbackVals = [
[2, undefined, function () {
a.attr("num", 2);
}],
[3, 2, function () {
start();
}]
],
callbackCount = 0;
a.bind("more", function (ev, newVal, oldVal) {
var vals = callbackVals[callbackCount++];
equal(newVal, vals[0], "newVal is correct");
equal(a.attr("more"), vals[0], "attr value is correct");
equal(oldVal, vals[1], "oldVal is correct");
setTimeout(vals[2], 10);
});
stop();
});
test("basic type", function () {
expect(6);
var Typer = can.Map.extend({
define: {
arrayWithAddedItem: {
type: function (value) {
if (value && value.push) {
value.push("item");
}
return value;
}
},
listWithAddedItem: {
type: function (value) {
if (value && value.push) {
value.push("item");
}
return value;
},
Type: can.List
}
}
});
var t = new Typer();
deepEqual(can.Map.keys(t), [], "no keys");
var array = [];
t.attr("arrayWithAddedItem", array);
deepEqual(array, ["item"], "updated array");
equal(t.attr("arrayWithAddedItem"), array, "leave value as array");
t.attr("listWithAddedItem", []);
ok(t.attr("listWithAddedItem") instanceof can.List, "convert to can.List");
equal(t.attr("listWithAddedItem").attr(0), "item", "has item in it");
t.bind("change", function (ev, attr) {
equal(attr, "listWithAddedItem.1", "got a bubbling event");
});
t.attr("listWithAddedItem").push("another item");
});
test("basic Type", function () {
var Foo = function (name) {
this.name = name;
};
Foo.prototype.getName = function () {
return this.name;
};
var Typer = can.Map.extend({
define: {
foo: {
Type: Foo
}
}
});
var t = new Typer({foo: "Justin"});
equal(t.attr("foo").getName(), "Justin", "correctly created an instance");
var brian = new Foo("brian");
t.attr("foo", brian);
equal(t.attr("foo"), brian, "same instances");
});
test("type converters", function () {
var Typer = can.Map.extend({
define: {
date: { type: 'date' },
string: {type: 'string'},
number: { type: 'number' },
'boolean': { type: 'boolean' },
htmlbool: { type: 'htmlbool' },
leaveAlone: { type: '*' }
}
});
var obj = {};
var t = new Typer({
date: 1395896701516,
string: 5,
number: '5',
'boolean': 'false',
htmlbool: "",
leaveAlone: obj
});
ok(t.attr("date") instanceof Date, "converted to date");
equal(t.attr("string"), '5', "converted to string");
equal(t.attr("number"), 5, "converted to number");
equal(t.attr("boolean"), false, "converted to boolean");
equal(t.attr("htmlbool"), true, "converted to htmlbool");
equal(t.attr("leaveAlone"), obj, "left as object");
t.attr({
'number': '15'
});
ok(t.attr("number") === 15, "converted to number");
});
test("basics value", function () {
var Typer = can.Map.extend({
define: {
prop: { value: 'foo' }
}
});
equal(new Typer().attr('prop'), "foo", "value is used as default value");
var Typer2 = can.Map.extend({
define: {
prop: {
value: function () {
return [];
},
type: "*"
}
}
});
var t1 = new Typer2(),
t2 = new Typer2();
ok(t1.attr("prop") !== t2.attr("prop"), "different array instances");
ok(can.isArray(t1.attr("prop")), "its an array");
});
test("basics Value", function () {
var Typer = can.Map.extend({
define: {
prop: {
Value: Array,
type: "*"
}
}
});
var t1 = new Typer(),
t2 = new Typer();
ok(t1.attr("prop") !== t2.attr("prop"), "different array instances");
ok(can.isArray(t1.attr("prop")), "its an array");
});
test("setter with no arguments and returns undefined does the default behavior, the setter is for side effects only", function () {
var Typer = can.Map.extend({
define: {
prop: {
set: function () {
this.attr("foo", "bar");
}
}
}
});
var t = new Typer();
t.attr("prop", false);
deepEqual(t.attr(), { foo: "bar", prop: false });
});
test("type happens before the set", function () {
var MyMap = can.Map.extend({
define: {
prop: {
type: "number",
set: function (newValue) {
equal(typeof newValue, "number", "got a number");
return newValue + 1;
}
}
}
});
var map = new MyMap();
map.attr("prop", "5");
equal(map.attr("prop"), 6, "number");
});
test("getter and setter work", function () {
expect(5);
var Paginate = can.Map.extend({
define: {
page: {
set: function (newVal) {
this.attr('offset', (parseInt(newVal) - 1) * this.attr('limit'));
},
get: function () {
return Math.floor(this.attr('offset') / this.attr('limit')) + 1;
}
}
}
});
var p = new Paginate({limit: 10, offset: 20});
equal(p.attr("page"), 3, "page get right");
p.bind("page", function (ev, newValue, oldValue) {
equal(newValue, 2, "got new value event");
equal(oldValue, 3, "got old value event");
});
p.attr("page", 2);
equal(p.attr("page"), 2, "page set right");
equal(p.attr("offset"), 10, "page offset set");
});
test("getter with initial value", function(){
var compute = can.compute(1);
var Grabber = can.Map.extend({
define: {
vals: {
type: "*",
Value: Array,
get: function(current, setVal){
if(setVal){
current.push( compute() );
}
return current;
}
}
}
});
var g = new Grabber();
// This assertion doesn't mean much. It's mostly testing
// that there were no errors.
equal(g.attr("vals").length,0,"zero items in array" );
});
test("serialize basics", function(){
var MyMap = can.Map.extend({
define: {
name: {
serialize: function(){
return;
}
},
locations: {
serialize: false
},
locationIds: {
get: function(){
var ids = [];
this.attr('locations').each(function(location){
ids.push(location.id);
});
return ids;
},
serialize: function(locationIds){
return locationIds.join(',');
}
},
bared: {
get: function(){
return this.attr("name")+"+bar";
},
serialize: true
},
ignored: {
get: function(){
return this.attr("name")+"+ignored";
}
}
}
});
var map = new MyMap({name: "foo"});
map.attr("locations", [{id: 1, name: "Chicago"}, {id: 2, name: "LA"}]);
equal(map.attr("locationIds").length, 2, "get locationIds");
equal(map.attr("locationIds")[0], 1, "get locationIds index 0");
equal(map.attr("locations")[0].id, 1, "get locations index 0");
var serialized = map.serialize();
equal(serialized.locations, undefined, "locations doesn't serialize");
equal(serialized.locationIds, "1,2", "locationIds serializes");
equal(serialized.name, undefined, "name doesn't serialize");
equal(serialized.bared, "foo+bar", "true adds computed props");
equal(serialized.ignored, undefined, "computed props are not serialized by default");
});
test("serialize context", function(){
var context, serializeContext;
var MyMap = can.Map.extend({
define: {
name: {
serialize: function(obj){
context = this;
return obj;
}
}
},
serialize: function(){
serializeContext = this;
can.Map.prototype.serialize.apply(this, arguments);
}
});
var map = new MyMap();
map.serialize();
equal(context, map);
equal(serializeContext, map);
});
test("methods contexts", function(){
var contexts = {};
var MyMap = can.Map.extend({
define: {
name: {
value: 'John Galt',
get: function(obj){
contexts.get = this;
return obj;
},
remove: function(obj){
contexts.remove = this;
return obj;
},
set: function(obj){
contexts.set = this;
return obj;
},
serialize: function(obj){
contexts.serialize = this;
return obj;
},
type: function(val){
contexts.type = this;
return val;
}
}
}
});
var map = new MyMap();
map.serialize();
map.removeAttr('name');
equal(contexts.get, map);
equal(contexts.remove, map);
equal(contexts.set, map);
equal(contexts.serialize, map);
equal(contexts.type, map);
});
test("value generator is not called if default passed", function () {
var TestMap = can.Map.extend({
define: {
foo: {
value: function () {
throw '"foo"\'s value method should not be called.';
}
}
}
});
var tm = new TestMap({ foo: 'baz' });
equal(tm.attr('foo'), 'baz');
});
test("Value generator can read other properties", function () {
var Map = can.Map.extend({
letters: 'ABC',
numbers: [1, 2, 3],
define: {
definedLetters: {
value: 'DEF'
},
definedNumbers: {
value: [4, 5, 6]
},
generatedLetters: {
value: function () {
return 'GHI';
}
},
generatedNumbers: {
value: function () {
return new can.List([7, 8, 9]);
}
},
// Get prototype defaults
firstLetter: {
value: function () {
return this.attr('letters').substr(0, 1);
}
},
firstNumber: {
value: function () {
return this.attr('numbers.0');
}
},
// Get defined simple `value` defaults
middleLetter: {
value: function () {
return this.attr('definedLetters').substr(1, 1);
}
},
middleNumber: {
value: function () {
return this.attr('definedNumbers.1');
}
},
// Get defined `value` function defaults
lastLetter: {
value: function () {
return this.attr('generatedLetters').substr(2, 1);
}
},
lastNumber: {
value: function () {
return this.attr('generatedNumbers.2');
}
}
}
});
var map = new Map();
var prefix = 'Was able to read dependent value from ';
equal(map.attr('firstLetter'), 'A',
prefix + 'traditional can.Map style property definition');
equal(map.attr('firstNumber'), 1,
prefix + 'traditional can.Map style property definition');
equal(map.attr('middleLetter'), 'E',
prefix + 'define plugin style default property definition');
equal(map.attr('middleNumber'), 5,
prefix + 'define plugin style default property definition');
equal(map.attr('lastLetter'), 'I',
prefix + 'define plugin style generated default property definition');
equal(map.attr('lastNumber'), 9,
prefix + 'define plugin style generated default property definition');
});
test('default behaviors with "*" work for attributes', function() {
expect(9);
var DefaultMap = can.Map.extend({
define: {
someNumber: {
value: '5'
},
'*': {
type: 'number',
serialize: function(value) {
return '' + value;
},
set: function(newVal) {
ok(true, 'set called');
return newVal;
},
remove: function(currentVal) {
ok(true, 'remove called');
return false;
}
}
}
});
var map = new DefaultMap(),
serializedMap;
equal(map.attr('someNumber'), 5, 'value of someNumber should be converted to a number');
map.attr('number', '10'); // Custom set should be called
equal(map.attr('number'), 10, 'value of number should be converted to a number');
map.removeAttr('number'); // Custom removed should be called
equal(map.attr('number'), 10, 'number should not be removed');
serializedMap = map.serialize();
equal(serializedMap.number, '10', 'number serialized as string');
equal(serializedMap.someNumber, '5', 'someNumber serialized as string');
equal(serializedMap['*'], undefined, '"*" is not a value in serialized object');
});
test('models properly serialize with default behaviors', function() {
var DefaultMap = can.Map.extend({
define: {
name: {
value: 'Alex'
},
shirt: {
value: 'blue',
serialize: true
},
'*': {
serialize: false
}
}
});
var map = new DefaultMap({age: 10, name: 'John'}),
serializedMap = map.serialize();
equal(serializedMap.age, undefined, 'age doesn\'t exist');
equal(serializedMap.name, undefined, 'name doesn\'t exist');
equal(serializedMap.shirt, 'blue', 'shirt exists');
});
test("nested define", function() {
var nailedIt = 'Nailed it';
var Example = can.Map.extend({ }, {
define: {
name: {
value: nailedIt
}
}
});
var NestedMap = can.Map.extend({ }, {
define: {
isEnabled: {
value: true
},
test: {
Value: Example
},
examples: {
value: {
define: {
one: {
Value: Example
},
two: {
value: {
define: {
deep: {
Value: Example
}
}
}
}
}
}
}
}
});
var nested = new NestedMap();
// values are correct
equal(nested.attr('test.name'), nailedIt);
equal(nested.attr('examples.one.name'), nailedIt);
equal(nested.attr('examples.two.deep.name'), nailedIt);
// objects are correctly instanced
ok(nested.attr('test') instanceof Example);
ok(nested.attr('examples.one') instanceof Example);
ok(nested.attr('examples.two.deep') instanceof Example);
});
test('Can make an attr alias a compute (#1470)', 9, function(){
var computeValue = can.compute(1);
var GetMap = can.Map.extend({
define: {
value: {
set: function(newValue, setVal, setErr, oldValue){
if(newValue.isComputed) {
return newValue;
}
if(oldValue && oldValue.isComputed) {
oldValue(newValue);
return oldValue;
}
return newValue;
},
get: function(value){
return value && value.isComputed ? value() : value;
}
}
}
});
var getMap = new GetMap();
getMap.attr("value", computeValue);
equal(getMap.attr("value"), 1);
var bindCallbacks = 0;
getMap.bind("value", function(ev, newVal, oldVal){
switch(bindCallbacks) {
case 0:
equal(newVal, 2, "0 - bind called with new val");
equal(oldVal, 1, "0 - bind called with old val");
break;
case 1:
equal(newVal, 3, "1 - bind called with new val");
equal(oldVal, 2, "1 - bind called with old val");
break;
case 2:
equal(newVal, 4, "2 - bind called with new val");
equal(oldVal, 3, "2 - bind called with old val");
break;
}
bindCallbacks++;
});
// Try updating the compute's value
computeValue(2);
// Try setting the value of the property
getMap.attr("value", 3);
equal(getMap.attr("value"), 3, "read value is 3");
equal(computeValue(), 3, "the compute value is 3");
// Try setting to a new comptue
var newComputeValue = can.compute(4);
getMap.attr("value", newComputeValue);
});
test('setting a value of a property with type "compute" triggers change events', function () {
var handler;
var message = 'The change event passed the correct {prop} when set with {method}';
var createChangeHandler = function (expectedOldVal, expectedNewVal, method) {
return function (ev, newVal, oldVal) {
var subs = { prop: 'newVal', method: method };
equal(newVal, expectedNewVal, can.sub(message, subs));
subs.prop = 'oldVal';
equal(oldVal, expectedOldVal, can.sub(message, subs));
};
};
var ComputableMap = can.Map.extend({
define: {
computed: {
type: 'compute',
}
}
});
var computed = can.compute(0);
var m1 = new ComputableMap({
computed: computed
});
equal(m1.attr('computed'), 0, 'm1 is 1');
handler = createChangeHandler(0, 1, ".attr('computed', newVal)");
handler = createChangeHandler(0, 1, ".attr('computed', newVal)");
m1.bind('computed', handler);
m1.attr('computed', 1);
m1.unbind('computed', handler);
handler = createChangeHandler(1, 2, "computed()");
m1.bind('computed', handler);
computed(2);
m1.unbind('computed', handler);
});
test('replacing the compute on a property with type "compute"', function () {
var compute1 = can.compute(0);
var compute2 = can.compute(1);
var ComputableMap = can.Map.extend({
define: {
computable: {
type: 'compute'
}
}
});
var m = new ComputableMap();
m.attr('computable', compute1);
equal(m.attr('computable'), 0, 'compute1 readable via .attr()');
m.attr('computable', compute2);
equal(m.attr('computable'), 1, 'compute2 readable via .attr()');
});
// The old attributes plugin interferes severly with this test.
// TODO remove this condition when taking the plugins out of the main repository
test('value and get (#1521)', function () {
var MyMap = can.Map.extend({
define: {
data: {
value: function () {
return new can.List(['test']);
}
},
size: {
value: 1,
get: function (val) {
var list = this.attr('data');
var length = list.attr('length');
return val + length;
}
}
}
});
var map = new MyMap({});
equal(map.attr('size'), 2);
});
test("One event on getters (#1585)", function(){
var AppState = can.Map.extend({
define: {
person: {
get: function(lastSetValue, setAttrValue) {
if (lastSetValue) {
return lastSetValue;
} else if (this.attr("personId")) {
setAttrValue( new can.Map({name: "Jose", id: 5}) );
} else {
return null;
}
}
}
}
});
var appState = new AppState();
var personEvents = 0;
appState.bind("person", function(ev, person) {
personEvents++;
});
appState.attr("personId", 5);
appState.attr("person", new can.Map({
name: "Julia"
}));
equal(personEvents,2);
});
test('Can read a defined property with a set/get method (#1648)', function () {
// Problem: "get" is not passed the correct "lastSetVal"
// Problem: Cannot read the value of "foo"
var Map = can.Map.extend({
define: {
foo: {
value: '',
set: function (setVal) {
return setVal;
},
get: function (lastSetVal) {
return lastSetVal;
}
}
}
});
var map = new Map();
equal(map.attr('foo'), '', 'Calling .attr(\'foo\') returned the correct value');
map.attr('foo', 'baz');
equal(map.attr('foo'), 'baz', 'Calling .attr(\'foo\') returned the correct value');
});
test('Can bind to a defined property with a set/get method (#1648)', 3, function () {
// Problem: "get" is not called before and after the "set"
// Problem: Function bound to "foo" is not called
// Problem: Cannot read the value of "foo"
var Map = can.Map.extend({
define: {
foo: {
value: '',
set: function (setVal) {
return setVal;
},
get: function (lastSetVal) {
return lastSetVal;
}
}
}
});
var map = new Map();
map.bind('foo', function () {
ok(true, 'Bound function is called');
});
equal(map.attr('foo'), '', 'Calling .attr(\'foo\') returned the correct value');
map.attr('foo', 'baz');
equal(map.attr('foo'), 'baz', 'Calling .attr(\'foo\') returned the correct value');
});
test("type converters handle null and undefined in expected ways (1693)", function () {
var Typer = can.Map.extend({
define: {
date: { type: 'date' },
string: {type: 'string'},
number: { type: 'number' },
'boolean': { type: 'boolean' },
htmlbool: { type: 'htmlbool' },
leaveAlone: { type: '*' }
}
});
var t = new Typer().attr({
date: undefined,
string: undefined,
number: undefined,
'boolean': undefined,
htmlbool: undefined,
leaveAlone: undefined
});
equal(t.attr("date"), undefined, "converted to date");
equal(t.attr("string"), undefined, "converted to string");
equal(t.attr("number"), undefined, "converted to number");
equal(t.attr("boolean"), false, "converted to boolean");
equal(t.attr("htmlbool"), false, "converted to htmlbool");
equal(t.attr("leaveAlone"), undefined, "left as object");
t = new Typer().attr({
date: null,
string: null,
number: null,
'boolean': null,
htmlbool: null,
leaveAlone: null
});
equal(t.attr("date"), null, "converted to date");
equal(t.attr("string"), null, "converted to string");
equal(t.attr("number"), null, "converted to number");
equal(t.attr("boolean"), false, "converted to boolean");
equal(t.attr("htmlbool"), false, "converted to htmlbool");
equal(t.attr("leaveAlone"), null, "left as object");
});
test('Initial value does not call getter', function() {
expect(0);
var Map = can.Map.extend({
define: {
count: {
get: function(lastVal) {
ok(false, 'Should not be called');
return lastVal;
}
}
}
});
new Map({ count: 100 });
});
test("getters produce change events", function(){
var Map = can.Map.extend({
define: {
count: {
get: function(lastVal) {
return lastVal;
}
}
}
});
var map = new Map();
map.bind("change", function(){
ok(true, "change called");
});
map.attr("count", 22);
});
test("Asynchronous virtual properties cause extra recomputes (#1915)", function() {
stop();
var ran = false;
var VM = can.Map.extend({
define : {
foo : {
get : function(lastVal, setVal) {
setTimeout(function() {
if (setVal) {
setVal(5);
}
}, 10);
}
},
bar : {
get : function() {
var foo = this.attr('foo');
if (foo) {
if (ran) {
ok(false, 'Getter ran twice');
}
ran = true;
return foo * 2;
}
}
}
}
});
var vm = new VM();
vm.bind('bar', function() {});
setTimeout(function() {
equal(vm.attr('bar'), 10);
start();
}, 200);
});
test("double get in a compute (#2230)", function() {
var VM = can.Map.extend({
define : {
names : {
get : function(val, setVal) {
ok(setVal, "setVal passed");
return 'Hi!';
}
}
}
});
var vm = new VM();
var c = can.compute(function(){
return vm.attr("names");
});
c.bind("change", function(){});
});
test("Define plugin supports can.List (#1127)", 2, function(){
var MyList = can.List.extend({
define: {
idMap: {
get: function() {
var map = {};
this.each(function(item) {
map[item.attr("id")] = item.attr();
});
return map;
},
type: "*"
}
}
});
var list = new MyList([{
id: 1,
name: "1"
}, {
id: 2,
name: "2"
}, {
id: 3,
name: "3"
}]);
deepEqual(list.attr("idMap"), {
"1": {id: 1,name: "1"},
"2": {id: 2,name: "2"},
"3": {id: 3,name: "3"}
}, "can read");
list.bind("idMap", function(ev, newVal) {
deepEqual(newVal, {
"1": {id: 1,name: "1"},
"2": {id: 2,name: "2"}
}, "got event");
});
list.pop();
});
test("compute props can be set to null or undefined (#2372)", function(assert) {
var VM = can.Map.extend({ define: {
foo: { type: 'compute' }
}});
var vmNull = new VM({foo: null});
assert.equal(vmNull.foo, null, "foo is null, no error thrown");
var vmUndef = new VM({foo: undefined});
assert.equal(vmUndef.foo, undefined, "foo is undefined, no error thrown");
});
test("can inherit computes from another map (#1322)", 4, function(){
var string1 = 'a string';
var string2 = 'another string';
var MapA = can.Map.extend({
define: {
propA: {
get: function() {
return string1;
}
},
propB: {
get: function() {
return string1;
},
set: function(newVal) {
equal(newVal, string1, 'set was called');
}
}
}
});
var MapB = MapA.extend({
define: {
propC: {
get: function() {
return string2;
}
},
propB: {
get: function() {
return string2;
}
}
}
});
var map = new MapB();
equal(map.attr('propC'), string2, 'props only in the child have the correct values');
equal(map.attr('propB'), string2, 'props in both have the child values');
equal(map.attr('propA'), string1, 'props only in the parent have the correct values');
map.attr('propB', string1);
});
test("can inherit primitive values from another map (#1322)", function(){
var string1 = 'a';
var string2 = 'b';
var MapA = can.Map.extend({
define: {
propA: {
value: string1
},
propB: {
value: string1
}
}
});
var MapB = MapA.extend({
define: {
propC: {
value: string2
},
propB: {
value: string2
}
}
});
var map = new MapB();
equal(map.propC, string2, 'props only in the child have the correct values');
equal(map.propB, string2, 'props in both have the child values');
equal(map.propA, string1, 'props only in the parent have the correct values');
});
test("can inherit object values from another map (#1322)", function(){
var object1 = {a: 'a'};
var object2 = {b: 'b'};
var MapA = can.Map.extend({
define: {
propA: {
get: function() {
return object1;
}
},
propB: {
get: function() {
return object1;
}
}
}
});
var MapB = MapA.extend({
define: {
propB: {
get: function() {
return object2;
}
},
propC: {
get: function() {
return object2;
}
}
}
});
var map = new MapB();
equal(map.attr('propC'), object2, 'props only in the child have the correct values');
equal(map.attr('propB'), object2, 'props in both have the child values');
equal(map.attr('propA'), object1, 'props only in the parent have the correct values');
});
test("value function not set on constructor defaults", function(){
var MyMap = can.Map.extend({
define: {
propA: {
value: function(){
return 1;
}
}
}
});
var map = new MyMap();
equal(MyMap.defaults.propA, undefined, 'Generator function does not result in property set on defaults');
notEqual(MyMap.defaultGenerators.propA, undefined, 'Generator function set on defaultGenerators');
equal(map.attr("propA"), 1, 'Instance value set properly'); //this is mainly so that CI doesn't complain about unused variable
});
});