Initial commit
This commit is contained in:
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
+88
@@ -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');
|
||||
});
|
||||
});
|
||||
+39
@@ -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">© Mapbox © 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([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user