AMBARI-15596. Add missing unit tests files for ambari-web utils (alexantonenko)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/d5d1afea Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/d5d1afea Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/d5d1afea Branch: refs/heads/trunk Commit: d5d1afea8be684fed1e712f7a80b5e06f4d79ba5 Parents: aa033d0 Author: Alex Antonenko <hiv...@gmail.com> Authored: Mon Mar 28 11:53:29 2016 +0300 Committer: Alex Antonenko <hiv...@gmail.com> Committed: Mon Mar 28 11:53:29 2016 +0300 ---------------------------------------------------------------------- ambari-web/app/assets/test/tests.js | 9 + ambari-web/app/utils/array_utils.js | 2 +- ambari-web/app/utils/configs_collection.js | 2 +- ambari-web/app/utils/heatmap.js | 1 - ambari-web/app/utils/hosts.js | 10 +- ambari-web/app/utils/polling.js | 20 +- ambari-web/test/utils/action_sequence_test.js | 323 ++ ambari-web/test/utils/array_utils_test.js | 125 + .../test/utils/configs_collection_test.js | 334 ++ ambari-web/test/utils/credentials_test.js | 717 ++++ ambari-web/test/utils/file_utils_test.js | 126 + .../test/utils/handlebars_helpers_test.js | 57 + ambari-web/test/utils/heatmap_test.js | 141 + ambari-web/test/utils/hosts_test.js | 3467 ++++++++++++++++++ ambari-web/test/utils/polling_test.js | 1333 +++++++ 15 files changed, 6646 insertions(+), 21 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/d5d1afea/ambari-web/app/assets/test/tests.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/assets/test/tests.js b/ambari-web/app/assets/test/tests.js index d114379..db504f2 100644 --- a/ambari-web/app/assets/test/tests.js +++ b/ambari-web/app/assets/test/tests.js @@ -167,9 +167,13 @@ var files = [ 'test/mixins/unit_convert/base_unit_convert_mixin_test', 'test/utils/ajax/ajax_test', 'test/utils/ajax/ajax_queue_test', + 'test/utils/action_sequence_test', + 'test/utils/array_utils_test', 'test/utils/batch_scheduled_requests_test', 'test/utils/blueprint_test', 'test/utils/config_test', + 'test/utils/configs_collection_test', + 'test/utils/credentials_test', 'test/utils/date/date_test', 'test/utils/date/timezone_test', 'test/utils/data_manipulation_test', @@ -178,9 +182,14 @@ var files = [ 'test/utils/ember_computed_test', 'test/utils/ember_reopen_test', 'test/utils/form_field_test', + 'test/utils/file_utils_test', + 'test/utils/handlebars_helpers_test', + 'test/utils/heatmap_test', 'test/utils/host_progress_popup_test', + 'test/utils/hosts_test', 'test/utils/misc_test', 'test/utils/number_utils_test', + 'test/utils/polling_test', 'test/utils/validator_test', 'test/utils/config_test', 'test/utils/string_utils_test', http://git-wip-us.apache.org/repos/asf/ambari/blob/d5d1afea/ambari-web/app/utils/array_utils.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/utils/array_utils.js b/ambari-web/app/utils/array_utils.js index bee12ee..643ed67 100644 --- a/ambari-web/app/utils/array_utils.js +++ b/ambari-web/app/utils/array_utils.js @@ -26,7 +26,7 @@ module.exports = { uniqObjectsbyId: function (arr, id) { var result = []; arr.forEach(function (item) { - var isIdPresent = result.someProperty('id', item.id); + var isIdPresent = result.someProperty(id, item[id]); if (!isIdPresent) { result.pushObject(item); } http://git-wip-us.apache.org/repos/asf/ambari/blob/d5d1afea/ambari-web/app/utils/configs_collection.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/utils/configs_collection.js b/ambari-web/app/utils/configs_collection.js index fde7a17..a9a570a 100644 --- a/ambari-web/app/utils/configs_collection.js +++ b/ambari-web/app/utils/configs_collection.js @@ -40,7 +40,7 @@ var configsCollection = [], App.configsCollection = Em.Object.create({ /** - * adds config property to configs array anf map + * adds config property to configs array and map * should assert error if config has no id * @param c * @method add http://git-wip-us.apache.org/repos/asf/ambari/blob/d5d1afea/ambari-web/app/utils/heatmap.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/utils/heatmap.js b/ambari-web/app/utils/heatmap.js index 9d8c178..e033005 100644 --- a/ambari-web/app/utils/heatmap.js +++ b/ambari-web/app/utils/heatmap.js @@ -16,7 +16,6 @@ * limitations under the License. */ -var App = require('app'); module.exports = { mappers: Em.Mixin.create({ metricMapperWithTransform: function (json, metricName, transformValueFunction) { http://git-wip-us.apache.org/repos/asf/ambari/blob/d5d1afea/ambari-web/app/utils/hosts.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/utils/hosts.js b/ambari-web/app/utils/hosts.js index 2467e1a..e4a437c 100644 --- a/ambari-web/app/utils/hosts.js +++ b/ambari-web/app/utils/hosts.js @@ -151,15 +151,11 @@ module.exports = { host.set('filterColumnValue', value); - if (!skip && filterText) { - if ((value == null || !value.toString().match(filterText)) && !host.get('host.publicHostName').match(filterText)) { - skip = true; - } + if (!skip && filterText && (value == null || !value.toString().match(filterText)) && !host.get('host.publicHostName').match(filterText)) { + skip = true; } - if (!skip && filterComponent) { - if (hostComponentNames.length > 0) { + if (!skip && filterComponent && hostComponentNames.length > 0) { skip = !hostComponentNames.contains(filterComponent.get('componentName')); - } } host.set('filtered', !skip); }, this); http://git-wip-us.apache.org/repos/asf/ambari/blob/d5d1afea/ambari-web/app/utils/polling.js ---------------------------------------------------------------------- diff --git a/ambari-web/app/utils/polling.js b/ambari-web/app/utils/polling.js index b83ca23..7337fbb 100644 --- a/ambari-web/app/utils/polling.js +++ b/ambari-web/app/utils/polling.js @@ -76,7 +76,7 @@ App.Poll = Em.Object.extend(App.ReloadPopupMixin, { self.set('isSuccess', true); self.set('isError', false); } else { - var requestId = jsonData.Requests.id; + var requestId = Em.get(jsonData, 'Requests.id'); self.set('requestId', requestId); self.doPolling(); } @@ -101,7 +101,7 @@ App.Poll = Em.Object.extend(App.ReloadPopupMixin, { }, doPolling: function () { - if (this.get('requestId')) { + if (!Em.isNone(this.get('requestId'))) { this.startPolling(); } }, @@ -110,7 +110,7 @@ App.Poll = Em.Object.extend(App.ReloadPopupMixin, { * server call to obtain task logs */ pollTaskLog: function () { - if (this.get('currentTaskId')) { + if (!Em.isNone(this.get('currentTaskId'))) { App.ajax.send({ name: 'background_operations.get_by_task', sender: this, @@ -119,7 +119,7 @@ App.Poll = Em.Object.extend(App.ReloadPopupMixin, { taskId: this.get('currentTaskId') }, success: 'pollTaskLogSuccessCallback' - }) + }); } }, @@ -139,7 +139,7 @@ App.Poll = Em.Object.extend(App.ReloadPopupMixin, { * @return {Boolean} */ startPolling: function () { - if (!this.get('requestId')) return false; + if (Em.isNone(this.get('requestId'))) return false; this.pollTaskLog(); App.ajax.send({ @@ -169,10 +169,8 @@ App.Poll = Em.Object.extend(App.ReloadPopupMixin, { reloadErrorCallback: function (request, ajaxOptions, error, opt, params) { this._super(request, ajaxOptions, error, opt, params); - if (request.status) { - if (!this.get('isSuccess')) { - this.set('isError', true); - } + if (request.status && !this.get('isSuccess')) { + this.set('isError', true); } }, @@ -182,7 +180,7 @@ App.Poll = Em.Object.extend(App.ReloadPopupMixin, { replacePolledData: function (polledData) { var currentTaskId = this.get('currentTaskId'); - if (currentTaskId) { + if (!Em.isNone(currentTaskId)) { var task = this.get('polledData').findProperty('Tasks.id', currentTaskId); var currentTask = polledData.findProperty('Tasks.id', currentTaskId); if (task && currentTask) { @@ -226,7 +224,7 @@ App.Poll = Em.Object.extend(App.ReloadPopupMixin, { parseInfo: function (polledData) { var tasksData = polledData.tasks; var requestId = this.get('requestId'); - if (polledData.Requests && polledData.Requests.id && polledData.Requests.id != requestId) { + if (polledData.Requests && !Em.isNone(polledData.Requests.id) && polledData.Requests.id != requestId) { // We don't want to use non-current requestId's tasks data to // determine the current install status. // Also, we don't want to keep polling if it is not the http://git-wip-us.apache.org/repos/asf/ambari/blob/d5d1afea/ambari-web/test/utils/action_sequence_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/utils/action_sequence_test.js b/ambari-web/test/utils/action_sequence_test.js new file mode 100644 index 0000000..4ae9dc8 --- /dev/null +++ b/ambari-web/test/utils/action_sequence_test.js @@ -0,0 +1,323 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); +require('utils/action_sequence'); + +describe('App.actionSequence', function () { + + var actionSequence; + + beforeEach(function () { + actionSequence = App.actionSequence.create(); + }); + + describe('#setSequence', function () { + + var cases = [ + { + sequenceIn: [{}, {}], + sequenceOut: [{}, {}], + title: 'array passed' + }, + { + sequenceIn: { + '0': {}, + '1': {}, + 'length': 2 + }, + sequenceOut: [{}], + title: 'array-like object passed' + }, + { + sequenceIn: 0, + sequenceOut: [{}], + title: 'primitive passed' + } + ], + result; + + cases.forEach(function (item) { + + describe(item.title, function () { + + beforeEach(function () { + actionSequence.set('sequence', [{}]); + result = actionSequence.setSequence(item.sequenceIn); + }); + + it('should return context', function () { + expect(result).to.eql(actionSequence); + }); + + it('sequence property', function () { + expect(actionSequence.get('sequence')).to.eql(item.sequenceOut); + }); + + }); + + }); + + }); + + describe('#start', function () { + + beforeEach(function () { + actionSequence.setProperties({ + actionCounter: 0, + sequence: [{}] + }); + sinon.stub(actionSequence, 'runNextAction', Em.K); + actionSequence.start(); + }); + + afterEach(function () { + actionSequence.runNextAction.restore(); + }); + + it('should set the counter', function () { + expect(actionSequence.get('actionCounter')).to.equal(1); + }); + + it('should start the sequence', function () { + expect(actionSequence.runNextAction.calledOnce).to.be.true; + }); + + it('should call runNextAction with correct arguments', function () { + expect(actionSequence.runNextAction.calledWith(0, null)).to.be.true; + }); + + }); + + describe('#onFinish', function () { + + var cases = [ + { + callbackIn: Em.isNone, + callbackOut: Em.isNone, + title: 'function passed' + }, + { + callbackIn: 'function () {}', + callbackOut: Em.clb, + title: 'array-like object passed' + }, + { + callbackIn: 'function () {}', + callbackOut: Em.clb, + title: 'primitive passed' + } + ], + result; + + cases.forEach(function (item) { + + describe(item.title, function () { + + beforeEach(function () { + actionSequence.set('finishedCallback', Em.clb); + result = actionSequence.onFinish(item.callbackIn); + }); + + it('should return context', function () { + expect(result).to.eql(actionSequence); + }); + + it('finishedCallback property', function () { + expect(actionSequence.get('finishedCallback')).to.eql(item.callbackOut); + }); + + }); + + }); + + }); + + describe('#runNextAction', function () { + + var actions = { + callback: Em.K, + sync: function (prevResponse) { + actions.callback(prevResponse); + return prevResponse; + }, + async: function (prevResponse) { + actions.callback(prevResponse); + return { + done: function (callback) { + return callback.call(this, prevResponse); + } + }; + } + }, + prevResponse = {}, + cases = [ + { + index: 0, + actionCounter: 0, + sequence: [ + { + callback: actions.sync, + type: 'sync' + } + ], + actionCallCount: 0, + title: 'no iterations left (case 1)' + }, + { + index: 3, + actionCounter: 3, + sequence: [ + { + callback: actions.sync, + type: 'sync' + }, + { + callback: actions.sync, + type: 'sync' + }, + { + callback: actions.sync, + type: 'sync' + } + ], + actionCallCount: 0, + title: 'no iterations left (case 2)' + }, + { + index: 1, + actionCounter: 3, + sequence: [ + { + callback: actions.sync, + type: 'sync' + }, + { + callback: actions.sync, + type: 'sync' + }, + { + callback: actions.sync, + type: 'sync' + } + ], + actionCallCount: 2, + title: 'starting from the middle' + }, + { + index: 0, + actionCounter: 2, + sequence: [ + { + callback: actions.sync, + type: 'sync' + }, + { + callback: actions.sync, + type: 'sync' + }, + { + callback: actions.sync, + type: 'sync' + } + ], + actionCallCount: 2, + title: 'ending at the middle' + }, + { + index: 0, + actionCounter: 3, + sequence: [ + { + callback: actions.sync, + type: 'sync' + }, + { + callback: actions.sync, + type: 'sync' + }, + { + callback: actions.sync, + type: 'sync' + } + ], + actionCallCount: 3, + title: 'all iterations' + }, + { + index: 0, + actionCounter: 3, + sequence: [ + { + callback: actions.sync, + type: 'sync' + }, + { + callback: actions.async, + type: 'async' + }, + { + callback: actions.sync, + type: 'sync' + } + ], + actionCallCount: 3, + title: 'asynchronous action' + } + ]; + + cases.forEach(function (item) { + + describe(item.title, function () { + + beforeEach(function () { + sinon.spy(actions, 'callback'); + sinon.stub(actionSequence, 'finishedCallback', Em.K); + actionSequence.setProperties({ + context: actionSequence, + actionCounter: item.actionCounter, + sequence: item.sequence + }); + actionSequence.runNextAction(item.index, prevResponse); + }); + + afterEach(function () { + actions.callback.restore(); + actionSequence.finishedCallback.restore(); + }); + + it('number of calls', function () { + expect(actions.callback.callCount).to.equal(item.actionCallCount); + }); + + if (item.actionCallCount) { + it('argument passed to callback', function () { + expect(actions.callback.alwaysCalledWith(prevResponse)).to.be.true; + }); + } + + it('finish callback', function () { + expect(actionSequence.finishedCallback.calledOnce).to.be.true; + }); + + }); + }); + + }); + +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/d5d1afea/ambari-web/test/utils/array_utils_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/utils/array_utils_test.js b/ambari-web/test/utils/array_utils_test.js new file mode 100644 index 0000000..ec7c5d8 --- /dev/null +++ b/ambari-web/test/utils/array_utils_test.js @@ -0,0 +1,125 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var arrayUtils = require('utils/array_utils'); + +describe('array_utils', function () { + + describe('#uniqObjectsbyId', function () { + + var arr = [ + { + n: 0, + v: 'v0' + }, + { + n: 0, + v: 'v01' + }, + { + n: 1, + v: 'v1' + }, + { + n: '1', + v: 'v11' + }, + { + n: 2, + v: 'v2' + } + ], + result = [ + { + n: 0, + v: 'v0' + }, + { + n: 1, + v: 'v1' + }, + { + n: '1', + v: 'v11' + }, + { + n: 2, + v: 'v2' + } + ]; + + it('should return one element for one id', function () { + expect(arrayUtils.uniqObjectsbyId(arr, 'n').toArray()).to.eql(result); + }); + + }); + + describe('#intersect', function () { + + var cases = [ + { + arr1: [Infinity, 0, 1, 2, {a: 1}, {b: 2}, null, undefined], + arr2: ['undefined', null, {b: '2'}, {a: 1}, 2.0, '1', 0, Infinity], + result: [null, 2, 0, Infinity], + title: 'arrays of the same length have common items' + }, + { + arr1: [true, false, [0, 1], [2, 3], [4, 5], [6], null, undefined], + arr2: [undefined, 'null', 6, [4, 5], ['2', '3'], [String(0), String(1)], '0,1', false, 'true'], + result: [false, undefined], + title: 'arrays of different length have common items' + }, + { + arr1: ['1', function () {}, NaN], + arr2: ['function () {}', Number('1'), NaN], + result: [], + title: 'arrays have no common items' + }, + { + arr1: [[0], undefined, null], + arr2: [], + result: [], + title: 'one of arrays is empty' + }, + { + arr1: [], + arr2: [], + result: [], + title: 'both arrays are empty' + } + ]; + + cases.forEach(function (item) { + + describe(item.title, function () { + + it('arrays intersection', function () { + expect(arrayUtils.intersect(item.arr1, item.arr2)).to.eql(item.result); + }); + + it('commutativity', function () { + expect(arrayUtils.intersect(item.arr1, item.arr2).sort()).to.eql(arrayUtils.intersect(item.arr2, item.arr1).sort()); + }); + + }); + + }); + + }); + +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/d5d1afea/ambari-web/test/utils/configs_collection_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/utils/configs_collection_test.js b/ambari-web/test/utils/configs_collection_test.js new file mode 100644 index 0000000..bd97950 --- /dev/null +++ b/ambari-web/test/utils/configs_collection_test.js @@ -0,0 +1,334 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); +require('utils/configs_collection'); + +describe('App.configsCollection', function () { + + var configsCollection; + + beforeEach(function () { + configsCollection = Em.Object.create(App.configsCollection); + sinon.spy(Em, 'assert'); + }); + + afterEach(function () { + Em.assert.restore(); + }); + + describe('#add', function () { + + var cases = [ + { + collection: [], + isError: false, + title: 'initial state' + }, + { + obj: undefined, + collection: [], + isError: true, + title: 'no item passed' + }, + { + obj: undefined, + collection: [], + isError: true, + title: 'null passed' + }, + { + obj: {}, + collection: [], + isError: true, + title: 'no id passed' + }, + { + obj: { + id: 1, + name: 'n10' + }, + collection: [ + { + id: 1, + name: 'n10' + } + ], + mapItem: { + id: 1, + name: 'n10' + }, + isError: false, + title: 'new item' + }, + { + obj: { + id: 1, + name: 'n11' + }, + collection: [ + { + id: 1, + name: 'n10' + } + ], + mapItem: { + id: 1, + name: 'n11' + }, + isError: false, + title: 'duplicate id' + }, + { + obj: { + id: '1', + name: 'n12' + }, + collection: [ + { + id: 1, + name: 'n10' + } + ], + mapItem: { + id: '1', + name: 'n12' + }, + isError: false, + title: 'duplicate id, key name conversion' + } + ]; + + cases.forEach(function (item) { + + describe(item.title, function () { + + beforeEach(function () { + try { + if (item.hasOwnProperty('obj')) { + configsCollection.add(item.obj); + } + } catch (e) {} + }); + + it('thrown error', function () { + expect(Em.assert.threw()).to.equal(item.isError); + }); + + it('configs array', function () { + expect(configsCollection.getAll()).to.eql(item.collection); + }); + + if (item.obj && item.obj.id) { + it('configs map', function () { + expect(configsCollection.getConfig(item.obj.id)).to.eql(item.mapItem); + }); + } + + }); + + }); + + }); + + describe('#getConfig', function () { + + var cases = [ + { + result: undefined, + isError: true, + title: 'no id passed' + }, + { + id: null, + result: undefined, + isError: true, + title: 'invalid id passed' + }, + { + id: 1, + result: { + id: 1 + }, + isError: false, + title: 'existing item' + }, + { + id: 1, + result: { + id: 1 + }, + isError: false, + title: 'existing item, key name conversion' + }, + { + id: 2, + result: undefined, + isError: false, + title: 'item doesn\'t exist' + } + ]; + + cases.forEach(function (item) { + + describe(item.title, function () { + + var result; + + beforeEach(function () { + configsCollection.add({ + id: 1 + }); + try { + result = configsCollection.getConfig(item.id); + } catch (e) {} + }); + + it('thrown error', function () { + expect(Em.assert.threw()).to.equal(item.isError); + }); + + it('returned value', function () { + expect(result).to.eql(item.result); + }); + + }); + + }); + + }); + + describe('#getConfigByName', function () { + + var configIds = ['n0_f0', 'n1_f1'], + cases = [ + { + fileName: 'f0', + result: undefined, + isError: true, + title: 'no name passed' + }, + { + name: 'n0', + result: undefined, + isError: true, + title: 'no filename passed' + }, + { + name: 'n0', + fileName: 'f0', + result: { + id: 'n0_f0' + }, + isError: false, + title: 'existing item' + }, + { + name: 'n0', + fileName: 'f1', + result: undefined, + isError: false, + title: 'not existing item' + } + ]; + + cases.forEach(function (item) { + + describe(item.title, function () { + + var result; + + beforeEach(function () { + sinon.stub(App.config, 'configId', function (name, fileName) { + return name + '_' + fileName; + }); + configIds.forEach(function (id) { + configsCollection.add({ + id: id + }); + }); + try { + result = configsCollection.getConfigByName(item.name, item.fileName); + } catch (e) {} + }); + + afterEach(function () { + App.config.configId.restore(); + configsCollection.clearAll(); + }); + + + it('thrown error', function () { + expect(Em.assert.threw()).to.equal(item.isError); + }); + + it('returned value', function () { + expect(result).to.eql(item.result); + }); + + }); + + }); + + }); + + describe('#getAll', function () { + + var configs = [ + { + id: 'c0' + }, + { + id: 'c1' + } + ]; + + beforeEach(function () { + configsCollection.clearAll(); + }); + + it('should return all configs', function () { + configs.forEach(function (item) { + configsCollection.add(item); + }); + expect(configsCollection.getAll()).to.eql(configs); + }); + + }); + + + describe('#clearAll', function () { + + beforeEach(function () { + configsCollection.add({ + id: 'c0' + }); + configsCollection.clearAll(); + }); + + it('should clear configs array', function () { + expect(configsCollection.getAll()).to.have.length(0); + }); + + it('should clear configs map', function () { + expect(configsCollection.getConfig('c0')).to.be.undefined; + }); + + }); + +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/d5d1afea/ambari-web/test/utils/credentials_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/utils/credentials_test.js b/ambari-web/test/utils/credentials_test.js new file mode 100644 index 0000000..7bf6791 --- /dev/null +++ b/ambari-web/test/utils/credentials_test.js @@ -0,0 +1,717 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var credentials = require('utils/credentials'); +var testHelpers = require('test/helpers'); + +describe('credentials utils', function () { + + var storeTypeStatusMock = function (clusterName, key) { + var result = {}; + result[key] = clusterName; + return result; + }; + + describe('#createCredentials', function () { + + it('should send AJAX request', function () { + credentials.createCredentials('c', 'a', {}); + expect(testHelpers.findAjaxRequest('name', 'credentials.create')).to.eql([ + { + sender: credentials, + name: 'credentials.create', + data: { + clusterName: 'c', + resource: {}, + alias: 'a' + }, + error: 'createCredentialsErrorCallback' + } + ]); + }); + + }); + + describe('#credentialsSuccessCallback', function () { + + var params = { + callback: Em.K + }, + cases = [ + { + items: [], + callbackArgument: [], + title: 'no data returned' + }, + { + items: [{}, {}], + callbackArgument: [undefined, undefined], + title: 'empty data returned' + }, + { + items: [ + { + Credential: { + id: 0 + } + }, + { + Credential: { + id: 1 + } + } + ], + callbackArgument: [ + { + id: 0 + }, + { + id: 1 + } + ], + title: 'valid data returned' + } + ]; + + beforeEach(function () { + sinon.spy(params, 'callback'); + }); + + afterEach(function () { + params.callback.restore(); + }); + + cases.forEach(function (item) { + + it(item.title, function () { + credentials.credentialsSuccessCallback({ + items: item.items + }, null, params); + expect(params.callback.firstCall.args).to.eql([item.callbackArgument]); + }); + + }); + + }); + + describe('#createOrUpdateCredentials', function () { + + var mock = { + dfd: { + getCredential: null, + updateCredentials: null, + createCredentials: null + }, + callback: Em.K, + getCredential: function () { + return mock.dfd.getCredential.promise(); + }, + updateCredentials: function () { + return mock.dfd.updateCredentials.promise(); + }, + createCredentials: function () { + return mock.dfd.createCredentials.promise(); + } + }, + cases = [ + { + getCredentialResolve: true, + credentialsCallback: 'updateCredentials', + isCredentialsCallbackResolve: true, + status: 'success', + result: { + status: 200 + }, + callbackArgs: [ + true, + { + status: 200 + } + ], + title: 'successful credentials update' + }, + { + getCredentialResolve: true, + credentialsCallback: 'updateCredentials', + isCredentialsCallbackResolve: false, + status: 'error', + result: { + status: 404 + }, + callbackArgs: [ + false, + { + status: 404 + } + ], + title: 'failed credentials update' + }, + { + getCredentialResolve: false, + credentialsCallback: 'createCredentials', + isCredentialsCallbackResolve: true, + status: 'success', + result: { + status: 201 + }, + callbackArgs: [ + true, + { + status: 201 + } + ], + title: 'successful credentials creation' + }, + { + getCredentialResolve: false, + credentialsCallback: 'createCredentials', + isCredentialsCallbackResolve: false, + status: 'error', + result: { + status: 500 + }, + callbackArgs: [ + false, + { + status: 500 + } + ], + title: 'failed credentials creation' + } + ]; + + beforeEach(function () { + sinon.stub(credentials, 'getCredential', mock.getCredential); + sinon.stub(credentials, 'updateCredentials', mock.updateCredentials); + sinon.stub(credentials, 'createCredentials', mock.createCredentials); + sinon.spy(mock, 'callback'); + mock.dfd.getCredential = $.Deferred(); + mock.dfd.updateCredentials = $.Deferred(); + mock.dfd.createCredentials = $.Deferred(); + }); + + afterEach(function () { + credentials.getCredential.restore(); + credentials.updateCredentials.restore(); + credentials.createCredentials.restore(); + mock.callback.restore(); + }); + + cases.forEach(function (item) { + + var getCredentialMethod = item.getCredentialResolve ? 'resolve' : 'reject', + credentialsCallbackMethod = item.isCredentialsCallbackResolve ? 'resolve' : 'reject'; + + it(item.title, function () { + mock.dfd.getCredential[getCredentialMethod](); + mock.dfd[item.credentialsCallback][credentialsCallbackMethod](null, item.status, item.result); + credentials.createOrUpdateCredentials().done(mock.callback); + expect(mock.callback.firstCall.args).to.eql(item.callbackArgs); + }); + + }); + + }); + + describe('#getCredential', function () { + + it('should send AJAX request', function () { + credentials.getCredential('c', 'a', Em.K); + expect(testHelpers.findAjaxRequest('name', 'credentials.get')).to.eql([ + { + sender: credentials, + name: 'credentials.get', + data: { + clusterName: 'c', + alias: 'a', + callback: Em.K + }, + success: 'getCredentialSuccessCallback', + error: 'getCredentialErrorCallback' + } + ]); + }); + + }); + + describe('#getCredentialSuccessCallback', function () { + + var params = { + callback: Em.K + }, + cases = [ + { + data: null, + callback: undefined, + callbackCallCount: 0, + title: 'no callback passed' + }, + { + data: null, + callback: null, + callbackCallCount: 0, + title: 'invalid callback passed' + }, + { + data: null, + callbackCallCount: 1, + callbackArgument: null, + title: 'no data passed' + }, + { + data: {}, + callbackCallCount: 1, + callbackArgument: null, + title: 'no credential info passed' + }, + { + data: { + Credential: 'c' + }, + callbackCallCount: 1, + callbackArgument: 'c', + title: 'credential info passed' + } + ]; + + cases.forEach(function (item) { + + describe(item.title, function () { + + beforeEach(function () { + sinon.spy(params, 'callback'); + credentials.getCredentialSuccessCallback(item.data, null, item.hasOwnProperty('callback') ? { + callback: item.callback + } : params); + }); + + afterEach(function () { + params.callback.restore(); + }); + + it('callback call count', function () { + expect(params.callback.callCount).to.equal(item.callbackCallCount); + }); + + if (item.callbackCallCount) { + it('callback argument', function () { + expect(params.callback.firstCall.args).to.eql([item.callbackArgument]); + }); + } + + }); + + }); + + }); + + describe('#updateCredentials', function () { + + it('should send AJAX request', function () { + credentials.updateCredentials('c', 'a', {}); + expect(testHelpers.findAjaxRequest('name', 'credentials.update')).to.eql([ + { + sender: credentials, + name: 'credentials.update', + data: { + clusterName: 'c', + alias: 'a', + resource: {} + } + } + ]); + }); + + }); + + describe('#credentials', function () { + + it('should send AJAX request', function () { + credentials.credentials('c', Em.K); + expect(testHelpers.findAjaxRequest('name', 'credentials.list')).to.eql([ + { + sender: credentials, + name: 'credentials.list', + data: { + clusterName: 'c', + callback: Em.K + }, + success: 'credentialsSuccessCallback' + } + ]); + }); + + }); + + describe('#removeCredentials', function () { + + it('should send AJAX request', function () { + credentials.removeCredentials('c', 'a'); + expect(testHelpers.findAjaxRequest('name', 'credentials.delete')).to.eql([ + { + sender: credentials, + name: 'credentials.delete', + data: { + clusterName: 'c', + alias: 'a' + } + } + ]); + }); + + }); + + describe('#storageInfo', function () { + + it('should send AJAX request', function () { + credentials.storageInfo('c', Em.K); + expect(testHelpers.findAjaxRequest('name', 'credentials.store.info')).to.eql([ + { + sender: credentials, + name: 'credentials.store.info', + data: { + clusterName: 'c', + callback: Em.K + }, + success: 'storageInfoSuccessCallback' + } + ]); + }); + + }); + + describe('#storageInfoSuccessCallback', function () { + + var params = { + callback: Em.K + }, + cases = [ + { + callbackArgument: null, + title: 'no clusters' + }, + { + clusters: null, + callbackArgument: null, + title: 'invalid clusters info' + }, + { + clusters: {}, + callbackArgument: { + persistent: false, + temporary: false + }, + title: 'empty clusters info' + }, + { + clusters: { + credential_store_properties: { + 'storage.persistent': true, + 'storage.temporary': true + } + }, + callbackArgument: { + persistent: false, + temporary: false + }, + title: 'invalid storage properties format' + }, + { + clusters: { + credential_store_properties: {} + }, + callbackArgument: { + persistent: false, + temporary: false + }, + title: 'no storage properties' + }, + { + clusters: { + credential_store_properties: { + 'storage.persistent': 'true', + 'storage.temporary': 'false' + } + }, + callbackArgument: { + persistent: true, + temporary: false + }, + title: 'valid storage properties format - persistent storage' + }, + { + clusters: { + credential_store_properties: { + 'storage.persistent': 'false', + 'storage.temporary': 'true' + } + }, + callbackArgument: { + persistent: false, + temporary: true + }, + title: 'valid storage properties format - temporary storage' + }, + { + clusters: { + credential_store_properties: { + 'storage.persistent': 'true', + 'storage.temporary': 'true' + } + }, + callbackArgument: { + persistent: true, + temporary: true + }, + title: 'valid storage properties format - both types' + } + ]; + + cases.forEach(function (item) { + + describe(item.title, function () { + + beforeEach(function () { + sinon.spy(params, 'callback'); + credentials.storageInfoSuccessCallback({ + Clusters: item.clusters + }, null, params); + }); + + afterEach(function () { + params.callback.restore(); + }); + + it('callback execution', function () { + expect(params.callback.calledOnce).to.be.true; + }); + + it('callback argument', function () { + expect(params.callback.firstCall.args).to.eql([item.callbackArgument]); + }); + + }); + + }); + + }); + + describe('#isStorePersisted', function () { + + beforeEach(function () { + sinon.stub(credentials, 'storeTypeStatus', storeTypeStatusMock); + }); + + afterEach(function () { + credentials.storeTypeStatus.restore(); + }); + + it('should return storeTypeStatus result', function () { + expect(credentials.isStorePersisted('c')).to.eql({ + persistent: 'c' + }); + }); + + }); + + + describe('#isStoreTemporary', function () { + + beforeEach(function () { + sinon.stub(credentials, 'storeTypeStatus', storeTypeStatusMock); + }); + + afterEach(function () { + credentials.storeTypeStatus.restore(); + }); + + it('should return storeTypeStatus result', function () { + expect(credentials.isStoreTemporary('c')).to.eql({ + temporary: 'c' + }); + }); + + }); + + describe('#storeTypeStatus', function () { + + var mock = { + successCallback: Em.K, + errorCallback: Em.K + }, + data = { + clusterName: 'c' + }, + error = { + status: 404 + }, + cases = [ + { + isSuccess: true, + callbackArgument: data, + title: 'success' + }, + { + isSuccess: false, + callbackArgument: error, + title: 'fail' + } + ]; + + cases.forEach(function (item) { + + describe(item.title, function () { + + var callbackName = item.isSuccess ? 'successCallback' : 'errorCallback'; + + beforeEach(function () { + sinon.spy(mock, 'successCallback'); + sinon.spy(mock, 'errorCallback'); + sinon.stub(credentials, 'storageInfo', function (clusterName, callback) { + var dfd = $.Deferred(); + if (item.isSuccess) { + callback({ + temporary: data + }); + } else { + dfd.reject(error); + } + return dfd.promise(); + }); + credentials.storeTypeStatus(null, 'temporary').then(mock.successCallback, mock.errorCallback); + }); + + afterEach(function () { + mock.successCallback.restore(); + mock.errorCallback.restore(); + credentials.storageInfo.restore(); + }); + + it('success callback', function () { + expect(mock.successCallback.called).to.equal(item.isSuccess); + }); + + it('error callback', function () { + expect(mock.errorCallback.called).to.not.equal(item.isSuccess); + }); + + it('callback called once', function () { + expect(mock[callbackName].calledOnce).to.be.true; + }); + + it('callback arguments', function () { + expect(mock[callbackName].firstCall.args).to.eql([item.callbackArgument]); + }); + + }); + + }); + + }); + + describe('#createCredentialResource', function () { + + it('should return object with arguments', function () { + expect(credentials.createCredentialResource('p', 'c', 't')).to.eql({ + principal: 'p', + key: 'c', + type: 't' + }); + }); + + }); + + describe('#isKDCCredentialsPersisted', function () { + + var cases = [ + { + credentials: [], + isKDCCredentialsPersisted: false, + title: 'empty array passed' + }, + { + credentials: [{}, {}], + isKDCCredentialsPersisted: false, + title: 'no aliases passed' + }, + { + credentials: [ + { + alias: 'a0' + }, + { + alias: 'a1' + } + ], + isKDCCredentialsPersisted: false, + title: 'no KDC admin credentials passed' + }, + { + credentials: [ + { + alias: 'kdc.admin.credential' + }, + { + alias: 'a2' + } + ], + isKDCCredentialsPersisted: false, + title: 'no KDC admin credentials type passed' + }, + { + credentials: [ + { + alias: 'kdc.admin.credential', + type: 'temporary' + }, + { + alias: 'a3' + } + ], + isKDCCredentialsPersisted: false, + title: 'temporary storage' + }, + { + credentials: [ + { + alias: 'kdc.admin.credential', + type: 'persisted' + }, + { + alias: 'kdc.admin.credential' + }, + { + alias: 'a4' + } + ], + isKDCCredentialsPersisted: true, + title: 'persistent storage' + } + ]; + + cases.forEach(function (item) { + + it(item.title, function () { + expect(credentials.isKDCCredentialsPersisted(item.credentials)).to.equal(item.isKDCCredentialsPersisted); + }); + + }); + + }); + +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/d5d1afea/ambari-web/test/utils/file_utils_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/utils/file_utils_test.js b/ambari-web/test/utils/file_utils_test.js new file mode 100644 index 0000000..8c9eb6d --- /dev/null +++ b/ambari-web/test/utils/file_utils_test.js @@ -0,0 +1,126 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var fileUtils = require('utils/file_utils'); + +describe('file_utils', function () { + + describe('#openInfoInNewTab', function () { + + var mock = { + document: { + write: Em.K + }, + focus: Em.K + }; + + beforeEach(function () { + sinon.stub(window, 'open').returns(mock); + sinon.spy(mock.document, 'write'); + sinon.spy(mock, 'focus'); + fileUtils.openInfoInNewTab('data'); + }); + + afterEach(function () { + window.open.restore(); + mock.document.write.restore(); + mock.focus.restore(); + }); + + it('opening new window', function () { + expect(window.open.calledOnce).to.be.true; + }); + + it('no URL for new window', function () { + expect(window.open.firstCall.args).to.eql(['']); + }); + + it('writing document contents', function () { + expect(mock.document.write.calledOnce).to.be.true; + }); + + it('document contents', function () { + expect(mock.document.write.firstCall.args).to.eql(['data']); + }); + + it('focusing on new window', function () { + expect(mock.focus.calledOnce).to.be.true; + }); + + }); + + describe('#safariDownload', function () { + + var linkEl = { + click: Em.K + }; + + beforeEach(function () { + sinon.stub(document, 'createElement').returns(linkEl); + sinon.stub(document.body, 'appendChild', Em.K); + sinon.stub(document.body, 'removeChild', Em.K); + sinon.spy(linkEl, 'click'); + fileUtils.safariDownload('file data', 'csv', 'file.csv'); + }); + + afterEach(function () { + document.createElement.restore(); + document.body.appendChild.restore(); + document.body.removeChild.restore(); + linkEl.click.restore(); + }); + + it('creating new element', function () { + expect(document.createElement.calledOnce).to.be.true; + }); + + it('new element is a link', function () { + expect(document.createElement.firstCall.args).to.eql(['a']); + }); + + it('link URL', function () { + expect(linkEl.href).to.equal('data:attachment/csv;charset=utf-8,file%20data'); + }); + + it('file name', function () { + expect(linkEl.download).to.equal('file.csv'); + }); + + it('appending element to document', function () { + expect(document.body.appendChild.calledOnce).to.be.true; + }); + + it('link is appended', function () { + expect(document.body.appendChild.firstCall.args).to.eql([linkEl]); + }); + + it('link is clicked', function () { + expect(linkEl.click.calledOnce).to.be.true; + }); + + it('removing element from document', function () { + expect(document.body.removeChild.calledOnce).to.be.true; + }); + + it('link is removed', function () { + expect(document.body.removeChild.firstCall.args).to.eql([linkEl]); + }); + + }); + +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/d5d1afea/ambari-web/test/utils/handlebars_helpers_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/utils/handlebars_helpers_test.js b/ambari-web/test/utils/handlebars_helpers_test.js new file mode 100644 index 0000000..4e705ad --- /dev/null +++ b/ambari-web/test/utils/handlebars_helpers_test.js @@ -0,0 +1,57 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var App = require('app'); +require('utils/handlebars_helpers'); + +describe('handlebars_helpers', function () { + + describe('#registerBoundHelper', function () { + + var options = { + hash: {} + }; + + beforeEach(function () { + sinon.stub(Em.Handlebars, 'registerHelper', function (name, callback) { + callback('prop', options); + }); + sinon.stub(Em.Handlebars.helpers, 'view', Em.K); + App.registerBoundHelper('helper', {}); + }); + + afterEach(function () { + Em.Handlebars.registerHelper.restore(); + Em.Handlebars.helpers.view.restore(); + }); + + it('contentBinding', function () { + expect(options.hash.contentBinding).to.equal('prop'); + }); + + it('view', function () { + expect(Em.Handlebars.helpers.view.calledOnce).to.be.true; + }); + + it('view arguments', function () { + expect(Em.Handlebars.helpers.view.firstCall.args).to.eql([{}, options]); + }); + + }); + +}); http://git-wip-us.apache.org/repos/asf/ambari/blob/d5d1afea/ambari-web/test/utils/heatmap_test.js ---------------------------------------------------------------------- diff --git a/ambari-web/test/utils/heatmap_test.js b/ambari-web/test/utils/heatmap_test.js new file mode 100644 index 0000000..0a63cbf --- /dev/null +++ b/ambari-web/test/utils/heatmap_test.js @@ -0,0 +1,141 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +var heatmap = require('utils/heatmap'); + +describe('heatmap utils', function () { + + describe('mappers', function () { + + var mappers; + + beforeEach(function () { + mappers = Em.Object.create(heatmap.mappers); + }); + + describe('#metricMapperWithTransform', function () { + + var cases = [ + { + hostComponents: null, + hostToValueMap: {}, + title: 'no host components data' + }, + { + hostComponents: [null, null], + metricName: 'm0', + hostToValueMap: {}, + title: 'host components data is absent' + }, + { + hostComponents: [{}, {}], + metricName: 'm1', + hostToValueMap: {}, + title: 'provided metric data is absent' + }, + { + hostComponents: [{}, {}], + metricName: 'm2.m3', + hostToValueMap: {}, + title: 'provided metrics data is absent' + }, + { + hostComponents: [ + null, + {}, + { + m4: 1, + HostRoles: { + host_name: 'h0' + } + }, + { + m4: 1.5, + HostRoles: { + host_name: 'h1' + } + }, + { + m4: 1.60, + HostRoles: { + host_name: 'h2' + } + }, + { + m4: 1.72, + HostRoles: { + host_name: 'h3' + } + }, + { + m4: 1.85, + HostRoles: { + host_name: 'h4' + } + }, + { + m4: 1.97, + HostRoles: { + host_name: 'h5' + } + } + ], + metricName: 'm4', + hostToValueMap: { + h0: '1.0', + h1: '1.5', + h2: '1.6', + h3: '1.7', + h4: '1.9', + h5: '2.0' + }, + title: 'no transform function' + }, + { + hostComponents: [ + { + m5: 100, + HostRoles: { + host_name: 'h6' + } + } + ], + metricName: 'm5', + transformValueFunction: Math.sqrt, + hostToValueMap: { + h6: '10.0' + }, + title: 'transform function provided' + } + ]; + + cases.forEach(function (item) { + + it(item.title, function () { + expect(mappers.metricMapperWithTransform({ + host_components: item.hostComponents + }, item.metricName, item.transformValueFunction)).to.eql(item.hostToValueMap); + }); + + }); + + }); + + }); + +});