cartodb-4.42/lib/assets/test/spec/builder/editor/style/style-content-view.spec.js
2024-04-06 05:25:13 +00:00

502 lines
17 KiB
JavaScript

var Backbone = require('backbone');
var _ = require('underscore');
var MetricsTracker = require('builder/components/metrics/metrics-tracker');
var UserActions = require('builder/data/user-actions');
var StyleContentView = require('builder/editor/style/style-content-view');
var StyleDefinitionModel = require('builder/editor/style/style-definition-model');
var QueryGeometryModel = require('builder/data/query-geometry-model');
var QuerySchemaModel = require('builder/data/query-schema-model');
var LayerContentModel = require('builder/data/layer-content-model');
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
var ConfigModel = require('builder/data/config-model');
var UserModel = require('builder/data/user-model');
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
var LayerDefinitionModel = require('builder/data/layer-definition-model');
var QueryRowsCollection = require('builder/data/query-rows-collection');
var FactoryModals = require('../../factories/modals');
var styleSQLErrorTemplate = require('builder/editor/style/style-content-sql-error.tpl');
var actionErrorTemplate = require('builder/editor/layers/sql-error-action.tpl');
var fakePromise = require('fixtures/builder/fake-promise.fixture.js');
describe('editor/style/style-content-view', function () {
beforeEach(function () {
this.configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.userModel = new UserModel({}, {
configModel: this.configModel
});
spyOn(this.userModel, 'featureEnabled').and.returnValue(true);
this.overlayModel = new Backbone.Model({
visible: false
});
this.model = new StyleDefinitionModel({
autogenerated: true
});
spyOn(MetricsTracker, 'track');
var attrs = {
query: 'SELECT * FROM table',
state: 'unfetched',
ready: true
};
var opts = {
configModel: this.configModel
};
this.querySchemaModel = new QuerySchemaModel(attrs, opts);
this.queryGeometryModel = new QueryGeometryModel(attrs, opts);
this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
configModel: this.configModel,
userModel: this.userModel
});
this.a0 = this.analysisDefinitionNodesCollection.add({
id: 'a0',
type: 'source',
table_name: 'foo'
});
spyOn(this.a0, 'isCustomQueryApplied').and.returnValue(false);
this.layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
configModel: this.configModel,
userModel: this.userModel,
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
mapId: 'm123',
stateDefinitionModel: {}
});
this.layerDefinitionsCollection.isThereAnyTorqueLayer = function () {};
this.layerDefinitionModel = new LayerDefinitionModel({
id: 'abc-123',
kind: 'carto',
options: {
type: 'CartoDB',
color: '#FABADA',
table_name: 'foo',
query: 'SELECT * FROM foo',
tile_style: 'asdasd',
visible: true
}
}, {
parse: true,
configModel: this.configModel,
collection: this.layerDefinitionsCollection
});
this.layerDefinitionModel.styleModel = this.model;
this.xhrSpy = jasmine.createSpyObj('xhr', ['abort', 'always', 'fail']);
spyOn(Backbone.Model.prototype, 'sync').and.returnValue(this.xhrSpy);
spyOn(this.layerDefinitionModel, 'save').and.callThrough();
spyOn(this.layerDefinitionModel, 'getAnalysisDefinitionNodeModel').and.returnValue(this.a0);
spyOn(this.querySchemaModel, 'fetch');
spyOn(this.queryGeometryModel, 'fetch');
spyOn(StyleContentView.prototype, 'render').and.callThrough();
this.userActions = UserActions({
userModel: this.userModel,
analysisDefinitionsCollection: {},
analysisDefinitionNodesCollection: {},
layerDefinitionsCollection: {},
widgetDefinitionsCollection: {}
});
this.queryRowsCollection = new QueryRowsCollection([], {
configModel: this.configModel,
querySchemaModel: this.querySchemaModel
});
spyOn(this.queryRowsCollection, 'fetch');
var querySchemaModel = new Backbone.Model();
querySchemaModel.hasRepeatedErrors = function () { return false; };
var queryGeometryModel = new Backbone.Model();
queryGeometryModel.hasRepeatedErrors = function () { return false; };
var queryRowsCollection = new Backbone.Collection();
queryRowsCollection.hasRepeatedErrors = function () { return false; };
this.layerContentModel = new LayerContentModel({}, {
querySchemaModel: querySchemaModel,
queryGeometryModel: queryGeometryModel,
queryRowsCollection: queryRowsCollection
});
this.view = new StyleContentView({
configModel: this.configModel,
userModel: this.userModel,
userActions: this.userActions,
layerDefinitionsCollection: this.layerDefinitionsCollection,
layerDefinitionModel: this.layerDefinitionModel,
queryGeometryModel: this.queryGeometryModel,
querySchemaModel: this.querySchemaModel,
queryRowsCollection: this.queryRowsCollection,
modals: FactoryModals.createModalService(),
styleModel: this.model,
overlayModel: this.overlayModel,
editorModel: new Backbone.Model(),
freezeTorgeAggregation: jasmine.createSpy('freezeTorgeAggregation'),
layerContentModel: this.layerContentModel
});
this.view.render();
});
describe('initialize', function () {
it('should have called to _buildAggregationCarouselCollection to create _carouselCollection and hook up events', function () {
expect(this.view._carouselCollection).toBeDefined();
});
});
describe('.render', function () {
it('should render properly', function () {
spyOn(this.view, '_initViews');
spyOn(this.view, '_toggleOverlay');
this.view.render();
expect(this.view._initViews).toHaveBeenCalled();
expect(this.view._toggleOverlay).toHaveBeenCalled();
});
it('should render the "apply" button properly in advanced mode', function () {
this.view.$('.Options-bar .CDB-Toggle.js-input').click();
expect(this.view.$('.js-apply').hasClass('.CDB-Size-small')).toBeFalsy();
});
});
describe('when layer content model has errors', function () {
describe('.render', function () {
it('should render properly', function () {
spyOn(this.view, '_isErrored').and.returnValue(true);
spyOn(this.view, '_toggleOverlay');
spyOn(this.view, '_renderError');
this.view.render();
expect(this.view._renderError).toHaveBeenCalled();
expect(this.view._toggleOverlay).toHaveBeenCalled();
});
});
});
describe('when data is filtered', function () {
describe('.render', function () {
it('should render properly', function () {
spyOn(this.view, '_renderFilteredData');
this.view._viewState.set('isDataFiltered', true);
this.view.render();
expect(this.view._renderFilteredData).toHaveBeenCalled();
});
});
});
describe('._initViews', function () {
it('should render the form and the carousel', function () {
this.view._queryGeometryModel.set({ simple_geom: 'point' });
this.view.render();
expect(this.view.$('.Carousel').length).toBe(1);
expect(this.view.$('.Editor-HeaderInfo').length).toBe(2);
expect(this.view.$el.children().first().hasClass('js-aggregationTypes')).toBe(true);
expect(_.size(this.view._subviews)).toBe(2); // [Carousel, Form]
});
it('should not render carousel view when geometry is not point', function () {
expect(this.view.$('.Carousel').length).toBe(0);
expect(this.view.$('.Editor-HeaderInfo').length).toBe(1);
expect(_.size(this.view._subviews)).toBe(1); // [Form]
});
it('should render the "apply" button properly in advanced mode', function () {
this.view.$('.Options-bar .CDB-Toggle.js-input').click();
expect(this.view.$('.js-apply').hasClass('.CDB-Size-small')).toBeFalsy();
});
});
describe('._initBinds', function () {
it('should call ._onAutoStyleChanged when _layerDefinitionModel:autoStyle changes', function () {
spyOn(this.view, '_onAutoStyleChanged');
this.view._initBinds();
this.view._layerDefinitionModel.set({ autoStyle: true });
expect(this.view._onAutoStyleChanged).toHaveBeenCalled();
});
it('should call .render when layerContentModel:state changes', function () {
this.view._initBinds();
this.view._layerContentModel.trigger('change:state');
expect(this.view.render).toHaveBeenCalled();
});
it('should call .render when _styleModel:render undo', function () {
this.view._initBinds();
this.view._styleModel.trigger('undo');
expect(this.view.render).toHaveBeenCalled();
});
it('should call .render when _styleModel:render redo', function () {
this.view._initBinds();
this.view._styleModel.trigger('redo');
expect(this.view.render).toHaveBeenCalled();
});
it('should call ._onStyleChanged when _layerDefinitionModel changes', function () {
spyOn(this.view, '_onStyleChanged');
this.view._initBinds();
this.view._layerDefinitionModel.set({ test: true });
expect(this.view._onStyleChanged).toHaveBeenCalled();
});
it('should call ._checkEditorModel when _layerContentModel:state changes', function () {
spyOn(this.view, '_checkEditorModel');
this.view._initBinds();
this.view._layerContentModel.trigger('change:state');
expect(this.view._checkEditorModel).toHaveBeenCalled();
});
it('should call ._toggleOverlay when _overlayModel:visible changes', function () {
spyOn(this.view, '_toggleOverlay');
this.view._initBinds();
this.view._overlayModel.trigger('change:visible');
expect(this.view._toggleOverlay).toHaveBeenCalled();
});
it('should call _setViewValues if layerContentModel:state changes', function () {
spyOn(this.view, '_setViewValues');
this.view._initBinds();
this.view._layerContentModel.trigger('change:state');
expect(this.view._setViewValues).toHaveBeenCalled();
});
it('should call _checkEditorModel if layerContentModel:state changes', function () {
spyOn(this.view, '_checkEditorModel');
this.view._initBinds();
this.view._layerContentModel.trigger('change:state');
expect(this.view._checkEditorModel).toHaveBeenCalled();
});
it('should call render if _queryGeometryModel:status changes', function () {
this.view._initBinds();
this.view._queryGeometryModel.trigger('change:status');
expect(this.view.render).toHaveBeenCalled();
});
});
describe('._initViewState', function () {
it('should initialize _viewState model', function () {
expect(this.view._viewState).toBeDefined();
expect(this.view._viewState.get('isDataFiltered')).toEqual(false);
});
it('should call _setViewValues', function () {
spyOn(this.view, '_setViewValues');
this.view._initViewState();
expect(this.view._setViewValues).toHaveBeenCalled();
});
});
describe('._setViewValues', function () {
it('should update _viewState', function (done) {
spyOn(this.view._layerDefinitionModel, 'isDataFiltered').and.returnValue(Promise.resolve(true));
this.view._viewState.set('isDataFiltered', false, { silent: true });
this.view._setViewValues();
setTimeout(function () {
expect(this.view._viewState.get('isDataFiltered')).toEqual(true);
done();
}.bind(this), 0);
});
});
describe('._buildAggregationCarouselCollection', function () {
it('should create _carouselCollection and listen to change:selected event', function () {
spyOn(this.view, '_onSelectAggregation');
var carouselCollection = this.view._buildAggregationCarouselCollection();
expect(carouselCollection).toBeDefined();
this.view._carouselCollection.trigger('change:selected');
expect(this.view._onSelectAggregation).toHaveBeenCalled();
});
});
describe('._renderCarousel', function () {
it('should append carousel markup', function () {
this.view.$el.html('');
spyOn(this.view, '_buildAggregationCarouselCollection').and.callThrough();
this.view._renderCarousel();
expect(this.view.$('ul.Carousel-list > li.Carousel-item').length).toBe(2);
expect(this.view._buildAggregationCarouselCollection).toHaveBeenCalled();
});
});
describe('._toggleOverlay', function () {
it('should toggle overlay', function () {
expect(this.view.$el.hasClass('is-disabled')).toBe(false);
this.view._overlayModel.set('visible', true, { silent: true });
this.view._toggleOverlay();
expect(this.view.$el.hasClass('is-disabled')).toBe(true);
});
});
describe('._renderError', function () {
it('should render the correct template', function () {
var expectedMarkup = styleSQLErrorTemplate({
body: _t('editor.error-query.body', {
action: actionErrorTemplate({
label: _t('editor.error-query.label')
})
})
});
this.view.$el.empty();
this.view._renderError();
expect(this.view.$el.html()).toContain(expectedMarkup);
});
});
describe('._renderFilteredData', function () {
it('should render the correct template', function () {
this.view._renderFilteredData();
expect(this.view.$el.html()).toContain('editor.layers.warnings.no-data.message');
expect(this.view.$el.html()).toContain('editor.layers.warnings.no-data.action-message');
});
});
describe('on style changed', function () {
beforeEach(function () {
spyOn(this.model, 'removeStylesPreAutoStyle');
this.layerDefinitionModel.save.calls.reset();
// Fake styleModel change
var fillAttrs = _.clone(this.model.get('fill'));
fillAttrs.size = 34;
this.model.set('fill', fillAttrs);
});
it('should remove autogenerated attribute', function () {
expect(this.model.get('autogenerated')).toBeFalsy();
});
it('should remove styles pre autostyle if autostyle', function () {
this.layerDefinitionModel.set({autoStyle: true}, {silent: true});
this.model._stylesPreAutoStyle = true;
var fillAttrs = _.clone(this.model.get('fill'));
fillAttrs.size = 12;
this.model.set('fill', fillAttrs);
expect(this.model.get('autogenerated')).toBeFalsy();
expect(this.model.removeStylesPreAutoStyle).toHaveBeenCalled();
});
it('should not change cartocss_custom property when styleModel changes', function () {
this.layerDefinitionModel.set('cartocss_custom', true);
var fillAttrs = _.clone(this.model.get('fill'));
fillAttrs.size = 12;
this.model.set('fill', fillAttrs);
expect(this.layerDefinitionModel.get('cartocss_custom')).toBe(true);
});
it('should save the layer', function () {
expect(this.layerDefinitionModel.save.calls.count()).toBe(1);
});
});
describe('autoStyle', function () {
it('render on autostyle changes', function () {
this.layerDefinitionModel.set({autoStyle: false});
expect(StyleContentView.prototype.render).toHaveBeenCalled();
});
});
describe('_checkEditorModel', function () {
var hasGeomPromise = null;
beforeEach(function () {
hasGeomPromise = fakePromise(this.view._queryGeometryModel, 'hasValueAsync');
});
afterEach(function () {
hasGeomPromise = null;
});
it('set _editorModel to disabled if _isErrored when the hasGeom promise resolves', function (done) {
var self = this;
spyOn(this.view, '_isErrored').and.returnValue(true);
this.view._editorModel.set({ disabled: false }, { silent: true });
this.view._checkEditorModel();
hasGeomPromise.resolve(true);
setTimeout(function () {
expect(self.view._editorModel.get('disabled')).toBe(true);
done();
}, 0);
});
it('set _editorModel to disabled if hasGeom resolve with false', function (done) {
var self = this;
spyOn(this.view, '_isErrored').and.returnValue(false);
this.view._editorModel.set({ disabled: false }, { silent: true });
this.view._checkEditorModel();
hasGeomPromise.resolve(false);
setTimeout(function () {
expect(self.view._editorModel.get('disabled')).toBe(true);
done();
}, 0);
});
it('set _editorModel to enabled if hasGeom resolve with true and not _isErrored', function (done) {
var self = this;
spyOn(this.view, '_isErrored').and.returnValue(false);
this.view._editorModel.set({ disabled: true }, { silent: true });
this.view._checkEditorModel();
hasGeomPromise.resolve(true);
setTimeout(function () {
expect(self.view._editorModel.get('disabled')).toBe(false);
done();
}, 0);
});
});
it('should not have leaks', function () {
expect(this.view).toHaveNoLeaks();
});
});