Initial commit
This commit is contained in:
+43
@@ -0,0 +1,43 @@
|
||||
var $ = require('jquery');
|
||||
var AnalysisTooltip = require('builder/editor/layers/analysis-views/analyses-tooltip-error');
|
||||
|
||||
describe('editor/layers/analysis-views/analysis-tooltip-error', function () {
|
||||
beforeEach(function () {
|
||||
this.view = $('<div class="view"><div class="Error" style="width:10px; height: 10px; background: red; "></div></div>');
|
||||
$('body').append(this.view);
|
||||
|
||||
this.analysisNode = {
|
||||
get: function (what) {
|
||||
if (what === 'status') return 'failed';
|
||||
if (what === 'error') return false;
|
||||
if (what === 'id') return 'a1';
|
||||
}
|
||||
};
|
||||
|
||||
this._analysisTooltip = new AnalysisTooltip({
|
||||
analysisNode: this.analysisNode,
|
||||
element: this.view,
|
||||
triggerSelector: '.Error'
|
||||
});
|
||||
});
|
||||
|
||||
it('should not have any leaks', function () {
|
||||
expect(this._analysisTooltip).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
it('should create the tooltip on mouseover', function () {
|
||||
this.view.find('.Error').trigger('mouseover');
|
||||
expect(this._analysisTooltip.tooltip).toBeDefined();
|
||||
});
|
||||
|
||||
it('should destroy the tooltip on mouseout', function () {
|
||||
this.view.find('.Error').trigger('mouseover');
|
||||
expect(this._analysisTooltip.tooltip).toBeDefined();
|
||||
this.view.find('.Error').trigger('mouseout');
|
||||
expect(this._analysisTooltip.tooltip).not.toBeDefined();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
$('.view').remove();
|
||||
});
|
||||
});
|
||||
+161
@@ -0,0 +1,161 @@
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var CompositeLayerAnalysisView = require('builder/editor/layers/analysis-views/composite-layer-analysis-view');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
|
||||
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
|
||||
|
||||
describe('editor/layers/analysis-views/composite-layer-analysis-view', function () {
|
||||
beforeEach(function () {
|
||||
var configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
|
||||
var userModel = new UserModel({}, {
|
||||
configModel: configModel
|
||||
});
|
||||
|
||||
this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
|
||||
configModel: configModel,
|
||||
userModel: userModel
|
||||
});
|
||||
|
||||
this.analysisDefinitionNodesCollection.add({
|
||||
id: 'a1',
|
||||
type: 'intersection',
|
||||
primary_source_name: 'target',
|
||||
status: 'ready',
|
||||
params: {
|
||||
source: {
|
||||
id: 'b0', // ref, belongs to another layer
|
||||
type: 'source',
|
||||
params: {
|
||||
query: 'SELECT * FROM second'
|
||||
},
|
||||
options: {
|
||||
table_name: 'second'
|
||||
}
|
||||
},
|
||||
target: {
|
||||
id: 'a0',
|
||||
type: 'source',
|
||||
params: {
|
||||
query: 'SELECT * FROM first'
|
||||
},
|
||||
options: {
|
||||
table_name: 'first'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
var model = this.analysisDefinitionNodesCollection.get('a1');
|
||||
|
||||
this.layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
|
||||
configModel: configModel,
|
||||
userModel: userModel,
|
||||
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
|
||||
mapId: 'map-123',
|
||||
stateDefinitionModel: {}
|
||||
});
|
||||
|
||||
this.layerDefinitionsCollection.add({
|
||||
id: 'l1',
|
||||
kind: 'carto',
|
||||
options: {
|
||||
table_name: 'first',
|
||||
cartocss: 'asd'
|
||||
}
|
||||
});
|
||||
this.layerDefinitionsCollection.add({
|
||||
id: 'l2',
|
||||
kind: 'carto',
|
||||
options: {
|
||||
table_name: 'second',
|
||||
cartocss: 'asd'
|
||||
}
|
||||
});
|
||||
|
||||
this.layerDefinitionModel = this.layerDefinitionsCollection.at(0);
|
||||
this.layerDefinitionModel.set({ source: model.id });
|
||||
|
||||
this.view = new CompositeLayerAnalysisView({
|
||||
model: model,
|
||||
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
|
||||
layerDefinitionModel: this.layerDefinitionModel
|
||||
});
|
||||
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
describe('when secondary source is a source node belonging to another layer', function () {
|
||||
beforeEach(function () {
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should render the own source', function () {
|
||||
expect(this.view.$('.js-primary-source').text()).toContain('a1');
|
||||
expect(this.view.$('.js-primary-source').text()).toContain('intersection');
|
||||
});
|
||||
|
||||
it('should render the secondary source as a reference to the other layer', function () {
|
||||
expect(this.view.$('.js-secondary-source').text()).toContain('second');
|
||||
});
|
||||
|
||||
it('should render the id', function () {
|
||||
expect(this.view.$('.js-secondary-source').text()).toContain('b0');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when secondary source is a (non-source) node belonging to another layer ', function () {
|
||||
beforeEach(function () {
|
||||
this.analysisDefinitionNodesCollection.add({
|
||||
id: 'b1',
|
||||
type: 'buffer',
|
||||
status: 'ready',
|
||||
params: {
|
||||
radius: 100,
|
||||
source: this.analysisDefinitionNodesCollection.get('b0').toJSON()
|
||||
}
|
||||
});
|
||||
this.analysisDefinitionNodesCollection.get('a1').set('source', 'b1');
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should render the own source', function () {
|
||||
expect(this.view.$('.js-primary-source').text()).toContain('a1');
|
||||
expect(this.view.$('.js-primary-source').text()).toContain('intersection');
|
||||
});
|
||||
|
||||
it('should render the secondary source as a reference to the other layer', function () {
|
||||
expect(this.view.$('.js-secondary-source').text()).toContain('area-of-influence');
|
||||
});
|
||||
|
||||
it('should render the id', function () {
|
||||
expect(this.view.$('.js-secondary-source').text()).toContain('b1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when secondary source is a source node belonging to own layer', function () {
|
||||
beforeEach(function () {
|
||||
this.analysisDefinitionNodesCollection.get('b0').set('id', 'own');
|
||||
this.analysisDefinitionNodesCollection.get('a1').set('source', 'own');
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should render the own source', function () {
|
||||
expect(this.view.$('.js-primary-source').text()).toContain('a1');
|
||||
expect(this.view.$('.js-primary-source').text()).toContain('intersection');
|
||||
});
|
||||
|
||||
it('should render the secondary source as a source but without the id', function () {
|
||||
expect(this.view.$('.js-secondary-source').text()).toContain('second');
|
||||
});
|
||||
|
||||
it('should not render the id', function () {
|
||||
expect(this.view.$('.js-secondary-source').text()).not.toContain('own');
|
||||
});
|
||||
});
|
||||
});
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
var Backbone = require('backbone');
|
||||
var _ = require('underscore');
|
||||
var AnalysisDefinitionNodeModel = require('builder/data/analysis-definition-node-model');
|
||||
var DefaultLayerAnalysisView = require('builder/editor/layers/analysis-views/default-layer-analysis-view');
|
||||
var $ = require('jquery');
|
||||
|
||||
describe('editor/layers/analysis-views/default-layers-analysis-view', function () {
|
||||
var $el;
|
||||
|
||||
beforeEach(function () {
|
||||
$el = $('<div class="js-layer"><div class="js-Editor-ListLayer-titleText"></div></div>');
|
||||
|
||||
this.model = new AnalysisDefinitionNodeModel({
|
||||
id: 'a3',
|
||||
type: 'trade-area',
|
||||
kind: 'walk',
|
||||
time: 300,
|
||||
source: 'a2'
|
||||
}, {
|
||||
configModel: {},
|
||||
sqlAPI: {}
|
||||
});
|
||||
|
||||
this.analysisNode = new Backbone.Model();
|
||||
this.layerDefinitionModel = new Backbone.Model({id: 'l-1'});
|
||||
|
||||
this.view = new DefaultLayerAnalysisView({
|
||||
model: this.model,
|
||||
analysisNode: this.analysisNode,
|
||||
layerDefinitionModel: this.layerDefinitionModel
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
$el.remove();
|
||||
});
|
||||
|
||||
describe('.render', function () {
|
||||
beforeEach(function () {
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should render correctly', function () {
|
||||
expect(_.size(this.view._subviews)).toBe(2); // [AnalysisTooltip, TipsyTooltipView]
|
||||
expect(this.view.$el.text()).toContain('analyses.area-of-influence');
|
||||
expect(this.view.el.className).toContain('js-analysis-node');
|
||||
expect(this.view.$el.attr('data-analysis-node-id')).toContain('a3');
|
||||
});
|
||||
|
||||
describe('when analysis node is loading', function () {
|
||||
it('should render the loading state', function () {
|
||||
expect(this.view.$el.html()).toContain('Loader');
|
||||
expect(this.view.$el.text()).not.toContain('a3');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when analysis node is ready', function () {
|
||||
it('should render the node id', function () {
|
||||
this.analysisNode.set('status', 'ready');
|
||||
|
||||
expect(this.view.$el.text()).toContain('a3');
|
||||
expect(this.view.$el.html()).not.toContain('Loader');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when analysis node status is failed', function () {
|
||||
it('should render the error elements', function () {
|
||||
this.analysisNode.set('status', 'failed');
|
||||
|
||||
expect(this.view.$el.hasClass('has-error')).toBe(true);
|
||||
expect(this.view.$('.Editor-ListAnalysis-itemError').length).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('._initBinds', function () {
|
||||
it('should call _toggleHover on _stateModel change highlighted', function () {
|
||||
spyOn(this.view, '_toggleHover');
|
||||
this.view._initBinds();
|
||||
this.view._stateModel.trigger('change:highlighted');
|
||||
|
||||
expect(this.view._toggleHover).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._onMouseEnter', function () {
|
||||
it('should set _stateModel highlighted to true', function () {
|
||||
expect(this.view._stateModel.get('highlighted')).toBe(false);
|
||||
|
||||
this.view._onMouseEnter();
|
||||
|
||||
expect(this.view._stateModel.get('highlighted')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._onMouseLeave', function () {
|
||||
it('should set _stateModel highlighted to false', function () {
|
||||
this.view._stateModel.set('highlighted', true, { silent: true });
|
||||
expect(this.view._stateModel.get('highlighted')).toBe(true);
|
||||
|
||||
this.view._onMouseLeave();
|
||||
|
||||
expect(this.view._stateModel.get('highlighted')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._toggleHover', function () {
|
||||
it('should toggle hover', function () {
|
||||
$el.append(this.view.render().el);
|
||||
|
||||
this.view._stateModel.set('highlighted', true, { silent: true });
|
||||
this.view._toggleHover();
|
||||
|
||||
expect($el.hasClass('is-hover')).toBe(true);
|
||||
expect($el.find('.js-Editor-ListLayer-titleText').hasClass('is-hover')).toBe(true);
|
||||
|
||||
this.view._stateModel.set('highlighted', false, { silent: true });
|
||||
this.view._toggleHover();
|
||||
|
||||
expect($el.hasClass('is-hover')).toBe(false);
|
||||
expect($el.find('.js-Editor-ListLayer-titleText').hasClass('is-hover')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
this.view.clean();
|
||||
});
|
||||
});
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
var Backbone = require('backbone');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var AnalysisDefinitionNodeModel = require('builder/data/analysis-definition-node-model');
|
||||
var RefLayerAnalysisView = require('builder/editor/layers/analysis-views/ref-layer-analysis-view');
|
||||
|
||||
describe('editor/layers/analysis-views/ref-layer-analysis-view', function () {
|
||||
beforeEach(function () {
|
||||
this.model = new AnalysisDefinitionNodeModel({
|
||||
id: 'a3',
|
||||
type: 'trade-area',
|
||||
kind: 'walk',
|
||||
time: 300,
|
||||
source: 'a2'
|
||||
}, {
|
||||
configModel: {},
|
||||
sqlAPI: {}
|
||||
});
|
||||
|
||||
this.analysisNode = new Backbone.Model();
|
||||
this.layerDefinitionModel = new LayerDefinitionModel({
|
||||
id: 'layerA',
|
||||
kind: 'carto',
|
||||
options: {
|
||||
table_name: 'alice'
|
||||
}
|
||||
}, {
|
||||
configModel: {}
|
||||
});
|
||||
spyOn(this.layerDefinitionModel, 'getName').and.returnValue('layer-name');
|
||||
|
||||
this.view = new RefLayerAnalysisView({
|
||||
model: this.model,
|
||||
analysisNode: this.analysisNode,
|
||||
layerDefinitionModel: this.layerDefinitionModel
|
||||
});
|
||||
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
it('should render title', function () {
|
||||
expect(this.view.$el.text()).toContain('analyses.area-of-influence');
|
||||
expect(this.view.$el.text()).toContain('layer-name');
|
||||
});
|
||||
|
||||
it('should render the loading until ready', function () {
|
||||
expect(this.view.$el.html()).toContain('Loader');
|
||||
expect(this.view.$el.text()).not.toContain('a3');
|
||||
});
|
||||
|
||||
it('should render the data-attrs and js hook to edit analysis', function () {
|
||||
expect(this.view.el.className).toContain('js-analysis-node');
|
||||
expect(this.view.el.dataset.analysisNodeId).toEqual('a3');
|
||||
});
|
||||
|
||||
describe('when analysis node is ready', function () {
|
||||
beforeEach(function () {
|
||||
this.analysisNode.set('status', 'ready');
|
||||
});
|
||||
|
||||
it('should render id', function () {
|
||||
expect(this.view.$el.text()).toContain('a3');
|
||||
expect(this.view.$el.html()).not.toContain('Loader');
|
||||
});
|
||||
});
|
||||
});
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
var Backbone = require('backbone');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var SourceLayerAnalysisView = require('builder/editor/layers/analysis-views/source-layer-analysis-view');
|
||||
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var TableModel = require('builder/data/table-model');
|
||||
var _ = require('underscore');
|
||||
var $ = require('jquery');
|
||||
|
||||
describe('editor/layers/analysis-views/source-layer-analysis-view', function () {
|
||||
var $el;
|
||||
|
||||
beforeEach(function () {
|
||||
$el = $('<div class="js-layer"><div class="js-Editor-ListLayer-titleText"></div></div>');
|
||||
|
||||
this.configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
|
||||
this.userModel = new UserModel({
|
||||
username: 'chacho'
|
||||
}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection([{
|
||||
id: 'a0',
|
||||
type: 'source',
|
||||
table_name: 'foo_bar',
|
||||
params: {
|
||||
query: 'SELECT * FROM foo_bar'
|
||||
}
|
||||
}], {
|
||||
configModel: this.configModel,
|
||||
userModel: this.userModel
|
||||
});
|
||||
this.sourceAnalysisDefinitionNodeModel = this.analysisDefinitionNodesCollection.get('a0');
|
||||
this.layerDefinitionModel = new Backbone.Model({user_name: 'somebody'});
|
||||
this.layerDefinitionModel.getQualifiedTableName = function () {};
|
||||
|
||||
this.view = new SourceLayerAnalysisView({
|
||||
model: this.sourceAnalysisDefinitionNodeModel,
|
||||
analysisNode: this.sourceAnalysisDefinitionNodeModel,
|
||||
layerDefinitionModel: this.layerDefinitionModel
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
$el.remove();
|
||||
});
|
||||
|
||||
describe('.render', function () {
|
||||
it('should render correctly', function () {
|
||||
this.view.render();
|
||||
|
||||
expect(this.view.$el.text()).toContain('a0');
|
||||
expect(this.view.$el.text()).toContain('foo_bar');
|
||||
expect(_.size(this.view._subviews)).toBe(1); // [tooltip]
|
||||
});
|
||||
|
||||
describe('when layer is sync', function () {
|
||||
var syncView;
|
||||
|
||||
beforeEach(function () {
|
||||
var table = new TableModel({
|
||||
id: 'harrr',
|
||||
name: 'another_table',
|
||||
synchronization: {
|
||||
id: 'test'
|
||||
}
|
||||
}, {
|
||||
parse: true,
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
spyOn(this.sourceAnalysisDefinitionNodeModel, 'getTableModel').and.returnValue(table);
|
||||
|
||||
syncView = new SourceLayerAnalysisView({
|
||||
model: this.sourceAnalysisDefinitionNodeModel,
|
||||
analysisNode: this.sourceAnalysisDefinitionNodeModel,
|
||||
layerDefinitionModel: this.layerDefinitionModel
|
||||
});
|
||||
|
||||
syncView.render();
|
||||
});
|
||||
|
||||
it('should render correctly', function () {
|
||||
expect(syncView.$el.html()).toContain('CDB-IconFont CDB-IconFont-wifi');
|
||||
expect(_.size(syncView._subviews)).toBe(2); // [tooltip, sync tooltip]
|
||||
});
|
||||
});
|
||||
|
||||
describe('when custom query is applied', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(this.view._analysisNode, 'isCustomQueryApplied').and.returnValue(true);
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should render correctly', function () {
|
||||
expect(this.view.$el.html()).toContain('js-sql');
|
||||
expect(this.view.$('.js-sql').html()).toContain('SQL');
|
||||
expect(_.size(this.view._subviews)).toBe(2); // [tooltip, customQuery tooltip]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('._initBinds', function () {
|
||||
it('should call _toggleHover on _stateModel change highlighted', function () {
|
||||
spyOn(this.view, '_toggleHover');
|
||||
this.view._initBinds();
|
||||
this.view._stateModel.trigger('change:highlighted');
|
||||
|
||||
expect(this.view._toggleHover).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call render on _tableNodeModel change synchronization', function () {
|
||||
spyOn(this.view, 'render');
|
||||
this.view._initBinds();
|
||||
this.view._tableNodeModel.trigger('change:synchronization');
|
||||
|
||||
expect(this.view.render).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._onMouseEnter', function () {
|
||||
it('should set _stateModel highlighted to true', function () {
|
||||
expect(this.view._stateModel.get('highlighted')).toBe(false);
|
||||
|
||||
this.view._onMouseEnter();
|
||||
|
||||
expect(this.view._stateModel.get('highlighted')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._onMouseLeave', function () {
|
||||
it('should set _stateModel highlighted to false', function () {
|
||||
this.view._stateModel.set('highlighted', true, { silent: true });
|
||||
expect(this.view._stateModel.get('highlighted')).toBe(true);
|
||||
|
||||
this.view._onMouseLeave();
|
||||
|
||||
expect(this.view._stateModel.get('highlighted')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._toggleHover', function () {
|
||||
it('should toggle hover', function () {
|
||||
$el.append(this.view.render().el);
|
||||
|
||||
this.view._stateModel.set('highlighted', true, { silent: true });
|
||||
this.view._toggleHover();
|
||||
|
||||
expect($el.hasClass('is-hover')).toBe(true);
|
||||
expect($el.find('.js-Editor-ListLayer-titleText').hasClass('is-hover')).toBe(true);
|
||||
|
||||
this.view._stateModel.set('highlighted', false, { silent: true });
|
||||
this.view._toggleHover();
|
||||
|
||||
expect($el.hasClass('is-hover')).toBe(false);
|
||||
expect($el.find('.js-Editor-ListLayer-titleText').hasClass('is-hover')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
this.view.clean();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,151 @@
|
||||
var BasemapContentView = require('builder/editor/layers/basemap-content-view');
|
||||
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
|
||||
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var CustomBaselayersCollection = require('builder/data/custom-baselayers-collection');
|
||||
var FactoryModals = require('../../factories/modals');
|
||||
|
||||
describe('editor/layers/basemap-content-view', function () {
|
||||
beforeEach(function () {
|
||||
var configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
|
||||
var userModel = new UserModel({}, {
|
||||
configModel: configModel
|
||||
});
|
||||
|
||||
this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
|
||||
configModel: configModel,
|
||||
userModel: userModel
|
||||
});
|
||||
|
||||
this.basemaps = {
|
||||
CARTO: {
|
||||
positron_rainbow: {
|
||||
className: 'positron_rainbow',
|
||||
urlTemplate: 'http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png'
|
||||
},
|
||||
dark_matter_rainbow: {
|
||||
className: 'dark_matter_rainbow',
|
||||
urlTemplate: 'http://{s}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}.png'
|
||||
}
|
||||
},
|
||||
Stamen: {
|
||||
watercolor: {
|
||||
className: 'watercolor_stamen',
|
||||
urlTemplate: 'https://stamen-tiles-{s}.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg'
|
||||
}
|
||||
},
|
||||
Here: {
|
||||
nokia_day: {
|
||||
className: 'nokia_day',
|
||||
urlTemplate: 'https://{s}.maps.nlp.nokia.com/maptile/2.1/maptile/newest/normal.day/{z}/{x}/{y}/256/png8?lg=eng&token='
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
|
||||
configModel: configModel,
|
||||
userModel: userModel,
|
||||
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
|
||||
mapId: 'm-123',
|
||||
stateDefinitionModel: {}
|
||||
});
|
||||
this.layerDefinitionsCollection.add({
|
||||
id: 'l-1',
|
||||
kind: 'carto',
|
||||
options: {
|
||||
table_name: 'foo',
|
||||
className: 'positron_rainbow',
|
||||
category: 'CARTO'
|
||||
}
|
||||
});
|
||||
|
||||
this.customBaselayersCollection = new CustomBaselayersCollection([{
|
||||
id: 'basemap-id-1',
|
||||
options: {
|
||||
urlTemplate: '',
|
||||
category: 'Custom'
|
||||
}
|
||||
}], {
|
||||
configModel: configModel,
|
||||
currentUserId: 'current-user-id'
|
||||
});
|
||||
|
||||
this.view = new BasemapContentView({
|
||||
layerDefinitionsCollection: this.layerDefinitionsCollection,
|
||||
basemaps: this.basemaps,
|
||||
stackLayoutModel: {},
|
||||
customBaselayersCollection: this.customBaselayersCollection,
|
||||
modals: FactoryModals.createModalService(),
|
||||
configModel: configModel
|
||||
});
|
||||
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should render properly', function () {
|
||||
expect(Object.keys(this.view._subviews).length).toBe(2); // header, content
|
||||
});
|
||||
|
||||
it('should update header if baseLayer changes', function () {
|
||||
expect(this.view.$('.Editor-HeaderInfo-titleText').text()).toBe('foo editor.layers.basemap.by CARTO');
|
||||
this.view._baseLayer.set({
|
||||
name: 'bar',
|
||||
category: 'Stamen'
|
||||
});
|
||||
expect(this.view.$('.Editor-HeaderInfo-titleText').text()).toBe('bar editor.layers.basemap.by Stamen');
|
||||
});
|
||||
|
||||
it('should update select if category changes', function () {
|
||||
expect(this.view._categoriesCollection.at(0).get('selected')).toBe(true);
|
||||
expect(this.view._categoriesCollection.at(1).get('selected')).toBe(false);
|
||||
expect(this.view.$('.Mosaic-item').length).toBe(2);
|
||||
|
||||
this.view._categoriesCollection.at(1).set('selected', true);
|
||||
|
||||
expect(this.view._categoriesCollection.at(0).get('selected')).toBe(false);
|
||||
expect(this.view._categoriesCollection.at(1).get('selected')).toBe(true);
|
||||
expect(this.view.$('.Mosaic-item').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should update disabled if model changes', function () {
|
||||
expect(this.view.$('.js-basemapContent').hasClass('is-disabled')).toEqual(false);
|
||||
this.view.model.set('disabled', true);
|
||||
expect(this.view.$('.js-basemapContent').hasClass('is-disabled')).toEqual(true);
|
||||
this.view.model.set('disabled', false);
|
||||
expect(this.view.$('.js-basemapContent').hasClass('is-disabled')).toEqual(false);
|
||||
});
|
||||
|
||||
describe('when selected basemap changes', function () {
|
||||
it('should update category', function () {
|
||||
expect(this.view._categoriesCollection.getSelected().getName()).toBe('CARTO');
|
||||
this.view._layerDefinitionsCollection.at(0).set('category', 'Here');
|
||||
expect(this.view._categoriesCollection.getSelected().getName()).toBe('Here');
|
||||
this.view._layerDefinitionsCollection.at(0).set('category', 'Stamen');
|
||||
expect(this.view._categoriesCollection.getSelected().getName()).toBe('Stamen');
|
||||
});
|
||||
});
|
||||
|
||||
describe('._onAddBasemap', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(this.view._basemapsCollection, 'updateSelected');
|
||||
|
||||
this.view._basemapsCollection.add({
|
||||
id: 'basemap-id-2',
|
||||
urlTemplate: 'https://b.example.com/{z}/{x}/{y}.png',
|
||||
val: 'httpsbexamplecomzxypng'
|
||||
});
|
||||
});
|
||||
|
||||
it('should update selected in basemapsCollection', function () {
|
||||
expect(this.view._basemapsCollection.updateSelected).toHaveBeenCalledWith('httpsbexamplecomzxypng');
|
||||
});
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
});
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
var BasemapCategoriesView = require('builder/editor/layers/basemap-content-views/basemap-categories-view');
|
||||
var CarouselCollection = require('builder/components/custom-carousel/custom-carousel-collection');
|
||||
|
||||
describe('editor/layers/basemap-content-views/basemap-categories-view', function () {
|
||||
beforeEach(function () {
|
||||
this.collection = new CarouselCollection([{
|
||||
selected: true,
|
||||
val: 'CARTO',
|
||||
label: 'CARTO',
|
||||
template: function () {
|
||||
return 'CARTO';
|
||||
}
|
||||
}, {
|
||||
selected: false,
|
||||
val: 'Here',
|
||||
label: 'Here',
|
||||
template: function () {
|
||||
return 'Here';
|
||||
}
|
||||
}, {
|
||||
selected: false,
|
||||
val: 'Stamen',
|
||||
label: 'Stamen',
|
||||
template: function () {
|
||||
return 'Stamen';
|
||||
}
|
||||
}, {
|
||||
selected: false,
|
||||
val: 'Custom',
|
||||
label: 'Custom',
|
||||
template: function () {
|
||||
return 'Custom';
|
||||
}
|
||||
}, {
|
||||
selected: false,
|
||||
val: 'Color',
|
||||
label: 'Color',
|
||||
template: function () {
|
||||
return 'Color';
|
||||
}
|
||||
}]);
|
||||
|
||||
this.view = new BasemapCategoriesView({
|
||||
categoriesCollection: this.collection
|
||||
});
|
||||
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should render properly', function () {
|
||||
expect(this.view.$('.Carousel-item').length).toBe(5);
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
});
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
var BasemapFormModel = require('builder/editor/layers/basemap-content-views/basemap-form-model');
|
||||
var BasemapsCollection = require('builder/editor/layers/basemap-content-views/basemaps-collection');
|
||||
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
|
||||
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var _ = require('underscore');
|
||||
|
||||
describe('editor/layers/basemap-content-views/basemap-form-model', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(_, 'debounce').and.callFake(function (func) {
|
||||
return function () {
|
||||
func.apply(this, arguments);
|
||||
};
|
||||
});
|
||||
|
||||
var configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
var userModel = new UserModel({}, {
|
||||
configModel: configModel
|
||||
});
|
||||
|
||||
this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
|
||||
configModel: configModel,
|
||||
userModel: userModel
|
||||
});
|
||||
|
||||
this.layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
|
||||
configModel: configModel,
|
||||
userModel: userModel,
|
||||
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
|
||||
mapId: 'm-123',
|
||||
stateDefinitionModel: {}
|
||||
});
|
||||
this.layerDefinitionsCollection.add({
|
||||
id: 'l-1',
|
||||
kind: 'carto',
|
||||
options: {
|
||||
table_name: 'foo'
|
||||
}
|
||||
});
|
||||
this.basemapsCollection = new BasemapsCollection();
|
||||
this.basemapsCollection.add({
|
||||
default: false,
|
||||
color: '#35AAE5',
|
||||
image: '',
|
||||
maxZoom: 32,
|
||||
className: 'plain',
|
||||
category: 'Color',
|
||||
type: 'Plain',
|
||||
selected: true,
|
||||
val: 'plain',
|
||||
label: 'plain',
|
||||
template: 'plain'
|
||||
});
|
||||
|
||||
this.formModel = new BasemapFormModel({
|
||||
color: {
|
||||
color: {
|
||||
fixed: '#35AAE5',
|
||||
opacity: 1
|
||||
}
|
||||
},
|
||||
image: ''
|
||||
}, {
|
||||
basemapsCollection: this.basemapsCollection,
|
||||
layerDefinitionsCollection: this.layerDefinitionsCollection
|
||||
});
|
||||
|
||||
spyOn(this.basemapsCollection, 'updateSelected');
|
||||
spyOn(this.layerDefinitionsCollection, 'setBaseLayer');
|
||||
});
|
||||
|
||||
describe('when form data changes', function () {
|
||||
beforeEach(function () {
|
||||
this._setColor = function () {
|
||||
this.formModel.set('color', {
|
||||
color: {
|
||||
fixed: '#FABADA',
|
||||
opacity: 1
|
||||
}
|
||||
});
|
||||
}.bind(this);
|
||||
});
|
||||
|
||||
it('should save changes if it is already selected', function () {
|
||||
this.basemapsCollection.at(0).set('selected', true);
|
||||
|
||||
this._setColor();
|
||||
|
||||
expect(this.basemapsCollection.updateSelected).not.toHaveBeenCalled();
|
||||
expect(this.layerDefinitionsCollection.setBaseLayer).toHaveBeenCalledWith({
|
||||
default: false,
|
||||
color: '#FABADA',
|
||||
image: '',
|
||||
maxZoom: 32,
|
||||
className: 'plain',
|
||||
category: 'Color',
|
||||
type: 'Plain',
|
||||
selected: true,
|
||||
val: 'plain',
|
||||
label: 'plain',
|
||||
template: 'plain',
|
||||
minZoom: 0,
|
||||
name: ''
|
||||
});
|
||||
});
|
||||
|
||||
it('should not save changes if it is not selected', function () {
|
||||
this.basemapsCollection.at(0).set('selected', false);
|
||||
|
||||
this._setColor();
|
||||
|
||||
expect(this.basemapsCollection.updateSelected).toHaveBeenCalledWith('plain');
|
||||
expect(this.layerDefinitionsCollection.setBaseLayer).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
var BasemapsCollection = require('builder/editor/layers/basemap-content-views/basemaps-collection');
|
||||
var BasemapFormView = require('builder/editor/layers/basemap-content-views/basemap-form-view');
|
||||
var Backbone = require('backbone');
|
||||
|
||||
describe('editor/layers/basemap-content-views/basemap-form-view', function () {
|
||||
beforeEach(function () {
|
||||
this.model = new Backbone.Model({
|
||||
default: false,
|
||||
color: '#35AAE5',
|
||||
image: '',
|
||||
maxZoom: 32,
|
||||
className: 'plain',
|
||||
category: 'Color',
|
||||
type: 'Plain',
|
||||
selected: true,
|
||||
val: 'plain',
|
||||
label: 'plain',
|
||||
template: function () {
|
||||
return 'plain';
|
||||
}
|
||||
});
|
||||
|
||||
this.view = new BasemapFormView({
|
||||
model: this.model,
|
||||
basemapsCollection: new BasemapsCollection(),
|
||||
layerDefinitionsCollection: {}
|
||||
});
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should render with data from form model', function () {
|
||||
expect(this.view.$('form').length).toEqual(1);
|
||||
expect(this.view.$el.html()).toContain('Form-InputFill');
|
||||
});
|
||||
|
||||
describe('when schema changes', function () {
|
||||
beforeEach(function () {
|
||||
this.prev$form = this.view.$('form');
|
||||
this.view._formModel.trigger('changeSchema');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
this.prev$form = null;
|
||||
});
|
||||
|
||||
it('should re-render the form', function () {
|
||||
expect(this.view.$('form').length).toEqual(1);
|
||||
expect(this.view.$('form')).not.toBe(this.prev$form);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when form is cleaned', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(Backbone.Form.prototype, 'remove').and.callThrough();
|
||||
this.view.clean();
|
||||
});
|
||||
|
||||
it('should remove form when view is cleaned', function () {
|
||||
expect(Backbone.Form.prototype.remove).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not have any leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
});
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var BasemapHeaderView = require('builder/editor/layers/basemap-content-views/basemap-header-view');
|
||||
|
||||
describe('editor/layers/basemap-content-view', function () {
|
||||
beforeEach(function () {
|
||||
this.model = new LayerDefinitionModel({
|
||||
id: 'l-1',
|
||||
type: 'CartoDB',
|
||||
name: 'thename'
|
||||
}, {
|
||||
configModel: {},
|
||||
stateDefinitionModel: {}
|
||||
});
|
||||
|
||||
this.view = new BasemapHeaderView({
|
||||
model: this.model,
|
||||
category: 'thecategory'
|
||||
});
|
||||
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should render title and default category of layer', function () {
|
||||
expect(this.view.$el.text()).toContain('thename editor.layers.basemap.by thecategory');
|
||||
});
|
||||
|
||||
it('should update title and category', function () {
|
||||
expect(this.view.$el.text()).toContain('thename editor.layers.basemap.by thecategory');
|
||||
|
||||
this.view.model.set('name', 'thename1');
|
||||
this.view.category = 'thecategory1';
|
||||
this.view.render();
|
||||
|
||||
expect(this.view.$el.text()).toContain('thename1 editor.layers.basemap.by thecategory1');
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
});
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var BasemapInnerView = require('builder/editor/layers/basemap-content-views/basemap-inner-view');
|
||||
var BasemapsCollection = require('builder/editor/layers/basemap-content-views/basemaps-collection');
|
||||
var CarouselCollection = require('builder/components/custom-carousel/custom-carousel-collection');
|
||||
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
|
||||
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
|
||||
var FactoryModals = require('../../../factories/modals');
|
||||
|
||||
describe('editor/layers/basemap-content-views/basemap-inner-view', function () {
|
||||
beforeEach(function () {
|
||||
var configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
var userModel = new UserModel({}, {
|
||||
configModel: configModel
|
||||
});
|
||||
|
||||
this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
|
||||
configModel: configModel,
|
||||
userModel: userModel
|
||||
});
|
||||
|
||||
this.layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
|
||||
configModel: configModel,
|
||||
userModel: userModel,
|
||||
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
|
||||
mapId: 'm-123',
|
||||
stateDefinitionModel: {}
|
||||
});
|
||||
|
||||
this.layerDefinitionsCollection.add({
|
||||
id: 'l-1',
|
||||
kind: 'carto',
|
||||
options: {
|
||||
table_name: 'foo',
|
||||
className: 'positron_rainbow',
|
||||
category: 'CARTO'
|
||||
}
|
||||
});
|
||||
|
||||
spyOn(BasemapInnerView.prototype, '_renderSelect').and.callThrough();
|
||||
var categoriesCollection = new CarouselCollection(
|
||||
[
|
||||
{ selected: true, val: 'CARTO' },
|
||||
{ val: 'Vizzuality' }
|
||||
]
|
||||
);
|
||||
|
||||
this.view = new BasemapInnerView({
|
||||
categoriesCollection: categoriesCollection,
|
||||
layerDefinitionsCollection: this.layerDefinitionsCollection,
|
||||
basemapsCollection: new BasemapsCollection(),
|
||||
customBaselayersCollection: {},
|
||||
selectedCategoryVal: 'CARTO',
|
||||
model: new Backbone.Model(),
|
||||
modals: FactoryModals.createModalService()
|
||||
});
|
||||
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should render properly', function () {
|
||||
expect(_.size(this.view._subviews)).toBe(2); // categories, select
|
||||
});
|
||||
|
||||
it('should render select content when a new category is selected', function () {
|
||||
this.view._categoriesCollection.at(1).set('selected', true);
|
||||
expect(BasemapInnerView.prototype._renderSelect).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
});
|
||||
+236
@@ -0,0 +1,236 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var BasemapModelFactory = require('builder/editor/layers/basemap-content-views/basemap-model-factory');
|
||||
|
||||
var customBaseLayerAttrs = {
|
||||
'id': '27d7e5bf-7a64-41b1-a6e0-378d842745a0',
|
||||
'type': 'Tiled',
|
||||
'urlTemplate': 'https://stamen-tiles-{s}.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg',
|
||||
'attribution': null,
|
||||
'maxZoom': 21,
|
||||
'minZoom': 0,
|
||||
'name': 'Custom basemap 16',
|
||||
'tms': false,
|
||||
'className': 'httpsstamentilessasslfastlynetwatercolorzxyjpg',
|
||||
'order': 16,
|
||||
'visible': true
|
||||
};
|
||||
|
||||
var expectedCustomBasemapModelAttrs = function (category) {
|
||||
return {
|
||||
'id': '27d7e5bf-7a64-41b1-a6e0-378d842745a0',
|
||||
'urlTemplate': 'https://stamen-tiles-{s}.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg',
|
||||
'minZoom': 0,
|
||||
'maxZoom': 21,
|
||||
'name': 'Custom basemap 16',
|
||||
'className': 'httpsstamentilessasslfastlynetwatercolorzxyjpg',
|
||||
'attribution': null,
|
||||
'category': category,
|
||||
'tms': false,
|
||||
'selected': false,
|
||||
'val': 'httpsstamentilessasslfastlynetwatercolorzxyjpg',
|
||||
'label': 'Custom basemap 16',
|
||||
'default': false,
|
||||
'type': 'Tiled',
|
||||
'template': jasmine.any(Function)
|
||||
};
|
||||
};
|
||||
|
||||
var testCases = [
|
||||
{
|
||||
basemapCategory: 'CARTO',
|
||||
basemapAttrs: {
|
||||
'default': true,
|
||||
'urlTemplate': 'http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png',
|
||||
'subdomains': 'abcd',
|
||||
'minZoom': '0',
|
||||
'maxZoom': '18',
|
||||
'name': 'Positron',
|
||||
'className': 'positron_rainbow_labels',
|
||||
'attribution': 'attribution',
|
||||
'labels': {
|
||||
'urlTemplate': 'http://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}.png'
|
||||
}
|
||||
},
|
||||
expectedBasemapModelAttrs: {
|
||||
'default': true,
|
||||
'urlTemplate': 'http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png',
|
||||
'subdomains': 'abcd',
|
||||
'minZoom': '0',
|
||||
'maxZoom': '18',
|
||||
'name': 'Positron',
|
||||
'className': 'positron_rainbow_labels',
|
||||
'attribution': 'attribution',
|
||||
'labels': {
|
||||
'urlTemplate': 'http://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}.png'
|
||||
},
|
||||
'type': 'Tiled',
|
||||
'category': 'CARTO',
|
||||
'selected': false,
|
||||
'val': 'positron_rainbow_labels',
|
||||
'label': 'Positron',
|
||||
'template': jasmine.any(Function)
|
||||
}
|
||||
},
|
||||
{
|
||||
basemapCategory: 'Stamen',
|
||||
basemapAttrs: {
|
||||
'urlTemplate': 'https://stamen-tiles-{s}.a.ssl.fastly.net/toner-background/{z}/{x}/{y}.png',
|
||||
'subdomains': 'abcd',
|
||||
'minZoom': '0',
|
||||
'maxZoom': '18',
|
||||
'name': 'Toner',
|
||||
'className': 'toner_stamen_labels',
|
||||
'attribution': 'attribution',
|
||||
'labels': {
|
||||
'urlTemplate': 'http://{s}.tile.stamen.com/toner-labels/{z}/{x}/{y}.png'
|
||||
}
|
||||
},
|
||||
expectedBasemapModelAttrs: {
|
||||
'urlTemplate': 'https://stamen-tiles-{s}.a.ssl.fastly.net/toner-background/{z}/{x}/{y}.png',
|
||||
'subdomains': 'abcd',
|
||||
'minZoom': '0',
|
||||
'maxZoom': '18',
|
||||
'name': 'Toner',
|
||||
'className': 'toner_stamen_labels',
|
||||
'attribution': 'attribution',
|
||||
'labels': {
|
||||
'urlTemplate': 'http://{s}.tile.stamen.com/toner-labels/{z}/{x}/{y}.png'
|
||||
},
|
||||
'type': 'Tiled',
|
||||
'category': 'Stamen',
|
||||
'selected': false,
|
||||
'val': 'toner_stamen_labels',
|
||||
'label': 'Toner',
|
||||
'default': false,
|
||||
'template': jasmine.any(Function)
|
||||
}
|
||||
},
|
||||
{
|
||||
basemapCategory: 'Here',
|
||||
basemapAttrs: {
|
||||
'urlTemplate': 'https://stamen-tiles-{s}.a.ssl.fastly.net/toner-hybrid/{z}/{x}/{y}.png',
|
||||
'subdomains': 'abcd',
|
||||
'minZoom': 0,
|
||||
'maxZoom': 18,
|
||||
'name': 'Toner Hybrid',
|
||||
'className': 'toner_hybrid_stamen',
|
||||
'attribution': 'Map tiles by <a href="http://stamen.com">"'
|
||||
},
|
||||
expectedBasemapModelAttrs: {
|
||||
'urlTemplate': 'https://stamen-tiles-{s}.a.ssl.fastly.net/toner-hybrid/{z}/{x}/{y}.png',
|
||||
'subdomains': 'abcd',
|
||||
'minZoom': 0,
|
||||
'maxZoom': 18,
|
||||
'name': 'Toner Hybrid',
|
||||
'className': 'toner_hybrid_stamen',
|
||||
'attribution': 'Map tiles by <a href=\"http://stamen.com\">\"',
|
||||
'type': 'Tiled',
|
||||
'category': 'Here',
|
||||
'selected': false,
|
||||
'val': 'toner_hybrid_stamen',
|
||||
'label': 'Toner Hybrid',
|
||||
'template': jasmine.any(Function),
|
||||
'default': false
|
||||
}
|
||||
},
|
||||
{
|
||||
basemapCategory: 'GMaps',
|
||||
basemapAttrs: {
|
||||
'name': 'GMaps Roadmap',
|
||||
'maxZoom': 40,
|
||||
'minZoom': 0,
|
||||
'baseType': 'roadmap',
|
||||
'className': 'googlemaps',
|
||||
'style': '[]',
|
||||
'default': true
|
||||
},
|
||||
expectedBasemapModelAttrs: {
|
||||
'name': 'GMaps Roadmap',
|
||||
'maxZoom': 40,
|
||||
'minZoom': 0,
|
||||
'baseType': 'roadmap',
|
||||
'className': 'googlemaps',
|
||||
'style': '[]',
|
||||
'default': true,
|
||||
'type': 'GMapsBase',
|
||||
'category': 'GMaps',
|
||||
'selected': false,
|
||||
'val': 'roadmap',
|
||||
'label': 'Roadmap',
|
||||
'template': jasmine.any(Function)
|
||||
}
|
||||
},
|
||||
{
|
||||
basemapCategory: 'Custom',
|
||||
basemapAttrs: customBaseLayerAttrs,
|
||||
expectedBasemapModelAttrs: expectedCustomBasemapModelAttrs('Custom')
|
||||
},
|
||||
{
|
||||
basemapCategory: 'NASA',
|
||||
basemapAttrs: customBaseLayerAttrs,
|
||||
expectedBasemapModelAttrs: expectedCustomBasemapModelAttrs('NASA')
|
||||
},
|
||||
{
|
||||
basemapCategory: 'TileJSON',
|
||||
basemapAttrs: customBaseLayerAttrs,
|
||||
expectedBasemapModelAttrs: expectedCustomBasemapModelAttrs('TileJSON')
|
||||
},
|
||||
{
|
||||
basemapCategory: 'Mapbox',
|
||||
basemapAttrs: customBaseLayerAttrs,
|
||||
expectedBasemapModelAttrs: expectedCustomBasemapModelAttrs('Mapbox')
|
||||
},
|
||||
{
|
||||
basemapCategory: 'WMS',
|
||||
basemapAttrs: customBaseLayerAttrs,
|
||||
expectedBasemapModelAttrs: expectedCustomBasemapModelAttrs('WMS')
|
||||
},
|
||||
{
|
||||
basemapCategory: 'Color',
|
||||
basemapAttrs: {
|
||||
'color': '#FABADA',
|
||||
'image': 'IMAGE',
|
||||
'className': 'CLASS_NAME'
|
||||
},
|
||||
expectedBasemapModelAttrs: {
|
||||
'default': false,
|
||||
'color': '#FABADA',
|
||||
'image': 'IMAGE',
|
||||
'minZoom': 0,
|
||||
'maxZoom': 32,
|
||||
'className': 'plain',
|
||||
'category': 'Color',
|
||||
'type': 'Plain',
|
||||
'selected': false,
|
||||
'name': '',
|
||||
'val': 'plain',
|
||||
'label': 'plain',
|
||||
'template': jasmine.any(Function)
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
describe('builder/editor/layers/basemap-content-views/basemap-model-factory.js', function () {
|
||||
describe('.createBasemapModel', function () {
|
||||
beforeEach(function () {
|
||||
var layerDefinitionsCollection = new Backbone.Collection();
|
||||
layerDefinitionsCollection.getBaseLayer = function () {
|
||||
return new Backbone.Model();
|
||||
};
|
||||
var configModel = new Backbone.Model();
|
||||
this.basemapModelFactory = new BasemapModelFactory(layerDefinitionsCollection, configModel);
|
||||
});
|
||||
|
||||
_.each(testCases, function (testCase) {
|
||||
var basemapCategory = testCase.basemapCategory;
|
||||
var basemapAttrs = testCase.basemapAttrs;
|
||||
var expectedBasemapModelAttrs = testCase.expectedBasemapModelAttrs;
|
||||
|
||||
it('should create a BasemapModel for ' + basemapCategory + ' basemaps', function () {
|
||||
var basemapModel = this.basemapModelFactory.createBasemapModel(basemapCategory, basemapAttrs);
|
||||
expect(basemapModel.toJSON()).toEqual(expectedBasemapModelAttrs);
|
||||
});
|
||||
}, this);
|
||||
});
|
||||
});
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
var BasemapsModel = require('builder/editor/layers/basemap-content-views/basemap-model');
|
||||
|
||||
describe('editor/layers/basemap-content-views/basemap-model', function () {
|
||||
beforeEach(function () {
|
||||
this.model1 = new BasemapsModel({
|
||||
selected: true,
|
||||
val: 'positron_rainbow',
|
||||
label: 'Positron (labels below)',
|
||||
template: function () {
|
||||
return 'positron_rainbow';
|
||||
}
|
||||
});
|
||||
|
||||
this.model2 = new BasemapsModel({
|
||||
selected: false,
|
||||
val: 'dark_matter_rainbow',
|
||||
label: '',
|
||||
template: function () {
|
||||
return 'dark_matter_rainbow';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('.getName', function () {
|
||||
it('should return label or value if empty', function () {
|
||||
expect(this.model1.get('label')).toBe('Positron (labels below)');
|
||||
expect(this.model1.get('label')).toBe(this.model1.getName());
|
||||
expect(this.model2.get('label')).toBe('');
|
||||
expect(this.model2.get('val')).toBe('dark_matter_rainbow');
|
||||
expect(this.model2.get('val')).toBe(this.model2.getName());
|
||||
});
|
||||
});
|
||||
|
||||
describe('.getValue', function () {
|
||||
it('.getValue', function () {
|
||||
expect(this.model1.get('val')).toBe('positron_rainbow');
|
||||
expect(this.model1.getValue()).toBe(this.model1.get('val'));
|
||||
});
|
||||
});
|
||||
});
|
||||
+256
@@ -0,0 +1,256 @@
|
||||
var BasemapSelectView = require('builder/editor/layers/basemap-content-views/basemap-select-view');
|
||||
var BasemapsCollection = require('builder/editor/layers/basemap-content-views/basemaps-collection');
|
||||
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
|
||||
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var CustomBaselayersCollection = require('builder/data/custom-baselayers-collection');
|
||||
var FactoryModals = require('../../../factories/modals');
|
||||
|
||||
describe('editor/layers/basemap-content-views/basemap-select-view', function () {
|
||||
beforeEach(function () {
|
||||
var configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
var userModel = new UserModel({}, {
|
||||
configModel: configModel
|
||||
});
|
||||
|
||||
this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
|
||||
configModel: configModel,
|
||||
userModel: userModel
|
||||
});
|
||||
|
||||
this.layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
|
||||
configModel: configModel,
|
||||
userModel: userModel,
|
||||
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
|
||||
mapId: 'm-123',
|
||||
stateDefinitionModel: {}
|
||||
});
|
||||
this.layerDefinitionsCollection.add({
|
||||
id: 'l-1',
|
||||
kind: 'carto',
|
||||
options: {
|
||||
table_name: 'foo'
|
||||
}
|
||||
});
|
||||
|
||||
this.basemapsCollection = new BasemapsCollection([{
|
||||
default: true,
|
||||
url: 'http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png',
|
||||
subdomains: 'abcd',
|
||||
minZoom: '0',
|
||||
maxZoom: '18',
|
||||
name: 'Positron (labels below)',
|
||||
className: 'positron_rainbow',
|
||||
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>',
|
||||
category: 'CARTO',
|
||||
selected: true,
|
||||
val: 'positron_rainbow',
|
||||
label: 'Positron (labels below)',
|
||||
template: function () {
|
||||
return 'positron_rainbow';
|
||||
}
|
||||
}, {
|
||||
default: false,
|
||||
url: 'http://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png',
|
||||
subdomains: 'abcd',
|
||||
minZoom: '0',
|
||||
maxZoom: '18',
|
||||
name: 'Dark matter (labels below)',
|
||||
className: 'dark_matter_rainbow',
|
||||
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>',
|
||||
category: 'CARTO',
|
||||
selected: false,
|
||||
val: 'dark_matter_rainbow',
|
||||
label: 'Dark matter (labels below)',
|
||||
template: function () {
|
||||
return 'dark_matter_rainbow';
|
||||
}
|
||||
}, {
|
||||
default: false,
|
||||
url: 'https://stamen-tiles-{s}.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.png',
|
||||
subdomains: 'abcd',
|
||||
minZoom: '0',
|
||||
maxZoom: '18',
|
||||
name: 'Watercolor',
|
||||
className: 'watercolor_stamen',
|
||||
attribution: 'Map tiles by <a href="http://stamen.com">Stamen Design</a>, under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://www.openstreetmap.org/copyright">ODbL</a>.',
|
||||
category: 'Stamen',
|
||||
selected: false,
|
||||
val: 'watercolor_stamen',
|
||||
label: 'Watercolor',
|
||||
template: function () {
|
||||
return 'watercolor_stamen';
|
||||
}
|
||||
}, {
|
||||
default: false,
|
||||
url: '://{s}.maps.nlp.nokia.com/maptile/2.1/maptile/newest/normal.day/{z}/{x}/{y}/256/png8?lg=eng&token=A7tBPacePg9Mj_zghvKt9Q&app_id=KuYppsdXZznpffJsKT24',
|
||||
subdomains: '1234',
|
||||
minZoom: '0',
|
||||
maxZoom: '20',
|
||||
name: 'Nokia Day',
|
||||
className: 'nokia_day',
|
||||
attribution: 'Map©2016 HERE <a href="http://here.net/services/terms" target="_blank">Terms of use</a>',
|
||||
category: 'Here',
|
||||
selected: false,
|
||||
val: 'nokia_day',
|
||||
label: 'Nokia Day',
|
||||
template: function () {
|
||||
return 'nokia_day';
|
||||
}
|
||||
}, {
|
||||
default: false,
|
||||
color: '#ff6600',
|
||||
image: '',
|
||||
maxZoom: 32,
|
||||
className: 'plain',
|
||||
category: 'Color',
|
||||
type: 'Plain',
|
||||
selected: false,
|
||||
val: 'plain',
|
||||
label: 'plain',
|
||||
template: function () {
|
||||
return 'plain';
|
||||
}
|
||||
}, {
|
||||
id: 'basemap-id-1',
|
||||
urlTemplate: 'https://stamen-tiles-{s}.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg',
|
||||
minZoom: 0,
|
||||
maxZoom: 21,
|
||||
name: 'Custom basemap 1',
|
||||
className: 'httpsstamentilessasslfastlynetwatercolorzxyjpg',
|
||||
attribution: null,
|
||||
category: 'Custom',
|
||||
selected: false,
|
||||
val: 'httpsstamentilessasslfastlynetwatercolorzxyjpg',
|
||||
default: false,
|
||||
subdomains: '',
|
||||
labels: null,
|
||||
type: 'Tiled'
|
||||
}, {
|
||||
id: 'basemap-id-2',
|
||||
urlTemplate: 'https://a.tiles.mapbox.com/v4/username.12ab45c/{z}/{x}/{y}.png?access_token=aBcDC12323abc',
|
||||
minZoom: 0,
|
||||
maxZoom: 21,
|
||||
name: 'MapBox Streets Outdoors Global Preview',
|
||||
className: 'httpsatilesmapboxcomv4username12ab45czxypngaccess_tokenaBcDC12323abc',
|
||||
attribution: '<a href=\'https://www.mapbox.com/about/maps/\' target=\'_blank\'>© Mapbox © OpenStreetMap</a> <a class=\'mapbox-improve-map\' href=\'https://www.mapbox.com/map-feedback/\' target=\'_blank\'>Improve this map</a>',
|
||||
category: 'Mapbox',
|
||||
selected: false,
|
||||
val: 'httpsatilesmapboxcomv4username12ab45czxypngaccess_tokenaBcDC12323abc',
|
||||
label: 'MapBox Streets Outdoors Global Preview',
|
||||
default: false,
|
||||
subdomains: '',
|
||||
labels: null,
|
||||
type: 'Tiled',
|
||||
highlighted: false
|
||||
}]);
|
||||
|
||||
this.customBaselayersCollection = new CustomBaselayersCollection([{
|
||||
id: 'basemap-id-1',
|
||||
options: {
|
||||
urlTemplate: '',
|
||||
category: 'Custom'
|
||||
}
|
||||
}], {
|
||||
configModel: configModel,
|
||||
currentUserId: 'current-user-id'
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
this.view.clean();
|
||||
});
|
||||
|
||||
it('should render properly', function () {
|
||||
this.view = new BasemapSelectView({
|
||||
layerDefinitionsCollection: this.layerDefinitionsCollection,
|
||||
basemapsCollection: this.basemapsCollection,
|
||||
selectedCategoryVal: 'CARTO',
|
||||
customBaselayersCollection: this.customBaselayersCollection,
|
||||
modals: FactoryModals.createModalService()
|
||||
});
|
||||
this.view.render();
|
||||
|
||||
expect(this.view._filteredBasemapsCollection.length).toBe(2);
|
||||
expect(this.view.$('.Mosaic-item').length).toBe(2);
|
||||
});
|
||||
|
||||
describe('select', function () {
|
||||
afterEach(function () {
|
||||
this.view.clean();
|
||||
});
|
||||
|
||||
it('should render mosaic if category is CARTO, or Here', function () {
|
||||
this.view = new BasemapSelectView({
|
||||
layerDefinitionsCollection: this.layerDefinitionsCollection,
|
||||
basemapsCollection: this.basemapsCollection,
|
||||
selectedCategoryVal: 'Here',
|
||||
customBaselayersCollection: this.customBaselayersCollection,
|
||||
modals: FactoryModals.createModalService()
|
||||
});
|
||||
this.view.render();
|
||||
|
||||
expect(this.view._filteredBasemapsCollection.length).toBe(1);
|
||||
expect(this.view.$('.Mosaic-item').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should render mosaic if category is Stamen', function () {
|
||||
this.view = new BasemapSelectView({
|
||||
layerDefinitionsCollection: this.layerDefinitionsCollection,
|
||||
basemapsCollection: this.basemapsCollection,
|
||||
selectedCategoryVal: 'Stamen',
|
||||
customBaselayersCollection: this.customBaselayersCollection,
|
||||
modals: FactoryModals.createModalService()
|
||||
});
|
||||
this.view.render();
|
||||
|
||||
expect(this.view._filteredBasemapsCollection.length).toBe(1);
|
||||
expect(this.view.$('.Mosaic-item').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should render mosaic if category is Custom', function () {
|
||||
this.view = new BasemapSelectView({
|
||||
layerDefinitionsCollection: this.layerDefinitionsCollection,
|
||||
basemapsCollection: this.basemapsCollection,
|
||||
selectedCategoryVal: 'Custom',
|
||||
customBaselayersCollection: this.customBaselayersCollection,
|
||||
modals: FactoryModals.createModalService()
|
||||
});
|
||||
this.view.render();
|
||||
|
||||
expect(this.view._filteredBasemapsCollection.length).toBe(1);
|
||||
expect(this.view.$('.Mosaic-item').length).toBe(2); // Custom basemap and "Add basemap" button
|
||||
});
|
||||
|
||||
it('should render mosaic if category is Mapbox', function () {
|
||||
this.view = new BasemapSelectView({
|
||||
layerDefinitionsCollection: this.layerDefinitionsCollection,
|
||||
basemapsCollection: this.basemapsCollection,
|
||||
selectedCategoryVal: 'Mapbox',
|
||||
customBaselayersCollection: this.customBaselayersCollection,
|
||||
modals: FactoryModals.createModalService()
|
||||
});
|
||||
this.view.render();
|
||||
|
||||
expect(this.view._filteredBasemapsCollection.length).toBe(1);
|
||||
expect(this.view.$('.Mosaic-item').length).toBe(2); // Mapbox basemap and "Add basemap" button
|
||||
});
|
||||
|
||||
it('should render form if category is Color', function () {
|
||||
this.view = new BasemapSelectView({
|
||||
layerDefinitionsCollection: this.layerDefinitionsCollection,
|
||||
basemapsCollection: this.basemapsCollection,
|
||||
selectedCategoryVal: 'Color',
|
||||
customBaselayersCollection: this.customBaselayersCollection,
|
||||
modals: FactoryModals.createModalService()
|
||||
});
|
||||
this.view.render();
|
||||
|
||||
expect(this.view._filteredBasemapsCollection.length).toBe(1);
|
||||
expect(this.view.$('.Form-InputFill').length).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
+193
@@ -0,0 +1,193 @@
|
||||
var BasemapsCollection = require('builder/editor/layers/basemap-content-views/basemaps-collection');
|
||||
|
||||
describe('editor/layers/basemap-content-views/basemaps-collection', function () {
|
||||
beforeEach(function () {
|
||||
this.collection = new BasemapsCollection([{
|
||||
default: true,
|
||||
url: 'http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png',
|
||||
subdomains: 'abcd',
|
||||
minZoom: '0',
|
||||
maxZoom: '18',
|
||||
name: 'Positron (labels below)',
|
||||
className: 'positron_rainbow',
|
||||
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>',
|
||||
category: 'CARTO',
|
||||
selected: true,
|
||||
val: 'positron_rainbow',
|
||||
label: 'Positron (labels below)',
|
||||
template: function () {
|
||||
return 'positron_rainbow';
|
||||
}
|
||||
}, {
|
||||
default: false,
|
||||
url: 'http://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png',
|
||||
subdomains: 'abcd',
|
||||
minZoom: '0',
|
||||
maxZoom: '18',
|
||||
name: 'Dark matter (labels below)',
|
||||
className: 'dark_matter_rainbow',
|
||||
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>',
|
||||
category: 'CARTO',
|
||||
selected: false,
|
||||
val: 'dark_matter_rainbow',
|
||||
label: 'Dark matter (labels below)',
|
||||
template: function () {
|
||||
return 'dark_matter_rainbow';
|
||||
}
|
||||
}, {
|
||||
default: false,
|
||||
url: 'https://stamen-tiles-{s}.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.png',
|
||||
subdomains: 'abcd',
|
||||
minZoom: '0',
|
||||
maxZoom: '18',
|
||||
name: 'Watercolor',
|
||||
className: 'watercolor_stamen',
|
||||
attribution: 'Map tiles by <a href="http://stamen.com">Stamen Design</a>, under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://www.openstreetmap.org/copyright">ODbL</a>.',
|
||||
category: 'Stamen',
|
||||
selected: false,
|
||||
val: 'watercolor_stamen',
|
||||
label: 'Watercolor',
|
||||
template: function () {
|
||||
return 'watercolor_stamen';
|
||||
}
|
||||
}, {
|
||||
default: false,
|
||||
url: '://{s}.maps.nlp.nokia.com/maptile/2.1/maptile/newest/normal.day/{z}/{x}/{y}/256/png8?lg=eng&token=A7tBPacePg9Mj_zghvKt9Q&app_id=KuYppsdXZznpffJsKT24',
|
||||
subdomains: '1234',
|
||||
minZoom: '0',
|
||||
maxZoom: '20',
|
||||
name: 'Nokia Day',
|
||||
className: 'nokia_day',
|
||||
attribution: 'Map©2016 HERE <a href="http://here.net/services/terms" target="_blank">Terms of use</a>',
|
||||
category: 'Here',
|
||||
selected: false,
|
||||
val: 'nokia_day',
|
||||
label: 'Nokia Day',
|
||||
template: function () {
|
||||
return 'nokia_day';
|
||||
}
|
||||
}, {
|
||||
default: false,
|
||||
color: '#ff6600',
|
||||
image: '',
|
||||
maxZoom: 32,
|
||||
className: 'plain',
|
||||
category: 'Color',
|
||||
type: 'Plain',
|
||||
selected: false,
|
||||
val: 'plain',
|
||||
label: 'plain',
|
||||
template: function () {
|
||||
return 'plain';
|
||||
}
|
||||
}]);
|
||||
|
||||
this.collection.add({
|
||||
id: 'basemap-id-1',
|
||||
urlTemplate: 'https://a.tiles.mapbox.com/v4/username.12ab45c/{z}/{x}/{y}.png?access_token=aBcDC12323abc',
|
||||
maxZoom: 21,
|
||||
minZoom: 0,
|
||||
name: 'MapBox Streets Outdoors Global Preview',
|
||||
className: 'httpsatilesmapboxcomv4username12ab45czxypngaccess_tokenaBcDC12323abc',
|
||||
attribution: '<a href="https://www.mapbox.com/about/maps/" target="_blank">© Mapbox © OpenStreetMap</a> <a class="mapbox-improve-map" href="https://www.mapbox.com/map-feedback/" target="_blank">Improve this map</a>',
|
||||
order: 26,
|
||||
val: 'httpsatilesmapboxcomv4username12ab45czxypngaccess_tokenaBcDC12323abc'
|
||||
});
|
||||
|
||||
this.collection.add({
|
||||
id: 'basemap-id-2',
|
||||
urlTemplate: 'https://stamen-tiles-{s}.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg',
|
||||
maxZoom: 21,
|
||||
minZoom: 0,
|
||||
name: 'Custom basemap 29',
|
||||
className: 'httpsstamentilessasslfastlynetwatercolorzxyjpg',
|
||||
attribution: null,
|
||||
order: 29,
|
||||
val: 'httpsstamentilessasslfastlynetwatercolorzxyjpg'
|
||||
});
|
||||
});
|
||||
|
||||
it('should have only one selected', function () {
|
||||
var l0 = this.collection.at(0);
|
||||
var l1 = this.collection.at(1);
|
||||
|
||||
expect(l0.get('selected')).toBeTruthy();
|
||||
expect(this.collection.where({ selected: true }).length).toBe(1);
|
||||
l1.set('selected', true);
|
||||
expect(this.collection.where({ selected: true }).length).toBe(1);
|
||||
expect(l0.get('selected')).toBeFalsy();
|
||||
expect(l1.get('selected')).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('.getSelected', function () {
|
||||
it('should return selected model', function () {
|
||||
var l0 = this.collection.at(0);
|
||||
var selected = this.collection.getSelected();
|
||||
|
||||
expect(l0.get('selected')).toBeTruthy();
|
||||
expect(selected.get('selected')).toBeTruthy();
|
||||
expect(l0.get('className')).toBe(selected.get('className'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('.findByCategory', function () {
|
||||
it('should return models for that category', function () {
|
||||
expect(this.collection.findByCategory('CARTO').length).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('.getDefaultCategory', function () {
|
||||
it('should return default category', function () {
|
||||
expect(this.collection.getDefaultCategory()).toBe('CARTO');
|
||||
});
|
||||
});
|
||||
|
||||
describe('.getCategories', function () {
|
||||
it('should return an array of categories', function () {
|
||||
expect(this.collection.getCategories()).toContain('CARTO');
|
||||
expect(this.collection.getCategories()).toContain('Here');
|
||||
expect(this.collection.getCategories()).toContain('Stamen');
|
||||
expect(this.collection.getCategories()).toContain('Color');
|
||||
expect(this.collection.getCategories()).toContain('Custom');
|
||||
expect(this.collection.getCategories()).toContain('Mapbox');
|
||||
expect(this.collection.getCategories()).toContain('WMS');
|
||||
expect(this.collection.getCategories()).toContain('TileJSON');
|
||||
});
|
||||
});
|
||||
|
||||
describe('.updateSelected', function () {
|
||||
it('should update selected passing the classname', function () {
|
||||
var l0 = this.collection.at(0);
|
||||
var l1 = this.collection.at(1);
|
||||
|
||||
expect(l0.get('className')).toBe('positron_rainbow');
|
||||
expect(l0.get('selected')).toBeTruthy();
|
||||
expect(l1.get('selected')).toBeFalsy();
|
||||
this.collection.updateSelected('dark_matter_rainbow');
|
||||
expect(l1.get('className')).toBe('dark_matter_rainbow');
|
||||
expect(l0.get('selected')).toBeFalsy();
|
||||
expect(l1).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('.updateCategory', function () {
|
||||
it('should update category passing the classname and a category', function () {
|
||||
var l5 = this.collection.at(5);
|
||||
|
||||
expect(l5.get('category')).toBe('Custom');
|
||||
|
||||
this.collection.updateCategory('httpsatilesmapboxcomv4username12ab45czxypngaccess_tokenaBcDC12323abc', 'Mapbox');
|
||||
|
||||
expect(l5.get('category')).toBe('Mapbox');
|
||||
});
|
||||
});
|
||||
|
||||
describe('.getByValue', function () {
|
||||
it('should return model by value', function () {
|
||||
var l0 = this.collection.at(0);
|
||||
var l1 = this.collection.getByValue('positron_rainbow');
|
||||
|
||||
expect(l0.get('className')).toBe(l1.get('className'));
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,270 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var EditFeatureContentView = require('builder/editor/layers/edit-feature-content-view');
|
||||
var MapModeModel = require('builder/map-mode-model');
|
||||
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
|
||||
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var FeatureDefinitionModel = require('builder/data/feature-definition-model');
|
||||
var EditorModel = require('builder/data/editor-model');
|
||||
var ModalsService = require('builder/components/modals/modals-service-model');
|
||||
var Stylehelper = require('builder/helpers/style');
|
||||
var Router = require('builder/routes/router');
|
||||
|
||||
var QueryGeometryModelMock = function (simple_geom) {
|
||||
var geom = simple_geom || '';
|
||||
|
||||
return {
|
||||
hasValueAsync: function () {
|
||||
return Promise.resolve(geom !== '');
|
||||
},
|
||||
resetFetch: function () {}
|
||||
};
|
||||
};
|
||||
|
||||
describe('editor/layers/edit-feature-content-view', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(Router, 'navigate');
|
||||
jasmine.Ajax.install();
|
||||
jasmine.Ajax.stubRequest(new RegExp('^http(s)?://.*tables.*'))
|
||||
.andReturn({ status: 200 });
|
||||
|
||||
this.configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
this.userModel = new UserModel({}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
var analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
|
||||
configModel: this.configModel,
|
||||
userModel: this.userModel
|
||||
});
|
||||
|
||||
var layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
|
||||
configModel: this.configModel,
|
||||
userModel: this.userModel,
|
||||
analysisDefinitionNodesCollection: analysisDefinitionNodesCollection,
|
||||
mapId: 'm-123',
|
||||
stateDefinitionModel: {}
|
||||
});
|
||||
layerDefinitionsCollection.add({
|
||||
id: 'l-1',
|
||||
kind: 'carto',
|
||||
options: {
|
||||
table_name: 'foo'
|
||||
}
|
||||
});
|
||||
this.layerDefinitionModel = layerDefinitionsCollection.at(0);
|
||||
|
||||
this.mapModeModel = new MapModeModel();
|
||||
this.editorModel = new EditorModel();
|
||||
this.mapStackLayoutModel = jasmine.createSpyObj('stackLayoutModel', ['prevStep', 'nextStep', 'goToStep']);
|
||||
this.modals = new ModalsService();
|
||||
|
||||
this.rowCollection = {
|
||||
resetFetch: jasmine.createSpy('resetFetch')
|
||||
};
|
||||
|
||||
var featureDefinition = new FeatureDefinitionModel({}, {
|
||||
configModel: this.configModel,
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
userModel: this.userModel
|
||||
});
|
||||
this.mapModeModel.enterDrawingFeatureMode(featureDefinition);
|
||||
|
||||
this.view = new EditFeatureContentView({
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
configModel: this.configModel,
|
||||
stackLayoutModel: this.mapStackLayoutModel,
|
||||
mapModeModel: this.mapModeModel,
|
||||
editorModel: this.editorModel,
|
||||
model: new Backbone.Model({
|
||||
hasChanges: false,
|
||||
isValidAttributes: true,
|
||||
isValidGeometry: true
|
||||
}),
|
||||
modals: this.modals
|
||||
});
|
||||
spyOn(this.view, '_addRow').and.callThrough();
|
||||
spyOn(this.view, '_renderInfo').and.callThrough();
|
||||
|
||||
this.view._rowsCollection = this.rowCollection;
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
jasmine.Ajax.uninstall();
|
||||
});
|
||||
|
||||
it('should get table details', function () {
|
||||
expect(this.view._tableName).toBe('foo');
|
||||
expect(this.view._url).toBe('/u/pepe/dataset/foo');
|
||||
});
|
||||
|
||||
it('should render properly', function () {
|
||||
expect(_.size(this.view._subviews)).toBe(2); // header, content
|
||||
});
|
||||
|
||||
describe('when feature is new', function () {
|
||||
it('should add row', function () {
|
||||
expect(this.view._addRow).toHaveBeenCalled();
|
||||
expect(this.view._renderInfo).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when feature already exists', function () {
|
||||
var view;
|
||||
|
||||
beforeEach(function () {
|
||||
var featureDefinition = new FeatureDefinitionModel({
|
||||
cartodb_id: 1
|
||||
}, {
|
||||
configModel: this.configModel,
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
userModel: this.userModel
|
||||
});
|
||||
featureDefinition.fetch();
|
||||
|
||||
this.mapModeModel.enterEditingFeatureMode(featureDefinition);
|
||||
|
||||
view = new EditFeatureContentView({
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
configModel: this.configModel,
|
||||
stackLayoutModel: this.mapStackLayoutModel,
|
||||
mapModeModel: this.mapModeModel,
|
||||
editorModel: this.editorModel,
|
||||
model: new Backbone.Model({
|
||||
hasChanges: false
|
||||
}),
|
||||
modals: this.modals
|
||||
});
|
||||
spyOn(view, '_addRow').and.callThrough();
|
||||
spyOn(view, '_renderInfo').and.callThrough();
|
||||
|
||||
view.render();
|
||||
});
|
||||
|
||||
it('should not add row', function () {
|
||||
expect(view._addRow).not.toHaveBeenCalled();
|
||||
expect(view._renderInfo).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._onDestroyFeatureSuccess', function () {
|
||||
it('should call `_onUpdateFeature`', function () {
|
||||
spyOn(this.view, '_onUpdateFeature').and.callThrough();
|
||||
spyOn(Router, 'goToStyleTab').and.callThrough();
|
||||
this.view.notification = {
|
||||
set: function () {}
|
||||
};
|
||||
spyOn(this.view.notification, 'set');
|
||||
|
||||
this.view._onDestroyFeatureSuccess();
|
||||
|
||||
expect(this.view._onUpdateFeature).toHaveBeenCalledWith('destroy');
|
||||
expect(this.view.notification.set).toHaveBeenCalled();
|
||||
expect(this.rowCollection.resetFetch).toHaveBeenCalled();
|
||||
expect(Router.goToStyleTab).toHaveBeenCalledWith(this.layerDefinitionModel.get('id'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('._onSaveFeatureSuccess', function () {
|
||||
it('should call `_onUpdateFeature`', function () {
|
||||
spyOn(this.view, '_onUpdateFeature').and.callThrough();
|
||||
spyOn(Router, 'editFeature');
|
||||
this.view.notification = {
|
||||
set: function () {}
|
||||
};
|
||||
spyOn(this.view.notification, 'set');
|
||||
|
||||
this.view._onSaveFeatureSuccess('save');
|
||||
|
||||
expect(this.view._onUpdateFeature).toHaveBeenCalled();
|
||||
expect(this.view.notification.set).toHaveBeenCalled();
|
||||
expect(this.rowCollection.resetFetch).not.toHaveBeenCalled();
|
||||
expect(Router.editFeature).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._onUpdateFeature', function () {
|
||||
it('should reset fetch if saving and current geometry model does not have value', function (done) {
|
||||
var self = this;
|
||||
this.view._queryGeometryModel = new QueryGeometryModelMock();
|
||||
spyOn(this.view._queryGeometryModel, 'resetFetch');
|
||||
|
||||
this.view._onUpdateFeature('save');
|
||||
|
||||
setTimeout(function () {
|
||||
expect(self.view._queryGeometryModel.resetFetch).toHaveBeenCalled();
|
||||
expect(self.rowCollection.resetFetch).not.toHaveBeenCalled();
|
||||
done();
|
||||
}, 200);
|
||||
});
|
||||
|
||||
it('should not reset fetch if saving and current geometry model has value', function (done) {
|
||||
var self = this;
|
||||
this.view._queryGeometryModel = new QueryGeometryModelMock('polygon');
|
||||
spyOn(this.view._queryGeometryModel, 'resetFetch');
|
||||
this.view._onUpdateFeature('save');
|
||||
|
||||
setTimeout(function () {
|
||||
expect(self.view._queryGeometryModel.resetFetch).not.toHaveBeenCalled();
|
||||
done();
|
||||
}, 200);
|
||||
});
|
||||
|
||||
it('should reset fetch if destroying', function (done) {
|
||||
var self = this;
|
||||
this.view._queryGeometryModel = new QueryGeometryModelMock();
|
||||
spyOn(this.view._queryGeometryModel, 'resetFetch');
|
||||
|
||||
this.view._onUpdateFeature('destroy');
|
||||
|
||||
setTimeout(function () {
|
||||
expect(self.view._queryGeometryModel.resetFetch).toHaveBeenCalled();
|
||||
expect(self.rowCollection.resetFetch).toHaveBeenCalled();
|
||||
done();
|
||||
}, 200);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when `cancelPreviousEditions` is triggered from _editorModel', function () {
|
||||
it('should call clean method', function () {
|
||||
spyOn(this.view, 'clean');
|
||||
|
||||
this.view._initBinds();
|
||||
this.view._editorModel.trigger('cancelPreviousEditions');
|
||||
|
||||
expect(this.view.clean).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('reload vis', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(Stylehelper, 'getColorAttribute');
|
||||
spyOn(Stylehelper, 'getSizeAttribute');
|
||||
});
|
||||
|
||||
it('should reload vis if metadata changes and layer is styled but that attribute', function () {
|
||||
Stylehelper.getColorAttribute.and.returnValue('foo');
|
||||
Stylehelper.getSizeAttribute.and.returnValue(undefined);
|
||||
this.view._featureModel.set('foo', 10);
|
||||
|
||||
expect(this.view._needReloadVis()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should reload vis if metadata changes and layer is not styled but that attribute', function () {
|
||||
Stylehelper.getColorAttribute.and.returnValue('bar');
|
||||
Stylehelper.getSizeAttribute.and.returnValue(undefined);
|
||||
this.view._featureModel.set('foo', 10);
|
||||
|
||||
expect(this.view._needReloadVis()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
});
|
||||
+184
@@ -0,0 +1,184 @@
|
||||
var Backbone = require('backbone');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var QuerySchemaModel = require('builder/data/query-schema-model');
|
||||
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
|
||||
var EditFeatureActionView = require('builder/editor/layers/edit-feature-content-views/edit-feature-action-view');
|
||||
var EditFeatureGeometryFormModel = require('builder/editor/layers/edit-feature-content-views/edit-feature-geometry-form-model');
|
||||
var EditFeatureAttributesFormModel = require('builder/editor/layers/edit-feature-content-views/edit-feature-attributes-form-model');
|
||||
var QueryColumnsCollection = require('builder/data/query-columns-collection');
|
||||
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
|
||||
|
||||
describe('editor/layers/edit-feature-content-views/edit-feature-action-view', function () {
|
||||
beforeEach(function () {
|
||||
this.configModel = new ConfigModel({
|
||||
base_url: '/u/pepe',
|
||||
user_name: 'pepe'
|
||||
});
|
||||
this.userModel = new UserModel({}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
this.querySchemaModel = new QuerySchemaModel({}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
var analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
|
||||
configModel: this.configModel,
|
||||
userModel: this.userModel
|
||||
});
|
||||
|
||||
var layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
|
||||
configModel: this.configModel,
|
||||
userModel: this.userModel,
|
||||
analysisDefinitionNodesCollection: analysisDefinitionNodesCollection,
|
||||
mapId: 'm-123',
|
||||
stateDefinitionModel: {}
|
||||
});
|
||||
layerDefinitionsCollection.add({
|
||||
id: 'l-1',
|
||||
kind: 'carto',
|
||||
options: {
|
||||
table_name: 'foo'
|
||||
}
|
||||
});
|
||||
var layerDefinitionModel = layerDefinitionsCollection.at(0);
|
||||
|
||||
this.columnsCollection = new QueryColumnsCollection([
|
||||
{
|
||||
name: 'cartodb_id',
|
||||
type: 'number'
|
||||
}, {
|
||||
name: 'name',
|
||||
type: 'string'
|
||||
}, {
|
||||
name: 'description',
|
||||
type: 'string'
|
||||
}, {
|
||||
name: 'the_geom',
|
||||
type: 'geometry'
|
||||
}
|
||||
], {
|
||||
querySchemaModel: this.querySchemaModel,
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
this.featureModel = new Backbone.Model({
|
||||
the_geom: '{"type":"LineString","coordinates":[[0,0],[0,1]]}',
|
||||
name: '',
|
||||
description: ''
|
||||
});
|
||||
|
||||
this.geometryFormModel = new EditFeatureGeometryFormModel(this.featureModel.toJSON(), {
|
||||
featureModel: this.featureModel
|
||||
});
|
||||
|
||||
this.attributesFormModel = new EditFeatureAttributesFormModel(this.featureModel.toJSON(), {
|
||||
featureModel: this.featureModel,
|
||||
columnsCollection: this.columnsCollection,
|
||||
configModel: this.configModel,
|
||||
nodeDefModel: layerDefinitionModel.getAnalysisDefinitionNodeModel()
|
||||
});
|
||||
|
||||
this.model = new Backbone.Model({
|
||||
hasChanges: false,
|
||||
isValidAttributes: true,
|
||||
isValidGeometry: true
|
||||
});
|
||||
|
||||
this.view = new EditFeatureActionView({
|
||||
model: this.model,
|
||||
featureModel: this.featureModel,
|
||||
geometryFormModel: this.geometryFormModel,
|
||||
attributesFormModel: this.attributesFormModel
|
||||
});
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should render properly', function () {
|
||||
expect(this.view.$('.js-save').length).toBe(1);
|
||||
});
|
||||
|
||||
it('button should be disabled by default', function () {
|
||||
expect(this.view.$el.html()).toContain('is-disabled');
|
||||
});
|
||||
|
||||
it('should update model if feature changes', function () {
|
||||
expect(this.model.get('hasChanges')).toBe(false);
|
||||
this.featureModel.set('the_geom', '{"type":"LineString","coordinates":[[0,0],[1,1]]}');
|
||||
expect(this.model.get('hasChanges')).toBe(true);
|
||||
});
|
||||
|
||||
describe('when is new', function () {
|
||||
beforeEach(function () {
|
||||
this.featureModel.isNew = function () { return true; };
|
||||
});
|
||||
|
||||
it('button should be add', function () {
|
||||
expect(this.view.$el.html()).toContain('editor.edit-feature.add');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when feature already exists', function () {
|
||||
beforeEach(function () {
|
||||
this.featureModel.isNew = function () { return false; };
|
||||
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('button should be save', function () {
|
||||
expect(this.view.$el.html()).toContain('editor.edit-feature.save');
|
||||
});
|
||||
});
|
||||
|
||||
describe('save feature', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(this.featureModel, 'save');
|
||||
});
|
||||
|
||||
describe('when has no changes', function () {
|
||||
beforeEach(function () {
|
||||
this.view.$('.js-save').click();
|
||||
});
|
||||
|
||||
it('button should be disabled', function () {
|
||||
expect(this.view.$el.html()).toContain('is-disabled');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when has changes', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(this.featureModel, 'trigger');
|
||||
this.model.set('hasChanges', true);
|
||||
|
||||
this.view.$('.js-save').click();
|
||||
});
|
||||
|
||||
it('should save the feature model', function () {
|
||||
expect(this.featureModel.save).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('when save succeeds', function () {
|
||||
it('should update changes', function () {
|
||||
this.featureModel.save.calls.argsFor(0)[0].success();
|
||||
|
||||
expect(this.model.get('hasChanges')).toBe(false);
|
||||
expect(this.featureModel.trigger).toHaveBeenCalledWith('saveFeatureSuccess', 'add', this.featureModel);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when save fails', function () {
|
||||
it('should not have changes', function () {
|
||||
this.featureModel.save.calls.argsFor(0)[0].error();
|
||||
|
||||
expect(this.model.get('hasChanges')).toBe(true);
|
||||
expect(this.featureModel.trigger).toHaveBeenCalledWith('saveFeatureFailed');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
});
|
||||
+278
@@ -0,0 +1,278 @@
|
||||
var Backbone = require('backbone');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var QuerySchemaModel = require('builder/data/query-schema-model');
|
||||
var QueryColumnsCollection = require('builder/data/query-columns-collection');
|
||||
var EditFeatureAttributesFormModel = require('builder/editor/layers/edit-feature-content-views/edit-feature-attributes-form-model');
|
||||
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
|
||||
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var DialogConstants = require('builder/components/form-components/_constants/_dialogs');
|
||||
var CDB = require('internal-carto.js');
|
||||
|
||||
describe('editor/layers/edit-feature-content-views/edit-feature-attributes-form-model', function () {
|
||||
beforeEach(function () {
|
||||
this.configModel = new ConfigModel({
|
||||
base_url: '/u/pepe',
|
||||
user_name: 'pepe'
|
||||
});
|
||||
this.userModel = new UserModel({}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
this.querySchemaModel = new QuerySchemaModel({}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
var analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
|
||||
configModel: this.configModel,
|
||||
userModel: this.userModel
|
||||
});
|
||||
|
||||
var layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
|
||||
configModel: this.configModel,
|
||||
userModel: this.userModel,
|
||||
analysisDefinitionNodesCollection: analysisDefinitionNodesCollection,
|
||||
mapId: 'm-123',
|
||||
stateDefinitionModel: {}
|
||||
});
|
||||
layerDefinitionsCollection.add({
|
||||
id: 'l-1',
|
||||
kind: 'carto',
|
||||
options: {
|
||||
table_name: 'foo'
|
||||
}
|
||||
});
|
||||
this.layerDefinitionModel = layerDefinitionsCollection.at(0);
|
||||
|
||||
this.columnsCollection = new QueryColumnsCollection([
|
||||
{
|
||||
name: 'cartodb_id',
|
||||
type: 'number'
|
||||
}, {
|
||||
name: 'string',
|
||||
type: 'string'
|
||||
}, {
|
||||
name: 'boolean',
|
||||
type: 'boolean'
|
||||
}, {
|
||||
name: 'number',
|
||||
type: 'number'
|
||||
}, {
|
||||
name: 'date',
|
||||
type: 'date'
|
||||
}, {
|
||||
name: 'created_at',
|
||||
type: 'date'
|
||||
}, {
|
||||
name: 'the_geom',
|
||||
type: 'geometry'
|
||||
}, {
|
||||
name: 'the_geom_webmercator',
|
||||
type: 'geometry'
|
||||
}, {
|
||||
name: 'updated_at',
|
||||
type: 'date'
|
||||
}, {
|
||||
name: 'coordonnees',
|
||||
type: 'number[]'
|
||||
}
|
||||
], {
|
||||
querySchemaModel: this.querySchemaModel,
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
this.featureModel = new Backbone.Model({
|
||||
cartodb_id: 1,
|
||||
string: 'string',
|
||||
boolean: false,
|
||||
number: 1,
|
||||
date: '2016-10-21T11:27:39+00:00',
|
||||
created_at: '2016-10-21T11:27:39+00:00',
|
||||
the_geom: '{"type":"Point","coordinates":[0,0]}',
|
||||
the_geom_webmercator: '{"type":"Point","coordinates":[0,0]}',
|
||||
updated_at: '2016-10-21T11:27:39+00:00',
|
||||
coordonnees: '48.1100259201,-1.6780371631'
|
||||
});
|
||||
|
||||
this.formModel = new EditFeatureAttributesFormModel(this.featureModel.toJSON(), {
|
||||
featureModel: this.featureModel,
|
||||
columnsCollection: this.columnsCollection,
|
||||
configModel: this.configModel,
|
||||
nodeDefModel: this.layerDefinitionModel.getAnalysisDefinitionNodeModel()
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate schema from columns collection, omit reserved columns, and assign default Text type', function () {
|
||||
expect(this.formModel.schema).toEqual({
|
||||
'cartodb_id': {
|
||||
'type': 'Number',
|
||||
'isFormatted': false,
|
||||
'showSlider': false,
|
||||
'editorAttrs': {
|
||||
'disabled': true
|
||||
}
|
||||
},
|
||||
'string': {
|
||||
'type': 'Text'
|
||||
},
|
||||
'boolean': {
|
||||
'type': 'Radio',
|
||||
'options': [
|
||||
{
|
||||
'label': 'form-components.editors.radio.true',
|
||||
'val': true
|
||||
},
|
||||
{
|
||||
'label': 'form-components.editors.radio.false',
|
||||
'val': false
|
||||
},
|
||||
{
|
||||
'label': 'form-components.editors.radio.null',
|
||||
'val': null
|
||||
}
|
||||
]
|
||||
},
|
||||
'number': {
|
||||
'type': 'Number',
|
||||
'isFormatted': true,
|
||||
'showSlider': false
|
||||
},
|
||||
'date': {
|
||||
'type': 'DateTime',
|
||||
'dialogMode': DialogConstants.Mode.FLOAT
|
||||
},
|
||||
'coordonnees': {
|
||||
'type': 'Text'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there is a column of type string', function () {
|
||||
beforeEach(function () {
|
||||
this.columnsCollection = new QueryColumnsCollection([
|
||||
{
|
||||
name: 'cartodb_id',
|
||||
type: 'number'
|
||||
}, {
|
||||
name: 'names',
|
||||
type: 'string'
|
||||
}
|
||||
], {
|
||||
querySchemaModel: this.querySchemaModel,
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
this.featureModel = new Backbone.Model({
|
||||
cartodb_id: 1,
|
||||
names: 'carlos'
|
||||
});
|
||||
});
|
||||
|
||||
describe('when all values are null', function () {
|
||||
var formModel;
|
||||
|
||||
beforeEach(function () {
|
||||
spyOn(CDB.SQL.prototype, 'execute').and.callFake(function (query, vars, params) {
|
||||
params && params.success({
|
||||
rows: [{
|
||||
names: null
|
||||
}, {
|
||||
names: null
|
||||
}, {
|
||||
names: null
|
||||
}],
|
||||
fields: []
|
||||
});
|
||||
});
|
||||
|
||||
spyOn(EditFeatureAttributesFormModel.prototype, '_onChangeSchema');
|
||||
|
||||
formModel = new EditFeatureAttributesFormModel(this.featureModel.toJSON(), {
|
||||
featureModel: this.featureModel,
|
||||
columnsCollection: this.columnsCollection,
|
||||
configModel: this.configModel,
|
||||
nodeDefModel: this.layerDefinitionModel.getAnalysisDefinitionNodeModel()
|
||||
});
|
||||
});
|
||||
|
||||
it('should', function () {
|
||||
expect(EditFeatureAttributesFormModel.prototype._onChangeSchema).not.toHaveBeenCalled();
|
||||
|
||||
expect(formModel.schema).toEqual({
|
||||
'cartodb_id': {
|
||||
'type': 'Number',
|
||||
'isFormatted': false,
|
||||
'showSlider': false,
|
||||
'editorAttrs': {
|
||||
'disabled': true
|
||||
}
|
||||
},
|
||||
'names': {
|
||||
'type': 'Text'
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when not all values are null', function () {
|
||||
var formModel;
|
||||
|
||||
beforeEach(function () {
|
||||
spyOn(CDB.SQL.prototype, 'execute').and.callFake(function (query, vars, params) {
|
||||
params && params.success({
|
||||
rows: [{
|
||||
names: 'pepe'
|
||||
}, {
|
||||
names: 'paco'
|
||||
}, {
|
||||
names: 'juan'
|
||||
}],
|
||||
fields: []
|
||||
});
|
||||
});
|
||||
|
||||
spyOn(EditFeatureAttributesFormModel.prototype, '_onChangeSchema');
|
||||
|
||||
formModel = new EditFeatureAttributesFormModel(this.featureModel.toJSON(), {
|
||||
featureModel: this.featureModel,
|
||||
columnsCollection: this.columnsCollection,
|
||||
configModel: this.configModel,
|
||||
nodeDefModel: this.layerDefinitionModel.getAnalysisDefinitionNodeModel()
|
||||
});
|
||||
});
|
||||
|
||||
it('should', function () {
|
||||
expect(EditFeatureAttributesFormModel.prototype._onChangeSchema).toHaveBeenCalled();
|
||||
|
||||
expect(formModel.schema).toEqual({
|
||||
cartodb_id: {
|
||||
type: 'Number',
|
||||
isFormatted: false,
|
||||
showSlider: false,
|
||||
editorAttrs: {
|
||||
disabled: true
|
||||
}
|
||||
},
|
||||
names: {
|
||||
type: 'Suggest',
|
||||
dialogMode: DialogConstants.Mode.FLOAT,
|
||||
editorAttrs: {
|
||||
showSearch: true,
|
||||
defaultValue: true,
|
||||
allowFreeTextInput: true,
|
||||
collectionData: [ 'pepe', 'paco', 'juan' ]
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when form data changes', function () {
|
||||
it('should update feature model', function () {
|
||||
expect(this.featureModel.get('boolean')).toBe(false);
|
||||
this.formModel.set('boolean', true);
|
||||
expect(this.featureModel.get('boolean')).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
var Backbone = require('backbone');
|
||||
var EditFeatureAttributesFormView = require('builder/editor/layers/edit-feature-content-views/edit-feature-attributes-form-view');
|
||||
|
||||
describe('editor/layers/edit-feature-content-views/edit-feature-attributes-form-view', function () {
|
||||
beforeEach(function () {
|
||||
this.model = new Backbone.Model({
|
||||
string: 'string',
|
||||
boolean: false,
|
||||
number: 1,
|
||||
date: '2016-10-21T11:27:39+00:00',
|
||||
the_geom: '{"type":"Point","coordinates":[0,0]}'
|
||||
});
|
||||
|
||||
this.view = new EditFeatureAttributesFormView({
|
||||
model: this.model
|
||||
});
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should render with data from form model', function () {
|
||||
expect(this.view.$('form').length).toEqual(1);
|
||||
});
|
||||
|
||||
describe('when schema changes', function () {
|
||||
beforeEach(function () {
|
||||
this.prev$form = this.view.$('form');
|
||||
this.view.model.trigger('changeSchema');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
this.prev$form = null;
|
||||
});
|
||||
|
||||
it('should re-render the form', function () {
|
||||
expect(this.view.$('form').length).toEqual(1);
|
||||
expect(this.view.$('form')).not.toBe(this.prev$form);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when form is cleaned', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(Backbone.Form.prototype, 'remove').and.callThrough();
|
||||
this.view.clean();
|
||||
});
|
||||
|
||||
it('should remove form when view is cleaned', function () {
|
||||
expect(Backbone.Form.prototype.remove).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not have any leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
});
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
var EditFeatureControlView = require('builder/editor/layers/edit-feature-content-views/edit-feature-control-view');
|
||||
|
||||
describe('editor/layers/edit-feature-content-views/edit-feature-control-view', function () {
|
||||
beforeEach(function () {
|
||||
this.view = new EditFeatureControlView();
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should render properly', function () {
|
||||
expect(this.view.$('.js-back').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
});
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
var Backbone = require('backbone');
|
||||
require('backbone-forms');
|
||||
|
||||
var EditFeatureGeometryFormModel = require('builder/editor/layers/edit-feature-content-views/edit-feature-geometry-form-model');
|
||||
var EditFeatureGeometryPointFormModel = require('builder/editor/layers/edit-feature-content-views/edit-feature-geometry-point-form-model');
|
||||
|
||||
describe('editor/layers/edit-feature-content-views/edit-feature-geometry-form-model', function () {
|
||||
beforeEach(function () {
|
||||
this.featureModel = new Backbone.Model({
|
||||
the_geom: '{"type":"Point","coordinates":[0,0]}',
|
||||
name: '',
|
||||
description: ''
|
||||
});
|
||||
});
|
||||
|
||||
describe('type is point', function () {
|
||||
beforeEach(function () {
|
||||
this.formModel = new EditFeatureGeometryPointFormModel({
|
||||
lat: 0,
|
||||
lng: 0
|
||||
}, {
|
||||
featureModel: this.featureModel
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate lat lng schema from geometry', function () {
|
||||
expect(Object.keys(this.formModel.schema).length).toEqual(2);
|
||||
expect(this.formModel.schema.lng.type).toEqual('Number');
|
||||
expect(this.formModel.schema.lat.type).toEqual('Number');
|
||||
});
|
||||
|
||||
describe('latitude is erroneous', function () {
|
||||
it('should return regular error if lat is not a number', function () {
|
||||
this.formModel.set({ lat: 'Look at me!', lng: 100 });
|
||||
var form = new Backbone.Form({ model: this.formModel });
|
||||
var validation = form.getEditor('lat').validate();
|
||||
expect(validation.message).toBe('editor.edit-feature.valid-lat');
|
||||
});
|
||||
|
||||
it('should return custom error if lat is out of bounds', function () {
|
||||
this.formModel.set({ lng: 10, lat: -120 });
|
||||
var form = new Backbone.Form({ model: this.formModel });
|
||||
|
||||
var validation = form.getEditor('lat').validate();
|
||||
expect(validation.message).toBe('editor.edit-feature.out-of-bounds-lat');
|
||||
});
|
||||
});
|
||||
|
||||
describe('longitude is erroneous', function () {
|
||||
it('should return regular error if lng is not a number', function () {
|
||||
this.formModel.set({ lng: 'Look at me!', lat: 10 });
|
||||
var form = new Backbone.Form({ model: this.formModel });
|
||||
var validation = form.getEditor('lng').validate();
|
||||
expect(validation.message).toBe('editor.edit-feature.valid-lng');
|
||||
});
|
||||
|
||||
it('should return custom error if lng is out of bounds', function () {
|
||||
this.formModel.set({ lng: -5000, lat: 10 });
|
||||
var form = new Backbone.Form({ model: this.formModel });
|
||||
|
||||
var validation = form.getEditor('lng').validate();
|
||||
expect(validation.message).toBe('editor.edit-feature.out-of-bounds-lng');
|
||||
});
|
||||
});
|
||||
|
||||
describe('form data changes', function () {
|
||||
it('should update feature model', function () {
|
||||
expect(this.featureModel.get('the_geom')).toBe('{"type":"Point","coordinates":[0,0]}');
|
||||
|
||||
this.formModel.set('lat', 3.141592653);
|
||||
|
||||
expect(this.featureModel.get('the_geom')).toBe('{"type":"Point","coordinates":[0,3.14159265]}');
|
||||
|
||||
this.formModel.set({
|
||||
'lat': -90,
|
||||
'lng': 180
|
||||
});
|
||||
|
||||
expect(this.featureModel.get('the_geom')).toBe('{"type":"Point","coordinates":[180,-90]}');
|
||||
|
||||
this.formModel.set({
|
||||
'lat': 0,
|
||||
'lng': 0
|
||||
});
|
||||
|
||||
expect(this.featureModel.get('the_geom')).toBe('{"type":"Point","coordinates":[0,0]}');
|
||||
});
|
||||
|
||||
it('should only update the_geom once when lat and lng change so that changes can be detected', function () {
|
||||
expect(this.featureModel.get('the_geom')).toBe('{"type":"Point","coordinates":[0,0]}');
|
||||
|
||||
this.formModel.set({
|
||||
'lat': -89.0123456789,
|
||||
'lng': 179.0123456789
|
||||
});
|
||||
|
||||
expect(this.featureModel.get('the_geom')).toBe('{"type":"Point","coordinates":[179.01234568,-89.01234568]}');
|
||||
expect(this.featureModel.hasChanged('the_geom')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should set the_geom\'s feature without setting a previous one', function () {
|
||||
this.featureModel.unset('the_geom');
|
||||
this.formModel.set({ lat: 10, lng: 20 });
|
||||
expect(this.featureModel.get('the_geom')).toEqual('{"type":"Point","coordinates":[20,10]}');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('type is not point', function () {
|
||||
var formModel;
|
||||
var featureModel;
|
||||
var coords = [39.46975, -0.37739];
|
||||
|
||||
beforeEach(function () {
|
||||
featureModel = new Backbone.Model({
|
||||
the_geom: '{"type":"LineString","coordinates":[[0,0],[0,1]]}',
|
||||
name: '',
|
||||
description: ''
|
||||
});
|
||||
|
||||
formModel = new EditFeatureGeometryFormModel(featureModel.toJSON(), {
|
||||
featureModel: featureModel
|
||||
});
|
||||
});
|
||||
|
||||
describe('schema', function () {
|
||||
it('should generate schema from geometry', function () {
|
||||
expect(formModel.schema).toEqual({
|
||||
the_geom: {
|
||||
type: 'Text',
|
||||
validators: [ 'required', jasmine.any(Function) ],
|
||||
editorAttrs: { disabled: true },
|
||||
hasCopyButton: true
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should NOT include copy attr if feature does NOT have the_geom', function () {
|
||||
featureModel.unset('the_geom');
|
||||
|
||||
formModel = new EditFeatureGeometryFormModel(featureModel.toJSON(), {
|
||||
featureModel: featureModel
|
||||
});
|
||||
|
||||
expect(formModel.schema.the_geom.hasCopyButton).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._formatCoordinate', function () {
|
||||
it('should format properly the coordinate', function () {
|
||||
expect(formModel._formatCoordinate(0)).toEqual(0);
|
||||
expect(formModel._formatCoordinate(coords[0])).toEqual(39.46975);
|
||||
expect(formModel._formatCoordinate(coords[1])).toEqual(-0.37739);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._isValidCoordinate', function () {
|
||||
it('should validate coordinate', function () {
|
||||
expect(formModel._isValidCoordinate(null)).toBe(false);
|
||||
expect(formModel._isValidCoordinate(0)).toBe(true);
|
||||
expect(formModel._isValidCoordinate(coords[0])).toBe(true);
|
||||
expect(formModel._isValidCoordinate(coords[1])).toBe(true);
|
||||
expect(formModel._isValidCoordinate(coords[1].toString())).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._getLatitude', function () {
|
||||
it('should return latitude', function () {
|
||||
expect(formModel._getLatitude(coords)).toBe(39.46975);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._getLongitude', function () {
|
||||
it('should return longitude', function () {
|
||||
expect(formModel._getLongitude(coords)).toBe(-0.37739);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._validateGeom', function () {
|
||||
var the_geom_invalid_lng, the_geom_invalid_lat, the_geom_invalid_latlng, the_geom_valid;
|
||||
var invalid_lng_message = '{"message":"editor.edit-feature.out-of-bounds-lng"}';
|
||||
var invalid_lat_message = '{"message":"editor.edit-feature.out-of-bounds-lat"}';
|
||||
var invalid_latlng_message = '{"message":"editor.edit-feature.out-of-bounds-lat editor.edit-feature.out-of-bounds-lng"}';
|
||||
|
||||
describe('type is line', function () {
|
||||
it('should validate the_geom', function () {
|
||||
the_geom_invalid_lng = '{"type":"LineString","coordinates":[[-200,0],[-190,0]]}';
|
||||
the_geom_invalid_lat = '{"type":"LineString","coordinates":[[0,-110],[0,-100]]}';
|
||||
the_geom_invalid_latlng = '{"type":"LineString","coordinates":[[-200,-110],[-190,-100]]}';
|
||||
the_geom_valid = '{"type":"LineString","coordinates":[[0,0],[1,1]]}';
|
||||
|
||||
formModel._featureModel.isLine = function () { return true; };
|
||||
|
||||
expect(JSON.stringify(formModel._validateGeom(the_geom_invalid_lng))).toBe(invalid_lng_message);
|
||||
expect(JSON.stringify(formModel._validateGeom(the_geom_invalid_lat))).toBe(invalid_lat_message);
|
||||
expect(JSON.stringify(formModel._validateGeom(the_geom_invalid_latlng))).toBe(invalid_latlng_message);
|
||||
expect(formModel._validateGeom(the_geom_valid)).toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe('type is not line', function () {
|
||||
it('should validate the_geom', function () {
|
||||
the_geom_invalid_lng = '{"type":"Polygon","coordinates":[[[-200,0],[-190,0],[-190,10],[-200,0]]]}';
|
||||
the_geom_invalid_lat = '{"type":"Polygon","coordinates":[[[0,-110],[0,-100],[10,-100],[0,-110]]]}';
|
||||
the_geom_invalid_latlng = '{"type":"Polygon","coordinates":[[[-200,-110],[-190,-100],[-190,-100],[-190,-110]]]}';
|
||||
the_geom_valid = '{"type":"Polygon","coordinates":[[[0,0],[2,2],[1,1],[0,0]]]}';
|
||||
|
||||
formModel._featureModel.isLine = function () { return false; };
|
||||
|
||||
expect(JSON.stringify(formModel._validateGeom(the_geom_invalid_lng))).toBe(invalid_lng_message);
|
||||
expect(JSON.stringify(formModel._validateGeom(the_geom_invalid_lat))).toBe(invalid_lat_message);
|
||||
expect(JSON.stringify(formModel._validateGeom(the_geom_invalid_latlng))).toBe(invalid_latlng_message);
|
||||
expect(formModel._validateGeom(the_geom_valid)).toBe(undefined);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
var Backbone = require('backbone');
|
||||
var EditFeatureGeometryFormView = require('builder/editor/layers/edit-feature-content-views/edit-feature-geometry-form-view');
|
||||
|
||||
describe('editor/layers/edit-feature-content-views/edit-feature-geometry-form-view', function () {
|
||||
beforeEach(function () {
|
||||
this.featureModel = new Backbone.Model({
|
||||
the_geom: '{"type":"LineString","coordinates":[[0,0],[0,1]]}',
|
||||
name: '',
|
||||
description: ''
|
||||
});
|
||||
this.featureModel.getFeatureType = function () { return 'line'; };
|
||||
this.featureModel.isPoint = function () { return false; };
|
||||
|
||||
this.view = new EditFeatureGeometryFormView({
|
||||
model: this.featureModel
|
||||
});
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should render with data from form model', function () {
|
||||
expect(this.view.$('form').length).toEqual(1);
|
||||
});
|
||||
|
||||
describe('when schema changes', function () {
|
||||
beforeEach(function () {
|
||||
this.prev$form = this.view.$('form');
|
||||
this.view.model.trigger('changeSchema');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
this.prev$form = null;
|
||||
});
|
||||
|
||||
it('should re-render the form', function () {
|
||||
expect(this.view.$('form').length).toEqual(1);
|
||||
expect(this.view.$('form')).not.toBe(this.prev$form);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when form is cleaned', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(Backbone.Form.prototype, 'remove').and.callThrough();
|
||||
this.view.clean();
|
||||
});
|
||||
|
||||
it('should remove form when view is cleaned', function () {
|
||||
expect(Backbone.Form.prototype.remove).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not have any leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
});
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
var Backbone = require('backbone');
|
||||
var EditFeatureHeaderView = require('builder/editor/layers/edit-feature-content-views/edit-feature-header-view');
|
||||
var FactoryModals = require('../../../factories/modals');
|
||||
|
||||
describe('editor/layers/edit-feature-content-views/edit-feature-header-view', function () {
|
||||
var letter = 'CustomLetter';
|
||||
var layerName = 'LayerName';
|
||||
var tableName = 'table_name';
|
||||
|
||||
beforeEach(function () {
|
||||
this.model = {
|
||||
getFeatureType: function () { return 'line'; }
|
||||
};
|
||||
|
||||
var layerDefinitionModel = new Backbone.Model({
|
||||
letter: letter
|
||||
});
|
||||
layerDefinitionModel.getColor = function () {
|
||||
return '#fabada';
|
||||
};
|
||||
layerDefinitionModel.getName = function () {
|
||||
return layerName;
|
||||
};
|
||||
layerDefinitionModel.tableName = function () {
|
||||
return tableName;
|
||||
};
|
||||
|
||||
this.view = new EditFeatureHeaderView({
|
||||
url: 'http://',
|
||||
tableName: tableName,
|
||||
model: this.model,
|
||||
modals: FactoryModals.createModalService(),
|
||||
layerDefinitionModel: layerDefinitionModel,
|
||||
isNew: false,
|
||||
backAction: function () {}
|
||||
});
|
||||
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
describe('.render', function () {
|
||||
it('should render properly', function () {
|
||||
expect(this.view.$el.text()).toContain('back');
|
||||
expect(this.view.$el.text()).toContain('editor.edit-feature.edit');
|
||||
expect(this.view.$el.html()).toContain(layerName);
|
||||
expect(this.view.$el.html()).toContain(letter);
|
||||
expect(this.view.$el.html()).toContain('<a href="http://" target="_blank" title="' + tableName + '" class="Editor-headerLayerName">' + tableName + '</a>');
|
||||
expect(this.view.$el.html()).toContain('js-context-menu');
|
||||
});
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
});
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var EditFeatureInnerView = require('builder/editor/layers/edit-feature-content-views/edit-feature-inner-view');
|
||||
var EditFeatureGeometryFormModel = require('builder/editor/layers/edit-feature-content-views/edit-feature-geometry-form-model');
|
||||
var EditFeatureAttributesFormModel = require('builder/editor/layers/edit-feature-content-views/edit-feature-attributes-form-model');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var QuerySchemaModel = require('builder/data/query-schema-model');
|
||||
var QueryColumnsCollection = require('builder/data/query-columns-collection');
|
||||
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
|
||||
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
|
||||
|
||||
describe('editor/layers/edit-feature-content-views/edit-feature-inner-view', function () {
|
||||
beforeEach(function () {
|
||||
this.configModel = new ConfigModel({
|
||||
base_url: '/u/pepe',
|
||||
user_name: 'pepe'
|
||||
});
|
||||
this.userModel = new UserModel({}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
var analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
|
||||
configModel: this.configModel,
|
||||
userModel: this.userModel
|
||||
});
|
||||
|
||||
var layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
|
||||
configModel: this.configModel,
|
||||
userModel: this.userModel,
|
||||
analysisDefinitionNodesCollection: analysisDefinitionNodesCollection,
|
||||
mapId: 'm-123',
|
||||
stateDefinitionModel: {}
|
||||
});
|
||||
layerDefinitionsCollection.add({
|
||||
id: 'l-1',
|
||||
kind: 'carto',
|
||||
options: {
|
||||
table_name: 'foo'
|
||||
}
|
||||
});
|
||||
var layerDefinitionModel = layerDefinitionsCollection.at(0);
|
||||
|
||||
this.querySchemaModel = new QuerySchemaModel({}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
this.columnsCollection = new QueryColumnsCollection([
|
||||
{
|
||||
name: 'cartodb_id',
|
||||
type: 'number'
|
||||
}, {
|
||||
name: 'name',
|
||||
type: 'string'
|
||||
}, {
|
||||
name: 'description',
|
||||
type: 'string'
|
||||
}, {
|
||||
name: 'the_geom',
|
||||
type: 'geometry'
|
||||
}
|
||||
], {
|
||||
querySchemaModel: this.querySchemaModel,
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
this.featureModel = new Backbone.Model({
|
||||
the_geom: '{"type":"LineString","coordinates":[[0,0],[0,1]]}',
|
||||
name: '',
|
||||
description: ''
|
||||
});
|
||||
|
||||
this.geometryFormModel = new EditFeatureGeometryFormModel(this.featureModel.toJSON(), {
|
||||
featureModel: this.featureModel
|
||||
});
|
||||
|
||||
this.attributesFormModel = new EditFeatureAttributesFormModel(this.featureModel.toJSON(), {
|
||||
featureModel: this.featureModel,
|
||||
columnsCollection: this.columnsCollection,
|
||||
configModel: this.configModel,
|
||||
nodeDefModel: layerDefinitionModel.getAnalysisDefinitionNodeModel()
|
||||
});
|
||||
this.featureModel.isPoint = function () { return false; };
|
||||
this.featureModel.getFeatureType = function () { return 'line'; };
|
||||
|
||||
this.view = new EditFeatureInnerView({
|
||||
featureModel: this.featureModel,
|
||||
geometryFormModel: this.geometryFormModel,
|
||||
attributesFormModel: this.attributesFormModel
|
||||
});
|
||||
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should render properly', function () {
|
||||
expect(_.size(this.view._subviews)).toBe(2); // geometry, attributes
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,166 @@
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
|
||||
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
|
||||
var LayerAnalysesView = require('builder/editor/layers/layer-analyses-view');
|
||||
var LayerAnalysisViewFactory = require('builder/editor/layers/layer-analysis-view-factory');
|
||||
|
||||
describe('editor/layers/layer-analyses-view', function () {
|
||||
beforeEach(function () {
|
||||
var configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
var userModel = new UserModel({}, {
|
||||
configModel: configModel
|
||||
});
|
||||
|
||||
this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
|
||||
configModel: configModel,
|
||||
userModel: userModel
|
||||
});
|
||||
this.analysisDefinitionNodesCollection.add({
|
||||
id: 'a0',
|
||||
type: 'source',
|
||||
status: 'ready',
|
||||
query: 'SELECT * FROM own_source',
|
||||
table_name: 'own_source'
|
||||
});
|
||||
this.layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
|
||||
configModel: configModel,
|
||||
userModel: userModel,
|
||||
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
|
||||
mapId: 'm-123',
|
||||
stateDefinitionModel: {}
|
||||
});
|
||||
this.layerAnalysisViewFactory = new LayerAnalysisViewFactory(this.analysisDefinitionNodesCollection);
|
||||
});
|
||||
|
||||
describe('given a layer definition with only its own source', function () {
|
||||
beforeEach(function () {
|
||||
this.layerDefinitionsCollection.add({
|
||||
id: 'l1',
|
||||
kind: 'carto',
|
||||
source: 'a0',
|
||||
options: {
|
||||
table_name: 'own_source',
|
||||
cartocss: 'asd'
|
||||
}
|
||||
});
|
||||
|
||||
this.view = new LayerAnalysesView({
|
||||
model: this.layerDefinitionsCollection.get('l1'),
|
||||
sortableSelector: '.js-layers',
|
||||
layerAnalysisViewFactory: this.layerAnalysisViewFactory
|
||||
});
|
||||
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
it('should render the source', function () {
|
||||
expect(this.view.$el.text()).toContain('a0');
|
||||
expect(this.view.$el.text()).toContain('own_source');
|
||||
});
|
||||
});
|
||||
|
||||
describe('given a layer definition with an analysis within its own layer', function () {
|
||||
beforeEach(function () {
|
||||
this.analysisDefinitionNodesCollection.add({
|
||||
id: 'b1',
|
||||
type: 'trade-area',
|
||||
status: 'ready',
|
||||
params: {
|
||||
kind: 'walk',
|
||||
time: 300,
|
||||
source: {
|
||||
id: 'b0',
|
||||
type: 'source',
|
||||
table_name: 'foo',
|
||||
params: {
|
||||
query: 'SELECT * FROM foo'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
this.layerDefinitionsCollection.add({
|
||||
id: 'l1',
|
||||
kind: 'carto',
|
||||
options: {
|
||||
table_name: 'foo',
|
||||
cartocss: 'asd',
|
||||
letter: 'b',
|
||||
source: 'b1',
|
||||
name: 'layerB'
|
||||
}
|
||||
});
|
||||
this.view = new LayerAnalysesView({
|
||||
model: this.layerDefinitionsCollection.get('l1'),
|
||||
sortableSelector: '.js-layers',
|
||||
layerAnalysisViewFactory: this.layerAnalysisViewFactory
|
||||
});
|
||||
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
it('should render the sources', function () {
|
||||
expect(this.view.$el.text()).toContain('b1');
|
||||
expect(this.view.$el.text()).toContain('analyses.area-of-influence');
|
||||
expect(this.view.$el.text()).toContain('b0');
|
||||
expect(this.view.$el.text()).toContain('foo');
|
||||
});
|
||||
|
||||
describe('given a layer definition with an analysis referencing the head of other layer', function () {
|
||||
beforeEach(function () {
|
||||
this.analysisDefinitionNodesCollection.add({
|
||||
id: 'c1',
|
||||
type: 'trade-area',
|
||||
status: 'ready',
|
||||
params: {
|
||||
kind: 'walk',
|
||||
time: 200,
|
||||
source: this.analysisDefinitionNodesCollection.get('b1').toJSON()
|
||||
}
|
||||
});
|
||||
this.layerDefinitionsCollection.add({
|
||||
id: 'l2',
|
||||
kind: 'carto',
|
||||
options: {
|
||||
table_name: 'foo',
|
||||
cartocss: 'asd',
|
||||
letter: 'c',
|
||||
source: 'c1',
|
||||
name: 'layerC'
|
||||
}
|
||||
});
|
||||
this.view = new LayerAnalysesView({
|
||||
model: this.layerDefinitionsCollection.get('l2'),
|
||||
sortableSelector: '.js-layers',
|
||||
layerAnalysisViewFactory: this.layerAnalysisViewFactory
|
||||
});
|
||||
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
it('should render the own source node', function () {
|
||||
expect(this.view.$el.text()).toContain('c1');
|
||||
expect(this.view.$el.text()).toContain('analyses.area-of-influence');
|
||||
});
|
||||
|
||||
it('should render the source of the other layer but not more', function () {
|
||||
expect(this.view.$el.text()).toContain('b1');
|
||||
expect(this.view.$el.text()).not.toContain('b0');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,74 @@
|
||||
var $ = require('jquery');
|
||||
var Backbone = require('backbone');
|
||||
var LayerAnalysisDraggableView = require('builder/editor/layers/layer-analysis-draggable-view');
|
||||
|
||||
describe('editor/layers/layer-analysis-draggable-view', function () {
|
||||
beforeEach(function () {
|
||||
this.$nodeViewElement = $('div');
|
||||
spyOn(this.$nodeViewElement, 'draggable').and.callThrough();
|
||||
|
||||
this.model = new Backbone.Model({
|
||||
id: 'a1',
|
||||
type: 'buffer'
|
||||
});
|
||||
|
||||
this.view = new LayerAnalysisDraggableView({
|
||||
layerDefinitionModel: new Backbone.Model(),
|
||||
model: this.model,
|
||||
getNextLetter: function () { return 'b'; },
|
||||
sortableSelector: '.js-layers',
|
||||
$nodeViewElement: this.$nodeViewElement
|
||||
});
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
this.$nodeViewElement = null;
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
describe('should initialize draggable for given nodeView element', function () {
|
||||
beforeEach(function () {
|
||||
expect(this.$nodeViewElement.draggable).toHaveBeenCalled();
|
||||
expect(this.$nodeViewElement.data('ui-draggable')).toBeDefined();
|
||||
this.draggableArgs = this.$nodeViewElement.draggable.calls.argsFor(0)[0];
|
||||
});
|
||||
|
||||
it('should connect draggable item to layers sortable list', function () {
|
||||
expect(this.draggableArgs.connectToSortable).toEqual('.js-layers');
|
||||
expect(this.draggableArgs.appendTo).toEqual('.js-layers');
|
||||
});
|
||||
|
||||
describe('when dragged', function () {
|
||||
beforeEach(function () {
|
||||
// simulate dragging
|
||||
this.html = this.draggableArgs.helper();
|
||||
});
|
||||
|
||||
it('should create a helper HTML, previewing a new layer', function () {
|
||||
expect(this.html).toEqual(jasmine.any(String));
|
||||
expect(this.html).toContain('Layer');
|
||||
expect(this.html).toContain('>b<', 'should have the next-letter representation');
|
||||
expect(this.html).toContain('"a1"', 'should have the node-id the new layer will be based on');
|
||||
expect(this.html).toContain('area-of-influence'); // title of a buffer type
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('.clean', function () {
|
||||
beforeEach(function () {
|
||||
this.view.clean();
|
||||
});
|
||||
|
||||
it('should remove draggable behavior from element', function () {
|
||||
expect(this.$nodeViewElement.data('ui-draggable')).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should remove reference to DOM element', function () {
|
||||
expect(this.view.options.$nodeViewElement).toBe(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,827 @@
|
||||
var _ = require('underscore');
|
||||
var $ = require('jquery');
|
||||
var Backbone = require('backbone');
|
||||
var AnalysisDefinitionNodeSourceModel = require('builder/data/analysis-definition-node-source-model');
|
||||
var AnalysisFormsCollection = require('builder/editor/layers/layer-content-views/analyses/analysis-forms-collection');
|
||||
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var LayerContentView = require('builder/editor/layers/layer-content-view');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var EditorModel = require('builder/data/editor-model');
|
||||
var UserNotifications = require('builder/data/user-notifications');
|
||||
var AnalysesService = require('builder/editor/layers/layer-content-views/analyses/analyses-service');
|
||||
var FactoryModals = require('../../factories/modals');
|
||||
var Router = require('builder/routes/router');
|
||||
|
||||
describe('editor/layers/layer-content-view/layer-content-view', function () {
|
||||
var view;
|
||||
var analysisDefinitionNodeModel;
|
||||
var editorModel;
|
||||
var layerDefinitionModel;
|
||||
var dashboardCanvasEl;
|
||||
var handleRouteSpy;
|
||||
var analysesTabName = 'analyses';
|
||||
var dataTabName = 'data';
|
||||
|
||||
var createViewFn = function (options) {
|
||||
dashboardCanvasEl = document.createElement('div');
|
||||
dashboardCanvasEl.className = 'CDB-Dashboard-canvas';
|
||||
document.body.appendChild(dashboardCanvasEl);
|
||||
|
||||
var canvas = document.createElement('div');
|
||||
canvas.classList.add('CDB-Map-canvas');
|
||||
dashboardCanvasEl.appendChild(canvas);
|
||||
|
||||
var configModel = new ConfigModel({
|
||||
base_url: '/u/pepe',
|
||||
user_name: 'pepe',
|
||||
sql_api_protocol: 'http'
|
||||
});
|
||||
|
||||
var userModel = new UserModel({}, {
|
||||
configModel: configModel,
|
||||
username: 'pepe'
|
||||
});
|
||||
|
||||
var mapDefModel = new Backbone.Model({
|
||||
legends: true
|
||||
});
|
||||
|
||||
layerDefinitionModel = new LayerDefinitionModel({
|
||||
id: 'l-1',
|
||||
kind: 'carto',
|
||||
options: {
|
||||
source: 'a0',
|
||||
letter: 'a',
|
||||
table_name: 'foo'
|
||||
}
|
||||
}, {
|
||||
parse: true,
|
||||
configModel: configModel
|
||||
});
|
||||
|
||||
var onboardings = new Backbone.Model();
|
||||
onboardings.create = function () {};
|
||||
onboardings.destroy = function () {};
|
||||
|
||||
analysisDefinitionNodeModel = new AnalysisDefinitionNodeSourceModel({
|
||||
id: 'a1',
|
||||
type: 'source',
|
||||
table_name: 'foo'
|
||||
}, {
|
||||
configModel: configModel,
|
||||
userModel: userModel,
|
||||
collection: new Backbone.Collection()
|
||||
});
|
||||
|
||||
analysisDefinitionNodeModel.querySchemaModel.attributes.query = 'SELECT * FROM foo';
|
||||
|
||||
var layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
|
||||
configModel: configModel,
|
||||
userModel: userModel,
|
||||
analysisDefinitionNodesCollection: new Backbone.Collection(),
|
||||
mapId: 'map-123',
|
||||
stateDefinitionModel: {}
|
||||
});
|
||||
|
||||
layerDefinitionsCollection.add(layerDefinitionModel);
|
||||
|
||||
var widgetDefinitionsCollection = new Backbone.Collection();
|
||||
|
||||
widgetDefinitionsCollection.isThereTimeSeries = function () {
|
||||
return false;
|
||||
};
|
||||
|
||||
widgetDefinitionsCollection.isThereOtherWidgets = function () {
|
||||
return false;
|
||||
};
|
||||
|
||||
spyOn(layerDefinitionModel, 'getAnalysisDefinitionNodeModel').and.returnValue(analysisDefinitionNodeModel);
|
||||
spyOn(layerDefinitionModel, 'findAnalysisDefinitionNodeModel').and.returnValue(analysisDefinitionNodeModel);
|
||||
spyOn(Router, 'goToLayerList');
|
||||
spyOn(Router, 'navigate');
|
||||
|
||||
handleRouteSpy = spyOn(LayerContentView.prototype, '_handleRoute');
|
||||
|
||||
var onboardingNotification = new UserNotifications({}, {
|
||||
key: 'builder',
|
||||
configModel: configModel
|
||||
});
|
||||
|
||||
var visDefinitionModel = new Backbone.Model();
|
||||
var stackLayoutModel = jasmine.createSpyObj('stackLayoutModel', ['prevStep', 'goToStep']);
|
||||
|
||||
AnalysesService.init({
|
||||
onboardings: onboardings,
|
||||
layerDefinitionsCollection: layerDefinitionsCollection,
|
||||
modals: FactoryModals.createModalService(),
|
||||
userModel: userModel,
|
||||
configModel: configModel
|
||||
});
|
||||
|
||||
editorModel = new EditorModel({ edition: false });
|
||||
|
||||
var viewOptions = {
|
||||
mapDefinitionModel: mapDefModel,
|
||||
layerDefinitionModel: layerDefinitionModel,
|
||||
analysisDefinitionsCollection: new Backbone.Collection(),
|
||||
analysisDefinitionNodesCollection: new Backbone.Collection(),
|
||||
layerDefinitionsCollection: layerDefinitionsCollection,
|
||||
legendDefinitionsCollection: new Backbone.Collection(),
|
||||
widgetDefinitionsCollection: widgetDefinitionsCollection,
|
||||
userModel: userModel,
|
||||
modals: FactoryModals.createModalService(),
|
||||
onboardings: onboardings,
|
||||
analysis: {},
|
||||
userActions: {},
|
||||
vis: {},
|
||||
stackLayoutModel: stackLayoutModel,
|
||||
configModel: configModel,
|
||||
editorModel: editorModel,
|
||||
mapModeModel: jasmine.createSpyObj('mapModeModel', ['enterDrawingFeatureMode']),
|
||||
stateDefinitionModel: {},
|
||||
onboardingNotification: onboardingNotification,
|
||||
visDefinitionModel: visDefinitionModel
|
||||
};
|
||||
|
||||
return new LayerContentView(_.extend(viewOptions, options));
|
||||
};
|
||||
|
||||
describe('.initialize', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(LayerContentView.prototype, '_updateViewState').and.callThrough();
|
||||
spyOn(LayerContentView.prototype, '_initViewState').and.callThrough();
|
||||
spyOn(LayerContentView.prototype, '_initBinds').and.callThrough();
|
||||
spyOn(AnalysesService, 'setLayerId').and.callThrough();
|
||||
|
||||
view = createViewFn();
|
||||
});
|
||||
|
||||
it('should initialize correctly', function () {
|
||||
expect(view.model._querySchemaModel).toBeDefined();
|
||||
expect(view.model._queryGeometryModel).toBeDefined();
|
||||
expect(view.model._queryRowsCollection).toBeDefined();
|
||||
|
||||
expect(LayerContentView.prototype._updateViewState).toHaveBeenCalled();
|
||||
expect(LayerContentView.prototype._initViewState).toHaveBeenCalled();
|
||||
expect(LayerContentView.prototype._initBinds).toHaveBeenCalled();
|
||||
expect(AnalysesService.setLayerId).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('.render', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
spyOn(view, '_renderHeaderView').and.callThrough();
|
||||
spyOn(view, '_renderLayerTabPaneView').and.callThrough();
|
||||
});
|
||||
|
||||
it('should render correctly', function () {
|
||||
view.render();
|
||||
|
||||
expect(_.size(view._subviews)).toBe(3); // [Header, Geometry Toolips]
|
||||
expect(view.$('.Editor-HeaderInfo-titleText').text()).toContain('foo');
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
describe('when all query objects are loaded', function () {
|
||||
beforeEach(function () {
|
||||
view.render();
|
||||
});
|
||||
|
||||
it('should render correctly', function () {
|
||||
expect(_.size(view._subviews)).toBe(3); // [Header, TabPane, Geometry Toolips]
|
||||
expect(view.$('.Editor-HeaderInfo-titleText').text()).toContain('foo');
|
||||
expect(view.$('.CDB-NavMenu .CDB-NavMenu-item').length).toBe(5);
|
||||
expect(view.$('.js-editorPanelContent').length).toBe(1); // Onboarding
|
||||
});
|
||||
});
|
||||
|
||||
describe('when can be georeferenced', function () {
|
||||
beforeEach(function () {
|
||||
view._viewState.set('canBeGeoreferenced', true);
|
||||
view.render();
|
||||
});
|
||||
|
||||
it('should disable tabs', function () {
|
||||
expect(view.$el.find('.CDB-NavMenu-item.is-disabled').length).toBe(3);
|
||||
});
|
||||
|
||||
it('should select the analysis tab', function () {
|
||||
expect(view._layerTabPaneView.getSelectedTabPaneName()).toBe(analysesTabName);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when analysisDefinitionNodeModel has errors', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(analysisDefinitionNodeModel, 'hasFailed').and.returnValue(true);
|
||||
|
||||
view.render();
|
||||
});
|
||||
|
||||
it('should disable tabs properly', function () {
|
||||
expect(view.$el.find('.CDB-NavMenu-item.is-disabled').length).toBe(3);
|
||||
});
|
||||
|
||||
it('should select the analysis tab', function () {
|
||||
expect(view._layerTabPaneView.getSelectedTabPaneName()).toBe(analysesTabName);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when is empty', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(analysisDefinitionNodeModel.querySchemaModel, 'isDone').and.returnValue(true);
|
||||
spyOn(analysisDefinitionNodeModel.queryGeometryModel, 'isDone').and.returnValue(true);
|
||||
spyOn(analysisDefinitionNodeModel.queryRowsCollection, 'isDone').and.returnValue(true);
|
||||
spyOn(analysisDefinitionNodeModel, 'hasFailed').and.returnValue(false);
|
||||
|
||||
view._viewState.set({
|
||||
isLayerEmpty: true
|
||||
});
|
||||
|
||||
view.render();
|
||||
});
|
||||
|
||||
it('should select the data tab', function () {
|
||||
expect(view._layerTabPaneView.getSelectedTabPaneName()).toBe(dataTabName);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('._initViewState', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
});
|
||||
|
||||
it('should initialize viewState', function () {
|
||||
view._initViewState();
|
||||
|
||||
expect(view._viewState).toBeDefined();
|
||||
expect(view._viewState.get('isLayerEmpty')).toEqual(false);
|
||||
expect(view._viewState.get('canBeGeoreferenced')).toEqual(false);
|
||||
expect(view._viewState.get('isLayerDone')).toEqual(false);
|
||||
});
|
||||
|
||||
it('should call setViewState', function () {
|
||||
spyOn(view, '_setViewState');
|
||||
|
||||
view._initViewState();
|
||||
|
||||
expect(view._setViewState).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._updateViewState', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
spyOn(view, '_fetchAllQueryObjects').and.returnValue(Promise.resolve(true));
|
||||
spyOn(view, '_setViewState').and.callThrough();
|
||||
});
|
||||
|
||||
it('should call _setViewState if isDone', function () {
|
||||
spyOn(view.model, 'isDone').and.returnValue(true);
|
||||
|
||||
view._updateViewState();
|
||||
|
||||
expect(view._setViewState).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call _setViewState after all queries have been fetched', function (done) {
|
||||
spyOn(view.model, 'isDone').and.returnValue(false);
|
||||
|
||||
view._updateViewState();
|
||||
|
||||
setTimeout(function () {
|
||||
expect(view._setViewState).toHaveBeenCalled();
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._setViewState', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
});
|
||||
|
||||
describe('set isLayerEmpty', function () {
|
||||
it('should be setted to true if layer is empty', function (done) {
|
||||
view.render();
|
||||
|
||||
spyOn(view, 'render');
|
||||
spyOn(view._layerDefinitionModel, 'isEmptyAsync').and.returnValue(Promise.resolve(true));
|
||||
spyOn(view._layerDefinitionModel, 'canBeGeoreferenced').and.returnValue(Promise.resolve(true));
|
||||
|
||||
view._setViewState();
|
||||
|
||||
setTimeout(function () {
|
||||
expect(view._viewState.get('isLayerEmpty')).toEqual(true);
|
||||
expect(view._viewState.get('canBeGeoreferenced')).toEqual(true);
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('set canBeGeoreferenced', function () {
|
||||
it('should be setted to true if layer can not be georeferenced', function (done) {
|
||||
view._viewState.set({
|
||||
isLayerEmpty: true,
|
||||
canBeGeoreferenced: false
|
||||
});
|
||||
|
||||
view.render();
|
||||
|
||||
spyOn(view._layerDefinitionModel, 'isEmptyAsync').and.returnValue(Promise.resolve(true));
|
||||
spyOn(view._layerDefinitionModel, 'canBeGeoreferenced').and.returnValue(Promise.resolve(true));
|
||||
|
||||
view._setViewState();
|
||||
|
||||
setTimeout(function () {
|
||||
expect(view._viewState.get('canBeGeoreferenced')).toEqual(true);
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
it('should be setted to false if layer can be georeferenced', function (done) {
|
||||
view._viewState.set({
|
||||
isLayerEmpty: true,
|
||||
canBeGeoreferenced: true
|
||||
});
|
||||
|
||||
view.render();
|
||||
|
||||
spyOn(view._layerDefinitionModel, 'isEmptyAsync').and.returnValue(Promise.resolve(true));
|
||||
spyOn(view._layerDefinitionModel, 'canBeGeoreferenced').and.returnValue(Promise.resolve(false));
|
||||
|
||||
view._setViewState();
|
||||
|
||||
setTimeout(function () {
|
||||
expect(view._viewState.get('canBeGeoreferenced')).toEqual(false);
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('set isLayerDone', function () {
|
||||
// isLayerDone can be false when entering and the UI should respond to that
|
||||
it('should set isLayerDone when entering the function and again when the promise resolves', function (done) {
|
||||
view._viewState.set('isLayerDone', true, { silent: true });
|
||||
spyOn(view._layerDefinitionModel, 'isEmptyAsync').and.returnValue(Promise.resolve(true));
|
||||
spyOn(view._layerDefinitionModel, 'canBeGeoreferenced').and.returnValue(Promise.resolve(false));
|
||||
spyOn(view._layerDefinitionModel, 'isDone').and.returnValue(false);
|
||||
|
||||
view._setViewState();
|
||||
|
||||
expect(view._viewState.get('isLayerDone')).toBe(false);
|
||||
view._layerDefinitionModel.isDone.and.returnValue(true);
|
||||
setTimeout(function () {
|
||||
expect(view._viewState.get('isLayerDone')).toEqual(true);
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('._renderLayerTabPaneView', function () {
|
||||
it('should disable all tabs but data is layer is empty', function () {
|
||||
view = createViewFn();
|
||||
view._viewState.set({
|
||||
isLayerEmpty: true,
|
||||
isLayerDone: true
|
||||
}, { silent: true });
|
||||
|
||||
view._renderLayerTabPaneView();
|
||||
|
||||
var tabs = view.$('nav > ul > li');
|
||||
|
||||
expect(tabs[0].classList.contains('is-disabled')).toBe(false);
|
||||
expect(tabs[1].classList.contains('is-disabled')).toBe(true);
|
||||
expect(tabs[2].classList.contains('is-disabled')).toBe(true);
|
||||
expect(tabs[3].classList.contains('is-disabled')).toBe(true);
|
||||
expect(tabs[4].classList.contains('is-disabled')).toBe(true);
|
||||
});
|
||||
|
||||
it('should enable all tabs but data is layer is not empty', function () {
|
||||
view = createViewFn();
|
||||
view._viewState.set({
|
||||
isLayerEmpty: false,
|
||||
isLayerDone: true
|
||||
}, { silent: true });
|
||||
|
||||
view._renderLayerTabPaneView();
|
||||
|
||||
var tabs = view.$('nav > ul > li');
|
||||
|
||||
expect(tabs[0].classList.contains('is-disabled')).toBe(false);
|
||||
expect(tabs[1].classList.contains('is-disabled')).toBe(false);
|
||||
expect(tabs[2].classList.contains('is-disabled')).toBe(false);
|
||||
expect(tabs[3].classList.contains('is-disabled')).toBe(false);
|
||||
expect(tabs[4].classList.contains('is-disabled')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._onClickBack', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
view.render();
|
||||
});
|
||||
|
||||
it('should call goToLayerList in Router', function () {
|
||||
view.$('.js-back').click();
|
||||
|
||||
expect(Router.goToLayerList).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should set editorModel edition to false', function () {
|
||||
editorModel.set('edition', true, { silent: true });
|
||||
|
||||
view.$('.js-back').click();
|
||||
|
||||
expect(editorModel.get('edition')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('.handleRoute', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
view.render();
|
||||
});
|
||||
|
||||
it('should create editOverlay', function () {
|
||||
handleRouteSpy.and.callThrough();
|
||||
var routeModel = new Backbone.Model({
|
||||
currentRoute: ['add_feature_']
|
||||
});
|
||||
|
||||
view._handleRoute(routeModel);
|
||||
|
||||
expect($(dashboardCanvasEl).find('.js-editOverlay').length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._renderContextButtons', function () {
|
||||
var analysisDefNodeModel;
|
||||
var layerDefinitionModel;
|
||||
var queryGeometryModel;
|
||||
var needsGeoSpy;
|
||||
var isEmptySpy;
|
||||
|
||||
function hasTipsy (subviews) {
|
||||
var newGeomItems = _.filter(subviews, function (elem) {
|
||||
return elem.el.classList.contains('js-newGeometryView');
|
||||
});
|
||||
|
||||
if (newGeomItems.length === 0) return false;
|
||||
|
||||
return newGeomItems.reduce(function (hasTipsy, btn) {
|
||||
return btn.tipsy && hasTipsy;
|
||||
}, true);
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
view.render();
|
||||
|
||||
analysisDefNodeModel = view._getAnalysisDefinitionNodeModel();
|
||||
layerDefinitionModel = view._layerDefinitionModel;
|
||||
queryGeometryModel = view._getQueryGeometryModel();
|
||||
|
||||
spyOn(analysisDefNodeModel, 'isReadOnly');
|
||||
needsGeoSpy = spyOn(view._layerDefinitionModel, 'canBeGeoreferenced');
|
||||
isEmptySpy = spyOn(view._layerDefinitionModel, 'isEmptyAsync');
|
||||
|
||||
view._renderContextButtons();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
view._destroyContextButtons();
|
||||
});
|
||||
|
||||
it('shouldn\'t disable tabs', function () {
|
||||
needsGeoSpy.and.returnValue(Promise.resolve(false));
|
||||
isEmptySpy.and.returnValue(Promise.resolve(false));
|
||||
|
||||
view.render();
|
||||
|
||||
expect(view.$el.find('.CDB-NavMenu-item.is-disabled').length).toBe(0);
|
||||
});
|
||||
|
||||
it('should not create editOverlay tip view', function () {
|
||||
expect($(dashboardCanvasEl).find('js-editOverlay').length).toBe(0);
|
||||
});
|
||||
|
||||
it('should navigate when click on new geometry', function () {
|
||||
queryGeometryModel.set('simple_geom', 'point');
|
||||
analysisDefNodeModel.isReadOnly.and.returnValue(false);
|
||||
layerDefinitionModel.set('visible', true);
|
||||
view._renderContextButtons();
|
||||
|
||||
$(dashboardCanvasEl).find('.js-newGeometry').trigger('click');
|
||||
|
||||
expect(Router.navigate).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should render context switch toggler properly', function () {
|
||||
expect($(dashboardCanvasEl).find('.Editor-contextSwitcher.js-mapTableView').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should render geometry buttons properly', function () {
|
||||
expect($(dashboardCanvasEl).find('.Editor-contextSwitcherItem.js-newGeometryItem').length).toBe(3);
|
||||
});
|
||||
|
||||
it('should disable edit geometry buttons when node is read only and visible', function () {
|
||||
analysisDefNodeModel.isReadOnly.and.returnValue(true);
|
||||
layerDefinitionModel.set('visible', true);
|
||||
expect($(dashboardCanvasEl).find('.Editor-contextSwitcherItem.js-newGeometryItem.is-disabled').length).toBe(3);
|
||||
});
|
||||
|
||||
it('should disable edit geometry buttons when node is read only and not visible', function () {
|
||||
analysisDefNodeModel.isReadOnly.and.returnValue(true);
|
||||
layerDefinitionModel.set('visible', false);
|
||||
expect($(dashboardCanvasEl).find('.Editor-contextSwitcherItem.js-newGeometryItem.is-disabled').length).toBe(3);
|
||||
});
|
||||
|
||||
it('should disable edit geometry buttons when node is neither visible nor read only', function () {
|
||||
analysisDefNodeModel.isReadOnly.and.returnValue(false);
|
||||
layerDefinitionModel.set('visible', false);
|
||||
expect($(dashboardCanvasEl).find('.Editor-contextSwitcherItem.js-newGeometryItem.is-disabled').length).toBe(3);
|
||||
});
|
||||
|
||||
it('should not disable edit geometry buttons when node is visible and not read only', function () {
|
||||
analysisDefNodeModel.isReadOnly.and.returnValue(false);
|
||||
layerDefinitionModel.set('visible', true);
|
||||
expect($(dashboardCanvasEl).find('.Editor-contextSwitcherItem.js-newGeometryItem.is-disabled').length).toBe(0);
|
||||
});
|
||||
|
||||
it('should add tipsy subview when not visible or read only', function () {
|
||||
view.clearSubViews();
|
||||
analysisDefNodeModel.isReadOnly.and.returnValue(true);
|
||||
layerDefinitionModel.set('visible', false);
|
||||
expect(hasTipsy(view._subviews)).toBe(true);
|
||||
});
|
||||
|
||||
it('should not add tipsy subview when not visible or read only', function () {
|
||||
view.clearSubViews();
|
||||
analysisDefNodeModel.isReadOnly.and.returnValue(false);
|
||||
layerDefinitionModel.set('visible', true);
|
||||
expect(hasTipsy(view._subviews)).toBe(false);
|
||||
});
|
||||
|
||||
it('should display only one button per geometry', function () {
|
||||
queryGeometryModel.set('simple_geom', 'polygon');
|
||||
view._renderContextButtons();
|
||||
expect($(dashboardCanvasEl).find('.Editor-contextSwitcherItem.js-newGeometryItem').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should show the button for the simple_geom type', function () {
|
||||
queryGeometryModel.set('simple_geom', 'point');
|
||||
view._renderContextButtons();
|
||||
expect($(dashboardCanvasEl).find('.js-newGeometry[data-feature-type="point"]').length).toBe(1);
|
||||
|
||||
queryGeometryModel.set('simple_geom', 'line');
|
||||
view._renderContextButtons();
|
||||
expect($(dashboardCanvasEl).find('.js-newGeometry[data-feature-type="line"]').length).toBe(1);
|
||||
|
||||
queryGeometryModel.set('simple_geom', 'polygon');
|
||||
view._renderContextButtons();
|
||||
expect($(dashboardCanvasEl).find('.js-newGeometry[data-feature-type="polygon"]').length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._initBinds', function () {
|
||||
function buildResponse (geom) {
|
||||
return {
|
||||
status: 200,
|
||||
contentType: 'application/json; charset=utf-8',
|
||||
responseText: '{"rows":[{"the_geom":"' + geom + '"}],"time":0.005,"fields":{"the_geom":{"type":"string"}},"total_rows":1}'
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
jasmine.Ajax.install();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
jasmine.Ajax.uninstall();
|
||||
});
|
||||
|
||||
it('should call _renderContextButtons and set default style properties if there is a change in simple_geom to a valid value', function () {
|
||||
var queryGeometryModel = view._getQueryGeometryModel();
|
||||
queryGeometryModel.set('status', 'unfetched');
|
||||
queryGeometryModel.set('simple_geom', 'point');
|
||||
queryGeometryModel.set('query', 'SELECT TOP 10 FROM fake_table;');
|
||||
spyOn(view, '_renderContextButtons');
|
||||
|
||||
jasmine.Ajax.stubRequest(/sql/)
|
||||
.andReturn(buildResponse('polygon'));
|
||||
view._initBinds();
|
||||
|
||||
queryGeometryModel.set('simple_geom', 'polygon');
|
||||
|
||||
expect(view._renderContextButtons).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call _renderContextButtons and not set default style properties if there is a change in simple_geom to nothing', function () {
|
||||
var queryGeometryModel = view._getQueryGeometryModel();
|
||||
queryGeometryModel.set('status', 'unfetched');
|
||||
queryGeometryModel.set('simple_geom', 'point');
|
||||
queryGeometryModel.set('query', 'SELECT TOP 10 FROM fake_table;');
|
||||
spyOn(view, '_renderContextButtons');
|
||||
|
||||
jasmine.Ajax.stubRequest(/sql/)
|
||||
.andReturn(buildResponse(''));
|
||||
view._initBinds();
|
||||
|
||||
queryGeometryModel.set('simple_geom', '');
|
||||
|
||||
expect(view._renderContextButtons).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call _onTogglerChanged if togglerModel:active changes', function () {
|
||||
spyOn(view, '_onTogglerChanged');
|
||||
view._initBinds();
|
||||
view._togglerModel.trigger('change:active');
|
||||
expect(view._onTogglerChanged).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call handleRoute if routeModel:currentRoute changes', function () {
|
||||
var routeModel = Router.getRouteModel();
|
||||
|
||||
routeModel.trigger('change:currentRoute', routeModel);
|
||||
|
||||
expect(view._handleRoute).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call render if analysisDefinitionNodeModel:error changes', function () {
|
||||
spyOn(view, 'render');
|
||||
view._initBinds();
|
||||
analysisDefinitionNodeModel.trigger('change:error');
|
||||
expect(view.render).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call _updateViewState on change model state', function () {
|
||||
spyOn(view, 'render');
|
||||
spyOn(view, '_updateViewState');
|
||||
|
||||
view._initBinds();
|
||||
|
||||
view.model.set('state', 'fake');
|
||||
expect(view._updateViewState).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call _updateViewState on change layerDefinitionModel source', function () {
|
||||
spyOn(view, 'render');
|
||||
spyOn(view, '_updateViewState');
|
||||
|
||||
view._initBinds();
|
||||
|
||||
view._layerDefinitionModel.set('source', 'fake');
|
||||
|
||||
expect(view._updateViewState).toHaveBeenCalled();
|
||||
});
|
||||
}); // end of '._initBinds'
|
||||
|
||||
describe('._onTogglerChanged', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
view.render();
|
||||
});
|
||||
|
||||
it('should set context correctly', function () {
|
||||
expect(view.model.get('context')).toEqual('map');
|
||||
view._togglerModel.set('active', true, { silent: true });
|
||||
view._onTogglerChanged();
|
||||
expect(view.model.get('context')).toEqual('table');
|
||||
|
||||
view._togglerModel.set('active', false, { silent: true });
|
||||
view._onTogglerChanged();
|
||||
expect(view.model.get('context')).toEqual('map');
|
||||
});
|
||||
|
||||
it('should call _initTable if context is table', function () {
|
||||
spyOn(view, '_initTable');
|
||||
view._togglerModel.set('active', true, { silent: true });
|
||||
view._onTogglerChanged();
|
||||
expect(view._initTable).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call _destroyTable if context is map', function () {
|
||||
spyOn(view, '_destroyTable');
|
||||
view._togglerModel.set('active', false, { silent: true });
|
||||
view._onTogglerChanged();
|
||||
expect(view._destroyTable).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._isContextTable', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
view.render();
|
||||
});
|
||||
|
||||
it('should return true if model context is table', function () {
|
||||
expect(view._isContextTable()).toBe(false);
|
||||
view.model.set('context', 'table', { silent: true });
|
||||
expect(view._isContextTable()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when rendering analysis tab', function () {
|
||||
var addBindingsAndRender = function () {
|
||||
spyOn(analysisDefinitionNodeModel.querySchemaModel, 'isDone').and.returnValue(true);
|
||||
spyOn(analysisDefinitionNodeModel.queryGeometryModel, 'isDone').and.returnValue(true);
|
||||
spyOn(analysisDefinitionNodeModel.queryRowsCollection, 'isDone').and.returnValue(true);
|
||||
|
||||
view.render();
|
||||
};
|
||||
|
||||
describe('when analysis payload is a string and its in the formsCollection', function () {
|
||||
it('should use the analysis payload as selectedNodeId', function () {
|
||||
var analysisPayload = 'a1';
|
||||
spyOn(AnalysisFormsCollection.prototype, 'get').and.returnValue(analysisPayload);
|
||||
view = createViewFn({
|
||||
selectedTabItem: 'analyses',
|
||||
analysisPayload: analysisPayload
|
||||
});
|
||||
|
||||
addBindingsAndRender();
|
||||
|
||||
var analysesContentView = _.find(view._layerTabPaneView._subviews, function (view) {
|
||||
return view.className === 'Editor-content';
|
||||
});
|
||||
|
||||
expect(analysesContentView._selectedNodeId).toEqual(analysisPayload);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when analysis payload is an object', function () {
|
||||
it('should use the analysis payload id key as selectedNodeId', function () {
|
||||
var analysisPayload = {
|
||||
id: 'a2'
|
||||
};
|
||||
view = createViewFn({
|
||||
selectedTabItem: 'analyses',
|
||||
analysisPayload: analysisPayload
|
||||
});
|
||||
|
||||
addBindingsAndRender();
|
||||
|
||||
var analysesContentView = _.find(view._layerTabPaneView._subviews, function (view) {
|
||||
return view.className === 'Editor-content';
|
||||
});
|
||||
|
||||
expect(analysesContentView._selectedNodeId).toEqual(analysisPayload.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when analysis payload is null', function () {
|
||||
describe('when last node exists', function () {
|
||||
it('should redirect to the last node id', function () {
|
||||
var lastAnalysisNode = new Backbone.Model({
|
||||
id: 'a1'
|
||||
});
|
||||
spyOn(AnalysisFormsCollection.prototype, 'at').and.returnValue(lastAnalysisNode);
|
||||
spyOn(Router, 'goToAnalysisNode');
|
||||
view = createViewFn({
|
||||
selectedTabItem: 'analyses',
|
||||
analysisPayload: null
|
||||
});
|
||||
|
||||
addBindingsAndRender();
|
||||
|
||||
expect(Router.goToAnalysisNode).toHaveBeenCalledWith(
|
||||
layerDefinitionModel.get('id'),
|
||||
lastAnalysisNode.get('id'),
|
||||
{ trigger: false, replace: true }
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there is no nodes left', function () {
|
||||
it('should redirect to the analysis tab', function () {
|
||||
spyOn(Router, 'goToAnalysisTab');
|
||||
view = createViewFn({
|
||||
selectedTabItem: 'analyses',
|
||||
analysisPayload: null
|
||||
});
|
||||
|
||||
addBindingsAndRender();
|
||||
|
||||
expect(Router.goToAnalysisTab).toHaveBeenCalledWith(
|
||||
layerDefinitionModel.get('id'),
|
||||
{ trigger: false, replace: true }
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
dashboardCanvasEl.parentNode && dashboardCanvasEl.parentNode.removeChild(dashboardCanvasEl);
|
||||
view.clean();
|
||||
});
|
||||
});
|
||||
+440
@@ -0,0 +1,440 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var AnalysisFormsCollection = require('builder/editor/layers/layer-content-views/analyses/analysis-forms-collection');
|
||||
var AnalysesContentView = require('builder/editor/layers/layer-content-views/analyses/analyses-content-view');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var LayerContentModel = require('builder/data/layer-content-model');
|
||||
var analysisPlaceholderTemplate = require('builder/editor/layers/layer-content-views/analyses/analyses-placeholder.tpl');
|
||||
var analysisSQLErrorTemplate = require('builder/editor/layers/layer-content-views/analyses/analyses-sql-error.tpl');
|
||||
var actionErrorTemplate = require('builder/editor/layers/sql-error-action.tpl');
|
||||
var ScrollView = require('builder/components/scroll/scroll-view');
|
||||
var AnalysisControlsView = require('builder/components/scroll/scroll-view');
|
||||
var Router = require('builder/routes/router');
|
||||
var AnalysesService = require('builder/editor/layers/layer-content-views/analyses/analyses-service');
|
||||
|
||||
describe('editor/layers/layer-content-view/analyses-content-view', function () {
|
||||
var view;
|
||||
var userActions;
|
||||
var analysisFormsCollection;
|
||||
var layerDefinitionModel;
|
||||
var analysisDefinitionNodesCollection;
|
||||
var overlayModel;
|
||||
var layerContentModel;
|
||||
var deleteAnalysisModel;
|
||||
var stackLayoutModel;
|
||||
var renderTooltipSpy;
|
||||
var renderSpy;
|
||||
var onLayerDefSourceChangedSpy;
|
||||
var setDefaultSelectedNodeIdSpy;
|
||||
var toggleOverlaySpy;
|
||||
var removeUselessModelsSpy;
|
||||
var onAnalysisFormsCollectionSyncSpy;
|
||||
var nodeA0;
|
||||
var nodeA1;
|
||||
var nodeB1;
|
||||
var formA0;
|
||||
var formA1;
|
||||
var formB1;
|
||||
|
||||
var createViewFn = function (options) {
|
||||
var configModel = new ConfigModel({
|
||||
user_name: 'pepe'
|
||||
});
|
||||
|
||||
var userModel = new UserModel({}, {
|
||||
configModel: configModel
|
||||
});
|
||||
spyOn(userModel, 'fetch');
|
||||
|
||||
userActions = jasmine.createSpyObj('userActions', ['saveLayer']);
|
||||
|
||||
deleteAnalysisModel = new Backbone.Model({
|
||||
analysisId: null
|
||||
});
|
||||
|
||||
overlayModel = new Backbone.Model({
|
||||
visible: false
|
||||
});
|
||||
|
||||
analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
|
||||
configModel: configModel,
|
||||
userModel: userModel
|
||||
});
|
||||
|
||||
nodeA0 = analysisDefinitionNodesCollection.add({
|
||||
id: 'a0',
|
||||
type: 'source',
|
||||
table_name: 'foo',
|
||||
query: 'SELECT * FROM table_name'
|
||||
});
|
||||
|
||||
nodeA1 = analysisDefinitionNodesCollection.add({
|
||||
id: 'a1',
|
||||
type: 'buffer',
|
||||
radio: 300,
|
||||
source: 'a0'
|
||||
});
|
||||
|
||||
nodeB1 = analysisDefinitionNodesCollection.add({
|
||||
id: 'b1',
|
||||
type: 'buffer',
|
||||
radio: 300,
|
||||
source: 'a1'
|
||||
});
|
||||
|
||||
layerDefinitionModel = new LayerDefinitionModel({
|
||||
id: 'a0',
|
||||
letter: 'a',
|
||||
source: 'a0',
|
||||
options: {
|
||||
type: 'CartoDB',
|
||||
table_name: 'foo',
|
||||
cartocss: 'asd',
|
||||
source: 'a0'
|
||||
}
|
||||
}, {
|
||||
configModel: configModel,
|
||||
collection: new Backbone.Collection(),
|
||||
parse: true
|
||||
});
|
||||
layerDefinitionModel.findAnalysisDefinitionNodeModel = function (id) {
|
||||
switch (id) {
|
||||
case 'a0': return nodeA0;
|
||||
case 'a1': return nodeA1;
|
||||
}
|
||||
};
|
||||
|
||||
analysisFormsCollection = new AnalysisFormsCollection(null, {
|
||||
configModel: configModel,
|
||||
userActions: userActions,
|
||||
layerDefinitionModel: layerDefinitionModel,
|
||||
analysisSourceOptionsModel: new Backbone.Model()
|
||||
});
|
||||
analysisFormsCollection.isEmpty = function () { return true; };
|
||||
|
||||
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; };
|
||||
|
||||
layerContentModel = new LayerContentModel({}, {
|
||||
querySchemaModel: querySchemaModel,
|
||||
queryGeometryModel: queryGeometryModel,
|
||||
queryRowsCollection: queryRowsCollection
|
||||
});
|
||||
layerContentModel.isErrored = function () { return false; };
|
||||
|
||||
stackLayoutModel = {};
|
||||
|
||||
var defaultOptions = {
|
||||
userActions,
|
||||
analysisDefinitionNodesCollection,
|
||||
analysisFormsCollection,
|
||||
layerDefinitionModel,
|
||||
userModel,
|
||||
configModel,
|
||||
stackLayoutModel,
|
||||
overlayModel,
|
||||
layerContentModel,
|
||||
deleteAnalysisModel
|
||||
};
|
||||
|
||||
var view = new AnalysesContentView(_.extend(defaultOptions, options));
|
||||
|
||||
renderTooltipSpy = spyOn(view, '_renderTooltip');
|
||||
|
||||
return view;
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
renderSpy = spyOn(AnalysesContentView.prototype, 'render');
|
||||
onLayerDefSourceChangedSpy = spyOn(AnalysesContentView.prototype, '_onLayerDefSourceChanged');
|
||||
setDefaultSelectedNodeIdSpy = spyOn(AnalysesContentView.prototype, '_setDefaultSelectedNodeId');
|
||||
toggleOverlaySpy = spyOn(AnalysesContentView.prototype, '_toggleOverlay');
|
||||
onAnalysisFormsCollectionSyncSpy = spyOn(AnalysesContentView.prototype, '_onAnalysisFormsCollectionSync');
|
||||
removeUselessModelsSpy = spyOn(AnalysisFormsCollection.prototype, 'removeUselessModels');
|
||||
|
||||
view = createViewFn();
|
||||
});
|
||||
|
||||
describe('.render', function () {
|
||||
it('should render properly', function () {
|
||||
renderSpy.and.callThrough();
|
||||
|
||||
spyOn(view, '_initViews');
|
||||
|
||||
view.render();
|
||||
|
||||
expect(view._initViews).toHaveBeenCalled();
|
||||
expect(toggleOverlaySpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not have any leaks', function () {
|
||||
renderSpy.and.callThrough();
|
||||
|
||||
view.render();
|
||||
|
||||
expect(view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
describe('is errored', function () {
|
||||
it('should render error', function () {
|
||||
renderSpy.and.callThrough();
|
||||
view._isErrored = function () { return true; };
|
||||
spyOn(view, '_renderError');
|
||||
|
||||
view.render();
|
||||
|
||||
expect(view._renderError).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('._isErrored', function () {
|
||||
it('should return if layer content model is errored', function () {
|
||||
expect(view._isErrored()).toBe(false);
|
||||
|
||||
layerContentModel.isErrored = function () { return true; };
|
||||
|
||||
expect(view._isErrored()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._renderError', function () {
|
||||
it('should render error', function () {
|
||||
var template = analysisSQLErrorTemplate({
|
||||
body: _t('editor.error-query.body', {
|
||||
action: actionErrorTemplate({
|
||||
label: _t('editor.error-query.label')
|
||||
})
|
||||
})
|
||||
});
|
||||
spyOn(view.$el, 'html');
|
||||
|
||||
view._renderError();
|
||||
|
||||
expect(view.$el.html).toHaveBeenCalledWith(template);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._initViews', function () {
|
||||
it('should init views', function () {
|
||||
var template = analysisPlaceholderTemplate();
|
||||
spyOn(view.$el, 'html');
|
||||
|
||||
view._initViews();
|
||||
|
||||
expect(view.$el.html).toHaveBeenCalledWith(template);
|
||||
expect(renderTooltipSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('analysis forms collection is not empty', function () {
|
||||
it('should init views', function () {
|
||||
analysisFormsCollection.isEmpty = function () { return false; };
|
||||
|
||||
var formModel = 'wadus';
|
||||
|
||||
spyOn(view, '_getFormModel').and.returnValue(formModel);
|
||||
spyOn(view, '_renderScrollableListView');
|
||||
spyOn(view, '_renderControlsView');
|
||||
|
||||
view._initViews();
|
||||
|
||||
expect(view._renderScrollableListView).toHaveBeenCalledWith(formModel);
|
||||
expect(view._renderControlsView).toHaveBeenCalledWith(formModel);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('._toggleOverlay', function () {
|
||||
it('should toggle overlay', function () {
|
||||
toggleOverlaySpy.and.callThrough();
|
||||
expect(view.$el.hasClass('is-disabled')).toBe(false);
|
||||
|
||||
overlayModel.set('visible', true, { silent: true });
|
||||
view._toggleOverlay();
|
||||
|
||||
expect(view.$el.hasClass('is-disabled')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('_renderTooltip', function () {
|
||||
it('should renderTooltip', function () {
|
||||
renderSpy.and.callThrough();
|
||||
|
||||
view.render();
|
||||
|
||||
const currentViews = _.size(view._subviews);
|
||||
renderTooltipSpy.and.callThrough();
|
||||
view._renderTooltip();
|
||||
|
||||
expect(_.size(view._subviews)).toBe(currentViews + 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('_initBinds', function () {
|
||||
it('should listen to layerDefinitionModel change source', function () {
|
||||
layerDefinitionModel.set('source', 'a1');
|
||||
|
||||
expect(onLayerDefSourceChangedSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should listen to analysisFormsCollection destroyedModels', function () {
|
||||
analysisFormsCollection.trigger('destroyedModels');
|
||||
|
||||
expect(setDefaultSelectedNodeIdSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should listen to analysisFormsCollection remove', function () {
|
||||
analysisFormsCollection.reset([{}]);
|
||||
analysisFormsCollection.remove(analysisFormsCollection.at(0));
|
||||
|
||||
expect(setDefaultSelectedNodeIdSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should listen to overlayModel change visible', function () {
|
||||
overlayModel.set('visible', true);
|
||||
|
||||
expect(toggleOverlaySpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should listen to analysisFormsCollection change persisted', function () {
|
||||
var analysisFormModel = analysisFormsCollection.add({ persisted: false });
|
||||
analysisFormModel.set('persisted', true);
|
||||
|
||||
expect(onAnalysisFormsCollectionSyncSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('_renderScrollableListView', function () {
|
||||
it('should render scrollable list view', function () {
|
||||
spyOn(ScrollView.prototype, 'render').and.returnValue(this);
|
||||
|
||||
view._renderScrollableListView();
|
||||
|
||||
expect(_.size(view._subviews)).toBe(1); // ['ScrollView']
|
||||
});
|
||||
});
|
||||
|
||||
describe('_renderControlsView', function () {
|
||||
it('should render constrols view', function () {
|
||||
spyOn(AnalysisControlsView.prototype, 'render').and.returnValue(this);
|
||||
|
||||
var formModel = new Backbone.Model({
|
||||
id: 'wadus',
|
||||
type: 'type'
|
||||
});
|
||||
|
||||
view._renderControlsView(formModel);
|
||||
|
||||
expect(_.size(view._subviews)).toBe(1); // ['AnalysisControlsView']
|
||||
});
|
||||
});
|
||||
|
||||
describe('_getFormModel', function () {
|
||||
afterEach(function () {
|
||||
analysisFormsCollection.reset([]);
|
||||
});
|
||||
|
||||
it('should get form model', function () {
|
||||
formA0 = analysisFormsCollection.add(nodeA0.attributes);
|
||||
|
||||
view = createViewFn({
|
||||
analysisFormsCollection: analysisFormsCollection
|
||||
});
|
||||
|
||||
expect(view._getFormModel()).toBe(formA0);
|
||||
});
|
||||
|
||||
describe('with selectedNodeId', function () {
|
||||
it('should get form model', function () {
|
||||
formA0 = analysisFormsCollection.add(nodeA0.attributes);
|
||||
formA1 = analysisFormsCollection.add(nodeA1.attributes);
|
||||
|
||||
view = createViewFn({
|
||||
selectedNodeId: formA1.get('id'),
|
||||
analysisFormsCollection: analysisFormsCollection
|
||||
});
|
||||
|
||||
expect(view._getFormModel()).toBe(formA1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('_getQuerySchemaModelForEstimation', function () {
|
||||
afterEach(function () {
|
||||
analysisFormsCollection.reset([]);
|
||||
});
|
||||
|
||||
it('should get QuerySchemaModel', function () {
|
||||
spyOn(layerDefinitionModel, 'getAnalysisDefinitionNodeModel').and.returnValue(nodeA0);
|
||||
|
||||
view = createViewFn({
|
||||
analysisFormsCollection: analysisFormsCollection
|
||||
});
|
||||
|
||||
expect(view._getQuerySchemaModelForEstimation()).toBe(nodeA0.querySchemaModel);
|
||||
});
|
||||
|
||||
describe('with selectedNodeId', function () {
|
||||
it('should get form model', function () {
|
||||
formA0 = analysisFormsCollection.add(nodeA0.attributes);
|
||||
formA1 = analysisFormsCollection.add(nodeA1.attributes);
|
||||
formB1 = analysisFormsCollection.add(nodeB1.attributes);
|
||||
|
||||
view = createViewFn({
|
||||
selectedNodeId: formB1.get('id'),
|
||||
analysisFormsCollection: analysisFormsCollection
|
||||
});
|
||||
|
||||
expect(view._getQuerySchemaModelForEstimation()).toBe(nodeA1.querySchemaModel);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('_onLayerDefSourceChanged', function () {
|
||||
it('should remove models from the analysis forms collection', function () {
|
||||
onLayerDefSourceChangedSpy.and.callThrough();
|
||||
|
||||
view._onLayerDefSourceChanged();
|
||||
|
||||
expect(removeUselessModelsSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('_setDefaultSelectedNodeId', function () {
|
||||
afterEach(function () {
|
||||
analysisFormsCollection.reset([]);
|
||||
});
|
||||
|
||||
it('should call Route.goToAnalysisNode with selectedNodeId', function () {
|
||||
setDefaultSelectedNodeIdSpy.and.callThrough();
|
||||
spyOn(Router, 'goToAnalysisNode');
|
||||
spyOn(AnalysesService, 'clearNotAppliedAnalysis');
|
||||
formA0 = analysisFormsCollection.add(nodeA0.attributes);
|
||||
|
||||
view._setDefaultSelectedNodeId();
|
||||
|
||||
expect(AnalysesService.clearNotAppliedAnalysis).toHaveBeenCalledWith(nodeA0.get('id'));
|
||||
expect(Router.goToAnalysisNode).toHaveBeenCalledWith(layerDefinitionModel.get('id'), nodeA0.get('id'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('_onAnalysisFormsCollectionSync', function () {
|
||||
it('should call AnalysesService.clearNotAppliedAnalysis', function () {
|
||||
onAnalysisFormsCollectionSyncSpy.and.callThrough();
|
||||
spyOn(AnalysesService, 'clearNotAppliedAnalysis');
|
||||
|
||||
view._onAnalysisFormsCollectionSync(nodeA0);
|
||||
|
||||
expect(AnalysesService.clearNotAppliedAnalysis).toHaveBeenCalledWith(nodeA0.get('id'));
|
||||
});
|
||||
});
|
||||
});
|
||||
+259
@@ -0,0 +1,259 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var AnalysisDefinitionNodeSourceModel = require('builder/data/analysis-definition-node-source-model');
|
||||
var AnalysisDefinitionNodeModel = require('builder/data/analysis-definition-node-model');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var AreaOfInfluenceFormModel = require('builder/editor/layers/layer-content-views/analyses/analysis-form-models/area-of-influence-form-model');
|
||||
var analyses = require('builder/data/analyses');
|
||||
|
||||
describe('editor/layers/layer-content-views/analyses/analysis-form-models/area-of-influence-form-model', function () {
|
||||
beforeEach(function () {
|
||||
this._configModel = new ConfigModel({base_url: '/u/pepe'});
|
||||
this._configModel.dataServiceEnabled = function () {
|
||||
return true;
|
||||
};
|
||||
|
||||
this.analysisDefinitionNodesCollection = new Backbone.Collection();
|
||||
|
||||
this.a0 = new AnalysisDefinitionNodeSourceModel({
|
||||
id: 'a0',
|
||||
type: 'buffer',
|
||||
query: 'SELECT * FROM a_table'
|
||||
}, {
|
||||
configModel: this._configModel,
|
||||
userModel: {},
|
||||
collection: this.analysisDefinitionNodesCollection
|
||||
});
|
||||
spyOn(this.a0, 'isValidAsInputForType').and.returnValue(true);
|
||||
|
||||
this.a1 = new AnalysisDefinitionNodeModel({
|
||||
id: 'a1',
|
||||
type: 'buffer',
|
||||
radius: undefined,
|
||||
source: 'a0'
|
||||
}, {
|
||||
configModel: this._configModel,
|
||||
userModel: {},
|
||||
collection: this.analysisDefinitionNodesCollection
|
||||
});
|
||||
|
||||
this.layerDefinitionModel = new LayerDefinitionModel(null, {
|
||||
configModel: this._configModel
|
||||
});
|
||||
spyOn(this.layerDefinitionModel, 'findAnalysisDefinitionNodeModel').and.callFake(function (id) {
|
||||
switch (id) {
|
||||
case 'a0': return this.a0;
|
||||
case 'a1': return this.a1;
|
||||
default: return null;
|
||||
}
|
||||
}.bind(this));
|
||||
this.model = new AreaOfInfluenceFormModel(this.a1.attributes, {
|
||||
analyses: analyses,
|
||||
configModel: this._configModel,
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
analysisSourceOptionsModel: {},
|
||||
parse: true
|
||||
});
|
||||
});
|
||||
|
||||
it('should add analysis schema', function () {
|
||||
expect(this.model.schema).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have attributes set with defaults', function () {
|
||||
expect(this.model.attributes).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'buffer',
|
||||
distance: 'meters',
|
||||
radius: 100,
|
||||
isolines: 1,
|
||||
dissolved: false
|
||||
});
|
||||
});
|
||||
|
||||
it('should not have disabled types', function () {
|
||||
spyOn(this.model._analyses, 'isAnalysisValidByType').and.returnValue(true);
|
||||
this.model._setSchema();
|
||||
var typeOptions = this.model.schema.type.options.map(function (d) {
|
||||
return _.pick(d, 'val', 'disabled');
|
||||
});
|
||||
expect(typeOptions).toEqual([
|
||||
{val: 'buffer', disabled: false},
|
||||
{val: 'trade-area', disabled: false}
|
||||
]);
|
||||
expect(this.a0.isValidAsInputForType).toHaveBeenCalledWith('trade-area');
|
||||
});
|
||||
|
||||
describe('when model is created with custom distance and radius', function () {
|
||||
beforeEach(function () {
|
||||
this.a1.set({
|
||||
radius: 2000,
|
||||
distance: 'kilometers'
|
||||
});
|
||||
|
||||
this.model = new AreaOfInfluenceFormModel(this.a1.attributes, {
|
||||
analyses: analyses,
|
||||
configModel: this._configModel,
|
||||
userActions: {},
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
analysisSourceOptionsModel: {},
|
||||
parse: true
|
||||
});
|
||||
});
|
||||
|
||||
it('should convert the radius to the distance scale', function () {
|
||||
expect(this.model.get('radius')).toEqual(2);
|
||||
expect(this.model.get('distance')).toEqual('kilometers');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when source is not valid for other type', function () {
|
||||
beforeEach(function () {
|
||||
this.a0.isValidAsInputForType.and.returnValue(false);
|
||||
this.model = new AreaOfInfluenceFormModel(this.a1.attributes, {
|
||||
analyses: analyses,
|
||||
configModel: this._configModel,
|
||||
userActions: {},
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
analysisSourceOptionsModel: {},
|
||||
parse: true
|
||||
});
|
||||
});
|
||||
|
||||
it('should disable type since source is not valid input for the other type', function () {
|
||||
var typeOptions = this.model.schema.type.options.map(function (d) {
|
||||
return _.pick(d, 'val', 'disabled');
|
||||
});
|
||||
expect(typeOptions).toEqual([
|
||||
{val: 'buffer', disabled: false},
|
||||
{val: 'trade-area', disabled: true}
|
||||
]);
|
||||
expect(this.a0.isValidAsInputForType).toHaveBeenCalledWith('trade-area');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when type is changed', function () {
|
||||
describe('for a new model', function () {
|
||||
beforeEach(function () {
|
||||
this.prevSchema = this.model.schema;
|
||||
this.model.set('type', 'trade-area');
|
||||
});
|
||||
|
||||
it('should update the schema', function () {
|
||||
expect(this.model.schema).not.toBe(this.prevSchema);
|
||||
});
|
||||
|
||||
it('should replace the attributes but maintain persisted state', function () {
|
||||
expect(this.model.attributes).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'trade-area',
|
||||
kind: 'walk',
|
||||
isolines: 1,
|
||||
time: 100,
|
||||
dissolved: false
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('for an persisted model', function () {
|
||||
beforeEach(function () {
|
||||
this.prevSchema = this.model.schema;
|
||||
this.model.set('persisted', true);
|
||||
this.model.set('type', 'trade-area');
|
||||
});
|
||||
|
||||
it('should update the schema', function () {
|
||||
expect(this.model.schema).not.toBe(this.prevSchema);
|
||||
});
|
||||
|
||||
it('should replace the attributes but maintain persisted state', function () {
|
||||
expect(this.model.attributes).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'trade-area',
|
||||
kind: 'walk',
|
||||
isolines: 1,
|
||||
time: 100,
|
||||
dissolved: false,
|
||||
persisted: true
|
||||
});
|
||||
});
|
||||
|
||||
it('should have proper attributes', function () {
|
||||
expect(this.model._typeDef().parametersDataFields).toBe('type,kind,time,isolines,dissolved');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('.getTemplateData', function () {
|
||||
it('should return the default data for given type', function () {
|
||||
var bufferTemplateData = this.model.getTemplateData();
|
||||
expect(bufferTemplateData).toEqual({
|
||||
parametersDataFields: jasmine.any(String)
|
||||
});
|
||||
|
||||
this.model.set('type', 'trade-area');
|
||||
|
||||
expect(this.model.getTemplateData()).toEqual({
|
||||
parametersDataFields: jasmine.any(String)
|
||||
});
|
||||
expect(this.model.getTemplateData().parametersDataFields).not.toEqual(bufferTemplateData.parametersDataFields);
|
||||
});
|
||||
});
|
||||
|
||||
describe('.updateNodeDefinition', function () {
|
||||
beforeEach(function () {
|
||||
this.model.updateNodeDefinition(this.a1);
|
||||
});
|
||||
|
||||
it('should replace the attributes on the given node model but maintain values not visible/editable in the form', function () {
|
||||
expect(this.a1.attributes).toEqual({
|
||||
id: 'a1',
|
||||
type: 'buffer',
|
||||
source: 'a0',
|
||||
radius: 100,
|
||||
isolines: 1,
|
||||
distance: 'meters',
|
||||
dissolved: false
|
||||
});
|
||||
});
|
||||
|
||||
it('should convert string values to integers before setting the attrs on model', function () {
|
||||
this.model.set({
|
||||
type: 'trade-area',
|
||||
dissolved: 'true',
|
||||
isolines: '3',
|
||||
time: '60'
|
||||
});
|
||||
|
||||
this.model.updateNodeDefinition(this.a1);
|
||||
expect(this.a1.attributes).toEqual({
|
||||
dissolved: true,
|
||||
isolines: 3,
|
||||
time: 60,
|
||||
type: 'trade-area',
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
kind: 'walk'
|
||||
});
|
||||
});
|
||||
|
||||
it('should have proper attributes', function () {
|
||||
expect(this.model._typeDef().parametersDataFields).toBe('type,distance,radius,isolines,dissolved');
|
||||
});
|
||||
|
||||
it('should convert radius to meters according to the selected distance', function () {
|
||||
this.model.set({
|
||||
distance: 'kilometers',
|
||||
radius: 3
|
||||
});
|
||||
|
||||
this.model.updateNodeDefinition(this.a1);
|
||||
expect(this.a1.get('radius')).toEqual(3000);
|
||||
expect(this.a1.get('distance')).toEqual('kilometers');
|
||||
});
|
||||
});
|
||||
});
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
var AREA_OF_INFLUENCE_TYPES = require('builder/editor/layers/layer-content-views/analyses/analysis-form-models/area-of-influence-types');
|
||||
|
||||
describe('editor/layers/layer-content-views/analyses/analysis-form-models/area-of-influence-types', function () {
|
||||
AREA_OF_INFLUENCE_TYPES.forEach(function (d) {
|
||||
it('should have a type', function () {
|
||||
expect(d.type).toEqual(jasmine.any(String));
|
||||
});
|
||||
|
||||
describe('area-of-influence type ' + d.type, function () {
|
||||
it('should have a label', function () {
|
||||
expect(d.label).toEqual(jasmine.any(String));
|
||||
});
|
||||
|
||||
it('should have parametersDataFields defined with which schema props to use', function () {
|
||||
expect(d.parametersDataFields).toEqual(jasmine.any(String), 'on the format of "source,type,some_other,…"');
|
||||
expect(d.parametersDataFields).not.toContain(' ', 'the parametersDataFields must not contain any spaces');
|
||||
});
|
||||
|
||||
it('should have a parse function', function () {
|
||||
expect(d.parse).toEqual(jasmine.any(Function));
|
||||
expect(d.parse.length).toEqual(1, 'should take one argument, which is the attrs from the analysis-definition-node-model');
|
||||
});
|
||||
|
||||
it('should have a formatAttrs function', function () {
|
||||
expect(d.formatAttrs).toEqual(jasmine.any(Function));
|
||||
expect(d.formatAttrs.length).toEqual(1, 'should take one argument of the area-of-influence-form-model');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
+380
@@ -0,0 +1,380 @@
|
||||
var Backbone = require('backbone');
|
||||
var camshaftReference = require('builder/data/camshaft-reference');
|
||||
var UserActions = require('builder/data/user-actions');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var BaseAnalysisFormModel = require('builder/editor/layers/layer-content-views/analyses/analysis-form-models/base-analysis-form-model');
|
||||
var analyses = require('builder/data/analyses');
|
||||
|
||||
describe('editor/layers/layer-content-views/analyses/analysis-form-models/base-analysis-form-model', function () {
|
||||
beforeEach(function () {
|
||||
this.layerDefinitionModel = new LayerDefinitionModel(null, {
|
||||
configModel: {}
|
||||
});
|
||||
|
||||
this.userActions = UserActions({
|
||||
userModel: {},
|
||||
analysisDefinitionsCollection: {},
|
||||
analysisDefinitionNodesCollection: {},
|
||||
layerDefinitionsCollection: {},
|
||||
widgetDefinitionsCollection: {}
|
||||
});
|
||||
|
||||
this.model = new BaseAnalysisFormModel(null, {
|
||||
analyses: analyses,
|
||||
configModel: {},
|
||||
userActions: this.userActions,
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
analysisSourceOptionsModel: new Backbone.Model({
|
||||
fetching: true
|
||||
})
|
||||
});
|
||||
|
||||
spyOn(camshaftReference, 'validate');
|
||||
});
|
||||
|
||||
describe('.validate', function () {
|
||||
it('should be valid if passing camshaft validation', function () {
|
||||
camshaftReference.validate.and.returnValue({some_required_param: 'is missing'});
|
||||
expect(this.model.isValid()).toBe(false);
|
||||
|
||||
camshaftReference.validate.and.returnValue(undefined);
|
||||
expect(this.model.isValid()).toBe(true);
|
||||
});
|
||||
|
||||
it('should return errors set through setFormValidationErrors', function () {
|
||||
expect(this.model.isValid()).toBe(true);
|
||||
expect(this.model.validationError).toBeNull();
|
||||
this.changeSpy = jasmine.createSpy('change');
|
||||
this.model.on('change', this.changeSpy);
|
||||
|
||||
this.model.setFormValidationErrors({some_prop: 'invalid'});
|
||||
expect(this.model.isValid()).toBe(false);
|
||||
expect(this.model.validationError).toEqual({some_prop: 'invalid'});
|
||||
expect(this.changeSpy).toHaveBeenCalled();
|
||||
|
||||
this.changeSpy.calls.reset();
|
||||
this.model.setFormValidationErrors(undefined);
|
||||
expect(this.model.isValid()).toBe(true);
|
||||
expect(this.model.validationError).toBeNull();
|
||||
expect(this.changeSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('.updateNodeDefinition', function () {
|
||||
beforeEach(function () {
|
||||
this.nodeDefModel = new Backbone.Model({id: 'a1'});
|
||||
spyOn(this.nodeDefModel, 'set');
|
||||
|
||||
this.model.set({
|
||||
id: 'a1',
|
||||
type: 'trade-area',
|
||||
isolines: '1',
|
||||
time: '123.75',
|
||||
dissolved: 'false',
|
||||
non_default_param: {}
|
||||
});
|
||||
|
||||
this.expectedFormattedAttrs = {
|
||||
id: 'a1',
|
||||
type: 'trade-area',
|
||||
isolines: 1,
|
||||
time: 123.75,
|
||||
dissolved: false,
|
||||
non_default_param: {}
|
||||
};
|
||||
|
||||
camshaftReference.validate.and.returnValue(true);
|
||||
spyOn(camshaftReference, 'parse').and.returnValue(this.expectedFormattedAttrs);
|
||||
|
||||
this.model.updateNodeDefinition(this.nodeDefModel);
|
||||
});
|
||||
|
||||
it('should parse attrs before', function () {
|
||||
expect(camshaftReference.parse).toHaveBeenCalledWith(this.model.attributes);
|
||||
});
|
||||
|
||||
it('should update the node with parsed attrs', function () {
|
||||
expect(this.nodeDefModel.set).toHaveBeenCalled();
|
||||
expect(this.nodeDefModel.set.calls.argsFor(0)[0]).toEqual(this.expectedFormattedAttrs);
|
||||
});
|
||||
});
|
||||
|
||||
describe('.createNodeDefinition', function () {
|
||||
beforeEach(function () {
|
||||
this.nodeDefModel = new Backbone.Model({id: 'a1'});
|
||||
spyOn(this.nodeDefModel, 'set');
|
||||
|
||||
this.model.set({
|
||||
id: 'b1',
|
||||
type: 'trade-area',
|
||||
isolines: '1',
|
||||
time: '123.75',
|
||||
dissolved: 'false',
|
||||
non_default_param: {}
|
||||
});
|
||||
|
||||
this.expectedFormattedAttrs = {
|
||||
id: 'b1',
|
||||
type: 'trade-area',
|
||||
isolines: 1,
|
||||
time: 123.75,
|
||||
dissolved: false,
|
||||
non_default_param: {}
|
||||
};
|
||||
|
||||
camshaftReference.validate.and.returnValue(true);
|
||||
spyOn(camshaftReference, 'parse').and.returnValue(this.expectedFormattedAttrs);
|
||||
|
||||
spyOn(this.userActions, 'createAnalysisNode').and.returnValue(this.nodeDefModel);
|
||||
|
||||
this.model.createNodeDefinition(this.userActions);
|
||||
});
|
||||
|
||||
it('should parse attrs before', function () {
|
||||
expect(camshaftReference.parse).toHaveBeenCalledWith(this.model.attributes);
|
||||
});
|
||||
|
||||
it('should create a new node model', function () {
|
||||
expect(this.userActions.createAnalysisNode).toHaveBeenCalledWith(this.expectedFormattedAttrs, this.layerDefinitionModel);
|
||||
});
|
||||
|
||||
it('should mark the model as persisted', function () {
|
||||
expect(this.model.get('persisted')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('.getTemplateData', function () {
|
||||
it('should return an empty object', function () {
|
||||
expect(this.model.getTemplateData()).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
describe('._isPrimarySource', function () {
|
||||
it('should return if the attr argument is the primary source', function () {
|
||||
this.model.set('primary_source_name', 'target');
|
||||
|
||||
expect(this.model._isPrimarySource('target')).toBe(true);
|
||||
expect(this.model._isPrimarySource('left_source')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._isFetchingOptions', function () {
|
||||
it('should return true if it is fetching options', function () {
|
||||
expect(this.model._isFetchingOptions()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._getSourceOption', function () {
|
||||
it('should call to _getSourceNodeDefModel to retrieve the sourceNodeDefModel', function () {
|
||||
var nodeDefModel = new Backbone.Model({
|
||||
id: 'wutang'
|
||||
});
|
||||
nodeDefModel.isSourceType = function () { return true; };
|
||||
nodeDefModel.getColor = function () { return 'magenta'; };
|
||||
spyOn(this.model, '_getSourceNodeDefModel').and.returnValue(nodeDefModel);
|
||||
|
||||
var sourceOption = this.model._getSourceOption();
|
||||
|
||||
expect(sourceOption[0].val).toEqual(nodeDefModel.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('_getSourceNodeDefModel', function () {
|
||||
beforeEach(function () {
|
||||
this.model.set('source', 'a0', { silent: true });
|
||||
this.model.set('primary_source', 'z1', { silent: true });
|
||||
});
|
||||
|
||||
it('should return first the `source` node definition model', function () {
|
||||
spyOn(this.model._layerDefinitionModel, 'findAnalysisDefinitionNodeModel').and.callFake(function (name) {
|
||||
return (name === 'a0' || name === 'z1')
|
||||
? 'the ' + name
|
||||
: null;
|
||||
});
|
||||
|
||||
var sourceNodeDefModel = this.model._getSourceNodeDefModel();
|
||||
|
||||
expect(sourceNodeDefModel).toEqual('the a0');
|
||||
});
|
||||
|
||||
it('should return the `primary_source` node definition model in case there is no `source`', function () {
|
||||
spyOn(this.model._layerDefinitionModel, 'findAnalysisDefinitionNodeModel').and.callFake(function (name) {
|
||||
return name === 'z1'
|
||||
? 'the z1'
|
||||
: null;
|
||||
});
|
||||
|
||||
var sourceNodeDefModel = this.model._getSourceNodeDefModel();
|
||||
|
||||
expect(sourceNodeDefModel).toEqual('the z1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('._getSourceOptionsForSource', function () {
|
||||
it('should throw error if no sourceAttrName provided', function () {
|
||||
var self = this;
|
||||
function wrap () {
|
||||
self.model._getSourceOptionsForSource({});
|
||||
}
|
||||
|
||||
expect(wrap).toThrow(Error('sourceAttrName is required.'));
|
||||
});
|
||||
|
||||
it('should return source if it is the primary source and ignorePrimarySource is false', function () {
|
||||
var sourceAttrName = 'target';
|
||||
this.model.set(sourceAttrName, 'the source');
|
||||
spyOn(this.model, '_isPrimarySource').and.returnValue(true);
|
||||
|
||||
var result = this.model._getSourceOptionsForSource({
|
||||
sourceAttrName: sourceAttrName
|
||||
});
|
||||
|
||||
expect(result).toEqual(['the source']);
|
||||
});
|
||||
|
||||
it('should return object with isLoading if it is fetching options', function () {
|
||||
var sourceAttrName = 'target';
|
||||
this.model.set(sourceAttrName, 'the source');
|
||||
spyOn(this.model, '_isPrimarySource').and.returnValue(true);
|
||||
spyOn(this.model, '_isFetchingOptions').and.returnValue(true);
|
||||
|
||||
var result = this.model._getSourceOptionsForSource({
|
||||
sourceAttrName: sourceAttrName,
|
||||
ignorePrimarySource: true
|
||||
});
|
||||
|
||||
expect(result).toEqual([{
|
||||
val: 'the source',
|
||||
label: 'editor.layers.analysis-form.loading',
|
||||
isLoading: true
|
||||
}]);
|
||||
});
|
||||
|
||||
it('should return source options if it is fetched', function () {
|
||||
var sourceAttrName = 'target';
|
||||
this.model.set(sourceAttrName, 'the source');
|
||||
this.model._layerDefinitionModel.set('source', 808);
|
||||
this.model._analysisSourceOptionsModel.getSelectOptions = function (geomType) {
|
||||
return [
|
||||
{
|
||||
val: 606,
|
||||
type: 'node'
|
||||
}, {
|
||||
val: 707,
|
||||
type: 'other'
|
||||
}, {
|
||||
val: 808,
|
||||
type: 'other'
|
||||
}
|
||||
];
|
||||
};
|
||||
spyOn(this.model, '_isPrimarySource').and.returnValue(false);
|
||||
spyOn(this.model, '_isFetchingOptions').and.returnValue(false);
|
||||
|
||||
var result = this.model._getSourceOptionsForSource({
|
||||
sourceAttrName: sourceAttrName,
|
||||
ignorePrimarySource: true
|
||||
});
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
val: 606,
|
||||
type: 'node'
|
||||
}, {
|
||||
val: 707,
|
||||
type: 'other'
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return only source options with type node if it is fetched and onlyNodes option provided', function () {
|
||||
var sourceAttrName = 'target';
|
||||
this.model.set(sourceAttrName, 'the source');
|
||||
this.model._layerDefinitionModel.set('source', 808);
|
||||
this.model._analysisSourceOptionsModel.getSelectOptions = function () {};
|
||||
spyOn(this.model._analysisSourceOptionsModel, 'getSelectOptions').and.returnValue([
|
||||
{
|
||||
val: 606,
|
||||
type: 'node'
|
||||
}, {
|
||||
val: 707,
|
||||
type: 'other'
|
||||
}, {
|
||||
val: 808,
|
||||
type: 'other'
|
||||
}
|
||||
]);
|
||||
spyOn(this.model, '_isPrimarySource').and.returnValue(false);
|
||||
spyOn(this.model, '_isFetchingOptions').and.returnValue(false);
|
||||
|
||||
var result = this.model._getSourceOptionsForSource({
|
||||
sourceAttrName: sourceAttrName,
|
||||
ignorePrimarySource: true,
|
||||
onlyNodes: true
|
||||
});
|
||||
|
||||
expect(result).toEqual([{
|
||||
val: 606,
|
||||
type: 'node'
|
||||
}]);
|
||||
expect(this.model._analysisSourceOptionsModel.getSelectOptions).toHaveBeenCalledWith('*');
|
||||
});
|
||||
|
||||
it('should call to getSelectOptions with the right type of geom if option is provided', function () {
|
||||
var sourceAttrName = 'target';
|
||||
this.model.set(sourceAttrName, 'the source');
|
||||
this.model._layerDefinitionModel.set('source', 808);
|
||||
this.model._analysisSourceOptionsModel.getSelectOptions = function () {};
|
||||
spyOn(this.model._analysisSourceOptionsModel, 'getSelectOptions').and.returnValue([]);
|
||||
spyOn(this.model, '_isPrimarySource').and.returnValue(false);
|
||||
spyOn(this.model, '_isFetchingOptions').and.returnValue(false);
|
||||
|
||||
this.model._getSourceOptionsForSource({
|
||||
sourceAttrName: sourceAttrName,
|
||||
ignorePrimarySource: true,
|
||||
onlyNodes: true,
|
||||
requiredSimpleGeometryType: 'point'
|
||||
});
|
||||
|
||||
expect(this.model._analysisSourceOptionsModel.getSelectOptions).toHaveBeenCalledWith('point');
|
||||
});
|
||||
|
||||
it('should return the layer source with the options if includeSourceNode param is provided', function () {
|
||||
var sourceAttrName = 'target';
|
||||
this.model.set(sourceAttrName, 'the source');
|
||||
this.model._layerDefinitionModel.set('source', 1);
|
||||
this.model._analysisSourceOptionsModel.getSelectOptions = function () {};
|
||||
spyOn(this.model._analysisSourceOptionsModel, 'getSelectOptions').and.returnValue([
|
||||
{
|
||||
val: 3,
|
||||
type: 'node'
|
||||
}, {
|
||||
val: 2,
|
||||
type: 'other'
|
||||
}, {
|
||||
val: 1,
|
||||
type: 'node'
|
||||
}
|
||||
]);
|
||||
spyOn(this.model, '_isPrimarySource').and.returnValue(false);
|
||||
spyOn(this.model, '_isFetchingOptions').and.returnValue(false);
|
||||
|
||||
var result = this.model._getSourceOptionsForSource({
|
||||
sourceAttrName: sourceAttrName,
|
||||
includeSourceNode: true
|
||||
});
|
||||
|
||||
expect(result).toEqual([{
|
||||
val: 3,
|
||||
type: 'node'
|
||||
}, {
|
||||
val: 2,
|
||||
type: 'other'
|
||||
}, {
|
||||
val: 1,
|
||||
type: 'node'
|
||||
}]);
|
||||
expect(this.model._analysisSourceOptionsModel.getSelectOptions).toHaveBeenCalledWith('*');
|
||||
});
|
||||
});
|
||||
});
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
var Backbone = require('backbone');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var AnalysisDefinitionNodeSourceModel = require('builder/data/analysis-definition-node-source-model');
|
||||
var AnalysisDefinitionNodeModel = require('builder/data/analysis-definition-node-model');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var CentroidFormModel = require('builder/editor/layers/layer-content-views/analyses/analysis-form-models/centroid-form-model');
|
||||
var analyses = require('builder/data/analyses');
|
||||
|
||||
describe('editor/layers/layer-content-views/analyses/analysis-form-models/centroid-form-model', function () {
|
||||
beforeEach(function () {
|
||||
var configModel = new ConfigModel({
|
||||
base_url: '/u/pepe',
|
||||
api_key: 'wadus',
|
||||
user_name: 'pepe'
|
||||
});
|
||||
|
||||
this.analysisDefinitionNodesCollection = new Backbone.Collection();
|
||||
|
||||
this.a0 = new AnalysisDefinitionNodeSourceModel({
|
||||
id: 'a0',
|
||||
type: 'centroid',
|
||||
query: 'SELECT * FROM a_table'
|
||||
}, {
|
||||
configModel: configModel,
|
||||
userModel: {},
|
||||
collection: this.analysisDefinitionNodesCollection
|
||||
});
|
||||
spyOn(this.a0, 'isValidAsInputForType').and.returnValue(true);
|
||||
|
||||
this.a1 = new AnalysisDefinitionNodeModel({
|
||||
id: 'a1',
|
||||
type: 'centroid',
|
||||
source: 'a0'
|
||||
}, {
|
||||
configModel: configModel,
|
||||
collection: this.analysisDefinitionNodesCollection
|
||||
});
|
||||
|
||||
this.layerDefinitionModel = new LayerDefinitionModel(null, {
|
||||
configModel: configModel
|
||||
});
|
||||
spyOn(this.layerDefinitionModel, 'findAnalysisDefinitionNodeModel').and.callFake(function (id) {
|
||||
switch (id) {
|
||||
case 'a0': return this.a0;
|
||||
case 'a1': return this.a1;
|
||||
default: return null;
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
this.model = new CentroidFormModel(this.a1.attributes, {
|
||||
analyses: analyses,
|
||||
configModel: configModel,
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
analysisSourceOptionsModel: {},
|
||||
parse: true
|
||||
});
|
||||
});
|
||||
|
||||
it('should add analysis schema', function () {
|
||||
expect(this.model.schema).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have attributes set with defaults', function () {
|
||||
expect(this.model.attributes).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'centroid',
|
||||
category: '',
|
||||
weight: '',
|
||||
aggregate: '',
|
||||
aggregation_column: '',
|
||||
aggregation: 'count'
|
||||
});
|
||||
});
|
||||
|
||||
it('should have generated form fields', function () {
|
||||
expect(Object.keys(this.model.schema).length).toBe(4);
|
||||
});
|
||||
|
||||
it('should bind properly', function () {
|
||||
this.model.set('category', 'wadus');
|
||||
expect(this.model.get('category_column')).toBe('wadus');
|
||||
this.model.set('category', '');
|
||||
expect(this.model.get('category_column')).not.toBeDefined();
|
||||
|
||||
this.model.set('weight', 'wadus');
|
||||
expect(this.model.get('weight_column')).toBe('wadus');
|
||||
this.model.set('weight', '');
|
||||
expect(this.model.get('weight_column')).not.toBeDefined();
|
||||
|
||||
this.model.set('aggregate', {operator: 'avg', attribute: 'wadus'});
|
||||
expect(this.model.get('aggregation_column')).toBe('wadus');
|
||||
expect(this.model.get('aggregation')).toBe('avg');
|
||||
});
|
||||
|
||||
describe('type centroid', function () {
|
||||
beforeEach(function () {
|
||||
this.model.set('category', 'wadus');
|
||||
});
|
||||
|
||||
it('should have type centroid', function () {
|
||||
expect(this.model.get('type')).toBe('centroid');
|
||||
});
|
||||
|
||||
it('should not include weight_column', function () {
|
||||
expect(this.model.get('weight_column')).not.toBeDefined();
|
||||
});
|
||||
|
||||
it('should have proper attributes', function () {
|
||||
expect(this.model.attributes).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'centroid',
|
||||
category: 'wadus',
|
||||
category_column: 'wadus',
|
||||
weight: '',
|
||||
aggregate: '',
|
||||
aggregation_column: '',
|
||||
aggregation: 'count'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('type weighted-centroid', function () {
|
||||
beforeEach(function () {
|
||||
this.model.set('weight', 'wadus');
|
||||
});
|
||||
|
||||
it('should change type to weighted-centroid', function () {
|
||||
expect(this.model.get('type')).toBe('weighted-centroid');
|
||||
});
|
||||
|
||||
it('should include weight_column', function () {
|
||||
expect(this.model.get('weight_column')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have proper attributes', function () {
|
||||
expect(this.model.attributes).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'weighted-centroid',
|
||||
category: '',
|
||||
weight: 'wadus',
|
||||
weight_column: 'wadus',
|
||||
aggregate: '',
|
||||
aggregation_column: '',
|
||||
aggregation: 'count'
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
+299
@@ -0,0 +1,299 @@
|
||||
var Backbone = require('backbone');
|
||||
var cdb = require('internal-carto.js');
|
||||
var Model = require('builder/editor/layers/layer-content-views/analyses/analysis-form-models/closest-form-model');
|
||||
var BaseAnalysisFormModel = require('builder/editor/layers/layer-content-views/analyses/analysis-form-models/base-analysis-form-model');
|
||||
var analyses = require('builder/data/analyses');
|
||||
var DialogConstants = require('builder/components/form-components/_constants/_dialogs');
|
||||
|
||||
describe('editor/layers/layer-content-views/analyses/analysis-form-models/closest-form-model', function () {
|
||||
var cdbSQLBackup;
|
||||
|
||||
beforeEach(function () {
|
||||
this.configModel = new Backbone.Model({});
|
||||
|
||||
this.layerDefinitionModel = new Backbone.Model({});
|
||||
this.layerDefinitionModel.getName = function () { return 'Layer Name'; };
|
||||
this.layerDefinitionModel.getTableName = function () { return 'table_name'; };
|
||||
this.layerDefinitionModel.findAnalysisDefinitionNodeModel = function () {
|
||||
var node = {
|
||||
id: 808,
|
||||
letter: function () { return 'A'; },
|
||||
getColor: function () { return '#fabada'; },
|
||||
isSourceType: function () { return true; },
|
||||
querySchemaModel: new Backbone.Model({
|
||||
query: 'SELECT * FROM somewhere;'
|
||||
})
|
||||
};
|
||||
return node;
|
||||
};
|
||||
this.layerDefinitionModel.getName = function () { return 'Metro Madrid'; };
|
||||
|
||||
this.analysisSourceOptionsModel = new Backbone.Model({
|
||||
fetching: false
|
||||
});
|
||||
this.analysisSourceOptionsModel.getSelectOptions = function () {
|
||||
return [];
|
||||
};
|
||||
this.analysisSourceOptionsModel.createSourceNodeUnlessExisting = function () {};
|
||||
|
||||
cdbSQLBackup = cdb.SQL;
|
||||
cdb.SQL = function () {
|
||||
return {
|
||||
execute: function () {}
|
||||
};
|
||||
};
|
||||
|
||||
this.initializeOptions = {
|
||||
analyses: analyses,
|
||||
configModel: this.configModel,
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
analysisSourceOptionsModel: this.analysisSourceOptionsModel
|
||||
};
|
||||
|
||||
this.model = new Model(null, this.initializeOptions);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
cdb.SQL = cdbSQLBackup;
|
||||
});
|
||||
|
||||
describe('parse', function () {
|
||||
it('should set default value to responses property', function () {
|
||||
var parsed = this.model.parse({});
|
||||
|
||||
expect(parsed.responses).toBe(1);
|
||||
});
|
||||
|
||||
it('should not modify responses propertyif present', function () {
|
||||
var parsed = this.model.parse({responses: 32});
|
||||
|
||||
expect(parsed.responses).toBe(32);
|
||||
});
|
||||
});
|
||||
|
||||
describe('initialize', function () {
|
||||
it('should call proper function and hook events', function () {
|
||||
spyOn(BaseAnalysisFormModel.prototype, 'initialize');
|
||||
spyOn(this.model, '_resetColumnOptions');
|
||||
spyOn(this.model, '_initBinds');
|
||||
spyOn(this.model, '_setSchema');
|
||||
spyOn(this.model, '_fetchColumns');
|
||||
|
||||
this.model.initialize(null, this.initializeOptions);
|
||||
|
||||
expect(BaseAnalysisFormModel.prototype.initialize).toHaveBeenCalled();
|
||||
expect(this.model._resetColumnOptions).toHaveBeenCalled();
|
||||
expect(this.model._initBinds).toHaveBeenCalled();
|
||||
expect(this.model._setSchema).toHaveBeenCalled();
|
||||
expect(this.model._fetchColumns).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('_initBinds', function () {
|
||||
it('should hook up change:fetching and change:target events', function () {
|
||||
spyOn(this.model, '_onSourceOptionsFetched');
|
||||
|
||||
this.model._initBinds();
|
||||
this.model._analysisSourceOptionsModel.set('fetching', true);
|
||||
|
||||
expect(this.model._events['change:target']).toBeDefined();
|
||||
expect(this.model._onSourceOptionsFetched).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('.getTemplate', function () {
|
||||
it('should return the template', function () {
|
||||
var template = this.model.getTemplate();
|
||||
|
||||
var templateContent = template({linkContent: '', hasTarget: false});
|
||||
expect(templateContent.indexOf('<form>')).toBeGreaterThan(-1);
|
||||
expect(templateContent.indexOf('data-fields="source,target"')).toBeGreaterThan(-1);
|
||||
expect(templateContent.indexOf('data-fields="responses,category"')).toBeGreaterThan(-1);
|
||||
});
|
||||
|
||||
it('should disable 2nd step fields if hasTarget false', function () {
|
||||
var template = this.model.getTemplate();
|
||||
var templateContent = template({linkContent: '', hasTarget: false});
|
||||
expect(templateContent.indexOf('Editor-HeaderInfo is-disabled')).toBeGreaterThan(-1);
|
||||
});
|
||||
|
||||
it('should not disable 2nd step fields if hasTarget true', function () {
|
||||
var template = this.model.getTemplate();
|
||||
var templateContent = template({linkContent: '', hasTarget: true});
|
||||
expect(templateContent.indexOf('Editor-HeaderInfo is-disabled')).toBe(-1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('.getTemplateData', function () {
|
||||
it('should return an object with hasTarget', function () {
|
||||
var templateData = this.model.getTemplateData();
|
||||
|
||||
expect(templateData).toEqual({ hasTarget: false });
|
||||
});
|
||||
});
|
||||
|
||||
describe('._resetColumnOptions', function () {
|
||||
it('should reset _columnOptions and hook up "columnsFetched" event', function () {
|
||||
this.model._columnOptions.set('aProperty', 'some value');
|
||||
spyOn(this.model, '_onColumnsFetched');
|
||||
|
||||
this.model._resetColumnOptions();
|
||||
this.model._columnOptions.trigger('columnsFetched');
|
||||
|
||||
expect(this.model._columnOptions.aProperty).not.toBeDefined();
|
||||
expect(this.model._onColumnsFetched).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._onSourceOptionsFetched', function () {
|
||||
it('should call _setSchema', function () {
|
||||
spyOn(this.model, '_setSchema');
|
||||
|
||||
this.model._onSourceOptionsFetched();
|
||||
|
||||
expect(this.model._setSchema).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._setSchema', function () {
|
||||
it('should call _setSchema of BaseAnalysisFormModel with proper schema', function () {
|
||||
spyOn(BaseAnalysisFormModel.prototype, '_setSchema');
|
||||
spyOn(this.model, '_primarySourceSchemaItem').and.callFake(function (title) {
|
||||
return title;
|
||||
});
|
||||
spyOn(this.model, '_getSourceOptionsForSource').and.callFake(function (sourceAttrName) {
|
||||
return sourceAttrName;
|
||||
});
|
||||
spyOn(this.model, '_isSourceDisabled').and.callFake(function (source) {
|
||||
return 'called with ' + source;
|
||||
});
|
||||
spyOn(this.model._columnOptions, 'filterByType').and.callFake(function (type) { return type; });
|
||||
|
||||
var expectedSchema = {
|
||||
source: 'editor.layers.analysis-form.base-layer',
|
||||
target: {
|
||||
type: 'NodeDataset',
|
||||
title: 'editor.layers.analysis-form.target',
|
||||
options: {sourceAttrName: 'target', includeSourceNode: true},
|
||||
dialogMode: DialogConstants.Mode.FLOAT,
|
||||
validators: ['required'],
|
||||
editorAttrs: {
|
||||
disabled: 'called with target'
|
||||
}
|
||||
},
|
||||
category: {
|
||||
type: 'EnablerEditor',
|
||||
title: '',
|
||||
label: 'editor.layers.analysis-form.group-by',
|
||||
help: 'editor.layers.analysis-form.find-nearest.categorized-help',
|
||||
editor: {
|
||||
type: 'Select',
|
||||
options: 'string',
|
||||
dialogMode: DialogConstants.Mode.FLOAT,
|
||||
editorAttrs: {
|
||||
showLabel: false
|
||||
}
|
||||
}
|
||||
},
|
||||
responses: {
|
||||
type: 'Number',
|
||||
title: 'editor.layers.analysis-form.find-nearest.max-results',
|
||||
value: 1,
|
||||
validators: ['required', {
|
||||
type: 'interval',
|
||||
min: 1,
|
||||
max: 32
|
||||
}]
|
||||
}
|
||||
};
|
||||
|
||||
this.model._setSchema();
|
||||
|
||||
expect(BaseAnalysisFormModel.prototype._setSchema).toHaveBeenCalledWith(expectedSchema);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._isSourceDisabled', function () {
|
||||
it('should return if attr is primary source or it is fetching, combination 1', function () {
|
||||
spyOn(this.model, '_isPrimarySource').and.returnValue(true);
|
||||
spyOn(this.model, '_isFetchingOptions').and.returnValue(false);
|
||||
|
||||
var result = this.model._isSourceDisabled('target');
|
||||
|
||||
expect(this.model._isPrimarySource).toHaveBeenCalledWith('target');
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return if attr is primary source or it is fetching, combination 2', function () {
|
||||
spyOn(this.model, '_isPrimarySource').and.returnValue(false);
|
||||
spyOn(this.model, '_isFetchingOptions').and.returnValue(true);
|
||||
|
||||
var result = this.model._isSourceDisabled('target');
|
||||
|
||||
expect(this.model._isFetchingOptions).toHaveBeenCalled();
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return if attr is primary source or it is fetching, combination 3', function () {
|
||||
spyOn(this.model, '_isPrimarySource').and.returnValue(false);
|
||||
spyOn(this.model, '_isFetchingOptions').and.returnValue(false);
|
||||
|
||||
var result = this.model._isSourceDisabled('target');
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._onChangeTarget', function () {
|
||||
it('should reset category and fetch columnds', function () {
|
||||
this.model.set('target', 'madrid');
|
||||
spyOn(this.model, '_setSchema');
|
||||
spyOn(this.model, '_fetchColumns');
|
||||
spyOn(this.model._analysisSourceOptionsModel, 'createSourceNodeUnlessExisting');
|
||||
|
||||
this.model._onChangeTarget();
|
||||
|
||||
expect(this.model.get('category')).toEqual('');
|
||||
expect(this.model._setSchema).toHaveBeenCalled();
|
||||
expect(this.model._analysisSourceOptionsModel.createSourceNodeUnlessExisting).toHaveBeenCalledWith('madrid');
|
||||
expect(this.model._fetchColumns).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._fetchColumns', function () {
|
||||
beforeEach(function () {
|
||||
this.model.set('target', 'a layer');
|
||||
this.layerDefinitionModel.findAnalysisDefinitionNodeModel = function () {};
|
||||
});
|
||||
|
||||
it('should set node in columnOptions if target is a node', function () {
|
||||
spyOn(this.model._layerDefinitionModel, 'findAnalysisDefinitionNodeModel').and.returnValue('node');
|
||||
spyOn(this.model._columnOptions, 'setNode');
|
||||
|
||||
this.model._fetchColumns();
|
||||
|
||||
expect(this.model._layerDefinitionModel.findAnalysisDefinitionNodeModel).toHaveBeenCalledWith('a layer');
|
||||
expect(this.model._columnOptions.setNode).toHaveBeenCalledWith('node');
|
||||
});
|
||||
|
||||
it('should set target dataset in columnOptions if target is a dataset', function () {
|
||||
spyOn(this.model._layerDefinitionModel, 'findAnalysisDefinitionNodeModel').and.returnValue(null);
|
||||
spyOn(this.model._columnOptions, 'setDataset');
|
||||
|
||||
this.model._fetchColumns();
|
||||
|
||||
expect(this.model._layerDefinitionModel.findAnalysisDefinitionNodeModel).toHaveBeenCalledWith('a layer');
|
||||
expect(this.model._columnOptions.setDataset).toHaveBeenCalledWith('a layer');
|
||||
});
|
||||
});
|
||||
|
||||
describe('._onColumnsFetched', function () {
|
||||
it('should call _setSchema', function () {
|
||||
spyOn(this.model, '_setSchema');
|
||||
|
||||
this.model._onColumnsFetched();
|
||||
|
||||
expect(this.model._setSchema).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
var Backbone = require('backbone');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var AnalysisDefinitionNodeSourceModel = require('builder/data/analysis-definition-node-source-model');
|
||||
var AnalysisDefinitionNodeModel = require('builder/data/analysis-definition-node-model');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var DataObservatoryMeasureModel = require('builder/editor/layers/layer-content-views/analyses/analysis-form-models/data-observatory-measure-model');
|
||||
var analyses = require('builder/data/analyses');
|
||||
|
||||
describe('editor/layers/layer-content-views/analyses/analysis-form-models/data-observatory-measure-model', function () {
|
||||
beforeEach(function () {
|
||||
var configModel = new ConfigModel({
|
||||
base_url: '/u/pepe',
|
||||
api_key: 'wadus',
|
||||
user_name: 'pepe'
|
||||
});
|
||||
|
||||
this.analysisDefinitionNodesCollection = new Backbone.Collection();
|
||||
|
||||
this.a0 = new AnalysisDefinitionNodeSourceModel({
|
||||
id: 'a0',
|
||||
type: 'concave-hull',
|
||||
query: 'SELECT * FROM a_table'
|
||||
}, {
|
||||
configModel: configModel,
|
||||
userModel: {},
|
||||
collection: this.analysisDefinitionNodesCollection
|
||||
});
|
||||
|
||||
spyOn(this.a0, 'isValidAsInputForType').and.returnValue(true);
|
||||
|
||||
this.a1 = new AnalysisDefinitionNodeModel({
|
||||
id: 'a1',
|
||||
type: 'data-observatory-measure',
|
||||
final_column: 'my column!!!',
|
||||
source: 'a0'
|
||||
}, {
|
||||
configModel: configModel,
|
||||
collection: this.analysisDefinitionNodesCollection
|
||||
});
|
||||
|
||||
this.layerDefinitionModel = new LayerDefinitionModel(null, {
|
||||
configModel: configModel
|
||||
});
|
||||
|
||||
spyOn(this.layerDefinitionModel, 'findAnalysisDefinitionNodeModel').and.callFake(function (id) {
|
||||
switch (id) {
|
||||
case 'a0': return this.a0;
|
||||
case 'a1': return this.a1;
|
||||
default: return null;
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
this.model = new DataObservatoryMeasureModel(this.a1.attributes, {
|
||||
analyses: analyses,
|
||||
configModel: configModel,
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
analysisSourceOptionsModel: {},
|
||||
parse: true
|
||||
});
|
||||
});
|
||||
|
||||
it('should add analysis schema', function () {
|
||||
expect(this.model.schema).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have generated form fields', function () {
|
||||
expect(Object.keys(this.model.schema).length).toBe(5);
|
||||
});
|
||||
|
||||
it('should format the attributes', function () {
|
||||
expect(this.model._formatAttrs(this.model.attributes)).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
final_column: 'my_column',
|
||||
type: 'data-observatory-measure',
|
||||
segment_name: null
|
||||
});
|
||||
});
|
||||
});
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
var Backbone = require('backbone');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var AnalysisDefinitionNodeModel = require('builder/data/analysis-definition-node-model');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var DataObservatoryMeasureModel = require('builder/editor/layers/layer-content-views/analyses/analysis-form-models/data-observatory-multiple-measure-model');
|
||||
var analyses = require('builder/data/analyses');
|
||||
|
||||
describe('editor/layers/layer-content-views/analyses/analysis-form-models/data-observatory-multiple-measure-model', function () {
|
||||
beforeEach(function () {
|
||||
var configModel = new ConfigModel({
|
||||
base_url: '/u/pepe',
|
||||
api_key: 'wadus',
|
||||
user_name: 'pepe'
|
||||
});
|
||||
|
||||
this.querySchemaModel = new Backbone.Model({
|
||||
query: 'select * from wadus'
|
||||
});
|
||||
|
||||
this.analysisDefinitionNodesCollection = new Backbone.Collection();
|
||||
|
||||
this.a0 = new AnalysisDefinitionNodeModel({
|
||||
id: 'a1',
|
||||
type: 'data-observatory-multiple-measures',
|
||||
area: 'calafornia',
|
||||
numerators: ['foo', 'bar'],
|
||||
column_names: ['final_column', 'otra'],
|
||||
denominators: [null, null],
|
||||
numerator_timespans: [null, null],
|
||||
geom_ids: ['wadus', null],
|
||||
normalizations: ['area', 'area'],
|
||||
source: 'a0'
|
||||
}, {
|
||||
configModel: configModel,
|
||||
collection: this.analysisDefinitionNodesCollection
|
||||
});
|
||||
|
||||
this.a0.querySchemaModel = this.querySchemaModel;
|
||||
this.a0.queryGeometryModel = new Backbone.Model();
|
||||
this.a0.queryGeometryModel.isFetched = function () { return true; };
|
||||
|
||||
this.layerDefinitionModel = new LayerDefinitionModel(null, {
|
||||
configModel: configModel
|
||||
});
|
||||
|
||||
spyOn(this.layerDefinitionModel, 'findAnalysisDefinitionNodeModel').and.returnValue(this.a0);
|
||||
|
||||
this.model = new DataObservatoryMeasureModel(this.a0.attributes, {
|
||||
analyses: analyses,
|
||||
configModel: configModel,
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
analysisSourceOptionsModel: {},
|
||||
parse: true
|
||||
});
|
||||
});
|
||||
|
||||
it('should add analysis schema', function () {
|
||||
expect(this.model.schema).toBeDefined();
|
||||
});
|
||||
|
||||
it('filter Global region', function () {
|
||||
spyOn(this.model.regions.SQL, 'execute').and.callFake(function (query, vars, params) {
|
||||
params && params.success({
|
||||
rows: [
|
||||
{
|
||||
num_measurements: 2,
|
||||
region_id: 'section/tags.global',
|
||||
region_name: '"Global"'
|
||||
}, {
|
||||
num_measurements: 634,
|
||||
region_id: 'section/tags.united_states',
|
||||
region_name: '"United States"'
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
this.model.regions.fetch();
|
||||
expect(this.model.regions.length).toBe(2);
|
||||
|
||||
var options = this.model._getRegionOptions();
|
||||
expect(options.length).toBe(1);
|
||||
expect(options[0].get('label')).toBe('United States');
|
||||
});
|
||||
|
||||
it('should generate schema properly', function () {
|
||||
expect(Object.keys(this.model.schema).length).toBe(3);
|
||||
expect(this.model.schema.measurements).toBeDefined();
|
||||
});
|
||||
|
||||
it('should generate measurements fields properly when region is selected', function () {
|
||||
this.model.set('area', null);
|
||||
var fields = this.model.getTemplateData().fields;
|
||||
expect(fields).toBe('');
|
||||
|
||||
this.model.set('area', 'wadus');
|
||||
fields = this.model.getTemplateData().fields;
|
||||
expect(fields).toBe('measurements');
|
||||
});
|
||||
|
||||
it('should format the attributes', function () {
|
||||
expect(this.model._formatAttrs(this.model.attributes)).toEqual({
|
||||
id: 'a1',
|
||||
type: 'data-observatory-multiple-measures',
|
||||
source: 'a0',
|
||||
area: 'calafornia',
|
||||
numerators: ['foo', 'bar'],
|
||||
column_names: ['final_column', 'otra'],
|
||||
denominators: [null, null],
|
||||
normalizations: ['area', 'area'],
|
||||
numerator_timespans: [null, null],
|
||||
geom_ids: ['wadus', null]
|
||||
});
|
||||
});
|
||||
|
||||
describe('validate', function () {
|
||||
it('no values should not return error', function () {
|
||||
spyOn(this.model, '_getNumerators').and.returnValue([]);
|
||||
spyOn(this.model, '_getColumnNames').and.returnValue([]);
|
||||
expect(this.model.validate()).toBeUndefined();
|
||||
});
|
||||
|
||||
it('empty values should return error', function () {
|
||||
spyOn(this.model, '_getNumerators').and.returnValue(['']);
|
||||
spyOn(this.model, '_getColumnNames').and.returnValue(['']);
|
||||
expect(this.model.validate()).not.toBeUndefined();
|
||||
});
|
||||
|
||||
it('mismatch length should return error', function () {
|
||||
spyOn(this.model, '_getNumerators').and.returnValue(['foo', 'bar']);
|
||||
spyOn(this.model, '_getColumnNames').and.returnValue(['wadus']);
|
||||
expect(this.model.validate()).not.toBeUndefined();
|
||||
});
|
||||
|
||||
it('right values should not return error', function () {
|
||||
spyOn(this.model, '_getNumerators').and.returnValue(['foo']);
|
||||
spyOn(this.model, '_getColumnNames').and.returnValue(['wadus']);
|
||||
expect(this.model.validate()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
var Backbone = require('backbone');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var AnalysisDefinitionNodeModel = require('builder/data/analysis-definition-node-model');
|
||||
var DataObservatoryMeasureModel = require('builder/editor/layers/layer-content-views/analyses/analysis-form-models/data-observatory-multiple-nested-measure-model');
|
||||
var RegionsCollection = require('builder/data/data-observatory/regions-collection');
|
||||
|
||||
describe('editor/layers/layer-content-views/analyses/analysis-form-models/data-observatory-multiple-nested-measure-model', function () {
|
||||
beforeEach(function () {
|
||||
var configModel = new ConfigModel({
|
||||
base_url: '/u/pepe',
|
||||
api_key: 'wadus',
|
||||
user_name: 'pepe'
|
||||
});
|
||||
|
||||
this.querySchemaModel = new Backbone.Model({
|
||||
query: 'select * from wadus'
|
||||
});
|
||||
|
||||
this.nodeDefModel = new AnalysisDefinitionNodeModel({
|
||||
id: 'a1',
|
||||
type: 'data-observatory-multiple-measures',
|
||||
numerators: ['foo', 'bar'],
|
||||
column_names: ['final_column', 'otra'],
|
||||
denominators: [null, null],
|
||||
numerator_timespans: [null, null],
|
||||
geom_ids: ['wadus', null],
|
||||
normalizations: ['area', 'area'],
|
||||
source: 'a0'
|
||||
}, {
|
||||
configModel: configModel,
|
||||
collection: this.analysisDefinitionNodesCollection
|
||||
});
|
||||
|
||||
this.nodeDefModel.queryGeometryModel = new Backbone.Model();
|
||||
this.nodeDefModel.queryGeometryModel.isFetched = function () { return true; };
|
||||
|
||||
this.regions = new RegionsCollection([], {
|
||||
configModel: configModel,
|
||||
nodeDefModel: this.nodeDefModel
|
||||
});
|
||||
|
||||
spyOn(this.regions.SQL, 'execute').and.callFake(function (query, vars, params) {
|
||||
var payload = {
|
||||
rows: [
|
||||
{
|
||||
num_measurements: 2,
|
||||
region_id: 'section/tags.global',
|
||||
region_name: '"Global"'
|
||||
}, {
|
||||
num_measurements: 634,
|
||||
region_id: 'section/tags.united_states',
|
||||
region_name: '"United States"'
|
||||
}
|
||||
]
|
||||
};
|
||||
params && params.success(payload);
|
||||
});
|
||||
|
||||
this.regions.fetch();
|
||||
|
||||
this.form = new Backbone.View();
|
||||
this.form.commit = jasmine.createSpy('commit');
|
||||
this.form.model = new Backbone.Model({
|
||||
area: 'Wadus'
|
||||
});
|
||||
|
||||
this.model = new DataObservatoryMeasureModel(this.nodeDefModel.attributes, {
|
||||
configModel: configModel,
|
||||
nodeDefModel: this.nodeDefModel,
|
||||
regions: this.regions,
|
||||
form: this.form
|
||||
});
|
||||
|
||||
spyOn(this.model.measurement.SQL, 'execute').and.callFake(function (query, vars, params) {
|
||||
var payload = {
|
||||
rows: [
|
||||
{
|
||||
numer_id: 'us.zillow.AllHomes_Zhvi',
|
||||
numer_name: 'Zillow Home Value Index for All homes',
|
||||
numer_tags: '{"subsection/tags.housing": "Housing", "license/us.zillow.zillow-license": "Zillow Terms of Use for Aggregate Data", "subsection/us.census.acs.segments": "US Population Segments"}'
|
||||
},
|
||||
{
|
||||
numer_id: 'us.census.acs.B19083001',
|
||||
numer_name: 'Gini Index',
|
||||
numer_tags: '{"subsection/tags.age_gender": "Age and Gender", "license/tags.norestrictions": "Unrestricted"}'
|
||||
},
|
||||
{
|
||||
numer_id: 'us.census.acs.B01001002',
|
||||
numer_name: 'Male Population',
|
||||
numer_tags: '{"subsection/tags.age_gender": "Age and Gender", "license/tags.norestrictions": "Unrestricted"}'
|
||||
}
|
||||
]
|
||||
};
|
||||
params && params.success(payload);
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate schema', function () {
|
||||
expect(this.model.schema).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have generated form fields', function () {
|
||||
expect(Object.keys(this.model.schema).length).toBe(6);
|
||||
});
|
||||
|
||||
describe('suggested column', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(this.model.columnName.SQL, 'execute').and.callFake(function (query, vars, params) {
|
||||
params && params.success({
|
||||
rows: [
|
||||
{
|
||||
obs_getmeta: [
|
||||
{
|
||||
suggested_name: 'wadus_2010_2014'
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
spyOn(this.model, '_getColumnNameOptions').and.returnValue({
|
||||
success: this.model._columnNameSuccess,
|
||||
numer_id: 'wadus'
|
||||
});
|
||||
});
|
||||
|
||||
it('should update suggested column', function () {
|
||||
this.model._fetchNewColumn();
|
||||
|
||||
expect(this.model.get('column_name')).toBe('wadus_2010_2014');
|
||||
});
|
||||
});
|
||||
});
|
||||
+396
@@ -0,0 +1,396 @@
|
||||
var Backbone = require('backbone');
|
||||
var cdb = require('internal-carto.js');
|
||||
var FormModel = require('builder/editor/layers/layer-content-views/analyses/analysis-form-models/deprecated-sql-function-form-model');
|
||||
var BaseAnalysisFormModel = require('builder/editor/layers/layer-content-views/analyses/analysis-form-models/base-analysis-form-model');
|
||||
var analyses = require('builder/data/analyses');
|
||||
var availableFunctionsQuery = require('builder/editor/layers/layer-content-views/analyses/analysis-form-models/available-functions.tpl');
|
||||
|
||||
describe('editor/layers/layer-content-views/analyses/analysis-form-models/deprecated-sql-function-form-model', function () {
|
||||
var cdbSQLBackup;
|
||||
|
||||
beforeEach(function () {
|
||||
this.configModel = new Backbone.Model({});
|
||||
|
||||
cdbSQLBackup = cdb.SQL;
|
||||
cdb.SQL = function () {
|
||||
return {
|
||||
execute: function () {}
|
||||
};
|
||||
};
|
||||
|
||||
this.layerDefinitionModel = new Backbone.Model({});
|
||||
this.layerDefinitionModel.getName = function () { return 'Layer Name'; };
|
||||
this.layerDefinitionModel.getTableName = function () { return 'table_name'; };
|
||||
this.layerDefinitionModel.findAnalysisDefinitionNodeModel = function () {
|
||||
var node = {
|
||||
id: 808,
|
||||
letter: function () { return 'A'; },
|
||||
getColor: function () { return '#fabada'; },
|
||||
isSourceType: function () { return true; },
|
||||
querySchemaModel: new Backbone.Model({
|
||||
query: 'SELECT * FROM somewhere;'
|
||||
})
|
||||
};
|
||||
return node;
|
||||
};
|
||||
this.layerDefinitionModel.getName = function () { return 'Metro Madrid'; };
|
||||
|
||||
this.analysisSourceOptionsModel = new Backbone.Model({
|
||||
fetching: false
|
||||
});
|
||||
|
||||
this.initializeOptions = {
|
||||
analyses: analyses,
|
||||
configModel: this.configModel,
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
analysisSourceOptionsModel: this.analysisSourceOptionsModel
|
||||
};
|
||||
|
||||
this.formModel = new FormModel(null, this.initializeOptions);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
cdb.SQL = cdbSQLBackup;
|
||||
});
|
||||
|
||||
describe('initialize', function () {
|
||||
it('should call proper function and hook events', function () {
|
||||
spyOn(BaseAnalysisFormModel.prototype, 'initialize');
|
||||
spyOn(this.formModel, '_setSchema');
|
||||
spyOn(this.formModel, '_initBinds');
|
||||
spyOn(this.formModel, '_fetchAvailableFunctions');
|
||||
|
||||
this.formModel.initialize(null, this.initializeOptions);
|
||||
|
||||
expect(BaseAnalysisFormModel.prototype.initialize).toHaveBeenCalled();
|
||||
expect(this.formModel._SQL).toBeDefined();
|
||||
expect(this.formModel.get('fetchStatus')).toBe('unfetched');
|
||||
expect(this.formModel._functionsInfo.length).toBe(0);
|
||||
expect(this.formModel._initBinds).toHaveBeenCalled();
|
||||
expect(this.formModel._setSchema).toHaveBeenCalled();
|
||||
expect(this.formModel._fetchAvailableFunctions).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._initBinds', function () {
|
||||
it('should hook up the proper events', function () {
|
||||
expect(this.formModel._events['change:fetchStatus'].length).toBe(1);
|
||||
expect(this.formModel._events['change:function_name'].length).toBe(1);
|
||||
expect(this.formModel._analysisSourceOptionsModel._events['change:fetching'].length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('.validate', function () {
|
||||
it('should serialize function arguments', function () {
|
||||
var attrs = 'the attributes';
|
||||
var options = 'the options';
|
||||
var calls = [];
|
||||
spyOn(this.formModel, '_serializeFunctionArgs').and.callFake(function () {
|
||||
calls.push('serialize');
|
||||
});
|
||||
spyOn(BaseAnalysisFormModel.prototype, 'validate').and.callFake(function () {
|
||||
calls.push('validate');
|
||||
});
|
||||
|
||||
this.formModel.validate(attrs, options);
|
||||
|
||||
expect(calls[0]).toEqual('serialize');
|
||||
expect(calls[1]).toEqual('validate');
|
||||
});
|
||||
});
|
||||
|
||||
describe('.getTemplate', function () {
|
||||
it('should return sql function template', function () {
|
||||
var template = this.formModel.getTemplate()({
|
||||
fields: ['oneField', 'otherField']
|
||||
});
|
||||
|
||||
expect(template).toContain('data-fields="oneField,otherField"');
|
||||
expect(template).toContain('editor.layers.analysis-form.deprecated-sql-function.title');
|
||||
});
|
||||
});
|
||||
|
||||
describe('.getTemplateData', function () {
|
||||
it('should return proper fields of the selected function', function () {
|
||||
var selectedFunction = 'dep_ext_fakeFunk';
|
||||
this.formModel._functionsInfo = [{
|
||||
functionName: selectedFunction,
|
||||
hasSecondaryNode: true,
|
||||
params: {
|
||||
radius: 'number',
|
||||
includeCities: 'boolean'
|
||||
}
|
||||
}];
|
||||
this.formModel.attributes.function_name = selectedFunction;
|
||||
|
||||
var templateData = this.formModel.getTemplateData();
|
||||
|
||||
expect(templateData.fields).toEqual('secondary_source,radius,includeCities');
|
||||
});
|
||||
});
|
||||
|
||||
// _getSelectedFunctions gets tested with this spec as well
|
||||
describe('.setSchema, ._buildSchema and _addParamToSchema', function () {
|
||||
it('should return schema based of selected function params', function () {
|
||||
var selectedFunction = 'dep_ext_fakeFunk';
|
||||
this.formModel.attributes.function_name = selectedFunction;
|
||||
this.formModel._functionsInfo = [{
|
||||
functionName: selectedFunction,
|
||||
hasSecondaryNode: true,
|
||||
params: {
|
||||
radius: 'number',
|
||||
includeCities: 'boolean',
|
||||
category: 'string'
|
||||
}
|
||||
}];
|
||||
spyOn(this.formModel, '_getSourceOptionsForSource').and.returnValue('sourceOptionsForSource');
|
||||
|
||||
var schema = this.formModel._buildSchema();
|
||||
|
||||
// Primary source
|
||||
expect(schema.primary_source).toBeDefined();
|
||||
expect(schema.primary_source.title).toEqual('editor.layers.analysis-form.deprecated-sql-function.input');
|
||||
expect(schema.primary_source.type).toEqual('NodeDataset');
|
||||
|
||||
// Secondary source
|
||||
expect(schema.secondary_source).toBeDefined();
|
||||
expect(schema.secondary_source.options).toEqual('sourceOptionsForSource');
|
||||
expect(schema.secondary_source.title).toEqual('editor.layers.analysis-form.deprecated-sql-function.target');
|
||||
expect(schema.secondary_source.type).toEqual('NodeDataset');
|
||||
|
||||
// Number param
|
||||
expect(schema.radius).toBeDefined();
|
||||
expect(schema.radius.showSlider).toBe(false);
|
||||
expect(schema.radius.title).toBe('radius');
|
||||
expect(schema.radius.type).toBe('Number');
|
||||
expect(schema.radius.validators[0]).toBe('required');
|
||||
expect(this.formModel.get('radius')).toBe(0);
|
||||
|
||||
// Boolean param
|
||||
expect(schema.includeCities).toBeDefined();
|
||||
expect(schema.includeCities.title).toBe('includeCities');
|
||||
expect(schema.includeCities.type).toBe('Radio');
|
||||
expect(schema.includeCities.options[0]).toEqual({val: 'true', label: 'true'});
|
||||
expect(schema.includeCities.options[1]).toEqual({val: 'false', label: 'false'});
|
||||
expect(this.formModel.get('includeCities')).toBe(true);
|
||||
|
||||
// String param
|
||||
expect(schema.category).toBeDefined();
|
||||
expect(schema.category.title).toBe('category');
|
||||
expect(schema.category.type).toBe('Text');
|
||||
expect(schema.category.validators[0]).toBe('required');
|
||||
expect(this.formModel.get('category')).toBe('');
|
||||
});
|
||||
|
||||
it('.setSchema should call base form', function () {
|
||||
spyOn(BaseAnalysisFormModel.prototype, '_setSchema');
|
||||
spyOn(this.formModel, '_buildSchema').and.returnValue('schema built');
|
||||
|
||||
this.formModel._setSchema();
|
||||
|
||||
expect(BaseAnalysisFormModel.prototype._setSchema).toHaveBeenCalledWith('schema built');
|
||||
});
|
||||
});
|
||||
|
||||
describe('_onSourceOptionsFetched', function () {
|
||||
it('should call _setSchema', function () {
|
||||
spyOn(this.formModel, '_setSchema');
|
||||
|
||||
this.formModel._onSourceOptionsFetched();
|
||||
|
||||
expect(this.formModel._setSchema).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('_onFetchStatusChanged', function () {
|
||||
it('should call _setSchema', function () {
|
||||
spyOn(this.formModel, '_setSchema');
|
||||
|
||||
this.formModel._onSourceOptionsFetched();
|
||||
|
||||
expect(this.formModel._setSchema).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('_onFunctionSelected', function () {
|
||||
it('should call _setSchema', function () {
|
||||
spyOn(this.formModel, '_setSchema');
|
||||
|
||||
this.formModel._onSourceOptionsFetched();
|
||||
|
||||
expect(this.formModel._setSchema).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._fetchAvailableFunctions', function () {
|
||||
var SQL;
|
||||
var SQLcallbacks;
|
||||
var queryUsed;
|
||||
var trackBackup;
|
||||
|
||||
beforeEach(function () {
|
||||
SQL = this.formModel._SQL;
|
||||
this.formModel._SQL = {
|
||||
execute: function (query, whatever, callbacks) {
|
||||
queryUsed = query;
|
||||
SQLcallbacks = callbacks;
|
||||
}
|
||||
};
|
||||
trackBackup = window.trackJs;
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
this.formModel._SQL = SQL;
|
||||
SQLcallbacks = null;
|
||||
queryUsed = null;
|
||||
window.trackJs = trackBackup;
|
||||
});
|
||||
|
||||
// This test checks also _parseFunctionsMetadata and _capitalizeFunctionName
|
||||
it('should fill _functionsInfo if successful', function () {
|
||||
var metadata = {
|
||||
rows: [{
|
||||
fn_name: 'dep_ext_funk',
|
||||
has_secondary_node: true,
|
||||
params_names: ['radius', 'category'],
|
||||
params_types: ['number', 'string']
|
||||
}, {
|
||||
fn_name: 'dep_ext_empty',
|
||||
has_secondary_node: false,
|
||||
params_names: [],
|
||||
params_types: []
|
||||
}]
|
||||
};
|
||||
this.formModel._fetchAvailableFunctions();
|
||||
|
||||
expect(this.formModel.get('fetchStatus')).toEqual('fetching');
|
||||
|
||||
SQLcallbacks.success(metadata);
|
||||
|
||||
expect(this.formModel.get('fetchStatus')).toEqual('fetched');
|
||||
expect(this.formModel._functionsInfo.length).toBe(2);
|
||||
var first = this.formModel._functionsInfo[0];
|
||||
var second = this.formModel._functionsInfo[1];
|
||||
expect(first).toEqual({
|
||||
functionName: 'DEP_EXT_funk',
|
||||
hasSecondaryNode: true,
|
||||
params: {
|
||||
category: 'string',
|
||||
radius: 'number'
|
||||
}
|
||||
});
|
||||
expect(second).toEqual({
|
||||
functionName: 'DEP_EXT_empty',
|
||||
hasSecondaryNode: false,
|
||||
params: {}
|
||||
});
|
||||
expect(queryUsed).toEqual(availableFunctionsQuery());
|
||||
});
|
||||
|
||||
it('should track error if error', function () {
|
||||
var errors = {
|
||||
responseJSON: '{anError: {}}'
|
||||
};
|
||||
var errorTracked;
|
||||
window.trackJs = {
|
||||
track: function (error) {
|
||||
errorTracked = error;
|
||||
}
|
||||
};
|
||||
this.formModel._fetchAvailableFunctions();
|
||||
|
||||
expect(this.formModel.get('fetchStatus')).toEqual('fetching');
|
||||
|
||||
SQLcallbacks.error(errors);
|
||||
|
||||
expect(this.formModel.get('fetchStatus')).toEqual('error');
|
||||
expect(errorTracked).toEqual(errors.responseJSON);
|
||||
});
|
||||
});
|
||||
|
||||
describe('_getSelectFunctionAttrs', function () {
|
||||
it('should return correct options for select function dropdown', function () {
|
||||
// Unfetched
|
||||
this.formModel.attributes.fetchStatus = 'unfetched';
|
||||
|
||||
var status = this.formModel._getSelectFunctionAttrs();
|
||||
|
||||
expect(status).toEqual({
|
||||
disabled: true
|
||||
});
|
||||
|
||||
// Fetching
|
||||
this.formModel.attributes.fetchStatus = 'fetching';
|
||||
|
||||
status = this.formModel._getSelectFunctionAttrs();
|
||||
|
||||
expect(status).toEqual({
|
||||
disabled: true,
|
||||
loading: true
|
||||
});
|
||||
|
||||
// Fetched
|
||||
this.formModel.attributes.fetchStatus = 'fetched';
|
||||
|
||||
status = this.formModel._getSelectFunctionAttrs();
|
||||
|
||||
expect(status).toEqual({});
|
||||
|
||||
this.formModel._functionsInfo = ['one', 'two'];
|
||||
|
||||
status = this.formModel._getSelectFunctionAttrs();
|
||||
|
||||
expect(status).toEqual({
|
||||
placeholder: 'editor.layers.analysis-form.deprecated-sql-function.choose-function-small'
|
||||
});
|
||||
|
||||
// Error
|
||||
this.formModel.attributes.fetchStatus = 'error';
|
||||
|
||||
status = this.formModel._getSelectFunctionAttrs();
|
||||
|
||||
expect(status).toEqual({
|
||||
disabled: true,
|
||||
placeholder: 'error'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('_parseFunctionOptions', function () {
|
||||
it('should parse functions info to dropdown values', function () {
|
||||
this.formModel._functionsInfo = [
|
||||
{ functionName: 'dep_ext_funk' },
|
||||
{ functionName: 'dep_ext_soul' }
|
||||
];
|
||||
|
||||
var parsed = this.formModel._parseFunctionOptions();
|
||||
|
||||
expect(parsed.length).toBe(2);
|
||||
expect(parsed).toEqual([
|
||||
{ val: 'dep_ext_funk', label: 'dep_ext_funk' },
|
||||
{ val: 'dep_ext_soul', label: 'dep_ext_soul' }
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._serializeFunctionArgs', function () {
|
||||
it('should set function_args to equal the arguments of the selected function', function () {
|
||||
this.formModel.attributes.function_name = 'dep_ext_funk';
|
||||
this.formModel.attributes.radius = 808;
|
||||
this.formModel.attributes.category = 'place';
|
||||
this.formModel._functionsInfo = [
|
||||
{
|
||||
functionName: 'dep_ext_funk',
|
||||
params: {
|
||||
radius: 'number',
|
||||
category: 'string'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
this.formModel._serializeFunctionArgs();
|
||||
|
||||
expect(this.formModel.get('function_args')).toEqual([808, 'place']);
|
||||
});
|
||||
});
|
||||
});
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
var template = require('builder/editor/layers/layer-content-views/analyses/analysis-form-models/deprecated-sql-function.tpl');
|
||||
|
||||
describe('deprecated-sql-function-form-template', function () {
|
||||
it('should have `primary_source` and `function_name` as data fields', function () {
|
||||
var html = template({fields: []});
|
||||
|
||||
expect(html).toContain('data-fields="primary_source,function_name"');
|
||||
});
|
||||
|
||||
it('should have the given data fields', function () {
|
||||
var html = template({fields: ['secondary_source', 'radius']});
|
||||
|
||||
expect(html).toContain('data-fields="secondary_source,radius"');
|
||||
});
|
||||
});
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
var Backbone = require('backbone');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var AnalysisSourceOptionsModel = require('builder/editor/layers/layer-content-views/analyses/analysis-source-options-model');
|
||||
var FallbackFormModel = require('builder/editor/layers/layer-content-views/analyses/analysis-form-models/fallback-form-model');
|
||||
var analyses = require('builder/data/analyses');
|
||||
|
||||
describe('editor/layers/layer-content-views/analyses/analysis-form-models/fallback-form-model', function () {
|
||||
beforeEach(function () {
|
||||
this.layerDefinitionModel = new LayerDefinitionModel(null, {
|
||||
configModel: {}
|
||||
});
|
||||
spyOn(this.layerDefinitionModel, 'findAnalysisDefinitionNodeModel');
|
||||
});
|
||||
|
||||
describe('when created for a type with a single source', function () {
|
||||
beforeEach(function () {
|
||||
this.analysisSourceOptionsModel = new AnalysisSourceOptionsModel({fetching: true}, {
|
||||
analysisDefinitionNodesCollection: {},
|
||||
layerDefinitionsCollection: {},
|
||||
tablesCollection: {}
|
||||
});
|
||||
|
||||
var attrs = {
|
||||
id: 'a2',
|
||||
type: 'buffer',
|
||||
source: 'a1'
|
||||
};
|
||||
|
||||
this.model = new FallbackFormModel(attrs, {
|
||||
analyses: analyses,
|
||||
configModel: {},
|
||||
userActions: {},
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
analysisSourceOptionsModel: {}
|
||||
});
|
||||
});
|
||||
|
||||
describe('should generate schema from camshaft reference', function () {
|
||||
beforeEach(function () {
|
||||
expect(this.model.schema).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have generated form fields', function () {
|
||||
expect(Object.keys(this.model.schema).length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should not allow to change source', function () {
|
||||
expect(this.model.schema.source.editorAttrs.disabled).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('for a model representing a type which contains params of type array', function () {
|
||||
beforeEach(function () {
|
||||
this.model.set({
|
||||
type: 'filter-category',
|
||||
source: 'a1',
|
||||
column: 'col'
|
||||
});
|
||||
});
|
||||
|
||||
describe('when updated', function () {
|
||||
beforeEach(function () {
|
||||
this.nodeDefModel = new Backbone.Model();
|
||||
expect(this.model.isValid()).toBe(true, 'the model should be valid to be saved');
|
||||
});
|
||||
|
||||
it('should split array params for values', function () {
|
||||
this.model.set('accept', 'a, b | b, c, d, e, and f');
|
||||
this.model.updateNodeDefinition(this.nodeDefModel);
|
||||
|
||||
expect(this.nodeDefModel.get('accept')).toEqual(jasmine.any(Array));
|
||||
expect(this.nodeDefModel.get('accept')).toEqual(['a', 'b | b', 'c', 'd', 'e', 'and f']);
|
||||
});
|
||||
|
||||
it('should set a null value if there is no value', function () {
|
||||
this.model.set('accept', '');
|
||||
this.model.updateNodeDefinition(this.nodeDefModel);
|
||||
|
||||
expect(this.nodeDefModel.get('accept')).toBe(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when creatd for a type with multple sources', function () {
|
||||
beforeEach(function () {
|
||||
this.analysisSourceOptionsModel = new AnalysisSourceOptionsModel({fetching: true}, {
|
||||
analysisDefinitionNodesCollection: {},
|
||||
layerDefinitionsCollection: {},
|
||||
tablesCollection: {}
|
||||
});
|
||||
|
||||
var attrs = {
|
||||
id: 'b2',
|
||||
type: 'aggregate-intersection'
|
||||
};
|
||||
|
||||
this.model = new FallbackFormModel(attrs, {
|
||||
analyses: analyses,
|
||||
configModel: {},
|
||||
userActions: {},
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
analysisSourceOptionsModel: this.analysisSourceOptionsModel
|
||||
});
|
||||
});
|
||||
|
||||
describe('when source options is fetched', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(this.analysisSourceOptionsModel, 'getSelectOptions').and.returnValue([
|
||||
{val: 'b1', label: 'b1'},
|
||||
{val: 'c2', label: 'c2'}
|
||||
]);
|
||||
|
||||
this.analysisSourceOptionsModel.set('fetching', false);
|
||||
});
|
||||
|
||||
it('should update schema', function () {
|
||||
expect(this.analysisSourceOptionsModel.getSelectOptions).toHaveBeenCalled();
|
||||
expect(this.model.schema).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var AnalysisDefinitionNodeSourceModel = require('builder/data/analysis-definition-node-source-model');
|
||||
var AnalysisDefinitionNodeModel = require('builder/data/analysis-definition-node-model');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var FilterFormModel = require('builder/editor/layers/layer-content-views/analyses/analysis-form-models/filter-by-node-column');
|
||||
var AnalysisSourceOptionsModel = require('builder/editor/layers/layer-content-views/analyses/analysis-source-options-model');
|
||||
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
|
||||
var analyses = require('builder/data/analyses');
|
||||
|
||||
describe('editor/layers/layer-content-views/analyses/analysis-form-models/filter-by-node-column', function () {
|
||||
var createModelFn = function (attributes, options) {
|
||||
var defaultOptions = {
|
||||
analyses: analyses,
|
||||
configModel: this.configModel,
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
analysisSourceOptionsModel: this.analysisSourceOptionsModel
|
||||
};
|
||||
|
||||
this.model = new FilterFormModel(_.extend({}, this.a1.attributes, attributes), _.extend({}, defaultOptions, options));
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
jasmine.Ajax.install();
|
||||
|
||||
jasmine.Ajax.stubRequest(new RegExp('.*api/v2/sql.*'))
|
||||
.andReturn({ status: 200 });
|
||||
|
||||
this.configModel = new ConfigModel({
|
||||
base_url: '/u/pepe',
|
||||
api_key: 'wadus',
|
||||
user_name: 'pepe'
|
||||
});
|
||||
|
||||
var userModel = new UserModel({}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
this.analysisDefinitionNodesCollection = new Backbone.Collection();
|
||||
|
||||
this.a0 = new AnalysisDefinitionNodeSourceModel({
|
||||
id: 'a0',
|
||||
type: 'filter-by-node-column',
|
||||
query: 'SELECT * FROM a_table'
|
||||
}, {
|
||||
configModel: this.configModel,
|
||||
userModel: {},
|
||||
collection: this.analysisDefinitionNodesCollection
|
||||
});
|
||||
|
||||
spyOn(this.a0, 'isValidAsInputForType').and.returnValue(true);
|
||||
|
||||
this.a1 = new AnalysisDefinitionNodeModel({
|
||||
id: 'a1',
|
||||
type: 'filter-by-node-column',
|
||||
source: 'a0'
|
||||
}, {
|
||||
configModel: this.configModel,
|
||||
collection: this.analysisDefinitionNodesCollection
|
||||
});
|
||||
|
||||
this.layerDefinitionModel = new LayerDefinitionModel(null, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
spyOn(this.layerDefinitionModel, 'findAnalysisDefinitionNodeModel').and.callFake(function (id) {
|
||||
switch (id) {
|
||||
case 'a0': return this.a0;
|
||||
case 'a1': return this.a1;
|
||||
default: return null;
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
this.layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
|
||||
configModel: this.configModel,
|
||||
userModel: userModel,
|
||||
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
|
||||
mapId: 'map-123',
|
||||
stateDefinitionModel: {}
|
||||
});
|
||||
|
||||
this.analysisSourceOptionsModel = new AnalysisSourceOptionsModel(null, {
|
||||
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
|
||||
layerDefinitionsCollection: this.layerDefinitionsCollection,
|
||||
tablesCollection: new Backbone.Collection()
|
||||
});
|
||||
|
||||
this.analysisSourceOptionsModel.getSelectOptions = function () {
|
||||
return this;
|
||||
};
|
||||
|
||||
this.analysisSourceOptionsModel.find = function () {
|
||||
return this;
|
||||
};
|
||||
|
||||
this.analysisSourceOptionsModel.filter = function () {
|
||||
return [];
|
||||
};
|
||||
|
||||
this.createModel = createModelFn.bind(this);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
jasmine.Ajax.uninstall();
|
||||
});
|
||||
|
||||
it('should add analysis schema', function () {
|
||||
this.createModel();
|
||||
|
||||
expect(this.model.schema).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have attributes set with defaults', function () {
|
||||
this.createModel();
|
||||
|
||||
expect(this.model.attributes).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'filter-by-node-column'
|
||||
});
|
||||
});
|
||||
|
||||
it('should have generated form fields', function () {
|
||||
this.createModel();
|
||||
|
||||
expect(Object.keys(this.model.schema).length).toBe(4);
|
||||
});
|
||||
|
||||
describe('template', function () {
|
||||
beforeEach(function () {
|
||||
this.createModel();
|
||||
});
|
||||
|
||||
describe('should return proper return template data', function () {
|
||||
it('when no filter source', function () {
|
||||
this.model.set('filter_source', undefined);
|
||||
|
||||
expect(this.model.getTemplateData()).toEqual({
|
||||
hasFilterSource: false
|
||||
});
|
||||
});
|
||||
|
||||
it('when something as filter_source', function () {
|
||||
this.model.set('filter_source', 'shiva');
|
||||
|
||||
expect(this.model.getTemplateData()).toEqual({
|
||||
hasFilterSource: true
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should disable the second step if not source column', function () {
|
||||
var template = this.model.getTemplate()({
|
||||
hasFilterSource: false,
|
||||
linkContent: ''
|
||||
});
|
||||
|
||||
expect(template.indexOf('Editor-HeaderInfo is-disabled')).not.toBe(-1);
|
||||
});
|
||||
|
||||
it('should enable the second step if not source column', function () {
|
||||
var template = this.model.getTemplate()({
|
||||
hasFilterSource: true,
|
||||
linkContent: ''
|
||||
});
|
||||
|
||||
expect(template.indexOf('Editor-HeaderInfo is-disabled')).toBe(-1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('type filter by node column', function () {
|
||||
beforeEach(function () {
|
||||
this.createModel({
|
||||
filter_source: 'a0',
|
||||
column: null,
|
||||
filter_column: null
|
||||
});
|
||||
});
|
||||
|
||||
it('should have proper attributes', function () {
|
||||
expect(this.model.attributes).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'filter-by-node-column',
|
||||
filter_source: 'a0',
|
||||
column: null,
|
||||
filter_column: null
|
||||
});
|
||||
});
|
||||
|
||||
describe('filter_column', function () {
|
||||
it('validator is correct', function () {
|
||||
this.model.set('column', 'cartodb_id');
|
||||
|
||||
expect(this.model.schema.column).toBeDefined();
|
||||
|
||||
this.model.set('filter_column', 'numero_de_finca');
|
||||
|
||||
expect(this.model.schema.filter_column).toBeDefined();
|
||||
expect(this.model.schema.filter_column.validators).toBeDefined();
|
||||
expect(this.model.schema.filter_column.validators[0]).toEqual('required');
|
||||
expect(this.model.schema.filter_column.validators[1]).toEqual(jasmine.any(Function));
|
||||
});
|
||||
});
|
||||
|
||||
describe('._validateFilterColumn', function () {
|
||||
it('should validate column types match', function () {
|
||||
var formValues = {
|
||||
column: 'cartodb_id',
|
||||
filter_column: 'numero_de_finca'
|
||||
};
|
||||
|
||||
spyOn(this.model._columnOptions, 'findColumn').and.returnValue({
|
||||
type: 'number'
|
||||
});
|
||||
spyOn(this.model._filterColumnOptions, 'findColumn').and.returnValue({
|
||||
type: 'string'
|
||||
});
|
||||
|
||||
expect(JSON.stringify(this.model._validateFilterColumn('numero_de_finca', formValues)))
|
||||
.toBe('{"message":"editor.layers.analysis-form.should-match"}');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
+530
@@ -0,0 +1,530 @@
|
||||
var Backbone = require('backbone');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var analyses = require('builder/data/analyses');
|
||||
var FilterFormModel = require('builder/editor/layers/layer-content-views/analyses/analysis-form-models/filter-form-model');
|
||||
var AnalysisDefinitionNodeModel = require('builder/data/analysis-definition-node-model');
|
||||
var AnalysisDefinitionNodeSourceModel = require('builder/data/analysis-definition-node-source-model');
|
||||
|
||||
describe('editor/layers/layer-content-views/analyses/analysis-form-models/filter-form-model', function () {
|
||||
beforeEach(function () {
|
||||
jasmine.Ajax.install();
|
||||
jasmine.Ajax.stubRequest(new RegExp('.*api/v2/sql.*'))
|
||||
.andReturn({ status: 200 });
|
||||
|
||||
var configModel = new ConfigModel({
|
||||
base_url: '/u/pepe',
|
||||
api_key: 'wadus',
|
||||
user_name: 'pepe'
|
||||
});
|
||||
|
||||
this.analysisDefinitionNodesCollection = new Backbone.Collection();
|
||||
|
||||
this.a0 = new AnalysisDefinitionNodeSourceModel({
|
||||
id: 'a0',
|
||||
type: 'source',
|
||||
query: 'SELECT * FROM a_table'
|
||||
}, {
|
||||
configModel: configModel,
|
||||
userModel: {},
|
||||
collection: this.analysisDefinitionNodesCollection
|
||||
});
|
||||
spyOn(this.a0, 'isValidAsInputForType').and.returnValue(true);
|
||||
|
||||
this.a1 = new AnalysisDefinitionNodeModel({
|
||||
id: 'a1',
|
||||
type: 'filter-range',
|
||||
source: 'a0'
|
||||
}, {
|
||||
configModel: configModel,
|
||||
collection: this.analysisDefinitionNodesCollection
|
||||
});
|
||||
|
||||
this.layerDefinitionModel = new LayerDefinitionModel(null, {
|
||||
configModel: configModel
|
||||
});
|
||||
spyOn(this.layerDefinitionModel, 'findAnalysisDefinitionNodeModel').and.callFake(function (id) {
|
||||
switch (id) {
|
||||
case 'a0': return this.a0;
|
||||
case 'a1': return this.a1;
|
||||
default: return null;
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
this.model = new FilterFormModel(this.a1.attributes, {
|
||||
analyses: analyses,
|
||||
configModel: configModel,
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
analysisSourceOptionsModel: {},
|
||||
parse: true
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
jasmine.Ajax.uninstall();
|
||||
});
|
||||
|
||||
it('should add analysis schema', function () {
|
||||
expect(this.model.schema).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have attributes set with defaults', function () {
|
||||
expect(this.model.attributes).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'filter-range',
|
||||
kind: 'between',
|
||||
greater_than_or_equal: 0,
|
||||
less_than_or_equal: 50,
|
||||
greater_than: 0,
|
||||
less_than: 50,
|
||||
column: '',
|
||||
accept_reject: 'accept'
|
||||
});
|
||||
});
|
||||
|
||||
it('should have generated form fields', function () {
|
||||
expect(Object.keys(this.model.schema).length).toBe(5);
|
||||
|
||||
expect(this.model.schema.min).not.toBeDefined();
|
||||
expect(this.model.schema.max).not.toBeDefined();
|
||||
});
|
||||
|
||||
it('should format the attributes', function () {
|
||||
expect(this.model._formatAttrs(this.model.attributes)).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'filter-range',
|
||||
column: null,
|
||||
kind: 'between',
|
||||
greater_than_or_equal: 0,
|
||||
less_than_or_equal: 50
|
||||
});
|
||||
});
|
||||
|
||||
it('should have the second step disabled for no column', function () {
|
||||
var templateData = { linkContent: '', column: null, histogram_stats: null, parametersDataFields: '' };
|
||||
expect(this.model.getTemplate()(templateData)).toContain('Editor-HeaderInfo is-disabled');
|
||||
});
|
||||
|
||||
it('should enable the second step if column selected', function () {
|
||||
var templateData = { linkContent: '', column: 'morty', histogram_stats: null, parametersDataFields: '' };
|
||||
expect(this.model.getTemplate()(templateData)).not.toContain('Editor-HeaderInfo is-disabled');
|
||||
});
|
||||
|
||||
describe('number', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(this.model, '_getSelectedColumnType').and.returnValue('number');
|
||||
|
||||
this.model.set('column', 'number');
|
||||
});
|
||||
|
||||
describe('kind is-equal-to', function () {
|
||||
beforeEach(function () {
|
||||
this.model.set({
|
||||
kind: 'is-equal-to',
|
||||
text: 1
|
||||
});
|
||||
});
|
||||
|
||||
it('should have proper attributes', function () {
|
||||
expect(this.model.attributes).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'filter-category',
|
||||
accept_reject: 'accept',
|
||||
column: 'number',
|
||||
kind: 'is-equal-to',
|
||||
greater_than_or_equal: 0,
|
||||
less_than_or_equal: 50,
|
||||
greater_than: 0,
|
||||
less_than: 50,
|
||||
text: 1
|
||||
});
|
||||
});
|
||||
|
||||
it('should have generated form fields', function () {
|
||||
expect(Object.keys(this.model.schema).length).toBe(4);
|
||||
|
||||
expect(this.model.schema.text).toBeDefined();
|
||||
expect(this.model.schema.min).not.toBeDefined();
|
||||
expect(this.model.schema.max).not.toBeDefined();
|
||||
});
|
||||
|
||||
it('should format the attributes', function () {
|
||||
expect(this.model._formatAttrs(this.model.attributes)).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'filter-category',
|
||||
column: 'number',
|
||||
kind: 'is-equal-to',
|
||||
accept: [1]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('kind is-less-or-equal-than', function () {
|
||||
beforeEach(function () {
|
||||
this.model.set({
|
||||
kind: 'is-less-or-equal-than',
|
||||
less_than_or_equal: 4
|
||||
});
|
||||
});
|
||||
|
||||
it('should have proper attributes', function () {
|
||||
expect(this.model.attributes).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'filter-range',
|
||||
accept_reject: 'accept',
|
||||
column: 'number',
|
||||
kind: 'is-less-or-equal-than',
|
||||
greater_than_or_equal: 0,
|
||||
less_than_or_equal: 4,
|
||||
greater_than: 0,
|
||||
less_than: 50
|
||||
});
|
||||
});
|
||||
|
||||
it('should have generated form fields', function () {
|
||||
expect(Object.keys(this.model.schema).length).toBe(4);
|
||||
|
||||
expect(this.model.schema.less_than_or_equal).toBeDefined();
|
||||
expect(this.model.schema.less_than).not.toBeDefined();
|
||||
expect(this.model.schema.greater_than).not.toBeDefined();
|
||||
expect(this.model.schema.greater_than_or_equal).not.toBeDefined();
|
||||
expect(this.model.schema.text).not.toBeDefined();
|
||||
});
|
||||
|
||||
it('should format the attributes', function () {
|
||||
expect(this.model._formatAttrs(this.model.attributes)).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'filter-range',
|
||||
column: 'number',
|
||||
kind: 'is-less-or-equal-than',
|
||||
less_than_or_equal: 4
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('kind is-less-than', function () {
|
||||
beforeEach(function () {
|
||||
this.model.set({
|
||||
kind: 'is-less-than',
|
||||
less_than: 1
|
||||
});
|
||||
});
|
||||
|
||||
it('should have proper attributes', function () {
|
||||
expect(this.model.attributes).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'filter-range',
|
||||
accept_reject: 'accept',
|
||||
column: 'number',
|
||||
kind: 'is-less-than',
|
||||
greater_than_or_equal: 0,
|
||||
less_than_or_equal: 50,
|
||||
greater_than: 0,
|
||||
less_than: 1
|
||||
});
|
||||
});
|
||||
|
||||
it('should have generated form fields', function () {
|
||||
expect(Object.keys(this.model.schema).length).toBe(4);
|
||||
|
||||
expect(this.model.schema.less_than).toBeDefined();
|
||||
expect(this.model.schema.greater_than).not.toBeDefined();
|
||||
expect(this.model.schema.greater_than_or_equal).not.toBeDefined();
|
||||
expect(this.model.schema.less_than_or_equal).not.toBeDefined();
|
||||
expect(this.model.schema.text).not.toBeDefined();
|
||||
});
|
||||
|
||||
it('should format the attributes', function () {
|
||||
expect(this.model._formatAttrs(this.model.attributes)).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'filter-range',
|
||||
column: 'number',
|
||||
kind: 'is-less-than',
|
||||
less_than: 1
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('kind is-greater-than', function () {
|
||||
beforeEach(function () {
|
||||
this.model.set({
|
||||
kind: 'is-greater-than',
|
||||
greater_than: 1
|
||||
});
|
||||
});
|
||||
|
||||
it('should have proper attributes', function () {
|
||||
expect(this.model.attributes).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'filter-range',
|
||||
accept_reject: 'accept',
|
||||
column: 'number',
|
||||
kind: 'is-greater-than',
|
||||
greater_than_or_equal: 0,
|
||||
less_than_or_equal: 50,
|
||||
greater_than: 1,
|
||||
less_than: 50
|
||||
});
|
||||
});
|
||||
|
||||
it('should have generated form fields', function () {
|
||||
expect(Object.keys(this.model.schema).length).toBe(4);
|
||||
|
||||
expect(this.model.schema.greater_than).toBeDefined();
|
||||
expect(this.model.schema.greater_than_or_equal).not.toBeDefined();
|
||||
expect(this.model.schema.less_than_or_equal).not.toBeDefined();
|
||||
expect(this.model.schema.less_than).not.toBeDefined();
|
||||
expect(this.model.schema.text).not.toBeDefined();
|
||||
});
|
||||
|
||||
it('should format the attributes', function () {
|
||||
expect(this.model._formatAttrs(this.model.attributes)).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'filter-range',
|
||||
column: 'number',
|
||||
kind: 'is-greater-than',
|
||||
greater_than: 1
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('kind is-greater-or-equal-than', function () {
|
||||
beforeEach(function () {
|
||||
this.model.set({
|
||||
kind: 'is-greater-or-equal-than',
|
||||
greater_than_or_equal: 4
|
||||
});
|
||||
});
|
||||
|
||||
it('should have proper attributes', function () {
|
||||
expect(this.model.attributes).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'filter-range',
|
||||
accept_reject: 'accept',
|
||||
column: 'number',
|
||||
kind: 'is-greater-or-equal-than',
|
||||
greater_than_or_equal: 4,
|
||||
less_than_or_equal: 50,
|
||||
greater_than: 0,
|
||||
less_than: 50
|
||||
});
|
||||
});
|
||||
|
||||
it('should have generated form fields', function () {
|
||||
expect(Object.keys(this.model.schema).length).toBe(4);
|
||||
|
||||
expect(this.model.schema.greater_than_or_equal).toBeDefined();
|
||||
expect(this.model.schema.less_than_or_equal).not.toBeDefined();
|
||||
expect(this.model.schema.less_than).not.toBeDefined();
|
||||
expect(this.model.schema.greater_than).not.toBeDefined();
|
||||
expect(this.model.schema.text).not.toBeDefined();
|
||||
});
|
||||
|
||||
it('should format the attributes', function () {
|
||||
expect(this.model._formatAttrs(this.model.attributes)).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'filter-range',
|
||||
column: 'number',
|
||||
kind: 'is-greater-or-equal-than',
|
||||
greater_than_or_equal: 4
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('string', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(this.model, '_getSelectedColumnType').and.returnValue('string');
|
||||
|
||||
this.model.set({
|
||||
column: 'string',
|
||||
text: 'wadus'
|
||||
});
|
||||
});
|
||||
|
||||
describe('accept', function () {
|
||||
it('should not include default attributes', function () {
|
||||
expect(this.model.get('kind')).toBe(null);
|
||||
});
|
||||
|
||||
it('should have proper attributes', function () {
|
||||
expect(this.model.attributes).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'filter-category',
|
||||
accept_reject: 'accept',
|
||||
column: 'string',
|
||||
kind: null,
|
||||
greater_than_or_equal: 0,
|
||||
less_than_or_equal: 50,
|
||||
greater_than: 0,
|
||||
less_than: 50,
|
||||
text: 'wadus'
|
||||
});
|
||||
});
|
||||
|
||||
it('should have generated form fields', function () {
|
||||
expect(Object.keys(this.model.schema).length).toBe(4);
|
||||
|
||||
expect(this.model.schema.text).toBeDefined();
|
||||
expect(this.model.schema.accept_reject).toBeDefined();
|
||||
});
|
||||
|
||||
it('should format the attributes', function () {
|
||||
expect(this.model._formatAttrs(this.model.attributes)).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'filter-category',
|
||||
column: 'string',
|
||||
accept: ['wadus']
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('reject', function () {
|
||||
beforeEach(function () {
|
||||
this.model.set('accept_reject', 'reject');
|
||||
});
|
||||
|
||||
it('should not include default attributes', function () {
|
||||
expect(this.model.get('kind')).toBe(null);
|
||||
});
|
||||
|
||||
it('should have proper attributes', function () {
|
||||
expect(this.model.attributes).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'filter-category',
|
||||
accept_reject: 'reject',
|
||||
column: 'string',
|
||||
kind: null,
|
||||
greater_than_or_equal: 0,
|
||||
less_than_or_equal: 50,
|
||||
greater_than: 0,
|
||||
less_than: 50,
|
||||
text: 'wadus'
|
||||
});
|
||||
});
|
||||
|
||||
it('should have generated form fields', function () {
|
||||
expect(Object.keys(this.model.schema).length).toBe(4);
|
||||
|
||||
expect(this.model.schema.text).toBeDefined();
|
||||
expect(this.model.schema.accept_reject).toBeDefined();
|
||||
});
|
||||
|
||||
it('should format the attributes', function () {
|
||||
expect(this.model._formatAttrs(this.model.attributes)).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'filter-category',
|
||||
column: 'string',
|
||||
reject: ['wadus']
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('boolean', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(this.model, '_getSelectedColumnType').and.returnValue('boolean');
|
||||
|
||||
this.model.set({
|
||||
column: 'boolean',
|
||||
text: 'true'
|
||||
});
|
||||
});
|
||||
|
||||
it('should not include default attributes', function () {
|
||||
expect(this.model.get('kind')).toBe('is-boolean');
|
||||
});
|
||||
|
||||
describe('accept', function () {
|
||||
it('should have proper attributes', function () {
|
||||
expect(this.model.attributes).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'filter-category',
|
||||
accept_reject: 'accept',
|
||||
column: 'boolean',
|
||||
kind: 'is-boolean',
|
||||
greater_than_or_equal: 0,
|
||||
less_than_or_equal: 50,
|
||||
greater_than: 0,
|
||||
less_than: 50,
|
||||
text: 'true'
|
||||
});
|
||||
});
|
||||
|
||||
it('should have generated form fields', function () {
|
||||
expect(Object.keys(this.model.schema).length).toBe(4);
|
||||
|
||||
expect(this.model.schema.text).toBeDefined();
|
||||
expect(this.model.schema.accept_reject).toBeDefined();
|
||||
});
|
||||
|
||||
it('should format the attributes', function () {
|
||||
expect(this.model._formatAttrs(this.model.attributes)).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'filter-category',
|
||||
column: 'boolean',
|
||||
kind: 'is-boolean',
|
||||
accept: ['true']
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('reject', function () {
|
||||
beforeEach(function () {
|
||||
this.model.set('accept_reject', 'reject');
|
||||
});
|
||||
|
||||
it('should have proper attributes', function () {
|
||||
expect(this.model.attributes).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'filter-category',
|
||||
accept_reject: 'reject',
|
||||
column: 'boolean',
|
||||
kind: 'is-boolean',
|
||||
greater_than_or_equal: 0,
|
||||
less_than_or_equal: 50,
|
||||
greater_than: 0,
|
||||
less_than: 50,
|
||||
text: 'true'
|
||||
});
|
||||
});
|
||||
|
||||
it('should have generated form fields', function () {
|
||||
expect(Object.keys(this.model.schema).length).toBe(4);
|
||||
|
||||
expect(this.model.schema.text).toBeDefined();
|
||||
expect(this.model.schema.accept_reject).toBeDefined();
|
||||
});
|
||||
|
||||
it('should format the attributes', function () {
|
||||
expect(this.model._formatAttrs(this.model.attributes)).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'filter-category',
|
||||
column: 'boolean',
|
||||
kind: 'is-boolean',
|
||||
reject: ['true']
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
var Backbone = require('backbone');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var analyses = require('builder/data/analyses');
|
||||
var GeoreferenceFormModel = require('builder/editor/layers/layer-content-views/analyses/analysis-form-models/georeference-form-model');
|
||||
var AnalysisDefinitionNodeSourceModel = require('builder/data/analysis-definition-node-source-model');
|
||||
var AnalysisDefinitionNodeModel = require('builder/data/analysis-definition-node-model');
|
||||
|
||||
describe('editor/layers/layer-content-views/analyses/analysis-form-models/georeference-form-model', function () {
|
||||
beforeEach(function () {
|
||||
var configModel = new ConfigModel({
|
||||
base_url: '/u/pepe',
|
||||
api_key: 'wadus',
|
||||
user_name: 'pepe'
|
||||
});
|
||||
|
||||
this.analysisDefinitionNodesCollection = new Backbone.Collection();
|
||||
|
||||
this.a0 = new AnalysisDefinitionNodeSourceModel({
|
||||
id: 'a0',
|
||||
type: 'source',
|
||||
query: 'SELECT * FROM a_table'
|
||||
}, {
|
||||
configModel: configModel,
|
||||
userModel: {},
|
||||
collection: this.analysisDefinitionNodesCollection
|
||||
});
|
||||
spyOn(this.a0, 'isValidAsInputForType').and.returnValue(true);
|
||||
|
||||
this.a1 = new AnalysisDefinitionNodeModel({
|
||||
id: 'a1',
|
||||
type: 'georeference-long-lat',
|
||||
source: 'a0'
|
||||
}, {
|
||||
configModel: configModel,
|
||||
collection: this.analysisDefinitionNodesCollection
|
||||
});
|
||||
|
||||
this.layerDefinitionModel = new LayerDefinitionModel(null, {
|
||||
configModel: configModel
|
||||
});
|
||||
spyOn(this.layerDefinitionModel, 'findAnalysisDefinitionNodeModel').and.callFake(function (id) {
|
||||
switch (id) {
|
||||
case 'a0': return this.a0;
|
||||
case 'a1': return this.a1;
|
||||
default: return null;
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
this.model = new GeoreferenceFormModel(this.a1.attributes, {
|
||||
analyses: analyses,
|
||||
configModel: configModel,
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
analysisSourceOptionsModel: {},
|
||||
parse: true
|
||||
});
|
||||
});
|
||||
|
||||
it('should add analysis schema', function () {
|
||||
expect(this.model.schema).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have attributes set with defaults', function () {
|
||||
expect(this.model.attributes).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'georeference-long-lat',
|
||||
latitude: undefined,
|
||||
longitude: undefined
|
||||
});
|
||||
});
|
||||
|
||||
it('should have generated form fields', function () {
|
||||
expect(Object.keys(this.model.schema).length).toBe(4);
|
||||
});
|
||||
|
||||
describe('type georeference-street-address', function () {
|
||||
beforeEach(function () {
|
||||
this.model.set('type', 'georeference-street-address');
|
||||
});
|
||||
|
||||
it('should have proper attributes', function () {
|
||||
expect(this.model.attributes).toEqual({
|
||||
context: undefined,
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'georeference-street-address',
|
||||
street_address_column: '',
|
||||
city: '',
|
||||
state: '',
|
||||
country: ''
|
||||
});
|
||||
});
|
||||
|
||||
it('should have generated form fields', function () {
|
||||
expect(Object.keys(this.model.schema).length).toBe(7);
|
||||
|
||||
expect(this.model.schema.street_address_column).toBeDefined();
|
||||
});
|
||||
|
||||
describe('context advance', function () {
|
||||
it('should have generated form fields', function () {
|
||||
this.model.set('context', 'advance');
|
||||
|
||||
expect(Object.keys(this.model.schema).length).toBe(7);
|
||||
expect(this.model.schema.street_address_column).not.toBeDefined();
|
||||
expect(this.model.schema.street_address_template).toBeDefined();
|
||||
});
|
||||
|
||||
it('should format street_address properly', function () {
|
||||
spyOn(this.model._typeDef(), '_wrapInBraces').and.callThrough();
|
||||
|
||||
this.model.set('street_address_column', 'address');
|
||||
this.model.set('context', 'advance');
|
||||
|
||||
expect(this.model._typeDef()._wrapInBraces).toHaveBeenCalledWith('address');
|
||||
expect(this.model.get('street_address_template')).toBe('{{address}}');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('type georeference-postal-code-model', function () {
|
||||
beforeEach(function () {
|
||||
this.model.set('type', 'georeference-postal-code');
|
||||
});
|
||||
|
||||
it('should have proper attributes', function () {
|
||||
expect(this.model.attributes).toEqual({
|
||||
context: undefined,
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'georeference-postal-code',
|
||||
postal_code_column: '',
|
||||
postal_code_country: ''
|
||||
});
|
||||
});
|
||||
|
||||
it('should have generated form fields', function () {
|
||||
expect(Object.keys(this.model.schema).length).toBe(4);
|
||||
|
||||
expect(this.model.schema.postal_code_column).toBeDefined();
|
||||
expect(this.model.schema.postal_code_column.validators[0]).toBe('required');
|
||||
expect(this.model.schema.postal_code_country).toBeDefined();
|
||||
expect(this.model.schema.postal_code_country.validators[0]).toBe('required');
|
||||
});
|
||||
});
|
||||
|
||||
it('should return only numeric columns for latitude and longitude', function () {
|
||||
this.model._columnOptions._columnOptions = {
|
||||
cartodb_id: { type: 'number' },
|
||||
title: { type: 'string' },
|
||||
created_at: { type: 'date' }
|
||||
};
|
||||
this.model._setSchema();
|
||||
|
||||
expect(this.model.schema.latitude.options.length).toBe(1);
|
||||
expect(this.model.schema.longitude.options.length).toBe(1);
|
||||
});
|
||||
});
|
||||
+171
@@ -0,0 +1,171 @@
|
||||
var Backbone = require('backbone');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var AnalysisDefinitionNodeSourceModel = require('builder/data/analysis-definition-node-source-model');
|
||||
var AnalysisDefinitionNodeModel = require('builder/data/analysis-definition-node-model');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var GroupPointsFormModel = require('builder/editor/layers/layer-content-views/analyses/analysis-form-models/group-points-form-model');
|
||||
var analyses = require('builder/data/analyses');
|
||||
|
||||
describe('editor/layers/layer-content-views/analyses/analysis-form-models/group-points-form-model', function () {
|
||||
beforeEach(function () {
|
||||
var configModel = new ConfigModel({
|
||||
base_url: '/u/pepe',
|
||||
api_key: 'wadus',
|
||||
user_name: 'pepe'
|
||||
});
|
||||
|
||||
this.analysisDefinitionNodesCollection = new Backbone.Collection();
|
||||
|
||||
this.a0 = new AnalysisDefinitionNodeSourceModel({
|
||||
id: 'a0',
|
||||
type: 'concave-hull',
|
||||
query: 'SELECT * FROM a_table'
|
||||
}, {
|
||||
configModel: configModel,
|
||||
userModel: {},
|
||||
collection: this.analysisDefinitionNodesCollection
|
||||
});
|
||||
|
||||
spyOn(this.a0, 'isValidAsInputForType').and.returnValue(true);
|
||||
|
||||
this.a1 = new AnalysisDefinitionNodeModel({
|
||||
id: 'a1',
|
||||
type: 'bounding-box',
|
||||
source: 'a0'
|
||||
}, {
|
||||
configModel: configModel,
|
||||
collection: this.analysisDefinitionNodesCollection
|
||||
});
|
||||
|
||||
this.layerDefinitionModel = new LayerDefinitionModel(null, {
|
||||
configModel: configModel
|
||||
});
|
||||
|
||||
spyOn(this.layerDefinitionModel, 'findAnalysisDefinitionNodeModel').and.callFake(function (id) {
|
||||
switch (id) {
|
||||
case 'a0': return this.a0;
|
||||
case 'a1': return this.a1;
|
||||
default: return null;
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
this.model = new GroupPointsFormModel(this.a1.attributes, {
|
||||
analyses: analyses,
|
||||
configModel: configModel,
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
analysisSourceOptionsModel: {},
|
||||
parse: true
|
||||
});
|
||||
});
|
||||
|
||||
it('should add analysis schema', function () {
|
||||
expect(this.model.schema).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have attributes set with defaults', function () {
|
||||
expect(this.model.attributes).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'bounding-box',
|
||||
category_column: undefined,
|
||||
aggregate: Object({ operator: 'count', attribute: '' }),
|
||||
aggregation_column: '',
|
||||
aggregation: 'count'
|
||||
});
|
||||
});
|
||||
|
||||
it('should have generated form fields', function () {
|
||||
expect(Object.keys(this.model.schema).length).toBe(4);
|
||||
});
|
||||
|
||||
it('should bind properly', function () {
|
||||
this.model.set('aggregate', { operator: 'avg', attribute: 'wadus' });
|
||||
expect(this.model.get('aggregation_column')).toBe('wadus');
|
||||
expect(this.model.get('aggregation')).toBe('avg');
|
||||
});
|
||||
|
||||
it('should have proper attributes', function () {
|
||||
expect(this.model._typeDef().parametersDataFields).toBe('source,type,category_column');
|
||||
});
|
||||
|
||||
it('should format the attributes', function () {
|
||||
this.model.set('aggregate', { operator: 'avg', attribute: 'wadus' });
|
||||
|
||||
expect(this.model._formatAttrs(this.model.attributes)).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'bounding-box',
|
||||
aggregation: 'avg',
|
||||
aggregation_column: 'wadus'
|
||||
});
|
||||
});
|
||||
|
||||
describe('type bounding-circle', function () {
|
||||
beforeEach(function () {
|
||||
this.model.set('type', 'bounding-circle');
|
||||
});
|
||||
|
||||
it('should have proper attributes', function () {
|
||||
expect(this.model._typeDef().parametersDataFields).toBe('source,type,category_column');
|
||||
});
|
||||
|
||||
it('should format the attributes', function () {
|
||||
this.model.set('category_column', 'cluster_no');
|
||||
this.model.set('aggregate', { operator: 'avg', attribute: 'wadus' });
|
||||
|
||||
expect(this.model._formatAttrs(this.model.attributes)).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'bounding-circle',
|
||||
category_column: 'cluster_no',
|
||||
aggregation: 'avg',
|
||||
aggregation_column: 'wadus'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('type convex-hull', function () {
|
||||
beforeEach(function () {
|
||||
this.model.set('type', 'convex-hull');
|
||||
});
|
||||
|
||||
it('should have proper attributes', function () {
|
||||
expect(this.model._typeDef().parametersDataFields).toBe('source,type,category_column');
|
||||
});
|
||||
|
||||
it('should format the attributes', function () {
|
||||
this.model.set('aggregate', { operator: 'sum', attribute: 'wadus' });
|
||||
|
||||
expect(this.model._formatAttrs(this.model.attributes)).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'convex-hull',
|
||||
aggregation: 'sum',
|
||||
aggregation_column: 'wadus'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('type concave-hull', function () {
|
||||
beforeEach(function () {
|
||||
this.model.set('type', 'concave-hull');
|
||||
});
|
||||
|
||||
it('should have proper attributes', function () {
|
||||
expect(this.model._typeDef().parametersDataFields).toBe('source,type,target_percentage,allow_holes,category_column');
|
||||
});
|
||||
|
||||
it('should format the attributes', function () {
|
||||
this.model.set('aggregate', { operator: 'sum', attribute: 'wadus' });
|
||||
|
||||
expect(this.model._formatAttrs(this.model.attributes)).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'concave-hull',
|
||||
target_percent: 0.7,
|
||||
aggregation: 'sum',
|
||||
aggregation_column: 'wadus'
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
+308
@@ -0,0 +1,308 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var AnalysisDefinitionNodeSourceModel = require('builder/data/analysis-definition-node-source-model');
|
||||
var AnalysisDefinitionNodeModel = require('builder/data/analysis-definition-node-model');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var MergeFormModel = require('builder/editor/layers/layer-content-views/analyses/analysis-form-models/merge-form-model');
|
||||
var AnalysisSourceOptionsModel = require('builder/editor/layers/layer-content-views/analyses/analysis-source-options-model');
|
||||
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
|
||||
var analyses = require('builder/data/analyses');
|
||||
|
||||
describe('editor/layers/layer-content-views/analyses/analysis-form-models/merge-form-model', function () {
|
||||
var createModelFn = function (attributes, options) {
|
||||
var defaultOptions = {
|
||||
analyses: analyses,
|
||||
configModel: this.configModel,
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
analysisSourceOptionsModel: this.analysisSourceOptionsModel,
|
||||
parse: true
|
||||
};
|
||||
|
||||
this.model = new MergeFormModel(_.extend({}, this.a1.attributes, attributes), _.extend({}, defaultOptions, options));
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
this.configModel = new ConfigModel({
|
||||
base_url: '/u/pepe',
|
||||
api_key: 'wadus',
|
||||
user_name: 'pepe'
|
||||
});
|
||||
|
||||
var userModel = new UserModel({}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
this.analysisDefinitionNodesCollection = new Backbone.Collection();
|
||||
|
||||
this.a0 = new AnalysisDefinitionNodeSourceModel({
|
||||
id: 'a0',
|
||||
type: 'merge',
|
||||
query: 'SELECT * FROM a_table'
|
||||
}, {
|
||||
configModel: this.configModel,
|
||||
userModel: {},
|
||||
collection: this.analysisDefinitionNodesCollection
|
||||
});
|
||||
|
||||
spyOn(this.a0, 'isValidAsInputForType').and.returnValue(true);
|
||||
|
||||
this.a1 = new AnalysisDefinitionNodeModel({
|
||||
id: 'a1',
|
||||
type: 'merge',
|
||||
source: 'a0'
|
||||
}, {
|
||||
configModel: this.configModel,
|
||||
collection: this.analysisDefinitionNodesCollection
|
||||
});
|
||||
|
||||
this.layerDefinitionModel = new LayerDefinitionModel(null, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
spyOn(this.layerDefinitionModel, 'findAnalysisDefinitionNodeModel').and.callFake(function (id) {
|
||||
switch (id) {
|
||||
case 'a0': return this.a0;
|
||||
case 'a1': return this.a1;
|
||||
default: return null;
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
this.layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
|
||||
configModel: this.configModel,
|
||||
userModel: userModel,
|
||||
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
|
||||
mapId: 'map-123',
|
||||
stateDefinitionModel: {}
|
||||
});
|
||||
|
||||
this.analysisSourceOptionsModel = new AnalysisSourceOptionsModel(null, {
|
||||
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
|
||||
layerDefinitionsCollection: this.layerDefinitionsCollection,
|
||||
tablesCollection: new Backbone.Collection()
|
||||
});
|
||||
|
||||
this.analysisSourceOptionsModel.getSelectOptions = function () {
|
||||
return this;
|
||||
};
|
||||
|
||||
this.analysisSourceOptionsModel.find = function () {
|
||||
return this;
|
||||
};
|
||||
|
||||
this.analysisSourceOptionsModel.filter = function () {
|
||||
return [];
|
||||
};
|
||||
|
||||
this.createModel = createModelFn.bind(this);
|
||||
});
|
||||
|
||||
it('should add analysis schema', function () {
|
||||
this.createModel();
|
||||
|
||||
expect(this.model.schema).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have attributes set with defaults', function () {
|
||||
this.createModel();
|
||||
|
||||
expect(this.model.attributes).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'merge',
|
||||
source_geometry_selector: undefined,
|
||||
join_operator: 'inner',
|
||||
source_geometry: 'left_source',
|
||||
right_source_columns: []
|
||||
});
|
||||
});
|
||||
|
||||
it('should have generated form fields', function () {
|
||||
this.createModel();
|
||||
|
||||
expect(Object.keys(this.model.schema).length).toBe(8);
|
||||
});
|
||||
|
||||
it('should disable the fields appropriately', function () {
|
||||
this.createModel();
|
||||
|
||||
var template = this.model.getTemplate()({
|
||||
linkContent: '',
|
||||
right_source: true,
|
||||
hasLeftAndRightSourceColumns: true
|
||||
});
|
||||
|
||||
expect((template.match(/Editor-HeaderInfo is-disabled/g) || []).length).toBe(0);
|
||||
|
||||
template = this.model.getTemplate()({
|
||||
linkContent: '',
|
||||
right_source: true,
|
||||
hasLeftAndRightSourceColumns: false
|
||||
});
|
||||
|
||||
expect((template.match(/Editor-HeaderInfo is-disabled/g) || []).length).toBe(1);
|
||||
|
||||
template = this.model.getTemplate()({
|
||||
linkContent: '',
|
||||
right_source: false,
|
||||
hasLeftAndRightSourceColumns: false
|
||||
});
|
||||
|
||||
expect((template.match(/Editor-HeaderInfo is-disabled/g) || []).length).toBe(2);
|
||||
});
|
||||
|
||||
it('should format the attributes', function () {
|
||||
this.createModel();
|
||||
|
||||
expect(this.model._formatAttrs(this.model.attributes)).toEqual({
|
||||
id: 'a1',
|
||||
type: 'merge',
|
||||
join_operator: 'inner',
|
||||
source_geometry_selector: undefined,
|
||||
right_source_columns: [],
|
||||
source_geometry: 'left_source',
|
||||
left_source_columns: [],
|
||||
left_source: '',
|
||||
right_source: '',
|
||||
left_source_column: null,
|
||||
right_source_column: null
|
||||
});
|
||||
});
|
||||
|
||||
describe('parsing the model', function () {
|
||||
beforeEach(function () {
|
||||
this.createModel();
|
||||
});
|
||||
|
||||
it('should parse the model with right source_geometry', function () {
|
||||
var attrs = {
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'merge',
|
||||
source_geometry: 'right_source',
|
||||
right_source: 'right'
|
||||
};
|
||||
|
||||
expect(this.model.parse(attrs)).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'merge',
|
||||
right_source: 'right',
|
||||
source_geometry_selector: 'right',
|
||||
source_geometry: 'right_source'
|
||||
});
|
||||
});
|
||||
|
||||
it('should parse the model without source_geometry', function () {
|
||||
var attrs = {
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'merge',
|
||||
source_geometry: null,
|
||||
left_source: 'left'
|
||||
};
|
||||
|
||||
expect(this.model.parse(attrs)).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'merge',
|
||||
left_source: 'left',
|
||||
source_geometry: null,
|
||||
source_geometry_selector: 'left'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('type join columns', function () {
|
||||
beforeEach(function () {
|
||||
jasmine.Ajax.install();
|
||||
jasmine.Ajax.stubRequest(new RegExp('.*api/v2/sql.*'))
|
||||
.andReturn({ status: 200 });
|
||||
|
||||
this.createModel({
|
||||
left_source: 'a0',
|
||||
right_source: 'a0',
|
||||
left_source_column: null,
|
||||
right_source_column: null
|
||||
}, {
|
||||
analyses: analyses,
|
||||
configModel: this.configModel,
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
analysisSourceOptionsModel: this.analysisSourceOptionsModel,
|
||||
parse: true
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
jasmine.Ajax.uninstall();
|
||||
});
|
||||
|
||||
it('should include left_source and right_source column', function () {
|
||||
expect(this.model.get('left_source_column')).toBeDefined();
|
||||
expect(this.model.get('right_source_column')).toBeDefined();
|
||||
});
|
||||
|
||||
it('should have proper attributes', function () {
|
||||
expect(this.model.attributes).toEqual({
|
||||
id: 'a1',
|
||||
source: 'a0',
|
||||
type: 'merge',
|
||||
source_geometry_selector: 'a0',
|
||||
join_operator: 'inner',
|
||||
source_geometry: 'left_source',
|
||||
right_source_columns: [],
|
||||
left_source_column: null,
|
||||
right_source_column: null,
|
||||
left_source: 'a0',
|
||||
right_source: 'a0'
|
||||
});
|
||||
});
|
||||
|
||||
it('should have generated form fields', function () {
|
||||
expect(Object.keys(this.model.schema).length).toBe(8);
|
||||
|
||||
expect(this.model.schema.left_source_column).toBeDefined();
|
||||
expect(this.model.schema.right_source_column).toBeDefined();
|
||||
});
|
||||
|
||||
describe('right_source_column', function () {
|
||||
it('validator is correct', function () {
|
||||
this.model.set('left_source_column', 'cartodb_id');
|
||||
|
||||
expect(this.model.schema.left_source_column).toBeDefined();
|
||||
expect(this.model.schema.left_source_column.validators).toBeDefined();
|
||||
expect(this.model.schema.left_source_column.validators[0]).toEqual('required');
|
||||
|
||||
this.model.set('right_source_column', 'numero_de_finca');
|
||||
|
||||
expect(this.model.schema.right_source_column).toBeDefined();
|
||||
expect(this.model.schema.right_source_column.validators).toBeDefined();
|
||||
expect(this.model.schema.right_source_column.validators[0]).toEqual('required');
|
||||
expect(this.model.schema.right_source_column.validators[1]).toEqual(jasmine.any(Function));
|
||||
});
|
||||
});
|
||||
|
||||
describe('._validateRightSourceColumn', function () {
|
||||
it('should validate column types match', function () {
|
||||
var self = this;
|
||||
|
||||
var formValues = {
|
||||
join_operator: 'inner',
|
||||
left_source_column: 'cartodb_id',
|
||||
right_source_column: 'numero_de_finca'
|
||||
};
|
||||
|
||||
spyOn(this.model._leftColumnOptions, 'findColumn').and.returnValue({
|
||||
type: 'number'
|
||||
});
|
||||
spyOn(this.model._rightColumnOptions, 'findColumn').and.returnValue({
|
||||
type: 'string'
|
||||
});
|
||||
|
||||
expect(JSON.stringify(self.model._validateRightSourceColumn('numero_de_finca', formValues)))
|
||||
.toBe('{"message":"editor.layers.analysis-form.should-match"}');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
var Backbone = require('backbone');
|
||||
var AnalysesQuotaEnough = require('builder/editor/layers/layer-content-views/analyses/analyses-quota/analyses-quota-enough');
|
||||
|
||||
describe('editor/layers/layer-content-view/analyses/analyses-quota/analyses-quota-enough', function () {
|
||||
beforeEach(function () {
|
||||
var configModel = new Backbone.Model({
|
||||
user_name: 'foo',
|
||||
sql_api_template: 'foo',
|
||||
api_key: 'foo'
|
||||
});
|
||||
|
||||
AnalysesQuotaEnough.init(configModel);
|
||||
this.successCallback = jasmine.createSpy('successCallback');
|
||||
this.errorCallback = jasmine.createSpy('errorCallback');
|
||||
});
|
||||
|
||||
describe('requests', function () {
|
||||
beforeEach(function () {
|
||||
AnalysesQuotaEnough.deferred = {
|
||||
state: function () {
|
||||
return 'pending';
|
||||
},
|
||||
promise: function () {
|
||||
return 'foo';
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
AnalysesQuotaEnough.deferred = null;
|
||||
});
|
||||
|
||||
it('should avoid multple request if executing', function () {
|
||||
spyOn(AnalysesQuotaEnough.SQL, 'execute');
|
||||
AnalysesQuotaEnough.fetch();
|
||||
expect(AnalysesQuotaEnough.SQL.execute).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('not enough quota', function () {
|
||||
spyOn(AnalysesQuotaEnough.SQL, 'execute').and.callFake(function (query, vars, params) {
|
||||
params && params.success({
|
||||
rows: [{ 'cdb_enough_quota': false }]
|
||||
});
|
||||
});
|
||||
|
||||
AnalysesQuotaEnough.fetch().then(this.successCallback, this.errorCallback);
|
||||
expect(this.successCallback).toHaveBeenCalledWith(false);
|
||||
});
|
||||
|
||||
it('enough quota', function () {
|
||||
spyOn(AnalysesQuotaEnough.SQL, 'execute').and.callFake(function (query, vars, params) {
|
||||
params && params.success({
|
||||
rows: [{ 'cdb_enough_quota': true }]
|
||||
});
|
||||
});
|
||||
|
||||
AnalysesQuotaEnough.fetch().then(this.successCallback, this.errorCallback);
|
||||
expect(this.successCallback).toHaveBeenCalledWith(true);
|
||||
});
|
||||
|
||||
it('error', function () {
|
||||
spyOn(AnalysesQuotaEnough.SQL, 'execute').and.callFake(function (query, vars, params) {
|
||||
params && params.error({
|
||||
responseText: '{"error": ["foo"]}'
|
||||
});
|
||||
});
|
||||
|
||||
AnalysesQuotaEnough.fetch().then(this.successCallback, this.errorCallback);
|
||||
expect(this.errorCallback).toHaveBeenCalled();
|
||||
expect(this.successCallback).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
var Backbone = require('backbone');
|
||||
var AnalysesQuotaEstimation = require('builder/editor/layers/layer-content-views/analyses/analyses-quota/analyses-quota-estimation-input');
|
||||
|
||||
describe('editor/layers/layer-content-view/analyses/analyses-quota/analyses-quota-estimation-input', function () {
|
||||
beforeEach(function () {
|
||||
var configModel = new Backbone.Model({
|
||||
user_name: 'foo',
|
||||
sql_api_template: 'foo',
|
||||
api_key: 'foo'
|
||||
});
|
||||
|
||||
AnalysesQuotaEstimation.init(configModel);
|
||||
this.successCallback = jasmine.createSpy('successCallback');
|
||||
this.errorCallback = jasmine.createSpy('errorCallback');
|
||||
});
|
||||
|
||||
describe('requests', function () {
|
||||
beforeEach(function () {
|
||||
AnalysesQuotaEstimation.deferred = {
|
||||
state: function () {
|
||||
return 'pending';
|
||||
},
|
||||
promise: function () {
|
||||
return 'foo';
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
AnalysesQuotaEstimation.deferred = null;
|
||||
});
|
||||
|
||||
it('should avoid multple request if executing', function () {
|
||||
spyOn(AnalysesQuotaEstimation.SQL, 'execute');
|
||||
AnalysesQuotaEstimation.fetch();
|
||||
expect(AnalysesQuotaEstimation.SQL.execute).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('success', function () {
|
||||
spyOn(AnalysesQuotaEstimation.SQL, 'execute').and.callFake(function (query, vars, params) {
|
||||
params && params.success({
|
||||
rows: [{ row_count: 3242 }]
|
||||
});
|
||||
});
|
||||
|
||||
AnalysesQuotaEstimation.fetch().then(this.successCallback, this.errorCallback);
|
||||
expect(this.successCallback).toHaveBeenCalledWith(3242);
|
||||
});
|
||||
|
||||
it('error', function () {
|
||||
spyOn(AnalysesQuotaEstimation.SQL, 'execute').and.callFake(function (query, vars, params) {
|
||||
params && params.error({
|
||||
responseText: '{"error": ["foo"]}'
|
||||
});
|
||||
});
|
||||
|
||||
AnalysesQuotaEstimation.fetch().then(this.successCallback, this.errorCallback);
|
||||
expect(this.errorCallback).toHaveBeenCalled();
|
||||
expect(this.successCallback).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
var Backbone = require('backbone');
|
||||
var AnalysesQuotaInfo = require('builder/editor/layers/layer-content-views/analyses/analyses-quota/analyses-quota-info');
|
||||
|
||||
describe('editor/layers/layer-content-view/analyses/analyses-quota/analyses-quota-info', function () {
|
||||
beforeEach(function () {
|
||||
var configModel = new Backbone.Model({
|
||||
user_name: 'foo',
|
||||
sql_api_template: 'foo',
|
||||
api_key: 'foo'
|
||||
});
|
||||
|
||||
this.quotaInfo = AnalysesQuotaInfo.get(configModel);
|
||||
spyOn(this.quotaInfo.SQL, 'execute').and.callFake(function (query, vars, params) {
|
||||
params && params.success({
|
||||
rows: [
|
||||
{ service: 'isolines', monthly_quota: 2000, used_quota: 23, soft_limit: false, provider: 'heremaps' },
|
||||
{ service: 'hires_geocoder', monthly_quota: 1000, used_quota: 0, soft_limit: true, provider: 'mapzen' },
|
||||
{ service: 'routing', monthly_quota: 1000, used_quota: 0, soft_limit: false, provider: 'mapzen' },
|
||||
{ service: 'observatory', monthly_quota: 1000, used_quota: 0, soft_limit: false, provider: 'data observatory' }
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
this.successCallback = jasmine.createSpy('successCallback');
|
||||
this.errorCallback = jasmine.createSpy('errorCallback');
|
||||
});
|
||||
|
||||
it('should fetch services', function () {
|
||||
this.quotaInfo.fetch({
|
||||
success: this.successCallback,
|
||||
error: this.errorCallback
|
||||
});
|
||||
|
||||
expect(this.successCallback).toHaveBeenCalled();
|
||||
expect(this.quotaInfo.length).toBe(4);
|
||||
});
|
||||
|
||||
it('should get service by name', function () {
|
||||
this.quotaInfo.fetch({
|
||||
success: this.successCallback,
|
||||
error: this.errorCallback
|
||||
});
|
||||
|
||||
var isolines = this.quotaInfo.getService('isolines');
|
||||
expect(isolines.get('provider')).toBe('heremaps');
|
||||
expect(isolines.get('monthly_quota')).toBe(2000);
|
||||
expect(isolines.get('used_quota')).toBe(23);
|
||||
});
|
||||
|
||||
it('should avoid multiple request if fetching', function () {
|
||||
spyOn(this.quotaInfo, 'isFetching').and.returnValue(true);
|
||||
this.quotaInfo.fetch({
|
||||
success: this.successCallback,
|
||||
error: this.errorCallback
|
||||
});
|
||||
|
||||
expect(this.quotaInfo.SQL.execute).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
var Backbone = require('backbone');
|
||||
var AnalysesQuotaOptions = require('builder/editor/layers/layer-content-views/analyses/analyses-quota/analyses-quota-options');
|
||||
var AnalysesQuotaInfo = require('builder/editor/layers/layer-content-views/analyses/analyses-quota/analyses-quota-info');
|
||||
|
||||
describe('editor/layers/layer-content-views/analyses/analyses-quota/analyses-quota-options', function () {
|
||||
beforeEach(function () {
|
||||
var configModel = new Backbone.Model({
|
||||
user_name: 'foo',
|
||||
sql_api_template: 'foo',
|
||||
api_key: 'foo'
|
||||
});
|
||||
|
||||
this.quotaInfo = AnalysesQuotaInfo.get(configModel);
|
||||
this.quotaInfo.reset([
|
||||
{service: 'isolines', monthly_quota: 2000, used_quota: 23, soft_limit: false, provider: 'heremaps'},
|
||||
{service: 'hires_geocoder', monthly_quota: 1000, used_quota: 0, soft_limit: true, provider: 'heremaps'},
|
||||
{service: 'routing', monthly_quota: 1000, used_quota: 0, soft_limit: false, provider: 'mapzen'},
|
||||
{service: 'observatory', monthly_quota: 1000, used_quota: 0, soft_limit: false, provider: 'data observatory'}
|
||||
]);
|
||||
});
|
||||
|
||||
describe('requiresQuota', function () {
|
||||
it('analysis does not require quota', function () {
|
||||
expect(AnalysesQuotaOptions.requiresQuota('foo', this.quotaInfo)).toBe(false);
|
||||
});
|
||||
|
||||
it('routing or data observatory', function () {
|
||||
expect(AnalysesQuotaOptions.requiresQuota('routing-sequential', this.quotaInfo)).toBe(true);
|
||||
expect(AnalysesQuotaOptions.requiresQuota('data-observatory-measure', this.quotaInfo)).toBe(true);
|
||||
});
|
||||
|
||||
it('data observatory multiple should NOT require quota', function () {
|
||||
expect(AnalysesQuotaOptions.requiresQuota('data-observatory-multiple-measures', this.quotaInfo)).toBeFalsy();
|
||||
});
|
||||
|
||||
describe('trade-area', function () {
|
||||
it('should require quota if provider is set', function () {
|
||||
var geocoder = this.quotaInfo.getService('hires_geocoder');
|
||||
expect(AnalysesQuotaOptions.requiresQuota('trade-area', this.quotaInfo)).toBeTruthy();
|
||||
geocoder.set('provider', 'mapzen');
|
||||
expect(AnalysesQuotaOptions.requiresQuota('trade-area', this.quotaInfo)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not require quota when provider is empty', function () {
|
||||
var isolines = this.quotaInfo.getService('isolines');
|
||||
expect(AnalysesQuotaOptions.requiresQuota('trade-area', this.quotaInfo)).toBeTruthy();
|
||||
isolines.set('provider', null);
|
||||
expect(AnalysesQuotaOptions.requiresQuota('trade-area', this.quotaInfo)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('georeference-street-address', function () {
|
||||
it('should return quota if provider is not google', function () {
|
||||
expect(AnalysesQuotaOptions.requiresQuota('georeference-street-address', this.quotaInfo)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not return quota if provider is google', function () {
|
||||
var serviceModel = this.quotaInfo.at(1);
|
||||
serviceModel.set('provider', 'google');
|
||||
expect(AnalysesQuotaOptions.requiresQuota('georeference-street-address', this.quotaInfo)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('georeference-city', function () {
|
||||
it('should return quota if provider is not google', function () {
|
||||
expect(AnalysesQuotaOptions.requiresQuota('georeference-city', this.quotaInfo)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not return quota if provider is google', function () {
|
||||
var serviceModel = this.quotaInfo.at(1);
|
||||
serviceModel.set('provider', 'google');
|
||||
expect(AnalysesQuotaOptions.requiresQuota('georeference-city', this.quotaInfo)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('transform input', function () {
|
||||
it('should return same input for analysis without quota', function () {
|
||||
expect(AnalysesQuotaOptions.transformInput('foo', 20, {})).toBe(20);
|
||||
});
|
||||
|
||||
it('should return same input for analysis with quota', function () {
|
||||
var formModel = new Backbone.Model({
|
||||
measurements: [1, 2]
|
||||
});
|
||||
|
||||
expect(AnalysesQuotaOptions.transformInput('georeference-street-address', 20, {})).toBe(20);
|
||||
expect(AnalysesQuotaOptions.transformInput('data-observatory-measure', 15, formModel)).toBe(15);
|
||||
});
|
||||
|
||||
it('should return input multiplied for tracts for isolines', function () {
|
||||
var formModel = new Backbone.Model({
|
||||
isolines: 1
|
||||
});
|
||||
|
||||
expect(AnalysesQuotaOptions.transformInput('trade-area', 20, formModel)).toBe(20);
|
||||
formModel.set('isolines', 2);
|
||||
expect(AnalysesQuotaOptions.transformInput('trade-area', 20, formModel)).toBe(40);
|
||||
formModel.set('isolines', 10);
|
||||
expect(AnalysesQuotaOptions.transformInput('trade-area', 20, formModel)).toBe(200);
|
||||
});
|
||||
});
|
||||
|
||||
it('getAnalysisName', function () {
|
||||
expect(AnalysesQuotaOptions.getAnalysisName('foo')).toBe(null);
|
||||
expect(AnalysesQuotaOptions.getAnalysisName('trade-area')).toBe('editor.layers.analysis-form.quota.analysis-type.trade-area');
|
||||
expect(AnalysesQuotaOptions.getAnalysisName('data-observatory-measure')).toBe('editor.layers.analysis-form.quota.analysis-type.data-observatory-measure');
|
||||
});
|
||||
|
||||
it('getServiceName', function () {
|
||||
expect(AnalysesQuotaOptions.getServiceName('foo')).toBe(null);
|
||||
expect(AnalysesQuotaOptions.getServiceName('trade-area')).toBe('isolines');
|
||||
expect(AnalysesQuotaOptions.getServiceName('data-observatory-measure')).toBe('observatory');
|
||||
});
|
||||
|
||||
it('getUserDataName', function () {
|
||||
expect(AnalysesQuotaOptions.getUserDataName('foo')).toBe(null);
|
||||
expect(AnalysesQuotaOptions.getUserDataName('trade-area')).toBe('here_isolines');
|
||||
expect(AnalysesQuotaOptions.getUserDataName('data-observatory-measure')).toBe('obs_general');
|
||||
});
|
||||
});
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
var Backbone = require('backbone');
|
||||
var AnalysesQuotaPresenter = require('builder/editor/layers/layer-content-views/analyses/analyses-quota/analyses-quota-presenter');
|
||||
|
||||
describe('editor/layers/layer-content-view/analyses/analyses-quota/analyses-quota-presenter', function () {
|
||||
var configModel = new Backbone.Model({
|
||||
cartodb_com_hosted: false
|
||||
});
|
||||
|
||||
var userModel = {
|
||||
isInsideOrg: function () {
|
||||
return false;
|
||||
},
|
||||
isOrgOwner: function () {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
it('Hard limit, enough quota', function () {
|
||||
var payload = {
|
||||
estimation: 127,
|
||||
quotaInfo: {
|
||||
blockPrice: 0,
|
||||
blockSize: 1000,
|
||||
enoughQuota: true,
|
||||
hardLimit: true,
|
||||
totalQuota: 2000,
|
||||
usedQuota: 1800
|
||||
},
|
||||
creditsLeft: 200
|
||||
};
|
||||
|
||||
var message = AnalysesQuotaPresenter.make(payload, configModel, userModel);
|
||||
expect(message.body).toBe('editor.layers.analysis-form.quota.enough-quota');
|
||||
expect(message.type).toBe('success');
|
||||
});
|
||||
|
||||
it('Hard limit, not enough quota', function () {
|
||||
var payload = {
|
||||
estimation: 127,
|
||||
quotaInfo: {
|
||||
blockPrice: 0,
|
||||
blockSize: 1000,
|
||||
enoughQuota: false,
|
||||
hardLimit: true,
|
||||
totalQuota: 2000,
|
||||
usedQuota: 1800
|
||||
},
|
||||
creditsLeft: 100
|
||||
};
|
||||
|
||||
var message = AnalysesQuotaPresenter.make(payload, configModel, userModel);
|
||||
expect(message.body).toBe('editor.layers.analysis-form.quota.hard-limit-not-enough-quota');
|
||||
expect(message.type).toBe('alert');
|
||||
});
|
||||
|
||||
it('Hard limit, not quota', function () {
|
||||
var payload = {
|
||||
estimation: 127,
|
||||
quotaInfo: {
|
||||
blockPrice: 0,
|
||||
blockSize: 1000,
|
||||
enoughQuota: true,
|
||||
hardLimit: true,
|
||||
totalQuota: 2000,
|
||||
usedQuota: 2000
|
||||
},
|
||||
creditsLeft: 0
|
||||
};
|
||||
|
||||
var message = AnalysesQuotaPresenter.make(payload, configModel, userModel);
|
||||
expect(message.body).toBe('editor.layers.analysis-form.quota.no-credits-body');
|
||||
expect(message.type).toBe('alert');
|
||||
});
|
||||
|
||||
it('Hard limit, not quota assigned', function () {
|
||||
var payload = {
|
||||
estimation: 127,
|
||||
quotaInfo: {
|
||||
blockPrice: 0,
|
||||
blockSize: 1000,
|
||||
enoughQuota: false,
|
||||
hardLimit: true,
|
||||
totalQuota: 0,
|
||||
usedQuota: 0
|
||||
},
|
||||
creditsLeft: 0
|
||||
};
|
||||
|
||||
var message = AnalysesQuotaPresenter.make(payload, configModel, userModel);
|
||||
expect(message.body).toBe('editor.layers.analysis-form.quota.no-quota-assigned-body');
|
||||
expect(message.type).toBe('alert');
|
||||
});
|
||||
|
||||
it('Soft limit, enough quota', function () {
|
||||
var payload = {
|
||||
estimation: 127,
|
||||
quotaInfo: {
|
||||
blockPrice: 0,
|
||||
blockSize: 1000,
|
||||
enoughQuota: true,
|
||||
hardLimit: false,
|
||||
totalQuota: 2000,
|
||||
usedQuota: 1800
|
||||
},
|
||||
creditsLeft: 200
|
||||
};
|
||||
|
||||
var message = AnalysesQuotaPresenter.make(payload, configModel, userModel);
|
||||
expect(message.body).toBe('editor.layers.analysis-form.quota.enough-quota');
|
||||
expect(message.type).toBe('success');
|
||||
});
|
||||
|
||||
it('Soft limit, not enough quota', function () {
|
||||
var payload = {
|
||||
estimation: 127,
|
||||
quotaInfo: {
|
||||
blockPrice: 0,
|
||||
blockSize: 1000,
|
||||
enoughQuota: false,
|
||||
hardLimit: false,
|
||||
totalQuota: 2000,
|
||||
usedQuota: 1900
|
||||
},
|
||||
creditsLeft: 100
|
||||
};
|
||||
|
||||
var message = AnalysesQuotaPresenter.make(payload, configModel, userModel);
|
||||
expect(message.body).toBe('editor.layers.analysis-form.quota.soft-limit-enough-quota');
|
||||
expect(message.type).toBe('alert');
|
||||
});
|
||||
|
||||
it('Soft limit, not quota', function () {
|
||||
var payload = {
|
||||
estimation: 127,
|
||||
quotaInfo: {
|
||||
blockPrice: 0,
|
||||
blockSize: 1000,
|
||||
enoughQuota: false,
|
||||
hardLimit: false,
|
||||
totalQuota: 2000,
|
||||
usedQuota: 2000
|
||||
},
|
||||
creditsLeft: 0
|
||||
};
|
||||
|
||||
var message = AnalysesQuotaPresenter.make(payload, configModel, userModel);
|
||||
expect(message.body).toBe('editor.layers.analysis-form.quota.soft-limit-enough-quota');
|
||||
expect(message.type).toBe('alert');
|
||||
});
|
||||
|
||||
it('Soft limit, not quota assigned', function () {
|
||||
var payload = {
|
||||
estimation: 127,
|
||||
quotaInfo: {
|
||||
blockPrice: 0,
|
||||
blockSize: 1000,
|
||||
enoughQuota: false,
|
||||
hardLimit: false,
|
||||
totalQuota: 0,
|
||||
usedQuota: 0
|
||||
},
|
||||
creditsLeft: 0
|
||||
};
|
||||
|
||||
var message = AnalysesQuotaPresenter.make(payload, configModel, userModel);
|
||||
expect(message.body).toBe('editor.layers.analysis-form.quota.no-quota-assigned-body');
|
||||
expect(message.type).toBe('alert');
|
||||
});
|
||||
});
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
mock: function (obj, type, response) {
|
||||
obj.SQL.execute = function (query, vars, params) {
|
||||
var callback = params && params[type] || params.success;
|
||||
callback(response);
|
||||
};
|
||||
}
|
||||
};
|
||||
+197
@@ -0,0 +1,197 @@
|
||||
var Backbone = require('backbone');
|
||||
var CoreView = require('backbone/core-view');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var AnalysesService = require('builder/editor/layers/layer-content-views/analyses/analyses-service');
|
||||
var StackLayoutView = require('builder/components/stack-layout/stack-layout-view');
|
||||
var ModalsService = require('builder/components/modals/modals-service-model');
|
||||
var AddAnalysisView = require('builder/components/modals/add-analysis/add-analysis-view');
|
||||
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
|
||||
|
||||
describe('editor/layers/layer-content-view/analyses/analyses-service', function () {
|
||||
beforeEach(function () {
|
||||
this.configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
|
||||
this.userModel = new UserModel({}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
this.layerDefinitionModel = new LayerDefinitionModel({
|
||||
id: 'l-1',
|
||||
options: {
|
||||
type: 'CartoDB',
|
||||
table_name: 'foo',
|
||||
letter: 'a',
|
||||
source: 'a0'
|
||||
}
|
||||
}, {
|
||||
parse: true,
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
this.onboardings = new Backbone.Model();
|
||||
this.onboardings.create = function () {};
|
||||
this.onboardings.destroy = function () {};
|
||||
|
||||
this.layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
|
||||
configModel: this.configModel,
|
||||
userModel: this.userModel,
|
||||
analysisDefinitionNodesCollection: new Backbone.Collection(),
|
||||
mapId: 'map-123',
|
||||
stateDefinitionModel: {}
|
||||
});
|
||||
|
||||
this.layerDefinitionsCollection.add(this.layerDefinitionModel);
|
||||
|
||||
this.modals = new ModalsService();
|
||||
|
||||
AnalysesService.init({
|
||||
onboardings: this.onboardings,
|
||||
layerDefinitionsCollection: this.layerDefinitionsCollection,
|
||||
modals: this.modals,
|
||||
userModel: this.userModel,
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
var model1 = new Backbone.Model({
|
||||
createStackView: function (stackLayoutModel) {
|
||||
return new CoreView();
|
||||
}
|
||||
});
|
||||
var model2 = new Backbone.Model({
|
||||
createStackView: function (stackLayoutModel) {
|
||||
return new CoreView();
|
||||
}
|
||||
});
|
||||
var collection = new Backbone.Collection([model1, model2]);
|
||||
this._stackLayoutView = new StackLayoutView({
|
||||
collection: collection
|
||||
});
|
||||
});
|
||||
|
||||
describe('.setLayerId', function () {
|
||||
it('should set LayerId', function () {
|
||||
AnalysesService.setLayerId(this.layerDefinitionModel.get('id'));
|
||||
expect(AnalysesService._layerId).toBe('l-1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('.setStackLayoutView', function () {
|
||||
it('should set StackLayoutView', function () {
|
||||
AnalysesService.setStackLayoutView(this._stackLayoutView);
|
||||
expect(AnalysesService._stackLayoutView).toBe(this._stackLayoutView);
|
||||
});
|
||||
});
|
||||
|
||||
describe('.generateGeoreferenceAnalysis', function () {
|
||||
it('should return a georeference analysis definition', function () {
|
||||
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);
|
||||
|
||||
var layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
|
||||
configModel: this.configModel,
|
||||
userModel: this.userModel,
|
||||
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
|
||||
mapId: 'm123',
|
||||
stateDefinitionModel: {}
|
||||
});
|
||||
layerDefinitionsCollection.add(this.layerDefinitionModel);
|
||||
|
||||
AnalysesService.init({
|
||||
onboardings: this.onboardings,
|
||||
layerDefinitionsCollection: layerDefinitionsCollection,
|
||||
modals: this.modals,
|
||||
userModel: this.userModel,
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
AnalysesService.setLayerId(this.layerDefinitionModel.get('id'));
|
||||
|
||||
expect(AnalysesService.generateGeoreferenceAnalysis()).toEqual({
|
||||
source: 'a0',
|
||||
id: 'a1',
|
||||
type: 'georeference-long-lat'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('.addAnalysis', function () {
|
||||
beforeEach(function () {
|
||||
var mockView = new CoreView();
|
||||
spyOn(AddAnalysisView.prototype, 'initialize');
|
||||
spyOn(AddAnalysisView.prototype, 'render').and.returnValue(mockView);
|
||||
|
||||
spyOn(AnalysesService._modals, 'create');
|
||||
spyOn(AnalysesService._modals, 'onDestroyOnce');
|
||||
|
||||
AnalysesService.setLayerId(this.layerDefinitionModel.get('id'));
|
||||
AnalysesService.setStackLayoutView(this._stackLayoutView);
|
||||
AnalysesService.addAnalysis();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
AnalysesService._modals.destroy();
|
||||
});
|
||||
|
||||
it('should open a modal add-analysis-view', function () {
|
||||
expect(AnalysesService._modals.create).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('when modal is closed', function () {
|
||||
beforeEach(function () {
|
||||
AnalysesService.setStackLayoutView(this._stackLayoutView);
|
||||
spyOn(AnalysesService._stackLayoutView.model, 'goToStep');
|
||||
|
||||
this.analysisFormAttrs = {id: 'a1', type: 'buffer'};
|
||||
var destroyOnceArgs = AnalysesService._modals.onDestroyOnce.calls.argsFor(0);
|
||||
destroyOnceArgs[0].call(destroyOnceArgs[1], this.analysisFormAttrs);
|
||||
});
|
||||
|
||||
it('should redirect stack to layer-content', function () {
|
||||
expect(AnalysesService._stackLayoutView.model.goToStep).toHaveBeenCalledWith(1, this.layerDefinitionModel, 'layer-content', 'analyses', this.analysisFormAttrs);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('.saveNotAppliedAnalysis', function () {
|
||||
it('should save the analysis attrs in _notAppliedAnalysis with persisted false', function () {
|
||||
AnalysesService.saveNotAppliedAnalysis({ id: 'someAnalysisId' });
|
||||
|
||||
expect(AnalysesService._notAppliedAnalysis).toEqual({ id: 'someAnalysisId', persisted: false });
|
||||
});
|
||||
});
|
||||
|
||||
describe('.getNotAppliedAnalysis', function () {
|
||||
it('should return the value in _notAppliedAnalysis', function () {
|
||||
var analysisAttrs = { id: 'whatAnAwesomeAnalysis' };
|
||||
AnalysesService._notAppliedAnalysis = analysisAttrs;
|
||||
|
||||
expect(AnalysesService.getNotAppliedAnalysis()).toEqual(analysisAttrs);
|
||||
});
|
||||
});
|
||||
|
||||
describe('.clearNotAppliedAnalysis', function () {
|
||||
it('should set _notAppliedAnalysis to null', function () {
|
||||
AnalysesService._notAppliedAnalysis = { id: 'thisExistsForSure' };
|
||||
|
||||
expect(AnalysesService._notAppliedAnalysis).not.toBeNull();
|
||||
|
||||
AnalysesService.clearNotAppliedAnalysis();
|
||||
|
||||
expect(AnalysesService._notAppliedAnalysis).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
+303
@@ -0,0 +1,303 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var AnalysesView = require('builder/editor/layers/layer-content-views/analyses/analyses-view');
|
||||
var PanelWithOptionsView = require('builder/components/view-options/panel-with-options-view');
|
||||
var UserNotifications = require('builder/data/user-notifications');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var QuerySchemaModel = require('builder/data/query-schema-model');
|
||||
var QueryGeometryModel = require('builder/data/query-geometry-model');
|
||||
var QueryRowsCollection = require('builder/data/query-rows-collection');
|
||||
var OnboardingsServiceModel = require('builder/components/onboardings/onboardings-service-model');
|
||||
var LayerContentModel = require('builder/data/layer-content-model');
|
||||
|
||||
var ONBOARDING_KEY = 'layer-analyses-onboarding';
|
||||
|
||||
describe('editor/layers/layer-content-views/analyses/analyses-view', function () {
|
||||
var analysisFormsCollection;
|
||||
var userActions;
|
||||
var layerDefinitionModel;
|
||||
var getQueryAndCheckStateSpy;
|
||||
var infoboxStateSpy;
|
||||
var initializeBackup = PanelWithOptionsView.prototype.initialize;
|
||||
var renderBackup = PanelWithOptionsView.prototype.render;
|
||||
|
||||
beforeEach(function () {
|
||||
var configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
|
||||
var onboardingNotification = new UserNotifications({}, {
|
||||
key: 'builder',
|
||||
configModel: configModel
|
||||
});
|
||||
|
||||
this.onboardings = new OnboardingsServiceModel();
|
||||
this.onboardings.create = function () {};
|
||||
|
||||
this.editorModel = new Backbone.Model();
|
||||
this.editorModel.isEditing = function () {
|
||||
return false;
|
||||
};
|
||||
|
||||
this.querySchemaModel = new QuerySchemaModel({
|
||||
query: 'SELECT * FROM table',
|
||||
status: 'fetched'
|
||||
}, {
|
||||
configModel: {}
|
||||
});
|
||||
|
||||
spyOn(this.querySchemaModel, 'fetch');
|
||||
|
||||
this.queryGeometryModel = new QueryGeometryModel({
|
||||
query: 'SELECT * FROM table',
|
||||
status: 'fetched',
|
||||
simple_geom: 'point'
|
||||
}, {
|
||||
configModel: {}
|
||||
});
|
||||
spyOn(this.queryGeometryModel, 'fetch');
|
||||
|
||||
this.queryRowsCollection = new QueryRowsCollection([], {
|
||||
configModel: this.configModel,
|
||||
querySchemaModel: this.querySchemaModel
|
||||
});
|
||||
|
||||
spyOn(this.queryRowsCollection, 'fetch');
|
||||
|
||||
layerDefinitionModel = new Backbone.Model();
|
||||
layerDefinitionModel.canBeGeoreferenced = function () { return Promise.resolve(false); };
|
||||
layerDefinitionModel.toggleVisible = function () { };
|
||||
layerDefinitionModel.getAnalysisDefinitionNodeModel = function () {
|
||||
return {
|
||||
querySchemaModel: this.querySchemaModel,
|
||||
queryGeometryModel: this.queryGeometryModel,
|
||||
queryRowsCollection: this.queryRowsCollection
|
||||
};
|
||||
}.bind(this);
|
||||
|
||||
var analysisDefinitionNodesCollection = new Backbone.Collection();
|
||||
analysisDefinitionNodesCollection.isEmpty = function () { return true; };
|
||||
|
||||
userActions = jasmine.createSpyObj('userActions', ['saveLayer']);
|
||||
|
||||
analysisFormsCollection = new Backbone.Collection();
|
||||
analysisFormsCollection.isEmpty = function () {
|
||||
return true;
|
||||
};
|
||||
|
||||
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; };
|
||||
|
||||
var layerContentModel = new LayerContentModel({}, {
|
||||
querySchemaModel: querySchemaModel,
|
||||
queryGeometryModel: queryGeometryModel,
|
||||
queryRowsCollection: queryRowsCollection
|
||||
});
|
||||
|
||||
getQueryAndCheckStateSpy = spyOn(AnalysesView.prototype, '_getQueryAndCheckState');
|
||||
infoboxStateSpy = spyOn(AnalysesView.prototype, '_infoboxState');
|
||||
|
||||
this.view = new AnalysesView({
|
||||
userActions: userActions,
|
||||
analysisFormsCollection: analysisFormsCollection,
|
||||
layerDefinitionModel: layerDefinitionModel,
|
||||
analysisDefinitionNodesCollection: analysisDefinitionNodesCollection,
|
||||
configModel: {},
|
||||
userModel: {},
|
||||
editorModel: this.editorModel,
|
||||
stackLayoutModel: {},
|
||||
onboardings: this.onboardings,
|
||||
onboardingNotification: onboardingNotification,
|
||||
layerContentModel: layerContentModel
|
||||
});
|
||||
|
||||
PanelWithOptionsView.prototype.initialize = function () { };
|
||||
PanelWithOptionsView.prototype.render = function () { return this; };
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
PanelWithOptionsView.prototype.initialize = initializeBackup;
|
||||
PanelWithOptionsView.prototype.render = renderBackup;
|
||||
});
|
||||
|
||||
describe('initialize', function () {
|
||||
it('should create _onboardingNotification', function () {
|
||||
expect(this.view._onboardingNotification).not.toBeUndefined();
|
||||
expect(this.view._onboardingNotification.get('key')).toBe('builder');
|
||||
});
|
||||
});
|
||||
|
||||
describe('.render', function () {
|
||||
it('should render properly', function () {
|
||||
spyOn(this.view, '_launchOnboarding');
|
||||
spyOn(this.view, '_initViews');
|
||||
|
||||
this.view.render();
|
||||
|
||||
expect(this.view._launchOnboarding).toHaveBeenCalled();
|
||||
expect(this.view._initViews).toHaveBeenCalled();
|
||||
expect(infoboxStateSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._initBinds', function () {
|
||||
it('should check and set infobox state when there is a remove or reset in the analyses form collection', function () {
|
||||
analysisFormsCollection.reset([{ id: 'whatever' }]);
|
||||
|
||||
expect(getQueryAndCheckStateSpy).toHaveBeenCalled();
|
||||
|
||||
getQueryAndCheckStateSpy.calls.reset();
|
||||
|
||||
analysisFormsCollection.remove(analysisFormsCollection.at(0));
|
||||
expect(getQueryAndCheckStateSpy.calls.count()).toBe(1);
|
||||
});
|
||||
|
||||
it('should set infobox when layer visibility changes', function () {
|
||||
layerDefinitionModel.set('visible', false);
|
||||
|
||||
expect(infoboxStateSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._initViews', function () {
|
||||
it('should init views', function () {
|
||||
this.view.render();
|
||||
|
||||
expect(_.size(this.view._subviews)).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('_getQueryAndCheckState', function () {
|
||||
describe('analysis forms collection is not empty', function () {
|
||||
it('should do nothing if onboarding was already skipped', function () {
|
||||
getQueryAndCheckStateSpy.and.callThrough();
|
||||
|
||||
analysisFormsCollection.isEmpty = function () {
|
||||
return false;
|
||||
};
|
||||
|
||||
this.view._getQueryAndCheckState();
|
||||
|
||||
expect(infoboxStateSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('_launchOnboarding', function () {
|
||||
it('should do nothing if onboarding was already skipped', function () {
|
||||
this.view._onboardingNotification.setKey(ONBOARDING_KEY, true);
|
||||
spyOn(this.view._onboardingLauncher, 'launch');
|
||||
spyOn(this.view._layerDefinitionModel, 'canBeGeoreferenced');
|
||||
|
||||
this.view._launchOnboarding();
|
||||
|
||||
expect(this.view._layerDefinitionModel.canBeGeoreferenced).not.toHaveBeenCalled();
|
||||
expect(this.view._onboardingLauncher.launch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should do nothing if there is an analysis', function (done) {
|
||||
this.view._onboardingNotification.setKey(ONBOARDING_KEY, false);
|
||||
spyOn(this.view._analysisFormsCollection, 'isEmpty').and.returnValue(false);
|
||||
spyOn(this.view._onboardingLauncher, 'launch');
|
||||
spyOn(this.view._layerDefinitionModel, 'canBeGeoreferenced').and.returnValue(Promise.resolve(true));
|
||||
|
||||
this.view._launchOnboarding();
|
||||
|
||||
setTimeout(function () {
|
||||
expect(this.view._onboardingNotification.getKey(ONBOARDING_KEY)).toEqual(true);
|
||||
expect(this.view._onboardingLauncher.launch).not.toHaveBeenCalled();
|
||||
done();
|
||||
}.bind(this), 0);
|
||||
});
|
||||
|
||||
it('should create and launch onboarding', function (done) {
|
||||
this.view._onboardingNotification.setKey(ONBOARDING_KEY, false);
|
||||
spyOn(this.view._analysisFormsCollection, 'isEmpty').and.returnValue(true);
|
||||
spyOn(this.view._onboardingLauncher, 'launch');
|
||||
spyOn(this.view._layerDefinitionModel, 'canBeGeoreferenced').and.returnValue(Promise.resolve(false));
|
||||
|
||||
this.view._launchOnboarding();
|
||||
|
||||
setTimeout(function () {
|
||||
expect(this.view._onboardingNotification.getKey(ONBOARDING_KEY)).toEqual(false);
|
||||
expect(this.view._onboardingLauncher.launch).toHaveBeenCalled();
|
||||
done();
|
||||
}.bind(this), 0);
|
||||
});
|
||||
|
||||
it('should skip onboading if layer can be geocoded', function (done) {
|
||||
this.view._onboardingNotification.setKey(ONBOARDING_KEY, false);
|
||||
spyOn(this.view._layerDefinitionModel, 'canBeGeoreferenced').and.returnValue(Promise.resolve(true));
|
||||
spyOn(this.view._onboardingNotification, 'setKey');
|
||||
|
||||
this.view._launchOnboarding();
|
||||
|
||||
setTimeout(function () {
|
||||
expect(this.view._onboardingNotification.setKey).toHaveBeenCalledWith(ONBOARDING_KEY, true);
|
||||
done();
|
||||
}.bind(this), 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._infoboxState', function () {
|
||||
beforeEach(function () {
|
||||
infoboxStateSpy.and.callThrough();
|
||||
|
||||
spyOn(this.view, '_isLayerHidden').and.returnValue(false);
|
||||
});
|
||||
|
||||
describe('if layer is hidden', function () {
|
||||
it('should set infobox state', function () {
|
||||
this.view._isLayerHidden.and.returnValue(true);
|
||||
|
||||
this.view._infoboxState();
|
||||
|
||||
expect(this.view._infoboxModel.get('state')).toBe('layer-hidden');
|
||||
expect(this.view._overlayModel.get('visible')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('if is deleting layer', function () {
|
||||
it('should set infobox state', function () {
|
||||
this.view._deleteAnalysisModel.set('analysisId', 'a1');
|
||||
|
||||
this.view._infoboxState();
|
||||
|
||||
expect(this.view._infoboxModel.get('state')).toBe('deleting-analysis');
|
||||
expect(this.view._overlayModel.get('visible')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should set infobox state', function () {
|
||||
this.view._infoboxState();
|
||||
|
||||
expect(this.view._infoboxModel.get('state')).toBe('');
|
||||
expect(this.view._overlayModel.get('visible')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._showHiddenLayer', function () {
|
||||
it('should show layer if is visible', function () {
|
||||
spyOn(layerDefinitionModel, 'toggleVisible');
|
||||
|
||||
this.view._showHiddenLayer();
|
||||
|
||||
expect(layerDefinitionModel.toggleVisible).toHaveBeenCalled();
|
||||
expect(userActions.saveLayer).toHaveBeenCalledWith(layerDefinitionModel);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._isLayerHidden', function () {
|
||||
it('should return true if layer is not visible', function () {
|
||||
layerDefinitionModel.set('visible', false);
|
||||
|
||||
expect(this.view._isLayerHidden()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
var _ = require('underscore');
|
||||
var AnalysisDefinitionNodeModel = require('builder/data/analysis-definition-node-model');
|
||||
var AnalysesWorkflowItemView = require('builder/editor/layers/layer-content-views/analyses/analyses-workflow-item-view');
|
||||
var Router = require('builder/routes/router');
|
||||
|
||||
describe('editor/layers/layer-content-view/analyses/analyses-workflow-item-view', function () {
|
||||
var view, analysisNode;
|
||||
var nodeId = 'a2';
|
||||
|
||||
var createViewFn = function (options) {
|
||||
spyOn(Router, 'navigate');
|
||||
|
||||
analysisNode = new AnalysisDefinitionNodeModel({
|
||||
id: nodeId,
|
||||
type: 'buffer',
|
||||
params: {}
|
||||
}, {
|
||||
configModel: {}
|
||||
});
|
||||
|
||||
view = new AnalysesWorkflowItemView({
|
||||
selectedNodeId: nodeId,
|
||||
analysisNode: analysisNode,
|
||||
layerId: 'l-1'
|
||||
});
|
||||
|
||||
return view;
|
||||
};
|
||||
|
||||
describe('.render', function () {
|
||||
it('should render propertly', function () {
|
||||
view = createViewFn();
|
||||
view.render();
|
||||
|
||||
expect(view.$el.html()).toContain(nodeId);
|
||||
expect(view.$el.html()).toContain('analyses.area-of-influence.title');
|
||||
expect(_.size(view._subviews)).toBe(0); // No tooltip if it's selected
|
||||
});
|
||||
|
||||
it('should not have any leaks', function () {
|
||||
view = createViewFn();
|
||||
view.render();
|
||||
|
||||
expect(view).toHaveNoLeaks();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when is not selected', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
view._selectedNodeId = 'a0';
|
||||
view.render();
|
||||
});
|
||||
|
||||
describe('.render', function () {
|
||||
it('should render propertly', function () {
|
||||
expect(_.size(view._subviews)).toBe(1); // [Tooltip]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when is new', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
analysisNode = undefined;
|
||||
view.render();
|
||||
});
|
||||
|
||||
describe('.render', function () {
|
||||
it('should render propertly', function () {
|
||||
expect(_.size(view._subviews)).toBe(0); // No tooltip if it's new
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('._onNodeClick', function () {
|
||||
it('should call Router.goToAnalysisNode', function () {
|
||||
spyOn(Router, 'goToAnalysisNode');
|
||||
view = createViewFn();
|
||||
|
||||
view._onNodeClick();
|
||||
|
||||
expect(Router.goToAnalysisNode).toHaveBeenCalledWith('l-1', nodeId);
|
||||
});
|
||||
});
|
||||
});
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
var Backbone = require('backbone');
|
||||
var _ = require('underscore');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var AnalysisFormsCollection = require('builder/editor/layers/layer-content-views/analyses/analysis-forms-collection');
|
||||
var AnalysesWorkflowListView = require('builder/editor/layers/layer-content-views/analyses/analyses-workflow-list-view');
|
||||
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
|
||||
var Router = require('builder/routes/router');
|
||||
|
||||
describe('editor/layers/layer-content-view/analyses/analyses-workflow-list-view', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(Router, 'navigate');
|
||||
|
||||
this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
|
||||
configModel: {},
|
||||
userModel: {}
|
||||
});
|
||||
|
||||
this.analysisDefinitionNodesCollection.add({
|
||||
id: 'a1',
|
||||
type: 'source',
|
||||
status: 'ready'
|
||||
});
|
||||
|
||||
this.analysisDefinitionNodesCollection.add({
|
||||
id: 'a2',
|
||||
type: 'source',
|
||||
status: 'ready'
|
||||
});
|
||||
|
||||
this.analysisDefinitionNodesCollection.add({
|
||||
id: 'a3',
|
||||
type: 'source',
|
||||
status: 'ready'
|
||||
});
|
||||
|
||||
this.layerDefModel = new LayerDefinitionModel({
|
||||
id: 'l-1',
|
||||
options: {
|
||||
type: 'CartoDB',
|
||||
table_name: 'foo',
|
||||
source: 'a1',
|
||||
letter: 'a'
|
||||
}
|
||||
}, {
|
||||
parse: true,
|
||||
collection: new Backbone.Collection(),
|
||||
configModel: {}
|
||||
});
|
||||
spyOn(this.layerDefModel, 'getAnalysisDefinitionNodeModel');
|
||||
|
||||
this.analysisFormsCollection = new AnalysisFormsCollection(null, {
|
||||
configModel: {},
|
||||
userActions: {},
|
||||
layerDefinitionModel: this.layerDefModel,
|
||||
analysisSourceOptionsModel: {}
|
||||
});
|
||||
this.analysisFormsCollection.add([
|
||||
{id: 'a3'},
|
||||
{id: 'a2'},
|
||||
{id: 'a1'}
|
||||
]);
|
||||
spyOn(this.analysisFormsCollection, 'deleteNode');
|
||||
|
||||
this.view = new AnalysesWorkflowListView({
|
||||
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
|
||||
analysisFormsCollection: this.analysisFormsCollection,
|
||||
selectedNodeId: 'a2',
|
||||
layerId: this.layerDefModel.id,
|
||||
hideAddButton: false
|
||||
});
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
describe('.render', function () {
|
||||
it('should render correctly', function () {
|
||||
expect(_.size(this.view._subviews)).toBe(4); // [3 x Analysis Node, Tooltip]
|
||||
});
|
||||
|
||||
it('should display add button and the list of analysis', function () {
|
||||
expect(this.view.$('.js-add-analysis').length).toBe(1);
|
||||
expect(this.view.$('.js-add-analysis').data('layer-id')).toEqual('l-1');
|
||||
});
|
||||
|
||||
it('should render an item per node', function () {
|
||||
var content = this.view.$el.html();
|
||||
|
||||
expect(content).toContain('a3');
|
||||
expect(content).toContain('a2');
|
||||
expect(content).toContain('a1');
|
||||
});
|
||||
|
||||
it('should highlight selected node', function () {
|
||||
expect(this.view.$('li:eq(1) .VerticalRadioList-radio .CDB-Radio').attr('checked')).toBe('checked');
|
||||
expect(this.view.$('li:eq(1)').text()).toContain('a2');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a node has failed', function () {
|
||||
it('should have a has-failed class', function () {
|
||||
var analysisNodeModel = this.analysisDefinitionNodesCollection.models[0];
|
||||
analysisNodeModel.set('status', 'failed');
|
||||
this.view.render();
|
||||
|
||||
// The first analysis is the last in the list
|
||||
expect(this.view.$('li:eq(2) .VerticalRadioList-itemInner').hasClass('has-error')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a node is loading', function () {
|
||||
it('should show a loader', function () {
|
||||
var analysisNodeModel = this.analysisDefinitionNodesCollection.models[0];
|
||||
analysisNodeModel.set('status', 'loading');
|
||||
this.view.render();
|
||||
|
||||
// The first analysis is the last in the list
|
||||
expect(this.view.$('li:eq(2)').html()).toContain('CDB-LoaderIcon');
|
||||
});
|
||||
});
|
||||
|
||||
it('should not have any leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
this.view.clean();
|
||||
});
|
||||
});
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
var Backbone = require('backbone');
|
||||
var AnalysisDefinitionNodeModel = require('builder/data/analysis-definition-node-model');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var AnalysisFormsCollection = require('builder/editor/layers/layer-content-views/analyses/analysis-forms-collection');
|
||||
var AnalysesWorkflowView = require('builder/editor/layers/layer-content-views/analyses/analyses-workflow-view');
|
||||
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
|
||||
|
||||
describe('editor/layers/layer-content-view/analyses/analyses-workflow-view', function () {
|
||||
beforeEach(function () {
|
||||
this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
|
||||
configModel: {},
|
||||
userModel: {}
|
||||
});
|
||||
|
||||
this.analysisDefinitionNodesCollection.add({
|
||||
id: 'a1',
|
||||
type: 'source',
|
||||
status: 'ready'
|
||||
});
|
||||
|
||||
this.analysisDefinitionNodesCollection.add({
|
||||
id: 'a2',
|
||||
type: 'source',
|
||||
status: 'ready'
|
||||
});
|
||||
|
||||
this.analysisDefinitionNodesCollection.add({
|
||||
id: 'a3',
|
||||
type: 'source',
|
||||
status: 'ready'
|
||||
});
|
||||
|
||||
this.layerDefModel = new LayerDefinitionModel({
|
||||
id: 'l-1',
|
||||
options: {
|
||||
type: 'CartoDB',
|
||||
table_name: 'foo',
|
||||
source: 'a1',
|
||||
letter: 'a'
|
||||
}
|
||||
}, {
|
||||
parse: true,
|
||||
collection: new Backbone.Collection(),
|
||||
configModel: {}
|
||||
});
|
||||
spyOn(this.layerDefModel, 'getAnalysisDefinitionNodeModel');
|
||||
spyOn(this.layerDefModel, 'findAnalysisDefinitionNodeModel');
|
||||
spyOn(this.layerDefModel, 'canBeGeoreferenced').and.returnValue(false);
|
||||
|
||||
this.nodeDefModel = new AnalysisDefinitionNodeModel({
|
||||
id: 'a2',
|
||||
type: 'buffer',
|
||||
params: {}
|
||||
}, {
|
||||
configModel: {}
|
||||
});
|
||||
|
||||
this.analysisFormsCollection = new AnalysisFormsCollection(null, {
|
||||
configModel: {},
|
||||
userActions: {},
|
||||
layerDefinitionModel: this.layerDefModel,
|
||||
analysisSourceOptionsModel: {}
|
||||
});
|
||||
this.analysisFormsCollection.add([
|
||||
{id: 'a3'},
|
||||
{id: 'a2'},
|
||||
{id: 'a1'}
|
||||
]);
|
||||
|
||||
this.view = new AnalysesWorkflowView({
|
||||
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
|
||||
analysisFormsCollection: this.analysisFormsCollection,
|
||||
selectedNodeId: 'a2',
|
||||
layerDefinitionModel: this.layerDefModel
|
||||
});
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should not have any leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
it('should render when analysis node is added', function () {
|
||||
spyOn(this.view, 'render').and.callThrough();
|
||||
|
||||
this.analysisDefinitionNodesCollection.add({
|
||||
id: 'a5',
|
||||
type: 'source',
|
||||
status: 'ready'
|
||||
});
|
||||
|
||||
expect(this.view.render).not.toHaveBeenCalled();
|
||||
|
||||
// A change in the source should trigger the render
|
||||
this.layerDefModel.set({ source: 'a5' });
|
||||
expect(this.view.render).toHaveBeenCalled();
|
||||
|
||||
// But another change in the source without adding a node should not
|
||||
this.layerDefModel.set({ source: 'a4' });
|
||||
expect(this.view.render.calls.count()).toBe(1);
|
||||
});
|
||||
});
|
||||
+668
@@ -0,0 +1,668 @@
|
||||
var Backbone = require('backbone');
|
||||
var _ = require('underscore');
|
||||
var userActions = require('builder/data/user-actions');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var BaseAnalysisFormModel = require('builder/editor/layers/layer-content-views/analyses/analysis-form-models/base-analysis-form-model');
|
||||
var AnalysisControlsView = require('builder/editor/layers/layer-content-views/analyses/analysis-controls-view');
|
||||
var analyses = require('builder/data/analyses');
|
||||
var AnalysesQuotaOptions = require('builder/editor/layers/layer-content-views/analyses/analyses-quota/analyses-quota-options');
|
||||
var AnalysesQuotaInfo = require('builder/editor/layers/layer-content-views/analyses/analyses-quota/analyses-quota-info');
|
||||
var AnalysesQuotaEnough = require('builder/editor/layers/layer-content-views/analyses/analyses-quota/analyses-quota-enough');
|
||||
var AnalysesQuotaEstimation = require('builder/editor/layers/layer-content-views/analyses/analyses-quota/analyses-quota-estimation-input');
|
||||
var AnalysisFormsCollection = require('builder/editor/layers/layer-content-views/analyses/analysis-forms-collection');
|
||||
var mockSQL = require('./analyses-quota/mock-sql');
|
||||
|
||||
describe('editor/layers/layer-content-view/analyses/analysis-controls-view', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(_, 'debounce').and.callFake(function (func) {
|
||||
return function () {
|
||||
func.apply(this, arguments);
|
||||
};
|
||||
});
|
||||
|
||||
this.configModel = new Backbone.Model({
|
||||
user_name: 'foo',
|
||||
sql_api_template: 'foo',
|
||||
api_key: 'foo'
|
||||
});
|
||||
|
||||
this.querySchemaModel = new Backbone.Model({
|
||||
query: 'SELECT * from foo'
|
||||
});
|
||||
|
||||
this.deleteAnalysisModel = new Backbone.Model({
|
||||
analysisId: null
|
||||
});
|
||||
|
||||
this.quotaInfo = AnalysesQuotaInfo.get(this.configModel);
|
||||
|
||||
mockSQL.mock(this.quotaInfo, 'success', {
|
||||
rows: [
|
||||
{service: 'isolines', monthly_quota: 2000, used_quota: 23, soft_limit: false, provider: 'heremaps'},
|
||||
{service: 'hires_geocoder', monthly_quota: 1000, used_quota: 0, soft_limit: true, provider: 'heremaps'},
|
||||
{service: 'routing', monthly_quota: 1000, used_quota: 0, soft_limit: false, provider: 'mapzen'},
|
||||
{service: 'observatory', monthly_quota: 1000, used_quota: 0, soft_limit: false, provider: 'data observatory'}
|
||||
]
|
||||
});
|
||||
|
||||
this.analysisNode = new Backbone.Model();
|
||||
this.analysisNode.hasFailed = function () {
|
||||
return this.analysisNode.get('status') === 'failed';
|
||||
}.bind(this);
|
||||
this.analysisNode.isDone = function () {
|
||||
return this.analysisNode.get('status') === 'ready';
|
||||
}.bind(this);
|
||||
this.analysisNode.canBeDeletedByUser = function () {
|
||||
return true;
|
||||
};
|
||||
|
||||
this.userModel = new UserModel({
|
||||
username: 'pepe',
|
||||
isolines_provider: 'heremaps',
|
||||
actions: {
|
||||
private_tables: true
|
||||
}
|
||||
}, {
|
||||
configModel: 'c'
|
||||
});
|
||||
|
||||
this.formModel = new BaseAnalysisFormModel({
|
||||
id: 'a1',
|
||||
type: 'routing'
|
||||
}, {
|
||||
analyses: analyses,
|
||||
configModel: {},
|
||||
layerDefinitionModel: {},
|
||||
analysisSourceOptionsModel: {}
|
||||
});
|
||||
|
||||
spyOn(this.formModel, 'isValid').and.returnValue(true);
|
||||
|
||||
this.userActions = userActions({
|
||||
userModel: this.userModel,
|
||||
analysisDefinitionsCollection: {},
|
||||
analysisDefinitionNodesCollection: {},
|
||||
layerDefinitionsCollection: {},
|
||||
widgetDefinitionsCollection: {}
|
||||
});
|
||||
spyOn(this.userActions, 'saveAnalysis');
|
||||
|
||||
this.analysisFormsCollection = new AnalysisFormsCollection(null, {
|
||||
userActions: this.userActions,
|
||||
configModel: this.configModel,
|
||||
layerDefinitionModel: {},
|
||||
analysisSourceOptionsModel: {}
|
||||
});
|
||||
spyOn(this.analysisFormsCollection, 'deleteNode');
|
||||
|
||||
this.analysisFormsCollection.add([{ id: 'a1' }]);
|
||||
|
||||
this.stackLayoutModel = jasmine.createSpyObj('stackLayoutModel', ['prevStep']);
|
||||
|
||||
this._layerDefinitionModel = {
|
||||
canBeGeoreferenced: function () {
|
||||
},
|
||||
hasAnalyses: function () {}
|
||||
};
|
||||
|
||||
spyOn(this._layerDefinitionModel, 'canBeGeoreferenced').and.returnValue(Promise.resolve(true));
|
||||
spyOn(this._layerDefinitionModel, 'hasAnalyses');
|
||||
});
|
||||
|
||||
it('should not fetch quota if no analysis node', function () {
|
||||
spyOn(AnalysisControlsView.prototype, '_fetchQuotaIfNeeded').and.callThrough();
|
||||
|
||||
var view = new AnalysisControlsView({
|
||||
analysisFormsCollection: this.analysisFormsCollection,
|
||||
userModel: this.userModel,
|
||||
userActions: this.userActions,
|
||||
formModel: this.formModel,
|
||||
stackLayoutModel: this.stackLayoutModel,
|
||||
configModel: this.configModel,
|
||||
quotaInfo: this.quotaInfo,
|
||||
querySchemaModel: this.querySchemaModel,
|
||||
layerDefinitionModel: this._layerDefinitionModel,
|
||||
deleteAnalysisModel: this.deleteAnalysisModel
|
||||
});
|
||||
|
||||
expect(view._viewModel).toBeDefined();
|
||||
expect(AnalysisControlsView.prototype._fetchQuotaIfNeeded).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('.initialize', function () {
|
||||
beforeEach(function () {
|
||||
this.view = new AnalysisControlsView({
|
||||
analysisNode: this.analysisNode,
|
||||
analysisFormsCollection: this.analysisFormsCollection,
|
||||
userModel: this.userModel,
|
||||
userActions: this.userActions,
|
||||
formModel: this.formModel,
|
||||
stackLayoutModel: this.stackLayoutModel,
|
||||
configModel: this.configModel,
|
||||
quotaInfo: this.quotaInfo,
|
||||
querySchemaModel: this.querySchemaModel,
|
||||
layerDefinitionModel: this._layerDefinitionModel,
|
||||
deleteAnalysisModel: this.deleteAnalysisModel
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate a viewModel', function () {
|
||||
expect(this.view._viewModel).toBeDefined();
|
||||
expect(this.view._viewModel.get('isNewAnalysis')).toBeFalsy();
|
||||
expect(this.view._viewModel.get('hasChanges')).toBeFalsy();
|
||||
});
|
||||
|
||||
describe('._initViewState', function () {
|
||||
it('should initialize viewState properly', function () {
|
||||
spyOn(this.view, '_setViewState');
|
||||
|
||||
this.view._initViewState();
|
||||
|
||||
expect(this.view._viewState).toBeDefined();
|
||||
expect(this.view._viewState.get('canBeGeoreferenced')).toEqual(false);
|
||||
expect(this.view._setViewState).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should render when form changes', function () {
|
||||
spyOn(this.view, 'render');
|
||||
this.formModel.set('type', 'foo');
|
||||
expect(this.view.render).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should add the track class', function () {
|
||||
this.view.render();
|
||||
expect(this.view.$('.js-save').hasClass('track-apply')).toBeTruthy();
|
||||
expect(this.view.$('.js-save').hasClass('track-routing-analysis')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not have any leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
});
|
||||
|
||||
describe('analysis without quota', function () {
|
||||
beforeEach(function () {
|
||||
this.formModel.set('type', 'centroid');
|
||||
this.view = new AnalysisControlsView({
|
||||
analysisNode: this.analysisNode,
|
||||
analysisFormsCollection: this.analysisFormsCollection,
|
||||
userModel: this.userModel,
|
||||
userActions: this.userActions,
|
||||
formModel: this.formModel,
|
||||
stackLayoutModel: this.stackLayoutModel,
|
||||
configModel: this.configModel,
|
||||
quotaInfo: this.quotaInfo,
|
||||
querySchemaModel: this.querySchemaModel,
|
||||
layerDefinitionModel: this._layerDefinitionModel,
|
||||
deleteAnalysisModel: this.deleteAnalysisModel
|
||||
});
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should render a save button disabled', function () {
|
||||
expect(this.view.$el.html()).toContain('is-disabled');
|
||||
});
|
||||
|
||||
it('should not allow to save', function () {
|
||||
this.view.$('.js-save').click();
|
||||
expect(this.userActions.saveAnalysis).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should have apply label since it is not persisted yet', function () {
|
||||
expect(this.view.$('.js-save').html()).toContain('apply');
|
||||
});
|
||||
|
||||
it('should not render quota info in any case', function () {
|
||||
spyOn(this.view, '_createQuotaView').and.callThrough();
|
||||
spyOn(this.view, '_createButtonsView').and.callThrough();
|
||||
spyOn(AnalysesQuotaOptions, 'requiresQuota').and.returnValue(false);
|
||||
|
||||
spyOn(this.view, '_isAnalysisDone').and.returnValue(false);
|
||||
spyOn(this.view, '_hasChanges').and.returnValue(true);
|
||||
this.view.render();
|
||||
expect(this.view._createQuotaView).not.toHaveBeenCalled();
|
||||
expect(this.view._createButtonsView).toHaveBeenCalled();
|
||||
|
||||
this.view._createButtonsView.calls.reset();
|
||||
this.view._viewModel.attributes.userFetchModelState = 'error';
|
||||
this.view.render();
|
||||
expect(this.view._createQuotaView).not.toHaveBeenCalled();
|
||||
expect(this.view._createButtonsView.calls.count()).toEqual(1);
|
||||
|
||||
this.view._createButtonsView.calls.reset();
|
||||
this.view._viewModel.attributes.userFetchModelState = 'fetching';
|
||||
this.view.render();
|
||||
expect(this.view._createQuotaView).not.toHaveBeenCalled();
|
||||
expect(this.view._createButtonsView.calls.count()).toEqual(1);
|
||||
});
|
||||
|
||||
describe('when form is valid', function () {
|
||||
beforeEach(function () {
|
||||
this.formModel.set('foo', 'just to trigger re-render');
|
||||
});
|
||||
|
||||
it('should enable button when analysis node\'s status is ready', function () {
|
||||
expect(this.view.$el.html()).toContain('is-disabled');
|
||||
this.analysisNode.set('status', 'ready');
|
||||
expect(this.view.$el.html()).not.toContain('is-disabled');
|
||||
});
|
||||
|
||||
describe('when save button is clicked', function () {
|
||||
beforeEach(function () {
|
||||
this.analysisNode.set('status', 'ready');
|
||||
this.view.$('.js-save').click();
|
||||
});
|
||||
|
||||
it('should save changes', function () {
|
||||
expect(this.userActions.saveAnalysis).toHaveBeenCalledWith(this.formModel);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the analysis failed', function () {
|
||||
beforeEach(function () {
|
||||
this.formModel.set('foo', 'just to trigger re-render');
|
||||
});
|
||||
|
||||
it('should enable button when analysis node\'s status is ready', function () {
|
||||
expect(this.view.$el.html()).toContain('is-disabled');
|
||||
this.analysisNode.set('status', 'failed');
|
||||
expect(this.view.$el.html()).not.toContain('is-disabled');
|
||||
});
|
||||
|
||||
describe('when save button is clicked', function () {
|
||||
beforeEach(function () {
|
||||
this.analysisNode.set('status', 'failed');
|
||||
this.view.$('.js-save').click();
|
||||
});
|
||||
|
||||
it('should save changes', function () {
|
||||
expect(this.userActions.saveAnalysis).toHaveBeenCalledWith(this.formModel);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when model is persisted', function () {
|
||||
beforeEach(function () {
|
||||
this.formModel.set('persisted', true);
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should change label to apply', function () {
|
||||
expect(this.view.$('.js-save').html()).toContain('apply');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('analysis with quota', function () {
|
||||
beforeEach(function () {
|
||||
this.formModel.set('type', 'trade-area');
|
||||
this.view = new AnalysisControlsView({
|
||||
analysisNode: this.analysisNode,
|
||||
analysisFormsCollection: this.analysisFormsCollection,
|
||||
userModel: this.userModel,
|
||||
userActions: this.userActions,
|
||||
formModel: this.formModel,
|
||||
stackLayoutModel: this.stackLayoutModel,
|
||||
configModel: this.configModel,
|
||||
quotaInfo: this.quotaInfo,
|
||||
querySchemaModel: this.querySchemaModel,
|
||||
layerDefinitionModel: this._layerDefinitionModel,
|
||||
deleteAnalysisModel: this.deleteAnalysisModel
|
||||
});
|
||||
|
||||
this.quotaInfo.fetch();
|
||||
|
||||
mockSQL.mock(AnalysesQuotaEstimation, 'success', {
|
||||
rows: [{'QUERY PLAN': 'Seq Scan on foo (cost=0.00..6.97 rows=325 width=108)'}]
|
||||
});
|
||||
|
||||
mockSQL.mock(AnalysesQuotaEnough, 'success', {
|
||||
rows: [{'cdb_enough_quota': true}]
|
||||
});
|
||||
});
|
||||
|
||||
it('should not fetch quota when form changes and not valid', function () {
|
||||
spyOn(this.view, '_fetchQuotaIfNeeded').and.callThrough();
|
||||
spyOn(this.view, '_fetchQuota');
|
||||
this.formModel.isValid.and.returnValue(false);
|
||||
this.view.render();
|
||||
|
||||
this.formModel.trigger('change');
|
||||
expect(this.view._fetchQuotaIfNeeded).toHaveBeenCalled();
|
||||
expect(this.view._fetchQuota).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should render loading when fetching', function () {
|
||||
this.view.render();
|
||||
this.view._infoboxModel.set('state', 'fetching');
|
||||
|
||||
expect(this.view.$('.Infobox-wrapper').length).toBe(1);
|
||||
expect(this.view.$('.Infobox .CDB-LoaderIcon').length).toBe(1); // Loading state
|
||||
expect(this.view.$('.Infobox-wrapper').text()).toContain('editor.layers.analysis-form.quota.loading');
|
||||
});
|
||||
|
||||
it('should not let user run the analysis if form is not valid', function () {
|
||||
this.formModel.isValid.and.returnValue(false);
|
||||
this.userActions.saveAnalysis.calls.reset();
|
||||
this.view.render();
|
||||
this.view.$('.js-action').click();
|
||||
expect(this.userActions.saveAnalysis).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not let user run the analysis if user doesn\'t have credit assigned', function () {
|
||||
spyOn(this.view, '_canSave').and.returnValue(true);
|
||||
|
||||
mockSQL.mock(this.quotaInfo, 'success', {
|
||||
rows: [
|
||||
{service: 'isolines', monthly_quota: 0, used_quota: 0, soft_limit: true, provider: 'heremaps'}
|
||||
]
|
||||
});
|
||||
|
||||
mockSQL.mock(AnalysesQuotaEnough, 'success', {
|
||||
rows: [{'cdb_enough_quota': true}]
|
||||
});
|
||||
|
||||
this.userModel.set('here_isolines', {
|
||||
block_price: 1
|
||||
});
|
||||
|
||||
this.userActions.saveAnalysis.calls.reset();
|
||||
this.view.render();
|
||||
this.formModel.trigger('change');
|
||||
|
||||
expect(this.view.$el.html()).toContain('is-disabled');
|
||||
|
||||
this.view.$('.js-action').click();
|
||||
expect(this.userActions.saveAnalysis).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not let user run the analysis if not enough quota', function () {
|
||||
spyOn(this.view, '_canSave').and.returnValue(true);
|
||||
|
||||
mockSQL.mock(this.quotaInfo, 'success', {
|
||||
rows: [
|
||||
{service: 'isolines', monthly_quota: 2000, used_quota: 2000, soft_limit: false, provider: 'heremaps'}
|
||||
]
|
||||
});
|
||||
|
||||
mockSQL.mock(AnalysesQuotaEnough, 'success', {
|
||||
rows: [{'cdb_enough_quota': false}]
|
||||
});
|
||||
|
||||
this.userModel.set('here_isolines', {
|
||||
block_price: 1
|
||||
});
|
||||
|
||||
this.userActions.saveAnalysis.calls.reset();
|
||||
this.formModel.trigger('change');
|
||||
|
||||
expect(this.view.$el.html()).toContain('is-disabled');
|
||||
|
||||
this.view.$('.js-action').click();
|
||||
expect(this.userActions.saveAnalysis).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should let user run the analysis if no credits but soft limit', function () {
|
||||
spyOn(this.view, '_canSave').and.returnValue(true);
|
||||
|
||||
mockSQL.mock(this.quotaInfo, 'success', {
|
||||
rows: [
|
||||
{service: 'isolines', monthly_quota: 2000, used_quota: 2000, soft_limit: false, provider: 'heremaps'}
|
||||
]
|
||||
});
|
||||
|
||||
mockSQL.mock(AnalysesQuotaEnough, 'success', {
|
||||
rows: [{'cdb_enough_quota': true}]
|
||||
});
|
||||
|
||||
this.userModel.set('here_isolines', {
|
||||
block_price: 1
|
||||
});
|
||||
|
||||
this.userActions.saveAnalysis.calls.reset();
|
||||
this.formModel.trigger('change');
|
||||
|
||||
expect(this.view.$el.html()).not.toContain('is-disabled');
|
||||
|
||||
this.view.$('.js-action').click();
|
||||
expect(this.userActions.saveAnalysis).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should change label to confirm', function () {
|
||||
spyOn(this.view, '_canSave').and.returnValue(true);
|
||||
mockSQL.mock(AnalysesQuotaEnough, 'success', {
|
||||
rows: [{'cdb_enough_quota': true}]
|
||||
});
|
||||
|
||||
mockSQL.mock(this.quotaInfo, 'success', {
|
||||
rows: [
|
||||
{service: 'isolines', monthly_quota: 2000, used_quota: 1900, soft_limit: false, provider: 'heremaps'}
|
||||
]
|
||||
});
|
||||
|
||||
this.formModel.trigger('change');
|
||||
|
||||
expect(this.view.$('.js-action').html()).toContain('confirm');
|
||||
});
|
||||
|
||||
it('should save changes', function () {
|
||||
spyOn(this.view, '_canSave').and.returnValue(true);
|
||||
this.analysisNode.set('status', 'ready');
|
||||
|
||||
mockSQL.mock(this.quotaInfo, 'success', {
|
||||
rows: [
|
||||
{service: 'isolines', monthly_quota: 2000, used_quota: 1900, soft_limit: false, provider: 'heremaps'}
|
||||
]
|
||||
});
|
||||
|
||||
this.userModel.set('here_isolines', {
|
||||
block_price: 1
|
||||
});
|
||||
|
||||
this.view.render();
|
||||
this.view.$('.js-save').click();
|
||||
|
||||
expect(this.userActions.saveAnalysis).toHaveBeenCalledWith(this.formModel);
|
||||
});
|
||||
|
||||
it('should change analysis node\'s state to launched when saving', function () {
|
||||
spyOn(this.view, '_canSave').and.returnValue(true);
|
||||
this.analysisNode.set('status', 'ready');
|
||||
|
||||
var nodeChange = jasmine.createSpy('nodeChange');
|
||||
this.analysisNode.on('change:status', nodeChange);
|
||||
|
||||
mockSQL.mock(this.quotaInfo, 'success', {
|
||||
rows: [
|
||||
{service: 'isolines', monthly_quota: 2000, used_quota: 1900, soft_limit: false, provider: 'heremaps'}
|
||||
]
|
||||
});
|
||||
|
||||
this.view.render();
|
||||
this.view.$('.js-save').click();
|
||||
|
||||
// silent when putting launched
|
||||
expect(nodeChange.calls.count()).toBe(0);
|
||||
expect(this.analysisNode.get('status')).toBe('launched');
|
||||
expect(this.userActions.saveAnalysis).toHaveBeenCalledWith(this.formModel);
|
||||
});
|
||||
|
||||
it('render a save button disabled after saved an analysis', function () {
|
||||
this.view._viewModel.set('hasChanges', false);
|
||||
this.view.render();
|
||||
|
||||
this.analysisNode.set('status', 'ready');
|
||||
expect(this.view.$el.html()).toContain('is-disabled');
|
||||
});
|
||||
|
||||
describe('dataservices api not existing', function () {
|
||||
beforeEach(function () {
|
||||
this.quotaInfo._state = 'error';
|
||||
});
|
||||
|
||||
it('should avoid fetching quota', function () {
|
||||
spyOn(this.view, '_fetchQuota');
|
||||
expect(this.view._fetchQuota).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should show an infowindow error if quota view should be shown for edited analysis', function () {
|
||||
spyOn(this.view, '_canSave').and.returnValue(true);
|
||||
this.view.render();
|
||||
|
||||
expect(this.view._infoboxModel.get('state')).toBe('error');
|
||||
expect(this.view.$el.html()).toContain('editor.layers.analysis-form.quota.quota-fetch-error');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('change analysis type', function () {
|
||||
beforeEach(function () {
|
||||
this.formModel.set('type', 'georeference-ip-address');
|
||||
this.view = new AnalysisControlsView({
|
||||
analysisNode: this.analysisNode,
|
||||
analysisFormsCollection: this.analysisFormsCollection,
|
||||
userModel: this.userModel,
|
||||
userActions: this.userActions,
|
||||
formModel: this.formModel,
|
||||
stackLayoutModel: this.stackLayoutModel,
|
||||
configModel: this.configModel,
|
||||
quotaInfo: this.quotaInfo,
|
||||
querySchemaModel: this.querySchemaModel,
|
||||
layerDefinitionModel: this._layerDefinitionModel,
|
||||
deleteAnalysisModel: this.deleteAnalysisModel
|
||||
});
|
||||
|
||||
this.quotaInfo.fetch();
|
||||
|
||||
mockSQL.mock(AnalysesQuotaEstimation, 'success', {
|
||||
rows: [{'QUERY PLAN': 'Seq Scan on foo (cost=0.00..6.97 rows=325 width=108)'}]
|
||||
});
|
||||
|
||||
mockSQL.mock(AnalysesQuotaEnough, 'success', {
|
||||
rows: [{'cdb_enough_quota': true}]
|
||||
});
|
||||
});
|
||||
|
||||
it('should render properly if type changes', function () {
|
||||
spyOn(this.view, '_canSave').and.returnValue(true);
|
||||
this.view.render();
|
||||
|
||||
expect(this.view.$el.html()).not.toContain('is-disabled');
|
||||
expect(this.view.$('.Infobox-wrapper').length).toBe(0);
|
||||
|
||||
this.formModel.set({type: 'georeference-street-address'});
|
||||
|
||||
expect(this.view.$('.Infobox-wrapper').length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when can delete', function () {
|
||||
beforeEach(function () {
|
||||
this.view = new AnalysisControlsView({
|
||||
analysisNode: this.analysisNode,
|
||||
analysisFormsCollection: this.analysisFormsCollection,
|
||||
userModel: this.userModel,
|
||||
userActions: this.userActions,
|
||||
formModel: this.formModel,
|
||||
stackLayoutModel: this.stackLayoutModel,
|
||||
configModel: this.configModel,
|
||||
quotaInfo: this.quotaInfo,
|
||||
querySchemaModel: this.querySchemaModel,
|
||||
layerDefinitionModel: this._layerDefinitionModel,
|
||||
deleteAnalysisModel: this.deleteAnalysisModel
|
||||
});
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should render deletion if can delete', function () {
|
||||
expect(this.view.$el.html()).toContain('js-delete');
|
||||
});
|
||||
|
||||
describe('when click delete-analysis', function () {
|
||||
describe('when the analysis is the last node', function () {
|
||||
it('should delete selectednode', function () {
|
||||
this.view._formModel = this.analysisFormsCollection.first();
|
||||
this.view.$('.js-delete').click();
|
||||
expect(this.analysisFormsCollection.deleteNode).toHaveBeenCalledWith('a1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the analysis is not the last node', function () {
|
||||
it('should set the nodeId in the deleteAnalysisNode model', function () {
|
||||
var newAnalysis = this.analysisFormsCollection.add({ id: 'a2' });
|
||||
this.view._formModel = newAnalysis;
|
||||
this.view.$('.js-delete').click();
|
||||
|
||||
expect(this.analysisFormsCollection.deleteNode).not.toHaveBeenCalled();
|
||||
expect(this.deleteAnalysisModel.get('analysisId')).toEqual('a2');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('delete button', function () {
|
||||
beforeEach(function () {
|
||||
this.view = new AnalysisControlsView({
|
||||
analysisFormsCollection: this.analysisFormsCollection,
|
||||
userModel: this.userModel,
|
||||
userActions: this.userActions,
|
||||
formModel: this.formModel,
|
||||
stackLayoutModel: this.stackLayoutModel,
|
||||
configModel: this.configModel,
|
||||
quotaInfo: this.quotaInfo,
|
||||
querySchemaModel: this.querySchemaModel,
|
||||
layerDefinitionModel: this._layerDefinitionModel,
|
||||
deleteAnalysisModel: this.deleteAnalysisModel
|
||||
});
|
||||
|
||||
spyOn(this.view, '_isAnalysisDone').and.returnValue(true);
|
||||
spyOn(this.view, '_hasChanges').and.returnValue(false);
|
||||
});
|
||||
|
||||
describe('should be disabled', function () {
|
||||
it('if layer needs georeferencing', function (done) {
|
||||
var self = this;
|
||||
this._layerDefinitionModel.canBeGeoreferenced.and.returnValue(Promise.resolve(true));
|
||||
this._layerDefinitionModel.hasAnalyses.and.returnValue(false);
|
||||
|
||||
this.view.render();
|
||||
|
||||
setTimeout(function () {
|
||||
expect(self.view.$('.js-delete').hasClass('is-disabled')).toBe(true);
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('should be enabled', function () {
|
||||
beforeEach(function () {
|
||||
this._layerDefinitionModel.hasAnalyses.and.returnValue(true);
|
||||
});
|
||||
|
||||
it('if layer needs georeferencing but there are analyses already', function (done) {
|
||||
var self = this;
|
||||
this._layerDefinitionModel.canBeGeoreferenced.and.returnValue(Promise.resolve(true));
|
||||
this.view.render();
|
||||
|
||||
setTimeout(function () {
|
||||
expect(self.view.$('.js-delete').hasClass('is-disabled')).toBe(false);
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
it('if layer does not need georeferencing', function (done) {
|
||||
var self = this;
|
||||
this._layerDefinitionModel.canBeGeoreferenced.and.returnValue(Promise.resolve(false));
|
||||
|
||||
this.view.render();
|
||||
|
||||
setTimeout(function () {
|
||||
expect(self.view.$('.js-delete').hasClass('is-disabled')).toBe(false);
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
var Backbone = require('backbone');
|
||||
var camshaftReference = require('builder/data/camshaft-reference');
|
||||
var areaOfInfluenceTemplate = require('builder/editor/layers/layer-content-views/analyses/analysis-form-models/area-of-influence-form.tpl');
|
||||
var BaseAnalysisFormModel = require('builder/editor/layers/layer-content-views/analyses/analysis-form-models/base-analysis-form-model');
|
||||
var AnalysisFormView = require('builder/editor/layers/layer-content-views/analyses/analysis-form-view');
|
||||
var analyses = require('builder/data/analyses');
|
||||
|
||||
describe('editor/layers/layer-content-view/analyses/analysis-form-view', function () {
|
||||
beforeEach(function () {
|
||||
this.formModel = new BaseAnalysisFormModel({
|
||||
id: 'a1',
|
||||
type: 'buffer',
|
||||
source: 'a0',
|
||||
radius: '100'
|
||||
}, {
|
||||
analyses: analyses,
|
||||
configModel: {},
|
||||
layerDefinitionModel: {},
|
||||
analysisSourceOptionsModel: {}
|
||||
});
|
||||
this.formModel.schema.source = {type: 'Text'};
|
||||
this.formModel.schema.radius = {type: 'Number'};
|
||||
spyOn(this.formModel, 'getTemplate').and.returnValue(areaOfInfluenceTemplate);
|
||||
spyOn(this.formModel, 'getTemplateData').and.returnValue({parametersDataFields: 'radius'});
|
||||
spyOn(this.formModel, 'setFormValidationErrors').and.callThrough();
|
||||
spyOn(camshaftReference, 'validate');
|
||||
|
||||
this.view = new AnalysisFormView({
|
||||
formModel: this.formModel,
|
||||
configModel: {}
|
||||
});
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should render with template and data from form model', function () {
|
||||
expect(this.view.$el.html()).toContain('form');
|
||||
expect(this.view.$el.html()).toContain('data-fields="radius"');
|
||||
});
|
||||
|
||||
it('should not validate when view is rendered intially', function () {
|
||||
expect(camshaftReference.validate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('when form changes with erroneous data', function () {
|
||||
beforeEach(function () {
|
||||
camshaftReference.validate.and.returnValue({radius: '42 is not the answer, you fool!'});
|
||||
|
||||
// simulate change
|
||||
this.view._formView.setValue('radius', '42');
|
||||
this.view._formView.trigger('change');
|
||||
});
|
||||
|
||||
it('should show errors when validation fails', function (done) {
|
||||
var self = this;
|
||||
|
||||
setTimeout(function () {
|
||||
expect(camshaftReference.validate).toHaveBeenCalled();
|
||||
expect(self.view.$el.html()).toContain('Error');
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
it('should update form model anyway', function () {
|
||||
expect(this.formModel.get('radius')).toEqual(42);
|
||||
});
|
||||
|
||||
it('should set form validation errors on the model', function (done) {
|
||||
var self = this;
|
||||
|
||||
setTimeout(function () {
|
||||
expect(self.formModel.setFormValidationErrors).toHaveBeenCalled();
|
||||
expect(self.formModel.setFormValidationErrors.calls.argsFor(0)[0]).toBeUndefined();
|
||||
expect(self.formModel.setFormValidationErrors.calls.argsFor(1)[0]).toEqual({radius: jasmine.any(String)});
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
describe('when validation passes', function () {
|
||||
beforeEach(function () {
|
||||
this.formModel.setFormValidationErrors.calls.reset();
|
||||
camshaftReference.validate.and.returnValue(undefined);
|
||||
|
||||
// simulate change
|
||||
this.view._formView.setValue('radius', '20');
|
||||
this.view._formView.trigger('change');
|
||||
});
|
||||
|
||||
it('should remove form validation errors', function () {
|
||||
expect(this.formModel.setFormValidationErrors.calls.argsFor(0)[0]).toBeUndefined();
|
||||
expect(this.formModel.setFormValidationErrors.calls.argsFor(1)[0]).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should update model', function () {
|
||||
expect(this.formModel.get('radius')).toEqual(20);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when schema changes', function () {
|
||||
beforeEach(function () {
|
||||
this.prev$form = this.view.$('form');
|
||||
this.formModel.trigger('changeSchema');
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
this.prev$form = null;
|
||||
});
|
||||
|
||||
it('should re-render the form', function () {
|
||||
expect(this.view.$('form').length).toEqual(1);
|
||||
expect(this.view.$('form')).not.toBe(this.prev$form);
|
||||
});
|
||||
});
|
||||
|
||||
describe('_onChangeAnalysisFormView and _showAnalysisFormErrors', function () {
|
||||
it('_onChangeAnalysisFormView calls _showAnalysisFormErrors with the form view Id that was active when the function was called', function (done) {
|
||||
var self = this;
|
||||
|
||||
spyOn(this.view, '_showAnalysisFormErrors');
|
||||
var formId = this.view._formView.cid;
|
||||
|
||||
this.view._onChangeAnalysisFormView();
|
||||
|
||||
setTimeout(function () {
|
||||
expect(self.view._showAnalysisFormErrors).toHaveBeenCalledWith(formId);
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
it('_showAnalysisFormErrors should not commit the form if the current form is different from the one who called it', function () {
|
||||
var _prevShowFn = this.view._showAnalysisFormErrors;
|
||||
var self = this;
|
||||
spyOn(this.view, '_showAnalysisFormErrors').and.callFake(function (formId) {
|
||||
self.view._formView.cid = 'another-form-808';
|
||||
_prevShowFn.call(this, formId);
|
||||
});
|
||||
spyOn(this.view, '_formView');
|
||||
|
||||
this.view._onChangeAnalysisFormView();
|
||||
|
||||
expect(this.view._formView).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when form is cleaned', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(Backbone.Form.prototype, 'remove').and.callThrough();
|
||||
this.view.clean();
|
||||
});
|
||||
|
||||
it('should remove form when view is cleaned', function () {
|
||||
expect(Backbone.Form.prototype.remove).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not have any leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
});
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
var Backbone = require('backbone');
|
||||
var AnalysisDefinitionNodeModel = require('builder/data/analysis-definition-node-model');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var AnalysisSourceOptionsModel = require('builder/editor/layers/layer-content-views/analyses/analysis-source-options-model');
|
||||
var AnalysisFormsCollection = require('builder/editor/layers/layer-content-views/analyses/analysis-forms-collection');
|
||||
var UserActions = require('builder/data/user-actions');
|
||||
var AnalysesService = require('builder/editor/layers/layer-content-views/analyses/analyses-service');
|
||||
|
||||
describe('builder/editor/layers/layer-content-view/analyses/analysis-forms-collection', function () {
|
||||
beforeEach(function () {
|
||||
var configModel = new Backbone.Model();
|
||||
configModel.dataServiceEnabled = function () {
|
||||
return true;
|
||||
};
|
||||
|
||||
this.analysisDefinitionNodesCollection = new Backbone.Collection();
|
||||
var nodeOpts = {
|
||||
configModel: configModel,
|
||||
collection: this.analysisDefinitionNodesCollection
|
||||
};
|
||||
this.a0 = new AnalysisDefinitionNodeModel({id: 'a0', type: 'trade-area'}, nodeOpts);
|
||||
spyOn(this.a0, 'hasPrimarySource').and.returnValue(false);
|
||||
|
||||
this.a1 = new AnalysisDefinitionNodeModel({id: 'a1', type: 'buffer'}, nodeOpts);
|
||||
spyOn(this.a1, 'hasPrimarySource').and.returnValue(true);
|
||||
spyOn(this.a1, 'getPrimarySource').and.returnValue(this.a0);
|
||||
|
||||
this.a2 = new AnalysisDefinitionNodeModel({id: 'a2', type: 'source'}, nodeOpts);
|
||||
spyOn(this.a2, 'hasPrimarySource').and.returnValue(true);
|
||||
spyOn(this.a2, 'getPrimarySource').and.returnValue(this.a1);
|
||||
|
||||
this.analysisDefinitionNodesCollection.reset([this.a0, this.a1, this.a2]);
|
||||
|
||||
this.layerDefModel = new LayerDefinitionModel({
|
||||
id: 'l-1',
|
||||
options: {
|
||||
type: 'CartoDB',
|
||||
table_name: 'foo',
|
||||
source: 'a2',
|
||||
letter: 'a'
|
||||
}
|
||||
}, {
|
||||
parse: true,
|
||||
collection: new Backbone.Collection(),
|
||||
configModel: configModel
|
||||
});
|
||||
spyOn(this.layerDefModel, 'findAnalysisDefinitionNodeModel').and.returnValue(this.a2);
|
||||
|
||||
this.analysisSourceOptionsModel = new AnalysisSourceOptionsModel(null, {
|
||||
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
|
||||
layerDefinitionsCollection: this.layerDefModel.collection,
|
||||
tablesCollection: new Backbone.Collection()
|
||||
});
|
||||
|
||||
var userModel = new Backbone.Model();
|
||||
userModel.dataServiceEnabled = function () {
|
||||
return true;
|
||||
};
|
||||
|
||||
this.userActions = UserActions({
|
||||
userModel: userModel,
|
||||
analysisDefinitionsCollection: {},
|
||||
analysisDefinitionNodesCollection: {},
|
||||
layerDefinitionsCollection: {},
|
||||
widgetDefinitionsCollection: {}
|
||||
});
|
||||
|
||||
this.collection = new AnalysisFormsCollection(null, {
|
||||
configModel: configModel,
|
||||
layerDefinitionModel: this.layerDefModel,
|
||||
analysisSourceOptionsModel: this.analysisSourceOptionsModel,
|
||||
userActions: this.userActions
|
||||
});
|
||||
});
|
||||
|
||||
describe('.resetByLayerDefinition', function () {
|
||||
beforeEach(function () {
|
||||
this.collection.resetByLayerDefinition();
|
||||
});
|
||||
|
||||
it('should have created a form model for each node (excluding the source)', function () {
|
||||
expect(this.collection.pluck('id')).toEqual(['a2', 'a1']);
|
||||
});
|
||||
|
||||
it('should have same attributes as the node models they represent', function () {
|
||||
expect(this.collection.get('a1').attributes).toEqual(jasmine.objectContaining(this.a1.attributes));
|
||||
expect(this.collection.get('a2').attributes).toEqual(jasmine.objectContaining(this.a2.attributes));
|
||||
});
|
||||
|
||||
it('should mark them as persisted', function () {
|
||||
expect(this.collection.get('a1').attributes).toEqual(jasmine.objectContaining({persisted: true}));
|
||||
expect(this.collection.get('a2').attributes).toEqual(jasmine.objectContaining({persisted: true}));
|
||||
});
|
||||
});
|
||||
|
||||
describe('.addHead', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(this.collection, 'remove').and.callThrough();
|
||||
spyOn(AnalysesService, 'saveNotAppliedAnalysis');
|
||||
|
||||
this.res = this.collection.addHead({id: 'a3'}); // unknown
|
||||
});
|
||||
|
||||
it('should return the created formModel', function () {
|
||||
expect(this.res).toBeDefined();
|
||||
});
|
||||
|
||||
it('should replace any existing node with same id', function () {
|
||||
// if have same id it's because the current head node is a temporary one that wasn't saved, so just replace it
|
||||
expect(this.collection.remove).toHaveBeenCalledWith('a3');
|
||||
});
|
||||
|
||||
it('should prepend new form model', function () {
|
||||
expect(this.collection.first().id).toEqual('a3');
|
||||
});
|
||||
|
||||
it('should call AnalysesService.saveNotAppliedAnalysis with the new form model', function () {
|
||||
expect(AnalysesService.saveNotAppliedAnalysis).toHaveBeenCalledWith({id: 'a3'});
|
||||
});
|
||||
});
|
||||
|
||||
describe('.deleteNode', function () {
|
||||
describe('when given id represents an persisted node', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(this.userActions, 'deleteAnalysisNode');
|
||||
this.collection.deleteNode('a2');
|
||||
});
|
||||
|
||||
it('should destroy the node-definition for given id', function () {
|
||||
expect(this.userActions.deleteAnalysisNode).toHaveBeenCalledWith('a2');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when given id is for a new form model', function () {
|
||||
beforeEach(function () {
|
||||
this.collection.add({id: 'a3'}, {at: 0});
|
||||
this.layerDefModel.findAnalysisDefinitionNodeModel.and.returnValue();
|
||||
this.collection.deleteNode('a3');
|
||||
});
|
||||
|
||||
it('should just remove the form model from the collection', function () {
|
||||
expect(this.collection.pluck('id')).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('for a form model', function () {
|
||||
beforeEach(function () {
|
||||
this.nodeDefModel = this.analysisDefinitionNodesCollection.add({
|
||||
id: 'a1'
|
||||
});
|
||||
this.model = this.collection.add({
|
||||
id: 'a1',
|
||||
type: 'buffer',
|
||||
radius: 100
|
||||
});
|
||||
});
|
||||
|
||||
it('should create a form model', function () {
|
||||
expect(this.model).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
var Backbone = require('backbone');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
|
||||
var TablesCollection = require('builder/data/tables-collection');
|
||||
var TableModel = require('builder/data/table-model');
|
||||
var AnalysisSourceOptionsModel = require('builder/editor/layers/layer-content-views/analyses/analysis-source-options-model');
|
||||
|
||||
describe('builder/editor/layers/layer-content-views/analyses/analysis-source-options-model', function () {
|
||||
beforeEach(function () {
|
||||
var configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
|
||||
spyOn(TableModel.prototype, 'fetch');
|
||||
|
||||
this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
|
||||
configModel: configModel,
|
||||
userModel: {}
|
||||
});
|
||||
|
||||
this.analysisDefinitionNodesCollection.add({
|
||||
id: 'a0',
|
||||
type: 'source',
|
||||
params: {
|
||||
table_name: 'hello',
|
||||
query: 'SELECT * FROM first'
|
||||
}
|
||||
});
|
||||
|
||||
var layerModel = new Backbone.Model({
|
||||
name: 'Layer Name',
|
||||
table_name: 'table_name',
|
||||
source: 'a0',
|
||||
color: '#000'
|
||||
});
|
||||
layerModel.getName = function () { return this.get('name'); };
|
||||
layerModel.getTableName = function () { return this.get('table_name'); };
|
||||
this.layerDefinitionsCollection = new Backbone.Collection(layerModel);
|
||||
this.layerDefinitionsCollection.findOwnerOfAnalysisNode = function (node) {
|
||||
return layerModel;
|
||||
};
|
||||
|
||||
this.tablesCollection = new TablesCollection(null, {
|
||||
configModel: configModel
|
||||
});
|
||||
|
||||
this.model = new AnalysisSourceOptionsModel(null, {
|
||||
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
|
||||
layerDefinitionsCollection: this.layerDefinitionsCollection,
|
||||
tablesCollection: this.tablesCollection
|
||||
});
|
||||
});
|
||||
|
||||
it('should not have any select options just yet', function () {
|
||||
expect(this.model.getSelectOptions('point')).toEqual([]);
|
||||
expect(this.model.getSelectOptions('polygon')).toEqual([]);
|
||||
});
|
||||
|
||||
describe('when analysis are fetching but there are nodes', function () {
|
||||
beforeEach(function () {
|
||||
var nodes = [{
|
||||
label: 'a0',
|
||||
simpleGeometryType: 'polygon',
|
||||
val: 'a0'
|
||||
}];
|
||||
|
||||
this.model.set({
|
||||
nodes_options: nodes,
|
||||
fetching: true
|
||||
});
|
||||
});
|
||||
|
||||
it('should populate nodes from already fetched nodes', function () {
|
||||
expect(this.model.getSelectOptions('polygon')).toEqual([
|
||||
jasmine.objectContaining({
|
||||
val: 'a0',
|
||||
label: 'a0',
|
||||
type: 'node'
|
||||
})
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when analysis are already fetched', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(this.tablesCollection, 'fetch');
|
||||
spyOn(this.analysisDefinitionNodesCollection.first().queryGeometryModel, 'fetch');
|
||||
this.analysisDefinitionNodesCollection.first().queryGeometryModel.set({
|
||||
simple_geom: 'polygon',
|
||||
status: 'fetched'
|
||||
});
|
||||
|
||||
this.model.fetch();
|
||||
|
||||
this.tablesCollection.trigger('sync');
|
||||
expect(this.model.get('fetching')).toBe(false);
|
||||
});
|
||||
|
||||
it('should populate nodes from already fetched nodes', function () {
|
||||
expect(this.model.getSelectOptions('polygon')).toEqual([
|
||||
jasmine.objectContaining({
|
||||
val: 'a0',
|
||||
label: 'a0',
|
||||
type: 'node'
|
||||
})
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when analysis nodes are fetched', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(this.tablesCollection, 'fetch');
|
||||
spyOn(this.analysisDefinitionNodesCollection.first().queryGeometryModel, 'fetch');
|
||||
this.model.fetch();
|
||||
});
|
||||
|
||||
it('should fetch tables reducing the data to be retrieved', function () {
|
||||
expect(this.tablesCollection.fetch.calls.first().args).toContain(
|
||||
{
|
||||
data: Object({
|
||||
show_liked: false,
|
||||
show_stats: false,
|
||||
show_table_size_and_row_count: false,
|
||||
show_permission: false,
|
||||
show_synchronization: false,
|
||||
show_uses_builder_features: false,
|
||||
load_totals: false,
|
||||
per_page: 1000
|
||||
})
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should be in fetching state', function () {
|
||||
expect(this.model.get('fetching')).toBe(true);
|
||||
});
|
||||
|
||||
describe('when all items are fetched successfully', function () {
|
||||
beforeEach(function () {
|
||||
this.tablesCollection.add({
|
||||
name: 'table_with_points',
|
||||
geometry_types: ['ST_POINT']
|
||||
});
|
||||
this.tablesCollection.trigger('sync');
|
||||
expect(this.model.get('fetching')).toBe(true);
|
||||
|
||||
this.analysisDefinitionNodesCollection.first().queryGeometryModel.set({
|
||||
simple_geom: 'polygon',
|
||||
status: 'fetched'
|
||||
});
|
||||
});
|
||||
|
||||
it('should not be fetching anymore', function () {
|
||||
expect(this.model.get('fetching')).toBe(false);
|
||||
});
|
||||
|
||||
it('should have items matching geometry type', function () {
|
||||
expect(this.model.getSelectOptions('polygon')).toEqual([
|
||||
jasmine.objectContaining({
|
||||
val: 'a0',
|
||||
label: 'a0',
|
||||
type: 'node'
|
||||
})
|
||||
]);
|
||||
|
||||
expect(this.model.getSelectOptions('point')).toEqual([
|
||||
jasmine.objectContaining({
|
||||
val: 'table_with_points',
|
||||
label: 'table_with_points',
|
||||
type: 'dataset'
|
||||
})
|
||||
]);
|
||||
});
|
||||
|
||||
it('should have items matching multiple accepted geometries', function () {
|
||||
expect(this.model.getSelectOptions(['polygon', 'point'])).toEqual([
|
||||
jasmine.objectContaining({
|
||||
val: 'a0',
|
||||
label: 'a0',
|
||||
type: 'node'
|
||||
}),
|
||||
jasmine.objectContaining({
|
||||
val: 'table_with_points',
|
||||
label: 'table_with_points',
|
||||
type: 'dataset'
|
||||
})
|
||||
]);
|
||||
});
|
||||
|
||||
it('should have items matching a wildcard geometry', function () {
|
||||
expect(this.model.getSelectOptions(['*'])).toEqual([
|
||||
jasmine.objectContaining({
|
||||
val: 'a0',
|
||||
label: 'a0',
|
||||
type: 'node'
|
||||
}),
|
||||
jasmine.objectContaining({
|
||||
val: 'table_with_points',
|
||||
label: 'table_with_points',
|
||||
type: 'dataset'
|
||||
})
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
+566
@@ -0,0 +1,566 @@
|
||||
var Backbone = require('backbone');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var LayerContentModel = require('builder/data/layer-content-model');
|
||||
var DataContentView = require('builder/editor/layers/layer-content-views/data/data-content-view');
|
||||
var DataColumnsModel = require('builder/editor/layers/layer-content-views/data/data-columns-model');
|
||||
var TableStats = require('builder/components/modals/add-widgets/tablestats');
|
||||
var QuerySchemaModel = require('builder/data/query-schema-model');
|
||||
var QueryGeometryModel = require('builder/data/query-geometry-model');
|
||||
var dataNoGeometryTemplate = require('builder/editor/layers/layer-content-views/data/data-content-nogeometry.tpl');
|
||||
|
||||
describe('editor/layers/layers-content-view/data/data-content-view', function () {
|
||||
var view;
|
||||
var layerDefinitionModel;
|
||||
var layerContentModel;
|
||||
var querySchemaModel;
|
||||
var queryGeometryModel;
|
||||
var columnsModel;
|
||||
var widgetDefinitionsCollection;
|
||||
|
||||
var createViewFn = function (options) {
|
||||
var configModel = new ConfigModel({
|
||||
base_url: '/u/pepe',
|
||||
user_name: 'cdb',
|
||||
api_key: 'foo'
|
||||
});
|
||||
|
||||
var ts = new TableStats({
|
||||
configModel: configModel
|
||||
});
|
||||
|
||||
var overlayModel = new Backbone.Model({
|
||||
visible: false
|
||||
});
|
||||
|
||||
querySchemaModel = new QuerySchemaModel({
|
||||
query: 'SELECT * FROM table',
|
||||
status: 'fetched'
|
||||
}, {
|
||||
configModel: {}
|
||||
});
|
||||
|
||||
queryGeometryModel = new QueryGeometryModel({
|
||||
query: 'SELECT * FROM table',
|
||||
simple_geom: 'point',
|
||||
status: 'fetched'
|
||||
}, {
|
||||
configModel: {}
|
||||
});
|
||||
|
||||
var analysisDefinitionNodeModel = new Backbone.Model({});
|
||||
analysisDefinitionNodeModel.querySchemaModel = querySchemaModel;
|
||||
analysisDefinitionNodeModel.queryGeometryModel = queryGeometryModel;
|
||||
|
||||
layerDefinitionModel = new Backbone.Model({});
|
||||
|
||||
layerDefinitionModel.getAnalysisDefinitionNodeModel = function () {
|
||||
return analysisDefinitionNodeModel;
|
||||
};
|
||||
|
||||
layerDefinitionModel.isDataFiltered = function () {};
|
||||
layerDefinitionModel.canBeGeoreferenced = function () {};
|
||||
|
||||
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
|
||||
});
|
||||
|
||||
var stackLayoutModel = jasmine.createSpyObj('stackLayoutModel', ['goToStep']);
|
||||
|
||||
columnsModel = new DataColumnsModel({}, {
|
||||
layerDefinitionModel: layerDefinitionModel,
|
||||
widgetDefinitionsCollection: widgetDefinitionsCollection,
|
||||
tableStats: ts
|
||||
});
|
||||
|
||||
var m0 = new Backbone.Model({
|
||||
cid: 1,
|
||||
type: 'histogram',
|
||||
selected: false
|
||||
});
|
||||
|
||||
m0.analysisDefinitionNodeModel = function () { return {id: 'a1'}; };
|
||||
|
||||
var m1 = new Backbone.Model({
|
||||
cid: 2,
|
||||
type: 'time-series',
|
||||
selected: false
|
||||
});
|
||||
|
||||
m1.analysisDefinitionNodeModel = function () { return {id: 'a2'}; };
|
||||
|
||||
var m2 = new Backbone.Model({
|
||||
cid: 3,
|
||||
type: 'time-series',
|
||||
selected: false
|
||||
});
|
||||
|
||||
m2.analysisDefinitionNodeModel = function () { return {id: 'a3'}; };
|
||||
|
||||
spyOn(columnsModel, 'getCollection').and.returnValue(new Backbone.Collection());
|
||||
|
||||
var userActions = {
|
||||
saveWidgetOption: function () {
|
||||
return Promise.resolve({id: 'w1'});
|
||||
},
|
||||
updateWidgetsOrder: function () {
|
||||
return Promise.resolve({});
|
||||
}
|
||||
};
|
||||
|
||||
var queryRowsCollection = new Backbone.Collection();
|
||||
queryRowsCollection.hasRepeatedErrors = function () { return false; };
|
||||
|
||||
layerContentModel = new LayerContentModel({}, {
|
||||
querySchemaModel: querySchemaModel,
|
||||
queryGeometryModel: queryGeometryModel,
|
||||
queryRowsCollection: queryRowsCollection
|
||||
});
|
||||
|
||||
view = new DataContentView({
|
||||
widgetDefinitionsCollection: widgetDefinitionsCollection,
|
||||
stackLayoutModel: stackLayoutModel,
|
||||
userActions: userActions,
|
||||
querySchemaModel: querySchemaModel,
|
||||
queryGeometryModel: queryGeometryModel,
|
||||
columnsNumberModel: columnModel,
|
||||
columnsModel: columnsModel,
|
||||
infoboxModel: new Backbone.Model(),
|
||||
overlayModel: overlayModel,
|
||||
layerContentModel: layerContentModel,
|
||||
layerDefinitionModel: layerDefinitionModel
|
||||
});
|
||||
|
||||
view._columnsCollection.add(m0);
|
||||
view._columnsCollection.add(m1);
|
||||
view._columnsCollection.add(m2);
|
||||
|
||||
spyOn(queryGeometryModel, 'hasValueAsync').and.returnValue(Promise.resolve(true));
|
||||
spyOn(layerDefinitionModel, 'isDataFiltered').and.returnValue(Promise.resolve(true));
|
||||
spyOn(layerDefinitionModel, 'canBeGeoreferenced').and.returnValue(Promise.resolve(true));
|
||||
|
||||
return view;
|
||||
};
|
||||
|
||||
describe('initialize', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(DataContentView.prototype, '_initViewState');
|
||||
view = createViewFn();
|
||||
});
|
||||
|
||||
it('should initialize properly', function () {
|
||||
expect(DataContentView.prototype._initViewState).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('.render', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
spyOn(view, '_renderStats');
|
||||
spyOn(view, '_toggleOverlay');
|
||||
|
||||
view.render();
|
||||
});
|
||||
|
||||
it('should render properly', function () {
|
||||
expect(view._renderStats).toHaveBeenCalled();
|
||||
expect(view._toggleOverlay).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should render the "apply" button properly in advanced mode', function () {
|
||||
view.$('.Options-bar .CDB-Toggle.js-input').click();
|
||||
expect(view.$('.js-apply').hasClass('.CDB-Size-small')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should not have any leaks', function () {
|
||||
expect(view).toHaveNoLeaks();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when layer content model has errors', function () {
|
||||
describe('.render', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
spyOn(view, '_isErrored').and.returnValue(true);
|
||||
spyOn(view, '_showError');
|
||||
spyOn(view, '_toggleOverlay');
|
||||
});
|
||||
|
||||
it('should render properly', function () {
|
||||
view.render();
|
||||
|
||||
expect(view._showError).toHaveBeenCalled();
|
||||
expect(view._toggleOverlay).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when data is filtered', function () {
|
||||
describe('.render', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
});
|
||||
|
||||
it('should render properly', function () {
|
||||
view._viewState.set('isDataFiltered', true);
|
||||
|
||||
view.render();
|
||||
|
||||
expect(view.el.innerHTML).toContain(
|
||||
dataNoGeometryTemplate({
|
||||
message: 'editor.layers.warnings.no-data.message',
|
||||
action: 'editor.layers.warnings.no-data.action-message'
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when query geometry model has no value', function () {
|
||||
describe('.render', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
});
|
||||
|
||||
it('should render properly', function () {
|
||||
view._viewState.set('hasGeom', false);
|
||||
|
||||
view.render();
|
||||
|
||||
expect(view.el.innerHTML).toContain(
|
||||
dataNoGeometryTemplate({
|
||||
message: 'editor.data.no-geometry-data.message',
|
||||
action: 'editor.data.no-geometry-data.action-message'
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('._initModels', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
});
|
||||
|
||||
it('should create _columnsCollection', function () {
|
||||
view._columnsCollection = undefined;
|
||||
expect(view._columnsCollection).not.toBeDefined();
|
||||
|
||||
view._initModels();
|
||||
expect(view._columnsCollection).toBeDefined();
|
||||
});
|
||||
|
||||
it('should create model', function () {
|
||||
view.model = undefined;
|
||||
expect(view.model).not.toBeDefined();
|
||||
|
||||
view._initModels();
|
||||
expect(view.model).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._initViewState', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
});
|
||||
|
||||
it('should initialize _viewState model properly', function () {
|
||||
spyOn(view, '_setViewState');
|
||||
view._initViewState();
|
||||
|
||||
expect(view._viewState.get('hasGeom')).toEqual(true);
|
||||
expect(view._viewState.get('isDataFiltered')).toEqual(false);
|
||||
expect(view._viewState.get('canBeGeoreferenced')).toEqual(false);
|
||||
|
||||
expect(view._setViewState).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._initBinds', function () {
|
||||
it('should call _setViewState when layerContentModel:state changes', function () {
|
||||
spyOn(DataContentView.prototype, '_setViewState');
|
||||
view = createViewFn();
|
||||
layerContentModel.set({ state: 'fetched' });
|
||||
|
||||
expect(DataContentView.prototype._setViewState).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call ._handleStats when _columnsModel:render changes', function () {
|
||||
spyOn(DataContentView.prototype, '_handleStats');
|
||||
view = createViewFn();
|
||||
view._columnsModel.set({ render: true });
|
||||
|
||||
expect(DataContentView.prototype._handleStats).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call ._handleWidget when _columnsCollection:selected changes', function () {
|
||||
spyOn(DataContentView.prototype, '_handleWidget');
|
||||
view = createViewFn();
|
||||
view._columnsCollection.at(0).set({ selected: true });
|
||||
|
||||
expect(DataContentView.prototype._handleWidget).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call .render when _overlayModel:visible changes', function () {
|
||||
spyOn(DataContentView.prototype, '_toggleOverlay');
|
||||
view = createViewFn();
|
||||
view._overlayModel.set({ visible: true });
|
||||
|
||||
expect(DataContentView.prototype._toggleOverlay).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call render when _viewState changes', function () {
|
||||
spyOn(DataContentView.prototype, 'render');
|
||||
view = createViewFn();
|
||||
view._viewState.set({ isDataFiltered: true });
|
||||
|
||||
expect(DataContentView.prototype.render).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._isErrored', function () {
|
||||
it('should return layerContentModel isErrored', function () {
|
||||
spyOn(view._layerContentModel, 'isErrored').and.returnValues(false, true);
|
||||
|
||||
expect(view._isErrored()).toBe(false);
|
||||
expect(view._isErrored()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._columnsReady', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
});
|
||||
|
||||
it('should return true if _columnsModel:render is true', function () {
|
||||
view._columnsModel.set({ render: false }, { silent: true });
|
||||
expect(view._columnsReady()).toBe(false);
|
||||
|
||||
view._columnsModel.set({ render: true }, { silent: true });
|
||||
expect(view._columnsReady()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._toggleOverlay', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
});
|
||||
|
||||
it('should toggle overlay', function () {
|
||||
expect(view.$el.hasClass('is-disabled')).toBe(false);
|
||||
|
||||
view._overlayModel.set('visible', true, { silent: true });
|
||||
view._toggleOverlay();
|
||||
|
||||
expect(view.$el.hasClass('is-disabled')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._handleWidget', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
|
||||
spyOn(view._userActions, 'saveWidgetOption')
|
||||
.and.returnValue(Promise.resolve(view._columnsCollection.at(1)));
|
||||
});
|
||||
|
||||
it('should call add a new widget if the model is selected', function (done) {
|
||||
spyOn(view, '_addWidget');
|
||||
|
||||
view.render();
|
||||
|
||||
view._columnsCollection.at(1).set({ selected: true });
|
||||
view._handleWidget(view._columnsCollection.at(1));
|
||||
|
||||
setTimeout(function () {
|
||||
expect(view._addWidget).toHaveBeenCalledWith(view._columnsCollection.at(1));
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
it('should destroy the widget if the model is selected', function (done) {
|
||||
spyOn(view, '_destroyWidget');
|
||||
|
||||
view.render();
|
||||
|
||||
view._handleWidget(view._columnsCollection.at(1));
|
||||
|
||||
setTimeout(function () {
|
||||
expect(view._destroyWidget).toHaveBeenCalledWith(view._columnsCollection.at(1));
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._addWidget', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
|
||||
spyOn(view._userActions, 'saveWidgetOption')
|
||||
.and
|
||||
.returnValue(Promise.resolve(view._columnsCollection.at(0)));
|
||||
});
|
||||
|
||||
it('should set the widget if the model exists', function (done) {
|
||||
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);
|
||||
|
||||
setTimeout(function () {
|
||||
expect(model.set).toHaveBeenCalled();
|
||||
expect(model.get('widget')).toEqual(existingModel);
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
it('should call saveWidget if the model does not exist', function (done) {
|
||||
var newModel = new Backbone.Model();
|
||||
spyOn(view, '_saveWidget');
|
||||
view.render();
|
||||
view._columnsModel.set({ render: true });
|
||||
|
||||
view._saveWidget(newModel);
|
||||
|
||||
setTimeout(function () {
|
||||
expect(view._saveWidget).toHaveBeenCalledWith(newModel);
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._saveWidget', function () {
|
||||
var newModel;
|
||||
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
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();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
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();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
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({}));
|
||||
|
||||
spyOn(view, 'render');
|
||||
view.render();
|
||||
view._saveWidget(newModel);
|
||||
|
||||
setTimeout(function () {
|
||||
expect(view.render).toHaveBeenCalledTimes(2);
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
it('should manage time-series properly', function (done) {
|
||||
spyOn(view._userActions, 'saveWidgetOption')
|
||||
.and
|
||||
.returnValue(Promise.resolve(view._columnsCollection.at(2)));
|
||||
|
||||
view.render();
|
||||
|
||||
spyOn(view, '_renderStats').and.callThrough();
|
||||
spyOn(view, '_normalizeTimeSeriesColumn').and.callThrough();
|
||||
|
||||
view._columnsModel.set({ render: true });
|
||||
view._columnsCollection.at(2).set({ selected: true });
|
||||
|
||||
setTimeout(function () {
|
||||
expect(view._renderStats).toHaveBeenCalled();
|
||||
expect(view._normalizeTimeSeriesColumn).toHaveBeenCalled();
|
||||
expect(view._columnsCollection.at(1).get('selected')).toBe(false);
|
||||
expect(view._columnsCollection.at(1).get('widget')).toBeFalsy();
|
||||
expect(view._columnsCollection.at(2).get('widget')).toBeTruthy();
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._destroyWidget', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._setViewState', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
view._viewState.set({
|
||||
hasGeom: false,
|
||||
canBeGeoreferenced: false,
|
||||
isDataFiltered: false
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to update the view state of hasGeom', function (done) {
|
||||
setTimeout(function () {
|
||||
expect(view._viewState.get('hasGeom')).toEqual(true);
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,523 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var LayerContentModel = require('builder/data/layer-content-model');
|
||||
var QuerySchemaModel = require('builder/data/query-schema-model');
|
||||
var QueryGeometryModel = require('builder/data/query-geometry-model');
|
||||
var SQLModel = require('builder/editor/layers/layer-content-views/data/data-sql-model');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var VisDefinitionModel = require('builder/data/vis-definition-model');
|
||||
var Notifier = require('builder/components/notifier/notifier.js');
|
||||
var DataView = require('builder/editor/layers/layer-content-views/data/data-view');
|
||||
var SQLNotifications = require('builder/sql-notifications.js');
|
||||
var UserNotifications = require('builder/data/user-notifications');
|
||||
var ONBOARDING_KEY = 'layer-data-onboarding';
|
||||
|
||||
describe('editor/layers/layers-content-view/data/data-view', function () {
|
||||
var canBeGeoreferencedPromise;
|
||||
|
||||
var createViewFn = function (options) {
|
||||
options = options || {};
|
||||
|
||||
this.sqlModel = new SQLModel({
|
||||
content: 'SELECT * FROM table'
|
||||
});
|
||||
|
||||
this.configModel = new ConfigModel({
|
||||
sql_api_template: 'http://{user}.localhost.lan:8080',
|
||||
user_name: 'pepito',
|
||||
api_key: 'hello-apikey'
|
||||
});
|
||||
|
||||
this.node = new Backbone.Model({
|
||||
type: 'source'
|
||||
});
|
||||
this.node.getDefaultQuery = function () {
|
||||
return 'SELECT * FROM table';
|
||||
};
|
||||
this.node.querySchemaModel = new QuerySchemaModel({
|
||||
query: 'SELECT * FROM table',
|
||||
status: 'fetched'
|
||||
}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
this.node.queryGeometryModel = new QueryGeometryModel({
|
||||
query: 'SELECT * FROM table',
|
||||
status: 'fetched',
|
||||
simple_geom: 'point'
|
||||
}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
this.stackLayoutModel = jasmine.createSpyObj('stackLayoutModel', ['goToStep']);
|
||||
|
||||
this.editorModel = new Backbone.Model();
|
||||
this.editorModel.isEditing = function () { return false; };
|
||||
this.editorModel.isDisabled = function () { return false; };
|
||||
this.userActions = jasmine.createSpyObj('userActions', ['saveAnalysisSourceQuery', 'saveLayer']);
|
||||
|
||||
this.visDefinitionModel = new VisDefinitionModel({
|
||||
name: 'Foo Map',
|
||||
privacy: 'PUBLIC',
|
||||
updated_at: '2016-06-21T15:30:06+00:00',
|
||||
type: 'derived'
|
||||
}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
if (!options.canBeGeoreferencedPromise) {
|
||||
this.layerDefinitionModel = new Backbone.Model({
|
||||
user_name: 'pepito'
|
||||
});
|
||||
this.layerDefinitionModel.isDataFiltered = function () {
|
||||
return Promise.resolve(true);
|
||||
};
|
||||
|
||||
this.layerDefinitionModel.getAnalysisDefinitionNodeModel = function () {
|
||||
return this.node;
|
||||
}.bind(this);
|
||||
this.layerDefinitionModel.getTableName = function () {
|
||||
return 'table';
|
||||
};
|
||||
this.layerDefinitionModel.canBeGeoreferenced = function () {};
|
||||
this.layerDefinitionModel.toggleVisible = function () { };
|
||||
|
||||
this.userModel = new UserModel({
|
||||
username: 'pepe'
|
||||
}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
this.layerDefinitionModel.sqlModel = this.sqlModel;
|
||||
|
||||
canBeGeoreferencedPromise = spyOn(this.layerDefinitionModel, 'canBeGeoreferenced');
|
||||
canBeGeoreferencedPromise.and.returnValue(Promise.resolve(true));
|
||||
}
|
||||
|
||||
Notifier.init({
|
||||
editorModel: this.editorModel,
|
||||
visDefinitionModel: this.visDefinitionModel
|
||||
});
|
||||
|
||||
this.onboardingNotification = new UserNotifications({}, {
|
||||
key: 'builder',
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
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
|
||||
});
|
||||
|
||||
return new DataView({
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
stackLayoutModel: this.stackLayoutModel,
|
||||
widgetDefinitionsCollection: new Backbone.Collection(),
|
||||
editorModel: this.editorModel,
|
||||
userModel: this.userModel,
|
||||
userActions: this.userActions,
|
||||
configModel: this.configModel,
|
||||
onboardings: {},
|
||||
onboardingNotification: this.onboardingNotification,
|
||||
layerContentModel: this.layerContentModel
|
||||
});
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
jasmine.Ajax.install();
|
||||
|
||||
spyOn(SQLNotifications, 'showErrorNotification');
|
||||
spyOn(SQLNotifications, 'removeNotification');
|
||||
spyOn(SQLNotifications, '_addOrUpdateNotification');
|
||||
|
||||
this.view = createViewFn.call(this);
|
||||
|
||||
spyOn(this.view._SQL, 'execute').and.callFake(function (query, vars, params) {
|
||||
params.success();
|
||||
});
|
||||
|
||||
spyOn(this.view._onboardingLauncher, 'launch');
|
||||
|
||||
this.view._widgetDefinitionsCollection.isThereTimeSeries = function () {
|
||||
return false;
|
||||
};
|
||||
spyOn(this.view, 'clean');
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
Notifier.off();
|
||||
jasmine.Ajax.uninstall();
|
||||
});
|
||||
|
||||
describe('.render', function () {
|
||||
it('should render properly', function () {
|
||||
expect(_.size(this.view._subviews)).toBe(1);
|
||||
});
|
||||
|
||||
it('render should call _launchOnboarding', function () {
|
||||
spyOn(this.view, '_launchOnboarding');
|
||||
|
||||
this.view.render();
|
||||
|
||||
expect(this.view._launchOnboarding).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('render should call _initViews', function () {
|
||||
spyOn(this.view, '_initViews');
|
||||
|
||||
this.view.render();
|
||||
|
||||
expect(this.view._initViews).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not have leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
});
|
||||
|
||||
it('should create internal codemirror model', function () {
|
||||
expect(this.view._codemirrorModel).toBeDefined();
|
||||
});
|
||||
|
||||
it('should create _onboardingNotification', function () {
|
||||
expect(this.view._onboardingNotification).not.toBe(null);
|
||||
});
|
||||
|
||||
describe('._initBinds', function () {
|
||||
it('should set codemirror value when query schema model changes', function () {
|
||||
spyOn(this.view._codemirrorModel, 'set');
|
||||
this.node.querySchemaModel.set('query_errors', ['Syntax error']);
|
||||
expect(this.view._codemirrorModel.set).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should update codemirror if query schema model changes', function () {
|
||||
spyOn(this.view._codemirrorModel, 'set');
|
||||
this.view._querySchemaModel.set({query: 'SELECT * FROM table LIMIT 10'});
|
||||
expect(this.view._codemirrorModel.set).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should set codemirror value when undo or redo is applied', function () {
|
||||
this.sqlModel.set('content', 'SELECT foo FROM table');
|
||||
spyOn(this.view._codemirrorModel, 'set');
|
||||
this.sqlModel.undo();
|
||||
expect(this.view._codemirrorModel.set).toHaveBeenCalled();
|
||||
this.view._codemirrorModel.set.calls.reset();
|
||||
this.sqlModel.redo();
|
||||
expect(this.view._codemirrorModel.set).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should sync the query status with the apply button status', function () {
|
||||
this.view._querySchemaModel.set('status', 'fetching');
|
||||
expect(this.view._applyButtonStatusModel.get('loading')).toBe(true);
|
||||
|
||||
this.view._querySchemaModel.set('status', 'fetched');
|
||||
expect(this.view._applyButtonStatusModel.get('loading')).toBe(false);
|
||||
|
||||
this.view._querySchemaModel.set('status', 'unavailable');
|
||||
expect(this.view._applyButtonStatusModel.get('loading')).toBe(false);
|
||||
|
||||
this.view._querySchemaModel.set('status', 'unfetched');
|
||||
expect(this.view._applyButtonStatusModel.get('loading')).toBe(false);
|
||||
});
|
||||
|
||||
it('should call ._onChangeEdition when _editorModel:edition changes', function () {
|
||||
spyOn(this.view, '_onChangeEdition');
|
||||
|
||||
this.view._initBinds();
|
||||
this.view._editorModel.trigger('change:edition');
|
||||
|
||||
expect(this.view._onChangeEdition).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call ._onChangeDisabled when _editorModel:disabled changes', function () {
|
||||
spyOn(this.view, '_onChangeDisabled');
|
||||
|
||||
this.view._initBinds();
|
||||
this.view._editorModel.trigger('change:disabled');
|
||||
|
||||
expect(this.view._onChangeDisabled).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call ._onTogglerChanged when _togglerModel:active changes', function () {
|
||||
spyOn(this.view, '_onTogglerChanged');
|
||||
|
||||
this.view._initBinds();
|
||||
this.view._togglerModel.trigger('change:active');
|
||||
|
||||
expect(this.view._onTogglerChanged).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._initModels', function () {
|
||||
it('should create _infoboxModel', function () {
|
||||
this.view._infoboxModel = undefined;
|
||||
expect(this.view._infoboxModel).not.toBeDefined();
|
||||
|
||||
this.view._initModels();
|
||||
expect(this.view._infoboxModel).toBeDefined();
|
||||
});
|
||||
|
||||
it('should change the _infoboxModel state to georeference if can be georeferenced', function (done) {
|
||||
spyOn(this.view, '_isLayerHidden').and.returnValue(false);
|
||||
|
||||
this.view = createViewFn.call(this);
|
||||
|
||||
setTimeout(function () {
|
||||
expect(this.view._infoboxModel.get('state')).toEqual('georeference');
|
||||
done();
|
||||
}.bind(this), 0);
|
||||
});
|
||||
|
||||
it('should change the _infoboxModel state to empty if it can not be georeferenced', function (done) {
|
||||
spyOn(this.view, '_isLayerHidden').and.returnValue(false);
|
||||
canBeGeoreferencedPromise.and.returnValue(Promise.resolve(false));
|
||||
|
||||
this.view = createViewFn.call(this, { canBeGeoreferencedPromise: true });
|
||||
|
||||
setTimeout(function () {
|
||||
expect(this.view._infoboxModel.get('state')).not.toEqual('georeference');
|
||||
done();
|
||||
}.bind(this), 0);
|
||||
});
|
||||
|
||||
it('should change the _infoboxModel to georeference if the layer is not hidden', function (done) {
|
||||
spyOn(this.view, '_isLayerHidden').and.returnValue(false);
|
||||
|
||||
this.view = createViewFn.call(this);
|
||||
|
||||
setTimeout(function () {
|
||||
expect(this.view._infoboxModel.get('state')).toEqual('georeference');
|
||||
done();
|
||||
}.bind(this), 0);
|
||||
});
|
||||
|
||||
it('should not the _infoboxModel state to georeference if the layer is hidden', function (done) {
|
||||
spyOn(this.view, '_isLayerHidden').and.returnValue(true);
|
||||
canBeGeoreferencedPromise.and.returnValue(Promise.resolve(false));
|
||||
|
||||
this.view = createViewFn.call(this, { canBeGeoreferencedPromise: true });
|
||||
|
||||
setTimeout(function () {
|
||||
expect(this.view._infoboxModel.get('state')).not.toEqual('georeference');
|
||||
done();
|
||||
}.bind(this), 0);
|
||||
});
|
||||
|
||||
it('should create _overlayModel', function () {
|
||||
this.view._overlayModel = undefined;
|
||||
expect(this.view._overlayModel).not.toBeDefined();
|
||||
|
||||
this.view._initModels();
|
||||
expect(this.view._overlayModel).toBeDefined();
|
||||
});
|
||||
|
||||
it('should create _applyButtonStatusModel', function () {
|
||||
this.view._applyButtonStatusModel = undefined;
|
||||
expect(this.view._applyButtonStatusModel).not.toBeDefined();
|
||||
|
||||
this.view._initModels();
|
||||
expect(this.view._applyButtonStatusModel).toBeDefined();
|
||||
});
|
||||
|
||||
it('should create _togglerModel', function () {
|
||||
this.view._togglerModel = undefined;
|
||||
expect(this.view._togglerModel).not.toBeDefined();
|
||||
|
||||
this.view._initModels();
|
||||
expect(this.view._togglerModel).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
it('should apply default query and refresh map and dataset view when query alters data', function () {
|
||||
jasmine.Ajax.stubRequest(new RegExp('^http(s)?.*'))
|
||||
.andReturn({ status: 200 });
|
||||
|
||||
var originalQuery = 'SELECT * FROM table';
|
||||
spyOn(this.editorModel, 'isEditing').and.returnValue(true);
|
||||
spyOn(this.node.querySchemaModel, 'resetDueToAlteredData');
|
||||
this.view._codemirrorModel.set('content', 'DELETE FROM table WHERE cartodb_id=2');
|
||||
this.view._parseSQL();
|
||||
expect(this.view._codemirrorModel.get('content')).toBe(originalQuery);
|
||||
expect(this.userActions.saveAnalysisSourceQuery).toHaveBeenCalled();
|
||||
expect(this.node.querySchemaModel.get('query_errors').length).toBe(0);
|
||||
expect(this.node.querySchemaModel.get('query')).toBe(originalQuery);
|
||||
expect(this.node.querySchemaModel.resetDueToAlteredData).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should apply error from the SQL api', function () {
|
||||
jasmine.Ajax.stubRequest(new RegExp('^http(s)?.*'))
|
||||
.andReturn({status: 429, responseText: '{"error":["You are over platform\'s limits. Please contact us to know more details"]}'});
|
||||
|
||||
spyOn(this.editorModel, 'isEditing').and.returnValue(true);
|
||||
spyOn(this.node.querySchemaModel, 'resetDueToAlteredData');
|
||||
spyOn(this.node.querySchemaModel, 'canFetch').and.returnValue(true);
|
||||
spyOn(this.view, '_forceErrors');
|
||||
this.view._codemirrorModel.set('content', 'SELECT *, pg_sleep(1) FROM table');
|
||||
this.view._parseSQL();
|
||||
|
||||
expect(this.userActions.saveAnalysisSourceQuery).not.toHaveBeenCalled();
|
||||
expect(this.node.querySchemaModel.get('query_errors').length).toBe(1);
|
||||
expect(this.view._forceErrors).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should fetch geometry when a new query is applied', function () {
|
||||
this.view._codemirrorModel.set('content', 'SELECT * FROM table LIMIT 10');
|
||||
spyOn(this.node.queryGeometryModel, 'fetch');
|
||||
spyOn(this.node.querySchemaModel, 'fetch');
|
||||
this.view._parseSQL();
|
||||
expect(this.node.queryGeometryModel.hasChanged('simple_geom')).toBeFalsy();
|
||||
expect(this.node.queryGeometryModel.get('query')).toBe('SELECT * FROM table LIMIT 10');
|
||||
expect(this.node.queryGeometryModel.fetch).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not let run the same query that is applied', function () {
|
||||
var originalQuery = 'SELECT * FROM table';
|
||||
spyOn(this.node.querySchemaModel, 'fetch');
|
||||
this.node.querySchemaModel.set('query', originalQuery);
|
||||
this.view._codemirrorModel.set('content', originalQuery);
|
||||
this.view._parseSQL();
|
||||
expect(this.node.querySchemaModel.fetch).not.toHaveBeenCalled();
|
||||
this.view._codemirrorModel.set('content', originalQuery.toUpperCase());
|
||||
this.view._parseSQL();
|
||||
expect(this.node.querySchemaModel.fetch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not apply an empty query', function () {
|
||||
spyOn(this.node.querySchemaModel, 'fetch');
|
||||
this.view._codemirrorModel.set('content', '');
|
||||
this.view._parseSQL();
|
||||
expect(this.node.querySchemaModel.fetch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not set a query with a ; character at the end', function () {
|
||||
spyOn(this.node.querySchemaModel, 'fetch');
|
||||
this.view._codemirrorModel.set('content', 'select * from whatever;');
|
||||
this.view._parseSQL();
|
||||
expect(this.view._codemirrorModel.get('content')).toBe('select * from whatever');
|
||||
expect(this.node.querySchemaModel.fetch).toHaveBeenCalled();
|
||||
|
||||
this.view._codemirrorModel.set('content', 'select * from whatever; hello');
|
||||
this.view._parseSQL();
|
||||
expect(this.view._codemirrorModel.get('content')).toBe('select * from whatever; hello');
|
||||
});
|
||||
|
||||
it('should not throw notification if same query that is applied', function () {
|
||||
SQLNotifications._addOrUpdateNotification.calls.reset();
|
||||
spyOn(this.node.querySchemaModel, 'fetch');
|
||||
this.view._sqlModel.set('content', 'SELECT * FROM table');
|
||||
this.view._codemirrorModel.set('content', 'SELECT * + FROM table');
|
||||
this.view._parseSQL();
|
||||
expect(this.node.querySchemaModel.fetch).toHaveBeenCalled();
|
||||
|
||||
this.node.querySchemaModel.set('query', 'SELECT * FROM table');
|
||||
this.node.querySchemaModel.set('query_errors', ['Syntax error']);
|
||||
this.view._codemirrorModel.set('content', 'SELECT * FROM table');
|
||||
this.node.querySchemaModel.set('query_errors', []);
|
||||
this.view._parseSQL();
|
||||
|
||||
expect(SQLNotifications.showErrorNotification).toHaveBeenCalledTimes(1);
|
||||
expect(SQLNotifications.removeNotification).toHaveBeenCalledTimes(1);
|
||||
expect(SQLNotifications._addOrUpdateNotification).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
describe('_launchOnboarding', function () {
|
||||
it('should do nothing if onboarding was skipped', function () {
|
||||
this.view._onboardingNotification.setKey(ONBOARDING_KEY, true);
|
||||
this.view._onboardingLauncher.launch.calls.reset();
|
||||
|
||||
this.view._launchOnboarding();
|
||||
|
||||
expect(this.view._onboardingLauncher.launch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should do nothing if we are on editing mode', function () {
|
||||
this.view._onboardingNotification.setKey(ONBOARDING_KEY, false);
|
||||
this.view._editorModel.isEditing = function () { return true; };
|
||||
this.view._onboardingLauncher.launch.calls.reset();
|
||||
|
||||
this.view._launchOnboarding();
|
||||
|
||||
expect(this.view._onboardingLauncher.launch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should launch the onboarding', function () {
|
||||
this.view._onboardingNotification.setKey(ONBOARDING_KEY, false);
|
||||
this.view._editorModel.isEditing = function () { return false; };
|
||||
this.view._onboardingLauncher.launch.calls.reset();
|
||||
|
||||
this.view._launchOnboarding();
|
||||
|
||||
expect(this.view._onboardingLauncher.launch).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._infoboxState', function () {
|
||||
beforeEach(function () {
|
||||
this.view._infoboxModel.set('state', '');
|
||||
this.view._overlayModel.set('visible', false);
|
||||
this.view._editorModel.set('edition', false);
|
||||
this.view._codemirrorModel.set('readonly', false);
|
||||
spyOn(this.view, '_hasAnalysisApplied').and.returnValue(false);
|
||||
spyOn(this.view, '_isLayerHidden').and.returnValue(false);
|
||||
});
|
||||
|
||||
describe('if layer is editing and has analysis', function () {
|
||||
it('should set infobox state', function () {
|
||||
this.view._editorModel.set('edition', true);
|
||||
this.view._hasAnalysisApplied.and.returnValue(true);
|
||||
|
||||
this.view._infoboxState();
|
||||
|
||||
expect(this.view._infoboxModel.get('state')).toBe('readonly');
|
||||
expect(this.view._codemirrorModel.get('readonly')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('if layer is hidden', function () {
|
||||
it('should set infobox state', function () {
|
||||
this.view._isLayerHidden.and.returnValue(true);
|
||||
|
||||
this.view._infoboxState();
|
||||
|
||||
expect(this.view._infoboxModel.get('state')).toBe('layer-hidden');
|
||||
expect(this.view._overlayModel.get('visible')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should unset infobox state', function () {
|
||||
this.view._infoboxState();
|
||||
|
||||
expect(this.view._infoboxModel.get('state')).toBe('');
|
||||
expect(this.view._overlayModel.get('visible')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._showHiddenLayer', function () {
|
||||
it('should show layer if is visible', function () {
|
||||
spyOn(this.view._layerDefinitionModel, 'toggleVisible');
|
||||
|
||||
this.view._showHiddenLayer();
|
||||
|
||||
expect(this.view._layerDefinitionModel.toggleVisible).toHaveBeenCalled();
|
||||
expect(this.view._userActions.saveLayer).toHaveBeenCalledWith(this.view._layerDefinitionModel, { shouldPreserveAutoStyle: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe('._isLayerHidden', function () {
|
||||
it('should return true if layer is not visible', function () {
|
||||
this.view._layerDefinitionModel.set('visible', false);
|
||||
|
||||
expect(this.view._isLayerHidden()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,181 @@
|
||||
var Backbone = require('backbone');
|
||||
var _ = require('underscore');
|
||||
var WidgetOptionModel = require('builder/components/modals/add-widgets/widget-option-model');
|
||||
var StatView = require('builder/editor/layers/layer-content-views/data/stat-view');
|
||||
var TableStats = require('builder/components/modals/add-widgets/tablestats');
|
||||
var Router = require('builder/routes/router');
|
||||
|
||||
describe('editor/layers/layers-content-view/data/stat-view', function () {
|
||||
var nullValue = 0.0059;
|
||||
var topCatValue = 0.8534;
|
||||
var view;
|
||||
var graph;
|
||||
|
||||
function createView (withBooleanColumn, withProperBoolean) {
|
||||
var layerDefinitionModel = new Backbone.Model();
|
||||
var analysisDefinitionNodeModel = new Backbone.Model({
|
||||
id: 'a0'
|
||||
});
|
||||
|
||||
var tuples = [{
|
||||
columnModel: new Backbone.Model(),
|
||||
analysisDefinitionNodeModel: analysisDefinitionNodeModel,
|
||||
layerDefinitionModel: layerDefinitionModel
|
||||
}];
|
||||
|
||||
var statModel = new WidgetOptionModel({
|
||||
type: 'category',
|
||||
column: withBooleanColumn ? 'boolean' : 'number',
|
||||
title: 'location of wadus',
|
||||
name: 'location',
|
||||
layer_index: 0,
|
||||
table: 'marias',
|
||||
selected: false,
|
||||
tuples: tuples,
|
||||
widget: new Backbone.Model({ id: 'widget-1' })
|
||||
});
|
||||
|
||||
view = new StatView({
|
||||
statModel: statModel,
|
||||
isVisible: true
|
||||
});
|
||||
|
||||
spyOn(view, 'clean');
|
||||
|
||||
graph = {
|
||||
stats: {
|
||||
freqs: true
|
||||
},
|
||||
getNullsPercentage: function () {
|
||||
return nullValue;
|
||||
},
|
||||
getPercentageInTopCategories: function () {
|
||||
return topCatValue;
|
||||
},
|
||||
getCategory: function () {
|
||||
return 'foo';
|
||||
},
|
||||
getTrues: function () {
|
||||
return withProperBoolean ? 0.45635 : undefined;
|
||||
}
|
||||
};
|
||||
|
||||
spyOn(Router, 'goToWidget');
|
||||
spyOn(TableStats.prototype, 'graphFor').and.callFake(function () {
|
||||
view.model.set({graph: graph});
|
||||
});
|
||||
|
||||
view.render();
|
||||
view.$el.appendTo(document.body);
|
||||
}
|
||||
|
||||
describe('.render', function () {
|
||||
beforeEach(function () {
|
||||
createView();
|
||||
});
|
||||
|
||||
it('should render properly', function () {
|
||||
expect(view.$('.js-checkbox').length).toBe(1);
|
||||
expect(view.$('.js-stat').length).toBe(1);
|
||||
expect(view.$('.js-title').text()).toBe('location of wadus');
|
||||
expect(view.$('.StatsList-tag').text()).toBe('number');
|
||||
expect(_.size(view._subviews)).toBe(0);
|
||||
});
|
||||
|
||||
describe('when _statModel is selected', function () {
|
||||
beforeEach(function () {
|
||||
view._statModel.set('selected', true);
|
||||
});
|
||||
|
||||
it('should render correctly', function () {
|
||||
expect(view.$('.js-help').length).toBe(1);
|
||||
expect(_.size(view._subviews)).toBe(1); // [tooltip]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should select/unselect the widget properly', function () {
|
||||
createView();
|
||||
view.$('.js-checkbox').trigger('click');
|
||||
expect(view._statModel.get('selected')).toBe(true);
|
||||
expect(view.$('.js-style').hasClass('is-hidden')).toBe(false);
|
||||
view.$('.js-checkbox').trigger('click');
|
||||
expect(view._statModel.get('selected')).toBe(false);
|
||||
expect(view.$('.js-style').length).toBe(0);
|
||||
});
|
||||
|
||||
it('should be selected when widget is added', function () {
|
||||
createView();
|
||||
view._statModel.set({selected: true});
|
||||
expect(view.$('.js-checkbox').prop('checked')).toBe(true);
|
||||
});
|
||||
|
||||
it('should go to style widget when clicked on style link', function () {
|
||||
createView();
|
||||
|
||||
view.$('.js-checkbox').trigger('click');
|
||||
expect(view.$('.js-style').hasClass('is-hidden')).toBe(false);
|
||||
|
||||
view.$('.js-style').trigger('click');
|
||||
expect(Router.goToWidget).toHaveBeenCalledWith('widget-1');
|
||||
});
|
||||
|
||||
it('should not have any leaks', function () {
|
||||
createView();
|
||||
expect(view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
describe('_showCategory', function () {
|
||||
it('non-zero null data should be rounded up to two decimal positions.', function () {
|
||||
// Arrange
|
||||
createView();
|
||||
|
||||
// Act
|
||||
view._showCategory(graph);
|
||||
|
||||
// Assert
|
||||
expect(view.$('.js-null')[0].textContent.trim()).toEqual('0.59editor.data.stats.null');
|
||||
expect(view.$('.js-percent')[0].textContent.trim()).toEqual('85.3editor.data.stats.top-cat');
|
||||
});
|
||||
|
||||
it('boolean column proper data should be rounded up to two decimal positions', function () {
|
||||
// Arrange
|
||||
createView(true, true);
|
||||
|
||||
// Act
|
||||
view._showCategory(graph);
|
||||
|
||||
// Arrange
|
||||
expect(view.$('.js-percent')[0].textContent.trim()).toEqual('45.6editor.data.stats.trues');
|
||||
});
|
||||
|
||||
it('boolean column with no proper data should not be rendered', function () {
|
||||
// Arrange
|
||||
createView(true, false);
|
||||
|
||||
// Act
|
||||
view._showCategory(graph);
|
||||
|
||||
// Arrange
|
||||
expect(view.$('.js-percent')[0].textContent.trim()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._isSelected', function () {
|
||||
it('returns the value of _statModel selected', function () {
|
||||
expect(view._isSelected()).toBe(false);
|
||||
view._statModel.set('selected', true);
|
||||
expect(view._isSelected()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
var parent = view.el.parentNode;
|
||||
parent && parent.removeChild(view.el);
|
||||
view.clean();
|
||||
});
|
||||
});
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
var _ = require('underscore');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var InfowindowBaseView = require('builder/editor/layers/layer-content-views/infowindow/infowindow-base-view');
|
||||
var InfowindowDefinitionModel = require('builder/data/infowindow-definition-model');
|
||||
var EditorModel = require('builder/data/editor-model');
|
||||
var ScrollView = require('builder/components/scroll/scroll-view');
|
||||
|
||||
var TEMPLATES = [
|
||||
{
|
||||
value: '',
|
||||
infowindowIcon: require('builder/editor/layers/layer-content-views/infowindow/infowindow-icons/infowindow-none.tpl'),
|
||||
label: _t('editor.layers.infowindow.style.none'),
|
||||
tooltip: _t('editor.layers.infowindow.tooltips.none')
|
||||
}, {
|
||||
value: 'infowindow_light',
|
||||
infowindowIcon: require('builder/editor/layers/layer-content-views/infowindow/infowindow-icons/infowindow-light.tpl'),
|
||||
label: _t('editor.layers.infowindow.style.infowindow_light'),
|
||||
tooltip: _t('editor.layers.infowindow.tooltips.infowindow_light')
|
||||
}
|
||||
];
|
||||
|
||||
describe('editor/layers/layer-content-view/infowindow/infowindow-base-view', function () {
|
||||
var view;
|
||||
var layerDefinitionModel;
|
||||
var editorModel;
|
||||
var model;
|
||||
var renderSpy;
|
||||
var onChangeSpy;
|
||||
var infoboxStateSpy;
|
||||
var onChangeEditionSpy;
|
||||
var onChangeDisabledSpy;
|
||||
var onTogglerChangedSpy;
|
||||
var toggleOverlaySpy;
|
||||
|
||||
var createViewFn = function (options) {
|
||||
layerDefinitionModel = new LayerDefinitionModel({
|
||||
id: 'l-1',
|
||||
fetched: true,
|
||||
options: {
|
||||
type: 'CartoDB',
|
||||
table_name: 'foo',
|
||||
cartocss: 'asd',
|
||||
source: 'a0'
|
||||
},
|
||||
infowindow: {},
|
||||
tooltip: {}
|
||||
}, {
|
||||
parse: true,
|
||||
configModel: {}
|
||||
});
|
||||
|
||||
editorModel = new EditorModel({
|
||||
disabled: false
|
||||
});
|
||||
|
||||
model = new InfowindowDefinitionModel({
|
||||
template: '',
|
||||
template_name: ''
|
||||
}, {
|
||||
configModel: {}
|
||||
});
|
||||
model.hasTemplate = function () { return true; };
|
||||
|
||||
InfowindowBaseView.prototype._initTemplates = function () { return TEMPLATES; };
|
||||
spyOn(ScrollView.prototype, 'render').and.returnValue(this);
|
||||
|
||||
view = new InfowindowBaseView({
|
||||
model: model,
|
||||
editorModel: editorModel,
|
||||
layerDefinitionModel: layerDefinitionModel,
|
||||
userActions: {},
|
||||
querySchemaModel: {}
|
||||
});
|
||||
|
||||
view._templates = TEMPLATES;
|
||||
|
||||
return view;
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
renderSpy = spyOn(InfowindowBaseView.prototype, 'render');
|
||||
onChangeSpy = spyOn(InfowindowBaseView.prototype, '_onChange');
|
||||
infoboxStateSpy = spyOn(InfowindowBaseView.prototype, '_infoboxState');
|
||||
onChangeEditionSpy = spyOn(InfowindowBaseView.prototype, '_onChangeEdition');
|
||||
onChangeDisabledSpy = spyOn(InfowindowBaseView.prototype, '_onChangeDisabled');
|
||||
onTogglerChangedSpy = spyOn(InfowindowBaseView.prototype, '_onTogglerChanged');
|
||||
toggleOverlaySpy = spyOn(InfowindowBaseView.prototype, '_toggleOverlay');
|
||||
|
||||
view = createViewFn();
|
||||
});
|
||||
|
||||
describe('.render', function () {
|
||||
it('should render properly', function () {
|
||||
renderSpy.and.callThrough();
|
||||
|
||||
spyOn(view, '_initViews');
|
||||
|
||||
view.render();
|
||||
|
||||
expect(view._initViews).toHaveBeenCalled();
|
||||
expect(infoboxStateSpy).toHaveBeenCalled();
|
||||
expect(toggleOverlaySpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
renderSpy.and.callThrough();
|
||||
|
||||
view.render();
|
||||
|
||||
expect(view).toHaveNoLeaks();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._initBinds', function () {
|
||||
it('should listen to model change', function () {
|
||||
view.model.set('wadus', true);
|
||||
|
||||
expect(onChangeSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should listen to layerDefinitionModel change visible', function () {
|
||||
layerDefinitionModel.set('visible', false);
|
||||
|
||||
expect(infoboxStateSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should listen to editorModel change edition', function () {
|
||||
view._editorModel.set('edition', true);
|
||||
|
||||
expect(onChangeEditionSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should listen to editorModel change disabled', function () {
|
||||
view._editorModel.set('disabled', true);
|
||||
|
||||
expect(onChangeDisabledSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should listen to togglerModel change active', function () {
|
||||
view._togglerModel.set('active', true);
|
||||
|
||||
expect(onTogglerChangedSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should listen to overlayModel change visible', function () {
|
||||
view._overlayModel.set('visible', true);
|
||||
|
||||
expect(toggleOverlaySpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._initViews', function () {
|
||||
it('should init views', function () {
|
||||
view._initViews();
|
||||
|
||||
expect(_.size(view._subviews)).toBe(1); // ['PanelWithOptionsView']
|
||||
});
|
||||
});
|
||||
});
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var InfowindowModel = require('builder/data/infowindow-click-model');
|
||||
|
||||
describe('editor/layers/layer-content-view/infowindow/infowindow-click-model', function () {
|
||||
beforeEach(function () {
|
||||
this.configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
|
||||
this.model = new InfowindowModel({
|
||||
fields: [{name: 'test', title: true, order: 0}]
|
||||
}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
});
|
||||
|
||||
it('should set template_name and template', function () {
|
||||
expect(this.model.get('template_name')).toEqual('');
|
||||
expect(this.model.get('template')).toEqual('');
|
||||
this.model.setTemplate('infowindow_dark');
|
||||
expect(this.model.get('template_name')).toEqual('infowindow_dark');
|
||||
expect(this.model.get('template')).toContain('<div class="CDB-infowindow CDB-infowindow--dark js-infowindow" style="background:#2E3C43">');
|
||||
});
|
||||
|
||||
it('should set default template', function () {
|
||||
this.model.setTemplate('infowindow_dark');
|
||||
expect(this.model.get('template_name')).toEqual('infowindow_dark');
|
||||
this.model.setDefault();
|
||||
expect(this.model.get('template_name')).toEqual('');
|
||||
});
|
||||
|
||||
it('should empty fields if template is empty', function () {
|
||||
expect(this.model.get('fields')).toEqual([{name: 'test', title: true, order: 0}]);
|
||||
this.model.setTemplate('');
|
||||
expect(this.model.get('fields')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should transform template with headerColor if template is infowindow_color', function () {
|
||||
expect(this.model.get('template_name')).toEqual('');
|
||||
expect(this.model.get('template')).toEqual('');
|
||||
this.model.setTemplate('infowindow_color');
|
||||
expect(this.model.get('template_name')).toEqual('infowindow_color');
|
||||
expect(this.model.get('template')).toContain('div class="CDB-infowindow-header CDB-infowindow-headerBg CDB-infowindow-headerBg--light js-header" style="background: #35AAE5;"');
|
||||
this.model.set('headerColor', {
|
||||
color: {
|
||||
fixed: '#FABADA',
|
||||
opacity: 1
|
||||
}
|
||||
});
|
||||
this.model.setTemplate('infowindow_color');
|
||||
expect(this.model.get('template_name')).toEqual('infowindow_color');
|
||||
expect(this.model.get('template')).toContain('div class="CDB-infowindow-header CDB-infowindow-headerBg CDB-infowindow-headerBg--light js-header" style="background: #FABADA;"');
|
||||
});
|
||||
});
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var InfowindowContentView = require('builder/editor/layers/layer-content-views/infowindow/infowindow-content-view');
|
||||
var QuerySchemaModel = require('builder/data/query-schema-model');
|
||||
var InfowindowDefinitionModel = require('builder/data/infowindow-definition-model');
|
||||
var EditorModel = require('builder/data/editor-model');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
|
||||
describe('editor/layers/layer-content-view/infowindow/infowindow-content-view', function () {
|
||||
var view;
|
||||
var model;
|
||||
var querySchemaModel;
|
||||
var layerDefinitionModel;
|
||||
var overlayModel;
|
||||
var editorModel;
|
||||
var renderItemsSpy;
|
||||
var updateEditorSpy;
|
||||
|
||||
beforeEach(function () {
|
||||
var configModel = new ConfigModel();
|
||||
|
||||
querySchemaModel = new QuerySchemaModel({
|
||||
query: 'SELECT * FROM table'
|
||||
}, {
|
||||
configModel: configModel
|
||||
});
|
||||
spyOn(querySchemaModel, 'fetch');
|
||||
|
||||
layerDefinitionModel = new LayerDefinitionModel({
|
||||
id: 'l-1',
|
||||
fetched: true,
|
||||
options: {
|
||||
type: 'CartoDB',
|
||||
table_name: 'foo',
|
||||
cartocss: 'asd',
|
||||
source: 'a0'
|
||||
},
|
||||
infowindow: {},
|
||||
tooltip: {}
|
||||
}, {
|
||||
parse: true,
|
||||
configModel: configModel
|
||||
});
|
||||
|
||||
model = new InfowindowDefinitionModel({
|
||||
template: '',
|
||||
template_name: ''
|
||||
}, {
|
||||
configModel: configModel
|
||||
});
|
||||
model.hasTemplate = function () { return true; };
|
||||
|
||||
overlayModel = new Backbone.Model({
|
||||
visible: false
|
||||
});
|
||||
|
||||
editorModel = new EditorModel({
|
||||
disabled: false
|
||||
});
|
||||
|
||||
renderItemsSpy = spyOn(InfowindowContentView.prototype, '_renderItems');
|
||||
updateEditorSpy = spyOn(InfowindowContentView.prototype, '_updateEditor');
|
||||
|
||||
view = new InfowindowContentView({
|
||||
querySchemaModel: querySchemaModel,
|
||||
model: model,
|
||||
layerDefinitionModel: layerDefinitionModel,
|
||||
overlayModel: overlayModel,
|
||||
editorModel: editorModel,
|
||||
templates: [{
|
||||
value: ''
|
||||
}, {
|
||||
value: 'infowindow_light'
|
||||
}]
|
||||
});
|
||||
});
|
||||
|
||||
describe('render', function () {
|
||||
it('should render properly', function () {
|
||||
spyOn(view, '_initViews');
|
||||
|
||||
view.render();
|
||||
|
||||
expect(view._initViews).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
view.render();
|
||||
|
||||
expect(view).toHaveNoLeaks();
|
||||
});
|
||||
});
|
||||
|
||||
describe('_initBinds', function () {
|
||||
it('should listen to model change template_name', function () {
|
||||
model.set('template_name', 'wadus');
|
||||
|
||||
expect(renderItemsSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should listen to model change template', function () {
|
||||
model.set('template', 'wadus');
|
||||
|
||||
expect(updateEditorSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('_initViews', function () {
|
||||
it('should init views', function () {
|
||||
view._initViews();
|
||||
|
||||
expect(_.size(view._subviews)).toBe(1); // ['InfowindowSelectView']
|
||||
expect(renderItemsSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('_renderItems', function () {
|
||||
it('should render items', function () {
|
||||
renderItemsSpy.and.callThrough();
|
||||
|
||||
view._renderItems();
|
||||
|
||||
expect(_.size(view._subviews)).toBe(1); // ['InfowindowItemsView']
|
||||
});
|
||||
});
|
||||
|
||||
describe('_initCollection', function () {
|
||||
it('should init collection', function () {
|
||||
view._initCollection();
|
||||
|
||||
expect(view._templatesCollection).toBeDefined();
|
||||
expect(view._templatesCollection.size()).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('_updateEditor', function () {
|
||||
beforeEach(function () {
|
||||
updateEditorSpy.and.callThrough();
|
||||
});
|
||||
|
||||
it('shouln\'t update editor', function () {
|
||||
expect(editorModel.get('disabled')).toBe(false);
|
||||
|
||||
view._updateEditor();
|
||||
|
||||
expect(editorModel.get('disabled')).toBe(false);
|
||||
});
|
||||
|
||||
describe('model hasn\'t template', function () {
|
||||
it('should update editor', function () {
|
||||
model.hasTemplate = function () { return false; };
|
||||
|
||||
expect(editorModel.get('disabled')).toBe(false);
|
||||
|
||||
view._updateEditor();
|
||||
|
||||
expect(editorModel.get('disabled')).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('_checkValidTemplate', function () {
|
||||
it('should check valid template', function () {
|
||||
expect(view._checkValidTemplate()).toBe(false);
|
||||
});
|
||||
|
||||
describe('with valid template', function () {
|
||||
it('should check valid template', function () {
|
||||
view.model.set('template_name', 'infowindow_light');
|
||||
|
||||
expect(view._checkValidTemplate()).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var InfowindowDescriptionView = require('builder/editor/layers/layer-content-views/infowindow/infowindow-description-view.js');
|
||||
var InfowindowDefinitionModel = require('builder/data/infowindow-definition-model');
|
||||
|
||||
describe('editor/layers/layer-content-view/infowindows/infowindow-description-view', function () {
|
||||
var view, model;
|
||||
|
||||
beforeEach(function () {
|
||||
this.configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
|
||||
model = new InfowindowDefinitionModel({
|
||||
fields: [
|
||||
{
|
||||
name: 'description',
|
||||
title: true,
|
||||
position: 0
|
||||
},
|
||||
{
|
||||
name: 'name',
|
||||
title: true,
|
||||
position: 1
|
||||
}
|
||||
]
|
||||
}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
view = new InfowindowDescriptionView({
|
||||
model: model,
|
||||
namesCount: 2
|
||||
});
|
||||
});
|
||||
|
||||
it('should render all selected', function () {
|
||||
view.render();
|
||||
expect(view.$('.js-textInfo').html().trim()).toEqual('All selected');
|
||||
});
|
||||
|
||||
it('should render count selected', function () {
|
||||
view.model.set('fields', [{ name: 'description', title: true, position: 0 }]);
|
||||
view.render();
|
||||
expect(view.$('.js-textInfo').html().trim()).toEqual('1 selected');
|
||||
});
|
||||
|
||||
it('should render none selected', function () {
|
||||
view.model.set('fields', []);
|
||||
view.render();
|
||||
expect(view.$('.js-textInfo').html().trim()).toEqual('None selected');
|
||||
});
|
||||
|
||||
it('should not have any leaks', function () {
|
||||
expect(view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
view.clean();
|
||||
});
|
||||
});
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
var $ = require('jquery');
|
||||
var _ = require('underscore');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var InfowindowFieldView = require('builder/editor/layers/layer-content-views/infowindow/infowindow-field-view');
|
||||
var InfowindowDefinitionModel = require('builder/data/infowindow-definition-model');
|
||||
|
||||
describe('editor/layers/layer-content-view/infowindows/infowindow-field-view', function () {
|
||||
var view, model;
|
||||
|
||||
beforeEach(function () {
|
||||
this.configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
|
||||
model = new InfowindowDefinitionModel({}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
view = new InfowindowFieldView({
|
||||
layerInfowindowModel: model,
|
||||
field: { name: 'name1', title: true },
|
||||
position: 0
|
||||
});
|
||||
});
|
||||
|
||||
describe('.render', function () {
|
||||
beforeEach(function () {
|
||||
view.render();
|
||||
});
|
||||
|
||||
it('should render correctly', function () {
|
||||
expect(_.size(view._subviews)).toBe(1); // [tooltip]
|
||||
});
|
||||
});
|
||||
|
||||
it('should toggle check', function () {
|
||||
view.render();
|
||||
model.addField('name1');
|
||||
expect(!!$(view.$el.find('.js-checkbox')).attr('checked')).toEqual(true);
|
||||
model.removeField('name1');
|
||||
expect(!!$(view.$el.find('.js-checkbox')).attr('checked')).toEqual(false);
|
||||
});
|
||||
|
||||
it('should toggle field on click', function () {
|
||||
view.render();
|
||||
model.addField('name1');
|
||||
expect(model.containsField('name1')).toEqual(true);
|
||||
view.toggle();
|
||||
expect(model.containsField('name1')).toEqual(false);
|
||||
});
|
||||
|
||||
it('should set empty alternative name after uncheck', function () {
|
||||
view.render();
|
||||
model.addField('name1');
|
||||
model.setAlternativeName('name1', 'nombre');
|
||||
expect(model.getAlternativeName('name1')).toEqual('nombre');
|
||||
view.toggle();
|
||||
expect(model.getAlternativeName('name1')).toEqual('');
|
||||
});
|
||||
|
||||
it('should not have any leaks', function () {
|
||||
expect(view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
view.clean();
|
||||
});
|
||||
});
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var QuerySchemaModel = require('builder/data/query-schema-model');
|
||||
var InfowindowFieldsView = require('builder/editor/layers/layer-content-views/infowindow/infowindow-fields-view');
|
||||
var InfowindowDefinitionModel = require('builder/data/infowindow-definition-model');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
require('jquery-ui');
|
||||
|
||||
describe('editor/layers/layer-content-view/infowindows/infowindow-fields-view', function () {
|
||||
var view, model;
|
||||
|
||||
beforeEach(function () {
|
||||
this.configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
|
||||
this.querySchemaModel = new QuerySchemaModel({
|
||||
query: 'SELECT * FROM foobar',
|
||||
status: 'fetched'
|
||||
}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
this.layerDefinitionModel = new LayerDefinitionModel({
|
||||
id: 'l-1',
|
||||
fetched: true,
|
||||
options: {
|
||||
type: 'CartoDB',
|
||||
table_name: 'foo',
|
||||
cartocss: 'asd',
|
||||
source: 'a0'
|
||||
},
|
||||
infowindow: {}
|
||||
}, {
|
||||
parse: true,
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
model = new InfowindowDefinitionModel({}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
view = new InfowindowFieldsView({
|
||||
querySchemaModel: this.querySchemaModel,
|
||||
model: model,
|
||||
layerDefinitionModel: this.layerDefinitionModel
|
||||
});
|
||||
});
|
||||
|
||||
it('should render fields', function () {
|
||||
view._querySchemaModel.columnsCollection.reset([
|
||||
{ name: 'name1', type: 'string', position: 0 },
|
||||
{ name: 'name2', type: 'number', position: 1 },
|
||||
{ name: 'name3', type: 'number', position: 2 }
|
||||
]);
|
||||
|
||||
view.render();
|
||||
expect(view.$el.find('li').length).toEqual(3);
|
||||
});
|
||||
|
||||
it('should render description', function () {
|
||||
view.render();
|
||||
expect(view.$el.find('.js-textInfo').length).toEqual(1);
|
||||
});
|
||||
|
||||
describe('sortable', function () {
|
||||
beforeEach(function () {
|
||||
view._querySchemaModel.columnsCollection.reset([
|
||||
{ name: 'name1', type: 'string', position: 0 },
|
||||
{ name: 'name2', type: 'number', position: 1 }
|
||||
]);
|
||||
|
||||
view.model.addField('name1', 0);
|
||||
view.model.addField('name2', 1);
|
||||
});
|
||||
|
||||
it('should unselect/select all', function () {
|
||||
view.render();
|
||||
expect(view.model.attributes.fields.length).toEqual(2);
|
||||
view._fieldsDescriptionView.trigger('toggle');
|
||||
expect(view.model.attributes.fields.length).toEqual(0);
|
||||
view._fieldsDescriptionView.trigger('toggle');
|
||||
expect(view.model.attributes.fields.length).toEqual(2);
|
||||
});
|
||||
|
||||
it('should be initialized when view is rendered', function () {
|
||||
spyOn(view, '_initSortable').and.callThrough();
|
||||
view.render();
|
||||
expect(view._initSortable).toHaveBeenCalled();
|
||||
expect(view.$('.js-fields').data('ui-sortable')).not.toBeUndefined();
|
||||
});
|
||||
|
||||
it('should update the order of the models when sort has finished', function () {
|
||||
view.render();
|
||||
// Impossible to fake sortable behaviour so...
|
||||
view.$('.js-field:eq(1)').insertBefore(view.$('.js-field:eq(0)'));
|
||||
view._onSortableFinish();
|
||||
// End of fake sortable
|
||||
expect(view.model.getFieldPos('name2')).toBe(0);
|
||||
expect(view.model.getFieldPos('name1')).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not have any leaks', function () {
|
||||
expect(view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
describe('._onSortableFinish', function () {
|
||||
it('should call _storeSortedFields function', function () {
|
||||
spyOn(view, '_storeSortedFields');
|
||||
view._onSortableFinish();
|
||||
expect(view._storeSortedFields).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
var InfowindowFormView = require('builder/editor/layers/layer-content-views/infowindow/infowindow-form-view.js');
|
||||
var InfowindowDefinitionModel = require('builder/data/infowindow-definition-model');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
|
||||
describe('editor/layers/layer-content-view/infowindows/infowindow-form-view', function () {
|
||||
var view, model;
|
||||
|
||||
beforeEach(function () {
|
||||
this.configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
|
||||
model = new InfowindowDefinitionModel({
|
||||
template_name: '',
|
||||
width: 400
|
||||
}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
view = new InfowindowFormView({
|
||||
model: model
|
||||
});
|
||||
});
|
||||
|
||||
describe('render', function () {
|
||||
it('should render properly', function () {
|
||||
view.render();
|
||||
expect(view.$('form').length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should update fields depending on template_name', function () {
|
||||
view.model.set('template_name', 'test1');
|
||||
view.render();
|
||||
expect(view.$('.Editor-formInner').length).toBe(1);
|
||||
expect(view.$('.Editor-formInner label').text().trim()).toEqual('editor.layers.infowindow.style.size.label');
|
||||
|
||||
view.model.set('template_name', 'infowindow_color');
|
||||
view.render();
|
||||
expect(view.$('.Editor-formInner').length).toBe(2);
|
||||
expect(view.$('.Editor-formInner:eq(1) label').text().trim()).toEqual('editor.layers.infowindow.style.header-color');
|
||||
});
|
||||
|
||||
it('should update width', function () {
|
||||
view.model.set('template_name', 'test1');
|
||||
view.render();
|
||||
expect(view.$('.Editor-formInner .js-input').val()).toBe('400');
|
||||
});
|
||||
|
||||
it('should not have any leaks', function () {
|
||||
expect(view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
view.clean();
|
||||
});
|
||||
});
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var QuerySchemaModel = require('builder/data/query-schema-model');
|
||||
var InfowindowItemsView = require('builder/editor/layers/layer-content-views/infowindow/infowindow-items-view');
|
||||
var InfowindowDefinitionModel = require('builder/data/infowindow-definition-model');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var _ = require('underscore');
|
||||
|
||||
describe('editor/layers/layer-content-view/infowindows/infowindow-items-view', function () {
|
||||
var view, model;
|
||||
|
||||
beforeEach(function () {
|
||||
this.configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
|
||||
this.querySchemaModel = new QuerySchemaModel({
|
||||
query: 'SELECT * FROM foobar',
|
||||
status: 'fetched'
|
||||
}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
spyOn(this.querySchemaModel, 'fetch').and.callThrough();
|
||||
|
||||
this.querySchemaModel.columnsCollection.reset([
|
||||
{ name: 'name1', type: 'string', position: 0 },
|
||||
{ name: 'name2', type: 'number', position: 1 },
|
||||
{ name: 'name3', type: 'number', position: 2 }
|
||||
]);
|
||||
|
||||
this.layerDefinitionModel = new LayerDefinitionModel({
|
||||
id: 'l-1',
|
||||
fetched: true,
|
||||
options: {
|
||||
type: 'CartoDB',
|
||||
table_name: 'foo',
|
||||
cartocss: 'asd',
|
||||
source: 'a0'
|
||||
},
|
||||
infowindow: {}
|
||||
}, {
|
||||
parse: true,
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
model = new InfowindowDefinitionModel({}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
view = new InfowindowItemsView({
|
||||
querySchemaModel: this.querySchemaModel,
|
||||
model: model,
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
hasValidTemplate: true
|
||||
});
|
||||
});
|
||||
|
||||
it('should not render items if has not valid template', function () {
|
||||
view.render();
|
||||
expect(_.size(view._subviews)).toBe(1);
|
||||
view._hasValidTemplate = false;
|
||||
view.render();
|
||||
expect(_.size(view._subviews)).toBe(0);
|
||||
});
|
||||
|
||||
it('should not have any leaks', function () {
|
||||
expect(view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
view.clean();
|
||||
});
|
||||
});
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
var InfowindowSelectView = require('builder/editor/layers/layer-content-views/infowindow/infowindow-select-view.js');
|
||||
var InfowindowDefinitionModel = require('builder/data/infowindow-definition-model');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var CarouselCollection = require('builder/components/custom-carousel/custom-carousel-collection');
|
||||
var _ = require('underscore');
|
||||
|
||||
describe('editor/layers/layer-content-view/infowindows/infowindow-select-view', function () {
|
||||
var view, model;
|
||||
|
||||
beforeEach(function () {
|
||||
this.configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
|
||||
model = new InfowindowDefinitionModel({
|
||||
template_name: 'test1'
|
||||
}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
var templates = [
|
||||
{
|
||||
value: '',
|
||||
label: 'None'
|
||||
}, {
|
||||
value: 'test1',
|
||||
label: 'Test1'
|
||||
}
|
||||
];
|
||||
|
||||
var collection = new CarouselCollection(
|
||||
_.map(templates, function (template) {
|
||||
return {
|
||||
selected: model.get('template_name') === template.value,
|
||||
val: template.value,
|
||||
label: template.label,
|
||||
template: function () {
|
||||
return template.label;
|
||||
}
|
||||
};
|
||||
}, this)
|
||||
);
|
||||
|
||||
view = new InfowindowSelectView({
|
||||
model: model,
|
||||
templatesCollection: collection
|
||||
});
|
||||
});
|
||||
|
||||
it('should select template if there is match', function () {
|
||||
view.render();
|
||||
var s = view._templatesCollection.find(function (mdl) { return mdl.get('selected'); });
|
||||
expect(s.get('val')).toEqual('test1');
|
||||
expect(s.get('label')).toEqual('Test1');
|
||||
});
|
||||
|
||||
it("should select 'None' if there is no match", function () {
|
||||
view.model.set('template_name', 'none');
|
||||
view.render();
|
||||
var s = view._templatesCollection.find(function (mdl) { return mdl.get('selected'); });
|
||||
expect(s.get('val')).toEqual('');
|
||||
expect(s.get('label')).toEqual('None');
|
||||
});
|
||||
|
||||
it('should not have any leaks', function () {
|
||||
expect(view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
view.clean();
|
||||
});
|
||||
});
|
||||
+354
@@ -0,0 +1,354 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var InfowindowsView = require('builder/editor/layers/layer-content-views/infowindow/infowindows-view');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var QuerySchemaModel = require('builder/data/query-schema-model');
|
||||
var QueryGeometryModel = require('builder/data/query-geometry-model');
|
||||
var InfowindowView = require('builder/editor/layers/layer-content-views/infowindow/infowindow-click-view');
|
||||
var TooltipView = require('builder/editor/layers/layer-content-views/infowindow/infowindow-hover-view');
|
||||
var InfowindowDefinitionModel = require('builder/data/infowindow-definition-model');
|
||||
var EditorModel = require('builder/data/editor-model');
|
||||
var UserActions = require('builder/data/user-actions');
|
||||
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
|
||||
var AnalysisDefinitionsCollection = require('builder/data/analysis-definitions-collection');
|
||||
var QueryRowsCollection = require('builder/data/query-rows-collection');
|
||||
|
||||
describe('editor/layers/layer-content-view/infowindow/infowindows-view', function () {
|
||||
var view, configModel, layerDefinitionModel, querySchemaModel, layerContentModel;
|
||||
|
||||
var createViewFn = function (options) {
|
||||
configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
|
||||
var userModel = new UserModel({}, {
|
||||
configModel: configModel
|
||||
});
|
||||
spyOn(userModel, 'featureEnabled').and.returnValue(true);
|
||||
|
||||
var analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
|
||||
configModel: configModel,
|
||||
userModel: userModel
|
||||
});
|
||||
|
||||
var a0 = analysisDefinitionNodesCollection.add({
|
||||
id: 'a0',
|
||||
type: 'source',
|
||||
table_name: 'foo'
|
||||
});
|
||||
spyOn(a0, 'isCustomQueryApplied').and.returnValue(false);
|
||||
|
||||
var layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
|
||||
configModel: configModel,
|
||||
userModel: userModel,
|
||||
analysisDefinitionNodesCollection: analysisDefinitionNodesCollection,
|
||||
mapId: 'm123',
|
||||
stateDefinitionModel: {}
|
||||
});
|
||||
|
||||
layerContentModel = new Backbone.Model({
|
||||
state: 'initial'
|
||||
});
|
||||
|
||||
var analysisDefinitionsCollection = new AnalysisDefinitionsCollection(null, {
|
||||
configModel: configModel,
|
||||
vizId: 'viz-123',
|
||||
analysisDefinitionNodesCollection: analysisDefinitionNodesCollection,
|
||||
layerDefinitionsCollection: layerDefinitionsCollection
|
||||
});
|
||||
|
||||
querySchemaModel = new QuerySchemaModel({
|
||||
query: 'SELECT * FROM foobar',
|
||||
status: 'fetched'
|
||||
}, {
|
||||
configModel: configModel
|
||||
});
|
||||
querySchemaModel.columnsCollection.reset([
|
||||
{
|
||||
type: 'number',
|
||||
name: 'cartodb_id'
|
||||
}, {
|
||||
type: 'number',
|
||||
name: 'a_number'
|
||||
}
|
||||
]);
|
||||
spyOn(querySchemaModel, 'fetch');
|
||||
|
||||
var queryGeometryModel = new QueryGeometryModel({
|
||||
query: 'SELECT * FROM foobar',
|
||||
status: 'fetched',
|
||||
simple_geom: 'point'
|
||||
}, {
|
||||
configModel: configModel
|
||||
});
|
||||
spyOn(queryGeometryModel, 'fetch');
|
||||
|
||||
var queryRowsCollection = new QueryRowsCollection([], {
|
||||
configModel: configModel,
|
||||
querySchemaModel: querySchemaModel
|
||||
});
|
||||
|
||||
spyOn(queryRowsCollection, 'fetch');
|
||||
|
||||
layerDefinitionModel = new LayerDefinitionModel({
|
||||
id: 'l-1',
|
||||
fetched: true,
|
||||
options: {
|
||||
type: 'CartoDB',
|
||||
table_name: 'foo',
|
||||
cartocss: 'asd',
|
||||
source: 'a0'
|
||||
},
|
||||
infowindow: {
|
||||
template_name: '',
|
||||
template: ''
|
||||
},
|
||||
tooltip: {
|
||||
template_name: '',
|
||||
template: ''
|
||||
}
|
||||
}, {
|
||||
parse: true,
|
||||
configModel: configModel
|
||||
});
|
||||
spyOn(layerDefinitionModel, 'save');
|
||||
spyOn(layerDefinitionModel, 'getAnalysisDefinitionNodeModel').and.returnValue(a0);
|
||||
|
||||
var defaultOptions = {
|
||||
configModel: configModel,
|
||||
editorModel: new EditorModel(),
|
||||
userActions: new UserActions({
|
||||
userModel: userModel,
|
||||
analysisDefinitionsCollection: analysisDefinitionsCollection,
|
||||
analysisDefinitionNodesCollection: analysisDefinitionNodesCollection,
|
||||
layerDefinitionsCollection: layerDefinitionsCollection,
|
||||
widgetDefinitionsCollection: {}
|
||||
}),
|
||||
layerDefinitionModel: layerDefinitionModel,
|
||||
querySchemaModel: querySchemaModel,
|
||||
queryGeometryModel: queryGeometryModel,
|
||||
queryRowsCollection: queryRowsCollection,
|
||||
layerContentModel: layerContentModel
|
||||
};
|
||||
|
||||
view = new InfowindowsView(_.extend(defaultOptions, options));
|
||||
|
||||
return view;
|
||||
};
|
||||
|
||||
describe('when data is filtered', function () {
|
||||
describe('.render', function () {
|
||||
it('should call _renderFilteredData', function () {
|
||||
view = createViewFn();
|
||||
spyOn(view, '_renderFilteredData');
|
||||
view._viewState.set('isDataFiltered', true, { silent: true });
|
||||
view.render();
|
||||
|
||||
expect(view._renderFilteredData).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when layer has infowindowModel', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
view.render();
|
||||
});
|
||||
|
||||
it('should render two tabs', function () {
|
||||
expect(view._layerTabPaneView).toBeDefined();
|
||||
expect(_.size(view._layerTabPaneView._subviews)).toBe(3); // 2 tabs, 1 pane
|
||||
expect(view.$('.CDB-NavSubmenu .CDB-NavSubmenu-item').length).toBe(2);
|
||||
expect(view.$('.CDB-NavSubmenu-item:eq(0)').text()).toContain('editor.layers.infowindow-menu-tab-pane-labels.click');
|
||||
expect(view.$('.CDB-NavSubmenu-item:eq(0) .CDB-NavSubmenu-status').text()).toBe('editor.layers.infowindow.style.none');
|
||||
expect(view.$('.CDB-NavSubmenu-item:eq(1)').text()).toContain('editor.layers.infowindow-menu-tab-pane-labels.hover');
|
||||
expect(view.$('.CDB-NavSubmenu-item:eq(1) .CDB-NavSubmenu-status').text()).toContain('editor.layers.tooltip.style.none');
|
||||
});
|
||||
|
||||
it('should change tab if infowindow (and tooltip) changes', function () {
|
||||
expect(view.$('.CDB-NavSubmenu-item:eq(0) .CDB-NavSubmenu-status').text()).toBe('editor.layers.infowindow.style.none');
|
||||
expect(view.$('.CDB-NavSubmenu-item:eq(1) .CDB-NavSubmenu-status').text()).toBe('editor.layers.tooltip.style.none');
|
||||
view._layerInfowindowModel.set('template_name', 'infowindow_dark');
|
||||
|
||||
view.render();
|
||||
|
||||
expect(view.$('.CDB-NavSubmenu-item:eq(0) .CDB-NavSubmenu-status').text()).toBe('editor.layers.infowindow.style.infowindow_dark');
|
||||
expect(view.$('.CDB-NavSubmenu-item:eq(1) .CDB-NavSubmenu-status').text()).toBe('editor.layers.tooltip.style.none');
|
||||
|
||||
// change tab and change template_name, '.js-menu .CDB-NavSubmenu-item.is-selected .js-NavSubmenu-status' is needed
|
||||
view._layerTabPaneView.collection.at(1).set('selected', true);
|
||||
view._layerTooltipModel.set('template_name', 'tooltip_dark');
|
||||
|
||||
view.render();
|
||||
|
||||
expect(view.$('.CDB-NavSubmenu-item:eq(0) .CDB-NavSubmenu-status').text()).toBe('editor.layers.infowindow.style.infowindow_dark');
|
||||
expect(view.$('.CDB-NavSubmenu-item:eq(1) .CDB-NavSubmenu-status').text()).toBe('editor.layers.tooltip.style.tooltip_dark');
|
||||
});
|
||||
|
||||
describe('infowindows tabs', function () {
|
||||
var model;
|
||||
|
||||
beforeEach(function () {
|
||||
model = new InfowindowDefinitionModel({}, {
|
||||
configModel: configModel
|
||||
});
|
||||
});
|
||||
|
||||
it('infowindow should render properly', function () {
|
||||
view = new InfowindowView({
|
||||
layerDefinitionModel: layerDefinitionModel,
|
||||
querySchemaModel: querySchemaModel,
|
||||
userActions: {},
|
||||
model: model,
|
||||
editorModel: new EditorModel()
|
||||
});
|
||||
|
||||
view.render();
|
||||
|
||||
expect(_.size(view._subviews)).toBe(1); // fields / codemirror view
|
||||
});
|
||||
|
||||
it('tooltip should render properly', function () {
|
||||
view = new TooltipView({
|
||||
layerDefinitionModel: layerDefinitionModel,
|
||||
querySchemaModel: querySchemaModel,
|
||||
userActions: {},
|
||||
model: model,
|
||||
editorModel: new EditorModel()
|
||||
});
|
||||
|
||||
view.render();
|
||||
|
||||
expect(_.size(view._subviews)).toBe(1); // fields / codemirror view
|
||||
});
|
||||
});
|
||||
|
||||
it('should render placeholder if style model type is aggregated', function () {
|
||||
expect(view.$('.CDB-Size-huge').length).toBe(0);
|
||||
|
||||
spyOn(layerDefinitionModel.styleModel, 'isAggregatedType').and.returnValue(true);
|
||||
|
||||
view.render();
|
||||
|
||||
expect(view.$('.CDB-Size-huge').length).toBe(1);
|
||||
expect(view.$('.CDB-Size-huge').text()).toBe('editor.layers.infowindow.placeholder-interactivity-text');
|
||||
});
|
||||
|
||||
it('should render placeholder if style model has animated enabled', function () {
|
||||
expect(view.$('.CDB-Size-huge').length).toBe(0);
|
||||
|
||||
spyOn(layerDefinitionModel.styleModel, 'isAnimation').and.returnValue(true);
|
||||
|
||||
view.render();
|
||||
|
||||
expect(view.$('.CDB-Size-huge').length).toBe(1);
|
||||
expect(view.$('.CDB-Size-huge').text()).toBe('editor.layers.infowindow.placeholder-interactivity-text');
|
||||
});
|
||||
|
||||
it('should render placeholder when has no columns', function () {
|
||||
expect(view.$('.CDB-Size-huge').length).toBe(0);
|
||||
|
||||
querySchemaModel.columnsCollection.reset([
|
||||
{
|
||||
type: 'number',
|
||||
name: 'cartodb_id'
|
||||
}
|
||||
]);
|
||||
|
||||
view.render();
|
||||
|
||||
expect(view.$('.CDB-Size-huge').length).toBe(1);
|
||||
expect(view.$('.CDB-Size-huge').text()).toBe('editor.layers.infowindow.placeholder-columns-text');
|
||||
});
|
||||
|
||||
it('should not have any leaks', function () {
|
||||
expect(view).toHaveNoLeaks();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when layer doesn\'t have infowindowModel (basemap, torque, ...)', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
view._layerInfowindowModel = undefined;
|
||||
view.render();
|
||||
});
|
||||
|
||||
it('should render placeholder', function () {
|
||||
expect(view._layerTabPaneView).not.toBeDefined();
|
||||
expect(view.$('.CDB-Size-huge').length).toBe(1);
|
||||
expect(view.$('.CDB-Size-huge').text()).toBe('editor.layers.infowindow.placeholder-interactivity-text');
|
||||
});
|
||||
|
||||
it('should not have any leaks', function () {
|
||||
expect(view).toHaveNoLeaks();
|
||||
});
|
||||
});
|
||||
|
||||
describe('.initBinds', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(InfowindowsView.prototype, '_changeStyle');
|
||||
spyOn(InfowindowsView.prototype, '_updateSelectedChild');
|
||||
spyOn(InfowindowsView.prototype, '_setViewValues');
|
||||
spyOn(InfowindowsView.prototype, 'render').and.callThrough();
|
||||
|
||||
view = createViewFn();
|
||||
view.render();
|
||||
});
|
||||
|
||||
it('should call _changeStyle if _editorModel:edition changes', function () {
|
||||
view._initBinds();
|
||||
view._editorModel.trigger('change:edition');
|
||||
expect(view._changeStyle).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call _updateSelectedChild if _layerInfowindowModel:template changes', function () {
|
||||
view._initBinds();
|
||||
view._layerInfowindowModel.trigger('change:template');
|
||||
expect(view._updateSelectedChild).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call _updateSelectedChild if _layerTooltipModel:template changes', function () {
|
||||
view._initBinds();
|
||||
view._layerTooltipModel.trigger('change:template');
|
||||
expect(view._updateSelectedChild).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call _setViewValues if layerContentModel:state changes', function () {
|
||||
view._initBinds();
|
||||
view._layerContentModel.trigger('change:state');
|
||||
expect(view._setViewValues).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should render if _querySchemaModel:status changes', function () {
|
||||
view._initBinds();
|
||||
view._querySchemaModel.trigger('change:status');
|
||||
expect(view.render).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._renderFilteredData', function () {
|
||||
it('should render the correct template', function () {
|
||||
view._renderFilteredData();
|
||||
|
||||
expect(view.$el.html()).toContain('editor.layers.warnings.no-data.message');
|
||||
expect(view.$el.html()).toContain('editor.layers.warnings.no-data.action-message');
|
||||
});
|
||||
});
|
||||
|
||||
describe('._setViewValues', function () {
|
||||
it('should update _viewState', function (done) {
|
||||
spyOn(view._layerDefinitionModel, 'isDataFiltered').and.returnValue(Promise.resolve(true));
|
||||
view._viewState.set('isDataFiltered', false, { silent: true });
|
||||
|
||||
view._setViewValues();
|
||||
|
||||
setTimeout(function () {
|
||||
expect(view._viewState.get('isDataFiltered')).toEqual(true);
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
});
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
var _ = require('underscore');
|
||||
|
||||
var LegendColorTypes = require('builder/editor/layers/layer-content-views/legend/color/legend-color-types');
|
||||
var LegendTypes = require('builder/editor/layers/layer-content-views/legend/legend-types');
|
||||
|
||||
var StyleConstants = require('builder/components/form-components/_constants/_style');
|
||||
var StyleModel = require('builder/editor/style/style-definition-model');
|
||||
|
||||
describe('editor/layers/layer-content-view/legend/color/legend-color-types', function () {
|
||||
function getLegendType (type) {
|
||||
return _.find(LegendColorTypes, function (legendColorType) {
|
||||
return legendColorType.value === type;
|
||||
}, this);
|
||||
}
|
||||
|
||||
var anStyleFillByBooleanAttribute = new StyleModel({
|
||||
type: StyleConstants.Type.SIMPLE,
|
||||
fill: {
|
||||
color: {
|
||||
attribute: 'a_boolean_field',
|
||||
attribute_type: 'boolean',
|
||||
range: ['#2bd900', '#d96b6b', '#e7e7e7'],
|
||||
quantification: undefined
|
||||
}
|
||||
},
|
||||
stroke: {
|
||||
color: {
|
||||
fixed: 'white'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
describe('.category legend', function () {
|
||||
beforeEach(function () {
|
||||
this.legendType = getLegendType(LegendTypes.CATEGORY);
|
||||
});
|
||||
|
||||
it('should be compatible with a boolean attribute in a color by value viz', function () {
|
||||
expect(this.legendType.isStyleCompatible(anStyleFillByBooleanAttribute)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('.choropleth legend', function () {
|
||||
beforeEach(function () {
|
||||
this.legendType = getLegendType(LegendTypes.CHOROPLETH);
|
||||
});
|
||||
|
||||
it('should be incompatible with a boolean attribute in a color by value viz', function () {
|
||||
expect(this.legendType.isStyleCompatible(anStyleFillByBooleanAttribute)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
var LegendColorHelper = require('builder/editor/layers/layer-content-views/legend/form/legend-color-helper');
|
||||
|
||||
describe('editor/layers/layer-content-views/legends/form/legend-color-helper', function () {
|
||||
it('should return collection category items properly', function () {
|
||||
var style = {
|
||||
'attribute': 'line',
|
||||
'attribute_type': 'string',
|
||||
'range': [
|
||||
'#555',
|
||||
'#5B3F95',
|
||||
'#1D6996',
|
||||
'#129C63',
|
||||
'#73AF48',
|
||||
'#EDAD08',
|
||||
'#E17C05',
|
||||
'#C94034',
|
||||
'#BA0040',
|
||||
'#8E1966',
|
||||
'#6F3072'
|
||||
],
|
||||
'domain': [
|
||||
'Others',
|
||||
'\'L1\'',
|
||||
'\'L10\'',
|
||||
'\'L12\'',
|
||||
'\'L2\'',
|
||||
'\'L3\'',
|
||||
'\'L4\'',
|
||||
'\'L5\'',
|
||||
'\'L6\'',
|
||||
'\'L7\'',
|
||||
'\'L9\''
|
||||
]
|
||||
};
|
||||
|
||||
var categories = LegendColorHelper.getCategories(style);
|
||||
expect(categories.length).toBe(11);
|
||||
expect(categories[0].fill.color.fixed).toBe('#555');
|
||||
expect(categories[0].title).toBe('Others');
|
||||
expect(categories[3].fill.color.fixed).toBe('#129C63');
|
||||
expect(categories[3].title).toBe('\'L12\'');
|
||||
expect(categories[10].fill.color.fixed).toBe('#6F3072');
|
||||
expect(categories[10].title).toBe('\'L9\'');
|
||||
});
|
||||
|
||||
it('should return collection category items properly for choropleth style', function () {
|
||||
var style = {
|
||||
'attribute': 'line',
|
||||
'attribute_type': 'number',
|
||||
'range': [
|
||||
'#555',
|
||||
'#5B3F95',
|
||||
'#1D6996',
|
||||
'#129C63',
|
||||
'#73AF48',
|
||||
'#EDAD08',
|
||||
'#E17C05',
|
||||
'#C94034',
|
||||
'#BA0040',
|
||||
'#8E1966',
|
||||
'#6F3072'
|
||||
]
|
||||
};
|
||||
|
||||
var categories = LegendColorHelper.getCategories(style);
|
||||
expect(categories.length).toBe(11);
|
||||
expect(categories[0].fill.color.fixed).toBe('#555');
|
||||
expect(categories[0].title).toBe('');
|
||||
expect(categories[3].fill.color.fixed).toBe('#129C63');
|
||||
expect(categories[3].title).toBe('');
|
||||
expect(categories[10].fill.color.fixed).toBe('#6F3072');
|
||||
expect(categories[10].title).toBe('');
|
||||
});
|
||||
|
||||
it('should return single category item properly', function () {
|
||||
var style = {
|
||||
'fixed': '#d4d438',
|
||||
'opacity': 1
|
||||
};
|
||||
|
||||
var categories = LegendColorHelper.getCategories(style);
|
||||
expect(categories.length).toBe(1);
|
||||
expect(categories[0].fill.color.fixed).toBe('#d4d438');
|
||||
expect(categories[0].title).toBe('');
|
||||
});
|
||||
|
||||
it('should return bubble properly', function () {
|
||||
var style = {
|
||||
'fixed': '#d4d438',
|
||||
'opacity': 1
|
||||
};
|
||||
|
||||
var bubble = LegendColorHelper.getBubbles(style);
|
||||
expect(bubble.color.fixed).toBe('#d4d438');
|
||||
});
|
||||
|
||||
describe('simpleColor', function () {
|
||||
it('should return same output as input in color object provided', function () {
|
||||
var color = {
|
||||
fixed: '#fabada',
|
||||
image: 'icon.svg'
|
||||
};
|
||||
|
||||
var categories = LegendColorHelper.getCategories(color);
|
||||
|
||||
expect(categories.length).toBe(1);
|
||||
expect(categories[0].fill.color.fixed).toEqual('#fabada');
|
||||
expect(categories[0].fill.color.image).toEqual('icon.svg');
|
||||
});
|
||||
});
|
||||
|
||||
describe('collectionColor', function () {
|
||||
it('', function () {
|
||||
var color = {
|
||||
attribute: 'name',
|
||||
range: ['#fabada', '#c0ffee'],
|
||||
domain: ['Pedro', 'Pablo'],
|
||||
images: ['pedro.png', '']
|
||||
};
|
||||
|
||||
var categories = LegendColorHelper.getCategories(color);
|
||||
|
||||
expect(categories.length).toBe(2);
|
||||
expect(categories[0].fill.color.fixed).toEqual('#fabada');
|
||||
expect(categories[0].fill.color.opacity).toBe(1);
|
||||
expect(categories[0].fill.color.image).toEqual('pedro.png');
|
||||
expect(categories[0].title).toEqual('Pedro');
|
||||
expect(categories[1].fill.color.fixed).toEqual('#c0ffee');
|
||||
expect(categories[1].fill.color.opacity).toBe(1);
|
||||
expect(categories[1].fill.color.image).toEqual('');
|
||||
expect(categories[1].title).toEqual('Pablo');
|
||||
});
|
||||
});
|
||||
});
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
var LegendColors = require('builder/editor/layers/layer-content-views/legend/form/legend-color-range');
|
||||
|
||||
describe('editor/layers/layer-content-views/legends/form/legend-color-range', function () {
|
||||
var COLORS = [
|
||||
'#66B79E',
|
||||
'#E65176',
|
||||
'#528995',
|
||||
'#FF710F',
|
||||
'#305482',
|
||||
'#CCD859',
|
||||
'#565175',
|
||||
'#FFB927',
|
||||
'#3EBCAE',
|
||||
'#E54C1F'
|
||||
];
|
||||
|
||||
it('should rotate properly', function () {
|
||||
expect(LegendColors.getNextColor()).toBe(COLORS[1]);
|
||||
expect(LegendColors.getNextColor()).toBe(COLORS[2]);
|
||||
expect(LegendColors.getNextColor()).toBe(COLORS[3]);
|
||||
expect(LegendColors.getNextColor()).toBe(COLORS[4]);
|
||||
expect(LegendColors.getNextColor()).toBe(COLORS[5]);
|
||||
expect(LegendColors.getNextColor()).toBe(COLORS[6]);
|
||||
expect(LegendColors.getNextColor()).toBe(COLORS[7]);
|
||||
expect(LegendColors.getNextColor()).toBe(COLORS[8]);
|
||||
expect(LegendColors.getNextColor()).toBe(COLORS[9]);
|
||||
expect(LegendColors.getNextColor()).toBe(COLORS[0]);
|
||||
expect(LegendColors.getNextColor()).toBe(COLORS[1]);
|
||||
});
|
||||
});
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var LegendCustomDefinitionModel = require('builder/data/legends/legend-custom-definition-model');
|
||||
var LegendCustomFormDefinitionModel = require('builder/editor/layers/layer-content-views/legend/form/legend-custom-definition-form-model');
|
||||
var FactoryModals = require('../../../../../factories/modals');
|
||||
|
||||
describe('editor/layers/layer-content-views/legends/form/legend-custom-definition-model', function () {
|
||||
var legendDefinitionModel;
|
||||
var legendFormModel;
|
||||
|
||||
beforeEach(function () {
|
||||
var configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
|
||||
var userModel = new UserModel({}, {
|
||||
configModel: configModel
|
||||
});
|
||||
|
||||
legendDefinitionModel = new LegendCustomDefinitionModel({
|
||||
title: 'Foo',
|
||||
items: [{
|
||||
title: 'foo',
|
||||
color: '#fabada',
|
||||
icon: 'http://image.io/foo.svg'
|
||||
}, {
|
||||
title: 'bar',
|
||||
color: '#abadaf',
|
||||
icon: 'http://image.io/bar.svg'
|
||||
}, {
|
||||
title: 'baz',
|
||||
color: '#ababab',
|
||||
icon: 'http://image.io/baz.svg'
|
||||
}]
|
||||
}, {
|
||||
layerDefinitionModel: new Backbone.Model(),
|
||||
configModel: configModel,
|
||||
vizId: 1
|
||||
});
|
||||
|
||||
legendFormModel = new LegendCustomFormDefinitionModel(
|
||||
_.pick(legendDefinitionModel.attributes, 'title', 'items'), {
|
||||
legendDefinitionModel: legendDefinitionModel,
|
||||
userModel: userModel,
|
||||
configModel: configModel,
|
||||
modals: FactoryModals.createModalService()
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('should initialize items correctly', function () {
|
||||
var items = legendFormModel.get('items');
|
||||
expect(items.length).toBe(3);
|
||||
expect(items[0].fill.color.fixed).toBe('#fabada');
|
||||
expect(items[0].fill.color.image).toBe('http://image.io/foo.svg');
|
||||
expect(items[1].fill.color.fixed).toBe('#abadaf');
|
||||
expect(items[1].fill.color.image).toBe('http://image.io/bar.svg');
|
||||
expect(items[2].fill.color.fixed).toBe('#ababab');
|
||||
expect(items[2].fill.color.image).toBe('http://image.io/baz.svg');
|
||||
});
|
||||
|
||||
it('should parse items correctly', function () {
|
||||
var items = legendFormModel.toJSON().items;
|
||||
expect(items.length).toBe(3);
|
||||
expect(items[0].color).toBe('#fabada');
|
||||
expect(items[0].image).toBe('http://image.io/foo.svg');
|
||||
expect(items[1].color).toBe('#abadaf');
|
||||
expect(items[1].image).toBe('http://image.io/bar.svg');
|
||||
expect(items[2].color).toBe('#ababab');
|
||||
expect(items[2].image).toBe('http://image.io/baz.svg');
|
||||
});
|
||||
});
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
var Backbone = require('backbone');
|
||||
var LegendBaseTypeView = require('builder/editor/layers/layer-content-views/legend/legend-base-type-view');
|
||||
var MapDefinitionModel = require('builder/data/map-definition-model');
|
||||
var LegendDefinitionsCollection = require('builder/data/legends/legend-definitions-collection');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var LayerContentModel = require('builder/data/layer-content-model');
|
||||
var FactoryModals = require('../../../../factories/modals');
|
||||
|
||||
describe('editor/layers/layer-content-view/legend/legend-base-type-view', function () {
|
||||
beforeEach(function () {
|
||||
this.configModel = new ConfigModel({
|
||||
base_url: '/u/wadus'
|
||||
});
|
||||
|
||||
this.userModel = new UserModel({
|
||||
username: 'foo',
|
||||
google_maps_api_key: 123456
|
||||
}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
this.mapDefinitionModel = new MapDefinitionModel({
|
||||
scrollwheel: false
|
||||
}, {
|
||||
parse: true,
|
||||
configModel: this.configModel,
|
||||
userModel: this.userModel,
|
||||
layerDefinitionsCollection: new Backbone.Collection()
|
||||
});
|
||||
|
||||
this.layerDefModel = new LayerDefinitionModel({
|
||||
id: 'layerA',
|
||||
type: 'cartoDB',
|
||||
letter: 'a',
|
||||
source: 'a0'
|
||||
}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
this.editorModel = new Backbone.Model();
|
||||
this.editorModel.isEditing = function () {
|
||||
return false;
|
||||
};
|
||||
this.editorModel.isDisabled = function () {
|
||||
return false;
|
||||
};
|
||||
|
||||
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; };
|
||||
|
||||
var layerContentModel = new LayerContentModel({}, {
|
||||
querySchemaModel: querySchemaModel,
|
||||
queryGeometryModel: queryGeometryModel,
|
||||
queryRowsCollection: queryRowsCollection
|
||||
});
|
||||
|
||||
this.legendDefinitionsCollection = new LegendDefinitionsCollection(null, {
|
||||
configModel: this.configModel,
|
||||
layerDefinitionsCollection: new Backbone.Collection(),
|
||||
vizId: 'v-123'
|
||||
});
|
||||
|
||||
this.view = new LegendBaseTypeView({
|
||||
mapDefinitionModel: this.mapDefinitionModel,
|
||||
userModel: this.userModel,
|
||||
configModel: this.configModel,
|
||||
layerDefinitionModel: this.layerDefModel,
|
||||
editorModel: this.editorModel,
|
||||
layerContentModel: layerContentModel,
|
||||
legendDefinitionsCollection: this.legendDefinitionsCollection,
|
||||
type: 'I dunno lol',
|
||||
userActions: {},
|
||||
modals: FactoryModals.createModalService(),
|
||||
overlayModel: new Backbone.Model(),
|
||||
infoboxModel: new Backbone.Model()
|
||||
});
|
||||
|
||||
spyOn(this.view, '_infoboxState');
|
||||
spyOn(this.view, '_onChangeEdition');
|
||||
spyOn(this.view, '_onChangeDisabled');
|
||||
spyOn(this.view, '_onTogglerChanged');
|
||||
spyOn(this.view, '_toggleOverlay');
|
||||
});
|
||||
|
||||
it('_initBinds', function () {
|
||||
this.view._initBinds();
|
||||
|
||||
this.view._layerContentModel.set('state', 'wadus');
|
||||
expect(this.view._infoboxState).toHaveBeenCalled();
|
||||
|
||||
this.view._layerDefinitionModel.set('visible', false);
|
||||
expect(this.view._infoboxState).toHaveBeenCalled();
|
||||
|
||||
this.view._mapDefinitionModel.set('legends', {});
|
||||
expect(this.view._infoboxState).toHaveBeenCalled();
|
||||
|
||||
this.view._editorModel.set('edition', true);
|
||||
expect(this.view._onChangeEdition).toHaveBeenCalled();
|
||||
|
||||
this.view._editorModel.set('disabled', false);
|
||||
expect(this.view._onChangeDisabled).toHaveBeenCalled();
|
||||
|
||||
this.view._togglerModel.set('active', true);
|
||||
expect(this.view._onTogglerChanged).toHaveBeenCalled();
|
||||
|
||||
this.view._overlayModel.set('visible', true);
|
||||
expect(this.view._toggleOverlay).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
+438
@@ -0,0 +1,438 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var LegendContentView = require('builder/editor/layers/layer-content-views/legend/legend-content-view');
|
||||
var LegendColorTypes = require('builder/editor/layers/layer-content-views/legend/color/legend-color-types');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var LegendDefinitionCollection = require('builder/data/legends/legend-definitions-collection');
|
||||
var LegendFactory = require('builder/editor/layers/layer-content-views/legend/legend-factory');
|
||||
var FactoryModals = require('../../../../factories/modals');
|
||||
var CategoryFormModel = require('builder/editor/layers/layer-content-views/legend/form/legend-category-definition-form-model');
|
||||
var StyleModel = require('builder/editor/style/style-definition-model');
|
||||
var StyleConstants = require('builder/components/form-components/_constants/_style');
|
||||
|
||||
describe('editor/layers/layer-content-view/legend/legend-content-view', function () {
|
||||
var legendType = {
|
||||
color: 'color',
|
||||
none: 'none',
|
||||
category: 'category'
|
||||
};
|
||||
|
||||
beforeEach(function () {
|
||||
this.configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
|
||||
this.userModel = new UserModel({}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
this.layerDefinitionModel = new LayerDefinitionModel({
|
||||
id: 'l-1',
|
||||
fetched: true,
|
||||
options: {
|
||||
type: 'CartoDB',
|
||||
table_name: 'foo',
|
||||
cartocss: 'asd',
|
||||
source: 'a0'
|
||||
}
|
||||
}, {
|
||||
parse: true,
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
this.layerDefinitionModel.styleModel = new StyleModel({
|
||||
type: StyleConstants.Type.SIMPLE,
|
||||
fill: {
|
||||
color: {
|
||||
fixed: '#892b27'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var layerDefinitionsCollection = new Backbone.Collection();
|
||||
layerDefinitionsCollection.add(this.layerDefinitionModel);
|
||||
|
||||
this.legendDefinitionsCollection = new LegendDefinitionCollection(null, {
|
||||
configModel: this.configModel,
|
||||
layerDefinitionsCollection: layerDefinitionsCollection,
|
||||
vizId: 'v-123'
|
||||
});
|
||||
|
||||
LegendFactory.init(this.legendDefinitionsCollection);
|
||||
|
||||
this.legendDefinitionModel = new Backbone.Model({
|
||||
type: legendType.none
|
||||
}, {
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
configModel: this.configModel,
|
||||
vizId: 'v-123'
|
||||
});
|
||||
this.legendDefinitionModel.setAttributes = jasmine.createSpy();
|
||||
|
||||
this.editorModel = new Backbone.Model({
|
||||
edition: false
|
||||
});
|
||||
this.editorModel.isEditing = function () { return false; };
|
||||
|
||||
function updateLegend () { }
|
||||
|
||||
this.view = new LegendContentView({
|
||||
overlayModel: new Backbone.Model(),
|
||||
legendTypes: LegendColorTypes,
|
||||
updateLegend: updateLegend,
|
||||
editorModel: this.editorModel,
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
legendDefinitionModel: this.legendDefinitionModel,
|
||||
legendDefinitionsCollection: this.legendDefinitionsCollection,
|
||||
type: legendType.color,
|
||||
userModel: this.userModel,
|
||||
configModel: this.configModel,
|
||||
modals: FactoryModals.createModalService()
|
||||
});
|
||||
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
describe('.render', function () {
|
||||
it('should render properly', function () {
|
||||
expect(this.view._carouselCollection.length).toBe(2); // none and custom
|
||||
expect(_.size(this.view._subviews)).toBe(1); // [Carousel]
|
||||
expect(this.view.$('.js-carousel').children().length).toBe(1);
|
||||
expect(this.view.$('.js-form').children().length).toBe(0);
|
||||
|
||||
this.view._carouselCollection.at(1).set({ selected: true });
|
||||
expect(_.size(this.view._subviews)).toBe(2); // [Carousel, FormView]
|
||||
expect(this.view.$('.js-form').children().length).toBe(1);
|
||||
|
||||
// Clean the form view when type is none
|
||||
this.view._carouselCollection.at(0).set({ selected: true });
|
||||
expect(_.size(this.view._subviews)).toBe(1);
|
||||
expect(this.view.$('.js-form').children().length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('.initBinds', function () {
|
||||
it('should call _onCarouselChange when _carouselCollection:selected changes', function () {
|
||||
spyOn(this.view, '_onCarouselChange');
|
||||
this.view._initBinds();
|
||||
this.view._carouselCollection.at(1).set({ selected: true });
|
||||
|
||||
expect(this.view._onCarouselChange).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._initViews', function () {
|
||||
it('should call ._renderCarousel', function () {
|
||||
spyOn(this.view, '_renderCarousel');
|
||||
|
||||
this.view._initViews();
|
||||
|
||||
expect(this.view._renderCarousel).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call ._renderForm', function () {
|
||||
spyOn(this.view, '_renderForm');
|
||||
|
||||
this.view._initViews();
|
||||
|
||||
expect(this.view._renderForm).toHaveBeenCalledWith(legendType.none);
|
||||
});
|
||||
|
||||
it('should call ._updateToggle', function () {
|
||||
spyOn(this.view, '_updateToggle');
|
||||
|
||||
this.view._initViews();
|
||||
|
||||
expect(this.view._updateToggle).toHaveBeenCalledWith(legendType.none);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._onCarouselChange', function () {
|
||||
it('should call the proper functions', function () {
|
||||
spyOn(this.view, '_updateLegendModel');
|
||||
spyOn(this.view, '_createFormModel');
|
||||
spyOn(this.view, '_renderForm');
|
||||
spyOn(this.view, '_updateToggle');
|
||||
|
||||
var model = this.view._carouselCollection.at(1);
|
||||
|
||||
this.view._onCarouselChange(model);
|
||||
expect(this.view._updateLegendModel).not.toHaveBeenCalled();
|
||||
expect(this.view._createFormModel).not.toHaveBeenCalled();
|
||||
expect(this.view._renderForm).not.toHaveBeenCalled();
|
||||
expect(this.view._updateToggle).not.toHaveBeenCalled();
|
||||
|
||||
model.set({ selected: true }, { silent: true });
|
||||
|
||||
this.view._onCarouselChange(model);
|
||||
expect(this.view._updateLegendModel).toHaveBeenCalled();
|
||||
expect(this.view._createFormModel).toHaveBeenCalled();
|
||||
expect(this.view._renderForm).toHaveBeenCalled();
|
||||
expect(this.view._updateToggle).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._createFormModel', function () {
|
||||
it('should unbind FormModel if exists', function () {
|
||||
var formModel = {};
|
||||
this.view._formModel = formModel;
|
||||
spyOn(this.view, '_unBindFormModel');
|
||||
|
||||
this.view._createFormModel();
|
||||
expect(this.view._unBindFormModel).toHaveBeenCalledWith(formModel);
|
||||
});
|
||||
|
||||
it('should not unbind FormModel if doesn\'t exist', function () {
|
||||
spyOn(this.view, '_unBindFormModel');
|
||||
|
||||
this.view._createFormModel();
|
||||
expect(this.view._unBindFormModel).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should create a new FormModel when type exists and not equal to "none"', function () {
|
||||
spyOn(this.view, '_bindFormModel');
|
||||
this.view._createFormModel(legendType.category);
|
||||
expect(this.view._formModel instanceof CategoryFormModel).toBe(true);
|
||||
expect(this.view._bindFormModel).toHaveBeenCalledWith(this.view._formModel);
|
||||
});
|
||||
|
||||
it('should not create a new FormModel when type is not defined or equal to "none"', function () {
|
||||
spyOn(this.view, '_bindFormModel');
|
||||
this.view._createFormModel(legendType.none);
|
||||
expect(this.view._formModel).not.toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._updateLegendModel', function () {
|
||||
var defaultAttrs = { default: 'attributes' };
|
||||
var newLegendModel = new Backbone.Model();
|
||||
|
||||
beforeEach(function () {
|
||||
spyOn(LegendFactory, 'enableLegend');
|
||||
spyOn(LegendFactory, 'createLegend').and.returnValue(newLegendModel);
|
||||
spyOn(LegendFactory, 'disableLegend');
|
||||
spyOn(LegendFactory, 'removeLegend');
|
||||
spyOn(this.view, '_updateLegend');
|
||||
spyOn(this.view, '_getDefaultAttributes').and.returnValue(defaultAttrs);
|
||||
});
|
||||
|
||||
it('should assign type value to this.lastType', function () {
|
||||
this.view._lastType = null;
|
||||
|
||||
this.view._updateLegendModel('bubble');
|
||||
|
||||
expect(this.view._lastType).toBe('bubble');
|
||||
});
|
||||
|
||||
describe('when type is not none', function () {
|
||||
beforeEach(function () {
|
||||
this.view._updateLegendModel('bubble');
|
||||
});
|
||||
|
||||
it('should call LegendFactory.enableLegend', function () {
|
||||
expect(LegendFactory.enableLegend).toHaveBeenCalledWith(this.view._type);
|
||||
});
|
||||
|
||||
it('should call LegendFactory.createLegend', function () {
|
||||
expect(LegendFactory.createLegend).toHaveBeenCalledWith(this.layerDefinitionModel, 'bubble', defaultAttrs);
|
||||
});
|
||||
|
||||
it('should call ._updateLegend', function () {
|
||||
expect(this.view._updateLegend).toHaveBeenCalledWith(newLegendModel);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when there is no type or is none', function () {
|
||||
it('should call LegendFactory.disableLegend', function () {
|
||||
this.view._updateLegendModel('none');
|
||||
expect(LegendFactory.disableLegend).toHaveBeenCalledWith('none');
|
||||
|
||||
this.view._updateLegendModel(null);
|
||||
expect(LegendFactory.disableLegend).toHaveBeenCalledWith(null);
|
||||
});
|
||||
|
||||
it('should call LegendFactory.removeLegend', function () {
|
||||
this.view._updateLegendModel('none');
|
||||
expect(LegendFactory.removeLegend).toHaveBeenCalled();
|
||||
|
||||
this.view._updateLegendModel(null);
|
||||
expect(LegendFactory.removeLegend).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call ._updateLeged', function () {
|
||||
this.view._updateLegendModel('none');
|
||||
expect(this.view._updateLegend).toHaveBeenCalledWith(null);
|
||||
|
||||
this.view._updateLegendModel(null);
|
||||
expect(this.view._updateLegend).toHaveBeenCalledWith(null);
|
||||
});
|
||||
|
||||
it('should delete this._legendDefinitionModel', function () {
|
||||
this.view._legendDefinitionModel = 'legendDefinitionModel';
|
||||
|
||||
this.view._updateLegendModel(null);
|
||||
|
||||
expect(this.view._legendDefinitionModel).not.toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('._renderForm', function () {
|
||||
it('should call ._removeForm', function () {
|
||||
spyOn(this.view, '_removeForm');
|
||||
this.view._renderForm();
|
||||
expect(this.view._removeForm).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call ._addForm if type is defined and not "none"', function () {
|
||||
spyOn(this.view, '_addForm');
|
||||
this.view._renderForm(legendType.color);
|
||||
expect(this.view._addForm).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not call ._addForm if there is no type or type is none', function () {
|
||||
spyOn(this.view, '_addForm');
|
||||
this.view._renderForm();
|
||||
expect(this.view._addForm).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._updateToggle', function () {
|
||||
it('should set editorModel:disabled true if type is "none"', function () {
|
||||
this.view._updateToggle(legendType.none);
|
||||
expect(this.view._editorModel.get('disabled')).toBe(true);
|
||||
});
|
||||
|
||||
it('should set editorModel:disabled false if type is not "none"', function () {
|
||||
this.view._updateToggle(legendType.color);
|
||||
expect(this.view._editorModel.get('disabled')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._updateChanges', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(LegendFactory, 'createLegend');
|
||||
this.view._formModel = new Backbone.Model();
|
||||
});
|
||||
|
||||
it('should call _legendDefinitionModel.setAttributes if _legendDefinitionModel is defined', function () {
|
||||
this.view._updateChanges();
|
||||
expect(this.legendDefinitionModel.setAttributes).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call LegendFactory.createLegend if _legendDefinitionModel is defined', function () {
|
||||
this.view._updateChanges();
|
||||
expect(LegendFactory.createLegend).toHaveBeenCalledWith(this.layerDefinitionModel, this.legendDefinitionModel.get('type'));
|
||||
|
||||
LegendFactory.createLegend.calls.reset();
|
||||
this.view._legendDefinitionModel = undefined;
|
||||
|
||||
this.view._updateChanges();
|
||||
expect(LegendFactory.createLegend).not.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('carousel', function () {
|
||||
it('should render carousel styleModel for points based', function () {
|
||||
this.layerDefinitionModel.styleModel = new StyleModel({
|
||||
type: StyleConstants.Type.SIMPLE,
|
||||
fill: {
|
||||
color: {
|
||||
attribute: 'n_anclajes',
|
||||
attribute_type: 'number'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var view = new LegendContentView({
|
||||
overlayModel: new Backbone.Model(),
|
||||
legendTypes: LegendColorTypes,
|
||||
updateLegend: function () { },
|
||||
editorModel: this.editorModel,
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
legendDefinitionModel: this.legendDefinitionModel,
|
||||
legendDefinitionsCollection: this.legendDefinitionsCollection,
|
||||
type: 'color',
|
||||
userModel: this.userModel,
|
||||
configModel: this.configModel,
|
||||
modals: FactoryModals.createModalService()
|
||||
});
|
||||
|
||||
view.render();
|
||||
|
||||
expect(view._carouselCollection.pluck('val')).toEqual(['none', 'choropleth', 'custom']);
|
||||
});
|
||||
|
||||
it('should render carousel styleModel for lines based', function () {
|
||||
this.layerDefinitionModel.styleModel = new StyleModel({
|
||||
type: StyleConstants.Type.SIMPLE,
|
||||
stroke: {
|
||||
color: {
|
||||
attribute: 'n_anclajes',
|
||||
attribute_type: 'number'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var view = new LegendContentView({
|
||||
overlayModel: new Backbone.Model(),
|
||||
legendTypes: LegendColorTypes,
|
||||
updateLegend: function () { },
|
||||
editorModel: this.editorModel,
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
legendDefinitionModel: this.legendDefinitionModel,
|
||||
legendDefinitionsCollection: this.legendDefinitionsCollection,
|
||||
type: 'color',
|
||||
userModel: this.userModel,
|
||||
configModel: this.configModel,
|
||||
modals: FactoryModals.createModalService()
|
||||
});
|
||||
|
||||
view.render();
|
||||
|
||||
expect(view._carouselCollection.pluck('val')).toEqual(['none', 'choropleth', 'custom']);
|
||||
});
|
||||
|
||||
it('should render category legend option in the carousel if styleModel has quantification category', function () {
|
||||
this.layerDefinitionModel.styleModel = new StyleModel({
|
||||
type: StyleConstants.Type.SIMPLE,
|
||||
fill: {
|
||||
color: {
|
||||
attribute: 'n_anclajes',
|
||||
attribute_type: 'number',
|
||||
quantification: 'category'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var view = new LegendContentView({
|
||||
overlayModel: new Backbone.Model(),
|
||||
legendTypes: LegendColorTypes,
|
||||
updateLegend: function () { },
|
||||
editorModel: this.editorModel,
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
legendDefinitionModel: this.legendDefinitionModel,
|
||||
legendDefinitionsCollection: this.legendDefinitionsCollection,
|
||||
type: 'color',
|
||||
userModel: this.userModel,
|
||||
configModel: this.configModel,
|
||||
modals: FactoryModals.createModalService()
|
||||
});
|
||||
|
||||
view.render();
|
||||
|
||||
expect(view._carouselCollection.pluck('val')).toEqual(['none', 'category', 'custom']);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not have any leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
});
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var LegendDefinitionsCollection = require('builder/data/legends/legend-definitions-collection');
|
||||
var LegendsState = require('builder/data/legends/legends-state');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var LegendFactory = require('builder/editor/layers/layer-content-views/legend/legend-factory');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var Validations = require('builder/editor/layers/layer-content-views/legend/legend-validations');
|
||||
|
||||
function getSuccessResponse (id, type, title) {
|
||||
var success = _.clone(TestResponses.save.success);
|
||||
success.responseText = _.template(success.responseText)({
|
||||
id: id,
|
||||
type: type,
|
||||
title: title || ''
|
||||
});
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
var TestResponses = {
|
||||
save: {
|
||||
success: {
|
||||
'status': '200',
|
||||
'content-type': 'application/json',
|
||||
responseText: '{"created_at":"2016-09-30T07:57:15+00:00","id":"<%= id %>","layer_id":"l-1","title":"<%= title %>","type":"<%= type %>","definition": {"color":"#fabada", "prefix":"", "suffix": "" }}'
|
||||
}
|
||||
},
|
||||
destroy: {
|
||||
success: {
|
||||
status: 204
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
describe('editor/layers/layer-content-view/legend/legend-factory', function () {
|
||||
beforeEach(function () {
|
||||
jasmine.Ajax.install();
|
||||
|
||||
this.configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
|
||||
this.layerDefinitionModel = new LayerDefinitionModel({
|
||||
id: 'l-1',
|
||||
fetched: true,
|
||||
options: {
|
||||
type: 'CartoDB',
|
||||
table_name: 'foo',
|
||||
cartocss: 'asd',
|
||||
source: 'a0'
|
||||
}
|
||||
}, {
|
||||
parse: true,
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
var styleModel = new Backbone.Model({
|
||||
fill: {
|
||||
color: {
|
||||
fixed: '#fabada'
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.layerDefinitionModel.styleModel = styleModel;
|
||||
|
||||
this.layerDefinitionsCollection = new Backbone.Collection();
|
||||
this.layerDefinitionsCollection.add(this.layerDefinitionModel);
|
||||
|
||||
this.legendDefinitionsCollection = new LegendDefinitionsCollection([], {
|
||||
layerDefinitionsCollection: this.layerDefinitionsCollection,
|
||||
configModel: this.configModel,
|
||||
vizId: 'v-123'
|
||||
});
|
||||
|
||||
LegendsState.init(this.layerDefinitionsCollection, this.legendDefinitionsCollection);
|
||||
|
||||
spyOn(Validations, 'bubble').and.returnValue(true);
|
||||
spyOn(Validations, 'choropleth').and.returnValue(true);
|
||||
spyOn(Validations, 'category').and.returnValue(true);
|
||||
|
||||
LegendFactory.init(this.legendDefinitionsCollection);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
jasmine.Ajax.uninstall();
|
||||
});
|
||||
|
||||
it('create legend', function () {
|
||||
LegendFactory.createLegend(this.layerDefinitionModel, 'bubble');
|
||||
jasmine.Ajax.requests.mostRecent().respondWith(getSuccessResponse(1, 'bubble'));
|
||||
|
||||
LegendFactory.createLegend(this.layerDefinitionModel, 'choropleth');
|
||||
jasmine.Ajax.requests.mostRecent().respondWith(getSuccessResponse(2, 'choropleth'));
|
||||
|
||||
expect(this.legendDefinitionsCollection.length).toBe(2);
|
||||
expect(this.legendDefinitionsCollection.at(0).get('type')).toBe('bubble');
|
||||
expect(this.legendDefinitionsCollection.at(1).get('type')).toBe('choropleth');
|
||||
});
|
||||
|
||||
it('update doesn\'t send two requests for the same attribute types', function () {
|
||||
LegendFactory.createLegend(this.layerDefinitionModel, 'category');
|
||||
jasmine.Ajax.requests.mostRecent().respondWith(getSuccessResponse(2, 'category'));
|
||||
|
||||
LegendFactory.createLegend(this.layerDefinitionModel, 'choropleth');
|
||||
jasmine.Ajax.requests.mostRecent().respondWith(TestResponses.destroy.success);
|
||||
jasmine.Ajax.requests.mostRecent().respondWith(getSuccessResponse(2, 'choropleth'));
|
||||
|
||||
LegendFactory.createLegend(this.layerDefinitionModel, 'custom');
|
||||
jasmine.Ajax.requests.mostRecent().respondWith(TestResponses.destroy.success);
|
||||
jasmine.Ajax.requests.mostRecent().respondWith(getSuccessResponse(2, 'custom'));
|
||||
|
||||
expect(this.legendDefinitionsCollection.length).toBe(1);
|
||||
expect(this.legendDefinitionsCollection.at(0).get('type')).toBe('custom');
|
||||
});
|
||||
|
||||
it('update an existent legend', function () {
|
||||
LegendFactory.createLegend(this.layerDefinitionModel, 'bubble', {title: 'foo'});
|
||||
jasmine.Ajax.requests.mostRecent().respondWith(getSuccessResponse(1, 'bubble'));
|
||||
|
||||
var model = this.legendDefinitionsCollection.findByLayerDefModelAndType(this.layerDefinitionModel, 'bubble');
|
||||
spyOn(model, 'set').and.callThrough();
|
||||
|
||||
model.setAttributes({title: 'fiu'});
|
||||
model.set.calls.reset();
|
||||
|
||||
LegendFactory.createLegend(this.layerDefinitionModel, 'bubble', {title: 'bar'});
|
||||
expect(model.set).toHaveBeenCalledWith({title: 'bar'});
|
||||
expect(model.get('title')).toEqual('fiu');
|
||||
expect(model.get('customState')).toEqual(['title']);
|
||||
|
||||
// Mimic the remove the title action by the UI form
|
||||
model.setAttributes({title: 'foo'});
|
||||
|
||||
model.set.calls.reset();
|
||||
LegendFactory.createLegend(this.layerDefinitionModel, 'bubble', {title: 'bar'});
|
||||
expect(model.set).toHaveBeenCalledWith({title: 'bar'});
|
||||
expect(model.get('title')).toEqual('foo');
|
||||
expect(model.get('customState')).toEqual(['title']);
|
||||
});
|
||||
|
||||
it('remove legend', function () {
|
||||
LegendFactory.createLegend(this.layerDefinitionModel, 'bubble');
|
||||
jasmine.Ajax.requests.mostRecent().respondWith(getSuccessResponse(1, 'bubble'));
|
||||
|
||||
LegendFactory.createLegend(this.layerDefinitionModel, 'choropleth');
|
||||
jasmine.Ajax.requests.mostRecent().respondWith(getSuccessResponse(2, 'choropleth'));
|
||||
|
||||
LegendFactory.removeLegend(this.layerDefinitionModel, 'bubble');
|
||||
jasmine.Ajax.requests.mostRecent().respondWith(TestResponses.destroy.success);
|
||||
|
||||
expect(this.legendDefinitionsCollection.length).toBe(1);
|
||||
expect(this.legendDefinitionsCollection.at(0).get('type')).toBe('choropleth');
|
||||
});
|
||||
|
||||
it('remove all legends', function () {
|
||||
LegendFactory.createLegend(this.layerDefinitionModel, 'bubble');
|
||||
jasmine.Ajax.requests.mostRecent().respondWith(getSuccessResponse(1, 'bubble'));
|
||||
|
||||
LegendFactory.createLegend(this.layerDefinitionModel, 'choropleth');
|
||||
jasmine.Ajax.requests.mostRecent().respondWith(getSuccessResponse(2, 'choropleth'));
|
||||
|
||||
LegendFactory.createLegend(this.layerDefinitionModel, 'category');
|
||||
jasmine.Ajax.requests.mostRecent().respondWith(getSuccessResponse(3, 'category'));
|
||||
|
||||
LegendFactory.removeAllLegend(this.layerDefinitionModel);
|
||||
expect(this.legendDefinitionsCollection.length).toBe(0);
|
||||
});
|
||||
});
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
var Backbone = require('backbone');
|
||||
var LegendPropertiesFormView = require('builder/editor/layers/layer-content-views/legend/legend-properties-form');
|
||||
|
||||
describe('editor/layers/layer-content-views/legend/legend-properties-form', function () {
|
||||
var view;
|
||||
|
||||
beforeEach(function () {
|
||||
view = new LegendPropertiesFormView({
|
||||
formModel: new Backbone.Model()
|
||||
});
|
||||
});
|
||||
|
||||
describe('.render', function () {
|
||||
it('should render properly', function () {
|
||||
spyOn(view, '_removeFormView');
|
||||
spyOn(view, '_initViews');
|
||||
view.render();
|
||||
|
||||
expect(view.$el.html()).toContain('js-propertiesForm');
|
||||
expect(view.$el.html()).toContain('js-selector');
|
||||
expect(view._removeFormView).toHaveBeenCalled();
|
||||
expect(view._initViews).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('_updateChanges', function () {
|
||||
it('should call .commit on _formView', function () {
|
||||
view.render();
|
||||
spyOn(view._formView, 'commit');
|
||||
|
||||
view._updateChanges();
|
||||
expect(view._formView.commit).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('_removeFormView', function () {
|
||||
it('should call .remove on _formView', function () {
|
||||
view.render();
|
||||
spyOn(view._formView, 'remove');
|
||||
|
||||
view._removeFormView();
|
||||
expect(view._formView.remove).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not have any leaks', function () {
|
||||
expect(view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
view.clean();
|
||||
});
|
||||
});
|
||||
+283
@@ -0,0 +1,283 @@
|
||||
var Backbone = require('backbone');
|
||||
var _ = require('underscore');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var LegendsView = require('builder/editor/layers/layer-content-views/legend/legends-view');
|
||||
var QuerySchemaModel = require('builder/data/query-schema-model');
|
||||
var QueryGeometryModel = require('builder/data/query-geometry-model');
|
||||
var LayerContentModel = require('builder/data/layer-content-model');
|
||||
var LegendDefinitionsCollection = require('builder/data/legends/legend-definitions-collection');
|
||||
var LegendFactory = require('builder/editor/layers/layer-content-views/legend/legend-factory');
|
||||
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
|
||||
var QueryRowsCollection = require('builder/data/query-rows-collection');
|
||||
var FactoryModals = require('../../../../factories/modals');
|
||||
var sqlErrorTemplate = require('builder/editor/layers/layer-content-views/legend/legend-content-sql-error.tpl');
|
||||
var actionErrorTemplate = require('builder/editor/layers/sql-error-action.tpl');
|
||||
var StyleModel = require('builder/editor/style/style-definition-model');
|
||||
var StyleConstants = require('builder/components/form-components/_constants/_style');
|
||||
|
||||
describe('editor/layers/layer-content-view/legend/legends-view', function () {
|
||||
beforeEach(function () {
|
||||
this.configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
|
||||
this.userModel = new UserModel({}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
this.querySchemaModel = new QuerySchemaModel({
|
||||
query: 'SELECT * FROM table',
|
||||
status: 'fetched'
|
||||
}, {
|
||||
configModel: {}
|
||||
});
|
||||
|
||||
spyOn(this.querySchemaModel, 'fetch');
|
||||
|
||||
this.queryGeometryModel = new QueryGeometryModel({
|
||||
query: 'SELECT * FROM table',
|
||||
status: 'fetched',
|
||||
simple_geom: 'point'
|
||||
}, {
|
||||
configModel: {}
|
||||
});
|
||||
spyOn(this.queryGeometryModel, 'fetch');
|
||||
|
||||
this.mapDefModel = new Backbone.Model({
|
||||
legends: true
|
||||
});
|
||||
|
||||
this.editorModel = new Backbone.Model({
|
||||
edition: false
|
||||
});
|
||||
this.editorModel.isEditing = function () { return false; };
|
||||
this.editorModel.isDisabled = function () { return false; };
|
||||
|
||||
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.layerDefinitionCollection = new LayerDefinitionsCollection(null, {
|
||||
configModel: this.configModel,
|
||||
userModel: this.userModel,
|
||||
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
|
||||
mapId: 'm123',
|
||||
stateDefinitionModel: {}
|
||||
});
|
||||
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.layerDefinitionCollection
|
||||
});
|
||||
spyOn(this.layerDefinitionModel, 'save');
|
||||
spyOn(this.layerDefinitionModel, 'getAnalysisDefinitionNodeModel').and.returnValue(this.a0);
|
||||
|
||||
this.layerDefinitionModel.styleModel = new StyleModel({
|
||||
type: StyleConstants.Type.SIMPLE,
|
||||
fill: {
|
||||
color: {
|
||||
fixed: '#892b27'
|
||||
},
|
||||
size: {
|
||||
attribute: 10
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.legendDefinitionsCollection = new LegendDefinitionsCollection(null, {
|
||||
configModel: {},
|
||||
layerDefinitionsCollection: new Backbone.Collection(),
|
||||
vizId: 'v-123'
|
||||
});
|
||||
|
||||
LegendFactory.init(this.legendDefinitionsCollection);
|
||||
|
||||
spyOn(LegendsView.prototype, '_changeStyle');
|
||||
|
||||
this.queryRowsCollection = new QueryRowsCollection([], {
|
||||
configModel: this.configModel,
|
||||
querySchemaModel: this.querySchemaModel
|
||||
});
|
||||
|
||||
spyOn(this.queryRowsCollection, 'fetch');
|
||||
|
||||
this.layerContentModel = new LayerContentModel({}, {
|
||||
querySchemaModel: this.querySchemaModel,
|
||||
queryGeometryModel: this.queryGeometryModel,
|
||||
queryRowsCollection: this.queryRowsCollection
|
||||
});
|
||||
|
||||
this.view = new LegendsView({
|
||||
mapDefinitionModel: this.mapDefModel,
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
legendDefinitionsCollection: this.legendDefinitionsCollection,
|
||||
editorModel: this.editorModel,
|
||||
queryGeometryModel: this.queryGeometryModel,
|
||||
layerContentModel: this.layerContentModel,
|
||||
userActions: {
|
||||
saveLayer: jasmine.createSpy('saveLayer')
|
||||
},
|
||||
userModel: this.userModel,
|
||||
configModel: this.configModel,
|
||||
modals: FactoryModals.createModalService()
|
||||
});
|
||||
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
describe('.render', function () {
|
||||
it('should render properly', function () {
|
||||
expect(_.size(this.view._subviews)).toBe(1); // [TabPaneView]
|
||||
expect(this.view.$el.text()).toContain('editor.legend.menu-tab-pane-labels.color');
|
||||
expect(this.view.$el.text()).toContain('editor.legend.menu-tab-pane-labels.size');
|
||||
});
|
||||
|
||||
it('should render error template if layer content model is errored', function () {
|
||||
spyOn(this.layerContentModel, 'isErrored').and.returnValue(true);
|
||||
var errorTemplate = _t('editor.error-query.body', {
|
||||
action: actionErrorTemplate({
|
||||
label: _t('editor.error-query.label')
|
||||
})
|
||||
});
|
||||
|
||||
this.view.render();
|
||||
|
||||
expect(this.view.el.innerHTML).toBe(sqlErrorTemplate({ body: errorTemplate }));
|
||||
});
|
||||
});
|
||||
|
||||
it('should show/hide overlay properly', function () {
|
||||
this.layerDefinitionModel.set('visible', false);
|
||||
expect(this.view._overlayModel.get('visible')).toBe(true);
|
||||
|
||||
this.layerDefinitionModel.set('visible', true);
|
||||
expect(this.view._overlayModel.get('visible')).toBe(false);
|
||||
});
|
||||
|
||||
describe('._initModels', function () {
|
||||
it('should create _infoboxModel', function () {
|
||||
this.view._infoboxModel = undefined;
|
||||
expect(this.view._infoboxModel).not.toBeDefined();
|
||||
|
||||
this.view._initModels();
|
||||
expect(this.view._infoboxModel).toBeDefined();
|
||||
});
|
||||
|
||||
it('should create _overlayModel', function () {
|
||||
this.view._overlayModel = undefined;
|
||||
expect(this.view._overlayModel).not.toBeDefined();
|
||||
|
||||
this.view._initModels();
|
||||
expect(this.view._overlayModel).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._initBinds', function () {
|
||||
it('should call _changeStyle when editorModel:edition changes', function () {
|
||||
this.editorModel.set({ edition: true });
|
||||
|
||||
expect(LegendsView.prototype._changeStyle).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._isErrored', function () {
|
||||
it('should return layerContentModel isErrored', function () {
|
||||
var isErroredSpy = spyOn(this.view._layerContentModel, 'isErrored');
|
||||
|
||||
isErroredSpy.and.returnValue(false);
|
||||
expect(this.view._isErrored()).toBe(false);
|
||||
|
||||
isErroredSpy.and.returnValue(true);
|
||||
expect(this.view._isErrored()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._isLayerHidden', function () {
|
||||
it('should return true if layerDefinitionModel.visible is false', function () {
|
||||
this.layerDefinitionModel.set('visible', false, { silent: true });
|
||||
expect(this.view._isLayerHidden()).toBe(true);
|
||||
|
||||
this.layerDefinitionModel.set('visible', true, { silent: true });
|
||||
expect(this.view._isLayerHidden()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._showHiddenLayer', function () {
|
||||
it('should call layerDefinitionModel.toggleVisible', function () {
|
||||
spyOn(this.view._layerDefinitionModel, 'toggleVisible');
|
||||
|
||||
this.view._showHiddenLayer();
|
||||
|
||||
expect(this.view._layerDefinitionModel.toggleVisible).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call userActions.saveLayer', function () {
|
||||
this.view._showHiddenLayer();
|
||||
expect(this.view._userActions.saveLayer).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('.isStyleCompatible', function () {
|
||||
it('should return true if legend type is not size', function () {
|
||||
expect(this.view._isStyleCompatible('color')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return true if any of the legends size is compatible', function () {
|
||||
expect(this.view._isStyleCompatible('size')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false if no legend size is compatible', function () {
|
||||
this.view._layerDefinitionModel.styleModel = new StyleModel({});
|
||||
expect(this.view._isStyleCompatible('size')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when layer has errors', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(this.view, '_isErrored').and.returnValue(true);
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
describe('.render', function () {
|
||||
it('should render properly', function () {
|
||||
expect(this.view.$el.text()).toContain('editor.error-query.body');
|
||||
});
|
||||
});
|
||||
|
||||
describe('._onQueryChanged', function () {
|
||||
it('should call render', function () {
|
||||
spyOn(this.view, 'render');
|
||||
|
||||
this.view._onQueryChanged();
|
||||
|
||||
expect(this.view.render).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should not have any leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
});
|
||||
+274
@@ -0,0 +1,274 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var LegendSizeView = require('builder/editor/layers/layer-content-views/legend/size/legend-size-view');
|
||||
var LayerContentModel = require('builder/data/layer-content-model');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var LegendDefinitionsCollection = require('builder/data/legends/legend-definitions-collection');
|
||||
var LegendBaseDefinitionModel = require('builder/data/legends/legend-base-definition-model');
|
||||
var LegendCustomDefinitionModel = require('builder/data/legends/legend-custom-definition-model');
|
||||
var LegendFactory = require('builder/editor/layers/layer-content-views/legend/legend-factory');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var FactoryModals = require('../../../../../factories/modals');
|
||||
var MetricsTracker = require('builder/components/metrics/metrics-tracker');
|
||||
|
||||
describe('editor/layers/layer-content-view/legend/size/legend-size-view', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(MetricsTracker, 'track');
|
||||
|
||||
this.configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
|
||||
this.userModel = new UserModel({}, {
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
this.mapDefModel = new Backbone.Model({
|
||||
legends: true
|
||||
});
|
||||
spyOn(this.mapDefModel, 'save');
|
||||
|
||||
this.userActions = {
|
||||
saveLayer: jasmine.createSpy('saveLayer')
|
||||
};
|
||||
|
||||
this.layerDefinitionModel = new LayerDefinitionModel({
|
||||
id: 'l-1',
|
||||
fetched: true,
|
||||
options: {
|
||||
type: 'CartoDB',
|
||||
table_name: 'foo',
|
||||
cartocss: 'asd',
|
||||
source: 'a0'
|
||||
}
|
||||
}, {
|
||||
parse: true,
|
||||
configModel: this.configModel
|
||||
});
|
||||
|
||||
this.layerDefinitionsCollection = new Backbone.Collection();
|
||||
this.layerDefinitionsCollection.add(this.layerDefinitionModel);
|
||||
|
||||
this.legendDefinitionsCollection = new LegendDefinitionsCollection(null, {
|
||||
configModel: this.configModel,
|
||||
layerDefinitionsCollection: this.layerDefinitionsCollection,
|
||||
vizId: 'v-123'
|
||||
});
|
||||
|
||||
LegendFactory.init(this.legendDefinitionsCollection);
|
||||
|
||||
this.editorModel = new Backbone.Model({
|
||||
edition: false
|
||||
});
|
||||
this.editorModel.isEditing = function () { return false; };
|
||||
this.editorModel.isDisabled = function () { return false; };
|
||||
|
||||
this.htmlModel = new Backbone.Model();
|
||||
|
||||
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 LegendSizeView({
|
||||
userActions: this.userActions,
|
||||
editorModel: this.editorModel,
|
||||
mapDefinitionModel: this.mapDefModel,
|
||||
layerContentModel: this.layerContentModel,
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
legendDefinitionsCollection: this.legendDefinitionsCollection,
|
||||
type: 'size',
|
||||
userModel: this.userModel,
|
||||
configModel: this.configModel,
|
||||
modals: FactoryModals.createModalService(),
|
||||
infoboxModel: new Backbone.Model(),
|
||||
overlayModel: new Backbone.Model()
|
||||
});
|
||||
});
|
||||
|
||||
describe('.render', function () {
|
||||
it('should render properly', function () {
|
||||
this.view.render();
|
||||
|
||||
expect(_.size(this.view._subviews)).toBe(1); // [PanelWithOptionsView]
|
||||
expect(this.view.$('.Infobox').length).toBe(0);
|
||||
});
|
||||
|
||||
it('should render custom html legend infobox properly', function () {
|
||||
this.legendDefinitionsCollection.add(new LegendCustomDefinitionModel({
|
||||
html: '<ul><li>Foo</li></ul>'
|
||||
}, {
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
configModel: this.configModel,
|
||||
vizId: 'v-123'
|
||||
}));
|
||||
this.view._legendDefinitionModel = this.legendDefinitionsCollection.at(0);
|
||||
this.view._infoboxState();
|
||||
this.view.render();
|
||||
|
||||
expect(this.view.$('.Infobox').length).toBe(1);
|
||||
expect(this.view.$('.Infobox').html()).toContain('editor.legend.messages.custom-legend.body');
|
||||
});
|
||||
|
||||
it('should render hidden layer infobox properly', function () {
|
||||
this.layerDefinitionModel.set({visible: false});
|
||||
this.view.render();
|
||||
|
||||
expect(this.view.$('.Infobox').length).toBe(1);
|
||||
expect(this.view.$('.Infobox').html()).toContain('editor.messages.layer-hidden.body');
|
||||
});
|
||||
|
||||
it('should render legends-disabled infobox properly', function () {
|
||||
this.mapDefModel.set({legends: false});
|
||||
this.view.render();
|
||||
|
||||
expect(this.view.$('.Infobox').length).toBe(1);
|
||||
expect(this.view.$('.Infobox').html()).toContain('editor.legend.messages.legends-disabled.body');
|
||||
|
||||
this.view.$('.Infobox .js-action').click();
|
||||
expect(this.mapDefModel.save).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._initBinds', function () {
|
||||
it('should call ._infoboxState when layerContentModel:state changes', function () {
|
||||
spyOn(this.view, '_infoboxState');
|
||||
|
||||
this.view._initBinds();
|
||||
this.view._layerContentModel.trigger('change:state');
|
||||
|
||||
expect(this.view._infoboxState).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call ._infoboxState when _layerDefinitionModel:visible changes', function () {
|
||||
spyOn(this.view, '_infoboxState');
|
||||
|
||||
this.view._initBinds();
|
||||
this.view._layerDefinitionModel.trigger('change:visible');
|
||||
|
||||
expect(this.view._infoboxState).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call ._infoboxState when _mapDefinitionModel:legends changes', function () {
|
||||
spyOn(this.view, '_infoboxState');
|
||||
|
||||
this.view._initBinds();
|
||||
this.view._mapDefinitionModel.trigger('change:legends');
|
||||
|
||||
expect(this.view._infoboxState).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call ._onChangeEdition when _editorModel:edition changes', function () {
|
||||
spyOn(this.view, '_onChangeEdition');
|
||||
|
||||
this.view._initBinds();
|
||||
this.view._editorModel.trigger('change:edition');
|
||||
|
||||
expect(this.view._onChangeEdition).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call ._onChangeDisabled when _editorModel:disabled changes', function () {
|
||||
spyOn(this.view, '_onChangeDisabled');
|
||||
|
||||
this.view._initBinds();
|
||||
this.view._editorModel.trigger('change:disabled');
|
||||
|
||||
expect(this.view._onChangeDisabled).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call ._onTogglerChanged when _togglerModel:active changes', function () {
|
||||
spyOn(this.view, '_onTogglerChanged');
|
||||
|
||||
this.view._initBinds();
|
||||
this.view._togglerModel.trigger('change:active');
|
||||
|
||||
expect(this.view._onTogglerChanged).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();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._isLayerHidden', function () {
|
||||
it('should return true if layerDefinitionModel.visible is false', function () {
|
||||
this.layerDefinitionModel.set('visible', false, { silent: true });
|
||||
expect(this.view._isLayerHidden()).toBe(true);
|
||||
|
||||
this.layerDefinitionModel.set('visible', true, { silent: true });
|
||||
expect(this.view._isLayerHidden()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._isErrored', function () {
|
||||
it('should return layerContentModel isErrored', function () {
|
||||
var isErroredSpy = spyOn(this.view._layerContentModel, 'isErrored');
|
||||
|
||||
isErroredSpy.and.returnValue(false);
|
||||
expect(this.view._isErrored()).toBe(false);
|
||||
|
||||
isErroredSpy.and.returnValue(true);
|
||||
expect(this.view._isErrored()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._showHiddenLayer', function () {
|
||||
it('should call layerDefinitionModel.toggleVisible', function () {
|
||||
spyOn(this.view._layerDefinitionModel, 'toggleVisible');
|
||||
|
||||
this.view._showHiddenLayer();
|
||||
|
||||
expect(this.view._layerDefinitionModel.toggleVisible).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call userActions.saveLayer', function () {
|
||||
this.view._showHiddenLayer();
|
||||
expect(this.view._userActions.saveLayer).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it('should transmit html snippets changes properly', function () {
|
||||
this.legendDefinitionsCollection.add(new LegendBaseDefinitionModel({
|
||||
type: 'category'
|
||||
}, {
|
||||
layerDefinitionModel: this.layerDefinitionModel,
|
||||
configModel: this.configModel,
|
||||
vizId: 'v-123'
|
||||
}));
|
||||
|
||||
this.view._legendDefinitionModel = this.legendDefinitionsCollection.at(0);
|
||||
this.view._codemirrorModel.set('content', 'Wadus[[Legend]]');
|
||||
this.view._saveLegendHTML();
|
||||
this.view.render();
|
||||
|
||||
expect(this.view._legendDefinitionModel.get('preHTMLSnippet')).toBe('Wadus');
|
||||
|
||||
this.view._codemirrorModel.set('content', 'Foo[[Legend]]Bar');
|
||||
this.view._saveLegendHTML();
|
||||
|
||||
expect(this.view._legendDefinitionModel.get('preHTMLSnippet')).toBe('Foo');
|
||||
expect(this.view._legendDefinitionModel.get('postHTMLSnippet')).toBe('Bar');
|
||||
});
|
||||
|
||||
it('should not have any leaks', function () {
|
||||
this.view.render();
|
||||
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,401 @@
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
|
||||
var LayerHeaderView = require('builder/editor/layers/layer-header-view');
|
||||
var SyncModel = require('builder/data/synchronization-model');
|
||||
var FactoryModals = require('../../factories/modals');
|
||||
|
||||
var getLayerDefinitionModelFixture = require('fixtures/builder/layer-definition-model.fixture.js');
|
||||
var getConfigModelFixture = require('fixtures/builder/config-model.fixture.js');
|
||||
var getQueryGeometryModelFixture = require('fixtures/builder/query-geometry-model.fixture.js');
|
||||
var getQuerySchemaModelFixture = require('fixtures/builder/query-schema-model.fixture.js');
|
||||
var getQueryRowsCollectionModelFixture = require('fixtures/builder/query-rows-collection.fixture.js');
|
||||
var getNodeModelFixture = require('fixtures/builder/node-model.fixture.js');
|
||||
|
||||
describe('editor/layers/layer-header-view', function () {
|
||||
var view;
|
||||
var configModel;
|
||||
var layerDefinitionModel;
|
||||
var stateDefinitionModel;
|
||||
var tableNodeModel;
|
||||
var queryGeometryModel;
|
||||
var querySchemaModel;
|
||||
var queryRowsCollection;
|
||||
var visDefinitionModel;
|
||||
var widgetDefinitionsCollection;
|
||||
var userModel;
|
||||
var editorModel;
|
||||
var sourceNodeModel;
|
||||
var tableName = 'foo';
|
||||
var NodeModel = getNodeModelFixture;
|
||||
|
||||
var createViewFn = function (options) {
|
||||
options = options || {};
|
||||
|
||||
var hasValueAsync = options.hasOwnProperty('hasValueAsync') ? options.hasValueAsync : false;
|
||||
var isEmptyAsync = options.hasOwnProperty('isEmptyAsync') ? options.isEmptyAsync : true;
|
||||
var canBeGeoreferenced = options.hasOwnProperty('canBeGeoreferenced') ? options.canBeGeoreferenced : false;
|
||||
|
||||
configModel = getConfigModelFixture();
|
||||
|
||||
editorModel = new Backbone.Model();
|
||||
editorModel.isEditing = function () {
|
||||
return options && options.hasOwnProperty('editing') ? options.editing : false;
|
||||
};
|
||||
|
||||
userModel = new Backbone.Model();
|
||||
|
||||
layerDefinitionModel = getLayerDefinitionModelFixture({
|
||||
id: 'l-1',
|
||||
name: tableName,
|
||||
configModel: configModel,
|
||||
parse: true
|
||||
});
|
||||
|
||||
stateDefinitionModel = {
|
||||
key: 'value'
|
||||
};
|
||||
|
||||
tableNodeModel = null;
|
||||
|
||||
queryGeometryModel = getQueryGeometryModelFixture();
|
||||
|
||||
querySchemaModel = getQuerySchemaModelFixture({
|
||||
query: options.query
|
||||
});
|
||||
|
||||
queryRowsCollection = getQueryRowsCollectionModelFixture({
|
||||
querySchemaModel: querySchemaModel
|
||||
});
|
||||
|
||||
sourceNodeModel = new Backbone.Model({
|
||||
type: 'source',
|
||||
table_name: tableName
|
||||
});
|
||||
|
||||
sourceNodeModel.queryGeometryModel = queryGeometryModel;
|
||||
sourceNodeModel.querySchemaModel = querySchemaModel;
|
||||
sourceNodeModel.queryRowsCollection = queryRowsCollection;
|
||||
sourceNodeModel.isCustomQueryApplied = function () { return true; };
|
||||
layerDefinitionModel.getAnalysisDefinitionNodeModel = function () {};
|
||||
|
||||
spyOn(sourceNodeModel.queryGeometryModel, 'hasValueAsync').and.returnValue(Promise.resolve(hasValueAsync));
|
||||
spyOn(layerDefinitionModel, 'isEmptyAsync').and.returnValue(Promise.resolve(isEmptyAsync));
|
||||
spyOn(layerDefinitionModel, 'canBeGeoreferenced').and.returnValue(Promise.resolve(canBeGeoreferenced));
|
||||
spyOn(layerDefinitionModel, 'getAnalysisDefinitionNodeModel').and.returnValue(sourceNodeModel);
|
||||
|
||||
visDefinitionModel = new Backbone.Model();
|
||||
widgetDefinitionsCollection = new Backbone.Collection();
|
||||
|
||||
return new LayerHeaderView({
|
||||
configModel: configModel,
|
||||
editorModel: editorModel,
|
||||
layerDefinitionModel: layerDefinitionModel,
|
||||
layerDefinitionsCollection: {},
|
||||
modals: FactoryModals.createModalService(),
|
||||
stateDefinitionModel: stateDefinitionModel,
|
||||
tableNodeModel: tableNodeModel,
|
||||
userActions: {},
|
||||
userModel: userModel,
|
||||
visDefinitionModel: visDefinitionModel,
|
||||
widgetDefinitionsCollection: widgetDefinitionsCollection
|
||||
});
|
||||
};
|
||||
|
||||
describe('.initialize', function () {
|
||||
describe('._initVisTableModel', function () {
|
||||
it('should initialize a VisTableModel if _sourceNodeModel is defined', function () {
|
||||
view = createViewFn();
|
||||
expect(view._visTableModel).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._initViewState', function () {
|
||||
it('should set default state values', function () {
|
||||
spyOn(LayerHeaderView.prototype, '_setViewStateValues');
|
||||
|
||||
view = createViewFn();
|
||||
|
||||
expect(view._viewState.get('isLayerEmpty')).toEqual(false);
|
||||
expect(view._viewState.get('hasGeom')).toEqual(true);
|
||||
expect(view._viewState.get('canBeGeoreferenced')).toEqual(false);
|
||||
expect(LayerHeaderView.prototype._setViewStateValues).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('._setViewStateValues', function () {
|
||||
it('should be able to update _viewState properties', function (done) {
|
||||
view = createViewFn({
|
||||
hasValueAsync: true,
|
||||
isEmptyAsync: true,
|
||||
canBeGeoreferenced: true
|
||||
});
|
||||
|
||||
view._viewState.set({
|
||||
isLayerEmpty: false,
|
||||
hasGeom: false,
|
||||
canBeGeoreferenced: false
|
||||
});
|
||||
|
||||
view._setViewStateValues();
|
||||
|
||||
setTimeout(function () {
|
||||
expect(view._viewState.get('isLayerEmpty')).toEqual(true);
|
||||
expect(view._viewState.get('hasGeom')).toEqual(true);
|
||||
expect(view._viewState.get('canBeGeoreferenced')).toEqual(true);
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('.render', function () {
|
||||
it('should render properly', function () {
|
||||
view = createViewFn();
|
||||
view.render();
|
||||
expect(_.size(view._subviews)).toBe(3); // [inlineEditor, centerTooltip, toggleMenuTooltip]
|
||||
});
|
||||
|
||||
it('should have class for onboarding', function () {
|
||||
view = createViewFn();
|
||||
view.render();
|
||||
|
||||
expect(view.$el.hasClass('js-editorPanelHeader')).toBe(true);
|
||||
});
|
||||
|
||||
it('should show zoom icon when there is geometry', function (done) {
|
||||
view = createViewFn({
|
||||
hasValueAsync: true,
|
||||
isEmptyAsync: true,
|
||||
canBeGeoreferenced: true
|
||||
});
|
||||
|
||||
view.render();
|
||||
|
||||
setTimeout(function () {
|
||||
expect(view.$('.js-zoom')[0].style.display).not.toBe('none');
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
it('should hide zoom icon when there is no geometry', function (done) {
|
||||
view = createViewFn({
|
||||
hasValueAsync: false,
|
||||
isEmptyAsync: true,
|
||||
canBeGeoreferenced: true
|
||||
});
|
||||
|
||||
view.render();
|
||||
|
||||
setTimeout(function () {
|
||||
expect(view.$('.js-zoom')[0].style.display).toBe('none');
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
it('should show warning icon when the layer is empty', function (done) {
|
||||
view = createViewFn({
|
||||
hasValueAsync: true,
|
||||
isEmptyAsync: true,
|
||||
canBeGeoreferenced: false
|
||||
});
|
||||
|
||||
view.render();
|
||||
|
||||
setTimeout(function () {
|
||||
expect(view.$el.find('.js-warningIcon').length).toBe(1);
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
it('should show warning icon when the layer can be georeferenced', function (done) {
|
||||
view = createViewFn({
|
||||
hasValueAsync: true,
|
||||
isEmptyAsync: false,
|
||||
canBeGeoreferenced: true
|
||||
});
|
||||
|
||||
view.render();
|
||||
|
||||
setTimeout(function () {
|
||||
expect(view.$el.find('.js-warningIcon').length).toBe(1);
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
it('should not show warning icon when the layer is not empty and can not be georeferenced', function (done) {
|
||||
view = createViewFn({
|
||||
hasValueAsync: true,
|
||||
isEmptyAsync: false,
|
||||
canBeGeoreferenced: false
|
||||
});
|
||||
|
||||
view.render();
|
||||
|
||||
setTimeout(function () {
|
||||
expect(view.$el.find('.js-warningIcon').length).toBe(0);
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._initViews', function () {
|
||||
it('should call _addSyncInfo', function () {
|
||||
view = createViewFn();
|
||||
spyOn(view, '_addSyncInfo');
|
||||
|
||||
view._initViews();
|
||||
|
||||
expect(view._addSyncInfo).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._addSyncInfo', function () {
|
||||
beforeEach(function () {
|
||||
view = createViewFn();
|
||||
});
|
||||
|
||||
it('should call _createSyncInfo with the proper argument if the node has a synced tableModel', function () {
|
||||
layerDefinitionModel.getAnalysisDefinitionNodeModel.and.returnValue(new NodeModel({ id: 765 }, true));
|
||||
spyOn(view, '_createSyncInfo');
|
||||
|
||||
view._addSyncInfo();
|
||||
|
||||
expect(view._createSyncInfo.calls.mostRecent().args[0].id).toEqual(765);
|
||||
});
|
||||
|
||||
it('should not call _createSyncInfo if the node has not a synced tableModel', function () {
|
||||
layerDefinitionModel.getAnalysisDefinitionNodeModel.and.returnValue(new NodeModel({}));
|
||||
spyOn(view, '_createSyncInfo');
|
||||
|
||||
view._addSyncInfo();
|
||||
|
||||
expect(view._createSyncInfo).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not call _createSyncInfo if the node does not have a tableModel', function () {
|
||||
layerDefinitionModel.getAnalysisDefinitionNodeModel.and.returnValue(new NodeModel());
|
||||
spyOn(view, '_createSyncInfo');
|
||||
|
||||
view._addSyncInfo();
|
||||
|
||||
expect(view._createSyncInfo).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._createSyncInfo', function () {
|
||||
it('should create SyncInfoView object with proper table and sync models', function () {
|
||||
view = createViewFn();
|
||||
var syncModel = new SyncModel(null, { configModel: configModel });
|
||||
var nodeModel = new NodeModel({ id: 765 }, true, syncModel);
|
||||
|
||||
view._createSyncInfo(nodeModel.tableModel);
|
||||
|
||||
expect(view._syncInfoView).not.toBeUndefined();
|
||||
expect(view._syncInfoView._syncModel.cid).toEqual(syncModel.cid);
|
||||
expect(view._syncInfoView._tableModel.id).toEqual(765);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._bindEvents', function () {
|
||||
it('should call _onSourceChanged when layer definition model changes source', function () {
|
||||
view = createViewFn();
|
||||
spyOn(view, '_onSourceChanged');
|
||||
view._bindEvents();
|
||||
|
||||
layerDefinitionModel.set('source', {});
|
||||
|
||||
expect(view._onSourceChanged).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._changeStyle', function () {
|
||||
it('should have proper classes if editing in expert mode', function () {
|
||||
view = createViewFn({ editing: true });
|
||||
view.render();
|
||||
view._changeStyle();
|
||||
|
||||
expect(view._getTitle().hasClass('u-whiteTextColor')).toBe(true);
|
||||
expect(view._getText().hasClass('u-altTextColor')).toBe(true);
|
||||
expect(view._getInlineEditor().hasClass('u-mainTextColor')).toBe(true);
|
||||
expect(view._getLink().hasClass('u-whiteTextColor')).toBe(true);
|
||||
expect(view._getBack().hasClass('u-whiteTextColor')).toBe(true);
|
||||
expect(view._getToggleMenu().hasClass('is-white')).toBe(true);
|
||||
expect(view._getZoom().hasClass('is-white')).toBe(true);
|
||||
});
|
||||
|
||||
it('should have proper classes if not in expert mode', function () {
|
||||
view = createViewFn();
|
||||
view.render();
|
||||
view._changeStyle();
|
||||
|
||||
expect(view._getTitle().hasClass('u-whiteTextColor')).toBe(false);
|
||||
expect(view._getText().hasClass('u-altTextColor')).toBe(false);
|
||||
expect(view._getInlineEditor().hasClass('u-mainTextColor')).toBe(false);
|
||||
expect(view._getLink().hasClass('u-altTextColor')).toBe(false);
|
||||
expect(view._getBack().hasClass('u-whiteTextColor')).toBe(false);
|
||||
expect(view._getToggleMenu().hasClass('is-white')).toBe(false);
|
||||
expect(view._getZoom().hasClass('is-white')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._onZoomClicked', function () {
|
||||
it('should call to zoomToData with the right query', function () {
|
||||
view = createViewFn({query: ''});
|
||||
|
||||
// The only way we have to know if zoomToData has been called is to provoke an
|
||||
// error. This way we know that the query has been extracted from our querySchemaModel
|
||||
// and that zoomToData has been called.
|
||||
expect(function () {
|
||||
view._onZoomClicked();
|
||||
}).toThrowError('query is required');
|
||||
});
|
||||
});
|
||||
|
||||
describe('._getNodeModel', function () {
|
||||
it('should return node model', function () {
|
||||
view = createViewFn();
|
||||
view._getNodeModel();
|
||||
|
||||
expect(layerDefinitionModel.getAnalysisDefinitionNodeModel).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('layer name edition', function () {
|
||||
it('should update name properly', function () {
|
||||
view = createViewFn();
|
||||
view.render();
|
||||
view._onSaveSuccess('wadus');
|
||||
|
||||
expect(view.$('.js-title-editor').attr('title')).toBe('wadus');
|
||||
expect(view.$('.js-title').text()).toBe('wadus');
|
||||
});
|
||||
});
|
||||
|
||||
describe('_showOrHideZoomVisibility', function () {
|
||||
it('should show zoom if layer has geometries', function () {
|
||||
view = createViewFn({
|
||||
hasValueAsync: true
|
||||
});
|
||||
|
||||
view._viewState.set('hasGeom', true);
|
||||
view.render();
|
||||
|
||||
var zoom = view._getZoom();
|
||||
expect(zoom[0].style.display).toBeDefined();
|
||||
expect(zoom[0].style.display).not.toEqual('none');
|
||||
});
|
||||
|
||||
it('should not show zoom if layer has not geometries', function () {
|
||||
view = createViewFn({
|
||||
hasValueAsync: false
|
||||
});
|
||||
|
||||
view._viewState.set('hasGeom', false);
|
||||
view.render();
|
||||
|
||||
var zoom = view._getZoom();
|
||||
expect(zoom[0].style.display).toEqual('none');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,128 @@
|
||||
var Backbone = require('backbone');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var BaseTiledLayerView = require('builder/editor/layers/layer-views/base-tiled-layer-view');
|
||||
var PlainColorLayerView = require('builder/editor/layers/layer-views/plain-color-layer-view');
|
||||
var BackgroundImageLayerView = require('builder/editor/layers/layer-views/background-image-layer-view');
|
||||
var DataLayerView = require('builder/editor/layers/layer-views/data-layer-view');
|
||||
var ErroredDataLayerView = require('builder/editor/layers/layer-views/errored-data-layer-view');
|
||||
var LabelsLayerView = require('builder/editor/layers/layer-views/labels-layer-view');
|
||||
var LayerViewFactory = require('builder/editor/layers/layer-view-factory');
|
||||
var ModalsService = require('builder/components/modals/modals-service-model');
|
||||
var AnalysisDefinitionNodeModel = require('builder/data/analysis-definition-node-model');
|
||||
|
||||
describe('builder/editor/layers/layer-view-factory', function () {
|
||||
beforeEach(function () {
|
||||
this.nodeDefModel = new AnalysisDefinitionNodeModel({
|
||||
id: 'a2',
|
||||
type: 'buffer',
|
||||
params: {}
|
||||
}, {
|
||||
configModel: {}
|
||||
});
|
||||
|
||||
spyOn(this.nodeDefModel.querySchemaModel, 'fetch');
|
||||
spyOn(this.nodeDefModel.queryGeometryModel, 'fetch');
|
||||
spyOn(this.nodeDefModel.queryRowsCollection, 'fetch');
|
||||
|
||||
this.layerViewFactory = new LayerViewFactory({
|
||||
modals: new ModalsService(),
|
||||
userActions: {},
|
||||
stackLayoutModel: {},
|
||||
layerDefinitionsCollection: {},
|
||||
analysisDefinitionNodesCollection: {},
|
||||
analysis: {},
|
||||
configModel: {},
|
||||
sortableSelector: '.selector',
|
||||
stateDefinitionModel: {},
|
||||
visDefinitionModel: {},
|
||||
widgetDefinitionsCollection: new Backbone.Collection()
|
||||
});
|
||||
});
|
||||
|
||||
it('should return an instance of BaseTiledLayerView', function () {
|
||||
var layerModel = new LayerDefinitionModel({
|
||||
type: 'Tiled',
|
||||
order: 0
|
||||
}, {
|
||||
configModel: {}
|
||||
});
|
||||
spyOn(layerModel, 'findAnalysisDefinitionNodeModel').and.returnValue(this.nodeDefModel);
|
||||
var layerView = this.layerViewFactory.createLayerView(layerModel);
|
||||
expect(layerView instanceof BaseTiledLayerView).toBe(true);
|
||||
});
|
||||
|
||||
it('should return an instance of PlainColorLayerView', function () {
|
||||
var layerModel = new LayerDefinitionModel({
|
||||
type: 'Plain',
|
||||
order: 0,
|
||||
color: '#FABADA'
|
||||
}, {
|
||||
configModel: {}
|
||||
});
|
||||
spyOn(layerModel, 'findAnalysisDefinitionNodeModel').and.returnValue(this.nodeDefModel);
|
||||
var layerView = this.layerViewFactory.createLayerView(layerModel);
|
||||
expect(layerView instanceof PlainColorLayerView).toBe(true);
|
||||
});
|
||||
|
||||
it('should return an instance of BackgroundImageLayerView', function () {
|
||||
var layerModel = new LayerDefinitionModel({
|
||||
type: 'Plain',
|
||||
order: 0,
|
||||
image: 'http://example.com/image.png'
|
||||
}, {
|
||||
configModel: {}
|
||||
});
|
||||
spyOn(layerModel, 'findAnalysisDefinitionNodeModel').and.returnValue(this.nodeDefModel);
|
||||
var layerView = this.layerViewFactory.createLayerView(layerModel);
|
||||
expect(layerView instanceof BackgroundImageLayerView).toBe(true);
|
||||
});
|
||||
|
||||
describe('data-layer-view', function () {
|
||||
var layerModel;
|
||||
beforeEach(function () {
|
||||
layerModel = new LayerDefinitionModel({
|
||||
type: 'CartoDB',
|
||||
order: 1
|
||||
}, {
|
||||
configModel: {}
|
||||
});
|
||||
spyOn(layerModel, 'findAnalysisDefinitionNodeModel');
|
||||
});
|
||||
|
||||
it('should return an instance of DataLayerView', function () {
|
||||
layerModel.findAnalysisDefinitionNodeModel.and.returnValue(this.nodeDefModel);
|
||||
var layerView = this.layerViewFactory.createLayerView(layerModel);
|
||||
expect(layerView instanceof DataLayerView).toBe(true);
|
||||
});
|
||||
|
||||
it('should return an instance of ErrorDataLayerView', function () {
|
||||
layerModel.findAnalysisDefinitionNodeModel.and.returnValue(null);
|
||||
var layerView = this.layerViewFactory.createLayerView(layerModel);
|
||||
expect(layerView instanceof ErroredDataLayerView).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return an instance of DataLayerView', function () {
|
||||
var layerModel = new LayerDefinitionModel({
|
||||
type: 'torque',
|
||||
order: 1
|
||||
}, {
|
||||
configModel: {}
|
||||
});
|
||||
spyOn(layerModel, 'findAnalysisDefinitionNodeModel').and.returnValue(this.nodeDefModel);
|
||||
var layerView = this.layerViewFactory.createLayerView(layerModel);
|
||||
expect(layerView instanceof DataLayerView).toBe(true);
|
||||
});
|
||||
|
||||
it('should return an instance of LabelsLayerView', function () {
|
||||
var layerModel = new LayerDefinitionModel({
|
||||
type: 'Tiled',
|
||||
order: 3
|
||||
}, {
|
||||
configModel: {}
|
||||
});
|
||||
spyOn(layerModel, 'findAnalysisDefinitionNodeModel').and.returnValue(this.nodeDefModel);
|
||||
var layerView = this.layerViewFactory.createLayerView(layerModel);
|
||||
expect(layerView instanceof LabelsLayerView).toBe(true);
|
||||
});
|
||||
});
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var BackgroundImageLayerView = require('builder/editor/layers/layer-views/background-image-layer-view');
|
||||
|
||||
describe('editor/layers/layer-views/background-image-layer-view', function () {
|
||||
beforeEach(function () {
|
||||
this.model = new LayerDefinitionModel({
|
||||
name: 'thename',
|
||||
image: 'http://example.com/image.png?size=big'
|
||||
}, {
|
||||
configModel: {}
|
||||
});
|
||||
|
||||
this.view = new BackgroundImageLayerView({
|
||||
model: this.model,
|
||||
stackLayoutModel: {}
|
||||
});
|
||||
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
it('should render title and description of layer', function () {
|
||||
expect(this.view.$el.text()).toContain('image.png');
|
||||
expect(this.view.$el.text()).toContain('editor.layers.image.title-label');
|
||||
});
|
||||
|
||||
it('should render thumbnail image', function () {
|
||||
expect(this.view.$('img').attr('src')).toEqual('http://example.com/image.png?size=big');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var BaseTiledLayerView = require('builder/editor/layers/layer-views/base-tiled-layer-view');
|
||||
|
||||
describe('editor/layers/layer-views/base-tiled-layer-view', function () {
|
||||
beforeEach(function () {
|
||||
this.model = new LayerDefinitionModel({
|
||||
name: 'thename',
|
||||
urlTemplate: 'http://{s}.example.com/basemap/{z}/{x}/{y}.png',
|
||||
subdomains: 'abcd'
|
||||
}, {
|
||||
configModel: {}
|
||||
});
|
||||
|
||||
this.view = new BaseTiledLayerView({
|
||||
model: this.model,
|
||||
stackLayoutModel: {}
|
||||
});
|
||||
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
it('should render title and description of layer', function () {
|
||||
expect(this.view.$el.text()).toContain('thename');
|
||||
expect(this.view.$el.text()).toContain('editor.layers.basemap.title-label');
|
||||
});
|
||||
|
||||
it('should render thumbnail image', function () {
|
||||
expect(this.view.$('img').attr('src')).toEqual('http://a.example.com/basemap/6/30/24.png');
|
||||
});
|
||||
|
||||
describe('when layer has no name', function () {
|
||||
beforeEach(function () {
|
||||
this.model.set('name', '');
|
||||
});
|
||||
|
||||
it('should render title and description of layer', function () {
|
||||
expect(this.view.$el.text()).toContain('editor.layers.basemap.custom-basemap');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,740 @@
|
||||
var $ = require('jquery');
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var CoreView = require('backbone/core-view');
|
||||
var UserActions = require('builder/data/user-actions');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var StackLayoutModel = require('builder/components/stack-layout/stack-layout-model');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var DataLayerView = require('builder/editor/layers/layer-views/data-layer-view');
|
||||
var AnalysisDefinitionNodeModel = require('builder/data/analysis-definition-node-model');
|
||||
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
|
||||
var StyleDefinitionModel = require('builder/editor/style/style-definition-model');
|
||||
var FactoryModals = require('../../../factories/modals');
|
||||
var Router = require('builder/routes/router');
|
||||
var getLayerDefinitionModelFixture = require('fixtures/builder/layer-definition-model.fixture.js');
|
||||
var getConfigModelFixture = require('fixtures/builder/config-model.fixture.js');
|
||||
var fakePromise = require('fixtures/builder/fake-promise.fixture.js');
|
||||
var ajaxFixtures = require('fixtures/builder/ajax-fixtures.js');
|
||||
|
||||
describe('editor/layers/data-layer-view', function () {
|
||||
var geometryModelFakePromise = null;
|
||||
var canBeGeoreferencedPromise = null;
|
||||
var isLayerEmptyPromise = null;
|
||||
|
||||
beforeEach(function () {
|
||||
jasmine.Ajax.install();
|
||||
jasmine.Ajax.stubRequest(ajaxFixtures.querySchemaModel.url.http)
|
||||
.andReturn(ajaxFixtures.querySchemaModel.response.ok);
|
||||
jasmine.Ajax.stubRequest(ajaxFixtures.queryGeometryModel.url.http)
|
||||
.andReturn(ajaxFixtures.queryGeometryModel.response.ok);
|
||||
jasmine.Ajax.stubRequest(ajaxFixtures.queryRowsCollection.url.http)
|
||||
.andReturn(ajaxFixtures.queryRowsCollection.response.ok);
|
||||
|
||||
this.modals = FactoryModals.createModalService();
|
||||
|
||||
spyOn(this.modals, 'create');
|
||||
|
||||
var configModel = getConfigModelFixture();
|
||||
|
||||
var userModel = new UserModel({
|
||||
username: 'pepe',
|
||||
quota: {}
|
||||
}, {
|
||||
configModel: configModel
|
||||
});
|
||||
|
||||
this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
|
||||
configModel: configModel,
|
||||
userModel: userModel
|
||||
});
|
||||
|
||||
this.a0 = new AnalysisDefinitionNodeModel({
|
||||
id: 'a0',
|
||||
type: 'buffer',
|
||||
params: {}
|
||||
}, {
|
||||
configModel: configModel
|
||||
});
|
||||
this.analysisDefinitionNodesCollection.add(this.a0);
|
||||
|
||||
this.model = getLayerDefinitionModelFixture({
|
||||
configModel: configModel
|
||||
});
|
||||
this.model.findAnalysisDefinitionNodeModel = function () {};
|
||||
|
||||
canBeGeoreferencedPromise = fakePromise(this.model, 'canBeGeoreferenced');
|
||||
isLayerEmptyPromise = fakePromise(this.model, 'isEmptyAsync');
|
||||
|
||||
this.layerDefinitionsCollection = this.model.collection;
|
||||
this.layerDefinitionsCollection.findAnalysisDefinitionNodeModel = function () {};
|
||||
|
||||
this.model.styleModel = new StyleDefinitionModel({}, {
|
||||
configModel: configModel
|
||||
});
|
||||
spyOn(this.model, 'canBeDeletedByUser');
|
||||
spyOn(this.model, 'getNumberOfAnalyses').and.returnValue(3);
|
||||
spyOn(this.model, 'getAnalysisDefinitionNodeModel').and.returnValue(this.a0);
|
||||
|
||||
this.a0.queryGeometryModel.set('simple_geom', 'polygon');
|
||||
this.queryGeometryModel = this.a0.queryGeometryModel;
|
||||
geometryModelFakePromise = fakePromise(this.queryGeometryModel, 'hasValueAsync');
|
||||
|
||||
this.c2 = new AnalysisDefinitionNodeModel({
|
||||
id: 'c2',
|
||||
type: 'buffer',
|
||||
params: {}
|
||||
}, {
|
||||
configModel: configModel
|
||||
});
|
||||
this.analysisDefinitionNodesCollection.add(this.c2);
|
||||
spyOn(this.model, 'findAnalysisDefinitionNodeModel').and.returnValue(this.c2);
|
||||
|
||||
this.stackLayoutModel = new StackLayoutModel(null, {stackLayoutItems: {}});
|
||||
spyOn(this.stackLayoutModel, 'nextStep');
|
||||
|
||||
this.newAnalysesViewSpy = jasmine.createSpy('newAnalysesView').and.callFake(function (el) {
|
||||
var view = new CoreView({
|
||||
el: el
|
||||
});
|
||||
view.render = function () {
|
||||
this.$el.html('ANALYSES');
|
||||
return this;
|
||||
};
|
||||
return view;
|
||||
});
|
||||
|
||||
this.userActions = UserActions({
|
||||
userModel: {},
|
||||
analysisDefinitionsCollection: {},
|
||||
analysisDefinitionNodesCollection: {},
|
||||
layerDefinitionsCollection: {},
|
||||
widgetDefinitionsCollection: {}
|
||||
});
|
||||
this.promise = $.Deferred();
|
||||
spyOn(this.userActions, 'saveLayer').and.returnValue(this.promise);
|
||||
|
||||
this.widgetDefinitionsCollection = new Backbone.Collection();
|
||||
this.widgetDefinitionsCollection.widgetsOwnedByLayer = function () { return 0; };
|
||||
|
||||
this.visDefinitionModel = new Backbone.Model();
|
||||
|
||||
this.view = new DataLayerView({
|
||||
modals: this.modals,
|
||||
model: this.model,
|
||||
userActions: this.userActions,
|
||||
stackLayoutModel: this.stackLayoutModel,
|
||||
newAnalysesView: this.newAnalysesViewSpy,
|
||||
layerDefinitionsCollection: this.layerDefinitionsCollection,
|
||||
configModel: configModel,
|
||||
stateDefinitionModel: {},
|
||||
visDefinitionModel: this.visDefinitionModel,
|
||||
widgetDefinitionsCollection: this.widgetDefinitionsCollection,
|
||||
analysisDefinitionNodesCollection: {}
|
||||
});
|
||||
|
||||
spyOn(this.view, '_renameLayer').and.callThrough();
|
||||
|
||||
this.view.$el.appendTo(document.body);
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
var parent = this.view.el.parentNode;
|
||||
parent && parent.removeChild(this.view.el);
|
||||
this.view.clean();
|
||||
jasmine.Ajax.uninstall();
|
||||
});
|
||||
|
||||
describe('_initialize', function () {
|
||||
it('initial values used in .render have the proper value', function () {
|
||||
expect(this.view._viewState.get('needsGeocoding')).toBe(false);
|
||||
expect(this.view._viewState.get('isLayerEmpty')).toBe(false);
|
||||
expect(this.view._viewState.get('queryGeometryHasGeom')).toBe(true);
|
||||
});
|
||||
|
||||
it('should call _setViewState', function () {
|
||||
expect(geometryModelFakePromise.hasBeenCalled()).toBe(true);
|
||||
expect(canBeGeoreferencedPromise.hasBeenCalled()).toBe(true);
|
||||
expect(isLayerEmptyPromise.hasBeenCalled()).toBe(true);
|
||||
});
|
||||
|
||||
it('should subscribe to fetch all query objects and then call to fetchQueryRowsIfRequired', function (done) {
|
||||
spyOn(this.view.model, 'fetchQueryRowsIfRequired');
|
||||
// spyOn(this.view, '_setViewState');
|
||||
var nodeDefModel = this.view.model.getAnalysisDefinitionNodeModel();
|
||||
var querySchemaModel = nodeDefModel.querySchemaModel;
|
||||
var queryRowsCollection = nodeDefModel.queryRowsCollection;
|
||||
var queryGeometryModel = this.view._queryGeometryModel;
|
||||
|
||||
querySchemaModel.set({ ready: true, query: 'SELECT * FROM wadus' }, { silent: true });
|
||||
queryGeometryModel.set({ ready: true, query: 'SELECT * FROM wadus' }, { silent: true });
|
||||
|
||||
queryGeometryModel.fetch();
|
||||
querySchemaModel.fetch();
|
||||
|
||||
setTimeout(function () {
|
||||
queryRowsCollection.trigger('inFinalStatus');
|
||||
}, 0);
|
||||
|
||||
setTimeout(function () {
|
||||
expect(this.view.model.fetchQueryRowsIfRequired).toHaveBeenCalled();
|
||||
done();
|
||||
}.bind(this), 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('_bindEvents', function () {
|
||||
it('should hook up proper event handlers', function () {
|
||||
spyOn(this.view, 'render');
|
||||
spyOn(this.view, '_setViewState');
|
||||
spyOn(this.view, '_onQueryRowsStatusChanged');
|
||||
|
||||
this.view._bindEvents();
|
||||
|
||||
this.view.model.trigger('change');
|
||||
expect(this.view.render).toHaveBeenCalled();
|
||||
|
||||
this.view._queryGeometryModel.trigger('change:simple_geom');
|
||||
expect(this.view._setViewState).toHaveBeenCalled();
|
||||
|
||||
this.view._queryRowsStatus.trigger('change:status');
|
||||
expect(this.view._onQueryRowsStatusChanged).toHaveBeenCalled();
|
||||
|
||||
this.view.render.calls.reset();
|
||||
this.view._viewState.trigger('change');
|
||||
expect(this.view.render).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should hook up model destroy event with _onDestroy only once', function () {
|
||||
spyOn(this.view, '_onDestroy');
|
||||
|
||||
this.view._bindEvents();
|
||||
|
||||
this.view.model.trigger('destroy');
|
||||
this.view.model.trigger('destroy');
|
||||
expect(this.view._onDestroy.calls.count()).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('.render', function () {
|
||||
it('should render properly depending on default values', function () {
|
||||
this.view.render();
|
||||
|
||||
expect(_.size(this.view._subviews)).toBe(3); // [inlineEditor, toggleTooltip, toggleMenuTooltip]
|
||||
});
|
||||
|
||||
describe('depending on _needsGeocoding value', function () {
|
||||
beforeEach(function () {
|
||||
this.view._queryGeometryHasGeom = false; // To avoid rendering `js-analyses`. We want to depend only on _needsGeocoding
|
||||
});
|
||||
|
||||
it('should render proper markup if no geocoding needed', function () {
|
||||
this.view._needsGeocoding = false;
|
||||
this.view.render();
|
||||
|
||||
// We're checking against some markup that renders if needsGeocoding is false
|
||||
expect(this.view.$('ul.js-analyses').length).toBe(1);
|
||||
expect(this.view.$el.hasClass('is-empty')).toBe(false);
|
||||
expect(this.view.$('.js-geocode').length).toBe(0);
|
||||
});
|
||||
|
||||
it('should render proper markup if geocoding needed', function () {
|
||||
this.view._viewState.set('needsGeocoding', true);
|
||||
this.view._viewState.set('queryGeometryHasGeom', true);
|
||||
this.view._viewState.set('brokenLayer', true);
|
||||
this.view.render();
|
||||
|
||||
// We're checking against some markup that renders if needsGeocoding is true
|
||||
// expect(this.view.$('ul.js-analyses').length).toBe(0);
|
||||
expect(this.view.$el.hasClass('is-empty')).toBe(true);
|
||||
expect(this.view.$('.js-geocode').length).toBe(1);
|
||||
expect(this.view.$('.js-geocode').data('tipsy')).not.toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('depending on _isLayerEmpty value', function () {
|
||||
it('should render proper markup if the layer is empty', function () {
|
||||
this.view._viewState.set('isLayerEmpty', true);
|
||||
this.view.render();
|
||||
|
||||
expect(this.view.$('.js-emptylayer').length).toBe(1);
|
||||
expect(this.view.$('.js-emptylayer').length).toBeGreaterThan(0); // Render warning icon
|
||||
});
|
||||
|
||||
it('should render proper markup if the layer is not empty', function () {
|
||||
this.view._viewState.set('isLayerEmpty', false);
|
||||
this.view.render();
|
||||
|
||||
expect(this.view.$('.js-emptylayer').length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('depending on _queryGeometryHasGeom value', function () {
|
||||
beforeEach(function () {
|
||||
this.view._viewState.set('needsGeocoding', true); // To avoid rendering `js-analyses`. We want to depend only on _queryGeometryHasGeom
|
||||
});
|
||||
|
||||
it('should render proper markup if the layer has geometry', function () {
|
||||
this.view._viewState.set('queryGeometryHasGeom', true);
|
||||
this.view.render();
|
||||
|
||||
expect(this.view.$('ul.js-analyses').length).toBe(1);
|
||||
expect(this.view.$('button.js-toggle').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should render proper markup if the layer has no geometry', function () {
|
||||
this.view._viewState.set('queryGeometryHasGeom', false);
|
||||
this.view.render();
|
||||
|
||||
expect(this.view.$('ul.js-analyses').length).toBe(0);
|
||||
expect(this.view.$('button.js-toggle').length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('depending on _isFetchingRows value', function () {
|
||||
it('should render proper markup (a placeholder) if query rows is fetching', function () {
|
||||
this.view._viewState.set('isFetchingRows', true);
|
||||
this.view.render();
|
||||
|
||||
expect(this.view.$('.Editor-ListLayer-itemHeader > .Editor-ListLayer-media--placeholder').length).toBe(1);
|
||||
expect(this.view.$('.js-thumbnail').length).toBe(0);
|
||||
expect(this.view.$el.hasClass('Editor-ListLayer-item--fetching')).toBe(true);
|
||||
});
|
||||
|
||||
it('should not render a placeholder if query rows is not fetching', function () {
|
||||
this.view._viewState.set('isFetchingRows', false);
|
||||
this.view.render();
|
||||
|
||||
expect(this.view.$('.Editor-ListLayer-itemHeader > .Editor-ListLayer-media--placeholder').length).toBe(0);
|
||||
expect(this.view.$('.js-thumbnail').length).toBe(1);
|
||||
expect(this.view.$el.hasClass('Editor-ListLayer-item--fetching')).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when layer is source', function () {
|
||||
var view;
|
||||
|
||||
beforeEach(function () {
|
||||
this.model = new LayerDefinitionModel({
|
||||
id: 'layer-A',
|
||||
name: 'table_name',
|
||||
letter: 'a',
|
||||
source: 'a0',
|
||||
visible: true
|
||||
}, {
|
||||
configModel: {},
|
||||
collection: this.layerDefinitionsCollection
|
||||
});
|
||||
this.model.styleModel = new StyleDefinitionModel({}, {
|
||||
configModel: {}
|
||||
});
|
||||
spyOn(this.model, 'canBeDeletedByUser');
|
||||
spyOn(this.model, 'getNumberOfAnalyses').and.returnValue(3);
|
||||
spyOn(this.model, 'getAnalysisDefinitionNodeModel').and.returnValue(this.a0);
|
||||
spyOn(this.model, 'findAnalysisDefinitionNodeModel').and.returnValue(this.c2);
|
||||
|
||||
view = new DataLayerView({
|
||||
modals: this.modals,
|
||||
model: this.model,
|
||||
userActions: this.userActions,
|
||||
stackLayoutModel: this.stackLayoutModel,
|
||||
newAnalysesView: this.newAnalysesViewSpy,
|
||||
layerDefinitionsCollection: this.layerDefinitionsCollection,
|
||||
configModel: {},
|
||||
stateDefinitionModel: {},
|
||||
visDefinitionModel: this.visDefinitionModel,
|
||||
widgetDefinitionsCollection: this.widgetDefinitionsCollection,
|
||||
analysisDefinitionNodesCollection: {}
|
||||
});
|
||||
view._queryGeometryHasGeom = function () {
|
||||
return true;
|
||||
};
|
||||
view.$el.appendTo(document.body);
|
||||
view.render();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
var parent = view.el.parentNode;
|
||||
parent && parent.removeChild(view.el);
|
||||
view.clean();
|
||||
});
|
||||
|
||||
describe('.render', function () {
|
||||
it('should render properly', function () {
|
||||
expect(_.size(view._subviews)).toBe(4); // [inlineEditor, analysesViewTooltip, toggleTooltip, toggleMenuTooltip]
|
||||
});
|
||||
});
|
||||
|
||||
describe('when layer has an analysis', function () {
|
||||
it('should render analysis views', function () {
|
||||
expect(view.$el.text()).toContain('ANALYSES');
|
||||
|
||||
// Assert args being what's expected too
|
||||
expect(this.newAnalysesViewSpy).toHaveBeenCalled();
|
||||
expect(this.newAnalysesViewSpy.calls.argsFor(0)[0]).toEqual(view.$('.js-analyses'));
|
||||
expect(this.newAnalysesViewSpy.calls.argsFor(0)[1]).toEqual(this.model);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when layer is Torque', function () {
|
||||
describe('.render', function () {
|
||||
it('should render properly', function () {
|
||||
this.view._isTorque = function () {
|
||||
return true;
|
||||
};
|
||||
|
||||
this.view.render();
|
||||
|
||||
expect(_.size(this.view._subviews)).toBe(4); // [inlineEditor, torqueTooltip, toggleTooltip, toggleMenuTooltip]
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
it('should render title of layer', function () {
|
||||
expect(this.view.$el.text()).toContain('table_name');
|
||||
});
|
||||
|
||||
it('should add sortable class', function () {
|
||||
expect(this.view.$el.hasClass('js-sortable-item')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should be displayed as hidden when layer is hidden', function () {
|
||||
expect(this.view.$('.js-thumbnail').hasClass('is-hidden')).toBe(false);
|
||||
expect(this.view.$('.js-title').hasClass('is-hidden')).toBe(false);
|
||||
expect(this.view.$('.js-analyses').hasClass('is-hidden')).toBe(false);
|
||||
expect(this.view.$('.js-analyses-widgets-info').hasClass('is-hidden')).toBe(false);
|
||||
|
||||
this.model.set('visible', false);
|
||||
|
||||
expect(this.view.$('.js-thumbnail').hasClass('is-hidden')).toBe(true);
|
||||
expect(this.view.$('.js-title').hasClass('is-hidden')).toBe(true);
|
||||
expect(this.view.$('.js-analyses').hasClass('is-hidden')).toBe(true);
|
||||
expect(this.view.$('.js-analyses-widgets-info').hasClass('is-hidden')).toBe(true);
|
||||
});
|
||||
|
||||
it('should be with errors when layer has errors', function () {
|
||||
expect(this.view.$('.js-error').length).toBe(0);
|
||||
this.model.set('error', 'an error');
|
||||
expect(this.view.$('.js-error').length).toBe(1);
|
||||
});
|
||||
|
||||
it('should not add js-sortable-item class if layer is torque', function () {
|
||||
this.model.set('type', 'torque');
|
||||
this.view.render();
|
||||
expect(this.view.$el.hasClass('js-sortable-item')).toBeFalsy();
|
||||
expect(this.view.$el.hasClass('is-animated')).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('context menu', function () {
|
||||
it('should toggle the context-menu box when option is clicked', function () {
|
||||
expect(this.view.$el.find('.CDB-Box-modal').length).toBe(0);
|
||||
|
||||
this.view.$('.js-toggle-menu').click();
|
||||
|
||||
expect($('body').find('.CDB-Box-modal').css('display')).toEqual('block');
|
||||
|
||||
this.view.$('.js-toggle-menu').click();
|
||||
|
||||
expect($('body').find('.CDB-Box-modal').length).toBe(0);
|
||||
});
|
||||
|
||||
describe('Delete layer…', function () {
|
||||
it('should not be posible to delete a layer if canBeDeletedByUser return false', function () {
|
||||
this.model.canBeDeletedByUser.and.returnValue(false);
|
||||
|
||||
this.view.$('.js-toggle-menu').click();
|
||||
expect($('body').find(".CDB-Box-modal button[title='editor.layers.options.delete']").length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should be posible to delete a layer if canBeDeletedByUser', function () {
|
||||
this.model.canBeDeletedByUser.and.returnValue(true);
|
||||
|
||||
this.view.$('.js-toggle-menu').click();
|
||||
expect($('body').find(".CDB-Box-modal button[title='editor.layers.options.delete']").length).toEqual(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when title is clicked', function () {
|
||||
var originalSetTimeout = null;
|
||||
|
||||
beforeEach(function () {
|
||||
spyOn(Router, 'goToStyleTab');
|
||||
originalSetTimeout = setTimeout;
|
||||
jasmine.clock().install();
|
||||
this.view.$('.js-title').click();
|
||||
jasmine.clock().tick(300);
|
||||
});
|
||||
|
||||
it('should edit layer', function (done) {
|
||||
var self = this;
|
||||
canBeGeoreferencedPromise.resolve(false);
|
||||
|
||||
originalSetTimeout(function () {
|
||||
expect(Router.goToStyleTab).toHaveBeenCalledWith(self.model.get('id'));
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
jasmine.clock().uninstall();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when title is doubleclicked', function () {
|
||||
beforeEach(function () {
|
||||
jasmine.clock().install();
|
||||
this.view.$('.js-title').click();
|
||||
this.view.$('.js-title').click();
|
||||
jasmine.clock().tick(300);
|
||||
});
|
||||
|
||||
it('should show inline editor', function () {
|
||||
expect(this.view.$('.js-input').is(':visible')).toBe(true);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
jasmine.clock().uninstall();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when thumbnail is clicked', function () {
|
||||
var originalSetTimeout = null;
|
||||
|
||||
beforeEach(function () {
|
||||
spyOn(Router, 'goToStyleTab');
|
||||
originalSetTimeout = setTimeout;
|
||||
jasmine.clock().install();
|
||||
this.view.$('.js-thumbnail').click();
|
||||
jasmine.clock().tick(300);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
jasmine.clock().uninstall();
|
||||
});
|
||||
|
||||
it('should edit layer', function (done) {
|
||||
var self = this;
|
||||
canBeGeoreferencedPromise.resolve(false);
|
||||
|
||||
originalSetTimeout(function () {
|
||||
expect(Router.goToStyleTab).toHaveBeenCalledWith(self.model.get('id'));
|
||||
done();
|
||||
}, 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the toggle icon is clicked', function () {
|
||||
beforeEach(function () {
|
||||
this.model.set({autoStyle: 'foo'});
|
||||
});
|
||||
|
||||
it('should toggle the layer', function () {
|
||||
this.view.$('.js-toggle').click();
|
||||
|
||||
expect(this.model.get('visible')).toEqual(false);
|
||||
expect(this.userActions.saveLayer).toHaveBeenCalled();
|
||||
expect(this.userActions.saveLayer.calls.argsFor(0)[0]).toEqual(this.view.model);
|
||||
expect(this.userActions.saveLayer.calls.argsFor(0)[1]).toEqual({
|
||||
shouldPreserveAutoStyle: true
|
||||
});
|
||||
expect(this.model.get('autoStyle')).toEqual('foo');
|
||||
|
||||
this.userActions.saveLayer.calls.reset();
|
||||
this.view.$('.js-toggle').click();
|
||||
expect(this.model.get('visible')).toEqual(true);
|
||||
expect(this.userActions.saveLayer).toHaveBeenCalled();
|
||||
expect(this.userActions.saveLayer.calls.argsFor(0)[0]).toEqual(this.view.model);
|
||||
expect(this.userActions.saveLayer.calls.argsFor(0)[1]).toEqual({
|
||||
shouldPreserveAutoStyle: true
|
||||
});
|
||||
expect(this.model.get('autoStyle')).toEqual('foo');
|
||||
});
|
||||
});
|
||||
|
||||
describe('expanded/collapsed states', function () {
|
||||
it('should expand and collapse the layer', function () {
|
||||
expect(this.view.$('.js-analyses').hasClass('is-collapsed')).toBe(false);
|
||||
|
||||
// Show the context menu and collapse the layer
|
||||
this.view.$('.js-toggle-menu').click();
|
||||
$('body').find('.CDB-Box-modal button[title="editor.layers.options.collapse"]').click();
|
||||
|
||||
expect(this.view.$('.js-analyses').hasClass('is-collapsed')).toBe(true);
|
||||
|
||||
// Show the context menu and expand the layer
|
||||
this.view.$('.js-toggle-menu').click();
|
||||
$('body').find('.CDB-Box-modal button[title="editor.layers.options.expand"]').click();
|
||||
|
||||
// Show the context menu and expand the layer again
|
||||
expect(this.view.$('.js-analyses').hasClass('is-collapsed')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('rename layer', function () {
|
||||
it('should rename the layer', function () {
|
||||
// Show the context menu and collapse the layer
|
||||
this.view.$('.js-toggle-menu').click();
|
||||
$('body').find('.CDB-Box-modal button[title="editor.layers.options.rename"]').click();
|
||||
|
||||
expect(this.view.$('.js-input').is(':visible')).toBe(true);
|
||||
this.view.$('.js-input').val('Foo');
|
||||
|
||||
var e = $.Event('keyup');
|
||||
e.which = 13;
|
||||
this.view.$('.js-input').trigger(e);
|
||||
|
||||
expect(this.model.get('table_name_alias')).toBe('Foo');
|
||||
expect(this.userActions.saveLayer).toHaveBeenCalledWith(this.model);
|
||||
});
|
||||
});
|
||||
|
||||
describe('geometry type', function () {
|
||||
it('should show polygon svg', function () {
|
||||
expect(this.view.$('svg').length).toBe(1);
|
||||
expect(this.view.$('svg circle').length).toBe(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._showContextMenu', function () {
|
||||
it('should show center map option if geom and clicking on it should call _centerMap', function () {
|
||||
this.view._viewState.set('needsGeocoding', false);
|
||||
this.view.render();
|
||||
spyOn(this.view, '_centerMap');
|
||||
|
||||
this.view._showContextMenu();
|
||||
|
||||
expect(this.view._menuView.$('button[title^="editor.layers.options.center-map"]').length).toBe(1);
|
||||
|
||||
this.view._menuView.$('button[title^="editor.layers.options.center-map"]').click();
|
||||
expect(this.view._centerMap).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not show center map option if no geom', function () {
|
||||
this.view._viewState.set('needsGeocoding', true);
|
||||
this.view.render();
|
||||
|
||||
this.view._showContextMenu();
|
||||
|
||||
expect(this.view._menuView.$('button[title^="editor.layers.options.center-map"]').length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('._centerMap', function () {
|
||||
it('should call to zoomToData with query from queryGeometryModel', function () {
|
||||
var theView = this.view;
|
||||
this.queryGeometryModel.set('simple_geom', '');
|
||||
|
||||
// The only way we have to know if zoomToData has been called is to provoke an
|
||||
// error. This way we know that the query has been extracted from our querySchemaModel
|
||||
// and that zoomToData has been called.
|
||||
expect(function () {
|
||||
theView._centerMap();
|
||||
}).toThrowError('query is required');
|
||||
|
||||
this.queryGeometryModel.set('simple_geom', 'polygon');
|
||||
});
|
||||
});
|
||||
|
||||
describe('._isTorque', function () {
|
||||
it('should return if model is Torque Layer', function () {
|
||||
spyOn(this.view.model, 'isTorqueLayer');
|
||||
|
||||
this.view._isTorque();
|
||||
|
||||
expect(this.view.model.isTorqueLayer).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('._setViewState', function () {
|
||||
var initialHasGeomValue;
|
||||
var initialGeocodingValue;
|
||||
var initialLayerEmptyValue;
|
||||
|
||||
beforeEach(function () {
|
||||
initialGeocodingValue = this.view._viewState.get('needsGeocoding');
|
||||
initialHasGeomValue = this.view._viewState.get('queryGeometryHasGeom');
|
||||
initialLayerEmptyValue = this.view._viewState.get('isLayerEmpty');
|
||||
});
|
||||
|
||||
function resolvePromises (hasGeomValue, geocodingValue, layerEmptyValue) {
|
||||
geometryModelFakePromise.resolve(hasGeomValue);
|
||||
canBeGeoreferencedPromise.resolve(geocodingValue);
|
||||
isLayerEmptyPromise.resolve(layerEmptyValue);
|
||||
}
|
||||
|
||||
it('shoud update needsGeocoding', function (done) {
|
||||
resolvePromises(initialHasGeomValue, !initialGeocodingValue, initialLayerEmptyValue);
|
||||
|
||||
setTimeout(function () {
|
||||
expect(this.view._viewState.get('needsGeocoding')).toEqual(!initialGeocodingValue);
|
||||
done();
|
||||
}.bind(this), 0);
|
||||
});
|
||||
|
||||
it('shoud update isLayerEmpty', function (done) {
|
||||
resolvePromises(initialHasGeomValue, initialGeocodingValue, !initialLayerEmptyValue);
|
||||
|
||||
setTimeout(function () {
|
||||
expect(this.view._viewState.get('isLayerEmpty')).toEqual(!initialLayerEmptyValue);
|
||||
done();
|
||||
}.bind(this), 0);
|
||||
});
|
||||
|
||||
it('should update queryGeometryHasGeom', function (done) {
|
||||
resolvePromises(!initialHasGeomValue, !initialGeocodingValue, initialLayerEmptyValue);
|
||||
|
||||
setTimeout(function () {
|
||||
expect(this.view._viewState.get('queryGeometryHasGeom')).toEqual(!initialHasGeomValue);
|
||||
done();
|
||||
}.bind(this), 0);
|
||||
});
|
||||
|
||||
it('should update isFetchingRows', function (done) {
|
||||
this.view._viewState.set('isFetchingRows', false, { silent: true });
|
||||
spyOn(this.view, '_isFetchingRows').and.returnValue(true);
|
||||
|
||||
resolvePromises(initialHasGeomValue, initialGeocodingValue, initialLayerEmptyValue);
|
||||
|
||||
setTimeout(function () {
|
||||
expect(this.view._viewState.get('isFetchingRows')).toBe(true);
|
||||
done();
|
||||
}.bind(this), 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('_isFetchingRows', function () {
|
||||
it('should return query rows status of the current analysisDefinitionModel', function () {
|
||||
var node = this.view.model.getAnalysisDefinitionNodeModel();
|
||||
var rowsStatus = node.queryRowsCollection.statusModel;
|
||||
|
||||
rowsStatus.set('status', 'fetching');
|
||||
var isFetching = this.view._isFetchingRows();
|
||||
expect(isFetching).toBe(true);
|
||||
|
||||
rowsStatus.set('status', 'fetched');
|
||||
isFetching = this.view._isFetchingRows();
|
||||
expect(isFetching).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('_onQueryRowsStatusChanged', function () {
|
||||
it('shoudl set viewState to isFetchingRows and call setViewState', function () {
|
||||
this.view._viewState.set('isFetchingRows', false, { silent: true });
|
||||
spyOn(this.view, '_isFetchingRows').and.returnValue(true);
|
||||
spyOn(this.view, '_setViewState');
|
||||
|
||||
this.view._onQueryRowsStatusChanged();
|
||||
|
||||
expect(this.view._viewState.get('isFetchingRows')).toBe(true);
|
||||
expect(this.view._setViewState).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
var $ = require('jquery');
|
||||
var Backbone = require('backbone');
|
||||
var CoreView = require('backbone/core-view');
|
||||
var UserActions = require('builder/data/user-actions');
|
||||
var StackLayoutModel = require('builder/components/stack-layout/stack-layout-model');
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var ErroredDataLayerView = require('builder/editor/layers/layer-views/errored-data-layer-view');
|
||||
var StyleDefinitionModel = require('builder/editor/style/style-definition-model');
|
||||
var QueryRowsCollection = require('builder/data/query-rows-collection');
|
||||
var QuerySchemaModel = require('builder/data/query-schema-model');
|
||||
var QueryGeometryModel = require('builder/data/query-geometry-model');
|
||||
var FactoryModals = require('../../../factories/modals');
|
||||
|
||||
describe('editor/layers/errored-data-layer-view', function () {
|
||||
beforeEach(function () {
|
||||
spyOn(QueryGeometryModel.prototype, 'fetch');
|
||||
spyOn(QuerySchemaModel.prototype, 'fetch');
|
||||
spyOn(QueryRowsCollection.prototype, 'fetch');
|
||||
|
||||
this.layerDefinitionsCollection = new Backbone.Collection([]);
|
||||
this.layerDefinitionsCollection.save = jasmine.createSpy('save');
|
||||
|
||||
this.modals = FactoryModals.createModalService();
|
||||
|
||||
spyOn(this.modals, 'create');
|
||||
|
||||
this.model = new LayerDefinitionModel({
|
||||
id: 'layer-A',
|
||||
name: 'table_name',
|
||||
letter: 'a',
|
||||
source: 'a0',
|
||||
visible: true
|
||||
}, {
|
||||
configModel: {},
|
||||
collection: this.layerDefinitionsCollection
|
||||
});
|
||||
this.model.styleModel = new StyleDefinitionModel({}, {
|
||||
configModel: {}
|
||||
});
|
||||
spyOn(this.model, 'canBeDeletedByUser');
|
||||
spyOn(this.model, 'getNumberOfAnalyses').and.returnValue(3);
|
||||
spyOn(this.model, 'getAnalysisDefinitionNodeModel').and.returnValue(null);
|
||||
spyOn(this.model, 'findAnalysisDefinitionNodeModel').and.returnValue(null);
|
||||
|
||||
this.stackLayoutModel = new StackLayoutModel(null, {stackLayoutItems: {}});
|
||||
spyOn(this.stackLayoutModel, 'nextStep');
|
||||
|
||||
this.newAnalysesViewSpy = jasmine.createSpy('newAnalysesView').and.callFake(function (el) {
|
||||
var view = new CoreView({
|
||||
el: el
|
||||
});
|
||||
view.render = function () {
|
||||
this.$el.html('ANALYSES');
|
||||
return this;
|
||||
};
|
||||
return view;
|
||||
});
|
||||
|
||||
this.userActions = UserActions({
|
||||
userModel: {},
|
||||
analysisDefinitionsCollection: {},
|
||||
analysisDefinitionNodesCollection: {},
|
||||
layerDefinitionsCollection: {},
|
||||
widgetDefinitionsCollection: {}
|
||||
});
|
||||
this.promise = $.Deferred();
|
||||
spyOn(this.userActions, 'saveLayer').and.returnValue(this.promise);
|
||||
|
||||
var widgetDefinitionsCollection = new Backbone.Collection();
|
||||
widgetDefinitionsCollection.widgetsOwnedByLayer = function () { return 0; };
|
||||
|
||||
var visDefinitionModel = new Backbone.Model();
|
||||
|
||||
this.view = new ErroredDataLayerView({
|
||||
modals: this.modals,
|
||||
model: this.model,
|
||||
userActions: this.userActions,
|
||||
stackLayoutModel: this.stackLayoutModel,
|
||||
newAnalysesView: this.newAnalysesViewSpy,
|
||||
layerDefinitionsCollection: this.layerDefinitionsCollection,
|
||||
configModel: {},
|
||||
stateDefinitionModel: {},
|
||||
visDefinitionModel: visDefinitionModel,
|
||||
widgetDefinitionsCollection: widgetDefinitionsCollection,
|
||||
analysisDefinitionNodesCollection: {}
|
||||
});
|
||||
|
||||
spyOn(this.view, '_renameLayer').and.callThrough();
|
||||
|
||||
this.view.$el.appendTo(document.body);
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
var parent = this.view.el.parentNode;
|
||||
parent && parent.removeChild(this.view.el);
|
||||
this.view.clean();
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
it('should render title of layer', function () {
|
||||
expect(this.view.$el.text()).toContain('table_name');
|
||||
});
|
||||
|
||||
it('should not add sortable class', function () {
|
||||
expect(this.view.$el.hasClass('js-sortable-item')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should be displayed always', function () {
|
||||
expect(this.view.$('.js-thumbnail').hasClass('is-hidden')).toBe(false);
|
||||
expect(this.view.$('.js-title').hasClass('is-hidden')).toBe(false);
|
||||
expect(this.view.$('.js-analyses').hasClass('is-hidden')).toBe(false);
|
||||
expect(this.view.$('.js-analyses-widgets-info').hasClass('is-hidden')).toBe(false);
|
||||
|
||||
this.model.set('visible', false);
|
||||
|
||||
expect(this.view.$('.js-thumbnail').hasClass('is-hidden')).toBe(false);
|
||||
expect(this.view.$('.js-title').hasClass('is-hidden')).toBe(false);
|
||||
expect(this.view.$('.js-analyses').hasClass('is-hidden')).toBe(false);
|
||||
expect(this.view.$('.js-analyses-widgets-info').hasClass('is-hidden')).toBe(false);
|
||||
});
|
||||
|
||||
it('should contain is-errored class', function () {
|
||||
expect(this.view.$el.hasClass('is-errored')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render the broken node', function () {
|
||||
expect(this.view.$('.js-analyses > li').size()).toBe(1);
|
||||
expect(this.view.$('.Editor-ListAnalysis-itemInfo .CDB-Text').text()).toContain('a0');
|
||||
expect(this.view.$('.Editor-ListAnalysis-title').text()).toContain('editor.layers.errors.broken-node');
|
||||
});
|
||||
|
||||
describe('context menu', function () {
|
||||
it('should toggle the context-menu box when option is clicked', function () {
|
||||
expect(this.view.$el.find('.CDB-Box-modal').length).toBe(0);
|
||||
this.view.$('.js-toggle-menu').click();
|
||||
expect($('body').find('.CDB-Box-modal').css('display')).toEqual('block');
|
||||
this.view.$('.js-toggle-menu').click();
|
||||
expect($('body').find('.CDB-Box-modal').length).toBe(0);
|
||||
});
|
||||
|
||||
describe('Delete layer…', function () {
|
||||
it('should be posible to delete the layer', function () {
|
||||
this.view.$('.js-toggle-menu').click();
|
||||
expect($('body').find(".CDB-Box-modal button[title='editor.layers.options.delete-and-reload']").length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should reload the map when layer is removed', function () {
|
||||
this.view.$('.js-toggle-menu').click();
|
||||
spyOn(this.view, '_reloadApplication');
|
||||
$('body').find(".CDB-Box-modal button[title='editor.layers.options.delete-and-reload']").click();
|
||||
this.model.trigger('destroy', this.model);
|
||||
expect(this.view._reloadApplication).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,33 @@
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var LabelsLayerView = require('builder/editor/layers/layer-views/labels-layer-view');
|
||||
|
||||
describe('editor/layers/layer-views/labels-layer-view', function () {
|
||||
beforeEach(function () {
|
||||
this.model = new LayerDefinitionModel({
|
||||
name: 'thename',
|
||||
urlTemplate: 'http://{s}.example.com/labels/{z}/{x}/{y}.png'
|
||||
}, {
|
||||
configModel: {}
|
||||
});
|
||||
|
||||
this.view = new LabelsLayerView({
|
||||
model: this.model,
|
||||
stackLayoutModel: {}
|
||||
});
|
||||
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
it('should render title and description of layer', function () {
|
||||
expect(this.view.$el.text()).toContain('thename');
|
||||
expect(this.view.$el.text()).toContain('editor.layers.labels.title-label');
|
||||
});
|
||||
|
||||
it('should render thumbnail image', function () {
|
||||
expect(this.view.$('img').attr('src')).toEqual('http://a.example.com/labels/6/30/24.png');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,33 @@
|
||||
var LayerDefinitionModel = require('builder/data/layer-definition-model');
|
||||
var PlainColorLayerView = require('builder/editor/layers/layer-views/plain-color-layer-view');
|
||||
|
||||
describe('editor/layers/layer-views/plain-color-layer-view', function () {
|
||||
beforeEach(function () {
|
||||
this.model = new LayerDefinitionModel({
|
||||
name: 'thename',
|
||||
color: '#FFFFFF'
|
||||
}, {
|
||||
configModel: {}
|
||||
});
|
||||
|
||||
this.view = new PlainColorLayerView({
|
||||
model: this.model,
|
||||
stackLayoutModel: {}
|
||||
});
|
||||
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
it('should render title and description of layer', function () {
|
||||
expect(this.view.$el.text()).toContain('#FFFFFF');
|
||||
expect(this.view.$el.text()).toContain('editor.layers.color.title-label');
|
||||
});
|
||||
|
||||
it('should render thumbnail image', function () {
|
||||
expect(this.view.$('.js-thumbnail').css('background-color')).toEqual('rgb(255, 255, 255)');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,294 @@
|
||||
var $ = require('jquery');
|
||||
var _ = require('underscore');
|
||||
var Backbone = require('backbone');
|
||||
var ModalsServiceModel = require('builder/components/modals/modals-service-model');
|
||||
var StackLayoutModel = require('builder/components/stack-layout/stack-layout-model');
|
||||
var Notifier = require('builder/components/notifier/notifier');
|
||||
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
|
||||
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
|
||||
var UserActions = require('builder/data/user-actions');
|
||||
var LayersView = require('builder/editor/layers/layers-view');
|
||||
var EditorModel = require('builder/data/editor-model');
|
||||
var ConfigModel = require('builder/data/config-model');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var NotifierCollection = require('builder/components/notifier/notifier-collection.js');
|
||||
|
||||
describe('editor/layers/layers-view', function () {
|
||||
beforeEach(function () {
|
||||
jasmine.Ajax.install();
|
||||
jasmine.Ajax.stubRequest(new RegExp('.*api/v2/sql.*'))
|
||||
.andReturn({ status: 200 });
|
||||
|
||||
var configModel = new ConfigModel({
|
||||
base_url: '/u/pepe'
|
||||
});
|
||||
|
||||
var userModel = new UserModel({
|
||||
limits: {
|
||||
max_layers: 2
|
||||
}
|
||||
}, {
|
||||
configModel: configModel
|
||||
});
|
||||
|
||||
this.stackLayoutModel = new StackLayoutModel(null, {
|
||||
stackLayoutItems: []
|
||||
});
|
||||
this.modals = new ModalsServiceModel();
|
||||
this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
|
||||
configModel: configModel,
|
||||
userModel: userModel
|
||||
});
|
||||
|
||||
this.layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
|
||||
configModel: configModel,
|
||||
userModel: userModel,
|
||||
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
|
||||
mapId: 'm-123',
|
||||
stateDefinitionModel: {}
|
||||
});
|
||||
this.layerDefinitionsCollection.reset([{
|
||||
kind: 'tiled',
|
||||
options: {
|
||||
urlTemplate: 'http://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}.png'
|
||||
},
|
||||
order: 2
|
||||
}, {
|
||||
id: 'l1',
|
||||
kind: 'carto',
|
||||
order: 1,
|
||||
options: {
|
||||
table_name: 'foo_bar',
|
||||
cartocss: ''
|
||||
}
|
||||
}, {
|
||||
kind: 'tiled',
|
||||
options: {
|
||||
urlTemplate: 'http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png'
|
||||
},
|
||||
order: 0
|
||||
}]);
|
||||
|
||||
spyOn($.prototype, 'sortable');
|
||||
|
||||
this.userActions = UserActions({
|
||||
userModel: {},
|
||||
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
|
||||
analysisDefinitionsCollection: {},
|
||||
layerDefinitionsCollection: this.layerDefinitionsCollection,
|
||||
widgetDefinitionsCollection: {}
|
||||
});
|
||||
|
||||
spyOn(this.userActions, 'createLayerForAnalysisNode');
|
||||
spyOn(this.userActions, 'moveLayer');
|
||||
|
||||
var widgetDefinitionsCollection = new Backbone.Collection();
|
||||
widgetDefinitionsCollection.widgetsOwnedByLayer = function () { return 0; };
|
||||
|
||||
var visDefinitionModel = new Backbone.Model();
|
||||
var editorModel = new EditorModel();
|
||||
|
||||
Notifier.init({
|
||||
editorModel: editorModel,
|
||||
visDefinitionModel: visDefinitionModel
|
||||
});
|
||||
|
||||
this.view = new LayersView({
|
||||
configModel: configModel,
|
||||
userModel: userModel,
|
||||
pollingModel: new Backbone.Model(),
|
||||
editorModel: editorModel,
|
||||
userActions: this.userActions,
|
||||
analysis: this.analysis,
|
||||
modals: this.modals,
|
||||
stackLayoutModel: this.stackLayoutModel,
|
||||
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
|
||||
layerDefinitionsCollection: this.layerDefinitionsCollection,
|
||||
stateDefinitionModel: {},
|
||||
visDefinitionModel: visDefinitionModel,
|
||||
widgetDefinitionsCollection: widgetDefinitionsCollection,
|
||||
showMaxLayerError: jasmine.createSpy('showMaxLayerError'),
|
||||
onNotificationCloseAction: jasmine.createSpy('onNotificationCloseAction')
|
||||
});
|
||||
spyOn(this.view._layerViewFactory, 'createLayerView').and.callThrough();
|
||||
|
||||
this.view.render();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
this.view.clean();
|
||||
this.userActions = null;
|
||||
jasmine.Ajax.uninstall();
|
||||
Notifier.off();
|
||||
});
|
||||
|
||||
it('should have no leaks', function () {
|
||||
expect(this.view).toHaveNoLeaks();
|
||||
});
|
||||
|
||||
it('should update ScrollView after notification closes', function () {
|
||||
this.view._createNotification(this.layerDefinitionsCollection.first());
|
||||
var notification = Notifier.getCollection().last();
|
||||
notification.trigger('notification:close');
|
||||
expect(this.view._onNotificationCloseAction).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('._addLayerView', function () {
|
||||
it('should add all layer views but labels layer', function () {
|
||||
var n = this.layerDefinitionsCollection.size();
|
||||
expect(this.view._layerViewFactory.createLayerView).toHaveBeenCalledTimes(n - 1);
|
||||
});
|
||||
|
||||
it('should show a notification when the layer is added', function () {
|
||||
spyOn(NotifierCollection.prototype, 'add');
|
||||
|
||||
const currentViews = _.size(this.view._subviews);
|
||||
|
||||
this.layerDefinitionsCollection.add({
|
||||
id: 'l-2',
|
||||
options: {
|
||||
type: 'torque',
|
||||
table_name: 'fee',
|
||||
source: 'a1'
|
||||
}
|
||||
});
|
||||
|
||||
var args = NotifierCollection.prototype.add.calls.mostRecent().args[0];
|
||||
|
||||
expect(args.info).toEqual('notifications.layer.added');
|
||||
expect(_.size(this.view._subviews)).toBe(currentViews + 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when max layers limit is reached', function () {
|
||||
it('should disable the add button when max layers are reached', function () {
|
||||
this.layerDefinitionsCollection.add({
|
||||
id: 'l-2',
|
||||
options: {
|
||||
type: 'torque',
|
||||
table_name: 'fee',
|
||||
source: 'a1'
|
||||
}
|
||||
});
|
||||
expect(this.view.$('.js-add').hasClass('is-disabled')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should re-enable the add button when we remove a layer after max layers are reached', function () {
|
||||
var otherLayer = this.layerDefinitionsCollection.add({
|
||||
id: 'l-2',
|
||||
options: {
|
||||
type: 'torque',
|
||||
table_name: 'fee',
|
||||
source: 'a1'
|
||||
}
|
||||
});
|
||||
this.layerDefinitionsCollection.remove(otherLayer);
|
||||
expect(this.view.$('.js-add').hasClass('is-disabled')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('should setup layers list as a sortable', function () {
|
||||
beforeEach(function () {
|
||||
expect($.prototype.sortable).toHaveBeenCalled();
|
||||
$.prototype.sortable.calls.reset();
|
||||
|
||||
// setup additional layer for test-cases
|
||||
this.layerDefinitionsCollection.at(0).set('order', 0);
|
||||
this.layerDefinitionsCollection.add({
|
||||
id: 'l2',
|
||||
order: 2,
|
||||
kind: 'carto',
|
||||
options: {
|
||||
table_name: 'foo_bar2',
|
||||
cartocss: ''
|
||||
}
|
||||
});
|
||||
this.view.render();
|
||||
|
||||
expect($.prototype.sortable).toHaveBeenCalled();
|
||||
this.sortableArgs = $.prototype.sortable.calls.argsFor(0)[0];
|
||||
});
|
||||
|
||||
it('should set items', function () {
|
||||
expect(this.sortableArgs.items).toEqual(jasmine.any(String));
|
||||
});
|
||||
|
||||
describe('when a layer is moved', function () {
|
||||
it('should move layers from top to bottom', function () {
|
||||
// Simulate drag and drop by moving the layer on top to the bottom
|
||||
var layer0 = this.view.$el.find('.js-layer')[0];
|
||||
var data = $(layer0).data();
|
||||
var element = $(layer0).clone();
|
||||
$(layer0).remove();
|
||||
this.view.$('.js-layers').append(element);
|
||||
element.data(data);
|
||||
|
||||
this.sortableArgs.update('event', { item: element });
|
||||
|
||||
expect(this.userActions.moveLayer).toHaveBeenCalledWith({ from: 2, to: 1 });
|
||||
});
|
||||
|
||||
it('should move layers from bottom to top', function () {
|
||||
// Simulate drag and drop by moving the layer at the bottom to the top
|
||||
var layer1 = this.view.$el.find('.js-layer')[1];
|
||||
var data = $(layer1).data();
|
||||
var element = $(layer1).clone();
|
||||
$(layer1).remove();
|
||||
this.view.$('.js-layers').prepend(element);
|
||||
element.data(data);
|
||||
|
||||
this.sortableArgs.update('event', { item: element });
|
||||
|
||||
expect(this.userActions.moveLayer).toHaveBeenCalledWith({ from: 1, to: 2 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('when an analyses is dropped on sortable list', function () {
|
||||
beforeEach(function () {
|
||||
this.simulateDragNDrop = function () {
|
||||
this.$draggedAnalysis = $('<li class="js-analysis">an analysis node</li>');
|
||||
this.$draggedAnalysis.data({
|
||||
'analysis-node-id': 'a2',
|
||||
'layer-letter': 'a'
|
||||
});
|
||||
spyOn(this.$draggedAnalysis, 'remove').and.callThrough();
|
||||
this.view.$('.js-layers').children().first().after(this.$draggedAnalysis);
|
||||
this.sortableArgs.update('event', { item: this.$draggedAnalysis });
|
||||
}.bind(this);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
this.$draggedAnalysis.remove();
|
||||
this.$draggedAnalysis = null;
|
||||
});
|
||||
|
||||
describe('when fine', function () {
|
||||
beforeEach(function () {
|
||||
this.simulateDragNDrop();
|
||||
});
|
||||
|
||||
it('should create a new layer', function () {
|
||||
expect(this.userActions.createLayerForAnalysisNode).toHaveBeenCalledWith('a2', 'a', { at: 2 });
|
||||
});
|
||||
});
|
||||
|
||||
describe('when already reached the max layers limit', function () {
|
||||
beforeEach(function () {
|
||||
var err = new Error('max layers reached');
|
||||
err.userMaxLayers = 4;
|
||||
this.userActions.createLayerForAnalysisNode.and.throwError(err);
|
||||
this.simulateDragNDrop();
|
||||
});
|
||||
|
||||
it('should remove dragged element', function () {
|
||||
expect(this.$draggedAnalysis.remove).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should log notification', function () {
|
||||
expect(this.userActions.createLayerForAnalysisNode).toHaveBeenCalledWith('a2', 'a', { at: 2 });
|
||||
expect(this.view._showMaxLayerError).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,67 @@
|
||||
var cdb = require('internal-carto.js');
|
||||
var NotificationErrorHandler = require('builder/editor/layers/notification-error-message-handler');
|
||||
|
||||
describe('NotificationErrorHandler.extractErrorFromAnalysisNode', function () {
|
||||
it('should extract the right error message', function () {
|
||||
var analysisDefinitionNodeModel = new cdb.core.Model({
|
||||
id: 'b1',
|
||||
error: {
|
||||
message: 'cdb_dataservices_client._cdb_route_point_to_point(7): [cartodb_user_a6f0d0fe-4f4f-4217-8150-22b0010fe409_db] REMOTE ERROR: spiexceptions.InternalError: plpy.Error: There was an error trying to obtain route using mapzen provider: Timeout reading from socket'
|
||||
}
|
||||
});
|
||||
|
||||
var errorMessage = NotificationErrorHandler.extractErrorFromAnalysisNode(analysisDefinitionNodeModel);
|
||||
expect(errorMessage.message).toEqual('notifications.analysis.failed: There was an error trying to obtain route using mapzen provider: Timeout reading from socket');
|
||||
|
||||
analysisDefinitionNodeModel.set('error', { message: 'InternalError: cdb_dataservices_server._obs_getmeasure(7): [obs_2016_10_14_65e534f91e] REMOTE ERROR: Error performing intersection: TopologyException: found non-noded intersection between LINESTRING (-96.5796 32.577, -96.579 32.5768) and LINESTRING (-96.579 32.5768, -96.579 32.5768) at -96.578972198741511 32.576761407730309' });
|
||||
|
||||
errorMessage = NotificationErrorHandler.extractErrorFromAnalysisNode(analysisDefinitionNodeModel);
|
||||
expect(errorMessage.message).toEqual('notifications.analysis.failed: found non-noded intersection between LINESTRING (-96.5796 32.577, -96.579 32.5768) and LINESTRING (-96.579 32.5768, -96.579 32.5768) at -96.578972198741511 32.576761407730309');
|
||||
expect(errorMessage.type).toEqual('error');
|
||||
|
||||
analysisDefinitionNodeModel.set('error', { message: 'There was an error' });
|
||||
errorMessage = NotificationErrorHandler.extractErrorFromAnalysisNode(analysisDefinitionNodeModel);
|
||||
expect(errorMessage.message).toEqual('notifications.analysis.failed: There was an error');
|
||||
|
||||
analysisDefinitionNodeModel.set('error', { message: 'OMG, weird Exception: There was an error' });
|
||||
errorMessage = NotificationErrorHandler.extractErrorFromAnalysisNode(analysisDefinitionNodeModel);
|
||||
expect(errorMessage.message).toEqual('notifications.analysis.failed: There was an error');
|
||||
});
|
||||
|
||||
it('should replace the message with a more meaningful one', function () {
|
||||
var analysisDefinitionNodeModel = new cdb.core.Model({
|
||||
id: 'b1',
|
||||
error: {
|
||||
message: 'RuntimeError: Execution of function interrupted by signal'
|
||||
}
|
||||
});
|
||||
var errorMessage = NotificationErrorHandler.extractErrorFromAnalysisNode(analysisDefinitionNodeModel);
|
||||
expect(errorMessage.message).toEqual('notifications.analysis.failed: notifications.analysis.errors.timeout');
|
||||
});
|
||||
|
||||
it('should return the same error if there isn\'t a replacement', function () {
|
||||
var analysisDefinitionNodeModel = new cdb.core.Model({
|
||||
id: 'b1',
|
||||
error: {
|
||||
message: 'I am an unsupported error message. Hi!'
|
||||
}
|
||||
});
|
||||
|
||||
var errorMessage = NotificationErrorHandler.extractErrorFromAnalysisNode(analysisDefinitionNodeModel);
|
||||
expect(errorMessage.message).toEqual('notifications.analysis.failed: I am an unsupported error message. Hi!');
|
||||
});
|
||||
|
||||
it('should return the standard error if the analysis does not contain an error message', function () {
|
||||
var analysisDefinitionNodeModel = new cdb.core.Model({
|
||||
id: 'b1'
|
||||
});
|
||||
var errorMessage = NotificationErrorHandler.extractErrorFromAnalysisNode(analysisDefinitionNodeModel);
|
||||
expect(errorMessage.message).toEqual('notifications.analysis.failed');
|
||||
});
|
||||
|
||||
it('should extract the error from a string', function () {
|
||||
var errorMessage = NotificationErrorHandler.extractError('this_is_a_test_1: Postgis Plugin: ERROR: column "the_geom_webmercator" does not exist LINE 1: ...ECT ST_AsTWKB(ST_Simplify(ST_RemoveRepeatedPoints("the_geom_... ^ in executeQuery Full sql was: \'SELECT ST_AsTWKB(ST_Simplify(ST_RemoveRepeatedPoints("the_geom_webmercator",1e-05),1e-05,true),5) AS geom FROM (SELECT 1 FROM this_is_a_test_1) as cdbq WHERE "the_geom_webmercator" && ST_MakeEnvelope(-20037508.3,20037508.25881302,-20037508.25881302,20037508.3,3857)\'');
|
||||
expect(errorMessage.type).toEqual('warning');
|
||||
expect(errorMessage.message).toEqual('notifications.analysis.errors.without-geom-webmercator');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,57 @@
|
||||
var SyncInfoView = require('builder/editor/layers/sync-info/sync-info-view');
|
||||
var SyncModel = require('builder/data/synchronization-model');
|
||||
var TableModel = require('builder/data/table-model');
|
||||
var UserModel = require('builder/data/user-model');
|
||||
var FactoryModals = require('../../../factories/modals');
|
||||
|
||||
describe('editor/layers/sync-info/sync-info-view', function () {
|
||||
beforeEach(function () {
|
||||
var configModel = {};
|
||||
|
||||
this.syncModel = new SyncModel({
|
||||
run_at: new Date('2016-03-25'),
|
||||
ran_at: new Date('2016-03-26'),
|
||||
from_external_source: true,
|
||||
state: 'yep',
|
||||
error_code: '',
|
||||
error_message: ''
|
||||
}, {
|
||||
configModel: configModel
|
||||
});
|
||||
|
||||
this.tableModel = new TableModel({}, {
|
||||
configModel: configModel
|
||||
});
|
||||
|
||||
this.userModel = new UserModel({}, {
|
||||
configModel: configModel
|
||||
});
|
||||
|
||||
this.view = new SyncInfoView({
|
||||
modals: FactoryModals.createModalService(),
|
||||
syncModel: this.syncModel,
|
||||
tableModel: this.tableModel,
|
||||
userModel: this.userModel
|
||||
});
|
||||
});
|
||||
|
||||
describe('.render', function () {
|
||||
it('should hide options if the user is not the owner', function () {
|
||||
spyOn(this.tableModel, 'isOwner').and.returnValue(false);
|
||||
|
||||
this.view.render();
|
||||
|
||||
expect(this.tableModel.isOwner.calls.argsFor(0)[0].cid).toEqual(this.userModel.cid);
|
||||
expect(this.view.$('.js-options').length).toBe(0);
|
||||
});
|
||||
|
||||
it('should show options if the user is the owner', function () {
|
||||
spyOn(this.tableModel, 'isOwner').and.returnValue(true);
|
||||
|
||||
this.view.render();
|
||||
|
||||
expect(this.tableModel.isOwner.calls.argsFor(0)[0].cid).toEqual(this.userModel.cid);
|
||||
expect(this.view.$('.js-options').length).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user