Browse Source

AMBARI-12071 Disabled webpage elements allow user to perform attached actions in IE 9-10. (ababiichuk)

aBabiichuk 10 năm trước cách đây
mục cha
commit
d8724c0770

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

@@ -282,6 +282,7 @@ var files = ['test/init_model_test',
   'test/models/host_test',
   'test/models/host_test',
   'test/models/host_component_test',
   'test/models/host_component_test',
   'test/models/hosts_test',
   'test/models/hosts_test',
+  'test/models/operating_system_test',
   'test/models/repository_test',
   'test/models/repository_test',
   'test/models/stack_service_component_test',
   'test/models/stack_service_component_test',
   'test/models/service_test',
   'test/models/service_test',

+ 6 - 0
ambari-web/app/controllers/main/host/details.js

@@ -216,6 +216,9 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @method deleteComponent
    * @method deleteComponent
    */
    */
   deleteComponent: function (event) {
   deleteComponent: function (event) {
+    if ($(event.target).closest('li').hasClass('disabled')) {
+      return;
+    }
     var self = this;
     var self = this;
     var component = event.context;
     var component = event.context;
     var componentName = component.get('componentName');
     var componentName = component.get('componentName');
@@ -2085,6 +2088,9 @@ App.MainHostDetailsController = Em.Controller.extend({
    * @method moveComponent
    * @method moveComponent
    */
    */
   moveComponent: function (event) {
   moveComponent: function (event) {
+    if ($(event.target).closest('li').hasClass('disabled')) {
+      return;
+    }
     return App.showConfirmationPopup(function () {
     return App.showConfirmationPopup(function () {
       var component = event.context;
       var component = event.context;
       var reassignMasterController = App.router.get('reassignMasterController');
       var reassignMasterController = App.router.get('reassignMasterController');

+ 3 - 0
ambari-web/app/mixins/wizard/wizardProgressPageController.js

@@ -656,6 +656,9 @@ App.wizardProgressPageControllerMixin = Em.Mixin.create({
   },
   },
 
 
   showHostProgressPopup: function (event) {
   showHostProgressPopup: function (event) {
+    if (!['IN_PROGRESS', 'FAILED', 'COMPLETED'].contains(Em.get(event.context, 'status')) || !this.get('content.requestIds.length')) {
+      return;
+    }
     var popupTitle = event.contexts[0].title;
     var popupTitle = event.contexts[0].title;
     var requestIds = event.contexts[0].requestIds;
     var requestIds = event.contexts[0].requestIds;
     var stageId = event.contexts[0].stageId;
     var stageId = event.contexts[0].stageId;

+ 2 - 1
ambari-web/app/models/operating_system.js

@@ -26,7 +26,8 @@ App.OperatingSystem = DS.Model.extend({
   stackVersion: DS.attr('string'),
   stackVersion: DS.attr('string'),
   repositories: DS.hasMany('App.Repository'),
   repositories: DS.hasMany('App.Repository'),
   stack: DS.belongsTo('App.Stack'),
   stack: DS.belongsTo('App.Stack'),
-  isSelected: DS.attr('boolean', {defaultValue: true})
+  isSelected: DS.attr('boolean', {defaultValue: true}),
+  isDeselected: Em.computed.not('isSelected')
 });
 });
 
 
 
 

+ 0 - 7
ambari-web/app/styles/stack_versions.less

@@ -111,13 +111,6 @@
         -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
         -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
         box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
         box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
       }
       }
-      .disabled-textfield input {
-        color: #808080;
-        disabled: disabled;
-        pointer-events: none;
-        cursor: default;
-        background: #E4E4E4;
-      }
       .disabled-label {
       .disabled-label {
         color: #808080;
         color: #808080;
       }
       }

+ 1 - 1
ambari-web/app/templates/main/host/details/host_component.hbs

@@ -112,7 +112,7 @@
           </li>
           </li>
         {{/unless}}
         {{/unless}}
         {{#if view.isInit}}
         {{#if view.isInit}}
-          <li {{bindAttr class="view."}}>
+          <li>
             <a href="javascript:void(null)" data-toggle="modal" {{action "installComponent" view.content target="controller"}}>
             <a href="javascript:void(null)" data-toggle="modal" {{action "installComponent" view.content target="controller"}}>
               {{t common.reinstall}}
               {{t common.reinstall}}
             </a>
             </a>

+ 2 - 2
ambari-web/app/templates/wizard/step1.hbs

@@ -69,8 +69,8 @@
                             {{view view.popoverView repositoryBinding="repository"}}
                             {{view view.popoverView repositoryBinding="repository"}}
                           {{/if}}
                           {{/if}}
                         </div>
                         </div>
-                        <div {{bindAttr class=":url-td operatingSystem.osType repository.repoId operatingSystem.isSelected::disabled-textfield repository.invalidFormatError:textfield-error repository.invalidError:textfield-error"}}>
-                          {{view Ember.TextField valueBinding="repository.baseUrl"}}
+                        <div {{bindAttr class=":url-td operatingSystem.osType repository.repoId repository.invalidFormatError:textfield-error repository.invalidError:textfield-error"}}>
+                          {{view Ember.TextField valueBinding="repository.baseUrl" disabledBinding="operatingSystem.isDeselected"}}
                         </div>
                         </div>
                         <div class="clear-td">
                         <div class="clear-td">
                           {{#if repository.clearAll}}
                           {{#if repository.clearAll}}

+ 6 - 0
ambari-web/app/views/main/host.js

@@ -548,6 +548,9 @@ App.MainHostView = App.TableView.extend(App.TableServerViewMixin, {
     },
     },
 
 
     displayComponents: function () {
     displayComponents: function () {
+      if (this.get('hasNoComponents')) {
+        return;
+      }
       var header = Em.I18n.t('common.components'),
       var header = Em.I18n.t('common.components'),
         hostName = this.get('content.hostName'),
         hostName = this.get('content.hostName'),
         items = this.get('content.hostComponents').getEach('displayName');
         items = this.get('content.hostComponents').getEach('displayName');
@@ -555,6 +558,9 @@ App.MainHostView = App.TableView.extend(App.TableServerViewMixin, {
     },
     },
 
 
     displayVersions: function () {
     displayVersions: function () {
+      if (this.get('hasSingleVersion')) {
+        return;
+      }
       var header = Em.I18n.t('common.versions'),
       var header = Em.I18n.t('common.versions'),
         hostName = this.get('content.hostName'),
         hostName = this.get('content.hostName'),
         items = this.get('content.stackVersions').filterProperty('isVisible').map(function (stackVersion) {
         items = this.get('content.stackVersions').filterProperty('isVisible').map(function (stackVersion) {

+ 91 - 21
ambari-web/test/controllers/main/host/details_test.js

@@ -314,20 +314,56 @@ describe('App.MainHostDetailsController', function () {
   });
   });
 
 
   describe('#deleteComponent()', function () {
   describe('#deleteComponent()', function () {
-    it('confirm popup should be displayed', function () {
-      var event = {
-        context: Em.Object.create({})
-      };
-      sinon.spy(App.ModalPopup, "show");
+
+    var jQueryMock,
+      cases = [
+        {
+          isDisabled: false,
+          showCallCount: 1,
+          title: 'confirm popup should be displayed'
+        },
+        {
+          isDisabled: true,
+          showCallCount: 0,
+          title: 'confirm popup shouldn\'t be displayed'
+        }
+      ];
+
+    beforeEach(function () {
+      jQueryMock = sinon.stub(window, '$');
+      sinon.spy(App.ModalPopup, 'show');
       sinon.stub(controller, '_doDeleteHostComponent', Em.K);
       sinon.stub(controller, '_doDeleteHostComponent', Em.K);
+    });
 
 
-      var popup = controller.deleteComponent(event);
-      expect(App.ModalPopup.show.calledOnce).to.be.true;
-      popup.onPrimary();
-      expect(controller._doDeleteHostComponent.calledWith(Em.Object.create({}))).to.be.true;
+    afterEach(function () {
+      window.$.restore();
       App.ModalPopup.show.restore();
       App.ModalPopup.show.restore();
       controller._doDeleteHostComponent.restore();
       controller._doDeleteHostComponent.restore();
     });
     });
+
+    cases.forEach(function (item) {
+      it(item.title, function () {
+        jQueryMock.returns({
+          closest: function () {
+            return {
+              hasClass: function () {
+                return item.isDisabled;
+              }
+            }
+          }
+        });
+        var event = {
+            context: Em.Object.create({})
+          },
+          popup = controller.deleteComponent(event);
+        expect(App.ModalPopup.show.callCount).to.equal(item.showCallCount);
+        if (item.showCallCount) {
+          popup.onPrimary();
+          expect(controller._doDeleteHostComponent.calledWith(Em.Object.create({}))).to.be.true;
+        }
+      });
+    });
+
   });
   });
 
 
   describe('#mimicWorkStatusChange()', function () {
   describe('#mimicWorkStatusChange()', function () {
@@ -1794,36 +1830,70 @@ describe('App.MainHostDetailsController', function () {
   });
   });
 
 
   describe('#moveComponent()', function () {
   describe('#moveComponent()', function () {
-    it('popup should be displayed', function () {
-      var mock = {
+
+    var jQueryMock,
+      mock = {
         saveComponentToReassign: Em.K,
         saveComponentToReassign: Em.K,
         getSecurityStatus: Em.K,
         getSecurityStatus: Em.K,
         setCurrentStep: Em.K
         setCurrentStep: Em.K
-      };
+      },
+      cases = [
+        {
+          isDisabled: false,
+          showConfirmationPopupCallCount: 1,
+          title: 'popup should be displayed'
+        },
+        {
+          isDisabled: true,
+          showConfirmationPopupCallCount: 0,
+          title: 'popup shouldn\'t be displayed'
+        }
+      ];
+
+    beforeEach(function () {
+      jQueryMock = sinon.stub(window, '$');
       sinon.spy(App, "showConfirmationPopup");
       sinon.spy(App, "showConfirmationPopup");
       sinon.stub(App.router, 'get').withArgs('reassignMasterController').returns(mock);
       sinon.stub(App.router, 'get').withArgs('reassignMasterController').returns(mock);
       sinon.stub(App.router, 'transitionTo', Em.K);
       sinon.stub(App.router, 'transitionTo', Em.K);
       sinon.spy(mock, "saveComponentToReassign");
       sinon.spy(mock, "saveComponentToReassign");
       sinon.spy(mock, "getSecurityStatus");
       sinon.spy(mock, "getSecurityStatus");
       sinon.spy(mock, "setCurrentStep");
       sinon.spy(mock, "setCurrentStep");
+    });
 
 
-      var popup = controller.moveComponent({context: {}});
-      expect(App.showConfirmationPopup.calledOnce).to.be.true;
-      popup.onPrimary();
-      expect(App.router.get.calledWith('reassignMasterController')).to.be.true;
-      expect(mock.saveComponentToReassign.calledWith({})).to.be.true;
-      expect(mock.getSecurityStatus.calledOnce).to.be.true;
-      expect(mock.setCurrentStep.calledWith('1')).to.be.true;
-      expect(App.router.transitionTo.calledWith('reassign')).to.be.true;
-
+    afterEach(function () {
+      window.$.restore();
       App.showConfirmationPopup.restore();
       App.showConfirmationPopup.restore();
       App.router.get.restore();
       App.router.get.restore();
       App.router.transitionTo.restore();
       App.router.transitionTo.restore();
       mock.saveComponentToReassign.restore();
       mock.saveComponentToReassign.restore();
       mock.getSecurityStatus.restore();
       mock.getSecurityStatus.restore();
       mock.setCurrentStep.restore();
       mock.setCurrentStep.restore();
+    });
 
 
+    cases.forEach(function (item) {
+      it(item.title, function () {
+        jQueryMock.returns({
+          closest: function () {
+            return {
+              hasClass: function () {
+                return item.isDisabled;
+              }
+            }
+          }
+        });
+        var popup = controller.moveComponent({context: {}});
+        expect(App.showConfirmationPopup.callCount).to.equal(item.showConfirmationPopupCallCount);
+        if (item.showConfirmationPopupCallCount) {
+          popup.onPrimary();
+          expect(App.router.get.calledWith('reassignMasterController')).to.be.true;
+          expect(mock.saveComponentToReassign.calledWith({})).to.be.true;
+          expect(mock.getSecurityStatus.calledOnce).to.be.true;
+          expect(mock.setCurrentStep.calledWith('1')).to.be.true;
+          expect(App.router.transitionTo.calledWith('reassign')).to.be.true;
+        }
+      });
     });
     });
+
   });
   });
 
 
   describe('#refreshConfigs()', function () {
   describe('#refreshConfigs()', function () {

+ 42 - 0
ambari-web/test/models/operating_system_test.js

@@ -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.
+ */
+
+var App = require('app');
+
+require('models/operating_system');
+
+describe('App.OperatingSystem', function () {
+
+  var os;
+
+  beforeEach(function () {
+    os = App.OperatingSystem.createRecord();
+  });
+
+  describe('#isDeselected', function () {
+
+    it('should be opposite to isSelected', function () {
+      os.set('isSelected', true);
+      expect(os.get('isDeselected')).to.be.false;
+      os.set('isSelected', false);
+      expect(os.get('isDeselected')).to.be.true;
+    });
+
+  });
+
+});

+ 98 - 37
ambari-web/test/views/main/host_test.js

@@ -120,17 +120,8 @@ describe('App.MainHostView', function () {
 
 
     describe('#displayComponents', function () {
     describe('#displayComponents', function () {
 
 
-      beforeEach(function () {
-        sinon.stub(App, 'showHostsTableListPopup', Em.K);
-      });
-
-      afterEach(function () {
-        App.showHostsTableListPopup.restore();
-      });
-
-      it('should display host components in modal popup', function () {
-        hostView.set('content', {
-          hostName: 'h',
+      var cases = [
+        {
           hostComponents: [
           hostComponents: [
             {
             {
               displayName: 'c0'
               displayName: 'c0'
@@ -138,16 +129,16 @@ describe('App.MainHostView', function () {
             {
             {
               displayName: 'c1'
               displayName: 'c1'
             }
             }
-          ]
-        });
-        hostView.displayComponents();
-        expect(App.showHostsTableListPopup.calledOnce).to.be.true;
-        expect(App.showHostsTableListPopup.calledWith(Em.I18n.t('common.components'), 'h', ['c0', 'c1'])).to.be.true;
-      });
-
-    });
-
-    describe('#displayVersions', function () {
+          ],
+          showHostsTableListPopupCallCount: 1,
+          title: 'should display host components in modal popup'
+        },
+        {
+          hostComponents: [],
+          showHostsTableListPopupCallCount: 0,
+          title: 'should not display modal popup'
+        }
+      ];
 
 
       beforeEach(function () {
       beforeEach(function () {
         sinon.stub(App, 'showHostsTableListPopup', Em.K);
         sinon.stub(App, 'showHostsTableListPopup', Em.K);
@@ -157,9 +148,26 @@ describe('App.MainHostView', function () {
         App.showHostsTableListPopup.restore();
         App.showHostsTableListPopup.restore();
       });
       });
 
 
-      it('should display stack versions in modal popup', function () {
-        hostView.set('content', {
-          hostName: 'h',
+      cases.forEach(function (item) {
+        it(item.title, function () {
+          hostView.set('content', {
+            hostName: 'h',
+            hostComponents: item.hostComponents
+          });
+          hostView.displayComponents();
+          expect(App.showHostsTableListPopup.callCount).to.equal(item.showHostsTableListPopupCallCount);
+          if (item.showHostsTableListPopupCallCount) {
+            expect(App.showHostsTableListPopup.calledWith(Em.I18n.t('common.components'), 'h', ['c0', 'c1'])).to.be.true;
+          }
+        });
+      });
+
+    });
+
+    describe('#displayVersions', function () {
+
+      var cases = [
+        {
           stackVersions: [
           stackVersions: [
             Em.Object.create({
             Em.Object.create({
               displayName: 'v0',
               displayName: 'v0',
@@ -176,20 +184,73 @@ describe('App.MainHostView', function () {
               status: 'INSTALL_FAILED',
               status: 'INSTALL_FAILED',
               isVisible: false
               isVisible: false
             })
             })
-          ]
-        });
-        hostView.displayVersions();
-        expect(App.showHostsTableListPopup.calledOnce).to.be.true;
-        expect(App.showHostsTableListPopup.calledWith(Em.I18n.t('common.versions'), 'h', [
-          {
-            name: 'v0',
-            status: 'Current'
-          },
-          {
-            name: 'v1',
-            status: 'Out Of Sync'
+          ],
+          showHostsTableListPopupCallCount: 1,
+          title: 'should display stack versions in modal popup'
+        },
+        {
+          stackVersions: [
+            Em.Object.create({
+              displayName: 'v0',
+              status: 'CURRENT',
+              isVisible: true
+            }),
+            Em.Object.create({
+              displayName: 'v2',
+              status: 'INSTALL_FAILED',
+              isVisible: false
+            })
+          ],
+          showHostsTableListPopupCallCount: 0,
+          title: 'should not display modal popup if there\'s only one visible stack version'
+        },
+        {
+          stackVersions: [
+            Em.Object.create({
+              displayName: 'v2',
+              status: 'INSTALL_FAILED',
+              isVisible: false
+            })
+          ],
+          showHostsTableListPopupCallCount: 0,
+          title: 'should not display modal popup if there are no visible stack versions available'
+        },
+        {
+          stackVersions: [],
+          showHostsTableListPopupCallCount: 0,
+          title: 'should not display modal popup if there are no stack versions available'
+        }
+      ];
+
+      beforeEach(function () {
+        sinon.stub(App, 'showHostsTableListPopup', Em.K);
+      });
+
+      afterEach(function () {
+        App.showHostsTableListPopup.restore();
+      });
+
+      cases.forEach(function (item) {
+        it('should display stack versions in modal popup', function () {
+          hostView.set('content', {
+            hostName: 'h',
+            stackVersions: item.stackVersions
+          });
+          hostView.displayVersions();
+          expect(App.showHostsTableListPopup.callCount).to.equal(item.showHostsTableListPopupCallCount);
+          if (item.showHostsTableListPopupCallCount) {
+            expect(App.showHostsTableListPopup.calledWith(Em.I18n.t('common.versions'), 'h', [
+              {
+                name: 'v0',
+                status: 'Current'
+              },
+              {
+                name: 'v1',
+                status: 'Out Of Sync'
+              }
+            ])).to.be.true;
           }
           }
-        ])).to.be.true;
+        });
       });
       });
 
 
     });
     });