You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by on...@apache.org on 2015/03/16 12:33:19 UTC

[2/4] ambari git commit: AMBARI-10077. Add UI unit tests for Create App Wizard controllers (onechiporenko)

http://git-wip-us.apache.org/repos/asf/ambari/blob/ed616f1d/contrib/views/slider/src/main/resources/ui/app/assets/javascripts/sinon-qunit-1.0.0.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/app/assets/javascripts/sinon-qunit-1.0.0.js b/contrib/views/slider/src/main/resources/ui/app/assets/javascripts/sinon-qunit-1.0.0.js
new file mode 100644
index 0000000..c26232f
--- /dev/null
+++ b/contrib/views/slider/src/main/resources/ui/app/assets/javascripts/sinon-qunit-1.0.0.js
@@ -0,0 +1,62 @@
+/**
+ * sinon-qunit 1.0.0, 2010/12/09
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ *
+ * (The BSD License)
+ * 
+ * Copyright (c) 2010-2011, Christian Johansen, christian@cjohansen.no
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ * 
+ *     * Redistributions of source code must retain the above copyright notice,
+ *       this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright notice,
+ *       this list of conditions and the following disclaimer in the documentation
+ *       and/or other materials provided with the distribution.
+ *     * Neither the name of Christian Johansen nor the names of his contributors
+ *       may be used to endorse or promote products derived from this software
+ *       without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*global sinon, QUnit, test*/
+sinon.assert.fail = function (msg) {
+    QUnit.ok(false, msg);
+};
+
+sinon.assert.pass = function (assertion) {
+    QUnit.ok(true, assertion);
+};
+
+sinon.config = {
+    injectIntoThis: true,
+    injectInto: null,
+    properties: ["spy", "stub", "mock", "clock", "sandbox"],
+    useFakeTimers: true,
+    useFakeServer: false
+};
+
+(function (global) {
+    var qTest = QUnit.test;
+    
+    QUnit.test = global.test = function (testName, expected, callback, async) {
+        if (arguments.length === 2) {
+            callback = expected;
+            expected = null;
+        }
+
+        return qTest(testName, expected, sinon.test(callback), async);
+    };
+}(this));

http://git-wip-us.apache.org/repos/asf/ambari/blob/ed616f1d/contrib/views/slider/src/main/resources/ui/app/assets/tests.html
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/app/assets/tests.html b/contrib/views/slider/src/main/resources/ui/app/assets/tests.html
index 6c08bff..b6591da 100644
--- a/contrib/views/slider/src/main/resources/ui/app/assets/tests.html
+++ b/contrib/views/slider/src/main/resources/ui/app/assets/tests.html
@@ -30,6 +30,8 @@
 <script src="javascripts/vendor.js"></script>
 <script src="javascripts/jquery.mockjax.js"></script>
 <script src="javascripts/ember-qunit.js"></script>
+<script src="javascripts/sinon-1.13.0.js"></script>
+<script src="javascripts/sinon-qunit-1.0.0.js"></script>
 <script src="javascripts/app.js"></script>
 <script>
   emq.globalize();

http://git-wip-us.apache.org/repos/asf/ambari/blob/ed616f1d/contrib/views/slider/src/main/resources/ui/test/integration/pages/index_test.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/test/integration/pages/index_test.js b/contrib/views/slider/src/main/resources/ui/test/integration/pages/index_test.js
index 2b63506..641a856 100644
--- a/contrib/views/slider/src/main/resources/ui/test/integration/pages/index_test.js
+++ b/contrib/views/slider/src/main/resources/ui/test/integration/pages/index_test.js
@@ -19,6 +19,7 @@
 QUnit.module('integration/pages - index', {
 
   setup: function () {
+    sinon.config.useFakeTimers = false;
     App.set('viewEnabled', true);
     App.__container__.lookup('controller:Slider').getViewDisplayParametersSuccessCallback({
       "ViewInstanceInfo": {

http://git-wip-us.apache.org/repos/asf/ambari/blob/ed616f1d/contrib/views/slider/src/main/resources/ui/test/integration/pages/slider_errors_test.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/test/integration/pages/slider_errors_test.js b/contrib/views/slider/src/main/resources/ui/test/integration/pages/slider_errors_test.js
index 7f81706..6ccc89c 100644
--- a/contrib/views/slider/src/main/resources/ui/test/integration/pages/slider_errors_test.js
+++ b/contrib/views/slider/src/main/resources/ui/test/integration/pages/slider_errors_test.js
@@ -19,6 +19,7 @@
 QUnit.module('integration/pages - index', {
 
   setup: function () {
+    sinon.config.useFakeTimers = false;
     Ember.run(App, App.advanceReadiness);
     Em.run(function () {
       var p = {

http://git-wip-us.apache.org/repos/asf/ambari/blob/ed616f1d/contrib/views/slider/src/main/resources/ui/test/integration/processes/create_new_app_test.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/test/integration/processes/create_new_app_test.js b/contrib/views/slider/src/main/resources/ui/test/integration/processes/create_new_app_test.js
index 5c658ef..5be4d5b 100644
--- a/contrib/views/slider/src/main/resources/ui/test/integration/processes/create_new_app_test.js
+++ b/contrib/views/slider/src/main/resources/ui/test/integration/processes/create_new_app_test.js
@@ -108,6 +108,8 @@ QUnit.module('integration/processes - Create New App', {
 
   setup: function () {
 
+    sinon.config.useFakeTimers = false;
+
     $.mockjax({
       type: 'GET',
       url: '*',

http://git-wip-us.apache.org/repos/asf/ambari/blob/ed616f1d/contrib/views/slider/src/main/resources/ui/test/unit/controllers/createAppWizard/step1_controller_test.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/test/unit/controllers/createAppWizard/step1_controller_test.js b/contrib/views/slider/src/main/resources/ui/test/unit/controllers/createAppWizard/step1_controller_test.js
index b167f1d..51bbe23 100644
--- a/contrib/views/slider/src/main/resources/ui/test/unit/controllers/createAppWizard/step1_controller_test.js
+++ b/contrib/views/slider/src/main/resources/ui/test/unit/controllers/createAppWizard/step1_controller_test.js
@@ -20,12 +20,49 @@ moduleFor('controller:createAppWizardStep1', 'App.CreateAppWizardStep1Controller
 
   needs: [
     'controller:createAppWizard'
-  ]
+  ],
+
+  setup: function () {
+    sinon.stub(App.ajax, 'send', Em.K);
+  },
+
+  teardown: function () {
+    App.ajax.send.restore();
+  }
+
+});
+
+var selectedType = Em.Object.create({
+  id: 'HBASE',
+  configs: {
+    n0: 'v0'
+  }
+});
+
+test('appWizardController', function () {
+
+  expect(1);
+
+  var controller = this.subject({
+    controllers: {
+      createAppWizard: {
+        key: 'value0'
+      }
+    }
+  });
+
+  Em.run(function () {
+    controller.set('controllers.createAppWizard.key', 'value1');
+  });
+
+  equal(controller.get('appWizardController.key'), 'value1', 'should link to App.CreateAppWizardController');
 
 });
 
 test('isAppTypesError', function () {
 
+  expect(2);
+
   var controller = this.subject({availableTypes: {content: []}});
   equal(controller.get('isAppTypesError'), true, 'should be true if no app types provided');
 
@@ -38,6 +75,92 @@ test('isAppTypesError', function () {
 
 });
 
+test('typeDescription', function () {
+
+  expect(2);
+
+  var controller = this.subject();
+
+  equal(controller.get('typeDescription'), '', 'default typeDescription');
+
+  Em.run(function () {
+    controller.set('selectedType', Em.Object.create({
+      displayName: 'HBASE'
+    }));
+  });
+
+  equal(controller.get('typeDescription'), Em.I18n.t('wizard.step1.typeDescription').format('HBASE'), 'typeDescription is set from selectedType.displayName');
+
+});
+
+test('initializeNewApp', function () {
+
+  expect(9);
+
+  var controller = this.subject({
+      store: Em.Object.create({
+        all: function () {
+          return [];
+        }
+      })
+    }),
+    app = Em.Object.create({
+      name: 'n',
+      includeFilePatterns: 'i',
+      excludeFilePatterns: 'e',
+      frequency: 'f',
+      queueName: 'q',
+      specialLabel: 's',
+      selectedYarnLabel: 'y'
+    }),
+    title = '{0} should be taken from appWizardController.newApp';
+
+  Em.run(function () {
+    controller.initializeNewApp();
+  });
+
+  equal(controller.get('newApp.selectedYarnLabel'), 0, 'selectedYarnLabel should be 0 as default');
+
+  var values = Em.keys(controller.get('newApp')).without('appType').without('configs').without('selectedYarnLabel').map(function (item) {
+    return controller.get('newApp.' + item);
+  });
+
+  propEqual(values.uniq(), [''], 'should set properties values to empty strings as default');
+
+  Em.run(function () {
+    controller.set('controllers.createAppWizard.newApp', app);
+    controller.initializeNewApp();
+  });
+
+  Em.keys(app).forEach(function (key) {
+    equal(controller.get('newApp.' + key), app.get(key), title.format(key));
+  });
+
+});
+
+test('loadAvailableTypes', function () {
+
+  expect(1);
+
+  var testObject = {
+      key: 'value'
+    },
+    controller = this.subject({
+    store: Em.Object.create({
+      all: function () {
+        return testObject;
+      }
+    })
+  });
+
+  Em.run(function () {
+    controller.loadAvailableTypes();
+  });
+
+  propEqual(controller.get('availableTypes'), testObject, 'availableTypes should be loaded from store');
+
+});
+
 test('nameValidator', function () {
   expect(7);
 
@@ -79,13 +202,9 @@ test('nameValidator', function () {
 
 test('validateAppNameSuccessCallback', function () {
 
-  var selectedType = Em.Object.create({
-      id: 'HBASE',
-      configs: {
-        n0: 'v0'
-      }
-    }),
-    title = 'newApp should have {0} set',
+  expect(5);
+
+  var title = 'newApp should have {0} set',
     controller = this.subject({
       newApp: Em.Object.create(),
       selectedType: selectedType
@@ -104,6 +223,67 @@ test('validateAppNameSuccessCallback', function () {
 
 });
 
+test('validateAppNameErrorCallback', function () {
+
+  expect(7);
+
+  var controller = this.subject();
+
+  Em.run(function () {
+    sinon.stub(Bootstrap.ModalManager, 'open', Em.K);
+    sinon.stub(controller, 'defaultErrorHandler', Em.K);
+    controller.validateAppNameErrorCallback({
+      status: 409
+    }, null, null, null, {
+      name: 'name'
+    });
+  });
+
+  ok(Bootstrap.ModalManager.open.calledOnce, 'app name conflict popup should be displayed');
+  ok(!controller.defaultErrorHandler.called, 'defaultErrorHandler shouldn\'t be executed');
+
+  Em.run(function () {
+    Bootstrap.ModalManager.open.restore();
+    controller.defaultErrorHandler.restore();
+    sinon.stub(Bootstrap.ModalManager, 'open', Em.K);
+    sinon.stub(controller, 'defaultErrorHandler', Em.K);
+    controller.validateAppNameErrorCallback({
+      status: 400
+    }, null, null, {
+      url: 'url',
+      type: 'type'
+    }, null);
+  });
+
+  ok(!Bootstrap.ModalManager.open.called, 'app name conflict popup shouldn\'t be displayed');
+  ok(controller.defaultErrorHandler.calledOnce, 'defaultErrorHandler should be executed');
+  propEqual(controller.defaultErrorHandler.firstCall.args[0], {
+    status: 400
+  }, 'should pass request info to defaultErrorHandler');
+  equal(controller.defaultErrorHandler.firstCall.args[1], 'url', 'should pass url to defaultErrorHandler');
+  equal(controller.defaultErrorHandler.firstCall.args[2], 'type', 'should pass type to defaultErrorHandler');
+
+  Bootstrap.ModalManager.open.restore();
+  controller.defaultErrorHandler.restore();
+
+});
+
+test('validateAppNameCompleteCallback', function () {
+
+  expect(1);
+
+  var controller = this.subject({
+    validateAppNameRequestExecuting: true
+  });
+
+  Em.run(function () {
+    controller.validateAppNameCompleteCallback();
+  });
+
+  ok(!controller.get('validateAppNameRequestExecuting'), 'validateAppNameRequestExecuting should be set to false');
+
+});
+
 test('isSubmitDisabled', function () {
 
   expect(6);
@@ -129,8 +309,8 @@ test('isSubmitDisabled', function () {
         title: 'app name is invalid'
       },
       {
-        key: 'no app types are available',
-        title: 'request is executing'
+        key: 'isAppTypesError',
+        title: 'no app types are available'
       },
       {
         key: 'isFrequencyError',
@@ -205,4 +385,47 @@ test('frequencyValidator', function () {
     equal(controller.get('frequencyErrorMessage'), item.frequencyErrorMessage, messageTitle.format(item.title));
   });
 
+});
+
+test('saveApp', function () {
+
+  expect(4);
+
+  var controller = this.subject({
+      newApp: Em.Object.create(),
+      selectedType: selectedType
+    }),
+    saveAppTitle = 'newApp should have {0} set';
+
+  Em.run(function () {
+    controller.saveApp();
+  });
+
+  propEqual(controller.get('newApp.appType'), selectedType, saveAppTitle.format('appType'));
+  propEqual(controller.get('newApp.configs'), selectedType.configs, saveAppTitle.format('configs'));
+  deepEqual(controller.get('newApp.predefinedConfigNames'), Em.keys(selectedType.configs), saveAppTitle.format('predefinedConfigNames'));
+  propEqual(controller.get('appWizardController.newApp'), controller.get('newApp'), 'newApp should be set in CreateAppWizardController');
+
+});
+
+test('actions.submit', function () {
+
+  expect(3);
+
+  var controller = this.subject({
+    validateAppNameRequestExecuting: false,
+    validateAppNameSuccessCallback: Em.K,
+    newApp: {
+      name: 'name'
+    }
+  });
+
+  Em.run(function () {
+    controller.send('submit');
+  });
+
+  ok(controller.get('validateAppNameRequestExecuting'), 'validateAppNameRequestExecuting should be set to true');
+  ok(App.ajax.send.calledOnce, 'request to server should be sent');
+  equal(App.ajax.send.firstCall.args[0].data.name, 'name', 'name should be passed');
+
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/ed616f1d/contrib/views/slider/src/main/resources/ui/test/unit/controllers/createAppWizard/step2_controller_test.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/test/unit/controllers/createAppWizard/step2_controller_test.js b/contrib/views/slider/src/main/resources/ui/test/unit/controllers/createAppWizard/step2_controller_test.js
index 641f51a..5115a64 100644
--- a/contrib/views/slider/src/main/resources/ui/test/unit/controllers/createAppWizard/step2_controller_test.js
+++ b/contrib/views/slider/src/main/resources/ui/test/unit/controllers/createAppWizard/step2_controller_test.js
@@ -24,8 +24,325 @@ moduleFor('controller:createAppWizardStep2', 'App.CreateAppWizardStep2Controller
 
 });
 
+var title = 'should be {0}';
+
+test('appWizardController', function () {
+
+  expect(1);
+
+  var controller = this.subject({
+    controllers: {
+      createAppWizard: {
+        key: 'value0'
+      }
+    }
+  });
+
+  Em.run(function () {
+    controller.set('controllers.createAppWizard.key', 'value1');
+  });
+
+  equal(controller.get('appWizardController.key'), 'value1', 'should link to App.CreateAppWizardController');
+
+});
+
+test('isError', function () {
+
+  expect(18);
+
+  var cases = [
+      {
+        content: [],
+        isError: false
+      },
+      {
+        content: [
+          Em.Object.create()
+        ],
+        isError: true
+      },
+      {
+        content: [
+          Em.Object.create({
+            numInstances: '0'
+          })
+        ],
+        isError: true
+      },
+      {
+        content: [
+          Em.Object.create({
+            yarnMemory: '0'
+          })
+        ],
+        isError: true
+      },
+      {
+        content: [
+          Em.Object.create({
+            yarnCPU: '0'
+          })
+        ],
+        isError: true
+      },
+      {
+        content: [
+          Em.Object.create({
+            numInstances: ' \r\n',
+            yarnMemory: ' \r\n',
+            yarnCPU: ' \r\n'
+          })
+        ],
+        isError: true
+      },
+      {
+        content: [
+          Em.Object.create({
+            numInstances: 'n',
+            yarnMemory: '0',
+            yarnCPU: '0'
+          })
+        ],
+        isError: true
+      },
+      {
+        content: [
+          Em.Object.create({
+            numInstances: '0',
+            yarnMemory: 'n',
+            yarnCPU: '0'
+          })
+        ],
+        isError: true
+      },
+      {
+        content: [
+          Em.Object.create({
+            numInstances: '0',
+            yarnMemory: '0',
+            yarnCPU: 'n'
+          })
+        ],
+        isError: true
+      },
+      {
+        content: [
+          Em.Object.create({
+            numInstances: '0.5',
+            yarnMemory: '0',
+            yarnCPU: '0'
+          })
+        ],
+        isError: true
+      },
+      {
+        content: [
+          Em.Object.create({
+            numInstances: '0',
+            yarnMemory: '0.5',
+            yarnCPU: '0'
+          })
+        ],
+        isError: true
+      },
+      {
+        content: [
+          Em.Object.create({
+            numInstances: '0',
+            yarnMemory: '0',
+            yarnCPU: '0.5'
+          })
+        ],
+        isError: true
+      },
+      {
+        content: [
+          Em.Object.create({
+            numInstances: '-1',
+            yarnMemory: '0',
+            yarnCPU: '0'
+          })
+        ],
+        isError: true
+      },
+      {
+        content: [
+          Em.Object.create({
+            numInstances: '0',
+            yarnMemory: '-1',
+            yarnCPU: '0'
+          })
+        ],
+        isError: true
+      },
+      {
+        content: [
+          Em.Object.create({
+            numInstances: '0',
+            yarnMemory: '0',
+            yarnCPU: '-1'
+          })
+        ],
+        isError: true
+      },
+      {
+        content: [
+          Em.Object.create({
+            numInstances: '0',
+            yarnMemory: '0',
+            yarnCPU: '0'
+          }),
+          Em.Object.create({
+            numInstances: '-1',
+            yarnMemory: '0',
+            yarnCPU: '0'
+          })
+        ],
+        isError: true
+      },
+      {
+        content: [
+          Em.Object.create({
+            numInstances: '0',
+            yarnMemory: '0',
+            yarnCPU: '0'
+          })
+        ],
+        isError: false
+      },
+      {
+        content: [
+          Em.Object.create({
+            numInstances: '0',
+            yarnMemory: '0',
+            yarnCPU: '0'
+          }),
+          Em.Object.create({
+            numInstances: '1',
+            yarnMemory: '1',
+            yarnCPU: '1'
+          })
+        ],
+        isError: false
+      }
+    ],
+    controller = this.subject();
+
+  cases.forEach(function (item) {
+
+    Em.run(function () {
+      controller.set('content', item.content);
+    });
+
+    equal(controller.get('isError'), item.isError, title.format(item.isError));
+
+  });
+
+});
+
+test('isSubmitDisabled', function () {
+
+  expect(2);
+
+  var cases = [true, false],
+  controller = this.subject();
+
+  cases.forEach(function (item) {
+
+    Em.run(function () {
+      controller.set('isError', item);
+    });
+
+    equal(controller.get('isSubmitDisabled'), item, title.format(item));
+
+  });
+
+});
+
+test('initializeNewApp', function () {
+
+  expect(4);
+
+  var controller = this.subject();
+
+  Em.run(function () {
+    controller.set('controllers.createAppWizard.newApp', {
+      components: [
+        Em.Object.create({
+          name: 'n'
+        })
+      ]
+    });
+    controller.initializeNewApp();
+  });
+
+  equal(controller.get('newApp.components.length'), 1, 'newApp should be taken from appWizardController');
+  equal(controller.get('newApp.components')[0].get('name'), 'n', 'newApp has correct names of components');
+  equal(controller.get('content.length'), 1, 'content should be taken from appWizardController');
+  equal(controller.get('content')[0].get('name'), 'n', 'content has correct names of components');
+
+});
+
+test('loadTypeComponents', function () {
+
+  expect(8);
+
+  var toStringTitle = 'should convert {0} to string',
+    controller = this.subject();
+
+  Em.run(function () {
+    controller.set('newApp', {
+      appType: {
+        components: [
+          Em.Object.create({
+            name: 'n0',
+            defaultNumInstances: 0,
+            defaultYARNMemory: 128,
+            defaultYARNCPU: 1
+          })
+        ]
+      }
+    });
+    controller.set('controllers.createAppWizard.newApp', {
+      components: [
+        Em.Object.create({
+          name: 'n1'
+        })
+      ]
+    });
+    controller.loadTypeComponents();
+  });
+
+  equal(controller.get('content.length'), 1, 'content should contain one item');
+  equal(controller.get('content')[0].get('name'), 'n1', 'should take components from wizard controller');
+
+  Em.run(function () {
+    controller.get('content').clear();
+    controller.get('controllers.createAppWizard.newApp.components').clear();
+    controller.loadTypeComponents();
+  });
+
+  equal(controller.get('content.length'), 1, 'content contains one item');
+  equal(controller.get('content')[0].get('name'), 'n0', 'should take components from step controller');
+  deepEqual(controller.get('content')[0].get('numInstances'), '0', toStringTitle.format('numInstances'));
+  deepEqual(controller.get('content')[0].get('yarnMemory'), '128', toStringTitle.format('yarnMemory'));
+  deepEqual(controller.get('content')[0].get('yarnCPU'), '1', toStringTitle.format('yarnCPU'));
+
+  Em.run(function () {
+    controller.get('content').clear();
+    controller.get('controllers.createAppWizard.newApp.components').clear();
+    controller.get('newApp.appType.components').clear();
+    controller.loadTypeComponents();
+  });
+
+  equal(controller.get('content.length'), 0, 'content should remain empty');
+
+});
+
 test('isNotInteger', function () {
 
+  expect(6);
+
   var controller = this.subject({});
   equal(controller.isNotInteger('1'), false, 'Valid value');
   equal(controller.isNotInteger('-1'), true, 'Invalid value (1)');
@@ -35,3 +352,52 @@ test('isNotInteger', function () {
   equal(controller.isNotInteger(null), true, 'Invalid value (5)');
 
 });
+
+test('saveComponents', function () {
+
+  expect(2);
+
+  var controller = this.subject({
+    content: [
+      Em.Object.create({
+        name: 'n'
+      })
+    ]
+  });
+
+  Em.run(function () {
+    controller.set('controllers.createAppWizard.newApp', {});
+    controller.saveComponents();
+  });
+
+  equal(controller.get('appWizardController.newApp.components.length'), 1, 'components in wizard controller should be set from content');
+  equal(controller.get('appWizardController.newApp.components')[0].get('name'), 'n', 'components in wizard controller have correct names');
+
+});
+
+test('actions.submit', function () {
+
+  expect(3);
+
+  var controller = this.subject({
+    content: [
+      Em.Object.create({
+        name: 'n'
+      })
+    ]
+  });
+
+  Em.run(function () {
+    controller.get('controllers.createAppWizard').setProperties({
+      newApp: {},
+      currentStep: 2,
+      transitionToRoute: Em.K
+    });
+    controller.send('submit');
+  });
+
+  equal(controller.get('appWizardController.newApp.components.length'), 1, 'components in wizard controller should be set from content');
+  equal(controller.get('appWizardController.newApp.components')[0].get('name'), 'n', 'components in wizard controller have correct names');
+  equal(controller.get('appWizardController.currentStep'), '3', 'should go to step3');
+
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/ed616f1d/contrib/views/slider/src/main/resources/ui/test/unit/controllers/createAppWizard/step3_controller_test.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/test/unit/controllers/createAppWizard/step3_controller_test.js b/contrib/views/slider/src/main/resources/ui/test/unit/controllers/createAppWizard/step3_controller_test.js
index 6a10e61..17d66e4 100644
--- a/contrib/views/slider/src/main/resources/ui/test/unit/controllers/createAppWizard/step3_controller_test.js
+++ b/contrib/views/slider/src/main/resources/ui/test/unit/controllers/createAppWizard/step3_controller_test.js
@@ -19,44 +19,403 @@
 moduleFor('controller:createAppWizardStep3', 'App.CreateAppWizardStep3Controller', {
 
   needs: [
-    'controller:createAppWizard',
-    'controller:slider'
-  ]
+    'controller:createAppWizard'
+  ],
+
+  teardown: function () {
+    App.reset();
+  }
+
+});
+
+test('appWizardController', function () {
+
+  expect(1);
+
+  var controller = this.subject({
+    controllers: {
+      createAppWizard: {
+        key: 'value0'
+      }
+    }
+  });
+
+  Em.run(function () {
+    controller.set('controllers.createAppWizard.key', 'value1');
+  });
+
+  equal(controller.get('appWizardController.key'), 'value1', 'should link to App.CreateAppWizardController');
+
+});
+
+test('newAppConfigs', function () {
+
+  expect(1);
+
+  var configs = {
+      java_home: '/usr/jdk64/jdk1.7.0_40'
+    },
+    controller = this.subject();
+
+  Em.run(function () {
+    controller.set('controllers.createAppWizard.newApp', {
+      configs: configs
+    });
+  });
+
+  propEqual(controller.get('newAppConfigs'), configs, 'configs should be taken from wizard controller');
+
+});
+
+test('sectionKeys', function () {
+
+  expect(2);
+
+  var cases = [
+      {
+        sectionKeys: ['general', 'custom'],
+        title: 'no newAppConfigs set'
+      },
+      {
+        newAppConfigs: {
+          'p0': 'v0',
+          'site.p1': 'v1',
+          'site.p2.0': 'v2',
+          'site.p2.1': 'v3'
+        },
+        sectionKeys: ['general', 'p1', 'p2', 'custom'],
+        title: 'newAppConfigs are set'
+      }
+    ],
+    controller = this.subject();
+
+  cases.forEach(function (item) {
+
+    Em.run(function () {
+      controller.set('controllers.createAppWizard.newApp', {
+        configs: item.newAppConfigs
+      });
+    });
+
+    propEqual(controller.get('sectionKeys'), item.sectionKeys, item.title);
+
+  });
+
+});
+
+test('loadStep', function () {
+
+  expect(3);
+
+  var controller = this.subject();
+
+  Em.run(function () {
+    sinon.stub(controller, 'clearStep', Em.K);
+    sinon.stub(controller, 'initConfigs', Em.K);
+    controller.loadStep();
+  });
+
+  ok(controller.clearStep.calledOnce, 'clearStep should be executed');
+  ok(controller.initConfigs.calledOnce, 'initConfigs should be executed');
+  ok(controller.initConfigs.firstCall.args[0], 'true should be passed as argument to initConfigs');
+
+  controller.clearStep.restore();
+  controller.initConfigs.restore();
 
 });
 
 test('initConfigs', function () {
 
+  expect(15);
+
   var controller = this.subject(),
-    wizardController = controller.get('controllers.createAppWizard');
+    titleDefault = 'should set default {0} property value',
+    titleCustom = 'should set custom {0} property value',
+    titleGlobal = 'should set global {0} property value',
+    titleLabel = 'label shouldn\'t contain \'site.\'';
 
   Em.run(function () {
-    wizardController.setProperties({
-      content: {},
-      newApp: {
-        appType: {
-          configs: []
+    App.setProperties({
+      javaHome: '/usr/jdk64/jdk1.7.0_45',
+      metricsHost: 'host0',
+      metricsPort: '3333',
+      metricsLibPath: '/metrics/lib'
+    });
+    controller.get('controllers.createAppWizard').setProperties({
+      'content': {},
+      'newApp': {
+        'appType': {
+          'configs': {}
         },
-        configs: {
-          java_home: '/usr/jdk64/jdk1.7.0_40'
+        'configs': {
+          'java_home': '/usr/jdk64/jdk1.7.0_40',
+          'site.global.metric_collector_host': 'host1',
+          'site.global.metric_collector_port': '8080',
+          'site.global.metric_collector_lib': '/ams/lib'
+        }
+      }
+    });
+    controller.initConfigs(true);
+  });
+
+  equal(controller.get('configs').findBy('name', 'java_home').get('value'), '/usr/jdk64/jdk1.7.0_45', titleGlobal.format('java_home'));
+  equal(controller.get('configs').findBy('name', 'java_home').get('label'), 'java_home', 'label should be equal to name');
+  equal(controller.get('configs').findBy('name', 'ams_metrics').configs.findBy('name', 'site.global.metric_collector_host')
+    .get('value'), 'host0', titleGlobal.format('site.global.metric_collector_host'));
+  equal(controller.get('configs').findBy('name', 'ams_metrics').configs.findBy('name', 'site.global.metric_collector_port')
+    .get('value'), '3333', titleGlobal.format('site.global.metric_collector_port'));
+  equal(controller.get('configs').findBy('name', 'ams_metrics').configs.findBy('name', 'site.global.metric_collector_lib')
+    .get('value'), '/metrics/lib', titleGlobal.format('site.global.metric_collector_lib'));
+  equal(controller.get('configs').findBy('name', 'ams_metrics').configs.findBy('name', 'site.global.metric_collector_host')
+    .get('label'), 'global.metric_collector_host', titleLabel);
+  equal(controller.get('configs').findBy('name', 'ams_metrics').configs.findBy('name', 'site.global.metric_collector_port')
+    .get('label'), 'global.metric_collector_port', titleLabel);
+  equal(controller.get('configs').findBy('name', 'ams_metrics').configs.findBy('name', 'site.global.metric_collector_lib')
+    .get('label'), 'global.metric_collector_lib', titleLabel);
+
+  Em.run(function () {
+    App.setProperties({
+      javaHome: null,
+      metricsHost: null,
+      metricsPort: null,
+      metricsLibPath: null
+    });
+    controller.initConfigs();
+  });
+
+  equal(controller.get('configs').findBy('name', 'java_home').get('value'), '/usr/jdk64/jdk1.7.0_40', titleCustom.format('java_home'));
+  equal(controller.get('configs').findBy('name', 'ams_metrics').configs.findBy('name', 'site.global.metric_collector_host')
+    .get('value'), 'host1', titleGlobal.format('site.global.metric_collector_host'));
+  equal(controller.get('configs').findBy('name', 'ams_metrics').configs.findBy('name', 'site.global.metric_collector_port')
+    .get('value'), '8080', titleGlobal.format('site.global.metric_collector_port'));
+  equal(controller.get('configs').findBy('name', 'ams_metrics').configs.findBy('name', 'site.global.metric_collector_lib')
+    .get('value'), '/ams/lib', titleGlobal.format('site.global.metric_collector_lib'));
+
+  Em.run(function () {
+    controller.get('controllers.createAppWizard').setProperties({
+      'newApp': {
+        'appType': {
+          'configs':  {
+            'site.global.metric_collector_host': 'host3',
+            'site.global.metric_collector_port': '8888',
+            'site.global.metric_collector_lib': '/var/ams/lib'
+          }
+        },
+        'configs': {}
+      }
+    });
+    controller.initConfigs();
+  });
+
+  equal(controller.get('configs').findBy('name', 'ams_metrics').configs.findBy('name', 'site.global.metric_collector_host')
+    .get('value'), 'host3', titleDefault.format('site.global.metric_collector_host'));
+  equal(controller.get('configs').findBy('name', 'ams_metrics').configs.findBy('name', 'site.global.metric_collector_port')
+    .get('value'), '8888', titleDefault.format('site.global.metric_collector_port'));
+  equal(controller.get('configs').findBy('name', 'ams_metrics').configs.findBy('name', 'site.global.metric_collector_lib')
+    .get('value'), '/var/ams/lib', titleDefault.format('site.global.metric_collector_lib'));
+
+});
+
+test('initConfigSetDependencies', function () {
+
+  expect(1);
+
+  var configSet = {
+      dependencies: [
+        {
+          name: 'App.javaHome'
         }
+      ]
+    },
+    javaHome = '/usr/jdk64/jdk1.7.0_40',
+    controller = this.subject();
+
+  Em.run(function () {
+    App.set('javaHome', javaHome);
+    controller.initConfigSetDependencies(configSet);
+  });
+
+  equal(configSet.dependencies[0].map, javaHome, 'should set map property');
+
+});
+
+test('clearStep', function () {
+
+  expect(1);
+
+  var controller = this.subject();
+
+  Em.run(function () {
+    controller.clearStep();
+  });
+
+  ok(!controller.get('isError'), 'isError should be false');
+
+});
+
+test('validateConfigs', function () {
+
+  expect(4);
+
+  var controller = this.subject({
+    configs: [
+      {
+        isSet: false,
+        name: 'p0',
+        value: 'v0'
       }
+    ]
+  });
+
+  ok(controller.validateConfigs(), 'configs are valid');
+  propEqual(controller.get('configsObject'), {
+    p0: 'v0'
+  }, 'configsObject is set');
+
+  Em.run(function () {
+    controller.set('addConfigSetProperties', function () {
+      return null;
     });
-    App.__container__.lookup('controller:slider').getViewDisplayParametersSuccessCallback({
-      "ViewInstanceInfo" : {
-        "instance_data": {
-          "java.home": "/usr/jdk64/jdk1.7.0_45"
+  });
+
+  ok(!controller.validateConfigs(), 'configs are invalid');
+  ok(controller.get('isError'), 'isError is set to true');
+
+});
+
+test('addConfigSetProperties', function () {
+
+  expect(1);
+
+  var configs = [
+      {
+        isSet: false,
+        configs: [
+          {
+            name: 'p0',
+            value: 'v0'
+          }
+        ]
+      },
+      {
+        isSet: true,
+        trigger: {},
+        configs: [
+          {
+            name: 'p1',
+            value: 'v1'
+          }
+        ]
+      },
+      {
+        isSet: true,
+        trigger: {
+          name: 'p2',
+          value: 'v2'
         },
-        "properties": {
-          "slider.user": "admin"
+        configs: [
+          {
+            name: 'p3',
+            value: 'v3'
+          }
+        ]
+      }
+    ],
+    controller = this.subject();
+
+    deepEqual(controller.addConfigSetProperties(configs), [configs[0], configs[2].configs[0]], 'should add config from config sets to general configs array');
+
+});
+
+test('saveConfigs', function () {
+
+  expect(5);
+
+  var configsObject = {
+      p0: 'v0'
+    },
+    controller = this.subject({
+      configsObject: configsObject
+    }),
+    metricsCases = [
+      {
+        configsObject: {
+          'site.global.metrics_enabled': null
         }
+      },
+      {
+        configsObject: {
+          'site.global.metrics_enabled': 'true',
+          'site.global.metric_collector_host': 'h0',
+          'site.global.metric_collector_port': '3333'
+        },
+        metricsEnabledExpected: 'true'
+      },
+      {
+        configsObject: {
+          'site.global.metrics_enabled': 'true',
+          'site.global.metric_collector_host': null,
+          'site.global.metric_collector_port': '8080'
+        },
+        metricsEnabledExpected: 'false'
+      },
+      {
+        configsObject: {
+          'site.global.metrics_enabled': 'true',
+          'site.global.metric_collector_host': 'h1',
+          'site.global.metric_collector_port': null
+        },
+        metricsEnabledExpected: 'false'
       }
+    ],
+    metricsTitle = 'site.global.metrics_enabled should be {0}';
+
+  Em.run(function () {
+    controller.set('controllers.createAppWizard.newApp', {});
+    controller.saveConfigs();
+  });
+
+  propEqual(controller.get('controllers.createAppWizard.newApp.configs'), configsObject, 'configs are saved to wizard controller');
+
+  metricsCases.forEach(function (item) {
+    controller.reopen({
+      configsObject: item.configsObject
     });
-    Em.run(function () {
-      controller.initConfigs(true);
+    controller.set('controllers.createAppWizard.newApp', {});
+    controller.saveConfigs();
+    equal(controller.get('controllers.createAppWizard.newApp.configs')['site.global.metrics_enabled'],
+      item.metricsEnabledExpected, metricsTitle.format(item.metricsEnabledExpected || 'undefined'));
+  }, this);
+
+});
+
+test('actions.submit', function () {
+
+  expect(2);
+
+  var configsObject = {
+      p0: 'v0'
+    },
+    controller = this.subject({
+      configs: [
+        {
+          isSet: false,
+          name: 'p0',
+          value: 'v0'
+        }
+      ]
+    });
+
+  Em.run(function () {
+    controller.get('controllers.createAppWizard').setProperties({
+      newApp: {},
+      currentStep: 3,
+      transitionToRoute: Em.K
     });
+    controller.send('submit');
   });
 
-  equal(controller.get('configs').findBy('label', 'java_home').get('value'), '/usr/jdk64/jdk1.7.0_45', 'should set default java_home property value');
+  propEqual(controller.get('controllers.createAppWizard.newApp.configs'), configsObject, 'configs are passed to wizard controller');
+  equal(controller.get('controllers.createAppWizard.currentStep'), 4, 'should go to step4');
 
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/ed616f1d/contrib/views/slider/src/main/resources/ui/test/unit/controllers/createAppWizard/step4_controller_test.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/test/unit/controllers/createAppWizard/step4_controller_test.js b/contrib/views/slider/src/main/resources/ui/test/unit/controllers/createAppWizard/step4_controller_test.js
index 627e726..fe44f47 100644
--- a/contrib/views/slider/src/main/resources/ui/test/unit/controllers/createAppWizard/step4_controller_test.js
+++ b/contrib/views/slider/src/main/resources/ui/test/unit/controllers/createAppWizard/step4_controller_test.js
@@ -23,17 +23,40 @@ moduleFor('controller:createAppWizardStep4', 'App.CreateAppWizardStep4Controller
   ],
 
   setup: function () {
-    App.ajax.send = Em.K;
+    sinon.stub(App.ajax, 'send', Em.K);
   },
 
   teardown: function () {
+    App.ajax.send.restore();
     App.reset();
   }
 
 });
 
+test('appWizardController', function () {
+
+  expect(1);
+
+  var controller = this.subject({
+    controllers: {
+      createAppWizard: {
+        key: 'value0'
+      }
+    }
+  });
+
+  Em.run(function () {
+    controller.set('controllers.createAppWizard.key', 'value1');
+  });
+
+  equal(controller.get('appWizardController.key'), 'value1', 'should link to App.CreateAppWizardController');
+
+});
+
 test('isSubmitDisabled', function () {
 
+  expect(3);
+
   var controller = this.subject({
     newApp: Em.Object.create({
       appType: {
@@ -58,9 +81,31 @@ test('isSubmitDisabled', function () {
 
 });
 
+test('configsFormatted', function () {
+
+  expect(1);
+
+  var controller = this.subject();
+
+  Em.run(function () {
+    controller.set('newApp', {
+      configs: {
+        p0: 'v0',
+        p1: 'v1',
+        p2: 'v2'
+      }
+    });
+  });
+
+  equal(controller.get('configsFormatted'), '"p0":"v0",\n"p1":"v1",\n"p2":"v2"', 'configs formatted correctly');
+
+});
+
 test('resourcesFormatted', function () {
 
-  var cases = [
+  expect(18);
+
+  var propertiesCases = [
       {
         propertyName: 'numInstances',
         expectedPropertyName: 'instanceCount',
@@ -82,24 +127,73 @@ test('resourcesFormatted', function () {
         value: 2
       }
     ],
+    globalCases = [
+      {
+        includeFilePatterns: null,
+        includeFilePatternsExpected: null,
+        excludeFilePatterns: null,
+        excludeFilePatternsExpected: null,
+        frequency: '1000',
+        frequencyExpected: '1000',
+        title: 'all parameters except one are null'
+      },
+      {
+        includeFilePatterns: '*.log',
+        includeFilePatternsExpected: '*.log',
+        excludeFilePatterns: '*.zip',
+        excludeFilePatternsExpected: '*.zip',
+        frequency: '1000',
+        frequencyExpected: '1000',
+        title: 'all parameters are valid'
+      }
+    ],
+    globalCasesUndefined = [
+      {
+        includeFilePatterns: null,
+        excludeFilePatterns: null,
+        frequency: null,
+        title: 'no patterns and frequency specified'
+      },
+      {
+        includeFilePatterns: ' \r\n',
+        excludeFilePatterns: null,
+        frequency: null,
+        title: 'one parameter is empty string after trimming'
+      }
+    ],
+    selectedYarnLabelCases = [
+      {
+        selectedYarnLabel: 0,
+        components: []
+      },
+      {
+        selectedYarnLabel: 1,
+        yarnLabelExpression: ''
+      },
+      {
+        selectedYarnLabel: 2,
+        specialLabel: 'specialLabel',
+        yarnLabelExpression: 'specialLabel'
+      }
+    ],
     title = '{0} should be {1}',
-    label = 'label';
+    selectedYarnLabelTitle = 'selected YARN label is {0}',
+    label = 'label',
+    controller = this.subject({
+      newApp: Em.Object.create({
+        components: [
+          Em.Object.create({
+            name: 'c',
+            numInstances: '0',
+            yarnMemory: '512',
+            yarnCPU: '1',
+            priority: 1
+          })
+        ]
+      })
+    });
 
-  var controller = this.subject({
-    newApp: Em.Object.create({
-      components: [
-        Em.Object.create({
-          name: 'c',
-          numInstances: '0',
-          yarnMemory: '512',
-          yarnCPU: '1',
-          priority: 1
-        })
-      ]
-    })
-  });
-
-  cases.forEach(function (item) {
+  propertiesCases.forEach(function (item) {
 
     Em.run(function () {
       controller.get('newApp.components')[0].set(item.propertyName, item.value);
@@ -130,4 +224,215 @@ test('resourcesFormatted', function () {
 
   equal(controller.get('resourcesFormatted.components')[0].yarnLabel, label, 'yarnLabel should be trimmed');
 
+  globalCases.forEach(function (item) {
+
+    Em.run(function () {
+      controller.get('newApp').setProperties({
+        includeFilePatterns: item.includeFilePatterns,
+        excludeFilePatterns: item.excludeFilePatterns,
+        frequency: item.frequency
+      });
+      controller.notifyPropertyChange('newApp.components.@each.numInstances');
+    });
+
+    var global = controller.get('resourcesFormatted.global');
+
+    deepEqual(global['yarn.log.include.patterns'], item.includeFilePatternsExpected, item.title);
+    deepEqual(global['yarn.log.exclude.patterns'], item.excludeFilePatternsExpected, item.title);
+    deepEqual(global['yarn.log.interval'], item.frequencyExpected, item.title);
+
+  });
+
+  globalCasesUndefined.forEach(function (item) {
+
+    Em.run(function () {
+      controller.get('newApp').setProperties({
+        includeFilePatterns: item.includeFilePatterns,
+        excludeFilePatterns: item.excludeFilePatterns,
+        frequency: item.frequency
+      });
+      controller.notifyPropertyChange('newApp.components.@each.numInstances');
+    });
+
+    equal(typeof controller.get('resourcesFormatted.global'), 'undefined', item.title);
+
+  });
+
+  selectedYarnLabelCases.forEach(function (item) {
+
+    Em.run(function () {
+      controller.get('newApp').setProperties({
+        selectedYarnLabel: item.selectedYarnLabel,
+        specialLabel: item.specialLabel
+      });
+      controller.notifyPropertyChange('newApp.components.@each.numInstances');
+    });
+
+    var message = selectedYarnLabelTitle.format(item.selectedYarnLabel);
+
+    if (Em.isNone(item.yarnLabelExpression)) {
+      ok(!controller.get('resourcesFormatted.components').isAny('id', 'slider-appmaster'), message);
+    } else {
+      equal(controller.get('resourcesFormatted.components').findBy('id', 'slider-appmaster')['yarn.label.expression'],
+        item.yarnLabelExpression, message);
+    }
+
+  });
+
+});
+
+test('loadStep', function () {
+
+  expect(1);
+
+  var controller = this.subject();
+
+  Em.run(function () {
+    sinon.stub(controller, 'initializeNewApp', Em.K);
+    controller.loadStep();
+  });
+
+  ok(controller.initializeNewApp.calledOnce, 'initializeNewApp should be executed');
+
+  controller.initializeNewApp.restore();
+
+});
+
+test('initializeNewApp', function () {
+
+  expect(1);
+
+  var newApp = {
+      key: 'value'
+    },
+    controller = this.subject({
+      controllers: {
+        createAppWizard: {
+          newApp: newApp
+        }
+      }
+    });
+
+  Em.run(function () {
+    controller.loadStep();
+  });
+
+  propEqual(controller.get('newApp'), newApp, 'should initialize new app');
+
+});
+
+test('sendAppDataToServerSuccessCallback', function () {
+
+  expect(1);
+
+  var controller = this.subject();
+
+  Em.run(function () {
+    sinon.stub(controller.get('appWizardController'), 'hidePopup', Em.K);
+    controller.sendAppDataToServerSuccessCallback();
+  });
+
+  ok(controller.get('appWizardController').hidePopup.calledOnce, 'popup should be closed');
+
+  controller.get('appWizardController').hidePopup.restore();
+
+});
+
+test('sendAppDataToServerCompleteCallback', function () {
+
+  expect(1);
+
+  var controller = this.subject({
+    isSubmitDisabled: true
+  });
+
+  Em.run(function () {
+    controller.sendAppDataToServerCompleteCallback();
+  });
+
+  ok(!controller.get('isSubmitDisabled'), 'Finish button should be enabled');
+
+});
+
+test('sendAppDataToServer', function () {
+
+  var controller = this.subject({
+      newApp: Em.Object.create({
+        appType: {
+          index: 'ACCUMULO',
+          version: '1'
+        },
+        name: 'name',
+        configs: {
+          key: 'value'
+        }
+      }),
+      resourcesFormatted: {
+        components: []
+      }
+    }),
+    cases = [
+      {
+        queueName: null,
+        title: 'queueName not set'
+      },
+      {
+        queueName: ' \n',
+        title: 'empty queueName value'
+      },
+      {
+        queueName: ' queue\n',
+        queue: 'queue',
+        title: 'queueName set correctly'
+      }
+    ];
+
+  Em.run(function () {
+    controller.sendAppDataToServer();
+  });
+
+  ok(controller.get('isSubmitDisabled'), 'Finish button should be disabled');
+  ok(App.ajax.send.calledOnce, 'request to server should be sent');
+
+  cases.forEach(function (item) {
+
+    Em.run(function () {
+      controller.set('newApp.queueName', item.queueName);
+      controller.sendAppDataToServer();
+    });
+
+    var data = {
+      typeName: 'ACCUMULO',
+      typeVersion: '1',
+      name: 'name',
+      resources: {
+        components: []
+      },
+      typeConfigs: {
+        key: 'value'
+      }
+    };
+    if (item.queue) {
+      data.queue = item.queue;
+    }
+
+    propEqual(App.ajax.send.lastCall.args[0].data.data, data, item.title);
+
+  });
+
+});
+
+test('actions.finish', function () {
+
+  var controller = this.subject();
+
+  Em.run(function () {
+    sinon.stub(controller, 'sendAppDataToServer', Em.K);
+    controller.send('finish');
+  });
+
+  ok(controller.sendAppDataToServer.calledOnce, 'data should be sent to server');
+
+  controller.sendAppDataToServer.restore();
+
 });
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/ed616f1d/contrib/views/slider/src/main/resources/ui/test/unit/controllers/slider_app_controller_test.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/test/unit/controllers/slider_app_controller_test.js b/contrib/views/slider/src/main/resources/ui/test/unit/controllers/slider_app_controller_test.js
index 1dd8b70..b9d632e 100644
--- a/contrib/views/slider/src/main/resources/ui/test/unit/controllers/slider_app_controller_test.js
+++ b/contrib/views/slider/src/main/resources/ui/test/unit/controllers/slider_app_controller_test.js
@@ -20,7 +20,21 @@ moduleFor('controller:sliderApp', 'App.SliderAppController', {
 
   needs: [
     'component:bs-modal'
-  ]
+  ],
+
+  setup: function () {
+    sinon.stub(Bootstrap.ModalManager, 'register', Em.K);
+    sinon.stub(Bootstrap.ModalManager, 'open', Em.K);
+    sinon.stub(App.ajax, 'send', Em.K);
+    sinon.stub(Bootstrap.ModalManager, 'close', Em.K);
+  },
+
+  teardown: function () {
+    Bootstrap.ModalManager.register.restore();
+    Bootstrap.ModalManager.open.restore();
+    App.ajax.send.restore();
+    Bootstrap.ModalManager.close.restore();
+  }
 
 });
 
@@ -168,8 +182,7 @@ test('destroyButtonEnabled', function () {
 
 test('confirmDestroy', function () {
 
-  var bsRegister = Bootstrap.ModalManager.register,
-    controller = this.subject(),
+  var controller = this.subject(),
     assertionsEqual = [
       {
         propertyName: 'name',
@@ -187,11 +200,13 @@ test('confirmDestroy', function () {
     assertionsDeepEqual = [
       {
         propertyName: 'targetObject',
-        value: controller
+        value: controller,
+        valueFormatted: 'App.SliderAppController'
       },
       {
         propertyName: 'controller',
-        value: controller
+        value: controller,
+        valueFormatted: 'App.SliderAppController'
       },
       {
         propertyName: 'body',
@@ -205,21 +220,17 @@ test('confirmDestroy', function () {
     title = 'modalComponent.{0} should be {1}';
 
   Em.run(function () {
-    Bootstrap.set('ModalManager.register', function (name, component) {
-      this.registeredComponent = component;
-    });
     controller.confirmDestroy();
   });
 
+  ok(Bootstrap.ModalManager.register.calledOnce, 'Bootstrap.ModalManager.register should be executed');
   assertionsEqual.forEach(function (item) {
-    equal(Bootstrap.ModalManager.registeredComponent[item.propertyName], item.value, title.format(item.propertyName, item.value));
+    equal(Bootstrap.ModalManager.register.firstCall.args[1][item.propertyName], item.value, title.format(item.propertyName, item.value));
   });
   assertionsDeepEqual.forEach(function (item) {
-    deepEqual(Bootstrap.ModalManager.registeredComponent[item.propertyName], item.value, title.format(item.propertyName, item.value));
+    deepEqual(Bootstrap.ModalManager.register.firstCall.args[1][item.propertyName], item.value, title.format(item.propertyName, item.valueFormatted || item.value));
   });
 
-  Bootstrap.set('ModalManager.register', bsRegister);
-
 });
 
 test('tryDoAction', function () {
@@ -306,8 +317,7 @@ test('validateGroupedComponents', function () {
 
 test('flex', function () {
 
-  var bsOpen = Bootstrap.ModalManager.open,
-    controller = this.subject({
+  var controller = this.subject({
       appType: {
         components: [
           Em.Object.create({
@@ -326,15 +336,12 @@ test('flex', function () {
     });
 
   Em.run(function () {
-    Bootstrap.set('ModalManager.open', Em.K);
     controller.flex();
   });
 
   equal(controller.get('groupedComponents')[0].count, 1, 'count should be incremented');
   equal(controller.get('groupedComponents')[1].count, 0, 'count shouldn\'t be incremented');
 
-  Bootstrap.set('ModalManager.open', bsOpen);
-
 });
 
 test('mapComponentsForFlexRequest', function () {
@@ -401,8 +408,7 @@ test('actionErrorCallback', function () {
 
 test('actions.submitFlex', function () {
 
-  var bsClose = Bootstrap.ModalManager.close,
-    controller = this.subject({
+  var controller = this.subject({
       model: Em.Object.create({
         id: 0,
         name: 'n'
@@ -414,15 +420,12 @@ test('actions.submitFlex', function () {
     });
 
   Em.run(function () {
-    Bootstrap.set('ModalManager.close', Em.K);
     controller.send('submitFlex');
   });
 
   equal(controller.get('groupedComponents.length'), 0, 'should clear grouped components');
   ok(!controller.get('groupedComponentsHaveErrors'), 'should clear components errors');
 
-  Bootstrap.set('ModalManager.close', bsClose);
-
 });
 
 test('actions.closeFlex', function () {
@@ -443,8 +446,7 @@ test('actions.closeFlex', function () {
 
 test('modalConfirmed', function () {
 
-  var bsClose = Bootstrap.ModalManager.close,
-    controller = this.subject({
+  var controller = this.subject({
       confirmChecked: true,
       currentAction: 'customMethod',
       customMethod: function () {
@@ -453,39 +455,31 @@ test('modalConfirmed', function () {
     });
 
   Em.run(function () {
-    Bootstrap.set('ModalManager.close', Em.K);
     controller.send('modalConfirmed');
   });
 
   deepEqual(controller.get('groupedComponents'), [{}], 'currentAction should be executed');
   ok(!controller.get('confirmChecked'), 'confirmChecked should be false');
 
-  Bootstrap.set('ModalManager.close', bsClose);
-
 });
 
 test('modalCanceled', function () {
 
-  var bsClose = Bootstrap.ModalManager.close,
-    controller = this.subject({
+  var controller = this.subject({
       confirmChecked: true
     });
 
   Em.run(function () {
-    Bootstrap.set('ModalManager.close', Em.K);
     controller.send('modalCanceled');
   });
 
   ok(!controller.get('confirmChecked'), 'confirmChecked should be false');
 
-  Bootstrap.set('ModalManager.close', bsClose);
-
 });
 
 test('openModal', function () {
 
-  var bsOpen = Bootstrap.ModalManager.open,
-    cases = [
+  var cases = [
       {
         options: {
           action: 'customMethod'
@@ -512,7 +506,6 @@ test('openModal', function () {
     });
 
   Em.run(function () {
-    Bootstrap.set('ModalManager.open', Em.K);
     controller.send('openModal', {
       action: 'customMethod'
     });
@@ -530,8 +523,6 @@ test('openModal', function () {
 
   });
 
-  Bootstrap.set('ModalManager.open', bsOpen);
-
 });
 
 test('quickLinksOrdered', function() {

http://git-wip-us.apache.org/repos/asf/ambari/blob/ed616f1d/contrib/views/slider/src/main/resources/ui/test/unit/controllers/slider_apps_controller_test.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/test/unit/controllers/slider_apps_controller_test.js b/contrib/views/slider/src/main/resources/ui/test/unit/controllers/slider_apps_controller_test.js
index b79ff1a..3462d22 100644
--- a/contrib/views/slider/src/main/resources/ui/test/unit/controllers/slider_apps_controller_test.js
+++ b/contrib/views/slider/src/main/resources/ui/test/unit/controllers/slider_apps_controller_test.js
@@ -16,12 +16,21 @@
  * limitations under the License.
  */
 
-moduleFor('controller:sliderApps', 'App.SliderAppsController');
+moduleFor('controller:sliderApps', 'App.SliderAppsController', {
+
+  setup: function () {
+    sinon.stub(Bootstrap.ModalManager, 'open', Em.K);
+  },
+
+  teardown: function () {
+    Bootstrap.ModalManager.open.restore();
+  }
+
+});
 
 test('showUnavailableAppsPopup', function () {
 
-  var bsOpen = Bootstrap.ModalManager.open,
-    cases = [
+  var cases = [
       {
         message: 'message',
         result: 'message',
@@ -39,14 +48,11 @@ test('showUnavailableAppsPopup', function () {
   cases.forEach(function (item) {
 
     Em.run(function () {
-      Bootstrap.set('ModalManager.open', Em.K);
       controller.showUnavailableAppsPopup(item.message);
     });
 
     equal(controller.get('errorMessage'), item.result, item.title);
 
-    Bootstrap.set('ModalManager.open', bsOpen);
-
   });
 
 });

http://git-wip-us.apache.org/repos/asf/ambari/blob/ed616f1d/contrib/views/slider/src/main/resources/ui/test/unit/controllers/slider_controller_test.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/test/unit/controllers/slider_controller_test.js b/contrib/views/slider/src/main/resources/ui/test/unit/controllers/slider_controller_test.js
index 308f455..e31d266 100644
--- a/contrib/views/slider/src/main/resources/ui/test/unit/controllers/slider_controller_test.js
+++ b/contrib/views/slider/src/main/resources/ui/test/unit/controllers/slider_controller_test.js
@@ -72,49 +72,41 @@ test('getViewDisplayParametersSuccessCallback', function () {
 
 test('getParametersFromViewPropertiesSuccessCallback', function () {
 
-  var storeAll = App.SliderApp.store.all,
-    controller = this.subject();
+  var controller = this.subject();
 
   Em.run(function () {
-    App.SliderApp.store.all = function () {
-      return properties;
-    };
+    sinon.stub(App.SliderApp.store, 'all').returns(properties);
     controller.getParametersFromViewPropertiesSuccessCallback({
       parameters: {
         'site.global.metric_collector_lib': 'file:///usr/lib/ambari-metrics-hadoop-sink/ambari-metrics-hadoop-sink.jar',
         'site.global.metric_collector_host': 'h2',
-        'site.global.metric_collector_port': '6188',
+        'site.global.metric_collector_port': '6188'
       },
       validations: [{}, {}]
     });
+    App.SliderApp.store.all.restore();
   });
 
   equal(App.get('metricsHost'), 'h2', 'should set metrics server host');
   equal(App.get('metricsPort'), '6188', 'should set metrics server port');
   equal(App.get('metricsLibPath'), 'file:///usr/lib/ambari-metrics-hadoop-sink/ambari-metrics-hadoop-sink.jar', 'should set metrics lib path');
 
-  App.SliderApp.store.set('all', storeAll);
-
 });
 
  test('initMetricsServerProperties', function () {
 
-   var storeAll = App.SliderApp.store.all,
-     controller = this.subject();
+   var controller = this.subject();
 
    Em.run(function () {
-     App.SliderApp.store.all = function () {
-       return properties;
-     };
+     sinon.stub(App.SliderApp.store, 'all').returns(properties);
      controller.initMetricsServerProperties();
+     App.SliderApp.store.all.restore();
    });
 
    equal(App.get('metricsHost'), 'h2', 'should set metrics server host');
    equal(App.get('metricsPort'), '6188', 'should set metrics server port');
    equal(App.get('metricsLibPath'), 'file:///usr/lib/ambari-metrics-hadoop-sink/ambari-metrics-hadoop-sink.jar', 'should set metrics lib path');
 
-   App.SliderApp.store.set('all', storeAll);
-
  });
 
 test('finishSliderConfiguration', function () {