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:18 UTC
[1/4] ambari git commit: AMBARI-10062. Ambari Web Client Makes
Request For All Alerts On Specific Alert Page (onechiporenko)
Repository: ambari
Updated Branches:
refs/heads/trunk 39157df9d -> ed616f1d5
AMBARI-10062. Ambari Web Client Makes Request For All Alerts On Specific Alert Page (onechiporenko)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/72b40b7b
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/72b40b7b
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/72b40b7b
Branch: refs/heads/trunk
Commit: 72b40b7b70daf5ad42e32aef4f94f08cddb35403
Parents: 39157df
Author: Oleg Nechiporenko <on...@apache.org>
Authored: Fri Mar 13 19:50:43 2015 +0200
Committer: Oleg Nechiporenko <on...@apache.org>
Committed: Mon Mar 16 10:58:43 2015 +0200
----------------------------------------------------------------------
.../app/controllers/global/update_controller.js | 12 +-
.../main/alert_definitions_controller.js | 161 ++---------------
.../main/alerts/alert_instances_controller.js | 181 +++++++++++++++++++
.../app/mixins/common/table_server_mixin.js | 6 +-
ambari-web/app/models/service.js | 4 +-
ambari-web/app/styles/application.less | 9 -
ambari-web/app/styles/common.less | 8 +
ambari-web/app/templates/application.hbs | 18 +-
.../common/modal_popups/alerts_popup.hbs | 5 +
.../main/alerts/definition_details.hbs | 2 +-
ambari-web/app/templates/main/host.hbs | 2 +-
ambari-web/app/utils/ajax/ajax.js | 14 +-
ambari-web/app/utils/ember_reopen.js | 13 ++
.../main/alerts/definition_details_view.js | 5 +-
ambari-web/app/views/main/host.js | 2 +-
.../main/alert_definitions_controller_test.js | 32 ----
.../alerts/alert_instances_controller_test.js | 33 ++++
17 files changed, 300 insertions(+), 207 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/72b40b7b/ambari-web/app/controllers/global/update_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/global/update_controller.js b/ambari-web/app/controllers/global/update_controller.js
index 41cbd10..d89e687 100644
--- a/ambari-web/app/controllers/global/update_controller.js
+++ b/ambari-web/app/controllers/global/update_controller.js
@@ -114,6 +114,15 @@ App.UpdateController = Em.Controller.extend({
}),
/**
+ * Pagination query-parameters for unhealthy alerts request
+ * @type {{from: Number, page_size: Number}}
+ */
+ queryParamsForUnhealthyAlertInstances: {
+ from: 0,
+ page_size: 10
+ },
+
+ /**
* map describes relations between updater function and table
*/
tableUpdaterMap: {
@@ -469,7 +478,8 @@ App.UpdateController = Em.Controller.extend({
updateUnhealthyAlertInstances: function (callback) {
var testUrl = '/data/alerts/alert_instances.json';
- var realUrl = '/alerts?fields=*&Alert/state.in(CRITICAL,WARNING)&Alert/maintenance_state.in(OFF)';
+ var queryParams = this.get('queryParamsForUnhealthyAlertInstances');
+ var realUrl = '/alerts?fields=*&Alert/state.in(CRITICAL,WARNING)&Alert/maintenance_state.in(OFF)&from=' + queryParams.from + '&page_size=' + queryParams.page_size;
var url = this.getUrl(testUrl, realUrl);
App.HttpClient.get(url, App.alertInstanceMapper, {
http://git-wip-us.apache.org/repos/asf/ambari/blob/72b40b7b/ambari-web/app/controllers/main/alert_definitions_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/alert_definitions_controller.js b/ambari-web/app/controllers/main/alert_definitions_controller.js
index f5086d1..8f7fcb8 100644
--- a/ambari-web/app/controllers/main/alert_definitions_controller.js
+++ b/ambari-web/app/controllers/main/alert_definitions_controller.js
@@ -37,14 +37,6 @@ App.MainAlertDefinitionsController = Em.ArrayController.extend({
/**
* List of all <code>App.AlertDefinition</code>
- * Consists of:
- * <ul>
- * <li>App.PortAlertDefinition</li>
- * <li>App.MetricsAlertDefinition</li>
- * <li>App.WebAlertDefinition</li>
- * <li>App.AggregateAlertDefinition</li>
- * <li>App.ScriptAlertDefinition</li>
- * </ul>
* @type {App.AlertDefinition[]}
*/
content: App.AlertDefinition.find(),
@@ -92,19 +84,23 @@ App.MainAlertDefinitionsController = Em.ArrayController.extend({
/**
* Calculate critical count for each service, to show up the label on services menu
* @method getCriticalAlertsCountForService
- * @return {number}
+ * @return {Number}
*/
getCriticalAlertsCountForService: function (service) {
- return this.get('criticalAlertInstances').filterProperty('service', service).get('length');
+ return this.get('content').filterProperty('service.serviceName', service).
+ invoke('getWithDefault', 'summary.CRITICAL.count', 0).
+ reduce(Em.sum, 0);
},
/**
* Calculate critical/warning count for each service, to show up the label on services menu
* @method getCriticalOrWarningAlertsCountForService
- * @return {number}
+ * @return {Number}
*/
getCriticalOrWarningAlertsCountForService: function (service) {
- return this.get('unhealthyAlertInstances').filterProperty('service', service).get('length');
+ return this.get('content').filterProperty('service.serviceName', service).map(function (alertDefinition) {
+ return alertDefinition.getWithDefault('summary.CRITICAL.count', 0) + alertDefinition.getWithDefault('summary.WARNING.count', 0);
+ }).reduce(Em.sum, 0);
},
/**
@@ -112,138 +108,21 @@ App.MainAlertDefinitionsController = Em.ArrayController.extend({
*/
/**
- * Alerts number to show up on top-nav bar: number of critical/warning alerts
- * @type {number}
+ * Number of all critical and warning alert instances
+ * Calculation is based on each <code>alertDefinitions.summary</code>
+ * @type {Number}
*/
- allAlertsCount: function () {
- return this.get('unhealthyAlertInstances.length');
- }.property('unhealthyAlertInstances.length'),
-
- criticalAlertInstances: function () {
- return App.AlertInstance.find().toArray().filterProperty('state', 'CRITICAL');
- }.property('mapperTimestamp'),
-
- warningAlertInstances: function () {
- return App.AlertInstance.find().toArray().filterProperty('state', 'WARNING');
- }.property('mapperTimestamp'),
-
- unhealthyAlertInstances: function () {
- return this.get('criticalAlertInstances').concat(this.get('warningAlertInstances'));
- }.property('criticalAlertInstances', 'warningAlertInstances'),
+ unhealthyAlertInstancesCount: function () {
+ return this.get('content').map(function (alertDefinition) {
+ return alertDefinition.getWithDefault('summary.CRITICAL.count', 0) + alertDefinition.getWithDefault('summary.WARNING.count', 0);
+ }).reduce(Em.sum, 0);
+ }.property('content.@each.summary'),
/**
- * if critical alerts exist, if true, the alert badge should be red.
+ * if critical alerts exist, the alert badge should be red.
+ * @type {Boolean}
*/
isCriticalAlerts: function () {
- return this.get('unhealthyAlertInstances').someProperty('state', 'CRITICAL');
- }.property('unhealthyAlertInstances.@each.state'),
-
- /**
- * Onclick handler for alerts number located right to bg ops number (see application.hbs)
- * @method showPopup
- * @return {App.ModalPopup}
- */
- showPopup: function () {
-
- var self = this;
-
- return App.ModalPopup.show({
-
- header: function () {
- return Em.I18n.t('alerts.fastAccess.popup.header').format(this.get('definitionsController.allAlertsCount'));
- }.property('definitionsController.allAlertsCount'),
-
- definitionsController: this,
-
- classNames: ['sixty-percent-width-modal', 'alerts-popup'],
-
- secondary: Em.I18n.t('alerts.fastAccess.popup.body.showmore'),
-
- autoHeight: false,
-
- isHideBodyScroll: true,
-
- onSecondary: function () {
- this.hide();
- App.router.transitionTo('main.alerts.index');
- },
-
- bodyClass: App.TableView.extend({
-
- templateName: require('templates/common/modal_popups/alerts_popup'),
-
- controller: self,
-
- isPaginate: true,
-
- content: function () {
- return this.get('controller.unhealthyAlertInstances');
- }.property('controller.unhealthyAlertInstances.length', 'controller.unhealthyAlertInstances.@each.state'),
-
- isLoaded: function () {
- return !!this.get('controller.unhealthyAlertInstances');
- }.property('controller.unhealthyAlertInstances'),
-
- isAlertEmptyList: function () {
- return !this.get('content.length');
- }.property('content.length'),
-
- /**
- * No filtering for alert definitions
- * @method filter
- */
- filter: function() {
- this.set('filteredContent', this.get('content'));
- }.observes('content.length'),
-
- refreshTooltips: function () {
- this.ensureTooltip();
- }.observes('contents.length'),
-
- ensureTooltip: function () {
- Em.run.next(this, function () {
- App.tooltip($(".timeago"));
- });
- },
- /**
- * Router transition to alert definition details page
- * @param event
- */
- gotoAlertDetails: function (event) {
- if (event && event.context) {
- this.get('parentView').hide();
- var definition = this.get('controller.content').findProperty('id', event.context.get('definitionId'));
- App.router.transitionTo('main.alerts.alertDetails', definition);
- }
- },
-
- /**
- * Router transition to service summary page
- * @param event
- */
- gotoService: function (event) {
- if (event && event.context) {
- this.get('parentView').hide();
- App.router.transitionTo('main.services.service.summary', event.context);
- }
- },
-
- /**
- * Router transition to host level alerts page
- * @param event
- */
- goToHostAlerts: function (event) {
- if (event && event.context) {
- this.get('parentView').hide();
- App.router.transitionTo('main.hosts.hostDetails.alerts', event.context);
- }
- },
-
- didInsertElement: function () {
- this.filter();
- this.ensureTooltip();
- }
- })
- });
- }
+ return !this.get('content').everyProperty('summary.CRITICAL.count', 0);
+ }.property('content.@each.summary')
});
http://git-wip-us.apache.org/repos/asf/ambari/blob/72b40b7b/ambari-web/app/controllers/main/alerts/alert_instances_controller.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/controllers/main/alerts/alert_instances_controller.js b/ambari-web/app/controllers/main/alerts/alert_instances_controller.js
index a3bf7fe..9bf91d5 100644
--- a/ambari-web/app/controllers/main/alerts/alert_instances_controller.js
+++ b/ambari-web/app/controllers/main/alerts/alert_instances_controller.js
@@ -22,6 +22,17 @@ App.MainAlertInstancesController = Em.Controller.extend({
name: 'mainAlertInstancesController',
+ content: App.AlertInstance.find(),
+
+ /**
+ * @type {App.AlertInstance[]}
+ */
+ unhealthyAlertInstances: function () {
+ return App.AlertInstance.find().filter(function (item) {
+ return ['CRITICAL', 'WARNING'].contains(item.get('state'));
+ });
+ }.property('content.[]'),
+
/**
* Are alertInstances loaded
* @type {boolean}
@@ -169,6 +180,176 @@ App.MainAlertInstancesController = Em.Controller.extend({
*/
getAlertInstancesErrorCallback: function () {
this.set('isLoaded', true);
+ },
+
+ /**
+ * Onclick handler for alerts number located right to bg ops number (see application.hbs)
+ * @method showPopup
+ * @return {App.ModalPopup}
+ */
+ showPopup: function () {
+
+ var self = this;
+
+ return App.ModalPopup.show({
+
+ alertsNumberBinding: 'App.router.mainAlertDefinitionsController.unhealthyAlertInstancesCount',
+
+ header: function () {
+ return Em.I18n.t('alerts.fastAccess.popup.header').format(this.get('alertsNumber'));
+ }.property('alertsNumber'),
+
+ definitionsController: this,
+
+ classNames: ['sixty-percent-width-modal', 'alerts-popup'],
+
+ secondary: Em.I18n.t('alerts.fastAccess.popup.body.showmore'),
+
+ autoHeight: false,
+
+ isHideBodyScroll: true,
+
+ onSecondary: function () {
+ this._super();
+ App.router.transitionTo('main.alerts.index');
+ },
+
+ bodyClass: App.TableView.extend(App.TableServerViewMixin, {
+
+ updaterBinding: 'App.router.updateController',
+
+ templateName: require('templates/common/modal_popups/alerts_popup'),
+
+ controller: self,
+
+ isPaginate: true,
+
+ willInsertElement: function () {
+ this._super();
+ this.updateAlertInstances();
+ },
+
+ /**
+ * Number of all critical and warning alert instances
+ * @type {Boolean}
+ */
+ filteredCount: function () {
+ return App.router.get('mainAlertDefinitionsController.unhealthyAlertInstancesCount');
+ }.property('alertsNumber'),
+
+ content: function () {
+ return this.get('controller.unhealthyAlertInstances');
+ }.property('controller.unhealthyAlertInstances.@each.state'),
+
+ isLoaded: function () {
+ return !!this.get('controller.unhealthyAlertInstances');
+ }.property('controller.unhealthyAlertInstances'),
+
+ isAlertEmptyList: function () {
+ return !this.get('content.length');
+ }.property('content.length'),
+
+ /**
+ * Update list of shown alert instances
+ * @method updateAlertInstances
+ */
+ updateAlertInstances: function () {
+ var self = this,
+ displayLength = this.get('displayLength'),
+ startIndex = this.get('startIndex');
+ if (!displayLength) return; // wait while table-info is loaded
+ this.get('updater').set('queryParamsForUnhealthyAlertInstances', {
+ from: startIndex - 1,
+ page_size: displayLength
+ });
+ this.set('filteringComplete', false);
+ this.get('updater').updateUnhealthyAlertInstances(function() {
+ self.set('filteringComplete', true);
+ });
+ }.observes('displayLength', 'startIndex'),
+
+ /**
+ * Show spinner when filter/sorting request is in processing
+ * @method overlayObserver
+ */
+ overlayObserver: function() {
+ var $tbody = this.$('#alert-info'),
+ $overlay = this.$('.table-overlay'),
+ $spinner = $($overlay).find('.spinner');
+ if (!this.get('filteringComplete')) {
+ if (!$tbody) return;
+ var tbodyPos = $tbody.position();
+ if (!tbodyPos) return;
+ $spinner.css('display', 'block');
+ $overlay.css({
+ top: tbodyPos.top + 1,
+ left: tbodyPos.left + 1,
+ width: $tbody.width() - 1,
+ height: $tbody.height() - 1
+ });
+ }
+ },
+
+ /**
+ * No filtering for alert definitions
+ * @method filter
+ */
+ filter: function() {
+ this.set('filteredContent', this.get('content'));
+ }.observes('content.length'),
+
+ refreshTooltips: function () {
+ this.ensureTooltip();
+ }.observes('contents.[]'),
+
+ ensureTooltip: function () {
+ Em.run.next(this, function () {
+ App.tooltip($(".timeago"));
+ });
+ },
+ /**
+ * Router transition to alert definition details page
+ * @param event
+ */
+ gotoAlertDetails: function (event) {
+ if (event && event.context) {
+ this.get('parentView').hide();
+ var definition = App.AlertDefinition.find().findProperty('id', event.context.get('definitionId'));
+ App.router.transitionTo('main.alerts.alertDetails', definition);
+ }
+ },
+
+ /**
+ * Router transition to service summary page
+ * @param event
+ */
+ gotoService: function (event) {
+ if (event && event.context) {
+ this.get('parentView').hide();
+ App.router.transitionTo('main.services.service.summary', event.context);
+ }
+ },
+
+ /**
+ * Router transition to host level alerts page
+ * @param event
+ */
+ goToHostAlerts: function (event) {
+ if (event && event.context) {
+ this.get('parentView').hide();
+ App.router.transitionTo('main.hosts.hostDetails.alerts', event.context);
+ }
+ },
+
+ didInsertElement: function () {
+ this.filter();
+ this.ensureTooltip();
+ this.addObserver('filteringComplete', this, this.overlayObserver);
+ this.overlayObserver();
+ return this._super();
+ }
+ })
+ });
}
});
http://git-wip-us.apache.org/repos/asf/ambari/blob/72b40b7b/ambari-web/app/mixins/common/table_server_mixin.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/mixins/common/table_server_mixin.js b/ambari-web/app/mixins/common/table_server_mixin.js
index 9c35d98..5162a02 100644
--- a/ambari-web/app/mixins/common/table_server_mixin.js
+++ b/ambari-web/app/mixins/common/table_server_mixin.js
@@ -19,10 +19,10 @@
var App = require('app');
var validator = require('utils/validator');
-//TODO integrate mixin into mainHostController to avoid code duplication
App.TableServerMixin = Em.Mixin.create({
queryParams: [],
resetStartIndex: false,
+
/**
* filterProps support follow types of filter:
* MATCH - match of RegExp
@@ -33,8 +33,9 @@ App.TableServerMixin = Em.Mixin.create({
* CUSTOM - substitute values with keys "{#}" in alias
*/
filterProps: [],
+
/**
- * include "from" nad "page_size"
+ * include "from" and "page_size"
*/
paginationProps: [
{
@@ -50,6 +51,7 @@ App.TableServerMixin = Em.Mixin.create({
type: 'EQUAL'
}
],
+
sortProps: [],
/**
http://git-wip-us.apache.org/repos/asf/ambari/blob/72b40b7b/ambari-web/app/models/service.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/models/service.js b/ambari-web/app/models/service.js
index d633ad2..a4696d1 100644
--- a/ambari-web/app/models/service.js
+++ b/ambari-web/app/models/service.js
@@ -143,12 +143,12 @@ App.Service = DS.Model.extend({
hasCriticalAlerts: function () {
var controller = App.router.get('mainAlertDefinitionsController');
- return controller.getCriticalAlertsCountForService(this) > 0;
+ return controller.getCriticalAlertsCountForService(this.get('serviceName')) > 0;
}.property('alertsCount'),
alertsCount: function () {
var controller = App.router.get('mainAlertDefinitionsController');
- return controller.getCriticalOrWarningAlertsCountForService(this);
+ return controller.getCriticalOrWarningAlertsCountForService(this.get('serviceName'));
}.property('App.router.mainAlertDefinitionsController.content.@each.isCriticalOrWarning')
});
http://git-wip-us.apache.org/repos/asf/ambari/blob/72b40b7b/ambari-web/app/styles/application.less
----------------------------------------------------------------------
diff --git a/ambari-web/app/styles/application.less b/ambari-web/app/styles/application.less
index 6b51006..ea132d1 100644
--- a/ambari-web/app/styles/application.less
+++ b/ambari-web/app/styles/application.less
@@ -2866,15 +2866,6 @@ table.graphs {
}
}
- .hosts-overlay {
- position: absolute;
- opacity: 0.9;
- background-color: #fefefe;
- .spinner {
- display: none;
- }
- }
-
.status-dot-position {
background-position: center;
background-repeat: no-repeat;
http://git-wip-us.apache.org/repos/asf/ambari/blob/72b40b7b/ambari-web/app/styles/common.less
----------------------------------------------------------------------
diff --git a/ambari-web/app/styles/common.less b/ambari-web/app/styles/common.less
index e9e2528..fde2bd3 100644
--- a/ambari-web/app/styles/common.less
+++ b/ambari-web/app/styles/common.less
@@ -304,3 +304,11 @@
opacity: 0;
}
+.table-overlay {
+ position: absolute;
+ opacity: 0.9;
+ background-color: #fefefe;
+ .spinner {
+ display: none;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/72b40b7b/ambari-web/app/templates/application.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/application.hbs b/ambari-web/app/templates/application.hbs
index dd63a11..63da6e7 100644
--- a/ambari-web/app/templates/application.hbs
+++ b/ambari-web/app/templates/application.hbs
@@ -38,16 +38,14 @@
{{allOperationsCount}} {{pluralize allOperationsCount singular="t:op" plural="t:ops"}}</span>
{{/if}}
{{/with}}
- {{#with App.router.mainAlertDefinitionsController}}
- {{#if allAlertsCount}}
- <span {{bindAttr class=":label isCriticalAlerts:alert-crit-count:alert-warn-count"}} {{action "showPopup" target="App.router.mainAlertDefinitionsController"}}>
- {{allAlertsCount}} {{pluralize allAlertsCount singular="alert" plural="alerts"}}</span>
- {{else}}
- <span rel="tooltip" {{translateAttr title="titlebar.alerts.noAlerts"}}
- class="label alerts-none-count" {{action "showPopup" target="App.router.mainAlertDefinitionsController"}}>
- {{allAlertsCount}} {{pluralize allAlertsCount singular="alert" plural="alerts"}}</span>
- {{/if}}
- {{/with}}
+ {{#if App.router.mainAlertDefinitionsController.unhealthyAlertInstancesCount}}
+ <span {{bindAttr class=":label App.router.mainAlertDefinitionsController.isCriticalAlerts:alert-crit-count:alert-warn-count"}} {{action "showPopup" target="App.router.mainAlertInstancesController"}}>
+ {{App.router.mainAlertDefinitionsController.unhealthyAlertInstancesCount}} {{pluralize App.router.mainAlertDefinitionsController.unhealthyAlertInstancesCount singular="alert" plural="alerts"}}</span>
+ {{else}}
+ <span rel="tooltip" {{translateAttr title="titlebar.alerts.noAlerts"}}
+ class="label alerts-none-count" {{action "showPopup" target="App.router.mainAlertInstancesController"}}>
+ {{App.router.mainAlertDefinitionsController.unhealthyAlertInstancesCount}} {{pluralize App.router.mainAlertDefinitionsController.unhealthyAlertInstancesCount singular="alert" plural="alerts"}}</span>
+ {{/if}}
</a>
{{else}}
<a class="logo"><img src="/img/logo-white.png" alt="Apache Ambari" title="Apache Ambari"></a>
http://git-wip-us.apache.org/repos/asf/ambari/blob/72b40b7b/ambari-web/app/templates/common/modal_popups/alerts_popup.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/common/modal_popups/alerts_popup.hbs b/ambari-web/app/templates/common/modal_popups/alerts_popup.hbs
index a0f7808..2da35ec 100644
--- a/ambari-web/app/templates/common/modal_popups/alerts_popup.hbs
+++ b/ambari-web/app/templates/common/modal_popups/alerts_popup.hbs
@@ -61,6 +61,11 @@
{{/if}}
</div>
</div>
+
+ <div {{bindAttr class="view.filteringComplete:hidden :table-overlay"}}>
+ <div class="spinner"></div>
+ </div>
+
{{#if view.isPaginate}}
<div class="page-bar pull-right no-borders mtm">
<div class="items-on-page">
http://git-wip-us.apache.org/repos/asf/ambari/blob/72b40b7b/ambari-web/app/templates/main/alerts/definition_details.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/alerts/definition_details.hbs b/ambari-web/app/templates/main/alerts/definition_details.hbs
index 960cf04..ac0652d 100644
--- a/ambari-web/app/templates/main/alerts/definition_details.hbs
+++ b/ambari-web/app/templates/main/alerts/definition_details.hbs
@@ -203,7 +203,7 @@
<time class="timeago"
rel="tooltip" {{bindAttr data-original-title="instance.statusChangedAndLastCheckedFormatted"}}>{{instance.lastTriggeredForFormatted}}</time>
</td>
- <td>{{view view.parentView.lastDayCount hostBinding="instance.host"}}</td>
+ <td>{{view view.parentView.lastDayCount hostNameBinding="instance.hostName"}}</td>
<td><span class="alert-text" rel="tooltip" {{bindAttr data-original-title="instance.text"}}
class="alert-text">{{instance.text}}</span></td>
{{/view}}
http://git-wip-us.apache.org/repos/asf/ambari/blob/72b40b7b/ambari-web/app/templates/main/host.hbs
----------------------------------------------------------------------
diff --git a/ambari-web/app/templates/main/host.hbs b/ambari-web/app/templates/main/host.hbs
index 3724926..2fa4f37 100644
--- a/ambari-web/app/templates/main/host.hbs
+++ b/ambari-web/app/templates/main/host.hbs
@@ -163,7 +163,7 @@
</tbody>
</table>
- <div {{bindAttr class="view.filteringComplete:hidden :hosts-overlay"}}>
+ <div {{bindAttr class="view.filteringComplete:hidden :table-overlay"}}>
<div class="spinner"></div>
</div>
http://git-wip-us.apache.org/repos/asf/ambari/blob/72b40b7b/ambari-web/app/utils/ajax/ajax.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/ajax/ajax.js b/ambari-web/app/utils/ajax/ajax.js
index 240d944..c3e7d80 100644
--- a/ambari-web/app/utils/ajax/ajax.js
+++ b/ambari-web/app/utils/ajax/ajax.js
@@ -373,20 +373,24 @@ var urls = {
'real': '/clusters/{clusterName}/alert_definitions?fields=*',
'mock': 'data/alerts/alertDefinitions.json'
},
+ 'alerts.notifications': {
+ 'real': '/alert_targets?fields=*',
+ 'mock': '/data/alerts/alertNotifications.json'
+ },
'alerts.instances': {
'real': '/clusters/{clusterName}/alerts?fields=*',
'mock': '/data/alerts/alert_instances.json'
},
- 'alerts.notifications': {
- 'real': '/alert_targets?fields=*',
- 'mock': '/data/alerts/alertNotifications.json'
+ 'alerts.instances.unhealthy': {
+ 'real': '/clusters/{clusterName}/alerts?fields=*&Alert/state.in(CRITICAL,WARNING)&{paginationInfo}',
+ 'mock': '/data/alerts/alert_instances.json'
},
'alerts.instances.by_definition': {
- 'real': '/clusters/{clusterName}/alerts?fields=*&(Alert/definition_id={definitionId}|Alert/state.in(CRITICAL,WARNING))',
+ 'real': '/clusters/{clusterName}/alerts?fields=*&Alert/definition_id={definitionId}',
'mock': '/data/alerts/alert_instances.json'
},
'alerts.instances.by_host': {
- 'real': '/clusters/{clusterName}/alerts?fields=*&(Alert/host_name={hostName}|Alert/state.in(CRITICAL,WARNING))',
+ 'real': '/clusters/{clusterName}/alerts?fields=*&Alert/host_name={hostName}',
'mock': '/data/alerts/alert_instances.json'
},
'alerts.update_alert_definition': {
http://git-wip-us.apache.org/repos/asf/ambari/blob/72b40b7b/ambari-web/app/utils/ember_reopen.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/utils/ember_reopen.js b/ambari-web/app/utils/ember_reopen.js
index a879cde..4446f1e 100644
--- a/ambari-web/app/utils/ember_reopen.js
+++ b/ambari-web/app/utils/ember_reopen.js
@@ -115,6 +115,19 @@ Ember.isBlank = function(obj) {
};
/**
+ * Calculates sum of two numbers
+ * Use this function as a callback on <code>invoke</code> etc
+ *
+ * @method sum
+ * @param {Number} a
+ * @param {Number} b
+ * @returns {Number}
+ */
+Ember.sum = function (a, b) {
+ return a + b;
+};
+
+/**
*
*/
Ember.RadioButton = Ember.Checkbox.extend({
http://git-wip-us.apache.org/repos/asf/ambari/blob/72b40b7b/ambari-web/app/views/main/alerts/definition_details_view.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/alerts/definition_details_view.js b/ambari-web/app/views/main/alerts/definition_details_view.js
index 28a7c9b..a5e717a 100644
--- a/ambari-web/app/views/main/alerts/definition_details_view.js
+++ b/ambari-web/app/views/main/alerts/definition_details_view.js
@@ -85,11 +85,12 @@ App.MainAlertDefinitionDetailsView = App.TableView.extend({
* View calculates and represents count of alerts on appropriate host during last day
*/
lastDayCount: Em.View.extend({
+ hostName: '', // binding from template
template: Ember.Handlebars.compile('<span>{{view.count}}</span>'),
count: function () {
var lastDayAlertsCount = this.get('parentView.controller.lastDayAlertsCount');
- return lastDayAlertsCount ? lastDayAlertsCount[this.get('host.hostName')] || 0 : Em.I18n.t('app.loadingPlaceholder');
- }.property('parentView.controller.lastDayAlertsCount', 'host')
+ return lastDayAlertsCount ? lastDayAlertsCount[this.get('hostName')] || 0 : Em.I18n.t('app.loadingPlaceholder');
+ }.property('parentView.controller.lastDayAlertsCount', 'hostName')
}),
/**
http://git-wip-us.apache.org/repos/asf/ambari/blob/72b40b7b/ambari-web/app/views/main/host.js
----------------------------------------------------------------------
diff --git a/ambari-web/app/views/main/host.js b/ambari-web/app/views/main/host.js
index f771f75..53445e0 100644
--- a/ambari-web/app/views/main/host.js
+++ b/ambari-web/app/views/main/host.js
@@ -293,7 +293,7 @@ App.MainHostView = App.TableView.extend(App.TableServerViewMixin, {
*/
overlayObserver: function() {
var $tbody = this.$('tbody'),
- $overlay = this.$('.hosts-overlay'),
+ $overlay = this.$('.table-overlay'),
$spinner = $($overlay).find('.spinner');
if (!this.get('filteringComplete')) {
if (!$tbody) return;
http://git-wip-us.apache.org/repos/asf/ambari/blob/72b40b7b/ambari-web/test/controllers/main/alert_definitions_controller_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/alert_definitions_controller_test.js b/ambari-web/test/controllers/main/alert_definitions_controller_test.js
index 103ab8e..03a4d80 100644
--- a/ambari-web/test/controllers/main/alert_definitions_controller_test.js
+++ b/ambari-web/test/controllers/main/alert_definitions_controller_test.js
@@ -53,36 +53,4 @@ describe('App.MainAlertDefinitionsController', function() {
});
- describe('#showPopup', function () {
-
- describe('#bodyClass', function () {
-
- var bodyView;
-
- beforeEach(function () {
- controller.reopen({unhealthyAlertInstances: [
- App.AlertInstance.createRecord({state: 'CRITICAL'}),
- App.AlertInstance.createRecord({state: 'WARNING'}),
- App.AlertInstance.createRecord({state: 'WARNING'}),
- App.AlertInstance.createRecord({state: 'CRITICAL'})
- ]});
- bodyView = controller.showPopup().get('bodyClass').create();
- });
-
- it('#content', function () {
- expect(bodyView.get('content.length')).to.equal(4);
- });
-
- it('#isLoaded', function () {
- expect(bodyView.get('isLoaded')).to.be.true;
- });
-
- it('#isAlertEmptyList', function () {
- expect(bodyView.get('isAlertEmptyList')).to.be.false;
- });
-
- });
-
- });
-
});
http://git-wip-us.apache.org/repos/asf/ambari/blob/72b40b7b/ambari-web/test/controllers/main/alerts/alert_instances_controller_test.js
----------------------------------------------------------------------
diff --git a/ambari-web/test/controllers/main/alerts/alert_instances_controller_test.js b/ambari-web/test/controllers/main/alerts/alert_instances_controller_test.js
index 8f24e5a..d5cda71 100644
--- a/ambari-web/test/controllers/main/alerts/alert_instances_controller_test.js
+++ b/ambari-web/test/controllers/main/alerts/alert_instances_controller_test.js
@@ -68,4 +68,37 @@ describe('App.MainAlertInstancesController', function () {
});
+
+ describe('#showPopup', function () {
+
+ describe('#bodyClass', function () {
+
+ var bodyView;
+
+ beforeEach(function () {
+ controller.reopen({unhealthyAlertInstances: [
+ App.AlertInstance.createRecord({state: 'CRITICAL'}),
+ App.AlertInstance.createRecord({state: 'WARNING'}),
+ App.AlertInstance.createRecord({state: 'WARNING'}),
+ App.AlertInstance.createRecord({state: 'CRITICAL'})
+ ]});
+ bodyView = controller.showPopup().get('bodyClass').create();
+ });
+
+ it('#content', function () {
+ expect(bodyView.get('content.length')).to.equal(4);
+ });
+
+ it('#isLoaded', function () {
+ expect(bodyView.get('isLoaded')).to.be.true;
+ });
+
+ it('#isAlertEmptyList', function () {
+ expect(bodyView.get('isAlertEmptyList')).to.be.false;
+ });
+
+ });
+
+ });
+
});
\ No newline at end of file
[4/4] ambari git commit: AMBARI-10077. Add UI unit tests for Create
App Wizard controllers (onechiporenko)
Posted by on...@apache.org.
AMBARI-10077. Add UI unit tests for Create App Wizard controllers (onechiporenko)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/ed616f1d
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/ed616f1d
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/ed616f1d
Branch: refs/heads/trunk
Commit: ed616f1d544eb1311c734d5199e9d081e794eaf1
Parents: 72b40b7
Author: Oleg Nechiporenko <on...@apache.org>
Authored: Mon Mar 16 11:10:53 2015 +0200
Committer: Oleg Nechiporenko <on...@apache.org>
Committed: Mon Mar 16 11:10:53 2015 +0200
----------------------------------------------------------------------
.../ui/app/assets/javascripts/sinon-1.13.0.js | 5830 ++++++++++++++++++
.../app/assets/javascripts/sinon-qunit-1.0.0.js | 62 +
.../src/main/resources/ui/app/assets/tests.html | 2 +
.../ui/test/integration/pages/index_test.js | 1 +
.../integration/pages/slider_errors_test.js | 1 +
.../processes/create_new_app_test.js | 2 +
.../createAppWizard/step1_controller_test.js | 243 +-
.../createAppWizard/step2_controller_test.js | 366 ++
.../createAppWizard/step3_controller_test.js | 399 +-
.../createAppWizard/step4_controller_test.js | 341 +-
.../controllers/slider_app_controller_test.js | 65 +-
.../controllers/slider_apps_controller_test.js | 18 +-
.../unit/controllers/slider_controller_test.js | 22 +-
13 files changed, 7246 insertions(+), 106 deletions(-)
----------------------------------------------------------------------
[3/4] ambari git commit: AMBARI-10077. Add UI unit tests for Create
App Wizard controllers (onechiporenko)
Posted by on...@apache.org.
http://git-wip-us.apache.org/repos/asf/ambari/blob/ed616f1d/contrib/views/slider/src/main/resources/ui/app/assets/javascripts/sinon-1.13.0.js
----------------------------------------------------------------------
diff --git a/contrib/views/slider/src/main/resources/ui/app/assets/javascripts/sinon-1.13.0.js b/contrib/views/slider/src/main/resources/ui/app/assets/javascripts/sinon-1.13.0.js
new file mode 100644
index 0000000..31ab75e
--- /dev/null
+++ b/contrib/views/slider/src/main/resources/ui/app/assets/javascripts/sinon-1.13.0.js
@@ -0,0 +1,5830 @@
+/**
+ * Sinon.JS 1.13.0, 2015/03/05
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @author Contributors: https://github.com/cjohansen/Sinon.JS/blob/master/AUTHORS
+ *
+ * (The BSD License)
+ *
+ * Copyright (c) 2010-2014, 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.
+ */
+
+(function (root, factory) {
+ if (typeof define === 'function' && define.amd) {
+ define('sinon', [], function () {
+ return (root.sinon = factory());
+ });
+ } else if (typeof exports === 'object') {
+ module.exports = factory();
+ } else {
+ root.sinon = factory();
+ }
+}(this, function () {
+ var samsam, formatio;
+ (function () {
+ function define(mod, deps, fn) {
+ if (mod == "samsam") {
+ samsam = deps();
+ } else if (typeof deps === "function" && mod.length === 0) {
+ lolex = deps();
+ } else if (typeof fn === "function") {
+ formatio = fn(samsam);
+ }
+ }
+ define.amd = {};
+((typeof define === "function" && define.amd && function (m) { define("samsam", m); }) ||
+ (typeof module === "object" &&
+ function (m) { module.exports = m(); }) || // Node
+ function (m) { this.samsam = m(); } // Browser globals
+)(function () {
+ var o = Object.prototype;
+ var div = typeof document !== "undefined" && document.createElement("div");
+
+ function isNaN(value) {
+ // Unlike global isNaN, this avoids type coercion
+ // typeof check avoids IE host object issues, hat tip to
+ // lodash
+ var val = value; // JsLint thinks value !== value is "weird"
+ return typeof value === "number" && value !== val;
+ }
+
+ function getClass(value) {
+ // Returns the internal [[Class]] by calling Object.prototype.toString
+ // with the provided value as this. Return value is a string, naming the
+ // internal class, e.g. "Array"
+ return o.toString.call(value).split(/[ \]]/)[1];
+ }
+
+ /**
+ * @name samsam.isArguments
+ * @param Object object
+ *
+ * Returns ``true`` if ``object`` is an ``arguments`` object,
+ * ``false`` otherwise.
+ */
+ function isArguments(object) {
+ if (getClass(object) === 'Arguments') { return true; }
+ if (typeof object !== "object" || typeof object.length !== "number" ||
+ getClass(object) === "Array") {
+ return false;
+ }
+ if (typeof object.callee == "function") { return true; }
+ try {
+ object[object.length] = 6;
+ delete object[object.length];
+ } catch (e) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @name samsam.isElement
+ * @param Object object
+ *
+ * Returns ``true`` if ``object`` is a DOM element node. Unlike
+ * Underscore.js/lodash, this function will return ``false`` if ``object``
+ * is an *element-like* object, i.e. a regular object with a ``nodeType``
+ * property that holds the value ``1``.
+ */
+ function isElement(object) {
+ if (!object || object.nodeType !== 1 || !div) { return false; }
+ try {
+ object.appendChild(div);
+ object.removeChild(div);
+ } catch (e) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * @name samsam.keys
+ * @param Object object
+ *
+ * Return an array of own property names.
+ */
+ function keys(object) {
+ var ks = [], prop;
+ for (prop in object) {
+ if (o.hasOwnProperty.call(object, prop)) { ks.push(prop); }
+ }
+ return ks;
+ }
+
+ /**
+ * @name samsam.isDate
+ * @param Object value
+ *
+ * Returns true if the object is a ``Date``, or *date-like*. Duck typing
+ * of date objects work by checking that the object has a ``getTime``
+ * function whose return value equals the return value from the object's
+ * ``valueOf``.
+ */
+ function isDate(value) {
+ return typeof value.getTime == "function" &&
+ value.getTime() == value.valueOf();
+ }
+
+ /**
+ * @name samsam.isNegZero
+ * @param Object value
+ *
+ * Returns ``true`` if ``value`` is ``-0``.
+ */
+ function isNegZero(value) {
+ return value === 0 && 1 / value === -Infinity;
+ }
+
+ /**
+ * @name samsam.equal
+ * @param Object obj1
+ * @param Object obj2
+ *
+ * Returns ``true`` if two objects are strictly equal. Compared to
+ * ``===`` there are two exceptions:
+ *
+ * - NaN is considered equal to NaN
+ * - -0 and +0 are not considered equal
+ */
+ function identical(obj1, obj2) {
+ if (obj1 === obj2 || (isNaN(obj1) && isNaN(obj2))) {
+ return obj1 !== 0 || isNegZero(obj1) === isNegZero(obj2);
+ }
+ }
+
+
+ /**
+ * @name samsam.deepEqual
+ * @param Object obj1
+ * @param Object obj2
+ *
+ * Deep equal comparison. Two values are "deep equal" if:
+ *
+ * - They are equal, according to samsam.identical
+ * - They are both date objects representing the same time
+ * - They are both arrays containing elements that are all deepEqual
+ * - They are objects with the same set of properties, and each property
+ * in ``obj1`` is deepEqual to the corresponding property in ``obj2``
+ *
+ * Supports cyclic objects.
+ */
+ function deepEqualCyclic(obj1, obj2) {
+
+ // used for cyclic comparison
+ // contain already visited objects
+ var objects1 = [],
+ objects2 = [],
+ // contain pathes (position in the object structure)
+ // of the already visited objects
+ // indexes same as in objects arrays
+ paths1 = [],
+ paths2 = [],
+ // contains combinations of already compared objects
+ // in the manner: { "$1['ref']$2['ref']": true }
+ compared = {};
+
+ /**
+ * used to check, if the value of a property is an object
+ * (cyclic logic is only needed for objects)
+ * only needed for cyclic logic
+ */
+ function isObject(value) {
+
+ if (typeof value === 'object' && value !== null &&
+ !(value instanceof Boolean) &&
+ !(value instanceof Date) &&
+ !(value instanceof Number) &&
+ !(value instanceof RegExp) &&
+ !(value instanceof String)) {
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * returns the index of the given object in the
+ * given objects array, -1 if not contained
+ * only needed for cyclic logic
+ */
+ function getIndex(objects, obj) {
+
+ var i;
+ for (i = 0; i < objects.length; i++) {
+ if (objects[i] === obj) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ // does the recursion for the deep equal check
+ return (function deepEqual(obj1, obj2, path1, path2) {
+ var type1 = typeof obj1;
+ var type2 = typeof obj2;
+
+ // == null also matches undefined
+ if (obj1 === obj2 ||
+ isNaN(obj1) || isNaN(obj2) ||
+ obj1 == null || obj2 == null ||
+ type1 !== "object" || type2 !== "object") {
+
+ return identical(obj1, obj2);
+ }
+
+ // Elements are only equal if identical(expected, actual)
+ if (isElement(obj1) || isElement(obj2)) { return false; }
+
+ var isDate1 = isDate(obj1), isDate2 = isDate(obj2);
+ if (isDate1 || isDate2) {
+ if (!isDate1 || !isDate2 || obj1.getTime() !== obj2.getTime()) {
+ return false;
+ }
+ }
+
+ if (obj1 instanceof RegExp && obj2 instanceof RegExp) {
+ if (obj1.toString() !== obj2.toString()) { return false; }
+ }
+
+ var class1 = getClass(obj1);
+ var class2 = getClass(obj2);
+ var keys1 = keys(obj1);
+ var keys2 = keys(obj2);
+
+ if (isArguments(obj1) || isArguments(obj2)) {
+ if (obj1.length !== obj2.length) { return false; }
+ } else {
+ if (type1 !== type2 || class1 !== class2 ||
+ keys1.length !== keys2.length) {
+ return false;
+ }
+ }
+
+ var key, i, l,
+ // following vars are used for the cyclic logic
+ value1, value2,
+ isObject1, isObject2,
+ index1, index2,
+ newPath1, newPath2;
+
+ for (i = 0, l = keys1.length; i < l; i++) {
+ key = keys1[i];
+ if (!o.hasOwnProperty.call(obj2, key)) {
+ return false;
+ }
+
+ // Start of the cyclic logic
+
+ value1 = obj1[key];
+ value2 = obj2[key];
+
+ isObject1 = isObject(value1);
+ isObject2 = isObject(value2);
+
+ // determine, if the objects were already visited
+ // (it's faster to check for isObject first, than to
+ // get -1 from getIndex for non objects)
+ index1 = isObject1 ? getIndex(objects1, value1) : -1;
+ index2 = isObject2 ? getIndex(objects2, value2) : -1;
+
+ // determine the new pathes of the objects
+ // - for non cyclic objects the current path will be extended
+ // by current property name
+ // - for cyclic objects the stored path is taken
+ newPath1 = index1 !== -1
+ ? paths1[index1]
+ : path1 + '[' + JSON.stringify(key) + ']';
+ newPath2 = index2 !== -1
+ ? paths2[index2]
+ : path2 + '[' + JSON.stringify(key) + ']';
+
+ // stop recursion if current objects are already compared
+ if (compared[newPath1 + newPath2]) {
+ return true;
+ }
+
+ // remember the current objects and their pathes
+ if (index1 === -1 && isObject1) {
+ objects1.push(value1);
+ paths1.push(newPath1);
+ }
+ if (index2 === -1 && isObject2) {
+ objects2.push(value2);
+ paths2.push(newPath2);
+ }
+
+ // remember that the current objects are already compared
+ if (isObject1 && isObject2) {
+ compared[newPath1 + newPath2] = true;
+ }
+
+ // End of cyclic logic
+
+ // neither value1 nor value2 is a cycle
+ // continue with next level
+ if (!deepEqual(value1, value2, newPath1, newPath2)) {
+ return false;
+ }
+ }
+
+ return true;
+
+ }(obj1, obj2, '$1', '$2'));
+ }
+
+ var match;
+
+ function arrayContains(array, subset) {
+ if (subset.length === 0) { return true; }
+ var i, l, j, k;
+ for (i = 0, l = array.length; i < l; ++i) {
+ if (match(array[i], subset[0])) {
+ for (j = 0, k = subset.length; j < k; ++j) {
+ if (!match(array[i + j], subset[j])) { return false; }
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @name samsam.match
+ * @param Object object
+ * @param Object matcher
+ *
+ * Compare arbitrary value ``object`` with matcher.
+ */
+ match = function match(object, matcher) {
+ if (matcher && typeof matcher.test === "function") {
+ return matcher.test(object);
+ }
+
+ if (typeof matcher === "function") {
+ return matcher(object) === true;
+ }
+
+ if (typeof matcher === "string") {
+ matcher = matcher.toLowerCase();
+ var notNull = typeof object === "string" || !!object;
+ return notNull &&
+ (String(object)).toLowerCase().indexOf(matcher) >= 0;
+ }
+
+ if (typeof matcher === "number") {
+ return matcher === object;
+ }
+
+ if (typeof matcher === "boolean") {
+ return matcher === object;
+ }
+
+ if (typeof(matcher) === "undefined") {
+ return typeof(object) === "undefined";
+ }
+
+ if (matcher === null) {
+ return object === null;
+ }
+
+ if (getClass(object) === "Array" && getClass(matcher) === "Array") {
+ return arrayContains(object, matcher);
+ }
+
+ if (matcher && typeof matcher === "object") {
+ if (matcher === object) {
+ return true;
+ }
+ var prop;
+ for (prop in matcher) {
+ var value = object[prop];
+ if (typeof value === "undefined" &&
+ typeof object.getAttribute === "function") {
+ value = object.getAttribute(prop);
+ }
+ if (matcher[prop] === null || typeof matcher[prop] === 'undefined') {
+ if (value !== matcher[prop]) {
+ return false;
+ }
+ } else if (typeof value === "undefined" || !match(value, matcher[prop])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ throw new Error("Matcher was not a string, a number, a " +
+ "function, a boolean or an object");
+ };
+
+ return {
+ isArguments: isArguments,
+ isElement: isElement,
+ isDate: isDate,
+ isNegZero: isNegZero,
+ identical: identical,
+ deepEqual: deepEqualCyclic,
+ match: match,
+ keys: keys
+ };
+});
+((typeof define === "function" && define.amd && function (m) {
+ define("formatio", ["samsam"], m);
+}) || (typeof module === "object" && function (m) {
+ module.exports = m(require("samsam"));
+}) || function (m) { this.formatio = m(this.samsam); }
+)(function (samsam) {
+
+ var formatio = {
+ excludeConstructors: ["Object", /^.$/],
+ quoteStrings: true,
+ limitChildrenCount: 0
+ };
+
+ var hasOwn = Object.prototype.hasOwnProperty;
+
+ var specialObjects = [];
+ if (typeof global !== "undefined") {
+ specialObjects.push({ object: global, value: "[object global]" });
+ }
+ if (typeof document !== "undefined") {
+ specialObjects.push({
+ object: document,
+ value: "[object HTMLDocument]"
+ });
+ }
+ if (typeof window !== "undefined") {
+ specialObjects.push({ object: window, value: "[object Window]" });
+ }
+
+ function functionName(func) {
+ if (!func) { return ""; }
+ if (func.displayName) { return func.displayName; }
+ if (func.name) { return func.name; }
+ var matches = func.toString().match(/function\s+([^\(]+)/m);
+ return (matches && matches[1]) || "";
+ }
+
+ function constructorName(f, object) {
+ var name = functionName(object && object.constructor);
+ var excludes = f.excludeConstructors ||
+ formatio.excludeConstructors || [];
+
+ var i, l;
+ for (i = 0, l = excludes.length; i < l; ++i) {
+ if (typeof excludes[i] === "string" && excludes[i] === name) {
+ return "";
+ } else if (excludes[i].test && excludes[i].test(name)) {
+ return "";
+ }
+ }
+
+ return name;
+ }
+
+ function isCircular(object, objects) {
+ if (typeof object !== "object") { return false; }
+ var i, l;
+ for (i = 0, l = objects.length; i < l; ++i) {
+ if (objects[i] === object) { return true; }
+ }
+ return false;
+ }
+
+ function ascii(f, object, processed, indent) {
+ if (typeof object === "string") {
+ var qs = f.quoteStrings;
+ var quote = typeof qs !== "boolean" || qs;
+ return processed || quote ? '"' + object + '"' : object;
+ }
+
+ if (typeof object === "function" && !(object instanceof RegExp)) {
+ return ascii.func(object);
+ }
+
+ processed = processed || [];
+
+ if (isCircular(object, processed)) { return "[Circular]"; }
+
+ if (Object.prototype.toString.call(object) === "[object Array]") {
+ return ascii.array.call(f, object, processed);
+ }
+
+ if (!object) { return String((1/object) === -Infinity ? "-0" : object); }
+ if (samsam.isElement(object)) { return ascii.element(object); }
+
+ if (typeof object.toString === "function" &&
+ object.toString !== Object.prototype.toString) {
+ return object.toString();
+ }
+
+ var i, l;
+ for (i = 0, l = specialObjects.length; i < l; i++) {
+ if (object === specialObjects[i].object) {
+ return specialObjects[i].value;
+ }
+ }
+
+ return ascii.object.call(f, object, processed, indent);
+ }
+
+ ascii.func = function (func) {
+ return "function " + functionName(func) + "() {}";
+ };
+
+ ascii.array = function (array, processed) {
+ processed = processed || [];
+ processed.push(array);
+ var pieces = [];
+ var i, l;
+ l = (this.limitChildrenCount > 0) ?
+ Math.min(this.limitChildrenCount, array.length) : array.length;
+
+ for (i = 0; i < l; ++i) {
+ pieces.push(ascii(this, array[i], processed));
+ }
+
+ if(l < array.length)
+ pieces.push("[... " + (array.length - l) + " more elements]");
+
+ return "[" + pieces.join(", ") + "]";
+ };
+
+ ascii.object = function (object, processed, indent) {
+ processed = processed || [];
+ processed.push(object);
+ indent = indent || 0;
+ var pieces = [], properties = samsam.keys(object).sort();
+ var length = 3;
+ var prop, str, obj, i, k, l;
+ l = (this.limitChildrenCount > 0) ?
+ Math.min(this.limitChildrenCount, properties.length) : properties.length;
+
+ for (i = 0; i < l; ++i) {
+ prop = properties[i];
+ obj = object[prop];
+
+ if (isCircular(obj, processed)) {
+ str = "[Circular]";
+ } else {
+ str = ascii(this, obj, processed, indent + 2);
+ }
+
+ str = (/\s/.test(prop) ? '"' + prop + '"' : prop) + ": " + str;
+ length += str.length;
+ pieces.push(str);
+ }
+
+ var cons = constructorName(this, object);
+ var prefix = cons ? "[" + cons + "] " : "";
+ var is = "";
+ for (i = 0, k = indent; i < k; ++i) { is += " "; }
+
+ if(l < properties.length)
+ pieces.push("[... " + (properties.length - l) + " more elements]");
+
+ if (length + indent > 80) {
+ return prefix + "{\n " + is + pieces.join(",\n " + is) + "\n" +
+ is + "}";
+ }
+ return prefix + "{ " + pieces.join(", ") + " }";
+ };
+
+ ascii.element = function (element) {
+ var tagName = element.tagName.toLowerCase();
+ var attrs = element.attributes, attr, pairs = [], attrName, i, l, val;
+
+ for (i = 0, l = attrs.length; i < l; ++i) {
+ attr = attrs.item(i);
+ attrName = attr.nodeName.toLowerCase().replace("html:", "");
+ val = attr.nodeValue;
+ if (attrName !== "contenteditable" || val !== "inherit") {
+ if (!!val) { pairs.push(attrName + "=\"" + val + "\""); }
+ }
+ }
+
+ var formatted = "<" + tagName + (pairs.length > 0 ? " " : "");
+ var content = element.innerHTML;
+
+ if (content.length > 20) {
+ content = content.substr(0, 20) + "[...]";
+ }
+
+ var res = formatted + pairs.join(" ") + ">" + content +
+ "</" + tagName + ">";
+
+ return res.replace(/ contentEditable="inherit"/, "");
+ };
+
+ function Formatio(options) {
+ for (var opt in options) {
+ this[opt] = options[opt];
+ }
+ }
+
+ Formatio.prototype = {
+ functionName: functionName,
+
+ configure: function (options) {
+ return new Formatio(options);
+ },
+
+ constructorName: function (object) {
+ return constructorName(this, object);
+ },
+
+ ascii: function (object, processed, indent) {
+ return ascii(this, object, processed, indent);
+ }
+ };
+
+ return Formatio.prototype;
+});
+!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.lolex=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
+(function (global){
+/*jslint eqeqeq: false, plusplus: false, evil: true, onevar: false, browser: true, forin: false*/
+/*global global*/
+/**
+ * @author Christian Johansen (christian@cjohansen.no) and contributors
+ * @license BSD
+ *
+ * Copyright (c) 2010-2014 Christian Johansen
+ */
+
+// node expects setTimeout/setInterval to return a fn object w/ .ref()/.unref()
+// browsers, a number.
+// see https://github.com/cjohansen/Sinon.JS/pull/436
+var timeoutResult = setTimeout(function() {}, 0);
+var addTimerReturnsObject = typeof timeoutResult === "object";
+clearTimeout(timeoutResult);
+
+var NativeDate = Date;
+var id = 1;
+
+/**
+ * Parse strings like "01:10:00" (meaning 1 hour, 10 minutes, 0 seconds) into
+ * number of milliseconds. This is used to support human-readable strings passed
+ * to clock.tick()
+ */
+function parseTime(str) {
+ if (!str) {
+ return 0;
+ }
+
+ var strings = str.split(":");
+ var l = strings.length, i = l;
+ var ms = 0, parsed;
+
+ if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) {
+ throw new Error("tick only understands numbers and 'h:m:s'");
+ }
+
+ while (i--) {
+ parsed = parseInt(strings[i], 10);
+
+ if (parsed >= 60) {
+ throw new Error("Invalid time " + str);
+ }
+
+ ms += parsed * Math.pow(60, (l - i - 1));
+ }
+
+ return ms * 1000;
+}
+
+/**
+ * Used to grok the `now` parameter to createClock.
+ */
+function getEpoch(epoch) {
+ if (!epoch) { return 0; }
+ if (typeof epoch.getTime === "function") { return epoch.getTime(); }
+ if (typeof epoch === "number") { return epoch; }
+ throw new TypeError("now should be milliseconds since UNIX epoch");
+}
+
+function inRange(from, to, timer) {
+ return timer && timer.callAt >= from && timer.callAt <= to;
+}
+
+function mirrorDateProperties(target, source) {
+ if (source.now) {
+ target.now = function now() {
+ return target.clock.now;
+ };
+ } else {
+ delete target.now;
+ }
+
+ if (source.toSource) {
+ target.toSource = function toSource() {
+ return source.toSource();
+ };
+ } else {
+ delete target.toSource;
+ }
+
+ target.toString = function toString() {
+ return source.toString();
+ };
+
+ target.prototype = source.prototype;
+ target.parse = source.parse;
+ target.UTC = source.UTC;
+ target.prototype.toUTCString = source.prototype.toUTCString;
+
+ for (var prop in source) {
+ if (source.hasOwnProperty(prop)) {
+ target[prop] = source[prop];
+ }
+ }
+
+ return target;
+}
+
+function createDate() {
+ function ClockDate(year, month, date, hour, minute, second, ms) {
+ // Defensive and verbose to avoid potential harm in passing
+ // explicit undefined when user does not pass argument
+ switch (arguments.length) {
+ case 0:
+ return new NativeDate(ClockDate.clock.now);
+ case 1:
+ return new NativeDate(year);
+ case 2:
+ return new NativeDate(year, month);
+ case 3:
+ return new NativeDate(year, month, date);
+ case 4:
+ return new NativeDate(year, month, date, hour);
+ case 5:
+ return new NativeDate(year, month, date, hour, minute);
+ case 6:
+ return new NativeDate(year, month, date, hour, minute, second);
+ default:
+ return new NativeDate(year, month, date, hour, minute, second, ms);
+ }
+ }
+
+ return mirrorDateProperties(ClockDate, NativeDate);
+}
+
+function addTimer(clock, timer) {
+ if (typeof timer.func === "undefined") {
+ throw new Error("Callback must be provided to timer calls");
+ }
+
+ if (!clock.timers) {
+ clock.timers = {};
+ }
+
+ timer.id = id++;
+ timer.createdAt = clock.now;
+ timer.callAt = clock.now + (timer.delay || 0);
+
+ clock.timers[timer.id] = timer;
+
+ if (addTimerReturnsObject) {
+ return {
+ id: timer.id,
+ ref: function() {},
+ unref: function() {}
+ };
+ }
+ else {
+ return timer.id;
+ }
+}
+
+function firstTimerInRange(clock, from, to) {
+ var timers = clock.timers, timer = null;
+
+ for (var id in timers) {
+ if (!inRange(from, to, timers[id])) {
+ continue;
+ }
+
+ if (!timer || ~compareTimers(timer, timers[id])) {
+ timer = timers[id];
+ }
+ }
+
+ return timer;
+}
+
+function compareTimers(a, b) {
+ // Sort first by absolute timing
+ if (a.callAt < b.callAt) {
+ return -1;
+ }
+ if (a.callAt > b.callAt) {
+ return 1;
+ }
+
+ // Sort next by immediate, immediate timers take precedence
+ if (a.immediate && !b.immediate) {
+ return -1;
+ }
+ if (!a.immediate && b.immediate) {
+ return 1;
+ }
+
+ // Sort next by creation time, earlier-created timers take precedence
+ if (a.createdAt < b.createdAt) {
+ return -1;
+ }
+ if (a.createdAt > b.createdAt) {
+ return 1;
+ }
+
+ // Sort next by id, lower-id timers take precedence
+ if (a.id < b.id) {
+ return -1;
+ }
+ if (a.id > b.id) {
+ return 1;
+ }
+
+ // As timer ids are unique, no fallback `0` is necessary
+}
+
+function callTimer(clock, timer) {
+ if (typeof timer.interval == "number") {
+ clock.timers[timer.id].callAt += timer.interval;
+ } else {
+ delete clock.timers[timer.id];
+ }
+
+ try {
+ if (typeof timer.func == "function") {
+ timer.func.apply(null, timer.args);
+ } else {
+ eval(timer.func);
+ }
+ } catch (e) {
+ var exception = e;
+ }
+
+ if (!clock.timers[timer.id]) {
+ if (exception) {
+ throw exception;
+ }
+ return;
+ }
+
+ if (exception) {
+ throw exception;
+ }
+}
+
+function uninstall(clock, target) {
+ var method;
+
+ for (var i = 0, l = clock.methods.length; i < l; i++) {
+ method = clock.methods[i];
+
+ if (target[method].hadOwnProperty) {
+ target[method] = clock["_" + method];
+ } else {
+ try {
+ delete target[method];
+ } catch (e) {}
+ }
+ }
+
+ // Prevent multiple executions which will completely remove these props
+ clock.methods = [];
+}
+
+function hijackMethod(target, method, clock) {
+ clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(target, method);
+ clock["_" + method] = target[method];
+
+ if (method == "Date") {
+ var date = mirrorDateProperties(clock[method], target[method]);
+ target[method] = date;
+ } else {
+ target[method] = function () {
+ return clock[method].apply(clock, arguments);
+ };
+
+ for (var prop in clock[method]) {
+ if (clock[method].hasOwnProperty(prop)) {
+ target[method][prop] = clock[method][prop];
+ }
+ }
+ }
+
+ target[method].clock = clock;
+}
+
+var timers = {
+ setTimeout: setTimeout,
+ clearTimeout: clearTimeout,
+ setImmediate: (typeof setImmediate !== "undefined" ? setImmediate : undefined),
+ clearImmediate: (typeof clearImmediate !== "undefined" ? clearImmediate: undefined),
+ setInterval: setInterval,
+ clearInterval: clearInterval,
+ Date: Date
+};
+
+var keys = Object.keys || function (obj) {
+ var ks = [];
+ for (var key in obj) {
+ ks.push(key);
+ }
+ return ks;
+};
+
+exports.timers = timers;
+
+var createClock = exports.createClock = function (now) {
+ var clock = {
+ now: getEpoch(now),
+ timeouts: {},
+ Date: createDate()
+ };
+
+ clock.Date.clock = clock;
+
+ clock.setTimeout = function setTimeout(func, timeout) {
+ return addTimer(clock, {
+ func: func,
+ args: Array.prototype.slice.call(arguments, 2),
+ delay: timeout
+ });
+ };
+
+ clock.clearTimeout = function clearTimeout(timerId) {
+ if (!timerId) {
+ // null appears to be allowed in most browsers, and appears to be
+ // relied upon by some libraries, like Bootstrap carousel
+ return;
+ }
+ if (!clock.timers) {
+ clock.timers = [];
+ }
+ // in Node, timerId is an object with .ref()/.unref(), and
+ // its .id field is the actual timer id.
+ if (typeof timerId === "object") {
+ timerId = timerId.id
+ }
+ if (timerId in clock.timers) {
+ delete clock.timers[timerId];
+ }
+ };
+
+ clock.setInterval = function setInterval(func, timeout) {
+ return addTimer(clock, {
+ func: func,
+ args: Array.prototype.slice.call(arguments, 2),
+ delay: timeout,
+ interval: timeout
+ });
+ };
+
+ clock.clearInterval = function clearInterval(timerId) {
+ clock.clearTimeout(timerId);
+ };
+
+ clock.setImmediate = function setImmediate(func) {
+ return addTimer(clock, {
+ func: func,
+ args: Array.prototype.slice.call(arguments, 1),
+ immediate: true
+ });
+ };
+
+ clock.clearImmediate = function clearImmediate(timerId) {
+ clock.clearTimeout(timerId);
+ };
+
+ clock.tick = function tick(ms) {
+ ms = typeof ms == "number" ? ms : parseTime(ms);
+ var tickFrom = clock.now, tickTo = clock.now + ms, previous = clock.now;
+ var timer = firstTimerInRange(clock, tickFrom, tickTo);
+
+ var firstException;
+ while (timer && tickFrom <= tickTo) {
+ if (clock.timers[timer.id]) {
+ tickFrom = clock.now = timer.callAt;
+ try {
+ callTimer(clock, timer);
+ } catch (e) {
+ firstException = firstException || e;
+ }
+ }
+
+ timer = firstTimerInRange(clock, previous, tickTo);
+ previous = tickFrom;
+ }
+
+ clock.now = tickTo;
+
+ if (firstException) {
+ throw firstException;
+ }
+
+ return clock.now;
+ };
+
+ clock.reset = function reset() {
+ clock.timers = {};
+ };
+
+ return clock;
+};
+
+exports.install = function install(target, now, toFake) {
+ if (typeof target === "number") {
+ toFake = now;
+ now = target;
+ target = null;
+ }
+
+ if (!target) {
+ target = global;
+ }
+
+ var clock = createClock(now);
+
+ clock.uninstall = function () {
+ uninstall(clock, target);
+ };
+
+ clock.methods = toFake || [];
+
+ if (clock.methods.length === 0) {
+ clock.methods = keys(timers);
+ }
+
+ for (var i = 0, l = clock.methods.length; i < l; i++) {
+ hijackMethod(target, clock.methods[i], clock);
+ }
+
+ return clock;
+};
+
+}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
+},{}]},{},[1])(1)
+});
+ })();
+ var define;
+/**
+ * Sinon core utilities. For internal use only.
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+var sinon = (function () {
+"use strict";
+
+ var sinon;
+ var isNode = typeof module !== "undefined" && module.exports && typeof require === "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ function loadDependencies(require, exports, module) {
+ sinon = module.exports = require("./sinon/util/core");
+ require("./sinon/extend");
+ require("./sinon/typeOf");
+ require("./sinon/times_in_words");
+ require("./sinon/spy");
+ require("./sinon/call");
+ require("./sinon/behavior");
+ require("./sinon/stub");
+ require("./sinon/mock");
+ require("./sinon/collection");
+ require("./sinon/assert");
+ require("./sinon/sandbox");
+ require("./sinon/test");
+ require("./sinon/test_case");
+ require("./sinon/match");
+ require("./sinon/format");
+ require("./sinon/log_error");
+ }
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ sinon = module.exports;
+ } else {
+ sinon = {};
+ }
+
+ return sinon;
+}());
+
+/**
+ * @depend ../../sinon.js
+ */
+/**
+ * Sinon core utilities. For internal use only.
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+(function (sinon) {
+ var div = typeof document != "undefined" && document.createElement("div");
+ var hasOwn = Object.prototype.hasOwnProperty;
+
+ function isDOMNode(obj) {
+ var success = false;
+
+ try {
+ obj.appendChild(div);
+ success = div.parentNode == obj;
+ } catch (e) {
+ return false;
+ } finally {
+ try {
+ obj.removeChild(div);
+ } catch (e) {
+ // Remove failed, not much we can do about that
+ }
+ }
+
+ return success;
+ }
+
+ function isElement(obj) {
+ return div && obj && obj.nodeType === 1 && isDOMNode(obj);
+ }
+
+ function isFunction(obj) {
+ return typeof obj === "function" || !!(obj && obj.constructor && obj.call && obj.apply);
+ }
+
+ function isReallyNaN(val) {
+ return typeof val === "number" && isNaN(val);
+ }
+
+ function mirrorProperties(target, source) {
+ for (var prop in source) {
+ if (!hasOwn.call(target, prop)) {
+ target[prop] = source[prop];
+ }
+ }
+ }
+
+ function isRestorable(obj) {
+ return typeof obj === "function" && typeof obj.restore === "function" && obj.restore.sinon;
+ }
+
+ function makeApi(sinon) {
+ sinon.wrapMethod = function wrapMethod(object, property, method) {
+ if (!object) {
+ throw new TypeError("Should wrap property of object");
+ }
+
+ if (typeof method != "function") {
+ throw new TypeError("Method wrapper should be function");
+ }
+
+ var wrappedMethod = object[property],
+ error;
+
+ if (!isFunction(wrappedMethod)) {
+ error = new TypeError("Attempted to wrap " + (typeof wrappedMethod) + " property " +
+ property + " as function");
+ } else if (wrappedMethod.restore && wrappedMethod.restore.sinon) {
+ error = new TypeError("Attempted to wrap " + property + " which is already wrapped");
+ } else if (wrappedMethod.calledBefore) {
+ var verb = !!wrappedMethod.returns ? "stubbed" : "spied on";
+ error = new TypeError("Attempted to wrap " + property + " which is already " + verb);
+ }
+
+ if (error) {
+ if (wrappedMethod && wrappedMethod.stackTrace) {
+ error.stack += "\n--------------\n" + wrappedMethod.stackTrace;
+ }
+ throw error;
+ }
+
+ // IE 8 does not support hasOwnProperty on the window object and Firefox has a problem
+ // when using hasOwn.call on objects from other frames.
+ var owned = object.hasOwnProperty ? object.hasOwnProperty(property) : hasOwn.call(object, property);
+ object[property] = method;
+ method.displayName = property;
+ // Set up a stack trace which can be used later to find what line of
+ // code the original method was created on.
+ method.stackTrace = (new Error("Stack Trace for original")).stack;
+
+ method.restore = function () {
+ // For prototype properties try to reset by delete first.
+ // If this fails (ex: localStorage on mobile safari) then force a reset
+ // via direct assignment.
+ if (!owned) {
+ delete object[property];
+ }
+ if (object[property] === method) {
+ object[property] = wrappedMethod;
+ }
+ };
+
+ method.restore.sinon = true;
+ mirrorProperties(method, wrappedMethod);
+
+ return method;
+ };
+
+ sinon.create = function create(proto) {
+ var F = function () {};
+ F.prototype = proto;
+ return new F();
+ };
+
+ sinon.deepEqual = function deepEqual(a, b) {
+ if (sinon.match && sinon.match.isMatcher(a)) {
+ return a.test(b);
+ }
+
+ if (typeof a != "object" || typeof b != "object") {
+ if (isReallyNaN(a) && isReallyNaN(b)) {
+ return true;
+ } else {
+ return a === b;
+ }
+ }
+
+ if (isElement(a) || isElement(b)) {
+ return a === b;
+ }
+
+ if (a === b) {
+ return true;
+ }
+
+ if ((a === null && b !== null) || (a !== null && b === null)) {
+ return false;
+ }
+
+ if (a instanceof RegExp && b instanceof RegExp) {
+ return (a.source === b.source) && (a.global === b.global) &&
+ (a.ignoreCase === b.ignoreCase) && (a.multiline === b.multiline);
+ }
+
+ var aString = Object.prototype.toString.call(a);
+ if (aString != Object.prototype.toString.call(b)) {
+ return false;
+ }
+
+ if (aString == "[object Date]") {
+ return a.valueOf() === b.valueOf();
+ }
+
+ var prop, aLength = 0, bLength = 0;
+
+ if (aString == "[object Array]" && a.length !== b.length) {
+ return false;
+ }
+
+ for (prop in a) {
+ aLength += 1;
+
+ if (!(prop in b)) {
+ return false;
+ }
+
+ if (!deepEqual(a[prop], b[prop])) {
+ return false;
+ }
+ }
+
+ for (prop in b) {
+ bLength += 1;
+ }
+
+ return aLength == bLength;
+ };
+
+ sinon.functionName = function functionName(func) {
+ var name = func.displayName || func.name;
+
+ // Use function decomposition as a last resort to get function
+ // name. Does not rely on function decomposition to work - if it
+ // doesn't debugging will be slightly less informative
+ // (i.e. toString will say 'spy' rather than 'myFunc').
+ if (!name) {
+ var matches = func.toString().match(/function ([^\s\(]+)/);
+ name = matches && matches[1];
+ }
+
+ return name;
+ };
+
+ sinon.functionToString = function toString() {
+ if (this.getCall && this.callCount) {
+ var thisValue, prop, i = this.callCount;
+
+ while (i--) {
+ thisValue = this.getCall(i).thisValue;
+
+ for (prop in thisValue) {
+ if (thisValue[prop] === this) {
+ return prop;
+ }
+ }
+ }
+ }
+
+ return this.displayName || "sinon fake";
+ };
+
+ sinon.getConfig = function (custom) {
+ var config = {};
+ custom = custom || {};
+ var defaults = sinon.defaultConfig;
+
+ for (var prop in defaults) {
+ if (defaults.hasOwnProperty(prop)) {
+ config[prop] = custom.hasOwnProperty(prop) ? custom[prop] : defaults[prop];
+ }
+ }
+
+ return config;
+ };
+
+ sinon.defaultConfig = {
+ injectIntoThis: true,
+ injectInto: null,
+ properties: ["spy", "stub", "mock", "clock", "server", "requests"],
+ useFakeTimers: true,
+ useFakeServer: true
+ };
+
+ sinon.timesInWords = function timesInWords(count) {
+ return count == 1 && "once" ||
+ count == 2 && "twice" ||
+ count == 3 && "thrice" ||
+ (count || 0) + " times";
+ };
+
+ sinon.calledInOrder = function (spies) {
+ for (var i = 1, l = spies.length; i < l; i++) {
+ if (!spies[i - 1].calledBefore(spies[i]) || !spies[i].called) {
+ return false;
+ }
+ }
+
+ return true;
+ };
+
+ sinon.orderByFirstCall = function (spies) {
+ return spies.sort(function (a, b) {
+ // uuid, won't ever be equal
+ var aCall = a.getCall(0);
+ var bCall = b.getCall(0);
+ var aId = aCall && aCall.callId || -1;
+ var bId = bCall && bCall.callId || -1;
+
+ return aId < bId ? -1 : 1;
+ });
+ };
+
+ sinon.createStubInstance = function (constructor) {
+ if (typeof constructor !== "function") {
+ throw new TypeError("The constructor should be a function.");
+ }
+ return sinon.stub(sinon.create(constructor.prototype));
+ };
+
+ sinon.restore = function (object) {
+ if (object !== null && typeof object === "object") {
+ for (var prop in object) {
+ if (isRestorable(object[prop])) {
+ object[prop].restore();
+ }
+ }
+ } else if (isRestorable(object)) {
+ object.restore();
+ }
+ };
+
+ return sinon;
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ function loadDependencies(require, exports) {
+ makeApi(exports);
+ }
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports);
+ } else if (!sinon) {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+}(typeof sinon == "object" && sinon || null));
+
+/**
+ * @depend util/core.js
+ */
+
+(function (sinon) {
+ function makeApi(sinon) {
+
+ // Adapted from https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug
+ var hasDontEnumBug = (function () {
+ var obj = {
+ constructor: function () {
+ return "0";
+ },
+ toString: function () {
+ return "1";
+ },
+ valueOf: function () {
+ return "2";
+ },
+ toLocaleString: function () {
+ return "3";
+ },
+ prototype: function () {
+ return "4";
+ },
+ isPrototypeOf: function () {
+ return "5";
+ },
+ propertyIsEnumerable: function () {
+ return "6";
+ },
+ hasOwnProperty: function () {
+ return "7";
+ },
+ length: function () {
+ return "8";
+ },
+ unique: function () {
+ return "9"
+ }
+ };
+
+ var result = [];
+ for (var prop in obj) {
+ result.push(obj[prop]());
+ }
+ return result.join("") !== "0123456789";
+ })();
+
+ /* Public: Extend target in place with all (own) properties from sources in-order. Thus, last source will
+ * override properties in previous sources.
+ *
+ * target - The Object to extend
+ * sources - Objects to copy properties from.
+ *
+ * Returns the extended target
+ */
+ function extend(target /*, sources */) {
+ var sources = Array.prototype.slice.call(arguments, 1),
+ source, i, prop;
+
+ for (i = 0; i < sources.length; i++) {
+ source = sources[i];
+
+ for (prop in source) {
+ if (source.hasOwnProperty(prop)) {
+ target[prop] = source[prop];
+ }
+ }
+
+ // Make sure we copy (own) toString method even when in JScript with DontEnum bug
+ // See https://developer.mozilla.org/en/docs/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug
+ if (hasDontEnumBug && source.hasOwnProperty("toString") && source.toString !== target.toString) {
+ target.toString = source.toString;
+ }
+ }
+
+ return target;
+ };
+
+ sinon.extend = extend;
+ return sinon.extend;
+ }
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./util/core");
+ module.exports = makeApi(sinon);
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else if (!sinon) {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+}(typeof sinon == "object" && sinon || null));
+
+/**
+ * @depend util/core.js
+ */
+
+(function (sinon) {
+ function makeApi(sinon) {
+
+ function timesInWords(count) {
+ switch (count) {
+ case 1:
+ return "once";
+ case 2:
+ return "twice";
+ case 3:
+ return "thrice";
+ default:
+ return (count || 0) + " times";
+ }
+ }
+
+ sinon.timesInWords = timesInWords;
+ return sinon.timesInWords;
+ }
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./util/core");
+ module.exports = makeApi(sinon);
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else if (!sinon) {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+}(typeof sinon == "object" && sinon || null));
+
+/**
+ * @depend util/core.js
+ */
+/**
+ * Format functions
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2014 Christian Johansen
+ */
+
+(function (sinon, formatio) {
+ function makeApi(sinon) {
+ function typeOf(value) {
+ if (value === null) {
+ return "null";
+ } else if (value === undefined) {
+ return "undefined";
+ }
+ var string = Object.prototype.toString.call(value);
+ return string.substring(8, string.length - 1).toLowerCase();
+ };
+
+ sinon.typeOf = typeOf;
+ return sinon.typeOf;
+ }
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./util/core");
+ module.exports = makeApi(sinon);
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else if (!sinon) {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+}(
+ (typeof sinon == "object" && sinon || null),
+ (typeof formatio == "object" && formatio)
+));
+
+/**
+ * @depend util/core.js
+ * @depend typeOf.js
+ */
+/*jslint eqeqeq: false, onevar: false, plusplus: false*/
+/*global module, require, sinon*/
+/**
+ * Match functions
+ *
+ * @author Maximilian Antoni (mail@maxantoni.de)
+ * @license BSD
+ *
+ * Copyright (c) 2012 Maximilian Antoni
+ */
+
+(function (sinon) {
+ function makeApi(sinon) {
+ function assertType(value, type, name) {
+ var actual = sinon.typeOf(value);
+ if (actual !== type) {
+ throw new TypeError("Expected type of " + name + " to be " +
+ type + ", but was " + actual);
+ }
+ }
+
+ var matcher = {
+ toString: function () {
+ return this.message;
+ }
+ };
+
+ function isMatcher(object) {
+ return matcher.isPrototypeOf(object);
+ }
+
+ function matchObject(expectation, actual) {
+ if (actual === null || actual === undefined) {
+ return false;
+ }
+ for (var key in expectation) {
+ if (expectation.hasOwnProperty(key)) {
+ var exp = expectation[key];
+ var act = actual[key];
+ if (match.isMatcher(exp)) {
+ if (!exp.test(act)) {
+ return false;
+ }
+ } else if (sinon.typeOf(exp) === "object") {
+ if (!matchObject(exp, act)) {
+ return false;
+ }
+ } else if (!sinon.deepEqual(exp, act)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ matcher.or = function (m2) {
+ if (!arguments.length) {
+ throw new TypeError("Matcher expected");
+ } else if (!isMatcher(m2)) {
+ m2 = match(m2);
+ }
+ var m1 = this;
+ var or = sinon.create(matcher);
+ or.test = function (actual) {
+ return m1.test(actual) || m2.test(actual);
+ };
+ or.message = m1.message + ".or(" + m2.message + ")";
+ return or;
+ };
+
+ matcher.and = function (m2) {
+ if (!arguments.length) {
+ throw new TypeError("Matcher expected");
+ } else if (!isMatcher(m2)) {
+ m2 = match(m2);
+ }
+ var m1 = this;
+ var and = sinon.create(matcher);
+ and.test = function (actual) {
+ return m1.test(actual) && m2.test(actual);
+ };
+ and.message = m1.message + ".and(" + m2.message + ")";
+ return and;
+ };
+
+ var match = function (expectation, message) {
+ var m = sinon.create(matcher);
+ var type = sinon.typeOf(expectation);
+ switch (type) {
+ case "object":
+ if (typeof expectation.test === "function") {
+ m.test = function (actual) {
+ return expectation.test(actual) === true;
+ };
+ m.message = "match(" + sinon.functionName(expectation.test) + ")";
+ return m;
+ }
+ var str = [];
+ for (var key in expectation) {
+ if (expectation.hasOwnProperty(key)) {
+ str.push(key + ": " + expectation[key]);
+ }
+ }
+ m.test = function (actual) {
+ return matchObject(expectation, actual);
+ };
+ m.message = "match(" + str.join(", ") + ")";
+ break;
+ case "number":
+ m.test = function (actual) {
+ return expectation == actual;
+ };
+ break;
+ case "string":
+ m.test = function (actual) {
+ if (typeof actual !== "string") {
+ return false;
+ }
+ return actual.indexOf(expectation) !== -1;
+ };
+ m.message = "match(\"" + expectation + "\")";
+ break;
+ case "regexp":
+ m.test = function (actual) {
+ if (typeof actual !== "string") {
+ return false;
+ }
+ return expectation.test(actual);
+ };
+ break;
+ case "function":
+ m.test = expectation;
+ if (message) {
+ m.message = message;
+ } else {
+ m.message = "match(" + sinon.functionName(expectation) + ")";
+ }
+ break;
+ default:
+ m.test = function (actual) {
+ return sinon.deepEqual(expectation, actual);
+ };
+ }
+ if (!m.message) {
+ m.message = "match(" + expectation + ")";
+ }
+ return m;
+ };
+
+ match.isMatcher = isMatcher;
+
+ match.any = match(function () {
+ return true;
+ }, "any");
+
+ match.defined = match(function (actual) {
+ return actual !== null && actual !== undefined;
+ }, "defined");
+
+ match.truthy = match(function (actual) {
+ return !!actual;
+ }, "truthy");
+
+ match.falsy = match(function (actual) {
+ return !actual;
+ }, "falsy");
+
+ match.same = function (expectation) {
+ return match(function (actual) {
+ return expectation === actual;
+ }, "same(" + expectation + ")");
+ };
+
+ match.typeOf = function (type) {
+ assertType(type, "string", "type");
+ return match(function (actual) {
+ return sinon.typeOf(actual) === type;
+ }, "typeOf(\"" + type + "\")");
+ };
+
+ match.instanceOf = function (type) {
+ assertType(type, "function", "type");
+ return match(function (actual) {
+ return actual instanceof type;
+ }, "instanceOf(" + sinon.functionName(type) + ")");
+ };
+
+ function createPropertyMatcher(propertyTest, messagePrefix) {
+ return function (property, value) {
+ assertType(property, "string", "property");
+ var onlyProperty = arguments.length === 1;
+ var message = messagePrefix + "(\"" + property + "\"";
+ if (!onlyProperty) {
+ message += ", " + value;
+ }
+ message += ")";
+ return match(function (actual) {
+ if (actual === undefined || actual === null ||
+ !propertyTest(actual, property)) {
+ return false;
+ }
+ return onlyProperty || sinon.deepEqual(value, actual[property]);
+ }, message);
+ };
+ }
+
+ match.has = createPropertyMatcher(function (actual, property) {
+ if (typeof actual === "object") {
+ return property in actual;
+ }
+ return actual[property] !== undefined;
+ }, "has");
+
+ match.hasOwn = createPropertyMatcher(function (actual, property) {
+ return actual.hasOwnProperty(property);
+ }, "hasOwn");
+
+ match.bool = match.typeOf("boolean");
+ match.number = match.typeOf("number");
+ match.string = match.typeOf("string");
+ match.object = match.typeOf("object");
+ match.func = match.typeOf("function");
+ match.array = match.typeOf("array");
+ match.regexp = match.typeOf("regexp");
+ match.date = match.typeOf("date");
+
+ sinon.match = match;
+ return match;
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./util/core");
+ require("./typeOf");
+ module.exports = makeApi(sinon);
+ }
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else if (!sinon) {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+}(typeof sinon == "object" && sinon || null));
+
+/**
+ * @depend util/core.js
+ */
+/**
+ * Format functions
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2014 Christian Johansen
+ */
+
+(function (sinon, formatio) {
+ function makeApi(sinon) {
+ function valueFormatter(value) {
+ return "" + value;
+ }
+
+ function getFormatioFormatter() {
+ var formatter = formatio.configure({
+ quoteStrings: false,
+ limitChildrenCount: 250
+ });
+
+ function format() {
+ return formatter.ascii.apply(formatter, arguments);
+ };
+
+ return format;
+ }
+
+ function getNodeFormatter(value) {
+ function format(value) {
+ return typeof value == "object" && value.toString === Object.prototype.toString ? util.inspect(value) : value;
+ };
+
+ try {
+ var util = require("util");
+ } catch (e) {
+ /* Node, but no util module - would be very old, but better safe than sorry */
+ }
+
+ return util ? format : valueFormatter;
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function",
+ formatter;
+
+ if (isNode) {
+ try {
+ formatio = require("formatio");
+ } catch (e) {}
+ }
+
+ if (formatio) {
+ formatter = getFormatioFormatter()
+ } else if (isNode) {
+ formatter = getNodeFormatter();
+ } else {
+ formatter = valueFormatter;
+ }
+
+ sinon.format = formatter;
+ return sinon.format;
+ }
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./util/core");
+ module.exports = makeApi(sinon);
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else if (!sinon) {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+}(
+ (typeof sinon == "object" && sinon || null),
+ (typeof formatio == "object" && formatio)
+));
+
+/**
+ * @depend util/core.js
+ * @depend match.js
+ * @depend format.js
+ */
+/**
+ * Spy calls
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @author Maximilian Antoni (mail@maxantoni.de)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ * Copyright (c) 2013 Maximilian Antoni
+ */
+
+(function (sinon) {
+ function makeApi(sinon) {
+ function throwYieldError(proxy, text, args) {
+ var msg = sinon.functionName(proxy) + text;
+ if (args.length) {
+ msg += " Received [" + slice.call(args).join(", ") + "]";
+ }
+ throw new Error(msg);
+ }
+
+ var slice = Array.prototype.slice;
+
+ var callProto = {
+ calledOn: function calledOn(thisValue) {
+ if (sinon.match && sinon.match.isMatcher(thisValue)) {
+ return thisValue.test(this.thisValue);
+ }
+ return this.thisValue === thisValue;
+ },
+
+ calledWith: function calledWith() {
+ var l = arguments.length;
+ if (l > this.args.length) {
+ return false;
+ }
+ for (var i = 0; i < l; i += 1) {
+ if (!sinon.deepEqual(arguments[i], this.args[i])) {
+ return false;
+ }
+ }
+
+ return true;
+ },
+
+ calledWithMatch: function calledWithMatch() {
+ var l = arguments.length;
+ if (l > this.args.length) {
+ return false;
+ }
+ for (var i = 0; i < l; i += 1) {
+ var actual = this.args[i];
+ var expectation = arguments[i];
+ if (!sinon.match || !sinon.match(expectation).test(actual)) {
+ return false;
+ }
+ }
+ return true;
+ },
+
+ calledWithExactly: function calledWithExactly() {
+ return arguments.length == this.args.length &&
+ this.calledWith.apply(this, arguments);
+ },
+
+ notCalledWith: function notCalledWith() {
+ return !this.calledWith.apply(this, arguments);
+ },
+
+ notCalledWithMatch: function notCalledWithMatch() {
+ return !this.calledWithMatch.apply(this, arguments);
+ },
+
+ returned: function returned(value) {
+ return sinon.deepEqual(value, this.returnValue);
+ },
+
+ threw: function threw(error) {
+ if (typeof error === "undefined" || !this.exception) {
+ return !!this.exception;
+ }
+
+ return this.exception === error || this.exception.name === error;
+ },
+
+ calledWithNew: function calledWithNew() {
+ return this.proxy.prototype && this.thisValue instanceof this.proxy;
+ },
+
+ calledBefore: function (other) {
+ return this.callId < other.callId;
+ },
+
+ calledAfter: function (other) {
+ return this.callId > other.callId;
+ },
+
+ callArg: function (pos) {
+ this.args[pos]();
+ },
+
+ callArgOn: function (pos, thisValue) {
+ this.args[pos].apply(thisValue);
+ },
+
+ callArgWith: function (pos) {
+ this.callArgOnWith.apply(this, [pos, null].concat(slice.call(arguments, 1)));
+ },
+
+ callArgOnWith: function (pos, thisValue) {
+ var args = slice.call(arguments, 2);
+ this.args[pos].apply(thisValue, args);
+ },
+
+ yield: function () {
+ this.yieldOn.apply(this, [null].concat(slice.call(arguments, 0)));
+ },
+
+ yieldOn: function (thisValue) {
+ var args = this.args;
+ for (var i = 0, l = args.length; i < l; ++i) {
+ if (typeof args[i] === "function") {
+ args[i].apply(thisValue, slice.call(arguments, 1));
+ return;
+ }
+ }
+ throwYieldError(this.proxy, " cannot yield since no callback was passed.", args);
+ },
+
+ yieldTo: function (prop) {
+ this.yieldToOn.apply(this, [prop, null].concat(slice.call(arguments, 1)));
+ },
+
+ yieldToOn: function (prop, thisValue) {
+ var args = this.args;
+ for (var i = 0, l = args.length; i < l; ++i) {
+ if (args[i] && typeof args[i][prop] === "function") {
+ args[i][prop].apply(thisValue, slice.call(arguments, 2));
+ return;
+ }
+ }
+ throwYieldError(this.proxy, " cannot yield to '" + prop +
+ "' since no callback was passed.", args);
+ },
+
+ toString: function () {
+ var callStr = this.proxy.toString() + "(";
+ var args = [];
+
+ for (var i = 0, l = this.args.length; i < l; ++i) {
+ args.push(sinon.format(this.args[i]));
+ }
+
+ callStr = callStr + args.join(", ") + ")";
+
+ if (typeof this.returnValue != "undefined") {
+ callStr += " => " + sinon.format(this.returnValue);
+ }
+
+ if (this.exception) {
+ callStr += " !" + this.exception.name;
+
+ if (this.exception.message) {
+ callStr += "(" + this.exception.message + ")";
+ }
+ }
+
+ return callStr;
+ }
+ };
+
+ callProto.invokeCallback = callProto.yield;
+
+ function createSpyCall(spy, thisValue, args, returnValue, exception, id) {
+ if (typeof id !== "number") {
+ throw new TypeError("Call id is not a number");
+ }
+ var proxyCall = sinon.create(callProto);
+ proxyCall.proxy = spy;
+ proxyCall.thisValue = thisValue;
+ proxyCall.args = args;
+ proxyCall.returnValue = returnValue;
+ proxyCall.exception = exception;
+ proxyCall.callId = id;
+
+ return proxyCall;
+ }
+ createSpyCall.toString = callProto.toString; // used by mocks
+
+ sinon.spyCall = createSpyCall;
+ return createSpyCall;
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./util/core");
+ require("./match");
+ require("./format");
+ module.exports = makeApi(sinon);
+ }
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else if (!sinon) {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+}(typeof sinon == "object" && sinon || null));
+
+/**
+ * @depend times_in_words.js
+ * @depend util/core.js
+ * @depend extend.js
+ * @depend call.js
+ * @depend format.js
+ */
+/**
+ * Spy functions
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+(function (sinon) {
+ function makeApi(sinon) {
+ var push = Array.prototype.push;
+ var slice = Array.prototype.slice;
+ var callId = 0;
+
+ function spy(object, property) {
+ if (!property && typeof object == "function") {
+ return spy.create(object);
+ }
+
+ if (!object && !property) {
+ return spy.create(function () { });
+ }
+
+ var method = object[property];
+ return sinon.wrapMethod(object, property, spy.create(method));
+ }
+
+ function matchingFake(fakes, args, strict) {
+ if (!fakes) {
+ return;
+ }
+
+ for (var i = 0, l = fakes.length; i < l; i++) {
+ if (fakes[i].matches(args, strict)) {
+ return fakes[i];
+ }
+ }
+ }
+
+ function incrementCallCount() {
+ this.called = true;
+ this.callCount += 1;
+ this.notCalled = false;
+ this.calledOnce = this.callCount == 1;
+ this.calledTwice = this.callCount == 2;
+ this.calledThrice = this.callCount == 3;
+ }
+
+ function createCallProperties() {
+ this.firstCall = this.getCall(0);
+ this.secondCall = this.getCall(1);
+ this.thirdCall = this.getCall(2);
+ this.lastCall = this.getCall(this.callCount - 1);
+ }
+
+ var vars = "a,b,c,d,e,f,g,h,i,j,k,l";
+ function createProxy(func, proxyLength) {
+ // Retain the function length:
+ var p;
+ if (proxyLength) {
+ eval("p = (function proxy(" + vars.substring(0, proxyLength * 2 - 1) +
+ ") { return p.invoke(func, this, slice.call(arguments)); });");
+ } else {
+ p = function proxy() {
+ return p.invoke(func, this, slice.call(arguments));
+ };
+ }
+ return p;
+ }
+
+ var uuid = 0;
+
+ // Public API
+ var spyApi = {
+ reset: function () {
+ if (this.invoking) {
+ var err = new Error("Cannot reset Sinon function while invoking it. " +
+ "Move the call to .reset outside of the callback.");
+ err.name = "InvalidResetException";
+ throw err;
+ }
+
+ this.called = false;
+ this.notCalled = true;
+ this.calledOnce = false;
+ this.calledTwice = false;
+ this.calledThrice = false;
+ this.callCount = 0;
+ this.firstCall = null;
+ this.secondCall = null;
+ this.thirdCall = null;
+ this.lastCall = null;
+ this.args = [];
+ this.returnValues = [];
+ this.thisValues = [];
+ this.exceptions = [];
+ this.callIds = [];
+ if (this.fakes) {
+ for (var i = 0; i < this.fakes.length; i++) {
+ this.fakes[i].reset();
+ }
+ }
+
+ return this;
+ },
+
+ create: function create(func, spyLength) {
+ var name;
+
+ if (typeof func != "function") {
+ func = function () { };
+ } else {
+ name = sinon.functionName(func);
+ }
+
+ if (!spyLength) {
+ spyLength = func.length;
+ }
+
+ var proxy = createProxy(func, spyLength);
+
+ sinon.extend(proxy, spy);
+ delete proxy.create;
+ sinon.extend(proxy, func);
+
+ proxy.reset();
+ proxy.prototype = func.prototype;
+ proxy.displayName = name || "spy";
+ proxy.toString = sinon.functionToString;
+ proxy.instantiateFake = sinon.spy.create;
+ proxy.id = "spy#" + uuid++;
+
+ return proxy;
+ },
+
+ invoke: function invoke(func, thisValue, args) {
+ var matching = matchingFake(this.fakes, args);
+ var exception, returnValue;
+
+ incrementCallCount.call(this);
+ push.call(this.thisValues, thisValue);
+ push.call(this.args, args);
+ push.call(this.callIds, callId++);
+
+ // Make call properties available from within the spied function:
+ createCallProperties.call(this);
+
+ try {
+ this.invoking = true;
+
+ if (matching) {
+ returnValue = matching.invoke(func, thisValue, args);
+ } else {
+ returnValue = (this.func || func).apply(thisValue, args);
+ }
+
+ var thisCall = this.getCall(this.callCount - 1);
+ if (thisCall.calledWithNew() && typeof returnValue !== "object") {
+ returnValue = thisValue;
+ }
+ } catch (e) {
+ exception = e;
+ } finally {
+ delete this.invoking;
+ }
+
+ push.call(this.exceptions, exception);
+ push.call(this.returnValues, returnValue);
+
+ // Make return value and exception available in the calls:
+ createCallProperties.call(this);
+
+ if (exception !== undefined) {
+ throw exception;
+ }
+
+ return returnValue;
+ },
+
+ named: function named(name) {
+ this.displayName = name;
+ return this;
+ },
+
+ getCall: function getCall(i) {
+ if (i < 0 || i >= this.callCount) {
+ return null;
+ }
+
+ return sinon.spyCall(this, this.thisValues[i], this.args[i],
+ this.returnValues[i], this.exceptions[i],
+ this.callIds[i]);
+ },
+
+ getCalls: function () {
+ var calls = [];
+ var i;
+
+ for (i = 0; i < this.callCount; i++) {
+ calls.push(this.getCall(i));
+ }
+
+ return calls;
+ },
+
+ calledBefore: function calledBefore(spyFn) {
+ if (!this.called) {
+ return false;
+ }
+
+ if (!spyFn.called) {
+ return true;
+ }
+
+ return this.callIds[0] < spyFn.callIds[spyFn.callIds.length - 1];
+ },
+
+ calledAfter: function calledAfter(spyFn) {
+ if (!this.called || !spyFn.called) {
+ return false;
+ }
+
+ return this.callIds[this.callCount - 1] > spyFn.callIds[spyFn.callCount - 1];
+ },
+
+ withArgs: function () {
+ var args = slice.call(arguments);
+
+ if (this.fakes) {
+ var match = matchingFake(this.fakes, args, true);
+
+ if (match) {
+ return match;
+ }
+ } else {
+ this.fakes = [];
+ }
+
+ var original = this;
+ var fake = this.instantiateFake();
+ fake.matchingAguments = args;
+ fake.parent = this;
+ push.call(this.fakes, fake);
+
+ fake.withArgs = function () {
+ return original.withArgs.apply(original, arguments);
+ };
+
+ for (var i = 0; i < this.args.length; i++) {
+ if (fake.matches(this.args[i])) {
+ incrementCallCount.call(fake);
+ push.call(fake.thisValues, this.thisValues[i]);
+ push.call(fake.args, this.args[i]);
+ push.call(fake.returnValues, this.returnValues[i]);
+ push.call(fake.exceptions, this.exceptions[i]);
+ push.call(fake.callIds, this.callIds[i]);
+ }
+ }
+ createCallProperties.call(fake);
+
+ return fake;
+ },
+
+ matches: function (args, strict) {
+ var margs = this.matchingAguments;
+
+ if (margs.length <= args.length &&
+ sinon.deepEqual(margs, args.slice(0, margs.length))) {
+ return !strict || margs.length == args.length;
+ }
+ },
+
+ printf: function (format) {
+ var spy = this;
+ var args = slice.call(arguments, 1);
+ var formatter;
+
+ return (format || "").replace(/%(.)/g, function (match, specifyer) {
+ formatter = spyApi.formatters[specifyer];
+
+ if (typeof formatter == "function") {
+ return formatter.call(null, spy, args);
+ } else if (!isNaN(parseInt(specifyer, 10))) {
+ return sinon.format(args[specifyer - 1]);
+ }
+
+ return "%" + specifyer;
+ });
+ }
+ };
+
+ function delegateToCalls(method, matchAny, actual, notCalled) {
+ spyApi[method] = function () {
+ if (!this.called) {
+ if (notCalled) {
+ return notCalled.apply(this, arguments);
+ }
+ return false;
+ }
+
+ var currentCall;
+ var matches = 0;
+
+ for (var i = 0, l = this.callCount; i < l; i += 1) {
+ currentCall = this.getCall(i);
+
+ if (currentCall[actual || method].apply(currentCall, arguments)) {
+ matches += 1;
+
+ if (matchAny) {
+ return true;
+ }
+ }
+ }
+
+ return matches === this.callCount;
+ };
+ }
+
+ delegateToCalls("calledOn", true);
+ delegateToCalls("alwaysCalledOn", false, "calledOn");
+ delegateToCalls("calledWith", true);
+ delegateToCalls("calledWithMatch", true);
+ delegateToCalls("alwaysCalledWith", false, "calledWith");
+ delegateToCalls("alwaysCalledWithMatch", false, "calledWithMatch");
+ delegateToCalls("calledWithExactly", true);
+ delegateToCalls("alwaysCalledWithExactly", false, "calledWithExactly");
+ delegateToCalls("neverCalledWith", false, "notCalledWith",
+ function () { return true; });
+ delegateToCalls("neverCalledWithMatch", false, "notCalledWithMatch",
+ function () { return true; });
+ delegateToCalls("threw", true);
+ delegateToCalls("alwaysThrew", false, "threw");
+ delegateToCalls("returned", true);
+ delegateToCalls("alwaysReturned", false, "returned");
+ delegateToCalls("calledWithNew", true);
+ delegateToCalls("alwaysCalledWithNew", false, "calledWithNew");
+ delegateToCalls("callArg", false, "callArgWith", function () {
+ throw new Error(this.toString() + " cannot call arg since it was not yet invoked.");
+ });
+ spyApi.callArgWith = spyApi.callArg;
+ delegateToCalls("callArgOn", false, "callArgOnWith", function () {
+ throw new Error(this.toString() + " cannot call arg since it was not yet invoked.");
+ });
+ spyApi.callArgOnWith = spyApi.callArgOn;
+ delegateToCalls("yield", false, "yield", function () {
+ throw new Error(this.toString() + " cannot yield since it was not yet invoked.");
+ });
+ // "invokeCallback" is an alias for "yield" since "yield" is invalid in strict mode.
+ spyApi.invokeCallback = spyApi.yield;
+ delegateToCalls("yieldOn", false, "yieldOn", function () {
+ throw new Error(this.toString() + " cannot yield since it was not yet invoked.");
+ });
+ delegateToCalls("yieldTo", false, "yieldTo", function (property) {
+ throw new Error(this.toString() + " cannot yield to '" + property +
+ "' since it was not yet invoked.");
+ });
+ delegateToCalls("yieldToOn", false, "yieldToOn", function (property) {
+ throw new Error(this.toString() + " cannot yield to '" + property +
+ "' since it was not yet invoked.");
+ });
+
+ spyApi.formatters = {
+ c: function (spy) {
+ return sinon.timesInWords(spy.callCount);
+ },
+
+ n: function (spy) {
+ return spy.toString();
+ },
+
+ C: function (spy) {
+ var calls = [];
+
+ for (var i = 0, l = spy.callCount; i < l; ++i) {
+ var stringifiedCall = " " + spy.getCall(i).toString();
+ if (/\n/.test(calls[i - 1])) {
+ stringifiedCall = "\n" + stringifiedCall;
+ }
+ push.call(calls, stringifiedCall);
+ }
+
+ return calls.length > 0 ? "\n" + calls.join("\n") : "";
+ },
+
+ t: function (spy) {
+ var objects = [];
+
+ for (var i = 0, l = spy.callCount; i < l; ++i) {
+ push.call(objects, sinon.format(spy.thisValues[i]));
+ }
+
+ return objects.join(", ");
+ },
+
+ "*": function (spy, args) {
+ var formatted = [];
+
+ for (var i = 0, l = args.length; i < l; ++i) {
+ push.call(formatted, sinon.format(args[i]));
+ }
+
+ return formatted.join(", ");
+ }
+ };
+
+ sinon.extend(spy, spyApi);
+
+ spy.spyCall = sinon.spyCall;
+ sinon.spy = spy;
+
+ return spy;
+ }
+
+ var isNode = typeof module !== "undefined" && module.exports && typeof require == "function";
+ var isAMD = typeof define === "function" && typeof define.amd === "object" && define.amd;
+
+ function loadDependencies(require, exports, module) {
+ var sinon = require("./util/core");
+ require("./call");
+ require("./extend");
+ require("./times_in_words");
+ require("./format");
+ module.exports = makeApi(sinon);
+ }
+
+ if (isAMD) {
+ define(loadDependencies);
+ } else if (isNode) {
+ loadDependencies(require, module.exports, module);
+ } else if (!sinon) {
+ return;
+ } else {
+ makeApi(sinon);
+ }
+}(typeof sinon == "object" && sinon || null));
+
+/**
+ * @depend util/core.js
+ * @depend extend.js
+ */
+/**
+ * Stub behavior
+ *
+ * @author Christian Johansen (christian@cjohansen.no)
+ * @author Tim Fischbach (mail@timfischbach.de)
+ * @license BSD
+ *
+ * Copyright (c) 2010-2013 Christian Johansen
+ */
+
+(function (sinon) {
+ var slice = Array.prototype.slice;
+ var join = Array.prototype.join;
+ var useLeftMostCallback = -1;
+ var useRightMostCallback = -2;
+
+ var nextTick = (function () {
+ if (typeof process === "object" && typeof process.nextTick === "function") {
+ return process.nextTick;
+ } else if (typeof setImmediate === "function") {
+ return setImmediate;
+ } else {
+ return function (callback) {
+ setTimeout(callback, 0);
+ };
+ }
+ })();
+
+ function throwsException(error, message) {
+ if (typeof error == "string") {
+ this.exception = new Error(message || "");
+ this.exception.name = error;
+ } else if (!error) {
+ this.exception = new Error("Error");
+ } else {
+ this.exception = error;
+ }
+
+ return this;
+ }
+
+ function getCallback(behavior, args) {
+ var callArgAt = behavior.callArgAt;
+
+ if (callArgAt >= 0) {
+ return args[callArgAt];
+ }
+
+ var argumentList;
+
+ if (callArgAt === useLeftMostCallback) {
+ argumentList = args;
+ }
+
+ if (callArgAt === useRightMostCallback) {
+ argumentList = slice.call(args).reverse();
+ }
+
+ var callArgProp = behavior.callArgProp;
+
+ for (var i = 0, l = argumentList.length; i < l; ++i) {
+ if (!callArgProp && typeof argumentList[i] == "function") {
+ return argumentList[i];
+ }
+
+ if (callArgProp && argumentList[i] &&
+ typeof argumentList[i][callArgProp] == "function") {
+ return argumentList[i][callArgProp];
+ }
+ }
+
+ return null;
+ }
+
+
<TRUNCATED>
[2/4] ambari git commit: AMBARI-10077. Add UI unit tests for Create
App Wizard controllers (onechiporenko)
Posted by on...@apache.org.
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 () {