Initial commit

This commit is contained in:
zhongjin
2020-06-13 18:34:34 +08:00
commit 52aaa9f15d
655 changed files with 96796 additions and 0 deletions

View File

@@ -0,0 +1,247 @@
var $ = require('jquery');
var createVis = require('../../../../src/api/create-vis');
var scenarios = require('./scenarios');
var Loader = require('../../../../src/core/loader');
describe('create-vis:', function () {
beforeEach(function () {
this.container = $('<div id="map">').css('height', '200px');
this.containerId = this.container[0].id;
$('body').append(this.container);
});
afterEach(function () {
this.container.remove();
});
it('should throw errors when required parameters are missing', function () {
expect(function () {
createVis();
}).toThrowError('a valid DOM element or selector must be provided');
expect(function () {
createVis('something');
}).toThrowError('a valid DOM element or selector must be provided');
expect(function () {
createVis(this.containerId);
}.bind(this)).toThrowError('a vizjson URL or object must be provided');
expect(function () {
createVis(this.container[0], 'vizjson');
}.bind(this)).not.toThrowError();
expect(function () {
createVis(this.containerId, 'vizjson');
}.bind(this)).not.toThrowError();
});
it('should use the given vis.json (instead downloading) when the visjson parameter is provided', function () {
spyOn(Loader, 'get');
var visJson = scenarios.load('basic');
createVis(this.containerId, visJson);
expect(Loader.get).not.toHaveBeenCalled();
});
it('should download the vizjson file from a URL when the visjson parameter is provided and is a string', function () {
spyOn(Loader, 'get');
createVis(this.containerId, 'www.example.com/fake_vis.json');
expect(Loader.get).toHaveBeenCalledWith('www.example.com/fake_vis.json', jasmine.any(Function));
});
describe('Default (no Options)', function () {
it('should get the map center from the visJson', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.map.get('center')).toEqual(visJson.center);
});
it('should get the title from the visJson', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.get('title')).toEqual(visJson.title);
});
it('should get the description from the visJson', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.get('description')).toEqual(visJson.description);
});
it('should initialize the right protocol (https:false)', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.get('https')).toEqual(false);
});
it('should not have interactive features by default', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.get('interactiveFeatures')).toEqual(false);
});
it('should display the "loader" overlay by default [loaderControl, tiles_loader]', function () {
// loaderControl and tiles_loader appear to do the same.
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.overlaysCollection.findWhere({ type: 'loader' })).toBeDefined();
});
it('should display the "logo" by default', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.overlaysCollection.findWhere({ type: 'logo' })).toBeDefined();
});
it('should not display empty infowindow fields by default', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.get('showEmptyInfowindowFields')).toEqual(false);
});
it('should have legends', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.settings.get('showLegends')).toEqual(true);
});
it('should show layer selector', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.settings.get('showLayerSelector')).toEqual(true);
expect(visModel.settings.get('layerSelectorEnabled')).toEqual(true);
});
it('should allow scrollwheel by default', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.map.get('scrollwheel')).toEqual(true);
});
});
describe('Options', function () {
describe('skipMapInstantiation', function () {
it('should instantiate map when skipMapInstantiation option is falsy', function (done) {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
setTimeout(function () {
expect(visModel._instantiateMapWasCalled).toEqual(true);
done();
}, 25);
});
it('should NOT instantiate map when skipMapInstantiation option is truthy', function (done) {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson, { skipMapInstantiation: true });
setTimeout(function () {
expect(visModel._instantiateMapWasCalled).toEqual(false);
done();
}, 25);
});
});
});
describe('VisModel.map', function () {
it('should have the right title', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.map.get('title')).toEqual(visJson.title);
});
it('should have the right description', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.map.get('description')).toEqual(visJson.description);
});
it('should have the right bounds', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.map.get('view_bounds_sw')).toEqual(visJson.bounds[0]);
expect(visModel.map.get('view_bounds_ne')).toEqual(visJson.bounds[1]);
});
it('should have the right zoom', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.map.get('zoom')).toEqual(visJson.zoom);
});
it('should have the right scrollwheel', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.map.get('scrollwheel')).toEqual(visJson.options.scrollwheel);
});
it('should have the right drag', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.map.get('drag')).toEqual(true);
});
it('should have the right provider', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.map.get('provider')).toEqual('leaflet');
});
it('should have the right feature interactivity', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.map.get('isFeatureInteractivityEnabled')).toEqual(false);
});
it('should have the right render mode', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.map.get('renderMode')).toEqual(visJson.vector ? 'vector' : 'raster');
});
});
describe('VisModel.overlays', function () {
it('should have a share overlay', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
// TODO: Review if this overlay is still supported!
expect(visModel.overlaysCollection.findWhere({ type: 'share' })).toBeDefined();
});
it('should have a search overlay', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.overlaysCollection.findWhere({ type: 'search' })).toBeDefined();
});
it('should have a zoom overlay', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.overlaysCollection.findWhere({ type: 'zoom' })).toBeDefined();
});
it('should have a loader overlay', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.overlaysCollection.findWhere({ type: 'loader' })).toBeDefined();
});
it('should have a logo overlay', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.overlaysCollection.findWhere({ type: 'logo' })).toBeDefined();
});
it('should have a attribution overlay', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel.overlaysCollection.findWhere({ type: 'attribution' })).toBeDefined();
});
});
describe('VisModel._dataviewsCollection', function () {
it('should not have dataviews', function () {
var visJson = scenarios.load('basic');
var visModel = createVis(this.containerId, visJson);
expect(visModel._dataviewsCollection.length).toEqual(0);
});
});
});

View File

@@ -0,0 +1,314 @@
module.exports = {
'bounds': [
[
38.994,
-8.74622
],
[
42.3508,
-1.86658
]
],
'center': [
40.67241595,
-5.306396485
],
'datasource': {
'user_name': 'iago-carto',
'maps_api_template': 'https://{user}.carto.com:443',
'stat_tag': 'd71f6316-b2df-4a33-8109-fa80e8fc793d',
'template_name': 'tpl_d71f6316_b2df_4a33_8109_fa80e8fc793d'
},
'description': null,
'options': {
'legends': true,
'scrollwheel': true,
'layer_selector': true,
'dashboard_menu': true
},
'id': 'd71f6316-b2df-4a33-8109-fa80e8fc793d',
'layers': [
{
'id': '5bdcb792-78ce-4875-8ab1-869561916426',
'type': 'tiled',
'options': {
'default': 'true',
'url': 'https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_nolabels/{z}/{x}/{y}.png',
'subdomains': 'abcd',
'minZoom': '0',
'maxZoom': '18',
'name': 'Positron',
'className': 'positron_rainbow_labels',
'attribution': "&copy; <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors",
'labels': {
'url': 'https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_only_labels/{z}/{x}/{y}.png'
},
'urlTemplate': 'https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_nolabels/{z}/{x}/{y}.png'
}
},
{
'id': '1b65ba49-e202-4a0a-bb4d-5d8a416788cd',
'type': 'CartoDB',
'visible': true,
'options': {
'layer_name': 'lugares',
'attribution': '',
'cartocss': '#layer {\n marker-width: ramp([population], range(6, 33), quantiles(5));\n marker-fill: #279aff;\n marker-fill-opacity: 0.9;\n marker-allow-overlap: true;\n marker-line-width: 1;\n marker-line-color: #FFF;\n marker-line-opacity: 1;\n}',
'source': 'a0'
},
'infowindow': {
'template_name': 'infowindow_color',
'fields': [
{
'name': 'population',
'title': true,
'position': null
},
{
'name': 'description',
'title': true,
'position': null
}
],
'maxHeight': 180,
'template': "<div class='CDB-infowindow CDB-infowindow--light js-infowindow'>\n <div class='CDB-infowindow-close js-close'></div>\n <div class='CDB-infowindow-container'>\n <div class='CDB-infowindow-header CDB-infowindow-headerBg CDB-infowindow-headerBg--light js-header' style='background: #35AAE5;'>\n {{#loading}}\u2026{{/loading}}\n <ul class='CDB-infowindow-list'>\n {{#content.fields}}\n {{^index}}\n <li class='CDB-infowindow-listItem'>\n {{#title}}<h5 class='CDB-infowindow-subtitle'>{{title}}</h5>{{/title}}\n {{#value}}<h4 class='CDB-infowindow-title {{#type}}{{ type }}{{/type}}'>{{{ value }}}</h4>{{/value}}\n </li>\n {{^value}}{{/value}}\n {{/index}}\n {{/content.fields}}\n </ul>\n </div>\n <div class='CDB-infowindow-inner js-inner'>\n {{#loading}}\n <div class='CDB-Loader js-loader is-visible'></div>\n {{/loading}}\n <ul class='CDB-infowindow-list js-content'>\n {{#content.fields}}\n {{#index}}\n <li class='CDB-infowindow-listItem'>\n {{#title}}\n <h5 class='CDB-infowindow-subtitle'>{{title}}</h5>\n {{/title}}\n {{#value}}\n <h4 class='CDB-infowindow-title'>{{{ value }}}</h4>\n {{/value}}\n {{^value}}\n <h4 class='CDB-infowindow-title'>NULL</h4>\n {{/value}}\n </li>\n {{/index}}\n {{/content.fields}}\n </ul>\n </div>\n <div class='CDB-hook'>\n <div class='CDB-hook-inner'></div>\n </div>\n </div>\n</div>\n",
'alternative_names': {
'name': '',
'description': ''
},
'width': 226,
'headerColor': {
'color': {
'fixed': '#35AAE5',
'opacity': 1
}
},
'template_type': 'mustache'
},
'tooltip': {
'fields': [
{
'name': 'name',
'title': true,
'position': null
},
{
'name': 'description',
'title': true,
'position': null
}
],
'template_name': 'tooltip_dark',
'template': "<div class='CDB-Tooltip CDB-Tooltip--isDark'>\n <ul class='CDB-Tooltip-list'>\n {{#fields}}\n <li class='CDB-Tooltip-listItem'>\n {{#title}}\n <h3 class='CDB-Tooltip-listTitle'>{{{ title }}}</h3>\n {{/title}}\n <h4 class='CDB-Tooltip-listText'>{{{ value }}}</h4>\n </li>\n {{/fields}}\n </ul>\n</div>\n",
'template_type': 'mustache'
},
'legends': [
{
'conf': {
'columns': [
'title'
]
},
'created_at': '2017-08-31T14:58:44+00:00',
'definition': {
'categories': [
{
'title': 'Iago',
'color': '#279aff'
},
{
'title': 'Pablo',
'color': '#e7c5ce'
},
{
'title': 'Untitled',
'color': '#528995'
}
]
},
'id': 'cc81d782-0037-4cba-9575-abe817147bfa',
'layer_id': '1b65ba49-e202-4a0a-bb4d-5d8a416788cd',
'post_html': '',
'pre_html': '',
'title': 'Wadus',
'type': 'custom',
'updated_at': '2017-08-31T14:59:29+00:00'
}
]
},
{
'id': 'a58f47f9-7fb2-4539-a7e6-dcedd7adc9e4',
'type': 'tiled',
'options': {
'default': 'true',
'url': 'https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_only_labels/{z}/{x}/{y}.png',
'subdomains': 'abcd',
'minZoom': '0',
'maxZoom': '18',
'attribution': "&copy; <a href='http://www.openstreetmap.org/copyright'>OpenStreetMap</a> contributors",
'urlTemplate': 'https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_only_labels/{z}/{x}/{y}.png',
'type': 'Tiled',
'name': 'Positron Labels'
}
}
],
'likes': 0,
'map_provider': 'leaflet',
'overlays': [
{
'type': 'share',
'order': 2,
'options': {
'display': true,
'x': 20,
'y': 20
},
'template': ''
},
{
'type': 'search',
'order': 3,
'options': null,
'template': null
},
{
'type': 'zoom',
'order': 6,
'options': null,
'template': null
},
{
'type': 'loader',
'order': 8,
'options': {
'display': true,
'x': 20,
'y': 150
},
'template': "<div class='loader' original-title=''></div>"
},
{
'type': 'logo',
'order': 10,
'options': null,
'template': null
}
],
'title': 'Cities',
'updated_at': '2017-08-31T15:36:12+00:00',
'user': {
'fullname': 'Iago Lastra',
'avatar_url': 'https://s3.amazonaws.com/com.cartodb.users-assets.production/production/iago-carto/assets/20170720105148Avatar250.png',
'profile_url': 'https://team.carto.com/u/iago-carto'
},
'version': '3.0.0',
'widgets': [
{
'id': '30a96a5d-349d-49c4-875a-c6eba7485635',
'type': 'category',
'title': 'name',
'order': 0,
'layer_id': '1b65ba49-e202-4a0a-bb4d-5d8a416788cd',
'options': {
'column': 'name',
'aggregation_column': 'name',
'aggregation': 'count',
'column_type': 'string',
'sync_on_bbox_change': true
},
'style': {
'widget_style': {
'definition': {
'color': {
'fixed': '#9DE0AD',
'opacity': 1
}
},
'widget_color_changed': false
},
'auto_style': {
'custom': false,
'allowed': true
}
},
'source': {
'id': 'a0'
}
},
{
'id': '3d26c92f-0244-4479-9fc0-fb0715283ecd',
'type': 'histogram',
'title': 'population',
'order': 2,
'layer_id': '1b65ba49-e202-4a0a-bb4d-5d8a416788cd',
'options': {
'column': 'population',
'bins': 10,
'column_type': 'number',
'sync_on_bbox_change': true
},
'style': {
'widget_style': {
'definition': {
'color': {
'fixed': '#9DE0AD',
'opacity': 1
}
},
'widget_color_changed': false
},
'auto_style': {
'custom': false,
'allowed': true
}
},
'source': {
'id': 'a0'
}
},
{
'id': 'd0d44271-43ef-468f-b2e5-64f4a266daa4',
'type': 'category',
'title': 'name',
'order': 3,
'layer_id': '1b65ba49-e202-4a0a-bb4d-5d8a416788cd',
'options': {
'column': 'name',
'aggregation_column': 'name',
'aggregation': 'count',
'column_type': 'string',
'sync_on_bbox_change': true
},
'style': {
'widget_style': {
'definition': {
'color': {
'fixed': '#9DE0AD',
'opacity': 1
}
},
'widget_color_changed': false
},
'auto_style': {
'custom': false,
'allowed': true
}
},
'source': {
'id': 'a0'
}
}
],
'zoom': 6,
'analyses': [
{
'id': 'a0',
'type': 'source',
'options': {
'table_name': "'iago-carto'.lugares",
'simple_geom': 'point'
}
}
],
'vector': false
};

View File

@@ -0,0 +1,19 @@
/**
* Load a vis.json from the scenarios folder
* It returns a copy from the object to easy reusing.
*/
function load (index) {
// We use a switch because our current build system doesn't support variables in the require.
switch (index) {
case 'basic':
return clone(require('./basic_vis.json.js'));
}
}
function clone (object) {
return JSON.parse(JSON.stringify(object));
}
module.exports = {
load: load
};

499
test/spec/api/sql.spec.js Normal file
View File

@@ -0,0 +1,499 @@
var _ = require('underscore');
var $ = require('jquery');
var Backbone = require('backbone');
var SQL = require('../../../src/api/sql');
describe('api/sql', function () {
var USER = 'cartojs-test';
var sql;
var ajax;
var ajaxParams;
var TEST_DATA = { test: 'good' };
var NO_BOUNDS = { 'rows': [
{ 'maxx': null }
]};
var throwError;
var abort = jasmine.createSpy('abort');
beforeEach(function () {
jasmine.clock().install();
ajaxParams = null;
ajax = function (params) {
ajaxParams = params;
_.defer(function () {
params.complete && params.complete();
if (!throwError && params.success) params.success(TEST_DATA, 200);
throwError && params.error && params.error({
responseText: JSON.stringify({
error: ['jaja']
})
});
});
return {
abort: abort
};
};
spyOn($, 'ajax').and.callFake(ajax);
sql = new SQL({
user: USER,
protocol: 'https'
});
});
afterEach(function () {
jasmine.clock().uninstall();
});
it('should compile the url if not completeDomain passed', function () {
expect(sql._host()).toEqual('https://cartojs-test.carto.com/api/v2/sql');
});
it('should compile the url if completeDomain passed', function () {
var sqlBis = new SQL({
user: USER,
protocol: 'https',
completeDomain: 'http://troloroloro.com'
});
expect(sqlBis._host()).toEqual('http://troloroloro.com/api/v2/sql');
});
it('should execute a query', function () {
sql.execute('select * from table');
expect(ajaxParams.url).toEqual(
'https://' + USER + '.carto.com/api/v2/sql?q=' + encodeURIComponent('select * from table')
);
expect(ajaxParams.type).toEqual('get');
expect(ajaxParams.dataType).toEqual('json');
expect(ajaxParams.crossDomain).toEqual(true);
});
it('should be abortable', function () {
sql = new SQL({
user: USER,
protocol: 'https',
abortable: true
});
sql.execute('select * from table');
expect(abort).not.toHaveBeenCalled();
sql.execute('select * from table limit 10');
expect(abort).toHaveBeenCalled();
});
it('should parse template', function () {
sql.execute('select * from {{table}}', {
table: 'cartojs-test'
});
expect(ajaxParams.url).toEqual(
'https://' + USER + '.carto.com/api/v2/sql?q=' + encodeURIComponent('select * from cartojs-test')
);
});
it('should execute a long query', function () {
// Generating a giant query
var longSQL = [];
var i = 2000;
while (--i) longSQL.push('10000');
var longQuery = 'SELECT * ' + longSQL;
// required to have jquery as transport, is checked in the execute method
sql.execute(longQuery);
expect(ajaxParams.url).toEqual(
'https://' + USER + '.carto.com/api/v2/sql'
);
expect(ajaxParams.data.q).toEqual(longQuery);
expect(ajaxParams.type).toEqual('post');
expect(ajaxParams.dataType).toEqual('json');
expect(ajaxParams.crossDomain).toEqual(true);
});
it('should execute a long query with params', function () {
var s = new SQL({
user: 'cartojs-test',
format: 'geojson',
protocol: 'http',
host: 'charlies.com',
api_key: 'testkey',
'cartojs-test': 'test'
});
// Generating a giant query
var longSQL = [];
var i = 2000;
while (--i) longSQL.push('10000');
var longQuery = 'SELECT * ' + longSQL;
s.execute(longQuery, null, {
dp: 0
});
expect(ajaxParams.url.indexOf('http://')).not.toEqual(-1);
expect(ajaxParams.url.indexOf('cartojs-test.charlies.com')).not.toEqual(-1);
// Check that we don't have params in the URI
expect(ajaxParams.url.indexOf('&format=geojson')).toEqual(-1);
expect(ajaxParams.url.indexOf('&api_key=testkey')).toEqual(-1);
expect(ajaxParams.url.indexOf('&dp=2')).toEqual(-1);
expect(ajaxParams.url.indexOf('&cartojs-test')).toEqual(-1);
// Check that we have the params in the body
expect(ajaxParams.data.q).toEqual(longQuery);
expect(ajaxParams.data.format).toEqual('geojson');
expect(ajaxParams.data.api_key).toEqual('testkey');
expect(ajaxParams.data.dp).toEqual(0);
expect(ajaxParams['cartojs-test']).toEqual('test');
});
it('should substitute mapnik tokens', function () {
sql.execute('select !pixel_width! as w, !pixel_height! as h, !bbox! as b from {{table}}', {
table: 't'
});
var earthCircumference = 40075017;
var tileSize = 256;
var srid = 3857;
var fullResolution = earthCircumference / tileSize;
var shift = earthCircumference / 2.0;
var pw = fullResolution;
var ph = pw;
var bbox = 'ST_MakeEnvelope(' + (-shift) + ',' + (-shift) + ',' +
shift + ',' + shift + ',' + srid + ')';
expect(ajaxParams.url).toEqual(
'https://' + USER + '.carto.com/api/v2/sql?q=' + encodeURIComponent(
'select ' + pw + ' as w, ' + ph + ' as h, ' +
bbox + ' as b from t')
);
});
it('should call promise', function () {
var data;
var dataCallback;
sql.execute('select * from bla', function (e, data) {
dataCallback = data;
}).done(function (d) {
data = d;
});
jasmine.clock().tick(100);
expect(data).toEqual(TEST_DATA);
expect(dataCallback).toEqual(TEST_DATA);
});
it('should call promise on error', function () {
throwError = true;
var err = false;
sql.execute('select * from bla').error(function () {
err = true;
});
jasmine.clock().tick(10);
expect(err).toEqual(true);
});
it('should include url params', function () {
var s = new SQL({
user: 'cartojs-test',
format: 'geojson',
protocol: 'http',
host: 'charlies.com',
api_key: 'testkey',
'cartojs-test': 'test'
});
s.execute('select * from cartojs-test', null, {
dp: 2
});
expect(ajaxParams.url.indexOf('http://')).not.toEqual(-1);
expect(ajaxParams.url.indexOf('cartojs-test.charlies.com')).not.toEqual(-1);
expect(ajaxParams.url.indexOf('&format=geojson')).not.toEqual(-1);
expect(ajaxParams.url.indexOf('&api_key=testkey')).not.toEqual(-1);
expect(ajaxParams.url.indexOf('&dp=2')).not.toEqual(-1);
expect(ajaxParams.url.indexOf('&cartojs-test')).toEqual(-1);
});
it('should include extra url params', function () {
var s = new SQL({
user: 'cartojs-test',
format: 'geojson',
protocol: 'http',
host: 'charlies.com',
api_key: 'testkey',
'cartojs-test': 'test',
extra_params: ['cartojs-test']
});
s.execute('select * from cartojs-test', null, {
dp: 2
});
expect(ajaxParams.url.indexOf('http://')).not.toEqual(-1);
expect(ajaxParams.url.indexOf('cartojs-test.charlies.com')).not.toEqual(-1);
expect(ajaxParams.url.indexOf('&format=geojson')).not.toEqual(-1);
expect(ajaxParams.url.indexOf('&api_key=testkey')).not.toEqual(-1);
expect(ajaxParams.url.indexOf('&dp=2')).not.toEqual(-1);
expect(ajaxParams.url.indexOf('&cartojs-test=test')).not.toEqual(-1);
s.execute('select * from cartojs-test', null, {
dp: 2,
'cartojs-test': 'test2'
});
expect(ajaxParams.url.indexOf('&cartojs-test=test2')).not.toEqual(-1);
});
it('should use jsonp if browser does not support cors', function () {
var corsPrev = $.support.cors;
$.support.cors = false;
var s = new SQL({ user: 'jaja' });
expect(s.options.jsonp).toEqual(true);
s.execute('select * from cartojs-test', null, {
dp: 2,
jsonpCallback: 'test_callback',
cache: false
});
expect(ajaxParams.dataType).toEqual('jsonp');
expect(ajaxParams.crossDomain).toEqual(undefined);
expect(ajaxParams.jsonp).toEqual(undefined);
expect(ajaxParams.jsonpCallback).toEqual('test_callback');
expect(ajaxParams.cache).toEqual(false);
$.support.cors = corsPrev;
});
describe('.getBounds', function () {
it('should get bounds for query', function () {
var sql = 'SELECT ST_XMin(ST_Extent(the_geom)) as minx,' +
' ST_YMin(ST_Extent(the_geom)) as miny,' +
' ST_XMax(ST_Extent(the_geom)) as maxx,' +
' ST_YMax(ST_Extent(the_geom)) as maxy' +
' from (select * from cartojs-test where id=2) as subq';
var s = new SQL({ user: 'jaja' });
s.getBounds('select * from cartojs-test where id={{id}}', {id: 2});
expect(ajaxParams.url.indexOf(encodeURIComponent(sql))).not.toEqual(-1);
});
it('should get bounds for query with appostrophes', function () {
var s = new SQL({ user: 'jaja' });
s.getBounds('select * from country where name={{ name }}', {name: "'Spain'"});
expect(ajaxParams.url.indexOf('%26amp%3B%2339%3B')).toEqual(-1);
});
it('should resolve promise as error in case there are no bounds', function () {
var prevTestData = TEST_DATA;
var actualErrors = null;
TEST_DATA = NO_BOUNDS;
throwError = false;
var s = new SQL({ user: 'jaja' });
s.getBounds('SELECT * FROM somewhere')
.error(function (err) {
actualErrors = err;
});
jasmine.clock().tick(10);
expect(actualErrors).not.toBeNull();
expect(actualErrors.length).toBe(1);
expect(actualErrors[0]).toEqual('No bounds');
// Cleaning
TEST_DATA = prevTestData;
});
it('should trigger the error callback in case there are no bounds', function () {
var prevTestData = TEST_DATA;
var actualErrors = null;
TEST_DATA = NO_BOUNDS;
throwError = false;
function cb (err) {
actualErrors = err;
}
var s = new SQL({ user: 'jaja' });
s.getBounds('SELECT * FROM somewhere', null, null, cb);
jasmine.clock().tick(10);
expect(actualErrors).not.toBeNull();
expect(actualErrors.length).toBe(1);
expect(actualErrors[0]).toEqual('No bounds');
// Cleaning
TEST_DATA = prevTestData;
});
});
});
describe('api/sql.table', function () {
var USER = 'cartojs-test';
var sql;
beforeEach(function () {
sql = new SQL({
user: USER,
protocol: 'https'
});
});
it('sql', function () {
var s = sql.table('test');
expect(s.sql()).toEqual('select * from test');
s.columns(['age', 'jeta']);
expect(s.sql()).toEqual('select age,jeta from test');
s.filter('age < 10');
expect(s.sql()).toEqual('select age,jeta from test where age < 10');
s.limit(15);
expect(s.sql()).toEqual('select age,jeta from test where age < 10 limit 15');
s.order_by('age');
expect(s.sql()).toEqual('select age,jeta from test where age < 10 limit 15 order by age');
});
});
describe('api/sql column descriptions', function () {
var USER = 'manolo';
var sql;
beforeAll(function () {
this.colDate = new Backbone.Model(JSON.parse('{"name":"object_postedtime","type":"date","geometry_type":"point","bbox":[[-28.92163128242129,-201.09375],[75.84516854027044,196.875]],"analyzed":true,"success":true,"stats":{"type":"date","start_time":"2015-02-19T15:13:16.000Z","end_time":"2015-02-22T04:34:05.000Z","range":220849000,"steps":1024,"null_ratio":0,"column":"object_postedtime"}}'));
this.colFloat = new Backbone.Model(JSON.parse('{"name":"asdfd","type":"number","geometry_type":"point"}'));
this.colString = new Backbone.Model(JSON.parse('{"name":"asdfd","type":"string","geometry_type":"point"}'));
this.colGeom = new Backbone.Model(JSON.parse('{"name":"asdfd","type":"geometry","geometry_type":"point"}'));
this.colBoolean = new Backbone.Model(JSON.parse('{"name":"asdfd","type":"boolean","geometry_type":"point"}'));
this.query = 'SELECT * FROM whatevs';
sql = new SQL({
user: USER,
protocol: 'https'
});
sql.execute = function (sql, callback) {
callback(null, {});
};
});
it('should deduct correct describe method', function () {
spyOn(sql, 'describeDate');
sql.describe(this.query, this.colDate, {type: this.colDate.get('type')}, function () {});
expect(sql.describeDate).toHaveBeenCalled();
spyOn(sql, 'describeFloat');
sql.describe(this.query, this.colFloat, {type: this.colFloat.get('type')}, function () {});
expect(sql.describeFloat).toHaveBeenCalled();
spyOn(sql, 'describeString');
sql.describe(this.query, this.colString, {type: this.colString.get('type')}, function () {});
expect(sql.describeString).toHaveBeenCalled();
spyOn(sql, 'describeGeom');
sql.describe(this.query, this.colGeom, {type: this.colGeom.get('type')}, function () {});
expect(sql.describeGeom).toHaveBeenCalled();
spyOn(sql, 'describeBoolean');
sql.describe(this.query, this.colBoolean, {type: this.colBoolean.get('type')}, function () {});
expect(sql.describeBoolean).toHaveBeenCalled();
});
describe('string describer', function () {
var description;
beforeAll(function (done) {
sql.execute = function (sql, callback) {
var data = JSON.parse('{"rows":[{"uniq":462,"cnt":487,"null_count":1,"null_ratio":0.002053388090349076,"skew":0.043121149897330596,"array_agg":""}],"time":0.01,"fields":{"uniq":{"type":"number"},"cnt":{"type":"number"},"null_count":{"type":"number"},"null_ratio":{"type":"number"},"skew":{"type":"number"},"array_agg":{"type":"unknown(2287)"}},"total_rows":1}');
callback(null, data);
};
var callback = function (e, stuff) {
description = stuff;
done();
};
sql.describeString(sql, this.colString, callback); // THE COLS DON'T MATCH!!!
});
it('should return correct properties', function () {
expect(description.hist.constructor).toEqual(Array); // Right now it's an empty array because JSON.parse doesn't like our way of notating histograms
expect(description.type).toEqual('string');
expect(typeof description.null_count).toEqual('number');
expect(typeof description.distinct).toEqual('number');
expect(typeof description.null_ratio).toEqual('number');
expect(typeof description.skew).toEqual('number');
expect(typeof description.weight).toEqual('number');
});
});
describe('geometry describer', function () {
var description;
beforeAll(function (done) {
sql.execute = function (sql, callback) {
var data = {'rows': [{'geometry_type': 'ST_Point'}], 'time': 0.035, 'fields': {'geometry_type': {'type': 'string'}}, 'total_rows': 1};
callback(null, data);
};
var callback = function (e, stuff) {
description = stuff;
done();
};
sql.describeGeom(sql, this.colGeom, callback);
});
it('should return correct properties', function () {
expect(description.type).toEqual('geom');
expect(['ST_Point', 'ST_Line', 'ST_Polygon'].indexOf(description.geometry_type) > -1).toBe(true);
});
});
describe('number describer', function () {
var description;
beforeAll(function (done) {
sql.execute = function (sql, callback) {
var data = JSON.parse('{"rows":[{"hist":"{\\"(1,empty,69368)\\",\\"(25,empty,11063)\\"}","min":0,"max":4,"avg":0.3745819397993311,"cnt":89401,"uniq":5,"null_ratio":0,"stddev":0.000009057366328792043,"stddevmean":2.1617223091836313,"dist_type":"U","quantiles":[0,1,2,2,3,4,4],"equalint":[0,0,0,0,0,0,0],"jenks":[0,1,2,3,4],"headtails":[0,1,2,3,4],"cat_hist":"{\\"(1,empty,69368)\\",\\"(25,empty,11063)\\"}"}],"time":1.442,"fields":{"hist":{"type":"unknown(2287)"},"min":{"type":"number"},"max":{"type":"number"},"avg":{"type":"number"},"cnt":{"type":"number"},"uniq":{"type":"number"},"null_ratio":{"type":"number"},"stddev":{"type":"number"},"stddevmean":{"type":"number"},"dist_type":{"type":"string"},"quantiles":{"type":"number[]"},"equalint":{"type":"number[]"},"jenks":{"type":"number[]"},"headtails":{"type":"number[]"},"cat_hist":{"type":"unknown(2287)"}},"total_rows":1}');
callback(null, data);
};
var callback = function (e, stuff) {
description = stuff;
done();
};
sql.describeFloat(sql, this.colGeom, callback);
});
it('should return correct properties', function () {
expect(description.type).toEqual('number');
expect(['A', 'U', 'F', 'J'].indexOf(description.dist_type) > -1).toBe(true);
var i;
var numTypes = ['avg', 'max', 'min', 'stddevmean', 'weight', 'stddev', 'null_ratio', 'count'];
for (i = 0; i < numTypes.length; i++) {
expect(typeof description[numTypes[i]]).toEqual('number');
}
var arrayTypes = ['quantiles', 'equalint', 'jenks', 'headtails', 'cat_hist', 'hist'];
for (i = 0; i < arrayTypes.length; i++) {
expect(description[arrayTypes[i]].constructor).toEqual(Array);
}
});
});
describe('boolean describer', function () {
var description;
beforeAll(function (done) {
sql.execute = function (sql, callback) {
var data = {'rows': [
{'true_ratio': 0.3377926421404682, 'null_ratio': 0, 'uniq': 2, 'cnt': 89401}
],
'time': 0.251,
'fields': {'true_ratio': {'type': 'number'}, 'null_ratio': {'type': 'number'}, 'uniq': {'type': 'number'}, 'cnt': {'type': 'number'}},
'total_rows': 1
};
callback(null, data);
};
var callback = function (e, stuff) {
description = stuff;
done();
};
sql.describeBoolean(sql, this.colGeom, callback);
});
it('should return correct properties', function () {
expect(description.type).toEqual('boolean');
expect(typeof description.true_ratio).toEqual('number');
expect(typeof description.distinct).toEqual('number');
expect(typeof description.count).toEqual('number');
expect(typeof description.null_ratio).toEqual('number');
});
});
});

View File

@@ -0,0 +1,459 @@
/* global L */
/* global google */
var _ = require('underscore');
var carto = require('../../../../src/api/v4');
var LeafletLayer = require('../../../../src/api/v4/native/leaflet-layer');
var GoogleMapsMapType = require('../../../../src/api/v4/native/google-maps-map-type');
var Engine = require('../../../../src/engine');
var Events = require('../../../../src/api/v4/events');
describe('api/v4/client', function () {
var client;
beforeEach(function () {
client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
serverUrl: 'https://cartojs-test.carto.com',
username: 'cartojs-test'
});
});
describe('constructor', function () {
it('should build a new client', function () {
expect(client).toBeDefined();
expect(client.getLayers()).toEqual([]);
});
it('should autogenerate the carto url when is not given', function () {
client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test'
});
expect(client._engine._windshaftSettings.urlTemplate).toEqual('https://cartojs-test.carto.com');
});
it('should autogenerate the carto url when a template is given', function () {
client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test',
serverUrl: 'https://{username}.mycarto.com'
});
expect(client._engine._windshaftSettings.urlTemplate).toEqual('https://cartojs-test.mycarto.com');
});
it('should accept a ipv4/user/{username} as a valid serverURL', function () {
expect(function () {
client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test',
serverUrl: 'https://192.168.0.1/user/cartojs-test'
});
}).not.toThrow();
});
it('should reject a valid ip adress with no /user/{username} as serverURL', function () {
expect(function () {
client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test',
serverUrl: 'https://10.10.0.1'
});
}).toThrow();
});
it('should reject an invalid ip adress with no /user/{username}', function () {
expect(function () {
client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test',
serverUrl: 'https://192.168.1'
});
}).toThrow();
});
describe('error handling', function () {
describe('apiKey', function () {
it('should throw a descriptive error when apikey is not given', function () {
expect(function () {
new carto.Client({ username: "cartojs-test" }); // eslint-disable-line
}).toThrowError('apiKey property is required.');
});
it('should throw a descriptive error when apikey is not a string', function () {
expect(function () {
new carto.Client({ apiKey: 1234, username: "cartojs-test" }); // eslint-disable-line
}).toThrowError('apiKey property must be a string.');
});
it('should throw a descriptive error when apikey is not a string', function () {
expect(function () {
new carto.Client({ apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18' }); // eslint-disable-line
}).toThrowError('username property is required.');
});
});
describe('username', function () {
it('should throw a descriptive error when username is not a string', function () {
expect(function () {
new carto.Client({ apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18', username: 1234 }); // eslint-disable-line
}).toThrowError('username property must be a string.');
});
});
describe('serverUrl', function () {
it('should throw a descriptive error when serverUrl is given and is not valid', function () {
expect(function () {
// eslint-disable-next-line
new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test',
serverUrl: 'invalid-url'
});
}).toThrowError('serverUrl is not a valid URL.');
});
it("should throw a descriptive error when serverUrl doesn't match the username", function () {
expect(function () {
// eslint-disable-next-line
new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test',
serverUrl: 'https://invald-username.carto.com'
});
}).toThrowError("serverUrl doesn't match the username.");
});
});
});
});
describe('.addLayer', function () {
var source;
var style;
var layer;
beforeEach(function () {
source = new carto.source.Dataset('ne_10m_populated_places_simple', {
id: 'a0'
});
style = new carto.style.CartoCSS('#layer { marker-fill: red; }');
layer = new carto.layer.Layer(source, style, {});
});
it('should add a new layer', function () {
client.addLayer(layer);
expect(client.getLayers()[0]).toEqual(layer);
});
it('should add a new layer triggering a reload cycle by default', function (done) {
spyOn(client._engine, 'reload').and.callThrough();
client.addLayer(layer).then(function () {
expect(client._engine.reload).toHaveBeenCalled();
expect(client._engine.reload.calls.count()).toEqual(1);
done();
});
});
it('should return a rejected promise when some error happened', function (done) {
var errorMock = new Error('Error-Mock');
spyOn(client._engine, 'reload').and.returnValue(
Promise.reject(errorMock)
);
client.addLayer(layer).catch(function (error) {
expect(error.message).toEqual(errorMock.message);
done();
});
});
it('should return a significative error when layer parameter is not a valid layer', function () {
expect(function () {
client.addLayer([]);
}).toThrowError('The given object is not a layer.');
});
it('should throw a descriptive error when two layers with the same id are added', function () {
expect(function () {
client.addLayer(layer);
client.addLayer(
new carto.layer.Layer(source, style, { id: layer.getId() })
);
}).toThrowError('A layer with the same ID already exists in the client.');
});
});
describe('.addLayers', function () {
it('should add a layers array', function () { });
it('should add a layer array triggering ONE reload cycle by default', function () { });
it('should add a layers array without triggering a reload cycle when opts.reload is false', function () { });
it('should return a rejected promise when some error happened', function () { });
});
describe('.getLayers', function () {
it('should return an empty array when there are no layers', function () {
expect(client.getLayers()).toEqual([]);
});
xit('should return the layers stored in the client', function (done) {
var source = new carto.source.Dataset('ne_10m_populated_places_simple');
var style = new carto.style.CartoCSS('#layer { marker-fill: red; }');
var layer = new carto.layer.Layer(source, style, {});
client.addLayer(layer).then(function () {
expect(client.getLayers()[0]).toEqual(layer);
done();
});
});
});
describe('.removeLayer', function () {
it('should throw a descriptive error when the parameter is invalid', function () {
expect(function () {
client.removeLayer({});
}).toThrowError('The given object is not a layer.');
});
it('¿should throw a descriptive error when layer is not in the client?', function () {
pending('We should decide if this makes sense.');
var source = new carto.source.Dataset('ne_10m_populated_places_simple');
var style = new carto.style.CartoCSS('#layer { marker-fill: red; }');
var layer = new carto.layer.Layer(source, style, {});
expect(function () {
client.removeLayer(layer);
}).toThrowError('The layer is not in the client');
});
it('should remove the layer when is in the client', function () {
var source = new carto.source.Dataset('ne_10m_populated_places_simple');
var style = new carto.style.CartoCSS('#layer { marker-fill: red; }');
var layer = new carto.layer.Layer(source, style, {});
client.addLayer(layer);
expect(client.getLayers().length).toEqual(1);
client.removeLayer(layer);
expect(client.getLayers().length).toEqual(0);
});
});
describe('.removeLayers', function () {
it('must remove all layers', function () {
var source = new carto.source.Dataset('ne_10m_populated_places_simple');
var style = new carto.style.CartoCSS('#layer { marker-fill: red; }');
var layerA = new carto.layer.Layer(source, style, {});
var layerB = new carto.layer.Layer(source, style, {});
var layerC = new carto.layer.Layer(source, style, {});
client.addLayers([layerA, layerB, layerC]);
expect(client.getLayers().length).toEqual(3);
client.removeLayers(client.getLayers());
expect(client.getLayers().length).toEqual(0);
});
});
describe('.removeDataview', function () {
var categoryDataview, populationDataview;
beforeEach(function () {
var source = new carto.source.Dataset('ne_10m_populated_places_simple');
categoryDataview = new carto.dataview.Category(source, 'adm0name', {
limit: 10,
operation: carto.operation.SUM,
operationColumn: 'pop_max'
});
populationDataview = new carto.dataview.Category(source, 'adm1name', {
limit: 10,
operation: carto.operation.SUM,
operationColumn: 'pop_max'
});
client.addDataview(categoryDataview);
client.addDataview(populationDataview);
spyOn(client._engine, 'removeDataview');
spyOn(categoryDataview, 'disable');
spyOn(client, '_reload');
});
it('removes the dataview', function () {
expect(client._dataviews.length).toBe(2);
client.removeDataview(categoryDataview);
expect(client._dataviews.length).toBe(1);
});
it('disables the dataview', function () {
client.removeDataview(categoryDataview);
expect(client._engine.removeDataview).toHaveBeenCalled();
});
it('triggers a reload cycle', function () {
client.removeDataview(categoryDataview);
expect(client._reload).toHaveBeenCalled();
});
});
describe('.moveLayer', function () {
it('should throw a descriptive error when the parameter is invalid', function () {
var source = new carto.source.Dataset('ne_10m_populated_places_simple');
var style = new carto.style.CartoCSS('#layer { marker-fill: red; }');
var layer = new carto.layer.Layer(source, style, {});
expect(function () {
client.moveLayer({}, 0);
}).toThrowError('The given object is not a layer.');
expect(function () {
client.moveLayer(layer, false);
}).toThrowError('index property must be a number.');
expect(function () {
client.moveLayer(layer, 1234);
}).toThrowError('index is out of range.');
});
it('should move the layer when is in the client', function () {
var source = new carto.source.Dataset('ne_10m_populated_places_simple');
var style = new carto.style.CartoCSS('#layer { marker-fill: red; }');
var layer0 = new carto.layer.Layer(source, style, {});
var layer1 = new carto.layer.Layer(source, style, {});
client.addLayers([layer0, layer1]);
expect(client.getLayers()[0]).toEqual(layer0);
expect(client.getLayers()[1]).toEqual(layer1);
client.moveLayer(layer0, 1);
expect(client.getLayers()[1]).toEqual(layer0);
expect(client.getLayers()[0]).toEqual(layer1);
});
});
describe('.getLeafletLayer', function () {
var leafletLayer;
beforeEach(function () {
leafletLayer = client.getLeafletLayer();
});
it('should return an instance of LeafletLayer', function () {
expect(leafletLayer instanceof LeafletLayer).toBe(true);
});
it('should return the same object', function () {
expect(leafletLayer === client.getLeafletLayer()).toBe(true);
});
it('should return a L.TileLayer', function () {
expect(leafletLayer instanceof L.TileLayer).toBe(true);
});
it('should have the OpenStreetMap / Carto attribution', function () {
expect(leafletLayer.getAttribution()).toBe(
'&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; <a href="https://carto.com/attribution">CARTO</a>'
);
});
it('should throw an error if Leaflet is not loaded', function () {
var L = _.clone(window.L);
window.L = undefined;
expect(function () {
client.getLeafletLayer();
}).toThrowError('Leaflet is required');
// Restore window.L
window.L = L;
});
it('should throw an error if Leaflet version is <1.0', function () {
var L = _.clone(window.L);
window.L = { version: '0.7' };
expect(function () {
client.getLeafletLayer();
}).toThrowError('Leaflet +1.0 is required');
// Restore window.L
window.L = L;
});
});
describe('.getGoogleMapsMapType', function () {
var element;
var mapType;
beforeEach(function () {
element = document.createElement('div');
mapType = client.getGoogleMapsMapType(new google.maps.Map(element));
});
afterEach(function () {
element.remove();
});
it('should return an instance of GoogleMapsMapType', function () {
expect(mapType instanceof GoogleMapsMapType).toBe(true);
});
it('should return the same object', function () {
expect(mapType === client.getGoogleMapsMapType()).toBe(true);
});
it('should return an object with a MapType interface', function () {
expect(mapType.tileSize).toBeDefined();
expect(mapType.getTile).toBeDefined();
});
it('should throw an error if Google Maps is not loaded', function () {
var google = _.clone(window.google);
window.google = undefined;
expect(function () {
client.getGoogleMapsMapType();
}).toThrowError('Google Maps is required');
window.google = { maps: undefined };
expect(function () {
client.getGoogleMapsMapType();
}).toThrowError('Google Maps is required');
// Restore window.google
window.google = google;
});
it('should throw an error if Google Maps version is < 3.31', function () {
var google = _.clone(window.google);
window.google.maps = { version: '2.4' };
expect(function () {
client.getGoogleMapsMapType();
}).toThrowError('Google Maps version should be >= 3.31');
// Restore window.google
window.google = google;
});
});
describe('engine bindings', function () {
it('should capture engine LAYER_ERROR and trigger own error', function () {
var capturedError;
client.on(Events.ERROR, function (error) {
capturedError = error;
});
client._engine._eventEmmitter.trigger(Engine.Events.LAYER_ERROR);
expect(capturedError).toBeDefined();
expect(capturedError.name).toEqual('CartoError');
});
});
});

View File

@@ -0,0 +1,243 @@
var DataviewBase = require('../../../../../src/api/v4/dataview/base');
var status = require('../../../../../src/api/v4/constants').status;
var carto = require('../../../../../src/api/v4/index');
function createSourceMock () {
return new carto.source.Dataset('foo');
}
function createEngineMock () {
var engine = {
name: 'Engine mock',
reload: function () {}
};
spyOn(engine, 'reload');
return engine;
}
describe('api/v4/dataview/base', function () {
var base = new DataviewBase();
it('.getStatus should return the internal status', function () {
expect(base.getStatus()).toEqual(base._status);
});
describe('.isLoading', function () {
it('should return true if loading and false otherwise', function () {
base._status = status.NOT_LOADED;
expect(base.isLoading()).toBe(false);
base._status = status.LOADING;
expect(base.isLoading()).toBe(true);
});
it('should return true if loading and false otherwise', function () {
base._status = status.NOT_LOADED;
expect(base.isLoaded()).toBe(false);
base._status = status.LOADED;
expect(base.isLoaded()).toBe(true);
});
});
describe('.hasError', function () {
it('should return true if loading and false otherwise', function () {
base._status = status.NOT_LOADED;
expect(base.hasError()).toBe(false);
base._status = status.ERROR;
expect(base.hasError()).toBe(true);
});
});
describe('.enable', function () {
it('should enable the dataview', function () {
base.enable();
expect(base._enabled).toBe(true);
});
it('should return the dataview', function () {
expect(base.enable()).toBe(base);
});
});
describe('.disable', function () {
it('should disable the dataview', function () {
base.disable();
expect(base._enabled).toBe(false);
});
it('should return the dataview', function () {
expect(base.disable()).toBe(base);
});
});
describe('.isEnabled', function () {
it('should return true if enabled and false otherwise', function () {
base.disable();
expect(base.isEnabled()).toBe(false);
base.enable();
expect(base.isEnabled()).toBe(true);
});
});
describe('.getSource', function () {
it('should return the source object', function () {
var source = new carto.source.Dataset('table_name');
base._source = source;
expect(base.getSource()).toBe(source);
});
});
describe('.setColumn', function () {
it('should set the column name as string', function () {
var column = 'column-test';
base.setColumn(column);
expect(base._column).toBe(column);
});
it('should throw an error if the argument is not string or undefined', function () {
var requiredColumnError;
var stringColumnError;
var emptyColumnError;
try { base.setColumn(); } catch (error) { requiredColumnError = error; }
try { base.setColumn(12); } catch (error) { stringColumnError = error; }
try { base.setColumn(''); } catch (error) { emptyColumnError = error; }
expect(requiredColumnError).toEqual(jasmine.objectContaining({
message: 'Column property is required.',
type: 'dataview',
errorCode: 'validation:dataview:column-required'
}));
expect(stringColumnError).toEqual(jasmine.objectContaining({
message: 'Column property must be a string.',
type: 'dataview',
errorCode: 'validation:dataview:column-string'
}));
expect(emptyColumnError).toEqual(jasmine.objectContaining({
message: 'Column property must be not empty.',
type: 'dataview',
errorCode: 'validation:dataview:empty-column'
}));
});
it('should return the dataview', function () {
var column = 'column-test';
expect(base.setColumn(column)).toBe(base);
});
});
describe('.getColumn', function () {
it('should return the column name', function () {
var column = 'column-test2';
base._column = column;
expect(base.getColumn()).toBe(column);
});
});
describe('.getData', function () {
it('.getData should not be defined in the base dataview', function () {
expect(function () { base.getData(); }).toThrowError(Error, 'getData must be implemented by the particular dataview.');
});
});
describe('._changeProperty', function () {
it('should set internal property', function () {
base._example = 'something';
base._changeProperty('example', 'whatever');
expect(base._example).toEqual('whatever');
});
it('should trigger change is there is no internal model', function () {
var eventValue = '';
base._example = 'something';
base.on('exampleChanged', function (newValue) {
eventValue = newValue;
});
base._changeProperty('example', 'whatever');
expect(eventValue).toEqual('whatever');
});
it('should update internal model and trigger a change when the internalModel exists', function () {
var internalModelSpy = jasmine.createSpyObj('internalModelSpy', ['set']);
base._example = 'something';
base._internalModel = internalModelSpy;
spyOn(base, '_triggerChange');
base._changeProperty('example', 'whatever');
expect(internalModelSpy.set).toHaveBeenCalledWith('example', 'whatever');
expect(base._triggerChange).toHaveBeenCalled();
});
});
describe('.$setEngine', function () {
var engine;
var dataview;
beforeEach(function () {
// We use Formula for these tests. Any other dataview could be used instead.
dataview = new carto.dataview.Formula(createSourceMock(), 'population', {
operation: carto.operation.MIN
});
engine = createEngineMock();
});
it('internalModel events should be properly hooked up', function () {
dataview.$setEngine(engine);
var internalModel = dataview._internalModel;
var eventStatus = null;
var eventError = null;
var dataviewError = null;
dataview.on('statusChanged', function (newStatus, error) {
eventStatus = newStatus;
eventError = error;
});
dataview.on('error', function (error) {
dataviewError = error;
});
// Loading
internalModel.trigger('loading');
expect(dataview.getStatus()).toEqual('loading');
expect(eventStatus).toEqual('loading');
// Loaded
internalModel.trigger('loaded');
expect(dataview.getStatus()).toEqual('loaded');
expect(eventStatus).toEqual('loaded');
// Error
internalModel.trigger('statusError', internalModel, 'an error');
expect(dataview.getStatus()).toEqual('error');
expect(eventStatus).toEqual('error');
expect(eventError).toEqual('an error');
expect(dataviewError.name).toEqual('CartoError');
});
});
describe('add bbox filter', function () {
it('should check if it is a proper object', function () {
function test () {
base.addFilter('invalid_filter');
}
expect(test).toThrowError('Filter property is required.');
});
it('should throw an error if an SQL filter is passed', function () {
function test () {
var categoryFilter = new carto.filter.Category('fake_column', { in: ['category_value'] });
base.addFilter(categoryFilter);
}
expect(test).toThrowError('Filter property is required.');
});
});
});

View File

@@ -0,0 +1,438 @@
var Backbone = require('backbone');
var _ = require('underscore');
var carto = require('../../../../../src/api/v4/index');
var createEngine = require('../../../fixtures/engine.fixture.js');
function createInternalModelMock () {
var internalModelMock = {
set: function () {},
get: function () {}
};
spyOn(internalModelMock, 'set');
spyOn(internalModelMock, 'get').and.callFake(function (key) {
switch (key) {
case 'count': return 42;
case 'max': return 9;
case 'min': return 1;
case 'nulls': return 0;
case 'data': return [
{
name: 'cat1',
value: 1,
agg: false
},
{
name: 'others',
value: 9,
agg: true
}
];
}
});
_.extend(internalModelMock, Backbone.Events);
return internalModelMock;
}
function createSourceMock () {
return new carto.source.Dataset('foo');
}
describe('api/v4/dataview/category', function () {
var source = createSourceMock();
describe('initialization', function () {
it('source must be provided', function () {
var error;
try { new carto.dataview.Category(); } catch (err) { error = err; } // eslint-disable-line no-new
expect(error).toEqual(jasmine.objectContaining({
message: 'Source property is required.',
type: 'dataview',
errorCode: 'validation:dataview:source-required'
}));
});
it('column must be provided', function () {
var error;
try { new carto.dataview.Category(source); } catch (err) { error = err; } // eslint-disable-line no-new
expect(error).toEqual(jasmine.objectContaining({
message: 'Column property is required.',
type: 'dataview',
errorCode: 'validation:dataview:column-required'
}));
});
it('options set to default if not provided', function () {
var column = 'population';
var dataview = new carto.dataview.Category(source, column);
expect(dataview._limit).toEqual(6);
expect(dataview._operation).toEqual(carto.operation.COUNT);
expect(dataview._operationColumn).toEqual('population');
});
it('options set to the provided value', function () {
var dataview = new carto.dataview.Category(source, 'population', {
limit: 10,
operation: carto.operation.AVG,
operationColumn: 'column-test'
});
expect(dataview._limit).toEqual(10);
expect(dataview._operation).toEqual(carto.operation.AVG);
expect(dataview._operationColumn).toEqual('column-test');
});
it('throw error if no correct operation is provided', function () {
var error;
var test = function () {
new carto.dataview.Category(source, 'population', { // eslint-disable-line no-new
operation: 'exponential'
});
};
try { test(); } catch (err) { error = err; }
expect(error).toEqual(jasmine.objectContaining({
message: 'Operation for category dataview is not valid. Use carto.operation',
type: 'dataview',
errorCode: 'validation:dataview:category-invalid-operation'
}));
});
});
describe('.setLimit', function () {
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Category(source, 'population');
});
it('checks if limit is valid', function () {
var requiredError;
var numberError;
var positiveError;
try { dataview.setLimit(); } catch (err) { requiredError = err; }
try { dataview.setLimit('12'); } catch (err) { numberError = err; }
try { dataview.setLimit(0); } catch (err) { positiveError = err; }
expect(requiredError).toEqual(jasmine.objectContaining({
message: 'Limit for category dataview is required.',
type: 'dataview',
errorCode: 'validation:dataview:category-limit-required'
}));
expect(numberError).toEqual(jasmine.objectContaining({
message: 'Limit for category dataview must be a number.',
type: 'dataview',
errorCode: 'validation:dataview:category-limit-number'
}));
expect(positiveError).toEqual(jasmine.objectContaining({
message: 'Limit for category dataview must be greater than 0.',
type: 'dataview',
errorCode: 'validation:dataview:category-limit-positive'
}));
});
it('if limit is valid, it assigns it to property, returns this and nothing else if there is no internaModel', function () {
var returnedObject = dataview.setLimit(10);
expect(dataview.getLimit()).toEqual(10);
expect(returnedObject).toBe(dataview);
});
it('sets limit in internal model if exists', function () {
var internalModelMock = createInternalModelMock();
dataview._internalModel = internalModelMock;
dataview.setLimit(1);
var operationArgs = internalModelMock.set.calls.mostRecent().args;
expect(operationArgs[0]).toEqual('categories');
expect(operationArgs[1]).toEqual(1);
});
it('should trigger a limitChanged event', function () {
var limitChangedSpy = jasmine.createSpy('operationaChangedSpy');
dataview.on('limitChanged', limitChangedSpy);
expect(limitChangedSpy).not.toHaveBeenCalled();
dataview.$setEngine(createEngine());
dataview.setLimit(7);
expect(limitChangedSpy).toHaveBeenCalledWith(7);
});
});
describe('.setOperation', function () {
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Category(source, 'population');
});
it('checks if operation is valid', function () {
var error;
var test = function () {
dataview.setOperation('swordfish');
};
try { test(); } catch (err) { error = err; }
expect(error).toEqual(jasmine.objectContaining({
message: 'Operation for category dataview is not valid. Use carto.operation',
type: 'dataview',
errorCode: 'validation:dataview:category-invalid-operation'
}));
});
it('if operation is valid, it assigns it to property, returns this and nothing else if there is no internaModel', function () {
var returnedObject = dataview.setOperation(carto.operation.AVG);
expect(dataview.getOperation()).toEqual(carto.operation.AVG);
expect(returnedObject).toBe(dataview);
});
it('sets operation in internal model if exists', function () {
var internalModelMock = createInternalModelMock();
dataview._internalModel = internalModelMock;
dataview.setOperation(carto.operation.AVG);
var operationArgs = internalModelMock.set.calls.mostRecent().args;
expect(operationArgs[0]).toEqual('aggregation');
expect(operationArgs[1]).toEqual(carto.operation.AVG);
});
it('should trigger a operationChanged event', function () {
var operationChangedSpy = jasmine.createSpy('operationaChangedSpy');
dataview.on('operationChanged', operationChangedSpy);
expect(operationChangedSpy).not.toHaveBeenCalled();
dataview.$setEngine(createEngine());
dataview.setOperation(carto.operation.AVG);
expect(operationChangedSpy).toHaveBeenCalledWith(carto.operation.AVG);
});
});
describe('.setOperationColumn', function () {
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Category(source, 'population');
});
it('checks if operation is valid', function () {
var requiredError;
var numberError;
var emptyError;
try { dataview.setOperationColumn(); } catch (err) { requiredError = err; }
try { dataview.setOperationColumn(12); } catch (err) { numberError = err; }
try { dataview.setOperationColumn(''); } catch (err) { emptyError = err; }
expect(requiredError).toEqual(jasmine.objectContaining({
message: 'Operation column for category dataview is required.',
type: 'dataview',
errorCode: 'validation:dataview:category-operation-required'
}));
expect(numberError).toEqual(jasmine.objectContaining({
message: 'Operation column for category dataview must be a string.',
type: 'dataview',
errorCode: 'validation:dataview:category-operation-string'
}));
expect(emptyError).toEqual(jasmine.objectContaining({
message: 'Operation column for category dataview must be not empty.',
type: 'dataview',
errorCode: 'validation:dataview:category-operation-empty'
}));
});
it('if operation is valid, it assigns it to property, returns this and nothing else if there is no internaModel', function () {
var returnedObject = dataview.setOperationColumn('columnA');
expect(dataview.getOperationColumn()).toEqual('columnA');
expect(returnedObject).toBe(dataview);
});
it('sets operation in internal model if exists', function () {
var internalModelMock = createInternalModelMock();
dataview._internalModel = internalModelMock;
dataview.setOperationColumn('columnB');
var operationArgs = internalModelMock.set.calls.mostRecent().args;
expect(operationArgs[0]).toEqual('aggregation_column');
expect(operationArgs[1]).toEqual('columnB');
});
it('should trigger a operationColumnChanged event', function () {
var operationColumnChangedSpy = jasmine.createSpy('operationaChangedSpy');
dataview.on('operationColumnChanged', operationColumnChangedSpy);
expect(operationColumnChangedSpy).not.toHaveBeenCalled();
dataview.$setEngine(createEngine());
dataview.setOperationColumn('column2');
expect(operationColumnChangedSpy).toHaveBeenCalledWith('column2');
});
});
describe('.getData', function () {
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Category(source, 'population', {
operation: carto.operation.SUM,
operationColumn: 'column-test'
});
});
it('returns null if there is no internalModel', function () {
var data = dataview.getData();
expect(data).toBeNull();
});
it('returns data from internalModel', function () {
var internalModelMock = createInternalModelMock();
dataview._internalModel = internalModelMock;
var data = dataview.getData();
expect(data).toEqual({
count: 42,
max: 9,
min: 1,
nulls: 0,
operation: carto.operation.SUM,
categories: [
{
name: 'cat1',
value: 1,
group: false
},
{
name: 'others',
value: 9,
group: true
}
]
});
});
});
describe('.$setEngine', function () {
var engine;
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Category(source, 'population', {
operation: carto.operation.MIN,
operationColumn: 'column-test'
});
engine = createEngine();
});
it('creates the internal model', function () {
dataview.disable(); // To test that it passes the ._enabled property to the internal model
dataview.$setEngine(engine);
var internalModel = dataview.$getInternalModel();
expect(internalModel.get('source')).toBe(dataview._source.$getInternalModel());
expect(internalModel.get('column')).toEqual(dataview._column);
expect(internalModel.get('categories')).toEqual(dataview._limit);
expect(internalModel.get('aggregation')).toEqual(dataview._operation);
expect(internalModel.get('aggregation_column')).toEqual(dataview._operationColumn);
expect(internalModel.isEnabled()).toBe(false);
expect(internalModel._engine).toBe(engine);
});
it('calling twice to $setEngine does not create another internalModel', function () {
spyOn(dataview, '_createInternalModel').and.callThrough();
dataview.$setEngine(engine);
dataview.$setEngine(engine);
expect(dataview._createInternalModel.calls.count()).toBe(1);
});
describe('spatial filters', function () {
it('creates the internal model with BoundingBox filter if provided', function () {
var filter = new carto.filter.BoundingBox();
dataview.addFilter(filter);
dataview.$setEngine(engine);
var internalModel = dataview.$getInternalModel();
expect(internalModel._bboxFilter).toBeDefined();
expect(internalModel.syncsOnBoundingBoxChanges()).toBe(true);
});
it('allows removing a BoundingBox filter', function () {
var filter = new carto.filter.BoundingBox();
dataview.addFilter(filter);
dataview.$setEngine(engine);
expect(dataview.hasFilter(filter)).toBe(true);
dataview.removeFilter(filter);
expect(dataview.hasFilter(filter)).toBe(false);
var internalModel = dataview.$getInternalModel();
expect(internalModel._bboxFilter).toBeNull();
expect(internalModel.syncsOnBoundingBoxChanges()).toBe(false);
});
it('creates the internal model with Circle filter if provided', function () {
var filter = new carto.filter.Circle();
dataview.addFilter(filter);
dataview.$setEngine(engine);
var internalModel = dataview.$getInternalModel();
expect(internalModel._circleFilter).toBeDefined();
expect(internalModel.syncsOnCircleChanges()).toBe(true);
});
it('allows removing a Circle filter', function () {
var filter = new carto.filter.Circle();
dataview.addFilter(filter);
dataview.$setEngine(engine);
expect(dataview.hasFilter(filter)).toBe(true);
dataview.removeFilter(filter);
expect(dataview.hasFilter(filter)).toBe(false);
var internalModel = dataview.$getInternalModel();
expect(internalModel._circleFilter).toBeNull();
expect(internalModel.syncsOnCircleChanges()).toBe(false);
});
it('creates the internal model with Polygon filter if provided', function () {
var filter = new carto.filter.Polygon();
dataview.addFilter(filter);
dataview.$setEngine(engine);
var internalModel = dataview.$getInternalModel();
expect(internalModel._polygonFilter).toBeDefined();
expect(internalModel.syncsOnPolygonChanges()).toBe(true);
});
it('allows removing a Polygon filter', function () {
var filter = new carto.filter.Polygon();
dataview.addFilter(filter);
dataview.$setEngine(engine);
expect(dataview.hasFilter(filter)).toBe(true);
dataview.removeFilter(filter);
expect(dataview.hasFilter(filter)).toBe(false);
var internalModel = dataview.$getInternalModel();
expect(internalModel._polygonFilter).toBeNull();
expect(internalModel.syncsOnPolygonChanges()).toBe(false);
});
});
});
});

View File

@@ -0,0 +1,265 @@
var Backbone = require('backbone');
var _ = require('underscore');
var carto = require('../../../../../src/api/v4/index');
function createInternalModelMock () {
var internalModelMock = {
set: function () {},
get: function () {}
};
spyOn(internalModelMock, 'set');
spyOn(internalModelMock, 'get').and.callFake(function (key) {
if (key === 'data') {
return 1234;
}
if (key === 'nulls') {
return 42;
}
});
_.extend(internalModelMock, Backbone.Events);
return internalModelMock;
}
function createSourceMock () {
return new carto.source.Dataset('foo');
}
function createEngineMock () {
var engine = {
name: 'Engine mock',
reload: function () {}
};
spyOn(engine, 'reload');
return engine;
}
describe('api/v4/dataview/formula', function () {
var source = createSourceMock();
describe('initialization', function () {
it('source must be provided', function () {
var test = function () {
new carto.dataview.Formula(); // eslint-disable-line no-new
};
expect(test).toThrowError(Error, 'Source property is required.');
});
it('column must be provided', function () {
var test = function () {
new carto.dataview.Formula(source); // eslint-disable-line no-new
};
expect(test).toThrowError('Column property is required.');
});
it('options set to default if not provided', function () {
var column = 'population';
var dataview = new carto.dataview.Formula(source, column);
expect(dataview._operation).toEqual(carto.operation.COUNT);
});
it('options set to the provided value', function () {
var dataview = new carto.dataview.Formula(source, 'population', {
operation: carto.operation.AVG
});
expect(dataview._operation).toEqual(carto.operation.AVG);
});
it('throw error if no correct operation is provided', function () {
var test = function () {
new carto.dataview.Formula(source, 'population', { // eslint-disable-line no-new
operation: 'exponential'
});
};
expect(test).toThrowError(Error, 'Operation for formula dataview is not valid. Use carto.operation');
});
});
describe('.setOperation', function () {
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Formula(source, 'population');
});
it('checks if operation is valid', function () {
var test = function () {
dataview.setOperation('swordfish');
};
expect(test).toThrowError(Error, 'Operation for formula dataview is not valid. Use carto.operation');
});
it('if operation is valid, it assigns it to property, returns this and nothing else if there is no internaModel', function () {
var returnedObject = dataview.setOperation(carto.operation.AVG);
expect(dataview.getOperation()).toEqual(carto.operation.AVG);
expect(returnedObject).toBe(dataview);
});
it('sets operation in internal model if exists', function () {
var internalModelMock = createInternalModelMock();
dataview._internalModel = internalModelMock;
dataview.setOperation(carto.operation.AVG);
var operationArgs = internalModelMock.set.calls.mostRecent().args;
expect(operationArgs[0]).toEqual('operation');
expect(operationArgs[1]).toEqual(carto.operation.AVG);
});
it('should Trigger a operationChanged event when the operation is changed', function () {
var operationChangedSpy = jasmine.createSpy('operationaChangedSpy');
dataview.on('operationChanged', operationChangedSpy);
expect(operationChangedSpy).not.toHaveBeenCalled();
dataview.$setEngine(createEngineMock());
dataview.setOperation(carto.operation.MAX);
expect(operationChangedSpy).toHaveBeenCalledWith(carto.operation.MAX);
});
});
describe('.getData', function () {
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Formula(source, 'population', {
operation: carto.operation.SUM
});
});
it('returns null if there is no internalModel', function () {
var data = dataview.getData();
expect(data).toBeNull();
});
it('returns data from internalModel', function () {
var internalModelMock = createInternalModelMock();
dataview._internalModel = internalModelMock;
var data = dataview.getData();
expect(data).toEqual({
nulls: 42,
operation: carto.operation.SUM,
result: 1234
});
});
});
describe('.$setEngine', function () {
var engine;
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Formula(source, 'population', {
operation: carto.operation.MIN
});
engine = createEngineMock();
});
it('creates the internal model', function () {
dataview.disable(); // To test that it passes the ._enabled property to the internal model
dataview.$setEngine(engine);
var internalModel = dataview.$getInternalModel();
expect(internalModel.get('source')).toBe(dataview._source.$getInternalModel());
expect(internalModel.get('column')).toEqual(dataview._column);
expect(internalModel.get('operation')).toEqual(dataview._operation);
expect(internalModel.isEnabled()).toBe(false);
expect(internalModel._engine.name).toEqual('Engine mock');
});
it('calling twice to $setEngine does not create another internalModel', function () {
spyOn(dataview, '_createInternalModel').and.callThrough();
dataview.$setEngine(engine);
dataview.$setEngine(engine);
expect(dataview._createInternalModel.calls.count()).toBe(1);
});
describe('spatial filters', function () {
it('creates the internal model with BoundingBox filter if provided', function () {
var filter = new carto.filter.BoundingBox();
dataview.addFilter(filter);
dataview.$setEngine(engine);
var internalModel = dataview.$getInternalModel();
expect(internalModel._bboxFilter).toBeDefined();
expect(internalModel.syncsOnBoundingBoxChanges()).toBe(true);
});
it('allows removing a BoundingBox filter', function () {
var filter = new carto.filter.BoundingBox();
dataview.addFilter(filter);
dataview.$setEngine(engine);
expect(dataview.hasFilter(filter)).toBe(true);
dataview.removeFilter(filter);
expect(dataview.hasFilter(filter)).toBe(false);
var internalModel = dataview.$getInternalModel();
expect(internalModel._bboxFilter).toBeNull();
expect(internalModel.syncsOnBoundingBoxChanges()).toBe(false);
});
it('creates the internal model with Circle filter if provided', function () {
var filter = new carto.filter.Circle();
dataview.addFilter(filter);
dataview.$setEngine(engine);
var internalModel = dataview.$getInternalModel();
expect(internalModel._circleFilter).toBeDefined();
expect(internalModel.syncsOnCircleChanges()).toBe(true);
});
it('allows removing a Circle filter', function () {
var filter = new carto.filter.Circle();
dataview.addFilter(filter);
dataview.$setEngine(engine);
expect(dataview.hasFilter(filter)).toBe(true);
dataview.removeFilter(filter);
expect(dataview.hasFilter(filter)).toBe(false);
var internalModel = dataview.$getInternalModel();
expect(internalModel._circleFilter).toBeNull();
expect(internalModel.syncsOnCircleChanges()).toBe(false);
});
it('creates the internal model with Polygon filter if provided', function () {
var filter = new carto.filter.Polygon();
dataview.addFilter(filter);
dataview.$setEngine(engine);
var internalModel = dataview.$getInternalModel();
expect(internalModel._polygonFilter).toBeDefined();
expect(internalModel.syncsOnPolygonChanges()).toBe(true);
});
it('allows removing a Polygon filter', function () {
var filter = new carto.filter.Polygon();
dataview.addFilter(filter);
dataview.$setEngine(engine);
expect(dataview.hasFilter(filter)).toBe(true);
dataview.removeFilter(filter);
expect(dataview.hasFilter(filter)).toBe(false);
var internalModel = dataview.$getInternalModel();
expect(internalModel._polygonFilter).toBeNull();
expect(internalModel.syncsOnPolygonChanges()).toBe(false);
});
});
});
});

View File

@@ -0,0 +1,452 @@
var Backbone = require('backbone');
var _ = require('underscore');
var carto = require('../../../../../src/api/v4/index');
var createEngine = require('../../../fixtures/engine.fixture.js');
function createHistogramInternalModelMock (options) {
options = options || {};
_.extend({
data: null,
nulls: null
}, options);
var internalModelMock = {
set: function () {},
get: function () {},
getUnfilteredDataModel: function () {},
getUnfilteredData: function () {
return [
{
freq: 23
}, {
freq: 46
}, {
}
];
}
};
spyOn(internalModelMock, 'set');
spyOn(internalModelMock, 'get').and.callFake(function (key) {
if (key === 'data') {
return options.data;
}
if (key === 'nulls') {
return options.nulls;
}
if (key === 'totalAmount') {
return 7654;
}
});
spyOn(internalModelMock, 'getUnfilteredDataModel').and.returnValue({
get: function (key) {
if (key === 'nulls') {
return 12;
}
if (key === 'totalAmount') {
return 707;
}
}
});
_.extend(internalModelMock, Backbone.Events);
return internalModelMock;
}
function createSourceMock () {
return new carto.source.Dataset('ne_10m_populated_places_simple');
}
describe('api/v4/dataview/histogram', function () {
var source = createSourceMock();
describe('initialization', function () {
it('source must be provided', function () {
var test = function () {
new carto.dataview.Histogram(); // eslint-disable-line no-new
};
expect(test).toThrowError(Error, 'Source property is required.');
});
it('column must be provided', function () {
var test = function () {
new carto.dataview.Histogram(source); // eslint-disable-line no-new
};
expect(test).toThrowError(Error, 'Column property is required.');
});
it('options set to default if not provided', function () {
var column = 'population';
var dataview = new carto.dataview.Histogram(source, column);
expect(dataview._bins).toEqual(10);
});
it('options set to the provided value', function () {
var dataview = new carto.dataview.Histogram(source, 'population', {
bins: 808
});
expect(dataview._bins).toEqual(808);
});
it('throw error if bins is not a positive integer value', function () {
var test = function () {
new carto.dataview.Histogram(source, 'population', { // eslint-disable-line no-new
bins: 0
});
};
expect(test).toThrowError(Error, 'Bins must be a positive integer value.');
});
it('throw error if start is present but not end', function () {
var test = function () {
new carto.dataview.Histogram(source, 'population', { // eslint-disable-line no-new
start: 10
});
};
expect(test).toThrowError(Error, 'Both start and end values must be a number or null.');
});
it('throw error if end is present but not start', function () {
var test = function () {
new carto.dataview.Histogram(source, 'population', { // eslint-disable-line no-new
end: 10
});
};
expect(test).toThrowError(Error, 'Both start and end values must be a number or null.');
});
});
describe('.getData', function () {
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Histogram(source, 'population');
});
it('returns null if there is no internalModel', function () {
var data = dataview.getData();
expect(data).toBeNull();
});
it('returns parsed data from the internal model', function () {
dataview._internalModel = createHistogramInternalModelMock({
data: [
{
freq: 35
}, {
freq: 50
}, {
}
],
nulls: 42
});
var data = dataview.getData();
expect(data.bins.length).toBe(3);
expect(data.bins[0].freq).toBe(35);
expect(data.bins[1].freq).toBe(50);
expect(data.bins[2].freq).toBeUndefined();
expect(data.bins[0].normalized).toBe(0.7);
expect(data.bins[1].normalized).toBe(1);
expect(data.bins[2].normalized).toBe(0);
expect(data.nulls).toBe(42);
expect(data.totalAmount).toBe(7654);
});
it('returns nulls as 0 in case the internal model has no nulls', function () {
dataview._internalModel = createHistogramInternalModelMock({
data: [
{
freq: 35
}, {
freq: 50
}, {
}
],
nulls: undefined
});
var data = dataview.getData();
expect(data.bins.length).toBe(3);
expect(data.bins[0].freq).toBe(35);
expect(data.bins[1].freq).toBe(50);
expect(data.bins[2].freq).toBeUndefined();
expect(data.bins[0].normalized).toBe(0.7);
expect(data.bins[1].normalized).toBe(1);
expect(data.bins[2].normalized).toBe(0);
expect(data.nulls).toBe(0);
expect(data.totalAmount).toBe(7654);
});
it('returns null if internal model has no data', function () {
dataview._internalModel = createHistogramInternalModelMock();
var data = dataview.getData();
expect(data).toBe(null);
});
});
describe('.setBins', function () {
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Histogram(source, 'population');
});
it('should validate bins', function () {
var test = function () {
dataview.setBins(-1);
};
expect(test).toThrowError(Error, 'Bins must be a positive integer value.');
});
it('should throw error if called with a float number', function () {
var test = function () {
dataview.setBins(15.7);
};
expect(test).toThrowError(Error, 'Bins must be a positive integer value.');
});
it('should set bins to internal model as well', function () {
dataview._internalModel = createHistogramInternalModelMock();
dataview.setBins(16);
expect(dataview._internalModel.set).toHaveBeenCalledWith('bins', 16);
expect(dataview.getBins()).toBe(16); // We assert .getBins() as well
// Clean
dataview._internalModel = null;
});
it('should Trigger a binsChanged event when the bins are changed', function () {
var binsChangedSpy = jasmine.createSpy('binsChangedSpy');
dataview.on('binsChanged', binsChangedSpy);
expect(binsChangedSpy).not.toHaveBeenCalled();
dataview.$setEngine(createEngine());
dataview.setBins(11);
expect(binsChangedSpy).toHaveBeenCalledWith(11);
});
});
describe('.setStartEnd', function () {
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Histogram(source, 'population');
});
it('should throw an error if only start is present', function () {
var test = function () {
dataview.setStartEnd(20, null);
};
expect(test).toThrowError(Error, 'Both start and end values must be a number or null.');
});
it('should throw an error if only end is present', function () {
var test = function () {
dataview.setStartEnd(null, 30);
};
expect(test).toThrowError(Error, 'Both start and end values must be a number or null.');
});
it('should set start and end with a number', function () {
dataview._internalModel = createHistogramInternalModelMock();
dataview.setStartEnd(20, 30);
expect(dataview._internalModel.set).toHaveBeenCalledWith({ start: 20, end: 30 });
expect(dataview.getStart()).toBe(20); // We assert .getStart() as well
expect(dataview.getEnd()).toBe(30); // We assert .getEnd() as well
// Clean
dataview._internalModel = null;
});
it('should set start and end with null', function () {
dataview._internalModel = createHistogramInternalModelMock();
dataview.setStartEnd(null, null);
expect(dataview._internalModel.set).toHaveBeenCalledWith({ start: null, end: null });
expect(dataview.getStart()).toBe(undefined); // We assert .getStart() as well
expect(dataview.getEnd()).toBe(undefined); // We assert .getEnd() as well
// Clean
dataview._internalModel = null;
});
it('should trigger a startChanged event when the start is changed', function () {
var startChangedSpy = jasmine.createSpy('startChangedSpy');
dataview.on('startChanged', startChangedSpy);
expect(startChangedSpy).not.toHaveBeenCalled();
dataview.$setEngine(createEngine());
dataview.setStartEnd(20, 30);
expect(startChangedSpy).toHaveBeenCalledWith(20);
});
it('should trigger a endChanged event when the end is changed', function () {
var endChangedSpy = jasmine.createSpy('endChangedSpy');
dataview.on('endChanged', endChangedSpy);
expect(endChangedSpy).not.toHaveBeenCalled();
dataview.$setEngine(createEngine());
dataview.setStartEnd(20, 30);
expect(endChangedSpy).toHaveBeenCalledWith(30);
});
});
describe('.$setEngine', function () {
var engine;
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Histogram(source, 'population');
engine = createEngine();
});
it('creates the internal model', function () {
dataview.disable(); // To test that it passes the ._enabled property to the internal model
dataview.setBins(15);
dataview.$setEngine(engine);
var internalModel = dataview.$getInternalModel();
expect(internalModel.get('source')).toBe(dataview._source.$getInternalModel());
expect(internalModel.get('column')).toEqual(dataview._column);
expect(internalModel.get('bins')).toBe(15);
expect(internalModel.isEnabled()).toBe(false);
expect(internalModel._engine).toBe(engine);
});
it('calling twice to $setEngine does not create another internalModel', function () {
spyOn(dataview, '_createInternalModel').and.callThrough();
dataview.$setEngine(engine);
dataview.$setEngine(engine);
expect(dataview._createInternalModel.calls.count()).toBe(1);
});
describe('spatial filters', function () {
it('creates the internal model with BoundingBox filter if provided', function () {
var filter = new carto.filter.BoundingBox();
dataview.addFilter(filter);
dataview.$setEngine(engine);
var internalModel = dataview.$getInternalModel();
expect(internalModel._bboxFilter).toBeDefined();
expect(internalModel.syncsOnBoundingBoxChanges()).toBe(true);
});
it('allows removing a BoundingBox filter', function () {
var filter = new carto.filter.BoundingBox();
dataview.addFilter(filter);
dataview.$setEngine(engine);
expect(dataview.hasFilter(filter)).toBe(true);
dataview.removeFilter(filter);
expect(dataview.hasFilter(filter)).toBe(false);
var internalModel = dataview.$getInternalModel();
expect(internalModel._bboxFilter).toBeNull();
expect(internalModel.syncsOnBoundingBoxChanges()).toBe(false);
});
it('creates the internal model with Circle filter if provided', function () {
var filter = new carto.filter.Circle();
dataview.addFilter(filter);
dataview.$setEngine(engine);
var internalModel = dataview.$getInternalModel();
expect(internalModel._circleFilter).toBeDefined();
expect(internalModel.syncsOnCircleChanges()).toBe(true);
});
it('allows removing a Circle filter', function () {
var filter = new carto.filter.Circle();
dataview.addFilter(filter);
dataview.$setEngine(engine);
expect(dataview.hasFilter(filter)).toBe(true);
dataview.removeFilter(filter);
expect(dataview.hasFilter(filter)).toBe(false);
var internalModel = dataview.$getInternalModel();
expect(internalModel._circleFilter).toBeNull();
expect(internalModel.syncsOnCircleChanges()).toBe(false);
});
it('creates the internal model with Polygon filter if provided', function () {
var filter = new carto.filter.Polygon();
dataview.addFilter(filter);
dataview.$setEngine(engine);
var internalModel = dataview.$getInternalModel();
expect(internalModel._polygonFilter).toBeDefined();
expect(internalModel.syncsOnPolygonChanges()).toBe(true);
});
it('allows removing a Polygon filter', function () {
var filter = new carto.filter.Polygon();
dataview.addFilter(filter);
dataview.$setEngine(engine);
expect(dataview.hasFilter(filter)).toBe(true);
dataview.removeFilter(filter);
expect(dataview.hasFilter(filter)).toBe(false);
var internalModel = dataview.$getInternalModel();
expect(internalModel._polygonFilter).toBeNull();
expect(internalModel.syncsOnPolygonChanges()).toBe(false);
});
});
});
describe('.getDistributionType', function () {
var dataview;
beforeEach(function () {
dataview = new carto.dataview.Histogram(source, 'population');
});
it('should return null if there is no internal model', function () {
var distribution = dataview.getDistributionType();
expect(distribution).toBe(null);
});
it('should call to the proper method in internal model', function () {
var internalModel = {
getData: function () {},
getDistributionType: function () {}
};
spyOn(internalModel, 'getData').and.returnValue('token');
spyOn(internalModel, 'getDistributionType').and.returnValue('a');
dataview._internalModel = internalModel;
var distribution = dataview.getDistributionType();
expect(internalModel.getDistributionType).toHaveBeenCalledWith('token');
expect(distribution).toEqual('a');
});
});
});

View File

@@ -0,0 +1,427 @@
var Backbone = require('backbone');
var _ = require('underscore');
var carto = require('../../../../../src/api/v4/index');
var createEngine = require('../../../fixtures/engine.fixture.js');
function createHistogramInternalModelMock (options) {
options = options || {};
_.extend({
data: null,
nulls: null
}, options);
var internalModelMock = {
set: function () {},
get: function () {},
getUnfilteredDataModel: function () {},
getUnfilteredData: function () {
return [
{
freq: 23
}, {
freq: 46
}, {
}
];
},
getCurrentOffset: function () {
return 7200;
}
};
spyOn(internalModelMock, 'set');
spyOn(internalModelMock, 'get').and.callFake(function (key) {
if (key === 'data') {
return options.data;
}
if (key === 'nulls') {
return options.nulls;
}
if (key === 'totalAmount') {
return 7654;
}
});
spyOn(internalModelMock, 'getUnfilteredDataModel').and.returnValue({
get: function (key) {
if (key === 'nulls') {
return 12;
}
if (key === 'totalAmount') {
return 707;
}
}
});
_.extend(internalModelMock, Backbone.Events);
return internalModelMock;
}
function createSourceMock () {
return new carto.source.Dataset('ne_10m_populated_places_simple');
}
describe('api/v4/dataview/time-series', function () {
var source = createSourceMock();
describe('initialization', function () {
it('source must be provided', function () {
var test = function () {
new carto.dataview.TimeSeries(); // eslint-disable-line no-new
};
expect(test).toThrowError(Error, 'Source property is required.');
});
it('column must be provided', function () {
var test = function () {
new carto.dataview.TimeSeries(source); // eslint-disable-line no-new
};
expect(test).toThrowError(Error, 'Column property is required.');
});
it('options set to default if not provided', function () {
var column = 'population';
var dataview = new carto.dataview.TimeSeries(source, column);
expect(dataview._aggregation).toEqual(carto.dataview.timeAggregation.AUTO);
expect(dataview._offset).toBe(0);
expect(dataview._localTimezone).toBe(false);
});
it('options set to the provided value', function () {
var dataview = new carto.dataview.TimeSeries(source, 'population', {
aggregation: carto.dataview.timeAggregation.QUARTER,
offset: -7,
useLocalTimezone: true
});
expect(dataview._aggregation).toEqual(carto.dataview.timeAggregation.QUARTER);
expect(dataview._offset).toBe(-7 * 3600); // Internaly stored in seconds
expect(dataview._localTimezone).toBe(true);
});
it('throw error if aggregation is not a proper value', function () {
var test = function () {
new carto.dataview.TimeSeries(source, 'population', { // eslint-disable-line no-new
aggregation: 'terasecond'
});
};
expect(test).toThrowError(Error, 'Time aggregation must be a valid value. Use carto.dataview.timeAggregation.');
});
it('throw error if offset is not a valid hour', function () {
var test = function () {
new carto.dataview.TimeSeries(source, 'population', { // eslint-disable-line no-new
offset: 34
});
};
expect(test).toThrowError(Error, 'Offset must an integer value between -12 and 14.');
test = function () {
new carto.dataview.TimeSeries(source, 'population', { // eslint-disable-line no-new
offset: 10.45
});
};
expect(test).toThrowError(Error, 'Offset must an integer value between -12 and 14.');
});
it('throw error if localTimezone is not a binary value', function () {
var test = function () {
new carto.dataview.TimeSeries(source, 'population', { // eslint-disable-line no-new
useLocalTimezone: 'Los Angeles'
});
};
expect(test).toThrowError(Error, 'useLocalTimezone must be a boolean value.');
});
});
describe('.getData', function () {
var dataview;
beforeEach(function () {
dataview = new carto.dataview.TimeSeries(source, 'population');
});
it('returns null if there is no internalModel', function () {
var data = dataview.getData();
expect(data).toBeNull();
});
it('returns parsed data from the internal model', function () {
dataview._internalModel = createHistogramInternalModelMock({
data: [
{
freq: 35
}, {
freq: 50
}, {
}
],
nulls: 42
});
var data = dataview.getData();
expect(data.bins.length).toBe(3);
expect(data.bins[0].freq).toBe(35);
expect(data.bins[1].freq).toBe(50);
expect(data.bins[2].freq).toBeUndefined();
expect(data.bins[0].normalized).toBe(0.7);
expect(data.bins[1].normalized).toBe(1);
expect(data.bins[2].normalized).toBe(0);
expect(data.nulls).toBe(42);
expect(data.totalAmount).toBe(7654);
expect(data.offset).toBe(2);
});
it('returns nulls as 0 in case the internal model has no nulls', function () {
dataview._internalModel = createHistogramInternalModelMock({
data: [
{
freq: 35
}, {
freq: 50
}, {
}
],
nulls: undefined
});
var data = dataview.getData();
expect(data.bins.length).toBe(3);
expect(data.bins[0].freq).toBe(35);
expect(data.bins[1].freq).toBe(50);
expect(data.bins[2].freq).toBeUndefined();
expect(data.bins[0].normalized).toBe(0.7);
expect(data.bins[1].normalized).toBe(1);
expect(data.bins[2].normalized).toBe(0);
expect(data.nulls).toBe(0);
expect(data.totalAmount).toBe(7654);
});
it('returns null if internal model has no data', function () {
dataview._internalModel = createHistogramInternalModelMock();
var data = dataview.getData();
expect(data).toBe(null);
});
});
describe('.setAggregation', function () {
var dataview;
beforeEach(function () {
dataview = new carto.dataview.TimeSeries(source, 'population');
});
it('should validate aggregation', function () {
var test = function () {
dataview.setAggregation('terasecond');
};
expect(test).toThrowError(Error, 'Time aggregation must be a valid value. Use carto.dataview.timeAggregation.');
});
it('should set aggregation to internal model as well', function () {
dataview._internalModel = createHistogramInternalModelMock();
dataview.setAggregation(carto.dataview.timeAggregation.HOUR);
expect(dataview._internalModel.set).toHaveBeenCalledWith('aggregation', carto.dataview.timeAggregation.HOUR);
expect(dataview.getAggregation()).toBe('hour');
// Clean
dataview._internalModel = null;
});
it('should Trigger a aggregationChanged event when the aggregation are changed', function () {
var aggregationChangedSpy = jasmine.createSpy('aggregationChangedSpy');
dataview.on('aggregationChanged', aggregationChangedSpy);
expect(aggregationChangedSpy).not.toHaveBeenCalled();
dataview.$setEngine(createEngine());
dataview.setAggregation(carto.dataview.timeAggregation.HOUR);
expect(aggregationChangedSpy).toHaveBeenCalledWith(carto.dataview.timeAggregation.HOUR);
});
});
describe('.setOffset', function () {
var dataview;
beforeEach(function () {
dataview = new carto.dataview.TimeSeries(source, 'population');
});
it('should validate offset', function () {
var test = function () {
dataview.setOffset(32);
};
expect(test).toThrowError(Error, 'Offset must an integer value between -12 and 14.');
test = function () {
dataview.setOffset(10.7);
};
expect(test).toThrowError(Error, 'Offset must an integer value between -12 and 14.');
});
it('should set offset to internal model as well translated to seconds', function () {
dataview._internalModel = createHistogramInternalModelMock();
dataview.setOffset(-5);
expect(dataview._internalModel.set).toHaveBeenCalledWith('offset', -5 * 3600);
expect(dataview.getOffset()).toBe(-5);
// Clean
dataview._internalModel = null;
});
});
describe('.useLocalTimezone', function () {
var dataview;
beforeEach(function () {
dataview = new carto.dataview.TimeSeries(source, 'population');
});
it('should validate localTimezone', function () {
var test = function () {
dataview.useLocalTimezone('Compton');
};
expect(test).toThrowError(Error, 'useLocalTimezone must be a boolean value.');
});
it('should set localTimezone to internal model as well', function () {
dataview._internalModel = createHistogramInternalModelMock();
dataview.useLocalTimezone(true);
expect(dataview._internalModel.set).toHaveBeenCalledWith('localTimezone', true);
expect(dataview.isUsingLocalTimezone()).toBe(true);
// Clean
dataview._internalModel = null;
});
});
describe('.$setEngine', function () {
var engine;
var dataview;
beforeEach(function () {
dataview = new carto.dataview.TimeSeries(source, 'population');
engine = createEngine();
});
it('creates the internal model', function () {
dataview.disable(); // To test that it passes the ._enabled property to the internal model
dataview.setAggregation(carto.dataview.timeAggregation.WEEK);
dataview.setOffset(6);
dataview.useLocalTimezone(true);
dataview.$setEngine(engine);
var internalModel = dataview.$getInternalModel();
expect(internalModel.get('source')).toBe(dataview._source.$getInternalModel());
expect(internalModel.get('column')).toEqual(dataview._column);
expect(internalModel.get('aggregation')).toBe('week');
expect(internalModel.get('localTimezone')).toBe(true);
expect(internalModel.get('offset')).toBe(6 * 3600);
expect(internalModel.isEnabled()).toBe(false);
expect(internalModel._engine).toBe(engine);
});
it('calling twice to $setEngine does not create another internalModel', function () {
spyOn(dataview, '_createInternalModel').and.callThrough();
dataview.$setEngine(engine);
dataview.$setEngine(engine);
expect(dataview._createInternalModel.calls.count()).toBe(1);
});
describe('spatial filters', function () {
it('creates the internal model with BoundingBox filter if provided', function () {
var filter = new carto.filter.BoundingBox();
dataview.addFilter(filter);
dataview.$setEngine(engine);
var internalModel = dataview.$getInternalModel();
expect(internalModel._bboxFilter).toBeDefined();
expect(internalModel.syncsOnBoundingBoxChanges()).toBe(true);
});
it('allows removing a BoundingBox filter', function () {
var filter = new carto.filter.BoundingBox();
dataview.addFilter(filter);
dataview.$setEngine(engine);
expect(dataview.hasFilter(filter)).toBe(true);
dataview.removeFilter(filter);
expect(dataview.hasFilter(filter)).toBe(false);
var internalModel = dataview.$getInternalModel();
expect(internalModel._bboxFilter).toBeNull();
expect(internalModel.syncsOnBoundingBoxChanges()).toBe(false);
});
it('creates the internal model with Circle filter if provided', function () {
var filter = new carto.filter.Circle();
dataview.addFilter(filter);
dataview.$setEngine(engine);
var internalModel = dataview.$getInternalModel();
expect(internalModel._circleFilter).toBeDefined();
expect(internalModel.syncsOnCircleChanges()).toBe(true);
});
it('allows removing a Circle filter', function () {
var filter = new carto.filter.Circle();
dataview.addFilter(filter);
dataview.$setEngine(engine);
expect(dataview.hasFilter(filter)).toBe(true);
dataview.removeFilter(filter);
expect(dataview.hasFilter(filter)).toBe(false);
var internalModel = dataview.$getInternalModel();
expect(internalModel._circleFilter).toBeNull();
expect(internalModel.syncsOnCircleChanges()).toBe(false);
});
it('creates the internal model with Polygon filter if provided', function () {
var filter = new carto.filter.Polygon();
dataview.addFilter(filter);
dataview.$setEngine(engine);
var internalModel = dataview.$getInternalModel();
expect(internalModel._polygonFilter).toBeDefined();
expect(internalModel.syncsOnPolygonChanges()).toBe(true);
});
it('allows removing a Polygon filter', function () {
var filter = new carto.filter.Polygon();
dataview.addFilter(filter);
dataview.$setEngine(engine);
expect(dataview.hasFilter(filter)).toBe(true);
dataview.removeFilter(filter);
expect(dataview.hasFilter(filter)).toBe(false);
var internalModel = dataview.$getInternalModel();
expect(internalModel._polygonFilter).toBeNull();
expect(internalModel.syncsOnPolygonChanges()).toBe(false);
});
});
});
});

View File

@@ -0,0 +1,49 @@
var getExtraFields = require('../../../../../src/api/v4/error-handling/carto-error-extender').getExtraFields;
describe('api/v4/error-handling/carto-error-extender', function () {
it('extending an error with no entry in the error list should return default values', function () {
var extendedError = getExtraFields({});
expect(extendedError.friendlyMessage).toEqual('');
expect(extendedError.errorCode).toEqual('unknown-error');
});
it('extending an error with an entry in the error list that needs a regex replacement should return proper code and friendly message', function () {
var error = {
origin: 'windshaft',
type: 'analysis',
message: 'relation "invalid_value" does not exist'
};
var extendedError = getExtraFields(error);
expect(extendedError.friendlyMessage).toEqual('Invalid dataset name used. Dataset "invalid_value" does not exist.');
expect(extendedError.errorCode).toEqual('windshaft:analysis:invalid-dataset');
});
it('extending an error with an entry that needs two regex replacements ($0 and $1) should return proper code and friendly message', function () {
var error = {
origin: 'validation',
type: 'layer',
message: 'wrongInteractivityColumns[column1, column2]#featureClick'
};
var extendedError = getExtraFields(error);
expect(extendedError.friendlyMessage).toEqual('Columns [column1, column2] set on `featureClick` do not match the columns set in aggregation options.');
expect(extendedError.errorCode).toEqual('validation:layer:wrong-interactivity-columns');
});
it('extending an error with an entry in the error list that does not have friendly message should return proper code and original message', function () {
var error = {
origin: 'windshaft',
type: 'analysis',
message: 'syntax error: thruster is not a valid SQL word'
};
var extendedError = getExtraFields(error);
expect(extendedError.friendlyMessage).toEqual('syntax error: thruster is not a valid SQL word');
expect(extendedError.errorCode).toEqual('windshaft:analysis:sql-syntax-error');
});
});

View File

@@ -0,0 +1,107 @@
var CartoError = require('../../../../../src/api/v4/error-handling/carto-error');
describe('v4/error-handling/carto-error', function () {
it('should return default values if the error is not qualified', function () {
var cartoError = new CartoError({
someProperty: 'some value'
});
expect(cartoError instanceof Error).toBe(true);
expect(cartoError.name).toEqual('CartoError');
expect(cartoError.message).toEqual('unexpected error');
expect(cartoError.origin).toEqual('generic');
expect(cartoError.type).toEqual('');
expect(cartoError.errorCode).toEqual('generic:unknown-error');
expect(cartoError.originalError).toEqual(jasmine.objectContaining({
someProperty: 'some value'
}));
});
describe('windshaft error', function () {
it('should return the original values and proper friendly message if it is a windshaft error', function () {
var cartoError = new CartoError({
origin: 'windshaft',
type: 'layer',
message: 'column "jonica" does not exist'
});
expect(cartoError instanceof Error).toBe(true);
expect(cartoError.name).toEqual('CartoError');
expect(cartoError.message).toEqual('Invalid column name. Column "jonica" does not exist.');
expect(cartoError.origin).toEqual('windshaft');
expect(cartoError.type).toEqual('layer');
expect(cartoError.errorCode).toEqual('windshaft:layer:column-does-not-exist');
expect(cartoError.originalError).toEqual(jasmine.objectContaining({
origin: 'windshaft',
type: 'layer',
message: 'column "jonica" does not exist'
}));
});
it('should return the source id if it is a windshaft analysis error and analysis is provided', function () {
var cartoError = new CartoError({
origin: 'windshaft',
type: 'analysis',
message: 'column "jonica" does not exist'
}, {
analysis: {
aKey: 'a value',
getId: function () { return 'S1'; }
}
});
expect(cartoError instanceof Error).toBe(true);
expect(cartoError.source).toBeDefined();
expect(cartoError.source.aKey).toEqual('a value');
expect(cartoError.sourceId).toEqual('S1');
});
it('should return the source id if the windshaft analysis error has that value', function () {
var cartoError = new CartoError({
origin: 'windshaft',
type: 'analysis',
message: 'column "jonica" does not exist',
analysisId: 'A1'
});
expect(cartoError instanceof Error).toBe(true);
expect(cartoError.sourceId).toEqual('A1');
});
});
it('should parse ajax error if original error is an ajax response', function () {
var cartoError = new CartoError({
responseText: '{ "errors": ["an error"] }',
statusText: '404'
});
expect(cartoError instanceof Error).toBe(true);
expect(cartoError.name).toEqual('CartoError');
expect(cartoError.message).toEqual('an error');
expect(cartoError.origin).toEqual('ajax');
expect(cartoError.type).toEqual('404');
expect(cartoError.errorCode).toEqual('ajax:404:unknown-error');
expect(cartoError.originalError).toEqual(jasmine.objectContaining({
responseText: '{ "errors": ["an error"] }',
statusText: '404'
}));
});
/**
* There's a bug in PhantomJS that don't fill the stack property
* of an error. We need to assert that the `stack` property gets
* defined so we restrict the spec to run in the browser.
*/
it('should create an error with a stack trace', function () {
var cartoError = new CartoError({
someProperty: 'some value'
});
var userAgent = navigator.userAgent;
if (userAgent.toLowerCase().indexOf('phantomjs') < 0) {
expect(cartoError.stack).toBeDefined();
} else {
expect(true).toBe(true);
}
});
});

View File

@@ -0,0 +1,12 @@
var CartoValidationError = require('../../../../../src/api/v4/error-handling/carto-validation-error');
describe('v4/error-handling/carto-validation-error', function () {
it('should return a CartoError with validation as type', function () {
var validationError = new CartoValidationError('layer', 'a message');
expect(validationError.name).toEqual('CartoError');
expect(validationError.origin).toEqual('validation');
expect(validationError.type).toEqual('layer');
expect(validationError.message).toEqual('a message');
});
});

View File

@@ -0,0 +1,250 @@
const SQLBase = require('../../../../../src/api/v4/filter/base-sql');
const PARAMETER_SPECIFICATION = {
in: { parameters: [{ name: 'in', allowedTypes: ['Array', 'String'] }] },
notIn: { parameters: [{ name: 'notIn', allowedTypes: ['Array', 'String'] }] },
like: { parameters: [{ name: 'like', allowedTypes: ['Array', 'String'] }] }
};
const SQL_TEMPLATES = {
'in': '<%= column %> IN (<%= value %>)',
'like': '<%= column %> LIKE <%= value %>'
};
const column = 'fake_column';
describe('api/v4/filter/base-sql', function () {
describe('constructor', function () {
it('should throw a descriptive error when column is undefined, not a string, or empty', function () {
expect(function () {
new SQLBase(undefined); // eslint-disable-line
}).toThrowError('Column property is required.');
expect(function () {
new SQLBase(1); // eslint-disable-line
}).toThrowError('Column property must be a string.');
expect(function () {
new SQLBase(''); // eslint-disable-line
}).toThrowError('Column property must be not empty.');
});
it('should throw a descriptive error when there is an invalid option', function () {
expect(function () {
new SQLBase('fake_column', { unknown_option: false }); // eslint-disable-line
}).toThrowError("'unknown_option' is not a valid option for this filter.");
});
it('should set column and options as class properties', function () {
const options = { includeNull: true };
const sqlFilter = new SQLBase(column, options);
expect(sqlFilter._column).toBe(column);
expect(sqlFilter._options).toBe(options);
});
});
describe('.set', function () {
it('should throw a descriptive error when an unknown filter has been passed', function () {
const sqlFilter = new SQLBase('fake_column');
expect(function () {
sqlFilter.set('unknown_filter', 'test_filter');
}).toThrowError("'unknown_filter' is not a valid filter. Please check documentation.");
});
it('should set the new filter to the filters object', function () {
const sqlFilter = new SQLBase(column);
sqlFilter.ALLOWED_FILTERS = ['in'];
sqlFilter.PARAMETER_SPECIFICATION = { in: PARAMETER_SPECIFICATION.in };
sqlFilter.set('in', ['test_filter']);
expect(sqlFilter._filters).toEqual({ in: ['test_filter'] });
});
it("should trigger a 'change:filters' event", function () {
const spy = jasmine.createSpy();
const sqlFilter = new SQLBase('fake_column');
sqlFilter.ALLOWED_FILTERS = ['in'];
sqlFilter.PARAMETER_SPECIFICATION = { in: PARAMETER_SPECIFICATION.in };
sqlFilter.on('change:filters', spy);
sqlFilter.set('in', ['test_filter']);
expect(spy).toHaveBeenCalled();
});
});
describe('.setFilters', function () {
it('should throw a descriptive error when an unknown filter has been passed', function () {
const sqlFilter = new SQLBase('fake_column');
expect(function () {
sqlFilter.setFilters({ unknown_filter: 'test_filter' });
}).toThrowError("'unknown_filter' is not a valid filter. Please check documentation.");
});
it('should set the new filters and override previous ones', function () {
const newFilters = { notIn: 'test_filter2' };
const sqlFilter = new SQLBase(column);
sqlFilter.ALLOWED_FILTERS = ['in', 'notIn'];
sqlFilter.PARAMETER_SPECIFICATION = {
in: PARAMETER_SPECIFICATION.in,
notIn: PARAMETER_SPECIFICATION.notIn
};
sqlFilter.set('in', ['test_filter']);
sqlFilter.setFilters(newFilters);
expect(sqlFilter._filters).toEqual(newFilters);
});
it("should trigger a 'change:filters' event", function () {
const newFilters = { notIn: 'test_filter2' };
const spy = jasmine.createSpy();
const sqlFilter = new SQLBase(column);
sqlFilter.ALLOWED_FILTERS = ['in', 'notIn'];
sqlFilter.PARAMETER_SPECIFICATION = {
in: PARAMETER_SPECIFICATION.in,
notIn: PARAMETER_SPECIFICATION.notIn
};
sqlFilter.set('in', ['test_filter']);
sqlFilter.on('change:filters', spy);
sqlFilter.setFilters(newFilters);
expect(spy).toHaveBeenCalledTimes(1);
});
});
describe('.resetFilters', function () {
it('should reset applied filters', function () {
const sqlFilter = new SQLBase(column);
sqlFilter.ALLOWED_FILTERS = ['in'];
sqlFilter.PARAMETER_SPECIFICATION = { in: PARAMETER_SPECIFICATION.in };
sqlFilter.set('in', ['test_filter']);
sqlFilter.resetFilters();
expect(sqlFilter._filters).toEqual({});
});
});
describe('.$getSQL', function () {
it('should return SQL string containing all the filters joined by AND clause', function () {
const sqlFilter = new SQLBase(column);
sqlFilter.ALLOWED_FILTERS = ['in', 'like'];
sqlFilter.PARAMETER_SPECIFICATION = {
in: PARAMETER_SPECIFICATION.in,
like: PARAMETER_SPECIFICATION.like
};
sqlFilter.SQL_TEMPLATES = {
in: SQL_TEMPLATES.in,
like: SQL_TEMPLATES.like
};
sqlFilter.setFilters({ in: ['category 1', 'category 2'], like: '%category%' });
expect(sqlFilter.$getSQL()).toBe("(fake_column IN ('category 1','category 2') AND fake_column LIKE '%category%')");
});
it('should call _includeNullInQuery if includeNull option is set', function () {
const sqlFilter = new SQLBase(column, { includeNull: true });
sqlFilter.ALLOWED_FILTERS = ['in'];
sqlFilter.PARAMETER_SPECIFICATION = { in: PARAMETER_SPECIFICATION.in };
sqlFilter.SQL_TEMPLATES = { in: SQL_TEMPLATES.in };
sqlFilter.setFilters({ in: ['category 1', 'category 2'] });
spyOn(sqlFilter, '_includeNullInQuery');
sqlFilter.$getSQL();
expect(sqlFilter._includeNullInQuery).toHaveBeenCalled();
});
});
describe('.checkFilters', function () {
let sqlFilter;
beforeEach(function () {
sqlFilter = new SQLBase(column);
});
it('should throw an error when an invalid filter is passed', function () {
expect(function () {
sqlFilter._checkFilters({ unknown_filter: 'filter' });
}).toThrowError("'unknown_filter' is not a valid filter. Please check documentation.");
});
it("should throw an error when there's a type mismatching in filter parameters", function () {
expect(function () {
sqlFilter.ALLOWED_FILTERS = ['in'];
sqlFilter.PARAMETER_SPECIFICATION = { in: PARAMETER_SPECIFICATION.in };
sqlFilter._checkFilters({ in: 1 });
}).toThrowError("Invalid parameter type for 'in'. Please check filters documentation.");
});
});
describe('._convertValueToSQLString', function () {
it('should format date to ISO8601 string', function () {
const sqlFilter = new SQLBase(column);
const fakeDate = new Date('Thu Jun 28 2018 15:04:31 GMT+0200 (Central European Summer Time)');
expect(sqlFilter._convertValueToSQLString(fakeDate)).toBe('\'2018-06-28T13:04:31.000Z\'');
});
it('should convert array to a comma-separated string wrapped by single comma', function () {
const sqlFilter = new SQLBase(column);
const fakeArray = ['Element 1', 'Element 2'];
expect(sqlFilter._convertValueToSQLString(fakeArray)).toBe("'Element 1','Element 2'");
});
it('should return number without modifying', function () {
const sqlFilter = new SQLBase(column);
expect(sqlFilter._convertValueToSQLString(1)).toBe(1);
});
it('should return object with string values without modifying', function () {
const sqlFilter = new SQLBase(column);
const fakeObject = { fakeProperty: 'fakeValue' };
expect(sqlFilter._convertValueToSQLString(fakeObject)).toEqual(fakeObject);
});
it('should return object with date values parsed properly', function () {
const sqlFilter = new SQLBase(column);
const fakeObject = { fakeDate: new Date('2014-01-01T00:00:00.00Z') };
expect(sqlFilter._convertValueToSQLString(fakeObject)).toEqual({ fakeDate: '\'2014-01-01T00:00:00.000Z\'' });
});
it('should wrap strings in single-quotes', function () {
const sqlFilter = new SQLBase(column);
const fakeString = 'fake_string';
expect(sqlFilter._convertValueToSQLString(fakeString)).toBe(`'${fakeString}'`);
});
});
describe('._interpolateFilterIntoTemplate', function () {
it('should inject filter values into SQL template', function () {
const sqlFilter = new SQLBase(column);
sqlFilter.SQL_TEMPLATES = {
gte: '<%= column %> > <%= value %>'
};
expect(sqlFilter._interpolateFilter('gte', 10)).toBe('fake_column > 10');
});
});
});

View File

@@ -0,0 +1,11 @@
var carto = require('../../../../../src/api/v4/index');
describe('api/v4/filter/bounding-box-gmaps', function () {
describe('constructor', function () {
it('should throw a descriptive error when initialized with invalid parameters', function () {
expect(function () {
new carto.filter.BoundingBoxGoogleMaps(undefined); // eslint-disable-line
}).toThrowError('Bounding box requires a Google Maps map but got: undefined');
});
});
});

View File

@@ -0,0 +1,11 @@
var carto = require('../../../../../src/api/v4/index');
describe('api/v4/filter/bounding-box-leaflet', function () {
describe('constructor', function () {
it('should throw a descriptive error when initialized with invalid parameters', function () {
expect(function () {
new carto.filter.BoundingBoxLeaflet(undefined); // eslint-disable-line
}).toThrowError('Bounding box requires a Leaflet map but got: undefined');
});
});
});

View File

@@ -0,0 +1,50 @@
var carto = require('../../../../../src/api/v4/index');
describe('api/v4/filter/bounding-box', function () {
describe('initialization', function () {
it('should create the internalModel', function () {
var bboxFilter = new carto.filter.BoundingBox();
expect(bboxFilter.$getInternalModel()).toBeDefined();
});
});
describe('.setBounds', function () {
var bboxFilter;
beforeEach(function () {
bboxFilter = new carto.filter.BoundingBox();
});
it('checks if bounds are valid', function () {
var test = function () {
bboxFilter.setBounds({ west: 0 });
};
expect(test).toThrowError(Error, 'Bounds object is not valid. Use a carto.filter.Bounds object');
});
it('if bounds are valid, it assigns it to property, triggers the boundsChanged event and returns this', function () {
spyOn(bboxFilter, 'trigger');
var bounds = { west: 1, south: 2, east: 3, north: 4 };
var returnedObject = bboxFilter.setBounds(bounds);
expect(bboxFilter.getBounds()).toEqual(bounds);
expect(bboxFilter.trigger).toHaveBeenCalledWith('boundsChanged', bounds);
expect(returnedObject).toBe(bboxFilter);
});
});
describe('.resetBounds', function () {
it('sets the bounds to 0,0,0,0', function () {
var bboxFilter = new carto.filter.BoundingBox();
bboxFilter.setBounds({ west: 1, south: 2, east: 3, north: 4 });
expect(bboxFilter.getBounds()).toEqual({ west: 1, south: 2, east: 3, north: 4 });
bboxFilter.resetBounds();
expect(bboxFilter.getBounds()).toEqual({ west: 0, south: 0, east: 0, north: 0 });
});
});
});

View File

@@ -0,0 +1,63 @@
const carto = require('../../../../../src/api/v4/index');
describe('api/v4/filter/category', function () {
describe('constructor', function () {
it('should throw a descriptive error when an unknown filter has been passed', function () {
expect(function () {
new carto.filter.Category('fake_column', { unknown_filter: '' }); // eslint-disable-line
}).toThrowError("'unknown_filter' is not a valid filter. Please check documentation.");
});
});
describe('SQL Templates', function () {
it('IN', function () {
const categoryFilter = new carto.filter.Category('fake_column', { in: ['Category 1'] });
expect(categoryFilter.$getSQL()).toBe("fake_column IN ('Category 1')");
});
it('IN with subquery', function () {
const categoryFilter = new carto.filter.Category('fake_column', { in: { query: 'SELECT name FROM neighbourhoods' } });
expect(categoryFilter.$getSQL()).toBe('fake_column IN (SELECT name FROM neighbourhoods)');
});
it('NOT IN', function () {
const categoryFilter = new carto.filter.Category('fake_column', { notIn: ['Category 1'] });
expect(categoryFilter.$getSQL()).toBe("fake_column NOT IN ('Category 1')");
});
it('NOT IN with subquery', function () {
const categoryFilter = new carto.filter.Category('fake_column', { notIn: { query: 'SELECT name FROM neighbourhoods' } });
expect(categoryFilter.$getSQL()).toBe('fake_column NOT IN (SELECT name FROM neighbourhoods)');
});
it('EQ', function () {
const categoryFilter = new carto.filter.Category('fake_column', { eq: 'Category 1' });
expect(categoryFilter.$getSQL()).toBe("fake_column = 'Category 1'");
});
it('EQ with subquery', function () {
const categoryFilter = new carto.filter.Category('fake_column', { eq: { query: 'SELECT avg(price) FROM neighbourhoods' } });
expect(categoryFilter.$getSQL()).toBe('fake_column = (SELECT avg(price) FROM neighbourhoods)');
});
it('NOT EQ', function () {
const categoryFilter = new carto.filter.Category('fake_column', { notEq: 'Category 1' });
expect(categoryFilter.$getSQL()).toBe("fake_column != 'Category 1'");
});
it('NOT EQ with subquery', function () {
const categoryFilter = new carto.filter.Category('fake_column', { notEq: { query: 'SELECT avg(price) FROM neighbourhoods' } });
expect(categoryFilter.$getSQL()).toBe('fake_column != (SELECT avg(price) FROM neighbourhoods)');
});
it('LIKE', function () {
const categoryFilter = new carto.filter.Category('fake_column', { like: '%Category%' });
expect(categoryFilter.$getSQL()).toBe("fake_column LIKE '%Category%'");
});
it('SIMILAR TO', function () {
const categoryFilter = new carto.filter.Category('fake_column', { similarTo: '%Category%' });
expect(categoryFilter.$getSQL()).toBe("fake_column SIMILAR TO '%Category%'");
});
});
});

View File

@@ -0,0 +1,56 @@
var carto = require('../../../../../src/api/v4/index');
describe('api/v4/filter/circle', function () {
describe('initialization', function () {
it('should create the internalModel', function () {
var circleFilter = new carto.filter.Circle();
expect(circleFilter.$getInternalModel()).toBeDefined();
});
});
describe('.setCircle', function () {
var circleFilter;
beforeEach(function () {
circleFilter = new carto.filter.Circle();
});
it('checks if circle is valid', function () {
var test = function () {
circleFilter.setCircle({ lng: 0 });
};
var test2 = function () {
circleFilter.setCircle({ lng: 'a' });
};
const expected = 'Circle object is not valid. Use a carto.filter.CircleData object';
expect(test).toThrowError(Error, expected);
expect(test2).toThrowError(Error, expected);
});
it('if circle is valid, it assigns it to property, triggers the circleChanged event and returns this', function () {
spyOn(circleFilter, 'trigger');
var circle = { lat: 1, lng: 2, radius: 3 };
var returnedObject = circleFilter.setCircle(circle);
expect(circleFilter.getCircle()).toEqual(circle);
expect(circleFilter.trigger).toHaveBeenCalledWith('circleChanged', circle);
expect(returnedObject).toBe(circleFilter);
});
});
describe('.resetCircle', function () {
it('sets the circle to 0,0,0', function () {
var circleFilter = new carto.filter.Circle();
circleFilter.setCircle({ lat: 1, lng: 2, radius: 3 });
expect(circleFilter.getCircle()).toEqual({ lat: 1, lng: 2, radius: 3 });
circleFilter.resetCircle();
expect(circleFilter.getCircle()).toEqual({ lat: 0, lng: 0, radius: 0 });
});
});
});

View File

@@ -0,0 +1,174 @@
const FiltersCollection = require('../../../../../src/api/v4/filter/filters-collection');
const carto = require('../../../../../src/api/v4/index');
const column = 'fake_column';
describe('api/v4/filter/filters-collection', function () {
describe('constructor', function () {
it('should call _initialize', function () {
spyOn(FiltersCollection.prototype, '_initialize');
new FiltersCollection(); // eslint-disable-line
expect(FiltersCollection.prototype._initialize).toHaveBeenCalled();
});
});
describe('._initialize', function () {
it('should set an empty array to filters', function () {
const filtersCollection = new FiltersCollection();
expect(filtersCollection._filters).toEqual([]);
});
it('should set provided filters and call add()', function () {
spyOn(FiltersCollection.prototype, 'addFilter').and.callThrough();
const filters = [
new carto.filter.Range(column, { lt: 1 }),
new carto.filter.Category(column, { in: ['category'] })
];
const filtersCollection = new FiltersCollection(filters);
expect(filtersCollection._filters).toEqual(filters);
expect(FiltersCollection.prototype.addFilter).toHaveBeenCalledTimes(2);
});
});
describe('.addFilter', function () {
let filtersCollection, rangeFilter, triggerFilterChangeSpy, listenToChangeSpy;
beforeEach(function () {
triggerFilterChangeSpy = spyOn(FiltersCollection.prototype, '_triggerFilterChange');
listenToChangeSpy = spyOn(FiltersCollection.prototype, 'listenTo');
filtersCollection = new FiltersCollection();
rangeFilter = new carto.filter.Range(column, { lt: 1 });
});
it('should throw an error if filter is not an instance of SQLBase or FiltersCollection', function () {
expect(function () {
filtersCollection.addFilter({});
}).toThrowError('Filters need to extend from carto.filter.SQLBase. Please use carto.filter.Category or carto.filter.Range.');
});
it('should not readd a filter if it is already added', function () {
filtersCollection.addFilter(rangeFilter);
filtersCollection.addFilter(rangeFilter);
expect(filtersCollection._filters.length).toBe(1);
expect(triggerFilterChangeSpy).toHaveBeenCalledTimes(1);
});
it('should add new filter and trigger change:filters event', function () {
filtersCollection.addFilter(rangeFilter);
expect(filtersCollection._filters.length).toBe(1);
expect(triggerFilterChangeSpy).toHaveBeenCalledTimes(1);
});
it('should register listener to change:filters event in the added filter', function () {
filtersCollection.addFilter(rangeFilter);
expect(listenToChangeSpy).toHaveBeenCalled();
expect(listenToChangeSpy.calls.mostRecent().args[0]).toBe(rangeFilter);
expect(listenToChangeSpy.calls.mostRecent().args[1]).toEqual('change:filters');
});
});
describe('.removeFilter', function () {
let filtersCollection, rangeFilter, triggerFilterChangeSpy;
beforeEach(function () {
triggerFilterChangeSpy = spyOn(FiltersCollection.prototype, '_triggerFilterChange');
filtersCollection = new FiltersCollection();
rangeFilter = new carto.filter.Range(column, { lt: 1 });
});
it('should not remove the filter if it was not already added', function () {
const removedElement = filtersCollection.removeFilter(rangeFilter);
expect(removedElement).toBeUndefined();
expect(triggerFilterChangeSpy).not.toHaveBeenCalled();
});
it('should remove the filter if it was already added', function () {
filtersCollection.addFilter(rangeFilter);
const removedElement = filtersCollection.removeFilter(rangeFilter);
expect(removedElement).toBe(rangeFilter);
expect(triggerFilterChangeSpy).toHaveBeenCalled();
});
});
describe('.count', function () {
let filtersCollection, rangeFilter;
beforeEach(function () {
filtersCollection = new FiltersCollection();
rangeFilter = new carto.filter.Range(column, { lt: 1 });
filtersCollection.addFilter(rangeFilter);
});
it('should return filters length', function () {
expect(filtersCollection.count()).toBe(1);
});
});
describe('.getFilters', function () {
let filtersCollection, rangeFilter;
beforeEach(function () {
filtersCollection = new FiltersCollection();
rangeFilter = new carto.filter.Range(column, { lt: 1 });
filtersCollection.addFilter(rangeFilter);
});
it('should return added filters', function () {
expect(filtersCollection.getFilters()).toEqual([rangeFilter]);
});
});
describe('.$getSQL', function () {
let filtersCollection;
beforeEach(function () {
let rangeFilter = new carto.filter.Range(column, { lt: 1 });
let categoryFilter = new carto.filter.Category(column, { in: ['category'] });
filtersCollection = new FiltersCollection();
filtersCollection.addFilter(rangeFilter);
filtersCollection.addFilter(categoryFilter);
});
it('should build the SQL string and join filters', function () {
expect(filtersCollection.$getSQL()).toEqual("(fake_column < 1 AND fake_column IN ('category'))");
});
it('should not take empty filters into account', function () {
let customRangeFilter = new carto.filter.Range(column, { lt: 1 });
let emptyFilter = new carto.filter.Category(column, {});
filtersCollection = new FiltersCollection();
filtersCollection.addFilter(customRangeFilter);
filtersCollection.addFilter(emptyFilter);
expect(filtersCollection.$getSQL()).toEqual('fake_column < 1');
});
});
describe('._triggerFilterChange', function () {
let filtersCollection;
beforeEach(function () {
filtersCollection = new FiltersCollection();
});
it('should trigger change:filters', function () {
spyOn(filtersCollection, 'trigger');
const filters = [];
filtersCollection._triggerFilterChange(filters);
expect(filtersCollection.trigger).toHaveBeenCalledWith('change:filters', filters);
});
});
});

View File

@@ -0,0 +1,75 @@
var carto = require('../../../../../src/api/v4/index');
describe('api/v4/filter/polygon', function () {
describe('initialization', function () {
it('should create the internalModel', function () {
var polygonFilter = new carto.filter.Polygon();
expect(polygonFilter.$getInternalModel()).toBeDefined();
});
});
describe('.setPolygon', function () {
var polygonFilter;
beforeEach(function () {
polygonFilter = new carto.filter.Polygon();
});
it('checks if polygon is valid', function () {
var test = function () {
polygonFilter.setPolygon({
ups: 'notExpected'
});
};
var test2 = function () {
polygonFilter.setPolygon({
type: 'NotAPolygon',
coordinates: {
content: 'isNotAnArrayOfCoords'
}
});
};
const expected = 'Polygon object is not valid. Use a carto.filter.PolygonData object';
expect(test).toThrowError(Error, expected);
expect(test2).toThrowError(Error, expected);
});
it('if polygon is valid, it assigns it to property, triggers the polygonChanged event and returns this', function () {
spyOn(polygonFilter, 'trigger');
var polygon = {
type: 'Polygon',
coordinates: [[1, 2], [3, 4], [5, 6], [1, 2]]
};
var returnedObject = polygonFilter.setPolygon(polygon);
expect(polygonFilter.getPolygon()).toEqual(polygon);
expect(polygonFilter.trigger).toHaveBeenCalledWith('polygonChanged', polygon);
expect(returnedObject).toBe(polygonFilter);
});
});
describe('.resetPolygon', function () {
it('sets the polygon as empty', function () {
var polygonFilter = new carto.filter.Polygon();
polygonFilter.setPolygon({
type: 'Polygon',
coordinates: [[1, 2], [3, 4], [5, 6], [1, 2]]
});
expect(polygonFilter.getPolygon()).toEqual({
type: 'Polygon',
coordinates: [[1, 2], [3, 4], [5, 6], [1, 2]]
});
polygonFilter.resetPolygon();
expect(polygonFilter.getPolygon()).toEqual({
type: 'Polygon',
coordinates: []
});
});
});
});

View File

@@ -0,0 +1,73 @@
const carto = require('../../../../../src/api/v4/index');
describe('api/v4/filter/range', function () {
describe('constructor', function () {
it('should throw a descriptive error when an unknown filter has been passed', function () {
expect(function () {
new carto.filter.Range('fake_column', { unknown_filter: '' }); // eslint-disable-line
}).toThrowError("'unknown_filter' is not a valid filter. Please check documentation.");
});
});
describe('SQL Templates', function () {
it('LT', function () {
const categoryFilter = new carto.filter.Range('fake_column', { lt: 10 });
expect(categoryFilter.$getSQL()).toBe('fake_column < 10');
});
it('LT with subquery', function () {
const categoryFilter = new carto.filter.Range('fake_column', { lt: { query: 'SELECT avg(price) FROM neighbourhoods' } });
expect(categoryFilter.$getSQL()).toBe('fake_column < (SELECT avg(price) FROM neighbourhoods)');
});
it('LTE', function () {
const categoryFilter = new carto.filter.Range('fake_column', { lte: 10 });
expect(categoryFilter.$getSQL()).toBe('fake_column <= 10');
});
it('LTE with subquery', function () {
const categoryFilter = new carto.filter.Range('fake_column', { lte: { query: 'SELECT avg(price) FROM neighbourhoods' } });
expect(categoryFilter.$getSQL()).toBe('fake_column <= (SELECT avg(price) FROM neighbourhoods)');
});
it('GT', function () {
const categoryFilter = new carto.filter.Range('fake_column', { gt: 10 });
expect(categoryFilter.$getSQL()).toBe('fake_column > 10');
});
it('GT with subquery', function () {
const categoryFilter = new carto.filter.Range('fake_column', { gt: { query: 'SELECT avg(price) FROM neighbourhoods' } });
expect(categoryFilter.$getSQL()).toBe('fake_column > (SELECT avg(price) FROM neighbourhoods)');
});
it('GTE', function () {
const categoryFilter = new carto.filter.Range('fake_column', { gte: 10 });
expect(categoryFilter.$getSQL()).toBe('fake_column >= 10');
});
it('GTE with subquery', function () {
const categoryFilter = new carto.filter.Range('fake_column', { gte: { query: 'SELECT avg(price) FROM neighbourhoods' } });
expect(categoryFilter.$getSQL()).toBe('fake_column >= (SELECT avg(price) FROM neighbourhoods)');
});
it('BETWEEN', function () {
const categoryFilter = new carto.filter.Range('fake_column', { between: { min: 1, max: 10 } });
expect(categoryFilter.$getSQL()).toBe('fake_column BETWEEN 1 AND 10');
});
it('NOT BETWEEN', function () {
const categoryFilter = new carto.filter.Range('fake_column', { notBetween: { min: 1, max: 10 } });
expect(categoryFilter.$getSQL()).toBe('fake_column NOT BETWEEN 1 AND 10');
});
it('BETWEEN SYMMETRIC', function () {
const categoryFilter = new carto.filter.Range('fake_column', { betweenSymmetric: { min: 1, max: 10 } });
expect(categoryFilter.$getSQL()).toBe('fake_column BETWEEN SYMMETRIC 1 AND 10');
});
it('NOT BETWEEN SYMMETRIC', function () {
const categoryFilter = new carto.filter.Range('fake_column', { notBetweenSymmetric: { min: 1, max: 10 } });
expect(categoryFilter.$getSQL()).toBe('fake_column NOT BETWEEN SYMMETRIC 1 AND 10');
});
});
});

View File

@@ -0,0 +1,171 @@
var Aggregation = require('../../../../../src/api/v4/layer/aggregation');
describe('layer-aggregation', function () {
var options;
beforeEach(function () {
options = {
threshold: 10000,
resolution: 1,
placement: 'point-sample',
columns: {
fake_name_0: {
aggregateFunction: 'sum',
aggregatedColumn: 'fake_column_0'
},
fake_name_1: {
aggregateFunction: 'avg',
aggregatedColumn: 'fake_column_1'
}
}
};
});
describe('constructor', function () {
it('should return a simple object when the parameters are valid', function () {
var aggregation = new Aggregation(options);
// Multiple specs for easy debugging
expect(aggregation.threshold).toEqual(options.threshold);
expect(aggregation.resolution).toEqual(options.resolution);
expect(aggregation.placement).toEqual(options.placement);
expect(aggregation.columns.fake_name_0).toEqual({
aggregate_function: 'sum',
aggregated_column: 'fake_column_0'
});
expect(aggregation.columns.fake_name_1).toEqual({
aggregate_function: 'avg',
aggregated_column: 'fake_column_1'
});
});
describe('errors', function () {
describe('threshold', function () {
it('should throw an error when threshold is not defined', function () {
delete options.threshold;
expect(function () {
new Aggregation(options); // eslint-disable-line
}).toThrowError('Aggregation threshold is required.');
});
it('should throw an error when threshold is not a positive integer', function () {
options.threshold = 0;
expect(function () {
new Aggregation(options); // eslint-disable-line
}).toThrowError('Aggregation threshold must be an integer value greater than 0.');
options.threshold = -1;
expect(function () {
new Aggregation(options); // eslint-disable-line
}).toThrowError('Aggregation threshold must be an integer value greater than 0.');
options.threshold = 2.5;
expect(function () {
new Aggregation(options); // eslint-disable-line
}).toThrowError('Aggregation threshold must be an integer value greater than 0.');
});
});
describe('resolution', function () {
it('should throw an error when resolution is not defined', function () {
options.resolution = undefined;
expect(function () {
new Aggregation(options); // eslint-disable-line
}).toThrowError('Aggregation resolution is required.');
});
it('should throw an error when resolution is not an integer between 1 and 16', function () {
var expectedErrorMessage = 'Aggregation resolution must be 0.5, 1 or powers of 2 up to 256 (2, 4, 8, 16, 32, 64, 128, 256).';
options.resolution = 0;
expect(function () {
new Aggregation(options); // eslint-disable-line
}).toThrowError(expectedErrorMessage);
options.resolution = 17;
expect(function () {
new Aggregation(options); // eslint-disable-line
}).toThrowError(expectedErrorMessage);
var validOnes = [0.5, 1, 2, 4, 8, 16, 32, 64, 128, 256];
validOnes.forEach(function (resolution) {
expect(function () {
options.resolution = resolution;
new Aggregation(options); // eslint-disable-line
}).not.toThrowError();
});
});
});
describe('placement', function () {
it('should throw an error when placement is not one of our three valid placements', function () {
options.placement = 'invalid_placement';
expect(function () {
new Aggregation(options); // eslint-disable-line
}).toThrowError('Aggregation placement is not valid. Must be one of these values: `point-sample`, `point-grid`, `centroid`');
});
});
describe('columns', function () {
it('should thrown an error when column.aggregateFunction is not defined', function () {
options.columns = {
fake_name_0: {
aggregatedColumn: 'fake_column_0'
}
};
expect(function () {
new Aggregation(options); // eslint-disable-line
}).toThrowError("Aggregation function for column 'fake_name_0' is required.");
});
it('should thrown an error when column.aggregateFunction is not a valid function', function () {
options.columns = {
fake_name_0: {
aggregatedColumn: 'fake_column_0',
aggregateFunction: 'invalid_function'
}
};
expect(function () {
new Aggregation(options); // eslint-disable-line
}).toThrowError("Aggregation function for column 'fake_name_0' is not valid. Use carto.aggregation.function");
});
it('should thrown an error when column.aggregatedColumn is not defined', function () {
options.columns = {
fake_name_0: {
aggregateFunction: 'sum'
}
};
expect(function () {
new Aggregation(options); // eslint-disable-line
}).toThrowError("Column to be aggregated to 'fake_name_0' is required.");
});
it('should thrown an error when column.aggregatedColumn is not a string', function () {
options.columns = {
fake_name_0: {
aggregatedColumn: 4500,
aggregateFunction: 'sum'
}
};
expect(function () {
new Aggregation(options); // eslint-disable-line
}).toThrowError("Column to be aggregated to 'fake_name_0' must be a string.");
});
});
describe('optional placement and columns', function () {
it('should now throw an error when neither placement nor columns appear', function () {
delete options.columns;
delete options.placement;
expect(function () {
new Aggregation(options); // eslint-disable-line
}).not.toThrow();
});
});
});
});
});

View File

@@ -0,0 +1,677 @@
var carto = require('../../../../../src/api/v4');
describe('api/v4/layer', function () {
var source;
var style;
var originalTimeout;
beforeEach(function () {
source = new carto.source.Dataset('ne_10m_populated_places_simple');
style = new carto.style.CartoCSS('#layer { marker-fill: red; }');
originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
});
afterEach(function () {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
});
describe('constructor', function () {
it('should build a new Layer params: (source, style)', function () {
var layer = new carto.layer.Layer(source, style);
expect(layer.getSource()).toEqual(source);
expect(layer.getStyle()).toEqual(style);
});
it('should assign a unique layer ID string', function () {
var layer1 = new carto.layer.Layer(source, style);
var layer2 = new carto.layer.Layer(source, style);
var id1 = layer1.getId();
var id2 = layer2.getId();
expect(id1).toMatch(/L\d+/);
expect(id2).toMatch(/L\d+/);
expect(id1).not.toEqual(id2);
});
it('should be able to create a hidden layer', function () {
var layer = new carto.layer.Layer(source, style, {
visible: false
});
expect(layer.isHidden()).toBe(true);
});
it('should build a new Layer params: (source, style, options)', function () {
var layer = new carto.layer.Layer(source, style, {
featureClickColumns: ['a', 'b'],
featureOverColumns: ['c', 'd']
});
expect(layer.getSource()).toEqual(source);
expect(layer.getStyle()).toEqual(style);
expect(layer.getFeatureClickColumns()).toEqual(['a', 'b']);
expect(layer.getFeatureOverColumns()).toEqual(['c', 'd']);
});
it('should throw an error if source is not valid', function () {
expect(function () {
new carto.layer.Layer({}, style); // eslint-disable-line
}).toThrowError('The given object is not a valid source. See "carto.source.Base".');
});
it('should throw an error if style is not valid', function () {
expect(function () {
new carto.layer.Layer(source, {}); // eslint-disable-line
}).toThrowError('The given object is not a valid style. See "carto.style.Base".');
});
it('should allow custom layer id as an option', function () {
var layer = new carto.layer.Layer(source, style, { id: 'fake_id' });
expect(layer.getId()).toEqual('fake_id');
});
describe('columns validation', function () {
var aggregation = new carto.layer.Aggregation({
threshold: 1,
resolution: 4,
columns: {
population: {
aggregateFunction: 'sum',
aggregatedColumn: 'pop_max'
}
}
});
it('should validate that featureClick columns are contained in aggregation columns', function () {
expect(function () {
new carto.layer.Layer(source, style, { // eslint-disable-line
featureClickColumns: ['a', 'b'],
aggregation: aggregation
});
}).toThrowError('Columns [a, b] set on `featureClick` do not match the columns set in aggregation options.');
});
it('should validate that featureOver columns are contained in aggregation columns', function () {
expect(function () {
new carto.layer.Layer(source, style, { // eslint-disable-line
featureOverColumns: ['a', 'b'],
aggregation: aggregation
});
}).toThrowError('Columns [a, b] set on `featureOver` do not match the columns set in aggregation options.');
});
});
});
describe('.setStyle', function () {
var layer;
var newStyle;
beforeEach(function () {
layer = new carto.layer.Layer(source, style);
newStyle = new carto.style.CartoCSS('#layer { marker-fill: green; }');
});
it('should throw an error when the parameter is not a valid style', function () {
expect(function () {
layer.setStyle('bad-style');
}).toThrowError('The given object is not a valid style. See "carto.style.Base".');
});
describe('when the layer has no engine', function () {
it('should set the layer style', function () {
layer.setStyle(newStyle);
expect(layer.getStyle()).toEqual(newStyle);
});
it('should fire a styleChanged event', function (done) {
layer.on('styleChanged', function (l) {
expect(l).toBe(layer);
expect(l.getStyle()).toEqual(newStyle);
done();
});
layer.setStyle(newStyle);
});
it('should not fire a styleChanged event when setting the same style twice', function () {
var styleChangedSpy = jasmine.createSpy('styleChangedSpy');
layer.on('styleChanged', styleChangedSpy);
layer.setStyle(style);
expect(styleChangedSpy).not.toHaveBeenCalled();
});
});
describe('when the layer has an engine', function () {
var client;
beforeEach(function () {
client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test'
});
});
describe('and the style has no engine', function () {
it('should set the engine into the style and update the internal style.', function (done) {
client.addLayer(layer)
.then(function () {
return layer.setStyle(newStyle);
})
.then(function () {
expect(layer.getStyle().$getEngine()).toBe(layer._engine);
done();
});
});
});
describe('and the style has an engine', function () {
it('should update the internal style when the engines are equal', function (done) {
newStyle.$setEngine(client._engine);
client.addLayer(layer)
.then(function () {
return layer.setStyle(newStyle);
})
.then(function () {
expect(layer.getStyle().$getEngine()).toBe(layer._engine);
done();
});
});
it('should throw an error when the engines are not equal', function (done) {
newStyle.$setEngine('fakeEngine');
client.addLayer(layer)
.then(function () {
expect(function () {
return layer.setStyle(newStyle);
}).toThrowError('A layer can\'t have a style which belongs to a different client.');
done();
});
});
});
it('should fire a cartoError when the style is invalid', function (done) {
var styleChangedSpy = jasmine.createSpy('styleChangedSpy');
layer.on('error', styleChangedSpy);
client.addLayer(layer)
.then(function () {
var malformedStyle = new carto.style.CartoCSS('#layer { invalid-rule: foo}');
return layer.setStyle(malformedStyle);
})
.catch(function () {
expect(styleChangedSpy).toHaveBeenCalled();
done();
});
});
it('should fire a styleChanged event when the layer belongs to a client', function (done) {
var styleChangedSpy = jasmine.createSpy('styleChangedSpy');
layer.on('styleChanged', styleChangedSpy);
client.addLayer(layer)
.then(function () {
return layer.setStyle(newStyle);
})
.then(function () {
expect(styleChangedSpy).toHaveBeenCalled();
done();
});
});
it('should set the internal model style', function (done) {
var client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test'
});
client.on(carto.events.ERROR, alert);
client.addLayer(layer)
.then(function () {
client.on(carto.events.SUCCESS, function () {
var expected = '#layer { marker-fill: green; }';
var actual = layer.$getInternalModel().get('cartocss');
expect(expected).toEqual(actual);
done();
});
layer.setStyle(newStyle);
});
});
});
});
describe('.$setEngine', function () {
it('probando', function () {
var layer = new carto.layer.Layer(source, style);
var engineMock = { on: jasmine.createSpy('on'), reload: jasmine.createSpy('reload').and.returnValue(Promise.resolve()) };
var error = {
message: 'an error'
};
var capturedError;
layer.$setEngine(engineMock);
layer.on(carto.events.ERROR, function (error) {
capturedError = error;
});
layer._internalModel.set('error', error);
expect(capturedError).toBeDefined();
expect(capturedError.name).toEqual('CartoError');
expect(capturedError.message).toEqual('an error');
});
});
describe('.setSource', function () {
var layer;
var newSource;
beforeEach(function () {
layer = new carto.layer.Layer(source, style);
newSource = new carto.source.SQL('SELECT * FROM ne_10m_populated_places_simple LIMIT 10');
});
it('should throw an error when the source is not a valid parameter', function () {
expect(function () {
layer.setSource('bad-parameter');
}).toThrowError('The given object is not a valid source. See "carto.source.Base".');
});
describe('when the layer hasn\'t been set an engine', function () {
it('should normally add the source', function () {
layer.setSource(newSource);
expect(layer.getSource()).toEqual(newSource);
});
it('should fire a sourceChanged event', function (done) {
layer.on('sourceChanged', function (l) {
expect(l).toBe(layer);
expect(l.getSource()).toEqual(newSource);
done();
});
layer.setSource(newSource);
});
});
describe('when the layer has been set an engine', function () {
var engineMock;
beforeEach(function () {
engineMock = { on: jasmine.createSpy('on'), reload: jasmine.createSpy('reload').and.returnValue(Promise.resolve()) };
layer.$setEngine(engineMock);
});
describe('and the source has no engine', function () {
it('should normally add the source', function (done) {
layer.setSource(newSource)
.then(function () {
var actualSource = layer.$getInternalModel().get('source');
var expectedSource = newSource.$getInternalModel();
expect(actualSource).toBeDefined();
expect(actualSource).toEqual(expectedSource);
done();
});
});
it('should fire a sourceChanged event', function (done) {
var sourceChangedSpy = jasmine.createSpy('sourceChangedSpy');
layer.on('sourceChanged', sourceChangedSpy);
layer.setSource(newSource)
.then(function () {
expect(sourceChangedSpy).toHaveBeenCalled();
done();
});
});
});
describe('and the source has an engine', function () {
it('should add the source if the engines are the same', function () {
newSource.$setEngine(engineMock);
layer.setSource(newSource);
var actualSource = layer.$getInternalModel().get('source');
var expectedSource = newSource.$getInternalModel();
expect(actualSource).toEqual(expectedSource);
});
it('should throw an error if the engines are different', function () {
// This engine is different from the layer's one.
var engineMock1 = { on: jasmine.createSpy('on'), reload: jasmine.createSpy('reload') };
newSource.$setEngine(engineMock1);
expect(function () {
layer.setSource(newSource);
}).toThrowError('A layer can\'t have a source which belongs to a different client.');
});
});
});
it('should not fire a sourceChanged event when setting the same source twice', function () {
var sourceChangedSpy = jasmine.createSpy('sourceChangedSpy');
layer.on('sourceChanged', sourceChangedSpy);
layer.setSource(source);
expect(sourceChangedSpy).not.toHaveBeenCalled();
});
it('should fire a cartoError when the source is invalid', function (done) {
var client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test'
});
var sourceChangedSoy = jasmine.createSpy('sourceChangedSoy');
layer.on('error', sourceChangedSoy);
client.addLayer(layer)
.then(function () {
var invalidDataset = new carto.source.Dataset('invalid_dataset');
return layer.setSource(invalidDataset);
})
.catch(function () {
expect(sourceChangedSoy).toHaveBeenCalled();
done();
});
});
});
describe('.setFeatureClickColumns', function () {
var layer;
var newColums;
beforeEach(function () {
layer = new carto.layer.Layer(source, style);
newColums = ['a', 'b'];
});
it('should throw an error when the columns are not a valid parameter', function () {
expect(function () {
layer.setFeatureClickColumns([1, 2, 3]);
}).toThrowError('The given object is not a valid array of string columns.');
});
describe('when the layer hasn\'t been set an engine', function () {
it('should normally add the columns', function () {
layer.setFeatureClickColumns(newColums);
expect(layer.getFeatureClickColumns()).toEqual(newColums);
});
});
describe('when the layer has been set an engine', function () {
var engineMock;
it('should normally add the columns', function (done) {
engineMock = { on: jasmine.createSpy('on'), reload: jasmine.createSpy('reload').and.returnValue(Promise.resolve()) };
layer.$setEngine(engineMock);
layer.setFeatureClickColumns(newColums)
.then(function () {
var actualColumns = layer.getFeatureClickColumns();
var expectedColumns = newColums;
expect(actualColumns).toBeDefined();
expect(actualColumns).toEqual(expectedColumns);
done();
});
});
it('should throw an error when the columns are not valid', function (done) {
engineMock = { on: jasmine.createSpy('on'), reload: jasmine.createSpy('reload').and.returnValue(Promise.reject(new Error())) };
layer.$setEngine(engineMock);
layer.setFeatureClickColumns(['wrong'])
.catch(function (error) {
expect(error instanceof Error).toBe(true);
done();
});
});
it('should fire an error event when the columns are not valid', function (done) {
engineMock = { on: jasmine.createSpy('on'), reload: jasmine.createSpy('reload').and.returnValue(Promise.reject(new Error())) };
layer.$setEngine(engineMock);
var columnChangedError = jasmine.createSpy('columnChangedError');
layer.on('error', columnChangedError);
layer.setFeatureClickColumns(['wrong'])
.catch(function () {
expect(columnChangedError).toHaveBeenCalled();
done();
});
});
});
});
describe('.setFeatureOverColumns', function () {
var layer;
var newColums;
beforeEach(function () {
layer = new carto.layer.Layer(source, style);
newColums = ['a', 'b'];
});
it('should throw an error when the columns are not a valid parameter', function () {
expect(function () {
layer.setFeatureOverColumns([1, 2, 3]);
}).toThrowError('The given object is not a valid array of string columns.');
});
describe('when the layer hasn\'t been set an engine', function () {
it('should normally add the columns', function () {
layer.setFeatureOverColumns(newColums);
expect(layer.getFeatureOverColumns()).toEqual(newColums);
});
});
describe('when the layer has been set an engine', function () {
var engineMock;
it('should normally add the columns', function (done) {
engineMock = { on: jasmine.createSpy('on'), reload: jasmine.createSpy('reload').and.returnValue(Promise.resolve()) };
layer.$setEngine(engineMock);
layer.setFeatureOverColumns(newColums)
.then(function () {
var actualColumns = layer.getFeatureOverColumns();
var expectedColumns = newColums;
expect(actualColumns).toBeDefined();
expect(actualColumns).toEqual(expectedColumns);
done();
});
});
it('should throw an error when the columns are not valid', function (done) {
engineMock = { on: jasmine.createSpy('on'), reload: jasmine.createSpy('reload').and.returnValue(Promise.reject(new Error())) };
layer.$setEngine(engineMock);
layer.setFeatureOverColumns(['wrong'])
.catch(function (error) {
expect(error instanceof Error).toBe(true);
done();
});
});
it('should fire an error event when the columns are not valid', function (done) {
engineMock = { on: jasmine.createSpy('on'), reload: jasmine.createSpy('reload').and.returnValue(Promise.reject(new Error())) };
layer.$setEngine(engineMock);
var columnChangedError = jasmine.createSpy('columnChangedError');
layer.on('error', columnChangedError);
layer.setFeatureOverColumns(['wrong'])
.catch(function () {
expect(columnChangedError).toHaveBeenCalled();
done();
});
});
});
});
describe('.show', function () {
it('should set the layer visibility to true', function () {
var layer = new carto.layer.Layer(source, style);
expect(layer.isVisible()).toEqual(true);
layer.hide();
expect(layer.isVisible()).toEqual(false);
expect(layer.isHidden()).toEqual(true);
layer.show();
expect(layer.isVisible()).toEqual(true);
expect(layer.isHidden()).toEqual(false);
});
it('should trigger a visibilityChanged event', function (done) {
var layer = new carto.layer.Layer(source, style);
expect(layer.isVisible()).toEqual(true);
layer.hide();
layer.on('visibilityChanged', function () {
expect(layer.isVisible()).toEqual(true);
expect(layer.isHidden()).toEqual(false);
done();
});
layer.show();
});
});
describe('.hide', function () {
it('should set the layer visibility to false', function () {
var layer = new carto.layer.Layer(source, style);
expect(layer.isVisible()).toEqual(true);
layer.hide();
expect(layer.isVisible()).toEqual(false);
expect(layer.isHidden()).toEqual(true);
});
it('should trigger a visibilityChanged event', function (done) {
var layer = new carto.layer.Layer(source, style);
expect(layer.isVisible()).toEqual(true);
layer.on('visibilityChanged', function () {
expect(layer.isVisible()).toEqual(false);
expect(layer.isHidden()).toEqual(true);
done();
});
layer.hide();
});
});
describe('.toggle', function () {
it('should toggle the layer visibility', function () {
var layer = new carto.layer.Layer(source, style);
expect(layer.isVisible()).toEqual(true);
layer.toggle();
expect(layer.isVisible()).toEqual(false);
expect(layer.isHidden()).toEqual(true);
layer.toggle();
expect(layer.isVisible()).toEqual(true);
expect(layer.isHidden()).toEqual(false);
});
it('should trigger a visibilityChanged event', function (done) {
var layer = new carto.layer.Layer(source, style);
expect(layer.isVisible()).toEqual(true);
layer.on('visibilityChanged', function () {
expect(layer.isVisible()).toEqual(false);
expect(layer.isHidden()).toEqual(true);
done();
});
layer.toggle();
});
});
describe('.setOrder', function () {
it('should call moveLayer with the passed index', function () {
var clientMock = { moveLayer: jasmine.createSpy('moveLayer') };
var layer = new carto.layer.Layer(source, style);
layer.$setClient(clientMock);
layer.setOrder(1);
expect(clientMock.moveLayer).toHaveBeenCalledWith(layer, 1);
});
});
describe('.bringToBack', function () {
it('should call moveLayer with the passed index', function () {
var clientMock = { moveLayer: jasmine.createSpy('moveLayer') };
var layer = new carto.layer.Layer(source, style);
layer.$setClient(clientMock);
layer.bringToBack();
expect(clientMock.moveLayer).toHaveBeenCalledWith(layer, 0);
});
});
describe('.bringToFront', function () {
it('should call moveLayer with the passed index', function () {
var numberOfLayers = 3;
var clientMock = { moveLayer: jasmine.createSpy('moveLayer'), _layers: { size: function () { return numberOfLayers; } } };
var layer = new carto.layer.Layer(source, style);
layer.$setClient(clientMock);
layer.bringToFront();
expect(clientMock.moveLayer).toHaveBeenCalledWith(layer, numberOfLayers - 1);
});
});
describe('.isInteractive', function () {
it('returns true if layer has featureClickColumns', function () {
const layer = new carto.layer.Layer(source, style, {
featureClickColumns: ['cartodb_id']
});
expect(layer.isInteractive()).toBe(true);
});
it('returns true if layer has featureOverColumns', function () {
const layer = new carto.layer.Layer(source, style, {
featureOverColumns: ['cartodb_id']
});
expect(layer.isInteractive()).toBe(true);
});
it('returns false if layer doesn\'t have getFeatureClickColumns or getFeatureHoverColumns', function () {
const layer = new carto.layer.Layer(source, style);
expect(layer.isInteractive()).toBe(false);
});
});
xit('should update "internalmodel.cartocss" when the style is updated', function (done) {
var client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test'
});
var layer = new carto.layer.Layer(source, style);
var newStyle = '#layer { marker-fill: #FABADA; }';
client.addLayer(layer)
.then(function () {
return style.setContent(newStyle);
})
.then(function () {
expect(layer.$getInternalModel().get('cartocss')).toEqual(newStyle);
done();
});
});
});

View File

@@ -0,0 +1,230 @@
var metadataParser = require('../../../../../../src/api/v4/layer/metadata/parser');
describe('api/v4/layer/metadata/parser', function () {
describe('.getMetadataFromRules', function () {
it('should parse correctly the range rules', function () {
// From:
// marker-width: ramp([scalerank], range(5, 20), quantiles(4));
// marker-fill-opacity: ramp([pop_max], range(0,1), jenks);
var rules = [
{
'selector': '#layer',
'prop': 'marker-width',
'column': 'scalerank',
'mapping': '>',
'buckets': [
{
'filter': {
'type': 'range',
'start': 0,
'end': 6
},
'value': 5
},
{
'filter': {
'type': 'range',
'start': 6,
'end': 7
},
'value': 10
},
{
'filter': {
'type': 'range',
'start': 7,
'end': 7
},
'value': 15
},
{
'filter': {
'type': 'range',
'start': 7,
'end': 10
},
'value': 20
}
],
'stats': {
'filter_avg': 6.642174269325321
}
},
{
'selector': '#layer',
'prop': 'marker-fill-opacity',
'column': 'pop_max',
'mapping': '>',
'buckets': [
{
'filter': {
'type': 'range',
'start': -99,
'end': 37945
},
'value': 0
},
{
'filter': {
'type': 'range',
'start': 37945,
'end': 138954
},
'value': 0.25
},
{
'filter': {
'type': 'range',
'start': 138954,
'end': 616990
},
'value': 0.5
},
{
'filter': {
'type': 'range',
'start': 616990,
'end': 2544000
},
'value': 0.75
},
{
'filter': {
'type': 'range',
'start': 2544000,
'end': 35676000
},
'value': 1
}
],
'stats': {
'filter_avg': 322717.4763725758
}
}
];
var metadataList = metadataParser.getMetadataFromRules(rules);
expect(metadataList).toBeDefined();
expect(metadataList[0].getType()).toBe('buckets');
expect(metadataList[0].getColumn()).toBe('scalerank');
expect(metadataList[0].getMapping()).toBe('>');
expect(metadataList[0].getProperty()).toBe('marker-width');
expect(metadataList[0].getAverage()).toBe(6.642174269325321);
expect(metadataList[0].getMin()).toBe(0);
expect(metadataList[0].getMax()).toBe(10);
expect(metadataList[0].getBuckets()).toEqual([
{ min: 0, max: 6, value: 5 },
{ min: 6, max: 7, value: 10 },
{ min: 7, max: 7, value: 15 },
{ min: 7, max: 10, value: 20 }
]);
expect(metadataList[0].getType()).toBe('buckets');
expect(metadataList[1].getColumn()).toBe('pop_max');
expect(metadataList[1].getMapping()).toBe('>');
expect(metadataList[1].getProperty()).toBe('marker-fill-opacity');
expect(metadataList[1].getAverage()).toBe(322717.4763725758);
expect(metadataList[1].getMin()).toBe(-99);
expect(metadataList[1].getMax()).toBe(35676000);
expect(metadataList[1].getBuckets()).toEqual([
{ min: -99, max: 37945, value: 0 },
{ min: 37945, max: 138954, value: 0.25 },
{ min: 138954, max: 616990, value: 0.5 },
{ min: 616990, max: 2544000, value: 0.75 },
{ min: 2544000, max: 35676000, value: 1 }
]);
});
it('should parse correctly the category rules', function () {
// From:
// marker-fill: ramp([scalerank], (#5F4690, #1D6996, #38A6A5, #666666), (7, 6, 10), '=', category);
// marker-file: ramp([scalerank], (url('https://s3.amazonaws.com/com.cartodb.users-assets.production/maki-icons/rail-light-18.svg'), url('https://s3.amazonaws.com/com.cartodb.users-assets.production/maki-icons/park-18.svg')), (7, 10), '=');
var rules = [
{
'selector': '#layer',
'prop': 'marker-fill',
'column': 'scalerank',
'mapping': '=',
'buckets': [
{
'filter': {
'name': 7,
'type': 'category'
},
'value': '#5F4690'
},
{
'filter': {
'name': 6,
'type': 'category'
},
'value': '#1D6996'
},
{
'filter': {
'name': 10,
'type': 'category'
},
'value': '#38A6A5'
},
{
'filter': {
'type': 'default'
},
'value': '#666666'
}
],
'stats': {}
},
{
'selector': '',
'prop': 'marker-file',
'column': 'scalerank',
'mapping': '=',
'buckets': [
{
'filter': {
'name': 7,
'type': 'category'
},
'value': 'url(\'https://s3.amazonaws.com/com.cartodb.users-assets.production/maki-icons/rail-light-18.svg\')'
},
{
'filter': {
'name': 10,
'type': 'category'
},
'value': 'url(\'https://s3.amazonaws.com/com.cartodb.users-assets.production/maki-icons/park-18.svg\')'
}
],
'stats': {}
}
];
var metadataList = metadataParser.getMetadataFromRules(rules);
expect(metadataList).toBeDefined();
expect(metadataList[0].getType()).toBe('categories');
expect(metadataList[0].getColumn()).toBe('scalerank');
expect(metadataList[0].getMapping()).toBe('=');
expect(metadataList[0].getProperty()).toBe('marker-fill');
expect(metadataList[0].getDefaultValue()).toBe('#666666');
expect(metadataList[0].getCategories()).toEqual([
{ name: 7, value: '#5F4690' },
{ name: 6, value: '#1D6996' },
{ name: 10, value: '#38A6A5' }
]);
expect(metadataList[0].getType()).toBe('categories');
expect(metadataList[1].getColumn()).toBe('scalerank');
expect(metadataList[1].getMapping()).toBe('=');
expect(metadataList[1].getProperty()).toBe('marker-file');
expect(metadataList[1].getDefaultValue()).not.toBeDefined();
expect(metadataList[1].getCategories()).toEqual([
{ name: 7, value: 'url(\'https://s3.amazonaws.com/com.cartodb.users-assets.production/maki-icons/rail-light-18.svg\')' },
{ name: 10, value: 'url(\'https://s3.amazonaws.com/com.cartodb.users-assets.production/maki-icons/park-18.svg\')' }
]);
});
});
});

View File

@@ -0,0 +1,41 @@
var Layers = require('../../../../src/api/v4/layers');
describe('api/v4/layers', function () {
var layers;
function createFakeLayer (id) {
return {
getId: function () {
return id;
}
};
}
function seedLayers (layers) {
var layerA = createFakeLayer('A');
var layerB = createFakeLayer('B');
var layerC = createFakeLayer('C');
layers.add(layerA);
layers.add(layerB);
layers.add(layerC);
return [layerA, layerB, layerC];
}
beforeEach(function () {
layers = new Layers();
});
describe('.remove', function () {
it('should remove the layer from the collection', function () {
var createdLayers = seedLayers(layers);
layers.remove(createdLayers[1]);
expect(layers.size()).toBe(2);
expect(layers.indexOf(createdLayers[0])).toBe(0);
expect(layers.indexOf(createdLayers[2])).toBe(1);
});
});
});

View File

@@ -0,0 +1,141 @@
/* global google */
var carto = require('../../../../../src/api/v4');
describe('src/api/v4/native/google-maps-map-type', function () {
var client;
var layer;
var mapType;
var map;
beforeEach(function () {
var element = document.createElement('div');
element.id = 'map';
document.body.appendChild(element);
client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test'
});
map = new google.maps.Map(element);
mapType = client.getGoogleMapsMapType(map);
});
afterEach(function () {
document.getElementById('map').remove();
});
describe('layer events', function () {
var spy;
var internalEventMock;
beforeEach(function () {
spy = jasmine.createSpy('spy');
map.overlayMapTypes.push(mapType);
var source = new carto.source.SQL('foo');
var style = new carto.style.CartoCSS('bar');
layer = new carto.layer.Layer(source, style);
client.addLayer(layer, { reload: false });
internalEventMock = {
layer: {
id: layer.getId()
},
latlng: [ 10, 20 ],
feature: { name: 'foo' }
};
});
it('should trigger carto.layer.events.FEATURE_CLICKED event', function () {
layer.on(carto.layer.events.FEATURE_CLICKED, spy);
var expectedExternalEvent = {
data: { name: 'foo' },
latLng: { lat: 10, lng: 20 }
};
mapType._internalView.trigger('featureClick', internalEventMock);
expect(spy).toHaveBeenCalledWith(expectedExternalEvent);
});
it('should trigger carto.layer.events.FEATURE_OVER event', function () {
layer.on(carto.layer.events.FEATURE_OVER, spy);
var expectedExternalEvent = {
data: { name: 'foo' },
latLng: { lat: 10, lng: 20 }
};
mapType._internalView.trigger('featureOver', internalEventMock);
expect(spy).toHaveBeenCalledWith(expectedExternalEvent);
});
it('should trigger carto.layer.events.FEATURE_OUT featureOut events', function () {
layer.on(carto.layer.events.FEATURE_OUT, spy);
var expectedExternalEvent = {
data: { name: 'foo' },
latLng: { lat: 10, lng: 20 }
};
mapType._internalView.trigger('featureOut', internalEventMock);
expect(spy).toHaveBeenCalledWith(expectedExternalEvent);
});
describe('mouse pointer', function () {
describe('when mousing over a feature', function () {
it("should NOT set the mouse cursor to 'pointer' if layer doesn't have featureOverColumns or featureClickColumns", function () {
expect(map.get('draggableCursor')).not.toBeDefined();
mapType._internalView.trigger('featureOver', internalEventMock);
expect(map.get('draggableCursor')).not.toBeDefined();
});
it("should set the mouse cursor to 'pointer' if layer has featureOverColumns", function () {
layer._featureOverColumns = [ 'foo' ];
expect(map.get('draggableCursor')).not.toBeDefined();
mapType._internalView.trigger('featureOver', internalEventMock);
expect(map.get('draggableCursor')).toEqual('pointer');
});
it("should set the mouse cursor to 'pointer' if layer has featureClickColumns", function () {
layer._featureClickColumns = [ 'foo' ];
expect(map.get('draggableCursor')).not.toBeDefined();
mapType._internalView.trigger('featureOver', internalEventMock);
expect(map.get('draggableCursor')).toEqual('pointer');
});
it("should set the mouse cursor to 'pointer' if layer has overed features after a featureOut", function () {
mapType._hoveredLayers = ['L100'];
expect(map.get('draggableCursor')).not.toBeDefined();
mapType._internalView.trigger('featureOut', internalEventMock);
expect(map.get('draggableCursor')).toEqual('pointer');
});
});
describe('when mousing over NO features', function () {
it("should set the mouse cursor to 'auto'", function () {
expect(map.get('draggableCursor')).not.toBeDefined();
mapType._internalView.trigger('featureOut', internalEventMock);
expect(map.get('draggableCursor')).toEqual('auto');
});
});
});
});
});

View File

@@ -0,0 +1,198 @@
/* global L */
var carto = require('../../../../../src/api/v4');
describe('src/api/v4/native/leaflet-layer', function () {
var client;
var layer;
var leafletLayer;
var map;
beforeEach(function () {
var element = document.createElement('div');
element.id = 'map';
document.body.appendChild(element);
client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test'
});
map = L.map('map').setView([42.431234, -8.643616], 5);
});
afterEach(function () {
document.getElementById('map').remove();
});
it('allows custom options', function () {
leafletLayer = client.getLeafletLayer({ maxZoom: 10 });
expect(leafletLayer.options.maxZoom).toBe(10);
});
describe('addTo', function () {
beforeEach(function () {
leafletLayer = client.getLeafletLayer();
});
it('should add a leaflet layer to the map', function () {
expect(countLeafletLayers(map)).toEqual(0);
leafletLayer.addTo(map);
expect(countLeafletLayers(map)).toEqual(1);
});
});
describe('removeFrom', function () {
beforeEach(function () {
leafletLayer = client.getLeafletLayer();
});
it('should remove the leaflet layer from the map', function () {
expect(countLeafletLayers(map)).toEqual(0);
leafletLayer.addTo(map);
expect(countLeafletLayers(map)).toEqual(1);
leafletLayer.removeFrom(map);
expect(countLeafletLayers(map)).toEqual(0);
});
});
describe('layer events', function () {
var spy;
var internalEventMock;
beforeEach(function () {
spy = jasmine.createSpy('spy');
leafletLayer = client.getLeafletLayer();
leafletLayer.addTo(map);
var source = new carto.source.SQL('foo');
var style = new carto.style.CartoCSS('bar');
layer = new carto.layer.Layer(source, style);
client.addLayer(layer);
internalEventMock = {
layer: {
id: layer.getId()
},
latlng: [ 10, 20 ],
feature: { name: 'foo' }
};
});
it('should trigger carto.layer.events.FEATURE_CLICKED event', function () {
layer.on(carto.layer.events.FEATURE_CLICKED, spy);
var expectedExternalEvent = {
data: { name: 'foo' },
latLng: { lat: 10, lng: 20 }
};
leafletLayer._internalView.trigger('featureClick', internalEventMock);
expect(spy).toHaveBeenCalledWith(expectedExternalEvent);
});
it('should trigger carto.layer.events.FEATURE_OVER event', function () {
layer.on(carto.layer.events.FEATURE_OVER, spy);
var expectedExternalEvent = {
data: { name: 'foo' },
latLng: { lat: 10, lng: 20 }
};
leafletLayer._internalView.trigger('featureOver', internalEventMock);
expect(spy).toHaveBeenCalledWith(expectedExternalEvent);
});
it('should trigger carto.layer.events.FEATURE_OUT featureOut events', function () {
layer.on(carto.layer.events.FEATURE_OUT, spy);
var expectedExternalEvent = {
data: { name: 'foo' },
latLng: { lat: 10, lng: 20 }
};
leafletLayer._internalView.trigger('featureOut', internalEventMock);
expect(spy).toHaveBeenCalledWith(expectedExternalEvent);
});
it('should trigger carto.layer.events.TILE_ERROR events', function () {
layer.on(carto.layer.events.TILE_ERROR, spy);
layer._featureClickColumns = [ 'foo' ];
var error = {
message: 'an error'
};
leafletLayer._internalView.trigger('featureError', error);
expect(spy).toHaveBeenCalledWith(jasmine.objectContaining({
name: 'CartoError',
message: 'an error'
}));
});
describe('mouse pointer', function () {
describe('when mousing over a feature', function () {
it("should NOT set the mouse cursor to 'pointer' if layer doesn't have featureOverColumns or featureClickColumns", function () {
expect(map.getContainer().style.cursor).toEqual('');
leafletLayer._internalView.trigger('featureOver', internalEventMock);
expect(map.getContainer().style.cursor).toEqual('');
});
it("should set the mouse cursor to 'pointer' if layer has featureOverColumns", function () {
layer._featureOverColumns = [ 'foo' ];
expect(map.getContainer().style.cursor).toEqual('');
leafletLayer._internalView.trigger('featureOver', internalEventMock);
expect(map.getContainer().style.cursor).toEqual('pointer');
});
it("should set the mouse cursor to 'pointer' if layer has featureClickColumns", function () {
layer._featureClickColumns = [ 'foo' ];
expect(map.getContainer().style.cursor).toEqual('');
leafletLayer._internalView.trigger('featureOver', internalEventMock);
expect(map.getContainer().style.cursor).toEqual('pointer');
});
it("should set the mouse cursor to 'pointer' if layer has overed features after a featureOut", function () {
leafletLayer._hoveredLayers = ['L100'];
expect(map.getContainer().style.cursor).toEqual('');
leafletLayer._internalView.trigger('featureOut', internalEventMock);
expect(map.getContainer().style.cursor).toEqual('pointer');
});
});
describe('when mousing over NO features', function () {
it("should set the mouse cursor to 'auto'", function () {
expect(map.getContainer().style.cursor).toEqual('');
leafletLayer._internalView.trigger('featureOut', internalEventMock);
expect(map.getContainer().style.cursor).toEqual('auto');
});
});
});
});
function countLeafletLayers (map) {
return Object.keys(map._layers).length;
}
});

View File

@@ -0,0 +1,268 @@
const Base = require('../../../../../src/api/v4/source/base');
const carto = require('../../../../../src/api/v4');
describe('api/v4/source/dataset', function () {
var originalTimeout;
beforeEach(function () {
originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000;
});
afterEach(function () {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
});
describe('constructor', function () {
it('should return a new Dataset object', function () {
var populatedPlacesDataset = new carto.source.Dataset('ne_10m_populated_places_simple');
expect(populatedPlacesDataset).toBeDefined();
});
it('should autogenerate an id when no ID is given', function () {
var populatedPlacesDataset = new carto.source.Dataset('ne_10m_populated_places_simple');
expect(populatedPlacesDataset.getId()).toMatch(/S\d+/);
});
it('should throw an error if tableName is not provided', function () {
expect(function () {
new carto.source.Dataset(); // eslint-disable-line
}).toThrowError('Table name is required.');
});
it('should throw an error if tableName is empty', function () {
expect(function () {
new carto.source.Dataset(''); // eslint-disable-line
}).toThrowError('Table name must be not empty.');
});
it('should throw an error if tableName is not a valid string', function () {
expect(function () {
new carto.source.Dataset(3333); // eslint-disable-line
}).toThrowError('Table name must be a string.');
});
});
describe('.setTableName', function () {
let populatedPlacesDataset;
beforeEach(function () {
populatedPlacesDataset = new carto.source.Dataset('ne_10m_populated_places_simple');
});
it('should set the dataset', function () {
populatedPlacesDataset.setTableName('airbnb_listings');
expect(populatedPlacesDataset.getTableName()).toEqual('airbnb_listings');
});
it('should throw an error if query is empty', function () {
expect(function () {
populatedPlacesDataset.setTableName(undefined);
}).toThrowError('Table name is required.');
});
it('should throw an error if query is not a valid string', function () {
expect(function () {
populatedPlacesDataset.setTableName(333);
}).toThrowError('Table name must be a string.');
});
it('should throw an error if query is empty', function () {
expect(function () {
populatedPlacesDataset.setTableName('');
}).toThrowError('Table name must be not empty.');
});
it('should trigger an tableNameChanged event when there is no internal model', function (done) {
const expectedTable = 'airbnb_listings';
populatedPlacesDataset.on('tableNameChanged', function (newQuery) {
expect(newQuery).toEqual(expectedTable);
done();
});
populatedPlacesDataset.setTableName(expectedTable);
});
it('should trigger an tableNameChanged event when there is an internal model', function (done) {
const client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test'
});
const style = new carto.style.CartoCSS('#layer { marker-fill: red; }');
const layer = new carto.layer.Layer(populatedPlacesDataset, style);
const tableNameChangedSpy = jasmine.createSpy('tableNameChangedSpy');
const newTableName = 'airbnb_listings';
populatedPlacesDataset.on('tableNameChanged', tableNameChangedSpy);
client.addLayer(layer)
.then(function () {
return populatedPlacesDataset.setTableName(newTableName);
})
.then(function () {
expect(tableNameChangedSpy).toHaveBeenCalledWith(newTableName);
done();
});
});
it('should return a resolved promise when there is no internal model', function (done) {
const newTableName = 'airbnb_listings';
populatedPlacesDataset.setTableName(newTableName)
.then(function () {
expect(populatedPlacesDataset.getTableName()).toEqual(newTableName);
done();
});
});
it('should return a resolved promise when there is an internal model', function (done) {
const client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test'
});
const style = new carto.style.CartoCSS('#layer { marker-fill: red; }');
const layer = new carto.layer.Layer(populatedPlacesDataset, style);
const newTableName = 'airbnb_listings';
client.addLayer(layer)
.then(function () {
return populatedPlacesDataset.setTableName(newTableName);
})
.then(function () {
expect(populatedPlacesDataset.getTableName()).toEqual(newTableName);
done();
});
});
it('should return a rejected promise with a CartoError when there is an internal model (and a reload error)', function (done) {
const client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test'
});
const style = new carto.style.CartoCSS('#layer { marker-fill: red; }');
const layer = new carto.layer.Layer(populatedPlacesDataset, style);
const newTableName = 'invalid_dataset';
client.addLayer(layer)
.then(function () {
return populatedPlacesDataset.setTableName(newTableName);
})
.catch(function (cartoError) {
expect(cartoError.message).toMatch(/Invalid dataset name used. Dataset "invalid_dataset" does not exist./);
done();
});
});
});
describe('.getTableName', function () {
it('should return the table name', function () {
var dataset = new carto.source.Dataset('ne_10m_populated_places_simple');
expect(dataset.getTableName()).toBe('ne_10m_populated_places_simple');
});
});
describe('$setEngine', function () {
it('should create an internal model with the dataset and the engine', function () {
var populatedPlacesDataset = new carto.source.Dataset('ne_10m_populated_places_simple');
populatedPlacesDataset.$setEngine('fakeEngine');
var internalModel = populatedPlacesDataset.$getInternalModel();
expect(internalModel.get('id')).toEqual(populatedPlacesDataset.getId());
expect(internalModel.get('query')).toEqual('SELECT * from ne_10m_populated_places_simple');
expect(internalModel._engine).toEqual('fakeEngine');
});
});
describe('.getQueryToApply', function () {
let populatedPlacesDataset;
beforeEach(function () {
populatedPlacesDataset = new carto.source.Dataset('ne_10m_populated_places_simple');
spyOn(populatedPlacesDataset, '_updateInternalModelQuery');
});
it('should return original query if applied filters returns no SQL', function () {
expect(populatedPlacesDataset._getQueryToApply()).toBe('SELECT * from ne_10m_populated_places_simple');
});
it('should return wrapped query if filters are applied', function () {
populatedPlacesDataset.addFilter(new carto.filter.Category('fake_column', { in: ['category'] }));
expect(populatedPlacesDataset._getQueryToApply()).toBe("SELECT * FROM (SELECT * from ne_10m_populated_places_simple) as datasetQuery WHERE fake_column IN ('category')");
});
});
describe('.addFilter', function () {
let populatedPlacesDataset;
beforeEach(function () {
spyOn(Base.prototype, 'addFilter');
populatedPlacesDataset = new carto.source.Dataset('ne_10m_populated_places_simple');
spyOn(populatedPlacesDataset, '_updateInternalModelQuery');
});
it('should call original addFilter and _updateInternalModelQuery', function () {
populatedPlacesDataset.addFilter(new carto.filter.Category('fake_column', { in: ['category'] }));
expect(Base.prototype.addFilter).toHaveBeenCalled();
expect(populatedPlacesDataset._updateInternalModelQuery).toHaveBeenCalledWith(populatedPlacesDataset._getQueryToApply());
});
});
describe('.removeFilter', function () {
let populatedPlacesDataset, filter;
beforeEach(function () {
spyOn(Base.prototype, 'addFilter');
populatedPlacesDataset = new carto.source.Dataset('ne_10m_populated_places_simple');
spyOn(populatedPlacesDataset, '_updateInternalModelQuery');
filter = populatedPlacesDataset.addFilter(new carto.filter.Category('fake_column', { in: ['category'] }));
populatedPlacesDataset.addFilter(filter);
});
it('should call original removeFilter and _updateInternalModelQuery', function () {
populatedPlacesDataset.removeFilter(filter);
expect(Base.prototype.addFilter).toHaveBeenCalled();
expect(populatedPlacesDataset._updateInternalModelQuery).toHaveBeenCalledWith(populatedPlacesDataset._getQueryToApply());
});
});
describe('.getFilters', function () {
let populatedPlacesDataset, filter;
beforeEach(function () {
filter = new carto.filter.Category('fake_column', { in: ['category'] });
populatedPlacesDataset = new carto.source.Dataset('ne_10m_populated_places_simple');
populatedPlacesDataset.addFilter(filter);
});
it('should return added filters', function () {
expect(populatedPlacesDataset.getFilters()).toEqual([filter]);
});
});
describe('errors', function () {
it('should trigger an error when invalid', function (done) {
var client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test'
});
var invalidSource = new carto.source.Dataset('invalid_dataset');
var cartoCSS = new carto.style.CartoCSS('#layer { marker-fill: red; }');
invalidSource.on('error', function (cartoError) {
expect(cartoError.message).toMatch(/Invalid dataset name used. Dataset "invalid_dataset" does not exist./);
done();
});
var layer = new carto.layer.Layer(invalidSource, cartoCSS);
client.addLayer(layer).catch(function () { }); // Prevent console "uncaught error" warning.
});
});
});

View File

@@ -0,0 +1,257 @@
const Base = require('../../../../../src/api/v4/source/base');
const carto = require('../../../../../src/api/v4');
describe('api/v4/source/sql', function () {
var sqlQuery;
beforeEach(function () {
sqlQuery = new carto.source.SQL('SELECT * FROM ne_10m_populated_places_simple WHERE adm0name = \'Spain\'');
});
describe('constructor', function () {
it('should return a new Dataset object', function () {
expect(sqlQuery).toBeDefined();
});
it('should autogenerate an id', function () {
expect(sqlQuery.getId()).toMatch(/S\d+/);
});
it('should throw an error if query is not provided', function () {
expect(function () {
new carto.source.SQL(); // eslint-disable-line
}).toThrowError('SQL Source must have a SQL query.');
});
it('should throw an error if query is empty', function () {
expect(function () {
new carto.source.SQL(''); // eslint-disable-line
}).toThrowError('SQL Source must have a SQL query.');
});
it('should throw an error if query is not a valid string', function () {
expect(function () {
new carto.source.SQL(3333); // eslint-disable-line
}).toThrowError('SQL Query must be a string.');
});
});
describe('.setQuery', function () {
it('should set the query', function () {
sqlQuery.setQuery('SELECT foo FROM bar');
expect(sqlQuery.getQuery()).toEqual('SELECT foo FROM bar');
});
it('should throw an error if query is empty', function () {
expect(function () {
sqlQuery.setQuery('');
}).toThrowError('SQL Source must have a SQL query.');
});
it('should throw an error if query is not a valid string', function () {
expect(function () {
sqlQuery.setQuery(333);
}).toThrowError('SQL Query must be a string.');
});
it('should trigger an queryChanged event when there is no internal model', function (done) {
var expectedQuery = 'SELECT * FROM ne_10m_populated_places_simple LIMIT 10';
sqlQuery.on('queryChanged', function (newQuery) {
expect(newQuery).toEqual(expectedQuery);
done();
});
sqlQuery.setQuery(expectedQuery);
});
it('should trigger an queryChanged event when there is an internal model', function (done) {
var client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test'
});
var style = new carto.style.CartoCSS('#layer { marker-fill: red; }');
var layer = new carto.layer.Layer(sqlQuery, style);
var queryChangedSpy = jasmine.createSpy('queryChangedSpy');
var newQuery = 'SELECT * FROM ne_10m_populated_places_simple LIMIT 10';
sqlQuery.on('queryChanged', queryChangedSpy);
client.addLayer(layer)
.then(function () {
return sqlQuery.setQuery(newQuery);
})
.then(function () {
expect(queryChangedSpy).toHaveBeenCalledWith(newQuery);
done();
});
});
it('should return a resolved promise when there is no internal model', function (done) {
var newQuery = 'SELECT * FROM ne_10m_populated_places_simple';
sqlQuery.setQuery(newQuery)
.then(function () {
expect(sqlQuery.getQuery()).toEqual(newQuery);
done();
});
});
it('should return a resolved promise when there is an internal model', function (done) {
var client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test'
});
var style = new carto.style.CartoCSS('#layer { marker-fill: red; }');
var layer = new carto.layer.Layer(sqlQuery, style);
var newQuery = 'SELECT * FROM ne_10m_populated_places_simple LIMIT 10';
client.addLayer(layer)
.then(function () {
return sqlQuery.setQuery(newQuery);
})
.then(function () {
expect(sqlQuery.getQuery()).toEqual(newQuery);
done();
});
});
it('should return a rejected promise with a CartoError when there is an internal model (and a reload error)', function (done) {
var client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test'
});
var style = new carto.style.CartoCSS('#layer { marker-fill: red; }');
var layer = new carto.layer.Layer(sqlQuery, style);
var newQuery = 'SELECT * FROM invalid_dataset';
client.addLayer(layer)
.then(function () {
return sqlQuery.setQuery(newQuery);
})
.catch(function (cartoError) {
expect(cartoError.message).toMatch(/Invalid dataset name used. Dataset "invalid_dataset" does not exist./);
done();
});
});
});
describe('$setEngine', function () {
it('should create an internal model with the dataset attrs and the engine', function () {
sqlQuery.$setEngine('fakeEngine');
var internalModel = sqlQuery.$getInternalModel();
expect(internalModel.get('id')).toEqual(sqlQuery.getId());
expect(internalModel.get('query')).toEqual('SELECT * FROM ne_10m_populated_places_simple WHERE adm0name = \'Spain\'');
expect(internalModel._engine).toEqual('fakeEngine');
});
});
describe('.getQueryToApply', function () {
let populatedPlacesSQL;
beforeEach(function () {
populatedPlacesSQL = new carto.source.SQL('SELECT * FROM ne_10m_populated_places_simple');
spyOn(populatedPlacesSQL, '_updateInternalModelQuery');
});
it('should return original query if applied filters returns no SQL', function () {
expect(populatedPlacesSQL._getQueryToApply()).toBe('SELECT * FROM ne_10m_populated_places_simple');
});
it('should return wrapped query if filters are applied', function () {
populatedPlacesSQL.addFilter(new carto.filter.Category('fake_column', { in: ['category'] }));
expect(populatedPlacesSQL._getQueryToApply()).toBe("SELECT * FROM (SELECT * FROM ne_10m_populated_places_simple) as originalQuery WHERE fake_column IN ('category')");
});
});
describe('.addFilter', function () {
let populatedPlacesSQL;
beforeEach(function () {
spyOn(Base.prototype, 'addFilter');
populatedPlacesSQL = new carto.source.SQL('SELECT * FROM ne_10m_populated_places_simple');
spyOn(populatedPlacesSQL, '_updateInternalModelQuery');
});
it('should call original addFilter and _updateInternalModelQuery', function () {
populatedPlacesSQL.addFilter(new carto.filter.Category('fake_column', { in: ['category'] }));
expect(Base.prototype.addFilter).toHaveBeenCalled();
expect(populatedPlacesSQL._updateInternalModelQuery).toHaveBeenCalledWith(populatedPlacesSQL._getQueryToApply());
});
});
describe('.removeFilter', function () {
let populatedPlacesDataset, filter;
beforeEach(function () {
spyOn(Base.prototype, 'addFilter');
populatedPlacesDataset = new carto.source.Dataset('ne_10m_populated_places_simple');
spyOn(populatedPlacesDataset, '_updateInternalModelQuery');
filter = populatedPlacesDataset.addFilter(new carto.filter.Category('fake_column', { in: ['category'] }));
populatedPlacesDataset.addFilter(filter);
});
it('should call original removeFilter and _updateInternalModelQuery', function () {
populatedPlacesDataset.removeFilter(filter);
expect(Base.prototype.addFilter).toHaveBeenCalled();
expect(populatedPlacesDataset._updateInternalModelQuery).toHaveBeenCalledWith(populatedPlacesDataset._getQueryToApply());
});
});
describe('.getFilters', function () {
let populatedPlacesDataset, filter;
beforeEach(function () {
filter = new carto.filter.Category('fake_column', { in: ['category'] });
populatedPlacesDataset = new carto.source.Dataset('ne_10m_populated_places_simple');
populatedPlacesDataset.addFilter(filter);
});
it('should return added filters', function () {
expect(populatedPlacesDataset.getFilters()).toEqual([filter]);
});
});
describe('errors', function () {
it('should trigger an error when invalid', function (done) {
var client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test'
});
// The following sql has the invalid operator: ===
var invalidSource = new carto.source.SQL('SELECT * FROM ne_10m_populated_places_simple WHERE adm0name === \'Spain\'');
var cartoCss = new carto.style.CartoCSS('#layer { marker-fill: red; }');
invalidSource.on('error', function (cartoError) {
expect(cartoError.message).toMatch(/operator does not exist/);
done();
});
var layer = new carto.layer.Layer(invalidSource, cartoCss);
client.addLayer(layer).catch(function () { }); // Prevent console "uncaught error" warning.
});
it('should trigger a CartoError when there is an error in the internal model', function () {
var client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test'
});
var source = new carto.source.SQL('SELECT * FROM ne_10m_populated_places_simple');
var cartoCss = new carto.style.CartoCSS('#layer { marker-fill: red; }');
var layer = new carto.layer.Layer(source, cartoCss);
var spy = jasmine.createSpy('spy');
source.on(carto.events.ERROR, spy);
client.addLayer(layer);
source._internalModel.set('error', 'an error');
expect(spy).toHaveBeenCalledWith(jasmine.objectContaining({
name: 'CartoError',
originalError: 'an error'
}));
});
});
});

View File

@@ -0,0 +1,161 @@
var carto = require('../../../../../src/api/v4');
describe('api/v4/style/cartocss', function () {
var cartoCSS;
beforeEach(function () {
cartoCSS = new carto.style.CartoCSS('#layer { marker-width:10; }');
});
describe('constructor', function () {
it('should return an object', function () {
expect(cartoCSS).toBeDefined();
});
it('should throw an error if cartoCSS is not provided', function () {
expect(function () {
new carto.style.CartoCSS(); // eslint-disable-line
}).toThrowError('CartoCSS is required.');
});
it('should throw an error if cartoCSS is empty', function () {
expect(function () {
new carto.style.CartoCSS(''); // eslint-disable-line
}).toThrowError('CartoCSS is required.');
});
it('should throw an error if cartoCSS is not a valid string', function () {
expect(function () {
new carto.style.CartoCSS(true); // eslint-disable-line
}).toThrowError('CartoCSS must be a string.');
});
});
describe('errors', function () {
it('should trigger a CartoError when the style is not valid', function (done) {
var client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test'
});
var source = new carto.source.Dataset('ne_10m_populated_places_simple');
var invalidCartoCSS = new carto.style.CartoCSS('#layer { invalid-property: 10; }');
invalidCartoCSS.on('error', function (cartoError) {
expect(cartoError.message).toMatch(/Unrecognized rule "invalid-property"/);
done();
});
var layer = new carto.layer.Layer(source, invalidCartoCSS);
client.addLayer(layer)
.catch(function () { }); // Prevent console "uncaught error" warning.
});
it('should NOT trigger a CartoError when the style is valid but there are some other errors', function (done) {
var client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test'
});
var source = new carto.source.Dataset('invalid_dataset');
var invalidCartoCSS = new carto.style.CartoCSS('#layer { marker-with: 10; }');
var errorCallbackSpy = jasmine.createSpy('errorCallbackSpy').and.callThrough();
invalidCartoCSS.on('error', errorCallbackSpy);
var layer = new carto.layer.Layer(source, invalidCartoCSS);
client.addLayer(layer)
.catch(function () {
expect(errorCallbackSpy).not.toHaveBeenCalled();
done();
});
});
});
describe('.getContent', function () {
it('should return the internal style', function () {
var expected = '#layer { marker-width:10; }';
var actual = cartoCSS.getContent();
expect(actual).toEqual(expected);
});
});
describe('.setContent', function () {
var layer;
var source;
var newContent;
beforeEach(function () {
source = new carto.source.Dataset('ne_10m_populated_places_simple');
layer = new carto.layer.Layer(source, cartoCSS);
newContent = '#layer { marker-fill: #FABADA }';
});
describe('when no engine is attached', function () {
it('should update the internal style', function (done) {
cartoCSS.setContent(newContent)
.then(function () {
expect(cartoCSS.getContent()).toEqual(newContent);
done();
});
});
it('should trigger a contentChanged event', function (done) {
var contentChangedSpy = jasmine.createSpy('contentChangedSpy');
cartoCSS.on('contentChanged', contentChangedSpy);
cartoCSS.setContent(newContent)
.then(function () {
expect(cartoCSS.getContent()).toEqual(newContent);
expect(contentChangedSpy).toHaveBeenCalled();
done();
});
});
});
describe('when an engine is attached', function () {
var client;
beforeAll(function () {
client = new carto.Client({
apiKey: '84fdbd587e4a942510270a48e843b4c1baa11e18',
username: 'cartojs-test'
});
});
it('should update the internal style', function (done) {
client.addLayer(layer)
.then(function () {
return cartoCSS.setContent(newContent);
})
.then(function () {
expect(cartoCSS.getContent()).toEqual(newContent);
done();
});
});
it('should trigger a contentChanged event?', function (done) {
var contentChangedSpy = jasmine.createSpy('contentChangedSpy');
cartoCSS.on('contentChanged', contentChangedSpy);
client.addLayer(layer)
.then(function () {
return cartoCSS.setContent(newContent);
})
.then(function () {
expect(contentChangedSpy).toHaveBeenCalled();
done();
})
.catch(console.warn);
});
it('should reject the promise with a CartoError when the reload fails', function (done) {
var malformedStyle = '#layer { invalid-property: foo }';
client.addLayer(layer)
.then(function () {
return cartoCSS.setContent(malformedStyle);
})
.catch(function (cartoError) {
expect(cartoError.message).toMatch(/Unrecognized rule "invalid-property"/);
done();
});
});
});
});
});

View File

@@ -0,0 +1,352 @@
var C = require('../../../src/constants');
var VizJSON = require('../../../src/api/vizjson');
describe('src/vis/vizjson', function () {
it('should expose the vizjson attributes', function () {
var vizjson = new VizJSON({
key1: 'value1',
key2: 'value2'
});
expect(vizjson.key1).toEqual('value1');
expect(vizjson.key2).toEqual('value2');
});
it('should have an attribution overlay by default', function () {
var vizjson = new VizJSON({});
expect(vizjson.getOverlayByType(C.OVERLAY_TYPES.ATTRIBUTION)).toEqual({
type: C.OVERLAY_TYPES.ATTRIBUTION
});
});
describe('.isNamedMap', function () {
it("should return false if datasource doesn't have a template_name", function () {
var vizjson = new VizJSON({
datasource: { }
});
expect(vizjson.isNamedMap()).toBeFalsy();
});
it('should return true if datasource has a template_name', function () {
var vizjson = new VizJSON({
datasource: {
template_name: 'tpl0123456789'
}
});
expect(vizjson.isNamedMap()).toBeTruthy();
});
});
describe('.hasZoomOverlay', function () {
it("should return true if there's a zoom overlay", function () {
var vizjson = new VizJSON({
overlays: [{
type: C.OVERLAY_TYPES.ZOOM
}]
});
expect(vizjson.hasZoomOverlay()).toBeTruthy();
});
});
describe('.hasOverlay', function () {
it("should return true if there's an overlay with the given type", function () {
var vizjson = new VizJSON({
overlays: [{
type: 'something'
}]
});
expect(vizjson.hasOverlay('something')).toBeTruthy();
});
it("should return false if there isn't an overlay with the given type", function () {
var vizjson = new VizJSON({
overlays: [{
type: 'something'
}]
});
expect(vizjson.hasOverlay('else')).toBeFalsy();
});
});
describe('.getOverlayByType', function () {
it("should return the overlay if there's an overlay with the given type", function () {
var vizjson = new VizJSON({
overlays: [{
type: 'something'
}]
});
expect(vizjson.getOverlayByType('something')).toEqual({
type: 'something'
});
});
it("should return nothing if there isn't an overlay with the given type", function () {
var vizjson = new VizJSON({
overlays: [{
type: 'something'
}]
});
expect(vizjson.getOverlayByType('else')).toBeUndefined();
});
});
describe('.addHeaderOverlay', function () {
it('should add a header Overlay', function () {
var vizjson = new VizJSON({
url: 'https://carto.com',
title: 'title',
description: 'description'
});
vizjson.addHeaderOverlay('show_title', 'show_description', 'is_shareable');
expect(vizjson.getOverlayByType('header')).toEqual({
type: 'header',
order: 1,
shareable: 'is_shareable',
url: 'https://carto.com',
options: {
extra: {
title: 'title',
description: 'description',
show_title: 'show_title',
show_description: 'show_description'
}
}
});
});
});
describe('.addSearchOverlay', function () {
it('should add a search overlay', function () {
var vizjson = new VizJSON({});
vizjson.addSearchOverlay();
expect(vizjson.getOverlayByType('search')).toEqual({
type: 'search',
order: 3
});
});
});
describe('.removeOverlay', function () {
it('should remove the overlay of the given type', function () {
var vizjson = new VizJSON({
overlays: [{
type: 'something'
}]
});
expect(vizjson.getOverlayByType('something')).toBeDefined();
vizjson.removeOverlay('something');
expect(vizjson.getOverlayByType('something')).not.toBeDefined();
});
});
describe('.removeLoaderOverlay', function () {
it('should remove the loader overlay', function () {
var vizjson = new VizJSON({
overlays: [{
type: C.OVERLAY_TYPES.LOADER
}]
});
expect(vizjson.getOverlayByType(C.OVERLAY_TYPES.LOADER)).toBeDefined();
vizjson.removeLoaderOverlay();
expect(vizjson.getOverlayByType(C.OVERLAY_TYPES.LOADER)).not.toBeDefined();
});
});
describe('.removeZoomOverlay', function () {
it('should remove the zoom overlay', function () {
var vizjson = new VizJSON({
overlays: [{
type: C.OVERLAY_TYPES.ZOOM
}]
});
expect(vizjson.getOverlayByType(C.OVERLAY_TYPES.ZOOM)).toBeDefined();
vizjson.removeZoomOverlay();
expect(vizjson.getOverlayByType(C.OVERLAY_TYPES.ZOOM)).not.toBeDefined();
});
});
describe('.removeSearchOverlay', function () {
it('should remove the search overlay', function () {
var vizjson = new VizJSON({
overlays: [{
type: C.OVERLAY_TYPES.SEARCH
}]
});
expect(vizjson.getOverlayByType(C.OVERLAY_TYPES.SEARCH)).toBeDefined();
vizjson.removeSearchOverlay();
expect(vizjson.getOverlayByType(C.OVERLAY_TYPES.SEARCH)).not.toBeDefined();
});
});
describe('.enforceGMapsBaseLayer', function () {
it('should replace the existing base layer by a GMaps one', function () {
var vizjson = new VizJSON({
map_provider: C.MAP_PROVIDER_TYPES.LEAFLET,
layers: [{
options: {
type: 'Tiled',
url: 'http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png',
name: 'Positron',
className: 'httpsbasemapscartocdncomlight_nolabelszxypng',
attribution: '&copy; <a href=\'http://www.openstreetmap.org/copyright\'>OpenStreetMap</a> contributors &copy; <a href= \'https://carto.com/attributions\'>CARTO</a>',
urlTemplate: 'http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png'
}
}]
});
vizjson.enforceGMapsBaseLayer('roadmap', { color: 'blue' });
expect(vizjson.layers[0]).toEqual({
options: {
type: 'GMapsBase',
url: 'http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png',
name: 'roadmap',
className: 'httpsbasemapscartocdncomlight_nolabelszxypng',
attribution: '',
urlTemplate: 'http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png',
baseType: 'roadmap',
style: {color: 'blue'}
}
});
expect(vizjson.map_provider).toEqual(C.MAP_PROVIDER_TYPES.GMAPS);
});
it('should NOT replace the existing base layer by a GMaps one if map_provider is not leaflet', function () {
var vizjson = new VizJSON({
map_provider: 'something',
layers: [{
options: {
type: 'Tiled',
url: 'http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png',
name: 'Positron',
className: 'httpsbasemapscartocdncomlight_nolabelszxypng',
attribution: '&copy; <a href=\'http://www.openstreetmap.org/copyright\'>OpenStreetMap</a> contributors &copy; <a href= \'https://carto.com/attributions\'>CARTO</a>',
urlTemplate: 'http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png'
}
}]
});
vizjson.enforceGMapsBaseLayer('roadmap', { color: 'blue' });
expect(vizjson.layers[0]).toEqual({
options: {
type: 'Tiled',
url: 'http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png',
name: 'Positron',
className: 'httpsbasemapscartocdncomlight_nolabelszxypng',
attribution: '&copy; <a href=\'http://www.openstreetmap.org/copyright\'>OpenStreetMap</a> contributors &copy; <a href= \'https://carto.com/attributions\'>CARTO</a>',
urlTemplate: 'http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png'
}
});
expect(vizjson.map_provider).toEqual('something');
});
it('should NOT replace the existing base layer by a GMaps one if the given type is not valid', function () {
var vizjson = new VizJSON({
map_provider: C.MAP_PROVIDER_TYPES.LEAFLET,
layers: [{
options: {
type: 'Tiled',
url: 'http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png',
name: 'Positron',
className: 'httpsbasemapscartocdncomlight_nolabelszxypng',
attribution: '&copy; <a href=\'http://www.openstreetmap.org/copyright\'>OpenStreetMap</a> contributors &copy; <a href= \'https://carto.com/attributions\'>CARTO</a>',
urlTemplate: 'http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png'
}
}]
});
vizjson.enforceGMapsBaseLayer('invalid type', { color: 'blue' });
expect(vizjson.layers[0]).toEqual({
options: {
type: 'Tiled',
url: 'http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png',
name: 'Positron',
className: 'httpsbasemapscartocdncomlight_nolabelszxypng',
attribution: '&copy; <a href=\'http://www.openstreetmap.org/copyright\'>OpenStreetMap</a> contributors &copy; <a href= \'https://carto.com/attributions\'>CARTO</a>',
urlTemplate: 'http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png'
}
});
expect(vizjson.map_provider).toEqual(C.MAP_PROVIDER_TYPES.LEAFLET);
});
});
describe('.setZoom', function () {
it('should set a new zoom and unset bounds', function () {
var vizjson = new VizJSON({
zoom: 'old_zoom',
bounds: 'bounds'
});
vizjson.setZoom('new_zoom');
expect(vizjson.zoom).toEqual('new_zoom');
expect(vizjson.bounds).toBeNull();
});
});
describe('.setCenter', function () {
it('should set a new center and unset bounds', function () {
var vizjson = new VizJSON({
center: 'old_center',
bounds: 'bounds'
});
vizjson.setCenter('new_center');
expect(vizjson.center).toEqual('new_center');
expect(vizjson.bounds).toBeNull();
});
});
describe('.setBounds', function () {
it('should set bounds', function () {
var vizjson = new VizJSON({
bounds: 'old_bounds'
});
vizjson.setBounds('new_bounds');
expect(vizjson.bounds).toEqual('new_bounds');
});
});
describe('.setVector', function () {
it('should set vector', function () {
var vizjson = new VizJSON({
vector: true
});
vizjson.setVector(false);
expect(vizjson.vector).toBeFalsy();
});
});
});