Browse Source

AMBARI-14574. multiple clicks on "Next" button causes skipping of steps while installing a cluster (alexantonenko)

Alex Antonenko 9 years ago
parent
commit
9e84052999

+ 2 - 0
ambari-web/app/controllers/wizard.js

@@ -59,6 +59,8 @@ App.WizardController = Em.Controller.extend(App.LocalStorage, App.ThemesMappingM
 
   connectOutlet:function(name, context) {
     if (name !== 'loading') this.set('isStepDisabled.isLocked', false);
+    App.get('router').set('transitionInProgress', false);
+    App.get('router').set('nextBtnClickInProgress', false);
     return this._super.apply(this,arguments);
   },
 

+ 5 - 0
ambari-web/app/controllers/wizard/step0_controller.js

@@ -66,9 +66,14 @@ App.WizardStep0Controller = Em.Controller.extend({
 
   /**
    * Onclick handler for <code>next</code> button
+   * Disable 'Next' button while it is already under process. (using Router's property 'nextBtnClickInProgress')
    * @method submit
    */
   submit: function () {
+    if(App.router.nextBtnClickInProgress){
+      return;
+    }
+    App.router.nextBtnClickInProgress = true;
     this.set('hasSubmitted', true);
     if (!this.get('invalidClusterName')) {
       App.clusterStatus.set('clusterName', this.get('content.cluster.name'));

+ 5 - 1
ambari-web/app/controllers/wizard/step3_controller.js

@@ -1648,12 +1648,16 @@ App.WizardStep3Controller = Em.Controller.extend(App.ReloadPopupMixin, {
 
   /**
    * Submit-click handler
+   * Disable 'Next' button while it is already under process. (using Router's property 'nextBtnClickInProgress')
    * @return {App.ModalPopup|null}
    * @method submit
    */
   submit: function () {
     var self = this;
-
+    if(App.router.nextBtnClickInProgress){
+      return;
+    }
+    App.router.nextBtnClickInProgress = true;
     if (this.get('isHostHaveWarnings')) {
       return App.showConfirmationPopup(
         function () {

+ 5 - 0
ambari-web/app/controllers/wizard/step4_controller.js

@@ -133,13 +133,18 @@ App.WizardStep4Controller = Em.ArrayController.extend({
 
   /**
    * Onclick handler for <code>Next</code> button.
+   * Disable 'Next' button while it is already under process. (using Router's property 'nextBtnClickInProgress')
    * @method submit
    */
   submit: function () {
+    if(App.router.nextBtnClickInProgress){
+      return;
+    }
     if (!this.get('isSubmitDisabled')) {
       this.unSelectServices();
       this.setGroupedServices();
       if (this.validate()) {
+        App.router.nextBtnClickInProgress = true;
         this.set('errorStack', []);
         App.router.send('next');
       }

+ 3 - 1
ambari-web/app/controllers/wizard/step7_controller.js

@@ -1648,9 +1648,10 @@ App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, App.E
    * @method submit
    */
   submit: function () {
-    if (this.get('isSubmitDisabled')) {
+    if (this.get('isSubmitDisabled') || App.router.nextBtnClickInProgress) {
       return false;
     }
+    App.router.nextBtnClickInProgress = true;
     var preInstallChecksController = App.router.get('preInstallChecksController');
     if (this.get('supportsPreInstallChecks')) {
       if (preInstallChecksController.get('preInstallChecksWhereRun')) {
@@ -1670,6 +1671,7 @@ App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, App.E
       .fail(function (value) {
         if ("invalid_configs" == value) {
           self.set('submitButtonClicked', false);
+          App.router.nextBtnClickInProgress = false;
         } else {
           // Failed due to validation mechanism failure.
           // Should proceed with other checks

+ 5 - 1
ambari-web/app/mixins/wizard/assign_master_components.js

@@ -1069,16 +1069,20 @@ App.AssignMasterComponents = Em.Mixin.create({
 
   /**
    * Submit button click handler
+   * Disable 'Next' button while it is already under process. (using Router's property 'nextBtnClickInProgress')
    * @method submit
    */
   submit: function () {
     var self = this;
-    if (!this.get('submitButtonClicked')) {
+    if (!this.get('submitButtonClicked') && !App.router.get('nextBtnClickInProgress')) {
       this.set('submitButtonClicked', true);
+      App.router.set('nextBtnClickInProgress', true);
 
       var goNextStepIfValid = function () {
         if (!self.get('submitDisabled')) {
           App.router.send('next');
+        }else{
+          App.router.set('nextBtnClickInProgress', false);
         }
         self.set('submitButtonClicked', false);
       };

+ 1 - 0
ambari-web/app/router.js

@@ -55,6 +55,7 @@ App.Router = Em.Router.extend({
   isFwdNavigation: true,
   backBtnForHigherStep: false,
   transitionInProgress: false,
+  nextBtnClickInProgress: false,
 
   /**
    * Path for local login page. This page will be always accessible without

+ 41 - 27
ambari-web/app/routes/installer.js

@@ -126,10 +126,15 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
     back: Em.Router.transitionTo('step0'),
     next: function (router) {
       console.time('step1 next');
+      if(App.router.nextBtnClickInProgress || router.transitionInProgress){
+        return;
+      }
       var wizardStep1Controller = router.get('wizardStep1Controller');
       var installerController = router.get('installerController');
       installerController.validateJDKVersion(function() {
         installerController.checkRepoURL(wizardStep1Controller).done(function () {
+          App.router.nextBtnClickInProgress = true;
+          router.transitionInProgress = true;
           installerController.setDBProperty('service', undefined);
           installerController.setStacks();
           installerController.clearInstallOptions();
@@ -156,11 +161,14 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
     back: Em.Router.transitionTo('step1'),
     next: function (router) {
       console.time('step2 next');
-      var controller = router.get('installerController');
-      controller.save('installOptions');
-      //hosts was saved to content.hosts inside wizardStep2Controller
-      controller.save('hosts');
-      router.transitionTo('step3');
+      if (!router.transitionInProgress) {
+        router.set('transitionInProgress', true);
+        var controller = router.get('installerController');
+        controller.save('installOptions');
+        //hosts was saved to content.hosts inside wizardStep2Controller
+        controller.save('hosts');
+        router.transitionTo('step3');
+      }
       console.timeEnd('step2 next');
     }
   }),
@@ -230,18 +238,21 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
 
     next: function (router) {
       console.time('step4 next');
-      var controller = router.get('installerController');
-      var wizardStep4Controller = router.get('wizardStep4Controller');
-      controller.saveServices(wizardStep4Controller);
-      controller.saveClients(wizardStep4Controller);
-      router.get('wizardStep5Controller').clearRecommendations(); // Force reload recommendation between steps 4 and 5
-      controller.setDBProperties({
-        recommendations: undefined,
-        masterComponentHosts: undefined
-      });
-      controller.set('stackConfigsLoaded', false);
-      App.configsCollection.clearAll();
-      router.transitionTo('step5');
+      if (!router.transitionInProgress) {
+        router.set('transitionInProgress', true);
+        var controller = router.get('installerController');
+        var wizardStep4Controller = router.get('wizardStep4Controller');
+        controller.saveServices(wizardStep4Controller);
+        controller.saveClients(wizardStep4Controller);
+        router.get('wizardStep5Controller').clearRecommendations(); // Force reload recommendation between steps 4 and 5
+        controller.setDBProperties({
+          recommendations: undefined,
+          masterComponentHosts: undefined
+        });
+        controller.set('stackConfigsLoaded', false);
+        App.configsCollection.clearAll();
+        router.transitionTo('step5');
+      }
       console.timeEnd('step4 next');
     }
   }),
@@ -268,16 +279,19 @@ module.exports = Em.Route.extend(App.RouterRedirections, {
     back: Em.Router.transitionTo('step4'),
     next: function (router) {
       console.time('step5 next');
-      var controller = router.get('installerController');
-      var wizardStep5Controller = router.get('wizardStep5Controller');
-      var wizardStep6Controller = router.get('wizardStep6Controller');
-      controller.saveMasterComponentHosts(wizardStep5Controller);
-      controller.setDBProperties({
-        slaveComponentHosts: undefined,
-        recommendations: wizardStep5Controller.get('content.recommendations')
-      });
-      wizardStep6Controller.set('isClientsSet', false);
-      router.transitionTo('step6');
+      if (!router.transitionInProgress) {
+        router.set('transitionInProgress', true);
+        var controller = router.get('installerController');
+        var wizardStep5Controller = router.get('wizardStep5Controller');
+        var wizardStep6Controller = router.get('wizardStep6Controller');
+        controller.saveMasterComponentHosts(wizardStep5Controller);
+        controller.setDBProperties({
+          slaveComponentHosts: undefined,
+          recommendations: wizardStep5Controller.get('content.recommendations')
+        });
+        wizardStep6Controller.set('isClientsSet', false);
+        router.transitionTo('step6');
+      }
       console.timeEnd('step5 next');
     }
   }),

+ 1 - 2
ambari-web/app/views/wizard/step10_view.js

@@ -25,7 +25,6 @@ App.WizardStep10View = Em.View.extend({
 
   didInsertElement: function () {
     this.get('controller').loadStep();
-    App.get('router').set('transitionInProgress', false);
   },
 
   serviceRestartText: function () {
@@ -35,4 +34,4 @@ App.WizardStep10View = Em.View.extend({
     else
       return '';
   }.property('controller.isAddServiceWizard')
-});
+});

+ 0 - 1
ambari-web/app/views/wizard/step3_view.js

@@ -101,7 +101,6 @@ App.WizardStep3View = App.TableView.extend({
 
   didInsertElement: function () {
     this.get('controller').loadStep();
-    App.get('router').set('transitionInProgress', false);
   },
 
   /**

+ 1 - 2
ambari-web/app/views/wizard/step4_view.js

@@ -24,7 +24,6 @@ App.WizardStep4View = Em.View.extend({
   templateName: require('templates/wizard/step4'),
 
   didInsertElement: function () {
-    App.get('router').set('transitionInProgress', false);
   }
 
-});
+});

+ 0 - 1
ambari-web/app/views/wizard/step7_view.js

@@ -23,6 +23,5 @@ App.WizardStep7View = Em.View.extend({
   templateName: require('templates/wizard/step7'),
 
   didInsertElement: function () {
-    App.get('router').set('transitionInProgress', false);
   }
 });

+ 0 - 1
ambari-web/app/views/wizard/step8_view.js

@@ -25,7 +25,6 @@ App.WizardStep8View = Em.View.extend({
 
   didInsertElement: function () {
     this.get('controller').loadStep();
-    App.get('router').set('transitionInProgress', false);
   },
 
   /**

+ 0 - 1
ambari-web/app/views/wizard/step9_view.js

@@ -222,7 +222,6 @@ App.WizardStep9View = App.TableView.extend({
   didInsertElement: function () {
     this.onStatus();
     this.get('controller').navigateStep();
-    App.get('router').set('transitionInProgress', false);
   },
 
   /**

+ 12 - 1
ambari-web/test/controllers/wizard/step0_test.js

@@ -27,11 +27,13 @@ describe('App.WizardStep0Controller', function () {
     wizardStep0Controller = App.WizardStep0Controller.create({content: {cluster: {}}});
     sinon.stub(App.clusterStatus, 'set', Em.K);
     sinon.stub(App.router, 'send', Em.K);
+    App.router.nextBtnClickInProgress = false;
   });
 
   afterEach(function() {
     App.clusterStatus.set.restore();
     App.router.send.restore();
+    App.router.nextBtnClickInProgress = false;
   });
 
   describe('#invalidClusterName', function () {
@@ -76,6 +78,15 @@ describe('App.WizardStep0Controller', function () {
       expect(App.router.send.called).to.equal(false);
       expect(App.clusterStatus.set.called).to.equal(false);
     });
+    it('if Next button is clicked multiple times before the next step renders, it must not be processed', function() {
+      wizardStep0Controller.set('content.cluster.name', 'tdk');
+      wizardStep0Controller.submit();
+      expect(App.router.send.calledWith('next')).to.equal(true);
+
+      App.router.send.reset();
+      wizardStep0Controller.submit();
+      expect(App.router.send.called).to.equal(false);
+    });
   });
 
-});
+});

+ 15 - 0
ambari-web/test/controllers/wizard/step3_test.js

@@ -42,12 +42,14 @@ describe('App.WizardStep3Controller', function () {
     sinon.stub(App.db, 'getDisplayLength', Em.K);
     sinon.stub(App.db, 'getFilterConditions').returns([]);
     sinon.stub(App.router, 'send', Em.K);
+    App.router.nextBtnClickInProgress = false;
   });
 
   afterEach(function () {
     App.db.getDisplayLength.restore();
     App.router.send.restore();
     App.db.getFilterConditions.restore();
+    App.router.nextBtnClickInProgress = false;
   });
 
   App.TestAliases.testAsComputedGt(getController(), 'isHostHaveWarnings', 'warnings.length', 0);
@@ -1084,6 +1086,19 @@ describe('App.WizardStep3Controller', function () {
       expect(c.get('confirmedHosts')).to.eql(bootHosts);
     });
 
+    it('if Next button is clicked multiple times before the next step renders, it must not be processed',function(){
+      var bootHosts = [
+        Em.Object.create({name: 'c1'})
+      ];
+      c.reopen({isHostHaveWarnings: false, bootHosts: bootHosts, hosts: []});
+      c.submit();
+      expect(App.router.send.calledWith('next')).to.equal(true);
+
+      App.router.send.reset();
+      c.submit();
+      expect(App.router.send.calledWith('next')).to.equal(false);
+    });
+
   });
 
   describe('#hostLogPopup', function () {

+ 21 - 0
ambari-web/test/controllers/wizard/step4_test.js

@@ -558,6 +558,27 @@ describe('App.WizardStep4Controller', function () {
     })
   });
 
+  describe('#submit for  Next click', function() {
+    var c;
+    beforeEach(function(){
+      c = App.WizardStep4Controller.create();
+      sinon.stub(App.router, 'send', Em.K);
+      App.router.nextBtnClickInProgress = false;
+    });
+    afterEach(function(){
+      App.router.nextBtnClickInProgress = false;
+      App.router.send.restore();
+    });
+    it('if Next button is clicked multiple times before the next step renders, it must not be processed',function(){
+      c.reopen({isSubmitDisabled:false});
+      c.submit();
+      expect(App.router.send.calledWith('next')).to.equal(true);
+
+      App.router.send.reset();
+      c.submit();
+      expect(App.router.send.calledWith('next')).to.equal(false);
+    });
+  });
   describe('#dependencies', function() {
     var tests = [
       {

+ 20 - 0
ambari-web/test/controllers/wizard/step5_test.js

@@ -25,6 +25,13 @@ describe('App.WizardStep5Controller', function () {
 
   beforeEach(function () {
     c = App.WizardStep5Controller.create();
+    sinon.stub(App.router, 'send', Em.K);
+    App.router.nextBtnClickInProgress = false;
+  });
+
+  afterEach(function () {
+    App.router.send.restore();
+    App.router.nextBtnClickInProgress = false;
   });
 
   var controller = App.WizardStep5Controller.create();
@@ -1284,4 +1291,17 @@ describe('App.WizardStep5Controller', function () {
 
   });
 
+  describe('#submit',function(){
+    it('if Next button is clicked multiple times before the next step renders, it must not be processed',function(){
+      c.reopen({isSubmitDisabled:false, submitDisabled:false, useServerValidation:false});
+      c.submit();
+      expect(App.router.send.calledWith('next')).to.equal(true);
+
+      App.router.send.reset();
+      c.submit();
+      expect(App.router.send.calledWith('next')).to.equal(false);
+
+    });
+  });
+
 });

+ 10 - 0
ambari-web/test/controllers/wizard/step7_test.js

@@ -111,9 +111,11 @@ describe('App.InstallerStep7Controller', function () {
   beforeEach(function () {
     sinon.stub(App.config, 'setPreDefinedServiceConfigs', Em.K);
     installerStep7Controller = getController();
+    App.router.nextBtnClickInProgress = false;
   });
 
   afterEach(function() {
+    App.router.nextBtnClickInProgress = false;
     App.config.setPreDefinedServiceConfigs.restore();
     installerStep7Controller.destroy();
   });
@@ -332,6 +334,14 @@ describe('App.InstallerStep7Controller', function () {
       installerStep7Controller.submit();
       expect(installerStep7Controller.get('submitButtonClicked')).to.be.false;
     });
+    it('if Next button is clicked multiple times before the next step renders, it must not be processed',function(){
+      installerStep7Controller.submit();
+      expect(App.router.send.calledWith('next')).to.equal(true);
+
+      App.router.send.reset();
+      installerStep7Controller.submit();
+      expect(App.router.send.calledWith('next')).to.equal(false);
+    });
   });
 
   describe('#getConfigTagsSuccess', function () {

+ 1 - 1
ambari-web/test/views/wizard/step4_view_test.js

@@ -36,7 +36,7 @@ describe('App.WizardStep4View', function () {
     });
     it('router.transitionInProgress is set to false', function () {
       view.didInsertElement();
-      expect(App.get('router').set.calledWith('transitionInProgress', false)).to.be.true;
+      expect(App.get('router').set.calledWith('transitionInProgress', false)).to.be.false;
     });
   });
 });

+ 2 - 2
ambari-web/test/views/wizard/step7_view_test.js

@@ -39,7 +39,7 @@ describe('App.WizardStep7View', function() {
 
     it('should call loadStep', function() {
       view.didInsertElement();
-      expect(App.get('router').set.calledWith('transitionInProgress', false)).to.be.true;
+      expect(App.get('router').set.calledWith('transitionInProgress', false)).to.be.false;
     });
   });
-});
+});