Repository: ambari Updated Branches: refs/heads/trunk e74bfa4a4 -> 9bef76ba0
AMBARI-15979 Provide UI validation for widget_name and description fields in Create/Edit Widget pop-up. (Keta Patel via atkach) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/9bef76ba Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/9bef76ba Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/9bef76ba Branch: refs/heads/trunk Commit: 9bef76ba08fe190ad40d128cf317dce0df36f7f4 Parents: e74bfa4 Author: Andrii Tkach <[email protected]> Authored: Thu Apr 28 11:53:07 2016 +0300 Committer: Andrii Tkach <[email protected]> Committed: Thu Apr 28 11:53:07 2016 +0300 ---------------------------------------------------------------------- .../service/widgets/create/step3_controller.js | 58 +++++++++++- ambari-web/app/messages.js | 2 + .../main/service/widgets/create/step3.hbs | 6 +- ambari-web/app/utils/validator.js | 20 +++++ .../widgets/create/step3_controller_test.js | 92 +++++++++++++++++++- 5 files changed, 169 insertions(+), 9 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/9bef76ba/ambari-web/app/controllers/main/service/widgets/create/step3_controller.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/controllers/main/service/widgets/create/step3_controller.js b/ambari-web/app/controllers/main/service/widgets/create/step3_controller.js index dd7a93f..eebe1f5 100644 --- a/ambari-web/app/controllers/main/service/widgets/create/step3_controller.js +++ b/ambari-web/app/controllers/main/service/widgets/create/step3_controller.js @@ -18,6 +18,8 @@ var App = require('app'); +var validator = require('utils/validator'); + App.WidgetWizardStep3Controller = Em.Controller.extend({ name: "widgetWizardStep3Controller", @@ -29,11 +31,26 @@ App.WidgetWizardStep3Controller = Em.Controller.extend({ widgetName: '', /** + * @type {boolean} + */ + isNameInvalid: false, + + /** * @type {string} */ widgetAuthor: '', /** + * @type {string} + */ + widgetNameErrorMessage: '', + + /** + * @type {string} + */ + descriptionErrorMessage: '', + + /** * @type {boolean} */ isSharedChecked: false, @@ -54,6 +71,11 @@ App.WidgetWizardStep3Controller = Em.Controller.extend({ widgetDescription: '', /** + * @type {boolean} + */ + isDescriptionInvalid: false, + + /** * actual values of properties in API format * @type {object} */ @@ -82,14 +104,42 @@ App.WidgetWizardStep3Controller = Em.Controller.extend({ isSubmitDisabled: Em.computed.or('widgetNameEmpty', 'isNameInvalid', 'isDescriptionInvalid'), /** - * @type {boolean} + * validates the name on 3rd step */ - isNameInvalid: Em.computed.gte('widgetName.length', 129), + validateName: function(){ + var errorMessage=''; + var widgetName = this.get('widgetName'); + this.set("isNameInvalid",false); + if(widgetName && widgetName.length > 128){ + errorMessage = Em.I18n.t("widget.create.wizard.step3.name.invalid.msg"); + this.set("isNameInvalid",true); + } + + if(widgetName && !validator.isValidWidgetName(widgetName)){ + errorMessage = Em.I18n.t("widget.create.wizard.step3.name.invalidCharacter.msg"); + this.set("isNameInvalid",true); + } + this.set('widgetNameErrorMessage',errorMessage); + }.observes('widgetName'), /** - * @type {boolean} + * validates the description on 3rd step */ - isDescriptionInvalid: Em.computed.gte('widgetDescription.length', 2049), + validateDescription: function(){ + var errorMessage=''; + var widgetDescription = this.get('widgetDescription'); + this.set("isDescriptionInvalid",false); + if(widgetDescription && widgetDescription.length > 2048){ + errorMessage = Em.I18n.t("widget.create.wizard.step3.description.invalid.msg"); + this.set("isDescriptionInvalid",true); + } + + if(widgetDescription && !validator.isValidWidgetDescription(widgetDescription)){ + errorMessage = Em.I18n.t("widget.create.wizard.step3.description.invalidCharacter.msg"); + this.set("isDescriptionInvalid",true); + } + this.set('descriptionErrorMessage',errorMessage); + }.observes('widgetDescription'), /** * restore widget data set on 2nd step http://git-wip-us.apache.org/repos/asf/ambari/blob/9bef76ba/ambari-web/app/messages.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/messages.js b/ambari-web/app/messages.js index 8c8b9e5..eea0e2a 100644 --- a/ambari-web/app/messages.js +++ b/ambari-web/app/messages.js @@ -2844,7 +2844,9 @@ Em.I18n.translations = { 'widget.create.wizard.step3.sharing.msg': 'Share this widget in the widget library', 'widget.create.wizard.step3.header': 'Name and Description', 'widget.create.wizard.step3.name.invalid.msg': 'Widget name is too long. Please enter a widget name less than 129 characters.', + 'widget.create.wizard.step3.name.invalidCharacter.msg': 'Invalid widget name. Only alphanumerics, underscores, hyphens, percentage and spaces are allowed.', 'widget.create.wizard.step3.description.invalid.msg': 'Description is too long. Please enter a description less than 2049 characters.', + 'widget.create.wizard.step3.description.invalidCharacter.msg': 'Invalid input. Only alphanumerics, underscores, hyphens, percentage and spaces are allowed.', 'widget.edit.wizard.header': 'Edit Widget', http://git-wip-us.apache.org/repos/asf/ambari/blob/9bef76ba/ambari-web/app/templates/main/service/widgets/create/step3.hbs ---------------------------------------------------------------------- diff --git a/ambari-web/app/templates/main/service/widgets/create/step3.hbs b/ambari-web/app/templates/main/service/widgets/create/step3.hbs index 9f431af..81177bc 100644 --- a/ambari-web/app/templates/main/service/widgets/create/step3.hbs +++ b/ambari-web/app/templates/main/service/widgets/create/step3.hbs @@ -25,7 +25,7 @@ <div {{bindAttr class=":span10 isNameInvalid:error"}}> {{view Ember.TextField valueBinding="widgetName"}} {{#if isNameInvalid}} - <div>{{t widget.create.wizard.step3.name.invalid.msg}}</div> + <div>{{widgetNameErrorMessage}}</div> {{/if}} </div> </div> @@ -50,7 +50,7 @@ <div {{bindAttr class=":span10 isDescriptionInvalid:error"}}> {{view Ember.TextArea valueBinding="widgetDescription" class="description-text-area"}} {{#if isDescriptionInvalid}} - <div>{{t widget.create.wizard.step3.description.invalid.msg}}</div> + <div>{{descriptionErrorMessage}}</div> {{/if}} </div> </div> @@ -62,4 +62,4 @@ <button id="add-widget-step3-save" class="btn btn-success pull-right" {{bindAttr disabled="isSubmitDisabled"}} {{action "complete" target="controller"}}>{{t common.save}}</button> <button id="add-widget-step3-cancel" class="btn pull-right" {{action "cancel" target="controller"}}>{{t common.cancel}}</button> </div> -</div> \ No newline at end of file +</div> http://git-wip-us.apache.org/repos/asf/ambari/blob/9bef76ba/ambari-web/app/utils/validator.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/utils/validator.js b/ambari-web/app/utils/validator.js index 490fec5..896228b 100644 --- a/ambari-web/app/utils/validator.js +++ b/ambari-web/app/utils/validator.js @@ -292,5 +292,25 @@ module.exports = { var remotePattern = /^(?:(?:https?|ftp):\/{2})(?:\S+(?::\S*)?@)?(?:(?:(?:[\w\-.]))*)(?::[0-9]+)?(?:\/\S*)?$/, localPattern = /^file:\/{2,3}([a-zA-Z][:|]\/){0,1}[\w~!*'();@&=\/\\\-+$,?%#.\[\]]+$/; return remotePattern.test(value) || localPattern.test(value); + }, + + /** + * Validate widget name + * @param {string} value + * @returns {boolean} + */ + isValidWidgetName: function(value) { + var widgetNameRegex = /^[\s0-9a-z_\-%]+$/i; + return widgetNameRegex.test(value); + }, + + /** + * Validate widget description + * @param {string} value + * @returns {boolean} + */ + isValidWidgetDescription: function(value) { + var widgetDescriptionRegex = /^[\s0-9a-z_\-%]+$/i; + return widgetDescriptionRegex.test(value); } }; http://git-wip-us.apache.org/repos/asf/ambari/blob/9bef76ba/ambari-web/test/controllers/main/service/widgets/create/step3_controller_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/controllers/main/service/widgets/create/step3_controller_test.js b/ambari-web/test/controllers/main/service/widgets/create/step3_controller_test.js index 6f92142..d54d04a 100644 --- a/ambari-web/test/controllers/main/service/widgets/create/step3_controller_test.js +++ b/ambari-web/test/controllers/main/service/widgets/create/step3_controller_test.js @@ -30,12 +30,100 @@ describe('App.WidgetWizardStep3Controller', function () { App.TestAliases.testAsComputedIfThenElse(controller, 'widgetScope', 'isSharedChecked', 'Cluster', 'User'); - App.TestAliases.testAsComputedGte(controller, 'isNameInvalid', 'widgetName.length', 129); + //App.TestAliases.testAsComputedGte(controller, 'isNameInvalid', 'widgetName.length', 129); - App.TestAliases.testAsComputedGte(controller, 'isDescriptionInvalid', 'widgetDescription.length', 2049); + //App.TestAliases.testAsComputedGte(controller, 'isDescriptionInvalid', 'widgetDescription.length', 2049); App.TestAliases.testAsComputedOr(controller, 'isSubmitDisabled', ['widgetNameEmpty', 'isNameInvalid', 'isDescriptionInvalid']); + describe("#validateName", function(){ + var testCases = [ + { + widgetName: 'abc 123 _ - %', + result: { + errorMessage: '', + isNameInvalid: false + } + }, + { + widgetName: '$#@!', + result: { + errorMessage: Em.I18n.t('widget.create.wizard.step3.name.invalidCharacter.msg'), + isNameInvalid: true + } + }, + { + widgetName: '123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789', + result: { + errorMessage: Em.I18n.t('widget.create.wizard.step3.name.invalid.msg'), + isNameInvalid: true + }, + }, + { + widgetName: '', + result: { + errorMessage: '', + isNameInvalid: false + } + } + ]; + + testCases.forEach(function(test){ + controller.setProperties({ + widgetName: test.widgetName + }); + + //Since validateName() observes the property "widgetName", Ember framework will call it + + expect(controller.get('widgetNameErrorMessage')).to.equal(test.result.errorMessage); + expect(controller.get('isNameInvalid')).to.equal(test.result.isNameInvalid); + }); + }); + + describe("#validateDescription", function(){ + var testCases = [ + { + widgetDescription: 'abc 123 _ - %', + result: { + errorMessage: '', + isDescriptionInvalid: false + } + }, + { + widgetDescription: '$#@!', + result: { + errorMessage: Em.I18n.t('widget.create.wizard.step3.description.invalidCharacter.msg'), + isDescriptionInvalid: true + } + }, + { + widgetDescription: '123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456 78901234567890123456789012345678901234567890123456789012345678901234567890123456789', + result: { + errorMessage: Em.I18n.t('widget.create.wizard.step3.description.invalid.msg'), + isDescriptionInvalid: true + } + }, + { + widgetDescription: '', + result: { + errorMessage: '', + isDescriptionInvalid: false + } + } + ]; + + testCases.forEach(function(test){ + controller.setProperties({ + widgetDescription: test.widgetDescription + }); + + //Since validateDescription() observes the property "widgetDescription", Ember framework will call it + + expect(controller.get('descriptionErrorMessage')).to.equal(test.result.errorMessage); + expect(controller.get('isDescriptionInvalid')).to.equal(test.result.isDescriptionInvalid); + }); + }); + describe("#initPreviewData()", function () { beforeEach(function () { sinon.stub(controller, 'addObserver');
