Browse Source

AMBARI-6000. Add unit tests for mirroring controllers/views. (akovalenko)

Aleksandr Kovalenko 11 years ago
parent
commit
65b11d50c1

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

@@ -76,6 +76,7 @@ require('test/controllers/main/jobs/hive_job_details_controller_test');
 require('test/controllers/main/service_test');
 require('test/controllers/main/admin_test');
 require('test/controllers/main/alerts_controller_test');
+require('test/controllers/main/mirroring/edit_dataset_controller_test');
 require('test/controllers/installer_test');
 require('test/controllers/wizard_test');
 require('test/controllers/wizard/step0_test');
@@ -158,6 +159,7 @@ require('test/views/main/jobs/hive_job_details_view_test');
 require('test/views/main/charts/heatmap/heatmap_host_test');
 require('test/views/main/charts/heatmap/heatmap_rack_test');
 require('test/views/main/service/info/config_test');
+require('test/views/main/mirroring/edit_dataset_view_test');
 require('test/views/common/configs/services_config_test');
 require('test/views/wizard/step3/hostLogPopupBody_view_test');
 require('test/views/wizard/step3/hostWarningPopupBody_view_test');

+ 119 - 40
ambari-web/app/controllers/main/mirroring/edit_dataset_controller.js

@@ -19,11 +19,22 @@
 App.MainMirroringEditDataSetController = Ember.Controller.extend({
   name: 'mainMirroringEditDataSetController',
 
+  /**
+   * Defines to show Edit Dataset or Create New Dataset popup
+   * @type {Boolean}
+   */
   isEdit: false,
 
+  /**
+   * Contains Dataset id if <code>isEdit</code> is true
+   * @type {Boolean}
+   */
   datasetIdToEdit: null,
 
-  // Fields values from Edit DataSet form
+  /**
+   * Fields values from Edit DataSet form
+   * @type {Object}
+   */
   formFields: Ember.Object.create({
     datasetName: null,
     datasetTargetClusterName: null,
@@ -41,7 +52,10 @@ App.MainMirroringEditDataSetController = Ember.Controller.extend({
     repeatOptionSelected: null
   }),
 
-  // Messages for errors occurred during Edit DataSet form validation
+  /**
+   * Messages for errors occurred during Edit DataSet form validation
+   * @type {Object}
+   */
   errorMessages: Ember.Object.create({
     name: '',
     sourceDir: '',
@@ -52,6 +66,10 @@ App.MainMirroringEditDataSetController = Ember.Controller.extend({
     targetClusterName: ''
   }),
 
+  /**
+   * Flags with errors related to each field in Edit/Create Dataset form
+   * @type {Object}
+   */
   errors: Ember.Object.create({
     isNameError: false,
     isSourceDirError: false,
@@ -62,6 +80,9 @@ App.MainMirroringEditDataSetController = Ember.Controller.extend({
     isTargetClusterNameError: false
   }),
 
+  /**
+   * Clear all fields in Edit/Create Dataset form and clears all errors
+   */
   clearStep: function () {
     var formFields = this.get('formFields');
     Em.keys(formFields).forEach(function (key) {
@@ -70,6 +91,9 @@ App.MainMirroringEditDataSetController = Ember.Controller.extend({
     this.clearErrors();
   },
 
+  /**
+   * Clear all error flags and messages
+   */
   clearErrors: function () {
     var errorMessages = this.get('errorMessages');
     Em.keys(errorMessages).forEach(function (key) {
@@ -81,17 +105,31 @@ App.MainMirroringEditDataSetController = Ember.Controller.extend({
     }, this);
   },
 
+  /**
+   * Show Create New Dataset popup
+   * @return {Object} popup view
+   */
   showAddPopup: function () {
-    this.showPopup(Em.I18n.t('mirroring.dataset.newDataset'));
+    var popup = this.showPopup(Em.I18n.t('mirroring.dataset.newDataset'));
     this.set('isEdit', false);
+    return popup;
   },
 
+  /**
+   * Show Edit Dataset popup
+   * @return {Object} popup view
+   */
   showEditPopup: function (dataset) {
     this.set('datasetIdToEdit', dataset.get('id'));
-    this.showPopup(Em.I18n.t('mirroring.dataset.editDataset'));
+    var popup = this.showPopup(Em.I18n.t('mirroring.dataset.editDataset'));
     this.set('isEdit', true);
+    return popup;
   },
 
+  /**
+   * Show popup with Dataset form fields
+   * @return {Object} popup view
+   */
   showPopup: function (header) {
     var self = this;
     var popup = App.ModalPopup.show({
@@ -129,9 +167,12 @@ App.MainMirroringEditDataSetController = Ember.Controller.extend({
       })
     });
     this.set('popup', popup);
+    return popup;
   },
 
-  // Set observer to call validate method if any property from formFields will change
+  /**
+   * Set observer to call validate method if any property from formFields will change
+   */
   applyValidation: function () {
     Em.keys(this.get('formFields')).forEach(function (key) {
       this.addObserver('formFields.' + key, this, 'validate');
@@ -139,7 +180,10 @@ App.MainMirroringEditDataSetController = Ember.Controller.extend({
     this.validate();
   },
 
-  // Return date object calculated from appropriate fields
+  /**
+   * Return date object calculated from appropriate fields
+   * @type {Date}
+   */
   scheduleStartDate: function () {
     var startDate = this.get('formFields.datasetStartDate');
     var hoursForStart = this.get('formFields.hoursForStart');
@@ -151,7 +195,10 @@ App.MainMirroringEditDataSetController = Ember.Controller.extend({
     return null;
   }.property('formFields.datasetStartDate', 'formFields.hoursForStart', 'formFields.minutesForStart', 'formFields.middayPeriodForStart'),
 
-  // Return date object calculated from appropriate fields
+  /**
+   * Return date object calculated from appropriate fields
+   * @type {Date}
+   */
   scheduleEndDate: function () {
     var endDate = this.get('formFields.datasetEndDate');
     var hoursForEnd = this.get('formFields.hoursForEnd');
@@ -164,7 +211,9 @@ App.MainMirroringEditDataSetController = Ember.Controller.extend({
   }.property('formFields.datasetEndDate', 'formFields.hoursForEnd', 'formFields.minutesForEnd', 'formFields.middayPeriodForEnd'),
 
 
-  // Validation for every field in Edit DataSet form
+  /**
+   * Validation for every field in Edit DataSet form
+   */
   validate: function () {
     var formFields = this.get('formFields');
     var errors = this.get('errors');
@@ -185,7 +234,7 @@ App.MainMirroringEditDataSetController = Ember.Controller.extend({
       errorMessages.set('endDate', Em.I18n.t('mirroring.dateOrder.error'));
     }
     // Check that startDate is after current date
-    if (!this.get('isEdit') && new Date(App.dateTime()) > scheduleStartDate) {
+    if (scheduleStartDate && !this.get('isEdit') && new Date(App.dateTime()) > scheduleStartDate) {
       errors.set('isStartDateError', true);
       errorMessages.set('startDate', Em.I18n.t('mirroring.startDate.error'));
     }
@@ -196,25 +245,50 @@ App.MainMirroringEditDataSetController = Ember.Controller.extend({
     }
   },
 
-  // Add '0' for numbers less than 10
+
+  /**
+   * Add '0' for numbers less than 10
+   * @param {Number|String} number
+   * @return {String}
+   */
   addZero: function (number) {
     return ('0' + number).slice(-2);
   },
 
-  // Convert date to TZ format
+  /**
+   * Convert date to TZ format
+   * @param {Date} date
+   * @return {String}
+   */
   toTZFormat: function (date) {
     return date.toISOString().replace(/\:\d{2}\.\d{3}/,'');
   },
 
-  // Converts hours value from 24-hours format to AM/PM format
+  /**
+   * Converts hours value from 24-hours format to AM/PM format
+   * @param {Number|String} hours
+   * @return {String}
+   */
   toAMPMHours: function (hours) {
     var result = hours % 12;
     result = result ? result : 12;
     return this.addZero(result);
   },
 
+  /**
+   * Save data from dataset form to server
+   */
   save: function () {
     this.set('popup.isSaving', true);
+    var datasetXML = this.createDatasetXML();
+    this.sendDatasetToServer(datasetXML);
+  },
+
+  /**
+   * Compose XML-object from populated dataset form fields
+   * @return {String}
+   */
+  createDatasetXML: function () {
     var datasetNamePrefix = App.mirroringDatasetNamePrefix;
     var datasetName = this.get('formFields.datasetName');
     var prefixedDatasetName = datasetNamePrefix + datasetName;
@@ -229,46 +303,47 @@ App.MainMirroringEditDataSetController = Ember.Controller.extend({
     var scheduleStartDateFormatted = this.toTZFormat(startDate);
     var scheduleEndDateFormatted = this.toTZFormat(endDate);
 
-    // Compose XML data, that will be sended to server
-    var dataToSend = '<?xml version="1.0"?><feed description="" name="' + prefixedDatasetName + '" xmlns="uri:falcon:feed:0.1"><frequency>' + repeatOptionSelected + '(' + datasetFrequency + ')' +
+    return '<?xml version="1.0"?><feed description="" name="' + prefixedDatasetName + '" xmlns="uri:falcon:feed:0.1"><frequency>' + repeatOptionSelected + '(' + datasetFrequency + ')' +
         '</frequency><clusters><cluster name="' + sourceCluster + '" type="source"><validity start="' + scheduleStartDateFormatted + '" end="' + scheduleEndDateFormatted +
         '"/><retention limit="days(7)" action="delete"/></cluster><cluster name="' + targetCluster + '" type="target"><validity start="' + scheduleStartDateFormatted + '" end="' + scheduleEndDateFormatted +
         '"/><retention limit="months(1)" action="delete"/><locations><location type="data" path="' + targetDir + '" /></locations></cluster></clusters><locations><location type="data" path="' +
         sourceDir + '" /></locations><ACL owner="hue" group="users" permission="0755" /><schema location="/none" provider="none"/></feed>';
-    if (this.get('isEdit')) {
-      App.ajax.send({
-        name: 'mirroring.update_entity',
-        sender: this,
-        data: {
-          name: prefixedDatasetName,
-          type: 'feed',
-          entity: dataToSend,
-          falconServer: App.get('falconServerURL')
-        },
-        success: 'onSaveSuccess',
-        error: 'onSaveError'
-      });
-    } else {
-      // Send request to server to create dataset
-      App.ajax.send({
-        name: 'mirroring.create_new_dataset',
-        sender: this,
-        data: {
-          dataset: dataToSend,
-          falconServer: App.get('falconServerURL')
-        },
-        success: 'onSaveSuccess',
-        error: 'onSaveError'
-      });
-    }
   },
 
+  /**
+   * Send dataset XML-data to server
+   * @param {String} datasetXML
+   */
+  sendDatasetToServer: function (datasetXML) {
+    var datasetNamePrefix = App.mirroringDatasetNamePrefix;
+    var datasetName = this.get('formFields.datasetName');
+    var prefixedDatasetName = datasetNamePrefix + datasetName;
+    return App.ajax.send({
+      name: this.get('isEdit') ? 'mirroring.update_entity' : 'mirroring.create_new_dataset',
+      sender: this,
+      data: {
+        name: prefixedDatasetName,
+        type: 'feed',
+        entity: datasetXML,
+        falconServer: App.get('falconServerURL')
+      },
+      success: 'onSaveSuccess',
+      error: 'onSaveError'
+    });
+  },
+
+  /**
+   * Callback for success saving XML-data on server
+   */
   onSaveSuccess: function () {
     this.set('popup.isSaving', false);
     this.get('popup').hide();
     App.router.get('mainMirroringController').loadData();
   },
 
+  /**
+   * Callback for error while saving XML-data on server
+   */
   onSaveError: function (response) {
     this.set('popup.isSaving', false);
     if (response && response.responseText) {
@@ -279,6 +354,10 @@ App.MainMirroringEditDataSetController = Ember.Controller.extend({
     }
   },
 
+  /**
+   * Defines if save button should be disabled
+   * @type {Boolean}
+   */
   saveDisabled: function () {
     var errors = this.get('errors');
     return errors.get('isNameError') || errors.get('isSourceDirError') || errors.get('isTargetDirError') || errors.get('isStartDateError') || errors.get('isEndDateError') || errors.get('isFrequencyError') || errors.get('isTargetClusterNameError');

+ 0 - 5
ambari-web/app/controllers/main/mirroring/manage_clusters_controller.js

@@ -64,11 +64,6 @@ App.MainMirroringManageClustersController = Em.ArrayController.extend({
 
   selectedCluster: null,
 
-  // Disable input fields for already created clusters
-  isEditDisabled: function () {
-    return !this.get('clustersToCreate').mapProperty('name').contains(this.get('selectedCluster.name'));
-  }.property('selectedCluster.name', 'clustersToCreate.@each.name'),
-
   addCluster: function () {
     var self = this;
     var newClusterPopup = App.ModalPopup.show({

+ 1 - 1
ambari-web/app/utils/ajax/ajax.js

@@ -1841,7 +1841,7 @@ var urls = {
       return {
         contentType: 'text/xml',
         dataType: 'xml',
-        data: data.dataset,
+        data: data.entity,
         headers: {
           'AmbariProxy-Content-Type': 'text/xml'
         }

+ 13 - 4
ambari-web/app/views/main/mirroring/edit_dataset_view.js

@@ -17,14 +17,17 @@
  */
 
 var App = require('app');
-var filters = require('views/common/filter_view');
-var sort = require('views/common/sort_view');
-var date = require('utils/date');
 
 App.MainMirroringEditDataSetView = Em.View.extend({
+
   name: 'mainMirroringEditDataSetView',
+
   templateName: require('templates/main/mirroring/edit_dataset'),
 
+  /**
+   * Defines if there are some target clusters defined
+   * @type {Boolean}
+   */
   hasTargetClusters: false,
 
   targetClusters: App.TargetCluster.find(),
@@ -46,6 +49,9 @@ App.MainMirroringEditDataSetView = Em.View.extend({
     }
   }),
 
+  /**
+   * Set <code>hasTargetClusters</code> after clustes load
+   */
   onTargetClustersChange: function () {
     if (this.get('isLoaded') && this.get('targetClusters.length') > 1) {
       this.set('hasTargetClusters', true);
@@ -71,9 +77,12 @@ App.MainMirroringEditDataSetView = Em.View.extend({
     App.router.get('mainMirroringController').manageClusters();
   },
 
+  /**
+   * Fill form input fields for selected dataset to edit
+   */
   fillForm: function () {
     var isEdit = this.get('controller.isEdit');
-    if (this.get('isLoaded')  && isEdit) {
+    if (this.get('isLoaded') && isEdit) {
       var controller = this.get('controller');
       var dataset = App.Dataset.find().findProperty('id', controller.get('datasetIdToEdit'));
       var scheduleStartDate = new Date(dataset.get('scheduleStartDate'));

+ 420 - 0
ambari-web/test/controllers/main/mirroring/edit_dataset_controller_test.js

@@ -0,0 +1,420 @@
+/**
+ * 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('controllers/main/mirroring/edit_dataset_controller');
+require('models/target_cluster');
+require('views/main/mirroring/edit_dataset_view');
+
+describe('App.MainMirroringEditDataSetController', function () {
+
+  describe('#clearStep', function () {
+    it('should clear all fields, error flags and messages', function () {
+      var mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create();
+      var errors = mainMirroringEditDataSetController.get('errors');
+      var errorMessages = mainMirroringEditDataSetController.get('errorMessages');
+      var formFeilds = mainMirroringEditDataSetController.get('formFields');
+      formFeilds.set('datasetName', 'test');
+      sinon.spy(mainMirroringEditDataSetController, 'clearErrors');
+      mainMirroringEditDataSetController.clearStep();
+      expect(mainMirroringEditDataSetController.clearErrors.calledOnce).to.be.true;
+      Em.keys(formFeilds).forEach(function (field) {
+        expect(formFeilds[field]).to.be.null;
+      });
+      mainMirroringEditDataSetController.clearErrors.restore();
+    });
+  });
+
+  describe('#clearErrors', function () {
+    it('should clear all error messages and flags', function () {
+      var mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create();
+      var errors = mainMirroringEditDataSetController.get('errors');
+      var errorMessages = mainMirroringEditDataSetController.get('errorMessages');
+      Em.keys(errors).forEach(function (error) {
+        errors[error] = true;
+      }, this);
+      Em.keys(errorMessages).forEach(function (errorMessage) {
+        errorMessages[errorMessage] = 'test';
+      }, this);
+      mainMirroringEditDataSetController.clearErrors();
+      Em.keys(errors).forEach(function (error) {
+        expect(errors[error]).to.be.false;
+      });
+      Em.keys(errorMessages).forEach(function (errorMessage) {
+        expect(errorMessages[errorMessage]).to.be.empty;
+      });
+    });
+  });
+
+  describe('#showAddPopup', function () {
+    it('should show popup and set isEdit as false', function () {
+      var mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create();
+      sinon.spy(App.ModalPopup, 'show');
+      mainMirroringEditDataSetController.showAddPopup();
+      expect(App.ModalPopup.show.calledOnce).to.be.true;
+      expect(mainMirroringEditDataSetController.get('isEdit')).to.be.false;
+      App.ModalPopup.show.restore();
+    });
+  });
+
+  describe('#showEditPopup', function () {
+    it('should show popup,set isEdit as true and set dataset id', function () {
+      var mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create();
+      var dataset = Ember.Object.create({
+        id: 'test'
+      });
+      sinon.spy(App.ModalPopup, 'show');
+      mainMirroringEditDataSetController.showEditPopup(dataset);
+      expect(App.ModalPopup.show.calledOnce).to.equal(true);
+      expect(mainMirroringEditDataSetController.get('isEdit')).to.be.true;
+      expect(mainMirroringEditDataSetController.get('datasetIdToEdit')).to.equal('test');
+      App.ModalPopup.show.restore();
+    });
+  });
+
+  describe('#showPopup', function () {
+    it('should show dataset popup and save its view', function () {
+      var mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create();
+      sinon.spy(App.ModalPopup, 'show');
+      mainMirroringEditDataSetController.showPopup();
+      expect(App.ModalPopup.show.calledOnce).to.equal(true);
+      expect(mainMirroringEditDataSetController.get('popup')).to.not.be.empty;
+      App.ModalPopup.show.restore();
+    });
+  });
+
+  describe('#applyValidation', function () {
+    it('should add observers to all fields to validate form', function () {
+      var mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create();
+      sinon.spy(mainMirroringEditDataSetController, 'validate');
+      mainMirroringEditDataSetController.applyValidation();
+      expect(mainMirroringEditDataSetController.validate.calledOnce).to.be.true;
+      Em.keys(mainMirroringEditDataSetController.get('formFields')).forEach(function (field) {
+        expect(mainMirroringEditDataSetController.hasObserverFor('formFields.' + field)).to.be.true;
+      });
+      mainMirroringEditDataSetController.validate.restore();
+    });
+  });
+
+  var testCases = [
+    {
+      day: '01/01/2001',
+      hours: '00',
+      minutes: '00',
+      middayPeriod: 'AM',
+      result: new Date('01/01/2001 00:00 AM'),
+      message: 'should return date object'
+    },
+    {
+      day: '06/05/2014',
+      hours: '12',
+      minutes: '59',
+      middayPeriod: 'PM',
+      result: new Date('06/05/2014 12:59 PM'),
+      message: 'should return date object'
+    },
+    {
+      day: '',
+      hours: '00',
+      minutes: '00',
+      middayPeriod: 'AM',
+      result: null,
+      message: 'should return null if there are empty fields'
+    }
+  ];
+
+  describe('#scheduleStartDate', function () {
+    testCases.forEach(function (test) {
+      it(test.message, function () {
+        var mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create({
+          formFields: Ember.Object.create({
+            datasetStartDate: test.day,
+            hoursForStart: test.hours,
+            minutesForStart: test.minutes,
+            middayPeriodForStart: test.middayPeriod
+          })
+        });
+        expect(mainMirroringEditDataSetController.get('scheduleStartDate')).to.deep.equal(test.result);
+      });
+    });
+  });
+
+  describe('#scheduleEndDate', function () {
+    testCases.forEach(function (test) {
+      it(test.message, function () {
+        var mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create({
+          formFields: Ember.Object.create({
+            datasetEndDate: test.day,
+            hoursForEnd: test.hours,
+            minutesForEnd: test.minutes,
+            middayPeriodForEnd: test.middayPeriod
+          })
+        });
+        expect(mainMirroringEditDataSetController.get('scheduleEndDate')).to.deep.equal(test.result);
+      });
+    });
+  });
+
+  var formFields = Ember.Object.create({
+    datasetName: 'test',
+    datasetTargetClusterName: 'test',
+    datasetSourceDir: '/test',
+    datasetTargetDir: '/test',
+    datasetStartDate: '01/19/2038',
+    hoursForStart: '03',
+    minutesForStart: '15',
+    middayPeriodForStart: 'AM',
+    datasetEndDate: '01/19/2039',
+    hoursForEnd: '03',
+    minutesForEnd: '15',
+    middayPeriodForEnd: 'AM',
+    datasetFrequency: '1',
+    repeatOptionSelected: 'days'
+  })
+
+  describe('#validate', function () {
+    it('should set an error for empty fields', function () {
+      var mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create();
+      mainMirroringEditDataSetController.validate();
+      var errors = mainMirroringEditDataSetController.get('errors');
+      var errorMessages = mainMirroringEditDataSetController.get('errorMessages');
+      Em.keys(errors).forEach(function (error) {
+        expect(errors[error]).to.be.true;
+      });
+      Em.keys(errorMessages).forEach(function (errorMessage) {
+        expect(errorMessages[errorMessage]).to.equal(Em.I18n.t('mirroring.required.error'));
+      });
+    });
+    it('should set an error if start date is after end date', function () {
+      var mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create();
+      var formFields = mainMirroringEditDataSetController.get('formFields');
+      formFields.set('datasetStartDate', '04/07/2014');
+      formFields.set('hoursForStart', '11');
+      formFields.set('minutesForStart', '00');
+      formFields.set('middayPeriodForStart', 'PM');
+      formFields.set('datasetEndDate', '04/07/2014');
+      formFields.set('hoursForEnd', '11');
+      formFields.set('minutesForEnd', '00');
+      formFields.set('middayPeriodForEnd', 'AM');
+      mainMirroringEditDataSetController.validate();
+      expect(mainMirroringEditDataSetController.get('errors.isEndDateError')).to.be.true;
+      expect(mainMirroringEditDataSetController.get('errorMessages.endDate')).to.equal(Em.I18n.t('mirroring.dateOrder.error'));
+    });
+    it('should set an error if start date is in the past', function () {
+      var mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create();
+      var formFields = mainMirroringEditDataSetController.get('formFields');
+      formFields.set('datasetStartDate', '04/07/2014');
+      formFields.set('hoursForStart', '11');
+      formFields.set('minutesForStart', '00');
+      formFields.set('middayPeriodForStart', 'AM');
+      mainMirroringEditDataSetController.validate();
+      expect(mainMirroringEditDataSetController.get('errors.isStartDateError')).to.be.true;
+      expect(mainMirroringEditDataSetController.get('errorMessages.startDate')).to.equal(Em.I18n.t('mirroring.startDate.error'));
+    });
+    it('should set an error if repeat field value consist not only form digits', function () {
+      var mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create();
+      var formFields = mainMirroringEditDataSetController.get('formFields');
+      formFields.set('datasetFrequency', 'test');
+      mainMirroringEditDataSetController.validate();
+      expect(mainMirroringEditDataSetController.get('errors.isFrequencyError')).to.be.true;
+      expect(mainMirroringEditDataSetController.get('errorMessages.frequency')).to.equal(Em.I18n.t('mirroring.required.invalidNumberError'));
+      formFields.set('datasetFrequency', '100test');
+      mainMirroringEditDataSetController.validate();
+      expect(mainMirroringEditDataSetController.get('errors.isFrequencyError')).to.be.true;
+      expect(mainMirroringEditDataSetController.get('errorMessages.frequency')).to.equal(Em.I18n.t('mirroring.required.invalidNumberError'));
+    });
+    it('should not set errors if all fields are filled correctly', function () {
+      var mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create({
+        formFields: formFields
+      });
+      mainMirroringEditDataSetController.validate();
+      var errors = mainMirroringEditDataSetController.get('errors');
+      var errorMessages = mainMirroringEditDataSetController.get('errorMessages');
+      Em.keys(errors).forEach(function (error) {
+        expect(errors[error]).to.be.false;
+      });
+      Em.keys(errorMessages).forEach(function (errorMessage) {
+        expect(errorMessages[errorMessage]).to.be.empty;
+      });
+    });
+  });
+
+  describe('#addZero', function () {
+    it('should add 0 for numbers less than 10', function () {
+      var mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create();
+      expect(mainMirroringEditDataSetController.addZero(1)).to.equal('01');
+      expect(mainMirroringEditDataSetController.addZero(9)).to.equal('09');
+      expect(mainMirroringEditDataSetController.addZero(0)).to.equal('00');
+    });
+    it('should not add 0 for numbers greater than 9', function () {
+      var mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create();
+      expect(mainMirroringEditDataSetController.addZero(10)).to.equal('10');
+      expect(mainMirroringEditDataSetController.addZero(99)).to.equal('99');
+    });
+  });
+
+  describe('#toTZFormat', function () {
+    it('should convert date to TZ format', function () {
+      var mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create();
+      expect(mainMirroringEditDataSetController.toTZFormat(new Date(Date.UTC(2014, 0, 1, 1, 1)))).to.equal('2014-01-01T01:01Z');
+      expect(mainMirroringEditDataSetController.toTZFormat(new Date(Date.UTC(2014, 11, 31, 23, 59)))).to.equal('2014-12-31T23:59Z');
+    });
+  });
+
+  describe('#toAMPMHours', function () {
+    it('should convert time to 12-hours format', function () {
+      var mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create();
+      expect(mainMirroringEditDataSetController.toAMPMHours(13)).to.equal('01');
+      expect(mainMirroringEditDataSetController.toAMPMHours(20)).to.equal('08');
+      expect(mainMirroringEditDataSetController.toAMPMHours(24)).to.equal('12');
+      expect(mainMirroringEditDataSetController.toAMPMHours(0)).to.equal('12');
+    });
+    it('should not convert time if argument is less than 12', function () {
+      var mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create();
+      expect(mainMirroringEditDataSetController.toAMPMHours(1)).to.equal('01');
+      expect(mainMirroringEditDataSetController.toAMPMHours(8)).to.equal('08');
+      expect(mainMirroringEditDataSetController.toAMPMHours(11)).to.equal('11');
+    });
+  });
+
+  describe('#save', function () {
+    it('should create XML and send it to server', function () {
+      var mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create({
+        popup: Ember.Object.create({
+          isSaving: false
+        })
+      });
+      sinon.spy(mainMirroringEditDataSetController, 'createDatasetXML');
+      sinon.spy(mainMirroringEditDataSetController, 'sendDatasetToServer');
+      mainMirroringEditDataSetController.save();
+      expect(mainMirroringEditDataSetController.createDatasetXML.calledOnce).to.be.true;
+      expect(mainMirroringEditDataSetController.sendDatasetToServer.calledOnce).to.be.true;
+      expect(mainMirroringEditDataSetController.get('popup.isSaving')).to.be.true;
+      mainMirroringEditDataSetController.createDatasetXML.restore();
+      mainMirroringEditDataSetController.sendDatasetToServer.restore();
+    });
+  });
+
+  describe('#createDatasetXML', function () {
+    it('should create XML-fromatted data', function () {
+      var mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create({
+        formFields: formFields
+      });
+      var startDate = new Date('01/19/2038 03:15 AM').toISOString().replace(/\:\d{2}\.\d{3}/, '');
+      var endDate = new Date('01/19/2039 03:15 AM').toISOString().replace(/\:\d{2}\.\d{3}/, '');
+      var expectedResult = '<?xml version="1.0"?><feed description="" name="' + App.mirroringDatasetNamePrefix + 'test" xmlns="uri:falcon:feed:0.1"><frequency>days(1)' +
+          '</frequency><clusters><cluster name="' + App.get('clusterName') + '" type="source"><validity start="' + startDate + '" end="' + endDate +
+          '"/><retention limit="days(7)" action="delete"/></cluster><cluster name="test" type="target"><validity start="' + startDate + '" end="' + endDate +
+          '"/><retention limit="months(1)" action="delete"/><locations><location type="data" path="/test" /></locations></cluster></clusters><locations><location type="data" path="' +
+          '/test" /></locations><ACL owner="hue" group="users" permission="0755" /><schema location="/none" provider="none"/></feed>';
+      var result = mainMirroringEditDataSetController.createDatasetXML();
+      expect(result).to.equal(expectedResult);
+    });
+  });
+
+  describe('#sendDatasetToServer', function () {
+    var mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create({
+      formFields: formFields
+    });
+    beforeEach(function () {
+      sinon.stub(App.ajax, 'send', Em.K);
+    });
+    afterEach(function () {
+      App.ajax.send.restore();
+    });
+    it('should send data with correct dataset name', function () {
+      mainMirroringEditDataSetController.sendDatasetToServer('test');
+      expect(App.ajax.send.args[0][0].data.name).to.equal(App.mirroringDatasetNamePrefix + formFields.datasetName);
+    });
+    it('should send data from param', function () {
+      mainMirroringEditDataSetController.sendDatasetToServer('test');
+      expect(App.ajax.send.args[0][0].data.entity).to.equal('test');
+    });
+    it('should use edit request if isEdit is true', function () {
+      mainMirroringEditDataSetController.set('isEdit', true);
+      mainMirroringEditDataSetController.sendDatasetToServer('test');
+      expect(App.ajax.send.args[0][0].name).to.equal('mirroring.update_entity');
+    });
+    it('should use create request if isEdit is false', function () {
+      mainMirroringEditDataSetController.set('isEdit', false);
+      mainMirroringEditDataSetController.sendDatasetToServer('test');
+      expect(App.ajax.send.args[0][0].name).to.equal('mirroring.create_new_dataset');
+    });
+  });
+
+  describe('#onSaveSuccess', function () {
+    it('should hide popup and load data', function () {
+      var mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create({
+        popup: Ember.Object.create({
+          isSaving: true,
+          hide: function () {
+          }
+        })
+      });
+      App.router.set('mainMirroringController', Ember.Object.create({
+        loadData: function () {
+        }
+      }));
+      sinon.spy(mainMirroringEditDataSetController.get('popup'), 'hide');
+      sinon.spy(App.router.get('mainMirroringController'), 'loadData');
+      mainMirroringEditDataSetController.onSaveSuccess();
+      expect(mainMirroringEditDataSetController.get('popup.isSaving')).to.be.false;
+      expect(App.router.get('mainMirroringController').loadData.calledOnce).to.be.true;
+      expect(mainMirroringEditDataSetController.get('popup').hide.calledOnce).to.be.true;
+      mainMirroringEditDataSetController.get('popup').hide.restore();
+      App.router.get('mainMirroringController').loadData.restore();
+    });
+  });
+
+  describe('#onSaveError', function () {
+    var mainMirroringEditDataSetController;
+    beforeEach(function () {
+      mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create({
+        popup: Ember.Object.create({
+          isSaving: true
+        })
+      });
+      sinon.stub(App, 'showAlertPopup', Em.K);
+    });
+    afterEach(function () {
+      App.showAlertPopup.restore();
+    });
+    it('shouldn\'t show error popup and enable button', function () {
+      mainMirroringEditDataSetController.onSaveError(null);
+      expect(App.showAlertPopup.calledOnce).to.be.false;
+      expect(mainMirroringEditDataSetController.get('popup.isSaving')).to.be.false;
+    });
+    it('should show error popup and enable button', function () {
+      mainMirroringEditDataSetController.onSaveError({responseText: '<message>test</message>'});
+      expect(App.showAlertPopup.args[0][1]).to.be.equal(Em.I18n.t('mirroring.manageClusters.error') + ': test');
+      expect(mainMirroringEditDataSetController.get('popup.isSaving')).to.be.false;
+    });
+  });
+
+  describe('#saveDisabled', function () {
+    it('should return false if there are no errors', function () {
+      var mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create({});
+      expect(mainMirroringEditDataSetController.get('saveDisabled')).to.be.false;
+    });
+    it('should return true if there are some errors', function () {
+      var mainMirroringEditDataSetController = App.MainMirroringEditDataSetController.create();
+      mainMirroringEditDataSetController.set('errors.isNameError', true);
+      expect(mainMirroringEditDataSetController.get('saveDisabled')).to.be.true;
+    });
+  });
+});

+ 243 - 0
ambari-web/test/views/main/mirroring/edit_dataset_view_test.js

@@ -0,0 +1,243 @@
+/**
+ * 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('controllers/main/mirroring/edit_dataset_controller');
+require('models/target_cluster');
+require('views/main/mirroring/edit_dataset_view');
+
+var mainMirroringEditDataSetView;
+describe('App.MainMirroringEditDataSetView', function () {
+
+  beforeEach(function () {
+    mainMirroringEditDataSetView = App.MainMirroringEditDataSetView.create({
+      controller: App.MainMirroringEditDataSetController.create(),
+      isLoaded: true
+    });
+  });
+
+  describe('targetClusterSelect.content', function () {
+    var targetClusterSelect;
+    beforeEach(function () {
+      targetClusterSelect = mainMirroringEditDataSetView.get('targetClusterSelect').create({
+        parentView: mainMirroringEditDataSetView
+      });
+    });
+
+    it('should be empty if data is not loaded', function () {
+      targetClusterSelect.set('parentView.isLoaded', false);
+      expect(targetClusterSelect.get('content')).to.be.empty;
+    });
+    it('should contain list of clusters if data is loaded', function () {
+      targetClusterSelect.set('parentView.isLoaded', true);
+      targetClusterSelect.set('parentView.targetClusters', [
+        {name: 'test1'},
+        {name: 'test2'},
+        {name: App.get('clusterName')}
+      ]);
+      expect(targetClusterSelect.get('content')).to.eql([
+        'test1',
+        'test2',
+        Em.I18n.t('mirroring.dataset.addTargetCluster')
+      ]);
+    });
+  });
+
+  describe('targetClusterSelect.change', function () {
+    var targetClusterSelect;
+    beforeEach(function () {
+      targetClusterSelect = mainMirroringEditDataSetView.get('targetClusterSelect').create({
+        parentView: mainMirroringEditDataSetView,
+        content: ['test1', 'test2', 'test3']
+      });
+      sinon.stub(targetClusterSelect.parentView, 'manageClusters', Em.K);
+    });
+
+    afterEach(function () {
+      targetClusterSelect.parentView.manageClusters.restore();
+    });
+
+    it('should open manage cluster popup if appropriate option was selected', function () {
+      targetClusterSelect.set('selection', Em.I18n.t('mirroring.dataset.addTargetCluster'));
+      targetClusterSelect.change();
+      expect(targetClusterSelect.get('selection')).to.equal('test1');
+      expect(targetClusterSelect.parentView.manageClusters.calledOnce).to.be.true;
+      expect(targetClusterSelect.get('parentView.controller.formFields.datasetTargetClusterName')).to.equal('test1');
+    });
+    it('should not open manage cluster popup if appropriate option was not selected', function () {
+      targetClusterSelect.set('selection', 'test3');
+      targetClusterSelect.change();
+      expect(targetClusterSelect.get('selection')).to.equal('test3');
+      expect(targetClusterSelect.parentView.manageClusters.calledOnce).to.be.false;
+      expect(targetClusterSelect.get('parentView.controller.formFields.datasetTargetClusterName')).to.equal('test3');
+    });
+  });
+
+  describe('onTargetClustersChange', function () {
+
+    var testCases = [
+      {
+        isLoaded: true,
+        targetClusters: [1, 2, 3],
+        targetClusterName: 'test',
+        hasTargetClusters: true
+      },
+      {
+        isLoaded: false,
+        targetClusters: [1, 2, 3],
+        targetClusterName: null,
+        hasTargetClusters: false
+      },
+      {
+        isLoaded: true,
+        targetClusters: [1],
+        targetClusterName: null,
+        hasTargetClusters: false
+      }
+    ];
+
+    testCases.forEach(function (test) {
+      it('should set hasTargetClusters property depending on cluster list', function () {
+        mainMirroringEditDataSetView.set('isLoaded', test.isLoaded);
+        mainMirroringEditDataSetView.set('targetClusters', test.targetClusters);
+        mainMirroringEditDataSetView.set('controller.formFields.datasetTargetClusterName', 'test');
+        mainMirroringEditDataSetView.onTargetClustersChange();
+        expect(mainMirroringEditDataSetView.get('hasTargetClusters')).to.equal(test.hasTargetClusters);
+        expect(mainMirroringEditDataSetView.get('controller.formFields.datasetTargetClusterName')).to.equal(test.targetClusterName);
+      });
+    });
+  });
+
+  describe('fillForm', function () {
+
+    App.store.loadMany(App.Dataset, [
+      {
+        id: 'test1',
+        name: 'test1',
+        target_cluster_name: 'testCluster1',
+        source_dir: '/testDir1',
+        target_dir: '/testDir1',
+        frequency: '5',
+        frequency_unit: 'days',
+        schedule_start_date: new Date('11/29/2014 01:00 AM').toISOString().replace(/\:\d{2}\.\d{3}/, ''),
+        schedule_end_date: new Date('11/29/2014 02:00 AM').toISOString().replace(/\:\d{2}\.\d{3}/, '')
+      },
+      {
+        id: 'test2',
+        name: 'test2',
+        target_cluster_name: 'testCluster2',
+        source_dir: '/testDir2',
+        target_dir: '/testDir2',
+        frequency: '10',
+        frequency_unit: 'hours',
+        schedule_start_date: new Date('11/20/2014 01:00 AM').toISOString().replace(/\:\d{2}\.\d{3}/, ''),
+        schedule_end_date: new Date('11/21/2014 02:00 PM').toISOString().replace(/\:\d{2}\.\d{3}/, '')
+      },
+      {
+        id: 'test3',
+        name: 'test3',
+        target_cluster_name: 'testCluster3',
+        source_dir: '/testDir3',
+        target_dir: '/testDir3',
+        frequency: '1',
+        frequency_unit: 'minutes',
+        schedule_start_date: new Date('10/29/2014 01:00 AM').toISOString().replace(/\:\d{2}\.\d{3}/, ''),
+        schedule_end_date: new Date('11/29/2015 02:00 AM').toISOString().replace(/\:\d{2}\.\d{3}/, '')
+      }
+    ]);
+
+    var testCases = [
+      {
+        datasetName: 'test1',
+        datasetSourceDir: '/testDir1',
+        datasetTargetDir: '/testDir1',
+        datasetTargetClusterName: 'testCluster1',
+        datasetFrequency: '5',
+        repeatOptionSelected: 'days',
+        datasetStartDate: '11/29/14',
+        datasetEndDate: '11/29/14',
+        hoursForStart: '01',
+        hoursForEnd: '02',
+        minutesForStart: '00',
+        minutesForEnd: '00',
+        middayPeriodForStart: 'AM',
+        middayPeriodForEnd: 'AM'
+      },
+      {
+        datasetName: 'test2',
+        datasetSourceDir: '/testDir2',
+        datasetTargetDir: '/testDir2',
+        datasetTargetClusterName: 'testCluster2',
+        datasetFrequency: '10',
+        repeatOptionSelected: 'hours',
+        datasetStartDate: '11/20/14',
+        datasetEndDate: '11/21/14',
+        hoursForStart: '01',
+        hoursForEnd: '02',
+        minutesForStart: '00',
+        minutesForEnd: '00',
+        middayPeriodForStart: 'AM',
+        middayPeriodForEnd: 'PM'
+      },
+      {
+        datasetName: 'test3',
+        datasetSourceDir: '/testDir3',
+        datasetTargetDir: '/testDir3',
+        datasetTargetClusterName: 'testCluster3',
+        datasetFrequency: '1',
+        repeatOptionSelected: 'minutes',
+        datasetStartDate: '10/29/14',
+        datasetEndDate: '11/29/15',
+        hoursForStart: '01',
+        hoursForEnd: '02',
+        minutesForStart: '00',
+        minutesForEnd: '00',
+        middayPeriodForStart: 'AM',
+        middayPeriodForEnd: 'AM'
+      }
+    ];
+
+    it('should not set form fields if isLoaded is false', function () {
+      mainMirroringEditDataSetView.set('isLoaded', false);
+      mainMirroringEditDataSetView.fillForm();
+      Em.keys(mainMirroringEditDataSetView.get('controller.formFields')).forEach(function (field) {
+        expect(mainMirroringEditDataSetView.get('controller.formFields.' + field)).to.be.null;
+      });
+    });
+
+    it('should not set form fields if controller.isEdit is false', function () {
+      mainMirroringEditDataSetView.set('controller.isEdit', false);
+      mainMirroringEditDataSetView.fillForm();
+      Em.keys(mainMirroringEditDataSetView.get('controller.formFields')).forEach(function (field) {
+        expect(mainMirroringEditDataSetView.get('controller.formFields.' + field)).to.be.null;
+      });
+    });
+
+    testCases.forEach(function (test) {
+      it('set appropriate form fields from dataset model', function () {
+        mainMirroringEditDataSetView.set('controller.datasetIdToEdit', test.datasetName);
+        mainMirroringEditDataSetView.set('controller.isEdit', true);
+        mainMirroringEditDataSetView.fillForm();
+        Em.keys(mainMirroringEditDataSetView.get('controller.formFields')).forEach(function (field) {
+          expect(mainMirroringEditDataSetView.get('controller.formFields.' + field)).to.equal(test[field]);
+        });
+      });
+    });
+  });
+});