This is an automated email from the ASF dual-hosted git repository.
atkach pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git
The following commit(s) were added to refs/heads/trunk by this push:
new 98457d1 AMBARI-25140 Cover widget mixin with unit tests
98457d1 is described below
commit 98457d182d11948820cde1d5c1b47b4ee3bb60d7
Author: Andrii Tkach <[email protected]>
AuthorDate: Mon Feb 4 10:09:56 2019 +0200
AMBARI-25140 Cover widget mixin with unit tests
---
.../app/mixins/common/widgets/widget_mixin.js | 60 +--
ambari-web/test/mixins/common/widget_mixin_test.js | 481 ++++++++++++++++++++-
2 files changed, 498 insertions(+), 43 deletions(-)
diff --git a/ambari-web/app/mixins/common/widgets/widget_mixin.js
b/ambari-web/app/mixins/common/widgets/widget_mixin.js
index 6486856..11ae6bc 100644
--- a/ambari-web/app/mixins/common/widgets/widget_mixin.js
+++ b/ambari-web/app/mixins/common/widgets/widget_mixin.js
@@ -263,7 +263,7 @@ App.WidgetMixin = Ember.Mixin.create({
};
if (request.tag) {
- data.selectedHostsParam = '&HostRoles/host_name.in(' +
App.HDFSService.find().objectAt(0).get('masterComponentGroups').findProperty('name',
request.tag).hosts.join(',') + ')';
+ data.selectedHostsParam = '&HostRoles/host_name.in(' +
App.HDFSService.find('HDFS').get('masterComponentGroups').findProperty('name',
request.tag).hosts.join(',') + ')';
}
if (metricPaths.length) {
@@ -293,30 +293,15 @@ App.WidgetMixin = Ember.Mixin.create({
* @param data
*/
getMetricsSuccessCallback: function (data) {
- var metrics = [];
var atLeastOneMetricPresent = false;
if (this.get('content.metrics')) {
this.get('content.metrics').forEach(function (_metric) {
- var metric_path = _metric.metric_path;
- var isAggregatorFunc = false;
- var metric_data = Em.get(data, metric_path.replace(/\//g, '.'));
+ var metricPath = _metric.metric_path;
+
+ var metric_data = Em.get(data, metricPath.replace(/\//g, '.'));
if (Em.isNone(metric_data)) {
- this.aggregatorFunc.forEach(function (_item) {
- if (metric_path.endsWith(_item) && !isAggregatorFunc) {
- isAggregatorFunc = true;
- var metricBeanProperty = metric_path.split("/").pop();
- var metricBean;
- metric_path = metric_path.substring(0,
metric_path.indexOf(metricBeanProperty));
- if (metric_path.endsWith("/")) {
- metric_path = metric_path.slice(0, -1);
- }
- metricBean = Em.get(data, metric_path.replace(/\//g, '.'));
- if (!Em.isNone(metricBean)) {
- metric_data = metricBean[metricBeanProperty];
- }
- }
- }, this);
+ metric_data = this.parseMetricsWithAggregatorFunc(data, metricPath);
}
if (!Em.isNone(metric_data)) {
atLeastOneMetricPresent = true;
@@ -330,6 +315,28 @@ App.WidgetMixin = Ember.Mixin.create({
}
}
},
+
+ parseMetricsWithAggregatorFunc: function(data, metric_path) {
+ let isAggregatorFunc = false;
+ let metric = null;
+ this.aggregatorFunc.forEach(function (_item) {
+ if (metric_path.endsWith(_item) && !isAggregatorFunc) {
+ isAggregatorFunc = true;
+ var metricBeanProperty = metric_path.split("/").pop();
+ var metricBean;
+ metric_path = metric_path.substring(0,
metric_path.indexOf(metricBeanProperty));
+
+ if (metric_path.endsWith("/")) {
+ metric_path = metric_path.slice(0, -1);
+ }
+ metricBean = Em.get(data, metric_path.replace(/\//g, '.'));
+ if (!Em.isNone(metricBean)) {
+ metric = metricBean[metricBeanProperty];
+ }
+ }
+ }, this);
+ return metric;
+ },
/**
* if no metrics were received from server then disable graph
@@ -377,8 +384,8 @@ App.WidgetMixin = Ember.Mixin.create({
* @return {$.ajax}
*/
getHostComponentsMetrics: function (request) {
- request.metric_paths.forEach(function (_metric, index) {
- request.metric_paths[index] = "host_components/" + _metric.metric_path;
+ request.metric_paths = request.metric_paths.map((_metric) => {
+ return "host_components/" + _metric.metric_path;
});
return App.ajax.send({
name: 'widgets.serviceComponent.metrics.get',
@@ -422,7 +429,7 @@ App.WidgetMixin = Ember.Mixin.create({
getHostsMetricsSuccessCallback: function (data) {
var metrics = this.get('content.metrics');
data.items.forEach(function (item) {
- metrics.forEach(function (_metric, index) {
+ metrics.forEach(function (_metric) {
const metric = $.extend({}, _metric, true);
metric.hostName = item.Hosts.host_name;
if (!Em.isNone(Em.get(item, _metric.metric_path.replace(/\//g, '.'))))
{
@@ -574,7 +581,7 @@ App.WidgetMixin = Ember.Mixin.create({
/*
* make call when clicking on "clone icon" on widget
*/
- cloneWidget: function (event) {
+ cloneWidget: function () {
var self = this;
return App.showConfirmationPopup(
function () {
@@ -666,8 +673,7 @@ App.WidgetMixin = Ember.Mixin.create({
var mainServiceInfoMetricsController =
App.router.get('mainServiceInfoMetricsController');
mainServiceInfoMetricsController.saveWidgetLayout(widgets).done(function()
{
mainServiceInfoMetricsController.getActiveWidgetLayout().done(function()
{
- var newWidget = App.Widget.find().findProperty('id', id);
- controller.editWidget(newWidget);
+ controller.editWidget(App.Widget.find(id));
});
});
},
@@ -675,7 +681,7 @@ App.WidgetMixin = Ember.Mixin.create({
/*
* make call when clicking on "edit icon" on widget
*/
- editWidget: function (event) {
+ editWidget: function () {
var self = this;
var isShared = this.get('content.scope') === 'CLUSTER';
if (!isShared) {
diff --git a/ambari-web/test/mixins/common/widget_mixin_test.js
b/ambari-web/test/mixins/common/widget_mixin_test.js
index fb8c5d0..a075100 100644
--- a/ambari-web/test/mixins/common/widget_mixin_test.js
+++ b/ambari-web/test/mixins/common/widget_mixin_test.js
@@ -24,7 +24,13 @@ describe('App.WidgetMixin', function () {
var mixinObject;
beforeEach(function () {
- mixinObject = mixinClass.create();
+ mixinObject = mixinClass.create({
+ content: Em.Object.create(),
+ controller: Em.Object.create({
+ hideWidget: sinon.spy(),
+ editWidget: sinon.spy()
+ })
+ });
});
afterEach(function () {
@@ -81,10 +87,20 @@ describe('App.WidgetMixin', function () {
beforeEach(function () {
this.mock = sinon.stub(mixinObject, 'getRequestData');
sinon.stub(App.WidgetLoadAggregator, 'add');
+ sinon.stub(mixinObject, 'getHostsMetrics').returns({
+ complete: Em.clb
+ });
+ sinon.stub(mixinObject, 'getHostComponentsMetrics').returns({
+ complete: Em.clb
+ });
+ sinon.stub(mixinObject, 'onMetricsLoaded');
});
afterEach(function () {
this.mock.restore();
App.WidgetLoadAggregator.add.restore();
+ mixinObject.getHostsMetrics.restore();
+ mixinObject.getHostComponentsMetrics.restore();
+ mixinObject.onMetricsLoaded.restore();
});
it('has host_component_criteria', function () {
this.mock.returns({'key1': {host_component_criteria: 'criteria'}});
@@ -100,6 +116,30 @@ describe('App.WidgetMixin', function () {
expect(App.WidgetLoadAggregator.add.calledOnce).to.be.true;
});
+
+ it('getHostsMetrics should be called', function () {
+ this.mock.returns({'key1': {
+ host_component_criteria: 'criteria',
+ service_name: 'STACK'
+ }});
+ mixinObject.set('content.widgetType', 'HEATMAP');
+ mixinObject.loadMetrics();
+
+ expect(mixinObject.getHostsMetrics.calledOnce).to.be.true;
+ expect(mixinObject.onMetricsLoaded.calledOnce).to.be.true;
+ });
+
+ it('getHostComponentsMetrics should be called', function () {
+ this.mock.returns({'key1': {
+ host_component_criteria: 'criteria',
+ service_name: 'S1'
+ }});
+ mixinObject.set('content.widgetType', 'HEATMAP');
+ mixinObject.loadMetrics();
+
+ expect(mixinObject.getHostComponentsMetrics.calledOnce).to.be.true;
+ expect(mixinObject.onMetricsLoaded.calledOnce).to.be.true;
+ });
});
describe("#extractExpressions()", function () {
@@ -264,10 +304,37 @@ describe('App.WidgetMixin', function () {
});
});
});
+
+ describe('#getHostComponentMetricsSuccessCallback', function() {
+ beforeEach(function() {
+ sinon.stub(mixinObject, 'getMetricsSuccessCallback');
+ });
+ afterEach(function() {
+ mixinObject.getMetricsSuccessCallback.restore();
+ });
+
+ it('getMetricsSuccessCallback should be called', function() {
+ mixinObject.getHostComponentMetricsSuccessCallback({items: [{}]});
+ expect(mixinObject.getMetricsSuccessCallback.calledWith({})).to.be.true;
+ });
+ });
describe("#getMetricsSuccessCallback()", function () {
+
+ beforeEach(function() {
+ sinon.stub(mixinObject, 'parseMetricsWithAggregatorFunc').returns(1);
+ });
+ afterEach(function() {
+ mixinObject.parseMetricsWithAggregatorFunc.restore();
+ });
+
it("metric is mapped from provided path", function () {
- var data = {
+ mixinObject.set('content.metrics', [
+ {
+ metric_path: 'metrics/hbase/ipc/IPC/numOpenConnections'
+ }
+ ]);
+ mixinObject.getMetricsSuccessCallback({
metrics: {
"hbase": {
"ipc": {
@@ -277,14 +344,25 @@ describe('App.WidgetMixin', function () {
}
}
}
- };
+ });
+ expect(mixinObject.get('metrics').findProperty('metric_path',
'metrics/hbase/ipc/IPC/numOpenConnections').data).to.equal(11.5);
+ });
+
+ it("parseMetricsWithAggregatorFunc should be called", function () {
mixinObject.set('content.metrics', [
{
metric_path: 'metrics/hbase/ipc/IPC/numOpenConnections'
}
]);
- mixinObject.getMetricsSuccessCallback(data);
- expect(mixinObject.get('metrics').findProperty('metric_path',
'metrics/hbase/ipc/IPC/numOpenConnections').data).to.equal(11.5);
+ mixinObject.getMetricsSuccessCallback({});
+ expect(mixinObject.parseMetricsWithAggregatorFunc.calledWith({},
'metrics/hbase/ipc/IPC/numOpenConnections')).to.be.true;
+ });
+ });
+
+ describe('#parseMetricsWithAggregatorFunc', function() {
+
+ it('should return metric value', function() {
+ expect(mixinObject.parseMetricsWithAggregatorFunc({path: {foo: {'._sum':
1}}}, 'path/foo/._sum')).to.equal(1);
});
});
@@ -377,10 +455,19 @@ describe('App.WidgetMixin', function () {
describe("#getHostComponentMetrics()", function () {
beforeEach(function () {
- sinon.stub(mixinObject,
'computeHostComponentCriteria').returns('criteria')
+ sinon.stub(mixinObject,
'computeHostComponentCriteria').returns('criteria');
+ sinon.stub(App.HDFSService, 'find').returns(Em.Object.create({
+ masterComponentGroups: [
+ {
+ name: 'tag1',
+ hosts: ['host1', 'host2']
+ }
+ ]
+ }));
});
afterEach(function () {
mixinObject.computeHostComponentCriteria.restore();
+ App.HDFSService.find.restore();
});
it("valid request is sent", function () {
var request = {
@@ -399,7 +486,8 @@ describe('App.WidgetMixin', function () {
"context": {}
}
],
- host_component_criteria: 'c1'
+ host_component_criteria: 'c1',
+ tag: 'tag1'
};
mixinObject.getHostComponentMetrics(request);
var args = testHelpers.findAjaxRequest('name',
'widgets.hostComponent.metrics.get');
@@ -408,7 +496,8 @@ describe('App.WidgetMixin', function () {
expect(args[0].data).to.be.eql({
componentName: 'C1',
metricPaths: 'w1,w2',
- hostComponentCriteria: 'criteria'
+ hostComponentCriteria: 'criteria',
+ selectedHostsParam: '&HostRoles/host_name.in(host1,host2)'
});
});
});
@@ -501,20 +590,74 @@ describe('App.WidgetMixin', function () {
describe("#postWidgetDefinition()", function () {
beforeEach(function () {
- sinon.stub(mixinObject, 'collectWidgetData').returns({});
+ sinon.stub(mixinObject, 'collectWidgetData').returns({
+ WidgetInfo: {
+ widget_name: 'widget1'
+ }
+ });
});
afterEach(function () {
mixinObject.collectWidgetData.restore();
});
- it("valid request is sent", function () {
- mixinObject.postWidgetDefinition();
- var args = testHelpers.findAjaxRequest('name', 'widgets.wizard.add');
- expect(args[0]).exists;
- expect(args[0].sender).to.be.eql(mixinObject);
- expect(args[0].data).to.be.eql({
- data: {}
+ it("Request for clone widget should be sent", function () {
+ mixinObject.postWidgetDefinition(true, true);
+ expect(testHelpers.findAjaxRequest('name',
'widgets.wizard.add')[0]).to.be.eql({
+ name: 'widgets.wizard.add',
+ sender: mixinObject,
+ data: {
+ data: {
+ WidgetInfo: {
+ widget_name: 'widget1(Copy)',
+ scope: 'USER'
+ }
+ }
+ },
+ success: 'editNewClonedWidget'
});
});
+
+ it("Request for new widget should be sent", function () {
+ mixinObject.postWidgetDefinition(false, false);
+ expect(testHelpers.findAjaxRequest('name',
'widgets.wizard.add')[0]).to.be.eql({
+ name: 'widgets.wizard.add',
+ sender: mixinObject,
+ data: {
+ data: {
+ WidgetInfo: {
+ widget_name: 'widget1'
+ }
+ }
+ },
+ success: 'postWidgetDefinitionSuccessCallback'
+ });
+ });
+ });
+
+ describe('#postWidgetDefinitionSuccessCallback', function() {
+ var mock = {
+ saveWidgetLayout: sinon.stub().returns({done: Em.clb}),
+ updateActiveLayout: sinon.spy()
+ };
+ beforeEach(function() {
+ sinon.stub(App.router, 'get').returns(mock);
+ mixinObject.set('content.layout', {widgets: []});
+ mixinObject.postWidgetDefinitionSuccessCallback({resources:
[{WidgetInfo: {id: 1}}]});
+ });
+ afterEach(function() {
+ App.router.get.restore();
+ });
+
+ it('saveWidgetLayout should be called', function() {
+ expect(mock.saveWidgetLayout.calledWith([
+ Em.Object.create({
+ id: 1
+ })
+ ])).to.be.true;
+ });
+
+ it('updateActiveLayout should be called', function() {
+ expect(mock.updateActiveLayout.called).to.be.true;
+ });
});
describe('#getMetricsErrorCallback()', function () {
@@ -674,6 +817,312 @@ describe('App.WidgetMixin', function () {
});
});
+
+ describe('#beforeRender', function() {
+ beforeEach(function() {
+ sinon.stub(mixinObject, 'loadMetrics');
+ });
+ afterEach(function() {
+ mixinObject.loadMetrics.restore();
+ });
+
+ it('loadMetrics should be called', function() {
+ mixinObject.beforeRender();
+ expect(mixinObject.loadMetrics.calledOnce).to.be.true
+; });
+ });
+
+ describe('#computeHostComponentCriteria', function() {
+
+ it('should return params', function() {
+ var request = {
+ host_component_criteria: 'host_components/param'
+ };
+
expect(mixinObject.computeHostComponentCriteria(request)).to.be.equal('¶m');
+ });
+ });
+
+ describe('#getHostComponentsMetrics', function() {
+
+ it('App.ajax.send should be called', function() {
+ mixinObject.getHostComponentsMetrics({
+ service_name: 'S1',
+ component_name: 'C1',
+ metric_paths: [{metric_path: 'key1'}, {metric_path: 'key2'}]
+ });
+ expect(testHelpers.findAjaxRequest('name',
'widgets.serviceComponent.metrics.get')[0].data).to.be.eql({
+ serviceName: 'S1',
+ componentName: 'C1',
+ metricPaths: 'host_components/key1,host_components/key2'
+ })
+ });
+ });
+
+ describe('#getHostComponentsMetricsSuccessCallback', function() {
+
+ it('should set metrics', function() {
+ var data = {
+ host_components: [
+ {
+ HostRoles: {
+ host_name: 'host1'
+ },
+ path: {
+ foo: 1
+ }
+ }
+ ]
+ };
+ mixinObject.set('content.metrics', [
+ {
+ metric_path: 'path/foo'
+ }
+ ]);
+ mixinObject.set('metrics', []);
+ mixinObject.getHostComponentsMetricsSuccessCallback(data);
+
expect(JSON.stringify(mixinObject.get('metrics'))).to.be.eql(JSON.stringify([
+ {
+ metric_path: 'path/foo',
+ hostName: 'host1',
+ data: 1
+ }
+ ]));
+ });
+ });
+
+ describe('#getHostsMetrics', function() {
+
+ it('App.ajax.send should be called', function() {
+ mixinObject.getHostsMetrics({
+ metric_paths: [{metric_path: 'key1'}, {metric_path: 'key2'}]
+ });
+ expect(testHelpers.findAjaxRequest('name',
'widgets.hosts.metrics.get')[0].data).to.be.eql({
+ metricPaths: 'key1,key2'
+ })
+ });
+ });
+
+ describe('#getHostComponentsMetricsSuccessCallback', function() {
+
+ it('should set metrics', function() {
+ var data = {
+ items: [
+ {
+ Hosts: {
+ host_name: 'host1'
+ },
+ path: {
+ foo: 1
+ }
+ }
+ ]
+ };
+ mixinObject.set('content.metrics', [
+ {
+ metric_path: 'path/foo'
+ }
+ ]);
+ mixinObject.set('metrics', []);
+ mixinObject.getHostsMetricsSuccessCallback(data);
+
expect(JSON.stringify(mixinObject.get('metrics'))).to.be.eql(JSON.stringify([
+ {
+ metric_path: 'path/foo',
+ hostName: 'host1',
+ data: 1
+ }
+ ]));
+ });
+ });
+
+
+ describe('#onMetricsLoaded', function() {
+ beforeEach(function() {
+ sinon.stub(mixinObject, 'drawWidget');
+ });
+ afterEach(function() {
+ mixinObject.drawWidget.restore();
+ });
+
+ it('drwaWidget should be called', function() {
+ mixinObject.onMetricsLoaded();
+ expect(mixinObject.drawWidget.calledOnce).to.be.true;
+ });
+
+ it('isLoaded should be true', function() {
+ mixinObject.onMetricsLoaded();
+ expect(mixinObject.get('isLoaded')).to.be.true;
+ });
+ });
+
+ describe('#drawWidget', function() {
+ beforeEach(function() {
+ sinon.stub(mixinObject, 'calculateValues');
+ mixinObject.set('isLoaded', true);
+ mixinObject.set('content.values', [{computedValue: 1}]);
+ mixinObject.drawWidget();
+ });
+ afterEach(function() {
+ mixinObject.calculateValues.restore();
+ });
+
+ it('calculateValues should be called', function() {
+ expect(mixinObject.calculateValues.calledOnce).to.be.true;
+ });
+
+ it('value should be set', function() {
+ expect(mixinObject.get('value')).to.equal(1);
+ });
+ });
+
+ describe('#hideWidget', function() {
+
+ it('hideWidget should be called', function() {
+ mixinObject.hideWidget({contexts: [1, 'layout1']});
+ expect(mixinObject.get('controller').hideWidget.calledWith({
+ context: Em.Object.create({
+ id: 1,
+ nsLayout: 'layout1'
+ })
+ })).to.be.true;
+ });
+ });
+
+ describe('#collectWidgetData', function() {
+
+ it('should return widget data', function() {
+ mixinObject.set('content', Em.Object.create({
+ widgetName: 'name1',
+ widgetType: 'type1',
+ widgetDescription: 'desc',
+ scope: 'HOST',
+ values: [1],
+ properties: [{}],
+ metrics: [
+ {
+ name: 'metric1',
+ service_name: 'S1',
+ component_name: 'C1',
+ host_component_criteria: 'criteria1',
+ metric_path: 'path/foo',
+ tag: 'tag1'
+ }
+ ]
+ }));
+ expect(mixinObject.collectWidgetData()).to.be.eql({
+ WidgetInfo: {
+ widget_name: 'name1',
+ widget_type: 'type1',
+ description: 'desc',
+ scope: 'HOST',
+ "metrics": [
+ {
+ "name": 'metric1',
+ "service_name": 'S1',
+ "component_name": 'C1',
+ "host_component_criteria": 'criteria1',
+ "metric_path": 'path/foo'
+ }
+ ],
+ values: [1],
+ properties: [{}],
+ tag: 'tag1'
+ }
+ });
+ });
+ });
+
+ describe('#editNewClonedWidget', function() {
+ var mock = {
+ saveWidgetLayout: sinon.stub().returns({done: Em.clb}),
+ getActiveWidgetLayout: sinon.stub().returns({done: Em.clb})
+ };
+ beforeEach(function() {
+ sinon.stub(App.router, 'get').returns(mock);
+ mixinObject.set('content.layout', {widgets: []});
+ mixinObject.editNewClonedWidget({resources:[{WidgetInfo: {id: 1}}]});
+ });
+ afterEach(function() {
+ App.router.get.restore();
+ });
+
+ it('saveWidgetLayout should be called', function() {
+ expect(mock.saveWidgetLayout.calledWith([
+ Em.Object.create({
+ id: 1
+ })
+ ])).to.be.true;
+ });
+
+ it('getActiveWidgetLayout should be called', function() {
+ expect(mock.getActiveWidgetLayout.called).to.be.true;
+ });
+
+ it('editWidget should be called', function() {
+ expect(mixinObject.get('controller').editWidget.called).to.be.true;
+ });
+ });
+
+ describe('#editWidget', function() {
+
+ it('controller.editWidget should be called', function() {
+ mixinObject.set('content', Em.Object.create({
+ scope: 'SERVICE'
+ }));
+ mixinObject.editWidget();
+
expect(mixinObject.get('controller').editWidget.calledWith(Em.Object.create({
+ scope: 'SERVICE'
+ }))).to.be.true;
+ });
+
+ it('App.ModalPopup.show should be called', function() {
+ mixinObject.set('content', Em.Object.create({
+ scope: 'CLUSTER'
+ }));
+ mixinObject.editWidget();
+ expect(App.ModalPopup.show.called).to.be.true;
+ });
+ });
+});
+
+describe('App.WidgetPreviewMixin', function() {
+ var widgetPreview;
+
+ beforeEach(function() {
+ widgetPreview = Em.Object.create({
+ drawWidget: sinon.spy(),
+ loadMetrics: Em.K,
+ controller: Em.Object.create(),
+ content: Em.Object.create()
+ }, App.WidgetPreviewMixin);
+ });
+
+ describe('#loadMetrics', function() {
+
+ it('widget properties should be set', function() {
+ widgetPreview.get('controller').setProperties({
+ widgetValues: [1],
+ widgetProperties: [{}],
+ widgetName: 'widget1',
+ widgetMetrics: [{}]
+ });
+ widgetPreview.loadMetrics();
+ expect(widgetPreview.get('content')).to.be.eql(Em.Object.create({
+ 'id': 1,
+ 'values': [1],
+ 'properties': [{}],
+ 'widgetName': 'widget1',
+ 'metrics': [{}]
+ }));
+ });
+ });
+
+ describe('#onMetricsLoaded', function() {
+
+ it('drawWidget should be called', function() {
+ widgetPreview.onMetricsLoaded();
+ expect(widgetPreview.drawWidget.calledOnce).to.be.true;
+ });
+ });
});