Repository: ambari Updated Branches: refs/heads/trunk 0a6a64477 -> 1b6002a5e
AMBARI-8526. Alerts UI: Create Notification dialog should have ability to add extra configs (onechiporenko) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/1b6002a5 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/1b6002a5 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/1b6002a5 Branch: refs/heads/trunk Commit: 1b6002a5e6f12c142185f8a0a827b944c267290a Parents: 0a6a644 Author: Oleg Nechiporenko <onechipore...@apache.org> Authored: Wed Dec 3 18:15:34 2014 +0200 Committer: Oleg Nechiporenko <onechipore...@apache.org> Committed: Wed Dec 3 18:15:34 2014 +0200 ---------------------------------------------------------------------- .../assets/data/alerts/alertNotifications.json | 45 ++--- .../manage_alert_notifications_controller.js | 163 +++++++++++++++++-- ambari-web/app/messages.js | 4 + ambari-web/app/styles/alerts.less | 5 + ...ustom_config_to_alert_notification_popup.hbs | 42 +++++ .../main/alerts/create_alert_notification.hbs | 13 ++ ambari-web/app/utils/ajax/ajax.js | 4 +- ambari-web/app/utils/validator.js | 8 +- ...anage_alert_notifications_controller_test.js | 128 ++++++++++++++- 9 files changed, 349 insertions(+), 63 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/1b6002a5/ambari-web/app/assets/data/alerts/alertNotifications.json ---------------------------------------------------------------------- diff --git a/ambari-web/app/assets/data/alerts/alertNotifications.json b/ambari-web/app/assets/data/alerts/alertNotifications.json index 8e19655..4e71e1c 100644 --- a/ambari-web/app/assets/data/alerts/alertNotifications.json +++ b/ambari-web/app/assets/data/alerts/alertNotifications.json @@ -1,45 +1,24 @@ { - "href" : "http://c6407.ambari.apache.org:8080/api/v1/alert_targets?fields=*", + "href" : "http://host:8080/api/v1/alert_targets?fields=*", "items" : [ { - "href" : "http://c6407.ambari.apache.org:8080/api/v1/alert_targets/1", + "href" : "http://host:8080/api/v1/alert_targets/1", "AlertTarget" : { - "description" : "The Admins", + "alert_states" : [ + "UNKNOWN", + "CRITICAL", + "OK", + "WARNING" + ], + "description" : "123", "id" : 1, - "name" : "Administrators", + "name" : "123", "notification_type" : "EMAIL", "properties" : { - "mail.smtp.from" : "amb...@repo.ambari.apache.org", - "ambari.dispatch.credential.username" : "ambari", - "mail.smtp.host" : "repo.ambari.apache.org", - "mail.smtp.port" : "25", - "mail.smtp.auth" : "true", - "ambari.dispatch.credential.password" : "password", "ambari.dispatch.recipients" : [ - "amb...@repo.ambari.apache.org" + "1...@123.com" ], - "mail.smtp.tarttls.enable" : "false" - } - } - }, - { - "href" : "http://c6407.ambari.apache.org:8080/api/v1/alert_targets/2", - "AlertTarget" : { - "description" : "another user", - "id" : 2, - "name" : "user1", - "notification_type" : "EMAIL", - "properties" : { - "mail.smtp.from" : "amb...@repo.ambari.apache.org", - "ambari.dispatch.credential.username" : "ambari", - "mail.smtp.host" : "repo.ambari.apache.org", - "mail.smtp.port" : "25", - "mail.smtp.auth" : "true", - "ambari.dispatch.credential.password" : "password", - "ambari.dispatch.recipients" : [ - "amb...@repo.ambari.apache.org" - ], - "mail.smtp.tarttls.enable" : "false" + "dd": "ddd" } } } http://git-wip-us.apache.org/repos/asf/ambari/blob/1b6002a5/ambari-web/app/controllers/main/alerts/manage_alert_notifications_controller.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/main/alerts/manage_alert_notifications_controller.js b/ambari-web/app/controllers/main/alerts/manage_alert_notifications_controller.js index a6d13b1..27f8303 100644 --- a/ambari-web/app/controllers/main/alerts/manage_alert_notifications_controller.js +++ b/ambari-web/app/controllers/main/alerts/manage_alert_notifications_controller.js @@ -17,11 +17,16 @@ */ var App = require('app'); +var validator = require('utils/validator'); App.ManageAlertNotificationsController = Em.Controller.extend({ name: 'manageAlertNotificationsController', + /** + * Are alert notifications loaded + * @type {boolean} + */ isLoaded: false, /** @@ -65,7 +70,8 @@ App.ManageAlertNotificationsController = Em.Controller.extend({ label: Em.I18n.t('common.description'), value: '', defaultValue: '' - } + }, + customProperties: Em.A([]) }), /** @@ -80,10 +86,7 @@ App.ManageAlertNotificationsController = Em.Controller.extend({ * @type {App.AlertNotification[]} */ alertNotifications: function () { - if (this.get('isLoaded')) { - return App.AlertNotification.find().toArray(); - } - return []; + return this.get('isLoaded') ? App.AlertNotification.find().toArray() : []; }.property('isLoaded'), /** @@ -93,6 +96,18 @@ App.ManageAlertNotificationsController = Em.Controller.extend({ selectedAlertNotification: null, /** + * Addable to <code>selectedAlertNotification.properties</code> custom property + * @type {{name: string, value: string}} + */ + newCustomProperty: {name: '', value: ''}, + + /** + * List custom property names that shouldn't be displayed on Edit page + * @type {string[]} + */ + ignoredCustomProperties: ['ambari.dispatch.recipients'], + + /** * Load all Alert Notifications from server * Don't do anything if controller not isLoaded * @returns {$.ajax|null} @@ -127,6 +142,7 @@ App.ManageAlertNotificationsController = Em.Controller.extend({ /** * Add Notification button handler + * @method addAlertNotification */ addAlertNotification: function () { var inputFields = this.get('inputFields'); @@ -138,6 +154,7 @@ App.ManageAlertNotificationsController = Em.Controller.extend({ /** * Edit Notification button handler + * @method editAlertNotification */ editAlertNotification: function () { this.fillEditCreateInputs(); @@ -147,6 +164,7 @@ App.ManageAlertNotificationsController = Em.Controller.extend({ /** * Fill inputs of Create/Edit popup form * @param addCopyToName define whether add 'Copy of ' to name + * @method fillEditCreateInputs */ fillEditCreateInputs: function (addCopyToName) { var inputFields = this.get('inputFields'); @@ -162,12 +180,24 @@ App.ManageAlertNotificationsController = Em.Controller.extend({ ]); inputFields.set('description.value', selectedAlertNotification.get('description')); inputFields.set('method.value', selectedAlertNotification.get('type')); + inputFields.get('customProperties').clear(); + var properties = selectedAlertNotification.get('properties'); + var ignoredCustomProperties = this.get('ignoredCustomProperties'); + Em.keys(properties).forEach(function (k) { + if (ignoredCustomProperties.contains(k)) return; + inputFields.get('customProperties').pushObject({ + name: k, + value: properties[k], + defaultValue: properties[k] + }); + }); }, /** * Show Edit or Create Notification popup - * @param isEdit + * @param {boolean} isEdit true - edit, false - create * @returns {App.ModalPopup} + * @method showCreateEditPopup */ showCreateEditPopup: function (isEdit) { var self = this; @@ -198,31 +228,35 @@ App.ManageAlertNotificationsController = Em.Controller.extend({ /** * Create API-formatted object from data populate by user * @returns {Object} + * @method formatNotificationAPIObject */ formatNotificationAPIObject: function () { var inputFields = this.get('inputFields'); var alertStates = []; var properties = {}; - if (inputFields.severityFilter.value[0]) { + if (inputFields.get('severityFilter.value')[0]) { alertStates.push('OK'); } - if (inputFields.severityFilter.value[1]) { + if (inputFields.get('severityFilter.value')[1]) { alertStates.push('WARNING'); } - if (inputFields.severityFilter.value[2]) { + if (inputFields.get('severityFilter.value')[2]) { alertStates.push('CRITICAL'); } - if (inputFields.severityFilter.value[3]) { + if (inputFields.get('severityFilter.value')[3]) { alertStates.push('UNKNOWN'); } - if (inputFields.method.value === 'EMAIL') { - properties['ambari.dispatch.recipients'] = inputFields.email.value.replace(/\s/g, '').split(','); + if (inputFields.get('method.value') === 'EMAIL') { + properties['ambari.dispatch.recipients'] = inputFields.get('email.value').replace(/\s/g, '').split(','); } + inputFields.get('customProperties').forEach(function (customProperty) { + properties[customProperty.name] = customProperty.value; + }); return { AlertTarget: { - name: inputFields.name.value, - description: inputFields.description.value, - notification_type: inputFields.method.value, + name: inputFields.get('name.value'), + description: inputFields.get('description.value'), + notification_type: inputFields.get('method.value'), alert_states: alertStates, properties: properties } @@ -231,8 +265,9 @@ App.ManageAlertNotificationsController = Em.Controller.extend({ /** * Send request to server to create Alert Notification - * @param apiObject + * @param {object} apiObject (@see formatNotificationAPIObject) * @returns {$.ajax} + * @method createAlertNotification */ createAlertNotification: function (apiObject) { return App.ajax.send({ @@ -247,6 +282,7 @@ App.ManageAlertNotificationsController = Em.Controller.extend({ /** * Success callback for <code>createAlertNotification</code> + * @method createAlertNotificationSuccessCallback */ createAlertNotificationSuccessCallback: function () { this.loadAlertNotifications(); @@ -258,8 +294,9 @@ App.ManageAlertNotificationsController = Em.Controller.extend({ /** * Send request to server to update Alert Notification - * @param apiObject + * @param {object} apiObject (@see formatNotificationAPIObject) * @returns {$.ajax} + * @method updateAlertNotification */ updateAlertNotification: function (apiObject) { return App.ajax.send({ @@ -275,6 +312,7 @@ App.ManageAlertNotificationsController = Em.Controller.extend({ /** * Success callback for <code>updateAlertNotification</code> + * @method updateAlertNotificationSuccessCallback */ updateAlertNotificationSuccessCallback: function () { this.loadAlertNotifications(); @@ -286,6 +324,8 @@ App.ManageAlertNotificationsController = Em.Controller.extend({ /** * Delete Notification button handler + * @return {App.ModalPopup} + * @method deleteAlertNotification */ deleteAlertNotification: function () { var self = this; @@ -303,6 +343,7 @@ App.ManageAlertNotificationsController = Em.Controller.extend({ /** * Success callback for <code>deleteAlertNotification</code> + * @method deleteAlertNotificationSuccessCallback */ deleteAlertNotificationSuccessCallback: function () { this.loadAlertNotifications(); @@ -313,10 +354,98 @@ App.ManageAlertNotificationsController = Em.Controller.extend({ /** * Duplicate Notification button handler + * @method duplicateAlertNotification */ duplicateAlertNotification: function () { this.fillEditCreateInputs(true); this.showCreateEditPopup(); + }, + + /** + * Show popup with form for new custom property + * @method addCustomPropertyHandler + * @return {App.ModalPopup} + */ + addCustomPropertyHandler: function () { + var self = this; + + return App.ModalPopup.show({ + + header: Em.I18n.t('alerts.notifications.addCustomPropertyPopup.header'), + + primary: Em.I18n.t('common.add'), + + bodyClass: Em.View.extend({ + + /** + * If some error with new custom property + * @type {boolean} + */ + isError: false, + + controller: this, + + /** + * Error message for new custom property (invalid name, existed name etc) + * @type {string} + */ + errorMessage: '', + + /** + * Check new custom property for errors with its name + * @method errorHandler + */ + errorsHandler: function () { + var name = this.get('controller.newCustomProperty.name'); + var flag = validator.isValidConfigKey(name); + if (flag) { + if (this.get('controller.inputFields.customProperties').mapProperty('name').contains(name) || + this.get('controller.ignoredCustomProperties').contains(name)) { + this.set('errorMessage', Em.I18n.t('alerts.notifications.addCustomPropertyPopup.error.propertyExists')); + flag = false; + } + } + else { + this.set('errorMessage', Em.I18n.t('alerts.notifications.addCustomPropertyPopup.error.invalidPropertyName')); + } + this.set('isError', !flag); + this.set('parentView.disablePrimary', !flag); + }.observes('controller.newCustomProperty.name'), + + templateName: require('templates/main/alerts/add_custom_config_to_alert_notification_popup') + }), + + onPrimary: function () { + self.addCustomProperty(); + self.set('newCustomProperty', {name: '', value: ''}); // cleanup + this.hide(); + } + + }); + }, + + /** + * Add Custom Property to <code>selectedAlertNotification</code> (push it to <code>properties</code>-field) + * @method addCustomProperty + */ + addCustomProperty: function () { + var newCustomProperty = this.get('newCustomProperty'); + this.get('inputFields.customProperties').pushObject({ + name: newCustomProperty.name, + value: newCustomProperty.value, + defaultValue: newCustomProperty.value + }); + }, + + /** + * "Remove"-button click handler + * Delete customProperty from <code>inputFields.customProperties</code> + * @param {object} e + * @method removeCustomProperty + */ + removeCustomPropertyHandler: function (e) { + var customProperties = this.get('inputFields.customProperties'); + this.set('inputFields.customProperties', customProperties.without(e.context)); } }); http://git-wip-us.apache.org/repos/asf/ambari/blob/1b6002a5/ambari-web/app/messages.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js index e148eb6..40b8d14 100644 --- a/ambari-web/app/messages.js +++ b/ambari-web/app/messages.js @@ -878,6 +878,10 @@ Em.I18n.translations = { 'alerts.definition.details.notification': 'Notification', 'alerts.definition.details.noAlerts': 'No alert instances to show', + 'alerts.notifications.addCustomPropertyPopup.header': 'Add Custom Property', + 'alerts.notifications.addCustomPropertyPopup.error.propertyExists': 'Custom Property with current name already exists', + 'alerts.notifications.addCustomPropertyPopup.error.invalidPropertyName': 'Custom Property name is invalid', + 'wizard.progressPage.notice.completed':'Please proceed to the next step.', 'wizard.progressPage.notice.failed':'You can click on the Retry button to retry failed tasks.', http://git-wip-us.apache.org/repos/asf/ambari/blob/1b6002a5/ambari-web/app/styles/alerts.less ---------------------------------------------------------------------- diff --git a/ambari-web/app/styles/alerts.less b/ambari-web/app/styles/alerts.less index df07714..fdb2cf8 100644 --- a/ambari-web/app/styles/alerts.less +++ b/ambari-web/app/styles/alerts.less @@ -324,6 +324,11 @@ width: inherit; } } + + .icon-minus-sign { + color: #FF4B4B; + } + } /*****start styles for manage alerts popup*****/ http://git-wip-us.apache.org/repos/asf/ambari/blob/1b6002a5/ambari-web/app/templates/main/alerts/add_custom_config_to_alert_notification_popup.hbs ---------------------------------------------------------------------- diff --git a/ambari-web/app/templates/main/alerts/add_custom_config_to_alert_notification_popup.hbs b/ambari-web/app/templates/main/alerts/add_custom_config_to_alert_notification_popup.hbs new file mode 100644 index 0000000..8f86d5e --- /dev/null +++ b/ambari-web/app/templates/main/alerts/add_custom_config_to_alert_notification_popup.hbs @@ -0,0 +1,42 @@ +{{! +* 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. +}} + +<form class="form-horizontal alert-configs"> + <div {{bindAttr class="view.isError:error :control-group"}}> + <label class="control-label">{{t common.name}}: </label> + + <div class="controls"> + {{view Em.TextField valueBinding="controller.newCustomProperty.name"}} + </div> + </div> + + <div class="control-group"> + <label class="control-label">{{t common.value}}: </label> + + <div class="controls"> + {{view Em.TextField valueBinding="controller.newCustomProperty.value"}} + </div> + </div> + + {{#if view.isError}} + <div class="alert alert-danger"> + {{view.errorMessage}} + </div> + {{/if}} + +</form> http://git-wip-us.apache.org/repos/asf/ambari/blob/1b6002a5/ambari-web/app/templates/main/alerts/create_alert_notification.hbs ---------------------------------------------------------------------- diff --git a/ambari-web/app/templates/main/alerts/create_alert_notification.hbs b/ambari-web/app/templates/main/alerts/create_alert_notification.hbs index 37ac7a9..9002809 100644 --- a/ambari-web/app/templates/main/alerts/create_alert_notification.hbs +++ b/ambari-web/app/templates/main/alerts/create_alert_notification.hbs @@ -70,5 +70,18 @@ </div> </div> + {{#each customProperty in controller.inputFields.customProperties}} + <div class="control-group"> + <label class="control-label" for="inputGroups">{{customProperty.name}}</label> + + <div class="controls"> + {{view Em.TextField valueBinding="customProperty.value" class="input-xlarge"}} + <a href="#" {{action "removeCustomPropertyHandler" customProperty target="controller"}} class="btn btn-small"><span class="icon-minus-sign"></span></a> + </div> + </div> + {{/each}} + + <a href="#" {{action addCustomPropertyHandler target="controller"}}>{{t installer.step7.config.addProperty}} ...</a> + </form> </div> http://git-wip-us.apache.org/repos/asf/ambari/blob/1b6002a5/ambari-web/app/utils/ajax/ajax.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/utils/ajax/ajax.js b/ambari-web/app/utils/ajax/ajax.js index 662c9b8..0956f1c 100644 --- a/ambari-web/app/utils/ajax/ajax.js +++ b/ambari-web/app/utils/ajax/ajax.js @@ -363,7 +363,7 @@ var urls = { }, 'alerts.notifications': { 'real': '/alert_targets?fields=*', - 'mock': '' + 'mock': '/data/alerts/alertNotifications.json' }, 'alerts.instances.by_definition': { 'real': '/clusters/{clusterName}/alerts?fields=*&(Alert/definition_id={definitionId}|Alert/state.in(CRITICAL,WARNING))', @@ -2021,7 +2021,7 @@ var urls = { action: 'check_host', parameters: { } }; - $.extend(true, requestInfo, data.requestInfo) + $.extend(true, requestInfo, data.requestInfo); return { type: 'POST', data: JSON.stringify({ http://git-wip-us.apache.org/repos/asf/ambari/blob/1b6002a5/ambari-web/app/utils/validator.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/utils/validator.js b/ambari-web/app/utils/validator.js index d1af967..3e84982 100644 --- a/ambari-web/app/utils/validator.js +++ b/ambari-web/app/utils/validator.js @@ -187,22 +187,22 @@ module.exports = { * Try to prevent invalid regexp. * For example: /api/v1/clusters/c1/hosts?Hosts/host_name.matches(.*localhost.) * - * @params {String} value - string to validate + * @param {String} value - string to validate * @return {Boolean} * @method isValidMatchesRegexp */ isValidMatchesRegexp: function(value) { var checkPair = function(chars) { - chars = chars.map(function(char) { return '\\' + char; }); + chars = chars.map(function(c) { return '\\' + c; }); var charsReg = new RegExp(chars.join('|'), 'g'); if (charsReg.test(value)) { var pairContentReg = new RegExp(chars.join('.*'), 'g'); if (!pairContentReg.test(value)) return false; - var pairCounts = chars.map(function(char) { return value.match(new RegExp(char, 'g')).length; }); + var pairCounts = chars.map(function(c) { return value.match(new RegExp(c, 'g')).length; }); if (pairCounts[0] != pairCounts[1] ) return false; } return true; - } + }; if (/^[\?\|\*\!,]/.test(value)) return false; return /^((\.\*?)?([\w\[\]\?\-_,\|\*\!\{\}]*)?)+(\.\*?)?$/g.test(value) && (checkPair(['[',']'])) && (checkPair(['{','}'])); }, http://git-wip-us.apache.org/repos/asf/ambari/blob/1b6002a5/ambari-web/test/controllers/main/alerts/manage_alert_notifications_controller_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/controllers/main/alerts/manage_alert_notifications_controller_test.js b/ambari-web/test/controllers/main/alerts/manage_alert_notifications_controller_test.js index 15f9ede..2ae6c6e 100644 --- a/ambari-web/test/controllers/main/alerts/manage_alert_notifications_controller_test.js +++ b/ambari-web/test/controllers/main/alerts/manage_alert_notifications_controller_test.js @@ -18,6 +18,8 @@ var App = require('app'); var controller; +var helpers = require('test/helpers'); + describe('App.ManageAlertNotificationsController', function () { beforeEach(function () { @@ -162,7 +164,7 @@ describe('App.ManageAlertNotificationsController', function () { describe('#fillEditCreateInputs()', function () { - it("should map properties from selectedAlertNotification to inputFields", function () { + it("should map properties from selectedAlertNotification to inputFields (ambari.dispatch.recipients ignored)", function () { controller.set('selectedAlertNotification', Em.Object.create({ name: 'test_name', @@ -173,7 +175,8 @@ describe('App.ManageAlertNotificationsController', function () { 'ambari.dispatch.recipients': [ 'te...@test.test', 'te...@test.test' - ] + ], + 'customName': 'customValue' } })); @@ -195,7 +198,11 @@ describe('App.ManageAlertNotificationsController', function () { }, description: { value: '' - } + }, + customProperties: [ + {name: 'customName', value: 'customValue1', defaultValue: 'customValue1'}, + {name: 'customName2', value: 'customValue1', defaultValue: 'customValue1'} + ] })); controller.fillEditCreateInputs(); @@ -218,7 +225,10 @@ describe('App.ManageAlertNotificationsController', function () { }, description: { value: 'test_description' - } + }, + customProperties: [ + {name: 'customName', value: 'customValue', defaultValue: 'customValue'} + ] })); }); @@ -271,7 +281,11 @@ describe('App.ManageAlertNotificationsController', function () { }, description: { value: 'test_description' - } + }, + customProperties: [ + {name: 'n1', value: 'v1'}, + {name: 'n2', value: 'v2'} + ] })); var result = controller.formatNotificationAPIObject(); @@ -288,7 +302,9 @@ describe('App.ManageAlertNotificationsController', function () { 'te...@test.test', 'te...@test.test', 'te...@test.test' - ] + ], + 'n1': 'v1', + 'n2': 'v2' } } }); @@ -387,7 +403,6 @@ describe('App.ManageAlertNotificationsController', function () { }); - describe('#deleteAlertNotificationSuccessCallback()', function () { it("should call loadAlertNotifications, selectedAlertNotification.deleteRecord and set null to selectedAlertNotification", function () { @@ -433,5 +448,104 @@ describe('App.ManageAlertNotificationsController', function () { }); + describe('#addCustomProperty', function () { + + beforeEach(function () { + controller.set('inputFields.customProperties', []); + }); + + it('should add custom Property to customProperties', function () { + + controller.set('newCustomProperty', {name: 'n1', value: 'v1'}); + controller.addCustomProperty(); + helpers.nestedExpect([{name: 'n1', value: 'v1', defaultValue: 'v1'}], controller.get('inputFields.customProperties')); + + }); + + }); + + describe('#removeCustomPropertyHandler', function () { + + var c = {name: 'n2', value: 'v2', defaultValue: 'v2'}; + + beforeEach(function () { + controller.set('inputFields.customProperties', [ + {name: 'n1', value: 'v1', defaultValue: 'v1'}, + c, + {name: 'n3', value: 'v3', defaultValue: 'v3'} + ]); + }); + + it('should remove selected custom property', function () { + + controller.removeCustomPropertyHandler({context: c}); + helpers.nestedExpect( + [ + {name: 'n1', value: 'v1', defaultValue: 'v1'}, + {name: 'n3', value: 'v3', defaultValue: 'v3'} + ], + controller.get('inputFields.customProperties') + ); + + }); + + }); + + describe('#addCustomPropertyHandler', function () { + + it('should clean up newCustomProperty on primary click', function () { + + controller.set('newCustomProperty', {name: 'n1', value: 'v1'}); + controller.addCustomPropertyHandler().onPrimary(); + expect(controller.get('newCustomProperty')).to.eql({name: '', value: ''}); + + }); + + describe('#bodyClass', function () { + + var view; + + beforeEach(function () { + view = controller.addCustomPropertyHandler().get('bodyClass').create({ + parentView: Em.View.create(), + controller: Em.Object.create({ + inputFields: Em.Object.create({ + customProperties: [ + {name: 'n1', value: 'v1', defaultValue: 'v1'} + ] + }), + newCustomProperty: {name: '', value: ''} + }) + }); + }); + + describe('#errorHandler', function () { + + it('should fire invalid name', function () { + + view.set('controller.newCustomProperty.name', '!!'); + view.errorsHandler(); + expect(view.get('isError')).to.be.true; + expect(view.get('parentView.disablePrimary')).to.be.true; + expect(view.get('errorMessage.length') > 0).to.be.true; + + }); + + it('should fire existing property name', function () { + + view.set('controller.newCustomProperty.name', 'n1'); + view.errorsHandler(); + expect(view.get('isError')).to.be.true; + expect(view.get('parentView.disablePrimary')).to.be.true; + expect(view.get('errorMessage.length') > 0).to.be.true; + + }); + + }); + + }); + + }); + });