Repository: ambari Updated Branches: refs/heads/branch-2.5 98baf6c04 -> 8423a3e55
AMBARI-19821 Recommendations for non-editable properties should be listed as 'Required Changes'. (ababiichuk) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/8423a3e5 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/8423a3e5 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/8423a3e5 Branch: refs/heads/branch-2.5 Commit: 8423a3e550f1b4d6ec5a49e0b894e645f74b76a5 Parents: 98baf6c Author: ababiichuk <[email protected]> Authored: Wed Feb 1 18:35:18 2017 +0200 Committer: ababiichuk <[email protected]> Committed: Wed Feb 1 19:40:23 2017 +0200 ---------------------------------------------------------------------- ambari-web/app/messages.js | 7 +- .../configs/config_recommendation_parser.js | 6 +- .../common/configs/config_recommendations.js | 11 +- ...onfig_with_override_recommendation_parser.js | 4 +- .../mixins/common/configs/enhanced_configs.js | 6 +- .../modal_popups/dependent_configs_list.hbs | 49 +- .../modal_popups/dependent_configs_table.hbs | 70 +++ .../dependent_configs_list_popup.js | 78 ++- .../configs/config_recommendations_test.js | 600 ++++++++++--------- .../dependent_configs_list_popup_test.js | 4 +- 10 files changed, 464 insertions(+), 371 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/8423a3e5/ambari-web/app/messages.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js index 9a41a63..33a8289 100644 --- a/ambari-web/app/messages.js +++ b/ambari-web/app/messages.js @@ -453,11 +453,16 @@ Em.I18n.translations = { 'popup.invalid.KDC.admin.password': 'Admin password', 'popup.dependent.configs.header': 'Dependent Configurations', - 'popup.dependent.configs.title': 'Based on your configuration changes, Ambari is recommending the following dependent configuration changes. <br/> Ambari will update all checked configuration changes to the <b>Recommended Value</b>. Uncheck any configuration to retain the <b>Current Value</b>.', + 'popup.dependent.configs.title.recommendation': 'Based on your configuration changes, Ambari is recommending the following dependent configuration changes.', + 'popup.dependent.configs.title.values': 'Ambari will update all checked configuration changes to the <b>Recommended Value</b>. Uncheck any configuration to retain the <b>Current Value</b>.', + 'popup.dependent.configs.title.required': 'The following configuration changes are required and will be applied automatically.', + 'popup.dependent.configs.table.recommended': 'Recommended Changes', + 'popup.dependent.configs.table.required': 'Required Changes', 'popup.dependent.configs.table.saveProperty': 'Save property', 'popup.dependent.configs.table.initValue': 'Initial value', 'popup.dependent.configs.table.currentValue': 'Current Value', 'popup.dependent.configs.table.recommendedValue': 'Recommended Value', + 'popup.dependent.configs.table.newValue': 'New Value', 'popup.dependent.configs.table.not.defined': 'Not Defined', http://git-wip-us.apache.org/repos/asf/ambari/blob/8423a3e5/ambari-web/app/mixins/common/configs/config_recommendation_parser.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/mixins/common/configs/config_recommendation_parser.js b/ambari-web/app/mixins/common/configs/config_recommendation_parser.js index b014ede..b8845a0 100644 --- a/ambari-web/app/mixins/common/configs/config_recommendation_parser.js +++ b/ambari-web/app/mixins/common/configs/config_recommendation_parser.js @@ -173,7 +173,7 @@ App.ConfigRecommendationParser = Em.Mixin.create(App.ConfigRecommendations, { if (!Em.isNone(recommendedValue) && !Em.get(config, 'hiddenBySection')) { Em.set(config, 'isVisible', true); } - this.applyRecommendation(Em.get(config, 'name'), Em.get(config, 'filename'), Em.get(config, 'group.name'), recommendedValue, this._getInitialValue(config), parentProperties); + this.applyRecommendation(Em.get(config, 'name'), Em.get(config, 'filename'), Em.get(config, 'group.name'), recommendedValue, this._getInitialValue(config), parentProperties, Em.get(config, 'isEditable')); } if (this.updateInitialOnRecommendations(Em.get(config, 'serviceName'))) { Em.set(config, 'initialValue', recommendedValue); @@ -202,7 +202,7 @@ App.ConfigRecommendationParser = Em.Mixin.create(App.ConfigRecommendations, { addedPropertyObject = App.ServiceConfigProperty.create(newConfig); this.applyRecommendation(name, fileName, "Default", - recommendedValue, null, parentProperties); + recommendedValue, null, parentProperties, true); return addedPropertyObject; }, @@ -245,7 +245,7 @@ App.ConfigRecommendationParser = Em.Mixin.create(App.ConfigRecommendations, { configsCollection.removeObject(config); this.applyRecommendation(Em.get(config, 'name'), Em.get(config, 'filename'), Em.get(config, 'group.name'), - null, this._getInitialValue(config), parentProperties); + null, this._getInitialValue(config), parentProperties, Em.get(config, 'isEditable')); }, /** http://git-wip-us.apache.org/repos/asf/ambari/blob/8423a3e5/ambari-web/app/mixins/common/configs/config_recommendations.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/mixins/common/configs/config_recommendations.js b/ambari-web/app/mixins/common/configs/config_recommendations.js index 7427a54..bccf3b7 100644 --- a/ambari-web/app/mixins/common/configs/config_recommendations.js +++ b/ambari-web/app/mixins/common/configs/config_recommendations.js @@ -54,16 +54,17 @@ App.ConfigRecommendations = Em.Mixin.create({ * @param {string} recommendedValue * @param {string} initialValue * @param {Object[]} parentProperties + * @param {boolean} isEditable * @returns {recommendation} */ - applyRecommendation: function (name, fileName, configGroupName, recommendedValue, initialValue, parentProperties) { + applyRecommendation: function (name, fileName, configGroupName, recommendedValue, initialValue, parentProperties, isEditable) { try { var parentPropertyIds = this.formatParentProperties(parentProperties); var recommendation = this.getRecommendation(name, fileName, configGroupName); if (recommendation) { return this.updateRecommendation(recommendation, recommendedValue, parentPropertyIds); } - return this.addRecommendation(name, fileName, configGroupName, recommendedValue, initialValue, parentPropertyIds); + return this.addRecommendation(name, fileName, configGroupName, recommendedValue, initialValue, parentPropertyIds, isEditable); } catch(e) { console.error(e.message); } @@ -90,9 +91,10 @@ App.ConfigRecommendations = Em.Mixin.create({ * @param {string} recommendedValue * @param {string} initialValue * @param {string[]} parentPropertyIds + * @param {boolean} isEditable * @returns {recommendation} */ - addRecommendation: function (name, fileName, configGroupName, recommendedValue, initialValue, parentPropertyIds) { + addRecommendation: function (name, fileName, configGroupName, recommendedValue, initialValue, parentPropertyIds, isEditable) { Em.assert('name and fileName should be defined', name && fileName); var site = App.config.getConfigTagFromFileName(fileName); var service = App.config.get('serviceByConfigTypeMap')[site]; @@ -113,7 +115,8 @@ App.ConfigRecommendations = Em.Mixin.create({ allowChangeGroup: false,//TODO groupName!= "Default" && (service.get('serviceName') != this.get('selectedService.serviceName')) //TODO&& (App.ServiceConfigGroup.find().filterProperty('serviceName', service.get('serviceName')).length > 1), //TODO serviceDisplayName: service.get('displayName'), - recommendedValue: recommendedValue + recommendedValue: recommendedValue, + isEditable: isEditable !== false }; this.get('recommendations').pushObject(recommendation); return recommendation; http://git-wip-us.apache.org/repos/asf/ambari/blob/8423a3e5/ambari-web/app/mixins/common/configs/config_with_override_recommendation_parser.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/mixins/common/configs/config_with_override_recommendation_parser.js b/ambari-web/app/mixins/common/configs/config_with_override_recommendation_parser.js index bb39451..4312170 100644 --- a/ambari-web/app/mixins/common/configs/config_with_override_recommendation_parser.js +++ b/ambari-web/app/mixins/common/configs/config_with_override_recommendation_parser.js @@ -94,7 +94,7 @@ App.ConfigWithOverrideRecommendationParser = Em.Mixin.create(App.ConfigRecommend var override = App.config.createOverride(config, coreObject, configGroup); this.applyRecommendation(Em.get(config, 'name'), Em.get(config, 'filename'), configGroup.get('name'), - recommendedValue, this._getInitialValue(override), parentProperties); + recommendedValue, this._getInitialValue(override), parentProperties, Em.get(config, 'isEditable')); }, /** @@ -114,4 +114,4 @@ App.ConfigWithOverrideRecommendationParser = Em.Mixin.create(App.ConfigRecommend } Em.set(stackProperty.valueAttributes[configGroup.get('name')], attr, value); } -}); \ No newline at end of file +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/8423a3e5/ambari-web/app/mixins/common/configs/enhanced_configs.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/mixins/common/configs/enhanced_configs.js b/ambari-web/app/mixins/common/configs/enhanced_configs.js index 882f69d..f7cc4cf 100644 --- a/ambari-web/app/mixins/common/configs/enhanced_configs.js +++ b/ambari-web/app/mixins/common/configs/enhanced_configs.js @@ -385,9 +385,11 @@ App.EnhancedConfigsMixin = Em.Mixin.create(App.ConfigWithOverrideRecommendationP */ showChangedDependentConfigs: function(event, callback, secondary) { var self = this; - var recommendations = event ? this.get('changedProperties') : this.get('recommendations'); + var recommendations = event ? this.get('changedProperties') : this.get('recommendations'), + recommendedChanges = recommendations.filterProperty('isEditable'), + requiredChanges = recommendations.filterProperty('isEditable', false); if (recommendations.length > 0) { - App.showDependentConfigsPopup(recommendations, function() { + App.showDependentConfigsPopup(recommendedChanges, requiredChanges, function() { self.onSaveRecommendedPopup(recommendations); if (callback) callback(); }, secondary); http://git-wip-us.apache.org/repos/asf/ambari/blob/8423a3e5/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs ---------------------------------------------------------------------- diff --git a/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs b/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs index 6a2d93b..3a03c07 100644 --- a/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs +++ b/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs @@ -16,48 +16,11 @@ * limitations under the License. }} -<div class="alert alert-warning"> - {{t popup.dependent.configs.title}} -</div> <span id="config-dependencies" class="limited-height-2"> - <table class="table table-striped"> - <thead> - <tr> - <th class="check-box-col">{{view view.toggleAll}}</th> - <th>{{t common.property}}</th> - <th>{{t common.service}}</th> - <th>{{t common.configGroup}}</th> - <th>{{t common.fileName}}</th> - <th class="row-fliud"> - <div class="span6"> - {{t popup.dependent.configs.table.currentValue}} - </div> - <div class="span6"> - {{t popup.dependent.configs.table.recommendedValue}} - </div> - </th> - </tr> - </thead> - <tbody> - {{#each recommendation in view.parentView.recommendations}} - <tr> - <td class="check-box-col">{{view Em.Checkbox checkedBinding="recommendation.saveRecommended"}}</td> - <td class="config-dependency-name">{{recommendation.propertyName}}</td> - <td class="config-dependency-service">{{recommendation.serviceDisplayName}}</td> - <td class="config-dependency-group"> - <span {{bindAttr class="recommendation.allowChangeGroup::not-active-link"}} ><a href="javascript:void(null);" class="black" - {{action showSelectGroupPopup recommendation.serviceName target="App.router.mainServiceInfoConfigsController"}}> - {{recommendation.configGroup}} - </a></span> - </td> - <td class="config-dependency-filename">{{recommendation.propertyFileName}}</td> - <td> - <div> - {{view App.ConfigDiffView configBinding="recommendation"}} - </div> - </td> - </tr> - {{/each}} - </tbody> - </table> + {{#if view.recommendations.length}} + {{view App.DependentConfigsTableView recommendationsBinding="view.recommendations"}} + {{/if}} + {{#if view.requiredChanges.length}} + {{view App.DependentConfigsTableView recommendationsBinding="view.requiredChanges" isEditable=false}} + {{/if}} </span> http://git-wip-us.apache.org/repos/asf/ambari/blob/8423a3e5/ambari-web/app/templates/common/modal_popups/dependent_configs_table.hbs ---------------------------------------------------------------------- diff --git a/ambari-web/app/templates/common/modal_popups/dependent_configs_table.hbs b/ambari-web/app/templates/common/modal_popups/dependent_configs_table.hbs new file mode 100644 index 0000000..b6fff84 --- /dev/null +++ b/ambari-web/app/templates/common/modal_popups/dependent_configs_table.hbs @@ -0,0 +1,70 @@ +{{! +* 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. +}} + +<h4>{{view.title}}</h4> +<div class="alert alert-warning">{{{view.message}}}</div> +<table class="table table-striped"> + <thead> + <tr> + {{#if view.isEditable}} + <th class="check-box-col">{{view view.parentView.toggleAll}}</th> + {{/if}} + <th>{{t common.property}}</th> + <th>{{t common.service}}</th> + <th>{{t common.configGroup}}</th> + <th>{{t common.fileName}}</th> + <th> + <div class="span6"> + {{t popup.dependent.configs.table.currentValue}} + </div> + <div class="span6"> + {{#if view.isEditable}} + {{t popup.dependent.configs.table.recommendedValue}} + {{else}} + {{t popup.dependent.configs.table.newValue}} + {{/if}} + </div> + </th> + </tr> + </thead> + <tbody> + {{#each recommendation in view.recommendations}} + <tr> + {{#if view.isEditable}} + <td class="check-box-col">{{view Em.Checkbox checkedBinding="recommendation.saveRecommended"}}</td> + {{/if}} + <td class="config-dependency-name">{{recommendation.propertyName}}</td> + <td class="config-dependency-service">{{recommendation.serviceDisplayName}}</td> + <td class="config-dependency-group"> + <span {{bindAttr class="recommendation.allowChangeGroup::not-active-link"}} > + <a href="javascript:void(null);" class="black" + {{action showSelectGroupPopup recommendation.serviceName target="App.router.mainServiceInfoConfigsController"}}> + {{recommendation.configGroup}} + </a> + </span> + </td> + <td class="config-dependency-filename">{{recommendation.propertyFileName}}</td> + <td> + <div> + {{view App.ConfigDiffView configBinding="recommendation"}} + </div> + </td> + </tr> + {{/each}} + </tbody> +</table> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/8423a3e5/ambari-web/app/views/common/modal_popups/dependent_configs_list_popup.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/views/common/modal_popups/dependent_configs_list_popup.js b/ambari-web/app/views/common/modal_popups/dependent_configs_list_popup.js index bcd8b86..0b6a09d 100644 --- a/ambari-web/app/views/common/modal_popups/dependent_configs_list_popup.js +++ b/ambari-web/app/views/common/modal_popups/dependent_configs_list_popup.js @@ -18,50 +18,76 @@ var App = require('app'); +App.DependentConfigsTableView = Em.View.extend({ + templateName: require('templates/common/modal_popups/dependent_configs_table'), + recommendations: [], + isEditable: true, + title: Em.computed.ifThenElse('isEditable', Em.I18n.t('popup.dependent.configs.table.recommended'), Em.I18n.t('popup.dependent.configs.table.required')), + message: function () { + var message = ''; + if (this.get('isEditable')) { + if (this.get('parentView.isAfterRecommendation')) { + message += Em.I18n.t('popup.dependent.configs.title.recommendation') + '<br>'; + } + message += Em.I18n.t('popup.dependent.configs.title.values'); + } else { + message += Em.I18n.t('popup.dependent.configs.title.required'); + } + return message; + }.property('isEditable') +}); + +App.DependentConfigsListView = Em.View.extend({ + templateName: require('templates/common/modal_popups/dependent_configs_list'), + isAfterRecommendation: true, + recommendations: [], + requiredChanges: [], + toggleAll: Em.Checkbox.extend({ + didInsertElement: function () { + this.updateCheckbox(); + }, + click: function () { + Em.run.next(this, 'updateSaveRecommended'); + }, + updateCheckboxObserver: function () { + Em.run.once(this, 'updateCheckbox'); + }.observes('[email protected]'), + + updateCheckbox: function() { + this.set('checked', !(this.get('parentView.recommendations') || []).someProperty('saveRecommended', false)); + }, + updateSaveRecommended: function() { + this.get('parentView.recommendations').setEach('saveRecommended', this.get('checked')); + } + }) +}); + /** * Show confirmation popup - * @param {[Object]} recommendations + * @param {[Object]} recommendedChanges + * @param {[Object]} requiredChanges * @param {function} [primary=null] * @param {function} [secondary=null] * we use this parameter to defer saving configs before we make some decisions. * @return {App.ModalPopup} */ -App.showDependentConfigsPopup = function (recommendations, primary, secondary) { +App.showDependentConfigsPopup = function (recommendedChanges, requiredChanges, primary, secondary) { return App.ModalPopup.show({ encodeBody: false, header: Em.I18n.t('popup.dependent.configs.header'), classNames: ['sixty-percent-width-modal','modal-full-width'], - recommendations: recommendations, secondaryClass: 'cancel-button', - bodyClass: Em.View.extend({ - templateName: require('templates/common/modal_popups/dependent_configs_list'), - - toggleAll: Em.Checkbox.extend({ - didInsertElement: function () { - this.updateCheckbox(); - }, - click: function () { - Em.run.next(this, 'updateSaveRecommended'); - }, - updateCheckboxObserver: function () { - Em.run.once(this, 'updateCheckbox'); - }.observes('[email protected]'), - - updateCheckbox: function() { - this.set('checked', !(this.get('parentView.parentView.recommendations') || []).someProperty('saveRecommended', false)); - }, - updateSaveRecommended: function() { - this.get('parentView.parentView.recommendations').setEach('saveRecommended', this.get('checked')); - } - }) + bodyClass: App.DependentConfigsListView.extend({ + recommendations: recommendedChanges, + requiredChanges: requiredChanges }), saveChanges: function() { - this.get('recommendations').forEach(function (c) { + recommendedChanges.forEach(function (c) { Em.set(c, 'saveRecommendedDefault', Em.get(c, 'saveRecommended')); }) }, discardChanges: function() { - this.get('recommendations').forEach(function(c) { + recommendedChanges.forEach(function(c) { Em.set(c, 'saveRecommended', Em.get(c, 'saveRecommendedDefault')); }); }, http://git-wip-us.apache.org/repos/asf/ambari/blob/8423a3e5/ambari-web/test/mixins/common/configs/config_recommendations_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/mixins/common/configs/config_recommendations_test.js b/ambari-web/test/mixins/common/configs/config_recommendations_test.js index baa8ed6..a72f6d3 100644 --- a/ambari-web/test/mixins/common/configs/config_recommendations_test.js +++ b/ambari-web/test/mixins/common/configs/config_recommendations_test.js @@ -19,29 +19,29 @@ var App = require('app'); describe('App.ConfigRecommendations', function() { - var mixinObject = Em.Controller.extend(App.ConfigRecommendations, {}); - var instanceObject = mixinObject.create({}); + var mixinObject = Em.Controller.extend(App.ConfigRecommendations, {}); + var instanceObject = mixinObject.create({}); - beforeEach(function() { - instanceObject.set('recommendations', []); - }); + beforeEach(function() { + instanceObject.set('recommendations', []); + }); - describe('#applyRecommendation', function() { + describe('#applyRecommendation', function() { beforeEach(function() { sinon.stub(instanceObject, 'formatParentProperties', function(parentProperties) { return parentProperties} ); sinon.stub(App.config, 'get').withArgs('serviceByConfigTypeMap').returns({ 'pFile': Em.Object.create({serviceName: 'sName', displayName: 'sDisplayName'}) }); - sinon.stub(Handlebars, 'SafeString'); + sinon.stub(Handlebars, 'SafeString'); }); afterEach(function() { instanceObject.formatParentProperties.restore(); App.config.get.restore(); - Handlebars.SafeString.restore(); + Handlebars.SafeString.restore(); }); it('adds new recommendation', function() { - var res = instanceObject.applyRecommendation('pName', 'pFile', 'pGroup', 'pRecommended', 'pInitial', ['p_id']); + var res = instanceObject.applyRecommendation('pName', 'pFile', 'pGroup', 'pRecommended', 'pInitial', ['p_id'], true); expect(res).to.eql({ saveRecommended: true, saveRecommendedDefault: true, @@ -56,26 +56,27 @@ describe('App.ConfigRecommendations', function() { allowChangeGroup: false, serviceDisplayName: 'sDisplayName', recommendedValue: 'pRecommended', + isEditable: true }); expect(instanceObject.getRecommendation('pName', 'pFile', 'pGroup')).to.eql(res); }); it('updates recommendation', function() { - instanceObject.set('recommendations', [{ - saveRecommended: true, - saveRecommendedDefault: true, - propertyFileName: 'pFile', - propertyName: 'pName', - isDeleted: false, - notDefined: false, - configGroup: 'pGroup', - initialValue: 'pInitial', - parentConfigs: ['p_id'], - serviceName: 'sName', - allowChangeGroup: false, - serviceDisplayName: 'sDisplayName', - recommendedValue: 'pRecommended' - }]); + instanceObject.set('recommendations', [{ + saveRecommended: true, + saveRecommendedDefault: true, + propertyFileName: 'pFile', + propertyName: 'pName', + isDeleted: false, + notDefined: false, + configGroup: 'pGroup', + initialValue: 'pInitial', + parentConfigs: ['p_id'], + serviceName: 'sName', + allowChangeGroup: false, + serviceDisplayName: 'sDisplayName', + recommendedValue: 'pRecommended' + }]); expect(instanceObject.applyRecommendation('pName', 'pFile', 'pGroup', 'pRecommended1', 'pInitial', ['p_id1'])).to.eql({ saveRecommended: true, saveRecommendedDefault: true, @@ -92,7 +93,7 @@ describe('App.ConfigRecommendations', function() { recommendedValue: 'pRecommended1' }); }); - }); + }); describe('#formatParentProperties', function() { beforeEach(function() { @@ -111,152 +112,175 @@ describe('App.ConfigRecommendations', function() { }); }); - describe('#addRecommendation', function() { - var cases = [ - { - title: 'add recommendation with full info', - name: 'pName', file: 'pFile.xml', group: 'pGroup', recommended: 'pRecommended', initial: 'pInitial', parent: ['p_id'], - service: Em.Object.create({serviceName: 'sName', displayName: 'sDisplayName'}), - result: { - saveRecommended: true, - saveRecommendedDefault: true, - propertyFileName: 'pFile', - propertyName: 'pName', - isDeleted: false, - notDefined: false, - configGroup: 'pGroup', - initialValue: 'pInitial', - parentConfigs: ['p_id'], - serviceName: 'sName', - allowChangeGroup: false, - serviceDisplayName: 'sDisplayName', - recommendedValue: 'pRecommended' - } - }, - { - title: 'add recommendation with min info', - name: 'pName', file: 'pFile.xml', - service: Em.Object.create({serviceName: 'sName', displayName: 'sDisplayName'}), - result: { - saveRecommended: true, - saveRecommendedDefault: true, - propertyFileName: 'pFile', - propertyName: 'pName', - isDeleted: true, - notDefined: true, - configGroup: 'Default', - initialValue: undefined, - parentConfigs: [], - serviceName: 'sName', - allowChangeGroup: false, - serviceDisplayName: 'sDisplayName', - recommendedValue: undefined - } - } - ]; - cases.forEach(function(c) { - describe('successful add recommendation', function() { - var recommendation; - beforeEach(function() { - instanceObject.set('recommendations', []); - sinon.stub(App.config, 'get').withArgs('serviceByConfigTypeMap').returns({ - 'pFile': c.service - }); - sinon.stub(Handlebars, 'SafeString'); - recommendation = instanceObject.addRecommendation(c.name, c.file, c.group, c.recommended, c.initial, c.parent); - }); - - afterEach(function() { - App.config.get.restore(); - Handlebars.SafeString.restore(); - }); - - it(c.title, function() { - expect(recommendation).to.eql(c.result); - }); - - it(c.title + ' check recommendations collection', function() { - expect(instanceObject.get('recommendations.0')).to.eql(c.result); - }); - }) - }); - - it('throw exception when name, fileName', function() { - expect(instanceObject.addRecommendation.bind()).to.throw(Error, 'name and fileName should be defined'); - expect(instanceObject.addRecommendation.bind(null, 'fname')).to.throw(Error, 'name and fileName should be defined'); - expect(instanceObject.addRecommendation.bind('name', null)).to.throw(Error, 'name and fileName should be defined'); - }); - }); - - describe('#removeRecommendationObject', function () { - var recommendations = [ - { - propertyName: 'p1', - propertyFileName: 'f1' - }, - { - propertyName: 'p2', - propertyFileName: 'f2' - } - ]; - - beforeEach(function () { - instanceObject.set('recommendations', recommendations); - }); - - it('remove recommendation', function () { - instanceObject.removeRecommendationObject(recommendations[1]); - - expect(instanceObject.get('recommendations.length')).to.equal(1); - expect(instanceObject.get('recommendations.0')).to.eql({ - propertyName: 'p1', - propertyFileName: 'f1' - }); - }); - - it('remove recommendation that is not exist (don\'t do anything)', function () { - instanceObject.removeRecommendationObject({propertyName: 'any', 'propertyFileName': 'aby'}); - expect(instanceObject.get('recommendations')).to.eql(recommendations); - }); - - it('throw error if recommendation is undefined ', function () { - expect(instanceObject.removeRecommendationObject.bind()).to.throw(Error, 'recommendation should be defined object'); - expect(instanceObject.removeRecommendationObject.bind(null)).to.throw(Error, 'recommendation should be defined object'); - }); - - it('throw error if recommendation is not an object ', function () { - expect(instanceObject.removeRecommendationObject.bind('recommendation')).to.throw(Error, 'recommendation should be defined object'); - expect(instanceObject.removeRecommendationObject.bind(['recommendation'])).to.throw(Error, 'recommendation should be defined object'); - }); - }); - - describe('#updateRecommendation', function () { - it('update recommended value and parent properties', function () { - expect(instanceObject.updateRecommendation({'recommendedValue': 'v2', parentConfigs: ['id1']}, 'v1', ['id2'])) - .to.eql({'recommendedValue': 'v1', parentConfigs: ['id2', 'id1']}); - }); - - it('update recommended value and add parent properties', function () { - expect(instanceObject.updateRecommendation({}, 'v1', ['id1'])).to.eql({'recommendedValue': 'v1', parentConfigs: ['id1']}); - }); - - it('update recommended value', function () { - expect(instanceObject.updateRecommendation({}, 'v1')).to.eql({'recommendedValue': 'v1'}); - expect(instanceObject.updateRecommendation({'recommendedValue': 'v1'}, 'v2')).to.eql({'recommendedValue': 'v2'}); - }); - - it('throw error if recommendation is undefined ', function () { - expect(instanceObject.updateRecommendation.bind()).to.throw(Error, 'recommendation should be defined object'); - expect(instanceObject.updateRecommendation.bind(null)).to.throw(Error, 'recommendation should be defined object'); - }); - - it('throw error if recommendation is not an object ', function () { - expect(instanceObject.updateRecommendation.bind('recommendation')).to.throw(Error, 'recommendation should be defined object'); - expect(instanceObject.updateRecommendation.bind(['recommendation'])).to.throw(Error, 'recommendation should be defined object'); - }); - }); - - describe('#saveRecommendation', function() { + describe('#addRecommendation', function() { + var cases = [ + { + title: 'add recommendation for editable property with full info', + name: 'pName', file: 'pFile.xml', group: 'pGroup', recommended: 'pRecommended', initial: 'pInitial', parent: ['p_id'], isEditable: true, + service: Em.Object.create({serviceName: 'sName', displayName: 'sDisplayName'}), + result: { + saveRecommended: true, + saveRecommendedDefault: true, + propertyFileName: 'pFile', + propertyName: 'pName', + isDeleted: false, + notDefined: false, + configGroup: 'pGroup', + initialValue: 'pInitial', + parentConfigs: ['p_id'], + serviceName: 'sName', + allowChangeGroup: false, + serviceDisplayName: 'sDisplayName', + recommendedValue: 'pRecommended', + isEditable: true + } + }, + { + title: 'add recommendation for read-only property with full info', + name: 'pName', file: 'pFile.xml', group: 'pGroup', recommended: 'pRecommended', initial: 'pInitial', parent: ['p_id'], isEditable: false, + service: Em.Object.create({serviceName: 'sName', displayName: 'sDisplayName'}), + result: { + saveRecommended: true, + saveRecommendedDefault: true, + propertyFileName: 'pFile', + propertyName: 'pName', + isDeleted: false, + notDefined: false, + configGroup: 'pGroup', + initialValue: 'pInitial', + parentConfigs: ['p_id'], + serviceName: 'sName', + allowChangeGroup: false, + serviceDisplayName: 'sDisplayName', + recommendedValue: 'pRecommended', + isEditable: false + } + }, + { + title: 'add recommendation with min info', + name: 'pName', file: 'pFile.xml', + service: Em.Object.create({serviceName: 'sName', displayName: 'sDisplayName'}), + result: { + saveRecommended: true, + saveRecommendedDefault: true, + propertyFileName: 'pFile', + propertyName: 'pName', + isDeleted: true, + notDefined: true, + configGroup: 'Default', + initialValue: undefined, + parentConfigs: [], + serviceName: 'sName', + allowChangeGroup: false, + serviceDisplayName: 'sDisplayName', + recommendedValue: undefined, + isEditable: true + } + } + ]; + cases.forEach(function(c) { + describe('successful add recommendation', function() { + var recommendation; + beforeEach(function() { + instanceObject.set('recommendations', []); + sinon.stub(App.config, 'get').withArgs('serviceByConfigTypeMap').returns({ + 'pFile': c.service + }); + sinon.stub(Handlebars, 'SafeString'); + recommendation = instanceObject.addRecommendation(c.name, c.file, c.group, c.recommended, c.initial, c.parent, c.isEditable); + }); + + afterEach(function() { + App.config.get.restore(); + Handlebars.SafeString.restore(); + }); + + it(c.title, function() { + expect(recommendation).to.eql(c.result); + }); + + it(c.title + ' check recommendations collection', function() { + expect(instanceObject.get('recommendations.0')).to.eql(c.result); + }); + }) + }); + + it('throw exception when name, fileName', function() { + expect(instanceObject.addRecommendation.bind()).to.throw(Error, 'name and fileName should be defined'); + expect(instanceObject.addRecommendation.bind(null, 'fname')).to.throw(Error, 'name and fileName should be defined'); + expect(instanceObject.addRecommendation.bind('name', null)).to.throw(Error, 'name and fileName should be defined'); + }); + }); + + describe('#removeRecommendationObject', function () { + var recommendations = [ + { + propertyName: 'p1', + propertyFileName: 'f1' + }, + { + propertyName: 'p2', + propertyFileName: 'f2' + } + ]; + + beforeEach(function () { + instanceObject.set('recommendations', recommendations); + }); + + it('remove recommendation', function () { + instanceObject.removeRecommendationObject(recommendations[1]); + + expect(instanceObject.get('recommendations.length')).to.equal(1); + expect(instanceObject.get('recommendations.0')).to.eql({ + propertyName: 'p1', + propertyFileName: 'f1' + }); + }); + + it('remove recommendation that is not exist (don\'t do anything)', function () { + instanceObject.removeRecommendationObject({propertyName: 'any', 'propertyFileName': 'aby'}); + expect(instanceObject.get('recommendations')).to.eql(recommendations); + }); + + it('throw error if recommendation is undefined ', function () { + expect(instanceObject.removeRecommendationObject.bind()).to.throw(Error, 'recommendation should be defined object'); + expect(instanceObject.removeRecommendationObject.bind(null)).to.throw(Error, 'recommendation should be defined object'); + }); + + it('throw error if recommendation is not an object ', function () { + expect(instanceObject.removeRecommendationObject.bind('recommendation')).to.throw(Error, 'recommendation should be defined object'); + expect(instanceObject.removeRecommendationObject.bind(['recommendation'])).to.throw(Error, 'recommendation should be defined object'); + }); + }); + + describe('#updateRecommendation', function () { + it('update recommended value and parent properties', function () { + expect(instanceObject.updateRecommendation({'recommendedValue': 'v2', parentConfigs: ['id1']}, 'v1', ['id2'])) + .to.eql({'recommendedValue': 'v1', parentConfigs: ['id2', 'id1']}); + }); + + it('update recommended value and add parent properties', function () { + expect(instanceObject.updateRecommendation({}, 'v1', ['id1'])).to.eql({'recommendedValue': 'v1', parentConfigs: ['id1']}); + }); + + it('update recommended value', function () { + expect(instanceObject.updateRecommendation({}, 'v1')).to.eql({'recommendedValue': 'v1'}); + expect(instanceObject.updateRecommendation({'recommendedValue': 'v1'}, 'v2')).to.eql({'recommendedValue': 'v2'}); + }); + + it('throw error if recommendation is undefined ', function () { + expect(instanceObject.updateRecommendation.bind()).to.throw(Error, 'recommendation should be defined object'); + expect(instanceObject.updateRecommendation.bind(null)).to.throw(Error, 'recommendation should be defined object'); + }); + + it('throw error if recommendation is not an object ', function () { + expect(instanceObject.updateRecommendation.bind('recommendation')).to.throw(Error, 'recommendation should be defined object'); + expect(instanceObject.updateRecommendation.bind(['recommendation'])).to.throw(Error, 'recommendation should be defined object'); + }); + }); + + describe('#saveRecommendation', function() { it('skip update since values are same', function() { expect(instanceObject.saveRecommendation({saveRecommended: false, saveRecommendedDefault: false}, false)).to.be.false; @@ -282,122 +306,122 @@ describe('App.ConfigRecommendations', function() { expect(instanceObject.updateRecommendation.bind('recommendation')).to.throw(Error, 'recommendation should be defined object'); expect(instanceObject.updateRecommendation.bind(['recommendation'])).to.throw(Error, 'recommendation should be defined object'); }); - }); - - describe('#getRecommendation', function () { - var recommendations = [ - { - propertyName: 'p1', - propertyFileName: 'f1', - configGroup: 'Default' - }, - { - propertyName: 'p2', - propertyFileName: 'f2', - configGroup: 'group1' - }, - { - propertyName: 'p1', - propertyFileName: 'f1', - configGroup: 'group1' - } - ]; - - beforeEach(function () { - instanceObject.set('recommendations', recommendations); - }); - - it('get recommendation for default group', function () { - expect(instanceObject.getRecommendation('p1', 'f1')).to.eql(recommendations[0]); - }); - - it('get recommendation for default group (2)', function () { - expect(instanceObject.getRecommendation('p1', 'f1', 'group1')).to.eql(recommendations[2]); - }); - - it('get recommendation for wrong group', function () { - expect(instanceObject.getRecommendation('p2', 'f2', 'group2')).to.equal(null); - }); - - it('get undefined recommendation', function () { - expect(instanceObject.getRecommendation('some', 'amy')).to.equal(null); - }); - - it('get throw error if undefined name or fileName passed', function () { - expect(instanceObject.getRecommendation.bind()).to.throw(Error, 'name and fileName should be defined'); - expect(instanceObject.getRecommendation.bind('name')).to.throw(Error, 'name and fileName should be defined'); - expect(instanceObject.getRecommendation.bind(null, 'fileName')).to.throw(Error, 'name and fileName should be defined'); - }); - }); - - describe('#cleanUpRecommendations', function() { - var cases = [ - { - title: 'remove recommendations with same init and recommended values', - recommendations: [{ - initialValue: 'v1', recommendedValue: 'v1' - }, { - initialValue: 'v1', recommendedValue: 'v2' - }], - cleanUpRecommendations: [{ - initialValue: 'v1', recommendedValue: 'v2' - }] - }, - { - title: 'remove recommendations with null init and recommended values', - recommendations: [{ - initialValue: null, recommendedValue: null - }, { - recommendedValue: null - }, { - initialValue: null - },{ - initialValue: null, recommendedValue: 'v1' - }, { - initialValue: 'v1', recommendedValue: null - }], - cleanUpRecommendations: [{ - initialValue: null, recommendedValue: 'v1' - }, { - initialValue: 'v1', recommendedValue: null - } - ] - } - ]; - - cases.forEach(function(c) { - describe(c.title, function() { - beforeEach(function() { - instanceObject.set('recommendations', c.recommendations); - instanceObject.cleanUpRecommendations() - }); - it('do clean up', function() { - expect(instanceObject.get('recommendations')).to.eql(c.cleanUpRecommendations); - }); - }); - }); - }); - - describe('#clearRecommendationsByServiceName', function () { - beforeEach(function () { - instanceObject.set('recommendations', [{serviceName: 's1'}, {serviceName: 's2'}, {serviceName: 's3'}]); - }); - - it('remove with specific service names ', function () { - instanceObject.clearRecommendationsByServiceName(['s2','s3']); - expect(instanceObject.get('recommendations')).to.eql([{serviceName: 's1'}]); - }); - }); - - describe('#clearAllRecommendations', function () { - beforeEach(function () { - instanceObject.set('recommendations', [{anyObject: 'o1'}, {anyObject: 'o2'}]); - }); - - it('remove all recommendations', function () { - instanceObject.clearAllRecommendations(); - expect(instanceObject.get('recommendations.length')).to.equal(0); - }); - }); + }); + + describe('#getRecommendation', function () { + var recommendations = [ + { + propertyName: 'p1', + propertyFileName: 'f1', + configGroup: 'Default' + }, + { + propertyName: 'p2', + propertyFileName: 'f2', + configGroup: 'group1' + }, + { + propertyName: 'p1', + propertyFileName: 'f1', + configGroup: 'group1' + } + ]; + + beforeEach(function () { + instanceObject.set('recommendations', recommendations); + }); + + it('get recommendation for default group', function () { + expect(instanceObject.getRecommendation('p1', 'f1')).to.eql(recommendations[0]); + }); + + it('get recommendation for default group (2)', function () { + expect(instanceObject.getRecommendation('p1', 'f1', 'group1')).to.eql(recommendations[2]); + }); + + it('get recommendation for wrong group', function () { + expect(instanceObject.getRecommendation('p2', 'f2', 'group2')).to.equal(null); + }); + + it('get undefined recommendation', function () { + expect(instanceObject.getRecommendation('some', 'amy')).to.equal(null); + }); + + it('get throw error if undefined name or fileName passed', function () { + expect(instanceObject.getRecommendation.bind()).to.throw(Error, 'name and fileName should be defined'); + expect(instanceObject.getRecommendation.bind('name')).to.throw(Error, 'name and fileName should be defined'); + expect(instanceObject.getRecommendation.bind(null, 'fileName')).to.throw(Error, 'name and fileName should be defined'); + }); + }); + + describe('#cleanUpRecommendations', function() { + var cases = [ + { + title: 'remove recommendations with same init and recommended values', + recommendations: [{ + initialValue: 'v1', recommendedValue: 'v1' + }, { + initialValue: 'v1', recommendedValue: 'v2' + }], + cleanUpRecommendations: [{ + initialValue: 'v1', recommendedValue: 'v2' + }] + }, + { + title: 'remove recommendations with null init and recommended values', + recommendations: [{ + initialValue: null, recommendedValue: null + }, { + recommendedValue: null + }, { + initialValue: null + },{ + initialValue: null, recommendedValue: 'v1' + }, { + initialValue: 'v1', recommendedValue: null + }], + cleanUpRecommendations: [{ + initialValue: null, recommendedValue: 'v1' + }, { + initialValue: 'v1', recommendedValue: null + } + ] + } + ]; + + cases.forEach(function(c) { + describe(c.title, function() { + beforeEach(function() { + instanceObject.set('recommendations', c.recommendations); + instanceObject.cleanUpRecommendations() + }); + it('do clean up', function() { + expect(instanceObject.get('recommendations')).to.eql(c.cleanUpRecommendations); + }); + }); + }); + }); + + describe('#clearRecommendationsByServiceName', function () { + beforeEach(function () { + instanceObject.set('recommendations', [{serviceName: 's1'}, {serviceName: 's2'}, {serviceName: 's3'}]); + }); + + it('remove with specific service names ', function () { + instanceObject.clearRecommendationsByServiceName(['s2','s3']); + expect(instanceObject.get('recommendations')).to.eql([{serviceName: 's1'}]); + }); + }); + + describe('#clearAllRecommendations', function () { + beforeEach(function () { + instanceObject.set('recommendations', [{anyObject: 'o1'}, {anyObject: 'o2'}]); + }); + + it('remove all recommendations', function () { + instanceObject.clearAllRecommendations(); + expect(instanceObject.get('recommendations.length')).to.equal(0); + }); + }); }); http://git-wip-us.apache.org/repos/asf/ambari/blob/8423a3e5/ambari-web/test/views/common/modal_popups/dependent_configs_list_popup_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/views/common/modal_popups/dependent_configs_list_popup_test.js b/ambari-web/test/views/common/modal_popups/dependent_configs_list_popup_test.js index 9dc4bf6..44c38b3 100644 --- a/ambari-web/test/views/common/modal_popups/dependent_configs_list_popup_test.js +++ b/ambari-web/test/views/common/modal_popups/dependent_configs_list_popup_test.js @@ -40,7 +40,7 @@ describe('App.showDependentConfigsPopup', function () { beforeEach(function () { this.ff = function () {}; sinon.spy(this, 'ff'); - view = App.showDependentConfigsPopup([], Em.K, this.ff); + view = App.showDependentConfigsPopup([], [], Em.K, this.ff); }); afterEach(function () { @@ -54,4 +54,4 @@ describe('App.showDependentConfigsPopup', function () { }); -}); \ No newline at end of file +});
