Skip to content

Commit

Permalink
Add more strict tests to Attribute
Browse files Browse the repository at this point in the history
Fixes #173
  • Loading branch information
ipeychev committed Apr 15, 2015
1 parent bdadb45 commit 4fbb8db
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 20 deletions.
48 changes: 37 additions & 11 deletions src/ui/react/src/oop/attribute.js
Expand Up @@ -15,7 +15,12 @@
Attribute.prototype = {
constructor: Attribute,


/**
* Retrieves the value of an attribute.
*
* @param {String} attr The attribute which value should be retrieved.
* @return {Any} The value of the attribute.
*/
get: function(attr) {
var currentAttr = this.constructor.ATTRS[attr];

Expand All @@ -36,6 +41,12 @@
return curValue;
},

/**
* Sets the value of an attribute.
*
* @param {String} attr The attribute which value should be set.
* @param {Any} value The value which should be set to the attribute.
*/
set: function(attr, value) {
var currentAttr = this.constructor.ATTRS[attr];

Expand Down Expand Up @@ -91,12 +102,18 @@
return result;
},

/**
* Initializes an attribute. Sets its default value depending on the flags of the
* attribute and the passed configuration object to the constructor.
*
* @param {String} attr The name of the attribute which have to be initialized.
*/
_init: function(attr) {
var value;

var currentAttr = this.constructor.ATTRS[attr];

// Get
// Check if there is default value or passed one via configuration object
var hasDefaultValue = Object.prototype.hasOwnProperty.call(currentAttr, 'value');
var hasPassedValueViaConfig = Object.prototype.hasOwnProperty.call(this.__config__, attr);

Expand All @@ -122,34 +139,43 @@
return;
}
}
// These two cases below are easy - set the value to be from the passed config or from the
// default value, in this order.
// These two cases below are easy - set the value to be from the passed config or
// from the default value, in this order.
else if (hasPassedValueViaConfig) {
value = this.__config__[attr];
} else if (hasDefaultValue) {
value = currentAttr.value;
}

// If there is validator, check the returned value. If it is false, then set as initial value
// the default one. However, if there is no default value, just return.
if (currentAttr.validator && !this._callStringOrFunction(currentAttr.validator, value)) {
// If there is validator, and user passed config object - check the returned value.
// If it is false, then set as initial value the default one.
// However, if there is no default value, just return.
if (currentAttr.validator && hasPassedValueViaConfig && !this._callStringOrFunction(currentAttr.validator, value)) {
if (hasDefaultValue) {
value = currentAttr.value;
} else {
return;
}
}

// If there is setter, pass thought it the value. The value might be one from defaultFn, default value or
// provided from the config.
if (currentAttr.setter) {
// If there is setter and user passed config object - pass the value thought the setter.
// The value might be one from defaultFn, default value or provided from the config.
if (currentAttr.setter && hasPassedValueViaConfig) {
value = this._callStringOrFunction(currentAttr.setter, value);
}

// Finally, set the value as init value to the storage with values.
// Finally, set the value as initial value to the storage with values.
this.__ATTRS__[attr] = value;
},

/**
* Checks if an attribute is initialized. An attribute is considered as initialized
* when there is an own property with this name in the local collection of attribute values
* for the current instance.
*
* @param {String} attr The attribute which should be checked if it is initialized.
* @return {Boolean} Returns true if the attribute has been initialized, false otherwise.
*/
_isInitialized: function(attr) {
return Object.prototype.hasOwnProperty.call(this.__ATTRS__, attr);
}
Expand Down
167 changes: 158 additions & 9 deletions src/ui/react/test/attribute.js
Expand Up @@ -67,11 +67,13 @@
Clazz.superclass.constructor.call(this, config);
};

var getter = sinon.spy(function(val) {
return val + '-test1';
});

Clazz.ATTRS = {
attr1: {
getter: function(val) {
return val + '-test1';
},
getter: getter,
value: 'val1'
}
};
Expand All @@ -80,15 +82,20 @@

var inst = new Clazz();

assert.strictEqual('val1-test1', inst.get('attr1'));
var val = inst.get('attr1');

assert.ok(getter.calledOnce);
assert.strictEqual('val1-test1', val);
});

it('should invoke the setter of an attribute', function() {
var Clazz = function(config) {
Clazz.superclass.constructor.call(this, config);
};

var setter = sinon.spy();
var setter = sinon.spy(function(val) {
return 'chema: ' + val;
});

Clazz.ATTRS = {
attr1: {
Expand All @@ -105,9 +112,10 @@
var val = inst.get('attr1');

assert.ok(setter.calledOnce);
assert.strictEqual('chema: val1', val);
});

it('should invoke the setter of an attribute when there is default value', function() {
it('should not invoke the setter of an attribute when there is default value', function() {
var Clazz = function(config) {
Clazz.superclass.constructor.call(this, config);
};
Expand All @@ -126,10 +134,34 @@
var inst = new Clazz();

inst.get('attr1');
assert.ok(setter.notCalled);
});

it('should invoke the setter of an attribute via set method', function() {
var Clazz = function(config) {
Clazz.superclass.constructor.call(this, config);
};

var setter = sinon.spy(function(val) {
return val;
});

Clazz.ATTRS = {
attr1: {
setter: setter,
value: 'val1'
}
};

Clazz = AlloyEditor.OOP.extend(Clazz, AlloyEditor.Attribute);

var inst = new Clazz();

inst.set('attr1', 'val2');
assert.ok(setter.calledOnce);
});

it('should not change the value of a read only attribute', function() {
it('should not change the value of a readOnly attribute', function() {
var Clazz = function(config) {
Clazz.superclass.constructor.call(this, config);
};
Expand All @@ -150,7 +182,28 @@
assert.strictEqual('val1', inst.get('attr1'));
});

it('should not change the value of a read only attribute when there is no default value', function() {
it('should not change the value of a readOnly attribute via set method', function() {
var Clazz = function(config) {
Clazz.superclass.constructor.call(this, config);
};

Clazz.ATTRS = {
attr1: {
readOnly: true,
value: 'val1'
}
};

Clazz = AlloyEditor.OOP.extend(Clazz, AlloyEditor.Attribute);

var inst = new Clazz();

inst.set('attr1', 'val2');

assert.strictEqual('val1', inst.get('attr1'));
});

it('should not change the value of a readOnly attribute when there is no default value', function() {
var Clazz = function(config) {
Clazz.superclass.constructor.call(this, config);
};
Expand All @@ -170,7 +223,7 @@
assert.isUndefined(inst.get('attr1'));
});

it('should not change the value of a read only attribute when writeOnce is set', function() {
it('should not change the value of a readOnly attribute when writeOnce is set', function() {
var Clazz = function(config) {
Clazz.superclass.constructor.call(this, config);
};
Expand Down Expand Up @@ -281,6 +334,53 @@
assert.strictEqual('val1', inst.get('attr1'));
});

it('should not set value if validator returns false via set method', function() {
var Clazz = function(config) {
Clazz.superclass.constructor.call(this, config);
};

Clazz.ATTRS = {
attr1: {
validator: function(val) {
return false;
},
value: 'val1'
}
};

Clazz = AlloyEditor.OOP.extend(Clazz, AlloyEditor.Attribute);

var inst = new Clazz();

inst.set('attr1', 'val2');
assert.strictEqual('val1', inst.get('attr1'));
});

it('should not set value if validator returns false via config there is no default value', function() {
var Clazz = function(config) {
Clazz.superclass.constructor.call(this, config);
};

var validator = sinon.spy(function(val) {
return false;
});

Clazz.ATTRS = {
attr1: {
validator: validator
}
};

Clazz = AlloyEditor.OOP.extend(Clazz, AlloyEditor.Attribute);

var inst = new Clazz({
attr1: 'val2'
});

assert.ok(validator.notCalled);
assert.isUndefined(inst.get('attr1'));
});

it('should set value if validator returns true', function() {
var Clazz = function(config) {
Clazz.superclass.constructor.call(this, config);
Expand All @@ -304,6 +404,29 @@
assert.strictEqual('val2', inst.get('attr1'));
});

it('should not call validator when there is default value', function() {
var Clazz = function(config) {
Clazz.superclass.constructor.call(this, config);
};

var validator = sinon.spy(function(val) {
return true;
});

Clazz.ATTRS = {
attr1: {
validator: validator,
value: 'val1'
}
};

Clazz = AlloyEditor.OOP.extend(Clazz, AlloyEditor.Attribute);

var inst = new Clazz();

assert.ok(validator.notCalled);
});

it('should not share values from two objects', function() {
var Clazz1 = function(config) {
Clazz1.superclass.constructor.call(this, config);
Expand Down Expand Up @@ -339,5 +462,31 @@
assert.strictEqual('val1.1', inst1.get('attr1'));
assert.strictEqual('val2.2', inst2.get('attr1'));
});

it('should set value from valueFn', function() {
var Clazz = function(config) {
Clazz.superclass.constructor.call(this, config);
};

var valueFn = sinon.spy(function() {
return 'value from valueFn';
});

Clazz.ATTRS = {
attr1: {
valueFn: valueFn,
value: 'val1'
}
};

Clazz = AlloyEditor.OOP.extend(Clazz, AlloyEditor.Attribute);

var inst = new Clazz();

var val = inst.get('attr1');

assert.ok(valueFn.calledOnce);
assert.strictEqual('value from valueFn', val);
});
});
}());

0 comments on commit 4fbb8db

Please sign in to comment.