Browse Source

AMBARI-10325 Implement automatic unit conversion support for all new widgets. (ababiichuk)

aBabiichuk 10 years ago
parent
commit
77c0be2f0d

+ 1 - 0
ambari-web/app/assets/test/tests.js

@@ -148,6 +148,7 @@ var files = ['test/init_model_test',
   'test/mixins/wizard/wizard_menu_view_test',
   'test/mixins/wizard/wizardProgressPageController_test',
   'test/mixins/wizard/wizardEnableDone_test',
+  'test/mixins/unit_convert/base_unit_convert_mixin_test',
   'test/utils/ajax/ajax_test',
   'test/utils/ajax/ajax_queue_test',
   'test/utils/batch_scheduled_requests_test',

+ 2 - 0
ambari-web/app/mixins.js

@@ -44,3 +44,5 @@ require('mixins/wizard/addSecurityConfigs');
 require('mixins/wizard/wizard_menu_view');
 require('mixins/common/configs/enhanced_configs');
 require('mixins/common/widget_mixin');
+require('mixins/unit_convert/base_unit_convert_mixin');
+require('mixins/unit_convert/convert_unit_widget_view_mixin');

+ 176 - 0
ambari-web/app/mixins/unit_convert/base_unit_convert_mixin.js

@@ -0,0 +1,176 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+
+App.BaseUnitConvertMixin = Em.Mixin.create({
+  /**
+   * Which type of units should be used e.g. size, time.
+   * Conversion dimensions will be used according this type.
+   * For more info regarding dimension table @see convertMapTable property.
+   *
+   * This property can be used in mixed object to specify which dimension table map will
+   * be used for conversion.
+   * @property currentDimensionType
+   * @type {String}
+   */
+  currentDimensionType: null,
+
+  convertMapTable: Em.Object.create({
+    size: {
+      b: 1024,
+      kb: 1024,
+      mb: 1024,
+      gb: 1024,
+      tb: 1024,
+      pb: 1024
+    },
+    time: {
+      milliseconds: 1,
+      seconds: 1000,
+      minutes: 60,
+      hours: 60,
+      days: 24
+    }
+  }),
+
+  /**
+   * Convert value between different unit types. Coversion works for following directions:
+   *  single unit -> single unit e.g. from "s" -> "s".
+   *  single unit -> multi unit e.g. from "ms" -> "days,hours"
+   *  multi unit -> single unit e.g. from "days,hours" -> "ms"
+   * For single -> single unit conversion returned value will be as is or an array of objects if
+   * isObjectOutput flag passed.
+   * If any passed unit is multi dimensions like "days,hours" array of objects will return always.
+   * For example:
+   *  <code>
+   *     convertValue(1024, 'kb', 'kb') // returns 1024
+   *     convertValue(1024, 'kb', 'kb', true) // returns [{ type: 'kb', value: 1024 }]
+   *     convertValue(60000, 'ms', 'hours,minutes') // returns [{type: 'hours', value: 0 }, {type: 'minutes', value: 1}]
+   *  </code>
+   * For more examples see unit tests.
+   *
+   * @method convertValue
+   * @param {String|Number|Object[]} value - value to convert
+   * @param {String|String[]} fromUnit - specified value unit type(s) e.g. "mb"
+   *    Form multi dimensional format pass units separated with "," or list of unit types
+   *    e.g. "gb,mb", ['gb', 'mb']
+   * @param {Boolean} [isObjectOutput] - returned value should be object this option usefull for widgets where its
+   *    value should be an Object
+   * @param {String|String[]} toUnit - desired unit(s) to convert specified value. Same format as for `fromUnit`
+   * @returns {Number|Object[]} returns single value or array of objects according to multi unit format
+   */
+  convertValue: function(value, fromUnit, toUnit, isObjectOutput) {
+    var self = this;
+    var valueIsArray = Em.isArray(value);
+    if (!valueIsArray) {
+      value = +value;
+    }
+    // if desired unit not passed or fromUnit and toUnit are the same
+    // just return the value
+    if ((fromUnit == toUnit || !toUnit) && !isObjectOutput) {
+      return value;
+    }
+    if (isNaN(value) && !valueIsArray) {
+      return null;
+    }
+    // convert passed toUnit string to array of units by ','
+    // for example "mb,kb" to ['mb','kb']
+    if (!Em.isArray(toUnit)) {
+      toUnit = toUnit.split(',');
+    }
+    // process multi unit format
+    if (toUnit.length > 1 || isObjectOutput) {
+      // returned array has following structure
+      //  .value - value according to unit
+      //  .type - unit name
+      return toUnit.map(function(unit) {
+        var convertedValue = Math.floor(self._convertToSingleValue(value, fromUnit, unit));
+        value -= self._convertToSingleValue(convertedValue, unit, fromUnit);
+        return {
+          value: convertedValue,
+          type: unit
+        };
+      });
+    }
+    // for single unit format just return single value
+    else {
+      if (!valueIsArray) {
+        return this._convertToSingleValue(value, fromUnit, toUnit[0]);
+      }
+      else {
+        return value.map(function(item) {
+          return self._convertToSingleValue(item.value, item.type, toUnit[0]);
+        }).reduce(Em.sum);
+      }
+    }
+  },
+
+  /**
+   * Get dimension table map. `currentDimensionType` will be used if specified or
+   * detected by property unit type.
+   *
+   * @method _converterGetUnitTable
+   * @private
+   * @param {String|String[]} unit - unit type
+   * @return {Object}
+   */
+  _converterGetUnitTable: function(unit) {
+    var unitType;
+    if (this.get('currentDimensionType')) {
+      unitType = this.get('currentDimensionType');
+    }
+    else {
+      unitType = Em.keys(this.get('convertMapTable')).filter(function(item) {
+        return Em.keys(this.get('convertMapTable.' + item)).contains(Em.isArray(unit) ? unit[0] : unit);
+      }, this)[0];
+    }
+    return this.get('convertMapTable.' + unitType);
+  },
+
+  /**
+   * @method _convertToSingleValue
+   * @private
+   * @param {Number} value - value to convert
+   * @param {String} fromUnit - unit type for current value
+   * @param {String} toUnit - desired unit type
+   * @return {Number}
+   */
+  _convertToSingleValue: function(value, fromUnit, toUnit) {
+    var convertTable = this._converterGetUnitTable(fromUnit);
+    var units = Em.keys(convertTable);
+    var fromUnitIndex = units.indexOf(fromUnit.toLowerCase());
+    var toUnitIndex = units.indexOf(toUnit.toLowerCase());
+    Em.assert("Invalid value unit type " + fromUnit, fromUnitIndex > -1);
+    Em.assert("Invalid desired unit type " + toUnit, toUnitIndex > -1);
+    if (fromUnitIndex == toUnitIndex) {
+      return value;
+    }
+    var range = [fromUnitIndex + 1, toUnitIndex + 1];
+    var processedUnits = Array.prototype.slice.apply(units, range[0] < range[1] ? range : range.slice().reverse());
+    var factor = processedUnits.map(function(unit) {
+      return Em.get(convertTable, unit);
+    }, this).reduce(function(p,c) { return p*c; });
+    if (range[0] < range[1]) {
+      return value / factor;
+    }
+    else {
+      return value * factor;
+    }
+  }
+});

+ 72 - 0
ambari-web/app/mixins/unit_convert/convert_unit_widget_view_mixin.js

@@ -0,0 +1,72 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+/**
+ * Unit conversion mixin for widget views.
+ * @type {Em.Mixin}
+ */
+App.ConvertUnitWidgetViewMixin = Em.Mixin.create(App.BaseUnitConvertMixin, {
+  /**
+   * Get converted value according to widget value format from specified config property value.
+   *
+   * @param {String|Number} value - value to convert
+   * @param {Boolean} returnObject - returned value should be an array of objects
+   * @returns {Number|Object[]}
+   */
+  widgetValueByConfigAttributes: function(value, returnObject) {
+    return this.convertValue(value, this._converterGetPropertyAttributes(), this._converterGetWidgetUnits(), returnObject);
+  },
+
+  /**
+   * Get converted value according to config property unit format from specified widget value.
+   *
+   * @param {String|Number|Object[]} value - value to convert
+   * @param {Boolean} returnObject - returned value should be an array of objects
+   * @returns {Number}
+   */
+  configValueByWidget: function(value) {
+    return this.convertValue(value, this._converterGetWidgetUnits(), this._converterGetPropertyAttributes());
+  },
+
+  /**
+   * @private
+   * @method _converterGetWidgetUnits
+   * @throw Error
+   * @returns {String|String[]}
+   */
+  _converterGetWidgetUnits: function() {
+    var widgetAttributes = this.get('config.stackConfigProperty.widget');
+    var widgetUnits = Em.getWithDefault(widgetAttributes, 'units.0.unit-name', false) || Em.getWithDefault(widgetAttributes, 'units.0.unit', false);
+    Em.assert('Invalid widget units for ' + this.get('config.name') + ' widget object: ' + JSON.stringify(widgetAttributes), widgetUnits);
+    return widgetUnits;
+  },
+
+  /**
+   * @private
+   * @method _converterGetPropertyAttributes
+   * @throw Error
+   * @returns {String|String[]}
+   */
+  _converterGetPropertyAttributes: function() {
+    var propertyAttributes = this.get('config.stackConfigProperty.valueAttributes');
+    var propertyUnits = Em.getWithDefault(propertyAttributes, 'unit', false);
+    Em.assert('Invalid property unit type for ' + this.get('config.name') + ' valueAttributes: ' + propertyAttributes, propertyUnits);
+    return propertyUnits;
+  }
+});

+ 1 - 1
ambari-web/app/views/common/configs/widgets/config_widget_view.js

@@ -22,7 +22,7 @@ require('views/common/controls_view');
  * Common view for config widgets
  * @type {Em.View}
  */
-App.ConfigWidgetView = Em.View.extend(App.SupportsDependentConfigs, App.WidgetPopoverSupport, {
+App.ConfigWidgetView = Em.View.extend(App.SupportsDependentConfigs, App.WidgetPopoverSupport, App.ConvertUnitWidgetViewMixin, {
 
   /**
    * @type {App.ConfigProperty}

+ 24 - 120
ambari-web/app/views/common/configs/widgets/time_interval_spinner_view.js

@@ -65,20 +65,7 @@ App.TimeIntervalSpinnerView = App.ConfigWidgetView.extend({
   minValue: null,
 
   /**
-   * Map with units lookup used for convertation.
-   *
-   * @property timeConvertMap
-   * @type {Object}
-   */
-  timeConvertMap: {
-    milliseconds: [],
-    seconds: [1000],
-    minutes: [60, 1000],
-    hours: [60, 60, 1000],
-    days: [24, 60, 60, 1000]
-  },
-
-  /**
+   * @TODO move it to unit conversion view mixin?
    * Map with maximum value per unit.
    *
    * @property timeMaxValueOverflow
@@ -93,7 +80,7 @@ App.TimeIntervalSpinnerView = App.ConfigWidgetView.extend({
   },
 
   didInsertElement: function () {
-    this.prepareContent();
+    Em.run.once(this, 'prepareContent');
     this._super();
   },
 
@@ -111,112 +98,27 @@ App.TimeIntervalSpinnerView = App.ConfigWidgetView.extend({
     var property = this.get('config');
     var propertyUnit = property.get('stackConfigProperty.valueAttributes').unit;
 
-    Em.run.once(function() {
-      self.set('propertyUnit', propertyUnit);
-      self.set('minValue', self.generateWidgetValue(property.get('stackConfigProperty.valueAttributes.minimum')));
-      self.set('maxValue', self.generateWidgetValue(property.get('stackConfigProperty.valueAttributes.maximum')));
-      self.set('content', self.generateWidgetValue(property.get('value')));
-    });
+    this.set('propertyUnit', propertyUnit);
+    this.set('minValue', this.generateWidgetValue(property.get('stackConfigProperty.valueAttributes.minimum')));
+    this.set('maxValue', this.generateWidgetValue(property.get('stackConfigProperty.valueAttributes.maximum')));
+    this.set('content', this.generateWidgetValue(property.get('value')));
   },
 
   /**
+   * @TODO move it to unit conversion view mixin?
    * Generate formatted value for widget.
    *
    * @param {String|Number} value
    * @returns {Object[]}
    */
   generateWidgetValue: function(value) {
-    var property = this.get('config');
-    var widgetUnits = property.get('stackConfigProperty.widget.units.firstObject.unit');
-    var propertyUnit = property.get('stackConfigProperty.valueAttributes').unit;
-    return this.convertToWidgetUnits(value, propertyUnit, widgetUnits);
-  },
-
-  /**
-   * Convert property value to widget format units.
-   *
-   * @param {String|Number} input - time to convert
-   * @param {String} inputUnitType - type of input value e.g. "milliseconds"
-   * @param {String|String[]} desiredUnits - units to convert input value e.g. ['days', 'hours', 'minutes']
-   *   or 'days,hours,minutes'
-   * @return {Object[]} - converted values according to desiredUnits order. Returns object
-   *   contains unit, value, label, minValue, maxValue, invertOnOverflow attributes according to desiredUnits array
-   *   For example:
-   *   <code>
-   *     [
-   *       {unit: 'days', label: 'Days', value: 2, minValue: 0, maxValue: 23},
-   *       {unit: 'hours', label: 'Hours', value: 3, minValue: 0, maxValue: 59}
-   *     ]
-   *   </code>
-   */
-  convertToWidgetUnits: function(input, inputUnitType, desiredUnits) {
-    var self = this;
-    var time = parseInt(input);
-    var msUnitMap = this.generateUnitsTable(desiredUnits, inputUnitType);
-    if (typeof desiredUnits == 'string') {
-      desiredUnits = desiredUnits.split(',');
-    }
-
-    return desiredUnits.map(function(item) {
-      var unitMs = msUnitMap[item];
-      var unitValue = Math.floor(time/unitMs);
-      time = time - unitValue*unitMs;
-
-      return {
-        label: Em.I18n.t('common.' + item),
-        unit: item,
-        value: unitValue,
-        minValue: 0,
-        maxValue: Em.get(self, 'timeMaxValueOverflow.' + item),
-        invertOnOverflow: true
-      };
-    });
-  },
-
-  /**
-   * Convert widget value to config property format.
-   *
-   * @param {Object[]} widgetValue - formatted value for widget
-   * @param {String} propertyUnit - config property unit to convert
-   * @return {Number}
-   */
-  convertToPropertyUnit: function(widgetValue, propertyUnit) {
-    var widgetUnitNames = widgetValue.mapProperty('unit');
-    var msUnitMap = this.generateUnitsTable(widgetUnitNames, propertyUnit);
-    return widgetUnitNames.map(function(item) {
-      return parseInt(Em.get(widgetValue.findProperty('unit', item), 'value')) * msUnitMap[item];
-    }).reduce(Em.sum);
-  },
-
-  /**
-   * Generate convertion map with specified unit.
-   *
-   * @param {String|String[]} units - units to convert
-   * @param {String} convertationUnit - unit factor name e.g 'milliseconds', 'minutes', 'seconds', etc.
-   */
-  generateUnitsTable: function(units, convertationUnit) {
-    var msUnitMap = $.extend({}, this.get('timeConvertMap'));
-    if (typeof units == 'string') {
-      units = units.split(',');
-    }
-    units.forEach(function(unit) {
-      var keys = Em.keys(msUnitMap);
-      // check the convertion level
-      var distance = keys.indexOf(unit) - keys.indexOf(convertationUnit);
-      var convertPath = msUnitMap[unit].slice(0, distance);
-      // set unit value of the property it always 1
-      if (distance === 0) {
-        msUnitMap[unit] = 1;
-      }
-      if (convertPath.length) {
-        // reduce convert path values to value
-        msUnitMap[unit] = convertPath.reduce(function(p,c) {
-          return p * c;
-        });
-      }
-    });
-
-    return msUnitMap;
+    return this.widgetValueByConfigAttributes(value, true).map(function(item) {
+      item.label = Em.I18n.t('common.' + item.type);
+      item.minValue = 0;
+      item.maxValue = Em.get(this, 'timeMaxValueOverflow.' + item.type);
+      item.invertOnOverflow = true;
+      return item;
+    }, this);
   },
 
   /**
@@ -225,25 +127,27 @@ App.TimeIntervalSpinnerView = App.ConfigWidgetView.extend({
   valueObserver: function() {
     if (!this.get('content')) return;
     var self = this;
-    Em.run.once(function() {
-      self.checkModified();
-      self.checkErrors();
-      self.setConfigValue();
-    });
+    Em.run.once(this, 'valueObserverCallback');
   }.observes('content.@each.value'),
 
+  valueObserverCallback: function() {
+    this.checkModified();
+    this.checkErrors();
+    this.setConfigValue();
+  },
+
   /**
    * Check for property modification.
    */
   checkModified: function() {
-    this.set('valueIsChanged', this.convertToPropertyUnit(this.get('content'), this.get('propertyUnit')) != parseInt(this.get('config.defaultValue')));
+    this.set('valueIsChanged', this.configValueByWidget(this.get('content')) != parseInt(this.get('config.defaultValue')));
   },
 
   /**
    * Check for validation errors like minimum or maximum required value.
    */
   checkErrors: function() {
-    var convertedValue = this.convertToPropertyUnit(this.get('content'), this.get('propertyUnit'));
+    var convertedValue = this.configValueByWidget(this.get('content'));
     var errorMessage = false;
     if (convertedValue < parseInt(this.get('config.stackConfigProperty.valueAttributes.minimum'))) {
       errorMessage = Em.I18n.t('number.validate.lessThanMinumum').format(this.dateToText(this.get('minValue')));
@@ -262,7 +166,7 @@ App.TimeIntervalSpinnerView = App.ConfigWidgetView.extend({
    * set appropriate attribute for configProperty model
    */
   setConfigValue: function() {
-    this.set('config.value', this.convertToPropertyUnit(this.get('content'), this.get('propertyUnit')));
+    this.set('config.value', this.configValueByWidget(this.get('content')));
   },
 
   /**

+ 115 - 0
ambari-web/test/mixins/unit_convert/base_unit_convert_mixin_test.js

@@ -0,0 +1,115 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var App = require('app');
+
+describe('App.BaseUnitConvertMixin', function() {
+  beforeEach(function() {
+    this.mixin = Em.Object.create(App.BaseUnitConvertMixin, {});
+  });
+
+  describe('#convertValue', function() {
+    var tests = [
+      {
+        value: "10",
+        fromUnit: "b",
+        toUnit: "b",
+        e: 10
+      },
+      {
+        value: "2048",
+        fromUnit: "b",
+        toUnit: "kb",
+        e: 2
+      },
+      {
+        value: "2097152",
+        fromUnit: "b",
+        toUnit: "mb",
+        e: 2
+      },
+      {
+        value: "1",
+        fromUnit: "mb",
+        toUnit: "kb",
+        e: 1024
+      },
+      {
+        value: "107374182400",
+        fromUnit: "b",
+        toUnit: "gb",
+        e: 100
+      },
+      {
+        value: "100",
+        fromUnit: "gb",
+        toUnit: "b",
+        e: 107374182400
+      },
+      {
+        value: "1294336",
+        fromUnit: "b",
+        toUnit: ["mb", "kb"],
+        e: [
+          { type: "mb", value: 1},
+          { type: "kb", value: 240}
+        ]
+      },
+      {
+        value: [
+          { type: "mb", value: 1},
+          { type: "kb", value: 240}
+        ],
+        fromUnit: ["mb", "kb"],
+        toUnit: "b",
+        e: 1294336,
+      },
+      {
+        value: [
+          { type: "mb", value: 1},
+          { type: "kb", value: 240}
+        ],
+        fromUnit: "mb,kb",
+        toUnit: "b",
+        e: 1294336,
+      },
+      {
+        value: 60000,
+        fromUnit: 'milliseconds',
+        toUnit: "days,hours,minutes",
+        e: [
+          { type: 'days', value: 0},
+          { type: 'hours', value: 0},
+          { type: 'minutes', value: 1}
+        ]
+      },
+    ];
+
+    tests.forEach(function(test) {
+      it('should convert {0} {1} to {2} {3}'.format(JSON.stringify(test.value), test.fromUnit, JSON.stringify(test.e), test.toUnit), function() {
+        var result = this.mixin.convertValue(test.value, test.fromUnit, test.toUnit);
+        if (Em.isArray(result)) {
+          result = result.map(function(item) {
+            return App.permit(item, ['type', 'value']);
+          });
+        }
+        expect(result).to.be.eql(test.e);
+      });
+    }, this);
+  });
+});

+ 29 - 57
ambari-web/test/views/common/configs/widgets/time_interval_spinner_view_test.js

@@ -19,18 +19,34 @@
 var App = require('app');
 
 describe('App.TimeIntervalSpinnerView', function() {
-
-  describe('#convertToWidgetUnits', function(){
+  describe('#generateWidgetValue', function(){
     beforeEach(function() {
       this.view = App.TimeIntervalSpinnerView.create({
         initPopover: Em.K
       });
     });
+
+    afterEach(function() {
+      this.view.destroy();
+      this.view = null;
+    });
+
+    var createProperty = function(widgetUnits, configPropertyUnits) {
+      return Em.Object.create({
+        stackConfigProperty: Em.Object.create({
+          widget: {
+            units: [
+              { unit: widgetUnits }
+            ]
+          },
+          valueAttributes: { unit: configPropertyUnits }
+        })
+      });
+    };
     var tests = [
       {
         input: 60000,
-        inputType: 'milliseconds',
-        desiredUnits: "days,hours,minutes",
+        config: createProperty("days,hours,minutes", "milliseconds"),
         e: [
           { label: 'Days', value: 0},
           { label: 'Hours', value: 0},
@@ -39,8 +55,7 @@ describe('App.TimeIntervalSpinnerView', function() {
       },
       {
         input: "2592000000",
-        inputType: 'milliseconds',
-        desiredUnits: "days,hours,minutes",
+        config: createProperty("days,hours,minutes", "milliseconds"),
         e: [
           { label: 'Days', value: 30},
           { label: 'Hours', value: 0},
@@ -49,8 +64,7 @@ describe('App.TimeIntervalSpinnerView', function() {
       },
       {
         input: "604800000",
-        inputType: 'milliseconds',
-        desiredUnits: "days,hours,minutes",
+        config: createProperty("days,hours,minutes", "milliseconds"),
         e: [
           { label: 'Days', value: 7},
           { label: 'Hours', value: 0},
@@ -59,8 +73,7 @@ describe('App.TimeIntervalSpinnerView', function() {
       },
       {
         input: "804820200",
-        inputType: 'milliseconds',
-        desiredUnits: "days,hours,minutes",
+        config: createProperty("days,hours,minutes", "milliseconds"),
         e: [
           { label: 'Days', value: 9},
           { label: 'Hours', value: 7},
@@ -69,16 +82,14 @@ describe('App.TimeIntervalSpinnerView', function() {
       },
       {
         input: "70000",
-        inputType: 'milliseconds',
-        desiredUnits: "minutes",
+        config: createProperty("minutes", "milliseconds"),
         e: [
           { label: 'Minutes', value: 1}
         ]
       },
       {
         input: "140",
-        inputType: 'minutes',
-        desiredUnits: "hours,minutes",
+        config: createProperty("hours,minutes", "minutes"),
         e: [
           { label: 'Hours', value: 2},
           { label: 'Minutes', value: 20}
@@ -86,8 +97,7 @@ describe('App.TimeIntervalSpinnerView', function() {
       },
       {
         input: "2",
-        inputType: 'hours',
-        desiredUnits: "hours",
+        config: createProperty("hours", "hours"),
         e: [
           { label: 'Hours', value: 2}
         ]
@@ -95,8 +105,9 @@ describe('App.TimeIntervalSpinnerView', function() {
     ];
 
     tests.forEach(function(test) {
-      it('should convert {0} {1} to {2}'.format(test.input, test.inputType, JSON.stringify(test.e)), function() {
-        var result = this.view.convertToWidgetUnits(test.input, test.inputType, test.desiredUnits).map(function(item) {
+      it('should convert {0} {1} to {2}'.format(test.input, test.config.get('stackConfigProperty.valueAttributes.unit'), JSON.stringify(test.e)), function() {
+        this.view.set('config', test.config);
+        var result = this.view.generateWidgetValue(test.input, test.inputType, test.desiredUnits).map(function(item) {
           // remove unneccessary keys
           return App.permit(item, ['label', 'value']);
         });
@@ -104,43 +115,4 @@ describe('App.TimeIntervalSpinnerView', function() {
       });
     });
   });
-
-  describe('#convertToPropertyUnit', function(){
-    beforeEach(function() {
-      this.view = App.TimeIntervalSpinnerView.create({});
-    });
-    var tests = [
-      {
-        widgetUnits: [
-          { unit: 'minutes', value: 10 },
-          { unit: 'seconds', value: 2 }
-        ],
-        inputUnit: "seconds",
-        e: 602
-      },
-      {
-        widgetUnits: [
-          { unit: 'minutes', value: 13 },
-          { unit: 'seconds', value: 10 }
-        ],
-        inputUnit: "milliseconds",
-        e: 790000
-      },
-      {
-        widgetUnits: [
-          { unit: 'days', value: 1 },
-          { unit: 'hours', value: 2 }
-        ],
-        inputUnit: "milliseconds",
-        e: 93600000
-      }
-    ];
-
-    tests.forEach(function(test) {
-      it('should convert {0} to {1} {2}'.format(JSON.stringify(test.widgetUnits), test.e, test.inputUnit), function() {
-        expect(this.view.convertToPropertyUnit(test.widgetUnits, test.inputUnit)).to.equal(test.e);
-      });
-    });
-  });
-
 });