diff --git a/.eslintrc.json b/.eslintrc.json index 39d9d4a951..24f0b955be 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -14,7 +14,6 @@ "rules": { "camelcase": "off", "no-mixed-operators": "off", - "no-useless-escape": "off", - "prefer-promise-reject-errors": "off" + "no-useless-escape": "off" } } diff --git a/lib/assets/javascripts/builder/data/widget-definitions-collection.js b/lib/assets/javascripts/builder/data/widget-definitions-collection.js index 98ef5f0542..6cfcce58b0 100755 --- a/lib/assets/javascripts/builder/data/widget-definitions-collection.js +++ b/lib/assets/javascripts/builder/data/widget-definitions-collection.js @@ -113,7 +113,7 @@ module.exports = Backbone.Collection.extend({ var self = this; var ORDER_NUMBER_TO_APPEAR_ON_TOP = -1; - var addWidgetPromise = new Promise(function (resolve, reject) { + return new Promise(function (resolve, reject) { _.extend(attrs, { order: ORDER_NUMBER_TO_APPEAR_ON_TOP }); self.create(attrs, { @@ -128,8 +128,6 @@ module.exports = Backbone.Collection.extend({ } }); }); - - return addWidgetPromise; }, updateWidgetsOrder: function () { @@ -137,7 +135,23 @@ module.exports = Backbone.Collection.extend({ widgetDefinitionModel.set({ order: index }); }); - this.save(); + return this.saveAsync(); + }, + + saveAsync: function () { + var self = this; + + return new Promise(function (resolve, reject) { + self.save({ + wait: true, + success: function (collection) { + resolve(collection); + }, + error: function (response, error) { + reject(error); + } + }); + }); }, save: function (options) { diff --git a/lib/assets/javascripts/builder/editor/layers/layer-content-views/data/data-content-view.js b/lib/assets/javascripts/builder/editor/layers/layer-content-views/data/data-content-view.js index e25e46d7ed..c112a1097f 100755 --- a/lib/assets/javascripts/builder/editor/layers/layer-content-views/data/data-content-view.js +++ b/lib/assets/javascripts/builder/editor/layers/layer-content-views/data/data-content-view.js @@ -114,25 +114,46 @@ module.exports = CoreView.extend({ }, _handleWidget: function (model) { - var m, candidate; - if (model.get('selected') === true) { - candidate = this._columnsModel.findWidget(model); + var self = this; + var widgetModel; - if (!candidate) { - m = this._userActions.saveWidgetOption(model); - model.set({widget: m}); + model.get('selected') === true + ? this._addWidget(model) + : this._destroyWidget(model); + }, + + _addWidget: function (model) { + var widgetModel = this._columnsModel.findWidget(model); + + widgetModel + ? model.set({widget: widgetModel}) + : this._saveWidget(model); + }, + + _destroyWidget: function (model) { + var widgetModel = model.get('widget'); + widgetModel && widgetModel.destroy(); + }, + + _saveWidget: function (model) { + var self = this; + var widgetModel; + + this._userActions.saveWidgetOption(model) + .then(function (newWidgetModel) { + widgetModel = newWidgetModel; + + model.set({widget: widgetModel}); - // uncheck previous date column if (model.get('type') === 'time-series') { - this._normalizeTimeSeriesColumn(model); + self._normalizeTimeSeriesColumn(model); } - } else { - model.set({widget: candidate}); - } - } else { - m = model.get('widget'); - m && m.destroy(); - } + + return self._userActions.updateWidgetsOrder(); + }) + .then(function () { + self.render(); + }); }, _normalizeTimeSeriesColumn: function (lastModel) { diff --git a/lib/assets/test/spec/builder/data/widget-definitions-collection.spec.js b/lib/assets/test/spec/builder/data/widget-definitions-collection.spec.js index 44a295b428..f8ab48592b 100644 --- a/lib/assets/test/spec/builder/data/widget-definitions-collection.spec.js +++ b/lib/assets/test/spec/builder/data/widget-definitions-collection.spec.js @@ -217,7 +217,8 @@ describe('data/widget-definitions-collection', function () { describe('.updateWidgetsOrder', function () { it('should change set the widgets in increasing order', function () { - var collectionSaveSpy = spyOn(this.collection, 'save').and.callThrough(); + spyOn(this.collection, 'save').and.callThrough(); + spyOn(this.collection, 'saveAsync').and.callThrough(); this.collection.updateWidgetsOrder(); @@ -225,7 +226,44 @@ describe('data/widget-definitions-collection', function () { expect(model.order).toEqual(index); }); - expect(collectionSaveSpy).toHaveBeenCalled(); + expect(this.collection.save).toHaveBeenCalled(); + expect(this.collection.saveAsync).toHaveBeenCalled(); + }); + }); + + describe('.saveAsync', function () { + var callback; + var successResponse = { response: 'success' }; + var errorResponse = { response: 'error' }; + + beforeEach(function () { + Backbone.sync = function (method, collection, callbacks) { + if (callback === 'success') { + callbacks.success.call(this, successResponse); + } else if (callback === 'error') { + callbacks[callback].call(this, 'error', errorResponse); + } + }; + }); + + it('should return a resolved promise if the save function works', function (done) { + callback = 'success'; + + this.collection.saveAsync() + .then(function (response) { + expect(response).toEqual(successResponse); + done(); + }); + }); + + it('should return a rejected promise if the save function does not work', function (done) { + callback = 'error'; + + this.collection.saveAsync() + .catch(function (response) { + expect(response).toEqual(errorResponse); + done(); + }); }); }); }); diff --git a/lib/assets/test/spec/builder/editor/layers/layer-content-view/data/data-content-view.spec.js b/lib/assets/test/spec/builder/editor/layers/layer-content-view/data/data-content-view.spec.js index 62cef9829f..8a77aed020 100644 --- a/lib/assets/test/spec/builder/editor/layers/layer-content-view/data/data-content-view.spec.js +++ b/lib/assets/test/spec/builder/editor/layers/layer-content-view/data/data-content-view.spec.js @@ -18,6 +18,7 @@ describe('editor/layers/layers-content-view/data/data-content-view', function () var normalizeTSColumnSpy; var renderSpy; var isErroredSpy; + var widgetDefinitionsCollection; var createViewFn = function (options) { var configModel = new ConfigModel({ @@ -52,7 +53,7 @@ describe('editor/layers/layers-content-view/data/data-content-view', function () return true; }; - var analysisDefinitionNodeModel = new Backbone.Model(); + var analysisDefinitionNodeModel = new Backbone.Model({}); analysisDefinitionNodeModel.querySchemaModel = querySchemaModel; analysisDefinitionNodeModel.queryGeometryModel = queryGeometryModel; @@ -61,13 +62,14 @@ describe('editor/layers/layers-content-view/data/data-content-view', function () return analysisDefinitionNodeModel; }; - var widgetDefinitionsCollection = new Backbone.Collection([ - { - type: 'category', - source: 'a0', - column: 'city' + widgetDefinitionsCollection = new Backbone.Collection([{ + type: 'category', + source: 'a0', + column: 'city', + analysisDefinitionNodeModel: function () { + return new Backbone.Model({id: 'a0'}); } - ]); + }]); var columnModel = new Backbone.Model({ columns: 3 @@ -112,7 +114,10 @@ describe('editor/layers/layers-content-view/data/data-content-view', function () var userActions = { saveWidgetOption: function () { - return {id: 'w1'}; + return Promise.resolve({id: 'w1'}); + }, + updateWidgetsOrder: function () { + return Promise.resolve({}); } }; @@ -290,23 +295,179 @@ describe('editor/layers/layers-content-view/data/data-content-view', function () expect(view.$('.js-apply').hasClass('.CDB-Size-small')).toBeFalsy(); }); - it('should manage time-series properly', function () { - renderSpy.and.callThrough(); - handleWidgetSpy.and.callThrough(); - normalizeTSColumnSpy.and.callThrough(); + describe('._handleWidget', function () { + var saveWidgetOptionSpy; - view.render(); + beforeEach(function () { + renderSpy.and.callThrough(); + handleWidgetSpy.and.callThrough(); + saveWidgetOptionSpy = spyOn(view._userActions, 'saveWidgetOption') + .and.returnValue(Promise.resolve(view._columnsCollection.at(1))); + }); + + it('should be called when a widget selected state is changed', function () { + view.render(); + + view._columnsCollection.at(1).set({ selected: true }); - view._columnsModel.set({ render: true }); - expect(view._renderStats).toHaveBeenCalled(); + expect(view._handleWidget).toHaveBeenCalled(); + }); + + it('should call add a new widget if the model is selected', function () { + spyOn(view, '_addWidget'); + + view.render(); - view._columnsCollection.at(1).set({ selected: true }); - expect(view._handleWidget).toHaveBeenCalled(); + view._columnsCollection.at(1).set({ selected: true }); + view._handleWidget(view._columnsCollection.at(1)); - view._columnsCollection.at(2).set({ selected: true }); - expect(view._columnsCollection.at(2).get('widget')).toBeTruthy(); - expect(view._normalizeTimeSeriesColumn).toHaveBeenCalled(); - expect(view._columnsCollection.at(1).get('selected')).toBe(false); - expect(view._columnsCollection.at(1).get('widget')).toBeFalsy(); + expect(view._addWidget).toHaveBeenCalledWith(view._columnsCollection.at(1)); + }); + + it('should destroy the widget if the model is selected', function () { + spyOn(view, '_destroyWidget'); + + view.render(); + + view._handleWidget(view._columnsCollection.at(1)); + + expect(view._handleWidget).toHaveBeenCalled(); + expect(view._destroyWidget).toHaveBeenCalledWith(view._columnsCollection.at(1)); + }); + }); + + describe('._addWidget', function () { + var saveWidgetOptionSpy; + + beforeEach(function () { + renderSpy.and.callThrough(); + handleWidgetSpy.and.callThrough(); + saveWidgetOptionSpy = spyOn(view._userActions, 'saveWidgetOption') + .and + .returnValue(Promise.resolve(view._columnsCollection.at(0))); + }); + + it('should set the widget if the model exists', function () { + var existingModel = widgetDefinitionsCollection.at(0); + var model = view._columnsCollection.at(0); + spyOn(view._columnsModel, 'findWidget').and.returnValue(existingModel); + spyOn(model, 'set').and.callThrough(); + + view.render(); + + view._addWidget(model); + expect(model.set).toHaveBeenCalled(); + expect(model.get('widget')).toEqual(existingModel); + }); + + it('should call saveWidget if the model does not exist', function () { + var newModel = new Backbone.Model(); + spyOn(view, '_saveWidget'); + view.render(); + view._columnsModel.set({ render: true }); + + view._saveWidget(newModel); + expect(view._saveWidget).toHaveBeenCalledWith(newModel); + }); + }); + + describe('._saveWidget', function () { + var newModel; + + beforeEach(function () { + newModel = new Backbone.Model({}); + }); + + it('should call saveWidgetOption', function (done) { + spyOn(view._userActions, 'saveWidgetOption') + .and + .returnValue(Promise.resolve(view._columnsCollection.at(0))); + + spyOn(newModel, 'set'); + + view.render(); + view._saveWidget(newModel); + + setTimeout(function () { + expect(view._userActions.saveWidgetOption).toHaveBeenCalledWith(newModel); + expect(newModel.set).toHaveBeenCalled(); + done(); + }, 200); + }); + + it('should call updateWidgetsOrder if the new widget model is created', function (done) { + var newModel = new Backbone.Model({}); + spyOn(view._userActions, 'saveWidgetOption') + .and + .returnValue(Promise.resolve(newModel)); + + spyOn(view._userActions, 'updateWidgetsOrder'); + + view.render(); + view._saveWidget(newModel); + + setTimeout(function () { + expect(view._userActions.updateWidgetsOrder).toHaveBeenCalled(); + done(); + }, 200); + }); + + it('should render again if both the creation and the update work', function (done) { + spyOn(view._userActions, 'saveWidgetOption') + .and + .returnValue(Promise.resolve(newModel)); + + spyOn(view._userActions, 'updateWidgetsOrder') + .and + .returnValue(Promise.resolve({})); + + view.render(); + view._saveWidget(newModel); + + setTimeout(function () { + expect(view.render).toHaveBeenCalledTimes(2); + done(); + }, 200); + }); + + it('should manage time-series properly', function (done) { + spyOn(view._userActions, 'saveWidgetOption') + .and + .returnValue(Promise.resolve(view._columnsCollection.at(2))); + + renderSpy.and.callThrough(); + handleWidgetSpy.and.callThrough(); + normalizeTSColumnSpy.and.callThrough(); + + view.render(); + + view._columnsModel.set({ render: true }); + expect(view._renderStats).toHaveBeenCalled(); + + view._columnsCollection.at(2).set({ selected: true }); + + setTimeout(function () { + expect(view._columnsCollection.at(2).get('widget')).toBeTruthy(); + expect(view._normalizeTimeSeriesColumn).toHaveBeenCalled(); + expect(view._columnsCollection.at(1).get('selected')).toBe(false); + expect(view._columnsCollection.at(1).get('widget')).toBeFalsy(); + done(); + }, 200); + }); + }); + + describe('._destroyWidget', function () { + it('should call the destroy function if the model exists', function () { + var widgetModel = widgetDefinitionsCollection.at(0); + var model = new Backbone.Model({ + widget: widgetModel + }); + + spyOn(widgetModel, 'destroy'); + + view.render(); + view._destroyWidget(model); + expect(widgetModel.destroy).toHaveBeenCalled(); + }); }); });