Initial commit

This commit is contained in:
zhongjin
2020-06-15 10:58:47 +08:00
commit 4f1dfe7564
8590 changed files with 1516878 additions and 0 deletions
@@ -0,0 +1,366 @@
var Backbone = require('backbone');
var _ = require('underscore');
var analyses = require('builder/data/analyses');
var ConfigModel = require('builder/data/config-model');
var DataServicesApiCheck = require('builder/editor/layers/layer-content-views/analyses/analyses-quota/analyses-quota-info');
var camshaftReference = require('camshaft-reference').getVersion('latest');
var UnknownTypeFormModel = require('builder/editor/layers/layer-content-views/analyses/analysis-form-models/unknown-type-form-model');
var FallbackFormModel = require('builder/editor/layers/layer-content-views/analyses/analysis-form-models/fallback-form-model');
var DeprecatedSQLFunctionOptionModel = require('builder/components/modals/add-analysis/analysis-option-models/deprecated-sql-function-option-model');
describe('builder/data/analyses', function () {
Object.keys(analyses.MAP).forEach(function (key) {
var def = analyses.MAP[key];
describe(key + ' item', function () {
it('should have a title', function () {
expect(def.title).toEqual(jasmine.any(String));
});
it('should have a FormModel', function () {
expect(def.FormModel).toEqual(jasmine.any(Function));
});
});
}, this);
describe('deprecated-sql-function analysis', function () {
it('should have ModalModel', function () {
var sqlAnalysis = analyses.MAP['deprecated-sql-function'];
var optionModel = new sqlAnalysis.ModalModel(null, {
nodeAttrs: {
key: 'value'
}
});
expect(optionModel instanceof DeprecatedSQLFunctionOptionModel).toBe(true);
});
});
describe('getAnalysesByModalCategory', function () {
beforeEach(function () {
this.configModel = new ConfigModel({
user_name: 'foo',
sql_api_template: 'foo',
api_key: 'foo',
dataservices_enabled: {
geocoder_internal: true,
hires_geocoder: true,
isolines: true,
routing: true,
data_observatory: true
}
});
this.userModel = new Backbone.Model();
this.queryGeometryModel = new Backbone.Model();
this.queryGeometryModel.isPolygon = function () { return true; };
});
describe('data observatory', function () {
beforeEach(function () {
dataservicesApiHealth = DataServicesApiCheck.get(this.configModel);
spyOn(dataservicesApiHealth, 'getService');
});
afterEach(function () {
DataServicesApiCheck.get(this.configModel)._ready = 'ready';
});
var findDataObservatory = function (models, type) {
return _.filter(models, function (model) {
return model.nodeAttrs.type === type;
});
};
var dataservicesApiHealth;
describe('dataservices api existing', function () {
it('should be enabled if the user has credits and hard limit', function () {
var DO = new Backbone.Model({
service: 'observatory',
monthly_quota: 100,
soft_limit: true
});
dataservicesApiHealth._ready = 'ready';
dataservicesApiHealth.getService.and.returnValue(DO);
var analysisModels = analyses.getAnalysesByModalCategory('create_clean', {
userModel: this.userModel,
configModel: this.configModel,
queryGeometryModel: this.queryGeometryModel
});
expect(analysisModels.length).toBe(4);
expect(findDataObservatory(analysisModels, 'data-observatory-measure').length).toBe(0);
});
it('should NOT be enabled if the user has hard limit', function () {
var DO = new Backbone.Model({
service: 'observatory',
monthly_quota: 100,
soft_limit: false
});
dataservicesApiHealth._ready = 'ready';
dataservicesApiHealth.getService.and.returnValue(DO);
var analysisModels = analyses.getAnalysesByModalCategory('create_clean', {
userModel: this.userModel,
configModel: this.configModel,
queryGeometryModel: this.queryGeometryModel
});
expect(analysisModels.length).toBe(4);
expect(findDataObservatory(analysisModels, 'data-observatory-measure').length).toBe(0);
});
it('should NOT be enabled if the user doesn\'t have credits', function () {
var DO = new Backbone.Model({
service: 'observatory',
monthly_quota: 0,
soft_limit: true
});
dataservicesApiHealth._ready = 'ready';
dataservicesApiHealth.getService.and.returnValue(DO);
var analysisModels = analyses.getAnalysesByModalCategory('create_clean', {
userModel: this.userModel,
configModel: this.configModel,
queryGeometryModel: this.queryGeometryModel
});
expect(analysisModels.length).toBe(4);
expect(findDataObservatory(analysisModels, 'data-observatory-measure').length).toBe(0);
});
});
it('dataservices api not existing', function () {
DataServicesApiCheck.get(this.configModel)._ready = 'notready';
var analysisModels = analyses.getAnalysesByModalCategory('create_clean', {
userModel: this.userModel,
configModel: this.configModel,
queryGeometryModel: this.queryGeometryModel
});
expect(analysisModels.length).toBe(3);
expect(findDataObservatory(analysisModels, 'data-observatory-measure').length).toBe(0);
});
});
});
describe('.findFormModelByType', function () {
describe('when given an unknown type', function () {
it('should return an unknown model', function () {
expect(analyses.findFormModelByType('unknown-indeed')).toBe(UnknownTypeFormModel);
expect(analyses.findFormModelByType()).toBe(UnknownTypeFormModel);
expect(analyses.findFormModelByType({})).toBe(UnknownTypeFormModel);
expect(analyses.findFormModelByType(true)).toBe(UnknownTypeFormModel);
expect(analyses.findFormModelByType(false)).toBe(UnknownTypeFormModel);
});
});
describe('when given a type which is not implemented (yet)', function () {
beforeEach(function () {
camshaftReference.analyses['just-for-testing'] = {};
});
afterEach(function () {
delete camshaftReference['just-for-testing'];
});
it('should return fallback model', function () {
expect(analyses.findFormModelByType('just-for-testing')).toBe(FallbackFormModel);
});
});
describe('when given a dummy type (not in camshaft)', function () {
it('should not return an Unknown model', function () {
expect(analyses.findFormModelByType('georeference-placeholder')).not.toBe(UnknownTypeFormModel);
});
});
describe('when given an implemented type', function () {
it('should return its formModel', function () {
var BufferFormModel = analyses.findFormModelByType('buffer');
expect(BufferFormModel).toBeDefined();
expect(BufferFormModel).not.toBe(UnknownTypeFormModel);
expect(BufferFormModel).not.toBe(FallbackFormModel);
});
});
});
describe('.title', function () {
describe('when given an unknown type', function () {
it('should return unknown title', function () {
expect(analyses.title()).toEqual('analyses.unknown');
expect(analyses.title('')).toEqual('analyses.unknown');
expect(analyses.title({})).toEqual('analyses.unknown');
expect(analyses.title(true)).toEqual('analyses.unknown');
});
});
describe('when given a type which is not implemented (yet)', function () {
beforeEach(function () {
camshaftReference.analyses['just-for-testing'] = {};
});
afterEach(function () {
delete camshaftReference['just-for-testing'];
});
it('should return the default for type', function () {
expect(analyses.title('just-for-testing')).toEqual('analyses.just-for-testing');
});
});
describe('when given an implemented type', function () {
it('should return a corresponding title', function () {
expect(analyses.title('buffer')).toEqual(jasmine.any(String));
expect(analyses.title('buffer')).not.toContain('unknown');
});
});
describe('when given a DO analysis', function () {
it('should return the measurement as the title for the DO analysis if implemented', function () {
var model = new Backbone.Model({
type: 'data-observatory-measure',
measurement: 'age-and-gender'
});
expect(analyses.title(model)).toEqual('analyses.data-observatory-measure.age-and-gender');
});
it('should return the default title if the measurement is not implemented', function () {
var model = new Backbone.Model({
type: 'data-observatory-measure',
measurement: 'something-very-new'
});
expect(analyses.title(model)).toEqual('analyses.data-observatory-measure.title');
});
});
describe('when given a model', function () {
it('should return the corresponding title', function () {
var model = new Backbone.Model({type: 'buffer'});
expect(analyses.title(model)).toEqual(jasmine.any(String));
expect(analyses.title(model)).not.toContain('unknown');
expect(analyses.title(model)).toEqual(analyses.title('buffer'));
});
});
});
describe('.link', function () {
describe('when given an unknown type', function () {
it('should return empty link', function () {
expect(analyses.link()).toBe('');
expect(analyses.link('')).toBe('');
expect(analyses.link({})).toBe('');
expect(analyses.link(true)).toBe('');
});
});
describe('when given a model', function () {
it('should return the corresponding link', function () {
var model = new Backbone.Model({type: 'buffer'});
expect(analyses.link(model)).toEqual(jasmine.any(String));
expect(analyses.link(model)).not.toContain('unknown');
expect(analyses.link(model)).toEqual(analyses.link('buffer'));
});
});
});
describe('MAP', function () {
it('should have right properties for closest analysis', function () {
expect(analyses.MAP.closest).toBeDefined();
expect(analyses.MAP.closest.modalTitle).toEqual('editor.layers.analysis-form.find-nearest.modal-title');
expect(analyses.MAP.closest.modalDesc).toEqual('editor.layers.analysis-form.find-nearest.modal-desc');
expect(analyses.MAP.closest.modalCategory).toEqual('analyze_predict');
expect(analyses.MAP.closest.modalLink).toEqual('https://carto.com/learn/guides/analysis/find-nearest');
expect(analyses.MAP.closest.onboardingTemplate).toEqual(jasmine.any(Function));
});
it('should have dummy property on certain analyses', function () {
expect(analyses.MAP['georeference-placeholder'].dummy).toBeDefined();
expect(analyses.MAP['georeference-placeholder'].dummy).toBe(true);
});
});
describe('checkIfxxx several checks', function () {
var checkIfxxxTest = function (testTypes, dataServicesFlag) {
var readyCalled = false;
var fakeDS = {
isReady: function () {
readyCalled = true;
return true;
}
};
spyOn(DataServicesApiCheck, 'get').and.returnValue(fakeDS);
var userModel = new Backbone.Model();
var queryGeometryModel = new Backbone.Model();
queryGeometryModel.isPolygon = function () { return true; };
_.each(testTypes, function (analysisType) {
var checkIfEnabled = analyses.MAP[analysisType].checkIfEnabled;
var configModel = {
dataServiceEnabled: function () {}
};
spyOn(configModel, 'dataServiceEnabled').and.returnValue(true);
readyCalled = false;
expect(checkIfEnabled).toBeDefined();
var result = checkIfEnabled({
userModel: userModel,
queryGeometryModel: queryGeometryModel,
configModel: configModel
});
expect(result).toBe(true);
expect(readyCalled).toBe(true);
expect(configModel.dataServiceEnabled).toHaveBeenCalledWith(dataServicesFlag);
});
};
it('analyses that needs georeference should check dataservices', function () {
var geoReferenceAnalyses = [
'georeference-city',
'georeference-ip-address',
'georeference-country',
'georeference-postal-code',
'georeference-admin-region'
];
checkIfxxxTest(geoReferenceAnalyses, 'geocoder_internal');
});
it('analyses that needs routing should check dataservices', function () {
var routingAnalyses = [
'routing-sequential',
'routing-to-layer-all-to-all',
'routing-to-single-point'
];
checkIfxxxTest(routingAnalyses, 'routing');
});
it('analyses that needs external geocoding should check dataservices', function () {
var externalGeocodingAnalyses = [
'georeference-street-address'
];
checkIfxxxTest(externalGeocodingAnalyses, 'hires_geocoder');
});
it('analyses that needs Data Observatory should check dataservices', function () {
var dataObservatoryAnalyses = [
'data-observatory-measure'
];
checkIfxxxTest(dataObservatoryAnalyses, 'data_observatory');
});
});
describe('isAnalysisValidByType', function () {
it('should call `checkIfEnabled` if the analysis has a `checkIf` function', function () {
var theAnalysis = analyses.MAP['georeference-city'];
spyOn(theAnalysis, 'checkIfEnabled').and.returnValue(true);
analyses.isAnalysisValidByType('georeference-city');
expect(theAnalysis.checkIfEnabled).toHaveBeenCalled();
});
});
});
@@ -0,0 +1,87 @@
var Backbone = require('backbone');
var ConfigModel = require('builder/data/config-model');
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
var AnalysisDefinitionsCollection = require('builder/data/analysis-definitions-collection');
describe('data/analysis-definition-model', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
configModel: configModel,
userModel: {}
});
this.collection = new AnalysisDefinitionsCollection(null, {
configModel: configModel,
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
layerDefinitionsCollection: new Backbone.Collection(),
vizId: 'v-123'
});
this.collection.add({
id: 'xyz123',
analysis_definition: {
id: 'a0',
type: 'source',
table_name: 'foo',
params: {
query: 'SELECT * FROM foo'
},
options: {
custom: 'value'
}
}
});
this.model = this.collection.get('xyz123');
this.analysisDefinitionNodesCollection.add({
id: 'b0',
type: 'source',
table_name: 'paco',
params: {
query: 'SELECT * FROM other'
}
});
});
describe('.containsNode', function () {
beforeEach(function () {
this.containedNode = this.analysisDefinitionNodesCollection.get('a0');
this.otherNode = this.analysisDefinitionNodesCollection.get('b0');
});
it('should return true if given node is contained in the analysis', function () {
expect(this.model.containsNode(this.containedNode)).toBe(true);
expect(this.model.containsNode(this.otherNode)).toBe(false);
expect(this.model.containsNode()).toBe(false);
});
it('should return false if the analysis has no node', function () {
this.model.set('node_id', 'x0');
expect(this.model.containsNode(this.containedNode)).toBe(false);
expect(this.model.containsNode(this.otherNode)).toBe(false);
expect(this.model.containsNode()).toBe(false);
});
});
describe('.toJSON', function () {
it('should return serialized model with custom options', function () {
expect(this.model.toJSON()).toEqual({
id: 'xyz123',
analysis_definition: {
id: 'a0',
type: 'source',
params: {
query: 'SELECT * FROM foo'
},
options: {
table_name: 'foo',
custom: 'value'
}
}
});
});
});
});
@@ -0,0 +1,488 @@
var _ = require('underscore');
var ConfigModel = require('builder/data/config-model');
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
describe('builder/data/analysis-definition-node-model', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.collection = new AnalysisDefinitionNodesCollection(null, {
configModel: configModel,
userModel: {}
});
this.a0raw = {
id: 'a0',
type: 'source',
params: {
query: 'SELECT * FROM bar'
},
options: {
table_name: 'bar',
test: 'hello'
}
};
this.collection.add([
{
id: 'a1',
type: 'trade-area',
params: {
source: this.a0raw,
kind: 'walk',
time: 300,
dissolved: true,
isolines: 3
},
options: {
optional: 'goes separately'
}
}, {
id: 'b1',
type: 'intersection',
params: {
source: this.a0raw,
target: {
id: 'b0',
type: 'source',
params: {
query: 'SELECT * FROM my_polygons'
},
options: {
table_name: 'my_polygons'
}
}
},
options: {
primary_source_name: 'target'
}
}, {
id: 'c1',
type: 'deprecated-sql-function',
params: {
function_name: 'DEP_EXT_buffer',
primary_source: this.a0raw
}
}, {
id: 'd1',
type: 'deprecated-sql-function',
params: {
function_name: 'DEP_EXT_spatialinterpolation',
primary_source: this.a0raw,
secondary_source: this.a0raw
}
}
]);
expect(this.collection.pluck('id')).toEqual(['a0', 'b0', 'a1', 'b1', 'c1', 'd1'], 'should have created individual nodes');
this.a0 = this.collection.get('a0');
this.b0 = this.collection.get('b0');
this.a1 = this.collection.get('a1');
this.b1 = this.collection.get('b1');
this.c1 = this.collection.get('c1');
this.d1 = this.collection.get('d1');
});
it('should init properly', function () {
this.collection.each(function (model) {
expect(model.queryGeometryModel).toBeDefined();
expect(model.querySchemaModel).toBeDefined();
expect(model.queryRowsCollection).toBeDefined();
});
});
it('should not have any geom from start', function () {
expect(this.a0.queryGeometryModel.get('simple_geom')).toBeFalsy();
});
it('should keep a flat, denormalized attrs structure internally', function () {
expect(this.a0.attributes).toEqual({
id: 'a0',
type: 'source',
query: 'SELECT * FROM bar',
table_name: 'bar',
test: 'hello',
status: 'ready'
});
expect(this.a1.attributes).toEqual({
id: 'a1',
type: 'trade-area',
source: 'a0',
kind: 'walk',
time: 300,
dissolved: true,
isolines: 3,
optional: 'goes separately'
});
expect(this.b1.attributes).toEqual({
id: 'b1',
type: 'intersection',
source: 'a0',
target: 'b0',
primary_source_name: 'target'
});
});
describe('.sourceIds', function () {
it('should return source ids if there are any', function () {
expect(this.a0.sourceIds()).toEqual([], 'a source node should not have any sources');
expect(this.a1.sourceIds()).toEqual(['a0']);
expect(this.b1.sourceIds()).toEqual(['a0', 'b0']);
});
});
describe('.hasPrimarySource', function () {
it('should return true if it has a primary source', function () {
expect(this.a0.hasPrimarySource()).toBe(false, 'a source node should not have any sources');
expect(this.a1.hasPrimarySource()).toBe(true, 'a trade-area should have a single, primary source');
expect(this.b1.hasPrimarySource()).toBe(true);
});
});
describe('.getPrimarySource', function () {
it('should return the primary source if it has any', function () {
expect(this.a0.getPrimarySource()).toBeUndefined('a source node should not have any sources');
expect(this.a1.getPrimarySource().id).toEqual('a0');
expect(this.b1.getPrimarySource().id).toEqual('b0');
});
});
describe('.hasSecondarySource', function () {
it('should return true if it has a secondary source', function () {
expect(this.a0.hasSecondarySource()).toBe(false, 'a source node should not have any sources');
expect(this.a1.hasSecondarySource()).toBe(false);
expect(this.b1.hasSecondarySource()).toBe(true, 'should be target');
});
});
describe('.getSecondarySource', function () {
it('should return the secondary source if it has any', function () {
expect(this.a0.getSecondarySource()).toBeUndefined('a source node should not have any sources');
expect(this.a1.getSecondarySource()).toBeUndefined();
expect(this.b1.getSecondarySource().id).toEqual('a0');
});
});
describe('.changeSourceIds', function () {
it('should change the source ids that matches the current id', function () {
this.a1.changeSourceIds('a0', 'c1');
expect(this.a1.sourceIds()).toEqual(['c1']);
this.b1.changeSourceIds('b0', 'c1');
expect(this.b1.sourceIds()).toEqual(['a0', 'c1']);
this.b1.changeSourceIds('a0', 'd1');
expect(this.b1.sourceIds()).toEqual(['d1', 'c1']);
});
it('should do nothing if given current id does not match any source', function () {
this.a1.changeSourceIds('x9', 'c1');
expect(this.a1.sourceIds()).toEqual(['a0']);
});
});
describe('.destroy', function () {
it('should destroy the query schema model', function () {
var querySchemaModel = this.a0.querySchemaModel;
spyOn(querySchemaModel, 'destroy');
this.a0.destroy();
expect(this.collection.pluck('id')).not.toContain('a0');
expect(querySchemaModel.destroy).toHaveBeenCalled();
expect(this.a0.querySchemaModel).toBeNull();
querySchemaModel = this.a1.querySchemaModel;
spyOn(querySchemaModel, 'destroy');
this.a1.destroy();
expect(this.collection.pluck('id')).not.toContain('a1');
expect(querySchemaModel.destroy).toHaveBeenCalled();
expect(this.a1.querySchemaModel).toBeNull();
querySchemaModel = this.b1.querySchemaModel;
spyOn(querySchemaModel, 'destroy');
this.b1.destroy();
expect(this.collection.pluck('id')).not.toContain('b1');
expect(querySchemaModel.destroy).toHaveBeenCalled();
expect(this.b1.querySchemaModel).toBeNull();
});
});
describe('.toJSON', function () {
it('should serialize the model', function () {
expect(this.a0.toJSON()).toEqual(this.a0raw);
expect(this.a1.toJSON()).toEqual(
jasmine.objectContaining({
id: 'a1',
type: 'trade-area',
params: jasmine.any(Object),
options: {
optional: 'goes separately'
}
}));
expect(this.a1.toJSON()).toEqual(
jasmine.objectContaining({
params: {
source: this.a0raw,
kind: 'walk',
time: 300,
dissolved: true,
isolines: 3
}
}));
expect(this.b1.toJSON()).toEqual(
jasmine.objectContaining({
id: 'b1',
type: 'intersection',
params: jasmine.any(Object),
options: {
primary_source_name: 'target'
}
}));
expect(this.b1.toJSON()).toEqual(
jasmine.objectContaining({
params: {
source: this.a0raw,
source_columns: undefined,
target: {
id: 'b0',
type: 'source',
params: {
query: 'SELECT * FROM my_polygons'
},
options: {
table_name: 'my_polygons'
}
}
}
}));
expect(this.c1.toJSON().params.function_name).toEqual('DEP_EXT_buffer');
expect(this.c1.toJSON().params.function_args).not.toBeDefined();
expect(this.c1.toJSON().params.primary_source).toBeDefined();
expect(this.c1.toJSON().params.secondary_source).not.toBeDefined();
expect(this.d1.toJSON().params.function_name).toEqual('DEP_EXT_spatialinterpolation');
expect(this.d1.toJSON().params.function_args).not.toBeDefined();
expect(this.d1.toJSON().params.primary_source).toBeDefined();
expect(this.d1.toJSON().params.secondary_source).toBeDefined();
});
describe('when skipOptions is set', function () {
beforeEach(function () {
this.options = {skipOptions: true};
});
it('should skip options if skipOptions is set to true', function () {
var a0WithoutOptions = _.omit(this.a0raw, 'options');
expect(this.a0.toJSON(this.options)).toEqual(a0WithoutOptions);
expect(this.a1.toJSON(this.options)).toEqual({
id: 'a1',
type: 'trade-area',
params: {
source: a0WithoutOptions,
kind: 'walk',
time: 300,
dissolved: true,
isolines: 3
}
});
expect(this.b1.toJSON(this.options)).toEqual(
jasmine.objectContaining({
id: 'b1',
type: 'intersection',
params: jasmine.any(Object)
}));
expect(this.b1.toJSON(this.options)).toEqual(
jasmine.objectContaining({
params: {
source: a0WithoutOptions,
source_columns: undefined,
target: {
id: 'b0',
type: 'source',
params: {
query: 'SELECT * FROM my_polygons'
}
}
}
}));
});
});
});
describe('.containsNode', function () {
it('should return true if given node is contained inside the analysis', function () {
expect(this.a0.containsNode(this.b1)).toBe(false, 'source should not contain any node, you fool!');
expect(this.a1.containsNode(this.a0)).toBe(true);
expect(this.a1.containsNode(this.b0)).toBe(false, 'should only contain a0');
expect(this.b1.containsNode(this.a0)).toBe(true);
expect(this.b1.containsNode(this.b0)).toBe(true);
expect(this.b1.containsNode(this.a1)).toBe(false);
});
});
describe('.isValidAsInputForType', function () {
describe('when geometry output is unknown', function () {
it('should return false', function () {
expect(this.a0.isValidAsInputForType('buffer')).toBe(null);
expect(this.a0.isValidAsInputForType('intersection')).toBe(null);
expect(this.a1.isValidAsInputForType('trade-area')).toBe(null);
expect(this.a1.isValidAsInputForType('buffer')).toBe(null);
expect(this.b1.isValidAsInputForType('buffer')).toBe(null);
expect(this.b1.isValidAsInputForType('trade-area')).toBe(null);
});
});
describe('when geometry output is known', function () {
it('should return true if is valid as input type', function () {
expect(this.a0.isValidAsInputForType('source')).toBe(false, 'a source node should not accept any input');
this.a0.queryGeometryModel.set('simple_geom', 'point');
expect(this.a0.isValidAsInputForType('trade-area')).toBe(true);
expect(this.a0.isValidAsInputForType('buffer')).toBe(true);
this.a0.queryGeometryModel.set('simple_geom', 'polygon');
expect(this.a0.isValidAsInputForType('trade-area')).toBe(false, 'trade-area only accepts points (unless the camshaft reference changed?)');
expect(this.a0.isValidAsInputForType('buffer')).toBe(true);
});
});
});
describe('.clone', function () {
var a0;
beforeEach(function () {
a0 = this.a0;
});
it('should throw error in bad input', function () {
expect(function () { a0.clone(); }).toThrowError(/required/);
expect(function () { a0.clone(null); }).toThrowError(/required/);
expect(function () { a0.clone(undefined); }).toThrowError(/required/);
expect(function () { a0.clone(true); }).toThrowError(/required/);
expect(function () { a0.clone({}); }).toThrowError(/required/);
});
it('should require a new id', function () {
expect(function () { a0.clone('a0'); }).toThrowError(/different/);
});
it('should create a new node with same params but new id', function () {
var m = a0.clone('g0');
expect(m).toBeDefined();
expect(m.id).toEqual('g0');
expect(m.get('type')).toEqual('source');
expect(m.get('query')).toEqual(jasmine.any(String));
expect(m.get('table_name')).toEqual(jasmine.any(String));
expect(m.get('test')).toEqual('hello');
});
it('should add the cloned item to the collection', function () {
var m = a0.clone('g0');
expect(this.collection.contains(m)).toBe(true);
});
});
describe('.linkedListBySameLetter', function () {
var a0;
beforeEach(function () {
a0 = this.a0;
});
describe('when given a source node', function () {
beforeEach(function () {
this.list = a0.linkedListBySameLetter();
});
it('should return a list with only the source', function () {
expect(_.pluck(this.list, 'id')).toEqual(['a0']);
});
});
describe('when called on a node which sources all belong to same letter', function () {
beforeEach(function () {
this.a2 = this.collection.add({
id: 'a2',
type: 'buffer',
params: {
source: this.a1.toJSON(),
radius: 100
}
});
this.list = this.a2.linkedListBySameLetter();
});
it('should return list with all nodes', function () {
expect(_.pluck(this.list, 'id')).toEqual(['a2', 'a1', 'a0']);
});
});
describe('when given a node which sub-tree belongs to other letter', function () {
beforeEach(function () {
this.c2 = this.collection.add({
id: 'c2',
type: 'buffer',
params: {
radius: 20,
source: {
id: 'c1',
type: 'buffer',
params: {
radius: 10,
source: this.b1.toJSON()
}
}
}
});
this.list = this.c2.linkedListBySameLetter();
});
it('should return list with all nodes', function () {
expect(_.pluck(this.list, 'id')).toEqual(['c2', 'c1']);
});
});
});
describe('.letter', function () {
it('should return the letter representation ', function () {
expect(this.a0.letter()).toEqual('a');
expect(this.b1.letter()).toEqual('b');
});
});
describe('.canBeDeletedByUser', function () {
it('should return true if it has a source node', function () {
expect(this.b1.canBeDeletedByUser()).toBe(true);
expect(this.a0.canBeDeletedByUser()).toBe(false);
expect(this.b0.canBeDeletedByUser()).toBe(false);
});
});
describe('when given a geom from start', function () {
beforeEach(function () {
this.model = this.collection.add({
id: 'x0',
type: 'buffer',
params: {},
options: {
simple_geom: 'point'
}
});
});
it('should set the geom on the query-schema-model', function () {
expect(this.model.queryGeometryModel.get('simple_geom')).toEqual('point');
});
});
});
@@ -0,0 +1,107 @@
var ConfigModel = require('builder/data/config-model');
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
var UserModel = require('builder/data/user-model');
describe('builder/data/analysis-definition-node-source-model', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.userModel = new UserModel({
username: 'pericoo'
}, {
configModel: configModel
});
this.collection = new AnalysisDefinitionNodesCollection(null, {
configModel: configModel,
userModel: this.userModel
});
var analysisParams = {
id: 'a0',
type: 'source',
params: {
query: 'SELECT * FROM bar'
},
options: {
table_name: 'bar',
test: 'hello'
}
};
this.model = this.collection.add(analysisParams);
});
describe('.getDefaultQuery', function () {
beforeEach(function () {
spyOn(this.userModel, 'isInsideOrg');
spyOn(this.model.tableModel, 'getOwnerName');
});
it('should provide a default query without qualifing if user does not belong to an organization', function () {
this.userModel.isInsideOrg.and.returnValue(false);
this.model.tableModel.getOwnerName.and.returnValue('pericoo');
expect(this.model.getDefaultQuery()).toBe('SELECT * FROM bar');
});
it('should provide a default query with qualifing if user does belong to an organization', function () {
this.userModel.isInsideOrg.and.returnValue(true);
this.model.tableModel.getOwnerName.and.returnValue('pericoo');
expect(this.model.getDefaultQuery()).toBe('SELECT * FROM pericoo.bar');
});
it('should provide a default query with qualifing if user does belong to an organization and is not the owner', function () {
this.userModel.isInsideOrg.and.returnValue(true);
this.model.tableModel.getOwnerName.and.returnValue('hello');
expect(this.model.getDefaultQuery()).toBe('SELECT * FROM hello.bar');
});
});
describe('.isCustomQueryApplied', function () {
/*
It just compares two queries, not worth it to be tested.
*/
});
describe('.isReadOnly', function () {
beforeEach(function () {
spyOn(this.model.tableModel, 'isReadOnly');
spyOn(this.model, 'isCustomQueryApplied');
});
it('should be true if table-model is read-only', function () {
this.model.tableModel.isReadOnly.and.returnValue(true);
expect(this.model.isReadOnly()).toBeTruthy();
});
it('should be true if table-model is read-only', function () {
this.model.tableModel.isReadOnly.and.returnValue(false);
this.model.isCustomQueryApplied.and.returnValue(true);
expect(this.model.isReadOnly()).toBeTruthy();
});
it('should be false if table-model is not read-only and custom query is not applied', function () {
this.model.tableModel.isReadOnly.and.returnValue(false);
this.model.isCustomQueryApplied.and.returnValue(false);
expect(this.model.isReadOnly()).toBeFalsy();
});
});
describe('setTableName', function () {
it('should set the new table name in several objects', function () {
expect(this.model.get('table_name')).toBe('bar');
expect(this.model.tableModel.get('name')).toBe('bar');
expect(this.model.queryRowsCollection._tableName).toBe('bar');
expect(this.model.querySchemaModel.get('query')).toBe('SELECT * FROM bar');
this.model.setTableName('har');
expect(this.model.get('table_name')).toBe('har');
expect(this.model.tableModel.get('name')).toBe('har');
expect(this.model.queryRowsCollection._tableName).toBe('har');
expect(this.model.querySchemaModel.get('query')).toBe('SELECT * FROM har');
});
});
});
@@ -0,0 +1,206 @@
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
var AnalysisDefinitionNodeModel = require('builder/data/analysis-definition-node-model');
var ConfigModel = require('builder/data/config-model');
describe('data/analysis-definition-nodes-collection', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.collection = new AnalysisDefinitionNodesCollection(null, {
configModel: configModel,
userModel: {},
relatedTableData: [
{
name: 'foo',
privacy: 'PRIVATE'
}
]
});
});
describe('.createSourceNode', function () {
describe('when given faulty input', function () {
it('should throw an error', function () {
var c = this.collection;
expect(function () { c.createSourceNode({}); }).toThrowError(/required/);
expect(function () { c.createSourceNode({id: 'missing table'}); }).toThrowError(/required/);
expect(function () { c.createSourceNode({tableName: 'missing id'}); }).toThrowError(/required/);
expect(function () { c.createSourceNode(); }).toThrowError(/required/);
});
});
describe('when given valid input', function () {
it('should add a new source node', function () {
var m = this.collection.createSourceNode({id: 'a0', tableName: 'foo'});
expect(m.attributes).toEqual({
id: 'a0',
type: 'source',
table_name: 'foo',
query: 'SELECT * FROM foo',
status: 'ready'
});
m = this.collection.createSourceNode({id: 'b0', tableName: '000cd294-b124-4f82-b569-0f7fe41d2db8'});
expect(m.attributes).toEqual({
id: 'b0',
type: 'source',
table_name: '000cd294-b124-4f82-b569-0f7fe41d2db8',
query: 'SELECT * FROM "000cd294-b124-4f82-b569-0f7fe41d2db8"',
status: 'ready'
});
m = this.collection.createSourceNode({id: 'c0', tableName: 'bar'});
expect(m.attributes).toEqual({
id: 'c0',
type: 'source',
table_name: 'bar',
query: 'SELECT * FROM bar',
status: 'ready'
});
m = this.collection.createSourceNode({id: 'd0', tableName: '000cd294-b124-4f82-b569-0f7fe41d2db8'});
expect(m.attributes).toEqual({
id: 'd0',
type: 'source',
table_name: '000cd294-b124-4f82-b569-0f7fe41d2db8',
query: 'SELECT * FROM "000cd294-b124-4f82-b569-0f7fe41d2db8"',
status: 'ready'
});
});
it('should allow to create source node with custom query', function () {
var m = this.collection.createSourceNode({id: 'c0', tableName: 'baz', query: 'SELECT the_geom FROM baz LIMIT 10'});
expect(m.attributes).toEqual({
id: 'c0',
type: 'source',
table_name: 'baz',
query: 'SELECT the_geom FROM baz LIMIT 10',
status: 'ready'
});
});
});
});
describe('when sources are added', function () {
beforeEach(function () {
this.collection.add([{
id: 'a0',
type: 'source',
params: {
query: 'SELECT * FROM foo'
},
options: {
table_name: 'foo'
}
}, {
id: 'a1',
type: 'buffer',
radio: 300,
distance: 'meters',
source: 'a0'
}]);
this.b0 = this.collection.add({
id: 'b0',
type: 'source',
params: {
query: 'SELECT * FROM bar'
},
options: {
table_name: 'bar'
}
});
this.b1 = this.collection.add({
id: 'b1',
type: 'buffer',
radio: 300,
distance: 'meters',
source: 'b0'
});
});
it('should have created a model for each item', function () {
expect(this.collection.pluck('id')).toEqual(['a0', 'a1', 'b0', 'b1']);
});
describe('when adding an analysis that contains references to existing sources', function () {
beforeEach(function () {
this.collection.add({
id: 'd1',
type: 'intersection',
params: {
source: {
id: 'b0',
type: 'source',
params: {
query: 'SELECT * FROM bar'
},
options: {
table_name: 'bar'
}
},
target: {
id: 'c0',
type: 'source',
params: {
query: 'SELECT * FROM baz'
},
options: {
table_name: 'baz'
}
}
}
});
});
it('should have created a model for the new analysis node and source nodes if not already existing', function () {
expect(this.collection.pluck('id')).toEqual(['a0', 'a1', 'b0', 'b1', 'c0', 'd1']);
});
});
describe('a table model should be created', function () {
it('from relatedTableData if available', function () {
expect(this.collection.get('a0').tableModel.get('name')).toEqual('foo');
expect(this.collection.get('a0').tableModel.get('privacy')).toEqual('PRIVATE');
});
it('as a default model if relatedTableData is not available', function () {
expect(this.collection.get('b0').tableModel.get('name')).toEqual('bar');
expect(this.collection.get('b0').tableModel.get('privacy')).toBeUndefined();
});
});
describe('._getNodeOnTop', function () {
it('should return node on top', function () {
expect(this.collection._getNodeOnTop()).toBe(this.b1);
});
});
describe('._getTopNodeIndex', function () {
it('should return node on top index', function () {
expect(this.collection._getTopNodeIndex()).toEqual(3);
});
});
describe('._isNodeOnTop', function () {
it('should return true if given node is on top', function () {
expect(this.collection._isNodeOnTop(this.b0)).toBe(false);
expect(this.collection._isNodeOnTop(this.b1)).toBe(true);
});
});
describe('._onQueryObjectsUpdated', function () {
it('should fetch Query Objects if node is on top', function () {
spyOn(AnalysisDefinitionNodeModel.prototype, 'fetchQueryObjects');
this.b0.trigger('queryObjectsUpdated', this.b0);
this.b1.trigger('queryObjectsUpdated', this.b1);
expect(AnalysisDefinitionNodeModel.prototype.fetchQueryObjects).toHaveBeenCalledTimes(1);
});
});
});
});
@@ -0,0 +1,277 @@
var Backbone = require('backbone');
var ConfigModel = require('builder/data/config-model');
var UserModel = require('builder/data/user-model');
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
var AnalysisDefinitionsCollection = require('builder/data/analysis-definitions-collection');
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
describe('builder/data/analysis-definitions-collection', function () {
beforeEach(function () {
this.configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.userModel = new UserModel({}, {
configModel: this.configModel
});
this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
configModel: this.configModel,
userModel: this.userModel
});
this.collection = new AnalysisDefinitionsCollection(null, {
vizId: 'v-123',
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
configModel: this.configModel
});
this.A = this.collection.add({
id: 'A',
analysis_definition: {
id: 'a1',
type: 'buffer',
params: {
radius: 100,
source: {
id: 'a0',
type: 'source',
params: {
query: 'SELECT * FROM somewhere'
}
}
}
}
});
this.B = this.collection.add({
id: 'B',
analysis_definition: {
id: 'b1',
type: 'buffer',
params: {
radius: 100,
source: {
id: 'a1'
}
}
}
});
this.C = this.collection.add({
id: 'C',
analysis_definition: {
id: 'c1',
type: 'buffer',
params: {
source: {
id: 'b1'
}
}
}
});
this.originalAjax = Backbone.ajax;
Backbone.ajax = function () {
return {
always: function (cb) {
cb();
}
};
};
});
afterEach(function () {
Backbone.ajax = this.originalAjax;
});
describe('.url', function () {
it('should return URL', function () {
expect(this.collection.url()).toMatch(/\/analyses$/);
});
});
describe('.findAnalysisThatContainsNode', function () {
describe('when given a node that does not have any analysis', function () {
it('should return undefined', function () {
expect(this.collection.findAnalysisThatContainsNode({id: 'x123'})).toBeUndefined();
});
});
describe('when given a node is root', function () {
it('should return the analysis', function () {
expect(this.collection.findAnalysisThatContainsNode({id: 'c1'})).toBeDefined();
});
});
describe('when given a node is contained in an analysis', function () {
it('should return the analysis', function () {
expect(this.collection.findAnalysisThatContainsNode({id: 'a0'})).toBeDefined();
});
});
});
describe('.saveAnalysisForLayer', function () {
beforeEach(function () {
this.a0 = this.analysisDefinitionNodesCollection.get('a0');
this.a1 = this.analysisDefinitionNodesCollection.get('a1');
this.b1 = this.analysisDefinitionNodesCollection.get('b1');
this.c1 = this.analysisDefinitionNodesCollection.get('c1');
this.x1 = this.analysisDefinitionNodesCollection.add({
id: 'x1',
type: 'buffer',
params: {
radius: 100,
source: {
id: 'a1'
}
}
});
this.layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
configModel: this.configModel,
userModel: this.userModel,
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
mapId: '123',
stateDefinitionModel: {}
});
this.layerA = this.layerDefinitionsCollection.add({
id: 'layerA',
kind: 'carto',
options: {
letter: 'a',
source: 'a1'
}
});
this.layerB = this.layerDefinitionsCollection.add({
id: 'layerB',
kind: 'carto',
options: {
letter: 'b',
source: 'b1'
}
});
this.layerC = this.layerDefinitionsCollection.add({
id: 'layerC',
kind: 'carto',
options: {
letter: 'c',
source: 'c1'
}
});
this.layerX = this.layerDefinitionsCollection.add({
id: 'layerX',
kind: 'carto',
options: {
letter: 'x',
source: 'x1'
}
});
this.beforeSaveSpy = jasmine.createSpy('beforeSave');
spyOn(this.A, 'save');
spyOn(this.B, 'save');
spyOn(this.C, 'save');
spyOn(this.layerA, 'save');
spyOn(this.layerB, 'save');
spyOn(this.layerC, 'save');
});
describe('when given a layer with an already persisted analysis', function () {
it('should save all analysis containing node', function () {
this.collection.saveAnalysisForLayer(this.layerA);
expect(this.A.save).toHaveBeenCalled();
expect(this.B.save).toHaveBeenCalled();
expect(this.C.save).toHaveBeenCalled();
this.A.save.calls.reset();
this.B.save.calls.reset();
this.collection.saveAnalysisForLayer(this.layerB);
expect(this.A.save).not.toHaveBeenCalled();
expect(this.B.save).toHaveBeenCalled();
expect(this.C.save).toHaveBeenCalled();
});
});
describe('when given a layer has a source that is owned by another layer', function () {
describe('when has a persisted analysis', function () {
beforeEach(function () {
this.layerB.set('source', 'a0');
this.collection.saveAnalysisForLayer(this.layerB);
});
it('should destroy analysis that existed for layer', function () {
expect(this.collection.pluck('node_id')).toEqual(['a1', 'c1']);
});
});
describe('when has no persisted analysis', function () {
beforeEach(function () {
this.layerX.set('source', 'a0');
this.collection.saveAnalysisForLayer(this.layerX);
});
it('should not create any analysis since its already persisted for its owning layer', function () {
expect(this.collection.pluck('node_id')).toEqual(['a1', 'b1', 'c1']);
});
});
});
describe('when a node is added on a node', function () {
beforeEach(function () {
this.analysisDefinitionNodesCollection.add({
id: 'b2',
type: 'buffer',
params: {
source: {
id: 'b1'
}
}
});
this.layerB.set('source', 'b2');
this.collection.saveAnalysisForLayer(this.layerB);
});
it('should update affected analysis', function () {
expect(this.A.save).not.toHaveBeenCalled();
expect(this.B.save).toHaveBeenCalled();
expect(this.C.save).not.toHaveBeenCalled();
});
it('should updated analysis', function () {
expect(this.collection.pluck('node_id').sort()).toEqual(['a1', 'b2', 'c1'], 'should contain x1 in addition to the others');
});
});
describe('when given a layer with a node that is not yet saved', function () {
beforeEach(function () {
this.collection.saveAnalysisForLayer(this.layerX);
});
it('should save layer after creating node if given a layer', function () {
expect(this.collection.pluck('node_id').sort()).toEqual(['a1', 'b1', 'c1', 'x1'], 'should contain x1 in addition to the others');
});
});
});
describe('.newAnalysisForNode', function () {
beforeEach(function () {
this.nodeDefModel = new Backbone.Model();
this.JSON = {
id: 'x0',
type: 'source',
params: {
query: 'SELECT * FROM a_table'
}
};
spyOn(this.nodeDefModel, 'toJSON').and.returnValue(this.JSON);
this.model = this.collection.newAnalysisForNode(this.nodeDefModel);
});
it('should return a new model', function () {
expect(this.model).toBeDefined();
});
it('should point to the given node id', function () {
expect(this.model.get('node_id')).toEqual('x0');
});
});
});
@@ -0,0 +1,52 @@
var AssetModel = require('builder/data/asset-model');
xdescribe('builder/data/asset-model', function () {
beforeEach(function () {
this.model = new AssetModel();
});
describe('.isValid', function () {
it('should validate upload', function () {
this.model.set({ filename: 'filename.png', state: 'idle' });
expect(this.model.isValid()).toBeTruthy();
this.model.set({ filename: 'filename.png', state: 'error' });
expect(this.model.isValid()).toBeFalsy();
});
});
describe('.upload', function () {
it('should upload', function () {
this.model.set({ type: 'filename', filename: 'filename.png' });
this.model.upload();
expect(this.model.validationError.msg).toBeUndefined();
});
});
describe('.validate', function () {
it('should validate a regular filename', function () {
this.model.save({ type: 'filename', filename: 'filename.png' });
expect(this.model.validationError.msg).toBeUndefined();
});
it('should validate an invalid file', function () {
this.model.save({ type: 'filename', filename: 'filename.zip' });
expect(this.model.validationError.msg).toBe('Unfortunately this file extension is not allowed');
});
it('should validate a list of files', function () {
this.model.save({ type: 'filename', filename: ['filename.png', 'filename2.png'] });
expect(this.model.validationError.msg).toBe('Unfortunately only one file is allowed per upload');
});
it('should validate a valid url', function () {
this.model.save({ type: 'url', filename: 'http://www.carto.com/map.jpg' });
expect(this.model.validationError.msg).toBeUndefined();
});
it('should validate an invalid url', function () {
this.model.save({ type: 'url', filename: 'this-is-not-a-url' });
expect(this.model.validationError.msg).toBe('Unfortunately the URL provided is not valid');
});
});
});
@@ -0,0 +1,23 @@
var Backbone = require('backbone');
var AssetsCollection = require('builder/data/assets-collection');
describe('data/assets-collection', function () {
beforeEach(function () {
var configModel = new Backbone.Model({
base_url: 'a_url'
});
configModel.urlVersion = function () { return 'v808'; };
this.collection = new AssetsCollection(null, {
configModel: configModel,
userModel: new Backbone.Model({
id: 707
})
});
});
it('should form url properly', function () {
var url = this.collection.url();
expect(url).toBe('a_url/api/v808/users/707/assets');
});
});
@@ -0,0 +1,201 @@
var NetworkInterceptor = require('builder/data/backbone/network-interceptors/interceptor').NetworkResponseInterceptor;
var $ = require('jquery');
describe('data/backbone/network-interceptors/interceptor', function () {
var interceptor;
beforeEach(function () {
jasmine.Ajax.install();
interceptor = new NetworkInterceptor();
});
afterEach(function () {
interceptor.stop();
jasmine.Ajax.uninstall();
});
describe('.start', function () {
it('should monkey-patch jQuery.ajax', function () {
var originalAjax = $.ajax;
interceptor.start();
expect(interceptor._originalAjax).toBe(originalAjax);
expect($.ajax).toBe(interceptor._onRequestStart);
});
});
describe('.addURLPattern', function () {
it('should add URL pattern to intercept', function () {
interceptor.addURLPattern('/api/test-url');
expect(interceptor._urlPatterns).toContain('/api/test-url');
});
});
describe('.addSuccessInterceptor', function () {
it('should add success interceptor function', function () {
var interceptorFn = function () {};
interceptor.addSuccessInterceptor(interceptorFn);
expect(interceptor._successInterceptors).toContain(interceptorFn);
});
});
describe('.addErrorInterceptor', function () {
it('should add error interceptor function', function () {
var interceptorFn = function () {};
interceptor.addErrorInterceptor(interceptorFn);
expect(interceptor._errorInterceptors).toContain(interceptorFn);
});
});
describe('._shouldListenToRequest', function () {
it('should return true if URL matches any patterns', function () {
interceptor.addURLPattern('/api/test');
expect(interceptor._shouldListenToRequest('/api/test')).toBe(true);
});
it('should return false if URL doesn\'t match any patterns', function () {
interceptor.addURLPattern('/api/non-matching-url');
expect(interceptor._shouldListenToRequest('/api/test')).toBe(false);
});
it('should return false if there aren\'t any patterns registered', function () {
expect(interceptor._shouldListenToRequest('/api/test')).toBe(false);
});
});
describe('._onRequestStart', function () {
var originalAjax;
beforeEach(function () {
originalAjax = interceptor._originalAjax;
spyOn(interceptor, '_originalAjax').and.callThrough();
});
afterEach(function () {
interceptor._originalAjax = originalAjax;
});
it('should fallback to original $.ajax if request should not be intercepted', function () {
var requestOptions = {
url: 'http://localhost:3000/api/test'
};
interceptor._onRequestStart(requestOptions);
var ajaxCall = interceptor._originalAjax.calls.mostRecent();
var requestOptionsInCall = ajaxCall.args[0];
expect(interceptor._originalAjax).toHaveBeenCalledWith(requestOptions);
expect(requestOptionsInCall.error).toBeUndefined();
expect(requestOptionsInCall.success).toBeUndefined();
});
it('should add our callbacks in request options', function () {
var requestOptions = {
url: 'http://localhost:3000/api/test',
success: jasmine.createSpy('success'),
error: jasmine.createSpy('error')
};
interceptor._onRequestStart(requestOptions);
var ajaxCall = interceptor._originalAjax.calls.mostRecent();
var requestOptionsInCall = ajaxCall.args[0];
expect(interceptor._originalAjax).toHaveBeenCalledWith(requestOptions);
expect(requestOptionsInCall.error).toBeDefined();
expect(requestOptionsInCall.success).toBeDefined();
});
it('should call success callback and apply interceptors when request is successful', function () {
var successCallback = jasmine.createSpy('success');
spyOn(interceptor, '_applySuccessInterceptors');
interceptor.addURLPattern('/api/test');
interceptor.start();
$.get(
'http://localhost:3000/api/test',
successCallback
);
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'text/plain',
responseText: 'awesome response'
});
expect(successCallback).toHaveBeenCalled();
expect(interceptor._applySuccessInterceptors).toHaveBeenCalled();
});
it('should call error callback and apply interceptors when request fails', function () {
var errorSpy = jasmine.createSpy('error');
spyOn(interceptor, '_applyErrorInterceptors');
interceptor.addURLPattern('/api/test');
interceptor.start();
interceptor._onRequestStart('http://localhost:3000/api/test', {
error: errorSpy
});
jasmine.Ajax.requests.mostRecent().respondWith({
status: 403
});
expect(errorSpy).toHaveBeenCalled();
expect(interceptor._applyErrorInterceptors).toHaveBeenCalled();
});
it('should call ajax with no interception added if url is string and options do not have error and success callback', function () {
var url = 'http://cdb.localhost.lan:3000/api/v3/me';
var options = {
data: 'something',
dataType: 'json',
contentType: 'application/json; charset=utf-8',
method: 'PUT'
};
interceptor._onRequestStart(url, options);
expect(interceptor._originalAjax).toHaveBeenCalledWith(url, options);
});
});
describe('._applyErrorInterceptors', function () {
it('should apply error interceptors if any', function () {
var xhr = 'xhr';
var textStatus = 'error';
var errorThrown = 'errorThrown';
var errorInterceptorFn = jasmine.createSpy('errorInterceptorFn');
interceptor.addErrorInterceptor(errorInterceptorFn);
interceptor._applyErrorInterceptors(xhr, textStatus, errorThrown);
expect(errorInterceptorFn).toHaveBeenCalledWith(xhr, textStatus, errorThrown);
});
});
describe('._applySuccessInterceptors', function () {
it('should apply success interceptors if any', function () {
var response = {};
var textStatus = 'success';
var xhr = 'xhr';
var successInterceptorFn = jasmine.createSpy('successInterceptorFn');
interceptor.addSuccessInterceptor(successInterceptorFn);
interceptor._applySuccessInterceptors(response, textStatus, xhr);
expect(successInterceptorFn).toHaveBeenCalledWith(response, textStatus, xhr);
});
});
});
@@ -0,0 +1,88 @@
var ForbiddenAction = require('builder/data/backbone/network-interceptors/interceptors/forbidden-403');
var URLFunctions = require('builder/data/backbone/network-interceptors/interceptors/forbidden-403').URLFunctions;
describe('data/backbone/network-interceptors/interceptors/forbidden-403', function () {
var action;
beforeEach(function () {
action = ForbiddenAction();
spyOn(URLFunctions, 'redirectTo');
spyOn(URLFunctions, 'getRedirectURL').and.callThrough();
});
it('should do nothing if error status is not 403', function () {
action({ status: 404 });
expect(URLFunctions.redirectTo).not.toHaveBeenCalled();
});
it('should do nothing if error is not within defined errors', function () {
action({
status: 403,
responseJSON: {
error: 'unknown error'
}
});
expect(URLFunctions.redirectTo).not.toHaveBeenCalled();
});
it('should redirect to login if error response contains `session_expired`', function () {
action({
status: 403,
responseJSON: {
error: 'session_expired'
}
});
expect(URLFunctions.redirectTo).toHaveBeenCalledWith(window.location.origin + '/login?error=session_expired');
});
it('should redirect to multifactor_authentication if error response contains `mfa_required`', function () {
action({
status: 403,
responseJSON: {
error: 'mfa_required'
}
});
expect(URLFunctions.redirectTo).toHaveBeenCalledWith(window.location.origin + '/multifactor_authentication');
});
it('should redirect to maintenance_mode if error response contains `maintenance_mode`', function () {
action({
status: 403,
responseJSON: {
error: 'maintenance_mode'
}
});
expect(URLFunctions.redirectTo).toHaveBeenCalledWith(window.location.origin + '/maintenance_mode');
});
it('should redirect to lockout if error response contains `lockout`', function () {
action({
status: 403,
responseJSON: {
error: 'lockout'
}
});
expect(URLFunctions.redirectTo).toHaveBeenCalledWith(window.location.origin + '/lockout');
});
it('should call getRedirectURL with username if provided', function () {
var action = ForbiddenAction('username');
action({
status: 403,
responseJSON: {
error: 'session_expired'
}
});
expect(URLFunctions.getRedirectURL).toHaveBeenCalledWith(window.location.origin, '/login?error=session_expired', 'username');
});
describe('.getRedirectURL', function () {
it('should return URL with /login with the current URL if no username is provided', function () {
expect(URLFunctions.getRedirectURL('http://test.carto.com', '/login?error=session_expired')).toBe('http://test.carto.com/login?error=session_expired');
});
it('should return current URL with username replaced if username is provided', function () {
expect(URLFunctions.getRedirectURL('http://test-user.carto.com', '/login?error=session_expired', 'username')).toBe('http://username.carto.com/login?error=session_expired');
});
});
});
@@ -0,0 +1,60 @@
var Backbone = require('backbone');
var ImportModelPoller = require('builder/data/background-importer/import-model-poller.js');
describe('ImportModelPoller', function () {
beforeEach(function () {
var Model = Backbone.Model.extend({
url: '/hello'
});
this.model = new Model();
this.model.sync = function (a, b, opts) {
opts.success();
};
spyOn(this.model, 'fetch').and.callThrough();
this.poller = new ImportModelPoller(this.model);
jasmine.clock().install();
});
afterEach(function () {
jasmine.clock().uninstall();
});
it('should stop polling when geocoding has failed', function () {
this.model.set('state', 'failure');
this.poller.start();
jasmine.clock().tick(4000);
expect(this.model.fetch.calls.count()).toBe(1);
});
it('should stop polling when geocoding has completed', function () {
this.model.set('state', 'complete');
this.poller.start();
jasmine.clock().tick(4000);
expect(this.model.fetch.calls.count()).toBe(1);
});
it('should increment the interval after a number of requests', function () {
this.poller.start();
// 2000 is the interval period and 30 the number of requests before
// the interval is incremented.
jasmine.clock().tick(2000 * 30);
// 30 requests have been made
expect(this.model.fetch.calls.count()).toBe(30);
// Interval has been incremented to 2000 * 2.5
jasmine.clock().tick(2000 * 2.5 + 1);
// Only one more request has been made (interval has been incremented)
expect(this.model.fetch.calls.count()).toBe(31);
});
});
@@ -0,0 +1,64 @@
var ConfigModel = require('builder/data/config-model');
var ImportModel = require('builder/data/background-importer/import-model.js');
describe('common/background-polling/import-model', function () {
beforeEach(function () {
this.configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.model = new ImportModel(null, {
configModel: this.configModel
});
spyOn(this.model, 'bind').and.callThrough();
});
it('should generate the right URL', function () {
expect(this.model.url()).toBe('/u/pepe/api/v1/imports');
});
it('should have several change binds', function () {
this.model._initBinds();
var args_0 = this.model.bind.calls.argsFor(0);
expect(args_0[0]).toEqual('change:item_queue_id');
});
it('should create a sync import when interval is greater than 0', function () {
spyOn(this.model, '_createSyncImport');
spyOn(this.model, '_createRegularImport');
this.model.createImport({ interval: 10 });
expect(this.model._createSyncImport).toHaveBeenCalled();
expect(this.model._createRegularImport).not.toHaveBeenCalled();
});
it('should create a sync import when there\'s no interval', function () {
spyOn(this.model, '_createSyncImport');
spyOn(this.model, '_createRegularImport');
this.model.createImport({ interval: null });
expect(this.model._createSyncImport).toHaveBeenCalled();
expect(this.model._createRegularImport).not.toHaveBeenCalled();
});
it('should create a regular import when interval is 0', function () {
spyOn(this.model, '_createSyncImport');
spyOn(this.model, '_createRegularImport');
this.model.createImport({ interval: 0 });
expect(this.model._createRegularImport).toHaveBeenCalled();
expect(this.model._createSyncImport).not.toHaveBeenCalled();
});
it('should stop polling when import post fails', function () {
this.model.sync = function (type, mdl, opts) {
opts.error();
};
spyOn(this.model, 'pollCheck');
this.model.createImport({ interval: 0 });
expect(this.model.get('state')).toBe('failure');
expect(this.model.pollCheck).not.toHaveBeenCalled();
});
it('should start polling when item_queue_id is set', function () {
spyOn(this.model, 'pollCheck');
this.model.set('item_queue_id', 'vamos-neno');
expect(this.model.pollCheck).toHaveBeenCalled();
});
});
@@ -0,0 +1,53 @@
var ImportsCollection = require('builder/data/background-importer/background-importer-imports-collection.js');
var ImportsModel = require('builder/data/background-importer/imports-model.js');
var UserModel = require('builder/data/user-model');
var ConfigModel = require('builder/data/config-model');
describe('common/background-polling/imports-collection', function () {
beforeEach(function () {
this.userModel = new UserModel({
username: 'pepe',
actions: {
private_tables: true
}
}, {
configModel: 'c'
});
this.configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.collection = new ImportsCollection(undefined, {
userModel: this.userModel,
configModel: this.configModel
});
});
it('should generate the right URL', function () {
expect(this.collection.url()).toBe('/u/pepe/api/v1/imports');
});
describe('.failedItems', function () {
it('should return the failed items', function () {
expect(this.collection.failedItems()).toEqual([]);
var importModel = new ImportsModel({}, {
userModel: this.userModel,
configModel: {}
});
var importModel2 = new ImportsModel({
state: 'failure',
step: 'import'
}, {
userModel: this.userModel,
configModel: {}
});
// Add three of which one is failed
this.collection.reset([importModel, importModel2]);
expect(this.collection.failedItems().length).toEqual(1);
});
});
});
@@ -0,0 +1,255 @@
var ImportsModel = require('builder/data/background-importer/imports-model.js');
var UploadModel = require('builder/data/upload-model.js');
var UserModel = require('builder/data/user-model');
var ConfigModel = require('builder/data/config-model');
describe('common/background-polling/imports-model', function () {
beforeEach(function () {
this.userModel = new UserModel({
username: 'pepe',
actions: {
private_tables: true
}
}, {
configModel: 'c'
});
this.configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.model = new ImportsModel({}, {
userModel: this.userModel,
configModel: this.configModel
});
// No jasmine spy objects
this.model._importModel.createImport = function () {};
this.model._importModel.pollCheck = function () {};
});
it('should start in upload step', function () {
expect(this.model.get('step')).toBe('upload');
});
it('should change state when upload or import state change', function () {
this.model._uploadModel.set('state', 'uploading');
expect(this.model.get('state')).toBe('uploading');
expect(this.model.get('step')).toBe('upload');
this.model._uploadModel.set({
state: 'uploaded',
item_queue_id: 'vamos-neno'
});
expect(this.model.get('state')).toBe('uploaded');
expect(this.model.get('id')).toBe('vamos-neno');
expect(this.model.get('step')).toBe('import');
this.model._importModel.set('state', 'zipping');
expect(this.model.get('state')).toBe('zipping');
});
it('should return error info', function () {
expect(this.model.getError().title).toBeDefined();
expect(this.model.getError().what_about).toBeDefined();
expect(this.model.getError().error_code).toBeDefined();
});
it('should return is import has failed', function () {
expect(this.model.hasFailed()).toBeFalsy();
this.model._uploadModel.set('state', 'error');
expect(this.model.hasFailed()).toBeTruthy();
this.model._uploadModel.set('state', 'upgraded');
expect(this.model.hasFailed()).toBeFalsy();
this.model.set('id', 'vamos-neno');
this.model._importModel.set('state', 'failure');
expect(this.model.hasFailed()).toBeTruthy();
this.model._importModel.set('state', 'error');
expect(this.model.hasFailed()).toBeFalsy();
});
it('should return is import has been completed', function () {
expect(this.model.hasCompleted()).toBeFalsy();
this.model.set('id', 'vamos-neno');
this.model._importModel.set('state', 'complete');
expect(this.model.hasCompleted()).toBeTruthy();
expect(this.model.get('state')).toBe('complete');
});
it('should be able to return import info', function () {
this.model._importModel.createImport = function () {};
this.model.set('id', 'vamos-neno');
expect(this.model.get('import')).not.toBe(undefined);
expect(this.model.get('import').item_queue_id).toBe('vamos-neno');
});
it('should be able to return upload info', function () {
this.model._importModel.createImport = function () {};
this.model._uploadModel.set({ type: 'url', value: 'http://fakeurl.es/j.csv' });
expect(this.model.get('upload')).not.toBe(undefined);
expect(this.model.get('upload').type).toBe('url');
expect(this.model.get('upload').value).toBe('http://fakeurl.es/j.csv');
});
it('should start importing if model id is set', function () {
spyOn(this.model._importModel, 'pollCheck');
this.model.set('id', 'vamos-neno');
expect(this.model._importModel.pollCheck).toHaveBeenCalled();
});
it('should stop upload and import when error is set', function () {
spyOn(this.model, 'stopUpload');
spyOn(this.model, 'stopImport');
this.model.setError({ error_code: 1111 });
expect(this.model.stopUpload).toHaveBeenCalled();
expect(this.model.stopImport).toHaveBeenCalled();
expect(this.model.get('state')).toBe('error');
});
describe('checkStatus', function () {
beforeEach(function () {
spyOn(this.model._importModel, 'set');
spyOn(this.model._importModel, 'createImport');
spyOn(this.model._uploadModel, 'upload');
});
it('should stop process when upload is not valid', function () {
this.model._checkStatus();
expect(this.model._importModel.set).not.toHaveBeenCalled();
expect(this.model._importModel.createImport).not.toHaveBeenCalled();
expect(this.model._uploadModel.upload).not.toHaveBeenCalled();
});
it('should not stop process when upload is not valid but there is an id already set', function () {
this.model._uploadModel.set({
type: 'url',
value: ''
});
this.model.set('id', 'idd');
this.model._checkStatus();
expect(this.model._importModel.set).toHaveBeenCalled();
});
it('should start upload when type is file', function () {
this.model._uploadModel.set({
type: 'file',
value: { name: 'fake.csv', size: 1000 }
});
this.model._checkStatus();
expect(this.model._uploadModel.upload).toHaveBeenCalled();
});
it('should create an import when type is not file and there is no id defined', function () {
this.model._uploadModel.set({
type: 'url',
value: 'http://paco.com'
});
this.model._checkStatus();
expect(this.model._importModel.createImport).toHaveBeenCalled();
});
});
describe('upload model', function () {
beforeEach(function () {
this.userModel = new UserModel({
username: 'pepe',
actions: {
private_tables: true
}
}, {
configModel: 'c'
});
this._uploadModel = new UploadModel({
create_vis: false
}, {
userModel: this.userModel,
configModel: this.configModel
});
spyOn(this._uploadModel, 'bind').and.callThrough();
});
it('should have progress, change:value and error/invalid binds', function () {
this._uploadModel._initBinds();
var args_0 = this._uploadModel.bind.calls.argsFor(0);
expect(args_0[0]).toEqual('progress');
var args_1 = this._uploadModel.bind.calls.argsFor(1);
expect(args_1[0]).toEqual('change:value');
var args_2 = this._uploadModel.bind.calls.argsFor(2);
expect(args_2[0]).toEqual('error invalid');
});
it('should set uploading state when progress change', function () {
this._uploadModel.set({ value: 'http://paco.csv', type: 'url' });
this._uploadModel.trigger('progress', 20);
expect(this._uploadModel.get('state')).toBe('uploading');
});
it('should have a fileAttribute set', function () {
expect(this._uploadModel.fileAttribute).toBe('filename');
});
it('should validate the model when changes', function () {
spyOn(this._uploadModel, '_validate');
this._uploadModel.set({
value: 'http://marca.com/paco.jjjjj',
type: 'url'
});
expect(this._uploadModel._validate).toHaveBeenCalled();
});
it('should add error code when size validation fails', function () {
this._uploadModel._userModel.set('remaining_byte_quota', 1);
this._uploadModel.set({
type: 'file',
value: { name: 'fake.csv', size: 1000 }
});
// expect(this._uploadModel.get('state')).toBe('error');
// expect(this._uploadModel.get('error_code')).toBe(8001);
this._uploadModel.set({
type: 'remote',
remote_visualization_id: 'iddd',
size: 1000
});
// expect(this._uploadModel.get('state')).toBe('error');
// expect(this._uploadModel.get('error_code')).toBe(8001);
this._uploadModel.set({
type: 'url',
value: 'heyheyhey.org'
});
// expect(this._uploadModel.get('state')).toBe('error');
expect(this._uploadModel.get('error_code')).toBe(undefined);
});
it('should raise an error when file name is not provided', function () {
this._uploadModel.set({
type: 'file',
value: { }
});
expect(this._uploadModel.get('state')).toBe('idle');
expect(this._uploadModel.validate(this._uploadModel.attributes).msg).toEqual('data.upload-model.file-defined');
});
describe('.setFresh', function () {
beforeEach(function () {
spyOn(this._uploadModel, 'set');
this._uploadModel.setFresh({
foo: 'bar',
create_vis: true
});
});
it('should set the given dataset', function () {
expect(this._uploadModel.set).toHaveBeenCalled();
expect(this._uploadModel.set.calls.argsFor(0)[0]).toEqual(jasmine.objectContaining({ foo: 'bar' }));
});
it('should omit the create_vis value since set in constructor', function () {
expect(this._uploadModel.set.calls.argsFor(0)[0]).not.toEqual(jasmine.objectContaining({ create_vis: true }));
});
});
});
});
@@ -0,0 +1,245 @@
var _ = require('underscore');
var camshaftReference = require('builder/data/camshaft-reference');
describe('builder/data/camshaft-reference', function () {
describe('.getSourceNamesForAnalysisType', function () {
it('should return the source names for a given analyses type', function () {
expect(camshaftReference.getSourceNamesForAnalysisType('source')).toEqual([]);
expect(camshaftReference.getSourceNamesForAnalysisType('intersection')).toEqual(['source', 'target']);
expect(camshaftReference.getSourceNamesForAnalysisType('trade-area')).toEqual(['source']);
});
});
describe('.paramsForType', function () {
it('should return the params for given type', function () {
expect(camshaftReference.paramsForType('buffer')).toEqual({
source: jasmine.any(Object),
radius: jasmine.any(Object),
isolines: jasmine.any(Object),
dissolved: jasmine.any(Object)
});
});
it('should throw error if there is no params for given type', function () {
expect(function () { camshaftReference.paramsForType('foobar'); }).toThrowError(/type: foobar/);
expect(function () { camshaftReference.paramsForType(); }).toThrowError(/type: undefined/);
});
});
describe('.parse', function () {
beforeEach(function () {
this.validFormAttrs = {
id: 'a1',
source: ' a0 ',
type: 'buffer',
radius: '100',
isolines: '2',
dissolved: 'false'
};
});
it('should return empty source', function () {
expect(camshaftReference.parse(_.omit(this.validFormAttrs, 'source'))).toEqual({
id: 'a1',
source: '',
type: 'buffer',
radius: 100,
isolines: 2,
dissolved: false
});
});
it('should parse given form attrs string values to the want types', function () {
expect(camshaftReference.parse(this.validFormAttrs)).toEqual({
id: 'a1',
source: 'a0',
type: 'buffer',
radius: 100,
isolines: 2,
dissolved: false
});
});
it('should not require any absent optional values', function () {
expect(camshaftReference.parse(_.omit(this.validFormAttrs, 'isolines', 'dissolved'))).toEqual({
id: 'a1',
source: 'a0',
type: 'buffer',
radius: 100
});
});
it('should return normalized values for invalid types', function () {
expect(camshaftReference.parse({
id: 'a1',
source: null,
type: 'buffer',
radius: {},
isolines: {},
dissolved: 'nope'
})).toEqual({
id: 'a1',
source: '',
type: 'buffer',
radius: NaN,
isolines: NaN,
dissolved: false
});
});
it('should trim string values', function () {
var validFormAttrs = {
id: 'a1',
source: 'a0',
type: 'population-in-area',
final_column: ' col '
};
expect(camshaftReference.parse(validFormAttrs)).toEqual({
id: 'a1',
source: 'a0',
type: 'population-in-area',
final_column: 'col'
});
expect(camshaftReference.parse(_.omit(validFormAttrs, 'final_column'))).toEqual({
id: 'a1',
source: 'a0',
type: 'population-in-area',
final_column: null
});
});
it('should check enum valid', function () {
var validFormAttrs = {
id: 'a1',
type: 'aggregate-intersection',
source: 'a0',
target: 'b1',
aggregate_function: 'count',
aggregate_column: 'col'
};
expect(camshaftReference.parse(validFormAttrs)).toEqual({
id: 'a1',
type: 'aggregate-intersection',
source: 'a0',
target: 'b1',
aggregate_function: 'count',
aggregate_column: 'col'
});
expect(camshaftReference.parse(_.omit(validFormAttrs, 'aggregate_function'))).toEqual({
id: 'a1',
type: 'aggregate-intersection',
source: 'a0',
target: 'b1',
aggregate_function: null,
aggregate_column: 'col'
});
});
});
describe('.validate', function () {
describe('when given a type', function () {
describe('with sources', function () {
beforeEach(function () {
this.validFormAttrs = {
id: 'a1',
type: 'intersection',
source: 'a0',
target: 'b1'
};
});
it('should return nothing for valid values', function () {
expect(camshaftReference.validate(this.validFormAttrs)).toBeUndefined();
});
it('should return error for missing a source', function () {
expect(camshaftReference.validate(_.omit(this.validFormAttrs, 'source'))).toEqual({source: jasmine.any(String)});
expect(camshaftReference.validate(_.omit(this.validFormAttrs, 'target'))).toEqual({target: jasmine.any(String)});
});
});
describe('with numbers', function () {
beforeEach(function () {
this.validFormAttrs = {
id: 'a1',
source: 'a0',
type: 'buffer',
radius: '1'
};
});
it('should return nothing for valid values', function () {
expect(camshaftReference.validate(this.validFormAttrs)).toBeUndefined();
});
it('should return errors if missing required param', function () {
expect(camshaftReference.validate(_.omit(this.validFormAttrs, 'radius'))).toEqual({
radius: jasmine.any(String)
});
});
});
describe('with enums', function () {
beforeEach(function () {
this.validFormAttrs = {
id: 'a1',
type: 'aggregate-intersection',
source: 'a0',
target: 'b1',
aggregate_function: 'count',
aggregate_column: 'col'
};
});
it('should return nothing for valid values', function () {
expect(camshaftReference.validate(this.validFormAttrs)).toBeUndefined();
});
it('should return error if enum is missing', function () {
expect(camshaftReference.validate(_.omit(this.validFormAttrs, 'aggregate_function'))).toEqual({
aggregate_function: jasmine.any(String)
});
});
it('should return error if enum is invalid', function () {
this.validFormAttrs.aggregate_function = 'FOOBAR';
expect(camshaftReference.validate(this.validFormAttrs)).toEqual({
aggregate_function: jasmine.any(String)
});
});
});
});
});
describe('.isValidInputGeometryForType', function () {
it('should return true if it is', function () {
expect(camshaftReference.isValidInputGeometryForType('point', 'buffer')).toBe(true);
expect(camshaftReference.isValidInputGeometryForType('polygon', 'buffer')).toBe(true);
expect(camshaftReference.isValidInputGeometryForType('line', 'buffer')).toBe(true);
expect(camshaftReference.isValidInputGeometryForType('point', 'kmeans')).toBe(true);
expect(camshaftReference.isValidInputGeometryForType('polygon', 'kmeans')).toBe(false);
expect(camshaftReference.isValidInputGeometryForType('line', 'kmeans')).toBe(false);
});
it('should throw an error if type is not valid', function () {
expect(function () { camshaftReference.isValidInputGeometryForType('point', 'foobar'); }).toThrowError(/ foobar/);
});
});
describe('.getValidInputGeometriesForType', function () {
it('should return the geometries for given type and source name', function () {
expect(camshaftReference.getValidInputGeometriesForType('buffer')).toEqual(['*']);
expect(camshaftReference.getValidInputGeometriesForType('data-observatory-measure')).toEqual(['point', 'polygon']);
expect(camshaftReference.getValidInputGeometriesForType('trade-area')).toEqual(['point']);
});
it('should throw an error if type is not valid', function () {
expect(function () { camshaftReference.getValidInputGeometriesForType('foobar'); }).toThrowError(/ foobar/);
expect(function () { camshaftReference.getValidInputGeometriesForType(); }).toThrowError(/ undefined/);
expect(function () { camshaftReference.getValidInputGeometriesForType(null); }).toThrowError(/ null/);
});
});
});
@@ -0,0 +1,29 @@
var ConfigModel = require('builder/data/config-model');
describe('data/config-model', function () {
beforeEach(function () {
this.model = new ConfigModel();
});
describe('.urlVersion', function () {
beforeEach(function () {
this.version = this.model.urlVersion();
});
it('should return v1 by default', function () {
expect(this.version).toEqual('v1');
});
});
describe('.getSqlApiUrl', function () {
beforeEach(function () {
this.url = this.model.getSqlApiUrl();
});
it('should return a API url', function () {
expect(this.url).toEqual(jasmine.any(String));
expect(this.url).toContain('/sql');
});
});
});
@@ -0,0 +1,83 @@
var CustomBaselayerModel = require('builder/data/custom-baselayer-model');
describe('data/custom-baselayer-model', function () {
describe('.validateTemplateURL', function () {
beforeEach(function () {
var self = this;
this.img = jasmine.createSpy('Image');
spyOn(window, 'Image').and.callFake(function () {
return self.img;
});
this.layer = {
visible: true,
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 1',
tms: false,
className: 'httpsstamentilessasslfastlynetwatercolorzxyjpg',
id: 'custom-basemap-1',
selected: false,
order: 1
};
this.model = new CustomBaselayerModel(this.layer);
this.successSpy = jasmine.createSpy('success');
this.errorSpy = jasmine.createSpy('error');
this.model.validateTemplateURL({
success: this.successSpy,
error: this.errorSpy
});
});
it('should check a base tile', function () {
expect(this.img.src).toMatch('https:\/\/stamen-tiles-[a-d]\.a');
expect(this.img.src).toContain('a.ssl.fastly.net/watercolor/0/0/0.jpg');
});
describe('when succeeds to validate template URL', function () {
beforeEach(function () {
this.img.onload();
});
it('should call success callback', function () {
expect(this.successSpy).toHaveBeenCalled();
expect(this.errorSpy).not.toHaveBeenCalled();
});
});
describe('when failed to validate template URL', function () {
beforeEach(function () {
this.img.onerror();
});
it('should call error callback', function () {
expect(this.successSpy).not.toHaveBeenCalled();
expect(this.errorSpy).toHaveBeenCalled();
});
});
describe('.toJSON', function () {
it('should return the original data', function () {
expect(this.model.toJSON()).toEqual({
options: {
visible: true,
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 1',
tms: false,
className: 'httpsstamentilessasslfastlynetwatercolorzxyjpg',
selected: false
},
kind: 'tiled',
order: 1,
id: 'custom-basemap-1'
});
});
});
});
});
@@ -0,0 +1,35 @@
var CustomBaselayersCollection = require('builder/data/custom-baselayers-collection');
var ConfigModel = require('builder/data/config-model');
describe('data/custom-baselayers-collection', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.customBaselayersCollection = new CustomBaselayersCollection([{
id: 'basemap-id-1',
options: {
urlTemplate: 'http://a.example.com/basemap/6/30/24.png'
}
}], {
configModel: configModel,
currentUserId: 'current-user-id'
});
});
describe('.getSelected', function () {
it('should return selected layer if any', function () {
expect(this.customBaselayersCollection.getSelected()).toBe(undefined);
this.customBaselayersCollection.at(0).set('selected', true);
expect(this.customBaselayersCollection.getSelected().get('id')).toBe('basemap-id-1');
});
});
describe('.hasCustomBaseLayer', function () {
it('should return layer with same classname if any', function () {
expect(this.customBaselayersCollection.hasCustomBaseLayer('httpcexamplecombasemap63024png')).toBe(false);
expect(this.customBaselayersCollection.hasCustomBaseLayer('httpaexamplecombasemap63024png')).toBe(true);
});
});
});
@@ -0,0 +1,91 @@
var Backbone = require('backbone');
var DataObservatoryColumnName = require('builder/data/data-observatory/column-name');
var AnalysisDefinitionNodeModel = require('builder/data/analysis-definition-node-model');
describe('data/data-observatory/column-name', function () {
beforeEach(function () {
var configModel = new Backbone.Model({
base_url: '/u/foo',
user_name: 'foo',
sql_api_template: 'foo',
api_key: 'foo'
});
this.nodeDefModel = new AnalysisDefinitionNodeModel({
id: 'a1',
type: 'data-observatory-multiple-measures',
final_column: 'foo',
source: 'a0'
}, {
configModel: configModel,
collection: new Backbone.Collection()
});
this.querySchemaModel = new Backbone.Model({
query: 'select * from wadus'
});
this.nodeDefModel.querySchemaModel = this.querySchemaModel;
this.columnName = new DataObservatoryColumnName({
configModel: configModel,
nodeDefModel: this.nodeDefModel
});
spyOn(this.columnName.SQL, 'execute').and.callFake(function (query, vars, params) {
params && params.success({
rows: [
{
obs_getmeta: [{
suggested_name: 'commuters_16_over_per_sq_km_2010_2014'
}]
}
]
});
});
this.successCallback = jasmine.createSpy('successCallback');
});
it('initial fetch state', function () {
expect(this.columnName.isFetching).toBe(false);
});
describe('fetch', function () {
it('fetch without numer_id', function () {
this.columnName.fetch({
success: this.successCallback
});
expect(this.successCallback).not.toHaveBeenCalled();
});
it('fetch with numer_id', function () {
this.columnName.fetch({
numer_id: 1,
success: this.successCallback
});
expect(this.successCallback).toHaveBeenCalledWith({
rows: [
{
obs_getmeta: [{
suggested_name: 'commuters_16_over_per_sq_km_2010_2014'
}]
}
]
});
});
});
it('buildQueryOptions', function () {
var options = this.columnName.buildQueryOptions({
key: 'foo'
});
expect(options).toEqual(jasmine.objectContaining({
metadata: "'[" + JSON.stringify({key: 'foo'}) + "]'",
query: 'select * from wadus'
}));
});
});
@@ -0,0 +1,79 @@
var Backbone = require('backbone');
var FilterListView = require('builder/components/form-components/editors/data-observatory-measurements/filters-list-view');
var FiltersCollection = require('builder/data/data-observatory/filters-collection');
var AnalysisDefinitionNodeModel = require('builder/data/analysis-definition-node-model');
describe('data/data-observatory/filter-collection', function () {
beforeEach(function () {
var configModel = new Backbone.Model({
base_url: '/u/foo',
user_name: 'foo',
sql_api_template: 'foo',
api_key: 'foo'
});
this.nodeDefModel = new AnalysisDefinitionNodeModel({
id: 'a1',
type: 'data-observatory-multiple-measures',
final_column: 'foo',
source: 'a0'
}, {
configModel: configModel,
collection: new Backbone.Collection()
});
this.querySchemaModel = new Backbone.Model({
query: 'select * from wadus'
});
this.nodeDefModel.querySchemaModel = this.querySchemaModel;
this.filtersCollection = new FiltersCollection([], {
configModel: configModel,
nodeDefModel: this.nodeDefModel
});
spyOn(this.filtersCollection.SQL, 'execute').and.callFake(function (query, vars, params) {
var payload = {
rows: [
{
subsection_id: 'subsection/tags.age_gender',
subsection_name: 'Age and Gender'
},
{
subsection_id: 'subsection/us.census.acs.segments',
subsection_name: 'US Population Segments'
},
{
subsection_id: 'subsection/tags.housing',
subsection_name: 'Housing'
},
{
subsection_id: 'subsection/us.zillow.indexes',
subsection_name: 'Zillow Home Value and Rental Indexes'
}
]
};
params && params.success(payload);
});
this.filtersCollection.fetch({
region: 'wadus'
});
this.view = new FilterListView({
filtersCollection: this.filtersCollection
});
this.view.render();
});
it('should render properly', function () {
expect(this.view._listView).toBeDefined();
expect(this.view.$('.js-back').length).toBe(1);
});
it('should not have any leaks', function () {
expect(this.view).toHaveNoLeaks();
});
});
@@ -0,0 +1,97 @@
var Backbone = require('backbone');
var MeasurementsCollection = require('builder/data/data-observatory/measurements-collection');
var AnalysisDefinitionNodeModel = require('builder/data/analysis-definition-node-model');
describe('data/data-observatory/measurements-collection', function () {
beforeEach(function () {
var configModel = new Backbone.Model({
base_url: '/u/foo',
user_name: 'foo',
sql_api_template: 'foo',
api_key: 'foo'
});
this.nodeDefModel = new AnalysisDefinitionNodeModel({
id: 'a1',
type: 'data-observatory-multiple-measures',
final_column: 'foo',
source: 'a0'
}, {
configModel: configModel,
collection: new Backbone.Collection()
});
this.querySchemaModel = new Backbone.Model({
query: 'select * from wadus'
});
this.nodeDefModel.querySchemaModel = this.querySchemaModel;
this.collection = new MeasurementsCollection([], {
configModel: configModel,
nodeDefModel: this.nodeDefModel
});
spyOn(this.collection.SQL, 'execute').and.callFake(function (query, vars, params) {
params && params.success({
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"}'
},
{
numer_id: 'us.census.acs.B19083001',
numer_name: 'Gini Index',
numer_tags: '{"subsection/tags.income": "Income", "section/tags.united_states": "United States", "license/tags.no-restrictions": "Unrestricted"}'
},
{
numer_id: 'us.census.acs.B01001002',
numer_name: 'Male Population',
numer_tags: '{"section/tags.united_states": "United States", "subsection/tags.age_gender": "Age and Gender", "license/tags.no-restrictions": "Unrestricted"}'
}
]
});
});
this.successCallback = jasmine.createSpy('successCallback');
this.errorCallback = jasmine.createSpy('errorCallback');
});
it('initial state', function () {
expect(this.collection.getState()).toBe('unfetched');
});
it('fetch', function () {
this.collection.fetch({
success: this.successCallback,
error: this.errorCallback
});
expect(this.successCallback).toHaveBeenCalled();
expect(this.collection.length).toBe(3);
expect(this.collection.getState()).toBe('fetched');
});
it('model', function () {
this.collection.fetch({
success: this.successCallback,
error: this.errorCallback
});
var m = this.collection.at(0);
expect(m.getValue()).toBe('us.zillow.AllHomes_Zhvi');
expect(m.getName()).toBe('Zillow Home Value Index for All homes');
expect(m.get('license')).toBe('Zillow Terms of Use for Aggregate Data');
});
it('getSelected', function () {
this.collection.fetch();
var m = this.collection.at(0);
this.collection.setSelected(m.getValue());
var selected = this.collection.getSelectedItem();
expect(selected.getValue()).toBe(m.getValue());
});
});
@@ -0,0 +1,113 @@
var Backbone = require('backbone');
var AnalysisDefinitionNodeModel = require('builder/data/analysis-definition-node-model');
var RegionsCollection = require('builder/data/data-observatory/regions-collection');
describe('data/data-observatory/regions-collection', function () {
beforeEach(function () {
var configModel = new Backbone.Model({
base_url: '/u/foo',
user_name: 'foo',
sql_api_template: 'foo',
api_key: 'foo'
});
this.nodeDefModel = new AnalysisDefinitionNodeModel({
id: 'a1',
type: 'data-observatory-multiple-measures',
final_column: 'foo',
source: 'a0'
}, {
configModel: configModel,
collection: new Backbone.Collection()
});
this.nodeDefModel.queryGeometryModel = new Backbone.Model();
this.nodeDefModel.queryGeometryModel.isFetched = function () { return true; };
this.querySchemaModel = new Backbone.Model({
query: 'select * from wadus'
});
var sourceNode = new Backbone.Model({
type: 'source',
table_name: 'wadus'
});
sourceNode.querySchemaModel = this.querySchemaModel;
this.collection = new RegionsCollection([], {
configModel: configModel,
nodeDefModel: this.nodeDefModel
});
spyOn(this.collection.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.successCallback = jasmine.createSpy('successCallback');
this.errorCallback = jasmine.createSpy('errorCallback');
});
it('initial state', function () {
expect(this.collection.getState()).toBe('unfetched');
});
it('format quotes', function () {
this.collection.fetch();
expect(this.collection.at(1).getName()).toBe('United States');
});
it('fetch', function () {
this.collection.fetch({
success: this.successCallback,
error: this.errorCallback
});
expect(this.successCallback).toHaveBeenCalled();
expect(this.collection.length).toBe(2);
expect(this.collection.getState()).toBe('fetched');
});
it('model', function () {
this.collection.fetch({
success: this.successCallback,
error: this.errorCallback
});
var m = this.collection.at(0);
expect(m.getValue()).toBe('section/tags.global');
expect(m.getName()).toBe('Global');
});
describe('setSelected', function () {
beforeEach(function () {
this.collection.fetch();
});
it('single value', function () {
this.collection.setSelected('section/tags.united_states');
expect(this.collection.at(1).get('selected')).toBe(true);
});
});
it('getSelected', function () {
this.collection.fetch();
this.collection.setSelected('section/tags.united_states');
var selected = this.collection.getSelectedItem();
expect(selected.get('selected')).toBe(true);
expect(selected.getValue()).toBe('section/tags.united_states');
expect(selected.getName()).toBe('United States');
});
});
@@ -0,0 +1,98 @@
var Backbone = require('backbone');
var TimeSpanCollection = require('builder/data/data-observatory/timespan-collection');
var AnalysisDefinitionNodeModel = require('builder/data/analysis-definition-node-model');
describe('data/data-observatory/timespan-collection', function () {
beforeEach(function () {
var configModel = new Backbone.Model({
base_url: '/u/foo',
user_name: 'foo',
sql_api_template: 'foo',
api_key: 'foo'
});
this.nodeDefModel = new AnalysisDefinitionNodeModel({
id: 'a1',
type: 'data-observatory-multiple-measures',
final_column: 'foo',
source: 'a0'
}, {
configModel: configModel,
collection: new Backbone.Collection()
});
this.querySchemaModel = new Backbone.Model({
query: 'select * from wadus'
});
this.nodeDefModel.querySchemaModel = this.querySchemaModel;
this.collection = new TimeSpanCollection([], {
configModel: configModel,
nodeDefModel: this.nodeDefModel
});
spyOn(this.collection.SQL, 'execute').and.callFake(function (query, vars, params) {
params && params.success({
rows: [
{
timespan_id: '2010',
timespan_name: '2010'
},
{
timespan_id: '2015',
timespan_name: '2015'
},
{
timespan_id: '2017',
timespan_name: '2017'
}
]
});
});
this.successCallback = jasmine.createSpy('successCallback');
this.errorCallback = jasmine.createSpy('errorCallback');
});
it('initial state', function () {
expect(this.collection.getState()).toBe('unfetched');
});
it('fetch', function () {
this.collection.fetch({
success: this.successCallback,
error: this.errorCallback
});
expect(this.successCallback).toHaveBeenCalled();
expect(this.collection.length).toBe(3);
expect(this.collection.getState()).toBe('fetched');
});
it('first timespan as default', function () {
this.collection.fetch({
success: this.successCallback,
error: this.errorCallback
});
this.collection.selectFirstAsDefault();
expect(this.collection.getSelectedItem().getValue()).toBe('2010');
});
it('should not select the first item if empty', function () {
this.collection.SQL.execute.and.callFake(function (query, vars, params) {
params && params.success({
rows: []
});
});
this.collection.fetch({
success: this.successCallback,
error: this.errorCallback
});
this.collection.selectFirstAsDefault();
expect(this.collection.getSelectedItem()).toBeUndefined();
});
});
@@ -0,0 +1,90 @@
var Backbone = require('backbone');
var $ = require('jquery');
var UserModel = require('builder/data/user-model');
var TableModel = require('builder/data/table-model');
var BackgroundPollingModel = require('builder/data/editor-background-polling-model');
var ImportsCollection = require('builder/data/background-importer/background-importer-imports-collection');
var UserActions = require('builder/data/user-actions');
describe('builder/data/editor-background-polling-model', function () {
beforeEach(function () {
var configModel = new Backbone.Model();
configModel.urlVersion = function () {};
spyOn($, 'ajax').and.callFake(function (req) {
var d = $.Deferred();
d.resolve();
return d.promise();
});
var userModel = new UserModel({
username: 'pepe'
}, {
configModel: configModel
});
this.importsCollection = new ImportsCollection(undefined, {
userModel: userModel,
configModel: configModel
});
spyOn(TableModel.prototype, 'fetch').and.callThrough();
this.userActions = UserActions({
userModel: {},
analysisDefinitionNodesCollection: {},
analysisDefinitionsCollection: {},
layerDefinitionsCollection: {},
widgetDefinitionsCollection: {}
});
spyOn(this.userActions, 'createLayerFromTable');
this.model = new BackgroundPollingModel({}, {
userModel: userModel,
configModel: configModel,
vis: new Backbone.Model({}),
importsCollection: this.importsCollection,
userActions: this.userActions
});
});
describe('when an imports completes', function () {
beforeEach(function () {
this.importsCollection.reset({});
this.importsModel = this.importsCollection.at(0);
this.importsModel.set({
step: 'import'
});
});
it('should fetch table data', function () {
this.importsModel._importModel.set({
state: 'complete',
table_name: 'tableName'
});
expect(TableModel.prototype.fetch).toHaveBeenCalled();
});
describe('when table is fetched', function () {
it('should trigger the "importCompleted" event', function () {
var callback = jasmine.createSpy('callback');
this.model.bind('importCompleted', callback);
this.importsModel._importModel.set({
state: 'complete',
table_name: 'tableName'
});
expect(callback).toHaveBeenCalledWith(this.importsModel, this.model);
});
it('should add a new layer definition to the collection', function () {
this.importsModel._importModel.set({
state: 'complete',
table_name: 'tableName'
});
expect(this.userActions.createLayerFromTable).toHaveBeenCalled();
});
});
});
});
@@ -0,0 +1,34 @@
var ConfigModel = require('builder/data/config-model');
var ExportMapDefinitionModel = require('builder/data/export-map-definition-model');
describe('data/export-map-definition-model', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/manolo'
});
this.model = new ExportMapDefinitionModel({
visualization_id: 'v-123'
}, {
configModel: configModel
});
spyOn(this.model, 'save');
spyOn(this.model, '_interrupt');
});
it('should request an export', function () {
this.model.requestExport();
expect(this.model.save).toHaveBeenCalled();
});
it('should cancel an export', function () {
this.model.cancelExport();
expect(this.model._interrupt).toHaveBeenCalled();
});
it('should have correct url', function () {
expect(this.model.urlRoot()).toBe('/u/manolo/api/v3/visualization_exports');
});
});
@@ -0,0 +1,325 @@
var _ = require('underscore');
var Backbone = require('backbone');
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
var ConfigModel = require('builder/data/config-model');
var UserModel = require('builder/data/user-model');
var FeatureDefinitionModel = require('builder/data/feature-definition-model');
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
describe('data/feature-definition-model', function () {
var userModel;
var configModel;
var layerDefinitionsCollection;
var layerDefinitionModel;
beforeEach(function () {
spyOn(_, 'debounce').and.callFake(function (func) {
return function () {
func.apply(this, arguments);
};
});
configModel = new ConfigModel({
base_url: '/u/pepe'
});
userModel = new UserModel({}, {
configModel: configModel
});
var analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
configModel: configModel,
userModel: userModel
});
layerDefinitionsCollection = new LayerDefinitionsCollection(null, {
configModel: configModel,
userModel: userModel,
analysisDefinitionNodesCollection: analysisDefinitionNodesCollection,
mapId: 'm-123',
stateDefinitionModel: {}
});
layerDefinitionsCollection.add({
id: 'l-1',
kind: 'carto',
options: {
table_name: 'foo'
}
});
layerDefinitionModel = layerDefinitionsCollection.at(0);
layerDefinitionModel.getColumnNamesFromSchema = function () { return ['name', 'country', 'the_geom']; };
this.feature = new FeatureDefinitionModel({
cartodb_id: '12345',
name: 'Madrid',
country: 'Spain'
}, {
configModel: configModel,
layerDefinitionModel: layerDefinitionModel,
userModel: userModel
});
this.fakeQueryRowModel = jasmine.createSpyObj('fakeQueryRowModel', ['save', 'fetch', 'destroy']);
spyOn(this.feature, '_getQueryRowModel').and.returnValue(this.fakeQueryRowModel);
});
it('should create _changesHistory at the beginning', function () {
expect(this.feature._changesHistory).toEqual([]);
});
describe('change bind', function () {
it('should set at the beginning if model is new', function () {
spyOn(FeatureDefinitionModel.prototype, '_bindChangeEvent');
var feature = new FeatureDefinitionModel({}, {
configModel: configModel,
layerDefinitionModel: layerDefinitionModel,
userModel: userModel
});
expect(FeatureDefinitionModel.prototype._bindChangeEvent).toHaveBeenCalled();
expect(feature.isNew()).toBeTruthy();
});
it('should not be set at the beginning if model is not new', function () {
spyOn(FeatureDefinitionModel.prototype, '_bindChangeEvent');
var feature = new FeatureDefinitionModel({
cartodb_id: 1
}, {
configModel: configModel,
layerDefinitionModel: layerDefinitionModel,
userModel: userModel
});
expect(FeatureDefinitionModel.prototype._bindChangeEvent).not.toHaveBeenCalled();
expect(feature.isNew()).toBeFalsy();
});
});
describe('.fetch', function () {
describe('on success', function () {
beforeEach(function () {
this.fakeQueryRowModel.fetch.and.callFake(function (options) {
options && options.success(
new Backbone.Model({
cartodb_id: '3',
the_geom: '{whatever}'
})
);
});
});
it('should fetch from query row model', function () {
this.feature.fetch();
expect(this.fakeQueryRowModel.fetch).toHaveBeenCalled();
});
it('should unbind change binding', function () {
spyOn(this.feature, '_unbindChangeEvent');
this.feature.fetch();
expect(this.feature._unbindChangeEvent).toHaveBeenCalled();
});
it('should set properties after fetch', function () {
expect(this.feature.get('cartodb_id')).toBe('12345');
this.feature.fetch();
expect(this.feature.get('cartodb_id')).toBe('3');
});
it('should listen changes after fetching successfully', function () {
spyOn(this.feature, '_onChange');
this.feature.fetch();
this.feature.set('cartodb_id', 'hello');
expect(this.feature._onChange).toHaveBeenCalled();
});
it('should add to changesHistory all changed attributes after fetching', function () {
this.feature.fetch();
this.feature.set('cartodb_id', 'hey');
expect(this.feature._changesHistory).toEqual(['cartodb_id']);
this.feature.set('the_geom', '{}');
expect(this.feature._changesHistory).toEqual(['cartodb_id', 'the_geom']);
this.feature.set('name', 'Barcelona');
expect(this.feature._changesHistory).toEqual(['cartodb_id', 'the_geom', 'name']);
this.feature.set('cartodb_id', '1');
expect(this.feature._changesHistory).toEqual(['cartodb_id', 'the_geom', 'name']);
});
});
});
describe('.hasBeenChangedAfterLastSaved', function () {
beforeEach(function () {
this.feature._changesHistory = ['the_geom', 'cartodb_id'];
});
it('should return if an attribute has been changed before last saved', function () {
expect(this.feature.hasBeenChangedAfterLastSaved('the_geom')).toBeTruthy();
expect(this.feature.hasBeenChangedAfterLastSaved('name')).toBeFalsy();
expect(this.feature.hasBeenChangedAfterLastSaved('cartodb_id')).toBeTruthy();
});
});
describe('._cleanChangesHistory', function () {
beforeEach(function () {
this.feature._changesHistory = ['the_geom', 'cartodb_id'];
});
it('should clean changesHistory', function () {
expect(this.feature.hasBeenChangedAfterLastSaved('the_geom')).toBeTruthy();
expect(this.feature.hasBeenChangedAfterLastSaved('cartodb_id')).toBeTruthy();
this.feature._cleanChangesHistory();
expect(this.feature.hasBeenChangedAfterLastSaved('the_geom')).toBeFalsy();
expect(this.feature.hasBeenChangedAfterLastSaved('cartodb_id')).toBeFalsy();
expect(this.feature._changesHistory).toEqual([]);
});
});
describe('.save', function () {
beforeEach(function () {
this.feature._changesHistory = ['the_geom'];
});
it('should save the feature using the query row model', function () {
this.feature.save();
expect(this.fakeQueryRowModel.save).toHaveBeenCalled();
});
describe('when save succeeds', function () {
beforeEach(function () {
this.fakeQueryRowModel.save.and.callFake(function (attrs, options) {
options && options.success(new Backbone.Model(attrs));
});
});
it('should invoke the success callback', function () {
var successCallback = jasmine.createSpy('successCallback');
this.feature.save({
success: successCallback
});
expect(successCallback).toHaveBeenCalled();
});
it('should clean changes history', function () {
this.feature.save();
expect(this.feature._changesHistory.length).toBe(0);
});
it('should trigger a save event', function () {
var saveCallback = jasmine.createSpy('save');
this.feature.on('save', saveCallback);
this.feature.save();
expect(saveCallback).toHaveBeenCalled();
});
it('should update the cartodb_id of the feature when creating a new row', function () {
this.feature.set('cartodb_id', null);
expect(this.feature.isNew()).toBeTruthy();
this.fakeQueryRowModel.save.and.callFake(function (attrs, options) {
attrs = _.extend({}, attrs, {
cartodb_id: '56789'
});
options && options.success(new Backbone.Model(attrs));
});
this.feature.save();
expect(this.fakeQueryRowModel.save.calls.mostRecent().args[0]).toEqual({
name: 'Madrid',
country: 'Spain'
});
expect(this.feature.get('cartodb_id')).toEqual('56789');
});
it('should NOT update the cartodb_id of the feature when updating an existing row', function () {
expect(this.feature.get('cartodb_id')).toEqual('12345');
// Row is updated and it returns a different cartodb_id. This will never happen
// in The Real World, but it helps in this test
this.fakeQueryRowModel.save.and.callFake(function (attrs, options) {
attrs = _.extend({}, attrs, {
cartodb_id: '56789'
});
options && options.success(new Backbone.Model(attrs));
});
this.feature.save();
expect(this.fakeQueryRowModel.save.calls.mostRecent().args[0]).toEqual({
name: 'Madrid',
country: 'Spain'
});
expect(this.feature.get('cartodb_id')).toEqual('12345');
});
});
describe('when save fails', function () {
beforeEach(function () {
this.fakeQueryRowModel.save.and.callFake(function (attrs, options) {
options && options.error();
});
});
it('should invoke the error callback', function () {
var errorCallback = jasmine.createSpy('errorCallback');
this.feature.save({
error: errorCallback
});
expect(errorCallback).toHaveBeenCalled();
});
it('should not clean changes history', function () {
this.feature.save();
expect(this.feature._changesHistory.length).toBe(1);
});
});
});
describe('.destroy', function () {
describe('when destroy succeeds', function () {
beforeEach(function () {
this.fakeQueryRowModel.destroy.and.callFake(function (options) {
options && options.success();
});
});
it('should invoke the success callback', function () {
var successCallback = jasmine.createSpy('successCallback');
this.feature.destroy({
success: successCallback
});
expect(successCallback).toHaveBeenCalled();
});
it('should trigger an event', function () {
var onRemoveCallback = jasmine.createSpy('onRemoveCallback');
this.feature.on('remove', onRemoveCallback);
this.feature.destroy();
expect(onRemoveCallback).toHaveBeenCalled();
});
});
describe('when destroy fails', function () {
beforeEach(function () {
this.fakeQueryRowModel.destroy.and.callFake(function (options) {
options && options.error();
});
});
it('should invoke the error callback', function () {
var errorCallback = jasmine.createSpy('errorCallback');
this.feature.destroy({
error: errorCallback
});
expect(errorCallback).toHaveBeenCalled();
});
});
});
});
@@ -0,0 +1,110 @@
var GrantablesCollection = require('builder/data/grantables-collection');
var ConfigModel = require('builder/data/config-model');
var OrganizationModel = require('builder/data/organization-model');
describe('data/grantables-collection', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.org = new OrganizationModel({
id: 'org1',
owner: {
id: 'hello',
username: 'dev',
email: 'hello@hello'
}
}, {
configModel: configModel
});
this.grantables = new GrantablesCollection([], {
configModel: configModel,
organization: this.org,
currentUserId: 'u2'
});
});
it('should have a working URL', function () {
expect(this.grantables.url()).toMatch('/u/pepe/api/v1/organization/org1/grantables');
});
describe('when is fetched', function () {
beforeEach(function () {
this.grantables.sync = function (a, b, options) {
options.success({
'grantables': [{
'id': 'u1',
'type': 'user',
'name': 'an user',
'avatar_url': 'images/avatars/avatar_marker_red.png',
'model': {
'id': 'u1',
'username': 'foo'
}
}, {
'id': 'u2',
'type': 'user',
'name': 'current user'
}, {
'id': 'g1',
'type': 'group',
'name': 'my group',
'avatar_url': 'avatar_marker_red.png',
'model': {
'id': 'g1',
'display_name': 'my group',
'name': 'my_group'
}
}],
'total_entries': 3
});
};
this.grantables.fetch();
});
it('should set grantables model', function () {
expect(this.grantables.length).toBe(2);
});
it('should have a organization set on collection', function () {
expect(this.grantables.organization).toBe(this.org);
});
it('should not add current user', function () {
expect(this.grantables.length).toEqual(2);
expect(this.grantables.total_entries).toEqual(2);
expect(this.grantables.pluck('id')).toEqual(['u1', 'g1']);
});
it('should set organization on each entity model', function () {
expect(this.grantables.first().get('organization')).toBe(this.org);
expect(this.grantables.last().get('organization')).toBe(this.org);
});
});
describe('totalCount', function () {
it('should not be set initially', function () {
expect(this.grantables.totalCount()).toBeUndefined();
});
describe('when data is fetched', function () {
beforeEach(function () {
this.grantables.sync = function (a, b, options) {
options.success({
grantables: [],
total_entries: 77
});
};
this.grantables.fetch();
});
it('should have the total count set', function () {
expect(this.grantables.totalCount()).toEqual(77);
});
});
});
});
@@ -0,0 +1,68 @@
var ConfigModel = require('builder/data/config-model');
var InfowindowDefinitionModel = require('builder/data/infowindow-definition-model');
describe('data/infowindow-definition-model', function () {
describe('model', function () {
var model;
beforeEach(function () {
this.configModel = new ConfigModel({
base_url: '/u/pepe'
});
model = new InfowindowDefinitionModel({}, {
configModel: this.configModel
});
});
it('should allow adding an alternative name', function () {
model.addField('name');
model.addField('description');
model.setAlternativeName('name', 'nombre');
model.setAlternativeName('description', 'descriptionn');
var n = model.getAlternativeName('name');
var d = model.getAlternativeName('description');
expect(n).toEqual('nombre');
expect(d).toEqual('descriptionn');
});
it('should add a field', function () {
expect(model.containsField('test')).toEqual(false);
model.addField('test');
model.addField('test2');
expect(model.containsField('test')).toEqual(true);
model.removeField('test');
expect(model.containsField('test')).toEqual(false);
expect(model.containsField('test2')).toEqual(true);
model.clearFields();
expect(model.containsField('test2')).toEqual(false);
});
it('should add a field in order', function () {
model.addField('test', 1);
model.addField('test2', 0);
expect(model.get('fields')[0].name).toEqual('test2');
expect(model.get('fields')[1].name).toEqual('test');
});
it('should allow modify field properties', function () {
model.addField('test');
var t = model.getFieldProperty('test', 'title');
expect(t).toEqual(true);
model.setFieldProperty('test', 'title', false);
t = model.getFieldProperty('test', 'title');
expect(t).toEqual(false);
});
it('should empty fields if template is none', function () {
model.addField('test');
expect(model.containsField('test')).toBeTruthy();
model.setTemplate('');
expect(model.containsField('test')).toBeFalsy();
expect(model.get('fields')).toEqual([]);
});
});
});
@@ -0,0 +1,36 @@
var ConfigModel = require('builder/data/config-model');
var InfowindowHoverModel = require('builder/data/infowindow-hover-model');
describe('data/infowindow-hover-model', function () {
describe('model', function () {
var model;
beforeEach(function () {
this.configModel = new ConfigModel({
base_url: '/u/pepe'
});
model = new InfowindowHoverModel({}, {
configModel: this.configModel
});
});
it('should relay in default attributes properly', function () {
expect(model.get('template_name')).toBe('');
expect(model.get('template')).toBe('');
});
it('should check isCustomTemplate properly', function () {
expect(model.isCustomTemplate()).toBe(false);
model.set({template: 'foo', template_name: ''});
expect(model.isCustomTemplate()).toBe(true);
model.set({template: '', template_name: 'foo'});
expect(model.isCustomTemplate()).toBe(false);
model.set({template: '', template_name: ''});
expect(model.isCustomTemplate()).toBe(false);
});
});
});
@@ -0,0 +1,38 @@
var layerColors = require('builder/data/layer-colors');
describe('data/layer-colors', function () {
describe('.getColorForLetter', function () {
it('should return the first color if no letter is given', function () {
expect(layerColors.getColorForLetter()).toEqual(layerColors.COLORS[0]);
});
it('should return the color for the given letter', function () {
expect(layerColors.getColorForLetter('a')).toEqual(layerColors.COLORS[0]);
expect(layerColors.getColorForLetter('b')).toEqual(layerColors.COLORS[1]);
expect(layerColors.getColorForLetter('c')).toEqual(layerColors.COLORS[2]);
expect(layerColors.getColorForLetter('d')).toEqual(layerColors.COLORS[3]);
expect(layerColors.getColorForLetter('e')).toEqual(layerColors.COLORS[4]);
expect(layerColors.getColorForLetter('f')).toEqual(layerColors.COLORS[5]);
expect(layerColors.getColorForLetter('g')).toEqual(layerColors.COLORS[6]);
expect(layerColors.getColorForLetter('h')).toEqual(layerColors.COLORS[7]);
expect(layerColors.getColorForLetter('i')).toEqual(layerColors.COLORS[8]);
expect(layerColors.getColorForLetter('j')).toEqual(layerColors.COLORS[9]);
expect(layerColors.getColorForLetter('k')).toEqual(layerColors.COLORS[0]);
expect(layerColors.getColorForLetter('l')).toEqual(layerColors.COLORS[1]);
expect(layerColors.getColorForLetter('m')).toEqual(layerColors.COLORS[2]);
expect(layerColors.getColorForLetter('n')).toEqual(layerColors.COLORS[3]);
expect(layerColors.getColorForLetter('o')).toEqual(layerColors.COLORS[4]);
expect(layerColors.getColorForLetter('p')).toEqual(layerColors.COLORS[5]);
expect(layerColors.getColorForLetter('q')).toEqual(layerColors.COLORS[6]);
expect(layerColors.getColorForLetter('r')).toEqual(layerColors.COLORS[7]);
expect(layerColors.getColorForLetter('s')).toEqual(layerColors.COLORS[8]);
expect(layerColors.getColorForLetter('t')).toEqual(layerColors.COLORS[9]);
expect(layerColors.getColorForLetter('u')).toEqual(layerColors.COLORS[0]);
expect(layerColors.getColorForLetter('v')).toEqual(layerColors.COLORS[1]);
expect(layerColors.getColorForLetter('w')).toEqual(layerColors.COLORS[2]);
expect(layerColors.getColorForLetter('x')).toEqual(layerColors.COLORS[3]);
expect(layerColors.getColorForLetter('y')).toEqual(layerColors.COLORS[4]);
expect(layerColors.getColorForLetter('z')).toEqual(layerColors.COLORS[5]);
});
});
});
@@ -0,0 +1,289 @@
var _ = require('underscore');
var LayerContentModel = require('builder/data/layer-content-model');
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 STATES = require('builder/data/query-base-status');
var CONTEXTS = {
map: 'map',
table: 'table'
};
describe('data/layer-content-model', function () {
var model;
var defaultOptions;
var querySchemaModel;
var queryGeometryModel;
var queryRowsCollection;
var createModelFn = function (options) {
querySchemaModel = getQuerySchemaModelFixture({
query: ''
});
queryGeometryModel = getQueryGeometryModelFixture();
queryRowsCollection = getQueryRowsCollectionModelFixture({
querySchemaModel: querySchemaModel
});
defaultOptions = {
querySchemaModel: querySchemaModel,
queryGeometryModel: queryGeometryModel,
queryRowsCollection: queryRowsCollection
};
return new LayerContentModel({}, _.extend(defaultOptions, options));
};
beforeEach(function () {
model = createModelFn();
});
describe('.initialize', function () {
it('should have initial state by default', function () {
expect(model.get('state')).toEqual(STATES.initial);
});
it('should have map context by default', function () {
expect(model.get('context')).toEqual(CONTEXTS.map);
});
});
describe('._initBinds', function () {
it('should call to _setState when _querySchemaModel:status changes', function () {
spyOn(model, '_setState');
model._initBinds();
model._querySchemaModel.trigger('change:status');
expect(model._setState).toHaveBeenCalled();
});
it('should call to _setState when _queryGeometryModel:status changes', function () {
spyOn(model, '_setState');
model._initBinds();
model._queryGeometryModel.trigger('change:status');
expect(model._setState).toHaveBeenCalled();
});
it('should call to _setState when _queryRowsCollection.statusModel:status changes', function () {
spyOn(model, '_setState');
model._initBinds();
model._queryRowsCollection.statusModel.trigger('change:status');
expect(model._setState).toHaveBeenCalled();
});
});
describe('_.getState', function () {
it('should return initial state if it is the initial state', function () {
spyOn(model, 'isErrored').and.returnValue(false);
spyOn(model, 'isFetched').and.returnValue(false);
spyOn(model, 'isFetching').and.returnValue(false);
expect(model._getState()).toEqual(STATES.initial);
});
it('should return undefined state by default', function () {
spyOn(model, 'isErrored').and.returnValue(false);
spyOn(model, 'isFetched').and.returnValue(false);
spyOn(model, 'isFetching').and.returnValue(false);
spyOn(model, 'isInitial').and.returnValue(false);
expect(model._getState()).toEqual(STATES.unavailable);
});
it('should return errored if it has errors', function () {
spyOn(model, 'isErrored').and.returnValue(true);
expect(model._getState()).toEqual(STATES.errored);
});
it('should return fetched if it is fetched', function () {
spyOn(model, 'isErrored').and.returnValue(false);
spyOn(model, 'isFetched').and.returnValue(true);
expect(model._getState()).toEqual(STATES.fetched);
});
it('should return fetching if it is fetching', function () {
spyOn(model, 'isErrored').and.returnValue(false);
spyOn(model, 'isFetched').and.returnValue(false);
spyOn(model, 'isFetching').and.returnValue(true);
expect(model._getState()).toEqual(STATES.fetching);
});
});
describe('_.setState', function () {
it('should updated the given state by getState', function () {
spyOn(model, '_getState').and.returnValue('final-state');
model.set('state', 'initial-state');
model._setState();
expect(model.get('state')).toEqual('final-state');
});
});
describe('.isErrored', function () {
it('should be true if querySchemaModel has repeated errors', function () {
spyOn(model._querySchemaModel, 'hasRepeatedErrors').and.returnValue(true);
spyOn(model._queryGeometryModel, 'hasRepeatedErrors').and.returnValue(false);
spyOn(model._queryRowsCollection, 'hasRepeatedErrors').and.returnValue(false);
expect(model.isErrored()).toEqual(true);
});
it('should be true if _queryGeometryModel has repeated errors', function () {
spyOn(model._querySchemaModel, 'hasRepeatedErrors').and.returnValue(false);
spyOn(model._queryGeometryModel, 'hasRepeatedErrors').and.returnValue(true);
spyOn(model._queryRowsCollection, 'hasRepeatedErrors').and.returnValue(false);
expect(model.isErrored()).toEqual(true);
});
it('should be true if _queryRowsCollection has repeated errors', function () {
spyOn(model._querySchemaModel, 'hasRepeatedErrors').and.returnValue(false);
spyOn(model._queryGeometryModel, 'hasRepeatedErrors').and.returnValue(false);
spyOn(model._queryRowsCollection, 'hasRepeatedErrors').and.returnValue(true);
expect(model.isErrored()).toEqual(true);
});
it('should be false if any query model have errors', function () {
spyOn(model._querySchemaModel, 'hasRepeatedErrors').and.returnValue(false);
spyOn(model._queryGeometryModel, 'hasRepeatedErrors').and.returnValue(false);
spyOn(model._queryRowsCollection, 'hasRepeatedErrors').and.returnValue(false);
expect(model.isErrored()).toEqual(false);
});
});
describe('.isFetching', function () {
it('should be true if querySchemaModel is fetching', function () {
spyOn(model._querySchemaModel, 'isFetching').and.returnValue(true);
spyOn(model._queryGeometryModel, 'isFetching').and.returnValue(false);
spyOn(model._queryRowsCollection, 'isFetching').and.returnValue(false);
expect(model.isFetching()).toEqual(true);
});
it('should be true if _queryGeometryModel is fetching', function () {
spyOn(model._querySchemaModel, 'isFetching').and.returnValue(false);
spyOn(model._queryGeometryModel, 'isFetching').and.returnValue(true);
spyOn(model._queryRowsCollection, 'isFetching').and.returnValue(false);
expect(model.isFetching()).toEqual(true);
});
it('should be true if _queryRowsCollection is fetching', function () {
spyOn(model._querySchemaModel, 'isFetching').and.returnValue(false);
spyOn(model._queryGeometryModel, 'isFetching').and.returnValue(false);
spyOn(model._queryRowsCollection, 'isFetching').and.returnValue(true);
expect(model.isFetching()).toEqual(true);
});
it('should be false if any query model is fetching', function () {
spyOn(model._querySchemaModel, 'isFetching').and.returnValue(false);
spyOn(model._queryGeometryModel, 'isFetching').and.returnValue(false);
spyOn(model._queryRowsCollection, 'isFetching').and.returnValue(false);
expect(model.isFetching()).toEqual(false);
});
});
describe('.isFetched', function () {
it('should be false if querySchemaModel is not fetched', function () {
spyOn(model._querySchemaModel, 'isFetched').and.returnValue(false);
spyOn(model._queryGeometryModel, 'isFetched').and.returnValue(true);
spyOn(model._queryRowsCollection, 'isFetched').and.returnValue(true);
expect(model.isFetched()).toEqual(false);
});
it('should be false if _queryGeometryModel is not fetched', function () {
spyOn(model._querySchemaModel, 'isFetched').and.returnValue(true);
spyOn(model._queryGeometryModel, 'isFetched').and.returnValue(false);
spyOn(model._queryRowsCollection, 'isFetched').and.returnValue(true);
expect(model.isFetched()).toEqual(false);
});
it('should be false if _queryRowsCollection is not fetched', function () {
spyOn(model._querySchemaModel, 'isFetched').and.returnValue(true);
spyOn(model._queryGeometryModel, 'isFetched').and.returnValue(true);
spyOn(model._queryRowsCollection, 'isFetched').and.returnValue(false);
expect(model.isFetched()).toEqual(false);
});
it('should be true if all query models are fetched', function () {
spyOn(model._querySchemaModel, 'isFetched').and.returnValue(true);
spyOn(model._queryGeometryModel, 'isFetched').and.returnValue(true);
spyOn(model._queryRowsCollection, 'isFetched').and.returnValue(true);
expect(model.isFetched()).toEqual(true);
});
});
describe('.isInFinalStatus', function () {
it('should be false if querySchemaModel is not in final status', function () {
spyOn(model._querySchemaModel, 'isInFinalStatus').and.returnValue(false);
spyOn(model._queryGeometryModel, 'isInFinalStatus').and.returnValue(true);
spyOn(model._queryRowsCollection, 'isInFinalStatus').and.returnValue(true);
expect(model.isInFinalStatus()).toEqual(false);
});
it('should be false if _queryGeometryModel is not in final status', function () {
spyOn(model._querySchemaModel, 'isInFinalStatus').and.returnValue(true);
spyOn(model._queryGeometryModel, 'isInFinalStatus').and.returnValue(false);
spyOn(model._queryRowsCollection, 'isInFinalStatus').and.returnValue(true);
expect(model.isInFinalStatus()).toEqual(false);
});
it('should be false if _queryRowsCollection is not in final status', function () {
spyOn(model._querySchemaModel, 'isInFinalStatus').and.returnValue(true);
spyOn(model._queryGeometryModel, 'isInFinalStatus').and.returnValue(true);
spyOn(model._queryRowsCollection, 'isInFinalStatus').and.returnValue(false);
expect(model.isInFinalStatus()).toEqual(false);
});
it('should be true if all query models are in final status', function () {
spyOn(model._querySchemaModel, 'isInFinalStatus').and.returnValue(true);
spyOn(model._queryGeometryModel, 'isInFinalStatus').and.returnValue(true);
spyOn(model._queryRowsCollection, 'isInFinalStatus').and.returnValue(true);
expect(model.isInFinalStatus()).toEqual(true);
});
});
describe('.isDone', function () {
it('should be true if it is fetched', function () {
spyOn(model, 'isFetched').and.returnValue(true);
expect(model.isDone()).toEqual(true);
});
it('should be true if it is errored', function () {
spyOn(model, 'isErrored').and.returnValue(true);
expect(model.isDone()).toEqual(true);
});
it('should be false it is not fetched nor errored', function () {
spyOn(model, 'isFetched').and.returnValue(false);
spyOn(model, 'isErrored').and.returnValue(false);
expect(model.isDone()).toEqual(false);
});
});
});
@@ -0,0 +1,976 @@
var Backbone = require('backbone');
var ConfigModel = require('builder/data/config-model');
var UserModel = require('builder/data/user-model');
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
var LayerDefinitionModel = require('builder/data/layer-definition-model');
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
describe('data/layer-definition-model', function () {
beforeEach(function () {
this.configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.userModel = new UserModel({}, {
configModel: this.configModel
});
this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
configModel: this.configModel,
userModel: this.userModel
});
this.collection = new LayerDefinitionsCollection(null, {
configModel: this.configModel,
userModel: this.userModel,
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
mapId: 'm123',
stateDefinitionModel: {}
});
this.model = 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.collection
});
});
it('should set autoStyle as false if it is not defined', function () {
expect(this.model.get('autoStyle')).toBe(false);
});
it('should transform some attrs to be compatible with cartodb.js', function () {
expect(this.model.get('cartocss')).toEqual('asdasd');
expect(this.model.get('tile_style')).toBeUndefined();
expect(this.model.get('sql')).toContain('SELECT');
expect(this.model.get('query')).toBeUndefined();
});
describe('.save', function () {
beforeEach(function () {
spyOn(this.model.styleModel, 'resetPropertiesFromAutoStyle');
this.model.sync = function () {};
});
describe('if autostyle is enabled', function () {
beforeEach(function () {
this.model.set('autoStyle', '8888-7777-6666-1111');
this.model.save();
});
it('should "remove" the autoStyle attribute when model is saved', function () {
expect(this.model.get('autoStyle')).toBeFalsy();
});
it('should reset styleModel styles from autostyle properties', function () {
expect(this.model.styleModel.resetPropertiesFromAutoStyle).toHaveBeenCalled();
});
it('should preserve auto style if attribute is true', function () {
var savedAttrs;
this.model.sync = function (method, model, opts) {
savedAttrs = model.attributes;
};
this.model.save({ hello: true }, { shouldPreserveAutoStyle: true });
expect(savedAttrs.autoStyle).toBeFalsy();
expect(savedAttrs.hello).toBeDefined();
});
});
describe('if autostyle is NOT enabled', function () {
beforeEach(function () {
delete this.model.attributes.autoStyle;
this.model.save();
});
it('should not reset styleModel styles if autostyle is not applied', function () {
expect(this.model.styleModel.resetPropertiesFromAutoStyle).not.toHaveBeenCalled();
expect(this.model.get('autoStyle')).toBeFalsy();
});
});
});
describe('.toJSON', function () {
it('should return the original data', function () {
expect(this.model.toJSON()).toEqual({
id: 'abc-123',
kind: 'carto',
options: {
type: 'CartoDB',
visible: true,
color: '#FABADA',
table_name: 'foo',
query: 'SELECT * FROM foo',
tile_style: 'asdasd',
style_properties: jasmine.any(Object),
cartocss_history: jasmine.any(Array),
sql_history: jasmine.any(Array)
}
});
});
it('should not include autoStyle info', function () {
this.model.set('autoStyle', '8888-7777-6666-1111');
var json = this.model.toJSON();
expect(json).not.toContain({
autoStyle: '8888-7777-6666-1111'
});
});
});
describe('styleModel', function () {
it('should create style model if table name attribute exists', function () {
expect(this.model.styleModel).toBeDefined();
});
it('should not add style model if table name doesn\'t exist', function () {
var mdl = new LayerDefinitionModel({
id: 'other',
options: {
type: 'tiled',
tile_style: 'asdasd'
}
}, {
parse: true,
configModel: this.configModel
});
expect(mdl.styleModel).toBeUndefined();
});
describe('.toJSON', function () {
it('should provide style model definition', function () {
spyOn(this.model.styleModel, 'isAutogenerated').and.returnValue(false);
var data = this.model.toJSON();
expect(data.options.style_properties).toBeDefined();
});
it('should not provide style definition if autogenerated option is true', function () {
spyOn(this.model.styleModel, 'isAutogenerated').and.returnValue(true);
var data = this.model.toJSON();
expect(data.options.style_properties).not.toBeDefined();
});
});
});
describe('.isOwnerOfAnalysisNode', function () {
beforeEach(function () {
this.nodeModel = this.analysisDefinitionNodesCollection.add({
id: 'b3',
type: 'source',
params: {
query: 'SELECT * FROM somewhere'
}
});
});
it('should return true if given layer definition model is considered owning it', function () {
expect(this.model.isOwnerOfAnalysisNode(this.nodeModel)).toBe(false);
this.model.set('letter', 'b');
expect(this.model.isOwnerOfAnalysisNode(this.nodeModel)).toBe(true);
});
});
describe('for a layer with an analysis source', function () {
beforeEach(function () {
this.model = new LayerDefinitionModel({
id: 'abc-123',
kind: 'carto',
options: {
type: 'CartoDB',
color: '#FABADA',
table_name: 'foo_table',
source: 'a1'
}
}, {
parse: true,
configModel: this.configModel
});
});
it('should have a source set', function () {
expect(this.model.get('source')).toEqual('a1');
});
describe('.toJSON', function () {
it('should return the original data', function () {
expect(this.model.toJSON()).toEqual({
id: 'abc-123',
kind: 'carto',
options: {
type: 'CartoDB',
color: '#FABADA',
table_name: 'foo_table',
source: 'a1',
style_properties: jasmine.any(Object),
cartocss_history: jasmine.any(Array),
sql_history: jasmine.any(Array)
}
});
});
});
});
describe('for a layer with an infowindow', function () {
beforeEach(function () {
this.configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.model = new LayerDefinitionModel({
id: 'abc-123',
kind: 'carto',
options: {
type: 'CartoDB',
color: '#FABADA',
table_name: 'foo_table',
source: 'a1',
style_properties: jasmine.any(Object),
cartocss_history: jasmine.any(Array),
sql_history: jasmine.any(Array)
},
infowindow: {
template_name: 'infowindow_light',
latlng: [0, 0],
offset: [28, 0],
maxHeight: 180,
autoPan: true,
template: '',
content: '',
visibility: false,
alternative_names: {},
fields: [
{
name: 'description',
title: true,
position: 0
},
{
name: 'name',
title: true,
position: 1
}
],
width: 226,
headerColor: {
color: { fixed: '#35AAE5;', opacity: 1 }
}
}
}, {
parse: true,
configModel: this.configModel
});
this.infowindowModel = this.model.infowindowModel;
});
it('should have an infowindow model', function () {
expect(this.infowindowModel).toBeDefined();
expect(this.infowindowModel.get('fields').length).toEqual(2);
});
describe('infowindow/tooltip reset', function () {
beforeEach(function () {
this.model.tooltipModel = new Backbone.Model();
this.model.tooltipModel.unsetTemplate = function () {};
this.model.infowindowModel = new Backbone.Model();
this.model.infowindowModel.unsetTemplate = function () {};
spyOn(this.model.tooltipModel, 'unsetTemplate');
spyOn(this.model.infowindowModel, 'unsetTemplate');
this.styleModel = this.model.styleModel;
});
it('should reset infowindow/tooltip template and fields if style model type is aggregated', function () {
this.styleModel.set('type', 'heatmap');
expect(this.model.infowindowModel.unsetTemplate).not.toHaveBeenCalled();
expect(this.model.tooltipModel.unsetTemplate).not.toHaveBeenCalled();
this.styleModel.set('type', 'squares');
expect(this.model.infowindowModel.unsetTemplate).toHaveBeenCalled();
expect(this.model.tooltipModel.unsetTemplate).toHaveBeenCalled();
});
it('should reset infowindow/tooltip template and fields if style model has animated enabled', function () {
this.styleModel.set('type', 'simple');
expect(this.model.infowindowModel.unsetTemplate).not.toHaveBeenCalled();
expect(this.model.tooltipModel.unsetTemplate).not.toHaveBeenCalled();
this.styleModel.set('type', 'animation');
expect(this.model.infowindowModel.unsetTemplate).toHaveBeenCalled();
expect(this.model.tooltipModel.unsetTemplate).toHaveBeenCalled();
});
});
it('should not save the model if infowindow changes', function () {
spyOn(this.model, 'save');
this.infowindowModel.set('template_name', 'infowindow_dark');
expect(this.model.save).not.toHaveBeenCalled();
});
describe('.toJSON', function () {
it('should modify infowindow attribute', function () {
this.infowindowModel.setTemplate('testing');
var data = this.model.toJSON();
expect(data.infowindow.template_name).toEqual('testing');
});
it('should return the original data', function () {
expect(this.model.toJSON()).toEqual({
id: 'abc-123',
kind: 'carto',
options: {
type: 'CartoDB',
color: '#FABADA',
table_name: 'foo_table',
source: 'a1',
style_properties: jasmine.any(Object),
cartocss_history: jasmine.any(Array),
sql_history: jasmine.any(Array)
},
infowindow: {
template_name: 'infowindow_light',
latlng: [0, 0],
offset: [28, 0],
maxHeight: 180,
autoPan: true,
template: '',
content: '',
visibility: false,
alternative_names: {},
fields: [
{
name: 'description',
title: true,
position: 0
},
{
name: 'name',
title: true,
position: 1
}
],
width: 226,
headerColor: {
color: { fixed: '#35AAE5;', opacity: 1 }
}
}
});
});
it('should not provide infowindow data if model is empty', function () {
this.infowindowModel.clear();
expect(this.model.toJSON()).toEqual({
id: 'abc-123',
kind: 'carto',
options: {
type: 'CartoDB',
color: '#FABADA',
table_name: 'foo_table',
source: 'a1',
style_properties: jasmine.any(Object),
cartocss_history: jasmine.any(Array),
sql_history: jasmine.any(Array)
}
});
});
});
});
describe('.getAnalysisDefinitionNodeModel', function () {
beforeEach(function () {
this.a1 = {};
spyOn(this.model, 'findAnalysisDefinitionNodeModel').and.returnValue(this.a1);
this.model.set('source', 'a1');
this.model.getAnalysisDefinitionNodeModel();
});
it('should return the current analysis model of layer', function () {
expect(this.model.findAnalysisDefinitionNodeModel).toHaveBeenCalledWith('a1');
expect(this.model.getAnalysisDefinitionNodeModel()).toBe(this.a1);
});
});
describe('.findAnalysisDefinitionNodeModel', function () {
beforeEach(function () {
this.b1 = {id: 'b1'};
spyOn(this.collection, 'findAnalysisDefinitionNodeModel');
this.model.findAnalysisDefinitionNodeModel('b1');
});
it('should return the node for given id', function () {
this.collection.findAnalysisDefinitionNodeModel.and.returnValue(this.b1);
expect(this.model.findAnalysisDefinitionNodeModel('b1')).toBe(this.b1);
});
it('should return nothing given an id of a non-existing node', function () {
expect(this.model.findAnalysisDefinitionNodeModel('x1')).toBeUndefined();
});
});
describe('.toggleVisible', function () {
it('should toggle the visible attr', function () {
this.model.toggleVisible();
expect(this.model.get('visible')).toBe(false);
this.model.toggleVisible();
expect(this.model.get('visible')).toBe(true);
this.model.toggleVisible();
expect(this.model.get('visible')).toBe(false);
});
});
describe('.toggleCollapse', function () {
it('should toggle the collapse attr', function () {
this.model.toggleCollapse();
expect(this.model.get('collapsed')).toBe(true);
this.model.toggleCollapse();
expect(this.model.get('collapsed')).toBe(false);
this.model.toggleCollapse();
expect(this.model.get('collapsed')).toBe(true);
});
});
describe('.getNumberOfAnalyses', function () {
beforeEach(function () {
this.model.set('letter', 'a');
});
it("should return 0 if layer doesn't have a source node", function () {
expect(this.model.getNumberOfAnalyses()).toEqual(0);
});
it('should return 0 if layer has an analysis node as source node', function () {
this.analysisDefinitionNodesCollection.add({
id: 'a0',
type: 'source',
params: {
query: 'SELECT * FROM foo',
table_name: 'foo'
}
});
expect(this.model.getNumberOfAnalyses()).toEqual(0);
});
it('should return the number of analyses owned by the layer', function () {
this.analysisDefinitionNodesCollection.add([{
id: 'a0',
type: 'source',
params: {
query: 'SELECT * FROM foo'
}
}, {
id: 'a1',
type: 'buffer',
radio: 300,
distance: 'meters',
source: 'a0'
}, {
id: 'a2',
type: 'buffer',
radio: 600,
distance: 'meters',
source: 'a1'
}, {
id: 'b0',
type: 'source',
params: {
query: 'SELECT * FROM bar'
}
}, {
id: 'b1',
type: 'buffer',
radio: 300,
distance: 'meters',
source: 'b0'
}]);
this.model.set('source', 'a2');
expect(this.model.getNumberOfAnalyses()).toEqual(2);
});
});
describe('.containsNode', function () {
beforeEach(function () {
this.analysisDefinitionNodesCollection.add([{
id: 'a0',
type: 'source',
params: {
query: 'SELECT * FROM foo'
}
}, {
id: 'a1',
type: 'buffer',
radio: 300,
distance: 'meters',
source: 'a0'
}, {
id: 'b1',
type: 'buffer',
radio: 300,
distance: 'meters',
source: 'a0'
}]);
this.model.set('source', 'a1');
});
it('should return true if layer contains given node', function () {
expect(this.model.containsNode(this.analysisDefinitionNodesCollection.get('a1'))).toBe(true);
expect(this.model.containsNode(this.analysisDefinitionNodesCollection.get('a0'))).toBe(true);
expect(this.model.containsNode(this.analysisDefinitionNodesCollection.get('b1'))).toBe(false);
expect(this.model.containsNode(false)).toBe(false);
});
});
describe('.canBeDeletedByUser', function () {
it('should return true as long as it is not the only data layer and not all the other layers depend on it', function () {
expect(this.model.canBeDeletedByUser()).toBe(false);
spyOn(this.collection, 'getNumberOfDataLayers').and.returnValue(3);
expect(this.model.canBeDeletedByUser()).toBe(true);
});
});
describe('.getAllDependentLayers', function () {
beforeEach(function () {
this.model.set('letter', 'a');
this.model.set('source', 'a0');
this.collection.add([{
id: '323',
kind: 'carto',
letter: 'b',
options: {
table_name: 'alice',
source: 'a1'
}
}, {
id: '454',
kind: 'carto',
letter: 'e',
options: {
table_name: 'robin',
source: 'b1'
}
}]);
this.analysisDefinitionNodesCollection.add([{
id: 'a0',
type: 'source',
params: {
query: 'SELECT * FROM foo'
}
}, {
id: 'a1',
type: 'buffer',
radio: 300,
distance: 'meters',
source: 'a0'
}, {
id: 'b1',
type: 'buffer',
radio: 300,
distance: 'meters',
source: 'a1'
}, {
id: 'e1',
type: 'buffer',
radio: 300,
distance: 'meters',
source: 'b1'
}]);
});
it('should return two layers depending on it', function () {
/* ************************************ */
/* Expected layers schema result: */
/* _________ _________ _________ */
/* | A | | B* | | E* | */
/* | - a0 | | - a1 | | - b1 | */
/* | - a1 | | - b1 | | - e1 | */
/* _________ _________ _________ */
/* ************************************ */
expect(this.model.getAllDependentLayers()).toBe(2);
});
it('should return three layers depending on it', function () {
/* ************************************************* */
/* Expected layers schema result: */
/* _________ _________ _________ _________ */
/* | A | | B* | | E* | | F* | */
/* | - a0 | | - a1 | | - b1 | | - e1 | */
/* | - a1 | | - b1 | | - e1 | | - f1 | */
/* _________ _________ _________ _________ */
/* ************************************************* */
this.collection.add({
id: '111',
kind: 'carto',
letter: 'f',
options: {
table_name: 'punki',
source: 'e1'
}
});
this.analysisDefinitionNodesCollection.add({
id: 'f1',
type: 'buffer',
radio: 300,
distance: 'meters',
source: 'b1'
});
expect(this.model.getAllDependentLayers()).toBe(3);
});
it('should return five layers depending on it', function () {
/* **************************************************************************** */
/* Expected layers schema result: */
/* _________ _________ _________ _________ _________ _________ */
/* | A | | B* | | E* | | F* | | G* | | H* | */
/* | - a0 | | - a1 | | - b1 | | - e1 | | - f2 | | - g2 | */
/* | - a1 | | - b1 | | - e1 | | - f1 | | - g1 | | - h1 | */
/* | | | | | | | - f2 | | - g2 | | | */
/* _________ _________ _________ _________ _________ _________ */
/* ************************************************************************** */
this.collection.add([{
id: '111',
kind: 'carto',
letter: 'f',
options: {
table_name: 'punki',
source: 'e1'
}
}, {
id: '765',
kind: 'carto',
letter: 'g',
options: {
table_name: 'alice',
source: 'f2'
}
}, {
id: '888',
kind: 'carto',
letter: 'h',
options: {
table_name: 'robin',
source: 'g2'
}
}]);
this.analysisDefinitionNodesCollection.add([{
id: 'f1',
type: 'buffer',
radio: 300,
distance: 'meters',
source: 'b1'
}, {
id: 'f2',
type: 'buffer',
radio: 300,
distance: 'meters',
source: 'f1'
}, {
id: 'g1',
type: 'buffer',
radio: 300,
distance: 'meters',
source: 'f2'
}, {
id: 'g2',
type: 'buffer',
radio: 300,
distance: 'meters',
source: 'g1'
}, {
id: 'h1',
type: 'buffer',
radio: 300,
distance: 'meters',
source: 'g2'
}]);
expect(this.model.getAllDependentLayers()).toBe(5);
});
});
describe('.isEmpty', function () {
it('should rise an exeption if this function is called', function (done) {
try {
this.model.isEmpty();
} catch (error) {
expect(error).toBeDefined();
done();
}
});
});
describe('.isEmptyAsync', function () {
beforeEach(function () {
this.nodeModel = this.analysisDefinitionNodesCollection.add({
id: 'a1',
type: 'source',
params: {
query: 'SELECT * FROM somewhere'
}
});
this.model.set('source', 'a1');
});
it('should return the promise given by isEmptyAsync method from queryRowsCollection', function (done) {
var response = false;
spyOn(this.nodeModel.queryRowsCollection, 'isEmptyAsync');
this.nodeModel.queryRowsCollection.isEmptyAsync.and.returnValue(Promise.resolve(response));
this.model.isEmptyAsync()
.then(function (isEmpty) {
expect(isEmpty).toEqual(response);
done();
});
});
});
describe('.isDone', function () {
beforeEach(function () {
this.nodeModel = this.analysisDefinitionNodesCollection.add({
id: 'a1',
type: 'source',
params: {
query: 'SELECT * FROM somewhere'
}
});
this.model.set('source', 'a1');
spyOn(this.nodeModel.queryRowsCollection, 'isDone');
spyOn(this.nodeModel.queryGeometryModel, 'isDone');
spyOn(this.nodeModel.querySchemaModel, 'isDone');
});
it('should be false if queryRowsCollection is not done', function () {
this.nodeModel.queryRowsCollection.isDone.and.returnValue(false);
this.nodeModel.queryGeometryModel.isDone.and.returnValue(true);
this.nodeModel.querySchemaModel.isDone.and.returnValue(true);
expect(this.model.isDone()).toEqual(false);
});
it('should be false if queryGeometryModel is not done', function () {
this.nodeModel.queryRowsCollection.isDone.and.returnValue(true);
this.nodeModel.queryGeometryModel.isDone.and.returnValue(false);
this.nodeModel.querySchemaModel.isDone.and.returnValue(true);
expect(this.model.isDone()).toEqual(false);
});
it('should be false if querySchemaModel is not done', function () {
this.nodeModel.queryRowsCollection.isDone.and.returnValue(true);
this.nodeModel.queryGeometryModel.isDone.and.returnValue(true);
this.nodeModel.querySchemaModel.isDone.and.returnValue(false);
expect(this.model.isDone()).toEqual(false);
});
it('should be true if queryRowsCollection, queryGeometryModel and querySchemaModel are done', function () {
this.nodeModel.queryRowsCollection.isDone.and.returnValue(true);
this.nodeModel.queryGeometryModel.isDone.and.returnValue(true);
this.nodeModel.querySchemaModel.isDone.and.returnValue(true);
expect(this.model.isDone()).toEqual(true);
});
});
describe('.canBeGeoreferenced', function () {
beforeEach(function () {
this.nodeModel = this.analysisDefinitionNodesCollection.add({
id: 'a1',
type: 'source',
params: {
query: 'SELECT * FROM somewhere'
}
});
this.model.set('source', 'a1');
spyOn(this.nodeModel.queryRowsCollection, 'isEmptyAsync');
spyOn(this.nodeModel.queryGeometryModel, 'hasValueAsync');
spyOn(this.model, 'getAnalysisDefinitionNodeModel').and.returnValue(this.nodeModel);
spyOn(this.nodeModel, 'isCustomQueryApplied');
});
it('should be true if there is data, no geocoding analysis applied, no custom query or doesn\'t have geom', function (done) {
this.nodeModel.queryRowsCollection.isEmptyAsync.and.returnValue(Promise.resolve(false));
this.nodeModel.queryGeometryModel.hasValueAsync.and.returnValue(Promise.resolve(false));
this.nodeModel.isCustomQueryApplied.and.returnValue(false);
this.model.canBeGeoreferenced()
.then(function (canBeGeoreferenced) {
expect(canBeGeoreferenced).toEqual(true);
done();
});
});
it('should be false if there is no data', function (done) {
this.nodeModel.queryRowsCollection.isEmptyAsync.and.returnValue(Promise.resolve(true));
this.nodeModel.queryGeometryModel.hasValueAsync.and.returnValue(Promise.resolve(false));
this.nodeModel.isCustomQueryApplied.and.returnValue(false);
this.model.canBeGeoreferenced()
.then(function (canBeGeoreferenced) {
expect(canBeGeoreferenced).toEqual(false);
done();
});
});
it('should be false if there is a geocoding analysis applied', function (done) {
this.nodeModel.set('type', 'geocoding');
this.nodeModel.queryRowsCollection.isEmptyAsync.and.returnValue(Promise.resolve(true));
this.nodeModel.queryGeometryModel.hasValueAsync.and.returnValue(Promise.resolve(false));
this.nodeModel.isCustomQueryApplied.and.returnValue(false);
this.model.canBeGeoreferenced()
.then(function (canBeGeoreferenced) {
expect(canBeGeoreferenced).toEqual(false);
done();
});
});
it('should be false if there is a custom query applied', function (done) {
this.nodeModel.queryRowsCollection.isEmptyAsync.and.returnValue(Promise.resolve(true));
this.nodeModel.queryGeometryModel.hasValueAsync.and.returnValue(Promise.resolve(false));
this.nodeModel.isCustomQueryApplied.and.returnValue(true);
this.model.canBeGeoreferenced()
.then(function (canBeGeoreferenced) {
expect(canBeGeoreferenced).toEqual(false);
done();
});
});
it('should be false if there is geom value', function (done) {
this.nodeModel.queryRowsCollection.isEmptyAsync.and.returnValue(Promise.resolve(true));
this.nodeModel.queryGeometryModel.hasValueAsync.and.returnValue(Promise.resolve(true));
this.nodeModel.isCustomQueryApplied.and.returnValue(true);
this.model.canBeGeoreferenced()
.then(function (canBeGeoreferenced) {
expect(canBeGeoreferenced).toEqual(false);
done();
});
});
});
describe('.fetchQueryRowsIfRequired', function () {
var fetchSpy;
beforeEach(function () {
this.nodeModel = this.analysisDefinitionNodesCollection.add({
id: 'a1',
type: 'source',
params: {
query: 'SELECT * FROM somewhere'
}
});
this.model.set('source', 'a1');
this.nodeModel.set('type', 'random');
spyOn(this.nodeModel.queryGeometryModel, 'hasValueAsync');
spyOn(this.nodeModel.queryRowsCollection, 'isUnavailable');
spyOn(this.nodeModel, 'isCustomQueryApplied');
spyOn(this.model, 'getAnalysisDefinitionNodeModel').and.returnValue(this.nodeModel);
fetchSpy = spyOn(this.nodeModel.queryRowsCollection, 'fetch');
});
it('should fetch if everything is ok', function (done) {
this.nodeModel.queryGeometryModel.hasValueAsync.and.returnValue(Promise.resolve(false));
this.nodeModel.isCustomQueryApplied.and.returnValue(false);
this.nodeModel.queryRowsCollection.isUnavailable.and.returnValue(true);
this.model.fetchQueryRowsIfRequired();
setTimeout(function () {
expect(fetchSpy).toHaveBeenCalled();
done();
}, 0);
});
it('should not fetch if it has geocoding', function (done) {
this.nodeModel.set('type', 'geocoding');
this.nodeModel.queryGeometryModel.hasValueAsync.and.returnValue(Promise.resolve(false));
this.nodeModel.isCustomQueryApplied.and.returnValue(false);
this.nodeModel.queryRowsCollection.isUnavailable.and.returnValue(true);
this.model.fetchQueryRowsIfRequired();
setTimeout(function () {
expect(fetchSpy).not.toHaveBeenCalled();
done();
}, 0);
});
it('should not fetch if queryGeometryModel has value', function (done) {
this.nodeModel.set('type', 'fake');
this.nodeModel.queryGeometryModel.hasValueAsync.and.returnValue(Promise.resolve(true));
this.nodeModel.isCustomQueryApplied.and.returnValue(false);
this.nodeModel.queryRowsCollection.isUnavailable.and.returnValue(true);
this.model.fetchQueryRowsIfRequired();
setTimeout(function () {
expect(fetchSpy).not.toHaveBeenCalled();
done();
}, 0);
});
it('should not fetch if there is a custom query applied', function (done) {
this.nodeModel.set('type', 'fake');
this.nodeModel.queryGeometryModel.hasValueAsync.and.returnValue(Promise.resolve(false));
this.nodeModel.isCustomQueryApplied.and.returnValue(true);
this.nodeModel.queryRowsCollection.isUnavailable.and.returnValue(true);
this.model.fetchQueryRowsIfRequired();
setTimeout(function () {
expect(fetchSpy).not.toHaveBeenCalled();
done();
}, 0);
});
it('should not fetch if it is available', function (done) {
this.nodeModel.set('type', 'fake');
this.nodeModel.queryGeometryModel.hasValueAsync.and.returnValue(Promise.resolve(false));
this.nodeModel.isCustomQueryApplied.and.returnValue(false);
this.nodeModel.queryRowsCollection.isUnavailable.and.returnValue(false);
this.model.fetchQueryRowsIfRequired();
setTimeout(function () {
expect(fetchSpy).not.toHaveBeenCalled();
done();
}, 0);
});
});
});
@@ -0,0 +1,919 @@
var _ = require('jquery');
var Backbone = require('backbone');
var ConfigModel = require('builder/data/config-model');
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
var QueryGeometryModel = require('builder/data/query-geometry-model');
var UserModel = require('builder/data/user-model');
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
var layerColors = require('builder/data/layer-colors');
describe('data/layer-definitions-collection', function () {
var collection;
var configModel;
var userModel;
var analysisDefinitionNodesCollection;
var createCollectionFn = function (options) {
configModel = new ConfigModel({
user_name: 'pepe',
base_url: '/u/pepe'
});
userModel = new UserModel({}, {
configModel: configModel
});
analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
configModel: configModel,
userModel: userModel
});
var defaultOptions = {
configModel: configModel,
userModel: userModel,
analysisDefinitionNodesCollection: analysisDefinitionNodesCollection,
mapId: 'm-123',
stateDefinitionModel: {}
};
return new LayerDefinitionsCollection(null, _.extend(defaultOptions, options));
};
beforeEach(function () {
collection = createCollectionFn({});
});
describe('.add', function () {
it('should not have created source nodes for layers that have non-sources', function () {
collection.add({
id: 'A',
kind: 'carto',
options: {
table_name: 'alice',
source: 'a1'
}
});
expect(analysisDefinitionNodesCollection.pluck('id')).not.toContain('a0');
});
it('should create source if it matches expected source node id', function () {
collection.add({
id: 'A',
kind: 'carto',
options: {
table_name: 'alice',
source: 'a0'
}
});
expect(analysisDefinitionNodesCollection.pluck('id')).toContain('a0');
});
});
it('should create the layer with the next letter representation available but only for data layers', function () {
// Adds A-D, like if the collection was created from scratch
var layersData = [
{kind: 'tiled', order: 0}, // a basemap
{kind: 'carto', order: 1, options: {letter: 'b'}},
{kind: 'carto', order: 2},
{kind: 'carto', order: 3, options: {letter: 'a'}},
{kind: 'torque', order: 4}
];
collection.resetByLayersData(layersData);
expect(collection.pluck('letter')).toEqual([undefined, 'b', 'c', 'a', 'd']);
// Remove C
collection.remove(collection.findWhere({letter: 'c'}));
expect(collection.pluck('letter')).toEqual([undefined, 'b', 'a', 'd']);
// Add new as C
collection.add({kind: 'carto', order: 4});
expect(collection.pluck('letter')).toEqual([undefined, 'b', 'a', 'd', 'c']);
// Add new as E, F, G
collection.add([
{kind: 'carto', order: 5},
{kind: 'carto', order: 6},
{kind: 'carto', order: 7}
]);
expect(collection.pluck('letter')).toEqual([undefined, 'b', 'a', 'd', 'c', 'e', 'f', 'g']);
// Add a new tiled (labels-on-top) layer
collection.add({kind: 'tiled', order: 8});
expect(collection.pluck('letter')).toEqual([undefined, 'b', 'a', 'd', 'c', 'e', 'f', 'g', undefined]);
// Remove all existing and add a new, should reset letters
collection.reset([
{kind: 'tiled'},
{kind: 'carto'}
]);
expect(collection.pluck('letter')).toEqual([undefined, 'a']);
});
it('should create the layer with the color that corresponds to the letter', function () {
var layersData = [
{ kind: 'tiled', order: 0 }, // a basemap
{ kind: 'carto', order: 1 },
{ kind: 'carto', order: 2 },
{ kind: 'torque', order: 3 }
];
collection.resetByLayersData(layersData);
expect(collection.pluck('letter')).toEqual([ undefined, 'a', 'b', 'c' ]);
expect(collection.pluck('color')).toEqual([ undefined, layerColors.COLORS[0], layerColors.COLORS[1], layerColors.COLORS[2] ]);
collection.remove(collection.at(0));
expect(collection.pluck('letter')).toEqual([ 'a', 'b', 'c' ]);
expect(collection.pluck('color')).toEqual([ layerColors.COLORS[0], layerColors.COLORS[1], layerColors.COLORS[2] ]);
collection.add({ kind: 'carto' });
expect(collection.pluck('letter')).toEqual([ 'a', 'b', 'c', 'd' ]);
expect(collection.pluck('color')).toEqual([ layerColors.COLORS[0], layerColors.COLORS[1], layerColors.COLORS[2], layerColors.COLORS[3] ]);
// Remove all existing and add a couple of new layers
collection.reset([
{ kind: 'carto' },
{ kind: 'carto' }
]);
expect(collection.pluck('letter')).toEqual([ 'a', 'b' ]);
expect(collection.pluck('color')).toEqual([layerColors.COLORS[0], layerColors.COLORS[1]]);
});
it('should sanitize labels layer when create layers collection', function () {
var layersData = [
{ kind: 'tiled', order: 0 }, // a basemap
{ kind: 'carto', order: 1 },
{ kind: 'tiled', order: 2 },
{ kind: 'carto', order: 3 },
{ kind: 'tiled', order: 4 }
];
collection.resetByLayersData(layersData);
expect(collection.length).toEqual(layersData.length - 1);
expect(collection.filter(function (layer) {
return layer.get('type') === 'Tiled';
}).length).toEqual(2);
});
it('should create a layer with the owner of the table in the query', function () {
var layer1 = collection.add({
kind: 'carto',
options: {
table_name: 'table_name',
user_name: 'paco'
}
});
var sourceNode1 = analysisDefinitionNodesCollection.get(layer1.get('source'));
expect(layer1.get('sql').toLowerCase()).toBe('select * from paco.table_name');
expect(sourceNode1.get('query').toLowerCase()).toBe('select * from paco.table_name');
var layer2 = collection.add({
kind: 'carto',
options: {
table_name: '000cd294-b124-4f82-b569-0f7fe41d2db8',
user_name: 'paco'
}
});
var sourceNode2 = analysisDefinitionNodesCollection.get(layer2.get('source'));
expect(layer2.get('sql').toLowerCase()).toBe('select * from paco."000cd294-b124-4f82-b569-0f7fe41d2db8"');
expect(sourceNode2.get('query').toLowerCase()).toBe('select * from paco."000cd294-b124-4f82-b569-0f7fe41d2db8"');
var layer3 = collection.add({
kind: 'carto',
options: {
table_name: 'table_name',
user_name: 'pepe'
}
});
var sourceNode3 = analysisDefinitionNodesCollection.get(layer3.get('source'));
expect(layer3.get('sql').toLowerCase()).toBe('select * from table_name');
expect(sourceNode3.get('query').toLowerCase()).toBe('select * from table_name');
var layer4 = collection.add({
kind: 'carto',
options: {
table_name: 'paco.table_name',
user_name: ''
}
});
var sourceNode4 = analysisDefinitionNodesCollection.get(layer4.get('source'));
expect(layer4.get('sql').toLowerCase()).toBe('select * from paco.table_name');
expect(sourceNode4.get('query').toLowerCase()).toBe('select * from paco.table_name');
});
describe('.save', function () {
beforeEach(function () {
spyOn(Backbone, 'sync');
var layer1 = new Backbone.Model({ id: 'layer1', type: 'Tiled' });
var layer2 = new Backbone.Model({ id: 'layer2', type: 'CartoDB' });
var layer3 = new Backbone.Model({ id: 'layer3', type: 'CartoDB' });
collection.reset([
layer3,
layer1,
layer2
]);
});
it('should order and save the entire collection when all analysis definitions have been created', function () {
var options = {};
collection.save(options);
expect(collection.pluck('id')).toEqual(['layer3', 'layer1', 'layer2']);
expect(Backbone.sync).toHaveBeenCalledWith('update', collection, options);
});
});
describe('.getNumberOfDataLayers', function () {
it('should return the number of CartoDB and Torque layers', function () {
expect(collection.getNumberOfDataLayers()).toEqual(0);
collection.reset([
{ kind: 'tiled' },
{ kind: 'carto' },
{ kind: 'torque' },
{ kind: 'background' }
]);
expect(collection.getNumberOfDataLayers()).toEqual(2);
});
});
describe('.findPrimaryParentLayerToAnalysisNode', function () {
beforeEach(function () {
var b1 = analysisDefinitionNodesCollection.add({ // eslint-disable-line no-unused-vars
id: 'b1',
type: 'buffer',
params: {
radius: 3,
source: {
id: 'a2',
type: 'buffer',
params: {
radius: 2,
source: {
id: 'a1',
type: 'buffer',
params: {
radius: 1,
source: {
id: 'a0',
type: 'source',
params: {
query: 'SELECT * FROM foobar'
}
}
}
}
}
}
}
});
collection.add([
{
id: 'A',
kind: 'carto',
options: {
source: 'a2'
}
}, {
id: 'B',
kind: 'carto',
options: {
source: 'b1'
}
}
]);
});
it('should return a parent layer if given node is a direct node below a node in another layer', function () {
var a2 = analysisDefinitionNodesCollection.get('a2');
expect(collection.findPrimaryParentLayerToAnalysisNode(a2).id).toEqual('B');
});
it('should not return anything if found layer should be excluded', function () {
var a2 = analysisDefinitionNodesCollection.get('a2');
var opts = {exclude: collection.get('B')};
expect(collection.findPrimaryParentLayerToAnalysisNode(a2, opts)).toBeUndefined();
opts = {exclude: [collection.get('B')]};
expect(collection.findPrimaryParentLayerToAnalysisNode(a2, opts)).toBeUndefined();
});
it('should not return anything if given node has no parent layer immediate above it', function () {
var a1 = analysisDefinitionNodesCollection.get('a1');
expect(collection.findPrimaryParentLayerToAnalysisNode(a1)).toBeUndefined();
var b1 = analysisDefinitionNodesCollection.get('b1');
expect(collection.findPrimaryParentLayerToAnalysisNode(b1)).toBeUndefined();
});
});
describe('layers position methods', function () {
beforeEach(function () {
collection.reset([
{ kind: 'tiled' },
{ kind: 'carto' },
{ kind: 'carto' },
{ kind: 'torque' },
{ kind: 'tiled' }
]);
});
it('.isDataLayerOnTop', function () {
expect(collection.isDataLayerOnTop(collection.at(1))).toBeFalsy();
expect(collection.isDataLayerOnTop(collection.at(2))).toBeFalsy();
expect(collection.isDataLayerOnTop(collection.at(3))).toBeTruthy();
// Removing "labels on top" layer
collection.remove(collection.last());
expect(collection.isDataLayerOnTop(collection.at(1))).toBeFalsy();
expect(collection.isDataLayerOnTop(collection.at(2))).toBeFalsy();
expect(collection.isDataLayerOnTop(collection.at(3))).toBeTruthy();
// Removing torque layer
collection.remove(collection.last());
expect(collection.isDataLayerOnTop(collection.at(1))).toBeFalsy();
expect(collection.isDataLayerOnTop(collection.at(2))).toBeTruthy();
});
it('.getTopDataLayerIndex', function () {
expect(collection.getTopDataLayerIndex()).toBe(3);
// Removing "labels on top" layer
collection.remove(collection.last());
expect(collection.getTopDataLayerIndex()).toBe(3);
// Removing torque layer
collection.remove(collection.last());
expect(collection.getTopDataLayerIndex()).toBe(2);
});
});
describe('.setBaseLayer', function () {
var changingBaseLayerCallback;
var baseLayerChangedCallback;
var baseLayerFailedCallback;
beforeEach(function () {
spyOn(Backbone, 'sync').and.returnValue({
always: function () {}
});
spyOn(Backbone.Model.prototype, 'save').and.callThrough();
spyOn(Backbone.Model.prototype, 'destroy').and.callThrough();
changingBaseLayerCallback = jasmine.createSpy('changingBaseLayer');
baseLayerChangedCallback = jasmine.createSpy('baseLayerChanged');
baseLayerFailedCallback = jasmine.createSpy('baseLayerFailed');
collection.on('changingBaseLayer', changingBaseLayerCallback);
collection.on('baseLayerChanged', baseLayerChangedCallback);
collection.on('baseLayerFailed', baseLayerFailedCallback);
});
describe('when trying to set the same baseLayer', function () {
beforeEach(function () {
collection.reset([
{
kind: 'tiled',
options: {
name: 'Positron',
category: 'CartoDB'
}
}
]);
});
it('should trigger events', function () {
var changingBaseLayerCallback = jasmine.createSpy('changingBaseLayer');
var baseLayerChangedCallback = jasmine.createSpy('baseLayerChanged');
collection.on('changingBaseLayer', changingBaseLayerCallback);
collection.on('baseLayerChanged', baseLayerChangedCallback);
collection.setBaseLayer({ type: 'Tiled', name: 'Positron', category: 'CartoDB' });
expect(collection.size()).toEqual(1);
expect(changingBaseLayerCallback).toHaveBeenCalled();
expect(baseLayerChangedCallback).toHaveBeenCalled();
});
});
it('should unset previous base layer attributes that are not present in the new layer (eg: urlTemplate)', function () {
Backbone.sync.and.callFake(function (method, model, options) {
options && options.success({
id: 'LAYER_ID',
kind: 'gmapsbase',
order: 0,
options: {
type: 'GMapsBase',
baseType: 'BASE_TYPE'
}
});
return {
always: function () {}
};
});
collection.reset([
{
id: 'LAYER_ID',
kind: 'tiled',
options: {
type: 'Tiled',
urlTemplate: 'URL_TEMPLATE'
}
}
]);
var baseLayer = collection.at(0);
expect(baseLayer.attributes).toEqual({ id: 'LAYER_ID', type: 'Tiled', urlTemplate: 'URL_TEMPLATE' });
collection.setBaseLayer({
type: 'GMapsBase',
category: 'GMaps',
baseType: 'hybrid'
});
// Ensure a request to update the object is made
expect(Backbone.sync).toHaveBeenCalledWith(
'update',
jasmine.objectContaining({
id: 'LAYER_ID'
}),
jasmine.objectContaining({ silent: true, wait: true })
);
// Attributes no longer contains the urlTemplate attr, which only applies to Tiled layers
expect(baseLayer.attributes).toEqual({
id: 'LAYER_ID',
type: 'GMapsBase',
autoStyle: false,
order: 0,
category: 'GMaps',
baseType: 'BASE_TYPE'
});
});
describe("when the current baseLayer doesn't have labels", function () {
beforeEach(function () {
collection.reset([
{
id: '1',
kind: 'tiled',
options: {
name: 'Positron',
category: 'CartoDB'
}
}
]);
});
describe("when the new baseLayer doesnt't have labels", function () {
beforeEach(function () {
collection.setBaseLayer({
type: 'Tiled',
name: 'Dark matter',
category: 'CartoDB'
});
});
it('should trigger a "changingBaseLayer event', function () {
expect(changingBaseLayerCallback.calls.count()).toEqual(1);
});
it('should save the existing base layer', function () {
expect(Backbone.Model.prototype.save.calls.count()).toEqual(1);
});
describe('when the base layer is saved', function () {
beforeEach(function () {
Backbone.sync.calls.argsFor(0)[2].success({
id: '1',
kind: 'tiled',
options: {
name: 'Dark matter',
category: 'CartoDB'
}
});
});
it('should update the base layer', function () {
expect(collection.first().toJSON()).toEqual({
id: '1',
kind: 'tiled',
options: {
name: 'Dark matter',
category: 'CartoDB',
type: 'Tiled'
},
order: 0
});
});
it('should trigger a "baseLayerChanged" event', function () {
expect(baseLayerChangedCallback.calls.count()).toEqual(1);
});
});
describe('when the base layer is NOT saved', function () {
beforeEach(function () {
Backbone.sync.calls.argsFor(0)[2].error();
});
it('should trigger a "baseLayerFailed" event', function () {
expect(baseLayerFailedCallback).toHaveBeenCalled();
});
});
});
describe('when the new baseLayer has labels', function () {
beforeEach(function () {
collection.setBaseLayer({
type: 'Tiled',
name: 'Dark matter',
category: 'CartoDB',
labels: {
urlTemplate: 'http://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}.png'
}
});
});
it('should trigger a "changingBaseLayer event', function () {
expect(changingBaseLayerCallback.calls.count()).toEqual(1);
});
it('should save the existing baseLayer', function () {
expect(Backbone.Model.prototype.save.calls.count()).toEqual(1);
});
describe('when the base layer is saved', function () {
beforeEach(function () {
Backbone.sync.calls.argsFor(0)[2].success({
id: '1',
kind: 'tiled',
options: {
name: 'Dark matter',
category: 'CartoDB',
labels: {
urlTemplate: 'http://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}.png'
}
}
});
});
it('should update the base layer', function () {
expect(collection.first().toJSON()).toEqual({
id: '1',
kind: 'tiled',
options: {
name: 'Dark matter',
category: 'CartoDB',
type: 'Tiled',
labels: {
urlTemplate: 'http://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}.png'
}
},
order: 0
});
});
describe('when the labels layer is saved', function () {
beforeEach(function () {
Backbone.sync.calls.argsFor(1)[2].success({
kind: 'tiled',
options: {
name: 'Dark matter Labels',
category: 'CartoDB',
urlTemplate: 'http://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}.png'
},
order: 1
});
});
it('should trigger a "baseLayerChanged" event', function () {
expect(baseLayerChangedCallback.calls.count()).toEqual(1);
});
it('should create the labels layer', function () {
expect(collection.last().toJSON()).toEqual({
kind: 'tiled',
options: {
type: 'Tiled',
name: 'Dark matter Labels',
category: 'CartoDB',
urlTemplate: 'http://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}.png'
},
order: 1
});
});
it('should add the layer to the collection', function () {
expect(collection.size()).toEqual(2);
});
});
describe('when the labels layer is NOT saved', function () {
beforeEach(function () {
Backbone.sync.calls.argsFor(1)[2].error();
});
it('should trigger a "baseLayerFailed" event', function () {
expect(baseLayerFailedCallback.calls.count()).toEqual(1);
});
it('should NOT add the layer to the collection', function () {
expect(collection.size()).toEqual(1);
});
});
});
describe('when the base layer is NOT saved', function () {
beforeEach(function () {
Backbone.sync.calls.argsFor(0)[2].error();
});
it('should NOT create the labels layer', function () {
expect(Backbone.Model.prototype.save.calls.count()).toEqual(1);
expect(collection.size()).toEqual(1);
});
it('should trigger a "baseLayerFailed" event', function () {
expect(baseLayerFailedCallback.calls.count()).toEqual(1);
});
});
});
});
describe('when the current baseLayer has labels', function () {
beforeEach(function () {
collection.reset([
{
id: '1',
kind: 'tiled',
options: {
name: 'Positron',
category: 'CartoDB',
type: 'Tiled'
},
order: 0
},
{
id: '2',
kind: 'tiled',
options: {
type: 'Tiled',
name: 'Positrong Labels',
category: 'CartoDB',
urlTemplate: 'http://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}.png'
},
order: 1
}
]);
});
describe("when the new baseLayer doesnt't have labels", function () {
beforeEach(function () {
collection.setBaseLayer({
type: 'Tiled',
name: 'Dark matter',
category: 'CartoDB'
});
});
it('should trigger a "changingBaseLayer event', function () {
expect(changingBaseLayerCallback.calls.count()).toEqual(1);
});
it('should save the existing baseLayer', function () {
expect(Backbone.Model.prototype.save.calls.count()).toEqual(1);
});
describe('when the base layer is saved', function () {
beforeEach(function () {
Backbone.sync.calls.argsFor(0)[2].success({
id: '1',
kind: 'tiled',
options: {
name: 'Dark matter',
category: 'CartoDB'
}
});
});
it('should update the base layer', function () {
expect(collection.first().toJSON()).toEqual({
id: '1',
kind: 'tiled',
options: {
name: 'Dark matter',
category: 'CartoDB',
type: 'Tiled'
},
order: 0
});
});
it('should remove the labels layer', function () {
expect(Backbone.Model.prototype.destroy).toHaveBeenCalled();
});
describe('when the labels layer is destroyed', function () {
beforeEach(function () {
Backbone.sync.calls.argsFor(1)[2].success();
});
it('should trigger a "baseLayerChanged" event', function () {
expect(baseLayerChangedCallback.calls.count()).toEqual(1);
});
it('should remove the layer from the collection', function () {
expect(collection.size()).toEqual(1);
});
});
describe('when the labels layer is NOT destroyed', function () {
beforeEach(function () {
Backbone.sync.calls.argsFor(1)[2].error();
});
it('should trigger a "baseLayerFailed" event', function () {
expect(baseLayerFailedCallback.calls.count()).toEqual(1);
});
it('should NOT remove the layer from the collection', function () {
expect(collection.size()).toEqual(2);
});
});
});
describe('when the base layer is NOT saved', function () {
beforeEach(function () {
Backbone.sync.calls.argsFor(0)[2].error();
});
it('should trigger a "baseLayerFailed" event', function () {
expect(baseLayerFailedCallback).toHaveBeenCalled();
});
});
});
describe('when the new baseLayer has labels', function () {
beforeEach(function () {
collection.setBaseLayer({
type: 'Tiled',
name: 'Dark matter',
category: 'CartoDB',
labels: {
urlTemplate: 'http://{s}.basemaps.cartocdn.com/dark_only_labels/{z}/{x}/{y}.png'
}
});
});
it('should trigger a "changingBaseLayer event', function () {
expect(changingBaseLayerCallback.calls.count()).toEqual(1);
});
it('should save the existing baseLayer', function () {
expect(Backbone.Model.prototype.save.calls.count()).toEqual(1);
});
describe('when the base layer is saved', function () {
beforeEach(function () {
Backbone.sync.calls.argsFor(0)[2].success({
id: '1',
kind: 'tiled',
options: {
name: 'Dark matter',
category: 'CartoDB',
labels: {
urlTemplate: 'http://{s}.basemaps.cartocdn.com/dark_only_labels/{z}/{x}/{y}.png'
}
}
});
});
it('should update the base layer', function () {
expect(collection.first().toJSON()).toEqual({
id: '1',
kind: 'tiled',
options: {
name: 'Dark matter',
category: 'CartoDB',
type: 'Tiled',
labels: {
urlTemplate: 'http://{s}.basemaps.cartocdn.com/dark_only_labels/{z}/{x}/{y}.png'
}
},
order: 0
});
});
describe('when the labels layer is saved', function () {
beforeEach(function () {
Backbone.sync.calls.argsFor(1)[2].success({
kind: 'tiled',
options: {
name: 'Dark matter Labels',
category: 'CartoDB',
urlTemplate: 'http://{s}.basemaps.cartocdn.com/dark_only_labels/{z}/{x}/{y}.png'
},
order: 1
});
});
it('should trigger a "baseLayerChanged" event', function () {
expect(baseLayerChangedCallback.calls.count()).toEqual(1);
});
it('should update the labels layer', function () {
expect(collection.last().toJSON()).toEqual({
id: '2',
kind: 'tiled',
options: {
type: 'Tiled',
name: 'Dark matter Labels',
category: 'CartoDB',
urlTemplate: 'http://{s}.basemaps.cartocdn.com/dark_only_labels/{z}/{x}/{y}.png'
},
order: 1
});
});
});
describe('when the labels layer is NOT saved', function () {
beforeEach(function () {
Backbone.sync.calls.argsFor(1)[2].error();
});
it('should trigger a "baseLayerFailed" event', function () {
expect(baseLayerFailedCallback.calls.count()).toEqual(1);
});
});
});
describe('when the base layer is NOT saved', function () {
beforeEach(function () {
Backbone.sync.calls.argsFor(0)[2].error();
});
it('should NOT update the labels layer', function () {
expect(collection.last().get('urlTemplate')).toEqual('http://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}.png');
});
it('should trigger a "baseLayerFailed" event', function () {
expect(baseLayerFailedCallback.calls.count()).toEqual(1);
});
});
});
});
});
describe('.isThereAnyGeometryData', function () {
beforeEach(function () {
collection.add({
id: 'A',
kind: 'carto',
options: {
table_name: 'alice',
source: 'a1'
}
});
analysisDefinitionNodesCollection.add({
id: 'b1',
type: 'buffer',
params: {
radius: 3,
source: {
id: 'a1',
type: 'buffer',
params: {}
}
}
});
});
it('should be true if there is a layer with geometry data', function (done) {
spyOn(QueryGeometryModel.prototype, 'hasValueAsync').and.returnValue(Promise.resolve(true));
collection.isThereAnyGeometryData()
.then(function (response) {
expect(response).toEqual(true);
done();
});
});
it('should be false if there is no layer with geometry data', function (done) {
spyOn(QueryGeometryModel.prototype, 'hasValueAsync').and.returnValue(Promise.resolve(false));
collection.isThereAnyGeometryData()
.then(function (response) {
expect(response).toEqual(false);
done();
});
});
});
});
@@ -0,0 +1,65 @@
var _ = require('underscore');
var layerTypesAndKinds = require('builder/data/layer-types-and-kinds');
describe('data/layer-types-and-kinds', function () {
describe('.getKind', function () {
it('should return the kind for a given type', function () {
expect(layerTypesAndKinds.getKind('Tiled')).toEqual('tiled');
expect(layerTypesAndKinds.getKind('CartoDB')).toEqual('carto');
expect(layerTypesAndKinds.getKind('WMS')).toEqual('wms');
expect(layerTypesAndKinds.getKind('Plain')).toEqual('background');
expect(layerTypesAndKinds.getKind('GMapsBase')).toEqual('gmapsbase');
expect(layerTypesAndKinds.getKind('torque')).toEqual('torque');
});
it('should throw exception when type is unknown', function () {
expect(function () {
layerTypesAndKinds.getKind(null);
}).toThrow();
});
});
describe('.getType', function () {
it('should return the type for a given kind', function () {
expect(layerTypesAndKinds.getType('tiled')).toEqual('Tiled');
expect(layerTypesAndKinds.getType('carto')).toEqual('CartoDB');
expect(layerTypesAndKinds.getType('wms')).toEqual('WMS');
expect(layerTypesAndKinds.getType('background')).toEqual('Plain');
expect(layerTypesAndKinds.getType('gmapsbase')).toEqual('GMapsBase');
expect(layerTypesAndKinds.getType('torque')).toEqual('torque');
});
it('should throw exception when kind is unknown', function () {
expect(function () {
layerTypesAndKinds.getType(null);
}).toThrow();
});
});
describe('.isKindDataLayer', function () {
it('should return true if matching', function () {
expect(layerTypesAndKinds.isKindDataLayer('carto')).toBe(true);
expect(layerTypesAndKinds.isKindDataLayer('torque')).toBe(true);
expect(layerTypesAndKinds.isKindDataLayer('tiled')).toBe(false);
expect(layerTypesAndKinds.isKindDataLayer('cartooo')).toBe(false);
});
});
_.each({
'isCartoDBType': 'CartoDB',
'isTiledType': 'Tiled',
'isTorqueType': 'torque',
'isPlainType': 'Plain'
}, function (type, method) {
describe('.' + method, function () {
it('should return true if type is "' + type + '"', function () {
expect(layerTypesAndKinds[method](type)).toBe(true);
});
it('should return false when given a different type', function () {
expect(layerTypesAndKinds[method]('invalid type')).toBe(false);
});
});
});
});
@@ -0,0 +1,35 @@
var Backbone = require('backbone');
var ConfigModel = require('builder/data/config-model');
var LegendDefinitionModel = require('builder/data/legends/legend-bubble-definition-model');
describe('data/legends/legend-bubble-defintion-model', function () {
var style;
var layerDef1;
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
layerDef1 = new Backbone.Model({
id: 'fa6cf872-fffa-4301-9a60-849cedba7864',
table_name: 'foo',
autoStyle: false
});
style = new Backbone.Model();
style.hasNoneStyles = function () {
return false;
};
layerDef1.styleModel = style;
this.model = new LegendDefinitionModel(null, {
configModel: configModel,
layerDefinitionModel: layerDef1,
vizId: 'v-123'
});
spyOn(this.model, '_inheritStyleColor');
spyOn(this.model, 'save');
});
});
@@ -0,0 +1,39 @@
var _ = require('underscore');
var Backbone = require('backbone');
var ConfigModel = require('builder/data/config-model');
var LegendDefinitionModel = require('builder/data/legends/legend-choropleth-definition-model');
describe('data/legends/legend-choropleth-defintion-model', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
var layerDef1 = new Backbone.Model({
id: 'fa6cf872-fffa-4301-9a60-849cedba7864',
table_name: 'foo'
});
this.model = new LegendDefinitionModel(null, {
configModel: configModel,
layerDefinitionModel: layerDef1,
vizId: 'v-123'
});
});
it('should toJSON properly', function () {
var attrs = this.model.toJSON();
expect(attrs.definition).toBeDefined();
expect(_.isEmpty(attrs.definition)).toBe(true);
this.model.set({prefix: 'foo'});
attrs = this.model.toJSON();
expect(attrs.definition.prefix).toBeDefined();
expect(attrs.definition.suffix).toBeUndefined();
this.model.set({suffix: 'bar'});
attrs = this.model.toJSON();
expect(attrs.definition.prefix).toBe('foo');
expect(attrs.definition.suffix).toBe('bar');
});
});
@@ -0,0 +1,39 @@
var Backbone = require('backbone');
var ConfigModel = require('builder/data/config-model');
var LegendCustomChoroplethDefinitionModel = require('builder/data/legends/legend-custom-choropleth-definition-model');
describe('data/legends/legend-choropleth-defintion-model', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
var layerDef1 = new Backbone.Model({
id: 'fa6cf872-fffa-4301-9a60-849cedba7864',
table_name: 'foo'
});
this.model = new LegendCustomChoroplethDefinitionModel(null, {
configModel: configModel,
layerDefinitionModel: layerDef1,
vizId: 'v-123'
});
});
it('should toJSON properly', function () {
var attrs = this.model.toJSON();
expect(attrs.definition).toBeDefined();
expect(attrs.definition.prefix).toBeUndefined();
expect(attrs.definition.suffix).toBeUndefined();
expect(attrs.definition.left_label).toBeUndefined();
expect(attrs.definition.right_Label).toBeUndefined();
expect(attrs.definition.colors.length).toBe(0);
this.model.set({ prefix: 'foo', suffix: 'bar', leftLabel: 'low', rightLabel: 'high' });
attrs = this.model.toJSON();
expect(attrs.definition.prefix).toBe('foo');
expect(attrs.definition.suffix).toBe('bar');
expect(attrs.definition.left_label).toBe('low');
expect(attrs.definition.right_label).toBe('high');
});
});
@@ -0,0 +1,120 @@
var _ = require('underscore');
var Backbone = require('backbone');
var ConfigModel = require('builder/data/config-model');
var LegendDefinitionModel = require('builder/data/legends/legend-custom-definition-model');
var StyleHelper = require('builder/helpers/style');
describe('data/legends/legend-custom-defintion-model', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
var layerDef1 = new Backbone.Model({
id: 'fa6cf872-fffa-4301-9a60-849cedba7864',
table_name: 'foo'
});
this.model = new LegendDefinitionModel({
items: [
{
title: '',
color: '#fabada'
},
{
title: 'Foo',
color: '#f4b4d4',
icon: 'http://image.io/building.png'
},
{
title: 'Bar',
color: '#f4b4d4',
image: 'http://image.io/logo.svg'
}
]
}, {
configModel: configModel,
layerDefinitionModel: layerDef1,
vizId: 'v-123'
});
});
describe('.toJSON', function () {
it('should toJSON properly', function () {
var attrs = this.model.toJSON();
expect(attrs.definition).toBeDefined();
expect(_.isArray(attrs.definition.categories)).toBe(true);
expect(attrs.definition.categories.length).toBe(3);
expect(attrs.definition.categories[0].title).toBe('');
expect(attrs.definition.categories[1].title).toContain('Foo');
expect(attrs.definition.categories[1].icon).toContain('http://image.io/building.png');
expect(attrs.definition.categories[2].icon).toContain('http://image.io/logo.svg');
});
it('should convert legend item title to String', function () {
var items = this.model.get('items');
items.push({
title: 1,
color: '#000111'
});
var attrs = this.model.toJSON();
expect(attrs.definition.categories.length).toBe(4);
expect(attrs.definition.categories[3].title).toBe('1');
items.push({
title: null,
color: '#000111'
});
attrs = this.model.toJSON();
expect(attrs.definition.categories.length).toBe(5);
expect(attrs.definition.categories[4].title).toBe('');
});
});
describe('inheritStyleCategories', function () {
it('should return proper output when fixed provided', function () {
var style = new Backbone.Model({
fill: {
color: {
fixed: '#fabada',
image: 'icon.svg'
}
}
});
var items = StyleHelper.getStyleCategories(style);
expect(items.length).toBe(1);
expect(items[0].color).toEqual('#fabada');
expect(items[0].icon).toEqual('icon.svg');
expect(items[0].title).toEqual('');
});
it('should return proper output when range provided', function () {
var style = new Backbone.Model({
fill: {
color: {
range: ['#fabada', '#c0ffee', '#0ff1ce'],
domain: ['Asturias', 'Starbucks', ''],
images: ['', 'starbucks.png', '']
}
}
});
var items = StyleHelper.getStyleCategories(style);
expect(items.length).toBe(3);
expect(items[0].color).toEqual('#fabada');
expect(items[0].title).toEqual('Asturias');
expect(items[0].icon).toEqual('');
expect(items[1].color).toEqual('#c0ffee');
expect(items[1].title).toEqual('Starbucks');
expect(items[1].icon).toEqual('starbucks.png');
expect(items[2].color).toEqual('#0ff1ce');
expect(items[2].title).toEqual('editor.legend.legend-form.others');
expect(items[2].icon).toEqual('');
});
});
});
@@ -0,0 +1,140 @@
var Backbone = require('backbone');
var LegendDefinitionCollection = require('builder/data/legends/legend-definitions-collection');
var ConfigModel = require('builder/data/config-model');
describe('data/legends/legend-definitions-collection', function () {
var legendDefCollection;
var layerDefCollection;
var vizJSON;
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
var styleModel = new Backbone.Model({
fill: {
size: {
attribute: 'n_anclajes',
range: [5, 20]
},
color: {
bins: 3,
range: ['#ffeda0', '#feb24c', '#f03b20'],
attribute: 'n_anclajes'
}
}
});
var layerDef1 = new Backbone.Model({
id: 'fa6cf872-fffa-4301-9a60-849cedba7864',
table_name: 'foo'
});
layerDef1.styleModel = styleModel;
var layerDef2 = new Backbone.Model({
id: 'fa6cf872-fffa-4301-9a60-849cedba7865',
table_name: 'bar'
});
layerDef2.styleModel = styleModel;
layerDefCollection = new Backbone.Collection([]);
layerDefCollection.add(layerDef1);
layerDefCollection.add(layerDef2);
vizJSON = {
layers: [
{
id: 'fa6cf872-fffa-4301-9a60-849cedba7864',
type: 'CartoDB',
legends: [
{
type: 'bubble',
title: 'My Bubble Legend',
fill_color: '#FABADA'
},
{
type: 'category',
title: 'My Category Legend',
prefix: 'prefix',
sufix: 'sufix'
},
{
type: 'choropleth',
title: 'My Choropleth Legend',
prefix: 'prefix',
sufix: 'sufix'
},
{
type: 'custom',
title: 'My Custom Legend',
items: [
{ name: 'Category 1', type: 'color', color: '#CACACA' },
{ name: 'Category 2', type: 'color', color: '#FABADA' }
]
}
]
},
{
id: 'fa6cf872-fffa-4301-9a60-849cedba7865',
type: 'CartoDB',
legends: [
{
type: 'bubble',
title: 'My Bubble Legend',
fill_color: '#FABADA'
},
{
type: 'category',
title: 'My Category Legend',
prefix: 'prefix',
sufix: 'sufix'
}
]
}
]
};
legendDefCollection = new LegendDefinitionCollection([], {
configModel: configModel,
layerDefinitionsCollection: layerDefCollection,
vizId: 'v-123'
});
legendDefCollection.resetByData(vizJSON);
});
it('should be populated properly', function () {
expect(legendDefCollection.length).toBe(6);
expect(legendDefCollection.at(0).get('type')).toBe('bubble');
expect(legendDefCollection.at(0).layerDefinitionModel.get('id')).toBe('fa6cf872-fffa-4301-9a60-849cedba7864');
expect(legendDefCollection.at(2).get('type')).toBe('choropleth');
expect(legendDefCollection.at(2).layerDefinitionModel.get('id')).toBe('fa6cf872-fffa-4301-9a60-849cedba7864');
expect(legendDefCollection.at(5).get('type')).toBe('category');
expect(legendDefCollection.at(5).layerDefinitionModel.get('id')).toBe('fa6cf872-fffa-4301-9a60-849cedba7865');
});
it('should search by layerDefModel properly', function () {
var layerDefModel = layerDefCollection.at(0);
var legends = legendDefCollection.findByLayerDefModel(layerDefModel);
expect(legends.length).toBe(4);
expect(legends[0].get('type')).toBe('bubble');
expect(legends[1].get('type')).toBe('category');
expect(legends[2].get('type')).toBe('choropleth');
});
it('should search by type properly', function () {
var layerDefModel = layerDefCollection.at(0);
var legend = legendDefCollection.findByLayerDefModelAndType(layerDefModel, 'choropleth');
expect(legend.layerDefinitionModel).toBe(layerDefModel);
expect(legend.get('title')).toBe('My Choropleth Legend');
});
it('should search by types properly', function () {
var layerDefModel = layerDefCollection.at(1);
var legends = legendDefCollection.findByLayerDefModelAndTypes(layerDefModel, ['category', 'choropleth', 'custom']);
expect(legends.length).toBe(1);
expect(legends[0].get('type')).toBe('category');
expect(legends[0].layerDefinitionModel).toBe(layerDefModel);
});
});
@@ -0,0 +1,94 @@
var _ = require('underscore');
var Backbone = require('backbone');
var ConfigModel = require('builder/data/config-model');
var LegendDefinitionModel = require('builder/data/legends/legend-torque-definition-model');
var StyleHelper = require('builder/helpers/style');
describe('data/legends/legend-torque-defintion-model', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
var layerDef1 = new Backbone.Model({
id: 'fa6cf872-fffa-4301-9a60-849cedba7864',
table_name: 'foo'
});
this.model = new LegendDefinitionModel({
items: [
{
title: 'Carto',
color: '#fabada'
},
{
title: 'Foo',
color: '#99cc66'
},
{
title: null,
color: '#996600'
}
]
}, {
configModel: configModel,
layerDefinitionModel: layerDef1,
vizId: 'v-123'
});
});
it('should toJSON properly', function () {
var attrs = this.model.toJSON();
expect(attrs.definition).toBeDefined();
expect(_.isArray(attrs.definition.categories)).toBe(true);
expect(attrs.definition.categories.length).toBe(3);
expect(attrs.definition.categories[0].title).toBe('Carto');
expect(attrs.definition.categories[1].title).toContain('Foo');
expect(attrs.definition.categories[2].title).toBe('');
});
describe('inheritStyleCategories', function () {
it('should return proper output when fixed provided', function () {
var style = new Backbone.Model({
fill: {
color: {
fixed: '#fabada',
image: 'icon.svg'
}
}
});
var items = StyleHelper.getStyleCategories(style);
expect(items.length).toBe(1);
expect(items[0].color).toEqual('#fabada');
expect(items[0].icon).toEqual('icon.svg');
expect(items[0].title).toEqual('');
});
it('should return proper output when range provided', function () {
var style = new Backbone.Model({
fill: {
color: {
range: ['#fabada', '#c0ffee', '#0ff1ce'],
domain: ['Asturias', 'Starbucks', ''],
images: ['', 'starbucks.png', '']
}
}
});
var items = StyleHelper.getStyleCategories(style);
expect(items.length).toBe(3);
expect(items[0].color).toEqual('#fabada');
expect(items[0].title).toEqual('Asturias');
expect(items[0].icon).toEqual('');
expect(items[1].color).toEqual('#c0ffee');
expect(items[1].title).toEqual('Starbucks');
expect(items[1].icon).toEqual('starbucks.png');
expect(items[2].color).toEqual('#0ff1ce');
expect(items[2].title).toEqual('editor.legend.legend-form.others');
expect(items[2].icon).toEqual('');
});
});
});
@@ -0,0 +1,86 @@
var Backbone = require('backbone');
var LegendsState = require('builder/data/legends/legends-state');
describe('data/legends/legends-state', function () {
beforeAll(function () {
this.layerDefinitionModel = new Backbone.Model({
id: 'l-1'
});
this.layerDefinitionsCollection = new Backbone.Collection();
this.layerDefinitionsCollection.add(this.layerDefinitionModel);
this.legendDefinitionsCollection = new Backbone.Collection();
var legendModel = new Backbone.Model({
type: 'choropleth',
title: 'foo',
conf: {
columns: ['title']
}
});
legendModel.layerDefinitionModel = this.layerDefinitionModel;
this.legendDefinitionsCollection.add(legendModel);
LegendsState.init(this.layerDefinitionsCollection, this.legendDefinitionsCollection);
});
it('should be populated properly', function () {
var instance = LegendsState.getInstance();
expect(instance['l-1']).toBeDefined();
expect(instance['l-1']['color']).toBeDefined();
expect(instance['l-1']['color']['title']).toBe('foo');
expect(instance['l-1']['size']).toBeUndefined();
});
describe('get', function () {
it('should return existing state properly', function () {
var state = LegendsState.get(this.layerDefinitionModel, 'choropleth');
expect(state.title).toBeDefined();
expect(state.title).toBe('foo');
});
it('should return undefined for non existint state', function () {
var state = LegendsState.get(this.layerDefinitionModel, 'bubble');
expect(state).toBeUndefined();
});
});
describe('set', function () {
it('should set properly', function () {
LegendsState.set(this.layerDefinitionModel, 'bubble', {title: 'bar'});
var instance = LegendsState.getInstance();
expect(instance['l-1']['size']).toBeDefined();
expect(instance['l-1']['size']['title']).toBe('bar');
});
it('should set properly for new layers', function () {
LegendsState.set(new Backbone.Model({id: 'l-2'}), 'custom', {title: 'pop'});
var instance = LegendsState.getInstance();
expect(instance['l-2']['color']).toBeDefined();
expect(instance['l-2']['color']['title']).toBe('pop');
});
});
describe('events', function () {
it('add layer', function () {
var layer = new Backbone.Model({id: 'l-200'});
this.layerDefinitionsCollection.add(layer);
var instance = LegendsState.getInstance();
expect(instance['l-200']).toBeDefined();
});
it('remove layer', function () {
var layer = this.layerDefinitionsCollection.at(0);
this.layerDefinitionsCollection.remove(layer);
var instance = LegendsState.getInstance();
expect(instance['l-1']).toBeUndefined();
});
});
});
@@ -0,0 +1,94 @@
var Backbone = require('backbone');
var _ = require('underscore');
var ConfigModel = require('builder/data/config-model');
var MapDefinitionModel = require('builder/data/map-definition-model');
var LayerDefinitionsCollection = require('builder/data/layer-definitions-collection');
describe('data/map-definition-model', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.layerDefinitionsCollection = new LayerDefinitionsCollection([
{
kind: 'tiled',
options: {
name: 'Positron',
category: 'CartoDB',
minZoom: 0,
maxZoom: 40
}
}
], {
configModel: {},
userModel: {},
analysisDefinitionNodesCollection: {},
mapId: {},
stateDefinitionModel: {}
});
spyOn(_, 'debounce').and.callFake(function (func) {
return function () {
func.apply(this, arguments);
};
});
this.model = new MapDefinitionModel({
id: 'm-123'
}, {
parse: true,
userModel: new Backbone.Model(),
vis: {
map: new Backbone.Model()
},
configModel: configModel,
layerDefinitionsCollection: this.layerDefinitionsCollection
});
});
it('should have a url', function () {
expect(this.model.url()).toEqual('/u/pepe/api/v2/maps/m-123');
});
it('should have a layerDefinitionsCollection', function () {
expect(this.model._layerDefinitionsCollection).toBe(this.layerDefinitionsCollection);
});
describe('when the base layer is changed', function () {
beforeEach(function () {
spyOn(this.model, 'save');
});
it('should save maxZoom and minZoom', function () {
this.layerDefinitionsCollection.trigger('baseLayerChanged');
expect(this.model.save).toHaveBeenCalledWith(jasmine.objectContaining({
minZoom: 0,
maxZoom: 40
}));
});
it('should save the provider', function () {
this.layerDefinitionsCollection.trigger('baseLayerChanged');
expect(this.model.save).toHaveBeenCalledWith(jasmine.objectContaining({
provider: 'leaflet'
}));
});
it('should save the provider', function () {
this.layerDefinitionsCollection.reset({
type: 'GMapsBase',
baseType: 'roadmap',
minZoom: 0,
maxZoom: 40
}, { parse: false });
this.layerDefinitionsCollection.trigger('baseLayerChanged');
expect(this.model.save).toHaveBeenCalledWith(jasmine.objectContaining({
provider: 'googlemaps'
}));
});
});
});
@@ -0,0 +1,24 @@
var Backbone = require('backbone');
var OrganizationAssetsCollection = require('builder/data/organization-assets-collection');
describe('data/organization-assets-collection', function () {
beforeEach(function () {
var orgId = '5p3c724-1ndv572135';
var configModel = new Backbone.Model({
base_url: 'a_url'
});
configModel.urlVersion = function () { return 'v808'; };
this.collection = new OrganizationAssetsCollection(null, {
configModel: configModel,
orgId: orgId
});
});
it('should form url properly', function () {
var url = this.collection.url();
expect(url).toBe('a_url/api/v808/organization/5p3c724-1ndv572135/assets');
});
});
@@ -0,0 +1,33 @@
var ConfigModel = require('builder/data/config-model');
var OrganizationModel = require('builder/data/organization-model');
describe('data/organization-model', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.model = new OrganizationModel({
id: 'org1',
owner: {
id: 'hello',
username: 'dev',
email: 'hello@hello'
}
}, {
configModel: configModel
});
});
it('should have owner', function () {
expect(this.model._ownerModel.get('username')).toEqual('dev');
});
it('should return the owner id', function () {
expect(this.model.getOwnerId()).toBe(this.model._ownerModel.get('id'));
});
it('should return the owner email', function () {
expect(this.model.getOwnerEmail()).toBe(this.model._ownerModel.get('email'));
});
});
@@ -0,0 +1,469 @@
var PermissionModel = require('builder/data/permission-model');
var UserModel = require('builder/data/user-model');
var GroupModel = require('builder/data/group-model');
var OrganizationModel = require('builder/data/organization-model');
// PermissionModel exports won't play nicely
var READ_ONLY = 'r';
var READ_WRITE = 'rw';
describe('data/permission-model', function () {
var model, user1, user2, owner;
beforeEach(function () {
this.owner = owner = new UserModel({
id: 'uuid_owner',
username: 'owner',
viewer: false
}, {
configModel: 'c'
});
this.permission = model = new PermissionModel({
owner: owner.toJSON(),
entity: {
id: 'test_id',
type: 'vis'
}
}, {
configModel: 'c'
});
user1 = new UserModel({
id: 'uuid',
username: 'u1',
viewer: false
}, {
configModel: 'c'
});
this.organization = new OrganizationModel({
id: 'org_uuid'
}, {
configModel: 'c'
});
user2 = new UserModel({
id: 'uuid2',
username: 'u2',
viewer: false,
organization: this.organization
}, {
configModel: 'c'
});
});
describe('.isOwner', function () {
it('should return true if given model is considered owner of item', function () {
expect(this.permission.isOwner(user1)).toBe(false);
expect(this.permission.isOwner({})).toBe(false);
expect(this.permission.isOwner(this.owner)).toBe(true);
});
it('should return true if ids are not set for owner', function () {
// otherwise there are tons of tests that fails randomly and stalls test suite
this.permission.owner.unset('id');
expect(this.permission.isOwner({})).toBe(true);
});
describe('when has no owner', function () {
beforeEach(function () {
this.permission.owner = undefined;
});
it('should return false when owner is not set', function () {
expect(this.permission.isOwner(this.owner)).toBe(false);
});
});
describe('when owner has no id', function () {
beforeEach(function () {
this.other = this.owner.clone();
});
it('should return false when owner has no id', function () {
expect(this.permission.isOwner(this.other)).toBe(false);
});
});
});
describe('.revokeWriteAccess', function () {
beforeEach(function () {
spyOn(this.permission, 'grantReadAccess');
this.permission.revokeWriteAccess(user1);
});
it('should downgrade to read access', function () {
expect(this.permission.grantReadAccess).toHaveBeenCalled();
expect(this.permission.grantReadAccess).toHaveBeenCalledWith(user1);
});
});
describe('.revokeAccess', function () {
describe('when model has own ACL item', function () {
beforeEach(function () {
this.permission.acl.add(this.permission._newAclItem(user1, READ_ONLY));
this.permission.revokeAccess(user1);
});
it('should return the ACL item', function () {
expect(this.permission.acl.length).toEqual(0);
});
});
});
// Test all can/has/grant-access methods at once, since they all depend on similar setups
describe('access', function () {
beforeEach(function () {
// Setup various group scenarios for later use
this.groupX = new GroupModel({ id: 'gx' }, { configModel: 'c' });
this.permission.acl.add(this.permission._newAclItem(this.groupX, READ_ONLY));
this.groupA = new GroupModel({ id: 'ga' }, { configModel: 'c' });
this.permission.acl.add(this.permission._newAclItem(this.groupA, READ_ONLY));
this.groupB = new GroupModel({ id: 'gb' }, { configModel: 'c' });
this.permission.acl.add(this.permission._newAclItem(this.groupB, READ_WRITE));
});
describe('owner', function () {
it('should have all access', function () {
expect(this.permission.hasAccess(this.owner)).toBe(true);
expect(this.permission.hasReadAccess(this.owner)).toBe(true);
expect(this.permission.hasWriteAccess(this.owner)).toBe(true);
});
it('should be able to set any access', function () {
expect(this.permission.canChangeReadAccess(this.owner)).toBe(true);
expect(this.permission.canChangeWriteAccess(this.owner)).toBe(true);
});
});
describe('user', function () {
describe('w/o any access', function () {
it('should not have any access', function () {
expect(this.permission.hasAccess(user2)).toBe(false);
expect(this.permission.hasReadAccess(user2)).toBe(false);
expect(this.permission.hasWriteAccess(user2)).toBe(false);
});
it('should be able to set any access', function () {
expect(this.permission.canChangeReadAccess(user2)).toBe(true);
expect(this.permission.canChangeWriteAccess(user2)).toBe(true);
});
});
describe('with read access', function () {
beforeEach(function () {
this.permission.grantReadAccess(user2);
});
it('should have read access', function () {
expect(this.permission.hasAccess(user2)).toBe(true);
expect(this.permission.hasReadAccess(user2)).toBe(true);
expect(this.permission.hasWriteAccess(user2)).toBe(false);
});
it('should be able to set any access', function () {
expect(this.permission.canChangeReadAccess(this.owner)).toBe(true);
expect(this.permission.canChangeWriteAccess(this.owner)).toBe(true);
});
});
describe('with write access', function () {
beforeEach(function () {
this.permission.grantWriteAccess(user2);
});
it('should have both read+write access', function () {
expect(this.permission.hasAccess(user2)).toBe(true);
expect(this.permission.hasReadAccess(user2)).toBe(true);
expect(this.permission.hasWriteAccess(user2)).toBe(true);
});
it('should be able to set any access', function () {
expect(this.permission.canChangeReadAccess(this.owner)).toBe(true);
expect(this.permission.canChangeWriteAccess(this.owner)).toBe(true);
});
});
describe('is part of organization with read access', function () {
beforeEach(function () {
this.permission.grantReadAccess(this.organization);
});
it('should have read access', function () {
expect(this.permission.hasAccess(user2)).toBe(true);
expect(this.permission.hasReadAccess(user2)).toBe(true);
expect(this.permission.hasWriteAccess(user2)).toBe(false);
});
it('should only be able to set write access if builder', function () {
expect(this.permission.canChangeReadAccess(user2)).toBe(false);
expect(this.permission.canChangeWriteAccess(user2)).toBe(true);
});
it('should not be able to set write access if viewer', function () {
user2.set({viewer: true});
expect(this.permission.canChangeReadAccess(user2)).toBe(false);
expect(this.permission.canChangeWriteAccess(user2)).toBe(false);
});
describe('when organization has write access', function () {
beforeEach(function () {
this.permission.grantWriteAccess(this.organization);
});
it('should have both read+write access', function () {
expect(this.permission.hasAccess(user2)).toBe(true);
expect(this.permission.hasReadAccess(user2)).toBe(true);
expect(this.permission.hasWriteAccess(user2)).toBe(true);
});
it('should not be able to change any access', function () {
expect(this.permission.canChangeReadAccess(user2)).toBe(false);
expect(this.permission.canChangeWriteAccess(user2)).toBe(false);
});
});
});
describe('w/o own access but member of group with read access', function () {
beforeEach(function () {
user2.groups.add(this.groupA);
});
it('should have the group access', function () {
expect(this.permission.hasAccess(user2)).toBe(true);
expect(this.permission.hasReadAccess(user2)).toBe(true);
expect(this.permission.hasWriteAccess(user2)).toBe(false);
});
it('should only be able to change write access', function () {
expect(this.permission.canChangeReadAccess(user2)).toBe(false);
expect(this.permission.canChangeWriteAccess(user2)).toBe(true);
});
describe('member of multiple groups', function () {
beforeEach(function () {
user2.groups.add(this.groupB);
});
it('should have the most privileged access of the groups', function () {
expect(this.permission.hasAccess(user2)).toBe(true);
expect(this.permission.hasReadAccess(user2)).toBe(true);
expect(this.permission.hasWriteAccess(user2)).toBe(true);
});
it('should not be able to change any access (since has write inherited)', function () {
expect(this.permission.canChangeReadAccess(user2)).toBe(false);
expect(this.permission.canChangeWriteAccess(user2)).toBe(false);
});
});
describe('is also part of organization with read+write access', function () {
beforeEach(function () {
this.permission.grantWriteAccess(this.organization);
});
it('should return the organization access since it has precedence over groups', function () {
expect(this.permission.hasAccess(user2)).toBe(true);
expect(this.permission.hasReadAccess(user2)).toBe(true);
expect(this.permission.hasWriteAccess(user2)).toBe(true);
});
it('should not be able to change any access (since has write inherited)', function () {
expect(this.permission.canChangeReadAccess(user2)).toBe(false);
expect(this.permission.canChangeWriteAccess(user2)).toBe(false);
});
});
describe('is part of organization with read+write access', function () {
beforeEach(function () {
this.permission.grantWriteAccess(this.organization);
});
it('should return the organization access since it has precedence over groups', function () {
expect(this.permission.hasAccess(user2)).toBe(true);
expect(this.permission.hasReadAccess(user2)).toBe(true);
expect(this.permission.hasWriteAccess(user2)).toBe(true);
});
it('should not be able to change any access (since has write inherited)', function () {
expect(this.permission.canChangeReadAccess(user2)).toBe(false);
expect(this.permission.canChangeWriteAccess(user2)).toBe(false);
});
});
describe('is part of organization with read-only access', function () {
beforeEach(function () {
this.permission.grantReadAccess(this.organization);
});
it('should return the organization access', function () {
expect(this.permission.hasAccess(user2)).toBe(true);
expect(this.permission.hasReadAccess(user2)).toBe(true);
expect(this.permission.hasWriteAccess(user2)).toBe(false);
});
it('should be able to change write access only', function () {
expect(this.permission.canChangeReadAccess(user2)).toBe(false);
expect(this.permission.canChangeWriteAccess(user2)).toBe(true);
});
describe('has better access than organization', function () {
beforeEach(function () {
this.permission.grantWriteAccess(user2);
});
it('should have read+write access', function () {
expect(this.permission.hasAccess(user2)).toBe(true);
expect(this.permission.hasReadAccess(user2)).toBe(true);
expect(this.permission.hasWriteAccess(user2)).toBe(true);
});
it('should be able to change access', function () {
expect(this.permission.canChangeReadAccess(user2)).toBe(true);
expect(this.permission.canChangeWriteAccess(user2)).toBe(true);
});
});
describe('is also owner', function () {
beforeEach(function () {
this.permission.owner = user2;
});
it('should have read+write access', function () {
expect(this.permission.hasAccess(user2)).toBe(true);
expect(this.permission.hasReadAccess(user2)).toBe(true);
expect(this.permission.hasWriteAccess(user2)).toBe(true);
});
it('should be able to change access', function () {
expect(this.permission.canChangeReadAccess(user2)).toBe(true);
expect(this.permission.canChangeWriteAccess(user2)).toBe(true);
});
});
});
});
});
describe('group', function () {
describe('with own access', function () {
it('should have the expected access', function () {
expect(this.permission.hasAccess(this.groupX)).toBe(true);
expect(this.permission.hasReadAccess(this.groupX)).toBe(true);
expect(this.permission.hasWriteAccess(this.groupX)).toBe(false);
expect(this.permission.hasAccess(this.groupA)).toBe(true);
expect(this.permission.hasReadAccess(this.groupA)).toBe(true);
expect(this.permission.hasWriteAccess(this.groupA)).toBe(false);
expect(this.permission.hasAccess(this.groupB)).toBe(true);
expect(this.permission.hasReadAccess(this.groupB)).toBe(true);
expect(this.permission.hasWriteAccess(this.groupB)).toBe(true);
});
});
});
});
it('should parse owner and acl', function () {
model = new PermissionModel({
owner: {
username: 'rambo'
},
acl: [
{
type: 'user',
entity: {
id: 'u1',
username: 'u1'
},
access: 'r'
},
{
type: 'user',
entity: {
id: 'u2',
username: 'u2'
},
access: 'rw'
}
]
}, { configModel: 'c' });
expect(model.owner.get('username')).toEqual('rambo');
expect(model.acl.length).toEqual(2);
expect(model.acl.at(0).get('entity').get('username')).toEqual('u1');
});
it('toJSON', function () {
model.grantReadAccess(user1);
model.grantWriteAccess(user2);
expect(model.toJSON()).toEqual({
entity: {
id: 'test_id',
type: 'vis'
},
acl: [
{ type: 'user', entity: { id: 'uuid', username: 'u1', avatar_url: 'http://cartodb.s3.amazonaws.com/static/public_dashboard_default_avatar.png' }, access: 'r' },
{ type: 'user', entity: { id: 'uuid2', username: 'u2', avatar_url: 'http://cartodb.s3.amazonaws.com/static/public_dashboard_default_avatar.png' }, access: 'rw' }
]
});
});
it('updates owner', function () {
model.set('owner', { username: 'changed' });
expect(model.owner.get('username')).toEqual('changed');
model.set('acl', [
{ type: 'user', entity: { id: 'uuid', username: 'u1', avatar_url: 'http://cartodb.s3.amazonaws.com/static/public_dashboard_default_avatar.png' }, access: 'r' },
{ type: 'user', entity: { id: 'uuid2', username: 'u2', avatar_url: 'http://cartodb.s3.amazonaws.com/static/public_dashboard_default_avatar.png' }, access: 'rw' }
]);
expect(model.acl.length).toEqual(2);
});
it("shouldn't trigger an acl reset change when acl is re-generated", function () {
var count = 0;
model.acl.bind('reset', function () {
++count;
});
model.set('acl', [
{ type: 'user', entity: { id: 'uuid', username: 'u1', avatar_url: 'http://cartodb.s3.amazonaws.com/static/public_dashboard_default_avatar.png' }, access: 'r' },
{ type: 'user', entity: { id: 'uuid2', username: 'u2', avatar_url: 'http://cartodb.s3.amazonaws.com/static/public_dashboard_default_avatar.png' }, access: 'rw' }
]);
expect(count).toEqual(0);
});
describe('.clone', function () {
beforeEach(function () {
model.set('id', 'abc-123', { silent: true });
this.permission = model.clone();
});
it('should return a new Permission object', function () {
expect(this.permission).not.toBe(model);
expect(this.permission instanceof PermissionModel).toBeTruthy();
});
it('should contain the same attributes as the original permission', function () {
expect(this.permission.owner).not.toBeUndefined();
expect(this.permission.acl).not.toBeUndefined();
});
it('should not have an id set', function () {
expect(this.permission.get('id')).toBeUndefined();
expect(this.permission.get('id')).not.toEqual(model.get('id'));
});
});
describe('.overwriteAcl', function () {
beforeEach(function () {
this.otherPermission = model.clone();
this.otherPermission.grantReadAccess(user1);
this.otherPermission.grantWriteAccess(user2);
model.overwriteAcl(this.otherPermission);
});
it('should set the ACL list from other permission object', function () {
expect(model.acl.models).toEqual(this.otherPermission.acl.models);
});
});
});
@@ -0,0 +1,391 @@
var QueryBaseModel = require('builder/data/query-base-model');
var STATUS = require('builder/data/query-base-status');
describe('data/query-base-model', function () {
var model;
var createModelFn = function (options) {
QueryBaseModel.prototype._onChange = function () {};
QueryBaseModel.prototype._getSqlApiQueryParam = function () {};
return new QueryBaseModel(options);
};
beforeEach(function () {
model = createModelFn({
status: 'test'
});
});
describe('.initialize', function () {
it('should have 0 repeated errors', function () {
expect(model.repeatedErrors).toEqual(0);
});
});
describe('.getStatusValue', function () {
it('should return the current status', function () {
expect(model.getStatusValue()).toEqual('test');
});
});
describe('.isInInitialStatus', function () {
it('should return true if the status is initial', function () {
model = createModelFn({
status: STATUS.initial
});
expect(model.isInInitialStatus()).toEqual(true);
});
it('should return false if the status is not initial', function () {
model = createModelFn({
status: STATUS.fetched
});
expect(model.isInInitialStatus()).toEqual(false);
});
});
describe('.isInFinalStatus', function () {
it('should return true if the status is fetched', function () {
model = createModelFn({
status: STATUS.fetched
});
expect(model.isInFinalStatus()).toEqual(true);
});
it('should return true if the status is unavailable', function () {
model = createModelFn({
status: STATUS.unavailable
});
expect(model.isInFinalStatus()).toEqual(true);
});
it('should return true if the status is errored', function () {
model = createModelFn({
status: STATUS.errored
});
expect(model.isInFinalStatus()).toEqual(true);
});
it('should return false if the status is not unavailable, errored or fetched', function () {
model = createModelFn({
status: 'test'
});
expect(model.isInFinalStatus()).toEqual(false);
});
});
describe('.isFetched', function () {
it('should return false if the status is not fetched', function () {
expect(model.isFetched()).toEqual(false);
});
it('should return true if the status is fetched', function () {
model = createModelFn({
status: STATUS.fetched
});
expect(model.isFetched()).toEqual(true);
});
});
describe('.isFetching', function () {
it('should return false if the status is not fetching', function () {
expect(model.isFetching()).toEqual(false);
});
it('should return true if the status is fetching', function () {
model = createModelFn({
status: STATUS.fetching
});
expect(model.isFetching()).toEqual(true);
});
});
describe('.isErrored', function () {
it('should return false if the status is not errored', function () {
expect(model.isErrored()).toEqual(false);
});
it('should return true if the status is errored', function () {
model = createModelFn({
status: STATUS.errored
});
expect(model.isErrored()).toEqual(true);
});
});
describe('.isUnavailable', function () {
it('should return false if the status is not unavailable', function () {
expect(model.isUnavailable()).toEqual(false);
});
it('should return true if the status is unavailable', function () {
model = createModelFn({
status: STATUS.unavailable
});
expect(model.isUnavailable()).toEqual(true);
});
});
describe('.isDone', function () {
it('should return false neither fetched nor errored', function () {
expect(model.isDone()).toEqual(false);
});
it('should return true if the status is fetched', function () {
model = createModelFn({
status: STATUS.fetched
});
expect(model.isDone()).toEqual(true);
});
it('should return true if the status is errored', function () {
model = createModelFn({
status: STATUS.errored
});
expect(model.isDone()).toEqual(true);
});
});
describe('.hasQuery', function () {
it('should return false if the model has not a query', function () {
expect(model.hasQuery()).toEqual(false);
});
it('should return true if the model has a query', function () {
model = createModelFn({
query: {}
});
expect(model.hasQuery()).toEqual(true);
});
});
describe('.canFetch', function () {
it('should return false if the model has not a query', function () {
model = createModelFn({
ready: true,
query: null
});
expect(model.canFetch()).toEqual(false);
});
it('should return false if the model is not ready', function () {
model = createModelFn({
ready: false,
query: {}
});
expect(model.canFetch()).toEqual(false);
});
it('should return true if the model has a query an it is ready', function () {
model = createModelFn({
ready: true,
query: {}
});
expect(model.canFetch()).toEqual(true);
});
});
describe('.shouldFetch', function () {
it('should return true if the model can fetch and is neither fetched nor fetching', function () {
spyOn(model, 'canFetch').and.returnValue(true);
spyOn(model, 'isFetched').and.returnValue(false);
spyOn(model, 'isFetching').and.returnValue(false);
expect(model.shouldFetch()).toEqual(true);
});
it('should return false if the model can not fetch', function () {
spyOn(model, 'canFetch').and.returnValue(false);
spyOn(model, 'isFetched').and.returnValue(false);
spyOn(model, 'isFetching').and.returnValue(false);
expect(model.shouldFetch()).toEqual(false);
});
it('should return false if the model is fetched', function () {
spyOn(model, 'canFetch').and.returnValue(true);
spyOn(model, 'isFetched').and.returnValue(true);
spyOn(model, 'isFetching').and.returnValue(false);
expect(model.shouldFetch()).toEqual(false);
});
it('should return false if the model is fetching', function () {
spyOn(model, 'canFetch').and.returnValue(true);
spyOn(model, 'isFetched').and.returnValue(false);
spyOn(model, 'isFetching').and.returnValue(true);
expect(model.shouldFetch()).toEqual(false);
});
});
describe('.resetFetch', function () {
it('should set the status to unfetched', function () {
model.resetFetch();
expect(model.get('status')).toEqual(STATUS.unfetched);
});
});
describe('.isNew', function () {
it('should be overwritten to always return true', function () {
expect(model.isNew()).toEqual(true);
});
});
describe('.hasRepeatedErrors', function () {
var MAX_REPEATED_ERRORS = 2;
it('should return false if it has less errors than errors allowed', function () {
expect(model.hasRepeatedErrors()).toEqual(false);
});
it('should return true if it has more errors than errors allowed', function () {
model.repeatedErrors = MAX_REPEATED_ERRORS + 1;
expect(model.hasRepeatedErrors()).toEqual(true);
});
});
describe('._onStatusChanged', function () {
it('should call _onStatusChanged if status changes', function () {
var onStatusChangedSpy = spyOn(QueryBaseModel.prototype, '_onStatusChanged').and.returnValue(true);
model = createModelFn({
status: STATUS.fetching
});
model.set('status', STATUS.fetched);
expect(onStatusChangedSpy).toHaveBeenCalled();
});
it('should trigger inFinalStatus if it is in final status', function () {
spyOn(model, 'isInFinalStatus').and.returnValue(true);
spyOn(model, 'trigger');
model._onStatusChanged();
expect(model.trigger).toHaveBeenCalledWith('inFinalStatus');
});
it('should not trigger inFinalStatus if it is not in final status', function () {
spyOn(model, 'isInFinalStatus').and.returnValue(false);
spyOn(model, 'trigger');
model._onStatusChanged();
expect(model.trigger).not.toHaveBeenCalled();
});
});
describe('._addChangeListener', function () {
it('should call onChange if the model changes', function () {
var onChangeSpy = spyOn(model, '_onChange').and.returnValue(true);
model._addChangeListener();
model.set('status', STATUS.fetched);
expect(onChangeSpy).toHaveBeenCalled();
});
});
describe('._removeChangeListener', function () {
it('should not call onChange if the model changes', function () {
var onChangeSpy = spyOn(model, '_onChange').and.returnValue(true);
model._addChangeListener();
model._removeChangeListener();
model.set('status', STATUS.fetched);
expect(onChangeSpy).not.toHaveBeenCalled();
});
});
describe('._httpMethod', function () {
var MAX_GET_LENGTH = 1024;
it('should be GET if sql api query param length is shorter tan max length allowed', function () {
spyOn(QueryBaseModel.prototype, '_getSqlApiQueryParam').and.returnValue([]);
expect(model._httpMethod()).toEqual('GET');
});
it('should be POST if sql api query param length is longer tan max length allowed', function () {
spyOn(QueryBaseModel.prototype, '_getSqlApiQueryParam').and.returnValue(new Array(MAX_GET_LENGTH + 1));
expect(model._httpMethod()).toEqual('POST');
});
});
describe('._incrementRepeatedError', function () {
it('should increment repeated errors in threshold by one', function () {
model.repeatedErrors = 2;
model._incrementRepeatedError();
expect(model.repeatedErrors).toEqual(3);
});
});
describe('._resetRepeatedError', function () {
it('should set repeatedErrors to zero', function () {
model.repeatedErrors = 2;
model._resetRepeatedError();
expect(model.repeatedErrors).toEqual(0);
});
});
describe('._onReadyChanged', function () {
it('should be called if ready property changes', function () {
spyOn(QueryBaseModel.prototype, '_onReadyChanged').and.returnValue(true);
model = createModelFn({
ready: false
});
model.set('ready', true);
expect(model._onReadyChanged).toHaveBeenCalled();
});
it('should fetch if is ready and it should fetch', function () {
model.set('ready', true);
spyOn(model, 'shouldFetch').and.returnValue(true);
spyOn(model, 'fetch');
model._onReadyChanged();
expect(model.fetch).toHaveBeenCalled();
});
it('should not fetch if it is not ready', function () {
model.set('ready', false);
spyOn(model, 'shouldFetch').and.returnValue(true);
spyOn(model, 'fetch');
model._onReadyChanged();
expect(model.fetch).not.toHaveBeenCalled();
});
it('should not fetch if it should not', function () {
model.set('ready', true);
spyOn(model, 'shouldFetch').and.returnValue(false);
spyOn(model, 'fetch');
model._onReadyChanged();
expect(model.fetch).not.toHaveBeenCalled();
});
});
});
@@ -0,0 +1,67 @@
var Backbone = require('backbone');
var QueryColumnModel = require('builder/data/query-column-model');
describe('data/query-columns-model', function () {
beforeEach(function () {
this.configModel = new Backbone.Model();
spyOn(QueryColumnModel.prototype, 'fetch');
this.model = new QueryColumnModel({}, {
configModel: this.configModel,
tableName: 'heyyy'
});
});
describe('parse', function () {
it('should parse correctly', function () {
var dataParsed = this.model.parse({
cartodb_type: 'string',
name: 'hello'
});
expect(dataParsed.type).toBe('string');
expect(dataParsed.name).toBe('hello');
expect(dataParsed.isNew).toBeFalsy();
});
});
describe('url', function () {
beforeEach(function () {
this.configModel.set({
base_url: ''
});
this.configModel.urlVersion = function () { return 'v1'; };
});
it('should not provide a valid url if tableName is not defined', function () {
this.model._tableName = '';
expect(this.model.url()).toBeFalsy();
this.model._tableName = 'heyman';
expect(this.model.url()).toContain('/api/v1/tables/heyman/columns');
});
it('should add column name unless isNew is true', function () {
this.model.set({
name: 'newName',
isNew: true
});
expect(this.model.url()).toBe('/api/v1/tables/heyyy/columns/');
this.model.set('isNew', false);
expect(this.model.url()).toBe('/api/v1/tables/heyyy/columns/newName');
});
});
describe('static methods', function () {
it('should provide information if a column change type is destructive', function () {
expect(QueryColumnModel.isTypeChangeDestructive).toBeDefined();
expect(QueryColumnModel.isTypeChangeDestructive('number', 'date')).toBeTruthy();
expect(QueryColumnModel.isTypeChangeDestructive('date', 'string')).toBeFalsy();
});
it('should provide information if a column change type is allowed', function () {
expect(QueryColumnModel.isTypeChangeAllowed).toBeDefined();
expect(QueryColumnModel.isTypeChangeAllowed('number', 'date')).toBeFalsy();
expect(QueryColumnModel.isTypeChangeAllowed('boolean', 'date')).toBeFalsy();
expect(QueryColumnModel.isTypeChangeAllowed('number', 'string')).toBeTruthy();
});
});
});
@@ -0,0 +1,102 @@
var Backbone = require('backbone');
var QuerySchemaModel = require('builder/data/query-schema-model');
var QueryColumnModel = require('builder/data/query-column-model');
var QueryColumnsCollection = require('builder/data/query-columns-collection');
describe('data/query-columns-collection', function () {
beforeEach(function () {
this.configModel = new Backbone.Model();
this.querySchemaModel = new QuerySchemaModel({}, {
configModel: this.configModel
});
spyOn(QueryColumnsCollection.prototype, 'fetch');
this.collection = new QueryColumnsCollection([], {
querySchemaModel: this.querySchemaModel,
configModel: this.configModel
});
});
describe('url', function () {
beforeEach(function () {
this.configModel.set({
base_url: ''
});
this.configModel.urlVersion = function () { return 'v1'; };
});
it('should not provide a valid url if tableName is not defined', function () {
this.collection._tableName = '';
expect(this.collection.url()).toBeFalsy();
this.collection._tableName = 'comeon';
expect(this.collection.url()).toBe('/api/v1/tables/comeon/columns');
});
});
describe('binds', function () {
it('should fetch query schema model when new column is added', function () {
this.querySchemaModel.set('status', 'fetched');
spyOn(this.querySchemaModel, 'fetch');
this.collection.add(new Backbone.Model());
expect(this.querySchemaModel.get('status')).toBe('unfetched');
expect(this.querySchemaModel.fetch).toHaveBeenCalled();
});
it('should take query-schema when status changes to fetched', function () {
spyOn(this.collection, 'reset').and.callThrough();
this.querySchemaModel.columnsCollection.reset([
{
type: 'number',
name: 'cartodb_id'
}, {
type: 'number',
name: 'a_number'
}, {
type: 'string',
name: 'texttt'
}
]);
this.querySchemaModel.set('status', 'fetched');
expect(this.collection.reset).toHaveBeenCalled();
expect(this.collection.size()).toBe(3);
});
});
describe('setTableName', function () {
it('should not change tableName when it is renamed but it is not referenced', function () {
this.collection.reset([{ id: 'hola' }]);
expect(this.collection.at(0)._tableName).toBe(undefined);
this.collection.setTableName('two');
expect(this.collection.at(0)._tableName).toBeUndefined();
expect(this.collection._tableName).toBeUndefined();
});
it('should change tableName from itself and its children when it is renamed', function () {
this.collection.reset([{ id: 'hola' }]);
this.collection._tableName = 'one';
expect(this.collection.at(0)._tableName).toBe(undefined);
this.collection.setTableName('two');
expect(this.collection.at(0)._tableName).toBe('two');
expect(this.collection._tableName).toBe('two');
});
});
describe('addColumn', function () {
it('should wait and not parse the new column', function () {
var options = {};
var attributes;
QueryColumnModel.prototype.save = function (attrs, opts) {
attributes = this.attributes;
options = opts;
};
this.collection.addColumn();
expect(attributes.isNew).toBeTruthy();
expect(attributes.type).toBe('string');
expect(attributes.name).toContain('column_');
expect(options.wait).toBeTruthy();
expect(options.parse).toBeFalsy();
});
});
});
@@ -0,0 +1,271 @@
var Backbone = require('backbone');
var ConfigModel = require('builder/data/config-model');
var QueryGeometryModel = require('builder/data/query-geometry-model');
describe('data/query-geometry-model', function () {
beforeEach(function () {
this.xhrSpy = jasmine.createSpyObj('xhr', ['abort', 'always', 'fail']);
spyOn(Backbone.Model.prototype, 'sync').and.returnValue(this.xhrSpy);
spyOn(Backbone.Model.prototype, 'fetch').and.callThrough();
var configModel = new ConfigModel({
base_url: '/u/pepe',
api_key: 'xyz123'
});
this.model = new QueryGeometryModel({
ready: true
}, {
configModel: configModel
});
});
it('should not have any geometry initially', function () {
expect(this.model.get('simple_geom')).toBeFalsy();
});
describe('when there is no query set', function () {
it('should be initial by default', function () {
expect(this.model.get('status')).toEqual('initial');
});
it('should not allow to fetch', function () {
expect(this.model.fetch());
expect(Backbone.Model.prototype.fetch).not.toHaveBeenCalled();
});
});
describe('when it is not ready', function () {
beforeEach(function () {
this.model.set({
ready: false,
query: 'SELECT * FROM wherever'
});
});
it('should not allow to fetch', function () {
expect(this.model.fetch());
expect(Backbone.Model.prototype.fetch).not.toHaveBeenCalled();
});
});
describe('when a query is changed', function () {
beforeEach(function () {
this.model.set('query', 'SELECT * FROM foo');
});
it('should update status accordingly', function () {
expect(this.model.get('status')).toEqual('unfetched');
this.model.unset('query');
expect(this.model.get('status')).toEqual('unavailable');
});
describe('when fetch', function () {
beforeEach(function () {
this.model.fetch();
});
it('should fetch with a wrapped query', function () {
expect(Backbone.Model.prototype.fetch).toHaveBeenCalled();
expect(Backbone.Model.prototype.fetch.calls.argsFor(0)[0].data.q).toMatch(/^SELECT .+$/);
expect(Backbone.Model.prototype.fetch.calls.argsFor(0)[0].data.q).toMatch(/FROM \(.+\) .+$/);
});
it('should add order, rows and page', function () {
expect(Backbone.Model.prototype.fetch.calls.argsFor(0)[0].data.rows_per_page).toBe(40);
expect(Backbone.Model.prototype.fetch.calls.argsFor(0)[0].data.page).toBe(0);
expect(Backbone.Model.prototype.fetch.calls.argsFor(0)[0].data.sort_order).toBe('asc');
});
it('should fetch using an API key', function () {
expect(Backbone.Model.prototype.fetch.calls.argsFor(0)[0].data.api_key).toEqual('xyz123');
});
it('should change status', function () {
expect(this.model.get('status')).toEqual('fetching');
});
describe('when a request is already ongoing', function () {
beforeEach(function () {
this.model.fetch();
});
it('should cancel current request', function () {
expect(this.xhrSpy.abort).toHaveBeenCalled();
});
it('should fetch again', function () {
expect(Backbone.Model.prototype.fetch.calls.count()).toEqual(2);
});
});
describe('when request succeeds', function () {
beforeEach(function () {
Backbone.Model.prototype.sync.calls.argsFor(0)[2].success({
fields: {
cartodb_id: {type: 'number'},
title: {type: 'string'},
the_geom: {type: 'string'}
},
rows: [
{ cartodb_id: 1, title: '1st', the_geom: '' },
{ cartodb_id: 2, title: '2nd', the_geom: 'line' },
{ cartodb_id: 3, title: '3rd', the_geom: 'line' }
]
});
});
it('should change status', function () {
expect(this.model.get('status')).toEqual('fetched');
});
it('should setup raw geom', function () {
expect(this.model.get('simple_geom')).toEqual(jasmine.any(String));
});
});
describe('when request fails', function () {
beforeEach(function () {
Backbone.Model.prototype.sync.calls.argsFor(0)[2].error({
responseText: '{"error": ["meh"]}'
});
});
it('should have unavailable status', function () {
expect(this.model.get('status')).toEqual('unavailable');
});
it('should don\'t have simple_geom', function () {
expect(this.model.get('simple_geom')).toEqual('');
});
});
describe('when request is aborted', function () {
beforeEach(function () {
Backbone.Model.prototype.sync.calls.argsFor(0)[2].error({
statusText: 'abort'
});
});
it('should not have anavailable status if error comes from an abort request', function () {
expect(this.model.get('status')).not.toEqual('unavailable');
expect(this.model.get('query_errors')).toBeUndefined();
});
it('should not change simple_geom', function () {
expect(this.model.get('status')).not.toEqual('unavailable');
expect(this.model.hasChanged('simple_geom')).toBeFalsy();
});
});
});
});
describe('when ready flag is changed', function () {
describe('when there is no query', function () {
beforeEach(function () {
this.model.set('ready', true);
});
it('should change status', function () {
expect(this.model.get('status')).toEqual('initial');
});
});
describe('when there is a query', function () {
beforeEach(function () {
this.model.set({
query: 'SELECT * FROM something',
ready: false
});
});
it('should change status', function () {
expect(this.model.get('status')).toEqual('unfetched');
});
});
});
describe('.destroy', function () {
beforeEach(function () {
this.destroySpy = jasmine.createSpy('destroy');
this.model.once('destroy', this.destroySpy);
this.model.destroy();
});
it('should do default destroy process to cleanup bindings', function () {
expect(this.destroySpy).toHaveBeenCalled();
});
});
describe('.hasValue', function () {
it('should rise an exeption if this function is called', function (done) {
try {
this.model.hasValue();
} catch (error) {
expect(error).toBeDefined();
done();
}
});
});
describe('.hasValueAsync', function () {
it('should return true is the status is fetched and there is geometry', function (done) {
this.model.set('simple_geom', true);
this.model.set('status', 'fetched');
this.model.hasValueAsync()
.then(function (response) {
expect(response).toEqual(true);
done();
});
});
it('should return false is the status is fetched but there is no geometry', function (done) {
this.model.set('simple_geom', false);
this.model.set('status', 'fetched');
this.model.hasValueAsync()
.then(function (response) {
expect(response).toEqual(false);
done();
});
});
it('should return true when it is done and there is geometry', function (done) {
this.model.set('status', 'fetching');
this.model.set('simple_geom', true);
this.model.hasValueAsync()
.then(function (response) {
expect(response).toEqual(true);
done();
});
this.model.trigger('inFinalStatus');
});
it('should return false when it is done but there is no geometry', function (done) {
this.model.set('status', 'fetching');
this.model.set('simple_geom', false);
this.model.hasValueAsync()
.then(function (response) {
expect(response).toEqual(false);
done();
});
this.model.trigger('inFinalStatus');
});
});
describe('resetFetch', function () {
it('should set status to `unfetched`', function () {
this.model.set('status', 'fetched');
this.model.resetFetch();
expect(this.model.get('status')).toEqual('unfetched');
});
});
});
@@ -0,0 +1,68 @@
var Backbone = require('backbone');
var QueryRowModel = require('builder/data/query-row-model');
describe('data/query-row-model', function () {
beforeEach(function () {
this.configModel = new Backbone.Model();
this.collection = new Backbone.Collection();
this.collection._tableName = 'paco';
this.collection._configModel = this.configModel;
spyOn(QueryRowModel.prototype, '_onError').and.callThrough();
this.model = new QueryRowModel({
hey: 'how',
hi: 'howdy',
the_geom_webmercator: 'EOEOEOEOEOE19203210'
}, {
collection: this.collection
});
});
it('should have __id as idAttribute', function () {
expect(this.model.idAttribute).toBe('__id');
});
describe('parse', function () {
it('should add __id attribute if it is not present', function () {
var attrs = this.model.parse(this.model.attributes);
expect(attrs.__id).toBeDefined();
});
});
describe('toJSON', function () {
it('should not include __id or the_geom_webmercator', function () {
var attrs = this.model.toJSON();
expect(attrs.__id).not.toBeDefined();
expect(attrs.the_geom_webmercator).not.toBeDefined();
});
});
describe('url', function () {
beforeEach(function () {
this.configModel.set('base_url', '');
this.configModel.urlVersion = function () { return 'v1'; };
});
it('should not provide a valid url if tableName is not defined', function () {
this.model._tableName = '';
expect(this.model.url()).toBeFalsy();
this.model._tableName = 'paco';
expect(this.model.url()).toBe('/api/v1/tables/paco/records');
});
});
describe('save', function () {
it('should set previous attributes if there is an error', function () {
this.model.sync = function (a, b, opts) {
opts.error();
};
this.model
.set({ hey: 'ha' })
.save();
expect(QueryRowModel.prototype._onError).toHaveBeenCalled();
expect(this.model.get('hey')).toBe('how');
});
});
});
@@ -0,0 +1,355 @@
var Backbone = require('backbone');
var QuerySchemaModel = require('builder/data/query-schema-model');
var QueryRowsCollection = require('builder/data/query-rows-collection');
describe('data/query-rows-collection', function () {
beforeEach(function () {
this.configModel = new Backbone.Model();
this.querySchemaModel = new QuerySchemaModel({}, {
configModel: this.configModel
});
spyOn(this.querySchemaModel, 'setError');
spyOn(QueryRowsCollection.prototype, 'fetch').and.callThrough();
this.collection = new QueryRowsCollection([], {
querySchemaModel: this.querySchemaModel,
configModel: this.configModel
});
this.collection.sync = function (a, b, opts) {
opts = opts || {};
opts.success && opts.success();
};
});
describe('._initModels', function () {
it('should init models', function () {
expect(this.collection.statusModel).toBeDefined();
});
});
describe('setTableName', function () {
it('should not change tableName when it is renamed but it is not referenced', function () {
this.collection.reset([{ id: 'hola' }]);
expect(this.collection.at(0)._tableName).toBe(undefined);
this.collection.setTableName('two');
expect(this.collection.at(0)._tableName).toBeUndefined();
expect(this.collection._tableName).toBeUndefined();
});
it('should change tableName from itself and its children when it is renamed', function () {
this.collection.reset([{ id: 'hola' }]);
this.collection._tableName = 'one';
expect(this.collection.at(0)._tableName).toBe(undefined);
this.collection.setTableName('two');
expect(this.collection.at(0)._tableName).toBe('two');
expect(this.collection._tableName).toBe('two');
});
});
describe('._initBinds', function () {
it('should change status and reset data when query-schema-model query changes', function () {
this.collection.statusModel.set('status', 'fetched');
this.collection.reset([{ id: 'hola' }]);
this.querySchemaModel.set('query', 'dummy query hey!');
expect(this.collection.statusModel.get('status')).toBe('unfetched');
expect(this.collection.size()).toBe(0);
expect(this.collection.canFetch()).toBeFalsy();
expect(this.collection.shouldFetch()).toBeFalsy();
});
it('should bind _onColumnsCollectionReset callback when "_querySchemaModel" is defined', function () {
spyOn(this.collection, 'listenTo');
expect(this.collection.listenTo).not.toHaveBeenCalled();
this.collection._initBinds();
expect(this.collection.listenTo).toHaveBeenCalledTimes(3);
});
it('should not bind _onColumnsCollectionReset callback when "_querySchemaModel" is not defined', function () {
// mock querySchemaModel object
this.collection._querySchemaModel = false;
spyOn(this.collection, 'listenTo');
expect(this.collection.listenTo).not.toHaveBeenCalled();
this.collection._initBinds();
expect(this.collection.listenTo).toHaveBeenCalledTimes(2);
});
it('should fetch new data when the column collection is changed', function () {
spyOn(this.collection, 'shouldFetch').and.returnValue(true);
spyOn(this.collection, '_onQuerySchemaQueryChange');
expect(this.collection.fetch).not.toHaveBeenCalled();
this.collection._querySchemaModel.columnsCollection.trigger('reset');
expect(this.collection.fetch).toHaveBeenCalledTimes(1);
expect(this.collection._onQuerySchemaQueryChange).toHaveBeenCalledTimes(1);
});
it('should not fetch new data when the column collection is changed but shouldn\'t fetch', function () {
spyOn(this.collection, 'shouldFetch').and.returnValue(false);
spyOn(this.collection, '_onQuerySchemaQueryChange');
expect(this.collection.fetch).not.toHaveBeenCalled();
this.collection._querySchemaModel.columnsCollection.trigger('reset');
expect(this.collection.fetch).not.toHaveBeenCalled();
expect(this.collection._onQuerySchemaQueryChange).toHaveBeenCalled();
});
});
describe('.canFetch', function () {
it('should allow to fetch if _querySchemaModel is ready and fetched, not errored, and doesn\'t have query', function () {
this.configModel = new Backbone.Model();
this.querySchemaModel = new QuerySchemaModel({
query: 'SELECT * FROM table',
ready: true
}, {
configModel: this.configModel
});
spyOn(this.querySchemaModel, 'isFetched').and.returnValue(true);
spyOn(this.querySchemaModel, 'isErrored').and.returnValue(false);
this.collection = new QueryRowsCollection([], {
querySchemaModel: this.querySchemaModel,
configModel: this.configModel
});
expect(this.collection.canFetch()).toBe(true);
});
it('should not allow to fetch if _querySchemaModel doesn\'t have query', function () {
this.configModel = new Backbone.Model();
this.querySchemaModel = new QuerySchemaModel({
ready: true
}, {
configModel: this.configModel
});
spyOn(this.querySchemaModel, 'isFetched').and.returnValue(true);
spyOn(this.querySchemaModel, 'isErrored').and.returnValue(false);
this.collection = new QueryRowsCollection([], {
querySchemaModel: this.querySchemaModel,
configModel: this.configModel
});
expect(this.collection.canFetch()).toBe(false);
});
it('should not allow to fetch if _querySchemaModel isn\'t ready', function () {
this.configModel = new Backbone.Model();
this.querySchemaModel = new QuerySchemaModel({
query: 'SELECT * FROM table',
ready: false
}, {
configModel: this.configModel
});
spyOn(this.querySchemaModel, 'isFetched').and.returnValue(true);
spyOn(this.querySchemaModel, 'isErrored').and.returnValue(false);
this.collection = new QueryRowsCollection([], {
querySchemaModel: this.querySchemaModel,
configModel: this.configModel
});
expect(this.collection.canFetch()).toBe(false);
});
it('should not allow to fetch if _querySchemaModel has not been already fetched', function () {
this.configModel = new Backbone.Model();
this.querySchemaModel = new QuerySchemaModel({
query: 'SELECT * FROM table',
ready: true
}, {
configModel: this.configModel
});
spyOn(this.querySchemaModel, 'isFetched').and.returnValue(false);
spyOn(this.querySchemaModel, 'isErrored').and.returnValue(false);
this.collection = new QueryRowsCollection([], {
querySchemaModel: this.querySchemaModel,
configModel: this.configModel
});
expect(this.collection.canFetch()).toBe(false);
});
it('should not allow to fetch if _querySchemaModel is errored', function () {
this.configModel = new Backbone.Model();
this.querySchemaModel = new QuerySchemaModel({
query: 'SELECT * FROM table',
ready: true
}, {
configModel: this.configModel
});
spyOn(this.querySchemaModel, 'isFetched').and.returnValue(true);
spyOn(this.querySchemaModel, 'isErrored').and.returnValue(true);
this.collection = new QueryRowsCollection([], {
querySchemaModel: this.querySchemaModel,
configModel: this.configModel
});
expect(this.collection.canFetch()).toBe(false);
});
});
describe('.isDone', function () {
describe('when status is fetched', function () {
it('should return true', function () {
spyOn(this.collection, 'isFetched').and.returnValue(true);
expect(this.collection.isDone()).toBe(true);
});
});
describe('when status is errored', function () {
it('should return true', function () {
spyOn(this.collection, 'isErrored').and.returnValue(true);
expect(this.collection.isDone()).toBe(true);
});
});
});
describe('.isEmpty', function () {
it('should rise an exeption if this function is called', function (done) {
try {
this.collection.isEmpty();
} catch (error) {
expect(error).toBeDefined();
done();
}
});
});
describe('.isEmptyAsync', function () {
describe('when collection is isInFinalStatus', function () {
beforeEach(function () {
spyOn(this.collection, 'isInFinalStatus').and.returnValue(true);
});
describe('when collection is empty', function () {
it('should return true', function (done) {
this.collection.isEmptyAsync()
.then(function (response) {
expect(response).toBe(true);
done();
});
});
});
describe('when collection is not isInFinalStatus', function () {
it('should return false', function (done) {
this.collection.add({});
this.collection.isEmptyAsync()
.then(function (response) {
expect(response).toBe(false);
done();
});
});
});
});
describe('when collection is not isInFinalStatus', function () {
beforeEach(function () {
spyOn(this.collection, 'isInFinalStatus').and.returnValue(false);
});
describe('when collection is empty', function () {
it('should return true', function (done) {
this.collection.isEmptyAsync()
.then(function (response) {
expect(response).toBe(true);
done();
});
setTimeout(function () {
this.collection.trigger('inFinalStatus');
}.bind(this), 0);
});
});
describe('when collection is not empty', function () {
it('should return false', function (done) {
this.collection.add({});
this.collection.isEmptyAsync()
.then(function (response) {
expect(response).toBe(false);
done();
});
setTimeout(function () {
this.collection.trigger('inFinalStatus');
}.bind(this), 0);
});
});
});
});
describe('.isFetching', function () {
it('should change "state" when is fetched', function () {
expect(this.collection.isFetching()).toBeFalsy();
this.collection.sync = function () {};
this.collection.fetch();
expect(this.collection.isFetching()).toBeTruthy();
});
it('should change "state" when fetch has finished', function () {
this.collection.fetch();
expect(this.collection.isFetching()).toBeFalsy();
expect(this.collection.isFetched()).toBeTruthy();
});
it('should change "state" when fetch has failed', function () {
this.collection.sync = function (a, b, opts) {
opts && opts.error();
};
this.collection.fetch();
expect(this.collection.isFetching()).toBeFalsy();
expect(this.collection.isFetched()).toBeFalsy();
});
});
describe('.fetch', function () {
it('should call _onQueryRowsCollectionError when success response has property error', function () {
var requestResponse = {
error: ['Postgres Error']
};
spyOn(Backbone.Collection.prototype, 'fetch').and.callFake(function (options) {
options.success([], requestResponse, options);
});
this.collection.fetch();
expect(this.querySchemaModel.setError).toHaveBeenCalledWith(requestResponse.error);
});
});
describe('.getStatusValue', function () {
it('should return the status', function () {
this.collection.statusModel.set('status', 'test');
expect(this.collection.getStatusValue()).toEqual('test');
});
});
});
@@ -0,0 +1,375 @@
var _ = require('underscore');
var Backbone = require('backbone');
var ConfigModel = require('builder/data/config-model');
var QuerySchemaModel = require('builder/data/query-schema-model');
describe('data/query-schema-model', function () {
beforeEach(function () {
this.xhrSpy = jasmine.createSpyObj('xhr', ['abort', 'always', 'fail']);
spyOn(Backbone.Model.prototype, 'sync').and.returnValue(this.xhrSpy);
spyOn(Backbone.Model.prototype, 'fetch').and.callThrough();
var configModel = new ConfigModel({
base_url: '/u/pepe',
api_key: 'xyz123'
});
this.modelOpts = {
configModel: configModel
};
this.model = new QuerySchemaModel({ ready: true }, this.modelOpts);
this.setSpy = spyOn(QuerySchemaModel.prototype, 'set');
this.setSpy.and.callThrough();
spyOn(this.model, '_incrementRepeatedError');
});
describe('when there is no query set initially', function () {
it('should be unavailable by default', function () {
expect(this.model.get('status')).toEqual('unavailable');
});
it('should not allow to fetch', function () {
expect(this.model.fetch());
expect(Backbone.Model.prototype.fetch).not.toHaveBeenCalled();
});
});
describe('when it is not ready', function () {
beforeEach(function () {
this.model.set({
ready: false,
query: 'SELECT * FROM wherever'
});
});
it('should not allow to fetch', function () {
expect(this.model.fetch());
expect(Backbone.Model.prototype.fetch).not.toHaveBeenCalled();
});
});
it('should setup status according to given query and status attrs when model is created', function () {
this.model = new QuerySchemaModel({query: 'SELECT * FROM foobar', ready: true}, this.modelOpts);
expect(this.model.get('status')).toEqual('unfetched', 'since have a query that can be fetched');
this.model = new QuerySchemaModel({
status: 'fetched',
query: 'SELECT * FROM foobar',
ready: true
}, this.modelOpts);
expect(this.model.get('status')).toEqual('fetched', 'should match given value from when model was created');
});
describe('.resetDueToAlteredData', function () {
it('should change status, trigger an event and fetch', function () {
var spyObj = jasmine.createSpy('resetDueToAlteredData');
spyOn(this.model, 'fetch');
this.model.bind('resetDueToAlteredData', spyObj);
this.model.resetDueToAlteredData();
expect(this.model.get('status')).toBe('unfetched');
expect(this.model.fetch).toHaveBeenCalled();
expect(spyObj).toHaveBeenCalled();
});
});
describe('when a query is changed', function () {
beforeEach(function () {
this.model.set('query', 'SELECT * FROM foo');
});
it('should update status accordingly', function () {
expect(this.model.get('status')).toEqual('unfetched');
this.model.unset('query');
expect(this.model.get('status')).toEqual('unavailable');
});
});
describe('.fetch', function () {
beforeEach(function () {
this.model.set('query', 'SELECT * FROM foo');
});
describe('when called with default', function () {
beforeEach(function () {
this.model.fetch();
});
it('should fetch with a wrapped query', function () {
expect(Backbone.Model.prototype.fetch).toHaveBeenCalled();
expect(Backbone.Model.prototype.fetch.calls.argsFor(0)[0].data.q).toMatch(/^SELECT \* FROM.+$/);
expect(Backbone.Model.prototype.fetch.calls.argsFor(0)[0].data.q).toMatch(/FROM \(.+\) .+$/);
});
it('should use default request params', function () {
expect(Backbone.Model.prototype.fetch.calls.argsFor(0)[0].data.rows_per_page).toBe(0);
expect(Backbone.Model.prototype.fetch.calls.argsFor(0)[0].data.page).toBe(0);
expect(Backbone.Model.prototype.fetch.calls.argsFor(0)[0].data.sort_order).toBe('asc');
});
it('should fetch using an API key', function () {
expect(Backbone.Model.prototype.fetch.calls.argsFor(0)[0].data.api_key).toEqual('xyz123');
});
it('should change status', function () {
expect(this.model.get('status')).toEqual('fetching');
});
describe('when a request is already ongoing', function () {
beforeEach(function () {
this.model.fetch();
});
it('should cancel current request', function () {
expect(this.xhrSpy.abort).toHaveBeenCalled();
});
it('should fetch again', function () {
expect(Backbone.Model.prototype.fetch.calls.count()).toEqual(2);
});
});
describe('when request succeeds', function () {
beforeEach(function () {
Backbone.Model.prototype.sync.calls.argsFor(0)[2].success({
fields: {
cartodb_id: {type: 'number'},
title: {type: 'string'},
the_geom: {type: 'geometry'}
},
rows: []
});
});
it('should change status', function () {
expect(this.model.get('status')).toEqual('fetched');
});
it('should reset columns', function () {
expect(this.model.columnsCollection.pluck('name')).toEqual(['cartodb_id', 'title', 'the_geom']);
});
});
describe('when request fails', function () {
beforeEach(function () {
Backbone.Model.prototype.sync.calls.argsFor(0)[2].error({
responseText: '{"error": ["meh"]}'
});
});
it('should have unavailable status', function () {
expect(this.model.get('status')).toEqual('unavailable');
expect(this.model.get('query_errors')).not.toBeUndefined();
});
});
describe('when request is aborted', function () {
beforeEach(function () {
Backbone.Model.prototype.sync.calls.argsFor(0)[2].error({
statusText: 'abort'
});
});
it('should not have anavailable status if error comes from an abort request', function () {
expect(this.model.get('status')).not.toEqual('unavailable');
expect(this.model.get('query_errors')).toBeUndefined();
});
});
});
describe('when called with custom attrs set', function () {
beforeEach(function () {
this.model.set({
page: 1,
rows_per_page: 40,
sort_order: 'desc'
});
this.model.fetch();
});
it('should use custom request params', function () {
this.model.set({
page: 1,
rows_per_page: 40,
sort_order: 'desc'
});
this.model.fetch();
expect(Backbone.Model.prototype.fetch).toHaveBeenCalled();
expect(Backbone.Model.prototype.fetch.calls.argsFor(0)[0].data.rows_per_page).toEqual(40, 'should use custom amount');
expect(Backbone.Model.prototype.fetch.calls.argsFor(0)[0].data.page).toEqual(1, 'should use custom page');
expect(Backbone.Model.prototype.fetch.calls.argsFor(0)[0].data.sort_order).toEqual('desc', 'should use custom order');
});
describe('when request succeeds', function () {
beforeEach(function () {
Backbone.Model.prototype.sync.calls.argsFor(0)[2].success({
fields: {
cartodb_id: {type: 'number'},
title: {type: 'string'},
the_geom: {type: 'geometry'}
},
rows: [
{ cartodb_id: 1, title: '1st', the_geom: '0101000020E6100000694C88B9A4AA0DC0FD4FFEEE1D354440' },
{ cartodb_id: 2, title: '2nd', the_geom: '0101000020E6100000694C88B9A4AA0DC0FD4FFEEE1D354440' },
{ cartodb_id: 3, title: '3rd', the_geom: '0101000020E6100000694C88B9A4AA0DC0FD4FFE' }
]
});
});
it('should change status', function () {
expect(this.model.get('status')).toEqual('fetched');
});
it('should reset columns', function () {
expect(this.model.columnsCollection.pluck('name')).toEqual(['cartodb_id', 'title', 'the_geom']);
});
it('should reset rows', function () {
expect(this.model.rowsCollection.pluck('title')).toEqual(['1st', '2nd', '3rd']);
});
});
});
});
describe('.hasDifferentSchemaThan', function () {
it('should return false when schema is the same', function () {
var sameSchema = [
{
name: 'cartodb_id',
type: 'number'
},
{
name: 'description',
type: 'string'
}
];
this.model.columnsCollection.reset(sameSchema);
expect(this.model.hasDifferentSchemaThan(sameSchema)).toBeFalsy();
});
it('should return false when compared schema is empty', function () {
this.model.columnsCollection.reset([
{
name: 'cartodb_id',
type: 'number'
}
]);
expect(this.model.hasDifferentSchemaThan([])).toBeFalsy();
});
it('should return true when compared schema has any different column', function () {
var comparingSchema = [
{
name: 'heyyy'
}, {
name: 'cartodb_id',
type: 'number'
}
];
this.model.columnsCollection.reset([
{
name: 'cartodb_id',
type: 'number'
}, {
name: 'description',
type: 'string'
}
]);
expect(this.model.hasDifferentSchemaThan(comparingSchema)).toBeTruthy();
});
it('should return false when a column doesn\'t contain the type', function () {
this.model.columnsCollection.reset([
{
name: 'cartodb_id',
type: 'number'
}
]);
var comparingSchema = [
{
name: 'cartodb_id'
}
];
expect(this.model.hasDifferentSchemaThan(comparingSchema)).toBeFalsy();
});
});
describe('.destroy', function () {
beforeEach(function () {
this.destroySpy = jasmine.createSpy('destroy');
this.model.once('destroy', this.destroySpy);
this.model.destroy();
});
it('should do default destroy process to cleanup bindings', function () {
expect(this.destroySpy).toHaveBeenCalled();
});
});
describe('.hasDefaultQueryFor', function () {
it('should return false if no query is applied', function () {
this.model.unset('query');
expect(this.model.hasDefaultQueryFor('foo')).toBeFalsy();
});
_.each({
'SELECT * FROM foo': 'bar',
'select * FROM foo': 'bar',
'select * FROM "foo"': 'bar',
'SELECT a,b FROM foo': 'foo',
'select * FROM foo LIMIT 10': 'foo',
'select * FROM "foo" WHERE a = 1': 'foo'
}, function (tableName, query) {
it('should return false', function () {
this.model.set('query', query);
expect(this.model.hasDefaultQueryFor(tableName)).toBeFalsy();
});
});
_.each({
'SELECT * FROM foo': 'foo',
'select * FROM foo': 'foo',
'select * FROM "foo"': 'foo',
'select * from foo.bar': '"foo".bar',
'select * from "foo".bar': 'foo.bar'
}, function (tableName, query) {
it('should return true', function () {
this.model.set('query', query);
expect(this.model.hasDefaultQueryFor(tableName)).toBeTruthy();
});
});
});
describe('._setError', function () {
it('should set error if there isn\'t any other error previously', function () {
var error = ['Postgres Error'];
this.model.setError(error);
expect(this.model._incrementRepeatedError).toHaveBeenCalled();
expect(this.model.set).toHaveBeenCalledWith({
query_errors: error,
status: 'unavailable'
});
});
it('should not set error if there is any other error previously', function () {
this.model.set({
query_errors: 'Test Error',
status: 'errored'
});
this.setSpy.calls.reset();
var error = ['Postgres Error'];
this.model.setError(error);
expect(this.setSpy).not.toHaveBeenCalled();
});
});
});
@@ -0,0 +1,34 @@
var StateDefinitionModel = require('builder/data/state-definition-model');
describe('data/state-definition-model', function () {
beforeEach(function () {
var anObject = { something: 'something' };
var visDefinitionModel = {};
this.model = new StateDefinitionModel({
json: JSON.stringify(anObject)
}, {
visDefinitionModel: visDefinitionModel
});
});
describe('.setBounds', function () {
it(' should trigger event when called', function () {
jasmine.clock().install();
var expectedBounds = [808];
var actualBounds;
function onBoundsSet (bounds) {
actualBounds = bounds;
}
this.model.on('boundsSet', onBoundsSet);
this.model.setBounds(expectedBounds);
jasmine.clock().tick(10);
expect(actualBounds[0]).toBe(expectedBounds[0]);
jasmine.clock().uninstall();
});
});
});
@@ -0,0 +1,13 @@
var StaticAssetModel = require('builder/data/static-asset-model');
describe('data/static-asset-model', function () {
beforeEach(function () {
this.model = new StaticAssetModel();
});
describe('.getUrlFor', function () {
it('should return the proper URL', function () {
expect(this.model.getURLFor('paella'))
.toEqual('https://s3.amazonaws.com/com.cartodb.users-assets.production/maki-icons/paella-18.svg');
});
});
});
@@ -0,0 +1,102 @@
var _ = require('underscore');
var TableModel = require('builder/data/table-model');
var SynchronizationModel = require('builder/data/synchronization-model');
describe('data/synchronization', function () {
beforeEach(function () {
jasmine.Ajax.install();
jasmine.Ajax.stubRequest(new RegExp('^http(s)?://.*'))
.andReturn({ status: 200 });
var configModel = jasmine.createSpyObj('configModel', ['get', 'urlVersion']);
this.tableModel = new TableModel({
id: 'abc-123',
name: 'foobar_table'
}, {
configModel: configModel
});
this.syncModel = new SynchronizationModel({}, {
configModel: configModel
});
});
afterEach(function () {
jasmine.Ajax.uninstall();
});
describe('JSON stringification', function () {
it("should return guessing parameters, interval and URL when it doesn't come from a service", function () {
this.syncModel.set({
content_guessing: true,
fake_guessing: false
});
var d = this.syncModel.toJSON();
expect(_.size(d)).toBe(5);
expect(d.type_guessing).not.toBeUndefined();
expect(d.url).not.toBeUndefined();
expect(d.interval).not.toBeUndefined();
expect(d.fake_guessing).toBeUndefined();
});
it('should return the right params when it comes from a remote source', function () {
this.syncModel.set({
type: 'remote',
remote_visualization_id: 123,
value: 1234
});
var d = this.syncModel.toJSON();
expect(d.value).not.toBeUndefined();
expect(d.create_vis).toBeFalsy();
expect(d.remote_visualization_id).not.toBeUndefined();
});
it('should add service attributes when it comes from a service', function () {
this.syncModel.set({
service_name: 'follower'
});
var d = this.syncModel.toJSON();
expect(_.size(d)).toBe(7);
expect(d.service_name).not.toBeUndefined();
expect(d.service_item_id).not.toBeUndefined();
});
});
describe('linkToTable', function () {
it('should assign id from table it exists and not fetch', function () {
var s = { id: 'test', duration: 10 };
this.tableModel.set('synchronization', s);
spyOn(this.syncModel, 'fetch');
this.syncModel.linkToTable(this.tableModel);
expect(this.syncModel.get('id')).toEqual(s.id);
expect(this.syncModel.get('duration')).toEqual(s.duration);
expect(this.syncModel.fetch).not.toHaveBeenCalled();
this.tableModel.set('synchronization', { id: 'test2' });
expect(this.syncModel.get('id')).toEqual('test2');
});
it('should remove sync', function () {
spyOn(this.syncModel, 'destroy');
this.syncModel.linkToTable(this.tableModel);
this.tableModel.destroy();
expect(this.syncModel.destroy).toHaveBeenCalled();
});
});
it('should set failure state if there is any error', function () {
expect(this.syncModel.get('state')).toBe('');
this.syncModel.set('error_code', 'heyyy-error');
expect(this.syncModel.get('state')).toBe('failure');
});
it('should unset id on destroy', function () {
this.syncModel.set({
id: 'test',
interval: 10
});
expect(this.syncModel.isSync()).toEqual(true);
this.syncModel.destroy();
expect(this.syncModel.isNew()).toEqual(true);
expect(this.syncModel.isSync()).toEqual(false);
});
});
@@ -0,0 +1,201 @@
var _ = require('underscore');
var ConfigModel = require('builder/data/config-model');
var TableModel = require('builder/data/table-model');
var PermissionModel = require('builder/data/permission-model');
var UserModel = require('builder/data/user-model');
describe('data/table-model', function () {
var configModel;
beforeEach(function () {
configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.userModel = new UserModel({
username: 'pericooo'
}, {
configModel: configModel
});
this.model = new TableModel({
id: 'abc-123',
name: 'foobar_table'
}, {
parse: true,
configModel: configModel
});
});
it('should have a custom URL to get data', function () {
expect(this.model.url()).toEqual('/u/pepe/api/v1/tables/foobar_table');
});
it('should provide means for a custom URL for columns collection', function () {
expect(this.model.columnsCollection.url()).toEqual('/u/pepe/api/v1/tables/foobar_table/columns');
});
describe('getGeometryType', function () {
it('should return the geometries those are present in the table', function () {
this.model.set('geometry_types', ['ST_MULTIPOINT']);
expect(this.model.getGeometryType()).toEqual(['point']);
this.model.set('geometry_types', ['ST_POINT']);
expect(this.model.getGeometryType()).toEqual(['point']);
this.model.set('geometry_types', ['ST_MULTIPOLYGON']);
expect(this.model.getGeometryType()).toEqual(['polygon']);
this.model.set('geometry_types', ['ST_POLYGON']);
expect(this.model.getGeometryType()).toEqual(['polygon']);
this.model.set('geometry_types', ['ST_LINESTRING']);
expect(this.model.getGeometryType()).toEqual(['line']);
this.model.set('geometry_types', ['ST_MULTILINESTRING']);
expect(this.model.getGeometryType()).toEqual(['line']);
});
it('shouldn\'t return the same geometry several times', function () {
this.model.set('geometry_types', ['ST_MULTIPOINT', 'ST_POINT', 'ST_POLYGON']);
expect(this.model.getGeometryType()).toEqual(['point', 'polygon']);
});
});
describe('.parse', function () {
it('should take table_visualization attributes from response if they exist, not removing or overwritting geometry_types', function () {
var parseAttrs = this.model.parse({
id: 'bad',
geometry_types: ['ST_POLYGON'],
table_visualization: {
id: 'good',
geometry_types: ['pepito']
}
});
expect(parseAttrs.id).toBe('good');
expect(parseAttrs.geometry_types).toEqual(['ST_POLYGON']);
parseAttrs = this.model.parse({
id: 'new_good',
geometry_types: ['ST_POINT'],
table_visualization: {}
});
expect(parseAttrs.id).toBe('new_good');
expect(parseAttrs.geometry_types).toEqual(['ST_POINT']);
});
describe('synchronization', function () {
it('should create synchronization model if sync data is provided and model is not created', function () {
var syncTableModel = createTableModel({ synchronization: {} }, true);
expect(syncTableModel._syncModel).toBeDefined();
expect(syncTableModel.attributes.synchronization).toBeDefined();
});
it('should update synchronization model if sync data is provided and that model is already created', function () {
var syncTableModel = createTableModel({ synchronization: {} }, true);
expect(syncTableModel._syncModel.get('id')).toBeUndefined();
syncTableModel.parse({ synchronization: { id: '3' } });
expect(syncTableModel._syncModel.get('id')).toBe('3');
});
});
describe('permission', function () {
it('should create permission model if permission attribute is provided', function () {
var permissionTableModel = createTableModel({ permission: {} }, true);
expect(permissionTableModel._permissionModel).toBeDefined();
expect(permissionTableModel.attributes.permission).toBeDefined();
});
it('should update permission model if permission attribute is provided and that model is already created', function () {
var permissionTableModel = createTableModel({ permission: {} }, true);
expect(permissionTableModel._permissionModel.get('id')).toBeUndefined();
permissionTableModel.parse({ permission: { id: 'perm' } });
expect(permissionTableModel._permissionModel.get('id')).toBe('perm');
});
});
});
describe('.isSync', function () {
it('should be truthy if there is a synchronization model and it has id', function () {
var syncTableModel = createTableModel({ synchronization: { id: 'ha' } }, true);
expect(syncTableModel.isSync()).toBeTruthy();
});
it('should be falsy if there isn\'t a synchronization model or if it doesn\'t have an id', function () {
var nonSyncTableModel = createTableModel({ synchronization: {} }, true);
expect(nonSyncTableModel.isSync()).toBeFalsy();
var nonSyncTableModel2 = createTableModel({}, true);
expect(nonSyncTableModel2.isSync()).toBeFalsy();
});
});
describe('getOwnerName', function () {
it('should take the owner name from the table name if it is included', function () {
this.model.set('name', 'perico.foobar_table');
expect(this.model.getOwnerName()).toBe('perico');
});
it('should take the owner name from the permission if it is available and it is not included in the table name', function () {
this.model._permissionModel = new PermissionModel({}, {
configModel: configModel,
_userModel: this.userModel
});
this.model.set('name', 'foobar_table');
expect(this.model.getOwnerName()).toBe('pericooo');
});
});
describe('.hasWriteAccess', function () {
beforeEach(function () {
this.model._permissionModel = new PermissionModel({}, {
configModel: configModel
});
this.permModel = this.model._permissionModel;
spyOn(this.permModel, 'hasWriteAccess');
});
it('should have write access if userModel has that permission', function () {
this.permModel.hasWriteAccess.and.returnValue(true);
expect(this.model.hasWriteAccess(this.userModel)).toBeTruthy();
});
it('should not have write access if userModel doesn\'t have that permission', function () {
this.permModel.hasWriteAccess.and.returnValue(false);
expect(this.model.hasWriteAccess(this.userModel)).toBeFalsy();
});
it('should not have write access if userModel is not defined', function () {
expect(this.model.hasWriteAccess()).toBeFalsy();
});
});
describe('.isReadOnly', function () {
it('should be true if it is synced', function () {
var syncTableModel = createTableModel({ synchronization: {} }, true);
expect(syncTableModel.isReadOnly(this.userModel)).toBe(true);
});
it('should be true if user doesn\'t have write permissions', function () {
var permissionTableModel = createTableModel({ permission: {} }, true);
spyOn(permissionTableModel, 'hasWriteAccess').and.returnValue(false);
expect(permissionTableModel.isReadOnly(this.userModel)).toBe(true);
});
it('should be false if user does have write permissions', function () {
var permissionTableModel = createTableModel({ permission: {} }, true);
spyOn(permissionTableModel, 'hasWriteAccess').and.returnValue(true);
expect(permissionTableModel.isReadOnly(this.userModel)).toBe(false);
});
});
function createTableModel (attrs, parseModel) {
return new TableModel(
_.extend(
{
id: 'har',
name: 'table'
},
attrs || {}
),
{
parse: parseModel,
configModel: configModel
}
);
}
});
@@ -0,0 +1,126 @@
var Backbone = require('backbone');
var TablesCollection = require('builder/data/tables-collection');
var ConfigModel = require('builder/data/config-model');
var UserModel = require('builder/data/user-model');
describe('data/tables-collection', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.userModel = new UserModel({
id: 'uuid',
username: 'paco',
organization: {
id: 'o1'
}
}, {
configModel: configModel
});
this.collection = new TablesCollection([], {
configModel: configModel
});
});
it('should have a custom URL to get data', function () {
expect(this.collection.url()).toEqual('/u/pepe/api/v1/viz');
});
describe('.fetch', function () {
beforeEach(function () {
spyOn(Backbone.Collection.prototype, 'fetch');
});
describe('when called without any args', function () {
beforeEach(function () {
this.collection.fetch();
});
it('should be called with default options if none is provided', function () {
expect(Backbone.Collection.prototype.fetch).toHaveBeenCalled();
var args = Backbone.Collection.prototype.fetch.calls.argsFor(0);
expect(args[0]).toBeDefined();
expect(args[0].data).toEqual(jasmine.objectContaining({ type: 'table' }));
expect(args[0].data).toEqual(jasmine.objectContaining({ page: 1 }));
expect(args[0].data).toEqual(jasmine.objectContaining({ per_page: jasmine.any(Number) }));
expect(args[0].data).toEqual(jasmine.objectContaining({ exclude_shared: false }));
expect(args[0].data).toEqual(jasmine.objectContaining({ tags: '' }));
expect(args[0].data).toEqual(jasmine.objectContaining({ q: '' }));
});
});
describe('when called with different args', function () {
beforeEach(function () {
this.collection.fetch({
data: {
exclude_shared: true,
type: 'remote'
}
});
});
it('should be called with default options if none is provided', function () {
expect(Backbone.Collection.prototype.fetch).toHaveBeenCalled();
var args = Backbone.Collection.prototype.fetch.calls.argsFor(0);
expect(args[0]).toBeDefined();
expect(args[0].data).toEqual(jasmine.objectContaining({ type: 'remote' }));
expect(args[0].data).toEqual(jasmine.objectContaining({ exclude_shared: true }));
});
});
describe('.parse', function () {
it('should return as many items as visualizations come', function () {
var items = this.collection.parse({
visualizations: [{
type: 'table',
table: {
geometry_types: ['point'],
id: 'hello-table',
name: 'table1',
privacy: 'PRIVATE',
rows_counted: 10,
table_size: 103423
}
}]
});
expect(items.length).toBe(1);
var tableData = items[0];
expect(tableData.id).toBe('hello-table');
});
it('should populate table_type to the items', function () {
var items = this.collection.parse({
visualizations: [{
type: 'remote',
table: {
id: 'hello-table2'
}
}]
});
var tableData = items[0];
expect(tableData.table_type).toBe('remote');
});
});
describe('.getTotalStat', function () {
beforeEach(function () {
this.collection.parse({
total_shared: 2,
total_user_entries: 3,
visualizations: []
});
});
it('should return stats from last collection parse', function () {
expect(this.collection.getTotalStat('total_shared')).toBe(2);
this.collection.parse({
total_shared: 1,
total_user_entries: 2,
visualizations: []
});
expect(this.collection.getTotalStat('total_shared')).toBe(1);
expect(this.collection.getTotalStat('total_user_entries')).toBe(2);
});
});
});
});
@@ -0,0 +1,99 @@
var Backbone = require('backbone');
var _ = require('underscore');
var UndoManager = require('builder/data/undo-manager');
describe('data/undo-manager', function () {
beforeEach(function () {
this.model = new Backbone.Model({
test: 1
});
UndoManager.init(this.model, { track: true });
});
it('should set undoManager', function () {
expect(this.model._undoManager).toBeDefined();
});
it('should accept history via params', function () {
var model = new Backbone.Model({
test: 1
});
var history = [{ test: 2 }, { test: 3 }, { test: 1 }];
UndoManager.init(model, { track: true, history: history });
expect(model._undoManager.stack.size()).toBe(2);
expect(model._undoManager.stack.pointer).toBe(1);
expect(model.canUndo()).toBeTruthy();
});
describe('trackEvents', function () {
beforeEach(function () {
this.model.set('test', 2);
});
it('should trigger undo when they happen', function () {
var onUndo = jasmine.createSpy('undo');
this.model.bind('undo', onUndo);
this.model.undo();
expect(onUndo).toHaveBeenCalled();
});
it('should trigger redo when they happen', function () {
var onRedo = jasmine.createSpy('redo');
this.model.undo();
this.model.bind('redo', onRedo);
this.model.redo();
expect(onRedo).toHaveBeenCalled();
});
it('should trigger undoredoChanged when any change has added to the stack', function () {
var onUnReDo = jasmine.createSpy('unredo');
this.model.bind('unredoChanged', onUnReDo);
this.model.set('test', 3);
expect(onUnReDo).toHaveBeenCalled();
this.model.set('test', 0);
expect(onUnReDo.calls.count()).toBe(2);
this.model.set('test', 5);
expect(onUnReDo.calls.count()).toBe(3);
});
});
describe('public methods', function () {
it('should add several public methods to the model', function () {
_.each(['undo', 'redo', 'canUndo', 'canRedo', 'getUndoHistory'], function (method) {
expect(this.model[method]).toBeDefined();
}, this);
});
it('should undo backbone-undo', function () {
spyOn(this.model._undoManager, 'undo');
this.model.undo();
expect(this.model._undoManager.undo).toHaveBeenCalled();
});
it('should redo backbone-undo', function () {
spyOn(this.model._undoManager, 'redo');
this.model.redo();
expect(this.model._undoManager.redo).toHaveBeenCalled();
});
it('should check if there is any undo or redo action', function () {
spyOn(this.model._undoManager, 'isAvailable');
this.model.canUndo();
expect(this.model._undoManager.isAvailable).toHaveBeenCalledWith('undo');
this.model._undoManager.isAvailable.calls.reset();
this.model.canRedo();
expect(this.model._undoManager.isAvailable).toHaveBeenCalledWith('redo');
});
it('should return stack history', function () {
this.model.set('test', 2);
this.model.set('test', 3);
var history = this.model.getUndoHistory();
expect(history.length).toBe(3);
expect(history[0]).toEqual({ test: 1 });
expect(history[1]).toEqual({ test: 2 });
expect(history[2]).toEqual({ test: 3 });
});
});
});
@@ -0,0 +1,134 @@
var ConfigModel = require('builder/data/config-model');
var UploadModel = require('builder/data/upload-model');
var UserModel = require('builder/data/user-model');
describe('data/upload-model', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.userModel = new UserModel({
id: 'uuid',
username: 'paco',
organization: {
id: 'o1'
}
}, {
configModel: configModel
});
this.model = new UploadModel({}, {
userModel: this.userModel,
configModel: configModel
});
spyOn(this.model, 'bind').and.callThrough();
});
it('should have progress, change:value and error/invalid binds', function () {
this.model._initBinds();
var args_0 = this.model.bind.calls.argsFor(0);
expect(args_0[0]).toEqual('progress');
var args_1 = this.model.bind.calls.argsFor(1);
expect(args_1[0]).toEqual('change:value');
var args_2 = this.model.bind.calls.argsFor(2);
expect(args_2[0]).toEqual('error invalid');
});
it('should set uploading state when progress change', function () {
this.model.set({ value: 'http://paco.csv', type: 'url' });
this.model.trigger('progress', 20);
expect(this.model.get('state')).toBe('uploading');
});
it('should have a fileAttribute set', function () {
expect(this.model.fileAttribute).toBe('filename');
});
it('should validate the model when changes', function () {
spyOn(this.model, 'validate');
this.model.setUpload({
value: 'http://marca.com/paco.jjjjj',
type: 'url'
});
expect(this.model.validate).toHaveBeenCalled();
});
it('should change state when validate fails', function () {
this.model.setUpload({
type: 'url',
value: 'hello'
});
expect(this.model.get('state')).toBe('error');
});
it('should add error code when size validation fails', function () {
this.model._userModel.set('remaining_byte_quota', 1);
this.model.setUpload({
type: 'file',
value: { name: 'fake.csv', size: 1000 }
});
expect(this.model.get('state')).toBe('error');
expect(this.model.get('error_code')).toBe(8001);
this.model.setUpload({
type: 'remote',
remote_visualization_id: 'iddd',
size: 1000
});
expect(this.model.get('state')).toBe('error');
expect(this.model.get('error_code')).toBe(8001);
this.model.setUpload({
type: 'url',
value: 'heyheyhey.org'
});
expect(this.model.get('state')).toBe('error');
expect(this.model.get('error_code')).toBe('');
});
it('should raise an error when file name is not provided', function () {
this.model.setUpload({
type: 'file',
value: {}
});
expect(this.model.get('state')).toBe('error');
expect(this.model.get('get_error_text').what_about).toBe('data.upload-model.file-defined');
});
describe('.setUpload', function () {
beforeEach(function () {
spyOn(this.model, 'set');
});
it('should set the proper data and validate it', function () {
this.model.setUpload({
type: 'url',
value: 'heyheyhey.org'
});
expect(this.model.set).toHaveBeenCalled();
expect(this.model.set.calls.argsFor(0)[0]).toEqual(jasmine.objectContaining({ type: 'url', value: 'heyheyhey.org' }));
expect(this.model.set.calls.argsFor(0)[1]).toEqual(jasmine.objectContaining({ validate: true }));
});
});
describe('.setFresh', function () {
beforeEach(function () {
spyOn(this.model, 'set');
this.model.setFresh({
foo: 'bar',
create_vis: true
});
});
it('should set the given dataset', function () {
expect(this.model.set).toHaveBeenCalled();
expect(this.model.set.calls.argsFor(0)[0]).toEqual(jasmine.objectContaining({ foo: 'bar' }));
});
it('should omit the create_vis value since set in constructor', function () {
expect(this.model.set.calls.argsFor(0)[0]).not.toEqual(jasmine.objectContaining({ create_vis: true }));
});
});
});
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,191 @@
var ConfigModel = require('builder/data/config-model');
var UserModel = require('builder/data/user-model');
describe('data/user-model', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.model = new UserModel({
id: 'uuid',
organization: {
id: 'o1',
admins: []
},
layers: [{
options: {
visible: true,
type: 'Tiled',
urlTemplate: 'https://a.tiles.mapbox.com/v4/username.12ab45c/{z}/{x}/{y}.png?access_token=aBcDC12323abc',
attribution: '<a href="https://www.mapbox.com/about/maps/" target="_blank">&copy; Mapbox &copy; OpenStreetMap</a> <a class="mapbox-improve-map" href="https://www.mapbox.com/map-feedback/" target="_blank">Improve this map</a>',
maxZoom: 21,
minZoom: 0,
name: 'MapBox Streets Outdoors Global Preview',
order: 26
},
kind: 'tiled',
infowindow: null,
tooltip: null,
id: 'basemap-id-1',
order: 26
}, {
id: 'basemap-id-2',
infowindow: null,
kind: 'tiled',
options: {
attribution: null,
className: 'httpsstamentilessasslfastlynetwatercolorzxyjpg',
maxZoom: 21,
minZoom: 0,
name: 'Custom basemap 29',
order: 29,
tms: false,
type: 'Tiled',
urlTemplate: 'https://stamen-tiles-{s}.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg',
visible: true
},
order: 29,
tooltip: null
}]
}, {
configModel: configModel
});
});
it('should create an organization', function () {
expect(this.model._organizationModel.id).toEqual('o1');
});
it('should have layers', function () {
expect(this.model.layers.length).toEqual(2);
});
it("shouldn't set avatar_url is it comes with null value", function () {
expect(this.model.get('avatar_url')).toBe('http://cartodb.s3.amazonaws.com/static/public_dashboard_default_avatar.png');
});
it('isInsideOrg', function () {
this.model._organizationModel.id = '';
expect(this.model.isInsideOrg()).toEqual(false);
this.model._organizationModel.id = 'hello-org-id';
expect(this.model.isInsideOrg()).toEqual(true);
});
it('isOrgOwner', function () {
this.model._organizationModel._ownerModel = this.model;
expect(this.model.isOrgOwner()).toEqual(true);
this.model._organizationModel._ownerModel = new UserModel({
id: 'test',
organization: {}
}, { configModel: 'c' });
expect(this.model.isOrgOwner()).toEqual(false);
});
it('hasOwnTwitterCredentials', function () {
expect(this.model.hasOwnTwitterCredentials()).toEqual(false);
var twitterConfig = {
quota: 100,
monthly_use: 0,
block_size: 10,
block_price: 1000,
enabled: true,
hard_limit: false,
customized_config: true
};
this.model.set('twitter', twitterConfig);
expect(this.model.hasOwnTwitterCredentials()).toEqual(true);
twitterConfig.customized_config = false;
this.model.set('twitter', twitterConfig);
expect(this.model.hasOwnTwitterCredentials()).toEqual(false);
});
it('isOrgAdmin', function () {
this.model._organizationModel.set('admins', [{id: this.model.id}]);
expect(this.model.isOrgAdmin()).toEqual(true);
this.model._organizationModel.set('admins', [{id: 'other_user'}]);
expect(this.model.isOrgAdmin()).toEqual(false);
this.model._organizationModel = null;
expect(this.model.isOrgAdmin()).toEqual(false);
});
it('should answer if user can create new datasets', function () {
this.model.set('remaining_byte_quota', 0);
expect(this.model.canCreateDatasets()).toEqual(false);
this.model.set('remaining_byte_quota', 10);
expect(this.model.canCreateDatasets()).toEqual(true);
this.model.set('remaining_byte_quota', undefined);
this.model.unset('remaining_byte_quota');
expect(this.model.canCreateDatasets()).toEqual(false);
});
it('hasFeatureFlagEnabled', function () {
var flagOK = 'test_flag';
var feature_flags = [];
feature_flags.push(flagOK);
this.model.set('feature_flags', feature_flags);
expect(this.model.featureEnabled(flagOK)).toEqual(true);
expect(this.model.featureEnabled('flagWrong')).toEqual(false);
});
describe('.upgradeContactEmail', function () {
describe('when is a normal user', function () {
beforeEach(function () {
spyOn(this.model, 'isInsideOrg').and.returnValue(false);
});
it('should return the general support email', function () {
expect(this.model.upgradeContactEmail()).toEqual('support@carto.com');
});
});
describe('when us a organization user', function () {
beforeEach(function () {
spyOn(this.model, 'isInsideOrg').and.returnValue(true);
});
describe('when user is also admin of organization', function () {
beforeEach(function () {
spyOn(this.model, 'isOrgOwner').and.returnValue(true);
});
it('should return enterprise support email', function () {
expect(this.model.upgradeContactEmail()).toEqual('enterprise-support@carto.com');
});
});
describe('when user is a normal organization member', function () {
beforeEach(function () {
this.model._organizationModel._ownerModel = new UserModel({
email: 'owner@org.com'
}, {
configModel: 'c'
});
});
it('should return the organiation owner email', function () {
expect(this.model.upgradeContactEmail()).toEqual('owner@org.com');
});
});
});
});
describe('.nameOrUsername', function () {
it('should return the name or username as fallback if name is not available', function () {
this.model.set('username', 'kalle');
expect(this.model.nameOrUsername()).toEqual('kalle');
this.model.set('name', 'Kalle');
expect(this.model.nameOrUsername()).toEqual('Kalle');
this.model.set('last_name', 'Anka');
expect(this.model.nameOrUsername()).toEqual('Kalle Anka');
this.model.set('name', '');
expect(this.model.nameOrUsername()).toEqual('Anka');
});
});
});
@@ -0,0 +1,36 @@
var ConfigModel = require('builder/data/config-model');
var UserNotifications = require('builder/data/user-notifications');
describe('data/user-notifications', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
this._notifications = {
test: 1234
};
this.model = new UserNotifications(this._notifications, {
key: 'my_category',
configModel: configModel
});
});
it('should populate the notifications', function () {
expect(this.model.get('notifications')).toEqual(this._notifications);
});
it('should use the category provided', function () {
expect(this.model.get('key')).toEqual('my_category');
});
it('should store a key', function () {
expect(this.model.setKey('hi', 'test'));
expect(this.model.get('notifications').hi).toBe('test');
});
it('should retrieve a key', function () {
expect(this.model.getKey('test')).toBe(1234);
});
});
@@ -0,0 +1,28 @@
var ConfigModel = require('builder/data/config-model');
var VisDefinitionModel = require('builder/data/vis-definition-model');
describe('data/vis-definition-model', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.model = new VisDefinitionModel({
id: 'v-123',
map_id: 'm-123'
}, {
configModel: configModel
});
});
it('should have a url', function () {
expect(this.model.url()).toEqual('/u/pepe/api/v1/viz/v-123');
});
it('should have an embed url', function () {
expect(this.model.embedURL()).toEqual('/u/pepe/builder/v-123/embed');
});
it('should have a mapcap url', function () {
expect(this.model.mapcapsURL()).toEqual('/u/pepe/api/v3/viz/v-123/mapcaps');
});
});
@@ -0,0 +1,50 @@
var VisualizationsFetchModel = require('builder/data/visualizations-fetch-model');
describe('data/visualizations-fetch-model', function () {
beforeEach(function () {
this.model = new VisualizationsFetchModel({
shared: false,
page: 1
});
});
describe('.isSearching', function () {
it('should return true if set to a search or tag query', function () {
this.model.set({
q: '',
tag: ''
});
expect(this.model.isSearching()).toBeFalsy();
this.model.set({
q: 'foobar',
tag: ''
});
expect(this.model.isSearching()).toBeTruthy();
this.model.set({
q: '',
tag: 'some-tag'
});
expect(this.model.isSearching()).toBeTruthy();
});
});
describe('.isDeepInsights', function () {
it('should return true if content_type is maps and deep-insights are enabled', function () {
this.model.set({
content_type: 'datasets',
deepInsights: true
});
expect(this.model.isDeepInsights()).toBeFalsy();
this.model.set({
content_type: 'maps'
});
expect(this.model.isDeepInsights()).toBeTruthy();
this.model.set({
deepInsights: false
});
expect(this.model.isDeepInsights()).toBeFalsy();
});
});
});
@@ -0,0 +1,197 @@
var Backbone = require('backbone');
var ConfigModel = require('builder/data/config-model');
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
var WidgetDefinitionModel = require('builder/data/widget-definition-model');
var WidgetDefinitionsCollection = require('builder/data/widget-definitions-collection');
var configModel;
describe('builder/data/widget-definition-model', function () {
beforeEach(function () {
configModel = new ConfigModel({
base_url: '/u/pepe'
});
this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
configModel: {},
userModel: {}
});
this.collection = new WidgetDefinitionsCollection(null, {
configModel: configModel,
mapId: 'm-123',
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection,
layerDefinitionsCollection: new Backbone.Collection()
});
this.widgetDefModel = new WidgetDefinitionModel({
id: 'w-456',
title: 'some title',
type: 'formula',
layer_id: 'l-1',
source: {
id: 'a0'
},
options: {
column: 'hello',
operation: 'avg'
}
}, {
parse: true,
configModel: configModel,
mapId: 'm-123',
collection: this.collection
});
});
it('should have a url pointing to layers API endpoint', function () {
expect(this.widgetDefModel.url()).toEqual('/u/pepe/api/v3/maps/m-123/layers/l-1/widgets/w-456');
// when no id:
this.widgetDefModel.set('id', null);
expect(this.widgetDefModel.url()).toEqual('/u/pepe/api/v3/maps/m-123/layers/l-1/widgets');
});
it('should flatten the structure on parse', function () {
expect(this.widgetDefModel.attributes).toEqual(
jasmine.objectContaining({
id: 'w-456',
title: 'some title',
type: 'formula',
layer_id: 'l-1',
source: 'a0',
column: 'hello',
operation: 'avg',
widget_style_definition: {
color: {
fixed: '#9DE0AD',
opacity: 1
}
},
widget_color_changed: false
})
);
});
it('should set some defaults in addition to the provided data', function () {
expect(this.widgetDefModel.attributes).toEqual(
jasmine.objectContaining({
sync_on_bbox_change: true,
auto_style_allowed: true
})
);
});
describe('.toJSON', function () {
beforeEach(function () {
this.d = this.widgetDefModel.toJSON();
});
it('should include expected attrs', function () {
expect(this.d).toEqual({
id: 'w-456',
type: 'formula',
title: 'some title',
layer_id: 'l-1',
source: {
id: 'a0'
},
style: {
widget_style: {
definition: {
color: {
fixed: '#9DE0AD',
opacity: 1
}
},
widget_color_changed: false
},
auto_style: {
custom: false,
allowed: true
}
},
options: {
column: 'hello',
operation: 'avg',
sync_on_bbox_change: true
}
});
});
});
describe('.changeType', function () {
beforeEach(function () {
this.widgetDefModel.changeType('category');
});
it('should keep some attrs', function () {
expect(this.widgetDefModel.id).toEqual('w-456');
expect(this.widgetDefModel.get('id')).toEqual('w-456');
expect(this.widgetDefModel.get('layer_id')).toEqual('l-1');
expect(this.widgetDefModel.get('sync_on_bbox_change')).toBe(true);
expect(this.widgetDefModel.get('auto_style_allowed')).toBe(true);
expect(this.widgetDefModel.get('title')).toEqual('some title');
expect(this.widgetDefModel.get('widget_style_definition')).toEqual({
color: {
fixed: '#9DE0AD',
opacity: 1
}
});
});
it('should have new relevant attrs', function () {
expect(this.widgetDefModel.get('type')).toEqual('category');
expect(this.widgetDefModel.get('column')).toEqual('hello');
expect(this.widgetDefModel.get('aggregation')).toEqual('count');
expect(this.widgetDefModel.get('aggregation_column')).toEqual('hello');
});
it('should remove attrs of no interest anymore', function () {
expect(this.widgetDefModel.get('operation')).toBeUndefined();
});
});
describe('.containsNode', function () {
beforeEach(function () {
this.analysisDefinitionNodesCollection.add([
{
id: 'a1',
type: 'buffer',
params: {
radius: 100,
source: {
id: 'a0',
type: 'source',
params: {
query: 'SELECT * FROM something'
}
}
}
}, {
id: 'b0',
type: 'source',
params: {
query: 'SELECT * FROM other'
}
}
]);
this.a1 = this.analysisDefinitionNodesCollection.get('a1');
this.a0 = this.analysisDefinitionNodesCollection.get('a0');
this.widgetDefModel.set('source', 'a1');
});
it('should return true if widget depends on given node', function () {
expect(this.widgetDefModel.containsNode(this.a1)).toBe(true);
expect(this.widgetDefModel.containsNode(this.a0)).toBe(true);
expect(this.widgetDefModel.containsNode(this.b0)).toBe(false);
expect(this.widgetDefModel.containsNode()).toBe(false);
expect(this.widgetDefModel.containsNode(false)).toBe(false);
});
it('should return false if widget source does not exist', function () {
this.widgetDefModel.set('source', 'y404');
expect(this.widgetDefModel.containsNode(this.a1)).toBe(false);
});
});
});
@@ -0,0 +1,304 @@
var Backbone = require('backbone');
var AnalysisDefinitionNodesCollection = require('builder/data/analysis-definition-nodes-collection');
var LayerDefinitionModel = require('builder/data/layer-definition-model');
var ConfigModel = require('builder/data/config-model');
var UserModel = require('builder/data/user-model');
var WidgetDefinitionsCollection = require('builder/data/widget-definitions-collection');
describe('data/widget-definitions-collection', function () {
beforeEach(function () {
var configModel = new ConfigModel({
base_url: '/u/pepe'
});
var userModel = new UserModel({}, {
configModel: configModel
});
this.layerDefinitionModel = new LayerDefinitionModel({
id: 'l-1',
type: 'CartoDB',
table_name: 'foobar'
}, {
configModel: configModel
});
this.layerDefinitionsCollection = new Backbone.Collection(this.layerDefinitionModel);
this.analysisDefinitionNodesCollection = new AnalysisDefinitionNodesCollection(null, {
configModel: configModel,
userModel: userModel
});
this.collection = new WidgetDefinitionsCollection(null, {
configModel: configModel,
mapId: 'm-123',
layerDefinitionsCollection: this.layerDefinitionsCollection,
analysisDefinitionNodesCollection: this.analysisDefinitionNodesCollection
});
this.originalAjax = Backbone.ajax;
Backbone.ajax = function () {
return {
always: function (cb) {
cb();
}
};
};
});
afterEach(function () {
Backbone.ajax = this.originalAjax;
});
describe('when a model is created', function () {
beforeEach(function () {
var histogram = {
type: 'histogram',
title: 'histogram',
layer_id: 'l-1',
source: {
id: 'a0'
},
options: {
column: 'col'
}
};
this.collection.create(histogram);
});
});
describe('autoStyle', function () {
beforeEach(function () {
var histogram = {
type: 'histogram',
title: 'histogram',
layer_id: 'l-1',
source: {
id: 'a0'
},
options: {
column: 'col'
}
};
var category = {
type: 'category',
title: 'category',
layer_id: 'l-1',
source: {
id: 'a0'
},
options: {
column: 'col2'
}
};
this.collection.create(histogram);
this.collection.create(category);
});
it('should update widgets autostyle when layer\'s style changes', function () {
var styleModel = new Backbone.Model({
type: 'simple',
fill: {
color: {
fixed: '#fabada'
}
}
});
styleModel.canApplyAutoStyle = function () {
return false;
};
this.layerDefinitionModel.styleModel = styleModel;
var histogram = this.collection.at(0);
var category = this.collection.at(0);
histogram.set({auto_style_allowed: true});
category.set({auto_style_allowed: true});
this.layerDefinitionModel.set({style_properties: 'foo'});
expect(histogram.get('auto_style_allowed')).toBeFalsy();
expect(category.get('auto_style_allowed')).toBeFalsy();
});
});
describe('.getColumnType', function () {
it('should return the column type if schemaModel is fetched', function () {
var querySchemaModel = new Backbone.Model({
status: 'fetched'
});
querySchemaModel.columnsCollection = new Backbone.Collection([
{ name: 'cartodb_id', type: 'number' }
]);
var node = {
querySchemaModel: querySchemaModel
};
spyOn(this.collection._analysisDefinitionNodesCollection, 'get').and.returnValue(node);
var type = this.collection.getColumnType('cartodb_id', 'a0');
expect(type).toEqual('number');
});
});
describe('.url', function () {
it('should return the url to update widgets', function () {
expect(this.collection.url()).toEqual('/u/pepe/api/v3/maps/m-123/widgets');
});
});
describe('.parse', function () {
it('should return an empty array if there are not widgets', function () {
expect(this.collection.parse({})).toEqual([]);
});
it('should parse the response from the server', function () {
var widgets = [{
id: 'widget',
order: 0
}];
expect(this.collection.parse({widgets: widgets})).toEqual(widgets);
});
});
describe('.save', function () {
it('should call the bakcbone method to update', function () {
spyOn(Backbone, 'sync');
this.collection.save();
expect(Backbone.sync.calls.mostRecent().args[0]).toEqual('update');
});
});
describe('.addWidget', function () {
var callback;
var successResponse = { response: 'success' };
var errorResponse = { response: 'error' };
beforeEach(function () {
this.collection.create = function (options, callbacks) {
if (callback === 'success') {
callbacks.success.call(this, successResponse);
} else {
callbacks[callback].call(this, 'error', errorResponse);
}
};
});
it('should return a success response if it has been created successfully', function (done) {
var widgetModel = new Backbone.Model();
callback = 'success';
this.collection.addWidget(widgetModel, {})
.then(function (response) {
expect(response).toEqual(successResponse);
done();
});
});
it('should return an error response if the it could not be created', function (done) {
var widgetModel = new Backbone.Model();
callback = 'error';
this.collection.addWidget(widgetModel, {})
.catch(function (response) {
expect(response).toEqual(errorResponse);
done();
});
});
});
describe('.updateWidgetsOrder', function () {
it('should change set the widgets in increasing order', function () {
spyOn(this.collection, 'save').and.callThrough();
spyOn(this.collection, 'saveAsync').and.callThrough();
this.collection.updateWidgetsOrder();
this.collection.each(function (model, index) {
expect(model.order).toEqual(index);
});
expect(this.collection.save).toHaveBeenCalled();
expect(this.collection.saveAsync).toHaveBeenCalled();
});
});
describe('.saveAsync', function () {
var callback;
var successResponse = { response: 'success' };
var errorResponse = { response: 'error' };
var syncFn = function (method, collection, callbacks) {
if (callback === 'success') {
callbacks.success.call(this, successResponse);
} else if (callback === 'error') {
callbacks.error.call(this, 'error', errorResponse);
}
};
beforeEach(function () {
spyOn(Backbone, 'sync').and.callFake(syncFn);
});
it('should trigger a loading event when called', function () {
var testModel = new Backbone.Model();
spyOn(this.collection, 'trigger');
this.collection.saveAsync(testModel);
expect(this.collection.trigger).toHaveBeenCalledWith('loading', testModel);
});
it('should trigger "errorAdd" if the save function has not worked', function (done) {
var testModel = new Backbone.Model();
callback = 'error';
spyOn(this.collection, 'trigger');
this.collection.saveAsync(testModel);
setTimeout(function () {
expect(this.collection.trigger).toHaveBeenCalledWith('errorAdd', 'error');
done();
}.bind(this));
});
it('should trigger "successAdd" if the save function has worked', function (done) {
var testModel = new Backbone.Model();
callback = 'success';
spyOn(this.collection, 'trigger');
this.collection.saveAsync(testModel);
setTimeout(function () {
expect(this.collection.trigger).toHaveBeenCalledWith('successAdd', testModel);
done();
}.bind(this));
});
it('should return a resolved promise if the save function works', function (done) {
callback = 'success';
this.collection.saveAsync()
.then(function (response) {
expect(response).toEqual(successResponse);
done();
});
});
it('should return a rejected promise if the save function does not work', function (done) {
callback = 'error';
this.collection.saveAsync()
.catch(function (response) {
expect(response).toEqual(errorResponse);
done();
});
});
});
});
@@ -0,0 +1,60 @@
var WMSService = require('builder/data/wms-service');
describe('data/wms-service', function () {
beforeEach(function () {
this.wmsService = new WMSService();
});
it('should add necessary params', function () {
var url = 'http://myURL';
this.wmsService.setUrl(url);
var resultURL = this.wmsService.generateURL({ method: 'create' });
expect(resultURL.indexOf(encodeURIComponent('request=GetCapabilities'))).toBeGreaterThan(0);
expect(resultURL.indexOf(encodeURIComponent('service=WMS'))).toBeGreaterThan(0);
expect(resultURL.indexOf('type=wms')).toBeGreaterThan(0);
});
it("shouldn't remove extra params in the URL", function () {
var url = 'http://myURL?request=GetCapabilities&service=WMS&type=wms&FORMAT=image/png24';
this.wmsService.setUrl(url);
var resultURL = this.wmsService.generateURL({ method: 'create' });
expect(resultURL.indexOf(encodeURIComponent('FORMAT=image/png24'))).toBeGreaterThan(0);
expect(resultURL.indexOf(encodeURIComponent('service=WMS'))).toBeGreaterThan(0);
});
it('should set the supported matrix sets to create a WMTS resource', function () {
var url = 'http://myURL';
this.wmsService.setUrl(url);
var createURL = this.wmsService.saveLayerURL({
wms_url: url,
matrix_sets: ['EPSG:4326'],
type: 'wmts',
layer: 'foobar_layer'
});
expect(createURL).toContain('&layer=foobar_layer&');
expect(createURL).toContain('&matrix_set=EPSG:4326');
});
describe('.url', function () {
it('should add necessary params', function () {
var url = 'http://myURL';
this.wmsService.setUrl(url);
expect(url).toEqual(this.wmsService._wms_url);
});
});
describe('.supportedMatrixSets', function () {
it('should return a subset of the supported matrix sets (used for a WMTS result)', function () {
expect(this.wmsService.supportedMatrixSets(['foo', 'bar', 'EPSG:4258', 'baz', 'EPSG:4326'])).toEqual(['EPSG:4258', 'EPSG:4326']);
});
it('should return empty results for empty or missing input', function () {
expect(this.wmsService.supportedMatrixSets()).toEqual([]);
expect(this.wmsService.supportedMatrixSets([])).toEqual([]);
});
});
});